Annotation of embedaddon/sudo/plugins/sudoers/logging.c, revision 1.1.1.6
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: /* Special message for log_warning() so we know to use ngettext() */
63: #define INCORRECT_PASSWORD_ATTEMPT ((char *)0x01)
64:
1.1 misho 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:
1.1.1.3 misho 72: extern char **NewArgv; /* XXX - for auditing */
73:
1.1 misho 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;
1.1.1.2 misho 92: debug_decl(mysyslog, SUDO_DEBUG_LOGGING)
1.1 misho 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();
1.1.1.2 misho 115: debug_return;
1.1 misho 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;
1.1.1.4 misho 128: int oldlocale;
1.1.1.2 misho 129: debug_decl(do_syslog, SUDO_DEBUG_LOGGING)
1.1 misho 130:
1.1.1.4 misho 131: sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
1.1 misho 132:
133: /*
134: * Log the full line, breaking into multiple syslog(3) calls if necessary
135: */
1.1.1.4 misho 136: fmt = _("%8s : %s");
1.1 misho 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: }
1.1.1.4 misho 164: fmt = _("%8s : (command continued) %s");
1.1 misho 165: maxlen = MAXSYSLOGLEN - (strlen(fmt) - 5 + strlen(user_name));
166: }
167:
1.1.1.4 misho 168: sudoers_setlocale(oldlocale, NULL);
1.1.1.2 misho 169:
170: debug_return;
1.1 misho 171: }
172:
173: static void
174: do_logfile(char *msg)
175: {
176: char *full_line;
177: size_t len;
178: mode_t oldmask;
1.1.1.4 misho 179: int oldlocale;
1.1 misho 180: FILE *fp;
1.1.1.2 misho 181: debug_decl(do_logfile, SUDO_DEBUG_LOGGING)
1.1 misho 182:
1.1.1.4 misho 183: sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
184:
1.1 misho 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 {
1.1.1.6 ! misho 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)) {
1.1 misho 199: /* Don't pretty-print long log file lines (hard to grep) */
1.1.1.5 misho 200: if (def_log_host) {
1.1 misho 201: (void) fprintf(fp, "%s : %s : HOST=%s : %s\n",
1.1.1.6 ! misho 202: timestr, user_name, user_srunhost, msg);
1.1.1.5 misho 203: } else {
1.1.1.6 ! misho 204: (void) fprintf(fp, "%s : %s : %s\n", timestr, user_name, msg);
1.1.1.5 misho 205: }
1.1 misho 206: } else {
1.1.1.5 misho 207: if (def_log_host) {
1.1 misho 208: len = easprintf(&full_line, "%s : %s : HOST=%s : %s",
1.1.1.6 ! misho 209: timestr, user_name, user_srunhost, msg);
1.1.1.5 misho 210: } else {
1.1 misho 211: len = easprintf(&full_line, "%s : %s : %s",
1.1.1.6 ! misho 212: timestr, user_name, msg);
1.1.1.5 misho 213: }
1.1 misho 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: }
1.1.1.4 misho 225: sudoers_setlocale(oldlocale, NULL);
226:
1.1.1.2 misho 227: debug_return;
1.1 misho 228: }
229:
230: /*
1.1.1.4 misho 231: * Log, audit and mail the denial message, optionally informing the user.
1.1 misho 232: */
1.1.1.4 misho 233: void
1.1.1.3 misho 234: log_denial(int status, bool inform_user)
1.1 misho 235: {
1.1.1.4 misho 236: const char *message;
237: char *logline;
238: int oldlocale;
1.1.1.2 misho 239: debug_decl(log_denial, SUDO_DEBUG_LOGGING)
1.1 misho 240:
1.1.1.4 misho 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:
1.1 misho 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:
1.1.1.4 misho 260: /* Become root if we are not already. */
261: set_perms(PERM_ROOT|PERM_NOEXIT);
262:
1.1 misho 263: if (should_mail(status))
264: send_mail("%s", logline); /* send mail based on status */
265:
1.1.1.4 misho 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). */
1.1 misho 282: if (inform_user) {
1.1.1.4 misho 283: sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale);
284:
1.1 misho 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"),
1.1.1.5 misho 291: user_name, user_srunhost);
1.1 misho 292: } else if (ISSET(status, FLAG_NO_CHECK)) {
293: sudo_printf(SUDO_CONV_ERROR_MSG, _("Sorry, user %s may not run "
1.1.1.5 misho 294: "sudo on %s.\n"), user_name, user_srunhost);
1.1 misho 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: }
1.1.1.4 misho 304: sudoers_setlocale(oldlocale, NULL);
1.1 misho 305: }
1.1.1.2 misho 306: debug_return;
1.1 misho 307: }
308:
309: /*
1.1.1.3 misho 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;
1.1.1.4 misho 316: debug_decl(log_failure, SUDO_DEBUG_LOGGING)
1.1.1.3 misho 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)
1.1.1.6 ! misho 333: warningx(U_("%s: command not found"), user_cmnd);
1.1.1.3 misho 334: else if (flags == NOT_FOUND_DOT)
1.1.1.6 ! misho 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);
1.1.1.3 misho 336: }
337:
338: debug_return;
339: }
340:
341: /*
342: * Log and audit that user was not able to authenticate themselves.
343: */
344: void
1.1.1.6 ! misho 345: log_auth_failure(int status, unsigned int tries)
1.1.1.3 misho 346: {
347: int flags = NO_MAIL;
348: debug_decl(log_auth_failure, SUDO_DEBUG_LOGGING)
349:
350: /* Handle auditing first. */
1.1.1.4 misho 351: audit_failure(NewArgv, N_("authentication failure"));
1.1.1.3 misho 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: */
1.1.1.4 misho 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"));
1.1.1.3 misho 378:
379: debug_return;
380: }
381:
382: /*
1.1 misho 383: * Log and potentially mail the allowed command.
384: */
385: void
386: log_allowed(int status)
387: {
388: char *logline;
1.1.1.4 misho 389: int oldlocale;
1.1.1.2 misho 390: debug_decl(log_allowed, SUDO_DEBUG_LOGGING)
1.1 misho 391:
1.1.1.4 misho 392: /* Log and mail messages should be in the sudoers locale. */
393: sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
394:
1.1 misho 395: logline = new_logline(NULL, 0);
396:
1.1.1.4 misho 397: /* Become root if we are not already. */
398: set_perms(PERM_ROOT|PERM_NOEXIT);
399:
1.1 misho 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:
1.1.1.4 misho 411: restore_perms();
412:
1.1 misho 413: efree(logline);
1.1.1.4 misho 414:
415: sudoers_setlocale(oldlocale, NULL);
416:
1.1.1.2 misho 417: debug_return;
1.1 misho 418: }
419:
1.1.1.2 misho 420: /*
1.1.1.4 misho 421: * Perform logging for log_warning()/log_fatal()
1.1.1.2 misho 422: */
423: static void
1.1.1.4 misho 424: vlog_warning(int flags, const char *fmt, va_list ap)
1.1 misho 425: {
1.1.1.4 misho 426: int oldlocale, serrno = errno;
1.1.1.2 misho 427: char *logline, *message;
1.1.1.4 misho 428: va_list ap2;
1.1.1.2 misho 429: debug_decl(vlog_error, SUDO_DEBUG_LOGGING)
1.1 misho 430:
1.1.1.4 misho 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) {
1.1.1.6 ! misho 440: unsigned int tries = va_arg(ap, unsigned int);
! 441: easprintf(&message, ngettext("%u incorrect password attempt",
! 442: "%u incorrect password attempts", tries), tries);
1.1.1.4 misho 443: } else {
444: evasprintf(&message, _(fmt), ap);
445: }
1.1 misho 446:
1.1.1.6 ! misho 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:
1.1.1.4 misho 456: if (ISSET(flags, MSG_ONLY)) {
1.1 misho 457: logline = message;
1.1.1.4 misho 458: } else {
1.1 misho 459: logline = new_logline(message, ISSET(flags, USE_ERRNO) ? serrno : 0);
460: efree(message);
1.1.1.4 misho 461: }
462:
463: /* Become root if we are not already. */
464: set_perms(PERM_ROOT|PERM_NOEXIT);
1.1 misho 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: */
1.1.1.3 misho 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: }
1.1 misho 481:
1.1.1.4 misho 482: restore_perms();
483:
1.1 misho 484: efree(logline);
485:
1.1.1.4 misho 486: sudoers_setlocale(oldlocale, NULL);
487:
488: /*
489: * Tell the user (in their locale).
490: */
491: if (!ISSET(flags, NO_STDERR)) {
1.1.1.6 ! misho 492: sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale);
1.1.1.4 misho 493: if (fmt == INCORRECT_PASSWORD_ATTEMPT) {
1.1.1.6 ! misho 494: unsigned int tries = va_arg(ap2, unsigned int);
! 495: warningx_nodebug(ngettext("%u incorrect password attempt",
! 496: "%u incorrect password attempts", tries), tries);
1.1.1.4 misho 497: } else {
498: if (ISSET(flags, USE_ERRNO))
1.1.1.6 ! misho 499: vwarning_nodebug(_(fmt), ap2);
1.1.1.4 misho 500: else
1.1.1.6 ! misho 501: vwarningx_nodebug(_(fmt), ap2);
1.1.1.4 misho 502: }
1.1.1.6 ! misho 503: sudoers_setlocale(oldlocale, NULL);
1.1.1.4 misho 504: va_end(ap2);
505: }
1.1 misho 506:
1.1.1.2 misho 507: debug_return;
508: }
509:
510: void
1.1.1.4 misho 511: log_warning(int flags, const char *fmt, ...)
1.1.1.2 misho 512: {
513: va_list ap;
514: debug_decl(log_error, SUDO_DEBUG_LOGGING)
515:
516: /* Log the error. */
517: va_start(ap, fmt);
1.1.1.4 misho 518: vlog_warning(flags, fmt, ap);
1.1.1.2 misho 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);
1.1.1.4 misho 532: vlog_warning(flags, fmt, ap);
1.1.1.2 misho 533: va_end(ap);
534:
535: /* Exit the plugin. */
1.1.1.4 misho 536: sudoers_cleanup();
1.1.1.2 misho 537: sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
1.1.1.4 misho 538: fatal_longjmp(1);
1.1 misho 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;
1.1.1.6 ! misho 551: const char *timestr;
1.1 misho 552: int fd, pfd[2], status;
553: pid_t pid, rv;
554: sigaction_t sa;
1.1.1.5 misho 555: struct stat sb;
1.1 misho 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 */
1.1.1.2 misho 567: debug_decl(send_mail, SUDO_DEBUG_LOGGING)
1.1 misho 568:
569: /* Just return if mailer is disabled. */
570: if (!def_mailerpath || !def_mailto)
1.1.1.2 misho 571: debug_return;
1.1 misho 572:
1.1.1.5 misho 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:
1.1 misho 577: /* Fork and return, child will daemonize. */
1.1.1.2 misho 578: switch (pid = sudo_debug_fork()) {
1.1 misho 579: case -1:
580: /* Error. */
1.1.1.6 ! misho 581: fatal(U_("unable to fork"));
1.1 misho 582: break;
583: case 0:
584: /* Child. */
585: switch (pid = fork()) {
586: case -1:
587: /* Error. */
588: mysyslog(LOG_ERR, _("unable to fork: %m"));
1.1.1.2 misho 589: sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to fork: %s",
590: strerror(errno));
1.1 misho 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);
1.1.1.2 misho 605: return; /* not debug */
1.1 misho 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:
1.1.1.4 misho 619: sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, NULL);
1.1 misho 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). */
1.1.1.5 misho 627: memset(&sa, 0, sizeof(sa));
1.1 misho 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"));
1.1.1.2 misho 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);
1.1 misho 638: _exit(1);
639: }
640:
1.1.1.2 misho 641: switch (pid = sudo_debug_fork()) {
1.1 misho 642: case -1:
643: /* Error. */
644: mysyslog(LOG_ERR, _("unable to fork: %m"));
1.1.1.2 misho 645: sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to fork: %s",
646: strerror(errno));
647: sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
1.1 misho 648: _exit(1);
649: break;
650: case 0:
651: {
652: char *argv[MAX_MAILFLAGS + 1];
1.1.1.5 misho 653: char *mflags, *mpath = def_mailerpath;
1.1 misho 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"));
1.1.1.2 misho 660: sudo_debug_printf(SUDO_DEBUG_ERROR,
661: "unable to dup stdin: %s", strerror(errno));
1.1 misho 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);
1.1.1.5 misho 670: if ((argv[0] = strrchr(mpath, '/')))
1.1 misho 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);
1.1.1.2 misho 695: sudo_debug_printf(SUDO_DEBUG_ERROR, "unable to execute %s: %s",
696: mpath, strerror(errno));
1.1 misho 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");
1.1.1.4 misho 708: for (p = _(def_mailsub); *p; p++) {
1.1 misho 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:
1.1.1.6 ! misho 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);
1.1 misho 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);
1.1.1.2 misho 743: sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);
1.1 misho 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: {
1.1.1.2 misho 753: debug_decl(should_mail, SUDO_DEBUG_LOGGING)
1.1 misho 754:
1.1.1.2 misho 755: debug_return_bool(def_mail_always || ISSET(status, VALIDATE_ERROR) ||
1.1 misho 756: (def_mail_no_user && ISSET(status, FLAG_NO_USER)) ||
757: (def_mail_no_host && ISSET(status, FLAG_NO_HOST)) ||
1.1.1.2 misho 758: (def_mail_no_perms && !ISSET(status, VALIDATE_OK)));
1.1 misho 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: {
1.1.1.4 misho 783: char *line, *errstr = NULL, *evstr = NULL;
784: #ifndef SUDOERS_NO_SEQ
785: char sessid[7];
786: #endif
787: const char *tsid = NULL;
1.1 misho 788: size_t len = 0;
1.1.1.2 misho 789: debug_decl(new_logline, SUDO_DEBUG_LOGGING)
1.1 misho 790:
1.1.1.4 misho 791: #ifndef SUDOERS_NO_SEQ
1.1 misho 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: }
1.1.1.4 misho 807: #endif
1.1 misho 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:
1.1.1.2 misho 912: debug_return_str(line);
1.1 misho 913: toobig:
1.1.1.6 ! misho 914: fatalx(U_("internal error: insufficient space for log line"));
1.1 misho 915: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>