diff options
-rw-r--r-- | src/providers/ad/ad_id.c | 388 | ||||
-rw-r--r-- | src/providers/ad/ad_subdomains.c | 11 |
2 files changed, 377 insertions, 22 deletions
diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c index a47aa4f75..db921b118 100644 --- a/src/providers/ad/ad_id.c +++ b/src/providers/ad/ad_id.c @@ -420,10 +420,13 @@ struct ad_enumeration_state { struct tevent_context *ev; struct sdap_domain *sdom; + struct sdap_domain *sditer; }; static void ad_enumeration_conn_done(struct tevent_req *subreq); static void ad_enumeration_master_done(struct tevent_req *subreq); +static errno_t ad_enum_sdom(struct tevent_req *req, struct sdap_domain *sd, + struct ad_id_ctx *id_ctx); static void ad_enumeration_done(struct tevent_req *subreq); struct tevent_req * @@ -452,6 +455,7 @@ ad_enumeration_send(TALLOC_CTX *mem_ctx, state->ectx = ectx; state->ev = ev; state->sdom = ectx->sdom; + state->sditer = state->sdom; state->id_ctx = talloc_get_type(ectx->pvt, struct ad_id_ctx); state->sdap_op = sdap_id_op_create(state, @@ -526,7 +530,6 @@ ad_enumeration_master_done(struct tevent_req *subreq) char *flat_name; char *master_sid; char *forest; - struct sdap_id_conn_ctx *user_conn; ret = ad_master_domain_recv(subreq, state, &flat_name, &master_sid, &forest); @@ -545,32 +548,57 @@ ad_enumeration_master_done(struct tevent_req *subreq) return; } - if (dp_opt_get_bool(state->id_ctx->ad_options->basic, AD_ENABLE_GC)) { - user_conn = state->id_ctx->gc_ctx; + ret = ad_enum_sdom(req, state->sdom, state->id_ctx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("Could not enumerate domain %s\n", state->sdom->dom->name)); + tevent_req_error(req, ret); + return; + } + + /* Execution will resume in ad_enumeration_done */ +} + +static errno_t +ad_enum_sdom(struct tevent_req *req, + struct sdap_domain *sd, + struct ad_id_ctx *id_ctx) +{ + struct sdap_id_conn_ctx *user_conn; + struct tevent_req *subreq; + struct ad_enumeration_state *state = tevent_req_data(req, + struct ad_enumeration_state); + + if (dp_opt_get_bool(id_ctx->ad_options->basic, AD_ENABLE_GC)) { + user_conn = id_ctx->gc_ctx; } else { - user_conn = state->id_ctx->ldap_ctx; + user_conn = id_ctx->ldap_ctx; } /* Groups are searched for in LDAP, users in GC. Services (if present, * which is unlikely in AD) from LDAP as well */ subreq = sdap_dom_enum_ex_send(state, state->ev, - state->id_ctx->sdap_id_ctx, - state->sdom, - user_conn, /* Users */ - state->id_ctx->ldap_ctx, /* Groups */ - state->id_ctx->ldap_ctx); /* Services */ + id_ctx->sdap_id_ctx, + sd, + user_conn, /* Users */ + id_ctx->ldap_ctx, /* Groups */ + id_ctx->ldap_ctx); /* Services */ if (subreq == NULL) { /* The ptask API will reschedule the enumeration on its own on * failure */ DEBUG(SSSDBG_OP_FAILURE, ("Failed to schedule enumeration, retrying later!\n")); - tevent_req_error(req, ENOMEM); - return; + return ENOMEM; } tevent_req_set_callback(subreq, ad_enumeration_done, req); + + return EOK; } +static errno_t ad_enum_cross_dom_members(struct sdap_options *opts, + struct sss_domain_info *dom); + static void ad_enumeration_done(struct tevent_req *subreq) { @@ -579,6 +607,7 @@ ad_enumeration_done(struct tevent_req *subreq) struct tevent_req); struct ad_enumeration_state *state = tevent_req_data(req, struct ad_enumeration_state); + struct ad_id_ctx *subdom_id_ctx; ret = sdap_dom_enum_ex_recv(subreq); talloc_zfree(subreq); @@ -589,9 +618,346 @@ ad_enumeration_done(struct tevent_req *subreq) return; } + state->sditer = state->sditer->next; + if (state->sditer != NULL) { + subdom_id_ctx = talloc_get_type(state->sdom->pvt, struct ad_id_ctx); + if (subdom_id_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot retrieve subdomain ad_id_ctx!\n")); + tevent_req_error(req, EFAULT); + return; + } + + ret = ad_enum_sdom(req, state->sditer, state->sditer->pvt); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not enumerate domain %s\n", + state->sditer->dom->name)); + tevent_req_error(req, ret); + return; + } + + /* Execution will resume in ad_enumeration_done */ + return; + } + + /* No more subdomains to enumerate. Check if we need to fixup + * cross-domain membership + */ + if (state->sditer != state->sdom) { + /* We did enumerate at least one subdomain. Walk the subdomains + * and fixup members for each of them + */ + for (state->sditer = state->sdom; + state->sditer; + state->sditer = state->sditer->next) { + ret = ad_enum_cross_dom_members(state->id_ctx->ad_options->id, + state->sditer->dom); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, ("Could not check cross-domain " + "memberships for %s, group memberships might be " + "incomplete!\n", state->sdom->dom->name)); + continue; + } + } + } + tevent_req_done(req); } +static errno_t ad_group_extra_members(TALLOC_CTX *mem_ctx, + const struct ldb_message *group, + struct sss_domain_info *dom, + char ***_group_only); +static errno_t ad_group_add_member(struct sdap_options *opts, + struct sss_domain_info *group_domain, + struct ldb_dn *group_dn, + const char *member); + +static errno_t +ad_enum_cross_dom_members(struct sdap_options *opts, + struct sss_domain_info *dom) +{ + errno_t ret; + errno_t sret; + char *filter; + TALLOC_CTX *tmp_ctx; + const char *attrs[] = { + SYSDB_NAME, + SYSDB_MEMBER, + SYSDB_ORIG_MEMBER, + NULL + }; + size_t count, i, mi; + struct ldb_message **msgs; + bool in_transaction = false; + char **group_only; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) return ENOMEM; + + ret = sysdb_transaction_start(dom->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n")); + goto done; + } + in_transaction = true; + + filter = talloc_asprintf(tmp_ctx, "(%s=*)", SYSDB_NAME); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_search_groups(tmp_ctx, dom, filter, attrs, &count, &msgs); + if (ret != EOK) { + goto done; + } + + for (i = 0; i < count; i++) { + ret = ad_group_extra_members(tmp_ctx, msgs[i], dom, &group_only); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Failed to check extra members\n")); + } else if (group_only == NULL) { + DEBUG(SSSDBG_TRACE_INTERNAL, ("No extra members\n")); + continue; + } + + /* Group has extra members */ + for (mi = 0; group_only[mi]; mi++) { + ret = ad_group_add_member(opts, dom, msgs[i]->dn, group_only[mi]); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to add [%s]: %s\n", + group_only[mi], strerror(ret))); + continue; + } + } + + talloc_zfree(group_only); + } + + ret = sysdb_transaction_commit(dom->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to commit transaction\n")); + goto done; + } + in_transaction = false; + + ret = EOK; +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(dom->sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Could not cancel transaction\n")); + } + } + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +ad_group_stored_orig_members(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom, + struct ldb_dn *dn, char ***_odn_list); + +static errno_t +ad_group_extra_members(TALLOC_CTX *mem_ctx, const struct ldb_message *group, + struct sss_domain_info *dom, char ***_group_only) +{ + TALLOC_CTX *tmp_ctx; + struct ldb_message_element *m, *om; + const char *name; + errno_t ret; + char **sysdb_odn_list; + const char **group_odn_list; + char **group_only = NULL; + + if (_group_only == NULL) return EINVAL; + *_group_only = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) return ENOMEM; + + om = ldb_msg_find_element(group, SYSDB_ORIG_MEMBER); + m = ldb_msg_find_element(group, SYSDB_MEMBER); + name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL); + if (name == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("A group with no name!\n")); + ret = EFAULT; + goto done; + } + + if (om == NULL || om->num_values == 0) { + DEBUG(SSSDBG_TRACE_FUNC, ("Group %s has no original members\n", name)); + ret = EOK; + goto done; + } + + if (m == NULL || (m->num_values < om->num_values)) { + DEBUG(SSSDBG_TRACE_FUNC, + ("Group %s has %d members but %d original members\n", + name, m ? m->num_values : 0, om->num_values)); + + /* Get the list of originalDN attributes that are already + * linked to the group + */ + ret = ad_group_stored_orig_members(tmp_ctx, dom, group->dn, + &sysdb_odn_list); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("Could not retrieve list of original members for %s\n", + name)); + goto done; + } + + /* Get the list of original DN attributes the group had in AD */ + group_odn_list = sss_ldb_el_to_string_list(tmp_ctx, om); + if (group_odn_list == NULL) { + ret = EFAULT; + goto done; + } + + /* Compare the two lists */ + ret = diff_string_lists(tmp_ctx, discard_const(group_odn_list), + sysdb_odn_list, &group_only, NULL, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + ("Could not compare lists of members for %s\n", name)); + goto done; + } + } + + ret = EOK; + *_group_only = talloc_steal(mem_ctx, group_only); +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +ad_group_stored_orig_members(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom, + struct ldb_dn *dn, char ***_odn_list) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx; + size_t m_count, i; + struct ldb_message **members; + const char *attrs[] = { + SYSDB_NAME, + SYSDB_ORIG_DN, + NULL + }; + char **odn_list; + const char *odn; + size_t oi; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) return ENOMEM; + + /* Get all entries member element points to */ + ret = sysdb_asq_search(tmp_ctx, dom, dn, NULL, SYSDB_MEMBER, + attrs, &m_count, &members); + if (ret != EOK) { + goto done; + } + + odn_list = talloc_zero_array(tmp_ctx, char *, m_count + 1); + if (odn_list == NULL) { + ret = ENOMEM; + goto done; + } + + /* Get a list of their original DNs */ + oi = 0; + for (i = 0; i < m_count; i++) { + odn = ldb_msg_find_attr_as_string(members[i], SYSDB_ORIG_DN, NULL); + if (odn == NULL) { + continue; + } + + odn_list[oi] = talloc_strdup(odn_list, odn); + if (odn_list[oi] == NULL) { + ret = ENOMEM; + goto done; + } + oi++; + DEBUG(SSSDBG_TRACE_INTERNAL, ("Member %s already in sysdb\n", odn)); + } + + ret = EOK; + *_odn_list = talloc_steal(mem_ctx, odn_list); +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +ad_group_add_member(struct sdap_options *opts, + struct sss_domain_info *group_domain, + struct ldb_dn *group_dn, + const char *member) +{ + struct sdap_domain *sd; + struct ldb_dn *base_dn; + TALLOC_CTX *tmp_ctx; + errno_t ret; + const char *mem_filter; + size_t msgs_count; + struct ldb_message **msgs; + + /* This member would be from a different domain */ + sd = sdap_domain_get_by_dn(opts, member); + if (sd == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, ("No matching domain for %s\n", member)); + return ENOENT; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) return ENOMEM; + + mem_filter = talloc_asprintf(tmp_ctx, "(%s=%s)", + SYSDB_ORIG_DN, member); + if (mem_filter == NULL) { + ret = ENOMEM; + goto done; + } + + base_dn = sysdb_domain_dn(tmp_ctx, sd->dom); + if (base_dn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_search_entry(tmp_ctx, sd->dom->sysdb, base_dn, + LDB_SCOPE_SUBTREE, mem_filter, NULL, + &msgs_count, &msgs); + if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_FUNC, ("No member [%s] in sysdb\n", member)); + ret = EOK; + goto done; + } else if (ret != EOK) { + goto done; + } + DEBUG(SSSDBG_TRACE_INTERNAL, ("[%s] found in sysdb\n", member)); + + if (msgs_count != 1) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Search by orig DN returned %zd results!\n", msgs_count)); + ret = EFAULT; + goto done; + } + + ret = sysdb_mod_group_member(group_domain, msgs[0]->dn, group_dn, SYSDB_MOD_ADD); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Could not add [%s] as a member of [%s]\n", + ldb_dn_get_linearized(msgs[0]->dn), + ldb_dn_get_linearized(group_dn))); + goto done; + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + errno_t ad_enumeration_recv(struct tevent_req *req) { diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c index e7871cc32..0d9652b5c 100644 --- a/src/providers/ad/ad_subdomains.c +++ b/src/providers/ad/ad_subdomains.c @@ -177,17 +177,6 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, return EFAULT; } - ret = sdap_id_setup_tasks(be_ctx, - ad_id_ctx->sdap_id_ctx, - sdom, - ldap_enumeration_send, - ldap_enumeration_recv, - ad_id_ctx->sdap_id_ctx); - if (ret != EOK) { - talloc_free(ad_options); - return ret; - } - /* Set up the ID mapping object */ ad_id_ctx->sdap_id_ctx->opts->idmap_ctx = id_ctx->sdap_id_ctx->opts->idmap_ctx; |