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