[sssd/el5/master] - Resolves: rhbz#636055 - SSSD initgroups does not behave as expected - Resolves: rhbz#634918 - mult
Stephen Gallagher
sgallagh at fedoraproject.org
Tue Oct 19 19:43:56 UTC 2010
commit 323ec5eaeb5612ad5514875f7809921df0c7b91d
Author: Stephen Gallagher <sgallagh at redhat.com>
Date: Tue Oct 19 15:43:51 2010 -0400
- Resolves: rhbz#636055 - SSSD initgroups does not behave as expected
- Resolves: rhbz#634918 - multilib conflicts in libpath_utils-devel and
- libini_config-devel
- Resolves: rhbz#633863 - SSSD log fills up the disk
- Resolves: rhbz#636823 - the krb5 locator plugin isn't packaged for multilib
- Resolves: rhbz#638241 - sssd stops on upgrade
- Resolves: rhbz#634861 - error: %post(sssd-1.2.1-28.1.el5.s390x) scriptlet
- failed, exit status 127
0010-Disable-events-on-ldap-fd-when-offline.patch | 41 +
0011-Remove-timestamp-from-doxygen-footers.patch | 112 +
0012-Assorted-fixes-for-group-processing.patch | 4187 +++++++++++++++++++++
sssd.spec | 60 +-
4 files changed, 4369 insertions(+), 31 deletions(-)
---
diff --git a/0010-Disable-events-on-ldap-fd-when-offline.patch b/0010-Disable-events-on-ldap-fd-when-offline.patch
new file mode 100644
index 0000000..71dead1
--- /dev/null
+++ b/0010-Disable-events-on-ldap-fd-when-offline.patch
@@ -0,0 +1,41 @@
+From 63a22d99ff3b77f559dc616a742d487b52087f01 Mon Sep 17 00:00:00 2001
+From: Jan Zeleny <jzeleny at redhat.com>
+Date: Mon, 4 Oct 2010 16:35:50 +0200
+Subject: [PATCH] Disable events on ldap fd when offline.
+
+Erase events on LDAP socket when backend is offline and an event appears on the socket.
+Normally this would lead to infinite loop, because event is present on the fd,
+but instead of being processed, an error log is written and the program continues to wait
+for the event.
+
+Ticket: #599
+---
+ src/providers/ldap/sdap_fd_events.c | 6 ++++--
+ 1 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/ldap/sdap_fd_events.c b/src/providers/ldap/sdap_fd_events.c
+index 347cf8b..c3f9e11 100644
+--- a/src/providers/ldap/sdap_fd_events.c
++++ b/src/providers/ldap/sdap_fd_events.c
+@@ -49,14 +49,16 @@ int get_fd_from_ldap(LDAP *ldap, int *fd)
+
+ int remove_ldap_connection_callbacks(struct sdap_handle *sh)
+ {
+-#ifdef HAVE_LDAP_CONNCB
+ /* sdap_fd_events might be NULL here if sdap_mark_offline()
+ * was called before a connection was established.
+ */
+ if (sh->sdap_fd_events) {
++#ifdef HAVE_LDAP_CONNCB
+ talloc_zfree(sh->sdap_fd_events->conncb);
+- }
++#else
++ talloc_zfree(sh->sdap_fd_events->fde);
+ #endif
++ }
+ return EOK;
+ }
+
+--
+1.7.2.3
+
diff --git a/0011-Remove-timestamp-from-doxygen-footers.patch b/0011-Remove-timestamp-from-doxygen-footers.patch
new file mode 100644
index 0000000..2e32439
--- /dev/null
+++ b/0011-Remove-timestamp-from-doxygen-footers.patch
@@ -0,0 +1,112 @@
+From 03e654641b70d0d37887c2d4f516f13417d71d90 Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh at redhat.com>
+Date: Fri, 8 Oct 2010 13:41:57 -0400
+Subject: [PATCH] Remove timestamp from doxygen footers
+
+---
+ common/collection/collection.cfg.doxy.in | 2 +-
+ common/collection/no_date_footer.html | 7 +++++++
+ common/ini/ini_config.cfg.doxy.in | 2 +-
+ common/ini/no_date_footer.html | 1 +
+ common/path_utils/no_date_footer.html | 1 +
+ common/path_utils/path_utils.cfg.doxy.in | 2 +-
+ common/refarray/no_date_footer.html | 1 +
+ common/refarray/ref_array.cfg.doxy.in | 2 +-
+ 8 files changed, 14 insertions(+), 4 deletions(-)
+ create mode 100644 common/collection/no_date_footer.html
+ create mode 120000 common/ini/no_date_footer.html
+ create mode 120000 common/path_utils/no_date_footer.html
+ create mode 120000 common/refarray/no_date_footer.html
+
+diff --git a/common/collection/collection.cfg.doxy.in b/common/collection/collection.cfg.doxy.in
+index 7f5d94e1b148f37319c7b439f72c9e68b58032fc..38d62a679dcae816232310e397de9750a6a1e89c 100644
+--- a/common/collection/collection.cfg.doxy.in
++++ b/common/collection/collection.cfg.doxy.in
+@@ -806,7 +806,7 @@ HTML_HEADER =
+ # each generated HTML page. If it is left blank doxygen will generate a
+ # standard footer.
+
+-HTML_FOOTER =
++HTML_FOOTER = no_date_footer.html
+
+ # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+ # style sheet that is used by each HTML page. It can be used to
+diff --git a/common/collection/no_date_footer.html b/common/collection/no_date_footer.html
+new file mode 100644
+index 0000000000000000000000000000000000000000..3d46190b169f6911aa57b845cbd90fa013701d40
+--- /dev/null
++++ b/common/collection/no_date_footer.html
+@@ -0,0 +1,7 @@
++<hr size="1"><address style="text-align: right;"><small>
++Generated for $projectname by <a href="http://www.doxygen.org/
++index.html"><img src="doxygen.png" alt="doxygen" align="middle" border="0"></a>
++$doxygenversion</small></address>
++</body>
++</html>
++
+diff --git a/common/ini/ini_config.cfg.doxy.in b/common/ini/ini_config.cfg.doxy.in
+index f7763f4f377b6c9575076fa7261a513fafa2ddb6..9b73e8ea5195e363ca6678d3d12dc53aa21657b6 100644
+--- a/common/ini/ini_config.cfg.doxy.in
++++ b/common/ini/ini_config.cfg.doxy.in
+@@ -806,7 +806,7 @@ HTML_HEADER =
+ # each generated HTML page. If it is left blank doxygen will generate a
+ # standard footer.
+
+-HTML_FOOTER =
++HTML_FOOTER = no_date_footer.html
+
+ # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+ # style sheet that is used by each HTML page. It can be used to
+diff --git a/common/ini/no_date_footer.html b/common/ini/no_date_footer.html
+new file mode 120000
+index 0000000000000000000000000000000000000000..86a54c4c463f7ed57f50a47ab7fe6365a0926fc2
+--- /dev/null
++++ b/common/ini/no_date_footer.html
+@@ -0,0 +1 @@
++../collection/no_date_footer.html
+\ No newline at end of file
+diff --git a/common/path_utils/no_date_footer.html b/common/path_utils/no_date_footer.html
+new file mode 120000
+index 0000000000000000000000000000000000000000..86a54c4c463f7ed57f50a47ab7fe6365a0926fc2
+--- /dev/null
++++ b/common/path_utils/no_date_footer.html
+@@ -0,0 +1 @@
++../collection/no_date_footer.html
+\ No newline at end of file
+diff --git a/common/path_utils/path_utils.cfg.doxy.in b/common/path_utils/path_utils.cfg.doxy.in
+index 90a7dd1bf8830ac75faa959270e673e787365ef4..b632bf3e757f97be7f20b4fc0a660c5c1cccff94 100644
+--- a/common/path_utils/path_utils.cfg.doxy.in
++++ b/common/path_utils/path_utils.cfg.doxy.in
+@@ -806,7 +806,7 @@ HTML_HEADER =
+ # each generated HTML page. If it is left blank doxygen will generate a
+ # standard footer.
+
+-HTML_FOOTER =
++HTML_FOOTER = no_date_footer.html
+
+ # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+ # style sheet that is used by each HTML page. It can be used to
+diff --git a/common/refarray/no_date_footer.html b/common/refarray/no_date_footer.html
+new file mode 120000
+index 0000000000000000000000000000000000000000..86a54c4c463f7ed57f50a47ab7fe6365a0926fc2
+--- /dev/null
++++ b/common/refarray/no_date_footer.html
+@@ -0,0 +1 @@
++../collection/no_date_footer.html
+\ No newline at end of file
+diff --git a/common/refarray/ref_array.cfg.doxy.in b/common/refarray/ref_array.cfg.doxy.in
+index 455db0a94e69c63d4b957459f9ccbdd6557d2ef4..ce57424e26e01e6a17c6f96ac8120d10d63bc464 100644
+--- a/common/refarray/ref_array.cfg.doxy.in
++++ b/common/refarray/ref_array.cfg.doxy.in
+@@ -806,7 +806,7 @@ HTML_HEADER =
+ # each generated HTML page. If it is left blank doxygen will generate a
+ # standard footer.
+
+-HTML_FOOTER =
++HTML_FOOTER = no_date_footer.html
+
+ # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+ # style sheet that is used by each HTML page. It can be used to
+--
+1.7.2.3
+
diff --git a/0012-Assorted-fixes-for-group-processing.patch b/0012-Assorted-fixes-for-group-processing.patch
new file mode 100644
index 0000000..0337506
--- /dev/null
+++ b/0012-Assorted-fixes-for-group-processing.patch
@@ -0,0 +1,4187 @@
+From 749a0a3635ce8c387bd671275c613632d7184155 Mon Sep 17 00:00:00 2001
+From: Stephen Gallagher <sgallagh at redhat.com>
+Date: Mon, 13 Sep 2010 11:45:42 -0400
+Subject: [PATCH 12/12] Assorted fixes for group processing
+
+Request all group attributes during initgroups processing
+
+We tried to be too clever and only requested the name of the group,
+but we require the objectClass to validate the results.
+
+https://fedorahosted.org/sssd/ticket/622
+
+Fix sysdb_group_dn_name
+
+Fix sysdb_attrs_to_list
+
+Request the correct attribute name
+
+Add common hash table setup
+
+sss_hash_create() produces a dhash table living in the talloc
+hierarchy.
+
+End update_members request if there's nothing to do
+
+sysdb interface for adding incomplete group entries
+
+Useful for optimizing the initgroups operation.
+
+Add fake groups during initgroups
+
+If during initgroups operation we find out that any of the groups
+the user is a member of is not cached yet we add a incomplete,
+expired group entry. That way, we save ourselves from looking up and
+saving all the potential user entries the group may also consist of.
+Because the group is expired, it will be refreshed during the next
+getgrgid/getgrnam call and correct member list will be returned.
+
+Add option to limit nested groups
+
+Make sdap_save_users_send handle zero users gracefully
+
+If we send a zero num_users value, we should just immediately
+return success, rather than starting a useless transaction
+
+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
+
+Make user argument of sysdb_update_members_send a const
+
+Modify sysdb_add_group_member_send to accept users and groups
+
+Previously, it assumed that all members were users. This changes
+the interface so that either a user or a group can be specified.
+
+Add proper nested initgroup support for RFC2307bis servers
+
+sysdb interface for adding expired user entries
+
+Shortcut for save_group() to accept sysdb DNs as member attributes
+
+This patch is a backport of original written by Ralf Haferkamp.
+
+Addtional parameter "populate_members" for save_group() and save_groups()
+to indicate that the "member" attribute of the groups is populated
+with sysdb DNs of the members (instead of LDAP DNs).
+
+Add fake users during saving of RFC2307 group
+---
+ src/Makefile.am | 3 +-
+ src/db/sysdb.c | 16 +-
+ src/db/sysdb.h | 42 +-
+ src/db/sysdb_ops.c | 424 +++++-
+ src/providers/ipa/ipa_common.c | 3 +-
+ src/providers/ipa/ipa_common.h | 2 +-
+ src/providers/ldap/ldap_common.c | 5 +-
+ src/providers/ldap/ldap_id_cleanup.c | 7 +-
+ src/providers/ldap/sdap.h | 1 +
+ src/providers/ldap/sdap_async_accounts.c | 2843 +++++++++++++++++++++++++++++-
+ src/responder/nss/nsssrv_cmd.c | 2 +-
+ src/tests/sysdb-tests.c | 81 +-
+ src/util/util.c | 53 +
+ src/util/util.h | 4 +
+ 14 files changed, 3401 insertions(+), 85 deletions(-)
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 847e97ad374970e689073ad51c5d94d811e1f4ef..1554ebb37fcab5ab74714a90abffda6f556f972e 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -125,7 +125,8 @@ endif
+ libsss_crypt_la_SOURCES = \
+ $(SSS_CRYPT_SOURCES)
+ libsss_crypt_la_CPPFLAGS = \
+- $(SSS_CRYPT_CFLAGS)
++ $(SSS_CRYPT_CFLAGS) \
++ $(DHASH_CFLAGS)
+ libsss_crypt_la_LIBADD = \
+ $(SSS_CRYPT_LIBS)
+
+diff --git a/src/db/sysdb.c b/src/db/sysdb.c
+index f6996c4310857a0bb4b1eaa78874293e728d9490..0091023b6e055b56f7f2a799f3ab31c234128dcf 100644
+--- a/src/db/sysdb.c
++++ b/src/db/sysdb.c
+@@ -56,6 +56,7 @@ errno_t sysdb_group_dn_name(struct sysdb_ctx *ctx, void *memctx,
+ const char *_dn, char **_name)
+ {
+ struct ldb_dn *dn;
++ const struct ldb_val *val;
+ *_name = NULL;
+
+ dn = ldb_dn_new_fmt(memctx, ctx->ldb, "%s", _dn);
+@@ -63,8 +64,15 @@ errno_t sysdb_group_dn_name(struct sysdb_ctx *ctx, void *memctx,
+ return ENOMEM;
+ }
+
+- *_name = talloc_strdup(memctx, ldb_dn_get_rdn_name(dn));
+- if (!_name) {
++ val = ldb_dn_get_rdn_val(dn);
++ if (val == NULL) {
++ talloc_zfree(dn);
++ return EINVAL;
++ }
++
++ *_name = talloc_strndup(memctx, (char *) val->data, val->length);
++
++ if (!*_name) {
+ talloc_zfree(dn);
+ return ENOMEM;
+ }
+@@ -1983,13 +1991,13 @@ errno_t sysdb_attrs_to_list(TALLOC_CTX *memctx,
+ for (attr_idx = 0; attr_idx < attr_count; attr_idx++) {
+ /* Examine each attribute within the entry */
+ for (i = 0; i < attrs[attr_idx]->num; i++) {
+- if (strcasecmp(attrs[attr_idx]->a->name, attr_name) == 0) {
++ if (strcasecmp(attrs[attr_idx]->a[i].name, attr_name) == 0) {
+ /* Attribute name matches the requested name
+ * Copy it to the output list
+ */
+ list[list_idx] = talloc_strdup(
+ list,
+- (const char *)attrs[attr_idx]->a->values[0].data);
++ (const char *)attrs[attr_idx]->a[i].values[0].data);
+ if (!list[list_idx]) {
+ talloc_free(list);
+ return ENOMEM;
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 04ce49e66c3d056b8b4a81375674816d9a91cc3e..e3e69d3b0247ff5577c20e2795b98aa480534b2e 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -451,6 +451,14 @@ struct tevent_req *sysdb_add_basic_user_send(TALLOC_CTX *mem_ctx,
+ const char *shell);
+ int sysdb_add_basic_user_recv(struct tevent_req *req);
+
++/* Add fake (expired) user */
++struct tevent_req *sysdb_add_fake_user_send(TALLOC_CTX *mem_ctx,
++ struct tevent_context *ev,
++ struct sysdb_handle *handle,
++ struct sss_domain_info *domain,
++ const char *name);
++int sysdb_add_fake_user_recv(struct tevent_req *req);
++
+ /* Add user (all checks) */
+ struct tevent_req *sysdb_add_user_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+@@ -483,6 +491,14 @@ struct tevent_req *sysdb_add_group_send(TALLOC_CTX *mem_ctx,
+ int cache_timeout);
+ int sysdb_add_group_recv(struct tevent_req *req);
+
++/* Add a incomplete, expired group */
++struct tevent_req *sysdb_add_incomplete_group_send(TALLOC_CTX *mem_ctx,
++ struct tevent_context *ev,
++ struct sysdb_handle *handle,
++ struct sss_domain_info *domain,
++ const char *name, gid_t gid);
++int sysdb_add_incomplete_group_recv(struct tevent_req *req);
++
+ /* mod_op must be either LDB_FLAG_MOD_ADD or LDB_FLAG_MOD_DELETE */
+ struct tevent_req *sysdb_mod_group_member_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+@@ -521,12 +537,18 @@ struct tevent_req *sysdb_store_group_send(TALLOC_CTX *mem_ctx,
+ uint64_t cache_timeout);
+ int sysdb_store_group_recv(struct tevent_req *req);
+
++enum sysdb_member_type {
++ SYSDB_MEMBER_USER,
++ SYSDB_MEMBER_GROUP
++};
++
+ struct tevent_req *sysdb_add_group_member_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct sss_domain_info *domain,
+ const char *group,
+- const char *member);
++ const char *member,
++ enum sysdb_member_type type);
+ int sysdb_add_group_member_recv(struct tevent_req *req);
+
+ struct tevent_req *sysdb_remove_group_member_send(TALLOC_CTX *mem_ctx,
+@@ -534,17 +556,19 @@ struct tevent_req *sysdb_remove_group_member_send(TALLOC_CTX *mem_ctx,
+ struct sysdb_handle *handle,
+ struct sss_domain_info *domain,
+ const char *group,
+- const char *member);
++ const char *member,
++ enum sysdb_member_type type);
+ int sysdb_remove_group_member_recv(struct tevent_req *req);
+
+
+-struct tevent_req * sysdb_update_members_send(TALLOC_CTX *mem_ctx,
+- struct tevent_context *ev,
+- struct sysdb_handle *handle,
+- struct sss_domain_info *domain,
+- char *user,
+- char **add_groups,
+- char **del_groups);
++struct tevent_req *sysdb_update_members_send(TALLOC_CTX *mem_ctx,
++ struct tevent_context *ev,
++ struct sysdb_handle *handle,
++ struct sss_domain_info *domain,
++ const char *member,
++ enum sysdb_member_type type,
++ char **add_groups,
++ char **del_groups);
+ errno_t sysdb_update_members_recv(struct tevent_req *req);
+
+ /* Password caching function.
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 52102706c494e29c6cdda6fb9677d45226cfec04..1c88e7d039e2e7f500ab720b6a5c1aa4261a90e3 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -2061,6 +2061,191 @@ int sysdb_add_user_recv(struct tevent_req *req)
+ return sysdb_op_default_recv(req);
+ }
+
++/* =Add-A-Fake-User====================================================== */
++
++struct sysdb_add_fake_user_state {
++ struct tevent_context *ev;
++ struct sysdb_handle *handle;
++ struct sss_domain_info *domain;
++
++ const char *name;
++};
++
++static void sysdb_add_fake_user_group_check(struct tevent_req *subreq);
++static int sysdb_add_fake_user_op(struct tevent_req *req);
++static void sysdb_add_fake_user_op_done(struct tevent_req *subreq);
++
++struct tevent_req *sysdb_add_fake_user_send(TALLOC_CTX *mem_ctx,
++ struct tevent_context *ev,
++ struct sysdb_handle *handle,
++ struct sss_domain_info *domain,
++ const char *name)
++{
++ struct tevent_req *req, *subreq;
++ struct sysdb_add_fake_user_state *state;
++ int ret;
++
++ req = tevent_req_create(mem_ctx, &state,
++ struct sysdb_add_fake_user_state);
++ if (!req) return NULL;
++
++ state->ev = ev;
++ state->handle = handle;
++ state->domain = domain;
++ state->name = name;
++
++ if (handle->ctx->mpg) {
++ /* In MPG domains you can't have groups with the same name as users,
++ * search if a user with the same name exists.
++ * Don't worry about users, if we try to add a user with the same
++ * name the operation will fail */
++ subreq = sysdb_search_group_by_name_send(state, ev, NULL, handle,
++ domain, name, NULL);
++ if (!subreq) {
++ ERROR_OUT(ret, ENOMEM, fail);
++ }
++ tevent_req_set_callback(subreq, sysdb_add_fake_user_group_check, req);
++ return req;
++ }
++
++ /* try to add the user */
++ ret = sysdb_add_fake_user_op(req);
++ if (ret != EOK) goto fail;
++
++ return req;
++
++fail:
++ DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
++ tevent_req_error(req, ret);
++ tevent_req_post(req, ev);
++ return req;
++}
++
++static void sysdb_add_fake_user_group_check(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sysdb_add_fake_user_state *state = tevent_req_data(req,
++ struct sysdb_add_fake_user_state);
++ struct ldb_message *msg;
++ int ret;
++
++ /* We can succeed only if we get an ENOENT error, which means no users
++ * with the same name exist.
++ * If any other error is returned fail as well. */
++ ret = sysdb_search_user_recv(subreq, state, &msg);
++ talloc_zfree(subreq);
++ if (ret != ENOENT) {
++ if (ret == EOK) ret = EEXIST;
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /* try to add the user */
++ ret = sysdb_add_fake_user_op(req);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ }
++}
++
++static int sysdb_add_fake_user_op(struct tevent_req *req)
++{
++ struct sysdb_add_fake_user_state *state = tevent_req_data(req,
++ struct sysdb_add_fake_user_state);
++ struct ldb_message *msg;
++ struct tevent_req *subreq;
++ struct ldb_request *ldbreq;
++ int ret;
++ time_t now;
++
++ msg = ldb_msg_new(state);
++ if (!msg) {
++ return ENOMEM;
++ }
++
++ /* user dn */
++ msg->dn = sysdb_user_dn(state->handle->ctx, msg,
++ state->domain->name, state->name);
++ if (!msg->dn) {
++ return ENOMEM;
++ }
++
++ now = time(NULL);
++
++ ret = add_string(msg, LDB_FLAG_MOD_ADD, "objectClass", SYSDB_USER_CLASS);
++ if (ret) return ret;
++
++ ret = add_string(msg, LDB_FLAG_MOD_ADD, SYSDB_NAME, state->name);
++ if (ret) return ret;
++
++ ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME,
++ (unsigned long) now);
++ if (ret) return ret;
++
++ ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_LAST_UPDATE,
++ (unsigned long) now);
++ if (ret) return ret;
++
++ /* set last login so that the fake entry does not get cleaned up
++ * immediately */
++ ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_LAST_LOGIN,
++ (unsigned long) now);
++ if (ret) return ret;
++
++ ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CACHE_EXPIRE,
++ (unsigned long) now-1);
++ if (ret) return ret;
++
++ ret = ldb_build_add_req(&ldbreq, state->handle->ctx->ldb, state, msg,
++ NULL, NULL, NULL, NULL);
++ if (ret != LDB_SUCCESS) {
++ DEBUG(1, ("Failed to build modify request: %s(%d)[%s]\n",
++ ldb_strerror(ret), ret, ldb_errstring(state->handle->ctx->ldb)));
++ return sysdb_error_to_errno(ret);
++ }
++
++ subreq = sldb_request_send(state, state->ev,
++ state->handle->ctx->ldb, ldbreq);
++ if (!subreq) {
++ return ENOMEM;
++ }
++ tevent_req_set_callback(subreq, sysdb_add_fake_user_op_done, req);
++
++ return EOK;
++}
++
++static void sysdb_add_fake_user_op_done(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sysdb_add_fake_user_state *state = tevent_req_data(req,
++ struct sysdb_add_fake_user_state);
++ struct ldb_reply *ldbreply;
++ int ret;
++
++ ret = sldb_request_recv(subreq, state, &ldbreply);
++ talloc_zfree(subreq);
++ if (ret) {
++ DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ if (ldbreply->type != LDB_REPLY_DONE) {
++ DEBUG(6, ("Error: %d (%s)\n", EIO, strerror(EIO)));
++ tevent_req_error(req, EIO);
++ return;
++ }
++
++ tevent_req_done(req);
++}
++
++int sysdb_add_fake_user_recv(struct tevent_req *req)
++{
++ TEVENT_REQ_RETURN_ON_ERROR(req);
++
++ return EOK;
++}
+
+ /* =Add-Basic-Group-NO-CHECKS============================================= */
+
+@@ -2448,6 +2633,181 @@ int sysdb_add_group_recv(struct tevent_req *req)
+ }
+
+
++/* =Add-A-Incomplete-Group====================================================== */
++
++struct sysdb_add_incomplete_group_state {
++ struct tevent_context *ev;
++ struct sysdb_handle *handle;
++ struct sss_domain_info *domain;
++
++ const char *name;
++ gid_t gid;
++};
++
++static void sysdb_add_incomplete_group_user_check(struct tevent_req *subreq);
++static void sysdb_add_incomplete_group_basic_done(struct tevent_req *subreq);
++static void sysdb_add_incomplete_group_set_attrs_done(struct tevent_req *subreq);
++
++struct tevent_req *sysdb_add_incomplete_group_send(TALLOC_CTX *mem_ctx,
++ struct tevent_context *ev,
++ struct sysdb_handle *handle,
++ struct sss_domain_info *domain,
++ const char *name, gid_t gid)
++{
++ struct tevent_req *req, *subreq;
++ struct sysdb_add_incomplete_group_state *state;
++ int ret;
++
++ req = tevent_req_create(mem_ctx, &state,
++ struct sysdb_add_incomplete_group_state);
++ if (!req) return NULL;
++
++ state->ev = ev;
++ state->handle = handle;
++ state->domain = domain;
++ state->name = name;
++ state->gid = gid;
++
++ if (handle->ctx->mpg) {
++ /* In MPG domains you can't have groups with the same name as users,
++ * search if a user with the same name exists.
++ * Don't worry about groups, if we try to add a group with the same
++ * name the operation will fail */
++
++ subreq = sysdb_search_user_by_name_send(state, ev, NULL, handle,
++ domain, name, NULL);
++ if (!subreq) {
++ ERROR_OUT(ret, ENOMEM, fail);
++ }
++ tevent_req_set_callback(subreq, sysdb_add_incomplete_group_user_check, req);
++ return req;
++ }
++
++ /* try to add the group */
++ subreq = sysdb_add_basic_group_send(state, ev, handle,
++ domain, name, gid);
++ if (!subreq) {
++ ERROR_OUT(ret, ENOMEM, fail);
++ }
++ tevent_req_set_callback(subreq, sysdb_add_incomplete_group_basic_done, req);
++ return req;
++
++fail:
++ DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
++ tevent_req_error(req, ret);
++ tevent_req_post(req, ev);
++ return req;
++}
++
++static void sysdb_add_incomplete_group_user_check(struct tevent_req *subreq)
++{
++
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sysdb_add_incomplete_group_state *state = tevent_req_data(req,
++ struct sysdb_add_incomplete_group_state);
++ struct ldb_message *msg;
++ int ret;
++
++ /* We can succeed only if we get an ENOENT error, which means no users
++ * with the same name exist.
++ * If any other error is returned fail as well. */
++ ret = sysdb_search_user_recv(subreq, state, &msg);
++ talloc_zfree(subreq);
++ if (ret != ENOENT) {
++ if (ret == EOK) ret = EEXIST;
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /* try to add the group */
++ subreq = sysdb_add_basic_group_send(state, state->ev,
++ state->handle, state->domain,
++ state->name, state->gid);
++ if (!subreq) {
++ DEBUG(6, ("Error: Out of memory\n"));
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++ tevent_req_set_callback(subreq, sysdb_add_incomplete_group_basic_done, req);
++}
++
++static void sysdb_add_incomplete_group_basic_done(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sysdb_add_incomplete_group_state *state = tevent_req_data(req,
++ struct sysdb_add_incomplete_group_state);
++ int ret;
++ struct sysdb_attrs *attrs;
++ time_t now;
++
++ ret = sysdb_add_basic_group_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret) {
++ DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ attrs = sysdb_new_attrs(state);
++ if (!attrs) {
++ DEBUG(6, ("Error: Out of memory\n"));
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++
++ now = time(NULL);
++
++ ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now);
++ if (ret) {
++ DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE,
++ now-1);
++ if (ret) {
++ DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ subreq = sysdb_set_group_attr_send(state, state->ev,
++ state->handle, state->domain,
++ state->name, attrs,
++ SYSDB_MOD_REP);
++ if (!subreq) {
++ DEBUG(6, ("Error: Out of memory\n"));
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++ tevent_req_set_callback(subreq, sysdb_add_incomplete_group_set_attrs_done, req);
++}
++
++static void sysdb_add_incomplete_group_set_attrs_done(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ int ret;
++
++ ret = sysdb_set_group_attr_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret) {
++ DEBUG(6, ("Error: %d (%s)\n", ret, strerror(ret)));
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ tevent_req_done(req);
++}
++
++int sysdb_add_incomplete_group_recv(struct tevent_req *req)
++{
++ return sysdb_op_default_recv(req);
++}
++
+ /* =Add-Or-Remove-Group-Memeber=========================================== */
+
+ /* mod_op must be either SYSDB_MOD_ADD or SYSDB_MOD_DEL */
+@@ -2967,11 +3327,12 @@ struct tevent_req *sysdb_add_group_member_send(TALLOC_CTX *mem_ctx,
+ struct sysdb_handle *handle,
+ struct sss_domain_info *domain,
+ const char *group,
+- const char *user)
++ const char *member,
++ enum sysdb_member_type type)
+ {
+ struct tevent_req *req, *subreq;
+ struct sysdb_op_state *state;
+- struct ldb_dn *group_dn, *user_dn;
++ struct ldb_dn *group_dn, *member_dn;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state);
+@@ -2987,13 +3348,24 @@ struct tevent_req *sysdb_add_group_member_send(TALLOC_CTX *mem_ctx,
+ ERROR_OUT(ret, ENOMEM, fail);
+ }
+
+- user_dn = sysdb_user_dn(handle->ctx, state, domain->name, user);
+- if (!user_dn) {
++ if (type == SYSDB_MEMBER_USER) {
++ member_dn = sysdb_user_dn(handle->ctx, state,
++ domain->name,
++ member);
++ } else if (type == SYSDB_MEMBER_GROUP) {
++ member_dn = sysdb_group_dn(handle->ctx, state,
++ domain->name,
++ member);
++ } else {
++ ERROR_OUT(ret, EINVAL, fail);
++ }
++
++ if (!member_dn) {
+ ERROR_OUT(ret, ENOMEM, fail);
+ }
+
+ subreq = sysdb_mod_group_member_send(state, ev, handle,
+- user_dn, group_dn,
++ member_dn, group_dn,
+ SYSDB_MOD_ADD);
+ if (!subreq) {
+ ERROR_OUT(ret, ENOMEM, fail);
+@@ -3041,11 +3413,12 @@ struct tevent_req *sysdb_remove_group_member_send(TALLOC_CTX *mem_ctx,
+ struct sysdb_handle *handle,
+ struct sss_domain_info *domain,
+ const char *group,
+- const char *user)
++ const char *member,
++ enum sysdb_member_type type)
+ {
+ struct tevent_req *req, *subreq;
+ struct sysdb_op_state *state;
+- struct ldb_dn *group_dn, *user_dn;
++ struct ldb_dn *group_dn, *member_dn;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct sysdb_op_state);
+@@ -3061,13 +3434,20 @@ struct tevent_req *sysdb_remove_group_member_send(TALLOC_CTX *mem_ctx,
+ ERROR_OUT(ret, ENOMEM, fail);
+ }
+
+- user_dn = sysdb_user_dn(handle->ctx, state, domain->name, user);
+- if (!user_dn) {
++ if (type == SYSDB_MEMBER_USER) {
++ member_dn = sysdb_user_dn(handle->ctx, state, domain->name, member);
++ } else if (type == SYSDB_MEMBER_GROUP) {
++ member_dn = sysdb_group_dn(handle->ctx, state, domain->name, member);
++ } else {
++ ERROR_OUT(ret, EINVAL, fail);
++ }
++
++ if (!member_dn) {
+ ERROR_OUT(ret, ENOMEM, fail);
+ }
+
+ subreq = sysdb_mod_group_member_send(state, ev, handle,
+- user_dn, group_dn,
++ member_dn, group_dn,
+ SYSDB_MOD_DEL);
+ if (!subreq) {
+ ERROR_OUT(ret, ENOMEM, fail);
+@@ -5067,11 +5447,13 @@ int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date,
+ }
+
+ struct sysdb_update_members_ctx {
+- char *user;
++ char *member;
+ struct sss_domain_info *domain;
+ struct tevent_context *ev;
+ struct sysdb_handle *handle;
+
++ enum sysdb_member_type membertype;
++
+ char **add_groups;
+ int add_group_iter;
+
+@@ -5099,7 +5481,8 @@ struct tevent_req *sysdb_update_members_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sysdb_handle *handle,
+ struct sss_domain_info *domain,
+- char *user,
++ const char *member,
++ enum sysdb_member_type type,
+ char **add_groups,
+ char **del_groups)
+ {
+@@ -5112,14 +5495,15 @@ struct tevent_req *sysdb_update_members_send(TALLOC_CTX *mem_ctx,
+ return NULL;
+ }
+
+- state->user = talloc_strdup(state, user);
+- if (!state->user) {
++ state->member = talloc_strdup(state, member);
++ if (!state->member) {
+ goto error;
+ }
+
+ state->domain = domain;
+ state->ev = ev;
+ state->handle = handle;
++ state->membertype = type;
+
+ if (add_groups) {
+ state->add_groups = dup_string_list(state, (const char**)add_groups);
+@@ -5143,6 +5527,14 @@ struct tevent_req *sysdb_update_members_send(TALLOC_CTX *mem_ctx,
+ }
+ state->del_group_iter = 0;
+
++ if (state->add_groups[state->add_group_iter] == NULL &&
++ state->del_groups[state->del_group_iter] == NULL) {
++ /* Nothing to do */
++ tevent_req_done(req);
++ tevent_req_post(req, state->ev);
++ return req;
++ }
++
+ ret = sysdb_update_members_step(req);
+ if (ret != EOK) {
+ /* Nothing to do. Finish up */
+@@ -5175,7 +5567,7 @@ sysdb_update_members_step(struct tevent_req *req)
+ state, state->ev, state->handle,
+ state->domain,
+ state->add_groups[state->add_group_iter],
+- state->user);
++ state->member, state->membertype);
+ if (!subreq) {
+ return EIO;
+ }
+@@ -5189,7 +5581,7 @@ sysdb_update_members_step(struct tevent_req *req)
+ state, state->ev,
+ state->handle, state->domain,
+ state->del_groups[state->del_group_iter],
+- state->user);
++ state->member, state->membertype);
+ if (!subreq) {
+ return EIO;
+ }
+diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
+index b6f4d65f45f176199f04e9695af752e84ecd389c..a95047d0fe0559dd693c15fe02d39f5c0118592c 100644
+--- a/src/providers/ipa/ipa_common.c
++++ b/src/providers/ipa/ipa_common.c
+@@ -71,7 +71,8 @@ struct dp_option ipa_def_ldap_opts[] = {
+ { "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER },
+ { "ldap_dns_service_name", DP_OPT_STRING, { SSS_LDAP_SRV_NAME }, NULL_STRING },
+ { "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+- { "ldap_krb5_ticket_lifetime", DP_OPT_NUMBER, { .number = (24 * 60 * 60) }, NULL_NUMBER }
++ { "ldap_krb5_ticket_lifetime", DP_OPT_NUMBER, { .number = (24 * 60 * 60) }, NULL_NUMBER },
++ { "ldap_group_nesting_level", DP_OPT_NUMBER, { .number = 2 }, 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 9678e0cea60642ca9b73592f87efdf218bc1e379..fe9ca5418dafe70ee09d8e4f60c1749f66eb7694 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 34
++#define IPA_OPTS_BASIC_TEST 35
+
+ /* 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 78bcac2dbbd5b5ab469dc7cea49e005045f06ce1..a867995cb5093a7e8cda494780ff6e7313da69be 100644
+--- a/src/providers/ldap/ldap_common.c
++++ b/src/providers/ldap/ldap_common.c
+@@ -61,12 +61,13 @@ struct dp_option default_basic_opts[] = {
+ { "ldap_krb5_init_creds", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+ /* use the same parm name as the krb5 module so we set it only once */
+ { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+- { "ldap_pwd_policy", DP_OPT_STRING, { "none" } , NULL_STRING },
++ { "ldap_pwd_policy", DP_OPT_STRING, { "none" }, NULL_STRING },
+ { "ldap_referrals", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+ { "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER },
+ { "ldap_dns_service_name", DP_OPT_STRING, { SSS_LDAP_SRV_NAME }, NULL_STRING },
+ { "ldap_access_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+- { "ldap_krb5_ticket_lifetime", DP_OPT_NUMBER, { .number = (24 * 60 * 60) }, NULL_NUMBER }
++ { "ldap_krb5_ticket_lifetime", DP_OPT_NUMBER, { .number = (24 * 60 * 60) }, NULL_NUMBER },
++ { "ldap_group_nesting_level", DP_OPT_NUMBER, { .number = 2 }, NULL_NUMBER }
+ };
+
+ struct sdap_attr_map generic_attr_map[] = {
+diff --git a/src/providers/ldap/ldap_id_cleanup.c b/src/providers/ldap/ldap_id_cleanup.c
+index 330094f7034dba34b91b871df9a2b1bf86633b5a..bb7c0ea785fd17bfc0ff3a66bd9d5ff7f859625d 100644
+--- a/src/providers/ldap/ldap_id_cleanup.c
++++ b/src/providers/ldap/ldap_id_cleanup.c
+@@ -413,7 +413,8 @@ static void cleanup_users_delete(struct tevent_req *req)
+ ret = cleanup_users_logged_in(state->uid_table, state->msgs[state->cur]);
+ if (ret == EOK) {
+ /* If the user is logged in, proceed to the next one */
+- DEBUG(5, ("User %s is still logged in, keeping his data\n", name));
++ DEBUG(5, ("User %s is still logged in or a dummy entry, "
++ "keeping his data\n", name));
+ cleanup_users_next(req);
+ return;
+ } else if (ret != ENOENT) {
+@@ -446,9 +447,9 @@ static int cleanup_users_logged_in(hash_table_t *table,
+ uid = ldb_msg_find_attr_as_uint64(msg,
+ SYSDB_UIDNUM, 0);
+ if (!uid) {
+- DEBUG(2, ("Entry %s has no UID Attribute ?!?\n",
++ DEBUG(2, ("Entry %s has no UID Attribute, fake user perhaps?\n",
+ ldb_dn_get_linearized(msg->dn)));
+- return EFAULT;
++ return ENOENT;
+ }
+
+ key.type = HASH_KEY_ULONG;
+diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
+index 09312282bd07e72cabe06a00898d77f04e3e07c0..eba97ba82dcfe3d8c5fbc721c6a76e38b8c7b96f 100644
+--- a/src/providers/ldap/sdap.h
++++ b/src/providers/ldap/sdap.h
+@@ -162,6 +162,7 @@ enum sdap_basic_opt {
+ SDAP_DNS_SERVICE_NAME,
+ SDAP_ACCESS_FILTER,
+ SDAP_KRB5_TICKET_LIFETIME,
++ SDAP_NESTING_LEVEL,
+
+ SDAP_OPTS_BASIC /* opts counter */
+ };
+diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
+index beab326a05fe429a56d4eee78590ed78a58279c8..bc242719f17b16cfd44db80ab2356596e34fc9d9 100644
+--- a/src/providers/ldap/sdap_async_accounts.c
++++ b/src/providers/ldap/sdap_async_accounts.c
+@@ -381,6 +381,15 @@ struct tevent_req *sdap_save_users_send(TALLOC_CTX *memctx,
+ state->handle = NULL;
+ state->higher_timestamp = NULL;
+
++ if (num_users == 0) {
++ /* Nothing to do if there are no
++ * users
++ */
++ tevent_req_done(req);
++ tevent_req_post(req, ev);
++ return req;
++ }
++
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+@@ -814,7 +823,8 @@ static struct tevent_req *sdap_save_group_send(TALLOC_CTX *memctx,
+ struct sdap_options *opts,
+ struct sss_domain_info *dom,
+ struct sysdb_attrs *attrs,
+- bool store_members)
++ bool store_members,
++ bool populate_members)
+ {
+ struct tevent_req *req, *subreq;
+ struct sdap_save_group_state *state;
+@@ -912,7 +922,23 @@ static struct tevent_req *sdap_save_group_send(TALLOC_CTX *memctx,
+ }
+ }
+
+- if (store_members) {
++ if (populate_members) {
++ struct ldb_message_element *el1;
++ ret = sysdb_attrs_get_el(attrs,
++ opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
++ &el1);
++ if (ret != EOK) {
++ goto fail;
++ }
++
++ ret = sysdb_attrs_get_el(group_attrs, SYSDB_MEMBER, &el);
++ if (ret != EOK) {
++ goto fail;
++ }
++
++ el->values = el1->values;
++ el->num_values = el1->num_values;
++ } else if (store_members) {
+ ret = sysdb_attrs_get_el(attrs,
+ opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
+ if (ret != EOK) {
+@@ -1120,6 +1146,7 @@ struct sdap_save_groups_state {
+ int count;
+ int cur;
+ bool twopass;
++ bool populate_members;
+
+ struct sysdb_handle *handle;
+
+@@ -1137,6 +1164,7 @@ struct tevent_req *sdap_save_groups_send(TALLOC_CTX *memctx,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct sysdb_attrs **groups,
++ bool populate_members,
+ int num_groups)
+ {
+ struct tevent_req *req, *subreq;
+@@ -1154,6 +1182,7 @@ struct tevent_req *sdap_save_groups_send(TALLOC_CTX *memctx,
+ state->cur = 0;
+ state->handle = NULL;
+ state->higher_timestamp = NULL;
++ state->populate_members = populate_members;
+
+ switch (opts->schema_type) {
+ case SDAP_SCHEMA_RFC2307:
+@@ -1213,7 +1242,7 @@ static void sdap_save_groups_save(struct tevent_req *req)
+ subreq = sdap_save_group_send(state, state->ev, state->handle,
+ state->opts, state->dom,
+ state->groups[state->cur],
+- (!state->twopass));
++ (!state->twopass), state->populate_members);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+@@ -1260,7 +1289,7 @@ static void sdap_save_groups_loop(struct tevent_req *subreq)
+
+ sdap_save_groups_save(req);
+
+- } else if (state->twopass) {
++ } else if (state->twopass && !state->populate_members) {
+
+ state->cur = 0;
+ sdap_save_groups_mem_save(req);
+@@ -1346,6 +1375,563 @@ static int sdap_save_groups_recv(struct tevent_req *req,
+ return EOK;
+ }
+
++/* ==Process-Groups======================================================= */
++
++struct tevent_req *
++sdap_process_group_members_2307_send(TALLOC_CTX *memctx,
++ struct tevent_context *ev,
++ struct sss_domain_info *dom,
++ struct sysdb_ctx *sysdb,
++ struct ldb_message_element *memberel,
++ struct ldb_message_element *sysdb_dns);
++static int sdap_process_group_members_2307_recv(struct tevent_req *req);
++
++struct sdap_process_group_state {
++ struct tevent_context *ev;
++ struct sss_domain_info *dom;
++ struct sysdb_ctx *sysdb;
++ struct sysdb_attrs *group;
++
++ struct ldb_message_element *sysdb_dns;
++ struct ldb_message_element *memberel;
++};
++
++
++static void sdap_process_group_2307_done(struct tevent_req *subreq);
++
++struct tevent_req *sdap_process_group_send(TALLOC_CTX *memctx,
++ struct tevent_context *ev,
++ struct sss_domain_info *dom,
++ struct sysdb_ctx *sysdb,
++ struct sdap_options *opts,
++ struct sysdb_attrs *group)
++{
++ struct sdap_process_group_state *grp_state;
++ struct tevent_req *req = NULL;
++ struct tevent_req *subreq;
++ const char **attrs;
++ char* filter;
++ int ret;
++
++ req = tevent_req_create(memctx, &grp_state,
++ struct sdap_process_group_state);
++ if (!req) return NULL;
++
++ ret = build_attrs_from_map(grp_state, opts->user_map,
++ SDAP_OPTS_USER, &attrs);
++ if (ret) {
++ goto fail;
++ }
++
++ /* FIXME: we ignore nested rfc2307bis groups for now */
++ filter = talloc_asprintf(grp_state, "(objectclass=%s)",
++ opts->user_map[SDAP_OC_USER].name);
++ if (!filter) {
++ talloc_zfree(req);
++ return NULL;
++ }
++
++ grp_state->ev = ev;
++ grp_state->dom = dom;
++ grp_state->sysdb = sysdb;
++ grp_state->group = group;
++
++ ret = sysdb_attrs_get_el(group,
++ opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
++ &grp_state->memberel);
++ if (ret) {
++ goto fail;
++ }
++
++ /* Group without members */
++ if (grp_state->memberel->num_values == 0) {
++ DEBUG(2, ("No Members. Done!\n"));
++ tevent_req_done(req);
++ tevent_req_post(req, ev);
++ return req;
++ }
++
++ grp_state->sysdb_dns = talloc(grp_state,
++ struct ldb_message_element);
++ if (!grp_state->sysdb_dns) {
++ ret = ENOMEM;
++ goto fail;
++ }
++ grp_state->sysdb_dns->values = talloc_array(grp_state, struct ldb_val,
++ grp_state->memberel->num_values);
++ if (!grp_state->sysdb_dns->values) {
++ ret = ENOMEM;
++ goto fail;
++ }
++ grp_state->sysdb_dns->num_values = 0;
++
++ switch (opts->schema_type) {
++ case SDAP_SCHEMA_RFC2307:
++ subreq = sdap_process_group_members_2307_send(grp_state,
++ grp_state->ev,
++ grp_state->dom,
++ grp_state->sysdb,
++ grp_state->memberel,
++ grp_state->sysdb_dns);
++ if (!subreq) {
++ ret = ENOMEM;
++ goto fail;
++ }
++ tevent_req_set_callback(subreq, sdap_process_group_2307_done,
++ req);
++ break;
++
++ case SDAP_SCHEMA_IPA_V1:
++ case SDAP_SCHEMA_AD:
++ case SDAP_SCHEMA_RFC2307BIS:
++ DEBUG(2, ("Processing users for RFC2307BIS not yet implemeted\n"));
++ tevent_req_done(req);
++ tevent_req_post(req, ev);
++ break;
++
++ default:
++ DEBUG(1, ("Unknown schema type %d\n", opts->schema_type));
++ ret = EINVAL;
++ goto fail;
++ }
++
++ return req;
++fail:
++ tevent_req_error(req, ret);
++ tevent_req_post(req, ev);
++ return req;
++}
++
++static void sdap_process_group_2307_done(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sdap_process_group_state *state =
++ tevent_req_data(req, struct sdap_process_group_state);
++
++ int ret;
++
++ ret = sdap_process_group_members_2307_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ state->memberel->values = talloc_steal(state->group,
++ state->sysdb_dns->values);
++ state->memberel->num_values = state->sysdb_dns->num_values;
++ tevent_req_done(req);
++}
++
++static int sdap_process_group_recv(struct tevent_req *req)
++{
++ TEVENT_REQ_RETURN_ON_ERROR(req);
++ return EOK;
++}
++
++/*===Process-group-members-of-RFC2307-group============================*/
++
++struct tevent_req *
++sdap_process_missing_member_2307_send(TALLOC_CTX *memctx,
++ struct tevent_context *ev,
++ struct sss_domain_info *dom,
++ struct sysdb_ctx *sysdb,
++ struct sysdb_handle *handle,
++ const char *username,
++ struct ldb_message_element* sysdb_dns);
++static int sdap_process_missing_member_2307_recv(struct tevent_req *req);
++
++struct sdap_process_group_members_2307_state {
++ struct tevent_context *ev;
++ struct sss_domain_info *dom;
++ struct sysdb_ctx *sysdb;
++ struct sysdb_handle *handle;
++
++ struct ldb_message_element *sysdb_dns;
++ struct ldb_message_element *memberel;
++ int cur;
++
++ const char **missing;
++ int mi;
++ int ai;
++};
++
++static void sdap_process_group_members_2307_added(struct tevent_req *subreq);
++void sdap_process_group_members_2307_step(struct tevent_req *req);
++static void sdap_process_group_members_2307_check_add(struct tevent_req *req);
++static void
++sdap_process_group_members_2307_trans(struct tevent_req *subreq);
++static void sdap_process_group_members_2307_add(struct tevent_req *req);
++static void sdap_process_group_members_2307_added(struct tevent_req *subreq);
++static void sdap_process_group_members_2307_post(struct tevent_req *req);
++static void sdap_process_group_members_2307_trans_done(struct tevent_req *subreq);
++
++struct tevent_req *
++sdap_process_group_members_2307_send(TALLOC_CTX *memctx,
++ struct tevent_context *ev,
++ struct sss_domain_info *dom,
++ struct sysdb_ctx *sysdb,
++ struct ldb_message_element *memberel,
++ struct ldb_message_element *sysdb_dns)
++{
++ struct tevent_req *req = NULL;
++ struct sdap_process_group_members_2307_state *state;
++ struct tevent_req *subreq = NULL;
++
++ req = tevent_req_create(memctx, &state,
++ struct sdap_process_group_members_2307_state);
++ if (!req) return NULL;
++
++ state->ev = ev;
++ state->dom = dom;
++ state->sysdb = sysdb;
++ state->sysdb_dns = sysdb_dns;
++ state->memberel = memberel;
++ state->cur = 0;
++
++ if (state->memberel->num_values == 0) {
++ /* No members. Done. */
++ tevent_req_done(req);
++ tevent_req_post(req, ev);
++ return req;
++ }
++
++ state->missing = talloc_array(state, const char *,
++ state->memberel->num_values+1);
++ if (!state->missing) {
++ talloc_zfree(req);
++ return NULL;
++ }
++ state->mi = 0;
++ state->missing[state->mi] = NULL;
++
++ subreq = sysdb_search_user_by_name_send(state, state->ev,
++ state->sysdb, NULL,
++ state->dom,
++ (const char *) state->memberel->values[state->cur].data,
++ NULL);
++ if (!subreq) {
++ talloc_zfree(req);
++ return NULL;
++ }
++ tevent_req_set_callback(subreq, sdap_process_group_members_2307_step, req);
++
++ return req;
++}
++
++void sdap_process_group_members_2307_step(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sdap_process_group_members_2307_state *state =
++ tevent_req_data(req, struct sdap_process_group_members_2307_state);
++ struct ldb_message *msg;
++ char *strdn;
++ int ret;
++
++ ret = sysdb_search_user_recv(subreq, state, &msg);
++ talloc_zfree(subreq);
++ if (ret == EOK) {
++ /*
++ * User already cached in sysdb. Remember the sysdb DN for later
++ * use by sdap_save_groups()
++ */
++ strdn = sysdb_user_strdn(state->sysdb_dns->values,
++ state->dom->name,
++ (const char *) state->memberel->values[state->cur].data);
++ if (!strdn) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++
++ DEBUG(7,("Member already cached in sysdb: %s\n", strdn));
++ state->sysdb_dns->values[state->sysdb_dns->num_values].data =
++ (uint8_t *) strdn;
++ state->sysdb_dns->values[state->sysdb_dns->num_values].length =
++ strlen(strdn);
++ state->sysdb_dns->num_values++;
++ } else if (ret == ENOENT) {
++ /* The user is not in sysdb, need to add it */
++ DEBUG(7, ("member #%d (%s): not found in sysdb\n",
++ state->cur,
++ (char *) state->memberel->values[state->cur].data));
++
++ /* Just remember the name and store all the fake
++ * entries later in one transaction */
++ state->missing[state->mi] =
++ (const char *) state->memberel->values[state->cur].data;
++ state->mi++;
++ state->missing[state->mi] = NULL;
++ } else {
++ DEBUG(1, ("Error checking cache for member #%d (%s):\n",
++ state->cur,
++ (char *) state->memberel->values[state->cur].data));
++ tevent_req_error(req, ret);
++ return;
++ }
++
++
++ state->cur++;
++ if (state->cur == state->memberel->num_values) {
++ /* All members processed. Add fake entries if needed. */
++ state->ai = 0;
++ sdap_process_group_members_2307_check_add(req);
++ return;
++ }
++
++ /* Go to the next member */
++ subreq = sysdb_search_user_by_name_send(state, state->ev,
++ state->sysdb, NULL,
++ state->dom,
++ (char *) state->memberel->values[state->cur].data,
++ NULL);
++ if (!subreq) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++
++ tevent_req_set_callback(subreq,
++ sdap_process_group_members_2307_step,
++ req);
++ return;
++}
++
++static void
++sdap_process_group_members_2307_check_add(struct tevent_req *req)
++{
++ struct sdap_process_group_members_2307_state *state =
++ tevent_req_data(req, struct sdap_process_group_members_2307_state);
++ struct tevent_req *subreq;
++
++ if (state->mi == 0) {
++ /* Do not need to add any members. We are done. */
++ tevent_req_done(req);
++ return;
++ }
++
++ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
++ if (!subreq) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++
++ tevent_req_set_callback(subreq, sdap_process_group_members_2307_trans,
++ req);
++}
++
++static void
++sdap_process_group_members_2307_trans(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sdap_process_group_members_2307_state *state =
++ tevent_req_data(req, struct sdap_process_group_members_2307_state);
++ int ret;
++
++ ret = sysdb_transaction_recv(subreq, state, &state->handle);
++ talloc_zfree(subreq);
++ if (ret) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ sdap_process_group_members_2307_add(req);
++}
++
++static void
++sdap_process_group_members_2307_add(struct tevent_req *req)
++{
++ struct sdap_process_group_members_2307_state *state =
++ tevent_req_data(req, struct sdap_process_group_members_2307_state);
++ struct tevent_req *subreq;
++
++ if (state->ai == state->mi) {
++ sdap_process_group_members_2307_post(req);
++ return;
++ }
++
++ subreq = sdap_process_missing_member_2307_send(state, state->ev,
++ state->dom,
++ state->sysdb,
++ state->handle,
++ state->missing[state->ai],
++ state->sysdb_dns);
++ if (!subreq) {
++ DEBUG(1, ("Error adding missing member #%d (%s):\n",
++ state->ai, state->missing[state->ai]));
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++ tevent_req_set_callback(subreq, sdap_process_group_members_2307_added, req);
++ return;
++}
++
++static void
++sdap_process_group_members_2307_added(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sdap_process_group_members_2307_state *state =
++ tevent_req_data(req, struct sdap_process_group_members_2307_state);
++ int ret;
++
++ ret = sdap_process_missing_member_2307_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ state->ai++;
++ sdap_process_group_members_2307_add(req);
++}
++
++static void
++sdap_process_group_members_2307_post(struct tevent_req *req)
++{
++ struct sdap_process_group_members_2307_state *state =
++ tevent_req_data(req, struct sdap_process_group_members_2307_state);
++ struct tevent_req *subreq;
++
++ /* Commit the transaction */
++ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
++ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++
++ tevent_req_set_callback(subreq,
++ sdap_process_group_members_2307_trans_done,
++ req);
++}
++
++static void
++sdap_process_group_members_2307_trans_done(struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++
++ ret = sysdb_transaction_commit_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /* Processing completed. */
++ tevent_req_done(req);
++}
++
++int sdap_process_group_members_2307_recv(struct tevent_req *req)
++{
++ TEVENT_REQ_RETURN_ON_ERROR(req);
++ return EOK;
++}
++
++/*===Process-missing-group-member-of-RFC2307-group============================*/
++
++struct sdap_process_missing_member_2307_state {
++ struct tevent_context *ev;
++ struct sss_domain_info *dom;
++ struct sysdb_ctx *sysdb;
++ struct sysdb_handle *handle;
++
++ const char *username;
++ struct ldb_message_element* sysdb_dns;
++
++};
++
++static void sdap_process_missing_member_2307_done(struct tevent_req *subreq);
++
++struct tevent_req *
++sdap_process_missing_member_2307_send(TALLOC_CTX *memctx,
++ struct tevent_context *ev,
++ struct sss_domain_info *dom,
++ struct sysdb_ctx *sysdb,
++ struct sysdb_handle *handle,
++ const char *username,
++ struct ldb_message_element *sysdb_dns)
++{
++ struct sdap_process_missing_member_2307_state *state;
++ struct tevent_req *req = NULL;
++ struct tevent_req *subreq;
++
++ req = tevent_req_create(memctx, &state,
++ struct sdap_process_missing_member_2307_state);
++ if (!req) return NULL;
++
++ state->ev = ev;
++ state->dom = dom;
++ state->handle = handle;
++ state->sysdb = sysdb;
++ state->sysdb_dns = sysdb_dns;
++ state->username = username;
++
++ DEBUG(7, ("Adding a dummy entry\n"));
++ subreq = sysdb_add_fake_user_send(state, state->ev, state->handle,
++ state->dom, state->username);
++ if (!subreq) {
++ DEBUG(2, ("Cannot store fake user entry\n"));
++ talloc_zfree(req);
++ return NULL;
++ }
++ tevent_req_set_callback(subreq,
++ sdap_process_missing_member_2307_done,
++ req);
++ return req;
++}
++
++static void sdap_process_missing_member_2307_done(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sdap_process_missing_member_2307_state *state =
++ tevent_req_data(req,
++ struct sdap_process_missing_member_2307_state);
++ int ret;
++ struct ldb_dn *dn;
++ char* dn_string;
++
++ ret = sysdb_add_fake_user_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /*
++ * Convert the just received DN into the corresponding sysdb DN
++ * for saving into member attribute of the group
++ */
++ dn = sysdb_user_dn(state->sysdb, state, state->dom->name,
++ state->username);
++ if (!dn) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++
++ dn_string = ldb_dn_alloc_linearized(state->sysdb_dns->values, dn);
++ if (!dn_string) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++
++ state->sysdb_dns->values[state->sysdb_dns->num_values].data =
++ (uint8_t *) dn_string;
++ state->sysdb_dns->values[state->sysdb_dns->num_values].length =
++ strlen(dn_string);
++ state->sysdb_dns->num_values++;
++
++ tevent_req_done(req);
++}
++
++static int sdap_process_missing_member_2307_recv(struct tevent_req *req)
++{
++ TEVENT_REQ_RETURN_ON_ERROR(req);
++ return EOK;
++}
+
+ /* ==Search-Groups-with-filter============================================ */
+
+@@ -1361,9 +1947,15 @@ struct sdap_get_groups_state {
+ char *higher_timestamp;
+ 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);
++static void sdap_get_groups_processed(struct tevent_req *subreq);
+ static void sdap_get_groups_done(struct tevent_req *subreq);
+
+ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
+@@ -1407,6 +1999,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 = tevent_req_callback_data(subreq,
+@@ -1414,6 +2015,7 @@ static void sdap_get_groups_process(struct tevent_req *subreq)
+ struct sdap_get_groups_state *state = tevent_req_data(req,
+ struct sdap_get_groups_state);
+ int ret;
++ int i;
+
+ ret = sdap_get_generic_recv(subreq, state,
+ &state->count, &state->groups);
+@@ -1425,18 +2027,207 @@ 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) {
++ state->check_count = state->count;
++ for (i=0; i < state->count; i++) {
++ subreq = sdap_process_group_send(state, state->ev, state->dom,
++ state->sysdb, state->opts,
++ state->groups[i]);
++ if (!subreq) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++ tevent_req_set_callback(subreq, sdap_get_groups_processed, req);
++ }
++ return;
++ } else {
++
++ /* Prepare hashes for nested user procesing */
++ 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;
++ }
++ break;
++
++ default:
++ /* Enumeration */
++ break;
+ }
+
+ subreq = sdap_save_groups_send(state, state->ev, state->dom,
+ state->sysdb, state->opts,
+- state->groups, state->count);
++ state->groups, false, state->count);
++ if (!subreq) {
++ tevent_req_error(req, ENOMEM);
++ }
++ tevent_req_set_callback(subreq, sdap_get_groups_done, req);
++}
++
++static void sdap_get_groups_processed(struct tevent_req *subreq)
++{
++ 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);
++ int ret;
++
++ ret = sdap_process_group_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret) {
++ DEBUG(2, ("Failed to process group.\n"));
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ state->check_count--;
++ DEBUG(9, ("Groups remaining: %d\n", state->check_count));
++
++ if (state->check_count == 0) {
++ subreq = sdap_save_groups_send(state, state->ev, state->dom,
++ state->sysdb, state->opts,
++ state->groups, true, state->count);
++ if (!subreq) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++ tevent_req_set_callback(subreq, sdap_get_groups_done, req);
++ }
++}
++
++static void sdap_nested_users_done(struct tevent_req *subreq);
++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 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.
++ */
++ subreq = sdap_save_users_send(state, state->ev, state->dom,
++ state->sysdb, state->opts,
++ users, count);
+ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++ tevent_req_set_callback(subreq, sdap_nested_users_done, req);
++}
++
++static void sdap_nested_users_done(struct tevent_req *subreq)
++{
++ errno_t ret;
++ int hret;
++ unsigned long i;
++ unsigned long count;
++ hash_value_t *values;
++ struct sysdb_attrs **groups;
++ 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_save_users_recv(subreq, NULL, NULL);
++ talloc_zfree(subreq);
++ 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);
++
++ subreq = sdap_save_groups_send(state, state->ev, state->dom,
++ state->sysdb, state->opts,
++ groups, false, count);
++ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
+ tevent_req_set_callback(subreq, sdap_get_groups_done, req);
+ }
+
+@@ -1476,6 +2267,206 @@ int sdap_get_groups_recv(struct tevent_req *req,
+ return EOK;
+ }
+
++/* ==Add-Incomplete-Groups====================================================== */
++struct sdap_add_incomplete_groups_state {
++ struct tevent_context *ev;
++ struct sysdb_ctx *sysdb;
++ struct sysdb_handle *handle;
++ struct sss_domain_info *dom;
++
++ char **groupnames;
++ int cur;
++
++ struct sysdb_attrs **ldap_groups;
++ int ldap_groups_count;
++};
++
++static void sdap_add_incomplete_groups_next(struct tevent_req *subreq);
++static void sdap_add_incomplete_groups_added(struct tevent_req *subreq);
++
++static
++struct tevent_req *sdap_add_incomplete_groups_send(TALLOC_CTX *memctx,
++ struct tevent_context *ev,
++ struct sysdb_ctx *sysdb,
++ struct sysdb_handle *handle,
++ struct sss_domain_info *dom,
++ char **groupnames,
++ struct sysdb_attrs **ldap_groups,
++ int ldap_groups_count)
++{
++ struct tevent_req *req, *subreq;
++ struct sdap_add_incomplete_groups_state *state;
++
++ req = tevent_req_create(memctx, &state, struct sdap_add_incomplete_groups_state);
++ if (!req) return NULL;
++
++ state->ev = ev;
++ state->sysdb = sysdb;
++ state->handle = handle;
++ state->dom = dom;
++ state->groupnames = groupnames;
++ state->ldap_groups = ldap_groups;
++ state->ldap_groups_count = ldap_groups_count;
++ state->cur = 0;
++
++ subreq = sysdb_search_group_by_name_send(state, ev, sysdb, handle, dom,
++ state->groupnames[state->cur],
++ NULL);
++ if (!subreq) {
++ talloc_zfree(req);
++ return NULL;
++ }
++ tevent_req_set_callback(subreq, sdap_add_incomplete_groups_next, req);
++
++ return req;
++}
++
++static void sdap_add_incomplete_groups_next(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sdap_add_incomplete_groups_state *state = tevent_req_data(req,
++ struct sdap_add_incomplete_groups_state);
++ errno_t ret;
++ int ai;
++ const char *name;
++ gid_t gid;
++ struct ldb_message *msg = NULL;
++ struct ldb_message_element *el;
++ struct tevent_req *add_req = NULL;
++ struct tevent_req *next_req = NULL;
++
++ ret = sysdb_search_group_recv(subreq, state, &msg);
++ talloc_zfree(subreq);
++ if (ret == EOK) {
++ state->cur++;
++ if (state->groupnames[state->cur]) {
++ next_req = sysdb_search_group_by_name_send(state, state->ev,
++ state->sysdb,
++ state->handle,
++ state->dom,
++ state->groupnames[state->cur],
++ NULL);
++ if (!next_req) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++ tevent_req_set_callback(next_req, sdap_add_incomplete_groups_next, req);
++ return;
++ }
++
++ tevent_req_done(req);
++ return;
++ } else if (ret == ENOENT) {
++ DEBUG(7, ("Group #%d [%s] is not cached, need to add a incomplete entry\n",
++ state->cur, state->groupnames[state->cur]));
++
++ /* The group is not in sysdb, need to add an incomplete entry */
++ for (ai=0; ai < state->ldap_groups_count; ai++) {
++ ret = sysdb_attrs_get_el(state->ldap_groups[ai],
++ SYSDB_NAME,
++ &el);
++ if (ret) {
++ tevent_req_error(req, ret);
++ return;
++ }
++ if (el->num_values == 0) {
++ tevent_req_error(req, EINVAL);
++ return;
++ }
++ name = (const char *)el->values[0].data;
++
++ if (strcmp(name, state->groupnames[state->cur]) == 0) {
++ ret = sysdb_attrs_get_el(state->ldap_groups[ai],
++ SYSDB_GIDNUM,
++ &el);
++ if (ret) {
++ tevent_req_error(req, ret);
++ return;
++ }
++ if (el->num_values == 0) {
++ DEBUG(1, ("no gid provided for [%s]\n",
++ name));
++ tevent_req_error(req, EINVAL);
++ return;
++ }
++
++ errno = 0;
++ gid = (gid_t) strtol((const char *)el->values[0].data, NULL, 0);
++ if (errno) {
++ tevent_req_error(req, errno);
++ return;
++ }
++
++ add_req = sysdb_add_incomplete_group_send(state,
++ state->ev,
++ state->handle,
++ state->dom,
++ state->groupnames[state->cur],
++ gid);
++ if (add_req == NULL) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++
++ tevent_req_set_callback(add_req,
++ sdap_add_incomplete_groups_added,
++ req);
++ return;
++ }
++ }
++
++ if (ai == state->ldap_groups_count) {
++ tevent_req_error(req, EINVAL);
++ return;
++ }
++ }
++
++ DEBUG(2, ("Search failed: %s (%d)\n", strerror(ret), ret));
++ tevent_req_error(req, ret);
++}
++
++static void sdap_add_incomplete_groups_added(struct tevent_req *subreq)
++{
++ struct tevent_req *req = tevent_req_callback_data(subreq,
++ struct tevent_req);
++ struct sdap_add_incomplete_groups_state *state = tevent_req_data(req,
++ struct sdap_add_incomplete_groups_state);
++ errno_t ret;
++ struct tevent_req *next_req = NULL;
++
++ ret = sysdb_add_incomplete_group_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ state->cur++;
++ if (state->groupnames[state->cur]) {
++ next_req = sysdb_search_group_by_name_send(state, state->ev,
++ state->sysdb, state->handle,
++ state->dom,
++ state->groupnames[state->cur],
++ NULL);
++ if (!next_req) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++ tevent_req_set_callback(next_req, sdap_add_incomplete_groups_next, req);
++ return;
++ }
++
++ tevent_req_done(req);
++ return;
++}
++
++int sdap_add_incomplete_groups_recv(struct tevent_req *req)
++{
++ TEVENT_REQ_RETURN_ON_ERROR(req);
++
++ return EOK;
++}
+
+ /* ==Initgr-call-(groups-a-user-is-member-of)-RFC2307-Classic/BIS========= */
+
+@@ -1485,11 +2476,17 @@ struct sdap_initgr_rfc2307_state {
+ struct sdap_options *opts;
+ struct sss_domain_info *dom;
+ struct sdap_handle *sh;
+- char *name;
++ const char *name;
+
+ struct sysdb_handle *handle;
+ char **ldap_grouplist;
+
++ struct sysdb_attrs **ldap_groups;
++ size_t ldap_groups_count;
++
++ char **add_groups;
++ char **del_groups;
++
+ struct sdap_op *op;
+ };
+
+@@ -1508,7 +2505,8 @@ struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx,
+ struct tevent_req *req, *subreq;
+ struct sdap_initgr_rfc2307_state *state;
+ const char *filter;
+- const char *attrs[2];
++ const char **attrs;
++ errno_t ret;
+
+ req = tevent_req_create(memctx, &state, struct sdap_initgr_rfc2307_state);
+ if (!req) return NULL;
+@@ -1525,12 +2523,12 @@ struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx,
+ return NULL;
+ }
+
+- attrs[0] = talloc_strdup(state, opts->group_map[SDAP_AT_GROUP_NAME].name);
+- if (!attrs[0]) {
+- talloc_zfree(req);
++ ret = build_attrs_from_map(state, opts->group_map,
++ SDAP_OPTS_GROUP, &attrs);
++ if (ret != EOK) {
++ talloc_free(req);
+ return NULL;
+ }
+- attrs[1] = NULL;
+
+ filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
+ opts->group_map[SDAP_AT_GROUP_MEMBER].name,
+@@ -1558,21 +2556,21 @@ static void sdap_initgr_rfc2307_process(struct tevent_req *subreq)
+ {
+ struct tevent_req *req;
+ struct sdap_initgr_rfc2307_state *state;
+- struct sysdb_attrs **groups;
+- size_t count;
+ int ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct sdap_initgr_rfc2307_state);
+
+- ret = sdap_get_generic_recv(subreq, state, &count, &groups);
++ ret = sdap_get_generic_recv(subreq, state,
++ &state->ldap_groups_count,
++ &state->ldap_groups);
+ talloc_zfree(subreq);
+ if (ret) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+- if (count == 0) {
++ if (state->ldap_groups_count == 0) {
+ /* No groups for this user in LDAP
+ * We need to ensure that there are no groups
+ * in the sysdb either.
+@@ -1581,8 +2579,9 @@ static void sdap_initgr_rfc2307_process(struct tevent_req *subreq)
+ state->ldap_grouplist = NULL;
+ }
+ else {
+- ret = sysdb_attrs_to_list(state, groups, count,
+- state->opts->group_map[SDAP_AT_GROUP_NAME].name,
++ ret = sysdb_attrs_to_list(state,
++ state->ldap_groups, state->ldap_groups_count,
++ SYSDB_NAME,
+ &state->ldap_grouplist);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+@@ -1644,6 +2643,9 @@ static void sdap_initgr_rfc2307_get_sysdb_groups(struct tevent_req *subreq)
+
+ static void
+ sdap_initgr_rfc2307_update_sysdb_groups_done(struct tevent_req *subreq);
++static void
++sdap_initgr_rfc2307_update_sysdb_groups_step(struct tevent_req *subreq);
++
+ static void sdap_initgr_rfc2307_update_sysdb_groups(struct tevent_req *subreq)
+ {
+ struct tevent_req *req =
+@@ -1654,8 +2656,6 @@ static void sdap_initgr_rfc2307_update_sysdb_groups(struct tevent_req *subreq)
+ struct ldb_message *reply;
+ struct ldb_message_element *groups;
+ char **sysdb_grouplist;
+- char **add_groups;
+- char **del_groups;
+
+ ret = sysdb_search_user_recv(subreq, state, &reply);
+ talloc_zfree(subreq);
+@@ -1667,47 +2667,65 @@ static void sdap_initgr_rfc2307_update_sysdb_groups(struct tevent_req *subreq)
+ groups = ldb_msg_find_element(reply, SYSDB_MEMBEROF);
+ if (!groups || groups->num_values == 0) {
+ DEBUG(6, ("User is not a member of any groups\n"));
++ sysdb_grouplist = NULL;
++ } else {
++ sysdb_grouplist = talloc_array(state, char *,
++ groups->num_values+1);
++ if (!sysdb_grouplist) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
+
+- tevent_req_done(req);
+- return;
+- }
+-
+- sysdb_grouplist = talloc_array(state, char *,
+- groups->num_values+1);
+- if (!sysdb_grouplist) {
+- tevent_req_error(req, ENOMEM);
+- return;
+- }
++ /* Get a list of the groups by groupname only */
++ for (i=0; i < groups->num_values; i++) {
++ ret = sysdb_group_dn_name(state->sysdb,
++ sysdb_grouplist,
++ (const char *)groups->values[i].data,
++ &sysdb_grouplist[i]);
++ if (ret != EOK) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++ }
+
+- /* Get a list of the groups by groupname only */
+- for (i=0; i < groups->num_values; i++) {
+- ret = sysdb_group_dn_name(state->sysdb,
+- sysdb_grouplist,
+- (const char *)groups->values[i].data,
+- &sysdb_grouplist[i]);
+- if (ret != EOK) {
+- tevent_req_error(req, ENOMEM);
+- return;
+- }
++ sysdb_grouplist[groups->num_values] = NULL;
+ }
+
+- sysdb_grouplist[groups->num_values] = NULL;
+-
+ /* Find the differences between the sysdb and ldap lists
+ * Groups in ldap only must be added to the sysdb;
+ * groups in the sysdb only must be removed.
+ */
+ ret = diff_string_lists(state,
+ state->ldap_grouplist, sysdb_grouplist,
+- &add_groups, &del_groups, NULL);
++ &state->add_groups, &state->del_groups, NULL);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
++ if (state->add_groups && state->add_groups[0]) {
++ subreq = sdap_add_incomplete_groups_send(state, state->ev, state->sysdb,
++ state->handle, state->dom,
++ state->add_groups,
++ state->ldap_groups,
++ state->ldap_groups_count);
++ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++
++ tevent_req_set_callback(subreq,
++ sdap_initgr_rfc2307_update_sysdb_groups_step,
++ req);
++ return;
++ }
++
++
++
+ subreq = sysdb_update_members_send(state, state->ev, state->handle,
+ state->dom, state->name,
+- add_groups, del_groups);
++ SYSDB_MEMBER_USER,
++ state->add_groups, state->del_groups);
+ if (!subreq) {
+ tevent_req_error(req, EIO);
+ return;
+@@ -1719,6 +2737,37 @@ static void sdap_initgr_rfc2307_update_sysdb_groups(struct tevent_req *subreq)
+ }
+
+ static void
++sdap_initgr_rfc2307_update_sysdb_groups_step(struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_initgr_rfc2307_state *state =
++ tevent_req_data(req, struct sdap_initgr_rfc2307_state);
++ struct tevent_req *updatereq;
++
++ ret = sdap_add_incomplete_groups_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ updatereq = sysdb_update_members_send(state, state->ev, state->handle,
++ state->dom, state->name,
++ SYSDB_MEMBER_USER,
++ state->add_groups, state->del_groups);
++ if (!updatereq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++
++ tevent_req_set_callback(updatereq,
++ sdap_initgr_rfc2307_update_sysdb_groups_done,
++ req);
++}
++
++static void
+ sdap_initgr_rfc2307_transaction_done(struct tevent_req *subreq);
+ static void
+ sdap_initgr_rfc2307_update_sysdb_groups_done(struct tevent_req *subreq)
+@@ -1936,7 +2985,7 @@ static void sdap_initgr_nested_store(struct tevent_req *req)
+
+ subreq = sdap_save_groups_send(state, state->ev, state->dom,
+ state->sysdb, state->opts,
+- state->groups, state->groups_cur);
++ state->groups, false, state->groups_cur);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+@@ -2145,6 +3194,17 @@ static void sdap_get_initgr_commit(struct tevent_req *subreq)
+ tevent_req_set_callback(subreq, sdap_get_initgr_process, req);
+ }
+
++static struct tevent_req *sdap_initgr_rfc2307bis_send(
++ TALLOC_CTX *memctx,
++ struct tevent_context *ev,
++ struct sdap_options *opts,
++ struct sysdb_ctx *sysdb,
++ struct sss_domain_info *dom,
++ struct sdap_handle *sh,
++ const char *base_dn,
++ const char *name,
++ const char *orig_dn);
++static int sdap_initgr_rfc2307bis_recv(struct tevent_req *req);
+ static void sdap_get_initgr_process(struct tevent_req *subreq)
+ {
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+@@ -2152,6 +3212,7 @@ static void sdap_get_initgr_process(struct tevent_req *subreq)
+ struct sdap_get_initgr_state *state = tevent_req_data(req,
+ struct sdap_get_initgr_state);
+ int ret;
++ const char *orig_dn;
+
+ DEBUG(9, ("Process user's groups\n"));
+
+@@ -2177,6 +3238,26 @@ static void sdap_get_initgr_process(struct tevent_req *subreq)
+ break;
+
+ case SDAP_SCHEMA_RFC2307BIS:
++ ret = sysdb_attrs_get_string(state->orig_user,
++ SYSDB_ORIG_DN,
++ &orig_dn);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ subreq = sdap_initgr_rfc2307bis_send(
++ state, state->ev, state->opts, state->sysdb, state->dom,
++ state->sh, dp_opt_get_string(state->opts->basic,
++ SDAP_GROUP_SEARCH_BASE),
++ state->name, orig_dn);
++ if (!subreq) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++ talloc_steal(subreq, orig_dn);
++ tevent_req_set_callback(subreq, sdap_get_initgr_done, req);
++ break;
+ case SDAP_SCHEMA_IPA_V1:
+ case SDAP_SCHEMA_AD:
+ /* TODO: AD uses a different member/memberof schema
+@@ -2216,6 +3297,10 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
+ break;
+
+ case SDAP_SCHEMA_RFC2307BIS:
++
++ ret = sdap_initgr_rfc2307bis_recv(subreq);
++ break;
++
+ case SDAP_SCHEMA_IPA_V1:
+ case SDAP_SCHEMA_AD:
+
+@@ -2244,3 +3329,1671 @@ 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_sysdb_users(struct tevent_req *subreq);
++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);
++ struct tevent_req *subreq;
++ char *filter;
++ static const char *attrs[] = SYSDB_PW_ATTRS;
++ bool has_key = false;
++ hash_key_t key;
++ uint8_t *data;
++
++ 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 */
++ subreq = sysdb_search_users_send(state, state->ev, state->sysdb,
++ NULL, state->domain, filter,
++ attrs);
++ if (!subreq) {
++ ret = EIO;
++ talloc_free(filter);
++ goto error;
++ }
++ talloc_steal(subreq, filter);
++ tevent_req_set_callback(subreq,
++ sdap_nested_group_process_sysdb_users,
++ req);
++
++ return EAGAIN;
++
++error:
++ talloc_zfree(state->member_dn);
++ return ret;
++}
++
++static void sdap_nested_group_process_user(struct tevent_req *subreq);
++static void sdap_nested_group_process_group(struct tevent_req *subreq);
++static void sdap_nested_group_process_sysdb_groups(struct tevent_req *subreq);
++static errno_t sdap_nested_group_lookup_user(struct tevent_req *req,
++ tevent_req_fn fn);
++static void sdap_nested_group_process_sysdb_users(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);
++ size_t count;
++ struct ldb_message **msgs;
++ uint64_t expiration;
++ time_t now = time(NULL);
++ char *filter;
++
++ static const char *attrs[] = SYSDB_GRSRC_ATTRS;
++
++ ret = sysdb_search_users_recv(subreq, state, &count, &msgs);
++ talloc_zfree(subreq);
++ if (ret != EOK && ret != ENOENT) {
++ tevent_req_error(req, ret);
++ return;
++ } 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) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++ subreq = sysdb_search_groups_send(state, state->ev, state->sysdb,
++ NULL, state->domain, filter,
++ attrs);
++ if (!subreq) {
++ talloc_free(filter);
++ tevent_req_error(req, EIO);
++ return;
++ }
++ talloc_steal(subreq, filter);
++ tevent_req_set_callback(subreq,
++ sdap_nested_group_process_sysdb_groups,
++ req);
++ return;
++ }
++
++ /* Check whether the entry is valid */
++ if (count != 1) {
++ DEBUG(1, ("More than one entry with this origDN? Skipping\n"));
++ goto skip;
++ }
++
++ 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"));
++ goto skip;
++ }
++
++ ret = sdap_nested_group_lookup_user(req, sdap_nested_group_process_user);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ }
++ return;
++
++skip:
++ state->member_index++;
++ talloc_zfree(state->member_dn);
++ ret = sdap_nested_group_process_step(req);
++ if (ret == EOK) {
++ tevent_req_done(req);
++ } else if (ret != EAGAIN) {
++ tevent_req_error(req, ret);
++ }
++
++ /* EAGAIN means that we should re-enter
++ * the mainloop
++ */
++}
++
++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 errno_t sdap_nested_group_lookup_group(struct tevent_req *req);
++static void sdap_nested_group_process_ldap_user(struct tevent_req *subreq);
++static void sdap_nested_group_process_sysdb_groups(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);
++ size_t count;
++ uint64_t expiration;
++ struct ldb_message **msgs;
++ time_t now = time(NULL);
++
++ ret = sysdb_search_groups_recv(subreq, state, &count, &msgs);
++ talloc_zfree(subreq);
++ if (ret != EOK && ret != ENOENT) {
++ tevent_req_error(req, ret);
++ return;
++ } if (ret == ENOENT || count == 0) {
++ /* 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;
++ }
++
++ /* Check whether the entry is valid */
++ if (count != 1) {
++ DEBUG(1, ("More than one entry with this origDN? Skipping\n"));
++ goto skip;
++ }
++
++ 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"));
++ goto skip;
++ }
++
++ /* Look up the group in LDAP */
++ ret = sdap_nested_group_lookup_group(req);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ }
++ return;
++
++skip:
++ state->member_index++;
++ talloc_zfree(state->member_dn);
++ ret = sdap_nested_group_process_step(req);
++ if (ret == EOK) {
++ tevent_req_done(req);
++ } else if (ret != EAGAIN) {
++ tevent_req_error(req, ret);
++ }
++}
++
++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;
++}
++
++static void sdap_initgr_rfc2307bis_process(struct tevent_req *subreq);
++static struct tevent_req *sdap_initgr_rfc2307bis_send(
++ TALLOC_CTX *memctx,
++ struct tevent_context *ev,
++ struct sdap_options *opts,
++ struct sysdb_ctx *sysdb,
++ struct sss_domain_info *dom,
++ struct sdap_handle *sh,
++ const char *base_dn,
++ const char *name,
++ const char *orig_dn)
++{
++ errno_t ret;
++ struct tevent_req *req;
++ struct tevent_req *subreq;
++ struct sdap_initgr_rfc2307_state *state;
++ const char *filter;
++ const char **attrs;
++
++ req = tevent_req_create(memctx, &state, struct sdap_initgr_rfc2307_state);
++ if (!req) return NULL;
++
++ state->ev = ev;
++ state->opts = opts;
++ state->sysdb = sysdb;
++ state->dom = dom;
++ state->sh = sh;
++ state->op = NULL;
++ state->name = name;
++
++ ret = build_attrs_from_map(state, opts->group_map,
++ SDAP_OPTS_GROUP, &attrs);
++ if (ret != EOK) {
++ talloc_free(req);
++ return NULL;
++ }
++
++ filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))",
++ opts->group_map[SDAP_AT_GROUP_MEMBER].name,
++ orig_dn, opts->group_map[SDAP_OC_GROUP].name);
++ if (!filter) {
++ talloc_zfree(req);
++ return NULL;
++ }
++
++ DEBUG(6, ("Looking up parent groups for user [%s]\n", orig_dn));
++ subreq = sdap_get_generic_send(state, state->ev, state->opts,
++ state->sh, base_dn, LDAP_SCOPE_SUBTREE,
++ filter, attrs,
++ state->opts->group_map, SDAP_OPTS_GROUP);
++ if (!subreq) {
++ talloc_zfree(req);
++ return NULL;
++ }
++ tevent_req_set_callback(subreq, sdap_initgr_rfc2307bis_process, req);
++
++ return req;
++
++}
++
++errno_t save_rfc2307bis_user_memberships(struct tevent_req *req);
++struct tevent_req *rfc2307bis_nested_groups_send(
++ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
++ struct sdap_options *opts, struct sysdb_ctx *sysdb,
++ struct sss_domain_info *dom, struct sdap_handle *sh,
++ struct sysdb_attrs **groups, size_t num_groups);
++static void sdap_initgr_rfc2307bis_done(struct tevent_req *subreq);
++static void sdap_initgr_rfc2307bis_process(struct tevent_req *subreq)
++{
++ struct tevent_req *req;
++ struct sdap_initgr_rfc2307_state *state;
++ int ret;
++
++ req = tevent_req_callback_data(subreq, struct tevent_req);
++ state = tevent_req_data(req, struct sdap_initgr_rfc2307_state);
++
++ ret = sdap_get_generic_recv(subreq, state,
++ &state->ldap_groups_count,
++ &state->ldap_groups);
++ talloc_zfree(subreq);
++ if (ret) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ if (state->ldap_groups_count == 0) {
++ /* Start a transaction to look up the groups in the sysdb
++ * and update them with LDAP data
++ */
++ ret = save_rfc2307bis_user_memberships(req);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ }
++ return;
++ }
++
++ subreq = rfc2307bis_nested_groups_send(state, state->ev, state->opts,
++ state->sysdb, state->dom,
++ state->sh, state->ldap_groups,
++ state->ldap_groups_count);
++ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++ tevent_req_set_callback(subreq, sdap_initgr_rfc2307bis_done, req);
++}
++
++static void sdap_initgr_rfc2307bis_get_sysdb_groups(struct tevent_req *subreq);
++errno_t save_rfc2307bis_user_memberships(struct tevent_req *req)
++{
++ struct tevent_req *subreq;
++ struct sdap_initgr_rfc2307_state *state =
++ tevent_req_data(req, struct sdap_initgr_rfc2307_state);
++
++ DEBUG(7, ("Save parent groups to sysdb\n"));
++ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
++ if (!subreq) {
++ return EIO;
++ }
++ /* Save this user and their memberships */
++ tevent_req_set_callback(subreq,
++ sdap_initgr_rfc2307bis_get_sysdb_groups,
++ req);
++ return EOK;
++}
++
++static errno_t rfc2307bis_sysdb_member_lookup(TALLOC_CTX *mem_ctx,
++ struct tevent_context *ev,
++ struct sysdb_ctx *sysdb,
++ struct sysdb_handle *handle,
++ struct sss_domain_info *dom,
++ enum sysdb_member_type type,
++ const char *domainname,
++ const char *name,
++ tevent_req_fn fn,
++ struct tevent_req *req);
++static void sdap_initgr_rfc2307bis_update_sysdb_groups(
++ struct tevent_req *subreq);
++static void sdap_initgr_rfc2307bis_get_sysdb_groups(struct tevent_req *subreq)
++{
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_initgr_rfc2307_state *state =
++ tevent_req_data(req, struct sdap_initgr_rfc2307_state);
++ int ret;
++
++ ret = sysdb_transaction_recv(subreq, state, &state->handle);
++ talloc_zfree(subreq);
++ if (ret) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ ret = rfc2307bis_sysdb_member_lookup(
++ state, state->ev, state->sysdb, state->handle, state->dom,
++ SYSDB_MEMBER_USER, state->dom->name, state->name,
++ sdap_initgr_rfc2307bis_update_sysdb_groups, req);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ }
++}
++static errno_t rfc2307bis_sysdb_member_lookup(TALLOC_CTX *mem_ctx,
++ struct tevent_context *ev,
++ struct sysdb_ctx *sysdb,
++ struct sysdb_handle *handle,
++ struct sss_domain_info *dom,
++ enum sysdb_member_type type,
++ const char *domainname,
++ const char *name,
++ tevent_req_fn fn,
++ struct tevent_req *req)
++{
++ errno_t ret;
++ TALLOC_CTX *tmp_ctx;
++ char *member_dn;
++ char *filter;
++ const char **attrs;
++ struct tevent_req *subreq;
++
++ tmp_ctx = talloc_new(mem_ctx);
++ if (!tmp_ctx) {
++ return ENOMEM;
++ }
++
++ attrs = talloc_array(tmp_ctx, const char *, 2);
++ if (!attrs) {
++ ret = ENOMEM;
++ goto error;
++ }
++ attrs[0] = SYSDB_NAME;
++ attrs[1] = NULL;
++
++ if (type == SYSDB_MEMBER_USER) {
++ member_dn = sysdb_user_strdn(tmp_ctx, domainname, name);
++ } else if (type == SYSDB_MEMBER_GROUP) {
++ member_dn = sysdb_group_strdn(tmp_ctx, domainname, name);
++ } else {
++ ret = EINVAL;
++ goto error;
++ }
++
++ if (!member_dn) {
++ ret = ENOMEM;
++ goto error;
++ }
++
++ filter = talloc_asprintf(tmp_ctx, "(member=%s)", member_dn);
++ if (!filter) {
++ ret = ENOMEM;
++ goto error;
++ }
++ talloc_free(member_dn);
++
++ /* Search for all groups for which this user is a direct member */
++ subreq = sysdb_search_groups_send(mem_ctx, ev, sysdb,
++ handle, dom,
++ filter, attrs);
++ if (!subreq) {
++ ret = EIO;
++ goto error;
++ }
++ talloc_steal(subreq, tmp_ctx);
++
++ tevent_req_set_callback(subreq, fn, req);
++ return EOK;
++
++error:
++ talloc_free(tmp_ctx);
++ return ret;
++}
++
++static void sdap_initgr_rfc2307bis_update_sysdb_groups_done(
++ struct tevent_req *subreq);
++static void sdap_initgr_rfc2307bis_update_sysdb_groups(
++ struct tevent_req *subreq)
++{
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_initgr_rfc2307_state *state =
++ tevent_req_data(req, struct sdap_initgr_rfc2307_state);
++ int ret, i;
++ size_t reply_count;
++ struct ldb_message **replies;
++ char **sysdb_grouplist;
++ const char *tmp_str;
++
++ ret = sysdb_search_groups_recv(subreq, state, &reply_count, &replies);
++ talloc_zfree(subreq);
++ if (ret != EOK && ret != ENOENT) {
++ tevent_req_error(req, ret);
++ return;
++ } else if (ret == ENOENT) {
++ reply_count = 0;
++ }
++
++ if (reply_count == 0) {
++ DEBUG(6, ("User [%s] is not a direct member of any groups\n",
++ state->name));
++ sysdb_grouplist = NULL;
++ } else {
++ sysdb_grouplist = talloc_array(state, char *, reply_count+1);
++ if (!sysdb_grouplist) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++
++ for (i = 0; i < reply_count; i++) {
++ tmp_str = ldb_msg_find_attr_as_string(replies[i],
++ SYSDB_NAME,
++ NULL);
++ if (!tmp_str) {
++ /* This should never happen, but if it
++ * does, just skip it.
++ */
++ continue;
++ }
++
++ sysdb_grouplist[i] = talloc_strdup(sysdb_grouplist, tmp_str);
++ if (!sysdb_grouplist[i]) {
++ talloc_free(sysdb_grouplist);
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++ }
++ sysdb_grouplist[reply_count] = NULL;
++ }
++
++ if (state->ldap_groups_count == 0) {
++ state->ldap_grouplist = NULL;
++ }
++ else {
++ ret = sysdb_attrs_to_list(state,
++ state->ldap_groups, state->ldap_groups_count,
++ SYSDB_NAME,
++ &state->ldap_grouplist);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++ }
++
++ /* Find the differences between the sysdb and ldap lists
++ * Groups in ldap only must be added to the sysdb;
++ * groups in the sysdb only must be removed.
++ */
++ ret = diff_string_lists(state,
++ state->ldap_grouplist, sysdb_grouplist,
++ &state->add_groups, &state->del_groups, NULL);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ subreq = sysdb_update_members_send(state, state->ev, state->handle,
++ state->dom, state->name,
++ SYSDB_MEMBER_USER,
++ state->add_groups, state->del_groups);
++ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++
++ tevent_req_set_callback(subreq,
++ sdap_initgr_rfc2307bis_update_sysdb_groups_done,
++ req);
++}
++
++static void sdap_initgr_rfc2307bis_transaction_done(struct tevent_req *subreq);
++static void sdap_initgr_rfc2307bis_update_sysdb_groups_done(
++ struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_initgr_rfc2307_state *state =
++ tevent_req_data(req, struct sdap_initgr_rfc2307_state);
++
++ ret = sysdb_update_members_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /* Commit the transaction */
++ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
++ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++
++ tevent_req_set_callback(subreq,
++ sdap_initgr_rfc2307bis_transaction_done,
++ req);
++}
++
++static void sdap_initgr_rfc2307bis_transaction_done(struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++
++ ret = sysdb_transaction_commit_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /* Processing completed. Return control to sdap_get_initgr_done() */
++ tevent_req_done(req);
++}
++
++static errno_t rfc2307bis_nested_groups_recv(struct tevent_req *req);
++static void sdap_initgr_rfc2307bis_done(struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++
++ ret = rfc2307bis_nested_groups_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /* save the user memberships */
++ ret = save_rfc2307bis_user_memberships(req);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ }
++ return;
++}
++
++struct sdap_rfc2307bis_nested_ctx {
++ struct tevent_context *ev;
++ struct sdap_options *opts;
++ struct sysdb_ctx *sysdb;
++ struct sss_domain_info *dom;
++ struct sdap_handle *sh;
++ struct sysdb_attrs **groups;
++ size_t num_groups;
++
++ size_t group_iter;
++ struct sysdb_attrs **ldap_groups;
++ size_t ldap_groups_count;
++
++ struct sysdb_handle *handle;
++};
++
++static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req);
++struct tevent_req *rfc2307bis_nested_groups_send(
++ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
++ struct sdap_options *opts, struct sysdb_ctx *sysdb,
++ struct sss_domain_info *dom, struct sdap_handle *sh,
++ struct sysdb_attrs **groups, size_t num_groups)
++{
++ errno_t ret;
++ struct tevent_req *req;
++ struct sdap_rfc2307bis_nested_ctx *state;
++
++ req = tevent_req_create(mem_ctx, &state,
++ struct sdap_rfc2307bis_nested_ctx);
++ if (!req) return NULL;
++
++ if (num_groups == 0) {
++ /* No parent groups to process */
++ tevent_req_done(req);
++ tevent_req_post(req, ev);
++ return req;
++ }
++
++ state->ev = ev;
++ state->opts = opts;
++ state->sysdb = sysdb;
++ state->dom = dom;
++ state->sh = sh;
++ state->groups = groups;
++ state->num_groups = num_groups;
++ state->group_iter = 0;
++
++ ret = rfc2307bis_nested_groups_step(req);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ tevent_req_post(req, ev);
++ }
++ return req;
++}
++
++static void rfc2307bis_nested_groups_incomplete_groups_save(
++ struct tevent_req *subreq);
++static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req)
++{
++ errno_t ret;
++ struct tevent_req *subreq;
++ const char *name;
++ struct sdap_rfc2307bis_nested_ctx *state =
++ tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
++
++ if (debug_level >= 6) {
++ ret = sysdb_attrs_get_string(state->groups[state->group_iter],
++ SYSDB_NAME, &name);
++ if (ret != EOK) {
++ return ret;
++ }
++
++ DEBUG(6, ("Processing group [%s]\n", name));
++ }
++
++ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
++ if (!subreq) {
++ return EIO;
++ }
++ tevent_req_set_callback(subreq,
++ rfc2307bis_nested_groups_incomplete_groups_save,
++ req);
++ return EOK;
++}
++
++static void rfc2307bis_nested_groups_incomplete_groups_done(
++ struct tevent_req *subreq);
++static void rfc2307bis_nested_groups_incomplete_groups_save(
++ struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_rfc2307bis_nested_ctx *state =
++ tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
++ const char *name;
++ struct sysdb_attrs **grouplist;
++ char **groupnamelist;
++ TALLOC_CTX *tmp_ctx = NULL;
++
++ ret = sysdb_transaction_recv(subreq, state, &state->handle);
++ if (ret != EOK) goto error;
++
++ tmp_ctx = talloc_new(state);
++ if (!tmp_ctx) {
++ ret = ENOMEM;
++ goto error;
++ }
++
++ /* First, save the group we're processing to the sysdb
++ * sdap_add_incomplete_groups_send will add them if needed
++ */
++ ret = sysdb_attrs_get_string(state->groups[state->group_iter],
++ SYSDB_NAME, &name);
++
++ /* sdap_add_incomplete_groups_send expects a list of groups */
++ grouplist = talloc_array(tmp_ctx, struct sysdb_attrs *, 1);
++ if (!grouplist) {
++ ret = ENOMEM;
++ goto error;
++ }
++ grouplist[0] = state->groups[state->group_iter];
++
++ groupnamelist = talloc_array(tmp_ctx, char *, 2);
++ if (!groupnamelist) {
++ ret = ENOMEM;
++ goto error;
++ }
++ groupnamelist[0] = talloc_strdup(groupnamelist, name);
++ if (!groupnamelist[0]) {
++ ret = ENOMEM;
++ goto error;
++ }
++ groupnamelist[1] = NULL;
++
++ DEBUG(6, ("Saving incomplete group [%s] to the sysdb\n",
++ groupnamelist[0]));
++ subreq = sdap_add_incomplete_groups_send(state, state->ev, state->sysdb,
++ state->handle, state->dom,
++ groupnamelist,
++ grouplist,
++ 1);
++ if (!subreq) {
++ ret = EIO;
++ goto error;
++ }
++
++ talloc_steal(subreq, tmp_ctx);
++ tevent_req_set_callback(subreq,
++ rfc2307bis_nested_groups_incomplete_groups_done,
++ req);
++ return;
++
++error:
++ talloc_free(tmp_ctx);
++ tevent_req_error(req, ret);
++}
++
++static void rfc2307bis_nested_groups_get_parents(struct tevent_req *subreq);
++static void rfc2307bis_nested_groups_incomplete_groups_done(
++ struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_rfc2307bis_nested_ctx *state =
++ tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
++
++ ret = sdap_add_incomplete_groups_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ DEBUG(7, ("sdap_add_incomplete_groups failed [%d][%s]\n",
++ ret, strerror(ret)));
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
++ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++ tevent_req_set_callback(subreq,
++ rfc2307bis_nested_groups_get_parents,
++ req);
++}
++
++static void rfc2307bis_nested_groups_process(struct tevent_req *subreq);
++static void rfc2307bis_nested_groups_get_parents(struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_rfc2307bis_nested_ctx *state =
++ tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
++ char *filter;
++ const char *orig_dn;
++ const char **attrs;
++ TALLOC_CTX *tmp_ctx;
++
++ ret = sysdb_transaction_commit_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ DEBUG(7, ("sysdb transaction failed [%d][%s]\n",
++ ret, strerror(ret)));
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /* Get any parent groups for this group */
++
++ tmp_ctx = talloc_new(state);
++ if (!tmp_ctx) {
++ ret = ENOMEM;
++ goto error;
++ }
++
++ ret = sysdb_attrs_get_string(state->groups[state->group_iter],
++ SYSDB_ORIG_DN,
++ &orig_dn);
++ if (ret != EOK) {
++ goto error;
++ }
++
++ ret = build_attrs_from_map(tmp_ctx, state->opts->group_map,
++ SDAP_OPTS_GROUP, &attrs);
++ if (ret != EOK) {
++ goto error;
++ }
++
++ filter = talloc_asprintf(
++ tmp_ctx, "(&(%s=%s)(objectclass=%s))",
++ state->opts->group_map[SDAP_AT_GROUP_MEMBER].name,
++ orig_dn,
++ state->opts->group_map[SDAP_OC_GROUP].name);
++ if (!filter) {
++ ret = ENOMEM;
++ goto error;
++ }
++
++ DEBUG(6, ("Looking up parent groups for group [%s]\n", orig_dn));
++ subreq = sdap_get_generic_send(state, state->ev, state->opts,
++ state->sh,
++ dp_opt_get_string(state->opts->basic,
++ SDAP_GROUP_SEARCH_BASE),
++ LDAP_SCOPE_SUBTREE,
++ filter, attrs,
++ state->opts->group_map, SDAP_OPTS_GROUP);
++ if (!subreq) {
++ ret = EIO;
++ goto error;
++ }
++ talloc_steal(subreq, tmp_ctx);
++ tevent_req_set_callback(subreq,
++ rfc2307bis_nested_groups_process,
++ req);
++
++ return;
++
++error:
++ talloc_free(tmp_ctx);
++ tevent_req_error(req, ret);
++}
++
++static errno_t rfc2307bis_nested_groups_update_sysdb(struct tevent_req *req);
++static void rfc2307bis_nested_groups_done(struct tevent_req *subreq);
++static void rfc2307bis_nested_groups_process(struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_rfc2307bis_nested_ctx *state =
++ tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
++
++ ret = sdap_get_generic_recv(subreq, state,
++ &state->ldap_groups_count,
++ &state->ldap_groups);
++ talloc_zfree(subreq);
++ if (ret) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ if (state->ldap_groups_count == 0) {
++ /* No groups for this user in LDAP
++ * We need to ensure that there are no groups
++ * in the sysdb either.
++ */
++
++ ret = rfc2307bis_nested_groups_update_sysdb(req);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ }
++ return;
++ }
++
++ /* Otherwise, recurse into the groups */
++ subreq = rfc2307bis_nested_groups_send(
++ state, state->ev, state->opts, state->sysdb,
++ state->dom, state->sh,
++ state->ldap_groups, state->ldap_groups_count);
++ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++ tevent_req_set_callback(subreq, rfc2307bis_nested_groups_done, req);
++}
++
++static errno_t rfc2307bis_nested_groups_recv(struct tevent_req *req)
++{
++ TEVENT_REQ_RETURN_ON_ERROR(req);
++ return EOK;
++}
++
++static void rfc2307bis_nested_groups_done(struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++
++ ret = rfc2307bis_nested_groups_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ DEBUG(6, ("rfc2307bis_nested failed [%d][%s]\n",
++ ret, strerror(ret)));
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /* All of the parent groups have been added
++ * Now add the memberships
++ */
++
++ ret = rfc2307bis_nested_groups_update_sysdb(req);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ }
++ return;
++}
++
++static void rfc2307bis_get_sysdb_groups(struct tevent_req *subreq);
++static errno_t rfc2307bis_nested_groups_update_sysdb(struct tevent_req *req)
++{
++ struct tevent_req *subreq;
++ struct sdap_rfc2307bis_nested_ctx *state =
++ tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
++
++ /* Start a transaction to look up the groups in the sysdb
++ * and update them with LDAP data
++ */
++
++ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
++ if (!subreq) {
++ return EIO;
++ }
++ tevent_req_set_callback(subreq,
++ rfc2307bis_get_sysdb_groups,
++ req);
++ return EOK;
++}
++
++static void rfc2307bis_update_sysdb_groups(struct tevent_req *subreq);
++static void rfc2307bis_get_sysdb_groups(struct tevent_req *subreq)
++{
++ errno_t ret;
++ const char *name;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_rfc2307bis_nested_ctx *state =
++ tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
++
++ ret = sysdb_transaction_recv(subreq, state, &state->handle);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ ret = sysdb_attrs_get_string(state->groups[state->group_iter],
++ SYSDB_NAME, &name);
++
++ ret = rfc2307bis_sysdb_member_lookup(
++ state, state->ev, state->sysdb, state->handle, state->dom,
++ SYSDB_MEMBER_GROUP, state->dom->name, name,
++ rfc2307bis_update_sysdb_groups, req);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ }
++}
++
++static void rfc2307bis_update_sysdb_groups_process(struct tevent_req *req);
++static void rfc2307bis_update_sysdb_groups(struct tevent_req *subreq)
++{
++ errno_t ret;
++ unsigned int i;
++ size_t reply_count;
++ struct ldb_message **replies;
++ const char *name;
++ TALLOC_CTX *tmp_ctx;
++ const char *tmp_str;
++ char **sysdb_grouplist;
++ char **ldap_grouplist;
++ char **add_groups;
++ char **del_groups;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_rfc2307bis_nested_ctx *state =
++ tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
++
++ ret = sysdb_search_groups_recv(subreq, state, &reply_count, &replies);
++ talloc_zfree(subreq);
++ if (ret != EOK && ret != ENOENT) {
++ tevent_req_error(req, ret);
++ return;
++ } else if (ret == ENOENT) {
++ reply_count = 0;
++ }
++
++ tmp_ctx = talloc_new(state);
++ if (!tmp_ctx) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++
++ ret = sysdb_attrs_get_string(state->groups[state->group_iter],
++ SYSDB_NAME, &name);
++ if (ret != EOK) goto error;
++
++ /* Create a list of the groups in the sysdb */
++ if (reply_count == 0) {
++ DEBUG(6, ("Group [%s] is not a direct member of any groups\n",
++ name));
++ sysdb_grouplist = NULL;
++ } else {
++ sysdb_grouplist = talloc_array(state, char *, reply_count+1);
++ if (!sysdb_grouplist) {
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++
++ for (i = 0; i < reply_count; i++) {
++ tmp_str = ldb_msg_find_attr_as_string(replies[i],
++ SYSDB_NAME,
++ NULL);
++ if (!tmp_str) {
++ /* This should never happen, but if it
++ * does, just skip it.
++ */
++ continue;
++ }
++
++ sysdb_grouplist[i] = talloc_strdup(sysdb_grouplist, tmp_str);
++ if (!sysdb_grouplist[i]) {
++ talloc_free(sysdb_grouplist);
++ tevent_req_error(req, ENOMEM);
++ return;
++ }
++ }
++ sysdb_grouplist[reply_count] = NULL;
++ }
++
++ /* Create a list of the groups in LDAP */
++ if (state->ldap_groups_count == 0) {
++ /* No groups for this user in LDAP
++ * We need to ensure that there are no groups
++ * in the sysdb either.
++ */
++
++ ldap_grouplist = NULL;
++ }
++ else {
++ ret = sysdb_attrs_to_list(tmp_ctx,
++ state->ldap_groups,
++ state->ldap_groups_count,
++ SYSDB_NAME,
++ &ldap_grouplist);
++ if (ret != EOK) {
++ goto error;
++ }
++ }
++
++ /* Find the differences between the sysdb and ldap lists
++ * Groups in ldap only must be added to the sysdb;
++ * groups in the sysdb only must be removed.
++ */
++ ret = diff_string_lists(tmp_ctx,
++ ldap_grouplist, sysdb_grouplist,
++ &add_groups, &del_groups, NULL);
++ if (ret != EOK) {
++ goto error;
++ }
++
++ /* Since all of the groups were created by nested calls,
++ * we don't need to step through sdap_add_incomplete_groups_send
++ * here like we do in sdap_initgr_rfc2307_update_sysdb_groups
++ *
++ * We can just update the memberships and move on
++ */
++ subreq = sysdb_update_members_send(state, state->ev, state->handle,
++ state->dom, name,
++ SYSDB_MEMBER_GROUP,
++ add_groups, del_groups);
++ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++ talloc_steal(subreq, tmp_ctx);
++
++ tevent_req_set_callback(subreq,
++ rfc2307bis_update_sysdb_groups_process,
++ req);
++
++ return;
++
++error:
++ talloc_free(tmp_ctx);
++ tevent_req_error(req, ret);
++}
++static void rfc2307bis_update_sysdb_groups_done(struct tevent_req *req);
++static void rfc2307bis_update_sysdb_groups_process(struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_rfc2307bis_nested_ctx *state =
++ tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
++
++ ret = sysdb_update_members_recv(subreq);
++ talloc_zfree(subreq);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /* Processing of this group is finished */
++ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
++ if (!subreq) {
++ tevent_req_error(req, EIO);
++ return;
++ }
++ tevent_req_set_callback(subreq,
++ rfc2307bis_update_sysdb_groups_done,
++ req);
++}
++
++static void rfc2307bis_update_sysdb_groups_done(struct tevent_req *subreq)
++{
++ errno_t ret;
++ struct tevent_req *req =
++ tevent_req_callback_data(subreq, struct tevent_req);
++ struct sdap_rfc2307bis_nested_ctx *state =
++ tevent_req_data(req, struct sdap_rfc2307bis_nested_ctx);
++
++ ret = sysdb_transaction_commit_recv(subreq);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ return;
++ }
++
++ /* Continue processing groups at this level */
++ state->group_iter++;
++ if (state->group_iter < state->num_groups) {
++ ret = rfc2307bis_nested_groups_step(req);
++ if (ret != EOK) {
++ tevent_req_error(req, ret);
++ }
++ return;
++ }
++
++ /* All done in this nesting level */
++ tevent_req_done(req);
++}
++
++static int sdap_initgr_rfc2307bis_recv(struct tevent_req *req)
++{
++ TEVENT_REQ_RETURN_ON_ERROR(req);
++ return EOK;
++}
++
+diff --git a/src/responder/nss/nsssrv_cmd.c b/src/responder/nss/nsssrv_cmd.c
+index 45334e23c7ff6bdecdf075ada9539f182ff43086..7a8645385cb7e056106ee3de8c5e7292f11f7bb7 100644
+--- a/src/responder/nss/nsssrv_cmd.c
++++ b/src/responder/nss/nsssrv_cmd.c
+@@ -159,7 +159,7 @@ static int fill_pwent(struct sss_packet *packet,
+ gid = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
+
+ if (!name || !uid || !gid) {
+- DEBUG(1, ("Incomplete user object for %s[%llu]! Skipping\n",
++ DEBUG(1, ("Incomplete or fake user object for %s[%llu]! Skipping\n",
+ name?name:"<NULL>", (unsigned long long int)uid));
+ continue;
+ }
+diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
+index 5bdc005473041243b254461dc3931ee30ba0afd0..24347014bb34a52c6e4b61f23df7d47e39d62f6d 100644
+--- a/src/tests/sysdb-tests.c
++++ b/src/tests/sysdb-tests.c
+@@ -511,6 +511,39 @@ static void test_store_group_done(struct tevent_req *subreq)
+ return test_return(data, ret);
+ }
+
++static void test_add_incomplete_group_done(struct tevent_req *subreq);
++
++static void test_add_incomplete_group(struct tevent_req *req)
++{
++ struct test_data *data = tevent_req_callback_data(req, struct test_data);
++ struct tevent_req *subreq;
++ int ret;
++
++ ret = sysdb_transaction_recv(req, data, &data->handle);
++ if (ret != EOK) {
++ return test_return(data, ret);
++ }
++
++ subreq = sysdb_add_incomplete_group_send(data, data->ev, data->handle,
++ data->ctx->domain, data->groupname,
++ data->gid);
++ if (!subreq) {
++ test_return(data, ret);
++ }
++ tevent_req_set_callback(subreq, test_add_incomplete_group_done, data);
++}
++
++static void test_add_incomplete_group_done(struct tevent_req *subreq)
++{
++ struct test_data *data = tevent_req_callback_data(subreq, struct test_data);
++ int ret;
++
++ ret = sysdb_add_group_recv(subreq);
++ talloc_zfree(subreq);
++
++ return test_return(data, ret);
++}
++
+ static void test_remove_group_done(struct tevent_req *subreq);
+
+ static void test_remove_group(struct tevent_req *req)
+@@ -798,7 +831,8 @@ static void test_add_group_member(struct tevent_req *req)
+
+ subreq = sysdb_add_group_member_send(data, data->ev,
+ data->handle, data->ctx->domain,
+- data->groupname, username);
++ data->groupname, username,
++ SYSDB_MEMBER_USER);
+ if (!subreq) {
+ test_return(data, ENOMEM);
+ }
+@@ -836,7 +870,8 @@ static void test_remove_group_member(struct tevent_req *req)
+
+ subreq = sysdb_remove_group_member_send(data, data->ev,
+ data->handle, data->ctx->domain,
+- data->groupname, username);
++ data->groupname, username,
++ SYSDB_MEMBER_USER);
+ if (!subreq) {
+ test_return(data, ENOMEM);
+ }
+@@ -1179,6 +1214,41 @@ START_TEST (test_sysdb_store_group)
+ }
+ END_TEST
+
++START_TEST (test_sysdb_add_incomplete_group)
++{
++ struct sysdb_test_ctx *test_ctx;
++ struct test_data *data;
++ struct tevent_req *req;
++ int ret;
++
++ /* Setup */
++ ret = setup_sysdb_tests(&test_ctx);
++ if (ret != EOK) {
++ fail("Could not set up the test");
++ return;
++ }
++
++ data = talloc_zero(test_ctx, struct test_data);
++ data->ctx = test_ctx;
++ data->ev = test_ctx->ev;
++ data->gid = _i;
++ data->groupname = talloc_asprintf(data, "testgroup%d", _i);
++
++ req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb);
++ if (!req) {
++ ret = ENOMEM;
++ }
++
++ if (ret == EOK) {
++ tevent_req_set_callback(req, test_add_incomplete_group, data);
++ ret = test_loop(data);
++ }
++
++ fail_if(ret != EOK, "Could not store incomplete group #%d", _i);
++ talloc_free(test_ctx);
++}
++END_TEST
++
+ START_TEST (test_sysdb_remove_local_user)
+ {
+ struct sysdb_test_ctx *test_ctx;
+@@ -3215,6 +3285,7 @@ static void test_sysdb_update_members_add(struct tevent_req *req)
+
+ req = sysdb_update_members_send(data, data->ev, data->handle,
+ data->ctx->domain, user,
++ SYSDB_MEMBER_USER,
+ add_groups, NULL);
+ talloc_free(add_groups);
+ talloc_free(user);
+@@ -3255,6 +3326,7 @@ static void test_sysdb_update_members_add_del(struct tevent_req *req)
+
+ req = sysdb_update_members_send(data, data->ev, data->handle,
+ data->ctx->domain, user,
++ SYSDB_MEMBER_USER,
+ add_groups, del_groups);
+ talloc_free(add_groups);
+ talloc_free(del_groups);
+@@ -3293,6 +3365,7 @@ static void test_sysdb_update_members_del(struct tevent_req *req)
+
+ req = sysdb_update_members_send(data, data->ev, data->handle,
+ data->ctx->domain, user,
++ SYSDB_MEMBER_USER,
+ NULL, del_groups);
+ talloc_free(del_groups);
+ talloc_free(user);
+@@ -3372,6 +3445,10 @@ Suite *create_sysdb_suite(void)
+ /* Create a new group */
+ tcase_add_loop_test(tc_sysdb, test_sysdb_store_group, 28010, 28020);
+
++ /* Create and remove a incomplete group */
++ tcase_add_loop_test(tc_sysdb, test_sysdb_add_incomplete_group, 28020, 28030);
++ tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group, 28020, 28030);
++
+ /* Verify the groups were added */
+
+ /* Verify the groups can be queried by GID */
+diff --git a/src/util/util.c b/src/util/util.c
+index 10bfb647b898c2c3696bcc7dbb6559585c66bd8a..06eea283770601997e3ff90df73db58a4fb25bba 100644
+--- a/src/util/util.c
++++ b/src/util/util.c
+@@ -462,3 +462,56 @@ done:
+ talloc_free(tmp_ctx);
+ return ret;
+ }
++
++static void *hash_talloc(const size_t size, void *pvt)
++{
++ return talloc_size(pvt, size);
++}
++
++static void hash_talloc_free(void *ptr, void *pvt)
++{
++ talloc_free(ptr);
++}
++
++errno_t sss_hash_create(TALLOC_CTX *mem_ctx,
++ unsigned long count,
++ hash_table_t **tbl)
++{
++ errno_t ret;
++ hash_table_t *table;
++ int hret;
++
++ TALLOC_CTX *internal_ctx;
++ internal_ctx = talloc_new(NULL);
++ if (!internal_ctx) {
++ return ENOMEM;
++ }
++
++ hret = hash_create_ex(count, &table, 0, 0, 0, 0,
++ hash_talloc, hash_talloc_free,
++ internal_ctx, NULL, NULL);
++ switch (hret) {
++ case HASH_SUCCESS:
++ /* Steal the table pointer onto the mem_ctx,
++ * then make the internal_ctx a child of
++ * table.
++ *
++ * This way, we can clean up the values when
++ * we talloc_free() the table
++ */
++ *tbl = talloc_steal(mem_ctx, table);
++ talloc_steal(table, internal_ctx);
++ return EOK;
++
++ case HASH_ERROR_NO_MEMORY:
++ ret = ENOMEM;
++ default:
++ ret = EIO;
++ }
++
++ DEBUG(0, ("Could not create hash table: [%d][%s]\n",
++ hret, hash_error_string(hret)));
++
++ talloc_free(internal_ctx);
++ return ret;
++}
+diff --git a/src/util/util.h b/src/util/util.h
+index 6bcc9984db06592e3c9092842feab5f5fdbcadbf..7fcca3a6db046bc69ae901282700d8966f1c208e 100644
+--- a/src/util/util.h
++++ b/src/util/util.h
+@@ -40,6 +40,7 @@
+ #include <talloc.h>
+ #include <tevent.h>
+ #include <ldb.h>
++#include <dhash.h>
+
+ #ifndef HAVE_ERRNO_T
+ #define HAVE_ERRNO_T
+@@ -332,6 +333,9 @@ int split_on_separator(TALLOC_CTX *mem_ctx, const char *str,
+
+ char **parse_args(const char *str);
+
++errno_t sss_hash_create(TALLOC_CTX *mem_ctx,
++ unsigned long count,
++ hash_table_t **tbl);
+
+ /* Copy a NULL-terminated string list
+ * Returns NULL on out of memory error or invalid input
+--
+1.7.2.3
+
diff --git a/sssd.spec b/sssd.spec
index e27896c..dc82afa 100644
--- a/sssd.spec
+++ b/sssd.spec
@@ -7,7 +7,7 @@ Name: sssd
Version: 1.2.1
#Never reset the Release, always increment it
#Otherwise we can have issues if library versions do not change
-Release: 27%{?dist}
+Release: 28%{?dist}.0.1
Group: Applications/System
Summary: System Security Services Daemon
License: GPLv3+
@@ -31,20 +31,23 @@ Patch0006: 0006-Allow-sssd-clients-to-reconnect.patch
Patch0007: 0007-Clean-up-initgroups-processing-for-RFC2307.patch
Patch0008: 0008-Fix-chpass-operations-with-LDAP-provider.patch
Patch0009: 0009-Treat-a-zero-length-password-as-a-failure.patch
+Patch0010: 0010-Disable-events-on-ldap-fd-when-offline.patch
+Patch0011: 0011-Remove-timestamp-from-doxygen-footers.patch
+Patch0012: 0012-Assorted-fixes-for-group-processing.patch
### Dependencies ###
Requires: libldb >= 0.9.3
Requires: libtdb >= 1.1.3
Requires: sssd-client = %{version}-%{release}
-Requires: libdhash = %{dhash_version}
-Requires: libcollection = %{collection_version}
-Requires: libini_config = %{ini_config_version}
+Requires: libdhash = %{dhash_version}-%{release}
+Requires: libcollection = %{collection_version}-%{release}
+Requires: libini_config = %{ini_config_version}-%{release}
Requires: cyrus-sasl-gssapi
Requires: keyutils-libs
-Requires(post): python
+Requires(post): initscripts chkconfig /sbin/ldconfig
Requires(preun): initscripts chkconfig
-Requires(postun): /sbin/service
+Requires(postun): initscripts chkconfig /sbin/ldconfig
%global servicename sssd
%global sssdstatedir %{_localstatedir}/lib/sss
@@ -171,7 +174,7 @@ and serialization
Summary: INI file parser for C
Group: Development/Libraries
Version: %{ini_config_version}
-Requires: libcollection = %{collection_version}
+Requires: libcollection = %{collection_version}-%{release}
License: LGPLv3+
%description -n libini_config
@@ -183,6 +186,7 @@ Summary: Development files for libini_config
Group: Development/Libraries
Version: %{ini_config_version}
Requires: libini_config = %{ini_config_version}-%{release}
+Requires: libcollection-devel = %{collection_version}-%{release}
License: LGPLv3+
%description -n libini_config-devel
@@ -219,6 +223,9 @@ A dynamically-growing, reference-counted array
%patch0007 -p1
%patch0008 -p1
%patch0009 -p1
+%patch0010 -p1
+%patch0011 -p1
+%patch0012 -p1
# RHEL 5 uses an old libtool, so we need to reconfigure
for i in libtool.m4 lt~obsolete.m4 ltoptions.m4 ltsugar.m4 ltversion.m4
@@ -234,15 +241,12 @@ autoreconf -ivf
--with-pubconf-path=%{pubconfpath} \
--with-init-dir=%{_initrddir} \
--enable-nsslibdir=/%{_lib} \
+ --with-test-dir=/dev/shm \
--disable-static \
--disable-rpath \
make %{?_smp_mflags}
-pushd common
-make %{?_smp_mflags} docs
-popd
-
%check
export CK_TIMEOUT_MULTIPLIER=10
make %{?_smp_mflags} check
@@ -296,12 +300,6 @@ rm -f \
$RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.la \
$RPM_BUILD_ROOT/%{python_sitearch}/pysss.la
-if test -e $RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.so
-then
- # Apppend this file to the sss_daemon.lang
- # Older versions of rpmbuild can only handle one -f option
- echo %{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.so >> sss_daemon.lang
-fi
for file in `ls $RPM_BUILD_ROOT/%{python_sitelib}/*.egg-info 2> /dev/null`
do
echo %{python_sitelib}/`basename $file` >> sss_daemon.lang
@@ -351,7 +349,6 @@ rm -rf $RPM_BUILD_ROOT
%{_mandir}/man8/sss_useradd.8*
%{_mandir}/man8/sss_userdel.8*
%{_mandir}/man8/sss_usermod.8*
-%{_mandir}/man8/sssd_krb5_locator_plugin.8*
%{python_sitearch}/pysss.so
%{python_sitelib}/*.py*
@@ -361,7 +358,9 @@ rm -rf $RPM_BUILD_ROOT
%doc src/sss_client/COPYING src/sss_client/COPYING.LESSER
/%{_lib}/libnss_sss.so.2
/%{_lib}/security/pam_sss.so
+%{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.so
%{_mandir}/man8/pam_sss.8*
+%{_mandir}/man8/sssd_krb5_locator_plugin.8*
%files -n libdhash
%defattr(-,root,root,-)
@@ -391,7 +390,6 @@ rm -rf $RPM_BUILD_ROOT
%{_libdir}/libpath_utils.so
%{_libdir}/pkgconfig/path_utils.pc
%doc common/path_utils/README
-%doc common/path_utils/doc/html/
%files -n libcollection
%defattr(-,root,root,-)
@@ -408,7 +406,6 @@ rm -rf $RPM_BUILD_ROOT
%{_includedir}/collection_stack.h
%{_libdir}/libcollection.so
%{_libdir}/pkgconfig/collection.pc
-%doc common/collection/doc/html/
%files -n libini_config
%defattr(-,root,root,-)
@@ -422,7 +419,6 @@ rm -rf $RPM_BUILD_ROOT
%{_includedir}/ini_config.h
%{_libdir}/libini_config.so
%{_libdir}/pkgconfig/ini_config.pc
-%doc common/ini/doc/html/
%files -n libref_array
%defattr(-,root,root,-)
@@ -437,16 +433,11 @@ rm -rf $RPM_BUILD_ROOT
%{_libdir}/libref_array.so
%{_libdir}/pkgconfig/ref_array.pc
%doc common/refarray/README
-%doc common/refarray/doc/html/
%post
/sbin/ldconfig
/sbin/chkconfig --add %{servicename}
-if [ $1 -ge 2 ] ; then
-# a one-time upgrade from confdb v1 to v2, only if upgrading
- python %{_libexecdir}/%{servicename}/upgrade_config.py
-fi
if [ $1 -ge 1 ] ; then
/sbin/service %{servicename} condrestart 2>&1 > /dev/null
@@ -458,11 +449,7 @@ if [ $1 = 0 ]; then
/sbin/chkconfig --del %{servicename}
fi
-%postun
-/sbin/ldconfig
-if [ $1 -ge 1 ] ; then
- /sbin/service %{servicename} condrestart 2>&1 > /dev/null
-fi
+%postun -p /sbin/ldconfig
%post client -p /sbin/ldconfig
@@ -485,9 +472,20 @@ fi
%postun -n libref_array -p /sbin/ldconfig
%changelog
+* Tue Oct 19 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-28.0.1
+- Resolves: rhbz#636055 - SSSD initgroups does not behave as expected
+- Resolves: rhbz#634918 - multilib conflicts in libpath_utils-devel and
+- libini_config-devel
+- Resolves: rhbz#633863 - SSSD log fills up the disk
+- Resolves: rhbz#636823 - the krb5 locator plugin isn't packaged for multilib
+- Resolves: rhbz#638241 - sssd stops on upgrade
+- Resolves: rhbz#634861 - error: %%post(sssd-1.2.1-28.1.el5.s390x) scriptlet
+- failed, exit status 127
+
* Tue Aug 24 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-27
- Resolves: CVE-2010-2940 - sssd allows null password entry to authenticate
- against LDAP
+
* Wed Aug 04 2010 Stephen Gallagher <sgallagh at redhat.com> - 1.2.1-26
- Resolves: rhbz#621307 - Password changes are broken on LDAP
More information about the scm-commits
mailing list