From 4ad362dd8fbd1c0809444dc41f69bd4b5eddf17c Mon Sep 17 00:00:00 2001 From: Robert Manner Date: Thu, 5 Dec 2019 15:21:25 +0100 Subject: [PATCH] plugins/python: example plugin demonstrating conversation and debug API --- plugins/python/example_conversation.py | 69 ++++++++++++++++++++++++++ plugins/python/example_debugging.py | 58 ++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 plugins/python/example_conversation.py create mode 100644 plugins/python/example_debugging.py diff --git a/plugins/python/example_conversation.py b/plugins/python/example_conversation.py new file mode 100644 index 000000000..3be1ed6ce --- /dev/null +++ b/plugins/python/example_conversation.py @@ -0,0 +1,69 @@ +import sudo +import signal + +class ReasonLoggerIOPlugin(sudo.Plugin): + """ + An example sudo plugin demonstrating how to use the sudo conversation API. + + From the python plugin, you can ask something from the user using the + "sudo.conv" function. It expects one or more "sudo.ConvMessage" instances + which specifies how the interaction has to look like. + + sudo.ConvMessage has the following fields (see help(sudo.ConvMessage)): + msg_type: int Specifies the type of the conversation. + See sudo.CONV_* constants below. + timeout: int The maximum amount of time for the conversation in seconds. + After the timeout exceeds, the "sudo.conv" function will + raise sudo.ConversationInterrupted exception. + msg: str The message to display for the user. + + To specify the conversion type you can use the following constants: + sudo.CONV_PROMPT_ECHO_OFF + sudo.CONV_PROMPT_ECHO_ON + sudo.CONV_ERROR_MSG + sudo.CONV_INFO_MSG + sudo.CONV_PROMPT_MASK + sudo.CONV_PROMPT_ECHO_OK + sudo.CONV_PREFER_TTY + """ + def open(self, argv, command_info): + try: + conv_timeout = 120 # in seconds + sudo.log_info("Please provide your reason for executing {}".format(argv)) + + # We ask two questions, the second is not visible on screen, so the user + # can hide a hidden message in case of criminals are forcing him for + # running the command. + # You can either specify the arguments in strict order (timeout being optional), or use named arguments. + message1 = sudo.ConvMessage(sudo.CONV_PROMPT_ECHO_ON, "Reason: ", conv_timeout) + message2 = sudo.ConvMessage(msg="Secret reason: ", timeout=conv_timeout, msg_type=sudo.CONV_PROMPT_MASK) + reply1, reply2 = sudo.conv(message1, message2, + on_suspend=self.on_conversation_suspend, + on_resume=self.on_conversation_resume) + + with open("/tmp/sudo_reasons.txt", "a") as file: + print("Executed", ' '.join(argv), file=file) + print("Reason:", reply1, file=file) + print("Hidden reason:", reply2, file=file) + + except sudo.ConversationInterrupted: + sudo.log_error("You did not answer in time") + return sudo.RC_REJECT + + def on_conversation_suspend(self, signum): + # This is just an example of how to do something on conversation suspend. + # You can skip specifying 'on_suspend' argument if there is no need + sudo.log_info("conversation suspend: signal", self._signal_name(signum)) + + def on_conversation_resume(self, signum): + # This is just an example of how to do something on conversation resume. + # You can skip specifying 'on_resume' argument if there is no need + sudo.log_info("conversation resume: signal was", self._signal_name(signum)) + + # helper functions: + @classmethod + def _signal_name(cls, signum): + try: + return "{} ({})".format(signal.Signals(signum).name, signum) + except Exception: + return "{}".format(signum) diff --git a/plugins/python/example_debugging.py b/plugins/python/example_debugging.py new file mode 100644 index 000000000..e2fd84906 --- /dev/null +++ b/plugins/python/example_debugging.py @@ -0,0 +1,58 @@ +import sudo + +class DebugDemoPlugin(sudo.Plugin): + """ + An example sudo plugin demonstrating the debugging capabilities. + + You can install it as an extra IO plugin for example by adding the following line to sudo.conf: + Plugin python_io python_plugin.so ModulePath=/example_debugging.py ClassName=DebugDemoPlugin + + To see the plugin's debug output, use the following line in sudo.conf: + Debug python_plugin.so /var/log/sudo_python_debug plugin@trace,c_calls@trace + ^ ^-- the options for the logging + ^----- the output will be placed here + + The options for the logging is in format of multiple "subsystem@level" separated by commas (","). + The most interesting subsystems are: + plugin Shows each call of sudo.debug API in the log + - py_calls Logs whenever a C function calls into the python module. (For example calling this __init__ function.) + c_calls Logs whenever python calls into a C sudo API function + + You can also specify "all" as subsystem name to get the debug messages of all subsystems. + + Other subsystems available: + internal logs internal functions of the python language wrapper plugin + sudo_cb logs when sudo calls into its plugin API + load logs python plugin loading / unloading + + Log levels + crit sudo.DEBUG_CRIT --> only cricital messages + err sudo.DEBUG_ERROR + warn sudo.DEBUG_WARN + notice sudo.DEBUG_NOTICE + diag sudo.DEBUG_DIAG + info sudo.DEBUG_INFO + trace sudo.DEBUG_TRACE + debug sudo.DEBUG_DEBUG --> very extreme verbose debugging + + See the sudo.conf manual for more details ("man sudo.conf"). + + """ + def __init__(self, plugin_options, **kwargs): + # Specify: "py_calls@info" debug option to show the call to this constructor and the arguments passed in + + # Specifying "plugin@error" debug option will show this message (or any more verbose level) + sudo.debug(sudo.DEBUG_ERROR, "My demo purpose plugin shows this ERROR level debug message") + + # Specifying "plugin@info" debug option will show this message (or any more verbose level) + sudo.debug(sudo.DEBUG_INFO, "My demo purpose plugin shows this INFO level debug message") + + # If you raise the level to info or below, the call of the debug will also be logged. + # An example output you will see in the debug log file: + # Dec 5 15:19:19 sudo[123040] __init__ @ /.../example_debugging.py:54 debugs: + # Dec 5 15:19:19 sudo[123040] My demo purpose plugin shows this ERROR level debug message + + # Specify: "c_calls@diag" debug option to show this call and its arguments + # If you specify info debug level instead ("c_call@info"), + # you will also see the python function and line from which you called the 'options_as_dict' function. + self.plugin_options = sudo.options_as_dict(plugin_options)