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:
6
NEWS
6
NEWS
@@ -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
|
||||
|
@@ -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.
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
|
Reference in New Issue
Block a user