File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers / iolog.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:26:49 2012 UTC (12 years, 1 month ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_5p1, HEAD
sudo 1.8.5p1

    1: /*
    2:  * Copyright (c) 2009-2011 Todd C. Miller <Todd.Miller@courtesan.com>
    3:  *
    4:  * Permission to use, copy, modify, and distribute this software for any
    5:  * purpose with or without fee is hereby granted, provided that the above
    6:  * copyright notice and this permission notice appear in all copies.
    7:  *
    8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   15:  */
   16: 
   17: #include <config.h>
   18: 
   19: #include <sys/types.h>
   20: #include <sys/param.h>
   21: #include <sys/stat.h>
   22: #include <sys/time.h>
   23: #include <stdio.h>
   24: #ifdef STDC_HEADERS
   25: # include <stdlib.h>
   26: # include <stddef.h>
   27: #else
   28: # ifdef HAVE_STDLIB_H
   29: #  include <stdlib.h>
   30: # endif
   31: #endif /* STDC_HEADERS */
   32: #ifdef HAVE_STRING_H
   33: # include <string.h>
   34: #endif /* HAVE_STRING_H */
   35: #ifdef HAVE_STRINGS_H
   36: # include <strings.h>
   37: #endif /* HAVE_STRINGS_H */
   38: #ifdef HAVE_UNISTD_H
   39: # include <unistd.h>
   40: #endif /* HAVE_UNISTD_H */
   41: #if TIME_WITH_SYS_TIME
   42: # include <time.h>
   43: #endif
   44: #include <errno.h>
   45: #include <fcntl.h>
   46: #include <signal.h>
   47: #include <setjmp.h>
   48: #include <pwd.h>
   49: #include <grp.h>
   50: #ifdef HAVE_ZLIB_H
   51: # include <zlib.h>
   52: #endif
   53: 
   54: #include "sudoers.h"
   55: 
   56: /* plugin_error.c */
   57: extern sigjmp_buf error_jmp;
   58: 
   59: union io_fd {
   60:     FILE *f;
   61: #ifdef HAVE_ZLIB_H
   62:     gzFile g;
   63: #endif
   64:     void *v;
   65: };
   66: 
   67: struct script_buf {
   68:     int len; /* buffer length (how much read in) */
   69:     int off; /* write position (how much already consumed) */
   70:     char buf[16 * 1024];
   71: };
   72: 
   73: /* XXX - separate sudoers.h and iolog.h? */
   74: #undef runas_pw
   75: #undef runas_gr
   76: 
   77: struct iolog_details {
   78:     const char *cwd;
   79:     const char *tty;
   80:     const char *user;
   81:     const char *command;
   82:     const char *iolog_path;
   83:     struct passwd *runas_pw;
   84:     struct group *runas_gr;
   85:     int iolog_stdin;
   86:     int iolog_stdout;
   87:     int iolog_stderr;
   88:     int iolog_ttyin;
   89:     int iolog_ttyout;
   90: };
   91: 
   92: #define IOFD_STDIN	0
   93: #define IOFD_STDOUT	1
   94: #define IOFD_STDERR	2
   95: #define IOFD_TTYIN	3
   96: #define IOFD_TTYOUT	4
   97: #define IOFD_TIMING	5
   98: #define IOFD_MAX	6
   99: 
  100: #define SESSID_MAX	2176782336U
  101: 
  102: static int iolog_compress;
  103: static struct timeval last_time;
  104: static union io_fd io_fds[IOFD_MAX];
  105: extern struct io_plugin sudoers_io;
  106: 
  107: /*
  108:  * Create parent directories for path as needed, but not path itself.
  109:  */
  110: static void
  111: mkdir_parents(char *path)
  112: {
  113:     struct stat sb;
  114:     char *slash = path;
  115:     debug_decl(mkdir_parents, SUDO_DEBUG_UTIL)
  116: 
  117:     for (;;) {
  118: 	if ((slash = strchr(slash + 1, '/')) == NULL)
  119: 	    break;
  120: 	*slash = '\0';
  121: 	if (stat(path, &sb) != 0) {
  122: 	    if (mkdir(path, S_IRWXU) != 0)
  123: 		log_fatal(USE_ERRNO, _("unable to mkdir %s"), path);
  124: 	} else if (!S_ISDIR(sb.st_mode)) {
  125: 	    log_fatal(0, _("%s: %s"), path, strerror(ENOTDIR));
  126: 	}
  127: 	*slash = '/';
  128:     }
  129:     debug_return;
  130: }
  131: 
  132: /*
  133:  * Read the on-disk sequence number, set sessid to the next
  134:  * number, and update the on-disk copy.
  135:  * Uses file locking to avoid sequence number collisions.
  136:  */
  137: void
  138: io_nextid(char *iolog_dir, char sessid[7])
  139: {
  140:     struct stat sb;
  141:     char buf[32], *ep;
  142:     int fd, i;
  143:     unsigned long id = 0;
  144:     int len;
  145:     ssize_t nread;
  146:     char pathbuf[PATH_MAX];
  147:     static const char b36char[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  148:     debug_decl(io_nextid, SUDO_DEBUG_UTIL)
  149: 
  150:     /*
  151:      * Create I/O log directory if it doesn't already exist.
  152:      */
  153:     mkdir_parents(iolog_dir);
  154:     if (stat(iolog_dir, &sb) != 0) {
  155: 	if (mkdir(iolog_dir, S_IRWXU) != 0)
  156: 	    log_fatal(USE_ERRNO, _("unable to mkdir %s"), iolog_dir);
  157:     } else if (!S_ISDIR(sb.st_mode)) {
  158: 	log_fatal(0, _("%s exists but is not a directory (0%o)"),
  159: 	    iolog_dir, (unsigned int) sb.st_mode);
  160:     }
  161: 
  162:     /*
  163:      * Open sequence file
  164:      */
  165:     len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", iolog_dir);
  166:     if (len <= 0 || len >= sizeof(pathbuf)) {
  167: 	errno = ENAMETOOLONG;
  168: 	log_fatal(USE_ERRNO, "%s/seq", pathbuf);
  169:     }
  170:     fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
  171:     if (fd == -1)
  172: 	log_fatal(USE_ERRNO, _("unable to open %s"), pathbuf);
  173:     lock_file(fd, SUDO_LOCK);
  174: 
  175:     /* Read seq number (base 36). */
  176:     nread = read(fd, buf, sizeof(buf));
  177:     if (nread != 0) {
  178: 	if (nread == -1)
  179: 	    log_fatal(USE_ERRNO, _("unable to read %s"), pathbuf);
  180: 	id = strtoul(buf, &ep, 36);
  181: 	if (buf == ep || id >= SESSID_MAX)
  182: 	    log_fatal(0, _("invalid sequence number %s"), pathbuf);
  183:     }
  184:     id++;
  185: 
  186:     /*
  187:      * Convert id to a string and stash in sessid.
  188:      * Note that that least significant digits go at the end of the string.
  189:      */
  190:     for (i = 5; i >= 0; i--) {
  191: 	buf[i] = b36char[id % 36];
  192: 	id /= 36;
  193:     }
  194:     buf[6] = '\n';
  195: 
  196:     /* Stash id for logging purposes. */
  197:     memcpy(sessid, buf, 6);
  198:     sessid[6] = '\0';
  199: 
  200:     /* Rewind and overwrite old seq file. */
  201:     if (lseek(fd, 0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7)
  202: 	log_fatal(USE_ERRNO, _("unable to write to %s"), pathbuf);
  203:     close(fd);
  204: 
  205:     debug_return;
  206: }
  207: 
  208: /*
  209:  * Copy iolog_path to pathbuf and create the directory and any intermediate
  210:  * directories.  If iolog_path ends in 'XXXXXX', use mkdtemp().
  211:  */
  212: static size_t
  213: mkdir_iopath(const char *iolog_path, char *pathbuf, size_t pathsize)
  214: {
  215:     size_t len;
  216:     debug_decl(mkdir_iopath, SUDO_DEBUG_UTIL)
  217: 
  218:     len = strlcpy(pathbuf, iolog_path, pathsize);
  219:     if (len >= pathsize) {
  220: 	errno = ENAMETOOLONG;
  221: 	log_fatal(USE_ERRNO, "%s", iolog_path);
  222:     }
  223: 
  224:     /*
  225:      * Create path and intermediate subdirs as needed.
  226:      * If path ends in at least 6 Xs (ala POSIX mktemp), use mkdtemp().
  227:      */
  228:     mkdir_parents(pathbuf);
  229:     if (len >= 6 && strcmp(&pathbuf[len - 6], "XXXXXX") == 0) {
  230: 	if (mkdtemp(pathbuf) == NULL)
  231: 	    log_fatal(USE_ERRNO, _("unable to create %s"), pathbuf);
  232:     } else {
  233: 	if (mkdir(pathbuf, S_IRWXU) != 0)
  234: 	    log_fatal(USE_ERRNO, _("unable to create %s"), pathbuf);
  235:     }
  236: 
  237:     debug_return_size_t(len);
  238: }
  239: 
  240: /*
  241:  * Append suffix to pathbuf after len chars and open the resulting file.
  242:  * Note that the size of pathbuf is assumed to be PATH_MAX.
  243:  * Uses zlib if docompress is true.
  244:  * Returns the open file handle which has the close-on-exec flag set.
  245:  */
  246: static void *
  247: open_io_fd(char *pathbuf, size_t len, const char *suffix, bool docompress)
  248: {
  249:     void *vfd = NULL;
  250:     int fd;
  251:     debug_decl(open_io_fd, SUDO_DEBUG_UTIL)
  252: 
  253:     pathbuf[len] = '\0';
  254:     strlcat(pathbuf, suffix, PATH_MAX);
  255:     fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
  256:     if (fd != -1) {
  257: 	fcntl(fd, F_SETFD, FD_CLOEXEC);
  258: #ifdef HAVE_ZLIB_H
  259: 	if (docompress)
  260: 	    vfd = gzdopen(fd, "w");
  261: 	else
  262: #endif
  263: 	    vfd = fdopen(fd, "w");
  264:     }
  265:     debug_return_ptr(vfd);
  266: }
  267: 
  268: /*
  269:  * Pull out I/O log related data from user_info and command_info arrays.
  270:  */
  271: static void
  272: iolog_deserialize_info(struct iolog_details *details, char * const user_info[],
  273:     char * const command_info[])
  274: {
  275:     const char *runas_uid_str = "0", *runas_euid_str = NULL;
  276:     const char *runas_gid_str = "0", *runas_egid_str = NULL;
  277:     char id[MAX_UID_T_LEN + 2], *ep;
  278:     char * const *cur;
  279:     unsigned long ulval;
  280:     uid_t runas_uid = 0;
  281:     gid_t runas_gid = 0;
  282:     debug_decl(iolog_deserialize_info, SUDO_DEBUG_UTIL)
  283: 
  284:     memset(details, 0, sizeof(*details));
  285: 
  286:     for (cur = user_info; *cur != NULL; cur++) {
  287: 	switch (**cur) {
  288: 	case 'c':
  289: 	    if (strncmp(*cur, "cwd=", sizeof("cwd=") - 1) == 0) {
  290: 		details->cwd = *cur + sizeof("cwd=") - 1;
  291: 		continue;
  292: 	    }
  293: 	    break;
  294: 	case 't':
  295: 	    if (strncmp(*cur, "tty=", sizeof("tty=") - 1) == 0) {
  296: 		details->tty = *cur + sizeof("tty=") - 1;
  297: 		continue;
  298: 	    }
  299: 	    break;
  300: 	case 'u':
  301: 	    if (strncmp(*cur, "user=", sizeof("user=") - 1) == 0) {
  302: 		details->user = *cur + sizeof("user=") - 1;
  303: 		continue;
  304: 	    }
  305: 	    break;
  306: 	}
  307:     }
  308: 
  309:     for (cur = command_info; *cur != NULL; cur++) {
  310: 	switch (**cur) {
  311: 	case 'c':
  312: 	    if (strncmp(*cur, "command=", sizeof("command=") - 1) == 0) {
  313: 		details->command = *cur + sizeof("command=") - 1;
  314: 		continue;
  315: 	    }
  316: 	    break;
  317: 	case 'i':
  318: 	    if (strncmp(*cur, "iolog_path=", sizeof("iolog_path=") - 1) == 0) {
  319: 		details->iolog_path = *cur + sizeof("iolog_path=") - 1;
  320: 		continue;
  321: 	    }
  322: 	    if (strncmp(*cur, "iolog_stdin=", sizeof("iolog_stdin=") - 1) == 0) {
  323: 		if (atobool(*cur + sizeof("iolog_stdin=") - 1) == true)
  324: 		    details->iolog_stdin = true;
  325: 		continue;
  326: 	    }
  327: 	    if (strncmp(*cur, "iolog_stdout=", sizeof("iolog_stdout=") - 1) == 0) {
  328: 		if (atobool(*cur + sizeof("iolog_stdout=") - 1) == true)
  329: 		    details->iolog_stdout = true;
  330: 		continue;
  331: 	    }
  332: 	    if (strncmp(*cur, "iolog_stderr=", sizeof("iolog_stderr=") - 1) == 0) {
  333: 		if (atobool(*cur + sizeof("iolog_stderr=") - 1) == true)
  334: 		    details->iolog_stderr = true;
  335: 		continue;
  336: 	    }
  337: 	    if (strncmp(*cur, "iolog_ttyin=", sizeof("iolog_ttyin=") - 1) == 0) {
  338: 		if (atobool(*cur + sizeof("iolog_ttyin=") - 1) == true)
  339: 		    details->iolog_ttyin = true;
  340: 		continue;
  341: 	    }
  342: 	    if (strncmp(*cur, "iolog_ttyout=", sizeof("iolog_ttyout=") - 1) == 0) {
  343: 		if (atobool(*cur + sizeof("iolog_ttyout=") - 1) == true)
  344: 		    details->iolog_ttyout = true;
  345: 		continue;
  346: 	    }
  347: 	    if (strncmp(*cur, "iolog_compress=", sizeof("iolog_compress=") - 1) == 0) {
  348: 		if (atobool(*cur + sizeof("iolog_compress=") - 1) == true)
  349: 		    iolog_compress = true; /* must be global */
  350: 		continue;
  351: 	    }
  352: 	    break;
  353: 	case 'r':
  354: 	    if (strncmp(*cur, "runas_gid=", sizeof("runas_gid=") - 1) == 0) {
  355: 		runas_gid_str = *cur + sizeof("runas_gid=") - 1;
  356: 		continue;
  357: 	    }
  358: 	    if (strncmp(*cur, "runas_egid=", sizeof("runas_egid=") - 1) == 0) {
  359: 		runas_egid_str = *cur + sizeof("runas_egid=") - 1;
  360: 		continue;
  361: 	    }
  362: 	    if (strncmp(*cur, "runas_uid=", sizeof("runas_uid=") - 1) == 0) {
  363: 		runas_uid_str = *cur + sizeof("runas_uid=") - 1;
  364: 		continue;
  365: 	    }
  366: 	    if (strncmp(*cur, "runas_euid=", sizeof("runas_euid=") - 1) == 0) {
  367: 		runas_euid_str = *cur + sizeof("runas_euid=") - 1;
  368: 		continue;
  369: 	    }
  370: 	    break;
  371: 	}
  372:     }
  373: 
  374:     /*
  375:      * Lookup runas user and group, preferring effective over real uid/gid.
  376:      */
  377:     if (runas_euid_str != NULL)
  378: 	runas_uid_str = runas_euid_str;
  379:     if (runas_uid_str != NULL) {
  380: 	errno = 0;
  381: 	ulval = strtoul(runas_uid_str, &ep, 0);
  382: 	if (*runas_uid_str != '\0' && *ep == '\0' &&
  383: 	    (errno != ERANGE || ulval != ULONG_MAX)) {
  384: 	    runas_uid = (uid_t)ulval;
  385: 	}
  386:     }
  387:     if (runas_egid_str != NULL)
  388: 	runas_gid_str = runas_egid_str;
  389:     if (runas_gid_str != NULL) {
  390: 	errno = 0;
  391: 	ulval = strtoul(runas_gid_str, &ep, 0);
  392: 	if (*runas_gid_str != '\0' && *ep == '\0' &&
  393: 	    (errno != ERANGE || ulval != ULONG_MAX)) {
  394: 	    runas_gid = (gid_t)ulval;
  395: 	}
  396:     }
  397: 
  398:     details->runas_pw = sudo_getpwuid(runas_uid);
  399:     if (details->runas_pw == NULL) {
  400: 	id[0] = '#';
  401: 	strlcpy(&id[1], runas_uid_str, sizeof(id) - 1);
  402: 	details->runas_pw = sudo_fakepwnam(id, runas_gid);
  403:     }
  404: 
  405:     if (runas_gid != details->runas_pw->pw_gid) {
  406: 	details->runas_gr = sudo_getgrgid(runas_gid);
  407: 	if (details->runas_gr == NULL) {
  408: 	    id[0] = '#';
  409: 	    strlcpy(&id[1], runas_gid_str, sizeof(id) - 1);
  410: 	    details->runas_gr = sudo_fakegrnam(id);
  411: 	}
  412:     }
  413:     debug_return;
  414: }
  415: 
  416: static int
  417: sudoers_io_open(unsigned int version, sudo_conv_t conversation,
  418:     sudo_printf_t plugin_printf, char * const settings[],
  419:     char * const user_info[], char * const command_info[],
  420:     int argc, char * const argv[], char * const user_env[], char * const args[])
  421: {
  422:     struct iolog_details details;
  423:     char pathbuf[PATH_MAX], sessid[7];
  424:     char *tofree = NULL;
  425:     char * const *cur;
  426:     const char *debug_flags = NULL;
  427:     FILE *io_logfile;
  428:     size_t len;
  429:     int rval = -1;
  430:     debug_decl(sudoers_io_open, SUDO_DEBUG_PLUGIN)
  431: 
  432:     if (!sudo_conv)
  433: 	sudo_conv = conversation;
  434:     if (!sudo_printf)
  435: 	sudo_printf = plugin_printf;
  436: 
  437:     /* If we have no command (because -V was specified) just return. */
  438:     if (argc == 0)
  439: 	debug_return_bool(true);
  440: 
  441:     if (sigsetjmp(error_jmp, 1)) {
  442: 	/* called via error(), errorx() or log_fatal() */
  443: 	rval = -1;
  444: 	goto done;
  445:     }
  446: 
  447:     bindtextdomain("sudoers", LOCALEDIR);
  448: 
  449:     sudo_setpwent();
  450:     sudo_setgrent();
  451: 
  452:     /*
  453:      * Check for debug flags in settings list.
  454:      */
  455:     for (cur = settings; *cur != NULL; cur++) {
  456: 	if (strncmp(*cur, "debug_flags=", sizeof("debug_flags=") - 1) == 0)
  457: 	    debug_flags = *cur + sizeof("debug_flags=") - 1;
  458:     }
  459:     if (debug_flags != NULL)
  460: 	sudo_debug_init(NULL, debug_flags);
  461: 
  462:     /*
  463:      * Pull iolog settings out of command_info, if any.
  464:      */
  465:     iolog_deserialize_info(&details, user_info, command_info);
  466:     /* Did policy module disable I/O logging? */
  467:     if (!details.iolog_stdin && !details.iolog_ttyin &&
  468: 	!details.iolog_stdout && !details.iolog_stderr &&
  469: 	!details.iolog_ttyout) {
  470: 	rval = false;
  471: 	goto done;
  472:     }
  473: 
  474:     /* If no I/O log path defined we need to figure it out ourselves. */
  475:     if (details.iolog_path == NULL) {
  476: 	/* Get next session ID and convert it into a path. */
  477: 	tofree = emalloc(sizeof(_PATH_SUDO_IO_LOGDIR) + sizeof(sessid) + 2);
  478: 	memcpy(tofree, _PATH_SUDO_IO_LOGDIR, sizeof(_PATH_SUDO_IO_LOGDIR));
  479: 	io_nextid(tofree, sessid);
  480: 	snprintf(tofree + sizeof(_PATH_SUDO_IO_LOGDIR), sizeof(sessid) + 2,
  481: 	    "%c%c/%c%c/%c%c", sessid[0], sessid[1], sessid[2], sessid[3],
  482: 	    sessid[4], sessid[5]);
  483: 	details.iolog_path = tofree;
  484:     }
  485: 
  486:     /*
  487:      * Make local copy of I/O log path and create it, along with any
  488:      * intermediate subdirs.  Calls mkdtemp() if iolog_path ends in XXXXXX.
  489:      */
  490:     len = mkdir_iopath(details.iolog_path, pathbuf, sizeof(pathbuf));
  491:     if (len >= sizeof(pathbuf))
  492: 	goto done;
  493: 
  494:     /*
  495:      * We create 7 files: a log file, a timing file and 5 for input/output.
  496:      */
  497:     io_logfile = open_io_fd(pathbuf, len, "/log", false);
  498:     if (io_logfile == NULL)
  499: 	log_fatal(USE_ERRNO, _("unable to create %s"), pathbuf);
  500: 
  501:     io_fds[IOFD_TIMING].v = open_io_fd(pathbuf, len, "/timing",
  502: 	iolog_compress);
  503:     if (io_fds[IOFD_TIMING].v == NULL)
  504: 	log_fatal(USE_ERRNO, _("unable to create %s"), pathbuf);
  505: 
  506:     if (details.iolog_ttyin) {
  507: 	io_fds[IOFD_TTYIN].v = open_io_fd(pathbuf, len, "/ttyin",
  508: 	    iolog_compress);
  509: 	if (io_fds[IOFD_TTYIN].v == NULL)
  510: 	    log_fatal(USE_ERRNO, _("unable to create %s"), pathbuf);
  511:     } else {
  512: 	sudoers_io.log_ttyin = NULL;
  513:     }
  514:     if (details.iolog_stdin) {
  515: 	io_fds[IOFD_STDIN].v = open_io_fd(pathbuf, len, "/stdin",
  516: 	    iolog_compress);
  517: 	if (io_fds[IOFD_STDIN].v == NULL)
  518: 	    log_fatal(USE_ERRNO, _("unable to create %s"), pathbuf);
  519:     } else {
  520: 	sudoers_io.log_stdin = NULL;
  521:     }
  522:     if (details.iolog_ttyout) {
  523: 	io_fds[IOFD_TTYOUT].v = open_io_fd(pathbuf, len, "/ttyout",
  524: 	    iolog_compress);
  525: 	if (io_fds[IOFD_TTYOUT].v == NULL)
  526: 	    log_fatal(USE_ERRNO, _("unable to create %s"), pathbuf);
  527:     } else {
  528: 	sudoers_io.log_ttyout = NULL;
  529:     }
  530:     if (details.iolog_stdout) {
  531: 	io_fds[IOFD_STDOUT].v = open_io_fd(pathbuf, len, "/stdout",
  532: 	    iolog_compress);
  533: 	if (io_fds[IOFD_STDOUT].v == NULL)
  534: 	    log_fatal(USE_ERRNO, _("unable to create %s"), pathbuf);
  535:     } else {
  536: 	sudoers_io.log_stdout = NULL;
  537:     }
  538:     if (details.iolog_stderr) {
  539: 	io_fds[IOFD_STDERR].v = open_io_fd(pathbuf, len, "/stderr",
  540: 	    iolog_compress);
  541: 	if (io_fds[IOFD_STDERR].v == NULL)
  542: 	    log_fatal(USE_ERRNO, _("unable to create %s"), pathbuf);
  543:     } else {
  544: 	sudoers_io.log_stderr = NULL;
  545:     }
  546: 
  547:     gettimeofday(&last_time, NULL);
  548: 
  549:     fprintf(io_logfile, "%ld:%s:%s:%s:%s\n", (long)last_time.tv_sec,
  550: 	details.user ? details.user : "unknown", details.runas_pw->pw_name,
  551: 	details.runas_gr ? details.runas_gr->gr_name : "",
  552: 	details.tty ? details.tty : "unknown");
  553:     fputs(details.cwd ? details.cwd : "unknown", io_logfile);
  554:     fputc('\n', io_logfile);
  555:     fputs(details.command ? details.command : "unknown", io_logfile);
  556:     for (cur = &argv[1]; *cur != NULL; cur++) {
  557: 	fputc(' ', io_logfile);
  558: 	fputs(*cur, io_logfile);
  559:     }
  560:     fputc('\n', io_logfile);
  561:     fclose(io_logfile);
  562: 
  563:     rval = true;
  564: 
  565: done:
  566:     efree(tofree);
  567:     if (details.runas_pw)
  568: 	pw_delref(details.runas_pw);
  569:     sudo_endpwent();
  570:     if (details.runas_gr)
  571: 	gr_delref(details.runas_gr);
  572:     sudo_endgrent();
  573: 
  574:     debug_return_bool(rval);
  575: }
  576: 
  577: static void
  578: sudoers_io_close(int exit_status, int error)
  579: {
  580:     int i;
  581:     debug_decl(sudoers_io_close, SUDO_DEBUG_PLUGIN)
  582: 
  583:     if (sigsetjmp(error_jmp, 1)) {
  584: 	/* called via error(), errorx() or log_fatal() */
  585: 	debug_return;
  586:     }
  587: 
  588:     for (i = 0; i < IOFD_MAX; i++) {
  589: 	if (io_fds[i].v == NULL)
  590: 	    continue;
  591: #ifdef HAVE_ZLIB_H
  592: 	if (iolog_compress)
  593: 	    gzclose(io_fds[i].g);
  594: 	else
  595: #endif
  596: 	    fclose(io_fds[i].f);
  597:     }
  598:     debug_return;
  599: }
  600: 
  601: static int
  602: sudoers_io_version(int verbose)
  603: {
  604:     debug_decl(sudoers_io_version, SUDO_DEBUG_PLUGIN)
  605: 
  606:     if (sigsetjmp(error_jmp, 1)) {
  607: 	/* called via error(), errorx() or log_fatal() */
  608: 	debug_return_bool(-1);
  609:     }
  610: 
  611:     sudo_printf(SUDO_CONV_INFO_MSG, "Sudoers I/O plugin version %s\n",
  612: 	PACKAGE_VERSION);
  613: 
  614:     debug_return_bool(true);
  615: }
  616: 
  617: /*
  618:  * Generic I/O logging function.  Called by the I/O logging entry points.
  619:  */
  620: static int
  621: sudoers_io_log(const char *buf, unsigned int len, int idx)
  622: {
  623:     struct timeval now, delay;
  624:     debug_decl(sudoers_io_version, SUDO_DEBUG_PLUGIN)
  625: 
  626:     gettimeofday(&now, NULL);
  627: 
  628:     if (sigsetjmp(error_jmp, 1)) {
  629: 	/* called via error(), errorx() or log_fatal() */
  630: 	debug_return_bool(-1);
  631:     }
  632: 
  633: #ifdef HAVE_ZLIB_H
  634:     if (iolog_compress)
  635: 	ignore_result(gzwrite(io_fds[idx].g, (const voidp)buf, len));
  636:     else
  637: #endif
  638: 	ignore_result(fwrite(buf, 1, len, io_fds[idx].f));
  639:     delay.tv_sec = now.tv_sec;
  640:     delay.tv_usec = now.tv_usec;
  641:     timevalsub(&delay, &last_time);
  642: #ifdef HAVE_ZLIB_H
  643:     if (iolog_compress)
  644: 	gzprintf(io_fds[IOFD_TIMING].g, "%d %f %d\n", idx,
  645: 	    delay.tv_sec + ((double)delay.tv_usec / 1000000), len);
  646:     else
  647: #endif
  648: 	fprintf(io_fds[IOFD_TIMING].f, "%d %f %d\n", idx,
  649: 	    delay.tv_sec + ((double)delay.tv_usec / 1000000), len);
  650:     last_time.tv_sec = now.tv_sec;
  651:     last_time.tv_usec = now.tv_usec;
  652: 
  653:     debug_return_bool(true);
  654: }
  655: 
  656: static int
  657: sudoers_io_log_ttyin(const char *buf, unsigned int len)
  658: {
  659:     return sudoers_io_log(buf, len, IOFD_TTYIN);
  660: }
  661: 
  662: static int
  663: sudoers_io_log_ttyout(const char *buf, unsigned int len)
  664: {
  665:     return sudoers_io_log(buf, len, IOFD_TTYOUT);
  666: }
  667: 
  668: static int
  669: sudoers_io_log_stdin(const char *buf, unsigned int len)
  670: {
  671:     return sudoers_io_log(buf, len, IOFD_STDIN);
  672: }
  673: 
  674: static int
  675: sudoers_io_log_stdout(const char *buf, unsigned int len)
  676: {
  677:     return sudoers_io_log(buf, len, IOFD_STDOUT);
  678: }
  679: 
  680: static int
  681: sudoers_io_log_stderr(const char *buf, unsigned int len)
  682: {
  683:     return sudoers_io_log(buf, len, IOFD_STDERR);
  684: }
  685: 
  686: struct io_plugin sudoers_io = {
  687:     SUDO_IO_PLUGIN,
  688:     SUDO_API_VERSION,
  689:     sudoers_io_open,
  690:     sudoers_io_close,
  691:     sudoers_io_version,
  692:     sudoers_io_log_ttyin,
  693:     sudoers_io_log_ttyout,
  694:     sudoers_io_log_stdin,
  695:     sudoers_io_log_stdout,
  696:     sudoers_io_log_stderr
  697: };

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>