From 14acf4a389c02d7d774e357d9887f2e16d25e6b3 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Mar 31 2016 11:52:33 +0000 Subject: import sssd-1.13.0-40.el7_2.2 --- diff --git a/SOURCES/0106-Add-a-new-option-ldap_group_external_member.patch b/SOURCES/0106-Add-a-new-option-ldap_group_external_member.patch new file mode 100644 index 0000000..a17aa8a --- /dev/null +++ b/SOURCES/0106-Add-a-new-option-ldap_group_external_member.patch @@ -0,0 +1,178 @@ +From b842e04a1e73dd9af3096b065fcf5b2a7fe55b51 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 25 Jan 2016 16:03:23 +0100 +Subject: [PATCH 106/108] Add a new option ldap_group_external_member + +Required for: + https://fedorahosted.org/sssd/ticket/2522 + +Reviewed-by: Sumit Bose +(cherry picked from commit 3cf7fdfcaedb986f42a6640e26aa057007b64045) +(cherry picked from commit 7db3bdfd6b1b845866c1ff062d25de5804141e89) +--- + 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.h | 1 + + src/providers/ipa/ipa_opts.h | 1 + + src/providers/ldap/ldap_opts.h | 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 a7cd1dd243a53e7038dc69628475c76ccdd93260..7ec28d1d4a121cef0f7a8f3122c95bd396f773a7 100644 +--- a/src/config/SSSDConfig/__init__.py.in ++++ b/src/config/SSSDConfig/__init__.py.in +@@ -328,6 +328,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 b636d93108ef0a3831970d7827895c14b0f3571c..e1083aa2a6d9a0ebf61a6029af05aea62ec7b217 100644 +--- a/src/config/etc/sssd.api.d/sssd-ad.conf ++++ b/src/config/etc/sssd.api.d/sssd-ad.conf +@@ -108,6 +108,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 ab712fe55cdac6d247a085aeca5cc82d65966623..3cbfb2ee5e06f2ef731e8f9eb79be374351d7281 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 8fd45fd4093714f458161eb352157c845d926f06..21a38b9581ea35f78618a272bc3a943f6968d37e 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 3fa3f040708a4984158206d66a1d28a079091cf7..817ecd2764c3fac3ca3e38ba78f9e8ef2afab51b 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 49e84560f825b1bff255c1ad131487ba3243300d..cc1b17108500c5d241008f683d14ab32cefab396 100644 +--- a/src/man/sssd-ldap.5.xml ++++ b/src/man/sssd-ldap.5.xml +@@ -942,6 +942,22 @@ + + + ++ ldap_group_external_member (string) ++ ++ ++ The LDAP attribute that references group ++ members that are defined in an external ++ domain. At the moment, only IPA's external ++ members are supported. ++ ++ ++ Default: ipaExternalMember in the IPA provider, ++ otherwise unset. ++ ++ ++ ++ ++ + ldap_group_nesting_level (integer) + + +diff --git a/src/providers/ad/ad_opts.h b/src/providers/ad/ad_opts.h +index 00586a7ada63ad4c89630e9589d3ff75d1726703..b2133db1859914d54d2db2a871d7fbae7aeff268 100644 +--- a/src/providers/ad/ad_opts.h ++++ b/src/providers/ad/ad_opts.h +@@ -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.h b/src/providers/ipa/ipa_opts.h +index 78949e3ddec95f7f4303eab905bbbf6ec14ed6ae..81ccc42fc0c9f21c8ef16e2d1735bc06199ba747 100644 +--- a/src/providers/ipa/ipa_opts.h ++++ b/src/providers/ipa/ipa_opts.h +@@ -221,6 +221,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", NULL, SYSDB_EXTERNAL_MEMBER, NULL }, + SDAP_ATTR_MAP_TERMINATOR + }; + +diff --git a/src/providers/ldap/ldap_opts.h b/src/providers/ldap/ldap_opts.h +index 9f58db5bd9eef1391e97c1890cbff94c2a5406d6..bda2e79e849ab9e4e3e91407faafc0d8b06df899 100644 +--- a/src/providers/ldap/ldap_opts.h ++++ b/src/providers/ldap/ldap_opts.h +@@ -197,6 +197,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 + }; + +@@ -253,6 +254,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 + }; + +@@ -309,6 +311,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 444502bf7159edcf4cebe530cce8b216c737ec30..d7053949f5804b637c27bb2d8e34991653770639 100644 +--- a/src/providers/ldap/sdap.h ++++ b/src/providers/ldap/sdap.h +@@ -303,6 +303,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 + diff --git a/SOURCES/0107-IPA-Add-interface-to-call-into-IPA-provider-from-LDA.patch b/SOURCES/0107-IPA-Add-interface-to-call-into-IPA-provider-from-LDA.patch new file mode 100644 index 0000000..9c83b71 --- /dev/null +++ b/SOURCES/0107-IPA-Add-interface-to-call-into-IPA-provider-from-LDA.patch @@ -0,0 +1,446 @@ +From 2515f7d9a19c2634baf1cd2f008a2148b5300db0 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 25 Jan 2016 16:11:59 +0100 +Subject: [PATCH 107/108] 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. + +Reviewed-by: Sumit Bose +(cherry picked from commit e2d96566aeb881bd89e5c9236d663f6a9a88019a) +(cherry picked from commit 00ee45423f0712b83926c6f8b354a1a18ff741c8) +--- + 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 e81ccb34dd6eb44618538593f5473fbe5e89d896..8782a81247f7ca7ba6d2aa55f8d01897a0b38523 100644 +--- a/src/providers/ipa/ipa_id.c ++++ b/src/providers/ipa/ipa_id.c +@@ -384,7 +384,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 7acbb38e66c2c36ff230ae35b236544195a8104b..ebbf3be71d7488c0e5138183925668fc6187df97 100644 +--- a/src/providers/ipa/ipa_subdomains_id.c ++++ b/src/providers/ipa/ipa_subdomains_id.c +@@ -1229,6 +1229,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 d7053949f5804b637c27bb2d8e34991653770639..312789411fa273dd263bd6319f7a5ff45437d8d0 100644 +--- a/src/providers/ldap/sdap.h ++++ b/src/providers/ldap/sdap.h +@@ -415,6 +415,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; +@@ -427,6 +447,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 + diff --git a/SOURCES/0108-LDAP-Use-the-IPA-provider-interface-to-resolve-exter.patch b/SOURCES/0108-LDAP-Use-the-IPA-provider-interface-to-resolve-exter.patch new file mode 100644 index 0000000..845a2c8 --- /dev/null +++ b/SOURCES/0108-LDAP-Use-the-IPA-provider-interface-to-resolve-exter.patch @@ -0,0 +1,916 @@ +From 3af57ba315eb5268c56919297e3a688c3f2f5b05 Mon Sep 17 00:00:00 2001 +From: Jakub Hrozek +Date: Mon, 25 Jan 2016 16:13:03 +0100 +Subject: [PATCH 108/108] 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. + +Reviewed-by: Sumit Bose +(cherry picked from commit c32266e79f9d4bebd0c31eaa8d6fa26050e7fb3e) +(cherry picked from commit 19194cb18a1cc20f02423861dd831aa5bc3a1003) +--- + src/providers/ldap/sdap_async_groups.c | 49 +- + src/providers/ldap/sdap_async_nested_groups.c | 618 +++++++++++++++++++++++++- + src/providers/ldap/sdap_async_private.h | 16 +- + src/tests/cmocka/test_nested_groups.c | 4 +- + 4 files changed, 659 insertions(+), 28 deletions(-) + +diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c +index 57a53af3f4eb46e6f31af9ee7c4d4625239d2a54..485f79d2fb2e7e34f1420b43baadb40ab3f96d17 100644 +--- a/src/providers/ldap/sdap_async_groups.c ++++ b/src/providers/ldap/sdap_async_groups.c +@@ -1728,6 +1728,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; +@@ -2289,6 +2290,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; +@@ -2304,7 +2307,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", +@@ -2343,8 +2347,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: +@@ -2357,6 +2378,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 08e199869ad16c3b19d998a2a28eae9a0dd0a371..f5be6b5a5946453151918f97aa0632df4b218a29 100644 +--- a/src/providers/ldap/sdap_async_nested_groups.c ++++ b/src/providers/ldap/sdap_async_nested_groups.c +@@ -55,6 +55,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; +@@ -63,6 +70,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; +@@ -183,37 +191,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) { +@@ -227,6 +230,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) +@@ -296,6 +314,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) +@@ -477,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"); +@@ -618,6 +713,66 @@ 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; ++ size_t i; ++ ++ 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 (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; +@@ -666,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); +@@ -759,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; +@@ -806,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; + } + +@@ -815,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; +@@ -865,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; +@@ -889,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) { +@@ -2332,3 +2522,387 @@ 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); ++ size_t i; ++ ++ 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 (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; ++ size_t i; ++ ++ 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 (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 8081ff26102e53b2e453838c3a18e4560ac5317e..22b8caefc92a44a2339cbdcaa98226ebb4dc1ef5 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 + diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec index 1808930..7423742 100644 --- a/SPECS/sssd.spec +++ b/SPECS/sssd.spec @@ -25,7 +25,7 @@ Name: sssd Version: 1.13.0 -Release: 40%{?dist}.1 +Release: 40%{?dist}.2 Group: Applications/System Summary: System Security Services Daemon License: GPLv3+ @@ -141,6 +141,9 @@ Patch0102: 0102-nss-send-original-name-and-id-with-local-views-if-po.patch Patch0103: 0103-sudo-search-with-view-even-if-user-is-found.patch Patch0104: 0104-sudo-send-original-name-and-id-with-local-views-if-p.patch Patch0105: 0105-IPA-fix-override-with-the-same-name.patch +Patch0106: 0106-Add-a-new-option-ldap_group_external_member.patch +Patch0107: 0107-IPA-Add-interface-to-call-into-IPA-provider-from-LDA.patch +Patch0108: 0108-LDAP-Use-the-IPA-provider-interface-to-resolve-exter.patch #This patch should not be removed in RHEL-7 Patch999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec @@ -1073,7 +1076,11 @@ fi /usr/bin/rm -f /var/tmp/sssd.upgrade || : %changelog -* Tue Nov 24 2015 Jakub Hrozek - 1.13.0-41 +* Wed Feb 24 2016 Jakub Hrozek - 1.13.0-40.2 +- Resolves: rhbz#1311569 - [RFE] IPA: resolve external group memberships + of IPA groups during getgrnam and getgrgid + +* Tue Nov 24 2015 Jakub Hrozek - 1.13.0-40.1 - Resolves: rhbz#1284814 - sssd: [sysdb_add_user] (0x0400): Error: 17 (File exists)