[tor-commits] [sandboxed-tor-browser/master] Switch back to using lbiseccomp2 for everything.

yawning at torproject.org yawning at torproject.org
Sun Dec 4 23:26:45 UTC 2016


commit a0408a73323bbcb4dbe1d445cb96e7e040694f4b
Author: Yawning Angel <yawning at schwanenlied.me>
Date:   Sun Dec 4 20:59:46 2016 +0000

    Switch back to using lbiseccomp2 for everything.
    
    This lets me filter arguments on x86.  Unfortunately, this means that
    debian stable users needs to use libseccomp2 from backports (along with
    the compile), but at least the backport works.
---
 README.md                                          |   2 +-
 .../internal/sandbox/application.go                |  27 +-
 .../internal/sandbox/seccomp.go                    | 122 ++++-
 .../internal/sandbox/seccomp_firefox.go            | 250 ++++++++++
 .../internal/sandbox/seccomp_tor.go                | 536 +++++++++++++++++++++
 src/tbb_stub/tbb_stub.c                            |   6 -
 6 files changed, 925 insertions(+), 18 deletions(-)

diff --git a/README.md b/README.md
index 4590c7e..82e4ac6 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Runtime dependencies:
 
  * A modern Linux system on x86/x86_64 architecture.
  * bubblewrap >= 0.1.3 (https://github.com/projectatomic/bubblewrap).
- * libseccomp2 >= 2.2.1 (i386 only, x86_64 does not require this).
+ * libseccomp2 >= 2.2.1
  * Gtk+ >= 3.14.0
  * (Optional) PulseAudio
 
diff --git a/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go b/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go
index edac173..939d805 100644
--- a/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go
+++ b/src/cmd/sandboxed-tor-browser/internal/sandbox/application.go
@@ -28,6 +28,7 @@ import (
 	"path/filepath"
 	"runtime"
 	"sort"
+	"strings"
 	"syscall"
 
 	"cmd/sandboxed-tor-browser/internal/dynlib"
@@ -230,17 +231,41 @@ func RunTorBrowser(cfg *config.Config, manif *config.Manifest, tor *tor.Tor) (cm
 				// you.
 				extraLibs = append(extraLibs, libPulse)
 				ldLibraryPath = ldLibraryPath + ":" + paLibsPath
-				h.roBind(paLibsPath, restrictedPulseDir, false)
+				h.dir(restrictedPulseDir)
 				extraLdLibraryPath = extraLdLibraryPath + ":" + restrictedPulseDir
 
+				boundPulseCore := false
 				matches, err := filepath.Glob(paLibsPath + "/*.so")
 				if err != nil {
 					return nil, err
 				}
 				for _, v := range matches {
 					_, f := filepath.Split(v)
+					if strings.HasPrefix(f, "libpulsecore") {
+						boundPulseCore = true
+					}
+					h.roBind(v, filepath.Join(restrictedPulseDir, f), false)
 					extraLibs = append(extraLibs, f)
 				}
+
+				if !boundPulseCore {
+					// Debian sticks libpulsecore-blah.so in /usr/lib, unlike
+					// everyone else who sticks it in /usr/lib/pulseaudo,
+					// because fuck you.
+					matches, err = filepath.Glob("/usr/lib/libpulsecore-*.so")
+					if err != nil {
+						return nil, err
+					}
+					if len(matches) == 0 {
+						log.Printf("sandbox: Failed to find `libpulsecore-<version>.so`, audio will crash the browser.")
+					} else {
+						for _, v := range matches {
+							_, f := filepath.Split(v)
+							h.roBind(v, filepath.Join(restrictedPulseDir, f), false)
+							extraLibs = append(extraLibs, f)
+						}
+					}
+				}
 			} else {
 				log.Printf("sandbox: Failed to find pulse audio libraries.")
 			}
diff --git a/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp.go b/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp.go
index de625d9..fed647e 100644
--- a/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp.go
+++ b/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp.go
@@ -16,7 +16,13 @@
 
 package sandbox
 
-import "os"
+import (
+	"log"
+	"os"
+	"runtime"
+
+	seccomp "github.com/seccomp/libseccomp-golang"
+)
 
 const (
 	torBrowserWhitelist = "torbrowser-launcher-whitelist.seccomp"
@@ -25,19 +31,115 @@ const (
 	basicBlacklist      = "blacklist.seccomp"
 )
 
-func installTorBrowserSeccompProfile(fd *os.File) error {
-	return installSeccomp(fd, torBrowserSeccompAssets, false)
+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 installBasicSeccompBlacklist(fd *os.File) error {
+	return installSeccomp(fd, blacklistSeccompAssets, true)
 }
 
-func installTorSeccompProfile(fd *os.File, useBridges bool) error {
-	assets := torSeccompAssets
-	if useBridges {
-		assets = torObfs4SeccompAssets
+func newWhitelist() (*seccomp.ScmpFilter, error) {
+	arch, err := seccomp.GetNativeArch()
+	if err != nil {
+		return nil, err
 	}
 
-	return installSeccomp(fd, assets, false)
+	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 installBasicSeccompBlacklist(fd *os.File) error {
-	return installSeccomp(fd, blacklistSeccompAssets, true)
+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 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
+}
+
+func is386() bool {
+	return runtime.GOARCH == "386"
 }
diff --git a/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp_firefox.go b/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp_firefox.go
new file mode 100644
index 0000000..4c09eda
--- /dev/null
+++ b/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp_firefox.go
@@ -0,0 +1,250 @@
+// 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
new file mode 100644
index 0000000..182a1f9
--- /dev/null
+++ b/src/cmd/sandboxed-tor-browser/internal/sandbox/seccomp_tor.go
@@ -0,0 +1,536 @@
+// 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
+}
diff --git a/src/tbb_stub/tbb_stub.c b/src/tbb_stub/tbb_stub.c
index 0f714b7..5ce7bd5 100644
--- a/src/tbb_stub/tbb_stub.c
+++ b/src/tbb_stub/tbb_stub.c
@@ -48,10 +48,8 @@
 #include <stdlib.h>
 #include <X11/Xlib.h>
 
-#ifndef __i386__
 #include <glob.h>
 #include <stdbool.h>
-#endif
 
 static pthread_once_t stub_init_once = PTHREAD_ONCE_INIT;
 static int (*real_connect)(int, const struct sockaddr *, socklen_t) = NULL;
@@ -176,8 +174,6 @@ XQueryExtension(Display *display, _Xconst char *name, int *major, int *event, in
   return real_XQueryExtension(display, name, major, event, error);
 }
 
-#ifndef __i386__
-
 typedef struct pa_mutex pm;
 static pm* (*real_pa_mutex_new)(bool, bool);
 
@@ -252,8 +248,6 @@ pa_mutex_new(bool recursive, bool inherit_priority) {
   return real_pa_mutex_new(recursive, false);
 }
 
-#endif
-
 /*  Initialize the stub. */
 static void
 stub_init(void)





More information about the tor-commits mailing list