File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers / logging.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 07:56:34 2013 UTC (10 years, 9 months ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_8p0, v1_8_8, HEAD
v 1.8.8

    1: /*
    2:  * Copyright (c) 1994-1996, 1998-2013 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:  * Sponsored in part by the Defense Advanced Research Projects
   17:  * Agency (DARPA) and Air Force Research Laboratory, Air Force
   18:  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
   19:  */
   20: 
   21: #ifdef __TANDEM
   22: # include <floss.h>
   23: #endif
   24: 
   25: #include <config.h>
   26: 
   27: #include <sys/types.h>
   28: #include <sys/stat.h>
   29: #include <sys/ioctl.h>
   30: #include <sys/wait.h>
   31: #include <stdio.h>
   32: #ifdef STDC_HEADERS
   33: # include <stdlib.h>
   34: # include <stddef.h>
   35: #else
   36: # ifdef HAVE_STDLIB_H
   37: #  include <stdlib.h>
   38: # endif
   39: #endif /* STDC_HEADERS */
   40: #ifdef HAVE_STRING_H
   41: # include <string.h>
   42: #endif /* HAVE_STRING_H */
   43: #ifdef HAVE_STRINGS_H
   44: # include <strings.h>
   45: #endif /* HAVE_STRINGS_H */
   46: #ifdef HAVE_UNISTD_H
   47: # include <unistd.h>
   48: #endif /* HAVE_UNISTD_H */
   49: #ifdef HAVE_NL_LANGINFO
   50: # include <langinfo.h>
   51: #endif /* HAVE_NL_LANGINFO */
   52: #include <pwd.h>
   53: #include <grp.h>
   54: #include <signal.h>
   55: #include <time.h>
   56: #include <ctype.h>
   57: #include <errno.h>
   58: #include <fcntl.h>
   59: 
   60: #include "sudoers.h"
   61: 
   62: #ifndef va_copy
   63: # define va_copy(d, s) memcpy(&(d), &(s), sizeof(d));
   64: #endif
   65: 
   66: /* Special message for log_warning() so we know to use ngettext() */
   67: #define INCORRECT_PASSWORD_ATTEMPT	((char *)0x01)
   68: 
   69: static void do_syslog(int, char *);
   70: static void do_logfile(char *);
   71: static void send_mail(const char *fmt, ...);
   72: static int should_mail(int);
   73: static void mysyslog(int, const char *, ...);
   74: static char *new_logline(const char *, int);
   75: 
   76: extern char **NewArgv; /* XXX - for auditing */
   77: 
   78: #define MAXSYSLOGTRIES	16	/* num of retries for broken syslogs */
   79: 
   80: /*
   81:  * We do an openlog(3)/closelog(3) for each message because some
   82:  * authentication methods (notably PAM) use syslog(3) for their
   83:  * own nefarious purposes and may call openlog(3) and closelog(3).
   84:  * Note that because we don't want to assume that all systems have
   85:  * vsyslog(3) (HP-UX doesn't) "%m" will not be expanded.
   86:  * Sadly this is a maze of #ifdefs.
   87:  */
   88: static void
   89: mysyslog(int pri, const char *fmt, ...)
   90: {
   91: #ifdef BROKEN_SYSLOG
   92:     int i;
   93: #endif
   94:     char buf[MAXSYSLOGLEN+1];
   95:     va_list ap;
   96:     debug_decl(mysyslog, SUDO_DEBUG_LOGGING)
   97: 
   98:     va_start(ap, fmt);
   99: #ifdef LOG_NFACILITIES
  100:     openlog("sudo", 0, def_syslog);
  101: #else
  102:     openlog("sudo", 0);
  103: #endif
  104:     vsnprintf(buf, sizeof(buf), fmt, ap);
  105: #ifdef BROKEN_SYSLOG
  106:     /*
  107:      * Some versions of syslog(3) don't guarantee success and return
  108:      * an int (notably HP-UX < 10.0).  So, if at first we don't succeed,
  109:      * try, try again...
  110:      */
  111:     for (i = 0; i < MAXSYSLOGTRIES; i++)
  112: 	if (syslog(pri, "%s", buf) == 0)
  113: 	    break;
  114: #else
  115:     syslog(pri, "%s", buf);
  116: #endif /* BROKEN_SYSLOG */
  117:     va_end(ap);
  118:     closelog();
  119:     debug_return;
  120: }
  121: 
  122: /*
  123:  * Log a message to syslog, pre-pending the username and splitting the
  124:  * message into parts if it is longer than MAXSYSLOGLEN.
  125:  */
  126: static void
  127: do_syslog(int pri, char *msg)
  128: {
  129:     size_t len, maxlen;
  130:     char *p, *tmp, save;
  131:     const char *fmt;
  132:     int oldlocale;
  133:     debug_decl(do_syslog, SUDO_DEBUG_LOGGING)
  134: 
  135:     sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
  136: 
  137:     /*
  138:      * Log the full line, breaking into multiple syslog(3) calls if necessary
  139:      */
  140:     fmt = _("%8s : %s");
  141:     maxlen = MAXSYSLOGLEN - (strlen(fmt) - 5 + strlen(user_name));
  142:     for (p = msg; *p != '\0'; ) {
  143: 	len = strlen(p);
  144: 	if (len > maxlen) {
  145: 	    /*
  146: 	     * Break up the line into what will fit on one syslog(3) line
  147: 	     * Try to avoid breaking words into several lines if possible.
  148: 	     */
  149: 	    tmp = memrchr(p, ' ', maxlen);
  150: 	    if (tmp == NULL)
  151: 		tmp = p + maxlen;
  152: 
  153: 	    /* NULL terminate line, but save the char to restore later */
  154: 	    save = *tmp;
  155: 	    *tmp = '\0';
  156: 
  157: 	    mysyslog(pri, fmt, user_name, p);
  158: 
  159: 	    *tmp = save;			/* restore saved character */
  160: 
  161: 	    /* Advance p and eliminate leading whitespace */
  162: 	    for (p = tmp; *p == ' '; p++)
  163: 		;
  164: 	} else {
  165: 	    mysyslog(pri, fmt, user_name, p);
  166: 	    p += len;
  167: 	}
  168: 	fmt = _("%8s : (command continued) %s");
  169: 	maxlen = MAXSYSLOGLEN - (strlen(fmt) - 5 + strlen(user_name));
  170:     }
  171: 
  172:     sudoers_setlocale(oldlocale, NULL);
  173: 
  174:     debug_return;
  175: }
  176: 
  177: static void
  178: do_logfile(char *msg)
  179: {
  180:     char *full_line;
  181:     size_t len;
  182:     mode_t oldmask;
  183:     time_t now;
  184:     int oldlocale;
  185:     FILE *fp;
  186:     debug_decl(do_logfile, SUDO_DEBUG_LOGGING)
  187: 
  188:     sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
  189: 
  190:     oldmask = umask(077);
  191:     fp = fopen(def_logfile, "a");
  192:     (void) umask(oldmask);
  193:     if (fp == NULL) {
  194: 	send_mail(_("unable to open log file: %s: %s"),
  195: 	    def_logfile, strerror(errno));
  196:     } else if (!lock_file(fileno(fp), SUDO_LOCK)) {
  197: 	send_mail(_("unable to lock log file: %s: %s"),
  198: 	    def_logfile, strerror(errno));
  199:     } else {
  200: 	time(&now);
  201: 	if (def_loglinelen < sizeof(LOG_INDENT)) {
  202: 	    /* Don't pretty-print long log file lines (hard to grep) */
  203: 	    if (def_log_host) {
  204: 		(void) fprintf(fp, "%s : %s : HOST=%s : %s\n",
  205: 		    get_timestr(now, def_log_year), user_name, user_srunhost,
  206: 		    msg);
  207: 	    } else {
  208: 		(void) fprintf(fp, "%s : %s : %s\n",
  209: 		    get_timestr(now, def_log_year), user_name, msg);
  210: 	    }
  211: 	} else {
  212: 	    if (def_log_host) {
  213: 		len = easprintf(&full_line, "%s : %s : HOST=%s : %s",
  214: 		    get_timestr(now, def_log_year), user_name, user_srunhost,
  215: 		    msg);
  216: 	    } else {
  217: 		len = easprintf(&full_line, "%s : %s : %s",
  218: 		    get_timestr(now, def_log_year), user_name, msg);
  219: 	    }
  220: 
  221: 	    /*
  222: 	     * Print out full_line with word wrap around def_loglinelen chars.
  223: 	     */
  224: 	    writeln_wrap(fp, full_line, len, def_loglinelen);
  225: 	    efree(full_line);
  226: 	}
  227: 	(void) fflush(fp);
  228: 	(void) lock_file(fileno(fp), SUDO_UNLOCK);
  229: 	(void) fclose(fp);
  230:     }
  231:     sudoers_setlocale(oldlocale, NULL);
  232: 
  233:     debug_return;
  234: }
  235: 
  236: /*
  237:  * Log, audit and mail the denial message, optionally informing the user.
  238:  */
  239: void
  240: log_denial(int status, bool inform_user)
  241: {
  242:     const char *message;
  243:     char *logline;
  244:     int oldlocale;
  245:     debug_decl(log_denial, SUDO_DEBUG_LOGGING)
  246: 
  247:     /* Handle auditing first (audit_failure() handles the locale itself). */
  248:     if (ISSET(status, FLAG_NO_USER | FLAG_NO_HOST))
  249: 	audit_failure(NewArgv, N_("No user or host"));
  250:     else
  251: 	audit_failure(NewArgv, N_("validation failure"));
  252: 
  253:     /* Log and mail messages should be in the sudoers locale. */
  254:     sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
  255: 
  256:     /* Set error message. */
  257:     if (ISSET(status, FLAG_NO_USER))
  258: 	message = _("user NOT in sudoers");
  259:     else if (ISSET(status, FLAG_NO_HOST))
  260: 	message = _("user NOT authorized on host");
  261:     else
  262: 	message = _("command not allowed");
  263: 
  264:     logline = new_logline(message, 0);
  265: 
  266:     /* Become root if we are not already. */
  267:     set_perms(PERM_ROOT|PERM_NOEXIT);
  268: 
  269:     if (should_mail(status))
  270: 	send_mail("%s", logline);	/* send mail based on status */
  271: 
  272:     /*
  273:      * Log via syslog and/or a file.
  274:      */
  275:     if (def_syslog)
  276: 	do_syslog(def_syslog_badpri, logline);
  277:     if (def_logfile)
  278: 	do_logfile(logline);
  279: 
  280:     restore_perms();
  281: 
  282:     efree(logline);
  283: 
  284:     /* Restore locale. */
  285:     sudoers_setlocale(oldlocale, NULL);
  286: 
  287:     /* Inform the user if they failed to authenticate (in their locale).  */
  288:     if (inform_user) {
  289: 	sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale);
  290: 
  291: 	if (ISSET(status, FLAG_NO_USER)) {
  292: 	    sudo_printf(SUDO_CONV_ERROR_MSG, _("%s is not in the sudoers "
  293: 		"file.  This incident will be reported.\n"), user_name);
  294: 	} else if (ISSET(status, FLAG_NO_HOST)) {
  295: 	    sudo_printf(SUDO_CONV_ERROR_MSG, _("%s is not allowed to run sudo "
  296: 		"on %s.  This incident will be reported.\n"),
  297: 		user_name, user_srunhost);
  298: 	} else if (ISSET(status, FLAG_NO_CHECK)) {
  299: 	    sudo_printf(SUDO_CONV_ERROR_MSG, _("Sorry, user %s may not run "
  300: 		"sudo on %s.\n"), user_name, user_srunhost);
  301: 	} else {
  302: 	    sudo_printf(SUDO_CONV_ERROR_MSG, _("Sorry, user %s is not allowed "
  303: 		"to execute '%s%s%s' as %s%s%s on %s.\n"),
  304: 		user_name, user_cmnd, user_args ? " " : "",
  305: 		user_args ? user_args : "",
  306: 		list_pw ? list_pw->pw_name : runas_pw ?
  307: 		runas_pw->pw_name : user_name, runas_gr ? ":" : "",
  308: 		runas_gr ? runas_gr->gr_name : "", user_host);
  309: 	}
  310: 	sudoers_setlocale(oldlocale, NULL);
  311:     }
  312:     debug_return;
  313: }
  314: 
  315: /*
  316:  * Log and audit that user was not allowed to run the command.
  317:  */
  318: void
  319: log_failure(int status, int flags)
  320: {
  321:     bool inform_user = true;
  322:     debug_decl(log_failure, SUDO_DEBUG_LOGGING)
  323: 
  324:     /* The user doesn't always get to see the log message (path info). */
  325:     if (!ISSET(status, FLAG_NO_USER | FLAG_NO_HOST) && def_path_info &&
  326: 	(flags == NOT_FOUND_DOT || flags == NOT_FOUND))
  327: 	inform_user = false;
  328:     log_denial(status, inform_user);
  329: 
  330:     if (!inform_user) {
  331: 	/*
  332: 	 * We'd like to not leak path info at all here, but that can
  333: 	 * *really* confuse the users.  To really close the leak we'd
  334: 	 * have to say "not allowed to run foo" even when the problem
  335: 	 * is just "no foo in path" since the user can trivially set
  336: 	 * their path to just contain a single dir.
  337: 	 */
  338: 	if (flags == NOT_FOUND)
  339: 	    warningx(_("%s: command not found"), user_cmnd);
  340: 	else if (flags == NOT_FOUND_DOT)
  341: 	    warningx(_("ignoring `%s' found in '.'\nUse `sudo ./%s' if this is the `%s' you wish to run."), user_cmnd, user_cmnd, user_cmnd);
  342:     }
  343: 
  344:     debug_return;
  345: }
  346: 
  347: /*
  348:  * Log and audit that user was not able to authenticate themselves.
  349:  */
  350: void
  351: log_auth_failure(int status, int tries)
  352: {
  353:     int flags = NO_MAIL;
  354:     debug_decl(log_auth_failure, SUDO_DEBUG_LOGGING)
  355: 
  356:     /* Handle auditing first. */
  357:     audit_failure(NewArgv, N_("authentication failure"));
  358: 
  359:     /*
  360:      * Do we need to send mail?
  361:      * We want to avoid sending multiple messages for the same command
  362:      * so if we are going to send an email about the denial, that takes
  363:      * precedence.
  364:      */
  365:     if (ISSET(status, VALIDATE_OK)) {
  366: 	/* Command allowed, auth failed; do we need to send mail? */
  367: 	if (def_mail_badpass || def_mail_always)
  368: 	    flags = 0;
  369:     } else {
  370: 	/* Command denied, auth failed; make sure we don't send mail twice. */
  371: 	if (def_mail_badpass && !should_mail(status))
  372: 	    flags = 0;
  373: 	/* Don't log the bad password message, we'll log a denial instead. */
  374: 	flags |= NO_LOG;
  375:     }
  376: 
  377:     /*
  378:      * If sudoers denied the command we'll log that separately.
  379:      */
  380:     if (ISSET(status, FLAG_BAD_PASSWORD))
  381: 	log_warning(flags, INCORRECT_PASSWORD_ATTEMPT, tries);
  382:     else if (ISSET(status, FLAG_NON_INTERACTIVE))
  383: 	log_warning(flags, N_("a password is required"));
  384: 
  385:     debug_return;
  386: }
  387: 
  388: /*
  389:  * Log and potentially mail the allowed command.
  390:  */
  391: void
  392: log_allowed(int status)
  393: {
  394:     char *logline;
  395:     int oldlocale;
  396:     debug_decl(log_allowed, SUDO_DEBUG_LOGGING)
  397: 
  398:     /* Log and mail messages should be in the sudoers locale. */
  399:     sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
  400: 
  401:     logline = new_logline(NULL, 0);
  402: 
  403:     /* Become root if we are not already. */
  404:     set_perms(PERM_ROOT|PERM_NOEXIT);
  405: 
  406:     if (should_mail(status))
  407: 	send_mail("%s", logline);	/* send mail based on status */
  408: 
  409:     /*
  410:      * Log via syslog and/or a file.
  411:      */
  412:     if (def_syslog)
  413: 	do_syslog(def_syslog_goodpri, logline);
  414:     if (def_logfile)
  415: 	do_logfile(logline);
  416: 
  417:     restore_perms();
  418: 
  419:     efree(logline);
  420: 
  421:     sudoers_setlocale(oldlocale, NULL);
  422: 
  423:     debug_return;
  424: }
  425: 
  426: /*
  427:  * Perform logging for log_warning()/log_fatal()
  428:  */
  429: static void
  430: vlog_warning(int flags, const char *fmt, va_list ap)
  431: {
  432:     int oldlocale, serrno = errno;
  433:     char *logline, *message;
  434:     va_list ap2;
  435:     debug_decl(vlog_error, SUDO_DEBUG_LOGGING)
  436: 
  437:     /* Need extra copy of ap for warning() below. */
  438:     if (!ISSET(flags, NO_STDERR))
  439: 	va_copy(ap2, ap);
  440: 
  441:     /* Log messages should be in the sudoers locale. */
  442:     sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
  443: 
  444:     /* Expand printf-style format + args (with a special case). */
  445:     if (fmt == INCORRECT_PASSWORD_ATTEMPT) {
  446: 	int tries = va_arg(ap, int);
  447: 	easprintf(&message, ngettext("%d incorrect password attempt",
  448: 	    "%d incorrect password attempts", tries), tries);
  449:     } else {
  450: 	evasprintf(&message, _(fmt), ap);
  451:     }
  452: 
  453:     if (ISSET(flags, MSG_ONLY)) {
  454: 	logline = message;
  455:     } else {
  456: 	logline = new_logline(message, ISSET(flags, USE_ERRNO) ? serrno : 0);
  457:         efree(message);
  458:     }
  459: 
  460:     /* Become root if we are not already. */
  461:     set_perms(PERM_ROOT|PERM_NOEXIT);
  462: 
  463:     /*
  464:      * Send a copy of the error via mail.
  465:      */
  466:     if (!ISSET(flags, NO_MAIL))
  467: 	send_mail("%s", logline);
  468: 
  469:     /*
  470:      * Log to syslog and/or a file.
  471:      */
  472:     if (!ISSET(flags, NO_LOG)) {
  473: 	if (def_syslog)
  474: 	    do_syslog(def_syslog_badpri, logline);
  475: 	if (def_logfile)
  476: 	    do_logfile(logline);
  477:     }
  478: 
  479:     restore_perms();
  480: 
  481:     efree(logline);
  482: 
  483:     sudoers_setlocale(oldlocale, NULL);
  484: 
  485:     /*
  486:      * Tell the user (in their locale).
  487:      */
  488:     if (!ISSET(flags, NO_STDERR)) {
  489: 	if (fmt == INCORRECT_PASSWORD_ATTEMPT) {
  490: 	    int tries = va_arg(ap2, int);
  491: 	    warningx(ngettext("%d incorrect password attempt",
  492: 		"%d incorrect password attempts", tries), tries);
  493: 	} else {
  494: 	    if (ISSET(flags, USE_ERRNO))
  495: 		vwarning(fmt, ap2);
  496: 	    else
  497: 		vwarningx(fmt, ap2);
  498: 	}
  499: 	va_end(ap2);
  500:     }
  501: 
  502:     debug_return;
  503: }
  504: 
  505: void
  506: log_warning(int flags, const char *fmt, ...)
  507: {
  508:     va_list ap;
  509:     debug_decl(log_error, SUDO_DEBUG_LOGGING)
  510: 
  511:     /* Log the error. */
  512:     va_start(ap, fmt);
  513:     vlog_warning(flags, fmt, ap);
  514:     va_end(ap);
  515: 
  516:     debug_return;
  517: }
  518: 
  519: void
  520: log_fatal(int flags, const char *fmt, ...)
  521: {
  522:     va_list ap;
  523:     debug_decl(log_error, SUDO_DEBUG_LOGGING)
  524: 
  525:     /* Log the error. */
  526:     va_start(ap, fmt);
  527:     vlog_warning(flags, fmt, ap);
  528:     va_end(ap);
  529: 
  530:     /* Exit the plugin. */
  531:     sudoers_cleanup();
  532:     sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
  533:     fatal_longjmp(1);
  534: }
  535: 
  536: #define MAX_MAILFLAGS	63
  537: 
  538: /*
  539:  * Send a message to MAILTO user
  540:  */
  541: static void
  542: send_mail(const char *fmt, ...)
  543: {
  544:     FILE *mail;
  545:     char *p;
  546:     int fd, pfd[2], status;
  547:     pid_t pid, rv;
  548:     sigaction_t sa;
  549:     struct stat sb;
  550:     va_list ap;
  551: #ifndef NO_ROOT_MAILER
  552:     static char *root_envp[] = {
  553: 	"HOME=/",
  554: 	"PATH=/usr/bin:/bin:/usr/sbin:/sbin",
  555: 	"LOGNAME=root",
  556: 	"USERNAME=root",
  557: 	"USER=root",
  558: 	NULL
  559:     };
  560: #endif /* NO_ROOT_MAILER */
  561:     debug_decl(send_mail, SUDO_DEBUG_LOGGING)
  562: 
  563:     /* Just return if mailer is disabled. */
  564:     if (!def_mailerpath || !def_mailto)
  565: 	debug_return;
  566: 
  567:     /* Make sure the mailer exists and is a regular file. */
  568:     if (stat(def_mailerpath, &sb) != 0 || !S_ISREG(sb.st_mode))
  569: 	debug_return;
  570: 
  571:     /* Fork and return, child will daemonize. */
  572:     switch (pid = sudo_debug_fork()) {
  573: 	case -1:
  574: 	    /* Error. */
  575: 	    fatal(_("unable to fork"));
  576: 	    break;
  577: 	case 0:
  578: 	    /* Child. */
  579: 	    switch (pid = fork()) {
  580: 		case -1:
  581: 		    /* Error. */
  582: 		    mysyslog(LOG_ERR, _("unable to fork: %m"));
  583: 		    sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to fork: %s",
  584: 			strerror(errno));
  585: 		    _exit(1);
  586: 		case 0:
  587: 		    /* Grandchild continues below. */
  588: 		    break;
  589: 		default:
  590: 		    /* Parent will wait for us. */
  591: 		    _exit(0);
  592: 	    }
  593: 	    break;
  594: 	default:
  595: 	    /* Parent. */
  596: 	    do {
  597: 		rv = waitpid(pid, &status, 0);
  598: 	    } while (rv == -1 && errno == EINTR);
  599: 	    return; /* not debug */
  600:     }
  601: 
  602:     /* Daemonize - disassociate from session/tty. */
  603:     if (setsid() == -1)
  604:       warning("setsid");
  605:     if (chdir("/") == -1)
  606:       warning("chdir(/)");
  607:     if ((fd = open(_PATH_DEVNULL, O_RDWR, 0644)) != -1) {
  608: 	(void) dup2(fd, STDIN_FILENO);
  609: 	(void) dup2(fd, STDOUT_FILENO);
  610: 	(void) dup2(fd, STDERR_FILENO);
  611:     }
  612: 
  613:     sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, NULL);
  614: 
  615:     /* Close password, group and other fds so we don't leak. */
  616:     sudo_endpwent();
  617:     sudo_endgrent();
  618:     closefrom(STDERR_FILENO + 1);
  619: 
  620:     /* Ignore SIGPIPE in case mailer exits prematurely (or is missing). */
  621:     memset(&sa, 0, sizeof(sa));
  622:     sigemptyset(&sa.sa_mask);
  623:     sa.sa_flags = SA_INTERRUPT;
  624:     sa.sa_handler = SIG_IGN;
  625:     (void) sigaction(SIGPIPE, &sa, NULL);
  626: 
  627:     if (pipe(pfd) == -1) {
  628: 	mysyslog(LOG_ERR, _("unable to open pipe: %m"));
  629: 	sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to open pipe: %s",
  630: 	    strerror(errno));
  631: 	sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
  632: 	_exit(1);
  633:     }
  634: 
  635:     switch (pid = sudo_debug_fork()) {
  636: 	case -1:
  637: 	    /* Error. */
  638: 	    mysyslog(LOG_ERR, _("unable to fork: %m"));
  639: 	    sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to fork: %s",
  640: 		strerror(errno));
  641: 	    sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
  642: 	    _exit(1);
  643: 	    break;
  644: 	case 0:
  645: 	    {
  646: 		char *argv[MAX_MAILFLAGS + 1];
  647: 		char *mflags, *mpath = def_mailerpath;
  648: 		int i;
  649: 
  650: 		/* Child, set stdin to output side of the pipe */
  651: 		if (pfd[0] != STDIN_FILENO) {
  652: 		    if (dup2(pfd[0], STDIN_FILENO) == -1) {
  653: 			mysyslog(LOG_ERR, _("unable to dup stdin: %m"));
  654: 			sudo_debug_printf(SUDO_DEBUG_ERROR,
  655: 			    "unable to dup stdin: %s", strerror(errno));
  656: 			_exit(127);
  657: 		    }
  658: 		    (void) close(pfd[0]);
  659: 		}
  660: 		(void) close(pfd[1]);
  661: 
  662: 		/* Build up an argv based on the mailer path and flags */
  663: 		mflags = estrdup(def_mailerflags);
  664: 		if ((argv[0] = strrchr(mpath, '/')))
  665: 		    argv[0]++;
  666: 		else
  667: 		    argv[0] = mpath;
  668: 
  669: 		i = 1;
  670: 		if ((p = strtok(mflags, " \t"))) {
  671: 		    do {
  672: 			argv[i] = p;
  673: 		    } while (++i < MAX_MAILFLAGS && (p = strtok(NULL, " \t")));
  674: 		}
  675: 		argv[i] = NULL;
  676: 
  677: 		/*
  678: 		 * Depending on the config, either run the mailer as root
  679: 		 * (so user cannot kill it) or as the user (for the paranoid).
  680: 		 */
  681: #ifndef NO_ROOT_MAILER
  682: 		set_perms(PERM_ROOT|PERM_NOEXIT);
  683: 		execve(mpath, argv, root_envp);
  684: #else
  685: 		set_perms(PERM_FULL_USER|PERM_NOEXIT);
  686: 		execv(mpath, argv);
  687: #endif /* NO_ROOT_MAILER */
  688: 		mysyslog(LOG_ERR, _("unable to execute %s: %m"), mpath);
  689: 		sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to execute %s: %s",
  690: 		    mpath, strerror(errno));
  691: 		_exit(127);
  692: 	    }
  693: 	    break;
  694:     }
  695: 
  696:     (void) close(pfd[0]);
  697:     mail = fdopen(pfd[1], "w");
  698: 
  699:     /* Pipes are all setup, send message. */
  700:     (void) fprintf(mail, "To: %s\nFrom: %s\nAuto-Submitted: %s\nSubject: ",
  701: 	def_mailto, def_mailfrom ? def_mailfrom : user_name, "auto-generated");
  702:     for (p = _(def_mailsub); *p; p++) {
  703: 	/* Expand escapes in the subject */
  704: 	if (*p == '%' && *(p+1) != '%') {
  705: 	    switch (*(++p)) {
  706: 		case 'h':
  707: 		    (void) fputs(user_host, mail);
  708: 		    break;
  709: 		case 'u':
  710: 		    (void) fputs(user_name, mail);
  711: 		    break;
  712: 		default:
  713: 		    p--;
  714: 		    break;
  715: 	    }
  716: 	} else
  717: 	    (void) fputc(*p, mail);
  718:     }
  719: 
  720: #ifdef HAVE_NL_LANGINFO
  721:     if (strcmp(def_sudoers_locale, "C") != 0)
  722: 	(void) fprintf(mail, "\nContent-Type: text/plain; charset=\"%s\"\nContent-Transfer-Encoding: 8bit", nl_langinfo(CODESET));
  723: #endif /* HAVE_NL_LANGINFO */
  724: 
  725:     (void) fprintf(mail, "\n\n%s : %s : %s : ", user_host,
  726: 	get_timestr(time(NULL), def_log_year), user_name);
  727:     va_start(ap, fmt);
  728:     (void) vfprintf(mail, fmt, ap);
  729:     va_end(ap);
  730:     fputs("\n\n", mail);
  731: 
  732:     fclose(mail);
  733:     do {
  734:         rv = waitpid(pid, &status, 0);
  735:     } while (rv == -1 && errno == EINTR);
  736:     sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
  737:     _exit(0);
  738: }
  739: 
  740: /*
  741:  * Determine whether we should send mail based on "status" and defaults options.
  742:  */
  743: static int
  744: should_mail(int status)
  745: {
  746:     debug_decl(should_mail, SUDO_DEBUG_LOGGING)
  747: 
  748:     debug_return_bool(def_mail_always || ISSET(status, VALIDATE_ERROR) ||
  749: 	(def_mail_no_user && ISSET(status, FLAG_NO_USER)) ||
  750: 	(def_mail_no_host && ISSET(status, FLAG_NO_HOST)) ||
  751: 	(def_mail_no_perms && !ISSET(status, VALIDATE_OK)));
  752: }
  753: 
  754: #define	LL_TTY_STR	"TTY="
  755: #define	LL_CWD_STR	"PWD="		/* XXX - should be CWD= */
  756: #define	LL_USER_STR	"USER="
  757: #define	LL_GROUP_STR	"GROUP="
  758: #define	LL_ENV_STR	"ENV="
  759: #define	LL_CMND_STR	"COMMAND="
  760: #define	LL_TSID_STR	"TSID="
  761: 
  762: #define IS_SESSID(s) ( \
  763:     isalnum((unsigned char)(s)[0]) && isalnum((unsigned char)(s)[1]) && \
  764:     (s)[2] == '/' && \
  765:     isalnum((unsigned char)(s)[3]) && isalnum((unsigned char)(s)[4]) && \
  766:     (s)[5] == '/' && \
  767:     isalnum((unsigned char)(s)[6]) && isalnum((unsigned char)(s)[7]) && \
  768:     (s)[8] == '\0')
  769: 
  770: /*
  771:  * Allocate and fill in a new logline.
  772:  */
  773: static char *
  774: new_logline(const char *message, int serrno)
  775: {
  776:     char *line, *errstr = NULL, *evstr = NULL;
  777: #ifndef SUDOERS_NO_SEQ
  778:     char sessid[7];
  779: #endif
  780:     const char *tsid = NULL;
  781:     size_t len = 0;
  782:     debug_decl(new_logline, SUDO_DEBUG_LOGGING)
  783: 
  784: #ifndef SUDOERS_NO_SEQ
  785:     /* A TSID may be a sudoers-style session ID or a free-form string. */
  786:     if (sudo_user.iolog_file != NULL) {
  787: 	if (IS_SESSID(sudo_user.iolog_file)) {
  788: 	    sessid[0] = sudo_user.iolog_file[0];
  789: 	    sessid[1] = sudo_user.iolog_file[1];
  790: 	    sessid[2] = sudo_user.iolog_file[3];
  791: 	    sessid[3] = sudo_user.iolog_file[4];
  792: 	    sessid[4] = sudo_user.iolog_file[6];
  793: 	    sessid[5] = sudo_user.iolog_file[7];
  794: 	    sessid[6] = '\0';
  795: 	    tsid = sessid;
  796: 	} else {
  797: 	    tsid = sudo_user.iolog_file;
  798: 	}
  799:     }
  800: #endif
  801: 
  802:     /*
  803:      * Compute line length
  804:      */
  805:     if (message != NULL)
  806: 	len += strlen(message) + 3;
  807:     if (serrno) {
  808: 	errstr = strerror(serrno);
  809: 	len += strlen(errstr) + 3;
  810:     }
  811:     len += sizeof(LL_TTY_STR) + 2 + strlen(user_tty);
  812:     len += sizeof(LL_CWD_STR) + 2 + strlen(user_cwd);
  813:     if (runas_pw != NULL)
  814: 	len += sizeof(LL_USER_STR) + 2 + strlen(runas_pw->pw_name);
  815:     if (runas_gr != NULL)
  816: 	len += sizeof(LL_GROUP_STR) + 2 + strlen(runas_gr->gr_name);
  817:     if (tsid != NULL)
  818: 	len += sizeof(LL_TSID_STR) + 2 + strlen(tsid);
  819:     if (sudo_user.env_vars != NULL) {
  820: 	size_t evlen = 0;
  821: 	char * const *ep;
  822: 
  823: 	for (ep = sudo_user.env_vars; *ep != NULL; ep++)
  824: 	    evlen += strlen(*ep) + 1;
  825: 	evstr = emalloc(evlen);
  826: 	evstr[0] = '\0';
  827: 	for (ep = sudo_user.env_vars; *ep != NULL; ep++) {
  828: 	    strlcat(evstr, *ep, evlen);
  829: 	    strlcat(evstr, " ", evlen);	/* NOTE: last one will fail */
  830: 	}
  831: 	len += sizeof(LL_ENV_STR) + 2 + evlen;
  832:     }
  833:     if (user_cmnd != NULL) {
  834: 	/* Note: we log "sudo -l command arg ..." as "list command arg ..." */
  835: 	len += sizeof(LL_CMND_STR) - 1 + strlen(user_cmnd);
  836: 	if (ISSET(sudo_mode, MODE_CHECK))
  837: 	    len += sizeof("list ") - 1;
  838: 	if (user_args != NULL)
  839: 	    len += strlen(user_args) + 1;
  840:     }
  841: 
  842:     /*
  843:      * Allocate and build up the line.
  844:      */
  845:     line = emalloc(++len);
  846:     line[0] = '\0';
  847: 
  848:     if (message != NULL) {
  849: 	if (strlcat(line, message, len) >= len ||
  850: 	    strlcat(line, errstr ? " : " : " ; ", len) >= len)
  851: 	    goto toobig;
  852:     }
  853:     if (serrno) {
  854: 	if (strlcat(line, errstr, len) >= len ||
  855: 	    strlcat(line, " ; ", len) >= len)
  856: 	    goto toobig;
  857:     }
  858:     if (strlcat(line, LL_TTY_STR, len) >= len ||
  859: 	strlcat(line, user_tty, len) >= len ||
  860: 	strlcat(line, " ; ", len) >= len)
  861: 	goto toobig;
  862:     if (strlcat(line, LL_CWD_STR, len) >= len ||
  863: 	strlcat(line, user_cwd, len) >= len ||
  864: 	strlcat(line, " ; ", len) >= len)
  865: 	goto toobig;
  866:     if (runas_pw != NULL) {
  867: 	if (strlcat(line, LL_USER_STR, len) >= len ||
  868: 	    strlcat(line, runas_pw->pw_name, len) >= len ||
  869: 	    strlcat(line, " ; ", len) >= len)
  870: 	    goto toobig;
  871:     }
  872:     if (runas_gr != NULL) {
  873: 	if (strlcat(line, LL_GROUP_STR, len) >= len ||
  874: 	    strlcat(line, runas_gr->gr_name, len) >= len ||
  875: 	    strlcat(line, " ; ", len) >= len)
  876: 	    goto toobig;
  877:     }
  878:     if (tsid != NULL) {
  879: 	if (strlcat(line, LL_TSID_STR, len) >= len ||
  880: 	    strlcat(line, tsid, len) >= len ||
  881: 	    strlcat(line, " ; ", len) >= len)
  882: 	    goto toobig;
  883:     }
  884:     if (evstr != NULL) {
  885: 	if (strlcat(line, LL_ENV_STR, len) >= len ||
  886: 	    strlcat(line, evstr, len) >= len ||
  887: 	    strlcat(line, " ; ", len) >= len)
  888: 	    goto toobig;
  889: 	efree(evstr);
  890:     }
  891:     if (user_cmnd != NULL) {
  892: 	if (strlcat(line, LL_CMND_STR, len) >= len)
  893: 	    goto toobig;
  894: 	if (ISSET(sudo_mode, MODE_CHECK) && strlcat(line, "list ", len) >= len)
  895: 	    goto toobig;
  896: 	if (strlcat(line, user_cmnd, len) >= len)
  897: 	    goto toobig;
  898: 	if (user_args != NULL) {
  899: 	    if (strlcat(line, " ", len) >= len ||
  900: 		strlcat(line, user_args, len) >= len)
  901: 		goto toobig;
  902: 	}
  903:     }
  904: 
  905:     debug_return_str(line);
  906: toobig:
  907:     fatalx(_("internal error: insufficient space for log line"));
  908: }

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