File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / common / sudo_debug.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:26:49 2012 UTC (12 years, 1 month ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_5p1, HEAD
sudo 1.8.5p1

    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>