If there are multiple parse errors, send them in a single mail message.

This commit is contained in:
Todd C. Miller
2022-03-14 13:54:11 -06:00
parent 1f64aca229
commit e5a50ae429
6 changed files with 155 additions and 25 deletions

View File

@@ -59,6 +59,14 @@
# include "strlist.h" # include "strlist.h"
#endif #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 should_mail(int);
static bool warned = false; 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. * Does not write the message to stderr.
*/ */
bool bool
log_parse_error(const char *file, int line, int column, const char *fmt, log_parse_error(const char *file, int line, int column, const char *fmt,
va_list args) 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; char *errstr, *tofree = NULL;
struct parse_error *pe;
bool ret; bool ret;
debug_decl(log_parse_error, SUDOERS_DEBUG_LOGGING); 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; tofree = errstr;
} }
if (line > 0) if (line > 0) {
ret = log_warningx(flags, _("%s:%d:%d: %s"), file, line, column, errstr); ret = log_warningx(flags, N_("%s:%d:%d: %s"), file, line, column,
else errstr);
ret = log_warningx(flags, _("%s: %s"), file, 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); free(tofree);
debug_return_bool(ret); debug_return_bool(ret);

View File

@@ -89,5 +89,6 @@ void sudoers_to_eventlog(struct eventlog *evlog, char * const argv[], char *cons
void init_eventlog_config(void); void init_eventlog_config(void);
bool init_log_details(struct log_details *details, struct eventlog *evlog); 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 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 */ #endif /* SUDOERS_LOGGING_H */

View File

@@ -434,7 +434,7 @@ sudoers_lookup(struct sudo_nss_list *snl, struct passwd *pw, int *cmnd_status,
*cmnd_status = info.status; *cmnd_status = info.status;
} }
if (defs != NULL) if (defs != NULL)
update_defaults(parse_tree, defs, SETDEF_GENERIC, false); (void)update_defaults(parse_tree, defs, SETDEF_GENERIC, false);
if (!apply_cmndspec(cs)) if (!apply_cmndspec(cs))
SET(validated, VALIDATE_ERROR); SET(validated, VALIDATE_ERROR);
else if (match == ALLOW) else if (match == ALLOW)

View File

@@ -742,6 +742,13 @@ log_exit_status(int exit_status)
return true; return true;
} }
/* STUB */
bool
mail_parse_errors(void)
{
return true;
}
/* STUB */ /* STUB */
bool bool
log_parse_error(const char *file, int line, int column, const char *fmt, log_parse_error(const char *file, int line, int column, const char *fmt,

View File

@@ -108,6 +108,13 @@ set_cmnd_path(const char *runchroot)
return NOT_FOUND; return NOT_FOUND;
} }
/* STUB */
bool
mail_parse_errors(void)
{
return true;
}
/* STUB */ /* STUB */
bool bool
log_warningx(int flags, const char *fmt, ...) log_warningx(int flags, const char *fmt, ...)

View File

@@ -149,6 +149,12 @@ restore_nproc(void)
#endif /* __linux__ */ #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 static bool
sudoers_reinit_defaults(void) sudoers_reinit_defaults(void)
{ {
@@ -161,21 +167,21 @@ sudoers_reinit_defaults(void)
} }
if (!update_defaults(NULL, &initial_defaults, 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); debug_return_bool(false);
}
TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) { TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) {
/* Missing/invalid defaults is not a fatal error. */
if (nss->getdefs(nss) == -1) { if (nss->getdefs(nss) == -1) {
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
N_("unable to get defaults from %s"), nss->source); N_("unable to get defaults from %s"), nss->source);
} } else {
/* Not a fatal error. */
(void)update_defaults(nss->parse_tree, NULL, (void)update_defaults(nss->parse_tree, NULL,
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false); SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false);
} }
}
debug_return_int(true); debug_return_bool(true);
} }
int int
@@ -223,34 +229,34 @@ sudoers_init(void *info, sudoers_logger_t logger, char * const envp[])
if (!set_perms(PERM_ROOT)) if (!set_perms(PERM_ROOT))
debug_return_int(-1); 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. */ /* Update defaults set by front-end. */
if (!update_defaults(NULL, &initial_defaults, 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_int(-1); goto cleanup;
} }
/* /* Open and parse sudoers, set global defaults. */
* 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);
init_parser(sudoers_file, false, false); init_parser(sudoers_file, false, false);
TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) { TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) {
if (nss->open(nss) == -1 || (nss->parse_tree = nss->parse(nss)) == NULL) { if (nss->open(nss) == -1 || (nss->parse_tree = nss->parse(nss)) == NULL) {
TAILQ_REMOVE(snl, nss, entries); TAILQ_REMOVE(snl, nss, entries);
continue; continue;
} }
sources++; sources++;
/* Missing/invalid defaults is not a fatal error. */
if (nss->getdefs(nss) == -1) { if (nss->getdefs(nss) == -1) {
log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR,
N_("unable to get defaults from %s"), nss->source); N_("unable to get defaults from %s"), nss->source);
} } else {
/* Not a fatal error. */
(void)update_defaults(nss->parse_tree, NULL, (void)update_defaults(nss->parse_tree, NULL,
SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false); SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false);
} }
}
if (sources == 0) { if (sources == 0) {
sudo_warnx("%s", U_("no valid sudoers sources found, quitting")); sudo_warnx("%s", U_("no valid sudoers sources found, quitting"));
goto cleanup; goto cleanup;
@@ -261,6 +267,8 @@ sudoers_init(void *info, sudoers_logger_t logger, char * const envp[])
ret = true; ret = true;
cleanup: cleanup:
mail_parse_errors();
if (!restore_perms()) if (!restore_perms())
ret = -1; ret = -1;
@@ -806,6 +814,8 @@ bad:
ret = false; ret = false;
done: done:
mail_parse_errors();
if (def_group_plugin) if (def_group_plugin)
group_plugin_unload(); group_plugin_unload();
init_parser(NULL, false, false); init_parser(NULL, false, false);
@@ -1046,7 +1056,7 @@ set_cmnd(void)
} }
TAILQ_FOREACH(nss, snl, entries) { 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); (void)update_defaults(nss->parse_tree, NULL, SETDEF_CMND, false);
} }