/*
- * Copyright (c) 2009-2011 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-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
#include "sudoers.h"
-union io_fd {
- FILE *f;
-#ifdef HAVE_ZLIB_H
- gzFile g;
-#endif
- void *v;
-};
-
struct script_buf {
int len; /* buffer length (how much read in) */
int off; /* write position (how much already consumed) */
struct group *runas_gr;
int lines;
int cols;
- int iolog_stdin;
- int iolog_stdout;
- int iolog_stderr;
- int iolog_ttyin;
- int iolog_ttyout;
};
-#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
+union io_fd {
+ FILE *f;
+#ifdef HAVE_ZLIB_H
+ gzFile g;
+#endif
+ void *v;
+};
+
+extern __dso_public struct io_plugin sudoers_io;
+
+struct io_log_file {
+ bool enabled;
+ const char *suffix;
+ int (**fn_ptr)(const char *buf, unsigned int len);
+ union io_fd fd;
+} io_log_files[] = {
+#define IOFD_LOG 0
+ { true, "/log", NULL },
+#define IOFD_TIMING 1
+ { true, "/timing", NULL },
+#define IOFD_STDIN 2
+ { false, "/stdin", &sudoers_io.log_stdin },
+#define IOFD_STDOUT 3
+ { false, "/stdout", &sudoers_io.log_stdout },
+#define IOFD_STDERR 4
+ { false, "/stderr", &sudoers_io.log_stderr },
+#define IOFD_TTYIN 5
+ { false, "/ttyin", &sudoers_io.log_ttyin },
+#define IOFD_TTYOUT 6
+ { false, "/ttyout", &sudoers_io.log_ttyout },
+#define IOFD_MAX 7
+ { false, NULL, NULL }
+};
#define SESSID_MAX 2176782336U
static int iolog_compress;
static struct timeval last_time;
-static union io_fd io_fds[IOFD_MAX];
-extern __dso_public struct io_plugin sudoers_io;
+static unsigned int sessid_max = SESSID_MAX;
/*
* Create path and any parent directories as needed.
debug_return;
}
+/*
+ * Set max session ID (aka sequence number)
+ */
+int
+io_set_max_sessid(const char *maxval)
+{
+ unsigned long ulval;
+ char *ep;
+
+ errno = 0;
+ ulval = strtoul(maxval, &ep, 0);
+ if (*maxval != '\0' && *ep == '\0' &&
+ (errno != ERANGE || ulval != ULONG_MAX)) {
+ sessid_max = MIN((unsigned int)ulval, SESSID_MAX);
+ return true;
+ }
+ return false;
+}
+
/*
* Read the on-disk sequence number, set sessid to the next
* number, and update the on-disk copy.
nread = read(fd2, buf, sizeof(buf));
if (nread > 0) {
id = strtoul(buf, &ep, 36);
- if (buf == ep || id >= SESSID_MAX)
+ if (buf == ep || id >= sessid_max)
id = 0;
}
close(fd2);
if (nread == -1)
log_fatal(USE_ERRNO, N_("unable to read %s"), pathbuf);
id = strtoul(buf, &ep, 36);
- if (buf == ep || id >= SESSID_MAX)
- log_fatal(0, N_("invalid sequence number %s"), pathbuf);
+ if (buf == ep || id >= sessid_max)
+ id = 0;
}
}
id++;
* Append suffix to pathbuf after len chars and open the resulting file.
* Note that the size of pathbuf is assumed to be PATH_MAX.
* Uses zlib if docompress is true.
- * Returns the open file handle which has the close-on-exec flag set.
+ * Stores the open file handle which has the close-on-exec flag set.
*/
-static void *
-open_io_fd(char *pathbuf, size_t len, const char *suffix, bool docompress)
+static void
+open_io_fd(char *pathbuf, size_t len, struct io_log_file *iol, bool docompress)
{
- void *vfd = NULL;
int fd;
debug_decl(open_io_fd, SUDO_DEBUG_UTIL)
pathbuf[len] = '\0';
- strlcat(pathbuf, suffix, PATH_MAX);
- fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
- if (fd != -1) {
- fcntl(fd, F_SETFD, FD_CLOEXEC);
+ strlcat(pathbuf, iol->suffix, PATH_MAX);
+ if (iol->enabled) {
+ fd = open(pathbuf, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (fd != -1) {
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
#ifdef HAVE_ZLIB_H
- if (docompress)
- vfd = gzdopen(fd, "w");
- else
+ if (docompress)
+ iol->fd.g = gzdopen(fd, "w");
+ else
#endif
- vfd = fdopen(fd, "w");
+ iol->fd.f = fdopen(fd, "w");
+ }
+ 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);
+ if (iol->fn_ptr != NULL)
+ *(iol->fn_ptr) = NULL;
}
- debug_return_ptr(vfd);
+ debug_return;
}
/*
* Pull out I/O log related data from user_info and command_info arrays.
+ * Returns true if I/O logging is enabled, else false.
*/
-static void
+static bool
iolog_deserialize_info(struct iolog_details *details, char * const user_info[],
char * const command_info[])
{
}
if (strncmp(*cur, "iolog_stdin=", sizeof("iolog_stdin=") - 1) == 0) {
if (atobool(*cur + sizeof("iolog_stdin=") - 1) == true)
- details->iolog_stdin = true;
+ io_log_files[IOFD_STDIN].enabled = true;
continue;
}
if (strncmp(*cur, "iolog_stdout=", sizeof("iolog_stdout=") - 1) == 0) {
if (atobool(*cur + sizeof("iolog_stdout=") - 1) == true)
- details->iolog_stdout = true;
+ io_log_files[IOFD_STDOUT].enabled = true;
continue;
}
if (strncmp(*cur, "iolog_stderr=", sizeof("iolog_stderr=") - 1) == 0) {
if (atobool(*cur + sizeof("iolog_stderr=") - 1) == true)
- details->iolog_stderr = true;
+ io_log_files[IOFD_STDERR].enabled = true;
continue;
}
if (strncmp(*cur, "iolog_ttyin=", sizeof("iolog_ttyin=") - 1) == 0) {
if (atobool(*cur + sizeof("iolog_ttyin=") - 1) == true)
- details->iolog_ttyin = true;
+ io_log_files[IOFD_TTYIN].enabled = true;
continue;
}
if (strncmp(*cur, "iolog_ttyout=", sizeof("iolog_ttyout=") - 1) == 0) {
if (atobool(*cur + sizeof("iolog_ttyout=") - 1) == true)
- details->iolog_ttyout = true;
+ io_log_files[IOFD_TTYOUT].enabled = true;
continue;
}
if (strncmp(*cur, "iolog_compress=", sizeof("iolog_compress=") - 1) == 0) {
continue;
}
break;
+ case 'm':
+ if (strncmp(*cur, "maxseq=", sizeof("maxseq=") - 1) == 0)
+ io_set_max_sessid(*cur + sizeof("maxseq=") - 1);
+ break;
case 'r':
if (strncmp(*cur, "runas_gid=", sizeof("runas_gid=") - 1) == 0) {
runas_gid_str = *cur + sizeof("runas_gid=") - 1;
details->runas_gr = sudo_fakegrnam(id);
}
}
- debug_return;
+ debug_return_bool(
+ io_log_files[IOFD_STDIN].enabled || io_log_files[IOFD_STDOUT].enabled ||
+ io_log_files[IOFD_STDERR].enabled || io_log_files[IOFD_TTYIN].enabled ||
+ io_log_files[IOFD_TTYOUT].enabled);
}
static int
char *tofree = NULL;
char * const *cur;
const char *debug_flags = NULL;
- FILE *io_logfile;
size_t len;
- int rval = -1;
+ int i, rval = -1;
debug_decl(sudoers_io_open, SUDO_DEBUG_PLUGIN)
sudo_conv = conversation;
sudo_debug_init(NULL, debug_flags);
/*
- * Pull iolog settings out of command_info, if any.
+ * Pull iolog settings out of command_info.
*/
- iolog_deserialize_info(&details, user_info, command_info);
- /* Did policy module disable I/O logging? */
- if (!details.iolog_stdin && !details.iolog_ttyin &&
- !details.iolog_stdout && !details.iolog_stderr &&
- !details.iolog_ttyout) {
+ if (!iolog_deserialize_info(&details, user_info, command_info)) {
rval = false;
goto done;
}
/*
* We create 7 files: a log file, a timing file and 5 for input/output.
*/
- io_logfile = open_io_fd(pathbuf, len, "/log", false);
- if (io_logfile == NULL)
- log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
-
- io_fds[IOFD_TIMING].v = open_io_fd(pathbuf, len, "/timing",
- iolog_compress);
- if (io_fds[IOFD_TIMING].v == NULL)
- log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
-
- if (details.iolog_ttyin) {
- io_fds[IOFD_TTYIN].v = open_io_fd(pathbuf, len, "/ttyin",
- iolog_compress);
- if (io_fds[IOFD_TTYIN].v == NULL)
- log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
- } else {
- sudoers_io.log_ttyin = NULL;
- }
- if (details.iolog_stdin) {
- io_fds[IOFD_STDIN].v = open_io_fd(pathbuf, len, "/stdin",
- iolog_compress);
- if (io_fds[IOFD_STDIN].v == NULL)
- log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
- } else {
- sudoers_io.log_stdin = NULL;
- }
- if (details.iolog_ttyout) {
- io_fds[IOFD_TTYOUT].v = open_io_fd(pathbuf, len, "/ttyout",
- iolog_compress);
- if (io_fds[IOFD_TTYOUT].v == NULL)
- log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
- } else {
- sudoers_io.log_ttyout = NULL;
- }
- if (details.iolog_stdout) {
- io_fds[IOFD_STDOUT].v = open_io_fd(pathbuf, len, "/stdout",
- iolog_compress);
- if (io_fds[IOFD_STDOUT].v == NULL)
- log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
- } else {
- sudoers_io.log_stdout = NULL;
- }
- if (details.iolog_stderr) {
- io_fds[IOFD_STDERR].v = open_io_fd(pathbuf, len, "/stderr",
- iolog_compress);
- if (io_fds[IOFD_STDERR].v == NULL)
- log_fatal(USE_ERRNO, N_("unable to create %s"), pathbuf);
- } else {
- sudoers_io.log_stderr = NULL;
+ for (i = 0; i < IOFD_MAX; i++) {
+ open_io_fd(pathbuf, len, &io_log_files[i], i ? iolog_compress : false);
}
- gettimeofday(&last_time, NULL);
-
- fprintf(io_logfile, "%ld:%s:%s:%s:%s:%d:%d\n", (long)last_time.tv_sec,
+ fprintf(io_log_files[IOFD_LOG].fd.f, "%ld:%s:%s:%s:%s:%d:%d\n%s\n%s",
+ (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);
- fputs(details.cwd ? details.cwd : "unknown", io_logfile);
- fputc('\n', io_logfile);
- fputs(details.command ? details.command : "unknown", io_logfile);
+ 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_logfile);
- fputs(*cur, io_logfile);
+ fputc(' ', io_log_files[IOFD_LOG].fd.f);
+ fputs(*cur, io_log_files[IOFD_LOG].fd.f);
}
- fputc('\n', io_logfile);
- fclose(io_logfile);
+ 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;
rval = true;
}
for (i = 0; i < IOFD_MAX; i++) {
- if (io_fds[i].v == NULL)
+ if (io_log_files[i].fd.v == NULL)
continue;
#ifdef HAVE_ZLIB_H
if (iolog_compress)
- gzclose(io_fds[i].g);
+ gzclose(io_log_files[i].fd.g);
else
#endif
- fclose(io_fds[i].f);
+ fclose(io_log_files[i].fd.f);
}
debug_return;
}
#ifdef HAVE_ZLIB_H
if (iolog_compress)
- ignore_result(gzwrite(io_fds[idx].g, (const voidp)buf, len));
+ ignore_result(gzwrite(io_log_files[idx].fd.g, (const voidp)buf, len));
else
#endif
- ignore_result(fwrite(buf, 1, len, io_fds[idx].f));
+ ignore_result(fwrite(buf, 1, len, io_log_files[idx].fd.f));
delay.tv_sec = now.tv_sec;
delay.tv_usec = now.tv_usec;
timevalsub(&delay, &last_time);
#ifdef HAVE_ZLIB_H
if (iolog_compress)
- gzprintf(io_fds[IOFD_TIMING].g, "%d %f %d\n", idx,
+ gzprintf(io_log_files[IOFD_TIMING].fd.g, "%d %f %d\n", idx,
delay.tv_sec + ((double)delay.tv_usec / 1000000), len);
else
#endif
- fprintf(io_fds[IOFD_TIMING].f, "%d %f %d\n", idx,
+ fprintf(io_log_files[IOFD_TIMING].fd.f, "%d %f %d\n", idx,
delay.tv_sec + ((double)delay.tv_usec / 1000000), len);
last_time.tv_sec = now.tv_sec;
last_time.tv_usec = now.tv_usec;