chiark / gitweb /
core: add new RestrictAddressFamilies= switch
[elogind.git] / src / core / execute.c
index f8b7521ff9ceb20b4a8f81b96036d40cb37c2e68..fff25c2b236b9c96dafe8232a507a126d7974502 100644 (file)
 #include <seccomp.h>
 #endif
 
+#ifdef HAVE_APPARMOR
+#include <sys/apparmor.h>
+#endif
+
 #include "execute.h"
 #include "strv.h"
 #include "macro.h"
@@ -77,6 +81,8 @@
 #include "async.h"
 #include "selinux-util.h"
 #include "errno-list.h"
+#include "af-list.h"
+#include "apparmor-util.h"
 
 #ifdef HAVE_SECCOMP
 #include "seccomp-util.h"
@@ -964,34 +970,155 @@ static int apply_seccomp(ExecContext *c) {
                         r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1);
                         if (r == -EEXIST)
                                 continue;
-                        if (r < 0) {
-                                seccomp_release(seccomp);
-                                return r;
-                        }
+                        if (r < 0)
+                                goto finish;
                 }
-        } else {
 
+        } else {
                 r = seccomp_add_secondary_archs(seccomp);
-                if (r < 0) {
-                        seccomp_release(seccomp);
-                        return r;
-                }
+                if (r < 0)
+                        goto finish;
         }
 
         action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action;
         SET_FOREACH(id, c->syscall_filter, i) {
                 r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0);
-                if (r < 0) {
-                        seccomp_release(seccomp);
-                        return r;
-                }
+                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;
+}
+
+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]) {
@@ -1579,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) {
@@ -1597,6 +1732,16 @@ int exec_spawn(ExecCommand *command,
                                 }
                         }
 #endif
+
+#ifdef HAVE_APPARMOR
+                        if (context->apparmor_profile && use_apparmor()) {
+                                err = aa_change_onexec(context->apparmor_profile);
+                                if (err < 0 && !context->apparmor_profile_ignore) {
+                                        r = EXIT_APPARMOR_PROFILE;
+                                        goto fail_child;
+                                }
+                        }
+#endif
                 }
 
                 err = build_environment(context, n_fds, watchdog_usec, home, username, shell, &our_env);
@@ -1759,13 +1904,17 @@ void exec_context_done(ExecContext *c) {
         free(c->selinux_context);
         c->selinux_context = NULL;
 
-#ifdef HAVE_SECCOMP
+        free(c->apparmor_profile);
+        c->apparmor_profile = NULL;
+
         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) {
@@ -2188,6 +2337,11 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 fprintf(f,
                         "%sSystemCallErrorNumber: %s\n",
                         prefix, strna(errno_to_name(c->syscall_errno)));
+
+        if (c->apparmor_profile)
+                fprintf(f,
+                        "%sAppArmorProfile: %s%s\n",
+                        prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
 }
 
 void exec_status_start(ExecStatus *s, pid_t pid) {