Annotation of embedaddon/sudo/common/sudo_debug.c, revision 1.1.1.3
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"
52: #include "error.h"
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:
158: /* Parse settings string. */
159: buf = estrdup(settings);
160: for ((cp = strtok(buf, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
161: /* Should be in the form subsys@pri. */
162: subsys = cp;
163: if ((pri = strchr(cp, '@')) == NULL)
164: continue;
165: *pri++ = '\0';
166:
167: /* Look up priority and subsystem, fill in sudo_debug_settings[]. */
168: for (i = 0; sudo_debug_priorities[i] != NULL; i++) {
169: if (strcasecmp(pri, sudo_debug_priorities[i]) == 0) {
170: for (j = 0; sudo_debug_subsystems[j] != NULL; j++) {
171: if (strcasecmp(subsys, "all") == 0) {
172: sudo_debug_settings[j] = i;
173: continue;
174: }
175: if (strcasecmp(subsys, sudo_debug_subsystems[j]) == 0) {
176: sudo_debug_settings[j] = i;
177: break;
178: }
179: }
180: break;
181: }
182: }
183: }
184: efree(buf);
185:
186: (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
187: (int)getpid());
188: sudo_debug_pidlen = strlen(sudo_debug_pidstr);
189:
190: return 1;
191: }
192:
193: pid_t
194: sudo_debug_fork(void)
195: {
196: pid_t pid;
197:
198: if ((pid = fork()) == 0) {
199: (void)snprintf(sudo_debug_pidstr, sizeof(sudo_debug_pidstr), "[%d] ",
200: (int)getpid());
201: sudo_debug_pidlen = strlen(sudo_debug_pidstr);
202: }
203:
204: return pid;
205: }
206:
207: void
208: sudo_debug_enter(const char *func, const char *file, int line,
209: int subsys)
210: {
211: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
212: "-> %s @ %s:%d", func, file, line);
213: }
214:
215: void sudo_debug_exit(const char *func, const char *file, int line,
216: int subsys)
217: {
218: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
219: "<- %s @ %s:%d", func, file, line);
220: }
221:
222: void sudo_debug_exit_int(const char *func, const char *file, int line,
223: int subsys, int rval)
224: {
225: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
226: "<- %s @ %s:%d := %d", func, file, line, rval);
227: }
228:
229: void sudo_debug_exit_long(const char *func, const char *file, int line,
230: int subsys, long rval)
231: {
232: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
233: "<- %s @ %s:%d := %ld", func, file, line, rval);
234: }
235:
236: void sudo_debug_exit_size_t(const char *func, const char *file, int line,
237: int subsys, size_t rval)
238: {
239: /* XXX - should use %zu but our snprintf.c doesn't support it */
240: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
241: "<- %s @ %s:%d := %lu", func, file, line, (unsigned long)rval);
242: }
243:
244: /* We use int, not bool, here for functions that return -1 on error. */
245: void sudo_debug_exit_bool(const char *func, const char *file, int line,
246: int subsys, int rval)
247: {
248: if (rval == true || rval == false) {
249: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
250: "<- %s @ %s:%d := %s", func, file, line, rval ? "true" : "false");
251: } else {
252: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
253: "<- %s @ %s:%d := %d", func, file, line, rval);
254: }
255: }
256:
257: void sudo_debug_exit_str(const char *func, const char *file, int line,
258: int subsys, const char *rval)
259: {
260: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
261: "<- %s @ %s:%d := %s", func, file, line, rval ? rval : "(null)");
262: }
263:
264: void sudo_debug_exit_str_masked(const char *func, const char *file, int line,
265: int subsys, const char *rval)
266: {
267: static const char stars[] = "********************************************************************************";
268: int len = rval ? strlen(rval) : sizeof("(null)") - 1;
269:
270: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
271: "<- %s @ %s:%d := %.*s", func, file, line, len, rval ? stars : "(null)");
272: }
273:
274: void sudo_debug_exit_ptr(const char *func, const char *file, int line,
275: int subsys, const void *rval)
276: {
277: sudo_debug_printf2(NULL, NULL, 0, subsys | SUDO_DEBUG_TRACE,
278: "<- %s @ %s:%d := %p", func, file, line, rval);
279: }
280:
281: static void
282: sudo_debug_write_conv(const char *func, const char *file, int lineno,
283: const char *str, int len, int errno_val)
284: {
1.1.1.3 ! misho 285: /* Remove the newline at the end if appending extra info. */
! 286: if (str[len - 1] == '\n')
! 287: len--;
! 288:
! 289: if (func != NULL && file != NULL) {
! 290: if (errno_val) {
! 291: sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s: %s @ %s() %s:%d",
! 292: len, str, strerror(errno_val), func, file, lineno);
! 293: } else {
! 294: sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s @ %s() %s:%d",
! 295: len, str, func, file, lineno);
! 296: }
! 297: } else {
! 298: if (errno_val) {
! 299: sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s: %s",
! 300: len, str, strerror(errno_val));
! 301: } else {
! 302: sudo_printf(SUDO_CONV_DEBUG_MSG, "%.*s", len, str);
1.1 misho 303: }
304: }
305: }
306:
307: static void
308: sudo_debug_write_file(const char *func, const char *file, int lineno,
309: const char *str, int len, int errno_val)
310: {
311: char *timestr, numbuf[(((sizeof(int) * 8) + 2) / 3) + 2];
312: time_t now;
313: struct iovec iov[12];
314: int iovcnt = 4;
315: bool need_newline = false;
316:
317: /* Prepend program name and pid with a trailing space. */
318: iov[1].iov_base = (char *)getprogname();
319: iov[1].iov_len = strlen(iov[1].iov_base);
320: iov[2].iov_base = sudo_debug_pidstr;
321: iov[2].iov_len = sudo_debug_pidlen;
322:
323: /* Add string along with newline if it doesn't have one. */
324: iov[3].iov_base = (char *)str;
325: iov[3].iov_len = len;
326: if (str[len - 1] != '\n')
327: need_newline = true;
328:
329: /* Append error string if errno is specified. */
330: if (errno_val) {
331: iov[iovcnt].iov_base = ": ";
332: iov[iovcnt].iov_len = 2;
333: iovcnt++;
334: iov[iovcnt].iov_base = strerror(errno_val);
335: iov[iovcnt].iov_len = strlen(iov[iovcnt].iov_base);
336: iovcnt++;
337:
338: /* Move newline to the end. */
339: if (!need_newline) {
340: need_newline = true;
341: iov[3].iov_len--;
342: }
343: }
344:
345: /* If function, file and lineno are specified, append them. */
346: if (func != NULL && file != NULL && lineno != 0) {
347: iov[iovcnt].iov_base = " @ ";
348: iov[iovcnt].iov_len = 3;
349: iovcnt++;
350:
351: iov[iovcnt].iov_base = (char *)func;
352: iov[iovcnt].iov_len = strlen(func);
353: iovcnt++;
354:
355: iov[iovcnt].iov_base = "() ";
356: iov[iovcnt].iov_len = 3;
357: iovcnt++;
358:
359: iov[iovcnt].iov_base = (char *)file;
360: iov[iovcnt].iov_len = strlen(file);
361: iovcnt++;
362:
363: (void)snprintf(numbuf, sizeof(numbuf), ":%d", lineno);
364: iov[iovcnt].iov_base = numbuf;
365: iov[iovcnt].iov_len = strlen(numbuf);
366: iovcnt++;
367:
368: /* Move newline to the end. */
369: if (!need_newline) {
370: need_newline = true;
371: iov[3].iov_len--;
372: }
373: }
374:
375: /* Append newline as needed. */
376: if (need_newline) {
377: /* force newline */
378: iov[iovcnt].iov_base = "\n";
379: iov[iovcnt].iov_len = 1;
380: iovcnt++;
381: }
382:
383: /* Do timestamp last due to ctime's static buffer. */
1.1.1.3 ! misho 384: time(&now);
1.1 misho 385: timestr = ctime(&now) + 4;
386: timestr[15] = ' '; /* replace year with a space */
387: timestr[16] = '\0';
388: iov[0].iov_base = timestr;
389: iov[0].iov_len = 16;
390:
391: /* Write message in a single syscall */
392: ignore_result(writev(sudo_debug_fd, iov, iovcnt));
393: }
394:
395: void
396: sudo_debug_write2(const char *func, const char *file, int lineno,
397: const char *str, int len, int errno_val)
398: {
399: if (len <= 0)
400: return;
401:
402: switch (sudo_debug_mode) {
403: case SUDO_DEBUG_MODE_CONV:
404: sudo_debug_write_conv(func, file, lineno, str, len, errno_val);
405: break;
406: case SUDO_DEBUG_MODE_FILE:
407: sudo_debug_write_file(func, file, lineno, str, len, errno_val);
408: break;
409: }
410: }
411:
412: /* XXX - turn into a macro */
413: void
414: sudo_debug_write(const char *str, int len, int errno_val)
415: {
416: sudo_debug_write2(NULL, NULL, 0, str, len, errno_val);
417: }
418:
419: void
1.1.1.3 ! misho 420: sudo_debug_vprintf2(const char *func, const char *file, int lineno, int level,
! 421: const char *fmt, va_list ap)
1.1 misho 422: {
423: int buflen, pri, subsys, saved_errno = errno;
424: char *buf;
425:
426: if (!sudo_debug_mode)
427: return;
428:
429: /* Extract pri and subsystem from level. */
430: pri = SUDO_DEBUG_PRI(level);
431: subsys = SUDO_DEBUG_SUBSYS(level);
432:
433: /* Make sure we want debug info at this level. */
434: if (subsys < NUM_SUBSYSTEMS && sudo_debug_settings[subsys] >= pri) {
435: buflen = vasprintf(&buf, fmt, ap);
436: va_end(ap);
437: if (buflen != -1) {
438: int errcode = ISSET(level, SUDO_DEBUG_ERRNO) ? saved_errno : 0;
439: if (ISSET(level, SUDO_DEBUG_LINENO))
440: sudo_debug_write2(func, file, lineno, buf, buflen, errcode);
441: else
442: sudo_debug_write2(NULL, NULL, 0, buf, buflen, errcode);
443: free(buf);
444: }
445: }
446:
447: errno = saved_errno;
448: }
449:
450: void
1.1.1.3 ! misho 451: sudo_debug_printf2(const char *func, const char *file, int lineno, int level,
! 452: const char *fmt, ...)
! 453: {
! 454: va_list ap;
! 455:
! 456: va_start(ap, fmt);
! 457: sudo_debug_vprintf2(func, file, lineno, level, fmt, ap);
! 458: va_end(ap);
! 459: }
! 460:
! 461: void
1.1 misho 462: sudo_debug_execve2(int level, const char *path, char *const argv[], char *const envp[])
463: {
464: char * const *av;
465: char *buf, *cp;
466: int buflen, pri, subsys, log_envp = 0;
467: size_t plen;
468:
469: if (!sudo_debug_mode)
470: return;
471:
472: /* Extract pri and subsystem from level. */
473: pri = SUDO_DEBUG_PRI(level);
474: subsys = SUDO_DEBUG_SUBSYS(level);
475:
476: /* Make sure we want debug info at this level. */
477: if (subsys >= NUM_SUBSYSTEMS || sudo_debug_settings[subsys] < pri)
478: return;
479:
480: /* Log envp for debug level "debug". */
481: if (sudo_debug_settings[subsys] >= SUDO_DEBUG_DEBUG - 1 && envp[0] != NULL)
482: log_envp = 1;
483:
484: #define EXEC_PREFIX "exec "
485:
486: /* Alloc and build up buffer. */
487: plen = strlen(path);
488: buflen = sizeof(EXEC_PREFIX) -1 + plen;
489: if (argv[0] != NULL) {
490: buflen += sizeof(" []") - 1;
491: for (av = argv; *av; av++)
492: buflen += strlen(*av) + 1;
493: buflen--;
494: }
495: if (log_envp) {
496: buflen += sizeof(" []") - 1;
497: for (av = envp; *av; av++)
498: buflen += strlen(*av) + 1;
499: buflen--;
500: }
501: buf = malloc(buflen + 1);
502: if (buf == NULL)
503: return;
504:
505: /* Copy prefix and command. */
506: memcpy(buf, EXEC_PREFIX, sizeof(EXEC_PREFIX) - 1);
507: cp = buf + sizeof(EXEC_PREFIX) - 1;
508: memcpy(cp, path, plen);
509: cp += plen;
510:
511: /* Copy argv. */
512: if (argv[0] != NULL) {
513: *cp++ = ' ';
514: *cp++ = '[';
515: for (av = argv; *av; av++) {
516: size_t avlen = strlen(*av);
517: memcpy(cp, *av, avlen);
518: cp += avlen;
519: *cp++ = ' ';
520: }
521: cp[-1] = ']';
522: }
523:
524: if (log_envp) {
525: *cp++ = ' ';
526: *cp++ = '[';
527: for (av = envp; *av; av++) {
528: size_t avlen = strlen(*av);
529: memcpy(cp, *av, avlen);
530: cp += avlen;
531: *cp++ = ' ';
532: }
533: cp[-1] = ']';
534: }
535:
536: *cp = '\0';
537:
538: sudo_debug_write(buf, buflen, 0);
539: free(buf);
540: }
541:
542: /*
543: * Dup sudo_debug_fd to the specified value so we don't
544: * close it when calling closefrom().
545: */
546: int
547: sudo_debug_fd_set(int fd)
548: {
549: if (sudo_debug_fd != -1 && fd != sudo_debug_fd) {
550: if (dup2(sudo_debug_fd, fd) == -1)
551: return -1;
552: (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
553: close(sudo_debug_fd);
554: sudo_debug_fd = fd;
555: }
556: return sudo_debug_fd;
557: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>