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