]> granicus.if.org Git - sudo/commitdiff
Add back io logging (transcript) support. Currently, the open function
authorTodd C. Miller <Todd.Miller@courtesan.com>
Sun, 28 Mar 2010 00:19:40 +0000 (20:19 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Sun, 28 Mar 2010 00:19:40 +0000 (20:19 -0400)
runs too early and it is not possible to use the io module independently
of the policy module.

plugins/sudoers/Makefile.in
plugins/sudoers/iolog.c [new file with mode: 0644]
plugins/sudoers/set_perms.c
plugins/sudoers/sudoers.c
plugins/sudoers/sudoers.h

index 4075455812660e73c62a022ba44440b6f42edcff..17b4ef85f1b191a27396b50d168012f82cef3474 100644 (file)
@@ -44,8 +44,8 @@ INSTALL = $(SHELL) $(top_srcdir)/install-sh -c
 # Libraries
 LIBS = $(top_srcdir)/@ac_config_libobj_dir@/libreplace.la
 NET_LIBS = @NET_LIBS@
-SUDOERS_LIBS = @SUDOERS_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ $(LIBS) $(NET_LIBS)
-REPLAY_LIBS = @REPLAY_LIBS@
+SUDOERS_LIBS = @SUDOERS_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ $(LIBS) $(NET_LIBS) @ZLIB@
+REPLAY_LIBS = @REPLAY_LIBS@ @ZLIB@
 
 # C preprocessor flags
 CPPFLAGS = -I$(incdir) -I$(top_builddir) -I$(srcdir) @CPPFLAGS@
@@ -101,9 +101,10 @@ LIBSUDOERS_OBJS = alias.lo alloc.lo atobool.lo defaults.lo gram.lo list.lo \
                  zero_bytes.lo @NONUNIX_GROUPS_IMPL@
 
 SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo fmt_string.lo \
-              plugin_error.lo env.lo getspwuid.lo gettime.lo goodpath.lo \
-              fileops.lo find_path.lo interfaces.lo lbuf.lo logging.lo \
-              parse.lo set_perms.lo sudoers.lo sudo_nss.lo @SUDOERS_OBJS@
+              plugin_error.lo env.lo getspwuid.lo gettime.lo \
+              goodpath.lo fileops.lo find_path.lo interfaces.lo lbuf.lo \
+              logging.lo parse.lo set_perms.lo sudoers.lo sudo_nss.lo \
+              iolog.lo @SUDOERS_OBJS@
 
 # XXX - need top level error.o; perhaps make #ifdef PIC?
 VISUDO_OBJS = visudo.o fileops.o gettime.o goodpath.o find_path.o error.o
@@ -145,7 +146,7 @@ visudo: libsudoers.la $(VISUDO_OBJS)
        $(LIBTOOL) --mode=link $(CC) -o $@ $(VISUDO_OBJS) $(LDFLAGS) libsudoers.la $(LIBS) $(NET_LIBS)
 
 sudoreplay: libsudoers.la $(REPLAY_OBJS)
-       $(LIBTOOL) --mode=link $(CC) -o $@ $(REPLAY_OBJS) $(LDFLAGS) $(REPLAY_LIBS) libsudoers.la $(LIBS) @ZLIB@
+       $(LIBTOOL) --mode=link $(CC) -o $@ $(REPLAY_OBJS) $(LDFLAGS) $(REPLAY_LIBS) libsudoers.la $(LIBS)
 
 testsudoers: $(TEST_OBJS)
        $(LIBTOOL) --mode=link $(CC) -o $@ $(TEST_OBJS) $(LDFLAGS) libsudoers.la $(LIBS) $(NET_LIBS)
@@ -205,6 +206,8 @@ gram.lo: $(devdir)/gram.c $(SUDODEP) $(srcdir)/parse.h $(incdir)/list.h $(devdir
        $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(devdir)/gram.c
 interfaces.lo: $(srcdir)/interfaces.c $(SUDODEP) $(srcdir)/interfaces.h
        $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/interfaces.c
+iolog.lo: $(srcdir)/iolog.c $(SUDODEP)
+       $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/iolog.c
 ldap.lo: $(srcdir)/ldap.c $(SUDODEP) $(srcdir)/parse.h $(incdir)/list.h
        $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/ldap.c
 logging.lo: $(srcdir)/logging.c $(SUDODEP)
diff --git a/plugins/sudoers/iolog.c b/plugins/sudoers/iolog.c
new file mode 100644 (file)
index 0000000..8ff8630
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2009-2010 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.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# ifdef HAVE_STRINGS_H
+#  include <strings.h>
+# endif
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#if TIME_WITH_SYS_TIME
+# include <time.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#ifdef HAVE_ZLIB
+# include <zlib.h>
+#endif
+
+#include "sudoers.h"
+
+union script_fd {
+    FILE *f;
+#ifdef HAVE_ZLIB
+    gzFile g;
+#endif
+    void *v;
+};
+
+struct script_buf {
+    int len; /* buffer length (how much read in) */
+    int off; /* write position (how much already consumed) */
+    char buf[16 * 1024];
+};
+
+static void
+io_nextid()
+{
+    struct stat sb;
+    char buf[32], *ep;
+    int fd, i, ch;
+    unsigned long id = 0;
+    int len;
+    ssize_t nread;
+    char pathbuf[PATH_MAX];
+
+    /*
+     * Create _PATH_SUDO_TRANSCRIPT if it doesn't already exist.
+     */
+    if (stat(_PATH_SUDO_TRANSCRIPT, &sb) != 0) {
+       if (mkdir(_PATH_SUDO_TRANSCRIPT, S_IRWXU) != 0)
+           log_error(USE_ERRNO, "Can't mkdir %s", _PATH_SUDO_TRANSCRIPT);
+    } else if (!S_ISDIR(sb.st_mode)) {
+       log_error(0, "%s exists but is not a directory (0%o)",
+           _PATH_SUDO_TRANSCRIPT, (unsigned int) sb.st_mode);
+    }
+
+    /*
+     * Open sequence file
+     */
+    len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", _PATH_SUDO_TRANSCRIPT);
+    if (len <= 0 || len >= sizeof(pathbuf)) {
+       errno = ENAMETOOLONG;
+       log_error(USE_ERRNO, "%s/seq", pathbuf);
+    }
+    fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
+    if (fd == -1)
+       log_error(USE_ERRNO, "cannot open %s", pathbuf);
+    lock_file(fd, SUDO_LOCK);
+
+    /* Read seq number (base 36). */
+    nread = read(fd, buf, sizeof(buf));
+    if (nread != 0) {
+       if (nread == -1)
+           log_error(USE_ERRNO, "cannot read %s", pathbuf);
+       id = strtoul(buf, &ep, 36);
+       if (buf == ep || id >= 2176782336U)
+           log_error(0, "invalid sequence number %s", pathbuf);
+    }
+    id++;
+
+    /*
+     * Convert id to a string and stash in sudo_user.sessid.
+     * Note that that least significant digits go at the end of the string.
+     */
+    for (i = 5; i >= 0; i--) {
+       ch = id % 36;
+       id /= 36;
+       buf[i] = ch < 10 ? ch + '0' : ch - 10 + 'A';
+    }
+    buf[6] = '\n';
+
+    /* Stash id logging purposes */
+    memcpy(sudo_user.sessid, buf, 6);
+    sudo_user.sessid[6] = '\0';
+
+    /* Rewind and overwrite old seq file. */
+    if (lseek(fd, 0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7)
+       log_error(USE_ERRNO, "Can't write to %s", pathbuf);
+    close(fd);
+}
+
+static int
+build_idpath(char *pathbuf, size_t pathsize)
+{
+    struct stat sb;
+    int i, len;
+
+    if (sudo_user.sessid[0] == '\0')
+       log_error(0, "tried to build a session id path without a session id");
+
+    /*
+     * Path is of the form /var/log/sudo-session/00/00/01.
+     */
+    len = snprintf(pathbuf, pathsize, "%s/%c%c/%c%c/%c%c", _PATH_SUDO_TRANSCRIPT,
+       sudo_user.sessid[0], sudo_user.sessid[1], sudo_user.sessid[2],
+       sudo_user.sessid[3], sudo_user.sessid[4], sudo_user.sessid[5]);
+    if (len <= 0 && len >= pathsize) {
+       errno = ENAMETOOLONG;
+       log_error(USE_ERRNO, "%s/%s", _PATH_SUDO_TRANSCRIPT, sudo_user.sessid);
+    }
+
+    /*
+     * Create the intermediate subdirs as needed.
+     */
+    for (i = 6; i > 0; i -= 3) {
+       pathbuf[len - i] = '\0';
+       if (stat(pathbuf, &sb) != 0) {
+           if (mkdir(pathbuf, S_IRWXU) != 0)
+               log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
+       } else if (!S_ISDIR(sb.st_mode)) {
+           log_error(0, "%s: %s", pathbuf, strerror(ENOTDIR));
+       }
+       pathbuf[len - i] = '/';
+    }
+
+    return(len);
+}
+
+/* XXX */
+static sudo_conv_t io_conv;
+static sigset_t ttyblock;
+static struct timeval last_time;
+static union script_fd io_outfile, io_timfile;
+
+/* XXX - need to defer this until after the policy check succeeds */
+int
+sudoers_io_open(unsigned int version, sudo_conv_t conversation,
+    char * const settings[], char * const user_info[], char * const user_env[])
+{
+    char pathbuf[PATH_MAX];
+    FILE *io_logfile;
+    int fd, len;
+
+    io_conv = conversation;
+
+    /* XXX - def_transcript may not be set yet */
+    if (!def_transcript)
+       return FALSE;
+
+    /*
+     * Build a path containing the session id split into two-digit subdirs,
+     * so ID 000001 becomes /var/log/sudo-session/00/00/01.
+     */
+    io_nextid();
+    len = build_idpath(pathbuf, sizeof(pathbuf));
+    if (len == -1)
+       return -1;
+
+    /*
+     * We create 3 files: a log file, one for the raw session data,
+     * and one for the timing info.
+     */
+    fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+    if (fd == -1)
+       log_error(USE_ERRNO, "Can't create %s", pathbuf);
+    io_logfile = fdopen(fd, "w");
+    if (io_logfile == NULL)
+        log_error(USE_ERRNO, "fdopen");
+
+    strlcat(pathbuf, ".scr", sizeof(pathbuf));
+    fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+    if (fd == -1)
+       log_error(USE_ERRNO, "Can't create %s", pathbuf);
+#ifdef HAVE_ZLIB
+    if (def_compress_transcript)
+        io_outfile.g = gzdopen(fd, "w");
+    else
+#endif
+       io_outfile.f = fdopen(fd, "w");
+    if (io_outfile.v == NULL)
+       log_error(USE_ERRNO, "Can't open %s", pathbuf);
+
+    pathbuf[len] = '\0';
+    strlcat(pathbuf, ".tim", sizeof(pathbuf));
+    fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+    if (fd == -1)
+       log_error(USE_ERRNO, "Can't create %s", pathbuf);
+#ifdef HAVE_ZLIB
+    if (def_compress_transcript)
+        io_timfile.g = gzdopen(fd, "w");
+    else
+#endif
+       io_timfile.f = fdopen(fd, "w");
+    if (io_timfile.v == NULL)
+       log_error(USE_ERRNO, "Can't open %s", pathbuf);
+
+    /* So we can block tty-generated signals */
+    sigemptyset(&ttyblock);
+    sigaddset(&ttyblock, SIGINT);
+    sigaddset(&ttyblock, SIGQUIT);
+    sigaddset(&ttyblock, SIGTSTP);
+    sigaddset(&ttyblock, SIGTTIN);
+    sigaddset(&ttyblock, SIGTTOU);
+
+    gettimeofday(&last_time, NULL);
+
+    /* XXX - too early, don't even have user_cmnd yet */
+    /* XXX - log more stuff?  window size? environment? */
+    fprintf(io_logfile, "%ld:%s:%s:%s:%s\n", last_time.tv_sec, user_name,
+        runas_pw->pw_name, runas_gr ? runas_gr->gr_name : "", user_tty);
+    fprintf(io_logfile, "%s\n", user_cwd);
+    fprintf(io_logfile, "%s%s%s\n", user_cmnd, user_args ? " " : "",
+        user_args ? user_args : "");
+    fclose(io_logfile);
+
+    return TRUE;
+}
+
+void
+sudoers_io_close(int exit_status, int error)
+{
+#ifdef HAVE_ZLIB
+    if (def_compress_transcript) {
+       gzclose(io_outfile.g);
+       gzclose(io_timfile.g);
+    } else
+#endif
+    {
+       fclose(io_outfile.f);
+       fclose(io_timfile.f);
+    }
+}
+
+int
+sudoers_io_version(int verbose)
+{
+    struct sudo_conv_message msg;
+    struct sudo_conv_reply repl;
+    char *str;
+
+    easprintf(&str, "Sudoers I/O plugin version %s\n", PACKAGE_VERSION);
+
+    /* Call conversation function */
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_type = SUDO_CONV_INFO_MSG;
+    msg.msg = str;
+    memset(&repl, 0, sizeof(repl));
+    io_conv(1, &msg, &repl);
+
+    return TRUE;
+}
+
+int
+sudoers_io_log_output(const char *buf, unsigned int len)
+{
+    struct timeval now, tv;
+    sigset_t omask;
+
+    gettimeofday(&now, NULL);
+
+    sigprocmask(SIG_BLOCK, &ttyblock, &omask);
+
+#ifdef HAVE_ZLIB
+    if (def_compress_transcript)
+       gzwrite(io_outfile.g, buf, len);
+    else
+#endif
+       fwrite(buf, 1, len, io_outfile.f);
+    timersub(&now, &last_time, &tv);
+#ifdef HAVE_ZLIB
+    if (def_compress_transcript)
+       gzprintf(io_timfile.g, "%f %d\n",
+           tv.tv_sec + ((double)tv.tv_usec / 1000000), len);
+    else
+#endif
+       fprintf(io_timfile.f, "%f %d\n",
+           tv.tv_sec + ((double)tv.tv_usec / 1000000), len);
+    last_time.tv_sec = now.tv_sec;
+    last_time.tv_usec = now.tv_usec;
+
+    sigprocmask(SIG_SETMASK, &omask, NULL);
+
+    return TRUE;
+}
index 099c35d3fce5e1685856e29e826167a6bae83101..d3b2c2b876438451c408654ab0ed4c2a840d85e1 100644 (file)
@@ -60,7 +60,9 @@
 /*
  * Prototypes
  */
+#if 0
 static void runas_setup(void);
+#endif
 static void runas_setgroups(void);
 static void restore_groups(void);
 
index 2e3743ed2081372d89c3ea4443a000c73133a485..1e8f18219a2f231b72b6ecb141ed26e6b3b2b929 100644 (file)
@@ -1300,14 +1300,12 @@ struct policy_plugin sudoers_policy = {
     sudoers_policy_invalidate
 };
 
-#ifdef notyet
 struct io_plugin sudoers_io = {
     SUDO_IO_PLUGIN,
     SUDO_API_VERSION,
-    io_open,
-    io_close,
-    io_version,
-    io_log_input,
-    io_log_output
+    sudoers_io_open,
+    sudoers_io_close,
+    sudoers_io_version,
+    NULL,
+    sudoers_io_log_output
 };
-#endif
index 8a829a19baea7dccdeb93473c88329a6b5738c9b..5a7fc10b900a000f549fa786e67e369180edd0cd 100644 (file)
@@ -297,6 +297,13 @@ YY_DECL;
 /* atobool.c */
 int atobool(const char *str);
 
+/* iolog.c */
+int sudoers_io_open(unsigned int version, sudo_conv_t conversation,
+    char * const settings[], char * const user_info[], char * const user_env[]);
+void sudoers_io_close(int exit_status, int error);
+int sudoers_io_version(int verbose);
+int sudoers_io_log_output(const char *buf, unsigned int len);
+
 /* Only provide extern declarations outside of sudo.c. */
 #ifndef _SUDO_MAIN
 extern struct sudo_user sudo_user;