diff --git a/doc/sudoers.ldap.pod b/doc/sudoers.ldap.pod index 0d1b473a2..a9816546b 100644 --- a/doc/sudoers.ldap.pod +++ b/doc/sudoers.ldap.pod @@ -369,6 +369,12 @@ this is of the form C for the domain C. Multiple B lines may be specified, in which case they are queried in the order specified. +=item B ldap_filter + +An LDAP filter which is used to restrict the set of records returned +when performing a B LDAP query. Typically, this is of the +form C or C<(&(attribute=value)(attribute2=value2))>. + =item B on/true/yes/off/false/no Whether or not to evaluate the C and C diff --git a/plugins/sudoers/ldap.c b/plugins/sudoers/ldap.c index c09046f03..ab95083b9 100644 --- a/plugins/sudoers/ldap.c +++ b/plugins/sudoers/ldap.c @@ -201,6 +201,7 @@ static struct ldap_config { char *bindpw; char *rootbinddn; struct ldap_config_list_str *base; + char *search_filter; char *ssl; char *tls_cacertfile; char *tls_cacertdir; @@ -285,6 +286,7 @@ static struct ldap_config_table ldap_conf_table[] = { { "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn }, { "sudoers_base", CONF_LIST_STR, FALSE, -1, &ldap_conf.base }, { "sudoers_timed", CONF_BOOL, FALSE, -1, &ldap_conf.timed }, + { "sudoers_search_filter", CONF_STR, FALSE, -1, &ldap_conf.search_filter }, #ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S { "use_sasl", CONF_BOOL, FALSE, -1, &ldap_conf.use_sasl }, { "sasl_auth_id", CONF_STR, FALSE, -1, &ldap_conf.sasl_auth_id }, @@ -949,6 +951,21 @@ done: return bytes; } +/* + * Builds up a filter to search for default settings + */ +static char * +sudo_ldap_build_default_filter() +{ + char *filt; + + if (ldap_conf.search_filter) + easprintf(&filt, "(&(%s)(cn=defaults))", ldap_conf.search_filter); + else + filt = estrdup("cn=defaults"); + return filt; +} + /* * Builds up a filter to check against LDAP. */ @@ -957,11 +974,15 @@ sudo_ldap_build_pass1(struct passwd *pw) { struct group *grp; char *buf, timebuffer[TIMEFILTER_LENGTH]; - size_t sz; + size_t sz = 0; int i; - /* Start with (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */ - sz = 29 + strlen(pw->pw_name); + /* Start with LDAP search filter length + 3 */ + if (ldap_conf.search_filter) + sz += strlen(ldap_conf.search_filter) + 3; + + /* Then add (|(sudoUser=USERNAME)(sudoUser=ALL)) + NUL */ + sz += 29 + strlen(pw->pw_name); /* Add space for groups */ if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) { @@ -984,12 +1005,15 @@ sudo_ldap_build_pass1(struct passwd *pw) *buf = '\0'; /* - * If timed, start a global AND clause that will have the time limits - * as the second leg. + * If timed or using a search filter, start a global AND clause to + * contain the search filter, search criteria, and time restriction. */ - if (ldap_conf.timed) + if (ldap_conf.timed || ldap_conf.search_filter) (void) strlcpy(buf, "(&", sz); + if (ldap_conf.search_filter) + (void) strlcat(buf, ldap_conf.search_filter, sz); + /* Global OR + sudoUser=user_name filter */ (void) strlcat(buf, "(|(sudoUser=", sz); (void) strlcat(buf, pw->pw_name, sz); @@ -1024,6 +1048,8 @@ sudo_ldap_build_pass1(struct passwd *pw) strlcat(buf, ")", sz); /* closes the global OR */ sudo_ldap_timefilter(timebuffer, sizeof(timebuffer)); strlcat(buf, timebuffer, sz); + } else if (ldap_conf.search_filter) { + strlcat(buf, ")", sz); /* closes the global OR */ } strlcat(buf, ")", sz); /* closes the global OR or the global AND */ @@ -1036,21 +1062,23 @@ sudo_ldap_build_pass1(struct passwd *pw) static char * sudo_ldap_build_pass2(void) { - char *buf, timebuffer[TIMEFILTER_LENGTH]; + char *filt, timebuffer[TIMEFILTER_LENGTH]; - if (ldap_conf.timed) { - /* - * If timed, use a global AND clause that has the time limit as - * as the second leg. - */ + if (ldap_conf.timed) sudo_ldap_timefilter(timebuffer, sizeof(timebuffer)); - easprintf(&buf, "(&(sudoUser=+*)%s)", timebuffer); - } else { - /* No time limit, just the netgroup selection. */ - buf = estrdup("sudoUser=+*"); - } - return buf; + /* + * Match all sudoUsers beginning with a '+'. + * If a search filter or time restriction is specified, + * those get ANDed in to the expression. + */ + easprintf(&filt, "%s%s(sudoUser=+*)%s%s", + (ldap_conf.timed || ldap_conf.search_filter) ? "(&" : "", + ldap_conf.search_filter ? ldap_conf.search_filter : "", + ldap_conf.timed ? timebuffer : "", + (ldap_conf.timed || ldap_conf.search_filter) ? ")" : ""); + + return filt; } static void @@ -1178,6 +1206,10 @@ sudo_ldap_read_config(void) sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base %s\n", "(NONE: LDAP disabled)"); } + if (ldap_conf.search_filter) { + sudo_printf(SUDO_CONV_ERROR_MSG, "search_filter %s\n", + ldap_conf.search_filter); + } sudo_printf(SUDO_CONV_ERROR_MSG, "binddn %s\n", ldap_conf.binddn ? ldap_conf.binddn : "(anonymous)"); sudo_printf(SUDO_CONV_ERROR_MSG, "bindpw %s\n", @@ -1188,7 +1220,7 @@ sudo_ldap_read_config(void) } if (ldap_conf.timelimit > 0) { sudo_printf(SUDO_CONV_ERROR_MSG, "timelimit %d\n", - ldap_conf.timelimit); + ldap_conf.timelimit); } sudo_printf(SUDO_CONV_ERROR_MSG, "ssl %s\n", ldap_conf.ssl ? ldap_conf.ssl : "(no)"); @@ -1291,6 +1323,18 @@ sudo_ldap_read_config(void) #endif } + /* If search filter is not parenthesized, make it so. */ + if (ldap_conf.search_filter && ldap_conf.search_filter[0] != '(') { + size_t len = strlen(ldap_conf.search_filter); + cp = ldap_conf.search_filter; + ldap_conf.search_filter = emalloc(len + 3); + ldap_conf.search_filter[0] = '('; + memcpy(ldap_conf.search_filter + 1, cp, len); + ldap_conf.search_filter[len + 1] = ')'; + ldap_conf.search_filter[len + 2] = '\0'; + efree(cp); + } + /* If rootbinddn set, read in /etc/ldap.secret if it exists. */ if (ldap_conf.rootbinddn) sudo_ldap_read_secret(_PATH_LDAP_SECRET); @@ -1361,13 +1405,14 @@ sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw, struct sudo_ldap_handle *handle = nss->handle; LDAP *ld; LDAPMessage *entry, *result; - char *prefix; + char *prefix, *filt; int rc, count = 0; if (handle == NULL || handle->ld == NULL) goto done; ld = handle->ld; + filt = sudo_ldap_build_default_filter(); for (base = ldap_conf.base; base != NULL; base = base->next) { if (ldap_conf.timeout > 0) { tv.tv_sec = ldap_conf.timeout; @@ -1376,7 +1421,7 @@ sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw, } result = NULL; rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, - "cn=defaults", NULL, 0, NULL, NULL, tvp, 0, &result); + filt, NULL, 0, NULL, NULL, tvp, 0, &result); if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) { bv = ldap_get_values_len(ld, entry, "sudoOption"); if (bv != NULL) { @@ -1395,6 +1440,7 @@ sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw, if (result) ldap_msgfree(result); } + efree(filt); done: return count; } @@ -2005,12 +2051,16 @@ sudo_ldap_setdefs(struct sudo_nss *nss) struct timeval tv, *tvp = NULL; LDAP *ld; LDAPMessage *entry, *result; + char *filt; int rc; if (handle == NULL || handle->ld == NULL) return -1; ld = handle->ld; + filt = sudo_ldap_build_default_filter(); + DPRINTF(("Looking for cn=defaults: %s", filt), 1); + for (base = ldap_conf.base; base != NULL; base = base->next) { if (ldap_conf.timeout > 0) { tv.tv_sec = ldap_conf.timeout; @@ -2019,7 +2069,7 @@ sudo_ldap_setdefs(struct sudo_nss *nss) } result = NULL; rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, - "cn=defaults", NULL, 0, NULL, NULL, NULL, 0, &result); + filt, NULL, 0, NULL, NULL, NULL, 0, &result); if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) { DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1); sudo_ldap_parse_options(ld, entry); @@ -2029,6 +2079,7 @@ sudo_ldap_setdefs(struct sudo_nss *nss) if (result) ldap_msgfree(result); } + efree(filt); return 0; } @@ -2062,7 +2113,7 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag) enum def_tuple pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; - for (i = 0; i < lres->nentries; i++) { + for (i = 0; i < lres->nentries; i++) { entry = lres->entries[i].entry; if ((pwcheck == any && doauth != FALSE) || (pwcheck == all && doauth == FALSE)) { @@ -2178,7 +2229,7 @@ sudo_ldap_result_last_search(struct ldap_result *lres) struct ldap_search_list *result = lres->searches; if (result) { - while (result->next) + while (result->next) result = result->next; } return result;