Copyright (c) 2009-2012 Todd C. Miller <Todd.Miller@courtesan.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=pod
=head1 NAME
sudo_plugin - Sudo Plugin API
=head1 DESCRIPTION
Starting with version 1.8, B<sudo> supports a plugin API
for policy and session logging. By default, the I<sudoers> policy
plugin and an associated I/O logging plugin are used. Via the plugin
API, B<sudo> can be configured to use alternate policy and/or I/O
logging plugins provided by third parties. The plugins to be used
are specified via the F<@sysconfdir@/sudo.conf> file.
The API is versioned with a major and minor number. The minor
version number is incremented when additions are made. The major
number is incremented when incompatible changes are made. A plugin
should be check the version passed to it and make sure that the
major version matches.
The plugin API is defined by the C<sudo_plugin.h> header file.
=head2 The sudo.conf File
The F<@sysconfdir@/sudo.conf> file contains plugin configuration directives.
Currently, the only supported keyword is the C<Plugin> directive,
which causes a plugin plugin to be loaded.
A C<Plugin> line consists of the C<Plugin> keyword, followed by the
I<symbol_name> and the I<path> to the shared object containing the
plugin. The I<symbol_name> is the name of the C<struct policy_plugin>
or C<struct io_plugin> in the plugin shared object. The I<path>
may be fully qualified or relative. If not fully qualified it is
relative to the F<@prefix@/libexec> directory. Any additional
parameters after the I<path> are passed as options to the plugin's
I<open> function. Lines that don't begin with C<Plugin>, C<Path>,
C<Debug> or C<Set> are silently ignored.
The same shared object may contain multiple plugins, each with a
different symbol name. The shared object file must be owned by uid
0 and only writable by its owner. Because of ambiguities that arise
from composite policies, only a single policy plugin may be specified.
This limitation does not apply to I/O plugins.
#
# Default @sysconfdir@/sudo.conf file
#
# Format:
# Plugin plugin_name plugin_path plugin_options ...
# Path askpass /path/to/askpass
# Path noexec /path/to/sudo_noexec.so
# Debug sudo /var/log/sudo_debug all@warn
# Set disable_coredump true
#
# The plugin_path is relative to @prefix@/libexec unless
# fully qualified.
# The plugin_name corresponds to a global symbol in the plugin
# that contains the plugin interface structure.
# The plugin_options are optional.
#
Plugin sudoers_policy sudoers.so
Plugin sudoers_io sudoers.so
=head2 Policy Plugin API
A policy plugin must declare and populate a C<policy_plugin> struct
in the global scope. This structure contains pointers to the functions
that implement the B<sudo> policy checks. The name of the symbol should
be specified in F<@sysconfdir@/sudo.conf> along with a path to the plugin
so that B<sudo> can load it.
struct policy_plugin {
#define SUDO_POLICY_PLUGIN 1
unsigned int type; /* always SUDO_POLICY_PLUGIN */
unsigned int version; /* always SUDO_API_VERSION */
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[]);
void (*close)(int exit_status, int 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[]);
int (*list)(int argc, char * const argv[], int verbose,
const char *list_user);
int (*validate)(void);
void (*invalidate)(int remove);
int (*init_session)(struct passwd *pwd, char **user_env[]);
void (*register_hooks)(int version,
int (*register_hook)(struct sudo_hook *hook));
void (*deregister_hooks)(int version,
int (*deregister_hook)(struct sudo_hook *hook));
};
The policy_plugin struct has the following fields:
=over 4
=item type
The C<type> field should always be set to SUDO_POLICY_PLUGIN.
=item version
The C<version> field should be set to SUDO_API_VERSION.
This allows B<sudo> to determine the API version the plugin was
built against.
=item open
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[]);
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, B<sudo> will
print a usage message before it exits. If an error occurs, the
plugin may optionally call the conversation or plugin_printf function
with C<SUDO_CONF_ERROR_MSG> to present additional error information
to the user.
The function arguments are as follows:
=over 4
=item version
The version passed in by B<sudo> allows the plugin to determine the
major and minor version number of the plugin API supported by
B<sudo>.
=item conversation
A pointer to the conversation function that can be used by the
plugin to interact with the user (see below).
Returns 0 on success and -1 on failure.
=item plugin_printf
A pointer to a printf-style function that may be used to display
informational or error messages (see below).
Returns the number of characters printed on success and -1 on failure.
=item settings
A vector of user-supplied B<sudo> settings in the form of "name=value"
strings. The vector is terminated by a C<NULL> pointer. These
settings correspond to flags the user specified when running B<sudo>.
As such, they will only be present when the corresponding flag has
been specified on the command line.
When parsing I<settings>, the plugin should split on the B<first>
equal sign ('=') since the I<name> field will never include one
itself but the I<value> might.
=over 4
=item debug_flags=string
A comma-separated list of debug flags that correspond to B<sudo>'s
C<Debug> entry in F<@sysconfdir@/sudo.conf>, if there is one. The
flags are passed to the plugin as they appear in F<@sysconfdir@/sudo.conf>.
The syntax used by B<sudo> and the I<sudoers> plugin is
I<subsystem>@I<priority> but the plugin is free to use a different
format so long as it does not include a command C<,>.
For reference, the priorities supported by the B<sudo> front end and
I<sudoers> are: I<crit>, I<err>, I<warn>, I<notice>, I<diag>,
I<info>, I<trace> and I<debug>.
The following subsystems are defined: I<main>, I<memory>, I<args>,
I<exec>, I<pty>, I<utmp>, I<conv>, I<pcomm>, I<util>, I<list>,
I<netif>, I<audit>, I<edit>, I<selinux>, I<ldap>, I<match>, I<parser>,
I<alias>, I<defaults>, I<auth>, I<env>, I<logging>, I<nss>, I<rbtree>,
I<perms>, I<plugin>. The subsystem I<all> includes every subsystem.
There is not currently a way to specify a set of debug flags specific
to the plugin--the flags are shared by B<sudo> and the plugin.
=item debug_level=number
This setting has been deprecated in favor of I<debug_flags>.
=item runas_user=string
The user name or uid to to run the command as, if specified via the
C<-u> flag.
=item runas_group=string
The group name or gid to to run the command as, if specified via
the C<-g> flag.
=item prompt=string
The prompt to use when requesting a password, if specified via
the C<-p> flag.
=item set_home=bool
Set to true if the user specified the C<-H> flag. If true, set the
C<HOME> environment variable to the target user's home directory.
=item preserve_environment=bool
Set to true if the user specified the C<-E> flag, indicating that
the user wishes to preserve the environment.
=item run_shell=bool
Set to true if the user specified the C<-s> flag, indicating that
the user wishes to run a shell.
=item login_shell=bool
Set to true if the user specified the C<-i> flag, indicating that
the user wishes to run a login shell.
=item implied_shell=bool
If the user does not specify a program on the command line, B<sudo>
will pass the plugin the path to the user's shell and set
I<implied_shell> to true. This allows B<sudo> with no arguments
to be used similarly to L<su(1)>. If the plugin does not to support
this usage, it may return a value of -2 from the C<check_policy>
function, which will cause B<sudo> to print a usage message and
exit.
=item preserve_groups=bool
Set to true if the user specified the C<-P> flag, indicating that
the user wishes to preserve the group vector instead of setting it
based on the runas user.
=item ignore_ticket=bool
Set to true if the user specified the C<-k> flag along with a
command, indicating that the user wishes to ignore any cached
authentication credentials.
=item noninteractive=bool
Set to true if the user specified the C<-n> flag, indicating that
B<sudo> should operate in non-interactive mode. The plugin may
reject a command run in non-interactive mode if user interaction
is required.
=item login_class=string
BSD login class to use when setting resource limits and nice value,
if specified by the C<-c> flag.
=item selinux_role=string
SELinux role to use when executing the command, if specified by
the C<-r> flag.
=item selinux_type=string
SELinux type to use when executing the command, if specified by
the C<-t> flag.
=item bsdauth_type=string
Authentication type, if specified by the C<-a> flag, to use on
systems where BSD authentication is supported.
=item network_addrs=list
A space-separated list of IP network addresses and netmasks in the
form "addr/netmask", e.g. "192.168.1.2/255.255.255.0". The address
and netmask pairs may be either IPv4 or IPv6, depending on what the
operating system supports. If the address contains a colon (':'),
it is an IPv6 address, else it is IPv4.
=item progname=string
The command name that sudo was run as, typically "sudo" or "sudoedit".
=item sudoedit=bool
Set to true when the C<-e> flag is is specified or if invoked as
B<sudoedit>. The plugin shall substitute an editor into I<argv>
in the I<check_policy> function or return C<-2> with a usage error
if the plugin does not support I<sudoedit>. For more information,
see the I<check_policy> section.
=item closefrom=number
If specified, the user has requested via the C<-C> flag that B<sudo>
close all files descriptors with a value of I<number> or higher.
The plugin may optionally pass this, or another value, back in the
I<command_info> list.
=back
Additional settings may be added in the future so the plugin should
silently ignore settings that it does not recognize.
=item user_info
A vector of information about the user running the command in the form of
"name=value" strings. The vector is terminated by a C<NULL> pointer.
When parsing I<user_info>, the plugin should split on the B<first>
equal sign ('=') since the I<name> field will never include one
itself but the I<value> might.
=over 4
=item pid=int
The process ID of the running B<sudo> process.
Only available starting with API version 1.2
=item ppid=int
The parent process ID of the running B<sudo> process.
Only available starting with API version 1.2
=item sid=int
The session ID of the running B<sudo> process or 0 if B<sudo> is
not part of a POSIX job control session.
Only available starting with API version 1.2
=item pgid=int
The ID of the process group that the running B<sudo> process belongs
to.
Only available starting with API version 1.2
=item tcpgid=int
The ID of the forground process group associated with the terminal
device associcated with the B<sudo> process or -1 if there is no
terminal present.
Only available starting with API version 1.2
=item user=string
The name of the user invoking B<sudo>.
=item euid=uid_t
The effective user ID of the user invoking B<sudo>.
=item uid=uid_t
The real user ID of the user invoking B<sudo>.
=item egid=gid_t
The effective group ID of the user invoking B<sudo>.
=item gid=gid_t
The real group ID of the user invoking B<sudo>.
=item groups=list
The user's supplementary group list formatted as a string of
comma-separated group IDs.
=item cwd=string
The user's current working directory.
=item tty=string
The path to the user's terminal device. If the user has no terminal
device associated with the session, the value will be empty, as in
C<tty=>.
=item host=string
The local machine's hostname as returned by the C<gethostname()>
system call.
=item lines=int
The number of lines the user's terminal supports. If there is
no terminal device available, a default value of 24 is used.
=item cols=int
The number of columns the user's terminal supports. If there is
no terminal device available, a default value of 80 is used.
=back
=item user_env
The user's environment in the form of a C<NULL>-terminated vector of
"name=value" strings.
When parsing I<user_env>, the plugin should split on the B<first>
equal sign ('=') since the I<name> field will never include one
itself but the I<value> might.
=item 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 C<NULL>-terminated array of strings. If no arguments were
specified, I<plugin_options> will be the NULL pointer.
NOTE: the I<plugin_options> parameter is only available starting with
API version 1.2. A plugin B<must> check the API version specified
by the B<sudo> front end before using I<plugin_options>. Failure to
do so may result in a crash.
=back
=item close
void (*close)(int exit_status, int error);
The C<close> function is called when the command being run by B<sudo>
finishes.
The function arguments are as follows:
=over 4
=item exit_status
The command's exit status, as returned by the wait(2) system call.
The value of C<exit_status> is undefined if C<error> is non-zero.
=item error
If the command could not be executed, this is set to the value of
C<errno> set by the execve(2) system call. The plugin is responsible
for displaying error information via the conversation or plugin_printf
function. If the command was successfully executed, the value of
C<error> is 0.
=back
=item show_version
int (*show_version)(int verbose);
The C<show_version> function is called by B<sudo> when the user specifies
the C<-V> option. The plugin may display its version information
to the user via the conversation or plugin_printf function using
C<SUDO_CONV_INFO_MSG>. If the user requests detailed version
information, the verbose flag will be set.
=item check_policy
int (*check_policy)(int argc, char * const argv[]
char *env_add[], char **command_info[],
char **argv_out[], char **user_env_out[]);
The I<check_policy> function is called by B<sudo> to determine
whether the user is allowed to run the specified commands.
If the I<sudoedit> option was enabled in the I<settings> array
passed to the I<open> function, the user has requested I<sudoedit>
mode. I<sudoedit> is a mechanism for editing one or more files
where an editor is run with the user's credentials instead of with
elevated privileges. B<sudo> achieves this by creating user-writable
temporary copies of the files to be edited and then overwriting the
originals with the temporary copies after editing is complete. If
the plugin supports B<sudoedit>, it should choose the editor to be
used, potentially from a variable in the user's environment, such
as C<EDITOR>, and include it in I<argv_out> (note that environment
variables may include command line flags). The files to be edited
should be copied from I<argv> into I<argv_out>, separated from the
editor and its arguments by a C<"--"> element. The C<"--"> will
be removed by B<sudo> before the editor is executed. The plugin
should also set I<sudoedit=true> in the I<command_info> list.
The I<check_policy> function returns 1 if the command is allowed,
0 if not allowed, -1 for a general error, or -2 for a usage error
or if B<sudoedit> was specified but is unsupported by the plugin.
In the latter case, B<sudo> will print a usage message before it
exits. If an error occurs, the plugin may optionally call the
conversation or plugin_printf function with C<SUDO_CONF_ERROR_MSG>
to present additional error information to the user.
The function arguments are as follows:
=over 4
=item argc
The number of elements in I<argv>, not counting the final C<NULL>
pointer.
=item argv
The argument vector describing the command the user wishes to run,
in the same form as what would be passed to the execve() system
call. The vector is terminated by a C<NULL> pointer.
=item env_add
Additional environment variables specified by the user on the command
line in the form of a C<NULL>-terminated vector of "name=value"
strings. The plugin may reject the command if one or more variables
are not allowed to be set, or it may silently ignore such variables.
When parsing I<env_add>, the plugin should split on the B<first>
equal sign ('=') since the I<name> field will never include one
itself but the I<value> might.
=item command_info
Information about the command being run in the form of "name=value"
strings. These values are used by B<sudo> to set the execution
environment when running a command. The plugin is responsible for
creating and populating the vector, which must be terminated with
a C<NULL> pointer. The following values are recognized by B<sudo>:
=over 4
=item command=string
Fully qualified path to the command to be executed.
=item runas_uid=uid
User ID to run the command as.
=item runas_euid=uid
Effective user ID to run the command as.
If not specified, the value of I<runas_uid> is used.
=item runas_gid=gid
Group ID to run the command as.
=item runas_egid=gid
Effective group ID to run the command as.
If not specified, the value of I<runas_gid> is used.
=item runas_groups=list
The supplementary group vector to use for the command in the form
of a comma-separated list of group IDs. If I<preserve_groups>
is set, this option is ignored.
=item login_class=string
BSD login class to use when setting resource limits and nice value
(optional). This option is only set on systems that support login
classes.
=item preserve_groups=bool
If set, B<sudo> will preserve the user's group vector instead of
initializing the group vector based on C<runas_user>.
=item cwd=string
The current working directory to change to when executing the command.
=item noexec=bool
If set, prevent the command from executing other programs.
=item chroot=string
The root directory to use when running the command.
=item nice=int
Nice value (priority) to use when executing the command. The nice
value, if specified, overrides the priority associated with the
I<login_class> on BSD systems.
=item umask=octal
The file creation mask to use when executing the command.
=item selinux_role=string
SELinux role to use when executing the command.
=item selinux_type=string
SELinux type to use when executing the command.
=item timeout=int
Command timeout. If non-zero then when the timeout expires the
command will be killed.
=item sudoedit=bool
Set to true when in I<sudoedit> mode. The plugin may enable
I<sudoedit> mode even if B<sudo> was not invoked as B<sudoedit>.
This allows the plugin to perform command substitution and transparently
enable I<sudoedit> when the user attempts to run an editor.
=item closefrom=number
If specified, B<sudo> will close all files descriptors with a value
of I<number> or higher.
=item iolog_compress=bool
Set to true if the I/O logging plugins, if any, should compress the
log data. This is a hint to the I/O logging plugin which may choose
to ignore it.
=item iolog_path=string
Fully qualified path to the file or directory in which I/O log is
to be stored. This is a hint to the I/O logging plugin which may
choose to ignore it. If no I/O logging plugin is loaded, this
setting has no effect.
=item iolog_stdin=bool
Set to true if the I/O logging plugins, if any, should log the
standard input if it is not connected to a terminal device. This
is a hint to the I/O logging plugin which may choose to ignore it.
=item iolog_stdout=bool
Set to true if the I/O logging plugins, if any, should log the
standard output if it is not connected to a terminal device. This
is a hint to the I/O logging plugin which may choose to ignore it.
=item iolog_stderr=bool
Set to true if the I/O logging plugins, if any, should log the
standard error if it is not connected to a terminal device. This
is a hint to the I/O logging plugin which may choose to ignore it.
=item iolog_ttyin=bool
Set to true if the I/O logging plugins, if any, should log all
terminal input. This only includes input typed by the user and not
from a pipe or redirected from a file. This is a hint to the I/O
logging plugin which may choose to ignore it.
=item iolog_ttyout=bool
Set to true if the I/O logging plugins, if any, should log all
terminal output. This only includes output to the screen, not
output to a pipe or file. This is a hint to the I/O logging plugin
which may choose to ignore it.
=item use_pty=bool
Allocate a pseudo-tty to run the command in, regardless of whether
or not I/O logging is in use. By default, B<sudo> will only run
the command in a pty when an I/O log plugin is loaded.
=item set_utmp=bool
Create a utmp (or utmpx) entry when a pseudo-tty is allocated. By
default, the new entry will be a copy of the user's existing utmp
entry (if any), with the tty, time, type and pid fields updated.
=item utmp_user=string
User name to use when constructing a new utmp (or utmpx) entry when
I<set_utmp> is enabled. This option can be used to set the user
field in the utmp entry to the user the command runs as rather than
the invoking user. If not set, B<sudo> will base the new entry on
the invoking user's existing entry.
=back
Unsupported values will be ignored.
=item argv_out
The C<NULL>-terminated argument vector to pass to the execve()
system call when executing the command. The plugin is responsible
for allocating and populating the vector.
=item user_env_out
The C<NULL>-terminated environment vector to use when executing the
command. The plugin is responsible for allocating and populating
the vector.
=back
=item list
int (*list)(int verbose, const char *list_user,
int argc, char * const argv[]);
List available privileges for the invoking user. Returns 1 on
success, 0 on failure and -1 on error. On error, the plugin may
optionally call the conversation or plugin_printf function with
C<SUDO_CONF_ERROR_MSG> to present additional error information to
the user.
Privileges should be output via the conversation or plugin_printf
function using C<SUDO_CONV_INFO_MSG>.
=over 4
=item verbose
Flag indicating whether to list in verbose mode or not.
=item list_user
The name of a different user to list privileges for if the policy
allows it. If C<NULL>, the plugin should list the privileges of
the invoking user.
=item argc
The number of elements in I<argv>, not counting the final C<NULL>
pointer.
=item argv
If non-C<NULL>, an argument vector describing a command the user
wishes to check against the policy in the same form as what would
be passed to the execve() system call. If the command is permitted
by the policy, the fully-qualified path to the command should be
displayed along with any command line arguments.
=back
=item validate
int (*validate)(void);
The C<validate> function is called when B<sudo> is run with the
C<-v> flag. For policy plugins such as I<sudoers> that cache
authentication credentials, this function will validate and cache
the credentials.
The C<validate> function should be C<NULL> if the plugin does not
support credential caching.
Returns 1 on success, 0 on failure and -1 on error.
On error, the plugin may optionally call the conversation or plugin_printf
function with C<SUDO_CONF_ERROR_MSG> to present additional
error information to the user.
=item invalidate
void (*invalidate)(int remove);
The C<invalidate> function is called when B<sudo> is called with
the C<-k> or C<-K> flag. For policy plugins such as I<sudoers> that
cache authentication credentials, this function will invalidate the
credentials. If the I<remove> flag is set, the plugin may remove
the credentials instead of simply invalidating them.
The C<invalidate> function should be C<NULL> if the plugin does not
support credential caching.
=item init_session
int (*init_session)(struct passwd *pwd, char **user_envp[);
The C<init_session> function is called before B<sudo> sets up the
execution environment for the command. It is run in the parent
B<sudo> process and before any uid or gid changes. This can be used
to perform session setup that is not supported by I<command_info>,
such as opening the PAM session. The C<close> function can be
used to tear down the session that was opened by C<init_session>.
The I<pwd> argument points to a passwd struct for the user the
command will be run as if the uid the command will run as was found
in the password database, otherwise it will be NULL.
The I<user_env> argument points to the environment the command will
run in, in the form of a C<NULL>-terminated vector of "name=value"
strings. This is the same string passed back to the front end via
the Policy Plugin's I<user_env_out> parameter. If the C<init_session>
function needs to modify the user environment, it should update the
pointer stored in I<user_env>. The expected use case is to merge
the contents of the PAM environment (if any) with the contents of
I<user_env>. NOTE: the I<user_env> parameter is only available
starting with API version 1.2. A plugin B<must> check the API
version specified by the B<sudo> front end before using I<user_env>.
Failure to do so may result in a crash.
Returns 1 on success, 0 on failure and -1 on error.
On error, the plugin may optionally call the conversation or plugin_printf
function with C<SUDO_CONF_ERROR_MSG> to present additional
error information to the user.
=item register_hooks
void (*register_hooks)(int version,
int (*register_hook)(struct sudo_hook *hook));
The C<register_hooks> function is called by the sudo front end to
register any hooks the plugin needs. If the plugin does not support
hooks, C<register_hooks> should be set to the NULL pointer.
The I<version> argument describes the version of the hooks API
supported by the B<sudo> front end.
The C<register_hook> function should be used to register any supported
hooks the plugin needs. It returns 0 on success, 1 if the hook
type is not supported and -1 if the major version in C<struct hook>
does not match the front end's major hook API version.
See the L<Hook Function API> section below for more information
about hooks.
NOTE: the C<register_hooks> function is only available starting
with API version 1.2. If the B<sudo> front end doesn't support API
version 1.2 or higher, C<register_hooks> will not be called.
=item deregister_hooks
void (*deregister_hooks)(int version,
int (*deregister_hook)(struct sudo_hook *hook));
The C<deregister_hooks> function is called by the sudo front end
to deregister any hooks the plugin has registered. If the plugin
does not support hooks, C<deregister_hooks> should be set to the
NULL pointer.
The I<version> argument describes the version of the hooks API
supported by the B<sudo> front end.
The C<deregister_hook> function should be used to deregister any
hooks that were put in place by the C<register_hook> function. If
the plugin tries to deregister a hook that the front end does not
support, C<deregister_hook> will return an error.
See the L<Hook Function API> section below for more information
about hooks.
NOTE: the C<deregister_hooks> function is only available starting
with API version 1.2. If the B<sudo> front end doesn't support API
version 1.2 or higher, C<deregister_hooks> will not be called.
=back
=head3 Policy Plugin Version Macros
/* Plugin API version major/minor. */
#define SUDO_API_VERSION_MAJOR 1
#define SUDO_API_VERSION_MINOR 2
#define SUDO_API_MKVERSION(x, y) ((x << 16) | y)
#define SUDO_API_VERSION SUDO_API_MKVERSION(SUDO_API_VERSION_MAJOR,\
SUDO_API_VERSION_MINOR)
/* Getters and setters for API version */
#define SUDO_API_VERSION_GET_MAJOR(v) ((v) >> 16)
#define SUDO_API_VERSION_GET_MINOR(v) ((v) & 0xffff)
#define SUDO_API_VERSION_SET_MAJOR(vp, n) do { \
*(vp) = (*(vp) & 0x0000ffff) | ((n) << 16); \
} while(0)
#define SUDO_VERSION_SET_MINOR(vp, n) do { \
*(vp) = (*(vp) & 0xffff0000) | (n); \
} while(0)
=head2 I/O Plugin API
struct io_plugin {
#define SUDO_IO_PLUGIN 2
unsigned int type; /* always SUDO_IO_PLUGIN */
unsigned int version; /* always SUDO_API_VERSION */
int (*open)(unsigned int version, sudo_conv_t conversation
sudo_printf_t plugin_printf, char * const settings[],
char * const user_info[], int argc, char * const argv[],
char * const user_env[], char * const plugin_options[]);
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);
void (*register_hooks)(int version,
int (*register_hook)(struct sudo_hook *hook));
void (*deregister_hooks)(int version,
int (*deregister_hook)(struct sudo_hook *hook));
};
When an I/O plugin is loaded, B<sudo> runs the command in a pseudo-tty.
This makes it possible to log the input and output from the user's
session. If any of the standard input, standard output or standard
error do not correspond to a tty, B<sudo> will open a pipe to capture
the I/O for logging before passing it on.
The log_ttyin function receives the raw user input from the terminal
device (note that this will include input even when echo is disabled,
such as when a password is read). The log_ttyout function receives
output from the pseudo-tty that is suitable for replaying the user's
session at a later time. The log_stdin, log_stdout and log_stderr
functions are only called if the standard input, standard output
or standard error respectively correspond to something other than
a tty.
Any of the logging functions may be set to the NULL
pointer if no logging is to be performed. If the open function
returns C<0>, no I/O will be sent to the plugin.
The io_plugin struct has the following fields:
=over 4
=item type
The C<type> field should always be set to SUDO_IO_PLUGIN
=item version
The C<version> field should be set to SUDO_API_VERSION.
This allows B<sudo> to determine the API version the plugin was
built against.
=item open
int (*open)(unsigned int version, sudo_conv_t conversation
sudo_printf_t plugin_printf, char * const settings[],
char * const user_info[], int argc, char * const argv[],
char * const user_env[], char * const plugin_options[]);
The I<open> function is run before the I<log_input>, I<log_output>
or I<show_version> functions are called. It is only called if the
version is being requested or the I<check_policy> function has
returned successfully. 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, B<sudo> will print a usage message before it exits.
If an error occurs, the plugin may optionally call the conversation
or plugin_printf function with C<SUDO_CONF_ERROR_MSG> to present
additional error information to the user.
The function arguments are as follows:
=over 4
=item version
The version passed in by B<sudo> allows the plugin to determine the
major and minor version number of the plugin API supported by
B<sudo>.
=item conversation
A pointer to the conversation function that may be used by the
I<show_version> function to display version information (see
show_version below). The conversation function may also be used
to display additional error message to the user.
The conversation function returns 0 on success and -1 on failure.
=item plugin_printf
A pointer to a printf-style function that may be used by the
I<show_version> function to display version information (see
show_version below). The plugin_printf function may also be used
to display additional error message to the user.
The plugin_printf function returns number of characters printed on
success and -1 on failure.
=item settings
A vector of user-supplied B<sudo> settings in the form of "name=value"
strings. The vector is terminated by a C<NULL> pointer. These
settings correspond to flags the user specified when running B<sudo>.
As such, they will only be present when the corresponding flag has
been specified on the command line.
When parsing I<settings>, the plugin should split on the B<first>
equal sign ('=') since the I<name> field will never include one
itself but the I<value> might.
See the L<Policy Plugin API> section for a list of all possible settings.
=item user_info
A vector of information about the user running the command in the form of
"name=value" strings. The vector is terminated by a C<NULL> pointer.
When parsing I<user_info>, the plugin should split on the B<first>
equal sign ('=') since the I<name> field will never include one
itself but the I<value> might.
See the L<Policy Plugin API> section for a list of all possible strings.
=item argc
The number of elements in I<argv>, not counting the final C<NULL>
pointer.
=item argv
If non-C<NULL>, an argument vector describing a command the user
wishes to run in the same form as what would be passed to the
execve() system call.
=item user_env
The user's environment in the form of a C<NULL>-terminated vector of
"name=value" strings.
When parsing I<user_env>, the plugin should split on the B<first>
equal sign ('=') since the I<name> field will never include one
itself but the I<value> might.
=item 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 C<NULL>-terminated array of strings. If no arguments were
specified, I<plugin_options> will be the NULL pointer.
NOTE: the I<plugin_options> parameter is only available starting with
API version 1.2. A plugin B<must> check the API version specified
by the B<sudo> front end before using I<plugin_options>. Failure to
do so may result in a crash.
=back
=item close
void (*close)(int exit_status, int error);
The C<close> function is called when the command being run by B<sudo>
finishes.
The function arguments are as follows:
=over 4
=item exit_status
The command's exit status, as returned by the wait(2) system call.
The value of C<exit_status> is undefined if C<error> is non-zero.
=item error
If the command could not be executed, this is set to the value of
C<errno> set by the execve(2) system call. If the command was
successfully executed, the value of C<error> is 0.
=back
=item show_version
int (*show_version)(int verbose);
The C<show_version> function is called by B<sudo> when the user specifies
the C<-V> option. The plugin may display its version information
to the user via the conversation or plugin_printf function using
C<SUDO_CONV_INFO_MSG>. If the user requests detailed version
information, the verbose flag will be set.
=item log_ttyin
int (*log_ttyin)(const char *buf, unsigned int len);
The I<log_ttyin> function is called whenever data can be read from
the user but before it is passed to the running command. This
allows the plugin to reject data if it chooses to (for instance
if the input contains banned content). Returns C<1> if the data
should be passed to the command, C<0> if the data is rejected
(which will terminate the command) or C<-1> if an error occurred.
The function arguments are as follows:
=over 4
=item buf
The buffer containing user input.
=item len
The length of I<buf> in bytes.
=back
=item log_ttyout
int (*log_ttyout)(const char *buf, unsigned int len);
The I<log_ttyout> function is called whenever data can be read from
the command but before it is written to the user's terminal. This
allows the plugin to reject data if it chooses to (for instance
if the output contains banned content). Returns C<1> if the data
should be passed to the user, C<0> if the data is rejected
(which will terminate the command) or C<-1> if an error occurred.
The function arguments are as follows:
=over 4
=item buf
The buffer containing command output.
=item len
The length of I<buf> in bytes.
=back
=item log_stdin
int (*log_stdin)(const char *buf, unsigned int len);
The I<log_stdin> function is only used if the standard input does
not correspond to a tty device. It is called whenever data can be
read from the standard input but before it is passed to the running
command. This allows the plugin to reject data if it chooses to
(for instance if the input contains banned content). Returns C<1>
if the data should be passed to the command, C<0> if the data is
rejected (which will terminate the command) or C<-1> if an error
occurred.
The function arguments are as follows:
=over 4
=item buf
The buffer containing user input.
=item len
The length of I<buf> in bytes.
=back
=item log_stdout
int (*log_stdout)(const char *buf, unsigned int len);
The I<log_stdout> function is only used if the standard output does
not correspond to a tty device. It is called whenever data can be
read from the command but before it is written to the standard
output. This allows the plugin to reject data if it chooses to
(for instance if the output contains banned content). Returns C<1>
if the data should be passed to the user, C<0> if the data is
rejected (which will terminate the command) or C<-1> if an error
occurred.
The function arguments are as follows:
=over 4
=item buf
The buffer containing command output.
=item len
The length of I<buf> in bytes.
=back
=item log_stderr
int (*log_stderr)(const char *buf, unsigned int len);
The I<log_stderr> function is only used if the standard error does
not correspond to a tty device. It is called whenever data can be
read from the command but before it is written to the standard
error. This allows the plugin to reject data if it chooses to
(for instance if the output contains banned content). Returns C<1>
if the data should be passed to the user, C<0> if the data is
rejected (which will terminate the command) or C<-1> if an error
occurred.
The function arguments are as follows:
=over 4
=item buf
The buffer containing command output.
=item len
The length of I<buf> in bytes.
=back
=item register_hooks
See the L<Policy Plugin API> section for a description of
C<register_hooks>.
=item deregister_hooks
See the L<Policy Plugin API> section for a description of
C<deregister_hooks>.
=back
=head3 I/O Plugin Version Macros
Same as for the L<Policy Plugin API>.
=head2 Hook Function API
Beginning with plugin API version 1.2, it is possible to install
hooks for certain functions called by the B<sudo> front end.
Currently, the only supported hooks relate to the handling of
environment variables. Hooks can be used to intercept attempts to
get, set, or remove environment variables so that these changes can
be reflected in the version of the environment that is used to
execute a command. A future version of the API will support
hooking internal B<sudo> front end functions as well.
=head3 Hook structure
Hooks in B<sudo> are described by the following structure:
typedef int (*sudo_hook_fn_t)();
struct sudo_hook {
int hook_version;
int hook_type;
sudo_hook_fn_t hook_fn;
void *closure;
};
The C<sudo_hook> structure has the following fields:
=over 4
=item hook_version
The C<hook_version> field should be set to SUDO_HOOK_VERSION.
=item hook_type
The C<hook_type> field may be one of the following supported hook types:
=over 4
=item SUDO_HOOK_SETENV
The C library C<setenv()> function. Any registered hooks will run
before the C library implementation. The C<hook_fn> field should
be a function that matches the following typedef:
typedef int (*sudo_hook_fn_setenv_t)(const char *name,
const char *value, int overwrite, void *closure);
If the registered hook does not match the typedef the results are
unspecified.
=item SUDO_HOOK_UNSETENV
The C library C<unsetenv()> function. Any registered hooks will run
before the C library implementation. The C<hook_fn> field should
be a function that matches the following typedef:
typedef int (*sudo_hook_fn_unsetenv_t)(const char *name,
void *closure);
=item SUDO_HOOK_GETENV
The C library C<getenv()> function. Any registered hooks will run
before the C library implementation. The C<hook_fn> field should
be a function that matches the following typedef:
typedef int (*sudo_hook_fn_getenv_t)(const char *name,
char **value, void *closure);
If the registered hook does not match the typedef the results are
unspecified.
=item SUDO_HOOK_PUTENV
The C library C<putenv()> function. Any registered hooks will run
before the C library implementation. The C<hook_fn> field should
be a function that matches the following typedef:
typedef int (*sudo_hook_fn_putenv_t)(char *string,
void *closure);
If the registered hook does not match the typedef the results are
unspecified.
=back
=item hook_fn
sudo_hook_fn_t hook_fn;
The C<hook_fn> field should be set to the plugin's hook implementation.
The actual function arguments will vary depending on the C<hook_type>
(see C<hook_type> above). In all cases, the C<closure> field of
C<struct sudo_hook> is passed as the last function parameter. This
can be used to pass arbitrary data to the plugin's hook implementation.
The function return value may be one of the following:
=over 4
=item SUDO_HOOK_RET_ERROR
The hook function encountered an error.
=item SUDO_HOOK_RET_NEXT
The hook completed without error, go on to the next hook (including
the native implementation if applicable). For example, a C<getenv>
hook might return C<SUDO_HOOK_RET_NEXT> if the specified variable
was not found in the private copy of the environment.
=item SUDO_HOOK_RET_STOP
The hook completed without error, stop processing hooks for this
invocation. This can be used to replace the native implementation.
For example, a C<setenv> hook that operates on a private copy of
the environment but leaves C<environ> unchanged.
=back
=back
Note that it is very easy to create an infinite loop when hooking
C library functions. For example, a C<getenv> hook that calls the
C<snprintf> function may create a loop if the C<snprintf> implementation
calls C<getenv> to check the locale. To prevent this, you may wish
to use a static variable in the hook function to guard against
nested calls. E.g.
static int in_progress = 0; /* avoid recursion */
if (in_progress)
return SUDO_HOOK_RET_NEXT;
in_progress = 1;
...
in_progress = 0;
return SUDO_HOOK_RET_STOP;
=head3 Hook API Version Macros
/* Hook API version major/minor */
#define SUDO_HOOK_VERSION_MAJOR 1
#define SUDO_HOOK_VERSION_MINOR 0
#define SUDO_HOOK_MKVERSION(x, y) ((x << 16) | y)
#define SUDO_HOOK_VERSION SUDO_HOOK_MKVERSION(SUDO_HOOK_VERSION_MAJOR,\
SUDO_HOOK_VERSION_MINOR)
/* Getters and setters for hook API version */
#define SUDO_HOOK_VERSION_GET_MAJOR(v) ((v) >> 16)
#define SUDO_HOOK_VERSION_GET_MINOR(v) ((v) & 0xffff)
#define SUDO_HOOK_VERSION_SET_MAJOR(vp, n) do { \
*(vp) = (*(vp) & 0x0000ffff) | ((n) << 16); \
} while(0)
#define SUDO_HOOK_VERSION_SET_MINOR(vp, n) do { \
*(vp) = (*(vp) & 0xffff0000) | (n); \
} while(0)
=head2 Conversation API
If the plugin needs to interact with the user, it may do so via the
conversation function. A plugin should not attempt to read directly
from the standard input or the user's tty (neither of which are
guaranteed to exist). The caller must include a trailing newline
in C<msg> if one is to be printed.
A printf-style function is also available that can be used to display
informational or error messages to the user, which is usually more
convenient for simple messages where no use input is required.
struct sudo_conv_message {
#define SUDO_CONV_PROMPT_ECHO_OFF 0x0001 /* do not echo user input */
#define SUDO_CONV_PROMPT_ECHO_ON 0x0002 /* echo user input */
#define SUDO_CONV_ERROR_MSG 0x0003 /* error message */
#define SUDO_CONV_INFO_MSG 0x0004 /* informational message */
#define SUDO_CONV_PROMPT_MASK 0x0005 /* mask user input */
#define SUDO_CONV_DEBUG_MSG 0x0006 /* debugging message */
#define SUDO_CONV_PROMPT_ECHO_OK 0x1000 /* flag: allow echo if no tty */
int msg_type;
int timeout;
const char *msg;
};
struct sudo_conv_reply {
char *reply;
};
typedef int (*sudo_conv_t)(int num_msgs,
const struct sudo_conv_message msgs[],
struct sudo_conv_reply replies[]);
typedef int (*sudo_printf_t)(int msg_type, const char *fmt, ...);
Pointers to the conversation and printf-style functions are passed
in to the plugin's C<open> function when the plugin is initialized.
To use the conversation function, the plugin must pass an array of
C<sudo_conv_message> and C<sudo_conv_reply> structures. There must
be a C<struct sudo_conv_message> and C<struct sudo_conv_reply> for
each message in the conversation. The plugin is responsible for
freeing the reply buffer filled in to the C<struct sudo_conv_reply>,
if any.
The printf-style function uses the same underlying mechanism as the
conversation function but only supports C<SUDO_CONV_INFO_MSG>,
C<SUDO_CONV_ERROR_MSG> and C<SUDO_CONV_DEBUG_MSG> for the I<msg_type>
parameter. It can be more convenient than using the conversation
function if no user reply is needed and supports standard printf()
escape sequences.
Unlike, C<SUDO_CONV_INFO_MSG> and C<SUDO_CONV_ERROR_MSG>, messages
sent with the <SUDO_CONV_DEBUG_MSG> I<msg_type> are not directly
user-visible. Instead, they are logged to the file specified in
the C<Debug> statement (if any) in the F<@sysconfdir@/sudo.conf>
file. This allows a plugin to log debugging information and is
intended to be used in conjunction with the I<debug_flags> setting.
See the sample plugin for an example of the conversation function usage.
=head2 Sudoers Group Plugin API
The I<sudoers> module supports a plugin interface to allow non-Unix
group lookups. This can be used to query a group source other than
the standard Unix group database. A sample group plugin is bundled
with B<sudo> that implements file-based lookups. Third party group
plugins include a QAS AD plugin available from Quest Software.
A group plugin must declare and populate a C<sudoers_group_plugin>
struct in the global scope. This structure contains pointers to
the functions that implement plugin initialization, cleanup and
group lookup.
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);
};
The C<sudoers_group_plugin> struct has the following fields:
=over 4
=item version
The C<version> field should be set to GROUP_API_VERSION.
This allows I<sudoers> to determine the API version the group plugin
was built against.
=item init
int (*init)(int version, sudo_printf_t plugin_printf,
char *const argv[]);
The I<init> function is called after I<sudoers> has been parsed but
before any policy checks. It returns 1 on success, 0 on failure
(or if the plugin is not configured), and -1 if a error occurred.
If an error occurs, the plugin may call the plugin_printf function
with C<SUDO_CONF_ERROR_MSG> to present additional error information
to the user.
The function arguments are as follows:
=over 4
=item version
The version passed in by I<sudoers> allows the plugin to determine the
major and minor version number of the group plugin API supported by
I<sudoers>.
=item plugin_printf
A pointer to a printf-style function that may be used to display
informational or error message to the user.
Returns the number of characters printed on success and -1 on failure.
=item argv
A NULL-terminated array of arguments generated from the I<group_plugin>
option in I<sudoers>. If no arguments were given, I<argv> will be
NULL.
=back
=item cleanup
void (*cleanup)();
The I<cleanup> function is called when I<sudoers> has finished its
group checks. The plugin should free any memory it has allocated
and close open file handles.
=item query
int (*query)(const char *user, const char *group,
const struct passwd *pwd);
The I<query> function is used to ask the group plugin whether I<user>
is a member of I<group>.
The function arguments are as follows:
=over 4
=item user
The name of the user being looked up in the external group database.
=item group
The name of the group being queried.
=item pwd
The password database entry for I<user>, if any. If I<user> is not
present in the password database, I<pwd> will be C<NULL>.
=back
=back
=head3 Group API Version Macros
/* Sudoers group plugin version major/minor */
#define GROUP_API_VERSION_MAJOR 1
#define GROUP_API_VERSION_MINOR 0
#define GROUP_API_VERSION ((GROUP_API_VERSION_MAJOR << 16) | \
GROUP_API_VERSION_MINOR)
/* Getters and setters for group version */
#define GROUP_API_VERSION_GET_MAJOR(v) ((v) >> 16)
#define GROUP_API_VERSION_GET_MINOR(v) ((v) & 0xffff)
#define GROUP_API_VERSION_SET_MAJOR(vp, n) do { \
*(vp) = (*(vp) & 0x0000ffff) | ((n) << 16); \
} while(0)
#define GROUP_API_VERSION_SET_MINOR(vp, n) do { \
*(vp) = (*(vp) & 0xffff0000) | (n); \
} while(0)
=head1 PLUGIN API CHANGELOG
The following revisions have been made to the Sudo Plugin API.
=over 4
=item Version 1.0
Initial API version.
=item Version 1.1
The I/O logging plugin's C<open> function was modified to take the
C<command_info> list as an argument.
=item Version 1.2
The Policy and I/O logging plugins' C<open> functions are now passed
a list of plugin options if any are specified in F<@sysconfdir@/sudo.conf>.
A simple hooks API has been introduced to allow plugins to hook in to the
system's environment handling functions.
The C<init_session> Policy plugin function is now passed a pointer
to the user environment which can be updated as needed. This can
be used to merge in environment variables stored in the PAM handle
before a command is run.
=back
=head1 SEE ALSO
L<sudoers(5)>, L<sudo(8)>
=head1 BUGS
If you feel you have found a bug in B<sudo>, please submit a bug report
at http://www.sudo.ws/sudo/bugs/
=head1 SUPPORT
Limited free support is available via the sudo-workers mailing list,
see http://www.sudo.ws/mailman/listinfo/sudo-workers to subscribe or
search the archives.
=head1 DISCLAIMER
B<sudo> is provided ``AS IS'' and any express or implied warranties,
including, but not limited to, the implied warranties of merchantability
and fitness for a particular purpose are disclaimed. See the LICENSE
file distributed with B<sudo> or http://www.sudo.ws/sudo/license.html
for complete details.
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>