Add a hook for sudoers parse errors (including defaults and aliases).
The hook can be used to log parser errors (sudoers module) or keep track of which files have an error (visudo). Previously, we only kept track of a single parse error.
This commit is contained in:
@@ -181,7 +181,7 @@ sudoers_audit_open(unsigned int version, sudo_conv_t conversation,
|
||||
info.settings = settings;
|
||||
info.user_info = user_info;
|
||||
info.plugin_args = plugin_options;
|
||||
ret = sudoers_init(&info, submit_envp);
|
||||
ret = sudoers_init(&info, log_parse_error, submit_envp);
|
||||
|
||||
if (ret == true) {
|
||||
/* Unset close function if we don't need it to avoid extra process. */
|
||||
|
@@ -38,6 +38,8 @@ struct alias_warned {
|
||||
};
|
||||
SLIST_HEAD(alias_warned_list, alias_warned);
|
||||
|
||||
static bool alias_warnx(const char *file, int line, int column, bool strict, bool quiet, const char *fmt, ...) __printflike(6, 7);
|
||||
|
||||
static bool
|
||||
alias_warned(struct alias_warned_list *warned, char *name)
|
||||
{
|
||||
@@ -67,6 +69,41 @@ alias_warned_add(struct alias_warned_list *warned, char *name)
|
||||
debug_return;
|
||||
}
|
||||
|
||||
static bool
|
||||
alias_warnx(const char *file, int line, int column, bool strict, bool quiet,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
bool ret = true;
|
||||
va_list ap;
|
||||
debug_decl(alias_warnx, SUDOERS_DEBUG_ALIAS);
|
||||
|
||||
if (strict && sudoers_error_hook != NULL)
|
||||
ret = sudoers_error_hook(file, line, column, fmt, ap);
|
||||
|
||||
if (!quiet) {
|
||||
int oldlocale;
|
||||
char *errstr;
|
||||
|
||||
sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale);
|
||||
va_start(ap, fmt);
|
||||
if (vasprintf(&errstr, _(fmt), ap) == -1) {
|
||||
errstr = NULL;
|
||||
ret = false;
|
||||
} else if (line > 0) {
|
||||
sudo_printf(SUDO_CONV_ERROR_MSG, _("%s:%d:%d: %s\n"), file,
|
||||
line, column, errstr);
|
||||
} else {
|
||||
sudo_printf(SUDO_CONV_ERROR_MSG, _("%s: %s\n"), file, errstr);
|
||||
}
|
||||
va_end(ap);
|
||||
sudoers_setlocale(oldlocale, NULL);
|
||||
|
||||
free(errstr);
|
||||
}
|
||||
|
||||
debug_return_bool(ret);
|
||||
}
|
||||
|
||||
static int
|
||||
check_alias(struct sudoers_parse_tree *parse_tree,
|
||||
struct alias_warned_list *warned, char *name, int type,
|
||||
@@ -87,24 +124,14 @@ check_alias(struct sudoers_parse_tree *parse_tree,
|
||||
}
|
||||
alias_put(a);
|
||||
} else {
|
||||
if (!quiet && !alias_warned(warned, name)) {
|
||||
if (!alias_warned(warned, name)) {
|
||||
if (errno == ELOOP) {
|
||||
sudo_printf(SUDO_CONV_ERROR_MSG, strict ?
|
||||
U_("Error: %s:%d:%d: cycle in %s \"%s\"") :
|
||||
U_("Warning: %s:%d:%d: cycle in %s \"%s\""),
|
||||
file, line, column, alias_type_to_string(type), name);
|
||||
alias_warnx(file, line, column, strict, quiet,
|
||||
N_("cycle in %s \"%s\""), alias_type_to_string(type), name);
|
||||
} else {
|
||||
sudo_printf(SUDO_CONV_ERROR_MSG, strict ?
|
||||
U_("Error: %s:%d:%d: %s \"%s\" referenced but not defined") :
|
||||
U_("Warning: %s:%d:%d: %s \"%s\" referenced but not defined"),
|
||||
file, line, column, alias_type_to_string(type), name);
|
||||
}
|
||||
sudo_printf(SUDO_CONV_ERROR_MSG, "\n");
|
||||
if (strict && errorfile == NULL) {
|
||||
errorfile = sudo_rcstr_addref(file);
|
||||
errorlineno = line;
|
||||
errorcolumn = column;
|
||||
/* No need to set errorstring, visudo doesn't use it. */
|
||||
alias_warnx(file, line, column, strict, quiet,
|
||||
N_("%s \"%s\" referenced but not defined"),
|
||||
alias_type_to_string(type), name);
|
||||
}
|
||||
alias_warned_add(warned, name);
|
||||
}
|
||||
@@ -117,6 +144,7 @@ check_alias(struct sudoers_parse_tree *parse_tree,
|
||||
/*
|
||||
* Iterate through the sudoers datastructures looking for undefined
|
||||
* aliases or unused aliases.
|
||||
* In strict mode, returns the number of errors, else 0.
|
||||
*/
|
||||
int
|
||||
check_aliases(struct sudoers_parse_tree *parse_tree, bool strict, bool quiet,
|
||||
|
@@ -753,9 +753,6 @@ parse_sudoers(const char *input_file, struct cvtsudoers_config *conf)
|
||||
if (sudoersparse() && !parse_error) {
|
||||
sudo_warnx(U_("failed to parse %s file, unknown error"), input_file);
|
||||
parse_error = true;
|
||||
sudo_rcstr_delref(errorfile);
|
||||
if ((errorfile = sudo_rcstr_dup(input_file)) == NULL)
|
||||
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
}
|
||||
debug_return_bool(!parse_error);
|
||||
}
|
||||
|
@@ -73,6 +73,7 @@ static bool store_timespec(const char *str, struct sudo_defs_types *def);
|
||||
static bool store_rlimit(const char *str, struct sudo_defs_types *def);
|
||||
static bool list_op(const char *str, size_t, struct list_members *list, 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);
|
||||
static bool defaults_warnx(const char *file, int line, int column, bool quiet, const char *fmt, ...) __printflike(5, 6);
|
||||
|
||||
/*
|
||||
* Table describing compile-time and run-time options.
|
||||
@@ -185,17 +186,9 @@ find_default(const char *name, const char *file, int line, int column, bool quie
|
||||
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);
|
||||
if (!def_ignore_unknown_defaults) {
|
||||
defaults_warnx(file, line, column, quiet,
|
||||
N_("unknown defaults entry \"%s\""), name);
|
||||
}
|
||||
debug_return_int(-1);
|
||||
}
|
||||
@@ -237,15 +230,8 @@ parse_default_entry(struct sudo_defs_types *def, const char *val, int op,
|
||||
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);
|
||||
}
|
||||
}
|
||||
defaults_warnx(file, line, column, quiet,
|
||||
N_("no value specified for \"%s\""), def->name);
|
||||
debug_return_bool(false);
|
||||
}
|
||||
}
|
||||
@@ -253,15 +239,8 @@ parse_default_entry(struct sudo_defs_types *def, const char *val, int op,
|
||||
|
||||
/* Only lists support append/remove. */
|
||||
if ((op == '+' || op == '-') && (def->type & T_MASK) != T_LIST) {
|
||||
if (!quiet) {
|
||||
if (line > 0) {
|
||||
sudo_warnx(U_("%s:%d:%d: invalid operator \"%c=\" for \"%s\""),
|
||||
file, line, column, op, def->name);
|
||||
} else {
|
||||
sudo_warnx(U_("%s: invalid operator \"%c=\" for \"%s\""),
|
||||
file, op, def->name);
|
||||
}
|
||||
}
|
||||
defaults_warnx(file, line, column, quiet,
|
||||
N_("invalid operator \"%c=\" for \"%s\""), op, def->name);
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
@@ -292,15 +271,8 @@ parse_default_entry(struct sudo_defs_types *def, const char *val, int op,
|
||||
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);
|
||||
}
|
||||
}
|
||||
defaults_warnx(file, line, column, quiet,
|
||||
N_("option \"%s\" does not take a value"), def->name);
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
@@ -323,28 +295,15 @@ parse_default_entry(struct sudo_defs_types *def, const char *val, int op,
|
||||
rc = store_rlimit(val, def);
|
||||
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);
|
||||
}
|
||||
}
|
||||
defaults_warnx(file, line, column, quiet,
|
||||
N_("invalid Defaults type 0x%x for option \"%s\""),
|
||||
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);
|
||||
}
|
||||
}
|
||||
defaults_warnx(file, line, column, quiet,
|
||||
N_("value \"%s\" is invalid for option \"%s\""), val, def->name);
|
||||
}
|
||||
|
||||
debug_return_bool(rc == true);
|
||||
@@ -1156,45 +1115,21 @@ valid_path(struct sudo_defs_types *def, const char *val,
|
||||
debug_decl(valid_path, SUDOERS_DEBUG_DEFAULTS);
|
||||
|
||||
if (strlen(val) >= PATH_MAX) {
|
||||
if (!quiet) {
|
||||
if (line > 0) {
|
||||
sudo_warnx(U_("%s:%d:%d: path name for \"%s\" too long"),
|
||||
file, line, column, def->name);
|
||||
} else {
|
||||
sudo_warnx(U_("%s: path name for \"%s\" too long"),
|
||||
file, def->name);
|
||||
}
|
||||
}
|
||||
defaults_warnx(file, line, column, quiet,
|
||||
N_("path name for \"%s\" too long"), def->name);
|
||||
ret = false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
defaults_warnx(file, line, column, quiet,
|
||||
N_("values for \"%s\" must start with a '/', '~', or '*'"),
|
||||
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);
|
||||
}
|
||||
}
|
||||
defaults_warnx(file, line, column, quiet,
|
||||
N_("values for \"%s\" must start with a '/'"), def->name);
|
||||
ret = false;
|
||||
}
|
||||
|
||||
@@ -1293,7 +1228,9 @@ cb_passprompt_regex(const union sudo_defs_val *sd_un, int op)
|
||||
if (op == '+' || op == true) {
|
||||
SLIST_FOREACH(lm, &sd_un->list, entries) {
|
||||
if (!sudo_regex_compile(NULL, lm->value, &errstr)) {
|
||||
sudo_warnx(U_("invalid regular expression \"%s\": %s"),
|
||||
/* XXX - need to pass file/file/col/quiet to callbacks */
|
||||
defaults_warnx(sudoers, -1, -1, false,
|
||||
U_("invalid regular expression \"%s\": %s"),
|
||||
lm->value, U_(errstr));
|
||||
debug_return_bool(false);
|
||||
}
|
||||
@@ -1302,3 +1239,41 @@ cb_passprompt_regex(const union sudo_defs_val *sd_un, int op)
|
||||
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
||||
static bool
|
||||
defaults_warnx(const char *file, int line, int column, bool quiet,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
bool ret = true;
|
||||
va_list ap;
|
||||
debug_decl(defaults_warnx, SUDOERS_DEBUG_DEFAULTS);
|
||||
|
||||
if (sudoers_error_hook != NULL) {
|
||||
va_start(ap, fmt);
|
||||
ret = sudoers_error_hook(file, line, column, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
if (!quiet) {
|
||||
int oldlocale;
|
||||
char *errstr;
|
||||
|
||||
sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale);
|
||||
va_start(ap, fmt);
|
||||
if (vasprintf(&errstr, _(fmt), ap) == -1) {
|
||||
errstr = NULL;
|
||||
ret = false;
|
||||
} else if (line > 0) {
|
||||
sudo_printf(SUDO_CONV_ERROR_MSG, _("%s:%d:%d: %s\n"), file,
|
||||
line, column, errstr);
|
||||
} else {
|
||||
sudo_printf(SUDO_CONV_ERROR_MSG, _("%s: %s\n"), file, errstr);
|
||||
}
|
||||
va_end(ap);
|
||||
sudoers_setlocale(oldlocale, NULL);
|
||||
|
||||
free(errstr);
|
||||
}
|
||||
|
||||
debug_return_bool(ret);
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2004-2005, 2007-2018 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
* Copyright (c) 2004-2005, 2007-2022 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
|
||||
@@ -102,20 +102,10 @@ sudo_file_parse(struct sudo_nss *nss)
|
||||
|
||||
sudoersin = handle->fp;
|
||||
error = sudoersparse();
|
||||
if (error || parse_error) {
|
||||
if (errorlineno != -1) {
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, N_("%s:%d:%d: %s"),
|
||||
errorfile, errorlineno, errorcolumn,
|
||||
errorstring ? errorstring : N_("syntax error"));
|
||||
} else {
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, N_("%s: %s"),
|
||||
errorfile, errorstring ? errorstring : N_("syntax error"));
|
||||
}
|
||||
if (error || !sudoers_recovery) {
|
||||
if (error || (parse_error && !sudoers_recovery)) {
|
||||
/* unrecoverable error */
|
||||
debug_return_ptr(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Move parsed sudoers policy to nss handle. */
|
||||
reparent_parse_tree(&handle->parse_tree);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -171,7 +171,7 @@ extern int sudoersdebug;
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
union YYSTYPE
|
||||
{
|
||||
#line 88 "gram.y"
|
||||
#line 87 "gram.y"
|
||||
|
||||
struct cmndspec *cmndspec;
|
||||
struct defaults *defaults;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2021
|
||||
* Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2022
|
||||
* Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
@@ -51,10 +51,9 @@
|
||||
bool sudoers_warnings = true;
|
||||
bool sudoers_strict = false;
|
||||
bool parse_error = false;
|
||||
int errorlineno = -1;
|
||||
int errorcolumn = -1;
|
||||
char *errorfile = NULL;
|
||||
char *errorstring = NULL;
|
||||
|
||||
/* Optional logging function for parse errors. */
|
||||
sudoers_logger_t sudoers_error_hook;
|
||||
|
||||
static int alias_line, alias_column;
|
||||
|
||||
@@ -1170,27 +1169,15 @@ group : ALIAS {
|
||||
void
|
||||
sudoerserrorf(const char *fmt, ...)
|
||||
{
|
||||
const int column = sudolinebuf.toke_start + 1;
|
||||
va_list ap;
|
||||
debug_decl(sudoerserrorf, SUDOERS_DEBUG_PARSER);
|
||||
|
||||
/* Save the line the first error occurred on. */
|
||||
if (errorlineno == -1) {
|
||||
sudo_rcstr_delref(errorfile);
|
||||
errorfile = sudo_rcstr_addref(sudoers);
|
||||
errorlineno = this_lineno;
|
||||
errorcolumn = sudolinebuf.toke_start + 1;
|
||||
if (fmt != NULL) {
|
||||
if (sudoers_error_hook != NULL) {
|
||||
va_start(ap, fmt);
|
||||
if (strcmp(fmt, "%s") == 0) {
|
||||
/* Optimize common case, a single string. */
|
||||
errorstring = strdup(_(va_arg(ap, char *)));
|
||||
} else {
|
||||
if (vasprintf(&errorstring, fmt, ap) == -1)
|
||||
errorstring = NULL;
|
||||
}
|
||||
sudoers_error_hook(sudoers, this_lineno, column, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
if (sudoers_warnings && fmt != NULL) {
|
||||
LEXTRACE("<*> ");
|
||||
#ifndef TRACELEXER
|
||||
@@ -1206,7 +1193,7 @@ sudoerserrorf(const char *fmt, ...)
|
||||
/* Optimize common case, a single string. */
|
||||
s = _(va_arg(ap, char *));
|
||||
} else {
|
||||
if (vasprintf(&s, fmt, ap) != -1)
|
||||
if (vasprintf(&s, _(fmt), ap) != -1)
|
||||
tofree = s;
|
||||
else
|
||||
s = _("syntax error");
|
||||
@@ -1767,12 +1754,6 @@ init_parser(const char *path, bool quiet, bool strict)
|
||||
}
|
||||
|
||||
parse_error = false;
|
||||
errorlineno = -1;
|
||||
errorcolumn = -1;
|
||||
free(errorstring);
|
||||
errorstring = NULL;
|
||||
sudo_rcstr_delref(errorfile);
|
||||
errorfile = NULL;
|
||||
sudoers_warnings = !quiet;
|
||||
sudoers_strict = strict;
|
||||
|
||||
|
@@ -769,6 +769,37 @@ gai_log_warning(int flags, int errnum, const char *fmt, ...)
|
||||
debug_return_bool(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Log and mail a parse error using log_warningx().
|
||||
* Does not write the message to stderr.
|
||||
*/
|
||||
bool
|
||||
log_parse_error(const char *file, int line, int column, const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
const int flags = SLOG_SEND_MAIL|SLOG_NO_STDERR;
|
||||
char *errstr, *tofree = NULL;
|
||||
bool ret;
|
||||
debug_decl(log_parse_error, SUDOERS_DEBUG_LOGGING);
|
||||
|
||||
if (strcmp(fmt, "%s") == 0) {
|
||||
/* Optimize common case, a single string. */
|
||||
errstr = _(va_arg(args, char *));
|
||||
} else {
|
||||
if (vasprintf(&errstr, _(fmt), args) == -1)
|
||||
debug_return_bool(false);
|
||||
tofree = errstr;
|
||||
}
|
||||
|
||||
if (line > 0)
|
||||
ret = log_warningx(flags, _("%s:%d:%d: %s"), file, line, column, errstr);
|
||||
else
|
||||
ret = log_warningx(flags, _("%s: %s"), file, errstr);
|
||||
free(tofree);
|
||||
|
||||
debug_return_bool(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine whether we should send mail based on "status" and defaults options.
|
||||
*/
|
||||
|
@@ -57,6 +57,8 @@ struct log_details {
|
||||
#define SLOG_NO_LOG 0x20 /* do not log via file or syslog */
|
||||
#define SLOG_AUDIT 0x40 /* send message to audit as well */
|
||||
|
||||
typedef bool (*sudoers_logger_t)(const char *file, int line, int column, const char *fmt, va_list args);
|
||||
|
||||
/* XXX - needed for auditing */
|
||||
extern int NewArgc;
|
||||
extern char **NewArgv;
|
||||
@@ -86,5 +88,6 @@ bool sudoers_locale_callback(const union sudo_defs_val *sd_un, int op);
|
||||
void sudoers_to_eventlog(struct eventlog *evlog, char * const argv[], char *const envp[], const char *uuid_str);
|
||||
void init_eventlog_config(void);
|
||||
bool init_log_details(struct log_details *details, struct eventlog *evlog);
|
||||
bool log_parse_error(const char *file, int line, int column, const char *fmt, va_list ap) __printflike(4, 0);
|
||||
|
||||
#endif /* SUDOERS_LOGGING_H */
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 1996, 1998-2000, 2004, 2007-2021
|
||||
* Copyright (c) 1996, 1998-2000, 2004, 2007-2022
|
||||
* Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
@@ -361,8 +361,9 @@ void alias_put(struct alias *a);
|
||||
/* check_aliases.c */
|
||||
int check_aliases(struct sudoers_parse_tree *parse_tree, bool strict, bool quiet, int (*cb_unused)(struct sudoers_parse_tree *, struct alias *, void *));
|
||||
|
||||
/* gram.c */
|
||||
/* gram.y */
|
||||
extern struct sudoers_parse_tree parsed_policy;
|
||||
extern bool (*sudoers_error_hook)(const char *file, int line, int column, const char *fmt, va_list args);
|
||||
bool init_parser(const char *path, bool quiet, bool strict);
|
||||
void free_member(struct member *m);
|
||||
void free_members(struct member_list *members);
|
||||
|
@@ -1000,7 +1000,7 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
|
||||
info.settings = settings;
|
||||
info.user_info = user_info;
|
||||
info.plugin_args = args;
|
||||
ret = sudoers_init(&info, envp);
|
||||
ret = sudoers_init(&info, log_parse_error, envp);
|
||||
|
||||
/* The audit functions set audit_msg on failure. */
|
||||
if (ret != 1 && audit_msg != NULL) {
|
||||
|
@@ -742,6 +742,14 @@ log_exit_status(int exit_status)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
bool
|
||||
log_parse_error(const char *file, int line, int column, const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
int
|
||||
audit_failure(char *const argv[], char const *const fmt, ...)
|
||||
|
@@ -6,5 +6,5 @@ WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND
|
||||
WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND
|
||||
WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND
|
||||
WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND
|
||||
testsudoers: sudoers:2:26: value "2d8h10m59ss" is invalid for option "command_timeout"
|
||||
testsudoers: sudoers:3:31: value "15f" is invalid for option "command_timeout"
|
||||
sudoers:2:26: value "2d8h10m59ss" is invalid for option "command_timeout"
|
||||
sudoers:3:31: value "15f" is invalid for option "command_timeout"
|
||||
|
@@ -1 +1 @@
|
||||
Error: stdin:1:12: cycle in User_Alias "FOO"
|
||||
stdin:1:12: cycle in User_Alias "FOO"
|
||||
|
@@ -162,8 +162,6 @@ sudoers_reinit_defaults(void)
|
||||
|
||||
if (!update_defaults(NULL, &initial_defaults,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) {
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
|
||||
N_("problem with defaults entries"));
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
@@ -172,19 +170,16 @@ sudoers_reinit_defaults(void)
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
|
||||
N_("unable to get defaults from %s"), nss->source);
|
||||
}
|
||||
if (!update_defaults(nss->parse_tree, NULL,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) {
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
|
||||
N_("problem with defaults entries"));
|
||||
/* not a fatal error */
|
||||
}
|
||||
/* Not a fatal error. */
|
||||
(void)update_defaults(nss->parse_tree, NULL,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false);
|
||||
}
|
||||
|
||||
debug_return_int(true);
|
||||
}
|
||||
|
||||
int
|
||||
sudoers_init(void *info, char * const envp[])
|
||||
sudoers_init(void *info, sudoers_logger_t logger, char * const envp[])
|
||||
{
|
||||
struct sudo_nss *nss, *nss_next;
|
||||
int oldlocale, sources = 0;
|
||||
@@ -197,6 +192,9 @@ sudoers_init(void *info, char * const envp[])
|
||||
|
||||
bindtextdomain("sudoers", LOCALEDIR);
|
||||
|
||||
/* Hook up logging function for parse errors. */
|
||||
sudoers_error_hook = logger;
|
||||
|
||||
/* Register fatal/fatalx callback. */
|
||||
sudo_fatal_callback_register(sudoers_cleanup);
|
||||
|
||||
@@ -228,8 +226,6 @@ sudoers_init(void *info, char * const envp[])
|
||||
/* Update defaults set by front-end. */
|
||||
if (!update_defaults(NULL, &initial_defaults,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) {
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
|
||||
N_("problem with defaults entries"));
|
||||
debug_return_int(-1);
|
||||
}
|
||||
|
||||
@@ -251,11 +247,9 @@ sudoers_init(void *info, char * const envp[])
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
|
||||
N_("unable to get defaults from %s"), nss->source);
|
||||
}
|
||||
if (!update_defaults(nss->parse_tree, NULL,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) {
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
|
||||
N_("problem with defaults entries"));
|
||||
}
|
||||
/* Not a fatal error. */
|
||||
(void)update_defaults(nss->parse_tree, NULL,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false);
|
||||
}
|
||||
if (sources == 0) {
|
||||
sudo_warnx("%s", U_("no valid sudoers sources found, quitting"));
|
||||
@@ -1052,10 +1046,8 @@ set_cmnd(void)
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(nss, snl, entries) {
|
||||
if (!update_defaults(nss->parse_tree, NULL, SETDEF_CMND, false)) {
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
|
||||
N_("problem with defaults entries"));
|
||||
}
|
||||
/* Not a fatal error. */
|
||||
(void)update_defaults(nss->parse_tree, NULL, SETDEF_CMND, false);
|
||||
}
|
||||
|
||||
debug_return_int(ret);
|
||||
|
@@ -310,10 +310,6 @@ int pam_prep_user(struct passwd *);
|
||||
/* gram.y */
|
||||
int sudoersparse(void);
|
||||
extern char *login_style;
|
||||
extern char *errorfile;
|
||||
extern int errorlineno;
|
||||
extern int errorcolumn;
|
||||
extern char *errorstring;
|
||||
extern bool parse_error;
|
||||
extern bool sudoers_warnings;
|
||||
extern bool sudoers_recovery;
|
||||
@@ -413,7 +409,7 @@ bool matches_env_pattern(const char *pattern, const char *var, bool *full_match)
|
||||
/* sudoers.c */
|
||||
FILE *open_sudoers(const char *, bool, bool *);
|
||||
int set_cmnd_path(const char *runchroot);
|
||||
int sudoers_init(void *info, char * const envp[]);
|
||||
int sudoers_init(void *info, sudoers_logger_t logger, char * const envp[]);
|
||||
int sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], bool verbose, void *closure);
|
||||
void sudoers_cleanup(void);
|
||||
void sudo_user_free(void);
|
||||
|
@@ -77,6 +77,7 @@ struct sudoersfile {
|
||||
bool modified;
|
||||
bool doedit;
|
||||
int fd;
|
||||
int errorline;
|
||||
};
|
||||
TAILQ_HEAD(sudoersfile_list, sudoersfile);
|
||||
|
||||
@@ -89,6 +90,7 @@ static char *get_editor(int *editor_argc, char ***editor_argv);
|
||||
static bool check_syntax(const char *, bool, bool, bool, bool);
|
||||
static bool edit_sudoers(struct sudoersfile *, char *, int, char **, int);
|
||||
static bool install_sudoers(struct sudoersfile *, bool, bool);
|
||||
static bool visudo_track_error(const char *file, int line, int column, const char *fmt, va_list args);
|
||||
static int print_unused(struct sudoers_parse_tree *, struct alias *, void *);
|
||||
static bool reparse_sudoers(char *, int, char **, bool, bool);
|
||||
static int run_command(char *, char **);
|
||||
@@ -107,6 +109,7 @@ struct sudo_user sudo_user;
|
||||
struct passwd *list_pw;
|
||||
static struct sudoersfile_list sudoerslist = TAILQ_HEAD_INITIALIZER(sudoerslist);
|
||||
static bool checkonly;
|
||||
static unsigned int errors;
|
||||
static const char short_opts[] = "cf:hOPqsVx:";
|
||||
static struct option long_opts[] = {
|
||||
{ "check", no_argument, NULL, 'c' },
|
||||
@@ -256,6 +259,9 @@ main(int argc, char *argv[])
|
||||
}
|
||||
get_hostname();
|
||||
|
||||
/* Hook the sudoers parser to track files with parse errors. */
|
||||
sudoers_error_hook = visudo_track_error;
|
||||
|
||||
/* Setup defaults data structures. */
|
||||
if (!init_defaults())
|
||||
sudo_fatalx("%s", U_("unable to initialize sudoers default values"));
|
||||
@@ -320,6 +326,28 @@ done:
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
static bool
|
||||
visudo_track_error(const char *file, int line, int column, const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
struct sudoersfile *sp;
|
||||
debug_decl(visudo_track_error, SUDOERS_DEBUG_UTIL);
|
||||
|
||||
TAILQ_FOREACH(sp, &sudoerslist, entries) {
|
||||
if (sp->errorline > 0)
|
||||
continue; /* preserve the first error */
|
||||
|
||||
if (strcmp(file, sp->path) == 0 ||
|
||||
(sp->tpath != NULL && strcmp(file, sp->tpath) == 0)) {
|
||||
sp->errorline = line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
errors++;
|
||||
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_editor(int *editor_argc, char ***editor_argv)
|
||||
{
|
||||
@@ -564,7 +592,7 @@ done:
|
||||
|
||||
/*
|
||||
* Check Defaults and Alias entries.
|
||||
* Sets parse_error on error and error{file,lineno,column} if possible.
|
||||
* On error, visudo_track_error() will set the line number in sudoerslist.
|
||||
*/
|
||||
static void
|
||||
check_defaults_and_aliases(bool strict, bool quiet)
|
||||
@@ -572,21 +600,6 @@ check_defaults_and_aliases(bool strict, bool quiet)
|
||||
debug_decl(check_defaults_and_aliases, SUDOERS_DEBUG_UTIL);
|
||||
|
||||
if (!check_defaults(&parsed_policy, quiet)) {
|
||||
struct defaults *d;
|
||||
sudo_rcstr_delref(errorfile);
|
||||
errorfile = NULL;
|
||||
errorlineno = -1;
|
||||
errorcolumn = -1;
|
||||
/* XXX - should edit all files with errors */
|
||||
TAILQ_FOREACH(d, &parsed_policy.defaults, entries) {
|
||||
if (d->error && errorlineno == -1) {
|
||||
/* Defaults parse error, set error{file,lineno,column}. */
|
||||
errorfile = sudo_rcstr_addref(d->file);
|
||||
errorlineno = d->line;
|
||||
errorcolumn = d->column;
|
||||
break;
|
||||
}
|
||||
}
|
||||
parse_error = true;
|
||||
}
|
||||
if (check_aliases(&parsed_policy, strict, quiet, print_unused) != 0) {
|
||||
@@ -610,6 +623,7 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv,
|
||||
/*
|
||||
* Parse the edited sudoers files.
|
||||
*/
|
||||
errors = 0;
|
||||
while ((sp = TAILQ_FIRST(&sudoerslist)) != NULL) {
|
||||
last = TAILQ_LAST(&sudoerslist, sudoersfile_list);
|
||||
fp = fopen(sp->tpath, "r+");
|
||||
@@ -621,6 +635,7 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv,
|
||||
if (!init_defaults())
|
||||
sudo_fatalx("%s", U_("unable to initialize sudoers default values"));
|
||||
init_parser(sp->path, quiet, true);
|
||||
sp->errorline = -1;
|
||||
|
||||
/* Parse the sudoers temp file(s) */
|
||||
sudoersrestart(fp);
|
||||
@@ -629,9 +644,6 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv,
|
||||
sudo_warnx(U_("unable to parse temporary file (%s), unknown error"),
|
||||
sp->tpath);
|
||||
parse_error = true;
|
||||
sudo_rcstr_delref(errorfile);
|
||||
if ((errorfile = sudo_rcstr_dup(sp->path)) == NULL)
|
||||
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
}
|
||||
fclose(sudoersin);
|
||||
if (!parse_error) {
|
||||
@@ -656,17 +668,11 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv,
|
||||
default:
|
||||
/* Edit file with the parse error */
|
||||
TAILQ_FOREACH(sp, &sudoerslist, entries) {
|
||||
if (errorfile == NULL || strcmp(sp->path, errorfile) == 0) {
|
||||
if (errors == 0 || sp->errorline > 0) {
|
||||
edit_sudoers(sp, editor, editor_argc, editor_argv,
|
||||
errorlineno);
|
||||
if (errorfile != NULL)
|
||||
break;
|
||||
sp->errorline);
|
||||
}
|
||||
}
|
||||
if (errorfile != NULL && sp == NULL) {
|
||||
sudo_fatalx(U_("internal error, unable to find %s in list!"),
|
||||
sudoers);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -959,9 +965,6 @@ check_syntax(const char *file, bool quiet, bool strict, bool check_owner,
|
||||
if (!quiet)
|
||||
sudo_warnx(U_("failed to parse %s file, unknown error"), file);
|
||||
parse_error = true;
|
||||
sudo_rcstr_delref(errorfile);
|
||||
if ((errorfile = sudo_rcstr_dup(file)) == NULL)
|
||||
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
}
|
||||
if (!parse_error) {
|
||||
(void) update_defaults(&parsed_policy, NULL,
|
||||
@@ -1186,13 +1189,9 @@ visudo_cleanup(void)
|
||||
static void
|
||||
quit(int signo)
|
||||
{
|
||||
struct sudoersfile *sp;
|
||||
struct iovec iov[4];
|
||||
|
||||
TAILQ_FOREACH(sp, &sudoerslist, entries) {
|
||||
if (sp->tpath != NULL)
|
||||
(void) unlink(sp->tpath);
|
||||
}
|
||||
visudo_cleanup();
|
||||
|
||||
#define emsg " exiting due to signal: "
|
||||
iov[0].iov_base = (char *)getprogname();
|
||||
|
Reference in New Issue
Block a user