Annotation of src/usr.bin/sudo/logging.c, Revision 1.24
1.1 millert 1: /*
1.22 millert 2: * Copyright (c) 1994-1996, 1998-2009 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 3: *
1.15 millert 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.
1.1 millert 7: *
1.15 millert 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.
1.13 millert 15: *
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.
1.1 millert 19: */
20:
1.15 millert 21: #ifdef __TANDEM
22: # include <floss.h>
23: #endif
24:
1.17 millert 25: #include <config.h>
1.1 millert 26:
1.5 millert 27: #include <sys/types.h>
28: #include <sys/param.h>
29: #include <sys/stat.h>
1.20 millert 30: #include <sys/ioctl.h>
1.5 millert 31: #include <sys/wait.h>
1.1 millert 32: #include <stdio.h>
33: #ifdef STDC_HEADERS
1.5 millert 34: # include <stdlib.h>
35: # include <stddef.h>
36: #else
37: # ifdef HAVE_STDLIB_H
38: # include <stdlib.h>
39: # endif
1.1 millert 40: #endif /* STDC_HEADERS */
1.5 millert 41: #ifdef HAVE_STRING_H
42: # include <string.h>
43: #else
44: # ifdef HAVE_STRINGS_H
45: # include <strings.h>
46: # endif
47: #endif /* HAVE_STRING_H */
1.1 millert 48: #ifdef HAVE_UNISTD_H
1.5 millert 49: # include <unistd.h>
1.1 millert 50: #endif /* HAVE_UNISTD_H */
51: #include <pwd.h>
1.21 millert 52: #include <grp.h>
1.1 millert 53: #include <signal.h>
54: #include <time.h>
55: #include <errno.h>
1.20 millert 56: #include <fcntl.h>
1.1 millert 57:
58: #include "sudo.h"
59:
60: static void do_syslog __P((int, char *));
61: static void do_logfile __P((char *));
62: static void send_mail __P((char *));
1.21 millert 63: static int should_mail __P((int));
1.1 millert 64: static char *get_timestr __P((void));
1.5 millert 65: static void mysyslog __P((int, const char *, ...));
1.21 millert 66: static char *new_logline __P((const char *, int));
1.1 millert 67:
1.5 millert 68: #define MAXSYSLOGTRIES 16 /* num of retries for broken syslogs */
1.1 millert 69:
70: /*
1.5 millert 71: * We do an openlog(3)/closelog(3) for each message because some
72: * authentication methods (notably PAM) use syslog(3) for their
73: * own nefarious purposes and may call openlog(3) and closelog(3).
74: * Note that because we don't want to assume that all systems have
75: * vsyslog(3) (HP-UX doesn't) "%m" will not be expanded.
76: * Sadly this is a maze of #ifdefs.
1.1 millert 77: */
78: static void
1.5 millert 79: #ifdef __STDC__
80: mysyslog(int pri, const char *fmt, ...)
81: #else
82: mysyslog(pri, fmt, va_alist)
1.1 millert 83: int pri;
84: const char *fmt;
1.5 millert 85: va_dcl
86: #endif
1.1 millert 87: {
1.5 millert 88: #ifdef BROKEN_SYSLOG
1.1 millert 89: int i;
1.5 millert 90: #endif
91: char buf[MAXSYSLOGLEN+1];
92: va_list ap;
1.1 millert 93:
1.5 millert 94: #ifdef __STDC__
95: va_start(ap, fmt);
96: #else
97: va_start(ap);
98: #endif
99: #ifdef LOG_NFACILITIES
1.15 millert 100: openlog("sudo", 0, def_syslog);
1.5 millert 101: #else
1.12 millert 102: openlog("sudo", 0);
1.5 millert 103: #endif
104: vsnprintf(buf, sizeof(buf), fmt, ap);
105: #ifdef BROKEN_SYSLOG
106: /*
107: * Some versions of syslog(3) don't guarantee success and return
108: * an int (notably HP-UX < 10.0). So, if at first we don't succeed,
109: * try, try again...
110: */
1.1 millert 111: for (i = 0; i < MAXSYSLOGTRIES; i++)
1.5 millert 112: if (syslog(pri, "%s", buf) == 0)
1.1 millert 113: break;
114: #else
1.5 millert 115: syslog(pri, "%s", buf);
1.1 millert 116: #endif /* BROKEN_SYSLOG */
1.5 millert 117: va_end(ap);
118: closelog();
119: }
1.1 millert 120:
1.23 millert 121: #define FMT_FIRST "%8s : %s"
122: #define FMT_CONTD "%8s : (command continued) %s"
123:
1.1 millert 124: /*
125: * Log a message to syslog, pre-pending the username and splitting the
126: * message into parts if it is longer than MAXSYSLOGLEN.
127: */
128: static void
129: do_syslog(pri, msg)
130: int pri;
131: char *msg;
132: {
1.17 millert 133: size_t len, maxlen;
134: char *p, *tmp, save;
135: const char *fmt;
1.1 millert 136:
137: /*
138: * Log the full line, breaking into multiple syslog(3) calls if necessary
139: */
1.23 millert 140: fmt = FMT_FIRST;
141: maxlen = MAXSYSLOGLEN - (sizeof(FMT_FIRST) - 6 + strlen(user_name));
1.17 millert 142: for (p = msg; *p != '\0'; ) {
143: len = strlen(p);
144: if (len > maxlen) {
1.1 millert 145: /*
146: * Break up the line into what will fit on one syslog(3) line
1.17 millert 147: * Try to avoid breaking words into several lines if possible.
1.1 millert 148: */
1.17 millert 149: tmp = memrchr(p, ' ', maxlen);
150: if (tmp == NULL)
151: tmp = p + maxlen;
1.1 millert 152:
153: /* NULL terminate line, but save the char to restore later */
154: save = *tmp;
155: *tmp = '\0';
156:
1.17 millert 157: mysyslog(pri, fmt, user_name, p);
1.1 millert 158:
159: *tmp = save; /* restore saved character */
160:
1.17 millert 161: /* Advance p and eliminate leading whitespace */
162: for (p = tmp; *p == ' '; p++)
1.1 millert 163: ;
164: } else {
1.17 millert 165: mysyslog(pri, fmt, user_name, p);
166: p += len;
1.1 millert 167: }
1.23 millert 168: fmt = FMT_CONTD;
169: maxlen = MAXSYSLOGLEN - (sizeof(FMT_CONTD) - 6 + strlen(user_name));
1.1 millert 170: }
171: }
172:
173: static void
174: do_logfile(msg)
175: char *msg;
176: {
177: char *full_line;
178: char *beg, *oldend, *end;
179: FILE *fp;
180: mode_t oldmask;
1.9 millert 181: size_t maxlen;
1.1 millert 182:
183: oldmask = umask(077);
1.15 millert 184: maxlen = def_loglinelen > 0 ? def_loglinelen : 0;
185: fp = fopen(def_logfile, "a");
1.1 millert 186: (void) umask(oldmask);
187: if (fp == NULL) {
188: easprintf(&full_line, "Can't open log file: %s: %s",
1.15 millert 189: def_logfile, strerror(errno));
1.1 millert 190: send_mail(full_line);
1.17 millert 191: efree(full_line);
1.1 millert 192: } else if (!lock_file(fileno(fp), SUDO_LOCK)) {
193: easprintf(&full_line, "Can't lock log file: %s: %s",
1.15 millert 194: def_logfile, strerror(errno));
1.1 millert 195: send_mail(full_line);
1.17 millert 196: efree(full_line);
1.1 millert 197: } else {
1.15 millert 198: if (def_loglinelen == 0) {
1.1 millert 199: /* Don't pretty-print long log file lines (hard to grep) */
1.15 millert 200: if (def_log_host)
1.1 millert 201: (void) fprintf(fp, "%s : %s : HOST=%s : %s\n", get_timestr(),
202: user_name, user_shost, msg);
203: else
204: (void) fprintf(fp, "%s : %s : %s\n", get_timestr(),
205: user_name, msg);
206: } else {
1.15 millert 207: if (def_log_host)
1.1 millert 208: easprintf(&full_line, "%s : %s : HOST=%s : %s", get_timestr(),
209: user_name, user_shost, msg);
210: else
211: easprintf(&full_line, "%s : %s : %s", get_timestr(),
212: user_name, msg);
213:
214: /*
215: * Print out full_line with word wrap
216: */
217: beg = end = full_line;
218: while (beg) {
219: oldend = end;
220: end = strchr(oldend, ' ');
221:
222: if (maxlen > 0 && end) {
223: *end = '\0';
224: if (strlen(beg) > maxlen) {
225: /* too far, need to back up & print the line */
226:
227: if (beg == (char *)full_line)
228: maxlen -= 4; /* don't indent first line */
229:
230: *end = ' ';
231: if (oldend != beg) {
232: /* rewind & print */
233: end = oldend-1;
234: while (*end == ' ')
235: --end;
236: *(++end) = '\0';
237: (void) fprintf(fp, "%s\n ", beg);
238: *end = ' ';
239: } else {
240: (void) fprintf(fp, "%s\n ", beg);
241: }
242:
243: /* reset beg to point to the start of the new substr */
244: beg = end;
245: while (*beg == ' ')
246: ++beg;
247: } else {
248: /* we still have room */
249: *end = ' ';
250: }
251:
252: /* remove leading whitespace */
253: while (*end == ' ')
254: ++end;
255: } else {
256: /* final line */
257: (void) fprintf(fp, "%s\n", beg);
258: beg = NULL; /* exit condition */
259: }
260: }
1.17 millert 261: efree(full_line);
1.1 millert 262: }
263: (void) fflush(fp);
264: (void) lock_file(fileno(fp), SUDO_UNLOCK);
265: (void) fclose(fp);
266: }
267: }
268:
269: /*
1.21 millert 270: * Log and mail the denial message, optionally informing the user.
1.1 millert 271: */
272: void
1.21 millert 273: log_denial(status, inform_user)
1.1 millert 274: int status;
275: int inform_user;
276: {
277: char *message;
278: char *logline;
279:
1.21 millert 280: /* Set error message. */
281: if (ISSET(status, FLAG_NO_USER))
282: message = "user NOT in sudoers";
1.15 millert 283: else if (ISSET(status, FLAG_NO_HOST))
1.21 millert 284: message = "user NOT authorized on host";
1.1 millert 285: else
1.21 millert 286: message = "command not allowed";
1.1 millert 287:
1.21 millert 288: logline = new_logline(message, 0);
1.1 millert 289:
1.21 millert 290: if (should_mail(status))
291: send_mail(logline); /* send mail based on status */
1.1 millert 292:
293: /* Inform the user if they failed to authenticate. */
1.21 millert 294: if (inform_user) {
1.15 millert 295: if (ISSET(status, FLAG_NO_USER))
1.1 millert 296: (void) fprintf(stderr, "%s is not in the sudoers file. %s",
297: user_name, "This incident will be reported.\n");
1.15 millert 298: else if (ISSET(status, FLAG_NO_HOST))
1.1 millert 299: (void) fprintf(stderr, "%s is not allowed to run sudo on %s. %s",
300: user_name, user_shost, "This incident will be reported.\n");
1.15 millert 301: else if (ISSET(status, FLAG_NO_CHECK))
1.1 millert 302: (void) fprintf(stderr, "Sorry, user %s may not run sudo on %s.\n",
303: user_name, user_shost);
304: else
305: (void) fprintf(stderr,
1.21 millert 306: "Sorry, user %s is not allowed to execute '%s%s%s' as %s%s%s on %s.\n",
1.1 millert 307: user_name, user_cmnd, user_args ? " " : "",
1.21 millert 308: user_args ? user_args : "",
309: list_pw ? list_pw->pw_name : runas_pw ?
310: runas_pw->pw_name : user_name, runas_gr ? ":" : "",
311: runas_gr ? runas_gr->gr_name : "", user_host);
1.1 millert 312: }
313:
314: /*
315: * Log via syslog and/or a file.
316: */
1.15 millert 317: if (def_syslog)
1.21 millert 318: do_syslog(def_syslog_badpri, logline);
319: if (def_logfile)
320: do_logfile(logline);
321:
322: efree(logline);
323: }
324:
325: /*
326: * Log and potentially mail the allowed command.
327: */
328: void
329: log_allowed(status)
330: int status;
331: {
332: char *logline;
333:
334: logline = new_logline(NULL, 0);
335:
336: if (should_mail(status))
337: send_mail(logline); /* send mail based on status */
338:
339: /*
340: * Log via syslog and/or a file.
341: */
342: if (def_syslog)
343: do_syslog(def_syslog_goodpri, logline);
1.15 millert 344: if (def_logfile)
1.1 millert 345: do_logfile(logline);
346:
1.17 millert 347: efree(logline);
1.1 millert 348: }
349:
350: void
351: #ifdef __STDC__
352: log_error(int flags, const char *fmt, ...)
353: #else
1.17 millert 354: log_error(flags, fmt, va_alist)
355: int flags;
356: const char *fmt;
1.1 millert 357: va_dcl
358: #endif
359: {
360: int serrno = errno;
361: char *message;
362: char *logline;
363: va_list ap;
364: #ifdef __STDC__
365: va_start(ap, fmt);
366: #else
367: va_start(ap);
368: #endif
369:
1.19 millert 370: /* Become root if we are not already to avoid user interference */
1.22 millert 371: set_perms(PERM_ROOT|PERM_NOEXIT);
1.1 millert 372:
373: /* Expand printf-style format + args. */
374: evasprintf(&message, fmt, ap);
375: va_end(ap);
376:
1.17 millert 377: if (ISSET(flags, MSG_ONLY))
1.1 millert 378: logline = message;
1.21 millert 379: else
380: logline = new_logline(message, ISSET(flags, USE_ERRNO) ? serrno : 0);
1.1 millert 381:
382: /*
383: * Tell the user.
384: */
1.21 millert 385: if (!ISSET(flags, NO_STDERR)) {
386: if (ISSET(flags, USE_ERRNO))
387: warning("%s", message);
388: else
389: warningx("%s", message);
390: }
1.23 millert 391: if (logline != message)
392: efree(message);
1.1 millert 393:
394: /*
395: * Send a copy of the error via mail.
396: */
1.17 millert 397: if (!ISSET(flags, NO_MAIL))
1.1 millert 398: send_mail(logline);
399:
400: /*
401: * Log to syslog and/or a file.
402: */
1.15 millert 403: if (def_syslog)
404: do_syslog(def_syslog_badpri, logline);
405: if (def_logfile)
1.1 millert 406: do_logfile(logline);
407:
1.23 millert 408: efree(logline);
1.1 millert 409:
1.21 millert 410: if (!ISSET(flags, NO_EXIT)) {
411: cleanup(0);
1.1 millert 412: exit(1);
1.21 millert 413: }
1.1 millert 414: }
415:
416: #define MAX_MAILFLAGS 63
417:
418: /*
419: * Send a message to MAILTO user
420: */
421: static void
422: send_mail(line)
423: char *line;
424: {
425: FILE *mail;
426: char *p;
1.20 millert 427: int fd, pfd[2], status;
428: pid_t pid, rv;
429: sigaction_t sa;
1.7 millert 430: #ifndef NO_ROOT_MAILER
431: static char *root_envp[] = {
432: "HOME=/",
433: "PATH=/usr/bin:/bin",
434: "LOGNAME=root",
1.17 millert 435: "USERNAME=root",
1.7 millert 436: "USER=root",
437: NULL
438: };
439: #endif
1.1 millert 440:
441: /* Just return if mailer is disabled. */
1.15 millert 442: if (!def_mailerpath || !def_mailto)
1.1 millert 443: return;
444:
1.20 millert 445: /* Fork and return, child will daemonize. */
446: switch (pid = fork()) {
447: case -1:
1.21 millert 448: /* Error. */
449: error(1, "cannot fork");
1.20 millert 450: break;
451: case 0:
1.21 millert 452: /* Child. */
1.20 millert 453: switch (pid = fork()) {
454: case -1:
455: /* Error. */
456: mysyslog(LOG_ERR, "cannot fork: %m");
457: _exit(1);
458: case 0:
459: /* Grandchild continues below. */
460: break;
461: default:
462: /* Parent will wait for us. */
463: _exit(0);
464: }
465: break;
466: default:
1.21 millert 467: /* Parent. */
1.20 millert 468: do {
469: #ifdef HAVE_WAITPID
470: rv = waitpid(pid, &status, 0);
471: #else
472: rv = wait(&status);
473: #endif
474: } while (rv == -1 && errno == EINTR);
475: return;
476: }
1.1 millert 477:
1.20 millert 478: /* Daemonize - disassociate from session/tty. */
479: #ifdef HAVE_SETSID
480: if (setsid() == -1)
1.21 millert 481: warning("setsid");
1.20 millert 482: #else
483: setpgrp(0, 0);
484: # ifdef TIOCNOTTY
485: if ((fd = open(_PATH_TTY, O_RDWR, 0644)) != -1) {
486: ioctl(fd, TIOCNOTTY, NULL);
487: close(fd);
488: }
489: # endif
490: #endif
491: chdir("/");
492: if ((fd = open(_PATH_DEVNULL, O_RDWR, 0644)) != -1) {
493: (void) dup2(fd, STDIN_FILENO);
494: (void) dup2(fd, STDOUT_FILENO);
495: (void) dup2(fd, STDERR_FILENO);
496: }
497:
1.21 millert 498: /* Close password, group and other fds so we don't leak. */
499: sudo_endpwent();
500: sudo_endgrent();
1.20 millert 501: closefrom(STDERR_FILENO + 1);
502:
503: /* Ignore SIGPIPE in case mailer exits prematurely (or is missing). */
1.21 millert 504: zero_bytes(&sa, sizeof(sa));
1.20 millert 505: sigemptyset(&sa.sa_mask);
506: sa.sa_flags = 0;
507: sa.sa_handler = SIG_IGN;
508: (void) sigaction(SIGPIPE, &sa, NULL);
509:
510: if (pipe(pfd) == -1) {
511: mysyslog(LOG_ERR, "cannot open pipe: %m");
512: _exit(1);
513: }
1.1 millert 514:
1.2 millert 515: switch (pid = fork()) {
516: case -1:
517: /* Error. */
1.20 millert 518: mysyslog(LOG_ERR, "cannot fork: %m");
519: _exit(1);
1.2 millert 520: break;
521: case 0:
522: {
523: char *argv[MAX_MAILFLAGS + 1];
524: char *mpath, *mflags;
525: int i;
526:
1.5 millert 527: /* Child, set stdin to output side of the pipe */
528: if (pfd[0] != STDIN_FILENO) {
529: (void) dup2(pfd[0], STDIN_FILENO);
530: (void) close(pfd[0]);
531: }
1.2 millert 532: (void) close(pfd[1]);
533:
534: /* Build up an argv based the mailer path and flags */
1.15 millert 535: mflags = estrdup(def_mailerflags);
536: mpath = estrdup(def_mailerpath);
1.2 millert 537: if ((argv[0] = strrchr(mpath, ' ')))
538: argv[0]++;
539: else
540: argv[0] = mpath;
541:
542: i = 1;
543: if ((p = strtok(mflags, " \t"))) {
544: do {
545: argv[i] = p;
546: } while (++i < MAX_MAILFLAGS && (p = strtok(NULL, " \t")));
547: }
548: argv[i] = NULL;
1.1 millert 549:
1.7 millert 550: /*
551: * Depending on the config, either run the mailer as root
552: * (so user cannot kill it) or as the user (for the paranoid).
553: */
554: #ifndef NO_ROOT_MAILER
1.22 millert 555: set_perms(PERM_ROOT|PERM_NOEXIT);
1.7 millert 556: execve(mpath, argv, root_envp);
557: #else
1.22 millert 558: set_perms(PERM_FULL_USER|PERM_NOEXIT);
1.2 millert 559: execv(mpath, argv);
1.7 millert 560: #endif /* NO_ROOT_MAILER */
1.20 millert 561: mysyslog(LOG_ERR, "cannot execute %s: %m", mpath);
1.2 millert 562: _exit(127);
563: }
564: break;
565: }
1.1 millert 566:
1.5 millert 567: (void) close(pfd[0]);
1.2 millert 568: mail = fdopen(pfd[1], "w");
1.1 millert 569:
1.21 millert 570: /* Pipes are all setup, send message. */
1.17 millert 571: (void) fprintf(mail, "To: %s\nFrom: %s\nAuto-Submitted: %s\nSubject: ",
1.21 millert 572: def_mailto, def_mailfrom ? def_mailfrom : user_name, "auto-generated");
1.15 millert 573: for (p = def_mailsub; *p; p++) {
1.2 millert 574: /* Expand escapes in the subject */
575: if (*p == '%' && *(p+1) != '%') {
576: switch (*(++p)) {
577: case 'h':
578: (void) fputs(user_host, mail);
579: break;
580: case 'u':
581: (void) fputs(user_name, mail);
582: break;
583: default:
584: p--;
585: break;
586: }
587: } else
588: (void) fputc(*p, mail);
589: }
590: (void) fprintf(mail, "\n\n%s : %s : %s : %s\n\n", user_host,
591: get_timestr(), user_name, line);
592: fclose(mail);
1.20 millert 593: do {
594: #ifdef HAVE_WAITPID
1.21 millert 595: rv = waitpid(pid, &status, 0);
1.20 millert 596: #else
1.21 millert 597: rv = wait(&status);
1.20 millert 598: #endif
599: } while (rv == -1 && errno == EINTR);
600: _exit(0);
1.1 millert 601: }
602:
603: /*
1.21 millert 604: * Determine whether we should send mail based on "status" and defaults options.
1.1 millert 605: */
1.21 millert 606: static int
607: should_mail(status)
1.1 millert 608: int status;
609: {
610:
1.21 millert 611: return(def_mail_always || ISSET(status, VALIDATE_ERROR) ||
612: (def_mail_no_user && ISSET(status, FLAG_NO_USER)) ||
613: (def_mail_no_host && ISSET(status, FLAG_NO_HOST)) ||
614: (def_mail_no_perms && !ISSET(status, VALIDATE_OK)));
1.1 millert 615: }
616:
617: /*
618: * Return an ascii string with the current date + time
619: * Uses strftime() if available, else falls back to ctime().
620: */
621: static char *
622: get_timestr()
623: {
624: char *s;
625: time_t now = time((time_t) 0);
626: #ifdef HAVE_STRFTIME
627: static char buf[128];
628: struct tm *timeptr;
629:
630: timeptr = localtime(&now);
1.15 millert 631: if (def_log_year)
1.1 millert 632: s = "%h %e %T %Y";
633: else
634: s = "%h %e %T";
635:
636: /* strftime() does not guarantee to NUL-terminate so we must check. */
637: buf[sizeof(buf) - 1] = '\0';
638: if (strftime(buf, sizeof(buf), s, timeptr) && buf[sizeof(buf) - 1] == '\0')
639: return(buf);
640:
641: #endif /* HAVE_STRFTIME */
642:
643: s = ctime(&now) + 4; /* skip day of the week */
1.15 millert 644: if (def_log_year)
1.1 millert 645: s[20] = '\0'; /* avoid the newline */
646: else
647: s[15] = '\0'; /* don't care about year */
648:
649: return(s);
1.21 millert 650: }
651:
652: #define LL_TTY_STR "TTY="
653: #define LL_CWD_STR "PWD=" /* XXX - should be CWD= */
654: #define LL_USER_STR "USER="
655: #define LL_GROUP_STR "GROUP="
656: #define LL_ENV_STR "ENV="
657: #define LL_CMND_STR "COMMAND="
658:
659: /*
660: * Allocate and fill in a new logline.
661: */
662: static char *
663: new_logline(message, serrno)
664: const char *message;
665: int serrno;
666: {
667: size_t len = 0;
668: char *evstr = NULL;
669: char *errstr = NULL;
670: char *line;
671:
672: /*
673: * Compute line length
674: */
675: if (message != NULL)
676: len += strlen(message) + 3;
677: if (serrno) {
678: errstr = strerror(serrno);
679: len += strlen(errstr) + 3;
680: }
681: len += sizeof(LL_TTY_STR) + 2 + strlen(user_tty);
682: len += sizeof(LL_CWD_STR) + 2 + strlen(user_cwd);
683: if (runas_pw != NULL)
684: len += sizeof(LL_USER_STR) + 2 + strlen(runas_pw->pw_name);
685: if (runas_gr != NULL)
686: len += sizeof(LL_GROUP_STR) + 2 + strlen(runas_gr->gr_name);
687: if (sudo_user.env_vars != NULL) {
688: size_t evlen = 0;
689: struct list_member *cur;
690: for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next)
691: evlen += strlen(cur->value) + 1;
692: evstr = emalloc(evlen);
693: evstr[0] = '\0';
694: for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next) {
695: strlcat(evstr, cur->value, evlen);
696: strlcat(evstr, " ", evlen); /* NOTE: last one will fail */
697: }
698: len += sizeof(LL_ENV_STR) + 2 + evlen;
699: }
700: len += sizeof(LL_CMND_STR) - 1 + strlen(user_cmnd);
701: if (user_args != NULL)
702: len += strlen(user_args) + 1;
703:
704: /*
705: * Allocate and build up the line.
706: */
707: line = emalloc(++len);
708: line[0] = '\0';
709:
710: if (message != NULL) {
711: if (strlcat(line, message, len) >= len ||
712: strlcat(line, errstr ? " : " : " ; ", len) >= len)
713: goto toobig;
714: }
715: if (serrno) {
716: if (strlcat(line, errstr, len) >= len ||
717: strlcat(line, " ; ", len) >= len)
718: goto toobig;
719: }
720: if (strlcat(line, LL_TTY_STR, len) >= len ||
721: strlcat(line, user_tty, len) >= len ||
722: strlcat(line, " ; ", len) >= len)
723: goto toobig;
724: if (strlcat(line, LL_CWD_STR, len) >= len ||
725: strlcat(line, user_cwd, len) >= len ||
726: strlcat(line, " ; ", len) >= len)
727: goto toobig;
728: if (runas_pw != NULL) {
729: if (strlcat(line, LL_USER_STR, len) >= len ||
730: strlcat(line, runas_pw->pw_name, len) >= len ||
731: strlcat(line, " ; ", len) >= len)
732: goto toobig;
733: }
734: if (runas_gr != NULL) {
735: if (strlcat(line, LL_GROUP_STR, len) >= len ||
736: strlcat(line, runas_gr->gr_name, len) >= len ||
737: strlcat(line, " ; ", len) >= len)
738: goto toobig;
739: }
740: if (evstr != NULL) {
741: if (strlcat(line, LL_ENV_STR, len) >= len ||
742: strlcat(line, evstr, len) >= len ||
743: strlcat(line, " ; ", len) >= len)
744: goto toobig;
745: efree(evstr);
746: }
747: if (strlcat(line, LL_CMND_STR, len) >= len ||
748: strlcat(line, user_cmnd, len) >= len)
749: goto toobig;
750: if (user_args != NULL) {
751: if (strlcat(line, " ", len) >= len ||
752: strlcat(line, user_args, len) >= len)
753: goto toobig;
754: }
755:
756: return (line);
757: toobig:
758: errorx(1, "internal error: insufficient space for log line");
1.1 millert 759: }