1// +build linux
2
3// Public API specification for libseccomp Go bindings
4// Contains public API for the bindings
5
6// Package seccomp provides bindings for libseccomp, a library wrapping the Linux
7// seccomp syscall. Seccomp enables an application to restrict system call use
8// for itself and its children.
9package seccomp
10
11import (
12	"fmt"
13	"os"
14	"runtime"
15	"strings"
16	"sync"
17	"syscall"
18	"unsafe"
19)
20
21// C wrapping code
22
23// #cgo pkg-config: libseccomp
24// #include <stdlib.h>
25// #include <seccomp.h>
26import "C"
27
28// Exported types
29
30// VersionError denotes that the system libseccomp version is incompatible
31// with this package.
32type VersionError struct {
33	message string
34	minimum string
35}
36
37func (e VersionError) Error() string {
38	format := "Libseccomp version too low: "
39	if e.message != "" {
40		format += e.message + ": "
41	}
42	format += "minimum supported is "
43	if e.minimum != "" {
44		format += e.minimum + ": "
45	} else {
46		format += "2.2.0: "
47	}
48	format += "detected %d.%d.%d"
49	return fmt.Sprintf(format, verMajor, verMinor, verMicro)
50}
51
52// ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a
53// per-architecture basis.
54type ScmpArch uint
55
56// ScmpAction represents an action to be taken on a filter rule match in
57// libseccomp
58type ScmpAction uint
59
60// ScmpCompareOp represents a comparison operator which can be used in a filter
61// rule
62type ScmpCompareOp uint
63
64// ScmpCondition represents a rule in a libseccomp filter context
65type ScmpCondition struct {
66	Argument uint          `json:"argument,omitempty"`
67	Op       ScmpCompareOp `json:"operator,omitempty"`
68	Operand1 uint64        `json:"operand_one,omitempty"`
69	Operand2 uint64        `json:"operand_two,omitempty"`
70}
71
72// ScmpSyscall represents a Linux System Call
73type ScmpSyscall int32
74
75// Exported Constants
76
77const (
78	// Valid architectures recognized by libseccomp
79	// PowerPC and S390(x) architectures are unavailable below library version
80	// v2.3.0 and will returns errors if used with incompatible libraries
81
82	// ArchInvalid is a placeholder to ensure uninitialized ScmpArch
83	// variables are invalid
84	ArchInvalid ScmpArch = iota
85	// ArchNative is the native architecture of the kernel
86	ArchNative ScmpArch = iota
87	// ArchX86 represents 32-bit x86 syscalls
88	ArchX86 ScmpArch = iota
89	// ArchAMD64 represents 64-bit x86-64 syscalls
90	ArchAMD64 ScmpArch = iota
91	// ArchX32 represents 64-bit x86-64 syscalls (32-bit pointers)
92	ArchX32 ScmpArch = iota
93	// ArchARM represents 32-bit ARM syscalls
94	ArchARM ScmpArch = iota
95	// ArchARM64 represents 64-bit ARM syscalls
96	ArchARM64 ScmpArch = iota
97	// ArchMIPS represents 32-bit MIPS syscalls
98	ArchMIPS ScmpArch = iota
99	// ArchMIPS64 represents 64-bit MIPS syscalls
100	ArchMIPS64 ScmpArch = iota
101	// ArchMIPS64N32 represents 64-bit MIPS syscalls (32-bit pointers)
102	ArchMIPS64N32 ScmpArch = iota
103	// ArchMIPSEL represents 32-bit MIPS syscalls (little endian)
104	ArchMIPSEL ScmpArch = iota
105	// ArchMIPSEL64 represents 64-bit MIPS syscalls (little endian)
106	ArchMIPSEL64 ScmpArch = iota
107	// ArchMIPSEL64N32 represents 64-bit MIPS syscalls (little endian,
108	// 32-bit pointers)
109	ArchMIPSEL64N32 ScmpArch = iota
110	// ArchPPC represents 32-bit POWERPC syscalls
111	ArchPPC ScmpArch = iota
112	// ArchPPC64 represents 64-bit POWER syscalls (big endian)
113	ArchPPC64 ScmpArch = iota
114	// ArchPPC64LE represents 64-bit POWER syscalls (little endian)
115	ArchPPC64LE ScmpArch = iota
116	// ArchS390 represents 31-bit System z/390 syscalls
117	ArchS390 ScmpArch = iota
118	// ArchS390X represents 64-bit System z/390 syscalls
119	ArchS390X ScmpArch = iota
120)
121
122const (
123	// Supported actions on filter match
124
125	// ActInvalid is a placeholder to ensure uninitialized ScmpAction
126	// variables are invalid
127	ActInvalid ScmpAction = iota
128	// ActKill kills the process
129	ActKill ScmpAction = iota
130	// ActTrap throws SIGSYS
131	ActTrap ScmpAction = iota
132	// ActErrno causes the syscall to return a negative error code. This
133	// code can be set with the SetReturnCode method
134	ActErrno ScmpAction = iota
135	// ActTrace causes the syscall to notify tracing processes with the
136	// given error code. This code can be set with the SetReturnCode method
137	ActTrace ScmpAction = iota
138	// ActAllow permits the syscall to continue execution
139	ActAllow ScmpAction = iota
140	// ActLog permits the syscall to continue execution after logging it.
141	// This action is only usable when libseccomp API level 3 or higher is
142	// supported.
143	ActLog ScmpAction = iota
144)
145
146const (
147	// These are comparison operators used in conditional seccomp rules
148	// They are used to compare the value of a single argument of a syscall
149	// against a user-defined constant
150
151	// CompareInvalid is a placeholder to ensure uninitialized ScmpCompareOp
152	// variables are invalid
153	CompareInvalid ScmpCompareOp = iota
154	// CompareNotEqual returns true if the argument is not equal to the
155	// given value
156	CompareNotEqual ScmpCompareOp = iota
157	// CompareLess returns true if the argument is less than the given value
158	CompareLess ScmpCompareOp = iota
159	// CompareLessOrEqual returns true if the argument is less than or equal
160	// to the given value
161	CompareLessOrEqual ScmpCompareOp = iota
162	// CompareEqual returns true if the argument is equal to the given value
163	CompareEqual ScmpCompareOp = iota
164	// CompareGreaterEqual returns true if the argument is greater than or
165	// equal to the given value
166	CompareGreaterEqual ScmpCompareOp = iota
167	// CompareGreater returns true if the argument is greater than the given
168	// value
169	CompareGreater ScmpCompareOp = iota
170	// CompareMaskedEqual returns true if the argument is equal to the given
171	// value, when masked (bitwise &) against the second given value
172	CompareMaskedEqual ScmpCompareOp = iota
173)
174
175// Helpers for types
176
177// GetArchFromString returns an ScmpArch constant from a string representing an
178// architecture
179func GetArchFromString(arch string) (ScmpArch, error) {
180	if err := ensureSupportedVersion(); err != nil {
181		return ArchInvalid, err
182	}
183
184	switch strings.ToLower(arch) {
185	case "x86":
186		return ArchX86, nil
187	case "amd64", "x86-64", "x86_64", "x64":
188		return ArchAMD64, nil
189	case "x32":
190		return ArchX32, nil
191	case "arm":
192		return ArchARM, nil
193	case "arm64", "aarch64":
194		return ArchARM64, nil
195	case "mips":
196		return ArchMIPS, nil
197	case "mips64":
198		return ArchMIPS64, nil
199	case "mips64n32":
200		return ArchMIPS64N32, nil
201	case "mipsel":
202		return ArchMIPSEL, nil
203	case "mipsel64":
204		return ArchMIPSEL64, nil
205	case "mipsel64n32":
206		return ArchMIPSEL64N32, nil
207	case "ppc":
208		return ArchPPC, nil
209	case "ppc64":
210		return ArchPPC64, nil
211	case "ppc64le":
212		return ArchPPC64LE, nil
213	case "s390":
214		return ArchS390, nil
215	case "s390x":
216		return ArchS390X, nil
217	default:
218		return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %q", arch)
219	}
220}
221
222// String returns a string representation of an architecture constant
223func (a ScmpArch) String() string {
224	switch a {
225	case ArchX86:
226		return "x86"
227	case ArchAMD64:
228		return "amd64"
229	case ArchX32:
230		return "x32"
231	case ArchARM:
232		return "arm"
233	case ArchARM64:
234		return "arm64"
235	case ArchMIPS:
236		return "mips"
237	case ArchMIPS64:
238		return "mips64"
239	case ArchMIPS64N32:
240		return "mips64n32"
241	case ArchMIPSEL:
242		return "mipsel"
243	case ArchMIPSEL64:
244		return "mipsel64"
245	case ArchMIPSEL64N32:
246		return "mipsel64n32"
247	case ArchPPC:
248		return "ppc"
249	case ArchPPC64:
250		return "ppc64"
251	case ArchPPC64LE:
252		return "ppc64le"
253	case ArchS390:
254		return "s390"
255	case ArchS390X:
256		return "s390x"
257	case ArchNative:
258		return "native"
259	case ArchInvalid:
260		return "Invalid architecture"
261	default:
262		return fmt.Sprintf("Unknown architecture %#x", uint(a))
263	}
264}
265
266// String returns a string representation of a comparison operator constant
267func (a ScmpCompareOp) String() string {
268	switch a {
269	case CompareNotEqual:
270		return "Not equal"
271	case CompareLess:
272		return "Less than"
273	case CompareLessOrEqual:
274		return "Less than or equal to"
275	case CompareEqual:
276		return "Equal"
277	case CompareGreaterEqual:
278		return "Greater than or equal to"
279	case CompareGreater:
280		return "Greater than"
281	case CompareMaskedEqual:
282		return "Masked equality"
283	case CompareInvalid:
284		return "Invalid comparison operator"
285	default:
286		return fmt.Sprintf("Unrecognized comparison operator %#x", uint(a))
287	}
288}
289
290// String returns a string representation of a seccomp match action
291func (a ScmpAction) String() string {
292	switch a & 0xFFFF {
293	case ActKill:
294		return "Action: Kill Process"
295	case ActTrap:
296		return "Action: Send SIGSYS"
297	case ActErrno:
298		return fmt.Sprintf("Action: Return error code %d", (a >> 16))
299	case ActTrace:
300		return fmt.Sprintf("Action: Notify tracing processes with code %d",
301			(a >> 16))
302	case ActLog:
303		return "Action: Log system call"
304	case ActAllow:
305		return "Action: Allow system call"
306	default:
307		return fmt.Sprintf("Unrecognized Action %#x", uint(a))
308	}
309}
310
311// SetReturnCode adds a return code to a supporting ScmpAction, clearing any
312// existing code Only valid on ActErrno and ActTrace. Takes no action otherwise.
313// Accepts 16-bit return code as argument.
314// Returns a valid ScmpAction of the original type with the new error code set.
315func (a ScmpAction) SetReturnCode(code int16) ScmpAction {
316	aTmp := a & 0x0000FFFF
317	if aTmp == ActErrno || aTmp == ActTrace {
318		return (aTmp | (ScmpAction(code)&0xFFFF)<<16)
319	}
320	return a
321}
322
323// GetReturnCode returns the return code of an ScmpAction
324func (a ScmpAction) GetReturnCode() int16 {
325	return int16(a >> 16)
326}
327
328// General utility functions
329
330// GetLibraryVersion returns the version of the library the bindings are built
331// against.
332// The version is formatted as follows: Major.Minor.Micro
333func GetLibraryVersion() (major, minor, micro uint) {
334	return verMajor, verMinor, verMicro
335}
336
337// GetApi returns the API level supported by the system.
338// Returns a positive int containing the API level, or 0 with an error if the
339// API level could not be detected due to the library being older than v2.4.0.
340// See the seccomp_api_get(3) man page for details on available API levels:
341// https://github.com/seccomp/libseccomp/blob/master/doc/man/man3/seccomp_api_get.3
342func GetApi() (uint, error) {
343	return getApi()
344}
345
346// SetApi forcibly sets the API level. General use of this function is strongly
347// discouraged.
348// Returns an error if the API level could not be set. An error is always
349// returned if the library is older than v2.4.0
350// See the seccomp_api_get(3) man page for details on available API levels:
351// https://github.com/seccomp/libseccomp/blob/master/doc/man/man3/seccomp_api_get.3
352func SetApi(api uint) error {
353	return setApi(api)
354}
355
356// Syscall functions
357
358// GetName retrieves the name of a syscall from its number.
359// Acts on any syscall number.
360// Returns either a string containing the name of the syscall, or an error.
361func (s ScmpSyscall) GetName() (string, error) {
362	return s.GetNameByArch(ArchNative)
363}
364
365// GetNameByArch retrieves the name of a syscall from its number for a given
366// architecture.
367// Acts on any syscall number.
368// Accepts a valid architecture constant.
369// Returns either a string containing the name of the syscall, or an error.
370// if the syscall is unrecognized or an issue occurred.
371func (s ScmpSyscall) GetNameByArch(arch ScmpArch) (string, error) {
372	if err := sanitizeArch(arch); err != nil {
373		return "", err
374	}
375
376	cString := C.seccomp_syscall_resolve_num_arch(arch.toNative(), C.int(s))
377	if cString == nil {
378		return "", fmt.Errorf("could not resolve syscall name for %#x", int32(s))
379	}
380	defer C.free(unsafe.Pointer(cString))
381
382	finalStr := C.GoString(cString)
383	return finalStr, nil
384}
385
386// GetSyscallFromName returns the number of a syscall by name on the kernel's
387// native architecture.
388// Accepts a string containing the name of a syscall.
389// Returns the number of the syscall, or an error if no syscall with that name
390// was found.
391func GetSyscallFromName(name string) (ScmpSyscall, error) {
392	if err := ensureSupportedVersion(); err != nil {
393		return 0, err
394	}
395
396	cString := C.CString(name)
397	defer C.free(unsafe.Pointer(cString))
398
399	result := C.seccomp_syscall_resolve_name(cString)
400	if result == scmpError {
401		return 0, fmt.Errorf("could not resolve name to syscall: %q", name)
402	}
403
404	return ScmpSyscall(result), nil
405}
406
407// GetSyscallFromNameByArch returns the number of a syscall by name for a given
408// architecture's ABI.
409// Accepts the name of a syscall and an architecture constant.
410// Returns the number of the syscall, or an error if an invalid architecture is
411// passed or a syscall with that name was not found.
412func GetSyscallFromNameByArch(name string, arch ScmpArch) (ScmpSyscall, error) {
413	if err := ensureSupportedVersion(); err != nil {
414		return 0, err
415	}
416	if err := sanitizeArch(arch); err != nil {
417		return 0, err
418	}
419
420	cString := C.CString(name)
421	defer C.free(unsafe.Pointer(cString))
422
423	result := C.seccomp_syscall_resolve_name_arch(arch.toNative(), cString)
424	if result == scmpError {
425		return 0, fmt.Errorf("could not resolve name to syscall: %q on %v", name, arch)
426	}
427
428	return ScmpSyscall(result), nil
429}
430
431// MakeCondition creates and returns a new condition to attach to a filter rule.
432// Associated rules will only match if this condition is true.
433// Accepts the number the argument we are checking, and a comparison operator
434// and value to compare to.
435// The rule will match if argument $arg (zero-indexed) of the syscall is
436// $COMPARE_OP the provided comparison value.
437// Some comparison operators accept two values. Masked equals, for example,
438// will mask $arg of the syscall with the second value provided (via bitwise
439// AND) and then compare against the first value provided.
440// For example, in the less than or equal case, if the syscall argument was
441// 0 and the value provided was 1, the condition would match, as 0 is less
442// than or equal to 1.
443// Return either an error on bad argument or a valid ScmpCondition struct.
444func MakeCondition(arg uint, comparison ScmpCompareOp, values ...uint64) (ScmpCondition, error) {
445	var condStruct ScmpCondition
446
447	if err := ensureSupportedVersion(); err != nil {
448		return condStruct, err
449	}
450
451	if comparison == CompareInvalid {
452		return condStruct, fmt.Errorf("invalid comparison operator")
453	} else if arg > 5 {
454		return condStruct, fmt.Errorf("syscalls only have up to 6 arguments (%d given)", arg)
455	} else if len(values) > 2 {
456		return condStruct, fmt.Errorf("conditions can have at most 2 arguments (%d given)", len(values))
457	} else if len(values) == 0 {
458		return condStruct, fmt.Errorf("must provide at least one value to compare against")
459	}
460
461	condStruct.Argument = arg
462	condStruct.Op = comparison
463	condStruct.Operand1 = values[0]
464	if len(values) == 2 {
465		condStruct.Operand2 = values[1]
466	} else {
467		condStruct.Operand2 = 0 // Unused
468	}
469
470	return condStruct, nil
471}
472
473// Utility Functions
474
475// GetNativeArch returns architecture token representing the native kernel
476// architecture
477func GetNativeArch() (ScmpArch, error) {
478	if err := ensureSupportedVersion(); err != nil {
479		return ArchInvalid, err
480	}
481
482	arch := C.seccomp_arch_native()
483
484	return archFromNative(arch)
485}
486
487// Public Filter API
488
489// ScmpFilter represents a filter context in libseccomp.
490// A filter context is initially empty. Rules can be added to it, and it can
491// then be loaded into the kernel.
492type ScmpFilter struct {
493	filterCtx C.scmp_filter_ctx
494	valid     bool
495	lock      sync.Mutex
496}
497
498// NewFilter creates and returns a new filter context.
499// Accepts a default action to be taken for syscalls which match no rules in
500// the filter.
501// Returns a reference to a valid filter context, or nil and an error if the
502// filter context could not be created or an invalid default action was given.
503func NewFilter(defaultAction ScmpAction) (*ScmpFilter, error) {
504	if err := ensureSupportedVersion(); err != nil {
505		return nil, err
506	}
507
508	if err := sanitizeAction(defaultAction); err != nil {
509		return nil, err
510	}
511
512	fPtr := C.seccomp_init(defaultAction.toNative())
513	if fPtr == nil {
514		return nil, fmt.Errorf("could not create filter")
515	}
516
517	filter := new(ScmpFilter)
518	filter.filterCtx = fPtr
519	filter.valid = true
520	runtime.SetFinalizer(filter, filterFinalizer)
521
522	// Enable TSync so all goroutines will receive the same rules
523	// If the kernel does not support TSYNC, allow us to continue without error
524	if err := filter.setFilterAttr(filterAttrTsync, 0x1); err != nil && err != syscall.ENOTSUP {
525		filter.Release()
526		return nil, fmt.Errorf("could not create filter - error setting tsync bit: %v", err)
527	}
528
529	return filter, nil
530}
531
532// IsValid determines whether a filter context is valid to use.
533// Some operations (Release and Merge) render filter contexts invalid and
534// consequently prevent further use.
535func (f *ScmpFilter) IsValid() bool {
536	f.lock.Lock()
537	defer f.lock.Unlock()
538
539	return f.valid
540}
541
542// Reset resets a filter context, removing all its existing state.
543// Accepts a new default action to be taken for syscalls which do not match.
544// Returns an error if the filter or action provided are invalid.
545func (f *ScmpFilter) Reset(defaultAction ScmpAction) error {
546	f.lock.Lock()
547	defer f.lock.Unlock()
548
549	if err := sanitizeAction(defaultAction); err != nil {
550		return err
551	} else if !f.valid {
552		return errBadFilter
553	}
554
555	retCode := C.seccomp_reset(f.filterCtx, defaultAction.toNative())
556	if retCode != 0 {
557		return syscall.Errno(-1 * retCode)
558	}
559
560	return nil
561}
562
563// Release releases a filter context, freeing its memory. Should be called after
564// loading into the kernel, when the filter is no longer needed.
565// After calling this function, the given filter is no longer valid and cannot
566// be used.
567// Release() will be invoked automatically when a filter context is garbage
568// collected, but can also be called manually to free memory.
569func (f *ScmpFilter) Release() {
570	f.lock.Lock()
571	defer f.lock.Unlock()
572
573	if !f.valid {
574		return
575	}
576
577	f.valid = false
578	C.seccomp_release(f.filterCtx)
579}
580
581// Merge merges two filter contexts.
582// The source filter src will be released as part of the process, and will no
583// longer be usable or valid after this call.
584// To be merged, filters must NOT share any architectures, and all their
585// attributes (Default Action, Bad Arch Action, and No New Privs bools)
586// must match.
587// The filter src will be merged into the filter this is called on.
588// The architectures of the src filter not present in the destination, and all
589// associated rules, will be added to the destination.
590// Returns an error if merging the filters failed.
591func (f *ScmpFilter) Merge(src *ScmpFilter) error {
592	f.lock.Lock()
593	defer f.lock.Unlock()
594
595	src.lock.Lock()
596	defer src.lock.Unlock()
597
598	if !src.valid || !f.valid {
599		return fmt.Errorf("one or more of the filter contexts is invalid or uninitialized")
600	}
601
602	// Merge the filters
603	retCode := C.seccomp_merge(f.filterCtx, src.filterCtx)
604	if syscall.Errno(-1*retCode) == syscall.EINVAL {
605		return fmt.Errorf("filters could not be merged due to a mismatch in attributes or invalid filter")
606	} else if retCode != 0 {
607		return syscall.Errno(-1 * retCode)
608	}
609
610	src.valid = false
611
612	return nil
613}
614
615// IsArchPresent checks if an architecture is present in a filter.
616// If a filter contains an architecture, it uses its default action for
617// syscalls which do not match rules in it, and its rules can match syscalls
618// for that ABI.
619// If a filter does not contain an architecture, all syscalls made to that
620// kernel ABI will fail with the filter's default Bad Architecture Action
621// (by default, killing the process).
622// Accepts an architecture constant.
623// Returns true if the architecture is present in the filter, false otherwise,
624// and an error on an invalid filter context, architecture constant, or an
625// issue with the call to libseccomp.
626func (f *ScmpFilter) IsArchPresent(arch ScmpArch) (bool, error) {
627	f.lock.Lock()
628	defer f.lock.Unlock()
629
630	if err := sanitizeArch(arch); err != nil {
631		return false, err
632	} else if !f.valid {
633		return false, errBadFilter
634	}
635
636	retCode := C.seccomp_arch_exist(f.filterCtx, arch.toNative())
637	if syscall.Errno(-1*retCode) == syscall.EEXIST {
638		// -EEXIST is "arch not present"
639		return false, nil
640	} else if retCode != 0 {
641		return false, syscall.Errno(-1 * retCode)
642	}
643
644	return true, nil
645}
646
647// AddArch adds an architecture to the filter.
648// Accepts an architecture constant.
649// Returns an error on invalid filter context or architecture token, or an
650// issue with the call to libseccomp.
651func (f *ScmpFilter) AddArch(arch ScmpArch) error {
652	f.lock.Lock()
653	defer f.lock.Unlock()
654
655	if err := sanitizeArch(arch); err != nil {
656		return err
657	} else if !f.valid {
658		return errBadFilter
659	}
660
661	// Libseccomp returns -EEXIST if the specified architecture is already
662	// present. Succeed silently in this case, as it's not fatal, and the
663	// architecture is present already.
664	retCode := C.seccomp_arch_add(f.filterCtx, arch.toNative())
665	if retCode != 0 && syscall.Errno(-1*retCode) != syscall.EEXIST {
666		return syscall.Errno(-1 * retCode)
667	}
668
669	return nil
670}
671
672// RemoveArch removes an architecture from the filter.
673// Accepts an architecture constant.
674// Returns an error on invalid filter context or architecture token, or an
675// issue with the call to libseccomp.
676func (f *ScmpFilter) RemoveArch(arch ScmpArch) error {
677	f.lock.Lock()
678	defer f.lock.Unlock()
679
680	if err := sanitizeArch(arch); err != nil {
681		return err
682	} else if !f.valid {
683		return errBadFilter
684	}
685
686	// Similar to AddArch, -EEXIST is returned if the arch is not present
687	// Succeed silently in that case, this is not fatal and the architecture
688	// is not present in the filter after RemoveArch
689	retCode := C.seccomp_arch_remove(f.filterCtx, arch.toNative())
690	if retCode != 0 && syscall.Errno(-1*retCode) != syscall.EEXIST {
691		return syscall.Errno(-1 * retCode)
692	}
693
694	return nil
695}
696
697// Load loads a filter context into the kernel.
698// Returns an error if the filter context is invalid or the syscall failed.
699func (f *ScmpFilter) Load() error {
700	f.lock.Lock()
701	defer f.lock.Unlock()
702
703	if !f.valid {
704		return errBadFilter
705	}
706
707	if retCode := C.seccomp_load(f.filterCtx); retCode != 0 {
708		return syscall.Errno(-1 * retCode)
709	}
710
711	return nil
712}
713
714// GetDefaultAction returns the default action taken on a syscall which does not
715// match a rule in the filter, or an error if an issue was encountered
716// retrieving the value.
717func (f *ScmpFilter) GetDefaultAction() (ScmpAction, error) {
718	action, err := f.getFilterAttr(filterAttrActDefault)
719	if err != nil {
720		return 0x0, err
721	}
722
723	return actionFromNative(action)
724}
725
726// GetBadArchAction returns the default action taken on a syscall for an
727// architecture not in the filter, or an error if an issue was encountered
728// retrieving the value.
729func (f *ScmpFilter) GetBadArchAction() (ScmpAction, error) {
730	action, err := f.getFilterAttr(filterAttrActBadArch)
731	if err != nil {
732		return 0x0, err
733	}
734
735	return actionFromNative(action)
736}
737
738// GetNoNewPrivsBit returns the current state the No New Privileges bit will be set
739// to on the filter being loaded, or an error if an issue was encountered
740// retrieving the value.
741// The No New Privileges bit tells the kernel that new processes run with exec()
742// cannot gain more privileges than the process that ran exec().
743// For example, a process with No New Privileges set would be unable to exec
744// setuid/setgid executables.
745func (f *ScmpFilter) GetNoNewPrivsBit() (bool, error) {
746	noNewPrivs, err := f.getFilterAttr(filterAttrNNP)
747	if err != nil {
748		return false, err
749	}
750
751	if noNewPrivs == 0 {
752		return false, nil
753	}
754
755	return true, nil
756}
757
758// GetLogBit returns the current state the Log bit will be set to on the filter
759// being loaded, or an error if an issue was encountered retrieving the value.
760// The Log bit tells the kernel that all actions taken by the filter, with the
761// exception of ActAllow, should be logged.
762// The Log bit is only usable when libseccomp API level 3 or higher is
763// supported.
764func (f *ScmpFilter) GetLogBit() (bool, error) {
765	log, err := f.getFilterAttr(filterAttrLog)
766	if err != nil {
767		api, apiErr := getApi()
768		if (apiErr != nil && api == 0) || (apiErr == nil && api < 3) {
769			return false, fmt.Errorf("getting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher")
770		}
771
772		return false, err
773	}
774
775	if log == 0 {
776		return false, nil
777	}
778
779	return true, nil
780}
781
782// SetBadArchAction sets the default action taken on a syscall for an
783// architecture not in the filter, or an error if an issue was encountered
784// setting the value.
785func (f *ScmpFilter) SetBadArchAction(action ScmpAction) error {
786	if err := sanitizeAction(action); err != nil {
787		return err
788	}
789
790	return f.setFilterAttr(filterAttrActBadArch, action.toNative())
791}
792
793// SetNoNewPrivsBit sets the state of the No New Privileges bit, which will be
794// applied on filter load, or an error if an issue was encountered setting the
795// value.
796// Filters with No New Privileges set to 0 can only be loaded if the process
797// has the CAP_SYS_ADMIN capability.
798func (f *ScmpFilter) SetNoNewPrivsBit(state bool) error {
799	var toSet C.uint32_t = 0x0
800
801	if state {
802		toSet = 0x1
803	}
804
805	return f.setFilterAttr(filterAttrNNP, toSet)
806}
807
808// SetLogBit sets the state of the Log bit, which will be applied on filter
809// load, or an error if an issue was encountered setting the value.
810// The Log bit is only usable when libseccomp API level 3 or higher is
811// supported.
812func (f *ScmpFilter) SetLogBit(state bool) error {
813	var toSet C.uint32_t = 0x0
814
815	if state {
816		toSet = 0x1
817	}
818
819	err := f.setFilterAttr(filterAttrLog, toSet)
820	if err != nil {
821		api, apiErr := getApi()
822		if (apiErr != nil && api == 0) || (apiErr == nil && api < 3) {
823			return fmt.Errorf("setting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher")
824		}
825	}
826
827	return err
828}
829
830// SetSyscallPriority sets a syscall's priority.
831// This provides a hint to the filter generator in libseccomp about the
832// importance of this syscall. High-priority syscalls are placed
833// first in the filter code, and incur less overhead (at the expense of
834// lower-priority syscalls).
835func (f *ScmpFilter) SetSyscallPriority(call ScmpSyscall, priority uint8) error {
836	f.lock.Lock()
837	defer f.lock.Unlock()
838
839	if !f.valid {
840		return errBadFilter
841	}
842
843	if retCode := C.seccomp_syscall_priority(f.filterCtx, C.int(call),
844		C.uint8_t(priority)); retCode != 0 {
845		return syscall.Errno(-1 * retCode)
846	}
847
848	return nil
849}
850
851// AddRule adds a single rule for an unconditional action on a syscall.
852// Accepts the number of the syscall and the action to be taken on the call
853// being made.
854// Returns an error if an issue was encountered adding the rule.
855func (f *ScmpFilter) AddRule(call ScmpSyscall, action ScmpAction) error {
856	return f.addRuleGeneric(call, action, false, nil)
857}
858
859// AddRuleExact adds a single rule for an unconditional action on a syscall.
860// Accepts the number of the syscall and the action to be taken on the call
861// being made.
862// No modifications will be made to the rule, and it will fail to add if it
863// cannot be applied to the current architecture without modification.
864// The rule will function exactly as described, but it may not function identically
865// (or be able to be applied to) all architectures.
866// Returns an error if an issue was encountered adding the rule.
867func (f *ScmpFilter) AddRuleExact(call ScmpSyscall, action ScmpAction) error {
868	return f.addRuleGeneric(call, action, true, nil)
869}
870
871// AddRuleConditional adds a single rule for a conditional action on a syscall.
872// Returns an error if an issue was encountered adding the rule.
873// All conditions must match for the rule to match.
874// There is a bug in library versions below v2.2.1 which can, in some cases,
875// cause conditions to be lost when more than one are used. Consequently,
876// AddRuleConditional is disabled on library versions lower than v2.2.1
877func (f *ScmpFilter) AddRuleConditional(call ScmpSyscall, action ScmpAction, conds []ScmpCondition) error {
878	return f.addRuleGeneric(call, action, false, conds)
879}
880
881// AddRuleConditionalExact adds a single rule for a conditional action on a
882// syscall.
883// No modifications will be made to the rule, and it will fail to add if it
884// cannot be applied to the current architecture without modification.
885// The rule will function exactly as described, but it may not function identically
886// (or be able to be applied to) all architectures.
887// Returns an error if an issue was encountered adding the rule.
888// There is a bug in library versions below v2.2.1 which can, in some cases,
889// cause conditions to be lost when more than one are used. Consequently,
890// AddRuleConditionalExact is disabled on library versions lower than v2.2.1
891func (f *ScmpFilter) AddRuleConditionalExact(call ScmpSyscall, action ScmpAction, conds []ScmpCondition) error {
892	return f.addRuleGeneric(call, action, true, conds)
893}
894
895// ExportPFC output PFC-formatted, human-readable dump of a filter context's
896// rules to a file.
897// Accepts file to write to (must be open for writing).
898// Returns an error if writing to the file fails.
899func (f *ScmpFilter) ExportPFC(file *os.File) error {
900	f.lock.Lock()
901	defer f.lock.Unlock()
902
903	fd := file.Fd()
904
905	if !f.valid {
906		return errBadFilter
907	}
908
909	if retCode := C.seccomp_export_pfc(f.filterCtx, C.int(fd)); retCode != 0 {
910		return syscall.Errno(-1 * retCode)
911	}
912
913	return nil
914}
915
916// ExportBPF outputs Berkeley Packet Filter-formatted, kernel-readable dump of a
917// filter context's rules to a file.
918// Accepts file to write to (must be open for writing).
919// Returns an error if writing to the file fails.
920func (f *ScmpFilter) ExportBPF(file *os.File) error {
921	f.lock.Lock()
922	defer f.lock.Unlock()
923
924	fd := file.Fd()
925
926	if !f.valid {
927		return errBadFilter
928	}
929
930	if retCode := C.seccomp_export_bpf(f.filterCtx, C.int(fd)); retCode != 0 {
931		return syscall.Errno(-1 * retCode)
932	}
933
934	return nil
935}
936