[tor-commits] [sandboxed-tor-browser/master] Switch to pre-compiling the seccomp BPF filters at compile time.

yawning at torproject.org yawning at torproject.org
Mon Dec 5 01:28:46 UTC 2016


commit 7d5a4c04a1df365bef19c9dfedd0713d3d0cd5b0
Author: Yawning Angel <yawning at schwanenlied.me>
Date:   Mon Dec 5 01:13:06 2016 +0000

    Switch to pre-compiling the seccomp BPF filters at compile time.
    
    libseccomp2 didn't get a wau to enumerate library version at runtime
    till farily recently, and Debian stable ships something older than
    the minimum required version.
    
    As long as the box that builds the binary has new enough libseccomp,
    this will do the Right Thing(TM).
---
 .gitignore                                         |   1 +
 Makefile                                           |  10 +-
 README.md                                          |   2 +-
 src/cmd/gen-seccomp/main.go                        |  90 ++++
 src/cmd/gen-seccomp/seccomp.go                     | 128 +++++
 src/cmd/gen-seccomp/seccomp_firefox.go             | 249 ++++++++++
 src/cmd/gen-seccomp/seccomp_tor.go                 | 535 ++++++++++++++++++++
 .../internal/sandbox/seccomp.go                    | 113 +----
 .../internal/sandbox/seccomp_firefox.go            | 250 ----------
 .../internal/sandbox/seccomp_tor.go                | 536 ---------------------
 10 files changed, 1026 insertions(+), 888 deletions(-)

diff --git a/.gitignore b/.gitignore
index 1bd17ca..8fc2492 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 bin/
 pkg/
 data/tbb_stub.so
+data/*.bpf
 src/cmd/sandboxed-tor-browser/internal/data/bindata.go
 *.swp
 *~
diff --git a/Makefile b/Makefile
index d50ed55..edbfa4c 100644
--- a/Makefile
+++ b/Makefile
@@ -6,10 +6,11 @@ GTK3TAG := gtk_3_14
 all: sandboxed-tor-browser
 
 sandboxed-tor-browser: static-assets
-	gb build -tags $(GTK3TAG)
+	gb build -tags $(GTK3TAG) cmd/sandboxed-tor-browser
 	mv ./bin/sandboxed-tor-browser-$(GTK3TAG) ./bin/sandboxed-tor-browser
 
-static-assets: go-bindata tbb_stub
+static-assets: go-bindata gen-seccomp tbb_stub
+	./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
@@ -17,10 +18,13 @@ tbb_stub: go-bindata
 
 go-bindata:
 	gb build github.com/jteeuwen/go-bindata/go-bindata
-	mkdir -p data
+
+gen-seccomp:
+	gb build cmd/gen-seccomp
 
 clean:
 	rm -f ./src/cmd/sandboxed-tor-browser/internal/data/bindata.go
 	rm -f ./data/tbb_stub.so
+	rm -f ./data/*.bpf
 	rm -Rf ./bin
 	rm -Rf ./pkg
diff --git a/README.md b/README.md
index 82e4ac6..18e26f9 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,6 @@ Runtime dependencies:
 
  * A modern Linux system on x86/x86_64 architecture.
  * bubblewrap >= 0.1.3 (https://github.com/projectatomic/bubblewrap).
- * libseccomp2 >= 2.2.1
  * Gtk+ >= 3.14.0
  * (Optional) PulseAudio
 
@@ -28,6 +27,7 @@ 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
 
 Things that the sandbox breaks:
 
diff --git a/src/cmd/gen-seccomp/main.go b/src/cmd/gen-seccomp/main.go
new file mode 100644
index 0000000..3ad1be5
--- /dev/null
+++ b/src/cmd/gen-seccomp/main.go
@@ -0,0 +1,90 @@
+// 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)
+	}
+
+	log.Printf("outDir: %v", outDir)
+
+	// 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, 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, false); 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, false); err != nil {
+		log.Fatalf("failed to create firefox amd64 profile: %v", err)
+	}
+
+	// Tor Browser (386)
+	f, err = os.Create(filepath.Join(outDir, "tor-386.bpf"))
+	if err != nil {
+		log.Fatalf("failed to create output: %v", err)
+	}
+	if err = compileTorSeccompProfile(f, false, true); err != nil {
+		log.Fatalf("failed to create tor 386 profile: %v", err)
+	}
+
+	// Tor Browser + obfs4proxy (386)
+	f, err = os.Create(filepath.Join(outDir, "tor-obfs4-386.bpf"))
+	if err != nil {
+		log.Fatalf("failed to create output: %v", err)
+	}
+	if err = compileTorSeccompProfile(f, true, true); err != nil {
+		log.Fatalf("failed to create tor-obfs4 386 profile: %v", err)
+	}
+
+	// Firefox (386)
+	f, err = os.Create(filepath.Join(outDir, "torbrowser-386.bpf"))
+	if err != nil {
+		log.Fatalf("failed to create output: %v", err)
+	}
+	if err = compileTorBrowserSeccompProfile(f, true); err != nil {
+		log.Fatalf("failed to create firefox 386 profile: %v", err)
+	}
+}
diff --git a/src/cmd/gen-seccomp/seccomp.go b/src/cmd/gen-seccomp/seccomp.go
new file mode 100644
index 0000000..6c7366f
--- /dev/null
+++ b/src/cmd/gen-seccomp/seccomp.go
@@ -0,0 +1,128 @@
+// 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 (
+	"log"
+
+	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(is386 bool) (*seccomp.ScmpFilter, error) {
+	arch := seccomp.ArchAMD64
+	if is386 {
+		arch = seccomp.ArchX86
+	}
+
+	actENOSYS := seccomp.ActErrno.SetReturnCode(38)
+	f, err := seccomp.NewFilter(actENOSYS)
+	if err != nil {
+		return nil, err
+	}
+
+	if err = f.AddArch(arch); err != nil {
+		f.Release()
+		return nil, err
+	}
+
+	return f, nil
+}
+
+func allowSyscalls(f *seccomp.ScmpFilter, calls []string, is386 bool) error {
+	for _, scallName := range calls {
+		scall, err := seccomp.GetSyscallFromName(scallName)
+		if err != nil {
+			if is386 && scallName == "newselect" {
+				scall = seccomp.ScmpSyscall(142)
+			} else {
+				log.Printf("seccomp: unknown system call: %v", scallName)
+				continue
+			}
+		}
+		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 {
+		log.Printf("seccomp: unknown system call: %v", scallName)
+		return nil
+	}
+
+	// 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
new file mode 100644
index 0000000..465d8fe
--- /dev/null
+++ b/src/cmd/gen-seccomp/seccomp_firefox.go
@@ -0,0 +1,249 @@
+// 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, is386 bool) error {
+	defer fd.Close()
+
+	f, err := newWhitelist(is386)
+	if err != nil {
+		return err
+	}
+	defer f.Release()
+
+	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",
+		"mlock",
+		"mprotect",
+		"mremap",
+		"munmap",
+
+		// XXX: Remove these?
+		"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",
+		"setrlimit",
+
+		"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",
+
+		// 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",
+	}
+	if is386 {
+		allowedNoArgs386 := []string{
+			"fadvise64_64",
+			"fcntl64",
+			"fstat64",
+			"fstatfs64",
+			"ftruncate64",
+			"lstat64",
+			"stat64",
+			"statfs64",
+			"_llseek",
+
+			"mmap2",
+			"prlimit64",
+			"ugetrlimit",
+			"set_thread_area",
+			"waitpid",
+
+			"getgid32",
+			"getuid32",
+			"getresgid32",
+			"getresuid32",
+
+			"recv",
+			"send",
+			"newselect",
+			"socketcall",
+
+			"socket", // Filtered on amd64.
+		}
+		allowedNoArgs = append(allowedNoArgs, allowedNoArgs386...)
+	}
+	if err = allowSyscalls(f, allowedNoArgs, is386); 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); 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 is386 {
+		if err = allowCmpEq(f, "time", 0, 0); err != nil {
+			return err
+		}
+	} else {
+		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
new file mode 100644
index 0000000..73ed3c0
--- /dev/null
+++ b/src/cmd/gen-seccomp/seccomp_tor.go
@@ -0,0 +1,535 @@
+// 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"
+)
+
+func compileTorSeccompProfile(fd *os.File, useBridges bool, is386 bool) error {
+	defer fd.Close()
+
+	f, err := newWhitelist(is386)
+	if err != nil {
+		return err
+	}
+	defer f.Release()
+
+	allowedNoArgs := []string{
+		"access",
+		"brk",
+		"clock_gettime",
+		"close",
+		"clone",
+		"epoll_create",
+		"epoll_wait",
+		"eventfd2",
+		"pipe2",
+		"pipe",
+		"fcntl",
+		"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",
+
+		// XXX: Calls that should be filtered by arg, but aren't yet.
+		"rt_sigaction",
+		"accept4",
+
+		// 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",
+	}
+	if is386 {
+		allowedNoArgs386 := []string{
+			"fstat64",
+			"getegid32",
+			"geteuid32",
+			"getgid32",
+			"getuid32",
+			"_llseek",
+			"sigreturn",
+			"fcntl64", // XXX: Filter by arg.
+
+			"recv",
+			"send",
+			"stat64",
+			"socketcall", // Sigh... (see accept4 in the tor code)
+
+			"ugetrlimit",
+			"set_thread_area",
+		}
+		allowedNoArgs = append(allowedNoArgs, allowedNoArgs386...)
+	}
+	if err = allowSyscalls(f, allowedNoArgs, is386); 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 = torFilterPoll(f); err != nil {
+		return err
+	}
+	if err = torFilterSocket(f, is386); err != nil {
+		return err
+	}
+	if err = torFilterSetsockopt(f, is386); err != nil {
+		return err
+	}
+	if err = torFilterGetsockopt(f, is386); err != nil {
+		return err
+	}
+	if err = torFilterSocketpair(f, is386); err != nil {
+		return err
+	}
+	if err = torFilterMmap(f, is386); 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 is386 {
+			obfsCalls = append(obfsCalls, "newselect")
+		}
+		if err = allowSyscalls(f, obfsCalls, is386); 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, is386); err != nil {
+			return err
+		}
+		if err = obfsFilterMmap(f, is386); 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 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, is386 bool) error {
+	scall, err := seccomp.GetSyscallFromName("socket")
+	if err != nil {
+		return err
+	}
+	if is386 {
+		return f.AddRule(scall, seccomp.ActAllow)
+	}
+
+	// XXX: Tighten this some more.
+	return allowCmpEq(f, "socket", 0, syscall.AF_UNIX, syscall.AF_INET, syscall.AF_INET6, syscall.AF_NETLINK)
+}
+
+func torFilterSetsockopt(f *seccomp.ScmpFilter, is386 bool) error {
+	scall, err := seccomp.GetSyscallFromName("setsockopt")
+	if err != nil {
+		return err
+	}
+	if is386 {
+		return f.AddRule(scall, seccomp.ActAllow)
+	}
+
+	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, is386 bool) error {
+	scall, err := seccomp.GetSyscallFromName("getsockopt")
+	if err != nil {
+		return err
+	}
+	if is386 {
+		return f.AddRule(scall, seccomp.ActAllow)
+	}
+
+	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, is386 bool) error {
+	scall, err := seccomp.GetSyscallFromName("socketpair")
+	if err != nil {
+		return err
+	}
+	if is386 {
+		return f.AddRule(scall, seccomp.ActAllow)
+	}
+
+	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, is386 bool) error {
+	scallMmap, err := seccomp.GetSyscallFromName("mmap")
+	if err != nil {
+		return err
+	}
+	scalls := []seccomp.ScmpSyscall{scallMmap}
+	if is386 {
+		scallMmap2, err := seccomp.GetSyscallFromName("mmap2")
+		if err != nil {
+			return err
+		}
+		scalls = append(scalls, scallMmap2)
+	}
+
+	// (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
+	}
+	for _, scall := range scalls {
+		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
+	}
+	for _, scall := range scalls {
+		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
+		}
+		for _, scall := range scalls {
+			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
+	}
+	for _, scall := range scalls {
+		if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isProtReadExec, isProtReadExecFlags}); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func obfsFilterSetsockopt(f *seccomp.ScmpFilter, is386 bool) error {
+	// 386 already blindly allows all setsockopt() calls.
+	if is386 {
+		return nil
+	}
+
+	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, is386 bool) error {
+	scallMmap, err := seccomp.GetSyscallFromName("mmap")
+	if err != nil {
+		return err
+	}
+	scalls := []seccomp.ScmpSyscall{scallMmap}
+	if is386 {
+		scallMmap2, err := seccomp.GetSyscallFromName("mmap2")
+		if err != nil {
+			return err
+		}
+		scalls = append(scalls, scallMmap2)
+	}
+
+	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
+		}
+		for _, scall := range scalls {
+			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 967d5b8..c88005e 100644
--- a/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp.go
+++ b/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp.go
@@ -17,117 +17,34 @@
 package sandbox
 
 import (
-	"log"
+	"os"
 	"runtime"
 
-	seccomp "github.com/seccomp/libseccomp-golang"
+	"cmd/sandboxed-tor-browser/internal/data"
 )
 
-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) {
-	arch, err := seccomp.GetNativeArch()
-	if err != nil {
-		return nil, err
+func installTorSeccompProfile(fd *os.File, useBridges bool) error {
+	assetFile := "tor-"
+	if useBridges {
+		assetFile = assetFile + "obfs4-"
 	}
+	assetFile = assetFile + runtime.GOARCH + ".bpf"
 
-	actENOSYS := seccomp.ActErrno.SetReturnCode(38)
-	f, err := seccomp.NewFilter(actENOSYS)
+	bpf, err := data.Asset(assetFile)
 	if err != nil {
-		return nil, err
-	}
-
-	if err = f.AddArch(arch); err != nil {
-		f.Release()
-		return nil, err
+		return err
 	}
 
-	return f, nil
+	return writeBuffer(fd, bpf)
 }
 
-func allowSyscalls(f *seccomp.ScmpFilter, calls []string) error {
-	for _, scallName := range calls {
-		scall, err := seccomp.GetSyscallFromName(scallName)
-		if err != nil {
-			if is386() && scallName == "newselect" {
-				scall = seccomp.ScmpSyscall(142)
-			} else {
-				log.Printf("seccomp: unknown system call: %v", scallName)
-				continue
-			}
-		}
-		if err = f.AddRule(scall, seccomp.ActAllow); err != nil {
-			return err
-		}
-	}
-	return nil
-}
+func installTorBrowserSeccompProfile(fd *os.File) error {
+	assetFile := "torbrowser-" + runtime.GOARCH + ".bpf"
 
-func allowCmpEq(f *seccomp.ScmpFilter, scallName string, arg uint, values ...uint64) error {
-	scall, err := seccomp.GetSyscallFromName(scallName)
+	bpf, err := data.Asset(assetFile)
 	if err != nil {
-		log.Printf("seccomp: unknown system call: %v", scallName)
-		return nil
-	}
-
-	// 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 err
 	}
-	return nil
-}
 
-func is386() bool {
-	return runtime.GOARCH == "386"
+	return writeBuffer(fd, bpf)
 }
diff --git a/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp_firefox.go b/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp_firefox.go
deleted file mode 100644
index 4c09eda..0000000
--- a/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp_firefox.go
+++ /dev/null
@@ -1,250 +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 sandbox
-
-import (
-	"os"
-	"syscall"
-	//	seccomp "github.com/seccomp/libseccomp-golang"
-)
-
-func installTorBrowserSeccompProfile(fd *os.File) error {
-	defer fd.Close()
-
-	f, err := newWhitelist()
-	if err != nil {
-		return err
-	}
-	defer f.Release()
-
-	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",
-		"mlock",
-		"mprotect",
-		"mremap",
-		"munmap",
-
-		// XXX: Remove these?
-		"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",
-		"setrlimit",
-
-		"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",
-
-		// 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",
-	}
-	if is386() {
-		allowedNoArgs386 := []string{
-			"fadvise64_64",
-			"fcntl64",
-			"fstat64",
-			"fstatfs64",
-			"ftruncate64",
-			"lstat64",
-			"stat64",
-			"statfs64",
-			"_llseek",
-
-			"mmap2",
-			"prlimit64",
-			"ugetrlimit",
-			"set_thread_area",
-			"waitpid",
-
-			"getgid32",
-			"getuid32",
-			"getresgid32",
-			"getresuid32",
-
-			"recv",
-			"send",
-			"newselect",
-			"socketcall",
-
-			"socket", // Filtered on amd64.
-		}
-		allowedNoArgs = append(allowedNoArgs, allowedNoArgs386...)
-	}
-	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); 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 is386() {
-		if err = allowCmpEq(f, "time", 0, 0); err != nil {
-			return err
-		}
-	} else {
-		if err = allowCmpEq(f, "socket", 0, syscall.AF_UNIX); err != nil {
-			return err
-		}
-	}
-
-	return f.ExportBPF(fd)
-}
diff --git a/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp_tor.go b/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp_tor.go
deleted file mode 100644
index 182a1f9..0000000
--- a/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp_tor.go
+++ /dev/null
@@ -1,536 +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 sandbox
-
-import (
-	"os"
-	"syscall"
-
-	seccomp "github.com/seccomp/libseccomp-golang"
-)
-
-func installTorSeccompProfile(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",
-		"fcntl",
-		"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",
-
-		// XXX: Calls that should be filtered by arg, but aren't yet.
-		"rt_sigaction",
-		"accept4",
-
-		// 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",
-	}
-	if is386() {
-		allowedNoArgs386 := []string{
-			"fstat64",
-			"getegid32",
-			"geteuid32",
-			"getgid32",
-			"getuid32",
-			"_llseek",
-			"sigreturn",
-			"fcntl64", // XXX: Filter by arg.
-
-			"recv",
-			"send",
-			"stat64",
-			"socketcall", // Sigh... (see accept4 in the tor code)
-			"prlimit",
-
-			"ugetrlimit",
-			"set_thread_area",
-		}
-		allowedNoArgs = append(allowedNoArgs, allowedNoArgs386...)
-	}
-	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 = 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 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 is386() {
-			obfsCalls = append(obfsCalls, "newselect")
-		}
-		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 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
-	}
-	if is386() {
-		return f.AddRule(scall, seccomp.ActAllow)
-	}
-
-	// XXX: Tighten this some more.
-	return allowCmpEq(f, "socket", 0, syscall.AF_UNIX, syscall.AF_INET, syscall.AF_INET6, syscall.AF_NETLINK)
-}
-
-func torFilterSetsockopt(f *seccomp.ScmpFilter) error {
-	scall, err := seccomp.GetSyscallFromName("setsockopt")
-	if err != nil {
-		return err
-	}
-	if is386() {
-		return f.AddRule(scall, seccomp.ActAllow)
-	}
-
-	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
-	}
-	if is386() {
-		return f.AddRule(scall, seccomp.ActAllow)
-	}
-
-	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
-	}
-	if is386() {
-		return f.AddRule(scall, seccomp.ActAllow)
-	}
-
-	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 {
-	scallMmap, err := seccomp.GetSyscallFromName("mmap")
-	if err != nil {
-		return err
-	}
-	scalls := []seccomp.ScmpSyscall{scallMmap}
-	if is386() {
-		scallMmap2, err := seccomp.GetSyscallFromName("mmap2")
-		if err != nil {
-			return err
-		}
-		scalls = append(scalls, scallMmap2)
-	}
-
-	// (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
-	}
-	for _, scall := range scalls {
-		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
-	}
-	for _, scall := range scalls {
-		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
-		}
-		for _, scall := range scalls {
-			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
-	}
-	for _, scall := range scalls {
-		if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isProtReadExec, isProtReadExecFlags}); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-func obfsFilterSetsockopt(f *seccomp.ScmpFilter) error {
-	// 386 already blindly allows all setsockopt() calls.
-	if is386() {
-		return nil
-	}
-
-	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 {
-	scallMmap, err := seccomp.GetSyscallFromName("mmap")
-	if err != nil {
-		return err
-	}
-	scalls := []seccomp.ScmpSyscall{scallMmap}
-	if is386() {
-		scallMmap2, err := seccomp.GetSyscallFromName("mmap2")
-		if err != nil {
-			return err
-		}
-		scalls = append(scalls, scallMmap2)
-	}
-
-	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
-		}
-		for _, scall := range scalls {
-			if err = f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isProtNone, isFlag}); err != nil {
-				return err
-			}
-		}
-	}
-	return nil
-}



More information about the tor-commits mailing list