sudoers_lookup_pseudo: sync with sudoers_lookup_check

This makes sudoers_lookup_pseudo(), which is used for pseudo-command
like "list" and "validate" a bit more like sudoers_lookup_check().
Time of day checks are performed, and callbacks are supported.  We
cannot use the same code for regular commands and pseudo-commands
due to the "pwcheck == all" case.
This commit is contained in:
Todd C. Miller
2023-07-25 15:57:20 -06:00
parent d486db46cf
commit 538be58ac0

View File

@@ -54,7 +54,8 @@ runas_matches_pw(struct sudoers_parse_tree *parse_tree,
* list, verify and kill. * list, verify and kill.
*/ */
static unsigned int static unsigned int
sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw, int pwflag) sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw,
time_t now, struct sudoers_lookup_callbacks *callbacks, int pwflag)
{ {
char *saved_runchroot; char *saved_runchroot;
struct passwd *root_pw = NULL; struct passwd *root_pw = NULL;
@@ -63,7 +64,7 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw, int pwflag)
struct privilege *priv; struct privilege *priv;
struct userspec *us; struct userspec *us;
struct defaults *def; struct defaults *def;
int cmnd_match, nopass, match = DENY; int nopass, match = DENY;
unsigned int validated = 0; unsigned int validated = 0;
enum def_tuple pwcheck; enum def_tuple pwcheck;
debug_decl(sudoers_lookup_pseudo, SUDOERS_DEBUG_PARSER); debug_decl(sudoers_lookup_pseudo, SUDOERS_DEBUG_PARSER);
@@ -89,19 +90,36 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw, int pwflag)
SET(validated, VALIDATE_ERROR); SET(validated, VALIDATE_ERROR);
break; break;
} }
/*
* We have to traverse the policy forwards, not in reverse,
* to support the "pwcheck == all" case.
*/
TAILQ_FOREACH(us, &nss->parse_tree->userspecs, entries) { TAILQ_FOREACH(us, &nss->parse_tree->userspecs, entries) {
if (userlist_matches(nss->parse_tree, pw, &us->users) != ALLOW) int user_match = userlist_matches(nss->parse_tree, pw, &us->users);
if (callbacks != NULL)
callbacks->cb_userspec(us, user_match);
if (user_match != ALLOW)
continue; continue;
TAILQ_FOREACH(priv, &us->privileges, entries) { TAILQ_FOREACH(priv, &us->privileges, entries) {
int priv_nopass = UNSPEC; int priv_nopass = UNSPEC;
int host_match = hostlist_matches(nss->parse_tree, pw,
if (hostlist_matches(nss->parse_tree, pw, &priv->hostlist) != ALLOW) &priv->hostlist);
if (callbacks != NULL)
callbacks->cb_privilege(priv, host_match);
if (host_match != ALLOW)
continue; continue;
TAILQ_FOREACH(def, &priv->defaults, entries) { TAILQ_FOREACH(def, &priv->defaults, entries) {
if (strcmp(def->var, "authenticate") == 0) if (strcmp(def->var, "authenticate") == 0) {
priv_nopass = !def->op; priv_nopass = !def->op;
break;
}
} }
TAILQ_FOREACH(cs, &priv->cmndlist, entries) { TAILQ_FOREACH(cs, &priv->cmndlist, entries) {
int cmnd_match = UNSPEC;
int date_match = UNSPEC;
int runas_match = UNSPEC;
if (pwcheck == any) { if (pwcheck == any) {
if (cs->tags.nopasswd == true || priv_nopass == true) if (cs->tags.nopasswd == true || priv_nopass == true)
nopass = true; nopass = true;
@@ -109,59 +127,72 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw, int pwflag)
if (cs->tags.nopasswd != true && priv_nopass != true) if (cs->tags.nopasswd != true && priv_nopass != true)
nopass = false; nopass = false;
} }
if (match == ALLOW)
continue;
if (cs->notbefore != UNSPEC) {
date_match = now < cs->notbefore ? DENY : ALLOW;
}
if (cs->notafter != UNSPEC) {
date_match = now > cs->notafter ? DENY : ALLOW;
}
/* /*
* Root can list any user's privileges. * Root can list any user's privileges.
* A user may always list their own privileges. * A user may always list their own privileges.
*/ */
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) {
match = ALLOW; cmnd_match = ALLOW;
continue; runas_match = ALLOW;
} } else if (date_match != DENY) {
/*
* To list another user's prilileges, the runas
* user must match the list user or root.
*/
switch (runas_matches_pw(nss->parse_tree, cs, list_pw)) {
case DENY:
break;
case ALLOW:
/* /*
* RunAs user matches list user. * To list another user's prilileges, the runas
* Match on command "list" or ALL. * user must match the list user or root.
*/ */
cmnd_match = cmnd_matches(nss->parse_tree, runas_match = runas_matches_pw(nss->parse_tree, cs,
cs->cmnd, cs->runchroot, NULL); list_pw);
if (cmnd_match != UNSPEC) { switch (runas_match) {
match = cmnd_match; case DENY:
goto done; break;
} case ALLOW:
break; /*
default: * RunAs user matches list user.
/* * Match on command "list" or ALL.
* RunAs user doesn't match list user. Only allow */
* listing if the user has "sudo ALL" for root. cmnd_match = cmnd_matches(nss->parse_tree,
*/
if (root_pw != NULL && runas_matches_pw(nss->parse_tree,
cs, root_pw) == ALLOW) {
cmnd_match = cmnd_matches_all(nss->parse_tree,
cs->cmnd, cs->runchroot, NULL); cs->cmnd, cs->runchroot, NULL);
if (cmnd_match != UNSPEC) { break;
match = cmnd_match; default:
goto done; /*
* RunAs user doesn't match list user.
* Only allow listing if the user has
* "sudo ALL" for root.
*/
if (root_pw != NULL &&
runas_matches_pw(nss->parse_tree, cs,
root_pw) == ALLOW) {
runas_match = ALLOW;
cmnd_match = cmnd_matches_all(nss->parse_tree,
cs->cmnd, cs->runchroot, NULL);
} }
break;
} }
break; }
if (callbacks != NULL) {
callbacks->cb_cmndspec(cs, date_match, runas_match,
cmnd_match);
}
if (cmnd_match != UNSPEC) {
/*
* We take the last match but must process
* the entire policy for pwcheck == all.
*/
match = cmnd_match;
} }
} }
} }
} }
if (!sudo_nss_can_continue(nss, match))
break;
} }
done:
if (root_pw != NULL) if (root_pw != NULL)
sudo_pw_delref(root_pw); sudo_pw_delref(root_pw);
if (match == ALLOW || user_uid == 0) { if (match == ALLOW || user_uid == 0) {
@@ -475,7 +506,7 @@ sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, time_t now,
* Special case checking the "validate", "list" and "kill" pseudo-commands. * Special case checking the "validate", "list" and "kill" pseudo-commands.
*/ */
if (pwflag) if (pwflag)
debug_return_uint(sudoers_lookup_pseudo(snl, pw, pwflag)); debug_return_uint(sudoers_lookup_pseudo(snl, pw, now, callbacks, pwflag));
/* Need to be runas user while stat'ing things. */ /* Need to be runas user while stat'ing things. */
if (!set_perms(PERM_RUNAS)) if (!set_perms(PERM_RUNAS))