[lxc-devel] [PATCH] seccomp: introduce v2 policy (v2)
Serge Hallyn
serge.hallyn at ubuntu.com
Wed Feb 12 21:50:20 UTC 2014
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 at ubuntu.com>
---
src/lxc/seccomp.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 236 insertions(+), 21 deletions(-)
diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c
index ea23b3a..4c01be7 100644
--- a/src/lxc/seccomp.c
+++ b/src/lxc/seccomp.c
@@ -34,6 +34,233 @@
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");
--
1.9.rc1
More information about the lxc-devel
mailing list