diff options
author | Stephen Gallagher <sgallagh@redhat.com> | 2012-09-18 16:51:15 -0400 |
---|---|---|
committer | Jakub Hrozek <jhrozek@redhat.com> | 2012-09-24 15:00:11 +0200 |
commit | d0e0e73e86f2afdb7f8fefbed70fda8d77b1c25a (patch) | |
tree | 445f1806f9f1344dfa3b4f05b5c9294cdc541696 /src/providers | |
parent | e6ba224432bfcd64802222a3544bc38c179727cd (diff) | |
download | sssd-d0e0e73e86f2afdb7f8fefbed70fda8d77b1c25a.tar.gz sssd-d0e0e73e86f2afdb7f8fefbed70fda8d77b1c25a.tar.bz2 sssd-d0e0e73e86f2afdb7f8fefbed70fda8d77b1c25a.zip |
AD: Optimize initgroups lookups with tokenGroups
https://fedorahosted.org/sssd/ticket/1355
Diffstat (limited to 'src/providers')
-rw-r--r-- | src/providers/ldap/sdap_async.h | 16 | ||||
-rw-r--r-- | src/providers/ldap/sdap_async_initgroups.c | 24 | ||||
-rw-r--r-- | src/providers/ldap/sdap_async_initgroups_ad.c | 277 |
3 files changed, 313 insertions, 4 deletions
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h index d024e30a..8c16d94e 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -31,6 +31,8 @@ #include "providers/ldap/sdap_id_op.h" #include "providers/fail_over.h" +#define AD_TOKENGROUPS_ATTR "tokenGroups" + struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sdap_options *opts, @@ -275,4 +277,18 @@ sdap_get_ad_match_rule_initgroups_send(TALLOC_CTX *mem_ctx, errno_t sdap_get_ad_match_rule_initgroups_recv(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); + +errno_t +sdap_get_ad_tokengroups_initgroups_recv(struct tevent_req *req); + #endif /* _SDAP_ASYNC_H_ */ diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c index d55f661f..71b4536b 100644 --- a/src/providers/ldap/sdap_async_initgroups.c +++ b/src/providers/ldap/sdap_async_initgroups.c @@ -2642,6 +2642,8 @@ static void sdap_get_initgr_user(struct tevent_req *subreq) const char *orig_dn; const char *cname; bool in_transaction = false; + bool use_id_mapping = + dp_opt_get_bool(state->opts->basic, SDAP_ID_MAPPING); DEBUG(9, ("Receiving info for the user\n")); @@ -2731,9 +2733,17 @@ static void sdap_get_initgr_user(struct tevent_req *subreq) return; } - if (state->opts->support_matching_rule - && dp_opt_get_bool(state->opts->basic, - SDAP_AD_MATCHING_RULE_INITGROUPS)) { + if (use_id_mapping + && state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) { + /* Take advantage of AD's tokenGroups mechanism to look up all + * parent groups in a single request. + */ + subreq = sdap_get_ad_tokengroups_initgroups_send( + state, state->ev, state->opts, state->sysdb, + state->sh, cname, orig_dn, state->timeout); + } else if (state->opts->support_matching_rule + && dp_opt_get_bool(state->opts->basic, + SDAP_AD_MATCHING_RULE_INITGROUPS)) { /* Take advantage of AD's extensibleMatch filter to look up * all parent groups in a single request. */ @@ -2815,7 +2825,13 @@ static void sdap_get_initgr_done(struct tevent_req *subreq) case SDAP_SCHEMA_RFC2307BIS: case SDAP_SCHEMA_AD: - if (dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_INITGROUPS)) { + if (use_id_mapping + && state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) { + ret = sdap_get_ad_tokengroups_initgroups_recv(subreq); + } + else if (state->opts->support_matching_rule + && dp_opt_get_bool(state->opts->basic, + SDAP_AD_MATCHING_RULE_INITGROUPS)) { ret = sdap_get_ad_match_rule_initgroups_recv(subreq); } else { ret = sdap_initgr_rfc2307bis_recv(subreq); 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; +} |