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