commit 1db6b5aeec0983b264469be96690d916e236e314 Author: Yawning Angel yawning@schwanenlied.me Date: Wed Dec 7 07:05:09 2016 +0000
Filter socketcall() by argument.
This still isn't great, particularly firefox that appears to need all the socket calls, but at least this denies calls that neither container needs. --- src/cmd/gen-seccomp/main.go | 2 - src/cmd/gen-seccomp/seccomp.go | 28 ++++++++-- src/cmd/gen-seccomp/seccomp_firefox.go | 57 +++++++++++++++----- src/cmd/gen-seccomp/seccomp_tor.go | 96 +++++++++++++++++++--------------- 4 files changed, 119 insertions(+), 64 deletions(-)
diff --git a/src/cmd/gen-seccomp/main.go b/src/cmd/gen-seccomp/main.go index 3ad1be5..5dcbbf2 100644 --- a/src/cmd/gen-seccomp/main.go +++ b/src/cmd/gen-seccomp/main.go @@ -32,8 +32,6 @@ func main() { 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 { diff --git a/src/cmd/gen-seccomp/seccomp.go b/src/cmd/gen-seccomp/seccomp.go index 9ec17e8..ece1e7e 100644 --- a/src/cmd/gen-seccomp/seccomp.go +++ b/src/cmd/gen-seccomp/seccomp.go @@ -66,6 +66,28 @@ const ( fionread = 0x541b tcgets = 0x5401 tiocgpgrp = 0x540f + + // socketcall() call numbers (linux/net.h) + sysSocket = 1 // sys_socket() + sysBind = 2 // sys_bind() + sysConnect = 3 // sys_connect() + sysListen = 4 // sys_listen() + sysAccept = 5 // sys_accept() + sysGetsockname = 6 // sys_getsockname() + sysGetpeername = 7 // sys_getpeername() + sysSocketpair = 8 // sys_socketpair() + sysSend = 9 // sys_send() + sysRecv = 10 // sys_recv() + sysSendto = 11 // sys_sendto() + sysRecvfrom = 12 // sys_recvfrom() + sysShutdown = 13 // sys_shutdown() + sysSetsockopt = 14 // sys_setsockopt() + sysGetsockopt = 15 // sys_getsockopt() + sysSendmsg = 16 // sys_sendmsg() + sysRecvmsg = 17 // sys_recvmsg() + sysAccept4 = 18 // sys_accept4() + sysRecvmmsg = 19 // sys_recvmmsg + sysSendmmsg = 20 // sys_sendmmsg )
func newWhitelist(is386 bool) (*seccomp.ScmpFilter, error) { @@ -95,11 +117,7 @@ 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 { - return fmt.Errorf("seccomp: unknown system call: %v", scallName) - } + return fmt.Errorf("seccomp: unknown system call: %v", scallName) } if err = f.AddRule(scall, seccomp.ActAllow); err != nil { return err diff --git a/src/cmd/gen-seccomp/seccomp_firefox.go b/src/cmd/gen-seccomp/seccomp_firefox.go index 22e4bb5..9b25a66 100644 --- a/src/cmd/gen-seccomp/seccomp_firefox.go +++ b/src/cmd/gen-seccomp/seccomp_firefox.go @@ -19,6 +19,8 @@ package main import ( "os" "syscall" + + seccomp "github.com/seccomp/libseccomp-golang" )
func compileTorBrowserSeccompProfile(fd *os.File, is386 bool) error { @@ -30,6 +32,7 @@ func compileTorBrowserSeccompProfile(fd *os.File, is386 bool) error { } defer f.Release()
+ // TODO; Filter the arguments on more of these calls. allowedNoArgs := []string{ "clock_gettime", "clock_getres", @@ -207,11 +210,7 @@ func compileTorBrowserSeccompProfile(fd *os.File, is386 bool) error {
"recv", "send", - "newselect", - - "socket", // Filtered on amd64. - - "socketcall", // Fuck Debian stable.... :( + "_newselect", } allowedNoArgs = append(allowedNoArgs, allowedNoArgs386...) } @@ -219,6 +218,19 @@ func compileTorBrowserSeccompProfile(fd *os.File, is386 bool) error { return err }
+ // Like with how I do the tor rules, handle socketcall() before everything + // else. + if is386 { + if err = ffFilterSocketcall(f); err != nil { + return err + } + + // Unrelated to sockets, only i386 needs this, and it can be filtered. + if err = allowCmpEq(f, "time", 0, 0); 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 { @@ -234,16 +246,33 @@ func compileTorBrowserSeccompProfile(fd *os.File, is386 bool) error { 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 - } + if err = allowCmpEq(f, "socket", 0, syscall.AF_UNIX); err != nil { + return err }
return f.ExportBPF(fd) } + +func ffFilterSocketcall(f *seccomp.ScmpFilter) error { + // This is kind of pointless because it allows basically all the things. + allowedCalls := []uint64{ + sysSocket, + sysBind, + sysConnect, + sysListen, + sysGetsockname, + sysGetpeername, + sysSocketpair, + sysSend, + sysRecv, + sysSendto, + sysRecvfrom, + sysShutdown, + sysSetsockopt, + sysGetsockopt, + sysSendmsg, + sysRecvmsg, + sysAccept4, + } + return allowCmpEq(f, "socketcall", 0, allowedCalls...) +} diff --git a/src/cmd/gen-seccomp/seccomp_tor.go b/src/cmd/gen-seccomp/seccomp_tor.go index 0a1b8cc..d6ec2ca 100644 --- a/src/cmd/gen-seccomp/seccomp_tor.go +++ b/src/cmd/gen-seccomp/seccomp_tor.go @@ -118,14 +118,18 @@ func compileTorSeccompProfile(fd *os.File, useBridges bool, is386 bool) error {
"ugetrlimit", "set_thread_area", - - "socketcall", // I *SHOULDN"T* need this, but Debian stable freaks out. } allowedNoArgs = append(allowedNoArgs, allowedNoArgs386...) } if err = allowSyscalls(f, allowedNoArgs, is386); err != nil { return err } + if is386 { + // Handle socketcall() before filtering other things. + if err = torFilterSocketcall(f, useBridges); err != nil { + return err + } + }
if err = allowCmpEq(f, "time", 0, 0); err != nil { return err @@ -157,22 +161,22 @@ func compileTorSeccompProfile(fd *os.File, useBridges bool, is386 bool) error { if err = allowCmpEq(f, "mremap", 3, mremapMaymove); err != nil { return err } - if err = torFilterAccept4(f, is386); err != nil { + if err = torFilterAccept4(f); err != nil { return err } if err = torFilterPoll(f); err != nil { return err } - if err = torFilterSocket(f, is386); err != nil { + if err = torFilterSocket(f); err != nil { return err } - if err = torFilterSetsockopt(f, is386); err != nil { + if err = torFilterSetsockopt(f); err != nil { return err } - if err = torFilterGetsockopt(f, is386); err != nil { + if err = torFilterGetsockopt(f); err != nil { return err } - if err = torFilterSocketpair(f, is386); err != nil { + if err = torFilterSocketpair(f); err != nil { return err } if err = torFilterMmap(f, is386); err != nil { @@ -196,7 +200,7 @@ func compileTorSeccompProfile(fd *os.File, useBridges bool, is386 bool) error { "getppid", } if is386 { - obfsCalls = append(obfsCalls, "newselect") + obfsCalls = append(obfsCalls, "_newselect") } if err = allowSyscalls(f, obfsCalls, is386); err != nil { return err @@ -212,7 +216,7 @@ func compileTorSeccompProfile(fd *os.File, useBridges bool, is386 bool) error { if err = allowCmpEq(f, "futex", 1, futexWake, futexWait); err != nil { return err } - if err = obfsFilterSetsockopt(f, is386); err != nil { + if err = obfsFilterSetsockopt(f); err != nil { return err } if err = obfsFilterMmap(f, is386); err != nil { @@ -223,6 +227,40 @@ func compileTorSeccompProfile(fd *os.File, useBridges bool, is386 bool) error { return f.ExportBPF(fd) }
+func torFilterSocketcall(f *seccomp.ScmpFilter, useBridges bool) error { + // This interface needs to die in a fire, because it's leaving + // gaping attack surface. It kind of will assuming that things + // move on to 4.3 or later. + // + // Emperically on Fedora 25 getsockopt and setsockopt still are + // multiplexed, though that may just be my rules or libseccomp2. + // + // Re-test after Debian stable moves to a modern kernel. + + allowedCalls := []uint64{ + sysSocket, + sysBind, + sysConnect, + sysListen, + sysGetsockname, + sysSocketpair, + sysSend, + sysRecv, + sysSendto, + sysRecvfrom, + sysSetsockopt, + sysGetsockopt, + sysSendmsg, + sysRecvmsg, + sysAccept4, + } + if useBridges { + allowedCalls = append(allowedCalls, sysGetpeername) + } + + return allowCmpEq(f, "socketcall", 0, allowedCalls...) +} + func torFilterPrctl(f *seccomp.ScmpFilter) error { scall, err := seccomp.GetSyscallFromName("prctl") if err != nil { @@ -248,17 +286,11 @@ func torFilterPrctl(f *seccomp.ScmpFilter) error { return f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isPrSetDeathsig}) }
-func torFilterAccept4(f *seccomp.ScmpFilter, is386 bool) error { +func torFilterAccept4(f *seccomp.ScmpFilter) error { scall, err := seccomp.GetSyscallFromName("accept4") if err != nil { return err } - if is386 { - // XXX: The tor common/sandbox.c file, explcitly allows socketcall() - // by arg for this call, and only this call, when libseccomp should - // do the right thing. - return f.AddRule(scall, seccomp.ActAllow) - }
cond, err := seccomp.MakeCondition(3, seccomp.CompareMaskedEqual, 0, syscall.SOCK_CLOEXEC|syscall.SOCK_NONBLOCK) if err != nil { @@ -285,27 +317,16 @@ func torFilterPoll(f *seccomp.ScmpFilter) error { 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) - } - +func torFilterSocket(f *seccomp.ScmpFilter) error { // 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 { +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 { @@ -331,14 +352,11 @@ func torFilterSetsockopt(f *seccomp.ScmpFilter, is386 bool) error { return nil }
-func torFilterGetsockopt(f *seccomp.ScmpFilter, is386 bool) error { +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 { @@ -351,14 +369,11 @@ func torFilterGetsockopt(f *seccomp.ScmpFilter, is386 bool) error { return f.AddRuleConditional(scall, seccomp.ActAllow, []seccomp.ScmpCondition{isSolSocket, optIsError}) }
-func torFilterSocketpair(f *seccomp.ScmpFilter, is386 bool) error { +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 { @@ -530,12 +545,7 @@ func torFilterFcntl(f *seccomp.ScmpFilter, is386 bool) error { return nil }
-func obfsFilterSetsockopt(f *seccomp.ScmpFilter, is386 bool) error { - // 386 already blindly allows all setsockopt() calls. - if is386 { - return nil - } - +func obfsFilterSetsockopt(f *seccomp.ScmpFilter) error { scall, err := seccomp.GetSyscallFromName("setsockopt") if err != nil { return err
tor-commits@lists.torproject.org