summaryrefslogtreecommitdiffstats
path: root/docs/sudo_plugin_python.mdoc.in
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 13:14:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 13:14:46 +0000
commit025c439e829e0db9ac511cd9c1b8d5fd53475ead (patch)
treefa6986b4690f991613ffb97cea1f6942427baf5d /docs/sudo_plugin_python.mdoc.in
parentInitial commit. (diff)
downloadsudo-025c439e829e0db9ac511cd9c1b8d5fd53475ead.tar.xz
sudo-025c439e829e0db9ac511cd9c1b8d5fd53475ead.zip
Adding upstream version 1.9.15p5.upstream/1.9.15p5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'docs/sudo_plugin_python.mdoc.in')
-rw-r--r--docs/sudo_plugin_python.mdoc.in1556
1 files changed, 1556 insertions, 0 deletions
diff --git a/docs/sudo_plugin_python.mdoc.in b/docs/sudo_plugin_python.mdoc.in
new file mode 100644
index 0000000..f6edd47
--- /dev/null
+++ b/docs/sudo_plugin_python.mdoc.in
@@ -0,0 +1,1556 @@
+.\"
+.\" SPDX-License-Identifier: ISC
+.\"
+.\" Copyright (c) 2019-2021 Robert Manner <robert.manner@oneidentity.com>
+.\" Copyright (c) 2019-2023 Todd C. Miller <Todd.Miller@sudo.ws>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" 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.
+.\"
+.Dd January 16, 2023
+.Dt SUDO_PLUGIN_PYTHON @mansectform@
+.Os Sudo @PACKAGE_VERSION@
+.Sh NAME
+.Nm sudo_plugin_python
+.Nd Sudo Plugin API (Python)
+.Sh DESCRIPTION
+Starting with version 1.9,
+.Nm sudo
+plugins can be written in python.
+The API closely follows the C
+.Nm sudo
+plugin API described by
+.Xr sudo_plugin @mansectform@ .
+.Pp
+The supported plugins types are:
+.Pp
+.Bl -bullet -compact -offset 1n -width 1n
+.It
+Policy plugin
+.It
+I/O plugin
+.It
+Audit plugin
+.It
+Approval plugin
+.It
+Group provider plugin
+.El
+.Pp
+Python plugin support needs to be explicitly enabled at build time
+with the configure option
+.Dq --enable-python .
+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
+.Em sudo.Plugin .
+The
+.Em sudo.Plugin
+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:
+.Bd -literal -offset 4n
+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
+.Ed
+.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,
+.Nm sudo
+will create an instance of this class and call the methods.
+The actual methods required depend on the type of the plugin,
+but most return an
+.Vt int
+result code, as documented in
+.Xr sudo_plugin @mansectform@ ,
+that indicates whether or not the method was successful.
+The Python sudo module defines the following constants to improve readability:
+.Bl -column "sudo.RC.USAGE_ERROR" "XXX" -offset 4n
+.It Sy Define Ta Sy Value
+.It Dv sudo.RC.OK Ta 1
+.It Dv sudo.RC.ACCEPT Ta 1
+.It Dv sudo.RC.REJECT Ta 0
+.It Dv sudo.RC.ERROR Ta \-1
+.It Dv sudo.RC.USAGE_ERROR Ta \-2
+.El
+.Pp
+If a function returns
+.Dv None
+(for example, if it does not call return),
+it will be considered to have returned
+.Dv sudo.RC.OK .
+If an exception is raised (other than sudo.PluginException), the
+backtrace will be shown to the user and the plugin function will return
+.Dv sudo.RC.ERROR .
+If that is not acceptable, you must catch the exception and handle it yourself.
+.Pp
+Instead of just returning
+.Dv sudo.RC.ERROR
+or
+.Dv sudo.RC.REJECT
+result code the plugin can also provide a message describing the problem.
+This can be done by raising one of the special exceptions:
+.Bd -literal -offset 4n
+raise sudo.PluginError("Message")
+raise sudo.PluginReject("Message")
+.Ed
+.Pp
+This added message will be used by the audit plugins.
+Both exceptions inherit from
+.Dv sudo.PluginException
+.Ss Python Plugin Loader
+Running the Python interpreter and bridging between C and Python is
+handled by the
+.Nm sudo
+plugin
+.Pa @python_plugin@ .
+This shared object can be loaded like any other dynamic
+.Nm sudo
+plugin and should receive the path and the class name of the Python
+plugin it is loading as arguments.
+.Pp
+Example usage in
+.Xr sudo.conf @mansectform@ :
+.Bd -literal -offset 4n
+Plugin python_policy @python_plugin@ ModulePath=<path> ClassName=<class>
+Plugin python_io @python_plugin@ ModulePath=<path> ClassName=<class>
+Plugin python_audit @python_plugin@ ModulePath=<path> ClassName=<class>
+Plugin python_approval @python_plugin@ ModulePath=<path> ClassName=<class>
+.Ed
+.Pp
+Example group provider plugin usage in the
+.Em sudoers
+file:
+.Bd -literal -offset 4n
+Defaults group_plugin="@python_plugin@ ModulePath=<path> ClassName=<class>"
+.Ed
+.Pp
+The plugin arguments are as follows:
+.Bl -tag -width 4n
+.It 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,
+.Pa @plugindir@/python .
+The parent directory of
+.Em ModulePath
+will be appended to Python's module search path (there is currently no
+way to force Python to load a module from a fully-qualified path).
+It is good practice to use a prefix for the module file that is unlikely
+to conflict with other installed Python modules, for example,
+.Pa sudo_policy.py .
+Otherwise, if the there is an installed Python module with the same
+file name as the sudo Python plugin file (without the directory),
+the wrong file will be loaded.
+.It 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.
+.El
+.Ss Policy plugin API
+Policy plugins must be registered in
+.Xr sudo.conf @mansectform@ .
+For example:
+.Bd -literal -offset 4n
+Plugin python_policy @python_plugin@ ModulePath=<path> ClassName=<class>
+.Ed
+.Pp
+Currently, only a single policy plugin may be specified in
+.Xr sudo.conf @mansectform@ .
+.Pp
+A policy plugin may have the following member functions:
+.Bl -tag -width 4n
+.It Fa constructor
+.Bd -literal -compact
+__init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...],
+ version: str, user_info: Tuple[str, ...],
+ plugin_options: Tuple[str, ...])
+.Ed
+.Pp
+Implementing this function is optional.
+The default constructor will set the keyword arguments it receives
+as member variables in the object.
+.Pp
+The constructor matches the
+.Fn open
+function in the C
+.Nm sudo
+plugin API.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa user_env
+The user's environment as a tuple of strings in
+.Dq key=value
+format.
+.It Fa settings
+A tuple of user-supplied
+.Em sudo
+settings in the form of
+.Dq key=value
+strings.
+.It Fa version
+The version of the Python Policy Plugin API.
+.It Fa user_info
+A tuple of information about the user running the command in the form of
+.Dq key=value
+strings.
+.It Fa plugin_options
+The plugin options passed as arguments in the
+.Xr sudo.conf @mansectform@
+plugin registration.
+This is a tuple of strings, usually (but not necessarily) in
+.Dq key=value
+format.
+.El
+.Pp
+The
+.Fn sudo.options_as_dict
+convenience function can be used to convert
+.Dq key=value
+pairs to a dictionary.
+For a list of recognized keys and their supported values,
+see the policy plugin
+.Fn open
+documentation in
+.Xr sudo_plugin @mansectform@ .
+.It Fa check_policy
+.Bd -literal -compact
+check_policy(self, argv: Tuple[str, ...], env_add: Tuple[str, ...])
+.Ed
+.Pp
+The
+.Fn check_policy
+function is called by
+.Nm sudo
+to determine whether the user is allowed to run the specified command.
+Implementing this function is mandatory for a policy plugin.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa argv
+A tuple describing the command the user wishes to run.
+.It Fa env_add
+Additional environment variables specified by the user on the command line in
+the form of a tuple of
+.Dq key=value
+pairs.
+The
+.Fn sudo.options_as_dict
+convenience function can be used to convert them to a dictionary.
+.El
+.Pp
+This function should return a result code or a tuple in the following format:
+.Bd -literal -offset 4n
+return (rc, command_info_out, argv_out, user_env_out)
+.Ed
+.Pp
+The tuple values are as follows:
+.Bl -tag -width 4n
+.It Fa rc
+The result of the policy check, one of the
+.Dv sudo.RC.*
+constants.
+.Dv sudo.RC.ACCEPT
+if the command is allowed,
+.Dv sudo.RC.REJECT
+if not allowed,
+.Dv sudo.RC.ERROR
+for a general error, or
+.Dv sudo.RC.USAGE_ERROR
+for a usage error.
+.It Fa command_info_out
+Optional (only required when the command is accepted).
+Information about the command being run in the form of
+.Dq key=value
+strings.
+.Pp
+To accept a command, at the very minimum the plugin must set in the
+.Em command ,
+.Em runas_uid ,
+and
+.Em runas_gid
+keys.
+.Pp
+For a list of recognized keys and supported values,
+see the
+.Fn check_policy
+documentation in
+.Xr sudo_plugin @mansectform@ .
+.It Fa argv_out
+Optional (only required when the command is accepted).
+The arguments to pass to the
+.Xr execve 2
+system call when executing the command.
+.It Fa user_env_out
+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
+.Dq key=value
+format.
+.El
+.It Fa init_session
+.Bd -literal -compact
+init_session(self, user_pwd: Tuple, user_env: Tuple[str, ...])
+.Ed
+.Pp
+Perform session setup (optional).
+The
+.Fn init_session
+function is called before
+.Nm sudo
+sets up the
+execution environment for the command before any user-ID or group-ID changes.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa user_pwd
+A tuple describing the user's passwd entry.
+Convertible to
+.Vt pwd.struct_passwd or
+.Dv None
+if the user is not present in the password database.
+.Pp
+Example conversion:
+.Bd -literal -compact -offset indent
+user_pwd = pwd.struct_passwd(user_pwd) if user_pwd else None
+.Ed
+.It Fa user_env
+The environment the command will run in.
+This is a tuple of strings in
+.Dq key=value
+format.
+.El
+.Pp
+This function should return a result code or a tuple in the following format:
+.Bd -literal -offset 4n
+return (rc, user_env_out)
+.Ed
+.Pp
+The tuple values are as follows:
+.Bl -tag -width 4n
+.It Fa rc
+The result of the session init, one of the
+.Dv sudo.RC.*
+constants.
+.Dv sudo.RC.OK
+on success, 0 on failure, or
+.Dv sudo.RC.ERROR
+if an error occurred.
+.It Fa user_env_out
+Optional.
+If the
+.Fn init_session
+function needs to modify the user environment, it can return the new
+environment in
+.Fa user_env_out .
+If this is omitted, no changes will be made to
+.Fa user_env .
+.El
+.It Fa list
+.Bd -literal -compact
+list(self, argv: Tuple[str, ...], is_verbose: int, user: str)
+.Ed
+.Pp
+List available privileges for the invoking user.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa argv
+If not set to
+.Dv None ,
+an argument vector describing a command the user wishes to check
+against the policy.
+.It Fa is_verbose
+Flag indicating whether to list in verbose mode or not.
+.It Fa user
+The name of a different user to list privileges for if the policy allows it.
+If
+.Dv None ,
+the plugin should list the privileges of the invoking user.
+.El
+.It Fa validate
+.Bd -literal -compact
+validate(self)
+.Ed
+.Pp
+For policy plugins that cache authentication credentials, this function is used to validate and cache the credentials (optional).
+.It Fa invalidate
+.Bd -literal -compact
+invalidate(self, remove: int)
+.Ed
+.Pp
+For policy plugins that cache authentication credentials, this function is used to invalidate the credentials (optional).
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa remove
+If this flag is set, the plugin may remove the credentials instead of simply
+invalidating them.
+.El
+.It Fa show_version
+.Bd -literal -compact
+show_version(self, is_verbose: int)
+.Ed
+.Pp
+Display the plugin version information to the user.
+The
+.Fn sudo.log_info
+function should be used.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa is_verbose
+A flag to indicate displaying more verbose information.
+Currently this is 1 if
+.Ql sudo -V
+is run as the root user.
+.El
+.It Fa close
+.Bd -literal -compact
+close(self, exit_status: int, error: int)
+.Ed
+.Pp
+Called when a command finishes executing.
+.Pp
+Works the same as the
+.Fn close
+function in the C
+.Nm sudo
+plugin API, except that it only gets called if
+.Nm sudo
+attempts to execute the command.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa exit_status
+The exit status of the command if was executed, otherwise \-1.
+.It Fa error
+If the command could not be executed, this is set to the value of
+errno set by the
+.Xr execve 2
+system call, otherwise 0.
+.El
+.El
+.Ss Policy plugin example
+Sudo ships with an example Python policy plugin.
+To try it, register it by adding the following lines to
+.Pa @sysconfdir@/sudo.conf :
+.Bd -literal
+Plugin python_policy @python_plugin@ \e
+ ModulePath=@EXAMPLES@/example_policy_plugin.py \e
+ ClassName=SudoPolicyPlugin
+.Ed
+.Pp
+Only one policy plugin can be enabled at a time so you must disable
+any other policy plugin listed in
+.Pa @sysconfdir@/sudo.conf ,
+such as
+.Xr sudoers @mansectform@ .
+.Ss I/O plugin API
+I/O plugins must be registered in
+.Xr sudo.conf @mansectform@ .
+For example:
+.Bd -literal -offset 4n
+Plugin python_io @python_plugin@ ModulePath=<path> ClassName=<class>
+.Ed
+.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:
+.Bl -tag -width 4n
+.It Fa constructor
+.Bd -literal -compact
+__init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...],
+ version: str, user_info: Tuple[str, ...],
+ plugin_options: Tuple[str, ...])
+.Ed
+.Pp
+Implementing this function is optional.
+The default constructor will set the keyword arguments it receives
+as member variables in the object.
+.Pp
+The constructor matches the
+.Fn open
+function in the C
+.Nm sudo
+plugin API.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa user_env
+The user's environment as a tuple of strings in
+.Dq key=value
+format.
+.It Fa settings
+A tuple of user-supplied
+.Em sudo
+settings in the form of
+.Dq key=value
+strings.
+.It Fa version
+The version of the Python I/O Plugin API.
+.It Fa user_info
+A tuple of information about the user running the command in the form of
+.Dq key=value
+strings.
+.It Fa plugin_options
+The plugin options passed as arguments in the
+.Xr sudo.conf @mansectform@
+plugin registration.
+This is a tuple of strings, usually (but not necessarily) in
+.Dq key=value
+format.
+.El
+.Pp
+The
+.Fn sudo.options_as_dict
+convenience function can be used to convert
+.Dq key=value
+pairs to a dictionary.
+For a list of recognized keys and their supported values,
+see the I/O plugin
+.Fn open
+documentation in
+.Xr sudo_plugin @mansectform@ .
+.It Fa open
+.Bd -literal -compact
+open(self, argv: Tuple[str, ...],
+ command_info: Tuple[str, ...]) -> int
+.Ed
+.Pp
+Receives the command the user wishes to run.
+.Pp
+Works the same as the
+.Fn open
+function in the C
+.Nm sudo
+plugin API except that:
+.Pp
+.Bl -bullet -compact -offset 1n -width 1n
+.It
+It only gets called when there is a command to be executed
+(and not for a version query for example).
+.It
+Other arguments of the C API
+.Fn open
+function are received through the constructor.
+.El
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa argv
+A tuple of the arguments describing the command the user wishes to run.
+.It Fa command_info
+Information about the command being run in the form of
+.Dq key=value
+strings.
+.El
+.Pp
+The
+.Fn sudo.options_as_dict
+convenience function can be used to convert
+.Dq key=value
+pairs to a dictionary.
+For a list of recognized keys and their supported values,
+see the I/O plugin
+.Fn open
+documentation in
+.Xr sudo_plugin @mansectform@ .
+.Pp
+The
+.Fn open
+function should return a result code, one of the
+.Dv sudo.RC.*
+constants.
+If the function returns
+.Dv sudo.RC.REJECT ,
+no I/O will be sent to the plugin.
+.It Fa log_ttyin , log_ttyout , log_stdin , log_stdout , log_stderr
+.Bd -literal -compact
+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
+.Ed
+.Pp
+Receive the user input or output of the terminal device and
+application standard input, standard output, or standard error.
+See the matching calls in
+.Xr sudo_plugin @mansectform@ .
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa buf
+The input (or output) buffer in the form of a string.
+.El
+.Pp
+The function should return a result code, one of the
+.Dv sudo.RC.*
+constants.
+.Pp
+If
+.Dv sudo.RC.ERROR
+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.
+.Pp
+If an input logging function rejects the data by returning
+.Dv sudo.RC.REJECT ,
+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
+.Dv sudo.RC.REJECT ,
+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.
+.It Fa change_winsize
+.Bd -literal -compact
+change_winsize(self, line: int, cols: int) -> int
+.Ed
+.Pp
+Called whenever the window size of the terminal changes.
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa line
+The number of lines of the terminal.
+.It Fa cols
+The number of columns of the terminal.
+.El
+.It Fa log_suspend
+.Bd -literal -compact
+log_suspend(self, signo: int) -> int
+.Ed
+Called whenever a command is suspended or resumed.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa signo
+The number of the signal that caused the command to be suspended or
+.Dv SIGCONT
+if the command was resumed.
+.El
+.It Fa show_version
+.Bd -literal -compact
+show_version(self, is_verbose: int)
+.Ed
+Display the plugin version information to the user.
+The
+.Fn sudo.log_info
+function should be used.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa is_verbose
+A flag to indicate displaying more verbose information.
+Currently this is 1 if
+.Ql sudo -V
+is run as the root user.
+.El
+.It Fa close
+.Bd -literal -compact
+close(self, exit_status: int, error: int) -> None
+.Ed
+Called when a command finishes execution.
+.Pp
+Works the same as the
+.Fn close
+function in the C
+.Nm sudo
+plugin API, except that it only gets called if
+.Nm sudo
+attempts to execute the command.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa exit_status
+The exit status of the command if was executed, otherwise \-1.
+.It Fa error
+If the command could not be executed, this is set to the value of
+errno set by the
+.Xr execve 2
+system call, otherwise 0.
+.El
+.El
+.Ss I/O plugin example
+Sudo ships with a Python I/O plugin example.
+To try it, register it by adding the following lines to
+.Pa @sysconfdir@/sudo.conf :
+.Bd -literal -offset 4n
+Plugin python_io @python_plugin@ \e
+ ModulePath=@EXAMPLES@/example_io_plugin.py \e
+ ClassName=SudoIOPlugin
+.Ed
+.Ss Audit plugin API
+Audit plugins must be registered in
+.Xr sudo.conf @mansectform@ .
+For example:
+.Bd -literal -offset 4n
+Plugin python_audit @python_plugin@ ModulePath=<path> ClassName=<class>
+.Ed
+.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 which are optional):
+.Bl -tag -width 4n
+.It Fa constructor
+.Bd -literal -compact
+__init__(self, user_env: Tuple[str, ...], settings: Tuple[str, ...],
+ version: str, user_info: Tuple[str, ...], plugin_options: Tuple[str, ...])
+.Ed
+.Pp
+The default constructor will set the keyword arguments it receives
+as member variables in the object.
+.Pp
+The constructor matches the
+.Fn open
+function in the C
+.Nm sudo
+plugin API.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa user_env
+The user's environment as a tuple of strings in
+.Dq key=value
+format.
+.It Fa settings
+A tuple of user-supplied
+.Em sudo
+settings in the form of
+.Dq key=value
+strings.
+.It Fa version
+The version of the Python Audit Plugin API.
+.It Fa user_info
+A tuple of information about the user running the command in the form of
+.Dq key=value
+strings.
+.It Fa plugin_options
+The plugin options passed as arguments in the
+.Xr sudo.conf @mansectform@
+plugin registration.
+This is a tuple of strings, usually (but not necessarily) in
+.Dq key=value
+format.
+.El
+.It Fa open
+.Bd -literal -compact
+open(self, submit_optind: int,
+ submit_argv: Tuple[str, ...]) -> int
+.Ed
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa submit_optind
+The index into
+.Fa submit_argv
+that corresponds to the first entry that is not a command line option.
+.It Fa submit_argv
+The argument vector sudo was invoked with, including all command line options.
+.El
+.It Fa close
+.Bd -literal -compact
+close(self, status_type: int, status: int) -> None
+.Ed
+.Pp
+Called when sudo is finished, shortly before it exits.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa status_type
+The type of status being passed.
+One of the
+.Dv sudo.EXIT_REASON.*
+constants.
+.It Fa status
+Depending on the value of
+.Fa status_type ,
+this value is either
+ignored, the command's exit status as returned by the
+.Xr wait 2
+system call, the value of
+.Va errno
+set by the
+.Xr execve 2
+system call, or the value of
+.Va errno
+resulting from an error in the
+.Nm sudo
+front-end.
+.El
+.It Fa show_version
+.Bd -literal -compact
+show_version(self, is_verbose: int) -> int
+.Ed
+.Pp
+Display the plugin version information to the user.
+The
+.Fn sudo.log_info
+function should be used.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa is_verbose
+A flag to indicate displaying more verbose information.
+Currently this is 1 if
+.Ql sudo -V
+is run as the root user.
+.El
+.It Fa accept
+.Bd -literal -compact
+accept(self, plugin_name: str, plugin_type: int, command_info: Tuple[str, ...],
+ run_argv: Tuple[str, ...], run_envp: Tuple[str, ...]) -> int
+.Ed
+.Pp
+This function is called when a command or action is accepted by a policy
+or approval plugin.
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It plugin_name
+The name of the plugin that accepted the command or
+.Dq sudo
+for the
+.Nm sudo
+front-end.
+.It plugin_type
+The type of plugin that accepted the command, currently either
+.Dv sudo.PLUGIN_TYPE.POLICY ,
+.Dv sudo.PLUGIN_TYPE.APPROVAL ,
+or
+.Dv sudo.PLUGIN_TYPE.SUDO .
+The
+.Fn accept
+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,
+.Fa command_info
+may include information from an I/O logging plugin as well.
+.Pp
+Typically, an audit plugin is interested in either the accept status from
+the
+.Nm sudo
+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
+.Fn accept
+and
+.Fn reject
+functions will
+.Em both
+be called.
+.It command_info
+A vector of information describing the command being run.
+See the
+.Xr sudo_plugin @mansectform@
+manual for possible values.
+.It run_argv
+Argument vector describing a command that will be run.
+.It run_envp
+The environment the command will be run with.
+.El
+.It Fa reject
+.Bd -literal -compact
+reject(self, plugin_name: str, plugin_type: int, audit_msg: str,
+ command_info: Tuple[str, ...]) -> int
+.Ed
+.Pp
+This function is called when a command or action is rejected by the policy
+plugin.
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It plugin_name
+The name of the plugin that rejected the command.
+.It plugin_type
+The type of plugin that rejected the command, currently either
+.Dv sudo.PLUGIN_TYPE.POLICY ,
+.Dv sudo.PLUGIN_TYPE.APPROVAL ,
+or
+.Dv sudo.PLUGIN_TYPE.IO .
+.Pp
+Unlike the
+.Fn accept
+function, the
+.Fn reject
+function is not called on behalf of the
+.Nm sudo
+front-end.
+.It 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
+.Dv None .
+.It command_info
+A vector of information describing the rejected command.
+See the
+.Xr sudo_plugin @mansectform@
+manual for possible values.
+.El
+.It Fa error
+.Bd -literal -compact
+error(self, plugin_name: str, plugin_type: int, audit_msg: str,
+ command_info: Tuple[str, ...]) -> int
+.Ed
+.Pp
+This function is called when a plugin or the
+.Nm sudo
+front-end returns an error.
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It plugin_name
+The name of the plugin that generated the error or
+.Dq sudo
+for the
+.Nm sudo
+front-end.
+.It plugin_type
+The type of plugin that generated the error, or
+.Dv SUDO_FRONT_END
+for the
+.Nm sudo
+front-end.
+.It audit_msg
+An optional string describing the plugin error.
+If the plugin did not provide a description, it will be
+.Dv None .
+.It command_info
+A vector of information describing the command.
+See the
+.Xr sudo_plugin @mansectform@
+manual for possible values.
+.El
+.El
+.Ss Audit plugin example
+Sudo ships with a Python Audit plugin example.
+To try it, register it by adding the following lines to
+.Pa @sysconfdir@/sudo.conf :
+.Bd -literal -offset 4n
+Plugin python_audit @python_plugin@ \e
+ ModulePath=@EXAMPLES@/example_audit_plugin.py \e
+ ClassName=SudoAuditPlugin
+.Ed
+.Pp
+It will log the plugin accept / reject / error results to the output.
+.Ss Approval plugin API
+Approval plugins must be registered in
+.Xr sudo.conf @mansectform@ .
+For example:
+.Bd -literal -offset 4n
+Plugin python_approval @python_plugin@ ModulePath=<path> ClassName=<class>
+.Ed
+.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:
+.Bl -tag -width 4n
+.It Fa constructor
+.Bd -literal -compact
+__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, ...])
+.Ed
+.Pp
+Optional.
+The default constructor will set the keyword arguments it receives
+as member variables in the object.
+.Pp
+The constructor matches the
+.Fn open
+function in the C
+.Nm sudo
+plugin API.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa user_env
+The user's environment as a tuple of strings in
+.Dq key=value
+format.
+.It Fa settings
+A tuple of user-supplied
+.Em sudo
+settings in the form of
+.Dq key=value
+strings.
+.It Fa version
+The version of the Python Approval Plugin API.
+.It Fa user_info
+A tuple of information about the user running the command in the form of
+.Dq key=value
+strings.
+.It Fa plugin_options
+The plugin options passed as arguments in the
+.Xr sudo.conf @mansectform@
+plugin registration.
+This is a tuple of strings, usually (but not necessarily) in
+.Dq key=value
+format.
+.It Fa submit_optind
+The index into
+.Fa submit_argv
+that corresponds to the first entry that is not a command line option.
+.It Fa submit_argv
+The argument vector sudo was invoked with, including all command line options.
+.El
+.It Fa show_version
+.Bd -literal -compact
+show_version(self, is_verbose: int) -> int
+.Ed
+.Pp
+Display the version.
+(Same as for all the other plugins.)
+.It Fa check
+.Bd -literal -compact
+check(self, command_info: Tuple[str, ...], run_argv: Tuple[str, ...],
+ run_env: Tuple[str, ...]) -> int
+.Ed
+.Pp
+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:
+.Bd -literal -offset 4n
+raise sudo.PluginReject("some message")
+.Ed
+.Pp
+with the message describing the problem.
+In the latter case, the audit plugins will get the description.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It command_info
+A vector of information describing the command that will run.
+See the
+.Xr sudo_plugin @mansectform@
+manual for possible values.
+.It run_argv
+Argument vector describing a command that will be run.
+.It run_env
+The environment the command will be run with.
+.El
+.El
+.Ss Approval plugin example
+Sudo ships with a Python Approval plugin example.
+To try it, register it by adding the following lines to
+.Pa @sysconfdir@/sudo.conf :
+.Bd -literal -offset 4n
+Plugin python_approval @python_plugin@ \e
+ ModulePath=@EXAMPLES@/example_approval_plugin.py \e
+ ClassName=BusinessHoursApprovalPlugin
+.Ed
+.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
+.Xr sudoers @mansectform@
+file.
+For example:
+.Bd -literal -offset 4n
+Defaults group_plugin="@python_plugin@ ModulePath=<path> ClassName=<class>"
+.Ed
+.Pp
+Currently, only a single group plugin can be registered in
+.Em sudoers .
+.Pp
+A group provider plugin may have the following member functions:
+.Bl -tag -width 4n
+.It Fa constructor
+.Bd -literal -compact
+__init__(self, args: Tuple[str, ...], version: str)
+.Ed
+.Pp
+Implementing this function is optional.
+The default constructor will set the keyword arguments it receives
+as member variables in the object.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa args
+The plugin options passed as arguments in the
+.Em sudoers
+file plugin registration.
+All the arguments are free form strings (not necessarily in
+.Dq key=value
+format).
+.It Fa version
+The version of the Python Group Plugin API.
+.El
+.It Fa query
+.Bd -literal -compact
+query(self, user: str, group: str, user_pwd: Tuple)
+.Ed
+.Pp
+The
+.Fn query
+function is used to ask the group plugin whether
+.Fa user
+is a member of
+.Fa group .
+This method is required.
+.El
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa user
+The name of the user being looked up in the external group database.
+.It Fa group
+The name of the group being queried.
+.It Fa user_pwd
+The password database entry for the user, if any.
+If
+.Fa user
+is not present in the password database,
+.Fa user_pwd
+will be
+.Dv NULL .
+.El
+.Ss Group plugin example
+Sudo ships with a Python group plugin example.
+To try it, register it in the
+.Em sudoers
+file by adding the following lines:
+.Bd -literal -offset 4n
+Defaults group_plugin="@python_plugin@ \e
+ ModulePath=@EXAMPLES@/example_group_plugin.py \e
+ ClassName=SudoGroupPlugin"
+.Ed
+.Pp
+The example plugin will tell
+.Nm sudo
+that the user
+.Em test
+is part of the non-Unix group
+.Em mygroup .
+If you add a rule that uses this group, it will affect the
+.Em test
+user.
+For example:
+.Bd -literal -offset 4n
+%:mygroup ALL=(ALL) NOPASSWD: ALL
+.Ed
+.Pp
+Will allow user
+.Em test
+to run
+.Nm sudo
+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
+.Fn sudo.conv
+function which displays one or more messages described by the
+.Dv sudo.ConvMessage
+class.
+This is the Python equivalent of the
+.Fn conversation
+function in the C
+.Nm 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
+.Dv sudo.ConvMessage
+class specifies how the user interaction should occur:
+.Bd -literal -offset 4n
+sudo.ConvMessage(msg_type: int, msg: str, timeout: int)
+.Ed
+.Pp
+.Dv sudo.ConvMessage
+member variables:
+.Bl -tag -width 4n
+.It Fa msg_type
+Specifies the type of the conversation.
+See the
+.Dv sudo.CONV.*
+constants below.
+.It Fa msg
+The message to display to the user.
+The caller must include a trailing newline in
+.Fa msg
+if one is to be displayed.
+.It Fa timeout
+Optional.
+The maximum amount of time for the conversation in seconds.
+If the timeout is exceeded, the
+.Fn sudo.conv
+function will raise a
+.Dv sudo.ConversationInterrupted
+exception.
+The default is to wait forever (no timeout).
+.El
+.Pp
+To specify the message type, the following constants are available:
+.Pp
+.Bl -bullet -compact -offset 1n -width 1n
+.It
+.Dv sudo.CONV.PROMPT_ECHO_OFF
+.It
+.Dv sudo.CONV.PROMPT_ECHO_ON
+.It
+.Dv sudo.CONV.ERROR_MSG
+.It
+.Dv sudo.CONV.INFO_MSG
+.It
+.Dv sudo.CONV.PROMPT_MASK
+.It
+.Dv sudo.CONV.PROMPT_ECHO_OK
+.It
+.Dv sudo.CONV.PREFER_TTY
+.El
+.Pp
+See the
+.Xr sudo_plugin @mansectform@
+manual for a description of the message types.
+.Pp
+The
+.Fn sudo.conv
+function performs the actual user interaction:
+.Bd -literal -offset 4n
+sudo.conv(message(s), on_suspend=suspend_function,
+ on_resume=resume_function)
+.Ed
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa message(s)
+One of more messages (of type
+.Dv sudo.ConvMessage ) ,
+each describing a conversation.
+At least one message is required.
+.It Fa on_suspend
+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.
+.It Fa on_resume
+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.
+.El
+.Pp
+The
+.Fn sudo.conv
+function can raise the following exceptions:
+.Bl -tag -width 4n
+.It Dv sudo.SudoException
+If the conversation fails, for example when the conversation function is not
+available.
+.It Dv sudo.ConversationInterrupted
+If the conversation function returns an error, e.g., the timeout passed
+or the user interrupted the conversation by pressing control-C.
+.El
+.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
+.Pa @sysconfdir@/sudo.conf :
+.Bd -literal -offset 4n
+Plugin python_io @python_plugin@ \e
+ ModulePath=@EXAMPLES@/example_conversation.py \e
+ ClassName=ReasonLoggerIOPlugin
+.Ed
+.Ss Information / error display API
+.Bd -literal
+sudo.log_info(string(s), sep=" ", end="\en")
+sudo.log_error(string(s), sep=" ", end="\en")
+.Ed
+.Pp
+To display information to the user, the
+.Fn sudo.log_info
+function can be used.
+To display error messages, use
+.Fn sudo.log_error .
+The syntax is similar to the Python
+.Fn print
+function.
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa string(s)
+One or more strings to display.
+.It Fa sep
+An optional string which will be used as the separator between the
+specified strings.
+The default is a space character,
+.Pq Sq \ .
+.It Fa end
+An optional string which will be displayed at the end of the message.
+The default is a new line character
+.Pq Sq \en .
+.El
+.Ss Debug API
+Debug messages are not visible to the user and are only logged debugging
+is explicitly enabled in
+.Xr sudo.conf @mansectform@ .
+Python plugins can use the
+.Fn sudo.debug
+function to make use of
+.Nm sudo Ns No 's
+debug system.
+.Pp
+.Em Enabling debugging in sudo.conf
+.Pp
+To enable debug messages, add a
+.Em Debug
+line to
+.Xr sudo.conf @mansectform@
+with the program set to
+.Pa @python_plugin@ .
+For example, to store debug output in
+.Pa @log_dir@/sudo_python_debug ,
+use a line like the following:
+.Bd -literal -offset 4n
+Debug @python_plugin@ @log_dir@/sudo_python_debug \e
+ plugin@trace,c_calls@trace
+.Ed
+.Pp
+The debug options are in the form of multiple
+.Dq subsystem@level
+strings, separated by commas
+.Pq Sq \&, .
+For example to just see the debug output of
+.Fn sudo.debug
+calls, use:
+.Bd -literal -offset 4n
+Debug @python_plugin@ @log_dir@/sudo_python_debug plugin@trace
+.Ed
+.Pp
+See
+.Xr sudo_conf @mansectform@
+for more details.
+.Pp
+The most interesting subsystems for Python plugin development are:
+.Bl -tag -width 4n
+.It Em plugin
+Logs each
+.Fn sudo.debug
+API call.
+.It Em py_calls
+Logs whenever a C function calls into the python module.
+For example, calling the
+.Fn __init__
+function.
+.It Em c_calls
+Logs whenever python calls into a C
+.Nm sudo
+API function.
+.It Em internal
+Logs internal functions of the python language wrapper plugin.
+.It Em sudo_cb
+Logs when
+.Nm sudo
+calls into the python plugin API.
+.It Em load
+Logs python plugin loading / unloading events.
+.El
+.Pp
+You can also specify
+.Dq all
+as the subsystem name to log debug messages for all subsystems.
+.Pp
+The
+.Fn sudo.debug
+function is defined as:
+.Bd -literal -offset 4n
+sudo.debug(level, message(s))
+.Ed
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa level
+an integer, use one of the log level constants below
+.It Fa message(s)
+one or more messages to log
+.El
+.Pp
+.Em Available log levels:
+.Bl -column "name in sudo.conf" "Python constant" "only critical messages"
+.It Sy sudo.conf name Ta Sy Python constant Ta Sy description
+.It crit Ta Dv sudo.DEBUG.CRIT Ta only critical messages
+.It err Ta Dv sudo.DEBUG.ERROR Ta
+.It warn Ta Dv sudo.DEBUG.WARN Ta
+.It notice Ta Dv sudo.DEBUG.NOTICE Ta
+.It diag Ta Dv sudo.DEBUG.DIAG Ta
+.It info Ta Dv sudo.DEBUG.INFO Ta
+.It trace Ta Dv sudo.DEBUG.TRACE Ta
+.It debug Ta Dv sudo.DEBUG.DEBUG Ta very extreme verbose debugging
+.El
+.Pp
+.Em Using the logging module
+.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.
+The sudo debug system will only receive messages that are 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 with an example debug plugin.
+To try it, register it by adding the following lines to
+.Pa @sysconfdir@/sudo.conf :
+.Bd -literal -offset 4n
+Plugin python_io @python_plugin@ \e
+ ModulePath=@EXAMPLES@/example_debugging.py \e
+ ClassName=DebugDemoPlugin
+
+Debug @python_plugin@ \e
+ @log_dir@/sudo_python_debug plugin@trace,c_calls@trace
+.Ed
+.Ss Option conversion API
+The Python plugin API includes two convenience functions to
+convert options in
+.Dq key=value
+format to a dictionary and vice versa.
+.Bl -tag -width 4n
+.It options_as_dict
+.Bd -literal -compact
+options_as_dict(options)
+.Ed
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa options
+An iterable (tuple, list, etc.) of strings, each in
+.Dq key=value
+format.
+This is how the plugin API passes options and settings to a Python plugin.
+.El
+.Pp
+The function returns the resulting dictionary.
+Each string of the passed in
+.Fa options
+will be split at the first equal sign
+.Pq Sq \&=
+into a
+.Em key
+and
+.Em value .
+Dictionary keys will never contain this symbol (but values may).
+.It options_from_dict
+.Bd -literal -compact
+options_from_dict(options_dict)
+.Ed
+.Pp
+The function arguments are as follows:
+.Bl -tag -width 4n
+.It Fa options_dict
+A dictionary where both the key and the value are strings.
+The key should not contain an equal sign
+.Pq Sq \&= ,
+otherwise the resulting string will have a different meaning.
+However, this is not currently enforced.
+.El
+.Pp
+The function returns a tuple containing the strings in
+.Dq key=value
+form for each key and value in the
+.Fa options_dict
+dictionary passed in.
+This is how the plugin API accepts options and settings.
+.El
+.Sh PLUGIN API CHANGELOG (Python)
+None yet
+.Sh LIMITATIONS
+A maximum of 8 python I/O plugins can be loaded at once.
+If
+.Pa @sysconfdir@/sudo.conf
+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
+.Xr sudo.conf @mansectform@ ,
+.Xr sudo_plugin @mansectform@ ,
+.Xr sudoers @mansectform@ ,
+.Xr sudo @mansectsu@
+.Sh AUTHORS
+Many people have worked on
+.Nm sudo
+over the years; this version consists of code written primarily by:
+.Bd -ragged -offset indent
+.An Todd C. Miller
+.Ed
+.Pp
+See the CONTRIBUTORS.md file in the
+.Nm sudo
+distribution (https://www.sudo.ws/about/contributors/) for an
+exhaustive list of people who have contributed to
+.Nm sudo .
+.Sh BUGS
+Python plugin support is currently considered experimental.
+.Pp
+If you believe you have found a bug in
+.Nm sudo ,
+you can submit a bug report at https://bugzilla.sudo.ws/
+.Sh SECURITY CONSIDERATIONS
+All Python plugin handling is implemented inside the
+.Pa @python_plugin@
+dynamic plugin.
+Therefore, if no Python plugin is registered in
+.Xr sudo.conf @mansectform@
+or the
+.Em sudoers
+file,
+.Nm sudo
+will not load the Python interpreter or the Python libraries.
+.Pp
+As
+.Nm sudo
+runs plugins as
+.Sy root ,
+care must be taken when writing Python plugins to avoid creating
+security vulnerabilities, just as one would when writing plugins
+in C.
+.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
+.Nm sudo
+is provided
+.Dq 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.md file distributed with
+.Nm sudo
+or https://www.sudo.ws/about/license/ for complete details.