Restrict "sudo -U other -l" to users with sudo ALL for root or "other".

Having "sudo ALL" permissions in no longer sufficient to be able to
list another user's privileges.  The invoking user must now have
"sudo ALL" for root or the target user.
GitHub issue #134
This commit is contained in:
Todd C. Miller
2022-02-14 13:09:55 -07:00
parent df1bb3814b
commit 9f695f0fcc
4 changed files with 41 additions and 20 deletions

View File

@@ -25,7 +25,7 @@
.nr BA @BAMAN@ .nr BA @BAMAN@
.nr LC @LCMAN@ .nr LC @LCMAN@
.nr PS @PSMAN@ .nr PS @PSMAN@
.TH "SUDO" "@mansectsu@" "February 11, 2022" "Sudo @PACKAGE_VERSION@" "System Manager's Manual" .TH "SUDO" "@mansectsu@" "February 14, 2022" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
.nh .nh
.if n .ad l .if n .ad l
.SH "NAME" .SH "NAME"
@@ -664,11 +664,12 @@ option to list the privileges for
\fIuser\fR \fIuser\fR
instead of for the invoking user. instead of for the invoking user.
The security policy may restrict listing other users' privileges. The security policy may restrict listing other users' privileges.
The When using the
\fIsudoers\fR \fIsudoers\fR
policy only allows root or a user with the policy, only root or a user with the ability to run any command as
\fRALL\fR either root or the specified
privilege on the current host to use this option. \fIuser\fR
on the current host may use this option.
.TP 12n .TP 12n
\fB\-T\fR \fItimeout\fR, \fB\--command-timeout\fR=\fItimeout\fR \fB\-T\fR \fItimeout\fR, \fB\--command-timeout\fR=\fItimeout\fR
Used to set a timeout for the command. Used to set a timeout for the command.

View File

@@ -24,7 +24,7 @@
.nr BA @BAMAN@ .nr BA @BAMAN@
.nr LC @LCMAN@ .nr LC @LCMAN@
.nr PS @PSMAN@ .nr PS @PSMAN@
.Dd February 11, 2022 .Dd February 14, 2022
.Dt SUDO @mansectsu@ .Dt SUDO @mansectsu@
.Os Sudo @PACKAGE_VERSION@ .Os Sudo @PACKAGE_VERSION@
.Sh NAME .Sh NAME
@@ -620,11 +620,12 @@ option to list the privileges for
.Ar user .Ar user
instead of for the invoking user. instead of for the invoking user.
The security policy may restrict listing other users' privileges. The security policy may restrict listing other users' privileges.
The When using the
.Em sudoers .Em sudoers
policy only allows root or a user with the policy, only root or a user with the ability to run any command as
.Li ALL either root or the specified
privilege on the current host to use this option. .Ar user
on the current host may use this option.
.It Fl T Ar timeout , Fl -command-timeout Ns = Ns Ar timeout .It Fl T Ar timeout , Fl -command-timeout Ns = Ns Ar timeout
Used to set a timeout for the command. Used to set a timeout for the command.
If the timeout expires before the command has exited, the If the timeout expires before the command has exited, the

View File

@@ -43,24 +43,26 @@ static int
sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw, sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw,
int validated, int pwflag) int validated, int pwflag)
{ {
int match; struct passwd *root_pw = NULL;
struct sudo_nss *nss; struct sudo_nss *nss;
struct cmndspec *cs; struct cmndspec *cs;
struct privilege *priv; struct privilege *priv;
struct userspec *us; struct userspec *us;
struct defaults *def; struct defaults *def;
int nopass; int nopass, match = DENY;
enum def_tuple pwcheck; enum def_tuple pwcheck;
debug_decl(sudoers_lookup_pseudo, SUDOERS_DEBUG_PARSER); debug_decl(sudoers_lookup_pseudo, SUDOERS_DEBUG_PARSER);
pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
nopass = (pwcheck == never || pwcheck == all) ? true : false; nopass = (pwcheck == never || pwcheck == all) ? true : false;
if (list_pw == NULL)
SET(validated, FLAG_NO_CHECK);
CLR(validated, FLAG_NO_USER); CLR(validated, FLAG_NO_USER);
CLR(validated, FLAG_NO_HOST); CLR(validated, FLAG_NO_HOST);
match = DENY; if (list_pw != NULL) {
root_pw = sudo_getpwuid(ROOT_UID);
} else {
SET(validated, FLAG_NO_CHECK);
}
TAILQ_FOREACH(nss, snl, entries) { TAILQ_FOREACH(nss, snl, entries) {
if (nss->query(nss, pw) == -1) { if (nss->query(nss, pw) == -1) {
/* The query function should have printed an error message. */ /* The query function should have printed an error message. */
@@ -89,16 +91,28 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw,
} }
if (match == ALLOW) if (match == ALLOW)
continue; continue;
/* Only check the command when listing another user. */
/* Only check runas/command when listing another user. */
if (user_uid == 0 || list_pw == NULL || if (user_uid == 0 || list_pw == NULL ||
user_uid == list_pw->pw_uid || user_uid == list_pw->pw_uid) {
cmnd_matches(nss->parse_tree, cs->cmnd, cs->runchroot, match = ALLOW;
continue;
}
/* Runas user must match list user or root. */
if (userlist_matches(nss->parse_tree, list_pw,
cs->runasuserlist) == DENY ||
userlist_matches(nss->parse_tree, root_pw,
cs->runasuserlist) != ALLOW)
continue;
if (cmnd_matches(nss->parse_tree, cs->cmnd, cs->runchroot,
NULL) == ALLOW) NULL) == ALLOW)
match = ALLOW; match = ALLOW;
} }
} }
} }
} }
if (root_pw != NULL)
sudo_pw_delref(root_pw);
if (match == ALLOW || user_uid == 0) { if (match == ALLOW || user_uid == 0) {
/* User has an entry for this host. */ /* User has an entry for this host. */
SET(validated, VALIDATE_SUCCESS); SET(validated, VALIDATE_SUCCESS);

View File

@@ -1188,6 +1188,11 @@ sudoers_policy_list(int argc, char * const argv[], int verbose,
sudo_warnx(U_("unknown user %s"), list_user); sudo_warnx(U_("unknown user %s"), list_user);
debug_return_int(-1); debug_return_int(-1);
} }
/* A user may only list another user they have runas access to. */
if (runas_pw != NULL)
sudo_pw_delref(runas_pw);
runas_pw = list_pw;
sudo_pw_addref(list_pw);
} }
ret = sudoers_policy_main(argc, argv, I_LISTPW, NULL, verbose, NULL); ret = sudoers_policy_main(argc, argv, I_LISTPW, NULL, verbose, NULL);
if (list_user) { if (list_user) {