From: Todd C. Miller Date: Tue, 15 Jun 2010 14:05:08 +0000 (-0400) Subject: Add support for multiple sudoers_base entries in ldap.conf. X-Git-Tag: SUDO_1_7_3~60 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=805690d1afd65987540899ae7b6ed597192fb121;p=sudo Add support for multiple sudoers_base entries in ldap.conf. From Joachim Henke --HG-- branch : 1.7 --- diff --git a/WHATSNEW b/WHATSNEW index 1615adc1b..f0d7cc8d7 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -14,6 +14,10 @@ What's new in Sudo 1.7.3? Mac OS X, and Linux systems with the devpts filesystem (pseudo-ttys only). + * Support for multiple 'sudoers_base' entries in ldap.conf. When + multiple bases are listed, sudo will try each one in the order + in which they are specified. + * Sudo's SELinux support should now function correctly when running commands as a non-root user and when one of stdin, stdout or stderr is not a terminal. diff --git a/ldap.c b/ldap.c index 56967e1a0..e99b75395 100644 --- a/ldap.c +++ b/ldap.c @@ -112,6 +112,7 @@ #define CONF_BOOL 0 #define CONF_INT 1 #define CONF_STR 2 +#define CONF_LIST_STR 4 #define SUDO_LDAP_SSL 1 #define SUDO_LDAP_STARTTLS 2 @@ -124,6 +125,11 @@ struct ldap_config_table { void *valp; /* pointer into ldap_conf */ }; +struct ldap_config_list_str { + struct ldap_config_list_str *next; + char val[1]; +}; + /* ldap configuration structure */ static struct ldap_config { int port; @@ -141,7 +147,7 @@ static struct ldap_config { char *binddn; char *bindpw; char *rootbinddn; - char *base; + struct ldap_config_list_str *base; char *ssl; char *tls_cacertfile; char *tls_cacertdir; @@ -214,7 +220,7 @@ static struct ldap_config_table ldap_conf_table[] = { { "binddn", CONF_STR, FALSE, -1, &ldap_conf.binddn }, { "bindpw", CONF_STR, FALSE, -1, &ldap_conf.bindpw }, { "rootbinddn", CONF_STR, FALSE, -1, &ldap_conf.rootbinddn }, - { "sudoers_base", CONF_STR, FALSE, -1, &ldap_conf.base }, + { "sudoers_base", CONF_LIST_STR, FALSE, -1, &ldap_conf.base }, #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 }, @@ -969,6 +975,21 @@ sudo_ldap_read_config() efree(*(char **)(cur->valp)); *(char **)(cur->valp) = estrdup(value); break; + case CONF_LIST_STR: + { + struct ldap_config_list_str **p; + size_t len = strlen(value); + + if (len > 0) { + p = (struct ldap_config_list_str **)cur->valp; + while (*p != NULL) + p = &(*p)->next; + *p = emalloc(sizeof(struct ldap_config_list_str) + len); + memcpy((*p)->val, value, len + 1); + (*p)->next = NULL; + } + } + break; } break; } @@ -993,9 +1014,16 @@ sudo_ldap_read_config() fprintf(stderr, "port %d\n", ldap_conf.port); } fprintf(stderr, "ldap_version %d\n", ldap_conf.version); + if (ldap_conf.base) { + struct ldap_config_list_str *base = ldap_conf.base; - fprintf(stderr, "sudoers_base %s\n", ldap_conf.base ? - ldap_conf.base : "(NONE) <---Sudo will ignore ldap)"); + do { + fprintf(stderr, "sudoers_base %s\n", base->val); + } while ((base = base->next) != NULL); + } else { + fprintf(stderr, "sudoers_base %s\n", + "(NONE) <---Sudo will ignore ldap)"); + } fprintf(stderr, "binddn %s\n", ldap_conf.binddn ? ldap_conf.binddn : "(anonymous)"); fprintf(stderr, "bindpw %s\n", ldap_conf.bindpw ? @@ -1154,30 +1182,34 @@ sudo_ldap_display_defaults(nss, pw, lbuf) struct lbuf *lbuf; { struct berval **bv, **p; + struct ldap_config_list_str *base; LDAP *ld = (LDAP *) nss->handle; - LDAPMessage *entry = NULL, *result = NULL; - char *prefix = NULL; + LDAPMessage *entry, *result; + char *prefix; int rc, count = 0; if (ld == NULL) return(-1); - rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, - "cn=defaults", NULL, 0, NULL, NULL, NULL, 0, &result); - if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) { - bv = ldap_get_values_len(ld, entry, "sudoOption"); - if (bv != NULL) { - prefix = " "; - for (p = bv; *p != NULL; p++) { - lbuf_append(lbuf, prefix, (*p)->bv_val, NULL); - prefix = ", "; - count++; + for (base = ldap_conf.base; base != NULL; base = base->next) { + result = NULL; + rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, + "cn=defaults", NULL, 0, NULL, NULL, NULL, 0, &result); + if (rc == LDAP_SUCCESS && (entry = ldap_first_entry(ld, result))) { + bv = ldap_get_values_len(ld, entry, "sudoOption"); + if (bv != NULL) { + prefix = " "; + for (p = bv; *p != NULL; p++) { + lbuf_append(lbuf, prefix, (*p)->bv_val, NULL); + prefix = ", "; + count++; + } + ldap_value_free_len(bv); } - ldap_value_free_len(bv); } + if (result) + ldap_msgfree(result); } - if (result) - ldap_msgfree(result); return(count); } @@ -1360,8 +1392,9 @@ sudo_ldap_display_privs(nss, pw, lbuf) struct passwd *pw; struct lbuf *lbuf; { + struct ldap_config_list_str *base; LDAP *ld = (LDAP *) nss->handle; - LDAPMessage *entry = NULL, *result = NULL; + LDAPMessage *entry, *result; char *filt; int rc, do_netgr, count = 0; @@ -1385,26 +1418,28 @@ sudo_ldap_display_privs(nss, pw, lbuf) for (do_netgr = 0; do_netgr < 2; do_netgr++) { filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw); DPRINTF(("ldap search '%s'", filt), 1); - rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt, - NULL, 0, NULL, NULL, NULL, 0, &result); - efree(filt); - if (rc != LDAP_SUCCESS) - continue; /* no entries for this pass */ - - /* print each matching entry */ - LDAP_FOREACH(entry, ld, result) { - if ((!do_netgr || - sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) && - sudo_ldap_check_host(ld, entry)) { - - if (long_list) - count += sudo_ldap_display_entry_long(ld, entry, lbuf); - else - count += sudo_ldap_display_entry_short(ld, entry, lbuf); + for (base = ldap_conf.base; 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); + if (rc != LDAP_SUCCESS) + continue; /* no entries for this pass */ + + /* print each matching entry */ + LDAP_FOREACH(entry, ld, result) { + if ((!do_netgr || + sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) && + sudo_ldap_check_host(ld, entry)) { + + if (long_list) + count += sudo_ldap_display_entry_long(ld, entry, lbuf); + else + count += sudo_ldap_display_entry_short(ld, entry, lbuf); + } } + ldap_msgfree(result); } - ldap_msgfree(result); - result = NULL; + efree(filt); } return(count); } @@ -1414,8 +1449,9 @@ sudo_ldap_display_cmnd(nss, pw) struct sudo_nss *nss; struct passwd *pw; { + struct ldap_config_list_str *base; LDAP *ld = (LDAP *) nss->handle; - LDAPMessage *entry = NULL, *result = NULL; /* used for searches */ + LDAPMessage *entry, *result; /* used for searches */ char *filt; /* used to parse attributes */ int rc, found, do_netgr; /* temp/final return values */ @@ -1439,25 +1475,27 @@ sudo_ldap_display_cmnd(nss, pw) for (found = FALSE, do_netgr = 0; !found && do_netgr < 2; do_netgr++) { filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw); DPRINTF(("ldap search '%s'", filt), 1); - rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt, - NULL, 0, NULL, NULL, NULL, 0, &result); - efree(filt); - if (rc != LDAP_SUCCESS) - continue; /* no entries for this pass */ + for (base = ldap_conf.base; 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); + if (rc != LDAP_SUCCESS) + continue; /* no entries for this pass */ - LDAP_FOREACH(entry, ld, result) { - if ((!do_netgr || - sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) && - sudo_ldap_check_host(ld, entry) && - sudo_ldap_check_command(ld, entry, NULL) && - sudo_ldap_check_runas(ld, entry)) { + LDAP_FOREACH(entry, ld, result) { + if ((!do_netgr || + sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) && + sudo_ldap_check_host(ld, entry) && + sudo_ldap_check_command(ld, entry, NULL) && + sudo_ldap_check_runas(ld, entry)) { - found = TRUE; - break; + found = TRUE; + break; + } } + ldap_msgfree(result); } - ldap_msgfree(result); - result = NULL; + efree(filt); } if (found) @@ -1740,23 +1778,27 @@ static int sudo_ldap_setdefs(nss) struct sudo_nss *nss; { + struct ldap_config_list_str *base; LDAP *ld = (LDAP *) nss->handle; - LDAPMessage *entry = NULL, *result = NULL; /* used for searches */ + LDAPMessage *entry, *result; /* used for searches */ int rc; /* temp return value */ if (ld == NULL) return(-1); - rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, - "cn=defaults", NULL, 0, NULL, NULL, NULL, 0, &result); - if (rc == 0 && (entry = ldap_first_entry(ld, result))) { - DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1); - sudo_ldap_parse_options(ld, entry); - } else - DPRINTF(("no default options found!"), 1); - - if (result) - ldap_msgfree(result); + for (base = ldap_conf.base; base != NULL; base = base->next) { + result = NULL; + rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, + "cn=defaults", 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); + } else + DPRINTF(("no default options found in %s", base->val), 1); + + if (result) + ldap_msgfree(result); + } return(0); } @@ -1770,8 +1812,9 @@ sudo_ldap_lookup(nss, ret, pwflag) int ret; int pwflag; { + struct ldap_config_list_str *base; LDAP *ld = (LDAP *) nss->handle; - LDAPMessage *entry = NULL, *result = NULL; + LDAPMessage *entry, *result; char *filt; int do_netgr, rc, matched; int setenv_implied; @@ -1788,34 +1831,36 @@ sudo_ldap_lookup(nss, ret, pwflag) for (matched = 0, do_netgr = 0; !matched && do_netgr < 2; do_netgr++) { filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw); - rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt, - NULL, 0, NULL, NULL, NULL, 0, &result); - efree(filt); - if (rc != LDAP_SUCCESS) - continue; - - LDAP_FOREACH(entry, ld, result) { - /* only verify netgroup matches in pass 2 */ - if (do_netgr && !sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) + for (base = ldap_conf.base; 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); + if (rc != LDAP_SUCCESS) continue; - ldap_user_matches = TRUE; - if (sudo_ldap_check_host(ld, entry)) { - ldap_host_matches = TRUE; - if ((pwcheck == any && doauth != FALSE) || - (pwcheck == all && doauth == FALSE)) - doauth = sudo_ldap_check_bool(ld, entry, "authenticate"); - /* Only check the command when listing another user. */ - if (user_uid == 0 || list_pw == NULL || - user_uid == list_pw->pw_uid || - sudo_ldap_check_command(ld, entry, NULL)) { - matched = 1; - break; /* end foreach */ + LDAP_FOREACH(entry, ld, result) { + /* only verify netgroup matches in pass 2 */ + if (do_netgr && !sudo_ldap_check_user_netgroup(ld, entry, pw->pw_name)) + continue; + + ldap_user_matches = TRUE; + if (sudo_ldap_check_host(ld, entry)) { + ldap_host_matches = TRUE; + if ((pwcheck == any && doauth != FALSE) || + (pwcheck == all && doauth == FALSE)) + doauth = sudo_ldap_check_bool(ld, entry, "authenticate"); + /* Only check the command when listing another user. */ + if (user_uid == 0 || list_pw == NULL || + user_uid == list_pw->pw_uid || + sudo_ldap_check_command(ld, entry, NULL)) { + matched = 1; + break; /* end foreach */ + } } } + ldap_msgfree(result); } - ldap_msgfree(result); - result = NULL; + efree(filt); } if (matched || user_uid == 0) { SET(ret, VALIDATE_OK); @@ -1859,14 +1904,16 @@ sudo_ldap_lookup(nss, ret, pwflag) for (matched = 0, do_netgr = 0; !matched && do_netgr < 2; do_netgr++) { filt = do_netgr ? estrdup("sudoUser=+*") : sudo_ldap_build_pass1(pw); DPRINTF(("ldap search '%s'", filt), 1); - rc = ldap_search_ext_s(ld, ldap_conf.base, LDAP_SCOPE_SUBTREE, filt, - NULL, 0, NULL, NULL, NULL, 0, &result); - if (rc != LDAP_SUCCESS) - DPRINTF(("nothing found for '%s'", filt), 1); - efree(filt); + for (base = ldap_conf.base; 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); + if (rc != LDAP_SUCCESS) { + DPRINTF(("nothing found for '%s'", filt), 1); + continue; + } - /* parse each entry returned from this most recent search */ - if (rc == LDAP_SUCCESS) { + /* parse each entry returned from this most recent search */ LDAP_FOREACH(entry, ld, result) { DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1); if ( @@ -1910,8 +1957,8 @@ sudo_ldap_lookup(nss, ret, pwflag) } } ldap_msgfree(result); - result = NULL; } + efree(filt); } done: diff --git a/sudoers.ldap.cat b/sudoers.ldap.cat index 45f7ceb88..25281eec9 100644 --- a/sudoers.ldap.cat +++ b/sudoers.ldap.cat @@ -61,7 +61,7 @@ DDEESSCCRRIIPPTTIIOONN -1.7.3b3 June 14, 2010 1 +1.7.3b3 June 15, 2010 1 @@ -127,7 +127,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) -1.7.3b3 June 14, 2010 2 +1.7.3b3 June 15, 2010 2 @@ -193,7 +193,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) -1.7.3b3 June 14, 2010 3 +1.7.3b3 June 15, 2010 3 @@ -259,7 +259,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) -1.7.3b3 June 14, 2010 4 +1.7.3b3 June 15, 2010 4 @@ -301,7 +301,8 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) SSUUDDOOEERRSS__BBAASSEE base The base DN to use when performing ssuuddoo LDAP queries. Typically this is of the form ou=SUDOers,dc=example,dc=com for the domain - example.com. + example.com. Multiple SSUUDDOOEERRSS__BBAASSEE lines may be specified, in + which case they are queried in the order specified. SSUUDDOOEERRSS__DDEEBBUUGG debug_level This sets the debug level for ssuuddoo LDAP queries. Debugging @@ -321,11 +322,10 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) BBIINNDDPPWW secret The BBIINNDDPPWW parameter specifies the password to use when performing LDAP operations. This is typically used in conjunction with the - BBIINNDDDDNN parameter. -1.7.3b3 June 14, 2010 5 +1.7.3b3 June 15, 2010 5 @@ -334,6 +334,8 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) + BBIINNDDDDNN parameter. + RROOOOTTBBIINNDDDDNN DN The RROOOOTTBBIINNDDDDNN parameter specifies the identity, in the form of a Distinguished Name (DN), to use when performing privileged LDAP @@ -387,11 +389,9 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) OpenLDAP: tls_cert /etc/ssl/client_cert.pem - Netscape-derived: - -1.7.3b3 June 14, 2010 6 +1.7.3b3 June 15, 2010 6 @@ -400,6 +400,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) + Netscape-derived: tls_cert /var/ldap/cert7.db When using Netscape-derived libraries, this file may also contain @@ -456,8 +457,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) - -1.7.3b3 June 14, 2010 7 +1.7.3b3 June 15, 2010 7 @@ -523,7 +523,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) -1.7.3b3 June 14, 2010 8 +1.7.3b3 June 15, 2010 8 @@ -569,7 +569,7 @@ EEXXAAMMPPLLEESS # The amount of time, in seconds, to wait while performing an LDAP query. timelimit 30 # - # must be set or sudo will ignore LDAP + # Must be set or sudo will ignore LDAP; may be specified multiple times. sudoers_base ou=SUDOers,dc=example,dc=com # # verbose sudoers matching from ldap @@ -589,7 +589,7 @@ EEXXAAMMPPLLEESS -1.7.3b3 June 14, 2010 9 +1.7.3b3 June 15, 2010 9 @@ -655,7 +655,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) -1.7.3b3 June 14, 2010 10 +1.7.3b3 June 15, 2010 10 @@ -721,7 +721,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4) -1.7.3b3 June 14, 2010 11 +1.7.3b3 June 15, 2010 11 @@ -787,6 +787,6 @@ DDIISSCCLLAAIIMMEERR -1.7.3b3 June 14, 2010 12 +1.7.3b3 June 15, 2010 12 diff --git a/sudoers.ldap.man.in b/sudoers.ldap.man.in index 3c7eb9a85..0013b7f34 100644 --- a/sudoers.ldap.man.in +++ b/sudoers.ldap.man.in @@ -140,7 +140,7 @@ .\" ======================================================================== .\" .IX Title "SUDOERS.LDAP @mansectform@" -.TH SUDOERS.LDAP @mansectform@ "June 14, 2010" "1.7.3b3" "MAINTENANCE COMMANDS" +.TH SUDOERS.LDAP @mansectform@ "June 15, 2010" "1.7.3b3" "MAINTENANCE COMMANDS" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -401,7 +401,8 @@ to wait for a response to an \s-1LDAP\s0 query. .IX Item "SUDOERS_BASE base" The base \s-1DN\s0 to use when performing \fBsudo\fR \s-1LDAP\s0 queries. Typically this is of the form \f(CW\*(C`ou=SUDOers,dc=example,dc=com\*(C'\fR for the domain -\&\f(CW\*(C`example.com\*(C'\fR. +\&\f(CW\*(C`example.com\*(C'\fR. Multiple \fB\s-1SUDOERS_BASE\s0\fR lines may be specified, +in which case they are queried in the order specified. .IP "\fB\s-1SUDOERS_DEBUG\s0\fR debug_level" 4 .IX Item "SUDOERS_DEBUG debug_level" This sets the debug level for \fBsudo\fR \s-1LDAP\s0 queries. Debugging @@ -647,7 +648,7 @@ determines sudoers source order on \s-1AIX\s0 \& # The amount of time, in seconds, to wait while performing an LDAP query. \& timelimit 30 \& # -\& # must be set or sudo will ignore LDAP +\& # Must be set or sudo will ignore LDAP; may be specified multiple times. \& sudoers_base ou=SUDOers,dc=example,dc=com \& # \& # verbose sudoers matching from ldap diff --git a/sudoers.ldap.pod b/sudoers.ldap.pod index 4b4533106..7dabfaa90 100644 --- a/sudoers.ldap.pod +++ b/sudoers.ldap.pod @@ -301,7 +301,8 @@ to wait for a response to an LDAP query. The base DN to use when performing B LDAP queries. Typically this is of the form C for the domain -C. +C. Multiple B lines may be specified, +in which case they are queried in the order specified. =item B debug_level @@ -559,7 +560,7 @@ determines sudoers source order on AIX # The amount of time, in seconds, to wait while performing an LDAP query. timelimit 30 # - # must be set or sudo will ignore LDAP + # Must be set or sudo will ignore LDAP; may be specified multiple times. sudoers_base ou=SUDOers,dc=example,dc=com # # verbose sudoers matching from ldap