Merge pull request #2760 from ronnychevalier/rc/core_no_new_privileges_seccompv3

core: set NoNewPrivileges for seccomp if we don't have CAP_SYS_ADMIN
This commit is contained in:
Daniel Mack 2016-03-21 12:57:43 +01:00
commit 68de79d6a4
11 changed files with 151 additions and 33 deletions

View file

@ -1563,6 +1563,7 @@ EXTRA_DIST += \
test/test-execute/exec-passenvironment-repeated.service \
test/test-execute/exec-passenvironment.service \
test/test-execute/exec-group.service \
test/test-execute/exec-group-nfsnobody.service \
test/test-execute/exec-ignoresigpipe-no.service \
test/test-execute/exec-ignoresigpipe-yes.service \
test/test-execute/exec-personality-x86-64.service \
@ -1578,7 +1579,10 @@ EXTRA_DIST += \
test/test-execute/exec-systemcallfilter-failing.service \
test/test-execute/exec-systemcallfilter-not-failing2.service \
test/test-execute/exec-systemcallfilter-not-failing.service \
test/test-execute/exec-systemcallfilter-system-user.service \
test/test-execute/exec-systemcallfilter-system-user-nfsnobody.service \
test/test-execute/exec-user.service \
test/test-execute/exec-user-nfsnobody.service \
test/test-execute/exec-workingdirectory.service \
test/test-execute/exec-umask-0177.service \
test/test-execute/exec-umask-default.service \
@ -1595,10 +1599,13 @@ EXTRA_DIST += \
test/test-execute/exec-capabilityboundingset-reset.service \
test/test-execute/exec-capabilityboundingset-simple.service \
test/test-execute/exec-capabilityambientset.service \
test/test-execute/exec-capabilityambientset-nfsnobody.service \
test/test-execute/exec-capabilityambientset-merge.service \
test/test-execute/exec-capabilityambientset-merge-nfsnobody.service \
test/test-execute/exec-runtimedirectory.service \
test/test-execute/exec-runtimedirectory-mode.service \
test/test-execute/exec-runtimedirectory-owner.service \
test/test-execute/exec-runtimedirectory-owner-nfsnobody.service \
test/bus-policy/hello.conf \
test/bus-policy/methods.conf \
test/bus-policy/ownerships.conf \

View file

@ -1155,7 +1155,9 @@
first character of the list is <literal>~</literal>, the
effect is inverted: only the listed system calls will result
in immediate process termination (blacklisting). If running in
user mode and this option is used,
user mode, or in system mode, but without the
<constant>CAP_SYS_ADMIN</constant> capabiblity (e.g. setting
<varname>User=nobody</varname>),
<varname>NoNewPrivileges=yes</varname> is implied. This
feature makes use of the Secure Computing Mode 2 interfaces of
the kernel ('seccomp filtering') and is useful for enforcing a
@ -1214,8 +1216,10 @@
systems. The special <constant>native</constant> identifier
implicitly maps to the native architecture of the system (or
more strictly: to the architecture the system manager is
compiled for). If running in user mode and this option is
used, <varname>NoNewPrivileges=yes</varname> is implied. Note
compiled for). If running in user mode, or in system mode,
but without the <constant>CAP_SYS_ADMIN</constant>
capabiblity (e.g. setting <varname>User=nobody</varname>),
<varname>NoNewPrivileges=yes</varname> is implied. Note
that setting this option to a non-empty list implies that
<constant>native</constant> is included too. By default, this
option is set to the empty list, i.e. no architecture system
@ -1244,8 +1248,10 @@
<function>socketpair()</function> (which creates connected
AF_UNIX sockets only) are unaffected. Note that this option
has no effect on 32-bit x86 and is ignored (but works
correctly on x86-64). If running in user mode and this option
is used, <varname>NoNewPrivileges=yes</varname> is implied. By
correctly on x86-64). If running in user mode, or in system
mode, but without the <constant>CAP_SYS_ADMIN</constant>
capabiblity (e.g. setting <varname>User=nobody</varname>),
<varname>NoNewPrivileges=yes</varname> is implied. By
default, no restriction applies, all address families are
accessible to processes. If assigned the empty string, any
previous list changes are undone.</para>

View file

@ -24,6 +24,7 @@
#include <poll.h>
#include <signal.h>
#include <string.h>
#include <sys/capability.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/socket.h>
@ -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;

View file

@ -130,18 +130,33 @@ 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 if (getpwnam("nfsnobody"))
test(m, "exec-systemcallfilter-system-user-nfsnobody.service", 0, CLD_EXITED);
else
log_error_errno(errno, "Skipping test_exec_systemcall_system_mode_with_user, could not find nobody/nfsnobody user: %m");
#endif
}
static void test_exec_user(Manager *m) {
if (getpwnam("nobody"))
test(m, "exec-user.service", 0, CLD_EXITED);
else if (getpwnam("nfsnobody"))
test(m, "exec-user-nfsnobody.service", 0, CLD_EXITED);
else
log_error_errno(errno, "Skipping test_exec_user, could not find nobody user: %m");
log_error_errno(errno, "Skipping test_exec_user, could not find nobody/nfsnobody user: %m");
}
static void test_exec_group(Manager *m) {
if (getgrnam("nobody"))
test(m, "exec-group.service", 0, CLD_EXITED);
else if (getgrnam("nfsnobody"))
test(m, "exec-group-nfsnobody.service", 0, CLD_EXITED);
else
log_error_errno(errno, "Skipping test_exec_group, could not find nobody group: %m");
log_error_errno(errno, "Skipping test_exec_group, could not find nobody/nfsnobody group: %m");
}
static void test_exec_environment(Manager *m) {
@ -204,8 +219,10 @@ static void test_exec_runtimedirectory(Manager *m) {
test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
if (getgrnam("nobody"))
test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
else if (getgrnam("nfsnobody"))
test(m, "exec-runtimedirectory-owner-nfsnobody.service", 0, CLD_EXITED);
else
log_error_errno(errno, "Skipping test_exec_runtimedirectory-owner, could not find nobody group: %m");
log_error_errno(errno, "Skipping test_exec_runtimedirectory-owner, could not find nobody/nfsnobody group: %m");
}
static void test_exec_capabilityboundingset(Manager *m) {
@ -234,9 +251,15 @@ static void test_exec_capabilityambientset(Manager *m) {
* in the first place for the tests. */
r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
if (r >= 0 || errno != EINVAL) {
test(m, "exec-capabilityambientset.service", 0, CLD_EXITED);
test(m, "exec-capabilityambientset-merge.service", 0, CLD_EXITED);
}
if (getpwnam("nobody")) {
test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
} else if (getpwnam("nfsnobody")) {
test(m, "exec-capabilityambientset.service", 0, CLD_EXITED);
test(m, "exec-capabilityambientset-merge.service", 0, CLD_EXITED);
} else
log_error_errno(errno, "Skipping test_exec_capabilityambientset, could not find nobody/nfsnobody user: %m");
} else
log_error_errno(errno, "Skipping test_exec_capabilityambientset, the kernel does not support ambient capabilities: %m");
}
static void test_exec_privatenetwork(Manager *m) {
@ -267,8 +290,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 +337,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 +365,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);
r = run_tests(MANAGER_USER, user_tests);
if (r != 0)
return r;
for (test = tests; test && *test; test++)
(*test)(m);
manager_free(m);
return 0;
return run_tests(MANAGER_SYSTEM, system_tests);
}

View file

@ -0,0 +1,9 @@
[Unit]
Description=Test for AmbientCapabilities
[Service]
ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000003000"'
Type=oneshot
User=nfsnobody
AmbientCapabilities=CAP_NET_ADMIN
AmbientCapabilities=CAP_NET_RAW

View file

@ -0,0 +1,8 @@
[Unit]
Description=Test for AmbientCapabilities
[Service]
ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000003000"'
Type=oneshot
User=nfsnobody
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW

View file

@ -0,0 +1,7 @@
[Unit]
Description=Test for Group
[Service]
ExecStart=/bin/sh -x -c 'test "$$(id -n -g)" = "nfsnobody"'
Type=oneshot
Group=nfsnobody

View file

@ -0,0 +1,9 @@
[Unit]
Description=Test for RuntimeDirectory owner (must not be the default group of the user if Group is set)
[Service]
ExecStart=/bin/sh -x -c 'group=$$(stat -c %%G /tmp/test-exec_runtimedirectory-owner); test "$$group" = "nfsnobody"'
Type=oneshot
Group=nfsnobody
User=root
RuntimeDirectory=test-exec_runtimedirectory-owner

View file

@ -0,0 +1,11 @@
[Unit]
Description=Test for SystemCallFilter in system mode with User set
[Service]
ExecStart=/bin/echo "Foo bar"
Type=oneshot
User=nfsnobody
SystemCallFilter=~read write open execve ioperm
SystemCallFilter=ioctl
SystemCallFilter=read write open execve
SystemCallFilter=~ioperm

View file

@ -0,0 +1,11 @@
[Unit]
Description=Test for SystemCallFilter in system mode with User set
[Service]
ExecStart=/bin/echo "Foo bar"
Type=oneshot
User=nobody
SystemCallFilter=~read write open execve ioperm
SystemCallFilter=ioctl
SystemCallFilter=read write open execve
SystemCallFilter=~ioperm

View file

@ -0,0 +1,7 @@
[Unit]
Description=Test for User
[Service]
ExecStart=/bin/sh -x -c 'test "$$USER" = "nfsnobody"'
Type=oneshot
User=nfsnobody