summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2014-01-22 15:21:24 +0100
committerJakub Hrozek <jhrozek@redhat.com>2014-01-29 17:27:55 +0100
commit580221eebb363fcda33d17f513f54752787bb829 (patch)
tree46137a4029ebb37766e51cf9d3d8fac3764a977a
parent407a7e05f9077ccaefe5412f794347e4c9da94bb (diff)
downloadsssd-580221eebb363fcda33d17f513f54752787bb829.tar.gz
sssd-580221eebb363fcda33d17f513f54752787bb829.tar.xz
sssd-580221eebb363fcda33d17f513f54752787bb829.zip
AD: Establish cross-domain memberships after enumeration finishes
Because domain enumeration currently works for each domain separately, the code has to establish cross-domain memberships after all domains are enumerated. The code works as follows: 1) check if any *sub*domains were enumerated. If not, do nothing 2) if any of the groups saved had more original members than sysdb members, check if members of these groups can be linked now that all users and groups are saved using the orig_member attribute of the group matched against originalDN member of the user. Related: https://fedorahosted.org/sssd/ticket/2142
-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;