Defer logging of the successful command until approval plugins have run.

This adds audit plugin support to the sudoers module, currently
only used for accept events.  As a result, the sudoers file is now
initially parsed as an audit plugin.
This commit is contained in:
Todd C. Miller
2020-06-02 09:07:46 -06:00
parent f0dc48548c
commit b519481912
11 changed files with 161 additions and 95 deletions

View File

@@ -26,6 +26,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sudoers.h"
@@ -41,15 +42,12 @@
char *audit_msg = NULL;
int
audit_success(int argc, char *argv[])
static int
audit_success(char *const argv[])
{
int rc = 0;
debug_decl(audit_success, SUDOERS_DEBUG_AUDIT);
if (!def_log_allowed)
debug_return_int(0);
if (argv != NULL) {
#ifdef HAVE_BSM_AUDIT
if (bsm_audit_success(argv) == -1)
@@ -60,7 +58,7 @@ audit_success(int argc, char *argv[])
rc = -1;
#endif
#ifdef HAVE_SOLARIS_AUDIT
if (solaris_audit_success(argc, argv) == -1)
if (solaris_audit_success(argv) == -1)
rc = -1;
#endif
}
@@ -69,52 +67,130 @@ audit_success(int argc, char *argv[])
}
int
audit_failure(int argc, char *argv[], char const *const fmt, ...)
audit_failure(char *const argv[], char const *const fmt, ...)
{
int rc = 0;
int oldlocale, rc = 0;
va_list ap;
debug_decl(audit_success, SUDOERS_DEBUG_AUDIT);
debug_decl(audit_failure, SUDOERS_DEBUG_AUDIT);
/* Audit messages should be in the sudoers locale. */
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
/* Set audit_msg for audit plugin. */
free(audit_msg);
audit_msg = NULL;
va_start(ap, fmt);
if (vasprintf(&audit_msg, fmt, ap) == -1)
if (vasprintf(&audit_msg, _(fmt), ap) == -1)
rc = -1;
va_end(ap);
if (!def_log_denied)
debug_return_int(0);
#if defined(HAVE_BSM_AUDIT) || defined(HAVE_LINUX_AUDIT)
if (argv != NULL) {
int oldlocale;
/* Audit error messages should be in the sudoers locale. */
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
if (def_log_denied && argv != NULL) {
#ifdef HAVE_BSM_AUDIT
va_start(ap, fmt);
if (bsm_audit_failure(argv, _(fmt), ap) == -1)
if (bsm_audit_failure(argv, audit_msg) == -1)
rc = -1;
va_end(ap);
#endif
#ifdef HAVE_LINUX_AUDIT
va_start(ap, fmt);
if (linux_audit_command(argv, 0) == -1)
rc = -1;
va_end(ap);
#endif
#ifdef HAVE_SOLARIS_AUDIT
va_start(ap, fmt);
if (solaris_audit_failure(argc, argv, _(fmt), ap) == -1)
if (solaris_audit_failure(argv, audit_msg) == -1)
rc = -1;
va_end(ap);
#endif
sudoers_setlocale(oldlocale, NULL);
}
#endif /* HAVE_BSM_AUDIT || HAVE_LINUX_AUDIT */
sudoers_setlocale(oldlocale, NULL);
debug_return_int(rc);
}
static int
sudoers_audit_open(unsigned int version, sudo_conv_t conversation,
sudo_printf_t plugin_printf, char * const settings[],
char * const user_info[], int submit_optind, char * const submit_argv[],
char * const submit_envp[], char * const plugin_options[],
const char **errstr)
{
struct sudo_conf_debug_file_list debug_files = TAILQ_HEAD_INITIALIZER(debug_files);
struct sudoers_open_info info;
const char *cp, *plugin_path = NULL;
char * const *cur;
int ret;
debug_decl(sudoers_audit_open, SUDOERS_DEBUG_PLUGIN);
/* Initialize the debug subsystem. */
for (cur = settings; (cp = *cur) != NULL; cur++) {
if (strncmp(cp, "debug_flags=", sizeof("debug_flags=") - 1) == 0) {
cp += sizeof("debug_flags=") - 1;
if (!sudoers_debug_parse_flags(&debug_files, cp))
debug_return_int(-1);
continue;
}
if (strncmp(cp, "plugin_path=", sizeof("plugin_path=") - 1) == 0) {
plugin_path = cp + sizeof("plugin_path=") - 1;
continue;
}
}
if (!sudoers_debug_register(plugin_path, &debug_files))
debug_return_int(-1);
/* Call the sudoers init function. */
info.settings = settings;
info.user_info = user_info;
info.plugin_args = plugin_options;
ret = sudoers_init(&info, submit_envp);
/* The audit functions set audit_msg on failure. */
if (ret != 1 && audit_msg != NULL)
*errstr = audit_msg;
debug_return_int(ret);
}
static int
sudoers_audit_accept(const char *plugin_name, unsigned int plugin_type,
char * const command_info[], char * const run_argv[],
char * const run_envp[], const char **errstr)
{
debug_decl(sudoers_audit_accept, SUDOERS_DEBUG_PLUGIN);
/* Only log the accept event from the sudo front-end */
if (plugin_type != SUDO_FRONT_END)
debug_return_bool(true);
if (!def_log_allowed)
debug_return_bool(true);
if (audit_success(run_argv) != 0 && !def_ignore_audit_errors)
debug_return_bool(false);
if (!log_allowed(VALIDATE_SUCCESS) && !def_ignore_logfile_errors)
debug_return_bool(false);
debug_return_bool(true);
}
static int
sudoers_audit_version(int verbose)
{
debug_decl(sudoers_audit_version, SUDOERS_DEBUG_PLUGIN);
sudo_printf(SUDO_CONV_INFO_MSG, "Sudoers audit plugin version %s\n",
PACKAGE_VERSION);
debug_return_int(true);
}
__dso_public struct audit_plugin sudoers_audit = {
SUDO_AUDIT_PLUGIN,
SUDO_API_VERSION,
sudoers_audit_open,
NULL, /* audit_close */
sudoers_audit_accept,
NULL, /* audit_reject */
NULL, /* audit_error */
sudoers_audit_version,
NULL, /* register_hooks */
NULL /* deregister_hooks */
};

View File

@@ -129,8 +129,7 @@ sudo_auth_init(struct passwd *pw)
if (IS_DISABLED(auth))
continue;
if (!IS_STANDALONE(auth)) {
audit_failure(NewArgc, NewArgv,
N_("invalid authentication methods"));
audit_failure(NewArgv, N_("invalid authentication methods"));
log_warningx(SLOG_SEND_MAIL,
N_("Invalid authentication methods compiled into sudo! "
"You may not mix standalone and non-standalone authentication."));
@@ -253,7 +252,7 @@ verify_user(struct passwd *pw, char *prompt, int validated,
/* Make sure we have at least one auth method. */
if (auth_switch[0].name == NULL) {
audit_failure(NewArgc, NewArgv, N_("no authentication methods"));
audit_failure(NewArgv, N_("no authentication methods"));
log_warningx(SLOG_SEND_MAIL,
N_("There are no authentication methods compiled into sudo! "
"If you want to turn off authentication, use the "
@@ -303,7 +302,7 @@ verify_user(struct passwd *pw, char *prompt, int validated,
}
}
if (num_methods == 0) {
audit_failure(NewArgc, NewArgv, N_("no authentication methods"));
audit_failure(NewArgv, N_("no authentication methods"));
log_warningx(SLOG_SEND_MAIL,
N_("Unable to initialize authentication methods."));
debug_return_int(-1);

View File

@@ -195,16 +195,15 @@ bsm_audit_success(char *exec_args[])
* Returns 0 on success or -1 on error.
*/
int
bsm_audit_failure(char *exec_args[], char const *const fmt, va_list ap)
bsm_audit_failure(char *exec_args[], const char *errmsg)
{
auditinfo_addr_t ainfo_addr;
char text[256];
token_t *tok;
long au_cond;
au_id_t auid;
pid_t pid;
int aufd;
debug_decl(bsm_audit_success, SUDOERS_DEBUG_AUDIT);
debug_decl(bsm_audit_failure, SUDOERS_DEBUG_AUDIT);
/*
* If we are not auditing, don't cut an audit record; just return.
@@ -257,8 +256,7 @@ bsm_audit_failure(char *exec_args[], char const *const fmt, va_list ap)
debug_return_int(-1);
}
au_write(aufd, tok);
(void) vsnprintf(text, sizeof(text), fmt, ap);
tok = au_to_text(text);
tok = au_to_text(errmsg);
if (tok == NULL) {
sudo_warn("au_to_text");
debug_return_int(-1);

View File

@@ -20,7 +20,7 @@
#ifndef SUDOERS_BSM_AUDIT_H
#define SUDOERS_BSM_AUDIT_H
int bsm_audit_success(char *argv[]);
int bsm_audit_failure(char *argv[], char const * const, va_list);
int bsm_audit_success(char *const argv[]);
int bsm_audit_failure(char *const argv[], const char *errmsg);
#endif /* SUDOERS_BSM_AUDIT_H */

View File

@@ -245,7 +245,7 @@ log_denial(int status, bool inform_user)
message = N_("command not allowed");
/* Do auditing first (audit_failure() handles the locale itself). */
audit_failure(NewArgc, NewArgv, "%s", message);
audit_failure(NewArgv, "%s", message);
if (def_log_denied || mailit) {
/* Log and mail messages should be in the sudoers locale. */
@@ -351,7 +351,7 @@ log_auth_failure(int status, unsigned int tries)
debug_decl(log_auth_failure, SUDOERS_DEBUG_LOGGING);
/* Do auditing first (audit_failure() handles the locale itself). */
audit_failure(NewArgc, NewArgv, "%s", N_("authentication failure"));
audit_failure(NewArgv, "%s", N_("authentication failure"));
/*
* Do we need to send mail?

View File

@@ -70,8 +70,7 @@ union sudo_defs_val;
bool sudoers_warn_setlocale(bool restore, int *cookie);
bool sudoers_setlocale(int newlocale, int *prevlocale);
int sudoers_getlocale(void);
int audit_success(int argc, char *argv[]);
int audit_failure(int argc, char *argv[], char const *const fmt, ...) __printflike(3, 4);
int audit_failure(char *const argv[], char const *const fmt, ...) __printflike(2, 3);
bool log_allowed(int status);
bool log_auth_failure(int status, unsigned int tries);
bool log_denial(int status, bool inform_user);

View File

@@ -38,15 +38,6 @@
#include "sudoers_version.h"
#include "interfaces.h"
/*
* Info passed in from the sudo front-end.
*/
struct sudoers_policy_open_info {
char * const *settings;
char * const *user_info;
char * const *plugin_args;
};
/*
* Command execution args to be filled in: argv, envp and command info.
*/
@@ -95,7 +86,7 @@ parse_bool(const char *line, int varlen, int *flags, int fval)
int
sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group)
{
struct sudoers_policy_open_info *info = v;
struct sudoers_open_info *info = v;
char * const *cur;
const char *p, *errstr, *groups = NULL;
const char *remhost = NULL;
@@ -845,7 +836,7 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
const char **errstr)
{
struct sudo_conf_debug_file_list debug_files = TAILQ_HEAD_INITIALIZER(debug_files);
struct sudoers_policy_open_info info;
struct sudoers_open_info info;
const char *cp, *plugin_path = NULL;
char * const *cur;
int ret;
@@ -879,7 +870,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_policy_init(&info, envp);
ret = sudoers_init(&info, envp);
/* The audit functions set audit_msg on failure. */
if (ret != 1 && audit_msg != NULL) {

View File

@@ -44,8 +44,10 @@ static char cwd[PATH_MAX];
static char cmdpath[PATH_MAX];
static int
adt_sudo_common(int argc, char *argv[])
adt_sudo_common(char *argv[])
{
int argc;
if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
log_warning(SLOG_NO_STDERR, "adt_start_session");
return -1;
@@ -76,6 +78,9 @@ adt_sudo_common(int argc, char *argv[])
}
}
for (argc = 0; argv[argc] != NULL; argc++)
continue;
event->adt_sudo.cmdpath = cmdpath;
event->adt_sudo.argc = argc - 1;
event->adt_sudo.argv = &argv[1];
@@ -89,11 +94,11 @@ adt_sudo_common(int argc, char *argv[])
* Returns 0 on success or -1 on error.
*/
int
solaris_audit_success(int argc, char *argv[])
solaris_audit_success(char *argv[])
{
int rc = -1;
if (adt_sudo_common(argc, argv) != 0) {
if (adt_sudo_common(argv) != 0) {
return -1;
}
if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
@@ -111,23 +116,20 @@ solaris_audit_success(int argc, char *argv[])
* Returns 0 on success or -1 on error.
*/
int
solaris_audit_failure(int argc, char *argv[], char const *const fmt, va_list ap)
solaris_audit_failure(char *argv[], const char *errmsg)
{
int rc = -1;
if (adt_sudo_common(argc, argv) != 0) {
if (adt_sudo_common(argv) != 0) {
return -1;
}
if (vasprintf(&event->adt_sudo.errmsg, fmt, ap) == -1) {
log_warning(SLOG_NO_STDERR,
_("audit_failure message too long"));
}
event->adt_sudo.errmsg = errmsg;
if (adt_put_event(event, ADT_FAILURE, ADT_FAIL_VALUE_PROGRAM) != 0) {
log_warning(SLOG_NO_STDERR, "adt_put_event(ADT_FAILURE)");
} else {
rc = 0;
}
free(event->adt_sudo.errmsg);
adt_free_event(event);
(void) adt_end_session(ah);

View File

@@ -19,7 +19,7 @@
#ifndef SUDOERS_SOLARIS_AUDIT_H
#define SUDOERS_SOLARIS_AUDIT_H
int solaris_audit_success(int argc, char *argv[]);
int solaris_audit_failure(int argc, char *argv[], char const *const fmt, va_list);
int solaris_audit_success(char *const argv[]);
int solaris_audit_failure(char *const argv[], const char *errmsg);
#endif /* SUDOERS_SOLARIS_AUDIT_H */

View File

@@ -157,12 +157,15 @@ restore_nproc(void)
}
int
sudoers_policy_init(void *info, char * const envp[])
sudoers_init(void *info, char * const envp[])
{
struct sudo_nss *nss, *nss_next;
int oldlocale, sources = 0;
int ret = -1;
debug_decl(sudoers_policy_init, SUDOERS_DEBUG_PLUGIN);
static int ret = -1;
debug_decl(sudoers_init, SUDOERS_DEBUG_PLUGIN);
if (ret == true)
debug_return_int(true);
bindtextdomain("sudoers", LOCALEDIR);
@@ -350,7 +353,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
/* Check for -C overriding def_closefrom. */
if (user_closefrom >= 0 && user_closefrom != def_closefrom) {
if (!def_closefrom_override) {
audit_failure(NewArgc, NewArgv,
audit_failure(NewArgv,
N_("user not allowed to override closefrom limit"));
sudo_warnx(U_("you are not permitted to use the -C option"));
goto bad;
@@ -381,15 +384,13 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
/* Defer uid/gid checks until after defaults have been updated. */
if (unknown_runas_uid && !def_runas_allow_unknown_id) {
audit_failure(NewArgc, NewArgv, N_("unknown user: %s"),
runas_pw->pw_name);
audit_failure(NewArgv, N_("unknown user: %s"), runas_pw->pw_name);
sudo_warnx(U_("unknown user: %s"), runas_pw->pw_name);
goto done;
}
if (runas_gr != NULL) {
if (unknown_runas_gid && !def_runas_allow_unknown_id) {
audit_failure(NewArgc, NewArgv, N_("unknown group: %s"),
runas_gr->gr_name);
audit_failure(NewArgv, N_("unknown group: %s"), runas_gr->gr_name);
sudo_warnx(U_("unknown group: %s"), runas_gr->gr_name);
goto done;
}
@@ -431,14 +432,14 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
/* Bail if a tty is required and we don't have one. */
if (def_requiretty && !tty_present()) {
audit_failure(NewArgc, NewArgv, N_("no tty"));
audit_failure(NewArgv, N_("no tty"));
sudo_warnx(U_("sorry, you must have a tty to run sudo"));
goto bad;
}
/* Check runas user's shell. */
if (!check_user_shell(runas_pw)) {
audit_failure(NewArgc, NewArgv, N_("invalid shell for user %s: %s"),
audit_failure(NewArgv, N_("invalid shell for user %s: %s"),
runas_pw->pw_name, runas_pw->pw_shell);
log_warningx(SLOG_RAW_MSG, N_("invalid shell for user %s: %s"),
runas_pw->pw_name, runas_pw->pw_shell);
@@ -503,16 +504,16 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
/* Finally tell the user if the command did not exist. */
if (cmnd_status == NOT_FOUND_DOT) {
audit_failure(NewArgc, NewArgv, N_("command in current directory"));
audit_failure(NewArgv, N_("command in current directory"));
sudo_warnx(U_("ignoring \"%s\" found in '.'\nUse \"sudo ./%s\" if this is the \"%s\" you wish to run."), user_cmnd, user_cmnd, user_cmnd);
goto bad;
} else if (cmnd_status == NOT_FOUND) {
if (ISSET(sudo_mode, MODE_CHECK)) {
audit_failure(NewArgc, NewArgv, N_("%s: command not found"),
audit_failure(NewArgv, N_("%s: command not found"),
NewArgv[0]);
sudo_warnx(U_("%s: command not found"), NewArgv[0]);
} else {
audit_failure(NewArgc, NewArgv, N_("%s: command not found"),
audit_failure(NewArgv, N_("%s: command not found"),
user_cmnd);
sudo_warnx(U_("%s: command not found"), user_cmnd);
}
@@ -521,8 +522,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
/* If user specified a timeout make sure sudoers allows it. */
if (!def_user_command_timeouts && user_timeout > 0) {
audit_failure(NewArgc, NewArgv,
N_("user not allowed to set a command timeout"));
audit_failure(NewArgv, N_("user not allowed to set a command timeout"));
sudo_warnx(U_("sorry, you are not allowed set a command timeout"));
goto bad;
}
@@ -530,7 +530,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
/* If user specified env vars make sure sudoers allows it. */
if (ISSET(sudo_mode, MODE_RUN) && !def_setenv) {
if (ISSET(sudo_mode, MODE_PRESERVE_ENV)) {
audit_failure(NewArgc, NewArgv,
audit_failure(NewArgv,
N_("user not allowed to set a preserve the environment"));
sudo_warnx(U_("sorry, you are not allowed to preserve the environment"));
goto bad;
@@ -552,9 +552,6 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
}
}
if (!log_allowed(validated) && !def_ignore_logfile_errors)
goto bad;
switch (sudo_mode & MODE_MASK) {
case MODE_CHECK:
ret = display_cmnd(snl, list_pw ? list_pw : sudo_user.pw);
@@ -665,7 +662,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
if (safe_cmnd == NULL) {
if (errno != ENOENT)
goto done;
audit_failure(NewArgc, NewArgv, N_("%s: command not found"),
audit_failure(NewArgv, N_("%s: command not found"),
env_editor ? env_editor : def_editor);
sudo_warnx(U_("%s: command not found"),
env_editor ? env_editor : def_editor);
@@ -674,14 +671,9 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
sudoers_gc_add(GC_VECTOR, edit_argv);
NewArgv = edit_argv;
NewArgc = edit_argc;
if (audit_success(NewArgc, NewArgv) != 0 && !def_ignore_audit_errors)
goto done;
/* We want to run the editor with the unmodified environment. */
env_swap_old();
} else {
if (audit_success(NewArgc, NewArgv) != 0 && !def_ignore_audit_errors)
goto done;
}
goto done;
@@ -883,7 +875,7 @@ set_cmnd(void)
}
if (ret == NOT_FOUND_ERROR) {
if (errno == ENAMETOOLONG) {
audit_failure(NewArgc, NewArgv, N_("command too long"));
audit_failure(NewArgv, N_("command too long"));
}
log_warning(0, "%s", NewArgv[0]);
debug_return_int(ret);

View File

@@ -48,6 +48,15 @@
#include "sudo_util.h"
#include "sudoers_debug.h"
/*
* Info passed in from the sudo front-end.
*/
struct sudoers_open_info {
char * const *settings;
char * const *user_info;
char * const *plugin_args;
};
/*
* Supplementary group IDs for a user.
*/
@@ -379,7 +388,7 @@ bool matches_env_pattern(const char *pattern, const char *var, bool *full_match)
/* sudoers.c */
FILE *open_sudoers(const char *, bool, bool *);
int sudoers_policy_init(void *info, char * const envp[]);
int sudoers_init(void *info, 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);
extern struct sudo_user sudo_user;