From: Todd C. Miller <Todd.Miller@courtesan.com> Date: Sat, 22 Oct 2011 18:00:52 +0000 (-0400) Subject: New debug framework for sudo and plugins using /etc/sudo.conf that X-Git-Tag: SUDO_1_8_4~185^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=53e3ad11b503db5630271df40220e714da2a4f64;p=sudo New debug framework for sudo and plugins using /etc/sudo.conf that also supports function call tracing. --- diff --git a/MANIFEST b/MANIFEST index f7c1f90ba..07d70ae89 100644 --- 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 diff --git a/common/Makefile.in b/common/Makefile.in index a1319eff6..598834e6e 100644 --- a/common/Makefile.in +++ b/common/Makefile.in @@ -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 index 000000000..a03546191 --- /dev/null +++ b/common/sudo_debug.c @@ -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 index 000000000..96733c272 --- /dev/null +++ b/include/sudo_debug.h @@ -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 */ diff --git a/include/sudo_plugin.h b/include/sudo_plugin.h index 230650332..6fd3e6794 100644 --- a/include/sudo_plugin.h +++ b/include/sudo_plugin.h @@ -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; diff --git a/src/conversation.c b/src/conversation.c index fa40f791a..59b8e26e5 100644 --- a/src/conversation.c +++ b/src/conversation.c @@ -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; } diff --git a/src/load_plugins.c b/src/load_plugins.c index 6bee3f847..8c8291c01 100644 --- a/src/load_plugins.c +++ b/src/load_plugins.c @@ -42,11 +42,13 @@ #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 @@ -56,19 +58,130 @@ 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) { diff --git a/src/parse_args.c b/src/parse_args.c index f8aac4b62..f6a8a4646 100644 --- a/src/parse_args.c +++ b/src/parse_args.c @@ -53,6 +53,10 @@ 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') diff --git a/src/sudo.c b/src/sudo.c index 40cfc2f4c..ee12e54db 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -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. */ diff --git a/src/sudo_plugin_int.h b/src/sudo_plugin_int.h index 772cde9c6..c7ab89e59 100644 --- a/src/sudo_plugin_int.h +++ b/src/sudo_plugin_int.h @@ -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 */