diff options
-rw-r--r-- | src/test/test-execute.c | 351 | ||||
-rw-r--r-- | test/test-execute/exec-dynamicuser-statedir-migrate-step1.service | 16 | ||||
-rw-r--r-- | test/test-execute/exec-dynamicuser-statedir-migrate-step2.service | 32 | ||||
-rw-r--r-- | test/test-execute/exec-dynamicuser-statedir.service | 122 | ||||
-rw-r--r-- | test/test-execute/exec-privatenetwork-yes.service | 1 | ||||
-rw-r--r-- | test/test-execute/exec-specifier-system.service | 11 | ||||
-rw-r--r-- | test/test-execute/exec-specifier-user.service | 11 | ||||
-rw-r--r-- | test/test-execute/exec-specifier.service | 5 | ||||
-rw-r--r-- | test/test-execute/exec-specifier@.service | 5 |
9 files changed, 333 insertions, 221 deletions
diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 52a38f7c52..ceecf373bf 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include <stdio.h> +#include <sys/mount.h> #include <sys/prctl.h> #include <sys/types.h> @@ -18,6 +19,7 @@ #include "manager.h" #include "missing_prctl.h" #include "mkdir.h" +#include "mount-util.h" #include "path-util.h" #include "process-util.h" #include "rm-rf.h" @@ -34,6 +36,8 @@ #include "user-util.h" #include "virt.h" +#define PRIVATE_UNIT_DIR "/run/test-execute-unit-dir" + static char *user_runtime_unit_dir = NULL; static bool can_unshare; @@ -413,7 +417,7 @@ static void test_exec_privatedevices(Manager *m) { test(m, "exec-privatedevices-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); test(m, "exec-privatedevices-no.service", 0, CLD_EXITED); test(m, "exec-privatedevices-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); - test(m, "exec-privatedevices-yes-with-group.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(m, "exec-privatedevices-yes-with-group.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_GROUP, CLD_EXITED); /* We use capsh to test if the capabilities are * properly set, so be sure that it exists */ @@ -424,9 +428,9 @@ static void test_exec_privatedevices(Manager *m) { } test(m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED); - test(m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED); + test(m, "exec-privatedevices-no-capability-mknod.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED); test(m, "exec-privatedevices-yes-capability-sys-rawio.service", 0, CLD_EXITED); - test(m, "exec-privatedevices-no-capability-sys-rawio.service", 0, CLD_EXITED); + test(m, "exec-privatedevices-no-capability-sys-rawio.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED); } static void test_exec_protecthome(Manager *m) { @@ -456,7 +460,7 @@ static void test_exec_protectkernelmodules(Manager *m) { return; } - test(m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED); + test(m, "exec-protectkernelmodules-no-capabilities.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED); test(m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED); test(m, "exec-protectkernelmodules-yes-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); } @@ -777,7 +781,7 @@ static void test_exec_systemcallfilter_system(Manager *m) { return; } - test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED); + test(m, "exec-systemcallfilter-system-user.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED); if (!check_nobody_user_and_group()) { log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__); @@ -789,12 +793,12 @@ static void test_exec_systemcallfilter_system(Manager *m) { return; } - test(m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED); + test(m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED); #endif } static void test_exec_user(Manager *m) { - test(m, "exec-user.service", 0, CLD_EXITED); + test(m, "exec-user.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED); if (!check_nobody_user_and_group()) { log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__); @@ -806,11 +810,11 @@ static void test_exec_user(Manager *m) { return; } - test(m, "exec-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED); + test(m, "exec-user-" NOBODY_USER_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED); } static void test_exec_group(Manager *m) { - test(m, "exec-group.service", 0, CLD_EXITED); + test(m, "exec-group.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED); if (!check_nobody_user_and_group()) { log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__); @@ -822,16 +826,17 @@ static void test_exec_group(Manager *m) { return; } - test(m, "exec-group-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED); + test(m, "exec-group-" NOBODY_GROUP_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED); } static void test_exec_supplementarygroups(Manager *m) { - test(m, "exec-supplementarygroups.service", 0, CLD_EXITED); - test(m, "exec-supplementarygroups-single-group.service", 0, CLD_EXITED); - test(m, "exec-supplementarygroups-single-group-user.service", 0, CLD_EXITED); - test(m, "exec-supplementarygroups-multiple-groups-default-group-user.service", 0, CLD_EXITED); - test(m, "exec-supplementarygroups-multiple-groups-withgid.service", 0, CLD_EXITED); - test(m, "exec-supplementarygroups-multiple-groups-withuid.service", 0, CLD_EXITED); + int status = MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP; + test(m, "exec-supplementarygroups.service", status, CLD_EXITED); + test(m, "exec-supplementarygroups-single-group.service", status, CLD_EXITED); + test(m, "exec-supplementarygroups-single-group-user.service", status, CLD_EXITED); + test(m, "exec-supplementarygroups-multiple-groups-default-group-user.service", status, CLD_EXITED); + test(m, "exec-supplementarygroups-multiple-groups-withgid.service", status, CLD_EXITED); + test(m, "exec-supplementarygroups-multiple-groups-withuid.service", status, CLD_EXITED); } static char* private_directory_bad(Manager *m) { @@ -863,14 +868,16 @@ static void test_exec_dynamicuser(Manager *m) { return; } - test(m, "exec-dynamicuser-fixeduser.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + int status = can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_NAMESPACE : EXIT_GROUP; + + test(m, "exec-dynamicuser-fixeduser.service", status, CLD_EXITED); if (check_user_has_group_with_same_name("adm")) - test(m, "exec-dynamicuser-fixeduser-adm.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(m, "exec-dynamicuser-fixeduser-adm.service", status, CLD_EXITED); if (check_user_has_group_with_same_name("games")) - test(m, "exec-dynamicuser-fixeduser-games.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); - test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); - test(m, "exec-dynamicuser-supplementarygroups.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); - test(m, "exec-dynamicuser-statedir.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(m, "exec-dynamicuser-fixeduser-games.service", status, CLD_EXITED); + test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", status, CLD_EXITED); + test(m, "exec-dynamicuser-supplementarygroups.service", status, CLD_EXITED); + test(m, "exec-dynamicuser-statedir.service", status, CLD_EXITED); (void) rm_rf("/var/lib/quux", REMOVE_ROOT|REMOVE_PHYSICAL); (void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL); @@ -882,7 +889,7 @@ static void test_exec_dynamicuser(Manager *m) { (void) rm_rf("/var/lib/private/waldo", REMOVE_ROOT|REMOVE_PHYSICAL); test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED); - test(m, "exec-dynamicuser-statedir-migrate-step2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(m, "exec-dynamicuser-statedir-migrate-step2.service", status, CLD_EXITED); test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED); (void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL); @@ -890,9 +897,9 @@ static void test_exec_dynamicuser(Manager *m) { (void) rm_rf("/var/lib/private/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL); (void) rm_rf("/var/lib/private/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL); - test(m, "exec-dynamicuser-runtimedirectory1.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); - test(m, "exec-dynamicuser-runtimedirectory2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); - test(m, "exec-dynamicuser-runtimedirectory3.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(m, "exec-dynamicuser-runtimedirectory1.service", status, CLD_EXITED); + test(m, "exec-dynamicuser-runtimedirectory2.service", status, CLD_EXITED); + test(m, "exec-dynamicuser-runtimedirectory3.service", status, CLD_EXITED); } static void test_exec_environment(Manager *m) { @@ -958,9 +965,12 @@ static void test_exec_umask(Manager *m) { } static void test_exec_runtimedirectory(Manager *m) { + (void) rm_rf("/run/test-exec_runtimedirectory2", REMOVE_ROOT|REMOVE_PHYSICAL); test(m, "exec-runtimedirectory.service", 0, CLD_EXITED); + (void) rm_rf("/run/test-exec_runtimedirectory2", REMOVE_ROOT|REMOVE_PHYSICAL); + test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED); - test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED); + test(m, "exec-runtimedirectory-owner.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED); if (!check_nobody_user_and_group()) { log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__); @@ -972,7 +982,7 @@ static void test_exec_runtimedirectory(Manager *m) { return; } - test(m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED); + test(m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED); } static void test_exec_capabilityboundingset(Manager *m) { @@ -1046,7 +1056,7 @@ static void test_exec_privatenetwork(Manager *m) { return; } - test(m, "exec-privatenetwork-yes.service", can_unshare ? 0 : EXIT_NETWORK, CLD_EXITED); + test(m, "exec-privatenetwork-yes.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_NETWORK : EXIT_FAILURE, CLD_EXITED); } static void test_exec_oomscoreadjust(Manager *m) { @@ -1056,7 +1066,7 @@ static void test_exec_oomscoreadjust(Manager *m) { log_notice("Testing in container, skipping remaining tests in %s", __func__); return; } - test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED); + test(m, "exec-oomscoreadjust-negative.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED); } static void test_exec_ioschedulingclass(Manager *m) { @@ -1068,7 +1078,7 @@ static void test_exec_ioschedulingclass(Manager *m) { log_notice("Testing in container, skipping remaining tests in %s", __func__); return; } - test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED); + test(m, "exec-ioschedulingclass-realtime.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_IOPRIO, CLD_EXITED); } static void test_exec_unsetenvironment(Manager *m) { @@ -1077,9 +1087,13 @@ static void test_exec_unsetenvironment(Manager *m) { static void test_exec_specifier(Manager *m) { test(m, "exec-specifier.service", 0, CLD_EXITED); + if (MANAGER_IS_SYSTEM(m)) + test(m, "exec-specifier-system.service", 0, CLD_EXITED); + else + test(m, "exec-specifier-user.service", 0, CLD_EXITED); test(m, "exec-specifier@foo-bar.service", 0, CLD_EXITED); test(m, "exec-specifier-interpolation.service", 0, CLD_EXITED); - test(m, "exec-specifier-credentials-dir.service", 0, CLD_EXITED); + test(m, "exec-specifier-credentials-dir.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_CREDENTIALS, CLD_EXITED); } static void test_exec_standardinput(Manager *m) { @@ -1112,7 +1126,7 @@ static void test_exec_umask_namespace(Manager *m) { log_notice("Testing without inaccessible, skipping %s", __func__); return; } - test(m, "exec-umask-namespace.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(m, "exec-umask-namespace.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_NAMESPACE : EXIT_GROUP, CLD_EXITED); } typedef struct test_entry { @@ -1122,40 +1136,27 @@ typedef struct test_entry { #define entry(x) {x, #x} -static int run_tests(LookupScope scope, const test_entry tests[], char **patterns) { +static void run_tests(LookupScope scope, char **patterns) { + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; + _cleanup_free_ char *unit_paths = NULL; _cleanup_(manager_freep) Manager *m = NULL; int r; - assert_se(tests); - - r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m); - m->default_std_output = EXEC_OUTPUT_NULL; /* don't rely on host journald */ - if (manager_errno_skip_test(r)) - return log_tests_skipped_errno(r, "manager_new"); - assert_se(r >= 0); - assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); - - for (const test_entry *test = tests; test->f; test++) - if (strv_fnmatch_or_empty(patterns, test->name, FNM_NOESCAPE)) - test->f(m); - else - log_info("Skipping %s because it does not match any pattern.", test->name); - - return 0; -} - -int main(int argc, char *argv[]) { - _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; - - static const test_entry user_tests[] = { + static const test_entry tests[] = { entry(test_exec_basic), entry(test_exec_ambientcapabilities), entry(test_exec_bindpaths), entry(test_exec_capabilityboundingset), entry(test_exec_condition), entry(test_exec_cpuaffinity), + entry(test_exec_dynamicuser), entry(test_exec_environment), entry(test_exec_environmentfile), + entry(test_exec_execsearchpath), + entry(test_exec_execsearchpath_environment), + entry(test_exec_execsearchpath_environment_files), + entry(test_exec_execsearchpath_passenvironment), + entry(test_exec_execsearchpath_specifier), entry(test_exec_group), entry(test_exec_ignoresigpipe), entry(test_exec_inaccessiblepaths), @@ -1174,6 +1175,7 @@ int main(int argc, char *argv[]) { entry(test_exec_readwritepaths), entry(test_exec_restrictnamespaces), entry(test_exec_runtimedirectory), + entry(test_exec_specifier), entry(test_exec_standardinput), entry(test_exec_standardoutput), entry(test_exec_standardoutput_append), @@ -1181,35 +1183,15 @@ int main(int argc, char *argv[]) { entry(test_exec_supplementarygroups), entry(test_exec_systemcallerrornumber), entry(test_exec_systemcallfilter), + entry(test_exec_systemcallfilter_system), entry(test_exec_temporaryfilesystem), entry(test_exec_umask), + entry(test_exec_umask_namespace), entry(test_exec_unsetenvironment), entry(test_exec_user), entry(test_exec_workingdirectory), - entry(test_exec_execsearchpath), - entry(test_exec_execsearchpath_environment), - entry(test_exec_execsearchpath_environment_files), - entry(test_exec_execsearchpath_passenvironment), - {}, - }; - static const test_entry system_tests[] = { - entry(test_exec_dynamicuser), - entry(test_exec_specifier), - entry(test_exec_execsearchpath_specifier), - entry(test_exec_systemcallfilter_system), - entry(test_exec_umask_namespace), {}, }; - int r; - - test_setup_logging(LOG_DEBUG); - -#if HAS_FEATURE_ADDRESS_SANITIZER - if (strstr_ptr(ci_environment(), "travis") || strstr_ptr(ci_environment(), "github-actions")) { - log_notice("Running on Travis CI/GH Actions under ASan, skipping, see https://github.com/systemd/systemd/issues/10696"); - return EXIT_TEST_SKIP; - } -#endif assert_se(unsetenv("USER") == 0); assert_se(unsetenv("LOGNAME") == 0); @@ -1217,68 +1199,185 @@ int main(int argc, char *argv[]) { assert_se(unsetenv("HOME") == 0); assert_se(unsetenv("TMPDIR") == 0); - can_unshare = have_namespaces(); - - /* It is needed otherwise cgroup creation fails */ - if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) - return log_tests_skipped("not privileged"); - - r = enter_cgroup_subroot(NULL); - if (r == -ENOMEDIUM) - return log_tests_skipped("cgroupfs not available"); - - if (path_is_read_only_fs("/sys") > 0) - return log_tests_skipped("/sys is mounted read-only"); + /* Unset VARx, especially, VAR1, VAR2 and VAR3, which are used in the PassEnvironment test cases, + * otherwise (and if they are present in the environment), `manager_default_environment` will copy + * them into the default environment which is passed to each created job, which will make the tests + * that expect those not to be present to fail. */ + assert_se(unsetenv("VAR1") == 0); + assert_se(unsetenv("VAR2") == 0); + assert_se(unsetenv("VAR3") == 0); + assert_se(unsetenv("VAR4") == 0); + assert_se(unsetenv("VAR5") == 0); - _cleanup_free_ char *unit_dir = NULL, *unit_paths = NULL; - assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); assert_se(user_runtime_unit_dir = path_join(runtime_dir, "systemd/user")); - assert_se(unit_paths = strjoin(unit_dir, ":", user_runtime_unit_dir)); + assert_se(unit_paths = strjoin(PRIVATE_UNIT_DIR, ":", user_runtime_unit_dir)); assert_se(set_unit_path(unit_paths) >= 0); - /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test - * cases, otherwise (and if they are present in the environment), - * `manager_default_environment` will copy them into the default - * environment which is passed to each created job, which will make the - * tests that expect those not to be present to fail. - */ - assert_se(unsetenv("VAR1") == 0); - assert_se(unsetenv("VAR2") == 0); - assert_se(unsetenv("VAR3") == 0); + r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m); + if (manager_errno_skip_test(r)) + return (void) log_tests_skipped_errno(r, "manager_new"); + assert_se(r >= 0); + + m->default_std_output = EXEC_OUTPUT_NULL; /* don't rely on host journald */ + assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); + + /* Uncomment below if you want to make debugging logs stored to journal. */ + //manager_override_log_target(m, LOG_TARGET_AUTO); + //manager_override_log_level(m, LOG_DEBUG); + + for (const test_entry *test = tests; test->f; test++) + if (strv_fnmatch_or_empty(patterns, test->name, FNM_NOESCAPE)) + test->f(m); + else + log_info("Skipping %s because it does not match any pattern.", test->name); +} + +static int prepare_ns(const char *process_name) { + int r; + + r = safe_fork(process_name, + FORK_RESET_SIGNALS | + FORK_CLOSE_ALL_FDS | + FORK_DEATHSIG | + FORK_WAIT | + FORK_REOPEN_LOG | + FORK_LOG | + FORK_NEW_MOUNTNS | + FORK_MOUNTNS_SLAVE, + NULL); + assert_se(r >= 0); + if (r == 0) { + _cleanup_free_ char *unit_dir = NULL; + + /* Make "/" read-only. */ + assert_se(mount_nofollow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_BIND|MS_REMOUNT, NULL) >= 0); + + /* Creating a new user namespace in the above means all MS_SHARED mounts become MS_SLAVE. + * Let's put them back to MS_SHARED here, since that's what we want as defaults. (This will + * not reconnect propagation, but simply create new peer groups for all our mounts). */ + assert_se(mount_follow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_SHARED|MS_REC, NULL) >= 0); + + assert_se(mkdir_p(PRIVATE_UNIT_DIR, 0755) >= 0); + + /* Mount tmpfs on the following directories to make not StateDirectory= or friends disturb the host. */ + FOREACH_STRING(p, "/root", "/tmp", "/var/tmp", "/var/lib", PRIVATE_UNIT_DIR) + assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, NULL) >= 0); - r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1); - if (r != 0) - return r; + /* Copy unit files to make them accessible even when unprivileged. */ + assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0); + assert_se(copy_directory(unit_dir, PRIVATE_UNIT_DIR, COPY_MERGE_EMPTY) >= 0); - r = run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1); - if (r != 0) - return r; + /* Prepare credstore like tmpfiles.d/credstore.conf for LoadCredential= tests. */ + FOREACH_STRING(p, "/run/credstore", "/run/credstore.encrypted") { + assert_se(mkdir_p(p, 0) >= 0); + assert_se(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, "mode=0000") >= 0); + } + } + + return r; +} + +TEST(run_tests_root) { + _cleanup_strv_free_ char **filters = NULL; + + if (!have_namespaces()) + return (void) log_tests_skipped("unshare() is disabled"); + + /* safe_fork() clears saved_argv in the child process. Let's copy it. */ + assert_se(filters = strv_copy(strv_skip(saved_argv, 1))); + + if (prepare_ns("(test-execute-root)") == 0) { + can_unshare = true; + run_tests(LOOKUP_SCOPE_SYSTEM, filters); + _exit(EXIT_SUCCESS); + } +} + +TEST(run_tests_without_unshare) { + if (!have_namespaces()) { + /* unshare() is already filtered. */ + can_unshare = false; + run_tests(LOOKUP_SCOPE_SYSTEM, strv_skip(saved_argv, 1)); + return; + } #if HAVE_SECCOMP + _cleanup_strv_free_ char **filters = NULL; + int r; + /* The following tests are for 1beab8b0d0ff2d7d1436b52d4a0c3d56dc908962. */ - if (!is_seccomp_available()) { - log_notice("Seccomp not available, skipping unshare() filtered tests."); - return 0; - } + if (!is_seccomp_available()) + return (void) log_tests_skipped("Seccomp not available, cannot run unshare() filtered tests"); + + /* safe_fork() clears saved_argv in the child process. Let's copy it. */ + assert_se(filters = strv_copy(strv_skip(saved_argv, 1))); - _cleanup_hashmap_free_ Hashmap *s = NULL; - assert_se(s = hashmap_new(NULL)); - r = seccomp_syscall_resolve_name("unshare"); - assert_se(r != __NR_SCMP_ERROR); - assert_se(hashmap_put(s, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)) >= 0); - assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true) >= 0); - assert_se(unshare(CLONE_NEWNS) < 0); - assert_se(errno == EOPNOTSUPP); + if (prepare_ns("(test-execute-without-unshare)") == 0) { + _cleanup_hashmap_free_ Hashmap *s = NULL; - can_unshare = false; + r = seccomp_syscall_resolve_name("unshare"); + assert_se(r != __NR_SCMP_ERROR); + assert_se(hashmap_ensure_put(&s, NULL, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)) >= 0); + assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true) >= 0); - r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1); - if (r != 0) - return r; + /* Check unshare() is actually filtered. */ + assert_se(unshare(CLONE_NEWNS) < 0); + assert_se(errno == EOPNOTSUPP); - return run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1); + can_unshare = false; + run_tests(LOOKUP_SCOPE_SYSTEM, filters); + _exit(EXIT_SUCCESS); + } #else - return 0; + log_tests_skipped("Built without seccomp support, cannot run unshare() filtered tests"); +#endif +} + +TEST(run_tests_unprivileged) { + _cleanup_strv_free_ char **filters = NULL; + + if (!have_namespaces()) + return (void) log_tests_skipped("unshare() is disabled"); + + /* safe_fork() clears saved_argv in the child process. Let's copy it. */ + assert_se(filters = strv_copy(strv_skip(saved_argv, 1))); + + if (prepare_ns("(test-execute-unprivileged)") == 0) { + assert_se(capability_bounding_set_drop(0, /* right_now = */ true) >= 0); + + can_unshare = false; + run_tests(LOOKUP_SCOPE_USER, filters); + _exit(EXIT_SUCCESS); + } +} + +static int intro(void) { +#if HAS_FEATURE_ADDRESS_SANITIZER + if (strstr_ptr(ci_environment(), "travis") || strstr_ptr(ci_environment(), "github-actions")) + return log_tests_skipped("Running on Travis CI/GH Actions under ASan, see https://github.com/systemd/systemd/issues/10696"); #endif + /* It is needed otherwise cgroup creation fails */ + if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) + return log_tests_skipped("not privileged"); + + if (enter_cgroup_subroot(NULL) == -ENOMEDIUM) + return log_tests_skipped("cgroupfs not available"); + + if (path_is_read_only_fs("/sys") > 0) + return log_tests_skipped("/sys is mounted read-only"); + + /* Create dummy network interface for testing PrivateNetwork=yes */ + (void) system("ip link add dummy-test-exec type dummy"); + + return EXIT_SUCCESS; } + +static int outro(void) { + (void) system("ip link del dummy-test-exec"); + (void) rmdir(PRIVATE_UNIT_DIR); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_FULL(LOG_DEBUG, intro, outro); diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service index 1c79e4f722..2a5a1e1ff3 100644 --- a/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service +++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step1.service @@ -3,14 +3,14 @@ Description=Test DynamicUser= migrate StateDirectory= (preparation) [Service] -ExecStart=test -w /var/lib/test-dynamicuser-migrate -ExecStart=test -w /var/lib/test-dynamicuser-migrate2/hoge -ExecStart=test ! -L /var/lib/test-dynamicuser-migrate -ExecStart=test ! -L /var/lib/test-dynamicuser-migrate2/hoge -ExecStart=test -d /var/lib/test-dynamicuser-migrate -ExecStart=test -d /var/lib/test-dynamicuser-migrate2/hoge -ExecStart=touch /var/lib/test-dynamicuser-migrate/yay -ExecStart=touch /var/lib/test-dynamicuser-migrate2/hoge/yayyay +ExecStart=test -w %S/test-dynamicuser-migrate +ExecStart=test -w %S/test-dynamicuser-migrate2/hoge +ExecStart=test ! -L %S/test-dynamicuser-migrate +ExecStart=test ! -L %S/test-dynamicuser-migrate2/hoge +ExecStart=test -d %S/test-dynamicuser-migrate +ExecStart=test -d %S/test-dynamicuser-migrate2/hoge +ExecStart=touch %S/test-dynamicuser-migrate/yay +ExecStart=touch %S/test-dynamicuser-migrate2/hoge/yayyay ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"' Type=oneshot diff --git a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service index 015b74ce22..e89f0c5aae 100644 --- a/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service +++ b/test/test-execute/exec-dynamicuser-statedir-migrate-step2.service @@ -3,22 +3,22 @@ Description=Test DynamicUser= migrate StateDirectory= [Service] -ExecStart=test -w /var/lib/test-dynamicuser-migrate -ExecStart=test -w /var/lib/test-dynamicuser-migrate2/hoge -ExecStart=test -L /var/lib/test-dynamicuser-migrate -ExecStart=test -L /var/lib/test-dynamicuser-migrate2/hoge -ExecStart=test -d /var/lib/test-dynamicuser-migrate -ExecStart=test -d /var/lib/test-dynamicuser-migrate2/hoge -ExecStart=test -f /var/lib/test-dynamicuser-migrate/yay -ExecStart=test -f /var/lib/test-dynamicuser-migrate2/hoge/yayyay -ExecStart=test -d /var/lib/private/test-dynamicuser-migrate -ExecStart=test -d /var/lib/private/test-dynamicuser-migrate2/hoge -ExecStart=test -f /var/lib/private/test-dynamicuser-migrate/yay -ExecStart=test -f /var/lib/private/test-dynamicuser-migrate2/hoge/yayyay -ExecStart=touch /var/lib/test-dynamicuser-migrate/yay -ExecStart=touch /var/lib/test-dynamicuser-migrate2/hoge/yayyay -ExecStart=touch /var/lib/private/test-dynamicuser-migrate/yay -ExecStart=touch /var/lib/private/test-dynamicuser-migrate2/hoge/yayyay +ExecStart=test -w %S/test-dynamicuser-migrate +ExecStart=test -w %S/test-dynamicuser-migrate2/hoge +ExecStart=test -L %S/test-dynamicuser-migrate +ExecStart=test -L %S/test-dynamicuser-migrate2/hoge +ExecStart=test -d %S/test-dynamicuser-migrate +ExecStart=test -d %S/test-dynamicuser-migrate2/hoge +ExecStart=test -f %S/test-dynamicuser-migrate/yay +ExecStart=test -f %S/test-dynamicuser-migrate2/hoge/yayyay +ExecStart=test -d %S/private/test-dynamicuser-migrate +ExecStart=test -d %S/private/test-dynamicuser-migrate2/hoge +ExecStart=test -f %S/private/test-dynamicuser-migrate/yay +ExecStart=test -f %S/private/test-dynamicuser-migrate2/hoge/yayyay +ExecStart=touch %S/test-dynamicuser-migrate/yay +ExecStart=touch %S/test-dynamicuser-migrate2/hoge/yayyay +ExecStart=touch %S/private/test-dynamicuser-migrate/yay +ExecStart=touch %S/private/test-dynamicuser-migrate2/hoge/yayyay ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"' Type=oneshot diff --git a/test/test-execute/exec-dynamicuser-statedir.service b/test/test-execute/exec-dynamicuser-statedir.service index b33b4da74a..b7e36f529e 100644 --- a/test/test-execute/exec-dynamicuser-statedir.service +++ b/test/test-execute/exec-dynamicuser-statedir.service @@ -3,71 +3,71 @@ Description=Test DynamicUser= with StateDirectory= [Service] -ExecStart=test -w /var/lib/waldo -ExecStart=test -w /var/lib/quux/pief -ExecStart=test -w /var/lib/aaa -ExecStart=test -w /var/lib/aaa/bbb -ExecStart=test -w /var/lib/aaa/ccc -ExecStart=test -w /var/lib/xxx -ExecStart=test -w /var/lib/xxx/yyy -ExecStart=test -w /var/lib/xxx/zzz -ExecStart=test -w /var/lib/aaa/111 -ExecStart=test -w /var/lib/aaa/222 -ExecStart=test -w /var/lib/aaa/333 +ExecStart=test -w %S/waldo +ExecStart=test -w %S/quux/pief +ExecStart=test -w %S/aaa +ExecStart=test -w %S/aaa/bbb +ExecStart=test -w %S/aaa/ccc +ExecStart=test -w %S/xxx +ExecStart=test -w %S/xxx/yyy +ExecStart=test -w %S/xxx/zzz +ExecStart=test -w %S/aaa/111 +ExecStart=test -w %S/aaa/222 +ExecStart=test -w %S/aaa/333 -ExecStart=test -d /var/lib/waldo -ExecStart=test -d /var/lib/quux/pief -ExecStart=test -d /var/lib/aaa -ExecStart=test -d /var/lib/aaa/bbb -ExecStart=test -d /var/lib/aaa/ccc -ExecStart=test -d /var/lib/xxx -ExecStart=test -d /var/lib/xxx/yyy -ExecStart=test -d /var/lib/xxx/zzz -ExecStart=test -L /var/lib/aaa/111 -ExecStart=test -L /var/lib/aaa/222 -ExecStart=test -L /var/lib/aaa/333 +ExecStart=test -d %S/waldo +ExecStart=test -d %S/quux/pief +ExecStart=test -d %S/aaa +ExecStart=test -d %S/aaa/bbb +ExecStart=test -d %S/aaa/ccc +ExecStart=test -d %S/xxx +ExecStart=test -d %S/xxx/yyy +ExecStart=test -d %S/xxx/zzz +ExecStart=test -L %S/aaa/111 +ExecStart=test -L %S/aaa/222 +ExecStart=test -L %S/aaa/333 -ExecStart=touch /var/lib/waldo/hoge -ExecStart=touch /var/lib/quux/pief/hoge -ExecStart=touch /var/lib/aaa/hoge -ExecStart=touch /var/lib/aaa/bbb/hoge -ExecStart=touch /var/lib/aaa/ccc/hoge -ExecStart=touch /var/lib/xxx/hoge -ExecStart=touch /var/lib/xxx/yyy/hoge -ExecStart=touch /var/lib/xxx/zzz/hoge -ExecStart=touch /var/lib/aaa/111/foo -ExecStart=touch /var/lib/aaa/222/foo -ExecStart=touch /var/lib/aaa/333/foo +ExecStart=touch %S/waldo/hoge +ExecStart=touch %S/quux/pief/hoge +ExecStart=touch %S/aaa/hoge +ExecStart=touch %S/aaa/bbb/hoge +ExecStart=touch %S/aaa/ccc/hoge +ExecStart=touch %S/xxx/hoge +ExecStart=touch %S/xxx/yyy/hoge +ExecStart=touch %S/xxx/zzz/hoge +ExecStart=touch %S/aaa/111/foo +ExecStart=touch %S/aaa/222/foo +ExecStart=touch %S/aaa/333/foo -ExecStart=test -f /var/lib/waldo/hoge -ExecStart=test -f /var/lib/quux/pief/hoge -ExecStart=test -f /var/lib/aaa/hoge -ExecStart=test -f /var/lib/aaa/bbb/hoge -ExecStart=test -f /var/lib/aaa/ccc/hoge -ExecStart=test -f /var/lib/xxx/hoge -ExecStart=test -f /var/lib/xxx/yyy/hoge -ExecStart=test -f /var/lib/xxx/zzz/hoge -ExecStart=test -f /var/lib/aaa/111/foo -ExecStart=test -f /var/lib/aaa/222/foo -ExecStart=test -f /var/lib/aaa/333/foo -ExecStart=test -f /var/lib/xxx/foo -ExecStart=test -f /var/lib/xxx/yyy/foo -ExecStart=test -f /var/lib/xxx/zzz/foo +ExecStart=test -f %S/waldo/hoge +ExecStart=test -f %S/quux/pief/hoge +ExecStart=test -f %S/aaa/hoge +ExecStart=test -f %S/aaa/bbb/hoge +ExecStart=test -f %S/aaa/ccc/hoge +ExecStart=test -f %S/xxx/hoge +ExecStart=test -f %S/xxx/yyy/hoge +ExecStart=test -f %S/xxx/zzz/hoge +ExecStart=test -f %S/aaa/111/foo +ExecStart=test -f %S/aaa/222/foo +ExecStart=test -f %S/aaa/333/foo +ExecStart=test -f %S/xxx/foo +ExecStart=test -f %S/xxx/yyy/foo +ExecStart=test -f %S/xxx/zzz/foo -ExecStart=test -f /var/lib/private/waldo/hoge -ExecStart=test -f /var/lib/private/quux/pief/hoge -ExecStart=test -f /var/lib/private/aaa/hoge -ExecStart=test -f /var/lib/private/aaa/bbb/hoge -ExecStart=test -f /var/lib/private/aaa/ccc/hoge -ExecStart=test -f /var/lib/private/xxx/hoge -ExecStart=test -f /var/lib/private/xxx/yyy/hoge -ExecStart=test -f /var/lib/private/xxx/zzz/hoge -ExecStart=test -f /var/lib/private/aaa/111/foo -ExecStart=test -f /var/lib/private/aaa/222/foo -ExecStart=test -f /var/lib/private/aaa/333/foo -ExecStart=test -f /var/lib/private/xxx/foo -ExecStart=test -f /var/lib/private/xxx/yyy/foo -ExecStart=test -f /var/lib/private/xxx/zzz/foo +ExecStart=test -f %S/private/waldo/hoge +ExecStart=test -f %S/private/quux/pief/hoge +ExecStart=test -f %S/private/aaa/hoge +ExecStart=test -f %S/private/aaa/bbb/hoge +ExecStart=test -f %S/private/aaa/ccc/hoge +ExecStart=test -f %S/private/xxx/hoge +ExecStart=test -f %S/private/xxx/yyy/hoge +ExecStart=test -f %S/private/xxx/zzz/hoge +ExecStart=test -f %S/private/aaa/111/foo +ExecStart=test -f %S/private/aaa/222/foo +ExecStart=test -f %S/private/aaa/333/foo +ExecStart=test -f %S/private/xxx/foo +ExecStart=test -f %S/private/xxx/yyy/foo +ExecStart=test -f %S/private/xxx/zzz/foo ExecStart=sh -x -c 'test "$$STATE_DIRECTORY" = "%S/aaa:%S/aaa/bbb:%S/aaa/ccc:%S/quux/pief:%S/waldo:%S/xxx:%S/xxx/yyy:%S/xxx/zzz"' diff --git a/test/test-execute/exec-privatenetwork-yes.service b/test/test-execute/exec-privatenetwork-yes.service index 0fff048b94..360099d337 100644 --- a/test/test-execute/exec-privatenetwork-yes.service +++ b/test/test-execute/exec-privatenetwork-yes.service @@ -4,5 +4,6 @@ Description=Test for PrivateNetwork [Service] ExecStart=/bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -Ev ": (lo|(erspan|gre|gretap|ip_vti|ip6_vti|ip6gre|ip6tnl|sit|tunl)0@.*):"' +ExecStart=/bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -F ": dummy-test-exec:"' Type=oneshot PrivateNetwork=yes diff --git a/test/test-execute/exec-specifier-system.service b/test/test-execute/exec-specifier-system.service new file mode 100644 index 0000000000..9e8ee567aa --- /dev/null +++ b/test/test-execute/exec-specifier-system.service @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Test for specifiers (system) + +[Service] +Type=oneshot +ExecStart=test %t = /run +ExecStart=test %S = /var/lib +ExecStart=test %C = /var/cache +ExecStart=test %L = /var/log +ExecStart=test %E = /etc diff --git a/test/test-execute/exec-specifier-user.service b/test/test-execute/exec-specifier-user.service new file mode 100644 index 0000000000..ee0301a426 --- /dev/null +++ b/test/test-execute/exec-specifier-user.service @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Test for specifiers + +[Service] +Type=oneshot +ExecStart=sh -c 'test %t = $$XDG_RUNTIME_DIR' +ExecStart=sh -c 'test %S = %h/.config' +ExecStart=sh -c 'test %C = %h/.cache' +ExecStart=sh -c 'test %L = %h/.config/log' +ExecStart=sh -c 'test %E = %h/.config' diff --git a/test/test-execute/exec-specifier.service b/test/test-execute/exec-specifier.service index 2b487bae8c..512f786f83 100644 --- a/test/test-execute/exec-specifier.service +++ b/test/test-execute/exec-specifier.service @@ -13,11 +13,6 @@ ExecStart=test %I = "" ExecStart=test %j = specifier ExecStart=test %J = specifier ExecStart=test %f = /exec/specifier -ExecStart=test %t = /run -ExecStart=test %S = /var/lib -ExecStart=test %C = /var/cache -ExecStart=test %L = /var/log -ExecStart=test %E = /etc ExecStart=test %T = /tmp ExecStart=test %V = /var/tmp ExecStart=test %d = %t/credentials/%n diff --git a/test/test-execute/exec-specifier@.service b/test/test-execute/exec-specifier@.service index 69e969f716..cb9d0a182a 100644 --- a/test/test-execute/exec-specifier@.service +++ b/test/test-execute/exec-specifier@.service @@ -13,11 +13,6 @@ ExecStart=test %I = foo/bar ExecStart=test %j = specifier ExecStart=test %J = specifier ExecStart=test %f = /foo/bar -ExecStart=test %t = /run -ExecStart=test %S = /var/lib -ExecStart=test %C = /var/cache -ExecStart=test %L = /var/log -ExecStart=test %E = /etc ExecStart=sh -c 'test %u = $$(id -un)' ExecStart=sh -c 'test %U = $$(id -u)' ExecStart=sh -c 'test %g = $$(id -gn)' |