diff --git a/SOURCES/0144-sysdb-add-parent_dom-to-sysdb_get_direct_parents.patch b/SOURCES/0144-sysdb-add-parent_dom-to-sysdb_get_direct_parents.patch
new file mode 100644
index 0000000..7cf09f2
--- /dev/null
+++ b/SOURCES/0144-sysdb-add-parent_dom-to-sysdb_get_direct_parents.patch
@@ -0,0 +1,124 @@
+From b368dca11e715400da64348a17049abf5b072f57 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 18 Oct 2016 14:59:19 +0200
+Subject: [PATCH 144/151] sysdb: add parent_dom to sysdb_get_direct_parents()
+
+Currently sysdb_get_direct_parents() only return direct parents from the
+same domain as the child object. In setups with sub-domains this might
+not be sufficient. A new option parent_dom is added which allows to
+specify a domain the direct parents should be lookup up in. If it is
+NULL the whole cache is searched.
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 3dd4c3eca80e9223a65f3318821bd0fb5b45aedd)
+---
+ src/db/sysdb.h                             | 21 +++++++++++++++++++++
+ src/db/sysdb_search.c                      |  7 ++++++-
+ src/providers/ldap/sdap_async_initgroups.c | 11 +++++++----
+ 3 files changed, 34 insertions(+), 5 deletions(-)
+
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 8713efa6e8fcc6fb620340fe152989a5dae58434..4164657c2b329a240d46fe3ecdfb4b2eefffc5b3 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -1135,8 +1135,29 @@ errno_t sysdb_remove_attrs(struct sss_domain_info *domain,
+                            enum sysdb_member_type type,
+                            char **remove_attrs);
+ 
++/**
++ * @brief Return direct parents of an object in the cache
++ *
++ * @param[in]  mem_ctx         Memory context the result should be allocated
++ *                             on
++ * @param[in]  dom             domain the object is in
++ * @param[in]  parent_dom      domain which should be searched for direct
++ *                             parents if NULL all domains in the given cache
++ *                             are searched
++ * @param[in]  mtype           Type of the object, SYSDB_MEMBER_USER or
++ *                             SYSDB_MEMBER_GROUP
++ * @param[in]  name            Name of the object
++ * @param[out] _direct_parents List of names of the direct parent groups
++ *
++ *
++ * @return
++ *  - EOK:    success
++ *  - EINVAL: wrong mtype
++ *  - ENOMEM: Memory allocation failed
++ */
+ errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
+                                  struct sss_domain_info *dom,
++                                 struct sss_domain_info *parent_dom,
+                                  enum sysdb_member_type mtype,
+                                  const char *name,
+                                  char ***_direct_parents);
+diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
+index cfee5784dbadd692f30d0758e7e5c3c9fb2814cb..4d63c3838a49392bbf2a57aeb6f7740f4d4fbdcd 100644
+--- a/src/db/sysdb_search.c
++++ b/src/db/sysdb_search.c
+@@ -1981,6 +1981,7 @@ done:
+ 
+ errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
+                                  struct sss_domain_info *dom,
++                                 struct sss_domain_info *parent_dom,
+                                  enum sysdb_member_type mtype,
+                                  const char *name,
+                                  char ***_direct_parents)
+@@ -2029,7 +2030,11 @@ errno_t sysdb_get_direct_parents(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    basedn = sysdb_group_base_dn(tmp_ctx, dom);
++    if (parent_dom == NULL) {
++        basedn = sysdb_base_dn(dom->sysdb, tmp_ctx);
++    } else {
++        basedn = sysdb_group_base_dn(tmp_ctx, parent_dom);
++    }
+     if (!basedn) {
+         ret = ENOMEM;
+         goto done;
+diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
+index f9593f0dfaa2dc6e33fd6c9d1f0c9b78cad3a1d9..77324d0ee9eb2ad2fc35c2098d6c9c23a62747c9 100644
+--- a/src/providers/ldap/sdap_async_initgroups.c
++++ b/src/providers/ldap/sdap_async_initgroups.c
+@@ -1301,7 +1301,8 @@ sdap_initgr_store_user_memberships(struct sdap_initgr_nested_state *state)
+         }
+     }
+ 
+-    ret = sysdb_get_direct_parents(tmp_ctx, state->dom, SYSDB_MEMBER_USER,
++    ret = sysdb_get_direct_parents(tmp_ctx, state->dom, state->dom,
++                                   SYSDB_MEMBER_USER,
+                                    state->username, &sysdb_parent_name_list);
+     if (ret) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+@@ -1388,7 +1389,7 @@ sdap_initgr_nested_get_membership_diff(TALLOC_CTX *mem_ctx,
+         goto done;
+     }
+ 
+-    ret = sysdb_get_direct_parents(tmp_ctx, dom, SYSDB_MEMBER_GROUP,
++    ret = sysdb_get_direct_parents(tmp_ctx, dom, dom, SYSDB_MEMBER_GROUP,
+                                    group_name, &sysdb_parents_names_list);
+     if (ret) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+@@ -2070,7 +2071,8 @@ rfc2307bis_group_memberships_build(hash_entry_t *item, void *user_data)
+         goto done;
+     }
+ 
+-    ret = sysdb_get_direct_parents(tmp_ctx, mstate->dom, SYSDB_MEMBER_GROUP,
++    ret = sysdb_get_direct_parents(tmp_ctx, mstate->dom, mstate->dom,
++                                   SYSDB_MEMBER_GROUP,
+                                    group_name, &sysdb_parents_names_list);
+     if (ret) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+@@ -2130,7 +2132,8 @@ errno_t save_rfc2307bis_user_memberships(
+     }
+     in_transaction = true;
+ 
+-    ret = sysdb_get_direct_parents(tmp_ctx, state->dom, SYSDB_MEMBER_USER,
++    ret = sysdb_get_direct_parents(tmp_ctx, state->dom, state->dom,
++                                   SYSDB_MEMBER_USER,
+                                    state->name, &sysdb_parent_name_list);
+     if (ret) {
+         DEBUG(SSSDBG_CRIT_FAILURE,
+-- 
+2.7.4
+
diff --git a/SOURCES/0145-sdap-make-some-nested-group-related-calls-public.patch b/SOURCES/0145-sdap-make-some-nested-group-related-calls-public.patch
new file mode 100644
index 0000000..abba383
--- /dev/null
+++ b/SOURCES/0145-sdap-make-some-nested-group-related-calls-public.patch
@@ -0,0 +1,80 @@
+From f0e6ed1942f1a4263a88a0c02d866d616448668f Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 18 Oct 2016 18:16:30 +0200
+Subject: [PATCH 145/151] sdap: make some nested group related calls public
+
+sdap_nested_groups_store() and rfc2307bis_nested_groups_send/recv() will
+be reused for domain local group lookups.
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 49d3f0a487d55571b2bdc9d3f8280b304b964b9d)
+---
+ src/providers/ldap/sdap_async_initgroups.c | 12 ++----------
+ src/providers/ldap/sdap_async_private.h    | 16 ++++++++++++++++
+ 2 files changed, 18 insertions(+), 10 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
+index 77324d0ee9eb2ad2fc35c2098d6c9c23a62747c9..f1ba65a98b07a593b0e3a722d0b2c56c8e4b821e 100644
+--- a/src/providers/ldap/sdap_async_initgroups.c
++++ b/src/providers/ldap/sdap_async_initgroups.c
+@@ -622,7 +622,7 @@ static int sdap_initgr_rfc2307_recv(struct tevent_req *req)
+ }
+ 
+ /* ==Common code for pure RFC2307bis and IPA/AD========================= */
+-static errno_t
++errno_t
+ sdap_nested_groups_store(struct sysdb_ctx *sysdb,
+                          struct sss_domain_info *domain,
+                          struct sdap_options *opts,
+@@ -1558,14 +1558,6 @@ static void sdap_initgr_rfc2307bis_process(struct tevent_req *subreq);
+ static void sdap_initgr_rfc2307bis_done(struct tevent_req *subreq);
+ errno_t save_rfc2307bis_user_memberships(
+         struct sdap_initgr_rfc2307bis_state *state);
+-struct tevent_req *rfc2307bis_nested_groups_send(
+-        TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+-        struct sdap_options *opts, struct sysdb_ctx *sysdb,
+-        struct sss_domain_info *dom, struct sdap_handle *sh,
+-        struct sdap_search_base **search_bases,
+-        struct sysdb_attrs **groups, size_t num_groups,
+-        hash_table_t *group_hash, size_t nesting);
+-static errno_t rfc2307bis_nested_groups_recv(struct tevent_req *req);
+ 
+ static struct tevent_req *sdap_initgr_rfc2307bis_send(
+         TALLOC_CTX *memctx,
+@@ -2616,7 +2608,7 @@ static void rfc2307bis_nested_groups_process(struct tevent_req *subreq)
+     tevent_req_set_callback(subreq, rfc2307bis_nested_groups_done, req);
+ }
+ 
+-static errno_t rfc2307bis_nested_groups_recv(struct tevent_req *req)
++errno_t rfc2307bis_nested_groups_recv(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 f09ddb71f48080251e61b8a850ccb8c9b5058331..4af4f7144d8855e4ed705f6a64e0a7818bc0b9a9 100644
+--- a/src/providers/ldap/sdap_async_private.h
++++ b/src/providers/ldap/sdap_async_private.h
+@@ -157,4 +157,20 @@ errno_t sdap_check_ad_group_type(struct sss_domain_info *dom,
+                                  struct sysdb_attrs *group_attrs,
+                                  const char *group_name,
+                                  bool *_need_filter);
++
++struct tevent_req *rfc2307bis_nested_groups_send(
++        TALLOC_CTX *mem_ctx, struct tevent_context *ev,
++        struct sdap_options *opts, struct sysdb_ctx *sysdb,
++        struct sss_domain_info *dom, struct sdap_handle *sh,
++        struct sdap_search_base **search_bases,
++        struct sysdb_attrs **groups, size_t num_groups,
++        hash_table_t *group_hash, size_t nesting);
++errno_t rfc2307bis_nested_groups_recv(struct tevent_req *req);
++
++errno_t sdap_nested_groups_store(struct sysdb_ctx *sysdb,
++                                 struct sss_domain_info *domain,
++                                 struct sdap_options *opts,
++                                 struct sysdb_attrs **groups,
++                                 unsigned long count);
++
+ #endif /* _SDAP_ASYNC_PRIVATE_H_ */
+-- 
+2.7.4
+
diff --git a/SOURCES/0146-LDAP-AD-resolve-domain-local-groups-for-remote-users.patch b/SOURCES/0146-LDAP-AD-resolve-domain-local-groups-for-remote-users.patch
new file mode 100644
index 0000000..3e2d38d
--- /dev/null
+++ b/SOURCES/0146-LDAP-AD-resolve-domain-local-groups-for-remote-users.patch
@@ -0,0 +1,683 @@
+From 2341059ba000d4fa87691f84bf3c39822b38aecb Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 18 Oct 2016 18:18:44 +0200
+Subject: [PATCH 146/151] LDAP/AD: resolve domain local groups for remote users
+
+If a user from a trusted domain in the same forest is a direct or
+indirect member of domain local groups from the local domain those
+memberships must be resolved as well. Since those domain local groups
+are not valid in the trusted domain a DC from the trusted domain which
+is used to lookup the user data is not aware of them. As a consequence
+those memberships must be resolved against a local DC in a second step.
+
+Resolves https://fedorahosted.org/sssd/ticket/3206
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 25699846bd1c9f8bb513b6271eb4366ab682fbd2)
+---
+ src/db/sysdb.h                                |   2 +
+ src/providers/ldap/sdap_async_initgroups.c    | 158 +++++++++-
+ src/providers/ldap/sdap_async_initgroups_ad.c | 407 ++++++++++++++++++++++++++
+ src/providers/ldap/sdap_async_private.h       |  10 +
+ 4 files changed, 570 insertions(+), 7 deletions(-)
+
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 4164657c2b329a240d46fe3ecdfb4b2eefffc5b3..a0279fb249e1258c9cb73a4fcab55e4b242c61f3 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -224,6 +224,8 @@
+                         SYSDB_OVERRIDE_DN, \
+                         SYSDB_OVERRIDE_OBJECT_DN, \
+                         SYSDB_DEFAULT_OVERRIDE_NAME, \
++                        SYSDB_UUID, \
++                        SYSDB_ORIG_DN, \
+                         NULL}
+ 
+ #define SYSDB_GRSRC_ATTRS {SYSDB_NAME, SYSDB_GIDNUM, \
+diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
+index f1ba65a98b07a593b0e3a722d0b2c56c8e4b821e..8eaba261c49082d086df9f19464ac0f40fae71fb 100644
+--- a/src/providers/ldap/sdap_async_initgroups.c
++++ b/src/providers/ldap/sdap_async_initgroups.c
+@@ -2317,6 +2317,7 @@ static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req)
+     struct sdap_rfc2307bis_nested_ctx *state =
+             tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
+     char *oc_list;
++    const char *class;
+ 
+     tmp_ctx = talloc_new(state);
+     if (!tmp_ctx) {
+@@ -2324,9 +2325,21 @@ static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req)
+         goto done;
+     }
+ 
+-    ret = sdap_get_group_primary_name(state, state->opts,
+-                                      state->groups[state->group_iter],
+-                                      state->dom, &state->primary_name);
++    ret = sysdb_attrs_get_string(state->groups[state->group_iter],
++                                 SYSDB_OBJECTCLASS, &class);
++    if (ret == EOK) {
++        /* If there is a objectClass attribute the object is coming from the
++         * cache and the name attribute of the object already has the primary
++         * name.
++         * If the objectClass attribute is missing the object is coming from
++         * LDAP and we have to find the primary name first. */
++        ret = sysdb_attrs_get_string(state->groups[state->group_iter],
++                                     SYSDB_NAME, &state->primary_name);
++    } else {
++        ret = sdap_get_group_primary_name(state, state->opts,
++                                          state->groups[state->group_iter],
++                                          state->dom, &state->primary_name);
++    }
+     if (ret != EOK) {
+         goto done;
+     }
+@@ -3069,6 +3082,103 @@ fail:
+     tevent_req_error(req, ret);
+ }
+ 
++static void sdap_ad_check_domain_local_groups_done(struct tevent_req *subreq);
++
++errno_t sdap_ad_check_domain_local_groups(struct tevent_req *req)
++{
++    struct sdap_get_initgr_state *state = tevent_req_data(req,
++                                               struct sdap_get_initgr_state);
++    int ret;
++    struct sdap_domain *local_sdom;
++    const char *orig_name;
++    const char *sysdb_name;
++    struct ldb_result *res;
++    struct tevent_req *subreq;
++    struct sysdb_attrs **groups;
++
++    /* We only need to check for domain local groups in the AD case and if the
++     * user is not from our domain, i.e. if the user comes from a sub-domain.
++     */
++    if (state->opts->schema_type != SDAP_SCHEMA_AD
++            || !IS_SUBDOMAIN(state->dom)
++            || !dp_target_enabled(state->id_ctx->be->provider, "ad", DPT_ID)) {
++        return EOK;
++    }
++
++    local_sdom = sdap_domain_get(state->id_ctx->opts, state->dom->parent);
++    if (local_sdom == NULL || local_sdom->pvt == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "No ID ctx available for [%s].\n",
++                                    state->dom->parent->name);
++        return EINVAL;
++    }
++
++    ret = sysdb_attrs_get_string(state->orig_user, SYSDB_NAME, &orig_name);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Missing name in user object.\n");
++        return ret;
++    }
++
++    sysdb_name = sss_create_internal_fqname(state, orig_name, state->dom->name);
++    if (sysdb_name == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_create_internal_fqname failed.\n");
++        return ENOMEM;
++    }
++
++    ret = sysdb_initgroups(state, state->dom, sysdb_name, &res);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_initgroups failed for user [%s].\n",
++                                   sysdb_name);
++        return ret;
++    }
++
++    if (res->count == 0) {
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "sysdb_initgroups returned no results for user [%s].\n",
++              sysdb_name);
++        return EINVAL;
++    }
++
++    /* The user object, the first entry in the res->msgs, is included as well
++     * to cover the case where the remote user is directly added to
++     * a domain local group. */
++    ret = sysdb_msg2attrs(state, res->count, res->msgs, &groups);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sysdb_msg2attrs failed.\n");
++        return ret;
++    }
++
++    subreq = sdap_ad_get_domain_local_groups_send(state, state->ev, local_sdom,
++                             state->opts, state->sysdb, state->dom->parent,
++                             groups, res->count);
++    if (subreq == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "sdap_ad_get_domain_local_groups_send failed.\n");
++        return ENOMEM;
++    }
++
++    tevent_req_set_callback(subreq, sdap_ad_check_domain_local_groups_done,
++                            req);
++
++    return EAGAIN;
++}
++
++static void sdap_ad_check_domain_local_groups_done(struct tevent_req *subreq)
++{
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    int ret;
++
++    ret = sdap_ad_get_domain_local_groups_recv(subreq);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    tevent_req_done(req);
++
++    return;
++}
++
+ static void sdap_get_initgr_pgid(struct tevent_req *req);
+ static void sdap_get_initgr_done(struct tevent_req *subreq)
+ {
+@@ -3201,8 +3311,6 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
+     if (ret == EOK) {
+         DEBUG(SSSDBG_TRACE_FUNC,
+               "Primary group already cached, nothing to do.\n");
+-        ret = EOK;
+-        goto done;
+     } else {
+         gid = talloc_asprintf(state, "%lu", (unsigned long)primary_gid);
+         if (gid == NULL) {
+@@ -3219,10 +3327,28 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
+             goto done;
+         }
+         tevent_req_set_callback(subreq, sdap_get_initgr_pgid, req);
++
++        talloc_free(tmp_ctx);
++        return;
+     }
+ 
+-    talloc_free(tmp_ctx);
+-    return;
++    ret = sdap_ad_check_domain_local_groups(req);
++    if (ret == EAGAIN) {
++        DEBUG(SSSDBG_TRACE_ALL,
++              "Checking for domain local group memberships.\n");
++        talloc_free(tmp_ctx);
++        return;
++    } else if (ret == EOK) {
++        DEBUG(SSSDBG_TRACE_ALL,
++              "No need to check for domain local group memberships.\n");
++    } else {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "sdap_ad_check_domain_local_groups failed, "
++              "meberships to domain local groups might be missing.\n");
++        /* do not let the request fail completely because we already have at
++         * least "some" groups */
++        ret = EOK;
++    }
+ 
+ done:
+     talloc_free(tmp_ctx);
+@@ -3247,7 +3373,25 @@ static void sdap_get_initgr_pgid(struct tevent_req *subreq)
+         return;
+     }
+ 
++    ret = sdap_ad_check_domain_local_groups(req);
++    if (ret == EAGAIN) {
++        DEBUG(SSSDBG_TRACE_ALL,
++              "Checking for domain local group memberships.\n");
++        return;
++    } else if (ret == EOK) {
++        DEBUG(SSSDBG_TRACE_ALL,
++              "No need to check for domain local group memberships.\n");
++    } else {
++        DEBUG(SSSDBG_OP_FAILURE, "sdap_ad_check_domain_local_groups failed.\n");
++        DEBUG(SSSDBG_OP_FAILURE,
++              "sdap_ad_check_domain_local_groups failed, "
++              "meberships to domain local groups might be missing.\n");
++        /* do not let the request fail completely because we already have at
++         * least "some" groups */
++    }
++
+     tevent_req_done(req);
++    return;
+ }
+ 
+ int sdap_get_initgr_recv(struct tevent_req *req)
+diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
+index ad54c1fb837eb4b8e123a1c230feb697eb37bb41..1fee4ab43a6c13803a088ffa4695dde7f39b3d2b 100644
+--- a/src/providers/ldap/sdap_async_initgroups_ad.c
++++ b/src/providers/ldap/sdap_async_initgroups_ad.c
+@@ -1412,6 +1412,413 @@ static errno_t sdap_ad_tokengroups_initgr_posix_recv(struct tevent_req *req)
+     return EOK;
+ }
+ 
++struct sdap_ad_get_domain_local_groups_state {
++    struct tevent_context *ev;
++    struct sdap_id_conn_ctx *conn;
++    struct sdap_options *opts;
++    struct sdap_id_op *op;
++    struct sysdb_ctx *sysdb;
++    struct sss_domain_info *dom;
++    int dp_error;
++
++    struct sdap_search_base **search_bases;
++    struct sysdb_attrs **groups;
++    size_t num_groups;
++    hash_table_t *group_hash;
++};
++
++static void
++sdap_ad_get_domain_local_groups_connect_done(struct tevent_req *subreq);
++static void sdap_ad_get_domain_local_groups_done(struct tevent_req *subreq);
++
++struct tevent_req *
++sdap_ad_get_domain_local_groups_send(TALLOC_CTX *mem_ctx,
++                                     struct tevent_context *ev,
++                                     struct sdap_domain *local_sdom,
++                                     struct sdap_options *opts,
++                                     struct sysdb_ctx *sysdb,
++                                     struct sss_domain_info *dom,
++                                     struct sysdb_attrs **groups,
++                                     size_t num_groups)
++{
++    struct sdap_ad_get_domain_local_groups_state *state;
++    struct tevent_req *req;
++    struct tevent_req *subreq;
++    struct ad_id_ctx *ad_id_ctx;
++    errno_t ret;
++
++    req = tevent_req_create(mem_ctx, &state,
++                            struct sdap_ad_get_domain_local_groups_state);
++    if (req == NULL) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
++        return NULL;
++    }
++
++    state->ev = ev;
++    ad_id_ctx = talloc_get_type(local_sdom->pvt, struct ad_id_ctx);
++    state->conn = ad_id_ctx->ldap_ctx;
++    state->opts = opts;
++    state->sysdb = sysdb;
++    state->dom = dom;
++    state->search_bases = state->conn->id_ctx->opts->sdom->group_search_bases;
++    state->groups = groups;
++    state->num_groups = num_groups;
++
++    ret = sss_hash_create(state, 32, &state->group_hash);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "sss_hash_create failed.\n");
++        goto fail;
++    }
++
++    state->op = sdap_id_op_create(state, state->conn->conn_cache);
++    if (state->op == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
++        ret = ENOMEM;
++        goto fail;
++    }
++
++    subreq = sdap_id_op_connect_send(state->op, state, &ret);
++    if (subreq == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed.\n");
++        goto fail;
++    }
++
++    tevent_req_set_callback(subreq,
++                            sdap_ad_get_domain_local_groups_connect_done, req);
++
++    return req;
++
++fail:
++    tevent_req_error(req, ret);
++    tevent_req_post(req, ev);
++    return req;
++}
++
++static void
++sdap_ad_get_domain_local_groups_connect_done(struct tevent_req *subreq)
++{
++
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct sdap_ad_get_domain_local_groups_state *state = tevent_req_data(req,
++                                  struct sdap_ad_get_domain_local_groups_state);
++    int dp_error = DP_ERR_FATAL;
++    int ret;
++
++    ret = sdap_id_op_connect_recv(subreq, &dp_error);
++    talloc_zfree(subreq);
++
++    if (ret != EOK) {
++        state->dp_error = dp_error;
++        tevent_req_error(req, ret);
++        return;
++    }
++    subreq = rfc2307bis_nested_groups_send(state, state->ev, state->opts,
++                                           state->sysdb, state->dom,
++                                           sdap_id_op_handle(state->op),
++                                           state->search_bases,
++                                           state->groups, state->num_groups,
++                                           state->group_hash, 0);
++    if (subreq == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "rfc2307bis_nested_groups_send failed.\n");
++        state->dp_error = DP_ERR_FATAL;
++        tevent_req_error(req, ENOMEM);
++        return;
++    }
++
++    tevent_req_set_callback(subreq,
++                            sdap_ad_get_domain_local_groups_done, req);
++
++    return;
++}
++
++struct sdap_nested_group {
++    struct sysdb_attrs *group;
++    struct sysdb_attrs **ldap_parents;
++    size_t parents_count;
++};
++
++static errno_t
++sdap_ad_get_domain_local_groups_parse_parents(TALLOC_CTX *mem_ctx,
++                                              struct sdap_nested_group *gr,
++                                              struct sss_domain_info *dom,
++                                              struct sysdb_ctx *sysdb,
++                                              struct sdap_options *opts,
++                                              const char **_sysdb_name,
++                                              enum sysdb_member_type *_type,
++                                              char ***_add_list,
++                                              char ***_del_list)
++{
++    int ret;
++    size_t c;
++    char **groupnamelist = NULL;
++    struct sysdb_attrs *groups[1];
++    enum sysdb_member_type type;
++    const char *sysdb_name;
++    const char *group_name;
++    const char *class;
++    struct sss_domain_info *obj_dom;
++    char *local_groups_base_dn;
++    char **cached_local_parents = NULL;
++    char **add_list = NULL;
++    char **del_list = NULL;
++    TALLOC_CTX *tmp_ctx;
++
++    tmp_ctx = talloc_new(NULL);
++    if (tmp_ctx == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
++        return ENOMEM;
++    }
++
++    local_groups_base_dn = talloc_asprintf(tmp_ctx, SYSDB_TMPL_GROUP_BASE,
++                                           dom->name);
++    if (local_groups_base_dn == NULL) {
++        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
++        ret = ENOMEM;
++        goto done;
++    }
++
++    if (gr->parents_count != 0) {
++        /* Store the parents if needed */
++        ret = sdap_nested_groups_store(sysdb, dom, opts,
++                                       gr->ldap_parents, gr->parents_count);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_MINOR_FAILURE, "Could not save groups [%d]: %s\n",
++                      ret, strerror(ret));
++            goto done;
++        }
++
++        ret = sysdb_attrs_primary_fqdn_list(dom, tmp_ctx,
++                                    gr->ldap_parents, gr->parents_count,
++                                    opts->group_map[SDAP_AT_GROUP_NAME].name,
++                                    &groupnamelist);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_primary_fqdn_list failed.\n");
++            goto done;
++        }
++    }
++
++    ret = sysdb_attrs_get_string(gr->group, SYSDB_NAME, &sysdb_name);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,
++              "sysdb_attrs_get_string failed to get SYSDB_NAME, "
++              "skipping.\n");
++        goto done;
++    }
++
++    ret = sysdb_attrs_get_string(gr->group, SYSDB_OBJECTCLASS, &class);
++    if (ret != EOK) {
++        /* If objectclass is missing gr->group is a nested parent found during
++         * the nested group lookup. It might not already stored in the cache.
++         */
++        DEBUG(SSSDBG_TRACE_LIBS,
++              "sysdb_attrs_get_string failed to get SYSDB_OBJECTCLASS "
++              "for [%s], assuming group.\n", sysdb_name);
++
++        /* make sure group exists in cache */
++        groups[0]= gr->group;
++        ret = sdap_nested_groups_store(sysdb, dom, opts, groups, 1);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_MINOR_FAILURE, "Could not save groups [%d]: %s\n",
++                      ret, strerror(ret));
++            goto done;
++        }
++
++        /* Since the object is coming from LDAP it cannot have the internal
++         * fully-qualified name, so we can expand it unconditionally. */
++        group_name = NULL;
++        ret = sysdb_attrs_primary_name(dom->sysdb, gr->group,
++                        opts->group_map[SDAP_AT_GROUP_NAME].name,
++                        &group_name);
++        if (ret != EOK || group_name == NULL) {
++            DEBUG(SSSDBG_OP_FAILURE, "Could not determine primary name\n");
++            group_name = sysdb_name;
++        }
++
++        group_name = sss_create_internal_fqname(tmp_ctx, group_name,
++                                                dom->name);
++        if (group_name != NULL) {
++            sysdb_name = group_name;
++        }
++
++        type = SYSDB_MEMBER_GROUP;
++    } else {
++        if (class != NULL && strcmp(class, SYSDB_USER_CLASS) == 0) {
++            type = SYSDB_MEMBER_USER;
++        } else {
++            type = SYSDB_MEMBER_GROUP;
++        }
++    }
++
++    /* We need to get the cached list of groups form the local domain the
++     * object is a member of to compare them with the current list just
++     * retrieved (groupnamelist). Even if this list is empty we have to
++     * proceed because the membership might have been removed recently on the
++     * server. */
++
++    obj_dom = find_domain_by_object_name(get_domains_head(dom),
++                                         sysdb_name);
++    if (obj_dom == NULL) {
++        obj_dom = dom;
++        DEBUG(SSSDBG_OP_FAILURE, "Cannot find domain for [%s], "
++                                 "trying with local domain [%s].\n",
++                                 sysdb_name, obj_dom->name);
++    }
++
++    ret = sysdb_get_direct_parents(tmp_ctx, obj_dom, dom, type, sysdb_name,
++                                   &cached_local_parents);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE,"sysdb_get_direct_parents failed.\n");
++        goto done;
++    }
++
++    if (cached_local_parents != NULL && cached_local_parents[0] == NULL) {
++        talloc_zfree(cached_local_parents);
++    }
++
++    if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
++        if (cached_local_parents != NULL) {
++            for (c = 0; cached_local_parents[c] != NULL; c++) {
++                DEBUG(SSSDBG_TRACE_ALL, "[%s] cached_local_parents [%s].\n",
++                                        sysdb_name, cached_local_parents[c]);
++            }
++        }
++
++        if (groupnamelist != NULL) {
++            for (c = 0; groupnamelist[c] != NULL; c++) {
++                DEBUG(SSSDBG_TRACE_ALL, "[%s] groupnamelist [%s].\n",
++                                        sysdb_name, groupnamelist[c]);
++            }
++        }
++    }
++
++    ret = diff_string_lists(tmp_ctx, cached_local_parents, groupnamelist,
++                            &del_list, &add_list, NULL);
++    if (ret != EOK) {
++        DEBUG(SSSDBG_OP_FAILURE, "diff_string_lists failed.\n");
++        goto done;
++    }
++
++    if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
++        if (add_list != NULL) {
++            for (c = 0; add_list[c] != NULL; c++) {
++                DEBUG(SSSDBG_TRACE_ALL, "add: [%s] will be member of [%s].\n",
++                                        sysdb_name, add_list[c]);
++            }
++        }
++        if (del_list != NULL) {
++            for (c = 0; del_list[c] != NULL; c++) {
++                DEBUG(SSSDBG_TRACE_ALL, "del: [%s] was member of [%s].\n",
++                                        sysdb_name, del_list[c]);
++            }
++        }
++    }
++
++    *_type = type;
++    *_sysdb_name = talloc_steal(mem_ctx, sysdb_name);
++    *_add_list = talloc_steal(mem_ctx, groupnamelist);
++    *_del_list = talloc_steal(mem_ctx, del_list);
++    ret = EOK;
++
++done:
++    talloc_free(tmp_ctx);
++
++    return ret;
++}
++
++static void sdap_ad_get_domain_local_groups_done(struct tevent_req *subreq)
++{
++
++    struct tevent_req *req = tevent_req_callback_data(subreq,
++                                                      struct tevent_req);
++    struct sdap_ad_get_domain_local_groups_state *state = tevent_req_data(req,
++                                  struct sdap_ad_get_domain_local_groups_state);
++    int ret;
++    int hret;
++    unsigned long count;
++    hash_value_t *values = NULL;
++    struct sdap_nested_group *gr;
++    size_t c;
++    const char *sysdb_name = NULL;
++    enum sysdb_member_type type;
++    char **add_list = NULL;
++    char **del_list = NULL;
++
++    ret = rfc2307bis_nested_groups_recv(subreq);
++    talloc_zfree(subreq);
++    if (ret != EOK) {
++        tevent_req_error(req, ret);
++        return;
++    }
++
++    hret = hash_values(state->group_hash, &count, &values);
++    if (hret != HASH_SUCCESS) {
++        DEBUG(SSSDBG_OP_FAILURE, "hash_values failed.\n");
++        ret = EIO;
++        goto done;
++    }
++
++    for (c = 0; c < count; c++) {
++        gr = talloc_get_type(values[c].ptr,
++                             struct sdap_nested_group);
++
++        /* The values from the hash are either user or group objects returned
++         * by sysdb_initgroups() which where used to start the request or
++         * nested parents found during the request. The nested parents contain
++         * the processed LDAP data and can be identified by a missing
++         * objectclass attribute. */
++        ret = sdap_ad_get_domain_local_groups_parse_parents(state, gr,
++                                                            state->dom,
++                                                            state->sysdb,
++                                                            state->opts,
++                                                            &sysdb_name,
++                                                            &type,
++                                                            &add_list,
++                                                            &del_list);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE,
++                  "sdap_ad_get_domain_local_groups_parse_parents failed.\n");
++            continue;
++        }
++
++        if ((add_list == NULL && del_list == NULL)
++                || (add_list == NULL && del_list != NULL && del_list[0] == NULL)
++                || (add_list != NULL && add_list[0] == NULL && del_list == NULL)
++                || (add_list != NULL && add_list[0] == NULL
++                        && del_list != NULL && del_list[0] == NULL) ) {
++            continue;
++        }
++
++        DEBUG(SSSDBG_TRACE_INTERNAL, "Updating domain local memberships for %s\n",
++                                     sysdb_name);
++        ret = sysdb_update_members(state->dom, sysdb_name, type,
++                                   (const char *const *) add_list,
++                                   (const char *const *) del_list);
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_members failed.\n");
++            goto done;
++        }
++    }
++
++    ret = EOK;
++done:
++    talloc_zfree(values);
++
++    if (ret == EOK) {
++        tevent_req_done(req);
++    } else {
++        tevent_req_error(req, ret);
++    }
++
++    return;
++}
++
++errno_t sdap_ad_get_domain_local_groups_recv(struct tevent_req *req)
++{
++    TEVENT_REQ_RETURN_ON_ERROR(req);
++    return EOK;
++}
++
+ struct sdap_ad_tokengroups_initgroups_state {
+     bool use_id_mapping;
+     struct sss_domain_info *domain;
+diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
+index 4af4f7144d8855e4ed705f6a64e0a7818bc0b9a9..266bc03115e2bdd6a283f5f7da565fd00d3a77be 100644
+--- a/src/providers/ldap/sdap_async_private.h
++++ b/src/providers/ldap/sdap_async_private.h
+@@ -173,4 +173,14 @@ errno_t sdap_nested_groups_store(struct sysdb_ctx *sysdb,
+                                  struct sysdb_attrs **groups,
+                                  unsigned long count);
+ 
++struct tevent_req *
++sdap_ad_get_domain_local_groups_send(TALLOC_CTX *mem_ctx,
++                                     struct tevent_context *ev,
++                                     struct sdap_domain *local_sdom,
++                                     struct sdap_options *opts,
++                                     struct sysdb_ctx *sysdb,
++                                     struct sss_domain_info *dom,
++                                     struct sysdb_attrs **groups,
++                                     size_t num_groups);
++errno_t sdap_ad_get_domain_local_groups_recv(struct tevent_req *req);
+ #endif /* _SDAP_ASYNC_PRIVATE_H_ */
+-- 
+2.7.4
+
diff --git a/SOURCES/0147-IPA-Initialize-a-boolean-control-value.patch b/SOURCES/0147-IPA-Initialize-a-boolean-control-value.patch
new file mode 100644
index 0000000..7a6ee0e
--- /dev/null
+++ b/SOURCES/0147-IPA-Initialize-a-boolean-control-value.patch
@@ -0,0 +1,60 @@
+From ffe5898f9588ba4ec3258807d377a82e52c38c67 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 4 Oct 2016 10:45:43 +0200
+Subject: [PATCH 147/149] IPA: Initialize a boolean control value
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+without this patch, valgrind was reporting:
+==30955== Conditional jump or move depends on uninitialised value(s)
+==30955== at 0xDBBACC3: ipa_subdomains_slave_search_done (ipa_subdomains.c:1111)
+==30955== by 0xE73B34D: sdap_search_bases_ex_done (sdap_ops.c:222)
+==30955== by 0xE6FFA98: sdap_get_generic_done (sdap_async.c:1872)
+==30955== by 0xE6FF4E2: generic_ext_search_handler (sdap_async.c:1689)
+==30955== by 0xE6FF840: sdap_get_and_parse_generic_done (sdap_async.c:1797)
+==30955== by 0xE6FEFB5: sdap_get_generic_op_finished (sdap_async.c:1579)
+==30955== by 0xE6FB1D2: sdap_process_message (sdap_async.c:353)
+==30955== by 0xE6FAD51: sdap_process_result (sdap_async.c:197)
+==30955== by 0xE6FAA14: sdap_ldap_next_result (sdap_async.c:145)
+==30955== by 0x8E157FF: tevent_common_loop_timer_delay (tevent_timed.c:341)
+==30955== by 0x8E16809: epoll_event_loop_once (tevent_epoll.c:911)
+==30955== by 0x8E14F09: std_event_loop_once (tevent_standard.c:114)
+==30955==
+
+Resolves:
+https://fedorahosted.org/sssd/ticket/3213
+
+Reviewed-by: Fabiano FidĂȘncio <fidencio@redhat.com>
+---
+ src/providers/ipa/ipa_subdomains.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
+index 4e5bceb8c761bf4476928168d620baf2beb62ad5..d02d2d5c05904c54c5e1997aece82f940b7334ee 100644
+--- a/src/providers/ipa/ipa_subdomains.c
++++ b/src/providers/ipa/ipa_subdomains.c
+@@ -482,6 +482,11 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
+     memset(handled, 0, sizeof(bool) * count);
+     h = 0;
+ 
++    if (changes == NULL) {
++        return EINVAL;
++    }
++    *changes = false;
++
+     /* check existing subdomains */
+     for (dom = get_next_domain(parent, SSS_GND_DESCEND);
+          dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
+@@ -1084,7 +1089,7 @@ static void ipa_subdomains_slave_search_done(struct tevent_req *subreq)
+     struct tevent_req *req;
+     struct sysdb_attrs **reply;
+     size_t reply_count;
+-    bool has_changes;
++    bool has_changes = false;
+     errno_t ret;
+ 
+     req = tevent_req_callback_data(subreq, struct tevent_req);
+-- 
+2.7.4
+
diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec
index 370e6d7..39bed2c 100644
--- a/SPECS/sssd.spec
+++ b/SPECS/sssd.spec
@@ -33,7 +33,7 @@
 
 Name: sssd
 Version: 1.14.0
-Release: 43%{?dist}
+Release: 43%{?dist}.4
 Group: Applications/System
 Summary: System Security Services Daemon
 License: GPLv3+
@@ -184,6 +184,10 @@ Patch0140: 0140-TEST-Add-regression-test-for-ticket-3179.patch
 Patch0141: 0141-p11-only-set-PKCS11_LOGIN_TOKEN_NAME-if-gdm-smartcar.patch
 Patch0142: 0142-p11-return-a-fully-qualified-name.patch
 Patch0143: 0143-pam_sss-check-PKCS11_LOGIN_TOKEN_NAME.patch
+Patch0144: 0144-sysdb-add-parent_dom-to-sysdb_get_direct_parents.patch
+Patch0145: 0145-sdap-make-some-nested-group-related-calls-public.patch
+Patch0146: 0146-LDAP-AD-resolve-domain-local-groups-for-remote-users.patch
+Patch0147: 0147-IPA-Initialize-a-boolean-control-value.patch
 
 #This patch should not be removed in RHEL-7
 Patch999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec
@@ -1178,6 +1182,22 @@ fi
 /usr/bin/rm -f /var/tmp/sssd.upgrade || :
 
 %changelog
+* Mon Nov 14 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.4
+- Revert the fix for ignoring sudoUser case as it breaks processing
+  of rules that completely lack a sudoUser attribute
+- Related: rhbz#1392946 - sudo: ignore case on case insensitive domains
+
+* Wed Nov  9 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.3
+- Resolves: rhbz#1392946 - sudo: ignore case on case insensitive domains
+
+* Tue Nov  8 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.2
+- Resolves: rhbz#1392893 - IPA: Uninitialized variable during subdomain check
+
+* Mon Nov  7 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43.1
+- Resolves: rhbz#1392896 - AD provider: SSSD does not retrieve a domain-local
+                           group with the AD provider when following AGGUDLP
+                           group structure across domains
+
 * Tue Sep 20 2016 Jakub Hrozek <jhrozek@redhat.com> - 1.14.0-43
 - Resolves: rhbz#1376831 - sssd-common is missing dependency on sssd-sudo