Hi,
the attached patches implement https://fedorahosted.org/sssd/ticket/2522
Here is what I tested:
1) topgr -> bottomgr -> extgr -> [email protected]
- this is a simple case to test a member of an external group
2) ipa_uni -> ipa_uni_ext -> [email protected]
|
+-- [email protected]
\- [email protected]
- this is to test that a universal group with members from two
domains resolves correctly
3) idm_admin -> idm_admin_ext -> [email protected]
\- [email protected]
-> [email protected]
- tests that a combination of user and group members work
4) toc_admin -> toc_admin_ext -> [email protected]
-> [email protected]
- tests that two users with the same name from different domains
work correctly
I also tested adding and removing group members and found some bugs in
our groups processing (#2940, #2952)
Please feel free to suggest more tests. If the patches are accepted,
I'll also add unit tests for the nested groups code, but currently we
need to make the patches available to upstream.
I also tested a combination of user lookups and group lookups,
especially with someuser to test that the initgroups and group lookups
play well together. And that's where I found one issue that I think we
should fix in master -- the initgroups code adds the external members as
direct members of the POSIX group, so I did the same in the group
lookup code. This is safer, but doesn't allow us to remove the hack we
have (6fac5e5f0c54a0f92872ce1450606cfcb577a920) to retain external
members.
I think in 1.14 it would be more systematic to store the group
membership as:
gr -> ext_gr -> ad_member
rather than:
gr -> ext_gr
-> ad_member
The reason we can't remove those hacks is that if the external group is
up-to-date and only the top group is resolved, we would only write the
group memberships as stored in LDAP, effectively removing the AD
memberships. This of course depends on fixing the issues in memberof
plugin, but I still think it's worth it. The current code works well,
though, so I guess we can accept it for 1.13.
Our initgroups code should be well equipped to handle that and we could
remove the hacks we have at the moment.
>From 18c99e7d59cc231d2012f77e857bd93b50bd98c8 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <[email protected]>
Date: Mon, 25 Jan 2016 16:03:23 +0100
Subject: [PATCH 1/3] Add a new option ldap_group_external_member
Required for:
https://fedorahosted.org/sssd/ticket/2522
---
src/config/SSSDConfig/__init__.py.in | 1 +
src/config/etc/sssd.api.d/sssd-ad.conf | 1 +
src/config/etc/sssd.api.d/sssd-ipa.conf | 1 +
src/config/etc/sssd.api.d/sssd-ldap.conf | 1 +
src/db/sysdb.h | 1 +
src/man/sssd-ldap.5.xml | 16 ++++++++++++++++
src/providers/ad/ad_opts.c | 1 +
src/providers/ipa/ipa_opts.c | 1 +
src/providers/ldap/ldap_opts.c | 3 +++
src/providers/ldap/sdap.h | 1 +
10 files changed, 27 insertions(+)
diff --git a/src/config/SSSDConfig/__init__.py.in
b/src/config/SSSDConfig/__init__.py.in
index
495cb650ee86e50031962a4fcf0c21aa79dc0142..65e5e655d2eb25c3366bb9f37b9542902fa6302e
100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -334,6 +334,7 @@ option_strings = {
'ldap_group_objectsid' : _("objectSID attribute"),
'ldap_group_modify_timestamp' : _('Modification time attribute for
groups'),
'ldap_group_type' : _('Type of the group and other flags'),
+ 'ldap_group_external_member' : _('The LDAP group external member
attribute'),
#replaced by ldap_entry_usn# 'ldap_group_entry_usn' : _('entryUSN
attribute'),
'ldap_group_nesting_level' : _('Maximum nesting level SSSd will follow'),
diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf
b/src/config/etc/sssd.api.d/sssd-ad.conf
index
149590f4f30de3438f2fc5534ae65c98ee0f10ad..23006d26ca6fe7ca2b912ef091b4c73d5d23bee1
100644
--- a/src/config/etc/sssd.api.d/sssd-ad.conf
+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
@@ -110,6 +110,7 @@ ldap_group_objectsid = str, None, false
ldap_group_modify_timestamp = str, None, false
ldap_group_entry_usn = str, None, false
ldap_group_type = int, None, false
+ldap_group_external_member = str, None, false
ldap_force_upper_case_realm = bool, None, false
ldap_group_nesting_level = int, None, false
ldap_netgroup_search_base = str, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ipa.conf
b/src/config/etc/sssd.api.d/sssd-ipa.conf
index
822599db6390ad2244a71db770c0b162345a3321..8cd20c0c621a513ca7bc85be6908de41d024b148
100644
--- a/src/config/etc/sssd.api.d/sssd-ipa.conf
+++ b/src/config/etc/sssd.api.d/sssd-ipa.conf
@@ -104,6 +104,7 @@ ldap_group_objectsid = str, None, false
ldap_group_modify_timestamp = str, None, false
ldap_group_entry_usn = str, None, false
ldap_group_type = int, None, false
+ldap_group_external_member = str, None, false
ldap_force_upper_case_realm = bool, None, false
ldap_group_nesting_level = int, None, false
ldap_netgroup_search_base = str, None, false
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf
b/src/config/etc/sssd.api.d/sssd-ldap.conf
index
fc9fcefce94891760a3f3ada4c044dbcaf156945..8b52f268af195bc68d45389cda52a0ad0aba1aa3
100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -98,6 +98,7 @@ ldap_group_objectsid = str, None, false
ldap_group_modify_timestamp = str, None, false
ldap_group_entry_usn = str, None, false
ldap_group_type = int, None, false
+ldap_group_external_member = str, None, false
ldap_group_nesting_level = int, None, false
ldap_force_upper_case_realm = bool, None, false
ldap_netgroup_search_base = str, None, false
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index
2e797fd7fa39163c2ab6a10e51228e0f1af3f9e3..95a9086766228a6c36c56d3a68a0bb0e493c0cbe
100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -81,6 +81,7 @@
#define SYSDB_USER_CATEGORY "userCategory"
#define SYSDB_HOST_CATEGORY "hostCategory"
#define SYSDB_GROUP_TYPE "groupType"
+#define SYSDB_EXTERNAL_MEMBER "externalMember"
#define SYSDB_GECOS "gecos"
#define SYSDB_LAST_LOGIN "lastLogin"
diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index
66b9024bcdc6faced67c4e44f9cde7caa9a5ecc8..a30100408c6e77f9156878cb6ff63dfbf7b041d1
100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -942,6 +942,22 @@
</varlistentry>
<varlistentry>
+ <term>ldap_group_external_member (string)</term>
+ <listitem>
+ <para>
+ The LDAP attribute that references group
+ members that are defined in an external
+ domain. At the moment, only IPA's external
+ members are supported.
+ </para>
+ <para>
+ Default: ipaExternalMember in the IPA provider,
+ otherwise unset.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>ldap_group_nesting_level (integer)</term>
<listitem>
<para>
diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c
index
28d4768b20bd035f7c1971c95f9b6b690844816e..15024adb7959de9e16cdc92ca30daa74bb5f648d
100644
--- a/src/providers/ad/ad_opts.c
+++ b/src/providers/ad/ad_opts.c
@@ -233,6 +233,7 @@ struct sdap_attr_map ad_2008r2_group_map[] = {
{ "ldap_group_modify_timestamp", "whenChanged", SYSDB_ORIG_MODSTAMP, NULL
},
{ "ldap_group_entry_usn", SDAP_AD_USN, SYSDB_USN, NULL },
{ "ldap_group_type", "groupType", SYSDB_GROUP_TYPE, NULL },
+ { "ldap_group_external_member", NULL, SYSDB_EXTERNAL_MEMBER, NULL },
SDAP_ATTR_MAP_TERMINATOR
};
diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c
index
cd87852e5891fd43d7ec728f76860f3050a54d2f..fe469852b527ad872502b3346c8c11ef9eea3bcd
100644
--- a/src/providers/ipa/ipa_opts.c
+++ b/src/providers/ipa/ipa_opts.c
@@ -219,6 +219,7 @@ struct sdap_attr_map ipa_group_map[] = {
{ "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP,
NULL },
{ "ldap_group_entry_usn", NULL, SYSDB_USN, NULL },
{ "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
+ { "ldap_group_external_member", "ipaExternalMember",
SYSDB_EXTERNAL_MEMBER, NULL },
SDAP_ATTR_MAP_TERMINATOR
};
diff --git a/src/providers/ldap/ldap_opts.c b/src/providers/ldap/ldap_opts.c
index
84ba2b54271bcb6650e0336131ace8bfc1a40fc8..ff9bf0d8b6d4a8f677e08219e5105e3750b7a4a8
100644
--- a/src/providers/ldap/ldap_opts.c
+++ b/src/providers/ldap/ldap_opts.c
@@ -195,6 +195,7 @@ struct sdap_attr_map rfc2307_group_map[] = {
{ "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP,
NULL },
{ "ldap_group_entry_usn", NULL, SYSDB_USN, NULL },
{ "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
+ { "ldap_group_external_member", NULL, SYSDB_EXTERNAL_MEMBER, NULL },
SDAP_ATTR_MAP_TERMINATOR
};
@@ -251,6 +252,7 @@ struct sdap_attr_map rfc2307bis_group_map[] = {
{ "ldap_group_modify_timestamp", "modifyTimestamp", SYSDB_ORIG_MODSTAMP,
NULL },
{ "ldap_group_entry_usn", NULL, SYSDB_USN, NULL },
{ "ldap_group_type", NULL, SYSDB_GROUP_TYPE, NULL },
+ { "ldap_group_external_member", NULL, SYSDB_EXTERNAL_MEMBER, NULL },
SDAP_ATTR_MAP_TERMINATOR
};
@@ -307,6 +309,7 @@ struct sdap_attr_map gen_ad2008r2_group_map[] = {
{ "ldap_group_modify_timestamp", "whenChanged", SYSDB_ORIG_MODSTAMP, NULL
},
{ "ldap_group_entry_usn", SDAP_AD_USN, SYSDB_USN, NULL },
{ "ldap_group_type", "groupType", SYSDB_GROUP_TYPE, NULL },
+ { "ldap_group_external_member", NULL, SYSDB_EXTERNAL_MEMBER, NULL },
SDAP_ATTR_MAP_TERMINATOR
};
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index
e06f2b6ac47990f21985fb86f8ad3f3ae5a74df3..9dc2e16a0da76246a1f4492cf70e9124edba4a31
100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -304,6 +304,7 @@ enum sdap_group_attrs {
SDAP_AT_GROUP_MODSTAMP,
SDAP_AT_GROUP_USN,
SDAP_AT_GROUP_TYPE,
+ SDAP_AT_GROUP_EXT_MEMBER,
SDAP_OPTS_GROUP /* attrs counter */
};
--
2.4.3
>From 353b045dfefbeece30f7bb6db3922c6c19009140 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <[email protected]>
Date: Mon, 25 Jan 2016 16:11:59 +0100
Subject: [PATCH 2/3] IPA: Add interface to call into IPA provider from LDAP
provider
https://fedorahosted.org/sssd/ticket/2522
Adds a pluggable interface that is able to resolve the IPA group's
external members. At the moment, the request calls the full be_
interface to make sure all corner cases like id-views are handled
internally.
---
src/providers/ipa/ipa_id.c | 5 +-
src/providers/ipa/ipa_init.c | 28 +++
src/providers/ipa/ipa_subdomains.h | 11 ++
src/providers/ipa/ipa_subdomains_ext_groups.c | 275 ++++++++++++++++++++++++++
src/providers/ipa/ipa_subdomains_id.c | 1 +
src/providers/ldap/sdap.h | 23 +++
6 files changed, 342 insertions(+), 1 deletion(-)
diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
index
27cc2548d8802c81311c6c5bd10a0db4e8930fa1..29e22982c415220c931f0422e10cd06dfa1a195b
100644
--- a/src/providers/ipa/ipa_id.c
+++ b/src/providers/ipa/ipa_id.c
@@ -405,7 +405,10 @@ static int ipa_initgr_get_overrides_step(struct tevent_req
*req)
/* This should never happen, the search filter used to get the list
* of groups includes "uuid=*"
*/
- DEBUG(SSSDBG_OP_FAILURE, "A group with no UUID, error!\n");
+ DEBUG(SSSDBG_OP_FAILURE,
+ "The group %s has no UUID attribute %s, error!\n",
+ ldb_dn_get_linearized(state->groups[state->group_idx]->dn),
+ state->groups_id_attr);
return EINVAL;
}
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index
0e16dd97c78a087256fb77be500c9741484867c5..453e2b25673ac709c9fa3809d35b7885630c8b24
100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -139,6 +139,24 @@ int common_ipa_init(struct be_ctx *bectx)
return EOK;
}
+static struct sdap_ext_member_ctx *
+ipa_create_ext_members_ctx(TALLOC_CTX *mem_ctx,
+ struct ipa_id_ctx *id_ctx)
+{
+ struct sdap_ext_member_ctx *ext_ctx = NULL;
+
+ ext_ctx = talloc_zero(mem_ctx, struct sdap_ext_member_ctx);
+ if (ext_ctx == NULL) {
+ return NULL;
+ }
+
+ ext_ctx->pvt = id_ctx;
+ ext_ctx->ext_member_resolve_send = ipa_ext_group_member_send;
+ ext_ctx->ext_member_resolve_recv = ipa_ext_group_member_recv;
+
+ return ext_ctx;
+}
+
int sssm_ipa_id_init(struct be_ctx *bectx,
struct bet_ops **ops,
void **pvt_data)
@@ -360,6 +378,16 @@ int sssm_ipa_id_init(struct be_ctx *bectx,
"will not work [%d]: %s\n", ret, strerror(ret));
}
+ ipa_ctx->sdap_id_ctx->opts->ext_ctx = ipa_create_ext_members_ctx(
+ ipa_ctx->sdap_id_ctx->opts,
+ ipa_ctx);
+ if (ipa_ctx->sdap_id_ctx->opts->ext_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to set SRV the extrernal group ctx\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
*ops = &ipa_id_ops;
*pvt_data = ipa_ctx;
ret = EOK;
diff --git a/src/providers/ipa/ipa_subdomains.h
b/src/providers/ipa/ipa_subdomains.h
index
0c13f8ed2eeda87237dfb097f532c7137095ddf1..23c3b7e3cd3ee1e0ac1dbcf98dc71a6c2337b835
100644
--- a/src/providers/ipa/ipa_subdomains.h
+++ b/src/providers/ipa/ipa_subdomains.h
@@ -137,4 +137,15 @@ struct tevent_req *ipa_get_ad_memberships_send(TALLOC_CTX
*mem_ctx,
const char *domain);
errno_t ipa_get_ad_memberships_recv(struct tevent_req *req, int *dp_error_out);
+
+struct tevent_req *ipa_ext_group_member_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *ext_member,
+ void *pvt);
+errno_t ipa_ext_group_member_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ enum sysdb_member_type *_member_type,
+ struct sss_domain_info **_dom,
+ struct sysdb_attrs **_member);
+
#endif /* _IPA_SUBDOMAINS_H_ */
diff --git a/src/providers/ipa/ipa_subdomains_ext_groups.c
b/src/providers/ipa/ipa_subdomains_ext_groups.c
index
d487a58b8adffabe09ff50e31cb750b800b1d252..5dc6d0d6417ec3fb5e7865e4cbaf3c07f4afbd07
100644
--- a/src/providers/ipa/ipa_subdomains_ext_groups.c
+++ b/src/providers/ipa/ipa_subdomains_ext_groups.c
@@ -923,3 +923,278 @@ static errno_t ipa_add_ad_memberships_recv(struct
tevent_req *req,
return EOK;
}
+
+static errno_t
+search_user_or_group_by_sid_str(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sid_str,
+ enum sysdb_member_type *_member_type,
+ struct ldb_message **_msg)
+{
+ errno_t ret;
+ struct ldb_message *msg = NULL;
+ const char *attrs[] = { SYSDB_NAME,
+ SYSDB_SID_STR,
+ SYSDB_ORIG_DN,
+ SYSDB_OBJECTCLASS,
+ SYSDB_CACHE_EXPIRE,
+ NULL };
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *sanitized_sid = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* In theory SID shouldn't contain any special LDAP characters, but let's
+ * be paranoid
+ */
+ ret = sss_filter_sanitize(tmp_ctx, sid_str, &sanitized_sid);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_search_user_by_sid_str(tmp_ctx, domain,
+ sid_str, attrs, &msg);
+ if (ret == EOK) {
+ *_member_type = SYSDB_MEMBER_USER;
+ } else if (ret == ENOENT) {
+ ret = sysdb_search_group_by_sid_str(tmp_ctx, domain,
+ sid_str, attrs, &msg);
+ if (ret == EOK) {
+ *_member_type = SYSDB_MEMBER_GROUP;
+ }
+ }
+
+ switch (ret) {
+ case EOK:
+ DEBUG(SSSDBG_TRACE_FUNC, "Found %s in sysdb\n", sid_str);
+ *_msg = talloc_steal(mem_ctx, msg);
+ break;
+ case ENOENT:
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Could not find %s in sysdb", sid_str);
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Error looking for %s in sysdb [%d]: %s\n",
+ sid_str, ret, sss_strerror(ret));
+ break;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+ipa_ext_group_member_check(TALLOC_CTX *mem_ctx,
+ struct ipa_id_ctx *ipa_ctx,
+ struct sss_domain_info *member_dom,
+ const char *ext_member,
+ enum sysdb_member_type *_member_type,
+ struct sysdb_attrs **_member)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ uint64_t expire;
+ time_t now = time(NULL);
+ struct ldb_message *msg;
+ struct sysdb_attrs **members;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = search_user_or_group_by_sid_str(tmp_ctx, member_dom, ext_member,
+ _member_type, &msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Error looking up sid %s: [%d]: %s\n",
+ ext_member, ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sysdb_msg2attrs(tmp_ctx, 1, &msg, &members);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not convert result to sysdb_attrs [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Return the member both expired and valid */
+ *_member = talloc_steal(mem_ctx, members[0]);
+
+ expire = ldb_msg_find_attr_as_uint64(msg, SYSDB_CACHE_EXPIRE, 0);
+ if (expire != 0 && expire <= now) {
+ DEBUG(SSSDBG_TRACE_FUNC, "%s is expired", ext_member);
+ ret = EAGAIN;
+ goto done;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/* For the IPA external member resolution, we expect a SID as the input.
+ * The _recv() function output is the member and a type (user/group)
+ * since nothing else can be a group member.
+ */
+struct ipa_ext_member_state {
+ const char *ext_member;
+ struct sss_domain_info *dom;
+
+ enum sysdb_member_type member_type;
+ struct sysdb_attrs *member;
+};
+
+static void ipa_ext_group_member_done(struct tevent_req *subreq);
+
+struct tevent_req *ipa_ext_group_member_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *ext_member,
+ void *pvt)
+{
+ struct ipa_id_ctx *ipa_ctx;
+ struct ipa_ext_member_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct be_acct_req *ar;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct ipa_ext_member_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ext_member = ext_member;
+
+ ipa_ctx = talloc_get_type(pvt, struct ipa_id_ctx);
+ if (ipa_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Wrong private context!\n");
+ ret = EINVAL;
+ goto immediate;
+ }
+
+ state->dom = find_domain_by_sid(ipa_ctx->sdap_id_ctx->be->domain,
+ ext_member);
+ if (state->dom == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot find domain of SID [%s]\n", ext_member);
+ ret = ENOENT;
+ goto immediate;
+ }
+
+ ret = ipa_ext_group_member_check(state, ipa_ctx, state->dom, ext_member,
+ &state->member_type, &state->member);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "external member %s already cached\n", ext_member);
+ goto immediate;
+ }
+
+ ret = get_be_acct_req_for_sid(state, ext_member, state->dom->name, &ar);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot create the account request for [%s]\n", ext_member);
+ goto immediate;
+ }
+
+ subreq = be_get_account_info_send(state, ev, NULL,
+ ipa_ctx->sdap_id_ctx->be, ar);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, ipa_ext_group_member_done, req);
+
+ return req;
+
+immediate:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ } else {
+ tevent_req_done(req);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void ipa_ext_group_member_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ipa_ext_member_state *state = tevent_req_data(req,
+ struct ipa_ext_member_state);
+ errno_t ret;
+ int err_maj;
+ int err_min;
+ const char *err_msg;
+ struct ldb_message *msg;
+ struct sysdb_attrs **members;
+
+ ret = be_get_account_info_recv(subreq, state,
+ &err_maj, &err_min, &err_msg);
+ talloc_free(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "be request failed %d:%d: %s\n", err_maj, err_min, err_msg);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = search_user_or_group_by_sid_str(state,
+ state->dom,
+ state->ext_member,
+ &state->member_type,
+ &msg);
+ if (ret != EOK) {
+ DEBUG(ret == ENOENT ? SSSDBG_TRACE_FUNC : SSSDBG_OP_FAILURE,
+ "Could not find %s in sysdb [%d]: %s\n",
+ state->ext_member, ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = sysdb_msg2attrs(state, 1, &msg, &members);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not convert result to sysdb_attrs [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ state->member = members[0];
+ tevent_req_done(req);
+}
+
+errno_t ipa_ext_group_member_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ enum sysdb_member_type *_member_type,
+ struct sss_domain_info **_dom,
+ struct sysdb_attrs **_member)
+{
+ struct ipa_ext_member_state *state = tevent_req_data(req,
+ struct ipa_ext_member_state);
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (_member_type != NULL) {
+ *_member_type = state->member_type;
+ }
+
+ if (_dom) {
+ *_dom = state->dom;
+ }
+
+ if (_member != NULL) {
+ *_member = talloc_steal(mem_ctx, state->member);
+ }
+
+ return EOK;
+}
diff --git a/src/providers/ipa/ipa_subdomains_id.c
b/src/providers/ipa/ipa_subdomains_id.c
index
472985d4ab4f785aa9c4af94bf8021829ca1c3c8..70a1b6a12799b5a645bbf69f8cc19c30dcff82c5
100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -1230,6 +1230,7 @@ static errno_t ipa_get_ad_apply_override_step(struct
tevent_req *req)
* attributes set, i.e. where overrides might not have been applied. */
ret = sysdb_asq_search(state, state->obj_dom, state->obj_msg->dn,
"(&("SYSDB_GC")("SYSDB_GIDNUM"=*)" \
+ "("SYSDB_POSIX"=TRUE)" \
"(!("ORIGINALAD_PREFIX SYSDB_GIDNUM"=*))" \
"(!("ORIGINALAD_PREFIX SYSDB_NAME"=*)))",
SYSDB_INITGR_ATTR,
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index
9dc2e16a0da76246a1f4492cf70e9124edba4a31..e0e05da0c8270a8f131870bc755da862e43783cb
100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -423,6 +423,26 @@ struct sdap_domain {
void *pvt;
};
+typedef struct tevent_req *
+(*ext_member_send_fn_t)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *ext_member,
+ void *pvt);
+typedef errno_t
+(*ext_member_recv_fn_t)(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req,
+ enum sysdb_member_type *member_type,
+ struct sss_domain_info **_dom,
+ struct sysdb_attrs **_member);
+
+struct sdap_ext_member_ctx {
+ /* Typically ID context of the external ID provider */
+ void *pvt;
+
+ ext_member_send_fn_t ext_member_resolve_send;
+ ext_member_recv_fn_t ext_member_resolve_recv;
+};
+
struct sdap_options {
struct dp_option *basic;
struct sdap_attr_map *gen_map;
@@ -435,6 +455,9 @@ struct sdap_options {
/* ID-mapping support */
struct sdap_idmap_ctx *idmap_ctx;
+ /* Resolving external members */
+ struct sdap_ext_member_ctx *ext_ctx;
+
/* FIXME - should this go to a special struct to avoid mixing with
name-service-switch maps? */
struct sdap_attr_map *sudorule_map;
struct sdap_attr_map *autofs_mobject_map;
--
2.4.3
>From db69d56107eb7f7700d1b1bb8c44e237b4b2d374 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <[email protected]>
Date: Mon, 25 Jan 2016 16:13:03 +0100
Subject: [PATCH 3/3] LDAP: Use the IPA provider interface to resolve external
group members
Resolves:
https://fedorahosted.org/sssd/ticket/2522
Currently the approach is not optimized for performance, because each
external member is resolved in a full transaction to make sure even ID
views and similar information is processed.
In future, we should implement https://fedorahosted.org/sssd/ticket/2943
we will again be able to process all the data in a single transaction.
---
src/providers/ldap/sdap_async_groups.c | 49 ++-
src/providers/ldap/sdap_async_nested_groups.c | 610 +++++++++++++++++++++++++-
src/providers/ldap/sdap_async_private.h | 16 +-
src/tests/cmocka/test_nested_groups.c | 4 +-
4 files changed, 651 insertions(+), 28 deletions(-)
diff --git a/src/providers/ldap/sdap_async_groups.c
b/src/providers/ldap/sdap_async_groups.c
index
75bfa413d43f7bb679d954fd1b90ea875ee731f5..3ccf7deb53d365a106cd18af1835f527424d5bf3
100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -1758,6 +1758,7 @@ struct sdap_get_groups_state {
struct sysdb_attrs **groups;
size_t count;
size_t check_count;
+ hash_table_t *missing_external;
hash_table_t *user_hash;
hash_table_t *group_hash;
@@ -2333,6 +2334,8 @@ int sdap_get_groups_recv(struct tevent_req *req,
return EOK;
}
+static void sdap_nested_ext_done(struct tevent_req *subreq);
+
static void sdap_nested_done(struct tevent_req *subreq)
{
errno_t ret, tret;
@@ -2348,7 +2351,8 @@ static void sdap_nested_done(struct tevent_req *subreq)
struct sdap_get_groups_state);
ret = sdap_nested_group_recv(state, subreq, &user_count, &users,
- &group_count, &groups);
+ &group_count, &groups,
+ &state->missing_external);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Nested group processing failed:
[%d][%s]\n",
@@ -2387,8 +2391,25 @@ static void sdap_nested_done(struct tevent_req *subreq)
}
in_transaction = false;
- /* Processing complete */
- tevent_req_done(req);
+ if (hash_count(state->missing_external) == 0) {
+ /* No external members. Processing complete */
+ DEBUG(SSSDBG_TRACE_INTERNAL, "No external members, done");
+ tevent_req_done(req);
+ return;
+ }
+
+ /* At the moment, we need to save the direct groups & members in one
+ * transaction and then query the others in a separate requests
+ */
+ subreq = sdap_nested_group_lookup_external_send(state, state->ev,
+ state->dom,
+ state->opts->ext_ctx,
+ state->missing_external);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, sdap_nested_ext_done, req);
return;
fail:
@@ -2401,6 +2422,28 @@ fail:
tevent_req_error(req, ret);
}
+static void sdap_nested_ext_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ 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_lookup_external_recv(state, subreq);
+ talloc_free(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot resolve external members [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx,
struct sysdb_ctx *sysdb,
struct sss_domain_info *domain,
diff --git a/src/providers/ldap/sdap_async_nested_groups.c
b/src/providers/ldap/sdap_async_nested_groups.c
index
9d715225243d8672850563473bd3938d4cc5db6b..e07869519de5e52ee211275537b25ac7dbbacb7a
100644
--- a/src/providers/ldap/sdap_async_nested_groups.c
+++ b/src/providers/ldap/sdap_async_nested_groups.c
@@ -56,6 +56,13 @@ struct sdap_nested_group_member {
const char *group_filter;
};
+const size_t external_members_chunk = 16;
+
+struct sdap_external_missing_member {
+ const char **parent_group_dns;
+ size_t parent_dn_idx;
+};
+
struct sdap_nested_group_ctx {
struct sss_domain_info *domain;
struct sdap_options *opts;
@@ -64,6 +71,7 @@ struct sdap_nested_group_ctx {
struct sdap_handle *sh;
hash_table_t *users;
hash_table_t *groups;
+ hash_table_t *missing_external;
bool try_deref;
int deref_treshold;
int max_nesting_level;
@@ -184,37 +192,32 @@ done:
return ret;
}
-static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
- struct sysdb_attrs *entry,
- const char *table_name)
+static errno_t sdap_nested_group_hash_insert(hash_table_t *table,
+ const char *entry_key,
+ void *entry_value,
+ bool overwrite,
+ const char *table_name)
{
hash_key_t key;
hash_value_t value;
- const char *name = NULL;
- errno_t ret;
int hret;
- ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
- if (ret != EOK) {
- return ret;
- }
-
DEBUG(SSSDBG_TRACE_ALL, "Inserting [%s] into hash table [%s]\n",
- name, table_name);
+ entry_key, table_name);
key.type = HASH_KEY_STRING;
- key.str = talloc_strdup(NULL, name);
+ key.str = talloc_strdup(NULL, entry_key);
if (key.str == NULL) {
return ENOMEM;
}
- if (hash_has_key(table, &key)) {
+ if (overwrite == false && hash_has_key(table, &key)) {
talloc_free(key.str);
return EEXIST;
}
value.type = HASH_VALUE_PTR;
- value.ptr = entry;
+ value.ptr = entry_value;
hret = hash_enter(table, &key, &value);
if (hret != HASH_SUCCESS) {
@@ -228,6 +231,21 @@ static errno_t sdap_nested_group_hash_entry(hash_table_t
*table,
return EOK;
}
+static errno_t sdap_nested_group_hash_entry(hash_table_t *table,
+ struct sysdb_attrs *entry,
+ const char *table_name)
+{
+ const char *name = NULL;
+ errno_t ret;
+
+ ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return sdap_nested_group_hash_insert(table, name, entry, false,
table_name);
+}
+
static errno_t
sdap_nested_group_hash_user(struct sdap_nested_group_ctx *group_ctx,
struct sysdb_attrs *user)
@@ -297,6 +315,75 @@ sdap_nested_group_hash_group(struct sdap_nested_group_ctx
*group_ctx,
return sdap_nested_group_hash_entry(group_ctx->groups, group, "groups");
}
+static errno_t sdap_nested_group_external_add(hash_table_t *table,
+ const char *ext_member,
+ const char *parent_group_dn)
+{
+ hash_key_t key;
+ hash_value_t value;
+ int hret;
+ int ret;
+ struct sdap_external_missing_member *ext_mem;
+
+ key.type = HASH_KEY_STRING;
+ key.str = discard_const(ext_member);
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Inserting external member [%s] into external members hash table\n",
+ ext_member);
+
+ hret = hash_lookup(table, &key, &value);
+ switch (hret) {
+ case HASH_ERROR_KEY_NOT_FOUND:
+ ext_mem = talloc_zero(table, struct sdap_external_missing_member);
+ if (ext_mem == NULL) {
+ return ENOMEM;
+ }
+ ext_mem->parent_group_dns = talloc_zero_array(ext_mem,
+ const char *,
+ external_members_chunk);
+ if (ext_mem->parent_group_dns == NULL) {
+ talloc_free(ext_mem);
+ return ENOMEM;
+ }
+ break;
+ case HASH_SUCCESS:
+ ext_mem = talloc_get_type(value.ptr,
+ struct sdap_external_missing_member);
+ if (ext_mem->parent_dn_idx == \
+ talloc_array_length(ext_mem->parent_group_dns)) {
+ ext_mem->parent_dn_idx += external_members_chunk;
+ ext_mem->parent_group_dns = talloc_realloc(ext_mem,
+
ext_mem->parent_group_dns,
+ const char *,
+ ext_mem->parent_dn_idx);
+ if (ext_mem->parent_group_dns == NULL) {
+ talloc_free(ext_mem);
+ return ENOMEM;
+ }
+ }
+ break;
+ default:
+ return EIO;
+ }
+
+ ext_mem->parent_group_dns[ext_mem->parent_dn_idx] = \
+
talloc_strdup(ext_mem->parent_group_dns,
+ parent_group_dn);
+ if (ext_mem->parent_group_dns[ext_mem->parent_dn_idx] == NULL) {
+ return ENOMEM;
+ }
+ ext_mem->parent_dn_idx++;
+
+ ret = sdap_nested_group_hash_insert(table, ext_member, ext_mem,
+ true, "missing external users");
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
static errno_t sdap_nested_group_sysdb_search(struct sss_domain_info *domain,
const char *filter,
bool user)
@@ -478,6 +565,13 @@ sdap_nested_group_split_members(TALLOC_CTX *mem_ctx,
errno_t ret;
int i;
+ if (members == NULL) {
+ *_missing = NULL;
+ *_num_missing = 0;
+ *_num_groups = 0;
+ return EOK;
+ }
+
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
@@ -619,6 +713,65 @@ done:
return ret;
}
+static errno_t
+sdap_nested_group_add_ext_members(TALLOC_CTX *mem_ctx,
+ struct sdap_nested_group_ctx *group_ctx,
+ struct sysdb_attrs *group,
+ struct ldb_message_element *ext_members)
+{
+ errno_t ret;
+ const char *ext_member_attr;
+ const char *orig_dn;
+
+ if (ext_members == NULL) {
+ return EOK;
+ }
+
+ ret = sysdb_attrs_get_string(group, SYSDB_ORIG_DN, &orig_dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "A group with no originalDN!?!\n");
+ return ret;
+ }
+
+ for (size_t i = 0; i < ext_members->num_values; i++) {
+ ext_member_attr = (const char *) ext_members->values[i].data;
+
+ ret = sdap_nested_group_external_add(group_ctx->missing_external,
+ ext_member_attr,
+ orig_dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot add %s into external members [%d]: %s\n",
+ ext_member_attr, ret, sss_strerror(ret));
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static struct ldb_message_element *
+sdap_nested_group_ext_members(struct sdap_options *opts,
+ struct sysdb_attrs *group)
+{
+ errno_t ret;
+ struct ldb_message_element *ext_members = NULL;
+
+ if (opts->ext_ctx == NULL) {
+ return NULL;
+ }
+
+ ret = sysdb_attrs_get_el_ext(group,
+ opts->group_map[SDAP_AT_GROUP_EXT_MEMBER].sys_name,
+ false, &ext_members);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve external member list "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ }
+
+ return ext_members;
+}
+
struct sdap_nested_group_state {
struct sdap_nested_group_ctx *group_ctx;
@@ -667,6 +820,14 @@ sdap_nested_group_send(TALLOC_CTX *mem_ctx,
goto immediately;
}
+ ret = sss_hash_create(state->group_ctx, 32,
+ &state->group_ctx->missing_external);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create hash table [%d]: %s\n",
+ ret, strerror(ret));
+ goto immediately;
+ }
+
state->group_ctx->try_deref = true;
state->group_ctx->deref_treshold = dp_opt_get_int(opts->basic,
SDAP_DEREF_THRESHOLD);
@@ -760,7 +921,8 @@ errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
unsigned long *_num_users,
struct sysdb_attrs ***_users,
unsigned long *_num_groups,
- struct sysdb_attrs ***_groups)
+ struct sysdb_attrs ***_groups,
+ hash_table_t **_missing_external)
{
struct sdap_nested_group_state *state = NULL;
struct sysdb_attrs **users = NULL;
@@ -807,6 +969,11 @@ errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
*_groups = talloc_steal(mem_ctx, groups);
}
+ if (_missing_external) {
+ *_missing_external = talloc_steal(mem_ctx,
+ state->group_ctx->missing_external);
+ }
+
return EOK;
}
@@ -816,6 +983,7 @@ struct sdap_nested_group_process_state {
struct sdap_nested_group_member *missing;
int num_missing_total;
int num_missing_groups;
+ struct ldb_message_element *ext_members;
int nesting_level;
char *group_dn;
bool deref;
@@ -866,13 +1034,16 @@ sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
DEBUG(SSSDBG_TRACE_INTERNAL, "About to process group [%s]\n", orig_dn);
- /* get member list */
+ /* get member list, both direct and external */
+ state->ext_members = sdap_nested_group_ext_members(state->group_ctx->opts,
+ group);
+
ret = sysdb_attrs_get_el_ext(group,
group_map[SDAP_AT_GROUP_MEMBER].sys_name,
false, &members);
- if (ret == ENOENT) {
- ret = EOK; /* no members */
+ if (ret == ENOENT && state->ext_members == NULL) {
+ ret = EOK; /* no members, direct or external */
goto immediately;
- } else if (ret != EOK) {
+ } else if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve member list "
"[%d]: %s\n", ret, strerror(ret));
goto immediately;
@@ -890,14 +1061,27 @@ sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
goto immediately;
}
- DEBUG(SSSDBG_TRACE_INTERNAL, "Looking up %d/%d members of group [%s]\n",
- state->num_missing_total, members->num_values, orig_dn);
+ ret = sdap_nested_group_add_ext_members(state,
+ state->group_ctx,
+ group,
+ state->ext_members);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to split external member list "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto immediately;
+ }
- if (state->num_missing_total == 0) {
+ if (state->num_missing_total == 0
+ && hash_count(state->group_ctx->missing_external) == 0) {
ret = EOK; /* we're done */
goto immediately;
}
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Looking up %d/%d members of group [%s]\n",
+ state->num_missing_total,
+ members ? members->num_values : 0,
+ orig_dn);
+
/* process members */
if (group_ctx->try_deref
&& state->num_missing_total > group_ctx->deref_treshold) {
@@ -2268,3 +2452,385 @@ static errno_t sdap_nested_group_deref_recv(struct
tevent_req *req)
return EOK;
}
+
+struct sdap_ext_member {
+ struct sdap_external_missing_member *missing_mem;
+ const char *ext_member_attr;
+
+ enum sysdb_member_type member_type;
+ struct sss_domain_info *dom;
+ struct sysdb_attrs *attrs;
+};
+
+struct sdap_nested_group_lookup_external_state {
+ struct tevent_context *ev;
+ struct sdap_ext_member_ctx *ext_ctx;
+ struct sss_domain_info *group_dom;
+ hash_table_t *missing_external;
+
+ hash_entry_t *entries;
+ unsigned long n_entries;
+ unsigned long eniter;
+
+ struct sdap_ext_member *ext_members;
+
+ ext_member_send_fn_t ext_member_resolve_send;
+ ext_member_recv_fn_t ext_member_resolve_recv;
+};
+
+static errno_t
+sdap_nested_group_lookup_external_step(struct tevent_req *req);
+static void
+sdap_nested_group_lookup_external_done(struct tevent_req *subreq);
+static errno_t
+sdap_nested_group_lookup_external_link(struct tevent_req *req);
+static errno_t
+sdap_nested_group_lookup_external_link_member(
+ struct sdap_nested_group_lookup_external_state *state,
+ struct sdap_ext_member *member);
+static errno_t
+sdap_nested_group_memberof_dn_by_original_dn(
+ TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *group_dom,
+ const char *original_dn,
+ const char ***_parents);
+
+struct tevent_req *
+sdap_nested_group_lookup_external_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *group_dom,
+ struct sdap_ext_member_ctx *ext_ctx,
+ hash_table_t *missing_external)
+{
+ struct sdap_nested_group_lookup_external_state *state = NULL;
+ struct tevent_req *req = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct sdap_nested_group_lookup_external_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->group_dom = group_dom;
+ state->ext_ctx = ext_ctx;
+ state->missing_external = missing_external;
+
+ if (state->ext_ctx->ext_member_resolve_send == NULL
+ || state->ext_ctx->ext_member_resolve_recv == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Wrong private context\n");
+ ret = EINVAL;
+ goto immediately;
+ }
+
+ ret = hash_entries(state->missing_external,
+ &state->n_entries, &state->entries);
+ if (ret != HASH_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "hash_entries returned %d\n", ret);
+ ret = EIO;
+ goto immediately;
+ }
+ state->eniter = 0;
+
+ state->ext_members = talloc_zero_array(state,
+ struct sdap_ext_member,
+ state->n_entries);
+ if (state->ext_members == NULL) {
+ ret = ENOMEM;
+ goto immediately;
+ }
+
+ ret = sdap_nested_group_lookup_external_step(req);
+ if (ret != EAGAIN) {
+ goto immediately;
+ }
+
+ return req;
+
+immediately:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static errno_t
+sdap_nested_group_lookup_external_step(struct tevent_req *req)
+{
+ struct tevent_req *subreq = NULL;
+ struct sdap_nested_group_lookup_external_state *state = NULL;
+ state = tevent_req_data(req,
+ struct sdap_nested_group_lookup_external_state);
+
+ subreq = state->ext_ctx->ext_member_resolve_send(state,
+ state->ev,
+ state->entries[state->eniter].key.str,
+ state->ext_ctx->pvt);
+ if (subreq == NULL) {
+ return ENOMEM;
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Refreshing member %lu/%lu\n",
+ state->eniter, state->n_entries);
+ tevent_req_set_callback(subreq,
+ sdap_nested_group_lookup_external_done,
+ req);
+
+ return EAGAIN;
+}
+
+static void
+sdap_nested_group_lookup_external_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = NULL;
+ struct sdap_nested_group_lookup_external_state *state = NULL;
+ enum sysdb_member_type member_type;
+ struct sysdb_attrs *member;
+ struct sss_domain_info *member_dom;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req,
+ struct sdap_nested_group_lookup_external_state);
+
+ ret = state->ext_ctx->ext_member_resolve_recv(state, subreq,
+ &member_type,
+ &member_dom,
+ &member);
+ talloc_free(subreq);
+ if (ret == EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC, "Refreshing member %lu\n", state->eniter);
+ state->ext_members[state->eniter].missing_mem = \
+ state->entries[state->eniter].value.ptr;
+ state->ext_members[state->eniter].dom = member_dom;
+
+ state->ext_members[state->eniter].ext_member_attr = \
+ talloc_steal(state->ext_members,
+ state->entries[state->eniter].key.str);
+ state->ext_members[state->eniter].member_type = member_type;
+ state->ext_members[state->eniter].attrs = \
+ talloc_steal(state->ext_members, member);
+ }
+
+ state->eniter++;
+ if (state->eniter >= state->n_entries) {
+ DEBUG(SSSDBG_TRACE_FUNC, "All external members processed\n");
+ ret = sdap_nested_group_lookup_external_link(req);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ tevent_req_done(req);
+ return;
+ }
+
+ ret = sdap_nested_group_lookup_external_step(req);
+ if (ret != EOK && ret != EAGAIN) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ return;
+}
+
+static errno_t
+sdap_nested_group_lookup_external_link(struct tevent_req *req)
+{
+ errno_t ret, tret;
+ bool in_transaction = false;
+ struct sdap_nested_group_lookup_external_state *state = NULL;
+ state = tevent_req_data(req,
+ struct sdap_nested_group_lookup_external_state);
+
+ ret = sysdb_transaction_start(state->group_dom->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+ goto fail;
+ }
+ in_transaction = true;
+
+
+ for (size_t i = 0; i < state->eniter; i++) {
+ if (state->ext_members[i].attrs == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "The member %s could not be
resolved\n",
+ state->ext_members[i].ext_member_attr);
+ continue;
+ }
+
+ ret = sdap_nested_group_lookup_external_link_member(state,
+ &state->ext_members[i]);
+ if (ret != EOK) {
+ goto fail;
+ }
+ }
+
+ ret = sysdb_transaction_commit(state->group_dom->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto fail;
+ }
+ in_transaction = false;
+
+ return EOK;
+
+fail:
+ if (in_transaction) {
+ tret = sysdb_transaction_cancel(state->group_dom->sysdb);
+ if (tret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
+ }
+ }
+ return EFAULT;
+}
+
+static errno_t
+sdap_nested_group_lookup_external_link_member(
+ struct sdap_nested_group_lookup_external_state *state,
+ struct sdap_ext_member *member)
+{
+ const char *name;
+ int ret;
+ const char **parents = NULL;
+ size_t i;
+ TALLOC_CTX *tmp_ctx;
+ const char *orig_dn;
+
+ tmp_ctx = talloc_new(state);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sysdb_attrs_get_string(member->attrs, SYSDB_NAME, &name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No name for a user\n");
+ goto done;
+ }
+
+ /* This only works because the groups were saved in a previous
+ * transaction */
+ for (i=0; member->missing_mem->parent_group_dns[i]; i++) {
+ orig_dn = member->missing_mem->parent_group_dns[i];
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Linking external members %s from domain %s to parents of %s\n",
+ name, member->dom->name, orig_dn);
+ ret = sdap_nested_group_memberof_dn_by_original_dn(tmp_ctx,
+ state->group_dom,
+ orig_dn,
+ &parents);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot find parents of %s\n", orig_dn);
+ continue;
+ }
+
+ /* We don't have to remove the members here, since all members
attributes
+ * are always written anew
+ */
+ ret = sysdb_update_members_dn(member->dom, name, member->member_type,
+ parents, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot link %s@%s to its parents\n",
+ name, member->dom->name);
+ goto done;
+ }
+
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sdap_nested_group_memberof_dn_by_original_dn(
+ TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *group_dom,
+ const char *original_dn,
+ const char ***_parents)
+{
+ errno_t ret;
+ char *sanitized_dn;
+ char *filter;
+ const char *attrs[] = { SYSDB_NAME,
+ SYSDB_MEMBEROF,
+ NULL };
+ struct ldb_message **msgs = NULL;
+ size_t count;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message_element *memberof;
+ const char **parents;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, original_dn, &sanitized_dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Cannot sanitize originalDN [%s]\n", original_dn);
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(%s=%s)", SYSDB_ORIG_DN, sanitized_dn);
+ if (filter == NULL) {
+ goto done;
+ }
+
+ ret = sysdb_search_groups(tmp_ctx, group_dom, filter, attrs,
+ &count, &msgs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (count != 1) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "More than one entry found by originalDN?\n");
+ goto done;
+ }
+
+ memberof = ldb_msg_find_element(msgs[0], SYSDB_MEMBEROF);
+ if (memberof == NULL || memberof->num_values == 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "The external group is not a member of any groups\n");
+ ret = ENOENT;
+ goto done;
+ }
+
+ parents = talloc_zero_array(tmp_ctx,
+ const char *,
+ memberof->num_values + 1);
+ if (parents == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (size_t i = 0; i < memberof->num_values; i++) {
+ parents[i] = talloc_strdup(parents,
+ (const char *) memberof->values[i].data);
+ if (parents[i] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ *_parents = talloc_steal(mem_ctx, parents);
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t
+sdap_nested_group_lookup_external_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
diff --git a/src/providers/ldap/sdap_async_private.h
b/src/providers/ldap/sdap_async_private.h
index
db542eaf869efcd53d0937bef3fc6e99cc78b938..9cde6f5dfe0114f797135b4989b9a4bd336a3f27
100644
--- a/src/providers/ldap/sdap_async_private.h
+++ b/src/providers/ldap/sdap_async_private.h
@@ -130,8 +130,20 @@ errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
unsigned long *_num_users,
struct sysdb_attrs ***_users,
unsigned long *_num_groups,
- struct sysdb_attrs ***_groups);
+ struct sysdb_attrs ***_groups,
+ hash_table_t **missing_external);
+struct tevent_req *
+sdap_nested_group_lookup_external_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *group_dom,
+ struct sdap_ext_member_ctx *ext_ctx,
+ hash_table_t *missing_external);
+errno_t
+sdap_nested_group_lookup_external_recv(TALLOC_CTX *mem_ctx,
+ struct tevent_req *req);
+
+/* from sdap_async_initgroups.c */
errno_t sdap_add_incomplete_groups(struct sysdb_ctx *sysdb,
struct sss_domain_info *domain,
struct sdap_options *opts,
@@ -139,7 +151,7 @@ errno_t sdap_add_incomplete_groups(struct sysdb_ctx *sysdb,
struct sysdb_attrs **ldap_groups,
int ldap_groups_count);
-/* from sdap_async_nested_groups.c */
+/* from sdap_ad_groups.c */
errno_t sdap_check_ad_group_type(struct sss_domain_info *dom,
struct sdap_options *opts,
struct sysdb_attrs *group_attrs,
diff --git a/src/tests/cmocka/test_nested_groups.c
b/src/tests/cmocka/test_nested_groups.c
index
dc29768c5660d5815d5fab56ee70cc8c9caab330..a3345ef5e087fc90466ce8400dda549fa5d79af8
100644
--- a/src/tests/cmocka/test_nested_groups.c
+++ b/src/tests/cmocka/test_nested_groups.c
@@ -57,6 +57,7 @@ struct nested_groups_test_ctx {
struct sdap_domain *sdap_domain;
struct sdap_idmap_ctx *idmap_ctx;
struct sdap_id_ctx *sdap_id_ctx;
+ hash_table_t *missing_external;
struct sysdb_attrs **users;
struct sysdb_attrs **groups;
@@ -110,7 +111,8 @@ static void nested_groups_test_done(struct tevent_req *req)
ctx->tctx->error = sdap_nested_group_recv(ctx, req,
&ctx->num_users, &ctx->users,
- &ctx->num_groups, &ctx->groups);
+ &ctx->num_groups, &ctx->groups,
+ &ctx->missing_external);
talloc_zfree(req);
ctx->tctx->done = true;
--
2.4.3
_______________________________________________
sssd-devel mailing list
[email protected]
https://lists.fedorahosted.org/admin/lists/[email protected]