Compare commits

...

No commits in common. "c8" and "c8s" have entirely different histories.
c8 ... c8s

43 changed files with 3264 additions and 3744 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/sudo-1.9.5p2.tar.gz
SOURCES/sudo-1.8.29.tar.gz

View File

@ -1 +1 @@
08bde247a1e08bc881eec43e09733f7ca06408f5 SOURCES/sudo-1.9.5p2.tar.gz
fdce342856f1803478eb549479190370001dca95 SOURCES/sudo-1.8.29.tar.gz

View File

@ -1,35 +0,0 @@
diff -up ./lib/eventlog/eventlog.c.covscan ./lib/eventlog/eventlog.c
--- ./lib/eventlog/eventlog.c.covscan 2021-08-26 11:06:35.068915415 +0200
+++ ./lib/eventlog/eventlog.c 2021-08-26 11:13:32.432472325 +0200
@@ -1075,10 +1075,13 @@ do_logfile_sudo(const char *logline, con
if (ferror(fp)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"unable to write log file %s", logfile);
- goto done;
+ goto ddone;
}
ret = true;
+ddone:
+ (void)free(full_line);
+
done:
(void)sudo_lock_file(fileno(fp), SUDO_UNLOCK);
evl_conf.close_log(EVLOG_FILE, fp);
diff -up ./logsrvd/logsrvd.c.covscan ./logsrvd/logsrvd.c
diff -up ./plugins/audit_json/audit_json.c.covscan ./plugins/audit_json/audit_json.c
diff -up ./plugins/sudoers/ldap.c.covscan ./plugins/sudoers/ldap.c
--- ./plugins/sudoers/ldap.c.covscan 2021-08-26 15:46:11.614179451 +0200
+++ ./plugins/sudoers/ldap.c 2021-08-26 15:51:40.871812534 +0200
@@ -443,6 +443,8 @@ sudo_ldap_parse_options(LDAP *ld, LDAPMe
goto done;
}
+ free(cp);
+
/* Walk through options, appending to defs. */
for (p = bv; *p != NULL; p++) {
char *var, *val;
diff -up ./plugins/sudoers/logging.c.covscan ./plugins/sudoers/logging.c
diff -up ./plugins/sudoers/rcstr.c.covscan ./plugins/sudoers/rcstr.c
diff -up ./src/utmp.c.covscan ./src/utmp.c

View File

@ -1,79 +0,0 @@
From ecaa9cd08d25870ec89fec82cf17c6cdaa4c7912 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Sat, 6 Feb 2021 08:36:01 -0700
Subject: [PATCH] Add libsudo_eventlog.la as a dependency of libsudo_iolog.la
No longer need to link against libsudo_eventlog.la in sudoers.
---
lib/iolog/Makefile.in | 7 ++++---
plugins/sudoers/Makefile.in | 8 ++++----
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/lib/iolog/Makefile.in b/lib/iolog/Makefile.in
index 2ae9c1b42..8fbc76c98 100644
--- a/lib/iolog/Makefile.in
+++ b/lib/iolog/Makefile.in
@@ -1,7 +1,7 @@
#
# SPDX-License-Identifier: ISC
#
-# Copyright (c) 2011-2020 Todd C. Miller <Todd.Miller@sudo.ws>
+# Copyright (c) 2011-2021 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
@@ -36,7 +36,8 @@ CC = @CC@
LIBTOOL = @LIBTOOL@
# Libraries
-LT_LIBS = $(top_builddir)/lib/util/libsudo_util.la
+LT_LIBS = $(top_builddir)/lib/eventlog/libsudo_eventlog.la \
+ $(top_builddir)/lib/util/libsudo_util.la
LIBS = @LIBS@ @ZLIB@ $(LT_LIBS)
# C preprocessor flags
@@ -76,7 +77,7 @@ PVS_LOG_OPTS = -a 'GA:1,2' -e -t errorfile -d $(PVS_IGNORE)
# Regression tests
TEST_PROGS = check_iolog_json check_iolog_mkpath check_iolog_path check_iolog_util host_port_test
-TEST_LIBS = @LIBS@ $(top_builddir)/lib/eventlog/libsudo_eventlog.la
+TEST_LIBS = @LIBS@
TEST_LDFLAGS = @LDFLAGS@
# Set to non-empty for development mode
diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in
index 77d54553b..8eadf479d 100644
--- a/plugins/sudoers/Makefile.in
+++ b/plugins/sudoers/Makefile.in
@@ -1,7 +1,7 @@
#
# SPDX-License-Identifier: ISC
#
-# Copyright (c) 1996, 1998-2005, 2007-2020
+# Copyright (c) 1996, 1998-2005, 2007-2021
# Todd C. Miller <Todd.Miller@sudo.ws>
#
# Permission to use, copy, modify, and distribute this software for any
@@ -61,8 +61,8 @@ LIBLOGSRV = @LIBLOGSRV@
LIBUTIL = $(top_builddir)/lib/util/libsudo_util.la
LIBS = $(LIBUTIL)
NET_LIBS = @NET_LIBS@
-SUDOERS_LIBS = @SUDOERS_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ @LIBTLS@ $(NET_LIBS) $(LIBIOLOG) $(LIBEVENTLOG) $(LIBLOGSRV)
-REPLAY_LIBS = @REPLAY_LIBS@ $(LIBEVENTLOG) $(LIBIOLOG)
+SUDOERS_LIBS = @SUDOERS_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ @LIBTLS@ $(NET_LIBS) $(LIBIOLOG) $(LIBLOGSRV)
+REPLAY_LIBS = @REPLAY_LIBS@ $(LIBIOLOG)
VISUDO_LIBS = $(NET_LIBS)
CVTSUDOERS_LIBS = $(NET_LIBS)
TESTSUDOERS_LIBS = $(NET_LIBS)
@@ -323,7 +323,7 @@ check_hexchar: $(CHECK_HEXCHAR_OBJS) $(LIBUTIL)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_HEXCHAR_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS)
check_iolog_plugin: $(CHECK_IOLOG_PLUGIN_OBJS) $(LIBUTIL) $(LIBIOLOG) $(LIBLOGSRV)
- $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_PLUGIN_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBIOLOG) $(LIBEVENTLOG) $(LIBLOGSRV) @LIBTLS@
+ $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_PLUGIN_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBIOLOG) $(LIBLOGSRV) @LIBTLS@
check_starttime: $(CHECK_STARTTIME_OBJS) $(LIBUTIL)
$(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_STARTTIME_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS)
--
2.43.0

View File

@ -0,0 +1,11 @@
--- sudo-1.6.7p5/install-sh.strip 2005-07-21 14:28:25.000000000 +0200
+++ sudo-1.6.7p5/install-sh 2005-07-21 14:29:18.000000000 +0200
@@ -138,7 +138,7 @@
fi
;;
X-s)
- STRIPIT=true
+ #STRIPIT=true
;;
X--)
shift

View File

@ -0,0 +1,27 @@
From 44a602b49365969e56c63c9f12eda197e951302f Mon Sep 17 00:00:00 2001
From: Tomas Sykora <tosykora@redhat.com>
Date: Fri, 19 Aug 2016 14:07:35 +0200
Subject: [PATCH 02/10] Added "Enviroment debugging" message
rebased from:
Patch2: sudo-1.7.2p1-envdebug.patch
---
configure.ac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index 9feddfd..39a2d86 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1390,7 +1390,7 @@ AC_ARG_ENABLE(env_debug,
[AS_HELP_STRING([--enable-env-debug], [Whether to enable environment debugging.])],
[ case "$enableval" in
yes) AC_MSG_RESULT(yes)
- AC_DEFINE(ENV_DEBUG)
+ AC_DEFINE(ENV_DEBUG, [], [Environment debugging.])
;;
no) AC_MSG_RESULT(no)
;;
--
2.7.4

View File

@ -0,0 +1,89 @@
diff -up ./plugins/sudoers/cvtsudoers.c.legacy-processing ./plugins/sudoers/cvtsudoers.c
--- ./plugins/sudoers/cvtsudoers.c.legacy-processing 2019-10-28 13:28:52.000000000 +0100
+++ ./plugins/sudoers/cvtsudoers.c 2019-10-30 13:32:43.309480623 +0100
@@ -347,6 +347,15 @@ main(int argc, char *argv[])
sudo_fatalx("error: unhandled input %d", input_format);
}
+ /*
+ * cvtsudoers group filtering doesn't work if def_match_group_by_gid
+ * is set to true by default (at compile-time). It cannot be set to false
+ * because cvtsudoers doesn't apply the parsed Defaults.
+ *
+ * Related: sudo-1.8.23-legacy-group-processing.patch
+ */
+ def_match_group_by_gid = def_legacy_group_processing = false;
+
/* Apply filters. */
filter_userspecs(&parsed_policy, conf);
filter_defaults(&parsed_policy, conf);
diff -up ./plugins/sudoers/defaults.c.legacy-processing ./plugins/sudoers/defaults.c
--- ./plugins/sudoers/defaults.c.legacy-processing 2019-10-28 13:28:52.000000000 +0100
+++ ./plugins/sudoers/defaults.c 2019-10-30 13:32:43.309480623 +0100
@@ -93,6 +93,7 @@ static struct early_default early_defaul
{ I_FQDN },
#endif
{ I_MATCH_GROUP_BY_GID },
+ { I_LEGACY_GROUP_PROCESSING },
{ I_GROUP_PLUGIN },
{ I_RUNAS_DEFAULT },
{ I_SUDOERS_LOCALE },
@@ -494,6 +495,8 @@ init_defaults(void)
}
/* First initialize the flags. */
+ def_legacy_group_processing = true;
+ def_match_group_by_gid = true;
#ifdef LONG_OTP_PROMPT
def_long_otp_prompt = true;
#endif
diff -up ./plugins/sudoers/def_data.c.legacy-processing ./plugins/sudoers/def_data.c
--- ./plugins/sudoers/def_data.c.legacy-processing 2019-10-30 13:32:43.309480623 +0100
+++ ./plugins/sudoers/def_data.c 2019-10-30 13:37:25.914602825 +0100
@@ -506,6 +506,10 @@ struct sudo_defs_types sudo_defs_table[]
N_("Log when a command is denied by sudoers"),
NULL,
}, {
+ "legacy_group_processing", T_FLAG,
+ N_("Don't pre-resolve all group names"),
+ NULL,
+ }, {
NULL, 0, NULL
}
};
diff -up ./plugins/sudoers/def_data.h.legacy-processing ./plugins/sudoers/def_data.h
--- ./plugins/sudoers/def_data.h.legacy-processing 2019-10-30 13:32:43.310480638 +0100
+++ ./plugins/sudoers/def_data.h 2019-10-30 13:40:59.651713757 +0100
@@ -232,6 +232,8 @@
#define def_log_allowed (sudo_defs_table[I_LOG_ALLOWED].sd_un.flag)
#define I_LOG_DENIED 116
#define def_log_denied (sudo_defs_table[I_LOG_DENIED].sd_un.flag)
+#define I_LEGACY_GROUP_PROCESSING 117
+#define def_legacy_group_processing (sudo_defs_table[I_LEGACY_GROUP_PROCESSING].sd_un.flag)
enum def_tuple {
never,
diff -up ./plugins/sudoers/def_data.in.legacy-processing ./plugins/sudoers/def_data.in
--- ./plugins/sudoers/def_data.in.legacy-processing 2019-10-30 13:32:43.310480638 +0100
+++ ./plugins/sudoers/def_data.in 2019-10-30 13:42:20.915896239 +0100
@@ -366,3 +366,6 @@ log_allowed
log_denied
T_FLAG
"Log when a command is denied by sudoers"
+legacy_group_processing
+ T_FLAG
+ "Don't pre-resolve all group names"
diff -up ./plugins/sudoers/sudoers.c.legacy-processing ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.legacy-processing 2019-10-28 13:28:53.000000000 +0100
+++ ./plugins/sudoers/sudoers.c 2019-10-30 13:32:43.310480638 +0100
@@ -221,6 +221,10 @@ sudoers_policy_init(void *info, char * c
if (set_loginclass(runas_pw ? runas_pw : sudo_user.pw))
ret = true;
+ if (!def_match_group_by_gid || !def_legacy_group_processing) {
+ def_match_group_by_gid = false;
+ def_legacy_group_processing = false;
+ }
cleanup:
if (!restore_perms())
ret = -1;

View File

@ -0,0 +1,60 @@
diff -up ./plugins/sudoers/def_data.c.nowait ./plugins/sudoers/def_data.c
--- ./plugins/sudoers/def_data.c.nowait 2019-10-30 13:43:48.376168944 +0100
+++ ./plugins/sudoers/def_data.c 2019-10-30 13:43:48.378168973 +0100
@@ -510,6 +510,10 @@ struct sudo_defs_types sudo_defs_table[]
N_("Don't pre-resolve all group names"),
NULL,
}, {
+ "cmnd_no_wait", T_FLAG,
+ N_("Don't fork and wait for the command to finish, just exec it"),
+ NULL,
+ }, {
NULL, 0, NULL
}
};
diff -up ./plugins/sudoers/def_data.h.nowait ./plugins/sudoers/def_data.h
--- ./plugins/sudoers/def_data.h.nowait 2019-10-30 13:43:48.378168973 +0100
+++ ./plugins/sudoers/def_data.h 2019-10-30 13:45:38.425770365 +0100
@@ -234,6 +234,8 @@
#define def_log_denied (sudo_defs_table[I_LOG_DENIED].sd_un.flag)
#define I_LEGACY_GROUP_PROCESSING 117
#define def_legacy_group_processing (sudo_defs_table[I_LEGACY_GROUP_PROCESSING].sd_un.flag)
+#define I_CMND_NO_WAIT 118
+#define def_cmnd_no_wait (sudo_defs_table[I_CMND_NO_WAIT].sd_un.flag)
enum def_tuple {
never,
diff -up ./plugins/sudoers/def_data.in.nowait ./plugins/sudoers/def_data.in
--- ./plugins/sudoers/def_data.in.nowait 2019-10-30 13:43:48.376168944 +0100
+++ ./plugins/sudoers/def_data.in 2019-10-30 13:43:48.379168987 +0100
@@ -369,3 +369,6 @@ log_denied
legacy_group_processing
T_FLAG
"Don't pre-resolve all group names"
+cmnd_no_wait
+ T_FLAG
+ "Don't fork and wait for the command to finish, just exec it"
diff -up ./plugins/sudoers/sudoers.c.nowait ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.nowait 2019-10-30 13:43:48.376168944 +0100
+++ ./plugins/sudoers/sudoers.c 2019-10-30 13:43:48.379168987 +0100
@@ -225,6 +225,20 @@ sudoers_policy_init(void *info, char * c
def_match_group_by_gid = false;
def_legacy_group_processing = false;
}
+
+ /*
+ * Emulate cmnd_no_wait option by disabling PAM session, PTY allocation
+ * and I/O logging. This will cause sudo to execute the given command
+ * directly instead of forking a separate process for it.
+ */
+ if (def_cmnd_no_wait) {
+ def_pam_setcred = false;
+ def_pam_session = false;
+ def_use_pty = false;
+ def_log_input = false;
+ def_log_output = false;
+ }
+
cleanup:
if (!restore_perms())
ret = -1;

View File

@ -0,0 +1,32 @@
diff -up sudo-1.8.23/doc/Makefile.in.sudoldapconfman sudo-1.8.23/doc/Makefile.in
--- sudo-1.8.23/doc/Makefile.in.sudoldapconfman 2018-04-29 21:59:31.000000000 +0200
+++ sudo-1.8.23/doc/Makefile.in 2018-05-17 13:56:24.693651178 +0200
@@ -345,10 +345,16 @@ install-doc: install-dirs
rm -f $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu)$(MANCOMPRESSEXT); \
echo ln -s sudo.$(mansectsu)$(MANCOMPRESSEXT) $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu)$(MANCOMPRESSEXT); \
ln -s sudo.$(mansectsu)$(MANCOMPRESSEXT) $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu)$(MANCOMPRESSEXT); \
+ rm -f $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform)$(MANCOMPRESSEXT); \
+ echo ln -s sudoers.ldap.$(mansectform)$(MANCOMPRESSEXT) $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform)$(MANCOMPRESSEXT); \
+ ln -s sudoers.ldap.$(mansectform)$(MANCOMPRESSEXT) $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform)$(MANCOMPRESSEXT); \
else \
rm -f $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu); \
echo ln -s sudo.$(mansectsu) $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu); \
ln -s sudo.$(mansectsu) $(DESTDIR)$(mandirsu)/sudoedit.$(mansectsu); \
+ rm -f $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform); \
+ echo ln -s sudoers.ldap.$(mansectform) $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform); \
+ ln -s sudoers.ldap.$(mansectform) $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform); \
fi
install-plugin:
@@ -363,8 +369,9 @@ uninstall:
$(DESTDIR)$(mandirsu)/visudo.$(mansectsu) \
$(DESTDIR)$(mandirform)/sudo.conf.$(mansectform) \
$(DESTDIR)$(mandirform)/sudoers.$(mansectform) \
- $(DESTDIR)$(mandirform)/sudoers_timestamp.$(mansectform)
- $(DESTDIR)$(mandirform)/sudoers.ldap.$(mansectform)
+ $(DESTDIR)$(mandirform)/sudoers_timestamp.$(mansectform) \
+ $(DESTDIR)$(mandirform)/sudoers.ldap.$(mansectform) \
+ $(DESTDIR)$(mandirform)/sudo-ldap.conf.$(mansectform)
splint:

View File

@ -0,0 +1,77 @@
diff -up ./src/tgetpass.c.CVE-2019-18634 ./src/tgetpass.c
--- ./src/tgetpass.c.CVE-2019-18634 2019-10-28 13:27:39.000000000 +0100
+++ ./src/tgetpass.c 2020-02-05 14:08:27.516101197 +0100
@@ -61,7 +61,7 @@ enum tgetpass_errval {
static volatile sig_atomic_t signo[NSIG];
static void tgetpass_handler(int);
-static char *getln(int, char *, size_t, int, enum tgetpass_errval *);
+static char *getln(int, char *, size_t, bool, enum tgetpass_errval *);
static char *sudo_askpass(const char *, const char *);
static int
@@ -125,6 +125,7 @@ tgetpass(const char *prompt, int timeout
static char buf[SUDO_CONV_REPL_MAX + 1];
int i, input, output, save_errno, ttyfd;
bool need_restart, neednl = false;
+ bool feedback = ISSET(flags, TGP_MASK);
enum tgetpass_errval errval;
debug_decl(tgetpass, SUDO_DEBUG_CONV)
@@ -180,7 +181,7 @@ restart:
*/
if (!ISSET(flags, TGP_ECHO)) {
for (;;) {
- if (ISSET(flags, TGP_MASK))
+ if (feedback)
neednl = sudo_term_cbreak(input);
else
neednl = sudo_term_noecho(input);
@@ -194,6 +195,9 @@ restart:
}
}
}
+ /* Only use feedback mode when we can disable echo. */
+ if (!neednl)
+ feedback = false;
/*
* Catch signals that would otherwise cause the user to end
@@ -224,7 +228,7 @@ restart:
if (timeout > 0)
alarm(timeout);
- pass = getln(input, buf, sizeof(buf), ISSET(flags, TGP_MASK), &errval);
+ pass = getln(input, buf, sizeof(buf), feedback, &errval);
alarm(0);
save_errno = errno;
@@ -360,7 +364,7 @@ sudo_askpass(const char *askpass, const
extern int sudo_term_eof, sudo_term_erase, sudo_term_kill;
static char *
-getln(int fd, char *buf, size_t bufsiz, int feedback,
+getln(int fd, char *buf, size_t bufsiz, bool feedback,
enum tgetpass_errval *errval)
{
size_t left = bufsiz;
@@ -389,15 +393,15 @@ getln(int fd, char *buf, size_t bufsiz,
while (cp > buf) {
if (write(fd, "\b \b", 3) == -1)
break;
- --cp;
+ cp--;
}
+ cp = buf;
left = bufsiz;
continue;
} else if (c == sudo_term_erase) {
if (cp > buf) {
- if (write(fd, "\b \b", 3) == -1)
- break;
- --cp;
+ ignore_result(write(fd, "\b \b", 3));
+ cp--;
left++;
}
continue;

View File

@ -0,0 +1,169 @@
diff -up ./doc/sudoers.man.in.CVE-2019-19232 ./doc/sudoers.man.in
--- ./doc/sudoers.man.in.CVE-2019-19232 2019-10-28 13:28:52.000000000 +0100
+++ ./doc/sudoers.man.in 2020-01-14 15:34:46.908027286 +0100
@@ -2942,6 +2942,23 @@ This flag is
\fIoff\fR
by default.
.TP 18n
+runas_allow_unknown_id
+If enabled, allow matching of runas user and group IDs that are
+not present in the password or group databases.
+In addition to explicitly matching unknown user or group IDs in a
+\fRRunas_List\fR,
+this option also allows the
+\fBALL\fR
+alias to match unknown IDs.
+This flag is
+\fIoff\fR
+by default.
+.sp
+This setting is only supported by version 1.8.29 or higher.
+Older versions of
+\fBsudo\fR
+always allowed matching of unknown user and group IDs.
+.TP 18n
runaspw
If set,
\fBsudo\fR
diff -up ./doc/sudoers.mdoc.in.CVE-2019-19232 ./doc/sudoers.mdoc.in
--- ./doc/sudoers.mdoc.in.CVE-2019-19232 2019-10-28 13:28:52.000000000 +0100
+++ ./doc/sudoers.mdoc.in 2020-01-14 15:34:46.908027286 +0100
@@ -2768,6 +2768,22 @@ when running a command or editing a file
This flag is
.Em off
by default.
+.It runas_allow_unknown_id
+If enabled, allow matching of runas user and group IDs that are
+not present in the password or group databases.
+In addition to explicitly matching unknown user or group IDs in a
+.Li Runas_List ,
+this option also allows the
+.Sy ALL
+alias to match unknown IDs.
+This flag is
+.Em off
+by default.
+.Pp
+This setting is only supported by version 1.8.29 or higher.
+Older versions of
+.Nm sudo
+always allowed matching of unknown user and group IDs.
.It runaspw
If set,
.Nm sudo
diff -up ./plugins/sudoers/defaults.c.CVE-2019-19232 ./plugins/sudoers/defaults.c
--- ./plugins/sudoers/defaults.c.CVE-2019-19232 2020-01-14 15:34:46.902027246 +0100
+++ ./plugins/sudoers/defaults.c 2020-01-14 15:34:46.909027293 +0100
@@ -581,6 +581,7 @@ init_defaults(void)
def_fdexec = digest_only;
def_log_allowed = true;
def_log_denied = true;
+ def_runas_allow_unknown_id = false;
/* Syslog options need special care since they both strings and ints */
#if (LOGGING & SLOG_SYSLOG)
diff -up ./plugins/sudoers/def_data.c.CVE-2019-19232 ./plugins/sudoers/def_data.c
--- ./plugins/sudoers/def_data.c.CVE-2019-19232 2020-01-14 15:34:46.908027286 +0100
+++ ./plugins/sudoers/def_data.c 2020-01-14 15:40:19.441555509 +0100
@@ -514,6 +514,10 @@ struct sudo_defs_types sudo_defs_table[]
N_("Don't fork and wait for the command to finish, just exec it"),
NULL,
}, {
+ "runas_allow_unknown_id", T_FLAG,
+ N_("Allow the use of unknown runas user and/or group ID"),
+ NULL,
+ }, {
NULL, 0, NULL
}
};
diff -up ./plugins/sudoers/def_data.h.CVE-2019-19232 ./plugins/sudoers/def_data.h
--- ./plugins/sudoers/def_data.h.CVE-2019-19232 2020-01-14 15:34:46.909027293 +0100
+++ ./plugins/sudoers/def_data.h 2020-01-14 15:41:33.658012401 +0100
@@ -236,6 +236,8 @@
#define def_legacy_group_processing (sudo_defs_table[I_LEGACY_GROUP_PROCESSING].sd_un.flag)
#define I_CMND_NO_WAIT 118
#define def_cmnd_no_wait (sudo_defs_table[I_CMND_NO_WAIT].sd_un.flag)
+#define I_RUNAS_ALLOW_UNKNOWN_ID 119
+#define def_runas_allow_unknown_id (sudo_defs_table[I_RUNAS_ALLOW_UNKNOWN_ID].sd_un.flag)
enum def_tuple {
never,
diff -up ./plugins/sudoers/def_data.in.CVE-2019-19232 ./plugins/sudoers/def_data.in
--- ./plugins/sudoers/def_data.in.CVE-2019-19232 2020-01-14 15:34:46.909027293 +0100
+++ ./plugins/sudoers/def_data.in 2020-01-14 15:42:42.176481484 +0100
@@ -372,3 +372,6 @@ legacy_group_processing
cmnd_no_wait
T_FLAG
"Don't fork and wait for the command to finish, just exec it"
+runas_allow_unknown_id
+ T_FLAG
+ "Allow the use of unknown runas user and/or group ID"
diff -up ./plugins/sudoers/sudoers.c.CVE-2019-19232 ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.CVE-2019-19232 2020-01-14 15:34:46.905027266 +0100
+++ ./plugins/sudoers/sudoers.c 2020-01-14 15:34:46.910027299 +0100
@@ -105,6 +105,8 @@ static char *prev_user;
static char *runas_user;
static char *runas_group;
static struct sudo_nss_list *snl;
+static bool unknown_runas_uid;
+static bool unknown_runas_gid;
#ifdef __linux__
static struct rlimit nproclimit;
@@ -354,6 +356,22 @@ sudoers_policy_main(int argc, char * con
}
}
+ /* Defer uid/gid checks until after defaults have been updated. */
+ if (unknown_runas_uid && !def_runas_allow_unknown_id) {
+ audit_failure(NewArgc, NewArgv, N_("unknown user: %s"),
+ runas_pw->pw_name);
+ sudo_warnx(U_("unknown user: %s"), runas_pw->pw_name);
+ goto done;
+ }
+ if (runas_gr != NULL) {
+ if (unknown_runas_gid && !def_runas_allow_unknown_id) {
+ audit_failure(NewArgc, NewArgv, N_("unknown group: %s"),
+ runas_gr->gr_name);
+ sudo_warnx(U_("unknown group: %s"), runas_gr->gr_name);
+ goto done;
+ }
+ }
+
/*
* Look up the timestamp dir owner if one is specified.
*/
@@ -1167,12 +1185,15 @@ set_runaspw(const char *user, bool quiet
struct passwd *pw = NULL;
debug_decl(set_runaspw, SUDOERS_DEBUG_PLUGIN)
+ unknown_runas_uid = false;
if (*user == '#') {
const char *errstr;
uid_t uid = sudo_strtoid(user + 1, &errstr);
if (errstr == NULL) {
- if ((pw = sudo_getpwuid(uid)) == NULL)
+ if ((pw = sudo_getpwuid(uid)) == NULL) {
+ unknown_runas_uid = true;
pw = sudo_fakepwnam(user, user_gid);
+ }
}
}
if (pw == NULL) {
@@ -1198,12 +1219,15 @@ set_runasgr(const char *group, bool quie
struct group *gr = NULL;
debug_decl(set_runasgr, SUDOERS_DEBUG_PLUGIN)
+ unknown_runas_gid = false;
if (*group == '#') {
const char *errstr;
gid_t gid = sudo_strtoid(group + 1, &errstr);
if (errstr == NULL) {
- if ((gr = sudo_getgrgid(gid)) == NULL)
+ if ((gr = sudo_getgrgid(gid)) == NULL) {
+ unknown_runas_gid = true;
gr = sudo_fakegrnam(group);
+ }
}
}
if (gr == NULL) {

View File

@ -0,0 +1,439 @@
diff -up ./config.h.in.CVE-2019-19234 ./config.h.in
--- ./config.h.in.CVE-2019-19234 2019-10-28 13:28:52.000000000 +0100
+++ ./config.h.in 2020-01-14 15:53:40.506988064 +0100
@@ -334,6 +334,9 @@
/* Define to 1 if you have the `getuserattr' function. */
#undef HAVE_GETUSERATTR
+/* Define to 1 if you have the `getusershell' function. */
+#undef HAVE_GETUSERSHELL
+
/* Define to 1 if you have the `getutid' function. */
#undef HAVE_GETUTID
diff -up ./configure.ac.CVE-2019-19234 ./configure.ac
--- ./configure.ac.CVE-2019-19234 2020-01-14 15:53:40.496987995 +0100
+++ ./configure.ac 2020-01-14 15:53:40.509988084 +0100
@@ -2562,6 +2562,10 @@ AC_CHECK_FUNCS([getdelim], [], [
SUDO_APPEND_COMPAT_EXP(sudo_getdelim)
COMPAT_TEST_PROGS="${COMPAT_TEST_PROGS}${COMPAT_TEST_PROGS+ }getdelim_test"
])
+AC_CHECK_FUNCS([getusershell], [], [
+ AC_LIBOBJ(getusershell)
+ SUDO_APPEND_COMPAT_EXP(sudo_getusershell)
+])
AC_CHECK_FUNCS([reallocarray], [], [
AC_LIBOBJ(reallocarray)
SUDO_APPEND_COMPAT_EXP(sudo_reallocarray)
diff -up ./configure.CVE-2019-19234 ./configure
--- ./configure.CVE-2019-19234 2019-10-28 13:29:14.000000000 +0100
+++ ./configure 2020-01-14 15:53:40.509988084 +0100
@@ -19395,6 +19395,32 @@ esac
fi
done
+for ac_func in getusershell
+do :
+ ac_fn_c_check_func "$LINENO" "getusershell" "ac_cv_func_getusershell"
+if test "x$ac_cv_func_getusershell" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_GETUSERSHELL 1
+_ACEOF
+
+else
+
+ case " $LIBOBJS " in
+ *" getusershell.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS getusershell.$ac_objext"
+ ;;
+esac
+
+
+ for _sym in sudo_getusershell; do
+ COMPAT_EXP="${COMPAT_EXP}${_sym}
+"
+ done
+
+
+fi
+done
+
for ac_func in reallocarray
do :
ac_fn_c_check_func "$LINENO" "reallocarray" "ac_cv_func_reallocarray"
diff -up ./doc/sudoers.man.in.CVE-2019-19234 ./doc/sudoers.man.in
--- ./doc/sudoers.man.in.CVE-2019-19234 2020-01-14 15:53:40.503988043 +0100
+++ ./doc/sudoers.man.in 2020-01-14 15:53:40.510988091 +0100
@@ -2959,6 +2959,28 @@ Older versions of
\fBsudo\fR
always allowed matching of unknown user and group IDs.
.TP 18n
+runas_check_shell
+.br
+If enabled,
+\fBsudo\fR
+will only run commands as a user whose shell appears in the
+\fI/etc/shells\fR
+file, even if the invoking user's
+\fRRunas_List\fR
+would otherwise permit it.
+If no
+\fI/etc/shells\fR
+file is present, a system-dependent list of built-in default shells is used.
+On many operating systems, system users such as
+\(lqbin\(rq,
+do not have a valid shell and this flag can be used to prevent
+commands from being run as those users.
+This flag is
+\fIoff\fR
+by default.
+.sp
+This setting is only supported by version 1.8.29 or higher.
+.TP 18n
runaspw
If set,
\fBsudo\fR
diff -up ./doc/sudoers.mdoc.in.CVE-2019-19234 ./doc/sudoers.mdoc.in
--- ./doc/sudoers.mdoc.in.CVE-2019-19234 2020-01-14 15:53:40.504988050 +0100
+++ ./doc/sudoers.mdoc.in 2020-01-14 15:53:40.510988091 +0100
@@ -2784,6 +2784,26 @@ This setting is only supported by versio
Older versions of
.Nm sudo
always allowed matching of unknown user and group IDs.
+.It runas_check_shell
+If enabled,
+.Nm sudo
+will only run commands as a user whose shell appears in the
+.Pa /etc/shells
+file, even if the invoking user's
+.Li Runas_List
+would otherwise permit it.
+If no
+.Pa /etc/shells
+file is present, a system-dependent list of built-in default shells is used.
+On many operating systems, system users such as
+.Dq bin ,
+do not have a valid shell and this flag can be used to prevent
+commands from being run as those users.
+This flag is
+.Em off
+by default.
+.Pp
+This setting is only supported by version 1.8.29 or higher.
.It runaspw
If set,
.Nm sudo
diff -up ./include/sudo_compat.h.CVE-2019-19234 ./include/sudo_compat.h
--- ./include/sudo_compat.h.CVE-2019-19234 2019-10-28 13:28:52.000000000 +0100
+++ ./include/sudo_compat.h 2020-01-14 15:53:40.511988098 +0100
@@ -407,6 +407,17 @@ __dso_public ssize_t sudo_getdelim(char
# undef getdelim
# define getdelim(_a, _b, _c, _d) sudo_getdelim((_a), (_b), (_c), (_d))
#endif /* HAVE_GETDELIM */
+#ifndef HAVE_GETUSERSHELL
+__dso_public char *sudo_getusershell(void);
+# undef getusershell
+# define getusershell() sudo_getusershell()
+__dso_public void sudo_setusershell(void);
+# undef setusershell
+# define setusershell() sudo_setusershell()
+__dso_public void sudo_endusershell(void);
+# undef endusershell
+# define endusershell() sudo_endusershell()
+#endif /* HAVE_GETUSERSHELL */
#ifndef HAVE_UTIMENSAT
__dso_public int sudo_utimensat(int fd, const char *file, const struct timespec *times, int flag);
# undef utimensat
diff -up ./lib/util/getusershell.c.CVE-2019-19234 ./lib/util/getusershell.c
--- ./lib/util/getusershell.c.CVE-2019-19234 2020-01-14 15:53:40.511988098 +0100
+++ ./lib/util/getusershell.c 2020-01-14 15:53:40.511988098 +0100
@@ -0,0 +1,138 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2019 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#define DEFAULT_TEXT_DOMAIN "sudo"
+#include "sudo_gettext.h" /* must be included before sudo_compat.h */
+
+#include "sudo_compat.h"
+#include "sudo_debug.h"
+#include "sudo_util.h"
+
+static char **allowed_shells, **current_shell;
+static char *default_shells[] = {
+ "/bin/sh",
+ "/bin/ksh",
+ "/bin/ksh93",
+ "/bin/bash",
+ "/bin/dash",
+ "/bin/zsh",
+ "/bin/csh",
+ "/bin/tcsh",
+ NULL
+};
+
+static char **
+read_shells(void)
+{
+ size_t maxshells = 16, nshells = 0;
+ size_t linesize = 0;
+ char *line = NULL;
+ FILE *fp;
+ debug_decl(read_shells, SUDO_DEBUG_UTIL)
+
+ if ((fp = fopen("/etc/shells", "r")) == NULL)
+ goto bad;
+
+ free(allowed_shells);
+ allowed_shells = reallocarray(NULL, maxshells, sizeof(char *));
+ if (allowed_shells == NULL)
+ goto bad;
+
+ while (sudo_parseln(&line, &linesize, NULL, fp, PARSELN_CONT_IGN) != -1) {
+ if (nshells + 1 >= maxshells) {
+ char **new_shells;
+
+ new_shells = reallocarray(NULL, maxshells + 16, sizeof(char *));
+ if (new_shells == NULL)
+ goto bad;
+ allowed_shells = new_shells;
+ maxshells += 16;
+ }
+ if ((allowed_shells[nshells] = strdup(line)) == NULL)
+ goto bad;
+ nshells++;
+ }
+ allowed_shells[nshells] = NULL;
+
+ free(line);
+ fclose(fp);
+ debug_return_ptr(allowed_shells);
+bad:
+ free(line);
+ if (fp != NULL)
+ fclose(fp);
+ while (nshells != 0)
+ free(allowed_shells[--nshells]);
+ free(allowed_shells);
+ allowed_shells = NULL;
+ debug_return_ptr(default_shells);
+}
+
+void
+sudo_setusershell(void)
+{
+ debug_decl(setusershell, SUDO_DEBUG_UTIL)
+
+ current_shell = read_shells();
+
+ debug_return;
+}
+
+void
+sudo_endusershell(void)
+{
+ debug_decl(endusershell, SUDO_DEBUG_UTIL)
+
+ if (allowed_shells != NULL) {
+ char **shell;
+
+ for (shell = allowed_shells; *shell != NULL; shell++)
+ free(*shell);
+ free(allowed_shells);
+ allowed_shells = NULL;
+ }
+ current_shell = NULL;
+
+ debug_return;
+}
+
+char *
+sudo_getusershell(void)
+{
+ debug_decl(getusershell, SUDO_DEBUG_UTIL)
+
+ if (current_shell == NULL)
+ current_shell = read_shells();
+
+ debug_return_str(*current_shell++);
+}
diff -up ./lib/util/Makefile.in.CVE-2019-19234 ./lib/util/Makefile.in
--- ./lib/util/Makefile.in.CVE-2019-19234 2019-10-28 13:28:53.000000000 +0100
+++ ./lib/util/Makefile.in 2020-01-14 15:53:40.511988098 +0100
@@ -678,6 +678,18 @@ gettime.i: $(srcdir)/gettime.c $(incdir)
$(CC) -E -o $@ $(CPPFLAGS) $<
gettime.plog: gettime.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/gettime.c --i-file $< --output-file $@
+getusershell.lo: $(srcdir)/getusershell.c $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
+ $(incdir)/sudo_gettext.h $(incdir)/sudo_queue.h \
+ $(incdir)/sudo_util.h $(top_builddir)/config.h
+ $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/getusershell.c
+getusershell.i: $(srcdir)/getusershell.c $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
+ $(incdir)/sudo_gettext.h $(incdir)/sudo_queue.h \
+ $(incdir)/sudo_util.h $(top_builddir)/config.h
+ $(CC) -E -o $@ $(CPPFLAGS) $<
+getusershell.plog: getusershell.i
+ rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/getusershell.c --i-file $< --output-file $@
gidlist.lo: $(srcdir)/gidlist.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
diff -up ./MANIFEST.CVE-2019-19234 ./MANIFEST
--- ./MANIFEST.CVE-2019-19234 2019-10-28 13:28:52.000000000 +0100
+++ ./MANIFEST 2020-01-14 15:53:40.506988064 +0100
@@ -103,6 +103,7 @@ lib/util/getgrouplist.c
lib/util/gethostname.c
lib/util/getopt_long.c
lib/util/gettime.c
+lib/util/getusershell.c
lib/util/gidlist.c
lib/util/glob.c
lib/util/inet_ntop.c
diff -up ./mkdep.pl.CVE-2019-19234 ./mkdep.pl
--- ./mkdep.pl.CVE-2019-19234 2019-10-28 13:28:52.000000000 +0100
+++ ./mkdep.pl 2020-01-14 15:53:40.511988098 +0100
@@ -116,7 +116,7 @@ sub mkdep {
# XXX - fill in AUTH_OBJS from contents of the auth dir instead
$makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:;
$makefile =~ s:\@DIGEST\@:digest.lo digest_openssl.lo digest_gcrypt.lo:;
- $makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_uniform.lo closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getentropy.lo getgrouplist.lo getdelim.lo getopt_long.lo glob.lo inet_ntop_lo inet_pton.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo nanosleep.lo pw_dup.lo reallocarray.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo str2sig.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo utimens.lo vsyslog.lo pipe2.lo:;
+ $makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_uniform.lo closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getentropy.lo getgrouplist.lo getdelim.lo getopt_long.lo getusershell.lo glob.lo inet_ntop_lo inet_pton.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo nanosleep.lo pw_dup.lo reallocarray.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo str2sig.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo utimens.lo vsyslog.lo pipe2.lo:;
# Parse OBJS lines
my %objs;
diff -up ./plugins/sudoers/check.c.CVE-2019-19234 ./plugins/sudoers/check.c
--- ./plugins/sudoers/check.c.CVE-2019-19234 2019-10-28 13:27:45.000000000 +0100
+++ ./plugins/sudoers/check.c 2020-01-14 15:53:40.511988098 +0100
@@ -333,3 +333,28 @@ get_authpw(int mode)
debug_return_ptr(pw);
}
+
+/*
+ * Returns true if the specified shell is allowed by /etc/shells, else false.
+ */
+bool
+check_user_shell(const struct passwd *pw)
+{
+ const char *shell;
+ debug_decl(check_user_shell, SUDOERS_DEBUG_AUTH)
+
+ if (!def_runas_check_shell)
+ debug_return_bool(true);
+
+ sudo_debug_printf(SUDO_DEBUG_INFO,
+ "%s: checking /etc/shells for %s", __func__, pw->pw_shell);
+
+ setusershell();
+ while ((shell = getusershell()) != NULL) {
+ if (strcmp(shell, pw->pw_shell) == 0)
+ debug_return_bool(true);
+ }
+ endusershell();
+
+ debug_return_bool(false);
+}
diff -up ./plugins/sudoers/def_data.c.CVE-2019-19234 ./plugins/sudoers/def_data.c
--- ./plugins/sudoers/def_data.c.CVE-2019-19234 2020-01-14 15:53:40.504988050 +0100
+++ ./plugins/sudoers/def_data.c 2020-01-14 15:53:40.511988098 +0100
@@ -518,6 +518,10 @@ struct sudo_defs_types sudo_defs_table[]
N_("Allow the use of unknown runas user and/or group ID"),
NULL,
}, {
+ "runas_check_shell", T_FLAG,
+ N_("Only permit running commands as a user with a valid shell"),
+ NULL,
+ }, {
NULL, 0, NULL
}
};
diff -up ./plugins/sudoers/def_data.h.CVE-2019-19234 ./plugins/sudoers/def_data.h
--- ./plugins/sudoers/def_data.h.CVE-2019-19234 2020-01-14 15:53:40.512988105 +0100
+++ ./plugins/sudoers/def_data.h 2020-01-14 15:58:06.927808982 +0100
@@ -238,6 +238,8 @@
#define def_cmnd_no_wait (sudo_defs_table[I_CMND_NO_WAIT].sd_un.flag)
#define I_RUNAS_ALLOW_UNKNOWN_ID 119
#define def_runas_allow_unknown_id (sudo_defs_table[I_RUNAS_ALLOW_UNKNOWN_ID].sd_un.flag)
+#define I_RUNAS_CHECK_SHELL 120
+#define def_runas_check_shell (sudo_defs_table[I_RUNAS_CHECK_SHELL].sd_un.flag)
enum def_tuple {
never,
diff -up ./plugins/sudoers/def_data.in.CVE-2019-19234 ./plugins/sudoers/def_data.in
--- ./plugins/sudoers/def_data.in.CVE-2019-19234 2020-01-14 15:53:40.505988057 +0100
+++ ./plugins/sudoers/def_data.in 2020-01-14 15:53:40.512988105 +0100
@@ -375,3 +375,7 @@ cmnd_no_wait
runas_allow_unknown_id
T_FLAG
"Allow the use of unknown runas user and/or group ID"
+runas_check_shell
+ T_FLAG
+ "Only permit running commands as a user with a valid shell"
+
diff -up ./plugins/sudoers/sudoers.c.CVE-2019-19234 ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.CVE-2019-19234 2020-01-14 15:53:40.505988057 +0100
+++ ./plugins/sudoers/sudoers.c 2020-01-14 15:53:40.512988105 +0100
@@ -273,7 +273,7 @@ sudoers_policy_main(int argc, char * con
/* Not an audit event. */
sudo_warnx(U_("sudoers specifies that root is not allowed to sudo"));
goto bad;
- }
+ }
if (!set_perms(PERM_INITIAL))
goto bad;
@@ -412,6 +412,13 @@ sudoers_policy_main(int argc, char * con
goto bad;
}
+ /* Check runas user's shell. */
+ if (!check_user_shell(runas_pw)) {
+ log_warningx(SLOG_RAW_MSG, N_("invalid shell for user %s: %s"),
+ runas_pw->pw_name, runas_pw->pw_shell);
+ goto bad;
+ }
+
/*
* We don't reset the environment for sudoedit or if the user
* specified the -E command line flag and they have setenv privs.
diff -up ./plugins/sudoers/sudoers.h.CVE-2019-19234 ./plugins/sudoers/sudoers.h
--- ./plugins/sudoers/sudoers.h.CVE-2019-19234 2020-01-14 15:53:40.502988036 +0100
+++ ./plugins/sudoers/sudoers.h 2020-01-14 15:53:40.512988105 +0100
@@ -264,6 +264,7 @@ int find_path(const char *infile, char *
/* check.c */
int check_user(int validate, int mode);
+bool check_user_shell(const struct passwd *pw);
bool user_is_exempt(void);
/* prompt.c */

View File

@ -0,0 +1,154 @@
From 4b6de608c25a6ffbdb507be958e12f814b43077c Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Wed, 4 Dec 2019 12:38:22 -0700
Subject: [PATCH] Only update the time stamp entry after the approval function
has succeeded. Bug #910
---
plugins/sudoers/check.c | 59 +++++++++++++++++++----------------------
1 file changed, 27 insertions(+), 32 deletions(-)
diff --git a/plugins/sudoers/check.c b/plugins/sudoers/check.c
index db8e05161..ea1d89085 100644
--- a/plugins/sudoers/check.c
+++ b/plugins/sudoers/check.c
@@ -51,6 +51,7 @@ static bool display_lecture(int);
static struct passwd *get_authpw(int);
struct getpass_closure {
+ int tstat;
void *cookie;
struct passwd *auth_pw;
};
@@ -89,27 +90,20 @@ getpass_resume(int signo, void *vclosure)
* or -1 on fatal error.
*/
static int
-check_user_interactive(int validated, int mode, struct passwd *auth_pw)
+check_user_interactive(int validated, int mode, struct getpass_closure *closure)
{
struct sudo_conv_callback cb, *callback = NULL;
- struct getpass_closure closure;
- int status = TS_ERROR;
int ret = -1;
char *prompt;
bool lectured;
debug_decl(check_user_interactive, SUDOERS_DEBUG_AUTH)
- /* Setup closure for getpass_{suspend,resume} */
- closure.auth_pw = auth_pw;
- closure.cookie = NULL;
- sudo_pw_addref(closure.auth_pw);
-
/* Open, lock and read time stamp file if we are using it. */
if (!ISSET(mode, MODE_IGNORE_TICKET)) {
/* Open time stamp file and check its status. */
- closure.cookie = timestamp_open(user_name, user_sid);
- if (timestamp_lock(closure.cookie, closure.auth_pw))
- status = timestamp_status(closure.cookie, closure.auth_pw);
+ closure->cookie = timestamp_open(user_name, user_sid);
+ if (timestamp_lock(closure->cookie, closure->auth_pw))
+ closure->tstat = timestamp_status(closure->cookie, closure->auth_pw);
/* Construct callback for getpass function. */
memset(&cb, 0, sizeof(cb));
@@ -120,7 +114,7 @@ check_user_interactive(int validated, int mode, struct passwd *auth_pw)
callback = &cb;
}
- switch (status) {
+ switch (closure->tstat) {
case TS_FATAL:
/* Fatal error (usually setuid failure), unsafe to proceed. */
goto done;
@@ -144,32 +138,22 @@ check_user_interactive(int validated, int mode, struct passwd *auth_pw)
}
/* XXX - should not lecture if askpass helper is being used. */
- lectured = display_lecture(status);
+ lectured = display_lecture(closure->tstat);
/* Expand any escapes in the prompt. */
prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt,
- closure.auth_pw->pw_name);
+ closure->auth_pw->pw_name);
if (prompt == NULL)
goto done;
- ret = verify_user(closure.auth_pw, prompt, validated, callback);
+ ret = verify_user(closure->auth_pw, prompt, validated, callback);
if (ret == true && lectured)
(void)set_lectured(); /* lecture error not fatal */
free(prompt);
break;
}
- /*
- * Only update time stamp if user was validated.
- * Failure to update the time stamp is not a fatal error.
- */
- if (ret == true && ISSET(validated, VALIDATE_SUCCESS) && status != TS_ERROR)
- (void)timestamp_update(closure.cookie, closure.auth_pw);
done:
- if (closure.cookie != NULL)
- timestamp_close(closure.cookie);
- sudo_pw_delref(closure.auth_pw);
-
debug_return_int(ret);
}
@@ -180,7 +164,7 @@ done:
int
check_user(int validated, int mode)
{
- struct passwd *auth_pw;
+ struct getpass_closure closure = { TS_ERROR };
int ret = -1;
bool exempt = false;
debug_decl(check_user, SUDOERS_DEBUG_AUTH)
@@ -189,9 +173,9 @@ check_user(int validated, int mode)
* Init authentication system regardless of whether we need a password.
* Required for proper PAM session support.
*/
- if ((auth_pw = get_authpw(mode)) == NULL)
+ if ((closure.auth_pw = get_authpw(mode)) == NULL)
goto done;
- if (sudo_auth_init(auth_pw) == -1)
+ if (sudo_auth_init(closure.auth_pw) == -1)
goto done;
/*
@@ -222,15 +206,26 @@ check_user(int validated, int mode)
}
}
- ret = check_user_interactive(validated, mode, auth_pw);
+ ret = check_user_interactive(validated, mode, &closure);
done:
if (ret == true) {
/* The approval function may disallow a user post-authentication. */
- ret = sudo_auth_approval(auth_pw, validated, exempt);
+ ret = sudo_auth_approval(closure.auth_pw, validated, exempt);
+
+ /*
+ * Only update time stamp if user validated and was approved.
+ * Failure to update the time stamp is not a fatal error.
+ */
+ if (ret == true && closure.tstat != TS_ERROR) {
+ if (ISSET(validated, VALIDATE_SUCCESS))
+ (void)timestamp_update(closure.cookie, closure.auth_pw);
+ }
}
- sudo_auth_cleanup(auth_pw);
- sudo_pw_delref(auth_pw);
+ timestamp_close(closure.cookie);
+ sudo_auth_cleanup(closure.auth_pw);
+ if (closure.auth_pw != NULL)
+ sudo_pw_delref(closure.auth_pw);
debug_return_int(ret);
}
--
2.25.1

View File

@ -0,0 +1,29 @@
From 5472b1751645f750e42a0ba6daac667983b1a56c Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Fri, 24 Jan 2020 11:13:55 -0700
Subject: [PATCH] Fix crash in sudo 1.8.30 when suspending sudo at the password
prompt. The closure pointer in sudo_conv_callback was being filled in with a
struct getpass_closure ** instead of a struct getpass_closure *. The bug was
introduced in the fix for Bug #910; previously the closure variable was a
struct getpass_closure, not a pointer. Fix from Michael Norton; Bug #914.
---
plugins/sudoers/check.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugins/sudoers/check.c b/plugins/sudoers/check.c
index 72e87eef6..9b03c7a05 100644
--- a/plugins/sudoers/check.c
+++ b/plugins/sudoers/check.c
@@ -108,7 +108,7 @@ check_user_interactive(int validated, int mode, struct getpass_closure *closure)
/* Construct callback for getpass function. */
memset(&cb, 0, sizeof(cb));
cb.version = SUDO_CONV_CALLBACK_VERSION;
- cb.closure = &closure;
+ cb.closure = closure;
cb.on_suspend = getpass_suspend;
cb.on_resume = getpass_resume;
callback = &cb;
--
2.25.1

View File

@ -0,0 +1,206 @@
diff -up ./plugins/sudoers/policy.c.heap-buffer ./plugins/sudoers/policy.c
--- ./plugins/sudoers/policy.c.heap-buffer 2019-10-28 13:28:52.000000000 +0100
+++ ./plugins/sudoers/policy.c 2021-01-20 11:38:06.481807015 +0100
@@ -100,10 +100,11 @@ parse_bool(const char *line, int varlen,
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)
@@ -332,6 +333,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_gid = (gid_t)-1;
user_sid = (pid_t)-1;
user_uid = (gid_t)-1;
diff -up ./plugins/sudoers/sudoers.c.heap-buffer ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.heap-buffer 2021-01-20 11:34:57.523317977 +0100
+++ ./plugins/sudoers/sudoers.c 2021-01-20 12:08:01.331553520 +0100
@@ -451,7 +451,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;
@@ -834,8 +834,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))
@@ -873,7 +873,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
@@ -881,10 +882,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 -up ./plugins/sudoers/timestamp.c.heap-buffer ./plugins/sudoers/timestamp.c
--- ./plugins/sudoers/timestamp.c.heap-buffer 2021-01-20 12:11:28.218774128 +0100
+++ ./plugins/sudoers/timestamp.c 2021-01-20 12:20:41.772324808 +0100
@@ -652,8 +652,8 @@ timestamp_lock(void *vcookie, struct pas
} 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));
+ 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);
}
diff -up ./src/parse_args.c.heap-buffer ./src/parse_args.c
--- ./src/parse_args.c.heap-buffer 2021-01-20 11:35:18.231043445 +0100
+++ ./src/parse_args.c 2021-01-20 12:26:31.290591673 +0100
@@ -124,7 +124,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
@@ -269,6 +272,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. */
@@ -360,7 +364,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':
assert(optarg != NULL);
@@ -371,6 +375,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) {
@@ -421,7 +426,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);
@@ -429,6 +434,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':
/* An empty prompt is allowed. */
@@ -478,7 +484,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)
@@ -505,7 +511,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;
@@ -568,23 +574,24 @@ 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;
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 != '$')

View File

@ -0,0 +1,90 @@
From 06b46ae226fecd4188af372ac0ccd7aa582e21c8 Mon Sep 17 00:00:00 2001
From: Tomas Sykora <tosykora@redhat.com>
Date: Wed, 17 Aug 2016 10:12:11 +0200
Subject: [PATCH] Sudo logs username root instead of realuser
RHEL7 sudo logs username root instead of realuser in /var/log/secure
Rebased from:
Patch50: sudo-1.8.6p7-logsudouser.patch
Resolves:
rhbz#1312486
---
plugins/sudoers/logging.c | 14 +++++++-------
plugins/sudoers/sudoers.h | 1 +
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/plugins/sudoers/logging.c b/plugins/sudoers/logging.c
index 45cae67..74b2220 100644
--- a/plugins/sudoers/logging.c
+++ b/plugins/sudoers/logging.c
@@ -104,7 +104,7 @@ do_syslog(int pri, char *msg)
* Log the full line, breaking into multiple syslog(3) calls if necessary
*/
fmt = _("%8s : %s");
- maxlen = def_syslog_maxlen - (strlen(fmt) - 5 + strlen(user_name));
+ maxlen = def_syslog_maxlen - (strlen(fmt) - 5 + strlen(sudo_user_name));
for (p = msg; *p != '\0'; ) {
len = strlen(p);
if (len > maxlen) {
@@ -120,7 +120,7 @@ do_syslog(int pri, char *msg)
save = *tmp;
*tmp = '\0';
- mysyslog(pri, fmt, user_name, p);
+ mysyslog(pri, fmt, sudo_user_name, p);
*tmp = save; /* restore saved character */
@@ -128,11 +128,11 @@ do_syslog(int pri, char *msg)
for (p = tmp; *p == ' '; p++)
continue;
} else {
- mysyslog(pri, fmt, user_name, p);
+ mysyslog(pri, fmt, sudo_user_name, p);
p += len;
}
fmt = _("%8s : (command continued) %s");
- maxlen = def_syslog_maxlen - (strlen(fmt) - 5 + strlen(user_name));
+ maxlen = def_syslog_maxlen - (strlen(fmt) - 5 + strlen(sudo_user_name));
}
sudoers_setlocale(oldlocale, NULL);
@@ -179,10 +179,10 @@ do_logfile(const char *msg)
timestr = "invalid date";
if (def_log_host) {
len = asprintf(&full_line, "%s : %s : HOST=%s : %s",
- timestr, user_name, user_srunhost, msg);
+ timestr, sudo_user_name, user_srunhost, msg);
} else {
len = asprintf(&full_line, "%s : %s : %s",
- timestr, user_name, msg);
+ timestr, sudo_user_name, msg);
}
if (len == -1) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
@@ -746,7 +746,7 @@ send_mail(const char *fmt, ...)
if ((timestr = get_timestr(time(NULL), def_log_year)) == NULL)
timestr = "invalid date";
- (void) fprintf(mail, "\n\n%s : %s : %s : ", user_host, timestr, user_name);
+ (void) fprintf(mail, "\n\n%s : %s : %s : ", user_host, timestr, sudo_user_name);
va_start(ap, fmt);
(void) vfprintf(mail, fmt, ap);
va_end(ap);
diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h
index cfd5abb..c69a043 100644
--- a/plugins/sudoers/sudoers.h
+++ b/plugins/sudoers/sudoers.h
@@ -180,6 +180,7 @@ struct sudo_user {
/*
* Shortcuts for sudo_user contents.
*/
+#define sudo_user_name (sudo_user.pw->pw_name)
#define user_name (sudo_user.name)
#define user_uid (sudo_user.uid)
#define user_gid (sudo_user.gid)
--
2.7.4

View File

@ -0,0 +1,171 @@
diff -up ./plugins/sudoers/editor.c.other ./plugins/sudoers/editor.c
--- ./plugins/sudoers/editor.c.other 2023-01-16 17:37:04.659967300 +0100
+++ ./plugins/sudoers/editor.c 2023-01-16 17:40:35.944400376 +0100
@@ -39,6 +39,82 @@
#include "sudoers.h"
/*
+ * Non-destructive word-split that handles single and double quotes and
+ * escaped white space. Quotes are only recognized at the start of a word.
+ * They are treated as normal characters inside a word.
+ */
+static const char *
+wordsplit(const char *str, const char *endstr, const char **last)
+{
+ const char *cp;
+ debug_decl(wordsplit, SUDO_DEBUG_UTIL);
+
+ /* If no str specified, use last ptr (if any). */
+ if (str == NULL) {
+ str = *last;
+ /* Consume end quote if present. */
+ if (*str == '"' || *str == '\'')
+ str++;
+ }
+
+ /* Skip leading white space characters. */
+ while (str < endstr && (*str == ' ' || *str == '\t'))
+ str++;
+
+ /* Empty string? */
+ if (str >= endstr) {
+ *last = endstr;
+ debug_return_ptr(NULL);
+ }
+
+ /* If word is quoted, skip to end quote and return. */
+ if (*str == '"' || *str == '\'') {
+ const char *endquote = memchr(str + 1, *str, endstr - str);
+ if (endquote != NULL) {
+ *last = endquote;
+ debug_return_const_ptr(str + 1);
+ }
+ }
+
+ /* Scan str until we encounter white space. */
+ for (cp = str; cp < endstr; cp++) {
+ if (*cp == '\\') {
+ /* quoted char, do not interpret */
+ cp++;
+ continue;
+ }
+ if (*cp == ' ' || *cp == '\t') {
+ /* end of word */
+ break;
+ }
+ }
+ *last = cp;
+ debug_return_const_ptr(str);
+}
+
+/* Copy len chars from string, collapsing chars escaped with a backslash. */
+static char *
+copy_arg(const char *src, size_t len)
+{
+ const char *src_end = src + len;
+ char *copy, *dst;
+ debug_decl(copy_arg, SUDOERS_DEBUG_UTIL);
+
+ if ((copy = malloc(len + 1)) != NULL) {
+ for (dst = copy; src < src_end; ) {
+ if (*src == '\\') {
+ src++;
+ continue;
+ }
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+ }
+
+ debug_return_ptr(copy);
+}
+
+/*
* Search for the specified editor in the user's PATH, checking
* the result against allowlist if non-NULL. An argument vector
* suitable for execve() is allocated and stored in argv_out.
@@ -52,7 +128,7 @@ static char *
resolve_editor(const char *ed, size_t edlen, int nfiles, char **files,
int *argc_out, char ***argv_out, char * const *allowlist)
{
- char **nargv, *editor, *editor_path = NULL;
+ char **nargv = NULL, *editor = NULL, *editor_path = NULL;
const char *cp, *ep, *tmp;
const char *edend = ed + edlen;
struct stat user_editor_sb;
@@ -64,14 +140,12 @@ resolve_editor(const char *ed, size_t ed
* The EDITOR and VISUAL environment variables may contain command
* line args so look for those and alloc space for them too.
*/
- cp = sudo_strsplit(ed, edend, " \t", &ep);
+ cp = wordsplit(ed, edend, &ep);
if (cp == NULL)
debug_return_str(NULL);
- editor = strndup(cp, (size_t)(ep - cp));
- if (editor == NULL) {
- sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
- debug_return_str(NULL);
- }
+ editor = copy_arg(cp, ep - cp);
+ if (editor == NULL)
+ goto oom;
/* If we can't find the editor in the user's PATH, give up. */
if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), 0, allowlist) != FOUND) {
@@ -81,30 +155,22 @@ resolve_editor(const char *ed, size_t ed
}
/* Count rest of arguments and allocate editor argv. */
- for (nargc = 1, tmp = ep; sudo_strsplit(NULL, edend, " \t", &tmp) != NULL; )
+ for (nargc = 1, tmp = ep; wordsplit(NULL, edend, &tmp) != NULL; )
nargc++;
if (nfiles != 0)
nargc += nfiles + 1;
nargv = reallocarray(NULL, nargc + 1, sizeof(char *));
- if (nargv == NULL) {
- sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
- free(editor);
- free(editor_path);
- debug_return_str(NULL);
- }
+ if (nargv == NULL)
+ goto oom;
/* Fill in editor argv (assumes files[] is NULL-terminated). */
nargv[0] = editor;
- for (nargc = 1; (cp = sudo_strsplit(NULL, edend, " \t", &ep)) != NULL; nargc++) {
- nargv[nargc] = strndup(cp, (size_t)(ep - cp));
- if (nargv[nargc] == NULL) {
- sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
- free(editor_path);
- while (nargc--)
- free(nargv[nargc]);
- free(nargv);
- debug_return_str(NULL);
- }
+ editor = NULL;
+ for (nargc = 1; (cp = wordsplit(NULL, edend, &ep)) != NULL; nargc++) {
+ /* Copy string, collapsing chars escaped with a backslash. */
+ nargv[nargc] = copy_arg(cp, ep - cp);
+ if (nargv[nargc] == NULL)
+ goto oom;
}
if (nfiles != 0) {
nargv[nargc++] = "--";
@@ -116,6 +182,16 @@ resolve_editor(const char *ed, size_t ed
*argc_out = nargc;
*argv_out = nargv;
debug_return_str(editor_path);
+oom:
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ free(editor);
+ free(editor_path);
+ if (nargv != NULL) {
+ while (nargc--)
+ free(nargv[nargc]);
+ free(nargv);
+ }
+ debug_return_str(NULL);
}
/*

View File

@ -0,0 +1,57 @@
diff -up ./plugins/sudoers/editor.c.whitelist ./plugins/sudoers/editor.c
--- ./plugins/sudoers/editor.c.whitelist 2023-01-16 17:31:58.108335076 +0100
+++ ./plugins/sudoers/editor.c 2023-01-16 17:33:37.375547672 +0100
@@ -40,7 +40,7 @@
/*
* Search for the specified editor in the user's PATH, checking
- * the result against whitelist if non-NULL. An argument vector
+ * the result against allowlist if non-NULL. An argument vector
* suitable for execve() is allocated and stored in argv_out.
* If nfiles is non-zero, files[] is added to the end of argv_out.
*
@@ -50,7 +50,7 @@
*/
static char *
resolve_editor(const char *ed, size_t edlen, int nfiles, char **files,
- int *argc_out, char ***argv_out, char * const *whitelist)
+ int *argc_out, char ***argv_out, char * const *allowlist)
{
char **nargv, *editor, *editor_path = NULL;
const char *cp, *ep, *tmp;
@@ -74,7 +74,7 @@ resolve_editor(const char *ed, size_t ed
}
/* If we can't find the editor in the user's PATH, give up. */
- if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), 0, whitelist) != FOUND) {
+ if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), 0, allowlist) != FOUND) {
free(editor);
errno = ENOENT;
debug_return_str(NULL);
@@ -130,7 +130,7 @@ resolve_editor(const char *ed, size_t ed
*/
char *
find_editor(int nfiles, char **files, int *argc_out, char ***argv_out,
- char * const *whitelist, const char **env_editor, bool env_error)
+ char * const *allowlist, const char **env_editor, bool env_error)
{
char *ev[3], *editor_path = NULL;
unsigned int i;
@@ -149,7 +149,7 @@ find_editor(int nfiles, char **files, in
if (editor != NULL && *editor != '\0') {
*env_editor = editor;
editor_path = resolve_editor(editor, strlen(editor),
- nfiles, files, argc_out, argv_out, whitelist);
+ nfiles, files, argc_out, argv_out, allowlist);
if (editor_path != NULL)
break;
if (errno != ENOENT)
@@ -169,7 +169,7 @@ find_editor(int nfiles, char **files, in
for (cp = sudo_strsplit(def_editor, def_editor_end, ":", &ep);
cp != NULL; cp = sudo_strsplit(NULL, def_editor_end, ":", &ep)) {
editor_path = resolve_editor(cp, (size_t)(ep - cp), nfiles,
- files, argc_out, argv_out, whitelist);
+ files, argc_out, argv_out, allowlist);
if (editor_path != NULL)
break;
if (errno != ENOENT)

View File

@ -1,19 +1,19 @@
diff -up ./plugins/sudoers/editor.c.cve ./plugins/sudoers/editor.c
--- ./plugins/sudoers/editor.c.cve 2021-01-09 21:12:16.000000000 +0100
+++ ./plugins/sudoers/editor.c 2023-01-17 13:57:05.598949058 +0100
@@ -126,7 +126,7 @@ resolve_editor(const char *ed, size_t ed
const char *tmp, *cp, *ep = NULL;
--- ./plugins/sudoers/editor.c.cve 2023-01-16 17:52:37.074066664 +0100
+++ ./plugins/sudoers/editor.c 2023-01-16 17:58:06.603774304 +0100
@@ -132,7 +132,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);
debug_decl(resolve_editor, SUDOERS_DEBUG_UTIL)
/*
@@ -144,9 +144,7 @@ resolve_editor(const char *ed, size_t ed
@@ -149,9 +149,7 @@ resolve_editor(const char *ed, size_t ed
/* If we can't find the editor in the user's PATH, give up. */
if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), NULL,
0, allowlist) != FOUND) {
if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), 0, allowlist) != FOUND) {
- free(editor);
- errno = ENOENT;
- debug_return_str(NULL);
@ -21,7 +21,7 @@ diff -up ./plugins/sudoers/editor.c.cve ./plugins/sudoers/editor.c
}
/* Count rest of arguments and allocate editor argv. */
@@ -166,6 +164,18 @@ resolve_editor(const char *ed, size_t ed
@@ -171,7 +169,19 @@ resolve_editor(const char *ed, size_t ed
nargv[nargc] = copy_arg(cp, ep - cp);
if (nargv[nargc] == NULL)
goto oom;
@ -36,11 +36,12 @@ diff -up ./plugins/sudoers/editor.c.cve ./plugins/sudoers/editor.c
+ errno = EINVAL;
+ goto bad;
+ }
+
}
+
if (nfiles != 0) {
nargv[nargc++] = "--";
@@ -179,6 +189,7 @@ resolve_editor(const char *ed, size_t ed
while (nfiles--)
@@ -184,6 +194,7 @@ resolve_editor(const char *ed, size_t ed
debug_return_str(editor_path);
oom:
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
@ -49,17 +50,16 @@ diff -up ./plugins/sudoers/editor.c.cve ./plugins/sudoers/editor.c
free(editor_path);
if (nargv != NULL) {
diff -up ./plugins/sudoers/sudoers.c.cve ./plugins/sudoers/sudoers.c
--- ./plugins/sudoers/sudoers.c.cve 2023-01-17 13:50:33.718255775 +0100
+++ ./plugins/sudoers/sudoers.c 2023-01-17 14:00:53.049710094 +0100
@@ -724,21 +724,34 @@ sudoers_policy_main(int argc, char * con
--- ./plugins/sudoers/sudoers.c.cve 2023-01-16 17:52:37.074066664 +0100
+++ ./plugins/sudoers/sudoers.c 2023-01-16 18:02:43.928307505 +0100
@@ -634,21 +634,32 @@ sudoers_policy_main(int argc, char * con
/* Note: must call audit before uid change. */
if (ISSET(sudo_mode, MODE_EDIT)) {
+ const char *env_editor = NULL;
char **edit_argv;
int edit_argc;
- int edit_argc;
- const char *env_editor;
+
+ const char *env_editor = NULL;
+ int edit_argc;
free(safe_cmnd);
safe_cmnd = find_editor(NewArgc - 1, NewArgv + 1, &edit_argc,
@ -67,46 +67,47 @@ diff -up ./plugins/sudoers/sudoers.c.cve ./plugins/sudoers/sudoers.c
if (safe_cmnd == NULL) {
- if (errno != ENOENT)
- goto done;
- audit_failure(NewArgv, N_("%s: command not found"),
- audit_failure(NewArgc, NewArgv, N_("%s: command not found"),
- env_editor ? env_editor : def_editor);
- sudo_warnx(U_("%s: command not found"),
- env_editor ? env_editor : def_editor);
- goto bad;
+
+ switch (errno) {
+ case ENOENT:
+ audit_failure(NewArgv, N_("%s: command not found"),
+ env_editor ? env_editor : def_editor);
+ sudo_warnx(U_("%s: command not found"),
+ env_editor ? env_editor : def_editor);
+ goto bad;
+ case EINVAL:
+ if (def_env_editor && env_editor != NULL) {
+ /* User tried to do something funny with the editor. */
+ log_warningx(SLOG_NO_STDERR|SLOG_AUDIT|SLOG_SEND_MAIL,
+ "invalid user-specified editor: %s", env_editor);
+ goto bad;
+ }
+ FALLTHROUGH;
+ default:
+ goto done;
- }
+ switch (errno) {
+ case ENOENT:
+ audit_failure(NewArgc, NewArgv, N_("%s: command not found"),
+ env_editor ? env_editor : def_editor);
+ sudo_warnx(U_("%s: command not found"),
+ env_editor ? env_editor : def_editor);
+ goto bad;
+ case EINVAL:
+ if (def_env_editor && env_editor != NULL) {
+ /* User tried to do something funny with the editor. */
+ log_warningx(SLOG_NO_STDERR|SLOG_SEND_MAIL,
+ "invalid user-specified editor: %s", env_editor);
+ goto bad;
+ }
+ default:
+ goto done;
+ }
}
sudoers_gc_add(GC_VECTOR, edit_argv);
NewArgv = edit_argv;
+ }
+
if (audit_success(edit_argc, edit_argv) != 0 && !def_ignore_audit_errors)
goto done;
diff -up ./plugins/sudoers/visudo.c.cve ./plugins/sudoers/visudo.c
--- ./plugins/sudoers/visudo.c.cve 2021-01-09 21:12:16.000000000 +0100
+++ ./plugins/sudoers/visudo.c 2023-01-17 14:02:01.393135129 +0100
@@ -303,7 +303,7 @@ static char *
--- ./plugins/sudoers/visudo.c.cve 2019-10-28 13:28:54.000000000 +0100
+++ ./plugins/sudoers/visudo.c 2023-01-16 18:03:48.713975354 +0100
@@ -308,7 +308,7 @@ static char *
get_editor(int *editor_argc, char ***editor_argv)
{
char *editor_path = NULL, **allowlist = NULL;
char *editor_path = NULL, **whitelist = NULL;
- const char *env_editor;
+ const char *env_editor = NULL;
static char *files[] = { "+1", "sudoers" };
unsigned int allowlist_len = 0;
debug_decl(get_editor, SUDOERS_DEBUG_UTIL);
@@ -337,7 +337,11 @@ get_editor(int *editor_argc, char ***edi
unsigned int whitelist_len = 0;
debug_decl(get_editor, SUDOERS_DEBUG_UTIL)
@@ -342,7 +342,11 @@ get_editor(int *editor_argc, char ***edi
if (editor_path == NULL) {
if (def_env_editor && env_editor != NULL) {
/* We are honoring $EDITOR so this is a fatal error. */

View File

@ -1,31 +0,0 @@
From e5c1778e7dd32ff3ed8cf969540404c9c0e6d5a1 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Thu, 18 Feb 2021 08:32:13 -0700
Subject: [PATCH] When logging JSON to syslog, wrap the contents in a "sudo"
object. This makes it easier for log parsers to identify what is a sudo log
entry.
---
lib/eventlog/eventlog.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/eventlog/eventlog.c b/lib/eventlog/eventlog.c
index 1f0183b1b..e6f744da5 100644
--- a/lib/eventlog/eventlog.c
+++ b/lib/eventlog/eventlog.c
@@ -950,10 +950,10 @@ do_syslog_json(int pri, int event_type, const char *reason,
if (json_str == NULL)
debug_return_bool(false);
- /* Syslog it with a @cee: prefix */
+ /* Syslog it in a sudo object with a @cee: prefix. */
/* TODO: use evl_conf.syslog_maxlen to break up long messages. */
evl_conf.open_log(EVLOG_SYSLOG, NULL);
- syslog(pri, "@cee:{%s}", json_str);
+ syslog(pri, "@cee:{\"sudo\":{%s}}", json_str);
evl_conf.close_log(EVLOG_SYSLOG, NULL);
free(json_str);
debug_return_bool(true);
--
2.43.0

View File

@ -1,187 +0,0 @@
From f399c449ad6fc7412588998aa92b52323ef63ee5 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Wed, 24 Feb 2021 13:59:17 -0700
Subject: [PATCH] Move eventlog_free() into its own file.
---
MANIFEST | 1 +
lib/eventlog/Makefile.in | 14 ++++++-
lib/eventlog/eventlog.c | 37 ------------------
lib/eventlog/eventlog_free.c | 73 ++++++++++++++++++++++++++++++++++++
4 files changed, 87 insertions(+), 38 deletions(-)
create mode 100644 lib/eventlog/eventlog_free.c
diff --git a/MANIFEST b/MANIFEST
index 8c5a57ae8..a2bed131d 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -104,6 +104,7 @@ include/sudo_util.h
install-sh
lib/eventlog/Makefile.in
lib/eventlog/eventlog.c
+lib/eventlog/eventlog_free.c
lib/eventlog/logwrap.c
lib/eventlog/regress/logwrap/check_wrap.c
lib/eventlog/regress/logwrap/check_wrap.in
diff --git a/lib/eventlog/Makefile.in b/lib/eventlog/Makefile.in
index 8790ac1ae..24c2dbce9 100644
--- a/lib/eventlog/Makefile.in
+++ b/lib/eventlog/Makefile.in
@@ -82,7 +82,7 @@ SHELL = @SHELL@
TEST_PROGS = check_wrap
-LIBEVENTLOG_OBJS = eventlog.lo logwrap.lo
+LIBEVENTLOG_OBJS = eventlog.lo eventlog_free.lo logwrap.lo
IOBJS = $(LIBEVENTLOG_OBJS:.lo=.i)
@@ -213,6 +213,18 @@ eventlog.i: $(srcdir)/eventlog.c $(incdir)/compat/stdbool.h \
$(CC) -E -o $@ $(CPPFLAGS) $<
eventlog.plog: eventlog.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/eventlog.c --i-file $< --output-file $@
+eventlog_free.lo: $(srcdir)/eventlog_free.c $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
+ $(incdir)/sudo_eventlog.h $(incdir)/sudo_queue.h \
+ $(incdir)/sudo_util.h $(top_builddir)/config.h
+ $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/eventlog_free.c
+eventlog_free.i: $(srcdir)/eventlog_free.c $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
+ $(incdir)/sudo_eventlog.h $(incdir)/sudo_queue.h \
+ $(incdir)/sudo_util.h $(top_builddir)/config.h
+ $(CC) -E -o $@ $(CPPFLAGS) $<
+eventlog_free.plog: eventlog_free.i
+ rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/eventlog_free.c --i-file $< --output-file $@
logwrap.lo: $(srcdir)/logwrap.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_eventlog.h $(incdir)/sudo_queue.h \
diff --git a/lib/eventlog/eventlog.c b/lib/eventlog/eventlog.c
index e6f744da5..c8c9b7ba5 100644
--- a/lib/eventlog/eventlog.c
+++ b/lib/eventlog/eventlog.c
@@ -1256,43 +1256,6 @@ eventlog_alert(const struct eventlog *evlog, int flags,
debug_return_bool(ret);
}
-/*
- * Free the strings in a struct eventlog.
- */
-void
-eventlog_free(struct eventlog *evlog)
-{
- int i;
- debug_decl(eventlog_free, SUDO_DEBUG_UTIL);
-
- if (evlog != NULL) {
- free(evlog->iolog_path);
- free(evlog->command);
- free(evlog->cwd);
- free(evlog->runchroot);
- free(evlog->runcwd);
- free(evlog->rungroup);
- free(evlog->runuser);
- free(evlog->submithost);
- free(evlog->submituser);
- free(evlog->submitgroup);
- free(evlog->ttyname);
- if (evlog->argv != NULL) {
- for (i = 0; evlog->argv[i] != NULL; i++)
- free(evlog->argv[i]);
- free(evlog->argv);
- }
- if (evlog->envp != NULL) {
- for (i = 0; evlog->envp[i] != NULL; i++)
- free(evlog->envp[i]);
- free(evlog->envp);
- }
- free(evlog);
- }
-
- debug_return;
-}
-
static FILE *
eventlog_stub_open_log(int type, const char *logfile)
{
diff --git a/lib/eventlog/eventlog_free.c b/lib/eventlog/eventlog_free.c
new file mode 100644
index 000000000..49583b61c
--- /dev/null
+++ b/lib/eventlog/eventlog_free.c
@@ -0,0 +1,73 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2020 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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sudo_compat.h"
+#include "sudo_debug.h"
+#include "sudo_eventlog.h"
+#include "sudo_util.h"
+
+/*
+ * Free the strings in a struct eventlog.
+ */
+void
+eventlog_free(struct eventlog *evlog)
+{
+ int i;
+ debug_decl(eventlog_free, SUDO_DEBUG_UTIL);
+
+ if (evlog != NULL) {
+ free(evlog->iolog_path);
+ free(evlog->command);
+ free(evlog->cwd);
+ free(evlog->runchroot);
+ free(evlog->runcwd);
+ free(evlog->rungroup);
+ free(evlog->runuser);
+ free(evlog->submithost);
+ free(evlog->submituser);
+ free(evlog->submitgroup);
+ free(evlog->ttyname);
+ if (evlog->argv != NULL) {
+ for (i = 0; evlog->argv[i] != NULL; i++)
+ free(evlog->argv[i]);
+ free(evlog->argv);
+ }
+ if (evlog->envp != NULL) {
+ for (i = 0; evlog->envp[i] != NULL; i++)
+ free(evlog->envp[i]);
+ free(evlog->envp);
+ }
+ free(evlog);
+ }
+
+ debug_return;
+}
--
2.43.0

View File

@ -1,884 +0,0 @@
From 412858272365f0e6d6f8873e794f64374da8fa06 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Wed, 24 Feb 2021 14:25:39 -0700
Subject: [PATCH] Move eventlog config code into eventlog_conf.c
---
MANIFEST | 1 +
include/sudo_eventlog.h | 3 +-
lib/eventlog/Makefile.in | 20 ++-
lib/eventlog/eventlog.c | 292 +++++++----------------------------
lib/eventlog/eventlog_conf.c | 226 +++++++++++++++++++++++++++
5 files changed, 303 insertions(+), 239 deletions(-)
create mode 100644 lib/eventlog/eventlog_conf.c
diff --git a/MANIFEST b/MANIFEST
index a2bed131d..c960e3e8b 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -104,6 +104,7 @@ include/sudo_util.h
install-sh
lib/eventlog/Makefile.in
lib/eventlog/eventlog.c
+lib/eventlog/eventlog_conf.c
lib/eventlog/eventlog_free.c
lib/eventlog/logwrap.c
lib/eventlog/regress/logwrap/check_wrap.c
diff --git a/include/sudo_eventlog.h b/include/sudo_eventlog.h
index 127ee5dd8..e40622a51 100644
--- a/include/sudo_eventlog.h
+++ b/include/sudo_eventlog.h
@@ -66,7 +66,7 @@ enum eventlog_format {
#define EVENTLOG_INDENT " "
/*
- * Event log config, used with eventlog_setconf()
+ * Event log config, used with eventlog_getconf()
*/
struct eventlog_config {
int type;
@@ -144,5 +144,6 @@ void eventlog_set_mailto(const char *to_addr);
void eventlog_set_mailsub(const char *subject);
void eventlog_set_open_log(FILE *(*fn)(int type, const char *));
void eventlog_set_close_log(void (*fn)(int type, FILE *));
+const struct eventlog_config *eventlog_getconf(void);
#endif /* SUDO_EVENTLOG_H */
diff --git a/lib/eventlog/Makefile.in b/lib/eventlog/Makefile.in
index 24c2dbce9..02236f70b 100644
--- a/lib/eventlog/Makefile.in
+++ b/lib/eventlog/Makefile.in
@@ -82,7 +82,7 @@ SHELL = @SHELL@
TEST_PROGS = check_wrap
-LIBEVENTLOG_OBJS = eventlog.lo eventlog_free.lo logwrap.lo
+LIBEVENTLOG_OBJS = eventlog.lo eventlog_conf.lo eventlog_free.lo logwrap.lo
IOBJS = $(LIBEVENTLOG_OBJS:.lo=.i)
@@ -213,6 +213,24 @@ eventlog.i: $(srcdir)/eventlog.c $(incdir)/compat/stdbool.h \
$(CC) -E -o $@ $(CPPFLAGS) $<
eventlog.plog: eventlog.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/eventlog.c --i-file $< --output-file $@
+eventlog_conf.lo: $(srcdir)/eventlog_conf.c $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
+ $(incdir)/sudo_eventlog.h $(incdir)/sudo_fatal.h \
+ $(incdir)/sudo_gettext.h $(incdir)/sudo_json.h \
+ $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
+ $(incdir)/sudo_util.h $(top_builddir)/config.h \
+ $(top_builddir)/pathnames.h
+ $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/eventlog_conf.c
+eventlog_conf.i: $(srcdir)/eventlog_conf.c $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
+ $(incdir)/sudo_eventlog.h $(incdir)/sudo_fatal.h \
+ $(incdir)/sudo_gettext.h $(incdir)/sudo_json.h \
+ $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \
+ $(incdir)/sudo_util.h $(top_builddir)/config.h \
+ $(top_builddir)/pathnames.h
+ $(CC) -E -o $@ $(CPPFLAGS) $<
+eventlog_conf.plog: eventlog_conf.i
+ rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/eventlog_conf.c --i-file $< --output-file $@
eventlog_free.lo: $(srcdir)/eventlog_free.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_eventlog.h $(incdir)/sudo_queue.h \
diff --git a/lib/eventlog/eventlog.c b/lib/eventlog/eventlog.c
index c8c9b7ba5..e7505d9d9 100644
--- a/lib/eventlog/eventlog.c
+++ b/lib/eventlog/eventlog.c
@@ -75,35 +75,6 @@
isalnum((unsigned char)(s)[6]) && isalnum((unsigned char)(s)[7]) && \
(s)[8] == '\0')
-static FILE *eventlog_stub_open_log(int type, const char *logfile);
-static void eventlog_stub_close_log(int type, FILE *fp);
-
-/* Eventlog config settings (default values). */
-static struct eventlog_config evl_conf = {
- EVLOG_NONE, /* type */
- EVLOG_SUDO, /* format */
- LOG_NOTICE, /* syslog_acceptpri */
- LOG_ALERT, /* syslog_rejectpri */
- LOG_ALERT, /* syslog_alertpri */
- MAXSYSLOGLEN, /* syslog_maxlen */
- 0, /* file_maxlen */
- ROOT_UID, /* mailuid */
- false, /* omit_hostname */
- _PATH_SUDO_LOGFILE, /* logpath */
- "%h %e %T", /* time_fmt */
-#ifdef _PATH_SUDO_SENDMAIL
- _PATH_SUDO_SENDMAIL, /* mailerpath */
-#else
- NULL, /* mailerpath (disabled) */
-#endif
- "-t", /* mailerflags */
- NULL, /* mailfrom */
- MAILTO, /* mailto */
- N_(MAILSUBJECT), /* mailsub */
- eventlog_stub_open_log, /* open_log */
- eventlog_stub_close_log /* close_log */
-};
-
/*
* Allocate and fill in a new logline.
*/
@@ -111,6 +82,7 @@ static char *
new_logline(int flags, const char *message, const char *errstr,
const struct eventlog *evlog)
{
+ const struct eventlog_config *evl_conf = eventlog_getconf();
char *line = NULL, *evstr = NULL;
const char *iolog_file = evlog->iolog_file;
const char *tty, *tsid = NULL;
@@ -159,7 +131,7 @@ new_logline(int flags, const char *message, const char *errstr,
len += strlen(message) + 3;
if (errstr != NULL)
len += strlen(errstr) + 3;
- if (evlog->submithost != NULL && !evl_conf.omit_hostname)
+ if (evlog->submithost != NULL && !evl_conf->omit_hostname)
len += sizeof(LL_HOST_STR) + 2 + strlen(evlog->submithost);
if (tty != NULL)
len += sizeof(LL_TTY_STR) + 2 + strlen(tty);
@@ -218,7 +190,7 @@ new_logline(int flags, const char *message, const char *errstr,
strlcat(line, " ; ", len) >= len)
goto toobig;
}
- if (evlog->submithost != NULL && !evl_conf.omit_hostname) {
+ if (evlog->submithost != NULL && !evl_conf->omit_hostname) {
if (strlcat(line, LL_HOST_STR, len) >= len ||
strlcat(line, evlog->submithost, len) >= len ||
strlcat(line, " ; ", len) >= len)
@@ -331,8 +303,9 @@ closefrom_nodebug(int lowfd)
static void __attribute__((__noreturn__))
exec_mailer(int pipein)
{
+ const struct eventlog_config *evl_conf = eventlog_getconf();
char *last, *mflags, *p, *argv[MAX_MAILFLAGS + 1];
- const char *mpath = evl_conf.mailerpath;
+ const char *mpath = evl_conf->mailerpath;
int i;
char * const root_envp[] = {
"HOME=/",
@@ -356,7 +329,7 @@ exec_mailer(int pipein)
}
/* Build up an argv based on the mailer path and flags */
- if ((mflags = strdup(evl_conf.mailerflags)) == NULL) {
+ if ((mflags = strdup(evl_conf->mailerflags)) == NULL) {
syslog(LOG_ERR, _("unable to allocate memory")); // -V618
sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
_exit(127);
@@ -379,14 +352,14 @@ exec_mailer(int pipein)
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to change uid to %u",
ROOT_UID);
}
- if (evl_conf.mailuid != ROOT_UID) {
- if (setuid(evl_conf.mailuid) != 0) {
+ if (evl_conf->mailuid != ROOT_UID) {
+ if (setuid(evl_conf->mailuid) != 0) {
sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to change uid to %u",
- (unsigned int)evl_conf.mailuid);
+ (unsigned int)evl_conf->mailuid);
}
}
sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
- if (evl_conf.mailuid == ROOT_UID)
+ if (evl_conf->mailuid == ROOT_UID)
execve(mpath, argv, root_envp);
else
execv(mpath, argv);
@@ -400,7 +373,8 @@ exec_mailer(int pipein)
static bool
send_mail(const struct eventlog *evlog, const char *fmt, ...)
{
- const char *cp, *timefmt = evl_conf.time_fmt;
+ const struct eventlog_config *evl_conf = eventlog_getconf();
+ const char *cp, *timefmt = evl_conf->time_fmt;
char timebuf[1024];
struct tm *tm;
time_t now;
@@ -415,11 +389,11 @@ send_mail(const struct eventlog *evlog, const char *fmt, ...)
debug_decl(send_mail, SUDO_DEBUG_UTIL);
/* If mailer is disabled just return. */
- if (evl_conf.mailerpath == NULL || evl_conf.mailto == NULL)
+ if (evl_conf->mailerpath == NULL || evl_conf->mailto == NULL)
debug_return_bool(true);
/* Make sure the mailer exists and is a regular file. */
- if (stat(evl_conf.mailerpath, &sb) != 0 || !S_ISREG(sb.st_mode))
+ if (stat(evl_conf->mailerpath, &sb) != 0 || !S_ISREG(sb.st_mode))
debug_return_bool(false);
time(&now);
@@ -516,11 +490,11 @@ send_mail(const struct eventlog *evlog, const char *fmt, ...)
/* Pipes are all setup, send message. */
(void) fprintf(mail, "To: %s\nFrom: %s\nAuto-Submitted: %s\nSubject: ",
- evl_conf.mailto,
- evl_conf.mailfrom ? evl_conf.mailfrom :
+ evl_conf->mailto,
+ evl_conf->mailfrom ? evl_conf->mailfrom :
(evlog ? evlog->submituser : "root"),
"auto-generated");
- for (cp = _(evl_conf.mailsub); *cp; cp++) {
+ for (cp = _(evl_conf->mailsub); *cp; cp++) {
/* Expand escapes in the subject */
if (*cp == '%' && *(cp+1) != '%') {
switch (*(++cp)) {
@@ -576,7 +550,8 @@ static bool
json_add_timestamp(struct json_container *json, const char *name,
const struct timespec *ts)
{
- const char *timefmt = evl_conf.time_fmt;
+ const struct eventlog_config *evl_conf = eventlog_getconf();
+ const char *timefmt = evl_conf->time_fmt;
struct json_value json_value;
time_t secs = ts->tv_sec;
char timebuf[1024];
@@ -880,12 +855,13 @@ bad:
static bool
do_syslog_sudo(int pri, char *logline, const struct eventlog *evlog)
{
+ const struct eventlog_config *evl_conf = eventlog_getconf();
size_t len, maxlen;
char *p, *tmp, save;
const char *fmt;
debug_decl(do_syslog_sudo, SUDO_DEBUG_UTIL);
- evl_conf.open_log(EVLOG_SYSLOG, NULL);
+ evl_conf->open_log(EVLOG_SYSLOG, NULL);
if (evlog == NULL) {
/* Not a command, just log it as-is. */
@@ -897,7 +873,7 @@ do_syslog_sudo(int pri, char *logline, const struct eventlog *evlog)
* Log the full line, breaking into multiple syslog(3) calls if necessary
*/
fmt = _("%8s : %s");
- maxlen = evl_conf.syslog_maxlen -
+ maxlen = evl_conf->syslog_maxlen -
(strlen(fmt) - 5 + strlen(evlog->submituser));
for (p = logline; *p != '\0'; ) {
len = strlen(p);
@@ -926,11 +902,11 @@ do_syslog_sudo(int pri, char *logline, const struct eventlog *evlog)
p += len;
}
fmt = _("%8s : (command continued) %s");
- maxlen = evl_conf.syslog_maxlen -
+ maxlen = evl_conf->syslog_maxlen -
(strlen(fmt) - 5 + strlen(evlog->submituser));
}
done:
- evl_conf.close_log(EVLOG_SYSLOG, NULL);
+ evl_conf->close_log(EVLOG_SYSLOG, NULL);
debug_return_bool(true);
}
@@ -941,6 +917,7 @@ do_syslog_json(int pri, int event_type, const char *reason,
const struct timespec *event_time,
eventlog_json_callback_t info_cb, void *info)
{
+ const struct eventlog_config *evl_conf = eventlog_getconf();
char *json_str;
debug_decl(do_syslog_json, SUDO_DEBUG_UTIL);
@@ -951,10 +928,10 @@ do_syslog_json(int pri, int event_type, const char *reason,
debug_return_bool(false);
/* Syslog it in a sudo object with a @cee: prefix. */
- /* TODO: use evl_conf.syslog_maxlen to break up long messages. */
- evl_conf.open_log(EVLOG_SYSLOG, NULL);
+ /* TODO: use evl_conf->syslog_maxlen to break up long messages. */
+ evl_conf->open_log(EVLOG_SYSLOG, NULL);
syslog(pri, "@cee:{\"sudo\":{%s}}", json_str);
- evl_conf.close_log(EVLOG_SYSLOG, NULL);
+ evl_conf->close_log(EVLOG_SYSLOG, NULL);
free(json_str);
debug_return_bool(true);
}
@@ -967,13 +944,14 @@ do_syslog(int event_type, int flags, const char *reason, const char *errstr,
const struct eventlog *evlog, const struct timespec *event_time,
eventlog_json_callback_t info_cb, void *info)
{
+ const struct eventlog_config *evl_conf = eventlog_getconf();
char *logline = NULL;
bool ret = false;
int pri;
debug_decl(do_syslog, SUDO_DEBUG_UTIL);
/* Sudo format logs and mailed logs use the same log line format. */
- if (evl_conf.format == EVLOG_SUDO || ISSET(flags, EVLOG_MAIL)) {
+ if (evl_conf->format == EVLOG_SUDO || ISSET(flags, EVLOG_MAIL)) {
logline = new_logline(flags, reason, errstr, evlog);
if (logline == NULL)
debug_return_bool(false);
@@ -992,13 +970,13 @@ do_syslog(int event_type, int flags, const char *reason, const char *errstr,
switch (event_type) {
case EVLOG_ACCEPT:
- pri = evl_conf.syslog_acceptpri;
+ pri = evl_conf->syslog_acceptpri;
break;
case EVLOG_REJECT:
- pri = evl_conf.syslog_rejectpri;
+ pri = evl_conf->syslog_rejectpri;
break;
case EVLOG_ALERT:
- pri = evl_conf.syslog_alertpri;
+ pri = evl_conf->syslog_alertpri;
break;
default:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
@@ -1012,7 +990,7 @@ do_syslog(int event_type, int flags, const char *reason, const char *errstr,
debug_return_bool(true);
}
- switch (evl_conf.format) {
+ switch (evl_conf->format) {
case EVLOG_SUDO:
ret = do_syslog_sudo(pri, logline, evlog);
break;
@@ -1022,7 +1000,7 @@ do_syslog(int event_type, int flags, const char *reason, const char *errstr,
break;
default:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
- "unexpected eventlog format %d", evl_conf.format);
+ "unexpected eventlog format %d", evl_conf->format);
break;
}
free(logline);
@@ -1034,9 +1012,10 @@ static bool
do_logfile_sudo(const char *logline, const struct eventlog *evlog,
const struct timespec *event_time)
{
+ const struct eventlog_config *evl_conf = eventlog_getconf();
char *full_line, timebuf[8192], *timestr = NULL;
- const char *timefmt = evl_conf.time_fmt;
- const char *logfile = evl_conf.logpath;
+ const char *timefmt = evl_conf->time_fmt;
+ const char *logfile = evl_conf->logpath;
time_t tv_sec = event_time->tv_sec;
struct tm *timeptr;
bool ret = false;
@@ -1044,7 +1023,7 @@ do_logfile_sudo(const char *logline, const struct eventlog *evlog,
int len;
debug_decl(do_logfile_sudo, SUDO_DEBUG_UTIL);
- if ((fp = evl_conf.open_log(EVLOG_FILE, logfile)) == NULL)
+ if ((fp = evl_conf->open_log(EVLOG_FILE, logfile)) == NULL)
debug_return_bool(false);
if (!sudo_lock_file(fileno(fp), SUDO_LOCK)) {
@@ -1067,7 +1046,7 @@ do_logfile_sudo(const char *logline, const struct eventlog *evlog,
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
}
- eventlog_writeln(fp, full_line, len, evl_conf.file_maxlen);
+ eventlog_writeln(fp, full_line, len, evl_conf->file_maxlen);
(void)fflush(fp);
if (ferror(fp)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
@@ -1078,7 +1057,7 @@ do_logfile_sudo(const char *logline, const struct eventlog *evlog,
done:
(void)sudo_lock_file(fileno(fp), SUDO_UNLOCK);
- evl_conf.close_log(EVLOG_FILE, fp);
+ evl_conf->close_log(EVLOG_FILE, fp);
debug_return_bool(ret);
}
@@ -1087,14 +1066,15 @@ do_logfile_json(int event_type, const char *reason, const char *errstr,
const struct eventlog *evlog, const struct timespec *event_time,
eventlog_json_callback_t info_cb, void *info)
{
- const char *logfile = evl_conf.logpath;
+ const struct eventlog_config *evl_conf = eventlog_getconf();
+ const char *logfile = evl_conf->logpath;
struct stat sb;
char *json_str;
int ret = false;
FILE *fp;
debug_decl(do_logfile_json, SUDO_DEBUG_UTIL);
- if ((fp = evl_conf.open_log(EVLOG_FILE, logfile)) == NULL)
+ if ((fp = evl_conf->open_log(EVLOG_FILE, logfile)) == NULL)
debug_return_bool(false);
json_str = format_json(event_type, reason, errstr, evlog, event_time,
@@ -1135,7 +1115,7 @@ do_logfile_json(int event_type, const char *reason, const char *errstr,
done:
free(json_str);
(void)sudo_lock_file(fileno(fp), SUDO_UNLOCK);
- evl_conf.close_log(EVLOG_FILE, fp);
+ evl_conf->close_log(EVLOG_FILE, fp);
debug_return_bool(ret);
}
@@ -1144,12 +1124,13 @@ do_logfile(int event_type, int flags, const char *reason, const char *errstr,
const struct eventlog *evlog, const struct timespec *event_time,
eventlog_json_callback_t info_cb, void *info)
{
+ const struct eventlog_config *evl_conf = eventlog_getconf();
bool ret = false;
char *logline = NULL;
debug_decl(do_logfile, SUDO_DEBUG_UTIL);
/* Sudo format logs and mailed logs use the same log line format. */
- if (evl_conf.format == EVLOG_SUDO || ISSET(flags, EVLOG_MAIL)) {
+ if (evl_conf->format == EVLOG_SUDO || ISSET(flags, EVLOG_MAIL)) {
logline = new_logline(flags, reason, errstr, evlog);
if (logline == NULL)
debug_return_bool(false);
@@ -1166,7 +1147,7 @@ do_logfile(int event_type, int flags, const char *reason, const char *errstr,
}
}
- switch (evl_conf.format) {
+ switch (evl_conf->format) {
case EVLOG_SUDO:
ret = do_logfile_sudo(logline ? logline : reason, evlog, event_time);
break;
@@ -1176,7 +1157,7 @@ do_logfile(int event_type, int flags, const char *reason, const char *errstr,
break;
default:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
- "unexpected eventlog format %d", evl_conf.format);
+ "unexpected eventlog format %d", evl_conf->format);
break;
}
free(logline);
@@ -1188,7 +1169,8 @@ bool
eventlog_accept(const struct eventlog *evlog, int flags,
eventlog_json_callback_t info_cb, void *info)
{
- const int log_type = evl_conf.type;
+ const struct eventlog_config *evl_conf = eventlog_getconf();
+ const int log_type = evl_conf->type;
bool ret = true;
debug_decl(log_accept, SUDO_DEBUG_UTIL);
@@ -1214,7 +1196,8 @@ bool
eventlog_reject(const struct eventlog *evlog, int flags, const char *reason,
eventlog_json_callback_t info_cb, void *info)
{
- const int log_type = evl_conf.type;
+ const struct eventlog_config *evl_conf = eventlog_getconf();
+ const int log_type = evl_conf->type;
bool ret = true;
debug_decl(log_reject, SUDO_DEBUG_UTIL);
@@ -1237,7 +1220,8 @@ bool
eventlog_alert(const struct eventlog *evlog, int flags,
struct timespec *alert_time, const char *reason, const char *errstr)
{
- const int log_type = evl_conf.type;
+ const struct eventlog_config *evl_conf = eventlog_getconf();
+ const int log_type = evl_conf->type;
bool ret = true;
debug_decl(log_alert, SUDO_DEBUG_UTIL);
@@ -1255,169 +1239,3 @@ eventlog_alert(const struct eventlog *evlog, int flags,
debug_return_bool(ret);
}
-
-static FILE *
-eventlog_stub_open_log(int type, const char *logfile)
-{
- debug_decl(eventlog_stub_open_log, SUDO_DEBUG_UTIL);
- sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
- "open_log not set, using stub");
- debug_return_ptr(NULL);
-}
-
-static void
-eventlog_stub_close_log(int type, FILE *fp)
-{
- debug_decl(eventlog_stub_close_log, SUDO_DEBUG_UTIL);
- sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
- "close_log not set, using stub");
- debug_return;
-}
-
-/*
- * Set eventlog config settings.
- */
-
-void
-eventlog_set_type(int type)
-{
- evl_conf.type = type;
-}
-
-void
-eventlog_set_format(enum eventlog_format format)
-{
- evl_conf.format = format;
-}
-
-void
-eventlog_set_syslog_acceptpri(int pri)
-{
- evl_conf.syslog_acceptpri = pri;
-}
-
-void
-eventlog_set_syslog_rejectpri(int pri)
-{
- evl_conf.syslog_rejectpri = pri;
-}
-
-void
-eventlog_set_syslog_alertpri(int pri)
-{
- evl_conf.syslog_alertpri = pri;
-}
-
-void
-eventlog_set_syslog_maxlen(int len)
-{
- evl_conf.syslog_maxlen = len;
-}
-
-void
-eventlog_set_file_maxlen(int len)
-{
- evl_conf.file_maxlen = len;
-}
-
-void
-eventlog_set_mailuid(uid_t uid)
-{
- evl_conf.mailuid = uid;
-}
-
-void
-eventlog_set_omit_hostname(bool omit_hostname)
-{
- evl_conf.omit_hostname = omit_hostname;
-}
-
-void
-eventlog_set_logpath(const char *path)
-{
- evl_conf.logpath = path;
-}
-
-void
-eventlog_set_time_fmt(const char *fmt)
-{
- evl_conf.time_fmt = fmt;
-}
-
-void
-eventlog_set_mailerpath(const char *path)
-{
- evl_conf.mailerpath = path;
-}
-
-void
-eventlog_set_mailerflags(const char *mflags)
-{
- evl_conf.mailerflags = mflags;
-}
-
-void
-eventlog_set_mailfrom(const char *from_addr)
-{
- evl_conf.mailfrom = from_addr;
-}
-
-void
-eventlog_set_mailto(const char *to_addr)
-{
- evl_conf.mailto = to_addr;
-}
-
-void
-eventlog_set_mailsub(const char *subject)
-{
- evl_conf.mailsub = subject;
-}
-
-void
-eventlog_set_open_log(FILE *(*fn)(int type, const char *))
-{
- evl_conf.open_log = fn;
-}
-
-void
-eventlog_set_close_log(void (*fn)(int type, FILE *))
-{
- evl_conf.close_log = fn;
-}
-
-bool
-eventlog_setconf(struct eventlog_config *conf)
-{
- debug_decl(eventlog_setconf, SUDO_DEBUG_UTIL);
-
- if (conf != NULL) {
- memcpy(&evl_conf, conf, sizeof(evl_conf));
- } else {
- memset(&evl_conf, 0, sizeof(evl_conf));
- }
-
- /* Apply default values where possible. */
- if (evl_conf.syslog_maxlen == 0)
- evl_conf.syslog_maxlen = MAXSYSLOGLEN;
- if (evl_conf.logpath == NULL)
- evl_conf.logpath = _PATH_SUDO_LOGFILE;
- if (evl_conf.time_fmt == NULL)
- evl_conf.time_fmt = "%h %e %T";
-#ifdef _PATH_SUDO_SENDMAIL
- if (evl_conf.mailerpath == NULL)
- evl_conf.mailerpath = _PATH_SUDO_SENDMAIL;
-#endif
- if (evl_conf.mailerflags == NULL)
- evl_conf.mailerflags = "-t";
- if (evl_conf.mailto == NULL)
- evl_conf.mailto = MAILTO;
- if (evl_conf.mailsub == NULL)
- evl_conf.mailsub = N_(MAILSUBJECT);
- if (evl_conf.open_log == NULL)
- evl_conf.open_log = eventlog_stub_open_log;
- if (evl_conf.close_log == NULL)
- evl_conf.close_log = eventlog_stub_close_log;
-
- debug_return_bool(true);
-}
diff --git a/lib/eventlog/eventlog_conf.c b/lib/eventlog/eventlog_conf.c
new file mode 100644
index 000000000..8ad03851f
--- /dev/null
+++ b/lib/eventlog/eventlog_conf.c
@@ -0,0 +1,226 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1994-1996, 1998-2020 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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <locale.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+#include "sudo_compat.h"
+#include "sudo_debug.h"
+#include "sudo_eventlog.h"
+#include "sudo_fatal.h"
+#include "sudo_gettext.h"
+#include "sudo_json.h"
+#include "sudo_queue.h"
+#include "sudo_util.h"
+
+static FILE *eventlog_stub_open_log(int type, const char *logfile);
+static void eventlog_stub_close_log(int type, FILE *fp);
+
+/* Eventlog config settings (default values). */
+static struct eventlog_config evl_conf = {
+ EVLOG_NONE, /* type */
+ EVLOG_SUDO, /* format */
+ LOG_NOTICE, /* syslog_acceptpri */
+ LOG_ALERT, /* syslog_rejectpri */
+ LOG_ALERT, /* syslog_alertpri */
+ MAXSYSLOGLEN, /* syslog_maxlen */
+ 0, /* file_maxlen */
+ ROOT_UID, /* mailuid */
+ false, /* omit_hostname */
+ _PATH_SUDO_LOGFILE, /* logpath */
+ "%h %e %T", /* time_fmt */
+#ifdef _PATH_SUDO_SENDMAIL
+ _PATH_SUDO_SENDMAIL, /* mailerpath */
+#else
+ NULL, /* mailerpath (disabled) */
+#endif
+ "-t", /* mailerflags */
+ NULL, /* mailfrom */
+ MAILTO, /* mailto */
+ N_(MAILSUBJECT), /* mailsub */
+ eventlog_stub_open_log, /* open_log */
+ eventlog_stub_close_log /* close_log */
+};
+
+static FILE *
+eventlog_stub_open_log(int type, const char *logfile)
+{
+ debug_decl(eventlog_stub_open_log, SUDO_DEBUG_UTIL);
+ sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+ "open_log not set, using stub");
+ debug_return_ptr(NULL);
+}
+
+static void
+eventlog_stub_close_log(int type, FILE *fp)
+{
+ debug_decl(eventlog_stub_close_log, SUDO_DEBUG_UTIL);
+ sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+ "close_log not set, using stub");
+ debug_return;
+}
+
+/*
+ * eventlog config setters.
+ */
+
+void
+eventlog_set_type(int type)
+{
+ evl_conf.type = type;
+}
+
+void
+eventlog_set_format(enum eventlog_format format)
+{
+ evl_conf.format = format;
+}
+
+void
+eventlog_set_syslog_acceptpri(int pri)
+{
+ evl_conf.syslog_acceptpri = pri;
+}
+
+void
+eventlog_set_syslog_rejectpri(int pri)
+{
+ evl_conf.syslog_rejectpri = pri;
+}
+
+void
+eventlog_set_syslog_alertpri(int pri)
+{
+ evl_conf.syslog_alertpri = pri;
+}
+
+void
+eventlog_set_syslog_maxlen(int len)
+{
+ evl_conf.syslog_maxlen = len;
+}
+
+void
+eventlog_set_file_maxlen(int len)
+{
+ evl_conf.file_maxlen = len;
+}
+
+void
+eventlog_set_mailuid(uid_t uid)
+{
+ evl_conf.mailuid = uid;
+}
+
+void
+eventlog_set_omit_hostname(bool omit_hostname)
+{
+ evl_conf.omit_hostname = omit_hostname;
+}
+
+void
+eventlog_set_logpath(const char *path)
+{
+ evl_conf.logpath = path;
+}
+
+void
+eventlog_set_time_fmt(const char *fmt)
+{
+ evl_conf.time_fmt = fmt;
+}
+
+void
+eventlog_set_mailerpath(const char *path)
+{
+ evl_conf.mailerpath = path;
+}
+
+void
+eventlog_set_mailerflags(const char *mflags)
+{
+ evl_conf.mailerflags = mflags;
+}
+
+void
+eventlog_set_mailfrom(const char *from_addr)
+{
+ evl_conf.mailfrom = from_addr;
+}
+
+void
+eventlog_set_mailto(const char *to_addr)
+{
+ evl_conf.mailto = to_addr;
+}
+
+void
+eventlog_set_mailsub(const char *subject)
+{
+ evl_conf.mailsub = subject;
+}
+
+void
+eventlog_set_open_log(FILE *(*fn)(int type, const char *))
+{
+ evl_conf.open_log = fn;
+}
+
+void
+eventlog_set_close_log(void (*fn)(int type, FILE *))
+{
+ evl_conf.close_log = fn;
+}
+
+/*
+ * get eventlog config.
+ */
+const struct eventlog_config *
+eventlog_getconf(void)
+{
+ return &evl_conf;
+}
--
2.43.0

View File

@ -1,66 +0,0 @@
From d452678787683da6498668cd1f1cbb8000d63178 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Tue, 2 Mar 2021 18:37:35 -0700
Subject: [PATCH] Log peer address in sudo_logsrvd JSON-format logs. The peer
that connected to us might not be the same host where the log entry
originated.
---
include/sudo_eventlog.h | 3 ++-
lib/eventlog/eventlog.c | 9 ++++++++-
logsrvd/iolog_writer.c | 8 ++++++--
logsrvd/logsrvd.c | 9 +++++----
logsrvd/logsrvd.h | 4 ++--
5 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/include/sudo_eventlog.h b/include/sudo_eventlog.h
index e40622a51..49153e173 100644
--- a/include/sudo_eventlog.h
+++ b/include/sudo_eventlog.h
@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
- * Copyright (c) 2020 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2020-2021 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
@@ -101,6 +101,7 @@ struct eventlog {
char *runcwd;
char *rungroup;
char *runuser;
+ char *peeraddr;
char *submithost;
char *submituser;
char *submitgroup;
diff --git a/lib/eventlog/eventlog.c b/lib/eventlog/eventlog.c
index e7505d9d9..0c1e74e38 100644
--- a/lib/eventlog/eventlog.c
+++ b/lib/eventlog/eventlog.c
@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
- * Copyright (c) 1994-1996, 1998-2020 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 1994-1996, 1998-2021 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
@@ -825,6 +825,13 @@ format_json(int event_type, const char *reason, const char *errstr,
/* Event log info may be missing for alert messages. */
if (evlog != NULL) {
+ if (evlog->peeraddr != NULL) {
+ json_value.type = JSON_STRING;
+ json_value.u.string = evlog->peeraddr;
+ if (!sudo_json_add_value(&json, "peeraddr", &json_value))
+ goto bad;
+ }
+
if (evlog->iolog_path != NULL) {
json_value.type = JSON_STRING;
json_value.u.string = evlog->iolog_path;
--
2.43.0

View File

@ -1,559 +0,0 @@
From b54a16e1749fc53e4ed047da72f97b8f99bf4d6a Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Fri, 9 Jul 2021 11:08:44 -0600
Subject: [PATCH] Add support for logging exit status events. For
sudo-formatted logs, this is a record with "EXIT=number" and potentially
"SIGNAL=name" after the command. For JSON-format logs, a new "exit" record
is logged which contains an "exit_value" and potentially "signal" and
"core_dumped". JSON-format logs now incude a UUID to associate the "exit"
record with the "accept" record.
---
include/sudo_eventlog.h | 2 +
lib/eventlog/eventlog.c | 255 +++++++++++++++++++++++++++-------------
2 files changed, 175 insertions(+), 82 deletions(-)
diff --git a/include/sudo_eventlog.h b/include/sudo_eventlog.h
index 49153e173..07ef9dcbe 100644
--- a/include/sudo_eventlog.h
+++ b/include/sudo_eventlog.h
@@ -31,6 +31,7 @@
enum event_type {
EVLOG_ACCEPT,
EVLOG_REJECT,
+ EVLOG_EXIT,
EVLOG_ALERT
};
@@ -122,6 +123,7 @@ struct json_container;
typedef bool (*eventlog_json_callback_t)(struct json_container *, void *);
bool eventlog_accept(const struct eventlog *evlog, int flags, eventlog_json_callback_t info_cb, void *info);
+bool eventlog_exit(const struct eventlog *evlog, int flags, struct timespec *run_time, int exit_value, const char *signal_name, bool core_dumped, eventlog_json_callback_t info_cb, void *info);
bool eventlog_alert(const struct eventlog *evlog, int flags, struct timespec *alert_time, const char *reason, const char *errstr);
bool eventlog_reject(const struct eventlog *evlog, int flags, const char *reason, eventlog_json_callback_t info_cb, void *info);
bool eventlog_store_json(struct json_container *json, const struct eventlog *evlog);
diff --git a/lib/eventlog/eventlog.c b/lib/eventlog/eventlog.c
index 0c1e74e38..efcd6859d 100644
--- a/lib/eventlog/eventlog.c
+++ b/lib/eventlog/eventlog.c
@@ -66,6 +66,8 @@
#define LL_ENV_STR "ENV="
#define LL_CMND_STR "COMMAND="
#define LL_TSID_STR "TSID="
+#define LL_EXIT_STR "EXIT="
+#define LL_SIGNAL_STR "SIGNAL="
#define IS_SESSID(s) ( \
isalnum((unsigned char)(s)[0]) && isalnum((unsigned char)(s)[1]) && \
@@ -75,28 +77,40 @@
isalnum((unsigned char)(s)[6]) && isalnum((unsigned char)(s)[7]) && \
(s)[8] == '\0')
+struct eventlog_args {
+ const char *reason;
+ const char *errstr;
+ const char *signal_name;
+ const struct timespec *event_time;
+ int exit_value;
+ bool core_dumped;
+ eventlog_json_callback_t json_info_cb;
+ void *json_info;
+};
+
/*
* Allocate and fill in a new logline.
*/
static char *
-new_logline(int flags, const char *message, const char *errstr,
+new_logline(int event_type, int flags, struct eventlog_args *args,
const struct eventlog *evlog)
{
const struct eventlog_config *evl_conf = eventlog_getconf();
char *line = NULL, *evstr = NULL;
const char *iolog_file = evlog->iolog_file;
const char *tty, *tsid = NULL;
+ char exit_str[(((sizeof(int) * 8) + 2) / 3) + 2];
char sessid[7];
size_t len = 0;
int i;
debug_decl(new_logline, SUDO_DEBUG_UTIL);
if (ISSET(flags, EVLOG_RAW)) {
- if (errstr != NULL) {
- if (asprintf(&line, "%s: %s", message, errstr) == -1)
+ if (args->errstr != NULL) {
+ if (asprintf(&line, "%s: %s", args->reason, args->errstr) == -1)
goto oom;
} else {
- if ((line = strdup(message)) == NULL)
+ if ((line = strdup(args->reason)) == NULL)
goto oom;
}
debug_return_str(line);
@@ -127,10 +141,10 @@ new_logline(int flags, const char *message, const char *errstr,
/*
* Compute line length
*/
- if (message != NULL)
- len += strlen(message) + 3;
- if (errstr != NULL)
- len += strlen(errstr) + 3;
+ if (args->reason != NULL)
+ len += strlen(args->reason) + 3;
+ if (args->errstr != NULL)
+ len += strlen(args->errstr) + 3;
if (evlog->submithost != NULL && !evl_conf->omit_hostname)
len += sizeof(LL_HOST_STR) + 2 + strlen(evlog->submithost);
if (tty != NULL)
@@ -171,6 +185,12 @@ new_logline(int flags, const char *message, const char *errstr,
for (i = 1; evlog->argv[i] != NULL; i++)
len += strlen(evlog->argv[i]) + 1;
}
+ if (event_type == EVLOG_EXIT) {
+ if (args->signal_name != NULL)
+ len += sizeof(LL_SIGNAL_STR) + 2 + strlen(args->signal_name);
+ (void)snprintf(exit_str, sizeof(exit_str), "%d", args->exit_value);
+ len += sizeof(LL_EXIT_STR) + 2 + strlen(exit_str);
+ }
}
/*
@@ -180,13 +200,13 @@ new_logline(int flags, const char *message, const char *errstr,
goto oom;
line[0] = '\0';
- if (message != NULL) {
- if (strlcat(line, message, len) >= len ||
- strlcat(line, errstr ? " : " : " ; ", len) >= len)
+ if (args->reason != NULL) {
+ if (strlcat(line, args->reason, len) >= len ||
+ strlcat(line, args->errstr ? " : " : " ; ", len) >= len)
goto toobig;
}
- if (errstr != NULL) {
- if (strlcat(line, errstr, len) >= len ||
+ if (args->errstr != NULL) {
+ if (strlcat(line, args->errstr, len) >= len ||
strlcat(line, " ; ", len) >= len)
goto toobig;
}
@@ -252,6 +272,18 @@ new_logline(int flags, const char *message, const char *errstr,
goto toobig;
}
}
+ if (event_type == EVLOG_EXIT) {
+ if (args->signal_name != NULL) {
+ if (strlcat(line, " ; ", len) >= len ||
+ strlcat(line, LL_SIGNAL_STR, len) >= len ||
+ strlcat(line, args->signal_name, len) >= len)
+ goto toobig;
+ }
+ if (strlcat(line, " ; ", len) >= len ||
+ strlcat(line, LL_EXIT_STR, len) >= len ||
+ strlcat(line, exit_str, len) >= len)
+ goto toobig;
+ }
}
debug_return_str(line);
@@ -369,7 +401,7 @@ exec_mailer(int pipein)
_exit(127);
}
-/* Send a message to MAILTO user */
+/* Send a message to the mailto user */
static bool
send_mail(const struct eventlog *evlog, const char *fmt, ...)
{
@@ -548,19 +580,11 @@ send_mail(const struct eventlog *evlog, const char *fmt, ...)
static bool
json_add_timestamp(struct json_container *json, const char *name,
- const struct timespec *ts)
+ const struct timespec *ts, bool format_timestamp)
{
- const struct eventlog_config *evl_conf = eventlog_getconf();
- const char *timefmt = evl_conf->time_fmt;
struct json_value json_value;
- time_t secs = ts->tv_sec;
- char timebuf[1024];
- struct tm *tm;
debug_decl(json_add_timestamp, SUDO_DEBUG_PLUGIN);
- if ((tm = gmtime(&secs)) == NULL)
- debug_return_bool(false);
-
if (!sudo_json_open_object(json, name))
goto oom;
@@ -574,17 +598,27 @@ json_add_timestamp(struct json_container *json, const char *name,
if (!sudo_json_add_value(json, "nanoseconds", &json_value))
goto oom;
- strftime(timebuf, sizeof(timebuf), "%Y%m%d%H%M%SZ", tm);
- json_value.type = JSON_STRING;
- json_value.u.string = timebuf;
- if (!sudo_json_add_value(json, "iso8601", &json_value))
- goto oom;
+ if (format_timestamp) {
+ const struct eventlog_config *evl_conf = eventlog_getconf();
+ const char *timefmt = evl_conf->time_fmt;
+ time_t secs = ts->tv_sec;
+ char timebuf[1024];
+ struct tm *tm;
- strftime(timebuf, sizeof(timebuf), timefmt, tm);
- json_value.type = JSON_STRING;
- json_value.u.string = timebuf;
- if (!sudo_json_add_value(json, "localtime", &json_value))
- goto oom;
+ if ((tm = gmtime(&secs)) != NULL) {
+ strftime(timebuf, sizeof(timebuf), "%Y%m%d%H%M%SZ", tm);
+ json_value.type = JSON_STRING;
+ json_value.u.string = timebuf;
+ if (!sudo_json_add_value(json, "iso8601", &json_value))
+ goto oom;
+
+ strftime(timebuf, sizeof(timebuf), timefmt, tm);
+ json_value.type = JSON_STRING;
+ json_value.u.string = timebuf;
+ if (!sudo_json_add_value(json, "localtime", &json_value))
+ goto oom;
+ }
+ }
if (!sudo_json_close_object(json))
goto oom;
@@ -741,14 +775,15 @@ default_json_cb(struct json_container *json, void *v)
}
static char *
-format_json(int event_type, const char *reason, const char *errstr,
- const struct eventlog *evlog, const struct timespec *event_time,
- eventlog_json_callback_t info_cb, void *info, bool compact)
+format_json(int event_type, struct eventlog_args *args,
+ const struct eventlog *evlog, bool compact)
{
- const char *type_str;
- const char *time_str;
+ eventlog_json_callback_t info_cb = args->json_info_cb;
+ void *info = args->json_info;
struct json_container json = { 0 };
struct json_value json_value;
+ const char *time_str, *type_str;
+ bool format_timestamp = true;
struct timespec now;
debug_decl(format_json, SUDO_DEBUG_UTIL);
@@ -776,6 +811,11 @@ format_json(int event_type, const char *reason, const char *errstr,
type_str = "alert";
time_str = "alert_time";
break;
+ case EVLOG_EXIT:
+ type_str = "exit";
+ time_str = "run_time";
+ format_timestamp = false;
+ break;
default:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unexpected event type %d", event_type);
@@ -788,18 +828,20 @@ format_json(int event_type, const char *reason, const char *errstr,
goto bad;
/* Reject and Alert events include a reason and optional error string. */
- if (reason != NULL) {
+ if (args->reason != NULL) {
char *ereason = NULL;
- if (errstr != NULL) {
- if (asprintf(&ereason, _("%s: %s"), reason, errstr) == -1) {
+ if (args->errstr != NULL) {
+ const int len = asprintf(&ereason, _("%s: %s"), args->reason,
+ args->errstr);
+ if (len == -1) {
sudo_warnx(U_("%s: %s"), __func__,
U_("unable to allocate memory"));
goto bad;
}
}
json_value.type = JSON_STRING;
- json_value.u.string = ereason ? ereason : reason;
+ json_value.u.string = ereason ? ereason : args->reason;
if (!sudo_json_add_value(&json, "reason", &json_value)) {
free(ereason);
goto bad;
@@ -810,19 +852,37 @@ format_json(int event_type, const char *reason, const char *errstr,
/* XXX - create and log uuid? */
/* Log event time on server (set earlier) */
- if (!json_add_timestamp(&json, "server_time", &now)) {
+ if (!json_add_timestamp(&json, "server_time", &now, true)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
"unable format timestamp");
goto bad;
}
/* Log event time from client */
- if (!json_add_timestamp(&json, time_str, event_time)) {
+ if (!json_add_timestamp(&json, time_str, args->event_time, format_timestamp)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
"unable format timestamp");
goto bad;
}
+ if (event_type == EVLOG_EXIT) {
+ if (args->signal_name != NULL) {
+ json_value.type = JSON_STRING;
+ json_value.u.string = args->signal_name;
+ if (!sudo_json_add_value(&json, "signal", &json_value))
+ goto bad;
+
+ json_value.type = JSON_BOOL;
+ json_value.u.boolean = args->signal_name;
+ if (!sudo_json_add_value(&json, "dumped_core", &json_value))
+ goto bad;
+ }
+ json_value.type = JSON_NUMBER;
+ json_value.u.number = args->exit_value;
+ if (!sudo_json_add_value(&json, "exit_value", &json_value))
+ goto bad;
+ }
+
/* Event log info may be missing for alert messages. */
if (evlog != NULL) {
if (evlog->peeraddr != NULL) {
@@ -919,18 +979,15 @@ done:
}
static bool
-do_syslog_json(int pri, int event_type, const char *reason,
- const char *errstr, const struct eventlog *evlog,
- const struct timespec *event_time,
- eventlog_json_callback_t info_cb, void *info)
+do_syslog_json(int pri, int event_type, struct eventlog_args *args,
+ const struct eventlog *evlog)
{
const struct eventlog_config *evl_conf = eventlog_getconf();
char *json_str;
debug_decl(do_syslog_json, SUDO_DEBUG_UTIL);
/* Format as a compact JSON message (no newlines) */
- json_str = format_json(event_type, reason, errstr, evlog, event_time,
- info_cb, info, true);
+ json_str = format_json(event_type, args, evlog, true);
if (json_str == NULL)
debug_return_bool(false);
@@ -947,9 +1004,8 @@ do_syslog_json(int pri, int event_type, const char *reason,
* Log a message to syslog in either sudo or JSON format.
*/
static bool
-do_syslog(int event_type, int flags, const char *reason, const char *errstr,
- const struct eventlog *evlog, const struct timespec *event_time,
- eventlog_json_callback_t info_cb, void *info)
+do_syslog(int event_type, int flags, struct eventlog_args *args,
+ const struct eventlog *evlog)
{
const struct eventlog_config *evl_conf = eventlog_getconf();
char *logline = NULL;
@@ -959,7 +1015,7 @@ do_syslog(int event_type, int flags, const char *reason, const char *errstr,
/* Sudo format logs and mailed logs use the same log line format. */
if (evl_conf->format == EVLOG_SUDO || ISSET(flags, EVLOG_MAIL)) {
- logline = new_logline(flags, reason, errstr, evlog);
+ logline = new_logline(event_type, flags, args, evlog);
if (logline == NULL)
debug_return_bool(false);
@@ -977,6 +1033,7 @@ do_syslog(int event_type, int flags, const char *reason, const char *errstr,
switch (event_type) {
case EVLOG_ACCEPT:
+ case EVLOG_EXIT:
pri = evl_conf->syslog_acceptpri;
break;
case EVLOG_REJECT:
@@ -1002,8 +1059,7 @@ do_syslog(int event_type, int flags, const char *reason, const char *errstr,
ret = do_syslog_sudo(pri, logline, evlog);
break;
case EVLOG_JSON:
- ret = do_syslog_json(pri, event_type, reason, errstr, evlog,
- event_time, info_cb, info);
+ ret = do_syslog_json(pri, event_type, args, evlog);
break;
default:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
@@ -1069,9 +1125,8 @@ done:
}
static bool
-do_logfile_json(int event_type, const char *reason, const char *errstr,
- const struct eventlog *evlog, const struct timespec *event_time,
- eventlog_json_callback_t info_cb, void *info)
+do_logfile_json(int event_type, struct eventlog_args *args,
+ const struct eventlog *evlog)
{
const struct eventlog_config *evl_conf = eventlog_getconf();
const char *logfile = evl_conf->logpath;
@@ -1084,8 +1139,7 @@ do_logfile_json(int event_type, const char *reason, const char *errstr,
if ((fp = evl_conf->open_log(EVLOG_FILE, logfile)) == NULL)
debug_return_bool(false);
- json_str = format_json(event_type, reason, errstr, evlog, event_time,
- info_cb, info, false);
+ json_str = format_json(event_type, args, evlog, false);
if (json_str == NULL)
goto done;
@@ -1127,9 +1181,8 @@ done:
}
static bool
-do_logfile(int event_type, int flags, const char *reason, const char *errstr,
- const struct eventlog *evlog, const struct timespec *event_time,
- eventlog_json_callback_t info_cb, void *info)
+do_logfile(int event_type, int flags, struct eventlog_args *args,
+ const struct eventlog *evlog)
{
const struct eventlog_config *evl_conf = eventlog_getconf();
bool ret = false;
@@ -1138,7 +1191,7 @@ do_logfile(int event_type, int flags, const char *reason, const char *errstr,
/* Sudo format logs and mailed logs use the same log line format. */
if (evl_conf->format == EVLOG_SUDO || ISSET(flags, EVLOG_MAIL)) {
- logline = new_logline(flags, reason, errstr, evlog);
+ logline = new_logline(event_type, flags, args, evlog);
if (logline == NULL)
debug_return_bool(false);
@@ -1156,11 +1209,11 @@ do_logfile(int event_type, int flags, const char *reason, const char *errstr,
switch (evl_conf->format) {
case EVLOG_SUDO:
- ret = do_logfile_sudo(logline ? logline : reason, evlog, event_time);
+ ret = do_logfile_sudo(logline ? logline : args->reason, evlog,
+ args->event_time);
break;
case EVLOG_JSON:
- ret = do_logfile_json(event_type, reason, errstr, evlog,
- event_time, info_cb, info);
+ ret = do_logfile_json(event_type, args, evlog);
break;
default:
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
@@ -1178,21 +1231,21 @@ eventlog_accept(const struct eventlog *evlog, int flags,
{
const struct eventlog_config *evl_conf = eventlog_getconf();
const int log_type = evl_conf->type;
+ struct eventlog_args args = { NULL };
bool ret = true;
debug_decl(log_accept, SUDO_DEBUG_UTIL);
- if (log_type == EVLOG_NONE)
- debug_return_bool(true);
+ args.event_time = &evlog->submit_time;
+ args.json_info_cb = info_cb;
+ args.json_info = info;
if (ISSET(log_type, EVLOG_SYSLOG)) {
- if (!do_syslog(EVLOG_ACCEPT, flags, NULL, NULL, evlog,
- &evlog->submit_time, info_cb, info))
+ if (!do_syslog(EVLOG_ACCEPT, flags, &args, evlog))
ret = false;
CLR(flags, EVLOG_MAIL);
}
if (ISSET(log_type, EVLOG_FILE)) {
- if (!do_logfile(EVLOG_ACCEPT, flags, NULL, NULL, evlog,
- &evlog->submit_time, info_cb, info))
+ if (!do_logfile(EVLOG_ACCEPT, flags, &args, evlog))
ret = false;
}
@@ -1205,18 +1258,22 @@ eventlog_reject(const struct eventlog *evlog, int flags, const char *reason,
{
const struct eventlog_config *evl_conf = eventlog_getconf();
const int log_type = evl_conf->type;
+ struct eventlog_args args = { NULL };
bool ret = true;
debug_decl(log_reject, SUDO_DEBUG_UTIL);
+ args.reason = reason;
+ args.event_time = &evlog->submit_time;
+ args.json_info_cb = info_cb;
+ args.json_info = info;
+
if (ISSET(log_type, EVLOG_SYSLOG)) {
- if (!do_syslog(EVLOG_REJECT, flags, reason, NULL, evlog,
- &evlog->submit_time, info_cb, info))
+ if (!do_syslog(EVLOG_REJECT, flags, &args, evlog))
ret = false;
CLR(flags, EVLOG_MAIL);
}
if (ISSET(log_type, EVLOG_FILE)) {
- if (!do_logfile(EVLOG_REJECT, flags, reason, NULL, evlog,
- &evlog->submit_time, info_cb, info))
+ if (!do_logfile(EVLOG_REJECT, flags, &args, evlog))
ret = false;
}
@@ -1229,18 +1286,52 @@ eventlog_alert(const struct eventlog *evlog, int flags,
{
const struct eventlog_config *evl_conf = eventlog_getconf();
const int log_type = evl_conf->type;
+ struct eventlog_args args = { NULL };
bool ret = true;
debug_decl(log_alert, SUDO_DEBUG_UTIL);
+ args.reason = reason;
+ args.errstr = errstr;
+ args.event_time = alert_time;
+
+ if (ISSET(log_type, EVLOG_SYSLOG)) {
+ if (!do_syslog(EVLOG_ALERT, flags, &args, evlog))
+ ret = false;
+ CLR(flags, EVLOG_MAIL);
+ }
+ if (ISSET(log_type, EVLOG_FILE)) {
+ if (!do_logfile(EVLOG_ALERT, flags, &args, evlog))
+ ret = false;
+ }
+
+ debug_return_bool(ret);
+}
+
+bool
+eventlog_exit(const struct eventlog *evlog, int flags,
+ struct timespec *run_time, int exit_value, const char *signal_name,
+ bool core_dumped, eventlog_json_callback_t info_cb, void *info)
+{
+ const struct eventlog_config *evl_conf = eventlog_getconf();
+ const int log_type = evl_conf->type;
+ struct eventlog_args args = { NULL };
+ bool ret = true;
+ debug_decl(eventlog_exit, SUDO_DEBUG_UTIL);
+
+ args.signal_name = signal_name;
+ args.core_dumped = core_dumped;
+ args.exit_value = exit_value;
+ args.event_time = run_time;
+ args.json_info_cb = info_cb;
+ args.json_info = info;
+
if (ISSET(log_type, EVLOG_SYSLOG)) {
- if (!do_syslog(EVLOG_ALERT, flags, reason, errstr, evlog, alert_time,
- NULL, NULL))
+ if (!do_syslog(EVLOG_EXIT, flags, &args, evlog))
ret = false;
CLR(flags, EVLOG_MAIL);
}
if (ISSET(log_type, EVLOG_FILE)) {
- if (!do_logfile(EVLOG_ALERT, flags, reason, errstr, evlog, alert_time,
- NULL, NULL))
+ if (!do_logfile(EVLOG_EXIT, flags, &args, evlog))
ret = false;
}
--
2.43.0

View File

@ -1,39 +0,0 @@
From d109cd61d9ff01053f1d43b7d4fc0e5e657c8da3 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Tue, 27 Jul 2021 12:19:53 -0600
Subject: [PATCH] In new_logline check for NULL args->reason for EVLOG_RAW.
This can't happen in practice since we never set EVLOG_RAW without passing in
a reason. Coverity CID 237142 237143
---
lib/eventlog/eventlog.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/lib/eventlog/eventlog.c b/lib/eventlog/eventlog.c
index 280be7fcf..26c5f6dc7 100644
--- a/lib/eventlog/eventlog.c
+++ b/lib/eventlog/eventlog.c
@@ -106,12 +106,14 @@ new_logline(int event_type, int flags, struct eventlog_args *args,
debug_decl(new_logline, SUDO_DEBUG_UTIL);
if (ISSET(flags, EVLOG_RAW)) {
- if (args->errstr != NULL) {
- if (asprintf(&line, "%s: %s", args->reason, args->errstr) == -1)
- goto oom;
- } else {
- if ((line = strdup(args->reason)) == NULL)
- goto oom;
+ if (args->reason != NULL) {
+ if (args->errstr != NULL) {
+ if (asprintf(&line, "%s: %s", args->reason, args->errstr) == -1)
+ goto oom;
+ } else {
+ if ((line = strdup(args->reason)) == NULL)
+ goto oom;
+ }
}
debug_return_str(line);
}
--
2.43.0

View File

@ -1,42 +0,0 @@
From babb498c6ebe09723a751127b104f43ab643ee91 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Sat, 14 Aug 2021 09:24:39 -0600
Subject: [PATCH] new_logline: handle case where evlog is NULL
---
lib/eventlog/eventlog.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/eventlog/eventlog.c b/lib/eventlog/eventlog.c
index 255ef1bbf..7f55859b6 100644
--- a/lib/eventlog/eventlog.c
+++ b/lib/eventlog/eventlog.c
@@ -97,7 +97,7 @@ new_logline(int event_type, int flags, struct eventlog_args *args,
{
const struct eventlog_config *evl_conf = eventlog_getconf();
char *line = NULL, *evstr = NULL;
- const char *iolog_file = evlog->iolog_file;
+ const char *iolog_file;
const char *tty, *tsid = NULL;
char exit_str[(((sizeof(int) * 8) + 2) / 3) + 2];
char sessid[7];
@@ -105,7 +105,7 @@ new_logline(int event_type, int flags, struct eventlog_args *args,
int i;
debug_decl(new_logline, SUDO_DEBUG_UTIL);
- if (ISSET(flags, EVLOG_RAW)) {
+ if (ISSET(flags, EVLOG_RAW) || evlog == NULL) {
if (args->reason != NULL) {
if (args->errstr != NULL) {
if (asprintf(&line, "%s: %s", args->reason, args->errstr) == -1)
@@ -119,6 +119,7 @@ new_logline(int event_type, int flags, struct eventlog_args *args,
}
/* A TSID may be a sudoers-style session ID or a free-form string. */
+ iolog_file = evlog->iolog_file;
if (iolog_file != NULL) {
if (IS_SESSID(iolog_file)) {
sessid[0] = iolog_file[0];
--
2.43.0

View File

@ -1,56 +0,0 @@
From 4e4b506f22006c485c7ce557c9905dae16c5c12e Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Fri, 1 Oct 2021 10:33:55 -0600
Subject: [PATCH] Sync "sudo -l" output with normal sudo log format. It now
prints runchroot and runcwd (falling back on cwd). As a result, submithost is
now printed first, matching sudo. Also avoid printing NULL pointers and skip
entries that don't have at least command, submituser and runuser set.
---
plugins/sudoers/sudoreplay.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/plugins/sudoers/sudoreplay.c b/plugins/sudoers/sudoreplay.c
index f32d44eb5..786aae30d 100644
--- a/plugins/sudoers/sudoreplay.c
+++ b/plugins/sudoers/sudoreplay.c
@@ -1388,6 +1388,11 @@ list_session(char *log_dir, regex_t *re, const char *user, const char *tty)
if ((evlog = iolog_parse_loginfo(-1, log_dir)) == NULL)
goto done;
+ if (evlog->command == NULL || evlog->submituser == NULL ||
+ evlog->runuser == NULL) {
+ goto done;
+ }
+
/* Match on search expression if there is one. */
if (!STAILQ_EMPTY(&search_expr) && !match_expr(&search_expr, evlog, true))
goto done;
@@ -1409,13 +1414,18 @@ list_session(char *log_dir, regex_t *re, const char *user, const char *tty)
}
/* XXX - print lines + cols? */
timestr = get_timestr(evlog->submit_time.tv_sec, 1);
- printf("%s : %s : TTY=%s ; CWD=%s ; USER=%s ; ",
- timestr ? timestr : "invalid date",
- evlog->submituser, evlog->ttyname, evlog->cwd, evlog->runuser);
- if (evlog->rungroup)
- printf("GROUP=%s ; ", evlog->rungroup);
- if (evlog->submithost)
+ printf("%s : %s : ", timestr ? timestr : "invalid date", evlog->submituser);
+ if (evlog->submithost != NULL)
printf("HOST=%s ; ", evlog->submithost);
+ if (evlog->ttyname != NULL)
+ printf("TTY=%s ; ", evlog->ttyname);
+ if (evlog->runchroot != NULL)
+ printf("CHROOT=%s ; ", evlog->runchroot);
+ if (evlog->runcwd != NULL || evlog->cwd != NULL)
+ printf("CWD=%s ; ", evlog->runcwd ? evlog->runcwd : evlog->cwd);
+ printf("USER=%s ; ", evlog->runuser);
+ if (evlog->rungroup != NULL)
+ printf("GROUP=%s ; ", evlog->rungroup);
printf("TSID=%s ; COMMAND=%s\n", idstr, evlog->command);
ret = 0;
--
2.43.0

View File

@ -1,910 +0,0 @@
diff -up ./doc/sudoers.man.in.cve ./doc/sudoers.man.in
--- ./doc/sudoers.man.in.cve 2021-01-09 21:12:16.000000000 +0100
+++ ./doc/sudoers.man.in 2023-12-04 16:52:54.499061280 +0100
@@ -4978,14 +4978,31 @@ can log events via
syslog(3),
to a local log file, or both.
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.
+Command line arguments that contain spaces are enclosed in single quotes
+('').
+This makes it possible to distinguish multiple command line arguments
+from a single argument that contains spaces.
+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):
.nf
.sp
.RS 4n
-date hostname progname: username : TTY=ttyname ; PWD=cwd ; \e
- USER=runasuser ; GROUP=runasgroup ; TSID=logid ; \e
+date hostname progname: username : TTY=ttyname ; CHROOT=chroot ; \e
+ PWD=cwd ; USER=runasuser ; GROUP=runasgroup ; TSID=logid ; \e
ENV=env_vars COMMAND=command
.RE
.fi
@@ -5034,6 +5051,9 @@ was run on, or
\(lqunknown\(rq
if there was no terminal present.
.TP 14n
+chroot
+The root directory that the command was run in, if one was specified.
+.TP 14n
cwd
The current working directory that
\fBsudo\fR
@@ -5058,7 +5078,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,
@@ -5294,17 +5314,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
diff -up ./doc/sudoers.mdoc.in.cve ./doc/sudoers.mdoc.in
--- ./doc/sudoers.mdoc.in.cve 2021-01-09 21:12:16.000000000 +0100
+++ ./doc/sudoers.mdoc.in 2023-12-04 16:52:54.500061266 +0100
@@ -4649,12 +4649,29 @@ can log events via
.Xr syslog 3 ,
to a local log file, or both.
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 .
+Command line arguments that contain spaces are enclosed in single quotes
+.Pq '' .
+This makes it possible to distinguish multiple command line arguments
+from a single argument that contains spaces.
+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):
.Bd -literal -offset 4n
-date hostname progname: username : TTY=ttyname ; PWD=cwd ; \e
- USER=runasuser ; GROUP=runasgroup ; TSID=logid ; \e
+date hostname progname: username : TTY=ttyname ; CHROOT=chroot ; \e
+ PWD=cwd ; USER=runasuser ; GROUP=runasgroup ; TSID=logid ; \e
ENV=env_vars COMMAND=command
.Ed
.Pp
@@ -4697,6 +4714,8 @@ or
was run on, or
.Dq unknown
if there was no terminal present.
+.It chroot
+The root directory that the command was run in, if one was specified.
.It cwd
The current working directory that
.Nm sudo
@@ -4716,7 +4735,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
@@ -4938,14 +4957,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
diff -up ./doc/sudoreplay.man.in.cve ./doc/sudoreplay.man.in
--- ./doc/sudoreplay.man.in.cve 2020-12-17 02:33:43.000000000 +0100
+++ ./doc/sudoreplay.man.in 2023-12-04 16:52:54.500061266 +0100
@@ -164,6 +164,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.
diff -up ./doc/sudoreplay.mdoc.in.cve ./doc/sudoreplay.mdoc.in
--- ./doc/sudoreplay.mdoc.in.cve 2020-12-17 02:33:43.000000000 +0100
+++ ./doc/sudoreplay.mdoc.in 2023-12-04 16:52:54.500061266 +0100
@@ -156,6 +156,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.
diff -up ./include/sudo_lbuf.h.cve ./include/sudo_lbuf.h
--- ./include/sudo_lbuf.h.cve 2020-12-17 02:33:43.000000000 +0100
+++ ./include/sudo_lbuf.h 2023-12-04 16:52:54.500061266 +0100
@@ -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
+
sudo_dso_public void sudo_lbuf_init_v1(struct sudo_lbuf *lbuf, sudo_lbuf_output_t output, int indent, const char *continuation, int cols);
sudo_dso_public void sudo_lbuf_destroy_v1(struct sudo_lbuf *lbuf);
sudo_dso_public bool sudo_lbuf_append_v1(struct sudo_lbuf *lbuf, const char *fmt, ...) __printflike(2, 3);
+sudo_dso_public bool sudo_lbuf_append_esc_v1(struct sudo_lbuf *lbuf, int flags, const char *fmt, ...) __printflike(3, 4);
sudo_dso_public bool sudo_lbuf_append_quoted_v1(struct sudo_lbuf *lbuf, const char *set, const char *fmt, ...) __printflike(3, 4);
sudo_dso_public void sudo_lbuf_print_v1(struct sudo_lbuf *lbuf);
sudo_dso_public bool sudo_lbuf_error_v1(struct sudo_lbuf *lbuf);
@@ -47,6 +53,7 @@ sudo_dso_public void sudo_lbuf_clearerr_
#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))
diff -up ./lib/eventlog/eventlog.c.cve ./lib/eventlog/eventlog.c
--- ./lib/eventlog/eventlog.c.cve 2023-12-04 16:52:54.497061306 +0100
+++ ./lib/eventlog/eventlog.c 2023-12-04 16:54:09.820048779 +0100
@@ -51,24 +51,13 @@
#include "sudo_compat.h"
#include "sudo_debug.h"
#include "sudo_eventlog.h"
+#include "sudo_lbuf.h"
#include "sudo_fatal.h"
#include "sudo_gettext.h"
#include "sudo_json.h"
#include "sudo_queue.h"
#include "sudo_util.h"
-#define LL_HOST_STR "HOST="
-#define LL_TTY_STR "TTY="
-#define LL_CHROOT_STR "CHROOT="
-#define LL_CWD_STR "PWD="
-#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 LL_EXIT_STR "EXIT="
-#define LL_SIGNAL_STR "SIGNAL="
-
#define IS_SESSID(s) ( \
isalnum((unsigned char)(s)[0]) && isalnum((unsigned char)(s)[1]) && \
(s)[2] == '/' && \
@@ -96,26 +85,28 @@ new_logline(int event_type, int flags, s
const struct eventlog *evlog)
{
const struct eventlog_config *evl_conf = eventlog_getconf();
- char *line = NULL, *evstr = NULL;
const char *iolog_file;
const char *tty, *tsid = NULL;
char exit_str[(((sizeof(int) * 8) + 2) / 3) + 2];
char sessid[7];
- size_t len = 0;
+ struct sudo_lbuf lbuf;
int i;
debug_decl(new_logline, SUDO_DEBUG_UTIL);
+ sudo_lbuf_init(&lbuf, NULL, 0, NULL, 0);
+
if (ISSET(flags, EVLOG_RAW) || evlog == NULL) {
if (args->reason != NULL) {
if (args->errstr != NULL) {
- if (asprintf(&line, "%s: %s", args->reason, args->errstr) == -1)
- goto oom;
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "%s: %s",
+ args->reason, args->errstr);
} else {
- if ((line = strdup(args->reason)) == NULL)
- goto oom;
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "%s", args->reason);
}
+ if (sudo_lbuf_error(&lbuf))
+ goto oom;
}
- debug_return_str(line);
+ debug_return_str(lbuf.buf);
}
/* A TSID may be a sudoers-style session ID or a free-form string. */
@@ -142,163 +133,92 @@ new_logline(int event_type, int flags, s
}
/*
- * Compute line length
+ * Format the log line as an lbuf, escaping control characters is
+ * octal form (#0nn). Error checking (ENOMEM) is done at the end
*/
- if (args->reason != NULL)
- len += strlen(args->reason) + 3;
- if (args->errstr != NULL)
- len += strlen(args->errstr) + 3;
- if (evlog->submithost != NULL && !evl_conf->omit_hostname)
- len += sizeof(LL_HOST_STR) + 2 + strlen(evlog->submithost);
- if (tty != NULL)
- len += sizeof(LL_TTY_STR) + 2 + strlen(tty);
- if (evlog->runchroot != NULL)
- len += sizeof(LL_CHROOT_STR) + 2 + strlen(evlog->runchroot);
- if (evlog->runcwd != NULL)
- len += sizeof(LL_CWD_STR) + 2 + strlen(evlog->runcwd);
- if (evlog->runuser != NULL)
- len += sizeof(LL_USER_STR) + 2 + strlen(evlog->runuser);
- if (evlog->rungroup != NULL)
- len += sizeof(LL_GROUP_STR) + 2 + strlen(evlog->rungroup);
- if (tsid != NULL)
- len += sizeof(LL_TSID_STR) + 2 + strlen(tsid);
- if (evlog->env_add != NULL) {
- size_t evlen = 0;
- char * const *ep;
-
- for (ep = evlog->env_add; *ep != NULL; ep++)
- evlen += strlen(*ep) + 1;
- if (evlen != 0) {
- if ((evstr = malloc(evlen)) == NULL)
- goto oom;
- ep = evlog->env_add;
- if (strlcpy(evstr, *ep, evlen) >= evlen)
- goto toobig;
- while (*++ep != NULL) {
- if (strlcat(evstr, " ", evlen) >= evlen ||
- strlcat(evstr, *ep, evlen) >= evlen)
- goto toobig;
- }
- len += sizeof(LL_ENV_STR) + 2 + evlen;
- }
- }
- if (evlog->command != NULL) {
- len += sizeof(LL_CMND_STR) - 1 + strlen(evlog->command);
- if (evlog->argv != NULL) {
- for (i = 1; evlog->argv[i] != NULL; i++)
- len += strlen(evlog->argv[i]) + 1;
- }
- if (event_type == EVLOG_EXIT) {
- if (args->signal_name != NULL)
- len += sizeof(LL_SIGNAL_STR) + 2 + strlen(args->signal_name);
- (void)snprintf(exit_str, sizeof(exit_str), "%d", args->exit_value);
- len += sizeof(LL_EXIT_STR) + 2 + strlen(exit_str);
- }
- }
-
- /*
- * Allocate and build up the line.
- */
- if ((line = malloc(++len)) == NULL)
- goto oom;
- line[0] = '\0';
-
if (args->reason != NULL) {
- if (strlcat(line, args->reason, len) >= len ||
- strlcat(line, args->errstr ? " : " : " ; ", len) >= len)
- goto toobig;
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "%s%s", args->reason,
+ args->errstr ? " : " : " ; ");
}
if (args->errstr != NULL) {
- if (strlcat(line, args->errstr, len) >= len ||
- strlcat(line, " ; ", len) >= len)
- goto toobig;
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "%s ; ", args->errstr);
}
if (evlog->submithost != NULL && !evl_conf->omit_hostname) {
- if (strlcat(line, LL_HOST_STR, len) >= len ||
- strlcat(line, evlog->submithost, len) >= len ||
- strlcat(line, " ; ", len) >= len)
- goto toobig;
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "HOST=%s ; ",
+ evlog->submithost);
}
if (tty != NULL) {
- if (strlcat(line, LL_TTY_STR, len) >= len ||
- strlcat(line, tty, len) >= len ||
- strlcat(line, " ; ", len) >= len)
- goto toobig;
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "TTY=%s ; ", tty);
}
if (evlog->runchroot != NULL) {
- if (strlcat(line, LL_CHROOT_STR, len) >= len ||
- strlcat(line, evlog->runchroot, len) >= len ||
- strlcat(line, " ; ", len) >= len)
- goto toobig;
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "CHROOT=%s ; ",
+ evlog->runchroot);
}
if (evlog->runcwd != NULL) {
- if (strlcat(line, LL_CWD_STR, len) >= len ||
- strlcat(line, evlog->runcwd, len) >= len ||
- strlcat(line, " ; ", len) >= len)
- goto toobig;
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "PWD=%s ; ",
+ evlog->runcwd);
}
if (evlog->runuser != NULL) {
- if (strlcat(line, LL_USER_STR, len) >= len ||
- strlcat(line, evlog->runuser, len) >= len ||
- strlcat(line, " ; ", len) >= len)
- goto toobig;
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "USER=%s ; ",
+ evlog->runuser);
}
if (evlog->rungroup != NULL) {
- if (strlcat(line, LL_GROUP_STR, len) >= len ||
- strlcat(line, evlog->rungroup, len) >= len ||
- strlcat(line, " ; ", len) >= len)
- goto toobig;
- }
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "GROUP=%s ; ",
+ evlog->rungroup);
+ }
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 (evlog->env_add != NULL && evlog->env_add[0] != NULL) {
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, "ENV=%s",
+ evlog->env_add[0]);
+ for (i = 1; evlog->env_add[i] != NULL; i++) {
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, " %s",
+ evlog->env_add[i]);
+ }
}
if (evlog->command != NULL) {
- if (strlcat(line, LL_CMND_STR, len) >= len)
- goto toobig;
- if (strlcat(line, evlog->command, len) >= len)
- goto toobig;
- if (evlog->argv != NULL) {
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL|LBUF_ESC_BLANK,
+ "COMMAND=%s", evlog->command);
+ if (evlog->argv != NULL && evlog->argv[0] != NULL) {
for (i = 1; evlog->argv[i] != NULL; i++) {
- if (strlcat(line, " ", len) >= len ||
- strlcat(line, evlog->argv[i], len) >= len)
- goto toobig;
+ sudo_lbuf_append(&lbuf, " ");
+ if (strchr(evlog->argv[i], ' ') != NULL) {
+ /* Wrap args containing spaces in single quotes. */
+ sudo_lbuf_append(&lbuf, "'");
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL|LBUF_ESC_QUOTE,
+ "%s", evlog->argv[i]);
+ sudo_lbuf_append(&lbuf, "'");
+ } else {
+ /* Escape quotes here too for consistency. */
+ sudo_lbuf_append_esc(&lbuf,
+ LBUF_ESC_CNTRL|LBUF_ESC_BLANK|LBUF_ESC_QUOTE,
+ "%s", evlog->argv[i]);
+ }
}
}
+/*
if (event_type == EVLOG_EXIT) {
if (args->signal_name != NULL) {
- if (strlcat(line, " ; ", len) >= len ||
- strlcat(line, LL_SIGNAL_STR, len) >= len ||
- strlcat(line, args->signal_name, len) >= len)
- goto toobig;
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, " ; SIGNAL=%s",
+ evlog->signal_name);
}
- if (strlcat(line, " ; ", len) >= len ||
- strlcat(line, LL_EXIT_STR, len) >= len ||
- strlcat(line, exit_str, len) >= len)
- goto toobig;
+ if (evlog->exit_value != -1) {
+ (void)snprintf(exit_str, sizeof(exit_str), "%d",
+ evlog->exit_value);
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, " ; EXIT=%s",
+ exit_str);
+ }
}
+*/
}
- debug_return_str(line);
+ if (!sudo_lbuf_error(&lbuf))
+ debug_return_str(lbuf.buf);
oom:
- free(evstr);
+ 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);
}
static void
diff -up ./lib/iolog/iolog_json.c.cve ./lib/iolog/iolog_json.c
--- ./lib/iolog/iolog_json.c.cve 2020-12-17 02:33:43.000000000 +0100
+++ ./lib/iolog/iolog_json.c 2023-12-04 16:52:54.500061266 +0100
@@ -443,35 +443,6 @@ iolog_parse_json_object(struct json_obje
}
}
- /* Merge cmd and argv as sudoreplay expects. */
- if (evlog->command != NULL && evlog->argv != NULL) {
- size_t len = strlen(evlog->command) + 1;
- char *newcmd;
- int ac;
-
- /* Skip argv[0], we use evlog->command instead. */
- for (ac = 1; evlog->argv[ac] != NULL; ac++)
- len += strlen(evlog->argv[ac]) + 1;
-
- if ((newcmd = malloc(len)) == NULL) {
- sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
- goto done;
- }
-
- /* TODO: optimize this. */
- if (strlcpy(newcmd, evlog->command, len) >= len)
- sudo_fatalx(U_("internal error, %s overflow"), __func__);
- for (ac = 1; evlog->argv[ac] != NULL; ac++) {
- if (strlcat(newcmd, " ", len) >= len)
- sudo_fatalx(U_("internal error, %s overflow"), __func__);
- if (strlcat(newcmd, evlog->argv[ac], len) >= len)
- sudo_fatalx(U_("internal error, %s overflow"), __func__);
- }
-
- free(evlog->command);
- evlog->command = newcmd;
- }
-
ret = true;
done:
diff -up ./lib/util/lbuf.c.cve ./lib/util/lbuf.c
--- ./lib/util/lbuf.c.cve 2020-12-17 02:33:43.000000000 +0100
+++ ./lib/util/lbuf.c 2023-12-04 16:52:54.501061253 +0100
@@ -85,6 +85,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.
*/
diff -up ./lib/util/util.exp.in.cve ./lib/util/util.exp.in
--- ./lib/util/util.exp.in.cve 2021-01-09 21:12:16.000000000 +0100
+++ ./lib/util/util.exp.in 2023-12-04 16:52:54.501061253 +0100
@@ -95,6 +95,7 @@ sudo_json_get_len_v1
sudo_json_init_v1
sudo_json_open_array_v1
sudo_json_open_object_v1
+sudo_lbuf_append_esc_v1
sudo_lbuf_append_quoted_v1
sudo_lbuf_append_v1
sudo_lbuf_clearerr_v1
diff -up ./plugins/sudoers/sudoreplay.c.cve ./plugins/sudoers/sudoreplay.c
--- ./plugins/sudoers/sudoreplay.c.cve 2023-12-04 16:52:54.498061293 +0100
+++ ./plugins/sudoers/sudoreplay.c 2023-12-04 16:52:54.501061253 +0100
@@ -62,6 +62,7 @@
#include "sudo_debug.h"
#include "sudo_event.h"
#include "sudo_eventlog.h"
+#include "sudo_lbuf.h"
#include "sudo_fatal.h"
#include "sudo_gettext.h"
#include "sudo_iolog.h"
@@ -363,6 +364,10 @@ main(int argc, char *argv[])
if ((evlog = iolog_parse_loginfo(iolog_dir_fd, iolog_dir)) == NULL)
goto done;
printf(_("Replaying sudo session: %s"), evlog->command);
+ if (evlog->argv != NULL && evlog->argv[0] != NULL) {
+ for (i = 1; evlog->argv[i] != NULL; i++)
+ printf(" %s", evlog->argv[i]);
+ }
/* Setup terminal if appropriate. */
if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO))
@@ -1291,11 +1296,57 @@ parse_expr(struct search_node_list *head
debug_return_int(av - argv);
}
+static char *
+expand_command(struct eventlog *evlog, char **newbuf)
+{
+ size_t len, bufsize = strlen(evlog->command) + 1;
+ char *cp, *buf;
+ int ac;
+ debug_decl(expand_command, SUDO_DEBUG_UTIL);
+
+ if (evlog->argv == NULL || evlog->argv[0] == NULL || evlog->argv[1] == NULL) {
+ /* No arguments, we can use the command as-is. */
+ *newbuf = NULL;
+ debug_return_str(evlog->command);
+ }
+
+ /* Skip argv[0], we use evlog->command instead. */
+ for (ac = 1; evlog->argv[ac] != NULL; ac++)
+ bufsize += strlen(evlog->argv[ac]) + 1;
+
+ if ((buf = malloc(bufsize)) == NULL)
+ sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ cp = buf;
+
+ len = strlcpy(cp, evlog->command, bufsize);
+ if (len >= bufsize)
+ sudo_fatalx(U_("internal error, %s overflow"), __func__);
+ cp += len;
+ bufsize -= len;
+
+ for (ac = 1; evlog->argv[ac] != NULL; ac++) {
+ if (bufsize < 2)
+ sudo_fatalx(U_("internal error, %s overflow"), __func__);
+ *cp++ = ' ';
+ bufsize--;
+
+ len = strlcpy(cp, evlog->argv[ac], bufsize);
+ if (len >= bufsize)
+ sudo_fatalx(U_("internal error, %s overflow"), __func__);
+ cp += len;
+ bufsize -= len;
+ }
+
+ *newbuf = buf;
+ debug_return_str(buf);
+}
+
static bool
match_expr(struct search_node_list *head, struct eventlog *evlog, bool last_match)
{
struct search_node *sn;
bool res = false, matched = last_match;
+ char *tofree;
int rc;
debug_decl(match_expr, SUDO_DEBUG_UTIL);
@@ -1329,13 +1380,15 @@ match_expr(struct search_node_list *head
res = strcmp(sn->u.user, evlog->submituser) == 0;
break;
case ST_PATTERN:
- rc = regexec(&sn->u.cmdre, evlog->command, 0, NULL, 0);
+ rc = regexec(&sn->u.cmdre, expand_command(evlog, &tofree),
+ 0, NULL, 0);
if (rc && rc != REG_NOMATCH) {
char buf[BUFSIZ];
regerror(rc, &sn->u.cmdre, buf, sizeof(buf));
sudo_fatalx("%s", buf);
}
res = rc == REG_NOMATCH ? 0 : 1;
+ free(tofree);
break;
case ST_FROMDATE:
res = sudo_timespeccmp(&evlog->submit_time, &sn->u.tstamp, >=);
@@ -1356,12 +1409,13 @@ match_expr(struct search_node_list *head
}
static int
-list_session(char *log_dir, regex_t *re, const char *user, const char *tty)
+list_session(struct sudo_lbuf *lbuf, char *log_dir, regex_t *re,
+ const char *user, const char *tty)
{
char idbuf[7], *idstr, *cp;
struct eventlog *evlog = NULL;
const char *timestr;
- int ret = -1;
+ int i, ret = -1;
debug_decl(list_session, SUDO_DEBUG_UTIL);
if ((evlog = iolog_parse_loginfo(-1, log_dir)) == NULL)
@@ -1393,23 +1447,71 @@ list_session(char *log_dir, regex_t *re,
}
/* XXX - print lines + cols? */
timestr = get_timestr(evlog->submit_time.tv_sec, 1);
- printf("%s : %s : ", timestr ? timestr : "invalid date", evlog->submituser);
- if (evlog->submithost != NULL)
- printf("HOST=%s ; ", evlog->submithost);
- if (evlog->ttyname != NULL)
- printf("TTY=%s ; ", evlog->ttyname);
- if (evlog->runchroot != NULL)
- printf("CHROOT=%s ; ", evlog->runchroot);
- if (evlog->runcwd != NULL || evlog->cwd != NULL)
- printf("CWD=%s ; ", evlog->runcwd ? evlog->runcwd : evlog->cwd);
- printf("USER=%s ; ", evlog->runuser);
- if (evlog->rungroup != NULL)
- printf("GROUP=%s ; ", evlog->rungroup);
- printf("TSID=%s ; COMMAND=%s\n", idstr, evlog->command);
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "%s : %s : ",
+ timestr ? timestr : "invalid date", evlog->submituser);
+ if (evlog->submithost != NULL) {
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "HOST=%s ; ",
+ evlog->submithost);
+ }
+ if (evlog->ttyname != NULL) {
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "TTY=%s ; ",
+ evlog->ttyname);
+ }
+ if (evlog->runchroot != NULL) {
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "CHROOT=%s ; ",
+ evlog->runchroot);
+ }
+ if (evlog->runcwd != NULL || evlog->cwd != NULL) {
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "CWD=%s ; ",
+ evlog->runcwd ? evlog->runcwd : evlog->cwd);
+ }
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "USER=%s ; ", evlog->runuser);
+ if (evlog->rungroup != NULL) {
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "GROUP=%s ; ",
+ evlog->rungroup);
+ }
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "TSID=%s ; ", idstr);
+
+ /*
+ * If we have both command and argv from info.json we can escape
+ * blanks in the the command and arguments. If all we have is a
+ * single string containing both the command and arguments we cannot.
+ */
+ if (evlog->argv != NULL) {
+ /* Command plus argv from the info.json file. */
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL|LBUF_ESC_BLANK,
+ "COMMAND=%s", evlog->command);
+ if (evlog->argv[0] != NULL) {
+ for (i = 1; evlog->argv[i] != NULL; i++) {
+ sudo_lbuf_append(lbuf, " ");
+ if (strchr(evlog->argv[i], ' ') != NULL) {
+ /* Wrap args containing spaces in single quotes. */
+ sudo_lbuf_append(lbuf, "'");
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL|LBUF_ESC_QUOTE,
+ "%s", evlog->argv[i]);
+ sudo_lbuf_append(lbuf, "'");
+ } else {
+ /* Escape quotes here too for consistency. */
+ sudo_lbuf_append_esc(lbuf,
+ LBUF_ESC_CNTRL|LBUF_ESC_BLANK|LBUF_ESC_QUOTE,
+ "%s", evlog->argv[i]);
+ }
+ }
+ }
+ } else {
+ /* Single string from the legacy info file. */
+ sudo_lbuf_append_esc(lbuf, LBUF_ESC_CNTRL, "COMMAND=%s",
+ evlog->command);
+ }
- ret = 0;
+ if (!sudo_lbuf_error(lbuf)) {
+ puts(lbuf->buf);
+ ret = 0;
+ }
done:
+ lbuf->error = 0;
+ lbuf->len = 0;
eventlog_free(evlog);
debug_return_int(ret);
}
@@ -1429,6 +1531,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;
@@ -1440,6 +1543,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);
@@ -1500,7 +1605,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)) {
pathbuf[sdlen + len - 4] = '\0';
- list_session(pathbuf, re, user, tty);
+ list_session(&lbuf, pathbuf, re, user, tty);
} else {
/* Strip off "/log" and recurse if a non-log dir. */
pathbuf[sdlen + len - 4] = '\0';
@@ -1511,6 +1616,7 @@ find_sessions(const char *dir, regex_t *
}
free(sessions);
}
+ sudo_lbuf_destroy(&lbuf);
debug_return_int(0);
}

View File

@ -1,598 +0,0 @@
diff -up ./plugins/sudoers/auth/passwd.c.rowhammer ./plugins/sudoers/auth/passwd.c
--- ./plugins/sudoers/auth/passwd.c.rowhammer 2020-12-17 02:33:44.000000000 +0100
+++ ./plugins/sudoers/auth/passwd.c 2024-01-22 16:01:16.331874669 +0100
@@ -62,7 +62,7 @@ sudo_passwd_verify(struct passwd *pw, ch
char sav, *epass;
char *pw_epasswd = auth->data;
size_t pw_len;
- int matched = 0;
+ int ret;
debug_decl(sudo_passwd_verify, SUDOERS_DEBUG_AUTH);
/* An empty plain-text password must match an empty encrypted password. */
@@ -85,27 +85,37 @@ sudo_passwd_verify(struct passwd *pw, ch
*/
epass = (char *) crypt(pass, pw_epasswd);
pass[8] = sav;
+ ret = AUTH_FAILURE;
if (epass != NULL) {
- if (HAS_AGEINFO(pw_epasswd, pw_len) && strlen(epass) == DESLEN)
- matched = !strncmp(pw_epasswd, epass, DESLEN);
- else
- matched = !strcmp(pw_epasswd, epass);
+ if (HAS_AGEINFO(pw_epasswd, pw_len) && strlen(epass) == DESLEN) {
+ if (strncmp(pw_epasswd, epass, DESLEN) == 0)
+ ret = AUTH_SUCCESS;
+ } else {
+ if (strcmp(pw_epasswd, epass) == 0)
+ ret = AUTH_SUCCESS;
+ }
}
- debug_return_int(matched ? AUTH_SUCCESS : AUTH_FAILURE);
+ explicit_bzero(des_pass, sizeof(des_pass));
+
+ debug_return_int(ret);
}
#else
int
sudo_passwd_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback)
{
char *pw_passwd = auth->data;
- int matched;
+ int ret;
debug_decl(sudo_passwd_verify, SUDOERS_DEBUG_AUTH);
/* Simple string compare for systems without crypt(). */
matched = !strcmp(pass, pw_passwd);
+ if (strcmp(pass, pw_passwd) == 0)
+ ret = AUTH_SUCCESS;
+ else
+ ret = AUTH_FAILURE;
- debug_return_int(matched ? AUTH_SUCCESS : AUTH_FAILURE);
+ debug_return_int(ret);
}
#endif
diff -up ./plugins/sudoers/auth/sudo_auth.c.rowhammer ./plugins/sudoers/auth/sudo_auth.c
--- ./plugins/sudoers/auth/sudo_auth.c.rowhammer 2020-12-17 02:33:43.000000000 +0100
+++ ./plugins/sudoers/auth/sudo_auth.c 2024-01-22 16:01:16.331874669 +0100
@@ -112,10 +112,16 @@ sudo_auth_init(struct passwd *pw)
if (auth->init && !IS_DISABLED(auth)) {
/* Disable if it failed to init unless there was a fatal error. */
status = (auth->init)(pw, auth);
- if (status == AUTH_FAILURE)
- SET(auth->flags, FLAG_DISABLED);
- else if (status == AUTH_FATAL)
- break; /* assume error msg already printed */
+ switch (status) {
+ case AUTH_SUCCESS:
+ break;
+ case AUTH_FAILURE:
+ SET(auth->flags, FLAG_DISABLED);
+ break;
+ default:
+ /* Assume error msg already printed. */
+ debug_return_int(-1);
+ }
}
}
@@ -161,7 +167,7 @@ sudo_auth_init(struct passwd *pw)
}
}
- debug_return_int(status == AUTH_FATAL ? -1 : 0);
+ debug_return_int(0);
}
/*
@@ -202,7 +208,7 @@ sudo_auth_cleanup(struct passwd *pw, boo
for (auth = auth_switch; auth->name; auth++) {
if (auth->cleanup && !IS_DISABLED(auth)) {
int status = (auth->cleanup)(pw, auth, force);
- if (status == AUTH_FATAL) {
+ if (status != AUTH_SUCCESS) {
/* Assume error msg already printed. */
debug_return_int(-1);
}
@@ -297,7 +303,7 @@ verify_user(struct passwd *pw, char *pro
status = (auth->setup)(pw, &prompt, auth);
if (status == AUTH_FAILURE)
SET(auth->flags, FLAG_DISABLED);
- else if (status == AUTH_FATAL || user_interrupted())
+ else if (status != AUTH_SUCCESS || user_interrupted())
goto done; /* assume error msg already printed */
}
}
@@ -348,7 +354,6 @@ done:
log_auth_failure(validated, ntries);
ret = false;
break;
- case AUTH_FATAL:
default:
log_auth_failure(validated, 0);
ret = -1;
@@ -360,24 +365,32 @@ done:
/*
* Call authentication method begin session hooks.
- * Returns 1 on success and -1 on error.
+ * Returns true on success, false on failure and -1 on error.
*/
int
sudo_auth_begin_session(struct passwd *pw, char **user_env[])
{
sudo_auth *auth;
+ int ret = true;
debug_decl(sudo_auth_begin_session, SUDOERS_DEBUG_AUTH);
for (auth = auth_switch; auth->name; auth++) {
if (auth->begin_session && !IS_DISABLED(auth)) {
int status = (auth->begin_session)(pw, user_env, auth);
- if (status != AUTH_SUCCESS) {
- /* Assume error msg already printed. */
- debug_return_int(-1);
+ switch (status) {
+ case AUTH_SUCCESS:
+ break;
+ case AUTH_FAILURE:
+ ret = false;
+ break;
+ default:
+ /* Assume error msg already printed. */
+ ret = -1;
+ break;
}
}
}
- debug_return_int(1);
+ debug_return_int(ret);
}
bool
@@ -398,25 +411,33 @@ sudo_auth_needs_end_session(void)
/*
* Call authentication method end session hooks.
- * Returns 1 on success and -1 on error.
+ * Returns true on success, false on failure and -1 on error.
*/
int
sudo_auth_end_session(struct passwd *pw)
{
sudo_auth *auth;
+ int ret = true;
int status;
debug_decl(sudo_auth_end_session, SUDOERS_DEBUG_AUTH);
for (auth = auth_switch; auth->name; auth++) {
if (auth->end_session && !IS_DISABLED(auth)) {
status = (auth->end_session)(pw, auth);
- if (status == AUTH_FATAL) {
- /* Assume error msg already printed. */
- debug_return_int(-1);
- }
+ switch (status) {
+ case AUTH_SUCCESS:
+ break;
+ case AUTH_FAILURE:
+ ret = false;
+ break;
+ default:
+ /* Assume error msg already printed. */
+ ret = -1;
+ break;
+ }
}
}
- debug_return_int(1);
+ debug_return_int(ret);
}
/*
diff -up ./plugins/sudoers/auth/sudo_auth.h.rowhammer ./plugins/sudoers/auth/sudo_auth.h
--- ./plugins/sudoers/auth/sudo_auth.h.rowhammer 2020-12-17 02:33:43.000000000 +0100
+++ ./plugins/sudoers/auth/sudo_auth.h 2024-01-22 16:01:16.332874679 +0100
@@ -19,11 +19,11 @@
#ifndef SUDO_AUTH_H
#define SUDO_AUTH_H
-/* Auth function return values. */
-#define AUTH_SUCCESS 0
-#define AUTH_FAILURE 1
-#define AUTH_INTR 2
-#define AUTH_FATAL 3
+/* Auth function return values (rowhammer resistent). */
+#define AUTH_SUCCESS 0x52a2925 /* 0101001010100010100100100101 */
+#define AUTH_FAILURE 0xad5d6da /* 1010110101011101011011011010 */
+#define AUTH_INTR 0x69d61fc8 /* 1101001110101100001111111001000 */
+#define AUTH_FATAL 0x1629e037 /* 0010110001010011110000000110111 */
typedef struct sudo_auth {
int flags; /* various flags, see below */
diff -up ./plugins/sudoers/cvtsudoers.c.rowhammer ./plugins/sudoers/cvtsudoers.c
--- ./plugins/sudoers/cvtsudoers.c.rowhammer 2024-01-22 18:30:09.585081693 +0100
+++ ./plugins/sudoers/cvtsudoers.c 2024-01-22 18:32:35.238519869 +0100
@@ -685,7 +685,7 @@ userlist_matches_filter(struct sudoers_p
pw.pw_uid = (uid_t)-1;
pw.pw_gid = (gid_t)-1;
- if (user_matches(parse_tree, &pw, m) == true)
+ if (user_matches(parse_tree, &pw, m) == ALLOW)
matched = true;
} else {
STAILQ_FOREACH(s, &filters->users, entries) {
@@ -711,7 +711,7 @@ userlist_matches_filter(struct sudoers_p
if (pw == NULL)
continue;
- if (user_matches(parse_tree, pw, m) == true)
+ if (user_matches(parse_tree, pw, m) == ALLOW)
matched = true;
sudo_pw_delref(pw);
@@ -787,7 +787,7 @@ hostlist_matches_filter(struct sudoers_p
/* Only need one host in the filter to match. */
/* XXX - can't use netgroup_tuple with NULL pw */
- if (host_matches(parse_tree, NULL, lhost, shost, m) == true) {
+ if (host_matches(parse_tree, NULL, lhost, shost, m) == ALLOW) {
matched = true;
break;
}
diff -up ./plugins/sudoers/match.c.rowhammer ./plugins/sudoers/match.c
--- ./plugins/sudoers/match.c.rowhammer 2020-12-17 02:33:44.000000000 +0100
+++ ./plugins/sudoers/match.c 2024-01-22 16:01:16.332874679 +0100
@@ -26,6 +26,7 @@
* PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
*/
+#include "parse.h"
#include <config.h>
#include <sys/stat.h>
@@ -70,37 +71,42 @@ user_matches(struct sudoers_parse_tree *
{
const char *lhost = parse_tree->lhost ? parse_tree->lhost : user_runhost;
const char *shost = parse_tree->shost ? parse_tree->shost : user_srunhost;
- int matched = UNSPEC;
+ int rc, matched = UNSPEC;
struct alias *a;
debug_decl(user_matches, SUDOERS_DEBUG_MATCH);
switch (m->type) {
case ALL:
- matched = !m->negated;
+ matched = m->negated ? DENY : ALLOW;
break;
case NETGROUP:
if (netgr_matches(m->name,
def_netgroup_tuple ? lhost : NULL,
def_netgroup_tuple ? shost : NULL, pw->pw_name))
- matched = !m->negated;
+ matched = m->negated ? DENY : ALLOW;
break;
case USERGROUP:
if (usergr_matches(m->name, pw->pw_name, pw))
- matched = !m->negated;
+ matched = m->negated ? DENY : ALLOW;
break;
case ALIAS:
if ((a = alias_get(parse_tree, m->name, USERALIAS)) != NULL) {
/* XXX */
- int rc = userlist_matches(parse_tree, pw, &a->members);
- if (rc != UNSPEC)
- matched = m->negated ? !rc : rc;
+ rc = userlist_matches(parse_tree, pw, &a->members);
+ if (SPECIFIED(rc)) {
+ if (m->negated) {
+ matched = rc == ALLOW ? DENY : ALLOW;
+ } else {
+ matched = rc;
+ }
+ }
alias_put(a);
break;
}
FALLTHROUGH;
case WORD:
if (userpw_matches(m->name, pw->pw_name, pw))
- matched = !m->negated;
+ matched = m->negated ? DENY : ALLOW;
break;
}
debug_return_int(matched);
@@ -119,7 +125,8 @@ userlist_matches(struct sudoers_parse_tr
debug_decl(userlist_matches, SUDOERS_DEBUG_MATCH);
TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
- if ((matched = user_matches(parse_tree, pw, m)) != UNSPEC)
+ matched = user_matches(parse_tree, pw, m);
+ if (SPECIFIED(matched))
break;
}
debug_return_int(matched);
@@ -164,48 +171,53 @@ runaslist_matches(struct sudoers_parse_t
/* If no runas user or runas group listed in sudoers, use default. */
if (user_list == NULL && group_list == NULL) {
debug_return_int(userpw_matches(def_runas_default,
- runas_pw->pw_name, runas_pw));
+ runas_pw->pw_name, runas_pw) ? ALLOW : DENY);
}
if (user_list != NULL) {
TAILQ_FOREACH_REVERSE(m, user_list, member_list, entries) {
switch (m->type) {
case ALL:
- user_matched = !m->negated;
+ user_matched = m->negated ? DENY : ALLOW;
break;
case NETGROUP:
if (netgr_matches(m->name,
def_netgroup_tuple ? lhost : NULL,
def_netgroup_tuple ? shost : NULL,
runas_pw->pw_name))
- user_matched = !m->negated;
+ user_matched = m->negated ? DENY : ALLOW;
break;
case USERGROUP:
if (usergr_matches(m->name, runas_pw->pw_name, runas_pw))
- user_matched = !m->negated;
+ user_matched = m->negated ? DENY : ALLOW;
break;
case ALIAS:
a = alias_get(parse_tree, m->name, RUNASALIAS);
if (a != NULL) {
rc = runaslist_matches(parse_tree, &a->members,
&empty, matching_user, NULL);
- if (rc != UNSPEC)
- user_matched = m->negated ? !rc : rc;
+ if (SPECIFIED(rc)) {
+ if (m->negated) {
+ user_matched = rc == ALLOW ? DENY : ALLOW;
+ } else {
+ user_matched = rc;
+ }
+ }
alias_put(a);
break;
}
FALLTHROUGH;
case WORD:
if (userpw_matches(m->name, runas_pw->pw_name, runas_pw))
- user_matched = !m->negated;
+ user_matched = m->negated ? DENY : ALLOW;
break;
case MYSELF:
if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED) ||
strcmp(user_name, runas_pw->pw_name) == 0)
- user_matched = !m->negated;
+ user_matched = m->negated ? DENY : ALLOW;
break;
}
- if (user_matched != UNSPEC) {
+ if (SPECIFIED(user_matched)) {
if (matching_user != NULL && m->type != ALIAS)
*matching_user = m;
break;
@@ -226,34 +238,40 @@ runaslist_matches(struct sudoers_parse_t
TAILQ_FOREACH_REVERSE(m, group_list, member_list, entries) {
switch (m->type) {
case ALL:
- group_matched = !m->negated;
+ group_matched = m->negated ? DENY : ALLOW;
break;
case ALIAS:
a = alias_get(parse_tree, m->name, RUNASALIAS);
if (a != NULL) {
rc = runaslist_matches(parse_tree, &empty,
&a->members, NULL, matching_group);
- if (rc != UNSPEC)
- group_matched = m->negated ? !rc : rc;
+ if (SPECIFIED(rc)) {
+ if (m->negated) {
+ group_matched = rc == ALLOW ? DENY : ALLOW;
+ } else {
+ group_matched = rc;
+ }
+ }
alias_put(a);
break;
}
FALLTHROUGH;
case WORD:
if (group_matches(m->name, runas_gr))
- group_matched = !m->negated;
+ group_matched = m->negated ? DENY : ALLOW;
break;
}
- if (group_matched != UNSPEC) {
+ if (SPECIFIED(group_matched)) {
if (matching_group != NULL && m->type != ALIAS)
*matching_group = m;
break;
}
}
}
- if (group_matched == UNSPEC) {
+ if (!SPECIFIED(group_matched)) {
struct gid_list *runas_groups;
/*
+ *
* The runas group was not explicitly allowed by sudoers.
* Check whether it is one of the target user's groups.
*/
@@ -295,7 +313,7 @@ hostlist_matches_int(struct sudoers_pars
TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
matched = host_matches(parse_tree, pw, lhost, shost, m);
- if (matched != UNSPEC)
+ if (SPECIFIED(matched))
break;
}
debug_return_int(matched);
@@ -324,37 +342,42 @@ host_matches(struct sudoers_parse_tree *
const char *lhost, const char *shost, const struct member *m)
{
struct alias *a;
- int matched = UNSPEC;
+ int rc, matched = UNSPEC;
debug_decl(host_matches, SUDOERS_DEBUG_MATCH);
switch (m->type) {
case ALL:
- matched = !m->negated;
+ matched = m->negated ? DENY : ALLOW;
break;
case NETGROUP:
if (netgr_matches(m->name, lhost, shost,
def_netgroup_tuple ? pw->pw_name : NULL))
- matched = !m->negated;
+ matched = m->negated ? DENY : ALLOW;
break;
case NTWKADDR:
if (addr_matches(m->name))
- matched = !m->negated;
+ matched = m->negated ? DENY : ALLOW;
break;
case ALIAS:
a = alias_get(parse_tree, m->name, HOSTALIAS);
if (a != NULL) {
/* XXX */
- int rc = hostlist_matches_int(parse_tree, pw, lhost, shost,
+ rc = hostlist_matches_int(parse_tree, pw, lhost, shost,
&a->members);
- if (rc != UNSPEC)
- matched = m->negated ? !rc : rc;
+ if (SPECIFIED(rc)) {
+ if (m->negated) {
+ matched = rc == ALLOW ? DENY : ALLOW;
+ } else {
+ matched = rc;
+ }
+ }
alias_put(a);
break;
}
FALLTHROUGH;
case WORD:
if (hostname_matches(shost, lhost, m->name))
- matched = !m->negated;
+ matched = m->negated ? DENY : ALLOW;
break;
}
debug_return_int(matched);
@@ -375,7 +398,7 @@ cmndlist_matches(struct sudoers_parse_tr
TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
matched = cmnd_matches(parse_tree, m, runchroot, info);
- if (matched != UNSPEC)
+ if (SPECIFIED(matched))
break;
}
debug_return_int(matched);
@@ -397,21 +420,26 @@ cmnd_matches(struct sudoers_parse_tree *
switch (m->type) {
case ALL:
if (m->name == NULL) {
- matched = !m->negated;
+ matched = m->negated ? DENY : ALLOW;
break;
}
FALLTHROUGH;
case COMMAND:
c = (struct sudo_command *)m->name;
if (command_matches(c->cmnd, c->args, runchroot, info, &c->digests))
- matched = !m->negated;
+ matched = m->negated ? DENY : ALLOW;
break;
case ALIAS:
a = alias_get(parse_tree, m->name, CMNDALIAS);
if (a != NULL) {
rc = cmndlist_matches(parse_tree, &a->members, runchroot, info);
- if (rc != UNSPEC)
- matched = m->negated ? !rc : rc;
+ if (SPECIFIED(rc)) {
+ if (m->negated) {
+ matched = rc == ALLOW ? DENY : ALLOW;
+ } else {
+ matched = rc;
+ }
+ }
alias_put(a);
}
break;
diff -up ./plugins/sudoers/parse.c.rowhammer ./plugins/sudoers/parse.c
--- ./plugins/sudoers/parse.c.rowhammer 2020-12-17 02:33:43.000000000 +0100
+++ ./plugins/sudoers/parse.c 2024-01-22 16:01:16.333874689 +0100
@@ -151,7 +151,7 @@ sudoers_lookup_check(struct sudo_nss *ns
if (runas_match == ALLOW) {
cmnd_match = cmnd_matches(nss->parse_tree, cs->cmnd,
cs->runchroot, info);
- if (cmnd_match != UNSPEC) {
+ if (SPECIFIED(cmnd_match)) {
/*
* If user is running command as himself,
* set runas_pw = sudo_user.pw.
@@ -365,7 +365,7 @@ sudoers_lookup(struct sudo_nss_list *snl
}
m = sudoers_lookup_check(nss, pw, &validated, &info, &cs, &defs, now);
- if (m != UNSPEC) {
+ if (SPECIFIED(m)) {
match = m;
parse_tree = nss->parse_tree;
}
@@ -373,7 +373,7 @@ sudoers_lookup(struct sudo_nss_list *snl
if (!sudo_nss_can_continue(nss, m))
break;
}
- if (match != UNSPEC) {
+ if (SPECIFIED(match)) {
if (info.cmnd_path != NULL) {
/* Update user_cmnd, user_stat, cmnd_status from matching entry. */
free(user_cmnd);
diff -up ./plugins/sudoers/parse.h.rowhammer ./plugins/sudoers/parse.h
--- ./plugins/sudoers/parse.h.rowhammer 2021-01-09 21:12:16.000000000 +0100
+++ ./plugins/sudoers/parse.h 2024-01-22 16:01:16.333874689 +0100
@@ -20,6 +20,9 @@
#ifndef SUDOERS_PARSE_H
#define SUDOERS_PARSE_H
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
#include <sys/stat.h>
#include "sudo_queue.h"
@@ -31,13 +34,26 @@
#undef UNSPEC
#define UNSPEC -1
+
+/* Denied by policy (rowhammer resistent). */
#undef DENY
-#define DENY 0
+#define DENY 0xad5d6da /* 1010110101011101011011011010 */
+
+/* Allowed by policy (rowhammer resistent). */
#undef ALLOW
-#define ALLOW 1
+#define ALLOW 0x52a2925 /* 0101001010100010100100100101 */
+
#undef IMPLIED
#define IMPLIED 2
+
+/*
+ * We must explicitly check against ALLOW and DENY instead testing
+ * that the value is not UNSPEC to avoid potential ROWHAMMER issues.
+ */
+#define SPECIFIED(_v) ((_v) == ALLOW || (_v) == DENY)
+
+
/*
* Initialize all tags to UNSPEC.
*/

View File

@ -0,0 +1,61 @@
From db1f27c0350e9e437c93780ffe88648ae1984467 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Wed, 6 Jan 2021 10:16:00 -0700
Subject: [PATCH] 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.
---
src/sudo_edit.c | 29 ++++++++++++++++++++++++-----
1 file changed, 24 insertions(+), 5 deletions(-)
diff --git a/src/sudo_edit.c b/src/sudo_edit.c
index 82e04a71b..5502b7bd9 100644
--- a/src/sudo_edit.c
+++ b/src/sudo_edit.c
@@ -541,14 +541,33 @@ sudo_edit_create_tfiles(struct command_details *command_details,
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;
--
2.26.2

View File

@ -0,0 +1,158 @@
From adb4360c40df99238c17c3ecedcb1d32d76e2b2e Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Fri, 17 Apr 2020 19:08:56 -0600
Subject: [PATCH] Extend the original file before to the new size before
updating it. Instead of opening the original file for writing w/ tuncation,
we first extend the file with zeroes (by writing, not seeking), then
overwrite it. This should allow sudo to fail early if the disk is out of
space before it overwrites the original file.
---
src/sudo_edit.c | 93 ++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 77 insertions(+), 16 deletions(-)
diff --git a/src/sudo_edit.c b/src/sudo_edit.c
index 28f6c6100..d99a5658a 100644
--- a/src/sudo_edit.c
+++ b/src/sudo_edit.c
@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
- * Copyright (c) 2004-2008, 2010-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2004-2008, 2010-2020 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
@@ -650,6 +650,51 @@ sudo_edit_create_tfiles(struct command_details *command_details,
debug_return_int(j);
}
+/*
+ * Extend the given fd to the specified size in bytes.
+ * We do this to allocate disk space up-front before overwriting
+ * the original file with the temporary. Otherwise, we could
+ * we run out of disk space after truncating the original file.
+ */
+static int
+sudo_edit_extend_file(int fd, off_t new_size)
+{
+ off_t old_size, size;
+ ssize_t nwritten;
+ char zeroes[1024] = { '\0' };
+ debug_decl(sudo_edit_extend_file, SUDO_DEBUG_EDIT);
+
+ if ((old_size = lseek(fd, 0, SEEK_END)) == -1) {
+ sudo_warn("lseek");
+ debug_return_int(-1);
+ }
+ sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending file from %lld to %lld",
+ __func__, (long long)old_size, (long long)new_size);
+
+ for (size = old_size; size < new_size; size += nwritten) {
+ size_t len = new_size - size;
+ if (len > sizeof(zeroes))
+ len = sizeof(zeroes);
+ nwritten = write(fd, zeroes, len);
+ if (nwritten == -1) {
+ int serrno = errno;
+ if (ftruncate(fd, old_size) == -1) {
+ sudo_debug_printf(
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+ "unable to truncate to %lld", (long long)old_size);
+ }
+ errno = serrno;
+ debug_return_int(-1);
+ }
+ }
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ sudo_warn("lseek");
+ debug_return_int(-1);
+ }
+
+ debug_return_int(0);
+}
+
/*
* Copy the temporary files specified in tf to the originals.
* Returns the number of copy errors or 0 if completely successful.
@@ -708,38 +753,53 @@ sudo_edit_copy_tfiles(struct command_details *command_details,
switch_user(command_details->euid, command_details->egid,
command_details->ngroups, command_details->groups);
oldmask = umask(command_details->umask);
- ofd = sudo_edit_open(tf[i].ofile, O_WRONLY|O_TRUNC|O_CREAT,
+ ofd = sudo_edit_open(tf[i].ofile, O_WRONLY|O_CREAT,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
umask(oldmask);
switch_user(ROOT_UID, user_details.egid,
user_details.ngroups, user_details.groups);
- if (ofd == -1) {
- sudo_warn(U_("unable to write to %s"), tf[i].ofile);
- sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
- close(tfd);
- errors++;
- continue;
+ if (ofd == -1)
+ goto write_error;
+ /* Extend the file to the new size if larger before copying. */
+ if (tf[i].osize > 0 && sb.st_size > tf[i].osize) {
+ if (sudo_edit_extend_file(ofd, sb.st_size) == -1)
+ goto write_error;
}
+ /* Overwrite the old file with the new contents. */
while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
- if ((nwritten = write(ofd, buf, nread)) != nread) {
+ ssize_t off = 0;
+ do {
+ nwritten = write(ofd, buf + off, nread - off);
if (nwritten == -1)
- sudo_warn("%s", tf[i].ofile);
- else
- sudo_warnx(U_("%s: short write"), tf[i].ofile);
- break;
- }
+ goto write_error;
+ off += nwritten;
+ } while (nread > off);
}
if (nread == 0) {
- /* success, got EOF */
+ /* success, read to EOF */
+ if (tf[i].osize > 0 && sb.st_size < tf[i].osize) {
+ /* We don't open with O_TRUNC so must truncate manually. */
+ if (ftruncate(ofd, sb.st_size) == -1) {
+ sudo_debug_printf(
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+ "unable to truncate %s to %lld", tf[i].ofile,
+ (long long)sb.st_size);
+ goto write_error;
+ }
+ }
unlink(tf[i].tfile);
} else if (nread < 0) {
sudo_warn(U_("unable to read temporary file"));
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
+ errors++;
} else {
+write_error:
sudo_warn(U_("unable to write to %s"), tf[i].ofile);
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
+ errors++;
}
- close(ofd);
+ if (ofd != -1)
+ close(ofd);
close(tfd);
}
debug_return_int(errors);
@@ -1065,6 +1125,7 @@ cleanup:
for (i = 0; i < nfiles; i++) {
if (tf[i].tfile != NULL)
unlink(tf[i].tfile);
+ free(tf[i].tfile);
}
}
free(tf);
--
2.26.2

View File

@ -0,0 +1,431 @@
diff -up ./src/copy_file.c.symbolic-link-attack-2 ./src/copy_file.c
--- ./src/copy_file.c.symbolic-link-attack-2 2021-02-02 15:31:20.555340446 +0100
+++ ./src/copy_file.c 2021-02-02 15:31:20.555340446 +0100
@@ -0,0 +1,128 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2020 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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "sudo.h"
+
+/*
+ * Extend the given fd to the specified size in bytes.
+ * We do this to allocate disk space up-front before overwriting
+ * the original file with the temporary. Otherwise, we could
+ * we run out of disk space after truncating the original file.
+ */
+static int
+sudo_extend_file(int fd, const char *name, off_t new_size)
+{
+ off_t old_size, size;
+ ssize_t nwritten;
+ char zeroes[BUFSIZ] = { '\0' };
+ debug_decl(sudo_extend_file, SUDO_DEBUG_UTIL);
+
+ if ((old_size = lseek(fd, 0, SEEK_END)) == -1) {
+ sudo_warn("lseek");
+ debug_return_int(-1);
+ }
+ sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending %s from %lld to %lld",
+ __func__, name, (long long)old_size, (long long)new_size);
+
+ for (size = old_size; size < new_size; size += nwritten) {
+ size_t len = new_size - size;
+ if (len > sizeof(zeroes))
+ len = sizeof(zeroes);
+ nwritten = write(fd, zeroes, len);
+ if (nwritten == -1) {
+ int serrno = errno;
+ if (ftruncate(fd, old_size) == -1) {
+ sudo_debug_printf(
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+ "unable to truncate %s to %lld", name, (long long)old_size);
+ }
+ errno = serrno;
+ debug_return_int(-1);
+ }
+ }
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ sudo_warn("lseek");
+ debug_return_int(-1);
+ }
+
+ debug_return_int(0);
+}
+
+/*
+ * Copy the contents of src_fd into dst_fd.
+ * Returns 0 on success or -1 on error.
+ */
+int
+sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst,
+ int dst_fd, off_t dst_len)
+{
+ char buf[BUFSIZ];
+ ssize_t nwritten, nread;
+ debug_decl(sudo_copy_file, SUDO_DEBUG_UTIL);
+
+ /* Extend the file to the new size if larger before copying. */
+ if (dst_len > 0 && src_len > dst_len) {
+ if (sudo_extend_file(dst_fd, dst, src_len) == -1)
+ goto write_error;
+ }
+
+ /* Overwrite the old file with the new contents. */
+ while ((nread = read(src_fd, buf, sizeof(buf))) > 0) {
+ ssize_t off = 0;
+ do {
+ nwritten = write(dst_fd, buf + off, nread - off);
+ if (nwritten == -1)
+ goto write_error;
+ off += nwritten;
+ } while (nread > off);
+ }
+ if (nread == 0) {
+ /* success, read to EOF */
+ if (src_len < dst_len) {
+ /* We don't open with O_TRUNC so must truncate manually. */
+ if (ftruncate(dst_fd, src_len) == -1) {
+ sudo_debug_printf(
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
+ "unable to truncate %s to %lld", dst, (long long)src_len);
+ goto write_error;
+ }
+ }
+ debug_return_int(0);
+ } else if (nread < 0) {
+ sudo_warn(U_("unable to read from %s"), src);
+ debug_return_int(-1);
+ } else {
+write_error:
+ sudo_warn(U_("unable to write to %s"), dst);
+ debug_return_int(-1);
+ }
+}
diff -up ./src/Makefile.in.symbolic-link-attack-2 ./src/Makefile.in
--- ./src/Makefile.in.symbolic-link-attack-2 2019-10-28 13:28:54.000000000 +0100
+++ ./src/Makefile.in 2021-02-02 15:31:20.555340446 +0100
@@ -120,16 +120,17 @@ SHELL = @SHELL@
PROGS = @PROGS@
-OBJS = conversation.o env_hooks.o exec.o exec_common.o exec_monitor.o \
- exec_nopty.o exec_pty.o get_pty.o hooks.o limits.o load_plugins.o \
- net_ifs.o parse_args.o preserve_fds.o signal.o sudo.o sudo_edit.o \
- tcsetpgrp_nobg.o tgetpass.o ttyname.o utmp.o @SUDO_OBJS@
+OBJS = conversation.o copy_file.o env_hooks.o exec.o exec_common.o \
+ exec_monitor.o exec_nopty.o exec_pty.o get_pty.o hooks.o \
+ limits.o load_plugins.o net_ifs.o parse_args.o preserve_fds.o \
+ signal.o sudo.o sudo_edit.o tcsetpgrp_nobg.o tgetpass.o \
+ ttyname.o utmp.o @SUDO_OBJS@
IOBJS = $(OBJS:.o=.i) sesh.i
POBJS = $(IOBJS:.i=.plog)
-SESH_OBJS = sesh.o exec_common.o
+SESH_OBJS = copy_file.o exec_common.o sesh.o
CHECK_NOEXEC_OBJS = check_noexec.o exec_common.o
@@ -335,6 +336,22 @@ conversation.i: $(srcdir)/conversation.c
$(CC) -E -o $@ $(CPPFLAGS) $<
conversation.plog: conversation.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/conversation.c --i-file $< --output-file $@
+copy_file.o: $(srcdir)/copy_file.c $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
+ $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
+ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
+ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/sudo.h \
+ $(top_builddir)/config.h $(top_builddir)/pathnames.h
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/copy_file.c
+copy_file.i: $(srcdir)/copy_file.c $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
+ $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
+ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
+ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/sudo.h \
+ $(top_builddir)/config.h $(top_builddir)/pathnames.h
+ $(CC) -E -o $@ $(CPPFLAGS) $<
+copy_file.plog: copy_file.i
+ rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/copy_file.c --i-file $< --output-file $@
env_hooks.o: $(srcdir)/env_hooks.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_dso.h \
diff -up ./src/sesh.c.symbolic-link-attack-2 ./src/sesh.c
--- ./src/sesh.c.symbolic-link-attack-2 2019-10-28 13:28:52.000000000 +0100
+++ ./src/sesh.c 2021-02-02 15:31:20.555340446 +0100
@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
- * Copyright (c) 2008, 2010-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2008, 2010-2018, 2020 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
@@ -182,7 +182,7 @@ sesh_sudoedit(int argc, char *argv[])
* so that it's ensured that the temporary files are
* created by us and that we are not opening any symlinks.
*/
- oflags_dst = O_WRONLY|O_TRUNC|O_CREAT|(post ? follow : O_EXCL);
+ oflags_dst = O_WRONLY|O_CREAT|(post ? follow : O_EXCL);
for (i = 0; i < argc - 1; i += 2) {
const char *path_src = argv[i];
const char *path_dst = argv[i + 1];
@@ -214,14 +214,29 @@ sesh_sudoedit(int argc, char *argv[])
}
if (fd_src != -1) {
- while ((nread = read(fd_src, buf, sizeof(buf))) > 0) {
- if ((nwritten = write(fd_dst, buf, nread)) != nread) {
- sudo_warn("%s", path_src);
- if (post) {
- ret = SESH_ERR_SOME_FILES;
- goto nocleanup;
- } else
- goto cleanup_0;
+ off_t len_src = -1;
+ off_t len_dst = -1;
+
+ if (post) {
+ if (fstat(fd_src, &sb) != 0) {
+ ret = SESH_ERR_SOME_FILES;
+ goto nocleanup;
+ }
+ len_src = sb.st_size;
+ if (fstat(fd_dst, &sb) != 0) {
+ ret = SESH_ERR_SOME_FILES;
+ goto nocleanup;
+ }
+ len_dst = sb.st_size;
+ }
+
+ if (sudo_copy_file(path_src, fd_src, len_src, path_dst, fd_dst,
+ len_dst) == -1) {
+ if (post) {
+ ret = SESH_ERR_SOME_FILES;
+ goto nocleanup;
+ } else {
+ goto cleanup_0;
}
}
}
diff -up ./src/sudo_edit.c.symbolic-link-attack-2 ./src/sudo_edit.c
--- ./src/sudo_edit.c.symbolic-link-attack-2 2021-02-02 15:31:20.554340459 +0100
+++ ./src/sudo_edit.c 2021-02-02 15:31:54.355884326 +0100
@@ -42,7 +42,6 @@
#include <grp.h>
#include <pwd.h>
#include <signal.h>
-#include <errno.h>
#include <fcntl.h>
#include "sudo.h"
@@ -551,8 +550,6 @@ sudo_edit_create_tfiles(struct command_d
struct tempfile *tf, char *files[], int nfiles)
{
int i, j, tfd, ofd, rc;
- char buf[BUFSIZ];
- ssize_t nwritten, nread;
struct timespec times[2];
struct stat sb;
debug_decl(sudo_edit_create_tfiles, SUDO_DEBUG_EDIT)
@@ -648,18 +645,7 @@ sudo_edit_create_tfiles(struct command_d
debug_return_int(-1);
}
if (ofd != -1) {
- while ((nread = read(ofd, buf, sizeof(buf))) > 0) {
- if ((nwritten = write(tfd, buf, nread)) != nread) {
- if (nwritten == -1)
- sudo_warn("%s", tf[j].tfile);
- else
- sudo_warnx(U_("%s: short write"), tf[j].tfile);
- break;
- }
- }
- if (nread != 0) {
- if (nread < 0)
- sudo_warn("%s", files[i]);
+ if (sudo_copy_file(tf[j].ofile, ofd, tf[j].osize, tf[j].tfile, tfd, -1) == -1) {
close(ofd);
close(tfd);
debug_return_int(-1);
@@ -689,51 +675,6 @@ sudo_edit_create_tfiles(struct command_d
}
/*
- * Extend the given fd to the specified size in bytes.
- * We do this to allocate disk space up-front before overwriting
- * the original file with the temporary. Otherwise, we could
- * we run out of disk space after truncating the original file.
- */
-static int
-sudo_edit_extend_file(int fd, off_t new_size)
-{
- off_t old_size, size;
- ssize_t nwritten;
- char zeroes[1024] = { '\0' };
- debug_decl(sudo_edit_extend_file, SUDO_DEBUG_EDIT);
-
- if ((old_size = lseek(fd, 0, SEEK_END)) == -1) {
- sudo_warn("lseek");
- debug_return_int(-1);
- }
- sudo_debug_printf(SUDO_DEBUG_INFO, "%s: extending file from %lld to %lld",
- __func__, (long long)old_size, (long long)new_size);
-
- for (size = old_size; size < new_size; size += nwritten) {
- size_t len = new_size - size;
- if (len > sizeof(zeroes))
- len = sizeof(zeroes);
- nwritten = write(fd, zeroes, len);
- if (nwritten == -1) {
- int serrno = errno;
- if (ftruncate(fd, old_size) == -1) {
- sudo_debug_printf(
- SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
- "unable to truncate to %lld", (long long)old_size);
- }
- errno = serrno;
- debug_return_int(-1);
- }
- }
- if (lseek(fd, 0, SEEK_SET) == -1) {
- sudo_warn("lseek");
- debug_return_int(-1);
- }
-
- debug_return_int(0);
-}
-
-/*
* Copy the temporary files specified in tf to the originals.
* Returns the number of copy errors or 0 if completely successful.
*/
@@ -741,9 +682,7 @@ static int
sudo_edit_copy_tfiles(struct command_details *command_details,
struct tempfile *tf, int nfiles, struct timespec *times)
{
- int i, tfd, ofd, rc, errors = 0;
- char buf[BUFSIZ];
- ssize_t nwritten, nread;
+ int i, tfd, ofd, errors = 0;
struct timespec ts;
struct stat sb;
mode_t oldmask;
@@ -751,7 +690,7 @@ sudo_edit_copy_tfiles(struct command_det
/* Copy contents of temp files to real ones. */
for (i = 0; i < nfiles; i++) {
- rc = -1;
+ int rc = -1;
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"seteuid(%u)", (unsigned int)user_details.uid);
if (seteuid(user_details.uid) != 0)
@@ -764,8 +703,8 @@ sudo_edit_copy_tfiles(struct command_det
"seteuid(%u)", ROOT_UID);
if (seteuid(ROOT_UID) != 0)
sudo_fatal("seteuid(ROOT_UID)");
- if (rc || !S_ISREG(sb.st_mode)) {
- if (rc)
+ if (rc == -1 || !S_ISREG(sb.st_mode)) {
+ if (rc == -1)
sudo_warn("%s", tf[i].tfile);
else
sudo_warnx(U_("%s: not a regular file"), tf[i].tfile);
@@ -796,46 +735,19 @@ sudo_edit_copy_tfiles(struct command_det
umask(oldmask);
switch_user(ROOT_UID, user_details.egid,
user_details.ngroups, user_details.groups);
- if (ofd == -1)
- goto write_error;
- /* Extend the file to the new size if larger before copying. */
- if (tf[i].osize > 0 && sb.st_size > tf[i].osize) {
- if (sudo_edit_extend_file(ofd, sb.st_size) == -1)
- goto write_error;
+ if (ofd == -1) {
+ sudo_warn(U_("unable to write to %s"), tf[i].ofile);
+ goto bad;
}
+
/* Overwrite the old file with the new contents. */
- while ((nread = read(tfd, buf, sizeof(buf))) > 0) {
- ssize_t off = 0;
- do {
- nwritten = write(ofd, buf + off, nread - off);
- if (nwritten == -1)
- goto write_error;
- off += nwritten;
- } while (nread > off);
- }
- if (nread == 0) {
- /* success, read to EOF */
- if (tf[i].osize > 0 && sb.st_size < tf[i].osize) {
- /* We don't open with O_TRUNC so must truncate manually. */
- if (ftruncate(ofd, sb.st_size) == -1) {
- sudo_debug_printf(
- SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
- "unable to truncate %s to %lld", tf[i].ofile,
- (long long)sb.st_size);
- goto write_error;
- }
- }
- unlink(tf[i].tfile);
- } else if (nread < 0) {
- sudo_warn(U_("unable to read temporary file"));
- sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
- errors++;
- } else {
-write_error:
- sudo_warn(U_("unable to write to %s"), tf[i].ofile);
+ if (sudo_copy_file(tf[i].tfile, tfd, sb.st_size, tf[i].ofile, ofd,
+ tf[i].osize) == -1) {
+bad:
sudo_warnx(U_("contents of edit session left in %s"), tf[i].tfile);
errors++;
}
+
if (ofd != -1)
close(ofd);
close(tfd);
diff -up ./src/sudo_exec.h.symbolic-link-attack-2 ./src/sudo_exec.h
--- ./src/sudo_exec.h.symbolic-link-attack-2 2019-10-28 13:27:39.000000000 +0100
+++ ./src/sudo_exec.h 2021-02-02 15:31:20.556340432 +0100
@@ -84,6 +84,9 @@
struct command_details;
struct command_status;
+/* copy_file.c */
+int sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst, int dst_fd, off_t dst_len);
+
/* exec.c */
void exec_cmnd(struct command_details *details, int errfd);
void terminate_command(pid_t pid, bool use_pgrp);

View File

@ -0,0 +1,345 @@
diff -up ./src/exec_monitor.c.symbolic-link-attack-3 ./src/exec_monitor.c
--- ./src/exec_monitor.c.symbolic-link-attack-3 2019-10-28 13:27:39.000000000 +0100
+++ ./src/exec_monitor.c 2021-02-02 17:11:32.382020407 +0100
@@ -613,7 +613,7 @@ exec_monitor(struct command_details *det
#ifdef HAVE_SELINUX
if (ISSET(details->flags, CD_RBAC_ENABLED)) {
if (selinux_setup(details->selinux_role, details->selinux_type,
- details->tty, io_fds[SFD_SLAVE]) == -1)
+ details->tty, io_fds[SFD_SLAVE], true) == -1)
goto bad;
}
#endif
diff -up ./src/exec_nopty.c.symbolic-link-attack-3 ./src/exec_nopty.c
--- ./src/exec_nopty.c.symbolic-link-attack-3 2019-10-28 13:27:39.000000000 +0100
+++ ./src/exec_nopty.c 2021-02-02 17:11:32.382020407 +0100
@@ -381,7 +381,7 @@ exec_nopty(struct command_details *detai
#ifdef HAVE_SELINUX
if (ISSET(details->flags, CD_RBAC_ENABLED)) {
if (selinux_setup(details->selinux_role, details->selinux_type,
- details->tty, -1) == -1) {
+ details->tty, -1, true) == -1) {
cstat->type = CMD_ERRNO;
cstat->val = errno;
debug_return;
diff -up ./src/selinux.c.symbolic-link-attack-3 ./src/selinux.c
--- ./src/selinux.c.symbolic-link-attack-3 2019-10-28 13:27:39.000000000 +0100
+++ ./src/selinux.c 2021-02-02 17:11:32.382020407 +0100
@@ -363,7 +363,7 @@ bad:
*/
int
selinux_setup(const char *role, const char *type, const char *ttyn,
- int ptyfd)
+ int ptyfd, bool label_tty)
{
int ret = -1;
debug_decl(selinux_setup, SUDO_DEBUG_SELINUX)
@@ -392,7 +392,7 @@ selinux_setup(const char *role, const ch
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: new context %s", __func__,
se_state.new_context);
- if (relabel_tty(ttyn, ptyfd) == -1) {
+ if (label_tty && relabel_tty(ttyn, ptyfd) == -1) {
sudo_warn(U_("unable to set tty context to %s"), se_state.new_context);
goto done;
}
@@ -408,6 +408,28 @@ done:
debug_return_int(ret);
}
+int
+selinux_setcon(void)
+{
+ debug_decl(selinux_setcon, SUDO_DEBUG_SELINUX);
+
+ if (setexeccon(se_state.new_context)) {
+ sudo_warn(U_("unable to set exec context to %s"), se_state.new_context);
+ if (se_state.enforcing)
+ debug_return_int(-1);
+ }
+
+#ifdef HAVE_SETKEYCREATECON
+ if (setkeycreatecon(se_state.new_context)) {
+ sudo_warn(U_("unable to set key creation context to %s"), se_state.new_context);
+ if (se_state.enforcing)
+ debug_return_int(-1);
+ }
+#endif /* HAVE_SETKEYCREATECON */
+
+ debug_return_int(0);
+}
+
void
selinux_execve(int fd, const char *path, char *const argv[], char *envp[],
bool noexec)
@@ -424,19 +446,9 @@ selinux_execve(int fd, const char *path,
debug_return;
}
- if (setexeccon(se_state.new_context)) {
- sudo_warn(U_("unable to set exec context to %s"), se_state.new_context);
- if (se_state.enforcing)
- debug_return;
- }
-
-#ifdef HAVE_SETKEYCREATECON
- if (setkeycreatecon(se_state.new_context)) {
- sudo_warn(U_("unable to set key creation context to %s"), se_state.new_context);
- if (se_state.enforcing)
- debug_return;
- }
-#endif /* HAVE_SETKEYCREATECON */
+ /* Set SELinux exec and keycreate contexts. */
+ if (selinux_setcon() == -1)
+ debug_return;
/*
* Build new argv with sesh as argv[0].
diff -up ./src/sudo.c.symbolic-link-attack-3 ./src/sudo.c
--- ./src/sudo.c.symbolic-link-attack-3 2021-02-02 17:12:32.773182386 +0100
+++ ./src/sudo.c 2021-02-02 17:12:48.510964009 +0100
@@ -971,10 +971,6 @@ run_command(struct command_details *deta
case CMD_WSTATUS:
/* Command ran, exited or was killed. */
status = cstat.val;
-#ifdef HAVE_SELINUX
- if (ISSET(details->flags, CD_SUDOEDIT_COPY))
- break;
-#endif
sudo_debug_printf(SUDO_DEBUG_DEBUG,
"calling policy close with wait status %d", status);
policy_close(&policy_plugin, status, 0);
diff -up ./src/sudo_edit.c.symbolic-link-attack-3 ./src/sudo_edit.c
--- ./src/sudo_edit.c.symbolic-link-attack-3 2021-02-02 17:11:32.380020435 +0100
+++ ./src/sudo_edit.c 2021-02-02 17:11:32.382020407 +0100
@@ -757,28 +757,54 @@ bad:
#ifdef HAVE_SELINUX
static int
+selinux_run_helper(char *argv[], char *envp[])
+{
+ int status, ret = SESH_ERR_FAILURE;
+ const char *sesh;
+ pid_t child, pid;
+ debug_decl(selinux_run_helper, SUDO_DEBUG_EDIT);
+
+ sesh = sudo_conf_sesh_path();
+ if (sesh == NULL) {
+ sudo_warnx("internal error: sesh path not set");
+ debug_return_int(-1);
+ }
+
+ child = sudo_debug_fork();
+ switch (child) {
+ case -1:
+ sudo_warn(U_("unable to fork"));
+ break;
+ case 0:
+ /* child runs sesh in new context */
+ if (selinux_setcon() == 0)
+ execve(sesh, argv, envp);
+ _exit(SESH_ERR_FAILURE);
+ default:
+ /* parent waits */
+ do {
+ pid = waitpid(child, &status, 0);
+ } while (pid == -1 && errno == EINTR);
+
+ ret = WIFSIGNALED(status) ? SESH_ERR_KILLED : WEXITSTATUS(status);
+ }
+
+ debug_return_int(ret);
+}
+
+static int
selinux_edit_create_tfiles(struct command_details *command_details,
struct tempfile *tf, char *files[], int nfiles)
{
char **sesh_args, **sesh_ap;
int i, rc, sesh_nargs;
struct stat sb;
- struct command_details saved_command_details;
debug_decl(selinux_edit_create_tfiles, SUDO_DEBUG_EDIT)
-
- /* Prepare selinux stuff (setexeccon) */
- if (selinux_setup(command_details->selinux_role,
- command_details->selinux_type, NULL, -1) != 0)
- debug_return_int(-1);
if (nfiles < 1)
debug_return_int(0);
/* Construct common args for sesh */
- memcpy(&saved_command_details, command_details, sizeof(struct command_details));
- command_details->command = _PATH_SUDO_SESH;
- command_details->flags |= CD_SUDOEDIT_COPY;
-
sesh_nargs = 4 + (nfiles * 2) + 1;
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
if (sesh_args == NULL) {
@@ -791,6 +817,7 @@ selinux_edit_create_tfiles(struct comman
*sesh_ap++ = "-h";
*sesh_ap++ = "0";
+ /* XXX - temp files should be created with user's context */
for (i = 0; i < nfiles; i++) {
char *tfile, *ofile = files[i];
int tfd;
@@ -820,8 +847,7 @@ selinux_edit_create_tfiles(struct comman
*sesh_ap = NULL;
/* Run sesh -e [-h] 0 <o1> <t1> ... <on> <tn> */
- command_details->argv = sesh_args;
- rc = run_command(command_details);
+ rc = selinux_run_helper(sesh_args, command_details->envp);
switch (rc) {
case SESH_SUCCESS:
break;
@@ -829,15 +855,12 @@ selinux_edit_create_tfiles(struct comman
sudo_fatalx(U_("sesh: internal error: odd number of paths"));
case SESH_ERR_NO_FILES:
sudo_fatalx(U_("sesh: unable to create temporary files"));
+ case SESH_ERR_KILLED:
+ sudo_fatalx(U_("sesh: killed by a signal"));
default:
sudo_fatalx(U_("sesh: unknown error %d"), rc);
}
- /* Restore saved command_details. */
- command_details->command = saved_command_details.command;
- command_details->flags = saved_command_details.flags;
- command_details->argv = saved_command_details.argv;
-
/* Chown to user's UID so they can edit the temporary files. */
for (i = 0; i < nfiles; i++) {
if (chown(tf[i].tfile, user_details.uid, user_details.gid) != 0) {
@@ -858,24 +881,14 @@ selinux_edit_copy_tfiles(struct command_
{
char **sesh_args, **sesh_ap;
int i, rc, sesh_nargs, ret = 1;
- struct command_details saved_command_details;
struct timespec ts;
struct stat sb;
debug_decl(selinux_edit_copy_tfiles, SUDO_DEBUG_EDIT)
-
- /* Prepare selinux stuff (setexeccon) */
- if (selinux_setup(command_details->selinux_role,
- command_details->selinux_type, NULL, -1) != 0)
- debug_return_int(1);
if (nfiles < 1)
debug_return_int(0);
/* Construct common args for sesh */
- memcpy(&saved_command_details, command_details, sizeof(struct command_details));
- command_details->command = _PATH_SUDO_SESH;
- command_details->flags |= CD_SUDOEDIT_COPY;
-
sesh_nargs = 3 + (nfiles * 2) + 1;
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
if (sesh_args == NULL) {
@@ -913,32 +926,29 @@ selinux_edit_copy_tfiles(struct command_
if (sesh_ap - sesh_args > 3) {
/* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
- command_details->argv = sesh_args;
- rc = run_command(command_details);
+ rc = selinux_run_helper(sesh_args, command_details->envp);
switch (rc) {
case SESH_SUCCESS:
ret = 0;
break;
case SESH_ERR_NO_FILES:
sudo_warnx(U_("unable to copy temporary files back to their original location"));
- sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir);
break;
case SESH_ERR_SOME_FILES:
sudo_warnx(U_("unable to copy some of the temporary files back to their original location"));
- sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir);
+ break;
+ case SESH_ERR_KILLED:
+ sudo_warnx(U_("sesh: killed by a signal"));
break;
default:
sudo_warnx(U_("sesh: unknown error %d"), rc);
break;
}
+ if (ret != 0)
+ sudo_warnx(U_("contents of edit session left in %s"), edit_tmpdir);
}
free(sesh_args);
- /* Restore saved command_details. */
- command_details->command = saved_command_details.command;
- command_details->flags = saved_command_details.flags;
- command_details->argv = saved_command_details.argv;
-
debug_return_int(ret);
}
#endif /* HAVE_SELINUX */
@@ -990,6 +1000,15 @@ sudo_edit(struct command_details *comman
goto cleanup;
}
+#ifdef HAVE_SELINUX
+ /* Compute new SELinux security context. */
+ if (ISSET(command_details->flags, CD_RBAC_ENABLED)) {
+ if (selinux_setup(command_details->selinux_role,
+ command_details->selinux_type, NULL, -1, false) != 0)
+ goto cleanup;
+ }
+#endif
+
/* Copy editor files to temporaries. */
tf = calloc(nfiles, sizeof(*tf));
if (tf == NULL) {
@@ -1025,6 +1044,7 @@ sudo_edit(struct command_details *comman
/*
* Run the editor with the invoking user's creds,
* keeping track of the time spent in the editor.
+ * XXX - should run editor with user's context
*/
if (sudo_gettime_real(&times[0]) == -1) {
sudo_warn(U_("unable to read the clock"));
diff -up ./src/sudo_exec.h.symbolic-link-attack-3 ./src/sudo_exec.h
--- ./src/sudo_exec.h.symbolic-link-attack-3 2021-02-02 17:11:32.380020435 +0100
+++ ./src/sudo_exec.h 2021-02-02 17:11:32.382020407 +0100
@@ -73,6 +73,7 @@
*/
#define SESH_SUCCESS 0 /* successful operation */
#define SESH_ERR_FAILURE 1 /* unspecified error */
+#define SESH_ERR_KILLED 2 /* killed by a signal */
#define SESH_ERR_INVALID 30 /* invalid -e arg value */
#define SESH_ERR_BAD_PATHS 31 /* odd number of paths */
#define SESH_ERR_NO_FILES 32 /* copy error, no files copied */
diff -up ./src/sudo.h.symbolic-link-attack-3 ./src/sudo.h
--- ./src/sudo.h.symbolic-link-attack-3 2019-10-28 13:28:52.000000000 +0100
+++ ./src/sudo.h 2021-02-02 17:11:32.382020407 +0100
@@ -135,12 +135,11 @@ struct user_details {
#define CD_USE_PTY 0x001000
#define CD_SET_UTMP 0x002000
#define CD_EXEC_BG 0x004000
-#define CD_SUDOEDIT_COPY 0x008000
-#define CD_SUDOEDIT_FOLLOW 0x010000
-#define CD_SUDOEDIT_CHECKDIR 0x020000
-#define CD_SET_GROUPS 0x040000
-#define CD_LOGIN_SHELL 0x080000
-#define CD_OVERRIDE_UMASK 0x100000
+#define CD_SUDOEDIT_FOLLOW 0x008000
+#define CD_SUDOEDIT_CHECKDIR 0x010000
+#define CD_SET_GROUPS 0x020000
+#define CD_LOGIN_SHELL 0x040000
+#define CD_OVERRIDE_UMASK 0x080000
struct preserved_fd {
TAILQ_ENTRY(preserved_fd) entries;
@@ -240,7 +239,8 @@ int os_init_openbsd(int argc, char *argv
/* selinux.c */
int selinux_restore_tty(void);
int selinux_setup(const char *role, const char *type, const char *ttyn,
- int ttyfd);
+ int ttyfd, bool label_tty);
+int selinux_setcon(void);
void selinux_execve(int fd, const char *path, char *const argv[],
char *envp[], bool noexec);

View File

@ -0,0 +1,380 @@
diff -up ./src/copy_file.c.symbolic-link-attack-4 ./src/copy_file.c
--- ./src/copy_file.c.symbolic-link-attack-4 2021-02-02 16:35:18.453036846 +0100
+++ ./src/copy_file.c 2021-02-02 16:38:09.430731749 +0100
@@ -23,6 +23,7 @@
#include <config.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
@@ -126,3 +127,35 @@ write_error:
debug_return_int(-1);
}
}
+
+#ifdef HAVE_SELINUX
+bool
+sudo_check_temp_file(int tfd, const char *tfile, uid_t uid, struct stat *sb)
+{
+ struct stat sbuf;
+ debug_decl(sudo_check_temp_file, SUDO_DEBUG_UTIL);
+
+ if (sb == NULL)
+ sb = &sbuf;
+
+ if (fstat(tfd, sb) == -1) {
+ sudo_warn(U_("unable to stat %s"), tfile);
+ debug_return_bool(false);
+ }
+ if (!S_ISREG(sb->st_mode)) {
+ sudo_warnx(U_("%s: not a regular file"), tfile);
+ debug_return_bool(false);
+ }
+ if ((sb->st_mode & ALLPERMS) != (S_IRUSR|S_IWUSR)) {
+ sudo_warnx(U_("%s: bad file mode: 0%o"), tfile,
+ (unsigned int)(sb->st_mode & ALLPERMS));
+ debug_return_bool(false);
+ }
+ if (sb->st_uid != uid) {
+ sudo_warnx(U_("%s is owned by uid %u, should be %u"),
+ tfile, (unsigned int)sb->st_uid, (unsigned int)uid);
+ debug_return_bool(false);
+ }
+ debug_return_bool(true);
+}
+#endif /* SELINUX */
diff -up ./src/sesh.c.symbolic-link-attack-4 ./src/sesh.c
--- ./src/sesh.c.symbolic-link-attack-4 2021-02-02 16:35:18.450036887 +0100
+++ ./src/sesh.c 2021-02-02 16:38:52.907146897 +0100
@@ -134,7 +134,7 @@ main(int argc, char *argv[], char *envp[
static int
sesh_sudoedit(int argc, char *argv[])
{
- int i, oflags_dst, post, ret = SESH_ERR_FAILURE;
+ int i, oflags_src, oflags_dst, post, ret = SESH_ERR_FAILURE;
int fd_src = -1, fd_dst = -1, follow = 0;
ssize_t nread, nwritten;
struct stat sb;
@@ -178,10 +178,12 @@ sesh_sudoedit(int argc, char *argv[])
debug_return_int(SESH_ERR_BAD_PATHS);
/*
- * Use O_EXCL if we are not in the post editing stage
- * so that it's ensured that the temporary files are
- * created by us and that we are not opening any symlinks.
+ * In the pre-editing stage, use O_EXCL to ensure that the temporary
+ * files are created by us and that we are not opening any symlinks.
+ * In the post-editing stage, use O_NOFOLLOW so we don't follow symlinks
+ * when opening the temporary files.
*/
+ oflags_src = O_RDONLY|(post ? O_NONBLOCK|O_NOFOLLOW : follow);
oflags_dst = O_WRONLY|O_CREAT|(post ? follow : O_EXCL);
for (i = 0; i < argc - 1; i += 2) {
const char *path_src = argv[i];
@@ -191,7 +193,7 @@ sesh_sudoedit(int argc, char *argv[])
* doesn't exist, that's OK, we'll create an empty
* destination file.
*/
- if ((fd_src = open(path_src, O_RDONLY|follow, S_IRUSR|S_IWUSR)) < 0) {
+ if ((fd_src = open(path_src, oflags_src, S_IRUSR|S_IWUSR)) < 0) {
if (errno != ENOENT) {
sudo_warn("%s", path_src);
if (post) {
@@ -201,6 +203,14 @@ sesh_sudoedit(int argc, char *argv[])
goto cleanup_0;
}
}
+ if (post) {
+ /* Make sure the temporary file is safe and has the proper owner. */
+ if (!sudo_check_temp_file(fd_src, path_src, geteuid(), &sb)) {
+ ret = SESH_ERR_SOME_FILES;
+ goto nocleanup;
+ }
+ fcntl(fd_src, F_SETFL, fcntl(fd_src, F_GETFL, 0) & ~O_NONBLOCK);
+ }
if ((fd_dst = open(path_dst, oflags_dst, post ?
(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR))) < 0) {
@@ -218,10 +228,7 @@ sesh_sudoedit(int argc, char *argv[])
off_t len_dst = -1;
if (post) {
- if (fstat(fd_src, &sb) != 0) {
- ret = SESH_ERR_SOME_FILES;
- goto nocleanup;
- }
+ /* sudo_check_temp_file() filled in sb for us. */
len_src = sb.st_size;
if (fstat(fd_dst, &sb) != 0) {
ret = SESH_ERR_SOME_FILES;
diff -up ./src/sudo_edit.c.symbolic-link-attack-4 ./src/sudo_edit.c
--- ./src/sudo_edit.c.symbolic-link-attack-4 2021-02-02 16:35:18.452036860 +0100
+++ ./src/sudo_edit.c 2021-02-02 16:54:25.943429580 +0100
@@ -253,8 +253,10 @@ sudo_edit_mktemp(const char *ofile, char
} else {
len = asprintf(tfile, "%s/%s.XXXXXXXX", edit_tmpdir, cp);
}
- if (len == -1)
- sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ if (len == -1) {
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ debug_return_int(-1);
+ }
tfd = mkstemps(*tfile, suff ? strlen(suff) : 0);
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"%s -> %s, fd %d", ofile, *tfile, tfd);
@@ -757,7 +759,8 @@ bad:
#ifdef HAVE_SELINUX
static int
-selinux_run_helper(char *argv[], char *envp[])
+selinux_run_helper(uid_t uid, gid_t gid, int ngroups, GETGROUPS_T *groups,
+ char *const argv[], char *const envp[])
{
int status, ret = SESH_ERR_FAILURE;
const char *sesh;
@@ -777,8 +780,10 @@ selinux_run_helper(char *argv[], char *e
break;
case 0:
/* child runs sesh in new context */
- if (selinux_setcon() == 0)
+ if (selinux_setcon() == 0) {
+ switch_user(uid, gid, ngroups, groups);
execve(sesh, argv, envp);
+ }
_exit(SESH_ERR_FAILURE);
default:
/* parent waits */
@@ -797,7 +802,7 @@ selinux_edit_create_tfiles(struct comman
struct tempfile *tf, char *files[], int nfiles)
{
char **sesh_args, **sesh_ap;
- int i, rc, sesh_nargs;
+ int i, rc, error, sesh_nargs, ret = -1;
struct stat sb;
debug_decl(selinux_edit_create_tfiles, SUDO_DEBUG_EDIT)
@@ -809,7 +814,7 @@ selinux_edit_create_tfiles(struct comman
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
if (sesh_args == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
- debug_return_int(-1);
+ goto done;
}
*sesh_ap++ = "sesh";
*sesh_ap++ = "-e";
@@ -817,7 +822,6 @@ selinux_edit_create_tfiles(struct comman
*sesh_ap++ = "-h";
*sesh_ap++ = "0";
- /* XXX - temp files should be created with user's context */
for (i = 0; i < nfiles; i++) {
char *tfile, *ofile = files[i];
int tfd;
@@ -835,8 +839,7 @@ selinux_edit_create_tfiles(struct comman
if (tfd == -1) {
sudo_warn("mkstemps");
free(tfile);
- free(sesh_args);
- debug_return_int(-1);
+ goto done;
}
/* Helper will re-create temp file with proper security context. */
close(tfd);
@@ -847,8 +850,10 @@ selinux_edit_create_tfiles(struct comman
*sesh_ap = NULL;
/* Run sesh -e [-h] 0 <o1> <t1> ... <on> <tn> */
- rc = selinux_run_helper(sesh_args, command_details->envp);
- switch (rc) {
+ error = selinux_run_helper(command_details->uid, command_details->gid,
+ command_details->ngroups, command_details->groups, sesh_args,
+ command_details->envp);
+ switch (error) {
case SESH_SUCCESS:
break;
case SESH_ERR_BAD_PATHS:
@@ -858,21 +863,34 @@ selinux_edit_create_tfiles(struct comman
case SESH_ERR_KILLED:
sudo_fatalx(U_("sesh: killed by a signal"));
default:
- sudo_fatalx(U_("sesh: unknown error %d"), rc);
+ sudo_fatalx(U_("sesh: unknown error %d"), error);
+ goto done;
}
- /* Chown to user's UID so they can edit the temporary files. */
for (i = 0; i < nfiles; i++) {
- if (chown(tf[i].tfile, user_details.uid, user_details.gid) != 0) {
- sudo_warn("unable to chown(%s) to %d:%d for editing",
- tf[i].tfile, user_details.uid, user_details.gid);
- }
+ int tfd = open(tf[i].tfile, O_RDONLY|O_NONBLOCK|O_NOFOLLOW);
+ if (tfd == -1) {
+ sudo_warn(U_("unable to open %s"), tf[i].tfile);
+ goto done;
+ }
+ if (!sudo_check_temp_file(tfd, tf[i].tfile, command_details->uid, NULL)) {
+ close(tfd);
+ goto done;
+ }
+ if (fchown(tfd, user_details.uid, user_details.gid) != 0) {
+ sudo_warn("unable to chown(%s) to %d:%d for editing",
+ tf[i].tfile, user_details.uid, user_details.gid);
+ close(tfd);
+ goto done;
+ }
+ close(tfd);
}
+done:
/* Contents of tf will be freed by caller. */
free(sesh_args);
- return (nfiles);
+ debug_return_int(ret);
}
static int
@@ -880,7 +898,8 @@ selinux_edit_copy_tfiles(struct command_
struct tempfile *tf, int nfiles, struct timespec *times)
{
char **sesh_args, **sesh_ap;
- int i, rc, sesh_nargs, ret = 1;
+ int i, rc, error, sesh_nargs, ret = 1;
+ int tfd = -1;
struct timespec ts;
struct stat sb;
debug_decl(selinux_edit_copy_tfiles, SUDO_DEBUG_EDIT)
@@ -901,33 +920,43 @@ selinux_edit_copy_tfiles(struct command_
/* Construct args for sesh -e 1 */
for (i = 0; i < nfiles; i++) {
- if (stat(tf[i].tfile, &sb) == 0) {
- mtim_get(&sb, ts);
- if (tf[i].osize == sb.st_size && sudo_timespeccmp(&tf[i].omtim, &ts, ==)) {
- /*
- * If mtime and size match but the user spent no measurable
- * time in the editor we can't tell if the file was changed.
- */
- if (sudo_timespeccmp(&times[0], &times[1], !=)) {
- sudo_warnx(U_("%s unchanged"), tf[i].ofile);
- unlink(tf[i].tfile);
- continue;
- }
+ if (tfd != -1)
+ close(tfd);
+ if ((tfd = open(tf[i].tfile, O_RDONLY|O_NONBLOCK|O_NOFOLLOW)) == -1) {
+ sudo_warn(U_("unable to open %s"), tf[i].tfile);
+ continue;
+ }
+ if (!sudo_check_temp_file(tfd, tf[i].tfile, user_details.uid, &sb))
+ continue;
+ mtim_get(&sb, ts);
+ if (tf[i].osize == sb.st_size && sudo_timespeccmp(&tf[i].omtim, &ts, ==)) {
+ /*
+ * If mtime and size match but the user spent no measurable
+ * time in the editor we can't tell if the file was changed.
+ */
+ if (sudo_timespeccmp(&times[0], &times[1], !=)) {
+ sudo_warnx(U_("%s unchanged"), tf[i].ofile);
+ unlink(tf[i].tfile);
+ continue;
}
}
*sesh_ap++ = tf[i].tfile;
*sesh_ap++ = tf[i].ofile;
- if (chown(tf[i].tfile, command_details->uid, command_details->gid) != 0) {
+ if (fchown(tfd, command_details->uid, command_details->gid) != 0) {
sudo_warn("unable to chown(%s) back to %d:%d", tf[i].tfile,
command_details->uid, command_details->gid);
}
}
*sesh_ap = NULL;
+ if (tfd != -1)
+ close(tfd);
if (sesh_ap - sesh_args > 3) {
/* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
- rc = selinux_run_helper(sesh_args, command_details->envp);
- switch (rc) {
+ error = selinux_run_helper(command_details->uid, command_details->gid,
+ command_details->ngroups, command_details->groups, sesh_args,
+ command_details->envp);
+ switch (error) {
case SESH_SUCCESS:
ret = 0;
break;
@@ -941,7 +970,7 @@ selinux_edit_copy_tfiles(struct command_
sudo_warnx(U_("sesh: killed by a signal"));
break;
default:
- sudo_warnx(U_("sesh: unknown error %d"), rc);
+ sudo_warnx(U_("sesh: unknown error %d"), error);
break;
}
if (ret != 0)
@@ -963,7 +992,7 @@ sudo_edit(struct command_details *comman
{
struct command_details saved_command_details;
char **nargv = NULL, **ap, **files = NULL;
- int errors, i, ac, nargc, rc;
+ int errors, i, ac, nargc, ret;
int editor_argc = 0, nfiles = 0;
struct timespec times[2];
struct tempfile *tf = NULL;
@@ -1058,7 +1087,7 @@ sudo_edit(struct command_details *comman
command_details->ngroups = user_details.ngroups;
command_details->groups = user_details.groups;
command_details->argv = nargv;
- rc = run_command(command_details);
+ ret = run_command(command_details);
if (sudo_gettime_real(&times[1]) == -1) {
sudo_warn(U_("unable to read the clock"));
goto cleanup;
@@ -1080,14 +1109,16 @@ sudo_edit(struct command_details *comman
else
#endif
errors = sudo_edit_copy_tfiles(command_details, tf, nfiles, times);
- if (errors)
- goto cleanup;
+ if (errors) {
+ /* Preserve the edited temporary files. */
+ ret = W_EXITCODE(1, 0);
+ }
for (i = 0; i < nfiles; i++)
free(tf[i].tfile);
free(tf);
free(nargv);
- debug_return_int(rc);
+ debug_return_int(ret);
cleanup:
/* Clean up temp files and return. */
diff -up ./src/sudo_exec.h.symbolic-link-attack-4 ./src/sudo_exec.h
--- ./src/sudo_exec.h.symbolic-link-attack-4 2021-02-02 16:35:18.452036860 +0100
+++ ./src/sudo_exec.h 2021-02-02 16:35:18.454036833 +0100
@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
- * Copyright (c) 2010-2016 Todd C. Miller <Todd.Miller@sudo.ws>
+ * Copyright (c) 2010-2017, 2020-2021 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
@@ -84,9 +84,11 @@
*/
struct command_details;
struct command_status;
+struct stat;
/* copy_file.c */
int sudo_copy_file(const char *src, int src_fd, off_t src_len, const char *dst, int dst_fd, off_t dst_len);
+bool sudo_check_temp_file(int tfd, const char *tname, uid_t uid, struct stat *sb);
/* exec.c */
void exec_cmnd(struct command_details *details, int errfd);

View File

@ -0,0 +1,47 @@
diff -up ./src/copy_file.c.symbolic-link-attack-5 ./src/copy_file.c
--- ./src/copy_file.c.symbolic-link-attack-5 2021-02-02 17:18:05.355567274 +0100
+++ ./src/copy_file.c 2021-02-02 17:19:09.904671563 +0100
@@ -128,7 +128,6 @@ write_error:
}
}
-#ifdef HAVE_SELINUX
bool
sudo_check_temp_file(int tfd, const char *tfile, uid_t uid, struct stat *sb)
{
@@ -158,4 +157,3 @@ sudo_check_temp_file(int tfd, const char
}
debug_return_bool(true);
}
-#endif /* SELINUX */
diff -up ./src/sudo_edit.c.symbolic-link-attack-5 ./src/sudo_edit.c
--- ./src/sudo_edit.c.symbolic-link-attack-5 2021-02-02 17:18:05.355567274 +0100
+++ ./src/sudo_edit.c 2021-02-02 17:18:05.356567260 +0100
@@ -692,24 +692,17 @@ sudo_edit_copy_tfiles(struct command_det
/* Copy contents of temp files to real ones. */
for (i = 0; i < nfiles; i++) {
- int rc = -1;
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
"seteuid(%u)", (unsigned int)user_details.uid);
if (seteuid(user_details.uid) != 0)
sudo_fatal("seteuid(%u)", (unsigned int)user_details.uid);
tfd = sudo_edit_open(tf[i].tfile, O_RDONLY,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, NULL);
- if (tfd != -1)
- rc = fstat(tfd, &sb);
- sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
- "seteuid(%u)", ROOT_UID);
if (seteuid(ROOT_UID) != 0)
sudo_fatal("seteuid(ROOT_UID)");
- if (rc == -1 || !S_ISREG(sb.st_mode)) {
- if (rc == -1)
- sudo_warn("%s", tf[i].tfile);
- else
- sudo_warnx(U_("%s: not a regular file"), tf[i].tfile);
+ sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ "seteuid(%u)", ROOT_UID);
+ if (tfd == -1 || !sudo_check_temp_file(tfd, tf[i].tfile, user_details.uid, &sb)) {
sudo_warnx(U_("%s left unmodified"), tf[i].ofile);
if (tfd != -1)
close(tfd);

View File

@ -1,74 +0,0 @@
From 73006fb25f0ebc35bc46b8f20036d40fcbb6de53 Mon Sep 17 00:00:00 2001
From: Radovan Sroka <rsroka@redhat.com>
Date: Thu, 1 Apr 2021 21:42:03 +0200
Subject: [PATCH] Removed depricated security_context_t
Signed-off-by: Radovan Sroka <rsroka@redhat.com>
---
src/selinux.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/selinux.c b/src/selinux.c
index a2f73f8d0..c2f50aafb 100644
--- a/src/selinux.c
+++ b/src/selinux.c
@@ -58,10 +58,10 @@
#include "sudo_exec.h"
static struct selinux_state {
- security_context_t old_context;
- security_context_t new_context;
- security_context_t tty_con_raw;
- security_context_t new_tty_con_raw;
+ char * old_context;
+ char * new_context;
+ char * tty_con_raw;
+ char * new_tty_con_raw;
const char *ttyn;
int ttyfd;
int enforcing;
@@ -69,8 +69,8 @@ static struct selinux_state {
#ifdef HAVE_LINUX_AUDIT
static int
-audit_role_change(const security_context_t old_context,
- const security_context_t new_context, const char *ttyn, int result)
+audit_role_change(const char * old_context,
+ const char * new_context, const char *ttyn, int result)
{
int au_fd, rc = -1;
char *message;
@@ -111,7 +111,7 @@ int
selinux_restore_tty(void)
{
int ret = -1;
- security_context_t chk_tty_con_raw = NULL;
+ char * chk_tty_con_raw = NULL;
debug_decl(selinux_restore_tty, SUDO_DEBUG_SELINUX);
if (se_state.ttyfd == -1 || se_state.new_tty_con_raw == NULL) {
@@ -166,8 +166,8 @@ selinux_restore_tty(void)
static int
relabel_tty(const char *ttyn, int ptyfd)
{
- security_context_t tty_con = NULL;
- security_context_t new_tty_con = NULL;
+ char * tty_con = NULL;
+ char * new_tty_con = NULL;
struct stat sb;
int fd;
debug_decl(relabel_tty, SUDO_DEBUG_SELINUX);
@@ -308,10 +308,10 @@ relabel_tty(const char *ttyn, int ptyfd)
* Returns a new security context based on the old context and the
* specified role and type.
*/
-security_context_t
-get_exec_context(security_context_t old_context, const char *role, const char *type)
+char *
+get_exec_context(char * old_context, const char *role, const char *type)
{
- security_context_t new_context = NULL;
+ char * new_context = NULL;
context_t context = NULL;
char *typebuf = NULL;
debug_decl(get_exec_context, SUDO_DEBUG_SELINUX);

View File

@ -1,51 +0,0 @@
From 613a8053dbc3ab43cf0cdaf09f207ffdb0b40e08 Mon Sep 17 00:00:00 2001
From: Radovan Sroka <rsroka@redhat.com>
Date: Wed, 7 Apr 2021 14:43:40 +0200
Subject: [PATCH] Fixed bad condition for sesh args
In selinux_edit_copy_tfiles() when there is only one file and the open()
fails then number of arguments is lower than expected.
Sudo should return error with or without "Defaults !sudoedit_checkdir" set.
This was found with regression testing of CVE-2021-23240.
Signed-off-by: Radovan Sroka <rsroka@redhat.com>
---
src/sudo_edit.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/src/sudo_edit.c b/src/sudo_edit.c
index 41fc61c3a..15c75d8c4 100644
--- a/src/sudo_edit.c
+++ b/src/sudo_edit.c
@@ -529,6 +529,8 @@ selinux_edit_copy_tfiles(struct command_details *command_details,
if (nfiles < 1)
debug_return_int(0);
+ const int check_dir = ISSET(command_details->flags, CD_SUDOEDIT_CHECKDIR);
+
/* Construct common args for sesh */
sesh_nargs = 5 + (nfiles * 2) + 1;
sesh_args = sesh_ap = reallocarray(NULL, sesh_nargs, sizeof(char *));
@@ -538,7 +540,7 @@ selinux_edit_copy_tfiles(struct command_details *command_details,
}
*sesh_ap++ = "sesh";
*sesh_ap++ = "-e";
- if (ISSET(command_details->flags, CD_SUDOEDIT_CHECKDIR)) {
+ if (check_dir) {
if ((user_str = selinux_fmt_sudo_user()) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto done;
@@ -581,7 +583,11 @@ selinux_edit_copy_tfiles(struct command_details *command_details,
if (tfd != -1)
close(tfd);
- if (sesh_ap - sesh_args > 3) {
+ /*
+ * check dir adds two more args to the array
+ */
+ if ((!check_dir && sesh_ap - sesh_args > 3)
+ || (check_dir && sesh_ap - sesh_args > 5)) {
/* Run sesh -e 1 <t1> <o1> ... <tn> <on> */
error = selinux_run_helper(command_details->cred.uid, command_details->cred.gid,
command_details->cred.ngroups, command_details->cred.groups, sesh_args,

View File

@ -0,0 +1,11 @@
diff -up ./src/sudo_edit.c.rest ./src/sudo_edit.c
--- ./src/sudo_edit.c.rest 2021-11-21 18:41:09.420657680 +0100
+++ ./src/sudo_edit.c 2021-11-21 18:42:23.214272777 +0100
@@ -878,6 +878,7 @@ selinux_edit_create_tfiles(struct comman
}
close(tfd);
}
+ ret = nfiles;
done:
/* Contents of tf will be freed by caller. */

View File

@ -1,19 +0,0 @@
diff -up ./plugins/sudoers/audit.c.undefined ./plugins/sudoers/audit.c
--- ./plugins/sudoers/audit.c.undefined 2021-07-12 14:59:53.472306208 +0200
+++ ./plugins/sudoers/audit.c 2021-07-12 15:00:45.620620369 +0200
@@ -197,7 +197,6 @@ sudoers_audit_open(unsigned int version,
debug_return_int(ret);
}
-#ifdef SUDOERS_LOG_CLIENT
static void
audit_to_eventlog(struct eventlog *evlog, char * const command_info[],
char * const run_argv[], char * const run_envp[])
@@ -244,6 +243,7 @@ audit_to_eventlog(struct eventlog *evlog
debug_return;
}
+#ifdef SUDOERS_LOG_CLIENT
static bool
log_server_accept(char * const command_info[], char * const run_argv[],
char * const run_envp[])

View File

@ -0,0 +1,54 @@
diff -up ./plugins/sudoers/auth/pam.c.krb5ccname ./plugins/sudoers/auth/pam.c
--- ./plugins/sudoers/auth/pam.c.krb5ccname 2019-10-28 13:27:38.000000000 +0100
+++ ./plugins/sudoers/auth/pam.c 2021-12-06 11:14:15.580226222 +0100
@@ -119,10 +119,10 @@ conv_filter_init(void)
/*
* Messages from PAM account management when trusted mode is enabled:
- * 1 Last successful login for %s: %s
- * 2 Last successful login for %s: %s on %s
- * 3 Last unsuccessful login for %s: %s
- * 4 Last unsuccessful login for %s: %s on %s
+ * 1 Last successful login for %s: %s
+ * 2 Last successful login for %s: %s on %s
+ * 3 Last unsuccessful login for %s: %s
+ * 4 Last unsuccessful login for %s: %s on %s
*/
if ((catd = catopen("pam_comsec", NL_CAT_LOCALE)) != -1) {
maxfilters += 4;
@@ -290,6 +290,7 @@ sudo_pam_init_quiet(struct passwd *pw, s
int
sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback)
{
+ const char *envccname;
const char *s;
int *pam_status = (int *) auth->data;
debug_decl(sudo_pam_verify, SUDOERS_DEBUG_AUTH)
@@ -298,8 +299,27 @@ sudo_pam_verify(struct passwd *pw, char
getpass_error = false; /* set by converse if user presses ^C */
conv_callback = callback; /* passed to conversation function */
+ /* Set KRB5CCNAME from the user environment if not set to propagate this
+ * information to PAM modules that may use it to authentication. */
+ envccname = sudo_getenv("KRB5CCNAME");
+ if (envccname == NULL && user_ccname != NULL) {
+ if (sudo_setenv("KRB5CCNAME", user_ccname, true) != 0) {
+ sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+ "unable to set KRB5CCNAME");
+ debug_return_int(AUTH_FAILURE);
+ }
+ }
+
/* PAM_SILENT prevents the authentication service from generating output. */
*pam_status = pam_authenticate(pamh, PAM_SILENT);
+
+ /* Restore KRB5CCNAME to its original value. */
+ if (envccname == NULL && sudo_unsetenv("KRB5CCNAME") != 0) {
+ sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
+ "unable to restore KRB5CCNAME");
+ debug_return_int(AUTH_FAILURE);
+ }
+
if (getpass_error) {
/* error or ^C from tgetpass() */
debug_return_int(AUTH_INTR);

View File

@ -0,0 +1,35 @@
From 727056e0c9519d8eecde801e950b35f2f69c72e2 Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
Date: Fri, 23 Apr 2021 07:41:27 -0600
Subject: [PATCH] Make sure SIGCHLD is not ignored when sudo is executed. If
SIGCHLD is ignored there is a race condition between when the process is
executed and when the SIGCHLD handler is installed. This fixes the bug
described by GitHub PR #98
---
src/signal.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/signal.c b/src/signal.c
index 7f90d707b..866b64790 100644
--- a/src/signal.c
+++ b/src/signal.c
@@ -133,6 +133,18 @@ init_signals(void)
case SIGTTOU:
/* Don't install these until exec time. */
break;
+ case SIGCHLD:
+ /* Sudo needs to be able to catch SIGCHLD. */
+ if (ss->sa.sa_handler == SIG_IGN) {
+ sudo_debug_printf(SUDO_DEBUG_INFO,
+ "will restore signal %d on exec", SIGCHLD);
+ ss->restore = true;
+ }
+ if (sigaction(SIGCHLD, &sa, NULL) != 0) {
+ sudo_warn(U_("unable to set handler for signal %d"),
+ SIGCHLD);
+ }
+ break;
default:
if (ss->sa.sa_handler != SIG_IGN) {
if (sigaction(ss->signo, &sa, NULL) != 0) {

View File

@ -1,7 +1,7 @@
Summary: Allows restricted root access for specified users
Name: sudo
Version: 1.9.5p2
Release: 1%{?dist}
Version: 1.8.29
Release: 10%{?dist}
License: ISC
Group: Applications/System
URL: https://www.sudo.ws/
@ -31,27 +31,57 @@ BuildRequires: openldap-devel
BuildRequires: pam-devel
BuildRequires: zlib-devel
Patch2: sudo-1.9.5-undefined-symbol.patch
Patch3: sudo-1.9.5-selinux-t.patch
Patch4: sudo-1.9.5-sesh-bad-condition.patch
Patch5: sudo-1.9.5-utmp-leak.patch
Patch6: covscan.patch
Patch7: sha-digest-calc.patch
Patch8: sudo-1.9.12-CVE-2023-22809.patch
# don't strip
Patch1: sudo-1.6.7p5-strip.patch
# 881258 - rpmdiff: added missing sudo-ldap.conf manpage
Patch2: sudo-1.8.23-sudoldapconfman.patch
# env debug patch
Patch3: sudo-1.7.2p1-envdebug.patch
# 1247591 - Sudo taking a long time when user information is stored externally.
Patch4: sudo-1.8.23-legacy-group-processing.patch
# 840980 - sudo creates a new parent process
# Adds cmnd_no_wait Defaults option
Patch5: sudo-1.8.23-nowaitopt.patch
# 1312486 - RHEL7 sudo logs username "root" instead of realuser in /var/log/secure
Patch6: sudo-1.8.6p7-logsudouser.patch
# 1786987 - CVE-2019-19232 sudo: attacker with access to a Runas ALL sudoer account
# can impersonate a nonexistent user [rhel-8]
Patch7: sudo-1.8.29-CVE-2019-19232.patch
# 1796518 - [RFE] add optional check for the target user shell
Patch8: sudo-1.8.29-CVE-2019-19234.patch
# 1798093 - CVE-2019-18634 sudo: Stack based buffer overflow in when pwfeedback is enabled [rhel-8.2.0]
Patch9: sudo-1.8.29-CVE-2019-18634.patch
Patch9: sudo-1.9.13-CVE-2023-28486-7-1.patch
Patch10: sudo-1.9.13-CVE-2023-28486-7-2.patch
Patch11: sudo-1.9.13-CVE-2023-28486-7-3.patch
Patch12: sudo-1.9.13-CVE-2023-28486-7-4.patch
Patch13: sudo-1.9.13-CVE-2023-28486-7-5.patch
Patch14: sudo-1.9.13-CVE-2023-28486-7-6.patch
Patch15: sudo-1.9.13-CVE-2023-28486-7-7.patch
Patch16: sudo-1.9.13-CVE-2023-28486-7-8.patch
Patch17: sudo-1.9.13-CVE-2023-28486-7-9.patch
# 1815164 - sudo allows privilege escalation with expire password
Patch10: sudo-1.8.29-expired-password-part1.patch
Patch11: sudo-1.8.29-expired-password-part2.patch
Patch18: linker.patch
# 1917734 - EMBARGOED CVE-2021-3156 sudo: Heap-buffer overflow in argument parsing [rhel-8.4.0]
Patch12: sudo-1.8.31-CVE-2021-3156.patch
# 1916434 - CVE-2021-23239 sudo: possible directory existence test due to race condition in sudoedit [rhel-8]
Patch13: sudo-1.9.5-CVE-2021-23239.patch
# 1917038 - CVE-2021-23240 sudo: symbolic link attack in SELinux-enabled sudoedit [rhel-8]
Patch14: sudo-1.9.5-CVE-2021-23240-1.patch
Patch15: sudo-1.9.5-CVE-2021-23240-2.patch
Patch16: sudo-1.9.5-CVE-2021-23240-3.patch
Patch17: sudo-1.9.5-CVE-2021-23240-4.patch
Patch18: sudo-1.9.5-CVE-2021-23240-5.patch
Patch19: sudo-1.9.15-CVE-2023-42465.patch
# 2029551 - sudoedit does not work with selinux args
Patch19: sudo-1.9.5-sudoedit-selinux.patch
# 1999751 - Request to backport https://www.sudo.ws/repos/sudo/rev/b4c91a0f72e7 to RHEL 8
Patch20: sudo-1.9.7-sigchild.patch
# 1917379 - [RFE] pass KRB5CCNAME to pam_authenticate environment if available
Patch21: sudo-1.9.7-krb5ccname.patch
# 1986572 - utmp resource leak in sudo
Patch22: sudo-1.9.7-utmp-leak.patch
# 2114576 - sudo digest check fails incorrectly for certain file sizes (SHA512/SHA384)
Patch23: sha-digest-calc.patch
# 2161221 - EMBARGOED CVE-2023-22809 sudo: arbitrary file write with privileges of the RunAs user [rhel-8.8.0]
Patch24: sudo-1.9.12-CVE-2023-22809-whitelist.patch
Patch25: sudo-1.9.12-CVE-2023-22809-backports.patch
Patch26: sudo-1.9.12-CVE-2023-22809.patch
%description
Sudo (superuser do) allows a system administrator to give certain
@ -76,27 +106,39 @@ plugins that use %{name}.
%prep
%setup -q
%patch -P 2 -p1 -b .undefined
%patch -P 3 -p1 -b .selinux-t
%patch -P 4 -p1 -b .bad-cond
%patch -P 5 -p1 -b .utmp-leak
%patch -P 6 -p1 -b .covscan
%patch -P 7 -p1 -b .sha-digest
%patch -P 8 -p1 -b .cve-fix
%patch1 -p1 -b .strip
%patch2 -p1 -b .sudoldapconfman
%patch3 -p1 -b .env-debug
%patch4 -p1 -b .legacy-processing
%patch5 -p1 -b .nowait
%patch6 -p1 -b .logsudouser
%patch7 -p1 -b .CVE-2019-19232
%patch8 -p1 -b .target-shell
%patch9 -p1 -b .CVE-2019-18634
%patch -P 9 -p1 -b .cve-escape-1
%patch -P 10 -p1 -b .cve-escape-2
%patch -P 11 -p1 -b .cve-escape-3
%patch -P 12 -p1 -b .cve-escape-4
%patch -P 13 -p1 -b .cve-escape-5
%patch -P 14 -p1 -b .cve-escape-6
%patch -P 15 -p1 -b .cve-escape-7
%patch -P 16 -p1 -b .cve-escape-8
%patch -P 17 -p1 -b .cve-escape-9
%patch10 -p1 -b .expired1
%patch11 -p1 -b .expired2
%patch -P 18 -p1 -b .linker
%patch12 -p1 -b .heap-buffer
%patch -P 19 -p1 -b .rowhammer
%patch13 -p1 -b .sudoedit-race
%patch14 -p1 -b .symbolic-link-attack-1
%patch15 -p1 -b .symbolic-link-attack-2
%patch16 -p1 -b .symbolic-link-attack-3
%patch17 -p1 -b .symbolic-link-attack-4
%patch18 -p1 -b .symbolic-link-attack-5
%patch19 -p1 -b .sudoedit-selinux
%patch20 -p1 -b .sigchild
%patch21 -p1 -b .krb5ccname
%patch22 -p1 -b .utmp-leak
%patch23 -p1 -b .sha-digest
%patch24 -p1 -b .whitelist
%patch25 -p1 -b .backports
%patch26 -p1 -b .cve
%build
# Remove bundled copy of zlib
@ -116,10 +158,7 @@ export CFLAGS="$RPM_OPT_FLAGS $F_PIE" LDFLAGS="-pie -Wl,-z,relro -Wl,-z,now"
--sbindir=%{_sbindir} \
--libdir=%{_libdir} \
--docdir=%{_pkgdocdir} \
--disable-openssl \
--disable-root-mailer \
--disable-log-server \
--disable-log-client \
--with-logging=syslog \
--with-logfac=authpriv \
--with-pam \
@ -133,7 +172,6 @@ export CFLAGS="$RPM_OPT_FLAGS $F_PIE" LDFLAGS="-pie -Wl,-z,relro -Wl,-z,now"
--with-selinux \
--with-passprompt="[sudo] password for %p: " \
--with-linux-audit \
--disable-python \
--with-sssd
# --without-kerb5 \
# --without-kerb4
@ -154,18 +192,8 @@ install -p -d -m 700 $RPM_BUILD_ROOT/var/db/sudo
install -p -d -m 700 $RPM_BUILD_ROOT/var/db/sudo/lectured
install -p -d -m 750 $RPM_BUILD_ROOT/etc/sudoers.d
install -p -c -m 0440 %{SOURCE1} $RPM_BUILD_ROOT/etc/sudoers
install -p -c -m 0640 %{SOURCE3} $RPM_BUILD_ROOT/etc/sudo.conf
install -p -c -m 0640 %{SOURCE2} $RPM_BUILD_ROOT/%{_sysconfdir}/sudo-ldap.conf
install -p -c -m 0640 %{SOURCE3} $RPM_BUILD_ROOT/%{_sysconfdir}/sudo.conf
# create sudo-ldap.conf man
echo ".so man5/sudoers.ldap.5" > sudo-ldap.conf.5
gzip sudo-ldap.conf.5
install -p -c -m 0644 sudo-ldap.conf.5.gz $RPM_BUILD_ROOT/%{_mandir}/man5/sudo-ldap.conf.5.gz
rm -f sudo-ldap.conf.5.gz
# we are not building sendlog so we don't need this
rm -rf $RPM_BUILD_ROOT/%{_mandir}/man8/sudo_sendlog.8
# Add sudo to protected packages
install -p -d -m 755 $RPM_BUILD_ROOT/etc/dnf/protected.d/
@ -236,9 +264,7 @@ rm -rf $RPM_BUILD_ROOT
%dir %{_libexecdir}/sudo
%attr(0755,root,root) %{_libexecdir}/sudo/sesh
%attr(0644,root,root) %{_libexecdir}/sudo/sudo_noexec.so
%attr(0644,root,root) %{_libexecdir}/sudo/audit_json.so
%attr(0644,root,root) %{_libexecdir}/sudo/sudoers.so
%attr(0644,root,root) %{_libexecdir}/sudo/sample_approval.so
%attr(0644,root,root) %{_libexecdir}/sudo/group_file.so
%attr(0644,root,root) %{_libexecdir}/sudo/system_group.so
%attr(0644,root,root) %{_libexecdir}/sudo/libsudo_util.so.?.?.?
@ -272,16 +298,6 @@ rm -rf $RPM_BUILD_ROOT
%{_mandir}/man8/sudo_plugin.8*
%changelog
* Mon Jan 22 2024 Radovan Sroka <rsroka@redhat.com> - 1.9.5p2-1
RHEL 8.9.0.Z ERRATUM
- Rebase to 1.9.5p2
- CVE-2023-28486 sudo: Sudo does not escape control characters in log messages
Resolves: RHEL-21825
- CVE-2023-28487 sudo: Sudo does not escape control characters in sudoreplay output
Resolves: RHEL-21831
- CVE-2023-42465 sudo: Targeted Corruption of Register and Stack Variables
Resolves: RHEL-21820
* Wed Jan 11 2023 Radovan Sroka <rsroka@redhat.com> - 1.8.29.9
RHEL 8.8.0 ERRATUM
- CVE-2023-22809 sudo: arbitrary file write with privileges of the RunAs user