From d0e0e73e86f2afdb7f8fefbed70fda8d77b1c25a Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Tue, 18 Sep 2012 16:51:15 -0400 Subject: AD: Optimize initgroups lookups with tokenGroups https://fedorahosted.org/sssd/ticket/1355 --- src/providers/ldap/sdap_async_initgroups_ad.c | 277 ++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) (limited to 'src/providers/ldap/sdap_async_initgroups_ad.c') diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c index f74729f4..1333e308 100644 --- a/src/providers/ldap/sdap_async_initgroups_ad.c +++ b/src/providers/ldap/sdap_async_initgroups_ad.c @@ -24,6 +24,8 @@ #include "providers/ldap/sdap_async.h" #include "providers/ldap/ldap_common.h" #include "providers/ldap/sdap_async_private.h" +#include "providers/ldap/sdap_idmap.h" +#include "lib/idmap/sss_idmap.h" struct sdap_ad_match_rule_initgr_state { struct tevent_context *ev; @@ -290,3 +292,278 @@ sdap_get_ad_match_rule_initgroups_recv(struct tevent_req *req) TEVENT_REQ_RETURN_ON_ERROR(req); return EOK; } + +struct sdap_ad_tokengroups_initgr_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct sysdb_ctx *sysdb; + struct sdap_handle *sh; + const char *username; +}; + +static void +sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *req); + +struct tevent_req * +sdap_get_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sdap_options *opts, + struct sysdb_ctx *sysdb, + struct sdap_handle *sh, + const char *name, + const char *orig_dn, + int timeout) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct sdap_ad_tokengroups_initgr_state *state; + const char *attrs[] = {AD_TOKENGROUPS_ATTR, NULL}; + + req = tevent_req_create(mem_ctx, &state, + struct sdap_ad_tokengroups_initgr_state); + if (!req) return NULL; + + state->ev = ev; + state->opts = opts; + state->sysdb = sysdb; + state->sh = sh; + state->username = name; + + subreq = sdap_get_generic_send( + state, state->ev, state->opts, state->sh, + orig_dn, LDAP_SCOPE_BASE, NULL, attrs, + NULL, 0, timeout, false); + if (!subreq) { + tevent_req_error(req, ENOMEM); + tevent_req_post(req, ev); + return req; + } + + tevent_req_set_callback(subreq, + sdap_get_ad_tokengroups_initgroups_lookup_done, + req); + return req; +} + +static void +sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *subreq) +{ + errno_t ret, sret; + enum idmap_error_code err; + size_t user_count, group_count, i; + TALLOC_CTX *tmp_ctx; + bool in_transaction = false; + char *sid_str; + gid_t gid; + time_t now; + struct sysdb_attrs **users; + struct ldb_message_element *el; + struct ldb_message *msg; + char **ldap_grouplist; + char **sysdb_grouplist; + char **add_groups; + char **del_groups; + const char *attrs[] = { SYSDB_NAME, NULL }; + const char *group_name; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct sdap_ad_tokengroups_initgr_state *state = + tevent_req_data(req, struct sdap_ad_tokengroups_initgr_state); + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + ret = ENOMEM; + goto done; + } + + ret = sdap_get_generic_recv(subreq, tmp_ctx, &user_count, &users); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("LDAP search failed: [%s]\n", strerror(ret))); + goto done; + } + + if (user_count != 1) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("More than one result on a base search!\n")); + ret = EINVAL; + goto done; + } + + /* Get the list of group SIDs */ + ret = sysdb_attrs_get_el_ext(users[0], AD_TOKENGROUPS_ATTR, + false, &el); + if (ret != EOK) { + if (ret == ENOENT) { + DEBUG(SSSDBG_TRACE_LIBS, + ("No tokenGroups entries for [%s]\n", + state->username)); + /* No groups in LDAP. We need to ensure that the + * sysdb matches. + */ + el = talloc_zero(tmp_ctx, struct ldb_message_element); + if (!el) { + ret = ENOMEM; + goto done; + } + el->num_values = 0; + + /* This will skip the group-processing loop below + * and proceed to removing any sysdb groups. + */ + } else { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not read tokenGroups attribute: [%s]\n", + strerror(ret))); + goto done; + } + } + + /* Process the groups */ + now = time(NULL); + + ret = sysdb_transaction_start(state->sysdb); + if (ret != EOK) goto done; + in_transaction = true; + + ldap_grouplist = talloc_array(tmp_ctx, char *, el->num_values + 1); + if (!ldap_grouplist) { + ret = ENOMEM; + goto done; + } + group_count = 0; + + for (i = 0; i < el->num_values; i++) { + /* Get the SID and convert it to a GID */ + + err = sss_idmap_bin_sid_to_sid(state->opts->idmap_ctx->map, + el->values[i].data, + el->values[i].length, + &sid_str); + if (err != IDMAP_SUCCESS) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not convert binary SID to string: [%s]. Skipping\n", + idmap_error_string(err))); + continue; + } + DEBUG(SSSDBG_TRACE_LIBS, + ("Processing membership SID [%s]\n", + sid_str)); + ret = sdap_idmap_sid_to_unix(state->opts->idmap_ctx, sid_str, + &gid); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not convert SID to GID: [%s]. Skipping\n", + strerror(ret))); + continue; + } + + DEBUG(SSSDBG_TRACE_LIBS, + ("Processing membership GID [%lu]\n", + gid)); + + /* Check whether this GID already exists in the sysdb */ + ret = sysdb_search_group_by_gid(tmp_ctx, state->sysdb, + gid, attrs, &msg); + if (ret == EOK) { + group_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + if (!group_name) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not retrieve group name from sysdb\n")); + ret = EINVAL; + goto done; + } + } else if (ret == ENOENT) { + /* This is a new group. For now, we will store it + * under the name of its SID. When a direct lookup of + * the group or its GID occurs, it will replace this + * temporary entry. + */ + group_name = sid_str; + ret = sysdb_add_incomplete_group(state->sysdb, group_name, + gid, NULL, false, now); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not create incomplete group: [%s]\n", + strerror(ret))); + goto done; + } + } + + ldap_grouplist[group_count] = + talloc_strdup(ldap_grouplist, group_name); + if (!ldap_grouplist[group_count]) { + ret = ENOMEM; + goto done; + } + + group_count++; + } + ldap_grouplist[group_count] = NULL; + + /* Get the current sysdb group list for this user + * so we can update it. + */ + ret = get_sysdb_grouplist(state, state->sysdb, state->username, + &sysdb_grouplist); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not get the list of groups for [%s] in the sysdb: " + "[%s]\n", + state->username, strerror(ret))); + goto done; + } + + /* Find the differences between the sysdb and LDAP lists + * Groups in the sysdb only must be removed. + */ + ret = diff_string_lists(tmp_ctx, ldap_grouplist, sysdb_grouplist, + &add_groups, &del_groups, NULL); + if (ret != EOK) goto done; + + DEBUG(SSSDBG_TRACE_LIBS, + ("Updating memberships for [%s]\n", state->username)); + ret = sysdb_update_members(state->sysdb, state->username, + SYSDB_MEMBER_USER, + (const char *const *) add_groups, + (const char *const *) del_groups); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Membership update failed [%d]: %s\n", + ret, strerror(ret))); + goto done; + } + + ret = sysdb_transaction_commit(state->sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Could not commit transaction! [%s]\n", + strerror(ret))); + goto done; + } + in_transaction = false; + +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(state->sysdb); + DEBUG(SSSDBG_FATAL_FAILURE, + ("Could not cancel transaction! [%s]\n", + sret)); + } + + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + talloc_free(tmp_ctx); + return; +} + +errno_t +sdap_get_ad_tokengroups_initgroups_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} -- cgit