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