Add an approval plugin type that runs after the policy plugin.

The basic idea is that the approval plugin adds an additional
layer of policy.  There can be multiple approval plugins.
This commit is contained in:
Todd C. Miller
2020-02-06 12:49:11 -07:00
parent 084cad2120
commit db17cadaf6
6 changed files with 800 additions and 48 deletions

View File

@@ -1751,7 +1751,7 @@ If an output logging function rejects the data by returning 0, the
command will be terminated and the data will not be written to the
terminal, though it will still be sent to any other I/O logging plugins.
.PP
The io_plugin struct has the following fields:
The audit_plugin struct has the following fields:
.TP 6n
type
The
@@ -3103,6 +3103,321 @@ See the
\fIPolicy plugin API\fR
section for a description of
\fRderegister_hooks\fR.
.SS "Approval plugin API"
.nf
.RS 0n
struct approval_plugin {
#define SUDO_APPROVAL_PLUGIN 4
unsigned int type; /* always SUDO_APPROVAL_PLUGIN */
unsigned int version; /* always SUDO_API_VERSION */
int (*check)(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 command_info[], char * const run_argv[],
char * const run_envp[], char * const plugin_options[],
const char **errstr);
int (*show_version)(unsigned int version, sudo_conv_t conversation,
sudo_printf_t sudo_printf, int verbose);
};
.RE
.fi
.PP
An approval plugin can be used to apply extra constraints after a
command has been accepted by the policy plugin.
Unlike the other plugin types, there are no
\fBopen\fR()
or
\fBclose\fR()
functions; functions in an approval function are stand-alone.
Multiple approval plugins may be specified in
sudo.conf(@mansectform@).
.PP
The approval_plugin struct has the following fields:
.TP 6n
type
The
\fRtype\fR
field should always be set to
\fRSUDO_APPROVAL_PLUGIN\fR.
.TP 6n
version
The
\fRversion\fR
field should be set to
\fRSUDO_API_VERSION\fR.
.sp
This allows
\fBsudo\fR
to determine the API version the plugin was
built against.
.TP 6n
check
.br
.nf
.RS 6n
int (*check)(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 command_info[], char * const run_argv[],
char * const run_envp[], char * const plugin_options[],
const char **errstr);
.RE
.fi
.RS 6n
.sp
The approval
\fBcheck\fR()
function is run after the policy plugin
\fBcheck\fR()
function and before any I/O logging plugins.
If multiple approval plugins are loaded, they must all succeeed for
the command to be allowed.
It returns 1 on success, 0 on failure, \-1 if a general error occurred,
or \-2 if there was a usage error.
In the latter case,
\fBsudo\fR
will print a usage message before it exits.
If an error occurs, the plugin may optionally call the
\fBconversation\fR()
or
\fBplugin_printf\fR()
function with
\fRSUDO_CONF_ERROR_MSG\fR
to present additional error information to the user.
.sp
The function arguments are as follows:
.TP 6n
version
The version passed in by
\fBsudo\fR
allows the plugin to determine the
major and minor version number of the plugin API supported by
\fBsudo\fR.
.TP 6n
conversation
A pointer to the
\fBconversation\fR()
function that can be used by the plugin to interact with the user (see
\fIConversation API\fR
for details).
Returns 0 on success and \-1 on failure.
.TP 6n
plugin_printf
A pointer to a
\fBprintf\fR()-style
function that may be used to display informational or error messages (see
\fIConversation API\fR
for details).
Returns the number of characters printed on success and \-1 on failure.
.TP 6n
settings
A vector of user-supplied
\fBsudo\fR
settings in the form of
\(lqname=value\(rq
strings.
The vector is terminated by a
\fRNULL\fR
pointer.
These settings correspond to options the user specified when running
\fBsudo\fR.
As such, they will only be present when the corresponding option has
been specified on the command line.
.sp
When parsing
\fIsettings\fR,
the plugin should split on the
\fBfirst\fR
equal sign
(\(oq=\(cq)
since the
\fIname\fR
field will never include one
itself but the
\fIvalue\fR
might.
.sp
See the
\fIPolicy plugin API\fR
section for a list of all possible settings.
.TP 6n
user_info
A vector of information about the user running the command in the form of
\(lqname=value\(rq
strings.
The vector is terminated by a
\fRNULL\fR
pointer.
.sp
When parsing
\fIuser_info\fR,
the plugin should split on the
\fBfirst\fR
equal sign
(\(oq=\(cq)
since the
\fIname\fR
field will never include one
itself but the
\fIvalue\fR
might.
.sp
See the
\fIPolicy plugin API\fR
section for a list of all possible strings.
.TP 6n
submit_optind
The index into
\fIsubmit_argv\fR
that corresponds to the first entry that is not a command line option.
If
\fIsubmit_argv\fR
only consists of options, which may be the case with the
\fB\-l\fR
or
\fB\-v\fR
options,
\fRsubmit_argv[submit_optind]\fR
will evaluate to the NULL pointer.
.TP 6n
submit_argv
The argument vector
\fBsudo\fR
was invoked with, including all command line options.
The
\fIsubmit_optind\fR
argument can be used to determine the end of the command line options.
.TP 6n
submit_envp
The invoking user's environment in the form of a
\fRNULL\fR-terminated
vector of
\(lqname=value\(rq
strings.
.sp
When parsing
\fIsubmit_envp\fR,
the plugin should split on the
\fBfirst\fR
equal sign
(\(oq=\(cq)
since the
\fIname\fR
field will never include one
itself but the
\fIvalue\fR
might.
.TP 6n
command_info
A vector of information describing the command being run in the form of
\(lqname=value\(rq
strings.
The vector is terminated by a
\fRNULL\fR
pointer.
.sp
When parsing
\fIcommand_info\fR,
the plugin should split on the
\fBfirst\fR
equal sign
(\(oq=\(cq)
since the
\fIname\fR
field will never include one
itself but the
\fIvalue\fR
might.
.sp
See the
\fIPolicy plugin API\fR
section for a list of all possible strings.
.TP 6n
run_argv
A
\fRNULL\fR-terminated
argument vector describing a command that will be run in the
same form as what would be passed to the
execve(2)
system call.
.TP 6n
run_envp
The environment the command will be run with in the form of a
\fRNULL\fR-terminated
vector of
\(lqname=value\(rq
strings.
.sp
When parsing
\fIrun_envp\fR,
the plugin should split on the
\fBfirst\fR
equal sign
(\(oq=\(cq)
since the
\fIname\fR
field will never include one
itself but the
\fIvalue\fR
might.
.TP 6n
plugin_options
Any (non-comment) strings immediately after the plugin path are
treated 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
\fRNULL\fR-terminated
array of strings.
If no arguments were specified,
\fIplugin_options\fR
will be the
\fRNULL\fR
pointer.
.TP 6n
errstr
If the
\fBopen\fR()
function returns a value other than 1, the plugin may
store a message describing the failure or error in
\fIerrstr\fR.
The
\fBsudo\fR
front end will then pass this value to any registered audit plugins.
.PD 0
.PP
.RE
.PD
.TP 6n
show_version
.nf
.RS 6n
int (*show_version)(int verbose);
.RE
.fi
.RS 6n
.sp
The
\fBshow_version\fR()
function is called by
\fBsudo\fR
when the user specifies
the
\fB\-V\fR
option.
The plugin may display its version information to the user via the
\fBconversation\fR()
or
\fBplugin_printf\fR()
function using
\fRSUDO_CONV_INFO_MSG\fR.
If the user requests detailed version information, the verbose flag will be set.
.sp
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.
.RE
.SS "Signal handlers"
The
\fBsudo\fR
@@ -3403,8 +3718,7 @@ function.
Events are described by the following structure:
.nf
.RS 0n
typedef void (*sudo_plugin_ev_callback_t)(int fd, int what,
void *closure);
typedef void (*sudo_plugin_ev_callback_t)(int fd, int what, void *closure);
struct sudo_plugin_event {
int (*set)(struct sudo_plugin_event *pev, int fd, int events,
@@ -3590,7 +3904,8 @@ function returns 1 on success, and \-1 if a error occurred.
\fBpending\fR()
.nf
.RS 6n
int (*pending)(struct sudo_plugin_event *pev, int events, struct timespec *ts);
int (*pending)(struct sudo_plugin_event *pev, int events,
struct timespec *ts);
.RE
.fi
.RS 6n
@@ -3798,9 +4113,8 @@ function:
.sp
.RS 0n
typedef int (*sudo_conv_t)(int num_msgs,
const struct sudo_conv_message msgs[],
struct sudo_conv_reply replies[],
struct sudo_conv_callback *callback);
const struct sudo_conv_message msgs[],
struct sudo_conv_reply replies[], struct sudo_conv_callback *callback);
typedef int (*sudo_printf_t)(int msg_type, const char *fmt, ...);
.RE
@@ -3984,12 +4298,12 @@ initialization, cleanup and group lookup.
.sp
.RS 0n
struct sudoers_group_plugin {
unsigned int version;
int (*init)(int version, sudo_printf_t sudo_printf,
char *const argv[]);
void (*cleanup)(void);
int (*query)(const char *user, const char *group,
const struct passwd *pwd);
unsigned int version;
int (*init)(int version, sudo_printf_t sudo_printf,
char *const argv[]);
void (*cleanup)(void);
int (*query)(const char *user, const char *group,
const struct passwd *pwd);
};
.RE
.fi
@@ -4012,7 +4326,7 @@ init
.nf
.RS 6n
int (*init)(int version, sudo_printf_t plugin_printf,
char *const argv[]);
char *const argv[]);
.RE
.fi
.RS 6n
@@ -4029,8 +4343,7 @@ If an error occurs, the plugin may call the
\fBplugin_printf\fR()
function with
\fRSUDO_CONF_ERROR_MSG\fR
to present additional error information
to the user.
to present additional error information to the user.
.sp
The function arguments are as follows:
.TP 6n
@@ -4085,7 +4398,7 @@ query
.nf
.RS 6n
int (*query)(const char *user, const char *group,
const struct passwd *pwd);
const struct passwd *pwd);
.RE
.fi
.RS 6n
@@ -4340,6 +4653,8 @@ command was not run.
.sp
\fRSUDO_CONV_REPL_MAX\fR
has increased from 255 to 1023 bytes.
.sp
Support for audit and approval plugins was added.
.SH "SEE ALSO"
sudo.conf(@mansectform@),
sudoers(@mansectform@),

View File

@@ -1565,7 +1565,7 @@ If an output logging function rejects the data by returning 0, the
command will be terminated and the data will not be written to the
terminal, though it will still be sent to any other I/O logging plugins.
.Pp
The io_plugin struct has the following fields:
The audit_plugin struct has the following fields:
.Bl -tag -width 4n
.It type
The
@@ -2748,6 +2748,294 @@ See the
section for a description of
.Li deregister_hooks .
.El
.Ss Approval plugin API
.Bd -literal
struct approval_plugin {
#define SUDO_APPROVAL_PLUGIN 4
unsigned int type; /* always SUDO_APPROVAL_PLUGIN */
unsigned int version; /* always SUDO_API_VERSION */
int (*check)(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 command_info[], char * const run_argv[],
char * const run_envp[], char * const plugin_options[],
const char **errstr);
int (*show_version)(unsigned int version, sudo_conv_t conversation,
sudo_printf_t sudo_printf, int verbose);
};
.Ed
.Pp
An approval plugin can be used to apply extra constraints after a
command has been accepted by the policy plugin.
Unlike the other plugin types, there are no
.Fn open
or
.Fn close
functions; functions in an approval function are stand-alone.
Multiple approval plugins may be specified in
.Xr sudo.conf @mansectform@ .
.Pp
The approval_plugin struct has the following fields:
.Bl -tag -width 4n
.It type
The
.Li type
field should always be set to
.Dv SUDO_APPROVAL_PLUGIN .
.It version
The
.Li version
field should be set to
.Dv SUDO_API_VERSION .
.Pp
This allows
.Nm sudo
to determine the API version the plugin was
built against.
.It check
.Bd -literal -compact
int (*check)(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 command_info[], char * const run_argv[],
char * const run_envp[], char * const plugin_options[],
const char **errstr);
.Ed
.Pp
The approval
.Fn check
function is run after the policy plugin
.Fn check
function and before any I/O logging plugins.
If multiple approval plugins are loaded, they must all succeeed for
the command to be allowed.
It returns 1 on success, 0 on failure, \-1 if a general error occurred,
or \-2 if there was a usage error.
In the latter case,
.Nm sudo
will print a usage message before it exits.
If an error occurs, the plugin may optionally call the
.Fn conversation
or
.Fn plugin_printf
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 version
The version passed in by
.Nm sudo
allows the plugin to determine the
major and minor version number of the plugin API supported by
.Nm sudo .
.It conversation
A pointer to the
.Fn conversation
function that can be used by the plugin to interact with the user (see
.Sx Conversation API
for details).
Returns 0 on success and \-1 on failure.
.It plugin_printf
A pointer to a
.Fn printf Ns -style
function that may be used to display informational or error messages (see
.Sx Conversation API
for details).
Returns the number of characters printed on success and \-1 on failure.
.It settings
A vector of user-supplied
.Nm sudo
settings in the form of
.Dq name=value
strings.
The vector is terminated by a
.Dv NULL
pointer.
These settings correspond to options the user specified when running
.Nm sudo .
As such, they will only be present when the corresponding option has
been specified on the command line.
.Pp
When parsing
.Em settings ,
the plugin should split on the
.Sy first
equal sign
.Pq Ql =
since the
.Em name
field will never include one
itself but the
.Em value
might.
.Pp
See the
.Sx Policy plugin API
section for a list of all possible settings.
.It user_info
A vector of information about the user running the command in the form of
.Dq name=value
strings.
The vector is terminated by a
.Dv NULL
pointer.
.Pp
When parsing
.Em user_info ,
the plugin should split on the
.Sy first
equal sign
.Pq Ql =
since the
.Em name
field will never include one
itself but the
.Em value
might.
.Pp
See the
.Sx Policy plugin API
section for a list of all possible strings.
.It submit_optind
The index into
.Fa submit_argv
that corresponds to the first entry that is not a command line option.
If
.Fa submit_argv
only consists of options, which may be the case with the
.Fl l
or
.Fl v
options,
.Li submit_argv[submit_optind]
will evaluate to the NULL pointer.
.It submit_argv
The argument vector
.Nm sudo
was invoked with, including all command line options.
The
.Fa submit_optind
argument can be used to determine the end of the command line options.
.It submit_envp
The invoking user's environment in the form of a
.Dv NULL Ns -terminated
vector of
.Dq name=value
strings.
.Pp
When parsing
.Em submit_envp ,
the plugin should split on the
.Sy first
equal sign
.Pq Ql =
since the
.Em name
field will never include one
itself but the
.Em value
might.
.It command_info
A vector of information describing the command being run in the form of
.Dq name=value
strings.
The vector is terminated by a
.Dv NULL
pointer.
.Pp
When parsing
.Em command_info ,
the plugin should split on the
.Sy first
equal sign
.Pq Ql =
since the
.Em name
field will never include one
itself but the
.Em value
might.
.Pp
See the
.Sx Policy plugin API
section for a list of all possible strings.
.It run_argv
A
.Dv NULL Ns -terminated
argument vector describing a command that will be run in the
same form as what would be passed to the
.Xr execve 2
system call.
.It run_envp
The environment the command will be run with in the form of a
.Dv NULL Ns -terminated
vector of
.Dq name=value
strings.
.Pp
When parsing
.Em run_envp ,
the plugin should split on the
.Sy first
equal sign
.Pq Ql =
since the
.Em name
field will never include one
itself but the
.Em value
might.
.It plugin_options
Any (non-comment) strings immediately after the plugin path are
treated 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.
.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.
.El
.It show_version
.Bd -literal -compact
int (*show_version)(int verbose);
.Ed
.Pp
The
.Fn show_version
function is called by
.Nm sudo
when the user specifies
the
.Fl V
option.
The plugin may display its version information to the user via the
.Fn conversation
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.
.El
.Ss Signal handlers
The
.Nm sudo
@@ -2993,8 +3281,7 @@ function.
Events are described by the following structure:
.Pp
.Bd -literal -compact
typedef void (*sudo_plugin_ev_callback_t)(int fd, int what,
void *closure);
typedef void (*sudo_plugin_ev_callback_t)(int fd, int what, void *closure);
struct sudo_plugin_event {
int (*set)(struct sudo_plugin_event *pev, int fd, int events,
@@ -3141,7 +3428,8 @@ The
function returns 1 on success, and \-1 if a error occurred.
.It Fn pending
.Bd -literal -compact
int (*pending)(struct sudo_plugin_event *pev, int events, struct timespec *ts);
int (*pending)(struct sudo_plugin_event *pev, int events,
struct timespec *ts);
.Ed
.Pp
The
@@ -3322,9 +3610,8 @@ The following type definitions can be used in the declaration of the
function:
.Bd -literal
typedef int (*sudo_conv_t)(int num_msgs,
const struct sudo_conv_message msgs[],
struct sudo_conv_reply replies[],
struct sudo_conv_callback *callback);
const struct sudo_conv_message msgs[],
struct sudo_conv_reply replies[], struct sudo_conv_callback *callback);
typedef int (*sudo_printf_t)(int msg_type, const char *fmt, ...);
.Ed
@@ -3502,12 +3789,12 @@ This structure contains pointers to the functions that implement plugin
initialization, cleanup and group lookup.
.Bd -literal
struct sudoers_group_plugin {
unsigned int version;
int (*init)(int version, sudo_printf_t sudo_printf,
char *const argv[]);
void (*cleanup)(void);
int (*query)(const char *user, const char *group,
const struct passwd *pwd);
unsigned int version;
int (*init)(int version, sudo_printf_t sudo_printf,
char *const argv[]);
void (*cleanup)(void);
int (*query)(const char *user, const char *group,
const struct passwd *pwd);
};
.Ed
.Pp
@@ -3527,7 +3814,7 @@ was built against.
.It init
.Bd -literal -compact
int (*init)(int version, sudo_printf_t plugin_printf,
char *const argv[]);
char *const argv[]);
.Ed
.Pp
The
@@ -3542,8 +3829,7 @@ If an error occurs, the plugin may call the
.Fn plugin_printf
function with
.Dv SUDO_CONF_ERROR_MSG
to present additional error information
to the user.
to present additional error information to the user.
.Pp
The function arguments are as follows:
.Bl -tag -width 4n
@@ -3585,7 +3871,7 @@ The plugin should free any memory it has allocated and close open file handles.
.It query
.Bd -literal -compact
int (*query)(const char *user, const char *group,
const struct passwd *pwd);
const struct passwd *pwd);
.Ed
.Pp
The
@@ -3815,6 +4101,8 @@ command was not run.
.Pp
.Dv SUDO_CONV_REPL_MAX
has increased from 255 to 1023 bytes.
.Pp
Support for audit and approval plugins was added.
.El
.Sh SEE ALSO
.Xr sudo.conf @mansectform@ ,

View File

@@ -237,6 +237,22 @@ struct audit_plugin {
void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook));
};
/* Approval plugin type and defines */
struct approval_plugin {
#define SUDO_APPROVAL_PLUGIN 4
unsigned int type; /* always SUDO_APPROVAL_PLUGIN */
unsigned int version; /* always SUDO_API_VERSION */
int (*check)(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 command_info[], char * const run_argv[],
char * const run_envp[], char * const plugin_options[],
const char **errstr);
int (*show_version)(unsigned int version, sudo_conv_t conversation,
sudo_printf_t sudo_printf, int verbose);
};
/* Sudoers group plugin version major/minor */
#define GROUP_API_VERSION_MAJOR 1
#define GROUP_API_VERSION_MINOR 0

View File

@@ -243,7 +243,9 @@ cleanup:
static bool
sudo_load_plugin(struct plugin_container *policy_plugin,
struct plugin_container_list *io_plugins,
struct plugin_container_list *audit_plugins, struct plugin_info *info)
struct plugin_container_list *audit_plugins,
struct plugin_container_list *approval_plugins,
struct plugin_info *info)
{
struct plugin_container *container = NULL;
struct generic_plugin *plugin;
@@ -330,6 +332,20 @@ sudo_load_plugin(struct plugin_container *policy_plugin,
goto done;
TAILQ_INSERT_TAIL(audit_plugins, container, entries);
break;
case SUDO_APPROVAL_PLUGIN:
if (plugin_exists(approval_plugins, info)) {
plugin = sudo_plugin_try_to_clone(handle, info->symbol_name);
if (plugin == NULL) {
sudo_warnx(U_("ignoring duplicate approval 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(approval_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);
@@ -370,7 +386,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 *audit_plugins)
struct plugin_container_list *audit_plugins,
struct plugin_container_list *approval_plugins)
{
struct plugin_container *container;
struct plugin_info_list *plugins;
@@ -381,7 +398,8 @@ 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, audit_plugins, info);
ret = sudo_load_plugin(policy_plugin, io_plugins, audit_plugins,
approval_plugins, info);
if (!ret)
goto done;
free_plugin_info(info);
@@ -407,7 +425,8 @@ sudo_load_plugins(struct plugin_container *policy_plugin,
goto done;
}
/* info->options = NULL; */
ret = sudo_load_plugin(policy_plugin, io_plugins, audit_plugins, info);
ret = sudo_load_plugin(policy_plugin, io_plugins, audit_plugins,
approval_plugins, info);
free_plugin_info(info);
if (!ret)
goto done;
@@ -427,7 +446,8 @@ sudo_load_plugins(struct plugin_container *policy_plugin,
goto done;
}
/* info->options = NULL; */
ret = sudo_load_plugin(policy_plugin, io_plugins, audit_plugins, info);
ret = sudo_load_plugin(policy_plugin, io_plugins, audit_plugins,
approval_plugins, info);
free_plugin_info(info);
if (!ret)
goto done;

View File

@@ -76,6 +76,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 plugin_container_list approval_plugins = TAILQ_HEAD_INITIALIZER(approval_plugins);
struct user_details user_details;
const char *list_user; /* extern for parse_args.c */
int sudo_debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
@@ -138,6 +139,13 @@ static void audit_accept(const char *plugin_name,
unsigned int plugin_type, char * const command_info[],
char * const run_argv[], char * const run_envp[]);
/* Approval plugin convenience functions. */
static void approval_check(struct sudo_settings *settings,
char * const user_info[], int submit_optind, char * const submit_argv[],
char * const submit_envp[], char * const command_info[],
char * const run_argv[], char * const run_envp[]);
static void approval_show_version(int verbose);
__dso_public int main(int argc, char *argv[], char *envp[]);
int
@@ -220,7 +228,8 @@ main(int argc, char *argv[], char *envp[])
sudo_warn_set_conversation(sudo_conversation);
/* Load plugins. */
if (!sudo_load_plugins(&policy_plugin, &io_plugins, &audit_plugins))
if (!sudo_load_plugins(&policy_plugin, &io_plugins, &audit_plugins,
&approval_plugins))
sudo_fatalx(U_("fatal error, unable to load plugins"));
/* Allocate event base so plugin can use it. */
@@ -237,6 +246,7 @@ main(int argc, char *argv[], char *envp[])
policy_show_version(!user_details.uid);
iolog_show_version(!user_details.uid, settings, user_info,
nargc, nargv, envp);
approval_show_version(!user_details.uid);
audit_show_version(!user_details.uid);
break;
case MODE_VALIDATE:
@@ -266,14 +276,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 plugin once policy plugin succeeds. */
/* Approval plugins run after policy plugin accepts the command. */
approval_check(settings, user_info, submit_optind, argv, envp,
command_info, nargv, user_env_out);
/* Open I/O plugin once policy and approval plugins succeed. */
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;
@@ -958,7 +968,6 @@ run_command(struct command_details *details)
#endif
policy_close(status, 0);
iolog_close(status, 0);
audit_close(SUDO_PLUGIN_WAIT_STATUS, status);
break;
default:
/* TODO: handle front end error conditions. */
@@ -1166,6 +1175,8 @@ policy_check(int argc, char * const argv[],
audit_close(SUDO_PLUGIN_NO_STATUS, 0);
exit(EXIT_FAILURE); /* policy plugin printed error message */
}
audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN, *command_info,
*argv_out, *user_env_out);
debug_return;
}
@@ -1691,6 +1702,105 @@ audit_error(const char *plugin_name, unsigned int plugin_type,
debug_return;
}
static int
approval_check_int(struct plugin_container *plugin,
struct sudo_settings *settings, char * const user_info[],
int submit_optind, char * const submit_argv[], char * const submit_envp[],
char * const command_info[], char * const run_argv[],
char * const run_envp[], const char **errstr)
{
char **plugin_settings;
int ret;
debug_decl(approval_check_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.approval->check(SUDO_API_VERSION, sudo_conversation,
sudo_conversation_printf, plugin_settings, user_info,
submit_optind, submit_argv, submit_envp, command_info, run_argv,
run_envp, plugin->options, errstr);
sudo_debug_set_active_instance(sudo_debug_instance);
sudo_debug_printf(SUDO_DEBUG_INFO, "approval plugin %s returns %d (%s)",
plugin->name, ret, *errstr ? *errstr : "");
debug_return_int(ret);
}
/*
* Run approval checks (there may be more than one).
* This is a "one-shot" plugin that has no open/close and is only
* called if the policy plugin accepts the command first.
*/
static void
approval_check(struct sudo_settings *settings, char * const user_info[],
int submit_optind, char * const submit_argv[], char * const submit_envp[],
char * const command_info[], char * const run_argv[],
char * const run_envp[])
{
struct plugin_container *plugin, *next;
const char *errstr = NULL;
debug_decl(approval_check, SUDO_DEBUG_PCOMM);
TAILQ_FOREACH_SAFE(plugin, &approval_plugins, entries, next) {
int ok = approval_check_int(plugin, settings, user_info,
submit_optind, submit_argv, submit_envp, command_info, run_argv,
run_envp, &errstr);
switch (ok) {
case 0:
audit_reject(plugin->name, SUDO_APPROVAL_PLUGIN,
errstr ? errstr : _("command rejected by approver"),
command_info);
break;
case 1:
audit_accept(plugin->name, SUDO_APPROVAL_PLUGIN, command_info,
run_argv, run_envp);
continue;
case -1:
audit_error(plugin->name, SUDO_APPROVAL_PLUGIN,
errstr ? errstr : _("approval plugin error"),
command_info);
break;
case -2:
usage(1);
break;
}
if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15))
policy_close(0, EPERM);
audit_close(SUDO_PLUGIN_NO_STATUS, 0);
exit(EXIT_FAILURE); /* approval plugin printed error message */
}
debug_return;
}
static void
approval_show_version(int verbose)
{
struct plugin_container *plugin;
debug_decl(approval_show_version, SUDO_DEBUG_PCOMM);
TAILQ_FOREACH(plugin, &approval_plugins, entries) {
if (plugin->u.approval->show_version != NULL) {
/* Return value of show_version currently ignored. */
sudo_debug_set_active_instance(plugin->debug_instance);
plugin->u.approval->show_version(SUDO_API_VERSION,
sudo_conversation, sudo_conversation_printf, verbose);
sudo_debug_set_active_instance(sudo_debug_instance);
}
}
debug_return;
}
static void
plugin_event_callback(int fd, int what, void *v)
{

View File

@@ -1,7 +1,7 @@
/*
* SPDX-License-Identifier: ISC
*
* Copyright (c) 2010-2014 Todd C. Miller <Todd.Miller@sudo.ws>
* Copyright (c) 2010-2020 Todd C. Miller <Todd.Miller@sudo.ws>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -101,6 +101,7 @@ struct plugin_container {
struct io_plugin_1_0 *io_1_0;
struct io_plugin_1_1 *io_1_1;
struct audit_plugin *audit;
struct approval_plugin *approval;
} u;
};
TAILQ_HEAD(plugin_container_list, plugin_container);
@@ -119,6 +120,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;
extern struct plugin_container_list approval_plugins;
int sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[],
struct sudo_conv_reply replies[], struct sudo_conv_callback *callback);
@@ -128,6 +130,7 @@ 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 *audit_plugins);
struct plugin_container_list *audit_plugins,
struct plugin_container_list *approval_plugins);
#endif /* SUDO_PLUGIN_INT_H */