aboutsummaryrefslogtreecommitdiffhomepage
path: root/container/seccomp/libseccomp.go
blob: c7a3b67053149164729cf045b8a56ce76b4f9e96 (plain)
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package seccomp

/*
#cgo linux pkg-config: --static libseccomp

#include <libseccomp-helper.h>
#include <sys/personality.h>
*/
import "C"
import (
	"errors"
	"fmt"
	"runtime"
	"syscall"
	"unsafe"
)

const (
	PER_LINUX   = C.PER_LINUX
	PER_LINUX32 = C.PER_LINUX32
)

var (
	ErrInvalidRules = errors.New("invalid native rules slice")
)

// LibraryError represents a libseccomp error.
type LibraryError struct {
	Prefix  string
	Seccomp syscall.Errno
	Errno   error
}

func (e *LibraryError) Error() string {
	if e.Seccomp == 0 {
		if e.Errno == nil {
			panic("invalid libseccomp error")
		}
		return fmt.Sprintf("%s: %s", e.Prefix, e.Errno)
	}
	if e.Errno == nil {
		return fmt.Sprintf("%s: %s", e.Prefix, e.Seccomp)
	}
	return fmt.Sprintf("%s: %s (%s)", e.Prefix, e.Seccomp, e.Errno)
}

func (e *LibraryError) Is(err error) bool {
	if e == nil {
		return err == nil
	}
	if ef, ok := err.(*LibraryError); ok {
		return *e == *ef
	}
	return (e.Seccomp != 0 && errors.Is(err, e.Seccomp)) ||
		(e.Errno != nil && errors.Is(err, e.Errno))
}

type (
	ScmpSyscall = C.int
	ScmpErrno   = C.int
)

// A NativeRule specifies an arch-specific action taken by seccomp under certain conditions.
type NativeRule struct {
	// Syscall is the arch-dependent syscall number to act against.
	Syscall ScmpSyscall
	// Errno is the errno value to return when the condition is satisfied.
	Errno ScmpErrno
	// Arg is the optional struct scmp_arg_cmp passed to libseccomp.
	Arg *ScmpArgCmp
}

type ExportFlag = C.hakurei_export_flag

const (
	// AllowMultiarch allows multiarch/emulation.
	AllowMultiarch ExportFlag = C.HAKUREI_EXPORT_MULTIARCH
	// AllowCAN allows AF_CAN.
	AllowCAN ExportFlag = C.HAKUREI_EXPORT_CAN
	// AllowBluetooth allows AF_BLUETOOTH.
	AllowBluetooth ExportFlag = C.HAKUREI_EXPORT_BLUETOOTH
)

var resPrefix = [...]string{
	0: "",
	1: "seccomp_init failed",
	2: "seccomp_arch_add failed",
	3: "seccomp_arch_add failed (multiarch)",
	4: "internal libseccomp failure",
	5: "seccomp_rule_add failed",
	6: "seccomp_export_bpf failed",
	7: "seccomp_load failed",
}

// Export streams filter contents to fd, or installs it to the current process if fd < 0.
func Export(fd int, rules []NativeRule, flags ExportFlag) error {
	if len(rules) == 0 {
		return ErrInvalidRules
	}

	var (
		arch      C.uint32_t = 0
		multiarch C.uint32_t = 0
	)
	switch runtime.GOARCH {
	case "386":
		arch = C.SCMP_ARCH_X86
	case "amd64":
		arch = C.SCMP_ARCH_X86_64
		multiarch = C.SCMP_ARCH_X86
	case "arm":
		arch = C.SCMP_ARCH_ARM
	case "arm64":
		arch = C.SCMP_ARCH_AARCH64
		multiarch = C.SCMP_ARCH_ARM
	}

	var ret C.int

	rulesPinner := new(runtime.Pinner)
	for i := range rules {
		rule := &rules[i]
		rulesPinner.Pin(rule)
		if rule.Arg != nil {
			rulesPinner.Pin(rule.Arg)
		}
	}
	res, err := C.hakurei_export_filter(
		&ret, C.int(fd),
		arch, multiarch,
		(*C.struct_hakurei_syscall_rule)(unsafe.Pointer(&rules[0])),
		C.size_t(len(rules)),
		flags,
	)
	rulesPinner.Unpin()

	if prefix := resPrefix[res]; prefix != "" {
		return &LibraryError{
			prefix,
			-syscall.Errno(ret),
			err,
		}
	}
	return err
}

// ScmpCompare is the equivalent of scmp_compare;
// Comparison operators
type ScmpCompare = C.enum_scmp_compare

const (
	_SCMP_CMP_MIN = C._SCMP_CMP_MIN

	// not equal
	SCMP_CMP_NE = C.SCMP_CMP_NE
	// less than
	SCMP_CMP_LT = C.SCMP_CMP_LT
	// less than or equal
	SCMP_CMP_LE = C.SCMP_CMP_LE
	// equal
	SCMP_CMP_EQ = C.SCMP_CMP_EQ
	// greater than or equal
	SCMP_CMP_GE = C.SCMP_CMP_GE
	// greater than
	SCMP_CMP_GT = C.SCMP_CMP_GT
	// masked equality
	SCMP_CMP_MASKED_EQ = C.SCMP_CMP_MASKED_EQ

	_SCMP_CMP_MAX = C._SCMP_CMP_MAX
)

// ScmpDatum is the equivalent of scmp_datum_t;
// Argument datum
type ScmpDatum uint64

// ScmpArgCmp is the equivalent of struct scmp_arg_cmp;
// Argument / Value comparison definition
type ScmpArgCmp struct {
	// argument number, starting at 0
	Arg C.uint
	// the comparison op, e.g. SCMP_CMP_*
	Op ScmpCompare

	DatumA, DatumB ScmpDatum
}

// only used for testing
func syscallResolveName(s string) (trap int) {
	v := C.CString(s)
	trap = int(C.seccomp_syscall_resolve_name(v))
	C.free(unsafe.Pointer(v))

	return
}