Pass a struct to the match functions to track the resolved command.

This makes it possible to update user_cmnd and cmnd_status modified
by per-rule CHROOT settings.
This commit is contained in:
Todd C. Miller
2020-09-09 15:26:45 -06:00
parent b6dbfe5094
commit 10d3d69aa1
7 changed files with 63 additions and 24 deletions

View File

@@ -688,7 +688,7 @@ default_binding_matches(struct sudoers_parse_tree *parse_tree,
debug_return_bool(true); debug_return_bool(true);
break; break;
case DEFAULTS_CMND: case DEFAULTS_CMND:
if (cmndlist_matches(parse_tree, d->binding, NULL) == ALLOW) if (cmndlist_matches(parse_tree, d->binding, NULL, NULL) == ALLOW)
debug_return_bool(true); debug_return_bool(true);
break; break;
} }

View File

@@ -366,14 +366,15 @@ host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw,
*/ */
int int
cmndlist_matches(struct sudoers_parse_tree *parse_tree, cmndlist_matches(struct sudoers_parse_tree *parse_tree,
const struct member_list *list, const char *runchroot) const struct member_list *list, const char *runchroot,
struct cmnd_info *info)
{ {
struct member *m; struct member *m;
int matched = UNSPEC; int matched = UNSPEC;
debug_decl(cmndlist_matches, SUDOERS_DEBUG_MATCH); debug_decl(cmndlist_matches, SUDOERS_DEBUG_MATCH);
TAILQ_FOREACH_REVERSE(m, list, member_list, entries) { TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
matched = cmnd_matches(parse_tree, m, runchroot); matched = cmnd_matches(parse_tree, m, runchroot, info);
if (matched != UNSPEC) if (matched != UNSPEC)
break; break;
} }
@@ -386,7 +387,7 @@ cmndlist_matches(struct sudoers_parse_tree *parse_tree,
*/ */
int int
cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m, cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m,
const char *runchroot) const char *runchroot, struct cmnd_info *info)
{ {
struct alias *a; struct alias *a;
struct sudo_command *c; struct sudo_command *c;
@@ -402,13 +403,13 @@ cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m,
FALLTHROUGH; FALLTHROUGH;
case COMMAND: case COMMAND:
c = (struct sudo_command *)m->name; c = (struct sudo_command *)m->name;
if (command_matches(c->cmnd, c->args, runchroot, &c->digests)) if (command_matches(c->cmnd, c->args, runchroot, info, &c->digests))
matched = !m->negated; matched = !m->negated;
break; break;
case ALIAS: case ALIAS:
a = alias_get(parse_tree, m->name, CMNDALIAS); a = alias_get(parse_tree, m->name, CMNDALIAS);
if (a != NULL) { if (a != NULL) {
rc = cmndlist_matches(parse_tree, &a->members, runchroot); rc = cmndlist_matches(parse_tree, &a->members, runchroot, info);
if (rc != UNSPEC) if (rc != UNSPEC)
matched = m->negated ? !rc : rc; matched = m->negated ? !rc : rc;
alias_put(a); alias_put(a);

View File

@@ -528,8 +528,10 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
/* If it ends in '/' it is a directory spec. */ /* If it ends in '/' it is a directory spec. */
dlen = strlen(sudoers_cmnd); dlen = strlen(sudoers_cmnd);
if (sudoers_cmnd[dlen - 1] == '/') if (sudoers_cmnd[dlen - 1] == '/') {
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, runchroot, digests)); debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, runchroot,
digests));
}
/* Only proceed if user_base and basename(sudoers_cmnd) match */ /* Only proceed if user_base and basename(sudoers_cmnd) match */
if ((base = strrchr(sudoers_cmnd, '/')) == NULL) if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
@@ -584,7 +586,8 @@ bad:
*/ */
bool bool
command_matches(const char *sudoers_cmnd, const char *sudoers_args, command_matches(const char *sudoers_cmnd, const char *sudoers_args,
const char *runchroot, const struct command_digest_list *digests) const char *runchroot, struct cmnd_info *info,
const struct command_digest_list *digests)
{ {
char *saved_user_cmnd = NULL; char *saved_user_cmnd = NULL;
struct stat saved_user_stat; struct stat saved_user_stat;
@@ -605,11 +608,16 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args,
runchroot = def_runchroot; runchroot = def_runchroot;
} else { } else {
/* Rule-specific runchroot, reset user_cmnd and user_stat. */ /* Rule-specific runchroot, reset user_cmnd and user_stat. */
int status;
saved_user_cmnd = user_cmnd; saved_user_cmnd = user_cmnd;
if (user_stat != NULL) if (user_stat != NULL)
saved_user_stat = *user_stat; saved_user_stat = *user_stat;
if (set_cmnd_path(runchroot) != FOUND) status = set_cmnd_path(runchroot);
if (status != FOUND)
saved_user_cmnd = NULL; saved_user_cmnd = NULL;
if (info != NULL)
info->status = status;
} }
if (sudoers_cmnd == NULL) { if (sudoers_cmnd == NULL) {
@@ -648,7 +656,13 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args,
} }
done: done:
if (saved_user_cmnd != NULL) { if (saved_user_cmnd != NULL) {
if (info != NULL) {
info->cmnd_path = user_cmnd;
if (user_stat != NULL)
info->cmnd_stat = *user_stat;
} else {
free(user_cmnd); free(user_cmnd);
}
user_cmnd = saved_user_cmnd; user_cmnd = saved_user_cmnd;
if (user_stat != NULL) if (user_stat != NULL)
*user_stat = saved_user_stat; *user_stat = saved_user_stat;

View File

@@ -92,7 +92,8 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw,
/* Only check the command when listing another user. */ /* Only check the 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) == ALLOW) cmnd_matches(nss->parse_tree, cs->cmnd, cs->runchroot,
NULL) == ALLOW)
match = ALLOW; match = ALLOW;
} }
} }
@@ -112,7 +113,7 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw,
static int static int
sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw, sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw,
int *validated, struct cmndspec **matching_cs, int *validated, struct cmnd_info *info, struct cmndspec **matching_cs,
struct defaults_list **defs, time_t now) struct defaults_list **defs, time_t now)
{ {
int host_match, runas_match, cmnd_match; int host_match, runas_match, cmnd_match;
@@ -122,6 +123,8 @@ sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw,
struct member *matching_user; struct member *matching_user;
debug_decl(sudoers_lookup_check, SUDOERS_DEBUG_PARSER); debug_decl(sudoers_lookup_check, SUDOERS_DEBUG_PARSER);
memset(info, 0, sizeof(*info));
TAILQ_FOREACH_REVERSE(us, &nss->parse_tree->userspecs, userspec_list, entries) { TAILQ_FOREACH_REVERSE(us, &nss->parse_tree->userspecs, userspec_list, entries) {
if (userlist_matches(nss->parse_tree, pw, &us->users) != ALLOW) if (userlist_matches(nss->parse_tree, pw, &us->users) != ALLOW)
continue; continue;
@@ -147,7 +150,7 @@ sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw,
NULL); NULL);
if (runas_match == ALLOW) { if (runas_match == ALLOW) {
cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd, cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd,
cs->runchroot); cs->runchroot, info);
if (cmnd_match != UNSPEC) { if (cmnd_match != UNSPEC) {
/* /*
* If user is running command as himself, * If user is running command as himself,
@@ -167,6 +170,8 @@ sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw,
cmnd_match ? "allowed" : "denied"); cmnd_match ? "allowed" : "denied");
debug_return_int(cmnd_match); debug_return_int(cmnd_match);
} }
free(info->cmnd_path);
memset(info, 0, sizeof(*info));
} }
} }
} }
@@ -327,13 +332,15 @@ apply_cmndspec(struct cmndspec *cs)
* allowed to run the specified command on this host as the target user. * allowed to run the specified command on this host as the target user.
*/ */
int int
sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated, sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int *cmnd_status,
int pwflag) int pwflag)
{ {
struct defaults_list *defs = NULL; struct defaults_list *defs = NULL;
struct sudoers_parse_tree *parse_tree = NULL; struct sudoers_parse_tree *parse_tree = NULL;
struct cmndspec *cs = NULL; struct cmndspec *cs = NULL;
struct sudo_nss *nss; struct sudo_nss *nss;
struct cmnd_info info;
int validated = FLAG_NO_USER | FLAG_NO_HOST;
int m, match = UNSPEC; int m, match = UNSPEC;
time_t now; time_t now;
debug_decl(sudoers_lookup, SUDOERS_DEBUG_PARSER); debug_decl(sudoers_lookup, SUDOERS_DEBUG_PARSER);
@@ -357,7 +364,7 @@ sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated,
break; break;
} }
m = sudoers_lookup_check(nss, pw, &validated, &cs, &defs, now); m = sudoers_lookup_check(nss, pw, &validated, &info, &cs, &defs, now);
if (m != UNSPEC) { if (m != UNSPEC) {
match = m; match = m;
parse_tree = nss->parse_tree; parse_tree = nss->parse_tree;
@@ -367,6 +374,14 @@ sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated,
break; break;
} }
if (match != UNSPEC) { if (match != UNSPEC) {
if (info.cmnd_path != NULL) {
/* Update user_cmnd, user_stat, cmnd_status from matching entry. */
free(user_cmnd);
user_cmnd = info.cmnd_path;
if (user_stat != NULL)
*user_stat = info.cmnd_stat;
*cmnd_status = info.status;
}
if (defs != NULL) if (defs != NULL)
update_defaults(parse_tree, defs, SETDEF_GENERIC, false); update_defaults(parse_tree, defs, SETDEF_GENERIC, false);
if (!apply_cmndspec(cs)) if (!apply_cmndspec(cs))
@@ -876,7 +891,7 @@ display_cmnd_check(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
cs->runasgrouplist, NULL, NULL); cs->runasgrouplist, NULL, NULL);
if (runas_match == ALLOW) { if (runas_match == ALLOW) {
cmnd_match = cmnd_matches(parse_tree, cs->cmnd, cmnd_match = cmnd_matches(parse_tree, cs->cmnd,
cs->runchroot); cs->runchroot, NULL);
if (cmnd_match != UNSPEC) if (cmnd_match != UNSPEC)
debug_return_int(cmnd_match); debug_return_int(cmnd_match);
} }

View File

@@ -20,6 +20,7 @@
#ifndef SUDOERS_PARSE_H #ifndef SUDOERS_PARSE_H
#define SUDOERS_PARSE_H #define SUDOERS_PARSE_H
#include <sys/stat.h>
#include "sudo_queue.h" #include "sudo_queue.h"
/* Characters that must be quoted in sudoers. */ /* Characters that must be quoted in sudoers. */
@@ -281,6 +282,15 @@ struct sudoers_parse_tree {
const char *shost, *lhost; const char *shost, *lhost;
}; };
/*
* Info about the command being resolved.
*/
struct cmnd_info {
struct stat cmnd_stat;
char *cmnd_path;
int status;
};
/* alias.c */ /* alias.c */
struct rbtree *alloc_aliases(void); struct rbtree *alloc_aliases(void);
void free_aliases(struct rbtree *aliases); void free_aliases(struct rbtree *aliases);
@@ -312,7 +322,7 @@ void reparent_parse_tree(struct sudoers_parse_tree *new_tree);
bool addr_matches(char *n); bool addr_matches(char *n);
/* match_command.c */ /* match_command.c */
bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const char *runchroot, const struct command_digest_list *digests); bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const char *runchroot, struct cmnd_info *info, const struct command_digest_list *digests);
/* match_digest.c */ /* match_digest.c */
bool digest_matches(int fd, const char *file, const struct command_digest_list *digests); bool digest_matches(int fd, const char *file, const struct command_digest_list *digests);
@@ -325,8 +335,8 @@ bool hostname_matches(const char *shost, const char *lhost, const char *pattern)
bool netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user); bool netgr_matches(const char *netgr, const char *lhost, const char *shost, const char *user);
bool usergr_matches(const char *group, const char *user, const struct passwd *pw); bool usergr_matches(const char *group, const char *user, const struct passwd *pw);
bool userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw); bool userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw);
int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m, const char *runchroot); int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m, const char *runchroot, struct cmnd_info *info);
int cmndlist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *list, const char *runchroot); int cmndlist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *list, const char *runchroot, struct cmnd_info *info);
int host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const char *host, const char *shost, const struct member *m); int host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const char *host, const char *shost, const struct member *m);
int hostlist_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member_list *list); int hostlist_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw, const struct member_list *list);
int runaslist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *user_list, const struct member_list *group_list, struct member **matching_user, struct member **matching_group); int runaslist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *user_list, const struct member_list *group_list, struct member **matching_user, struct member **matching_group);
@@ -362,7 +372,7 @@ const char *digest_type_to_name(int digest_type);
/* parse.c */ /* parse.c */
struct sudo_nss_list; struct sudo_nss_list;
int sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int validated, int pwflag); int sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int *cmnd_status, int pwflag);
int display_privs(struct sudo_nss_list *snl, struct passwd *pw, bool verbose); int display_privs(struct sudo_nss_list *snl, struct passwd *pw, bool verbose);
int display_cmnd(struct sudo_nss_list *snl, struct passwd *pw); int display_cmnd(struct sudo_nss_list *snl, struct passwd *pw);

View File

@@ -418,8 +418,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
* Check sudoers sources, using the locale specified in sudoers. * Check sudoers sources, using the locale specified in sudoers.
*/ */
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
validated = sudoers_lookup(snl, sudo_user.pw, FLAG_NO_USER | FLAG_NO_HOST, validated = sudoers_lookup(snl, sudo_user.pw, &cmnd_status, pwflag);
pwflag);
if (ISSET(validated, VALIDATE_ERROR)) { if (ISSET(validated, VALIDATE_ERROR)) {
/* The lookup function should have printed an error. */ /* The lookup function should have printed an error. */
goto done; goto done;

View File

@@ -337,7 +337,7 @@ main(int argc, char *argv[])
if (runas_match == ALLOW) { if (runas_match == ALLOW) {
puts("\trunas matched"); puts("\trunas matched");
cmnd_match = cmnd_matches(&parsed_policy, cs->cmnd, cmnd_match = cmnd_matches(&parsed_policy, cs->cmnd,
cs->runchroot); cs->runchroot, NULL);
if (cmnd_match != UNSPEC) if (cmnd_match != UNSPEC)
match = cmnd_match; match = cmnd_match;
printf("\tcmnd %s\n", match == ALLOW ? "allowed" : printf("\tcmnd %s\n", match == ALLOW ? "allowed" :