971 lines
31 KiB
Diff
971 lines
31 KiB
Diff
|
|
From 334daf92b31b79ce68ed75e2ee14fca265f029ca Mon Sep 17 00:00:00 2001
|
||
|
|
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
|
||
|
|
Date: Wed, 18 Jan 2023 08:21:34 -0700
|
||
|
|
Subject: [PATCH] Escape control characters in log messages and "sudoreplay -l"
|
||
|
|
output. The log message contains user-controlled strings that could include
|
||
|
|
things like terminal control characters. Space characters in the command
|
||
|
|
path are now also escaped.
|
||
|
|
|
||
|
|
Command line arguments that contain spaces are surrounded with
|
||
|
|
single quotes and any literal single quote or backslash characters
|
||
|
|
are escaped with a backslash. This makes it possible to distinguish
|
||
|
|
multiple command line arguments from a single argument that contains
|
||
|
|
spaces.
|
||
|
|
|
||
|
|
Issue found by Matthieu Barjole and Victor Cutillas of Synacktiv
|
||
|
|
(https://synacktiv.com).
|
||
|
|
---
|
||
|
|
docs/sudoers.man.in | 44 ++++++--
|
||
|
|
docs/sudoers.mdoc.in | 38 +++++--
|
||
|
|
docs/sudoreplay.man.in | 9 ++
|
||
|
|
docs/sudoreplay.mdoc.in | 10 ++
|
||
|
|
include/sudo_lbuf.h | 7 ++
|
||
|
|
lib/eventlog/eventlog.c | 210 ++++++++++-------------------------
|
||
|
|
lib/iolog/iolog_json.c | 39 -------
|
||
|
|
lib/util/lbuf.c | 106 ++++++++++++++++++
|
||
|
|
lib/util/util.exp.in | 1 +
|
||
|
|
plugins/sudoers/sudoreplay.c | 144 ++++++++++++++++++++----
|
||
|
|
10 files changed, 383 insertions(+), 225 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/docs/sudoers.man.in b/docs/sudoers.man.in
|
||
|
|
index 787591111..be9dc327e 100644
|
||
|
|
--- a/docs/sudoers.man.in
|
||
|
|
+++ b/docs/sudoers.man.in
|
||
|
|
@@ -5877,14 +5877,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
|
||
|
|
@@ -5933,6 +5950,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
|
||
|
|
@@ -5957,7 +5977,7 @@ A list of environment variables specified on the command line,
|
||
|
|
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,
|
||
|
|
@@ -6195,17 +6215,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 --git a/docs/sudoers.mdoc.in b/docs/sudoers.mdoc.in
|
||
|
|
index c72bc660d..92edacb9d 100644
|
||
|
|
--- a/docs/sudoers.mdoc.in
|
||
|
|
+++ b/docs/sudoers.mdoc.in
|
||
|
|
@@ -5503,12 +5503,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
|
||
|
|
@@ -5551,6 +5568,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
|
||
|
|
@@ -5570,7 +5589,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
|
||
|
|
@@ -5794,14 +5813,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 --git a/docs/sudoreplay.man.in b/docs/sudoreplay.man.in
|
||
|
|
index f1fed2d42..73dbd52f9 100644
|
||
|
|
--- a/docs/sudoreplay.man.in
|
||
|
|
+++ b/docs/sudoreplay.man.in
|
||
|
|
@@ -170,6 +170,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 --git a/docs/sudoreplay.mdoc.in b/docs/sudoreplay.mdoc.in
|
||
|
|
index 940a41462..005cf1f7f 100644
|
||
|
|
--- a/docs/sudoreplay.mdoc.in
|
||
|
|
+++ b/docs/sudoreplay.mdoc.in
|
||
|
|
@@ -162,6 +162,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 --git a/include/sudo_lbuf.h b/include/sudo_lbuf.h
|
||
|
|
index be0a04f73..29090cb8d 100644
|
||
|
|
--- a/include/sudo_lbuf.h
|
||
|
|
+++ b/include/sudo_lbuf.h
|
||
|
|
@@ -36,9 +36,15 @@ struct sudo_lbuf {
|
||
|
|
|
||
|
|
typedef int (*sudo_lbuf_output_t)(const char *);
|
||
|
|
|
||
|
|
+/* Flags for sudo_lbuf_append_esc() */
|
||
|
|
+#define LBUF_ESC_CNTRL 0x01
|
||
|
|
+#define LBUF_ESC_BLANK 0x02
|
||
|
|
+#define LBUF_ESC_QUOTE 0x04
|
||
|
|
+
|
||
|
|
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, ...) sudo_printflike(2, 3);
|
||
|
|
+sudo_dso_public bool sudo_lbuf_append_esc_v1(struct sudo_lbuf *lbuf, int flags, const char *fmt, ...) sudo_printflike(3, 4);
|
||
|
|
sudo_dso_public bool sudo_lbuf_append_quoted_v1(struct sudo_lbuf *lbuf, const char *set, const char *fmt, ...) sudo_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_v1(struct sudo_lbuf *lbuf);
|
||
|
|
#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 --git a/lib/eventlog/eventlog.c b/lib/eventlog/eventlog.c
|
||
|
|
index c0183d3d2..e5dae626a 100644
|
||
|
|
--- a/lib/eventlog/eventlog.c
|
||
|
|
+++ b/lib/eventlog/eventlog.c
|
||
|
|
@@ -1,7 +1,7 @@
|
||
|
|
/*
|
||
|
|
* SPDX-License-Identifier: ISC
|
||
|
|
*
|
||
|
|
- * Copyright (c) 1994-1996, 1998-2021 Todd C. Miller <Todd.Miller@sudo.ws>
|
||
|
|
+ * Copyright (c) 1994-1996, 1998-2023 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
|
||
|
|
@@ -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] == '/' && \
|
||
|
|
@@ -93,26 +82,28 @@ 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;
|
||
|
|
const char *tty, *tsid = NULL;
|
||
|
|
char exit_str[(((sizeof(int) * 8) + 2) / 3) + 2];
|
||
|
|
char sessid[7], offsetstr[64] = "";
|
||
|
|
- 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. */
|
||
|
|
@@ -150,169 +141,90 @@ new_logline(int event_type, int flags, struct eventlog_args *args,
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
- * Compute line length
|
||
|
|
+ * Format the log line as an lbuf, escaping control characters in
|
||
|
|
+ * octal form (#0nn). Error checking (ENOMEM) is done at the end.
|
||
|
|
*/
|
||
|
|
- if (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) + strlen(offsetstr);
|
||
|
|
- }
|
||
|
|
- 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 && evlog->argv[0] != NULL) {
|
||
|
|
- for (i = 1; evlog->argv[i] != NULL; i++)
|
||
|
|
- len += strlen(evlog->argv[i]) + 1;
|
||
|
|
- }
|
||
|
|
- if (event_type == EVLOG_EXIT) {
|
||
|
|
- if (evlog->signal_name != NULL)
|
||
|
|
- len += sizeof(LL_SIGNAL_STR) + 2 + strlen(evlog->signal_name);
|
||
|
|
- if (evlog->exit_value != -1) {
|
||
|
|
- (void)snprintf(exit_str, sizeof(exit_str), "%d", evlog->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, offsetstr, 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%s ; ", tsid,
|
||
|
|
+ offsetstr);
|
||
|
|
+ }
|
||
|
|
+ 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;
|
||
|
|
+ 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 (evlog->signal_name != NULL) {
|
||
|
|
- if (strlcat(line, " ; ", len) >= len ||
|
||
|
|
- strlcat(line, LL_SIGNAL_STR, len) >= len ||
|
||
|
|
- strlcat(line, evlog->signal_name, len) >= len)
|
||
|
|
- goto toobig;
|
||
|
|
+ sudo_lbuf_append_esc(&lbuf, LBUF_ESC_CNTRL, " ; SIGNAL=%s",
|
||
|
|
+ evlog->signal_name);
|
||
|
|
}
|
||
|
|
if (evlog->exit_value != -1) {
|
||
|
|
- if (strlcat(line, " ; ", len) >= len ||
|
||
|
|
- strlcat(line, LL_EXIT_STR, len) >= len ||
|
||
|
|
- strlcat(line, exit_str, len) >= len)
|
||
|
|
- goto toobig;
|
||
|
|
+ (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 --git a/lib/iolog/iolog_json.c b/lib/iolog/iolog_json.c
|
||
|
|
index 5ea338e5d..6f384ea59 100644
|
||
|
|
--- a/lib/iolog/iolog_json.c
|
||
|
|
+++ b/lib/iolog/iolog_json.c
|
||
|
|
@@ -551,45 +551,6 @@ iolog_parse_json_object(struct json_object *object, struct eventlog *evlog)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
- /* Merge cmd and argv as sudoreplay expects. */
|
||
|
|
- if (evlog->command != NULL && evlog->argv != NULL && evlog->argv[0] != NULL) {
|
||
|
|
- size_t len, bufsize = strlen(evlog->command) + 1;
|
||
|
|
- char *cp, *buf;
|
||
|
|
- int ac;
|
||
|
|
-
|
||
|
|
- /* 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_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||
|
|
- goto done;
|
||
|
|
- }
|
||
|
|
- 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;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
- free(evlog->command);
|
||
|
|
- evlog->command = buf;
|
||
|
|
- }
|
||
|
|
-
|
||
|
|
ret = true;
|
||
|
|
|
||
|
|
done:
|
||
|
|
diff --git a/lib/util/lbuf.c b/lib/util/lbuf.c
|
||
|
|
index 101982065..72bcac26f 100644
|
||
|
|
--- a/lib/util/lbuf.c
|
||
|
|
+++ b/lib/util/lbuf.c
|
||
|
|
@@ -94,6 +94,112 @@ sudo_lbuf_expand(struct sudo_lbuf *lbuf, unsigned int extra)
|
||
|
|
debug_return_bool(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
+/*
|
||
|
|
+ * 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 --git a/lib/util/util.exp.in b/lib/util/util.exp.in
|
||
|
|
index 554904c5f..6c8130a02 100644
|
||
|
|
--- a/lib/util/util.exp.in
|
||
|
|
+++ b/lib/util/util.exp.in
|
||
|
|
@@ -98,6 +98,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 --git a/plugins/sudoers/sudoreplay.c b/plugins/sudoers/sudoreplay.c
|
||
|
|
index 750dfeed9..43ff23999 100644
|
||
|
|
--- a/plugins/sudoers/sudoreplay.c
|
||
|
|
+++ b/plugins/sudoers/sudoreplay.c
|
||
|
|
@@ -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"
|
||
|
|
@@ -373,6 +374,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))
|
||
|
|
@@ -1315,11 +1320,57 @@ parse_expr(struct search_node_list *head, char *argv[], bool sub_expr)
|
||
|
|
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);
|
||
|
|
|
||
|
|
@@ -1353,13 +1404,15 @@ match_expr(struct search_node_list *head, struct eventlog *evlog, bool last_matc
|
||
|
|
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, >=);
|
||
|
|
@@ -1380,12 +1433,13 @@ match_expr(struct search_node_list *head, struct eventlog *evlog, bool last_matc
|
||
|
|
}
|
||
|
|
|
||
|
|
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)
|
||
|
|
@@ -1417,23 +1471,71 @@ 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 : ", 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;
|
||
|
|
+ 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);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if (!sudo_lbuf_error(lbuf)) {
|
||
|
|
+ puts(lbuf->buf);
|
||
|
|
+ ret = 0;
|
||
|
|
+ }
|
||
|
|
|
||
|
|
done:
|
||
|
|
+ lbuf->error = 0;
|
||
|
|
+ lbuf->len = 0;
|
||
|
|
eventlog_free(evlog);
|
||
|
|
debug_return_int(ret);
|
||
|
|
}
|
||
|
|
@@ -1453,6 +1555,7 @@ find_sessions(const char *dir, regex_t *re, const char *user, const char *tty)
|
||
|
|
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;
|
||
|
|
@@ -1464,6 +1567,8 @@ find_sessions(const char *dir, regex_t *re, const char *user, const char *tty)
|
||
|
|
#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);
|
||
|
|
@@ -1524,7 +1629,7 @@ find_sessions(const char *dir, regex_t *re, const char *user, const char *tty)
|
||
|
|
/* 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';
|
||
|
|
@@ -1535,6 +1640,7 @@ find_sessions(const char *dir, regex_t *re, const char *user, const char *tty)
|
||
|
|
}
|
||
|
|
free(sessions);
|
||
|
|
}
|
||
|
|
+ sudo_lbuf_destroy(&lbuf);
|
||
|
|
|
||
|
|
debug_return_int(0);
|
||
|
|
}
|