Blame SOURCES/0146-LDAP-AD-resolve-domain-local-groups-for-remote-users.patch

9f89df
From 2341059ba000d4fa87691f84bf3c39822b38aecb Mon Sep 17 00:00:00 2001
9f89df
From: Sumit Bose <sbose@redhat.com>
9f89df
Date: Tue, 18 Oct 2016 18:18:44 +0200
9f89df
Subject: [PATCH 146/151] LDAP/AD: resolve domain local groups for remote users
9f89df
9f89df
If a user from a trusted domain in the same forest is a direct or
9f89df
indirect member of domain local groups from the local domain those
9f89df
memberships must be resolved as well. Since those domain local groups
9f89df
are not valid in the trusted domain a DC from the trusted domain which
9f89df
is used to lookup the user data is not aware of them. As a consequence
9f89df
those memberships must be resolved against a local DC in a second step.
9f89df
9f89df
Resolves https://fedorahosted.org/sssd/ticket/3206
9f89df
9f89df
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
9f89df
(cherry picked from commit 25699846bd1c9f8bb513b6271eb4366ab682fbd2)
9f89df
---
9f89df
 src/db/sysdb.h                                |   2 +
9f89df
 src/providers/ldap/sdap_async_initgroups.c    | 158 +++++++++-
9f89df
 src/providers/ldap/sdap_async_initgroups_ad.c | 407 ++++++++++++++++++++++++++
9f89df
 src/providers/ldap/sdap_async_private.h       |  10 +
9f89df
 4 files changed, 570 insertions(+), 7 deletions(-)
9f89df
9f89df
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
9f89df
index 4164657c2b329a240d46fe3ecdfb4b2eefffc5b3..a0279fb249e1258c9cb73a4fcab55e4b242c61f3 100644
9f89df
--- a/src/db/sysdb.h
9f89df
+++ b/src/db/sysdb.h
9f89df
@@ -224,6 +224,8 @@
9f89df
                         SYSDB_OVERRIDE_DN, \
9f89df
                         SYSDB_OVERRIDE_OBJECT_DN, \
9f89df
                         SYSDB_DEFAULT_OVERRIDE_NAME, \
9f89df
+                        SYSDB_UUID, \
9f89df
+                        SYSDB_ORIG_DN, \
9f89df
                         NULL}
9f89df
 
9f89df
 #define SYSDB_GRSRC_ATTRS {SYSDB_NAME, SYSDB_GIDNUM, \
9f89df
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
9f89df
index f1ba65a98b07a593b0e3a722d0b2c56c8e4b821e..8eaba261c49082d086df9f19464ac0f40fae71fb 100644
9f89df
--- a/src/providers/ldap/sdap_async_initgroups.c
9f89df
+++ b/src/providers/ldap/sdap_async_initgroups.c
9f89df
@@ -2317,6 +2317,7 @@ static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req)
9f89df
     struct sdap_rfc2307bis_nested_ctx *state =
9f89df
             tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
9f89df
     char *oc_list;
9f89df
+    const char *class;
9f89df
 
9f89df
     tmp_ctx = talloc_new(state);
9f89df
     if (!tmp_ctx) {
9f89df
@@ -2324,9 +2325,21 @@ static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req)
9f89df
         goto done;
9f89df
     }
9f89df
 
9f89df
-    ret = sdap_get_group_primary_name(state, state->opts,
9f89df
-                                      state->groups[state->group_iter],
9f89df
-                                      state->dom, &state->primary_name);
9f89df
+    ret = sysdb_attrs_get_string(state->groups[state->group_iter],
9f89df
+                                 SYSDB_OBJECTCLASS, &class);
9f89df
+    if (ret == EOK) {
9f89df
+        /* If there is a objectClass attribute the object is coming from the
9f89df
+         * cache and the name attribute of the object already has the primary
9f89df
+         * name.
9f89df
+         * If the objectClass attribute is missing the object is coming from
9f89df
+         * LDAP and we have to find the primary name first. */
9f89df
+        ret = sysdb_attrs_get_string(state->groups[state->group_iter],
9f89df
+                                     SYSDB_NAME, &state->primary_name);
9f89df
+    } else {
9f89df
+        ret = sdap_get_group_primary_name(state, state->opts,
9f89df
+                                          state->groups[state->group_iter],
9f89df
+                                          state->dom, &state->primary_name);
9f89df
+    }
9f89df
     if (ret != EOK) {
9f89df
         goto done;
9f89df
     }
9f89df
@@ -3069,6 +3082,103 @@ fail:
9f89df
     tevent_req_error(req, ret);
9f89df
 }
9f89df
 
9f89df
+static void sdap_ad_check_domain_local_groups_done(struct tevent_req *subreq);
9f89df
+
9f89df
+errno_t sdap_ad_check_domain_local_groups(struct tevent_req *req)
9f89df
+{
9f89df
+    struct sdap_get_initgr_state *state = tevent_req_data(req,
9f89df
+                                               struct sdap_get_initgr_state);
9f89df
+    int ret;
9f89df
+    struct sdap_domain *local_sdom;
9f89df
+    const char *orig_name;
9f89df
+    const char *sysdb_name;
9f89df
+    struct ldb_result *res;
9f89df
+    struct tevent_req *subreq;
9f89df
+    struct sysdb_attrs **groups;
9f89df
+
9f89df
+    /* We only need to check for domain local groups in the AD case and if the
9f89df
+     * user is not from our domain, i.e. if the user comes from a sub-domain.
9f89df
+     */
9f89df
+    if (state->opts->schema_type != SDAP_SCHEMA_AD
9f89df
+            || !IS_SUBDOMAIN(state->dom)
9f89df
+            || !dp_target_enabled(state->id_ctx->be->provider, "ad", DPT_ID)) {
9f89df
+        return EOK;
9f89df
+    }
9f89df
+
9f89df
+    local_sdom = sdap_domain_get(state->id_ctx->opts, state->dom->parent);
9f89df
+    if (local_sdom == NULL || local_sdom->pvt == NULL) {
9f89df
+        DEBUG(SSSDBG_CRIT_FAILURE, "No ID ctx available for [%s].\n",
9f89df
+                                    state->dom->parent->name);
9f89df
+        return EINVAL;
9f89df
+    }
9f89df
+
9f89df
+    ret = sysdb_attrs_get_string(state->orig_user, SYSDB_NAME, &orig_name);
9f89df
+    if (ret != EOK) {
9f89df
+        DEBUG(SSSDBG_CRIT_FAILURE, "Missing name in user object.\n");
9f89df
+        return ret;
9f89df
+    }
9f89df
+
9f89df
+    sysdb_name = sss_create_internal_fqname(state, orig_name, state->dom->name);
9f89df
+    if (sysdb_name == NULL) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "sss_create_internal_fqname failed.\n");
9f89df
+        return ENOMEM;
9f89df
+    }
9f89df
+
9f89df
+    ret = sysdb_initgroups(state, state->dom, sysdb_name, &res;;
9f89df
+    if (ret != EOK) {
9f89df
+        DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_initgroups failed for user [%s].\n",
9f89df
+                                   sysdb_name);
9f89df
+        return ret;
9f89df
+    }
9f89df
+
9f89df
+    if (res->count == 0) {
9f89df
+        DEBUG(SSSDBG_CRIT_FAILURE,
9f89df
+              "sysdb_initgroups returned no results for user [%s].\n",
9f89df
+              sysdb_name);
9f89df
+        return EINVAL;
9f89df
+    }
9f89df
+
9f89df
+    /* The user object, the first entry in the res->msgs, is included as well
9f89df
+     * to cover the case where the remote user is directly added to
9f89df
+     * a domain local group. */
9f89df
+    ret = sysdb_msg2attrs(state, res->count, res->msgs, &groups);
9f89df
+    if (ret != EOK) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_msg2attrs failed.\n");
9f89df
+        return ret;
9f89df
+    }
9f89df
+
9f89df
+    subreq = sdap_ad_get_domain_local_groups_send(state, state->ev, local_sdom,
9f89df
+                             state->opts, state->sysdb, state->dom->parent,
9f89df
+                             groups, res->count);
9f89df
+    if (subreq == NULL) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_ad_get_domain_local_groups_send failed.\n");
9f89df
+        return ENOMEM;
9f89df
+    }
9f89df
+
9f89df
+    tevent_req_set_callback(subreq, sdap_ad_check_domain_local_groups_done,
9f89df
+                            req);
9f89df
+
9f89df
+    return EAGAIN;
9f89df
+}
9f89df
+
9f89df
+static void sdap_ad_check_domain_local_groups_done(struct tevent_req *subreq)
9f89df
+{
9f89df
+    struct tevent_req *req = tevent_req_callback_data(subreq,
9f89df
+                                                      struct tevent_req);
9f89df
+    int ret;
9f89df
+
9f89df
+    ret = sdap_ad_get_domain_local_groups_recv(subreq);
9f89df
+    talloc_zfree(subreq);
9f89df
+    if (ret != EOK) {
9f89df
+        tevent_req_error(req, ret);
9f89df
+        return;
9f89df
+    }
9f89df
+
9f89df
+    tevent_req_done(req);
9f89df
+
9f89df
+    return;
9f89df
+}
9f89df
+
9f89df
 static void sdap_get_initgr_pgid(struct tevent_req *req);
9f89df
 static void sdap_get_initgr_done(struct tevent_req *subreq)
9f89df
 {
9f89df
@@ -3201,8 +3311,6 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
9f89df
     if (ret == EOK) {
9f89df
         DEBUG(SSSDBG_TRACE_FUNC,
9f89df
               "Primary group already cached, nothing to do.\n");
9f89df
-        ret = EOK;
9f89df
-        goto done;
9f89df
     } else {
9f89df
         gid = talloc_asprintf(state, "%lu", (unsigned long)primary_gid);
9f89df
         if (gid == NULL) {
9f89df
@@ -3219,10 +3327,28 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
9f89df
             goto done;
9f89df
         }
9f89df
         tevent_req_set_callback(subreq, sdap_get_initgr_pgid, req);
9f89df
+
9f89df
+        talloc_free(tmp_ctx);
9f89df
+        return;
9f89df
     }
9f89df
 
9f89df
-    talloc_free(tmp_ctx);
9f89df
-    return;
9f89df
+    ret = sdap_ad_check_domain_local_groups(req);
9f89df
+    if (ret == EAGAIN) {
9f89df
+        DEBUG(SSSDBG_TRACE_ALL,
9f89df
+              "Checking for domain local group memberships.\n");
9f89df
+        talloc_free(tmp_ctx);
9f89df
+        return;
9f89df
+    } else if (ret == EOK) {
9f89df
+        DEBUG(SSSDBG_TRACE_ALL,
9f89df
+              "No need to check for domain local group memberships.\n");
9f89df
+    } else {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE,
9f89df
+              "sdap_ad_check_domain_local_groups failed, "
9f89df
+              "meberships to domain local groups might be missing.\n");
9f89df
+        /* do not let the request fail completely because we already have at
9f89df
+         * least "some" groups */
9f89df
+        ret = EOK;
9f89df
+    }
9f89df
 
9f89df
 done:
9f89df
     talloc_free(tmp_ctx);
9f89df
@@ -3247,7 +3373,25 @@ static void sdap_get_initgr_pgid(struct tevent_req *subreq)
9f89df
         return;
9f89df
     }
9f89df
 
9f89df
+    ret = sdap_ad_check_domain_local_groups(req);
9f89df
+    if (ret == EAGAIN) {
9f89df
+        DEBUG(SSSDBG_TRACE_ALL,
9f89df
+              "Checking for domain local group memberships.\n");
9f89df
+        return;
9f89df
+    } else if (ret == EOK) {
9f89df
+        DEBUG(SSSDBG_TRACE_ALL,
9f89df
+              "No need to check for domain local group memberships.\n");
9f89df
+    } else {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_ad_check_domain_local_groups failed.\n");
9f89df
+        DEBUG(SSSDBG_OP_FAILURE,
9f89df
+              "sdap_ad_check_domain_local_groups failed, "
9f89df
+              "meberships to domain local groups might be missing.\n");
9f89df
+        /* do not let the request fail completely because we already have at
9f89df
+         * least "some" groups */
9f89df
+    }
9f89df
+
9f89df
     tevent_req_done(req);
9f89df
+    return;
9f89df
 }
9f89df
 
9f89df
 int sdap_get_initgr_recv(struct tevent_req *req)
9f89df
diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
9f89df
index ad54c1fb837eb4b8e123a1c230feb697eb37bb41..1fee4ab43a6c13803a088ffa4695dde7f39b3d2b 100644
9f89df
--- a/src/providers/ldap/sdap_async_initgroups_ad.c
9f89df
+++ b/src/providers/ldap/sdap_async_initgroups_ad.c
9f89df
@@ -1412,6 +1412,413 @@ static errno_t sdap_ad_tokengroups_initgr_posix_recv(struct tevent_req *req)
9f89df
     return EOK;
9f89df
 }
9f89df
 
9f89df
+struct sdap_ad_get_domain_local_groups_state {
9f89df
+    struct tevent_context *ev;
9f89df
+    struct sdap_id_conn_ctx *conn;
9f89df
+    struct sdap_options *opts;
9f89df
+    struct sdap_id_op *op;
9f89df
+    struct sysdb_ctx *sysdb;
9f89df
+    struct sss_domain_info *dom;
9f89df
+    int dp_error;
9f89df
+
9f89df
+    struct sdap_search_base **search_bases;
9f89df
+    struct sysdb_attrs **groups;
9f89df
+    size_t num_groups;
9f89df
+    hash_table_t *group_hash;
9f89df
+};
9f89df
+
9f89df
+static void
9f89df
+sdap_ad_get_domain_local_groups_connect_done(struct tevent_req *subreq);
9f89df
+static void sdap_ad_get_domain_local_groups_done(struct tevent_req *subreq);
9f89df
+
9f89df
+struct tevent_req *
9f89df
+sdap_ad_get_domain_local_groups_send(TALLOC_CTX *mem_ctx,
9f89df
+                                     struct tevent_context *ev,
9f89df
+                                     struct sdap_domain *local_sdom,
9f89df
+                                     struct sdap_options *opts,
9f89df
+                                     struct sysdb_ctx *sysdb,
9f89df
+                                     struct sss_domain_info *dom,
9f89df
+                                     struct sysdb_attrs **groups,
9f89df
+                                     size_t num_groups)
9f89df
+{
9f89df
+    struct sdap_ad_get_domain_local_groups_state *state;
9f89df
+    struct tevent_req *req;
9f89df
+    struct tevent_req *subreq;
9f89df
+    struct ad_id_ctx *ad_id_ctx;
9f89df
+    errno_t ret;
9f89df
+
9f89df
+    req = tevent_req_create(mem_ctx, &state,
9f89df
+                            struct sdap_ad_get_domain_local_groups_state);
9f89df
+    if (req == NULL) {
9f89df
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
9f89df
+        return NULL;
9f89df
+    }
9f89df
+
9f89df
+    state->ev = ev;
9f89df
+    ad_id_ctx = talloc_get_type(local_sdom->pvt, struct ad_id_ctx);
9f89df
+    state->conn = ad_id_ctx->ldap_ctx;
9f89df
+    state->opts = opts;
9f89df
+    state->sysdb = sysdb;
9f89df
+    state->dom = dom;
9f89df
+    state->search_bases = state->conn->id_ctx->opts->sdom->group_search_bases;
9f89df
+    state->groups = groups;
9f89df
+    state->num_groups = num_groups;
9f89df
+
9f89df
+    ret = sss_hash_create(state, 32, &state->group_hash);
9f89df
+    if (ret != EOK) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "sss_hash_create failed.\n");
9f89df
+        goto fail;
9f89df
+    }
9f89df
+
9f89df
+    state->op = sdap_id_op_create(state, state->conn->conn_cache);
9f89df
+    if (state->op == NULL) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed\n");
9f89df
+        ret = ENOMEM;
9f89df
+        goto fail;
9f89df
+    }
9f89df
+
9f89df
+    subreq = sdap_id_op_connect_send(state->op, state, &ret;;
9f89df
+    if (subreq == NULL) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed.\n");
9f89df
+        goto fail;
9f89df
+    }
9f89df
+
9f89df
+    tevent_req_set_callback(subreq,
9f89df
+                            sdap_ad_get_domain_local_groups_connect_done, req);
9f89df
+
9f89df
+    return req;
9f89df
+
9f89df
+fail:
9f89df
+    tevent_req_error(req, ret);
9f89df
+    tevent_req_post(req, ev);
9f89df
+    return req;
9f89df
+}
9f89df
+
9f89df
+static void
9f89df
+sdap_ad_get_domain_local_groups_connect_done(struct tevent_req *subreq)
9f89df
+{
9f89df
+
9f89df
+    struct tevent_req *req = tevent_req_callback_data(subreq,
9f89df
+                                                      struct tevent_req);
9f89df
+    struct sdap_ad_get_domain_local_groups_state *state = tevent_req_data(req,
9f89df
+                                  struct sdap_ad_get_domain_local_groups_state);
9f89df
+    int dp_error = DP_ERR_FATAL;
9f89df
+    int ret;
9f89df
+
9f89df
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
9f89df
+    talloc_zfree(subreq);
9f89df
+
9f89df
+    if (ret != EOK) {
9f89df
+        state->dp_error = dp_error;
9f89df
+        tevent_req_error(req, ret);
9f89df
+        return;
9f89df
+    }
9f89df
+    subreq = rfc2307bis_nested_groups_send(state, state->ev, state->opts,
9f89df
+                                           state->sysdb, state->dom,
9f89df
+                                           sdap_id_op_handle(state->op),
9f89df
+                                           state->search_bases,
9f89df
+                                           state->groups, state->num_groups,
9f89df
+                                           state->group_hash, 0);
9f89df
+    if (subreq == NULL) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "rfc2307bis_nested_groups_send failed.\n");
9f89df
+        state->dp_error = DP_ERR_FATAL;
9f89df
+        tevent_req_error(req, ENOMEM);
9f89df
+        return;
9f89df
+    }
9f89df
+
9f89df
+    tevent_req_set_callback(subreq,
9f89df
+                            sdap_ad_get_domain_local_groups_done, req);
9f89df
+
9f89df
+    return;
9f89df
+}
9f89df
+
9f89df
+struct sdap_nested_group {
9f89df
+    struct sysdb_attrs *group;
9f89df
+    struct sysdb_attrs **ldap_parents;
9f89df
+    size_t parents_count;
9f89df
+};
9f89df
+
9f89df
+static errno_t
9f89df
+sdap_ad_get_domain_local_groups_parse_parents(TALLOC_CTX *mem_ctx,
9f89df
+                                              struct sdap_nested_group *gr,
9f89df
+                                              struct sss_domain_info *dom,
9f89df
+                                              struct sysdb_ctx *sysdb,
9f89df
+                                              struct sdap_options *opts,
9f89df
+                                              const char **_sysdb_name,
9f89df
+                                              enum sysdb_member_type *_type,
9f89df
+                                              char ***_add_list,
9f89df
+                                              char ***_del_list)
9f89df
+{
9f89df
+    int ret;
9f89df
+    size_t c;
9f89df
+    char **groupnamelist = NULL;
9f89df
+    struct sysdb_attrs *groups[1];
9f89df
+    enum sysdb_member_type type;
9f89df
+    const char *sysdb_name;
9f89df
+    const char *group_name;
9f89df
+    const char *class;
9f89df
+    struct sss_domain_info *obj_dom;
9f89df
+    char *local_groups_base_dn;
9f89df
+    char **cached_local_parents = NULL;
9f89df
+    char **add_list = NULL;
9f89df
+    char **del_list = NULL;
9f89df
+    TALLOC_CTX *tmp_ctx;
9f89df
+
9f89df
+    tmp_ctx = talloc_new(NULL);
9f89df
+    if (tmp_ctx == NULL) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
9f89df
+        return ENOMEM;
9f89df
+    }
9f89df
+
9f89df
+    local_groups_base_dn = talloc_asprintf(tmp_ctx, SYSDB_TMPL_GROUP_BASE,
9f89df
+                                           dom->name);
9f89df
+    if (local_groups_base_dn == NULL) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
9f89df
+        ret = ENOMEM;
9f89df
+        goto done;
9f89df
+    }
9f89df
+
9f89df
+    if (gr->parents_count != 0) {
9f89df
+        /* Store the parents if needed */
9f89df
+        ret = sdap_nested_groups_store(sysdb, dom, opts,
9f89df
+                                       gr->ldap_parents, gr->parents_count);
9f89df
+        if (ret != EOK) {
9f89df
+            DEBUG(SSSDBG_MINOR_FAILURE, "Could not save groups [%d]: %s\n",
9f89df
+                      ret, strerror(ret));
9f89df
+            goto done;
9f89df
+        }
9f89df
+
9f89df
+        ret = sysdb_attrs_primary_fqdn_list(dom, tmp_ctx,
9f89df
+                                    gr->ldap_parents, gr->parents_count,
9f89df
+                                    opts->group_map[SDAP_AT_GROUP_NAME].name,
9f89df
+                                    &groupnamelist);
9f89df
+        if (ret != EOK) {
9f89df
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_primary_fqdn_list failed.\n");
9f89df
+            goto done;
9f89df
+        }
9f89df
+    }
9f89df
+
9f89df
+    ret = sysdb_attrs_get_string(gr->group, SYSDB_NAME, &sysdb_name);
9f89df
+    if (ret != EOK) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE,
9f89df
+              "sysdb_attrs_get_string failed to get SYSDB_NAME, "
9f89df
+              "skipping.\n");
9f89df
+        goto done;
9f89df
+    }
9f89df
+
9f89df
+    ret = sysdb_attrs_get_string(gr->group, SYSDB_OBJECTCLASS, &class);
9f89df
+    if (ret != EOK) {
9f89df
+        /* If objectclass is missing gr->group is a nested parent found during
9f89df
+         * the nested group lookup. It might not already stored in the cache.
9f89df
+         */
9f89df
+        DEBUG(SSSDBG_TRACE_LIBS,
9f89df
+              "sysdb_attrs_get_string failed to get SYSDB_OBJECTCLASS "
9f89df
+              "for [%s], assuming group.\n", sysdb_name);
9f89df
+
9f89df
+        /* make sure group exists in cache */
9f89df
+        groups[0]= gr->group;
9f89df
+        ret = sdap_nested_groups_store(sysdb, dom, opts, groups, 1);
9f89df
+        if (ret != EOK) {
9f89df
+            DEBUG(SSSDBG_MINOR_FAILURE, "Could not save groups [%d]: %s\n",
9f89df
+                      ret, strerror(ret));
9f89df
+            goto done;
9f89df
+        }
9f89df
+
9f89df
+        /* Since the object is coming from LDAP it cannot have the internal
9f89df
+         * fully-qualified name, so we can expand it unconditionally. */
9f89df
+        group_name = NULL;
9f89df
+        ret = sysdb_attrs_primary_name(dom->sysdb, gr->group,
9f89df
+                        opts->group_map[SDAP_AT_GROUP_NAME].name,
9f89df
+                        &group_name);
9f89df
+        if (ret != EOK || group_name == NULL) {
9f89df
+            DEBUG(SSSDBG_OP_FAILURE, "Could not determine primary name\n");
9f89df
+            group_name = sysdb_name;
9f89df
+        }
9f89df
+
9f89df
+        group_name = sss_create_internal_fqname(tmp_ctx, group_name,
9f89df
+                                                dom->name);
9f89df
+        if (group_name != NULL) {
9f89df
+            sysdb_name = group_name;
9f89df
+        }
9f89df
+
9f89df
+        type = SYSDB_MEMBER_GROUP;
9f89df
+    } else {
9f89df
+        if (class != NULL && strcmp(class, SYSDB_USER_CLASS) == 0) {
9f89df
+            type = SYSDB_MEMBER_USER;
9f89df
+        } else {
9f89df
+            type = SYSDB_MEMBER_GROUP;
9f89df
+        }
9f89df
+    }
9f89df
+
9f89df
+    /* We need to get the cached list of groups form the local domain the
9f89df
+     * object is a member of to compare them with the current list just
9f89df
+     * retrieved (groupnamelist). Even if this list is empty we have to
9f89df
+     * proceed because the membership might have been removed recently on the
9f89df
+     * server. */
9f89df
+
9f89df
+    obj_dom = find_domain_by_object_name(get_domains_head(dom),
9f89df
+                                         sysdb_name);
9f89df
+    if (obj_dom == NULL) {
9f89df
+        obj_dom = dom;
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot find domain for [%s], "
9f89df
+                                 "trying with local domain [%s].\n",
9f89df
+                                 sysdb_name, obj_dom->name);
9f89df
+    }
9f89df
+
9f89df
+    ret = sysdb_get_direct_parents(tmp_ctx, obj_dom, dom, type, sysdb_name,
9f89df
+                                   &cached_local_parents);
9f89df
+    if (ret != EOK) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE,"sysdb_get_direct_parents failed.\n");
9f89df
+        goto done;
9f89df
+    }
9f89df
+
9f89df
+    if (cached_local_parents != NULL && cached_local_parents[0] == NULL) {
9f89df
+        talloc_zfree(cached_local_parents);
9f89df
+    }
9f89df
+
9f89df
+    if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
9f89df
+        if (cached_local_parents != NULL) {
9f89df
+            for (c = 0; cached_local_parents[c] != NULL; c++) {
9f89df
+                DEBUG(SSSDBG_TRACE_ALL, "[%s] cached_local_parents [%s].\n",
9f89df
+                                        sysdb_name, cached_local_parents[c]);
9f89df
+            }
9f89df
+        }
9f89df
+
9f89df
+        if (groupnamelist != NULL) {
9f89df
+            for (c = 0; groupnamelist[c] != NULL; c++) {
9f89df
+                DEBUG(SSSDBG_TRACE_ALL, "[%s] groupnamelist [%s].\n",
9f89df
+                                        sysdb_name, groupnamelist[c]);
9f89df
+            }
9f89df
+        }
9f89df
+    }
9f89df
+
9f89df
+    ret = diff_string_lists(tmp_ctx, cached_local_parents, groupnamelist,
9f89df
+                            &del_list, &add_list, NULL);
9f89df
+    if (ret != EOK) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "diff_string_lists failed.\n");
9f89df
+        goto done;
9f89df
+    }
9f89df
+
9f89df
+    if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
9f89df
+        if (add_list != NULL) {
9f89df
+            for (c = 0; add_list[c] != NULL; c++) {
9f89df
+                DEBUG(SSSDBG_TRACE_ALL, "add: [%s] will be member of [%s].\n",
9f89df
+                                        sysdb_name, add_list[c]);
9f89df
+            }
9f89df
+        }
9f89df
+        if (del_list != NULL) {
9f89df
+            for (c = 0; del_list[c] != NULL; c++) {
9f89df
+                DEBUG(SSSDBG_TRACE_ALL, "del: [%s] was member of [%s].\n",
9f89df
+                                        sysdb_name, del_list[c]);
9f89df
+            }
9f89df
+        }
9f89df
+    }
9f89df
+
9f89df
+    *_type = type;
9f89df
+    *_sysdb_name = talloc_steal(mem_ctx, sysdb_name);
9f89df
+    *_add_list = talloc_steal(mem_ctx, groupnamelist);
9f89df
+    *_del_list = talloc_steal(mem_ctx, del_list);
9f89df
+    ret = EOK;
9f89df
+
9f89df
+done:
9f89df
+    talloc_free(tmp_ctx);
9f89df
+
9f89df
+    return ret;
9f89df
+}
9f89df
+
9f89df
+static void sdap_ad_get_domain_local_groups_done(struct tevent_req *subreq)
9f89df
+{
9f89df
+
9f89df
+    struct tevent_req *req = tevent_req_callback_data(subreq,
9f89df
+                                                      struct tevent_req);
9f89df
+    struct sdap_ad_get_domain_local_groups_state *state = tevent_req_data(req,
9f89df
+                                  struct sdap_ad_get_domain_local_groups_state);
9f89df
+    int ret;
9f89df
+    int hret;
9f89df
+    unsigned long count;
9f89df
+    hash_value_t *values = NULL;
9f89df
+    struct sdap_nested_group *gr;
9f89df
+    size_t c;
9f89df
+    const char *sysdb_name = NULL;
9f89df
+    enum sysdb_member_type type;
9f89df
+    char **add_list = NULL;
9f89df
+    char **del_list = NULL;
9f89df
+
9f89df
+    ret = rfc2307bis_nested_groups_recv(subreq);
9f89df
+    talloc_zfree(subreq);
9f89df
+    if (ret != EOK) {
9f89df
+        tevent_req_error(req, ret);
9f89df
+        return;
9f89df
+    }
9f89df
+
9f89df
+    hret = hash_values(state->group_hash, &count, &values);
9f89df
+    if (hret != HASH_SUCCESS) {
9f89df
+        DEBUG(SSSDBG_OP_FAILURE, "hash_values failed.\n");
9f89df
+        ret = EIO;
9f89df
+        goto done;
9f89df
+    }
9f89df
+
9f89df
+    for (c = 0; c < count; c++) {
9f89df
+        gr = talloc_get_type(values[c].ptr,
9f89df
+                             struct sdap_nested_group);
9f89df
+
9f89df
+        /* The values from the hash are either user or group objects returned
9f89df
+         * by sysdb_initgroups() which where used to start the request or
9f89df
+         * nested parents found during the request. The nested parents contain
9f89df
+         * the processed LDAP data and can be identified by a missing
9f89df
+         * objectclass attribute. */
9f89df
+        ret = sdap_ad_get_domain_local_groups_parse_parents(state, gr,
9f89df
+                                                            state->dom,
9f89df
+                                                            state->sysdb,
9f89df
+                                                            state->opts,
9f89df
+                                                            &sysdb_name,
9f89df
+                                                            &type,
9f89df
+                                                            &add_list,
9f89df
+                                                            &del_list);
9f89df
+        if (ret != EOK) {
9f89df
+            DEBUG(SSSDBG_OP_FAILURE,
9f89df
+                  "sdap_ad_get_domain_local_groups_parse_parents failed.\n");
9f89df
+            continue;
9f89df
+        }
9f89df
+
9f89df
+        if ((add_list == NULL && del_list == NULL)
9f89df
+                || (add_list == NULL && del_list != NULL && del_list[0] == NULL)
9f89df
+                || (add_list != NULL && add_list[0] == NULL && del_list == NULL)
9f89df
+                || (add_list != NULL && add_list[0] == NULL
9f89df
+                        && del_list != NULL && del_list[0] == NULL) ) {
9f89df
+            continue;
9f89df
+        }
9f89df
+
9f89df
+        DEBUG(SSSDBG_TRACE_INTERNAL, "Updating domain local memberships for %s\n",
9f89df
+                                     sysdb_name);
9f89df
+        ret = sysdb_update_members(state->dom, sysdb_name, type,
9f89df
+                                   (const char *const *) add_list,
9f89df
+                                   (const char *const *) del_list);
9f89df
+        if (ret != EOK) {
9f89df
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_members failed.\n");
9f89df
+            goto done;
9f89df
+        }
9f89df
+    }
9f89df
+
9f89df
+    ret = EOK;
9f89df
+done:
9f89df
+    talloc_zfree(values);
9f89df
+
9f89df
+    if (ret == EOK) {
9f89df
+        tevent_req_done(req);
9f89df
+    } else {
9f89df
+        tevent_req_error(req, ret);
9f89df
+    }
9f89df
+
9f89df
+    return;
9f89df
+}
9f89df
+
9f89df
+errno_t sdap_ad_get_domain_local_groups_recv(struct tevent_req *req)
9f89df
+{
9f89df
+    TEVENT_REQ_RETURN_ON_ERROR(req);
9f89df
+    return EOK;
9f89df
+}
9f89df
+
9f89df
 struct sdap_ad_tokengroups_initgroups_state {
9f89df
     bool use_id_mapping;
9f89df
     struct sss_domain_info *domain;
9f89df
diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
9f89df
index 4af4f7144d8855e4ed705f6a64e0a7818bc0b9a9..266bc03115e2bdd6a283f5f7da565fd00d3a77be 100644
9f89df
--- a/src/providers/ldap/sdap_async_private.h
9f89df
+++ b/src/providers/ldap/sdap_async_private.h
9f89df
@@ -173,4 +173,14 @@ errno_t sdap_nested_groups_store(struct sysdb_ctx *sysdb,
9f89df
                                  struct sysdb_attrs **groups,
9f89df
                                  unsigned long count);
9f89df
 
9f89df
+struct tevent_req *
9f89df
+sdap_ad_get_domain_local_groups_send(TALLOC_CTX *mem_ctx,
9f89df
+                                     struct tevent_context *ev,
9f89df
+                                     struct sdap_domain *local_sdom,
9f89df
+                                     struct sdap_options *opts,
9f89df
+                                     struct sysdb_ctx *sysdb,
9f89df
+                                     struct sss_domain_info *dom,
9f89df
+                                     struct sysdb_attrs **groups,
9f89df
+                                     size_t num_groups);
9f89df
+errno_t sdap_ad_get_domain_local_groups_recv(struct tevent_req *req);
9f89df
 #endif /* _SDAP_ASYNC_PRIVATE_H_ */
9f89df
-- 
9f89df
2.7.4
9f89df