summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2010-10-15 11:35:31 -0400
committerStephen Gallagher <sgallagh@redhat.com>2010-10-18 13:12:04 -0400
commit55769ee01eac9ce8ce55b29222f14e1c4362fc3c (patch)
treebb98ca4f3863a5b1cebe60267f093abbc7695b1f /src
parentd5d2af3a47753527e96cef47162eecedfb6b2b5b (diff)
downloadsssd-55769ee01eac9ce8ce55b29222f14e1c4362fc3c.tar.gz
sssd-55769ee01eac9ce8ce55b29222f14e1c4362fc3c.tar.bz2
sssd-55769ee01eac9ce8ce55b29222f14e1c4362fc3c.zip
Handle nested groups in RFC2307bis
This first approach handles the non-optimized "pure" RFC2307bis case. It recursively calls into nested groups until it it has found them all or hits the pre-defined nesting limit. It then saves all member users first, then all groups to the sysdb
Diffstat (limited to 'src')
-rw-r--r--src/providers/ldap/sdap_async_accounts.c777
1 files changed, 776 insertions, 1 deletions
diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
index 1684af63..3ee8a4ea 100644
--- a/src/providers/ldap/sdap_async_accounts.c
+++ b/src/providers/ldap/sdap_async_accounts.c
@@ -1458,6 +1458,9 @@ struct sdap_get_groups_state {
struct sysdb_attrs **groups;
size_t count;
size_t check_count;
+
+ hash_table_t *user_hash;
+ hash_table_t *group_hash;
};
static void sdap_get_groups_process(struct tevent_req *subreq);
@@ -1504,6 +1507,15 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
return req;
}
+static struct tevent_req *sdap_nested_group_process_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct sss_domain_info *domain,
+ struct sysdb_ctx *sysdb, struct sysdb_attrs *group,
+ hash_table_t *users, hash_table_t *groups,
+ struct sdap_options *opts, struct sdap_handle *sh,
+ uint32_t nesting);
+static void sdap_nested_done(struct tevent_req *req);
+static errno_t sdap_nested_group_process_recv(struct tevent_req *req);
static void sdap_get_groups_process(struct tevent_req *subreq)
{
struct tevent_req *req =
@@ -1523,9 +1535,56 @@ static void sdap_get_groups_process(struct tevent_req *subreq)
DEBUG(6, ("Search for groups, returned %d results.\n", state->count));
- if (state->count == 0) {
+ switch(state->count) {
+ case 0:
tevent_req_error(req, ENOENT);
return;
+
+ case 1:
+ /* Single group search */
+ if ((state->opts->schema_type == SDAP_SCHEMA_RFC2307) ||
+ (dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL) == 0)) {
+ /* Either this is RFC2307 or we have disabled nested group
+ * support for RFC2307bis. Either way, we'll process the
+ * groups in single-level, multiple-request mode.
+ */
+ break;
+ }
+
+ /* Prepare hashes for nested user processing */
+ ret = sss_hash_create(state, 32, &state->user_hash);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = sss_hash_create(state, 32, &state->group_hash);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = sdap_nested_group_process_send(state,
+ state->ev,
+ state->dom,
+ state->sysdb,
+ state->groups[0],
+ state->user_hash,
+ state->group_hash,
+ state->opts,
+ state->sh,
+ 0);
+ if (!subreq) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ tevent_req_set_callback(subreq, sdap_nested_done, req);
+ return;
+
+ default:
+ /* Enumeration */
+ break;
}
state->check_count = state->count;
@@ -1594,6 +1653,89 @@ int sdap_get_groups_recv(struct tevent_req *req,
return EOK;
}
+static void sdap_nested_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ int hret;
+ unsigned long i;
+ unsigned long count;
+ hash_value_t *values;
+ struct sysdb_attrs **users = NULL;
+ struct sysdb_attrs **groups = NULL;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct sdap_get_groups_state *state = tevent_req_data(req,
+ struct sdap_get_groups_state);
+
+ ret = sdap_nested_group_process_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(1, ("Nested group processing failed: [%d][%s]\n",
+ ret, strerror(ret)));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ hret = hash_values(state->user_hash, &count, &values);
+ if (hret != HASH_SUCCESS) {
+ tevent_req_error(req, EIO);
+ }
+
+ if (count) {
+ users = talloc_array(state, struct sysdb_attrs *, count);
+ if (!users) {
+ talloc_free(values);
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ users[i] = talloc_get_type(values[i].ptr, struct sysdb_attrs);
+ }
+ talloc_zfree(values);
+ }
+
+ /* Save all of the users first so that they are in
+ * place for the groups to add them.
+ */
+ ret = sdap_save_users(state, state->sysdb, state->dom, state->opts,
+ users, count, &state->higher_timestamp);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /* Users are all saved. Now save groups */
+ hret = hash_values(state->group_hash, &count, &values);
+ if (hret != HASH_SUCCESS) {
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ groups = talloc_array(state, struct sysdb_attrs *, count);
+ if (!groups) {
+ talloc_free(values);
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ groups[i] = talloc_get_type(values[i].ptr, struct sysdb_attrs);
+ }
+ talloc_zfree(values);
+
+ ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
+ groups, count, false, &state->higher_timestamp);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /* Processing complete */
+ tevent_req_done(req);
+}
+
+
/* ==Save-fake-group-list=====================================*/
static errno_t sdap_add_incomplete_groups(struct sysdb_ctx *sysdb,
struct sss_domain_info *dom,
@@ -2296,3 +2438,636 @@ int sdap_get_initgr_recv(struct tevent_req *req)
return EOK;
}
+struct sdap_nested_group_ctx {
+ struct tevent_context *ev;
+ struct sysdb_ctx *sysdb;
+ struct sss_domain_info *domain;
+
+ hash_table_t *users;
+ hash_table_t *groups;
+
+ struct sdap_options *opts;
+ struct sdap_handle *sh;
+
+ uint32_t nesting_level;
+
+ struct ldb_message_element *members;
+ uint32_t member_index;
+ char *member_dn;
+};
+
+static errno_t sdap_nested_group_process_step(struct tevent_req *req);
+static struct tevent_req *sdap_nested_group_process_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct sss_domain_info *domain,
+ struct sysdb_ctx *sysdb, struct sysdb_attrs *group,
+ hash_table_t *users, hash_table_t *groups,
+ struct sdap_options *opts, struct sdap_handle *sh,
+ uint32_t nesting)
+{
+ errno_t ret;
+ int hret;
+ struct tevent_req *req;
+ struct sdap_nested_group_ctx *state;
+ const char *groupname;
+ hash_key_t key;
+ hash_value_t value;
+
+ req = tevent_req_create(mem_ctx, &state, struct sdap_nested_group_ctx);
+ if (!req) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->sysdb = sysdb;
+ state->domain = domain;
+ state->users = users;
+ state->groups = groups;
+ state->opts = opts;
+ state->sh = sh;
+ state->nesting_level = nesting;
+
+ /* If this is too many levels deep, just return success */
+ if (nesting > dp_opt_get_int(opts->basic, SDAP_NESTING_LEVEL)) {
+ ret = EOK;
+ goto immediate;
+ }
+
+ /* Add the current group to the groups hash so we don't
+ * look it up more than once
+ */
+ key.type = HASH_KEY_STRING;
+
+ ret = sysdb_attrs_get_string(
+ group,
+ opts->group_map[SDAP_AT_GROUP_NAME].sys_name,
+ &groupname);
+ if (ret != EOK) goto immediate;
+
+ key.str = talloc_strdup(state, groupname);
+ if (!key.str) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ if (hash_has_key(groups, &key)) {
+ /* This group has already been processed
+ * (or is in progress)
+ * Skip it and just return success
+ */
+ ret = EOK;
+ goto immediate;
+ }
+
+ value.type = HASH_VALUE_PTR;
+ value.ptr = talloc_steal(groups, group);
+
+ hret = hash_enter(groups, &key, &value);
+ if (hret != HASH_SUCCESS) {
+ ret = EIO;
+ goto immediate;
+ }
+ talloc_free(key.str);
+
+ /* Process group memberships */
+
+ /* TODO: future enhancement, check for memberuid as well
+ * See https://fedorahosted.org/sssd/ticket/445
+ */
+
+ ret = sysdb_attrs_get_el(
+ group,
+ opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
+ &state->members);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ /* No members to process */
+ ret = EOK;
+ }
+ goto immediate;
+ }
+
+ state->member_index = 0;
+
+ ret = sdap_nested_group_process_step(req);
+ if (ret != EAGAIN) goto immediate;
+
+ return req;
+
+immediate:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+
+static void sdap_nested_group_process_ldap_user(struct tevent_req *subreq);
+static void sdap_nested_group_process_user(struct tevent_req *subreq);
+static errno_t sdap_nested_group_lookup_user(struct tevent_req *req,
+ tevent_req_fn fn);
+static errno_t sdap_nested_group_lookup_group(struct tevent_req *req);
+static errno_t sdap_nested_group_process_step(struct tevent_req *req)
+{
+ errno_t ret;
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+ char *filter;
+ static const char *attrs[] = SYSDB_PW_ATTRS;
+ size_t count;
+ struct ldb_message **msgs;
+ uint64_t expiration;
+ bool has_key = false;
+ hash_key_t key;
+ uint8_t *data;
+ time_t now = time(NULL);
+
+ while (true) {
+ /* Continue to loop through until all entries have been
+ * processed.
+ */
+ do {
+ if (state->member_index >= state->members->num_values) {
+ /* No more entries to check. Return success */
+ return EOK;
+ }
+
+ /* First check whether this origDN is present (and not expired)
+ * in the sysdb
+ */
+ data = state->members->values[state->member_index].data;
+ state->member_dn = talloc_strdup(state, (const char *)data);
+ if (!state->member_dn) {
+ ret = ENOMEM;
+ goto error;
+ }
+
+ /* Check the user hash
+ * If it's there, we can save ourselves a trip to the
+ * sysdb and possibly LDAP as well
+ */
+ key.type = HASH_KEY_STRING;
+ key.str = state->member_dn;
+ has_key = hash_has_key(state->users, &key);
+ if (has_key) {
+ talloc_zfree(state->member_dn);
+ state->member_index++;
+ continue;
+ }
+
+
+ } while (has_key);
+
+ /* Check for the specified origDN in the sysdb */
+ filter = talloc_asprintf(NULL, "(%s=%s)",
+ SYSDB_ORIG_DN,
+ state->member_dn);
+ if (!filter) {
+ ret = ENOMEM;
+ goto error;
+ }
+
+ /* Try users first */
+ ret = sysdb_search_users(state, state->sysdb, state->domain, filter,
+ attrs, &count, &msgs);
+ talloc_zfree(filter);
+ if (ret != EOK && ret != ENOENT) {
+ goto error;
+ } if (ret == ENOENT || count == 0) {
+ /* It wasn't a user. Check whether it's a group */
+ if (ret == EOK) talloc_zfree(msgs);
+
+ filter = talloc_asprintf(NULL, "(%s=%s)",
+ SYSDB_ORIG_DN,
+ state->member_dn);
+ if (!filter) {
+ ret = ENOMEM;
+ goto error;
+ }
+ ret = sysdb_search_groups(state, state->sysdb, state->domain,
+ filter, attrs, &count, &msgs);
+ talloc_zfree(filter);
+ if (ret != EOK && ret != ENOENT) {
+ ret = EIO;
+ goto error;
+ } else if (ret == ENOENT || count == 9) {
+ if (ret == EOK) talloc_zfree(msgs);
+
+ /* It wasn't found in the groups either
+ * We'll have to do a blind lookup for both
+ */
+
+ /* Try users first */
+ ret = sdap_nested_group_lookup_user(
+ req, sdap_nested_group_process_ldap_user);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ }
+ return EAGAIN;
+ }
+
+ /* We found a group with this origDN in the sysdb */
+
+ /* Check whether the entry is valid */
+ if (count != 1) {
+ DEBUG(1, ("More than one entry with this origDN? Skipping\n"));
+ state->member_index++;
+ talloc_zfree(state->member_dn);
+ continue;
+ }
+
+ expiration = ldb_msg_find_attr_as_uint64(msgs[0],
+ SYSDB_CACHE_EXPIRE,
+ 0);
+ if (expiration && expiration > now) {
+ DEBUG(6, ("Cached values are still valid. Skipping\n"));
+ state->member_index++;
+ talloc_zfree(state->member_dn);
+ continue;
+ }
+
+ /* Refresh the group from LDAP */
+ ret = sdap_nested_group_lookup_group(req);
+ if (ret != EOK) goto error;
+
+ return EAGAIN;
+ }
+
+ /* We found a user with this origDN in the sysdb */
+
+ /* Check whether the entry is valid */
+ if (count != 1) {
+ DEBUG(1, ("More than one entry with this origDN? Skipping\n"));
+ state->member_index++;
+ talloc_zfree(state->member_dn);
+ continue;
+ }
+
+ expiration = ldb_msg_find_attr_as_uint64(msgs[0],
+ SYSDB_CACHE_EXPIRE,
+ 0);
+ if (expiration && expiration > now) {
+ DEBUG(6, ("Cached values are still valid. Skipping\n"));
+ state->member_index++;
+ talloc_zfree(state->member_dn);
+ continue;
+ }
+
+ /* Refresh the user from LDAP */
+ ret = sdap_nested_group_lookup_user(
+ req, sdap_nested_group_process_user);
+ if (ret != EOK) goto error;
+
+ return EAGAIN;
+ } /* while (true) */
+
+error:
+ talloc_zfree(state->member_dn);
+ return ret;
+}
+
+static errno_t sdap_nested_group_lookup_user(struct tevent_req *req,
+ tevent_req_fn fn)
+{
+ errno_t ret;
+ const char **sdap_attrs;
+ char *filter;
+ struct tevent_req *subreq;
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+
+ ret = build_attrs_from_map(state, state->opts->user_map,
+ SDAP_OPTS_USER, &sdap_attrs);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ filter = talloc_asprintf(
+ sdap_attrs, "(objectclass=%s)",
+ state->opts->user_map[SDAP_OC_USER].name);
+ if (!filter) {
+ talloc_free(sdap_attrs);
+ return ENOMEM;
+ }
+
+ subreq = sdap_get_generic_send(state, state->ev, state->opts,
+ state->sh, state->member_dn,
+ LDAP_SCOPE_BASE,
+ filter, sdap_attrs,
+ state->opts->user_map,
+ SDAP_OPTS_USER);
+ if (!subreq) {
+ talloc_free(sdap_attrs);
+ return EIO;
+ }
+ talloc_steal(subreq, sdap_attrs);
+
+ tevent_req_set_callback(subreq, fn, req);
+ return EOK;
+}
+
+static void sdap_nested_group_process_group(struct tevent_req *subreq);
+static errno_t sdap_nested_group_lookup_group(struct tevent_req *req)
+{
+ errno_t ret;
+ const char **sdap_attrs;
+ char *filter;
+ struct tevent_req *subreq;
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+
+ ret = build_attrs_from_map(state, state->opts->group_map,
+ SDAP_OPTS_GROUP, &sdap_attrs);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ filter = talloc_asprintf(
+ sdap_attrs, "(objectclass=%s)",
+ state->opts->group_map[SDAP_OC_GROUP].name);
+ if (!filter) {
+ talloc_free(sdap_attrs);
+ return ENOMEM;
+ }
+
+ subreq = sdap_get_generic_send(state, state->ev, state->opts,
+ state->sh, state->member_dn,
+ LDAP_SCOPE_BASE,
+ filter, sdap_attrs,
+ state->opts->group_map,
+ SDAP_OPTS_GROUP);
+ if (!subreq) {
+ talloc_free(sdap_attrs);
+ return EIO;
+ }
+ talloc_steal(subreq, sdap_attrs);
+
+ tevent_req_set_callback(subreq, sdap_nested_group_process_group, req);
+ return EOK;
+}
+
+static void sdap_nested_group_process_user(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+ TALLOC_CTX *tmp_ctx;
+ size_t count;
+ struct sysdb_attrs **replies;
+ int hret;
+ hash_key_t key;
+ hash_value_t value;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ ret = sdap_get_generic_recv(subreq, tmp_ctx, &count, &replies);
+ talloc_zfree(subreq);
+ if (ret != EOK && ret != ENOENT) {
+ tevent_req_error(req, ret);
+ goto done;
+ } else if (ret == ENOENT || count == 0) {
+ /* Nothing to do if the user doesn't exist */
+ goto skip;
+ }
+
+ if (count != 1) {
+ /* There should only ever be one reply for a
+ * BASE search. If otherwise, it's a serious
+ * error.
+ */
+ DEBUG(1,("Received multiple replies for a BASE search!\n"));
+ tevent_req_error(req, EIO);
+ goto done;
+ }
+
+ /* Save the user attributes to the user hash so we can store
+ * them all at once later.
+ */
+
+ key.type = HASH_KEY_STRING;
+ key.str = state->member_dn;
+
+ value.type = HASH_VALUE_PTR;
+ value.ptr = replies[0];
+
+ hret = hash_enter(state->users, &key, &value);
+ if (hret != HASH_SUCCESS) {
+ tevent_req_error(req, EIO);
+ goto done;
+ }
+ talloc_steal(state->users, replies[0]);
+
+skip:
+ state->member_index++;
+ talloc_zfree(state->member_dn);
+ ret = sdap_nested_group_process_step(req);
+ if (ret == EOK) {
+ /* EOK means it's complete */
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+
+ /* EAGAIN means that we should re-enter
+ * the mainloop
+ */
+
+done:
+ talloc_free(tmp_ctx);
+}
+
+static void sdap_group_internal_nesting_done(struct tevent_req *subreq);
+static void sdap_nested_group_process_group(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+ TALLOC_CTX *tmp_ctx;
+ size_t count;
+ struct sysdb_attrs **replies;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ ret = sdap_get_generic_recv(subreq, tmp_ctx, &count, &replies);
+ talloc_zfree(subreq);
+ if (ret != EOK && ret != ENOENT) {
+ tevent_req_error(req, ret);
+ goto done;
+ } else if (ret == ENOENT || count == 0) {
+ /* Nothing to do if the group doesn't exist */
+ goto skip;
+ }
+
+ if (count != 1) {
+ /* There should only ever be one reply for a
+ * BASE search. If otherwise, it's a serious
+ * error.
+ */
+ DEBUG(1,("Received multiple replies for a BASE search!\n"));
+ tevent_req_error(req, EIO);
+ goto done;
+ }
+
+ /* Recurse down into the member group */
+ subreq = sdap_nested_group_process_send(state, state->ev, state->domain,
+ state->sysdb, replies[0],
+ state->users, state->groups,
+ state->opts, state->sh,
+ state->nesting_level + 1);
+ if (!subreq) {
+ tevent_req_error(req, EIO);
+ goto done;
+ }
+ tevent_req_set_callback(subreq, sdap_group_internal_nesting_done, req);
+
+ talloc_free(tmp_ctx);
+ return;
+
+skip:
+ state->member_index++;
+ talloc_zfree(state->member_dn);
+ ret = sdap_nested_group_process_step(req);
+ if (ret == EOK) {
+ /* EOK means it's complete */
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+
+ /* EAGAIN means that we should re-enter
+ * the mainloop
+ */
+
+done:
+ talloc_free(tmp_ctx);
+}
+
+static void sdap_group_internal_nesting_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+
+ ret = sdap_nested_group_process_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ }
+
+ state->member_index++;
+ talloc_zfree(state->member_dn);
+ ret = sdap_nested_group_process_step(req);
+ if (ret == EOK) {
+ /* EOK means it's complete */
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+
+ /* EAGAIN means that we should re-enter
+ * the mainloop
+ */
+}
+
+static void sdap_nested_group_process_ldap_user(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+ TALLOC_CTX *tmp_ctx;
+ size_t count;
+ struct sysdb_attrs **replies;
+ int hret;
+ hash_key_t key;
+ hash_value_t value;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ ret = sdap_get_generic_recv(subreq, tmp_ctx, &count, &replies);
+ talloc_zfree(subreq);
+ if (ret != EOK && ret != ENOENT) {
+ tevent_req_error(req, ret);
+ goto done;
+ } else if (ret == ENOENT || count == 0) {
+ /* No user found. Assume it's a group */
+ ret = sdap_nested_group_lookup_group(req);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ }
+ goto done;
+ }
+
+ if (count != 1) {
+ /* There should only ever be one reply for a
+ * BASE search. If otherwise, it's a serious
+ * error.
+ */
+ DEBUG(1,("Received multiple replies for a BASE search!\n"));
+ tevent_req_error(req, EIO);
+ goto done;
+ }
+
+ /* Save the user attributes to the user hash so we can store
+ * them all at once later.
+ */
+ key.type = HASH_KEY_STRING;
+ key.str = state->member_dn;
+
+ value.type = HASH_VALUE_PTR;
+ value.ptr = replies[0];
+
+ hret = hash_enter(state->users, &key, &value);
+ if (hret != HASH_SUCCESS) {
+ tevent_req_error(req, EIO);
+ goto done;
+ }
+ talloc_steal(state->users, replies[0]);
+
+ /* Move on to the next member */
+ state->member_index++;
+ talloc_zfree(state->member_dn);
+ ret = sdap_nested_group_process_step(req);
+ if (ret == EOK) {
+ /* EOK means it's complete */
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+
+ /* EAGAIN means that we should re-enter
+ * the mainloop
+ */
+
+done:
+ talloc_free(tmp_ctx);
+}
+
+static errno_t sdap_nested_group_process_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}