commit c3a280dc1210c679cf7324f76d8f023a3feb4750 Author: Yawning Angel yawning@schwanenlied.me Date: Thu Dec 29 02:18:02 2016 +0000
Bug 21093: Go back to using gosecco for seccomp rule compilation.
This means that there is no longer a dependency on libseccomp at all, yay. --- .gitignore | 1 - ChangeLog | 1 + Makefile | 6 +- README.md | 1 - data/tor-amd64.seccomp | 38 + data/tor-common-amd64.seccomp | 125 +++ data/tor-obfs4-amd64.seccomp | 58 ++ data/torbrowser-amd64.seccomp | 210 +++++ src/cmd/gen-seccomp/main.go | 61 -- src/cmd/gen-seccomp/seccomp.go | 120 --- src/cmd/gen-seccomp/seccomp_firefox.go | 215 ------ src/cmd/gen-seccomp/seccomp_tor.go | 595 -------------- .../internal/sandbox/seccomp.go | 71 +- vendor/manifest | 6 - .../github.com/seccomp/libseccomp-golang/LICENSE | 22 - .../github.com/seccomp/libseccomp-golang/README | 26 - .../seccomp/libseccomp-golang/seccomp.go | 857 --------------------- .../seccomp/libseccomp-golang/seccomp_internal.go | 506 ------------ .../seccomp/libseccomp-golang/seccomp_test.go | 457 ----------- 19 files changed, 492 insertions(+), 2884 deletions(-)
diff --git a/.gitignore b/.gitignore index 9334d77..659d2b0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ bin/ pkg/ data/revision data/tbb_stub.so -data/*.bpf src/cmd/sandboxed-tor-browser/internal/data/bindata.go *.swp *~ diff --git a/ChangeLog b/ChangeLog index 8bcc45b..4f3a908 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,5 @@ Changes in version 0.0.3 - UNRELEASED: + * Bug 21903: Go back to using gosecco for seccomp rule compilation. * Bug 20940: Deprecate x86 support. * Bug 20778: Check for updates in the background. * Bug 20851: If the incremental update fails, fall back to the complete diff --git a/Makefile b/Makefile index 67f6723..78c9a4c 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,8 @@ sandboxed-tor-browser: static-assets gb build -tags $(GTK3TAG) cmd/sandboxed-tor-browser mv ./bin/sandboxed-tor-browser-$(GTK3TAG) ./bin/sandboxed-tor-browser
-static-assets: go-bindata gen-seccomp tbb_stub +static-assets: go-bindata tbb_stub git rev-parse --short HEAD > data/revision - ./bin/gen-seccomp -o ./data ./bin/go-bindata -nometadata -pkg data -prefix data -o ./src/cmd/sandboxed-tor-browser/internal/data/bindata.go data/...
tbb_stub: go-bindata @@ -20,9 +19,6 @@ tbb_stub: go-bindata go-bindata: gb build github.com/jteeuwen/go-bindata/go-bindata
-gen-seccomp: - gb build cmd/gen-seccomp - clean: rm -f ./src/cmd/sandboxed-tor-browser/internal/data/bindata.go rm -f ./data/revision diff --git a/README.md b/README.md index badb125..675ee2f 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ Build time dependencies: * A C compiler * gb (https://getgb.io/ Yes I know it's behind fucking cloudflare) * Go (Tested with 1.7.x) - * libseccomp2 >= 2.2.1 * libnotify
Things that the sandbox breaks: diff --git a/data/tor-amd64.seccomp b/data/tor-amd64.seccomp new file mode 100644 index 0000000..097052a --- /dev/null +++ b/data/tor-amd64.seccomp @@ -0,0 +1,38 @@ +# tor binary (x86_64) specific seccomp whitelist. +# +# This is based off of tor's src/common/sandbox.c + +# +# Extra constant definitions needed for filtering. +# + +FUTEX_WAIT=0 +FUTEX_WAKE=1 +FUTEX_FD=2 +FUTEX_REQUEUE=3 +FUTEX_CMP_REQUEUE=4 +FUTEX_WAKE_OP=5 +#FUTEX_LOCK_PI=6 +#FUTEX_UNLOCK_PI=7 +FUTEX_WAIT_BITSET=9 +FUTEX_PRIVATE_FLAG=128 +FUTEX_CLOCK_REALTIME=256 + +FUTEX_WAIT_PRIVATE=FUTEX_WAIT | FUTEX_PRIVATE_FLAG +FUTEX_WAKE_PRIVATE=FUTEX_WAKE | FUTEX_PRIVATE_FLAG +FUTEX_CMP_REQUEUE_PRIVATE=FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG +FUTEX_WAKE_OP_PRIVATE=FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG +#FUTEX_LOCK_PI_PRIVATE=FUTEX_LOCK_PI | FUTEX_PRIVATE_FLAG +#FUTEX_UNLOCK_PI_PRIVATE=FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG +FUTEX_WAIT_BITSET_PRIVATE=FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG + +# +# System calls allowed with filtering. +# * mmap: Asan (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE) +# (arg2 == PROT_NONE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE) +# + +futex: arg1 == FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME || arg1 == FUTEX_WAKE_PRIVATE || arg1 == FUTEX_WAIT_PRIVATE +mprotect: arg2 == PROT_READ || arg2 == PROT_NONE +mmap: (arg2 == PROT_READ && arg3 == MAP_PRIVATE) || (arg2 == PROT_NONE && arg3 == MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE) || (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_ANONYMOUS) || (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK) || (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE) || (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS) || (arg2 == PROT_READ|PROT_EXEC && arg3 == MAP_PRIVATE|MAP_DENYWRITE) || (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE) || (arg2 == PROT_NONE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE) +setsockopt: arg1 == SOL_SOCKET && (arg2 == SO_REUSEADDR || arg2 == SO_SNDBUF || arg2 == SO_RCVBUF) diff --git a/data/tor-common-amd64.seccomp b/data/tor-common-amd64.seccomp new file mode 100644 index 0000000..59ce3e1 --- /dev/null +++ b/data/tor-common-amd64.seccomp @@ -0,0 +1,125 @@ +# tor binary (x86_64) common seccomp whitelist. +# +# This is based off of tor's src/common/sandbox.c and is the whitelist for +# calls that aren't affected by the presence of obfs4proxy. gosecco's compiler +# doesn't allow multiple rules for the same system call that aren't identical. + +# +# Extra constant definitions needed for filtering. +# + +MADV_FREE=8 +MREMAP_MAYMOVE=1 + +SIG_BLOCK=1 +SIG_SETMASK=2 + +PF_INET=AF_INET +PF_INET6=AF_INET6 +PF_LOCAL=AF_LOCAL +PF_UNIX=AF_UNIX +POLLIN=1 + +MASKED_CLOEXEC_NONBLOCK = 0xFFF7F7FF + +# +# System calls allowed unconditionally without argument filtering. +# + +access: 1 +brk: 1 +clock_gettime: 1 +close: 1 +clone: 1 +epoll_create: 1 +epoll_wait: 1 +eventfd2: 1 +pipe2: 1 +pipe: 1 +fstat: 1 +getdents: 1 +getdents64: 1 +getegid: 1 +geteuid: 1 +getgid: 1 +getrlimit: 1 +gettimeofday: 1 +gettid: 1 +getuid: 1 +lseek: 1 +mkdir: 1 +munmap: 1 +prlimit64: 1 +read: 1 +rt_sigreturn: 1 +sched_getaffinity: 1 +sched_yield: 1 +sendmsg: 1 +set_robust_list: 1 +setrlimit: 1 +sigaltstack: 1 +stat: 1 +uname: 1 +wait4: 1 +write: 1 +writev: 1 +exit_group: 1 +exit: 1 +getrandom: 1 +sysinfo: 1 +bind: 1 +listen: 1 +connect: 1 +getsockname: 1 +recvmsg: 1 +recvfrom: 1 +sendto: 1 +unlink: 1 + +# tor's sandbox filters these, but we can't because we are not in the tor +# daemon's process space. +chown: 1 +chmod: 1 +open: 1 +openat: 1 +rename: 1 + +# Calls made prior to tor's UseSeccomp being enabled. +arch_prctl: 1 +chdir: 1 +execve: 1 +getpid: 1 +kill: 1 +restart_syscall: 1 +set_tid_address: 1 +unshare: 1 +rt_sigaction: 1 + +# XXX: This is only required for ASAN builds, so this should be included at +# runtime. +readlink: 1 + +# +# System calls allowed with filtering. +# +# Note: +# * socket: +# * tor explicitly allows PF_FILE separately from PF_UNIX which is +# pointless/nonsensical under Linux. +# * Tor allows socket(PF_NETLINK, SOCK_RAW, 0) but will accept no. +# + +time: arg0 == 0 +madvise: arg2 == MADV_FREE +umask: arg0 == 022 +rt_sigprocmask: arg0 == SIG_BLOCK || arg0 == SIG_SETMASK +epoll_ctl: arg1 == EPOLL_CTL_ADD || arg1 == EPOLL_CTL_MOD || arg1 == EPOLL_CTL_DEL +prctl: (arg0 == PR_SET_DUMPABLE && arg1 == 0) || arg0 == PR_SET_PDEATHSIG +flock: arg1 == (LOCK_EX | LOCK_NB) || arg1 == LOCK_UN +mremap: arg3 == MREMAP_MAYMOVE +accept4: argL3 & MASKED_CLOEXEC_NONBLOCK == 0 && argH3 == 0 +poll: arg1 == POLLIN && arg2 == 10 +socket: argH1 == 0 && (arg0 == PF_INET && argL1 & MASKED_CLOEXEC_NONBLOCK == SOCK_STREAM && arg2 == IPPROTO_TCP) || (arg0 == PF_INET && argL1 & MASKED_CLOEXEC_NONBLOCK == SOCK_STREAM && arg2 == IPPROTO_IP) || (arg0 == PF_INET && argL1 & MASKED_CLOEXEC_NONBLOCK == SOCK_DGRAM && arg2 == IPPROTO_IP) || (arg0 == PF_INET && argL1 & MASKED_CLOEXEC_NONBLOCK == SOCK_DGRAM && arg2 == IPPROTO_UDP) || (arg0 == PF_INET6 && argL1 & MASKED_CLOEXEC_NONBLOCK == SOCK_STREAM && arg2 == IPPROTO_TCP) || (arg0 == PF_INET6 && argL1 & MASKED_CLOEXEC_NONBLOCK == SOCK_STREAM && arg2 == IPPROTO_IP) || (arg0 == PF_INET6 && argL1 & MASKED_CLOEXEC_NONBLOCK == SOCK_DGRAM && arg2 == IPPROTO_IP) || (arg0 == PF_INET6 && argL1 & MASKED_CLOEXEC_NONBLOCK == SOCK_DGRAM && arg2 == IPPROTO_UDP) || (arg0 == PF_UNIX && argL1 & MASKED_CLOEXEC_NONBLOCK == SOCK_STREAM && arg2 == 0) || (arg0 == PF_UNIX && argL1 & MASKED_CLOEXEC_NONBLOCK == SOCK_DGRAM && arg2 == 0) +getsockopt: arg1 == SOL_SOCKET && arg2 == SO_ERROR +socketpair: arg0 == PF_LOCAL && (arg1 == SOCK_STREAM || arg1 == SOCK_STREAM | SOCK_CLOEXEC) +fcntl: arg1 == F_GETFL || (arg1 == F_SETFL && (arg2 == O_RDWR|O_NONBLOCK || arg2 == O_RDONLY |O_NONBLOCK)) || arg1 == F_GETFD || (arg1 == F_SETFD && arg2 == FD_CLOEXEC) diff --git a/data/tor-obfs4-amd64.seccomp b/data/tor-obfs4-amd64.seccomp new file mode 100644 index 0000000..b7ac52d --- /dev/null +++ b/data/tor-obfs4-amd64.seccomp @@ -0,0 +1,58 @@ +# tor binary (x86_64) obfs4proxy seccomp whitelist. +# +# These are the rules that should apply to tor and obfs4proxy. Eventually, +# obfs4proxy should live in it's own container so this file should go away. + +# +# Extra constant definitions needed for filtering. +# + +FUTEX_WAIT=0 +FUTEX_WAKE=1 +FUTEX_FD=2 +FUTEX_REQUEUE=3 +FUTEX_CMP_REQUEUE=4 +FUTEX_WAKE_OP=5 +#FUTEX_LOCK_PI=6 +#FUTEX_UNLOCK_PI=7 +FUTEX_WAIT_BITSET=9 +FUTEX_PRIVATE_FLAG=128 +FUTEX_CLOCK_REALTIME=256 + +FUTEX_WAIT_PRIVATE=FUTEX_WAIT | FUTEX_PRIVATE_FLAG +FUTEX_WAKE_PRIVATE=FUTEX_WAKE | FUTEX_PRIVATE_FLAG +FUTEX_CMP_REQUEUE_PRIVATE=FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG +FUTEX_WAKE_OP_PRIVATE=FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG +#FUTEX_LOCK_PI_PRIVATE=FUTEX_LOCK_PI | FUTEX_PRIVATE_FLAG +#FUTEX_UNLOCK_PI_PRIVATE=FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG +FUTEX_WAIT_BITSET_PRIVATE=FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG + + +# +# obfs4proxy specific system calls allowed unconditionally without argument +# filtering. +# + +mincore: 1 +dup2: 1 +select: 1 +mkdirat: 1 +fsync: 1 +getpeername: 1 +getppid: 1 + +# +# obfs4proxy specific system calls allowed with filtering. +# + +epoll_create1: arg0 == EPOLL_CLOEXEC + +# +# System calls allowed with filtering that obfs4proxy/tor want to allow +# different things for. +# + +futex: arg1 == FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME || arg1 == FUTEX_WAKE_PRIVATE || arg1 == FUTEX_WAIT_PRIVATE || arg1 == FUTEX_WAKE || arg1 == FUTEX_WAIT +mprotect: arg2 == PROT_READ || arg2 == PROT_NONE || arg2 == PROT_READ|PROT_WRITE +mmap: (arg2 == PROT_READ && arg3 == MAP_PRIVATE) || (arg2 == PROT_NONE && arg3 == MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE) || (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_ANONYMOUS) || (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK) || (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE) || (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS) || (arg2 == PROT_READ|PROT_EXEC && arg3 == MAP_PRIVATE|MAP_DENYWRITE) || (arg2 == PROT_READ|PROT_WRITE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE) || (arg2 == PROT_NONE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE) || (arg2 == PROT_NONE && arg3 == MAP_PRIVATE|MAP_ANONYMOUS) || (arg2 == PROT_NONE && arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS) +setsockopt: (arg1 == SOL_SOCKET && (arg2 == SO_REUSEADDR || arg2 == SO_SNDBUF || arg2 == SO_RCVBUF || arg2 == SO_BROADCAST)) || (arg1 == SOL_TCP && arg2 == TCP_NODELAY) || (arg1 == SOL_IPV6 && arg2 == IPV6_V6ONLY) diff --git a/data/torbrowser-amd64.seccomp b/data/torbrowser-amd64.seccomp new file mode 100644 index 0000000..a2c2817 --- /dev/null +++ b/data/torbrowser-amd64.seccomp @@ -0,0 +1,210 @@ +# Tor Browser (x86_64) seccomp whitelist. +# +# This is based off of: +# https://github.com/subgraph/subgraph-oz-profiles/blob/master/torbrowser-laun... +# https://github.com/mozilla/gecko-dev/blob/master/security/sandbox/linux/Sand... + +# +# Extra constant definitions needed for filtering. +# + +FIONREAD = 0x541b +TCGETS = 0x5401 +TIOCGPGRP = 0x540f + +MADV_NORMAL=0 +MADV_DONTNEED=4 +MADV_FREE=8 + +FUTEX_WAIT=0 +FUTEX_WAKE=1 +FUTEX_FD=2 +FUTEX_REQUEUE=3 +FUTEX_CMP_REQUEUE=4 +FUTEX_WAKE_OP=5 +#FUTEX_LOCK_PI=6 +#FUTEX_UNLOCK_PI=7 +FUTEX_WAIT_BITSET=9 +FUTEX_PRIVATE_FLAG=128 +FUTEX_CLOCK_REALTIME=256 + +FUTEX_WAIT_PRIVATE=FUTEX_WAIT | FUTEX_PRIVATE_FLAG +FUTEX_WAKE_PRIVATE=FUTEX_WAKE | FUTEX_PRIVATE_FLAG +FUTEX_CMP_REQUEUE_PRIVATE=FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG +FUTEX_WAKE_OP_PRIVATE=FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG +#FUTEX_LOCK_PI_PRIVATE=FUTEX_LOCK_PI | FUTEX_PRIVATE_FLAG +#FUTEX_UNLOCK_PI_PRIVATE=FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG +FUTEX_WAIT_BITSET_PRIVATE=FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG + +# +# System calls allowed unconditionally without argument filtering. +# + +clock_gettime: 1 +clock_getres: 1 +gettimeofday: 1 +nanosleep: 1 +sched_yield: 1 + +open: 1 +openat: 1 +pread64: 1 +read: 1 +recvfrom: 1 +pwrite64: 1 +sendto: 1 +write: 1 +writev: 1 +close: 1 + +access: 1 +creat: 1 +chmod: 1 +chdir: 1 +dup2: 1 +dup: 1 +fadvise64: 1 +fallocate: 1 +fcntl: 1 +fchmod: 1 +fchown: 1 +fchdir: 1 +fdatasync: 1 +fstat: 1 +fstatfs: 1 +ftruncate: 1 +fsync: 1 +getcwd: 1 +getdents: 1 +getdents64: 1 +link: 1 +lseek: 1 +lstat: 1 +mkdir: 1 +name_to_handle_at: 1 +newfstatat: 1 +pipe: 1 +pipe2: 1 +readahead: 1 +readlink: 1 +readlinkat: 1 +rename: 1 +rmdir: 1 +stat: 1 +splice: 1 +statfs: 1 +symlink: 1 +unlink: 1 +utime: 1 +utimes: 1 + +accept4: 1 +bind: 1 +connect: 1 +epoll_create: 1 +epoll_create1: 1 +epoll_ctl: 1 +epoll_wait: 1 +eventfd2: 1 +getsockname: 1 +getsockopt: 1 +getpeername: 1 +listen: 1 +poll: 1 +ppoll: 1 +recvmsg: 1 +socketpair: 1 +select: 1 +sendmsg: 1 +setsockopt: 1 +shutdown: 1 + +inotify_add_watch: 1 +inotify_init1: 1 +inotify_rm_watch: 1 + +brk: 1 +mincore: 1 +mmap: 1 +mprotect: 1 +mremap: 1 +munmap: 1 + +shmdt: 1 +shmat: 1 +shmctl: 1 +shmget: 1 + +alarm: 1 +execve: 1 +getrandom: 1 +getrlimit: 1 +getrusage: 1 +getpgrp: 1 +getppid: 1 +getpid: 1 +getpriority: 1 +getresgid: 1 +getresuid: 1 +gettid: 1 +getuid: 1 +geteuid: 1 +getgid: 1 +getegid: 1 +rt_sigaction: 1 +rt_sigprocmask: 1 +rt_sigreturn: 1 +rt_tgsigqueueinfo: 1 +sigaltstack: 1 + +arch_prctl: 1 +capset: 1 +capget: 1 +clone: 1 +exit: 1 +exit_group: 1 +kill: 1 +restart_syscall: 1 +seccomp: 1 +sched_getaffinity: 1 +sched_setscheduler: 1 +setpriority: 1 +set_robust_list: 1 +setsid: 1 +set_tid_address: 1 +setresuid: 1 +setresgid: 1 +sysinfo: 1 +tgkill: 1 +umask: 1 +uname: 1 +unshare: 1 +wait4: 1 + +# XXX: This is only required for ASAN builds, so this should be included at +# runtime. +setrlimit: 1 + +# +# System calls allowed with filtering. +# +# Note: Because we patch PulseAudio from tbb_stub.so, we can omit all PI futex +# calls. +# + +futex: arg1 == FUTEX_CMP_REQUEUE_PRIVATE || arg1 == FUTEX_WAIT || arg1 == FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME || arg1 == FUTEX_WAIT_PRIVATE || arg1 == FUTEX_WAKE || arg1 == FUTEX_WAKE_OP_PRIVATE || arg1 == FUTEX_WAKE_PRIVATE || arg1 == FUTEX_WAIT_BITSET_PRIVATE +madvise: arg2 == MADV_NORMAL || arg2 == MADV_DONTNEED || arg2 == MADV_FREE +ioctl: arg1 == FIONREAD || arg1 == TCGETS || arg1 == TIOCGPGRP +prctl: arg0 == PR_SET_NAME || arg0 == PR_GET_NAME || arg0 == PR_GET_TIMERSLACK || arg0 == PR_SET_SECCOMP +socket: arg0 == AF_UNIX + +# Calls that other people think we should have but we deny: +# +# Firefox: +# * quotactl - gracefully deals with rejection. +# +# Subgraph (all probably python): +# * vfork +# * memfd_create +# * personality +# * mlock diff --git a/src/cmd/gen-seccomp/main.go b/src/cmd/gen-seccomp/main.go deleted file mode 100644 index 8216166..0000000 --- a/src/cmd/gen-seccomp/main.go +++ /dev/null @@ -1,61 +0,0 @@ -// gen-seccomp.go - Pre-generate seccomp rules. -// Copyright (C) 2016 Yawning Angel. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. - -package main - -import ( - "flag" - "log" - "os" - "path/filepath" -) - -func main() { - outFlag := flag.String("o", "", "output directory") - flag.Parse() - - outDir, err := filepath.Abs(*outFlag) - if err != nil { - log.Fatalf("failed to get absolute path: %v", err) - } - - // Tor Browser (amd64) - f, err := os.Create(filepath.Join(outDir, "tor-amd64.bpf")) - if err != nil { - log.Fatalf("failed to create output: %v", err) - } - if err = compileTorSeccompProfile(f, false); err != nil { - log.Fatalf("failed to create tor amd64 profile: %v", err) - } - - // Tor Browser + obfs4proxy (amd64) - f, err = os.Create(filepath.Join(outDir, "tor-obfs4-amd64.bpf")) - if err != nil { - log.Fatalf("failed to create output: %v", err) - } - if err = compileTorSeccompProfile(f, true); err != nil { - log.Fatalf("failed to create tor-obfs4 amd64 profile: %v", err) - } - - // Firefox (amd64) - f, err = os.Create(filepath.Join(outDir, "torbrowser-amd64.bpf")) - if err != nil { - log.Fatalf("failed to create output: %v", err) - } - if err = compileTorBrowserSeccompProfile(f); err != nil { - log.Fatalf("failed to create firefox amd64 profile: %v", err) - } -} diff --git a/src/cmd/gen-seccomp/seccomp.go b/src/cmd/gen-seccomp/seccomp.go deleted file mode 100644 index b016f4c..0000000 --- a/src/cmd/gen-seccomp/seccomp.go +++ /dev/null @@ -1,120 +0,0 @@ -// secomp.go - Sandbox seccomp rules. -// Copyright (C) 2016 Yawning Angel. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. - -package main - -import ( - "fmt" - - seccomp "github.com/seccomp/libseccomp-golang" -) - -const ( - madvNormal = 0 // MADV_NORMAL - madvDontneed = 4 // MADV_DONTNEED - madvFree = 8 // MADV_FREE - mremapMaymove = 1 - - sigBlock = 1 // SIG_BLOCK - sigSetmask = 2 // SIG_SETMASK - - futexWait = 0 - futexWake = 1 - futexFd = 2 - futexRequeue = 3 - futexCmpRequeue = 4 - futexWakeOp = 5 - futexLockPi = 6 - futexUnlockPi = 7 - futexTrylockPi = 8 - futexWaitBitset = 9 - futexWakeBitset = 10 - futexWaitRequeuePi = 11 - futexCmpRequeuePi = 12 - - futexPrivateFlag = 128 - futexClockRealtime = 256 - - futexWaitPrivate = futexWait | futexPrivateFlag - futexWakePrivate = futexWake | futexPrivateFlag - futexRequeuePrivate = futexRequeue | futexPrivateFlag - futexCmpRequeuePrivate = futexCmpRequeue | futexPrivateFlag - futexWakeOpPrivate = futexWakeOp | futexPrivateFlag - futexLockPiPrivate = futexLockPi | futexPrivateFlag - futexUnlockPiPrivate = futexUnlockPi | futexPrivateFlag - futexTrylockPiPrivate = futexTrylockPi | futexPrivateFlag - futexWaitBitsetPrivate = futexWaitBitset | futexPrivateFlag - futexWakeBitsetPrivate = futexWakeBitset | futexPrivateFlag - futexWaitRequeuePiPrivate = futexWaitRequeuePi | futexPrivateFlag - futexCmpRequeuePiPrivate = futexCmpRequeuePi | futexPrivateFlag - - pollIn = 1 - - fionread = 0x541b - tcgets = 0x5401 - tiocgpgrp = 0x540f -) - -func newWhitelist() (*seccomp.ScmpFilter, error) { - actENOSYS := seccomp.ActErrno.SetReturnCode(38) - f, err := seccomp.NewFilter(actENOSYS) - if err != nil { - return nil, err - } - - if err = f.AddArch(seccomp.ArchAMD64); err != nil { - f.Release() - return nil, err - } - if err = f.SetBadArchAction(seccomp.ActKill); err != nil { - return nil, err - } - - return f, nil -} - -func allowSyscalls(f *seccomp.ScmpFilter, calls []string) error { - for _, scallName := range calls { - scall, err := seccomp.GetSyscallFromName(scallName) - if err != nil { - return fmt.Errorf("seccomp: unknown system call: %v", scallName) - } - if err = f.AddRule(scall, seccomp.ActAllow); err != nil { - return err - } - } - return nil -} - -func allowCmpEq(f *seccomp.ScmpFilter, scallName string, arg uint, values ...uint64) error { - scall, err := seccomp.GetSyscallFromName(scallName) - if err != nil { - return fmt.Errorf("seccomp: unknown system call: %v", scallName) - } - - // Allow if the arg matches any of the values. Implemented as multiple - // rules. - for _, v := range values { - argIsEqual, err := seccomp.MakeCondition(arg, seccomp.CompareEqual, v) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{argIsEqual}); err != nil { - return err - } - } - return nil -} diff --git a/src/cmd/gen-seccomp/seccomp_firefox.go b/src/cmd/gen-seccomp/seccomp_firefox.go deleted file mode 100644 index 427c5f9..0000000 --- a/src/cmd/gen-seccomp/seccomp_firefox.go +++ /dev/null @@ -1,215 +0,0 @@ -// secomp_firefox.go - Firefox sandbox seccomp rules. -// Copyright (C) 2016 Yawning Angel. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. - -package main - -import ( - "os" - "syscall" -) - -func compileTorBrowserSeccompProfile(fd *os.File) error { - defer fd.Close() - - f, err := newWhitelist() - if err != nil { - return err - } - defer f.Release() - - // TODO; Filter the arguments on more of these calls. - // - // Maybe draaw inspiration from: - // https://github.com/mozilla/gecko-dev/blob/master/security/sandbox/linux/Sand... - allowedNoArgs := []string{ - "clock_gettime", - "clock_getres", - "gettimeofday", - "nanosleep", - "sched_yield", - - "open", - "openat", - "pread64", - "read", - "recvfrom", - "pwrite64", - "sendto", - "write", - "writev", - "close", - - "access", - "creat", - "chmod", - "chdir", - "dup2", - "dup", - "fadvise64", - "fallocate", - "fcntl", - "fchmod", - "fchown", - "fchdir", - "fdatasync", - "fstat", - "fstatfs", - "ftruncate", - "fsync", - "getcwd", - "getdents", - "getdents64", - "link", - "lseek", - "lstat", - "mkdir", - "name_to_handle_at", - "newfstatat", - "pipe", - "pipe2", - "readahead", - "readlink", - "readlinkat", - "rename", - "rmdir", - "stat", - "splice", - "statfs", - "symlink", - "unlink", - "utime", - "utimes", - - "accept4", - "bind", - "connect", - "epoll_create", - "epoll_create1", - "epoll_ctl", - "epoll_wait", - "eventfd2", - "getsockname", - "getsockopt", - "getpeername", - "listen", - "poll", - "ppoll", - "recvmsg", - "socketpair", - "select", - "sendmsg", - "setsockopt", - "shutdown", - - "inotify_add_watch", - "inotify_init1", - "inotify_rm_watch", - - "brk", - "mincore", - "mmap", - "mprotect", - "mremap", - "munmap", - - "shmdt", - "shmat", - "shmctl", - "shmget", - - "alarm", - "execve", - "getrandom", - "getrlimit", - "getrusage", - "getpgrp", - "getppid", - "getpid", - "getpriority", - "getresgid", - "getresuid", - "gettid", - "getuid", - "geteuid", - "getgid", - "getegid", - "rt_sigaction", - "rt_sigprocmask", - "rt_sigreturn", - "sigaltstack", - - "arch_prctl", - "capset", - "capget", - "clone", - "exit", - "exit_group", - "kill", - "restart_syscall", - "seccomp", - "sched_getaffinity", - "sched_setscheduler", - "setpriority", - "set_robust_list", - "setsid", - "set_tid_address", - "setresuid", - "setresgid", - "sysinfo", - "tgkill", - "umask", - "uname", - "unshare", - "wait4", - - // ASAN explodes if this doesn't work. Sigh. - "setrlimit", - - // Firefox uses this, but will take no for an answer. - // "quotactl", - - // Subgraph's profile has these, but that's for Tor Browser Launcher. - // - // "vfork", - // "memfd_create", (PulseAudio? Won't work in our container.) - // "personality", - // "mlock", - } - if err = allowSyscalls(f, allowedNoArgs); err != nil { - return err - } - - // Because we patch PulseAudio's mutex creation, we can omit all PI futex - // calls. - if err = allowCmpEq(f, "futex", 1, futexWait, futexWaitPrivate, futexWakePrivate, futexCmpRequeuePrivate, futexWakeOpPrivate, futexWaitBitsetPrivate|futexClockRealtime, futexWake, futexWaitBitsetPrivate); err != nil { - return err - } - - if err = allowCmpEq(f, "madvise", 2, madvNormal, madvDontneed, madvFree); err != nil { - return err - } - if err = allowCmpEq(f, "ioctl", 1, fionread, tcgets, tiocgpgrp); err != nil { - return err - } - if err = allowCmpEq(f, "prctl", 0, syscall.PR_SET_NAME, syscall.PR_GET_NAME, syscall.PR_GET_TIMERSLACK, syscall.PR_SET_SECCOMP); err != nil { - return err - } - if err = allowCmpEq(f, "socket", 0, syscall.AF_UNIX); err != nil { - return err - } - - return f.ExportBPF(fd) -} diff --git a/src/cmd/gen-seccomp/seccomp_tor.go b/src/cmd/gen-seccomp/seccomp_tor.go deleted file mode 100644 index e6501e3..0000000 --- a/src/cmd/gen-seccomp/seccomp_tor.go +++ /dev/null @@ -1,595 +0,0 @@ -// secomp_tor.go - Sandbox tor seccomp rules. -// Copyright (C) 2016 Yawning Angel. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see http://www.gnu.org/licenses/. - -package main - -import ( - "os" - "syscall" - - seccomp "github.com/seccomp/libseccomp-golang" -) - -var maskedCloexecNonblock = ^(uint64(syscall.SOCK_CLOEXEC | syscall.SOCK_NONBLOCK)) - -func compileTorSeccompProfile(fd *os.File, useBridges bool) error { - defer fd.Close() - - f, err := newWhitelist() - if err != nil { - return err - } - defer f.Release() - - allowedNoArgs := []string{ - "access", - "brk", - "clock_gettime", - "close", - "clone", - "epoll_create", - "epoll_wait", - "eventfd2", - "pipe2", - "pipe", - "fstat", - "getdents", - "getdents64", - "getegid", - "geteuid", - "getgid", - "getrlimit", - "gettimeofday", - "gettid", - "getuid", - "lseek", - "mkdir", - "munmap", - "prlimit64", - "read", - "rt_sigreturn", - "sched_getaffinity", - "sched_yield", - "sendmsg", - "set_robust_list", - "setrlimit", - "sigaltstack", - "stat", - "uname", - "wait4", - "write", - "writev", - "exit_group", - "exit", - "getrandom", - "sysinfo", - "bind", - "listen", - "connect", - "getsockname", - "recvmsg", - "recvfrom", - "sendto", - "unlink", - - // Calls that tor can filter, but I can't due to not being in - // the tor daemon's process space. - "chown", - "chmod", - "open", - "openat", - "rename", - - // Calls made prior to tor's UseSeccomp being installed. - "arch_prctl", - "chdir", - "execve", - "getpid", - "kill", - "restart_syscall", - "set_tid_address", - "unshare", - "rt_sigaction", // Tor filters this but libc does more. - - "readlink", // ASAN needs this. - } - if err = allowSyscalls(f, allowedNoArgs); err != nil { - return err - } - - if err = allowCmpEq(f, "time", 0, 0); err != nil { - return err - } - if err = allowCmpEq(f, "madvise", 2, madvFree); err != nil { - return err - } - if err = allowCmpEq(f, "umask", 0, 022); err != nil { - return err - } - if err = allowCmpEq(f, "rt_sigprocmask", 0, sigBlock, sigSetmask); err != nil { - return err - } - if err = allowCmpEq(f, "epoll_ctl", 1, syscall.EPOLL_CTL_ADD, syscall.EPOLL_CTL_MOD, syscall.EPOLL_CTL_DEL); err != nil { - return err - } - if err = torFilterPrctl(f); err != nil { - return err - } - if err = allowCmpEq(f, "mprotect", 2, syscall.PROT_READ, syscall.PROT_NONE); err != nil { - return err - } - if err = allowCmpEq(f, "flock", 1, syscall.LOCK_EX|syscall.LOCK_NB, syscall.LOCK_UN); err != nil { - return err - } - if err = allowCmpEq(f, "futex", 1, futexWaitBitsetPrivate|futexClockRealtime, futexWaitPrivate, futexWakePrivate); err != nil { - return err - } - if err = allowCmpEq(f, "mremap", 3, mremapMaymove); err != nil { - return err - } - if err = torFilterAccept4(f); err != nil { - return err - } - if err = torFilterPoll(f); err != nil { - return err - } - if err = torFilterSocket(f); err != nil { - return err - } - if err = torFilterSetsockopt(f); err != nil { - return err - } - if err = torFilterGetsockopt(f); err != nil { - return err - } - if err = torFilterSocketpair(f); err != nil { - return err - } - if err = torFilterMmap(f); err != nil { - return err - } - if err = torFilterFcntl(f); err != nil { - return err - } - - if useBridges { - // XXX: One day, all the PTs will live in their own containers. - // - // Till then, just whitelist the extra calls obfs4proxy needs. - obfsCalls := []string{ - "mincore", - "dup2", - "select", - "mkdirat", - "fsync", - "getpeername", - "getppid", - } - if err = allowSyscalls(f, obfsCalls); err != nil { - return err - } - - // `mmap` -> `arg2 == PROT_NONE && (arg3 == MAP_PRIVATE|MAP_ANONYMOUS || arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS)` - if err = allowCmpEq(f, "epoll_create1", 0, syscall.EPOLL_CLOEXEC); err != nil { - return err - } - if err = allowCmpEq(f, "mprotect", 2, syscall.PROT_READ|syscall.PROT_WRITE); err != nil { - return err - } - if err = allowCmpEq(f, "futex", 1, futexWake, futexWait); err != nil { - return err - } - if err = obfsFilterSetsockopt(f); err != nil { - return err - } - if err = obfsFilterMmap(f); err != nil { - return err - } - } - - return f.ExportBPF(fd) -} - -func torFilterPrctl(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("prctl") - if err != nil { - return err - } - - isPrSetDumpable, err := seccomp.MakeCondition(0, seccomp.CompareEqual, syscall.PR_SET_DUMPABLE) - if err != nil { - return err - } - arg1IsZero, err := seccomp.MakeCondition(1, seccomp.CompareEqual, 0) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isPrSetDumpable, arg1IsZero}); err != nil { - return err - } - - isPrSetDeathsig, err := seccomp.MakeCondition(0, seccomp.CompareEqual, syscall.PR_SET_PDEATHSIG) - if err != nil { - return err - } - return f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isPrSetDeathsig}) -} - -func torFilterAccept4(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("accept4") - if err != nil { - return err - } - - cond, err := seccomp.MakeCondition(3, seccomp.CompareMaskedEqual, maskedCloexecNonblock, 0) - if err != nil { - return nil - } - - return f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{cond}) -} - -func torFilterPoll(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("poll") - if err != nil { - return err - } - - isPollIn, err := seccomp.MakeCondition(1, seccomp.CompareEqual, pollIn) - if err != nil { - return err - } - timeoutIsTen, err := seccomp.MakeCondition(2, seccomp.CompareEqual, 10) - if err != nil { - return err - } - return f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isPollIn, timeoutIsTen}) -} - -func torFilterSocket(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("socket") - if err != nil { - return err - } - - makeCondType := func(t uint64) (seccomp.ScmpCondition, error) { - return seccomp.MakeCondition(1, seccomp.CompareMaskedEqual, maskedCloexecNonblock, t) - } - - // tor allows PF_FILE, which is PF_LOCAL on Linux, not sure why. - - for _, d := range []uint64{syscall.AF_INET, syscall.AF_INET6} { - isDomain, err := seccomp.MakeCondition(0, seccomp.CompareEqual, d) - if err != nil { - return err - } - - for _, t := range []uint64{syscall.SOCK_STREAM, syscall.SOCK_DGRAM} { - protocols := []uint64{syscall.IPPROTO_IP, syscall.IPPROTO_UDP} - if t == syscall.SOCK_STREAM { - protocols = append(protocols, syscall.IPPROTO_TCP) - } - - isType, err := makeCondType(t) - if err != nil { - return err - } - - for _, p := range protocols { - isProtocol, err := seccomp.MakeCondition(2, seccomp.CompareEqual, p) - if err != nil { - return err - } - - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isDomain, isType, isProtocol}); err != nil { - return err - } - } - } - } - - isAfLocal, err := seccomp.MakeCondition(0, seccomp.CompareEqual, syscall.AF_LOCAL) - if err != nil { - return err - } - for _, t := range []uint64{syscall.SOCK_STREAM, syscall.SOCK_DGRAM} { - isType, err := makeCondType(t) - if err != nil { - return err - } - isProtocol, err := seccomp.MakeCondition(2, seccomp.CompareEqual, 0) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isAfLocal, isType, isProtocol}); err != nil { - return err - } - } - - // tor allows socket(AF_NETLINK, SOCK_RAW, 0), which is used to check it's - // IP address, but will take "no". - - return nil -} - -func torFilterSetsockopt(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("setsockopt") - if err != nil { - return err - } - - isSolSocket, err := seccomp.MakeCondition(1, seccomp.CompareEqual, syscall.SOL_SOCKET) - if err != nil { - return err - } - - okOpts := []uint64{ - syscall.SO_REUSEADDR, - syscall.SO_SNDBUF, - syscall.SO_RCVBUF, - } - - for _, opt := range okOpts { - isOpt, err := seccomp.MakeCondition(2, seccomp.CompareEqual, opt) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isSolSocket, isOpt}); err != nil { - return err - } - } - - return nil -} - -func torFilterGetsockopt(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("getsockopt") - if err != nil { - return err - } - - isSolSocket, err := seccomp.MakeCondition(1, seccomp.CompareEqual, syscall.SOL_SOCKET) - if err != nil { - return err - } - optIsError, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.SO_ERROR) - if err != nil { - return err - } - return f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isSolSocket, optIsError}) -} - -func torFilterSocketpair(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("socketpair") - if err != nil { - return err - } - - isPfLocal, err := seccomp.MakeCondition(0, seccomp.CompareEqual, syscall.AF_LOCAL) - if err != nil { - return err - } - - // XXX: src/common/compat.c:tor_socketpair looks like it uses SOCK_CLOEXEC, - // but according to strace, fcntl is used to actually set the flag (6.0.6). - okTypes := []uint64{ - syscall.SOCK_STREAM, - syscall.SOCK_STREAM | syscall.SOCK_CLOEXEC, - } - for _, t := range okTypes { - isType, err := seccomp.MakeCondition(1, seccomp.CompareEqual, t) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isPfLocal, isType}); err != nil { - return err - } - } - return nil -} - -func torFilterMmap(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("mmap") - if err != nil { - return err - } - - // (arg2 == PROT_READ && arg3 == MAP_PRIVATE) - isProtRead, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.PROT_READ) - if err != nil { - return err - } - isPrivate, err := seccomp.MakeCondition(3, seccomp.CompareEqual, syscall.MAP_PRIVATE) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isProtRead, isPrivate}); err != nil { - return err - } - - // (arg2 == PROT_NONE && arg3 == MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE) - isProtNone, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.PROT_NONE) - if err != nil { - return err - } - isProtNoneFlags, err := seccomp.MakeCondition(3, seccomp.CompareEqual, syscall.MAP_PRIVATE|syscall.MAP_ANONYMOUS|syscall.MAP_NORESERVE) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isProtNone, isProtNoneFlags}); err != nil { - return err - } - - isProtReadWrite, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.PROT_READ|syscall.PROT_WRITE) - if err != nil { - return err - } - rwFlags := []uint64{ - syscall.MAP_PRIVATE | syscall.MAP_ANONYMOUS, - syscall.MAP_PRIVATE | syscall.MAP_ANONYMOUS | syscall.MAP_STACK, - syscall.MAP_PRIVATE | syscall.MAP_FIXED | syscall.MAP_DENYWRITE, - syscall.MAP_PRIVATE | syscall.MAP_FIXED | syscall.MAP_ANONYMOUS, - syscall.MAP_PRIVATE | syscall.MAP_DENYWRITE, - } - for _, flag := range rwFlags { - isFlag, err := seccomp.MakeCondition(3, seccomp.CompareEqual, flag) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isProtReadWrite, isFlag}); err != nil { - return err - } - } - - // (arg2 == PROT_READ | PROT_EXEC && arg3 == MAP_PRIVATE | MAP_DENYWRITE) - // This is needed for ld-linux.so. - isProtReadExec, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.PROT_READ|syscall.PROT_EXEC) - if err != nil { - return err - } - isProtReadExecFlags, err := seccomp.MakeCondition(3, seccomp.CompareEqual, syscall.MAP_PRIVATE|syscall.MAP_DENYWRITE) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isProtReadExec, isProtReadExecFlags}); err != nil { - return err - } - - return nil -} - -func torFilterFcntl(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("fcntl") - if err != nil { - return err - } - - isFGetfl, err := seccomp.MakeCondition(1, seccomp.CompareEqual, syscall.F_GETFL) - if err != nil { - return err - } - isFGetfd, err := seccomp.MakeCondition(1, seccomp.CompareEqual, syscall.F_GETFD) - if err != nil { - return err - } - - isFSetfl, err := seccomp.MakeCondition(1, seccomp.CompareEqual, syscall.F_SETFL) - if err != nil { - return err - } - isFSetflFlags, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.O_RDWR|syscall.O_NONBLOCK) - if err != nil { - return err - } - - isFSetfd, err := seccomp.MakeCondition(1, seccomp.CompareEqual, syscall.F_SETFD) - if err != nil { - return err - } - isFdCloexec, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.FD_CLOEXEC) - if err != nil { - return err - } - - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isFGetfl}); err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isFGetfd}); err != nil { - return err - } - - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isFSetfl, isFSetflFlags}); err != nil { - return err - } - - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isFSetfd, isFdCloexec}); err != nil { - return err - } - - return nil -} - -func obfsFilterSetsockopt(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("setsockopt") - if err != nil { - return err - } - - isSolTcp, err := seccomp.MakeCondition(1, seccomp.CompareEqual, syscall.SOL_TCP) - if err != nil { - return err - } - isTcpNodelay, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.TCP_NODELAY) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isSolTcp, isTcpNodelay}); err != nil { - return err - } - - isSolSocket, err := seccomp.MakeCondition(1, seccomp.CompareEqual, syscall.SOL_SOCKET) - if err != nil { - return err - } - isSoBroadcast, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.SO_BROADCAST) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isSolSocket, isSoBroadcast}); err != nil { - return err - } - - isSolIpv6, err := seccomp.MakeCondition(1, seccomp.CompareEqual, syscall.SOL_IPV6) - if err != nil { - return err - } - isIpv6Only, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.IPV6_V6ONLY) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isSolIpv6, isIpv6Only}); err != nil { - return err - } - - return nil -} - -// `mmap` -> `arg2 == PROT_NONE && (arg3 == MAP_PRIVATE|MAP_ANONYMOUS || arg3 == MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS)` -func obfsFilterMmap(f *seccomp.ScmpFilter) error { - scall, err := seccomp.GetSyscallFromName("mmap") - if err != nil { - return err - } - - isProtNone, err := seccomp.MakeCondition(2, seccomp.CompareEqual, syscall.PROT_NONE) - if err != nil { - return err - } - protNoneFlags := []uint64{ - syscall.MAP_PRIVATE | syscall.MAP_ANONYMOUS, - syscall.MAP_PRIVATE | syscall.MAP_FIXED | syscall.MAP_ANONYMOUS, - } - for _, flag := range protNoneFlags { - isFlag, err := seccomp.MakeCondition(3, seccomp.CompareEqual, flag) - if err != nil { - return err - } - if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isProtNone, isFlag}); err != nil { - return err - } - } - return nil -} diff --git a/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp.go b/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp.go index c88005e..9d5ec90 100644 --- a/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp.go +++ b/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp.go @@ -17,34 +17,81 @@ package sandbox
import ( + "encoding/binary" + "fmt" "os" "runtime"
+ "github.com/twtiger/gosecco" + "github.com/twtiger/gosecco/parser" + "cmd/sandboxed-tor-browser/internal/data" )
func installTorSeccompProfile(fd *os.File, useBridges bool) error { - assetFile := "tor-" - if useBridges { - assetFile = assetFile + "obfs4-" - } - assetFile = assetFile + runtime.GOARCH + ".bpf" + commonAssetFile := "tor-common-" + runtime.GOARCH + ".seccomp"
- bpf, err := data.Asset(assetFile) - if err != nil { - return err + assets := []string{commonAssetFile} + if useBridges { + assets = append(assets, "tor-obfs4-"+runtime.GOARCH+".seccomp") + } else { + assets = append(assets, "tor-"+runtime.GOARCH+".seccomp") }
- return writeBuffer(fd, bpf) + return installSeccomp(fd, assets) }
func installTorBrowserSeccompProfile(fd *os.File) error { - assetFile := "torbrowser-" + runtime.GOARCH + ".bpf" + assetFile := "torbrowser-" + runtime.GOARCH + ".seccomp" + + return installSeccomp(fd, []string{assetFile}) +} + +func installSeccomp(fd *os.File, ruleAssets []string) error { + defer fd.Close()
- bpf, err := data.Asset(assetFile) + settings := gosecco.SeccompSettings{ + DefaultPositiveAction: "allow", + DefaultNegativeAction: "ENOSYS", + DefaultPolicyAction: "ENOSYS", + ActionOnX32: "kill", + ActionOnAuditFailure: "kill", + } + + if len(ruleAssets) == 0 { + return fmt.Errorf("installSeccomp() called with no rules") + } + + // Combine the rules into a single source. + var sources []parser.Source + for _, asset := range ruleAssets { + rules, err := data.Asset(asset) + if err != nil { + return err + } + source := &parser.StringSource{ + Name: asset, + Content: string(rules), + } + sources = append(sources, source) + } + + // Compile the combined source into bpf bytecode. + combined := parser.CombineSources(sources...) + bpf, err := gosecco.PrepareSource(combined, settings) if err != nil { return err }
- return writeBuffer(fd, bpf) + // Install the bpf bytecode. + if size, limit := len(bpf), 0xffff; size > limit { + return fmt.Errorf("filter program too big: %d bpf instructions (limit = %d)", size, limit) + } + for _, rule := range bpf { + if err := binary.Write(fd, binary.LittleEndian, rule); err != nil { + return err + } + } + + return nil } diff --git a/vendor/manifest b/vendor/manifest index 04dfc33..a783eed 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -38,12 +38,6 @@ "branch": "master" }, { - "importpath": "github.com/seccomp/libseccomp-golang", - "repository": "https://github.com/seccomp/libseccomp-golang", - "revision": "32f571b70023028bd57d9288c20efbcb237f3ce0", - "branch": "master" - }, - { "importpath": "github.com/twtiger/gosecco", "repository": "https://github.com/twtiger/gosecco", "revision": "81110d334ed7a530d99ad620375855246f21d524", diff --git a/vendor/src/github.com/seccomp/libseccomp-golang/LICENSE b/vendor/src/github.com/seccomp/libseccomp-golang/LICENSE deleted file mode 100644 index 81cf60d..0000000 --- a/vendor/src/github.com/seccomp/libseccomp-golang/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2015 Matthew Heon mheon@redhat.com -Copyright (c) 2015 Paul Moore pmoore@redhat.com -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: -- Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/src/github.com/seccomp/libseccomp-golang/README b/vendor/src/github.com/seccomp/libseccomp-golang/README deleted file mode 100644 index 64cab69..0000000 --- a/vendor/src/github.com/seccomp/libseccomp-golang/README +++ /dev/null @@ -1,26 +0,0 @@ -libseccomp-golang: Go Language Bindings for the libseccomp Project -=============================================================================== -https://github.com/seccomp/libseccomp-golang -https://github.com/seccomp/libseccomp - -The libseccomp library provides an easy to use, platform independent, interface -to the Linux Kernel's syscall filtering mechanism. The libseccomp API is -designed to abstract away the underlying BPF based syscall filter language and -present a more conventional function-call based filtering interface that should -be familiar to, and easily adopted by, application developers. - -The libseccomp-golang library provides a Go based interface to the libseccomp -library. - -* Online Resources - -The library source repository currently lives on GitHub at the following URLs: - - -> https://github.com/seccomp/libseccomp-golang - -> https://github.com/seccomp/libseccomp - -The project mailing list is currently hosted on Google Groups at the URL below, -please note that a Google account is not required to subscribe to the mailing -list. - - -> https://groups.google.com/d/forum/libseccomp diff --git a/vendor/src/github.com/seccomp/libseccomp-golang/seccomp.go b/vendor/src/github.com/seccomp/libseccomp-golang/seccomp.go deleted file mode 100644 index b2c010f..0000000 --- a/vendor/src/github.com/seccomp/libseccomp-golang/seccomp.go +++ /dev/null @@ -1,857 +0,0 @@ -// +build linux - -// Public API specification for libseccomp Go bindings -// Contains public API for the bindings - -// Package seccomp provides bindings for libseccomp, a library wrapping the Linux -// seccomp syscall. Seccomp enables an application to restrict system call use -// for itself and its children. -package seccomp - -import ( - "fmt" - "os" - "runtime" - "strings" - "sync" - "syscall" - "unsafe" -) - -// C wrapping code - -// #cgo pkg-config: libseccomp -// #include <stdlib.h> -// #include <seccomp.h> -import "C" - -// Exported types - -// ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a -// per-architecture basis. -type ScmpArch uint - -// ScmpAction represents an action to be taken on a filter rule match in -// libseccomp -type ScmpAction uint - -// ScmpCompareOp represents a comparison operator which can be used in a filter -// rule -type ScmpCompareOp uint - -// ScmpCondition represents a rule in a libseccomp filter context -type ScmpCondition struct { - Argument uint `json:"argument,omitempty"` - Op ScmpCompareOp `json:"operator,omitempty"` - Operand1 uint64 `json:"operand_one,omitempty"` - Operand2 uint64 `json:"operand_two,omitempty"` -} - -// ScmpSyscall represents a Linux System Call -type ScmpSyscall int32 - -// Exported Constants - -const ( - // Valid architectures recognized by libseccomp - // ARM64 and all MIPS architectures are unsupported by versions of the - // library before v2.2 and will return errors if used - - // ArchInvalid is a placeholder to ensure uninitialized ScmpArch - // variables are invalid - ArchInvalid ScmpArch = iota - // ArchNative is the native architecture of the kernel - ArchNative ScmpArch = iota - // ArchX86 represents 32-bit x86 syscalls - ArchX86 ScmpArch = iota - // ArchAMD64 represents 64-bit x86-64 syscalls - ArchAMD64 ScmpArch = iota - // ArchX32 represents 64-bit x86-64 syscalls (32-bit pointers) - ArchX32 ScmpArch = iota - // ArchARM represents 32-bit ARM syscalls - ArchARM ScmpArch = iota - // ArchARM64 represents 64-bit ARM syscalls - ArchARM64 ScmpArch = iota - // ArchMIPS represents 32-bit MIPS syscalls - ArchMIPS ScmpArch = iota - // ArchMIPS64 represents 64-bit MIPS syscalls - ArchMIPS64 ScmpArch = iota - // ArchMIPS64N32 represents 64-bit MIPS syscalls (32-bit pointers) - ArchMIPS64N32 ScmpArch = iota - // ArchMIPSEL represents 32-bit MIPS syscalls (little endian) - ArchMIPSEL ScmpArch = iota - // ArchMIPSEL64 represents 64-bit MIPS syscalls (little endian) - ArchMIPSEL64 ScmpArch = iota - // ArchMIPSEL64N32 represents 64-bit MIPS syscalls (little endian, - // 32-bit pointers) - ArchMIPSEL64N32 ScmpArch = iota - // ArchPPC represents 32-bit POWERPC syscalls - ArchPPC ScmpArch = iota - // ArchPPC64 represents 64-bit POWER syscalls (big endian) - ArchPPC64 ScmpArch = iota - // ArchPPC64LE represents 64-bit POWER syscalls (little endian) - ArchPPC64LE ScmpArch = iota - // ArchS390 represents 31-bit System z/390 syscalls - ArchS390 ScmpArch = iota - // ArchS390X represents 64-bit System z/390 syscalls - ArchS390X ScmpArch = iota -) - -const ( - // Supported actions on filter match - - // ActInvalid is a placeholder to ensure uninitialized ScmpAction - // variables are invalid - ActInvalid ScmpAction = iota - // ActKill kills the process - ActKill ScmpAction = iota - // ActTrap throws SIGSYS - ActTrap ScmpAction = iota - // ActErrno causes the syscall to return a negative error code. This - // code can be set with the SetReturnCode method - ActErrno ScmpAction = iota - // ActTrace causes the syscall to notify tracing processes with the - // given error code. This code can be set with the SetReturnCode method - ActTrace ScmpAction = iota - // ActAllow permits the syscall to continue execution - ActAllow ScmpAction = iota -) - -const ( - // These are comparison operators used in conditional seccomp rules - // They are used to compare the value of a single argument of a syscall - // against a user-defined constant - - // CompareInvalid is a placeholder to ensure uninitialized ScmpCompareOp - // variables are invalid - CompareInvalid ScmpCompareOp = iota - // CompareNotEqual returns true if the argument is not equal to the - // given value - CompareNotEqual ScmpCompareOp = iota - // CompareLess returns true if the argument is less than the given value - CompareLess ScmpCompareOp = iota - // CompareLessOrEqual returns true if the argument is less than or equal - // to the given value - CompareLessOrEqual ScmpCompareOp = iota - // CompareEqual returns true if the argument is equal to the given value - CompareEqual ScmpCompareOp = iota - // CompareGreaterEqual returns true if the argument is greater than or - // equal to the given value - CompareGreaterEqual ScmpCompareOp = iota - // CompareGreater returns true if the argument is greater than the given - // value - CompareGreater ScmpCompareOp = iota - // CompareMaskedEqual returns true if the argument is equal to the given - // value, when masked (bitwise &) against the second given value - CompareMaskedEqual ScmpCompareOp = iota -) - -// Helpers for types - -// GetArchFromString returns an ScmpArch constant from a string representing an -// architecture -func GetArchFromString(arch string) (ScmpArch, error) { - switch strings.ToLower(arch) { - case "x86": - return ArchX86, nil - case "amd64", "x86-64", "x86_64", "x64": - return ArchAMD64, nil - case "x32": - return ArchX32, nil - case "arm": - return ArchARM, nil - case "arm64", "aarch64": - return ArchARM64, nil - case "mips": - return ArchMIPS, nil - case "mips64": - return ArchMIPS64, nil - case "mips64n32": - return ArchMIPS64N32, nil - case "mipsel": - return ArchMIPSEL, nil - case "mipsel64": - return ArchMIPSEL64, nil - case "mipsel64n32": - return ArchMIPSEL64N32, nil - case "ppc": - return ArchPPC, nil - case "ppc64": - return ArchPPC64, nil - case "ppc64le": - return ArchPPC64LE, nil - case "s390": - return ArchS390, nil - case "s390x": - return ArchS390X, nil - default: - return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %s", arch) - } -} - -// String returns a string representation of an architecture constant -func (a ScmpArch) String() string { - switch a { - case ArchX86: - return "x86" - case ArchAMD64: - return "amd64" - case ArchX32: - return "x32" - case ArchARM: - return "arm" - case ArchARM64: - return "arm64" - case ArchMIPS: - return "mips" - case ArchMIPS64: - return "mips64" - case ArchMIPS64N32: - return "mips64n32" - case ArchMIPSEL: - return "mipsel" - case ArchMIPSEL64: - return "mipsel64" - case ArchMIPSEL64N32: - return "mipsel64n32" - case ArchPPC: - return "ppc" - case ArchPPC64: - return "ppc64" - case ArchPPC64LE: - return "ppc64le" - case ArchS390: - return "s390" - case ArchS390X: - return "s390x" - case ArchNative: - return "native" - case ArchInvalid: - return "Invalid architecture" - default: - return "Unknown architecture" - } -} - -// String returns a string representation of a comparison operator constant -func (a ScmpCompareOp) String() string { - switch a { - case CompareNotEqual: - return "Not equal" - case CompareLess: - return "Less than" - case CompareLessOrEqual: - return "Less than or equal to" - case CompareEqual: - return "Equal" - case CompareGreaterEqual: - return "Greater than or equal to" - case CompareGreater: - return "Greater than" - case CompareMaskedEqual: - return "Masked equality" - case CompareInvalid: - return "Invalid comparison operator" - default: - return "Unrecognized comparison operator" - } -} - -// String returns a string representation of a seccomp match action -func (a ScmpAction) String() string { - switch a & 0xFFFF { - case ActKill: - return "Action: Kill Process" - case ActTrap: - return "Action: Send SIGSYS" - case ActErrno: - return fmt.Sprintf("Action: Return error code %d", (a >> 16)) - case ActTrace: - return fmt.Sprintf("Action: Notify tracing processes with code %d", - (a >> 16)) - case ActAllow: - return "Action: Allow system call" - default: - return "Unrecognized Action" - } -} - -// SetReturnCode adds a return code to a supporting ScmpAction, clearing any -// existing code Only valid on ActErrno and ActTrace. Takes no action otherwise. -// Accepts 16-bit return code as argument. -// Returns a valid ScmpAction of the original type with the new error code set. -func (a ScmpAction) SetReturnCode(code int16) ScmpAction { - aTmp := a & 0x0000FFFF - if aTmp == ActErrno || aTmp == ActTrace { - return (aTmp | (ScmpAction(code)&0xFFFF)<<16) - } - return a -} - -// GetReturnCode returns the return code of an ScmpAction -func (a ScmpAction) GetReturnCode() int16 { - return int16(a >> 16) -} - -// General utility functions - -// GetLibraryVersion returns the version of the library the bindings are built -// against. -// The version is formatted as follows: Major.Minor.Micro -func GetLibraryVersion() (major, minor, micro int) { - return verMajor, verMinor, verMicro -} - -// Syscall functions - -// GetName retrieves the name of a syscall from its number. -// Acts on any syscall number. -// Returns either a string containing the name of the syscall, or an error. -func (s ScmpSyscall) GetName() (string, error) { - return s.GetNameByArch(ArchNative) -} - -// GetNameByArch retrieves the name of a syscall from its number for a given -// architecture. -// Acts on any syscall number. -// Accepts a valid architecture constant. -// Returns either a string containing the name of the syscall, or an error. -// if the syscall is unrecognized or an issue occurred. -func (s ScmpSyscall) GetNameByArch(arch ScmpArch) (string, error) { - if err := sanitizeArch(arch); err != nil { - return "", err - } - - cString := C.seccomp_syscall_resolve_num_arch(arch.toNative(), C.int(s)) - if cString == nil { - return "", fmt.Errorf("could not resolve syscall name") - } - defer C.free(unsafe.Pointer(cString)) - - finalStr := C.GoString(cString) - return finalStr, nil -} - -// GetSyscallFromName returns the number of a syscall by name on the kernel's -// native architecture. -// Accepts a string containing the name of a syscall. -// Returns the number of the syscall, or an error if no syscall with that name -// was found. -func GetSyscallFromName(name string) (ScmpSyscall, error) { - cString := C.CString(name) - defer C.free(unsafe.Pointer(cString)) - - result := C.seccomp_syscall_resolve_name(cString) - if result == scmpError { - return 0, fmt.Errorf("could not resolve name to syscall") - } - - return ScmpSyscall(result), nil -} - -// GetSyscallFromNameByArch returns the number of a syscall by name for a given -// architecture's ABI. -// Accepts the name of a syscall and an architecture constant. -// Returns the number of the syscall, or an error if an invalid architecture is -// passed or a syscall with that name was not found. -func GetSyscallFromNameByArch(name string, arch ScmpArch) (ScmpSyscall, error) { - if err := sanitizeArch(arch); err != nil { - return 0, err - } - - cString := C.CString(name) - defer C.free(unsafe.Pointer(cString)) - - result := C.seccomp_syscall_resolve_name_arch(arch.toNative(), cString) - if result == scmpError { - return 0, fmt.Errorf("could not resolve name to syscall") - } - - return ScmpSyscall(result), nil -} - -// MakeCondition creates and returns a new condition to attach to a filter rule. -// Associated rules will only match if this condition is true. -// Accepts the number the argument we are checking, and a comparison operator -// and value to compare to. -// The rule will match if argument $arg (zero-indexed) of the syscall is -// $COMPARE_OP the provided comparison value. -// Some comparison operators accept two values. Masked equals, for example, -// will mask $arg of the syscall with the second value provided (via bitwise -// AND) and then compare against the first value provided. -// For example, in the less than or equal case, if the syscall argument was -// 0 and the value provided was 1, the condition would match, as 0 is less -// than or equal to 1. -// Return either an error on bad argument or a valid ScmpCondition struct. -func MakeCondition(arg uint, comparison ScmpCompareOp, values ...uint64) (ScmpCondition, error) { - var condStruct ScmpCondition - - if comparison == CompareInvalid { - return condStruct, fmt.Errorf("invalid comparison operator") - } else if arg > 5 { - return condStruct, fmt.Errorf("syscalls only have up to 6 arguments") - } else if len(values) > 2 { - return condStruct, fmt.Errorf("conditions can have at most 2 arguments") - } else if len(values) == 0 { - return condStruct, fmt.Errorf("must provide at least one value to compare against") - } - - condStruct.Argument = arg - condStruct.Op = comparison - condStruct.Operand1 = values[0] - if len(values) == 2 { - condStruct.Operand2 = values[1] - } else { - condStruct.Operand2 = 0 // Unused - } - - return condStruct, nil -} - -// Utility Functions - -// GetNativeArch returns architecture token representing the native kernel -// architecture -func GetNativeArch() (ScmpArch, error) { - arch := C.seccomp_arch_native() - - return archFromNative(arch) -} - -// Public Filter API - -// ScmpFilter represents a filter context in libseccomp. -// A filter context is initially empty. Rules can be added to it, and it can -// then be loaded into the kernel. -type ScmpFilter struct { - filterCtx C.scmp_filter_ctx - valid bool - lock sync.Mutex -} - -// NewFilter creates and returns a new filter context. -// Accepts a default action to be taken for syscalls which match no rules in -// the filter. -// Returns a reference to a valid filter context, or nil and an error if the -// filter context could not be created or an invalid default action was given. -func NewFilter(defaultAction ScmpAction) (*ScmpFilter, error) { - if err := sanitizeAction(defaultAction); err != nil { - return nil, err - } - - fPtr := C.seccomp_init(defaultAction.toNative()) - if fPtr == nil { - return nil, fmt.Errorf("could not create filter") - } - - filter := new(ScmpFilter) - filter.filterCtx = fPtr - filter.valid = true - runtime.SetFinalizer(filter, filterFinalizer) - - return filter, nil -} - -// IsValid determines whether a filter context is valid to use. -// Some operations (Release and Merge) render filter contexts invalid and -// consequently prevent further use. -func (f *ScmpFilter) IsValid() bool { - f.lock.Lock() - defer f.lock.Unlock() - - return f.valid -} - -// Reset resets a filter context, removing all its existing state. -// Accepts a new default action to be taken for syscalls which do not match. -// Returns an error if the filter or action provided are invalid. -func (f *ScmpFilter) Reset(defaultAction ScmpAction) error { - f.lock.Lock() - defer f.lock.Unlock() - - if err := sanitizeAction(defaultAction); err != nil { - return err - } else if !f.valid { - return errBadFilter - } - - retCode := C.seccomp_reset(f.filterCtx, defaultAction.toNative()) - if retCode != 0 { - return syscall.Errno(-1 * retCode) - } - - return nil -} - -// Release releases a filter context, freeing its memory. Should be called after -// loading into the kernel, when the filter is no longer needed. -// After calling this function, the given filter is no longer valid and cannot -// be used. -// Release() will be invoked automatically when a filter context is garbage -// collected, but can also be called manually to free memory. -func (f *ScmpFilter) Release() { - f.lock.Lock() - defer f.lock.Unlock() - - if !f.valid { - return - } - - f.valid = false - C.seccomp_release(f.filterCtx) -} - -// Merge merges two filter contexts. -// The source filter src will be released as part of the process, and will no -// longer be usable or valid after this call. -// To be merged, filters must NOT share any architectures, and all their -// attributes (Default Action, Bad Arch Action, No New Privs and TSync bools) -// must match. -// The filter src will be merged into the filter this is called on. -// The architectures of the src filter not present in the destination, and all -// associated rules, will be added to the destination. -// Returns an error if merging the filters failed. -func (f *ScmpFilter) Merge(src *ScmpFilter) error { - f.lock.Lock() - defer f.lock.Unlock() - - src.lock.Lock() - defer src.lock.Unlock() - - if !src.valid || !f.valid { - return fmt.Errorf("one or more of the filter contexts is invalid or uninitialized") - } - - // Merge the filters - retCode := C.seccomp_merge(f.filterCtx, src.filterCtx) - if syscall.Errno(-1*retCode) == syscall.EINVAL { - return fmt.Errorf("filters could not be merged due to a mismatch in attributes or invalid filter") - } else if retCode != 0 { - return syscall.Errno(-1 * retCode) - } - - src.valid = false - - return nil -} - -// IsArchPresent checks if an architecture is present in a filter. -// If a filter contains an architecture, it uses its default action for -// syscalls which do not match rules in it, and its rules can match syscalls -// for that ABI. -// If a filter does not contain an architecture, all syscalls made to that -// kernel ABI will fail with the filter's default Bad Architecture Action -// (by default, killing the process). -// Accepts an architecture constant. -// Returns true if the architecture is present in the filter, false otherwise, -// and an error on an invalid filter context, architecture constant, or an -// issue with the call to libseccomp. -func (f *ScmpFilter) IsArchPresent(arch ScmpArch) (bool, error) { - f.lock.Lock() - defer f.lock.Unlock() - - if err := sanitizeArch(arch); err != nil { - return false, err - } else if !f.valid { - return false, errBadFilter - } - - retCode := C.seccomp_arch_exist(f.filterCtx, arch.toNative()) - if syscall.Errno(-1*retCode) == syscall.EEXIST { - // -EEXIST is "arch not present" - return false, nil - } else if retCode != 0 { - return false, syscall.Errno(-1 * retCode) - } - - return true, nil -} - -// AddArch adds an architecture to the filter. -// Accepts an architecture constant. -// Returns an error on invalid filter context or architecture token, or an -// issue with the call to libseccomp. -func (f *ScmpFilter) AddArch(arch ScmpArch) error { - f.lock.Lock() - defer f.lock.Unlock() - - if err := sanitizeArch(arch); err != nil { - return err - } else if !f.valid { - return errBadFilter - } - - // Libseccomp returns -EEXIST if the specified architecture is already - // present. Succeed silently in this case, as it's not fatal, and the - // architecture is present already. - retCode := C.seccomp_arch_add(f.filterCtx, arch.toNative()) - if retCode != 0 && syscall.Errno(-1*retCode) != syscall.EEXIST { - return syscall.Errno(-1 * retCode) - } - - return nil -} - -// RemoveArch removes an architecture from the filter. -// Accepts an architecture constant. -// Returns an error on invalid filter context or architecture token, or an -// issue with the call to libseccomp. -func (f *ScmpFilter) RemoveArch(arch ScmpArch) error { - f.lock.Lock() - defer f.lock.Unlock() - - if err := sanitizeArch(arch); err != nil { - return err - } else if !f.valid { - return errBadFilter - } - - // Similar to AddArch, -EEXIST is returned if the arch is not present - // Succeed silently in that case, this is not fatal and the architecture - // is not present in the filter after RemoveArch - retCode := C.seccomp_arch_remove(f.filterCtx, arch.toNative()) - if retCode != 0 && syscall.Errno(-1*retCode) != syscall.EEXIST { - return syscall.Errno(-1 * retCode) - } - - return nil -} - -// Load loads a filter context into the kernel. -// Returns an error if the filter context is invalid or the syscall failed. -func (f *ScmpFilter) Load() error { - f.lock.Lock() - defer f.lock.Unlock() - - if !f.valid { - return errBadFilter - } - - if retCode := C.seccomp_load(f.filterCtx); retCode != 0 { - return syscall.Errno(-1 * retCode) - } - - return nil -} - -// GetDefaultAction returns the default action taken on a syscall which does not -// match a rule in the filter, or an error if an issue was encountered -// retrieving the value. -func (f *ScmpFilter) GetDefaultAction() (ScmpAction, error) { - action, err := f.getFilterAttr(filterAttrActDefault) - if err != nil { - return 0x0, err - } - - return actionFromNative(action) -} - -// GetBadArchAction returns the default action taken on a syscall for an -// architecture not in the filter, or an error if an issue was encountered -// retrieving the value. -func (f *ScmpFilter) GetBadArchAction() (ScmpAction, error) { - action, err := f.getFilterAttr(filterAttrActBadArch) - if err != nil { - return 0x0, err - } - - return actionFromNative(action) -} - -// GetNoNewPrivsBit returns the current state the No New Privileges bit will be set -// to on the filter being loaded, or an error if an issue was encountered -// retrieving the value. -// The No New Privileges bit tells the kernel that new processes run with exec() -// cannot gain more privileges than the process that ran exec(). -// For example, a process with No New Privileges set would be unable to exec -// setuid/setgid executables. -func (f *ScmpFilter) GetNoNewPrivsBit() (bool, error) { - noNewPrivs, err := f.getFilterAttr(filterAttrNNP) - if err != nil { - return false, err - } - - if noNewPrivs == 0 { - return false, nil - } - - return true, nil -} - -// GetTsyncBit returns whether Thread Synchronization will be enabled on the -// filter being loaded, or an error if an issue was encountered retrieving the -// value. -// Thread Sync ensures that all members of the thread group of the calling -// process will share the same Seccomp filter set. -// Tsync is a fairly recent addition to the Linux kernel and older kernels -// lack support. If the running kernel does not support Tsync and it is -// requested in a filter, Libseccomp will not enable TSync support and will -// proceed as normal. -// This function is unavailable before v2.2 of libseccomp and will return an -// error. -func (f *ScmpFilter) GetTsyncBit() (bool, error) { - tSync, err := f.getFilterAttr(filterAttrTsync) - if err != nil { - return false, err - } - - if tSync == 0 { - return false, nil - } - - return true, nil -} - -// SetBadArchAction sets the default action taken on a syscall for an -// architecture not in the filter, or an error if an issue was encountered -// setting the value. -func (f *ScmpFilter) SetBadArchAction(action ScmpAction) error { - if err := sanitizeAction(action); err != nil { - return err - } - - return f.setFilterAttr(filterAttrActBadArch, action.toNative()) -} - -// SetNoNewPrivsBit sets the state of the No New Privileges bit, which will be -// applied on filter load, or an error if an issue was encountered setting the -// value. -// Filters with No New Privileges set to 0 can only be loaded if the process -// has the CAP_SYS_ADMIN capability. -func (f *ScmpFilter) SetNoNewPrivsBit(state bool) error { - var toSet C.uint32_t = 0x0 - - if state { - toSet = 0x1 - } - - return f.setFilterAttr(filterAttrNNP, toSet) -} - -// SetTsync sets whether Thread Synchronization will be enabled on the filter -// being loaded. Returns an error if setting Tsync failed, or the filter is -// invalid. -// Thread Sync ensures that all members of the thread group of the calling -// process will share the same Seccomp filter set. -// Tsync is a fairly recent addition to the Linux kernel and older kernels -// lack support. If the running kernel does not support Tsync and it is -// requested in a filter, Libseccomp will not enable TSync support and will -// proceed as normal. -// This function is unavailable before v2.2 of libseccomp and will return an -// error. -func (f *ScmpFilter) SetTsync(enable bool) error { - var toSet C.uint32_t = 0x0 - - if enable { - toSet = 0x1 - } - - return f.setFilterAttr(filterAttrTsync, toSet) -} - -// SetSyscallPriority sets a syscall's priority. -// This provides a hint to the filter generator in libseccomp about the -// importance of this syscall. High-priority syscalls are placed -// first in the filter code, and incur less overhead (at the expense of -// lower-priority syscalls). -func (f *ScmpFilter) SetSyscallPriority(call ScmpSyscall, priority uint8) error { - f.lock.Lock() - defer f.lock.Unlock() - - if !f.valid { - return errBadFilter - } - - if retCode := C.seccomp_syscall_priority(f.filterCtx, C.int(call), - C.uint8_t(priority)); retCode != 0 { - return syscall.Errno(-1 * retCode) - } - - return nil -} - -// AddRule adds a single rule for an unconditional action on a syscall. -// Accepts the number of the syscall and the action to be taken on the call -// being made. -// Returns an error if an issue was encountered adding the rule. -func (f *ScmpFilter) AddRule(call ScmpSyscall, action ScmpAction) error { - return f.addRuleGeneric(call, action, false, nil) -} - -// AddRuleExact adds a single rule for an unconditional action on a syscall. -// Accepts the number of the syscall and the action to be taken on the call -// being made. -// No modifications will be made to the rule, and it will fail to add if it -// cannot be applied to the current architecture without modification. -// The rule will function exactly as described, but it may not function identically -// (or be able to be applied to) all architectures. -// Returns an error if an issue was encountered adding the rule. -func (f *ScmpFilter) AddRuleExact(call ScmpSyscall, action ScmpAction) error { - return f.addRuleGeneric(call, action, true, nil) -} - -// AddRuleConditional adds a single rule for a conditional action on a syscall. -// Returns an error if an issue was encountered adding the rule. -// All conditions must match for the rule to match. -// There is a bug in library versions below v2.2.1 which can, in some cases, -// cause conditions to be lost when more than one are used. Consequently, -// AddRuleConditional is disabled on library versions lower than v2.2.1 -func (f *ScmpFilter) AddRuleConditional(call ScmpSyscall, action ScmpAction, conds []ScmpCondition) error { - return f.addRuleGeneric(call, action, false, conds) -} - -// AddRuleConditionalExact adds a single rule for a conditional action on a -// syscall. -// No modifications will be made to the rule, and it will fail to add if it -// cannot be applied to the current architecture without modification. -// The rule will function exactly as described, but it may not function identically -// (or be able to be applied to) all architectures. -// Returns an error if an issue was encountered adding the rule. -// There is a bug in library versions below v2.2.1 which can, in some cases, -// cause conditions to be lost when more than one are used. Consequently, -// AddRuleConditionalExact is disabled on library versions lower than v2.2.1 -func (f *ScmpFilter) AddRuleConditionalExact(call ScmpSyscall, action ScmpAction, conds []ScmpCondition) error { - return f.addRuleGeneric(call, action, true, conds) -} - -// ExportPFC output PFC-formatted, human-readable dump of a filter context's -// rules to a file. -// Accepts file to write to (must be open for writing). -// Returns an error if writing to the file fails. -func (f *ScmpFilter) ExportPFC(file *os.File) error { - f.lock.Lock() - defer f.lock.Unlock() - - fd := file.Fd() - - if !f.valid { - return errBadFilter - } - - if retCode := C.seccomp_export_pfc(f.filterCtx, C.int(fd)); retCode != 0 { - return syscall.Errno(-1 * retCode) - } - - return nil -} - -// ExportBPF outputs Berkeley Packet Filter-formatted, kernel-readable dump of a -// filter context's rules to a file. -// Accepts file to write to (must be open for writing). -// Returns an error if writing to the file fails. -func (f *ScmpFilter) ExportBPF(file *os.File) error { - f.lock.Lock() - defer f.lock.Unlock() - - fd := file.Fd() - - if !f.valid { - return errBadFilter - } - - if retCode := C.seccomp_export_bpf(f.filterCtx, C.int(fd)); retCode != 0 { - return syscall.Errno(-1 * retCode) - } - - return nil -} diff --git a/vendor/src/github.com/seccomp/libseccomp-golang/seccomp_internal.go b/vendor/src/github.com/seccomp/libseccomp-golang/seccomp_internal.go deleted file mode 100644 index ab67a3d..0000000 --- a/vendor/src/github.com/seccomp/libseccomp-golang/seccomp_internal.go +++ /dev/null @@ -1,506 +0,0 @@ -// +build linux - -// Internal functions for libseccomp Go bindings -// No exported functions - -package seccomp - -import ( - "fmt" - "os" - "syscall" -) - -// Unexported C wrapping code - provides the C-Golang interface -// Get the seccomp header in scope -// Need stdlib.h for free() on cstrings - -// #cgo pkg-config: libseccomp -/* -#include <stdlib.h> -#include <seccomp.h> - -#if SCMP_VER_MAJOR < 2 -#error Minimum supported version of Libseccomp is v2.1.0 -#elif SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 1 -#error Minimum supported version of Libseccomp is v2.1.0 -#endif - -#define ARCH_BAD ~0 - -const uint32_t C_ARCH_BAD = ARCH_BAD; - -#ifndef SCMP_ARCH_AARCH64 -#define SCMP_ARCH_AARCH64 ARCH_BAD -#endif - -#ifndef SCMP_ARCH_MIPS -#define SCMP_ARCH_MIPS ARCH_BAD -#endif - -#ifndef SCMP_ARCH_MIPS64 -#define SCMP_ARCH_MIPS64 ARCH_BAD -#endif - -#ifndef SCMP_ARCH_MIPS64N32 -#define SCMP_ARCH_MIPS64N32 ARCH_BAD -#endif - -#ifndef SCMP_ARCH_MIPSEL -#define SCMP_ARCH_MIPSEL ARCH_BAD -#endif - -#ifndef SCMP_ARCH_MIPSEL64 -#define SCMP_ARCH_MIPSEL64 ARCH_BAD -#endif - -#ifndef SCMP_ARCH_MIPSEL64N32 -#define SCMP_ARCH_MIPSEL64N32 ARCH_BAD -#endif - -#ifndef SCMP_ARCH_PPC -#define SCMP_ARCH_PPC ARCH_BAD -#endif - -#ifndef SCMP_ARCH_PPC64 -#define SCMP_ARCH_PPC64 ARCH_BAD -#endif - -#ifndef SCMP_ARCH_PPC64LE -#define SCMP_ARCH_PPC64LE ARCH_BAD -#endif - -#ifndef SCMP_ARCH_S390 -#define SCMP_ARCH_S390 ARCH_BAD -#endif - -#ifndef SCMP_ARCH_S390X -#define SCMP_ARCH_S390X ARCH_BAD -#endif - -const uint32_t C_ARCH_NATIVE = SCMP_ARCH_NATIVE; -const uint32_t C_ARCH_X86 = SCMP_ARCH_X86; -const uint32_t C_ARCH_X86_64 = SCMP_ARCH_X86_64; -const uint32_t C_ARCH_X32 = SCMP_ARCH_X32; -const uint32_t C_ARCH_ARM = SCMP_ARCH_ARM; -const uint32_t C_ARCH_AARCH64 = SCMP_ARCH_AARCH64; -const uint32_t C_ARCH_MIPS = SCMP_ARCH_MIPS; -const uint32_t C_ARCH_MIPS64 = SCMP_ARCH_MIPS64; -const uint32_t C_ARCH_MIPS64N32 = SCMP_ARCH_MIPS64N32; -const uint32_t C_ARCH_MIPSEL = SCMP_ARCH_MIPSEL; -const uint32_t C_ARCH_MIPSEL64 = SCMP_ARCH_MIPSEL64; -const uint32_t C_ARCH_MIPSEL64N32 = SCMP_ARCH_MIPSEL64N32; -const uint32_t C_ARCH_PPC = SCMP_ARCH_PPC; -const uint32_t C_ARCH_PPC64 = SCMP_ARCH_PPC64; -const uint32_t C_ARCH_PPC64LE = SCMP_ARCH_PPC64LE; -const uint32_t C_ARCH_S390 = SCMP_ARCH_S390; -const uint32_t C_ARCH_S390X = SCMP_ARCH_S390X; - -const uint32_t C_ACT_KILL = SCMP_ACT_KILL; -const uint32_t C_ACT_TRAP = SCMP_ACT_TRAP; -const uint32_t C_ACT_ERRNO = SCMP_ACT_ERRNO(0); -const uint32_t C_ACT_TRACE = SCMP_ACT_TRACE(0); -const uint32_t C_ACT_ALLOW = SCMP_ACT_ALLOW; - -// If TSync is not supported, make sure it doesn't map to a supported filter attribute -// Don't worry about major version < 2, the minimum version checks should catch that case -#if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 2 -#define SCMP_FLTATR_CTL_TSYNC _SCMP_CMP_MIN -#endif - -const uint32_t C_ATTRIBUTE_DEFAULT = (uint32_t)SCMP_FLTATR_ACT_DEFAULT; -const uint32_t C_ATTRIBUTE_BADARCH = (uint32_t)SCMP_FLTATR_ACT_BADARCH; -const uint32_t C_ATTRIBUTE_NNP = (uint32_t)SCMP_FLTATR_CTL_NNP; -const uint32_t C_ATTRIBUTE_TSYNC = (uint32_t)SCMP_FLTATR_CTL_TSYNC; - -const int C_CMP_NE = (int)SCMP_CMP_NE; -const int C_CMP_LT = (int)SCMP_CMP_LT; -const int C_CMP_LE = (int)SCMP_CMP_LE; -const int C_CMP_EQ = (int)SCMP_CMP_EQ; -const int C_CMP_GE = (int)SCMP_CMP_GE; -const int C_CMP_GT = (int)SCMP_CMP_GT; -const int C_CMP_MASKED_EQ = (int)SCMP_CMP_MASKED_EQ; - -const int C_VERSION_MAJOR = SCMP_VER_MAJOR; -const int C_VERSION_MINOR = SCMP_VER_MINOR; -const int C_VERSION_MICRO = SCMP_VER_MICRO; - -typedef struct scmp_arg_cmp* scmp_cast_t; - -// Wrapper to create an scmp_arg_cmp struct -void* -make_struct_arg_cmp( - unsigned int arg, - int compare, - uint64_t a, - uint64_t b - ) -{ - struct scmp_arg_cmp *s = malloc(sizeof(struct scmp_arg_cmp)); - - s->arg = arg; - s->op = compare; - s->datum_a = a; - s->datum_b = b; - - return s; -} -*/ -import "C" - -// Nonexported types -type scmpFilterAttr uint32 - -// Nonexported constants - -const ( - filterAttrActDefault scmpFilterAttr = iota - filterAttrActBadArch scmpFilterAttr = iota - filterAttrNNP scmpFilterAttr = iota - filterAttrTsync scmpFilterAttr = iota -) - -const ( - // An error return from certain libseccomp functions - scmpError C.int = -1 - // Comparison boundaries to check for architecture validity - archStart ScmpArch = ArchNative - archEnd ScmpArch = ArchS390X - // Comparison boundaries to check for action validity - actionStart ScmpAction = ActKill - actionEnd ScmpAction = ActAllow - // Comparison boundaries to check for comparison operator validity - compareOpStart ScmpCompareOp = CompareNotEqual - compareOpEnd ScmpCompareOp = CompareMaskedEqual -) - -var ( - // Error thrown on bad filter context - errBadFilter = fmt.Errorf("filter is invalid or uninitialized") - // Constants representing library major, minor, and micro versions - verMajor = int(C.C_VERSION_MAJOR) - verMinor = int(C.C_VERSION_MINOR) - verMicro = int(C.C_VERSION_MICRO) -) - -// Nonexported functions - -// Check if library version is greater than or equal to the given one -func checkVersionAbove(major, minor, micro int) bool { - return (verMajor > major) || - (verMajor == major && verMinor > minor) || - (verMajor == major && verMinor == minor && verMicro >= micro) -} - -// Init function: Verify library version is appropriate -func init() { - if !checkVersionAbove(2, 1, 0) { - fmt.Fprintf(os.Stderr, "Libseccomp version too low: minimum supported is 2.1.0, detected %d.%d.%d", C.C_VERSION_MAJOR, C.C_VERSION_MINOR, C.C_VERSION_MICRO) - os.Exit(-1) - } -} - -// Filter helpers - -// Filter finalizer - ensure that kernel context for filters is freed -func filterFinalizer(f *ScmpFilter) { - f.Release() -} - -// Get a raw filter attribute -func (f *ScmpFilter) getFilterAttr(attr scmpFilterAttr) (C.uint32_t, error) { - f.lock.Lock() - defer f.lock.Unlock() - - if !f.valid { - return 0x0, errBadFilter - } - - if !checkVersionAbove(2, 2, 0) && attr == filterAttrTsync { - return 0x0, fmt.Errorf("the thread synchronization attribute is not supported in this version of the library") - } - - var attribute C.uint32_t - - retCode := C.seccomp_attr_get(f.filterCtx, attr.toNative(), &attribute) - if retCode != 0 { - return 0x0, syscall.Errno(-1 * retCode) - } - - return attribute, nil -} - -// Set a raw filter attribute -func (f *ScmpFilter) setFilterAttr(attr scmpFilterAttr, value C.uint32_t) error { - f.lock.Lock() - defer f.lock.Unlock() - - if !f.valid { - return errBadFilter - } - - if !checkVersionAbove(2, 2, 0) && attr == filterAttrTsync { - return fmt.Errorf("the thread synchronization attribute is not supported in this version of the library") - } - - retCode := C.seccomp_attr_set(f.filterCtx, attr.toNative(), value) - if retCode != 0 { - return syscall.Errno(-1 * retCode) - } - - return nil -} - -// DOES NOT LOCK OR CHECK VALIDITY -// Assumes caller has already done this -// Wrapper for seccomp_rule_add_... functions -func (f *ScmpFilter) addRuleWrapper(call ScmpSyscall, action ScmpAction, exact bool, cond C.scmp_cast_t) error { - var length C.uint - if cond != nil { - length = 1 - } else { - length = 0 - } - - var retCode C.int - if exact { - retCode = C.seccomp_rule_add_exact_array(f.filterCtx, action.toNative(), C.int(call), length, cond) - } else { - retCode = C.seccomp_rule_add_array(f.filterCtx, action.toNative(), C.int(call), length, cond) - } - - if syscall.Errno(-1*retCode) == syscall.EFAULT { - return fmt.Errorf("unrecognized syscall") - } else if syscall.Errno(-1*retCode) == syscall.EPERM { - return fmt.Errorf("requested action matches default action of filter") - } else if retCode != 0 { - return syscall.Errno(-1 * retCode) - } - - return nil -} - -// Generic add function for filter rules -func (f *ScmpFilter) addRuleGeneric(call ScmpSyscall, action ScmpAction, exact bool, conds []ScmpCondition) error { - f.lock.Lock() - defer f.lock.Unlock() - - if !f.valid { - return errBadFilter - } - - if len(conds) == 0 { - if err := f.addRuleWrapper(call, action, exact, nil); err != nil { - return err - } - } else { - // We don't support conditional filtering in library version v2.1 - if !checkVersionAbove(2, 2, 1) { - return fmt.Errorf("conditional filtering requires libseccomp version >= 2.2.1") - } - - for _, cond := range conds { - cmpStruct := C.make_struct_arg_cmp(C.uint(cond.Argument), cond.Op.toNative(), C.uint64_t(cond.Operand1), C.uint64_t(cond.Operand2)) - defer C.free(cmpStruct) - - if err := f.addRuleWrapper(call, action, exact, C.scmp_cast_t(cmpStruct)); err != nil { - return err - } - } - } - - return nil -} - -// Generic Helpers - -// Helper - Sanitize Arch token input -func sanitizeArch(in ScmpArch) error { - if in < archStart || in > archEnd { - return fmt.Errorf("unrecognized architecture") - } - - if in.toNative() == C.C_ARCH_BAD { - return fmt.Errorf("architecture is not supported on this version of the library") - } - - return nil -} - -func sanitizeAction(in ScmpAction) error { - inTmp := in & 0x0000FFFF - if inTmp < actionStart || inTmp > actionEnd { - return fmt.Errorf("unrecognized action") - } - - if inTmp != ActTrace && inTmp != ActErrno && (in&0xFFFF0000) != 0 { - return fmt.Errorf("highest 16 bits must be zeroed except for Trace and Errno") - } - - return nil -} - -func sanitizeCompareOp(in ScmpCompareOp) error { - if in < compareOpStart || in > compareOpEnd { - return fmt.Errorf("unrecognized comparison operator") - } - - return nil -} - -func archFromNative(a C.uint32_t) (ScmpArch, error) { - switch a { - case C.C_ARCH_X86: - return ArchX86, nil - case C.C_ARCH_X86_64: - return ArchAMD64, nil - case C.C_ARCH_X32: - return ArchX32, nil - case C.C_ARCH_ARM: - return ArchARM, nil - case C.C_ARCH_NATIVE: - return ArchNative, nil - case C.C_ARCH_AARCH64: - return ArchARM64, nil - case C.C_ARCH_MIPS: - return ArchMIPS, nil - case C.C_ARCH_MIPS64: - return ArchMIPS64, nil - case C.C_ARCH_MIPS64N32: - return ArchMIPS64N32, nil - case C.C_ARCH_MIPSEL: - return ArchMIPSEL, nil - case C.C_ARCH_MIPSEL64: - return ArchMIPSEL64, nil - case C.C_ARCH_MIPSEL64N32: - return ArchMIPSEL64N32, nil - case C.C_ARCH_PPC: - return ArchPPC, nil - case C.C_ARCH_PPC64: - return ArchPPC64, nil - case C.C_ARCH_PPC64LE: - return ArchPPC64LE, nil - case C.C_ARCH_S390: - return ArchS390, nil - case C.C_ARCH_S390X: - return ArchS390X, nil - default: - return 0x0, fmt.Errorf("unrecognized architecture") - } -} - -// Only use with sanitized arches, no error handling -func (a ScmpArch) toNative() C.uint32_t { - switch a { - case ArchX86: - return C.C_ARCH_X86 - case ArchAMD64: - return C.C_ARCH_X86_64 - case ArchX32: - return C.C_ARCH_X32 - case ArchARM: - return C.C_ARCH_ARM - case ArchARM64: - return C.C_ARCH_AARCH64 - case ArchMIPS: - return C.C_ARCH_MIPS - case ArchMIPS64: - return C.C_ARCH_MIPS64 - case ArchMIPS64N32: - return C.C_ARCH_MIPS64N32 - case ArchMIPSEL: - return C.C_ARCH_MIPSEL - case ArchMIPSEL64: - return C.C_ARCH_MIPSEL64 - case ArchMIPSEL64N32: - return C.C_ARCH_MIPSEL64N32 - case ArchPPC: - return C.C_ARCH_PPC - case ArchPPC64: - return C.C_ARCH_PPC64 - case ArchPPC64LE: - return C.C_ARCH_PPC64LE - case ArchS390: - return C.C_ARCH_S390 - case ArchS390X: - return C.C_ARCH_S390X - case ArchNative: - return C.C_ARCH_NATIVE - default: - return 0x0 - } -} - -// Only use with sanitized ops, no error handling -func (a ScmpCompareOp) toNative() C.int { - switch a { - case CompareNotEqual: - return C.C_CMP_NE - case CompareLess: - return C.C_CMP_LT - case CompareLessOrEqual: - return C.C_CMP_LE - case CompareEqual: - return C.C_CMP_EQ - case CompareGreaterEqual: - return C.C_CMP_GE - case CompareGreater: - return C.C_CMP_GT - case CompareMaskedEqual: - return C.C_CMP_MASKED_EQ - default: - return 0x0 - } -} - -func actionFromNative(a C.uint32_t) (ScmpAction, error) { - aTmp := a & 0xFFFF - switch a & 0xFFFF0000 { - case C.C_ACT_KILL: - return ActKill, nil - case C.C_ACT_TRAP: - return ActTrap, nil - case C.C_ACT_ERRNO: - return ActErrno.SetReturnCode(int16(aTmp)), nil - case C.C_ACT_TRACE: - return ActTrace.SetReturnCode(int16(aTmp)), nil - case C.C_ACT_ALLOW: - return ActAllow, nil - default: - return 0x0, fmt.Errorf("unrecognized action") - } -} - -// Only use with sanitized actions, no error handling -func (a ScmpAction) toNative() C.uint32_t { - switch a & 0xFFFF { - case ActKill: - return C.C_ACT_KILL - case ActTrap: - return C.C_ACT_TRAP - case ActErrno: - return C.C_ACT_ERRNO | (C.uint32_t(a) >> 16) - case ActTrace: - return C.C_ACT_TRACE | (C.uint32_t(a) >> 16) - case ActAllow: - return C.C_ACT_ALLOW - default: - return 0x0 - } -} - -// Internal only, assumes safe attribute -func (a scmpFilterAttr) toNative() uint32 { - switch a { - case filterAttrActDefault: - return uint32(C.C_ATTRIBUTE_DEFAULT) - case filterAttrActBadArch: - return uint32(C.C_ATTRIBUTE_BADARCH) - case filterAttrNNP: - return uint32(C.C_ATTRIBUTE_NNP) - case filterAttrTsync: - return uint32(C.C_ATTRIBUTE_TSYNC) - default: - return 0x0 - } -} diff --git a/vendor/src/github.com/seccomp/libseccomp-golang/seccomp_test.go b/vendor/src/github.com/seccomp/libseccomp-golang/seccomp_test.go deleted file mode 100644 index b3a49d2..0000000 --- a/vendor/src/github.com/seccomp/libseccomp-golang/seccomp_test.go +++ /dev/null @@ -1,457 +0,0 @@ -// +build linux - -// Tests for public API of libseccomp Go bindings - -package seccomp - -import ( - "fmt" - "syscall" - "testing" -) - -// Type Function Tests - -func TestActionSetReturnCode(t *testing.T) { - if ActInvalid.SetReturnCode(0x0010) != ActInvalid { - t.Errorf("Able to set a return code on invalid action!") - } - - codeSet := ActErrno.SetReturnCode(0x0001) - if codeSet == ActErrno || codeSet.GetReturnCode() != 0x0001 { - t.Errorf("Could not set return code on ActErrno") - } -} - -func TestSyscallGetName(t *testing.T) { - call1 := ScmpSyscall(0x1) - callFail := ScmpSyscall(0x999) - - name, err := call1.GetName() - if err != nil { - t.Errorf("Error getting syscall name for number 0x1") - } else if len(name) == 0 { - t.Errorf("Empty name returned for syscall 0x1") - } - fmt.Printf("Got name of syscall 0x1 on native arch as %s\n", name) - - _, err = callFail.GetName() - if err == nil { - t.Errorf("Getting nonexistant syscall should error!") - } -} - -func TestSyscallGetNameByArch(t *testing.T) { - call1 := ScmpSyscall(0x1) - callInvalid := ScmpSyscall(0x999) - archGood := ArchAMD64 - archBad := ArchInvalid - - name, err := call1.GetNameByArch(archGood) - if err != nil { - t.Errorf("Error getting syscall name for number 0x1 and arch AMD64") - } else if name != "write" { - t.Errorf("Got incorrect name for syscall 0x1 - expected write, got %s", name) - } - - _, err = call1.GetNameByArch(archBad) - if err == nil { - t.Errorf("Bad architecture GetNameByArch() should error!") - } - - _, err = callInvalid.GetNameByArch(archGood) - if err == nil { - t.Errorf("Bad syscall GetNameByArch() should error!") - } - - _, err = callInvalid.GetNameByArch(archBad) - if err == nil { - t.Errorf("Bad syscall and bad arch GetNameByArch() should error!") - } -} - -func TestGetSyscallFromName(t *testing.T) { - name1 := "write" - nameInval := "NOTASYSCALL" - - syscall, err := GetSyscallFromName(name1) - if err != nil { - t.Errorf("Error getting syscall number of write: %s", err) - } - fmt.Printf("Got syscall number of write on native arch as %d\n", syscall) - - _, err = GetSyscallFromName(nameInval) - if err == nil { - t.Errorf("Getting an invalid syscall should error!") - } -} - -func TestGetSyscallFromNameByArch(t *testing.T) { - name1 := "write" - nameInval := "NOTASYSCALL" - arch1 := ArchAMD64 - archInval := ArchInvalid - - syscall, err := GetSyscallFromNameByArch(name1, arch1) - if err != nil { - t.Errorf("Error getting syscall number of write on AMD64: %s", err) - } - fmt.Printf("Got syscall number of write on AMD64 as %d\n", syscall) - - _, err = GetSyscallFromNameByArch(nameInval, arch1) - if err == nil { - t.Errorf("Getting invalid syscall with valid arch should error") - } - - _, err = GetSyscallFromNameByArch(name1, archInval) - if err == nil { - t.Errorf("Getting valid syscall for invalid arch should error") - } - - _, err = GetSyscallFromNameByArch(nameInval, archInval) - if err == nil { - t.Errorf("Getting invalid syscall for invalid arch should error") - } -} - -func TestMakeCondition(t *testing.T) { - condition, err := MakeCondition(3, CompareNotEqual, 0x10) - if err != nil { - t.Errorf("Error making condition struct: %s", err) - } else if condition.Argument != 3 || condition.Operand1 != 0x10 || - condition.Operand2 != 0 || condition.Op != CompareNotEqual { - t.Errorf("Condition struct was filled incorrectly") - } - - condition, err = MakeCondition(3, CompareMaskedEqual, 0x10, 0x20) - if err != nil { - t.Errorf("Error making condition struct: %s", err) - } else if condition.Argument != 3 || condition.Operand1 != 0x10 || - condition.Operand2 != 0x20 || condition.Op != CompareMaskedEqual { - t.Errorf("Condition struct was filled incorrectly") - } - - _, err = MakeCondition(7, CompareNotEqual, 0x10) - if err == nil { - t.Errorf("Condition struct with bad syscall argument number should error") - } - - _, err = MakeCondition(3, CompareInvalid, 0x10) - if err == nil { - t.Errorf("Condition struct with bad comparison operator should error") - } - - _, err = MakeCondition(3, CompareMaskedEqual, 0x10, 0x20, 0x30) - if err == nil { - t.Errorf("MakeCondition with more than 2 arguments should fail") - } - - _, err = MakeCondition(3, CompareMaskedEqual) - if err == nil { - t.Errorf("MakeCondition with no arguments should fail") - } -} - -// Utility Function Tests - -func TestGetNativeArch(t *testing.T) { - arch, err := GetNativeArch() - if err != nil { - t.Errorf("GetNativeArch should not error!") - } - fmt.Printf("Got native arch of system as %s\n", arch.String()) -} - -// Filter Tests - -func TestFilterCreateRelease(t *testing.T) { - _, err := NewFilter(ActInvalid) - if err == nil { - t.Errorf("Can create filter with invalid action") - } - - filter, err := NewFilter(ActKill) - if err != nil { - t.Errorf("Error creating filter: %s", err) - } - - if !filter.IsValid() { - t.Errorf("Filter created by NewFilter was not valid") - } - - filter.Release() - - if filter.IsValid() { - t.Errorf("Filter is valid after being released") - } -} - -func TestFilterReset(t *testing.T) { - filter, err := NewFilter(ActKill) - if err != nil { - t.Errorf("Error creating filter: %s", err) - } - defer filter.Release() - - // Ensure the default action is ActKill - action, err := filter.GetDefaultAction() - if err != nil { - t.Errorf("Error getting default action of filter") - } else if action != ActKill { - t.Errorf("Default action of filter was set incorrectly!") - } - - // Reset with a different default action - err = filter.Reset(ActAllow) - if err != nil { - t.Errorf("Error resetting filter!") - } - - valid := filter.IsValid() - if !valid { - t.Errorf("Filter is no longer valid after reset!") - } - - // The default action should no longer be ActKill - action, err = filter.GetDefaultAction() - if err != nil { - t.Errorf("Error getting default action of filter") - } else if action != ActAllow { - t.Errorf("Default action of filter was set incorrectly!") - } -} - -func TestFilterArchFunctions(t *testing.T) { - filter, err := NewFilter(ActKill) - if err != nil { - t.Errorf("Error creating filter: %s", err) - } - defer filter.Release() - - arch, err := GetNativeArch() - if err != nil { - t.Errorf("Error getting native architecture: %s", err) - } - - present, err := filter.IsArchPresent(arch) - if err != nil { - t.Errorf("Error retrieving arch from filter: %s", err) - } else if !present { - t.Errorf("Filter does not contain native architecture by default") - } - - // Adding the native arch again should succeed, as it's already present - err = filter.AddArch(arch) - if err != nil { - t.Errorf("Adding arch to filter already containing it should succeed") - } - - // Make sure we don't add the native arch again - prospectiveArch := ArchX86 - if arch == ArchX86 { - prospectiveArch = ArchAMD64 - } - - // Check to make sure this other arch isn't in the filter - present, err = filter.IsArchPresent(prospectiveArch) - if err != nil { - t.Errorf("Error retrieving arch from filter: %s", err) - } else if present { - t.Errorf("Arch not added to filter is present") - } - - // Try removing the nonexistant arch - should succeed - err = filter.RemoveArch(prospectiveArch) - if err != nil { - t.Errorf("Error removing nonexistant arch: %s", err) - } - - // Add an arch, see if it's in the filter - err = filter.AddArch(prospectiveArch) - if err != nil { - t.Errorf("Could not add arch %s to filter: %s", - prospectiveArch.String(), err) - } - - present, err = filter.IsArchPresent(prospectiveArch) - if err != nil { - t.Errorf("Error retrieving arch from filter: %s", err) - } else if !present { - t.Errorf("Filter does not contain architecture %s after it was added", - prospectiveArch.String()) - } - - // Remove the arch again, make sure it's not in the filter - err = filter.RemoveArch(prospectiveArch) - if err != nil { - t.Errorf("Could not remove arch %s from filter: %s", - prospectiveArch.String(), err) - } - - present, err = filter.IsArchPresent(prospectiveArch) - if err != nil { - t.Errorf("Error retrieving arch from filter: %s", err) - } else if present { - t.Errorf("Filter contains architecture %s after it was removed", - prospectiveArch.String()) - } -} - -func TestFilterAttributeGettersAndSetters(t *testing.T) { - filter, err := NewFilter(ActKill) - if err != nil { - t.Errorf("Error creating filter: %s", err) - } - defer filter.Release() - - act, err := filter.GetDefaultAction() - if err != nil { - t.Errorf("Error getting default action: %s", err) - } else if act != ActKill { - t.Errorf("Default action was set incorrectly") - } - - err = filter.SetBadArchAction(ActAllow) - if err != nil { - t.Errorf("Error setting bad arch action: %s", err) - } - - act, err = filter.GetBadArchAction() - if err != nil { - t.Errorf("Error getting bad arch action") - } else if act != ActAllow { - t.Errorf("Bad arch action was not set correcly!") - } - - err = filter.SetNoNewPrivsBit(false) - if err != nil { - t.Errorf("Error setting no new privileges bit") - } - - privs, err := filter.GetNoNewPrivsBit() - if err != nil { - t.Errorf("Error getting no new privileges bit!") - } else if privs != false { - t.Errorf("No new privileges bit was not set correctly") - } - - err = filter.SetBadArchAction(ActInvalid) - if err == nil { - t.Errorf("Setting bad arch action to an invalid action should error") - } -} - -func TestMergeFilters(t *testing.T) { - filter1, err := NewFilter(ActAllow) - if err != nil { - t.Errorf("Error creating filter: %s", err) - } - - filter2, err := NewFilter(ActAllow) - if err != nil { - t.Errorf("Error creating filter: %s", err) - } - - // Need to remove the native arch and add another to the second filter - // Filters must NOT share architectures to be successfully merged - nativeArch, err := GetNativeArch() - if err != nil { - t.Errorf("Error getting native arch: %s", err) - } - - prospectiveArch := ArchAMD64 - if nativeArch == ArchAMD64 { - prospectiveArch = ArchX86 - } - - err = filter2.AddArch(prospectiveArch) - if err != nil { - t.Errorf("Error adding architecture to filter: %s", err) - } - - err = filter2.RemoveArch(nativeArch) - if err != nil { - t.Errorf("Error removing architecture from filter: %s", err) - } - - err = filter1.Merge(filter2) - if err != nil { - t.Errorf("Error merging filters: %s", err) - } - - if filter2.IsValid() { - t.Errorf("Source filter should not be valid after merging") - } - - filter3, err := NewFilter(ActKill) - if err != nil { - t.Errorf("Error creating filter: %s", err) - } - defer filter3.Release() - - err = filter1.Merge(filter3) - if err == nil { - t.Errorf("Attributes should have to match to merge filters") - } -} - -func TestRuleAddAndLoad(t *testing.T) { - // Test #1: Add a trivial filter - filter1, err := NewFilter(ActAllow) - if err != nil { - t.Errorf("Error creating filter: %s", err) - } - defer filter1.Release() - - call, err := GetSyscallFromName("getpid") - if err != nil { - t.Errorf("Error getting syscall number of getpid: %s", err) - } - - call2, err := GetSyscallFromName("setreuid") - if err != nil { - t.Errorf("Error getting syscall number of setreuid: %s", err) - } - - uid := syscall.Getuid() - euid := syscall.Geteuid() - - err = filter1.AddRule(call, ActErrno.SetReturnCode(0x1)) - if err != nil { - t.Errorf("Error adding rule to restrict syscall: %s", err) - } - - cond, err := MakeCondition(1, CompareEqual, uint64(euid)) - if err != nil { - t.Errorf("Error making rule to restrict syscall: %s", err) - } - - cond2, err := MakeCondition(0, CompareEqual, uint64(uid)) - if err != nil { - t.Errorf("Error making rule to restrict syscall: %s", err) - } - - conditions := []ScmpCondition{cond, cond2} - - err = filter1.AddRuleConditional(call2, ActErrno.SetReturnCode(0x2), conditions) - if err != nil { - t.Errorf("Error adding conditional rule: %s", err) - } - - err = filter1.Load() - if err != nil { - t.Errorf("Error loading filter: %s", err) - } - - // Try making a simple syscall, it should error - pid := syscall.Getpid() - if pid != -1 { - t.Errorf("Syscall should have returned error code!") - } - - // Try making a Geteuid syscall that should normally succeed - err = syscall.Setreuid(uid, euid) - if err != syscall.Errno(2) { - t.Errorf("Syscall should have returned error code!") - } -}
tor-commits@lists.torproject.org