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