Annotation of embedaddon/sudo/plugins/sudoers/iolog.c, revision 1.1

1.1     ! misho       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: 
        !           116:     for (;;) {
        !           117:        if ((slash = strchr(slash + 1, '/')) == NULL)
        !           118:            break;
        !           119:        *slash = '\0';
        !           120:        if (stat(path, &sb) != 0) {
        !           121:            if (mkdir(path, S_IRWXU) != 0)
        !           122:                log_error(USE_ERRNO, _("unable to mkdir %s"), path);
        !           123:        } else if (!S_ISDIR(sb.st_mode)) {
        !           124:            log_error(0, _("%s: %s"), path, strerror(ENOTDIR));
        !           125:        }
        !           126:        *slash = '/';
        !           127:     }
        !           128: }
        !           129: 
        !           130: /*
        !           131:  * Read the on-disk sequence number, set sessid to the next
        !           132:  * number, and update the on-disk copy.
        !           133:  * Uses file locking to avoid sequence number collisions.
        !           134:  */
        !           135: void
        !           136: io_nextid(char *iolog_dir, char sessid[7])
        !           137: {
        !           138:     struct stat sb;
        !           139:     char buf[32], *ep;
        !           140:     int fd, i;
        !           141:     unsigned long id = 0;
        !           142:     int len;
        !           143:     ssize_t nread;
        !           144:     char pathbuf[PATH_MAX];
        !           145:     static const char b36char[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        !           146: 
        !           147:     /*
        !           148:      * Create I/O log directory if it doesn't already exist.
        !           149:      */
        !           150:     mkdir_parents(iolog_dir);
        !           151:     if (stat(iolog_dir, &sb) != 0) {
        !           152:        if (mkdir(iolog_dir, S_IRWXU) != 0)
        !           153:            log_error(USE_ERRNO, _("unable to mkdir %s"), iolog_dir);
        !           154:     } else if (!S_ISDIR(sb.st_mode)) {
        !           155:        log_error(0, _("%s exists but is not a directory (0%o)"),
        !           156:            iolog_dir, (unsigned int) sb.st_mode);
        !           157:     }
        !           158: 
        !           159:     /*
        !           160:      * Open sequence file
        !           161:      */
        !           162:     len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", iolog_dir);
        !           163:     if (len <= 0 || len >= sizeof(pathbuf)) {
        !           164:        errno = ENAMETOOLONG;
        !           165:        log_error(USE_ERRNO, "%s/seq", pathbuf);
        !           166:     }
        !           167:     fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
        !           168:     if (fd == -1)
        !           169:        log_error(USE_ERRNO, _("unable to open %s"), pathbuf);
        !           170:     lock_file(fd, SUDO_LOCK);
        !           171: 
        !           172:     /* Read seq number (base 36). */
        !           173:     nread = read(fd, buf, sizeof(buf));
        !           174:     if (nread != 0) {
        !           175:        if (nread == -1)
        !           176:            log_error(USE_ERRNO, _("unable to read %s"), pathbuf);
        !           177:        id = strtoul(buf, &ep, 36);
        !           178:        if (buf == ep || id >= SESSID_MAX)
        !           179:            log_error(0, _("invalid sequence number %s"), pathbuf);
        !           180:     }
        !           181:     id++;
        !           182: 
        !           183:     /*
        !           184:      * Convert id to a string and stash in sessid.
        !           185:      * Note that that least significant digits go at the end of the string.
        !           186:      */
        !           187:     for (i = 5; i >= 0; i--) {
        !           188:        buf[i] = b36char[id % 36];
        !           189:        id /= 36;
        !           190:     }
        !           191:     buf[6] = '\n';
        !           192: 
        !           193:     /* Stash id logging purposes */
        !           194:     memcpy(sessid, buf, 6);
        !           195:     sessid[6] = '\0';
        !           196: 
        !           197:     /* Rewind and overwrite old seq file. */
        !           198:     if (lseek(fd, 0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7)
        !           199:        log_error(USE_ERRNO, _("unable to write to %s"), pathbuf);
        !           200:     close(fd);
        !           201: }
        !           202: 
        !           203: /*
        !           204:  * Copy iolog_path to pathbuf and create the directory and any intermediate
        !           205:  * directories.  If iolog_path ends in 'XXXXXX', use mkdtemp().
        !           206:  */
        !           207: static size_t
        !           208: mkdir_iopath(const char *iolog_path, char *pathbuf, size_t pathsize)
        !           209: {
        !           210:     size_t len;
        !           211: 
        !           212:     len = strlcpy(pathbuf, iolog_path, pathsize);
        !           213:     if (len >= pathsize) {
        !           214:        errno = ENAMETOOLONG;
        !           215:        log_error(USE_ERRNO, "%s", iolog_path);
        !           216:     }
        !           217: 
        !           218:     /*
        !           219:      * Create path and intermediate subdirs as needed.
        !           220:      * If path ends in at least 6 Xs (ala POSIX mktemp), use mkdtemp().
        !           221:      */
        !           222:     mkdir_parents(pathbuf);
        !           223:     if (len >= 6 && strcmp(&pathbuf[len - 6], "XXXXXX") == 0) {
        !           224:        if (mkdtemp(pathbuf) == NULL)
        !           225:            log_error(USE_ERRNO, _("unable to create %s"), pathbuf);
        !           226:     } else {
        !           227:        if (mkdir(pathbuf, S_IRWXU) != 0)
        !           228:            log_error(USE_ERRNO, _("unable to create %s"), pathbuf);
        !           229:     }
        !           230: 
        !           231:     return len;
        !           232: }
        !           233: 
        !           234: /*
        !           235:  * Append suffix to pathbuf after len chars and open the resulting file.
        !           236:  * Note that the size of pathbuf is assumed to be PATH_MAX.
        !           237:  * Uses zlib if docompress is TRUE.
        !           238:  * Returns the open file handle which has the close-on-exec flag set.
        !           239:  */
        !           240: static void *
        !           241: open_io_fd(char *pathbuf, size_t len, const char *suffix, int docompress)
        !           242: {
        !           243:     void *vfd = NULL;
        !           244:     int fd;
        !           245: 
        !           246:     pathbuf[len] = '\0';
        !           247:     strlcat(pathbuf, suffix, PATH_MAX);
        !           248:     fd = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
        !           249:     if (fd != -1) {
        !           250:        fcntl(fd, F_SETFD, FD_CLOEXEC);
        !           251: #ifdef HAVE_ZLIB_H
        !           252:        if (docompress)
        !           253:            vfd = gzdopen(fd, "w");
        !           254:        else
        !           255: #endif
        !           256:            vfd = fdopen(fd, "w");
        !           257:     }
        !           258:     return vfd;
        !           259: }
        !           260: 
        !           261: /*
        !           262:  * Pull out I/O log related data from user_info and command_info arrays.
        !           263:  */
        !           264: static void
        !           265: iolog_deserialize_info(struct iolog_details *details, char * const user_info[],
        !           266:     char * const command_info[])
        !           267: {
        !           268:     const char *runas_uid_str = "0", *runas_euid_str = NULL;
        !           269:     const char *runas_gid_str = "0", *runas_egid_str = NULL;
        !           270:     char id[MAX_UID_T_LEN + 2], *ep;
        !           271:     char * const *cur;
        !           272:     unsigned long ulval;
        !           273:     uid_t runas_uid = 0;
        !           274:     gid_t runas_gid = 0;
        !           275: 
        !           276:     memset(details, 0, sizeof(*details));
        !           277: 
        !           278:     for (cur = user_info; *cur != NULL; cur++) {
        !           279:        switch (**cur) {
        !           280:        case 'c':
        !           281:            if (strncmp(*cur, "cwd=", sizeof("cwd=") - 1) == 0) {
        !           282:                details->cwd = *cur + sizeof("cwd=") - 1;
        !           283:                continue;
        !           284:            }
        !           285:            break;
        !           286:        case 't':
        !           287:            if (strncmp(*cur, "tty=", sizeof("tty=") - 1) == 0) {
        !           288:                details->tty = *cur + sizeof("tty=") - 1;
        !           289:                continue;
        !           290:            }
        !           291:            break;
        !           292:        case 'u':
        !           293:            if (strncmp(*cur, "user=", sizeof("user=") - 1) == 0) {
        !           294:                details->user = *cur + sizeof("user=") - 1;
        !           295:                continue;
        !           296:            }
        !           297:            break;
        !           298:        }
        !           299:     }
        !           300: 
        !           301:     for (cur = command_info; *cur != NULL; cur++) {
        !           302:        switch (**cur) {
        !           303:        case 'c':
        !           304:            if (strncmp(*cur, "command=", sizeof("command=") - 1) == 0) {
        !           305:                details->command = *cur + sizeof("command=") - 1;
        !           306:                continue;
        !           307:            }
        !           308:            break;
        !           309:        case 'i':
        !           310:            if (strncmp(*cur, "iolog_path=", sizeof("iolog_path=") - 1) == 0) {
        !           311:                details->iolog_path = *cur + sizeof("iolog_path=") - 1;
        !           312:                continue;
        !           313:            }
        !           314:            if (strncmp(*cur, "iolog_stdin=", sizeof("iolog_stdin=") - 1) == 0) {
        !           315:                if (atobool(*cur + sizeof("iolog_stdin=") - 1) == TRUE)
        !           316:                    details->iolog_stdin = TRUE;
        !           317:                continue;
        !           318:            }
        !           319:            if (strncmp(*cur, "iolog_stdout=", sizeof("iolog_stdout=") - 1) == 0) {
        !           320:                if (atobool(*cur + sizeof("iolog_stdout=") - 1) == TRUE)
        !           321:                    details->iolog_stdout = TRUE;
        !           322:                continue;
        !           323:            }
        !           324:            if (strncmp(*cur, "iolog_stderr=", sizeof("iolog_stderr=") - 1) == 0) {
        !           325:                if (atobool(*cur + sizeof("iolog_stderr=") - 1) == TRUE)
        !           326:                    details->iolog_stderr = TRUE;
        !           327:                continue;
        !           328:            }
        !           329:            if (strncmp(*cur, "iolog_ttyin=", sizeof("iolog_ttyin=") - 1) == 0) {
        !           330:                if (atobool(*cur + sizeof("iolog_ttyin=") - 1) == TRUE)
        !           331:                    details->iolog_ttyin = TRUE;
        !           332:                continue;
        !           333:            }
        !           334:            if (strncmp(*cur, "iolog_ttyout=", sizeof("iolog_ttyout=") - 1) == 0) {
        !           335:                if (atobool(*cur + sizeof("iolog_ttyout=") - 1) == TRUE)
        !           336:                    details->iolog_ttyout = TRUE;
        !           337:                continue;
        !           338:            }
        !           339:            if (strncmp(*cur, "iolog_compress=", sizeof("iolog_compress=") - 1) == 0) {
        !           340:                if (atobool(*cur + sizeof("iolog_compress=") - 1) == TRUE)
        !           341:                    iolog_compress = TRUE; /* must be global */
        !           342:                continue;
        !           343:            }
        !           344:            break;
        !           345:        case 'r':
        !           346:            if (strncmp(*cur, "runas_gid=", sizeof("runas_gid=") - 1) == 0) {
        !           347:                runas_gid_str = *cur + sizeof("runas_gid=") - 1;
        !           348:                continue;
        !           349:            }
        !           350:            if (strncmp(*cur, "runas_egid=", sizeof("runas_egid=") - 1) == 0) {
        !           351:                runas_egid_str = *cur + sizeof("runas_egid=") - 1;
        !           352:                continue;
        !           353:            }
        !           354:            if (strncmp(*cur, "runas_uid=", sizeof("runas_uid=") - 1) == 0) {
        !           355:                runas_uid_str = *cur + sizeof("runas_uid=") - 1;
        !           356:                continue;
        !           357:            }
        !           358:            if (strncmp(*cur, "runas_euid=", sizeof("runas_euid=") - 1) == 0) {
        !           359:                runas_euid_str = *cur + sizeof("runas_euid=") - 1;
        !           360:                continue;
        !           361:            }
        !           362:            break;
        !           363:        }
        !           364:     }
        !           365: 
        !           366:     /*
        !           367:      * Lookup runas user and group, preferring effective over real uid/gid.
        !           368:      */
        !           369:     if (runas_euid_str != NULL)
        !           370:        runas_uid_str = runas_euid_str;
        !           371:     if (runas_uid_str != NULL) {
        !           372:        errno = 0;
        !           373:        ulval = strtoul(runas_uid_str, &ep, 0);
        !           374:        if (*runas_uid_str != '\0' && *ep == '\0' &&
        !           375:            (errno != ERANGE || ulval != ULONG_MAX)) {
        !           376:            runas_uid = (uid_t)ulval;
        !           377:        }
        !           378:     }
        !           379:     if (runas_egid_str != NULL)
        !           380:        runas_gid_str = runas_egid_str;
        !           381:     if (runas_gid_str != NULL) {
        !           382:        errno = 0;
        !           383:        ulval = strtoul(runas_gid_str, &ep, 0);
        !           384:        if (*runas_gid_str != '\0' && *ep == '\0' &&
        !           385:            (errno != ERANGE || ulval != ULONG_MAX)) {
        !           386:            runas_gid = (gid_t)ulval;
        !           387:        }
        !           388:     }
        !           389: 
        !           390:     details->runas_pw = sudo_getpwuid(runas_uid);
        !           391:     if (details->runas_pw == NULL) {
        !           392:        id[0] = '#';
        !           393:        strlcpy(&id[1], runas_uid_str, sizeof(id) - 1);
        !           394:        details->runas_pw = sudo_fakepwnam(id, runas_gid);
        !           395:     }
        !           396: 
        !           397:     if (runas_gid != details->runas_pw->pw_gid) {
        !           398:        details->runas_gr = sudo_getgrgid(runas_gid);
        !           399:        if (details->runas_gr == NULL) {
        !           400:            id[0] = '#';
        !           401:            strlcpy(&id[1], runas_gid_str, sizeof(id) - 1);
        !           402:            details->runas_gr = sudo_fakegrnam(id);
        !           403:        }
        !           404:     }
        !           405: }
        !           406: 
        !           407: static int
        !           408: sudoers_io_open(unsigned int version, sudo_conv_t conversation,
        !           409:     sudo_printf_t plugin_printf, char * const settings[],
        !           410:     char * const user_info[], char * const command_info[],
        !           411:     int argc, char * const argv[], char * const user_env[])
        !           412: {
        !           413:     struct iolog_details details;
        !           414:     char pathbuf[PATH_MAX], sessid[7];
        !           415:     char *tofree = NULL;
        !           416:     char * const *cur;
        !           417:     FILE *io_logfile;
        !           418:     size_t len;
        !           419:     int rval = -1;
        !           420: 
        !           421:     if (!sudo_conv)
        !           422:        sudo_conv = conversation;
        !           423:     if (!sudo_printf)
        !           424:        sudo_printf = plugin_printf;
        !           425: 
        !           426:     /* If we have no command (because -V was specified) just return. */
        !           427:     if (argc == 0)
        !           428:        return TRUE;
        !           429: 
        !           430:     if (sigsetjmp(error_jmp, 1)) {
        !           431:        /* called via error(), errorx() or log_error() */
        !           432:        rval = -1;
        !           433:        goto done;
        !           434:     }
        !           435: 
        !           436:     bindtextdomain("sudoers", LOCALEDIR);
        !           437: 
        !           438:     sudo_setpwent();
        !           439:     sudo_setgrent();
        !           440: 
        !           441:     /*
        !           442:      * Pull iolog settings out of command_info, if any.
        !           443:      */
        !           444:     iolog_deserialize_info(&details, user_info, command_info);
        !           445:     /* Did policy module disable I/O logging? */
        !           446:     if (!details.iolog_stdin && !details.iolog_ttyin &&
        !           447:        !details.iolog_stdout && !details.iolog_stderr &&
        !           448:        !details.iolog_ttyout) {
        !           449:        rval = FALSE;
        !           450:        goto done;
        !           451:     }
        !           452: 
        !           453:     /* If no I/O log path defined we need to figure it out ourselves. */
        !           454:     if (details.iolog_path == NULL) {
        !           455:        /* Get next session ID and convert it into a path. */
        !           456:        tofree = emalloc(sizeof(_PATH_SUDO_IO_LOGDIR) + sizeof(sessid) + 2);
        !           457:        memcpy(tofree, _PATH_SUDO_IO_LOGDIR, sizeof(_PATH_SUDO_IO_LOGDIR));
        !           458:        io_nextid(tofree, sessid);
        !           459:        snprintf(tofree + sizeof(_PATH_SUDO_IO_LOGDIR), sizeof(sessid) + 2,
        !           460:            "%c%c/%c%c/%c%c", sessid[0], sessid[1], sessid[2], sessid[3],
        !           461:            sessid[4], sessid[5]);
        !           462:        details.iolog_path = tofree;
        !           463:     }
        !           464: 
        !           465:     /*
        !           466:      * Make local copy of I/O log path and create it, along with any
        !           467:      * intermediate subdirs.  Calls mkdtemp() if iolog_path ends in XXXXXX.
        !           468:      */
        !           469:     len = mkdir_iopath(details.iolog_path, pathbuf, sizeof(pathbuf));
        !           470:     if (len >= sizeof(pathbuf))
        !           471:        goto done;
        !           472: 
        !           473:     /*
        !           474:      * We create 7 files: a log file, a timing file and 5 for input/output.
        !           475:      */
        !           476:     io_logfile = open_io_fd(pathbuf, len, "/log", FALSE);
        !           477:     if (io_logfile == NULL)
        !           478:        log_error(USE_ERRNO, _("unable to create %s"), pathbuf);
        !           479: 
        !           480:     io_fds[IOFD_TIMING].v = open_io_fd(pathbuf, len, "/timing",
        !           481:        iolog_compress);
        !           482:     if (io_fds[IOFD_TIMING].v == NULL)
        !           483:        log_error(USE_ERRNO, _("unable to create %s"), pathbuf);
        !           484: 
        !           485:     if (details.iolog_ttyin) {
        !           486:        io_fds[IOFD_TTYIN].v = open_io_fd(pathbuf, len, "/ttyin",
        !           487:            iolog_compress);
        !           488:        if (io_fds[IOFD_TTYIN].v == NULL)
        !           489:            log_error(USE_ERRNO, _("unable to create %s"), pathbuf);
        !           490:     } else {
        !           491:        sudoers_io.log_ttyin = NULL;
        !           492:     }
        !           493:     if (details.iolog_stdin) {
        !           494:        io_fds[IOFD_STDIN].v = open_io_fd(pathbuf, len, "/stdin",
        !           495:            iolog_compress);
        !           496:        if (io_fds[IOFD_STDIN].v == NULL)
        !           497:            log_error(USE_ERRNO, _("unable to create %s"), pathbuf);
        !           498:     } else {
        !           499:        sudoers_io.log_stdin = NULL;
        !           500:     }
        !           501:     if (details.iolog_ttyout) {
        !           502:        io_fds[IOFD_TTYOUT].v = open_io_fd(pathbuf, len, "/ttyout",
        !           503:            iolog_compress);
        !           504:        if (io_fds[IOFD_TTYOUT].v == NULL)
        !           505:            log_error(USE_ERRNO, _("unable to create %s"), pathbuf);
        !           506:     } else {
        !           507:        sudoers_io.log_ttyout = NULL;
        !           508:     }
        !           509:     if (details.iolog_stdout) {
        !           510:        io_fds[IOFD_STDOUT].v = open_io_fd(pathbuf, len, "/stdout",
        !           511:            iolog_compress);
        !           512:        if (io_fds[IOFD_STDOUT].v == NULL)
        !           513:            log_error(USE_ERRNO, _("unable to create %s"), pathbuf);
        !           514:     } else {
        !           515:        sudoers_io.log_stdout = NULL;
        !           516:     }
        !           517:     if (details.iolog_stderr) {
        !           518:        io_fds[IOFD_STDERR].v = open_io_fd(pathbuf, len, "/stderr",
        !           519:            iolog_compress);
        !           520:        if (io_fds[IOFD_STDERR].v == NULL)
        !           521:            log_error(USE_ERRNO, _("unable to create %s"), pathbuf);
        !           522:     } else {
        !           523:        sudoers_io.log_stderr = NULL;
        !           524:     }
        !           525: 
        !           526:     gettimeofday(&last_time, NULL);
        !           527: 
        !           528:     fprintf(io_logfile, "%ld:%s:%s:%s:%s\n", (long)last_time.tv_sec,
        !           529:        details.user ? details.user : "unknown", details.runas_pw->pw_name,
        !           530:        details.runas_gr ? details.runas_gr->gr_name : "",
        !           531:        details.tty ? details.tty : "unknown");
        !           532:     fputs(details.cwd ? details.cwd : "unknown", io_logfile);
        !           533:     fputc('\n', io_logfile);
        !           534:     fputs(details.command ? details.command : "unknown", io_logfile);
        !           535:     for (cur = &argv[1]; *cur != NULL; cur++) {
        !           536:        fputc(' ', io_logfile);
        !           537:        fputs(*cur, io_logfile);
        !           538:     }
        !           539:     fputc('\n', io_logfile);
        !           540:     fclose(io_logfile);
        !           541: 
        !           542:     rval = TRUE;
        !           543: 
        !           544: done:
        !           545:     efree(tofree);
        !           546:     if (details.runas_pw)
        !           547:        pw_delref(details.runas_pw);
        !           548:     sudo_endpwent();
        !           549:     if (details.runas_gr)
        !           550:        gr_delref(details.runas_gr);
        !           551:     sudo_endgrent();
        !           552: 
        !           553:     return rval;
        !           554: }
        !           555: 
        !           556: static void
        !           557: sudoers_io_close(int exit_status, int error)
        !           558: {
        !           559:     int i;
        !           560: 
        !           561:     if (sigsetjmp(error_jmp, 1)) {
        !           562:        /* called via error(), errorx() or log_error() */
        !           563:        return;
        !           564:     }
        !           565: 
        !           566:     for (i = 0; i < IOFD_MAX; i++) {
        !           567:        if (io_fds[i].v == NULL)
        !           568:            continue;
        !           569: #ifdef HAVE_ZLIB_H
        !           570:        if (iolog_compress)
        !           571:            gzclose(io_fds[i].g);
        !           572:        else
        !           573: #endif
        !           574:            fclose(io_fds[i].f);
        !           575:     }
        !           576: }
        !           577: 
        !           578: static int
        !           579: sudoers_io_version(int verbose)
        !           580: {
        !           581:     if (sigsetjmp(error_jmp, 1)) {
        !           582:        /* called via error(), errorx() or log_error() */
        !           583:        return -1;
        !           584:     }
        !           585: 
        !           586:     sudo_printf(SUDO_CONV_INFO_MSG, "Sudoers I/O plugin version %s\n",
        !           587:        PACKAGE_VERSION);
        !           588: 
        !           589:     return TRUE;
        !           590: }
        !           591: 
        !           592: /*
        !           593:  * Generic I/O logging function.  Called by the I/O logging entry points.
        !           594:  */
        !           595: static int
        !           596: sudoers_io_log(const char *buf, unsigned int len, int idx)
        !           597: {
        !           598:     struct timeval now, delay;
        !           599: 
        !           600:     gettimeofday(&now, NULL);
        !           601: 
        !           602:     if (sigsetjmp(error_jmp, 1)) {
        !           603:        /* called via error(), errorx() or log_error() */
        !           604:        return -1;
        !           605:     }
        !           606: 
        !           607: #ifdef HAVE_ZLIB_H
        !           608:     if (iolog_compress)
        !           609:        gzwrite(io_fds[idx].g, buf, len);
        !           610:     else
        !           611: #endif
        !           612:        fwrite(buf, 1, len, io_fds[idx].f);
        !           613:     delay.tv_sec = now.tv_sec;
        !           614:     delay.tv_usec = now.tv_usec;
        !           615:     timevalsub(&delay, &last_time);
        !           616: #ifdef HAVE_ZLIB_H
        !           617:     if (iolog_compress)
        !           618:        gzprintf(io_fds[IOFD_TIMING].g, "%d %f %d\n", idx,
        !           619:            delay.tv_sec + ((double)delay.tv_usec / 1000000), len);
        !           620:     else
        !           621: #endif
        !           622:        fprintf(io_fds[IOFD_TIMING].f, "%d %f %d\n", idx,
        !           623:            delay.tv_sec + ((double)delay.tv_usec / 1000000), len);
        !           624:     last_time.tv_sec = now.tv_sec;
        !           625:     last_time.tv_usec = now.tv_usec;
        !           626: 
        !           627:     return TRUE;
        !           628: }
        !           629: 
        !           630: static int
        !           631: sudoers_io_log_ttyin(const char *buf, unsigned int len)
        !           632: {
        !           633:     return sudoers_io_log(buf, len, IOFD_TTYIN);
        !           634: }
        !           635: 
        !           636: static int
        !           637: sudoers_io_log_ttyout(const char *buf, unsigned int len)
        !           638: {
        !           639:     return sudoers_io_log(buf, len, IOFD_TTYOUT);
        !           640: }
        !           641: 
        !           642: static int
        !           643: sudoers_io_log_stdin(const char *buf, unsigned int len)
        !           644: {
        !           645:     return sudoers_io_log(buf, len, IOFD_STDIN);
        !           646: }
        !           647: 
        !           648: static int
        !           649: sudoers_io_log_stdout(const char *buf, unsigned int len)
        !           650: {
        !           651:     return sudoers_io_log(buf, len, IOFD_STDOUT);
        !           652: }
        !           653: 
        !           654: static int
        !           655: sudoers_io_log_stderr(const char *buf, unsigned int len)
        !           656: {
        !           657:     return sudoers_io_log(buf, len, IOFD_STDERR);
        !           658: }
        !           659: 
        !           660: struct io_plugin sudoers_io = {
        !           661:     SUDO_IO_PLUGIN,
        !           662:     SUDO_API_VERSION,
        !           663:     sudoers_io_open,
        !           664:     sudoers_io_close,
        !           665:     sudoers_io_version,
        !           666:     sudoers_io_log_ttyin,
        !           667:     sudoers_io_log_ttyout,
        !           668:     sudoers_io_log_stdin,
        !           669:     sudoers_io_log_stdout,
        !           670:     sudoers_io_log_stderr
        !           671: };

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