plugins/python: use separate python interpreter for each plugin
On each plugin initialization we create a separate python interpreter which gets stored in the plugin_ctx. The main interpreter is stored in py_ctx and is used for creating more interpreters (if more plugins get loaded) and final python deinitialization. The "traceback" module import and the ImportBlocker initialization was moved, because it has to happen inside the plugin specific interpreters.
This commit is contained in:

committed by
Todd C. Miller

parent
99f8394182
commit
22c64f58c0
@@ -116,13 +116,19 @@ py_create_traceback_string(PyObject *py_traceback)
|
|||||||
|
|
||||||
char* traceback = NULL;
|
char* traceback = NULL;
|
||||||
|
|
||||||
if (py_ctx.py_traceback_module != NULL) {
|
|
||||||
PyObject *py_traceback_str_list = PyObject_CallMethod(py_ctx.py_traceback_module, "format_tb", "(O)", py_traceback);
|
PyObject *py_traceback_module = PyImport_ImportModule("traceback");
|
||||||
|
if (py_traceback_module == NULL) {
|
||||||
|
PyErr_Clear(); // do not care, we just won't show backtrace
|
||||||
|
} else {
|
||||||
|
PyObject *py_traceback_str_list = PyObject_CallMethod(py_traceback_module, "format_tb", "(O)", py_traceback);
|
||||||
|
|
||||||
if (py_traceback_str_list != NULL) {
|
if (py_traceback_str_list != NULL) {
|
||||||
traceback = py_join_str_list(py_traceback_str_list, "");
|
traceback = py_join_str_list(py_traceback_str_list, "");
|
||||||
Py_DECREF(py_traceback_str_list);
|
Py_DECREF(py_traceback_str_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Py_CLEAR(py_traceback_module);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_return_str(traceback ? traceback : strdup(""));
|
debug_return_str(traceback ? traceback : strdup(""));
|
||||||
|
@@ -43,8 +43,7 @@ struct PythonContext
|
|||||||
sudo_printf_t sudo_log;
|
sudo_printf_t sudo_log;
|
||||||
sudo_conv_t sudo_conv;
|
sudo_conv_t sudo_conv;
|
||||||
int open_plugin_count;
|
int open_plugin_count;
|
||||||
|
PyThreadState *py_main_interpreter;
|
||||||
PyObject *py_traceback_module;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct PythonContext py_ctx;
|
extern struct PythonContext py_ctx;
|
||||||
|
@@ -242,15 +242,9 @@ _python_plugin_register_plugin_in_py_ctx(void)
|
|||||||
|
|
||||||
PyImport_AppendInittab("sudo", sudo_module_init);
|
PyImport_AppendInittab("sudo", sudo_module_init);
|
||||||
Py_InitializeEx(0);
|
Py_InitializeEx(0);
|
||||||
|
py_ctx.py_main_interpreter = PyThreadState_Get();
|
||||||
if (!sudo_conf_developer_mode() && sudo_module_register_importblocker() < 0) {
|
} else {
|
||||||
py_log_last_error(NULL);
|
PyThreadState_Swap(py_ctx.py_main_interpreter);
|
||||||
debug_return_int(SUDO_RC_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
py_ctx.py_traceback_module = PyImport_ImportModule("traceback");
|
|
||||||
// if getting the traceback module fails, we just don't show tracebacks
|
|
||||||
PyErr_Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
++py_ctx.open_plugin_count;
|
++py_ctx.open_plugin_count;
|
||||||
@@ -267,6 +261,17 @@ python_plugin_init(struct PluginContext *plugin_ctx, char * const plugin_options
|
|||||||
if (_python_plugin_register_plugin_in_py_ctx() != SUDO_RC_OK)
|
if (_python_plugin_register_plugin_in_py_ctx() != SUDO_RC_OK)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
plugin_ctx->py_interpreter = Py_NewInterpreter();
|
||||||
|
if (plugin_ctx->py_interpreter == NULL) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
PyThreadState_Swap(plugin_ctx->py_interpreter);
|
||||||
|
|
||||||
|
if (!sudo_conf_developer_mode() && sudo_module_register_importblocker() < 0) {
|
||||||
|
py_log_last_error(NULL);
|
||||||
|
debug_return_int(SUDO_RC_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
const char *module_path = _lookup_value(plugin_options, "ModulePath");
|
const char *module_path = _lookup_value(plugin_options, "ModulePath");
|
||||||
if (module_path == NULL) {
|
if (module_path == NULL) {
|
||||||
py_sudo_log(SUDO_CONV_ERROR_MSG, "No python module path is specified. "
|
py_sudo_log(SUDO_CONV_ERROR_MSG, "No python module path is specified. "
|
||||||
@@ -321,13 +326,21 @@ python_plugin_deinit(struct PluginContext *plugin_ctx)
|
|||||||
Py_CLEAR(plugin_ctx->py_instance);
|
Py_CLEAR(plugin_ctx->py_instance);
|
||||||
Py_CLEAR(plugin_ctx->py_class);
|
Py_CLEAR(plugin_ctx->py_class);
|
||||||
Py_CLEAR(plugin_ctx->py_module);
|
Py_CLEAR(plugin_ctx->py_module);
|
||||||
|
|
||||||
|
if (plugin_ctx->py_interpreter != NULL) {
|
||||||
|
sudo_debug_printf(SUDO_DEBUG_TRACE, "deinit python interpreter for plugin\n");
|
||||||
|
Py_EndInterpreter(plugin_ctx->py_interpreter);
|
||||||
|
}
|
||||||
|
|
||||||
memset(plugin_ctx, 0, sizeof(*plugin_ctx));
|
memset(plugin_ctx, 0, sizeof(*plugin_ctx));
|
||||||
|
|
||||||
if (py_ctx.open_plugin_count <= 0) {
|
if (py_ctx.open_plugin_count <= 0) {
|
||||||
Py_CLEAR(py_ctx.py_traceback_module);
|
|
||||||
|
|
||||||
if (Py_IsInitialized()) {
|
if (Py_IsInitialized()) {
|
||||||
sudo_debug_printf(SUDO_DEBUG_NOTICE, "Closing: deinit python interpreter\n");
|
sudo_debug_printf(SUDO_DEBUG_NOTICE, "Closing: deinit python interpreter\n");
|
||||||
|
|
||||||
|
// we need to call finalize from the main interpreter
|
||||||
|
PyThreadState_Swap(py_ctx.py_main_interpreter);
|
||||||
|
|
||||||
Py_Finalize();
|
Py_Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,6 +426,8 @@ python_plugin_close(struct PluginContext *plugin_ctx, const char *python_callbac
|
|||||||
{
|
{
|
||||||
debug_decl(python_plugin_close, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_close, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
|
||||||
|
PyThreadState_Swap(plugin_ctx->py_interpreter);
|
||||||
|
|
||||||
if (!plugin_ctx->call_close) {
|
if (!plugin_ctx->call_close) {
|
||||||
sudo_debug_printf(SUDO_DEBUG_INFO, "Skipping close call, because there was no command run\n");
|
sudo_debug_printf(SUDO_DEBUG_INFO, "Skipping close call, because there was no command run\n");
|
||||||
|
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
#include "pyhelpers.h"
|
#include "pyhelpers.h"
|
||||||
|
|
||||||
struct PluginContext {
|
struct PluginContext {
|
||||||
|
PyThreadState *py_interpreter;
|
||||||
PyObject *py_module;
|
PyObject *py_module;
|
||||||
PyObject *py_class;
|
PyObject *py_class;
|
||||||
PyObject *py_instance;
|
PyObject *py_instance;
|
||||||
|
@@ -83,6 +83,7 @@ void
|
|||||||
python_plugin_group_cleanup(void)
|
python_plugin_group_cleanup(void)
|
||||||
{
|
{
|
||||||
debug_decl(python_plugin_group_cleanup, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_group_cleanup, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
PyThreadState_Swap(plugin_ctx.py_interpreter);
|
||||||
python_plugin_deinit(&plugin_ctx);
|
python_plugin_deinit(&plugin_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +92,8 @@ python_plugin_group_query(const char *user, const char *group, const struct pass
|
|||||||
{
|
{
|
||||||
debug_decl(python_plugin_group_query, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_group_query, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
|
||||||
|
PyThreadState_Swap(plugin_ctx.py_interpreter);
|
||||||
|
|
||||||
PyObject *py_pwd = py_from_passwd(pwd);
|
PyObject *py_pwd = py_from_passwd(pwd);
|
||||||
if (py_pwd == NULL) {
|
if (py_pwd == NULL) {
|
||||||
debug_return_int(SUDO_RC_ERROR);
|
debug_return_int(SUDO_RC_ERROR);
|
||||||
|
@@ -134,6 +134,8 @@ python_plugin_io_show_version(struct IOPluginContext *io_ctx, int verbose)
|
|||||||
{
|
{
|
||||||
debug_decl(python_plugin_io_show_version, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_io_show_version, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
|
||||||
|
PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
py_sudo_log(SUDO_CONV_INFO_MSG, "Python io plugin API version %d.%d\n", "%d.%d",
|
py_sudo_log(SUDO_CONV_INFO_MSG, "Python io plugin API version %d.%d\n", "%d.%d",
|
||||||
SUDO_API_VERSION_GET_MAJOR(PY_IO_PLUGIN_VERSION),
|
SUDO_API_VERSION_GET_MAJOR(PY_IO_PLUGIN_VERSION),
|
||||||
@@ -147,6 +149,7 @@ int
|
|||||||
python_plugin_io_log_ttyin(struct IOPluginContext *io_ctx, const char *buf, unsigned int len)
|
python_plugin_io_log_ttyin(struct IOPluginContext *io_ctx, const char *buf, unsigned int len)
|
||||||
{
|
{
|
||||||
debug_decl(python_plugin_io_log_ttyin, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_io_log_ttyin, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter);
|
||||||
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_ttyin),
|
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_ttyin),
|
||||||
Py_BuildValue("(s#)", buf, len)));
|
Py_BuildValue("(s#)", buf, len)));
|
||||||
}
|
}
|
||||||
@@ -155,6 +158,7 @@ int
|
|||||||
python_plugin_io_log_ttyout(struct IOPluginContext *io_ctx, const char *buf, unsigned int len)
|
python_plugin_io_log_ttyout(struct IOPluginContext *io_ctx, const char *buf, unsigned int len)
|
||||||
{
|
{
|
||||||
debug_decl(python_plugin_io_log_ttyout, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_io_log_ttyout, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter);
|
||||||
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_ttyout),
|
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_ttyout),
|
||||||
Py_BuildValue("(s#)", buf, len)));
|
Py_BuildValue("(s#)", buf, len)));
|
||||||
}
|
}
|
||||||
@@ -163,6 +167,7 @@ int
|
|||||||
python_plugin_io_log_stdin(struct IOPluginContext *io_ctx, const char *buf, unsigned int len)
|
python_plugin_io_log_stdin(struct IOPluginContext *io_ctx, const char *buf, unsigned int len)
|
||||||
{
|
{
|
||||||
debug_decl(python_plugin_io_log_stdin, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_io_log_stdin, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter);
|
||||||
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_stdin),
|
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_stdin),
|
||||||
Py_BuildValue("(s#)", buf, len)));
|
Py_BuildValue("(s#)", buf, len)));
|
||||||
}
|
}
|
||||||
@@ -171,6 +176,7 @@ int
|
|||||||
python_plugin_io_log_stdout(struct IOPluginContext *io_ctx, const char *buf, unsigned int len)
|
python_plugin_io_log_stdout(struct IOPluginContext *io_ctx, const char *buf, unsigned int len)
|
||||||
{
|
{
|
||||||
debug_decl(python_plugin_io_log_stdout, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_io_log_stdout, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter);
|
||||||
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_stdout),
|
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_stdout),
|
||||||
Py_BuildValue("(s#)", buf, len)));
|
Py_BuildValue("(s#)", buf, len)));
|
||||||
}
|
}
|
||||||
@@ -179,6 +185,7 @@ int
|
|||||||
python_plugin_io_log_stderr(struct IOPluginContext *io_ctx, const char *buf, unsigned int len)
|
python_plugin_io_log_stderr(struct IOPluginContext *io_ctx, const char *buf, unsigned int len)
|
||||||
{
|
{
|
||||||
debug_decl(python_plugin_io_log_stderr, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_io_log_stderr, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter);
|
||||||
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_stderr),
|
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_stderr),
|
||||||
Py_BuildValue("(s#)", buf, len)));
|
Py_BuildValue("(s#)", buf, len)));
|
||||||
}
|
}
|
||||||
@@ -187,6 +194,7 @@ int
|
|||||||
python_plugin_io_change_winsize(struct IOPluginContext *io_ctx, unsigned int line, unsigned int cols)
|
python_plugin_io_change_winsize(struct IOPluginContext *io_ctx, unsigned int line, unsigned int cols)
|
||||||
{
|
{
|
||||||
debug_decl(python_plugin_io_change_winsize, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_io_change_winsize, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter);
|
||||||
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(change_winsize),
|
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(change_winsize),
|
||||||
Py_BuildValue("(ii)", line, cols)));
|
Py_BuildValue("(ii)", line, cols)));
|
||||||
}
|
}
|
||||||
@@ -195,6 +203,7 @@ int
|
|||||||
python_plugin_io_log_suspend(struct IOPluginContext *io_ctx, int signo)
|
python_plugin_io_log_suspend(struct IOPluginContext *io_ctx, int signo)
|
||||||
{
|
{
|
||||||
debug_decl(python_plugin_io_log_suspend, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_io_log_suspend, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
PyThreadState_Swap(BASE_CTX(io_ctx)->py_interpreter);
|
||||||
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_suspend),
|
debug_return_int(python_plugin_api_rc_call(BASE_CTX(io_ctx), CALLBACK_PYNAME(log_suspend),
|
||||||
Py_BuildValue("(i)", signo)));
|
Py_BuildValue("(i)", signo)));
|
||||||
}
|
}
|
||||||
|
@@ -96,6 +96,8 @@ python_plugin_policy_check(int argc, char * const argv[],
|
|||||||
debug_decl(python_plugin_policy_check, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_policy_check, PYTHON_DEBUG_CALLBACKS);
|
||||||
int rc = SUDO_RC_ERROR;
|
int rc = SUDO_RC_ERROR;
|
||||||
|
|
||||||
|
PyThreadState_Swap(plugin_ctx.py_interpreter);
|
||||||
|
|
||||||
*command_info_out = *argv_out = *user_env_out = NULL;
|
*command_info_out = *argv_out = *user_env_out = NULL;
|
||||||
|
|
||||||
PyObject *py_argv = py_str_array_to_tuple_with_count(argc, argv);
|
PyObject *py_argv = py_str_array_to_tuple_with_count(argc, argv);
|
||||||
@@ -169,6 +171,8 @@ python_plugin_policy_list(int argc, char * const argv[], int verbose, const char
|
|||||||
{
|
{
|
||||||
debug_decl(python_plugin_policy_list, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_policy_list, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
|
||||||
|
PyThreadState_Swap(plugin_ctx.py_interpreter);
|
||||||
|
|
||||||
PyObject *py_argv = py_str_array_to_tuple_with_count(argc, argv);
|
PyObject *py_argv = py_str_array_to_tuple_with_count(argc, argv);
|
||||||
if (py_argv == NULL) {
|
if (py_argv == NULL) {
|
||||||
sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: Failed to create argv argument for the python call\n", __PRETTY_FUNCTION__);
|
sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: Failed to create argv argument for the python call\n", __PRETTY_FUNCTION__);
|
||||||
@@ -187,6 +191,8 @@ python_plugin_policy_version(int verbose)
|
|||||||
{
|
{
|
||||||
debug_decl(python_plugin_policy_version, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_policy_version, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
|
||||||
|
PyThreadState_Swap(plugin_ctx.py_interpreter);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
py_sudo_log(SUDO_CONV_INFO_MSG, "Python policy plugin API version %d.%d\n", "%d.%d",
|
py_sudo_log(SUDO_CONV_INFO_MSG, "Python policy plugin API version %d.%d\n", "%d.%d",
|
||||||
SUDO_API_VERSION_GET_MAJOR(PY_POLICY_PLUGIN_VERSION),
|
SUDO_API_VERSION_GET_MAJOR(PY_POLICY_PLUGIN_VERSION),
|
||||||
@@ -200,6 +206,7 @@ int
|
|||||||
python_plugin_policy_validate(void)
|
python_plugin_policy_validate(void)
|
||||||
{
|
{
|
||||||
debug_decl(python_plugin_policy_validate, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_policy_validate, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
PyThreadState_Swap(plugin_ctx.py_interpreter);
|
||||||
debug_return_int(python_plugin_api_rc_call(&plugin_ctx, CALLBACK_PYNAME(validate), NULL));
|
debug_return_int(python_plugin_api_rc_call(&plugin_ctx, CALLBACK_PYNAME(validate), NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +214,7 @@ void
|
|||||||
python_plugin_policy_invalidate(int remove)
|
python_plugin_policy_invalidate(int remove)
|
||||||
{
|
{
|
||||||
debug_decl(python_plugin_policy_invalidate, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_policy_invalidate, PYTHON_DEBUG_CALLBACKS);
|
||||||
|
PyThreadState_Swap(plugin_ctx.py_interpreter);
|
||||||
python_plugin_api_rc_call(&plugin_ctx, CALLBACK_PYNAME(invalidate),
|
python_plugin_api_rc_call(&plugin_ctx, CALLBACK_PYNAME(invalidate),
|
||||||
Py_BuildValue("(i)", remove));
|
Py_BuildValue("(i)", remove));
|
||||||
debug_return;
|
debug_return;
|
||||||
@@ -217,6 +225,7 @@ python_plugin_policy_init_session(struct passwd *pwd, char **user_env[])
|
|||||||
{
|
{
|
||||||
debug_decl(python_plugin_policy_init_session, PYTHON_DEBUG_CALLBACKS);
|
debug_decl(python_plugin_policy_init_session, PYTHON_DEBUG_CALLBACKS);
|
||||||
int rc = SUDO_RC_ERROR;
|
int rc = SUDO_RC_ERROR;
|
||||||
|
PyThreadState_Swap(plugin_ctx.py_interpreter);
|
||||||
PyObject *py_pwd = NULL, *py_user_env = NULL, *py_result = NULL;
|
PyObject *py_pwd = NULL, *py_user_env = NULL, *py_result = NULL;
|
||||||
|
|
||||||
py_pwd = py_from_passwd(pwd);
|
py_pwd = py_from_passwd(pwd);
|
||||||
|
Reference in New Issue
Block a user