summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/providers/ad/ad_id.c390
-rw-r--r--src/providers/ad/ad_subdomains.c11
2 files changed, 379 insertions, 22 deletions
diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
index a47aa4f75..e3302c157 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,348 @@ 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->sysdb, 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->sysdb, 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(sd->dom->sysdb, 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->sysdb, 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;