Path: blob/main/plugins/python/regress/check_python_examples.c
1532 views
/*1* SPDX-License-Identifier: ISC2*3* Copyright (c) 2020 Robert Manner <[email protected]>4*5* Permission to use, copy, modify, and distribute this software for any6* purpose with or without fee is hereby granted, provided that the above7* copyright notice and this permission notice appear in all copies.8*9* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES10* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF11* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR12* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES13* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN14* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF15* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.16*/1718#include "testhelpers.h"19#include <unistd.h>2021#include <sudo_dso.h>2223#define DECL_PLUGIN(type, variable_name) \24static struct type *variable_name = NULL; \25static struct type variable_name ## _original2627#define RESTORE_PYTHON_PLUGIN(variable_name) \28memcpy(variable_name, &(variable_name ## _original), sizeof(variable_name ## _original))2930#define SAVE_PYTHON_PLUGIN(variable_name) \31memcpy(&(variable_name ## _original), variable_name, sizeof(variable_name ## _original))3233static const char *python_plugin_so_path = NULL;34static void *python_plugin_handle = NULL;35DECL_PLUGIN(io_plugin, python_io);36DECL_PLUGIN(policy_plugin, python_policy);37DECL_PLUGIN(approval_plugin, python_approval);38DECL_PLUGIN(audit_plugin, python_audit);39DECL_PLUGIN(sudoers_group_plugin, group_plugin);4041static struct passwd example_pwd;42static bool verbose;4344static int _init_symbols(void);45static int _unlink_symbols(void);4647static void48create_plugin_options(const char *module_name, const char *class_name, const char *extra_option)49{50char opt_module_path[PATH_MAX + 256];51char opt_classname[PATH_MAX + 256];52snprintf(opt_module_path, sizeof(opt_module_path),53"ModulePath=" SRC_DIR "/%s.py", module_name);5455snprintf(opt_classname, sizeof(opt_classname), "ClassName=%s", class_name);5657str_array_free(&data.plugin_options);58size_t count = 3 + (extra_option != NULL);59data.plugin_options = create_str_array(count, opt_module_path,60opt_classname, extra_option, NULL);61}6263static void64create_io_plugin_options(const char *log_path)65{66char opt_logpath[PATH_MAX + 16];67snprintf(opt_logpath, sizeof(opt_logpath), "LogPath=%s", log_path);68create_plugin_options("example_io_plugin", "SudoIOPlugin", opt_logpath);69}7071static void72create_debugging_plugin_options(void)73{74create_plugin_options("example_debugging", "DebugDemoPlugin", NULL);75}7677static void78create_audit_plugin_options(const char *extra_argument)79{80create_plugin_options("example_audit_plugin", "SudoAuditPlugin", extra_argument);81}8283static void84create_conversation_plugin_options(void)85{86char opt_logpath[PATH_MAX + 16];87snprintf(opt_logpath, sizeof(opt_logpath), "LogPath=%s", data.tmp_dir);88create_plugin_options("example_conversation", "ReasonLoggerIOPlugin", opt_logpath);89}9091static void92create_policy_plugin_options(void)93{94create_plugin_options("example_policy_plugin", "SudoPolicyPlugin", NULL);95}9697static int98init(void)99{100// always start each test from clean state101memset(&data, 0, sizeof(data));102103memset(&example_pwd, 0, sizeof(example_pwd));104example_pwd.pw_name = (char *)"pw_name";105example_pwd.pw_passwd = (char *)"pw_passwd";106example_pwd.pw_gecos = (char *)"pw_gecos";107example_pwd.pw_shell = (char *)"pw_shell";108example_pwd.pw_dir = (char *)"pw_dir";109example_pwd.pw_uid = (uid_t)1001;110example_pwd.pw_gid = (gid_t)101;111112VERIFY_TRUE(asprintf(&data.tmp_dir, TEMP_PATH_TEMPLATE) >= 0);113VERIFY_NOT_NULL(mkdtemp(data.tmp_dir));114115sudo_conf_clear_paths();116117// some default values for the plugin open:118data.settings = create_str_array(1, NULL);119data.user_info = create_str_array(1, NULL);120data.command_info = create_str_array(1, NULL);121data.plugin_argc = 0;122data.plugin_argv = create_str_array(1, NULL);123data.user_env = create_str_array(1, NULL);124125VERIFY_TRUE(_init_symbols());126return true;127}128129static int130cleanup(int success)131{132if (!success) {133printf("\nThe output of the plugin:\n%s", data.stdout_str);134printf("\nThe error output of the plugin:\n%s", data.stderr_str);135}136137VERIFY_TRUE(rmdir_recursive(data.tmp_dir));138if (data.tmp_dir2) {139VERIFY_TRUE(rmdir_recursive(data.tmp_dir2));140}141142free(data.tmp_dir);143free(data.tmp_dir2);144145str_array_free(&data.settings);146str_array_free(&data.user_info);147str_array_free(&data.command_info);148str_array_free(&data.plugin_argv);149str_array_free(&data.user_env);150str_array_free(&data.plugin_options);151152return true;153}154155static int156check_example_io_plugin_version_display(int is_verbose)157{158const char *errstr = NULL;159create_io_plugin_options(data.tmp_dir);160161VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,162data.user_info, data.command_info, data.plugin_argc, data.plugin_argv, data.user_env,163data.plugin_options, &errstr), SUDO_RC_OK);164VERIFY_INT(python_io->show_version(is_verbose), SUDO_RC_OK);165166python_io->close(0, 0); // this should not call the python plugin close as there was no command run invocation167168if (is_verbose) {169// Note: the exact python version is environment dependent170VERIFY_STR_CONTAINS(data.stdout_str, "Python interpreter version:");171*strstr(data.stdout_str, "Python interpreter version:") = '\0';172VERIFY_STDOUT(expected_path("check_example_io_plugin_version_display_full.stdout"));173} else {174VERIFY_STDOUT(expected_path("check_example_io_plugin_version_display.stdout"));175}176177VERIFY_STDERR(expected_path("check_example_io_plugin_version_display.stderr"));178VERIFY_FILE("sudo.log", expected_path("check_example_io_plugin_version_display.stored"));179180return true;181}182183static int184check_example_io_plugin_command_log(void)185{186const char *errstr = NULL;187create_io_plugin_options(data.tmp_dir);188189str_array_free(&data.plugin_argv);190data.plugin_argc = 2;191data.plugin_argv = create_str_array(3, "id", "--help", NULL);192193str_array_free(&data.command_info);194data.command_info = create_str_array(3, "command=/bin/id", "runas_uid=0", NULL);195196VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,197data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,198data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);199VERIFY_PTR(errstr, NULL);200VERIFY_INT(python_io->log_stdin("some standard input", strlen("some standard input"), &errstr), SUDO_RC_OK);201VERIFY_PTR(errstr, NULL);202VERIFY_INT(python_io->log_stdout("some standard output", strlen("some standard output"), &errstr), SUDO_RC_OK);203VERIFY_PTR(errstr, NULL);204VERIFY_INT(python_io->log_stderr("some standard error", strlen("some standard error"), &errstr), SUDO_RC_OK);205VERIFY_PTR(errstr, NULL);206VERIFY_INT(python_io->log_suspend(SIGTSTP, &errstr), SUDO_RC_OK);207VERIFY_PTR(errstr, NULL);208VERIFY_INT(python_io->log_suspend(SIGCONT, &errstr), SUDO_RC_OK);209VERIFY_PTR(errstr, NULL);210VERIFY_INT(python_io->change_winsize(200, 100, &errstr), SUDO_RC_OK);211VERIFY_PTR(errstr, NULL);212VERIFY_INT(python_io->log_ttyin("some tty input", strlen("some tty input"), &errstr), SUDO_RC_OK);213VERIFY_PTR(errstr, NULL);214VERIFY_INT(python_io->log_ttyout("some tty output", strlen("some tty output"), &errstr), SUDO_RC_OK);215VERIFY_PTR(errstr, NULL);216217python_io->close(1, 0); // successful execution, command returned 1218219VERIFY_STDOUT(expected_path("check_example_io_plugin_command_log.stdout"));220VERIFY_STDERR(expected_path("check_example_io_plugin_command_log.stderr"));221VERIFY_FILE("sudo.log", expected_path("check_example_io_plugin_command_log.stored"));222223return true;224}225226typedef struct io_plugin * (io_clone_func)(void);227228static int229check_example_io_plugin_command_log_multiple(void)230{231const char *errstr = NULL;232233// verify multiple python io plugin symbols are available234io_clone_func *python_io_clone = (io_clone_func *)sudo_dso_findsym(python_plugin_handle, "python_io_clone");235VERIFY_PTR_NE(python_io_clone, NULL);236237struct io_plugin *python_io2 = NULL;238239for (int i = 0; i < 7; ++i) {240python_io2 = (*python_io_clone)();241VERIFY_PTR_NE(python_io2, NULL);242VERIFY_PTR_NE(python_io2, python_io);243}244245// open the first plugin and let it log to tmp_dir246create_io_plugin_options(data.tmp_dir);247248str_array_free(&data.plugin_argv);249data.plugin_argc = 2;250data.plugin_argv = create_str_array(3, "id", "--help", NULL);251252str_array_free(&data.command_info);253data.command_info = create_str_array(3, "command=/bin/id", "runas_uid=0", NULL);254255VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,256data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,257data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);258VERIFY_PTR(errstr, NULL);259260// For verifying the error message of no more plugin. It should be displayed only once.261VERIFY_PTR((*python_io_clone)(), NULL);262VERIFY_PTR((*python_io_clone)(), NULL);263264// open the second plugin with another log directory265VERIFY_TRUE(asprintf(&data.tmp_dir2, TEMP_PATH_TEMPLATE) >= 0);266VERIFY_NOT_NULL(mkdtemp(data.tmp_dir2));267create_io_plugin_options(data.tmp_dir2);268269str_array_free(&data.plugin_argv);270data.plugin_argc = 1;271data.plugin_argv = create_str_array(2, "whoami", NULL);272273str_array_free(&data.command_info);274data.command_info = create_str_array(3, "command=/bin/whoami", "runas_uid=1", NULL);275276VERIFY_INT(python_io2->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,277data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,278data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);279VERIFY_PTR(errstr, NULL);280281VERIFY_INT(python_io->log_stdin("stdin for plugin 1", strlen("stdin for plugin 1"), &errstr), SUDO_RC_OK);282VERIFY_PTR(errstr, NULL);283VERIFY_INT(python_io2->log_stdin("stdin for plugin 2", strlen("stdin for plugin 2"), &errstr), SUDO_RC_OK);284VERIFY_PTR(errstr, NULL);285VERIFY_INT(python_io->log_stdout("stdout for plugin 1", strlen("stdout for plugin 1"), &errstr), SUDO_RC_OK);286VERIFY_PTR(errstr, NULL);287VERIFY_INT(python_io2->log_stdout("stdout for plugin 2", strlen("stdout for plugin 2"), &errstr), SUDO_RC_OK);288VERIFY_PTR(errstr, NULL);289VERIFY_INT(python_io->log_stderr("stderr for plugin 1", strlen("stderr for plugin 1"), &errstr), SUDO_RC_OK);290VERIFY_PTR(errstr, NULL);291VERIFY_INT(python_io2->log_stderr("stderr for plugin 2", strlen("stderr for plugin 2"), &errstr), SUDO_RC_OK);292VERIFY_PTR(errstr, NULL);293VERIFY_INT(python_io->log_suspend(SIGTSTP, &errstr), SUDO_RC_OK);294VERIFY_PTR(errstr, NULL);295VERIFY_INT(python_io2->log_suspend(SIGSTOP, &errstr), SUDO_RC_OK);296VERIFY_PTR(errstr, NULL);297VERIFY_INT(python_io->log_suspend(SIGCONT, &errstr), SUDO_RC_OK);298VERIFY_PTR(errstr, NULL);299VERIFY_INT(python_io2->log_suspend(SIGCONT, &errstr), SUDO_RC_OK);300VERIFY_PTR(errstr, NULL);301VERIFY_INT(python_io->change_winsize(20, 10, &errstr), SUDO_RC_OK);302VERIFY_PTR(errstr, NULL);303VERIFY_INT(python_io2->change_winsize(30, 40, &errstr), SUDO_RC_OK);304VERIFY_PTR(errstr, NULL);305VERIFY_INT(python_io->log_ttyin("tty input for plugin 1", strlen("tty input for plugin 1"), &errstr), SUDO_RC_OK);306VERIFY_PTR(errstr, NULL);307VERIFY_INT(python_io2->log_ttyin("tty input for plugin 2", strlen("tty input for plugin 2"), &errstr), SUDO_RC_OK);308VERIFY_PTR(errstr, NULL);309VERIFY_INT(python_io->log_ttyout("tty output for plugin 1", strlen("tty output for plugin 1"), &errstr), SUDO_RC_OK);310VERIFY_PTR(errstr, NULL);311VERIFY_INT(python_io2->log_ttyout("tty output for plugin 2", strlen("tty output for plugin 2"), &errstr), SUDO_RC_OK);312VERIFY_PTR(errstr, NULL);313314python_io->close(1, 0); // successful execution, command returned 1315python_io2->close(2, 0); // command returned 2316317VERIFY_STDOUT(expected_path("check_example_io_plugin_command_log_multiple.stdout"));318VERIFY_STDERR(expected_path("check_example_io_plugin_command_log_multiple.stderr"));319VERIFY_FILE("sudo.log", expected_path("check_example_io_plugin_command_log_multiple1.stored"));320VERIFY_TRUE(verify_file(data.tmp_dir2, "sudo.log", expected_path("check_example_io_plugin_command_log_multiple2.stored")));321322return true;323}324325static int326check_example_io_plugin_failed_to_start_command(void)327{328const char *errstr = NULL;329330create_io_plugin_options(data.tmp_dir);331332str_array_free(&data.plugin_argv);333data.plugin_argc = 1;334data.plugin_argv = create_str_array(2, "cmd", NULL);335336str_array_free(&data.command_info);337data.command_info = create_str_array(3, "command=/usr/share/cmd", "runas_uid=0", NULL);338339VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,340data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,341data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);342VERIFY_PTR(errstr, NULL);343344python_io->close(0, EPERM); // execve returned with error345346VERIFY_STDOUT(expected_path("check_example_io_plugin_failed_to_start_command.stdout"));347VERIFY_STDERR(expected_path("check_example_io_plugin_failed_to_start_command.stderr"));348VERIFY_FILE("sudo.log", expected_path("check_example_io_plugin_failed_to_start_command.stored"));349350return true;351}352353static int354check_example_io_plugin_fails_with_python_backtrace(void)355{356const char *errstr = NULL;357358create_io_plugin_options("/some/not/writable/directory");359360VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,361data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,362data.user_env, data.plugin_options, &errstr), SUDO_RC_ERROR);363VERIFY_PTR(errstr, NULL);364365VERIFY_STDOUT(expected_path("check_example_io_plugin_fails_with_python_backtrace.stdout"));366VERIFY_STDERR(expected_path("check_example_io_plugin_fails_with_python_backtrace.stderr"));367368python_io->close(0, 0);369return true;370}371372static int373check_io_plugin_reports_error(void)374{375const char *errstr = NULL;376str_array_free(&data.plugin_options);377data.plugin_options = create_str_array(3783,379"ModulePath=" SRC_DIR "/regress/plugin_errorstr.py",380"ClassName=ConstructErrorPlugin",381NULL382);383384VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,385data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,386data.user_env, data.plugin_options, &errstr), SUDO_RC_ERROR);387388VERIFY_STR(errstr, "Something wrong in plugin constructor");389errstr = NULL;390391python_io->close(0, 0);392393str_array_free(&data.plugin_options);394data.plugin_options = create_str_array(3953,396"ModulePath=" SRC_DIR "/regress/plugin_errorstr.py",397"ClassName=ErrorMsgPlugin",398NULL399);400401VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,402data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,403data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);404VERIFY_PTR(errstr, NULL);405406VERIFY_INT(python_io->log_stdin("", 0, &errstr), SUDO_RC_ERROR);407VERIFY_STR(errstr, "Something wrong in log_stdin");408409errstr = (void *)13;410VERIFY_INT(python_io->log_stdout("", 0, &errstr), SUDO_RC_ERROR);411VERIFY_STR(errstr, "Something wrong in log_stdout");412413errstr = NULL;414VERIFY_INT(python_io->log_stderr("", 0, &errstr), SUDO_RC_ERROR);415VERIFY_STR(errstr, "Something wrong in log_stderr");416417errstr = NULL;418VERIFY_INT(python_io->log_ttyin("", 0, &errstr), SUDO_RC_ERROR);419VERIFY_STR(errstr, "Something wrong in log_ttyin");420421errstr = NULL;422VERIFY_INT(python_io->log_ttyout("", 0, &errstr), SUDO_RC_ERROR);423VERIFY_STR(errstr, "Something wrong in log_ttyout");424425errstr = NULL;426VERIFY_INT(python_io->log_suspend(SIGTSTP, &errstr), SUDO_RC_ERROR);427VERIFY_STR(errstr, "Something wrong in log_suspend");428429errstr = NULL;430VERIFY_INT(python_io->change_winsize(200, 100, &errstr), SUDO_RC_ERROR);431VERIFY_STR(errstr, "Something wrong in change_winsize");432433python_io->close(0, 0);434435VERIFY_STR(data.stderr_str, "");436VERIFY_STR(data.stdout_str, "");437return true;438}439440static int441check_example_group_plugin(void)442{443create_plugin_options("example_group_plugin", "SudoGroupPlugin", NULL);444445VERIFY_INT(group_plugin->init(GROUP_API_VERSION, fake_printf, data.plugin_options), SUDO_RC_OK);446447VERIFY_INT(group_plugin->query("test", "mygroup", NULL), SUDO_RC_OK);448VERIFY_INT(group_plugin->query("testuser2", "testgroup", NULL), SUDO_RC_OK);449VERIFY_INT(group_plugin->query("testuser2", "mygroup", NULL), SUDO_RC_REJECT);450VERIFY_INT(group_plugin->query("test", "testgroup", NULL), SUDO_RC_REJECT);451452group_plugin->cleanup();453VERIFY_STR(data.stderr_str, "");454VERIFY_STR(data.stdout_str, "");455return true;456}457458#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION459static const char *460create_debug_config(const char *debug_spec)461{462char *result = NULL;463464static char config_path[PATH_MAX] = "/";465snprintf(config_path, sizeof(config_path), "%s/sudo.conf", data.tmp_dir);466467char *content = NULL;468if (asprintf(&content, "Debug %s %s/debug.log %s\n",469"python_plugin.so", data.tmp_dir, debug_spec) < 0)470{471puts("Failed to allocate string");472goto cleanup;473}474475if (fwriteall(config_path, content) != true) {476printf("Failed to write '%s'\n", config_path);477goto cleanup;478}479480result = config_path;481482cleanup:483free(content);484485return result;486}487488static int489check_example_group_plugin_is_able_to_debug(void)490{491const char *config_path = create_debug_config("py_calls@diag");492VERIFY_NOT_NULL(config_path);493VERIFY_INT(sudo_conf_read(config_path, SUDO_CONF_ALL), true);494495create_plugin_options("example_group_plugin", "SudoGroupPlugin", NULL);496497group_plugin->init(GROUP_API_VERSION, fake_printf, data.plugin_options);498499group_plugin->query("user", "group", &example_pwd);500501group_plugin->cleanup();502503VERIFY_STR(data.stderr_str, "");504VERIFY_STR(data.stdout_str, "");505506VERIFY_LOG_LINES(expected_path("check_example_group_plugin_is_able_to_debug.log"));507508return true;509}510#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */511512static int513check_plugin_unload(void)514{515// You can call this test to avoid having a lot of subinterpreters516// (each plugin->open starts one, and only plugin unlink closes)517// It only verifies that python was shut down correctly.518VERIFY_TRUE(Py_IsInitialized());519VERIFY_TRUE(_unlink_symbols());520VERIFY_FALSE(Py_IsInitialized()); // python interpreter could be stopped521return true;522}523524#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION525static int526check_example_debugging(const char *debug_spec)527{528const char *errstr = NULL;529const char *config_path = create_debug_config(debug_spec);530VERIFY_NOT_NULL(config_path);531VERIFY_INT(sudo_conf_read(config_path, SUDO_CONF_ALL), true);532533create_debugging_plugin_options();534535str_array_free(&data.settings);536char *debug_flags_setting = NULL;537VERIFY_TRUE(asprintf(&debug_flags_setting, "debug_flags=%s/debug.log %s", data.tmp_dir, debug_spec) >= 0);538539data.settings = create_str_array(3, debug_flags_setting, "plugin_path=python_plugin.so", NULL);540541VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,542data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,543data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);544VERIFY_PTR(errstr, NULL);545python_io->close(0, 0);546547VERIFY_STR(data.stderr_str, "");548VERIFY_STR(data.stdout_str, "");549550VERIFY_LOG_LINES(expected_path("check_example_debugging_%s.log", debug_spec));551552free(debug_flags_setting);553return true;554}555#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */556557static int558check_loading_fails(const char *name)559{560const char *errstr = NULL;561562VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,563data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,564data.user_env, data.plugin_options, &errstr), SUDO_RC_ERROR);565VERIFY_PTR(errstr, NULL);566python_io->close(0, 0);567568VERIFY_STDOUT(expected_path("check_loading_fails_%s.stdout", name));569VERIFY_STDERR(expected_path("check_loading_fails_%s.stderr", name));570571return true;572}573574static int575check_loading_fails_with_missing_path(void)576{577str_array_free(&data.plugin_options);578data.plugin_options = create_str_array(2, "ClassName=DebugDemoPlugin", NULL);579return check_loading_fails("missing_path");580}581582static int583check_loading_succeeds_with_missing_classname(void)584{585str_array_free(&data.plugin_options);586data.plugin_options = create_str_array(2, "ModulePath=" SRC_DIR "/example_debugging.py", NULL);587588const char *errstr = NULL;589590VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,591data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,592data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);593VERIFY_PTR(errstr, NULL);594VERIFY_INT(python_io->show_version(1), SUDO_RC_OK);595python_io->close(0, 0);596597VERIFY_STDOUT(expected_path("check_loading_succeeds_with_missing_classname.stdout"));598VERIFY_STR(data.stderr_str, "");599600return true;601}602603static int604check_loading_fails_with_missing_classname(void)605{606str_array_free(&data.plugin_options);607data.plugin_options = create_str_array(2, "ModulePath=" SRC_DIR "/regress/plugin_errorstr.py", NULL);608return check_loading_fails("missing_classname");609}610611static int612check_loading_fails_with_wrong_classname(void)613{614create_plugin_options("example_debugging", "MispelledPluginName", NULL);615return check_loading_fails("wrong_classname");616}617618static int619check_loading_fails_with_wrong_path(void)620{621str_array_free(&data.plugin_options);622data.plugin_options = create_str_array(3, "ModulePath=/wrong_path.py", "ClassName=PluginName", NULL);623return check_loading_fails("wrong_path");624}625626static int627check_example_conversation_plugin_reason_log(int simulate_suspend, const char *description)628{629const char *errstr = NULL;630631create_conversation_plugin_options();632633str_array_free(&data.plugin_argv); // have a command run634data.plugin_argc = 1;635data.plugin_argv = create_str_array(2, "/bin/whoami", NULL);636637data.conv_replies[0] = "my fake reason";638data.conv_replies[1] = "my real secret reason";639640sudo_conv_t conversation = simulate_suspend ? fake_conversation_with_suspend : fake_conversation;641642VERIFY_INT(python_io->open(SUDO_API_VERSION, conversation, fake_printf, data.settings,643data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,644data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);645VERIFY_PTR(errstr, NULL);646python_io->close(0, 0);647648VERIFY_STDOUT(expected_path("check_example_conversation_plugin_reason_log_%s.stdout", description));649VERIFY_STDERR(expected_path("check_example_conversation_plugin_reason_log_%s.stderr", description));650VERIFY_CONV(expected_path("check_example_conversation_plugin_reason_log_%s.conversation", description));651VERIFY_FILE("sudo_reasons.txt", expected_path("check_example_conversation_plugin_reason_log_%s.stored", description));652return true;653}654655static int656check_example_conversation_plugin_user_interrupts(void)657{658const char *errstr = NULL;659660create_conversation_plugin_options();661662str_array_free(&data.plugin_argv); // have a command run663data.plugin_argc = 1;664data.plugin_argv = create_str_array(2, "/bin/whoami", NULL);665666data.conv_replies[0] = NULL; // this simulates user interrupt for the first question667668VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,669data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,670data.user_env, data.plugin_options, &errstr), SUDO_RC_REJECT);671VERIFY_PTR(errstr, NULL);672python_io->close(0, 0);673674VERIFY_STDOUT(expected_path("check_example_conversation_plugin_user_interrupts.stdout"));675VERIFY_STDERR(expected_path("check_example_conversation_plugin_user_interrupts.stderr"));676VERIFY_CONV(expected_path("check_example_conversation_plugin_user_interrupts.conversation"));677return true;678}679680static int681check_example_policy_plugin_version_display(int is_verbose)682{683const char *errstr = NULL;684685create_policy_plugin_options();686687VERIFY_INT(python_policy->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,688data.user_info, data.user_env, data.plugin_options, &errstr),689SUDO_RC_OK);690VERIFY_PTR(errstr, NULL);691VERIFY_INT(python_policy->show_version(is_verbose), SUDO_RC_OK);692693python_policy->close(0, 0); // this should not call the python plugin close as there was no command run invocation694695if (is_verbose) {696// Note: the exact python version is environment dependent697VERIFY_STR_CONTAINS(data.stdout_str, "Python interpreter version:");698*strstr(data.stdout_str, "Python interpreter version:") = '\0';699VERIFY_STDOUT(expected_path("check_example_policy_plugin_version_display_full.stdout"));700} else {701VERIFY_STDOUT(expected_path("check_example_policy_plugin_version_display.stdout"));702}703704VERIFY_STDERR(expected_path("check_example_policy_plugin_version_display.stderr"));705706return true;707}708709static int710check_example_policy_plugin_accepted_execution(void)711{712const char *errstr = NULL;713714create_policy_plugin_options();715716str_array_free(&data.plugin_argv);717data.plugin_argc = 2;718data.plugin_argv = create_str_array(3, "/bin/whoami", "--help", NULL);719720str_array_free(&data.user_env);721data.user_env = create_str_array(3, "USER_ENV1=VALUE1", "USER_ENV2=value2", NULL);722723VERIFY_INT(python_policy->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,724data.user_info, data.user_env, data.plugin_options, &errstr),725SUDO_RC_OK);726VERIFY_PTR(errstr, NULL);727728char **env_add = create_str_array(3, "REQUESTED_ENV1=VALUE1", "REQUESTED_ENV2=value2", NULL);729730char **argv_out, **user_env_out, **command_info_out; // free to contain garbage731732VERIFY_INT(python_policy->check_policy(data.plugin_argc, data.plugin_argv, env_add,733&command_info_out, &argv_out, &user_env_out, &errstr),734SUDO_RC_ACCEPT);735VERIFY_PTR(errstr, NULL);736737VERIFY_STR_SET(command_info_out, 4, "command=/bin/whoami", "runas_uid=0", "runas_gid=0", NULL);738VERIFY_STR_SET(user_env_out, 5, "USER_ENV1=VALUE1", "USER_ENV2=value2",739"REQUESTED_ENV1=VALUE1", "REQUESTED_ENV2=value2", NULL);740VERIFY_STR_SET(argv_out, 3, "/bin/whoami", "--help", NULL);741742VERIFY_INT(python_policy->init_session(&example_pwd, &user_env_out, &errstr), SUDO_RC_ACCEPT);743VERIFY_PTR(errstr, NULL);744745// init session is able to modify the user env:746VERIFY_STR_SET(user_env_out, 6, "USER_ENV1=VALUE1", "USER_ENV2=value2",747"REQUESTED_ENV1=VALUE1", "REQUESTED_ENV2=value2", "PLUGIN_EXAMPLE_ENV=1", NULL);748749python_policy->close(3, 0); // successful execution returned exit code 3750751VERIFY_STDOUT(expected_path("check_example_policy_plugin_accepted_execution.stdout"));752VERIFY_STDERR(expected_path("check_example_policy_plugin_accepted_execution.stderr"));753754str_array_free(&env_add);755str_array_free(&user_env_out);756str_array_free(&command_info_out);757str_array_free(&argv_out);758return true;759}760761static int762check_example_policy_plugin_failed_execution(void)763{764const char *errstr = NULL;765766create_policy_plugin_options();767768str_array_free(&data.plugin_argv);769data.plugin_argc = 2;770data.plugin_argv = create_str_array(3, "/bin/id", "--help", NULL);771772VERIFY_INT(python_policy->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,773data.user_info, data.user_env, data.plugin_options, &errstr),774SUDO_RC_OK);775VERIFY_PTR(errstr, NULL);776777char **argv_out, **user_env_out, **command_info_out; // free to contain garbage778779VERIFY_INT(python_policy->check_policy(data.plugin_argc, data.plugin_argv, NULL,780&command_info_out, &argv_out, &user_env_out, &errstr),781SUDO_RC_ACCEPT);782VERIFY_PTR(errstr, NULL);783784// pwd is unset (user is not part of /etc/passwd)785VERIFY_INT(python_policy->init_session(NULL, &user_env_out, &errstr), SUDO_RC_ACCEPT);786VERIFY_PTR(errstr, NULL);787788python_policy->close(12345, ENOENT); // failed to execute789790VERIFY_STDOUT(expected_path("check_example_policy_plugin_failed_execution.stdout"));791VERIFY_STDERR(expected_path("check_example_policy_plugin_failed_execution.stderr"));792793str_array_free(&user_env_out);794str_array_free(&command_info_out);795str_array_free(&argv_out);796return true;797}798799static int800check_example_policy_plugin_denied_execution(void)801{802const char *errstr = NULL;803804create_policy_plugin_options();805806str_array_free(&data.plugin_argv);807data.plugin_argc = 1;808data.plugin_argv = create_str_array(2, "/bin/passwd", NULL);809810VERIFY_INT(python_policy->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,811data.user_info, data.user_env, data.plugin_options, &errstr),812SUDO_RC_OK);813VERIFY_PTR(errstr, NULL);814815char **argv_out, **user_env_out, **command_info_out; // free to contain garbage816817VERIFY_INT(python_policy->check_policy(data.plugin_argc, data.plugin_argv, NULL,818&command_info_out, &argv_out, &user_env_out, &errstr),819SUDO_RC_REJECT);820VERIFY_PTR(errstr, NULL);821822VERIFY_PTR(command_info_out, NULL);823VERIFY_PTR(argv_out, NULL);824VERIFY_PTR(user_env_out, NULL);825826python_policy->close(0, 0); // there was no execution827828VERIFY_STDOUT(expected_path("check_example_policy_plugin_denied_execution.stdout"));829VERIFY_STDERR(expected_path("check_example_policy_plugin_denied_execution.stderr"));830831return true;832}833834static int835check_example_policy_plugin_list(void)836{837const char *errstr = NULL;838839create_policy_plugin_options();840841VERIFY_INT(python_policy->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,842data.user_info, data.user_env, data.plugin_options, &errstr),843SUDO_RC_OK);844VERIFY_PTR(errstr, NULL);845846snprintf_append(data.stdout_str, MAX_OUTPUT, "-- minimal --\n");847VERIFY_INT(python_policy->list(data.plugin_argc, data.plugin_argv, false, NULL, &errstr), SUDO_RC_OK);848VERIFY_PTR(errstr, NULL);849850snprintf_append(data.stdout_str, MAX_OUTPUT, "\n-- minimal (verbose) --\n");851VERIFY_INT(python_policy->list(data.plugin_argc, data.plugin_argv, true, NULL, &errstr), SUDO_RC_OK);852VERIFY_PTR(errstr, NULL);853854snprintf_append(data.stdout_str, MAX_OUTPUT, "\n-- with user --\n");855VERIFY_INT(python_policy->list(data.plugin_argc, data.plugin_argv, false, "testuser", &errstr), SUDO_RC_OK);856VERIFY_PTR(errstr, NULL);857858snprintf_append(data.stdout_str, MAX_OUTPUT, "\n-- with user (verbose) --\n");859VERIFY_INT(python_policy->list(data.plugin_argc, data.plugin_argv, true, "testuser", &errstr), SUDO_RC_OK);860VERIFY_PTR(errstr, NULL);861862snprintf_append(data.stdout_str, MAX_OUTPUT, "\n-- with allowed program --\n");863str_array_free(&data.plugin_argv);864data.plugin_argc = 3;865data.plugin_argv = create_str_array(4, "/bin/id", "some", "arguments", NULL);866VERIFY_INT(python_policy->list(data.plugin_argc, data.plugin_argv, false, NULL, &errstr), SUDO_RC_OK);867VERIFY_PTR(errstr, NULL);868869snprintf_append(data.stdout_str, MAX_OUTPUT, "\n-- with allowed program (verbose) --\n");870VERIFY_INT(python_policy->list(data.plugin_argc, data.plugin_argv, true, NULL, &errstr), SUDO_RC_OK);871VERIFY_PTR(errstr, NULL);872873snprintf_append(data.stdout_str, MAX_OUTPUT, "\n-- with denied program --\n");874str_array_free(&data.plugin_argv);875data.plugin_argc = 1;876data.plugin_argv = create_str_array(2, "/bin/passwd", NULL);877VERIFY_INT(python_policy->list(data.plugin_argc, data.plugin_argv, false, NULL, &errstr), SUDO_RC_OK);878VERIFY_PTR(errstr, NULL);879880snprintf_append(data.stdout_str, MAX_OUTPUT, "\n-- with denied program (verbose) --\n");881VERIFY_INT(python_policy->list(data.plugin_argc, data.plugin_argv, true, NULL, &errstr), SUDO_RC_OK);882VERIFY_PTR(errstr, NULL);883884python_policy->close(0, 0); // there was no execution885886VERIFY_STDOUT(expected_path("check_example_policy_plugin_list.stdout"));887VERIFY_STDERR(expected_path("check_example_policy_plugin_list.stderr"));888889return true;890}891892static int893check_example_policy_plugin_validate_invalidate(void)894{895const char *errstr = NULL;896897#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION898// the plugin does not do any meaningful for these, so using log to validate instead899const char *config_path = create_debug_config("py_calls@diag");900VERIFY_NOT_NULL(config_path);901VERIFY_INT(sudo_conf_read(config_path, SUDO_CONF_ALL), true);902#endif903904create_policy_plugin_options();905906VERIFY_INT(python_policy->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,907data.user_info, data.user_env, data.plugin_options, &errstr),908SUDO_RC_OK);909VERIFY_PTR(errstr, NULL);910911VERIFY_INT(python_policy->validate(&errstr), SUDO_RC_OK);912VERIFY_PTR(errstr, NULL);913914python_policy->invalidate(true);915python_policy->invalidate(false);916917python_policy->close(0, 0); // no command execution918919#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION920VERIFY_LOG_LINES(expected_path("check_example_policy_plugin_validate_invalidate.log"));921#endif922VERIFY_STR(data.stderr_str, "");923VERIFY_STR(data.stdout_str, "");924return true;925}926927static int928check_policy_plugin_callbacks_are_optional(void)929{930const char *errstr = NULL;931932create_debugging_plugin_options();933934VERIFY_INT(python_policy->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,935data.user_info, data.user_env, data.plugin_options, &errstr),936SUDO_RC_OK);937VERIFY_PTR(errstr, NULL);938939VERIFY_PTR(python_policy->list, NULL);940VERIFY_PTR(python_policy->validate, NULL);941VERIFY_PTR(python_policy->invalidate, NULL);942VERIFY_PTR_NE(python_policy->check_policy, NULL); // (not optional)943VERIFY_PTR(python_policy->init_session, NULL);944945// show_version always displays the plugin, but it is optional in the python layer946VERIFY_PTR_NE(python_policy->show_version, NULL);947VERIFY_INT(python_policy->show_version(1), SUDO_RC_OK);948949python_policy->close(0, 0);950return true;951}952953static int954check_policy_plugin_reports_error(void)955{956const char *errstr = NULL;957str_array_free(&data.plugin_options);958data.plugin_options = create_str_array(9593,960"ModulePath=" SRC_DIR "/regress/plugin_errorstr.py",961"ClassName=ConstructErrorPlugin",962NULL963);964965VERIFY_INT(python_policy->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,966data.user_info, data.user_env, data.plugin_options, &errstr), SUDO_RC_ERROR);967VERIFY_STR(errstr, "Something wrong in plugin constructor");968errstr = NULL;969970python_policy->close(0, 0);971972str_array_free(&data.plugin_options);973data.plugin_options = create_str_array(9743,975"ModulePath=" SRC_DIR "/regress/plugin_errorstr.py",976"ClassName=ErrorMsgPlugin",977NULL978);979980data.plugin_argc = 1;981str_array_free(&data.plugin_argv);982data.plugin_argv = create_str_array(2, "id", NULL);983984VERIFY_INT(python_policy->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,985data.user_info, data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);986VERIFY_PTR(errstr, NULL);987988char **command_info_out = NULL;989char **argv_out = NULL;990char **user_env_out = NULL;991992VERIFY_INT(python_policy->list(data.plugin_argc, data.plugin_argv, true, NULL, &errstr), SUDO_RC_ERROR);993VERIFY_STR(errstr, "Something wrong in list");994995errstr = NULL;996VERIFY_INT(python_policy->validate(&errstr), SUDO_RC_ERROR);997VERIFY_STR(errstr, "Something wrong in validate");998999errstr = NULL;1000VERIFY_INT(python_policy->check_policy(data.plugin_argc, data.plugin_argv, data.user_env,1001&command_info_out, &argv_out, &user_env_out, &errstr),1002SUDO_RC_ERROR);1003VERIFY_STR(errstr, "Something wrong in check_policy");10041005errstr = NULL;1006VERIFY_INT(python_policy->init_session(&example_pwd, &user_env_out, &errstr), SUDO_RC_ERROR);1007VERIFY_STR(errstr, "Something wrong in init_session");10081009python_policy->close(0, 0);10101011VERIFY_STR(data.stderr_str, "");1012VERIFY_STR(data.stdout_str, "");1013return true;1014}10151016static int1017check_io_plugin_callbacks_are_optional(void)1018{1019const char *errstr = NULL;10201021create_debugging_plugin_options();10221023VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,1024data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,1025data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1026VERIFY_PTR(errstr, NULL);10271028VERIFY_PTR(python_io->log_stdin, NULL);1029VERIFY_PTR(python_io->log_stdout, NULL);1030VERIFY_PTR(python_io->log_stderr, NULL);1031VERIFY_PTR(python_io->log_ttyin, NULL);1032VERIFY_PTR(python_io->log_ttyout, NULL);1033VERIFY_PTR(python_io->change_winsize, NULL);10341035// show_version always displays the plugin, but it is optional in the python layer1036VERIFY_PTR_NE(python_io->show_version, NULL);1037VERIFY_INT(python_io->show_version(1), SUDO_RC_OK);10381039python_io->close(0, 0);1040return true;1041}10421043static int1044check_python_plugins_do_not_affect_each_other(void)1045{1046const char *errstr = NULL;10471048// We test here that one plugin is not able to effect the environment of another1049// This is important so they do not ruin or depend on each other's state.1050create_plugin_options("regress/plugin_conflict", "ConflictPlugin", "Path=path_for_first_plugin");10511052VERIFY_INT(python_io->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,1053data.user_info, data.command_info, data.plugin_argc, data.plugin_argv,1054data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1055VERIFY_PTR(errstr, NULL);10561057create_plugin_options("regress/plugin_conflict", "ConflictPlugin", "Path=path_for_second_plugin");1058VERIFY_INT(python_policy->open(SUDO_API_VERSION, fake_conversation, fake_printf, data.settings,1059data.user_info, data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1060VERIFY_PTR(errstr, NULL);10611062python_io->close(0, 0);1063python_policy->close(0, 0);10641065VERIFY_STDOUT(expected_path("check_python_plugins_do_not_affect_each_other.stdout"));1066VERIFY_STR(data.stderr_str, "");1067return true;1068}10691070static int1071check_example_audit_plugin_receives_accept(void)1072{1073create_audit_plugin_options("");1074const char *errstr = NULL;10751076str_array_free(&data.plugin_argv);1077data.plugin_argv = create_str_array(6, "sudo", "-u", "user", "id", "--help", NULL);10781079str_array_free(&data.user_env);1080data.user_env = create_str_array(3, "KEY1=VALUE1", "KEY2=VALUE2", NULL);10811082str_array_free(&data.user_info);1083data.user_info = create_str_array(3, "user=testuser1", "uid=123", NULL);10841085VERIFY_INT(python_audit->open(SUDO_API_VERSION, fake_conversation, fake_printf,1086data.settings, data.user_info, 3, data.plugin_argv,1087data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1088VERIFY_PTR(errstr, NULL);10891090str_array_free(&data.command_info);1091data.command_info = create_str_array(2, "command=/sbin/id", NULL);10921093str_array_free(&data.plugin_argv);1094data.plugin_argv = create_str_array(3, "id", "--help", NULL);10951096VERIFY_INT(python_audit->accept("accepter plugin name", SUDO_POLICY_PLUGIN,1097data.command_info, data.plugin_argv,1098data.user_env, &errstr), SUDO_RC_OK);1099VERIFY_PTR(errstr, NULL);11001101python_audit->close(SUDO_PLUGIN_WAIT_STATUS, W_EXITCODE(2, 0)); // process exited with 211021103VERIFY_STDOUT(expected_path("check_example_audit_plugin_receives_accept.stdout"));1104VERIFY_STR(data.stderr_str, "");11051106return true;1107}11081109static int1110check_example_audit_plugin_receives_reject(void)1111{1112create_audit_plugin_options(NULL);1113const char *errstr = NULL;11141115str_array_free(&data.plugin_argv);1116data.plugin_argv = create_str_array(3, "sudo", "passwd", NULL);11171118str_array_free(&data.user_info);1119data.user_info = create_str_array(3, "user=root", "uid=0", NULL);11201121VERIFY_INT(python_audit->open(SUDO_API_VERSION, fake_conversation, fake_printf,1122data.settings, data.user_info, 1, data.plugin_argv,1123data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1124VERIFY_PTR(errstr, NULL);11251126VERIFY_INT(python_audit->reject("rejecter plugin name", SUDO_IO_PLUGIN,1127"Rejected just because!", data.command_info,1128&errstr), SUDO_RC_OK);1129VERIFY_PTR(errstr, NULL);11301131python_audit->close(SUDO_PLUGIN_NO_STATUS, 0); // program was not run11321133VERIFY_STDOUT(expected_path("check_example_audit_plugin_receives_reject.stdout"));1134VERIFY_STR(data.stderr_str, "");11351136return true;1137}11381139static int1140check_example_audit_plugin_receives_error(void)1141{1142create_audit_plugin_options("");1143const char *errstr = NULL;11441145str_array_free(&data.plugin_argv);1146data.plugin_argv = create_str_array(5, "sudo", "-u", "user", "id", NULL);11471148VERIFY_INT(python_audit->open(SUDO_API_VERSION, fake_conversation, fake_printf,1149data.settings, data.user_info, 3, data.plugin_argv,1150data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1151VERIFY_PTR(errstr, NULL);11521153str_array_free(&data.command_info);1154data.command_info = create_str_array(2, "command=/sbin/id", NULL);11551156VERIFY_INT(python_audit->error("errorer plugin name", SUDO_AUDIT_PLUGIN,1157"Some error has happened", data.command_info,1158&errstr), SUDO_RC_OK);1159VERIFY_PTR(errstr, NULL);11601161python_audit->close(SUDO_PLUGIN_SUDO_ERROR, 222);11621163VERIFY_STDOUT(expected_path("check_example_audit_plugin_receives_error.stdout"));1164VERIFY_STR(data.stderr_str, "");11651166return true;1167}11681169typedef struct audit_plugin * (audit_clone_func)(void);11701171static int1172check_example_audit_plugin_workflow_multiple(void)1173{1174// verify multiple python audit plugins are available1175audit_clone_func *python_audit_clone = (audit_clone_func *)sudo_dso_findsym(1176python_plugin_handle, "python_audit_clone");1177VERIFY_PTR_NE(python_audit_clone, NULL);11781179struct audit_plugin *python_audit2 = NULL;11801181for (int i = 0; i < 7; ++i) {1182python_audit2 = (*python_audit_clone)();1183VERIFY_PTR_NE(python_audit2, NULL);1184VERIFY_PTR_NE(python_audit2, python_audit);1185}11861187const char *errstr = NULL;11881189str_array_free(&data.plugin_argv);1190data.plugin_argv = create_str_array(6, "sudo", "-u", "user", "id", "--help", NULL);11911192str_array_free(&data.user_env);1193data.user_env = create_str_array(3, "KEY1=VALUE1", "KEY2=VALUE2", NULL);11941195str_array_free(&data.user_info);1196data.user_info = create_str_array(3, "user=default", "uid=1000", NULL);11971198create_audit_plugin_options("Id=1");1199VERIFY_INT(python_audit->open(SUDO_API_VERSION, fake_conversation, fake_printf,1200data.settings, data.user_info, 3, data.plugin_argv,1201data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1202VERIFY_PTR(errstr, NULL);12031204// For verifying the error message of no more plugin. It should be displayed only once.1205VERIFY_PTR((*python_audit_clone)(), NULL);1206VERIFY_PTR((*python_audit_clone)(), NULL);12071208create_audit_plugin_options("Id=2");1209VERIFY_INT(python_audit2->open(SUDO_API_VERSION, fake_conversation, fake_printf,1210data.settings, data.user_info, 3, data.plugin_argv,1211data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1212VERIFY_PTR(errstr, NULL);12131214str_array_free(&data.command_info);1215data.command_info = create_str_array(2, "command=/sbin/id", NULL);12161217str_array_free(&data.plugin_argv);1218data.plugin_argv = create_str_array(3, "id", "--help", NULL);12191220VERIFY_INT(python_audit->accept("accepter plugin name", SUDO_POLICY_PLUGIN,1221data.command_info, data.plugin_argv,1222data.user_env, &errstr), SUDO_RC_OK);1223VERIFY_PTR(errstr, NULL);12241225VERIFY_INT(python_audit2->accept("accepter plugin name", SUDO_POLICY_PLUGIN,1226data.command_info, data.plugin_argv,1227data.user_env, &errstr), SUDO_RC_OK);1228VERIFY_PTR(errstr, NULL);12291230python_audit->close(SUDO_PLUGIN_WAIT_STATUS, W_EXITCODE(0, 11)); // process got signal 111231python_audit2->close(SUDO_PLUGIN_WAIT_STATUS, W_EXITCODE(0, 11));12321233VERIFY_STDOUT(expected_path("check_example_audit_plugin_workflow_multiple.stdout"));1234VERIFY_STDERR(expected_path("check_example_audit_plugin_workflow_multiple.stderr"));12351236return true;1237}12381239static int1240check_example_audit_plugin_version_display(void)1241{1242create_audit_plugin_options(NULL);1243const char *errstr = NULL;12441245str_array_free(&data.user_info);1246data.user_info = create_str_array(3, "user=root", "uid=0", NULL);12471248str_array_free(&data.plugin_argv);1249data.plugin_argv = create_str_array(3, "sudo", "-V", NULL);12501251VERIFY_INT(python_audit->open(SUDO_API_VERSION, fake_conversation, fake_printf,1252data.settings, data.user_info, 2, data.plugin_argv,1253data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1254VERIFY_PTR(errstr, NULL);12551256VERIFY_INT(python_audit->show_version(false), SUDO_RC_OK);1257VERIFY_INT(python_audit->show_version(true), SUDO_RC_OK);12581259python_audit->close(SUDO_PLUGIN_SUDO_ERROR, 222);12601261VERIFY_STDOUT(expected_path("check_example_audit_plugin_version_display.stdout"));1262VERIFY_STR(data.stderr_str, "");12631264return true;1265}12661267static int1268check_audit_plugin_callbacks_are_optional(void)1269{1270const char *errstr = NULL;12711272create_debugging_plugin_options();12731274VERIFY_INT(python_audit->open(SUDO_API_VERSION, fake_conversation, fake_printf,1275data.settings, data.user_info, 2, data.plugin_argv,1276data.user_env, data.plugin_options, &errstr),1277SUDO_RC_OK);1278VERIFY_PTR(errstr, NULL);12791280VERIFY_PTR(python_audit->accept, NULL);1281VERIFY_PTR(python_audit->reject, NULL);1282VERIFY_PTR(python_audit->error, NULL);12831284// show_version always displays the plugin, but it is optional in the python layer1285VERIFY_PTR_NE(python_audit->show_version, NULL);1286VERIFY_INT(python_audit->show_version(1), SUDO_RC_OK);12871288python_audit->close(SUDO_PLUGIN_NO_STATUS, 0);1289return true;1290}12911292static int1293check_audit_plugin_reports_error(void)1294{1295const char *errstr = NULL;1296create_plugin_options("regress/plugin_errorstr", "ConstructErrorPlugin", NULL);12971298VERIFY_INT(python_audit->open(SUDO_API_VERSION, fake_conversation, fake_printf,1299data.settings, data.user_info, 0, data.plugin_argv,1300data.user_env, data.plugin_options, &errstr), SUDO_RC_ERROR);13011302VERIFY_STR(errstr, "Something wrong in plugin constructor");1303errstr = NULL;13041305python_audit->close(SUDO_PLUGIN_NO_STATUS, 0);13061307create_plugin_options("regress/plugin_errorstr", "ErrorMsgPlugin", NULL);13081309VERIFY_INT(python_audit->open(SUDO_API_VERSION, fake_conversation, fake_printf,1310data.settings, data.user_info, 0, data.plugin_argv,1311data.user_env, data.plugin_options, &errstr), SUDO_RC_ERROR);1312VERIFY_STR(errstr, "Something wrong in open");13131314errstr = NULL;1315VERIFY_INT(python_audit->accept("plugin name", SUDO_POLICY_PLUGIN,1316data.command_info, data.plugin_argv,1317data.user_env, &errstr), SUDO_RC_ERROR);1318VERIFY_STR(errstr, "Something wrong in accept");13191320errstr = NULL;1321VERIFY_INT(python_audit->reject("plugin name", SUDO_POLICY_PLUGIN,1322"audit message", data.command_info,1323&errstr), SUDO_RC_ERROR);1324VERIFY_STR(errstr, "Something wrong in reject");13251326errstr = NULL;1327VERIFY_INT(python_audit->error("plugin name", SUDO_POLICY_PLUGIN,1328"audit message", data.command_info,1329&errstr), SUDO_RC_ERROR);1330VERIFY_STR(errstr, "Something wrong in error");13311332python_audit->close(SUDO_PLUGIN_NO_STATUS, 0);13331334VERIFY_STR(data.stderr_str, "");1335VERIFY_STR(data.stdout_str, "");1336return true;1337}13381339static int1340check_example_approval_plugin(const char *date_str, const char *expected_error)1341{1342const char *errstr = NULL;13431344create_plugin_options("example_approval_plugin", "BusinessHoursApprovalPlugin", NULL);13451346VERIFY_INT(python_approval->open(SUDO_API_VERSION, fake_conversation, fake_printf,1347data.settings, data.user_info, 0, data.plugin_argv,1348data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);13491350VERIFY_TRUE(mock_python_datetime_now("example_approval_plugin", date_str));13511352int expected_rc = (expected_error == NULL) ? SUDO_RC_ACCEPT : SUDO_RC_REJECT;13531354VERIFY_INT(python_approval->check(data.command_info, data.plugin_argv, data.user_env, &errstr),1355expected_rc);13561357if (expected_error == NULL) {1358VERIFY_PTR(errstr, NULL);1359VERIFY_STR(data.stdout_str, "");1360} else {1361VERIFY_STR(errstr, expected_error);1362VERIFY_STR_CONTAINS(data.stdout_str, expected_error); // (ends with \n)1363}1364VERIFY_STR(data.stderr_str, "");13651366python_approval->close();13671368return true;1369}13701371typedef struct approval_plugin * (approval_clone_func)(void);13721373static int1374check_multiple_approval_plugin_and_arguments(void)1375{1376// verify multiple python approval plugins are available1377approval_clone_func *python_approval_clone = (approval_clone_func *)sudo_dso_findsym(1378python_plugin_handle, "python_approval_clone");1379VERIFY_PTR_NE(python_approval_clone, NULL);13801381struct approval_plugin *python_approval2 = NULL;13821383for (int i = 0; i < 7; ++i) {1384python_approval2 = (*python_approval_clone)();1385VERIFY_PTR_NE(python_approval2, NULL);1386VERIFY_PTR_NE(python_approval2, python_approval);1387}13881389const char *errstr = NULL;1390create_plugin_options("regress/plugin_approval_test", "ApprovalTestPlugin", "Id=1");13911392str_array_free(&data.plugin_argv);1393data.plugin_argv = create_str_array(6, "sudo", "-u", "user", "whoami", "--help", NULL);13941395str_array_free(&data.user_env);1396data.user_env = create_str_array(3, "USER_ENV1=VALUE1", "USER_ENV2=value2", NULL);13971398str_array_free(&data.user_info);1399data.user_info = create_str_array(3, "INFO1=VALUE1", "info2=value2", NULL);14001401str_array_free(&data.settings);1402data.settings = create_str_array(3, "SETTING1=VALUE1", "setting2=value2", NULL);14031404VERIFY_INT(python_approval->open(SUDO_API_VERSION, fake_conversation, fake_printf,1405data.settings, data.user_info, 3, data.plugin_argv,1406data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1407VERIFY_PTR(errstr, NULL);14081409// For verifying the error message of no more plugin. It should be displayed only once.1410VERIFY_PTR((*python_approval_clone)(), NULL);1411VERIFY_PTR((*python_approval_clone)(), NULL);14121413create_plugin_options("regress/plugin_approval_test", "ApprovalTestPlugin", "Id=2");1414VERIFY_INT(python_approval2->open(SUDO_API_VERSION, fake_conversation, fake_printf,1415data.settings, data.user_info, 3, data.plugin_argv,1416data.user_env, data.plugin_options, &errstr), SUDO_RC_OK);1417VERIFY_PTR(errstr, NULL);14181419VERIFY_INT(python_approval->show_version(false), SUDO_RC_OK);1420VERIFY_INT(python_approval2->show_version(true), SUDO_RC_OK);14211422str_array_free(&data.command_info);1423data.command_info = create_str_array(3, "CMDINFO1=value1", "CMDINFO2=VALUE2", NULL);14241425str_array_free(&data.plugin_argv);1426data.plugin_argv = create_str_array(3, "whoami", "--help", NULL);14271428VERIFY_INT(python_approval->check(data.command_info, data.plugin_argv, data.user_env, &errstr),1429SUDO_RC_OK);1430VERIFY_PTR(errstr, NULL);14311432VERIFY_INT(python_approval2->check(data.command_info, data.plugin_argv, data.user_env, &errstr),1433SUDO_RC_OK);1434VERIFY_PTR(errstr, NULL);14351436python_approval->close();1437python_approval2->close();14381439VERIFY_STDOUT(expected_path("check_multiple_approval_plugin_and_arguments.stdout"));1440VERIFY_STDERR(expected_path("check_multiple_approval_plugin_and_arguments.stderr"));14411442return true;1443}144414451446static int1447_init_symbols(void)1448{1449if (python_plugin_handle != NULL) {1450// symbols are already loaded, we just restore1451RESTORE_PYTHON_PLUGIN(python_io);1452RESTORE_PYTHON_PLUGIN(python_policy);1453RESTORE_PYTHON_PLUGIN(python_approval);1454RESTORE_PYTHON_PLUGIN(python_audit);1455RESTORE_PYTHON_PLUGIN(group_plugin);1456return true;1457}14581459// we load the symbols1460python_plugin_handle = sudo_dso_load(python_plugin_so_path, SUDO_DSO_LAZY|SUDO_DSO_GLOBAL);1461VERIFY_PTR_NE(python_plugin_handle, NULL);14621463python_io = sudo_dso_findsym(python_plugin_handle, "python_io");1464VERIFY_PTR_NE(python_io, NULL);14651466group_plugin = sudo_dso_findsym(python_plugin_handle, "group_plugin");1467VERIFY_PTR_NE(group_plugin, NULL);14681469python_policy = sudo_dso_findsym(python_plugin_handle, "python_policy");1470VERIFY_PTR_NE(python_policy, NULL);14711472python_audit = sudo_dso_findsym(python_plugin_handle, "python_audit");1473VERIFY_PTR_NE(python_audit, NULL);14741475python_approval = sudo_dso_findsym(python_plugin_handle, "python_approval");1476VERIFY_PTR_NE(python_approval, NULL);14771478SAVE_PYTHON_PLUGIN(python_io);1479SAVE_PYTHON_PLUGIN(python_policy);1480SAVE_PYTHON_PLUGIN(python_approval);1481SAVE_PYTHON_PLUGIN(python_audit);1482SAVE_PYTHON_PLUGIN(group_plugin);14831484return true;1485}14861487static int1488_unlink_symbols(void)1489{1490python_io = NULL;1491group_plugin = NULL;1492python_policy = NULL;1493python_approval = NULL;1494python_audit = NULL;1495VERIFY_INT(sudo_dso_unload(python_plugin_handle), 0);1496python_plugin_handle = NULL;1497VERIFY_FALSE(Py_IsInitialized());1498return true;1499}15001501int1502main(int argc, char *argv[])1503{1504int ch, errors = 0, ntests = 0;15051506while ((ch = getopt(argc, argv, "v")) != -1) {1507switch (ch) {1508case 'v':1509verbose = true;1510break;1511default:1512fprintf(stderr, "usage: %s [-v]\n", getprogname());1513return EXIT_FAILURE;1514}1515}1516argc -= optind;1517argv += optind;15181519if (argc != 1) {1520printf("Please specify the python_plugin.so as argument!\n");1521return EXIT_FAILURE;1522}1523python_plugin_so_path = argv[0];15241525// Unbuffer stdout so we don't miss output during a crash.1526setvbuf(stdout, NULL, _IONBF, 0);15271528RUN_TEST(check_example_io_plugin_version_display(true));1529RUN_TEST(check_example_io_plugin_version_display(false));1530RUN_TEST(check_example_io_plugin_command_log());1531RUN_TEST(check_example_io_plugin_command_log_multiple());1532RUN_TEST(check_example_io_plugin_failed_to_start_command());1533RUN_TEST(check_example_io_plugin_fails_with_python_backtrace());1534RUN_TEST(check_io_plugin_callbacks_are_optional());1535RUN_TEST(check_io_plugin_reports_error());1536RUN_TEST(check_plugin_unload());15371538RUN_TEST(check_example_group_plugin());1539#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION1540RUN_TEST(check_example_group_plugin_is_able_to_debug());1541#endif1542RUN_TEST(check_plugin_unload());15431544RUN_TEST(check_loading_fails_with_missing_path());1545RUN_TEST(check_loading_succeeds_with_missing_classname());1546RUN_TEST(check_loading_fails_with_missing_classname());1547RUN_TEST(check_loading_fails_with_wrong_classname());1548RUN_TEST(check_loading_fails_with_wrong_path());1549RUN_TEST(check_plugin_unload());15501551RUN_TEST(check_example_conversation_plugin_reason_log(false, "without_suspend"));1552RUN_TEST(check_example_conversation_plugin_reason_log(true, "with_suspend"));1553RUN_TEST(check_example_conversation_plugin_user_interrupts());1554RUN_TEST(check_plugin_unload());15551556RUN_TEST(check_example_policy_plugin_version_display(true));1557RUN_TEST(check_example_policy_plugin_version_display(false));1558RUN_TEST(check_example_policy_plugin_accepted_execution());1559RUN_TEST(check_example_policy_plugin_failed_execution());1560RUN_TEST(check_example_policy_plugin_denied_execution());1561RUN_TEST(check_example_policy_plugin_list());1562RUN_TEST(check_example_policy_plugin_validate_invalidate());1563RUN_TEST(check_policy_plugin_callbacks_are_optional());1564RUN_TEST(check_policy_plugin_reports_error());1565RUN_TEST(check_plugin_unload());15661567RUN_TEST(check_example_audit_plugin_receives_accept());1568RUN_TEST(check_example_audit_plugin_receives_reject());1569RUN_TEST(check_example_audit_plugin_receives_error());1570RUN_TEST(check_example_audit_plugin_workflow_multiple());1571RUN_TEST(check_example_audit_plugin_version_display());1572RUN_TEST(check_audit_plugin_callbacks_are_optional());1573RUN_TEST(check_audit_plugin_reports_error());1574RUN_TEST(check_plugin_unload());15751576// Monday, too early1577RUN_TEST(check_example_approval_plugin(1578"2020-02-10T07:55:23", "That is not allowed outside the business hours!"));1579// Monday, good time1580RUN_TEST(check_example_approval_plugin("2020-02-10T08:05:23", NULL));1581// Friday, good time1582RUN_TEST(check_example_approval_plugin("2020-02-14T17:59:23", NULL));1583// Friday, too late1584RUN_TEST(check_example_approval_plugin(1585"2020-02-10T18:05:23", "That is not allowed outside the business hours!"));1586// Saturday1587RUN_TEST(check_example_approval_plugin(1588"2020-02-15T08:05:23", "That is not allowed on the weekend!"));1589RUN_TEST(check_multiple_approval_plugin_and_arguments());15901591RUN_TEST(check_python_plugins_do_not_affect_each_other());1592RUN_TEST(check_plugin_unload());15931594#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION1595RUN_TEST(check_example_debugging("plugin@err"));1596RUN_TEST(check_example_debugging("plugin@info"));1597RUN_TEST(check_example_debugging("load@diag"));1598RUN_TEST(check_example_debugging("sudo_cb@info"));1599RUN_TEST(check_example_debugging("c_calls@diag"));1600RUN_TEST(check_example_debugging("c_calls@info"));1601RUN_TEST(check_example_debugging("py_calls@diag"));1602RUN_TEST(check_example_debugging("py_calls@info"));1603RUN_TEST(check_example_debugging("plugin@err"));1604RUN_TEST(check_plugin_unload());1605#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */16061607if (ntests != 0) {1608printf("%s: %d tests run, %d errors, %d%% success rate\n",1609getprogname(), ntests, errors, (ntests - errors) * 100 / ntests);1610}16111612return errors;1613}161416151616