From: Todd C. Miller Date: Sat, 20 Feb 2010 14:30:27 +0000 (-0500) Subject: Sudo plugin API X-Git-Tag: SUDO_1_8_0~892 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=90c06ad7f297c38f41224ac17a4fa91d2da9901f;p=sudo Sudo plugin API --- diff --git a/doc/plugin.pod b/doc/plugin.pod new file mode 100644 index 000000000..ca9de74ed --- /dev/null +++ b/doc/plugin.pod @@ -0,0 +1,757 @@ +=pod + +=head1 Sudo Plugin API + +Starting with version 1.8, B supports a plugin API +for policy and session logging. By default, the I policy +plugin and an associated I/O logging plugin are used. Via the plugin +API, B 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 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 header file. + +=head2 The sudo.conf File + +The F file contains plugin configuration directives. +Currently, the only supported keyword is the C directive, +which causes a plugin plugin to be loaded. + +A C line consists of the C keyword, followed by the +I and the I to the shared object containing the +plugin. The I is the name of the C +or C in the plugin shared object. The I +may be fully qualified or relative. If not fully qualified it is +relative to the $prefix/libexec directory where the prefix is +specified at build time (/usr/local by default). + +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 /etc/sudo.conf file + # + # Format: + # Plugin plugin_name plugin_path + # + # The plugin_path relative to prefix/libexec unless fully qualified + # The plugin_name corresponds to a global symbol in the plugin + # that contains the plugin interface. + # + Plugin policy_plugin sudoers.so + Plugin io_plugin sudoers.so + +=head2 Policy Plugin API + +A policy plugin must declare and populate a C struct +in the global scope. This structure contains pointers to the functions +that implement the B policy checks. The name of the symbol should +be specified in F along with a path to the plugin +so that B 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, + char * const settings[], char * const user_info[], + char * const user_env[]); + void (*close)(int exit_status, int error); + int (*show_version)(int verbose); + int (*check_policy)(int agrc, 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); + }; + +The policy_plugin struct has the following fields: + +=over 4 + +=item type + +The C field should always be set to SUDO_POLICY_PLUGIN + +=item version + +The C field should be set to SUDO_API_VERSION. + +This allows B to determine the API version the plugin was +built against. + +=item open + + int (*open)(unsigned int version, sudo_conv_t conversation, + char * const settings[], char * const user_info[], + char * const user_env[]); + +Returns 1 on success, 0 on failure and -1 on error. +On error, the plugin may optionally call the conversation function with +C to present additional error information to the +user. + +The function arguments are as follows: + +=over 4 + +=item version + +The version passed in by B allows the plugin to determine the +major and minor version number of the plugin API supported by +B. + +=item conversation + +A pointer to the conversation function that can be used by the +plugin to interact with the user (see below). + +=item settings + +A vector of user-supplied B settings in the form of "name=value" +strings. The vector is terminated by a C pointer. These +settings correspond to flags the user specified when running B. +As such, they will only be present when the corresponding flag has +been specified on the command line. + +When parsing I, the plugin should split on the B +equal sign ('=') since the I field will never include one +itself but the I might. + +=over 4 + +=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 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 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 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 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. + +=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 pointer. + +When parsing I, the plugin should split on the B +equal sign ('=') since the I field will never include one +itself but the I might. + +=over 4 + +=item user=string + +The name of the user invoking B. + +=item uid=uid_t + +The real user ID of the user invoking B. + +=item gid=gid_t + +The real group ID of the user invoking B. + +=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. + +=item host=string + +The local machine's hostname as returned by the C +system call. + +=back + +=item user_env + +The user's environment in the form of a C-terminated vector of +"name=value" strings. + +When parsing I, the plugin should split on the B +equal sign ('=') since the I field will never include one +itself but the I might. + +=back + +=item close + + void (*close)(int exit_status, int error); + +The C function is called when the command being run by B +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 is undefined if C is non-zero. + +=item error + +If the command could not be executed, this is set to the value of +C set by the execve(2) system call. The plugin is responsible +for displaying error information via the conversation function. If +the command was successfully executed, the value of C is 0. + +=back + +=item show_version + + int (*show_version)(int verbose); + +The C function is called by B when the user specifies +the C<-V> option. The plugin may display its version information +to the user via the conversation function using C. +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 function is called by B to determine +whether the user is allowed to run the specified commands. +Returns 1 if the command is allowed, 0 if not and -1 on error. +On error, the plugin may optionally call the conversation function with +C to present additional error information to the +user. + +The function arguments are as follows: + +=over 4 + +=item argc + +The number of elements in I, not counting the final C +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 pointer. + +=item env_add + +Additional environment variables specified by the user on the command +line in the form of a C-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, the plugin should split on the B +equal sign ('=') since the I field will never include one +itself but the I might. + +=item command_info + +Information about the command being run in the form of "name=value" +strings. These values are used by B 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 pointer. The following values are recognized by B: + +=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 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 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 +is set, this option is ignored. + +=item login_class=login_class + +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 will preserve the user's group vector instead of +initializing the group vector based on C. + +=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 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. + +=back + +Unsupported values will be ignored. + +=item argv_out + +The C-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-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 function with +C to present additional error information to the +user. + +Privileges should be output via the conversation function using +C. + +=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, the plugin should list the privileges of +the invoking user. + +=item argc + +The number of elements in I, not counting the final C +pointer. + +=item argv + +If non-C, 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 function is called when B is run with the +C<-v> flag. For policy plugins such as I that cache +authentication credentials, this function will validate and cache +the credentials. + +The C function should be C 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 function with +C to present additional error information to the +user. + +=item invalidate + + void (*invalidate)(int remove); + +The C function is called when B is called with +the C<-k> or C<-K> flag. For policy plugins such as I that +cache authentication credentials, this function will invalidate the +credentials. If the I flag is set, the plugin may remove +the credentials instead of simply invalidating them. + +The C function should be C if the plugin does not +support credential caching. + +=back + +=head3 Conversation API + +If the plugin needs to interact with the user or display informational +or error messages, 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. + + struct sudo_conv_message { + #define SUDO_CONV_PROMPT_ECHO_OFF 1 + #define SUDO_CONV_PROMPT_ECHO_ON 2 + #define SUDO_CONV_ERROR_MSG 3 + #define SUDO_CONV_INFO_MSG 4 + 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[]); + +A conversation function is passed in to the plugin's C function +when the plugin is initialized. The plugin passes in a C and C for each message +in the conversation. The plugin is responsible for freeing the +actual reply buffer in C. + +See the sample plugin for an example of the conversation function usage. + +=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 + char * const settings[], char * const user_info[], + char * const user_env[]); + void (*close)(int exit_status, int error); /* wait status or error */ + int (*show_version)(int verbose); + int (*log_input)(const char *buf, unsigned int len); + int (*log_output)(const char *buf, unsigned int len); + }; + +When an I/O plugin is loaded, B runs the command in a pseudo-tty. +This makes it possible to log the input and output from the user's +session. If the log_input function is defined, it will receive the +raw user input (note that this will include input even when echo +is disabled, such as passwords). The log_output function receives +output from the pseudo-tty that is suitable for replaying the user's +session at a later time. Either log_input or log_output may be +NULL. 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 field should always be set to SUDO_IO_PLUGIN + +=item version + +The C field should be set to SUDO_API_VERSION. + +This allows B to determine the API version the plugin was +built against. + +=item open + + int (*open)(unsigned int version, sudo_conv_t conversation + char * const settings[], char * const user_info[], + char * const user_env[]); + +Returns 1 on success, 0 if the command should not be logged and -1 on error. +On error, the plugin may optionally call the conversation function with +C to present additional error information to the +user. + +The function arguments are as follows: + +=over 4 + +=item version + +The version passed in by B allows the plugin to determine the +major and minor version number of the plugin API supported by +B. + +=item conversation + +A pointer to the conversation function that may be used by the +I function to display version information (see +show_version below). The conversation function may also be used +to display additional error message to the user. + +=item settings + +A vector of user-supplied B settings in the form of "name=value" +strings. The vector is terminated by a C pointer. These +settings correspond to flags the user specified when running B. +As such, they will only be present when the corresponding flag has +been specified on the command line. + +When parsing I, the plugin should split on the B +equal sign ('=') since the I field will never include one +itself but the I might. + +See the L 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 pointer. + +When parsing I, the plugin should split on the B +equal sign ('=') since the I field will never include one +itself but the I might. + +See the L section for a list of all possible strings. + +=item user_env + +The user's environment in the form of a C-terminated vector of +"name=value" strings. + +When parsing I, the plugin should split on the B +equal sign ('=') since the I field will never include one +itself but the I might. + +=back + +=item close + + void (*close)(int exit_status, int error); + +The C function is called when the command being run by B +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 is undefined if C is non-zero. + +=item error + +If the command could not be executed, this is set to the value of +C set by the execve(2) system call. If the command was +successfully executed, the value of C is 0. + +=back + +=item show_version + + int (*show_version)(int verbose); + +The C function is called by B when the user specifies +the C<-V> option. The plugin may display its version information +to the user via the conversation function using C. +If the user requests detailed version information, the verbose flag +will be set. + +=item log_input + + int (*log_input)(const char *buf, unsigned int len); + +The I 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 in bytes. + +=back + +=item log_output + + int (*log_output)(const char *buf, unsigned int len); + +The I 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 input 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 in bytes. + +=back + +=back + +=head2 Unhandled command line options + +The C<-L> command line option has been deprecated as its output is +covered by the I manual page. + +=head2 Sudo implementation details + +Version macros: + + #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) + + #define SUDO_API_VERSION_MAJOR 1 + #define SUDO_API_VERSION_MINOR 0 + #define SUDO_API_VERSION ((SUDO_API_VERSION_MAJOR << 16) | SUDO_API_VERSION_MINOR) + +Sudo will cast the plugin symbol to the following when determining +the plugin type. + + union sudo_plugin { + struct policy_plugin policy; + struct io_plugin io; + }; diff --git a/include/sudo_plugin.h b/include/sudo_plugin.h new file mode 100644 index 000000000..e694ec9e9 --- /dev/null +++ b/include/sudo_plugin.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2009-2010 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 + * 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. + */ + +#ifndef _SUDO_PLUGIN_H +#define _SUDO_PLUGIN_H + +/* API version major/minor */ +#define SUDO_API_VERSION_MAJOR 1 +#define SUDO_API_VERSION_MINOR 0 +#define SUDO_API_VERSION ((SUDO_API_VERSION_MAJOR << 16) | 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) + +/* Conversation function types and defines */ +struct sudo_conv_message { +#define SUDO_CONV_PROMPT_ECHO_OFF 1 +#define SUDO_CONV_PROMPT_ECHO_ON 2 +#define SUDO_CONV_ERROR_MSG 3 +#define SUDO_CONV_INFO_MSG 4 + 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[]); + +/* Policy plugin type and defines */ +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, + char * const settings[], char * const user_info[], + char * const user_env[]); + 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[]); + int (*list)(int argc, char * const argv[], int verbose, + const char *list_user); + int (*validate)(void); + void (*invalidate)(int remove); +}; + +/* I/O plugin type and defines */ +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, + char * const settings[], char * const user_info[], + char * const user_env[]); + void (*close)(int exit_status, int error); /* wait status or error */ + int (*show_version)(int verbose); + int (*log_input)(const char *buf, unsigned int len); + int (*log_output)(const char *buf, unsigned int len); +}; + +/* Internal use only */ +struct generic_plugin { + unsigned int type; + unsigned int version; + int (*open)(unsigned int version, sudo_conv_t conversation, + char * const settings[], char * const user_info[], + char * const user_env[]); + void (*close)(int exit_status, int error); /* wait status or error */ + int (*show_version)(int verbose); + /* the rest depends on the type... */ +}; + +#endif /* _SUDO_PLUGIN_H */