This flag is _\bo_\bn by default when s\bsu\bud\bdo\bo is compiled with
z\bzl\bli\bib\bb support.
- use_netgroups If set, netgroups (prefixed with `+'), may be used in
- place of a user or host. For LDAP-based sudoers,
- netgroup support requires an expensive substring match
- on the server unless the N\bNE\bET\bTG\bGR\bRO\bOU\bUP\bP_\b_B\bBA\bAS\bSE\bE directive is
- present in the _\b/_\be_\bt_\bc_\b/_\bl_\bd_\ba_\bp_\b._\bc_\bo_\bn_\bf file. If netgroups are
- not needed, this option can be disabled to reduce the
- load on the LDAP server. This flag is _\bo_\bn by default.
-
exec_background By default, s\bsu\bud\bdo\bo runs a command as the foreground
process as long as s\bsu\bud\bdo\bo itself is running in the
foreground. When the _\be_\bx_\be_\bc_\b__\bb_\ba_\bc_\bk_\bg_\br_\bo_\bu_\bn_\bd flag is enabled
invoking user is not in the _\bs_\bu_\bd_\bo_\be_\br_\bs file. This flag is
_\bo_\bn by default.
+ netgroup_tuple If set, netgroup lookups will be performed using the
+ full netgroup tuple: host name, user name and domain
+ (if one is set). Historically, s\bsu\bud\bdo\bo only matched the
+ user name and domain for netgroups used in a User_List
+ and only matched the host name and domain for netgroups
+ used in a Host_List. This flag is _\bo_\bf_\bf by default.
+
noexec If set, all commands run via s\bsu\bud\bdo\bo will behave as if the
NOEXEC tag has been set, unless overridden by an EXEC
tag. See the description of _\bE_\bX_\bE_\bC _\ba_\bn_\bd _\bN_\bO_\bE_\bX_\bE_\bC above as
available if s\bsu\bud\bdo\bo is configured with the
--with-logincap option. This flag is _\bo_\bf_\bf by default.
+ use_netgroups If set, netgroups (prefixed with `+'), may be used in
+ place of a user or host. For LDAP-based sudoers,
+ netgroup support requires an expensive substring match
+ on the server unless the N\bNE\bET\bTG\bGR\bRO\bOU\bUP\bP_\b_B\bBA\bAS\bSE\bE directive is
+ present in the _\b/_\be_\bt_\bc_\b/_\bl_\bd_\ba_\bp_\b._\bc_\bo_\bn_\bf file. If netgroups are
+ not needed, this option can be disabled to reduce the
+ load on the LDAP server. This flag is _\bo_\bn by default.
+
use_pty If set, s\bsu\bud\bdo\bo will run the command in a pseudo-pty even
if no I/O logging is being gone. A malicious program
run under s\bsu\bud\bdo\bo could conceivably fork a background
file distributed with s\bsu\bud\bdo\bo or https://www.sudo.ws/license.html for
complete details.
-Sudo 1.8.16 January 9, 2016 Sudo 1.8.16
+Sudo 1.8.16 January 12, 2016 Sudo 1.8.16
.\" Agency (DARPA) and Air Force Research Laboratory, Air Force
.\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
.\"
-.TH "SUDOERS" "5" "January 9, 2016" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
+.TH "SUDOERS" "5" "January 12, 2016" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.nh
.if n .ad l
.SH "NAME"
\fBzlib\fR
support.
.TP 18n
-use_netgroups
-If set, netgroups (prefixed with
-\(oq+\(cq),
-may be used in place of a user or host.
-For LDAP-based sudoers, netgroup support requires an expensive
-substring match on the server unless the
-\fBNETGROUP_BASE\fR
-directive is present in the
-\fI@ldap_conf@\fR
-file.
-If netgroups are not needed, this option can be disabled to reduce the
-load on the LDAP server.
-This flag is
-\fIon\fR
-by default.
-.TP 18n
exec_background
By default,
\fBsudo\fR
\fI@mail_no_user@\fR
by default.
.TP 18n
+netgroup_tuple
+If set, netgroup lookups will be performed using the full netgroup
+tuple: host name, user name and domain (if one is set).
+Historically,
+\fBsudo\fR
+only matched the user name and domain for netgroups used in a
+\fRUser_List\fR
+and only matched the host name and domain for netgroups used in a
+\fRHost_List\fR.
+This flag is
+\fIoff\fR
+by default.
+.TP 18n
noexec
If set, all commands run via
\fBsudo\fR
\fIoff\fR
by default.
.TP 18n
+use_netgroups
+If set, netgroups (prefixed with
+\(oq+\(cq),
+may be used in place of a user or host.
+For LDAP-based sudoers, netgroup support requires an expensive
+substring match on the server unless the
+\fBNETGROUP_BASE\fR
+directive is present in the
+\fI@ldap_conf@\fR
+file.
+If netgroups are not needed, this option can be disabled to reduce the
+load on the LDAP server.
+This flag is
+\fIon\fR
+by default.
+.TP 18n
use_pty
If set,
\fBsudo\fR
.\" Agency (DARPA) and Air Force Research Laboratory, Air Force
.\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
.\"
-.Dd January 9, 2016
+.Dd January 12, 2016
.Dt SUDOERS @mansectform@
.Os Sudo @PACKAGE_VERSION@
.Sh NAME
is compiled with
.Sy zlib
support.
-.It use_netgroups
-If set, netgroups (prefixed with
-.Ql + ) ,
-may be used in place of a user or host.
-For LDAP-based sudoers, netgroup support requires an expensive
-substring match on the server unless the
-.Sy NETGROUP_BASE
-directive is present in the
-.Pa @ldap_conf@
-file.
-If netgroups are not needed, this option can be disabled to reduce the
-load on the LDAP server.
-This flag is
-.Em on
-by default.
.It exec_background
By default,
.Nm sudo
This flag is
.Em @mail_no_user@
by default.
+.It netgroup_tuple
+If set, netgroup lookups will be performed using the full netgroup
+tuple: host name, user name and domain (if one is set).
+Historically,
+.Nm sudo
+only matched the user name and domain for netgroups used in a
+.Li User_List
+and only matched the host name and domain for netgroups used in a
+.Li Host_List .
+This flag is
+.Em off
+by default.
.It noexec
If set, all commands run via
.Nm sudo
This flag is
.Em off
by default.
+.It use_netgroups
+If set, netgroups (prefixed with
+.Ql + ) ,
+may be used in place of a user or host.
+For LDAP-based sudoers, netgroup support requires an expensive
+substring match on the server unless the
+.Sy NETGROUP_BASE
+directive is present in the
+.Pa @ldap_conf@
+file.
+If netgroups are not needed, this option can be disabled to reduce the
+load on the LDAP server.
+This flag is
+.Em on
+by default.
.It use_pty
If set,
.Nm sudo
"always_query_group_plugin", T_FLAG,
N_("Query the group plugin for unknown system groups"),
NULL,
+ }, {
+ "netgroup_tuple", T_FLAG,
+ N_("Match netgroups based on the entire tuple: user, host and domain"),
+ NULL,
}, {
NULL, 0, NULL
}
#define I_SUDOEDIT_FOLLOW 92
#define def_always_query_group_plugin (sudo_defs_table[93].sd_un.flag)
#define I_ALWAYS_QUERY_GROUP_PLUGIN93
+#define def_netgroup_tuple (sudo_defs_table[94].sd_un.flag)
+#define I_NETGROUP_TUPLE 94
enum def_tuple {
never,
always_query_group_plugin
T_FLAG
"Query the group plugin for unknown system groups"
+netgroup_tuple
+ T_FLAG
+ "Match netgroups based on the entire tuple: user, host and domain"
#ifdef HAVE_INNETGR
def_use_netgroups = true;
#endif
+ def_netgroup_tuple = false;
/* Syslog options need special care since they both strings and ints */
#if (LOGGING & SLOG_SYSLOG)
break;
case DEFAULTS_HOST:
if (ISSET(what, SETDEF_HOST) &&
- hostlist_matches(def->binding) == ALLOW &&
+ hostlist_matches(sudo_user.pw, def->binding) == ALLOW &&
!set_default(def->var, def->val, def->op))
rc = false;
break;
for (p = bv; *p != NULL && !ret; p++) {
val = (*p)->bv_val;
if (*val == '+') {
- if (netgr_matches(val, NULL, NULL, pw->pw_name))
+ if (netgr_matches(val, def_netgroup_tuple ? user_runhost : NULL,
+ def_netgroup_tuple ? user_srunhost : NULL, pw->pw_name))
ret = true;
DPRINTF2("ldap sudoUser netgroup '%s' ... %s", val,
ret ? "MATCH!" : "not");
* host match, else false.
*/
static bool
-sudo_ldap_check_host(LDAP *ld, LDAPMessage *entry)
+sudo_ldap_check_host(LDAP *ld, LDAPMessage *entry, struct passwd *pw)
{
struct berval **bv, **p;
char *val;
val = (*p)->bv_val;
/* match any or address or netgroup or hostname */
if (!strcmp(val, "ALL") || addr_matches(val) ||
- netgr_matches(val, user_runhost, user_srunhost, NULL) ||
+ netgr_matches(val, user_runhost, user_srunhost,
+ def_netgroup_tuple ? pw->pw_name : NULL) ||
hostname_matches(user_srunhost, user_runhost, val))
ret = true;
DPRINTF2("ldap sudoHost '%s' ... %s", val, ret ? "MATCH!" : "not");
val = (*p)->bv_val;
switch (val[0]) {
case '+':
- if (netgr_matches(val, NULL, NULL, runas_pw->pw_name))
+ if (netgr_matches(val, def_netgroup_tuple ? user_runhost : NULL,
+ def_netgroup_tuple ? user_srunhost : NULL, runas_pw->pw_name))
ret = true;
break;
case '%':
struct timeval tv, *tvp = NULL;
LDAPMessage *entry, *result = NULL;
const char *domain;
- char *escaped_domain, *escaped_host, *escaped_shost, *escaped_user;
- char *filt = NULL;
+ char *escaped_domain = NULL, *escaped_user = NULL;
+ char *escaped_host = NULL, *escaped_shost = NULL, *filt = NULL;
int filt_len, rc;
debug_decl(sudo_netgroup_lookup, SUDOERS_DEBUG_LDAP);
domain = sudo_getdomainname();
/* Escape the domain, host names, and user name per RFC 4515. */
- escaped_domain = domain ? sudo_ldap_value_dup(domain) : NULL;
- escaped_host = sudo_ldap_value_dup(user_runhost);
- if (user_runhost == user_srunhost)
- escaped_shost = escaped_host;
- else
- escaped_shost = sudo_ldap_value_dup(user_srunhost);
- escaped_user = sudo_ldap_value_dup(pw->pw_name);
- if (escaped_domain == NULL || escaped_host == NULL ||
- escaped_shost == NULL || escaped_user == NULL)
- goto oom;
+ if (domain != NULL) {
+ if ((escaped_domain = sudo_ldap_value_dup(domain)) == NULL)
+ goto oom;
+ }
+ if ((escaped_user = sudo_ldap_value_dup(pw->pw_name)) == NULL)
+ goto oom;
+ if (def_netgroup_tuple) {
+ escaped_host = sudo_ldap_value_dup(user_runhost);
+ if (user_runhost == user_srunhost)
+ escaped_shost = escaped_host;
+ else
+ escaped_shost = sudo_ldap_value_dup(user_srunhost);
+ if (escaped_host == NULL || escaped_shost == NULL)
+ goto oom;
+ }
/* Build query, using NIS domain if it is set. */
if (domain != NULL) {
- if (user_runhost != user_srunhost) {
- filt_len = asprintf(&filt, "(&%s(|(nisNetgroupTriple=\\28,%s,%s\\29)(nisNetgroupTriple=\\28%s,%s,%s\\29)(nisNetgroupTriple=\\28%s,%s,%s\\29)(nisNetgroupTriple=\\28,%s,\\29)(nisNetgroupTriple=\\28%s,%s,\\29)(nisNetgroupTriple=\\28%s,%s,\\29)))", ldap_conf.netgroup_search_filter, escaped_user, escaped_domain, escaped_shost, escaped_user, escaped_domain, escaped_host, escaped_user, escaped_domain, escaped_user, escaped_shost, escaped_user, escaped_host, escaped_user);
+ if (escaped_host != escaped_shost) {
+ filt_len = asprintf(&filt, "(&%s(|"
+ "(nisNetgroupTriple=\\28,%s,%s\\29)"
+ "(nisNetgroupTriple=\\28%s,%s,%s\\29)"
+ "(nisNetgroupTriple=\\28%s,%s,%s\\29)"
+ "(nisNetgroupTriple=\\28,%s,\\29)"
+ "(nisNetgroupTriple=\\28%s,%s,\\29)"
+ "(nisNetgroupTriple=\\28%s,%s,\\29)))",
+ ldap_conf.netgroup_search_filter, escaped_user, escaped_domain,
+ escaped_shost, escaped_user, escaped_domain,
+ escaped_host, escaped_user, escaped_domain, escaped_user,
+ escaped_shost, escaped_user, escaped_host, escaped_user);
+ } else if (escaped_shost != NULL) {
+ filt_len = asprintf(&filt, "(&%s(|"
+ "(nisNetgroupTriple=\\28,%s,%s\\29)"
+ "(nisNetgroupTriple=\\28%s,%s,%s\\29)"
+ "(nisNetgroupTriple=\\28,%s,\\29)"
+ "(nisNetgroupTriple=\\28%s,%s,\\29)))",
+ ldap_conf.netgroup_search_filter, escaped_user, escaped_domain,
+ escaped_shost, escaped_user, escaped_domain,
+ escaped_user, escaped_shost, escaped_user);
} else {
- filt_len = asprintf(&filt, "(&%s(|(nisNetgroupTriple=\\28,%s,%s\\29)(nisNetgroupTriple=\\28%s,%s,%s\\29)(nisNetgroupTriple=\\28,%s,\\29)(nisNetgroupTriple=\\28%s,%s,\\29)))", ldap_conf.netgroup_search_filter, escaped_user, escaped_domain, escaped_shost, escaped_user, escaped_domain, escaped_user, escaped_shost, escaped_user);
+ filt_len = asprintf(&filt, "(&%s(|"
+ "(nisNetgroupTriple=\\28*,%s,%s\\29)"
+ "(nisNetgroupTriple=\\28*,%s,\\29)))",
+ ldap_conf.netgroup_search_filter, escaped_user, escaped_domain,
+ escaped_user);
}
} else {
- if (user_runhost != user_srunhost) {
- filt_len = asprintf(&filt, "(&%s(|(nisNetgroupTriple=\\28,%s,*\\29)(nisNetgroupTriple=\\28%s,%s,*\\29)(nisNetgroupTriple=\\28%s,%s,*\\29)))", ldap_conf.netgroup_search_filter, escaped_user, escaped_shost, escaped_user, escaped_host, escaped_user);
+ if (escaped_host != escaped_shost) {
+ filt_len = asprintf(&filt, "(&%s(|"
+ "(nisNetgroupTriple=\\28,%s,*\\29)"
+ "(nisNetgroupTriple=\\28%s,%s,*\\29)"
+ "(nisNetgroupTriple=\\28%s,%s,*\\29)))",
+ ldap_conf.netgroup_search_filter, escaped_user,
+ escaped_shost, escaped_user, escaped_host, escaped_user);
+ } else if (escaped_shost != NULL) {
+ filt_len = asprintf(&filt, "(&%s(|"
+ "(nisNetgroupTriple=\\28,%s,*\\29)"
+ "(nisNetgroupTriple=\\28%s,%s,*\\29)))",
+ ldap_conf.netgroup_search_filter, escaped_user,
+ escaped_shost, escaped_user);
} else {
- filt_len = asprintf(&filt, "(&%s(|(nisNetgroupTriple=\\28,%s,*\\29)(nisNetgroupTriple=\\28%s,%s,*\\29)))", ldap_conf.netgroup_search_filter, escaped_user, escaped_shost, escaped_user);
+ filt_len = asprintf(&filt,
+ "(&%s(|(nisNetgroupTriple=\\28*,%s,*\\29)))",
+ ldap_conf.netgroup_search_filter, escaped_user);
}
}
if (filt_len == -1)
oom:
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
free(escaped_domain);
+ free(escaped_user);
free(escaped_host);
if (escaped_host != escaped_shost)
free(escaped_shost);
- free(escaped_user);
free(filt);
ldap_msgfree(result);
debug_return_bool(false);
continue;
lres->user_matches = true;
/* Check host. */
- if (!sudo_ldap_check_host(ld, entry))
+ if (!sudo_ldap_check_host(ld, entry, pw))
continue;
lres->host_matches = true;
if (sudo_ldap_result_add_entry(lres, entry) == NULL) {
matched = !m->negated;
break;
case NETGROUP:
- if (netgr_matches(m->name, NULL, NULL, pw->pw_name))
+ if (netgr_matches(m->name,
+ def_netgroup_tuple ? user_runhost : NULL,
+ def_netgroup_tuple ? user_srunhost : NULL, pw->pw_name))
matched = !m->negated;
break;
case USERGROUP:
user_matched = !m->negated;
break;
case NETGROUP:
- if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name))
+ if (netgr_matches(m->name,
+ def_netgroup_tuple ? user_runhost : NULL,
+ def_netgroup_tuple ? user_srunhost : NULL,
+ runas_pw->pw_name))
user_matched = !m->negated;
break;
case USERGROUP:
* Returns ALLOW, DENY or UNSPEC.
*/
int
-hostlist_matches(const struct member_list *list)
+hostlist_matches(const struct passwd *pw, const struct member_list *list)
{
struct member *m;
struct alias *a;
matched = !m->negated;
break;
case NETGROUP:
- if (netgr_matches(m->name, user_runhost, user_srunhost, NULL))
+ if (netgr_matches(m->name, user_runhost, user_srunhost,
+ pw->pw_name))
matched = !m->negated;
break;
case NTWKADDR:
break;
case ALIAS:
if ((a = alias_get(m->name, HOSTALIAS)) != NULL) {
- rval = hostlist_matches(&a->members);
+ rval = hostlist_matches(pw, &a->members);
if (rval != UNSPEC)
matched = m->negated ? !rval : rval;
alias_put(a);
if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
continue;
TAILQ_FOREACH(priv, &us->privileges, entries) {
- if (hostlist_matches(&priv->hostlist) != ALLOW)
+ if (hostlist_matches(sudo_user.pw, &priv->hostlist) != ALLOW)
continue;
TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
/* Only check the command when listing another user. */
continue;
CLR(validated, FLAG_NO_USER);
TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) {
- host_match = hostlist_matches(&priv->hostlist);
+ host_match = hostlist_matches(sudo_user.pw, &priv->hostlist);
if (host_match == ALLOW)
CLR(validated, FLAG_NO_HOST);
else
/* gcc -Wuninitialized false positive */
TAGS_INIT(tags);
TAILQ_FOREACH(priv, &us->privileges, entries) {
- if (hostlist_matches(&priv->hostlist) != ALLOW)
+ if (hostlist_matches(pw, &priv->hostlist) != ALLOW)
continue;
prev_cs = NULL;
TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
debug_decl(sudo_file_display_priv_long, SUDOERS_DEBUG_NSS)
TAILQ_FOREACH(priv, &us->privileges, entries) {
- if (hostlist_matches(&priv->hostlist) != ALLOW)
+ if (hostlist_matches(pw, &priv->hostlist) != ALLOW)
continue;
prev_cs = NULL;
TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
TAILQ_FOREACH(d, &defaults, entries) {
switch (d->type) {
case DEFAULTS_HOST:
- if (hostlist_matches(d->binding) != ALLOW)
+ if (hostlist_matches(pw, d->binding) != ALLOW)
continue;
break;
case DEFAULTS_USER:
continue;
TAILQ_FOREACH_REVERSE(priv, &us->privileges, privilege_list, entries) {
- host_match = hostlist_matches(&priv->hostlist);
+ host_match = hostlist_matches(pw, &priv->hostlist);
if (host_match != ALLOW)
continue;
TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) {
bool userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw);
int cmnd_matches(const struct member *m);
int cmndlist_matches(const struct member_list *list);
-int hostlist_matches(const struct member_list *list);
+int hostlist_matches(const struct passwd *pw, const struct member_list *list);
int runaslist_matches(const struct member_list *user_list, const struct member_list *group_list, struct member **matching_user, struct member **matching_group);
int userlist_matches(const struct passwd *pw, const struct member_list *list);
const char *sudo_getdomainname(void);
switch (val[0]) {
case '+':
sudo_debug_printf(SUDO_DEBUG_DEBUG, "netgr_");
- if (netgr_matches(val, NULL, NULL, runas_pw->pw_name)) {
+ if (netgr_matches(val, def_netgroup_tuple ? user_runhost : NULL,
+ def_netgroup_tuple ? user_srunhost : NULL, runas_pw->pw_name)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "=> match");
ret = true;
}
debug_return_bool(ret);
/* get the values from the rule */
- switch (handle->fn_get_values(rule, "sudoHost", &val_array))
- {
+ switch (handle->fn_get_values(rule, "sudoHost", &val_array)) {
case 0:
break;
case ENOENT:
sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val);
/* match any or address or netgroup or hostname */
- if (!strcmp(val, "ALL") || addr_matches(val) ||
- netgr_matches(val, user_runhost, user_srunhost, NULL) ||
+ if (!strcmp(val, "ALL") || addr_matches(val) || netgr_matches(val,
+ user_runhost, user_srunhost, handle->pw->pw_name) ||
hostname_matches(user_srunhost, user_runhost, val))
ret = true;
netgroup_spec_found = true;
}
sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val);
- if (strcmp(val, "ALL") == 0 || netgr_matches(val, NULL, NULL, handle->pw->pw_name)) {
+ if (strcmp(val, "ALL") == 0 || netgr_matches(val,
+ def_netgroup_tuple ? user_runhost : NULL,
+ def_netgroup_tuple ? user_srunhost : NULL, handle->pw->pw_name)) {
ret = true;
sudo_debug_printf(SUDO_DEBUG_DIAG,
"sssd/ldap sudoUser '%s' ... MATCH! (%s)",
putchar('\n');
print_privilege(priv);
putchar('\n');
- host_match = hostlist_matches(&priv->hostlist);
+ host_match = hostlist_matches(sudo_user.pw, &priv->hostlist);
if (host_match == ALLOW) {
puts("\thost matched");
TAILQ_FOREACH_REVERSE(cs, &priv->cmndlist, cmndspec_list, entries) {