f8883a97a0
- 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 Signed-off-by: Radovan Sroka <rsroka@redhat.com>
911 lines
30 KiB
Diff
911 lines
30 KiB
Diff
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);
|
|
}
|