diff --git a/MANIFEST b/MANIFEST index 33fdd318e..11c26ed71 100644 --- a/MANIFEST +++ b/MANIFEST @@ -465,7 +465,6 @@ plugins/python/pyhelpers.h plugins/python/pyhelpers_cpychecker.h plugins/python/python_baseplugin.c plugins/python/python_convmessage.c -plugins/python/python_importblocker.c plugins/python/python_loghandler.c plugins/python/python_plugin.exp plugins/python/python_plugin_approval.c @@ -553,8 +552,6 @@ plugins/python/regress/testdata/check_loading_succeeds_with_missing_classname.st plugins/python/regress/testdata/check_multiple_approval_plugin_and_arguments.stderr plugins/python/regress/testdata/check_multiple_approval_plugin_and_arguments.stdout plugins/python/regress/testdata/check_python_plugins_do_not_affect_each_other.stdout -plugins/python/regress/testdata/sudo.conf.developer_mode -plugins/python/regress/testdata/sudo.conf.normal_mode plugins/python/regress/testhelpers.c plugins/python/regress/testhelpers.h plugins/python/sudo_python_debug.c diff --git a/plugins/python/Makefile.in b/plugins/python/Makefile.in index 1ea1e9a52..4cac26c72 100644 --- a/plugins/python/Makefile.in +++ b/plugins/python/Makefile.in @@ -115,13 +115,14 @@ install_gid = 0 SHELL = @SHELL@ -EXAMPLES = example_conversation.py example_debugging.py example_group_plugin.py example_io_plugin.py example_policy_plugin.py \ - example_audit_plugin.py example_approval_plugin.py +EXAMPLES = example_approval_plugin.py example_audit_plugin.py \ + example_conversation.py example_debugging.py \ + example_group_plugin.py example_io_plugin.py example_policy_plugin.py -OBJS = python_plugin_common.lo python_plugin_policy.lo python_plugin_io.lo python_plugin_group.lo pyhelpers.lo \ - python_loghandler.lo \ - python_importblocker.lo python_convmessage.lo sudo_python_module.lo sudo_python_debug.lo \ - python_baseplugin.lo python_plugin_audit.lo python_plugin_approval.lo +OBJS = python_plugin_common.lo python_plugin_policy.lo python_plugin_io.lo \ + python_plugin_group.lo pyhelpers.lo python_loghandler.lo \ + python_convmessage.lo sudo_python_module.lo sudo_python_debug.lo \ + python_baseplugin.lo python_plugin_audit.lo python_plugin_approval.lo IOBJS = $(OBJS:.lo=.i) @@ -342,26 +343,6 @@ python_convmessage.i: $(srcdir)/python_convmessage.c \ $(CC) -E -o $@ $(CPPFLAGS) $< python_convmessage.plog: python_convmessage.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/python_convmessage.c --i-file $< --output-file $@ -python_importblocker.lo: $(srcdir)/python_importblocker.c \ - $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ - $(incdir)/sudo_debug.h $(incdir)/sudo_plugin.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(srcdir)/pyhelpers.h \ - $(srcdir)/pyhelpers_cpychecker.h \ - $(srcdir)/sudo_python_debug.h \ - $(srcdir)/sudo_python_module.h $(top_builddir)/config.h - $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/python_importblocker.c -python_importblocker.i: $(srcdir)/python_importblocker.c \ - $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ - $(incdir)/sudo_debug.h $(incdir)/sudo_plugin.h \ - $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(srcdir)/pyhelpers.h \ - $(srcdir)/pyhelpers_cpychecker.h \ - $(srcdir)/sudo_python_debug.h \ - $(srcdir)/sudo_python_module.h $(top_builddir)/config.h - $(CC) -E -o $@ $(CPPFLAGS) $< -python_importblocker.plog: python_importblocker.i - rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/python_importblocker.c --i-file $< --output-file $@ python_loghandler.lo: $(srcdir)/python_loghandler.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ diff --git a/plugins/python/python_importblocker.c b/plugins/python/python_importblocker.c deleted file mode 100644 index 109a816a4..000000000 --- a/plugins/python/python_importblocker.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * SPDX-License-Identifier: ISC - * - * Copyright (c) 2019-2020 Robert Manner - * - * 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. - */ - -/* - * This is an open source non-commercial project. Dear PVS-Studio, please check it. - * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - */ - -#include "sudo_python_module.h" - -#include "sudo_util.h" - -#include -#include -#include - - -static int -_verify_import(const char *file_path) -{ - debug_decl(_verify_import, PYTHON_DEBUG_INTERNAL); - - // Check mode and owner similar to what we do in open_sudoers(). - // This is to help avoid loading a potentially insecure module. - struct stat sb; - if (stat(file_path, &sb) != 0) { - PyErr_Format(PyExc_ImportError, "Failed to stat file '%s'", file_path); - debug_return_int(SUDO_RC_ERROR); - } - - if (sb.st_uid != ROOT_UID) { - PyErr_Format(PyExc_ImportError, "File '%s' must be owned by uid %d", file_path, ROOT_UID); - debug_return_int(SUDO_RC_ERROR); - } - - if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) { - PyErr_Format(PyExc_ImportError, "File '%s' must be only be writable by owner", file_path); - debug_return_int(SUDO_RC_ERROR); - } - - debug_return_int(SUDO_RC_OK); -} - -static PyObject * -_sudo_ImportBlocker__Init(PyObject *py_self, PyObject *py_args) -{ - debug_decl(_sudo_ImportBlocker__Init, PYTHON_DEBUG_C_CALLS); - - py_debug_python_call("ImportBlocker", "__init__", py_args, NULL, PYTHON_DEBUG_C_CALLS); - - PyObject *py_meta_path = NULL; - if (!PyArg_UnpackTuple(py_args, "sudo.ImportBlocker.__init__", 2, 2, &py_self, &py_meta_path)) - goto cleanup; - - if (PyObject_SetAttrString(py_self, "meta_path", py_meta_path) != 0) - goto cleanup; - -cleanup: - if (PyErr_Occurred()) - debug_return_ptr(NULL); - - debug_return_ptr_pynone; -} - -static PyObject * -_sudo_ImportBlocker__find_spec(PyObject *py_self, PyObject *py_args) -{ - debug_decl(_sudo_ImportBlocker__find_spec, PYTHON_DEBUG_C_CALLS); - - PyObject *py_fullname = NULL, *py_path = NULL, *py_target = NULL, - *py_meta_path = NULL, *py_meta_path_iterator = NULL, - *py_finder = NULL, *py_spec = NULL, *py_loader = NULL, - *py_import_path = NULL; - - py_debug_python_call("ImportBlocker", "find_spec", py_args, NULL, PYTHON_DEBUG_C_CALLS); - - if (!PyArg_UnpackTuple(py_args, "sudo.ImportBlocker.find_spec", 2, 4, &py_self, &py_fullname, &py_path, &py_target)) - goto cleanup; - - py_meta_path = PyObject_GetAttrString(py_self, "meta_path"); - if (py_meta_path == NULL) - goto cleanup; - - py_meta_path_iterator = PyObject_GetIter(py_meta_path); - if (py_meta_path_iterator == NULL) - goto cleanup; - - while ((py_finder = PyIter_Next(py_meta_path_iterator)) != NULL) { - py_spec = PyObject_CallMethod(py_finder, "find_spec", "(OO)", - py_fullname, py_path, py_target); - if (py_spec == NULL) { - goto cleanup; - } - - if (py_spec != Py_None && PyObject_HasAttrString(py_spec, "loader")) { - // the finder could be resolved and contains a loader - py_loader = PyObject_GetAttrString(py_spec, "loader"); - if (py_loader != NULL && PyObject_HasAttrString(py_loader, "get_filename")) { - // there is a file associated with the import (.py, .so, etc) - py_import_path = PyObject_CallMethod(py_loader, "get_filename", ""); - const char *import_path = PyUnicode_AsUTF8(py_import_path); - - sudo_debug_printf(SUDO_DEBUG_DIAG, "ImportBlocker: verifying permissions " - "on file '%s'\n", import_path); - if (_verify_import(import_path) != SUDO_RC_OK) - goto cleanup; - - Py_CLEAR(py_import_path); - - } else { - sudo_debug_printf(SUDO_DEBUG_DIAG, "ImportBlocker: internal module import '%s'\n", - PyUnicode_AsUTF8(py_fullname)); - } - - goto cleanup; - } - - Py_CLEAR(py_spec); - Py_CLEAR(py_finder); - } - - Py_CLEAR(py_spec); - py_spec = Py_None; - Py_INCREF(py_spec); - -cleanup: - Py_CLEAR(py_meta_path_iterator); - Py_CLEAR(py_meta_path); - Py_CLEAR(py_finder); - Py_CLEAR(py_import_path); - Py_CLEAR(py_loader); - - if (PyErr_Occurred()) { - Py_CLEAR(py_spec); - debug_return_ptr(NULL); - } - - debug_return_ptr(py_spec); -} - -static PyMethodDef _sudo_ImportBlocker_class_methods[] = -{ - {"__init__", _sudo_ImportBlocker__Init, METH_VARARGS, ""}, - {"find_spec", _sudo_ImportBlocker__find_spec, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} -}; - -// This possibly can be replaced with PySys_AddAuditHook for python >= 3.8 -// -// This function is equivalent of the python call: -// sys.meta_path = [sudo.ImportBlocker(sys.meta_path)] -int -sudo_module_register_importblocker(void) -{ - debug_decl(sudo_module_register_importblocker, PYTHON_DEBUG_C_CALLS); - - int rc = SUDO_RC_ERROR; - - PyObject *py_meta_path = NULL, *py_import_blocker_cls = NULL, - *py_import_blocker = NULL; - - py_meta_path = PySys_GetObject("meta_path"); // note: borrowed reference - if (py_meta_path == NULL) { - PyErr_Format(sudo_exc_SudoException, "'sys.meta_path' is not available. " - "Unable to register import blocker hook which is meant to " - "verify that no such module get loaded by the sudo python plugins" - "which are writable by others than root."); - goto cleanup; - } - Py_INCREF(py_meta_path); - - py_import_blocker_cls = sudo_module_create_class("sudo.ImportBlocker", _sudo_ImportBlocker_class_methods, NULL); - if (py_import_blocker_cls == NULL) - goto cleanup; - - // call the constructor - py_import_blocker = PyObject_CallFunctionObjArgs(py_import_blocker_cls, py_meta_path, NULL); - if (py_import_blocker == NULL) - goto cleanup; - - Py_DECREF(py_meta_path); - py_meta_path = PyList_New(1); - if (py_meta_path == NULL) - goto cleanup; - - if (PyList_SetItem(py_meta_path, 0, py_import_blocker) != 0) - goto cleanup; - py_import_blocker = NULL; // list has stolen it - - if (PySys_SetObject("meta_path", py_meta_path) != 0) { - goto cleanup; - } - - rc = SUDO_RC_OK; - -cleanup: - Py_CLEAR(py_meta_path); - Py_CLEAR(py_import_blocker); - Py_CLEAR(py_import_blocker_cls); - - debug_return_int(rc); -} diff --git a/plugins/python/python_plugin_common.c b/plugins/python/python_plugin_common.c index 77db50e4b..0c174cb6c 100644 --- a/plugins/python/python_plugin_common.c +++ b/plugins/python/python_plugin_common.c @@ -518,10 +518,6 @@ python_plugin_init(struct PluginContext *plugin_ctx, char * const plugin_options } PyThreadState_Swap(plugin_ctx->py_interpreter); - if (!sudo_conf_developer_mode() && sudo_module_register_importblocker() < 0) { - goto cleanup; - } - if (sudo_module_set_default_loghandler() < 0) goto cleanup; diff --git a/plugins/python/regress/check_python_examples.c b/plugins/python/regress/check_python_examples.c index 122797a5e..dfdbe500e 100644 --- a/plugins/python/regress/check_python_examples.c +++ b/plugins/python/regress/check_python_examples.c @@ -118,10 +118,7 @@ init(void) VERIFY_TRUE(asprintf(&data.tmp_dir, TEMP_PATH_TEMPLATE) >= 0); VERIFY_NOT_NULL(mkdtemp(data.tmp_dir)); - // by default we test in developer mode, so the python plugin can be loaded sudo_conf_clear_paths(); - VERIFY_INT(sudo_conf_read(sudo_conf_developer_mode, SUDO_CONF_ALL), true); - VERIFY_TRUE(sudo_conf_developer_mode()); // some default values for the plugin open: data.settings = create_str_array(1, NULL); @@ -473,8 +470,7 @@ create_debug_config(const char *debug_spec) snprintf(config_path, sizeof(config_path), "%s/sudo.conf", data.tmp_dir); char *content = NULL; - if (asprintf(&content, "Set developer_mode true\n" - "Debug %s %s/debug.log %s\n", + if (asprintf(&content, "Debug %s %s/debug.log %s\n", "python_plugin.so", data.tmp_dir, debug_spec) < 0) { printf("Failed to allocate string\n"); @@ -629,16 +625,6 @@ check_loading_fails_with_wrong_path(void) return check_loading_fails("wrong_path"); } -static int -check_loading_fails_plugin_is_not_owned_by_root(void) -{ - sudo_conf_clear_paths(); - VERIFY_INT(sudo_conf_read(sudo_conf_normal_mode, SUDO_CONF_ALL), true); - - create_debugging_plugin_options(); - return check_loading_fails("not_owned_by_root"); -} - static int check_example_conversation_plugin_reason_log(int simulate_suspend, const char *description) { @@ -1553,7 +1539,6 @@ main(int argc, char *argv[]) RUN_TEST(check_loading_fails_with_missing_classname()); RUN_TEST(check_loading_fails_with_wrong_classname()); RUN_TEST(check_loading_fails_with_wrong_path()); - RUN_TEST(check_loading_fails_plugin_is_not_owned_by_root()); RUN_TEST(check_plugin_unload()); RUN_TEST(check_example_conversation_plugin_reason_log(false, "without_suspend")); diff --git a/plugins/python/regress/testdata/sudo.conf.developer_mode b/plugins/python/regress/testdata/sudo.conf.developer_mode deleted file mode 100644 index 4da2ad94e..000000000 --- a/plugins/python/regress/testdata/sudo.conf.developer_mode +++ /dev/null @@ -1 +0,0 @@ -Set developer_mode true diff --git a/plugins/python/regress/testdata/sudo.conf.normal_mode b/plugins/python/regress/testdata/sudo.conf.normal_mode deleted file mode 100644 index b972a6a0b..000000000 --- a/plugins/python/regress/testdata/sudo.conf.normal_mode +++ /dev/null @@ -1 +0,0 @@ -Set developer_mode false diff --git a/plugins/python/regress/testhelpers.c b/plugins/python/regress/testhelpers.c index 7675c7d17..42971bbb6 100644 --- a/plugins/python/regress/testhelpers.c +++ b/plugins/python/regress/testhelpers.c @@ -23,9 +23,6 @@ #include "testhelpers.h" -const char *sudo_conf_developer_mode = TESTDATA_DIR "sudo.conf.developer_mode"; -const char *sudo_conf_normal_mode = TESTDATA_DIR "sudo.conf.normal_mode"; - struct TestData data; /* diff --git a/plugins/python/regress/testhelpers.h b/plugins/python/regress/testhelpers.h index 39305ef07..2dd1d542f 100644 --- a/plugins/python/regress/testhelpers.h +++ b/plugins/python/regress/testhelpers.h @@ -31,9 +31,6 @@ #endif #define TESTDATA_DIR SRC_DIR "/regress/testdata/" -extern const char *sudo_conf_developer_mode; -extern const char *sudo_conf_normal_mode; - #define TEMP_PATH_TEMPLATE "/tmp/sudo_check_python_exampleXXXXXX" extern struct TestData { diff --git a/plugins/python/sudo_python_module.h b/plugins/python/sudo_python_module.h index a6c4f045c..dea8b83a6 100644 --- a/plugins/python/sudo_python_module.h +++ b/plugins/python/sudo_python_module.h @@ -36,9 +36,6 @@ extern PyObject *sudo_type_LogHandler; PyObject *sudo_module_create_class(const char *class_name, PyMethodDef *class_methods, PyObject *base_class); -CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION -int sudo_module_register_importblocker(void); - CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION int sudo_module_register_conv_message(PyObject *py_module);