Blame SOURCES/0108-LDAP-Use-the-IPA-provider-interface-to-resolve-exter.patch

14acf4
From 3af57ba315eb5268c56919297e3a688c3f2f5b05 Mon Sep 17 00:00:00 2001
14acf4
From: Jakub Hrozek <jhrozek@redhat.com>
14acf4
Date: Mon, 25 Jan 2016 16:13:03 +0100
14acf4
Subject: [PATCH 108/108] LDAP: Use the IPA provider interface to resolve
14acf4
 external group members
14acf4
14acf4
Resolves:
14acf4
    https://fedorahosted.org/sssd/ticket/2522
14acf4
14acf4
Currently the approach is not optimized for performance, because each
14acf4
external member is resolved in a full transaction to make sure even ID
14acf4
views and similar information is processed.
14acf4
14acf4
In future, we should implement https://fedorahosted.org/sssd/ticket/2943
14acf4
we will again be able to process all the data in a single transaction.
14acf4
14acf4
Reviewed-by: Sumit Bose <sbose@redhat.com>
14acf4
(cherry picked from commit c32266e79f9d4bebd0c31eaa8d6fa26050e7fb3e)
14acf4
(cherry picked from commit 19194cb18a1cc20f02423861dd831aa5bc3a1003)
14acf4
---
14acf4
 src/providers/ldap/sdap_async_groups.c        |  49 +-
14acf4
 src/providers/ldap/sdap_async_nested_groups.c | 618 +++++++++++++++++++++++++-
14acf4
 src/providers/ldap/sdap_async_private.h       |  16 +-
14acf4
 src/tests/cmocka/test_nested_groups.c         |   4 +-
14acf4
 4 files changed, 659 insertions(+), 28 deletions(-)
14acf4
14acf4
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
14acf4
index 57a53af3f4eb46e6f31af9ee7c4d4625239d2a54..485f79d2fb2e7e34f1420b43baadb40ab3f96d17 100644
14acf4
--- a/src/providers/ldap/sdap_async_groups.c
14acf4
+++ b/src/providers/ldap/sdap_async_groups.c
14acf4
@@ -1728,6 +1728,7 @@ struct sdap_get_groups_state {
14acf4
     struct sysdb_attrs **groups;
14acf4
     size_t count;
14acf4
     size_t check_count;
14acf4
+    hash_table_t *missing_external;
14acf4
 
14acf4
     hash_table_t *user_hash;
14acf4
     hash_table_t *group_hash;
14acf4
@@ -2289,6 +2290,8 @@ int sdap_get_groups_recv(struct tevent_req *req,
14acf4
     return EOK;
14acf4
 }
14acf4
 
14acf4
+static void sdap_nested_ext_done(struct tevent_req *subreq);
14acf4
+
14acf4
 static void sdap_nested_done(struct tevent_req *subreq)
14acf4
 {
14acf4
     errno_t ret, tret;
14acf4
@@ -2304,7 +2307,8 @@ static void sdap_nested_done(struct tevent_req *subreq)
14acf4
                                             struct sdap_get_groups_state);
14acf4
 
14acf4
     ret = sdap_nested_group_recv(state, subreq, &user_count, &users,
14acf4
-                                 &group_count, &groups);
14acf4
+                                 &group_count, &groups,
14acf4
+                                 &state->missing_external);
14acf4
     talloc_zfree(subreq);
14acf4
     if (ret != EOK) {
14acf4
         DEBUG(SSSDBG_CRIT_FAILURE, "Nested group processing failed: [%d][%s]\n",
14acf4
@@ -2343,8 +2347,25 @@ static void sdap_nested_done(struct tevent_req *subreq)
14acf4
     }
14acf4
     in_transaction = false;
14acf4
 
14acf4
-    /* Processing complete */
14acf4
-    tevent_req_done(req);
14acf4
+    if (hash_count(state->missing_external) == 0) {
14acf4
+        /* No external members. Processing complete */
14acf4
+        DEBUG(SSSDBG_TRACE_INTERNAL, "No external members, done");
14acf4
+        tevent_req_done(req);
14acf4
+        return;
14acf4
+    }
14acf4
+
14acf4
+    /* At the moment, we need to save the direct groups & members in one
14acf4
+     * transaction and then query the others in a separate requests
14acf4
+     */
14acf4
+    subreq = sdap_nested_group_lookup_external_send(state, state->ev,
14acf4
+                                                    state->dom,
14acf4
+                                                    state->opts->ext_ctx,
14acf4
+                                                    state->missing_external);
14acf4
+    if (subreq == NULL) {
14acf4
+        ret = ENOMEM;
14acf4
+        goto fail;
14acf4
+    }
14acf4
+    tevent_req_set_callback(subreq, sdap_nested_ext_done, req);
14acf4
     return;
14acf4
 
14acf4
 fail:
14acf4
@@ -2357,6 +2378,28 @@ fail:
14acf4
     tevent_req_error(req, ret);
14acf4
 }
14acf4
 
14acf4
+static void sdap_nested_ext_done(struct tevent_req *subreq)
14acf4
+{
14acf4
+    errno_t ret;
14acf4
+    struct tevent_req *req = tevent_req_callback_data(subreq,
14acf4
+                                                      struct tevent_req);
14acf4
+    struct sdap_get_groups_state *state = tevent_req_data(req,
14acf4
+                                            struct sdap_get_groups_state);
14acf4
+
14acf4
+    ret = sdap_nested_group_lookup_external_recv(state, subreq);
14acf4
+    talloc_free(subreq);
14acf4
+    if (ret != EOK) {
14acf4
+        DEBUG(SSSDBG_OP_FAILURE,
14acf4
+              "Cannot resolve external members [%d]: %s\n",
14acf4
+              ret, sss_strerror(ret));
14acf4
+        tevent_req_error(req, ret);
14acf4
+        return;
14acf4
+    }
14acf4
+
14acf4
+    tevent_req_done(req);
14acf4
+    return;
14acf4
+}
14acf4
+
14acf4
 static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
14acf4
                                                 struct sysdb_ctx *sysdb,
14acf4
                                                 struct sss_domain_info *domain,
14acf4
diff --git a/src/providers/ldap/sdap_async_nested_groups.c b/src/providers/ldap/sdap_async_nested_groups.c
14acf4
index 08e199869ad16c3b19d998a2a28eae9a0dd0a371..f5be6b5a5946453151918f97aa0632df4b218a29 100644
14acf4
--- a/src/providers/ldap/sdap_async_nested_groups.c
14acf4
+++ b/src/providers/ldap/sdap_async_nested_groups.c
14acf4
@@ -55,6 +55,13 @@ struct sdap_nested_group_member {
14acf4
     const char *group_filter;
14acf4
 };
14acf4
 
14acf4
+const size_t external_members_chunk = 16;
14acf4
+
14acf4
+struct sdap_external_missing_member {
14acf4
+    const char **parent_group_dns;
14acf4
+    size_t parent_dn_idx;
14acf4
+};
14acf4
+
14acf4
 struct sdap_nested_group_ctx {
14acf4
     struct sss_domain_info *domain;
14acf4
     struct sdap_options *opts;
14acf4
@@ -63,6 +70,7 @@ struct sdap_nested_group_ctx {
14acf4
     struct sdap_handle *sh;
14acf4
     hash_table_t *users;
14acf4
     hash_table_t *groups;
14acf4
+    hash_table_t *missing_external;
14acf4
     bool try_deref;
14acf4
     int deref_treshold;
14acf4
     int max_nesting_level;
14acf4
@@ -183,37 +191,32 @@ done:
14acf4
     return ret;
14acf4
 }
14acf4
 
14acf4
-static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
14acf4
-                                            struct sysdb_attrs *entry,
14acf4
-                                            const char *table_name)
14acf4
+static errno_t sdap_nested_group_hash_insert(hash_table_t *table,
14acf4
+                                             const char *entry_key,
14acf4
+                                             void *entry_value,
14acf4
+                                             bool overwrite,
14acf4
+                                             const char *table_name)
14acf4
 {
14acf4
     hash_key_t key;
14acf4
     hash_value_t value;
14acf4
-    const char *name = NULL;
14acf4
-    errno_t ret;
14acf4
     int hret;
14acf4
 
14acf4
-    ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
14acf4
-    if (ret != EOK) {
14acf4
-        return ret;
14acf4
-    }
14acf4
-
14acf4
     DEBUG(SSSDBG_TRACE_ALL, "Inserting [%s] into hash table [%s]\n",
14acf4
-                             name, table_name);
14acf4
+                             entry_key, table_name);
14acf4
 
14acf4
     key.type = HASH_KEY_STRING;
14acf4
-    key.str = talloc_strdup(NULL, name);
14acf4
+    key.str = talloc_strdup(NULL, entry_key);
14acf4
     if (key.str == NULL) {
14acf4
         return ENOMEM;
14acf4
     }
14acf4
 
14acf4
-    if (hash_has_key(table, &key)) {
14acf4
+    if (overwrite == false && hash_has_key(table, &key)) {
14acf4
         talloc_free(key.str);
14acf4
         return EEXIST;
14acf4
     }
14acf4
 
14acf4
     value.type = HASH_VALUE_PTR;
14acf4
-    value.ptr = entry;
14acf4
+    value.ptr = entry_value;
14acf4
 
14acf4
     hret = hash_enter(table, &key, &value);
14acf4
     if (hret != HASH_SUCCESS) {
14acf4
@@ -227,6 +230,21 @@ static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
14acf4
     return EOK;
14acf4
 }
14acf4
 
14acf4
+static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
14acf4
+                                            struct sysdb_attrs *entry,
14acf4
+                                            const char *table_name)
14acf4
+{
14acf4
+    const char *name = NULL;
14acf4
+    errno_t ret;
14acf4
+
14acf4
+    ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
14acf4
+    if (ret != EOK) {
14acf4
+        return ret;
14acf4
+    }
14acf4
+
14acf4
+    return sdap_nested_group_hash_insert(table, name, entry, false, table_name);
14acf4
+}
14acf4
+
14acf4
 static errno_t
14acf4
 sdap_nested_group_hash_user(struct sdap_nested_group_ctx *group_ctx,
14acf4
                             struct sysdb_attrs *user)
14acf4
@@ -296,6 +314,76 @@ sdap_nested_group_hash_group(struct sdap_nested_group_ctx *group_ctx,
14acf4
     return sdap_nested_group_hash_entry(group_ctx->groups, group, "groups");
14acf4
 }
14acf4
 
14acf4
+static errno_t sdap_nested_group_external_add(hash_table_t *table,
14acf4
+                                              const char *ext_member,
14acf4
+                                              const char *parent_group_dn)
14acf4
+{
14acf4
+    hash_key_t key;
14acf4
+    hash_value_t value;
14acf4
+    int hret;
14acf4
+    int ret;
14acf4
+    struct sdap_external_missing_member *ext_mem;
14acf4
+
14acf4
+    key.type = HASH_KEY_STRING;
14acf4
+    key.str = discard_const(ext_member);
14acf4
+
14acf4
+    DEBUG(SSSDBG_TRACE_ALL,
14acf4
+          "Inserting external member [%s] into external members hash table\n",
14acf4
+          ext_member);
14acf4
+
14acf4
+    hret = hash_lookup(table, &key, &value);
14acf4
+    switch (hret) {
14acf4
+    case HASH_ERROR_KEY_NOT_FOUND:
14acf4
+        ext_mem = talloc_zero(table, struct sdap_external_missing_member);
14acf4
+        if (ext_mem == NULL) {
14acf4
+            return ENOMEM;
14acf4
+        }
14acf4
+        ext_mem->parent_group_dns = talloc_zero_array(ext_mem,
14acf4
+                                                      const char *,
14acf4
+                                                      external_members_chunk);
14acf4
+        if (ext_mem->parent_group_dns == NULL) {
14acf4
+            talloc_free(ext_mem);
14acf4
+            return ENOMEM;
14acf4
+        }
14acf4
+
14acf4
+        ret = sdap_nested_group_hash_insert(table, ext_member, ext_mem,
14acf4
+                                            true, "missing external users");
14acf4
+        if (ret != EOK) {
14acf4
+            return ret;
14acf4
+        }
14acf4
+        break;
14acf4
+
14acf4
+    case HASH_SUCCESS:
14acf4
+        ext_mem = talloc_get_type(value.ptr,
14acf4
+                                  struct sdap_external_missing_member);
14acf4
+        if (ext_mem->parent_dn_idx == \
14acf4
+                talloc_array_length(ext_mem->parent_group_dns)) {
14acf4
+            ext_mem->parent_group_dns = talloc_realloc(ext_mem,
14acf4
+                                                ext_mem->parent_group_dns,
14acf4
+                                                const char *,
14acf4
+                                                ext_mem->parent_dn_idx + \
14acf4
+                                                    external_members_chunk);
14acf4
+            if (ext_mem->parent_group_dns == NULL) {
14acf4
+                talloc_free(ext_mem);
14acf4
+                return ENOMEM;
14acf4
+            }
14acf4
+        }
14acf4
+        break;
14acf4
+    default:
14acf4
+        return EIO;
14acf4
+    }
14acf4
+
14acf4
+    ext_mem->parent_group_dns[ext_mem->parent_dn_idx] = \
14acf4
+                                        talloc_strdup(ext_mem->parent_group_dns,
14acf4
+                                                      parent_group_dn);
14acf4
+    if (ext_mem->parent_group_dns[ext_mem->parent_dn_idx] == NULL) {
14acf4
+        return ENOMEM;
14acf4
+    }
14acf4
+    ext_mem->parent_dn_idx++;
14acf4
+
14acf4
+    return EOK;
14acf4
+}
14acf4
+
14acf4
 static errno_t sdap_nested_group_sysdb_search(struct sss_domain_info *domain,
14acf4
                                               const char *filter,
14acf4
                                               bool user)
14acf4
@@ -477,6 +565,13 @@ sdap_nested_group_split_members(TALLOC_CTX *mem_ctx,
14acf4
     errno_t ret;
14acf4
     int i;
14acf4
 
14acf4
+    if (members == NULL) {
14acf4
+        *_missing = NULL;
14acf4
+        *_num_missing = 0;
14acf4
+        *_num_groups = 0;
14acf4
+        return EOK;
14acf4
+    }
14acf4
+
14acf4
     tmp_ctx = talloc_new(NULL);
14acf4
     if (tmp_ctx == NULL) {
14acf4
         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
14acf4
@@ -618,6 +713,66 @@ done:
14acf4
     return ret;
14acf4
 }
14acf4
 
14acf4
+static errno_t
14acf4
+sdap_nested_group_add_ext_members(TALLOC_CTX *mem_ctx,
14acf4
+                                  struct sdap_nested_group_ctx *group_ctx,
14acf4
+                                  struct sysdb_attrs *group,
14acf4
+                                  struct ldb_message_element *ext_members)
14acf4
+{
14acf4
+    errno_t ret;
14acf4
+    const char *ext_member_attr;
14acf4
+    const char *orig_dn;
14acf4
+    size_t i;
14acf4
+
14acf4
+    if (ext_members == NULL) {
14acf4
+        return EOK;
14acf4
+    }
14acf4
+
14acf4
+    ret = sysdb_attrs_get_string(group, SYSDB_ORIG_DN, &orig_dn);
14acf4
+    if (ret != EOK) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE, "A group with no originalDN!?!\n");
14acf4
+        return ret;
14acf4
+    }
14acf4
+
14acf4
+    for (i = 0; i < ext_members->num_values; i++) {
14acf4
+        ext_member_attr = (const char *) ext_members->values[i].data;
14acf4
+
14acf4
+        ret = sdap_nested_group_external_add(group_ctx->missing_external,
14acf4
+                                             ext_member_attr,
14acf4
+                                             orig_dn);
14acf4
+        if (ret != EOK) {
14acf4
+            DEBUG(SSSDBG_CRIT_FAILURE,
14acf4
+                    "Cannot add %s into external members [%d]: %s\n",
14acf4
+                    ext_member_attr, ret, sss_strerror(ret));
14acf4
+            return ret;
14acf4
+        }
14acf4
+    }
14acf4
+
14acf4
+    return EOK;
14acf4
+}
14acf4
+
14acf4
+static struct ldb_message_element *
14acf4
+sdap_nested_group_ext_members(struct sdap_options *opts,
14acf4
+                              struct sysdb_attrs *group)
14acf4
+{
14acf4
+    errno_t ret;
14acf4
+    struct ldb_message_element *ext_members = NULL;
14acf4
+
14acf4
+    if (opts->ext_ctx == NULL) {
14acf4
+        return NULL;
14acf4
+    }
14acf4
+
14acf4
+    ret = sysdb_attrs_get_el_ext(group,
14acf4
+                 opts->group_map[SDAP_AT_GROUP_EXT_MEMBER].sys_name,
14acf4
+                 false, &ext_members);
14acf4
+    if (ret != EOK && ret != ENOENT) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve external member list "
14acf4
+                                   "[%d]: %s\n", ret, sss_strerror(ret));
14acf4
+    }
14acf4
+
14acf4
+    return ext_members;
14acf4
+}
14acf4
+
14acf4
 
14acf4
 struct sdap_nested_group_state {
14acf4
     struct sdap_nested_group_ctx *group_ctx;
14acf4
@@ -666,6 +821,14 @@ sdap_nested_group_send(TALLOC_CTX *mem_ctx,
14acf4
         goto immediately;
14acf4
     }
14acf4
 
14acf4
+    ret = sss_hash_create(state->group_ctx, 32,
14acf4
+                          &state->group_ctx->missing_external);
14acf4
+    if (ret != EOK) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
14acf4
+                                    ret, strerror(ret));
14acf4
+        goto immediately;
14acf4
+    }
14acf4
+
14acf4
     state->group_ctx->try_deref = true;
14acf4
     state->group_ctx->deref_treshold = dp_opt_get_int(opts->basic,
14acf4
                                                       SDAP_DEREF_THRESHOLD);
14acf4
@@ -759,7 +922,8 @@ errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
14acf4
                                unsigned long *_num_users,
14acf4
                                struct sysdb_attrs ***_users,
14acf4
                                unsigned long *_num_groups,
14acf4
-                               struct sysdb_attrs ***_groups)
14acf4
+                               struct sysdb_attrs ***_groups,
14acf4
+                               hash_table_t **_missing_external)
14acf4
 {
14acf4
     struct sdap_nested_group_state *state = NULL;
14acf4
     struct sysdb_attrs **users = NULL;
14acf4
@@ -806,6 +970,11 @@ errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
14acf4
         *_groups = talloc_steal(mem_ctx, groups);
14acf4
     }
14acf4
 
14acf4
+    if (_missing_external) {
14acf4
+        *_missing_external = talloc_steal(mem_ctx,
14acf4
+                                          state->group_ctx->missing_external);
14acf4
+    }
14acf4
+
14acf4
     return EOK;
14acf4
 }
14acf4
 
14acf4
@@ -815,6 +984,7 @@ struct sdap_nested_group_process_state {
14acf4
     struct sdap_nested_group_member *missing;
14acf4
     int num_missing_total;
14acf4
     int num_missing_groups;
14acf4
+    struct ldb_message_element *ext_members;
14acf4
     int nesting_level;
14acf4
     char *group_dn;
14acf4
     bool deref;
14acf4
@@ -865,13 +1035,16 @@ sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
14acf4
 
14acf4
     DEBUG(SSSDBG_TRACE_INTERNAL, "About to process group [%s]\n", orig_dn);
14acf4
 
14acf4
-    /* get member list */
14acf4
+    /* get member list, both direct and external */
14acf4
+    state->ext_members = sdap_nested_group_ext_members(state->group_ctx->opts,
14acf4
+                                                       group);
14acf4
+
14acf4
     ret = sysdb_attrs_get_el_ext(group, group_map[SDAP_AT_GROUP_MEMBER].sys_name,
14acf4
                                  false, &members);
14acf4
-    if (ret == ENOENT) {
14acf4
-        ret = EOK; /* no members */
14acf4
+    if (ret == ENOENT && state->ext_members == NULL) {
14acf4
+        ret = EOK; /* no members, direct or external */
14acf4
         goto immediately;
14acf4
-    } else if (ret != EOK) {
14acf4
+    } else if (ret != EOK && ret != ENOENT) {
14acf4
         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve member list "
14acf4
                                     "[%d]: %s\n", ret, strerror(ret));
14acf4
         goto immediately;
14acf4
@@ -889,14 +1062,31 @@ sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
14acf4
         goto immediately;
14acf4
     }
14acf4
 
14acf4
-    DEBUG(SSSDBG_TRACE_INTERNAL, "Looking up %d/%d members of group [%s]\n",
14acf4
-          state->num_missing_total, members->num_values, orig_dn);
14acf4
+    ret = sdap_nested_group_add_ext_members(state,
14acf4
+                                            state->group_ctx,
14acf4
+                                            group,
14acf4
+                                            state->ext_members);
14acf4
+    if (ret != EOK) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to split external member list "
14acf4
+                                    "[%d]: %s\n", ret, sss_strerror(ret));
14acf4
+        goto immediately;
14acf4
+    }
14acf4
 
14acf4
-    if (state->num_missing_total == 0) {
14acf4
+    if (state->num_missing_total == 0
14acf4
+            && hash_count(state->group_ctx->missing_external) == 0) {
14acf4
         ret = EOK; /* we're done */
14acf4
         goto immediately;
14acf4
     }
14acf4
 
14acf4
+    /* If there are only indirect members of the group, it's still safe to
14acf4
+     * proceed and let the direct lookup code just fall through.
14acf4
+     */
14acf4
+
14acf4
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Looking up %d/%d members of group [%s]\n",
14acf4
+                                 state->num_missing_total,
14acf4
+                                 members ? members->num_values : 0,
14acf4
+                                 orig_dn);
14acf4
+
14acf4
     /* process members */
14acf4
     if (group_ctx->try_deref
14acf4
             && state->num_missing_total > group_ctx->deref_treshold) {
14acf4
@@ -2332,3 +2522,387 @@ static errno_t sdap_nested_group_deref_recv(struct tevent_req *req)
14acf4
 
14acf4
     return EOK;
14acf4
 }
14acf4
+
14acf4
+struct sdap_ext_member {
14acf4
+    struct sdap_external_missing_member *missing_mem;
14acf4
+    const char *ext_member_attr;
14acf4
+
14acf4
+    enum sysdb_member_type member_type;
14acf4
+    struct sss_domain_info *dom;
14acf4
+    struct sysdb_attrs *attrs;
14acf4
+};
14acf4
+
14acf4
+struct sdap_nested_group_lookup_external_state {
14acf4
+    struct tevent_context *ev;
14acf4
+    struct sdap_ext_member_ctx *ext_ctx;
14acf4
+    struct sss_domain_info *group_dom;
14acf4
+    hash_table_t *missing_external;
14acf4
+
14acf4
+    hash_entry_t *entries;
14acf4
+    unsigned long n_entries;
14acf4
+    unsigned long eniter;
14acf4
+
14acf4
+    struct sdap_ext_member *ext_members;
14acf4
+
14acf4
+    ext_member_send_fn_t ext_member_resolve_send;
14acf4
+    ext_member_recv_fn_t ext_member_resolve_recv;
14acf4
+};
14acf4
+
14acf4
+static errno_t
14acf4
+sdap_nested_group_lookup_external_step(struct tevent_req *req);
14acf4
+static void
14acf4
+sdap_nested_group_lookup_external_done(struct tevent_req *subreq);
14acf4
+static errno_t
14acf4
+sdap_nested_group_lookup_external_link(struct tevent_req *req);
14acf4
+static errno_t
14acf4
+sdap_nested_group_lookup_external_link_member(
14acf4
+                        struct sdap_nested_group_lookup_external_state *state,
14acf4
+                        struct sdap_ext_member *member);
14acf4
+static errno_t
14acf4
+sdap_nested_group_memberof_dn_by_original_dn(
14acf4
+                            TALLOC_CTX *mem_ctx,
14acf4
+                            struct sss_domain_info *group_dom,
14acf4
+                            const char *original_dn,
14acf4
+                            const char ***_parents);
14acf4
+
14acf4
+struct tevent_req *
14acf4
+sdap_nested_group_lookup_external_send(TALLOC_CTX *mem_ctx,
14acf4
+                                       struct tevent_context *ev,
14acf4
+                                       struct sss_domain_info *group_dom,
14acf4
+                                       struct sdap_ext_member_ctx *ext_ctx,
14acf4
+                                       hash_table_t *missing_external)
14acf4
+{
14acf4
+    struct sdap_nested_group_lookup_external_state *state = NULL;
14acf4
+    struct tevent_req *req = NULL;
14acf4
+    errno_t ret;
14acf4
+
14acf4
+    req = tevent_req_create(mem_ctx, &state,
14acf4
+                            struct sdap_nested_group_lookup_external_state);
14acf4
+    if (req == NULL) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
14acf4
+        return NULL;
14acf4
+    }
14acf4
+
14acf4
+    state->ev = ev;
14acf4
+    state->group_dom = group_dom;
14acf4
+    state->ext_ctx = ext_ctx;
14acf4
+    state->missing_external = missing_external;
14acf4
+
14acf4
+    if (state->ext_ctx->ext_member_resolve_send == NULL
14acf4
+            || state->ext_ctx->ext_member_resolve_recv == NULL) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE, "Wrong private context\n");
14acf4
+        ret = EINVAL;
14acf4
+        goto immediately;
14acf4
+    }
14acf4
+
14acf4
+    ret = hash_entries(state->missing_external,
14acf4
+                       &state->n_entries, &state->entries);
14acf4
+    if (ret != HASH_SUCCESS) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE, "hash_entries returned %d\n", ret);
14acf4
+        ret = EIO;
14acf4
+        goto immediately;
14acf4
+    }
14acf4
+    state->eniter = 0;
14acf4
+
14acf4
+    state->ext_members = talloc_zero_array(state,
14acf4
+                                           struct sdap_ext_member,
14acf4
+                                           state->n_entries);
14acf4
+    if (state->ext_members == NULL) {
14acf4
+        ret = ENOMEM;
14acf4
+        goto immediately;
14acf4
+    }
14acf4
+
14acf4
+    ret = sdap_nested_group_lookup_external_step(req);
14acf4
+    if (ret != EAGAIN) {
14acf4
+        goto immediately;
14acf4
+    }
14acf4
+
14acf4
+    return req;
14acf4
+
14acf4
+immediately:
14acf4
+    if (ret == EOK) {
14acf4
+        tevent_req_done(req);
14acf4
+    } else {
14acf4
+        tevent_req_error(req, ret);
14acf4
+    }
14acf4
+    tevent_req_post(req, ev);
14acf4
+    return req;
14acf4
+}
14acf4
+
14acf4
+static errno_t
14acf4
+sdap_nested_group_lookup_external_step(struct tevent_req *req)
14acf4
+{
14acf4
+    struct tevent_req *subreq = NULL;
14acf4
+    struct sdap_nested_group_lookup_external_state *state = NULL;
14acf4
+    state = tevent_req_data(req,
14acf4
+                            struct sdap_nested_group_lookup_external_state);
14acf4
+
14acf4
+    subreq = state->ext_ctx->ext_member_resolve_send(state,
14acf4
+                                        state->ev,
14acf4
+                                        state->entries[state->eniter].key.str,
14acf4
+                                        state->ext_ctx->pvt);
14acf4
+    if (subreq == NULL) {
14acf4
+        return ENOMEM;
14acf4
+    }
14acf4
+    DEBUG(SSSDBG_TRACE_FUNC, "Refreshing member %lu/%lu\n",
14acf4
+                             state->eniter, state->n_entries);
14acf4
+    tevent_req_set_callback(subreq,
14acf4
+                            sdap_nested_group_lookup_external_done,
14acf4
+                            req);
14acf4
+
14acf4
+    return EAGAIN;
14acf4
+}
14acf4
+
14acf4
+static void
14acf4
+sdap_nested_group_lookup_external_done(struct tevent_req *subreq)
14acf4
+{
14acf4
+    errno_t ret;
14acf4
+    struct tevent_req *req = NULL;
14acf4
+    struct sdap_nested_group_lookup_external_state *state = NULL;
14acf4
+    enum sysdb_member_type member_type;
14acf4
+    struct sysdb_attrs *member;
14acf4
+    struct sss_domain_info *member_dom;
14acf4
+
14acf4
+    req = tevent_req_callback_data(subreq, struct tevent_req);
14acf4
+    state = tevent_req_data(req,
14acf4
+                            struct sdap_nested_group_lookup_external_state);
14acf4
+
14acf4
+    ret = state->ext_ctx->ext_member_resolve_recv(state, subreq,
14acf4
+                                                  &member_type,
14acf4
+                                                  &member_dom,
14acf4
+                                                  &member);
14acf4
+    talloc_free(subreq);
14acf4
+    if (ret == EOK) {
14acf4
+        DEBUG(SSSDBG_TRACE_FUNC, "Refreshing member %lu\n", state->eniter);
14acf4
+        state->ext_members[state->eniter].missing_mem = \
14acf4
+                                    state->entries[state->eniter].value.ptr;
14acf4
+        state->ext_members[state->eniter].dom = member_dom;
14acf4
+
14acf4
+        state->ext_members[state->eniter].ext_member_attr = \
14acf4
+                        talloc_steal(state->ext_members,
14acf4
+                                     state->entries[state->eniter].key.str);
14acf4
+        state->ext_members[state->eniter].member_type = member_type;
14acf4
+        state->ext_members[state->eniter].attrs = \
14acf4
+                            talloc_steal(state->ext_members, member);
14acf4
+    }
14acf4
+
14acf4
+    state->eniter++;
14acf4
+    if (state->eniter >= state->n_entries) {
14acf4
+        DEBUG(SSSDBG_TRACE_FUNC, "All external members processed\n");
14acf4
+        ret = sdap_nested_group_lookup_external_link(req);
14acf4
+        if (ret != EOK) {
14acf4
+            tevent_req_error(req, ret);
14acf4
+            return;
14acf4
+        }
14acf4
+        tevent_req_done(req);
14acf4
+        return;
14acf4
+    }
14acf4
+
14acf4
+    ret = sdap_nested_group_lookup_external_step(req);
14acf4
+    if (ret != EOK && ret != EAGAIN) {
14acf4
+        tevent_req_error(req, ret);
14acf4
+        return;
14acf4
+    }
14acf4
+
14acf4
+    return;
14acf4
+}
14acf4
+
14acf4
+static errno_t
14acf4
+sdap_nested_group_lookup_external_link(struct tevent_req *req)
14acf4
+{
14acf4
+    errno_t ret, tret;
14acf4
+    bool in_transaction = false;
14acf4
+    struct sdap_nested_group_lookup_external_state *state = NULL;
14acf4
+    state = tevent_req_data(req,
14acf4
+                            struct sdap_nested_group_lookup_external_state);
14acf4
+    size_t i;
14acf4
+
14acf4
+    ret = sysdb_transaction_start(state->group_dom->sysdb);
14acf4
+    if (ret != EOK) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
14acf4
+        goto fail;
14acf4
+    }
14acf4
+    in_transaction = true;
14acf4
+
14acf4
+
14acf4
+    for (i = 0; i < state->eniter; i++) {
14acf4
+        if (state->ext_members[i].attrs == NULL) {
14acf4
+            DEBUG(SSSDBG_MINOR_FAILURE, "The member %s could not be resolved\n",
14acf4
+                                        state->ext_members[i].ext_member_attr);
14acf4
+            continue;
14acf4
+        }
14acf4
+
14acf4
+        ret = sdap_nested_group_lookup_external_link_member(state,
14acf4
+                                                    &state->ext_members[i]);
14acf4
+        if (ret != EOK) {
14acf4
+            goto fail;
14acf4
+        }
14acf4
+    }
14acf4
+
14acf4
+    ret = sysdb_transaction_commit(state->group_dom->sysdb);
14acf4
+    if (ret != EOK) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
14acf4
+        goto fail;
14acf4
+    }
14acf4
+    in_transaction = false;
14acf4
+
14acf4
+    return EOK;
14acf4
+
14acf4
+fail:
14acf4
+    if (in_transaction) {
14acf4
+        tret = sysdb_transaction_cancel(state->group_dom->sysdb);
14acf4
+        if (tret != EOK) {
14acf4
+            DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
14acf4
+        }
14acf4
+    }
14acf4
+    return EFAULT;
14acf4
+}
14acf4
+
14acf4
+static errno_t
14acf4
+sdap_nested_group_lookup_external_link_member(
14acf4
+                        struct sdap_nested_group_lookup_external_state *state,
14acf4
+                        struct sdap_ext_member *member)
14acf4
+{
14acf4
+    const char *name;
14acf4
+    int ret;
14acf4
+    const char **parents = NULL;
14acf4
+    size_t i;
14acf4
+    TALLOC_CTX *tmp_ctx;
14acf4
+    const char *orig_dn;
14acf4
+
14acf4
+    tmp_ctx = talloc_new(state);
14acf4
+    if (tmp_ctx == NULL) {
14acf4
+        return ENOMEM;
14acf4
+    }
14acf4
+
14acf4
+    ret = sysdb_attrs_get_string(member->attrs, SYSDB_NAME, &name);
14acf4
+    if (ret != EOK) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE, "No name for a user\n");
14acf4
+        goto done;
14acf4
+    }
14acf4
+
14acf4
+    /* This only works because the groups were saved in a previous
14acf4
+     * transaction */
14acf4
+    for (i=0; i < member->missing_mem->parent_dn_idx; i++) {
14acf4
+        orig_dn = member->missing_mem->parent_group_dns[i];
14acf4
+        DEBUG(SSSDBG_TRACE_INTERNAL,
14acf4
+              "Linking external members %s from domain %s to parents of %s\n",
14acf4
+              name, member->dom->name, orig_dn);
14acf4
+        ret = sdap_nested_group_memberof_dn_by_original_dn(tmp_ctx,
14acf4
+                                                           state->group_dom,
14acf4
+                                                           orig_dn,
14acf4
+                                                           &parents);
14acf4
+        if (ret != EOK) {
14acf4
+            DEBUG(SSSDBG_MINOR_FAILURE,
14acf4
+                  "Cannot find parents of %s\n", orig_dn);
14acf4
+            continue;
14acf4
+        }
14acf4
+
14acf4
+        /* We don't have to remove the members here, since all members attributes
14acf4
+         * are always written anew
14acf4
+         */
14acf4
+        ret = sysdb_update_members_dn(member->dom, name, member->member_type,
14acf4
+                                      parents, NULL);
14acf4
+        if (ret != EOK) {
14acf4
+            DEBUG(SSSDBG_CRIT_FAILURE, "Cannot link %s@%s to its parents\n",
14acf4
+                                       name, member->dom->name);
14acf4
+            goto done;
14acf4
+        }
14acf4
+
14acf4
+    }
14acf4
+
14acf4
+    ret = EOK;
14acf4
+done:
14acf4
+    talloc_free(tmp_ctx);
14acf4
+    return ret;
14acf4
+}
14acf4
+
14acf4
+static errno_t
14acf4
+sdap_nested_group_memberof_dn_by_original_dn(
14acf4
+                            TALLOC_CTX *mem_ctx,
14acf4
+                            struct sss_domain_info *group_dom,
14acf4
+                            const char *original_dn,
14acf4
+                            const char ***_parents)
14acf4
+{
14acf4
+    errno_t ret;
14acf4
+    char *sanitized_dn;
14acf4
+    char *filter;
14acf4
+    const char *attrs[] = { SYSDB_NAME,
14acf4
+                            SYSDB_MEMBEROF,
14acf4
+                            NULL };
14acf4
+    struct ldb_message **msgs = NULL;
14acf4
+    size_t count;
14acf4
+    TALLOC_CTX *tmp_ctx;
14acf4
+    struct ldb_message_element *memberof;
14acf4
+    const char **parents;
14acf4
+    size_t i;
14acf4
+
14acf4
+    tmp_ctx = talloc_new(mem_ctx);
14acf4
+    if (tmp_ctx == NULL) {
14acf4
+        return ENOMEM;
14acf4
+    }
14acf4
+
14acf4
+    ret = sss_filter_sanitize(tmp_ctx, original_dn, &sanitized_dn);
14acf4
+    if (ret != EOK) {
14acf4
+        DEBUG(SSSDBG_CRIT_FAILURE,
14acf4
+                "Cannot sanitize originalDN [%s]\n", original_dn);
14acf4
+        goto done;
14acf4
+    }
14acf4
+
14acf4
+    filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, sanitized_dn);
14acf4
+    if (filter == NULL) {
14acf4
+        goto done;
14acf4
+    }
14acf4
+
14acf4
+    ret = sysdb_search_groups(tmp_ctx, group_dom, filter, attrs,
14acf4
+                              &count, &msgs);
14acf4
+    if (ret != EOK) {
14acf4
+        goto done;
14acf4
+    }
14acf4
+
14acf4
+    if (count != 1) {
14acf4
+        DEBUG(SSSDBG_OP_FAILURE,
14acf4
+              "More than one entry found by originalDN?\n");
14acf4
+        goto done;
14acf4
+    }
14acf4
+
14acf4
+    memberof = ldb_msg_find_element(msgs[0], SYSDB_MEMBEROF);
14acf4
+    if (memberof == NULL || memberof->num_values == 0) {
14acf4
+        DEBUG(SSSDBG_MINOR_FAILURE,
14acf4
+              "The external group is not a member of any groups\n");
14acf4
+        ret = ENOENT;
14acf4
+        goto done;
14acf4
+    }
14acf4
+
14acf4
+    parents = talloc_zero_array(tmp_ctx,
14acf4
+                                const char *,
14acf4
+                                memberof->num_values + 1);
14acf4
+    if (parents == NULL) {
14acf4
+        ret = ENOMEM;
14acf4
+        goto done;
14acf4
+    }
14acf4
+
14acf4
+    for (i = 0; i < memberof->num_values; i++) {
14acf4
+        parents[i] = talloc_strdup(parents,
14acf4
+                                   (const char *) memberof->values[i].data);
14acf4
+        if (parents[i] == NULL) {
14acf4
+            ret = ENOMEM;
14acf4
+            goto done;
14acf4
+        }
14acf4
+    }
14acf4
+
14acf4
+    *_parents = talloc_steal(mem_ctx, parents);
14acf4
+    ret = EOK;
14acf4
+done:
14acf4
+    talloc_free(tmp_ctx);
14acf4
+    return ret;
14acf4
+}
14acf4
+
14acf4
+errno_t
14acf4
+sdap_nested_group_lookup_external_recv(TALLOC_CTX *mem_ctx,
14acf4
+                                       struct tevent_req *req)
14acf4
+{
14acf4
+    TEVENT_REQ_RETURN_ON_ERROR(req);
14acf4
+
14acf4
+    return EOK;
14acf4
+}
14acf4
diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
14acf4
index db542eaf869efcd53d0937bef3fc6e99cc78b938..9cde6f5dfe0114f797135b4989b9a4bd336a3f27 100644
14acf4
--- a/src/providers/ldap/sdap_async_private.h
14acf4
+++ b/src/providers/ldap/sdap_async_private.h
14acf4
@@ -130,8 +130,20 @@ errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
14acf4
                                unsigned long *_num_users,
14acf4
                                struct sysdb_attrs ***_users,
14acf4
                                unsigned long *_num_groups,
14acf4
-                               struct sysdb_attrs ***_groups);
14acf4
+                               struct sysdb_attrs ***_groups,
14acf4
+                               hash_table_t **missing_external);
14acf4
 
14acf4
+struct tevent_req *
14acf4
+sdap_nested_group_lookup_external_send(TALLOC_CTX *mem_ctx,
14acf4
+                                       struct tevent_context *ev,
14acf4
+                                       struct sss_domain_info *group_dom,
14acf4
+                                       struct sdap_ext_member_ctx *ext_ctx,
14acf4
+                                       hash_table_t *missing_external);
14acf4
+errno_t
14acf4
+sdap_nested_group_lookup_external_recv(TALLOC_CTX *mem_ctx,
14acf4
+                                       struct tevent_req *req);
14acf4
+
14acf4
+/* from sdap_async_initgroups.c */
14acf4
 errno_t sdap_add_incomplete_groups(struct sysdb_ctx *sysdb,
14acf4
                                    struct sss_domain_info *domain,
14acf4
                                    struct sdap_options *opts,
14acf4
@@ -139,7 +151,7 @@ errno_t sdap_add_incomplete_groups(struct sysdb_ctx *sysdb,
14acf4
                                    struct sysdb_attrs **ldap_groups,
14acf4
                                    int ldap_groups_count);
14acf4
 
14acf4
-/* from sdap_async_nested_groups.c */
14acf4
+/* from sdap_ad_groups.c */
14acf4
 errno_t sdap_check_ad_group_type(struct sss_domain_info *dom,
14acf4
                                  struct sdap_options *opts,
14acf4
                                  struct sysdb_attrs *group_attrs,
14acf4
diff --git a/src/tests/cmocka/test_nested_groups.c b/src/tests/cmocka/test_nested_groups.c
14acf4
index 8081ff26102e53b2e453838c3a18e4560ac5317e..22b8caefc92a44a2339cbdcaa98226ebb4dc1ef5 100644
14acf4
--- a/src/tests/cmocka/test_nested_groups.c
14acf4
+++ b/src/tests/cmocka/test_nested_groups.c
14acf4
@@ -57,6 +57,7 @@ struct nested_groups_test_ctx {
14acf4
     struct sdap_domain *sdap_domain;
14acf4
     struct sdap_idmap_ctx *idmap_ctx;
14acf4
     struct sdap_id_ctx *sdap_id_ctx;
14acf4
+    hash_table_t *missing_external;
14acf4
 
14acf4
     struct sysdb_attrs **users;
14acf4
     struct sysdb_attrs **groups;
14acf4
@@ -110,7 +111,8 @@ static void nested_groups_test_done(struct tevent_req *req)
14acf4
 
14acf4
     ctx->tctx->error = sdap_nested_group_recv(ctx, req,
14acf4
                                               &ctx->num_users, &ctx->users,
14acf4
-                                              &ctx->num_groups, &ctx->groups);
14acf4
+                                              &ctx->num_groups, &ctx->groups,
14acf4
+                                              &ctx->missing_external);
14acf4
     talloc_zfree(req);
14acf4
 
14acf4
     ctx->tctx->done = true;
14acf4
-- 
14acf4
2.4.3
14acf4