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