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