summaryrefslogtreecommitdiffstats
path: root/plugin/rpcplugin/sandbox/seccomp_linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/rpcplugin/sandbox/seccomp_linux.go')
-rw-r--r--plugin/rpcplugin/sandbox/seccomp_linux.go178
1 files changed, 178 insertions, 0 deletions
diff --git a/plugin/rpcplugin/sandbox/seccomp_linux.go b/plugin/rpcplugin/sandbox/seccomp_linux.go
new file mode 100644
index 000000000..afe86e90a
--- /dev/null
+++ b/plugin/rpcplugin/sandbox/seccomp_linux.go
@@ -0,0 +1,178 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package sandbox
+
+import (
+ "syscall"
+ "unsafe"
+
+ "github.com/pkg/errors"
+ "golang.org/x/net/bpf"
+ "golang.org/x/sys/unix"
+)
+
+const (
+ SECCOMP_RET_ALLOW = 0x7fff0000
+ SECCOMP_RET_ERRNO = 0x00050000
+)
+
+const (
+ EM_X86_64 = 62
+
+ __AUDIT_ARCH_64BIT = 0x80000000
+ __AUDIT_ARCH_LE = 0x40000000
+
+ AUDIT_ARCH_X86_64 = EM_X86_64 | __AUDIT_ARCH_64BIT | __AUDIT_ARCH_LE
+
+ nrSize = 4
+ archOffset = nrSize
+ ipOffset = archOffset + 4
+ argsOffset = ipOffset + 8
+)
+
+type SeccompCondition interface {
+ Filter(littleEndian bool, skipFalseSentinel uint8) []bpf.Instruction
+}
+
+func seccompArgLowWord(arg int, littleEndian bool) uint32 {
+ offset := uint32(argsOffset + arg*8)
+ if !littleEndian {
+ offset += 4
+ }
+ return offset
+}
+
+func seccompArgHighWord(arg int, littleEndian bool) uint32 {
+ offset := uint32(argsOffset + arg*8)
+ if littleEndian {
+ offset += 4
+ }
+ return offset
+}
+
+type SeccompArgHasNoBits struct {
+ Arg int
+ Mask uint64
+}
+
+func (c SeccompArgHasNoBits) Filter(littleEndian bool, skipFalseSentinel uint8) []bpf.Instruction {
+ return []bpf.Instruction{
+ bpf.LoadAbsolute{Off: seccompArgHighWord(c.Arg, littleEndian), Size: 4},
+ bpf.JumpIf{Cond: bpf.JumpBitsSet, Val: uint32(c.Mask >> 32), SkipTrue: skipFalseSentinel},
+ bpf.LoadAbsolute{Off: seccompArgLowWord(c.Arg, littleEndian), Size: 4},
+ bpf.JumpIf{Cond: bpf.JumpBitsSet, Val: uint32(c.Mask), SkipTrue: skipFalseSentinel},
+ }
+}
+
+type SeccompArgHasAnyBit struct {
+ Arg int
+ Mask uint64
+}
+
+func (c SeccompArgHasAnyBit) Filter(littleEndian bool, skipFalseSentinel uint8) []bpf.Instruction {
+ return []bpf.Instruction{
+ bpf.LoadAbsolute{Off: seccompArgHighWord(c.Arg, littleEndian), Size: 4},
+ bpf.JumpIf{Cond: bpf.JumpBitsSet, Val: uint32(c.Mask >> 32), SkipTrue: 2},
+ bpf.LoadAbsolute{Off: seccompArgLowWord(c.Arg, littleEndian), Size: 4},
+ bpf.JumpIf{Cond: bpf.JumpBitsSet, Val: uint32(c.Mask), SkipFalse: skipFalseSentinel},
+ }
+}
+
+type SeccompArgEquals struct {
+ Arg int
+ Value uint64
+}
+
+func (c SeccompArgEquals) Filter(littleEndian bool, skipFalseSentinel uint8) []bpf.Instruction {
+ return []bpf.Instruction{
+ bpf.LoadAbsolute{Off: seccompArgHighWord(c.Arg, littleEndian), Size: 4},
+ bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(c.Value >> 32), SkipFalse: skipFalseSentinel},
+ bpf.LoadAbsolute{Off: seccompArgLowWord(c.Arg, littleEndian), Size: 4},
+ bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(c.Value), SkipFalse: skipFalseSentinel},
+ }
+}
+
+type SeccompConditions struct {
+ All []SeccompCondition
+}
+
+type SeccompSyscall struct {
+ Syscall uint32
+ Any []SeccompConditions
+}
+
+func SeccompFilter(arch uint32, allowedSyscalls []SeccompSyscall) (filter []bpf.Instruction) {
+ filter = append(filter,
+ bpf.LoadAbsolute{Off: archOffset, Size: 4},
+ bpf.JumpIf{Cond: bpf.JumpEqual, Val: arch, SkipTrue: 1},
+ bpf.RetConstant{Val: uint32(SECCOMP_RET_ERRNO | unix.EPERM)},
+ )
+
+ filter = append(filter, bpf.LoadAbsolute{Off: 0, Size: nrSize})
+ for _, s := range allowedSyscalls {
+ if s.Any != nil {
+ syscallStart := len(filter)
+ filter = append(filter, bpf.Instruction(nil))
+ for _, cs := range s.Any {
+ anyStart := len(filter)
+ for _, c := range cs.All {
+ filter = append(filter, c.Filter((arch&__AUDIT_ARCH_LE) != 0, 255)...)
+ }
+ filter = append(filter, bpf.RetConstant{Val: SECCOMP_RET_ALLOW})
+ for i := anyStart; i < len(filter); i++ {
+ if jump, ok := filter[i].(bpf.JumpIf); ok {
+ if len(filter)-i-1 > 255 {
+ panic("condition too long")
+ }
+ if jump.SkipFalse == 255 {
+ jump.SkipFalse = uint8(len(filter) - i - 1)
+ }
+ if jump.SkipTrue == 255 {
+ jump.SkipTrue = uint8(len(filter) - i - 1)
+ }
+ filter[i] = jump
+ }
+ }
+ }
+ filter = append(filter, bpf.RetConstant{Val: uint32(SECCOMP_RET_ERRNO | unix.EPERM)})
+ if len(filter)-syscallStart-1 > 255 {
+ panic("conditions too long")
+ }
+ filter[syscallStart] = bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(s.Syscall), SkipFalse: uint8(len(filter) - syscallStart - 1)}
+ } else {
+ filter = append(filter,
+ bpf.JumpIf{Cond: bpf.JumpEqual, Val: uint32(s.Syscall), SkipFalse: 1},
+ bpf.RetConstant{Val: SECCOMP_RET_ALLOW},
+ )
+ }
+ }
+
+ return append(filter, bpf.RetConstant{Val: uint32(SECCOMP_RET_ERRNO | unix.EPERM)})
+}
+
+func EnableSeccompFilter(filter []bpf.Instruction) error {
+ assembled, err := bpf.Assemble(filter)
+ if err != nil {
+ return errors.Wrapf(err, "unable to assemble filter")
+ }
+
+ sockFilter := make([]unix.SockFilter, len(filter))
+ for i, instruction := range assembled {
+ sockFilter[i].Code = instruction.Op
+ sockFilter[i].Jt = instruction.Jt
+ sockFilter[i].Jf = instruction.Jf
+ sockFilter[i].K = instruction.K
+ }
+
+ prog := unix.SockFprog{
+ Len: uint16(len(sockFilter)),
+ Filter: &sockFilter[0],
+ }
+
+ if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, uintptr(unsafe.Pointer(&prog))); errno != 0 {
+ return errors.Wrapf(syscall.Errno(errno), "syscall error")
+ }
+
+ return nil
+}