forked from elastic/go-seccomp-bpf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
seccomp_linux.go
115 lines (100 loc) · 3.13 KB
/
seccomp_linux.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package seccomp
import (
"fmt"
"syscall"
"unsafe"
"golang.org/x/net/bpf"
"golang.org/x/sys/unix"
)
// Supported returns true if the seccomp syscall is supported.
func Supported() bool {
// Strict mode requires that flags be set to 0, but we are sending 1 so
// this will return EINVAL if the syscall exists and is allowed.
if err := seccomp(seccompSetModeStrict, 1, nil); err == syscall.EINVAL {
return true
}
return false
}
// SetNoNewPrivs will use prctl to set the calling thread's no_new_privs bit to
// 1 (true). Once set, this bit cannot be unset.
func SetNoNewPrivs() error {
return prctl(prSetNoNewPrivs, 1)
}
// LoadFilter will install seccomp using native methods.
func LoadFilter(filter Filter) error {
insts, err := filter.Policy.Assemble()
if err != nil {
return fmt.Errorf("failed to assemble policy: %w", err)
}
raw, err := bpf.Assemble(insts)
if err != nil {
return fmt.Errorf("failed to assemble BPF instructions: %w", err)
}
sockFilter := sockFilter(raw)
program := &syscall.SockFprog{
Len: uint16(len(sockFilter)),
Filter: &sockFilter[0],
}
if filter.NoNewPrivs {
if err = SetNoNewPrivs(); err != nil {
return fmt.Errorf("failed to set no_new_privs with prctl: %w", err)
}
}
if err = seccomp(seccompSetModeFilter, filter.Flag, unsafe.Pointer(program)); err != nil {
if err == syscall.ENOSYS {
return fmt.Errorf("failed loading seccomp filter: seccomp "+
"is not supported by the kernel: %w", err)
}
return fmt.Errorf("failed loading seccomp filter: %w", err)
}
return nil
}
func sockFilter(raw []bpf.RawInstruction) []syscall.SockFilter {
filter := make([]syscall.SockFilter, 0, len(raw))
for _, instruction := range raw {
filter = append(filter, syscall.SockFilter{
Code: instruction.Op,
Jt: instruction.Jt,
Jf: instruction.Jf,
K: instruction.K,
})
}
return filter
}
// prctl syscall wrapper.
func prctl(option uintptr, args ...uintptr) error {
if len(args) > 4 {
return syscall.E2BIG
}
var arg [4]uintptr
copy(arg[:], args)
_, _, e := syscall.Syscall6(syscall.SYS_PRCTL, option, arg[0], arg[1], arg[2], arg[3], 0)
if e != 0 {
return e
}
return nil
}
// seccomp syscall wrapper.
func seccomp(op uintptr, flags FilterFlag, uargs unsafe.Pointer) error {
_, _, e := syscall.Syscall(unix.SYS_SECCOMP, op, uintptr(flags), uintptr(uargs))
if e != 0 {
return e
}
return nil
}