From 2c62da337e31217d03f5bf0f768b574d166bb2fe Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Tue, 12 Jun 2012 20:29:26 -0400 Subject: LDAP: Auto-detect support for the ldap match rule This patch extends the RootDSE lookup so that we will perform a second request to test whether the match rule syntax can be used. If both groups and initgroups are disabled in the configuration, this lookup request can be skipped. --- src/man/sssd-ldap.5.xml | 14 ++++- src/providers/ldap/ldap_opts.h | 2 +- src/providers/ldap/sdap.c | 7 ++- src/providers/ldap/sdap.h | 2 + src/providers/ldap/sdap_async.c | 96 +++++++++++++++++++++++++++++- src/providers/ldap/sdap_async_groups.c | 1 + src/providers/ldap/sdap_async_initgroups.c | 4 +- 7 files changed, 120 insertions(+), 6 deletions(-) diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml index e04befdba..d20d84bca 100644 --- a/src/man/sssd-ldap.5.xml +++ b/src/man/sssd-ldap.5.xml @@ -844,6 +844,12 @@ option disabled. It generally only provides a performance increase on very complex nestings. + + If this option is enabled, SSSD will use it if it + detects that the server supports it during initial + connection. So "True" here essentially means + "auto-detect". + Note: This feature is currently known to work only with Active Directory 2008 R1 and later. See @@ -865,6 +871,12 @@ up initgroups operations (most notably when dealing with complex or deep nested groups). + + If this option is enabled, SSSD will use it if it + detects that the server supports it during initial + connection. So "True" here essentially means + "auto-detect". + Note: This feature is currently known to work only with Active Directory 2008 R1 and later. See @@ -872,7 +884,7 @@ MSDN(TM) documentation for more details. - Default: False + Default: True diff --git a/src/providers/ldap/ldap_opts.h b/src/providers/ldap/ldap_opts.h index 1c21bea9c..2d08f7a60 100644 --- a/src/providers/ldap/ldap_opts.h +++ b/src/providers/ldap/ldap_opts.h @@ -103,7 +103,7 @@ struct dp_option default_basic_opts[] = { { "ldap_idmap_default_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_idmap_default_domain_sid", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_groups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, - { "ldap_initgroups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, + { "ldap_initgroups_use_matching_rule_in_chain", DP_OPT_BOOL, BOOL_TRUE, BOOL_FALSE }, DP_OPTION_TERMINATOR }; diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c index cb02f4a5b..325825e2d 100644 --- a/src/providers/ldap/sdap.c +++ b/src/providers/ldap/sdap.c @@ -179,8 +179,11 @@ int sdap_parse_entry(TALLOC_CTX *memctx, str = ldap_first_attribute(sh->ldap, sm->msg, &ber); if (!str) { ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); - DEBUG(1, ("Entry has no attributes [%d(%s)]!?\n", - lerrno, sss_ldap_err2string(lerrno))); + DEBUG(lerrno == LDAP_SUCCESS + ? SSSDBG_TRACE_INTERNAL + : SSSDBG_MINOR_FAILURE, + ("Entry has no attributes [%d(%s)]!?\n", + lerrno, sss_ldap_err2string(lerrno))); if (map) { ret = EINVAL; goto done; diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index a92305ff2..253b08c57 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -386,6 +386,8 @@ struct sdap_options { struct sdap_search_base **sudo_search_bases; struct sdap_search_base **service_search_bases; struct sdap_search_base **autofs_search_bases; + + bool support_matching_rule; }; struct sdap_server_opts { diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c index 46f0215a8..4b2869220 100644 --- a/src/providers/ldap/sdap_async.c +++ b/src/providers/ldap/sdap_async.c @@ -836,6 +836,7 @@ struct sdap_get_rootdse_state { }; static void sdap_get_rootdse_done(struct tevent_req *subreq); +static void sdap_get_matching_rule_done(struct tevent_req *subreq); struct tevent_req *sdap_get_rootdse_send(TALLOC_CTX *memctx, struct tevent_context *ev, @@ -883,6 +884,11 @@ struct tevent_req *sdap_get_rootdse_send(TALLOC_CTX *memctx, return req; } +/* This is not a real attribute, it's just there to avoid + * actually pulling real data down, to save bandwidth + */ +#define SDAP_MATCHING_RULE_TEST_ATTR "sssmatchingruletest" + static void sdap_get_rootdse_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, @@ -892,6 +898,8 @@ static void sdap_get_rootdse_done(struct tevent_req *subreq) struct sysdb_attrs **results; size_t num_results; int ret; + const char *filter; + const char *attrs[] = { SDAP_MATCHING_RULE_TEST_ATTR, NULL }; ret = sdap_get_generic_recv(subreq, state, &num_results, &results); talloc_zfree(subreq); @@ -917,7 +925,81 @@ static void sdap_get_rootdse_done(struct tevent_req *subreq) state->rootdse = talloc_steal(state, results[0]); talloc_zfree(results); - DEBUG(9, ("Got rootdse\n")); + DEBUG(SSSDBG_TRACE_INTERNAL, ("Got rootdse\n")); + + /* Auto-detect the ldap matching rule if requested */ + if ((!dp_opt_get_bool(state->opts->basic, + SDAP_AD_MATCHING_RULE_INITGROUPS)) + && !dp_opt_get_bool(state->opts->basic, + SDAP_AD_MATCHING_RULE_GROUPS)) { + /* This feature is disabled for both groups + * and initgroups. Skip the auto-detection + * lookup. + */ + DEBUG(SSSDBG_TRACE_INTERNAL, + ("Skipping auto-detection of match rule\n")); + tevent_req_done(req); + return; + } + + DEBUG(SSSDBG_TRACE_INTERNAL, + ("Auto-detecting support for match rule\n")); + + /* Create a filter using the matching rule. It need not point + * at any valid data. We're only going to be looking for the + * error code. + */ + filter = "("SDAP_MATCHING_RULE_TEST_ATTR":" + SDAP_MATCHING_RULE_IN_CHAIN":=)"; + + /* Perform a trivial query with the matching rule in play. + * If it returns success, we know it is available. If it + * returns EIO, we know it isn't. + */ + subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, + "", LDAP_SCOPE_BASE, filter, attrs, NULL, + 0, dp_opt_get_int(state->opts->basic, + SDAP_SEARCH_TIMEOUT), + false); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, sdap_get_matching_rule_done, req); +} + +static void sdap_get_matching_rule_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct sdap_get_rootdse_state *state = tevent_req_data(req, + struct sdap_get_rootdse_state); + size_t num_results; + struct sysdb_attrs **results; + + ret = sdap_get_generic_recv(subreq, state, &num_results, &results); + talloc_zfree(subreq); + if (ret == EOK) { + /* The search succeeded */ + state->opts->support_matching_rule = true; + } else if (ret == EIO) { + /* The search failed. Disable support for + * matching rule lookups. + */ + state->opts->support_matching_rule = false; + } else { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Unexpected error while testing for matching rule support\n")); + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_CONF_SETTINGS, + ("LDAP server %s the matching rule extension\n", + state->opts->support_matching_rule + ? "supports" + : "does not support")); tevent_req_done(req); } @@ -1292,6 +1374,18 @@ static void sdap_get_generic_ext_done(struct sdap_op *op, /* Try to return what we've got */ DEBUG(SSSDBG_MINOR_FAILURE, ("LDAP sizelimit was exceeded, returning incomplete data\n")); + } else if (result == LDAP_INAPPROPRIATE_MATCHING) { + /* This error should only occur when we're testing for + * specialized functionality like the ldap matching rule + * filter for Active Directory. Warn at a higher log + * level and return EIO. + */ + DEBUG(SSSDBG_TRACE_INTERNAL, + ("LDAP_INAPPROPRIATE_MATCHING: %s\n", + errmsg ? errmsg : "no errmsg set")); + ldap_memfree(errmsg); + tevent_req_error(req, EIO); + return; } else if (result != LDAP_SUCCESS && result != LDAP_NO_SUCH_OBJECT) { DEBUG(SSSDBG_OP_FAILURE, ("Unexpected result from ldap: %s(%d), %s\n", diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c index 8be5ff1d9..720c3cc29 100644 --- a/src/providers/ldap/sdap_async_groups.c +++ b/src/providers/ldap/sdap_async_groups.c @@ -1513,6 +1513,7 @@ static void sdap_get_groups_process(struct tevent_req *subreq) */ if (!state->enumeration && (state->opts->schema_type != SDAP_SCHEMA_RFC2307) + && state->opts->support_matching_rule && dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_GROUPS)) { subreq = sdap_get_ad_match_rule_members_send( state, state->ev, state->opts, state->sh, diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c index 2f146b016..8a837bcc5 100644 --- a/src/providers/ldap/sdap_async_initgroups.c +++ b/src/providers/ldap/sdap_async_initgroups.c @@ -2665,7 +2665,9 @@ static void sdap_get_initgr_user(struct tevent_req *subreq) return; } - if (dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_INITGROUPS)) { + 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. */ -- cgit