Annotation of embedaddon/sudo/plugins/sudoers/logging.c, revision 1.1.1.5
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) */
1.1.1.5 ! misho 203: if (def_log_host) {
1.1 misho 204: (void) fprintf(fp, "%s : %s : HOST=%s : %s\n",
1.1.1.5 ! misho 205: get_timestr(now, def_log_year), user_name, user_srunhost,
! 206: msg);
! 207: } else {
1.1 misho 208: (void) fprintf(fp, "%s : %s : %s\n",
209: get_timestr(now, def_log_year), user_name, msg);
1.1.1.5 ! misho 210: }
1.1 misho 211: } else {
1.1.1.5 ! misho 212: if (def_log_host) {
1.1 misho 213: len = easprintf(&full_line, "%s : %s : HOST=%s : %s",
1.1.1.5 ! misho 214: get_timestr(now, def_log_year), user_name, user_srunhost,
! 215: msg);
! 216: } else {
1.1 misho 217: len = easprintf(&full_line, "%s : %s : %s",
218: get_timestr(now, def_log_year), user_name, msg);
1.1.1.5 ! misho 219: }
1.1 misho 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: }
1.1.1.4 misho 231: sudoers_setlocale(oldlocale, NULL);
232:
1.1.1.2 misho 233: debug_return;
1.1 misho 234: }
235:
236: /*
1.1.1.4 misho 237: * Log, audit and mail the denial message, optionally informing the user.
1.1 misho 238: */
1.1.1.4 misho 239: void
1.1.1.3 misho 240: log_denial(int status, bool inform_user)
1.1 misho 241: {
1.1.1.4 misho 242: const char *message;
243: char *logline;
244: int oldlocale;
1.1.1.2 misho 245: debug_decl(log_denial, SUDO_DEBUG_LOGGING)
1.1 misho 246:
1.1.1.4 misho 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:
1.1 misho 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:
1.1.1.4 misho 266: /* Become root if we are not already. */
267: set_perms(PERM_ROOT|PERM_NOEXIT);
268:
1.1 misho 269: if (should_mail(status))
270: send_mail("%s", logline); /* send mail based on status */
271:
1.1.1.4 misho 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). */
1.1 misho 288: if (inform_user) {
1.1.1.4 misho 289: sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale);
290:
1.1 misho 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"),
1.1.1.5 ! misho 297: user_name, user_srunhost);
1.1 misho 298: } else if (ISSET(status, FLAG_NO_CHECK)) {
299: sudo_printf(SUDO_CONV_ERROR_MSG, _("Sorry, user %s may not run "
1.1.1.5 ! misho 300: "sudo on %s.\n"), user_name, user_srunhost);
1.1 misho 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: }
1.1.1.4 misho 310: sudoers_setlocale(oldlocale, NULL);
1.1 misho 311: }
1.1.1.2 misho 312: debug_return;
1.1 misho 313: }
314:
315: /*
1.1.1.3 misho 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;
1.1.1.4 misho 322: debug_decl(log_failure, SUDO_DEBUG_LOGGING)
1.1.1.3 misho 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. */
1.1.1.4 misho 357: audit_failure(NewArgv, N_("authentication failure"));
1.1.1.3 misho 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: */
1.1.1.4 misho 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"));
1.1.1.3 misho 384:
385: debug_return;
386: }
387:
388: /*
1.1 misho 389: * Log and potentially mail the allowed command.
390: */
391: void
392: log_allowed(int status)
393: {
394: char *logline;
1.1.1.4 misho 395: int oldlocale;
1.1.1.2 misho 396: debug_decl(log_allowed, SUDO_DEBUG_LOGGING)
1.1 misho 397:
1.1.1.4 misho 398: /* Log and mail messages should be in the sudoers locale. */
399: sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
400:
1.1 misho 401: logline = new_logline(NULL, 0);
402:
1.1.1.4 misho 403: /* Become root if we are not already. */
404: set_perms(PERM_ROOT|PERM_NOEXIT);
405:
1.1 misho 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:
1.1.1.4 misho 417: restore_perms();
418:
1.1 misho 419: efree(logline);
1.1.1.4 misho 420:
421: sudoers_setlocale(oldlocale, NULL);
422:
1.1.1.2 misho 423: debug_return;
1.1 misho 424: }
425:
1.1.1.2 misho 426: /*
1.1.1.4 misho 427: * Perform logging for log_warning()/log_fatal()
1.1.1.2 misho 428: */
429: static void
1.1.1.4 misho 430: vlog_warning(int flags, const char *fmt, va_list ap)
1.1 misho 431: {
1.1.1.4 misho 432: int oldlocale, serrno = errno;
1.1.1.2 misho 433: char *logline, *message;
1.1.1.4 misho 434: va_list ap2;
1.1.1.2 misho 435: debug_decl(vlog_error, SUDO_DEBUG_LOGGING)
1.1 misho 436:
1.1.1.4 misho 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: }
1.1 misho 452:
1.1.1.4 misho 453: if (ISSET(flags, MSG_ONLY)) {
1.1 misho 454: logline = message;
1.1.1.4 misho 455: } else {
1.1 misho 456: logline = new_logline(message, ISSET(flags, USE_ERRNO) ? serrno : 0);
457: efree(message);
1.1.1.4 misho 458: }
459:
460: /* Become root if we are not already. */
461: set_perms(PERM_ROOT|PERM_NOEXIT);
1.1 misho 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: */
1.1.1.3 misho 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: }
1.1 misho 478:
1.1.1.4 misho 479: restore_perms();
480:
1.1 misho 481: efree(logline);
482:
1.1.1.4 misho 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: }
1.1 misho 501:
1.1.1.2 misho 502: debug_return;
503: }
504:
505: void
1.1.1.4 misho 506: log_warning(int flags, const char *fmt, ...)
1.1.1.2 misho 507: {
508: va_list ap;
509: debug_decl(log_error, SUDO_DEBUG_LOGGING)
510:
511: /* Log the error. */
512: va_start(ap, fmt);
1.1.1.4 misho 513: vlog_warning(flags, fmt, ap);
1.1.1.2 misho 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);
1.1.1.4 misho 527: vlog_warning(flags, fmt, ap);
1.1.1.2 misho 528: va_end(ap);
529:
530: /* Exit the plugin. */
1.1.1.4 misho 531: sudoers_cleanup();
1.1.1.2 misho 532: sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
1.1.1.4 misho 533: fatal_longjmp(1);
1.1 misho 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;
1.1.1.5 ! misho 549: struct stat sb;
1.1 misho 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 */
1.1.1.2 misho 561: debug_decl(send_mail, SUDO_DEBUG_LOGGING)
1.1 misho 562:
563: /* Just return if mailer is disabled. */
564: if (!def_mailerpath || !def_mailto)
1.1.1.2 misho 565: debug_return;
1.1 misho 566:
1.1.1.5 ! misho 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:
1.1 misho 571: /* Fork and return, child will daemonize. */
1.1.1.2 misho 572: switch (pid = sudo_debug_fork()) {
1.1 misho 573: case -1:
574: /* Error. */
1.1.1.4 misho 575: fatal(_("unable to fork"));
1.1 misho 576: break;
577: case 0:
578: /* Child. */
579: switch (pid = fork()) {
580: case -1:
581: /* Error. */
582: mysyslog(LOG_ERR, _("unable to fork: %m"));
1.1.1.2 misho 583: sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to fork: %s",
584: strerror(errno));
1.1 misho 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);
1.1.1.2 misho 599: return; /* not debug */
1.1 misho 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:
1.1.1.4 misho 613: sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, NULL);
1.1 misho 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). */
1.1.1.5 ! misho 621: memset(&sa, 0, sizeof(sa));
1.1 misho 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"));
1.1.1.2 misho 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);
1.1 misho 632: _exit(1);
633: }
634:
1.1.1.2 misho 635: switch (pid = sudo_debug_fork()) {
1.1 misho 636: case -1:
637: /* Error. */
638: mysyslog(LOG_ERR, _("unable to fork: %m"));
1.1.1.2 misho 639: sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to fork: %s",
640: strerror(errno));
641: sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
1.1 misho 642: _exit(1);
643: break;
644: case 0:
645: {
646: char *argv[MAX_MAILFLAGS + 1];
1.1.1.5 ! misho 647: char *mflags, *mpath = def_mailerpath;
1.1 misho 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"));
1.1.1.2 misho 654: sudo_debug_printf(SUDO_DEBUG_ERROR,
655: "unable to dup stdin: %s", strerror(errno));
1.1 misho 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);
1.1.1.5 ! misho 664: if ((argv[0] = strrchr(mpath, '/')))
1.1 misho 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);
1.1.1.2 misho 689: sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to execute %s: %s",
690: mpath, strerror(errno));
1.1 misho 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");
1.1.1.4 misho 702: for (p = _(def_mailsub); *p; p++) {
1.1 misho 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);
1.1.1.2 misho 736: sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
1.1 misho 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: {
1.1.1.2 misho 746: debug_decl(should_mail, SUDO_DEBUG_LOGGING)
1.1 misho 747:
1.1.1.2 misho 748: debug_return_bool(def_mail_always || ISSET(status, VALIDATE_ERROR) ||
1.1 misho 749: (def_mail_no_user && ISSET(status, FLAG_NO_USER)) ||
750: (def_mail_no_host && ISSET(status, FLAG_NO_HOST)) ||
1.1.1.2 misho 751: (def_mail_no_perms && !ISSET(status, VALIDATE_OK)));
1.1 misho 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: {
1.1.1.4 misho 776: char *line, *errstr = NULL, *evstr = NULL;
777: #ifndef SUDOERS_NO_SEQ
778: char sessid[7];
779: #endif
780: const char *tsid = NULL;
1.1 misho 781: size_t len = 0;
1.1.1.2 misho 782: debug_decl(new_logline, SUDO_DEBUG_LOGGING)
1.1 misho 783:
1.1.1.4 misho 784: #ifndef SUDOERS_NO_SEQ
1.1 misho 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: }
1.1.1.4 misho 800: #endif
1.1 misho 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:
1.1.1.2 misho 905: debug_return_str(line);
1.1 misho 906: toobig:
1.1.1.4 misho 907: fatalx(_("internal error: insufficient space for log line"));
1.1 misho 908: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>