summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2011-04-29 15:59:17 +0200
committerStephen Gallagher <sgallagh@redhat.com>2011-05-20 07:21:45 -0400
commit7bdaf2a712d73763e7c3d25f6bb544b18f7028eb (patch)
treedca11da1c2c4b258d4bc33aed583d9ba48147f27
parent6b95a91c1a49c2eff480820cfd8be51d70a29ffe (diff)
downloadsssd-7bdaf2a712d73763e7c3d25f6bb544b18f7028eb.tar.gz
sssd-7bdaf2a712d73763e7c3d25f6bb544b18f7028eb.tar.bz2
sssd-7bdaf2a712d73763e7c3d25f6bb544b18f7028eb.zip
Use dereference when processing RFC2307bis nested groups
Instead of issuing N LDAP requests when processing a group with N users, utilize the dereference functionality to pull down all the members in a single LDAP request. https://fedorahosted.org/sssd/ticket/799
-rw-r--r--src/config/SSSDConfig.py1
-rw-r--r--src/config/etc/sssd.api.d/sssd-ipa.conf2
-rw-r--r--src/config/etc/sssd.api.d/sssd-ldap.conf1
-rw-r--r--src/man/sssd-ldap.5.xml23
-rw-r--r--src/providers/ipa/ipa_common.c3
-rw-r--r--src/providers/ipa/ipa_common.h2
-rw-r--r--src/providers/ldap/ldap_common.c3
-rw-r--r--src/providers/ldap/sdap.h1
-rw-r--r--src/providers/ldap/sdap_async_accounts.c468
9 files changed, 487 insertions, 17 deletions
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 5a26e31a..5114a178 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -145,6 +145,7 @@ option_strings = {
'ldap_deref' : _('How to dereference aliases'),
'ldap_dns_service_name' : _('Service name for DNS service lookups'),
'ldap_page_size' : _('The number of records to retrieve in a single LDAP query'),
+ 'ldap_deref_threshold' : _('The number of members that must be missing to trigger a full deref'),
'ldap_entry_usn' : _('entryUSN attribute'),
'ldap_rootdse_last_usn' : _('lastUSN attribute'),
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf b/src/config/etc/sssd.api.d/sssd-ipa.conf
index 7719069c..75222250 100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -35,6 +35,8 @@ ldap_referrals = bool, None, false
ldap_krb5_ticket_lifetime = int, None, false
ldap_dns_service_name = str, None, false
ldap_deref = str, None, false
+ldap_page_size = int, None, false
+ldap_deref_threshold = int, None, false
[provider/ipa/id]
ldap_search_timeout = int, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
index e568c74d..ce9ec513 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -28,6 +28,7 @@ ldap_krb5_ticket_lifetime = int, None, false
ldap_dns_service_name = str, None, false
ldap_deref = str, None, false
ldap_page_size = int, None, false
+ldap_deref_threshold = int, None, false
[provider/ldap/id]
ldap_search_timeout = int, None, false
diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index 49c9e491..42ea848a 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -869,6 +869,29 @@
</varlistentry>
<varlistentry>
+ <term>ldap_deref_threshold (integer)</term>
+ <listitem>
+ <para>
+ Specify the number of group members that must be
+ missing from the internal cache in order to trigger
+ a dereference lookup. If less members are missing,
+ they are looked up individually.
+ </para>
+ <para>
+ A dereference lookup is a means of fetching all
+ group members in a single LDAP call.
+ Different LDAP servers may implement different
+ dereference methods. The currently supported
+ servers are 389/RHDS, OpenLDAP and Active
+ Directory.
+ </para>
+ <para>
+ Default: 10
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>ldap_tls_reqcert (string)</term>
<listitem>
<para>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index b0fe523c..da3fcd6f 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -94,7 +94,8 @@ struct dp_option ipa_def_ldap_opts[] = {
* manpages or SSSDConfig API
*/
{ "ldap_auth_disable_tls_never_use_in_production", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
- { "ldap_page_size", DP_OPT_NUMBER, { .number = 1000 }, NULL_NUMBER }
+ { "ldap_page_size", DP_OPT_NUMBER, { .number = 1000 }, NULL_NUMBER },
+ { "ldap_deref_threshold", DP_OPT_NUMBER, { .number = 10 }, NULL_NUMBER }
};
struct sdap_attr_map ipa_attr_map[] = {
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index 02c4276c..8f0f35a8 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -35,7 +35,7 @@ struct ipa_service {
/* the following defines are used to keep track of the options in the ldap
* module, so that if they change and ipa is not updated correspondingly
* this will trigger a runtime abort error */
-#define IPA_OPTS_BASIC_TEST 50
+#define IPA_OPTS_BASIC_TEST 51
/* the following define is used to keep track of the options in the krb5
* module, so that if they change and ipa is not updated correspondingly
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 12028b01..1b52190f 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -87,7 +87,8 @@ struct dp_option default_basic_opts[] = {
* manpages or SSSDConfig API
*/
{ "ldap_auth_disable_tls_never_use_in_production", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
- { "ldap_page_size", DP_OPT_NUMBER, { .number = 1000 }, NULL_NUMBER }
+ { "ldap_page_size", DP_OPT_NUMBER, { .number = 1000 }, NULL_NUMBER },
+ { "ldap_deref_threshold", DP_OPT_NUMBER, { .number = 10 }, NULL_NUMBER }
};
struct sdap_attr_map generic_attr_map[] = {
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index e98b27bc..4b662c15 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -194,6 +194,7 @@ enum sdap_basic_opt {
SDAP_ENUM_SEARCH_TIMEOUT,
SDAP_DISABLE_AUTH_TLS,
SDAP_PAGE_SIZE,
+ SDAP_DEREF_THRESHOLD,
SDAP_OPTS_BASIC /* opts counter */
};
diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
index 89feff05..720ce96c 100644
--- a/src/providers/ldap/sdap_async_accounts.c
+++ b/src/providers/ldap/sdap_async_accounts.c
@@ -3278,6 +3278,28 @@ int sdap_get_initgr_recv(struct tevent_req *req)
return EOK;
}
+struct sdap_deref_ctx {
+ const char *orig_dn;
+
+ size_t expired_users_num;
+ uint32_t expired_users_index;
+ char **expired_users;
+
+ size_t expired_groups_num;
+ uint32_t expired_groups_index;
+ char **expired_groups;
+
+ size_t missing_dns_num;
+ uint32_t missing_dns_index;
+ char **missing_dns;
+
+ struct sdap_deref_attrs **deref_result;
+ size_t num_results;
+ uint32_t result_index;
+
+ int deref_threshold;
+};
+
struct sdap_nested_group_ctx {
struct tevent_context *ev;
struct sysdb_ctx *sysdb;
@@ -3294,9 +3316,13 @@ struct sdap_nested_group_ctx {
struct ldb_message_element *members;
uint32_t member_index;
char *member_dn;
+
+ struct sdap_deref_ctx *derefctx;
};
+static errno_t sdap_nested_group_process_deref_step(struct tevent_req *req);
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,
@@ -3390,8 +3416,20 @@ static struct tevent_req *sdap_nested_group_process_send(
state->member_index = 0;
- ret = sdap_nested_group_process_step(req);
- if (ret != EAGAIN) goto immediate;
+ if (sdap_has_deref_support(state->sh)) {
+ state->derefctx = talloc_zero(state, struct sdap_deref_ctx);
+ if (!state->derefctx) goto immediate;
+
+ ret = sysdb_attrs_get_string(group, SYSDB_ORIG_DN,
+ &state->derefctx->orig_dn);
+ if (ret != EOK) goto immediate;
+
+ ret = sdap_nested_group_process_deref_step(req);
+ if (ret != EAGAIN) goto immediate;
+ } else {
+ ret = sdap_nested_group_process_step(req);
+ if (ret != EAGAIN) goto immediate;
+ }
return req;
@@ -3418,6 +3456,136 @@ 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_deref_call(struct tevent_req *req);
+static errno_t sdap_nested_group_process_noderef(struct tevent_req *req);
+
+static errno_t sdap_nested_group_process_deref_step(struct tevent_req *req)
+{
+ errno_t ret;
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+ size_t missing = 0;
+ struct ldb_message **msgs = NULL;
+ enum sysdb_member_type mtype;
+ struct sdap_deref_ctx *dctx = state->derefctx;
+
+ dctx->deref_threshold = dp_opt_get_int(state->opts->basic,
+ SDAP_DEREF_THRESHOLD);
+
+ dctx->expired_users = talloc_array(dctx, char *,
+ state->members->num_values + 1);
+ dctx->expired_groups = talloc_array(dctx, char *,
+ state->members->num_values + 1);
+ dctx->missing_dns = talloc_array(dctx, char *,
+ state->members->num_values + 1);
+ if (!dctx->expired_users ||
+ !dctx->expired_groups ||
+ !dctx->missing_dns) return ENOMEM;
+
+ while (true) {
+ if (state->member_index >= state->members->num_values) {
+ /* No more entries to check. Return success */
+ talloc_zfree(state->member_dn);
+ ret = EOK;
+ break;
+ }
+
+ /* Continue to loop through until all entries have been
+ * processed.
+ */
+ ret = sdap_nested_group_check_hash(state);
+ if (ret == EOK) {
+ talloc_zfree(state->member_dn);
+ break; /* All remaining members in hash, check missing */
+ } else if (ret != ENOENT) {
+ goto done; /* Unexpected error */
+ }
+
+ ret = sdap_nested_group_check_cache(state, state->sysdb,
+ state->domain, state->opts,
+ state->member_dn,
+ &msgs, &mtype);
+ if (ret == EOK) {
+ /* The entry is cached and valid */
+ state->member_index++;
+ talloc_zfree(state->member_dn);
+ continue;
+ } else if (ret == EAGAIN) {
+ /* The entry is cached but needs refresh */
+ switch(mtype) {
+ case SYSDB_MEMBER_GROUP:
+ DEBUG(8, ("Cached LDAP group [%s] needs refresh\n",
+ state->member_dn));
+
+ missing++;
+
+ dctx->expired_groups[dctx->expired_groups_num] =
+ talloc_move(dctx, &state->member_dn);
+ dctx->expired_groups_num++;
+
+ state->member_index++;
+ continue;
+ case SYSDB_MEMBER_USER:
+ DEBUG(8, ("Cached LDAP user [%s] needs refresh\n",
+ state->member_dn));
+ missing++;
+
+ dctx->expired_users[dctx->expired_users_num] =
+ talloc_move(dctx, &state->member_dn);
+ dctx->expired_users_num++;
+
+ state->member_index++;
+ continue;
+ default:
+ DEBUG(2, ("Unknown member value\n"));
+ ret = EINVAL;
+ goto done;
+ }
+ } else if (ret == ENOENT) {
+ /* The entry is missing. It is unclear whether it
+ * is a user or a group so we'll need to try looking
+ * it up */
+ missing++;
+
+ dctx->missing_dns[dctx->missing_dns_num] =
+ talloc_move(dctx, &state->member_dn);
+ dctx->missing_dns_num++;
+
+ state->member_index++;
+ continue;
+ }
+
+ /* Unexpected error, skip this entry */
+ state->member_index++;
+ continue;
+ } /* while (true) */
+
+
+ dctx->expired_users[dctx->expired_users_num] = NULL;
+ dctx->expired_groups[dctx->expired_groups_num] = NULL;
+ dctx->missing_dns[dctx->missing_dns_num] = NULL;
+
+ if (missing == 0) {
+ ret = EOK;
+ goto done;
+ }
+
+ if (missing > dctx->deref_threshold) {
+ DEBUG(6, ("Missing data past threshold, doing a full deref\n"));
+ ret = sdap_nested_group_process_deref_call(req);
+ } else {
+ DEBUG(6, ("Falling back to individual lookups\n"));
+ ret = sdap_nested_group_process_noderef(req);
+ }
+
+ if (ret != EOK && ret != EAGAIN) goto done;
+ return EAGAIN;
+
+done:
+ talloc_zfree(state->member_dn);
+ return ret;
+}
+
static errno_t sdap_nested_group_process_step(struct tevent_req *req)
{
@@ -3658,6 +3826,110 @@ done:
return ret;
}
+static void sdap_nested_group_process_deref(struct tevent_req *subreq);
+
+static errno_t
+sdap_nested_group_process_deref_call(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct sdap_attr_map_info *maps;
+ const char **sdap_attrs;
+ int ret;
+ int timeout;
+ const int num_maps = 2;
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+
+ maps = talloc_array(state, struct sdap_attr_map_info, num_maps+1);
+ if (!maps) return ENOMEM;
+
+ maps[0].map = state->opts->user_map;
+ maps[0].num_attrs = SDAP_OPTS_USER;
+ maps[1].map = state->opts->group_map;
+ maps[1].num_attrs = SDAP_OPTS_GROUP;
+ maps[2].map = NULL;
+
+ /* Pull down the whole group map, but only pull down username
+ * and originalDN for users. */
+ ret = build_attrs_from_map(state, state->opts->group_map,
+ SDAP_OPTS_GROUP, &sdap_attrs);
+ if (ret != EOK) goto fail;
+
+ sdap_attrs = talloc_realloc(NULL, sdap_attrs, const char *,
+ SDAP_OPTS_GROUP + 2);
+ if (!sdap_attrs) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ sdap_attrs[SDAP_OPTS_GROUP] = \
+ state->opts->user_map[SDAP_AT_USER_NAME].name;
+ sdap_attrs[SDAP_OPTS_GROUP + 1] = NULL;
+
+ timeout = dp_opt_get_int(state->opts->basic, SDAP_ENTRY_CACHE_TIMEOUT);
+
+ subreq = sdap_deref_search_send(state, state->ev, state->opts,
+ state->sh, state->derefctx->orig_dn,
+ state->opts->group_map[SDAP_AT_GROUP_MEMBER].name,
+ sdap_attrs, num_maps, maps, timeout);
+ if (!subreq) {
+ ret = EIO;
+ goto fail;
+ }
+ talloc_steal(subreq, sdap_attrs);
+ talloc_steal(subreq, maps);
+
+ tevent_req_set_callback(subreq, sdap_nested_group_process_deref, req);
+ return EOK;
+
+fail:
+ talloc_free(sdap_attrs);
+ talloc_free(maps);
+ return ret;
+}
+
+static errno_t sdap_nested_group_process_noderef(struct tevent_req *req)
+{
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+ struct sdap_deref_ctx *dctx = state->derefctx;
+ errno_t ret;
+
+ if (dctx->expired_users_index < dctx->expired_users_num) {
+ state->member_dn = dctx->expired_users[dctx->expired_users_index];
+ DEBUG(8, ("Refreshing expired user [%s]\n", state->member_dn));
+
+ ret = sdap_nested_group_lookup_user(
+ req, sdap_nested_group_process_user);
+ if (ret != EOK) goto done;
+ return EAGAIN;
+ }
+
+ if (dctx->expired_groups_index < dctx->expired_groups_num) {
+ state->member_dn = dctx->expired_groups[dctx->expired_groups_index];
+ DEBUG(8, ("Refreshing expired group [%s]\n", state->member_dn));
+
+ ret = sdap_nested_group_lookup_group(req);
+ if (ret != EOK) goto done;
+ return EAGAIN;
+ }
+
+ if (dctx->missing_dns_index < dctx->missing_dns_num) {
+ state->member_dn = dctx->missing_dns[dctx->missing_dns_index];
+ DEBUG(8, ("Looking up missing DN [%s]\n", state->member_dn));
+
+ /* Try users first for generic missing DNs */
+ ret = sdap_nested_group_lookup_user(
+ req, sdap_nested_group_process_ldap_user);
+ if (ret != EOK) goto done;
+ return EAGAIN;
+ }
+
+ ret = EOK;
+done:
+ return ret;
+}
+
static errno_t sdap_nested_group_lookup_user(struct tevent_req *req,
tevent_req_fn fn)
{
@@ -3803,9 +4075,15 @@ static void sdap_nested_group_process_user(struct tevent_req *subreq)
talloc_steal(state->users, replies[0]);
skip:
- state->member_index++;
- talloc_zfree(state->member_dn);
- ret = sdap_nested_group_process_step(req);
+ if (state->derefctx) {
+ state->derefctx->expired_users_index++;
+ ret = sdap_nested_group_process_noderef(req);
+ } else {
+ 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);
@@ -3875,9 +4153,15 @@ static void sdap_nested_group_process_group(struct tevent_req *subreq)
return;
skip:
- state->member_index++;
- talloc_zfree(state->member_dn);
- ret = sdap_nested_group_process_step(req);
+ if (state->derefctx) {
+ state->derefctx->expired_groups_index++;
+ ret = sdap_nested_group_process_noderef(req);
+ } else {
+ 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);
@@ -3908,9 +4192,22 @@ static void sdap_group_internal_nesting_done(struct tevent_req *subreq)
return;
}
- state->member_index++;
- talloc_zfree(state->member_dn);
- ret = sdap_nested_group_process_step(req);
+ if (state->derefctx) {
+ if (state->derefctx->expired_groups_index <
+ state->derefctx->expired_groups_num) {
+ state->derefctx->expired_groups_index++;
+ } else {
+ state->derefctx->missing_dns_index++;
+ }
+
+ state->derefctx->expired_users_index++;
+ ret = sdap_nested_group_process_noderef(req);
+ } else {
+ 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);
@@ -3984,9 +4281,15 @@ static void sdap_nested_group_process_ldap_user(struct tevent_req *subreq)
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 (state->derefctx) {
+ state->derefctx->missing_dns_index++;
+ ret = sdap_nested_group_process_noderef(req);
+ } else {
+ 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);
@@ -4002,6 +4305,143 @@ done:
talloc_free(tmp_ctx);
}
+static errno_t
+sdap_nested_group_process_deref_result(struct tevent_req *req);
+
+static void sdap_nested_group_process_deref(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_deref_search_recv(subreq, state->derefctx,
+ &state->derefctx->num_results,
+ &state->derefctx->deref_result);
+ talloc_zfree(subreq);
+ if (ret != EOK && ret != ENOENT) {
+ tevent_req_error(req, ret);
+ return;
+ } else if (ret == ENOENT || state->derefctx->deref_result == 0) {
+ /* Nothing could be dereferenced. Done. */
+ tevent_req_done(req);
+ return;
+ }
+
+ state->derefctx->result_index = 0;
+
+ DEBUG(8, ("Received %d dereference results, about to process them\n",
+ state->derefctx->num_results));
+ ret = sdap_nested_group_process_deref_result(req);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+
+ /* EAGAIN means a recursive search is in progress */
+}
+
+static void
+sdap_nested_group_process_deref_recurse_done(struct tevent_req *subreq);
+
+static errno_t
+sdap_nested_group_process_deref_result(struct tevent_req *req)
+{
+ struct sdap_nested_group_ctx *state =
+ tevent_req_data(req, struct sdap_nested_group_ctx);
+ struct tevent_req *subreq;
+ hash_key_t key;
+ hash_value_t value;
+ int hret;
+ const char *orig_dn;
+ errno_t ret;
+ struct sdap_deref_ctx *dctx = state->derefctx;
+
+ while (dctx->result_index < dctx->num_results) {
+ if (dctx->deref_result[dctx->result_index]->map == \
+ state->opts->user_map) {
+ /* Add to appropriate hash table */
+ ret = sysdb_attrs_get_string(
+ dctx->deref_result[dctx->result_index]->attrs,
+ SYSDB_ORIG_DN, &orig_dn);
+ if (ret != EOK) {
+ DEBUG(2, ("The entry has no originalDN\n"));
+ return ret;
+ }
+
+ DEBUG(9, ("Found member user [%s]\n", orig_dn));
+
+ key.type = HASH_KEY_STRING;
+ key.str = talloc_strdup(state, orig_dn);
+
+ value.type = HASH_VALUE_PTR;
+ value.ptr = dctx->deref_result[dctx->result_index]->attrs;
+
+ hret = hash_enter(state->users, &key, &value);
+ if (hret != HASH_SUCCESS) return EIO;
+
+ talloc_steal(state->users,
+ dctx->deref_result[dctx->result_index]->attrs);
+ dctx->result_index++;
+ } else if (dctx->deref_result[dctx->result_index]->map == \
+ state->opts->group_map) {
+ DEBUG(6, ("Recursing down a nested group\n"));
+ subreq = sdap_nested_group_process_send(state, state->ev,
+ state->domain, state->sysdb,
+ dctx->deref_result[dctx->result_index]->attrs,
+ state->users, state->groups,
+ state->opts, state->sh,
+ state->nesting_level + 1);
+ if (!subreq) return EIO;
+
+ tevent_req_set_callback(subreq,
+ sdap_nested_group_process_deref_recurse_done,
+ req);
+ return EAGAIN;
+ } else {
+ /* This should never happen, but if it does,
+ * do not loop forever */
+ DEBUG(2, ("Entry does not match any known map, skipping\n"));
+ dctx->result_index++;
+ continue;
+ }
+ }
+
+ /* All deref results processed */
+ DEBUG(8, ("All dereference results processed\n"));
+ return EOK;
+}
+
+static void
+sdap_nested_group_process_deref_recurse_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);
+ return;
+ }
+
+ state->derefctx->result_index++;
+
+ ret = sdap_nested_group_process_deref_result(req);
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else if (ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ }
+
+ /* EAGAIN means a recursive search is in progress */
+}
+
static errno_t sdap_nested_group_process_recv(struct tevent_req *req)
{
TEVENT_REQ_RETURN_ON_ERROR(req);