diff -Nru sudo-1.8.21p2/debian/changelog sudo-1.8.21p2/debian/changelog --- sudo-1.8.21p2/debian/changelog 2020-01-31 17:18:41.000000000 +0000 +++ sudo-1.8.21p2/debian/changelog 2023-04-04 12:44:58.000000000 +0000 @@ -1,3 +1,64 @@ +sudo (1.8.21p2-3ubuntu1.6) bionic-security; urgency=medium + + * SECURITY UPDATE: does not escape control characters + - debian/patches/CVE-2023-2848x-1.patch: escape control characters in + log messages and sudoreplay output in docs/sudoers.man.in, + docs/sudoers.mdoc.in, docs/sudoreplay.man.in, + docs/sudoreplay.mdoc.in, include/sudo_compat.h, include/sudo_lbuf.h, + lib/util/lbuf.c, lib/util/util.exp.in, plugins/sudoers/logging.c, + plugins/sudoers/sudoreplay.c. + - debian/patches/CVE-2023-2848x-2.patch: fix regression in + plugins/sudoers/logging.c. + - CVE-2023-28486 + - CVE-2023-28487 + + -- Marc Deslauriers Tue, 04 Apr 2023 08:44:58 -0400 + +sudo (1.8.21p2-3ubuntu1.5) bionic-security; urgency=medium + + * SECURITY UPDATE: arbitrary file overwrite via sudoedit + - debian/patches/CVE-2023-22809.patch: do not permit editor arguments + to include -- in plugins/sudoers/editor.c. + - CVE-2023-22809 + + -- Marc Deslauriers Mon, 16 Jan 2023 09:40:55 -0500 + +sudo (1.8.21p2-3ubuntu1.4) bionic-security; urgency=medium + + * SECURITY UPDATE: dir existence issue via sudoedit race + - debian/patches/CVE-2021-23239.patch: fix potential directory existing + info leak in sudoedit in src/sudo_edit.c. + - CVE-2021-23239 + * SECURITY UPDATE: heap-based buffer overflow + - debian/patches/CVE-2021-3156-pre1.patch: check lock record size in + plugins/sudoers/timestamp.c. + - debian/patches/CVE-2021-3156-pre2.patch: sanity check size when + converting the first record to TS_LOCKEXCL in + plugins/sudoers/timestamp.c. + - debian/patches/CVE-2021-3156-1.patch: reset valid_flags to + MODE_NONINTERACTIVE for sudoedit in src/parse_args.c. + - debian/patches/CVE-2021-3156-2.patch: add sudoedit flag checks in + plugin in plugins/sudoers/policy.c. + - debian/patches/CVE-2021-3156-3.patch: fix potential buffer overflow + when unescaping backslashes in plugins/sudoers/sudoers.c. + - debian/patches/CVE-2021-3156-4.patch: fix the memset offset when + converting a v1 timestamp to TS_LOCKEXCL in + plugins/sudoers/timestamp.c. + - debian/patches/CVE-2021-3156-5.patch: don't assume that argv is + allocated as a single flat buffer in src/parse_args.c. + - CVE-2021-3156 + * debian/control: added tzdata to Build-Depends so that the time zone + data directory is present during builds. + + -- Marc Deslauriers Tue, 19 Jan 2021 09:36:00 -0500 + +sudo (1.8.21p2-3ubuntu1.3) bionic; urgency=medium + + * d/p/0001-In-pty_close-close-the-slave-and-remove-any-events-a.patch: + - fix sudo hangs when IO logging is enabled (LP: #1895757) + + -- Heitor Alves de Siqueira Wed, 23 Sep 2020 14:59:35 +0000 + sudo (1.8.21p2-3ubuntu1.2) bionic-security; urgency=medium * SECURITY UPDATE: buffer overflow in sudo when pwfeedback is enabled diff -Nru sudo-1.8.21p2/debian/control sudo-1.8.21p2/debian/control --- sudo-1.8.21p2/debian/control 2018-01-18 00:08:16.000000000 +0000 +++ sudo-1.8.21p2/debian/control 2021-01-19 14:36:00.000000000 +0000 @@ -4,7 +4,7 @@ Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Bdale Garbee Build-Depends: debhelper (>= 10), libpam0g-dev, libldap2-dev, libsasl2-dev, libselinux1-dev [linux-any], autoconf, autotools-dev, bison, flex, libaudit-dev [linux-any], mandoc, - dh-autoreconf, + dh-autoreconf, tzdata Standards-Version: 4.1.1 Vcs-Git: git://anonscm.debian.org/collab-maint/sudo.git Vcs-Browser: http://anonscm.debian.org/gitweb/?p=collab-maint/sudo.git diff -Nru sudo-1.8.21p2/debian/patches/0001-In-pty_close-close-the-slave-and-remove-any-events-a.patch sudo-1.8.21p2/debian/patches/0001-In-pty_close-close-the-slave-and-remove-any-events-a.patch --- sudo-1.8.21p2/debian/patches/0001-In-pty_close-close-the-slave-and-remove-any-events-a.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/0001-In-pty_close-close-the-slave-and-remove-any-events-a.patch 2020-09-23 14:59:35.000000000 +0000 @@ -0,0 +1,69 @@ +Description: In pty_close() close the slave and remove any events associated + with it. Fixes a potential hang when performing the final flush + on non-BSD systems. + +Author: Todd C. Miller +Origin: backport, https://github.com/sudo-project/sudo/commit/4df454310dae +Bug-ubuntu: https://bugs.launchpad.net/bugs/1895757 +--- a/src/exec_pty.c ++++ b/src/exec_pty.c +@@ -680,12 +680,19 @@ + } + + static void +-pty_close(struct command_status *cstat) ++pty_close(struct sudo_event_base *evbase, struct command_status *cstat) + { + struct io_buffer *iob; + int n; + debug_decl(pty_close, SUDO_DEBUG_EXEC); + ++ /* Close the pty slave first so reads from the master don't block. */ ++ if (io_fds[SFD_SLAVE] != -1) { ++ ev_free_by_fd(evbase, io_fds[SFD_SLAVE]); ++ close(io_fds[SFD_SLAVE]); ++ io_fds[SFD_SLAVE] = -1; ++ } ++ + /* Flush any remaining output (the plugin already got it). */ + if (io_fds[SFD_USERTTY] != -1) { + n = fcntl(io_fds[SFD_USERTTY], F_GETFL, 0); +@@ -713,6 +720,11 @@ + /* Update utmp */ + if (utmp_user != NULL) + utmp_logout(slavename, cstat->type == CMD_WSTATUS ? cstat->val : 0); ++ ++ /* Close pty master. */ ++ if (io_fds[SFD_MASTER] != -1) ++ close(io_fds[SFD_MASTER]); ++ + debug_return; + } + +@@ -1409,7 +1421,7 @@ + } + + /* Flush any remaining output, free I/O bufs and events, do logout. */ +- pty_close(cstat); ++ pty_close(ec.evbase, cstat); + + /* Free things up. */ + free_exec_closure_pty(&ec); +@@ -1509,6 +1521,8 @@ + } + } + } ++ sudo_debug_printf(SUDO_DEBUG_INFO, ++ "%s: flushing remaining I/O buffers (nonblocking)", __func__); + (void) sudo_ev_loop(evbase, SUDO_EVLOOP_NONBLOCK); + + /* +@@ -1534,6 +1548,8 @@ + } + } + } ++ sudo_debug_printf(SUDO_DEBUG_INFO, ++ "%s: flushing remaining write buffers (blocking)", __func__); + (void) sudo_ev_loop(evbase, 0); + + /* We should now have flushed all write buffers. */ diff -Nru sudo-1.8.21p2/debian/patches/CVE-2021-23239.patch sudo-1.8.21p2/debian/patches/CVE-2021-23239.patch --- sudo-1.8.21p2/debian/patches/CVE-2021-23239.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2021-23239.patch 2021-01-19 14:21:43.000000000 +0000 @@ -0,0 +1,55 @@ + +# HG changeset patch +# User Todd C. Miller +# Date 1609953360 25200 +# Node ID ea19d0073c02951bbbf35342dd63304da83edce8 +# Parent f1ca39a0d87089d005b78a2556e2b1a2dc17f672 +Fix potential directory existing info leak in sudoedit. +When creating a new file, sudoedit checks to make sure the parent +directory exists so it can provide the user with a sensible error +message. However, this could be used to test for the existence of +directories not normally accessible to the user by pointing to them +with a symbolic link when the parent directory is controlled by the +user. Problem reported by Matthias Gerstner of SUSE. + +--- a/src/sudo_edit.c ++++ b/src/sudo_edit.c +@@ -549,14 +549,33 @@ sudo_edit_create_tfiles(struct command_d + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details); + if (ofd != -1 || errno == ENOENT) { + if (ofd == -1) { +- /* New file, verify parent dir exists unless in cwd. */ ++ /* ++ * New file, verify parent dir exists unless in cwd. ++ * This fails early so the user knows ahead of time if the ++ * edit won't succeed. Additional checks are performed ++ * when copying the temporary file back to the origin. ++ */ + char *slash = strrchr(files[i], '/'); + if (slash != NULL && slash != files[i]) { +- int serrno = errno; ++ const int sflags = command_details->flags; ++ const int serrno = errno; ++ int dfd; ++ ++ /* ++ * The parent directory is allowed to be a symbolic ++ * link as long as *its* parent is not writable. ++ */ + *slash = '\0'; +- if (stat(files[i], &sb) == 0 && S_ISDIR(sb.st_mode)) { +- memset(&sb, 0, sizeof(sb)); +- rc = 0; ++ SET(command_details->flags, CD_SUDOEDIT_FOLLOW); ++ dfd = sudo_edit_open(files[i], DIR_OPEN_FLAGS, ++ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details); ++ command_details->flags = sflags; ++ if (dfd != -1) { ++ if (fstat(dfd, &sb) == 0 && S_ISDIR(sb.st_mode)) { ++ memset(&sb, 0, sizeof(sb)); ++ rc = 0; ++ } ++ close(dfd); + } + *slash = '/'; + errno = serrno; diff -Nru sudo-1.8.21p2/debian/patches/CVE-2021-3156-1.patch sudo-1.8.21p2/debian/patches/CVE-2021-3156-1.patch --- sudo-1.8.21p2/debian/patches/CVE-2021-3156-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2021-3156-1.patch 2021-01-19 14:36:00.000000000 +0000 @@ -0,0 +1,90 @@ +# HG changeset patch +# Parent 111fde52d1166af65b622da6eae19791ce0e8871 +Reset valid_flags to MODE_NONINTERACTIVE for sudoedit. +This is consistent with how the -e option is handled. +Also reject -H and -P flags for sudoedit as was done in sudo 1.7. +Found by Qualys. + +--- a/src/parse_args.c ++++ b/src/parse_args.c +@@ -116,7 +116,10 @@ struct environment { + /* + * Default flags allowed when running a command. + */ +-#define DEFAULT_VALID_FLAGS (MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_SHELL) ++#define DEFAULT_VALID_FLAGS (MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_PRESERVE_GROUPS|MODE_SHELL) ++#define EDIT_VALID_FLAGS MODE_NONINTERACTIVE ++#define LIST_VALID_FLAGS (MODE_NONINTERACTIVE|MODE_LONG_LIST) ++#define VALIDATE_VALID_FLAGS MODE_NONINTERACTIVE + + /* Option number for the --host long option due to ambiguity of the -h flag. */ + #define OPT_HOSTNAME 256 +@@ -260,6 +263,7 @@ parse_args(int argc, char **argv, int *n + progname = "sudoedit"; + mode = MODE_EDIT; + sudo_settings[ARG_SUDOEDIT].value = "true"; ++ valid_flags = EDIT_VALID_FLAGS; + } + + /* Load local IP addresses and masks. */ +@@ -339,7 +343,7 @@ parse_args(int argc, char **argv, int *n + usage_excl(1); + mode = MODE_EDIT; + sudo_settings[ARG_SUDOEDIT].value = "true"; +- valid_flags = MODE_NONINTERACTIVE; ++ valid_flags = EDIT_VALID_FLAGS; + break; + case 'g': + runas_group = optarg; +@@ -347,6 +351,7 @@ parse_args(int argc, char **argv, int *n + break; + case 'H': + sudo_settings[ARG_SET_HOME].value = "true"; ++ SET(flags, MODE_RESET_HOME); + break; + case 'h': + if (optarg == NULL) { +@@ -394,7 +399,7 @@ parse_args(int argc, char **argv, int *n + usage_excl(1); + } + mode = MODE_LIST; +- valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST; ++ valid_flags = LIST_VALID_FLAGS; + break; + case 'n': + SET(flags, MODE_NONINTERACTIVE); +@@ -402,6 +407,7 @@ parse_args(int argc, char **argv, int *n + break; + case 'P': + sudo_settings[ARG_PRESERVE_GROUPS].value = "true"; ++ SET(flags, MODE_PRESERVE_GROUPS); + break; + case 'p': + sudo_settings[ARG_PROMPT].value = optarg; +@@ -435,7 +441,7 @@ parse_args(int argc, char **argv, int *n + if (mode && mode != MODE_VALIDATE) + usage_excl(1); + mode = MODE_VALIDATE; +- valid_flags = MODE_NONINTERACTIVE; ++ valid_flags = VALIDATE_VALID_FLAGS; + break; + case 'V': + if (mode && mode != MODE_VERSION) +@@ -462,7 +468,7 @@ parse_args(int argc, char **argv, int *n + if (!mode) { + /* Defer -k mode setting until we know whether it is a flag or not */ + if (sudo_settings[ARG_IGNORE_TICKET].value != NULL) { +- if (argc == 0 && !(flags & (MODE_SHELL|MODE_LOGIN_SHELL))) { ++ if (argc == 0 && !ISSET(flags, MODE_SHELL|MODE_LOGIN_SHELL)) { + mode = MODE_INVALIDATE; /* -k by itself */ + sudo_settings[ARG_IGNORE_TICKET].value = NULL; + valid_flags = 0; +@@ -525,7 +531,7 @@ parse_args(int argc, char **argv, int *n + /* + * For shell mode we need to rewrite argv + */ +- if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) { ++ if (ISSET(flags, MODE_SHELL|MODE_LOGIN_SHELL) && ISSET(mode, MODE_RUN)) { + char **av, *cmnd = NULL; + int ac = 1; + diff -Nru sudo-1.8.21p2/debian/patches/CVE-2021-3156-2.patch sudo-1.8.21p2/debian/patches/CVE-2021-3156-2.patch --- sudo-1.8.21p2/debian/patches/CVE-2021-3156-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2021-3156-2.patch 2021-01-19 14:35:15.000000000 +0000 @@ -0,0 +1,37 @@ +Backport of: + +# HG changeset patch +# Parent 4e0af3ef53d71d1e0d1ee5894a0c078020ab391a +Add sudoedit flag checks in plugin that are consistent with front-end. +Don't assume the sudo front-end is sending reasonable mode flags. +These checks need to be kept consistent between the sudo front-end +and the sudoers plugin. + +--- a/plugins/sudoers/policy.c ++++ b/plugins/sudoers/policy.c +@@ -75,10 +75,11 @@ extern char *login_style; + int + sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group) + { ++ const int edit_mask = MODE_EDIT|MODE_IGNORE_TICKET|MODE_NONINTERACTIVE; + struct sudoers_policy_open_info *info = v; +- char * const *cur; + const char *p, *errstr, *groups = NULL; + const char *remhost = NULL; ++ char * const *cur; + int flags = 0; + debug_decl(sudoers_policy_deserialize_info, SUDOERS_DEBUG_PLUGIN) + +@@ -278,6 +279,12 @@ sudoers_policy_deserialize_info(void *v, + #endif + } + ++ /* Sudo front-end should restrict mode flags for sudoedit. */ ++ if (ISSET(flags, MODE_EDIT) && (flags & edit_mask) != flags) { ++ sudo_warnx(U_("invalid mode flags from sudo front end: 0x%x"), flags); ++ goto bad; ++ } ++ + user_umask = (mode_t)-1; + for (cur = info->user_info; *cur != NULL; cur++) { + if (MATCHES(*cur, "user=")) { diff -Nru sudo-1.8.21p2/debian/patches/CVE-2021-3156-3.patch sudo-1.8.21p2/debian/patches/CVE-2021-3156-3.patch --- sudo-1.8.21p2/debian/patches/CVE-2021-3156-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2021-3156-3.patch 2021-01-19 14:35:39.000000000 +0000 @@ -0,0 +1,65 @@ +Backport of: + +# HG changeset patch +# Parent 9b29e05ea310e187e41d8bcb58eddef8bd8b70d3 +Fix potential buffer overflow when unescaping backslashes in user_args. +Do not try to unescaping backslashes unless in run mode *and* we are +running the command via a shell. +Found by Qualys. + +--- a/plugins/sudoers/sudoers.c ++++ b/plugins/sudoers/sudoers.c +@@ -438,7 +438,7 @@ sudoers_policy_main(int argc, char * con + + /* If run as root with SUDO_USER set, set sudo_user.pw to that user. */ + /* XXX - causes confusion when root is not listed in sudoers */ +- if (sudo_mode & (MODE_RUN | MODE_EDIT) && prev_user != NULL) { ++ if (ISSET(sudo_mode, MODE_RUN|MODE_EDIT) && prev_user != NULL) { + if (user_uid == 0 && strcmp(prev_user, "root") != 0) { + struct passwd *pw; + +@@ -811,8 +811,8 @@ set_cmnd(void) + if (user_cmnd == NULL) + user_cmnd = NewArgv[0]; + +- if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) { +- if (ISSET(sudo_mode, MODE_RUN | MODE_CHECK)) { ++ if (ISSET(sudo_mode, MODE_RUN|MODE_EDIT|MODE_CHECK)) { ++ if (!ISSET(sudo_mode, MODE_EDIT)) { + if (def_secure_path && !user_is_exempt()) + path = def_secure_path; + if (!set_perms(PERM_RUNAS)) +@@ -850,7 +850,8 @@ set_cmnd(void) + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + debug_return_int(-1); + } +- if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) { ++ if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL) && ++ ISSET(sudo_mode, MODE_RUN)) { + /* + * When running a command via a shell, the sudo front-end + * escapes potential meta chars. We unescape non-spaces +@@ -858,10 +859,22 @@ set_cmnd(void) + */ + for (to = user_args, av = NewArgv + 1; (from = *av); av++) { + while (*from) { +- if (from[0] == '\\' && !isspace((unsigned char)from[1])) ++ if (from[0] == '\\' && from[1] != '\0' && ++ !isspace((unsigned char)from[1])) { + from++; ++ } ++ if (size - (to - user_args) < 1) { ++ sudo_warnx(U_("internal error, %s overflow"), ++ __func__); ++ debug_return_int(NOT_FOUND_ERROR); ++ } + *to++ = *from++; + } ++ if (size - (to - user_args) < 1) { ++ sudo_warnx(U_("internal error, %s overflow"), ++ __func__); ++ debug_return_int(NOT_FOUND_ERROR); ++ } + *to++ = ' '; + } + *--to = '\0'; diff -Nru sudo-1.8.21p2/debian/patches/CVE-2021-3156-4.patch sudo-1.8.21p2/debian/patches/CVE-2021-3156-4.patch --- sudo-1.8.21p2/debian/patches/CVE-2021-3156-4.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2021-3156-4.patch 2021-01-19 14:36:00.000000000 +0000 @@ -0,0 +1,19 @@ +# HG changeset patch +# Parent 5c6c54c8f971dfa21977935328942a57591ce5a8 +Fix the memset offset when converting a v1 timestamp to TS_LOCKEXCL. +We want to zero the struct starting at flags, not type (which was just set). +Found by Qualys. + +--- a/plugins/sudoers/timestamp.c ++++ b/plugins/sudoers/timestamp.c +@@ -620,8 +620,8 @@ timestamp_lock(void *vcookie, struct pas + if (entry.size == sizeof(struct timestamp_entry)) { + /* Old sudo record, convert it to TS_LOCKEXCL. */ + entry.type = TS_LOCKEXCL; +- memset((char *)&entry + offsetof(struct timestamp_entry, type), 0, +- nread - offsetof(struct timestamp_entry, type)); ++ memset((char *)&entry + offsetof(struct timestamp_entry, flags), 0, ++ nread - offsetof(struct timestamp_entry, flags)); + if (ts_write(cookie->fd, cookie->fname, &entry, 0) == -1) + debug_return_bool(false); + } else { diff -Nru sudo-1.8.21p2/debian/patches/CVE-2021-3156-5.patch sudo-1.8.21p2/debian/patches/CVE-2021-3156-5.patch --- sudo-1.8.21p2/debian/patches/CVE-2021-3156-5.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2021-3156-5.patch 2021-01-19 14:35:53.000000000 +0000 @@ -0,0 +1,33 @@ +Backport of: + +# HG changeset patch +# Parent a84c8fe05da6097efaa00d8dee8a07b5816ae84e +Don't assume that argv is allocated as a single flat buffer. +While this is how the kernel behaves it is not a portable assumption. +The assumption may also be violated if getopt_long(3) permutes arguments. +Found by Qualys. + +--- a/src/parse_args.c ++++ b/src/parse_args.c +@@ -538,16 +538,16 @@ parse_args(int argc, char **argv, int *n + if (argc != 0) { + /* shell -c "command" */ + char *src, *dst; +- size_t cmnd_size = (size_t) (argv[argc - 1] - argv[0]) + +- strlen(argv[argc - 1]) + 1; ++ size_t size = 0; + +- cmnd = dst = reallocarray(NULL, cmnd_size, 2); +- if (cmnd == NULL) ++ for (av = argv; *av != NULL; av++) ++ size += strlen(*av) + 1; ++ if (size == 0 || (cmnd = reallocarray(NULL, size, 2)) == NULL) + sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + if (!gc_add(GC_PTR, cmnd)) + exit(1); + +- for (av = argv; *av != NULL; av++) { ++ for (dst = cmnd, av = argv; *av != NULL; av++) { + for (src = *av; *src != '\0'; src++) { + /* quote potential meta characters */ + if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$') diff -Nru sudo-1.8.21p2/debian/patches/CVE-2021-3156-pre1.patch sudo-1.8.21p2/debian/patches/CVE-2021-3156-pre1.patch --- sudo-1.8.21p2/debian/patches/CVE-2021-3156-pre1.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2021-3156-pre1.patch 2021-01-19 14:30:42.000000000 +0000 @@ -0,0 +1,30 @@ + +# HG changeset patch +# User Todd C. Miller +# Date 1513397318 25200 +# Node ID e8e4c3815db58fb6d60d52a95a620af084472086 +# Parent 7cb966d69bc6843688f333a54533cf9b49a14706 +If the lock record doesn't match the expected record size we need +to seek to the end of the record as we otherwise may have gone too +far (or not far enough). Fixes interop problems when the time stamp +record changes size. + +diff -r 7cb966d69bc6 -r e8e4c3815db5 plugins/sudoers/timestamp.c +--- a/plugins/sudoers/timestamp.c Tue Dec 12 21:44:23 2017 -0700 ++++ b/plugins/sudoers/timestamp.c Fri Dec 15 21:08:38 2017 -0700 +@@ -608,6 +608,14 @@ + if (ts_write(cookie->fd, cookie->fname, &entry, 0) == -1) + debug_return_bool(false); + } ++ if (entry.size != sizeof(entry)) { ++ /* Reset position if the lock record has an unexpected size. */ ++ if (lseek(cookie->fd, entry.size, SEEK_SET) == -1) { ++ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, ++ "unable to seek to %lld", (long long)entry.size); ++ debug_return_bool(false); ++ } ++ } + + /* Search for a tty/ppid-based record or append a new one. */ + sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, + diff -Nru sudo-1.8.21p2/debian/patches/CVE-2021-3156-pre2.patch sudo-1.8.21p2/debian/patches/CVE-2021-3156-pre2.patch --- sudo-1.8.21p2/debian/patches/CVE-2021-3156-pre2.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2021-3156-pre2.patch 2021-01-19 14:36:00.000000000 +0000 @@ -0,0 +1,102 @@ +Backport of: + + +# HG changeset patch +# User Todd C. Miller +# Date 1578242229 25200 +# Node ID 5b94873c4051e9f18b7d4254b8f85fd759393844 +# Parent 4bcba58004c8df07e671bfcd2037b0b34c560815 +Sanity check size when converting the first record to TS_LOCKEXCL +Coverity CID 206591 + +--- a/plugins/sudoers/timestamp.c ++++ b/plugins/sudoers/timestamp.c +@@ -564,6 +564,25 @@ done: + } + + /* ++ * Write a TS_LOCKEXCL record at the beginning of the time stamp file. ++ */ ++bool ++timestamp_lock_write(struct ts_cookie *cookie) ++{ ++ struct timestamp_entry entry; ++ bool ret = true; ++ debug_decl(timestamp_lock_write, SUDOERS_DEBUG_AUTH); ++ ++ memset(&entry, 0, sizeof(entry)); ++ entry.version = TS_VERSION; ++ entry.size = sizeof(entry); ++ entry.type = TS_LOCKEXCL; ++ if (ts_write(cookie->fd, cookie->fname, &entry, -1) == -1) ++ ret = false; ++ debug_return_bool(ret); ++} ++ ++/* + * Lock a record in the time stamp file for exclusive access. + * If the record does not exist, it is created (as disabled). + */ +@@ -572,6 +591,7 @@ timestamp_lock(void *vcookie, struct pas + { + struct ts_cookie *cookie = vcookie; + struct timestamp_entry entry; ++ bool overwrite = false; + off_t lock_pos; + ssize_t nread; + debug_decl(timestamp_lock, SUDOERS_DEBUG_AUTH) +@@ -593,26 +613,39 @@ timestamp_lock(void *vcookie, struct pas + /* Make sure the first record is of type TS_LOCKEXCL. */ + memset(&entry, 0, sizeof(entry)); + nread = read(cookie->fd, &entry, sizeof(entry)); +- if (nread == 0) { +- /* New file, add TS_LOCKEXCL record. */ +- entry.version = TS_VERSION; +- entry.size = sizeof(entry); +- entry.type = TS_LOCKEXCL; +- if (ts_write(cookie->fd, cookie->fname, &entry, -1) == -1) +- debug_return_bool(false); ++ if (nread < sizeof(struct timestamp_entry)) { ++ /* New or invalid time stamp file. */ ++ overwrite = true; + } else if (entry.type != TS_LOCKEXCL) { +- /* Old sudo record, convert it to TS_LOCKEXCL. */ +- entry.type = TS_LOCKEXCL; +- memset((char *)&entry + offsetof(struct timestamp_entry, type), 0, +- nread - offsetof(struct timestamp_entry, type)); +- if (ts_write(cookie->fd, cookie->fname, &entry, 0) == -1) +- debug_return_bool(false); ++ if (entry.size == sizeof(struct timestamp_entry)) { ++ /* Old sudo record, convert it to TS_LOCKEXCL. */ ++ entry.type = TS_LOCKEXCL; ++ memset((char *)&entry + offsetof(struct timestamp_entry, type), 0, ++ nread - offsetof(struct timestamp_entry, type)); ++ if (ts_write(cookie->fd, cookie->fname, &entry, 0) == -1) ++ debug_return_bool(false); ++ } else { ++ /* Corrupted time stamp file? Just overwrite it. */ ++ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, ++ "corrupt initial record, type: %hu, size: %hu (expected %zu)", ++ entry.type, entry.size, sizeof(struct timestamp_entry)); ++ overwrite = true; ++ } + } +- if (entry.size != sizeof(entry)) { ++ if (overwrite) { ++ /* Rewrite existing time stamp file or create new one. */ ++ if (ftruncate(cookie->fd, 0) != 0) { ++ sudo_warn(U_("unable to truncate time stamp file to %lld bytes"), ++ 0LL); ++ debug_return_bool(false); ++ } ++ if (!timestamp_lock_write(cookie)) ++ debug_return_bool(false); ++ } else if (entry.size != sizeof(entry)) { + /* Reset position if the lock record has an unexpected size. */ + if (lseek(cookie->fd, entry.size, SEEK_SET) == -1) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, +- "unable to seek to %lld", (long long)entry.size); ++ "unable to seek to %hu", entry.size); + debug_return_bool(false); + } + } diff -Nru sudo-1.8.21p2/debian/patches/CVE-2023-22809.patch sudo-1.8.21p2/debian/patches/CVE-2023-22809.patch --- sudo-1.8.21p2/debian/patches/CVE-2023-22809.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2023-22809.patch 2023-01-16 14:40:55.000000000 +0000 @@ -0,0 +1,62 @@ +Backport of: + +# HG changeset patch +# Parent 7275148cad1f8cd3c350026460acc4d6ad349c3a +sudoedit: do not permit editor arguments to include "--" +We use "--" to separate the editor and arguments from the files to edit. +If the editor arguments include "--", sudo can be tricked into allowing +the user to edit a file not permitted by the security policy. +Thanks to Matthieu Barjole and Victor Cutillas of Synacktiv +(https://synacktiv.com) for finding this bug. + +--- a/plugins/sudoers/editor.c ++++ b/plugins/sudoers/editor.c +@@ -48,7 +48,7 @@ resolve_editor(const char *ed, size_t ed + const char *cp, *ep, *tmp; + const char *edend = ed + edlen; + struct stat user_editor_sb; +- int nargc; ++ int nargc = 0; + debug_decl(resolve_editor, SUDOERS_DEBUG_UTIL) + + /* +@@ -94,6 +94,21 @@ resolve_editor(const char *ed, size_t ed + free(editor_path); + while (nargc--) + free(nargv[nargc]); ++ free(nargv); ++ debug_return_str(NULL); ++ } ++ ++ /* ++ * We use "--" to separate the editor and arguments from the files ++ * to edit. The editor arguments themselves may not contain "--". ++ */ ++ if (strcmp(nargv[nargc], "--") == 0) { ++ sudo_warnx(U_("ignoring editor: %.*s"), (int)edlen, ed); ++ sudo_warnx("%s", U_("editor arguments may not contain \"--\"")); ++ errno = EINVAL; ++ free(editor_path); ++ while (nargc--) ++ free(nargv[nargc]); + free(nargv); + debug_return_str(NULL); + } +--- a/plugins/sudoers/sudoers.c ++++ b/plugins/sudoers/sudoers.c +@@ -626,9 +626,13 @@ sudoers_policy_main(int argc, char * con + safe_cmnd = find_editor(NewArgc - 1, NewArgv + 1, &edit_argc, + &edit_argv); + if (safe_cmnd == NULL) { +- if (errno != ENOENT) ++ switch (errno) { ++ case ENOENT: ++ case EINVAL: ++ goto bad; ++ default: + goto done; +- goto bad; ++ } + } + if (audit_success(edit_argc, edit_argv) != 0 && !def_ignore_audit_errors) + goto done; diff -Nru sudo-1.8.21p2/debian/patches/CVE-2023-2848x-1.patch sudo-1.8.21p2/debian/patches/CVE-2023-2848x-1.patch --- sudo-1.8.21p2/debian/patches/CVE-2023-2848x-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2023-2848x-1.patch 2023-04-04 12:44:58.000000000 +0000 @@ -0,0 +1,647 @@ +Origin: Backport obtained from SUSE. Thanks! + +From 334daf92b31b79ce68ed75e2ee14fca265f029ca Mon Sep 17 00:00:00 2001 +From: "Todd C. Miller" +Date: Wed, 18 Jan 2023 08:21:34 -0700 +Subject: [PATCH] Escape control characters in log messages and "sudoreplay -l" + output. The log message contains user-controlled strings that could include + things like terminal control characters. Space characters in the command + path are now also escaped. + +Command line arguments that contain spaces are surrounded with +single quotes and any literal single quote or backslash characters +are escaped with a backslash. This makes it possible to distinguish +multiple command line arguments from a single argument that contains +spaces. + +Issue found by Matthieu Barjole and Victor Cutillas of Synacktiv +(https://synacktiv.com). +--- + doc/sudoers.man.in | 33 +++++++-- + doc/sudoers.mdoc.in | 28 ++++++- + doc/sudoreplay.man.in | 9 ++ + doc/sudoreplay.mdoc.in | 10 ++ + include/sudo_compat.h | 6 + + include/sudo_lbuf.h | 7 + + lib/util/lbuf.c | 106 ++++++++++++++++++++++++++++++ + lib/util/util.exp.in | 1 + plugins/sudoers/logging.c | 151 +++++++++++-------------------------------- + plugins/sudoers/sudoreplay.c | 44 +++++++++--- + 10 files changed, 261 insertions(+), 134 deletions(-) + +--- a/doc/sudoers.man.in ++++ b/doc/sudoers.man.in +@@ -4273,6 +4273,19 @@ can log events using either + syslog(3) + or a simple log file. + The log format is almost identical in both cases. ++Any control characters present in the log data are formatted in octal ++with a leading ++\(oq#\(cq ++character. ++For example, a horizontal tab is stored as ++\(oq#011\(cq ++and an embedded carriage return is stored as ++\(oq#015\(cq. ++In addition, space characters in the command path are stored as ++\(oq#040\(cq. ++Literal single quotes and backslash characters ++(\(oq\e\(cq) ++in command line arguments are escaped with a backslash. + .SS "Accepted command log entries" + Commands that sudo runs are logged using the following format (split + into multiple lines for readability): +@@ -4353,7 +4366,7 @@ A list of environment variables specifie + if specified. + .TP 14n + command +-The actual command that was executed. ++The actual command that was executed, including any command line arguments. + .PP + Messages are logged using the locale specified by + \fIsudoers_locale\fR, +@@ -4589,17 +4602,21 @@ with a few important differences: + 1.\& + The + \fIprogname\fR +-and +-\fIhostname\fR +-fields are not present. ++field is not present. + .TP 5n + 2.\& +-If the +-\fIlog_year\fR +-option is enabled, +-the date will also include the year. ++The ++\fIhostname\fR ++is only logged if the ++\fIlog_host\fR ++option is enabled. + .TP 5n + 3.\& ++The date does not include the year unless the ++\fIlog_year\fR ++option is enabled. ++.TP 5n ++4.\& + Lines that are longer than + \fIloglinelen\fR + characters (80 by default) are word-wrapped and continued on the +--- a/doc/sudoers.mdoc.in ++++ b/doc/sudoers.mdoc.in +@@ -3977,6 +3977,19 @@ can log events using either + .Xr syslog 3 + or a simple log file. + The log format is almost identical in both cases. ++Any control characters present in the log data are formatted in octal ++with a leading ++.Ql # ++character. ++For example, a horizontal tab is stored as ++.Ql #011 ++and an embedded carriage return is stored as ++.Ql #015 . ++In addition, space characters in the command path are stored as ++.Ql #040 . ++Literal single quotes and backslash characters ++.Pq Ql \e ++in command line arguments are escaped with a backslash. + .Ss Accepted command log entries + Commands that sudo runs are logged using the following format (split + into multiple lines for readability): +@@ -4044,7 +4057,7 @@ option is enabled. + A list of environment variables specified on the command line, + if specified. + .It command +-The actual command that was executed. ++The actual command that was executed, including any command line arguments. + .El + .Pp + Messages are logged using the locale specified by +@@ -4266,14 +4279,17 @@ with a few important differences: + .It + The + .Em progname +-and ++field is not present. ++.It ++The + .Em hostname +-fields are not present. ++is only logged if the ++.Em log_host ++option is enabled. + .It +-If the ++The date does not include the year unless the + .Em log_year +-option is enabled, +-the date will also include the year. ++option is enabled. + .It + Lines that are longer than + .Em loglinelen +--- a/doc/sudoreplay.man.in ++++ b/doc/sudoreplay.man.in +@@ -140,6 +140,15 @@ In this mode, + will list available sessions in a format similar to the + \fBsudo\fR + log file format, sorted by file name (or sequence number). ++Any control characters present in the log data are formated in octal ++with a leading ++\(oq#\(cq ++character. ++For example, a horizontal tab is displayed as ++\(oq#011\(cq ++and an embedded carriage return is displayed as ++\(oq#015\(cq. ++.sp + If a + \fIsearch expression\fR + is specified, it will be used to restrict the IDs that are displayed. +--- a/doc/sudoreplay.mdoc.in ++++ b/doc/sudoreplay.mdoc.in +@@ -132,6 +132,16 @@ In this mode, + will list available sessions in a format similar to the + .Nm sudo + log file format, sorted by file name (or sequence number). ++Any control characters present in the log data are formatted in octal ++with a leading ++.Ql # ++character. ++For example, a horizontal tab is displayed as ++.Ql #011 ++and an embedded carriage return is displayed as ++.Ql #015 . ++Space characters in the command name and arguments are also formatted in octal. ++.Pp + If a + .Ar search expression + is specified, it will be used to restrict the IDs that are displayed. +--- a/include/sudo_compat.h ++++ b/include/sudo_compat.h +@@ -69,6 +69,12 @@ + # endif + #endif + ++#ifdef HAVE_FALLTHROUGH_ATTRIBUTE ++# define FALLTHROUGH __attribute__((__fallthrough__)) ++#else ++# define FALLTHROUGH do { } while (0) ++#endif ++ + /* + * Given the pointer x to the member m of the struct s, return + * a pointer to the containing structure. +--- a/include/sudo_lbuf.h ++++ b/include/sudo_lbuf.h +@@ -36,9 +36,15 @@ struct sudo_lbuf { + + typedef int (*sudo_lbuf_output_t)(const char *); + ++/* Flags for sudo_lbuf_append_esc() */ ++#define LBUF_ESC_CNTRL 0x01 ++#define LBUF_ESC_BLANK 0x02 ++#define LBUF_ESC_QUOTE 0x04 ++ + __dso_public void sudo_lbuf_init_v1(struct sudo_lbuf *lbuf, sudo_lbuf_output_t output, int indent, const char *continuation, int cols); + __dso_public void sudo_lbuf_destroy_v1(struct sudo_lbuf *lbuf); + __dso_public bool sudo_lbuf_append_v1(struct sudo_lbuf *lbuf, const char *fmt, ...) __printflike(2, 3); ++__dso_public bool sudo_lbuf_append_esc_v1(struct sudo_lbuf *lbuf, int flags, const char *fmt, ...) __printflike(3, 4); + __dso_public bool sudo_lbuf_append_quoted_v1(struct sudo_lbuf *lbuf, const char *set, const char *fmt, ...) __printflike(3, 4); + __dso_public void sudo_lbuf_print_v1(struct sudo_lbuf *lbuf); + __dso_public bool sudo_lbuf_error_v1(struct sudo_lbuf *lbuf); +@@ -47,6 +53,7 @@ __dso_public void sudo_lbuf_clearerr_v1( + #define sudo_lbuf_init(_a, _b, _c, _d, _e) sudo_lbuf_init_v1((_a), (_b), (_c), (_d), (_e)) + #define sudo_lbuf_destroy(_a) sudo_lbuf_destroy_v1((_a)) + #define sudo_lbuf_append sudo_lbuf_append_v1 ++#define sudo_lbuf_append_esc sudo_lbuf_append_esc_v1 + #define sudo_lbuf_append_quoted sudo_lbuf_append_quoted_v1 + #define sudo_lbuf_print(_a) sudo_lbuf_print_v1((_a)) + #define sudo_lbuf_error(_a) sudo_lbuf_error_v1((_a)) +--- a/lib/util/lbuf.c ++++ b/lib/util/lbuf.c +@@ -88,6 +88,112 @@ sudo_lbuf_expand(struct sudo_lbuf *lbuf, + } + + /* ++ * Escape a character in octal form (#0n) and store it as a string ++ * in buf, which must have at least 6 bytes available. ++ * Returns the length of buf, not counting the terminating NUL byte. ++ */ ++static int ++escape(unsigned char ch, char *buf) ++{ ++ const int len = ch < 0100 ? (ch < 010 ? 3 : 4) : 5; ++ ++ /* Work backwards from the least significant digit to most significant. */ ++ switch (len) { ++ case 5: ++ buf[4] = (ch & 7) + '0'; ++ ch >>= 3; ++ FALLTHROUGH; ++ case 4: ++ buf[3] = (ch & 7) + '0'; ++ ch >>= 3; ++ FALLTHROUGH; ++ case 3: ++ buf[2] = (ch & 7) + '0'; ++ buf[1] = '0'; ++ buf[0] = '#'; ++ break; ++ } ++ buf[len] = '\0'; ++ ++ return len; ++} ++ ++/* ++ * Parse the format and append strings, only %s and %% escapes are supported. ++ * Any non-printable characters are escaped in octal as #0nn. ++ */ ++bool ++sudo_lbuf_append_esc_v1(struct sudo_lbuf *lbuf, int flags, const char *fmt, ...) ++{ ++ unsigned int saved_len = lbuf->len; ++ bool ret = false; ++ const char *s; ++ va_list ap; ++ debug_decl(sudo_lbuf_append_esc, SUDO_DEBUG_UTIL); ++ ++ if (sudo_lbuf_error(lbuf)) ++ debug_return_bool(false); ++ ++#define should_escape(ch) \ ++ ((ISSET(flags, LBUF_ESC_CNTRL) && iscntrl((unsigned char)ch)) || \ ++ (ISSET(flags, LBUF_ESC_BLANK) && isblank((unsigned char)ch))) ++#define should_quote(ch) \ ++ (ISSET(flags, LBUF_ESC_QUOTE) && (ch == '\'' || ch == '\\')) ++ ++ va_start(ap, fmt); ++ while (*fmt != '\0') { ++ if (fmt[0] == '%' && fmt[1] == 's') { ++ if ((s = va_arg(ap, char *)) == NULL) ++ s = "(NULL)"; ++ while (*s != '\0') { ++ if (should_escape(*s)) { ++ if (!sudo_lbuf_expand(lbuf, sizeof("#0177") - 1)) ++ goto done; ++ lbuf->len += escape(*s++, lbuf->buf + lbuf->len); ++ continue; ++ } ++ if (should_quote(*s)) { ++ if (!sudo_lbuf_expand(lbuf, 2)) ++ goto done; ++ lbuf->buf[lbuf->len++] = '\\'; ++ lbuf->buf[lbuf->len++] = *s++; ++ continue; ++ } ++ if (!sudo_lbuf_expand(lbuf, 1)) ++ goto done; ++ lbuf->buf[lbuf->len++] = *s++; ++ } ++ fmt += 2; ++ continue; ++ } ++ if (should_escape(*fmt)) { ++ if (!sudo_lbuf_expand(lbuf, sizeof("#0177") - 1)) ++ goto done; ++ if (*fmt == '\'') { ++ lbuf->buf[lbuf->len++] = '\\'; ++ lbuf->buf[lbuf->len++] = *fmt++; ++ } else { ++ lbuf->len += escape(*fmt++, lbuf->buf + lbuf->len); ++ } ++ continue; ++ } ++ if (!sudo_lbuf_expand(lbuf, 1)) ++ goto done; ++ lbuf->buf[lbuf->len++] = *fmt++; ++ } ++ ret = true; ++ ++done: ++ if (!ret) ++ lbuf->len = saved_len; ++ if (lbuf->size != 0) ++ lbuf->buf[lbuf->len] = '\0'; ++ va_end(ap); ++ ++ debug_return_bool(ret); ++} ++ ++/* + * Parse the format and append strings, only %s and %% escapes are supported. + * Any characters in set are quoted with a backslash. + */ +--- a/lib/util/util.exp.in ++++ b/lib/util/util.exp.in +@@ -64,6 +64,7 @@ sudo_get_ttysize_v1 + sudo_gethostname_v1 + sudo_gettime_mono_v1 + sudo_gettime_real_v1 ++sudo_lbuf_append_esc_v1 + sudo_lbuf_append_quoted_v1 + sudo_lbuf_append_v1 + sudo_lbuf_clearerr_v1 +--- a/plugins/sudoers/logging.c ++++ b/plugins/sudoers/logging.c +@@ -49,6 +49,7 @@ + #include + + #include "sudoers.h" ++#include "sudo_lbuf.h" + + /* Special message for log_warning() so we know to use ngettext() */ + #define INCORRECT_PASSWORD_ATTEMPT ((char *)0x01) +@@ -780,14 +781,6 @@ should_mail(int status) + (def_mail_no_perms && !ISSET(status, VALIDATE_SUCCESS))); + } + +-#define LL_TTY_STR "TTY=" +-#define LL_CWD_STR "PWD=" /* XXX - should be CWD= */ +-#define LL_USER_STR "USER=" +-#define LL_GROUP_STR "GROUP=" +-#define LL_ENV_STR "ENV=" +-#define LL_CMND_STR "COMMAND=" +-#define LL_TSID_STR "TSID=" +- + #define IS_SESSID(s) ( \ + isalnum((unsigned char)(s)[0]) && isalnum((unsigned char)(s)[1]) && \ + (s)[2] == '/' && \ +@@ -802,14 +795,21 @@ should_mail(int status) + static char * + new_logline(const char *message, int serrno) + { +- char *line = NULL, *errstr = NULL, *evstr = NULL; ++ char *errstr = NULL; + #ifndef SUDOERS_NO_SEQ + char sessid[7]; + #endif + const char *tsid = NULL; +- size_t len = 0; ++ struct sudo_lbuf lbuf; ++ int i; + debug_decl(new_logline, SUDOERS_DEBUG_LOGGING) + ++ sudo_lbuf_init(&lbuf, NULL, 0, NULL, 0); ++ ++ if (serrno) { ++ errstr = strerror(serrno); ++ } ++ + #ifndef SUDOERS_NO_SEQ + /* A TSID may be a sudoers-style session ID or a free-form string. */ + if (sudo_user.iolog_file != NULL) { +@@ -829,121 +829,54 @@ new_logline(const char *message, int ser + #endif + + /* +- * Compute line length ++ * Format the log line as an lbuf, escaping control characters in ++ * octal form (#0nn). Error checking (ENOMEM) is done at the end. + */ +- if (message != NULL) +- len += strlen(message) + 3; +- if (serrno) { +- errstr = strerror(serrno); +- len += strlen(errstr) + 3; ++ if (message != NULL) { ++ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "%s%s", message, ++ errstr ? " : " : " ; "); + } +- len += sizeof(LL_TTY_STR) + 2 + strlen(user_tty); +- len += sizeof(LL_CWD_STR) + 2 + strlen(user_cwd); +- if (runas_pw != NULL) +- len += sizeof(LL_USER_STR) + 2 + strlen(runas_pw->pw_name); +- if (runas_gr != NULL) +- len += sizeof(LL_GROUP_STR) + 2 + strlen(runas_gr->gr_name); +- if (tsid != NULL) +- len += sizeof(LL_TSID_STR) + 2 + strlen(tsid); +- if (sudo_user.env_vars != NULL) { +- size_t evlen = 0; +- char * const *ep; +- +- for (ep = sudo_user.env_vars; *ep != NULL; ep++) +- evlen += strlen(*ep) + 1; +- if (evlen != 0) { +- if ((evstr = malloc(evlen)) == NULL) +- goto oom; +- evstr[0] = '\0'; +- for (ep = sudo_user.env_vars; *ep != NULL; ep++) { +- strlcat(evstr, *ep, evlen); +- strlcat(evstr, " ", evlen); /* NOTE: last one will fail */ +- } +- len += sizeof(LL_ENV_STR) + 2 + evlen; +- } ++ if (serrno) { ++ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "%s ; ", errstr); + } +- if (user_cmnd != NULL) { +- /* Note: we log "sudo -l command arg ..." as "list command arg ..." */ +- len += sizeof(LL_CMND_STR) - 1 + strlen(user_cmnd); +- if (ISSET(sudo_mode, MODE_CHECK)) +- len += sizeof("list ") - 1; +- if (user_args != NULL) +- len += strlen(user_args) + 1; ++ if (user_tty != NULL) { ++ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "TTY=%s ; ", user_tty); + } +- +- /* +- * Allocate and build up the line. +- */ +- if ((line = malloc(++len)) == NULL) +- goto oom; +- line[0] = '\0'; +- +- if (message != NULL) { +- if (strlcat(line, message, len) >= len || +- strlcat(line, errstr ? " : " : " ; ", len) >= len) +- goto toobig; ++ if (user_cwd != NULL) { ++ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "PWD=%s ; ", user_cwd); + } +- if (serrno) { +- if (strlcat(line, errstr, len) >= len || +- strlcat(line, " ; ", len) >= len) +- goto toobig; +- } +- if (strlcat(line, LL_TTY_STR, len) >= len || +- strlcat(line, user_tty, len) >= len || +- strlcat(line, " ; ", len) >= len) +- goto toobig; +- if (strlcat(line, LL_CWD_STR, len) >= len || +- strlcat(line, user_cwd, len) >= len || +- strlcat(line, " ; ", len) >= len) +- goto toobig; + if (runas_pw != NULL) { +- if (strlcat(line, LL_USER_STR, len) >= len || +- strlcat(line, runas_pw->pw_name, len) >= len || +- strlcat(line, " ; ", len) >= len) +- goto toobig; ++ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "USER=%s ; ", ++ runas_pw->pw_name); + } + if (runas_gr != NULL) { +- if (strlcat(line, LL_GROUP_STR, len) >= len || +- strlcat(line, runas_gr->gr_name, len) >= len || +- strlcat(line, " ; ", len) >= len) +- goto toobig; ++ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "GROUP=%s ; ", ++ runas_gr->gr_name); + } + if (tsid != NULL) { +- if (strlcat(line, LL_TSID_STR, len) >= len || +- strlcat(line, tsid, len) >= len || +- strlcat(line, " ; ", len) >= len) +- goto toobig; +- } +- if (evstr != NULL) { +- if (strlcat(line, LL_ENV_STR, len) >= len || +- strlcat(line, evstr, len) >= len || +- strlcat(line, " ; ", len) >= len) +- goto toobig; +- free(evstr); +- evstr = NULL; ++ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "TSID=%s ; ", tsid); ++ } ++ if (sudo_user.env_vars != NULL) { ++ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "ENV=%s", sudo_user.env_vars[0]); ++ for (i = 1; sudo_user.env_vars[i] != NULL; i++) { ++ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, " %s", ++ sudo_user.env_vars[i]); ++ } + } + if (user_cmnd != NULL) { +- if (strlcat(line, LL_CMND_STR, len) >= len) +- goto toobig; +- if (ISSET(sudo_mode, MODE_CHECK) && strlcat(line, "list ", len) >= len) +- goto toobig; +- if (strlcat(line, user_cmnd, len) >= len) +- goto toobig; ++ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL|LBUF_ESC_BLANK, ++ "COMMAND=%s", user_cmnd); + if (user_args != NULL) { +- if (strlcat(line, " ", len) >= len || +- strlcat(line, user_args, len) >= len) +- goto toobig; ++ sudo_lbuf_append_esc(&lbuf, ++ LBUF_ESC_CNTRL|LBUF_ESC_QUOTE, ++ " %s", user_args); + } + } + +- debug_return_str(line); +-oom: +- free(evstr); ++ if (!sudo_lbuf_error(&lbuf)) ++ debug_return_str(lbuf.buf); ++ ++ sudo_lbuf_destroy(&lbuf); + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + debug_return_str(NULL); +-toobig: +- free(evstr); +- free(line); +- sudo_warnx(U_("internal error, %s overflow"), __func__); +- debug_return_str(NULL); + } +--- a/plugins/sudoers/sudoreplay.c ++++ b/plugins/sudoers/sudoreplay.c +@@ -68,6 +68,7 @@ + #include "sudo_conf.h" + #include "sudo_debug.h" + #include "sudo_event.h" ++#include "sudo_lbuf.h" + #include "sudo_util.h" + + #ifdef HAVE_GETOPT_LONG +@@ -1480,7 +1481,8 @@ free_log_info(struct log_info *li) + } + + static int +-list_session(char *logfile, regex_t *re, const char *user, const char *tty) ++list_session(struct sudo_lbuf *lbuf, char *logfile, regex_t *re, ++ const char *user, const char *tty) + { + char idbuf[7], *idstr, *cp; + const char *timestr; +@@ -1513,16 +1515,32 @@ list_session(char *logfile, regex_t *re, + } + /* XXX - print rows + cols? */ + timestr = get_timestr(li->tstamp, 1); +- printf("%s : %s : TTY=%s ; CWD=%s ; USER=%s ; ", +- timestr ? timestr : "invalid date", +- li->user, li->tty, li->cwd, li->runas_user); +- if (li->runas_group) +- printf("GROUP=%s ; ", li->runas_group); +- printf("TSID=%s ; COMMAND=%s\n", idstr, li->cmd); +- +- ret = 0; +- ++ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "%s : %s : ", ++ timestr ? timestr : "invalid date", li->user); ++ if (li->tty != NULL) { ++ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "TTY=%s ; ", ++ li->tty); ++ } ++ if (li->cwd != NULL) { ++ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "CWD=%s ; ", ++ li->cwd); ++ } ++ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "USER=%s ; ", li->runas_user); ++ if (li->runas_group != NULL) { ++ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "GROUP=%s ; ", ++ li->runas_group); ++ } ++ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "TSID=%s ; ", idstr); ++ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "COMMAND=%s", ++ li->cmd); ++ ++ if (!sudo_lbuf_error(lbuf)) { ++ puts(lbuf->buf); ++ ret = 0; ++ } + done: ++ lbuf->error = 0; ++ lbuf->len = 0; + free_log_info(li); + debug_return_int(ret); + } +@@ -1542,6 +1560,7 @@ find_sessions(const char *dir, regex_t * + DIR *d; + struct dirent *dp; + struct stat sb; ++ struct sudo_lbuf lbuf; + size_t sdlen, sessions_len = 0, sessions_size = 0; + unsigned int i; + int len; +@@ -1553,6 +1572,8 @@ find_sessions(const char *dir, regex_t * + #endif + debug_decl(find_sessions, SUDO_DEBUG_UTIL) + ++ sudo_lbuf_init(&lbuf, NULL, 0, NULL, 0); ++ + d = opendir(dir); + if (d == NULL) + sudo_fatal(U_("unable to open %s"), dir); +@@ -1612,7 +1633,7 @@ find_sessions(const char *dir, regex_t * + + /* Check for dir with a log file. */ + if (lstat(pathbuf, &sb) == 0 && S_ISREG(sb.st_mode)) { +- list_session(pathbuf, re, user, tty); ++ list_session(&lbuf, pathbuf, re, user, tty); + } else { + /* Strip off "/log" and recurse if a dir. */ + pathbuf[sdlen + len - 4] = '\0'; +@@ -1623,6 +1644,7 @@ find_sessions(const char *dir, regex_t * + } + free(sessions); + } ++ sudo_lbuf_destroy(&lbuf); + + debug_return_int(0); + } diff -Nru sudo-1.8.21p2/debian/patches/CVE-2023-2848x-2.patch sudo-1.8.21p2/debian/patches/CVE-2023-2848x-2.patch --- sudo-1.8.21p2/debian/patches/CVE-2023-2848x-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/CVE-2023-2848x-2.patch 2023-04-04 12:44:58.000000000 +0000 @@ -0,0 +1,22 @@ +Backport of: + +From 12648b4e0a8cf486480442efd52f0e0b6cab6e8b Mon Sep 17 00:00:00 2001 +From: "Todd C. Miller" +Date: Mon, 13 Mar 2023 08:04:32 -0600 +Subject: [PATCH] Add missing " ; " separator between environment variables and + command. This is a regression introduced in sudo 1.9.13. GitHub issue #254. + +--- + lib/eventlog/eventlog.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/plugins/sudoers/logging.c ++++ b/plugins/sudoers/logging.c +@@ -862,6 +862,7 @@ new_logline(const char *message, int ser + sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, " %s", + sudo_user.env_vars[i]); + } ++ sudo_lbuf_append(&lbuf, " ; "); + } + if (user_cmnd != NULL) { + sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL|LBUF_ESC_BLANK, diff -Nru sudo-1.8.21p2/debian/patches/series sudo-1.8.21p2/debian/patches/series --- sudo-1.8.21p2/debian/patches/series 2020-01-31 17:16:35.000000000 +0000 +++ sudo-1.8.21p2/debian/patches/series 2023-04-04 12:44:51.000000000 +0000 @@ -5,3 +5,15 @@ CVE-2019-14287.patch CVE-2019-14287-2.patch CVE-2019-18634.patch +0001-In-pty_close-close-the-slave-and-remove-any-events-a.patch +CVE-2021-23239.patch +CVE-2021-3156-pre1.patch +CVE-2021-3156-pre2.patch +CVE-2021-3156-1.patch +CVE-2021-3156-2.patch +CVE-2021-3156-3.patch +CVE-2021-3156-4.patch +CVE-2021-3156-5.patch +CVE-2023-22809.patch +CVE-2023-2848x-1.patch +CVE-2023-2848x-2.patch