URL: https://github.com/SSSD/sssd/pull/57
Author: sumit-bose
 Title: #57: LDAP/AD: resolve domain local groups for remote users
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/57/head:pr57
git checkout pr57
From 1aa8ad842ca327c6dd8dc27b9e904f8486d9abf4 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 18 Oct 2016 14:59:19 +0200
Subject: [PATCH 1/3] 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.
---
 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 7de3acd..f5d3ddb 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -1137,8 +1137,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 cfee578..4d63c38 100644
--- a/src/db/sysdb_search.c
+++ b/src/db/sysdb_search.c
@@ -1981,6 +1981,7 @@ int sysdb_get_netgroup_attr(TALLOC_CTX *mem_ctx,
 
 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 df39de3..7a2eef4 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,

From 81665499765e1530c1ba9070ae9b9dd2588faeea Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 18 Oct 2016 18:16:30 +0200
Subject: [PATCH 2/3] 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.
---
 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 7a2eef4..0f56b87 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 f09ddb7..4af4f71 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_ */

From 619743a4fd9320e50badd188447f465568f23855 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 18 Oct 2016 18:18:44 +0200
Subject: [PATCH 3/3] 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
---
 src/db/sysdb.h                                |   1 +
 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, 569 insertions(+), 7 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index f5d3ddb..9012683 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -225,6 +225,7 @@
                         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 0f56b87..45fc007 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 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
     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 ad54c1f..1fee4ab 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 4af4f71..266bc03 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_ */
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org

Reply via email to