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