]> granicus.if.org Git - sudo/commitdiff
When iterating over returned LDAP entries, keep looking at remaining
authorTodd C. Miller <Todd.Miller@courtesan.com>
Fri, 1 Oct 2010 16:17:01 +0000 (12:17 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Fri, 1 Oct 2010 16:17:01 +0000 (12:17 -0400)
matches even if we have a positive match.  This catches negative
matches that may exist in other entries and more closely match the
sudoers file behavior.

plugins/sudoers/ldap.c

index 140b519cf9837811560beb4eb906419b3b56a115..0bc02042e94c6369945bcd26c34090ab8ae174a1 100644 (file)
 #define SUDO_LDAP_SSL          1
 #define SUDO_LDAP_STARTTLS     2
 
+struct ldap_result_list {
+    struct ldap_result_list *next;
+    LDAPMessage *result;
+};
+
 struct ldap_config_table {
     const char *conf_str;      /* config file string */
     short type;                        /* CONF_BOOL, CONF_INT, CONF_STR */
@@ -1795,12 +1800,13 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
 {
     struct ldap_config_list_str *base;
     LDAP *ld = (LDAP *) nss->handle;
-    LDAPMessage *entry, *result;
+    LDAPMessage *entry, *result, *matching_entry = NULL;
     char *filt;
-    int do_netgr, rc, matched;
+    int do_netgr, rc, allowed = UNSPEC;
     int setenv_implied;
     int ldap_user_matches = FALSE, ldap_host_matches = FALSE;
     struct passwd *pw = list_pw ? list_pw : sudo_user.pw;
+    struct ldap_result_list *rl, *results = NULL;
 
     if (ld == NULL)
        return(ret);
@@ -1810,7 +1816,7 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
        enum def_tupple pwcheck = 
            (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
 
-       for (matched = 0, do_netgr = 0; !matched && do_netgr < 2; do_netgr++) {
+       for (allowed = 0, do_netgr = 0; !allowed && do_netgr < 2; do_netgr++) {
            filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw);
            for (base = ldap_conf.base; base != NULL; base = base->next) {
                result = NULL;
@@ -1834,7 +1840,7 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
                        if (user_uid == 0 || list_pw == NULL ||
                            user_uid == list_pw->pw_uid ||
                            sudo_ldap_check_command(ld, entry, NULL)) {
-                           matched = 1;
+                           allowed = 1;
                            break;      /* end foreach */
                        }
                    }
@@ -1843,7 +1849,7 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
            }
            efree(filt);
        }
-       if (matched || user_uid == 0) {
+       if (allowed || user_uid == 0) {
            SET(ret, VALIDATE_OK);
            CLR(ret, VALIDATE_NOT_OK);
            if (def_authenticate) {
@@ -1882,10 +1888,10 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
      * try to match them against the username.
      */
     setenv_implied = FALSE;
-    for (matched = 0, do_netgr = 0; !matched && do_netgr < 2; do_netgr++) {
+    for (do_netgr = 0; allowed != FALSE && do_netgr < 2; do_netgr++) {
        filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw);
        DPRINTF(("ldap search '%s'", filt), 1);
-       for (base = ldap_conf.base; base != NULL; base = base->next) {
+       for (base = ldap_conf.base; allowed != FALSE && base != NULL; base = base->next) {
            result = NULL;
            rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, filt,
                NULL, 0, NULL, NULL, NULL, 0, &result);
@@ -1894,6 +1900,12 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
                continue;
            }
 
+           /* Add result to list for later free()ing. */
+           rl = emalloc(sizeof(*rl));
+           rl->result = result;
+           rl->next = results;
+           results = rl;
+
            /* parse each entry returned from this most recent search */
            LDAP_FOREACH(entry, ld, result) {
                DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
@@ -1913,34 +1925,46 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
                    ) {
                    /* We have a match! */
                    DPRINTF(("Command %sallowed", rc == TRUE ? "" : "NOT "), 1);
-                   matched = TRUE;
-                   if (rc == TRUE) {
-                       /* pick up any options */
-                       if (setenv_implied)
-                           def_setenv = TRUE;
-                       sudo_ldap_parse_options(ld, entry);
-#ifdef HAVE_SELINUX
-                       /* Set role and type if not specified on command line. */
-                       if (user_role == NULL)
-                           user_role = def_role;
-                       if (user_type == NULL)
-                           user_type = def_type;
-#endif /* HAVE_SELINUX */
-                       /* make sure we don't reenter loop */
-                       SET(ret, VALIDATE_OK);
-                       CLR(ret, VALIDATE_NOT_OK);
-                   } else {
-                       SET(ret, VALIDATE_NOT_OK);
-                       CLR(ret, VALIDATE_OK);
+                   if (rc == FALSE) {
+                       /* Command explicitly denied, we are done. */
+                       allowed = FALSE;
+                       break;
+                   } else if (rc == TRUE && allowed == UNSPEC) {
+                       /* Command allowed, no other matches yet. */
+                       allowed = TRUE;
+                       matching_entry = entry;
                    }
-                   /* break from inside for loop */
-                   break;
                }
            }
-           ldap_msgfree(result);
        }
        efree(filt);
     }
+    if (allowed == TRUE) {
+       SET(ret, VALIDATE_OK);
+       CLR(ret, VALIDATE_NOT_OK);
+
+       /* Set options based on matching entry. */
+       if (setenv_implied)
+           def_setenv = TRUE;
+       sudo_ldap_parse_options(ld, matching_entry);
+#ifdef HAVE_SELINUX
+       /* Set role and type if not specified on command line. */
+       if (user_role == NULL)
+           user_role = def_role;
+       if (user_type == NULL)
+           user_type = def_type;
+#endif /* HAVE_SELINUX */
+    } else if (allowed == FALSE) {
+       SET(ret, VALIDATE_NOT_OK);
+       CLR(ret, VALIDATE_OK);
+    }
+
+    /* Free all results. */
+    while ((rl = results) != NULL) {
+       results = results->next;
+       ldap_msgfree(rl->result);
+       efree(rl);
+    }
 
 done:
     DPRINTF(("user_matches=%d", ldap_user_matches), 1);