diff --git a/doc/sudoers.man.in b/doc/sudoers.man.in index 3923d9954..7338c2af0 100644 --- a/doc/sudoers.man.in +++ b/doc/sudoers.man.in @@ -25,7 +25,7 @@ .nr BA @BAMAN@ .nr LC @LCMAN@ .nr PS @PSMAN@ -.TH "SUDOERS" "@mansectform@" "May 7, 2021" "Sudo @PACKAGE_VERSION@" "File Formats Manual" +.TH "SUDOERS" "@mansectform@" "July 9, 2021" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .nh .if n .ad l .SH "NAME" @@ -2728,6 +2728,17 @@ by default. .sp This setting is only supported by version 1.8.29 or higher. .TP 18n +log_exit_status +If set, +\fBsudoers\fR +will log the exit value of commands that are run to syslog and/or a log file. +If a command was terminated by a signal, the signal name is logged as well. +This flag is +\fIoff\fR +by default. +.sp +This setting is only supported by version 1.9.8 or higher. +.TP 18n log_host If set, the host name will be included in log entries written to the file configured by the diff --git a/doc/sudoers.mdoc.in b/doc/sudoers.mdoc.in index ce7c07305..193bbf9a8 100644 --- a/doc/sudoers.mdoc.in +++ b/doc/sudoers.mdoc.in @@ -24,7 +24,7 @@ .nr BA @BAMAN@ .nr LC @LCMAN@ .nr PS @PSMAN@ -.Dd May 7, 2021 +.Dd July 9, 2021 .Dt SUDOERS @mansectform@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -2573,6 +2573,16 @@ This flag is by default. .Pp This setting is only supported by version 1.8.29 or higher. +.It log_exit_status +If set, +.Nm +will log the exit value of commands that are run to syslog and/or a log file. +If a command was terminated by a signal, the signal name is logged as well. +This flag is +.Em off +by default. +.Pp +This setting is only supported by version 1.9.8 or higher. .It log_host If set, the host name will be included in log entries written to the file configured by the diff --git a/plugins/sudoers/def_data.c b/plugins/sudoers/def_data.c index 5952a069e..d781f3571 100644 --- a/plugins/sudoers/def_data.c +++ b/plugins/sudoers/def_data.c @@ -577,6 +577,10 @@ struct sudo_defs_types sudo_defs_table[] = { "admin_flag", T_STR|T_BOOL|T_CHPATH, N_("Path to the file that is created the first time sudo is run: %s"), NULL, + }, { + "log_exit_status", T_FLAG, + N_("Log the exit status of commands"), + NULL, }, { NULL, 0, NULL } diff --git a/plugins/sudoers/def_data.h b/plugins/sudoers/def_data.h index fb3f8aa4b..0d6126dbb 100644 --- a/plugins/sudoers/def_data.h +++ b/plugins/sudoers/def_data.h @@ -266,6 +266,8 @@ #define def_selinux (sudo_defs_table[I_SELINUX].sd_un.flag) #define I_ADMIN_FLAG 132 #define def_admin_flag (sudo_defs_table[I_ADMIN_FLAG].sd_un.str) +#define I_LOG_EXIT_STATUS 133 +#define def_log_exit_status (sudo_defs_table[I_LOG_EXIT_STATUS].sd_un.flag) enum def_tuple { never, diff --git a/plugins/sudoers/def_data.in b/plugins/sudoers/def_data.in index 3183fae18..fd8d3715f 100644 --- a/plugins/sudoers/def_data.in +++ b/plugins/sudoers/def_data.in @@ -415,3 +415,6 @@ selinux admin_flag T_STR|T_BOOL|T_CHPATH "Path to the file that is created the first time sudo is run: %s" +log_exit_status + T_FLAG + "Log the exit status of commands" diff --git a/plugins/sudoers/logging.c b/plugins/sudoers/logging.c index 06efbf576..d290cfd17 100644 --- a/plugins/sudoers/logging.c +++ b/plugins/sudoers/logging.c @@ -492,6 +492,63 @@ log_allowed(void) debug_return_bool(ret); } +bool +log_exit_status(int exit_status) +{ + struct eventlog evlog; + int evl_flags = 0; + int ecode = 0; + int oldlocale; + struct timespec run_time; + char sigbuf[SIG2STR_MAX]; + char *signame = NULL; + bool dumped_core = false; + bool ret = true; + debug_decl(log_exit_status, SUDOERS_DEBUG_LOGGING); + + if (def_log_exit_status || def_mail_always) { + if (sudo_gettime_real(&run_time) == -1) { + sudo_warn("%s", U_("unable to get time of day")); + ret = false; + goto done; + } + sudo_timespecsub(&run_time, &sudo_user.submit_time, &run_time); + + if (WIFEXITED(exit_status)) { + ecode = WEXITSTATUS(exit_status); + } else if (WIFSIGNALED(exit_status)) { + int signo = WTERMSIG(exit_status); + if (signo <= 0 || sig2str(signo, sigbuf) == -1) + (void)snprintf(sigbuf, sizeof(sigbuf), "%d", signo); + signame = sigbuf; + ecode = signo | 128; + dumped_core = WCOREDUMP(exit_status); + } else { + sudo_warnx("invalid exit status 0x%x", exit_status); + ret = false; + goto done; + } + + /* Log and mail messages should be in the sudoers locale. */ + sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale); + + sudoers_to_eventlog(&evlog, NewArgv, env_get()); + if (def_mail_always) { + SET(evl_flags, EVLOG_MAIL); + if (!def_log_exit_status) + SET(evl_flags, EVLOG_MAIL_ONLY); + } + if (!eventlog_exit(&evlog, evl_flags, &run_time, ecode, signame, + dumped_core, NULL, NULL)) + ret = false; + + sudoers_setlocale(oldlocale, NULL); + } + +done: + debug_return_bool(ret); +} + /* * Perform logging for log_warning()/log_warningx(). */ diff --git a/plugins/sudoers/logging.h b/plugins/sudoers/logging.h index 029b5616e..17938bd2d 100644 --- a/plugins/sudoers/logging.h +++ b/plugins/sudoers/logging.h @@ -57,6 +57,7 @@ int sudoers_getlocale(void); int audit_failure(char *const argv[], char const *const fmt, ...) __printflike(2, 3); int vaudit_failure(char *const argv[], char const *const fmt, va_list ap) __printflike(2, 0); bool log_allowed(void); +bool log_exit_status(int exit_status); bool log_auth_failure(int status, unsigned int tries); bool log_denial(int status, bool inform_user); bool log_failure(int status, int flags); diff --git a/plugins/sudoers/policy.c b/plugins/sudoers/policy.c index e42fb932d..199a8be93 100644 --- a/plugins/sudoers/policy.c +++ b/plugins/sudoers/policy.c @@ -961,10 +961,11 @@ sudoers_policy_close(int exit_status, int error_code) /* Close the session we opened in sudoers_policy_init_session(). */ (void)sudo_auth_end_session(runas_pw); - /* We do not currently log the exit status. */ if (error_code) { errno = error_code; sudo_warn(U_("unable to execute %s"), safe_cmnd); + } else { + log_exit_status(exit_status); } } @@ -1044,8 +1045,8 @@ sudoers_policy_check(int argc, char * const argv[], char *env_add[], #ifndef NO_LEAKS if (ret == true && sudo_version >= SUDO_API_MKVERSION(1, 3)) { /* Unset close function if we don't need it to avoid extra process. */ - if (!def_log_input && !def_log_output && !def_use_pty && - !sudo_auth_needs_end_session()) + if (!def_log_input && !def_log_output && !def_log_exit_status && + !def_use_pty && !sudo_auth_needs_end_session()) sudoers_policy.close = NULL; } #endif diff --git a/plugins/sudoers/regress/fuzz/fuzz_policy.c b/plugins/sudoers/regress/fuzz/fuzz_policy.c index 9e285bf3b..6383dc2d9 100644 --- a/plugins/sudoers/regress/fuzz/fuzz_policy.c +++ b/plugins/sudoers/regress/fuzz/fuzz_policy.c @@ -704,6 +704,13 @@ log_failure(int status, int flags) return true; } +/* STUB */ +bool +log_exit_status(int exit_status) +{ + return true; +} + /* STUB */ int audit_failure(char *const argv[], char const *const fmt, ...)