On Mon, Feb 22, 2016 at 06:04:07PM +0100, Jakub Hrozek wrote: > 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.
The attached patches fix issues in the reallocation logic that Sumit found during his review. Only the third patch had changed.
>From e8a93192c3e5246175c3ec9473e1e4c6dd1d20b8 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 9f8e4c4b757851fc213d0d0d6ae4edf85cb5c832 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 db489b139e9de02a4de1937ab45d935024dc9256 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 | 615 +++++++++++++++++++++++++- src/providers/ldap/sdap_async_private.h | 16 +- src/tests/cmocka/test_nested_groups.c | 4 +- 4 files changed, 656 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..f0d04fa0948abd58470785d07b8d42f3cfeb9eb0 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,76 @@ 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; + } + + ret = sdap_nested_group_hash_insert(table, ext_member, ext_mem, + true, "missing external users"); + if (ret != EOK) { + return ret; + } + 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_group_dns = talloc_realloc(ext_mem, + ext_mem->parent_group_dns, + const char *, + ext_mem->parent_dn_idx + \ + external_members_chunk); + 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++; + + return EOK; +} + static errno_t sdap_nested_group_sysdb_search(struct sss_domain_info *domain, const char *filter, bool user) @@ -478,6 +566,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 +714,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 +821,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 +922,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 +970,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 +984,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 +1035,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 +1062,31 @@ 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; } + /* If there are only indirect members of the group, it's still safe to + * proceed and let the direct lookup code just fall through. + */ + + 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 +2457,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; i < member->missing_mem->parent_dn_idx; 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]
