Annotation of embedaddon/sudo/common/sudo_debug.c, revision 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>