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;
/* 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) {
errno = ENOENT;
goto bad;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -288,12 +288,11 @@ struct timespec;
#define YY_DECL int sudoerslex(void)
/* 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 */
int find_path(const char *infile, char **outfile, struct stat *sbp,
const char *path, const char *runchroot, int ignore_dot,
char * const *allowlist);
const char *path, int ignore_dot, char * const *allowlist);
/* check.c */
int check_user(int validate, int mode);