summaryrefslogtreecommitdiffstats
path: root/plugins/sudoers/defaults.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plugins/sudoers/defaults.c1109
1 files changed, 1109 insertions, 0 deletions
diff --git a/plugins/sudoers/defaults.c b/plugins/sudoers/defaults.c
new file mode 100644
index 0000000..8bc48b7
--- /dev/null
+++ b/plugins/sudoers/defaults.c
@@ -0,0 +1,1109 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1999-2005, 2007-2020
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+/*
+ * This is an open source non-commercial project. Dear PVS-Studio, please check it.
+ * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+ */
+
+#include <config.h>
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <syslog.h>
+
+#include "sudoers.h"
+#include "sudo_eventlog.h"
+#include <gram.h>
+
+static struct early_default early_defaults[] = {
+ { I_IGNORE_UNKNOWN_DEFAULTS },
+#ifdef FQDN
+ { I_FQDN, true },
+#else
+ { I_FQDN },
+#endif
+ { I_MATCH_GROUP_BY_GID },
+ { I_GROUP_PLUGIN },
+ { I_RUNAS_DEFAULT },
+ { I_SUDOERS_LOCALE },
+ { -1 }
+};
+
+/*
+ * Local prototypes.
+ */
+static bool store_int(const char *str, union sudo_defs_val *sd_un);
+static bool store_list(const char *str, union sudo_defs_val *sd_un, int op);
+static bool store_mode(const char *str, union sudo_defs_val *sd_un);
+static int store_str(const char *str, union sudo_defs_val *sd_un);
+static bool store_syslogfac(const char *str, union sudo_defs_val *sd_un);
+static bool store_syslogpri(const char *str, union sudo_defs_val *sd_un);
+static bool store_timeout(const char *str, union sudo_defs_val *sd_un);
+static bool store_tuple(const char *str, union sudo_defs_val *sd_un, struct def_values *tuple_vals);
+static bool store_uint(const char *str, union sudo_defs_val *sd_un);
+static bool store_timespec(const char *str, union sudo_defs_val *sd_un);
+static bool list_op(const char *str, size_t, union sudo_defs_val *sd_un, enum list_ops op);
+static bool valid_path(struct sudo_defs_types *def, const char *val, const char *file, int line, int column, bool quiet);
+
+/*
+ * Table describing compile-time and run-time options.
+ */
+#include <def_data.c>
+
+/*
+ * Print version and configure info.
+ */
+void
+dump_defaults(void)
+{
+ struct sudo_defs_types *cur;
+ struct list_member *item;
+ struct def_values *def;
+ char *desc;
+ debug_decl(dump_defaults, SUDOERS_DEBUG_DEFAULTS);
+
+ for (cur = sudo_defs_table; cur->name; cur++) {
+ if (cur->desc) {
+ desc = _(cur->desc);
+ switch (cur->type & T_MASK) {
+ case T_FLAG:
+ if (cur->sd_un.flag)
+ sudo_printf(SUDO_CONV_INFO_MSG, "%s\n", desc);
+ break;
+ case T_STR:
+ if (cur->sd_un.str) {
+ sudo_printf(SUDO_CONV_INFO_MSG, desc, cur->sd_un.str);
+ sudo_printf(SUDO_CONV_INFO_MSG, "\n");
+ }
+ break;
+ case T_LOGFAC:
+ if (cur->sd_un.ival) {
+ sudo_printf(SUDO_CONV_INFO_MSG, desc,
+ sudo_logfac2str(cur->sd_un.ival));
+ sudo_printf(SUDO_CONV_INFO_MSG, "\n");
+ }
+ break;
+ case T_LOGPRI:
+ if (cur->sd_un.ival) {
+ sudo_printf(SUDO_CONV_INFO_MSG, desc,
+ sudo_logpri2str(cur->sd_un.ival));
+ sudo_printf(SUDO_CONV_INFO_MSG, "\n");
+ }
+ break;
+ case T_INT:
+ sudo_printf(SUDO_CONV_INFO_MSG, desc, cur->sd_un.ival);
+ sudo_printf(SUDO_CONV_INFO_MSG, "\n");
+ break;
+ case T_UINT:
+ sudo_printf(SUDO_CONV_INFO_MSG, desc, cur->sd_un.uival);
+ sudo_printf(SUDO_CONV_INFO_MSG, "\n");
+ break;
+ case T_TIMESPEC: {
+ /* display timespec in minutes as a double */
+ double d = cur->sd_un.tspec.tv_sec +
+ (cur->sd_un.tspec.tv_nsec / 1000000000.0);
+ sudo_printf(SUDO_CONV_INFO_MSG, desc, d / 60.0);
+ sudo_printf(SUDO_CONV_INFO_MSG, "\n");
+ break;
+ }
+ case T_MODE:
+ sudo_printf(SUDO_CONV_INFO_MSG, desc, cur->sd_un.mode);
+ sudo_printf(SUDO_CONV_INFO_MSG, "\n");
+ break;
+ case T_LIST:
+ if (!SLIST_EMPTY(&cur->sd_un.list)) {
+ sudo_printf(SUDO_CONV_INFO_MSG, "%s\n", desc);
+ SLIST_FOREACH(item, &cur->sd_un.list, entries) {
+ sudo_printf(SUDO_CONV_INFO_MSG,
+ "\t%s\n", item->value);
+ }
+ }
+ break;
+ case T_TIMEOUT:
+ if (cur->sd_un.ival) {
+ sudo_printf(SUDO_CONV_INFO_MSG, desc,
+ cur->sd_un.ival);
+ sudo_printf(SUDO_CONV_INFO_MSG, "\n");
+ }
+ break;
+ case T_TUPLE:
+ for (def = cur->values; def->sval; def++) {
+ if (cur->sd_un.tuple == def->nval) {
+ sudo_printf(SUDO_CONV_INFO_MSG, desc, def->sval);
+ break;
+ }
+ }
+ sudo_printf(SUDO_CONV_INFO_MSG, "\n");
+ break;
+ }
+ }
+ }
+ debug_return;
+}
+
+/*
+ * Find the index of the specified Defaults name in sudo_defs_table[]
+ * On success, returns the matching index or -1 on failure.
+ */
+static int
+find_default(const char *name, const char *file, int line, int column, bool quiet)
+{
+ int i;
+ debug_decl(find_default, SUDOERS_DEBUG_DEFAULTS);
+
+ for (i = 0; sudo_defs_table[i].name != NULL; i++) {
+ if (strcmp(name, sudo_defs_table[i].name) == 0)
+ debug_return_int(i);
+ }
+ if (!quiet && !def_ignore_unknown_defaults) {
+ if (line > 0) {
+ sudo_warnx(U_("%s:%d:%d: unknown defaults entry \"%s\""),
+ file, line, column + 1, name);
+ } else {
+ sudo_warnx(U_("%s: unknown defaults entry \"%s\""),
+ file, name);
+ }
+ } else {
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "%s: unknown defaults entry \"%s\"", __func__, name);
+ }
+ debug_return_int(-1);
+}
+
+/*
+ * Parse a defaults entry, storing the parsed entry in sd_un.
+ * Returns true on success or false on failure.
+ */
+static bool
+parse_default_entry(struct sudo_defs_types *def, const char *val, int op,
+ const char *file, int line, int column, bool quiet)
+{
+ int rc;
+ debug_decl(parse_default_entry, SUDOERS_DEBUG_DEFAULTS);
+
+ sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %s:%d:%d: %s=%s op=%d",
+ __func__, file, line, column, def->name, val ? val : "", op);
+
+ /*
+ * If no value specified, the boolean flag must be set for non-flags.
+ * Only flags and tuples support boolean "true".
+ */
+ if (val == NULL) {
+ switch (def->type & T_MASK) {
+ case T_FLAG:
+ break;
+ case T_TUPLE:
+ if (ISSET(def->type, T_BOOL))
+ break;
+ FALLTHROUGH;
+ case T_LOGFAC:
+ if (op == true) {
+ /* Use default syslog facility if none specified. */
+ val = LOGFAC;
+ }
+ break;
+ default:
+ if (!ISSET(def->type, T_BOOL) || op != false) {
+ if (!quiet) {
+ if (line > 0) {
+ sudo_warnx(U_("%s:%d:%d: no value specified for \"%s\""),
+ file, line, column, def->name);
+ } else {
+ sudo_warnx(U_("%s: no value specified for \"%s\""),
+ file, def->name);
+ }
+ }
+ debug_return_bool(false);
+ }
+ }
+ }
+
+ switch (def->type & T_MASK) {
+ case T_LOGFAC:
+ rc = store_syslogfac(val, &def->sd_un);
+ break;
+ case T_LOGPRI:
+ rc = store_syslogpri(val, &def->sd_un);
+ break;
+ case T_STR:
+ if (val != NULL && ISSET(def->type, T_PATH|T_CHPATH)) {
+ if (!valid_path(def, val, file, line, column, quiet)) {
+ rc = -1;
+ break;
+ }
+ }
+ rc = store_str(val, &def->sd_un);
+ break;
+ case T_INT:
+ rc = store_int(val, &def->sd_un);
+ break;
+ case T_UINT:
+ rc = store_uint(val, &def->sd_un);
+ break;
+ case T_MODE:
+ rc = store_mode(val, &def->sd_un);
+ break;
+ case T_FLAG:
+ if (val != NULL) {
+ if (!quiet) {
+ if (line > 0) {
+ sudo_warnx(U_("%s:%d:%d: option \"%s\" does not take a value"),
+ file, line, column, def->name);
+ } else {
+ sudo_warnx(U_("%s: option \"%s\" does not take a value"),
+ file, def->name);
+ }
+ }
+ rc = -1;
+ break;
+ }
+ def->sd_un.flag = op;
+ rc = true;
+ break;
+ case T_LIST:
+ rc = store_list(val, &def->sd_un, op);
+ break;
+ case T_TIMEOUT:
+ rc = store_timeout(val, &def->sd_un);
+ break;
+ case T_TUPLE:
+ rc = store_tuple(val, &def->sd_un, def->values);
+ break;
+ case T_TIMESPEC:
+ rc = store_timespec(val, &def->sd_un);
+ break;
+ default:
+ if (!quiet) {
+ if (line > 0) {
+ sudo_warnx(U_("%s:%d:%d: invalid Defaults type 0x%x for option \"%s\""),
+ file, line, column, def->type, def->name);
+ } else {
+ sudo_warnx(U_("%s: invalid Defaults type 0x%x for option \"%s\""),
+ file, def->type, def->name);
+ }
+ }
+ rc = -1;
+ break;
+ }
+ if (rc == false) {
+ if (!quiet) {
+ if (line > 0) {
+ sudo_warnx(U_("%s:%d:%d: value \"%s\" is invalid for option \"%s\""),
+ file, line, column, val, def->name);
+ } else {
+ sudo_warnx(U_("%s: value \"%s\" is invalid for option \"%s\""),
+ file, val, def->name);
+ }
+ }
+ }
+
+ debug_return_bool(rc == true);
+}
+
+struct early_default *
+is_early_default(const char *name)
+{
+ struct early_default *early;
+ debug_decl(is_early_default, SUDOERS_DEBUG_DEFAULTS);
+
+ for (early = early_defaults; early->idx != -1; early++) {
+ if (strcmp(name, sudo_defs_table[early->idx].name) == 0)
+ debug_return_ptr(early);
+ }
+ debug_return_ptr(NULL);
+}
+
+static bool
+run_callback(struct sudo_defs_types *def)
+{
+ debug_decl(run_callback, SUDOERS_DEBUG_DEFAULTS);
+
+ if (def->callback == NULL)
+ debug_return_bool(true);
+ debug_return_bool(def->callback(&def->sd_un));
+}
+
+/*
+ * Sets/clears an entry in the defaults structure.
+ * Runs the callback if present on success.
+ */
+bool
+set_default(const char *var, const char *val, int op, const char *file,
+ int line, int column, bool quiet)
+{
+ int idx;
+ debug_decl(set_default, SUDOERS_DEBUG_DEFAULTS);
+
+ sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ "%s: setting Defaults %s -> %s", __func__, var, val ? val : "false");
+
+ idx = find_default(var, file, line, column, quiet);
+ if (idx != -1) {
+ /* Set parsed value in sudo_defs_table and run callback (if any). */
+ struct sudo_defs_types *def = &sudo_defs_table[idx];
+ if (parse_default_entry(def, val, op, file, line, column, quiet))
+ debug_return_bool(run_callback(def));
+ }
+ debug_return_bool(false);
+}
+
+/*
+ * Like set_default() but stores the matching default value
+ * and does not run callbacks.
+ */
+bool
+set_early_default(const char *var, const char *val, int op, const char *file,
+ int line, int column, bool quiet, struct early_default *early)
+{
+ int idx;
+ debug_decl(set_early_default, SUDOERS_DEBUG_DEFAULTS);
+
+ idx = find_default(var, file, line, column, quiet);
+ if (idx != -1) {
+ /* Set parsed value in sudo_defs_table but defer callback (if any). */
+ struct sudo_defs_types *def = &sudo_defs_table[idx];
+ if (parse_default_entry(def, val, op, file, line, column, quiet)) {
+ early->run_callback = true;
+ debug_return_bool(true);
+ }
+ }
+ debug_return_bool(false);
+}
+
+/*
+ * Run callbacks for early defaults.
+ */
+bool
+run_early_defaults(void)
+{
+ struct early_default *early;
+ bool ret = true;
+ debug_decl(run_early_defaults, SUDOERS_DEBUG_DEFAULTS);
+
+ for (early = early_defaults; early->idx != -1; early++) {
+ if (early->run_callback) {
+ if (!run_callback(&sudo_defs_table[early->idx]))
+ ret = false;
+ early->run_callback = false;
+ }
+ }
+ debug_return_bool(ret);
+}
+
+static void
+free_defs_val(int type, union sudo_defs_val *sd_un)
+{
+ switch (type & T_MASK) {
+ case T_STR:
+ free(sd_un->str);
+ break;
+ case T_LIST:
+ (void)list_op(NULL, 0, sd_un, freeall);
+ break;
+ }
+ memset(sd_un, 0, sizeof(*sd_un));
+}
+
+/*
+ * Set default options to compiled-in values.
+ * Any of these may be overridden at runtime by a "Defaults" file.
+ */
+bool
+init_defaults(void)
+{
+ static int firsttime = 1;
+ struct sudo_defs_types *def;
+ debug_decl(init_defaults, SUDOERS_DEBUG_DEFAULTS);
+
+ /* Clear any old settings. */
+ if (!firsttime) {
+ for (def = sudo_defs_table; def->name != NULL; def++)
+ free_defs_val(def->type, &def->sd_un);
+ }
+
+ /* First initialize the flags. */
+#ifdef LONG_OTP_PROMPT
+ def_long_otp_prompt = true;
+#endif
+#ifdef IGNORE_DOT_PATH
+ def_ignore_dot = true;
+#endif
+#ifdef ALWAYS_SEND_MAIL
+ def_mail_always = true;
+#endif
+#ifdef SEND_MAIL_WHEN_NO_USER
+ def_mail_no_user = true;
+#endif
+#ifdef SEND_MAIL_WHEN_NO_HOST
+ def_mail_no_host = true;
+#endif
+#ifdef SEND_MAIL_WHEN_NOT_OK
+ def_mail_no_perms = true;
+#endif
+#ifndef NO_LECTURE
+ def_lecture = once;
+#endif
+#ifndef NO_AUTHENTICATION
+ def_authenticate = true;
+#endif
+#ifndef NO_ROOT_SUDO
+ def_root_sudo = true;
+#endif
+#ifdef HOST_IN_LOG
+ def_log_host = true;
+#endif
+#ifdef SHELL_IF_NO_ARGS
+ def_shell_noargs = true;
+#endif
+#ifdef SHELL_SETS_HOME
+ def_set_home = true;
+#endif
+#ifndef DONT_LEAK_PATH_INFO
+ def_path_info = true;
+#endif
+#ifdef USE_INSULTS
+ def_insults = true;
+#endif
+#ifdef FQDN
+ def_fqdn = true;
+#endif
+#ifdef ENV_EDITOR
+ def_env_editor = true;
+#endif
+#ifdef UMASK_OVERRIDE
+ def_umask_override = true;
+#endif
+ def_timestamp_type = TIMESTAMP_TYPE;
+ if ((def_iolog_file = strdup("%{seq}")) == NULL)
+ goto oom;
+ if ((def_iolog_dir = strdup(_PATH_SUDO_IO_LOGDIR)) == NULL)
+ goto oom;
+ if ((def_sudoers_locale = strdup("C")) == NULL)
+ goto oom;
+ def_env_reset = ENV_RESET;
+ def_set_logname = true;
+ def_closefrom = STDERR_FILENO + 1;
+ def_pam_ruser = true;
+#ifdef __sun__
+ def_pam_rhost = true;
+#endif
+ if ((def_pam_service = strdup("sudo")) == NULL)
+ goto oom;
+#ifdef HAVE_PAM_LOGIN
+ if ((def_pam_login_service = strdup("sudo-i")) == NULL)
+ goto oom;
+#else
+ if ((def_pam_login_service = strdup("sudo")) == NULL)
+ goto oom;
+#endif
+#ifdef NO_PAM_SESSION
+ def_pam_session = false;
+#else
+ def_pam_session = true;
+#endif
+#ifdef HAVE_SELINUX
+ def_selinux = true;
+#endif
+#ifdef HAVE_INNETGR
+ def_use_netgroups = true;
+#endif
+ def_netgroup_tuple = false;
+ def_sudoedit_checkdir = true;
+ def_iolog_mode = S_IRUSR|S_IWUSR;
+ def_fdexec = digest_only;
+ def_log_allowed = true;
+ def_log_denied = true;
+ def_log_format = sudo;
+ def_runas_allow_unknown_id = false;
+
+ /* Syslog options need special care since they both strings and ints */
+#if (LOGGING & SLOG_SYSLOG)
+ (void) store_syslogfac(LOGFAC, &sudo_defs_table[I_SYSLOG].sd_un);
+ (void) store_syslogpri(PRI_SUCCESS, &sudo_defs_table[I_SYSLOG_GOODPRI].sd_un);
+ (void) store_syslogpri(PRI_FAILURE, &sudo_defs_table[I_SYSLOG_BADPRI].sd_un);
+#endif
+
+ /* Password flags also have a string and integer component. */
+ (void) store_tuple("any", &sudo_defs_table[I_LISTPW].sd_un, sudo_defs_table[I_LISTPW].values);
+ (void) store_tuple("all", &sudo_defs_table[I_VERIFYPW].sd_un, sudo_defs_table[I_VERIFYPW].values);
+
+ /* Then initialize the int-like things. */
+#ifdef SUDO_UMASK
+ def_umask = SUDO_UMASK;
+#else
+ def_umask = ACCESSPERMS;
+#endif
+ def_loglinelen = MAXLOGFILELEN;
+ def_timestamp_timeout.tv_sec = TIMEOUT * 60;
+ def_passwd_timeout.tv_sec = PASSWORD_TIMEOUT * 60;
+ def_passwd_tries = TRIES_FOR_PASSWORD;
+#ifdef HAVE_ZLIB_H
+ def_compress_io = true;
+#endif
+ def_log_server_timeout = 30;
+ def_log_server_verify = true;
+ def_log_server_keepalive = true;
+ def_ignore_audit_errors = true;
+ def_ignore_iolog_errors = false;
+ def_ignore_logfile_errors = true;
+
+ /* Now do the strings */
+ if ((def_mailto = strdup(MAILTO)) == NULL)
+ goto oom;
+ if ((def_mailsub = strdup(N_(MAILSUBJECT))) == NULL)
+ goto oom;
+ if ((def_badpass_message = strdup(_(INCORRECT_PASSWORD))) == NULL)
+ goto oom;
+ if ((def_lecture_status_dir = strdup(_PATH_SUDO_LECTURE_DIR)) == NULL)
+ goto oom;
+ if ((def_timestampdir = strdup(_PATH_SUDO_TIMEDIR)) == NULL)
+ goto oom;
+ if ((def_passprompt = strdup(_(PASSPROMPT))) == NULL)
+ goto oom;
+ if ((def_runas_default = strdup(RUNAS_DEFAULT)) == NULL)
+ goto oom;
+#ifdef _PATH_SUDO_SENDMAIL
+ if ((def_mailerpath = strdup(_PATH_SUDO_SENDMAIL)) == NULL)
+ goto oom;
+#endif
+ if ((def_mailerflags = strdup("-t")) == NULL)
+ goto oom;
+#if (LOGGING & SLOG_FILE)
+ if ((def_logfile = strdup(_PATH_SUDO_LOGFILE)) == NULL)
+ goto oom;
+#endif
+#ifdef EXEMPTGROUP
+ if ((def_exempt_group = strdup(EXEMPTGROUP)) == NULL)
+ goto oom;
+#endif
+#ifdef SECURE_PATH
+ if ((def_secure_path = strdup(SECURE_PATH)) == NULL)
+ goto oom;
+#endif
+ if ((def_editor = strdup(EDITOR)) == NULL)
+ goto oom;
+ def_set_utmp = true;
+ def_pam_acct_mgmt = true;
+ def_pam_setcred = true;
+ def_syslog_maxlen = MAXSYSLOGLEN;
+ def_case_insensitive_user = true;
+ def_case_insensitive_group = true;
+
+ /* Reset the locale. */
+ if (!firsttime) {
+ if (!sudoers_initlocale(NULL, def_sudoers_locale))
+ goto oom;
+ }
+
+ /* Finally do the lists (currently just environment tables). */
+ if (!init_envtables())
+ goto oom;
+
+ /* Init eventlog config. */
+ init_eventlog_config();
+
+ firsttime = 0;
+
+ debug_return_bool(true);
+oom:
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ debug_return_bool(false);
+}
+
+/*
+ * Check whether a defaults entry matches the specified type.
+ * Returns true if it matches, else false.
+ */
+static bool
+default_type_matches(struct defaults *d, int what)
+{
+ debug_decl(default_type_matches, SUDOERS_DEBUG_DEFAULTS);
+
+ switch (d->type) {
+ case DEFAULTS:
+ if (ISSET(what, SETDEF_GENERIC))
+ debug_return_bool(true);
+ break;
+ case DEFAULTS_USER:
+ if (ISSET(what, SETDEF_USER))
+ debug_return_bool(true);
+ break;
+ case DEFAULTS_RUNAS:
+ if (ISSET(what, SETDEF_RUNAS))
+ debug_return_bool(true);
+ break;
+ case DEFAULTS_HOST:
+ if (ISSET(what, SETDEF_HOST))
+ debug_return_bool(true);
+ break;
+ case DEFAULTS_CMND:
+ if (ISSET(what, SETDEF_CMND))
+ debug_return_bool(true);
+ break;
+ }
+ debug_return_bool(false);
+}
+
+/*
+ * Check whether a defaults entry's binding matches.
+ * Returns true if it matches, else false.
+ */
+static bool
+default_binding_matches(struct sudoers_parse_tree *parse_tree,
+ struct defaults *d, int what)
+{
+ debug_decl(default_binding_matches, SUDOERS_DEBUG_DEFAULTS);
+
+ switch (d->type) {
+ case DEFAULTS:
+ debug_return_bool(true);
+ break;
+ case DEFAULTS_USER:
+ if (userlist_matches(parse_tree, sudo_user.pw, d->binding) == ALLOW)
+ debug_return_bool(true);
+ break;
+ case DEFAULTS_RUNAS:
+ if (runaslist_matches(parse_tree, d->binding, NULL, NULL, NULL) == ALLOW)
+ debug_return_bool(true);
+ break;
+ case DEFAULTS_HOST:
+ if (hostlist_matches(parse_tree, sudo_user.pw, d->binding) == ALLOW)
+ debug_return_bool(true);
+ break;
+ case DEFAULTS_CMND:
+ if (cmndlist_matches(parse_tree, d->binding, NULL, NULL) == ALLOW)
+ debug_return_bool(true);
+ break;
+ }
+ debug_return_bool(false);
+}
+
+/*
+ * Update the global defaults based on the given defaults list.
+ * Pass in an OR'd list of which default types to update.
+ */
+bool
+update_defaults(struct sudoers_parse_tree *parse_tree,
+ struct defaults_list *defs, int what, bool quiet)
+{
+ struct defaults *d;
+ bool ret = true;
+ debug_decl(update_defaults, SUDOERS_DEBUG_DEFAULTS);
+
+ sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
+ "what: 0x%02x", what);
+
+ /* If no defaults list specified, use the global one in the parse tree. */
+ if (defs == NULL)
+ defs = &parse_tree->defaults;
+
+ /*
+ * First apply Defaults values marked as early.
+ */
+ TAILQ_FOREACH(d, defs, entries) {
+ struct early_default *early = is_early_default(d->var);
+ if (early == NULL)
+ continue;
+
+ /* Defaults type and binding must match. */
+ if (!default_type_matches(d, what) ||
+ !default_binding_matches(parse_tree, d, what))
+ continue;
+
+ /* Copy the value to sudo_defs_table and mark as early. */
+ if (!set_early_default(d->var, d->val, d->op, d->file, d->line,
+ d->column, quiet, early))
+ ret = false;
+ }
+ /* Run callbacks for early defaults (if any) */
+ if (!run_early_defaults())
+ ret = false;
+
+ /*
+ * Then set the rest of the defaults.
+ */
+ TAILQ_FOREACH(d, defs, entries) {
+ /* Skip Defaults marked as early, we already did them. */
+ if (is_early_default(d->var))
+ continue;
+
+ /* Defaults type and binding must match. */
+ if (!default_type_matches(d, what) ||
+ !default_binding_matches(parse_tree, d, what))
+ continue;
+
+ /* Copy the value to sudo_defs_table and run callback (if any) */
+ if (!set_default(d->var, d->val, d->op, d->file, d->line, d->column, quiet))
+ ret = false;
+ }
+
+ debug_return_bool(ret);
+}
+
+/*
+ * Check all defaults entries without actually setting them.
+ */
+bool
+check_defaults(struct sudoers_parse_tree *parse_tree, bool quiet)
+{
+ struct defaults *d;
+ bool ret = true;
+ int idx;
+ debug_decl(check_defaults, SUDOERS_DEBUG_DEFAULTS);
+
+ TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
+ idx = find_default(d->var, d->file, d->line, d->column, quiet);
+ if (idx != -1) {
+ struct sudo_defs_types def = sudo_defs_table[idx];
+ memset(&def.sd_un, 0, sizeof(def.sd_un));
+ if (parse_default_entry(&def, d->val, d->op, d->file,
+ d->line, d->column, quiet)) {
+ free_defs_val(def.type, &def.sd_un);
+ continue;
+ }
+ }
+ /* There was an error in the entry, flag it. */
+ d->error = true;
+ ret = false;
+ }
+ debug_return_bool(ret);
+}
+
+static bool
+store_int(const char *str, union sudo_defs_val *sd_un)
+{
+ const char *errstr;
+ int i;
+ debug_decl(store_int, SUDOERS_DEBUG_DEFAULTS);
+
+ if (str == NULL) {
+ sd_un->ival = 0;
+ } else {
+ i = sudo_strtonum(str, INT_MIN, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "%s: %s", str, errstr);
+ debug_return_bool(false);
+ }
+ sd_un->ival = i;
+ }
+ debug_return_bool(true);
+}
+
+static bool
+store_uint(const char *str, union sudo_defs_val *sd_un)
+{
+ const char *errstr;
+ unsigned int u;
+ debug_decl(store_uint, SUDOERS_DEBUG_DEFAULTS);
+
+ if (str == NULL) {
+ sd_un->uival = 0;
+ } else {
+ u = sudo_strtonum(str, 0, UINT_MAX, &errstr);
+ if (errstr != NULL) {
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "%s: %s", str, errstr);
+ debug_return_bool(false);
+ }
+ sd_un->uival = u;
+ }
+ debug_return_bool(true);
+}
+
+static bool
+store_timespec(const char *str, union sudo_defs_val *sd_un)
+{
+ struct timespec ts;
+ char sign = '+';
+ int i;
+ debug_decl(store_timespec, SUDOERS_DEBUG_DEFAULTS);
+
+ sudo_timespecclear(&ts);
+ if (str != NULL) {
+ /* Convert from minutes to timespec. */
+ if (*str == '+' || *str == '-')
+ sign = *str++;
+ while (*str != '\0' && *str != '.') {
+ if (!isdigit((unsigned char)*str))
+ debug_return_bool(false); /* invalid number */
+ if (ts.tv_sec > TIME_T_MAX / 10)
+ debug_return_bool(false); /* overflow */
+ ts.tv_sec *= 10;
+ ts.tv_sec += *str++ - '0';
+ }
+ if (*str++ == '.') {
+ /* Convert optional fractional component to nanosecs. */
+ for (i = 100000000; i > 0; i /= 10) {
+ if (*str == '\0')
+ break;
+ if (!isdigit((unsigned char)*str))
+ debug_return_bool(false); /* invalid number */
+ ts.tv_nsec += i * (*str++ - '0');
+ }
+ }
+ /* Convert from minutes to seconds. */
+ if (ts.tv_sec > TIME_T_MAX / 60)
+ debug_return_bool(false); /* overflow */
+ ts.tv_sec *= 60;
+ ts.tv_nsec *= 60;
+ while (ts.tv_nsec >= 1000000000) {
+ ts.tv_sec++;
+ ts.tv_nsec -= 1000000000;
+ }
+ }
+ if (sign == '-') {
+ sd_un->tspec.tv_sec = -ts.tv_sec;
+ sd_un->tspec.tv_nsec = -ts.tv_nsec;
+ } else {
+ sd_un->tspec.tv_sec = ts.tv_sec;
+ sd_un->tspec.tv_nsec = ts.tv_nsec;
+ }
+ debug_return_bool(true);
+}
+
+static bool
+store_tuple(const char *str, union sudo_defs_val *sd_un,
+ struct def_values *tuple_vals)
+{
+ struct def_values *v;
+ debug_decl(store_tuple, SUDOERS_DEBUG_DEFAULTS);
+
+ /*
+ * Look up tuple value by name to find enum def_tuple value.
+ * For negation to work the first element of enum def_tuple
+ * must be equivalent to boolean false.
+ */
+ if (str == NULL) {
+ sd_un->ival = 0;
+ } else {
+ for (v = tuple_vals; v->sval != NULL; v++) {
+ if (strcmp(v->sval, str) == 0) {
+ sd_un->tuple = v->nval;
+ break;
+ }
+ }
+ if (v->sval == NULL)
+ debug_return_bool(false);
+ }
+ debug_return_bool(true);
+}
+
+static int
+store_str(const char *str, union sudo_defs_val *sd_un)
+{
+ debug_decl(store_str, SUDOERS_DEBUG_DEFAULTS);
+
+ free(sd_un->str);
+ if (str == NULL) {
+ sd_un->str = NULL;
+ } else {
+ if ((sd_un->str = strdup(str)) == NULL) {
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ debug_return_int(-1);
+ }
+ }
+ debug_return_int(true);
+}
+
+static bool
+store_list(const char *str, union sudo_defs_val *sd_un, int op)
+{
+ debug_decl(store_list, SUDOERS_DEBUG_DEFAULTS);
+
+ /* Remove all old members. */
+ if (op == false || op == true)
+ (void)list_op(NULL, 0, sd_un, freeall);
+
+ /* Split str into multiple space-separated words and act on each one. */
+ if (str != NULL) {
+ const char *cp, *ep;
+ const char *end = str + strlen(str);
+ const enum list_ops lop = op == '-' ? delete : add;
+
+ for (cp = sudo_strsplit(str, end, " \t", &ep); cp != NULL;
+ cp = sudo_strsplit(NULL, end, " \t", &ep)) {
+ if (!list_op(cp, ep - cp, sd_un, lop))
+ debug_return_bool(false);
+ }
+ }
+ debug_return_bool(true);
+}
+
+static bool
+store_syslogfac(const char *str, union sudo_defs_val *sd_un)
+{
+ debug_decl(store_syslogfac, SUDOERS_DEBUG_DEFAULTS);
+
+ if (str == NULL) {
+ sd_un->ival = false;
+ debug_return_bool(true);
+ }
+ debug_return_bool(sudo_str2logfac(str, &sd_un->ival));
+}
+
+static bool
+store_syslogpri(const char *str, union sudo_defs_val *sd_un)
+{
+ debug_decl(store_syslogpri, SUDOERS_DEBUG_DEFAULTS);
+
+ if (str == NULL) {
+ sd_un->ival = -1;
+ debug_return_bool(true);
+ }
+ debug_return_bool(sudo_str2logpri(str, &sd_un->ival));
+}
+
+static bool
+store_mode(const char *str, union sudo_defs_val *sd_un)
+{
+ mode_t mode;
+ const char *errstr;
+ debug_decl(store_mode, SUDOERS_DEBUG_DEFAULTS);
+
+ if (str == NULL) {
+ sd_un->mode = ACCESSPERMS;
+ } else {
+ mode = sudo_strtomode(str, &errstr);
+ if (errstr != NULL) {
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
+ "%s is %s", str, errstr);
+ debug_return_bool(false);
+ }
+ sd_un->mode = mode;
+ }
+ debug_return_bool(true);
+}
+
+static bool
+store_timeout(const char *str, union sudo_defs_val *sd_un)
+{
+ debug_decl(store_mode, SUDOERS_DEBUG_DEFAULTS);
+
+ if (str == NULL) {
+ sd_un->ival = 0;
+ } else {
+ int seconds = parse_timeout(str);
+ if (seconds == -1) {
+ sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO,
+ "%s", str);
+ debug_return_bool(false);
+ }
+ sd_un->ival = seconds;
+ }
+ debug_return_bool(true);
+}
+
+static bool
+valid_path(struct sudo_defs_types *def, const char *val,
+ const char *file, int line, int column, bool quiet)
+{
+ bool ret = true;
+ debug_decl(valid_path, SUDOERS_DEBUG_DEFAULTS);
+
+ if (ISSET(def->type, T_CHPATH)) {
+ if (val[0] != '/' && val[0] != '~' && (val[0] != '*' || val[1] != '\0')) {
+ if (!quiet) {
+ if (line > 0) {
+ sudo_warnx(
+ U_("%s:%d:%d: values for \"%s\" must start with a '/', '~', or '*'"),
+ file, line, column, def->name);
+ } else {
+ sudo_warnx(
+ U_("%s: values for \"%s\" must start with a '/', '~', or '*'"),
+ file, def->name);
+ }
+ }
+ ret = false;
+ }
+ } else {
+ if (val[0] != '/') {
+ if (!quiet) {
+ if (line > 0) {
+ sudo_warnx(
+ U_("%s:%d:%d: values for \"%s\" must start with a '/'"),
+ file, line, column, def->name);
+ } else {
+ sudo_warnx(
+ U_("%s: values for \"%s\" must start with a '/'"),
+ file, def->name);
+ }
+ }
+ ret = false;
+ }
+
+ }
+ debug_return_bool(ret);
+}
+
+static bool
+list_op(const char *str, size_t len, union sudo_defs_val *sd_un,
+ enum list_ops op)
+{
+ struct list_member *cur, *prev = NULL;
+ debug_decl(list_op, SUDOERS_DEBUG_DEFAULTS);
+
+ if (op == freeall) {
+ while ((cur = SLIST_FIRST(&sd_un->list)) != NULL) {
+ SLIST_REMOVE_HEAD(&sd_un->list, entries);
+ free(cur->value);
+ free(cur);
+ }
+ debug_return_bool(true);
+ }
+
+ SLIST_FOREACH(cur, &sd_un->list, entries) {
+ if ((strncmp(cur->value, str, len) == 0 && cur->value[len] == '\0')) {
+
+ if (op == add)
+ debug_return_bool(true); /* already exists */
+
+ /* Delete node */
+ if (prev == NULL)
+ SLIST_REMOVE_HEAD(&sd_un->list, entries);
+ else
+ SLIST_REMOVE_AFTER(prev, entries);
+ free(cur->value);
+ free(cur);
+ break;
+ }
+ prev = cur;
+ }
+
+ /* Add new node to the head of the list. */
+ if (op == add) {
+ cur = calloc(1, sizeof(struct list_member));
+ if (cur == NULL || (cur->value = strndup(str, len)) == NULL) {
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ free(cur);
+ debug_return_bool(false);
+ }
+ SLIST_INSERT_HEAD(&sd_un->list, cur, entries);
+ }
+ debug_return_bool(true);
+}