Use sudo_mmap_alloc functions in DSO-based intercept code.

This commit is contained in:
Todd C. Miller
2022-07-25 16:05:11 -06:00
parent e43c964c43
commit 226a6cd754
4 changed files with 202 additions and 49 deletions

View File

@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2009-2021 Todd C. Miller <Todd.Miller@sudo.ws>
* Copyright (c) 2009-2022 Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -24,6 +24,7 @@
#include <config.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -31,13 +32,145 @@
#include "sudo.h"
#include "sudo_exec.h"
#include "sudo_util.h"
#ifdef RTLD_PRELOAD_VAR
typedef void * (*sudo_alloc_fn_t)(size_t, size_t);
typedef void (*sudo_free_fn_t)(void *);
static void *
sudo_allocarray(size_t nmemb, size_t size)
{
return reallocarray(NULL, nmemb, size);
}
/*
* Allocate space for the string described by fmt and return it,
* or NULL on error.
* Currently only supports %%, %c, %d, and %s escapes.
*/
static char *
fmtstr(sudo_alloc_fn_t alloc_fn, sudo_free_fn_t free_fn, const char *ofmt, ...)
{
char *cp, *cur, *newstr = NULL;
size_t len, size = 1;
const char *fmt;
va_list ap;
debug_decl(fmtstr, SUDO_DEBUG_UTIL);
/* Determine size. */
va_start(ap, ofmt);
for (fmt = ofmt; *fmt != '\0'; ) {
if (fmt[0] == '%') {
switch (fmt[1]) {
case '%':
case 'c':
size++;
fmt += 2;
continue;
case 's':
cp = va_arg(ap, char *);
size += strlen(cp ? cp : "(NULL)");
fmt += 2;
continue;
case 'd': {
char numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
len = snprintf(numbuf, sizeof(numbuf), "%d", va_arg(ap, int));
if (len >= sizeof(numbuf)) {
goto oflow;
}
size += len;
fmt += 2;
continue;
}
default:
/* Treat as literal. */
break;
}
}
size++;
fmt++;
}
va_end(ap);
newstr = alloc_fn(1, size);
if (newstr == NULL)
debug_return_str(NULL);
/* Format/copy data. */
cur = newstr;
va_start(ap, ofmt);
for (fmt = ofmt; *fmt != '\0'; ) {
if (fmt[0] == '%') {
switch (fmt[1]) {
case '%':
if (size < 2) {
goto oflow;
}
*cur++ = '%';
size--;
fmt += 2;
continue;
case 'c':
if (size < 2) {
goto oflow;
}
*cur++ = va_arg(ap, int);
size--;
fmt += 2;
continue;
case 's':
cp = va_arg(ap, char *);
len = strlcpy(cur, cp ? cp : "(NULL)", size);
if (len >= size) {
goto oflow;
}
cur += len;
size -= len;
fmt += 2;
continue;
case 'd':
len = snprintf(cur, size, "%d", va_arg(ap, int));
if (len >= size) {
goto oflow;
}
cur += len;
size -= len;
fmt += 2;
continue;
default:
/* Treat as literal. */
break;
}
}
if (size < 2) {
goto oflow;
}
*cur++ = *fmt++;
size++;
}
va_end(ap);
if (size < 1) {
goto oflow;
}
*cur = '\0';
debug_return_str(newstr);
oflow:
/* We pre-allocate enough space, so this should never happen. */
free_fn(newstr);
sudo_warnx(U_("internal error, %s overflow"), __func__);
debug_return_str(NULL);
}
/*
* Add a DSO file to LD_PRELOAD or the system equivalent.
*/
char **
sudo_preload_dso(char *const envp[], const char *dso_file, int intercept_fd)
static char **
sudo_preload_dso_alloc(char *const envp[], const char *dso_file,
int intercept_fd, sudo_alloc_fn_t alloc_fn, sudo_free_fn_t free_fn)
{
char *preload = NULL;
char **nep, **nenvp = NULL;
@@ -56,17 +189,17 @@ sudo_preload_dso(char *const envp[], const char *dso_file, int intercept_fd)
char *dso_buf = NULL;
# endif
size_t env_size;
int len;
debug_decl(sudo_preload_dso, SUDO_DEBUG_UTIL);
# ifdef _PATH_ASAN_LIB
/*
* The address sanitizer DSO needs to be first in the list.
*/
len = asprintf(&dso_buf, "%s%c%s", _PATH_ASAN_LIB, RTLD_PRELOAD_DELIM,
dso_file);
if (len == -1)
dso_buf = fmtstr(alloc_fn, free_fn, "%s%c%s", _PATH_ASAN_LIB,
RTLD_PRELOAD_DELIM, dso_file);
if (dso_buf == NULL) {
goto oom;
}
dso_file = dso_buf;
# endif
@@ -90,11 +223,9 @@ sudo_preload_dso(char *const envp[], const char *dso_file, int intercept_fd)
env_size += 2; /* dso_file + terminating NULL */
/* Allocate new envp. */
nenvp = reallocarray(NULL, env_size, sizeof(*nenvp));
if (nenvp == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
debug_return_ptr(NULL);
}
nenvp = alloc_fn(env_size, sizeof(*nenvp));
if (nenvp == NULL)
goto oom;
/*
* Shallow copy envp, with special handling for RTLD_PRELOAD_VAR,
@@ -156,13 +287,14 @@ copy:
if (!dso_present) {
if (preload_ptr == NULL) {
# ifdef RTLD_PRELOAD_DEFAULT
len = asprintf(&preload, "%s=%s%c%s", RTLD_PRELOAD_VAR, dso_file,
RTLD_PRELOAD_DELIM, RTLD_PRELOAD_DEFAULT);
if (len == -1) {
preload = fmtstr(alloc_fn, free_fn, "%s=%s%c%s", RTLD_PRELOAD_VAR,
dso_file, RTLD_PRELOAD_DELIM, RTLD_PRELOAD_DEFAULT);
if (preload == NULL) {
goto oom;
}
# else
preload = sudo_new_key_val(RTLD_PRELOAD_VAR, dso_file);
preload = fmtstr(alloc_fn, free_fn, "%s=%s", RTLD_PRELOAD_VAR,
dso_file);
if (preload == NULL) {
goto oom;
}
@@ -170,9 +302,9 @@ copy:
*nep++ = preload;
} else {
const char *old_val = *preload_ptr + sizeof(RTLD_PRELOAD_VAR);
len = asprintf(&preload, "%s=%s%c%s", RTLD_PRELOAD_VAR,
preload = fmtstr(alloc_fn, free_fn, "%s=%s%c%s", RTLD_PRELOAD_VAR,
dso_file, RTLD_PRELOAD_DELIM, old_val);
if (len == -1) {
if (preload == NULL) {
goto oom;
}
*preload_ptr = preload;
@@ -184,10 +316,9 @@ copy:
}
# endif
if (!fd_present && intercept_fd != -1) {
char *fdstr;
len = asprintf(&fdstr, "SUDO_INTERCEPT_FD=%d", intercept_fd);
if (len == -1) {
char *fdstr = fmtstr(alloc_fn, free_fn, "SUDO_INTERCEPT_FD=%d",
intercept_fd);
if (fdstr == NULL) {
goto oom;
}
if (intercept_ptr != NULL) {
@@ -201,17 +332,33 @@ copy:
*nep = NULL;
# ifdef _PATH_ASAN_LIB
free(dso_buf);
free_fn(dso_buf);
# endif
debug_return_ptr(nenvp);
oom:
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
# ifdef _PATH_ASAN_LIB
free(dso_buf);
free_fn(dso_buf);
# endif
free(preload);
free(nenvp);
free_fn(preload);
free_fn(nenvp);
debug_return_ptr(NULL);
}
char **
sudo_preload_dso_mmap(char *const envp[], const char *dso_file,
int intercept_fd)
{
return sudo_preload_dso_alloc(envp, dso_file, intercept_fd,
sudo_mmap_allocarray_v1, sudo_mmap_free_v1);
}
char **
sudo_preload_dso(char *const envp[], const char *dso_file,
int intercept_fd)
{
return sudo_preload_dso_alloc(envp, dso_file, intercept_fd,
sudo_allocarray, free);
}
#endif /* RTLD_PRELOAD_VAR */

View File

@@ -145,6 +145,7 @@ bool utmp_logout(const char *line, int status);
/* exec_preload.c */
char **sudo_preload_dso(char *const envp[], const char *dso_file, int intercept_fd);
char **sudo_preload_dso_mmap(char *const envp[], const char *dso_file, int intercept_fd);
/* exec_ptrace.c */
bool exec_ptrace_stopped(pid_t pid, int status, void *intercept);

View File

@@ -135,6 +135,7 @@ exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
char *ncmnd = NULL, **nargv = NULL, **nenvp = NULL;
char cmnd_buf[PATH_MAX];
void *fn = NULL;
unsigned int i;
debug_decl(exec_wrapper, SUDO_DEBUG_EXEC);
if (cmnd == NULL) {
@@ -188,24 +189,28 @@ exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
for (argc = 0; argv[argc] != NULL; argc++)
continue;
shargv = reallocarray(NULL, (argc + 2), sizeof(char *));
shargv = sudo_mmap_allocarray(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, (char **)shargv, nenvp);
free(shargv);
sudo_mmap_free(shargv);
}
} else {
errno = EACCES;
}
if (ncmnd != cmnd)
free(ncmnd);
if (nargv != argv)
free(nargv);
sudo_mmap_free(ncmnd);
if (nargv != argv && nargv != NULL) {
for (i = 0; nargv[i] != NULL; i++)
sudo_mmap_free(nargv[i]);
sudo_mmap_free(nargv);
}
/* Leaks allocated preload vars. */
if (nenvp != envp)
free(nenvp);
sudo_mmap_free(nenvp);
debug_return_int(-1);
}
@@ -228,7 +233,7 @@ execl_wrapper(int type, const char *name, const char *arg, va_list ap)
while (va_arg(ap2, char *) != NULL)
argc++;
va_end(ap2);
argv = reallocarray(NULL, (argc + 1), sizeof(char *));
argv = sudo_mmap_allocarray(argc + 1, sizeof(char *));
if (argv == NULL)
debug_return_int(-1);
@@ -240,7 +245,7 @@ execl_wrapper(int type, const char *name, const char *arg, va_list ap)
envp = va_arg(ap, char **);
exec_wrapper(name, argv, envp, type == SUDO_EXECLP);
free(argv);
sudo_mmap_free(argv);
debug_return_int(-1);
}

View File

@@ -111,7 +111,7 @@ send_client_hello(int sock)
msg_len = len;
len += sizeof(msg_len);
if ((buf = malloc(len)) == NULL) {
if ((buf = sudo_mmap_alloc(len)) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
}
@@ -121,7 +121,7 @@ send_client_hello(int sock)
ret = send_req(sock, buf, len);
done:
free(buf);
sudo_mmap_free(buf);
debug_return_bool(ret);
}
@@ -169,7 +169,7 @@ recv_intercept_response(int fd)
}
/* Read response from sudo (blocking). */
if ((buf = malloc(res_len)) == NULL) {
if ((buf = sudo_mmap_alloc(res_len)) == NULL) {
goto done;
}
cp = buf;
@@ -202,7 +202,7 @@ recv_intercept_response(int fd)
}
done:
free(buf);
sudo_mmap_free(buf);
debug_return_ptr(res);
}
@@ -338,7 +338,7 @@ send_policy_check_req(int sock, const char *cmnd, char * const argv[],
msg_len = len;
len += sizeof(msg_len);
if ((buf = malloc(len)) == NULL) {
if ((buf = sudo_mmap_alloc(len)) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
}
@@ -348,7 +348,7 @@ send_policy_check_req(int sock, const char *cmnd, char * const argv[],
ret = send_req(sock, buf, len);
done:
free(buf);
sudo_mmap_free(buf);
debug_return_bool(ret);
}
@@ -427,7 +427,7 @@ command_allowed(const char *cmnd, char * const argv[],
if (log_only) {
/* Just logging, no policy check. */
nenvp = sudo_preload_dso(envp, sudo_conf_intercept_path(), sock);
nenvp = sudo_preload_dso_mmap(envp, sudo_conf_intercept_path(), sock);
if (nenvp == NULL)
goto oom;
*ncmndp = (char *)cmnd; /* safe */
@@ -451,20 +451,20 @@ command_allowed(const char *cmnd, char * const argv[],
"run_argv[%zu]: %s", idx, res->u.accept_msg->run_argv[idx]);
}
}
ncmnd = strdup(res->u.accept_msg->run_command);
ncmnd = sudo_mmap_strdup(res->u.accept_msg->run_command);
if (ncmnd == NULL)
goto oom;
nargv = reallocarray(NULL, res->u.accept_msg->n_run_argv + 1,
nargv = sudo_mmap_allocarray(res->u.accept_msg->n_run_argv + 1,
sizeof(char *));
if (nargv == NULL)
goto oom;
for (len = 0; len < res->u.accept_msg->n_run_argv; len++) {
nargv[len] = strdup(res->u.accept_msg->run_argv[len]);
nargv[len] = sudo_mmap_strdup(res->u.accept_msg->run_argv[len]);
if (nargv[len] == NULL)
goto oom;
}
nargv[len] = NULL;
nenvp = sudo_preload_dso(envp, sudo_conf_intercept_path(), sock);
nenvp = sudo_preload_dso_mmap(envp, sudo_conf_intercept_path(), sock);
if (nenvp == NULL)
goto oom;
*ncmndp = ncmnd;
@@ -489,10 +489,10 @@ command_allowed(const char *cmnd, char * const argv[],
}
oom:
free(ncmnd);
sudo_mmap_free(ncmnd);
while (len > 0)
free(nargv[--len]);
free(nargv);
sudo_mmap_free(nargv[--len]);
sudo_mmap_free(nargv);
done:
/* Keep socket open for ctor when we execute the command. */