]> granicus.if.org Git - sudo/commitdiff
Add support for sudoers_search_filter setting in ldap.conf. This
authorTodd C. Miller <Todd.Miller@courtesan.com>
Fri, 18 Mar 2011 13:38:42 +0000 (09:38 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Fri, 18 Mar 2011 13:38:42 +0000 (09:38 -0400)
can be used to restrict the set of records returned by the LDAP
query.

doc/sudoers.ldap.pod
plugins/sudoers/ldap.c

index 0d1b473a2e302f8bc8bc427efd664e48f1ceddd1..a9816546b348bdcb7e308d0cb4724cb4042ca08d 100644 (file)
@@ -369,6 +369,12 @@ this is of the form C<ou=SUDOers,dc=example,dc=com> for the domain
 C<example.com>.  Multiple B<SUDOERS_BASE> lines may be specified,
 in which case they are queried in the order specified.
 
+=item B<SUDOERS_SEARCH_FILTER> ldap_filter
+
+An LDAP filter which is used to restrict the set of records returned
+when performing a B<sudo> LDAP query.  Typically, this is of the
+form C<attribute=value> or C<(&(attribute=value)(attribute2=value2))>.
+
 =item B<SUDOERS_TIMED> on/true/yes/off/false/no
 
 Whether or not to evaluate the C<sudoNotBefore> and C<sudoNotAfter>
index c09046f039abbed77da497a695578ad6c48d17f0..ab95083b97bd4378614b0ef7ad1018803f5d2e68 100644 (file)
@@ -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;