Playing with seccomp

Seccomp is a linux kernel feature by Andrea Arcangeli which limits the system calls which a task can use, by allowing a task to say “from now on, msyelf and my new children should not be able to do anything but compute, and read and write to already-open files (and return and exit).” The intent was to allow untrusted guests to use your cpu resources without them being able to abuse any other resources.

Years later, Will Drewry extended the seccomp idea by adding a new mode, typically called seccomp2. He had the brilliant idea to use a BPF (berkeley packet filter) compiler to allow clients to more flexibly define the constraints to be applied. He also managed to support many use cases by providing options for what to do on a denied action. The offending task can be killed; or the system call can be made to return a specified error code (i.e. ENOSYS). Or it can be traced.

In order to make seccomp2 easier to use, libseccomp was introduced. Over the next few weeks I want to add seccomp2 support to lxc containers. So I thought I’d first try a simple test program using libseccomp. Here is the program I used:

#include <stdio.h>
#include <stdlib.h>
#include <seccomp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main()
{
	FILE *f1;
	int fd;
	int ret;

	f1 = fopen("/tmp/test1", "w");

	ret = seccomp_init(SCMP_ACT_ERRNO(5));
	if (ret < 0)
		printf("Error from seccomp_init\n");
	ret = seccomp_rule_add(SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
	if (!ret)
		ret = seccomp_rule_add(SCMP_ACT_ALLOW, SCMP_SYS(dup), 0);
	if (!ret)
		ret = seccomp_rule_add(SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
	if (!ret)
		ret = seccomp_rule_add(SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
	if (!ret)
		ret = seccomp_load();
	if (ret)
		printf("error setting seccomp\n");

	fprintf(f1, "hi there\n");
	fd = open("/tmp/test2", O_RDWR);
	if (fd >= 0)
		printf("error, was able to open f2\n");
	else
		fprintf(f1, "open returned %d errno %d\n", fd, errno);
	fclose(f1);
	exit(0);
}

I installed libseccomp-dev, compiled the program, and executed it using

sudo apt-get -y install libseccomp-dev
gcc -o seccomp1 seccomp1.c -lseccomp
./seccomp1

The program opens /tmp/test1 for writing. Then it loads a seccomp policy to allow it to only write to files, close files, and exit. It then writes to the open file (allowed), opens a new file (not allowed), and writes the errno it received to the already open file. I told seccomp_init() to give us errno 5, so if you run the program you can check the output in /tmp/test1 to verify that -5 is what you got. If I had called seccomp_init() with the SCMP_KILL argument, then the program would have exited at the open() call, and the last line in the output file would not have been written. You can get much fancier by having the kernel the offending program a SIGSYS and rewinding its execution, or notifying a tracer.

Neat!

This entry was posted in Uncategorized. Bookmark the permalink.

7 Responses to Playing with seccomp

  1. nshrine says:

    Have you tried the sourcecode language=”C” tag for posting source code?

  2. fser says:

    For your information, this code does not work any longer. Here is a patch to apply to make it work again. It mainly consists in adding a sscmp_filter context.

    — orig.c 2014-06-27 18:03:52.389690277 +0200
    +++ sscomp.c 2014-06-27 18:00:30.086678002 +0200
    @@ -11,21 +11,23 @@
    FILE *f1;
    int fd;
    int ret;
    + scmp_filter_ctx ctx;

    f1 = fopen(“/tmp/test1”, “w”);

    – ret = seccomp_init(SCMP_ACT_ERRNO(5));
    – if (ret < 0)
    – printf("Error from seccomp_init\n");
    – ret = seccomp_rule_add(SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
    + ctx = seccomp_init(SCMP_ACT_KILL); /* SCMP_ACT_ERRNO(5)); */
    + if (ctx == NULL)
    + printf("Error from seccomp_init\n"), exit(1);
    +
    + ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
    if (!ret)
    – ret = seccomp_rule_add(SCMP_ACT_ALLOW, SCMP_SYS(dup), 0);
    + ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(dup), 0);
    if (!ret)
    – ret = seccomp_rule_add(SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    + ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    if (!ret)
    – ret = seccomp_rule_add(SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    + ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    if (!ret)
    – ret = seccomp_load();
    + ret = seccomp_load(ctx);
    if (ret)
    printf("error setting seccomp\n");

    • s3hh says:

      Thanks – indeed there have been some changes. You can look at the lxc code (src/lxc/seccomp.c) for how we make it compile with both old and newer libraries (#ifdef hell 🙂

  3. Pingback: The History of Containers | Red Hat Enterprise Linux Blog

  4. Ranran says:

    Hi S3hh,

    Thanks for the article.
    But it is that seccomp protects only the process which we declraed the filtering ?
    I need to use seccomp to make sure that ANY process in system obey the same filtering.
    Is there a way to make sure ? Do I need to make the filtering as part of init (0) ? How can I do it ?
    Thanks

    • s3hh says:

      Indeed – that’s simply how seccomp is designed to work. If you want the whole system to be subject to a seccomp filter, then you want to have init confine itself.

Leave a comment