Repair writing of the I/O log file indices broken in sudo 1.8.7.
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 3 Sep 2013 15:22:44 +0000 (09:22 -0600)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 3 Sep 2013 15:22:44 +0000 (09:22 -0600)
MANIFEST
plugins/sudoers/iolog.c
plugins/sudoers/iolog.h [new file with mode: 0644]
plugins/sudoers/sudoreplay.c

index 0a7c4b0ae63a5ce3010f4f45bf7e0bde9f29f1ea..fb2d90f62b0f724b1b815d722aa2492b737c9fbb 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -219,6 +219,7 @@ plugins/sudoers/insults.h
 plugins/sudoers/interfaces.c
 plugins/sudoers/interfaces.h
 plugins/sudoers/iolog.c
+plugins/sudoers/iolog.h
 plugins/sudoers/iolog_path.c
 plugins/sudoers/ldap.c
 plugins/sudoers/linux_audit.c
index af7fcac84a6c9af241c41feb9cf4fb16448a64af..57b2026b485ef4955d928273d29f040ed47bbe64 100644 (file)
@@ -50,6 +50,7 @@
 #endif
 
 #include "sudoers.h"
+#include "iolog.h"
 
 struct script_buf {
     int len; /* buffer length (how much read in) */
@@ -73,39 +74,6 @@ struct iolog_details {
     int cols;
 };
 
-union io_fd {
-    FILE *f;
-#ifdef HAVE_ZLIB_H
-    gzFile g;
-#endif
-    void *v;
-};
-
-static struct io_log_file {
-    bool enabled;
-    const char *suffix;
-    union io_fd fd;
-} io_log_files[] = {
-#define IOFD_LOG       0
-    { true,  "/log" },
-#define IOFD_TIMING    1
-    { true,  "/timing" },
-#define IOFD_STDIN     2
-    { false, "/stdin" },
-#define IOFD_STDOUT    3
-    { false, "/stdout" },
-#define IOFD_STDERR    4
-    { false, "/stderr" },
-#define IOFD_TTYIN     5
-    { false, "/ttyin" },
-#define IOFD_TTYOUT    6
-    { false, "/ttyout" },
-#define IOFD_MAX       7
-    { false, NULL }
-};
-
-#define SESSID_MAX     2176782336U
-
 static int iolog_compress;
 static struct timeval last_time;
 static unsigned int sessid_max = SESSID_MAX;
@@ -330,11 +298,8 @@ open_io_fd(char *pathbuf, size_t len, struct io_log_file *iol, bool docompress)
 #endif
                iol->fd.f = fdopen(fd, "w");
        }
-       if (fd == -1 || iol->fd.v == NULL) {
+       if (fd == -1 || iol->fd.v == NULL)
            log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
-           if (fd != -1)
-               close(fd);
-       }
     } else {
        /* Remove old log file if we recycled sequence numbers. */
        unlink(pathbuf);
@@ -508,6 +473,37 @@ iolog_deserialize_info(struct iolog_details *details, char * const user_info[],
        io_log_files[IOFD_TTYOUT].enabled);
 }
 
+/*
+ * Write the "/log" file that contains the user and command info.
+ */
+void
+write_info_log(char *pathbuf, size_t len, struct iolog_details *details,
+    char * const argv[], struct timeval *now)
+{
+    char * const *av;
+    FILE *fp;
+    int fd;
+
+    pathbuf[len] = '\0';
+    strlcat(pathbuf, "/log", PATH_MAX);
+    fd = open(pathbuf, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
+    if (fd != -1 || (fp = fdopen(fd, "w")) == NULL)
+       log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
+
+    fprintf(fp, "%lld:%s:%s:%s:%s:%d:%d\n%s\n%s", (long long)now->tv_sec,
+       details->user ? details->user : "unknown", details->runas_pw->pw_name,
+       details->runas_gr ? details->runas_gr->gr_name : "",
+       details->tty ? details->tty : "unknown", details->lines, details->cols,
+       details->cwd ? details->cwd : "unknown",
+       details->command ? details->command : "unknown");
+    for (av = argv + 1; *av != NULL; av++) {
+       fputc(' ', fp);
+       fputs(*av, fp);
+    }
+    fputc('\n', fp);
+    fclose(fp);
+}
+
 static int
 sudoers_io_open(unsigned int version, sudo_conv_t conversation,
     sudo_printf_t plugin_printf, char * const settings[],
@@ -581,28 +577,13 @@ sudoers_io_open(unsigned int version, sudo_conv_t conversation,
     if (len >= sizeof(pathbuf))
        goto done;
 
-    /*
-     * We create 7 files: a log file, a timing file and 5 for input/output.
-     */
-    for (i = 0; i < IOFD_MAX; i++) {
-       open_io_fd(pathbuf, len, &io_log_files[i], i ? iolog_compress : false);
-    }
-
+    /* Write log file with user and command details. */
     gettimeofday(&last_time, NULL);
-    fprintf(io_log_files[IOFD_LOG].fd.f, "%lld:%s:%s:%s:%s:%d:%d\n%s\n%s",
-       (long long)last_time.tv_sec,
-       details.user ? details.user : "unknown", details.runas_pw->pw_name,
-       details.runas_gr ? details.runas_gr->gr_name : "",
-       details.tty ? details.tty : "unknown", details.lines, details.cols,
-       details.cwd ? details.cwd : "unknown",
-       details.command ? details.command : "unknown");
-    for (cur = &argv[1]; *cur != NULL; cur++) {
-       fputc(' ', io_log_files[IOFD_LOG].fd.f);
-       fputs(*cur, io_log_files[IOFD_LOG].fd.f);
-    }
-    fputc('\n', io_log_files[IOFD_LOG].fd.f);
-    fclose(io_log_files[IOFD_LOG].fd.f);
-    io_log_files[IOFD_LOG].fd.f = NULL;
+    write_info_log(pathbuf, len, &details, argv, &last_time);
+
+    /* Create the timing I/O log files. */
+    for (i = 0; i < IOFD_MAX; i++)
+       open_io_fd(pathbuf, len, &io_log_files[i], iolog_compress);
 
     /*
      * Clear I/O log function pointers for disabled log functions.
diff --git a/plugins/sudoers/iolog.h b/plugins/sudoers/iolog.h
new file mode 100644 (file)
index 0000000..54d9955
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SUDOERS_IOLOG_H
+#define _SUDOERS_IOLOG_H
+
+/*
+ * I/O log fd numbers as stored in the timing file.
+ * Changing these will result in incompatible I/O log files!
+ */
+#define IOFD_STDIN     0
+#define IOFD_STDOUT    1
+#define IOFD_STDERR    2
+#define IOFD_TTYIN     3
+#define IOFD_TTYOUT    4
+#define IOFD_TIMING    5
+#define IOFD_MAX       6
+
+/* Default maximum session ID */
+#define SESSID_MAX      2176782336U
+
+union io_fd {
+    FILE *f;
+#ifdef HAVE_ZLIB_H
+    gzFile g;
+#endif
+    void *v;
+};
+
+struct io_log_file {
+    bool enabled;
+    const char *suffix;
+    union io_fd fd;
+};
+
+static struct io_log_file io_log_files[] = {
+    { false, "/stdin" },       /* IOFD_STDIN */
+    { false, "/stdout" },      /* IOFD_STDOUT */
+    { false, "/stderr" },      /* IOFD_STDERR */
+    { false, "/ttyin" },       /* IOFD_TTYIN  */
+    { false, "/ttyout" },      /* IOFD_TTYOUT */
+    { true,  "/timing" },      /* IOFD_TIMING */
+    { false, NULL }            /* IOFD_MAX */
+};
+
+#endif /* _SUDOERS_IOLOG_H */
index 5a9bbc25345bee8e73a247c4ecb024e56d7f5edc..8cefbbd8b0b26bc1a338b971b744fba0b91f3b03 100644 (file)
 #include "fatal.h"
 #include "gettext.h"
 #include "logging.h"
+#include "iolog.h"
 #include "sudo_plugin.h"
 #include "sudo_conf.h"
 #include "sudo_debug.h"
 # define LINE_MAX 2048
 #endif
 
-/* Must match the defines in iolog.c */
-#define IOFD_STDIN      0
-#define IOFD_STDOUT     1
-#define IOFD_STDERR     2
-#define IOFD_TTYIN      3
-#define IOFD_TTYOUT     4
-#define IOFD_TIMING     5
-#define IOFD_MAX        6
-
-/* Bitmap of iofds to be replayed */
-unsigned int replay_filter = (1 << IOFD_STDOUT) | (1 << IOFD_STDERR) |
-                            (1 << IOFD_TTYOUT);
-
-union io_fd {
-    FILE *f;
-#ifdef HAVE_ZLIB_H
-    gzFile g;
-#endif
-    void *v;
-};
-
 /*
  * Info present in the I/O log file
  */
@@ -185,16 +165,6 @@ static int stack_top;
 
 static const char *session_dir = _PATH_SUDO_IO_LOGDIR;
 
-static union io_fd io_fds[IOFD_MAX];
-static const char *io_fnames[IOFD_MAX] = {
-    "/stdin",
-    "/stdout",
-    "/stderr",
-    "/ttyin",
-    "/ttyout",
-    "/timing"
-};
-
 static const char short_opts[] =  "d:f:hlm:s:V";
 static struct option long_opts[] = {
     { "directory",     required_argument,      NULL,   'd' },
@@ -219,7 +189,7 @@ static void check_input(int, double *);
 static void delay(double);
 static void help(void) __attribute__((__noreturn__));
 static void usage(int);
-static int open_io_fd(char *pathbuf, int len, const char *suffix, union io_fd *fdp);
+static int open_io_fd(char *path, int len, struct io_log_file *iol);
 static int parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, size_t *nbytes);
 static struct log_info *parse_logfile(char *logfile);
 static void free_log_info(struct log_info *li);
@@ -254,6 +224,7 @@ main(int argc, char *argv[])
 {
     int ch, idx, plen, exitcode = 0, rows = 0, cols = 0;
     bool interactive = false, listonly = false, need_nlcr = false;
+    bool def_filter = true;
     const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL;
     char path[PATH_MAX], buf[LINE_MAX], *cp, *ep;
     double seconds, to_wait, speed = 1.0, max_wait = 0;
@@ -293,14 +264,14 @@ main(int argc, char *argv[])
            break;
        case 'f':
            /* Set the replay filter. */
-           replay_filter = 0;
+           def_filter = false;
            for (cp = strtok(optarg, ","); cp; cp = strtok(NULL, ",")) {
                if (strcmp(cp, "stdout") == 0)
-                   SET(replay_filter, 1 << IOFD_STDOUT);
+                   io_log_files[IOFD_STDOUT].enabled = true;
                else if (strcmp(cp, "stderr") == 0)
-                   SET(replay_filter, 1 << IOFD_STDERR);
+                   io_log_files[IOFD_STDERR].enabled = true;
                else if (strcmp(cp, "ttyout") == 0)
-                   SET(replay_filter, 1 << IOFD_TTYOUT);
+                   io_log_files[IOFD_TTYOUT].enabled = true;
                else
                    fatalx(_("invalid filter option: %s"), optarg);
            }
@@ -343,6 +314,13 @@ main(int argc, char *argv[])
     if (argc != 1)
        usage(1);
 
+    /* By default we replay stdout, stderr and ttyout. */
+    if (def_filter) {
+       io_log_files[IOFD_STDOUT].enabled = true;
+       io_log_files[IOFD_STDERR].enabled = true;
+       io_log_files[IOFD_TTYOUT].enabled = true;
+    }
+
     /* 6 digit ID in base 36, e.g. 01G712AB or free-form name */
     id = argv[0];
     if (VALID_ID(id)) {
@@ -362,10 +340,8 @@ main(int argc, char *argv[])
 
     /* Open files for replay, applying replay filter for the -f flag. */
     for (idx = 0; idx < IOFD_MAX; idx++) {
-       if (ISSET(replay_filter, 1 << idx) || idx == IOFD_TIMING) {
-           if (open_io_fd(path, plen, io_fnames[idx], &io_fds[idx]) == -1)
-               fatal(_("unable to open %s"), path);
-       }
+       if (open_io_fd(path, plen, &io_log_files[idx]) == -1) 
+           fatal(_("unable to open %s"), path);
     }
 
     /* Parse log file. */
@@ -419,9 +395,9 @@ main(int argc, char *argv[])
      * Timing file consists of line of the format: "%f %d\n"
      */
 #ifdef HAVE_ZLIB_H
-    while (gzgets(io_fds[IOFD_TIMING].g, buf, sizeof(buf)) != NULL) {
+    while (gzgets(io_log_files[IOFD_TIMING].fd.g, buf, sizeof(buf)) != NULL) {
 #else
-    while (fgets(buf, sizeof(buf), io_fds[IOFD_TIMING].f) != NULL) {
+    while (fgets(buf, sizeof(buf), io_log_files[IOFD_TIMING].fd.f) != NULL) {
 #endif
        char last_char = '\0';
 
@@ -437,8 +413,8 @@ main(int argc, char *argv[])
            to_wait = max_wait;
        delay(to_wait);
 
-       /* Even if we are not relaying, we still have to delay. */
-       if (io_fds[idx].v == NULL)
+       /* Even if we are not replaying, we still have to delay. */
+       if (io_log_files[idx].fd.v == NULL)
            continue;
 
        /* Check whether we need to convert newline to CR LF pairs. */
@@ -452,9 +428,9 @@ main(int argc, char *argv[])
            else
                len = nbytes;
 #ifdef HAVE_ZLIB_H
-           nread = gzread(io_fds[idx].g, buf, len);
+           nread = gzread(io_log_files[idx].fd.g, buf, len);
 #else
-           nread = fread(buf, 1, len, io_fds[idx].f);
+           nread = fread(buf, 1, len, io_log_files[idx].fd.f);
 #endif
            nbytes -= nread;
 
@@ -544,19 +520,21 @@ delay(double secs)
 }
 
 static int
-open_io_fd(char *path, int len, const char *suffix, union io_fd *fdp)
+open_io_fd(char *path, int len, struct io_log_file *iol)
 {
     debug_decl(open_io_fd, SUDO_DEBUG_UTIL)
 
-    path[len] = '\0';
-    strlcat(path, suffix, PATH_MAX);
+    if (!iol->enabled)
+       debug_return_int(0);
 
+    path[len] = '\0';
+    strlcat(path, iol->suffix, PATH_MAX);
 #ifdef HAVE_ZLIB_H
-    fdp->g = gzopen(path, "r");
+    iol->fd.g = gzopen(path, "r");
 #else
-    fdp->f = fopen(path, "r");
+    iol->fd.f = fopen(path, "r");
 #endif
-    debug_return_int(fdp->v ? 0 : -1);
+    debug_return_int(iol->fd.v ? 0 : -1);
 }
 
 /*