diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 12:52:13 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 12:52:13 +0000 |
commit | f8e5c55a036f0e2e2a958e30456270f3f9eba933 (patch) | |
tree | 4a06ff510774a7a3373e492df4e2984d7b0664b1 /plugins/sudoers/defaults.c | |
parent | Initial commit. (diff) | |
download | sudo-f8e5c55a036f0e2e2a958e30456270f3f9eba933.tar.xz sudo-f8e5c55a036f0e2e2a958e30456270f3f9eba933.zip |
Adding upstream version 1.9.5p2.upstream/1.9.5p2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/sudoers/defaults.c')
-rw-r--r-- | plugins/sudoers/defaults.c | 1109 |
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); +} |