also supports function call tracing.
common/lbuf.c
common/list.c
common/setgroups.c
+common/sudo_debug.c
common/term.c
common/zero_bytes.c
compat/Makefile.in
include/lbuf.h
include/list.h
include/missing.h
+include/sudo_debug.h
include/sudo_plugin.h
indent.pro
install-sh
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
$(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 \
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
#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;
#include "sudo.h"
#include "sudo_plugin.h"
+#include "sudo_debug.h"
extern int tgetpass_flags; /* XXX */
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;
}
#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) {
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));
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));
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;
}
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) {
extern char *optarg;
extern int optind;
+/* XXX */
+extern const char *debug_file;
+extern const char *debug_flags;
+
int tgetpass_flags;
/*
{ "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 }
};
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')
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);
(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. */
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 */