Annotation of src/usr.bin/sudo/logging.c, Revision 1.16
1.1 millert 1: /*
1.15 millert 2: * Copyright (c) 1994-1996,1998-2004 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 3: *
1.15 millert 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.
1.1 millert 7: *
1.15 millert 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.
1.13 millert 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.
1.1 millert 19: */
20:
1.15 millert 21: #ifdef __TANDEM
22: # include <floss.h>
23: #endif
24:
1.1 millert 25: #include "config.h"
26:
1.5 millert 27: #include <sys/types.h>
28: #include <sys/param.h>
29: #include <sys/stat.h>
30: #include <sys/wait.h>
1.1 millert 31: #include <stdio.h>
32: #ifdef STDC_HEADERS
1.5 millert 33: # include <stdlib.h>
34: # include <stddef.h>
35: #else
36: # ifdef HAVE_STDLIB_H
37: # include <stdlib.h>
38: # endif
1.1 millert 39: #endif /* STDC_HEADERS */
1.5 millert 40: #ifdef HAVE_STRING_H
41: # include <string.h>
42: #else
43: # ifdef HAVE_STRINGS_H
44: # include <strings.h>
45: # endif
46: #endif /* HAVE_STRING_H */
1.1 millert 47: #ifdef HAVE_UNISTD_H
1.5 millert 48: # include <unistd.h>
1.1 millert 49: #endif /* HAVE_UNISTD_H */
1.12 millert 50: #ifdef HAVE_ERR_H
51: # include <err.h>
52: #else
53: # include "emul/err.h"
54: #endif /* HAVE_ERR_H */
1.1 millert 55: #include <pwd.h>
56: #include <signal.h>
57: #include <time.h>
58: #include <errno.h>
59:
60: #include "sudo.h"
61:
62: #ifndef lint
1.15 millert 63: static const char rcsid[] = "$Sudo: logging.c,v 1.168 2004/05/17 20:08:46 millert Exp $";
1.1 millert 64: #endif /* lint */
65:
66: static void do_syslog __P((int, char *));
67: static void do_logfile __P((char *));
68: static void send_mail __P((char *));
69: static void mail_auth __P((int, char *));
70: static char *get_timestr __P((void));
1.5 millert 71: static void mysyslog __P((int, const char *, ...));
1.1 millert 72:
1.5 millert 73: #define MAXSYSLOGTRIES 16 /* num of retries for broken syslogs */
1.1 millert 74:
75: /*
1.5 millert 76: * We do an openlog(3)/closelog(3) for each message because some
77: * authentication methods (notably PAM) use syslog(3) for their
78: * own nefarious purposes and may call openlog(3) and closelog(3).
79: * Note that because we don't want to assume that all systems have
80: * vsyslog(3) (HP-UX doesn't) "%m" will not be expanded.
81: * Sadly this is a maze of #ifdefs.
1.1 millert 82: */
83: static void
1.5 millert 84: #ifdef __STDC__
85: mysyslog(int pri, const char *fmt, ...)
86: #else
87: mysyslog(pri, fmt, va_alist)
1.1 millert 88: int pri;
89: const char *fmt;
1.5 millert 90: va_dcl
91: #endif
1.1 millert 92: {
1.5 millert 93: #ifdef BROKEN_SYSLOG
1.1 millert 94: int i;
1.5 millert 95: #endif
96: char buf[MAXSYSLOGLEN+1];
97: va_list ap;
1.1 millert 98:
1.5 millert 99: #ifdef __STDC__
100: va_start(ap, fmt);
101: #else
102: va_start(ap);
103: #endif
104: #ifdef LOG_NFACILITIES
1.15 millert 105: openlog("sudo", 0, def_syslog);
1.5 millert 106: #else
1.12 millert 107: openlog("sudo", 0);
1.5 millert 108: #endif
109: vsnprintf(buf, sizeof(buf), fmt, ap);
110: #ifdef BROKEN_SYSLOG
111: /*
112: * Some versions of syslog(3) don't guarantee success and return
113: * an int (notably HP-UX < 10.0). So, if at first we don't succeed,
114: * try, try again...
115: */
1.1 millert 116: for (i = 0; i < MAXSYSLOGTRIES; i++)
1.5 millert 117: if (syslog(pri, "%s", buf) == 0)
1.1 millert 118: break;
119: #else
1.5 millert 120: syslog(pri, "%s", buf);
1.1 millert 121: #endif /* BROKEN_SYSLOG */
1.5 millert 122: va_end(ap);
123: closelog();
124: }
1.1 millert 125:
126: /*
127: * Log a message to syslog, pre-pending the username and splitting the
128: * message into parts if it is longer than MAXSYSLOGLEN.
129: */
130: static void
131: do_syslog(pri, msg)
132: int pri;
133: char *msg;
134: {
1.9 millert 135: size_t count;
1.1 millert 136: char *p;
137: char *tmp;
138: char save;
139:
140: /*
141: * Log the full line, breaking into multiple syslog(3) calls if necessary
142: */
1.3 millert 143: for (p = msg, count = 0; *p && count < strlen(msg) / MAXSYSLOGLEN + 1;
144: count++) {
1.1 millert 145: if (strlen(p) > MAXSYSLOGLEN) {
146: /*
147: * Break up the line into what will fit on one syslog(3) line
148: * Try to break on a word boundary if possible.
149: */
150: for (tmp = p + MAXSYSLOGLEN; tmp > p && *tmp != ' '; tmp--)
151: ;
152: if (tmp <= p)
153: tmp = p + MAXSYSLOGLEN;
154:
155: /* NULL terminate line, but save the char to restore later */
156: save = *tmp;
157: *tmp = '\0';
158:
159: if (count == 0)
1.15 millert 160: mysyslog(pri, "%8s : %s", user_name, p);
1.1 millert 161: else
1.15 millert 162: mysyslog(pri, "%8s : (command continued) %s", user_name, p);
1.1 millert 163:
164: *tmp = save; /* restore saved character */
165:
166: /* Eliminate leading whitespace */
1.3 millert 167: for (p = tmp; *p != ' ' && *p !='\0'; p++)
1.1 millert 168: ;
169: } else {
170: if (count == 0)
1.15 millert 171: mysyslog(pri, "%8s : %s", user_name, p);
1.1 millert 172: else
1.15 millert 173: mysyslog(pri, "%8s : (command continued) %s", user_name, p);
1.1 millert 174: }
175: }
176: }
177:
178: static void
179: do_logfile(msg)
180: char *msg;
181: {
182: char *full_line;
183: char *beg, *oldend, *end;
184: FILE *fp;
185: mode_t oldmask;
1.9 millert 186: size_t maxlen;
1.1 millert 187:
188: oldmask = umask(077);
1.15 millert 189: maxlen = def_loglinelen > 0 ? def_loglinelen : 0;
190: fp = fopen(def_logfile, "a");
1.1 millert 191: (void) umask(oldmask);
192: if (fp == NULL) {
193: easprintf(&full_line, "Can't open log file: %s: %s",
1.15 millert 194: def_logfile, strerror(errno));
1.1 millert 195: send_mail(full_line);
196: free(full_line);
197: } else if (!lock_file(fileno(fp), SUDO_LOCK)) {
198: easprintf(&full_line, "Can't lock log file: %s: %s",
1.15 millert 199: def_logfile, strerror(errno));
1.1 millert 200: send_mail(full_line);
201: free(full_line);
202: } else {
1.15 millert 203: if (def_loglinelen == 0) {
1.1 millert 204: /* Don't pretty-print long log file lines (hard to grep) */
1.15 millert 205: if (def_log_host)
1.1 millert 206: (void) fprintf(fp, "%s : %s : HOST=%s : %s\n", get_timestr(),
207: user_name, user_shost, msg);
208: else
209: (void) fprintf(fp, "%s : %s : %s\n", get_timestr(),
210: user_name, msg);
211: } else {
1.15 millert 212: if (def_log_host)
1.1 millert 213: easprintf(&full_line, "%s : %s : HOST=%s : %s", get_timestr(),
214: user_name, user_shost, msg);
215: else
216: easprintf(&full_line, "%s : %s : %s", get_timestr(),
217: user_name, msg);
218:
219: /*
220: * Print out full_line with word wrap
221: */
222: beg = end = full_line;
223: while (beg) {
224: oldend = end;
225: end = strchr(oldend, ' ');
226:
227: if (maxlen > 0 && end) {
228: *end = '\0';
229: if (strlen(beg) > maxlen) {
230: /* too far, need to back up & print the line */
231:
232: if (beg == (char *)full_line)
233: maxlen -= 4; /* don't indent first line */
234:
235: *end = ' ';
236: if (oldend != beg) {
237: /* rewind & print */
238: end = oldend-1;
239: while (*end == ' ')
240: --end;
241: *(++end) = '\0';
242: (void) fprintf(fp, "%s\n ", beg);
243: *end = ' ';
244: } else {
245: (void) fprintf(fp, "%s\n ", beg);
246: }
247:
248: /* reset beg to point to the start of the new substr */
249: beg = end;
250: while (*beg == ' ')
251: ++beg;
252: } else {
253: /* we still have room */
254: *end = ' ';
255: }
256:
257: /* remove leading whitespace */
258: while (*end == ' ')
259: ++end;
260: } else {
261: /* final line */
262: (void) fprintf(fp, "%s\n", beg);
263: beg = NULL; /* exit condition */
264: }
265: }
266: free(full_line);
267: }
268: (void) fflush(fp);
269: (void) lock_file(fileno(fp), SUDO_UNLOCK);
270: (void) fclose(fp);
271: }
272: }
273:
274: /*
275: * Two main functions, log_error() to log errors and log_auth() to
276: * log allow/deny messages.
277: */
278: void
279: log_auth(status, inform_user)
280: int status;
281: int inform_user;
282: {
283: char *message;
284: char *logline;
285: int pri;
286:
1.15 millert 287: if (ISSET(status, VALIDATE_OK))
288: pri = def_syslog_goodpri;
1.1 millert 289: else
1.15 millert 290: pri = def_syslog_badpri;
1.1 millert 291:
292: /* Set error message, if any. */
1.15 millert 293: if (ISSET(status, VALIDATE_OK))
1.1 millert 294: message = "";
1.15 millert 295: else if (ISSET(status, FLAG_NO_USER))
1.1 millert 296: message = "user NOT in sudoers ; ";
1.15 millert 297: else if (ISSET(status, FLAG_NO_HOST))
1.1 millert 298: message = "user NOT authorized on host ; ";
1.15 millert 299: else if (ISSET(status, VALIDATE_NOT_OK))
1.1 millert 300: message = "command not allowed ; ";
301: else
302: message = "unknown error ; ";
303:
304: easprintf(&logline, "%sTTY=%s ; PWD=%s ; USER=%s ; COMMAND=%s%s%s",
305: message, user_tty, user_cwd, *user_runas, user_cmnd,
306: user_args ? " " : "", user_args ? user_args : "");
307:
308: mail_auth(status, logline); /* send mail based on status */
309:
310: /* Inform the user if they failed to authenticate. */
1.15 millert 311: if (inform_user && ISSET(status, VALIDATE_NOT_OK)) {
312: if (ISSET(status, FLAG_NO_USER))
1.1 millert 313: (void) fprintf(stderr, "%s is not in the sudoers file. %s",
314: user_name, "This incident will be reported.\n");
1.15 millert 315: else if (ISSET(status, FLAG_NO_HOST))
1.1 millert 316: (void) fprintf(stderr, "%s is not allowed to run sudo on %s. %s",
317: user_name, user_shost, "This incident will be reported.\n");
1.15 millert 318: else if (ISSET(status, FLAG_NO_CHECK))
1.1 millert 319: (void) fprintf(stderr, "Sorry, user %s may not run sudo on %s.\n",
320: user_name, user_shost);
321: else
322: (void) fprintf(stderr,
323: "Sorry, user %s is not allowed to execute '%s%s%s' as %s on %s.\n",
324: user_name, user_cmnd, user_args ? " " : "",
325: user_args ? user_args : "", *user_runas, user_host);
326: }
327:
328: /*
329: * Log via syslog and/or a file.
330: */
1.15 millert 331: if (def_syslog)
1.1 millert 332: do_syslog(pri, logline);
1.15 millert 333: if (def_logfile)
1.1 millert 334: do_logfile(logline);
335:
336: free(logline);
337: }
338:
339: void
340: #ifdef __STDC__
341: log_error(int flags, const char *fmt, ...)
342: #else
343: log_error(va_alist)
344: va_dcl
345: #endif
346: {
347: int serrno = errno;
348: char *message;
349: char *logline;
350: va_list ap;
351: #ifdef __STDC__
352: va_start(ap, fmt);
353: #else
354: int flags;
355: const char *fmt;
356:
357: va_start(ap);
358: flags = va_arg(ap, int);
359: fmt = va_arg(ap, const char *);
360: #endif
361:
362: /* Become root if we are not already to avoid user control */
363: if (geteuid() != 0)
1.9 millert 364: set_perms(PERM_ROOT);
1.1 millert 365:
366: /* Expand printf-style format + args. */
367: evasprintf(&message, fmt, ap);
368: va_end(ap);
369:
370: if (flags & MSG_ONLY)
371: logline = message;
372: else if (flags & USE_ERRNO) {
373: if (user_args) {
374: easprintf(&logline,
375: "%s: %s ; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=%s %s",
376: message, strerror(serrno), user_tty, user_cwd, *user_runas,
377: user_cmnd, user_args);
378: } else {
379: easprintf(&logline,
380: "%s: %s ; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=%s", message,
381: strerror(serrno), user_tty, user_cwd, *user_runas, user_cmnd);
382: }
383: } else {
384: if (user_args) {
385: easprintf(&logline,
386: "%s ; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=%s %s", message,
387: user_tty, user_cwd, *user_runas, user_cmnd, user_args);
388: } else {
389: easprintf(&logline,
390: "%s ; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=%s", message,
391: user_tty, user_cwd, *user_runas, user_cmnd);
392: }
393: }
394:
395: /*
396: * Tell the user.
397: */
398: if (flags & USE_ERRNO)
1.12 millert 399: warn("%s", message);
400: else
401: warnx("%s", message);
1.1 millert 402:
403: /*
404: * Send a copy of the error via mail.
405: */
406: if (!(flags & NO_MAIL))
407: send_mail(logline);
408:
409: /*
410: * Log to syslog and/or a file.
411: */
1.15 millert 412: if (def_syslog)
413: do_syslog(def_syslog_badpri, logline);
414: if (def_logfile)
1.1 millert 415: do_logfile(logline);
416:
1.5 millert 417: free(message);
418: if (logline != message)
419: free(logline);
1.1 millert 420:
421: if (!(flags & NO_EXIT))
422: exit(1);
423: }
424:
425: #define MAX_MAILFLAGS 63
426:
427: /*
428: * Send a message to MAILTO user
429: */
430: static void
431: send_mail(line)
432: char *line;
433: {
434: FILE *mail;
435: char *p;
1.11 millert 436: int pfd[2];
1.8 mpech 437: pid_t pid;
1.2 millert 438: sigset_t set, oset;
1.7 millert 439: #ifndef NO_ROOT_MAILER
440: static char *root_envp[] = {
441: "HOME=/",
442: "PATH=/usr/bin:/bin",
443: "LOGNAME=root",
444: "USER=root",
445: NULL
446: };
447: #endif
1.1 millert 448:
449: /* Just return if mailer is disabled. */
1.15 millert 450: if (!def_mailerpath || !def_mailto)
1.1 millert 451: return;
452:
1.2 millert 453: (void) sigemptyset(&set);
454: (void) sigaddset(&set, SIGCHLD);
455: (void) sigprocmask(SIG_BLOCK, &set, &oset);
1.1 millert 456:
1.12 millert 457: if (pipe(pfd) == -1)
458: err(1, "cannot open pipe");
1.1 millert 459:
1.2 millert 460: switch (pid = fork()) {
461: case -1:
462: /* Error. */
1.12 millert 463: err(1, "cannot fork");
1.2 millert 464: break;
465: case 0:
466: {
467: char *argv[MAX_MAILFLAGS + 1];
468: char *mpath, *mflags;
469: int i;
470:
1.5 millert 471: /* Child, set stdin to output side of the pipe */
472: if (pfd[0] != STDIN_FILENO) {
473: (void) dup2(pfd[0], STDIN_FILENO);
474: (void) close(pfd[0]);
475: }
1.2 millert 476: (void) close(pfd[1]);
477:
478: /* Build up an argv based the mailer path and flags */
1.15 millert 479: mflags = estrdup(def_mailerflags);
480: mpath = estrdup(def_mailerpath);
1.2 millert 481: if ((argv[0] = strrchr(mpath, ' ')))
482: argv[0]++;
483: else
484: argv[0] = mpath;
485:
486: i = 1;
487: if ((p = strtok(mflags, " \t"))) {
488: do {
489: argv[i] = p;
490: } while (++i < MAX_MAILFLAGS && (p = strtok(NULL, " \t")));
491: }
492: argv[i] = NULL;
1.1 millert 493:
1.5 millert 494: /* Close password file so we don't leak the fd. */
495: endpwent();
496:
1.7 millert 497: /*
498: * Depending on the config, either run the mailer as root
499: * (so user cannot kill it) or as the user (for the paranoid).
500: */
501: #ifndef NO_ROOT_MAILER
1.9 millert 502: set_perms(PERM_FULL_ROOT);
1.7 millert 503: execve(mpath, argv, root_envp);
504: #else
1.9 millert 505: set_perms(PERM_FULL_USER);
1.2 millert 506: execv(mpath, argv);
1.7 millert 507: #endif /* NO_ROOT_MAILER */
1.2 millert 508: _exit(127);
509: }
510: break;
511: }
1.1 millert 512:
1.5 millert 513: (void) close(pfd[0]);
1.2 millert 514: mail = fdopen(pfd[1], "w");
1.1 millert 515:
1.2 millert 516: /* Pipes are all setup, send message via sendmail. */
1.16 ! deraadt 517: (void) fprintf(mail, "Auto-Submitted: auto-generated\n"
! 518: "To: %s\nFrom: %s\nSubject: ",
1.15 millert 519: def_mailto, user_name);
520: for (p = def_mailsub; *p; p++) {
1.2 millert 521: /* Expand escapes in the subject */
522: if (*p == '%' && *(p+1) != '%') {
523: switch (*(++p)) {
524: case 'h':
525: (void) fputs(user_host, mail);
526: break;
527: case 'u':
528: (void) fputs(user_name, mail);
529: break;
530: default:
531: p--;
532: break;
533: }
534: } else
535: (void) fputc(*p, mail);
536: }
537: (void) fprintf(mail, "\n\n%s : %s : %s : %s\n\n", user_host,
538: get_timestr(), user_name, line);
539: fclose(mail);
1.1 millert 540:
1.10 millert 541: /* If mailer is done, wait for it now. If not, we'll get it later. */
542: reapchild(SIGCHLD);
1.2 millert 543: (void) sigprocmask(SIG_SETMASK, &oset, NULL);
1.1 millert 544: }
545:
546: /*
547: * Send mail based on the value of "status" and compile-time options.
548: */
549: static void
550: mail_auth(status, line)
551: int status;
552: char *line;
553: {
554: int mail_mask;
555:
556: /* If any of these bits are set in status, we send mail. */
1.15 millert 557: if (def_mail_always)
1.1 millert 558: mail_mask =
559: VALIDATE_ERROR|VALIDATE_OK|FLAG_NO_USER|FLAG_NO_HOST|VALIDATE_NOT_OK;
560: else {
561: mail_mask = VALIDATE_ERROR;
1.15 millert 562: if (def_mail_no_user)
563: SET(mail_mask, FLAG_NO_USER);
564: if (def_mail_no_host)
565: SET(mail_mask, FLAG_NO_HOST);
566: if (def_mail_no_perms)
567: SET(mail_mask, VALIDATE_NOT_OK);
1.1 millert 568: }
569:
570: if ((status & mail_mask) != 0)
571: send_mail(line);
572: }
573:
574: /*
575: * SIGCHLD sig handler--wait for children as they die.
576: */
577: RETSIGTYPE
578: reapchild(sig)
579: int sig;
580: {
581: int status, serrno = errno;
1.14 millert 582: #ifdef sudo_waitpid
583: pid_t pid;
1.1 millert 584:
1.14 millert 585: do {
586: pid = sudo_waitpid(-1, &status, WNOHANG);
587: } while (pid != 0 && (pid != -1 || errno == EINTR));
1.1 millert 588: #else
589: (void) wait(&status);
590: #endif
591: errno = serrno;
592: }
593:
594: /*
595: * Return an ascii string with the current date + time
596: * Uses strftime() if available, else falls back to ctime().
597: */
598: static char *
599: get_timestr()
600: {
601: char *s;
602: time_t now = time((time_t) 0);
603: #ifdef HAVE_STRFTIME
604: static char buf[128];
605: struct tm *timeptr;
606:
607: timeptr = localtime(&now);
1.15 millert 608: if (def_log_year)
1.1 millert 609: s = "%h %e %T %Y";
610: else
611: s = "%h %e %T";
612:
613: /* strftime() does not guarantee to NUL-terminate so we must check. */
614: buf[sizeof(buf) - 1] = '\0';
615: if (strftime(buf, sizeof(buf), s, timeptr) && buf[sizeof(buf) - 1] == '\0')
616: return(buf);
617:
618: #endif /* HAVE_STRFTIME */
619:
620: s = ctime(&now) + 4; /* skip day of the week */
1.15 millert 621: if (def_log_year)
1.1 millert 622: s[20] = '\0'; /* avoid the newline */
623: else
624: s[15] = '\0'; /* don't care about year */
625:
626: return(s);
627: }