From 09b663e6dfd2ed09cead04f926d3e99e9ac01894 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Tue, 13 Sep 2011 09:07:26 -0400 Subject: LDAP: Add parser for multiple search bases --- src/providers/ipa/ipa_common.c | 20 +++ src/providers/ldap/ldap_common.c | 261 +++++++++++++++++++++++++++++++++++++++ src/providers/ldap/ldap_common.h | 5 + src/providers/ldap/sdap.c | 109 ++++++++++++---- src/providers/ldap/sdap.h | 11 ++ 5 files changed, 380 insertions(+), 26 deletions(-) diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index c6229456..12c0bb67 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -332,10 +332,15 @@ int ipa_get_id_options(struct ipa_options *ipa_opts, if (ret != EOK) { goto done; } + DEBUG(6, ("Option %s set to %s\n", ipa_opts->id->basic[SDAP_SEARCH_BASE].opt_name, dp_opt_get_string(ipa_opts->id->basic, SDAP_SEARCH_BASE))); } + ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id, + SDAP_SEARCH_BASE, + &ipa_opts->id->search_bases); + if (ret != EOK) goto done; /* set krb realm */ if (NULL == dp_opt_get_string(ipa_opts->id->basic, SDAP_KRB5_REALM)) { @@ -414,11 +419,16 @@ int ipa_get_id_options(struct ipa_options *ipa_opts, if (ret != EOK) { goto done; } + DEBUG(6, ("Option %s set to %s\n", ipa_opts->id->basic[SDAP_USER_SEARCH_BASE].opt_name, dp_opt_get_string(ipa_opts->id->basic, SDAP_USER_SEARCH_BASE))); } + ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id, + SDAP_USER_SEARCH_BASE, + &ipa_opts->id->user_search_bases); + if (ret != EOK) goto done; if (NULL == dp_opt_get_string(ipa_opts->id->basic, SDAP_GROUP_SEARCH_BASE)) { @@ -428,11 +438,16 @@ int ipa_get_id_options(struct ipa_options *ipa_opts, if (ret != EOK) { goto done; } + DEBUG(6, ("Option %s set to %s\n", ipa_opts->id->basic[SDAP_GROUP_SEARCH_BASE].opt_name, dp_opt_get_string(ipa_opts->id->basic, SDAP_GROUP_SEARCH_BASE))); } + ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id, + SDAP_GROUP_SEARCH_BASE, + &ipa_opts->id->group_search_bases); + if (ret != EOK) goto done; if (NULL == dp_opt_get_string(ipa_opts->id->basic, SDAP_NETGROUP_SEARCH_BASE)) { @@ -461,11 +476,16 @@ int ipa_get_id_options(struct ipa_options *ipa_opts, goto done; } #endif + DEBUG(6, ("Option %s set to %s\n", ipa_opts->id->basic[SDAP_NETGROUP_SEARCH_BASE].opt_name, dp_opt_get_string(ipa_opts->id->basic, SDAP_NETGROUP_SEARCH_BASE))); } + ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id, + SDAP_NETGROUP_SEARCH_BASE, + &ipa_opts->id->netgroup_search_bases); + if (ret != EOK) goto done; value = dp_opt_get_string(ipa_opts->id->basic, SDAP_DEREF); if (value != NULL) { diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 5a375bf0..233d0194 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -249,6 +249,7 @@ int ldap_get_options(TALLOC_CTX *memctx, goto done; } + /* Handle search bases */ search_base = dp_opt_get_string(opts->basic, SDAP_SEARCH_BASE); if (search_base != NULL) { /* set user/group/netgroup search bases if they are not */ @@ -270,6 +271,30 @@ int ldap_get_options(TALLOC_CTX *memctx, "connecting to the LDAP server.\n")); } + /* Default search */ + ret = sdap_parse_search_base(opts, opts, + SDAP_SEARCH_BASE, + &opts->search_bases); + if (ret != EOK && ret != ENOENT) goto done; + + /* User search */ + ret = sdap_parse_search_base(opts, opts, + SDAP_USER_SEARCH_BASE, + &opts->user_search_bases); + if (ret != EOK && ret != ENOENT) goto done; + + /* Group search base */ + ret = sdap_parse_search_base(opts, opts, + SDAP_GROUP_SEARCH_BASE, + &opts->group_search_bases); + if (ret != EOK && ret != ENOENT) goto done; + + /* Netgroup search */ + ret = sdap_parse_search_base(opts, opts, + SDAP_NETGROUP_SEARCH_BASE, + &opts->netgroup_search_bases); + if (ret != EOK && ret != ENOENT) goto done; + pwd_policy = dp_opt_get_string(opts->basic, SDAP_PWD_POLICY); if (pwd_policy == NULL) { DEBUG(1, ("Missing password policy, this may not happen.\n")); @@ -468,6 +493,242 @@ done: return ret; } +errno_t sdap_parse_search_base(TALLOC_CTX *mem_ctx, + struct sdap_options *opts, + enum sdap_basic_opt class, + struct sdap_search_base ***_search_bases) +{ + errno_t ret; + struct sdap_search_base **search_bases; + TALLOC_CTX *tmp_ctx; + struct ldb_context *ldb; + struct ldb_dn *ldn; + struct ldb_parse_tree *tree; + const char *class_name; + char *unparsed_base; + char **split_bases; + char *filter; + int count; + int i, c; + + *_search_bases = NULL; + + switch (class) { + case SDAP_SEARCH_BASE: + class_name = "DEFAULT"; + break; + case SDAP_USER_SEARCH_BASE: + class_name = "USER"; + break; + case SDAP_GROUP_SEARCH_BASE: + class_name = "GROUP"; + break; + case SDAP_NETGROUP_SEARCH_BASE: + class_name = "NETGROUP"; + break; + default: + DEBUG(SSSDBG_CONF_SETTINGS, + ("Unknown search base type: [%d]\n", class)); + class_name = "UNKNOWN"; + /* Non-fatal */ + } + + unparsed_base = dp_opt_get_string(opts->basic, class); + if (!unparsed_base || unparsed_base[0] == '\0') return ENOENT; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + ret = ENOMEM; + goto done; + } + + /* Create a throwaway LDB context for validating the DN */ + ldb = ldb_init(tmp_ctx, NULL); + if (!ldb) { + ret = ENOMEM; + goto done; + } + + ret = split_on_separator(tmp_ctx, unparsed_base, '?', false, + &split_bases, &count); + if (ret != EOK) goto done; + + /* The split must be either exactly one value or a multiple of + * three in order to be valid. + * One value: just a base, backwards-compatible with pre-1.7.0 versions + * Multiple: search_base?scope?filter[?search_base?scope?filter]* + */ + if (count > 1 && (count % 3)) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Unparseable search base: [%s][%d]\n", unparsed_base, count)); + ret = EINVAL; + goto done; + } + + if (count == 1) { + search_bases = talloc_array(tmp_ctx, struct sdap_search_base *, 2); + if (!search_bases) { + ret = ENOMEM; + goto done; + } + search_bases[0] = talloc_zero(search_bases, struct sdap_search_base); + if (!search_bases[0]) { + ret = ENOMEM; + goto done; + } + + search_bases[0]->basedn = talloc_strdup(search_bases[0], + unparsed_base); + if (!search_bases[0]->basedn) { + ret = ENOMEM; + goto done; + } + + /* Validate the basedn */ + ldn = ldb_dn_new(tmp_ctx, ldb, unparsed_base); + if (!ldn) { + ret = ENOMEM; + goto done; + } + + if (!ldb_dn_validate(ldn)) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Invalid base DN [%s]\n", + unparsed_base)); + ret = EINVAL; + goto done; + } + talloc_zfree(ldn); + + search_bases[0]->scope = LDAP_SCOPE_SUBTREE; + search_bases[0]->filter = NULL; + + + DEBUG(SSSDBG_CONF_SETTINGS, + ("Search base added: [%s][%s][%s][%s]\n", + class_name, + search_bases[0]->basedn, + "SUBTREE", + search_bases[0]->filter ? search_bases[0]->filter : "")); + + search_bases[1] = NULL; + } else { + search_bases = talloc_array(tmp_ctx, struct sdap_search_base *, + (count / 3) + 1); + if (!search_bases) { + ret = ENOMEM; + goto done; + } + + i = 0; + for (c = 0; c < count; c += 3) { + search_bases[i] = talloc_zero(search_bases, + struct sdap_search_base); + if (!search_bases[i]) { + ret = ENOMEM; + goto done; + } + + if (split_bases[c][0] == '\0') { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Zero-length search base: [%s]\n", unparsed_base)); + ret = EINVAL; + goto done; + } + + /* Validate the basedn */ + ldn = ldb_dn_new(tmp_ctx, ldb, split_bases[c]); + if (!ldn) { + ret = ENOMEM; + goto done; + } + + if (!ldb_dn_validate(ldn)) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Invalid base DN [%s]\n", + split_bases[c])); + ret = EINVAL; + goto done; + } + talloc_zfree(ldn); + + /* Set the search base DN */ + search_bases[i]->basedn = talloc_strdup(search_bases[i], + split_bases[c]); + if (!search_bases[i]->basedn) { + ret = ENOMEM; + goto done; + } + + /* Set the search scope for this base DN */ + if ((split_bases[c+1][0] == '\0') + || strcasecmp(split_bases[c+1], "sub") == 0 + || strcasecmp(split_bases[c+1], "subtree") == 0) { + /* If unspecified, default to subtree */ + search_bases[i]->scope = LDAP_SCOPE_SUBTREE; + } else if (strcasecmp(split_bases[c+1], "one") == 0 + || strcasecmp(split_bases[c+1], "onelevel") == 0) { + search_bases[i]->scope = LDAP_SCOPE_ONELEVEL; + } else if (strcasecmp(split_bases[c+1], "base") == 0) { + search_bases[i]->scope = LDAP_SCOPE_BASE; + } else { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Unknown search scope: [%s]\n", split_bases[c+1])); + ret = EINVAL; + goto done; + } + + /* Get a specialized filter if provided */ + if (split_bases[c+2][0] == '\0') { + search_bases[i]->filter = NULL; + } else { + if (split_bases[c+2][0] != '(') { + /* Filters need to be enclosed in parentheses + * to be validated properly by ldb_parse_tree() + */ + filter = talloc_asprintf(tmp_ctx, "(%s)", + split_bases[c+2]); + } else { + filter = talloc_strdup(tmp_ctx, split_bases[c+2]); + } + if (!filter) { + ret = ENOMEM; + goto done; + } + + tree = ldb_parse_tree(tmp_ctx, filter); + if(!tree) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Invalid search filter: [%s]\n", filter)); + ret = EINVAL; + goto done; + } + talloc_zfree(tree); + + search_bases[i]->filter = talloc_steal(search_bases[i], + filter); + } + + DEBUG(SSSDBG_CONF_SETTINGS, + ("Search base added: [%s][%s][%s][%s]\n", + class_name, + search_bases[i]->basedn, + split_bases[c+1][0] ? split_bases[c+1] : "SUBTREE", + search_bases[i]->filter ? search_bases[i]->filter : "")); + + i++; + } + search_bases[i] = NULL; + } + + *_search_bases = talloc_steal(mem_ctx, search_bases); + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + void sdap_handler_done(struct be_req *req, int dp_err, int error, const char *errstr) { diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index e08a65bb..e64b65c2 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -170,4 +170,9 @@ errno_t msgs2attrs_array(TALLOC_CTX *mem_ctx, size_t count, struct ldb_message **msgs, struct sysdb_attrs ***attrs); +errno_t sdap_parse_search_base(TALLOC_CTX *mem_ctx, + struct sdap_options *opts, + enum sdap_basic_opt class, + struct sdap_search_base ***_search_bases); + #endif /* _LDAP_COMMON_H_ */ diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c index 02504a4f..c3f507bb 100644 --- a/src/providers/ldap/sdap.c +++ b/src/providers/ldap/sdap.c @@ -690,42 +690,99 @@ static char *get_naming_context(TALLOC_CTX *mem_ctx, return naming_context; } +static errno_t sdap_set_search_base(struct sdap_options *opts, + enum sdap_basic_opt class, + char *naming_context) +{ + errno_t ret; + struct sdap_search_base ***bases; + + switch(class) { + case SDAP_SEARCH_BASE: + bases = &opts->search_bases; + break; + case SDAP_USER_SEARCH_BASE: + bases = &opts->user_search_bases; + break; + case SDAP_GROUP_SEARCH_BASE: + bases = &opts->group_search_bases; + break; + case SDAP_NETGROUP_SEARCH_BASE: + bases = &opts->netgroup_search_bases; + break; + default: + return EINVAL; + } + + DEBUG(SSSDBG_CONF_SETTINGS, + ("Setting option [%s] to [%s].\n", + opts->basic[class].opt_name, naming_context)); + + ret = dp_opt_set_string(opts->basic, class, naming_context); + if (ret != EOK) { + DEBUG(1, ("dp_opt_set_string failed.\n")); + goto done; + } + + ret = sdap_parse_search_base(opts, opts, class, bases); + if (ret != EOK) goto done; + + ret = EOK; +done: + return ret; +} + errno_t sdap_set_config_options_with_rootdse(struct sysdb_attrs *rootdse, struct sdap_handle *sh, struct sdap_options *opts) { int ret; char *naming_context = NULL; - const int search_base_options[] = { SDAP_SEARCH_BASE, - SDAP_USER_SEARCH_BASE, - SDAP_GROUP_SEARCH_BASE, - SDAP_NETGROUP_SEARCH_BASE, - -1 }; - size_t c; - - for (c = 0; search_base_options[c] != -1; c++) { - if (dp_opt_get_string(opts->basic, search_base_options[c]) == NULL) { - if (naming_context == NULL) { - naming_context = get_naming_context(opts->basic, rootdse); - if (naming_context == NULL) { - DEBUG(1, ("get_naming_context failed.\n")); - ret = EINVAL; - goto done; - } - } - DEBUG(3, ("Setting option [%s] to [%s].\n", - opts->basic[search_base_options[c]].opt_name, - naming_context)); - ret = dp_opt_set_string(opts->basic, search_base_options[c], - naming_context); - if (ret != EOK) { - DEBUG(1, ("dp_opt_set_string failed.\n")); - goto done; - } + if (!opts->search_bases + ||!opts->user_search_bases + || !opts->group_search_bases + || !opts->netgroup_search_bases) { + naming_context = get_naming_context(opts->basic, rootdse); + if (naming_context == NULL) { + DEBUG(1, ("get_naming_context failed.\n")); + ret = EINVAL; + goto done; } } + /* Default */ + if (!opts->search_bases) { + ret = sdap_set_search_base(opts, + SDAP_SEARCH_BASE, + naming_context); + if (ret != EOK) goto done; + } + + /* Users */ + if (!opts->user_search_bases) { + ret = sdap_set_search_base(opts, + SDAP_USER_SEARCH_BASE, + naming_context); + if (ret != EOK) goto done; + } + + /* Groups */ + if (!opts->group_search_bases) { + ret = sdap_set_search_base(opts, + SDAP_GROUP_SEARCH_BASE, + naming_context); + if (ret != EOK) goto done; + } + + /* Netgroups */ + if (!opts->netgroup_search_bases) { + ret = sdap_set_search_base(opts, + SDAP_NETGROUP_SEARCH_BASE, + naming_context); + if (ret != EOK) goto done; + } + ret = EOK; done: diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index cfa9b1f3..1ea57082 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -286,6 +286,12 @@ struct sdap_attr_map { char *name; }; +struct sdap_search_base { + const char *basedn; + int scope; + const char *filter; +}; + struct sdap_options { struct dp_option *basic; struct sdap_attr_map *gen_map; @@ -300,6 +306,11 @@ struct sdap_options { SDAP_SCHEMA_IPA_V1 = 3, /* member/memberof */ SDAP_SCHEMA_AD = 4 /* AD's member/memberof */ } schema_type; + + struct sdap_search_base **search_bases; + struct sdap_search_base **user_search_bases; + struct sdap_search_base **group_search_bases; + struct sdap_search_base **netgroup_search_bases; }; struct sdap_server_opts { -- cgit