match_command: apply runchroot if set when matching the command
Previously we would prepend runchroot to the path we were checking but that does not properly handle symbolic links.
This commit is contained in:
@@ -116,25 +116,14 @@ command_args_match(const char *sudoers_cmnd, const char *sudoers_args)
|
|||||||
* Returns true on success, else false.
|
* Returns true on success, else false.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
do_stat(int fd, const char *path, const char *runchroot, struct stat *sb)
|
do_stat(int fd, const char *path, struct stat *sb)
|
||||||
{
|
{
|
||||||
char pathbuf[PATH_MAX];
|
|
||||||
bool ret;
|
bool ret;
|
||||||
debug_decl(do_stat, SUDOERS_DEBUG_MATCH);
|
debug_decl(do_stat, SUDOERS_DEBUG_MATCH);
|
||||||
|
|
||||||
if (fd != -1) {
|
if (fd != -1) {
|
||||||
ret = fstat(fd, sb) == 0;
|
ret = fstat(fd, sb) == 0;
|
||||||
} else {
|
} else {
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
ret = stat(path, sb) == 0;
|
ret = stat(path, sb) == 0;
|
||||||
}
|
}
|
||||||
debug_return_bool(ret);
|
debug_return_bool(ret);
|
||||||
@@ -182,28 +171,15 @@ is_script(int fd)
|
|||||||
* Returns false on error, else true.
|
* Returns false on error, else true.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
open_cmnd(const char *path, const char *runchroot,
|
open_cmnd(const char *path, const struct command_digest_list *digests, int *fdp)
|
||||||
const struct command_digest_list *digests, int *fdp)
|
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
char pathbuf[PATH_MAX];
|
|
||||||
debug_decl(open_cmnd, SUDOERS_DEBUG_MATCH);
|
debug_decl(open_cmnd, SUDOERS_DEBUG_MATCH);
|
||||||
|
|
||||||
/* Only open the file for fdexec or for digest matching. */
|
/* Only open the file for fdexec or for digest matching. */
|
||||||
if (def_fdexec != always && TAILQ_EMPTY(digests))
|
if (def_fdexec != always && TAILQ_EMPTY(digests))
|
||||||
debug_return_bool(true);
|
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);
|
fd = open(path, O_RDONLY|O_NONBLOCK);
|
||||||
# ifdef O_EXEC
|
# ifdef O_EXEC
|
||||||
if (fd == -1 && errno == EACCES && TAILQ_EMPTY(digests)) {
|
if (fd == -1 && errno == EACCES && TAILQ_EMPTY(digests)) {
|
||||||
@@ -222,7 +198,7 @@ open_cmnd(const char *path, const char *runchroot,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_cmnd_fd(int fd)
|
set_cmnd_fd(int fd, int rootfd)
|
||||||
{
|
{
|
||||||
debug_decl(set_cmnd_fd, SUDOERS_DEBUG_MATCH);
|
debug_decl(set_cmnd_fd, SUDOERS_DEBUG_MATCH);
|
||||||
|
|
||||||
@@ -237,11 +213,19 @@ set_cmnd_fd(int fd)
|
|||||||
} else if (is_script(fd)) {
|
} else if (is_script(fd)) {
|
||||||
char fdpath[PATH_MAX];
|
char fdpath[PATH_MAX];
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
int flags;
|
int error, flags;
|
||||||
|
|
||||||
/* We can only use fexecve() on a script if /dev/fd/N exists. */
|
/* We can only use fexecve() on a script if /dev/fd/N exists. */
|
||||||
(void)snprintf(fdpath, sizeof(fdpath), "/dev/fd/%d", fd);
|
if (rootfd != -1) {
|
||||||
if (stat(fdpath, &sb) != 0) {
|
/* Path relative to old root directory. */
|
||||||
|
(void)snprintf(fdpath, sizeof(fdpath), "dev/fd/%d", fd);
|
||||||
|
error = fstatat(rootfd, fdpath, &sb, 0);
|
||||||
|
} else {
|
||||||
|
/* Absolute path. */
|
||||||
|
(void)snprintf(fdpath, sizeof(fdpath), "/dev/fd/%d", fd);
|
||||||
|
error = stat(fdpath, &sb);
|
||||||
|
}
|
||||||
|
if (error != 0) {
|
||||||
/* Missing /dev/fd file, can't use fexecve(). */
|
/* Missing /dev/fd file, can't use fexecve(). */
|
||||||
close(fd);
|
close(fd);
|
||||||
fd = -1;
|
fd = -1;
|
||||||
@@ -266,29 +250,16 @@ set_cmnd_fd(int fd)
|
|||||||
* Return true if user_cmnd names one of the inodes in dir, else false.
|
* Return true if user_cmnd names one of the inodes in dir, else false.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
command_matches_dir(const char *sudoers_dir, size_t dlen, const char *runchroot,
|
command_matches_dir(const char *sudoers_dir, size_t dlen, int rootfd,
|
||||||
bool intercepted, const struct command_digest_list *digests)
|
bool intercepted, const struct command_digest_list *digests)
|
||||||
{
|
{
|
||||||
char buf[PATH_MAX], sdbuf[PATH_MAX];
|
char buf[PATH_MAX];
|
||||||
struct stat sudoers_stat;
|
struct stat sudoers_stat;
|
||||||
struct dirent *dent;
|
struct dirent *dent;
|
||||||
size_t chrootlen = 0;
|
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
debug_decl(command_matches_dir, SUDOERS_DEBUG_MATCH);
|
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.
|
* Grot through directory entries, looking for user_base.
|
||||||
*/
|
*/
|
||||||
@@ -316,9 +287,9 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, const char *runchroot,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Open the file for fdexec or for digest matching. */
|
/* Open the file for fdexec or for digest matching. */
|
||||||
if (!open_cmnd(buf, NULL, digests, &fd))
|
if (!open_cmnd(buf, digests, &fd))
|
||||||
continue;
|
continue;
|
||||||
if (!do_stat(fd, buf, NULL, &sudoers_stat))
|
if (!do_stat(fd, buf, &sudoers_stat))
|
||||||
continue;
|
continue;
|
||||||
if (!intercept_ok(buf, intercepted, &sudoers_stat))
|
if (!intercept_ok(buf, intercepted, &sudoers_stat))
|
||||||
continue;
|
continue;
|
||||||
@@ -326,11 +297,10 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, const char *runchroot,
|
|||||||
if (user_stat == NULL ||
|
if (user_stat == NULL ||
|
||||||
(user_stat->st_dev == sudoers_stat.st_dev &&
|
(user_stat->st_dev == sudoers_stat.st_dev &&
|
||||||
user_stat->st_ino == sudoers_stat.st_ino)) {
|
user_stat->st_ino == sudoers_stat.st_ino)) {
|
||||||
/* buf is already relative to runchroot */
|
if (!digest_matches(fd, buf, digests))
|
||||||
if (!digest_matches(fd, buf, NULL, digests))
|
|
||||||
continue;
|
continue;
|
||||||
free(safe_cmnd);
|
free(safe_cmnd);
|
||||||
if ((safe_cmnd = strdup(buf + chrootlen)) == NULL) {
|
if ((safe_cmnd = strdup(buf)) == NULL) {
|
||||||
sudo_warnx(U_("%s: %s"), __func__,
|
sudo_warnx(U_("%s: %s"), __func__,
|
||||||
U_("unable to allocate memory"));
|
U_("unable to allocate memory"));
|
||||||
dent = NULL;
|
dent = NULL;
|
||||||
@@ -341,7 +311,7 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, const char *runchroot,
|
|||||||
closedir(dirp);
|
closedir(dirp);
|
||||||
|
|
||||||
if (dent != NULL) {
|
if (dent != NULL) {
|
||||||
set_cmnd_fd(fd);
|
set_cmnd_fd(fd, rootfd);
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
}
|
}
|
||||||
if (fd != -1)
|
if (fd != -1)
|
||||||
@@ -353,7 +323,7 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, const char *runchroot,
|
|||||||
* Return true if user_cmnd names one of the inodes in dir, else false.
|
* Return true if user_cmnd names one of the inodes in dir, else false.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
command_matches_dir(const char *sudoers_dir, size_t dlen, const char *runchroot,
|
command_matches_dir(const char *sudoers_dir, size_t dlen, int rootfd,
|
||||||
bool intercepted, const struct command_digest_list *digests)
|
bool intercepted, const struct command_digest_list *digests)
|
||||||
{
|
{
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
@@ -368,11 +338,11 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, const char *runchroot,
|
|||||||
goto bad;
|
goto bad;
|
||||||
|
|
||||||
/* Open the file for fdexec or for digest matching. */
|
/* Open the file for fdexec or for digest matching. */
|
||||||
if (!open_cmnd(user_cmnd, runchroot, digests, &fd))
|
if (!open_cmnd(user_cmnd, digests, &fd))
|
||||||
goto bad;
|
goto bad;
|
||||||
if (!digest_matches(fd, user_cmnd, runchroot, digests))
|
if (!digest_matches(fd, user_cmnd, digests))
|
||||||
goto bad;
|
goto bad;
|
||||||
set_cmnd_fd(fd);
|
set_cmnd_fd(fd, rootfd);
|
||||||
|
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
bad:
|
bad:
|
||||||
@@ -383,8 +353,8 @@ bad:
|
|||||||
#endif /* SUDOERS_NAME_MATCH */
|
#endif /* SUDOERS_NAME_MATCH */
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
command_matches_all(const char *runchroot,
|
command_matches_all(int rootfd, bool intercepted,
|
||||||
bool intercepted, const struct command_digest_list *digests)
|
const struct command_digest_list *digests)
|
||||||
{
|
{
|
||||||
#ifndef SUDOERS_NAME_MATCH
|
#ifndef SUDOERS_NAME_MATCH
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
@@ -395,10 +365,10 @@ command_matches_all(const char *runchroot,
|
|||||||
if (user_cmnd[0] == '/') {
|
if (user_cmnd[0] == '/') {
|
||||||
#ifndef SUDOERS_NAME_MATCH
|
#ifndef SUDOERS_NAME_MATCH
|
||||||
/* Open the file for fdexec or for digest matching. */
|
/* Open the file for fdexec or for digest matching. */
|
||||||
bool open_error = !open_cmnd(user_cmnd, runchroot, digests, &fd);
|
bool open_error = !open_cmnd(user_cmnd, digests, &fd);
|
||||||
|
|
||||||
/* A non-existent file is not an error for "sudo ALL". */
|
/* A non-existent file is not an error for "sudo ALL". */
|
||||||
if (do_stat(fd, user_cmnd, runchroot, &sb)) {
|
if (do_stat(fd, user_cmnd, &sb)) {
|
||||||
if (open_error) {
|
if (open_error) {
|
||||||
/* File exists but we couldn't open it above? */
|
/* File exists but we couldn't open it above? */
|
||||||
goto bad;
|
goto bad;
|
||||||
@@ -408,14 +378,14 @@ command_matches_all(const char *runchroot,
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* Open the file for fdexec or for digest matching. */
|
/* Open the file for fdexec or for digest matching. */
|
||||||
(void)open_cmnd(user_cmnd, runchroot, digests, &fd);
|
(void)open_cmnd(user_cmnd, digests, &fd);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check digest of user_cmnd since we have no sudoers_cmnd for ALL. */
|
/* Check digest of user_cmnd since we have no sudoers_cmnd for ALL. */
|
||||||
if (!digest_matches(fd, user_cmnd, runchroot, digests))
|
if (!digest_matches(fd, user_cmnd, digests))
|
||||||
goto bad;
|
goto bad;
|
||||||
set_cmnd_fd(fd);
|
set_cmnd_fd(fd, rootfd);
|
||||||
|
|
||||||
/* No need to set safe_cmnd for ALL. */
|
/* No need to set safe_cmnd for ALL. */
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
@@ -427,8 +397,7 @@ bad:
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args,
|
command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args,
|
||||||
const char *runchroot, bool intercepted,
|
int rootfd, bool intercepted, const struct command_digest_list *digests)
|
||||||
const struct command_digest_list *digests)
|
|
||||||
{
|
{
|
||||||
#ifndef SUDOERS_NAME_MATCH
|
#ifndef SUDOERS_NAME_MATCH
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
@@ -442,25 +411,23 @@ command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
* b) there are no args on command line and none required by 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
|
* 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)
|
if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
|
||||||
debug_return_bool(false);
|
debug_return_bool(false);
|
||||||
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
||||||
/* Open the file for fdexec or for digest matching. */
|
/* Open the file for fdexec or for digest matching. */
|
||||||
if (!open_cmnd(user_cmnd, runchroot, digests, &fd))
|
if (!open_cmnd(user_cmnd, digests, &fd))
|
||||||
goto bad;
|
goto bad;
|
||||||
#ifndef SUDOERS_NAME_MATCH
|
#ifndef SUDOERS_NAME_MATCH
|
||||||
if (!do_stat(fd, user_cmnd, runchroot, &sb))
|
if (!do_stat(fd, user_cmnd, &sb))
|
||||||
goto bad;
|
goto bad;
|
||||||
if (!intercept_ok(user_cmnd, intercepted, &sb))
|
if (!intercept_ok(user_cmnd, intercepted, &sb))
|
||||||
goto bad;
|
goto bad;
|
||||||
#endif
|
#endif
|
||||||
/* Check digest of user_cmnd since sudoers_cmnd is a pattern. */
|
/* Check digest of user_cmnd since sudoers_cmnd is a pattern. */
|
||||||
if (!digest_matches(fd, user_cmnd, runchroot, digests))
|
if (!digest_matches(fd, user_cmnd, digests))
|
||||||
goto bad;
|
goto bad;
|
||||||
set_cmnd_fd(fd);
|
set_cmnd_fd(fd, rootfd);
|
||||||
|
|
||||||
/* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */
|
/* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
@@ -474,8 +441,7 @@ bad:
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
command_matches_regex(const char *sudoers_cmnd, const char *sudoers_args,
|
command_matches_regex(const char *sudoers_cmnd, const char *sudoers_args,
|
||||||
const char *runchroot, bool intercepted,
|
int rootfd, bool intercepted, const struct command_digest_list *digests)
|
||||||
const struct command_digest_list *digests)
|
|
||||||
{
|
{
|
||||||
#ifndef SUDOERS_NAME_MATCH
|
#ifndef SUDOERS_NAME_MATCH
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
@@ -489,26 +455,24 @@ command_matches_regex(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
* b) there are no args on command line and none required by 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
|
* 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 (!regex_matches(sudoers_cmnd, user_cmnd))
|
if (!regex_matches(sudoers_cmnd, user_cmnd))
|
||||||
debug_return_bool(false);
|
debug_return_bool(false);
|
||||||
|
|
||||||
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
||||||
/* Open the file for fdexec or for digest matching. */
|
/* Open the file for fdexec or for digest matching. */
|
||||||
if (!open_cmnd(user_cmnd, runchroot, digests, &fd))
|
if (!open_cmnd(user_cmnd, digests, &fd))
|
||||||
goto bad;
|
goto bad;
|
||||||
#ifndef SUDOERS_NAME_MATCH
|
#ifndef SUDOERS_NAME_MATCH
|
||||||
if (!do_stat(fd, user_cmnd, runchroot, &sb))
|
if (!do_stat(fd, user_cmnd, &sb))
|
||||||
goto bad;
|
goto bad;
|
||||||
if (!intercept_ok(user_cmnd, intercepted, &sb))
|
if (!intercept_ok(user_cmnd, intercepted, &sb))
|
||||||
goto bad;
|
goto bad;
|
||||||
#endif
|
#endif
|
||||||
/* Check digest of user_cmnd since sudoers_cmnd is a pattern. */
|
/* Check digest of user_cmnd since sudoers_cmnd is a pattern. */
|
||||||
if (!digest_matches(fd, user_cmnd, runchroot, digests))
|
if (!digest_matches(fd, user_cmnd, digests))
|
||||||
goto bad;
|
goto bad;
|
||||||
set_cmnd_fd(fd);
|
set_cmnd_fd(fd, rootfd);
|
||||||
|
|
||||||
/* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */
|
/* No need to set safe_cmnd since user_cmnd matches sudoers_cmnd */
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
@@ -523,15 +487,13 @@ bad:
|
|||||||
#ifndef SUDOERS_NAME_MATCH
|
#ifndef SUDOERS_NAME_MATCH
|
||||||
static bool
|
static bool
|
||||||
command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
||||||
const char *runchroot, bool intercepted,
|
int rootfd, bool intercepted, const struct command_digest_list *digests)
|
||||||
const struct command_digest_list *digests)
|
|
||||||
{
|
{
|
||||||
struct stat sudoers_stat;
|
struct stat sudoers_stat;
|
||||||
bool bad_digest = false;
|
bool bad_digest = false;
|
||||||
char **ap, *base, *cp;
|
char **ap, *base, *cp;
|
||||||
char pathbuf[PATH_MAX];
|
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
size_t dlen, chrootlen = 0;
|
size_t dlen;
|
||||||
glob_t gl;
|
glob_t gl;
|
||||||
debug_decl(command_matches_glob, SUDOERS_DEBUG_MATCH);
|
debug_decl(command_matches_glob, SUDOERS_DEBUG_MATCH);
|
||||||
|
|
||||||
@@ -547,18 +509,6 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
debug_return_bool(false);
|
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
|
* Return true if we find a match in the glob(3) results AND
|
||||||
* a) there are no args in sudoers OR
|
* a) there are no args in sudoers OR
|
||||||
@@ -577,15 +527,13 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
close(fd);
|
close(fd);
|
||||||
fd = -1;
|
fd = -1;
|
||||||
}
|
}
|
||||||
/* Remove the runchroot, if any. */
|
|
||||||
cp += chrootlen;
|
|
||||||
|
|
||||||
if (strcmp(cp, user_cmnd) != 0)
|
if (strcmp(cp, user_cmnd) != 0)
|
||||||
continue;
|
continue;
|
||||||
/* Open the file for fdexec or for digest matching. */
|
/* Open the file for fdexec or for digest matching. */
|
||||||
if (!open_cmnd(cp, runchroot, digests, &fd))
|
if (!open_cmnd(cp, digests, &fd))
|
||||||
continue;
|
continue;
|
||||||
if (!do_stat(fd, cp, runchroot, &sudoers_stat))
|
if (!do_stat(fd, cp, &sudoers_stat))
|
||||||
continue;
|
continue;
|
||||||
if (!intercept_ok(cp, intercepted, &sudoers_stat))
|
if (!intercept_ok(cp, intercepted, &sudoers_stat))
|
||||||
continue;
|
continue;
|
||||||
@@ -593,7 +541,7 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
(user_stat->st_dev == sudoers_stat.st_dev &&
|
(user_stat->st_dev == sudoers_stat.st_dev &&
|
||||||
user_stat->st_ino == sudoers_stat.st_ino)) {
|
user_stat->st_ino == sudoers_stat.st_ino)) {
|
||||||
/* There could be multiple matches, check digest early. */
|
/* There could be multiple matches, check digest early. */
|
||||||
if (!digest_matches(fd, cp, runchroot, digests)) {
|
if (!digest_matches(fd, cp, digests)) {
|
||||||
bad_digest = true;
|
bad_digest = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -618,13 +566,10 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
fd = -1;
|
fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove the runchroot, if any. */
|
|
||||||
cp += chrootlen;
|
|
||||||
|
|
||||||
/* If it ends in '/' it is a directory spec. */
|
/* If it ends in '/' it is a directory spec. */
|
||||||
dlen = strlen(cp);
|
dlen = strlen(cp);
|
||||||
if (cp[dlen - 1] == '/') {
|
if (cp[dlen - 1] == '/') {
|
||||||
if (command_matches_dir(cp, dlen, runchroot, intercepted, digests)) {
|
if (command_matches_dir(cp, dlen, rootfd, intercepted, digests)) {
|
||||||
globfree(&gl);
|
globfree(&gl);
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
}
|
}
|
||||||
@@ -637,16 +582,16 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Open the file for fdexec or for digest matching. */
|
/* Open the file for fdexec or for digest matching. */
|
||||||
if (!open_cmnd(cp, runchroot, digests, &fd))
|
if (!open_cmnd(cp, digests, &fd))
|
||||||
continue;
|
continue;
|
||||||
if (!do_stat(fd, cp, runchroot, &sudoers_stat))
|
if (!do_stat(fd, cp, &sudoers_stat))
|
||||||
continue;
|
continue;
|
||||||
if (!intercept_ok(cp, intercepted, &sudoers_stat))
|
if (!intercept_ok(cp, intercepted, &sudoers_stat))
|
||||||
continue;
|
continue;
|
||||||
if (user_stat == NULL ||
|
if (user_stat == NULL ||
|
||||||
(user_stat->st_dev == sudoers_stat.st_dev &&
|
(user_stat->st_dev == sudoers_stat.st_dev &&
|
||||||
user_stat->st_ino == sudoers_stat.st_ino)) {
|
user_stat->st_ino == sudoers_stat.st_ino)) {
|
||||||
if (!digest_matches(fd, cp, runchroot, digests))
|
if (!digest_matches(fd, cp, digests))
|
||||||
continue;
|
continue;
|
||||||
free(safe_cmnd);
|
free(safe_cmnd);
|
||||||
if ((safe_cmnd = strdup(cp)) == NULL) {
|
if ((safe_cmnd = strdup(cp)) == NULL) {
|
||||||
@@ -663,7 +608,7 @@ done:
|
|||||||
if (cp != NULL) {
|
if (cp != NULL) {
|
||||||
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
||||||
/* safe_cmnd was set above. */
|
/* safe_cmnd was set above. */
|
||||||
set_cmnd_fd(fd);
|
set_cmnd_fd(fd, rootfd);
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -674,8 +619,7 @@ done:
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
|
command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
|
||||||
const char *runchroot, bool intercepted,
|
int rootfd, bool intercepted, const struct command_digest_list *digests)
|
||||||
const struct command_digest_list *digests)
|
|
||||||
{
|
{
|
||||||
struct stat sudoers_stat;
|
struct stat sudoers_stat;
|
||||||
const char *base;
|
const char *base;
|
||||||
@@ -686,7 +630,7 @@ 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,
|
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, rootfd,
|
||||||
intercepted, digests));
|
intercepted, digests));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -696,7 +640,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
debug_return_bool(false);
|
debug_return_bool(false);
|
||||||
|
|
||||||
/* Open the file for fdexec or for digest matching. */
|
/* Open the file for fdexec or for digest matching. */
|
||||||
if (!open_cmnd(sudoers_cmnd, runchroot, digests, &fd))
|
if (!open_cmnd(sudoers_cmnd, digests, &fd))
|
||||||
goto bad;
|
goto bad;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -706,7 +650,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
* c) there are args in sudoers and on command line and they match
|
* c) there are args in sudoers and on command line and they match
|
||||||
* d) there is a digest and it matches
|
* d) there is a digest and it matches
|
||||||
*/
|
*/
|
||||||
if (user_stat != NULL && do_stat(fd, sudoers_cmnd, runchroot, &sudoers_stat)) {
|
if (user_stat != NULL && do_stat(fd, sudoers_cmnd, &sudoers_stat)) {
|
||||||
if (!intercept_ok(sudoers_cmnd, intercepted, &sudoers_stat))
|
if (!intercept_ok(sudoers_cmnd, intercepted, &sudoers_stat))
|
||||||
goto bad;
|
goto bad;
|
||||||
if (user_stat->st_dev != sudoers_stat.st_dev ||
|
if (user_stat->st_dev != sudoers_stat.st_dev ||
|
||||||
@@ -719,7 +663,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
}
|
}
|
||||||
if (!command_args_match(sudoers_cmnd, sudoers_args))
|
if (!command_args_match(sudoers_cmnd, sudoers_args))
|
||||||
goto bad;
|
goto bad;
|
||||||
if (!digest_matches(fd, sudoers_cmnd, runchroot, digests)) {
|
if (!digest_matches(fd, sudoers_cmnd, digests)) {
|
||||||
/* XXX - log functions not available but we should log very loudly */
|
/* XXX - log functions not available but we should log very loudly */
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
@@ -728,7 +672,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
set_cmnd_fd(fd);
|
set_cmnd_fd(fd, rootfd);
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
bad:
|
bad:
|
||||||
if (fd != -1)
|
if (fd != -1)
|
||||||
@@ -738,17 +682,15 @@ bad:
|
|||||||
#else /* SUDOERS_NAME_MATCH */
|
#else /* SUDOERS_NAME_MATCH */
|
||||||
static bool
|
static bool
|
||||||
command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args,
|
||||||
const char *runchroot, bool intercepted,
|
int rootfd, bool intercepted, const struct command_digest_list *digests)
|
||||||
const struct command_digest_list *digests)
|
|
||||||
{
|
{
|
||||||
return command_matches_fnmatch(sudoers_cmnd, sudoers_args, runchroot,
|
return command_matches_fnmatch(sudoers_cmnd, sudoers_args,
|
||||||
intercepted, digests);
|
intercepted, digests);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
|
command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
|
||||||
const char *runchroot, bool intercepted,
|
int rootfd, bool intercepted, const struct command_digest_list *digests)
|
||||||
const struct command_digest_list *digests)
|
|
||||||
{
|
{
|
||||||
size_t dlen;
|
size_t dlen;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
@@ -757,16 +699,16 @@ 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,
|
debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, rootfd,
|
||||||
intercepted, digests));
|
intercepted, digests));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(user_cmnd, sudoers_cmnd) == 0) {
|
if (strcmp(user_cmnd, sudoers_cmnd) == 0) {
|
||||||
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
if (command_args_match(sudoers_cmnd, sudoers_args)) {
|
||||||
/* Open the file for fdexec or for digest matching. */
|
/* Open the file for fdexec or for digest matching. */
|
||||||
if (!open_cmnd(user_cmnd, runchroot, digests, &fd))
|
if (!open_cmnd(user_cmnd, digests, &fd))
|
||||||
goto bad;
|
goto bad;
|
||||||
if (!digest_matches(fd, user_cmnd, runchroot, digests))
|
if (!digest_matches(fd, user_cmnd, digests))
|
||||||
goto bad;
|
goto bad;
|
||||||
|
|
||||||
/* Successful match. */
|
/* Successful match. */
|
||||||
@@ -776,7 +718,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
U_("unable to allocate memory"));
|
U_("unable to allocate memory"));
|
||||||
goto bad;
|
goto bad;
|
||||||
}
|
}
|
||||||
set_cmnd_fd(fd);
|
set_cmnd_fd(fd, rootfd);
|
||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -797,8 +739,10 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
const struct command_digest_list *digests)
|
const struct command_digest_list *digests)
|
||||||
{
|
{
|
||||||
const bool intercepted = info ? info->intercepted : false;
|
const bool intercepted = info ? info->intercepted : false;
|
||||||
|
int pivot_fds[2] = { -1, -1 };
|
||||||
char *saved_user_cmnd = NULL;
|
char *saved_user_cmnd = NULL;
|
||||||
struct stat saved_user_stat;
|
struct stat saved_user_stat;
|
||||||
|
bool reset_cmnd = false;
|
||||||
bool rc = false;
|
bool rc = false;
|
||||||
debug_decl(command_matches, SUDOERS_DEBUG_MATCH);
|
debug_decl(command_matches, SUDOERS_DEBUG_MATCH);
|
||||||
|
|
||||||
@@ -815,13 +759,24 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
if (def_runchroot != NULL && strcmp(def_runchroot, "*") != '\0')
|
if (def_runchroot != NULL && strcmp(def_runchroot, "*") != '\0')
|
||||||
runchroot = def_runchroot;
|
runchroot = def_runchroot;
|
||||||
} else {
|
} else {
|
||||||
/* Rule-specific runchroot, reset user_cmnd and user_stat. */
|
/* Rule-specific runchroot, must reset user_cmnd and user_stat. */
|
||||||
|
reset_cmnd = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pivot root. */
|
||||||
|
if (runchroot != NULL) {
|
||||||
|
if (!pivot_root(runchroot, pivot_fds))
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reset_cmnd) {
|
||||||
|
/* Rule-specific runchroot, set user_cmnd and user_stat after pivot. */
|
||||||
int status;
|
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;
|
||||||
status = set_cmnd_path(runchroot);
|
status = set_cmnd_path(NULL);
|
||||||
if (status != FOUND)
|
if (status != FOUND)
|
||||||
saved_user_cmnd = NULL;
|
saved_user_cmnd = NULL;
|
||||||
if (info != NULL)
|
if (info != NULL)
|
||||||
@@ -830,13 +785,13 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
|
|
||||||
if (sudoers_cmnd == NULL) {
|
if (sudoers_cmnd == NULL) {
|
||||||
sudoers_cmnd = "ALL";
|
sudoers_cmnd = "ALL";
|
||||||
rc = command_matches_all(runchroot, intercepted, digests);
|
rc = command_matches_all(pivot_fds[0], intercepted, digests);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for regular expressions first. */
|
/* Check for regular expressions first. */
|
||||||
if (sudoers_cmnd[0] == '^') {
|
if (sudoers_cmnd[0] == '^') {
|
||||||
rc = command_matches_regex(sudoers_cmnd, sudoers_args, runchroot,
|
rc = command_matches_regex(sudoers_cmnd, sudoers_args, pivot_fds[0],
|
||||||
intercepted, digests);
|
intercepted, digests);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
@@ -866,17 +821,22 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args,
|
|||||||
* use glob(3) and/or fnmatch(3) to do the matching.
|
* use glob(3) and/or fnmatch(3) to do the matching.
|
||||||
*/
|
*/
|
||||||
if (def_fast_glob) {
|
if (def_fast_glob) {
|
||||||
rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, runchroot,
|
rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args,
|
||||||
intercepted, digests);
|
pivot_fds[0], intercepted, digests);
|
||||||
} else {
|
} else {
|
||||||
rc = command_matches_glob(sudoers_cmnd, sudoers_args, runchroot,
|
rc = command_matches_glob(sudoers_cmnd, sudoers_args, pivot_fds[0],
|
||||||
intercepted, digests);
|
intercepted, digests);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rc = command_matches_normal(sudoers_cmnd, sudoers_args, runchroot,
|
rc = command_matches_normal(sudoers_cmnd, sudoers_args, pivot_fds[0],
|
||||||
intercepted, digests);
|
intercepted, digests);
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
|
/* Restore root. */
|
||||||
|
if (runchroot != NULL)
|
||||||
|
(void)unpivot_root(pivot_fds);
|
||||||
|
|
||||||
|
/* Restore user_cmnd and user_stat. */
|
||||||
if (saved_user_cmnd != NULL) {
|
if (saved_user_cmnd != NULL) {
|
||||||
if (info != NULL) {
|
if (info != NULL) {
|
||||||
info->cmnd_path = user_cmnd;
|
info->cmnd_path = user_cmnd;
|
||||||
|
@@ -39,7 +39,7 @@
|
|||||||
#include <gram.h>
|
#include <gram.h>
|
||||||
|
|
||||||
bool
|
bool
|
||||||
digest_matches(int fd, const char *path, const char *runchroot,
|
digest_matches(int fd, const char *path,
|
||||||
const struct command_digest_list *digests)
|
const struct command_digest_list *digests)
|
||||||
{
|
{
|
||||||
unsigned int digest_type = SUDO_DIGEST_INVALID;
|
unsigned int digest_type = SUDO_DIGEST_INVALID;
|
||||||
@@ -47,7 +47,6 @@ digest_matches(int fd, const char *path, const char *runchroot,
|
|||||||
unsigned char *sudoers_digest = NULL;
|
unsigned char *sudoers_digest = NULL;
|
||||||
struct command_digest *digest;
|
struct command_digest *digest;
|
||||||
size_t digest_len = (size_t)-1;
|
size_t digest_len = (size_t)-1;
|
||||||
char pathbuf[PATH_MAX];
|
|
||||||
bool matched = false;
|
bool matched = false;
|
||||||
debug_decl(digest_matches, SUDOERS_DEBUG_MATCH);
|
debug_decl(digest_matches, SUDOERS_DEBUG_MATCH);
|
||||||
|
|
||||||
@@ -61,16 +60,6 @@ digest_matches(int fd, const char *path, const char *runchroot,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
TAILQ_FOREACH(digest, digests, entries) {
|
TAILQ_FOREACH(digest, digests, entries) {
|
||||||
/* Compute file digest if needed. */
|
/* Compute file digest if needed. */
|
||||||
if (digest->digest_type != digest_type) {
|
if (digest->digest_type != digest_type) {
|
||||||
|
@@ -394,7 +394,7 @@ bool addr_matches(char *n);
|
|||||||
bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const char *runchroot, struct cmnd_info *info, 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 *path, const char *runchroot, const struct command_digest_list *digests);
|
bool digest_matches(int fd, const char *path, const struct command_digest_list *digests);
|
||||||
|
|
||||||
/* match.c */
|
/* match.c */
|
||||||
struct group;
|
struct group;
|
||||||
|
Reference in New Issue
Block a user