If there are multiple parse errors, send them in a single mail message.
This commit is contained in:
@@ -59,6 +59,14 @@
|
||||
# include "strlist.h"
|
||||
#endif
|
||||
|
||||
struct parse_error {
|
||||
STAILQ_ENTRY(parse_error) entries;
|
||||
char *errstr;
|
||||
};
|
||||
STAILQ_HEAD(parse_error_list, parse_error);
|
||||
static struct parse_error_list parse_error_list =
|
||||
STAILQ_HEAD_INITIALIZER(parse_error_list);
|
||||
|
||||
static bool should_mail(int);
|
||||
static bool warned = false;
|
||||
|
||||
@@ -770,15 +778,91 @@ gai_log_warning(int flags, int errnum, const char *fmt, ...)
|
||||
}
|
||||
|
||||
/*
|
||||
* Log and mail a parse error using log_warningx().
|
||||
* Send mail about accumulated parser errors.
|
||||
* Frees the list of parse errors when done.
|
||||
*/
|
||||
bool
|
||||
mail_parse_errors(void)
|
||||
{
|
||||
const int evl_flags = EVLOG_MAIL|EVLOG_MAIL_ONLY|EVLOG_RAW;
|
||||
struct parse_error *pe;
|
||||
struct eventlog evlog;
|
||||
char *cp, *mailbody = NULL;
|
||||
struct timespec now;
|
||||
size_t len, n;
|
||||
bool ret;
|
||||
debug_decl(mail_parse_errors, SUDOERS_DEBUG_LOGGING);
|
||||
|
||||
if (STAILQ_EMPTY(&parse_error_list))
|
||||
debug_return_bool(true);
|
||||
|
||||
if (sudo_gettime_real(&now) == -1) {
|
||||
sudo_warn("%s", U_("unable to get time of day"));
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
sudoers_to_eventlog(&evlog, NewArgv, env_get(), sudo_user.uuid_str);
|
||||
|
||||
len = sizeof(_("problem parsing sudoers"));
|
||||
STAILQ_FOREACH(pe, &parse_error_list, entries) {
|
||||
len += strlen(_(pe->errstr)) + 1;
|
||||
}
|
||||
mailbody = malloc(len);
|
||||
if (mailbody == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
cp = mailbody;
|
||||
|
||||
n = strlcpy(cp, _("problem parsing sudoers"), len);
|
||||
if (n >= len) {
|
||||
sudo_warnx(U_("internal error, %s overflow"), __func__);
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
cp += n;
|
||||
len -= n;
|
||||
|
||||
STAILQ_FOREACH(pe, &parse_error_list, entries) {
|
||||
n = snprintf(cp, len, "\n%s", _(pe->errstr));
|
||||
if (n >= len) {
|
||||
sudo_warnx(U_("internal error, %s overflow"), __func__);
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
cp += n;
|
||||
len -= n;
|
||||
}
|
||||
|
||||
ret = eventlog_alert(&evlog, evl_flags, &now, mailbody, NULL);
|
||||
if (!log_server_alert(&evlog, &now, mailbody, NULL,
|
||||
sudoers_policy.event_alloc)) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
done:
|
||||
free(mailbody);
|
||||
while ((pe = STAILQ_FIRST(&parse_error_list)) != NULL) {
|
||||
STAILQ_REMOVE_HEAD(&parse_error_list, entries);
|
||||
free(pe->errstr);
|
||||
free(pe);
|
||||
}
|
||||
debug_return_bool(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Log a parse error using log_warningx().
|
||||
* Journals the message to be mailed after parsing is complete.
|
||||
* 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;
|
||||
const int flags = SLOG_RAW_MSG|SLOG_NO_STDERR;
|
||||
char *errstr, *tofree = NULL;
|
||||
struct parse_error *pe;
|
||||
bool ret;
|
||||
debug_decl(log_parse_error, SUDOERS_DEBUG_LOGGING);
|
||||
|
||||
@@ -791,10 +875,31 @@ log_parse_error(const char *file, int line, int column, const char *fmt,
|
||||
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);
|
||||
if (line > 0) {
|
||||
ret = log_warningx(flags, N_("%s:%d:%d: %s"), file, line, column,
|
||||
errstr);
|
||||
} else {
|
||||
ret = log_warningx(flags, N_("%s: %s"), file, errstr);
|
||||
}
|
||||
|
||||
/* Journal parse error for later mailing. */
|
||||
pe = malloc(sizeof(*pe));
|
||||
if (pe != NULL) {
|
||||
int len;
|
||||
|
||||
if (line > 0) {
|
||||
len = asprintf(&pe->errstr, _("%s:%d:%d: %s"), file, line, column,
|
||||
errstr);
|
||||
} else {
|
||||
len = asprintf(&pe->errstr, _("%s: %s"), file, errstr);
|
||||
}
|
||||
if (len != -1) {
|
||||
STAILQ_INSERT_TAIL(&parse_error_list, pe, entries);
|
||||
} else {
|
||||
free(pe);
|
||||
}
|
||||
}
|
||||
|
||||
free(tofree);
|
||||
|
||||
debug_return_bool(ret);
|
||||
|
@@ -89,5 +89,6 @@ void sudoers_to_eventlog(struct eventlog *evlog, char * const argv[], char *cons
|
||||
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);
|
||||
bool mail_parse_errors(void);
|
||||
|
||||
#endif /* SUDOERS_LOGGING_H */
|
||||
|
@@ -434,7 +434,7 @@ sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int *cmnd_status,
|
||||
*cmnd_status = info.status;
|
||||
}
|
||||
if (defs != NULL)
|
||||
update_defaults(parse_tree, defs, SETDEF_GENERIC, false);
|
||||
(void)update_defaults(parse_tree, defs, SETDEF_GENERIC, false);
|
||||
if (!apply_cmndspec(cs))
|
||||
SET(validated, VALIDATE_ERROR);
|
||||
else if (match == ALLOW)
|
||||
|
@@ -742,6 +742,13 @@ log_exit_status(int exit_status)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
bool
|
||||
mail_parse_errors(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
bool
|
||||
log_parse_error(const char *file, int line, int column, const char *fmt,
|
||||
|
@@ -108,6 +108,13 @@ set_cmnd_path(const char *runchroot)
|
||||
return NOT_FOUND;
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
bool
|
||||
mail_parse_errors(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
bool
|
||||
log_warningx(int flags, const char *fmt, ...)
|
||||
|
@@ -149,6 +149,12 @@ restore_nproc(void)
|
||||
#endif /* __linux__ */
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-initialize Defaults settings.
|
||||
* We do not send mail for errors when reinitializing, mail would have
|
||||
* already been sent the first time.
|
||||
* TODO: prevent Defaults error logging too
|
||||
*/
|
||||
static bool
|
||||
sudoers_reinit_defaults(void)
|
||||
{
|
||||
@@ -161,21 +167,21 @@ sudoers_reinit_defaults(void)
|
||||
}
|
||||
|
||||
if (!update_defaults(NULL, &initial_defaults,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) {
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false))
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) {
|
||||
/* Missing/invalid defaults is not a fatal error. */
|
||||
if (nss->getdefs(nss) == -1) {
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
|
||||
N_("unable to get defaults from %s"), nss->source);
|
||||
} else {
|
||||
(void)update_defaults(nss->parse_tree, NULL,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false);
|
||||
}
|
||||
/* Not a fatal error. */
|
||||
(void)update_defaults(nss->parse_tree, NULL,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false);
|
||||
}
|
||||
|
||||
debug_return_int(true);
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -223,33 +229,33 @@ sudoers_init(void *info, sudoers_logger_t logger, char * const envp[])
|
||||
if (!set_perms(PERM_ROOT))
|
||||
debug_return_int(-1);
|
||||
|
||||
/* Use the C locale unless another is specified in sudoers. */
|
||||
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
|
||||
sudo_warn_set_locale_func(sudoers_warn_setlocale);
|
||||
|
||||
/* Update defaults set by front-end. */
|
||||
if (!update_defaults(NULL, &initial_defaults,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) {
|
||||
debug_return_int(-1);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open and parse sudoers, set global defaults.
|
||||
* Uses the C locale unless another is specified in sudoers.
|
||||
*/
|
||||
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
|
||||
sudo_warn_set_locale_func(sudoers_warn_setlocale);
|
||||
/* Open and parse sudoers, set global defaults. */
|
||||
init_parser(sudoers_file, false, false);
|
||||
TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) {
|
||||
if (nss->open(nss) == -1 || (nss->parse_tree = nss->parse(nss)) == NULL) {
|
||||
TAILQ_REMOVE(snl, nss, entries);
|
||||
continue;
|
||||
}
|
||||
|
||||
sources++;
|
||||
|
||||
/* Missing/invalid defaults is not a fatal error. */
|
||||
if (nss->getdefs(nss) == -1) {
|
||||
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
|
||||
N_("unable to get defaults from %s"), nss->source);
|
||||
} else {
|
||||
(void)update_defaults(nss->parse_tree, NULL,
|
||||
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false);
|
||||
}
|
||||
/* 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"));
|
||||
@@ -261,6 +267,8 @@ sudoers_init(void *info, sudoers_logger_t logger, char * const envp[])
|
||||
ret = true;
|
||||
|
||||
cleanup:
|
||||
mail_parse_errors();
|
||||
|
||||
if (!restore_perms())
|
||||
ret = -1;
|
||||
|
||||
@@ -806,6 +814,8 @@ bad:
|
||||
ret = false;
|
||||
|
||||
done:
|
||||
mail_parse_errors();
|
||||
|
||||
if (def_group_plugin)
|
||||
group_plugin_unload();
|
||||
init_parser(NULL, false, false);
|
||||
@@ -1046,7 +1056,7 @@ set_cmnd(void)
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(nss, snl, entries) {
|
||||
/* Not a fatal error. */
|
||||
/* Missing/invalid defaults is not a fatal error. */
|
||||
(void)update_defaults(nss->parse_tree, NULL, SETDEF_CMND, false);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user