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