common/fmt_string.c
common/lbuf.c
common/list.c
+common/regress/sudo_conf/conf_test.c
+common/regress/sudo_conf/test1.in
+common/regress/sudo_conf/test1.out.ok
+common/regress/sudo_conf/test2.in
+common/regress/sudo_conf/test2.out.ok
+common/regress/sudo_conf/test3.in
+common/regress/sudo_conf/test3.out.ok
common/regress/sudo_parseln/parseln_test.c
common/regress/sudo_parseln/test1.in
common/regress/sudo_parseln/test1.out.ok
SSP_LDFLAGS = @SSP_LDFLAGS@
# Regression tests
-TEST_PROGS = parseln_test
+TEST_PROGS = conf_test parseln_test
TEST_LIBS = @LIBS@ @LIBINTL@
TEST_LDFLAGS = @LDFLAGS@
PARSELN_TEST_OBJS = parseln_test.lo
+CONF_TEST_OBJS = conf_test.lo
+
all: libcommon.la
Makefile: $(srcdir)/Makefile.in
libcommon.la: $(LTOBJS)
$(LIBTOOL) --mode=link $(CC) -o $@ $(LTOBJS) -no-install
+conf_test: $(CONF_TEST_OBJS) libcommon.la
+ $(LIBTOOL) --mode=link $(CC) -o $@ $(CONF_TEST_OBJS) libcommon.la $(TEST_LDFLAGS) $(TEST_LIBS)
+
parseln_test: $(PARSELN_TEST_OBJS) libcommon.la
$(LIBTOOL) --mode=link $(CC) -o $@ $(PARSELN_TEST_OBJS) libcommon.la $(TEST_LDFLAGS) $(TEST_LIBS)
check: $(TEST_PROGS)
@if test X"$(cross_compiling)" != X"yes"; then \
passed=0; failed=0; total=0; \
- dir=sudo_parseln; \
- mkdir -p regress/$$dir; \
- for t in $(srcdir)/regress/$$dir/*.in; do \
- base=`basename $$t .in`; \
- out="regress/$$dir/$$base.out"; \
- ./parseln_test <$$t >$$out; \
- if cmp $$out $(srcdir)/$$out.ok >/dev/null; then \
- passed=`expr $$passed + 1`; \
- echo "$$dir/$$base: OK"; \
- else \
- failed=`expr $$failed + 1`; \
- echo "$$dir/$$base: FAIL"; \
- diff $$out $(srcdir)/$$out.ok; \
- fi; \
- total=`expr $$total + 1`; \
+ for dir in sudo_conf sudo_parseln; do \
+ mkdir -p regress/$$dir; \
+ for t in $(srcdir)/regress/$$dir/*.in; do \
+ base=`basename $$t .in`; \
+ out="regress/$$dir/$$base.out"; \
+ if test "$$dir" = "sudo_conf"; then \
+ ./conf_test $$t >$$out; \
+ else \
+ ./parseln_test <$$t >$$out; \
+ fi; \
+ if cmp $$out $(srcdir)/$$out.ok >/dev/null; then \
+ passed=`expr $$passed + 1`; \
+ echo "$$dir/$$base: OK"; \
+ else \
+ failed=`expr $$failed + 1`; \
+ echo "$$dir/$$base: FAIL"; \
+ diff $$out $(srcdir)/$$out.ok; \
+ fi; \
+ total=`expr $$total + 1`; \
+ done; \
done; \
echo "$$dir: $$passed/$$total tests passed; $$failed/$$total tests failed"; \
exit $$failed; \
list.lo: $(srcdir)/list.c $(top_builddir)/config.h $(incdir)/missing.h \
$(incdir)/list.h $(incdir)/error.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/list.c
+conf_test.lo: $(srcdir)/regress/sudo_conf/conf_test.c \
+ $(top_builddir)/config.h $(top_srcdir)/compat/stdbool.h \
+ $(incdir)/missing.h $(incdir)/fileops.h
+ $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/sudo_conf/conf_test.c
parseln_test.lo: $(srcdir)/regress/sudo_parseln/parseln_test.c \
$(top_builddir)/config.h $(top_srcdir)/compat/stdbool.h \
$(incdir)/missing.h $(incdir)/fileops.h
--- /dev/null
+/*
+ * Copyright (c) 2013 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 <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
+# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
+# include <memory.h>
+# endif
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include "compat/stdbool.h"
+#endif
+
+#include "missing.h"
+#include "sudo_conf.h"
+
+static void sudo_conf_dump(void);
+
+/*
+ * Simple test driver for sudo_conf().
+ * Parses the given configuration file and dumps the resulting
+ * sudo_conf_data struct to the standard output.
+ */
+int
+main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ fprintf(stderr, "usage: conf_test conf_file\n");
+ exit(1);
+ }
+ sudo_conf_read(argv[1]);
+ sudo_conf_dump();
+
+ exit(0);
+}
+
+static void
+sudo_conf_dump(void)
+{
+ struct plugin_info_list *plugins = sudo_conf_plugins();
+ struct sudo_conf_paths *scp;
+ struct plugin_info *info;
+
+ printf("Set disable_coredump %s\n",
+ sudo_conf_disable_coredump() ? "true" : "false");
+ printf("Set group_source %s\n",
+ sudo_conf_group_source() == GROUP_SOURCE_ADAPTIVE ? "adaptive" :
+ sudo_conf_group_source() == GROUP_SOURCE_STATIC ? "static" : "dynamic");
+ printf("Set max_groups %d\n", sudo_conf_max_groups());
+ if (sudo_conf_debug_flags() != NULL)
+ printf("Debug %s %s\n", getprogname(), sudo_conf_debug_flags());
+ if (sudo_conf_askpass_path() != NULL)
+ printf("Path askpass %s\n", sudo_conf_askpass_path());
+#ifdef _PATH_SUDO_NOEXEC
+ if (sudo_conf_noexec_path() != NULL)
+ printf("Path noexec %s\n", sudo_conf_noexec_path());
+#endif
+ tq_foreach_fwd(plugins, info) {
+ printf("Plugin %s %s", info->symbol_name, info->path);
+ if (info->options) {
+ char * const * op;
+ for (op = info->options; *op != NULL; op++)
+ printf(" %s", *op);
+ }
+ putchar('\n');
+ }
+}
+
+/* STUB */
+void
+warning_set_locale(void)
+{
+ return;
+}
+
+/* STUB */
+void
+warning_restore_locale(void)
+{
+ return;
+}
--- /dev/null
+#
+# Sample /etc/sudo.conf file
+#
+# Format:
+# Plugin plugin_name plugin_path plugin_options ...
+# Path askpass /path/to/askpass
+# Path noexec /path/to/sudo_noexec.so
+# Debug sudo /var/log/sudo_debug all@warn
+# Set disable_coredump true
+#
+# Sudo plugins:
+#
+# The plugin_path is relative to ${prefix}/libexec unless fully qualified.
+# The plugin_name corresponds to a global symbol in the plugin
+# that contains the plugin interface structure.
+# The plugin_options are optional.
+#
+# The sudoers plugin is used by default if no Plugin lines are present.
+Plugin sudoers_policy sudoers.so
+Plugin sudoers_io sudoers.so
+
+#
+# Sudo askpass:
+#
+# An askpass helper program may be specified to provide a graphical
+# password prompt for "sudo -A" support. Sudo does not ship with its
+# own askpass program but can use the OpenSSH askpass.
+#
+# Use the OpenSSH askpass
+Path askpass /usr/X11R6/bin/ssh-askpass
+#
+# Use the Gnome OpenSSH askpass
+#Path askpass /usr/libexec/openssh/gnome-ssh-askpass
+
+#
+# Sudo noexec:
+#
+# Path to a shared library containing dummy versions of the execv(),
+# execve() and fexecve() library functions that just return an error.
+# This is used to implement the "noexec" functionality on systems that
+# support C<LD_PRELOAD> or its equivalent.
+# The compiled-in value is usually sufficient and should only be changed
+# if you rename or move the sudo_noexec.so file.
+#
+Path noexec /usr/libexec/sudo_noexec.so
+
+#
+# Core dumps:
+#
+# By default, sudo disables core dumps while it is executing (they
+# are re-enabled for the command that is run).
+# To aid in debugging sudo problems, you may wish to enable core
+# dumps by setting "disable_coredump" to false.
+#
+Set disable_coredump false
+
+#
+# User groups:
+#
+# Sudo passes the user's group list to the policy plugin.
+# If the user is a member of the maximum number of groups (usually 16),
+# sudo will query the group database directly to be sure to include
+# the full list of groups.
+#
+# On some systems, this can be expensive so the behavior is configurable.
+# The "group_source" setting has three possible values:
+# static - use the user's list of groups returned by the kernel.
+# dynamic - query the group database to find the list of groups.
+# adaptive - if user is in less than the maximum number of groups.
+# use the kernel list, else query the group database.
+#
+Set group_source static
--- /dev/null
+Set disable_coredump false
+Set group_source static
+Set max_groups -1
+Path askpass /usr/X11R6/bin/ssh-askpass
+Plugin sudoers_policy sudoers.so
+Plugin sudoers_io sudoers.so
--- /dev/null
+Set disable_coredump true
+Set group_source adaptive
+Set max_groups -1
--- /dev/null
+Plugin sudoers_policy sudoers.so sudoers_file=/etc/sudoers sudoers_mode=0400 sudoers_gid=0 sudoers_uid=0
+Plugin sudoers_io sudoers.so
--- /dev/null
+Set disable_coredump true
+Set group_source adaptive
+Set max_groups -1
+Plugin sudoers_policy sudoers.so sudoers_file=/etc/sudoers sudoers_mode=0400 sudoers_gid=0 sudoers_uid=0
+Plugin sudoers_io sudoers.so
struct sudo_conf_table {
const char *name;
unsigned int namelen;
- void (*setter)(const char *entry);
+ void (*setter)(const char *entry, const char *conf_file);
};
struct sudo_conf_paths {
const char *pval;
};
-static void set_debug(const char *entry);
-static void set_path(const char *entry);
-static void set_plugin(const char *entry);
-static void set_variable(const char *entry);
-static void set_var_disable_coredump(const char *entry);
-static void set_var_group_source(const char *entry);
-static void set_var_max_groups(const char *entry);
+static void set_debug(const char *entry, const char *conf_file);
+static void set_path(const char *entry, const char *conf_file);
+static void set_plugin(const char *entry, const char *conf_file);
+static void set_variable(const char *entry, const char *conf_file);
+static void set_var_disable_coredump(const char *entry, const char *conf_file);
+static void set_var_group_source(const char *entry, const char *conf_file);
+static void set_var_max_groups(const char *entry, const char *conf_file);
-static unsigned int lineno;
+static unsigned int conf_lineno;
static struct sudo_conf_table sudo_conf_table[] = {
{ "Debug", sizeof("Debug") - 1, set_debug },
* "Set variable_name value"
*/
static void
-set_variable(const char *entry)
+set_variable(const char *entry, const char *conf_file)
{
struct sudo_conf_table *var;
entry += var->namelen + 1;
while (isblank((unsigned char)*entry))
entry++;
- var->setter(entry);
+ var->setter(entry, conf_file);
break;
}
}
}
static void
-set_var_disable_coredump(const char *entry)
+set_var_disable_coredump(const char *entry, const char *conf_file)
{
sudo_conf_data.disable_coredump = atobool(entry);
}
static void
-set_var_group_source(const char *entry)
+set_var_group_source(const char *entry, const char *conf_file)
{
if (strcasecmp(entry, "adaptive") == 0) {
sudo_conf_data.group_source = GROUP_SOURCE_ADAPTIVE;
sudo_conf_data.group_source = GROUP_SOURCE_DYNAMIC;
} else {
warningx(_("unsupported group source `%s' in %s, line %d"), entry,
- _PATH_SUDO_CONF, lineno);
+ conf_file, conf_lineno);
}
}
static void
-set_var_max_groups(const char *entry)
+set_var_max_groups(const char *entry, const char *conf_file)
{
long lval;
char *ep;
if (*entry == '\0' || *ep != '\0' || lval < 0 || lval > INT_MAX ||
(errno == ERANGE && lval == LONG_MAX)) {
warningx(_("invalid max groups `%s' in %s, line %d"), entry,
- _PATH_SUDO_CONF, lineno);
+ conf_file, conf_lineno);
} else {
sudo_conf_data.max_groups = (int)lval;
}
* "Debug progname debug_file debug_flags"
*/
static void
-set_debug(const char *entry)
+set_debug(const char *entry, const char *conf_file)
{
size_t filelen, proglen;
const char *progname;
}
static void
-set_path(const char *entry)
+set_path(const char *entry, const char *conf_file)
{
const char *name, *path;
struct sudo_conf_paths *cur;
}
static void
-set_plugin(const char *entry)
+set_plugin(const char *entry, const char *conf_file)
{
struct plugin_info *info;
const char *name, *path, *cp, *ep;
info->options = options;
info->prev = info;
/* info->next = NULL; */
- info->lineno = lineno;
+ info->lineno = conf_lineno;
tq_append(&sudo_conf_data.plugins, info);
}
* Reads in /etc/sudo.conf and populates sudo_conf_data.
*/
void
-sudo_conf_read(void)
+sudo_conf_read(const char *conf_file)
{
struct sudo_conf_table *cur;
struct stat sb;
if (prev_locale[0] != 'C' || prev_locale[1] != '\0')
setlocale(LC_ALL, "C");
- switch (sudo_secure_file(_PATH_SUDO_CONF, ROOT_UID, -1, &sb)) {
- case SUDO_PATH_SECURE:
- break;
- case SUDO_PATH_MISSING:
- /* Root should always be able to read sudo.conf. */
- if (errno != ENOENT && geteuid() == ROOT_UID)
- warning(_("unable to stat %s"), _PATH_SUDO_CONF);
- goto done;
- case SUDO_PATH_BAD_TYPE:
- warningx(_("%s is not a regular file"), _PATH_SUDO_CONF);
- goto done;
- case SUDO_PATH_WRONG_OWNER:
- warningx(_("%s is owned by uid %u, should be %u"),
- _PATH_SUDO_CONF, (unsigned int) sb.st_uid, ROOT_UID);
- goto done;
- case SUDO_PATH_WORLD_WRITABLE:
- warningx(_("%s is world writable"), _PATH_SUDO_CONF);
- goto done;
- case SUDO_PATH_GROUP_WRITABLE:
- warningx(_("%s is group writable"), _PATH_SUDO_CONF);
- goto done;
- default:
- /* NOTREACHED */
- goto done;
+ if (conf_file == NULL) {
+ conf_file = _PATH_SUDO_CONF;
+ switch (sudo_secure_file(conf_file, ROOT_UID, -1, &sb)) {
+ case SUDO_PATH_SECURE:
+ break;
+ case SUDO_PATH_MISSING:
+ /* Root should always be able to read sudo.conf. */
+ if (errno != ENOENT && geteuid() == ROOT_UID)
+ warning(_("unable to stat %s"), conf_file);
+ goto done;
+ case SUDO_PATH_BAD_TYPE:
+ warningx(_("%s is not a regular file"), conf_file);
+ goto done;
+ case SUDO_PATH_WRONG_OWNER:
+ warningx(_("%s is owned by uid %u, should be %u"),
+ conf_file, (unsigned int) sb.st_uid, ROOT_UID);
+ goto done;
+ case SUDO_PATH_WORLD_WRITABLE:
+ warningx(_("%s is world writable"), conf_file);
+ goto done;
+ case SUDO_PATH_GROUP_WRITABLE:
+ warningx(_("%s is group writable"), conf_file);
+ goto done;
+ default:
+ /* NOTREACHED */
+ goto done;
+ }
}
- if ((fp = fopen(_PATH_SUDO_CONF, "r")) == NULL) {
+ if ((fp = fopen(conf_file, "r")) == NULL) {
if (errno != ENOENT && geteuid() == ROOT_UID)
- warning(_("unable to open %s"), _PATH_SUDO_CONF);
+ warning(_("unable to open %s"), conf_file);
goto done;
}
- lineno = 0;
- while (sudo_parseln(&line, &linesize, &lineno, fp) != -1) {
+ conf_lineno = 0;
+ while (sudo_parseln(&line, &linesize, &conf_lineno, fp) != -1) {
if (*(cp = line) == '\0')
continue; /* empty line or comment */
cp += cur->namelen;
while (isblank((unsigned char)*cp))
cp++;
- cur->setter(cp);
+ cur->setter(cp, conf_file);
break;
}
}
TQ_DECLARE(plugin_info)
/* Read main sudo.conf file. */
-void sudo_conf_read(void);
+void sudo_conf_read(const char *);
/* Accessor functions. */
const char *sudo_conf_askpass_path(void);
error_callback_register(sudoreplay_cleanup);
/* Read sudo.conf. */
- sudo_conf_read();
+ sudo_conf_read(NULL);
while ((ch = getopt(argc, argv, "d:f:hlm:s:V")) != -1) {
switch(ch) {
textdomain("sudoers");
/* Read sudo.conf. */
- sudo_conf_read();
+ sudo_conf_read(NULL);
dflag = 0;
grfile = pwfile = NULL;
error_callback_register(visudo_cleanup);
/* Read sudo.conf. */
- sudo_conf_read();
+ sudo_conf_read(NULL);
/*
* Arg handling.
errorx(EXIT_FAILURE, _("requires at least one argument"));
/* Read sudo.conf. */
- sudo_conf_read();
+ sudo_conf_read(NULL);
/* If argv[0] ends in -noexec, pass the flag to sudo_execve() */
if ((cp = strrchr(argv[0], '-')) != NULL && cp != argv[0])
fix_fds();
/* Read sudo.conf. */
- sudo_conf_read();
+ sudo_conf_read(NULL);
/* Fill in user_info with user name, uid, cwd, etc. */
memset(&user_details, 0, sizeof(user_details));