File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers / logging.c
Revision 1.1.1.6 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 16:12:54 2014 UTC (10 years, 2 months ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_10p3_0, v1_8_10p3, HEAD
sudo v 1.8.10p3

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

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