2 * Copyright (c) 1994-1996,1998-2007 Todd C. Miller <Todd.Miller@courtesan.com>
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.
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.
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.
27 #include <sys/types.h>
28 #include <sys/param.h>
39 #endif /* STDC_HEADERS */
43 # ifdef HAVE_STRINGS_H
46 #endif /* HAVE_STRING_H */
49 #endif /* HAVE_UNISTD_H */
53 # include "emul/err.h"
54 #endif /* HAVE_ERR_H */
63 __unused static const char rcsid[] = "$Sudo: logging.c,v 1.168.2.13 2007/11/25 13:07:38 millert Exp $";
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));
71 static void mysyslog __P((int, const char *, ...));
73 #define MAXSYSLOGTRIES 16 /* num of retries for broken syslogs */
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.
85 mysyslog(int pri, const char *fmt, ...)
87 mysyslog(pri, fmt, va_alist)
96 char buf[MAXSYSLOGLEN+1];
104 #ifdef LOG_NFACILITIES
105 openlog("sudo", 0, def_syslog);
109 vsnprintf(buf, sizeof(buf), fmt, ap);
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,
116 for (i = 0; i < MAXSYSLOGTRIES; i++)
117 if (syslog(pri, "%s", buf) == 0)
120 syslog(pri, "%s", buf);
121 #endif /* BROKEN_SYSLOG */
127 * Log a message to syslog, pre-pending the username and splitting the
128 * message into parts if it is longer than MAXSYSLOGLEN.
138 const char *fmt_first = "%8s : %s";
139 const char *fmt_contd = "%8s : (command continued) %s";
142 * Log the full line, breaking into multiple syslog(3) calls if necessary
145 maxlen = MAXSYSLOGLEN - (sizeof(fmt_first) - 6 + strlen(user_name));
146 for (p = msg; *p != '\0'; ) {
150 * Break up the line into what will fit on one syslog(3) line
151 * Try to avoid breaking words into several lines if possible.
153 tmp = memrchr(p, ' ', maxlen);
157 /* NULL terminate line, but save the char to restore later */
161 mysyslog(pri, fmt, user_name, p);
163 *tmp = save; /* restore saved character */
165 /* Advance p and eliminate leading whitespace */
166 for (p = tmp; *p == ' '; p++)
169 mysyslog(pri, fmt, user_name, p);
173 maxlen = MAXSYSLOGLEN - (sizeof(fmt_contd) - 6 + strlen(user_name));
182 char *beg, *oldend, *end;
187 oldmask = umask(077);
188 maxlen = def_loglinelen > 0 ? def_loglinelen : 0;
189 fp = fopen(def_logfile, "a");
190 (void) umask(oldmask);
192 easprintf(&full_line, "Can't open log file: %s: %s",
193 def_logfile, strerror(errno));
194 send_mail(full_line);
196 } else if (!lock_file(fileno(fp), SUDO_LOCK)) {
197 easprintf(&full_line, "Can't lock log file: %s: %s",
198 def_logfile, strerror(errno));
199 send_mail(full_line);
202 if (def_loglinelen == 0) {
203 /* Don't pretty-print long log file lines (hard to grep) */
205 (void) fprintf(fp, "%s : %s : HOST=%s : %s\n", get_timestr(),
206 user_name, user_shost, msg);
208 (void) fprintf(fp, "%s : %s : %s\n", get_timestr(),
212 easprintf(&full_line, "%s : %s : HOST=%s : %s", get_timestr(),
213 user_name, user_shost, msg);
215 easprintf(&full_line, "%s : %s : %s", get_timestr(),
219 * Print out full_line with word wrap
221 beg = end = full_line;
224 end = strchr(oldend, ' ');
226 if (maxlen > 0 && end) {
228 if (strlen(beg) > maxlen) {
229 /* too far, need to back up & print the line */
231 if (beg == (char *)full_line)
232 maxlen -= 4; /* don't indent first line */
241 (void) fprintf(fp, "%s\n ", beg);
244 (void) fprintf(fp, "%s\n ", beg);
247 /* reset beg to point to the start of the new substr */
252 /* we still have room */
256 /* remove leading whitespace */
261 (void) fprintf(fp, "%s\n", beg);
262 beg = NULL; /* exit condition */
268 (void) lock_file(fileno(fp), SUDO_UNLOCK);
274 * Two main functions, log_error() to log errors and log_auth() to
275 * log allow/deny messages.
278 log_auth(status, inform_user)
287 if (ISSET(status, VALIDATE_OK))
288 pri = def_syslog_goodpri;
290 pri = def_syslog_badpri;
292 /* Set error message, if any. */
293 if (ISSET(status, VALIDATE_OK))
295 else if (ISSET(status, FLAG_NO_USER))
296 message = "user NOT in sudoers ; ";
297 else if (ISSET(status, FLAG_NO_HOST))
298 message = "user NOT authorized on host ; ";
299 else if (ISSET(status, VALIDATE_NOT_OK))
300 message = "command not allowed ; ";
302 message = "unknown error ; ";
304 if (sudo_user.env_vars != NULL) {
305 size_t len = 7; /* " ; ENV=" */
306 struct list_member *cur;
307 for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next)
308 len += strlen(cur->value) + 1;
309 evstr = emalloc(len);
310 strlcpy(evstr, " ; ENV=", len);
311 for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next) {
312 strlcat(evstr, cur->value, len);
313 strlcat(evstr, " ", len); /* NOTE: last one will fail */
316 easprintf(&logline, "%sTTY=%s ; PWD=%s ; USER=%s%s ; COMMAND=%s%s%s",
317 message, user_tty, user_cwd, *user_runas, evstr ? evstr : "",
318 user_cmnd, user_args ? " " : "", user_args ? user_args : "");
320 mail_auth(status, logline); /* send mail based on status */
322 /* Inform the user if they failed to authenticate. */
323 if (inform_user && ISSET(status, VALIDATE_NOT_OK)) {
324 if (ISSET(status, FLAG_NO_USER))
325 (void) fprintf(stderr, "%s is not in the sudoers file. %s",
326 user_name, "This incident will be reported.\n");
327 else if (ISSET(status, FLAG_NO_HOST))
328 (void) fprintf(stderr, "%s is not allowed to run sudo on %s. %s",
329 user_name, user_shost, "This incident will be reported.\n");
330 else if (ISSET(status, FLAG_NO_CHECK))
331 (void) fprintf(stderr, "Sorry, user %s may not run sudo on %s.\n",
332 user_name, user_shost);
334 (void) fprintf(stderr,
335 "Sorry, user %s is not allowed to execute '%s%s%s' as %s on %s.\n",
336 user_name, user_cmnd, user_args ? " " : "",
337 user_args ? user_args : "", *user_runas, user_host);
341 * Log via syslog and/or a file.
344 do_syslog(pri, logline);
354 log_error(int flags, const char *fmt, ...)
356 log_error(flags, fmt, va_alist)
373 /* Become root if we are not already to avoid user interference */
374 set_perms(PERM_ROOT);
376 /* Expand printf-style format + args. */
377 evasprintf(&message, fmt, ap);
380 if (sudo_user.env_vars != NULL) {
381 size_t len = 7; /* " ; ENV=" */
382 struct list_member *cur;
383 for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next)
384 len += strlen(cur->value) + 1;
385 evstr = emalloc(len);
386 strlcpy(evstr, " ; ENV=", len);
387 for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next) {
388 strlcat(evstr, cur->value, len);
389 strlcat(evstr, " ", len); /* NOTE: last one will fail */
393 if (ISSET(flags, MSG_ONLY))
395 else if (ISSET(flags, USE_ERRNO)) {
398 "%s: %s ; TTY=%s ; PWD=%s ; USER=%s%s ; COMMAND=%s %s",
399 message, strerror(serrno), user_tty, user_cwd, *user_runas,
400 evstr ? evstr : "", user_cmnd, user_args);
403 "%s: %s ; TTY=%s ; PWD=%s ; USER=%s%s ; COMMAND=%s", message,
404 strerror(serrno), user_tty, user_cwd, *user_runas,
405 evstr ? evstr : "", user_cmnd);
410 "%s ; TTY=%s ; PWD=%s ; USER=%s%s ; COMMAND=%s %s", message,
411 user_tty, user_cwd, *user_runas, evstr ? evstr : "",
412 user_cmnd, user_args);
415 "%s ; TTY=%s ; PWD=%s ; USER=%s%s ; COMMAND=%s", message,
416 user_tty, user_cwd, *user_runas, evstr ? evstr : "", user_cmnd);
423 if (ISSET(flags, USE_ERRNO))
426 warnx("%s", message);
429 * Send a copy of the error via mail.
431 if (!ISSET(flags, NO_MAIL))
435 * Log to syslog and/or a file.
438 do_syslog(def_syslog_badpri, logline);
443 if (logline != message)
446 if (!ISSET(flags, NO_EXIT))
450 #define MAX_MAILFLAGS 63
453 * Send a message to MAILTO user
464 #ifndef NO_ROOT_MAILER
465 static char *root_envp[] = {
467 "PATH=/usr/bin:/bin",
475 /* Just return if mailer is disabled. */
476 if (!def_mailerpath || !def_mailto)
479 (void) sigemptyset(&set);
480 (void) sigaddset(&set, SIGCHLD);
481 (void) sigprocmask(SIG_BLOCK, &set, &oset);
484 err(1, "cannot open pipe");
486 switch (pid = fork()) {
489 err(1, "cannot fork");
493 char *argv[MAX_MAILFLAGS + 1];
494 char *mpath, *mflags;
497 /* Child, set stdin to output side of the pipe */
498 if (pfd[0] != STDIN_FILENO) {
499 (void) dup2(pfd[0], STDIN_FILENO);
500 (void) close(pfd[0]);
502 (void) close(pfd[1]);
504 /* Build up an argv based the mailer path and flags */
505 mflags = estrdup(def_mailerflags);
506 mpath = estrdup(def_mailerpath);
507 if ((argv[0] = strrchr(mpath, ' ')))
513 if ((p = strtok(mflags, " \t"))) {
516 } while (++i < MAX_MAILFLAGS && (p = strtok(NULL, " \t")));
520 /* Close password file so we don't leak the fd. */
524 * Depending on the config, either run the mailer as root
525 * (so user cannot kill it) or as the user (for the paranoid).
527 #ifndef NO_ROOT_MAILER
528 set_perms(PERM_ROOT);
529 execve(mpath, argv, root_envp);
531 set_perms(PERM_FULL_USER);
533 #endif /* NO_ROOT_MAILER */
539 (void) close(pfd[0]);
540 mail = fdopen(pfd[1], "w");
542 /* Pipes are all setup, send message via sendmail. */
543 (void) fprintf(mail, "To: %s\nFrom: %s\nAuto-Submitted: %s\nSubject: ",
544 def_mailto, user_name, "auto-generated");
545 for (p = def_mailsub; *p; p++) {
546 /* Expand escapes in the subject */
547 if (*p == '%' && *(p+1) != '%') {
550 (void) fputs(user_host, mail);
553 (void) fputs(user_name, mail);
560 (void) fputc(*p, mail);
562 (void) fprintf(mail, "\n\n%s : %s : %s : %s\n\n", user_host,
563 get_timestr(), user_name, line);
566 (void) sigprocmask(SIG_SETMASK, &oset, NULL);
567 /* If mailer is done, wait for it now. If not, we'll get it later. */
572 * Send mail based on the value of "status" and compile-time options.
575 mail_auth(status, line)
581 /* If any of these bits are set in status, we send mail. */
584 VALIDATE_ERROR|VALIDATE_OK|FLAG_NO_USER|FLAG_NO_HOST|VALIDATE_NOT_OK;
586 mail_mask = VALIDATE_ERROR;
587 if (def_mail_no_user)
588 SET(mail_mask, FLAG_NO_USER);
589 if (def_mail_no_host)
590 SET(mail_mask, FLAG_NO_HOST);
591 if (def_mail_no_perms)
592 SET(mail_mask, VALIDATE_NOT_OK);
595 if ((status & mail_mask) != 0)
600 * SIGCHLD sig handler--wait for children as they die.
606 int status, serrno = errno;
611 pid = sudo_waitpid(-1, &status, WNOHANG);
612 } while (pid != 0 && (pid != -1 || errno == EINTR));
614 (void) wait(&status);
620 * Return an ascii string with the current date + time
621 * Uses strftime() if available, else falls back to ctime().
627 time_t now = time((time_t) 0);
629 static char buf[128];
632 timeptr = localtime(&now);
638 /* strftime() does not guarantee to NUL-terminate so we must check. */
639 buf[sizeof(buf) - 1] = '\0';
640 if (strftime(buf, sizeof(buf), s, timeptr) && buf[sizeof(buf) - 1] == '\0')
643 #endif /* HAVE_STRFTIME */
645 s = ctime(&now) + 4; /* skip day of the week */
647 s[20] = '\0'; /* avoid the newline */
649 s[15] = '\0'; /* don't care about year */