]> granicus.if.org Git - sudo/commitdiff
New debug framework for sudo and plugins using /etc/sudo.conf that
authorTodd C. Miller <Todd.Miller@courtesan.com>
Sat, 22 Oct 2011 18:00:52 +0000 (14:00 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Sat, 22 Oct 2011 18:00:52 +0000 (14:00 -0400)
also supports function call tracing.

MANIFEST
common/Makefile.in
common/sudo_debug.c [new file with mode: 0644]
include/sudo_debug.h [new file with mode: 0644]
include/sudo_plugin.h
src/conversation.c
src/load_plugins.c
src/parse_args.c
src/sudo.c
src/sudo_plugin_int.h

index f7c1f90ba6034515b3962c9e4d38b6a06afc8fcc..07d70ae89ec502acc752e184ff4ec4c9e5f26176 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -16,6 +16,7 @@ common/fmt_string.c
 common/lbuf.c
 common/list.c
 common/setgroups.c
+common/sudo_debug.c
 common/term.c
 common/zero_bytes.c
 compat/Makefile.in
@@ -101,6 +102,7 @@ include/gettext.h
 include/lbuf.h
 include/list.h
 include/missing.h
+include/sudo_debug.h
 include/sudo_plugin.h
 indent.pro
 install-sh
index a1319eff6304842f8b3884511636c7ce8a5e9f88..598834e6eb398129cf2ba9a40bc457f679e736ce 100644 (file)
@@ -42,8 +42,8 @@ DEFS = @OSDEFS@
 
 SHELL = @SHELL@
 
-LTOBJS = alloc.lo atobool.lo fileops.lo fmt_string.lo \
-        lbuf.lo list.lo setgroups.lo term.lo zero_bytes.lo @COMMON_OBJS@
+LTOBJS = alloc.lo atobool.lo fileops.lo fmt_string.lo lbuf.lo list.lo \
+       setgroups.lo sudo_debug.lo term.lo zero_bytes.lo @COMMON_OBJS@
 
 all: libcommon.la
 
@@ -115,6 +115,10 @@ list.lo: $(srcdir)/list.c $(top_builddir)/config.h $(incdir)/missing.h \
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/list.c
 setgroups.lo: $(srcdir)/setgroups.c $(top_builddir)/config.h $(incdir)/missing.h
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/setgroups.c
+sudo_debug.lo: $(srcdir)/sudo_debug.c $(top_builddir)/config.h \
+              $(incdir)/missing.h $(incdir)/alloc.h $(incdir)/error.h \
+              $(incdir)/gettext.h $(incdir)/sudo_debug.h
+       $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/sudo_debug.c
 term.lo: $(srcdir)/term.c $(top_builddir)/config.h $(incdir)/missing.h
        $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/term.c
 zero_bytes.lo: $(srcdir)/zero_bytes.c $(top_builddir)/config.h \
diff --git a/common/sudo_debug.c b/common/sudo_debug.c
new file mode 100644 (file)
index 0000000..a035461
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2011 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "missing.h"
+#include "alloc.h"
+#include "error.h"
+#include "gettext.h"
+#include "sudo_debug.h"
+
+/*
+ * The debug priorities and subsystems are currently hard-coded.
+ * In the future we might consider allowing plugins to register their
+ * own subsystems and provide direct access to the debugging API.
+ */
+
+/* Note: this must match the order in sudo_debug.h */
+const char *const sudo_debug_priorities[] = {
+    "crit",
+    "syserr",
+    "progerr",
+    "warn",
+    "notice",
+    "diag",
+    "info",
+    "trace",
+    "debug",
+    NULL
+};
+
+/* Note: this must match the order in sudo_debug.h */
+const char *const sudo_debug_subsystems[] = {
+    "main",
+    "memory",
+    "args",
+    "exec",
+    "pty",
+    "utmp",
+    "conv",
+    "pcomm",
+    "util",
+    "list",
+    "netif",
+    "audit",
+    "edit",
+    "selinux",
+    "ldap",
+    "match",
+    "parser",
+    "alias",
+    "defaults",
+    "auth",
+    "env",
+    "logging",
+    "nss",
+    "rbtree",
+    "perms",
+    "plugin",
+    NULL
+};
+
+#define NUM_SUBSYSTEMS (sizeof(sudo_debug_subsystems) / sizeof(sudo_debug_subsystems[0]) - 1)
+
+static int sudo_debug_settings[NUM_SUBSYSTEMS];
+static int sudo_debug_fd = -1;
+
+/*
+ * Parse settings string from sudo.conf and open debugfile.
+ * Returns 1 on success, 0 if cannot open debugfile.
+ * Unsupported subsystems and priorities are silently ignored.
+ */
+int sudo_debug_init(const char *debugfile, const char *settings)
+{
+    char *buf, *cp, *subsys, *pri;
+    int i, j;
+
+    /* Init per-subsystems settings to -1 since 0 is a valid priority. */
+    for (i = 0; i < NUM_SUBSYSTEMS; i++)
+       sudo_debug_settings[i] = -1;
+
+    /* Open debug file descriptor. */
+    if (sudo_debug_fd != -1)
+       close(sudo_debug_fd);
+    sudo_debug_fd = open(debugfile, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
+    if (sudo_debug_fd == -1)
+       return 0;
+    (void)fcntl(sudo_debug_fd, F_SETFD, FD_CLOEXEC);
+
+    /* Parse settings string. */
+    buf = estrdup(settings);
+    for ((cp = strtok(buf, ",")); cp != NULL; (cp = strtok(NULL, ","))) {
+       /* Should be in the form subsys@pri. */
+       subsys = cp;
+       if ((pri = strchr(cp, '@')) == NULL)
+           continue;
+       *pri++ = '\0';
+
+       /* Look up priority and subsystem, fill in sudo_debug_settings[]. */
+       for (i = 0; sudo_debug_priorities[i] != NULL; i++) {
+           if (strcasecmp(pri, sudo_debug_priorities[i]) == 0) {
+               for (j = 0; sudo_debug_subsystems[j] != NULL; j++) {
+                   if (strcasecmp(subsys, "all") == 0) {
+                       sudo_debug_settings[j] = i;
+                       continue;
+                   }
+                   if (strcasecmp(subsys, sudo_debug_subsystems[j]) == 0) {
+                       sudo_debug_settings[j] = i;
+                       break;
+                   }
+               }
+               break;
+           }
+       }
+    }
+    efree(buf);
+
+    return 1;
+}
+
+void
+sudo_debug_enter(const char *func, const char *file, int line,
+    int subsys)
+{
+    sudo_debug_printf2(subsys | SUDO_DEBUG_TRACE, "-> %s @ %s:%d", func,
+       file, line);
+}
+
+void sudo_debug_exit(const char *func, const char *file, int line,
+    int subsys)
+{
+    sudo_debug_printf2(subsys | SUDO_DEBUG_TRACE, "<- %s @ %s:%d", func,
+       file, line);
+}
+
+void sudo_debug_exit_int(const char *func, const char *file, int line,
+    int subsys, int rval)
+{
+    sudo_debug_printf2(subsys | SUDO_DEBUG_TRACE, "<- %s @ %s:%d := %d", func,
+       file, line, rval);
+}
+
+void sudo_debug_exit_long(const char *func, const char *file, int line,
+    int subsys, long rval)
+{
+    sudo_debug_printf2(subsys | SUDO_DEBUG_TRACE, "<- %s @ %s:%d := %ld", func,
+       file, line, rval);
+}
+
+void sudo_debug_exit_size_t(const char *func, const char *file, int line,
+    int subsys, size_t rval)
+{
+    /* XXX - should use %zu but snprintf.c doesn't support it */
+    sudo_debug_printf2(subsys | SUDO_DEBUG_TRACE, "<- %s @ %s:%d := %lu", func,
+       file, line, (unsigned long)rval);
+}
+
+void sudo_debug_exit_bool(const char *func, const char *file, int line,
+    int subsys, int rval)
+{
+    if (rval == 0 || rval == 1) {
+       sudo_debug_printf2(subsys | SUDO_DEBUG_TRACE, "<- %s @ %s:%d := %s",
+           func, file, line, rval ? "true" : "false");
+    } else {
+       sudo_debug_printf2(subsys | SUDO_DEBUG_TRACE, "<- %s @ %s:%d := %d",
+           func, file, line, rval);
+    }
+}
+
+void sudo_debug_exit_str(const char *func, const char *file, int line,
+    int subsys, const char *rval)
+{
+    sudo_debug_printf2(subsys | SUDO_DEBUG_TRACE, "<- %s @ %s:%d := %s", func,
+       file, line, rval ? rval : "(null)");
+}
+
+void sudo_debug_exit_str_masked(const char *func, const char *file, int line,
+    int subsys, const char *rval)
+{
+    static const char stars[] = "********************************************************************************";
+    int len = rval ? strlen(rval) : sizeof("(null)") - 1;
+
+    sudo_debug_printf2(subsys | SUDO_DEBUG_TRACE, "<- %s @ %s:%d := %.*s", func,
+       file, line, len, rval ? stars : "(null)");
+}
+
+void sudo_debug_exit_ptr(const char *func, const char *file, int line,
+    int subsys, const void *rval)
+{
+    sudo_debug_printf2(subsys | SUDO_DEBUG_TRACE, "<- %s @ %s:%d := %p", func,
+       file, line, rval);
+}
+
+void
+sudo_debug_write(const char *str, int len)
+{
+    char *timestr;
+    time_t now;
+    ssize_t n;
+    struct iovec iov[5];
+    int iovcnt = 4;
+
+    if (sudo_debug_fd == -1 || len <= 0)
+       return;
+
+    /* Prepend program name with trailing space. */
+    iov[1].iov_base = (char *)getprogname();
+    iov[1].iov_len = strlen(iov[1].iov_base);
+    iov[2].iov_base = " ";
+    iov[2].iov_len = 1;
+
+    /* Add string along with newline if it doesn't have one. */
+    iov[3].iov_base = (char *)str;
+    iov[3].iov_len = len;
+    if (str[len - 1] != '\n') {
+       /* force newline */
+       iov[4].iov_base = "\n";
+       iov[4].iov_len = 1;
+       iovcnt++;
+    }
+
+    /* Do timestamp last due to ctime's static buffer. */
+    now = time(NULL);
+    timestr = ctime(&now) + 4;
+    timestr[15] = ' '; /* replace year with a space */
+    timestr[16] = '\0';
+    iov[0].iov_base = timestr;
+    iov[0].iov_len = 16;
+
+    /* Write message in a single syscall */
+    n = writev(sudo_debug_fd, iov, iovcnt);
+}
+
+void
+sudo_debug_printf2(int level, const char *fmt, ...)
+{
+    int buflen, pri, subsys;
+    va_list ap;
+    char *buf;
+
+    if (sudo_debug_fd == -1)
+       return;
+
+    /* Extract pri and subsystem from level. */
+    pri = SUDO_DEBUG_PRI(level);
+    subsys = SUDO_DEBUG_SUBSYS(level);
+
+    /* Make sure we want debug info at this level. */
+    if (subsys >= NUM_SUBSYSTEMS || sudo_debug_settings[subsys] < pri)
+       return;
+
+    va_start(ap, fmt);
+    buflen = vasprintf(&buf, fmt, ap);
+    va_end(ap);
+    if (buflen != -1) {
+       sudo_debug_write(buf, buflen);
+       free(buf);
+    }
+}
+
+void
+sudo_debug_execve2(int level, const char *path, char *const argv[], char *const envp[])
+{
+    char * const *av;
+    char *buf, *cp;
+    int buflen, pri, subsys, log_envp = 0;
+    size_t plen;
+
+    if (sudo_debug_fd == -1)
+       return;
+
+#define EXEC_PREFIX "exec "
+    if (sudo_debug_fd == -1)
+       return;
+
+    /* Extract pri and subsystem from level. */
+    pri = SUDO_DEBUG_PRI(level);
+    subsys = SUDO_DEBUG_SUBSYS(level);
+
+    /* Make sure we want debug info at this level. */
+    if (subsys >= NUM_SUBSYSTEMS || sudo_debug_settings[subsys] < pri)
+       return;
+
+    /* Log envp for debug level "debug". */
+    if (sudo_debug_settings[subsys] >= SUDO_DEBUG_DEBUG - 1 && envp[0] != NULL)
+       log_envp = 1;
+
+    /* Alloc and build up buffer. */
+    plen = strlen(path);
+    buflen = sizeof(EXEC_PREFIX) -1 + plen;
+    if (argv[0] != NULL) {
+       buflen += sizeof(" []") - 1;
+       for (av = argv; *av; av++)
+           buflen += strlen(*av) + 1;
+       buflen--;
+    }
+    if (log_envp) {
+       buflen += sizeof(" []") - 1;
+       for (av = envp; *av; av++)
+           buflen += strlen(*av) + 1;
+       buflen--;
+    }
+    buf = malloc(buflen + 1);
+    if (buf == NULL)
+       return;
+
+    /* Copy prefix and command. */
+    memcpy(buf, EXEC_PREFIX, sizeof(EXEC_PREFIX) - 1);
+    cp = buf + sizeof(EXEC_PREFIX) - 1;
+    memcpy(cp, path, plen);
+    cp += plen;
+
+    /* Copy argv. */
+    if (argv[0] != NULL) {
+       *cp++ = ' ';
+       *cp++ = '[';
+       for (av = argv; *av; av++) {
+           size_t avlen = strlen(*av);
+           memcpy(cp, *av, avlen);
+           cp += avlen;
+           *cp++ = ' ';
+       }
+       cp[-1] = ']';
+    }
+
+    if (log_envp) {
+       *cp++ = ' ';
+       *cp++ = '[';
+       for (av = envp; *av; av++) {
+           size_t avlen = strlen(*av);
+           memcpy(cp, *av, avlen);
+           cp += avlen;
+           *cp++ = ' ';
+       }
+       cp[-1] = ']';
+    }
+
+    *cp = '\0';
+
+    sudo_debug_write(buf, buflen);
+    free(buf);
+}
diff --git a/include/sudo_debug.h b/include/sudo_debug.h
new file mode 100644 (file)
index 0000000..96733c2
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2011 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SUDO_DEBUG_H
+#define _SUDO_DEBUG_H
+
+#include <stdarg.h>
+
+/*
+ * The priority and subsystem are encoded in a single 32-bit value.
+ * The lower 4 bits are the priority and the top 28 bits are the subsystem.
+ * This allows for 16 priorities and a very large number of subsystems.
+ */
+
+/*
+ * Sudo debug priorities, ordered least to most verbose,
+ * in other words, highest to lowest priority.  Max pri is 15.
+ * Note: order must match sudo_debug_priorities[]
+ */
+#define SUDO_DEBUG_CRIT                1       /* critical errors */
+#define SUDO_DEBUG_SYSERR      2       /* system errors */
+#define SUDO_DEBUG_PROGERR     3       /* program errors */
+#define SUDO_DEBUG_WARN                4       /* non-fatal warnings */
+#define SUDO_DEBUG_NOTICE      5       /* non-error condition notices */
+#define SUDO_DEBUG_DIAG                6       /* diagnostic messages */
+#define SUDO_DEBUG_INFO                7       /* informational message */
+#define SUDO_DEBUG_TRACE       8       /* log function enter/exit */
+#define SUDO_DEBUG_DEBUG       9       /* very verbose debugging */
+
+/*
+ * Sudo debug subsystems.
+ * This includes subsystems in the sudoers plugin.
+ * Note: order must match sudo_debug_subsystems[]
+ */
+#define SUDO_DEBUG_MAIN                (1<<4)  /* sudo main() */
+#define SUDO_DEBUG_MEMORY      (2<<4)  /* memory subsystems */
+#define SUDO_DEBUG_ARGS                (3<<4)  /* command line argument processing */
+#define SUDO_DEBUG_EXEC                (4<<4)  /* command execution */
+#define SUDO_DEBUG_PTY         (5<<4)  /* pseudo-tty */
+#define SUDO_DEBUG_UTMP                (6<<4)  /* utmp file ops */
+#define SUDO_DEBUG_CONV                (7<<4)  /* user conversation */
+#define SUDO_DEBUG_PCOMM       (8<<4)  /* plugin communications */
+#define SUDO_DEBUG_UTIL                (9<<4)  /* utility functions */
+#define SUDO_DEBUG_LIST                (10<<4) /* linked list functions */
+#define SUDO_DEBUG_NETIF       (11<<4) /* network interface functions */
+#define SUDO_DEBUG_AUDIT       (12<<4) /* audit */
+#define SUDO_DEBUG_EDIT                (13<<4) /* sudoedit */
+#define SUDO_DEBUG_SELINUX     (14<<4) /* selinux */
+#define SUDO_DEBUG_LDAP                (15<<4) /* sudoers LDAP */
+#define SUDO_DEBUG_MATCH       (16<<4) /* sudoers matching */
+#define SUDO_DEBUG_PARSER      (17<<4) /* sudoers parser */
+#define SUDO_DEBUG_ALIAS       (18<<4) /* sudoers alias functions */
+#define SUDO_DEBUG_DEFAULTS    (19<<4) /* sudoers defaults settings */
+#define SUDO_DEBUG_AUTH                (20<<4) /* authentication functions */
+#define SUDO_DEBUG_ENV         (21<<4) /* environment handling */
+#define SUDO_DEBUG_LOGGING     (22<<4) /* logging functions */
+#define SUDO_DEBUG_NSS         (23<<4) /* network service switch */
+#define SUDO_DEBUG_RBTREE      (24<<4) /* red-black tree functions */
+#define SUDO_DEBUG_PERMS       (25<<4) /* uid/gid swapping functions */
+#define SUDO_DEBUG_PLUGIN      (26<<4) /* main plugin functions */
+#define SUDO_DEBUG_ALL         0xfff0  /* all subsystems */
+
+/* Extract priority and convert to an index. */
+#define SUDO_DEBUG_PRI(n) (((n) & 0xf) - 1)
+
+/* Extract subsystem and convert to an index. */
+#define SUDO_DEBUG_SUBSYS(n) (((n) >> 4) - 1)
+
+/*
+ * Wrapper for sudo_debug_enter() that declares __func__ as needed
+ * and sets sudo_debug_subsys for sudo_debug_exit().
+ */
+#ifdef HAVE___FUNC__
+# define debug_decl(funcname, subsys)                                         \
+    const int sudo_debug_subsys = (subsys);                                   \
+    sudo_debug_enter(__func__, __FILE__, __LINE__, sudo_debug_subsys);
+#else
+# define debug_decl(funcname, subsys)                                         \
+    const int sudo_debug_subsys = (subsys);                                   \
+    const char *__func__ = #funcname;                                         \
+    sudo_debug_enter(__func__, __FILE__, __LINE__, sudo_debug_subsys);
+#endif
+
+/*
+ * Wrappers for sudo_debug_exit() and friends.
+ */
+#define debug_return                                                          \
+    do {                                                                      \
+       sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys);      \
+       return;                                                                \
+    } while (0)
+
+#define debug_return_int(rval)                                                \
+    do {                                                                      \
+       int sudo_debug_rval = (rval);                                          \
+       sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys,   \
+           sudo_debug_rval);                                                  \
+       return sudo_debug_rval;                                                \
+    } while (0)
+
+#define debug_return_size_t(rval)                                             \
+    do {                                                                      \
+       size_t sudo_debug_rval = (rval);                                       \
+       sudo_debug_exit_size_t(__func__, __FILE__, __LINE__, sudo_debug_subsys,\
+           sudo_debug_rval);                                                  \
+       return sudo_debug_rval;                                                \
+    } while (0)
+
+#define debug_return_long(rval)                                                       \
+    do {                                                                      \
+       long sudo_debug_rval = (rval);                                         \
+       sudo_debug_exit_long(__func__, __FILE__, __LINE__, sudo_debug_subsys,  \
+           sudo_debug_rval);                                                  \
+       return sudo_debug_rval;                                                \
+    } while (0)
+
+#define debug_return_bool(rval)                                                       \
+    do {                                                                      \
+       int sudo_debug_rval = (rval);                                          \
+       sudo_debug_exit_bool(__func__, __FILE__, __LINE__, sudo_debug_subsys,  \
+           sudo_debug_rval);                                                  \
+       return sudo_debug_rval;                                                \
+    } while (0)
+
+#define debug_return_str(rval)                                                \
+    do {                                                                      \
+       const char *sudo_debug_rval = (rval);                                  \
+       sudo_debug_exit_str(__func__, __FILE__, __LINE__, sudo_debug_subsys,   \
+           sudo_debug_rval);                                                  \
+       return (char *)sudo_debug_rval;                                        \
+    } while (0)
+
+#define debug_return_str_masked(rval)                                                 \
+    do {                                                                      \
+       const char *sudo_debug_rval = (rval);                                  \
+       sudo_debug_exit_str_masked(__func__, __FILE__, __LINE__,               \
+           sudo_debug_subsys, sudo_debug_rval);                               \
+       return (char *)sudo_debug_rval;                                        \
+    } while (0)
+
+#define debug_return_ptr(rval)                                                \
+    do {                                                                      \
+       const void *sudo_debug_rval = (rval);                                  \
+       sudo_debug_exit_ptr(__func__, __FILE__, __LINE__, sudo_debug_subsys,   \
+           sudo_debug_rval);                                                  \
+       return (void *)sudo_debug_rval;                                        \
+    } while (0)
+
+/*
+ * Variadic macros are a C99 feature but GNU cpp has supported
+ * a (different) version of them for a long time.
+ */
+#if defined(__GNUC__) && __GNUC__ == 2
+# define sudo_debug_printf(pri, fmt...) \
+    sudo_debug_printf2((pri)|sudo_debug_subsys, (fmt))
+#else
+# define sudo_debug_printf(pri, ...) \
+    sudo_debug_printf2((pri)|sudo_debug_subsys, __VA_ARGS__)
+#endif
+
+#define sudo_debug_execve(pri, path, argv, envp) \
+    sudo_debug_execve2((pri)|sudo_debug_subsys, (path), (argv), (envp))
+
+/*
+ * NULL-terminated string lists of priorities and subsystems.
+ */
+extern const char *const sudo_debug_priorities[];
+extern const char *const sudo_debug_subsystems[];
+
+void sudo_debug_enter(const char *func, const char *file, int line, int subsys);
+void sudo_debug_exit(const char *func, const char *file, int line, int subsys);
+void sudo_debug_exit_int(const char *func, const char *file, int line, int subsys, int rval);
+void sudo_debug_exit_long(const char *func, const char *file, int line, int subsys, long rval);
+void sudo_debug_exit_size_t(const char *func, const char *file, int line, int subsys, size_t rval);
+void sudo_debug_exit_bool(const char *func, const char *file, int line, int subsys, int rval);
+void sudo_debug_exit_str(const char *func, const char *file, int line, int subsys, const char *rval);
+void sudo_debug_exit_str_masked(const char *func, const char *file, int line, int subsys, const char *rval);
+void sudo_debug_exit_ptr(const char *func, const char *file, int line, int subsys, const void *rval);
+int sudo_debug_init(const char *debugfile, const char *settings);
+void sudo_debug_printf2(int level, const char *format, ...) __printflike(2, 3);
+void sudo_debug_write(const char *str, int len);
+void sudo_debug_execve2(int level, const char *path, char *const argv[], char *const envp[]);
+
+#endif /* _SUDO_DEBUG_H */
index 2306503324cd67beabfdd39eda759fa8cfc7000a..6fd3e6794b7d3c205ee7bb052357722931e5bdf8 100644 (file)
@@ -40,6 +40,7 @@ struct sudo_conv_message {
 #define SUDO_CONV_ERROR_MSG        0x0003  /* error message */
 #define SUDO_CONV_INFO_MSG         0x0004  /* informational message */
 #define SUDO_CONV_PROMPT_MASK      0x0005  /* mask user input */
+#define SUDO_CONV_DEBUG_MSG        0x0006  /* debugging message */
 #define SUDO_CONV_PROMPT_ECHO_OK    0x1000  /* flag: allow echo if no tty */
     int msg_type;
     int timeout;
index fa40f791a0434174c477a8c18fb417c5ac038c1c..59b8e26e5af6f3a7149cb986dc62687fe46c9241 100644 (file)
@@ -47,6 +47,7 @@
 
 #include "sudo.h"
 #include "sudo_plugin.h"
+#include "sudo_debug.h"
 
 extern int tgetpass_flags; /* XXX */
 
@@ -91,6 +92,10 @@ sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[],
                if (msg->msg)
                    (void) fputs(msg->msg, stderr);
                break;
+           case SUDO_CONV_DEBUG_MSG:
+               if (msg->msg)
+                   sudo_debug_write(msg->msg, strlen(msg->msg));
+               break;
            default:
                goto err;
        }
index 6bee3f8471dd7f46b6e48e5c24fcc9e3ea07fef3..8c8291c012b1e0adc5582980103760cfdfc6c2b3 100644 (file)
 #else
 # include "compat/dlfcn.h"
 #endif
+#include <ctype.h>
 #include <errno.h>
 
 #include "sudo.h"
 #include "sudo_plugin.h"
 #include "sudo_plugin_int.h"
+#include "sudo_debug.h"
 
 #ifndef RTLD_GLOBAL
 # define RTLD_GLOBAL   0
 const char *noexec_path = _PATH_SUDO_NOEXEC;
 #endif
 
+/* XXX - for parse_args() */
+const char *debug_file;
+const char *debug_flags;
+
+struct sudo_conf_table {
+    const char *name;
+    unsigned int namelen;
+    int (*setter)(const char *entry, void *data);
+};
+
+struct sudo_conf_paths {
+    const char *pname;
+    unsigned int pnamelen;
+    const char **pval;
+};
+
+static int set_debug(const char *entry, void *data);
+static int set_path(const char *entry, void *data);
+static int set_plugin(const char *entry, void *data);
+
+static struct plugin_info_list plugin_info_list;
+
+static struct sudo_conf_table sudo_conf_table[] = {
+    { "Debug", sizeof("Debug") - 1, set_debug },
+    { "Path", sizeof("Path") - 1, set_path },
+    { "Plugin", sizeof("Plugin") - 1, set_plugin },
+    { NULL }
+};
+
+static struct sudo_conf_paths sudo_conf_paths[] = {
+    { "askpass", sizeof("askpass"), &askpass_path },
+#ifdef _PATH_SUDO_NOEXEC
+    { "noexec", sizeof("noexec"), &noexec_path },
+#endif
+    { NULL }
+};
+
+static int
+set_debug(const char *entry, void *data)
+{
+    size_t filelen;
+
+    /* Parse Debug line */
+    debug_flags = strpbrk(entry, " \t");
+    if (debug_flags == NULL)
+       return FALSE;
+    filelen = (size_t)(debug_flags - entry);
+    while (isblank((unsigned char)*debug_flags))
+       debug_flags++;
+
+    /* Set debug file and parse the flags. */
+    debug_file = estrndup(entry, filelen);
+    debug_flags = estrdup(debug_flags);
+    sudo_debug_init(debug_file, debug_flags);
+
+    return TRUE;
+}
+
+static int
+set_path(const char *entry, void *data)
+{
+    const char *name, *path;
+    struct sudo_conf_paths *cur;
+
+    /* Parse Path line */
+    name = entry;
+    path = strpbrk(entry, " \t");
+    if (path == NULL)
+       return FALSE;
+    while (isblank((unsigned char)*path))
+       path++;
+
+    /* Match supported paths, ignore the rest. */
+    for (cur = sudo_conf_paths; cur->pname != NULL; cur++) {
+       if (strncasecmp(name, cur->pname, cur->pnamelen) == 0 &&
+           isblank((unsigned char)name[cur->pnamelen])) {
+           *(cur->pval) = estrdup(path);
+           break;
+       }
+    }
+
+    return TRUE;
+}
+
+static int
+set_plugin(const char *entry, void *data)
+{
+    struct plugin_info_list *pil = data;
+    struct plugin_info *info;
+    const char *name, *path;
+    size_t namelen;
+
+    /* Parse Plugin line */
+    name = entry;
+    path = strpbrk(entry, " \t");
+    if (path == NULL)
+       return FALSE;
+    namelen = (size_t)(path - name);
+    while (isblank((unsigned char)*path))
+       path++;
+
+    info = emalloc(sizeof(*info));
+    info->symbol_name = estrndup(name, namelen);
+    info->path = estrdup(path);
+    info->prev = info;
+    info->next = NULL;
+    tq_append(pil, info);
+
+    return TRUE;
+}
+
 /*
- * Read in /etc/sudo.conf
+ * Reads in /etc/sudo.conf
  * Returns a list of plugins.
  */
-static struct plugin_info_list *
-sudo_read_conf(const char *conf_file)
+void
+sudo_read_conf(void)
 {
-    FILE *fp;
-    char *cp, *name, *path;
+    struct sudo_conf_table *cur;
     struct plugin_info *info;
-    static struct plugin_info_list pil; /* XXX */
+    FILE *fp;
+    char *cp;
 
-    if ((fp = fopen(conf_file, "r")) == NULL)
+    if ((fp = fopen(_PATH_SUDO_CONF, "r")) == NULL)
        goto done;
 
     while ((cp = sudo_parseln(fp)) != NULL) {
@@ -76,49 +189,28 @@ sudo_read_conf(const char *conf_file)
        if (*cp == '\0')
            continue;
 
-       /* Look for a line starting with "Path" */
-       if (strncasecmp(cp, "Path", 4) == 0) {
-           /* Parse line */
-           if ((name = strtok(cp + 4, " \t")) == NULL ||
-               (path = strtok(NULL, " \t")) == NULL) {
-               continue;
-           }
-           if (strcasecmp(name, "askpass") == 0)
-               askpass_path = estrdup(path);
-#ifdef _PATH_SUDO_NOEXEC
-           else if (strcasecmp(name, "noexec") == 0)
-               noexec_path = estrdup(path);
-#endif
-           continue;
-       }
-
-       /* Look for a line starting with "Plugin" */
-       if (strncasecmp(cp, "Plugin", 6) == 0) {
-           /* Parse line */
-           if ((name = strtok(cp + 6, " \t")) == NULL ||
-               (path = strtok(NULL, " \t")) == NULL) {
-               continue;
+       for (cur = sudo_conf_table; cur->name != NULL; cur++) {
+           if (strncasecmp(cp, cur->name, cur->namelen) == 0 &&
+               isblank((unsigned char)cp[cur->namelen])) {
+               cp += cur->namelen;
+               while (isblank((unsigned char)*cp))
+                   cp++;
+               if (cur->setter(cp, &plugin_info_list))
+                   break;
            }
-           info = emalloc(sizeof(*info));
-           info->symbol_name = estrdup(name);
-           info->path = estrdup(path);
-           info->prev = info;
-           info->next = NULL;
-           tq_append(&pil, info);
-           continue;
        }
     }
     fclose(fp);
 
 done:
-    if (tq_empty(&pil)) {
+    if (tq_empty(&plugin_info_list)) {
        /* Default policy plugin */
        info = emalloc(sizeof(*info));
        info->symbol_name = "sudoers_policy";
        info->path = SUDOERS_PLUGIN;
        info->prev = info;
        info->next = NULL;
-       tq_append(&pil, info);
+       tq_append(&plugin_info_list, info);
 
        /* Default I/O plugin */
        info = emalloc(sizeof(*info));
@@ -126,33 +218,27 @@ done:
        info->path = SUDOERS_PLUGIN;
        info->prev = info;
        info->next = NULL;
-       tq_append(&pil, info);
+       tq_append(&plugin_info_list, info);
     }
-
-    return &pil;
 }
 
 /*
- * Load the plugins listed in conf_file.
+ * Load the plugins listed in sudo.conf.
  */
 int
-sudo_load_plugins(const char *conf_file,
-    struct plugin_container *policy_plugin,
+sudo_load_plugins(struct plugin_container *policy_plugin,
     struct plugin_container_list *io_plugins)
 {
     struct generic_plugin *plugin;
     struct plugin_container *container;
     struct plugin_info *info;
-    struct plugin_info_list *plugin_list;
     struct stat sb;
     void *handle;
     char path[PATH_MAX];
     int rval = FALSE;
 
-    /* Parse sudo.conf */
-    plugin_list = sudo_read_conf(conf_file);
-
-    tq_foreach_fwd(plugin_list, info) {
+    /* Walk plugin list. */
+    tq_foreach_fwd(&plugin_info_list, info) {
        if (info->path[0] == '/') {
            if (strlcpy(path, info->path, sizeof(path)) >= sizeof(path)) {
                warningx(_("%s: %s"), info->path, strerror(ENAMETOOLONG));
@@ -205,7 +291,7 @@ sudo_load_plugins(const char *conf_file,
        if (plugin->type == SUDO_POLICY_PLUGIN) {
            if (policy_plugin->handle) {
                warningx(_("%s: only a single policy plugin may be loaded"),
-                   conf_file);
+                   _PATH_SUDO_CONF);
                goto done;
            }
            policy_plugin->handle = handle;
@@ -223,7 +309,7 @@ sudo_load_plugins(const char *conf_file,
     }
     if (policy_plugin->handle == NULL) {
        warningx(_("%s: at least one policy plugin must be specified"),
-           conf_file);
+           _PATH_SUDO_CONF);
        goto done;
     }
     if (policy_plugin->u.policy->check_policy == NULL) {
index f8aac4b62b9f4b07c6042eec0e0338cb24fdbb0d..f6a8a4646a446a96bc0c0903d03702df2b81ea24 100644 (file)
 extern char *optarg;
 extern int optind;
 
+/* XXX */
+extern const char *debug_file;
+extern const char *debug_flags;
+
 int tgetpass_flags;
 
 /*
@@ -72,43 +76,47 @@ static struct sudo_settings {
     { "bsdauth_type" },
 #define ARG_LOGIN_CLASS 1
     { "login_class" },
-#define ARG_DEBUG_LEVEL 2
+#define ARG_DEBUG_FILE 2
+    { "debug_file" },
+#define ARG_DEBUG_FLAGS 3
+    { "debug_flags" },
+#define ARG_DEBUG_LEVEL 4
     { "debug_level" },
-#define ARG_PRESERVE_ENVIRONMENT 3
+#define ARG_PRESERVE_ENVIRONMENT 5
     { "preserve_environment" },
-#define ARG_RUNAS_GROUP 4
+#define ARG_RUNAS_GROUP 6
     { "runas_group" },
-#define ARG_SET_HOME 5
+#define ARG_SET_HOME 7
     { "set_home" },
-#define ARG_USER_SHELL 6
+#define ARG_USER_SHELL 8
     { "run_shell" },
-#define ARG_LOGIN_SHELL 7
+#define ARG_LOGIN_SHELL 9
     { "login_shell" },
-#define ARG_IGNORE_TICKET 8
+#define ARG_IGNORE_TICKET 10
     { "ignore_ticket" },
-#define ARG_PROMPT 9
+#define ARG_PROMPT 11
     { "prompt" },
-#define ARG_SELINUX_ROLE 10
+#define ARG_SELINUX_ROLE 12
     { "selinux_role" },
-#define ARG_SELINUX_TYPE 11
+#define ARG_SELINUX_TYPE 13
     { "selinux_type" },
-#define ARG_RUNAS_USER 12
+#define ARG_RUNAS_USER 14
     { "runas_user" },
-#define ARG_PROGNAME 13
+#define ARG_PROGNAME 15
     { "progname" },
-#define ARG_IMPLIED_SHELL 14
+#define ARG_IMPLIED_SHELL 16
     { "implied_shell" },
-#define ARG_PRESERVE_GROUPS 15
+#define ARG_PRESERVE_GROUPS 17
     { "preserve_groups" },
-#define ARG_NONINTERACTIVE 16
+#define ARG_NONINTERACTIVE 18
     { "noninteractive" },
-#define ARG_SUDOEDIT 17
+#define ARG_SUDOEDIT 19
     { "sudoedit" },
-#define ARG_CLOSEFROM 18
+#define ARG_CLOSEFROM 20
     { "closefrom" },
-#define ARG_NET_ADDRS 19
+#define ARG_NET_ADDRS 21
     { "network_addrs" },
-#define NUM_SETTINGS 20
+#define NUM_SETTINGS 22
     { NULL }
 };
 
@@ -144,6 +152,12 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp,
     if (get_net_ifs(&cp) > 0)
        sudo_settings[ARG_NET_ADDRS].value = cp;
 
+    /* Set debug file and flags from sudo.conf. */
+    if (debug_file != NULL)
+       sudo_settings[ARG_DEBUG_FILE].value = debug_file;
+    if (debug_flags != NULL)
+       sudo_settings[ARG_DEBUG_FLAGS].value = debug_flags;
+
     /* Returns true if the last option string was "--" */
 #define got_end_of_args        (optind > 1 && argv[optind - 1][0] == '-' && \
            argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0')
index 40cfc2f4cfeb4103bdf33a74965b7326d9752529..ee12e54db5d023979e4b2ce4c000c0d1e780b266 100644 (file)
@@ -200,6 +200,9 @@ main(int argc, char *argv[], char *envp[])
     memset(&user_details, 0, sizeof(user_details));
     user_info = get_user_info(&user_details);
 
+    /* Read sudo.conf. */
+    sudo_read_conf();
+
     /* Parse command line arguments. */
     sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add);
     sudo_debug(9, "sudo_mode %d", sudo_mode);
@@ -211,8 +214,8 @@ main(int argc, char *argv[], char *envp[])
            (void) printf(_("Configure options: %s\n"), CONFIGURE_ARGS);
     }
 
-    /* Read sudo.conf and load plugins. */
-    if (!sudo_load_plugins(_PATH_SUDO_CONF, &policy_plugin, &io_plugins))
+    /* Load plugins. */
+    if (!sudo_load_plugins(&policy_plugin, &io_plugins))
        errorx(1, _("fatal error, unable to load plugins"));
 
     /* Open policy plugin. */
index 772cde9c6af3169d0277c0a46ea6484a5972d956..c7ab89e59d0a0c30a676c95ecaedc8e412a33908 100644 (file)
@@ -78,8 +78,8 @@ int sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[],
     struct sudo_conv_reply replies[]);
 int _sudo_printf(int msg_type, const char *fmt, ...);
 
-int sudo_load_plugins(const char *conf_file,
-    struct plugin_container *policy_plugin,
+void sudo_read_conf(void);
+int sudo_load_plugins(struct plugin_container *policy_plugin,
     struct plugin_container_list *io_plugins);
 
 #endif /* _SUDO_PLUGIN_INT_H */