diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3388578 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +OS := $(shell uname -s| tr '[:upper:]' '[:lower:]') +ARCH := amd64 + +GO := GOOS=$(OS) GOARCH=$(ARCH) GO15VENDOREXPERIMENT=1 go +GOGETTER := GOPATH=$(shell pwd)/.tmpdeps go get -d + +all: build + +build: + $(GO) build + +go_vendor_dependencies: + $(GOGETTER) github.com/seccomp/libseccomp-golang + echo 'removing .git from vendored pkg and moving them to vendor' + find .tmpdeps/src -type d -name ".git" ! -name ".gitignore" -exec rm -rf {} \; || exit 0 + cp -ar .tmpdeps/src/* vendor/ + rm -rf .tmpdeps + diff --git a/vendor/seccomp/libseccomp-golang/LICENSE b/vendor/seccomp/libseccomp-golang/LICENSE new file mode 100644 index 0000000..81cf60d --- /dev/null +++ b/vendor/seccomp/libseccomp-golang/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2015 Matthew Heon +Copyright (c) 2015 Paul Moore +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/seccomp/libseccomp-golang/README b/vendor/seccomp/libseccomp-golang/README new file mode 100644 index 0000000..64cab69 --- /dev/null +++ b/vendor/seccomp/libseccomp-golang/README @@ -0,0 +1,26 @@ +libseccomp-golang: Go Language Bindings for the libseccomp Project +=============================================================================== +https://github.com/seccomp/libseccomp-golang +https://github.com/seccomp/libseccomp + +The libseccomp library provides an easy to use, platform independent, interface +to the Linux Kernel's syscall filtering mechanism. The libseccomp API is +designed to abstract away the underlying BPF based syscall filter language and +present a more conventional function-call based filtering interface that should +be familiar to, and easily adopted by, application developers. + +The libseccomp-golang library provides a Go based interface to the libseccomp +library. + +* Online Resources + +The library source repository currently lives on GitHub at the following URLs: + + -> https://github.com/seccomp/libseccomp-golang + -> https://github.com/seccomp/libseccomp + +The project mailing list is currently hosted on Google Groups at the URL below, +please note that a Google account is not required to subscribe to the mailing +list. + + -> https://groups.google.com/d/forum/libseccomp diff --git a/vendor/seccomp/libseccomp-golang/seccomp.go b/vendor/seccomp/libseccomp-golang/seccomp.go new file mode 100644 index 0000000..cebafdf --- /dev/null +++ b/vendor/seccomp/libseccomp-golang/seccomp.go @@ -0,0 +1,827 @@ +// +build linux + +// Public API specification for libseccomp Go bindings +// Contains public API for the bindings + +// Package seccomp rovides bindings for libseccomp, a library wrapping the Linux +// seccomp syscall. Seccomp enables an application to restrict system call use +// for itself and its children. +package seccomp + +import ( + "fmt" + "os" + "runtime" + "strings" + "sync" + "syscall" + "unsafe" +) + +// C wrapping code + +// #cgo LDFLAGS: -lseccomp +// #include +// #include +import "C" + +// Exported types + +// ScmpArch represents a CPU architecture. Seccomp can restrict syscalls on a +// per-architecture basis. +type ScmpArch uint + +// ScmpAction represents an action to be taken on a filter rule match in +// libseccomp +type ScmpAction uint + +// ScmpCompareOp represents a comparison operator which can be used in a filter +// rule +type ScmpCompareOp uint + +// ScmpCondition represents a rule in a libseccomp filter context +type ScmpCondition struct { + Argument uint `json:"argument,omitempty"` + Op ScmpCompareOp `json:"operator,omitempty"` + Operand1 uint64 `json:"operand_one,omitempty"` + Operand2 uint64 `json:"operand_two,omitempty"` +} + +// ScmpSyscall represents a Linux System Call +type ScmpSyscall int32 + +// Exported Constants + +const ( + // Valid architectures recognized by libseccomp + // ARM64 and all MIPS architectures are unsupported by versions of the + // library before v2.2 and will return errors if used + + // ArchInvalid is a placeholder to ensure uninitialized ScmpArch + // variables are invalid + ArchInvalid ScmpArch = iota + // ArchNative is the native architecture of the kernel + ArchNative ScmpArch = iota + // ArchX86 represents 32-bit x86 syscalls + ArchX86 ScmpArch = iota + // ArchAMD64 represents 64-bit x86-64 syscalls + ArchAMD64 ScmpArch = iota + // ArchX32 represents 64-bit x86-64 syscalls (32-bit pointers) + ArchX32 ScmpArch = iota + // ArchARM represents 32-bit ARM syscalls + ArchARM ScmpArch = iota + // ArchARM64 represents 64-bit ARM syscalls + ArchARM64 ScmpArch = iota + // ArchMIPS represents 32-bit MIPS syscalls + ArchMIPS ScmpArch = iota + // ArchMIPS64 represents 64-bit MIPS syscalls + ArchMIPS64 ScmpArch = iota + // ArchMIPS64N32 represents 64-bit MIPS syscalls (32-bit pointers) + ArchMIPS64N32 ScmpArch = iota + // ArchMIPSEL represents 32-bit MIPS syscalls (little endian) + ArchMIPSEL ScmpArch = iota + // ArchMIPSEL64 represents 64-bit MIPS syscalls (little endian) + ArchMIPSEL64 ScmpArch = iota + // ArchMIPSEL64N32 represents 64-bit MIPS syscalls (little endian, + // 32-bit pointers) + ArchMIPSEL64N32 ScmpArch = iota +) + +const ( + // Supported actions on filter match + + // ActInvalid is a placeholder to ensure uninitialized ScmpAction + // variables are invalid + ActInvalid ScmpAction = iota + // ActKill kills the process + ActKill ScmpAction = iota + // ActTrap throws SIGSYS + ActTrap ScmpAction = iota + // ActErrno causes the syscall to return a negative error code. This + // code can be set with the SetReturnCode method + ActErrno ScmpAction = iota + // ActTrace causes the syscall to notify tracing processes with the + // given error code. This code can be set with the SetReturnCode method + ActTrace ScmpAction = iota + // ActAllow permits the syscall to continue execution + ActAllow ScmpAction = iota +) + +const ( + // These are comparison operators used in conditional seccomp rules + // They are used to compare the value of a single argument of a syscall + // against a user-defined constant + + // CompareInvalid is a placeholder to ensure uninitialized ScmpCompareOp + // variables are invalid + CompareInvalid ScmpCompareOp = iota + // CompareNotEqual returns true if the argument is not equal to the + // given value + CompareNotEqual ScmpCompareOp = iota + // CompareLess returns true if the argument is less than the given value + CompareLess ScmpCompareOp = iota + // CompareLessOrEqual returns true if the argument is less than or equal + // to the given value + CompareLessOrEqual ScmpCompareOp = iota + // CompareEqual returns true if the argument is equal to the given value + CompareEqual ScmpCompareOp = iota + // CompareGreaterEqual returns true if the argument is greater than or + // equal to the given value + CompareGreaterEqual ScmpCompareOp = iota + // CompareGreater returns true if the argument is greater than the given + // value + CompareGreater ScmpCompareOp = iota + // CompareMaskedEqual returns true if the argument is equal to the given + // value, when masked (bitwise &) against the second given value + CompareMaskedEqual ScmpCompareOp = iota +) + +// Helpers for types + +// GetArchFromString returns an ScmpArch constant from a string representing an +// architecture +func GetArchFromString(arch string) (ScmpArch, error) { + switch strings.ToLower(arch) { + case "x86": + return ArchX86, nil + case "amd64", "x86-64", "x86_64", "x64": + return ArchAMD64, nil + case "x32": + return ArchX32, nil + case "arm": + return ArchARM, nil + case "arm64", "aarch64": + return ArchARM64, nil + case "mips": + return ArchMIPS, nil + case "mips64": + return ArchMIPS64, nil + case "mips64n32": + return ArchMIPS64N32, nil + case "mipsel": + return ArchMIPSEL, nil + case "mipsel64": + return ArchMIPSEL64, nil + case "mipsel64n32": + return ArchMIPSEL64N32, nil + default: + return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %s", arch) + } +} + +// String returns a string representation of an architecture constant +func (a ScmpArch) String() string { + switch a { + case ArchX86: + return "x86" + case ArchAMD64: + return "amd64" + case ArchX32: + return "x32" + case ArchARM: + return "arm" + case ArchARM64: + return "arm64" + case ArchMIPS: + return "mips" + case ArchMIPS64: + return "mips64" + case ArchMIPS64N32: + return "mips64n32" + case ArchMIPSEL: + return "mipsel" + case ArchMIPSEL64: + return "mipsel64" + case ArchMIPSEL64N32: + return "mipsel64n32" + case ArchNative: + return "native" + case ArchInvalid: + return "Invalid architecture" + default: + return "Unknown architecture" + } +} + +// String returns a string representation of a comparison operator constant +func (a ScmpCompareOp) String() string { + switch a { + case CompareNotEqual: + return "Not equal" + case CompareLess: + return "Less than" + case CompareLessOrEqual: + return "Less than or equal to" + case CompareEqual: + return "Equal" + case CompareGreaterEqual: + return "Greater than or equal to" + case CompareGreater: + return "Greater than" + case CompareMaskedEqual: + return "Masked equality" + case CompareInvalid: + return "Invalid comparison operator" + default: + return "Unrecognized comparison operator" + } +} + +// String returns a string representation of a seccomp match action +func (a ScmpAction) String() string { + switch a & 0xFFFF { + case ActKill: + return "Action: Kill Process" + case ActTrap: + return "Action: Send SIGSYS" + case ActErrno: + return fmt.Sprintf("Action: Return error code %d", (a >> 16)) + case ActTrace: + return fmt.Sprintf("Action: Notify tracing processes with code %d", + (a >> 16)) + case ActAllow: + return "Action: Allow system call" + default: + return "Unrecognized Action" + } +} + +// SetReturnCode adds a return code to a supporting ScmpAction, clearing any +// existing code Only valid on ActErrno and ActTrace. Takes no action otherwise. +// Accepts 16-bit return code as argument. +// Returns a valid ScmpAction of the original type with the new error code set. +func (a ScmpAction) SetReturnCode(code int16) ScmpAction { + aTmp := a & 0x0000FFFF + if aTmp == ActErrno || aTmp == ActTrace { + return (aTmp | (ScmpAction(code)&0xFFFF)<<16) + } + return a +} + +// GetReturnCode returns the return code of an ScmpAction +func (a ScmpAction) GetReturnCode() int16 { + return int16(a >> 16) +} + +// General utility functions + +// GetLibraryVersion returns the version of the library the bindings are built +// against. +// The version is formatted as follows: Major.Minor.Micro +func GetLibraryVersion() (major, minor, micro int) { + return verMajor, verMinor, verMicro +} + +// Syscall functions + +// GetName retrieves the name of a syscall from its number. +// Acts on any syscall number. +// Returns either a string containing the name of the syscall, or an error. +func (s ScmpSyscall) GetName() (string, error) { + return s.GetNameByArch(ArchNative) +} + +// GetNameByArch retrieves the name of a syscall from its number for a given +// architecture. +// Acts on any syscall number. +// Accepts a valid architecture constant. +// Returns either a string containing the name of the syscall, or an error. +// if the syscall is unrecognized or an issue occurred. +func (s ScmpSyscall) GetNameByArch(arch ScmpArch) (string, error) { + if err := sanitizeArch(arch); err != nil { + return "", err + } + + cString := C.seccomp_syscall_resolve_num_arch(arch.toNative(), C.int(s)) + if cString == nil { + return "", fmt.Errorf("could not resolve syscall name") + } + defer C.free(unsafe.Pointer(cString)) + + finalStr := C.GoString(cString) + return finalStr, nil +} + +// GetSyscallFromName returns the number of a syscall by name on the kernel's +// native architecture. +// Accepts a string containing the name of a syscall. +// Returns the number of the syscall, or an error if no syscall with that name +// was found. +func GetSyscallFromName(name string) (ScmpSyscall, error) { + cString := C.CString(name) + defer C.free(unsafe.Pointer(cString)) + + result := C.seccomp_syscall_resolve_name(cString) + if result == scmpError { + return 0, fmt.Errorf("could not resolve name to syscall") + } + + return ScmpSyscall(result), nil +} + +// GetSyscallFromNameByArch returns the number of a syscall by name for a given +// architecture's ABI. +// Accepts the name of a syscall and an architecture constant. +// Returns the number of the syscall, or an error if an invalid architecture is +// passed or a syscall with that name was not found. +func GetSyscallFromNameByArch(name string, arch ScmpArch) (ScmpSyscall, error) { + if err := sanitizeArch(arch); err != nil { + return 0, err + } + + cString := C.CString(name) + defer C.free(unsafe.Pointer(cString)) + + result := C.seccomp_syscall_resolve_name_arch(arch.toNative(), cString) + if result == scmpError { + return 0, fmt.Errorf("could not resolve name to syscall") + } + + return ScmpSyscall(result), nil +} + +// MakeCondition creates and returns a new condition to attach to a filter rule. +// Associated rules will only match if this condition is true. +// Accepts the number the argument we are checking, and a comparison operator +// and value to compare to. +// The rule will match if argument $arg (zero-indexed) of the syscall is +// $COMPARE_OP the provided comparison value. +// Some comparison operators accept two values. Masked equals, for example, +// will mask $arg of the syscall with the second value provided (via bitwise +// AND) and then compare against the first value provided. +// For example, in the less than or equal case, if the syscall argument was +// 0 and the value provided was 1, the condition would match, as 0 is less +// than or equal to 1. +// Return either an error on bad argument or a valid ScmpCondition struct. +func MakeCondition(arg uint, comparison ScmpCompareOp, values ...uint64) (ScmpCondition, error) { + var condStruct ScmpCondition + + if comparison == CompareInvalid { + return condStruct, fmt.Errorf("invalid comparison operator") + } else if arg > 5 { + return condStruct, fmt.Errorf("syscalls only have up to 6 arguments") + } else if len(values) > 2 { + return condStruct, fmt.Errorf("conditions can have at most 2 arguments") + } else if len(values) == 0 { + return condStruct, fmt.Errorf("must provide at least one value to compare against") + } + + condStruct.Argument = arg + condStruct.Op = comparison + condStruct.Operand1 = values[0] + if len(values) == 2 { + condStruct.Operand2 = values[1] + } else { + condStruct.Operand2 = 0 // Unused + } + + return condStruct, nil +} + +// Utility Functions + +// GetNativeArch returns architecture token representing the native kernel +// architecture +func GetNativeArch() (ScmpArch, error) { + arch := C.seccomp_arch_native() + + return archFromNative(arch) +} + +// Public Filter API + +// ScmpFilter represents a filter context in libseccomp. +// A filter context is initially empty. Rules can be added to it, and it can +// then be loaded into the kernel. +type ScmpFilter struct { + filterCtx C.scmp_filter_ctx + valid bool + lock sync.Mutex +} + +// NewFilter creates and returns a new filter context. +// Accepts a default action to be taken for syscalls which match no rules in +// the filter. +// Returns a reference to a valid filter context, or nil and an error if the +// filter context could not be created or an invalid default action was given. +func NewFilter(defaultAction ScmpAction) (*ScmpFilter, error) { + if err := sanitizeAction(defaultAction); err != nil { + return nil, err + } + + fPtr := C.seccomp_init(defaultAction.toNative()) + if fPtr == nil { + return nil, fmt.Errorf("could not create filter") + } + + filter := new(ScmpFilter) + filter.filterCtx = fPtr + filter.valid = true + runtime.SetFinalizer(filter, filterFinalizer) + + return filter, nil +} + +// IsValid determines whether a filter context is valid to use. +// Some operations (Release and Merge) render filter contexts invalid and +// consequently prevent further use. +func (f *ScmpFilter) IsValid() bool { + f.lock.Lock() + defer f.lock.Unlock() + + return f.valid +} + +// Reset resets a filter context, removing all its existing state. +// Accepts a new default action to be taken for syscalls which do not match. +// Returns an error if the filter or action provided are invalid. +func (f *ScmpFilter) Reset(defaultAction ScmpAction) error { + f.lock.Lock() + defer f.lock.Unlock() + + if err := sanitizeAction(defaultAction); err != nil { + return err + } else if !f.valid { + return errBadFilter + } + + retCode := C.seccomp_reset(f.filterCtx, defaultAction.toNative()) + if retCode != 0 { + return syscall.Errno(-1 * retCode) + } + + return nil +} + +// Release releases a filter context, freeing its memory. Should be called after +// loading into the kernel, when the filter is no longer needed. +// After calling this function, the given filter is no longer valid and cannot +// be used. +// Release() will be invoked automatically when a filter context is garbage +// collected, but can also be called manually to free memory. +func (f *ScmpFilter) Release() { + f.lock.Lock() + defer f.lock.Unlock() + + if !f.valid { + return + } + + f.valid = false + C.seccomp_release(f.filterCtx) +} + +// Merge merges two filter contexts. +// The source filter src will be released as part of the process, and will no +// longer be usable or valid after this call. +// To be merged, filters must NOT share any architectures, and all their +// attributes (Default Action, Bad Arch Action, No New Privs and TSync bools) +// must match. +// The filter src will be merged into the filter this is called on. +// The architectures of the src filter not present in the destination, and all +// associated rules, will be added to the destination. +// Returns an error if merging the filters failed. +func (f *ScmpFilter) Merge(src *ScmpFilter) error { + f.lock.Lock() + defer f.lock.Unlock() + + src.lock.Lock() + defer src.lock.Unlock() + + if !src.valid || !f.valid { + return fmt.Errorf("one or more of the filter contexts is invalid or uninitialized") + } + + // Merge the filters + retCode := C.seccomp_merge(f.filterCtx, src.filterCtx) + if syscall.Errno(-1*retCode) == syscall.EINVAL { + return fmt.Errorf("filters could not be merged due to a mismatch in attributes or invalid filter") + } else if retCode != 0 { + return syscall.Errno(-1 * retCode) + } + + src.valid = false + + return nil +} + +// IsArchPresent checks if an architecture is present in a filter. +// If a filter contains an architecture, it uses its default action for +// syscalls which do not match rules in it, and its rules can match syscalls +// for that ABI. +// If a filter does not contain an architecture, all syscalls made to that +// kernel ABI will fail with the filter's default Bad Architecture Action +// (by default, killing the process). +// Accepts an architecture constant. +// Returns true if the architecture is present in the filter, false otherwise, +// and an error on an invalid filter context, architecture constant, or an +// issue with the call to libseccomp. +func (f *ScmpFilter) IsArchPresent(arch ScmpArch) (bool, error) { + f.lock.Lock() + defer f.lock.Unlock() + + if err := sanitizeArch(arch); err != nil { + return false, err + } else if !f.valid { + return false, errBadFilter + } + + retCode := C.seccomp_arch_exist(f.filterCtx, arch.toNative()) + if syscall.Errno(-1*retCode) == syscall.EEXIST { + // -EEXIST is "arch not present" + return false, nil + } else if retCode != 0 { + return false, syscall.Errno(-1 * retCode) + } + + return true, nil +} + +// AddArch adds an architecture to the filter. +// Accepts an architecture constant. +// Returns an error on invalid filter context or architecture token, or an +// issue with the call to libseccomp. +func (f *ScmpFilter) AddArch(arch ScmpArch) error { + f.lock.Lock() + defer f.lock.Unlock() + + if err := sanitizeArch(arch); err != nil { + return err + } else if !f.valid { + return errBadFilter + } + + // Libseccomp returns -EEXIST if the specified architecture is already + // present. Succeed silently in this case, as it's not fatal, and the + // architecture is present already. + retCode := C.seccomp_arch_add(f.filterCtx, arch.toNative()) + if retCode != 0 && syscall.Errno(-1*retCode) != syscall.EEXIST { + return syscall.Errno(-1 * retCode) + } + + return nil +} + +// RemoveArch removes an architecture from the filter. +// Accepts an architecture constant. +// Returns an error on invalid filter context or architecture token, or an +// issue with the call to libseccomp. +func (f *ScmpFilter) RemoveArch(arch ScmpArch) error { + f.lock.Lock() + defer f.lock.Unlock() + + if err := sanitizeArch(arch); err != nil { + return err + } else if !f.valid { + return errBadFilter + } + + // Similar to AddArch, -EEXIST is returned if the arch is not present + // Succeed silently in that case, this is not fatal and the architecture + // is not present in the filter after RemoveArch + retCode := C.seccomp_arch_remove(f.filterCtx, arch.toNative()) + if retCode != 0 && syscall.Errno(-1*retCode) != syscall.EEXIST { + return syscall.Errno(-1 * retCode) + } + + return nil +} + +// Load loads a filter context into the kernel. +// Returns an error if the filter context is invalid or the syscall failed. +func (f *ScmpFilter) Load() error { + f.lock.Lock() + defer f.lock.Unlock() + + if !f.valid { + return errBadFilter + } + + if retCode := C.seccomp_load(f.filterCtx); retCode != 0 { + return syscall.Errno(-1 * retCode) + } + + return nil +} + +// GetDefaultAction returns the default action taken on a syscall which does not +// match a rule in the filter, or an error if an issue was encountered +// retrieving the value. +func (f *ScmpFilter) GetDefaultAction() (ScmpAction, error) { + action, err := f.getFilterAttr(filterAttrActDefault) + if err != nil { + return 0x0, err + } + + return actionFromNative(action) +} + +// GetBadArchAction returns the default action taken on a syscall for an +// architecture not in the filter, or an error if an issue was encountered +// retrieving the value. +func (f *ScmpFilter) GetBadArchAction() (ScmpAction, error) { + action, err := f.getFilterAttr(filterAttrActBadArch) + if err != nil { + return 0x0, err + } + + return actionFromNative(action) +} + +// GetNoNewPrivsBit returns the current state the No New Privileges bit will be set +// to on the filter being loaded, or an error if an issue was encountered +// retrieving the value. +// The No New Privileges bit tells the kernel that new processes run with exec() +// cannot gain more privileges than the process that ran exec(). +// For example, a process with No New Privileges set would be unable to exec +// setuid/setgid executables. +func (f *ScmpFilter) GetNoNewPrivsBit() (bool, error) { + noNewPrivs, err := f.getFilterAttr(filterAttrNNP) + if err != nil { + return false, err + } + + if noNewPrivs == 0 { + return false, nil + } + + return true, nil +} + +// GetTsyncBit returns whether Thread Synchronization will be enabled on the +// filter being loaded, or an error if an issue was encountered retrieving the +// value. +// Thread Sync ensures that all members of the thread group of the calling +// process will share the same Seccomp filter set. +// Tsync is a fairly recent addition to the Linux kernel and older kernels +// lack support. If the running kernel does not support Tsync and it is +// requested in a filter, Libseccomp will not enable TSync support and will +// proceed as normal. +// This function is unavailable before v2.2 of libseccomp and will return an +// error. +func (f *ScmpFilter) GetTsyncBit() (bool, error) { + tSync, err := f.getFilterAttr(filterAttrTsync) + if err != nil { + return false, err + } + + if tSync == 0 { + return false, nil + } + + return true, nil +} + +// SetBadArchAction sets the default action taken on a syscall for an +// architecture not in the filter, or an error if an issue was encountered +// setting the value. +func (f *ScmpFilter) SetBadArchAction(action ScmpAction) error { + if err := sanitizeAction(action); err != nil { + return err + } + + return f.setFilterAttr(filterAttrActBadArch, action.toNative()) +} + +// SetNoNewPrivsBit sets the state of the No New Privileges bit, which will be +// applied on filter load, or an error if an issue was encountered setting the +// value. +// Filters with No New Privileges set to 0 can only be loaded if the process +// has the CAP_SYS_ADMIN capability. +func (f *ScmpFilter) SetNoNewPrivsBit(state bool) error { + var toSet C.uint32_t = 0x0 + + if state { + toSet = 0x1 + } + + return f.setFilterAttr(filterAttrNNP, toSet) +} + +// SetTsync sets whether Thread Synchronization will be enabled on the filter +// being loaded. Returns an error if setting Tsync failed, or the filter is +// invalid. +// Thread Sync ensures that all members of the thread group of the calling +// process will share the same Seccomp filter set. +// Tsync is a fairly recent addition to the Linux kernel and older kernels +// lack support. If the running kernel does not support Tsync and it is +// requested in a filter, Libseccomp will not enable TSync support and will +// proceed as normal. +// This function is unavailable before v2.2 of libseccomp and will return an +// error. +func (f *ScmpFilter) SetTsync(enable bool) error { + var toSet C.uint32_t = 0x0 + + if enable { + toSet = 0x1 + } + + return f.setFilterAttr(filterAttrTsync, toSet) +} + +// SetSyscallPriority sets a syscall's priority. +// This provides a hint to the filter generator in libseccomp about the +// importance of this syscall. High-priority syscalls are placed +// first in the filter code, and incur less overhead (at the expense of +// lower-priority syscalls). +func (f *ScmpFilter) SetSyscallPriority(call ScmpSyscall, priority uint8) error { + f.lock.Lock() + defer f.lock.Unlock() + + if !f.valid { + return errBadFilter + } + + if retCode := C.seccomp_syscall_priority(f.filterCtx, C.int(call), + C.uint8_t(priority)); retCode != 0 { + return syscall.Errno(-1 * retCode) + } + + return nil +} + +// AddRule adds a single rule for an unconditional action on a syscall. +// Accepts the number of the syscall and the action to be taken on the call +// being made. +// Returns an error if an issue was encountered adding the rule. +func (f *ScmpFilter) AddRule(call ScmpSyscall, action ScmpAction) error { + return f.addRuleGeneric(call, action, false, nil) +} + +// AddRuleExact adds a single rule for an unconditional action on a syscall. +// Accepts the number of the syscall and the action to be taken on the call +// being made. +// No modifications will be made to the rule, and it will fail to add if it +// cannot be applied to the current architecture without modification. +// The rule will function exactly as described, but it may not function identically +// (or be able to be applied to) all architectures. +// Returns an error if an issue was encountered adding the rule. +func (f *ScmpFilter) AddRuleExact(call ScmpSyscall, action ScmpAction) error { + return f.addRuleGeneric(call, action, true, nil) +} + +// AddRuleConditional adds a single rule for a conditional action on a syscall. +// Returns an error if an issue was encountered adding the rule. +// All conditions must match for the rule to match. +// There is a bug in library versions below v2.2.1 which can, in some cases, +// cause conditions to be lost when more than one are used. Consequently, +// AddRuleConditional is disabled on library versions lower than v2.2.1 +func (f *ScmpFilter) AddRuleConditional(call ScmpSyscall, action ScmpAction, conds []ScmpCondition) error { + return f.addRuleGeneric(call, action, false, conds) +} + +// AddRuleConditionalExact adds a single rule for a conditional action on a +// syscall. +// No modifications will be made to the rule, and it will fail to add if it +// cannot be applied to the current architecture without modification. +// The rule will function exactly as described, but it may not function identically +// (or be able to be applied to) all architectures. +// Returns an error if an issue was encountered adding the rule. +// There is a bug in library versions below v2.2.1 which can, in some cases, +// cause conditions to be lost when more than one are used. Consequently, +// AddRuleConditionalExact is disabled on library versions lower than v2.2.1 +func (f *ScmpFilter) AddRuleConditionalExact(call ScmpSyscall, action ScmpAction, conds []ScmpCondition) error { + return f.addRuleGeneric(call, action, true, conds) +} + +// ExportPFC output PFC-formatted, human-readable dump of a filter context's +// rules to a file. +// Accepts file to write to (must be open for writing). +// Returns an error if writing to the file fails. +func (f *ScmpFilter) ExportPFC(file *os.File) error { + f.lock.Lock() + defer f.lock.Unlock() + + fd := file.Fd() + + if !f.valid { + return errBadFilter + } + + if retCode := C.seccomp_export_pfc(f.filterCtx, C.int(fd)); retCode != 0 { + return syscall.Errno(-1 * retCode) + } + + return nil +} + +// ExportBPF outputs Berkeley Packet Filter-formatted, kernel-readable dump of a +// filter context's rules to a file. +// Accepts file to write to (must be open for writing). +// Returns an error if writing to the file fails. +func (f *ScmpFilter) ExportBPF(file *os.File) error { + f.lock.Lock() + defer f.lock.Unlock() + + fd := file.Fd() + + if !f.valid { + return errBadFilter + } + + if retCode := C.seccomp_export_bpf(f.filterCtx, C.int(fd)); retCode != 0 { + return syscall.Errno(-1 * retCode) + } + + return nil +} diff --git a/vendor/seccomp/libseccomp-golang/seccomp_internal.go b/vendor/seccomp/libseccomp-golang/seccomp_internal.go new file mode 100644 index 0000000..306ed17 --- /dev/null +++ b/vendor/seccomp/libseccomp-golang/seccomp_internal.go @@ -0,0 +1,461 @@ +// +build linux + +// Internal functions for libseccomp Go bindings +// No exported functions + +package seccomp + +import ( + "fmt" + "os" + "syscall" +) + +// Unexported C wrapping code - provides the C-Golang interface +// Get the seccomp header in scope +// Need stdlib.h for free() on cstrings + +// #cgo LDFLAGS: -lseccomp +/* +#include +#include + +#if SCMP_VER_MAJOR < 2 +#error Minimum supported version of Libseccomp is v2.1.0 +#elif SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 1 +#error Minimum supported version of Libseccomp is v2.1.0 +#endif + +#define ARCH_BAD ~0 + +const uint32_t C_ARCH_BAD = ARCH_BAD; + +#ifndef SCMP_ARCH_AARCH64 +#define SCMP_ARCH_AARCH64 ARCH_BAD +#endif + +#ifndef SCMP_ARCH_MIPS +#define SCMP_ARCH_MIPS ARCH_BAD +#endif + +#ifndef SCMP_ARCH_MIPS64 +#define SCMP_ARCH_MIPS64 ARCH_BAD +#endif + +#ifndef SCMP_ARCH_MIPS64N32 +#define SCMP_ARCH_MIPS64N32 ARCH_BAD +#endif + +#ifndef SCMP_ARCH_MIPSEL +#define SCMP_ARCH_MIPSEL ARCH_BAD +#endif + +#ifndef SCMP_ARCH_MIPSEL64 +#define SCMP_ARCH_MIPSEL64 ARCH_BAD +#endif + +#ifndef SCMP_ARCH_MIPSEL64N32 +#define SCMP_ARCH_MIPSEL64N32 ARCH_BAD +#endif + +const uint32_t C_ARCH_NATIVE = SCMP_ARCH_NATIVE; +const uint32_t C_ARCH_X86 = SCMP_ARCH_X86; +const uint32_t C_ARCH_X86_64 = SCMP_ARCH_X86_64; +const uint32_t C_ARCH_X32 = SCMP_ARCH_X32; +const uint32_t C_ARCH_ARM = SCMP_ARCH_ARM; +const uint32_t C_ARCH_AARCH64 = SCMP_ARCH_AARCH64; +const uint32_t C_ARCH_MIPS = SCMP_ARCH_MIPS; +const uint32_t C_ARCH_MIPS64 = SCMP_ARCH_MIPS64; +const uint32_t C_ARCH_MIPS64N32 = SCMP_ARCH_MIPS64N32; +const uint32_t C_ARCH_MIPSEL = SCMP_ARCH_MIPSEL; +const uint32_t C_ARCH_MIPSEL64 = SCMP_ARCH_MIPSEL64; +const uint32_t C_ARCH_MIPSEL64N32 = SCMP_ARCH_MIPSEL64N32; + +const uint32_t C_ACT_KILL = SCMP_ACT_KILL; +const uint32_t C_ACT_TRAP = SCMP_ACT_TRAP; +const uint32_t C_ACT_ERRNO = SCMP_ACT_ERRNO(0); +const uint32_t C_ACT_TRACE = SCMP_ACT_TRACE(0); +const uint32_t C_ACT_ALLOW = SCMP_ACT_ALLOW; + +// If TSync is not supported, make sure it doesn't map to a supported filter attribute +// Don't worry about major version < 2, the minimum version checks should catch that case +#if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 2 +#define SCMP_FLTATR_CTL_TSYNC _SCMP_CMP_MIN +#endif + +const uint32_t C_ATTRIBUTE_DEFAULT = (uint32_t)SCMP_FLTATR_ACT_DEFAULT; +const uint32_t C_ATTRIBUTE_BADARCH = (uint32_t)SCMP_FLTATR_ACT_BADARCH; +const uint32_t C_ATTRIBUTE_NNP = (uint32_t)SCMP_FLTATR_CTL_NNP; +const uint32_t C_ATTRIBUTE_TSYNC = (uint32_t)SCMP_FLTATR_CTL_TSYNC; + +const int C_CMP_NE = (int)SCMP_CMP_NE; +const int C_CMP_LT = (int)SCMP_CMP_LT; +const int C_CMP_LE = (int)SCMP_CMP_LE; +const int C_CMP_EQ = (int)SCMP_CMP_EQ; +const int C_CMP_GE = (int)SCMP_CMP_GE; +const int C_CMP_GT = (int)SCMP_CMP_GT; +const int C_CMP_MASKED_EQ = (int)SCMP_CMP_MASKED_EQ; + +const int C_VERSION_MAJOR = SCMP_VER_MAJOR; +const int C_VERSION_MINOR = SCMP_VER_MINOR; +const int C_VERSION_MICRO = SCMP_VER_MICRO; + +typedef struct scmp_arg_cmp* scmp_cast_t; + +// Wrapper to create an scmp_arg_cmp struct +void* +make_struct_arg_cmp( + unsigned int arg, + int compare, + uint64_t a, + uint64_t b + ) +{ + struct scmp_arg_cmp *s = malloc(sizeof(struct scmp_arg_cmp)); + + s->arg = arg; + s->op = compare; + s->datum_a = a; + s->datum_b = b; + + return s; +} +*/ +import "C" + +// Nonexported types +type scmpFilterAttr uint32 + +// Nonexported constants + +const ( + filterAttrActDefault scmpFilterAttr = iota + filterAttrActBadArch scmpFilterAttr = iota + filterAttrNNP scmpFilterAttr = iota + filterAttrTsync scmpFilterAttr = iota +) + +const ( + // An error return from certain libseccomp functions + scmpError C.int = -1 + // Comparison boundaries to check for architecture validity + archStart ScmpArch = ArchNative + archEnd ScmpArch = ArchMIPSEL64N32 + // Comparison boundaries to check for action validity + actionStart ScmpAction = ActKill + actionEnd ScmpAction = ActAllow + // Comparison boundaries to check for comparison operator validity + compareOpStart ScmpCompareOp = CompareNotEqual + compareOpEnd ScmpCompareOp = CompareMaskedEqual +) + +var ( + // Error thrown on bad filter context + errBadFilter = fmt.Errorf("filter is invalid or uninitialized") + // Constants representing library major, minor, and micro versions + verMajor = int(C.C_VERSION_MAJOR) + verMinor = int(C.C_VERSION_MINOR) + verMicro = int(C.C_VERSION_MICRO) +) + +// Nonexported functions + +// Check if library version is greater than or equal to the given one +func checkVersionAbove(major, minor, micro int) bool { + return (verMajor > major) || + (verMajor == major && verMinor > minor) || + (verMajor == major && verMinor == minor && verMicro >= micro) +} + +// Init function: Verify library version is appropriate +func init() { + if !checkVersionAbove(2, 1, 0) { + fmt.Fprintf(os.Stderr, "Libseccomp version too low: minimum supported is 2.1.0, detected %d.%d.%d", C.C_VERSION_MAJOR, C.C_VERSION_MINOR, C.C_VERSION_MICRO) + os.Exit(-1) + } +} + +// Filter helpers + +// Filter finalizer - ensure that kernel context for filters is freed +func filterFinalizer(f *ScmpFilter) { + f.Release() +} + +// Get a raw filter attribute +func (f *ScmpFilter) getFilterAttr(attr scmpFilterAttr) (C.uint32_t, error) { + f.lock.Lock() + defer f.lock.Unlock() + + if !f.valid { + return 0x0, errBadFilter + } + + if !checkVersionAbove(2, 2, 0) && attr == filterAttrTsync { + return 0x0, fmt.Errorf("the thread synchronization attribute is not supported in this version of the library") + } + + var attribute C.uint32_t + + retCode := C.seccomp_attr_get(f.filterCtx, attr.toNative(), &attribute) + if retCode != 0 { + return 0x0, syscall.Errno(-1 * retCode) + } + + return attribute, nil +} + +// Set a raw filter attribute +func (f *ScmpFilter) setFilterAttr(attr scmpFilterAttr, value C.uint32_t) error { + f.lock.Lock() + defer f.lock.Unlock() + + if !f.valid { + return errBadFilter + } + + if !checkVersionAbove(2, 2, 0) && attr == filterAttrTsync { + return fmt.Errorf("the thread synchronization attribute is not supported in this version of the library") + } + + retCode := C.seccomp_attr_set(f.filterCtx, attr.toNative(), value) + if retCode != 0 { + return syscall.Errno(-1 * retCode) + } + + return nil +} + +// DOES NOT LOCK OR CHECK VALIDITY +// Assumes caller has already done this +// Wrapper for seccomp_rule_add_... functions +func (f *ScmpFilter) addRuleWrapper(call ScmpSyscall, action ScmpAction, exact bool, cond C.scmp_cast_t) error { + var length C.uint + if cond != nil { + length = 1 + } else { + length = 0 + } + + var retCode C.int + if exact { + retCode = C.seccomp_rule_add_exact_array(f.filterCtx, action.toNative(), C.int(call), length, cond) + } else { + retCode = C.seccomp_rule_add_array(f.filterCtx, action.toNative(), C.int(call), length, cond) + } + + if syscall.Errno(-1*retCode) == syscall.EFAULT { + return fmt.Errorf("unrecognized syscall") + } else if syscall.Errno(-1*retCode) == syscall.EPERM { + return fmt.Errorf("requested action matches default action of filter") + } else if retCode != 0 { + return syscall.Errno(-1 * retCode) + } + + return nil +} + +// Generic add function for filter rules +func (f *ScmpFilter) addRuleGeneric(call ScmpSyscall, action ScmpAction, exact bool, conds []ScmpCondition) error { + f.lock.Lock() + defer f.lock.Unlock() + + if !f.valid { + return errBadFilter + } + + if len(conds) == 0 { + if err := f.addRuleWrapper(call, action, exact, nil); err != nil { + return err + } + } else { + // We don't support conditional filtering in library version v2.1 + if !checkVersionAbove(2, 2, 1) { + return fmt.Errorf("conditional filtering requires libseccomp version >= 2.2.1") + } + + for _, cond := range conds { + cmpStruct := C.make_struct_arg_cmp(C.uint(cond.Argument), cond.Op.toNative(), C.uint64_t(cond.Operand1), C.uint64_t(cond.Operand2)) + defer C.free(cmpStruct) + + if err := f.addRuleWrapper(call, action, exact, C.scmp_cast_t(cmpStruct)); err != nil { + return err + } + } + } + + return nil +} + +// Generic Helpers + +// Helper - Sanitize Arch token input +func sanitizeArch(in ScmpArch) error { + if in < archStart || in > archEnd { + return fmt.Errorf("unrecognized architecture") + } + + if in.toNative() == C.C_ARCH_BAD { + return fmt.Errorf("architecture is not supported on this version of the library") + } + + return nil +} + +func sanitizeAction(in ScmpAction) error { + inTmp := in & 0x0000FFFF + if inTmp < actionStart || inTmp > actionEnd { + return fmt.Errorf("unrecognized action") + } + + if inTmp != ActTrace && inTmp != ActErrno && (in&0xFFFF0000) != 0 { + return fmt.Errorf("highest 16 bits must be zeroed except for Trace and Errno") + } + + return nil +} + +func sanitizeCompareOp(in ScmpCompareOp) error { + if in < compareOpStart || in > compareOpEnd { + return fmt.Errorf("unrecognized comparison operator") + } + + return nil +} + +func archFromNative(a C.uint32_t) (ScmpArch, error) { + switch a { + case C.C_ARCH_X86: + return ArchX86, nil + case C.C_ARCH_X86_64: + return ArchAMD64, nil + case C.C_ARCH_X32: + return ArchX32, nil + case C.C_ARCH_ARM: + return ArchARM, nil + case C.C_ARCH_NATIVE: + return ArchNative, nil + case C.C_ARCH_AARCH64: + return ArchARM64, nil + case C.C_ARCH_MIPS: + return ArchMIPS, nil + case C.C_ARCH_MIPS64: + return ArchMIPS64, nil + case C.C_ARCH_MIPS64N32: + return ArchMIPS64N32, nil + case C.C_ARCH_MIPSEL: + return ArchMIPSEL, nil + case C.C_ARCH_MIPSEL64: + return ArchMIPSEL64, nil + case C.C_ARCH_MIPSEL64N32: + return ArchMIPSEL64N32, nil + default: + return 0x0, fmt.Errorf("unrecognized architecture") + } +} + +// Only use with sanitized arches, no error handling +func (a ScmpArch) toNative() C.uint32_t { + switch a { + case ArchX86: + return C.C_ARCH_X86 + case ArchAMD64: + return C.C_ARCH_X86_64 + case ArchX32: + return C.C_ARCH_X32 + case ArchARM: + return C.C_ARCH_ARM + case ArchARM64: + return C.C_ARCH_AARCH64 + case ArchMIPS: + return C.C_ARCH_MIPS + case ArchMIPS64: + return C.C_ARCH_MIPS64 + case ArchMIPS64N32: + return C.C_ARCH_MIPS64N32 + case ArchMIPSEL: + return C.C_ARCH_MIPSEL + case ArchMIPSEL64: + return C.C_ARCH_MIPSEL64 + case ArchMIPSEL64N32: + return C.C_ARCH_MIPSEL64N32 + case ArchNative: + return C.C_ARCH_NATIVE + default: + return 0x0 + } +} + +// Only use with sanitized ops, no error handling +func (a ScmpCompareOp) toNative() C.int { + switch a { + case CompareNotEqual: + return C.C_CMP_NE + case CompareLess: + return C.C_CMP_LT + case CompareLessOrEqual: + return C.C_CMP_LE + case CompareEqual: + return C.C_CMP_EQ + case CompareGreaterEqual: + return C.C_CMP_GE + case CompareGreater: + return C.C_CMP_GT + case CompareMaskedEqual: + return C.C_CMP_MASKED_EQ + default: + return 0x0 + } +} + +func actionFromNative(a C.uint32_t) (ScmpAction, error) { + aTmp := a & 0xFFFF + switch a & 0xFFFF0000 { + case C.C_ACT_KILL: + return ActKill, nil + case C.C_ACT_TRAP: + return ActTrap, nil + case C.C_ACT_ERRNO: + return ActErrno.SetReturnCode(int16(aTmp)), nil + case C.C_ACT_TRACE: + return ActTrace.SetReturnCode(int16(aTmp)), nil + case C.C_ACT_ALLOW: + return ActAllow, nil + default: + return 0x0, fmt.Errorf("unrecognized action") + } +} + +// Only use with sanitized actions, no error handling +func (a ScmpAction) toNative() C.uint32_t { + switch a & 0xFFFF { + case ActKill: + return C.C_ACT_KILL + case ActTrap: + return C.C_ACT_TRAP + case ActErrno: + return C.C_ACT_ERRNO | (C.uint32_t(a) >> 16) + case ActTrace: + return C.C_ACT_TRACE | (C.uint32_t(a) >> 16) + case ActAllow: + return C.C_ACT_ALLOW + default: + return 0x0 + } +} + +// Internal only, assumes safe attribute +func (a scmpFilterAttr) toNative() uint32 { + switch a { + case filterAttrActDefault: + return uint32(C.C_ATTRIBUTE_DEFAULT) + case filterAttrActBadArch: + return uint32(C.C_ATTRIBUTE_BADARCH) + case filterAttrNNP: + return uint32(C.C_ATTRIBUTE_NNP) + case filterAttrTsync: + return uint32(C.C_ATTRIBUTE_TSYNC) + default: + return 0x0 + } +} diff --git a/vendor/seccomp/libseccomp-golang/seccomp_test.go b/vendor/seccomp/libseccomp-golang/seccomp_test.go new file mode 100644 index 0000000..b3a49d2 --- /dev/null +++ b/vendor/seccomp/libseccomp-golang/seccomp_test.go @@ -0,0 +1,457 @@ +// +build linux + +// Tests for public API of libseccomp Go bindings + +package seccomp + +import ( + "fmt" + "syscall" + "testing" +) + +// Type Function Tests + +func TestActionSetReturnCode(t *testing.T) { + if ActInvalid.SetReturnCode(0x0010) != ActInvalid { + t.Errorf("Able to set a return code on invalid action!") + } + + codeSet := ActErrno.SetReturnCode(0x0001) + if codeSet == ActErrno || codeSet.GetReturnCode() != 0x0001 { + t.Errorf("Could not set return code on ActErrno") + } +} + +func TestSyscallGetName(t *testing.T) { + call1 := ScmpSyscall(0x1) + callFail := ScmpSyscall(0x999) + + name, err := call1.GetName() + if err != nil { + t.Errorf("Error getting syscall name for number 0x1") + } else if len(name) == 0 { + t.Errorf("Empty name returned for syscall 0x1") + } + fmt.Printf("Got name of syscall 0x1 on native arch as %s\n", name) + + _, err = callFail.GetName() + if err == nil { + t.Errorf("Getting nonexistant syscall should error!") + } +} + +func TestSyscallGetNameByArch(t *testing.T) { + call1 := ScmpSyscall(0x1) + callInvalid := ScmpSyscall(0x999) + archGood := ArchAMD64 + archBad := ArchInvalid + + name, err := call1.GetNameByArch(archGood) + if err != nil { + t.Errorf("Error getting syscall name for number 0x1 and arch AMD64") + } else if name != "write" { + t.Errorf("Got incorrect name for syscall 0x1 - expected write, got %s", name) + } + + _, err = call1.GetNameByArch(archBad) + if err == nil { + t.Errorf("Bad architecture GetNameByArch() should error!") + } + + _, err = callInvalid.GetNameByArch(archGood) + if err == nil { + t.Errorf("Bad syscall GetNameByArch() should error!") + } + + _, err = callInvalid.GetNameByArch(archBad) + if err == nil { + t.Errorf("Bad syscall and bad arch GetNameByArch() should error!") + } +} + +func TestGetSyscallFromName(t *testing.T) { + name1 := "write" + nameInval := "NOTASYSCALL" + + syscall, err := GetSyscallFromName(name1) + if err != nil { + t.Errorf("Error getting syscall number of write: %s", err) + } + fmt.Printf("Got syscall number of write on native arch as %d\n", syscall) + + _, err = GetSyscallFromName(nameInval) + if err == nil { + t.Errorf("Getting an invalid syscall should error!") + } +} + +func TestGetSyscallFromNameByArch(t *testing.T) { + name1 := "write" + nameInval := "NOTASYSCALL" + arch1 := ArchAMD64 + archInval := ArchInvalid + + syscall, err := GetSyscallFromNameByArch(name1, arch1) + if err != nil { + t.Errorf("Error getting syscall number of write on AMD64: %s", err) + } + fmt.Printf("Got syscall number of write on AMD64 as %d\n", syscall) + + _, err = GetSyscallFromNameByArch(nameInval, arch1) + if err == nil { + t.Errorf("Getting invalid syscall with valid arch should error") + } + + _, err = GetSyscallFromNameByArch(name1, archInval) + if err == nil { + t.Errorf("Getting valid syscall for invalid arch should error") + } + + _, err = GetSyscallFromNameByArch(nameInval, archInval) + if err == nil { + t.Errorf("Getting invalid syscall for invalid arch should error") + } +} + +func TestMakeCondition(t *testing.T) { + condition, err := MakeCondition(3, CompareNotEqual, 0x10) + if err != nil { + t.Errorf("Error making condition struct: %s", err) + } else if condition.Argument != 3 || condition.Operand1 != 0x10 || + condition.Operand2 != 0 || condition.Op != CompareNotEqual { + t.Errorf("Condition struct was filled incorrectly") + } + + condition, err = MakeCondition(3, CompareMaskedEqual, 0x10, 0x20) + if err != nil { + t.Errorf("Error making condition struct: %s", err) + } else if condition.Argument != 3 || condition.Operand1 != 0x10 || + condition.Operand2 != 0x20 || condition.Op != CompareMaskedEqual { + t.Errorf("Condition struct was filled incorrectly") + } + + _, err = MakeCondition(7, CompareNotEqual, 0x10) + if err == nil { + t.Errorf("Condition struct with bad syscall argument number should error") + } + + _, err = MakeCondition(3, CompareInvalid, 0x10) + if err == nil { + t.Errorf("Condition struct with bad comparison operator should error") + } + + _, err = MakeCondition(3, CompareMaskedEqual, 0x10, 0x20, 0x30) + if err == nil { + t.Errorf("MakeCondition with more than 2 arguments should fail") + } + + _, err = MakeCondition(3, CompareMaskedEqual) + if err == nil { + t.Errorf("MakeCondition with no arguments should fail") + } +} + +// Utility Function Tests + +func TestGetNativeArch(t *testing.T) { + arch, err := GetNativeArch() + if err != nil { + t.Errorf("GetNativeArch should not error!") + } + fmt.Printf("Got native arch of system as %s\n", arch.String()) +} + +// Filter Tests + +func TestFilterCreateRelease(t *testing.T) { + _, err := NewFilter(ActInvalid) + if err == nil { + t.Errorf("Can create filter with invalid action") + } + + filter, err := NewFilter(ActKill) + if err != nil { + t.Errorf("Error creating filter: %s", err) + } + + if !filter.IsValid() { + t.Errorf("Filter created by NewFilter was not valid") + } + + filter.Release() + + if filter.IsValid() { + t.Errorf("Filter is valid after being released") + } +} + +func TestFilterReset(t *testing.T) { + filter, err := NewFilter(ActKill) + if err != nil { + t.Errorf("Error creating filter: %s", err) + } + defer filter.Release() + + // Ensure the default action is ActKill + action, err := filter.GetDefaultAction() + if err != nil { + t.Errorf("Error getting default action of filter") + } else if action != ActKill { + t.Errorf("Default action of filter was set incorrectly!") + } + + // Reset with a different default action + err = filter.Reset(ActAllow) + if err != nil { + t.Errorf("Error resetting filter!") + } + + valid := filter.IsValid() + if !valid { + t.Errorf("Filter is no longer valid after reset!") + } + + // The default action should no longer be ActKill + action, err = filter.GetDefaultAction() + if err != nil { + t.Errorf("Error getting default action of filter") + } else if action != ActAllow { + t.Errorf("Default action of filter was set incorrectly!") + } +} + +func TestFilterArchFunctions(t *testing.T) { + filter, err := NewFilter(ActKill) + if err != nil { + t.Errorf("Error creating filter: %s", err) + } + defer filter.Release() + + arch, err := GetNativeArch() + if err != nil { + t.Errorf("Error getting native architecture: %s", err) + } + + present, err := filter.IsArchPresent(arch) + if err != nil { + t.Errorf("Error retrieving arch from filter: %s", err) + } else if !present { + t.Errorf("Filter does not contain native architecture by default") + } + + // Adding the native arch again should succeed, as it's already present + err = filter.AddArch(arch) + if err != nil { + t.Errorf("Adding arch to filter already containing it should succeed") + } + + // Make sure we don't add the native arch again + prospectiveArch := ArchX86 + if arch == ArchX86 { + prospectiveArch = ArchAMD64 + } + + // Check to make sure this other arch isn't in the filter + present, err = filter.IsArchPresent(prospectiveArch) + if err != nil { + t.Errorf("Error retrieving arch from filter: %s", err) + } else if present { + t.Errorf("Arch not added to filter is present") + } + + // Try removing the nonexistant arch - should succeed + err = filter.RemoveArch(prospectiveArch) + if err != nil { + t.Errorf("Error removing nonexistant arch: %s", err) + } + + // Add an arch, see if it's in the filter + err = filter.AddArch(prospectiveArch) + if err != nil { + t.Errorf("Could not add arch %s to filter: %s", + prospectiveArch.String(), err) + } + + present, err = filter.IsArchPresent(prospectiveArch) + if err != nil { + t.Errorf("Error retrieving arch from filter: %s", err) + } else if !present { + t.Errorf("Filter does not contain architecture %s after it was added", + prospectiveArch.String()) + } + + // Remove the arch again, make sure it's not in the filter + err = filter.RemoveArch(prospectiveArch) + if err != nil { + t.Errorf("Could not remove arch %s from filter: %s", + prospectiveArch.String(), err) + } + + present, err = filter.IsArchPresent(prospectiveArch) + if err != nil { + t.Errorf("Error retrieving arch from filter: %s", err) + } else if present { + t.Errorf("Filter contains architecture %s after it was removed", + prospectiveArch.String()) + } +} + +func TestFilterAttributeGettersAndSetters(t *testing.T) { + filter, err := NewFilter(ActKill) + if err != nil { + t.Errorf("Error creating filter: %s", err) + } + defer filter.Release() + + act, err := filter.GetDefaultAction() + if err != nil { + t.Errorf("Error getting default action: %s", err) + } else if act != ActKill { + t.Errorf("Default action was set incorrectly") + } + + err = filter.SetBadArchAction(ActAllow) + if err != nil { + t.Errorf("Error setting bad arch action: %s", err) + } + + act, err = filter.GetBadArchAction() + if err != nil { + t.Errorf("Error getting bad arch action") + } else if act != ActAllow { + t.Errorf("Bad arch action was not set correcly!") + } + + err = filter.SetNoNewPrivsBit(false) + if err != nil { + t.Errorf("Error setting no new privileges bit") + } + + privs, err := filter.GetNoNewPrivsBit() + if err != nil { + t.Errorf("Error getting no new privileges bit!") + } else if privs != false { + t.Errorf("No new privileges bit was not set correctly") + } + + err = filter.SetBadArchAction(ActInvalid) + if err == nil { + t.Errorf("Setting bad arch action to an invalid action should error") + } +} + +func TestMergeFilters(t *testing.T) { + filter1, err := NewFilter(ActAllow) + if err != nil { + t.Errorf("Error creating filter: %s", err) + } + + filter2, err := NewFilter(ActAllow) + if err != nil { + t.Errorf("Error creating filter: %s", err) + } + + // Need to remove the native arch and add another to the second filter + // Filters must NOT share architectures to be successfully merged + nativeArch, err := GetNativeArch() + if err != nil { + t.Errorf("Error getting native arch: %s", err) + } + + prospectiveArch := ArchAMD64 + if nativeArch == ArchAMD64 { + prospectiveArch = ArchX86 + } + + err = filter2.AddArch(prospectiveArch) + if err != nil { + t.Errorf("Error adding architecture to filter: %s", err) + } + + err = filter2.RemoveArch(nativeArch) + if err != nil { + t.Errorf("Error removing architecture from filter: %s", err) + } + + err = filter1.Merge(filter2) + if err != nil { + t.Errorf("Error merging filters: %s", err) + } + + if filter2.IsValid() { + t.Errorf("Source filter should not be valid after merging") + } + + filter3, err := NewFilter(ActKill) + if err != nil { + t.Errorf("Error creating filter: %s", err) + } + defer filter3.Release() + + err = filter1.Merge(filter3) + if err == nil { + t.Errorf("Attributes should have to match to merge filters") + } +} + +func TestRuleAddAndLoad(t *testing.T) { + // Test #1: Add a trivial filter + filter1, err := NewFilter(ActAllow) + if err != nil { + t.Errorf("Error creating filter: %s", err) + } + defer filter1.Release() + + call, err := GetSyscallFromName("getpid") + if err != nil { + t.Errorf("Error getting syscall number of getpid: %s", err) + } + + call2, err := GetSyscallFromName("setreuid") + if err != nil { + t.Errorf("Error getting syscall number of setreuid: %s", err) + } + + uid := syscall.Getuid() + euid := syscall.Geteuid() + + err = filter1.AddRule(call, ActErrno.SetReturnCode(0x1)) + if err != nil { + t.Errorf("Error adding rule to restrict syscall: %s", err) + } + + cond, err := MakeCondition(1, CompareEqual, uint64(euid)) + if err != nil { + t.Errorf("Error making rule to restrict syscall: %s", err) + } + + cond2, err := MakeCondition(0, CompareEqual, uint64(uid)) + if err != nil { + t.Errorf("Error making rule to restrict syscall: %s", err) + } + + conditions := []ScmpCondition{cond, cond2} + + err = filter1.AddRuleConditional(call2, ActErrno.SetReturnCode(0x2), conditions) + if err != nil { + t.Errorf("Error adding conditional rule: %s", err) + } + + err = filter1.Load() + if err != nil { + t.Errorf("Error loading filter: %s", err) + } + + // Try making a simple syscall, it should error + pid := syscall.Getpid() + if pid != -1 { + t.Errorf("Syscall should have returned error code!") + } + + // Try making a Geteuid syscall that should normally succeed + err = syscall.Setreuid(uid, euid) + if err != syscall.Errno(2) { + t.Errorf("Syscall should have returned error code!") + } +}