For preload DSO make copies of cmnd, argv, envp and map them read-only.
This commit is contained in:
@@ -257,6 +257,8 @@ sudo_dso_public void sudo_mmap_free_v1(void *ptr);
|
|||||||
#define sudo_mmap_free(_a) sudo_mmap_free_v1(_a)
|
#define sudo_mmap_free(_a) sudo_mmap_free_v1(_a)
|
||||||
sudo_dso_public char *sudo_mmap_strdup_v1(const char *str);
|
sudo_dso_public char *sudo_mmap_strdup_v1(const char *str);
|
||||||
#define sudo_mmap_strdup(_a) sudo_mmap_strdup_v1(_a)
|
#define sudo_mmap_strdup(_a) sudo_mmap_strdup_v1(_a)
|
||||||
|
sudo_dso_public int sudo_mmap_protect_v1(void *ptr);
|
||||||
|
#define sudo_mmap_protect(_a) sudo_mmap_protect_v1(_a)
|
||||||
|
|
||||||
/* parseln.c */
|
/* parseln.c */
|
||||||
sudo_dso_public ssize_t sudo_parseln_v1(char **buf, size_t *bufsize, unsigned int *lineno, FILE *fp);
|
sudo_dso_public ssize_t sudo_parseln_v1(char **buf, size_t *bufsize, unsigned int *lineno, FILE *fp);
|
||||||
|
@@ -121,6 +121,24 @@ sudo_mmap_strdup_v1(const char *str)
|
|||||||
return newstr;
|
return newstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the page permissions for the allocation represented by "ptr" to
|
||||||
|
* read-only. Returns 0 on success, -1 on failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sudo_mmap_protect_v1(void *ptr)
|
||||||
|
{
|
||||||
|
if (ptr != NULL) {
|
||||||
|
unsigned long *ulp = ptr;
|
||||||
|
const unsigned long size = ulp[-1];
|
||||||
|
return mprotect((void *)&ulp[-1], size, PROT_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can't protect NULL. */
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free "ptr" allocated by sudo_mmap_alloc().
|
* Free "ptr" allocated by sudo_mmap_alloc().
|
||||||
* The allocated size is stored (as unsigned long) in ptr[-1].
|
* The allocated size is stored (as unsigned long) in ptr[-1].
|
||||||
@@ -131,7 +149,9 @@ sudo_mmap_free_v1(void *ptr)
|
|||||||
if (ptr != NULL) {
|
if (ptr != NULL) {
|
||||||
unsigned long *ulp = ptr;
|
unsigned long *ulp = ptr;
|
||||||
const unsigned long size = ulp[-1];
|
const unsigned long size = ulp[-1];
|
||||||
|
int saved_errno = errno;
|
||||||
|
|
||||||
munmap((void *)&ulp[-1], size);
|
(void)munmap((void *)&ulp[-1], size);
|
||||||
|
errno = saved_errno;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -113,6 +113,7 @@ sudo_mkdir_parents_v1
|
|||||||
sudo_mmap_alloc_v1
|
sudo_mmap_alloc_v1
|
||||||
sudo_mmap_allocarray_v1
|
sudo_mmap_allocarray_v1
|
||||||
sudo_mmap_free_v1
|
sudo_mmap_free_v1
|
||||||
|
sudo_mmap_protect_v1
|
||||||
sudo_mmap_strdup_v1
|
sudo_mmap_strdup_v1
|
||||||
sudo_new_key_val_v1
|
sudo_new_key_val_v1
|
||||||
sudo_parse_gids_v1
|
sudo_parse_gids_v1
|
||||||
|
@@ -62,6 +62,51 @@ extern bool command_allowed(const char *cmnd, char * const argv[], char * const
|
|||||||
|
|
||||||
typedef int (*sudo_fn_execve_t)(const char *, char *const *, char *const *);
|
typedef int (*sudo_fn_execve_t)(const char *, char *const *, char *const *);
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_vector(char **vec)
|
||||||
|
{
|
||||||
|
char **cur;
|
||||||
|
debug_decl(free_vector, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
|
if (vec != NULL) {
|
||||||
|
for (cur = vec; *cur != NULL; cur++) {
|
||||||
|
sudo_mmap_free(*cur);
|
||||||
|
}
|
||||||
|
sudo_mmap_free(vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char **
|
||||||
|
copy_vector(char * const *src)
|
||||||
|
{
|
||||||
|
char **copy;
|
||||||
|
int i, len;
|
||||||
|
debug_decl(copy_vector, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
|
if (src == NULL)
|
||||||
|
debug_return_ptr(NULL);
|
||||||
|
|
||||||
|
for (len = 0; src[len] != NULL; len++) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
copy = sudo_mmap_allocarray(len + 1, sizeof(char *));
|
||||||
|
if (copy == NULL) {
|
||||||
|
debug_return_ptr(NULL);
|
||||||
|
}
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
copy[i] = sudo_mmap_strdup(src[i]);
|
||||||
|
if (copy[i] == NULL) {
|
||||||
|
sudo_mmap_free(copy);
|
||||||
|
debug_return_ptr(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copy[i] = NULL;
|
||||||
|
|
||||||
|
debug_return_ptr(copy);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We do PATH resolution here rather than in the policy because we
|
* We do PATH resolution here rather than in the policy because we
|
||||||
* want to use the PATH in the current environment.
|
* want to use the PATH in the current environment.
|
||||||
@@ -74,6 +119,7 @@ resolve_path(const char *cmnd, char *out_cmnd, size_t out_size)
|
|||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
char **p, *cp, *endp;
|
char **p, *cp, *endp;
|
||||||
int dirlen, len;
|
int dirlen, len;
|
||||||
|
debug_decl(resolve_path, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
for (p = environ; (cp = *p) != NULL; p++) {
|
for (p = environ; (cp = *p) != NULL; p++) {
|
||||||
if (strncmp(cp, "PATH=", sizeof("PATH=") - 1) == 0) {
|
if (strncmp(cp, "PATH=", sizeof("PATH=") - 1) == 0) {
|
||||||
@@ -83,7 +129,7 @@ resolve_path(const char *cmnd, char *out_cmnd, size_t out_size)
|
|||||||
}
|
}
|
||||||
if (cp == NULL) {
|
if (cp == NULL) {
|
||||||
errno = ENOENT;
|
errno = ENOENT;
|
||||||
return false;
|
debug_return_bool(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
endp = cp + strlen(cp);
|
endp = cp + strlen(cp);
|
||||||
@@ -110,7 +156,7 @@ resolve_path(const char *cmnd, char *out_cmnd, size_t out_size)
|
|||||||
errval = ENAMETOOLONG;
|
errval = ENAMETOOLONG;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
debug_return_bool(true);
|
||||||
}
|
}
|
||||||
switch (errno) {
|
switch (errno) {
|
||||||
case EACCES:
|
case EACCES:
|
||||||
@@ -121,21 +167,21 @@ resolve_path(const char *cmnd, char *out_cmnd, size_t out_size)
|
|||||||
case ENOENT:
|
case ENOENT:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
debug_return_bool(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errno = errval;
|
errno = errval;
|
||||||
return false;
|
debug_return_bool(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
|
exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
|
||||||
bool is_execvp)
|
bool is_execvp)
|
||||||
{
|
{
|
||||||
|
char *cmnd_copy = NULL, **argv_copy = NULL, **envp_copy = NULL;
|
||||||
char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL;
|
char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL;
|
||||||
char cmnd_buf[PATH_MAX];
|
char cmnd_buf[PATH_MAX];
|
||||||
void *fn = NULL;
|
void *fn = NULL;
|
||||||
unsigned int i;
|
|
||||||
debug_decl(exec_wrapper, SUDO_DEBUG_EXEC);
|
debug_decl(exec_wrapper, SUDO_DEBUG_EXEC);
|
||||||
|
|
||||||
if (cmnd == NULL) {
|
if (cmnd == NULL) {
|
||||||
@@ -147,10 +193,10 @@ exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
|
|||||||
if (strchr(cmnd, '/') == NULL) {
|
if (strchr(cmnd, '/') == NULL) {
|
||||||
if (!is_execvp) {
|
if (!is_execvp) {
|
||||||
errno = ENOENT;
|
errno = ENOENT;
|
||||||
debug_return_int(-1);
|
goto bad;
|
||||||
}
|
}
|
||||||
if (!resolve_path(cmnd, cmnd_buf, sizeof(cmnd_buf))) {
|
if (!resolve_path(cmnd, cmnd_buf, sizeof(cmnd_buf))) {
|
||||||
debug_return_int(-1);
|
goto bad;
|
||||||
}
|
}
|
||||||
cmnd = cmnd_buf;
|
cmnd = cmnd_buf;
|
||||||
} else {
|
} else {
|
||||||
@@ -159,13 +205,37 @@ exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
|
|||||||
/* Absolute or relative path name. */
|
/* Absolute or relative path name. */
|
||||||
if (stat(cmnd, &sb) == -1) {
|
if (stat(cmnd, &sb) == -1) {
|
||||||
/* Leave errno unchanged. */
|
/* Leave errno unchanged. */
|
||||||
debug_return_int(-1);
|
goto bad;
|
||||||
} else if (!S_ISREG(sb.st_mode)) {
|
} else if (!S_ISREG(sb.st_mode)) {
|
||||||
errno = EACCES;
|
errno = EACCES;
|
||||||
debug_return_int(-1);
|
goto bad;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make copies of cmnd, argv, and envp.
|
||||||
|
*/
|
||||||
|
cmnd_copy = sudo_mmap_strdup(cmnd);
|
||||||
|
if (cmnd_copy == NULL) {
|
||||||
|
debug_return_int(-1);
|
||||||
|
}
|
||||||
|
sudo_mmap_protect(cmnd_copy);
|
||||||
|
cmnd = cmnd_copy;
|
||||||
|
|
||||||
|
argv_copy = copy_vector(argv);
|
||||||
|
if (argv_copy == NULL) {
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
sudo_mmap_protect(argv_copy);
|
||||||
|
argv = argv_copy;
|
||||||
|
|
||||||
|
envp_copy = copy_vector(envp);
|
||||||
|
if (envp_copy == NULL) {
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
sudo_mmap_protect(envp_copy);
|
||||||
|
envp = envp_copy;
|
||||||
|
|
||||||
# if defined(HAVE___INTERPOSE)
|
# if defined(HAVE___INTERPOSE)
|
||||||
fn = execve;
|
fn = execve;
|
||||||
# elif defined(HAVE_DLOPEN)
|
# elif defined(HAVE_DLOPEN)
|
||||||
@@ -175,7 +245,7 @@ exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
|
|||||||
# endif
|
# endif
|
||||||
if (fn == NULL) {
|
if (fn == NULL) {
|
||||||
errno = EACCES;
|
errno = EACCES;
|
||||||
debug_return_int(-1);
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command_allowed(cmnd, argv, envp, &ncmnd, &nargv, &nenvp)) {
|
if (command_allowed(cmnd, argv, envp, &ncmnd, &nargv, &nenvp)) {
|
||||||
@@ -191,7 +261,7 @@ exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
|
|||||||
continue;
|
continue;
|
||||||
shargv = sudo_mmap_allocarray(argc + 2, sizeof(char *));
|
shargv = sudo_mmap_allocarray(argc + 2, sizeof(char *));
|
||||||
if (shargv == NULL)
|
if (shargv == NULL)
|
||||||
return -1;
|
goto bad;
|
||||||
shargv[0] = "sh";
|
shargv[0] = "sh";
|
||||||
shargv[1] = ncmnd;
|
shargv[1] = ncmnd;
|
||||||
memcpy(shargv + 2, nargv + 1, argc * sizeof(char *));
|
memcpy(shargv + 2, nargv + 1, argc * sizeof(char *));
|
||||||
@@ -201,15 +271,17 @@ exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
|
|||||||
} else {
|
} else {
|
||||||
errno = EACCES;
|
errno = EACCES;
|
||||||
}
|
}
|
||||||
if (ncmnd != cmnd)
|
|
||||||
|
bad:
|
||||||
|
sudo_mmap_free(cmnd_copy);
|
||||||
|
if (ncmnd != cmnd_copy)
|
||||||
sudo_mmap_free(ncmnd);
|
sudo_mmap_free(ncmnd);
|
||||||
if (nargv != argv && nargv != NULL) {
|
free_vector(argv_copy);
|
||||||
for (i = 0; nargv[i] != NULL; i++)
|
if (nargv != argv_copy)
|
||||||
sudo_mmap_free(nargv[i]);
|
free_vector(nargv);
|
||||||
sudo_mmap_free(nargv);
|
free_vector(envp_copy);
|
||||||
}
|
|
||||||
/* Leaks allocated preload vars. */
|
/* Leaks allocated preload vars. */
|
||||||
if (nenvp != envp)
|
if (nenvp != envp_copy)
|
||||||
sudo_mmap_free(nenvp);
|
sudo_mmap_free(nenvp);
|
||||||
|
|
||||||
debug_return_int(-1);
|
debug_return_int(-1);
|
||||||
|
Reference in New Issue
Block a user