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

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

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