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