http://outflux.net/teach-seccomp/
Introduction
Example Program
/* * seccomp example with syscall reporting * * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org> * Authors: * Kees Cook <keescook@chromium.org> * Will Drewry <wad@chromium.org> * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #define _GNU_SOURCE 1 #include <stdio.h> #include <stddef.h> #include <stdlib.h> #include <unistd.h> #include "config.h" int main(int argc, char *argv[]) { char buf[1024]; printf("Type stuff here: "); fflush(NULL); buf[0] = ''; fgets(buf, sizeof(buf), stdin); printf("You typed: %s", buf); printf("And now we fork, which should do quite the opposite ...\n"); fflush(NULL); sleep(1); fork(); printf("You should not see this because I'm dead.\n"); return 0; }
$ autoconf $ ./configure checking for gcc... gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... no checking for suffix of object files... o checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ISO C89... none needed configure: creating ./config.status config.status: creating config.h $ make gcc -Wall -c -o example.o example.c gcc example.o -o example $ ./example Type stuff here: asdf You typed: asdf And now we fork, which should do quite the opposite ... You should not see this because I'm dead. You should not see this because I'm dead.
Everything is working, even the “fork” we want to eliminate.
Adding basic seccomp filtering
--- step-1/example.c 2012-03-22 21:43:10.845732543 -0700 +++ step-2/example.c 2012-03-22 21:50:56.373304922 -0700 @@ -16,11 +16,54 @@ #include <unistd.h> #include "config.h" +#include "seccomp-bpf.h" + +static int install_syscall_filter(void) +{ + struct sock_filter filter[] = { + /* Validate architecture. */ + VALIDATE_ARCHITECTURE, + /* Grab the system call number. */ + EXAMINE_SYSCALL, + /* List allowed syscalls. */ + ALLOW_SYSCALL(rt_sigreturn), +#ifdef __NR_sigreturn + ALLOW_SYSCALL(sigreturn), +#endif + ALLOW_SYSCALL(exit_group), + ALLOW_SYSCALL(exit), + ALLOW_SYSCALL(read), + ALLOW_SYSCALL(write), + KILL_PROCESS, + }; + struct sock_fprog prog = { + .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), + .filter = filter, + }; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + perror("prctl(NO_NEW_PRIVS)"); + goto failed; + } + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { + perror("prctl(SECCOMP)"); + goto failed; + } + return 0; + +failed: + if (errno == EINVAL) + fprintf(stderr, "SECCOMP_FILTER is not available. :(\n"); + return 1; +} int main(int argc, char *argv[]) { char buf[1024]; + if (install_syscall_filter()) + return 1; + printf("Type stuff here: "); fflush(NULL); buf[0] = ''; --- step-1/configure.ac 2012-03-22 21:40:51.651435417 -0700 +++ step-2/configure.ac 2012-03-22 21:44:19.438868163 -0700 @@ -2,4 +2,5 @@ AC_PREREQ([2.59]) AC_CONFIG_HEADERS([config.h]) AC_PROG_CC +AC_CHECK_HEADERS([linux/seccomp.h]) AC_OUTPUT
$ ./configure ... checking for linux/seccomp.h... yes configure: creating ./config.status config.status: creating config.h $ make gcc -Wall -c -o example.o example.c gcc example.o -o example $ ./example Bad system call $ echo $? 159
Adding syscall reporting
--- step-2/example.c 2012-03-22 21:50:56.373304922 -0700 +++ step-3/example.c 2012-03-22 21:51:04.377433872 -0700 @@ -17,6 +17,7 @@ #include "config.h" #include "seccomp-bpf.h" +#include "syscall-reporter.h" static int install_syscall_filter(void) { @@ -34,6 +35,7 @@ ALLOW_SYSCALL(exit), ALLOW_SYSCALL(read), ALLOW_SYSCALL(write), + /* Add more syscalls here. */ KILL_PROCESS, }; struct sock_fprog prog = { @@ -61,6 +63,8 @@ { char buf[1024]; + if (install_syscall_reporter()) + return 1; if (install_syscall_filter()) return 1; --- step-2/Makefile 2012-03-22 19:41:02.510347542 -0700 +++ step-3/Makefile 2012-03-22 19:41:33.706847395 -0700 @@ -3,7 +3,9 @@ all: example -example: example.o +include syscall-reporter.mk + +example: example.o syscall-reporter.o .PHONY: clean clean:
$ make gcc -Wall -c -o example.o example.c In file included from example.c:20:0: syscall-reporter.h:21:2: warning: #warning "You've included the syscall reporter. Do not use in production!" [-Wcpp] echo "static const char *syscall_names[] = {" > syscall-names.h ;\ echo "#include <syscall.h>" | cpp -dM | grep '^#define __NR_' | \ LC_ALL=C sed -r -n -e 's/^\#define[ \t]+__NR_([a-z0-9_]+)[ \t]+([0-9]+)(.*)/ [\2] = "\1",/p' >> syscall-names.h;\ echo "};" >> syscall-names.h gcc -Wall -c -o syscall-reporter.o syscall-reporter.c In file included from syscall-reporter.c:12:0: syscall-reporter.h:21:2: warning: #warning "You've included the syscall reporter. Do not use in production!" [-Wcpp] gcc example.o syscall-reporter.o -o example $ ./example Looks like you need syscall fstat(5) too! $ vi example.c ... $ make gcc -Wall -c -o example.o example.c gcc example.o syscall-reporter.o -o example $ ./example Looks like you need syscall mmap(9) too! $ vi example.c ... $ make gcc -Wall -c -o example.o example.c gcc example.o syscall-reporter.o -o example $ ./example Type stuff here: asdf You typed: asdf And now we fork, which should do quite the opposite ... Looks like you need syscall rt_sigprocmask(14) too! $ ...
Testing is done
--- step-3/example.c 2012-03-22 21:51:04.377433872 -0700 +++ step-4/example.c 2012-03-22 21:51:13.577583466 -0700 @@ -36,6 +36,11 @@ ALLOW_SYSCALL(read), ALLOW_SYSCALL(write), /* Add more syscalls here. */ + ALLOW_SYSCALL(fstat), + ALLOW_SYSCALL(mmap), + ALLOW_SYSCALL(rt_sigprocmask), + ALLOW_SYSCALL(rt_sigaction), + ALLOW_SYSCALL(nanosleep), KILL_PROCESS, }; struct sock_fprog prog = {
$ ./example Type stuff here: asdf You typed: asdf And now we fork, which should do quite the opposite ... Looks like you need syscall clone(56) too!
Ready for prime-time
--- step-4/example.c 2012-03-22 21:51:13.577583466 -0700 +++ step-5/example.c 2012-03-22 21:51:21.785717260 -0700 @@ -17,7 +17,6 @@ #include "config.h" #include "seccomp-bpf.h" -#include "syscall-reporter.h" static int install_syscall_filter(void) { @@ -35,7 +34,6 @@ ALLOW_SYSCALL(exit), ALLOW_SYSCALL(read), ALLOW_SYSCALL(write), - /* Add more syscalls here. */ ALLOW_SYSCALL(fstat), ALLOW_SYSCALL(mmap), ALLOW_SYSCALL(rt_sigprocmask), @@ -68,8 +66,6 @@ { char buf[1024]; - if (install_syscall_reporter()) - return 1; if (install_syscall_filter()) return 1; --- step-4/Makefile 2012-03-22 19:55:27.056164102 -0700 +++ step-5/Makefile 2012-03-22 19:55:33.680270186 -0700 @@ -3,9 +3,7 @@ all: example -include syscall-reporter.mk - -example: example.o syscall-reporter.o +example: example.o .PHONY: clean clean:
$ ./example Type stuff here: asdf You typed: asdf And now we fork, which should do quite the opposite ... Bad system call $ echo $? 159