Take the chroot into account when search for the command.
This could a a user-specific chroot via the -R option, a runchroot Defaults value, or a per-command CHROOT spec in the sudoers rule.
This commit is contained in:
@@ -688,7 +688,7 @@ default_binding_matches(struct sudoers_parse_tree *parse_tree,
|
||||
debug_return_bool(true);
|
||||
break;
|
||||
case DEFAULTS_CMND:
|
||||
if (cmndlist_matches(parse_tree, d->binding) == ALLOW)
|
||||
if (cmndlist_matches(parse_tree, d->binding, NULL) == ALLOW)
|
||||
debug_return_bool(true);
|
||||
break;
|
||||
}
|
||||
|
@@ -68,7 +68,8 @@ resolve_editor(const char *ed, size_t edlen, int nfiles, char **files,
|
||||
}
|
||||
|
||||
/* If we can't find the editor in the user's PATH, give up. */
|
||||
if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), 0, whitelist) != FOUND) {
|
||||
if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), NULL,
|
||||
0, whitelist) != FOUND) {
|
||||
free(editor);
|
||||
errno = ENOENT;
|
||||
debug_return_str(NULL);
|
||||
|
@@ -43,14 +43,14 @@
|
||||
* On failure, returns false.
|
||||
*/
|
||||
static bool
|
||||
cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp,
|
||||
char * const *whitelist)
|
||||
cmnd_allowed(char *cmnd, size_t cmnd_size, const char *runchroot,
|
||||
struct stat *cmnd_sbp, char * const *whitelist)
|
||||
{
|
||||
const char *cmnd_base;
|
||||
char * const *wl;
|
||||
debug_decl(cmnd_allowed, SUDOERS_DEBUG_UTIL);
|
||||
|
||||
if (!sudo_goodpath(cmnd, cmnd_sbp))
|
||||
if (!sudo_goodpath(cmnd, runchroot, cmnd_sbp))
|
||||
debug_return_bool(false);
|
||||
|
||||
if (whitelist == NULL)
|
||||
@@ -62,21 +62,20 @@ cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp,
|
||||
cmnd_base++;
|
||||
|
||||
for (wl = whitelist; *wl != NULL; wl++) {
|
||||
const char *base, *path = *wl;
|
||||
struct stat sb;
|
||||
const char *base;
|
||||
|
||||
if ((base = strrchr(*wl, '/')) == NULL)
|
||||
if ((base = strrchr(path, '/')) == NULL)
|
||||
continue; /* XXX - warn? */
|
||||
base++;
|
||||
|
||||
if (strcmp(cmnd_base, base) != 0)
|
||||
continue;
|
||||
|
||||
if (sudo_goodpath(*wl, &sb) &&
|
||||
if (sudo_goodpath(path, runchroot, &sb) &&
|
||||
sb.st_dev == cmnd_sbp->st_dev && sb.st_ino == cmnd_sbp->st_ino) {
|
||||
/* Overwrite cmnd with safe version from whitelist. */
|
||||
if (strlcpy(cmnd, *wl, cmnd_size) < cmnd_size)
|
||||
return true;
|
||||
if (strlcpy(cmnd, path, cmnd_size) < cmnd_size)
|
||||
debug_return_bool(true);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +92,8 @@ cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp,
|
||||
*/
|
||||
int
|
||||
find_path(const char *infile, char **outfile, struct stat *sbp,
|
||||
const char *path, int ignore_dot, char * const *whitelist)
|
||||
const char *path, const char *runchroot, int ignore_dot,
|
||||
char * const *whitelist)
|
||||
{
|
||||
char command[PATH_MAX];
|
||||
const char *cp, *ep, *pathend;
|
||||
@@ -111,7 +111,8 @@ find_path(const char *infile, char **outfile, struct stat *sbp,
|
||||
errno = ENAMETOOLONG;
|
||||
debug_return_int(NOT_FOUND_ERROR);
|
||||
}
|
||||
found = cmnd_allowed(command, sizeof(command), sbp, whitelist);
|
||||
found = cmnd_allowed(command, sizeof(command), runchroot, sbp,
|
||||
whitelist);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -140,7 +141,8 @@ find_path(const char *infile, char **outfile, struct stat *sbp,
|
||||
errno = ENAMETOOLONG;
|
||||
debug_return_int(NOT_FOUND_ERROR);
|
||||
}
|
||||
found = cmnd_allowed(command, sizeof(command), sbp, whitelist);
|
||||
found = cmnd_allowed(command, sizeof(command), runchroot,
|
||||
sbp, whitelist);
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
@@ -154,7 +156,8 @@ find_path(const char *infile, char **outfile, struct stat *sbp,
|
||||
errno = ENAMETOOLONG;
|
||||
debug_return_int(NOT_FOUND_ERROR);
|
||||
}
|
||||
found = cmnd_allowed(command, sizeof(command), sbp, whitelist);
|
||||
found = cmnd_allowed(command, sizeof(command), runchroot,
|
||||
sbp, whitelist);
|
||||
if (found && ignore_dot)
|
||||
debug_return_int(NOT_FOUND_DOT);
|
||||
}
|
||||
|
@@ -39,14 +39,24 @@
|
||||
* Verify that path is a normal file and executable by root.
|
||||
*/
|
||||
bool
|
||||
sudo_goodpath(const char *path, struct stat *sbp)
|
||||
sudo_goodpath(const char *path, const char *runchroot, struct stat *sbp)
|
||||
{
|
||||
bool ret = false;
|
||||
debug_decl(sudo_goodpath, SUDOERS_DEBUG_UTIL);
|
||||
|
||||
if (path != NULL) {
|
||||
char pathbuf[PATH_MAX];
|
||||
struct stat sb;
|
||||
|
||||
if (runchroot != NULL) {
|
||||
const int len =
|
||||
snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, path);
|
||||
if (len >= ssizeof(pathbuf)) {
|
||||
errno = ENAMETOOLONG;
|
||||
goto done;
|
||||
}
|
||||
path = pathbuf;
|
||||
}
|
||||
if (sbp == NULL)
|
||||
sbp = &sb;
|
||||
|
||||
@@ -58,6 +68,6 @@ sudo_goodpath(const char *path, struct stat *sbp)
|
||||
errno = EACCES;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
debug_return_bool(ret);
|
||||
}
|
||||
|
@@ -366,14 +366,14 @@ host_matches(struct sudoers_parse_tree *parse_tree, const struct passwd *pw,
|
||||
*/
|
||||
int
|
||||
cmndlist_matches(struct sudoers_parse_tree *parse_tree,
|
||||
const struct member_list *list)
|
||||
const struct member_list *list, const char *runchroot)
|
||||
{
|
||||
struct member *m;
|
||||
int matched = UNSPEC;
|
||||
debug_decl(cmndlist_matches, SUDOERS_DEBUG_MATCH);
|
||||
|
||||
TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
|
||||
matched = cmnd_matches(parse_tree, m);
|
||||
matched = cmnd_matches(parse_tree, m, runchroot);
|
||||
if (matched != UNSPEC)
|
||||
break;
|
||||
}
|
||||
@@ -385,7 +385,8 @@ cmndlist_matches(struct sudoers_parse_tree *parse_tree,
|
||||
* Returns ALLOW, DENY or UNSPEC.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
struct alias *a;
|
||||
struct sudo_command *c;
|
||||
@@ -401,13 +402,13 @@ cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m)
|
||||
FALLTHROUGH;
|
||||
case COMMAND:
|
||||
c = (struct sudo_command *)m->name;
|
||||
if (command_matches(c->cmnd, c->args, &c->digests))
|
||||
if (command_matches(c->cmnd, c->args, runchroot, &c->digests))
|
||||
matched = !m->negated;
|
||||
break;
|
||||
case ALIAS:
|
||||
a = alias_get(parse_tree, m->name, CMNDALIAS);
|
||||
if (a != NULL) {
|
||||
rc = cmndlist_matches(parse_tree, &a->members);
|
||||
rc = cmndlist_matches(parse_tree, &a->members, runchroot);
|
||||
if (rc != UNSPEC)
|
||||
matched = m->negated ? !rc : rc;
|
||||
alias_put(a);
|
||||
|
@@ -84,12 +84,24 @@ command_args_match(const char *sudoers_cmnd, const char *sudoers_args)
|
||||
* Returns true on success, else false.
|
||||
*/
|
||||
static bool
|
||||
do_stat(int fd, const char *path, struct stat *sb)
|
||||
do_stat(int fd, const char *path, const char *runchroot, struct stat *sb)
|
||||
{
|
||||
char pathbuf[PATH_MAX];
|
||||
debug_decl(do_stat, SUDOERS_DEBUG_MATCH);
|
||||
|
||||
if (fd != -1)
|
||||
debug_return_bool(fstat(fd, sb) == 0);
|
||||
|
||||
/* Make path relative to the new root, if any. */
|
||||
if (runchroot != NULL) {
|
||||
const int len =
|
||||
snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, path);
|
||||
if (len >= ssizeof(pathbuf)) {
|
||||
errno = ENAMETOOLONG;
|
||||
debug_return_bool(false);
|
||||
}
|
||||
path = pathbuf;
|
||||
}
|
||||
debug_return_bool(stat(path, sb) == 0);
|
||||
}
|
||||
|
||||
@@ -119,15 +131,28 @@ is_script(int fd)
|
||||
* Returns false on error, else true.
|
||||
*/
|
||||
static bool
|
||||
open_cmnd(const char *path, const struct command_digest_list *digests, int *fdp)
|
||||
open_cmnd(const char *path, const char *runchroot,
|
||||
const struct command_digest_list *digests, int *fdp)
|
||||
{
|
||||
int fd = -1;
|
||||
char pathbuf[PATH_MAX];
|
||||
debug_decl(open_cmnd, SUDOERS_DEBUG_MATCH);
|
||||
|
||||
/* Only open the file for fdexec or for digest matching. */
|
||||
if (def_fdexec != always && TAILQ_EMPTY(digests))
|
||||
debug_return_bool(true);
|
||||
|
||||
/* Make path relative to the new root, if any. */
|
||||
if (runchroot != NULL) {
|
||||
const int len =
|
||||
snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, path);
|
||||
if (len >= ssizeof(pathbuf)) {
|
||||
errno = ENAMETOOLONG;
|
||||
debug_return_bool(false);
|
||||
}
|
||||
path = pathbuf;
|
||||
}
|
||||
|
||||
fd = open(path, O_RDONLY|O_NONBLOCK);
|
||||
# ifdef O_EXEC
|
||||
if (fd == -1 && errno == EACCES && TAILQ_EMPTY(digests)) {
|
||||
@@ -189,16 +214,29 @@ set_cmnd_fd(int fd)
|
||||
* Return true if user_cmnd names one of the inodes in dir, else false.
|
||||
*/
|
||||
static bool
|
||||
command_matches_dir(const char *sudoers_dir, size_t dlen,
|
||||
command_matches_dir(const char *sudoers_dir, size_t dlen, const char *runchroot,
|
||||
const struct command_digest_list *digests)
|
||||
{
|
||||
char buf[PATH_MAX], sdbuf[PATH_MAX];
|
||||
struct stat sudoers_stat;
|
||||
struct dirent *dent;
|
||||
char buf[PATH_MAX];
|
||||
size_t chrootlen = 0;
|
||||
int fd = -1;
|
||||
DIR *dirp;
|
||||
debug_decl(command_matches_dir, SUDOERS_DEBUG_MATCH);
|
||||
|
||||
/* Make sudoers_dir relative to the new root, if any. */
|
||||
if (runchroot != NULL) {
|
||||
const int len =
|
||||
snprintf(sdbuf, sizeof(sdbuf), "%s%s", runchroot, sudoers_dir);
|
||||
if (len >= ssizeof(sdbuf)) {
|
||||
errno = ENAMETOOLONG;
|
||||
debug_return_bool(false);
|
||||
}
|
||||
sudoers_dir = sdbuf;
|
||||
chrootlen = strlen(runchroot);
|
||||
}
|
||||
|
||||
/*
|
||||
* Grot through directory entries, looking for user_base.
|
||||
*/
|
||||
@@ -226,9 +264,9 @@ command_matches_dir(const char *sudoers_dir, size_t dlen,
|
||||
continue;
|
||||
|
||||
/* Open the file for fdexec or for digest matching. */
|
||||
if (!open_cmnd(buf, digests, &fd))
|
||||
if (!open_cmnd(buf, NULL, digests, &fd))
|
||||
continue;
|
||||
if (!do_stat(fd, buf, &sudoers_stat))
|
||||
if (!do_stat(fd, buf, NULL, &sudoers_stat))
|
||||
continue;
|
||||
|
||||
if (user_stat == NULL ||
|
||||
@@ -237,7 +275,7 @@ command_matches_dir(const char *sudoers_dir, size_t dlen,
|
||||
if (!digest_matches(fd, buf, digests))
|
||||
continue;
|
||||
free(safe_cmnd);
|
||||
if ((safe_cmnd = strdup(buf)) == NULL) {
|
||||
if ((safe_cmnd = strdup(buf + chrootlen)) == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
dent = NULL;
|
||||
@@ -257,7 +295,8 @@ command_matches_dir(const char *sudoers_dir, size_t dlen,
|
||||
}
|
||||
|
||||
static bool
|
||||
command_matches_all(const struct command_digest_list *digests)
|
||||
command_matches_all(const char *runchroot,
|
||||
const struct command_digest_list *digests)
|
||||
{
|
||||
struct stat sb; /* XXX - unused */
|
||||
int fd = -1;
|
||||
@@ -265,9 +304,9 @@ command_matches_all(const struct command_digest_list *digests)
|
||||
|
||||
if (user_cmnd[0] == '/') {
|
||||
/* Open the file for fdexec or for digest matching. */
|
||||
if (!open_cmnd(user_cmnd, digests, &fd))
|
||||
if (!open_cmnd(user_cmnd, runchroot, digests, &fd))
|
||||
goto bad;
|
||||
if (!do_stat(fd, user_cmnd, &sb))
|
||||
if (!do_stat(fd, user_cmnd, runchroot, &sb))
|
||||
goto bad;
|
||||
}
|
||||
|
||||
@@ -288,7 +327,7 @@ bad:
|
||||
|
||||
static bool
|
||||
command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args,
|
||||
const struct command_digest_list *digests)
|
||||
const char *runchroot, const struct command_digest_list *digests)
|
||||
{
|
||||
struct stat sb; /* XXX - unused */
|
||||
int fd = -1;
|
||||
@@ -299,15 +338,17 @@ command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args,
|
||||
* a) there are no args in sudoers OR
|
||||
* b) there are no args on command line and none required by sudoers OR
|
||||
* c) there are args in sudoers and on command line and they match
|
||||
* else return false.
|
||||
* else return false.
|
||||
*
|
||||
* Neither sudoers_cmnd nor user_cmnd are relative to runchroot.
|
||||
*/
|
||||
if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
|
||||
debug_return_bool(false);
|
||||
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
||||
/* Open the file for fdexec or for digest matching. */
|
||||
if (!open_cmnd(user_cmnd, digests, &fd))
|
||||
if (!open_cmnd(user_cmnd, runchroot, digests, &fd))
|
||||
goto bad;
|
||||
if (!do_stat(fd, user_cmnd, &sb))
|
||||
if (!do_stat(fd, user_cmnd, runchroot, &sb))
|
||||
goto bad;
|
||||
/* Check digest of user_cmnd since sudoers_cmnd is a pattern. */
|
||||
if (!digest_matches(fd, user_cmnd, digests))
|
||||
@@ -328,13 +369,14 @@ bad:
|
||||
|
||||
static bool
|
||||
command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
||||
const struct command_digest_list *digests)
|
||||
const char *runchroot, const struct command_digest_list *digests)
|
||||
{
|
||||
struct stat sudoers_stat;
|
||||
bool bad_digest = false;
|
||||
char **ap, *base, *cp;
|
||||
char pathbuf[PATH_MAX];
|
||||
int fd = -1;
|
||||
size_t dlen;
|
||||
size_t dlen, chrootlen = 0;
|
||||
glob_t gl;
|
||||
debug_decl(command_matches_glob, SUDOERS_DEBUG_MATCH);
|
||||
|
||||
@@ -351,6 +393,19 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
||||
debug_return_bool(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sudoers_cmnd relative to the new root, if any. */
|
||||
if (runchroot != NULL) {
|
||||
const int len =
|
||||
snprintf(pathbuf, sizeof(pathbuf), "%s%s", runchroot, sudoers_cmnd);
|
||||
if (len >= ssizeof(pathbuf)) {
|
||||
errno = ENAMETOOLONG;
|
||||
debug_return_bool(false);
|
||||
}
|
||||
sudoers_cmnd = pathbuf;
|
||||
chrootlen = strlen(runchroot);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if we find a match in the glob(3) results AND
|
||||
* a) there are no args in sudoers OR
|
||||
@@ -369,12 +424,15 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
/* Remove the runchroot, if any. */
|
||||
cp += chrootlen;
|
||||
|
||||
if (strcmp(cp, user_cmnd) != 0)
|
||||
continue;
|
||||
/* Open the file for fdexec or for digest matching. */
|
||||
if (!open_cmnd(cp, digests, &fd))
|
||||
if (!open_cmnd(cp, runchroot, digests, &fd))
|
||||
continue;
|
||||
if (!do_stat(fd, cp, &sudoers_stat))
|
||||
if (!do_stat(fd, cp, runchroot, &sudoers_stat))
|
||||
continue;
|
||||
if (user_stat == NULL ||
|
||||
(user_stat->st_dev == sudoers_stat.st_dev &&
|
||||
@@ -405,10 +463,13 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
/* Remove the runchroot, if any. */
|
||||
cp += chrootlen;
|
||||
|
||||
/* If it ends in '/' it is a directory spec. */
|
||||
dlen = strlen(cp);
|
||||
if (cp[dlen - 1] == '/') {
|
||||
if (command_matches_dir(cp, dlen, digests))
|
||||
if (command_matches_dir(cp, dlen, runchroot, digests))
|
||||
debug_return_bool(true);
|
||||
continue;
|
||||
}
|
||||
@@ -422,9 +483,9 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
||||
continue;
|
||||
|
||||
/* Open the file for fdexec or for digest matching. */
|
||||
if (!open_cmnd(cp, digests, &fd))
|
||||
if (!open_cmnd(cp, runchroot, digests, &fd))
|
||||
continue;
|
||||
if (!do_stat(fd, cp, &sudoers_stat))
|
||||
if (!do_stat(fd, cp, runchroot, &sudoers_stat))
|
||||
continue;
|
||||
if (user_stat == NULL ||
|
||||
(user_stat->st_dev == sudoers_stat.st_dev &&
|
||||
@@ -456,7 +517,8 @@ done:
|
||||
}
|
||||
|
||||
static bool
|
||||
command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests)
|
||||
command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
|
||||
const char *runchroot, const struct command_digest_list *digests)
|
||||
{
|
||||
struct stat sudoers_stat;
|
||||
const char *base;
|
||||
@@ -467,7 +529,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
|
||||
/* If it ends in '/' it is a directory spec. */
|
||||
dlen = strlen(sudoers_cmnd);
|
||||
if (sudoers_cmnd[dlen - 1] == '/')
|
||||
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, digests));
|
||||
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, runchroot, digests));
|
||||
|
||||
/* Only proceed if user_base and basename(sudoers_cmnd) match */
|
||||
if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
|
||||
@@ -478,7 +540,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
|
||||
debug_return_bool(false);
|
||||
|
||||
/* Open the file for fdexec or for digest matching. */
|
||||
if (!open_cmnd(sudoers_cmnd, digests, &fd))
|
||||
if (!open_cmnd(sudoers_cmnd, runchroot, digests, &fd))
|
||||
goto bad;
|
||||
|
||||
/*
|
||||
@@ -488,7 +550,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
|
||||
* c) there are args in sudoers and on command line and they match
|
||||
* d) there is a digest and it matches
|
||||
*/
|
||||
if (user_stat != NULL && do_stat(fd, sudoers_cmnd, &sudoers_stat)) {
|
||||
if (user_stat != NULL && do_stat(fd, sudoers_cmnd, runchroot, &sudoers_stat)) {
|
||||
if (user_stat->st_dev != sudoers_stat.st_dev ||
|
||||
user_stat->st_ino != sudoers_stat.st_ino)
|
||||
goto bad;
|
||||
@@ -521,13 +583,37 @@ bad:
|
||||
* otherwise, return true if user_cmnd names one of the inodes in path.
|
||||
*/
|
||||
bool
|
||||
command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests)
|
||||
command_matches(const char *sudoers_cmnd, const char *sudoers_args,
|
||||
const char *runchroot, const struct command_digest_list *digests)
|
||||
{
|
||||
char *saved_user_cmnd = NULL;
|
||||
struct stat saved_user_stat;
|
||||
bool rc = false;
|
||||
debug_decl(command_matches, SUDOERS_DEBUG_MATCH);
|
||||
|
||||
if (user_runchroot != NULL) {
|
||||
if (runchroot != NULL && strcmp(runchroot, "*") != 0 &&
|
||||
strcmp(runchroot, user_runchroot) != 0) {
|
||||
/* CHROOT mismatch */
|
||||
goto done;
|
||||
}
|
||||
/* User-specified runchroot (user_stat already set appropriately). */
|
||||
runchroot = user_runchroot;
|
||||
} else if (runchroot == NULL) {
|
||||
/* No rule-specific runchroot, use global (user_stat already set). */
|
||||
if (def_runchroot != NULL && strcmp(def_runchroot, "*") != '\0')
|
||||
runchroot = def_runchroot;
|
||||
} else {
|
||||
/* Rule-specific runchroot, reset user_cmnd and user_stat. */
|
||||
saved_user_cmnd = user_cmnd;
|
||||
if (user_stat != NULL)
|
||||
saved_user_stat = *user_stat;
|
||||
if (set_cmnd_path(runchroot) != FOUND)
|
||||
saved_user_cmnd = NULL;
|
||||
}
|
||||
|
||||
if (sudoers_cmnd == NULL) {
|
||||
rc = command_matches_all(digests);
|
||||
rc = command_matches_all(runchroot, digests);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -554,17 +640,24 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct
|
||||
* use glob(3) and/or fnmatch(3) to do the matching.
|
||||
*/
|
||||
if (def_fast_glob)
|
||||
rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digests);
|
||||
rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, runchroot, digests);
|
||||
else
|
||||
rc = command_matches_glob(sudoers_cmnd, sudoers_args, digests);
|
||||
rc = command_matches_glob(sudoers_cmnd, sudoers_args, runchroot, digests);
|
||||
} else {
|
||||
rc = command_matches_normal(sudoers_cmnd, sudoers_args, digests);
|
||||
rc = command_matches_normal(sudoers_cmnd, sudoers_args, runchroot, digests);
|
||||
}
|
||||
done:
|
||||
if (saved_user_cmnd != NULL) {
|
||||
free(user_cmnd);
|
||||
user_cmnd = saved_user_cmnd;
|
||||
if (user_stat != NULL)
|
||||
*user_stat = saved_user_stat;
|
||||
}
|
||||
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
|
||||
"user command \"%s%s%s\" matches sudoers command \"%s%s%s\": %s",
|
||||
"user command \"%s%s%s\" matches sudoers command \"%s%s%s\"%s%s: %s",
|
||||
user_cmnd, user_args ? " " : "", user_args ? user_args : "",
|
||||
sudoers_cmnd, sudoers_args ? " " : "", sudoers_args ? sudoers_args : "",
|
||||
runchroot ? ", chroot " : "", runchroot ? runchroot : "",
|
||||
rc ? "true" : "false");
|
||||
debug_return_bool(rc);
|
||||
}
|
||||
|
@@ -92,7 +92,7 @@ sudoers_lookup_pseudo(struct sudo_nss_list *snl, struct passwd *pw,
|
||||
/* Only check the 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) == ALLOW)
|
||||
cmnd_matches(nss->parse_tree, cs->cmnd, cs->runchroot) == ALLOW)
|
||||
match = ALLOW;
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,8 @@ sudoers_lookup_check(struct sudo_nss *nss, struct passwd *pw,
|
||||
cs->runasuserlist, cs->runasgrouplist, &matching_user,
|
||||
NULL);
|
||||
if (runas_match == ALLOW) {
|
||||
cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd);
|
||||
cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd,
|
||||
cs->runchroot);
|
||||
if (cmnd_match != UNSPEC) {
|
||||
/*
|
||||
* If user is running command as himself,
|
||||
@@ -196,6 +197,8 @@ apply_cmndspec(struct cmndspec *cs)
|
||||
} else {
|
||||
user_role = def_role;
|
||||
}
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"user_role -> %s", user_role);
|
||||
}
|
||||
if (user_type == NULL) {
|
||||
if (cs->type != NULL) {
|
||||
@@ -208,6 +211,8 @@ apply_cmndspec(struct cmndspec *cs)
|
||||
} else {
|
||||
user_type = def_type;
|
||||
}
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"user_type -> %s", user_type);
|
||||
}
|
||||
#endif /* HAVE_SELINUX */
|
||||
#ifdef HAVE_PRIV_SET
|
||||
@@ -223,6 +228,8 @@ apply_cmndspec(struct cmndspec *cs)
|
||||
} else {
|
||||
runas_privs = def_privs;
|
||||
}
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"runas_privs -> %s", runas_privs);
|
||||
}
|
||||
if (runas_limitprivs == NULL) {
|
||||
if (cs->limitprivs != NULL) {
|
||||
@@ -235,10 +242,15 @@ apply_cmndspec(struct cmndspec *cs)
|
||||
} else {
|
||||
runas_limitprivs = def_limitprivs;
|
||||
}
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"runas_limitprivs -> %s", runas_limitprivs);
|
||||
}
|
||||
#endif /* HAVE_PRIV_SET */
|
||||
if (cs->timeout > 0)
|
||||
if (cs->timeout > 0) {
|
||||
def_command_timeout = cs->timeout;
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_command_timeout -> %d", def_command_timeout);
|
||||
}
|
||||
if (cs->runcwd != NULL) {
|
||||
free(def_runcwd);
|
||||
def_runcwd = strdup(cs->runcwd);
|
||||
@@ -247,6 +259,8 @@ apply_cmndspec(struct cmndspec *cs)
|
||||
U_("unable to allocate memory"));
|
||||
debug_return_bool(false);
|
||||
}
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_runcwd -> %s", def_runcwd);
|
||||
}
|
||||
if (cs->runchroot != NULL) {
|
||||
free(def_runchroot);
|
||||
@@ -256,28 +270,53 @@ apply_cmndspec(struct cmndspec *cs)
|
||||
U_("unable to allocate memory"));
|
||||
debug_return_bool(false);
|
||||
}
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_runchroot -> %s", def_runchroot);
|
||||
}
|
||||
if (cs->tags.nopasswd != UNSPEC)
|
||||
if (cs->tags.nopasswd != UNSPEC) {
|
||||
def_authenticate = !cs->tags.nopasswd;
|
||||
if (cs->tags.noexec != UNSPEC)
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_authenticate -> %s", def_authenticate ? "true" : "false");
|
||||
}
|
||||
if (cs->tags.noexec != UNSPEC) {
|
||||
def_noexec = cs->tags.noexec;
|
||||
if (cs->tags.setenv != UNSPEC)
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_noexec -> %s", def_noexec ? "true" : "false");
|
||||
}
|
||||
if (cs->tags.setenv != UNSPEC) {
|
||||
def_setenv = cs->tags.setenv;
|
||||
if (cs->tags.log_input != UNSPEC)
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_setenv -> %s", def_setenv ? "true" : "false");
|
||||
}
|
||||
if (cs->tags.log_input != UNSPEC) {
|
||||
def_log_input = cs->tags.log_input;
|
||||
if (cs->tags.log_output != UNSPEC)
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_log_input -> %s", def_log_input ? "true" : "false");
|
||||
}
|
||||
if (cs->tags.log_output != UNSPEC) {
|
||||
def_log_output = cs->tags.log_output;
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_log_output -> %s", def_log_output ? "true" : "false");
|
||||
}
|
||||
if (cs->tags.send_mail != UNSPEC) {
|
||||
if (cs->tags.send_mail) {
|
||||
def_mail_all_cmnds = true;
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_mail_all_cmnds -> true");
|
||||
} else {
|
||||
def_mail_all_cmnds = false;
|
||||
def_mail_always = false;
|
||||
def_mail_no_perms = false;
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_mail_all_cmnds -> false, def_mail_always -> false, "
|
||||
"def_mail_no_perms -> false");
|
||||
}
|
||||
}
|
||||
if (cs->tags.follow != UNSPEC)
|
||||
if (cs->tags.follow != UNSPEC) {
|
||||
def_sudoedit_follow = cs->tags.follow;
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_sudoedit_follow -> %s", def_sudoedit_follow ? "true" : "false");
|
||||
}
|
||||
}
|
||||
|
||||
debug_return_bool(true);
|
||||
@@ -836,7 +875,8 @@ display_cmnd_check(struct sudoers_parse_tree *parse_tree, struct passwd *pw,
|
||||
runas_match = runaslist_matches(parse_tree, cs->runasuserlist,
|
||||
cs->runasgrouplist, NULL, NULL);
|
||||
if (runas_match == ALLOW) {
|
||||
cmnd_match = cmnd_matches(parse_tree, cs->cmnd);
|
||||
cmnd_match = cmnd_matches(parse_tree, cs->cmnd,
|
||||
cs->runchroot);
|
||||
if (cmnd_match != UNSPEC)
|
||||
debug_return_int(cmnd_match);
|
||||
}
|
||||
|
@@ -312,7 +312,7 @@ void reparent_parse_tree(struct sudoers_parse_tree *new_tree);
|
||||
bool addr_matches(char *n);
|
||||
|
||||
/* match_command.c */
|
||||
bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests);
|
||||
bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const char *runchroot, const struct command_digest_list *digests);
|
||||
|
||||
/* match_digest.c */
|
||||
bool digest_matches(int fd, const char *file, const struct command_digest_list *digests);
|
||||
@@ -325,8 +325,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 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);
|
||||
int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m);
|
||||
int cmndlist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *list);
|
||||
int cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m, const char *runchroot);
|
||||
int cmndlist_matches(struct sudoers_parse_tree *parse_tree, const struct member_list *list, const char *runchroot);
|
||||
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 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);
|
||||
|
@@ -80,6 +80,13 @@ get_interfaces(void)
|
||||
return &dummy;
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
int
|
||||
set_cmnd_path(const char *runchroot)
|
||||
{
|
||||
return FOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up the hostname and set user_host and user_shost.
|
||||
*/
|
||||
|
@@ -275,6 +275,59 @@ done:
|
||||
debug_return_str(iolog_path);
|
||||
}
|
||||
|
||||
static int
|
||||
check_runchroot(void)
|
||||
{
|
||||
debug_decl(check_runchroot, SUDOERS_DEBUG_PLUGIN);
|
||||
|
||||
if (user_runchroot == NULL)
|
||||
debug_return_bool(true);
|
||||
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_runchroot %s, user_runchroot %s",
|
||||
def_runchroot ? def_runchroot : "NULL", user_runchroot);
|
||||
|
||||
if (def_runchroot == NULL || (strcmp(def_runchroot, "*") != 0 &&
|
||||
strcmp(def_runchroot, user_runchroot) != 0)) {
|
||||
audit_failure(NewArgv,
|
||||
N_("user not allowed to change root directory to %s"),
|
||||
user_runchroot);
|
||||
sudo_warnx("%s", U_("you are not permitted to use the -R option"));
|
||||
debug_return_bool(false);
|
||||
}
|
||||
free(def_runchroot);
|
||||
if ((def_runchroot = strdup(user_runchroot)) == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
debug_return_bool(-1);
|
||||
}
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
||||
static int
|
||||
check_runcwd(void)
|
||||
{
|
||||
debug_decl(check_runcwd, SUDOERS_DEBUG_PLUGIN);
|
||||
|
||||
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||
"def_runcwd %s, user_runcwd %s, user_cwd %s",
|
||||
def_runcwd, user_runcwd, user_cwd);
|
||||
|
||||
if (strcmp(user_cwd, user_runcwd) != 0) {
|
||||
if (def_runcwd == NULL || strcmp(def_runcwd, "*") != 0) {
|
||||
audit_failure(NewArgv,
|
||||
N_("user not allowed to change directory to %s"), user_runcwd);
|
||||
sudo_warnx("%s", U_("you are not permitted to use the -D option"));
|
||||
debug_return_bool(false);
|
||||
}
|
||||
free(def_runcwd);
|
||||
if ((def_runcwd = strdup(user_runcwd)) == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
debug_return_int(-1);
|
||||
}
|
||||
}
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
||||
int
|
||||
sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
|
||||
bool verbose, void *closure)
|
||||
@@ -396,32 +449,24 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
|
||||
}
|
||||
}
|
||||
|
||||
if (user_runchroot != NULL) {
|
||||
if (def_runchroot == NULL || strcmp(def_runchroot, "*") != 0) {
|
||||
audit_failure(NewArgv,
|
||||
N_("user not allowed to change root directory to %s"),
|
||||
user_runchroot);
|
||||
sudo_warnx("%s", U_("you are not permitted to use the -R option"));
|
||||
goto bad;
|
||||
}
|
||||
free(def_runchroot);
|
||||
if ((def_runchroot = strdup(user_runchroot)) == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
goto done;
|
||||
}
|
||||
/* Check whether user_runchroot is permitted (if specified). */
|
||||
switch (check_runchroot()) {
|
||||
case true:
|
||||
break;
|
||||
case false:
|
||||
goto bad;
|
||||
default:
|
||||
goto done;
|
||||
}
|
||||
if (strcmp(user_cwd, user_runcwd) != 0) {
|
||||
if (def_runcwd == NULL || strcmp(def_runcwd, "*") != 0) {
|
||||
audit_failure(NewArgv,
|
||||
N_("user not allowed to change directory to %s"), user_runcwd);
|
||||
sudo_warnx("%s", U_("you are not permitted to use the -D option"));
|
||||
goto bad;
|
||||
}
|
||||
free(def_runcwd);
|
||||
if ((def_runcwd = strdup(user_runcwd)) == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check whether user_runcwd is permitted (if specified). */
|
||||
switch (check_runcwd()) {
|
||||
case true:
|
||||
break;
|
||||
case false:
|
||||
goto bad;
|
||||
default:
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -861,6 +906,38 @@ init_vars(char * const envp[])
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in user_cmnd and user_stat variables.
|
||||
* Does not fill in user_base.
|
||||
*/
|
||||
int
|
||||
set_cmnd_path(const char *runchroot)
|
||||
{
|
||||
char *path = user_path;
|
||||
int ret;
|
||||
debug_decl(set_cmnd_path, SUDOERS_DEBUG_PLUGIN);
|
||||
|
||||
if (def_secure_path && !user_is_exempt())
|
||||
path = def_secure_path;
|
||||
if (!set_perms(PERM_RUNAS))
|
||||
debug_return_int(NOT_FOUND_ERROR);
|
||||
ret = find_path(NewArgv[0], &user_cmnd, user_stat, path,
|
||||
runchroot, def_ignore_dot, NULL);
|
||||
if (!restore_perms())
|
||||
debug_return_int(NOT_FOUND_ERROR);
|
||||
if (ret == NOT_FOUND) {
|
||||
/* Failed as root, try as invoking user. */
|
||||
if (!set_perms(PERM_USER))
|
||||
debug_return_int(false);
|
||||
ret = find_path(NewArgv[0], &user_cmnd, user_stat, path,
|
||||
runchroot, def_ignore_dot, NULL);
|
||||
if (!restore_perms())
|
||||
debug_return_int(NOT_FOUND_ERROR);
|
||||
}
|
||||
|
||||
debug_return_int(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in user_cmnd, user_args, user_base and user_stat variables
|
||||
* and apply any command-specific defaults entries.
|
||||
@@ -869,7 +946,6 @@ static int
|
||||
set_cmnd(void)
|
||||
{
|
||||
struct sudo_nss *nss;
|
||||
char *path = user_path;
|
||||
int ret = FOUND;
|
||||
debug_decl(set_cmnd, SUDOERS_DEBUG_PLUGIN);
|
||||
|
||||
@@ -886,23 +962,12 @@ set_cmnd(void)
|
||||
|
||||
if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
|
||||
if (ISSET(sudo_mode, MODE_RUN | MODE_CHECK)) {
|
||||
if (def_secure_path && !user_is_exempt())
|
||||
path = def_secure_path;
|
||||
if (!set_perms(PERM_RUNAS))
|
||||
debug_return_int(-1);
|
||||
ret = find_path(NewArgv[0], &user_cmnd, user_stat, path,
|
||||
def_ignore_dot, NULL);
|
||||
if (!restore_perms())
|
||||
debug_return_int(-1);
|
||||
if (ret == NOT_FOUND) {
|
||||
/* Failed as root, try as invoking user. */
|
||||
if (!set_perms(PERM_USER))
|
||||
debug_return_int(-1);
|
||||
ret = find_path(NewArgv[0], &user_cmnd, user_stat, path,
|
||||
def_ignore_dot, NULL);
|
||||
if (!restore_perms())
|
||||
debug_return_int(-1);
|
||||
}
|
||||
const char *runchroot = user_runchroot;
|
||||
if (runchroot == NULL && def_runchroot != NULL &&
|
||||
strcmp(def_runchroot, "*") != 0)
|
||||
runchroot = def_runchroot;
|
||||
|
||||
ret = set_cmnd_path(runchroot);
|
||||
if (ret == NOT_FOUND_ERROR) {
|
||||
if (errno == ENAMETOOLONG) {
|
||||
audit_failure(NewArgv, N_("command too long"));
|
||||
@@ -922,7 +987,7 @@ set_cmnd(void)
|
||||
size += strlen(*av) + 1;
|
||||
if (size == 0 || (user_args = malloc(size)) == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
debug_return_int(-1);
|
||||
debug_return_int(NOT_FOUND_ERROR);
|
||||
}
|
||||
if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
|
||||
/*
|
||||
@@ -944,7 +1009,7 @@ set_cmnd(void)
|
||||
n = strlcpy(to, *av, size - (to - user_args));
|
||||
if (n >= size - (to - user_args)) {
|
||||
sudo_warnx(U_("internal error, %s overflow"), __func__);
|
||||
debug_return_int(-1);
|
||||
debug_return_int(NOT_FOUND_ERROR);
|
||||
}
|
||||
to += n;
|
||||
*to++ = ' ';
|
||||
|
@@ -263,11 +263,12 @@ struct timespec;
|
||||
#define YY_DECL int sudoerslex(void)
|
||||
|
||||
/* goodpath.c */
|
||||
bool sudo_goodpath(const char *path, struct stat *sbp);
|
||||
bool sudo_goodpath(const char *path, const char *runchroot, struct stat *sbp);
|
||||
|
||||
/* findpath.c */
|
||||
int find_path(const char *infile, char **outfile, struct stat *sbp,
|
||||
const char *path, int ignore_dot, char * const *whitelist);
|
||||
const char *path, const char *runchroot, int ignore_dot,
|
||||
char * const *whitelist);
|
||||
|
||||
/* check.c */
|
||||
int check_user(int validate, int mode);
|
||||
@@ -394,6 +395,7 @@ bool matches_env_pattern(const char *pattern, const char *var, bool *full_match)
|
||||
|
||||
/* sudoers.c */
|
||||
FILE *open_sudoers(const char *, bool, bool *);
|
||||
int set_cmnd_path(const char *runchroot);
|
||||
int sudoers_init(void *info, char * const envp[]);
|
||||
int sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], bool verbose, void *closure);
|
||||
void sudoers_cleanup(void);
|
||||
|
@@ -336,7 +336,8 @@ main(int argc, char *argv[])
|
||||
cs->runasuserlist, cs->runasgrouplist, NULL, NULL);
|
||||
if (runas_match == ALLOW) {
|
||||
puts("\trunas matched");
|
||||
cmnd_match = cmnd_matches(&parsed_policy, cs->cmnd);
|
||||
cmnd_match = cmnd_matches(&parsed_policy, cs->cmnd,
|
||||
cs->runchroot);
|
||||
if (cmnd_match != UNSPEC)
|
||||
match = cmnd_match;
|
||||
printf("\tcmnd %s\n", match == ALLOW ? "allowed" :
|
||||
@@ -499,6 +500,12 @@ restore_perms(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
set_cmnd_path(const char *runchroot)
|
||||
{
|
||||
return FOUND;
|
||||
}
|
||||
|
||||
static bool
|
||||
print_defaults(struct sudo_lbuf *lbuf)
|
||||
{
|
||||
|
Reference in New Issue
Block a user