From 22105009d86a5c252cc3d4d187fb54ac18a6ec0f Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Thu, 30 Jan 2020 13:25:34 -0700 Subject: [PATCH] Define a new plugin type that receives accept and reject messages. This can be used to implement logging-only plugins. The plugin functions now take an errstr argument that can be used to return an error string to be logged on failure or error. --- doc/sudo_plugin.mdoc.in | 492 +++++++++++++++++++++++++++++++++----- include/sudo_plugin.h | 59 +++-- src/exec_pty.c | 67 ++++-- src/load_plugins.c | 26 +- src/parse_args.c | 26 +- src/sudo.c | 511 +++++++++++++++++++++++++++++++--------- src/sudo.h | 8 +- src/sudo_plugin_int.h | 5 +- 8 files changed, 983 insertions(+), 211 deletions(-) diff --git a/doc/sudo_plugin.mdoc.in b/doc/sudo_plugin.mdoc.in index 2dc93e07e..a5467e54a 100644 --- a/doc/sudo_plugin.mdoc.in +++ b/doc/sudo_plugin.mdoc.in @@ -1,7 +1,7 @@ .\" .\" SPDX-License-Identifier: ISC .\" -.\" Copyright (c) 2009-2019 Todd C. Miller +.\" Copyright (c) 2009-2020 Todd C. Miller .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd December 5, 2019 +.Dd Januarry 28, 2020 .Dt SUDO_PLUGIN @mansectform@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -70,17 +70,19 @@ struct policy_plugin { int (*open)(unsigned int version, sudo_conv_t conversation, sudo_printf_t plugin_printf, char * const settings[], char * const user_info[], char * const user_env[], - char * const plugin_options[]); + char * const plugin_options[], const char **errstr); void (*close)(int exit_status, int error); - int (*show_version)(int verbose); + int (*show_version)(int verbose, const char **errstr); int (*check_policy)(int argc, char * const argv[], char *env_add[], char **command_info[], - char **argv_out[], char **user_env_out[]); + char **argv_out[], char **user_env_out[], + const char **errstr); int (*list)(int argc, char * const argv[], int verbose, - const char *list_user); - int (*validate)(void); + const char *list_user, const char **errstr); + int (*validate)(const char **errstr); void (*invalidate)(int remove); - int (*init_session)(struct passwd *pwd, char **user_env[]); + int (*init_session)(struct passwd *pwd, char **user_env[], + const char **errstr); void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook)); void (*deregister_hooks)(int version, @@ -110,7 +112,7 @@ built against. int (*open)(unsigned int version, sudo_conv_t conversation, sudo_printf_t plugin_printf, char * const settings[], char * const user_info[], char * const user_env[], - char * const plugin_options[]); + char * const plugin_options[], const char **errstr); .Ed .Pp Returns 1 on success, 0 on failure, \-1 if a general error occurred, @@ -441,32 +443,6 @@ The process ID of the running .Nm sudo process. Only available starting with API version 1.2. -.It plugin_options -Any (non-comment) strings immediately after the plugin path are -passed as arguments to the plugin. -These arguments are split on a white space boundary and are passed to -the plugin in the form of a -.Dv NULL Ns -terminated -array of strings. -If no arguments were -specified, -.Em plugin_options -will be the -.Dv NULL -pointer. -.Pp -NOTE: the -.Em plugin_options -parameter is only available starting with -API version 1.2. -A plugin -.Sy must -check the API version specified -by the -.Nm sudo -front end before using -.Em plugin_options . -Failure to do so may result in a crash. .It ppid=int The parent process ID of the running .Nm sudo @@ -518,6 +494,53 @@ field will never include one itself but the .Em value might. +.It plugin_options +Any (non-comment) strings immediately after the plugin path are +passed as arguments to the plugin. +These arguments are split on a white space boundary and are passed to +the plugin in the form of a +.Dv NULL Ns -terminated +array of strings. +If no arguments were +specified, +.Em plugin_options +will be the +.Dv NULL +pointer. +.Pp +NOTE: the +.Em plugin_options +parameter is only available starting with +API version 1.2. +A plugin +.Sy must +check the API version specified +by the +.Nm sudo +front end before using +.Em plugin_options . +Failure to do so may result in a crash. +.It errstr +If the +.Fn open +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .El .It close .Bd -literal -compact @@ -585,7 +608,7 @@ front end may execute the command directly instead of running it as a child process. .It show_version .Bd -literal -compact -int (*show_version)(int verbose); +int (*show_version)(int verbose, const char **errstr); .Ed .Pp The @@ -602,16 +625,45 @@ or .Fn plugin_printf function using .Dv SUDO_CONV_INFO_MSG . -If the user requests detailed version information, the verbose flag will be set. .Pp Returns 1 on success, 0 on failure, \-1 if a general error occurred, or \-2 if there was a usage error, although the return value is currently ignored. +.Pp +The function arguments are as follows: +.Bl -tag -width 4n +.It verbose +If the user requests detailed version information, the +.Em verbose +flag will be set to 1. +.It errstr +If the +.Fn show_version +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. +.El .It check_policy .Bd -literal -compact int (*check_policy)(int argc, char * const argv[], char *env_add[], char **command_info[], - char **argv_out[], char **user_env_out[]); + char **argv_out[], char **user_env_out[], + const char **errstr); .Ed .Pp The @@ -1006,11 +1058,32 @@ The .Dv NULL Ns -terminated environment vector to use when executing the command. The plugin is responsible for allocating and populating the vector. +.It errstr +If the +.Fn check_policy +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .El .It list .Bd -literal -compact int (*list)(int argc, char * const argv[], - int verbose, const char *list_user); + int verbose, const char *list_user, const char **errstr); .Ed .Pp List available privileges for the invoking user. @@ -1057,10 +1130,31 @@ allows it. If .Dv NULL , the plugin should list the privileges of the invoking user. +.It errstr +If the +.Fn list +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .El .It validate .Bd -literal -compact -int (*validate)(void); +int (*validate)(const char **errstr); .Ed .Pp The @@ -1091,6 +1185,31 @@ function with .Dv SUDO_CONF_ERROR_MSG to present additional error information to the user. +.Pp +The function arguments are as follows: +.Bl -tag -width 4n +.It errstr +If the +.Fn validate +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. +.El .It invalidate .Bd -literal -compact void (*invalidate)(int remove); @@ -1100,8 +1219,7 @@ The .Fn invalidate function is called when .Nm sudo -is called with -the +is run with the .Fl k or .Fl K @@ -1321,6 +1439,27 @@ front end doesn't support API version 1.15 or higher, .Fn event_alloc will not be set. +.It errstr +If the +.Fn init_session +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .El .Pp .Em Policy Plugin Version Macros @@ -1352,20 +1491,26 @@ struct io_plugin { sudo_printf_t plugin_printf, char * const settings[], char * const user_info[], char * const command_info[], int argc, char * const argv[], char * const user_env[], - char * const plugin_options[]); + char * const plugin_options[], const char **errstr); void (*close)(int exit_status, int error); /* wait status or error */ - int (*show_version)(int verbose); - int (*log_ttyin)(const char *buf, unsigned int len); - int (*log_ttyout)(const char *buf, unsigned int len); - int (*log_stdin)(const char *buf, unsigned int len); - int (*log_stdout)(const char *buf, unsigned int len); - int (*log_stderr)(const char *buf, unsigned int len); + int (*show_version)(int verbose, const char **errstr); + int (*log_ttyin)(const char *buf, unsigned int len, + const char **errstr); + int (*log_ttyout)(const char *buf, unsigned int len, + const char **errstr); + int (*log_stdin)(const char *buf, unsigned int len, + const char **errstr); + int (*log_stdout)(const char *buf, unsigned int len, + const char **errstr); + int (*log_stderr)(const char *buf, unsigned int len, + const char **errstr); void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook)); void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook)); - int (*change_winsize)(unsigned int lines, unsigned int cols); - int (*log_suspend)(int signo); + int (*change_winsize)(unsigned int lines, unsigned int cols, + const char **errstr); + int (*log_suspend)(int signo, const char **errstr); struct sudo_plugin_event * (*event_alloc)(void); }; .Ed @@ -1621,6 +1766,27 @@ by the front end before using .Em plugin_options . Failure to do so may result in a crash. +.It errstr +If the +.Fn open +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .El .It close .Bd -literal -compact @@ -1678,14 +1844,43 @@ or .Fn plugin_printf function using .Dv SUDO_CONV_INFO_MSG . -If the user requests detailed version information, the verbose flag will be set. .Pp Returns 1 on success, 0 on failure, \-1 if a general error occurred, or \-2 if there was a usage error, although the return value is currently ignored. +.Pp +The function arguments are as follows: +.Bl -tag -width 4n +.It verbose +If the user requests detailed version information, the +.Em verbose +flag will be set to 1. +.It errstr +If the +.Fn show_version +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. +.El .It log_ttyin .Bd -literal -compact -int (*log_ttyin)(const char *buf, unsigned int len); +int (*log_ttyin)(const char *buf, unsigned int len, + const char **errstr); .Ed .Pp The @@ -1706,10 +1901,32 @@ The buffer containing user input. The length of .Em buf in bytes. +.It errstr +If the +.Fn log_ttyin +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .El .It log_ttyout .Bd -literal -compact -int (*log_ttyout)(const char *buf, unsigned int len); +int (*log_ttyout)(const char *buf, unsigned int len, + const char **errstr); .Ed .Pp The @@ -1729,10 +1946,32 @@ The buffer containing command output. The length of .Em buf in bytes. +.It errstr +If the +.Fn log_ttyout +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .El .It log_stdin .Bd -literal -compact -int (*log_stdin)(const char *buf, unsigned int len); +int (*log_stdin)(const char *buf, unsigned int len, + const char **errstr); .Ed .Pp The @@ -1754,10 +1993,32 @@ The buffer containing user input. The length of .Em buf in bytes. +.It errstr +If the +.Fn log_stdin +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .El .It log_stdout .Bd -literal -compact -int (*log_stdout)(const char *buf, unsigned int len); +int (*log_stdout)(const char *buf, unsigned int len, + const char **errstr); .Ed .Pp The @@ -1779,10 +2040,32 @@ The buffer containing command output. The length of .Em buf in bytes. +.It errstr +If the +.Fn log_stdout +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .El .It log_stderr .Bd -literal -compact -int (*log_stderr)(const char *buf, unsigned int len); +int (*log_stderr)(const char *buf, unsigned int len, + const char **errstr); .Ed .Pp The @@ -1804,6 +2087,27 @@ The buffer containing command output. The length of .Em buf in bytes. +.It errstr +If the +.Fn log_stderr +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .El .It register_hooks See the @@ -1817,7 +2121,8 @@ section for a description of .Li deregister_hooks . .It change_winsize .Bd -literal -compact -int (*change_winsize)(unsigned int lines, unsigned int cols); +int (*change_winsize)(unsigned int lines, unsigned int cols, + const char **errstr); .Ed .Pp The @@ -1829,24 +2134,76 @@ list. Returns \-1 if an error occurred, in which case no further calls to .Fn change_winsize will be made, +.Pp +The function arguments are as follows: +.Bl -tag -width 4n +.It lines +The number of lines (rows) in the resized terminal. +.It cols +The number of columns in the resized terminal. +.It errstr +If the +.Fn change_winsize +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. +.El .It log_suspend .Bd -literal -compact -int (*log_suspend)(int signo); +int (*log_suspend)(int signo, const char **errstr); .Ed .Pp The .Fn log_suspend function is called whenever a command is suspended or resumed. -The -.Fa signo -argument is either the signal that caused the command to be suspended or -.Dv SIGCONT -if the command was resumed. Logging this information makes it possible to skip the period of time when the command was suspended during playback of a session. Returns \-1 if an error occurred, in which case no further calls to .Fn log_suspend will be made, +.Pp +The function arguments are as follows: +.Bl -tag -width 4n +.It signo +The signal that caused the command to be suspended, or +.Dv SIGCONT +if the command was resumed. +.It errstr +If the +.Fn log_suspend +function returns a value other than 1, the plugin may +store a message describing the failure or error in +.Em errstr . +The +.Nm sudo +front end will then pass this value to any registered audit plugins. +.Pp +NOTE: the +.Em errstr +parameter is only available starting with +API version 1.15. +A plugin +.Sy must +check the API version specified by the +.Nm sudo +front end before using +.Em errstr . +Failure to do so may result in a crash. .It event_alloc .Bd -literal -compact struct sudo_plugin_event * (*event_alloc)(void); @@ -2930,6 +3287,13 @@ The field was added to the policy_plugin and io_plugin structs. .Pp The +.Fa errstr +argument was added to the policy and I/O plugin functions +which the plugin function can use to return an error string. +This string may be used by the audit plugin to report failure or +error conditions set by the other plugins. +.Pp +The .Fn close function is now is called regardless of whether or not a command was actually executed. diff --git a/include/sudo_plugin.h b/include/sudo_plugin.h index 461f66941..1298d251f 100644 --- a/include/sudo_plugin.h +++ b/include/sudo_plugin.h @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2018 Todd C. Miller + * Copyright (c) 2009-2020 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -162,17 +162,18 @@ struct policy_plugin { int (*open)(unsigned int version, sudo_conv_t conversation, sudo_printf_t sudo_printf, char * const settings[], char * const user_info[], char * const user_env[], - char * const plugin_options[]); + char * const plugin_options[], const char **errstr); void (*close)(int exit_status, int error); /* wait status or error */ int (*show_version)(int verbose); int (*check_policy)(int argc, char * const argv[], char *env_add[], char **command_info[], - char **argv_out[], char **user_env_out[]); + char **argv_out[], char **user_env_out[], const char **errstr); int (*list)(int argc, char * const argv[], int verbose, - const char *list_user); - int (*validate)(void); + const char *list_user, const char **errstr); + int (*validate)(const char **errstr); void (*invalidate)(int remove); - int (*init_session)(struct passwd *pwd, char **user_env_out[]); + int (*init_session)(struct passwd *pwd, char **user_env_out[], + const char **errstr); void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook)); void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook)); struct sudo_plugin_event * (*event_alloc)(void); @@ -187,19 +188,47 @@ struct io_plugin { sudo_printf_t sudo_printf, char * const settings[], char * const user_info[], char * const command_info[], int argc, char * const argv[], char * const user_env[], - char * const plugin_options[]); + char * const plugin_options[], const char **errstr); void (*close)(int exit_status, int error); /* wait status or error */ int (*show_version)(int verbose); - int (*log_ttyin)(const char *buf, unsigned int len); - int (*log_ttyout)(const char *buf, unsigned int len); - int (*log_stdin)(const char *buf, unsigned int len); - int (*log_stdout)(const char *buf, unsigned int len); - int (*log_stderr)(const char *buf, unsigned int len); + int (*log_ttyin)(const char *buf, unsigned int len, const char **errstr); + int (*log_ttyout)(const char *buf, unsigned int len, const char **errstr); + int (*log_stdin)(const char *buf, unsigned int len, const char **errstr); + int (*log_stdout)(const char *buf, unsigned int len, const char **errstr); + int (*log_stderr)(const char *buf, unsigned int len, const char **errstr); + void (*register_hooks)(int version, + int (*register_hook)(struct sudo_hook *hook)); + void (*deregister_hooks)(int version, + int (*deregister_hook)(struct sudo_hook *hook)); + int (*change_winsize)(unsigned int line, unsigned int cols, + const char **errstr); + int (*log_suspend)(int signo, const char **errstr); + struct sudo_plugin_event * (*event_alloc)(void); +}; + +/* Audit plugin type and defines */ +struct audit_plugin { +#define SUDO_AUDIT_PLUGIN 3 + unsigned int type; /* always SUDO_AUDIT_PLUGIN */ + unsigned int version; /* always SUDO_API_VERSION */ + int (*open)(unsigned int version, sudo_conv_t conversation, + sudo_printf_t sudo_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); + void (*close)(int exit_status, int error); + int (*accept)(const char *plugin_name, unsigned int plugin_type, + char * const command_info[], char * const run_argv[], + char * const run_envp[], const char **errstr); + int (*reject)(const char *plugin_name, unsigned int plugin_type, + const char *audit_msg, char * const command_info[], + const char **errstr); + int (*error)(const char *plugin_name, unsigned int plugin_type, + const char *audit_msg, char * const command_info[], + const char **errstr); + int (*show_version)(int verbose); void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook)); void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook)); - int (*change_winsize)(unsigned int line, unsigned int cols); - int (*log_suspend)(int signo); - struct sudo_plugin_event * (*event_alloc)(void); }; /* Sudoers group plugin version major/minor */ diff --git a/src/exec_pty.c b/src/exec_pty.c index 287c5bb54..9d192b900 100644 --- a/src/exec_pty.c +++ b/src/exec_pty.c @@ -201,6 +201,7 @@ static bool log_ttyin(const char *buf, unsigned int n, struct io_buffer *iob) { struct plugin_container *plugin; + const char *errstr = NULL; sigset_t omask; bool ret = true; debug_decl(log_ttyin, SUDO_DEBUG_EXEC); @@ -211,11 +212,17 @@ log_ttyin(const char *buf, unsigned int n, struct io_buffer *iob) int rc; sudo_debug_set_active_instance(plugin->debug_instance); - rc = plugin->u.io->log_ttyin(buf, n); + rc = plugin->u.io->log_ttyin(buf, n, &errstr); if (rc <= 0) { if (rc < 0) { /* Error: disable plugin's I/O function. */ plugin->u.io->log_ttyin = NULL; + audit_error(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("I/O plugin error"), NULL); + } else { + audit_reject(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("command rejected by I/O plugin"), + NULL); } ret = false; break; @@ -233,6 +240,7 @@ static bool log_stdin(const char *buf, unsigned int n, struct io_buffer *iob) { struct plugin_container *plugin; + const char *errstr = NULL; sigset_t omask; bool ret = true; debug_decl(log_stdin, SUDO_DEBUG_EXEC); @@ -243,11 +251,17 @@ log_stdin(const char *buf, unsigned int n, struct io_buffer *iob) int rc; sudo_debug_set_active_instance(plugin->debug_instance); - rc = plugin->u.io->log_stdin(buf, n); + rc = plugin->u.io->log_stdin(buf, n, &errstr); if (rc <= 0) { if (rc < 0) { /* Error: disable plugin's I/O function. */ plugin->u.io->log_stdin = NULL; + audit_error(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("I/O plugin error"), NULL); + } else { + audit_reject(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("command rejected by I/O plugin"), + NULL); } ret = false; break; @@ -265,6 +279,7 @@ static bool log_ttyout(const char *buf, unsigned int n, struct io_buffer *iob) { struct plugin_container *plugin; + const char *errstr = NULL; sigset_t omask; bool ret = true; debug_decl(log_ttyout, SUDO_DEBUG_EXEC); @@ -275,11 +290,17 @@ log_ttyout(const char *buf, unsigned int n, struct io_buffer *iob) int rc; sudo_debug_set_active_instance(plugin->debug_instance); - rc = plugin->u.io->log_ttyout(buf, n); + rc = plugin->u.io->log_ttyout(buf, n, &errstr); if (rc <= 0) { if (rc < 0) { /* Error: disable plugin's I/O function. */ plugin->u.io->log_ttyout = NULL; + audit_error(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("I/O plugin error"), NULL); + } else { + audit_reject(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("command rejected by I/O plugin"), + NULL); } ret = false; break; @@ -308,6 +329,7 @@ static bool log_stdout(const char *buf, unsigned int n, struct io_buffer *iob) { struct plugin_container *plugin; + const char *errstr = NULL; sigset_t omask; bool ret = true; debug_decl(log_stdout, SUDO_DEBUG_EXEC); @@ -318,11 +340,17 @@ log_stdout(const char *buf, unsigned int n, struct io_buffer *iob) int rc; sudo_debug_set_active_instance(plugin->debug_instance); - rc = plugin->u.io->log_stdout(buf, n); + rc = plugin->u.io->log_stdout(buf, n, &errstr); if (rc <= 0) { if (rc < 0) { /* Error: disable plugin's I/O function. */ plugin->u.io->log_stdout = NULL; + audit_error(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("I/O plugin error"), NULL); + } else { + audit_reject(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("command rejected by I/O plugin"), + NULL); } ret = false; break; @@ -351,6 +379,7 @@ static bool log_stderr(const char *buf, unsigned int n, struct io_buffer *iob) { struct plugin_container *plugin; + const char *errstr = NULL; sigset_t omask; bool ret = true; debug_decl(log_stderr, SUDO_DEBUG_EXEC); @@ -361,11 +390,17 @@ log_stderr(const char *buf, unsigned int n, struct io_buffer *iob) int rc; sudo_debug_set_active_instance(plugin->debug_instance); - rc = plugin->u.io->log_stderr(buf, n); + rc = plugin->u.io->log_stderr(buf, n, &errstr); if (rc <= 0) { if (rc < 0) { /* Error: disable plugin's I/O function. */ plugin->u.io->log_stderr = NULL; + audit_error(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("I/O plugin error"), NULL); + } else { + audit_reject(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("command rejected by I/O plugin"), + NULL); } ret = false; break; @@ -394,6 +429,7 @@ static void log_suspend(int signo) { struct plugin_container *plugin; + const char *errstr = NULL; sigset_t omask; debug_decl(log_suspend, SUDO_DEBUG_EXEC); @@ -405,12 +441,12 @@ log_suspend(int signo) int rc; sudo_debug_set_active_instance(plugin->debug_instance); - rc = plugin->u.io->log_suspend(signo); + rc = plugin->u.io->log_suspend(signo, &errstr); if (rc <= 0) { - if (rc < 0) { - /* Error: disable plugin's I/O function. */ - plugin->u.io->log_suspend = NULL; - } + /* Error: disable plugin's I/O function. */ + plugin->u.io->log_suspend = NULL; + audit_error(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("error logging suspend"), NULL); break; } } @@ -426,6 +462,7 @@ static void log_winchange(unsigned int rows, unsigned int cols) { struct plugin_container *plugin; + const char *errstr = NULL; sigset_t omask; debug_decl(log_winchange, SUDO_DEBUG_EXEC); @@ -437,12 +474,12 @@ log_winchange(unsigned int rows, unsigned int cols) int rc; sudo_debug_set_active_instance(plugin->debug_instance); - rc = plugin->u.io->change_winsize(rows, cols); + rc = plugin->u.io->change_winsize(rows, cols, &errstr); if (rc <= 0) { - if (rc < 0) { - /* Error: disable plugin's I/O function. */ - plugin->u.io->change_winsize = NULL; - } + /* Error: disable plugin's I/O function. */ + plugin->u.io->change_winsize = NULL; + audit_error(plugin->name, SUDO_IO_PLUGIN, + errstr ? errstr : _("error changing window size"), NULL); break; } } diff --git a/src/load_plugins.c b/src/load_plugins.c index e3fb06057..28006ec4d 100644 --- a/src/load_plugins.c +++ b/src/load_plugins.c @@ -242,7 +242,8 @@ cleanup: */ static bool sudo_load_plugin(struct plugin_container *policy_plugin, - struct plugin_container_list *io_plugins, struct plugin_info *info) + struct plugin_container_list *io_plugins, + struct plugin_container_list *audit_plugins, struct plugin_info *info) { struct plugin_container *container = NULL; struct generic_plugin *plugin; @@ -315,6 +316,20 @@ sudo_load_plugin(struct plugin_container *policy_plugin, goto done; TAILQ_INSERT_TAIL(io_plugins, container, entries); break; + case SUDO_AUDIT_PLUGIN: + if (plugin_exists(audit_plugins, info)) { + plugin = sudo_plugin_try_to_clone(handle, info->symbol_name); + if (plugin == NULL) { + sudo_warnx(U_("ignoring duplicate audit plugin \"%s\" in %s, line %d"), + info->symbol_name, _PATH_SUDO_CONF, info->lineno); + ret = true; + goto done; + } + } + if ((container = new_container(handle, path, plugin, info)) == NULL) + goto done; + TAILQ_INSERT_TAIL(audit_plugins, container, entries); + break; default: sudo_warnx(U_("error in %s, line %d while loading plugin \"%s\""), _PATH_SUDO_CONF, info->lineno, info->symbol_name); @@ -354,7 +369,8 @@ free_plugin_info(struct plugin_info *info) */ bool sudo_load_plugins(struct plugin_container *policy_plugin, - struct plugin_container_list *io_plugins) + struct plugin_container_list *io_plugins, + struct plugin_container_list *audit_plugins) { struct plugin_container *container; struct plugin_info_list *plugins; @@ -365,7 +381,7 @@ sudo_load_plugins(struct plugin_container *policy_plugin, /* Walk the plugin list from sudo.conf, if any and free it. */ plugins = sudo_conf_plugins(); TAILQ_FOREACH_SAFE(info, plugins, entries, next) { - ret = sudo_load_plugin(policy_plugin, io_plugins, info); + ret = sudo_load_plugin(policy_plugin, io_plugins, audit_plugins, info); if (!ret) goto done; free_plugin_info(info); @@ -391,7 +407,7 @@ sudo_load_plugins(struct plugin_container *policy_plugin, goto done; } /* info->options = NULL; */ - ret = sudo_load_plugin(policy_plugin, io_plugins, info); + ret = sudo_load_plugin(policy_plugin, io_plugins, audit_plugins, info); free_plugin_info(info); if (!ret) goto done; @@ -411,7 +427,7 @@ sudo_load_plugins(struct plugin_container *policy_plugin, goto done; } /* info->options = NULL; */ - ret = sudo_load_plugin(policy_plugin, io_plugins, info); + ret = sudo_load_plugin(policy_plugin, io_plugins, audit_plugins, info); free_plugin_info(info); if (!ret) goto done; diff --git a/src/parse_args.c b/src/parse_args.c index d1abed696..7c8baa0a4 100644 --- a/src/parse_args.c +++ b/src/parse_args.c @@ -240,7 +240,7 @@ parse_env_list(struct environment *e, char *list) * for the command to be run (if we are running one). */ int -parse_args(int argc, char **argv, int *nargc, char ***nargv, +parse_args(int argc, char **argv, int *old_optind, int *nargc, char ***nargv, struct sudo_settings **settingsp, char ***env_addp) { struct environment extra_env; @@ -501,6 +501,7 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv, argc -= optind; argv += optind; + *old_optind = optind; if (!mode) { /* Defer -k mode setting until we know whether it is a flag or not */ @@ -617,12 +618,29 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv, argc = ac; } + /* + * For sudoedit we need to rewrite argv + */ if (mode == MODE_EDIT) { #if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID) + char **av; + int ac; + + av = reallocarray(NULL, argc + 2, sizeof(char *)); + if (av == NULL) + sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + if (!gc_add(GC_PTR, av)) + exit(1); + /* Must have the command in argv[0]. */ - argc++; - argv--; - argv[0] = "sudoedit"; + av[0] = "sudoedit"; + for (ac = 0; argv[ac] != NULL; ac++) { + av[ac + 1] = argv[ac]; + } + av[++ac] = NULL; + + argv = av; + argc = ac; #else sudo_fatalx(U_("sudoedit is not supported on this platform")); #endif diff --git a/src/sudo.c b/src/sudo.c index 00560bb28..87f105737 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2009-2019 Todd C. Miller + * Copyright (c) 2009-2020 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -75,6 +75,7 @@ */ struct plugin_container policy_plugin; struct plugin_container_list io_plugins = TAILQ_HEAD_INITIALIZER(io_plugins); +struct plugin_container_list audit_plugins = TAILQ_HEAD_INITIALIZER(audit_plugins); struct user_details user_details; const char *list_user; /* extern for parse_args.c */ int sudo_debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER; @@ -106,15 +107,15 @@ static void command_info_to_details(char * const info[], static void gc_init(void); /* Policy plugin convenience functions. */ -static int policy_open(struct sudo_settings *settings, +static void policy_open(struct sudo_settings *settings, char * const user_info[], char * const user_env[]); static void policy_close(int exit_status, int error); static int policy_show_version(int verbose); -static int policy_check(int argc, char * const argv[], char *env_add[], +static void policy_check(int argc, char * const argv[], char *env_add[], char **command_info[], char **argv_out[], char **user_env_out[]); -static int policy_list(int argc, char * const argv[], int verbose, - const char *list_user); -static int policy_validate(void); +static void policy_list(int argc, char * const argv[], + int verbose, const char *list_user, char * const envp[]); +static void policy_validate(char * const argv[], char * const envp[]); static void policy_invalidate(int remove); /* I/O log plugin convenience functions. */ @@ -125,18 +126,28 @@ static void iolog_close(int exit_status, int error); static void iolog_show_version(int verbose, struct sudo_settings *settings, char * const user_info[], int argc, char * const argv[], char * const user_env[]); -static void iolog_unlink(struct plugin_container *plugin); +static void unlink_plugin(struct plugin_container_list *plugin_list, struct plugin_container *plugin); static void free_plugin_container(struct plugin_container *plugin, bool ioplugin); +/* Audit plugin convenience functions. */ +static void audit_open(struct sudo_settings *settings, char * const user_info[], + int submit_optind, char * const submit_argv[], char * const submit_envp[]); +static void audit_close(int exit_status, int error); +static void audit_show_version(int verbose); +static void audit_accept(const char *plugin_name, + unsigned int plugin_type, char * const command_info[], + char * const run_argv[], char * const run_envp[]); + __dso_public int main(int argc, char *argv[], char *envp[]); int main(int argc, char *argv[], char *envp[]) { - int nargc, ok, status = 0; - char **nargv, **env_add; - char **user_info, **command_info, **argv_out, **user_env_out; + int nargc, status = 0; + char **nargv, **env_add, **user_info; + char **command_info = NULL, **argv_out = NULL, **user_env_out = NULL; struct sudo_settings *settings; + int submit_optind; sigset_t mask; debug_decl_vars(main, SUDO_DEBUG_MAIN); @@ -194,7 +205,8 @@ main(int argc, char *argv[], char *envp[]) disable_coredump(); /* Parse command line arguments. */ - sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add); + sudo_mode = parse_args(argc, argv, &submit_optind, &nargc, &nargv, + &settings, &env_add); sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_mode %d", sudo_mode); /* Print sudo version early, in case of plugin init failure. */ @@ -208,54 +220,44 @@ main(int argc, char *argv[], char *envp[]) sudo_warn_set_conversation(sudo_conversation); /* Load plugins. */ - if (!sudo_load_plugins(&policy_plugin, &io_plugins)) + if (!sudo_load_plugins(&policy_plugin, &io_plugins, &audit_plugins)) sudo_fatalx(U_("fatal error, unable to load plugins")); /* Allocate event base so plugin can use it. */ if ((sudo_event_base = sudo_ev_base_alloc()) == NULL) sudo_fatalx("%s", U_("unable to allocate memory")); - /* Open policy plugin. */ - ok = policy_open(settings, user_info, envp); - if (ok != 1) { - if (ok == -2) - usage(1); - else - sudo_fatalx(U_("unable to initialize policy plugin")); - } + /* Open policy and audit plugins. */ + /* XXX - audit policy_open errors */ + audit_open(settings, user_info, submit_optind, argv, envp); + policy_open(settings, user_info, envp); switch (sudo_mode & MODE_MASK) { case MODE_VERSION: policy_show_version(!user_details.uid); iolog_show_version(!user_details.uid, settings, user_info, nargc, nargv, envp); + audit_show_version(!user_details.uid); break; case MODE_VALIDATE: case MODE_VALIDATE|MODE_INVALIDATE: - ok = policy_validate(); - exit(ok != 1); + policy_validate(nargv, envp); + break; case MODE_KILL: case MODE_INVALIDATE: policy_invalidate(sudo_mode == MODE_KILL); - exit(0); break; case MODE_CHECK: case MODE_CHECK|MODE_INVALIDATE: case MODE_LIST: case MODE_LIST|MODE_INVALIDATE: - ok = policy_list(nargc, nargv, ISSET(sudo_mode, MODE_LONG_LIST), - list_user); - exit(ok != 1); + policy_list(nargc, nargv, ISSET(sudo_mode, MODE_LONG_LIST), + list_user, envp); + break; case MODE_EDIT: case MODE_RUN: - ok = policy_check(nargc, nargv, env_add, &command_info, &argv_out, + policy_check(nargc, nargv, env_add, &command_info, &argv_out, &user_env_out); - sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d", ok); - if (ok != 1) { - if (ok == -2) - usage(1); - exit(EXIT_FAILURE); /* plugin printed error message */ - } /* Reset nargv/nargc based on argv_out. */ /* XXX - leaks old nargv in shell mode */ @@ -264,10 +266,14 @@ main(int argc, char *argv[], char *envp[]) if (nargc == 0) sudo_fatalx(U_("plugin did not return a command to execute")); - /* Open I/O plugins once policy plugin succeeds. */ + /* Open I/O plugin once policy plugin succeeds. */ iolog_open(settings, user_info, command_info, nargc, nargv, user_env_out); + /* Audit command we are going to run. */ + audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN, command_info, + nargv, user_env_out); + /* Setup command details and run command/edit. */ command_info_to_details(command_info, &command_details); command_details.tty = user_details.tty; @@ -941,6 +947,7 @@ run_command(struct command_details *details) /* exec_setup() or execve() returned an error. */ policy_close(0, cstat.val); iolog_close(0, cstat.val); + audit_close(0, cstat.val); break; case CMD_WSTATUS: /* Command ran, exited or was killed. */ @@ -951,6 +958,7 @@ run_command(struct command_details *details) #endif policy_close(status, 0); iolog_close(status, 0); + audit_close(status, 0); break; default: sudo_warnx(U_("unexpected child termination condition: %d"), cstat.type); @@ -1022,20 +1030,19 @@ bad: debug_return_ptr(NULL); } -static int +static void policy_open(struct sudo_settings *settings, char * const user_info[], char * const user_env[]) { char **plugin_settings; - int ret; + const char *errstr = NULL; + int ok; debug_decl(policy_open, SUDO_DEBUG_PCOMM); /* Convert struct sudo_settings to plugin_settings[] */ plugin_settings = format_plugin_settings(&policy_plugin, settings); - if (plugin_settings == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - debug_return_int(-1); - } + if (plugin_settings == NULL) + sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); /* * Backwards compatibility for older API versions @@ -1044,21 +1051,30 @@ policy_open(struct sudo_settings *settings, char * const user_info[], switch (policy_plugin.u.generic->version) { case SUDO_API_MKVERSION(1, 0): case SUDO_API_MKVERSION(1, 1): - ret = policy_plugin.u.policy_1_0->open(policy_plugin.u.io_1_0->version, + ok = policy_plugin.u.policy_1_0->open(policy_plugin.u.io_1_0->version, sudo_conversation_1_7, sudo_conversation_printf, plugin_settings, user_info, user_env); break; default: - ret = policy_plugin.u.policy->open(SUDO_API_VERSION, sudo_conversation, + ok = policy_plugin.u.policy->open(SUDO_API_VERSION, sudo_conversation, sudo_conversation_printf, plugin_settings, user_info, user_env, - policy_plugin.options); + policy_plugin.options, &errstr); } /* Stash plugin debug instance ID if set in open() function. */ policy_plugin.debug_instance = sudo_debug_get_active_instance(); sudo_debug_set_active_instance(sudo_debug_instance); - debug_return_int(ret); + if (ok != 1) { + if (ok == -2) + usage(1); + else { + /* XXX - audit */ + sudo_fatalx(U_("unable to initialize policy plugin")); + } + } + + debug_return; } static void @@ -1083,33 +1099,35 @@ policy_close(int exit_status, int error_code) errno = error_code; sudo_warn(U_("unable to execute %s"), command_details.command); } + debug_return; } static int policy_show_version(int verbose) { - int ret; + int ret = true; debug_decl(policy_show_version, SUDO_DEBUG_PCOMM); - if (policy_plugin.u.policy->show_version == NULL) - debug_return_int(true); sudo_debug_set_active_instance(policy_plugin.debug_instance); - ret = policy_plugin.u.policy->show_version(verbose); + if (policy_plugin.u.policy->show_version != NULL) + ret = policy_plugin.u.policy->show_version(verbose); if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) { if (policy_plugin.u.policy->close != NULL) policy_plugin.u.policy->close(0, 0); } sudo_debug_set_active_instance(sudo_debug_instance); + debug_return_int(ret); } -static int +static void policy_check(int argc, char * const argv[], char *env_add[], char **command_info[], char **argv_out[], char **user_env_out[]) { - int ret; + const char *errstr = NULL; + int ok; debug_decl(policy_check, SUDO_DEBUG_PCOMM); if (policy_plugin.u.policy->check_policy == NULL) { @@ -1117,65 +1135,132 @@ policy_check(int argc, char * const argv[], policy_plugin.name); } sudo_debug_set_active_instance(policy_plugin.debug_instance); - ret = policy_plugin.u.policy->check_policy(argc, argv, env_add, - command_info, argv_out, user_env_out); - /* On success, the close method will be called by sudo_edit/run_command. */ - if (ret != 1) { - if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) { - if (policy_plugin.u.policy->close != NULL) - policy_plugin.u.policy->close(0, 0); - } - } + ok = policy_plugin.u.policy->check_policy(argc, argv, env_add, + command_info, argv_out, user_env_out, &errstr); sudo_debug_set_active_instance(sudo_debug_instance); - debug_return_int(ret); + sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d (%s)", + ok, errstr ? errstr : ""); + + /* On success, the close method will be called by sudo_edit/run_command. */ + if (ok != 1) { + switch (ok) { + case 0: + audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN, + errstr ? errstr : _("command rejected by policy"), + *command_info); + break; + case -1: + audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN, + errstr ? errstr : _("policy plugin error"), + *command_info); + break; + case -2: + usage(1); + break; + } + + /* Policy must be closed after auditing to avoid use after free. */ + if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) + policy_close(0, 0); + exit(EXIT_FAILURE); /* policy plugin printed error message */ + } + + debug_return; } -static int -policy_list(int argc, char * const argv[], int verbose, const char *list_user) +static void +policy_list(int argc, char * const argv[], int verbose, + const char *list_user, char * const envp[]) { - int ret; + const char *errstr = NULL; + /* TODO: add list_user */ + char * const command_info[] = { + "command=list", + NULL + }; + int ok; debug_decl(policy_list, SUDO_DEBUG_PCOMM); if (policy_plugin.u.policy->list == NULL) { - sudo_warnx(U_("policy plugin %s does not support listing privileges"), + sudo_fatalx(U_("policy plugin %s does not support listing privileges"), policy_plugin.name); - debug_return_int(false); } sudo_debug_set_active_instance(policy_plugin.debug_instance); - ret = policy_plugin.u.policy->list(argc, argv, verbose, list_user); - if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) { - if (policy_plugin.u.policy->close != NULL) - policy_plugin.u.policy->close(0, 0); - } + ok = policy_plugin.u.policy->list(argc, argv, verbose, list_user, &errstr); sudo_debug_set_active_instance(sudo_debug_instance); - debug_return_int(ret); + + switch (ok) { + case 1: + audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN, + command_info, argv, envp); + break; + case 0: + audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN, + errstr ? errstr : _("command rejected by policy"), + command_info); + break; + default: + audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN, + errstr ? errstr : _("policy plugin error"), + command_info); + break; + } + + /* Policy must be closed after auditing to avoid use after free. */ + if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) + policy_close(0, 0); + + exit(ok != 1); } -static int -policy_validate(void) +static void +policy_validate(char * const argv[], char * const envp[]) { - int ret; + const char *errstr = NULL; + char * const command_info[] = { + "command=validate", + NULL + }; + int ok = 0; debug_decl(policy_validate, SUDO_DEBUG_PCOMM); if (policy_plugin.u.policy->validate == NULL) { - sudo_warnx(U_("policy plugin %s does not support the -v option"), + sudo_fatalx(U_("policy plugin %s does not support the -v option"), policy_plugin.name); - debug_return_int(false); } sudo_debug_set_active_instance(policy_plugin.debug_instance); - ret = policy_plugin.u.policy->validate(); - if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) { - if (policy_plugin.u.policy->close != NULL) - policy_plugin.u.policy->close(0, 0); - } + ok = policy_plugin.u.policy->validate(&errstr); sudo_debug_set_active_instance(sudo_debug_instance); - debug_return_int(ret); + + switch (ok) { + case 1: + audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN, command_info, + argv, envp); + break; + case 0: + audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN, + errstr ? errstr : _("command rejected by policy"), + command_info); + break; + default: + audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN, + errstr ? errstr : _("policy plugin error"), + command_info); + break; + } + + /* Policy must be closed after auditing to avoid use after free. */ + if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) + policy_close(0, 0); + + exit(ok != 1); } static void policy_invalidate(int remove) { debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM); + if (policy_plugin.u.policy->invalidate == NULL) { sudo_fatalx(U_("policy plugin %s does not support the -k/-K options"), policy_plugin.name); @@ -1187,12 +1272,14 @@ policy_invalidate(int remove) policy_plugin.u.policy->close(0, 0); } sudo_debug_set_active_instance(sudo_debug_instance); - debug_return; + + exit(EXIT_SUCCESS); } int policy_init_session(struct command_details *details) { + const char *errstr = NULL; int ret = true; debug_decl(policy_init_session, SUDO_DEBUG_PCOMM); @@ -1223,9 +1310,10 @@ policy_init_session(struct command_details *details) break; default: ret = policy_plugin.u.policy->init_session(details->pw, - &details->envp); + &details->envp, &errstr); } sudo_debug_set_active_instance(sudo_debug_instance); + /* TODO: audit on error */ } done: debug_return_int(ret); @@ -1234,7 +1322,7 @@ done: static int iolog_open_int(struct plugin_container *plugin, struct sudo_settings *settings, char * const user_info[], char * const command_info[], - int argc, char * const argv[], char * const user_env[]) + int argc, char * const argv[], char * const user_env[], const char **errstr) { char **plugin_settings; int ret; @@ -1265,7 +1353,7 @@ iolog_open_int(struct plugin_container *plugin, struct sudo_settings *settings, default: ret = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation, sudo_conversation_printf, plugin_settings, user_info, command_info, - argc, argv, user_env, plugin->options); + argc, argv, user_env, plugin->options, errstr); } /* Stash plugin debug instance ID if set in open() function. */ @@ -1281,22 +1369,26 @@ iolog_open(struct sudo_settings *settings, char * const user_info[], char * const user_env[]) { struct plugin_container *plugin, *next; + const char *errstr = NULL; debug_decl(iolog_open, SUDO_DEBUG_PCOMM); + /* XXX - iolog_open should audit errors */ TAILQ_FOREACH_SAFE(plugin, &io_plugins, entries, next) { int ok = iolog_open_int(plugin, settings, user_info, - command_info, argc, argv, user_env); + command_info, argc, argv, user_env, &errstr); switch (ok) { case 1: break; case 0: /* I/O plugin asked to be disabled, remove and free. */ - iolog_unlink(plugin); + /* XXX - audit */ + unlink_plugin(&io_plugins, plugin); break; case -2: usage(1); break; default: + /* XXX - audit error */ sudo_fatalx(U_("error initializing I/O plugin %s"), plugin->name); } @@ -1336,23 +1428,24 @@ iolog_show_version(int verbose, struct sudo_settings *settings, char * const user_info[], int argc, char * const argv[], char * const user_env[]) { + const char *errstr = NULL; struct plugin_container *plugin; debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM); TAILQ_FOREACH(plugin, &io_plugins, entries) { int ok = iolog_open_int(plugin, settings, user_info, NULL, - argc, argv, user_env); + argc, argv, user_env, &errstr); if (ok != -1) { + sudo_debug_set_active_instance(plugin->debug_instance); if (plugin->u.io->show_version != NULL) { - sudo_debug_set_active_instance(plugin->debug_instance); /* Return value of show_version currently ignored. */ plugin->u.io->show_version(verbose); - if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 15)) { - if (plugin->u.io->close != NULL) - plugin->u.io->close(0, 0); - } - sudo_debug_set_active_instance(sudo_debug_instance); } + if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 15)) { + if (plugin->u.io->close != NULL) + plugin->u.io->close(0, 0); + } + sudo_debug_set_active_instance(sudo_debug_instance); } } @@ -1360,26 +1453,234 @@ iolog_show_version(int verbose, struct sudo_settings *settings, } /* - * Remove the specified I/O logging plugin from the io_plugins list. + * Remove the specified plugin from the plugins list. * Deregisters any hooks before unlinking, then frees the container. */ static void -iolog_unlink(struct plugin_container *plugin) +unlink_plugin(struct plugin_container_list *plugin_list, + struct plugin_container *plugin) { - debug_decl(iolog_unlink, SUDO_DEBUG_PCOMM); + void (*deregister_hooks)(int , int (*)(struct sudo_hook *)) = NULL; + debug_decl(unlink_plugin, SUDO_DEBUG_PCOMM); /* Deregister hooks, if any. */ - if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 2)) { - if (plugin->u.io->deregister_hooks != NULL) { + if (plugin->u.generic->version >= SUDO_API_MKVERSION(1, 2)) { + switch (plugin->u.generic->type) { + case SUDO_IO_PLUGIN: + deregister_hooks = plugin->u.io->deregister_hooks; + break; + case SUDO_AUDIT_PLUGIN: + deregister_hooks = plugin->u.audit->deregister_hooks; + break; + default: + sudo_debug_printf(SUDO_DEBUG_ERROR, + "%s: unsupported plugin type %d", __func__, + plugin->u.generic->type); + break; + } + } + if (deregister_hooks != NULL) { + sudo_debug_set_active_instance(plugin->debug_instance); + deregister_hooks(SUDO_HOOK_VERSION, deregister_hook); + sudo_debug_set_active_instance(sudo_debug_instance); + } + + /* Remove from plugin list and free. */ + TAILQ_REMOVE(plugin_list, plugin, entries); + free_plugin_container(plugin, true); + + debug_return; +} + +static int +audit_open_int(struct plugin_container *plugin, struct sudo_settings *settings, + char * const user_info[], int submit_optind, char * const submit_argv[], + char * const submit_envp[], const char **errstr) +{ + char **plugin_settings; + int ret; + debug_decl(audit_open_int, SUDO_DEBUG_PCOMM); + + /* Convert struct sudo_settings to plugin_settings[] */ + plugin_settings = format_plugin_settings(plugin, settings); + if (plugin_settings == NULL) { + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + debug_return_int(-1); + } + + sudo_debug_set_active_instance(plugin->debug_instance); + ret = plugin->u.audit->open(SUDO_API_VERSION, sudo_conversation, + sudo_conversation_printf, plugin_settings, user_info, + submit_optind, submit_argv, submit_envp, plugin->options, errstr); + + /* Stash plugin debug instance ID if set in open() function. */ + plugin->debug_instance = sudo_debug_get_active_instance(); + sudo_debug_set_active_instance(sudo_debug_instance); + + debug_return_int(ret); +} + +static void +audit_open(struct sudo_settings *settings, char * const user_info[], + int submit_optind, char * const submit_argv[], char * const submit_envp[]) +{ + struct plugin_container *plugin, *next; + const char *errstr = NULL; + debug_decl(audit_open, SUDO_DEBUG_PCOMM); + + TAILQ_FOREACH_SAFE(plugin, &audit_plugins, entries, next) { + int ok = audit_open_int(plugin, settings, user_info, + submit_optind, submit_argv, submit_envp, &errstr); + switch (ok) { + case 1: + break; + case 0: + /* Audit plugin asked to be disabled, remove and free. */ + unlink_plugin(&audit_plugins, plugin); + break; + case -2: + usage(1); + break; + default: + /* TODO: pass error message to other audit plugins */ + sudo_fatalx(U_("error initializing audit plugin %s"), + plugin->name); + } + } + + debug_return; +} + +static void +audit_close(int exit_status, int error_code) +{ + struct plugin_container *plugin; + debug_decl(audit_close, SUDO_DEBUG_PCOMM); + + TAILQ_FOREACH(plugin, &audit_plugins, entries) { + if (plugin->u.audit->close != NULL) { sudo_debug_set_active_instance(plugin->debug_instance); - plugin->u.io->deregister_hooks(SUDO_HOOK_VERSION, - deregister_hook); + plugin->u.audit->close(exit_status, error_code); sudo_debug_set_active_instance(sudo_debug_instance); } } - /* Remove from io_plugins list and free. */ - TAILQ_REMOVE(&io_plugins, plugin, entries); - free_plugin_container(plugin, true); + + debug_return; +} + +static void +audit_show_version(int verbose) +{ + struct plugin_container *plugin; + debug_decl(audit_show_version, SUDO_DEBUG_PCOMM); + + TAILQ_FOREACH(plugin, &audit_plugins, entries) { + sudo_debug_set_active_instance(plugin->debug_instance); + if (plugin->u.audit->show_version != NULL) { + /* Return value of show_version currently ignored. */ + plugin->u.audit->show_version(verbose); + } + if (plugin->u.audit->close != NULL) + plugin->u.audit->close(0, 0); + sudo_debug_set_active_instance(sudo_debug_instance); + } + + debug_return; +} + +/* + * Command accepted by policy. + * See command_info[] for additional info. + * XXX - actual environment may be updated by policy_init_session(). + */ +static void +audit_accept(const char *plugin_name, unsigned int plugin_type, + char * const command_info[], char * const run_argv[], + char * const run_envp[]) +{ + struct plugin_container *plugin; + const char *errstr = NULL; + int ok; + debug_decl(audit_accept, SUDO_DEBUG_PCOMM); + + /* XXX - kill command if can't audit accept event */ + TAILQ_FOREACH(plugin, &audit_plugins, entries) { + if (plugin->u.audit->accept == NULL) + continue; + + sudo_debug_set_active_instance(plugin->debug_instance); + ok = plugin->u.audit->accept(plugin_name, plugin_type, + command_info, run_argv, run_envp, &errstr); + if (ok != 1) { + /* XXX - fatal error? log error with other audit modules? */ + sudo_debug_printf(SUDO_DEBUG_ERROR, + "%s: plugin %s accept failed, ret %d", __func__, + plugin->name, ok); + } + sudo_debug_set_active_instance(sudo_debug_instance); + } + + debug_return; +} + +/* + * Command rejected by policy or I/O plugin. + */ +void +audit_reject(const char *plugin_name, unsigned int plugin_type, + const char *audit_msg, char * const command_info[]) +{ + struct plugin_container *plugin; + const char *errstr = NULL; + int ok; + debug_decl(audit_reject, SUDO_DEBUG_PCOMM); + + TAILQ_FOREACH(plugin, &audit_plugins, entries) { + if (plugin->u.audit->reject == NULL) + continue; + + sudo_debug_set_active_instance(plugin->debug_instance); + ok = plugin->u.audit->reject(plugin_name, plugin_type, + audit_msg, command_info, &errstr); + if (ok != 1) { + /* TODO: notify other audit plugins of the error. */ + sudo_debug_printf(SUDO_DEBUG_ERROR, + "%s: plugin %s reject failed, ret %d", __func__, + plugin->name, ok); + } + sudo_debug_set_active_instance(sudo_debug_instance); + } + + debug_return; +} + +/* + * Error from policy or I/O plugin. + */ +void +audit_error(const char *plugin_name, unsigned int plugin_type, + const char *audit_msg, char * const command_info[]) +{ + struct plugin_container *plugin; + const char *errstr = NULL; + int ok; + debug_decl(audit_error, SUDO_DEBUG_PCOMM); + + TAILQ_FOREACH(plugin, &audit_plugins, entries) { + if (plugin->u.audit->error == NULL) + continue; + + sudo_debug_set_active_instance(plugin->debug_instance); + ok = plugin->u.audit->error(plugin_name, plugin_type, + audit_msg, command_info, &errstr); + if (ok != 1) { + /* TODO: notify other audit plugins of the error. */ + sudo_debug_printf(SUDO_DEBUG_ERROR, + "%s: plugin %s error failed, ret %d", __func__, + plugin->name, ok); + } + sudo_debug_set_active_instance(sudo_debug_instance); + } debug_return; } diff --git a/src/sudo.h b/src/sudo.h index 879612674..190f9e266 100644 --- a/src/sudo.h +++ b/src/sudo.h @@ -207,8 +207,8 @@ char *tgetpass(const char *prompt, int timeout, int flags, int sudo_execute(struct command_details *details, struct command_status *cstat); /* parse_args.c */ -int parse_args(int argc, char **argv, int *nargc, char ***nargv, - struct sudo_settings **settingsp, char ***env_addp); +int parse_args(int argc, char **argv, int *old_optind, int *nargc, + char ***nargv, struct sudo_settings **settingsp, char ***env_addp); extern int tgetpass_flags; /* get_pty.c */ @@ -221,6 +221,10 @@ int os_init_common(int argc, char *argv[], char *envp[]); bool gc_add(enum sudo_gc_types type, void *v); bool set_user_groups(struct command_details *details); struct sudo_plugin_event *sudo_plugin_event_alloc(void); +void audit_reject(const char *plugin_name, unsigned int plugin_type, + const char *audit_msg, char * const command_info[]); +void audit_error(const char *plugin_name, unsigned int plugin_type, + const char *audit_msg, char * const command_info[]); extern const char *list_user; extern struct user_details user_details; extern int sudo_debug_instance; diff --git a/src/sudo_plugin_int.h b/src/sudo_plugin_int.h index beda546cd..6eb7abb43 100644 --- a/src/sudo_plugin_int.h +++ b/src/sudo_plugin_int.h @@ -100,6 +100,7 @@ struct plugin_container { struct io_plugin *io; struct io_plugin_1_0 *io_1_0; struct io_plugin_1_1 *io_1_1; + struct audit_plugin *audit; } u; }; TAILQ_HEAD(plugin_container_list, plugin_container); @@ -117,6 +118,7 @@ struct sudo_plugin_event_int { extern struct plugin_container policy_plugin; extern struct plugin_container_list io_plugins; +extern struct plugin_container_list audit_plugins; int sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[], struct sudo_conv_reply replies[], struct sudo_conv_callback *callback); @@ -125,6 +127,7 @@ int sudo_conversation_1_7(int num_msgs, const struct sudo_conv_message msgs[], int sudo_conversation_printf(int msg_type, const char *fmt, ...); bool sudo_load_plugins(struct plugin_container *policy_plugin, - struct plugin_container_list *io_plugins); + struct plugin_container_list *io_plugins, + struct plugin_container_list *audit_plugins); #endif /* SUDO_PLUGIN_INT_H */