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