Add support for execl, execle, execlp, execvp, and execvpe.

Currently, PATH traversal is handled by sudoers which uses
the original PATH, not the one updated by the shell.
This commit is contained in:
Todd C. Miller
2021-09-07 19:55:45 -06:00
parent bf60451845
commit 7ae62866e4
6 changed files with 253 additions and 66 deletions

View File

@@ -48,9 +48,104 @@
#include "sudo_util.h"
#include "pathnames.h"
/* execl flavors */
#define SUDO_EXECL 0x0
#define SUDO_EXECLE 0x1
#define SUDO_EXECLP 0x2
extern char **environ;
extern bool command_allowed(const char *cmnd, char * const argv[], char * const envp[], char **ncmnd, char ***nargv, char ***nenvp);
typedef int (*sudo_fn_execve_t)(const char *, char *const *, char *const *);
static int
exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
bool is_execvp)
{
char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL;
void *fn = NULL;
debug_decl(exec_wrapper, SUDO_DEBUG_EXEC);
/* Only check PATH for the command for execlp/execvp/execvpe. */
if (!is_execvp && strchr(cmnd, '/') == NULL) {
errno = ENOENT;
debug_return_int(-1);
}
# if defined(HAVE___INTERPOSE)
fn = execve;
# elif defined(HAVE_DLOPEN)
fn = dlsym(RTLD_NEXT, "execve");
# elif defined(HAVE_SHL_LOAD)
fn = sudo_shl_get_next("execve", TYPE_PROCEDURE);
# endif
if (fn == NULL) {
errno = EACCES;
debug_return_int(-1);
}
if (command_allowed(cmnd, argv, envp, &ncmnd, &nargv, &nenvp)) {
/* Execute the command using the "real" execve() function. */
((sudo_fn_execve_t)fn)(ncmnd, nargv, nenvp);
/* Fall back to exec via shell for execvp and friends. */
if (errno == ENOEXEC && is_execvp) {
int argc;
char **shargv;
for (argc = 0; argv[argc] != NULL; argc++)
continue;
shargv = reallocarray(NULL, (argc + 2), sizeof(char *));
if (shargv == NULL)
return -1;
shargv[0] = "sh";
shargv[1] = ncmnd;
memcpy(shargv + 2, nargv + 1, argc * sizeof(char *));
((sudo_fn_execve_t)fn)(_PATH_SUDO_BSHELL, shargv, nenvp);
free(shargv);
}
} else {
errno = EACCES;
}
if (ncmnd != cmnd)
free(ncmnd);
if (nargv != argv)
free(nargv);
if (nenvp != envp)
free(nenvp);
debug_return_int(-1);
}
static int
execl_wrapper(int type, const char *name, const char *arg, va_list ap)
{
char **argv, **envp = environ;
int argc = 1;
va_list ap2;
debug_decl(execl_wrapper, SUDO_DEBUG_EXEC);
va_copy(ap2, ap);
while (va_arg(ap2, char *) != NULL)
argc++;
va_end(ap2);
argv = reallocarray(NULL, (argc + 1), sizeof(char *));
if (argv == NULL)
debug_return_int(-1);
argc = 0;
argv[argc++] = (char *)arg;
while ((argv[argc] = va_arg(ap, char *)) != NULL)
argc++;
if (type == SUDO_EXECLE)
envp = va_arg(ap, char **);
exec_wrapper(name, argv, envp, type == SUDO_EXECLP);
free(argv);
debug_return_int(-1);
}
#ifdef HAVE___INTERPOSE
/*
* Mac OS X 10.4 and above has support for library symbol interposition.
@@ -64,41 +159,77 @@ typedef struct interpose_s {
static int
my_execve(const char *cmnd, char * const argv[], char * const envp[])
{
char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL;
if (command_allowed(cmnd, argv, envp, &ncmnd, &nargv, &nenvp)) {
/* Execute the command using the "real" execve() function. */
execve(ncmnd, nargv, nenvp);
} else {
errno = EACCES;
}
if (ncmnd != cmnd)
free(ncmnd);
if (nargv != argv)
free(nargv);
if (nenvp != envp)
free(nenvp);
return -1;
return exec_wrapper(cmnd, argv, environ, false);
}
static int
my_execv(const char *cmnd, char * const argv[])
{
return my_execve(cmnd, argv, environ);
return execve(cmnd, argv, environ);
}
static int
my_execvpe(const char *cmnd, char * const argv[], char * const envp[])
{
return exec_wrapper(cmnd, argv, envp, true);
}
static int
my_execvp(const char *cmnd, char * const argv[])
{
return exec_wrapper(cmnd, argv, environ, true);
}
static int
my_execl(const char *name, const char *arg, ...)
{
va_list ap;
va_start(ap, arg);
execl_wrapper(SUDO_EXECL, name, arg, ap);
va_end(ap);
return -1;
}
static int
my_execle(const char *name, const char *arg, ...)
{
va_list ap;
va_start(ap, arg);
execl_wrapper(SUDO_EXECLE, name, arg, ap);
va_end(ap);
return -1;
}
static int
my_execlp(const char *name, const char *arg, ...)
{
va_list ap;
va_start(ap, arg);
execl_wrapper(SUDO_EXECLP, name, arg, ap);
va_end(ap);
return -1;
}
/* Magic to tell dyld to do symbol interposition. */
__attribute__((__used__)) static const interpose_t interposers[]
__attribute__((__section__("__DATA,__interpose"))) = {
{ (void *)my_execl, (void *)execl },
{ (void *)my_execle, (void *)execle },
{ (void *)my_execlp, (void *)execlp },
{ (void *)my_execv, (void *)execv },
{ (void *)my_execve, (void *)execve },
{ (void *)my_execv, (void *)execv }
{ (void *)my_execvp, (void *)execvp },
{ (void *)my_execvpe, (void *)execvpe }
};
#else /* HAVE___INTERPOSE */
typedef int (*sudo_fn_execve_t)(const char *, char *const *, char *const *);
# if defined(HAVE_SHL_LOAD)
static void *
sudo_shl_get_next(const char *symbol, short type)
@@ -127,34 +258,7 @@ sudo_shl_get_next(const char *symbol, short type)
sudo_dso_public int
execve(const char *cmnd, char * const argv[], char * const envp[])
{
char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL;
void *fn = NULL;
debug_decl(execve, SUDO_DEBUG_EXEC);
# if defined(HAVE_DLOPEN)
fn = dlsym(RTLD_NEXT, "execve");
# elif defined(HAVE_SHL_LOAD)
fn = sudo_shl_get_next("execve", TYPE_PROCEDURE);
# endif
if (fn == NULL) {
errno = EACCES;
return -1;
}
if (command_allowed(cmnd, argv, envp, &ncmnd, &nargv, &nenvp)) {
/* Execute the command using the "real" execve() function. */
return ((sudo_fn_execve_t)fn)(ncmnd, nargv, nenvp);
} else {
errno = EACCES;
}
if (ncmnd != cmnd)
free(ncmnd);
if (nargv != argv)
free(nargv);
if (nenvp != envp)
free(nenvp);
return -1;
return exec_wrapper(cmnd, argv, environ, false);
}
sudo_dso_public int
@@ -162,4 +266,52 @@ execv(const char *cmnd, char * const argv[])
{
return execve(cmnd, argv, environ);
}
sudo_dso_public int
execvpe(const char *cmnd, char * const argv[], char * const envp[])
{
return exec_wrapper(cmnd, argv, envp, true);
}
sudo_dso_public int
execvp(const char *cmnd, char * const argv[])
{
return exec_wrapper(cmnd, argv, environ, true);
}
sudo_dso_public int
execl(const char *name, const char *arg, ...)
{
va_list ap;
va_start(ap, arg);
execl_wrapper(SUDO_EXECL, name, arg, ap);
va_end(ap);
return -1;
}
sudo_dso_public int
execle(const char *name, const char *arg, ...)
{
va_list ap;
va_start(ap, arg);
execl_wrapper(SUDO_EXECLE, name, arg, ap);
va_end(ap);
return -1;
}
sudo_dso_public int
execlp(const char *name, const char *arg, ...)
{
va_list ap;
va_start(ap, arg);
execl_wrapper(SUDO_EXECLP, name, arg, ap);
va_end(ap);
return -1;
}
#endif /* HAVE___INTERPOSE) */