aboutsummaryrefslogtreecommitdiffhomepage
path: root/sandbox/seccomp/seccomp.go
blob: c9a201e2581c3445f34f0e2c1ee1b7ae0407bd56 (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
// Package seccomp provides filter presets and high level wrappers around libseccomp.
package seccomp

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

#include "seccomp-build.h"
*/
import "C"

import (
	"errors"
	"fmt"
	"runtime"
	"syscall"
	"unsafe"
)

// 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))
}

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",
}

type FilterOpts = C.hakurei_filter_opts

const (
	filterVerbose FilterOpts = C.HAKUREI_VERBOSE
	// FilterExt are project-specific extensions.
	FilterExt FilterOpts = C.HAKUREI_EXT
	// FilterDenyNS denies namespace setup syscalls.
	FilterDenyNS FilterOpts = C.HAKUREI_DENY_NS
	// FilterDenyTTY denies faking input.
	FilterDenyTTY FilterOpts = C.HAKUREI_DENY_TTY
	// FilterDenyDevel denies development-related syscalls.
	FilterDenyDevel FilterOpts = C.HAKUREI_DENY_DEVEL
	// FilterMultiarch allows multiarch/emulation.
	FilterMultiarch FilterOpts = C.HAKUREI_MULTIARCH
	// FilterLinux32 sets PER_LINUX32.
	FilterLinux32 FilterOpts = C.HAKUREI_LINUX32
	// FilterCan allows AF_CAN.
	FilterCan FilterOpts = C.HAKUREI_CAN
	// FilterBluetooth allows AF_BLUETOOTH.
	FilterBluetooth FilterOpts = C.HAKUREI_BLUETOOTH
)

func buildFilter(fd int, opts FilterOpts) error {
	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
	}

	// this removes repeated transitions between C and Go execution
	// when producing log output via hakurei_println and CPrintln is nil
	if fp := printlnP.Load(); fp != nil {
		opts |= filterVerbose
	}

	var ret C.int
	res, err := C.hakurei_build_filter(&ret, C.int(fd), arch, multiarch, opts)
	if prefix := resPrefix[res]; prefix != "" {
		return &LibraryError{
			prefix,
			-syscall.Errno(ret),
			err,
		}
	}
	return err
}

// 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
}