Add support for multiple sudoers_base entries in ldap.conf.

From Joachim Henke
This commit is contained in:
Todd C. Miller
2010-06-15 10:33:30 -04:00
parent 54ffb19ccb
commit d92c82ea3f
5 changed files with 174 additions and 120 deletions

View File

@@ -22,6 +22,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
that 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.

View File

@@ -61,7 +61,7 @@ DDEESSCCRRIIPPTTIIOONN
1.8.0b1 June 11, 2010 1
1.8.0b1 June 15, 2010 1
@@ -127,7 +127,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4)
1.8.0b1 June 11, 2010 2
1.8.0b1 June 15, 2010 2
@@ -193,7 +193,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4)
1.8.0b1 June 11, 2010 3
1.8.0b1 June 15, 2010 3
@@ -259,7 +259,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4)
1.8.0b1 June 11, 2010 4
1.8.0b1 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.8.0b1 June 11, 2010 5
1.8.0b1 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.8.0b1 June 11, 2010 6
1.8.0b1 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.8.0b1 June 11, 2010 7
1.8.0b1 June 15, 2010 7
@@ -523,7 +523,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4)
1.8.0b1 June 11, 2010 8
1.8.0b1 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.8.0b1 June 11, 2010 9
1.8.0b1 June 15, 2010 9
@@ -655,7 +655,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4)
1.8.0b1 June 11, 2010 10
1.8.0b1 June 15, 2010 10
@@ -721,7 +721,7 @@ SUDOERS.LDAP(4) MAINTENANCE COMMANDS SUDOERS.LDAP(4)
1.8.0b1 June 11, 2010 11
1.8.0b1 June 15, 2010 11
@@ -787,6 +787,6 @@ DDIISSCCLLAAIIMMEERR
1.8.0b1 June 11, 2010 12
1.8.0b1 June 15, 2010 12

View File

@@ -140,7 +140,7 @@
.\" ========================================================================
.\"
.IX Title "SUDOERS.LDAP @mansectform@"
.TH SUDOERS.LDAP @mansectform@ "June 11, 2010" "1.8.0b1" "MAINTENANCE COMMANDS"
.TH SUDOERS.LDAP @mansectform@ "June 15, 2010" "1.8.0b1" "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

View File

@@ -301,7 +301,8 @@ to wait for a response to an LDAP query.
The base DN to use when performing B<sudo> LDAP queries. Typically
this is of the form C<ou=SUDOers,dc=example,dc=com> for the domain
C<example.com>.
C<example.com>. Multiple B<SUDOERS_BASE> lines may be specified,
in which case they are queried in the order specified.
=item B<SUDOERS_DEBUG> 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

View File

@@ -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 },
@@ -904,6 +910,21 @@ sudo_ldap_read_config(void)
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;
}
@@ -932,8 +953,16 @@ sudo_ldap_read_config(void)
sudo_printf(SUDO_CONV_ERROR_MSG, "ldap_version %d\n",
ldap_conf.version);
if (ldap_conf.base) {
struct ldap_config_list_str *base = ldap_conf.base;
do {
sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base %s\n",
ldap_conf.base ? ldap_conf.base : "(NONE: LDAP disabled)");
base->val);
} while ((base = base->next) != NULL);
} else {
sudo_printf(SUDO_CONV_ERROR_MSG, "sudoers_base %s\n",
"(NONE: LDAP disabled)");
}
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",
@@ -1106,15 +1135,18 @@ sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
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,
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");
@@ -1133,6 +1165,7 @@ sudo_ldap_display_defaults(struct sudo_nss *nss, struct passwd *pw,
}
if (result)
ldap_msgfree(result);
}
return(count);
}
@@ -1305,8 +1338,9 @@ static int
sudo_ldap_display_privs(struct sudo_nss *nss, 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;
@@ -1330,9 +1364,10 @@ sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
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,
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);
efree(filt);
if (rc != LDAP_SUCCESS)
continue; /* no entries for this pass */
@@ -1349,7 +1384,8 @@ sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
}
}
ldap_msgfree(result);
result = NULL;
}
efree(filt);
}
return(count);
}
@@ -1357,8 +1393,9 @@ sudo_ldap_display_privs(struct sudo_nss *nss, struct passwd *pw,
static int
sudo_ldap_display_cmnd(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 */
@@ -1382,9 +1419,10 @@ sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *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,
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);
efree(filt);
if (rc != LDAP_SUCCESS)
continue; /* no entries for this pass */
@@ -1400,7 +1438,8 @@ sudo_ldap_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
}
}
ldap_msgfree(result);
result = NULL;
}
efree(filt);
}
if (found)
@@ -1676,23 +1715,27 @@ sudo_ldap_open(struct sudo_nss *nss)
static int
sudo_ldap_setdefs(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,
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 == 0 && (entry = ldap_first_entry(ld, 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!"), 1);
DPRINTF(("no default options found in %s", base->val), 1);
if (result)
ldap_msgfree(result);
}
return(0);
}
@@ -1703,8 +1746,9 @@ sudo_ldap_setdefs(struct sudo_nss *nss)
static int
sudo_ldap_lookup(struct sudo_nss *nss, 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;
@@ -1721,9 +1765,10 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int 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,
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);
efree(filt);
if (rc != LDAP_SUCCESS)
continue;
@@ -1748,7 +1793,8 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
}
}
ldap_msgfree(result);
result = NULL;
}
efree(filt);
}
if (matched || user_uid == 0) {
SET(ret, VALIDATE_OK);
@@ -1792,14 +1838,16 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int 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,
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)
if (rc != LDAP_SUCCESS) {
DPRINTF(("nothing found for '%s'", filt), 1);
efree(filt);
continue;
}
/* parse each entry returned from this most recent search */
if (rc == LDAP_SUCCESS) {
LDAP_FOREACH(entry, ld, result) {
DPRINTF(("found:%s", ldap_get_dn(ld, entry)), 1);
if (
@@ -1843,8 +1891,8 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
}
}
ldap_msgfree(result);
result = NULL;
}
efree(filt);
}
done: