From 353b045dfefbeece30f7bb6db3922c6c19009140 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek 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