diff --git a/SOURCES/0106-Add-a-new-option-ldap_group_external_member.patch b/SOURCES/0106-Add-a-new-option-ldap_group_external_member.patch
new file mode 100644
index 0000000..a17aa8a
--- /dev/null
+++ b/SOURCES/0106-Add-a-new-option-ldap_group_external_member.patch
@@ -0,0 +1,178 @@
+From b842e04a1e73dd9af3096b065fcf5b2a7fe55b51 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 25 Jan 2016 16:03:23 +0100
+Subject: [PATCH 106/108] Add a new option ldap_group_external_member
+
+Required for:
+    https://fedorahosted.org/sssd/ticket/2522
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit 3cf7fdfcaedb986f42a6640e26aa057007b64045)
+(cherry picked from commit 7db3bdfd6b1b845866c1ff062d25de5804141e89)
+---
+ src/config/SSSDConfig/__init__.py.in     |  1 +
+ src/config/etc/sssd.api.d/sssd-ad.conf   |  1 +
+ src/config/etc/sssd.api.d/sssd-ipa.conf  |  1 +
+ src/config/etc/sssd.api.d/sssd-ldap.conf |  1 +
+ src/db/sysdb.h                           |  1 +
+ src/man/sssd-ldap.5.xml                  | 16 ++++++++++++++++
+ src/providers/ad/ad_opts.h               |  1 +
+ src/providers/ipa/ipa_opts.h             |  1 +
+ src/providers/ldap/ldap_opts.h           |  3 +++
+ src/providers/ldap/sdap.h                |  1 +
+ 10 files changed, 27 insertions(+)
+
+diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
+index a7cd1dd243a53e7038dc69628475c76ccdd93260..7ec28d1d4a121cef0f7a8f3122c95bd396f773a7 100644
+--- a/src/config/SSSDConfig/__init__.py.in
++++ b/src/config/SSSDConfig/__init__.py.in
+@@ -328,6 +328,7 @@ option_strings = {
+     'ldap_group_objectsid' : _("objectSID attribute"),
+     'ldap_group_modify_timestamp' : _('Modification time attribute for groups'),
+     'ldap_group_type' : _('Type of the group and other flags'),
++    'ldap_group_external_member' : _('The LDAP group external member attribute'),
+     #replaced by ldap_entry_usn# 'ldap_group_entry_usn' : _('entryUSN attribute'),
+     'ldap_group_nesting_level' : _('Maximum nesting level SSSd will follow'),
+ 
+diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
+index b636d93108ef0a3831970d7827895c14b0f3571c..e1083aa2a6d9a0ebf61a6029af05aea62ec7b217 100644
+--- a/src/config/etc/sssd.api.d/sssd-ad.conf
++++ b/src/config/etc/sssd.api.d/sssd-ad.conf
+@@ -108,6 +108,7 @@ ldap_group_objectsid = str, None, false
+ ldap_group_modify_timestamp = str, None, false
+ ldap_group_entry_usn = str, None, false
+ ldap_group_type = int, None, false
++ldap_group_external_member = str, None, false
+ ldap_force_upper_case_realm = bool, None, false
+ ldap_group_nesting_level = int, None, false
+ ldap_netgroup_search_base = str, None, false
+diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
+index ab712fe55cdac6d247a085aeca5cc82d65966623..3cbfb2ee5e06f2ef731e8f9eb79be374351d7281 100644
+--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
++++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
+@@ -104,6 +104,7 @@ ldap_group_objectsid = str, None, false
+ ldap_group_modify_timestamp = str, None, false
+ ldap_group_entry_usn = str, None, false
+ ldap_group_type = int, None, false
++ldap_group_external_member = str, None, false
+ ldap_force_upper_case_realm = bool, None, false
+ ldap_group_nesting_level = int, None, false
+ ldap_netgroup_search_base = str, None, false
+diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
+index 8fd45fd4093714f458161eb352157c845d926f06..21a38b9581ea35f78618a272bc3a943f6968d37e 100644
+--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
++++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
+@@ -98,6 +98,7 @@ ldap_group_objectsid = str, None, false
+ ldap_group_modify_timestamp = str, None, false
+ ldap_group_entry_usn = str, None, false
+ ldap_group_type = int, None, false
++ldap_group_external_member = str, None, false
+ ldap_group_nesting_level = int, None, false
+ ldap_force_upper_case_realm = bool, None, false
+ ldap_netgroup_search_base = str, None, false
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 3fa3f040708a4984158206d66a1d28a079091cf7..817ecd2764c3fac3ca3e38ba78f9e8ef2afab51b 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -81,6 +81,7 @@
+ #define SYSDB_USER_CATEGORY "userCategory"
+ #define SYSDB_HOST_CATEGORY "hostCategory"
+ #define SYSDB_GROUP_TYPE "groupType"
++#define SYSDB_EXTERNAL_MEMBER "externalMember"
+ 
+ #define SYSDB_GECOS "gecos"
+ #define SYSDB_LAST_LOGIN "lastLogin"
+diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
+index 49e84560f825b1bff255c1ad131487ba3243300d..cc1b17108500c5d241008f683d14ab32cefab396 100644
+--- a/src/man/sssd-ldap.5.xml
++++ b/src/man/sssd-ldap.5.xml
+@@ -942,6 +942,22 @@
+                 </varlistentry>
+ 
+                 <varlistentry>
++                    <term>ldap_group_external_member (string)</term>
++                    <listitem>
++                        <para>
++                            The LDAP attribute that references group
++                            members that are defined in an external
++                            domain. At the moment, only IPA's external
++                            members are supported.
++                        </para>
++                        <para>
++                            Default: ipaExternalMember in the IPA provider,
++                            otherwise unset.
++                        </para>
++                    </listitem>
++                </varlistentry>
++
++                <varlistentry>
+                     <term>ldap_group_nesting_level (integer)</term>
+                     <listitem>
+                         <para>
+diff --git a/src/providers/ad/ad_opts.h b/src/providers/ad/ad_opts.h
+index 00586a7ada63ad4c89630e9589d3ff75d1726703..b2133db1859914d54d2db2a871d7fbae7aeff268 100644
+--- a/src/providers/ad/ad_opts.h
++++ b/src/providers/ad/ad_opts.h
+@@ -233,6 +233,7 @@ struct sdap_attr_map ad_2008r2_group_map[] = {
+     { "ldap_group_modify_timestamp", "whenChanged", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", SDAP_AD_USN, SYSDB_USN, NULL },
+     { "ldap_group_type", "groupType", SYSDB_GROUP_TYPE, NULL },
++    { "ldap_group_external_member", NULL, SYSDB_EXTERNAL_MEMBER, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
+index 78949e3ddec95f7f4303eab905bbbf6ec14ed6ae..81ccc42fc0c9f21c8ef16e2d1735bc06199ba747 100644
+--- a/src/providers/ipa/ipa_opts.h
++++ b/src/providers/ipa/ipa_opts.h
+@@ -221,6 +221,7 @@ struct sdap_attr_map ipa_group_map[] = {
+     { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", NULL, SYSDB_USN, NULL },
+     { "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
++    { "ldap_group_external_member", NULL, SYSDB_EXTERNAL_MEMBER, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+diff --git a/src/providers/ldap/ldap_opts.h b/src/providers/ldap/ldap_opts.h
+index 9f58db5bd9eef1391e97c1890cbff94c2a5406d6..bda2e79e849ab9e4e3e91407faafc0d8b06df899 100644
+--- a/src/providers/ldap/ldap_opts.h
++++ b/src/providers/ldap/ldap_opts.h
+@@ -197,6 +197,7 @@ struct sdap_attr_map rfc2307_group_map[] = {
+     { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", NULL, SYSDB_USN, NULL },
+     { "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
++    { "ldap_group_external_member", NULL, SYSDB_EXTERNAL_MEMBER, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+@@ -253,6 +254,7 @@ struct sdap_attr_map rfc2307bis_group_map[] = {
+     { "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", NULL, SYSDB_USN, NULL },
+     { "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
++    { "ldap_group_external_member", NULL, SYSDB_EXTERNAL_MEMBER, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+@@ -309,6 +311,7 @@ struct sdap_attr_map gen_ad2008r2_group_map[] = {
+     { "ldap_group_modify_timestamp", "whenChanged", SYSDB_ORIG_MODSTAMP, NULL },
+     { "ldap_group_entry_usn", SDAP_AD_USN, SYSDB_USN, NULL },
+     { "ldap_group_type", "groupType", SYSDB_GROUP_TYPE, NULL },
++    { "ldap_group_external_member", NULL, SYSDB_EXTERNAL_MEMBER, NULL },
+     SDAP_ATTR_MAP_TERMINATOR
+ };
+ 
+diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
+index 444502bf7159edcf4cebe530cce8b216c737ec30..d7053949f5804b637c27bb2d8e34991653770639 100644
+--- a/src/providers/ldap/sdap.h
++++ b/src/providers/ldap/sdap.h
+@@ -303,6 +303,7 @@ enum sdap_group_attrs {
+     SDAP_AT_GROUP_MODSTAMP,
+     SDAP_AT_GROUP_USN,
+     SDAP_AT_GROUP_TYPE,
++    SDAP_AT_GROUP_EXT_MEMBER,
+ 
+     SDAP_OPTS_GROUP /* attrs counter */
+ };
+-- 
+2.4.3
+
diff --git a/SOURCES/0107-IPA-Add-interface-to-call-into-IPA-provider-from-LDA.patch b/SOURCES/0107-IPA-Add-interface-to-call-into-IPA-provider-from-LDA.patch
new file mode 100644
index 0000000..9c83b71
--- /dev/null
+++ b/SOURCES/0107-IPA-Add-interface-to-call-into-IPA-provider-from-LDA.patch
@@ -0,0 +1,446 @@
+From 2515f7d9a19c2634baf1cd2f008a2148b5300db0 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 25 Jan 2016 16:11:59 +0100
+Subject: [PATCH 107/108] IPA: Add interface to call into IPA provider from
+ LDAP provider
+
+https://fedorahosted.org/sssd/ticket/2522
+
+Adds a pluggable interface that is able to resolve the IPA group's
+external members. At the moment, the request calls the full be_
+interface to make sure all corner cases like id-views are handled
+internally.
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit e2d96566aeb881bd89e5c9236d663f6a9a88019a)
+(cherry picked from commit 00ee45423f0712b83926c6f8b354a1a18ff741c8)
+---
+ src/providers/ipa/ipa_id.c                    |   5 +-
+ src/providers/ipa/ipa_init.c                  |  28 +++
+ src/providers/ipa/ipa_subdomains.h            |  11 ++
+ src/providers/ipa/ipa_subdomains_ext_groups.c | 275 ++++++++++++++++++++++++++
+ src/providers/ipa/ipa_subdomains_id.c         |   1 +
+ src/providers/ldap/sdap.h                     |  23 +++
+ 6 files changed, 342 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
+index e81ccb34dd6eb44618538593f5473fbe5e89d896..8782a81247f7ca7ba6d2aa55f8d01897a0b38523 100644
+--- a/src/providers/ipa/ipa_id.c
++++ b/src/providers/ipa/ipa_id.c
+@@ -384,7 +384,10 @@ static int ipa_initgr_get_overrides_step(struct tevent_req *req)
+         /* This should never happen, the search filter used to get the list
+          * of groups includes "uuid=*"
+          */
+-        DEBUG(SSSDBG_OP_FAILURE, "A group with no UUID, error!\n");
++        DEBUG(SSSDBG_OP_FAILURE,
++              "The group %s has no UUID attribute %s, error!\n",
++              ldb_dn_get_linearized(state->groups[state->group_idx]->dn),
++              state->groups_id_attr);
+         return EINVAL;
+     }
+ 
+diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
+index 0e16dd97c78a087256fb77be500c9741484867c5..453e2b25673ac709c9fa3809d35b7885630c8b24 100644
+--- a/src/providers/ipa/ipa_init.c
++++ b/src/providers/ipa/ipa_init.c
+@@ -139,6 +139,24 @@ int common_ipa_init(struct be_ctx *bectx)
+     return EOK;
+ }
+ 
++static struct sdap_ext_member_ctx *
++ipa_create_ext_members_ctx(TALLOC_CTX *mem_ctx,
++                           struct ipa_id_ctx *id_ctx)
++{
++    struct sdap_ext_member_ctx *ext_ctx = NULL;
++
++    ext_ctx = talloc_zero(mem_ctx, struct sdap_ext_member_ctx);
++    if (ext_ctx == NULL) {
++        return NULL;
++    }
++
++    ext_ctx->pvt = id_ctx;
++    ext_ctx->ext_member_resolve_send = ipa_ext_group_member_send;
++    ext_ctx->ext_member_resolve_recv = ipa_ext_group_member_recv;
++
++    return ext_ctx;
++}
++
+ int sssm_ipa_id_init(struct be_ctx *bectx,
+                      struct bet_ops **ops,
+                      void **pvt_data)
+@@ -360,6 +378,16 @@ int sssm_ipa_id_init(struct be_ctx *bectx,
+               "will not work [%d]: %s\n", ret, strerror(ret));
+     }
+ 
++    ipa_ctx->sdap_id_ctx->opts->ext_ctx = ipa_create_ext_members_ctx(
++                                                    ipa_ctx->sdap_id_ctx->opts,
++                                                    ipa_ctx);
++    if (ipa_ctx->sdap_id_ctx->opts->ext_ctx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Unable to set SRV the extrernal group ctx\n");
++        ret = ENOMEM;
++        goto done;
++    }
++
+     *ops = &ipa_id_ops;
+     *pvt_data = ipa_ctx;
+     ret = EOK;
+diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h
+index 0c13f8ed2eeda87237dfb097f532c7137095ddf1..23c3b7e3cd3ee1e0ac1dbcf98dc71a6c2337b835 100644
+--- a/src/providers/ipa/ipa_subdomains.h
++++ b/src/providers/ipa/ipa_subdomains.h
+@@ -137,4 +137,15 @@ struct tevent_req *ipa_get_ad_memberships_send(TALLOC_CTX *mem_ctx,
+                                         const char *domain);
+ 
+ errno_t ipa_get_ad_memberships_recv(struct tevent_req *req, int *dp_error_out);
++
++struct tevent_req *ipa_ext_group_member_send(TALLOC_CTX *mem_ctx,
++                                             struct tevent_context *ev,
++                                             const char *ext_member,
++                                             void *pvt);
++errno_t ipa_ext_group_member_recv(TALLOC_CTX *mem_ctx,
++                                  struct tevent_req *req,
++                                  enum sysdb_member_type *_member_type,
++                                  struct sss_domain_info **_dom,
++                                  struct sysdb_attrs **_member);
++
+ #endif /* _IPA_SUBDOMAINS_H_ */
+diff --git a/src/providers/ipa/ipa_subdomains_ext_groups.c b/src/providers/ipa/ipa_subdomains_ext_groups.c
+index d487a58b8adffabe09ff50e31cb750b800b1d252..5dc6d0d6417ec3fb5e7865e4cbaf3c07f4afbd07 100644
+--- a/src/providers/ipa/ipa_subdomains_ext_groups.c
++++ b/src/providers/ipa/ipa_subdomains_ext_groups.c
+@@ -923,3 +923,278 @@ static errno_t ipa_add_ad_memberships_recv(struct tevent_req *req,
+ 
+     return EOK;
+ }
++
++static errno_t
++search_user_or_group_by_sid_str(TALLOC_CTX *mem_ctx,
++                                struct sss_domain_info *domain,
++                                const char *sid_str,
++                                enum sysdb_member_type *_member_type,
++                                struct ldb_message **_msg)
++{
++    errno_t ret;
++    struct ldb_message *msg = NULL;
++    const char *attrs[] = { SYSDB_NAME,
++                            SYSDB_SID_STR,
++                            SYSDB_ORIG_DN,
++                            SYSDB_OBJECTCLASS,
++                            SYSDB_CACHE_EXPIRE,
++                            NULL };
++    TALLOC_CTX *tmp_ctx = NULL;
++    char *sanitized_sid = NULL;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
++        return ENOMEM;
++    }
++
++    /* In theory SID shouldn't contain any special LDAP characters, but let's
++     * be paranoid
++     */
++    ret = sss_filter_sanitize(tmp_ctx, sid_str, &sanitized_sid);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    ret = sysdb_search_user_by_sid_str(tmp_ctx, domain,
++                                       sid_str, attrs, &msg);
++    if (ret == EOK) {
++        *_member_type = SYSDB_MEMBER_USER;
++    } else if (ret == ENOENT) {
++        ret = sysdb_search_group_by_sid_str(tmp_ctx, domain,
++                                            sid_str, attrs, &msg);
++        if (ret == EOK) {
++            *_member_type = SYSDB_MEMBER_GROUP;
++        }
++    }
++
++    switch (ret) {
++    case EOK:
++        DEBUG(SSSDBG_TRACE_FUNC, "Found %s in sysdb\n", sid_str);
++        *_msg = talloc_steal(mem_ctx, msg);
++        break;
++    case ENOENT:
++        DEBUG(SSSDBG_TRACE_FUNC,
++              "Could not find %s in sysdb", sid_str);
++        break;
++    default:
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Error looking for %s in sysdb [%d]: %s\n",
++              sid_str, ret, sss_strerror(ret));
++        break;
++    }
++
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++static errno_t
++ipa_ext_group_member_check(TALLOC_CTX *mem_ctx,
++                           struct ipa_id_ctx *ipa_ctx,
++                           struct sss_domain_info *member_dom,
++                           const char *ext_member,
++                           enum sysdb_member_type *_member_type,
++                           struct sysdb_attrs **_member)
++{
++    TALLOC_CTX *tmp_ctx = NULL;
++    errno_t ret;
++    uint64_t expire;
++    time_t now = time(NULL);
++    struct ldb_message *msg;
++    struct sysdb_attrs **members;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
++        return ENOMEM;
++    }
++
++    ret = search_user_or_group_by_sid_str(tmp_ctx, member_dom, ext_member,
++                                          _member_type, &msg);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Error looking up sid %s: [%d]: %s\n",
++               ext_member, ret, sss_strerror(ret));
++        goto done;
++    }
++
++    ret = sysdb_msg2attrs(tmp_ctx, 1, &msg, &members);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Could not convert result to sysdb_attrs [%d]: %s\n",
++               ret, sss_strerror(ret));
++        goto done;
++    }
++
++    /* Return the member both expired and valid */
++    *_member = talloc_steal(mem_ctx, members[0]);
++
++    expire = ldb_msg_find_attr_as_uint64(msg, SYSDB_CACHE_EXPIRE, 0);
++    if (expire != 0 && expire <= now) {
++        DEBUG(SSSDBG_TRACE_FUNC, "%s is expired", ext_member);
++        ret = EAGAIN;
++        goto done;
++    }
++
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++/* For the IPA external member resolution, we expect a SID as the input.
++ * The _recv() function output is the member and a type (user/group)
++ * since nothing else can be a group member.
++ */
++struct ipa_ext_member_state {
++    const char *ext_member;
++    struct sss_domain_info *dom;
++
++    enum sysdb_member_type member_type;
++    struct sysdb_attrs *member;
++};
++
++static void ipa_ext_group_member_done(struct tevent_req *subreq);
++
++struct tevent_req *ipa_ext_group_member_send(TALLOC_CTX *mem_ctx,
++                                             struct tevent_context *ev,
++                                             const char *ext_member,
++                                             void *pvt)
++{
++    struct ipa_id_ctx *ipa_ctx;
++    struct ipa_ext_member_state *state;
++    struct tevent_req *req;
++    struct tevent_req *subreq;
++    struct be_acct_req *ar;
++    errno_t ret;
++
++    req = tevent_req_create(mem_ctx, &state, struct ipa_ext_member_state);
++    if (req == NULL) {
++        return NULL;
++    }
++    state->ext_member = ext_member;
++
++    ipa_ctx = talloc_get_type(pvt, struct ipa_id_ctx);
++    if (ipa_ctx == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Wrong private context!\n");
++        ret = EINVAL;
++        goto immediate;
++    }
++
++    state->dom = find_domain_by_sid(ipa_ctx->sdap_id_ctx->be->domain,
++                                    ext_member);
++    if (state->dom == NULL) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              "Cannot find domain of SID [%s]\n", ext_member);
++        ret = ENOENT;
++        goto immediate;
++    }
++
++    ret = ipa_ext_group_member_check(state, ipa_ctx, state->dom, ext_member,
++                                     &state->member_type, &state->member);
++    if (ret == EOK) {
++        DEBUG(SSSDBG_TRACE_INTERNAL,
++              "external member %s already cached\n", ext_member);
++        goto immediate;
++    }
++
++    ret = get_be_acct_req_for_sid(state, ext_member, state->dom->name, &ar);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              "Cannot create the account request for [%s]\n", ext_member);
++        goto immediate;
++    }
++
++    subreq = be_get_account_info_send(state, ev, NULL,
++                                      ipa_ctx->sdap_id_ctx->be, ar);
++    if (subreq == NULL) {
++        ret = ENOMEM;
++        goto immediate;
++    }
++    tevent_req_set_callback(subreq, ipa_ext_group_member_done, req);
++
++    return req;
++
++immediate:
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++    } else {
++        tevent_req_done(req);
++    }
++    tevent_req_post(req, ev);
++    return req;
++}
++
++static void ipa_ext_group_member_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct ipa_ext_member_state *state = tevent_req_data(req,
++                                                struct ipa_ext_member_state);
++    errno_t ret;
++    int err_maj;
++    int err_min;
++    const char *err_msg;
++    struct ldb_message *msg;
++    struct sysdb_attrs **members;
++
++    ret = be_get_account_info_recv(subreq, state,
++                                   &err_maj, &err_min, &err_msg);
++    talloc_free(subreq);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "be request failed %d:%d: %s\n", err_maj, err_min, err_msg);
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    ret = search_user_or_group_by_sid_str(state,
++                                          state->dom,
++                                          state->ext_member,
++                                          &state->member_type,
++                                          &msg);
++    if (ret != EOK) {
++        DEBUG(ret == ENOENT ? SSSDBG_TRACE_FUNC : SSSDBG_OP_FAILURE,
++              "Could not find %s in sysdb [%d]: %s\n",
++              state->ext_member, ret, sss_strerror(ret));
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    ret = sysdb_msg2attrs(state, 1, &msg, &members);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Could not convert result to sysdb_attrs [%d]: %s\n",
++               ret, sss_strerror(ret));
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    state->member = members[0];
++    tevent_req_done(req);
++}
++
++errno_t ipa_ext_group_member_recv(TALLOC_CTX *mem_ctx,
++                                  struct tevent_req *req,
++                                  enum sysdb_member_type *_member_type,
++                                  struct sss_domain_info **_dom,
++                                  struct sysdb_attrs **_member)
++{
++    struct ipa_ext_member_state *state = tevent_req_data(req,
++                                                struct ipa_ext_member_state);
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    if (_member_type != NULL) {
++        *_member_type = state->member_type;
++    }
++
++    if (_dom) {
++        *_dom = state->dom;
++    }
++
++    if (_member != NULL) {
++        *_member = talloc_steal(mem_ctx, state->member);
++    }
++
++    return EOK;
++}
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index 7acbb38e66c2c36ff230ae35b236544195a8104b..ebbf3be71d7488c0e5138183925668fc6187df97 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -1229,6 +1229,7 @@ static errno_t ipa_get_ad_apply_override_step(struct tevent_req *req)
+      * attributes set, i.e. where overrides might not have been applied. */
+     ret = sysdb_asq_search(state, state->obj_dom, state->obj_msg->dn,
+                           "(&("SYSDB_GC")("SYSDB_GIDNUM"=*)" \
++                            "("SYSDB_POSIX"=TRUE)" \
+                             "(!("ORIGINALAD_PREFIX SYSDB_GIDNUM"=*))" \
+                             "(!("ORIGINALAD_PREFIX SYSDB_NAME"=*)))",
+                           SYSDB_INITGR_ATTR,
+diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
+index d7053949f5804b637c27bb2d8e34991653770639..312789411fa273dd263bd6319f7a5ff45437d8d0 100644
+--- a/src/providers/ldap/sdap.h
++++ b/src/providers/ldap/sdap.h
+@@ -415,6 +415,26 @@ struct sdap_domain {
+     void *pvt;
+ };
+ 
++typedef struct tevent_req *
++(*ext_member_send_fn_t)(TALLOC_CTX *mem_ctx,
++                        struct tevent_context *ev,
++                        const char *ext_member,
++                        void *pvt);
++typedef errno_t
++(*ext_member_recv_fn_t)(TALLOC_CTX *mem_ctx,
++                        struct tevent_req *req,
++                        enum sysdb_member_type *member_type,
++                        struct sss_domain_info **_dom,
++                        struct sysdb_attrs **_member);
++
++struct sdap_ext_member_ctx {
++    /* Typically ID context of the external ID provider */
++    void *pvt;
++
++    ext_member_send_fn_t ext_member_resolve_send;
++    ext_member_recv_fn_t ext_member_resolve_recv;
++};
++
+ struct sdap_options {
+     struct dp_option *basic;
+     struct sdap_attr_map *gen_map;
+@@ -427,6 +447,9 @@ struct sdap_options {
+     /* ID-mapping support */
+     struct sdap_idmap_ctx *idmap_ctx;
+ 
++    /* Resolving external members */
++    struct sdap_ext_member_ctx *ext_ctx;
++
+     /* FIXME - should this go to a special struct to avoid mixing with name-service-switch maps? */
+     struct sdap_attr_map *sudorule_map;
+     struct sdap_attr_map *autofs_mobject_map;
+-- 
+2.4.3
+
diff --git a/SOURCES/0108-LDAP-Use-the-IPA-provider-interface-to-resolve-exter.patch b/SOURCES/0108-LDAP-Use-the-IPA-provider-interface-to-resolve-exter.patch
new file mode 100644
index 0000000..845a2c8
--- /dev/null
+++ b/SOURCES/0108-LDAP-Use-the-IPA-provider-interface-to-resolve-exter.patch
@@ -0,0 +1,916 @@
+From 3af57ba315eb5268c56919297e3a688c3f2f5b05 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 25 Jan 2016 16:13:03 +0100
+Subject: [PATCH 108/108] LDAP: Use the IPA provider interface to resolve
+ external group members
+
+Resolves:
+    https://fedorahosted.org/sssd/ticket/2522
+
+Currently the approach is not optimized for performance, because each
+external member is resolved in a full transaction to make sure even ID
+views and similar information is processed.
+
+In future, we should implement https://fedorahosted.org/sssd/ticket/2943
+we will again be able to process all the data in a single transaction.
+
+Reviewed-by: Sumit Bose <sbose@redhat.com>
+(cherry picked from commit c32266e79f9d4bebd0c31eaa8d6fa26050e7fb3e)
+(cherry picked from commit 19194cb18a1cc20f02423861dd831aa5bc3a1003)
+---
+ src/providers/ldap/sdap_async_groups.c        |  49 +-
+ src/providers/ldap/sdap_async_nested_groups.c | 618 +++++++++++++++++++++++++-
+ src/providers/ldap/sdap_async_private.h       |  16 +-
+ src/tests/cmocka/test_nested_groups.c         |   4 +-
+ 4 files changed, 659 insertions(+), 28 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
+index 57a53af3f4eb46e6f31af9ee7c4d4625239d2a54..485f79d2fb2e7e34f1420b43baadb40ab3f96d17 100644
+--- a/src/providers/ldap/sdap_async_groups.c
++++ b/src/providers/ldap/sdap_async_groups.c
+@@ -1728,6 +1728,7 @@ struct sdap_get_groups_state {
+     struct sysdb_attrs **groups;
+     size_t count;
+     size_t check_count;
++    hash_table_t *missing_external;
+ 
+     hash_table_t *user_hash;
+     hash_table_t *group_hash;
+@@ -2289,6 +2290,8 @@ int sdap_get_groups_recv(struct tevent_req *req,
+     return EOK;
+ }
+ 
++static void sdap_nested_ext_done(struct tevent_req *subreq);
++
+ static void sdap_nested_done(struct tevent_req *subreq)
+ {
+     errno_t ret, tret;
+@@ -2304,7 +2307,8 @@ static void sdap_nested_done(struct tevent_req *subreq)
+                                             struct sdap_get_groups_state);
+ 
+     ret = sdap_nested_group_recv(state, subreq, &user_count, &users,
+-                                 &group_count, &groups);
++                                 &group_count, &groups,
++                                 &state->missing_external);
+     talloc_zfree(subreq);
+     if (ret != EOK) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Nested group processing failed: [%d][%s]\n",
+@@ -2343,8 +2347,25 @@ static void sdap_nested_done(struct tevent_req *subreq)
+     }
+     in_transaction = false;
+ 
+-    /* Processing complete */
+-    tevent_req_done(req);
++    if (hash_count(state->missing_external) == 0) {
++        /* No external members. Processing complete */
++        DEBUG(SSSDBG_TRACE_INTERNAL, "No external members, done");
++        tevent_req_done(req);
++        return;
++    }
++
++    /* At the moment, we need to save the direct groups & members in one
++     * transaction and then query the others in a separate requests
++     */
++    subreq = sdap_nested_group_lookup_external_send(state, state->ev,
++                                                    state->dom,
++                                                    state->opts->ext_ctx,
++                                                    state->missing_external);
++    if (subreq == NULL) {
++        ret = ENOMEM;
++        goto fail;
++    }
++    tevent_req_set_callback(subreq, sdap_nested_ext_done, req);
+     return;
+ 
+ fail:
+@@ -2357,6 +2378,28 @@ fail:
+     tevent_req_error(req, ret);
+ }
+ 
++static void sdap_nested_ext_done(struct tevent_req *subreq)
++{
++    errno_t ret;
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct sdap_get_groups_state *state = tevent_req_data(req,
++                                            struct sdap_get_groups_state);
++
++    ret = sdap_nested_group_lookup_external_recv(state, subreq);
++    talloc_free(subreq);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "Cannot resolve external members [%d]: %s\n",
++              ret, sss_strerror(ret));
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    tevent_req_done(req);
++    return;
++}
++
+ static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
+                                                 struct sysdb_ctx *sysdb,
+                                                 struct sss_domain_info *domain,
+diff --git a/src/providers/ldap/sdap_async_nested_groups.c b/src/providers/ldap/sdap_async_nested_groups.c
+index 08e199869ad16c3b19d998a2a28eae9a0dd0a371..f5be6b5a5946453151918f97aa0632df4b218a29 100644
+--- a/src/providers/ldap/sdap_async_nested_groups.c
++++ b/src/providers/ldap/sdap_async_nested_groups.c
+@@ -55,6 +55,13 @@ struct sdap_nested_group_member {
+     const char *group_filter;
+ };
+ 
++const size_t external_members_chunk = 16;
++
++struct sdap_external_missing_member {
++    const char **parent_group_dns;
++    size_t parent_dn_idx;
++};
++
+ struct sdap_nested_group_ctx {
+     struct sss_domain_info *domain;
+     struct sdap_options *opts;
+@@ -63,6 +70,7 @@ struct sdap_nested_group_ctx {
+     struct sdap_handle *sh;
+     hash_table_t *users;
+     hash_table_t *groups;
++    hash_table_t *missing_external;
+     bool try_deref;
+     int deref_treshold;
+     int max_nesting_level;
+@@ -183,37 +191,32 @@ done:
+     return ret;
+ }
+ 
+-static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
+-                                            struct sysdb_attrs *entry,
+-                                            const char *table_name)
++static errno_t sdap_nested_group_hash_insert(hash_table_t *table,
++                                             const char *entry_key,
++                                             void *entry_value,
++                                             bool overwrite,
++                                             const char *table_name)
+ {
+     hash_key_t key;
+     hash_value_t value;
+-    const char *name = NULL;
+-    errno_t ret;
+     int hret;
+ 
+-    ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
+-    if (ret != EOK) {
+-        return ret;
+-    }
+-
+     DEBUG(SSSDBG_TRACE_ALL, "Inserting [%s] into hash table [%s]\n",
+-                             name, table_name);
++                             entry_key, table_name);
+ 
+     key.type = HASH_KEY_STRING;
+-    key.str = talloc_strdup(NULL, name);
++    key.str = talloc_strdup(NULL, entry_key);
+     if (key.str == NULL) {
+         return ENOMEM;
+     }
+ 
+-    if (hash_has_key(table, &key)) {
++    if (overwrite == false && hash_has_key(table, &key)) {
+         talloc_free(key.str);
+         return EEXIST;
+     }
+ 
+     value.type = HASH_VALUE_PTR;
+-    value.ptr = entry;
++    value.ptr = entry_value;
+ 
+     hret = hash_enter(table, &key, &value);
+     if (hret != HASH_SUCCESS) {
+@@ -227,6 +230,21 @@ static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
+     return EOK;
+ }
+ 
++static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
++                                            struct sysdb_attrs *entry,
++                                            const char *table_name)
++{
++    const char *name = NULL;
++    errno_t ret;
++
++    ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
++    if (ret != EOK) {
++        return ret;
++    }
++
++    return sdap_nested_group_hash_insert(table, name, entry, false, table_name);
++}
++
+ static errno_t
+ sdap_nested_group_hash_user(struct sdap_nested_group_ctx *group_ctx,
+                             struct sysdb_attrs *user)
+@@ -296,6 +314,76 @@ sdap_nested_group_hash_group(struct sdap_nested_group_ctx *group_ctx,
+     return sdap_nested_group_hash_entry(group_ctx->groups, group, "groups");
+ }
+ 
++static errno_t sdap_nested_group_external_add(hash_table_t *table,
++                                              const char *ext_member,
++                                              const char *parent_group_dn)
++{
++    hash_key_t key;
++    hash_value_t value;
++    int hret;
++    int ret;
++    struct sdap_external_missing_member *ext_mem;
++
++    key.type = HASH_KEY_STRING;
++    key.str = discard_const(ext_member);
++
++    DEBUG(SSSDBG_TRACE_ALL,
++          "Inserting external member [%s] into external members hash table\n",
++          ext_member);
++
++    hret = hash_lookup(table, &key, &value);
++    switch (hret) {
++    case HASH_ERROR_KEY_NOT_FOUND:
++        ext_mem = talloc_zero(table, struct sdap_external_missing_member);
++        if (ext_mem == NULL) {
++            return ENOMEM;
++        }
++        ext_mem->parent_group_dns = talloc_zero_array(ext_mem,
++                                                      const char *,
++                                                      external_members_chunk);
++        if (ext_mem->parent_group_dns == NULL) {
++            talloc_free(ext_mem);
++            return ENOMEM;
++        }
++
++        ret = sdap_nested_group_hash_insert(table, ext_member, ext_mem,
++                                            true, "missing external users");
++        if (ret != EOK) {
++            return ret;
++        }
++        break;
++
++    case HASH_SUCCESS:
++        ext_mem = talloc_get_type(value.ptr,
++                                  struct sdap_external_missing_member);
++        if (ext_mem->parent_dn_idx == \
++                talloc_array_length(ext_mem->parent_group_dns)) {
++            ext_mem->parent_group_dns = talloc_realloc(ext_mem,
++                                                ext_mem->parent_group_dns,
++                                                const char *,
++                                                ext_mem->parent_dn_idx + \
++                                                    external_members_chunk);
++            if (ext_mem->parent_group_dns == NULL) {
++                talloc_free(ext_mem);
++                return ENOMEM;
++            }
++        }
++        break;
++    default:
++        return EIO;
++    }
++
++    ext_mem->parent_group_dns[ext_mem->parent_dn_idx] = \
++                                        talloc_strdup(ext_mem->parent_group_dns,
++                                                      parent_group_dn);
++    if (ext_mem->parent_group_dns[ext_mem->parent_dn_idx] == NULL) {
++        return ENOMEM;
++    }
++    ext_mem->parent_dn_idx++;
++
++    return EOK;
++}
++
+ static errno_t sdap_nested_group_sysdb_search(struct sss_domain_info *domain,
+                                               const char *filter,
+                                               bool user)
+@@ -477,6 +565,13 @@ sdap_nested_group_split_members(TALLOC_CTX *mem_ctx,
+     errno_t ret;
+     int i;
+ 
++    if (members == NULL) {
++        *_missing = NULL;
++        *_num_missing = 0;
++        *_num_groups = 0;
++        return EOK;
++    }
++
+     tmp_ctx = talloc_new(NULL);
+     if (tmp_ctx == NULL) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+@@ -618,6 +713,66 @@ done:
+     return ret;
+ }
+ 
++static errno_t
++sdap_nested_group_add_ext_members(TALLOC_CTX *mem_ctx,
++                                  struct sdap_nested_group_ctx *group_ctx,
++                                  struct sysdb_attrs *group,
++                                  struct ldb_message_element *ext_members)
++{
++    errno_t ret;
++    const char *ext_member_attr;
++    const char *orig_dn;
++    size_t i;
++
++    if (ext_members == NULL) {
++        return EOK;
++    }
++
++    ret = sysdb_attrs_get_string(group, SYSDB_ORIG_DN, &orig_dn);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "A group with no originalDN!?!\n");
++        return ret;
++    }
++
++    for (i = 0; i < ext_members->num_values; i++) {
++        ext_member_attr = (const char *) ext_members->values[i].data;
++
++        ret = sdap_nested_group_external_add(group_ctx->missing_external,
++                                             ext_member_attr,
++                                             orig_dn);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                    "Cannot add %s into external members [%d]: %s\n",
++                    ext_member_attr, ret, sss_strerror(ret));
++            return ret;
++        }
++    }
++
++    return EOK;
++}
++
++static struct ldb_message_element *
++sdap_nested_group_ext_members(struct sdap_options *opts,
++                              struct sysdb_attrs *group)
++{
++    errno_t ret;
++    struct ldb_message_element *ext_members = NULL;
++
++    if (opts->ext_ctx == NULL) {
++        return NULL;
++    }
++
++    ret = sysdb_attrs_get_el_ext(group,
++                 opts->group_map[SDAP_AT_GROUP_EXT_MEMBER].sys_name,
++                 false, &ext_members);
++    if (ret != EOK && ret != ENOENT) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve external member list "
++                                   "[%d]: %s\n", ret, sss_strerror(ret));
++    }
++
++    return ext_members;
++}
++
+ 
+ struct sdap_nested_group_state {
+     struct sdap_nested_group_ctx *group_ctx;
+@@ -666,6 +821,14 @@ sdap_nested_group_send(TALLOC_CTX *mem_ctx,
+         goto immediately;
+     }
+ 
++    ret = sss_hash_create(state->group_ctx, 32,
++                          &state->group_ctx->missing_external);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
++                                    ret, strerror(ret));
++        goto immediately;
++    }
++
+     state->group_ctx->try_deref = true;
+     state->group_ctx->deref_treshold = dp_opt_get_int(opts->basic,
+                                                       SDAP_DEREF_THRESHOLD);
+@@ -759,7 +922,8 @@ errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
+                                unsigned long *_num_users,
+                                struct sysdb_attrs ***_users,
+                                unsigned long *_num_groups,
+-                               struct sysdb_attrs ***_groups)
++                               struct sysdb_attrs ***_groups,
++                               hash_table_t **_missing_external)
+ {
+     struct sdap_nested_group_state *state = NULL;
+     struct sysdb_attrs **users = NULL;
+@@ -806,6 +970,11 @@ errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
+         *_groups = talloc_steal(mem_ctx, groups);
+     }
+ 
++    if (_missing_external) {
++        *_missing_external = talloc_steal(mem_ctx,
++                                          state->group_ctx->missing_external);
++    }
++
+     return EOK;
+ }
+ 
+@@ -815,6 +984,7 @@ struct sdap_nested_group_process_state {
+     struct sdap_nested_group_member *missing;
+     int num_missing_total;
+     int num_missing_groups;
++    struct ldb_message_element *ext_members;
+     int nesting_level;
+     char *group_dn;
+     bool deref;
+@@ -865,13 +1035,16 @@ sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
+ 
+     DEBUG(SSSDBG_TRACE_INTERNAL, "About to process group [%s]\n", orig_dn);
+ 
+-    /* get member list */
++    /* get member list, both direct and external */
++    state->ext_members = sdap_nested_group_ext_members(state->group_ctx->opts,
++                                                       group);
++
+     ret = sysdb_attrs_get_el_ext(group, group_map[SDAP_AT_GROUP_MEMBER].sys_name,
+                                  false, &members);
+-    if (ret == ENOENT) {
+-        ret = EOK; /* no members */
++    if (ret == ENOENT && state->ext_members == NULL) {
++        ret = EOK; /* no members, direct or external */
+         goto immediately;
+-    } else if (ret != EOK) {
++    } else if (ret != EOK && ret != ENOENT) {
+         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve member list "
+                                     "[%d]: %s\n", ret, strerror(ret));
+         goto immediately;
+@@ -889,14 +1062,31 @@ sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
+         goto immediately;
+     }
+ 
+-    DEBUG(SSSDBG_TRACE_INTERNAL, "Looking up %d/%d members of group [%s]\n",
+-          state->num_missing_total, members->num_values, orig_dn);
++    ret = sdap_nested_group_add_ext_members(state,
++                                            state->group_ctx,
++                                            group,
++                                            state->ext_members);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to split external member list "
++                                    "[%d]: %s\n", ret, sss_strerror(ret));
++        goto immediately;
++    }
+ 
+-    if (state->num_missing_total == 0) {
++    if (state->num_missing_total == 0
++            && hash_count(state->group_ctx->missing_external) == 0) {
+         ret = EOK; /* we're done */
+         goto immediately;
+     }
+ 
++    /* If there are only indirect members of the group, it's still safe to
++     * proceed and let the direct lookup code just fall through.
++     */
++
++    DEBUG(SSSDBG_TRACE_INTERNAL, "Looking up %d/%d members of group [%s]\n",
++                                 state->num_missing_total,
++                                 members ? members->num_values : 0,
++                                 orig_dn);
++
+     /* process members */
+     if (group_ctx->try_deref
+             && state->num_missing_total > group_ctx->deref_treshold) {
+@@ -2332,3 +2522,387 @@ static errno_t sdap_nested_group_deref_recv(struct tevent_req *req)
+ 
+     return EOK;
+ }
++
++struct sdap_ext_member {
++    struct sdap_external_missing_member *missing_mem;
++    const char *ext_member_attr;
++
++    enum sysdb_member_type member_type;
++    struct sss_domain_info *dom;
++    struct sysdb_attrs *attrs;
++};
++
++struct sdap_nested_group_lookup_external_state {
++    struct tevent_context *ev;
++    struct sdap_ext_member_ctx *ext_ctx;
++    struct sss_domain_info *group_dom;
++    hash_table_t *missing_external;
++
++    hash_entry_t *entries;
++    unsigned long n_entries;
++    unsigned long eniter;
++
++    struct sdap_ext_member *ext_members;
++
++    ext_member_send_fn_t ext_member_resolve_send;
++    ext_member_recv_fn_t ext_member_resolve_recv;
++};
++
++static errno_t
++sdap_nested_group_lookup_external_step(struct tevent_req *req);
++static void
++sdap_nested_group_lookup_external_done(struct tevent_req *subreq);
++static errno_t
++sdap_nested_group_lookup_external_link(struct tevent_req *req);
++static errno_t
++sdap_nested_group_lookup_external_link_member(
++                        struct sdap_nested_group_lookup_external_state *state,
++                        struct sdap_ext_member *member);
++static errno_t
++sdap_nested_group_memberof_dn_by_original_dn(
++                            TALLOC_CTX *mem_ctx,
++                            struct sss_domain_info *group_dom,
++                            const char *original_dn,
++                            const char ***_parents);
++
++struct tevent_req *
++sdap_nested_group_lookup_external_send(TALLOC_CTX *mem_ctx,
++                                       struct tevent_context *ev,
++                                       struct sss_domain_info *group_dom,
++                                       struct sdap_ext_member_ctx *ext_ctx,
++                                       hash_table_t *missing_external)
++{
++    struct sdap_nested_group_lookup_external_state *state = NULL;
++    struct tevent_req *req = NULL;
++    errno_t ret;
++
++    req = tevent_req_create(mem_ctx, &state,
++                            struct sdap_nested_group_lookup_external_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
++        return NULL;
++    }
++
++    state->ev = ev;
++    state->group_dom = group_dom;
++    state->ext_ctx = ext_ctx;
++    state->missing_external = missing_external;
++
++    if (state->ext_ctx->ext_member_resolve_send == NULL
++            || state->ext_ctx->ext_member_resolve_recv == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Wrong private context\n");
++        ret = EINVAL;
++        goto immediately;
++    }
++
++    ret = hash_entries(state->missing_external,
++                       &state->n_entries, &state->entries);
++    if (ret != HASH_SUCCESS) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "hash_entries returned %d\n", ret);
++        ret = EIO;
++        goto immediately;
++    }
++    state->eniter = 0;
++
++    state->ext_members = talloc_zero_array(state,
++                                           struct sdap_ext_member,
++                                           state->n_entries);
++    if (state->ext_members == NULL) {
++        ret = ENOMEM;
++        goto immediately;
++    }
++
++    ret = sdap_nested_group_lookup_external_step(req);
++    if (ret != EAGAIN) {
++        goto immediately;
++    }
++
++    return req;
++
++immediately:
++    if (ret == EOK) {
++        tevent_req_done(req);
++    } else {
++        tevent_req_error(req, ret);
++    }
++    tevent_req_post(req, ev);
++    return req;
++}
++
++static errno_t
++sdap_nested_group_lookup_external_step(struct tevent_req *req)
++{
++    struct tevent_req *subreq = NULL;
++    struct sdap_nested_group_lookup_external_state *state = NULL;
++    state = tevent_req_data(req,
++                            struct sdap_nested_group_lookup_external_state);
++
++    subreq = state->ext_ctx->ext_member_resolve_send(state,
++                                        state->ev,
++                                        state->entries[state->eniter].key.str,
++                                        state->ext_ctx->pvt);
++    if (subreq == NULL) {
++        return ENOMEM;
++    }
++    DEBUG(SSSDBG_TRACE_FUNC, "Refreshing member %lu/%lu\n",
++                             state->eniter, state->n_entries);
++    tevent_req_set_callback(subreq,
++                            sdap_nested_group_lookup_external_done,
++                            req);
++
++    return EAGAIN;
++}
++
++static void
++sdap_nested_group_lookup_external_done(struct tevent_req *subreq)
++{
++    errno_t ret;
++    struct tevent_req *req = NULL;
++    struct sdap_nested_group_lookup_external_state *state = NULL;
++    enum sysdb_member_type member_type;
++    struct sysdb_attrs *member;
++    struct sss_domain_info *member_dom;
++
++    req = tevent_req_callback_data(subreq, struct tevent_req);
++    state = tevent_req_data(req,
++                            struct sdap_nested_group_lookup_external_state);
++
++    ret = state->ext_ctx->ext_member_resolve_recv(state, subreq,
++                                                  &member_type,
++                                                  &member_dom,
++                                                  &member);
++    talloc_free(subreq);
++    if (ret == EOK) {
++        DEBUG(SSSDBG_TRACE_FUNC, "Refreshing member %lu\n", state->eniter);
++        state->ext_members[state->eniter].missing_mem = \
++                                    state->entries[state->eniter].value.ptr;
++        state->ext_members[state->eniter].dom = member_dom;
++
++        state->ext_members[state->eniter].ext_member_attr = \
++                        talloc_steal(state->ext_members,
++                                     state->entries[state->eniter].key.str);
++        state->ext_members[state->eniter].member_type = member_type;
++        state->ext_members[state->eniter].attrs = \
++                            talloc_steal(state->ext_members, member);
++    }
++
++    state->eniter++;
++    if (state->eniter >= state->n_entries) {
++        DEBUG(SSSDBG_TRACE_FUNC, "All external members processed\n");
++        ret = sdap_nested_group_lookup_external_link(req);
++        if (ret != EOK) {
++            tevent_req_error(req, ret);
++            return;
++        }
++        tevent_req_done(req);
++        return;
++    }
++
++    ret = sdap_nested_group_lookup_external_step(req);
++    if (ret != EOK && ret != EAGAIN) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    return;
++}
++
++static errno_t
++sdap_nested_group_lookup_external_link(struct tevent_req *req)
++{
++    errno_t ret, tret;
++    bool in_transaction = false;
++    struct sdap_nested_group_lookup_external_state *state = NULL;
++    state = tevent_req_data(req,
++                            struct sdap_nested_group_lookup_external_state);
++    size_t i;
++
++    ret = sysdb_transaction_start(state->group_dom->sysdb);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
++        goto fail;
++    }
++    in_transaction = true;
++
++
++    for (i = 0; i < state->eniter; i++) {
++        if (state->ext_members[i].attrs == NULL) {
++            DEBUG(SSSDBG_MINOR_FAILURE, "The member %s could not be resolved\n",
++                                        state->ext_members[i].ext_member_attr);
++            continue;
++        }
++
++        ret = sdap_nested_group_lookup_external_link_member(state,
++                                                    &state->ext_members[i]);
++        if (ret != EOK) {
++            goto fail;
++        }
++    }
++
++    ret = sysdb_transaction_commit(state->group_dom->sysdb);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
++        goto fail;
++    }
++    in_transaction = false;
++
++    return EOK;
++
++fail:
++    if (in_transaction) {
++        tret = sysdb_transaction_cancel(state->group_dom->sysdb);
++        if (tret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
++        }
++    }
++    return EFAULT;
++}
++
++static errno_t
++sdap_nested_group_lookup_external_link_member(
++                        struct sdap_nested_group_lookup_external_state *state,
++                        struct sdap_ext_member *member)
++{
++    const char *name;
++    int ret;
++    const char **parents = NULL;
++    size_t i;
++    TALLOC_CTX *tmp_ctx;
++    const char *orig_dn;
++
++    tmp_ctx = talloc_new(state);
++    if (tmp_ctx == NULL) {
++        return ENOMEM;
++    }
++
++    ret = sysdb_attrs_get_string(member->attrs, SYSDB_NAME, &name);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "No name for a user\n");
++        goto done;
++    }
++
++    /* This only works because the groups were saved in a previous
++     * transaction */
++    for (i=0; i < member->missing_mem->parent_dn_idx; i++) {
++        orig_dn = member->missing_mem->parent_group_dns[i];
++        DEBUG(SSSDBG_TRACE_INTERNAL,
++              "Linking external members %s from domain %s to parents of %s\n",
++              name, member->dom->name, orig_dn);
++        ret = sdap_nested_group_memberof_dn_by_original_dn(tmp_ctx,
++                                                           state->group_dom,
++                                                           orig_dn,
++                                                           &parents);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_MINOR_FAILURE,
++                  "Cannot find parents of %s\n", orig_dn);
++            continue;
++        }
++
++        /* We don't have to remove the members here, since all members attributes
++         * are always written anew
++         */
++        ret = sysdb_update_members_dn(member->dom, name, member->member_type,
++                                      parents, NULL);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_CRIT_FAILURE, "Cannot link %s@%s to its parents\n",
++                                       name, member->dom->name);
++            goto done;
++        }
++
++    }
++
++    ret = EOK;
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++static errno_t
++sdap_nested_group_memberof_dn_by_original_dn(
++                            TALLOC_CTX *mem_ctx,
++                            struct sss_domain_info *group_dom,
++                            const char *original_dn,
++                            const char ***_parents)
++{
++    errno_t ret;
++    char *sanitized_dn;
++    char *filter;
++    const char *attrs[] = { SYSDB_NAME,
++                            SYSDB_MEMBEROF,
++                            NULL };
++    struct ldb_message **msgs = NULL;
++    size_t count;
++    TALLOC_CTX *tmp_ctx;
++    struct ldb_message_element *memberof;
++    const char **parents;
++    size_t i;
++
++    tmp_ctx = talloc_new(mem_ctx);
++    if (tmp_ctx == NULL) {
++        return ENOMEM;
++    }
++
++    ret = sss_filter_sanitize(tmp_ctx, original_dn, &sanitized_dn);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++                "Cannot sanitize originalDN [%s]\n", original_dn);
++        goto done;
++    }
++
++    filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, sanitized_dn);
++    if (filter == NULL) {
++        goto done;
++    }
++
++    ret = sysdb_search_groups(tmp_ctx, group_dom, filter, attrs,
++                              &count, &msgs);
++    if (ret != EOK) {
++        goto done;
++    }
++
++    if (count != 1) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "More than one entry found by originalDN?\n");
++        goto done;
++    }
++
++    memberof = ldb_msg_find_element(msgs[0], SYSDB_MEMBEROF);
++    if (memberof == NULL || memberof->num_values == 0) {
++        DEBUG(SSSDBG_MINOR_FAILURE,
++              "The external group is not a member of any groups\n");
++        ret = ENOENT;
++        goto done;
++    }
++
++    parents = talloc_zero_array(tmp_ctx,
++                                const char *,
++                                memberof->num_values + 1);
++    if (parents == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    for (i = 0; i < memberof->num_values; i++) {
++        parents[i] = talloc_strdup(parents,
++                                   (const char *) memberof->values[i].data);
++        if (parents[i] == NULL) {
++            ret = ENOMEM;
++            goto done;
++        }
++    }
++
++    *_parents = talloc_steal(mem_ctx, parents);
++    ret = EOK;
++done:
++    talloc_free(tmp_ctx);
++    return ret;
++}
++
++errno_t
++sdap_nested_group_lookup_external_recv(TALLOC_CTX *mem_ctx,
++                                       struct tevent_req *req)
++{
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++
++    return EOK;
++}
+diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
+index db542eaf869efcd53d0937bef3fc6e99cc78b938..9cde6f5dfe0114f797135b4989b9a4bd336a3f27 100644
+--- a/src/providers/ldap/sdap_async_private.h
++++ b/src/providers/ldap/sdap_async_private.h
+@@ -130,8 +130,20 @@ errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
+                                unsigned long *_num_users,
+                                struct sysdb_attrs ***_users,
+                                unsigned long *_num_groups,
+-                               struct sysdb_attrs ***_groups);
++                               struct sysdb_attrs ***_groups,
++                               hash_table_t **missing_external);
+ 
++struct tevent_req *
++sdap_nested_group_lookup_external_send(TALLOC_CTX *mem_ctx,
++                                       struct tevent_context *ev,
++                                       struct sss_domain_info *group_dom,
++                                       struct sdap_ext_member_ctx *ext_ctx,
++                                       hash_table_t *missing_external);
++errno_t
++sdap_nested_group_lookup_external_recv(TALLOC_CTX *mem_ctx,
++                                       struct tevent_req *req);
++
++/* from sdap_async_initgroups.c */
+ errno_t sdap_add_incomplete_groups(struct sysdb_ctx *sysdb,
+                                    struct sss_domain_info *domain,
+                                    struct sdap_options *opts,
+@@ -139,7 +151,7 @@ errno_t sdap_add_incomplete_groups(struct sysdb_ctx *sysdb,
+                                    struct sysdb_attrs **ldap_groups,
+                                    int ldap_groups_count);
+ 
+-/* from sdap_async_nested_groups.c */
++/* from sdap_ad_groups.c */
+ errno_t sdap_check_ad_group_type(struct sss_domain_info *dom,
+                                  struct sdap_options *opts,
+                                  struct sysdb_attrs *group_attrs,
+diff --git a/src/tests/cmocka/test_nested_groups.c b/src/tests/cmocka/test_nested_groups.c
+index 8081ff26102e53b2e453838c3a18e4560ac5317e..22b8caefc92a44a2339cbdcaa98226ebb4dc1ef5 100644
+--- a/src/tests/cmocka/test_nested_groups.c
++++ b/src/tests/cmocka/test_nested_groups.c
+@@ -57,6 +57,7 @@ struct nested_groups_test_ctx {
+     struct sdap_domain *sdap_domain;
+     struct sdap_idmap_ctx *idmap_ctx;
+     struct sdap_id_ctx *sdap_id_ctx;
++    hash_table_t *missing_external;
+ 
+     struct sysdb_attrs **users;
+     struct sysdb_attrs **groups;
+@@ -110,7 +111,8 @@ static void nested_groups_test_done(struct tevent_req *req)
+ 
+     ctx->tctx->error = sdap_nested_group_recv(ctx, req,
+                                               &ctx->num_users, &ctx->users,
+-                                              &ctx->num_groups, &ctx->groups);
++                                              &ctx->num_groups, &ctx->groups,
++                                              &ctx->missing_external);
+     talloc_zfree(req);
+ 
+     ctx->tctx->done = true;
+-- 
+2.4.3
+
diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec
index 1808930..7423742 100644
--- a/SPECS/sssd.spec
+++ b/SPECS/sssd.spec
@@ -25,7 +25,7 @@
 
 Name: sssd
 Version: 1.13.0
-Release: 40%{?dist}.1
+Release: 40%{?dist}.2
 Group: Applications/System
 Summary: System Security Services Daemon
 License: GPLv3+
@@ -141,6 +141,9 @@ Patch0102:  0102-nss-send-original-name-and-id-with-local-views-if-po.patch
 Patch0103:  0103-sudo-search-with-view-even-if-user-is-found.patch
 Patch0104:  0104-sudo-send-original-name-and-id-with-local-views-if-p.patch
 Patch0105:  0105-IPA-fix-override-with-the-same-name.patch
+Patch0106:  0106-Add-a-new-option-ldap_group_external_member.patch
+Patch0107:  0107-IPA-Add-interface-to-call-into-IPA-provider-from-LDA.patch
+Patch0108:  0108-LDAP-Use-the-IPA-provider-interface-to-resolve-exter.patch
 
 #This patch should not be removed in RHEL-7
 Patch999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec
@@ -1073,7 +1076,11 @@ fi
 /usr/bin/rm -f /var/tmp/sssd.upgrade || :
 
 %changelog
-* Tue Nov 24 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-41
+* Wed Feb 24 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-40.2
+- Resolves: rhbz#1311569 - [RFE] IPA: resolve external group memberships
+                           of IPA groups during getgrnam and getgrgid
+
+* Tue Nov 24 2015 Jakub Hrozek <jhrozek@redhat.com> - 1.13.0-40.1
 - Resolves: rhbz#1284814  - sssd: [sysdb_add_user] (0x0400): Error: 17
                            (File exists)