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"
|
# 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);
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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)
|
||||||
|
@@ -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,
|
||||||
|
@@ -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, ...)
|
||||||
|
@@ -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 {
|
||||||
|
(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
|
int
|
||||||
@@ -223,33 +229,33 @@ 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 {
|
||||||
|
(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) {
|
if (sources == 0) {
|
||||||
sudo_warnx("%s", U_("no valid sudoers sources found, quitting"));
|
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;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user