Add support for querying netgroups directly via LDAP since there

is no other way to look up all the netgroups for a user (unlike
regular groups).  This introduces netgroup_base and netgroup_search_filter
options to ldap.conf.  Based on a diff from Steven Soulen.
This commit is contained in:
Todd C. Miller
2015-01-29 14:08:30 -07:00
parent 4ae3ab43c0
commit 4dd2a3c6b8
5 changed files with 506 additions and 33 deletions

6
NEWS
View File

@@ -55,6 +55,12 @@ What's new in Sudo 1.8.12
This limit was introduced when sudo's version of fnmatch()
was replaced in sudo 1.8.4.
* LDAP-based sudoers can now query an LDAP server for a user's
netgroups directly. This is often much faster than fetching
every sudoRole object containing a sudoUser that begins with a
`+' prefix and checking whether the user is a member of any of
the returned netgroups.
What's new in Sudo 1.8.11p2
* Fixed a bug where dynamic shared objects loaded from a plugin

View File

@@ -189,13 +189,34 @@ DDEESSCCRRIIPPTTIIOONN
The second is to match against the user's name and the groups that the
user belongs to. (The special ALL tag is matched in this query too.) If
no match is returned for the user's name and groups, a third query
returns all entries containing user netgroups and checks to see if the
user belongs to any of them.
returns all entries containing user netgroups and other non-Unix groups
and checks to see if the user belongs to any of them.
If timed entries are enabled with the SSUUDDOOEERRSS__TTIIMMEEDD configuration
directive, the LDAP queries include a sub-filter that limits retrieval to
entries that satisfy the time constraints, if any.
If the NNEETTGGRROOUUPP__BBAASSEE configuration directive is present, queries are
performed to determine the list of netgroups the user belongs to before
the sudoers query. This makes it possible to include netgroups in the
sudoers query string in the same manner as Unix groups. The third query
mentioned above is not performed unless a group provider plugin is also
configured. The actual LDAP queries performed by ssuuddoo are as follows:
1. Match all nisNetgroup records with a nisNetgroupTriple containing
the user and host. The query will match nisNetgroupTriple entries
with either the short or long form of the host name or no host name
specified in the tuple. A wildcard is used to match any domain name
but be aware that the NIS schema used by some LDAP servers may not
support wild cards for nisNetgroupTriple.
2. Repeated queries are performed to find any nested nisNetgroup
records with a memberNisNetgroup entry that refers to an already-
matched record.
For sites with a large number of netgroups, using NNEETTGGRROOUUPP__BBAASSEE can
significantly speed up ssuuddoo's execution time.
DDiiffffeerreenncceess bbeettwweeeenn LLDDAAPP aanndd nnoonn--LLDDAAPP ssuuddooeerrss
There are some subtle differences in the way sudoers is handled once in
LDAP. Probably the biggest is that according to the RFC, LDAP ordering
@@ -337,6 +358,35 @@ DDEESSCCRRIIPPTTIIOONN
The version of the LDAP protocol to use when connecting to the
server. The default value is protocol version 3.
NNEETTGGRROOUUPP__BBAASSEE _b_a_s_e
The base DN to use when performing LDAP netgroup queries.
Typically this is of the form ou=netgroup,dc=example,dc=com for the
domain example.com. Multiple NNEETTGGRROOUUPP__BBAASSEE lines may be specified,
in which case they are queried in the order specified.
This option can be used to query a user's netgroups directly via
LDAP which is usually faster than fetching every sudoRole object
containing a sudoUser that begins with a `+' prefix. The NIS
schema used by some LDAP servers need a modificaton to support
querying the nisNetgroup object by its nisNetgroupTriple member.
OpenLDAP's ssllaappdd requires the following change to the
nisNetgroupTriple attribute:
attributetype ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple'
DESC 'Netgroup triple'
EQUALITY caseIgnoreIA5Match
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
NNEETTGGRROOUUPP__SSEEAARRCCHH__FFIILLTTEERR _l_d_a_p___f_i_l_t_e_r
An LDAP filter which is used to restrict the set of records
returned when performing an LDAP netgroup query. Typically, this
is of the form attribute=value or
(&(attribute=value)(attribute2=value2)). The default search filter
is: objectClass=nisNetgroup. If _l_d_a_p___f_i_l_t_e_r is omitted, no search
filter will be used. This option is only when querying netgroups
directly via LDAP.
NNEETTWWOORRKK__TTIIMMEEOOUUTT _s_e_c_o_n_d_s
An alias for BBIINNDD__TTIIMMEELLIIMMIITT provided for OpenLDAP compatibility.

View File

@@ -367,13 +367,52 @@ the user belongs to.
\fRALL\fR
tag is matched in this query too.)
If no match is returned for the user's name and groups, a third
query returns all entries containing user netgroups and checks
to see if the user belongs to any of them.
query returns all entries containing user netgroups and other
non-Unix groups and checks to see if the user belongs to any of them.
.PP
If timed entries are enabled with the
\fBSUDOERS_TIMED\fR
configuration directive, the LDAP queries include a sub-filter that
limits retrieval to entries that satisfy the time constraints, if any.
.PP
If the
\fBNETGROUP_BASE\fR
configuration directive is present, queries are performed to determine
the list of netgroups the user belongs to before the sudoers query.
This makes it possible to include netgroups in the sudoers query
string in the same manner as Unix groups.
The third query mentioned above is not performed unless a group provider
plugin is also configured.
The actual LDAP queries performed by
\fBsudo\fR
are as follows:
.TP 5n
1.\&
Match all
\fRnisNetgroup\fR
records with a
\fRnisNetgroupTriple\fR
containing the user and host.
The query will match
\fRnisNetgroupTriple\fR
entries with either the short or long form of the host name or
no host name specified in the tuple.
A wildcard is used to match any domain name but be aware that the
NIS schema used by some LDAP servers may not support wild cards for
\fRnisNetgroupTriple\fR.
.TP 5n
2.\&
Repeated queries are performed to find any nested
\fRnisNetgroup\fR
records with a
\fRmemberNisNetgroup\fR
entry that refers to an already-matched record.
.PP
For sites with a large number of netgroups, using
\fBNETGROUP_BASE\fR
can significantly speed up
\fBsudo\fR's
execution time.
.SS "Differences between LDAP and non-LDAP sudoers"
There are some subtle differences in the way sudoers is handled
once in LDAP.
@@ -601,6 +640,61 @@ This option is only relevant when using SASL authentication (see below).
The version of the LDAP protocol to use when connecting to the server.
The default value is protocol version 3.
.TP 6n
\fBNETGROUP_BASE\fR \fIbase\fR
The base DN to use when performing LDAP netgroup queries.
Typically this is of the form
\fRou=netgroup,dc=example,dc=com\fR
for the domain
\fRexample.com\fR.
Multiple
\fBNETGROUP_BASE\fR
lines may be specified, in which case they are queried in the order specified.
.sp
This option can be used to query a user's netgroups directly via LDAP
which is usually faster than fetching every
\fRsudoRole\fR
object containing a
\fRsudoUser\fR
that begins with a
\(oq+\(cq
prefix.
The NIS schema used by some LDAP servers need a modificaton to
support querying the
\fRnisNetgroup\fR
object by its
\fRnisNetgroupTriple\fR
member.
OpenLDAP's
\fBslapd\fR
requires the following change to the
\fRnisNetgroupTriple\fR
attribute:
.nf
.sp
.RS 10n
attributetype ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple'
DESC 'Netgroup triple'
EQUALITY caseIgnoreIA5Match
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
.RE
.fi
.TP 6n
\fBNETGROUP_SEARCH_FILTER\fR \fIldap_filter\fR
An LDAP filter which is used to restrict the set of records returned
when performing an LDAP netgroup query.
Typically, this is of the
form
\fRattribute=value\fR
or
\fR(&(attribute=value)(attribute2=value2))\fR.
The default search filter is:
\fRobjectClass=nisNetgroup\fR.
If
\fIldap_filter\fR
is omitted, no search filter will be used.
This option is only when querying netgroups directly via LDAP.
.TP 6n
\fBNETWORK_TIMEOUT\fR \fIseconds\fR
An alias for
\fBBIND_TIMELIMIT\fR

View File

@@ -345,13 +345,52 @@ the user belongs to.
.Li ALL
tag is matched in this query too.)
If no match is returned for the user's name and groups, a third
query returns all entries containing user netgroups and checks
to see if the user belongs to any of them.
query returns all entries containing user netgroups and other
non-Unix groups and checks to see if the user belongs to any of them.
.Pp
If timed entries are enabled with the
.Sy SUDOERS_TIMED
configuration directive, the LDAP queries include a sub-filter that
limits retrieval to entries that satisfy the time constraints, if any.
.Pp
If the
.Sy NETGROUP_BASE
configuration directive is present, queries are performed to determine
the list of netgroups the user belongs to before the sudoers query.
This makes it possible to include netgroups in the sudoers query
string in the same manner as Unix groups.
The third query mentioned above is not performed unless a group provider
plugin is also configured.
The actual LDAP queries performed by
.Nm sudo
are as follows:
.Bl -enum
.It
Match all
.Li nisNetgroup
records with a
.Li nisNetgroupTriple
containing the user and host.
The query will match
.Li nisNetgroupTriple
entries with either the short or long form of the host name or
no host name specified in the tuple.
A wildcard is used to match any domain name but be aware that the
NIS schema used by some LDAP servers may not support wild cards for
.Li nisNetgroupTriple .
.It
Repeated queries are performed to find any nested
.Li nisNetgroup
records with a
.Li memberNisNetgroup
entry that refers to an already-matched record.
.El
.Pp
For sites with a large number of netgroups, using
.Sy NETGROUP_BASE
can significantly speed up
.Nm sudo Ns 's
execution time.
.Ss Differences between LDAP and non-LDAP sudoers
There are some subtle differences in the way sudoers is handled
once in LDAP.
@@ -561,6 +600,56 @@ This option is only relevant when using SASL authentication (see below).
.It Sy LDAP_VERSION Ar number
The version of the LDAP protocol to use when connecting to the server.
The default value is protocol version 3.
.It Sy NETGROUP_BASE Ar base
The base DN to use when performing LDAP netgroup queries.
Typically this is of the form
.Li ou=netgroup,dc=example,dc=com
for the domain
.Li example.com .
Multiple
.Sy NETGROUP_BASE
lines may be specified, in which case they are queried in the order specified.
.Pp
This option can be used to query a user's netgroups directly via LDAP
which is usually faster than fetching every
.Li sudoRole
object containing a
.Li sudoUser
that begins with a
.Ql +
prefix.
The NIS schema used by some LDAP servers need a modificaton to
support querying the
.Li nisNetgroup
object by its
.Li nisNetgroupTriple
member.
OpenLDAP's
.Sy slapd
requires the following change to the
.Li nisNetgroupTriple
attribute:
.Bd -literal -offset 4n
attributetype ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple'
DESC 'Netgroup triple'
EQUALITY caseIgnoreIA5Match
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
.Ed
.It Sy NETGROUP_SEARCH_FILTER Ar ldap_filter
An LDAP filter which is used to restrict the set of records returned
when performing an LDAP netgroup query.
Typically, this is of the
form
.Li attribute=value
or
.Li (&(attribute=value)(attribute2=value2)) .
The default search filter is:
.Li objectClass=nisNetgroup .
If
.Ar ldap_filter
is omitted, no search filter will be used.
This option is only when querying netgroups directly via LDAP.
.It Sy NETWORK_TIMEOUT Ar seconds
An alias for
.Sy BIND_TIMELIMIT

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2014 Todd C. Miller <Todd.Miller@courtesan.com>
* Copyright (c) 2003-2015 Todd C. Miller <Todd.Miller@courtesan.com>
*
* This code is derived from software contributed by Aaron Spangler.
*
@@ -162,6 +162,9 @@ extern int ldapssl_set_strength(LDAP *ldap, int strength);
/* Default search filter. */
#define DEFAULT_SEARCH_FILTER "(objectClass=sudoRole)"
/* Default netgroup search filter. */
#define DEFAULT_NETGROUP_SEARCH_FILTER "(objectClass=nisNetgroup)"
/* The TIMEFILTER_LENGTH is the length of the filter when timed entries
are used. The length is computed as follows:
81 for the filter itself
@@ -207,6 +210,16 @@ struct ldap_result {
};
#define ALLOCATION_INCREMENT 100
/*
* The ldap_netgroup structure implements a singly-linked tail queue of
* netgroups a user is a member of when querying netgroups directly.
*/
struct ldap_netgroup {
STAILQ_ENTRY(ldap_netgroup) entries;
char *name;
};
STAILQ_HEAD(ldap_netgroup_list, ldap_netgroup);
struct ldap_config_table {
const char *conf_str; /* config file string */
int type; /* CONF_BOOL, CONF_INT, CONF_STR */
@@ -218,7 +231,6 @@ struct ldap_config_str {
STAILQ_ENTRY(ldap_config_str) entries;
char val[1];
};
STAILQ_HEAD(ldap_config_str_list, ldap_config_str);
/* LDAP configuration structure */
@@ -242,7 +254,9 @@ static struct ldap_config {
char *bindpw;
char *rootbinddn;
struct ldap_config_str_list base;
struct ldap_config_str_list netgroup_base;
char *search_filter;
char *netgroup_search_filter;
char *ssl;
char *tls_cacertfile;
char *tls_cacertdir;
@@ -315,6 +329,8 @@ static struct ldap_config_table ldap_conf_global[] = {
{ "sudoers_base", CONF_LIST_STR, -1, &ldap_conf.base },
{ "sudoers_timed", CONF_BOOL, -1, &ldap_conf.timed },
{ "sudoers_search_filter", CONF_STR, -1, &ldap_conf.search_filter },
{ "netgroup_base", CONF_LIST_STR, -1, &ldap_conf.netgroup_base },
{ "netgroup_search_filter", CONF_STR, -1, &ldap_conf.netgroup_search_filter },
#ifdef HAVE_LDAP_SASL_INTERACTIVE_BIND_S
{ "use_sasl", CONF_BOOL, -1, &ldap_conf.use_sasl },
{ "sasl_auth_id", CONF_STR, -1, &ldap_conf.sasl_auth_id },
@@ -408,7 +424,6 @@ struct sudo_nss sudo_nss_ldap = {
static bool
sudo_ldap_conf_add_ports(void)
{
char *host, *port, defport[13];
char hostbuf[LINE_MAX * 2];
int len;
@@ -744,18 +759,18 @@ sudo_ldap_check_runas_user(LDAP *ld, LDAPMessage *entry)
/*
* BUG:
*
*
* if runas is not specified on the command line, the only information
* as to which user to run as is in the runas_default option. We should
* check to see if we have the local option present. Unfortunately we
* don't parse these options until after this routine says yes or no.
* The query has already returned, so we could peek at the attribute
* values here though.
*
*
* For now just require users to always use -u option unless its set
* in the global defaults. This behaviour is no different than the global
* /etc/sudoers.
*
*
* Sigh - maybe add this feature later
*/
@@ -1227,19 +1242,198 @@ done:
return dlen + (s - src); /* count does not include NUL */
}
/*
* Check the netgroups list beginning at "start" for nesting.
* Parent nodes with a memberNisNetgroup that match one of the
* netgroups are added to the list and checked for further nesting.
* Return true on success or false if there was an internal overflow.
*/
static bool
sudo_netgroup_lookup_nested(LDAP *ld, char *base, struct timeval *timeout,
struct ldap_netgroup_list *netgroups, struct ldap_netgroup *start)
{
struct ldap_netgroup *ng, *old_tail;
LDAPMessage *entry, *result;
size_t filt_len;
char *filt;
int rc;
debug_decl(sudo_netgroup_lookup_nested, SUDOERS_DEBUG_LDAP, sudoers_debug_instance);
DPRINTF1("Checking for nested netgroups from netgroup_base '%s'", base);
do {
old_tail = STAILQ_LAST(netgroups, ldap_netgroup, entries);
filt_len = strlen(ldap_conf.netgroup_search_filter) + 7;
for (ng = start; ng != NULL; ng = STAILQ_NEXT(ng, entries)) {
filt_len += sudo_ldap_value_len(ng->name) + 20;
}
filt = sudo_emalloc(filt_len);
CHECK_STRLCPY(filt, "(&", filt_len);
CHECK_STRLCAT(filt, ldap_conf.netgroup_search_filter, filt_len);
CHECK_STRLCAT(filt, "(|", filt_len);
for (ng = start; ng != NULL; ng = STAILQ_NEXT(ng, entries)) {
CHECK_STRLCAT(filt, "(memberNisNetgroup=", filt_len);
CHECK_LDAP_VCAT(filt, ng->name, filt_len);
CHECK_STRLCAT(filt, ")", filt_len);
}
CHECK_STRLCAT(filt, "))", filt_len);
DPRINTF1("ldap netgroup search filter: '%s'", filt);
result = NULL;
rc = ldap_search_ext_s(ld, base, LDAP_SCOPE_SUBTREE, filt,
NULL, 0, NULL, NULL, timeout, 0, &result);
if (rc == LDAP_SUCCESS) {
LDAP_FOREACH(entry, ld, result) {
struct berval **bv;
bv = ldap_get_values_len(ld, entry, "cn");
if (bv != NULL) {
/* Don't add a netgroup twice. */
STAILQ_FOREACH(ng, netgroups, entries) {
/* Assumes only one cn per entry. */
if (strcasecmp(ng->name, (*bv)->bv_val) == 0)
break;
}
if (ng == NULL) {
ng = sudo_emalloc(sizeof(*ng));
ng->name = sudo_estrdup((*bv)->bv_val);
STAILQ_INSERT_TAIL(netgroups, ng, entries);
DPRINTF1("Found new netgroup %s for %s", ng->name, base);
}
ldap_value_free_len(bv);
}
}
}
if (result)
ldap_msgfree(result);
sudo_efree(filt);
/* Check for nested netgroups in what we added. */
start = old_tail ? STAILQ_NEXT(old_tail, entries) : STAILQ_FIRST(netgroups);
} while (start != NULL);
debug_return_bool(true);
overflow:
sudo_warnx(U_("internal error, %s overflow"), __func__);
debug_return_bool(false);
}
/*
* Look up netgroups that the specified user is a member of.
* Appends new entries to the netgroups list.
* Return true on success or false if there was an internal overflow.
*/
static bool
sudo_netgroup_lookup(LDAP *ld, struct passwd *pw,
struct ldap_netgroup_list *netgroups)
{
struct ldap_config_str *base;
struct ldap_netgroup *ng, *old_tail;
struct timeval tv, *tvp = NULL;
LDAPMessage *entry, *result;
size_t filt_len;
char *filt;
int rc;
debug_decl(sudo_netgroup_lookup, SUDOERS_DEBUG_LDAP, sudoers_debug_instance);
if (ldap_conf.timeout > 0) {
tv.tv_sec = ldap_conf.timeout;
tv.tv_usec = 0;
tvp = &tv;
}
STAILQ_FOREACH(base, &ldap_conf.netgroup_base, entries) {
/* Build query. */
filt_len = 2 + strlen(ldap_conf.netgroup_search_filter) +
24 + (2 * sudo_ldap_value_len(pw->pw_name)) + 26 +
sudo_ldap_value_len(user_shost) + 1 + 7 + 1;
if (user_host != user_shost) {
filt_len += 26 + sudo_ldap_value_len(user_host) + 1 +
sudo_ldap_value_len(pw->pw_name);
}
filt = sudo_emalloc(filt_len);
DPRINTF1("searching from netgroup_base '%s'", base->val);
CHECK_STRLCPY(filt, "(&", filt_len);
CHECK_STRLCAT(filt, ldap_conf.netgroup_search_filter, filt_len);
CHECK_STRLCAT(filt, "(|(nisNetgroupTriple=\\(,", filt_len);
CHECK_LDAP_VCAT(filt, pw->pw_name, filt_len);
CHECK_STRLCAT(filt, ",*\\))(nisNetgroupTriple=\\(", filt_len);
CHECK_LDAP_VCAT(filt, user_shost, filt_len);
CHECK_STRLCAT(filt, ",", filt_len);
CHECK_LDAP_VCAT(filt, pw->pw_name, filt_len);
if (user_host != user_shost) {
CHECK_STRLCAT(filt, ",*\\))(nisNetgroupTriple=\\(", filt_len);
CHECK_LDAP_VCAT(filt, user_host, filt_len);
CHECK_STRLCAT(filt, ",", filt_len);
CHECK_LDAP_VCAT(filt, pw->pw_name, filt_len);
}
CHECK_STRLCAT(filt, ",*\\))))", filt_len);
DPRINTF1("ldap netgroup search filter: '%s'", filt);
result = NULL;
rc = ldap_search_ext_s(ld, base->val, LDAP_SCOPE_SUBTREE, filt,
NULL, 0, NULL, NULL, tvp, 0, &result);
if (rc != LDAP_SUCCESS) {
DPRINTF1("nothing found for '%s'", filt);
if (result)
ldap_msgfree(result);
sudo_efree(filt);
continue;
}
sudo_efree(filt);
old_tail = STAILQ_LAST(netgroups, ldap_netgroup, entries);
LDAP_FOREACH(entry, ld, result) {
struct berval **bv;
bv = ldap_get_values_len(ld, entry, "cn");
if (bv != NULL) {
/* Don't add a netgroup twice. */
STAILQ_FOREACH(ng, netgroups, entries) {
/* Assumes only one cn per entry. */
if (strcasecmp(ng->name, (*bv)->bv_val) == 0)
break;
}
if (ng == NULL) {
ng = sudo_emalloc(sizeof(*ng));
ng->name = sudo_estrdup((*bv)->bv_val);
STAILQ_INSERT_TAIL(netgroups, ng, entries);
DPRINTF1("Found new netgroup %s for %s", ng->name,
base->val);
}
ldap_value_free_len(bv);
}
}
ldap_msgfree(result);
/* Check for nested netgroups in what we added. */
ng = old_tail ? STAILQ_NEXT(old_tail, entries) : STAILQ_FIRST(netgroups);
if (ng != NULL) {
if (!sudo_netgroup_lookup_nested(ld, base->val, tvp, netgroups, ng))
debug_return_bool(false);
}
}
debug_return_bool(true);
overflow:
sudo_warnx(U_("internal error, %s overflow"), __func__);
debug_return_bool(false);
}
/*
* Builds up a filter to check against LDAP.
*/
static char *
sudo_ldap_build_pass1(struct passwd *pw)
sudo_ldap_build_pass1(LDAP *ld, struct passwd *pw)
{
struct group *grp;
char *buf, timebuffer[TIMEFILTER_LENGTH + 1], gidbuf[MAX_UID_T_LEN + 1];
struct ldap_netgroup_list netgroups;
struct ldap_netgroup *ng, *nextng;
struct group_list *grlist;
struct group *grp;
size_t sz = 0;
int i;
debug_decl(sudo_ldap_build_pass1, SUDOERS_DEBUG_LDAP, sudoers_debug_instance)
STAILQ_INIT(&netgroups);
/* If there is a filter, allocate space for the global AND. */
if (ldap_conf.timed || ldap_conf.search_filter)
sz += 3;
@@ -1269,6 +1463,23 @@ sudo_ldap_build_pass1(struct passwd *pw)
}
}
/* Add space for user netgroups if netgroup_base specified. */
if (!STAILQ_EMPTY(&ldap_conf.netgroup_base)) {
DPRINTF1("Looking up netgroups for %s", pw->pw_name);
if (sudo_netgroup_lookup(ld, pw, &netgroups)) {
STAILQ_FOREACH(ng, &netgroups, entries) {
sz += 14 + strlen(ng->name);
}
} else {
/* sudo_netgroup_lookup() failed, clean up. */
STAILQ_FOREACH_SAFE(ng, &netgroups, entries, nextng) {
sudo_efree(ng->name);
sudo_efree(ng);
}
STAILQ_INIT(&netgroups);
}
}
/* If timed, add space for time limits. */
if (ldap_conf.timed)
sz += TIMEFILTER_LENGTH;
@@ -1327,7 +1538,16 @@ sudo_ldap_build_pass1(struct passwd *pw)
if (grp != NULL)
sudo_gr_delref(grp);
/* Add ALL to list and end the global OR */
/* Add netgroups (if any), freeing the list as we go. */
STAILQ_FOREACH_SAFE(ng, &netgroups, entries, nextng) {
CHECK_STRLCAT(buf, "(sudoUser=+", sz);
CHECK_LDAP_VCAT(buf, ng->name, sz);
CHECK_STRLCAT(buf, ")", sz);
sudo_efree(ng->name);
sudo_efree(ng);
}
/* Add ALL to list and end the global OR. */
CHECK_STRLCAT(buf, "(sudoUser=ALL)", sz);
/* Add the time restriction, or simply end the global OR. */
@@ -1354,32 +1574,36 @@ static char *
sudo_ldap_build_pass2(void)
{
char *filt, timebuffer[TIMEFILTER_LENGTH + 1];
bool query_netgroups = def_use_netgroups;
debug_decl(sudo_ldap_build_pass2, SUDOERS_DEBUG_LDAP, sudoers_debug_instance)
/* Short circuit if no non-Unix group support. */
if (!def_use_netgroups && !def_group_plugin) {
/* No need to query netgroups if using netgroup_base. */
if (!STAILQ_EMPTY(&ldap_conf.netgroup_base))
query_netgroups = false;
/* Short circuit if no netgroups and no non-Unix groups. */
if (!query_netgroups && !def_group_plugin)
debug_return_str(NULL);
}
if (ldap_conf.timed)
sudo_ldap_timefilter(timebuffer, sizeof(timebuffer));
/*
* Match all sudoUsers beginning with '+' or '%:'.
* If a search filter or time restriction is specified,
* If a search filter or time restriction is specified,
* those get ANDed in to the expression.
*/
if (def_group_plugin) {
sudo_easprintf(&filt, "%s%s(|(sudoUser=%s*)(sudoUser=%%:*))%s%s",
if (query_netgroups && def_group_plugin) {
sudo_easprintf(&filt, "%s%s(|(sudoUser=+*)(sudoUser=%%:*))%s%s",
(ldap_conf.timed || ldap_conf.search_filter) ? "(&" : "",
ldap_conf.search_filter ? ldap_conf.search_filter : "",
def_use_netgroups ? "+" : "",
ldap_conf.timed ? timebuffer : "",
(ldap_conf.timed || ldap_conf.search_filter) ? ")" : "");
} else {
sudo_easprintf(&filt, "%s%s(sudoUser=*)(sudoUser=+*)%s%s",
sudo_easprintf(&filt, "%s%s(sudoUser=*)(sudoUser=%s*)%s%s",
(ldap_conf.timed || ldap_conf.search_filter) ? "(&" : "",
ldap_conf.search_filter ? ldap_conf.search_filter : "",
query_netgroups ? "+" : "%:",
ldap_conf.timed ? timebuffer : "",
(ldap_conf.timed || ldap_conf.search_filter) ? ")" : "");
}
@@ -1559,9 +1783,10 @@ sudo_check_krb5_ccname(const char *ccname)
static bool
sudo_ldap_read_config(void)
{
FILE *fp;
char *cp, *keyword, *value, *line = NULL;
struct ldap_config_str *conf_str;
size_t linesize = 0;
FILE *fp;
debug_decl(sudo_ldap_read_config, SUDOERS_DEBUG_LDAP, sudoers_debug_instance)
/* defaults */
@@ -1575,8 +1800,10 @@ sudo_ldap_read_config(void)
ldap_conf.rootuse_sasl = -1;
ldap_conf.deref = -1;
ldap_conf.search_filter = sudo_estrdup(DEFAULT_SEARCH_FILTER);
ldap_conf.netgroup_search_filter = sudo_estrdup(DEFAULT_NETGROUP_SEARCH_FILTER);
STAILQ_INIT(&ldap_conf.uri);
STAILQ_INIT(&ldap_conf.base);
STAILQ_INIT(&ldap_conf.netgroup_base);
if ((fp = fopen(path_ldap_conf, "r")) == NULL)
debug_return_bool(false);
@@ -1610,10 +1837,8 @@ sudo_ldap_read_config(void)
DPRINTF1("LDAP Config Summary");
DPRINTF1("===================");
if (!STAILQ_EMPTY(&ldap_conf.uri)) {
struct ldap_config_str *uri;
STAILQ_FOREACH(uri, &ldap_conf.uri, entries) {
DPRINTF1("uri %s", uri->val);
STAILQ_FOREACH(conf_str, &ldap_conf.uri, entries) {
DPRINTF1("uri %s", conf_str->val);
}
} else {
DPRINTF1("host %s",
@@ -1623,9 +1848,8 @@ sudo_ldap_read_config(void)
DPRINTF1("ldap_version %d", ldap_conf.version);
if (!STAILQ_EMPTY(&ldap_conf.base)) {
struct ldap_config_str *base;
STAILQ_FOREACH(base, &ldap_conf.base, entries) {
DPRINTF1("sudoers_base %s", base->val);
STAILQ_FOREACH(conf_str, &ldap_conf.base, entries) {
DPRINTF1("sudoers_base %s", conf_str->val);
}
} else {
DPRINTF1("sudoers_base %s", "(NONE: LDAP disabled)");
@@ -1633,6 +1857,16 @@ sudo_ldap_read_config(void)
if (ldap_conf.search_filter) {
DPRINTF1("search_filter %s", ldap_conf.search_filter);
}
if (!STAILQ_EMPTY(&ldap_conf.netgroup_base)) {
STAILQ_FOREACH(conf_str, &ldap_conf.netgroup_base, entries) {
DPRINTF1("netgroup_base %s", conf_str->val);
}
} else {
DPRINTF1("netgroup_base %s", "(NONE: will use nsswitch)");
}
if (ldap_conf.netgroup_search_filter) {
DPRINTF1("netgroup_search_filter %s", ldap_conf.netgroup_search_filter);
}
DPRINTF1("binddn %s",
ldap_conf.binddn ? ldap_conf.binddn : "(anonymous)");
DPRINTF1("bindpw %s",
@@ -2706,7 +2940,7 @@ sudo_ldap_lookup(struct sudo_nss *nss, int ret, int pwflag)
if (pwflag) {
int doauth = UNSPEC;
int matched = UNSPEC;
enum def_tuple pwcheck =
enum def_tuple pwcheck =
(pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
DPRINTF1("perform search for pwflag %d", pwflag);
@@ -2949,7 +3183,7 @@ sudo_ldap_result_get(struct sudo_nss *nss, struct passwd *pw)
*/
lres = sudo_ldap_result_alloc();
for (pass = 0; pass < 2; pass++) {
filt = pass ? sudo_ldap_build_pass2() : sudo_ldap_build_pass1(pw);
filt = pass ? sudo_ldap_build_pass2() : sudo_ldap_build_pass1(ld, pw);
if (filt != NULL) {
DPRINTF1("ldap search '%s'", filt);
STAILQ_FOREACH(base, &ldap_conf.base, entries) {