From 4dacf81082213219c282c10dfc5ad742f8c2dede Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Thu, 24 Oct 2019 20:04:29 -0600 Subject: [PATCH] Refactor I/O log code so it can be shared between sudoers and logsrvd --- MANIFEST | 3 +- logsrvd/Makefile.in | 53 +- logsrvd/iolog.h | 55 --- logsrvd/iolog_reader.c | 452 ------------------ logsrvd/iolog_writer.c | 84 ++-- logsrvd/logsrvd.h | 19 + logsrvd/sendlog.c | 252 ++++++++-- logsrvd/sendlog.h | 14 +- mkdep.pl | 3 +- plugins/sudoers/Makefile.in | 26 +- plugins/sudoers/iolog.h | 39 +- plugins/sudoers/iolog_util.c | 39 +- plugins/sudoers/iolog_util.h | 61 +++ .../regress/iolog_plugin/check_iolog_plugin.c | 23 +- .../regress/iolog_util/check_iolog_util.c | 6 +- plugins/sudoers/sudoreplay.c | 87 ++-- 16 files changed, 459 insertions(+), 757 deletions(-) delete mode 100644 logsrvd/iolog.h delete mode 100644 logsrvd/iolog_reader.c create mode 100644 plugins/sudoers/iolog_util.h diff --git a/MANIFEST b/MANIFEST index 7666dbcfa..c5f3d0696 100644 --- a/MANIFEST +++ b/MANIFEST @@ -231,8 +231,6 @@ logsrvd/Makefile.in logsrvd/log_server.pb-c.c logsrvd/log_server.pb-c.h logsrvd/log_server.proto -logsrvd/iolog.h -logsrvd/iolog_reader.c logsrvd/iolog_writer.c logsrvd/logsrvd.c logsrvd/logsrvd.h @@ -330,6 +328,7 @@ plugins/sudoers/iolog.h plugins/sudoers/iolog_files.h plugins/sudoers/iolog_path.c plugins/sudoers/iolog_util.c +plugins/sudoers/iolog_util.h plugins/sudoers/ldap.c plugins/sudoers/ldap_conf.c plugins/sudoers/ldap_util.c diff --git a/logsrvd/Makefile.in b/logsrvd/Makefile.in index d312f5845..4e59f468c 100644 --- a/logsrvd/Makefile.in +++ b/logsrvd/Makefile.in @@ -24,6 +24,7 @@ srcdir = @srcdir@ devdir = @devdir@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ +sudoers_srcdir = @top_srcdir@/plugins/sudoers incdir = $(top_srcdir)/include rundir = @rundir@ cross_compiling = @CROSS_COMPILING@ @@ -48,7 +49,7 @@ CPPDEFS = -D_PATH_SUDO_LOGSRVD_CONF=\"$(sysconfdir)/sudo_logsrvd.conf\" \ # C preprocessor flags CPPFLAGS = -I$(incdir) -I$(top_builddir) -I$(devdir) -I$(srcdir) \ - -I$(top_srcdir) $(CPPDEFS) @CPPFLAGS@ + -I$(sudoers_srcdir) -I$(top_srcdir) $(CPPDEFS) @CPPFLAGS@ # Usually -O and/or -g CFLAGS = @CFLAGS@ @@ -109,7 +110,7 @@ PROGS = logsrvd sendlog LOGSRVD_OBJS = iolog_writer.o logsrvd.o log_server.pb-c.o -SENDLOG_OBJS = sendlog.o iolog_reader.o log_server.pb-c.o +SENDLOG_OBJS = sendlog.o iolog_util.o log_server.pb-c.o IOBJS = $(LOGSRVD_OBJS:.o=.i) $(SENDLOG_OBJS:.o=.i) @@ -209,31 +210,35 @@ realclean: distclean cleandir: realclean # Autogenerated dependencies, do not modify -iolog_reader.o: $(srcdir)/iolog_reader.c $(devdir)/log_server.pb-c.h \ - $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ - $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/iolog.h \ - $(srcdir)/sendlog.h $(top_builddir)/config.h - $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_reader.c -iolog_reader.i: $(srcdir)/iolog_reader.c $(devdir)/log_server.pb-c.h \ - $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ - $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/iolog.h \ - $(srcdir)/sendlog.h $(top_builddir)/config.h +iolog_util.o: plugins/sudoers/iolog_util.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \ + $(top_builddir)/config.h + $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) plugins/sudoers/iolog_util.c +iolog_util.i: plugins/sudoers/iolog_util.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \ + $(top_builddir)/config.h $(CC) -E -o $@ $(CPPFLAGS) $< -iolog_reader.plog: iolog_reader.i - rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_reader.c --i-file $< --output-file $@ +iolog_util.plog: iolog_util.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file plugins/sudoers/iolog_util.c --i-file $< --output-file $@ iolog_writer.o: $(srcdir)/iolog_writer.c $(devdir)/log_server.pb-c.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/iolog.h \ - $(srcdir)/logsrvd.h $(top_builddir)/config.h + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/logsrvd.h $(sudoers_srcdir)/iolog.h \ + $(top_builddir)/config.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_writer.c iolog_writer.i: $(srcdir)/iolog_writer.c $(devdir)/log_server.pb-c.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/iolog.h \ - $(srcdir)/logsrvd.h $(top_builddir)/config.h + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/logsrvd.h $(sudoers_srcdir)/iolog.h \ + $(top_builddir)/config.h $(CC) -E -o $@ $(CPPFLAGS) $< iolog_writer.plog: iolog_writer.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_writer.c --i-file $< --output-file $@ @@ -264,16 +269,18 @@ sendlog.o: $(srcdir)/sendlog.c $(devdir)/log_server.pb-c.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)/iolog.h \ - $(srcdir)/sendlog.h $(top_builddir)/config.h + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/sendlog.h \ + $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \ + $(top_builddir)/config.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/sendlog.c sendlog.i: $(srcdir)/sendlog.c $(devdir)/log_server.pb-c.h \ $(incdir)/compat/getaddrinfo.h $(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)/iolog.h \ - $(srcdir)/sendlog.h $(top_builddir)/config.h + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/sendlog.h \ + $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \ + $(top_builddir)/config.h $(CC) -E -o $@ $(CPPFLAGS) $< sendlog.plog: sendlog.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/sendlog.c --i-file $< --output-file $@ diff --git a/logsrvd/iolog.h b/logsrvd/iolog.h deleted file mode 100644 index 161c0f01a..000000000 --- a/logsrvd/iolog.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2019 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * 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. - */ - -#ifndef LOGSRVD_IOLOG_H -#define LOGSRVD_IOLOG_H - -#include - -/* - * I/O log event types as stored as the first field in the timing file. - * Changing existing values will result in incompatible I/O log files. - */ -#define IO_EVENT_STDIN 0 -#define IO_EVENT_STDOUT 1 -#define IO_EVENT_STDERR 2 -#define IO_EVENT_TTYIN 3 -#define IO_EVENT_TTYOUT 4 -#define IO_EVENT_WINSIZE 5 -#define IO_EVENT_TTYOUT_1_8_7 6 -#define IO_EVENT_SUSPEND 7 -#define IO_EVENT_COUNT 8 - -/* - * Contents of the sudo I/O info log - */ -struct log_info { - char *command; - char *cwd; - char *iolog_dir; - char *rungroup; - char *runuser; - char *submithost; - char *submituser; - char *ttyname; - char **argv; - time_t start_time; - int argc; - int lines; - int columns; -}; - -#endif /* LOGSRVD_IOLOG_H */ diff --git a/logsrvd/iolog_reader.c b/logsrvd/iolog_reader.c deleted file mode 100644 index 67d89ff66..000000000 --- a/logsrvd/iolog_reader.c +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (c) 2019 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * 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. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log_server.pb-c.h" -#include "sudo_compat.h" -#include "sudo_debug.h" -#include "sudo_util.h" -#include "sudo_fatal.h" -#include "iolog.h" -#include "sendlog.h" - -static int timing_event_adj; -static gzFile io_fds[IOFD_MAX]; - -/* I/O log file names relative to iolog_dir. */ -/* XXX - duplicated with server */ -static const char *iolog_names[] = { - "stdin", /* IOFD_STDIN */ - "stdout", /* IOFD_STDOUT */ - "stderr", /* IOFD_STDERR */ - "ttyin", /* IOFD_TTYIN */ - "ttyout", /* IOFD_TTYOUT */ - "timing", /* IOFD_TIMING */ - NULL /* IOFD_MAX */ -}; - -void -free_log_info(struct log_info *li) -{ - if (li != NULL) { - free(li->cwd); - free(li->submituser); - free(li->runuser); - free(li->rungroup); - free(li->ttyname); - free(li->command); - free(li); - } -} - -/* - * Open any I/O log files that are present. - * The timing file must always exist. - */ -bool -iolog_open(const char *iolog_path) -{ - char fname[PATH_MAX]; - int i, len; - debug_decl(iolog_open, SUDO_DEBUG_UTIL) - - for (i = 0; iolog_names[i] != NULL; i++) { - len = snprintf(fname, sizeof(fname), "%s/%s", iolog_path, - iolog_names[i]); - if (len < 0 || len >= ssizeof(fname)) { - errno = ENAMETOOLONG; - sudo_warn("%s/%s", iolog_path, iolog_names[i]); - } - io_fds[i] = gzopen(fname, "r"); - if (io_fds[i] == NULL && i == IOFD_TIMING) { - /* The timing file is not optional. */ - sudo_warn("unable to open %s/%s", iolog_path, iolog_names[i]); - debug_return_bool(false); - } - } - debug_return_bool(true); -} - -struct log_info * -parse_logfile(const char *logfile) -{ - FILE *fp; - char *buf = NULL, *cp, *ep; - size_t bufsize = 0, cwdsize = 0, cmdsize = 0; - long long llval; - struct log_info *li = NULL; - debug_decl(parse_logfile, SUDO_DEBUG_UTIL) - - fp = fopen(logfile, "r"); - if (fp == NULL) { - sudo_warn("unable to open %s", logfile); - goto bad; - } - - /* - * ID file has three lines: - * 1) a log info line - * 2) cwd - * 3) command with args - */ - if ((li = calloc(1, sizeof(*li))) == NULL) - sudo_fatalx("%s: %s", __func__, "unable to allocate memory"); - if (getdelim(&buf, &bufsize, '\n', fp) == -1 || - getdelim(&li->cwd, &cwdsize, '\n', fp) == -1 || - getdelim(&li->command, &cmdsize, '\n', fp) == -1) { - sudo_warn("%s: invalid log file", logfile); - goto bad; - } - - /* Strip the newline from the cwd and command. */ - li->cwd[strcspn(li->cwd, "\n")] = '\0'; - li->command[strcspn(li->command, "\n")] = '\0'; - - /* - * Crack the log line (lines and columns not present in old versions). - * timestamp:submituser:runuser:rungroup:ttyname:lines:columns - * XXX - probably better to use strtok and switch on the state. - */ - buf[strcspn(buf, "\n")] = '\0'; - cp = buf; - - /* timestamp */ - errno = 0; - llval = strtoll(cp, &ep, 10); - if (cp == ep || *ep != ':') { - sudo_warn("%s: time stamp field is missing", logfile); - goto bad; - } - if (errno == ERANGE && (llval == LLONG_MAX || llval == LLONG_MIN)) { - sudo_warn("%s: time stamp %s: out of range", logfile, cp); - goto bad; - } - li->start_time = llval; - - /* submituser */ - cp = ep + 1; - if ((ep = strchr(cp, ':')) == NULL) { - sudo_warn("%s: submituser field is missing", logfile); - goto bad; - } - if ((li->submituser = strndup(cp, (size_t)(ep - cp))) == NULL) - sudo_fatalx("%s: %s", __func__, "unable to allocate memory"); - - /* runuser */ - cp = ep + 1; - if ((ep = strchr(cp, ':')) == NULL) { - sudo_warn("%s: runuser field is missing", logfile); - goto bad; - } - if ((li->runuser = strndup(cp, (size_t)(ep - cp))) == NULL) - sudo_fatalx("%s: %s", __func__, "unable to allocate memory"); - - /* rungroup */ - cp = ep + 1; - if ((ep = strchr(cp, ':')) == NULL) { - sudo_warn("%s: rungroup field is missing", logfile); - goto bad; - } - if (cp != ep) { - if ((li->rungroup = strndup(cp, (size_t)(ep - cp))) == NULL) - sudo_fatalx("%s: %s", __func__, "unable to allocate memory"); - } - - /* ttyname, followed by optional lines + columns */ - cp = ep + 1; - if ((ep = strchr(cp, ':')) == NULL) { - /* just the ttyname */ - if ((li->ttyname = strdup(cp)) == NULL) - sudo_fatalx("%s: %s", __func__, "unable to allocate memory"); - } else { - /* ttyname followed by lines + columns */ - unsigned long ulval; - - if ((li->ttyname = strndup(cp, (size_t)(ep - cp))) == NULL) - sudo_fatalx("%s: %s", __func__, "unable to allocate memory"); - - /* lines */ - cp = ep + 1; - errno = 0; - ulval = strtoul(cp, &ep, 10); - if (cp == ep || *ep != ':') { - sudo_warn("%s: terminal lines field is missing", logfile); - goto bad; - } - if ((errno == ERANGE && ulval == ULONG_MAX) || ulval > INT_MAX) { - sudo_warn("%s: terminal lines %s: out of range", logfile, cp); - goto bad; - } - li->lines = (int)ulval; - - /* columns */ - cp = ep + 1; - errno = 0; - ulval = strtoul(cp, &ep, 10); - if (cp == ep || (*ep != ':' && *ep != '\0')) { - sudo_warn("%s: terminal columns field is missing", logfile); - goto bad; - } - if ((errno == ERANGE && ulval == ULONG_MAX) || ulval > INT_MAX) { - sudo_warn("%s: terminal columns %s: out of range", logfile, cp); - goto bad; - } - li->columns = (int)ulval; - } - fclose(fp); - free(buf); - debug_return_ptr(li); - -bad: - if (fp != NULL) - fclose(fp); - free(buf); - free_log_info(li); - debug_return_ptr(NULL); -} - -/* - * Parse the delay as seconds and nanoseconds: %lld.%09ld - * Sudo used to write this as a double, but since timing data is logged - * in the C locale this may not match the current locale. - */ -static char * -parse_delay(const char *cp, struct timespec *delay) -{ - long long llval; - size_t len; - char *ep; - debug_decl(parse_delay, SUDO_DEBUG_UTIL) - - /* Parse seconds (whole number portion). */ - errno = 0; - llval = strtoll(cp, &ep, 10); - /* Radix may be in user's locale for sudo < 1.7.4 so accept that too. */ - if (cp == ep || *ep != '.') { - sudo_warnx("invalid characters after seconds: %s", ep); - debug_return_ptr(NULL); - } - if (errno == ERANGE && (llval == LLONG_MAX || llval == LLONG_MIN)) { - sudo_warnx("%s: number of seconds out of range", cp); - debug_return_ptr(NULL); - } - delay->tv_sec = (time_t)llval; - cp = ep + 1; - - /* Parse fractional part, we may read more precision than we can store. */ - errno = 0; - llval = strtoll(cp, &ep, 10); - if (cp == ep || (*ep != ' ' && *ep != '\0')) { - sudo_warnx("invalid characters after nanoseconds: %s", ep); - debug_return_ptr(NULL); - } - if (errno == ERANGE && (llval == LLONG_MAX || llval == LLONG_MIN)) { - sudo_warnx("%s: number of nanoseconds out of range", cp); - debug_return_ptr(NULL); - } - - /* Adjust fractional part to nanosecond precision. */ - len = (size_t)(ep - cp); - if (len < 9) { - /* Convert to nanosecond precision. */ - do { - llval *= 10; - } while (++len < 9); - } else if (len > 9) { - /* Clamp to nanoseconds. */ - do { - llval /= 10; - } while (--len > 9); - } - delay->tv_nsec = (long)llval; - - /* Advance to the next field. */ - while (isspace((unsigned char)*ep)) - ep++; - - debug_return_str((char *)ep); -} - -/* - * Parse a timing line, which is formatted as: - * IO_EVENT_TTYOUT sleep_time num_bytes - * IO_EVENT_WINSIZE sleep_time lines columns - * IO_EVENT_SUSPEND sleep_time signal - * Where type is IO_EVENT_*, sleep_time is the number of seconds to sleep - * before writing the data and num_bytes is the number of bytes to output. - * Returns true on success and false on failure. - */ -static bool -parse_timing(const char *buf, struct timing_closure *timing) -{ - unsigned long ulval; - char *cp, *ep; - debug_decl(parse_timing, SUDO_DEBUG_UTIL) - - /* Parse event type. */ - ulval = strtoul(buf, &ep, 10); - if (ep == buf || !isspace((unsigned char) *ep)) - goto bad; - if (ulval >= IO_EVENT_COUNT) - goto bad; - if (ulval == IO_EVENT_TTYOUT_1_8_7) { - /* work around a bug in timing files generated by sudo 1.8.7 */ - timing_event_adj = 2; - } - timing->event = (int)ulval - timing_event_adj; - for (cp = ep + 1; isspace((unsigned char) *cp); cp++) - continue; - - /* Parse delay, returns the next field or NULL on error. */ - if ((cp = parse_delay(cp, &timing->delay)) == NULL) - goto bad; - - switch (timing->event) { - case IO_EVENT_SUSPEND: - /* Signal name (no leading SIG prefix) or number. */ - if (isdigit((unsigned char)*cp)) { - /* Signal number, convert to name. */ - ulval = strtoul(cp, &ep, 10); - if (ep == cp || *ep != '\0') - goto bad; - if (ulval > INT_MAX) - goto bad; - if (sig2str(ulval, timing->buf) == -1) - goto bad; - } else { - /* Signal name. */ - if (strlcpy(timing->buf, cp, timing->bufsize) >= timing->bufsize) - goto bad; - } - break; - case IO_EVENT_WINSIZE: - ulval = strtoul(cp, &ep, 10); - if (ep == cp || !isspace((unsigned char) *ep)) - goto bad; - if (ulval > INT_MAX) - goto bad; - timing->u.winsize.lines = (int)ulval; - for (cp = ep + 1; isspace((unsigned char) *cp); cp++) - continue; - - ulval = strtoul(cp, &ep, 10); - if (ep == cp || *ep != '\0') - goto bad; - if (ulval > INT_MAX) - goto bad; - timing->u.winsize.columns = (int)ulval; - break; - default: - errno = 0; - ulval = strtoul(cp, &ep, 10); - if (ep == cp || *ep != '\0') - goto bad; - /* Note: assumes SIZE_MAX == ULONG_MAX */ - if (errno == ERANGE && ulval == ULONG_MAX) - goto bad; - timing->u.nbytes = (size_t)ulval; - break; - } - - debug_return_bool(true); -bad: - debug_return_bool(false); -} - -/* - * Read the next record from the timing file. - * Return 0 on success, 1 on EOF and -1 on error. - */ -int -read_timing_record(struct timing_closure *timing) -{ - const char *errstr; - char line[LINE_MAX]; - int errnum; - debug_decl(read_timing_record, SUDO_DEBUG_UTIL) - - /* Read next record from timing file. */ - if (gzgets(io_fds[IOFD_TIMING], line, sizeof(line)) == NULL) { - /* EOF or error reading timing file, we are done. */ - if (gzeof(io_fds[IOFD_TIMING])) - debug_return_int(1); /* EOF */ - if ((errstr = gzerror(io_fds[IOFD_TIMING], &errnum)) == NULL) - errstr = strerror(errno); - sudo_warnx("error reading timing file: %s", errstr); - debug_return_int(-1); - } - - /* Parse timing file record. */ - line[strcspn(line, "\n")] = '\0'; - if (!parse_timing(line, timing)) { - sudo_warnx("invalid timing file line: %s", line); - debug_return_int(-1); - } - - debug_return_int(0); -} - -bool -read_io_buf(struct timing_closure *timing) -{ - size_t nread; - debug_decl(read_io_buf, SUDO_DEBUG_UTIL) - - if (io_fds[timing->event] == NULL) { - sudo_warnx("%s file not open", iolog_names[timing->event]); - debug_return_bool(false); - } - - /* Expand buf as needed. */ - if (timing->u.nbytes > timing->bufsize) { - free(timing->buf); - do { - timing->bufsize *= 2; - } while (timing->u.nbytes > timing->bufsize); - if ((timing->buf = malloc(timing->bufsize)) == NULL) { - sudo_warn("malloc %zu", timing->bufsize); - timing->u.nbytes = 0; - debug_return_bool(false); - } - } - - nread = gzread(io_fds[timing->event], timing->buf, timing->u.nbytes); - if (nread != timing->u.nbytes) { - int errnum; - const char *errstr; - - if ((errstr = gzerror(io_fds[timing->event], &errnum)) == NULL) - errstr = strerror(errno); - sudo_warnx("unable to read %s file: %s", iolog_names[timing->event], errstr); - debug_return_bool(false); - } - debug_return_bool(true); -} diff --git a/logsrvd/iolog_writer.c b/logsrvd/iolog_writer.c index caa21bc2e..870667644 100644 --- a/logsrvd/iolog_writer.c +++ b/logsrvd/iolog_writer.c @@ -75,24 +75,24 @@ has_strlistval(InfoMessage *info) } /* - * Fill in log info from an ExecMessage + * Fill in I/O log details from an ExecMessage * Only makes a shallow copy of strings and string lists. */ static bool -log_info_fill(struct log_info *log_info, ExecMessage *msg) +iolog_details_fill(struct iolog_details *details, ExecMessage *msg) { size_t idx; bool ret = true; - debug_decl(log_info_fill, SUDO_DEBUG_UTIL) + debug_decl(iolog_details_fill, SUDO_DEBUG_UTIL) - memset(log_info, 0, sizeof(*log_info)); + memset(details, 0, sizeof(*details)); /* Start time. */ - log_info->start_time = msg->start_time->tv_sec; + details->start_time = msg->start_time->tv_sec; /* Default values */ - log_info->lines = 24; - log_info->columns = 80; + details->lines = 24; + details->columns = 80; /* Pull out values by key from info array. */ for (idx = 0; idx < msg->n_info_msgs; idx++) { @@ -106,13 +106,13 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) } else if (info->numval <= 0 || info->numval > INT_MAX) { sudo_warnx("columns (%" PRId64 ") out of range", info->numval); } else { - log_info->columns = info->numval; + details->columns = info->numval; } continue; } if (strcmp(key, "command") == 0) { if (has_strval(info)) { - log_info->command = info->strval; + details->command = info->strval; } else { sudo_warnx("command specified but not a string"); } @@ -120,7 +120,7 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) } if (strcmp(key, "cwd") == 0) { if (has_strval(info)) { - log_info->cwd = info->strval; + details->cwd = info->strval; } else { sudo_warnx("cwd specified but not a string"); } @@ -134,7 +134,7 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) } else if (info->numval <= 0 || info->numval > INT_MAX) { sudo_warnx("lines (%" PRId64 ") out of range", info->numval); } else { - log_info->lines = info->numval; + details->lines = info->numval; } continue; } @@ -142,8 +142,8 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) case 'r': if (strcmp(key, "runargv") == 0) { if (has_strlistval(info)) { - log_info->argv = info->strlistval->strings; - log_info->argc = info->strlistval->n_strings; + details->argv = info->strlistval->strings; + details->argc = info->strlistval->n_strings; } else { sudo_warnx("runargv specified but not a string list"); } @@ -151,7 +151,7 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) } if (strcmp(key, "rungroup") == 0) { if (has_strval(info)) { - log_info->rungroup = info->strval; + details->rungroup = info->strval; } else { sudo_warnx("rungroup specified but not a string"); } @@ -159,7 +159,7 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) } if (strcmp(key, "runuser") == 0) { if (has_strval(info)) { - log_info->runuser = info->strval; + details->runuser = info->strval; } else { sudo_warnx("runuser specified but not a string"); } @@ -169,7 +169,7 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) case 's': if (strcmp(key, "submithost") == 0) { if (has_strval(info)) { - log_info->submithost = info->strval; + details->submithost = info->strval; } else { sudo_warnx("submithost specified but not a string"); } @@ -177,7 +177,7 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) } if (strcmp(key, "submituser") == 0) { if (has_strval(info)) { - log_info->submituser = info->strval; + details->submituser = info->strval; } else { sudo_warnx("submituser specified but not a string"); } @@ -187,7 +187,7 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) case 't': if (strcmp(key, "ttyname") == 0) { if (has_strval(info)) { - log_info->ttyname = info->strval; + details->ttyname = info->strval; } else { sudo_warnx("ttyname specified but not a string"); } @@ -198,15 +198,15 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) } /* Check for required settings */ - if (log_info->submituser == NULL) { + if (details->submituser == NULL) { sudo_warnx("missing user in ExecMessage"); ret = false; } - if (log_info->submithost == NULL) { + if (details->submithost == NULL) { sudo_warnx("missing host in ExecMessage"); ret = false; } - if (log_info->command == NULL) { + if (details->command == NULL) { sudo_warnx("missing command in ExecMessage"); ret = false; } @@ -219,7 +219,7 @@ log_info_fill(struct log_info *log_info, ExecMessage *msg) * Set iolog_dir and iolog_dir_fd in the closure */ static bool -create_iolog_dir(struct log_info *log_info, struct connection_closure *closure) +create_iolog_dir(struct iolog_details *details, struct connection_closure *closure) { char path[PATH_MAX]; int len; @@ -231,7 +231,7 @@ create_iolog_dir(struct log_info *log_info, struct connection_closure *closure) goto bad; } len = snprintf(path, sizeof(path), "%s/%s", IOLOG_DIR, - log_info->submithost); + details->submithost); if (len < 0 || len >= ssizeof(path)) { sudo_warn("snprintf"); goto bad; @@ -241,7 +241,7 @@ create_iolog_dir(struct log_info *log_info, struct connection_closure *closure) goto bad; } len = snprintf(path, sizeof(path), "%s/%s/%s", IOLOG_DIR, - log_info->submithost, log_info->submituser); + details->submithost, details->submituser); if (len < 0 || len >= ssizeof(path)) { sudo_warn("snprintf"); goto bad; @@ -251,7 +251,7 @@ create_iolog_dir(struct log_info *log_info, struct connection_closure *closure) goto bad; } len = snprintf(path, sizeof(path), "%s/%s/%s/XXXXXX", IOLOG_DIR, - log_info->submithost, log_info->submituser); + details->submithost, details->submituser); if (len < 0 || len >= ssizeof(path)) { sudo_warn("snprintf"); goto bad; @@ -264,7 +264,7 @@ create_iolog_dir(struct log_info *log_info, struct connection_closure *closure) /* Make a copy of iolog_dir for error messages. */ if ((closure->iolog_dir = strdup(path)) == NULL) { - sudo_warn("strdup"); + sudo_warn(NULL); goto bad; } @@ -285,12 +285,12 @@ bad: * Write the sudo-style I/O log info file containing user and command info. */ static bool -log_info_write(struct log_info *log_info, struct connection_closure *closure) +iolog_details_write(struct iolog_details *details, struct connection_closure *closure) { int fd, i; FILE *fp; int error; - debug_decl(log_info_write, SUDO_DEBUG_UTIL) + debug_decl(iolog_details_write, SUDO_DEBUG_UTIL) fd = openat(closure->iolog_dir_fd, "log", O_CREAT|O_EXCL|O_WRONLY, 0600); if (fd == -1 || (fp = fdopen(fd, "w")) == NULL) { @@ -301,16 +301,16 @@ log_info_write(struct log_info *log_info, struct connection_closure *closure) } fprintf(fp, "%lld:%s:%s:%s:%s:%d:%d\n%s\n", - (long long)log_info->start_time, log_info->submituser, - log_info->runuser ? log_info->runuser : RUNAS_DEFAULT, - log_info->rungroup ? log_info->rungroup : "", - log_info->ttyname ? log_info->ttyname : "unknown", - log_info->lines, log_info->columns, - log_info->cwd ? log_info->cwd : "unknown"); - fputs(log_info->command, fp); - for (i = 1; i < log_info->argc; i++) { + (long long)details->start_time, details->submituser, + details->runuser ? details->runuser : RUNAS_DEFAULT, + details->rungroup ? details->rungroup : "", + details->ttyname ? details->ttyname : "unknown", + details->lines, details->columns, + details->cwd ? details->cwd : "unknown"); + fputs(details->command, fp); + for (i = 1; i < details->argc; i++) { fputc(' ', fp); - fputs(log_info->argv[i], fp); + fputs(details->argv[i], fp); } fputc('\n', fp); fflush(fp); @@ -356,7 +356,7 @@ iolog_close(struct connection_closure *closure) bool iolog_init(ExecMessage *msg, struct connection_closure *closure) { - struct log_info log_info; + struct iolog_details details; int i; debug_decl(iolog_init, SUDO_DEBUG_UTIL) @@ -364,16 +364,16 @@ iolog_init(ExecMessage *msg, struct connection_closure *closure) for (i = 0; i < IOFD_MAX; i++) closure->io_fds[i] = -1; - /* Fill in log_info */ - if (!log_info_fill(&log_info, msg)) + /* Fill in iolog_details */ + if (!iolog_details_fill(&details, msg)) debug_return_bool(false); /* Create I/O log dir */ - if (!create_iolog_dir(&log_info, closure)) + if (!create_iolog_dir(&details, closure)) debug_return_bool(false); /* Write sudo I/O log info file */ - if (!log_info_write(&log_info, closure)) + if (!iolog_details_write(&details, closure)) debug_return_bool(false); /* Create timing, stdout, stderr and ttyout files for sudoreplay. */ diff --git a/logsrvd/logsrvd.h b/logsrvd/logsrvd.h index 94e11de19..63a4bb051 100644 --- a/logsrvd/logsrvd.h +++ b/logsrvd/logsrvd.h @@ -40,6 +40,25 @@ #define IOFD_TIMING 5 #define IOFD_MAX 6 +/* + * I/O log details from the ExecMessage + */ +struct iolog_details { + char *command; + char *cwd; + char *iolog_dir; + char *rungroup; + char *runuser; + char *submithost; + char *submituser; + char *ttyname; + char **argv; + time_t start_time; + int argc; + int lines; + int columns; +}; + /* * Connection status. * In the RUNNING state we expect I/O log buffers. diff --git a/logsrvd/sendlog.c b/logsrvd/sendlog.c index aa1b956dc..f1f21be04 100644 --- a/logsrvd/sendlog.c +++ b/logsrvd/sendlog.c @@ -54,9 +54,23 @@ #include "sudo_util.h" #include "sudo_event.h" #include "sudo_fatal.h" -#include "iolog.h" +#include "iolog_util.h" #include "sendlog.h" +static gzFile io_fds[IOFD_MAX]; + +/* I/O log file names relative to iolog_dir. */ +/* XXX - duplicated with server */ +static const char *iolog_names[] = { + "stdin", /* IOFD_STDIN */ + "stdout", /* IOFD_STDOUT */ + "stderr", /* IOFD_STDERR */ + "ttyin", /* IOFD_TTYIN */ + "ttyout", /* IOFD_TTYOUT */ + "timing", /* IOFD_TIMING */ + NULL /* IOFD_MAX */ +}; + static void usage(void) { @@ -135,13 +149,87 @@ client_closure_free(struct client_closure *closure) sudo_ev_free(closure->write_ev); free(closure->read_buf.data); free(closure->write_buf.data); - free(closure->timing.buf); + free(closure->buf); free(closure); } debug_return; } +/* + * Read the next record from the timing file. + * Return 0 on success, 1 on EOF and -1 on error. + */ +int +read_timing_record(struct timing_closure *timing) +{ + const char *errstr; + char line[LINE_MAX]; + int errnum; + debug_decl(read_timing_record, SUDO_DEBUG_UTIL) + + /* Read next record from timing file. */ + if (gzgets(io_fds[IOFD_TIMING], line, sizeof(line)) == NULL) { + /* EOF or error reading timing file, we are done. */ + if (gzeof(io_fds[IOFD_TIMING])) + debug_return_int(1); /* EOF */ + if ((errstr = gzerror(io_fds[IOFD_TIMING], &errnum)) == NULL) + errstr = strerror(errno); + sudo_warnx("error reading timing file: %s", errstr); + debug_return_int(-1); + } + + /* Parse timing file record. */ + line[strcspn(line, "\n")] = '\0'; + if (!parse_timing(line, timing)) { + sudo_warnx("invalid timing file line: %s", line); + debug_return_int(-1); + } + + debug_return_int(0); +} + +/* + * Read the next I/O buffer as described by closure->timing. + */ +static bool +read_io_buf(struct client_closure *closure) +{ + struct timing_closure *timing = &closure->timing; + size_t nread; + debug_decl(read_io_buf, SUDO_DEBUG_UTIL) + + if (io_fds[timing->event] == NULL) { + sudo_warnx("%s file not open", iolog_names[timing->event]); + debug_return_bool(false); + } + + /* Expand buf as needed. */ + if (timing->u.nbytes > closure->bufsize) { + free(closure->buf); + do { + closure->bufsize *= 2; + } while (timing->u.nbytes > closure->bufsize); + if ((closure->buf = malloc(closure->bufsize)) == NULL) { + sudo_warn("malloc %zu", closure->bufsize); + timing->u.nbytes = 0; + debug_return_bool(false); + } + } + + nread = gzread(io_fds[timing->event], closure->buf, timing->u.nbytes); + if (nread != timing->u.nbytes) { + int errnum; + const char *errstr; + + if ((errstr = gzerror(io_fds[timing->event], &errnum)) == NULL) + errstr = strerror(errno); + sudo_warnx("unable to read %s file: %s", iolog_names[timing->event], errstr); + debug_return_bool(false); + } + debug_return_bool(true); +} + /* * Format a ClientMessage and store the wire format message in buf. * Returns true on success, false on failure. @@ -177,6 +265,40 @@ done: debug_return_bool(ret); } +/* + * Split command + args into an array of strings. + * Returns an array containing command and args, reusing space in "command". + * Note that the returned array does not end with a terminating NULL. + */ +static char ** +split_command(char *command, size_t *lenp) +{ + char *cp; + char **args; + size_t len; + debug_decl(split_command, SUDO_DEBUG_UTIL) + + for (cp = command, len = 0;;) { + len++; + if ((cp = strchr(cp, ' ')) == NULL) + break; + cp++; + } + args = reallocarray(NULL, len, sizeof(char *)); + if (args == NULL) + debug_return_ptr(NULL); + + for (cp = command, len = 0;;) { + args[len++] = cp; + if ((cp = strchr(cp, ' ')) == NULL) + break; + *cp++ = '\0'; + } + + *lenp = len; + debug_return_ptr(args); +} + /* * Build and format an ExecMessage wrapped in a ClientMessage. * Stores the wire format message in the closure's write buffer. @@ -189,7 +311,7 @@ fmt_exec_message(struct client_closure *closure) ExecMessage exec_msg = EXEC_MESSAGE__INIT; TimeSpec tv = TIME_SPEC__INIT; InfoMessage__StringList runargv = INFO_MESSAGE__STRING_LIST__INIT; - struct log_info *log_info = closure->log_info; + struct iolog_info *log_info = closure->log_info; char hostname[1024]; bool ret = false; size_t n; @@ -205,15 +327,16 @@ fmt_exec_message(struct client_closure *closure) } hostname[sizeof(hostname) - 1] = '\0'; - /* Format argv/argc as a StringList */ - runargv.strings = log_info->argv; - runargv.n_strings = log_info->argc; - /* Sudo I/O logs only store start time in seconds. */ - tv.tv_sec = log_info->start_time; + tv.tv_sec = log_info->tstamp; tv.tv_nsec = 0; exec_msg.start_time = &tv; + /* Split command into a StringList. */ + runargv.strings = split_command(log_info->cmd, &runargv.n_strings); + if (runargv.strings == NULL) + sudo_fatal(NULL); + /* The sudo I/O log info file has limited info. */ exec_msg.n_info_msgs = 10; exec_msg.info_msgs = calloc(exec_msg.n_info_msgs, sizeof(InfoMessage *)); @@ -231,55 +354,56 @@ fmt_exec_message(struct client_closure *closure) /* Fill in info_msgs */ n = 0; exec_msg.info_msgs[n]->key = "command"; - exec_msg.info_msgs[n]->strval = log_info->command; + exec_msg.info_msgs[n]->strval = log_info->cmd; exec_msg.info_msgs[n]->value_case = INFO_MESSAGE__VALUE_STRVAL; - n++; + exec_msg.info_msgs[n]->key = "columns"; - exec_msg.info_msgs[n]->numval = log_info->columns; + exec_msg.info_msgs[n]->numval = log_info->cols; exec_msg.info_msgs[n]->value_case = INFO_MESSAGE__VALUE_NUMVAL; - n++; + exec_msg.info_msgs[n]->key = "cwd"; exec_msg.info_msgs[n]->strval = log_info->cwd; exec_msg.info_msgs[n]->value_case = INFO_MESSAGE__VALUE_STRVAL; - n++; + exec_msg.info_msgs[n]->key = "lines"; exec_msg.info_msgs[n]->numval = log_info->lines; exec_msg.info_msgs[n]->value_case = INFO_MESSAGE__VALUE_NUMVAL; - n++; + exec_msg.info_msgs[n]->key = "runargv"; exec_msg.info_msgs[n]->strlistval = &runargv; exec_msg.info_msgs[n]->value_case = INFO_MESSAGE__VALUE_STRLISTVAL; + n++; - if (log_info->rungroup != NULL) { - n++; + if (log_info->runas_group != NULL) { exec_msg.info_msgs[n]->key = "rungroup"; - exec_msg.info_msgs[n]->strval = log_info->rungroup; + exec_msg.info_msgs[n]->strval = log_info->runas_group; exec_msg.info_msgs[n]->value_case = INFO_MESSAGE__VALUE_STRVAL; + n++; } - n++; exec_msg.info_msgs[n]->key = "runuser"; - exec_msg.info_msgs[n]->strval = log_info->runuser; + exec_msg.info_msgs[n]->strval = log_info->runas_user; exec_msg.info_msgs[n]->value_case = INFO_MESSAGE__VALUE_STRVAL; - n++; + exec_msg.info_msgs[n]->key = "submithost"; exec_msg.info_msgs[n]->strval = hostname; exec_msg.info_msgs[n]->value_case = INFO_MESSAGE__VALUE_STRVAL; - n++; + exec_msg.info_msgs[n]->key = "submituser"; - exec_msg.info_msgs[n]->strval = log_info->submituser; + exec_msg.info_msgs[n]->strval = log_info->user; exec_msg.info_msgs[n]->value_case = INFO_MESSAGE__VALUE_STRVAL; - n++; + exec_msg.info_msgs[n]->key = "ttyname"; - exec_msg.info_msgs[n]->strval = log_info->ttyname; + exec_msg.info_msgs[n]->strval = log_info->tty; exec_msg.info_msgs[n]->value_case = INFO_MESSAGE__VALUE_STRVAL; + n++; /* Update n_info_msgs. */ exec_msg.n_info_msgs = n; @@ -344,7 +468,7 @@ done: * Returns true on success, false on failure. */ static bool -fmt_io_buf(int type, struct timing_closure *timing, +fmt_io_buf(int type, struct client_closure *closure, struct connection_buffer *buf) { ClientMessage client_msg = CLIENT_MESSAGE__INIT; @@ -353,15 +477,15 @@ fmt_io_buf(int type, struct timing_closure *timing, bool ret = false; debug_decl(fmt_io_buf, SUDO_DEBUG_UTIL) - if (!read_io_buf(timing)) + if (!read_io_buf(closure)) goto done; /* Fill in IoBuffer. */ - delay.tv_sec = timing->delay.tv_sec; - delay.tv_nsec = timing->delay.tv_nsec; + delay.tv_sec = closure->timing.delay.tv_sec; + delay.tv_nsec = closure->timing.delay.tv_nsec; iobuf_msg.delay = &delay; - iobuf_msg.data.data = (void *)timing->buf; - iobuf_msg.data.len = timing->u.nbytes; + iobuf_msg.data.data = (void *)closure->buf; + iobuf_msg.data.len = closure->timing.u.nbytes; /* TODO: split buffer if it is too large */ sudo_warnx("sending IoBuffer length %zu, type %d, size %zu", iobuf_msg.data.len, type, io_buffer__get_packed_size(&iobuf_msg)); // XXX @@ -384,11 +508,12 @@ done: * Returns true on success, false on failure. */ static bool -fmt_winsize(struct timing_closure *timing, struct connection_buffer *buf) +fmt_winsize(struct client_closure *closure, struct connection_buffer *buf) { ClientMessage client_msg = CLIENT_MESSAGE__INIT; ChangeWindowSize winsize_msg = CHANGE_WINDOW_SIZE__INIT; TimeSpec delay = TIME_SPEC__INIT; + struct timing_closure *timing = &closure->timing; bool ret = false; debug_decl(fmt_winsize, SUDO_DEBUG_UTIL) @@ -397,7 +522,7 @@ fmt_winsize(struct timing_closure *timing, struct connection_buffer *buf) delay.tv_nsec = timing->delay.tv_nsec; winsize_msg.delay = &delay; winsize_msg.rows = timing->u.winsize.lines; - winsize_msg.cols = timing->u.winsize.columns; + winsize_msg.cols = timing->u.winsize.cols; sudo_warnx("sending ChangeWindowSize, %dx%d, size %zu", winsize_msg.rows, winsize_msg.cols, change_window_size__get_packed_size(&winsize_msg)); // XXX @@ -419,11 +544,12 @@ done: * Returns true on success, false on failure. */ static bool -fmt_suspend(struct timing_closure *timing, struct connection_buffer *buf) +fmt_suspend(struct client_closure *closure, struct connection_buffer *buf) { ClientMessage client_msg = CLIENT_MESSAGE__INIT; CommandSuspend suspend_msg = COMMAND_SUSPEND__INIT; TimeSpec delay = TIME_SPEC__INIT; + struct timing_closure *timing = &closure->timing; bool ret = false; debug_decl(fmt_suspend, SUDO_DEBUG_UTIL) @@ -431,7 +557,9 @@ fmt_suspend(struct timing_closure *timing, struct connection_buffer *buf) delay.tv_sec = timing->delay.tv_sec; delay.tv_nsec = timing->delay.tv_nsec; suspend_msg.delay = &delay; - suspend_msg.signal = timing->buf; + if (sig2str(timing->u.signo, closure->buf) == -1) + goto done; + suspend_msg.signal = closure->buf; sudo_warnx("sending CommandSuspend, %s, size %zu", suspend_msg.signal, command_suspend__get_packed_size(&suspend_msg)); // XXX @@ -481,25 +609,25 @@ fmt_next_iolog(struct client_closure *closure) switch (timing->event) { case IO_EVENT_STDIN: - ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDIN_BUF, timing, buf); + ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDIN_BUF, closure, buf); break; case IO_EVENT_STDOUT: - ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDOUT_BUF, timing, buf); + ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDOUT_BUF, closure, buf); break; case IO_EVENT_STDERR: - ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDERR_BUF, timing, buf); + ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDERR_BUF, closure, buf); break; case IO_EVENT_TTYIN: - ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_TTYIN_BUF, timing, buf); + ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_TTYIN_BUF, closure, buf); break; case IO_EVENT_TTYOUT: - ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_TTYOUT_BUF, timing, buf); + ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_TTYOUT_BUF, closure, buf); break; case IO_EVENT_WINSIZE: - ret = fmt_winsize(timing, buf); + ret = fmt_winsize(closure, buf); break; case IO_EVENT_SUSPEND: - ret = fmt_suspend(timing, buf); + ret = fmt_suspend(closure, buf); break; default: sudo_warnx("unexpected I/O event %d", timing->event); @@ -607,7 +735,7 @@ handle_log_id(char *id, struct client_closure *closure) debug_decl(handle_log_id, SUDO_DEBUG_UTIL) sudo_warnx("remote log ID: %s", id); - if ((closure->log_info->iolog_dir = strdup(id)) == NULL) + if ((closure->iolog_dir = strdup(id)) == NULL) sudo_fatal(NULL); debug_return_bool(true); } @@ -794,7 +922,7 @@ bad: * Allocate a new connection closure. */ static struct client_closure * -client_closure_alloc(int sock, struct log_info *log_info) +client_closure_alloc(int sock, struct iolog_info *log_info) { struct client_closure *closure; debug_decl(client_closure_alloc, SUDO_DEBUG_UTIL) @@ -805,9 +933,9 @@ client_closure_alloc(int sock, struct log_info *log_info) closure->state = RECV_HELLO; closure->log_info = log_info; - closure->timing.bufsize = 10240; - closure->timing.buf = malloc(closure->timing.bufsize); - if (closure->timing.buf == NULL) + closure->bufsize = 10240; + closure->buf = malloc(closure->bufsize); + if (closure->buf == NULL) goto bad; closure->read_buf.size = UINT16_MAX + sizeof(uint16_t); @@ -836,12 +964,40 @@ bad: debug_return_ptr(NULL); } +/* + * Open any I/O log files that are present. + * The timing file must always exist. + */ +bool +iolog_open_all(const char *iolog_path) +{ + char fname[PATH_MAX]; + int i, len; + debug_decl(iolog_open_all, SUDO_DEBUG_UTIL) + + for (i = 0; iolog_names[i] != NULL; i++) { + len = snprintf(fname, sizeof(fname), "%s/%s", iolog_path, + iolog_names[i]); + if (len < 0 || len >= ssizeof(fname)) { + errno = ENAMETOOLONG; + sudo_warn("%s/%s", iolog_path, iolog_names[i]); + } + io_fds[i] = gzopen(fname, "r"); + if (io_fds[i] == NULL && i == IOFD_TIMING) { + /* The timing file is not optional. */ + sudo_warn("unable to open %s/%s", iolog_path, iolog_names[i]); + debug_return_bool(false); + } + } + debug_return_bool(true); +} + int main(int argc, char *argv[]) { struct client_closure *closure; struct sudo_event_base *evbase; - struct log_info *log_info; + struct iolog_info *log_info; const char *host = "localhost"; const char *port = DEFAULT_PORT_STR; char fname[PATH_MAX]; @@ -892,7 +1048,7 @@ main(int argc, char *argv[]) sudo_warnx("parsed log file %s", fname); // XXX /* Open the I/O log files. */ - if (!iolog_open(iolog_path)) + if (!iolog_open_all(iolog_path)) goto bad; /* Connect to server, setup events. */ diff --git a/logsrvd/sendlog.h b/logsrvd/sendlog.h index 0154d4cf1..a4f3b780f 100644 --- a/logsrvd/sendlog.h +++ b/logsrvd/sendlog.h @@ -33,6 +33,7 @@ #define IOFD_TIMING 5 #define IOFD_MAX 6 +#if 0 struct timing_closure { struct timespec delay; int event; @@ -46,6 +47,7 @@ struct timing_closure { char *buf; size_t bufsize; }; +#endif enum client_state { ERROR, @@ -73,13 +75,9 @@ struct client_closure { struct connection_buffer write_buf; struct sudo_event *read_ev; struct sudo_event *write_ev; - struct log_info *log_info; + struct iolog_info *log_info; + char *iolog_dir; + char *buf; /* XXX */ + size_t bufsize; /* XXX */ enum client_state state; }; - -/* iolog_reader.c */ -bool iolog_open(const char *iolog_path); -bool read_io_buf(struct timing_closure *timing); -int read_timing_record(struct timing_closure *timing); -struct log_info *parse_logfile(const char *logfile); -void free_log_info(struct log_info *li); diff --git a/mkdep.pl b/mkdep.pl index bc17631eb..9f5c073cd 100755 --- a/mkdep.pl +++ b/mkdep.pl @@ -147,7 +147,8 @@ sub mkdep { $dir_vars{'devdir'} = $dir_vars{'srcdir'}; $dir_vars{'authdir'} = $dir_vars{'srcdir'} . "/auth"; $dir_vars{'builddir'} = $top_builddir . "/" . $dir_vars{'srcdir'}; - $dir_vars{'top_srcdir'} = '.'; + $dir_vars{'top_srcdir'} = $top_srcdir; + $dir_vars{'sudoers_srcdir'} = $top_srcdir . "/plugins/sudoers"; #$dir_vars{'top_builddir'} = '.'; $dir_vars{'incdir'} = 'include'; diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index c1c1d65ba..b0f12594f 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -964,7 +964,8 @@ check_iolog_plugin.o: $(srcdir)/regress/iolog_plugin/check_iolog_plugin.c \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ $(incdir)/sudo_util.h $(srcdir)/defaults.h \ - $(srcdir)/iolog.h $(srcdir)/logging.h $(srcdir)/parse.h \ + $(srcdir)/iolog.h $(srcdir)/iolog_util.h \ + $(srcdir)/logging.h $(srcdir)/parse.h \ $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ $(top_builddir)/pathnames.h @@ -976,7 +977,8 @@ check_iolog_plugin.i: $(srcdir)/regress/iolog_plugin/check_iolog_plugin.c \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ $(incdir)/sudo_util.h $(srcdir)/defaults.h \ - $(srcdir)/iolog.h $(srcdir)/logging.h $(srcdir)/parse.h \ + $(srcdir)/iolog.h $(srcdir)/iolog_util.h \ + $(srcdir)/logging.h $(srcdir)/parse.h \ $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ $(top_builddir)/pathnames.h @@ -986,12 +988,14 @@ check_iolog_plugin.plog: check_iolog_plugin.i check_iolog_util.o: $(srcdir)/regress/iolog_util/check_iolog_util.c \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_util.h \ - $(srcdir)/iolog.h $(top_builddir)/config.h + $(srcdir)/iolog.h $(srcdir)/iolog_util.h \ + $(top_builddir)/config.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/iolog_util/check_iolog_util.c check_iolog_util.i: $(srcdir)/regress/iolog_util/check_iolog_util.c \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_util.h \ - $(srcdir)/iolog.h $(top_builddir)/config.h + $(srcdir)/iolog.h $(srcdir)/iolog_util.h \ + $(top_builddir)/config.h $(CC) -E -o $@ $(CPPFLAGS) $< check_iolog_util.plog: check_iolog_util.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/iolog_util/check_iolog_util.c --i-file $< --output-file $@ @@ -1589,13 +1593,13 @@ iolog_util.o: $(srcdir)/iolog_util.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/iolog.h \ - $(top_builddir)/config.h + $(srcdir)/iolog_util.h $(top_builddir)/config.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_util.c iolog_util.i: $(srcdir)/iolog_util.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/iolog.h \ - $(top_builddir)/config.h + $(srcdir)/iolog_util.h $(top_builddir)/config.h $(CC) -E -o $@ $(CPPFLAGS) $< iolog_util.plog: iolog_util.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_util.c --i-file $< --output-file $@ @@ -2431,8 +2435,9 @@ sudoreplay.o: $(srcdir)/sudoreplay.c $(incdir)/compat/getopt.h \ $(incdir)/sudo_event.h $(incdir)/sudo_fatal.h \ $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/iolog.h \ - $(srcdir)/iolog_files.h $(srcdir)/logging.h \ - $(top_builddir)/config.h $(top_builddir)/pathnames.h + $(srcdir)/iolog_files.h $(srcdir)/iolog_util.h \ + $(srcdir)/logging.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/sudoreplay.c sudoreplay.i: $(srcdir)/sudoreplay.c $(incdir)/compat/getopt.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ @@ -2440,8 +2445,9 @@ sudoreplay.i: $(srcdir)/sudoreplay.c $(incdir)/compat/getopt.h \ $(incdir)/sudo_event.h $(incdir)/sudo_fatal.h \ $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/iolog.h \ - $(srcdir)/iolog_files.h $(srcdir)/logging.h \ - $(top_builddir)/config.h $(top_builddir)/pathnames.h + $(srcdir)/iolog_files.h $(srcdir)/iolog_util.h \ + $(srcdir)/logging.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h $(CC) -E -o $@ $(CPPFLAGS) $< sudoreplay.plog: sudoreplay.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/sudoreplay.c --i-file $< --output-file $@ diff --git a/plugins/sudoers/iolog.h b/plugins/sudoers/iolog.h index 4ef89632b..194516829 100644 --- a/plugins/sudoers/iolog.h +++ b/plugins/sudoers/iolog.h @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2018 Todd C. Miller + * Copyright (c) 2009-2019 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -48,41 +48,4 @@ union io_fd { void *v; }; -/* - * Info present in the I/O log file - */ -struct log_info { - char *cwd; - char *user; - char *runas_user; - char *runas_group; - char *tty; - char *cmd; - time_t tstamp; - int rows; - int cols; -}; - -struct timing_closure { - const char *decimal; - struct timespec *max_delay; - union io_fd fd; - int event; - union { - struct { - int rows; - int cols; - } winsize; - size_t nbytes; // XXX - int signo; - } u; -}; - -/* iolog_util.c */ -bool parse_timing(const char *buf, struct timespec *delay, struct timing_closure *timing); -char *parse_delay(const char *cp, struct timespec *delay, const char *decimal_point); -struct log_info *parse_logfile(const char *logfile); -void free_log_info(struct log_info *li); -void adjust_delay(struct timespec *delay, struct timespec *max_delay, double scale_factor); - #endif /* SUDOERS_IOLOG_H */ diff --git a/plugins/sudoers/iolog_util.c b/plugins/sudoers/iolog_util.c index e50ea5026..068c1e4d6 100644 --- a/plugins/sudoers/iolog_util.c +++ b/plugins/sudoers/iolog_util.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2018 Todd C. Miller + * Copyright (c) 2009-2019 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -56,18 +56,18 @@ #include "sudo_fatal.h" #include "sudo_debug.h" #include "sudo_util.h" -#include "iolog.h" +#include "iolog_util.h" static int timing_event_adj; -struct log_info * +struct iolog_info * parse_logfile(const char *logfile) { FILE *fp; char *buf = NULL, *cp, *ep; const char *errstr; size_t bufsize = 0, cwdsize = 0, cmdsize = 0; - struct log_info *li = NULL; + struct iolog_info *li = NULL; debug_decl(parse_logfile, SUDO_DEBUG_UTIL) fp = fopen(logfile, "r"); @@ -96,8 +96,8 @@ parse_logfile(const char *logfile) li->cmd[strcspn(li->cmd, "\n")] = '\0'; /* - * Crack the log line (rows and cols not present in old versions). - * timestamp:user:runas_user:runas_group:tty:rows:cols + * Crack the log line (lines and cols not present in old versions). + * timestamp:user:runas_user:runas_group:tty:lines:cols * XXX - probably better to use strtok and switch on the state. */ buf[strcspn(buf, "\n")] = '\0'; @@ -115,7 +115,7 @@ parse_logfile(const char *logfile) goto bad; } - /* user */ + /* submit user */ cp = ep + 1; if ((ep = strchr(cp, ':')) == NULL) { sudo_warn(U_("%s: user field is missing"), logfile); @@ -144,14 +144,14 @@ parse_logfile(const char *logfile) sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); } - /* tty, followed by optional rows + columns */ + /* tty, followed by optional lines + cols */ cp = ep + 1; if ((ep = strchr(cp, ':')) == NULL) { /* just the tty */ if ((li->tty = strdup(cp)) == NULL) sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); } else { - /* tty followed by rows + columns */ + /* tty followed by lines + cols */ if ((li->tty = strndup(cp, (size_t)(ep - cp))) == NULL) sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); cp = ep + 1; @@ -160,10 +160,10 @@ parse_logfile(const char *logfile) if ((ep = strchr(cp, ':')) != NULL) { *ep = '\0'; } - li->rows = sudo_strtonum(cp, 1, INT_MAX, &errstr); + li->lines = sudo_strtonum(cp, 1, INT_MAX, &errstr); if (errstr != NULL) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "%s: tty rows %s: %s", logfile, cp, errstr); + "%s: tty lines %s: %s", logfile, cp, errstr); } if (ep != NULL) { cp = ep + 1; @@ -182,7 +182,7 @@ bad: if (fp != NULL) fclose(fp); free(buf); - free_log_info(li); + free_iolog_info(li); debug_return_ptr(NULL); } @@ -298,15 +298,14 @@ parse_delay(const char *cp, struct timespec *delay, const char *decimal_point) /* * Parse a timing line, which is formatted as: * IO_EVENT_TTYOUT sleep_time num_bytes - * IO_EVENT_WINSIZE sleep_time rows cols + * IO_EVENT_WINSIZE sleep_time lines cols * IO_EVENT_SUSPEND sleep_time signo * Where type is IO_EVENT_*, sleep_time is the number of seconds to sleep * before writing the data and num_bytes is the number of bytes to output. * Returns true on success and false on failure. */ bool -parse_timing(const char *buf, struct timespec *delay, - struct timing_closure *timing) +parse_timing(const char *line, struct timing_closure *timing) { unsigned long ulval; char *cp, *ep; @@ -316,8 +315,8 @@ parse_timing(const char *buf, struct timespec *delay, timing->fd.v = NULL; /* Parse event type. */ - ulval = strtoul(buf, &ep, 10); - if (ep == buf || !isspace((unsigned char) *ep)) + ulval = strtoul(line, &ep, 10); + if (ep == line || !isspace((unsigned char) *ep)) goto bad; if (ulval >= IO_EVENT_COUNT) goto bad; @@ -330,7 +329,7 @@ parse_timing(const char *buf, struct timespec *delay, continue; /* Parse delay, returns the next field or NULL on error. */ - if ((cp = parse_delay(cp, delay, timing->decimal)) == NULL) + if ((cp = parse_delay(cp, &timing->delay, timing->decimal)) == NULL) goto bad; switch (timing->event) { @@ -345,7 +344,7 @@ parse_timing(const char *buf, struct timespec *delay, goto bad; if (ulval > INT_MAX) goto bad; - timing->u.winsize.rows = (int)ulval; + timing->u.winsize.lines = (int)ulval; for (cp = ep + 1; isspace((unsigned char) *cp); cp++) continue; @@ -374,7 +373,7 @@ bad: } void -free_log_info(struct log_info *li) +free_iolog_info(struct iolog_info *li) { if (li != NULL) { free(li->cwd); diff --git a/plugins/sudoers/iolog_util.h b/plugins/sudoers/iolog_util.h new file mode 100644 index 000000000..f9682be13 --- /dev/null +++ b/plugins/sudoers/iolog_util.h @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2009-2019 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * 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. + */ + +#ifndef SUDOERS_IOLOG_READER_H +#define SUDOERS_IOLOG_READER_H + +#include "iolog.h" + +/* + * Info present in the I/O log file + */ +struct iolog_info { + char *cwd; + char *user; + char *runas_user; + char *runas_group; + char *tty; + char *cmd; + time_t tstamp; + int lines; + int cols; +}; + +struct timing_closure { + struct timespec delay; + const char *decimal; + union io_fd fd; + int event; + union { + struct { + int lines; + int cols; + } winsize; + size_t nbytes; // XXX + int signo; + } u; +}; + +/* iolog_reader.c */ +bool parse_timing(const char *line, struct timing_closure *timing); +char *parse_delay(const char *cp, struct timespec *delay, const char *decimal_point); +struct iolog_info *parse_logfile(const char *logfile); +void free_iolog_info(struct iolog_info *li); +void adjust_delay(struct timespec *delay, struct timespec *max_delay, double scale_factor); + +#endif /* SUDOERS_IOLOG_READER_H */ diff --git a/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c b/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c index d3523607c..9ec02bcab 100644 --- a/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c +++ b/plugins/sudoers/regress/iolog_plugin/check_iolog_plugin.c @@ -37,7 +37,7 @@ #include "sudoers.h" #include "def_data.c" /* for iolog_path.c */ #include "sudo_plugin.h" -#include "iolog.h" +#include "iolog_util.h" extern struct io_plugin sudoers_io; @@ -85,7 +85,7 @@ bool validate_iolog_info(const char *logfile) { time_t now; - struct log_info *info; + struct iolog_info *info; time(&now); @@ -123,8 +123,8 @@ validate_iolog_info(const char *logfile) return false; } - if (info->rows != 24) { - sudo_warnx("bad rows: want 24 got %d", info->rows); + if (info->lines != 24) { + sudo_warnx("bad lines: want 24 got %d", info->lines); return false; } @@ -139,7 +139,7 @@ validate_iolog_info(const char *logfile) return false; } - free_log_info(info); + free_iolog_info(info); return true; } @@ -149,14 +149,13 @@ validate_timing(FILE *fp, int recno, int type, unsigned int p1, unsigned int p2) { struct timing_closure timing; char buf[LINE_MAX]; - struct timespec delay; if (!fgets(buf, sizeof(buf), fp)) { sudo_warn("unable to read timing file"); return false; } buf[strcspn(buf, "\n")] = '\0'; - if (!parse_timing(buf, &delay, &timing)) { + if (!parse_timing(buf, &timing)) { sudo_warnx("invalid timing file line: %s", buf); return false; } @@ -166,9 +165,9 @@ validate_timing(FILE *fp, int recno, int type, unsigned int p1, unsigned int p2) return false; } if (type == IO_EVENT_WINSIZE) { - if (timing.u.winsize.rows != (int)p1) { - sudo_warnx("record %d: want %u rows, got %u", recno, p1, - timing.u.winsize.rows); + if (timing.u.winsize.lines != (int)p1) { + sudo_warnx("record %d: want %u lines, got %u", recno, p1, + timing.u.winsize.lines); return false; } if (timing.u.winsize.cols != (int)p2) { @@ -183,9 +182,9 @@ validate_timing(FILE *fp, int recno, int type, unsigned int p1, unsigned int p2) return false; } } - if (delay.tv_sec != 0 || delay.tv_nsec > 10000000) { + if (timing.delay.tv_sec != 0 || timing.delay.tv_nsec > 10000000) { sudo_warnx("record %d: got excessive delay %lld.%09ld", recno, - (long long)delay.tv_sec, delay.tv_nsec); + (long long)timing.delay.tv_sec, timing.delay.tv_nsec); return false; } diff --git a/plugins/sudoers/regress/iolog_util/check_iolog_util.c b/plugins/sudoers/regress/iolog_util/check_iolog_util.c index 64976c5ea..fb3fd3370 100644 --- a/plugins/sudoers/regress/iolog_util/check_iolog_util.c +++ b/plugins/sudoers/regress/iolog_util/check_iolog_util.c @@ -35,7 +35,7 @@ #include "sudo_compat.h" #include "sudo_util.h" #include "sudo_fatal.h" -#include "iolog.h" +#include "iolog_util.h" __dso_public int main(int argc, char *argv[]); @@ -137,14 +137,14 @@ main(int argc, char *argv[]) { int tests = 0, errors = 0; - initprogname(argc > 0 ? argv[0] : "check_iolog_util"); + initprogname(argc > 0 ? argv[0] : "check_iolog_reader"); test_parse_delay(&tests, &errors); test_adjust_delay(&tests, &errors); if (tests != 0) { - printf("check_iolog_util: %d test%s run, %d errors, %d%% success rate\n", + printf("check_iolog_reader: %d test%s run, %d errors, %d%% success rate\n", tests, tests == 1 ? "" : "s", errors, (tests - errors) * 100 / tests); } diff --git a/plugins/sudoers/sudoreplay.c b/plugins/sudoers/sudoreplay.c index 5c2e0d233..e411b8ac1 100644 --- a/plugins/sudoers/sudoreplay.c +++ b/plugins/sudoers/sudoreplay.c @@ -64,7 +64,7 @@ #include "sudo_compat.h" #include "sudo_fatal.h" #include "logging.h" -#include "iolog.h" +#include "iolog_util.h" #include "iolog_files.h" #include "sudo_queue.h" #include "sudo_plugin.h" @@ -89,6 +89,7 @@ struct replay_closure { struct sudo_event *sigquit_ev; struct sudo_event *sigterm_ev; struct sudo_event *sigtstp_ev; + struct timespec *max_delay; struct timing_closure timing; bool interactive; bool suspend_wait; @@ -141,7 +142,7 @@ static const char *session_dir = _PATH_SUDO_IO_LOGDIR; static bool terminal_can_resize, terminal_was_resized; -static int terminal_rows, terminal_cols; +static int terminal_lines, terminal_cols; static int ttyfd = -1; @@ -174,7 +175,7 @@ static void sudoreplay_cleanup(void); static void usage(int); static void write_output(int fd, int what, void *v); static void restore_terminal_size(void); -static void setup_terminal(struct log_info *li, bool interactive, bool resize); +static void setup_terminal(struct iolog_info *li, bool interactive, bool resize); #define VALID_ID(s) (isalnum((unsigned char)(s)[0]) && \ isalnum((unsigned char)(s)[1]) && isalnum((unsigned char)(s)[2]) && \ @@ -200,7 +201,7 @@ main(int argc, char *argv[]) bool interactive = true, suspend_wait = false, resize = true; const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL; char *cp, *ep, path[PATH_MAX]; - struct log_info *li; + struct iolog_info *li; struct timespec max_delay_storage, *max_delay = NULL; double dval; debug_decl(main, SUDO_DEBUG_MAIN) @@ -353,7 +354,7 @@ main(int argc, char *argv[]) putchar('\n'); /* Done with parsed log file. */ - free_log_info(li); + free_iolog_info(li); li = NULL; /* Replay session corresponding to io_log_files[]. */ @@ -551,7 +552,7 @@ done: * Get the terminal size using vt100 terminal escapes. */ static bool -xterm_get_size(int *new_rows, int *new_cols) +xterm_get_size(int *new_lines, int *new_cols) { struct sudo_event_base *evbase; struct getsize_closure gc; @@ -569,7 +570,7 @@ xterm_get_size(int *new_rows, int *new_cols) /* * Callback info for reading back the size with a 10 second timeout. - * We expect two numbers (rows and cols). + * We expect two numbers (lines and cols). */ gc.state = INITIAL|READCHAR; gc.nums_depth = 0; @@ -594,7 +595,7 @@ xterm_get_size(int *new_rows, int *new_cols) if (gc.state == GOTSIZE) { sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "terminal size %d x %x", gc.nums[0], gc.nums[1]); - *new_rows = gc.nums[0]; + *new_lines = gc.nums[0]; *new_cols = gc.nums[1]; ret = true; } @@ -607,21 +608,21 @@ done: } /* - * Set the size of the text area to rows and cols. + * Set the size of the text area to lines and cols. * Depending on the terminal implementation, the window itself may * or may not shrink to a smaller size. */ static bool -xterm_set_size(int rows, int cols) +xterm_set_size(int lines, int cols) { const char setsize_fmt[] = "\033[8;%d;%dt"; - int len, new_rows, new_cols; + int len, new_lines, new_cols; bool ret = false; char buf[1024]; debug_decl(xterm_set_size, SUDO_DEBUG_UTIL) /* XXX - save cursor and position restore after resizing */ - len = snprintf(buf, sizeof(buf), setsize_fmt, rows, cols); + len = snprintf(buf, sizeof(buf), setsize_fmt, lines, cols); if (len < 0 || len >= ssizeof(buf)) { /* not possible due to size of buf */ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, @@ -634,9 +635,9 @@ xterm_set_size(int rows, int cols) goto done; } /* XXX - keyboard input will interfere with this */ - if (!xterm_get_size(&new_rows, &new_cols)) + if (!xterm_get_size(&new_lines, &new_cols)) goto done; - if (rows == new_rows && cols == new_cols) + if (lines == new_lines && cols == new_cols) ret = true; done: @@ -644,7 +645,7 @@ done: } static void -setup_terminal(struct log_info *li, bool interactive, bool resize) +setup_terminal(struct iolog_info *li, bool interactive, bool resize) { const char *term; debug_decl(check_terminal, SUDO_DEBUG_UTIL) @@ -662,7 +663,7 @@ setup_terminal(struct log_info *li, bool interactive, bool resize) } /* Find terminal size if the session has size info. */ - if (li->rows == 0 && li->cols == 0) { + if (li->lines == 0 && li->cols == 0) { /* no tty size info, hope for the best... */ debug_return; } @@ -675,7 +676,7 @@ setup_terminal(struct log_info *li, bool interactive, bool resize) for (tn = compatible_terms; tn->name != NULL; tn++) { if (strncmp(term, tn->name, tn->len) == 0) { /* xterm-like terminals can resize themselves. */ - if (xterm_get_size(&terminal_rows, &terminal_cols)) + if (xterm_get_size(&terminal_lines, &terminal_cols)) terminal_can_resize = true; break; } @@ -685,20 +686,20 @@ setup_terminal(struct log_info *li, bool interactive, bool resize) if (!terminal_can_resize) { /* either not xterm or not interactive */ - sudo_get_ttysize(&terminal_rows, &terminal_cols); + sudo_get_ttysize(&terminal_lines, &terminal_cols); } - if (li->rows == terminal_rows && li->cols == terminal_cols) { + if (li->lines == terminal_lines && li->cols == terminal_cols) { /* nothing to change */ debug_return; } if (terminal_can_resize) { /* session terminal size is different, try to resize ours */ - if (xterm_set_size(li->rows, li->cols)) { + if (xterm_set_size(li->lines, li->cols)) { /* success */ sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, - "resized terminal to %d x %x", li->rows, li->cols); + "resized terminal to %d x %x", li->lines, li->cols); terminal_was_resized = true; debug_return; } @@ -706,20 +707,20 @@ setup_terminal(struct log_info *li, bool interactive, bool resize) terminal_can_resize = false; } - if (li->rows > terminal_rows || li->cols > terminal_cols) { + if (li->lines > terminal_lines || li->cols > terminal_cols) { fputs(_("Warning: your terminal is too small to properly replay the log.\n"), stdout); - printf(_("Log geometry is %d x %d, your terminal's geometry is %d x %d."), li->rows, li->cols, terminal_rows, terminal_cols); + printf(_("Log geometry is %d x %d, your terminal's geometry is %d x %d."), li->lines, li->cols, terminal_lines, terminal_cols); } debug_return; } static void -resize_terminal(int rows, int cols) +resize_terminal(int lines, int cols) { debug_decl(resize_terminal, SUDO_DEBUG_UTIL) if (terminal_can_resize) { - if (xterm_set_size(rows, cols)) + if (xterm_set_size(lines, cols)) terminal_was_resized = true; else terminal_can_resize = false; @@ -740,7 +741,7 @@ restore_terminal_size(void) stdout); fflush(stdout); (void)getchar(); - xterm_set_size(terminal_rows, terminal_cols); + xterm_set_size(terminal_lines, terminal_cols); putchar('\r'); putchar('\n'); } @@ -756,36 +757,36 @@ restore_terminal_size(void) static int read_timing_record(struct replay_closure *closure) { - struct timespec timeout; - char buf[LINE_MAX]; + struct timing_closure *timing = &closure->timing; + char line[LINE_MAX]; debug_decl(read_timing_record, SUDO_DEBUG_UTIL) /* Read next record from timing file. */ - if (io_log_gets(io_log_files[IOFD_TIMING].fd, buf, sizeof(buf)) == NULL) { + if (io_log_gets(io_log_files[IOFD_TIMING].fd, line, sizeof(line)) == NULL) { /* EOF or error reading timing file, we are done. */ debug_return_int(io_log_eof(io_log_files[IOFD_TIMING].fd) ? 1 : -1); } /* Parse timing file record. */ - buf[strcspn(buf, "\n")] = '\0'; - if (!parse_timing(buf, &timeout, &closure->timing)) - sudo_fatalx(U_("invalid timing file line: %s"), buf); + line[strcspn(line, "\n")] = '\0'; + if (!parse_timing(line, timing)) + sudo_fatalx(U_("invalid timing file line: %s"), line); /* Record number bytes to read. */ /* XXX - remove timing->nbytes? */ - if (closure->timing.event != IO_EVENT_WINSIZE && - closure->timing.event != IO_EVENT_SUSPEND) { + if (timing->event != IO_EVENT_WINSIZE && + timing->event != IO_EVENT_SUSPEND) { closure->iobuf.len = 0; closure->iobuf.off = 0; closure->iobuf.lastc = '\0'; - closure->iobuf.toread = closure->timing.u.nbytes; + closure->iobuf.toread = timing->u.nbytes; } /* Adjust delay using speed factor and max_delay. */ - adjust_delay(&timeout, closure->timing.max_delay, speed_factor); + adjust_delay(&timing->delay, closure->max_delay, speed_factor); /* Schedule the delay event. */ - if (sudo_ev_add(closure->evbase, closure->delay_ev, &timeout, false) == -1) + if (sudo_ev_add(closure->evbase, closure->delay_ev, &timing->delay, false) == -1) sudo_fatal(U_("unable to add event to queue")); debug_return_int(0); @@ -868,7 +869,7 @@ delay_cb(int fd, int what, void *v) switch (timing->event) { case IO_EVENT_WINSIZE: - resize_terminal(timing->u.winsize.rows, timing->u.winsize.cols); + resize_terminal(timing->u.winsize.lines, timing->u.winsize.cols); break; case IO_EVENT_STDIN: if (io_log_files[IOFD_STDIN].enabled) @@ -960,7 +961,7 @@ replay_closure_alloc(struct timespec *max_delay, const char *decimal, closure->interactive = interactive; closure->suspend_wait = suspend_wait; - closure->timing.max_delay = max_delay; + closure->max_delay = max_delay; closure->timing.decimal = decimal; /* @@ -1297,7 +1298,7 @@ parse_expr(struct search_node_list *head, char *argv[], bool sub_expr) } static bool -match_expr(struct search_node_list *head, struct log_info *log, bool last_match) +match_expr(struct search_node_list *head, struct iolog_info *log, bool last_match) { struct search_node *sn; bool res = false, matched = last_match; @@ -1357,7 +1358,7 @@ list_session(char *logfile, regex_t *re, const char *user, const char *tty) { char idbuf[7], *idstr, *cp; const char *timestr; - struct log_info *li; + struct iolog_info *li; int ret = -1; debug_decl(list_session, SUDO_DEBUG_UTIL) @@ -1384,7 +1385,7 @@ list_session(char *logfile, regex_t *re, const char *user, const char *tty) cp[strlen(cp) - 4] = '\0'; idstr = cp; } - /* XXX - print rows + cols? */ + /* XXX - print lines + cols? */ timestr = get_timestr(li->tstamp, 1); printf("%s : %s : TTY=%s ; CWD=%s ; USER=%s ; ", timestr ? timestr : "invalid date", @@ -1396,7 +1397,7 @@ list_session(char *logfile, regex_t *re, const char *user, const char *tty) ret = 0; done: - free_log_info(li); + free_iolog_info(li); debug_return_int(ret); }