]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
seccomp: introduce v2 policy (v2)
authorSerge Hallyn <serge.hallyn@ubuntu.com>
Wed, 12 Feb 2014 21:50:20 +0000 (15:50 -0600)
committerStéphane Graber <stgraber@ubuntu.com>
Wed, 12 Feb 2014 22:01:57 +0000 (17:01 -0500)
v2 allows specifying system calls by name, and specifying
architecture.  A policy looks like:

2
whitelist
open
read
write
close
mount
[x86]
open
read

Also use SCMP_ACT_KILL by default rather than SCMP_ACT_ERRNO(31)  -
which confusingly returns 'EMLINK' on x86_64.  Note this change
is also done for v1 as I think it is worthwhile.

With this patch, I can in fact use a seccomp policy like:

2
blacklist
mknod errno 0

after which 'sudo mknod null c 1 3' silently succeeds without
creating the null device.

changelog v2:
  add blacklist support
  support default action
  support per-rule action

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
Acked-by: Stéphane Graber <stgraber@ubuntu.com>
src/lxc/seccomp.c

index ea23b3ab48b33a1161e4deccd2c1c7882d9cd7ce..4c01be73b536012c3ed8fe090117e44bbc459d0e 100644 (file)
 
 lxc_log_define(lxc_seccomp, lxc);
 
+static int parse_config_v1(FILE *f, struct lxc_conf *conf)
+{
+       char line[1024];
+       int ret;
+
+       while (fgets(line, 1024, f)) {
+               int nr;
+               ret = sscanf(line, "%d", &nr);
+               if (ret != 1)
+                       return -1;
+               ret = seccomp_rule_add(
+#if HAVE_SCMP_FILTER_CTX
+                       conf->seccomp_ctx,
+#endif
+                       SCMP_ACT_ALLOW, nr, 0);
+               if (ret < 0) {
+                       ERROR("failed loading allow rule for %d", nr);
+                       return ret;
+               }
+       }
+       return 0;
+}
+
+static void remove_trailing_newlines(char *l)
+{
+       char *p = l;
+
+       while (*p)
+               p++;
+       while (--p >= l && *p == '\n')
+               *p = '\0';
+}
+
+static uint32_t get_v2_default_action(char *line)
+{
+       uint32_t ret_action = -1;
+
+       while (*line == ' ') line++;
+       // after 'whitelist' or 'blacklist' comes default behavior
+       if (strncmp(line, "kill", 4) == 0)
+               ret_action = SCMP_ACT_KILL;
+       else if (strncmp(line, "errno", 5) == 0) {
+               int e;
+               if (sscanf(line+5, "%d", &e) != 1) {
+                       ERROR("Bad errno value in %s", line);
+                       return -2;
+               }
+               ret_action = SCMP_ACT_ERRNO(e);
+       } else if (strncmp(line, "allow", 5) == 0)
+               ret_action = SCMP_ACT_ALLOW;
+       else if (strncmp(line, "trap", 4) == 0)
+               ret_action = SCMP_ACT_TRAP;
+       return ret_action;
+}
+
+static uint32_t get_and_clear_v2_action(char *line, uint32_t def_action)
+{
+       char *p = strchr(line, ' ');
+       uint32_t ret;
+
+       if (!p)
+               return def_action;
+       *p = '\0';
+       p++;
+       while (*p == ' ')
+               p++;
+       if (!*p || *p == '#')
+               return def_action;
+       ret = get_v2_default_action(p);
+       switch(ret) {
+       case -2: return -1;
+       case -1: return def_action;
+       default: return ret;
+       }
+}
+
+/*
+ * v2 consists of
+ * [x86]
+ * open
+ * read
+ * write
+ * close
+ * # a comment
+ * [x86_64]
+ * open
+ * read
+ * write
+ * close
+ */
+static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf)
+{
+#if HAVE_SCMP_FILTER_CTX
+       char *p;
+       int ret;
+       scmp_filter_ctx *ctx = NULL;
+       bool blacklist = false;
+       uint32_t default_policy_action = -1, default_rule_action = -1, action;
+       uint32_t arch = SCMP_ARCH_NATIVE;
+
+       if (strncmp(line, "blacklist", 9) == 0)
+               blacklist = true;
+       else if (strncmp(line, "whitelist", 9) != 0) {
+               ERROR("Bad seccomp policy style: %s", line);
+               return -1;
+       }
+
+       if ((p = strchr(line, ' '))) {
+               default_policy_action = get_v2_default_action(p+1);
+               if (default_policy_action == -2)
+                       return -1;
+       }
+
+       /* for blacklist, allow any syscall which has no rule */
+       if (blacklist) {
+               if (default_policy_action == -1)
+                       default_policy_action = SCMP_ACT_ALLOW;
+               if (default_rule_action == -1)
+                       default_rule_action = SCMP_ACT_KILL;
+       } else {
+               if (default_policy_action == -1)
+                       default_policy_action = SCMP_ACT_KILL;
+               if (default_rule_action == -1)
+                       default_rule_action = SCMP_ACT_ALLOW;
+       }
+
+       if (default_policy_action != SCMP_ACT_KILL) {
+               ret = seccomp_reset(conf->seccomp_ctx, default_policy_action);
+               if (ret != 0) {
+                       ERROR("Error re-initializing seccomp");
+                       return -1;
+               }
+               if (seccomp_attr_set(conf->seccomp_ctx, SCMP_FLTATR_CTL_NNP, 0)) {
+                       ERROR("failed to turn off n-new-privs");
+                       return -1;
+               }
+       }
+
+       while (fgets(line, 1024, f)) {
+               int nr;
+
+               if (line[0] == '#')
+                       continue;
+               if (strlen(line) == 0)
+                       continue;
+               remove_trailing_newlines(line);
+               INFO("processing: .%s.", line);
+               if (line[0] == '[') {
+                       // read the architecture for next set of rules
+                       if (strcmp(line, "[x86]") == 0 ||
+                                       strcmp(line, "[X86]") == 0)
+                               arch = SCMP_ARCH_X86;
+                       else if (strcmp(line, "[X86_64]") == 0 ||
+                                       strcmp(line, "[x86_64]") == 0)
+                               arch = SCMP_ARCH_X86_64;
+                       else if (strcmp(line, "[arm]") == 0 ||
+                                       strcmp(line, "[ARM]") == 0)
+                               arch = SCMP_ARCH_ARM;
+                       else
+                               goto bad_arch;
+                       if (ctx) {
+                               ERROR("Only two arch sections per policy supported");
+                               goto bad_arch;
+                       }
+                       if ((ctx = seccomp_init(default_policy_action)) == NULL) {
+                               ERROR("Error initializing seccomp context");
+                               return -1;
+                       }
+                       if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0)) {
+                               ERROR("failed to turn off n-new-privs");
+                               seccomp_release(ctx);
+                               return -1;
+                       }
+                       ret = seccomp_arch_add(ctx, arch);
+                       if (ret == -EEXIST) {
+                               seccomp_release(ctx);
+                               ctx = NULL;
+                               continue;
+                       }
+                       if (ret != 0) {
+                               ERROR("Error %d adding arch: %s", ret, line);
+                               goto bad_arch;
+                       }
+                       if (seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE) != 0) {
+                               ERROR("Error removing native arch from %s", line);
+                               goto bad_arch;
+                       }
+                       continue;
+               }
+
+               action = get_and_clear_v2_action(line, default_rule_action);
+               if (action == -1) {
+                       ERROR("Failed to interpret action");
+                       goto bad_rule;
+               }
+               nr = seccomp_syscall_resolve_name_arch(arch, line);
+               if (nr < 0) {
+                       ERROR("Failed to resolve syscall: %s", line);
+                       goto bad_rule;
+               }
+               ret = seccomp_rule_add(ctx ? ctx : conf->seccomp_ctx,
+                               action, nr, 0);
+               if (ret < 0) {
+                       ERROR("failed (%d) loading rule for %s", ret, line);
+                       goto bad_rule;
+               }
+       }
+       if (ctx) {
+               if (seccomp_merge(conf->seccomp_ctx, ctx) != 0) {
+                       seccomp_release(ctx);
+                       ERROR("Error merging seccomp contexts");
+                       return -1;
+               }
+       }
+       return 0;
+
+bad_arch:
+       ERROR("Unsupported arch: %s", line);
+bad_rule:
+       if (ctx)
+               seccomp_release(ctx);
+       return -1;
+#else
+       return -1;
+#endif
+}
+
 /*
  * The first line of the config file has a policy language version
  * the second line has some directives
@@ -48,7 +275,7 @@ static int parse_config(FILE *f, struct lxc_conf *conf)
        int ret, version;
 
        ret = fscanf(f, "%d\n", &version);
-       if (ret != 1 || version != 1) {
+       if (ret != 1 || (version != 1 && version != 2)) {
                ERROR("invalid version");
                return -1;
        }
@@ -56,31 +283,19 @@ static int parse_config(FILE *f, struct lxc_conf *conf)
                ERROR("invalid config file");
                return -1;
        }
-       if (!strstr(line, "whitelist")) {
+       if (version == 1 && !strstr(line, "whitelist")) {
                ERROR("only whitelist policy is supported");
                return -1;
        }
+
        if (strstr(line, "debug")) {
                ERROR("debug not yet implemented");
                return -1;
        }
-       /* now read in the whitelist entries one per line */
-       while (fgets(line, 1024, f)) {
-               int nr;
-               ret = sscanf(line, "%d", &nr);
-               if (ret != 1)
-                       return -1;
-               ret = seccomp_rule_add(
-#if HAVE_SCMP_FILTER_CTX
-                       conf->seccomp_ctx,
-#endif
-                       SCMP_ACT_ALLOW, nr, 0);
-               if (ret < 0) {
-                       ERROR("failed loading allow rule for %d", nr);
-                       return ret;
-               }
-       }
-       return 0;
+
+       if (version == 1)
+               return parse_config_v1(f, conf);
+       return parse_config_v2(f, line, conf);
 }
 
 int lxc_read_seccomp_config(struct lxc_conf *conf)
@@ -93,10 +308,10 @@ int lxc_read_seccomp_config(struct lxc_conf *conf)
 
 #if HAVE_SCMP_FILTER_CTX
        /* XXX for debug, pass in SCMP_ACT_TRAP */
-       conf->seccomp_ctx = seccomp_init(SCMP_ACT_ERRNO(31));
+       conf->seccomp_ctx = seccomp_init(SCMP_ACT_KILL);
        ret = !conf->seccomp_ctx;
 #else
-       ret = seccomp_init(SCMP_ACT_ERRNO(31)) < 0;
+       ret = seccomp_init(SCMP_ACT_KILL) < 0;
 #endif
        if (ret) {
                ERROR("failed initializing seccomp");