diff options
author | Lennart Poettering <lennart@poettering.net> | 2014-02-25 20:37:03 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2014-02-26 02:19:28 +0100 |
commit | 4298d0b5128326621c8f537107c4c8b459490721 (patch) | |
tree | 38ed9f6806b45d153f39ea9da61eae5d34530523 /src/core/execute.c | |
parent | 9875fd7875d433eea5c6e3319916e1be18722086 (diff) |
core: add new RestrictAddressFamilies= switch
This new unit settings allows restricting which address families are
available to processes. This is an effective way to minimize the attack
surface of services, by turning off entire network stacks for them.
This is based on seccomp, and does not work on x86-32, since seccomp
cannot filter socketcall() syscalls on that platform.
Diffstat (limited to 'src/core/execute.c')
-rw-r--r-- | src/core/execute.c | 135 |
1 files changed, 133 insertions, 2 deletions
diff --git a/src/core/execute.c b/src/core/execute.c index aeddd2e412..fff25c2b23 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -81,6 +81,7 @@ #include "async.h" #include "selinux-util.h" #include "errno-list.h" +#include "af-list.h" #include "apparmor-util.h" #ifdef HAVE_SECCOMP @@ -994,9 +995,130 @@ static int apply_seccomp(ExecContext *c) { finish: seccomp_release(seccomp); + return r; +} + +static int apply_address_families(ExecContext *c) { + scmp_filter_ctx *seccomp; + Iterator i; + int r; + + assert(c); + + seccomp = seccomp_init(SCMP_ACT_ALLOW); + if (!seccomp) + return -ENOMEM; + + r = seccomp_add_secondary_archs(seccomp); + if (r < 0) + goto finish; + + if (c->address_families_whitelist) { + int af, first = 0, last = 0; + void *afp; + + /* If this is a whitelist, we first block the address + * families that are out of range and then everything + * that is not in the set. First, we find the lowest + * and highest address family in the set. */ + + SET_FOREACH(afp, c->address_families, i) { + af = PTR_TO_INT(afp); + if (af <= 0 || af >= af_max()) + continue; + + if (first == 0 || af < first) + first = af; + + if (last == 0 || af > last) + last = af; + } + + assert((first == 0) == (last == 0)); + + if (first == 0) { + + /* No entries in the valid range, block everything */ + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPROTONOSUPPORT), + SCMP_SYS(socket), + 0); + if (r < 0) + goto finish; + + } else { + + /* Block everything below the first entry */ + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPROTONOSUPPORT), + SCMP_SYS(socket), + 1, + SCMP_A0(SCMP_CMP_LT, first)); + if (r < 0) + goto finish; + + /* Block everything above the last entry */ + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPROTONOSUPPORT), + SCMP_SYS(socket), + 1, + SCMP_A0(SCMP_CMP_GT, last)); + if (r < 0) + goto finish; + + /* Block everything between the first and last + * entry */ + for (af = 1; af < af_max(); af++) { + + if (set_contains(c->address_families, INT_TO_PTR(af))) + continue; + + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPROTONOSUPPORT), + SCMP_SYS(socket), + 1, + SCMP_A0(SCMP_CMP_EQ, af)); + if (r < 0) + goto finish; + } + } + + } else { + void *af; + + /* If this is a blacklist, then generate one rule for + * each address family that are then combined in OR + * checks. */ + + SET_FOREACH(af, c->address_families, i) { + + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPROTONOSUPPORT), + SCMP_SYS(socket), + 1, + SCMP_A0(SCMP_CMP_EQ, PTR_TO_INT(af))); + if (r < 0) + goto finish; + } + } + + r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); + if (r < 0) + goto finish; + + r = seccomp_load(seccomp); + +finish: + seccomp_release(seccomp); return r; } + #endif static void do_idle_pipe_dance(int idle_pipe[4]) { @@ -1584,6 +1706,14 @@ int exec_spawn(ExecCommand *command, } #ifdef HAVE_SECCOMP + if (context->address_families) { + err = apply_address_families(context); + if (err < 0) { + r = EXIT_ADDRESS_FAMILIES; + goto fail_child; + } + } + if (context->syscall_filter || context->syscall_archs) { err = apply_seccomp(context); if (err < 0) { @@ -1777,13 +1907,14 @@ void exec_context_done(ExecContext *c) { free(c->apparmor_profile); c->apparmor_profile = NULL; -#ifdef HAVE_SECCOMP set_free(c->syscall_filter); c->syscall_filter = NULL; set_free(c->syscall_archs); c->syscall_archs = NULL; -#endif + + set_free(c->address_families); + c->address_families = NULL; } void exec_command_done(ExecCommand *c) { |