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