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

@@ -17,7 +17,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.nr SL @SEMAN@ .nr SL @SEMAN@
.TH "SUDO.CONF" "@mansectform@" "September 1, 2021" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .TH "SUDO.CONF" "@mansectform@" "September 7, 2021" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.nh .nh
.if n .ad l .if n .ad l
.SH "NAME" .SH "NAME"
@@ -290,9 +290,14 @@ macOS and Solaris.
intercept intercept
.br .br
The fully-qualified path to a shared library containing a wrappers for the The fully-qualified path to a shared library containing a wrappers for the
\fBexecv\fR() \fBexecl\fR(),
\fBexecle\fR(),
\fBexeclp\fR(),
\fBexecv\fR(),
\fBexecve\fR(),
\fBexecvp\fR(),
and and
\fBexecve\fR() \fBexecvpe\fR()
library functions that intercepts attempts to run further commands and library functions that intercepts attempts to run further commands and
performs a policy check before allowing them to be executed. performs a policy check before allowing them to be executed.
This is used to implement the This is used to implement the

View File

@@ -16,7 +16,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.nr SL @SEMAN@ .nr SL @SEMAN@
.Dd September 1, 2021 .Dd September 7, 2021
.Dt SUDO.CONF @mansectform@ .Dt SUDO.CONF @mansectform@
.Os Sudo @PACKAGE_VERSION@ .Os Sudo @PACKAGE_VERSION@
.Sh NAME .Sh NAME
@@ -265,9 +265,14 @@ functions, for example
macOS and Solaris. macOS and Solaris.
.It intercept .It intercept
The fully-qualified path to a shared library containing a wrappers for the The fully-qualified path to a shared library containing a wrappers for the
.Fn execv .Fn execl ,
.Fn execle ,
.Fn execlp ,
.Fn execv ,
.Fn execve ,
.Fn execvp ,
and and
.Fn execve .Fn execvpe
library functions that intercepts attempts to run further commands and library functions that intercepts attempts to run further commands and
performs a policy check before allowing them to be executed. performs a policy check before allowing them to be executed.
This is used to implement the This is used to implement the

View File

@@ -25,7 +25,7 @@
.nr BA @BAMAN@ .nr BA @BAMAN@
.nr LC @LCMAN@ .nr LC @LCMAN@
.nr PS @PSMAN@ .nr PS @PSMAN@
.TH "SUDOERS" "@mansectform@" "September 3, 2021" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .TH "SUDOERS" "@mansectform@" "September 7, 2021" "Sudo @PACKAGE_VERSION@" "File Formats Manual"
.nh .nh
.if n .ad l .if n .ad l
.SH "NAME" .SH "NAME"
@@ -2860,9 +2860,14 @@ If set,
\fBsudoers\fR \fBsudoers\fR
will log when a command spawns a child process and executes a program will log when a command spawns a child process and executes a program
using the using the
\fBexecv\fR() \fBexecl\fR(),
\fBexecle\fR(),
\fBexeclp\fR(),
\fBexecv\fR(),
\fBexecve\fR(),
\fBexecvp\fR(),
or or
\fBexecve\fR() \fBexecvpe\fR()
library functions. library functions.
For example, if a shell is run by For example, if a shell is run by
\fBsudo\fR, \fBsudo\fR,
@@ -6331,9 +6336,14 @@ Currently,
\fBsudo\fR's \fBsudo\fR's
\fIintercept\fR \fIintercept\fR
functionality only works for programs that use the functionality only works for programs that use the
\fBexecv\fR() \fBexecl\fR(),
and \fBexecle\fR(),
\fBexecve\fR() \fBexeclp\fR(),
\fBexecv\fR(),
\fBexecve\fR(),
\fBexecvp\fR(),
or
\fBexecvpe\fR()
library functions to run the new command. library functions to run the new command.
This may be expanded in a future release of This may be expanded in a future release of
\fBsudo\fR. \fBsudo\fR.

View File

@@ -24,7 +24,7 @@
.nr BA @BAMAN@ .nr BA @BAMAN@
.nr LC @LCMAN@ .nr LC @LCMAN@
.nr PS @PSMAN@ .nr PS @PSMAN@
.Dd September 3, 2021 .Dd September 7, 2021
.Dt SUDOERS @mansectform@ .Dt SUDOERS @mansectform@
.Os Sudo @PACKAGE_VERSION@ .Os Sudo @PACKAGE_VERSION@
.Sh NAME .Sh NAME
@@ -2692,9 +2692,14 @@ If set,
.Nm .Nm
will log when a command spawns a child process and executes a program will log when a command spawns a child process and executes a program
using the using the
.Fn execv .Fn execl ,
.Fn execle ,
.Fn execlp ,
.Fn execv ,
.Fn execve ,
.Fn execvp ,
or or
.Fn execve .Fn execvpe
library functions. library functions.
For example, if a shell is run by For example, if a shell is run by
.Nm sudo , .Nm sudo ,
@@ -5852,9 +5857,14 @@ Currently,
.Nm sudo Ns 's .Nm sudo Ns 's
.Em intercept .Em intercept
functionality only works for programs that use the functionality only works for programs that use the
.Fn execv .Fn execl ,
and .Fn execle ,
.Fn execve .Fn execlp ,
.Fn execv ,
.Fn execve ,
.Fn execvp ,
or
.Fn execvpe
library functions to run the new command. library functions to run the new command.
This may be expanded in a future release of This may be expanded in a future release of
.Nm sudo . .Nm sudo .

View File

@@ -1,2 +1,7 @@
execl
execle
execlp
execv execv
execve execve
execvp
execvpe

View File

@@ -48,9 +48,104 @@
#include "sudo_util.h" #include "sudo_util.h"
#include "pathnames.h" #include "pathnames.h"
/* execl flavors */
#define SUDO_EXECL 0x0
#define SUDO_EXECLE 0x1
#define SUDO_EXECLP 0x2
extern char **environ; extern char **environ;
extern bool command_allowed(const char *cmnd, char * const argv[], char * const envp[], char **ncmnd, char ***nargv, char ***nenvp); 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 #ifdef HAVE___INTERPOSE
/* /*
* Mac OS X 10.4 and above has support for library symbol interposition. * Mac OS X 10.4 and above has support for library symbol interposition.
@@ -64,41 +159,77 @@ typedef struct interpose_s {
static int static int
my_execve(const char *cmnd, char * const argv[], char * const envp[]) my_execve(const char *cmnd, char * const argv[], char * const envp[])
{ {
char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL; return exec_wrapper(cmnd, argv, environ, false);
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;
} }
static int static int
my_execv(const char *cmnd, char * const argv[]) 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. */ /* Magic to tell dyld to do symbol interposition. */
__attribute__((__used__)) static const interpose_t interposers[] __attribute__((__used__)) static const interpose_t interposers[]
__attribute__((__section__("__DATA,__interpose"))) = { __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_execve, (void *)execve },
{ (void *)my_execv, (void *)execv } { (void *)my_execvp, (void *)execvp },
{ (void *)my_execvpe, (void *)execvpe }
}; };
#else /* HAVE___INTERPOSE */ #else /* HAVE___INTERPOSE */
typedef int (*sudo_fn_execve_t)(const char *, char *const *, char *const *);
# if defined(HAVE_SHL_LOAD) # if defined(HAVE_SHL_LOAD)
static void * static void *
sudo_shl_get_next(const char *symbol, short type) 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 sudo_dso_public int
execve(const char *cmnd, char * const argv[], char * const envp[]) execve(const char *cmnd, char * const argv[], char * const envp[])
{ {
char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL; return exec_wrapper(cmnd, argv, environ, false);
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;
} }
sudo_dso_public int sudo_dso_public int
@@ -162,4 +266,52 @@ execv(const char *cmnd, char * const argv[])
{ {
return execve(cmnd, argv, environ); 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) */ #endif /* HAVE___INTERPOSE) */