diff options
| author | Will Drewry <wad@chromium.org> | 2012-04-12 17:48:04 -0400 |
|---|---|---|
| committer | James Morris <james.l.morris@oracle.com> | 2012-04-13 21:13:22 -0400 |
| commit | 8ac270d1e29f0428228ab2b9a8ae5e1ed4a5cd84 (patch) | |
| tree | 6deba4ed83da9ace758004b29d15aa0d2ec875a7 /samples/seccomp | |
| parent | c6cfbeb4029610c8c330c312dcf4d514cc067554 (diff) | |
Documentation: prctl/seccomp_filter
Documents how system call filtering using Berkeley Packet
Filter programs works and how it may be used.
Includes an example for x86 and a semi-generic
example using a macro-based code generator.
Acked-by: Eric Paris <eparis@redhat.com>
Signed-off-by: Will Drewry <wad@chromium.org>
Acked-by: Kees Cook <keescook@chromium.org>
v18: - added acked by
- update no new privs numbers
v17: - remove @compat note and add Pitfalls section for arch checking
(keescook@chromium.org)
v16: -
v15: -
v14: - rebase/nochanges
v13: - rebase on to 88ebdda6159ffc15699f204c33feb3e431bf9bdc
v12: - comment on the ptrace_event use
- update arch support comment
- note the behavior of SECCOMP_RET_DATA when there are multiple filters
(keescook@chromium.org)
- lots of samples/ clean up incl 64-bit bpf-direct support
(markus@chromium.org)
- rebase to linux-next
v11: - overhaul return value language, updates (keescook@chromium.org)
- comment on do_exit(SIGSYS)
v10: - update for SIGSYS
- update for new seccomp_data layout
- update for ptrace option use
v9: - updated bpf-direct.c for SIGILL
v8: - add PR_SET_NO_NEW_PRIVS to the samples.
v7: - updated for all the new stuff in v7: TRAP, TRACE
- only talk about PR_SET_SECCOMP now
- fixed bad JLE32 check (coreyb@linux.vnet.ibm.com)
- adds dropper.c: a simple system call disabler
v6: - tweak the language to note the requirement of
PR_SET_NO_NEW_PRIVS being called prior to use. (luto@mit.edu)
v5: - update sample to use system call arguments
- adds a "fancy" example using a macro-based generator
- cleaned up bpf in the sample
- update docs to mention arguments
- fix prctl value (eparis@redhat.com)
- language cleanup (rdunlap@xenotime.net)
v4: - update for no_new_privs use
- minor tweaks
v3: - call out BPF <-> Berkeley Packet Filter (rdunlap@xenotime.net)
- document use of tentative always-unprivileged
- guard sample compilation for i386 and x86_64
v2: - move code to samples (corbet@lwn.net)
Signed-off-by: James Morris <james.l.morris@oracle.com>
Diffstat (limited to 'samples/seccomp')
| -rw-r--r-- | samples/seccomp/Makefile | 38 | ||||
| -rw-r--r-- | samples/seccomp/bpf-direct.c | 176 | ||||
| -rw-r--r-- | samples/seccomp/bpf-fancy.c | 102 | ||||
| -rw-r--r-- | samples/seccomp/bpf-helper.c | 89 | ||||
| -rw-r--r-- | samples/seccomp/bpf-helper.h | 238 | ||||
| -rw-r--r-- | samples/seccomp/dropper.c | 68 |
6 files changed, 711 insertions, 0 deletions
diff --git a/samples/seccomp/Makefile b/samples/seccomp/Makefile new file mode 100644 index 000000000000..e8fe0f57b68f --- /dev/null +++ b/samples/seccomp/Makefile | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | # kbuild trick to avoid linker error. Can be omitted if a module is built. | ||
| 2 | obj- := dummy.o | ||
| 3 | |||
| 4 | hostprogs-$(CONFIG_SECCOMP) := bpf-fancy dropper | ||
| 5 | bpf-fancy-objs := bpf-fancy.o bpf-helper.o | ||
| 6 | |||
| 7 | HOSTCFLAGS_bpf-fancy.o += -I$(objtree)/usr/include | ||
| 8 | HOSTCFLAGS_bpf-fancy.o += -idirafter $(objtree)/include | ||
| 9 | HOSTCFLAGS_bpf-helper.o += -I$(objtree)/usr/include | ||
| 10 | HOSTCFLAGS_bpf-helper.o += -idirafter $(objtree)/include | ||
| 11 | |||
| 12 | HOSTCFLAGS_dropper.o += -I$(objtree)/usr/include | ||
| 13 | HOSTCFLAGS_dropper.o += -idirafter $(objtree)/include | ||
| 14 | dropper-objs := dropper.o | ||
| 15 | |||
| 16 | # bpf-direct.c is x86-only. | ||
| 17 | ifeq ($(SRCARCH),x86) | ||
| 18 | # List of programs to build | ||
| 19 | hostprogs-$(CONFIG_SECCOMP) += bpf-direct | ||
| 20 | bpf-direct-objs := bpf-direct.o | ||
| 21 | endif | ||
| 22 | |||
| 23 | HOSTCFLAGS_bpf-direct.o += -I$(objtree)/usr/include | ||
| 24 | HOSTCFLAGS_bpf-direct.o += -idirafter $(objtree)/include | ||
| 25 | |||
| 26 | # Try to match the kernel target. | ||
| 27 | ifeq ($(CONFIG_64BIT),) | ||
| 28 | HOSTCFLAGS_bpf-direct.o += -m32 | ||
| 29 | HOSTCFLAGS_dropper.o += -m32 | ||
| 30 | HOSTCFLAGS_bpf-helper.o += -m32 | ||
| 31 | HOSTCFLAGS_bpf-fancy.o += -m32 | ||
| 32 | HOSTLOADLIBES_bpf-direct += -m32 | ||
| 33 | HOSTLOADLIBES_bpf-fancy += -m32 | ||
| 34 | HOSTLOADLIBES_dropper += -m32 | ||
| 35 | endif | ||
| 36 | |||
| 37 | # Tell kbuild to always build the programs | ||
| 38 | always := $(hostprogs-y) | ||
diff --git a/samples/seccomp/bpf-direct.c b/samples/seccomp/bpf-direct.c new file mode 100644 index 000000000000..26f523e6ed74 --- /dev/null +++ b/samples/seccomp/bpf-direct.c | |||
| @@ -0,0 +1,176 @@ | |||
| 1 | /* | ||
| 2 | * Seccomp filter example for x86 (32-bit and 64-bit) with BPF macros | ||
| 3 | * | ||
| 4 | * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org> | ||
| 5 | * Author: Will Drewry <wad@chromium.org> | ||
| 6 | * | ||
| 7 | * The code may be used by anyone for any purpose, | ||
| 8 | * and can serve as a starting point for developing | ||
| 9 | * applications using prctl(PR_SET_SECCOMP, 2, ...). | ||
| 10 | */ | ||
| 11 | #define __USE_GNU 1 | ||
| 12 | #define _GNU_SOURCE 1 | ||
| 13 | |||
| 14 | #include <linux/types.h> | ||
| 15 | #include <linux/filter.h> | ||
| 16 | #include <linux/seccomp.h> | ||
| 17 | #include <linux/unistd.h> | ||
| 18 | #include <signal.h> | ||
| 19 | #include <stdio.h> | ||
| 20 | #include <stddef.h> | ||
| 21 | #include <string.h> | ||
| 22 | #include <sys/prctl.h> | ||
| 23 | #include <unistd.h> | ||
| 24 | |||
| 25 | #define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n])) | ||
| 26 | #define syscall_nr (offsetof(struct seccomp_data, nr)) | ||
| 27 | |||
| 28 | #if defined(__i386__) | ||
| 29 | #define REG_RESULT REG_EAX | ||
| 30 | #define REG_SYSCALL REG_EAX | ||
| 31 | #define REG_ARG0 REG_EBX | ||
| 32 | #define REG_ARG1 REG_ECX | ||
| 33 | #define REG_ARG2 REG_EDX | ||
| 34 | #define REG_ARG3 REG_ESI | ||
| 35 | #define REG_ARG4 REG_EDI | ||
| 36 | #define REG_ARG5 REG_EBP | ||
| 37 | #elif defined(__x86_64__) | ||
| 38 | #define REG_RESULT REG_RAX | ||
| 39 | #define REG_SYSCALL REG_RAX | ||
| 40 | #define REG_ARG0 REG_RDI | ||
| 41 | #define REG_ARG1 REG_RSI | ||
| 42 | #define REG_ARG2 REG_RDX | ||
| 43 | #define REG_ARG3 REG_R10 | ||
| 44 | #define REG_ARG4 REG_R8 | ||
| 45 | #define REG_ARG5 REG_R9 | ||
| 46 | #else | ||
| 47 | #error Unsupported platform | ||
| 48 | #endif | ||
| 49 | |||
| 50 | #ifndef PR_SET_NO_NEW_PRIVS | ||
| 51 | #define PR_SET_NO_NEW_PRIVS 38 | ||
| 52 | #endif | ||
| 53 | |||
| 54 | #ifndef SYS_SECCOMP | ||
| 55 | #define SYS_SECCOMP 1 | ||
| 56 | #endif | ||
| 57 | |||
| 58 | static void emulator(int nr, siginfo_t *info, void *void_context) | ||
| 59 | { | ||
| 60 | ucontext_t *ctx = (ucontext_t *)(void_context); | ||
| 61 | int syscall; | ||
| 62 | char *buf; | ||
| 63 | ssize_t bytes; | ||
| 64 | size_t len; | ||
| 65 | if (info->si_code != SYS_SECCOMP) | ||
| 66 | return; | ||
| 67 | if (!ctx) | ||
| 68 | return; | ||
| 69 | syscall = ctx->uc_mcontext.gregs[REG_SYSCALL]; | ||
| 70 | buf = (char *) ctx->uc_mcontext.gregs[REG_ARG1]; | ||
| 71 | len = (size_t) ctx->uc_mcontext.gregs[REG_ARG2]; | ||
| 72 | |||
| 73 | if (syscall != __NR_write) | ||
| 74 | return; | ||
| 75 | if (ctx->uc_mcontext.gregs[REG_ARG0] != STDERR_FILENO) | ||
| 76 | return; | ||
| 77 | /* Redirect stderr messages to stdout. Doesn't handle EINTR, etc */ | ||
| 78 | ctx->uc_mcontext.gregs[REG_RESULT] = -1; | ||
| 79 | if (write(STDOUT_FILENO, "[ERR] ", 6) > 0) { | ||
| 80 | bytes = write(STDOUT_FILENO, buf, len); | ||
| 81 | ctx->uc_mcontext.gregs[REG_RESULT] = bytes; | ||
| 82 | } | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | static int install_emulator(void) | ||
| 87 | { | ||
| 88 | struct sigaction act; | ||
| 89 | sigset_t mask; | ||
| 90 | memset(&act, 0, sizeof(act)); | ||
| 91 | sigemptyset(&mask); | ||
| 92 | sigaddset(&mask, SIGSYS); | ||
| 93 | |||
| 94 | act.sa_sigaction = &emulator; | ||
| 95 | act.sa_flags = SA_SIGINFO; | ||
| 96 | if (sigaction(SIGSYS, &act, NULL) < 0) { | ||
| 97 | perror("sigaction"); | ||
| 98 | return -1; | ||
| 99 | } | ||
| 100 | if (sigprocmask(SIG_UNBLOCK, &mask, NULL)) { | ||
| 101 | perror("sigprocmask"); | ||
| 102 | return -1; | ||
| 103 | } | ||
| 104 | return 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | static int install_filter(void) | ||
| 108 | { | ||
| 109 | struct sock_filter filter[] = { | ||
| 110 | /* Grab the system call number */ | ||
| 111 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr), | ||
| 112 | /* Jump table for the allowed syscalls */ | ||
| 113 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 0, 1), | ||
| 114 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), | ||
| 115 | #ifdef __NR_sigreturn | ||
| 116 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 0, 1), | ||
| 117 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), | ||
| 118 | #endif | ||
| 119 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit_group, 0, 1), | ||
| 120 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), | ||
| 121 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 0, 1), | ||
| 122 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), | ||
| 123 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_read, 1, 0), | ||
| 124 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 3, 2), | ||
| 125 | |||
| 126 | /* Check that read is only using stdin. */ | ||
| 127 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_arg(0)), | ||
| 128 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDIN_FILENO, 4, 0), | ||
| 129 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL), | ||
| 130 | |||
| 131 | /* Check that write is only using stdout */ | ||
| 132 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_arg(0)), | ||
| 133 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDOUT_FILENO, 1, 0), | ||
| 134 | /* Trap attempts to write to stderr */ | ||
| 135 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, STDERR_FILENO, 1, 2), | ||
| 136 | |||
| 137 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), | ||
| 138 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP), | ||
| 139 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL), | ||
| 140 | }; | ||
| 141 | struct sock_fprog prog = { | ||
| 142 | .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), | ||
| 143 | .filter = filter, | ||
| 144 | }; | ||
| 145 | |||
| 146 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
| 147 | perror("prctl(NO_NEW_PRIVS)"); | ||
| 148 | return 1; | ||
| 149 | } | ||
| 150 | |||
| 151 | |||
| 152 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | ||
| 153 | perror("prctl"); | ||
| 154 | return 1; | ||
| 155 | } | ||
| 156 | return 0; | ||
| 157 | } | ||
| 158 | |||
| 159 | #define payload(_c) (_c), sizeof((_c)) | ||
| 160 | int main(int argc, char **argv) | ||
| 161 | { | ||
| 162 | char buf[4096]; | ||
| 163 | ssize_t bytes = 0; | ||
| 164 | if (install_emulator()) | ||
| 165 | return 1; | ||
| 166 | if (install_filter()) | ||
| 167 | return 1; | ||
| 168 | syscall(__NR_write, STDOUT_FILENO, | ||
| 169 | payload("OHAI! WHAT IS YOUR NAME? ")); | ||
| 170 | bytes = syscall(__NR_read, STDIN_FILENO, buf, sizeof(buf)); | ||
| 171 | syscall(__NR_write, STDOUT_FILENO, payload("HELLO, ")); | ||
| 172 | syscall(__NR_write, STDOUT_FILENO, buf, bytes); | ||
| 173 | syscall(__NR_write, STDERR_FILENO, | ||
| 174 | payload("Error message going to STDERR\n")); | ||
| 175 | return 0; | ||
| 176 | } | ||
diff --git a/samples/seccomp/bpf-fancy.c b/samples/seccomp/bpf-fancy.c new file mode 100644 index 000000000000..8eb483aaec46 --- /dev/null +++ b/samples/seccomp/bpf-fancy.c | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | /* | ||
| 2 | * Seccomp BPF example using a macro-based generator. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org> | ||
| 5 | * Author: Will Drewry <wad@chromium.org> | ||
| 6 | * | ||
| 7 | * The code may be used by anyone for any purpose, | ||
| 8 | * and can serve as a starting point for developing | ||
| 9 | * applications using prctl(PR_ATTACH_SECCOMP_FILTER). | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/filter.h> | ||
| 13 | #include <linux/seccomp.h> | ||
| 14 | #include <linux/unistd.h> | ||
| 15 | #include <stdio.h> | ||
| 16 | #include <string.h> | ||
| 17 | #include <sys/prctl.h> | ||
| 18 | #include <unistd.h> | ||
| 19 | |||
| 20 | #include "bpf-helper.h" | ||
| 21 | |||
| 22 | #ifndef PR_SET_NO_NEW_PRIVS | ||
| 23 | #define PR_SET_NO_NEW_PRIVS 38 | ||
| 24 | #endif | ||
| 25 | |||
| 26 | int main(int argc, char **argv) | ||
| 27 | { | ||
| 28 | struct bpf_labels l; | ||
| 29 | static const char msg1[] = "Please type something: "; | ||
| 30 | static const char msg2[] = "You typed: "; | ||
| 31 | char buf[256]; | ||
| 32 | struct sock_filter filter[] = { | ||
| 33 | /* TODO: LOAD_SYSCALL_NR(arch) and enforce an arch */ | ||
| 34 | LOAD_SYSCALL_NR, | ||
| 35 | SYSCALL(__NR_exit, ALLOW), | ||
| 36 | SYSCALL(__NR_exit_group, ALLOW), | ||
| 37 | SYSCALL(__NR_write, JUMP(&l, write_fd)), | ||
| 38 | SYSCALL(__NR_read, JUMP(&l, read)), | ||
| 39 | DENY, /* Don't passthrough into a label */ | ||
| 40 | |||
| 41 | LABEL(&l, read), | ||
| 42 | ARG(0), | ||
| 43 | JNE(STDIN_FILENO, DENY), | ||
| 44 | ARG(1), | ||
| 45 | JNE((unsigned long)buf, DENY), | ||
| 46 | ARG(2), | ||
| 47 | JGE(sizeof(buf), DENY), | ||
| 48 | ALLOW, | ||
| 49 | |||
| 50 | LABEL(&l, write_fd), | ||
| 51 | ARG(0), | ||
| 52 | JEQ(STDOUT_FILENO, JUMP(&l, write_buf)), | ||
| 53 | JEQ(STDERR_FILENO, JUMP(&l, write_buf)), | ||
| 54 | DENY, | ||
| 55 | |||
| 56 | LABEL(&l, write_buf), | ||
| 57 | ARG(1), | ||
| 58 | JEQ((unsigned long)msg1, JUMP(&l, msg1_len)), | ||
| 59 | JEQ((unsigned long)msg2, JUMP(&l, msg2_len)), | ||
| 60 | JEQ((unsigned long)buf, JUMP(&l, buf_len)), | ||
| 61 | DENY, | ||
| 62 | |||
| 63 | LABEL(&l, msg1_len), | ||
| 64 | ARG(2), | ||
| 65 | JLT(sizeof(msg1), ALLOW), | ||
| 66 | DENY, | ||
| 67 | |||
| 68 | LABEL(&l, msg2_len), | ||
| 69 | ARG(2), | ||
| 70 | JLT(sizeof(msg2), ALLOW), | ||
| 71 | DENY, | ||
| 72 | |||
| 73 | LABEL(&l, buf_len), | ||
| 74 | ARG(2), | ||
| 75 | JLT(sizeof(buf), ALLOW), | ||
| 76 | DENY, | ||
| 77 | }; | ||
| 78 | struct sock_fprog prog = { | ||
| 79 | .filter = filter, | ||
| 80 | .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), | ||
| 81 | }; | ||
| 82 | ssize_t bytes; | ||
| 83 | bpf_resolve_jumps(&l, filter, sizeof(filter)/sizeof(*filter)); | ||
| 84 | |||
| 85 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
| 86 | perror("prctl(NO_NEW_PRIVS)"); | ||
| 87 | return 1; | ||
| 88 | } | ||
| 89 | |||
| 90 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | ||
| 91 | perror("prctl(SECCOMP)"); | ||
| 92 | return 1; | ||
| 93 | } | ||
| 94 | syscall(__NR_write, STDOUT_FILENO, msg1, strlen(msg1)); | ||
| 95 | bytes = syscall(__NR_read, STDIN_FILENO, buf, sizeof(buf)-1); | ||
| 96 | bytes = (bytes > 0 ? bytes : 0); | ||
| 97 | syscall(__NR_write, STDERR_FILENO, msg2, strlen(msg2)); | ||
| 98 | syscall(__NR_write, STDERR_FILENO, buf, bytes); | ||
| 99 | /* Now get killed */ | ||
| 100 | syscall(__NR_write, STDERR_FILENO, msg2, strlen(msg2)+2); | ||
| 101 | return 0; | ||
| 102 | } | ||
diff --git a/samples/seccomp/bpf-helper.c b/samples/seccomp/bpf-helper.c new file mode 100644 index 000000000000..579cfe331886 --- /dev/null +++ b/samples/seccomp/bpf-helper.c | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | /* | ||
| 2 | * Seccomp BPF helper functions | ||
| 3 | * | ||
| 4 | * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org> | ||
| 5 | * Author: Will Drewry <wad@chromium.org> | ||
| 6 | * | ||
| 7 | * The code may be used by anyone for any purpose, | ||
| 8 | * and can serve as a starting point for developing | ||
| 9 | * applications using prctl(PR_ATTACH_SECCOMP_FILTER). | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <stdio.h> | ||
| 13 | #include <string.h> | ||
| 14 | |||
| 15 | #include "bpf-helper.h" | ||
| 16 | |||
| 17 | int bpf_resolve_jumps(struct bpf_labels *labels, | ||
| 18 | struct sock_filter *filter, size_t count) | ||
| 19 | { | ||
| 20 | struct sock_filter *begin = filter; | ||
| 21 | __u8 insn = count - 1; | ||
| 22 | |||
| 23 | if (count < 1) | ||
| 24 | return -1; | ||
| 25 | /* | ||
| 26 | * Walk it once, backwards, to build the label table and do fixups. | ||
| 27 | * Since backward jumps are disallowed by BPF, this is easy. | ||
| 28 | */ | ||
| 29 | filter += insn; | ||
| 30 | for (; filter >= begin; --insn, --filter) { | ||
| 31 | if (filter->code != (BPF_JMP+BPF_JA)) | ||
| 32 | continue; | ||
| 33 | switch ((filter->jt<<8)|filter->jf) { | ||
| 34 | case (JUMP_JT<<8)|JUMP_JF: | ||
| 35 | if (labels->labels[filter->k].location == 0xffffffff) { | ||
| 36 | fprintf(stderr, "Unresolved label: '%s'\n", | ||
| 37 | labels->labels[filter->k].label); | ||
| 38 | return 1; | ||
| 39 | } | ||
| 40 | filter->k = labels->labels[filter->k].location - | ||
| 41 | (insn + 1); | ||
| 42 | filter->jt = 0; | ||
| 43 | filter->jf = 0; | ||
| 44 | continue; | ||
| 45 | case (LABEL_JT<<8)|LABEL_JF: | ||
| 46 | if (labels->labels[filter->k].location != 0xffffffff) { | ||
| 47 | fprintf(stderr, "Duplicate label use: '%s'\n", | ||
| 48 | labels->labels[filter->k].label); | ||
| 49 | return 1; | ||
| 50 | } | ||
| 51 | labels->labels[filter->k].location = insn; | ||
| 52 | filter->k = 0; /* fall through */ | ||
| 53 | filter->jt = 0; | ||
| 54 | filter->jf = 0; | ||
| 55 | continue; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | return 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | /* Simple lookup table for labels. */ | ||
| 62 | __u32 seccomp_bpf_label(struct bpf_labels *labels, const char *label) | ||
| 63 | { | ||
| 64 | struct __bpf_label *begin = labels->labels, *end; | ||
| 65 | int id; | ||
| 66 | if (labels->count == 0) { | ||
| 67 | begin->label = label; | ||
| 68 | begin->location = 0xffffffff; | ||
| 69 | labels->count++; | ||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | end = begin + labels->count; | ||
| 73 | for (id = 0; begin < end; ++begin, ++id) { | ||
| 74 | if (!strcmp(label, begin->label)) | ||
| 75 | return id; | ||
| 76 | } | ||
| 77 | begin->label = label; | ||
| 78 | begin->location = 0xffffffff; | ||
| 79 | labels->count++; | ||
| 80 | return id; | ||
| 81 | } | ||
| 82 | |||
| 83 | void seccomp_bpf_print(struct sock_filter *filter, size_t count) | ||
| 84 | { | ||
| 85 | struct sock_filter *end = filter + count; | ||
| 86 | for ( ; filter < end; ++filter) | ||
| 87 | printf("{ code=%u,jt=%u,jf=%u,k=%u },\n", | ||
| 88 | filter->code, filter->jt, filter->jf, filter->k); | ||
| 89 | } | ||
diff --git a/samples/seccomp/bpf-helper.h b/samples/seccomp/bpf-helper.h new file mode 100644 index 000000000000..643279dd30fb --- /dev/null +++ b/samples/seccomp/bpf-helper.h | |||
| @@ -0,0 +1,238 @@ | |||
| 1 | /* | ||
| 2 | * Example wrapper around BPF macros. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org> | ||
| 5 | * Author: Will Drewry <wad@chromium.org> | ||
| 6 | * | ||
| 7 | * The code may be used by anyone for any purpose, | ||
| 8 | * and can serve as a starting point for developing | ||
| 9 | * applications using prctl(PR_SET_SECCOMP, 2, ...). | ||
| 10 | * | ||
| 11 | * No guarantees are provided with respect to the correctness | ||
| 12 | * or functionality of this code. | ||
| 13 | */ | ||
| 14 | #ifndef __BPF_HELPER_H__ | ||
| 15 | #define __BPF_HELPER_H__ | ||
| 16 | |||
| 17 | #include <asm/bitsperlong.h> /* for __BITS_PER_LONG */ | ||
| 18 | #include <endian.h> | ||
| 19 | #include <linux/filter.h> | ||
| 20 | #include <linux/seccomp.h> /* for seccomp_data */ | ||
| 21 | #include <linux/types.h> | ||
| 22 | #include <linux/unistd.h> | ||
| 23 | #include <stddef.h> | ||
| 24 | |||
| 25 | #define BPF_LABELS_MAX 256 | ||
| 26 | struct bpf_labels { | ||
| 27 | int count; | ||
| 28 | struct __bpf_label { | ||
| 29 | const char *label; | ||
| 30 | __u32 location; | ||
| 31 | } labels[BPF_LABELS_MAX]; | ||
| 32 | }; | ||
| 33 | |||
| 34 | int bpf_resolve_jumps(struct bpf_labels *labels, | ||
| 35 | struct sock_filter *filter, size_t count); | ||
| 36 | __u32 seccomp_bpf_label(struct bpf_labels *labels, const char *label); | ||
| 37 | void seccomp_bpf_print(struct sock_filter *filter, size_t count); | ||
| 38 | |||
| 39 | #define JUMP_JT 0xff | ||
| 40 | #define JUMP_JF 0xff | ||
| 41 | #define LABEL_JT 0xfe | ||
| 42 | #define LABEL_JF 0xfe | ||
| 43 | |||
| 44 | #define ALLOW \ | ||
| 45 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) | ||
| 46 | #define DENY \ | ||
| 47 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL) | ||
| 48 | #define JUMP(labels, label) \ | ||
| 49 | BPF_JUMP(BPF_JMP+BPF_JA, FIND_LABEL((labels), (label)), \ | ||
| 50 | JUMP_JT, JUMP_JF) | ||
| 51 | #define LABEL(labels, label) \ | ||
| 52 | BPF_JUMP(BPF_JMP+BPF_JA, FIND_LABEL((labels), (label)), \ | ||
| 53 | LABEL_JT, LABEL_JF) | ||
| 54 | #define SYSCALL(nr, jt) \ | ||
| 55 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (nr), 0, 1), \ | ||
| 56 | jt | ||
| 57 | |||
| 58 | /* Lame, but just an example */ | ||
| 59 | #define FIND_LABEL(labels, label) seccomp_bpf_label((labels), #label) | ||
| 60 | |||
| 61 | #define EXPAND(...) __VA_ARGS__ | ||
| 62 | /* Map all width-sensitive operations */ | ||
| 63 | #if __BITS_PER_LONG == 32 | ||
| 64 | |||
| 65 | #define JEQ(x, jt) JEQ32(x, EXPAND(jt)) | ||
| 66 | #define JNE(x, jt) JNE32(x, EXPAND(jt)) | ||
| 67 | #define JGT(x, jt) JGT32(x, EXPAND(jt)) | ||
| 68 | #define JLT(x, jt) JLT32(x, EXPAND(jt)) | ||
| 69 | #define JGE(x, jt) JGE32(x, EXPAND(jt)) | ||
| 70 | #define JLE(x, jt) JLE32(x, EXPAND(jt)) | ||
| 71 | #define JA(x, jt) JA32(x, EXPAND(jt)) | ||
| 72 | #define ARG(i) ARG_32(i) | ||
| 73 | #define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) | ||
| 74 | |||
| 75 | #elif __BITS_PER_LONG == 64 | ||
| 76 | |||
| 77 | /* Ensure that we load the logically correct offset. */ | ||
| 78 | #if __BYTE_ORDER == __LITTLE_ENDIAN | ||
| 79 | #define ENDIAN(_lo, _hi) _lo, _hi | ||
| 80 | #define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) | ||
| 81 | #define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32) | ||
| 82 | #elif __BYTE_ORDER == __BIG_ENDIAN | ||
| 83 | #define ENDIAN(_lo, _hi) _hi, _lo | ||
| 84 | #define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32) | ||
| 85 | #define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) | ||
| 86 | #else | ||
| 87 | #error "Unknown endianness" | ||
| 88 | #endif | ||
| 89 | |||
| 90 | union arg64 { | ||
| 91 | struct { | ||
| 92 | __u32 ENDIAN(lo32, hi32); | ||
| 93 | }; | ||
| 94 | __u64 u64; | ||
| 95 | }; | ||
| 96 | |||
| 97 | #define JEQ(x, jt) \ | ||
| 98 | JEQ64(((union arg64){.u64 = (x)}).lo32, \ | ||
| 99 | ((union arg64){.u64 = (x)}).hi32, \ | ||
| 100 | EXPAND(jt)) | ||
| 101 | #define JGT(x, jt) \ | ||
| 102 | JGT64(((union arg64){.u64 = (x)}).lo32, \ | ||
| 103 | ((union arg64){.u64 = (x)}).hi32, \ | ||
| 104 | EXPAND(jt)) | ||
| 105 | #define JGE(x, jt) \ | ||
| 106 | JGE64(((union arg64){.u64 = (x)}).lo32, \ | ||
| 107 | ((union arg64){.u64 = (x)}).hi32, \ | ||
| 108 | EXPAND(jt)) | ||
| 109 | #define JNE(x, jt) \ | ||
| 110 | JNE64(((union arg64){.u64 = (x)}).lo32, \ | ||
| 111 | ((union arg64){.u64 = (x)}).hi32, \ | ||
| 112 | EXPAND(jt)) | ||
| 113 | #define JLT(x, jt) \ | ||
| 114 | JLT64(((union arg64){.u64 = (x)}).lo32, \ | ||
| 115 | ((union arg64){.u64 = (x)}).hi32, \ | ||
| 116 | EXPAND(jt)) | ||
| 117 | #define JLE(x, jt) \ | ||
| 118 | JLE64(((union arg64){.u64 = (x)}).lo32, \ | ||
| 119 | ((union arg64){.u64 = (x)}).hi32, \ | ||
| 120 | EXPAND(jt)) | ||
| 121 | |||
| 122 | #define JA(x, jt) \ | ||
| 123 | JA64(((union arg64){.u64 = (x)}).lo32, \ | ||
| 124 | ((union arg64){.u64 = (x)}).hi32, \ | ||
| 125 | EXPAND(jt)) | ||
| 126 | #define ARG(i) ARG_64(i) | ||
| 127 | |||
| 128 | #else | ||
| 129 | #error __BITS_PER_LONG value unusable. | ||
| 130 | #endif | ||
| 131 | |||
| 132 | /* Loads the arg into A */ | ||
| 133 | #define ARG_32(idx) \ | ||
| 134 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)) | ||
| 135 | |||
| 136 | /* Loads hi into A and lo in X */ | ||
| 137 | #define ARG_64(idx) \ | ||
| 138 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, LO_ARG(idx)), \ | ||
| 139 | BPF_STMT(BPF_ST, 0), /* lo -> M[0] */ \ | ||
| 140 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, HI_ARG(idx)), \ | ||
| 141 | BPF_STMT(BPF_ST, 1) /* hi -> M[1] */ | ||
| 142 | |||
| 143 | #define JEQ32(value, jt) \ | ||
| 144 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 0, 1), \ | ||
| 145 | jt | ||
| 146 | |||
| 147 | #define JNE32(value, jt) \ | ||
| 148 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (value), 1, 0), \ | ||
| 149 | jt | ||
| 150 | |||
| 151 | /* Checks the lo, then swaps to check the hi. A=lo,X=hi */ | ||
| 152 | #define JEQ64(lo, hi, jt) \ | ||
| 153 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ | ||
| 154 | BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ | ||
| 155 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 0, 2), \ | ||
| 156 | BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ | ||
| 157 | jt, \ | ||
| 158 | BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ | ||
| 159 | |||
| 160 | #define JNE64(lo, hi, jt) \ | ||
| 161 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 5, 0), \ | ||
| 162 | BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ | ||
| 163 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (lo), 2, 0), \ | ||
| 164 | BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ | ||
| 165 | jt, \ | ||
| 166 | BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ | ||
| 167 | |||
| 168 | #define JA32(value, jt) \ | ||
| 169 | BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (value), 0, 1), \ | ||
| 170 | jt | ||
| 171 | |||
| 172 | #define JA64(lo, hi, jt) \ | ||
| 173 | BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (hi), 3, 0), \ | ||
| 174 | BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ | ||
| 175 | BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, (lo), 0, 2), \ | ||
| 176 | BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ | ||
| 177 | jt, \ | ||
| 178 | BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ | ||
| 179 | |||
| 180 | #define JGE32(value, jt) \ | ||
| 181 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 0, 1), \ | ||
| 182 | jt | ||
| 183 | |||
| 184 | #define JLT32(value, jt) \ | ||
| 185 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (value), 1, 0), \ | ||
| 186 | jt | ||
| 187 | |||
| 188 | /* Shortcut checking if hi > arg.hi. */ | ||
| 189 | #define JGE64(lo, hi, jt) \ | ||
| 190 | BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \ | ||
| 191 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ | ||
| 192 | BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ | ||
| 193 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (lo), 0, 2), \ | ||
| 194 | BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ | ||
| 195 | jt, \ | ||
| 196 | BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ | ||
| 197 | |||
| 198 | #define JLT64(lo, hi, jt) \ | ||
| 199 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, (hi), 0, 4), \ | ||
| 200 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ | ||
| 201 | BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ | ||
| 202 | BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \ | ||
| 203 | BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ | ||
| 204 | jt, \ | ||
| 205 | BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ | ||
| 206 | |||
| 207 | #define JGT32(value, jt) \ | ||
| 208 | BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 0, 1), \ | ||
| 209 | jt | ||
| 210 | |||
| 211 | #define JLE32(value, jt) \ | ||
| 212 | BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (value), 1, 0), \ | ||
| 213 | jt | ||
| 214 | |||
| 215 | /* Check hi > args.hi first, then do the GE checking */ | ||
| 216 | #define JGT64(lo, hi, jt) \ | ||
| 217 | BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 4, 0), \ | ||
| 218 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 5), \ | ||
| 219 | BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ | ||
| 220 | BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 0, 2), \ | ||
| 221 | BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ | ||
| 222 | jt, \ | ||
| 223 | BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ | ||
| 224 | |||
| 225 | #define JLE64(lo, hi, jt) \ | ||
| 226 | BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (hi), 6, 0), \ | ||
| 227 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (hi), 0, 3), \ | ||
| 228 | BPF_STMT(BPF_LD+BPF_MEM, 0), /* swap in lo */ \ | ||
| 229 | BPF_JUMP(BPF_JMP+BPF_JGT+BPF_K, (lo), 2, 0), \ | ||
| 230 | BPF_STMT(BPF_LD+BPF_MEM, 1), /* passed: swap hi back in */ \ | ||
| 231 | jt, \ | ||
| 232 | BPF_STMT(BPF_LD+BPF_MEM, 1) /* failed: swap hi back in */ | ||
| 233 | |||
| 234 | #define LOAD_SYSCALL_NR \ | ||
| 235 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ | ||
| 236 | offsetof(struct seccomp_data, nr)) | ||
| 237 | |||
| 238 | #endif /* __BPF_HELPER_H__ */ | ||
diff --git a/samples/seccomp/dropper.c b/samples/seccomp/dropper.c new file mode 100644 index 000000000000..c69c347c7011 --- /dev/null +++ b/samples/seccomp/dropper.c | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | /* | ||
| 2 | * Naive system call dropper built on seccomp_filter. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2012 The Chromium OS Authors <chromium-os-dev@chromium.org> | ||
| 5 | * Author: Will Drewry <wad@chromium.org> | ||
| 6 | * | ||
| 7 | * The code may be used by anyone for any purpose, | ||
| 8 | * and can serve as a starting point for developing | ||
| 9 | * applications using prctl(PR_SET_SECCOMP, 2, ...). | ||
| 10 | * | ||
| 11 | * When run, returns the specified errno for the specified | ||
| 12 | * system call number against the given architecture. | ||
| 13 | * | ||
| 14 | * Run this one as root as PR_SET_NO_NEW_PRIVS is not called. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <errno.h> | ||
| 18 | #include <linux/audit.h> | ||
| 19 | #include <linux/filter.h> | ||
| 20 | #include <linux/seccomp.h> | ||
| 21 | #include <linux/unistd.h> | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <stddef.h> | ||
| 24 | #include <stdlib.h> | ||
| 25 | #include <sys/prctl.h> | ||
| 26 | #include <unistd.h> | ||
| 27 | |||
| 28 | static int install_filter(int nr, int arch, int error) | ||
| 29 | { | ||
| 30 | struct sock_filter filter[] = { | ||
| 31 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, | ||
| 32 | (offsetof(struct seccomp_data, arch))), | ||
| 33 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, arch, 0, 3), | ||
| 34 | BPF_STMT(BPF_LD+BPF_W+BPF_ABS, | ||
| 35 | (offsetof(struct seccomp_data, nr))), | ||
| 36 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, nr, 0, 1), | ||
| 37 | BPF_STMT(BPF_RET+BPF_K, | ||
| 38 | SECCOMP_RET_ERRNO|(error & SECCOMP_RET_DATA)), | ||
| 39 | BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), | ||
| 40 | }; | ||
| 41 | struct sock_fprog prog = { | ||
| 42 | .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), | ||
| 43 | .filter = filter, | ||
| 44 | }; | ||
| 45 | if (prctl(PR_SET_SECCOMP, 2, &prog)) { | ||
| 46 | perror("prctl"); | ||
| 47 | return 1; | ||
| 48 | } | ||
| 49 | return 0; | ||
| 50 | } | ||
| 51 | |||
| 52 | int main(int argc, char **argv) | ||
| 53 | { | ||
| 54 | if (argc < 5) { | ||
| 55 | fprintf(stderr, "Usage:\n" | ||
| 56 | "dropper <syscall_nr> <arch> <errno> <prog> [<args>]\n" | ||
| 57 | "Hint: AUDIT_ARCH_I386: 0x%X\n" | ||
| 58 | " AUDIT_ARCH_X86_64: 0x%X\n" | ||
| 59 | "\n", AUDIT_ARCH_I386, AUDIT_ARCH_X86_64); | ||
| 60 | return 1; | ||
| 61 | } | ||
| 62 | if (install_filter(strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0), | ||
| 63 | strtol(argv[3], NULL, 0))) | ||
| 64 | return 1; | ||
| 65 | execv(argv[4], &argv[4]); | ||
| 66 | printf("Failed to execv\n"); | ||
| 67 | return 255; | ||
| 68 | } | ||
