Return to iolog.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers |
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: };