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 * 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 * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -24,6 +24,7 @@
#include <config.h> #include <config.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -31,13 +32,145 @@
#include "sudo.h" #include "sudo.h"
#include "sudo_exec.h" #include "sudo_exec.h"
#include "sudo_util.h"
#ifdef RTLD_PRELOAD_VAR #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. * Add a DSO file to LD_PRELOAD or the system equivalent.
*/ */
char ** static char **
sudo_preload_dso(char *const envp[], const char *dso_file, int intercept_fd) 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 *preload = NULL;
char **nep, **nenvp = 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; char *dso_buf = NULL;
# endif # endif
size_t env_size; size_t env_size;
int len;
debug_decl(sudo_preload_dso, SUDO_DEBUG_UTIL); debug_decl(sudo_preload_dso, SUDO_DEBUG_UTIL);
# ifdef _PATH_ASAN_LIB # ifdef _PATH_ASAN_LIB
/* /*
* The address sanitizer DSO needs to be first in the list. * 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_buf = fmtstr(alloc_fn, free_fn, "%s%c%s", _PATH_ASAN_LIB,
dso_file); RTLD_PRELOAD_DELIM, dso_file);
if (len == -1) if (dso_buf == NULL) {
goto oom; goto oom;
}
dso_file = dso_buf; dso_file = dso_buf;
# endif # 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 */ env_size += 2; /* dso_file + terminating NULL */
/* Allocate new envp. */ /* Allocate new envp. */
nenvp = reallocarray(NULL, env_size, sizeof(*nenvp)); nenvp = alloc_fn(env_size, sizeof(*nenvp));
if (nenvp == NULL) { if (nenvp == NULL)
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto oom;
debug_return_ptr(NULL);
}
/* /*
* Shallow copy envp, with special handling for RTLD_PRELOAD_VAR, * Shallow copy envp, with special handling for RTLD_PRELOAD_VAR,
@@ -156,13 +287,14 @@ copy:
if (!dso_present) { if (!dso_present) {
if (preload_ptr == NULL) { if (preload_ptr == NULL) {
# ifdef RTLD_PRELOAD_DEFAULT # ifdef RTLD_PRELOAD_DEFAULT
len = asprintf(&preload, "%s=%s%c%s", RTLD_PRELOAD_VAR, dso_file, preload = fmtstr(alloc_fn, free_fn, "%s=%s%c%s", RTLD_PRELOAD_VAR,
RTLD_PRELOAD_DELIM, RTLD_PRELOAD_DEFAULT); dso_file, RTLD_PRELOAD_DELIM, RTLD_PRELOAD_DEFAULT);
if (len == -1) { if (preload == NULL) {
goto oom; goto oom;
} }
# else # 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) { if (preload == NULL) {
goto oom; goto oom;
} }
@@ -170,9 +302,9 @@ copy:
*nep++ = preload; *nep++ = preload;
} else { } else {
const char *old_val = *preload_ptr + sizeof(RTLD_PRELOAD_VAR); 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); dso_file, RTLD_PRELOAD_DELIM, old_val);
if (len == -1) { if (preload == NULL) {
goto oom; goto oom;
} }
*preload_ptr = preload; *preload_ptr = preload;
@@ -184,10 +316,9 @@ copy:
} }
# endif # endif
if (!fd_present && intercept_fd != -1) { if (!fd_present && intercept_fd != -1) {
char *fdstr; char *fdstr = fmtstr(alloc_fn, free_fn, "SUDO_INTERCEPT_FD=%d",
intercept_fd);
len = asprintf(&fdstr, "SUDO_INTERCEPT_FD=%d", intercept_fd); if (fdstr == NULL) {
if (len == -1) {
goto oom; goto oom;
} }
if (intercept_ptr != NULL) { if (intercept_ptr != NULL) {
@@ -201,17 +332,33 @@ copy:
*nep = NULL; *nep = NULL;
# ifdef _PATH_ASAN_LIB # ifdef _PATH_ASAN_LIB
free(dso_buf); free_fn(dso_buf);
# endif # endif
debug_return_ptr(nenvp); debug_return_ptr(nenvp);
oom: oom:
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
# ifdef _PATH_ASAN_LIB # ifdef _PATH_ASAN_LIB
free(dso_buf); free_fn(dso_buf);
# endif # endif
free(preload); free_fn(preload);
free(nenvp); free_fn(nenvp);
debug_return_ptr(NULL); 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 */ #endif /* RTLD_PRELOAD_VAR */

View File

@@ -145,6 +145,7 @@ bool utmp_logout(const char *line, int status);
/* exec_preload.c */ /* exec_preload.c */
char **sudo_preload_dso(char *const envp[], const char *dso_file, int intercept_fd); 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 */ /* exec_ptrace.c */
bool exec_ptrace_stopped(pid_t pid, int status, void *intercept); 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 *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) {
@@ -188,24 +189,28 @@ exec_wrapper(const char *cmnd, char * const argv[], char * const envp[],
for (argc = 0; argv[argc] != NULL; argc++) for (argc = 0; argv[argc] != NULL; argc++)
continue; continue;
shargv = reallocarray(NULL, (argc + 2), sizeof(char *)); shargv = sudo_mmap_allocarray(argc + 2, sizeof(char *));
if (shargv == NULL) if (shargv == NULL)
return -1; return -1;
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 *));
((sudo_fn_execve_t)fn)(_PATH_SUDO_BSHELL, (char **)shargv, nenvp); ((sudo_fn_execve_t)fn)(_PATH_SUDO_BSHELL, (char **)shargv, nenvp);
free(shargv); sudo_mmap_free(shargv);
} }
} else { } else {
errno = EACCES; errno = EACCES;
} }
if (ncmnd != cmnd) if (ncmnd != cmnd)
free(ncmnd); sudo_mmap_free(ncmnd);
if (nargv != argv) if (nargv != argv && nargv != NULL) {
free(nargv); for (i = 0; nargv[i] != NULL; i++)
sudo_mmap_free(nargv[i]);
sudo_mmap_free(nargv);
}
/* Leaks allocated preload vars. */
if (nenvp != envp) if (nenvp != envp)
free(nenvp); sudo_mmap_free(nenvp);
debug_return_int(-1); 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) while (va_arg(ap2, char *) != NULL)
argc++; argc++;
va_end(ap2); va_end(ap2);
argv = reallocarray(NULL, (argc + 1), sizeof(char *)); argv = sudo_mmap_allocarray(argc + 1, sizeof(char *));
if (argv == NULL) if (argv == NULL)
debug_return_int(-1); 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 **); envp = va_arg(ap, char **);
exec_wrapper(name, argv, envp, type == SUDO_EXECLP); exec_wrapper(name, argv, envp, type == SUDO_EXECLP);
free(argv); sudo_mmap_free(argv);
debug_return_int(-1); debug_return_int(-1);
} }

View File

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