diff options
Diffstat (limited to '')
-rw-r--r-- | doc/sudo_plugin_python.man.in | 1890 |
1 files changed, 1890 insertions, 0 deletions
diff --git a/doc/sudo_plugin_python.man.in b/doc/sudo_plugin_python.man.in new file mode 100644 index 0000000..919ecd9 --- /dev/null +++ b/doc/sudo_plugin_python.man.in @@ -0,0 +1,1890 @@ +.\" Automatically generated from an mdoc input file. Do not edit. +.\" +.\" SPDX-License-Identifier: ISC +.\" +.\" Copyright (c) 2019-2020 Robert Manner <robert.manner@oneidentity.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. +.\" +.TH "SUDO_PLUGIN_PYTHON" "5" "February 19, 2020" "Sudo @PACKAGE_VERSION@" "File Formats Manual" +.nh +.if n .ad l +.SH "NAME" +\fBsudo_plugin_python\fR +\- Sudo Plugin API (Python) +.SH "DESCRIPTION" +Starting with version 1.9, +\fBsudo\fR +plugins can be written in python. +The API closely follows the C +\fBsudo\fR +plugin API described by +sudo_plugin(@mansectform@). +.PP +The supported plugins types are: +.PP +.RS 4n +.PD 0 +.TP 3n +\fB\(bu\fR +Policy plugin +.TP 3n +\fB\(bu\fR +I/O plugin +.TP 3n +\fB\(bu\fR +Audit plugin +.TP 3n +\fB\(bu\fR +Approval plugin +.TP 3n +\fB\(bu\fR +Group provider plugin +.RE +.PD +.PP +Python plugin support needs to be explicitly enabled at build time +with the configure option +\(lq--enable-python\(rq. +Python version 3.0 or higher is required. +.SS "Sudo Python Plugin Base" +A plugin written in Python should be a class in a python file that +inherits from +\fIsudo.Plugin\fR. +The +\fIsudo.Plugin\fR +base class has no real purpose other than to identify this class as a plugin. +.PP +The only implemented method is a constructor, which stores the +keyword arguments it receives as fields (member variables) in the object. +This is intended as a convenience to allow you to avoid writing the +constructor yourself. +.PP +For example: +.nf +.sp +.RS 6n +import sudo + +class MySudoPlugin(sudo.Plugin): + # example constructor (optional) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # example destructor (optional) + def __del__(self): + pass +.RE +.fi +.PP +Both the constructor and destructor are optional and can be omitted. +.PP +The customized Plugin class should define a few plugin-specific methods. +When the plugin loads, +\fBsudo\fR +will create an instance of this class and call the methods. +The actual methods required depent on the type of the plugin, +but most return an +\(lqint\(rq +result code, as documented in +sudo_plugin(@mansctsu@), +that indicates whether or not the method was successful. +The Python sudo module defines the following constants to improve readability: +.RS 4n +.TS +l l. +.PP +\fBDefine\fR \fBValue\fR +.PP +\fRsudo.RC.OK\fR 1 +.PP +\fRsudo.RC.ACCEPT\fR 1 +.PP +\fRsudo.RC.REJECT\fR 0 +.PP +\fRsudo.RC.ERROR\fR -1 +.PP +\fRsudo.RC.USAGE_ERROR\fR -2 +.TE +.RE +.PP +If a function returns +\fINone\fR +(for example, if it does not call return), +it will be considered to have returned +\fRsudo.RC.OK\fR. +If an exception is raised (other than sudo.PluginException), the backtrace will be +shown to the user and the plugin function will return +\fRsudo.RC.ERROR\fR. +If that is not acceptable, you must catch the exception and handle it yourself. +.PP +Instead of just returning +\fRsudo.RC.ERROR\fR +or +\fRsudo.RC.REJECT\fR +result code the plugin can also provide a message describing the problem. +This can be done by raising one of the special exceptions: +.nf +.sp +.RS 6n +raise sudo.PluginError("Message") +raise sudo.PluginReject("Message") +.RE +.fi +.PP +This added message will be used by the audit plugins. +Both exceptions inherit from +\fRsudo.PluginException\fR +.SS "Python Plugin Loader" +Running the Python interpreter and bridging between C and Python is +handled by the +\fBsudo\fR +plugin +\fRpython_plugin.so\fR. +This shared object can be loaded like any other dynamic +\fBsudo\fR +plugin and should receive the path and the class name of the Python +plugin it is loading as arguments. +.PP +Example usage in +sudo.conf(@mansectform@): +.nf +.sp +.RS 6n +Plugin python_policy python_plugin.so ModulePath=<path> ClassName=<class> +Plugin python_io python_plugin.so ModulePath=<path> ClassName=<class> +Plugin python_audit python_plugin.so ModulePath=<path> ClassName=<class> +Plugin python_approval python_plugin.so ModulePath=<path> ClassName=<class> +.RE +.fi +.PP +Example group provider plugin usage in the +\fIsudoers\fR +file: +.nf +.sp +.RS 6n +Defaults group_plugin="python_plugin.so ModulePath=<path> ClassName=<class>" +.RE +.fi +.PP +The plugin arguments are as follows: +.TP 6n +ModulePath +The path of a python file which contains the class of the sudo Python plugin. +It must be either an absolute path or a path relative to the sudo Python plugin +directory: "@plugindir@/python". +.TP 6n +ClassName +(Optional.) The name of the class implementing the sudo Python plugin. +If not supplied, the one and only sudo.Plugin that is present in the module +will be used. +If there are multiple such plugins in the module (or none), it +will result in an error. +.SS "Policy plugin API" +Policy plugins must be registered in +sudo.conf(@mansectform@). +For example: +.nf +.sp +.RS 6n +Plugin python_policy python_plugin.so ModulePath=<path> ClassName=<class> +.RE +.fi +.PP +Currently, only a single policy plugin may be specified in +sudo.conf(@mansectform@). +.PP +A policy plugin may have the following member functions: +.TP 6n +\fBconstructor\fR +.nf +.RS 6n +__init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...], + version: str, user_info: Tuple[str, ...], + plugin_options: Tuple[str, ...]) +.RE +.fi +.RS 6n +.sp +Implementing this function is optional. +The default constructor will set the keyword arguments it receives +as member variables in the object. +.sp +The constructor matches the +\fBopen\fR() +function in the C sudo plugin API. +.sp +The function arguments are as follows: +.TP 6n +\fIuser_env\fR +The user's environment as a tuple of strings in +\(lqkey=value\(rq +format. +.TP 6n +\fIsettings\fR +A tuple of user-supplied +\fIsudo\fR +settings in the form of +\(lqkey=value\(rq +strings. +.TP 6n +\fIversion\fR +The version of the Python Policy Plugin API. +.TP 6n +\fIuser_info\fR +A tuple of information about the user running the command in the form of +\(lqkey=value\(rq +strings. +.TP 6n +\fIplugin_options\fR +The plugin options passed as arguments in the +sudo.conf(@mansectform@) +plugin registration. +This is a tuple of strings, usually (but not necessarily) in +\(lqkey=value\(rq +format. +.PP +The +\fBsudo.options_as_dict\fR() +convenience function can be used to convert +\(lqkey=value\(rq +pairs to a dictionary. +For a list of recognized keys and their supported values, +see the policy plugin +\fBopen\fR() +documentation in +sudo_plugin(@mansectform@). +.RE +.TP 6n +\fBcheck_policy\fR +.nf +.RS 6n +check_policy(self, argv: Tuple[str, ...], env_add: Tuple[str, ...]) +.RE +.fi +.RS 6n +.sp +The +\fBcheck_policy\fR() +function is called by +\fBsudo\fR +to determine whether the user is allowed to run the specified command. +Implementing this function is mandatory for a policy plugin. +.sp +The function arguments are as follows: +.TP 6n +\fIargv\fR +A tuple describing the command the user wishes to run. +.TP 6n +\fIenv_add\fR +Additional environment variables specified by the user on the command line in +the form of a tuple of +\(lqkey=value\(rq +pairs. +The +\fBsudo.options_as_dict\fR() +convenience function can be used to convert them to a dictionary. +.PP +This function should return a result code or a tuple in the following format: +.nf +.sp +.RS 12n +return (rc, command_info_out, argv_out, user_env_out) +.RE +.fi +.sp +The tuple values are as follows: +.TP 6n +\fIrc\fR +The result of the policy check, one of the +\fRsudo.RC.*\fR +constants. +\fRsudo.RC.ACCEPT\fR +if the command is allowed, +\fRsudo.RC.REJECT\fR +if not allowed, +\fRsudo.RC.ERROR\fR +for a general error, or +\fRsudo.RC.USAGE_ERROR\fR +for a usage error. +.TP 6n +\fIcommand_info_out\fR +Optional (only required when the command is accepted). +Information about the command being run in the form of +\(lqkey=value\(rq +strings. +.sp +To accept a command, at the very minimum the plugin must set in the +\fIcommand\fR, +\fIrunas_uid\fR +and +\fIrunas_gid\fR +keys. +.sp +For a list of recognized keys and supported values, +see the +\fBcheck_policy\fR() +documentation in +sudo_plugin(@mansectform@). +.TP 6n +\fIargv_out\fR +Optional (only required when the command is accepted). +The arguments to pass to the +execve(2) +system call when executing the command. +.TP 6n +\fIuser_env_out\fR +Optional (only required when the command is accepted). +The environment to use when executing the command in the form of a +tuple of strings in +\(lqkey=value\(rq +format. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBinit_session\fR +.nf +.RS 6n +init_session(self, user_pwd: Tuple, user_env: Tuple[str, ...]) +.RE +.fi +.RS 6n +.sp +Perform session setup (optional). +The +\fBinit_session\fR() +function is called before +\fBsudo\fR +sets up the +execution environment for the command before any uid or gid changes. +.sp +The function arguments are as follows: +.TP 6n +\fIuser_pwd\fR +A tuple describing the user's passwd entry. +Convertible to pwd.struct_passwd or +\fINone\fR +if the user is not present in the password database. +.sp +Example conversion: +.nf +.RS 12n +user_pwd = pwd.struct_passwd(user_pwd) if user_pwd else None +.RE +.fi +.TP 6n +\fIuser_env\fR +The environment the command will run in. +This is a tuple of strings in +\(lqkey=value\(rq +format. +.PP +This function should return a result code or a tuple in the following format: +.nf +.sp +.RS 10n +return (rc, user_env_out) +.RE +.fi +.sp +The tuple values are as follows: +.TP 6n +\fIrc\fR +The result of the session init, one of the +\fRsudo.RC.*\fR +constants. +\fRsudo.RC.OK\fR +on success, 0 on failure, or +\fRsudo.RC.ERROR\fR +if an error occurred. +.TP 6n +\fIuser_env_out\fR +Optional. +If the +\fBinit_session\fR() +function needs to modify the user environment, it can return the new +environment in +\fIuser_env_out\fR. +If this is omitted, no changes will be made to +\fIuser_env\fR. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBlist\fR +.nf +.RS 6n +list(self, argv: Tuple[str, ...], is_verbose: int, user: str) +.RE +.fi +.RS 6n +.sp +List available privileges for the invoking user. +.sp +The function arguments are as follows: +.TP 6n +\fIargv\fR +If not set to +\fINone\fR, +an argument vector describing a command the user wishes to check +against the policy. +.TP 6n +\fIis_verbose\fR +Flag indicating whether to list in verbose mode or not. +.TP 6n +\fIuser\fR +The name of a different user to list privileges for if the policy allows it. +If +\fINone\fR, +the plugin should list the privileges of the invoking user. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBvalidate\fR +.nf +.RS 6n +validate(self) +.RE +.fi +.RS 6n +.sp +For policy plugins that cache authentication credentials, this function is used to validate and cache the credentials (optional). +.RE +.TP 6n +\fBinvalidate\fR +.nf +.RS 6n +invalidate(self, remove: int) +.RE +.fi +.RS 6n +.sp +For policy plugins that cache authentication credentials, this function is used to invalidate the credentials (optional). +.sp +The function arguments are as follows: +.TP 6n +\fIremove\fR +If this flag is set, the plugin may remove the credentials instead of simply +invalidating them. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBshow_version\fR +.nf +.RS 6n +show_version(self, is_verbose: int) +.RE +.fi +.RS 6n +.sp +Display the plugin version information to the user. +The +\fBsudo.log_info\fR() +function should be used. +.sp +The function arguments are as follows: +.TP 6n +\fIis_verbose\fR +A flag to indicate displaying more verbose information. +Currently this is 1 if +\(oqsudo -V\(cq +is run as the root user. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBclose\fR +.br +.nf +.RS 6n +close(self, exit_status: int, error: int) +.RE +.fi +.RS 6n +.sp +Called when a command finishes executing. +.sp +Works the same as the +\fBclose\fR() +function in the C sudo plugin API, except that it only gets called if +\fBsudo\fR +attempts to execute the command. +.sp +The function arguments are as follows: +.TP 6n +\fIexit_status\fR +The exit status of the command if was executed, otherwise -1. +.TP 6n +\fIerror\fR +.br +If the command could not be executed, this is set to the value of +errno set by the +execve(2) +system call, otherwise 0. +.PD 0 +.PP +.RE +.PD +.SS "Policy plugin example" +Sudo ships with an example Python policy plugin. +To try it, register it by adding the following lines to +\fI@sysconfdir@/sudo.conf\fR: +.nf +.sp +.RS 0n +Plugin python_policy python_plugin.so \e + ModulePath=@prefix@/share/doc/sudo/examples/example_policy_plugin.py \e + ClassName=SudoPolicyPlugin +.RE +.fi +.PP +Be aware, however, that you cannot enable the Python policy plugin +in addition to another policy plugin, such as +sudoers(@mansectform@). +.SS "I/O plugin API" +I/O plugins must be registered in +sudo.conf(@mansectform@). +For example: +.nf +.sp +.RS 6n +Plugin python_io python_plugin.so ModulePath=<path> ClassName=<class> +.RE +.fi +.PP +Sudo supports loading multiple I/O plugins. +Currently only 8 python I/O plugins can be loaded at once. +.PP +An I/O plugin may have the following member functions: +.TP 6n +\fBconstructor\fR +.nf +.RS 6n +__init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...], + version: str, user_info: Tuple[str, ...], + plugin_options: Tuple[str, ...]) +.RE +.fi +.RS 6n +.sp +Implementing this function is optional. +The default constructor will set the keyword arguments it receives +as member variables in the object. +.sp +The constructor matches the +\fBopen\fR() +function in the C sudo plugin API. +.sp +The function arguments are as follows: +.TP 6n +\fIuser_env\fR +The user's environment as a tuple of strings in +\(lqkey=value\(rq +format. +.TP 6n +\fIsettings\fR +A tuple of user-supplied +\fIsudo\fR +settings in the form of +\(lqkey=value\(rq +strings. +.TP 6n +\fIversion\fR +The version of the Python I/O Plugin API. +.TP 6n +\fIuser_info\fR +A tuple of information about the user running the command in the form of +\(lqkey=value\(rq +strings. +.TP 6n +\fIplugin_options\fR +The plugin options passed as arguments in the +sudo.conf(@mansectform@) +plugin registration. +This is a tuple of strings, usually (but not necessarily) in +\(lqkey=value\(rq +format. +.PP +The +\fBsudo.options_as_dict\fR() +convenience function can be used to convert +\(lqkey=value\(rq +pairs to a dictionary. +For a list of recognized keys and their supported values, +see the I/O plugin +\fBopen\fR() +documentation in +sudo_plugin(@mansectform@). +.RE +.TP 6n +\fBopen\fR +.nf +.RS 6n +open(self, argv: Tuple[str, ...], + command_info: Tuple[str, ...]) -> int +.RE +.fi +.RS 6n +.sp +Receives the command the user wishes to run. +.sp +Works the same as the +\fBopen\fR() +function in the C sudo plugin API except that: +.sp +.RS 10n +.PD 0 +.TP 3n +\fB\(bu\fR +It only gets called before the user would execute some command +(and not for a version query for example). +.TP 3n +\fB\(bu\fR +Other arguments of the C API +\fBopen\fR() +function are received through the constructor. +.RE +.sp +The function arguments are as follows: +.PD +.TP 6n +\fIargv\fR +A tuple of the arguments describing the command the user wishes to run. +.TP 6n +\fIcommand_info\fR +Information about the command being run in the form of +\(lqkey=value\(rq +strings. +.PP +The +\fBsudo.options_as_dict\fR() +convenience function can be used to convert +\(lqkey=value\(rq +pairs to a dictionary. +For a list of recognized keys and their supported values, +see the I/O plugin +\fBopen\fR() +documentation in +sudo_plugin(@mansectform@). +.sp +The +\fBopen\fR() +function should return a result code, one of the +\fRsudo.RC.*\fR +constants. +If the function returns +\fRsudo.RC.REJECT\fR, +no I/O will be sent to the plugin. +.RE +.TP 6n +\fBlog_ttyin\fR, \fBlog_ttyout\fR, \fBlog_stdin\fR, \fBlog_stdout\fR, \fBlog_stderr\fR +.nf +.RS 6n +log_ttyin(self, buf: str) -> int +log_ttyout(self, buf: str) -> int +log_stdin(self, buf: str) -> int +log_stdout(self, buf: str) -> int +log_stderr(self, buf: str) -> int +.RE +.fi +.RS 6n +.sp +Receive the user input or output of the terminal device and +application standard input / output / error. +See the matching calls in +sudo_plugin(@mansectform@). +.sp +The function arguments are as follows: +.TP 6n +\fIbuf\fR +The input (or output) buffer in the form of a string. +.PP +The function should return a result code, one of the +\fRsudo.RC.*\fR +constants. +.sp +If +\fRsudo.RC.ERROR\fR +is returned, the running command will be terminated and all of the plugin's logging +functions will be disabled. +Other I/O logging plugins will still receive any remaining +input or output that has not yet been processed. +.sp +If an input logging function rejects the data by returning +\fRsudo.RC.REJECT\fR, +the command will be terminated and the data will not be passed to the +command, though it will still be sent to any other I/O logging plugins. +If an output logging function rejects the data by returning +\fRsudo.RC.REJECT\fR, +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. +.RE +.TP 6n +\fBchange_winsize\fR +.nf +.RS 6n +change_winsize(self, line: int, cols: int) -> int +.RE +.fi +.RS 6n +.sp +Called whenever the window size of the terminal changes. +The function arguments are as follows: +.TP 6n +\fIline\fR +The number of lines of the terminal. +.TP 6n +\fIcols\fR +The number of columns of the terminal. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBlog_suspend\fR +.nf +.RS 6n +log_suspend(self, signo: int) -> int +.RE +.fi +.RS 6n +Called whenever a command is suspended or resumed. +.sp +The function arguments are as follows: +.TP 6n +\fIsigno\fR +.br +The number of the signal that caused the command to be suspended or +\fRSIGCONT\fR +if the command was resumed. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBshow_version\fR +.nf +.RS 6n +show_version(self, is_verbose: int) +.RE +.fi +.RS 6n +Display the plugin version information to the user. +The +\fBsudo.log_info\fR() +function should be used. +.sp +The function arguments are as follows: +.TP 6n +\fIis_verbose\fR +A flag to indicate displaying more verbose information. +Currently this is 1 if +\(oqsudo -V\(cq +is run as the root user. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBclose\fR +.br +.nf +.RS 6n +close(self, exit_status: int, error: int) -> None +.RE +.fi +.RS 6n +Called when a command execution finished. +.sp +Works the same as the +\fBclose\fR() +function in the C sudo plugin API, except that it only gets called if +\fBsudo\fR +attempts to execute the command. +.sp +The function arguments are as follows: +.TP 6n +\fIexit_status\fR +The exit status of the command if was executed, otherwise -1. +.TP 6n +\fIerror\fR +.br +If the command could not be executed, this is set to the value of +errno set by the +execve(2) +system call, otherwise 0. +.PD 0 +.PP +.RE +.PD +.SS "I/O plugin example" +Sudo ships a Python I/O plugin example. +To try it, register it by adding the following lines to +\fI@sysconfdir@/sudo.conf\fR: +.nf +.sp +.RS 6n +Plugin python_io python_plugin.so \e + ModulePath=@prefix@/share/doc/sudo/examples/example_io_plugin.py \e + ClassName=SudoIOPlugin +.RE +.fi +.SS "Audit plugin API" +Audit plugins must be registered in +sudo.conf(@mansectform@). +For example: +.nf +.sp +.RS 6n +Plugin python_audit python_plugin.so ModulePath=<path> ClassName=<class> +.RE +.fi +.PP +Sudo supports loading multiple audit plugins. +Currently only 8 python audit plugins can be loaded at once. +.PP +An audit plugin may have the following member functions (all of them are optional): +.TP 6n +\fBconstructor\fR +.nf +.RS 6n +__init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...], + version: str, user_info: Tuple[str, ...], plugin_options: Tuple[str, ...]) +.RE +.fi +.RS 6n +.sp +The default constructor will set the keyword arguments it receives +as member variables in the object. +.sp +The constructor matches the +\fBopen\fR() +function in the C sudo plugin API. +.sp +The function arguments are as follows: +.TP 6n +\fIuser_env\fR +The user's environment as a tuple of strings in +\(lqkey=value\(rq +format. +.TP 6n +\fIsettings\fR +A tuple of user-supplied +\fIsudo\fR +settings in the form of +\(lqkey=value\(rq +strings. +.TP 6n +\fIversion\fR +The version of the Python Audit Plugin API. +.TP 6n +\fIuser_info\fR +A tuple of information about the user running the command in the form of +\(lqkey=value\(rq +strings. +.TP 6n +\fIplugin_options\fR +The plugin options passed as arguments in the +sudo.conf(@mansectform@) +plugin registration. +This is a tuple of strings, usually (but not necessarily) in +\(lqkey=value\(rq +format. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBopen\fR +.nf +.RS 6n +open(self, submit_optind: int, + submit_argv: Tuple[str, ...]) -> int +.RE +.fi +.RS 6n +.sp +The function arguments are as follows: +.TP 6n +\fIsubmit_optind\fR +The index into +\fIsubmit_argv\fR +that corresponds to the first entry that is not a command line option. +.TP 6n +\fIsubmit_argv\fR +The argument vector sudo was invoked with, including all command line options. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBclose\fR +.br +.nf +.RS 6n +close(self, status_type: int, status: int) -> None +.RE +.fi +.RS 6n +.sp +Called when sudo is finished, shortly before it exits. +.sp +The function arguments are as follows: +.TP 6n +\fIstatus_type\fR +The type of status being passed. +One of the sudo.EXIT_REASON.* constants. +.TP 6n +\fIstatus\fR +Depending on the value of +\fIstatus_type\fR, +this value is either +ignored, the command's exit status as returned by the +wait(2) +system call, the value of +\fRerrno\fR +set by the +execve(2) +system call, or the value of +\fRerrno\fR +resulting from an error in the +\fBsudo\fR +front end. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBshow_version\fR +.nf +.RS 6n +show_version(self, is_verbose: int) -> int +.RE +.fi +.RS 6n +.sp +Display the plugin version information to the user. +The +\fBsudo.log_info\fR() +function should be used. +.sp +The function arguments are as follows: +.TP 6n +\fIis_verbose\fR +A flag to indicate displaying more verbose information. +Currently this is 1 if +\(oqsudo -V\(cq +is run as the root user. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBaccept\fR +.nf +.RS 6n +accept(self, plugin_name: str, plugin_type: int, command_info: Tuple[str, ...], + run_argv: Tuple[str, ...], run_envp: Tuple[str, ...]) -> int +.RE +.fi +.RS 6n +.sp +This function is called when a command or action is accepted by a policy +or approval plugin. +The function arguments are as follows: +.TP 6n +plugin_name +The name of the plugin that accepted the command or +\(lqsudo\(rq +for the +\fBsudo\fR +front-end. +.TP 6n +plugin_type +The type of plugin that accepted the command, currently either +\fRsudo.PLUGIN_TYPE.POLICY\fR, +\fRsudo.PLUGIN_TYPE.APPROVAL\fR +or +\fRsudo.PLUGIN_TYPE.SUDO\fR. +The +\fBaccept\fR() +function is called multiple times--once for each policy or approval +plugin that succeeds and once for the sudo front-end. +When called on behalf of the sudo front-end, +\fIcommand_info\fR +may include information from an I/O logging plugin as well. +.sp +Typically, an audit plugin is interested in either the accept status from +the +\fBsudo\fR +front-end or from the various policy and approval plugins, but not both. +It is possible for the policy plugin to accept a command that is +later rejected by an approval plugin, in which case the audit +plugin's +\fBaccept\fR() +and +\fBreject\fR() +functions will +\fIboth\fR +be called. +.TP 6n +command_info +A vector of information describing the command being run. +See the +sudo_plugin(@mansectform@) +manual for possible values. +.TP 6n +run_argv +Argument vector describing a command that will be run. +.TP 6n +run_envp +The environment the command will be run with. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBreject\fR +.nf +.RS 6n +reject(self, plugin_name: str, plugin_type: int, audit_msg: str, + command_info: Tuple[str, ...]) -> int +.RE +.fi +.RS 6n +.sp +This function is called when a command or action is rejected by the policy +plugin. +The function arguments are as follows: +.TP 6n +plugin_name +The name of the plugin that rejected the command. +.TP 6n +plugin_type +The type of plugin that rejected the command, currently either +\fRsudo.PLUGIN_TYPE.POLICY\fR, +\fRsudo.PLUGIN_TYPE.APPROVAL\fR +or +\fRsudo.PLUGIN_TYPE.IO\fR. +.sp +Unlike the +\fBaccept\fR() +function, the +\fBreject\fR() +function is not called on behalf of the +\fBsudo\fR +front-end. +.TP 6n +audit_msg +An optional string describing the reason the command was rejected by the plugin. +If the plugin did not provide a reason, audit_msg will be +\fINone\fR +.TP 6n +command_info +A vector of information describing the rejected command. +See the +sudo_plugin(@mansectform@) +manual for possible values. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBerror\fR +.br +.nf +.RS 6n +error(self, plugin_name: str, plugin_type: int, audit_msg: str, + command_info: Tuple[str, ...]) -> int +.RE +.fi +.RS 6n +.sp +This function is called when a plugin or the +\fBsudo\fR +front-end returns an error. +The function arguments are as follows: +.TP 6n +plugin_name +The name of the plugin that generated the error or +\(lqsudo\(rq +for the +\fBsudo\fR +front-end. +.TP 6n +plugin_type +The type of plugin that generated the error, or +\fRSUDO_FRONT_END\fR +for the +\fBsudo\fR +front-end. +.TP 6n +audit_msg +An optional string describing the plugin error. +If the plugin did not provide a description, it will be +\fINone\fR +.TP 6n +command_info +A vector of information describing the command. +See the +sudo_plugin(@mansectform@) +manual for possible values. +.PD 0 +.PP +.RE +.PD +.SS "Audit plugin example" +Sudo ships a Python Audit plugin example. +To try it, register it by adding the following lines to +\fI@sysconfdir@/sudo.conf\fR: +.nf +.sp +.RS 6n +Plugin python_audit python_plugin.so \e + ModulePath=@prefix@/share/doc/sudo/examples/example_audit_plugin.py \e + ClassName=SudoAuditPlugin +.RE +.fi +.PP +It will log the plugin accept / reject / error results to the output. +.SS "Approval plugin API" +Approval plugins must be registered in +sudo.conf(@mansectform@). +For example: +.nf +.sp +.RS 6n +Plugin python_approval python_plugin.so ModulePath=<path> ClassName=<class> +.RE +.fi +.PP +Sudo supports loading multiple approval plugins. +Currently only 8 python approval plugins can be loaded at once. +.PP +An approval plugin may have the following member functions: +.TP 6n +\fBconstructor\fR +.nf +.RS 6n +__init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...], + version: str, user_info: Tuple[str, ...], plugin_options: Tuple[str, ...], + submit_optind: int, submit_argv: Tuple[str, ...]) +.RE +.fi +.RS 6n +.sp +Optional. +The default constructor will set the keyword arguments it receives +as member variables in the object. +.sp +The constructor matches the +\fBopen\fR() +function in the C sudo plugin API. +.sp +The function arguments are as follows: +.TP 6n +\fIuser_env\fR +The user's environment as a tuple of strings in +\(lqkey=value\(rq +format. +.TP 6n +\fIsettings\fR +A tuple of user-supplied +\fIsudo\fR +settings in the form of +\(lqkey=value\(rq +strings. +.TP 6n +\fIversion\fR +The version of the Python Approval Plugin API. +.TP 6n +\fIuser_info\fR +A tuple of information about the user running the command in the form of +\(lqkey=value\(rq +strings. +.TP 6n +\fIplugin_options\fR +The plugin options passed as arguments in the +sudo.conf(@mansectform@) +plugin registration. +This is a tuple of strings, usually (but not necessarily) in +\(lqkey=value\(rq +format. +.TP 6n +\fIsubmit_optind\fR +The index into +\fIsubmit_argv\fR +that corresponds to the first entry that is not a command line option. +.TP 6n +\fIsubmit_argv\fR +The argument vector sudo was invoked with, including all command line options. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBshow_version\fR +.nf +.RS 6n +show_version(self, is_verbose: int) -> int +.RE +.fi +.RS 6n +.sp +Display the version. (Same as for all the other plugins.) +.RE +.TP 6n +\fBcheck\fR +.br +.nf +.RS 6n +check(self, command_info: Tuple[str, ...], run_argv: Tuple[str, ...], + run_env: Tuple[str, ...]) -> int +.RE +.fi +.RS 6n +.sp +This function is called after policy plugin's check_policy has succeeded. +It can reject execution of the command by returning sudo.RC.REJECT or +raising the special exception: +.nf +.sp +.RS 12n +raise sudo.PluginReject("some message") +.RE +.fi +.sp +with the message describing the problem. +In the latter case, the audit plugins will get the description. +.sp +The function arguments are as follows: +.TP 6n +command_info +A vector of information describing the command that will run. +See the +sudo_plugin(@mansectform@) +manual for possible values. +.TP 6n +run_argv +Argument vector describing a command that will be run. +.TP 6n +run_env +The environment the command will be run with. +.PD 0 +.PP +.RE +.PD +.SS "Approval plugin example" +Sudo ships a Python Approval plugin example. +To try it, register it by adding the following lines to +\fI@sysconfdir@/sudo.conf\fR: +.nf +.sp +.RS 6n +Plugin python_approval python_plugin.so \e + ModulePath=@prefix@/share/doc/sudo/examples/example_approval_plugin.py \e + ClassName=BusinessHoursApprovalPlugin +.RE +.fi +.PP +It will only allow execution of commands in the "business hours" (from Monday +to Friday between 8:00 and 17:59:59). +.SS "Sudoers group provider plugin API" +A group provider plugin is registered in the +sudoers(@mansectform@) +file. +For example: +.nf +.sp +.RS 6n +Defaults group_plugin="python_plugin.so ModulePath=<path> ClassName=<class>" +.RE +.fi +.PP +Currently, only a single group plugin can be registered in +\fIsudoers\fR. +.PP +A group provider plugin may have the following member functions: +.TP 6n +\fBconstructor\fR +.nf +.RS 6n +__init__(self, args: Tuple[str, ...], version: str) +.RE +.fi +.RS 6n +.sp +Implementing this function is optional. +The default constructor will set the keyword arguments it receives +as member variables in the object. +.sp +The function arguments are as follows: +.TP 6n +\fIargs\fR +The plugin options passed as arguments in the +\fIsudoers\fR +file plugin registration. +All the arguments are free form strings (not necessarily in +\(lqkey=value\(rq +format). +.TP 6n +\fIversion\fR +The version of the Python Group Plugin API. +.PD 0 +.PP +.RE +.PD +.TP 6n +\fBquery\fR +.br +.nf +.RS 6n +query(self, user: str, group: str, user_pwd: Tuple) +.RE +.fi +.RS 6n +.sp +The +\fBquery\fR() +function is used to ask the group plugin whether +\fIuser\fR +is a member of +\fIgroup\fR. +This method is required. +.RE +.PP +The function arguments are as follows: +.TP 6n +\fIuser\fR +The name of the user being looked up in the external group database. +.TP 6n +\fIgroup\fR +.br +The name of the group being queried. +.TP 6n +\fIuser_pwd\fR +The password database entry for the user, if any. +If +\fIuser\fR +is not present in the password database, +\fIuser_pwd\fR +will be +\fRNULL\fR. +.SS "Group plugin example" +Sudo ships a Python group plugin example. +To try it, register it in the +\fIsudoers\fR +file by adding the following lines: +.nf +.sp +.RS 6n +Defaults group_plugin="python_plugin.so \e + ModulePath=@prefix@/share/doc/sudo/examples/example_group_plugin.py \e + ClassName=SudoGroupPlugin" +.RE +.fi +.PP +The example plugin will tell +\fBsudo\fR +that the user +\fItest\fR +is part of the non-unix group +\fImygroup\fR. +If you add a rule that uses this group, it will affect the +\fItest\fR +user. +For example: +.nf +.sp +.RS 6n +%:mygroup ALL=(ALL) NOPASSWD: ALL +.RE +.fi +.PP +Will allow user +\fItest\fR +to run +\fBsudo\fR +without a password. +.SS "Hook function API" +The hook function API is currently not supported for plugins +written in Python. +.SS "Conversation API" +A Python plugin can interact with the user using the +\fBsudo.conv\fR() +function which displays one or more messages described by the +\fBsudo.ConvMessage\fR +class. +This is the Python equivalent of the +\fBconversation\fR() +function in the C sudo plugin API. +A plugin should not attempt to read directly from the standard input or +the user's tty (neither of which are guaranteed to exist). +.PP +The +\fBsudo.ConvMessage\fR +class specifies how the user interaction should occur: +.nf +.sp +.RS 4n +sudo.ConvMessage(msg_type: int, msg: str, timeout: int) +.RE +.fi +.PP +\fBsudo.ConvMessage\fR +member variables: +.TP 6n +\fImsg_type\fR +Specifies the type of the conversation. +See the +\fRsudo.CONV.*\fR +constants below. +.TP 6n +\fImsg\fR +The message to display to the user. +The caller must include a trailing newline in +\fRmsg\fR +if one is to be displayed. +.TP 6n +\fItimeout\fR +Optional. +The maximum amount of time for the conversation in seconds. +If the timeout is exceeded, the +\fBsudo.conv\fR() +function will raise a +\fRsudo.ConversationInterrupted\fR +exception. +The default is to wait forever (no timeout). +.PP +To specify the message type, the following constants are available: +.PP +.RS 4n +.PD 0 +.TP 3n +\fB\(bu\fR +sudo.CONV.PROMPT_ECHO_OFF +.TP 3n +\fB\(bu\fR +sudo.CONV.PROMPT_ECHO_ON +.TP 3n +\fB\(bu\fR +sudo.CONV.ERROR_MSG +.TP 3n +\fB\(bu\fR +sudo.CONV.INFO_MSG +.TP 3n +\fB\(bu\fR +sudo.CONV.PROMPT_MASK +.TP 3n +\fB\(bu\fR +sudo.CONV.PROMPT_ECHO_OK +.TP 3n +\fB\(bu\fR +sudo.CONV.PREFER_TTY +.RE +.PD +.PP +See the +sudo_plugin(@mansectform@) +manual for a description of the message types. +.PP +The +\fBsudo.conv\fR() +function performs the actual user interaction: +.nf +.sp +.RS 4n +sudo.conv(message(s), on_suspend=suspend_function, + on_resume=resume_function) +.RE +.fi +.PP +The function arguments are as follows: +.TP 6n +\fImessage(s)\fR +One of more messages (of type +\fBsudo.ConvMessage\fR), +each describing a conversation. +At least one message is required. +.TP 6n +\fIon_suspend\fR +An optional callback function which gets called if the conversation +is suspended, for example by the user pressing control-Z. +The specified function must take a single argument which will be filled +with the number of the signal that caused the process to be suspended. +.TP 6n +\fIon_resume\fR +An optional callback function which gets called when the previously +suspended conversation is resumed. +The specified function must take a single argument which will be filled +with the number of the signal that caused the process to be suspended. +.PP +The +\fBsudo.conv\fR() +function can raise the following exceptions: +.TP 6n +\fBsudo.SudoException\fR +If the conversation fails, for example when the conversation function is not +available. +.TP 6n +\fBsudo.ConversationInterrupted\fR +If the conversation function returns an error, e.g., the timeout passed +or the user interrupted the conversation by pressing control-C. +.SS "Conversation example" +Sudo ships with an example plugin demonstrating the Python conversation API. +To try it, register it by adding the following lines to +\fI@sysconfdir@/sudo.conf\fR: +.nf +.sp +.RS 6n +Plugin python_io python_plugin.so \e + ModulePath=@prefix@/share/doc/sudo/examples/example_conversation.py \e + ClassName=ReasonLoggerIOPlugin +.RE +.fi +.SS "Information / error display API" +.nf +.RS 0n +sudo.log_info(string(s), sep=" ", end="\en") +sudo.log_error(string(s), sep=" ", end="\en") +.RE +.fi +.PP +To display information to the user, the +\fBsudo.log_info\fR() +function can be used. +To display error messages, use +\fBsudo.log_error\fR(). +The syntax is similar to the Python +\fBprint\fR() +function. +.PP +The function arguments are as follows: +.TP 6n +\fIstring(s)\fR +One or more strings to display. +.TP 6n +\fIsep\fR +An optional string which will be used as the separator between the +specified strings. +The default is a space character, +(\(oq\ \(cq). +.TP 6n +\fIend\fR +An optional string which will be displayed at the end of the message. +The default is a new line character +(\(oq\en\(cq). +.SS "Debug API" +Debug messages are not visible to the user and are only logged debugging +is explicitly enabled in +sudo.conf(@mansectform@). +Python plugins can use the +\fBsudo.debug\fR() +function to make use of +\fBsudo\fR's +debug system. +.PP +\fIEnabling debugging in sudo.conf\fR +.PP +To enable debug messages, add a +\fRDebug\fR +line to +sudo.conf(@mansectform@) +with the program set to +\fIpython_plugin.so\fR. +For example, to store debug output in +\fI/var/log/sudo_python_debug\fR, +use a line like the following: +.nf +.sp +.RS 6n +Debug python_plugin.so /var/log/sudo_python_debug \e + plugin@trace,c_calls@trace +.RE +.fi +.PP +The debug options are in the form of multiple +\(lqsubsystem@level\(rq +strings, separated by commas +(\(oq\&,\(cq). +For example to just see the debug output of +\fBsudo.debug\fR() +calls, use: +.nf +.sp +.RS 6n +Debug python_plugin.so /var/log/sudo_python_debug plugin@trace +.RE +.fi +.PP +See +sudo_conf(@mansectform@) +for more details. +.PP +The most interesting subsystems for Python plugin development are: +.TP 6n +\fIplugin\fR +Logs each +\fBsudo.debug\fR() +API call. +.TP 6n +\fIpy_calls\fR +Logs whenever a C function calls into the python module. +For example, calling the +\fB__init__\fR() +function. +.TP 6n +\fIc_calls\fR +Logs whenever python calls into a C +\fBsudo\fR +API function. +.TP 6n +\fIinternal\fR +Logs internal functions of the python language wrapper plugin. +.TP 6n +\fIsudo_cb\fR +Logs when +\fBsudo\fR +calls into the python plugin API. +.TP 6n +\fIload\fR +Logs python plugin loading / unloading events. +.PP +You can also specify +\(lqall\(rq +as the subsystem name to log debug messages for all subsystems. +.PP +The +\fBsudo.debug\fR() +function is defined as: +.nf +.sp +.RS 4n +sudo.debug(level, message(s)) +.RE +.fi +.PP +The function arguments are as follows: +.TP 6n +\fIlevel\fR +.br +an integer, use one of the log level constants below +.TP 6n +\fImessage(s)\fR +one or more messages to log +.PP +\fIAvailable log levels:\fR +.TS +l l l. +.PP +\fBsudo.conf name\fR \fBPython constant\fR \fBdescription\fR +.PP +crit sudo.DEBUG.CRIT only critical messages +.PP +err sudo.DEBUG.ERROR +.PP +warn sudo.DEBUG.WARN +.PP +notice sudo.DEBUG.NOTICE +.PP +diag sudo.DEBUG.DIAG +.PP +info sudo.DEBUG.INFO +.PP +trace sudo.DEBUG.TRACE +.PP +debug sudo.DEBUG.DEBUG very extreme verbose debugging +.TE +.PP +\fIUsing the logging module\fR +.PP +Alternatively, a plugin can use the built in logging module of Python as well. +Sudo adds its log handler to the root logger, so by default all output of a +logger will get forwarded to sudo log system, as it would call sudo.debug. +.PP +The log handler of sudo will map each Python log level of a message to +the appropriate sudo debug level. +Note however, that sudo debug system will only get the messages not filtered +out by the Python loggers. +For example, the log level of the python logger will be an additional filter +for the log messages, and is usually very different from what level is set in sudo.conf +for the sudo debug system. +.SS "Debug example" +Sudo ships an example debug plugin by default. +To try it, register it by adding the following lines to +\fI@sysconfdir@/sudo.conf\fR: +.nf +.sp +.RS 6n +Plugin python_io python_plugin.so \e + ModulePath=@prefix@/share/doc/sudo/examples/example_debugging.py \e + ClassName=DebugDemoPlugin + +Debug python_plugin.so \e + /var/log/sudo_python_debug plugin@trace,c_calls@trace +.RE +.fi +.SS "Option conversion API" +The Python plugin API includes two convenience functions to +convert options in +\(lqkey=value\(rq +format to a dictionary and vice versa. +.TP 6n +options_as_dict +.nf +.RS 6n +options_as_dict(options) +.RE +.fi +.RS 6n +.sp +The function arguments are as follows: +.TP 6n +\fIoptions\fR +An iterable (tuple, list, etc.) of strings, each in +\(lqkey=value\(rq +format. +This is how the plugin API passes options and settings to a Python plugin. +.PP +The function returns the resulting dictionary. +Each string of the passed in +\fIoptions\fR +will be split at the first equal sign +(\(oq\&=\(cq) +into a +\fIkey\fR +and +\fIvalue\fR. +Dictionary keys will never contain this symbol (but values may). +.RE +.TP 6n +options_from_dict +.nf +.RS 6n +options_from_dict(options_dict) +.RE +.fi +.RS 6n +.sp +The function arguments are as follows: +.TP 6n +\fIoptions_dict\fR +A dictionary where both the key and the value are strings. +Note that the key should not contain an equal sign +(\(oq\&=\(cq), +otherwise the resulting string will have a different meaning. +However, this is not currently enforced. +.PP +The function returns a tuple containing the strings in +\(lqkey=value\(rq +form for each key and value in the +\fIoptions_dict\fR +dictionary passed in. +This is how the plugin API accepts options and settings. +.RE +.SH "PLUGIN API CHANGELOG (Python)" +None yet +.SH "LIMITATIONS" +Only a maximum number of 8 python I/O plugins can be loaded at once. +If +\fI@sysconfdir@/sudo.conf\fR +contains more, those will be rejected with a warning message. +.PP +The Event API and the hook function API is currently not accessible for Python plugins. +.SH "SEE ALSO" +sudo.conf(@mansectform@), +sudo_plugin(@mansectform@), +sudoers(@mansectform@), +sudo(@mansectsu@) +.SH "AUTHORS" +Many people have worked on +\fBsudo\fR +over the years; this version consists of code written primarily by: +.sp +.RS 6n +Todd C. Miller +.RE +.PP +See the CONTRIBUTORS file in the +\fBsudo\fR +distribution (https://www.sudo.ws/contributors.html) for an +exhaustive list of people who have contributed to +\fBsudo\fR. +.SH "BUGS" +Python plugin support is currently considered experimental. +.PP +If you feel you have found a bug in +\fBsudo\fR, +please submit a bug report at https://bugzilla.sudo.ws/ +.SH "SECURITY CONSIDERATIONS" +All Python plugin handling is implemented inside the +\fRpython_plugin.so\fR +dynamic plugin. +Therefore, if no Python plugin is registered in +sudo.conf(@mansectform@) +or the +\fIsudoers\fR +file, +\fBsudo\fR +will not load the Python interpreter or the Python libraries. +.PP +By default, a Python plugin can only import Python modules which are +owned by +\fIroot\fR +and are only writable by the owner. +The reason for this is to prevent a file getting imported accidentally +which is modifiable by a non-root user. +As +\fBsudo\fR +plugins run as +\fIroot\fR, +accidentally importing such file would make it possible for any user +(having write access) to execute any code with administrative rights. +.PP +However, during development of a plugin this might not be very convenient. +The +sudo.conf(@mansectform@) +\fRdeveloper_mode\fR +option can be used to disable it. +For example: +.RS 6n +Set developer_mode true +.RE +.PP +Please note that this creates a security risk, so it is not recommended +on critical systems such as a desktop machine for daily use, but is intended +to be used in development environments (VM, container, etc). +Before enabling developer mode, ensure you understand the implications. +.SH "SUPPORT" +Limited free support is available via the sudo-users mailing list, +see https://www.sudo.ws/mailman/listinfo/sudo-users to subscribe or +search the archives. +.SH "DISCLAIMER" +\fBsudo\fR +is provided +\(lqAS IS\(rq +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 +\fBsudo\fR +or https://www.sudo.ws/license.html for complete details. |