From 19c0b0b9a5039b842cf9e6c3e7ece75fb8725602 Mon Sep 17 00:00:00 2001 From: Ronny Chevalier Date: Sat, 30 Jan 2016 17:26:39 +0100 Subject: core: set NoNewPrivileges for seccomp if we don't have CAP_SYS_ADMIN The manpage of seccomp specify that using seccomp with SECCOMP_SET_MODE_FILTER will return EACCES if the caller do not have CAP_SYS_ADMIN set, or if the no_new_privileges bit is not set. Hence, without NoNewPrivilege set, it is impossible to use a SystemCall* directive with a User directive set in system mode. Now, NoNewPrivileges is set if we are in user mode, or if we are in system mode and we don't have CAP_SYS_ADMIN, and SystemCall* directives are used. --- src/core/execute.c | 16 ++++++++------ src/test/test-execute.c | 57 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 51 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/core/execute.c b/src/core/execute.c index 8ede9e9afb..0c311ec330 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1824,6 +1825,11 @@ static int exec_child( if (params->apply_permissions) { + bool use_address_families = context->address_families_whitelist || + !set_isempty(context->address_families); + bool use_syscall_filter = context->syscall_whitelist || + !set_isempty(context->syscall_filter) || + !set_isempty(context->syscall_archs); int secure_bits = context->secure_bits; for (i = 0; i < _RLIMIT_MAX; i++) { @@ -1890,15 +1896,15 @@ static int exec_child( return -errno; } - if (context->no_new_privileges) + if (context->no_new_privileges || + (!have_effective_cap(CAP_SYS_ADMIN) && (use_address_families || use_syscall_filter))) if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { *exit_status = EXIT_NO_NEW_PRIVILEGES; return -errno; } #ifdef HAVE_SECCOMP - if (context->address_families_whitelist || - !set_isempty(context->address_families)) { + if (use_address_families) { r = apply_address_families(context); if (r < 0) { *exit_status = EXIT_ADDRESS_FAMILIES; @@ -1906,9 +1912,7 @@ static int exec_child( } } - if (context->syscall_whitelist || - !set_isempty(context->syscall_filter) || - !set_isempty(context->syscall_archs)) { + if (use_syscall_filter) { r = apply_seccomp(context); if (r < 0) { *exit_status = EXIT_SECCOMP; diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 0d2e4bfc15..5645f5c086 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -130,6 +130,15 @@ static void test_exec_systemcallerrornumber(Manager *m) { #endif } +static void test_exec_systemcall_system_mode_with_user(Manager *m) { +#ifdef HAVE_SECCOMP + if (getpwnam("nobody")) + test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED); + else + log_error_errno(errno, "Skipping test_exec_systemcall_system_mode_with_user, could not find nobody user: %m"); +#endif +} + static void test_exec_user(Manager *m) { if (getpwnam("nobody")) test(m, "exec-user.service", 0, CLD_EXITED); @@ -267,8 +276,31 @@ static void test_exec_spec_interpolation(Manager *m) { test(m, "exec-spec-interpolation.service", 0, CLD_EXITED); } +static int run_tests(ManagerRunningAs running_as, test_function_t *tests) { + test_function_t *test = NULL; + Manager *m = NULL; + int r; + + assert_se(tests); + + r = manager_new(running_as, true, &m); + if (MANAGER_SKIP_TEST(r)) { + printf("Skipping test: manager_new: %s\n", strerror(-r)); + return EXIT_TEST_SKIP; + } + assert_se(r >= 0); + assert_se(manager_startup(m, NULL, NULL) >= 0); + + for (test = tests; test && *test; test++) + (*test)(m); + + manager_free(m); + + return 0; +} + int main(int argc, char *argv[]) { - test_function_t tests[] = { + test_function_t user_tests[] = { test_exec_workingdirectory, test_exec_personality, test_exec_ignoresigpipe, @@ -291,8 +323,10 @@ int main(int argc, char *argv[]) { test_exec_spec_interpolation, NULL, }; - test_function_t *test = NULL; - Manager *m = NULL; + test_function_t system_tests[] = { + test_exec_systemcall_system_mode_with_user, + NULL, + }; int r; log_parse_environment(); @@ -317,18 +351,9 @@ int main(int argc, char *argv[]) { assert_se(unsetenv("VAR2") == 0); assert_se(unsetenv("VAR3") == 0); - r = manager_new(MANAGER_USER, true, &m); - if (MANAGER_SKIP_TEST(r)) { - printf("Skipping test: manager_new: %s\n", strerror(-r)); - return EXIT_TEST_SKIP; - } - assert_se(r >= 0); - assert_se(manager_startup(m, NULL, NULL) >= 0); - - for (test = tests; test && *test; test++) - (*test)(m); + r = run_tests(MANAGER_USER, user_tests); + if (r != 0) + return r; - manager_free(m); - - return 0; + return run_tests(MANAGER_SYSTEM, system_tests); } -- cgit v1.2.3-54-g00ecf