Annotation of embedaddon/sudo/common/sudo_debug.c, revision 1.1.1.4
1.1 misho 1: /*
1.1.1.3 misho 2: * Copyright (c) 2011-2013 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 misho 3: *
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.
7: *
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.
15: */
16:
17: #include <config.h>
18:
19: #include <sys/types.h>
20: #include <sys/stat.h>
21: #include <sys/uio.h>
22: #include <stdio.h>
23: #ifdef STDC_HEADERS
24: # include <stdlib.h>
25: # include <stddef.h>
26: #else
27: # ifdef HAVE_STDLIB_H
28: # include <stdlib.h>
29: # endif
30: #endif /* STDC_HEADERS */
31: #ifdef HAVE_STDBOOL_H
32: # include <stdbool.h>
33: #else
34: # include "compat/stdbool.h"
35: #endif
36: #ifdef HAVE_STRING_H
37: # include <string.h>
38: #endif /* HAVE_STRING_H */
39: #ifdef HAVE_STRINGS_H
40: # include <strings.h>
41: #endif /* HAVE_STRINGS_H */
42: #ifdef HAVE_UNISTD_H
43: # include <unistd.h>
44: #endif /* HAVE_UNISTD_H */
45: #include <ctype.h>
46: #include <errno.h>
47: #include <fcntl.h>
48: #include <time.h>
49:
50: #include "missing.h"
51: #include "alloc.h"
1.1.1.4 ! misho 52: #include "fatal.h"
1.1 misho 53: #include "sudo_plugin.h"
54: #include "sudo_debug.h"
55:
1.1.1.3 misho 56: #define DEFAULT_TEXT_DOMAIN "sudo"
57: #include "gettext.h"
58:
1.1 misho 59: /*
60: * The debug priorities and subsystems are currently hard-coded.
61: * In the future we might consider allowing plugins to register their
62: * own subsystems and provide direct access to the debugging API.
63: */
64:
65: /* Note: this must match the order in sudo_debug.h */
66: const char *const sudo_debug_priorities[] = {
67: "crit",
68: "err",
69: "warn",
70: "notice",
71: "diag",
72: "info",
73: "trace",
74: "debug",
75: NULL
76: };
77:
78: /* Note: this must match the order in sudo_debug.h */
79: const char *const sudo_debug_subsystems[] = {
80: "main",
81: "args",
82: "exec",
83: "pty",
84: "utmp",
85: "conv",
86: "pcomm",
87: "util",
88: "netif",
89: "audit",
90: "edit",
91: "selinux",
92: "ldap",
93: "match",
94: "parser",
95: "alias",
96: "defaults",
97: "auth",
98: "env",
99: "logging",
100: "nss",
101: "rbtree",
102: "perms",
103: "plugin",
104: "hooks",
1.1.1.2 misho 105: "sssd",
1.1 misho 106: NULL
107: };
108:
109: #define NUM_SUBSYSTEMS (sizeof(sudo_debug_subsystems) / sizeof(sudo_debug_subsystems[0]) - 1)
110:
111: /* Values for sudo_debug_mode */
112: #define SUDO_DEBUG_MODE_DISABLED 0
113: #define SUDO_DEBUG_MODE_FILE 1
114: #define SUDO_DEBUG_MODE_CONV 2
115:
116: static int sudo_debug_settings[NUM_SUBSYSTEMS];
117: static int sudo_debug_fd = -1;
118: static int sudo_debug_mode;
119: static char sudo_debug_pidstr[(((sizeof(int) * 8) + 2) / 3) + 3];
120: static size_t sudo_debug_pidlen;
121:
122: /*
123: * Parse settings string from sudo.conf and open debugfile.
124: * Returns 1 on success, 0 if cannot open debugfile.
125: * Unsupported subsystems and priorities are silently ignored.
126: */
127: int sudo_debug_init(const char *debugfile, const char *settings)
128: {
129: char *buf, *cp, *subsys, *pri;
130: int i, j;
131:
132: /* Init per-subsystems settings to -1 since 0 is a valid priority. */
133: for (i = 0; i < NUM_SUBSYSTEMS; i++)
134: sudo_debug_settings[i] = -1;
135:
136: /* Open debug file if specified. */
137: if (debugfile != NULL) {
138: if (sudo_debug_fd != -1)
139: close(sudo_debug_fd);
1.1.1.3 misho 140: sudo_debug_fd = open(debugfile, O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR);
141: if (sudo_debug_fd == -1) {
142: /* Create debug file as needed and set group ownership. */
143: if (errno == ENOENT) {
144: sudo_debug_fd = open(debugfile, O_WRONLY|O_APPEND|O_CREAT,
145: S_IRUSR|S_IWUSR);
146: }
147: if (sudo_debug_fd == -1)
148: return 0;
149: ignore_result(fchown(sudo_debug_fd, (uid_t)-1, 0));
150: }
1.1 misho 151: (void)fcntl(sudo_debug_fd, F_SETFD, FD_CLOEXEC);
152: sudo_debug_mode = SUDO_DEBUG_MODE_FILE;
153: } else {
154: /* Called from the plugin, no debug file. */
155: sudo_debug_mode = SUDO_DEBUG_MODE_CONV;
156: }
157:
1.1.1.4 ! misho 158: /* Stash the pid string so we only have to format it once. */
! 159: (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
! 160: (int)getpid());
! 161: sudo_debug_pidlen = strlen(sudo_debug_pidstr);
! 162:
1.1 misho 163: /* Parse settings string. */
1.1.1.4 ! misho 164: if ((buf = strdup(settings)) == NULL)
! 165: return 0;
1.1 misho 166: for ((cp = strtok(buf, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
167: /* Should be in the form subsys@pri. */
168: subsys = cp;
169: if ((pri = strchr(cp, '@')) == NULL)
170: continue;
171: *pri++ = '\0';
172:
173: /* Look up priority and subsystem, fill in sudo_debug_settings[]. */
174: for (i = 0; sudo_debug_priorities[i] != NULL; i++) {
175: if (strcasecmp(pri, sudo_debug_priorities[i]) == 0) {
176: for (j = 0; sudo_debug_subsystems[j] != NULL; j++) {
177: if (strcasecmp(subsys, "all") == 0) {
178: sudo_debug_settings[j] = i;
179: continue;
180: }
181: if (strcasecmp(subsys, sudo_debug_subsystems[j]) == 0) {
182: sudo_debug_settings[j] = i;
183: break;
184: }
185: }
186: break;
187: }
188: }
189: }
1.1.1.4 ! misho 190: free(buf);
1.1 misho 191:
192: return 1;
193: }
194:
195: pid_t
196: sudo_debug_fork(void)
197: {
198: pid_t pid;
199:
200: if ((pid = fork()) == 0) {
201: (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
202: (int)getpid());
203: sudo_debug_pidlen = strlen(sudo_debug_pidstr);
204: }
205:
206: return pid;
207: }
208:
209: void
210: sudo_debug_enter(const char *func, const char *file, int line,
211: int subsys)
212: {
213: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
214: "-> %s @ %s:%d", func, file, line);
215: }
216:
1.1.1.4 ! misho 217: void
! 218: sudo_debug_exit(const char *func, const char *file, int line,
1.1 misho 219: int subsys)
220: {
221: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
222: "<- %s @ %s:%d", func, file, line);
223: }
224:
1.1.1.4 ! misho 225: void
! 226: sudo_debug_exit_int(const char *func, const char *file, int line,
1.1 misho 227: int subsys, int rval)
228: {
229: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
230: "<- %s @ %s:%d := %d", func, file, line, rval);
231: }
232:
1.1.1.4 ! misho 233: void
! 234: sudo_debug_exit_long(const char *func, const char *file, int line,
1.1 misho 235: int subsys, long rval)
236: {
237: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
238: "<- %s @ %s:%d := %ld", func, file, line, rval);
239: }
240:
1.1.1.4 ! misho 241: void
! 242: sudo_debug_exit_size_t(const char *func, const char *file, int line,
1.1 misho 243: int subsys, size_t rval)
244: {
245: /* XXX - should use %zu but our snprintf.c doesn't support it */
246: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
247: "<- %s @ %s:%d := %lu", func, file, line, (unsigned long)rval);
248: }
249:
250: /* We use int, not bool, here for functions that return -1 on error. */
1.1.1.4 ! misho 251: void
! 252: sudo_debug_exit_bool(const char *func, const char *file, int line,
1.1 misho 253: int subsys, int rval)
254: {
255: if (rval == true || rval == false) {
256: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
257: "<- %s @ %s:%d := %s", func, file, line, rval ? "true" : "false");
258: } else {
259: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
260: "<- %s @ %s:%d := %d", func, file, line, rval);
261: }
262: }
263:
1.1.1.4 ! misho 264: void
! 265: sudo_debug_exit_str(const char *func, const char *file, int line,
1.1 misho 266: int subsys, const char *rval)
267: {
268: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
269: "<- %s @ %s:%d := %s", func, file, line, rval ? rval : "(null)");
270: }
271:
1.1.1.4 ! misho 272: void
! 273: sudo_debug_exit_str_masked(const char *func, const char *file, int line,
1.1 misho 274: int subsys, const char *rval)
275: {
276: static const char stars[] = "********************************************************************************";
277: int len = rval ? strlen(rval) : sizeof("(null)") - 1;
278:
279: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
280: "<- %s @ %s:%d := %.*s", func, file, line, len, rval ? stars : "(null)");
281: }
282:
1.1.1.4 ! misho 283: void
! 284: sudo_debug_exit_ptr(const char *func, const char *file, int line,
1.1 misho 285: int subsys, const void *rval)
286: {
287: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
288: "<- %s @ %s:%d := %p", func, file, line, rval);
289: }
290:
291: static void
292: sudo_debug_write_conv(const char *func, const char *file, int lineno,
293: const char *str, int len, int errno_val)
294: {
1.1.1.4 ! misho 295: /* Remove trailing newlines. */
! 296: while (len > 0 && str[len - 1] == '\n')
1.1.1.3 misho 297: len--;
298:
1.1.1.4 ! misho 299: if (len > 0) {
! 300: if (func != NULL && file != NULL) {
! 301: if (errno_val) {
! 302: sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s: %s @ %s() %s:%d",
! 303: len, str, strerror(errno_val), func, file, lineno);
! 304: } else {
! 305: sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s @ %s() %s:%d",
! 306: len, str, func, file, lineno);
! 307: }
1.1.1.3 misho 308: } else {
1.1.1.4 ! misho 309: if (errno_val) {
! 310: sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s: %s",
! 311: len, str, strerror(errno_val));
! 312: } else {
! 313: sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s", len, str);
! 314: }
1.1.1.3 misho 315: }
1.1.1.4 ! misho 316: } else if (errno_val) {
! 317: /* Only print error string. */
! 318: if (func != NULL && file != NULL) {
! 319: sudo_printf(SUDO_CONV_DEBUG_MSG, "%s @ %s() %s:%d",
! 320: strerror(errno_val), func, file, lineno);
1.1.1.3 misho 321: } else {
1.1.1.4 ! misho 322: sudo_printf(SUDO_CONV_DEBUG_MSG, "%s", strerror(errno_val));
1.1 misho 323: }
324: }
325: }
326:
327: static void
328: sudo_debug_write_file(const char *func, const char *file, int lineno,
329: const char *str, int len, int errno_val)
330: {
331: char *timestr, numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
332: time_t now;
333: struct iovec iov[12];
1.1.1.4 ! misho 334: int iovcnt = 3;
1.1 misho 335:
336: /* Prepend program name and pid with a trailing space. */
337: iov[1].iov_base = (char *)getprogname();
338: iov[1].iov_len = strlen(iov[1].iov_base);
339: iov[2].iov_base = sudo_debug_pidstr;
340: iov[2].iov_len = sudo_debug_pidlen;
341:
342: /* Add string along with newline if it doesn't have one. */
1.1.1.4 ! misho 343: if (len > 0) {
! 344: iov[iovcnt].iov_base = (char *)str;
! 345: iov[iovcnt].iov_len = len;
! 346: while (len > 0 && str[len - 1] == '\n')
! 347: iov[iovcnt].iov_len--;
! 348: iovcnt++;
! 349: }
1.1 misho 350:
351: /* Append error string if errno is specified. */
352: if (errno_val) {
1.1.1.4 ! misho 353: if (len > 0) {
! 354: iov[iovcnt].iov_base = ": ";
! 355: iov[iovcnt].iov_len = 2;
! 356: iovcnt++;
! 357: }
1.1 misho 358: iov[iovcnt].iov_base = strerror(errno_val);
359: iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base);
360: iovcnt++;
361: }
362:
363: /* If function, file and lineno are specified, append them. */
364: if (func != NULL && file != NULL && lineno != 0) {
365: iov[iovcnt].iov_base = " @ ";
366: iov[iovcnt].iov_len = 3;
367: iovcnt++;
368:
369: iov[iovcnt].iov_base = (char *)func;
370: iov[iovcnt].iov_len = strlen(func);
371: iovcnt++;
372:
373: iov[iovcnt].iov_base = "() ";
374: iov[iovcnt].iov_len = 3;
375: iovcnt++;
376:
377: iov[iovcnt].iov_base = (char *)file;
378: iov[iovcnt].iov_len = strlen(file);
379: iovcnt++;
380:
381: (void)snprintf(numbuf, sizeof(numbuf), ":%d", lineno);
382: iov[iovcnt].iov_base = numbuf;
383: iov[iovcnt].iov_len = strlen(numbuf);
384: iovcnt++;
385: }
386:
1.1.1.4 ! misho 387: /* Append newline. */
! 388: iov[iovcnt].iov_base = "\n";
! 389: iov[iovcnt].iov_len = 1;
! 390: iovcnt++;
1.1 misho 391:
392: /* Do timestamp last due to ctime's static buffer. */
1.1.1.3 misho 393: time(&now);
1.1 misho 394: timestr = ctime(&now) + 4;
395: timestr[15] = ' '; /* replace year with a space */
396: timestr[16] = '\0';
397: iov[0].iov_base = timestr;
398: iov[0].iov_len = 16;
399:
400: /* Write message in a single syscall */
401: ignore_result(writev(sudo_debug_fd, iov, iovcnt));
402: }
403:
404: void
405: sudo_debug_write2(const char *func, const char *file, int lineno,
406: const char *str, int len, int errno_val)
407: {
408: switch (sudo_debug_mode) {
409: case SUDO_DEBUG_MODE_CONV:
410: sudo_debug_write_conv(func, file, lineno, str, len, errno_val);
411: break;
412: case SUDO_DEBUG_MODE_FILE:
413: sudo_debug_write_file(func, file, lineno, str, len, errno_val);
414: break;
415: }
416: }
417:
418: /* XXX - turn into a macro */
419: void
420: sudo_debug_write(const char *str, int len, int errno_val)
421: {
422: sudo_debug_write2(NULL, NULL, 0, str, len, errno_val);
423: }
424:
425: void
1.1.1.3 misho 426: sudo_debug_vprintf2(const char *func, const char *file, int lineno, int level,
427: const char *fmt, va_list ap)
1.1 misho 428: {
429: int buflen, pri, subsys, saved_errno = errno;
1.1.1.4 ! misho 430: char *buf = NULL;
1.1 misho 431:
432: if (!sudo_debug_mode)
433: return;
434:
435: /* Extract pri and subsystem from level. */
436: pri = SUDO_DEBUG_PRI(level);
437: subsys = SUDO_DEBUG_SUBSYS(level);
438:
439: /* Make sure we want debug info at this level. */
440: if (subsys < NUM_SUBSYSTEMS && sudo_debug_settings[subsys] >= pri) {
1.1.1.4 ! misho 441: buflen = fmt ? vasprintf(&buf, fmt, ap) : 0;
1.1 misho 442: if (buflen != -1) {
443: int errcode = ISSET(level, SUDO_DEBUG_ERRNO) ? saved_errno : 0;
444: if (ISSET(level, SUDO_DEBUG_LINENO))
445: sudo_debug_write2(func, file, lineno, buf, buflen, errcode);
446: else
447: sudo_debug_write2(NULL, NULL, 0, buf, buflen, errcode);
448: free(buf);
449: }
450: }
451:
452: errno = saved_errno;
453: }
454:
455: void
1.1.1.3 misho 456: sudo_debug_printf2(const char *func, const char *file, int lineno, int level,
457: const char *fmt, ...)
458: {
459: va_list ap;
460:
461: va_start(ap, fmt);
462: sudo_debug_vprintf2(func, file, lineno, level, fmt, ap);
463: va_end(ap);
464: }
465:
466: void
1.1 misho 467: sudo_debug_execve2(int level, const char *path, char *const argv[], char *const envp[])
468: {
469: char * const *av;
470: char *buf, *cp;
471: int buflen, pri, subsys, log_envp = 0;
472: size_t plen;
473:
474: if (!sudo_debug_mode)
475: return;
476:
477: /* Extract pri and subsystem from level. */
478: pri = SUDO_DEBUG_PRI(level);
479: subsys = SUDO_DEBUG_SUBSYS(level);
480:
481: /* Make sure we want debug info at this level. */
482: if (subsys >= NUM_SUBSYSTEMS || sudo_debug_settings[subsys] < pri)
483: return;
484:
485: /* Log envp for debug level "debug". */
486: if (sudo_debug_settings[subsys] >= SUDO_DEBUG_DEBUG - 1 && envp[0] != NULL)
487: log_envp = 1;
488:
489: #define EXEC_PREFIX "exec "
490:
491: /* Alloc and build up buffer. */
492: plen = strlen(path);
493: buflen = sizeof(EXEC_PREFIX) -1 + plen;
494: if (argv[0] != NULL) {
495: buflen += sizeof(" []") - 1;
496: for (av = argv; *av; av++)
497: buflen += strlen(*av) + 1;
498: buflen--;
499: }
500: if (log_envp) {
501: buflen += sizeof(" []") - 1;
502: for (av = envp; *av; av++)
503: buflen += strlen(*av) + 1;
504: buflen--;
505: }
506: buf = malloc(buflen + 1);
507: if (buf == NULL)
508: return;
509:
510: /* Copy prefix and command. */
511: memcpy(buf, EXEC_PREFIX, sizeof(EXEC_PREFIX) - 1);
512: cp = buf + sizeof(EXEC_PREFIX) - 1;
513: memcpy(cp, path, plen);
514: cp += plen;
515:
516: /* Copy argv. */
517: if (argv[0] != NULL) {
518: *cp++ = ' ';
519: *cp++ = '[';
520: for (av = argv; *av; av++) {
521: size_t avlen = strlen(*av);
522: memcpy(cp, *av, avlen);
523: cp += avlen;
524: *cp++ = ' ';
525: }
526: cp[-1] = ']';
527: }
528:
529: if (log_envp) {
530: *cp++ = ' ';
531: *cp++ = '[';
532: for (av = envp; *av; av++) {
533: size_t avlen = strlen(*av);
534: memcpy(cp, *av, avlen);
535: cp += avlen;
536: *cp++ = ' ';
537: }
538: cp[-1] = ']';
539: }
540:
541: *cp = '\0';
542:
543: sudo_debug_write(buf, buflen, 0);
544: free(buf);
545: }
546:
547: /*
548: * Dup sudo_debug_fd to the specified value so we don't
549: * close it when calling closefrom().
550: */
551: int
552: sudo_debug_fd_set(int fd)
553: {
554: if (sudo_debug_fd != -1 && fd != sudo_debug_fd) {
555: if (dup2(sudo_debug_fd, fd) == -1)
556: return -1;
557: (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
558: close(sudo_debug_fd);
559: sudo_debug_fd = fd;
560: }
561: return sudo_debug_fd;
562: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>