diff --git a/src/exec_preload.c b/src/exec_preload.c index 81e865333..2e9dc2ef7 100644 --- a/src/exec_preload.c +++ b/src/exec_preload.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2021 Todd C. Miller + * Copyright (c) 2009-2022 Todd C. Miller * * 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 #include +#include #include #include #include @@ -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) - goto oom; + 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 */ diff --git a/src/sudo_exec.h b/src/sudo_exec.h index 590767522..b8a8bcff2 100644 --- a/src/sudo_exec.h +++ b/src/sudo_exec.h @@ -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); diff --git a/src/sudo_intercept.c b/src/sudo_intercept.c index 2e11faf3e..71bffd1d8 100644 --- a/src/sudo_intercept.c +++ b/src/sudo_intercept.c @@ -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); } diff --git a/src/sudo_intercept_common.c b/src/sudo_intercept_common.c index a81a3c880..ea7f0d926 100644 --- a/src/sudo_intercept_common.c +++ b/src/sudo_intercept_common.c @@ -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. */