From d42d371c00c83ae44b9d1c3e88ecbe0e01b112e6 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Sun, 10 Jun 2012 14:50:43 -0400 Subject: LDAP: Add support for AD chain matching extension in initgroups --- src/providers/ldap/sdap_async.h | 13 ++ src/providers/ldap/sdap_async_initgroups.c | 29 ++- src/providers/ldap/sdap_async_initgroups_ad.c | 292 ++++++++++++++++++++++++++ 3 files changed, 325 insertions(+), 9 deletions(-) create mode 100644 src/providers/ldap/sdap_async_initgroups_ad.c (limited to 'src') diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h index 9a65b25e0..d024e30a9 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -262,4 +262,17 @@ sdap_get_ad_match_rule_members_recv(struct tevent_req *req, size_t *num_users, struct sysdb_attrs ***users); +struct tevent_req * +sdap_get_ad_match_rule_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_match_rule_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 8524b1374..2f146b016 100644 --- a/src/providers/ldap/sdap_async_initgroups.c +++ b/src/providers/ldap/sdap_async_initgroups.c @@ -2657,10 +2657,6 @@ static void sdap_get_initgr_user(struct tevent_req *subreq) case SDAP_SCHEMA_RFC2307BIS: case SDAP_SCHEMA_AD: - /* TODO: AD uses a different member/memberof schema - * We need an AD specific call that is able to unroll - * nested groups by doing extensive recursive searches */ - ret = sysdb_attrs_get_string(state->orig_user, SYSDB_ORIG_DN, &orig_dn); @@ -2669,17 +2665,28 @@ static void sdap_get_initgr_user(struct tevent_req *subreq) return; } - subreq = sdap_initgr_rfc2307bis_send( - state, state->ev, state->opts, state->sysdb, - state->dom, state->sh, - cname, orig_dn); + if (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. + */ + subreq = sdap_get_ad_match_rule_initgroups_send( + state, state->ev, state->opts, state->sysdb, + state->sh, cname, orig_dn, state->timeout); + } else { + subreq = sdap_initgr_rfc2307bis_send( + state, state->ev, state->opts, state->sysdb, + state->dom, state->sh, + cname, orig_dn); + } if (!subreq) { tevent_req_error(req, ENOMEM); return; } + talloc_steal(subreq, orig_dn); tevent_req_set_callback(subreq, sdap_get_initgr_done, req); break; + case SDAP_SCHEMA_IPA_V1: subreq = sdap_initgr_nested_send(state, state->ev, state->opts, state->sysdb, state->dom, state->sh, @@ -2730,7 +2737,11 @@ static void sdap_get_initgr_done(struct tevent_req *subreq) case SDAP_SCHEMA_RFC2307BIS: case SDAP_SCHEMA_AD: - ret = sdap_initgr_rfc2307bis_recv(subreq); + if (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); + } break; case SDAP_SCHEMA_IPA_V1: diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c new file mode 100644 index 000000000..f74729f47 --- /dev/null +++ b/src/providers/ldap/sdap_async_initgroups_ad.c @@ -0,0 +1,292 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "providers/ldap/sdap_async.h" +#include "providers/ldap/ldap_common.h" +#include "providers/ldap/sdap_async_private.h" + +struct sdap_ad_match_rule_initgr_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct sysdb_ctx *sysdb; + struct sdap_handle *sh; + const char *name; + const char *orig_dn; + const char **attrs; + int timeout; + const char *base_filter; + char *filter; + + size_t count; + struct sysdb_attrs **groups; + + size_t base_iter; + struct sdap_search_base **search_bases; +}; + +static errno_t +sdap_get_ad_match_rule_initgroups_next_base(struct tevent_req *req); + +static void +sdap_get_ad_match_rule_initgroups_step(struct tevent_req *subreq); + +struct tevent_req * +sdap_get_ad_match_rule_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 ret; + struct tevent_req *req; + struct sdap_ad_match_rule_initgr_state *state; + const char **filter_members; + char *sanitized_user_dn; + + req = tevent_req_create(mem_ctx, &state, + struct sdap_ad_match_rule_initgr_state); + if (!req) return NULL; + + state->ev = ev; + state->opts = opts; + state->sysdb = sysdb; + state->sh = sh; + state->name = name; + state->orig_dn = orig_dn; + state->base_iter = 0; + state->search_bases = opts->group_search_bases; + + /* Request all of the group attributes that we know + * about, except for 'member' because that wastes a + * lot of bandwidth here and we only really + * care about a single member (the one we already + * have). + */ + filter_members = talloc_array(state, const char *, 2); + if (!filter_members) { + ret = ENOMEM; + goto immediate; + } + filter_members[0] = opts->group_map[SDAP_AT_GROUP_MEMBER].name; + filter_members[1] = NULL; + + ret = build_attrs_from_map(state, opts->group_map, + SDAP_OPTS_GROUP, + filter_members, + &state->attrs, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not build attribute map: [%s]\n", + strerror(ret))); + goto immediate; + } + + /* Sanitize the user DN in case we have special characters in DN */ + ret = sss_filter_sanitize(state, state->orig_dn, &sanitized_user_dn); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not sanitize user DN: %s\n", + strerror(ret))); + goto immediate; + } + + /* Craft a special filter according to + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa746475%28v=vs.85%29.aspx + */ + state->base_filter = + talloc_asprintf(state, + "(&(%s:%s:=%s)(objectClass=%s))", + state->opts->group_map[SDAP_AT_GROUP_MEMBER].name, + SDAP_MATCHING_RULE_IN_CHAIN, + sanitized_user_dn, + state->opts->group_map[SDAP_OC_GROUP].name); + talloc_zfree(sanitized_user_dn); + if (!state->base_filter) { + ret = ENOMEM; + goto immediate; + } + + /* Start the loop through the search bases to get all of the + * groups to which this user belongs. + */ + ret = sdap_get_ad_match_rule_initgroups_next_base(req); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("sdap_get_ad_match_rule_members_next_base failed: [%s]\n", + strerror(ret))); + goto immediate; + } + + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static errno_t +sdap_get_ad_match_rule_initgroups_next_base(struct tevent_req *req) +{ + struct tevent_req *subreq; + struct sdap_ad_match_rule_initgr_state *state; + + state = tevent_req_data(req, struct sdap_ad_match_rule_initgr_state); + + talloc_zfree(state->filter); + state->filter = sdap_get_id_specific_filter(state, + state->base_filter, + state->search_bases[state->base_iter]->filter); + if (!state->filter) { + return ENOMEM; + } + + DEBUG(SSSDBG_TRACE_FUNC, + ("Searching for groups with base [%s]\n", + state->search_bases[state->base_iter]->basedn)); + + subreq = sdap_get_generic_send( + state, state->ev, state->opts, state->sh, + state->search_bases[state->base_iter]->basedn, + state->search_bases[state->base_iter]->scope, + state->filter, state->attrs, + state->opts->group_map, SDAP_OPTS_GROUP, + state->timeout, true); + if (!subreq) { + return ENOMEM; + } + + tevent_req_set_callback(subreq, + sdap_get_ad_match_rule_initgroups_step, + req); + + return EOK; +} + +static void +sdap_get_ad_match_rule_initgroups_step(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct sdap_ad_match_rule_initgr_state *state = + tevent_req_data(req, struct sdap_ad_match_rule_initgr_state); + size_t count, i; + struct sysdb_attrs **groups; + char **sysdb_grouplist; + + ret = sdap_get_generic_recv(subreq, state, &count, &groups); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("LDAP search failed: [%s]\n", strerror(ret))); + goto error; + } + + DEBUG(SSSDBG_TRACE_LIBS, + ("Search for users returned %d results\n", count)); + + /* Add this batch of groups to the list */ + if (count > 0) { + state->groups = talloc_realloc(state, state->groups, + struct sysdb_attrs *, + state->count + count + 1); + if (!state->groups) { + tevent_req_error(req, ENOMEM); + return; + } + + /* Copy the new groups into the list */ + for (i = 0; i < count; i++) { + state->groups[state->count + i] = + talloc_steal(state->groups, groups[i]); + } + + state->count += count; + state->groups[state->count] = NULL; + } + + /* Continue checking other search bases */ + state->base_iter++; + if (state->search_bases[state->base_iter]) { + /* There are more search bases to try */ + ret = sdap_get_ad_match_rule_initgroups_next_base(req); + if (ret != EOK) { + goto error; + } + return; + } + + /* No more search bases. Save the groups. */ + + if (state->count == 0) { + DEBUG(SSSDBG_TRACE_LIBS, + ("User is not a member of any group in the search bases\n")); + } + + /* Get the current sysdb group list for this user + * so we can update it. + */ + ret = get_sysdb_grouplist(state, state->sysdb, state->name, + &sysdb_grouplist); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not get the list of groups for [%s] in the sysdb: " + "[%s]\n", + state->name, strerror(ret))); + goto error; + } + + /* The extensibleMatch search rule eliminates the need for + * nested group searches, so we can just update the + * memberships now. + */ + ret = sdap_initgr_common_store(state->sysdb, state->opts, + state->name, + SYSDB_MEMBER_USER, + sysdb_grouplist, + state->groups, + state->count); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not store groups for user [%s]: [%s]\n", + state->name, strerror(ret))); + goto error; + } + + tevent_req_done(req); + return; + +error: + tevent_req_error(req, ret); +} + +errno_t +sdap_get_ad_match_rule_initgroups_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} -- cgit