From 9f695f0fcc749b3cdebc453ba4fdeae84114f3ae Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Mon, 14 Feb 2022 13:09:55 -0700 Subject: [PATCH] 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 --- docs/sudo.man.in | 11 ++++++----- docs/sudo.mdoc.in | 11 ++++++----- plugins/sudoers/parse.c | 34 ++++++++++++++++++++++++---------- plugins/sudoers/policy.c | 5 +++++ 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/docs/sudo.man.in b/docs/sudo.man.in index 8e1855df4..c87ed2903 100644 --- a/docs/sudo.man.in +++ b/docs/sudo.man.in @@ -25,7 +25,7 @@ .nr BA @BAMAN@ .nr LC @LCMAN@ .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 .if n .ad l .SH "NAME" @@ -664,11 +664,12 @@ option to list the privileges for \fIuser\fR instead of for the invoking user. The security policy may restrict listing other users' privileges. -The +When using the \fIsudoers\fR -policy only allows root or a user with the -\fRALL\fR -privilege on the current host to use this option. +policy, only root or a user with the ability to run any command as +either root or the specified +\fIuser\fR +on the current host may use this option. .TP 12n \fB\-T\fR \fItimeout\fR, \fB\--command-timeout\fR=\fItimeout\fR Used to set a timeout for the command. diff --git a/docs/sudo.mdoc.in b/docs/sudo.mdoc.in index a666bff39..1ae34f5d3 100644 --- a/docs/sudo.mdoc.in +++ b/docs/sudo.mdoc.in @@ -24,7 +24,7 @@ .nr BA @BAMAN@ .nr LC @LCMAN@ .nr PS @PSMAN@ -.Dd February 11, 2022 +.Dd February 14, 2022 .Dt SUDO @mansectsu@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -620,11 +620,12 @@ option to list the privileges for .Ar user instead of for the invoking user. The security policy may restrict listing other users' privileges. -The +When using the .Em sudoers -policy only allows root or a user with the -.Li ALL -privilege on the current host to use this option. +policy, only root or a user with the ability to run any command as +either root or the specified +.Ar user +on the current host may use this option. .It Fl T Ar timeout , Fl -command-timeout Ns = Ns Ar timeout Used to set a timeout for the command. If the timeout expires before the command has exited, the diff --git a/plugins/sudoers/parse.c b/plugins/sudoers/parse.c index ff305e88f..b2e44e1be 100644 --- a/plugins/sudoers/parse.c +++ b/plugins/sudoers/parse.c @@ -43,24 +43,26 @@ static int sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw, int validated, int pwflag) { - int match; + struct passwd *root_pw = NULL; struct sudo_nss *nss; struct cmndspec *cs; struct privilege *priv; struct userspec *us; struct defaults *def; - int nopass; + int nopass, match = DENY; enum def_tuple pwcheck; debug_decl(sudoers_lookup_pseudo, SUDOERS_DEBUG_PARSER); pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; 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_HOST); - match = DENY; + if (list_pw != NULL) { + root_pw = sudo_getpwuid(ROOT_UID); + } else { + SET(validated, FLAG_NO_CHECK); + } TAILQ_FOREACH(nss, snl, entries) { if (nss->query(nss, pw) == -1) { /* 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) continue; - /* Only check the command when listing another user. */ + + /* Only check runas/command when listing another user. */ if (user_uid == 0 || list_pw == NULL || - user_uid == list_pw->pw_uid || - cmnd_matches(nss->parse_tree, cs->cmnd, cs->runchroot, - NULL) == ALLOW) - match = ALLOW; + user_uid == list_pw->pw_uid) { + 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) + match = ALLOW; } } } } + if (root_pw != NULL) + sudo_pw_delref(root_pw); if (match == ALLOW || user_uid == 0) { /* User has an entry for this host. */ SET(validated, VALIDATE_SUCCESS); diff --git a/plugins/sudoers/policy.c b/plugins/sudoers/policy.c index 0482b58dd..c2aee5130 100644 --- a/plugins/sudoers/policy.c +++ b/plugins/sudoers/policy.c @@ -1188,6 +1188,11 @@ sudoers_policy_list(int argc, char * const argv[], int verbose, sudo_warnx(U_("unknown user %s"), list_user); 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); if (list_user) {