[lxc-devel] [PATCH] seccomp: introduce v2 policy (v2)

Stéphane Graber stgraber at ubuntu.com
Wed Feb 12 22:01:03 UTC 2014


On Wed, Feb 12, 2014 at 03:50:20PM -0600, Serge Hallyn wrote:
> 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>

Looks good to me. Can I just ask you to send another patch updating our
documentation? It currently only lists v1 and says that only whitelist
is allowed (lxc.container.conf.sgml). Adding seccomp-v1 and seccomp-v2
example files to doc/ may also be worthwhile.

Acked-by: Stéphane Graber <stgraber 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
> 
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel

-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20140212/930836fc/attachment.pgp>


More information about the lxc-devel mailing list