set_cmnd_path: apply runchroot if set when finding the command path

Previously we would prepend runchroot to the path we were checking
but that does not properly handle symbolic links.
This commit is contained in:
Todd C. Miller
2023-02-21 13:24:33 -07:00
parent 13a311bc71
commit bff4e3ce16
7 changed files with 31 additions and 38 deletions

View File

@@ -147,7 +147,7 @@ resolve_editor(const char *ed, size_t edlen, int nfiles, char * const *files,
goto oom; goto oom;
/* If we can't find the editor in the user's PATH, give up. */ /* 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"), NULL, if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"),
0, allowlist) != FOUND) { 0, allowlist) != FOUND) {
errno = ENOENT; errno = ENOENT;
goto bad; goto bad;

View File

@@ -43,14 +43,14 @@
* On failure, returns false. * On failure, returns false.
*/ */
static bool static bool
cmnd_allowed(char *cmnd, size_t cmnd_size, const char *runchroot, cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp,
struct stat *cmnd_sbp, char * const *allowlist) char * const *allowlist)
{ {
const char *cmnd_base; const char *cmnd_base;
char * const *al; char * const *al;
debug_decl(cmnd_allowed, SUDOERS_DEBUG_UTIL); debug_decl(cmnd_allowed, SUDOERS_DEBUG_UTIL);
if (!sudo_goodpath(cmnd, runchroot, cmnd_sbp)) if (!sudo_goodpath(cmnd, cmnd_sbp))
debug_return_bool(false); debug_return_bool(false);
if (allowlist == NULL) if (allowlist == NULL)
@@ -67,7 +67,7 @@ cmnd_allowed(char *cmnd, size_t cmnd_size, const char *runchroot,
if (strcmp(cmnd_base, base) != 0) if (strcmp(cmnd_base, base) != 0)
continue; continue;
if (sudo_goodpath(path, runchroot, &sb) && if (sudo_goodpath(path, &sb) &&
sb.st_dev == cmnd_sbp->st_dev && sb.st_ino == cmnd_sbp->st_ino) { sb.st_dev == cmnd_sbp->st_dev && sb.st_ino == cmnd_sbp->st_ino) {
/* Overwrite cmnd with safe version from allowlist. */ /* Overwrite cmnd with safe version from allowlist. */
if (strlcpy(cmnd, path, cmnd_size) < cmnd_size) if (strlcpy(cmnd, path, cmnd_size) < cmnd_size)
@@ -87,8 +87,7 @@ cmnd_allowed(char *cmnd, size_t cmnd_size, const char *runchroot,
*/ */
int int
find_path(const char *infile, char **outfile, struct stat *sbp, find_path(const char *infile, char **outfile, struct stat *sbp,
const char *path, const char *runchroot, int ignore_dot, const char *path, int ignore_dot, char * const *allowlist)
char * const *allowlist)
{ {
char command[PATH_MAX]; char command[PATH_MAX];
const char *cp, *ep, *pathend; const char *cp, *ep, *pathend;
@@ -109,8 +108,7 @@ find_path(const char *infile, char **outfile, struct stat *sbp,
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
debug_return_int(NOT_FOUND_ERROR); debug_return_int(NOT_FOUND_ERROR);
} }
found = cmnd_allowed(command, sizeof(command), runchroot, sbp, found = cmnd_allowed(command, sizeof(command), sbp, allowlist);
allowlist);
goto done; goto done;
} }
@@ -139,8 +137,7 @@ find_path(const char *infile, char **outfile, struct stat *sbp,
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
debug_return_int(NOT_FOUND_ERROR); debug_return_int(NOT_FOUND_ERROR);
} }
found = cmnd_allowed(command, sizeof(command), runchroot, found = cmnd_allowed(command, sizeof(command), sbp, allowlist);
sbp, allowlist);
if (found) if (found)
break; break;
} }
@@ -154,8 +151,7 @@ find_path(const char *infile, char **outfile, struct stat *sbp,
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
debug_return_int(NOT_FOUND_ERROR); debug_return_int(NOT_FOUND_ERROR);
} }
found = cmnd_allowed(command, sizeof(command), runchroot, found = cmnd_allowed(command, sizeof(command), sbp, allowlist);
sbp, allowlist);
if (found && ignore_dot) if (found && ignore_dot)
debug_return_int(NOT_FOUND_DOT); debug_return_int(NOT_FOUND_DOT);
} }

View File

@@ -39,24 +39,13 @@
* Verify that path is a normal file and executable by root. * Verify that path is a normal file and executable by root.
*/ */
bool bool
sudo_goodpath(const char *path, const char *runchroot, struct stat *sbp) sudo_goodpath(const char *path, struct stat *sbp)
{ {
bool ret = false; bool ret = false;
struct stat sb;
debug_decl(sudo_goodpath, SUDOERS_DEBUG_UTIL); debug_decl(sudo_goodpath, SUDOERS_DEBUG_UTIL);
if (path != NULL) { 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; // -V507
}
if (sbp == NULL) if (sbp == NULL)
sbp = &sb; sbp = &sb;
@@ -68,6 +57,5 @@ sudo_goodpath(const char *path, const char *runchroot, struct stat *sbp)
errno = EACCES; errno = EACCES;
} }
} }
done:
debug_return_bool(ret); debug_return_bool(ret);
} }

View File

@@ -80,8 +80,7 @@ sudo_dso_public int main(int argc, char *argv[]);
/* STUB */ /* STUB */
int int
find_path(const char *infile, char **outfile, struct stat *sbp, find_path(const char *infile, char **outfile, struct stat *sbp,
const char *path, const char *runchroot, int ignore_dot, const char *path, int ignore_dot, char * const *allowlist)
char * const *allowlist)
{ {
if (infile[0] == '/') { if (infile[0] == '/') {
*outfile = strdup(infile); *outfile = strdup(infile);

View File

@@ -799,8 +799,7 @@ display_privs(struct sudo_nss_list *snl, struct passwd *pw, bool verbose)
/* STUB */ /* STUB */
int int
find_path(const char *infile, char **outfile, struct stat *sbp, find_path(const char *infile, char **outfile, struct stat *sbp,
const char *path, const char *runchroot, int ignore_dot, const char *path, int ignore_dot, char * const *allowlist)
char * const *allowlist)
{ {
switch (pass) { switch (pass) {
case PASS_CHECK_NOT_FOUND: case PASS_CHECK_NOT_FOUND:

View File

@@ -998,7 +998,7 @@ set_cmnd_path(const char *runchroot)
const char *cmnd_in; const char *cmnd_in;
char *cmnd_out = NULL; char *cmnd_out = NULL;
char *path = user_path; char *path = user_path;
int ret; int ret, pivot_fds[2];
debug_decl(set_cmnd_path, SUDOERS_DEBUG_PLUGIN); debug_decl(set_cmnd_path, SUDOERS_DEBUG_PLUGIN);
cmnd_in = ISSET(sudo_mode, MODE_CHECK) ? NewArgv[1] : NewArgv[0]; cmnd_in = ISSET(sudo_mode, MODE_CHECK) ? NewArgv[1] : NewArgv[0];
@@ -1009,10 +1009,16 @@ set_cmnd_path(const char *runchroot)
user_cmnd = NULL; user_cmnd = NULL;
if (def_secure_path && !user_is_exempt()) if (def_secure_path && !user_is_exempt())
path = def_secure_path; path = def_secure_path;
/* Pivot root. */
if (runchroot != NULL) {
if (!pivot_root(runchroot, pivot_fds))
goto error;
}
if (!set_perms(PERM_RUNAS)) if (!set_perms(PERM_RUNAS))
goto error; goto error;
ret = find_path(cmnd_in, &cmnd_out, user_stat, path, ret = find_path(cmnd_in, &cmnd_out, user_stat, path, def_ignore_dot, NULL);
runchroot, def_ignore_dot, NULL);
if (!restore_perms()) if (!restore_perms())
goto error; goto error;
if (ret == NOT_FOUND) { if (ret == NOT_FOUND) {
@@ -1020,7 +1026,7 @@ set_cmnd_path(const char *runchroot)
if (!set_perms(PERM_USER)) if (!set_perms(PERM_USER))
goto error; goto error;
ret = find_path(cmnd_in, &cmnd_out, user_stat, path, ret = find_path(cmnd_in, &cmnd_out, user_stat, path,
runchroot, def_ignore_dot, NULL); def_ignore_dot, NULL);
if (!restore_perms()) if (!restore_perms())
goto error; goto error;
} }
@@ -1030,8 +1036,14 @@ set_cmnd_path(const char *runchroot)
else else
user_cmnd = cmnd_out; user_cmnd = cmnd_out;
/* Restore root. */
if (runchroot != NULL)
(void)unpivot_root(pivot_fds);
debug_return_int(ret); debug_return_int(ret);
error: error:
if (runchroot != NULL)
(void)unpivot_root(pivot_fds);
free(cmnd_out); free(cmnd_out);
debug_return_int(NOT_FOUND_ERROR); debug_return_int(NOT_FOUND_ERROR);
} }

View File

@@ -288,12 +288,11 @@ struct timespec;
#define YY_DECL int sudoerslex(void) #define YY_DECL int sudoerslex(void)
/* goodpath.c */ /* goodpath.c */
bool sudo_goodpath(const char *path, const char *runchroot, struct stat *sbp); bool sudo_goodpath(const char *path, struct stat *sbp);
/* findpath.c */ /* findpath.c */
int find_path(const char *infile, char **outfile, struct stat *sbp, int find_path(const char *infile, char **outfile, struct stat *sbp,
const char *path, const char *runchroot, int ignore_dot, const char *path, int ignore_dot, char * const *allowlist);
char * const *allowlist);
/* check.c */ /* check.c */
int check_user(int validate, int mode); int check_user(int validate, int mode);