summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/db/sysdb.h1
-rw-r--r--src/providers/ldap/sdap_async_initgroups.c158
-rw-r--r--src/providers/ldap/sdap_async_initgroups_ad.c407
-rw-r--r--src/providers/ldap/sdap_async_private.h10
4 files changed, 569 insertions, 7 deletions
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index f5d3ddb84..901268390 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 0f56b8740..45fc007e0 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 @@ fail:
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 ad54c1fb8..1fee4ab43 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 4af4f7144..266bc0311 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_ */