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