Commit 50798138 by Serge Hallyn Committed by Stéphane Graber

seccomp: introduce v2 policy (v2)

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: 's avatarSerge Hallyn <serge.hallyn@ubuntu.com> Acked-by: 's avatarStéphane Graber <stgraber@ubuntu.com>
parent 1f92162d
...@@ -34,6 +34,233 @@ ...@@ -34,6 +34,233 @@
lxc_log_define(lxc_seccomp, lxc); 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 first line of the config file has a policy language version
* the second line has some directives * the second line has some directives
...@@ -48,7 +275,7 @@ static int parse_config(FILE *f, struct lxc_conf *conf) ...@@ -48,7 +275,7 @@ static int parse_config(FILE *f, struct lxc_conf *conf)
int ret, version; int ret, version;
ret = fscanf(f, "%d\n", &version); ret = fscanf(f, "%d\n", &version);
if (ret != 1 || version != 1) { if (ret != 1 || (version != 1 && version != 2)) {
ERROR("invalid version"); ERROR("invalid version");
return -1; return -1;
} }
...@@ -56,31 +283,19 @@ static int parse_config(FILE *f, struct lxc_conf *conf) ...@@ -56,31 +283,19 @@ static int parse_config(FILE *f, struct lxc_conf *conf)
ERROR("invalid config file"); ERROR("invalid config file");
return -1; return -1;
} }
if (!strstr(line, "whitelist")) { if (version == 1 && !strstr(line, "whitelist")) {
ERROR("only whitelist policy is supported"); ERROR("only whitelist policy is supported");
return -1; return -1;
} }
if (strstr(line, "debug")) { if (strstr(line, "debug")) {
ERROR("debug not yet implemented"); ERROR("debug not yet implemented");
return -1; return -1;
} }
/* now read in the whitelist entries one per line */
while (fgets(line, 1024, f)) { if (version == 1)
int nr; return parse_config_v1(f, conf);
ret = sscanf(line, "%d", &nr); return parse_config_v2(f, line, conf);
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;
} }
int lxc_read_seccomp_config(struct lxc_conf *conf) int lxc_read_seccomp_config(struct lxc_conf *conf)
...@@ -93,10 +308,10 @@ 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 #if HAVE_SCMP_FILTER_CTX
/* XXX for debug, pass in SCMP_ACT_TRAP */ /* 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; ret = !conf->seccomp_ctx;
#else #else
ret = seccomp_init(SCMP_ACT_ERRNO(31)) < 0; ret = seccomp_init(SCMP_ACT_KILL) < 0;
#endif #endif
if (ret) { if (ret) {
ERROR("failed initializing seccomp"); ERROR("failed initializing seccomp");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment