Merge pull request #41395 from cpuguy83/no_libseccomp
Remove dependency in dockerd on libseccomp
This commit is contained in:
commit
1155b6bc7a
|
@ -6,6 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -277,16 +278,24 @@ func applyCgroupNsInfo(info *SysInfo, _ map[string]string) []string {
|
||||||
return warnings
|
return warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
seccompOnce sync.Once
|
||||||
|
seccompEnabled bool
|
||||||
|
)
|
||||||
|
|
||||||
// applySeccompInfo checks if Seccomp is supported, via CONFIG_SECCOMP.
|
// applySeccompInfo checks if Seccomp is supported, via CONFIG_SECCOMP.
|
||||||
func applySeccompInfo(info *SysInfo, _ map[string]string) []string {
|
func applySeccompInfo(info *SysInfo, _ map[string]string) []string {
|
||||||
var warnings []string
|
var warnings []string
|
||||||
// Check if Seccomp is supported, via CONFIG_SECCOMP.
|
seccompOnce.Do(func() {
|
||||||
if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
|
// Check if Seccomp is supported, via CONFIG_SECCOMP.
|
||||||
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
|
||||||
if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
|
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
||||||
info.Seccomp = true
|
if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
|
||||||
|
seccompEnabled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
info.Seccomp = seccompEnabled
|
||||||
return warnings
|
return warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build linux,seccomp
|
// +build seccomp
|
||||||
|
|
||||||
package seccomp // import "github.com/docker/docker/profiles/seccomp"
|
package seccomp // import "github.com/docker/docker/profiles/seccomp"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build linux
|
//go:generate go run -tags 'seccomp' generate.go
|
||||||
|
|
||||||
package seccomp // import "github.com/docker/docker/profiles/seccomp"
|
package seccomp // import "github.com/docker/docker/profiles/seccomp"
|
||||||
|
|
||||||
|
@ -6,15 +6,13 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/pkg/parsers/kernel"
|
"github.com/docker/docker/pkg/parsers/kernel"
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
libseccomp "github.com/seccomp/libseccomp-golang"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run -tags 'seccomp' generate.go
|
|
||||||
|
|
||||||
// GetDefaultProfile returns the default seccomp profile.
|
// GetDefaultProfile returns the default seccomp profile.
|
||||||
func GetDefaultProfile(rs *specs.Spec) (*specs.LinuxSeccomp, error) {
|
func GetDefaultProfile(rs *specs.Spec) (*specs.LinuxSeccomp, error) {
|
||||||
return setupSeccomp(DefaultProfile(), rs)
|
return setupSeccomp(DefaultProfile(), rs)
|
||||||
|
@ -29,16 +27,42 @@ func LoadProfile(body string, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
|
||||||
return setupSeccomp(&config, rs)
|
return setupSeccomp(&config, rs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// libseccomp string => seccomp arch
|
||||||
var nativeToSeccomp = map[string]types.Arch{
|
var nativeToSeccomp = map[string]types.Arch{
|
||||||
|
"x86": types.ArchX86,
|
||||||
"amd64": types.ArchX86_64,
|
"amd64": types.ArchX86_64,
|
||||||
|
"arm": types.ArchARM,
|
||||||
"arm64": types.ArchAARCH64,
|
"arm64": types.ArchAARCH64,
|
||||||
"mips64": types.ArchMIPS64,
|
"mips64": types.ArchMIPS64,
|
||||||
"mips64n32": types.ArchMIPS64N32,
|
"mips64n32": types.ArchMIPS64N32,
|
||||||
"mipsel64": types.ArchMIPSEL64,
|
"mipsel64": types.ArchMIPSEL64,
|
||||||
"mipsel64n32": types.ArchMIPSEL64N32,
|
"mips3l64n32": types.ArchMIPSEL64N32,
|
||||||
|
"mipsle": types.ArchMIPSEL,
|
||||||
|
"ppc": types.ArchPPC,
|
||||||
|
"ppc64": types.ArchPPC64,
|
||||||
|
"ppc64le": types.ArchPPC64LE,
|
||||||
|
"s390": types.ArchS390,
|
||||||
"s390x": types.ArchS390X,
|
"s390x": types.ArchS390X,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GOARCH => libseccomp string
|
||||||
|
var goToNative = map[string]string{
|
||||||
|
"386": "x86",
|
||||||
|
"amd64": "amd64",
|
||||||
|
"arm": "arm",
|
||||||
|
"arm64": "arm64",
|
||||||
|
"mips64": "mips64",
|
||||||
|
"mips64p32": "mips64n32",
|
||||||
|
"mips64le": "mipsel64",
|
||||||
|
"mips64p32le": "mips3l64n32",
|
||||||
|
"mipsle": "mipsel",
|
||||||
|
"ppc": "ppc",
|
||||||
|
"ppc64": "ppc64",
|
||||||
|
"ppc64le": "ppc64le",
|
||||||
|
"s390": "s390",
|
||||||
|
"s390x": "s390x",
|
||||||
|
}
|
||||||
|
|
||||||
// inSlice tests whether a string is contained in a slice of strings or not.
|
// inSlice tests whether a string is contained in a slice of strings or not.
|
||||||
// Comparison is case sensitive
|
// Comparison is case sensitive
|
||||||
func inSlice(slice []string, s string) bool {
|
func inSlice(slice []string, s string) bool {
|
||||||
|
@ -62,12 +86,6 @@ func setupSeccomp(config *types.Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, e
|
||||||
|
|
||||||
newConfig := &specs.LinuxSeccomp{}
|
newConfig := &specs.LinuxSeccomp{}
|
||||||
|
|
||||||
var arch string
|
|
||||||
var native, err = libseccomp.GetNativeArch()
|
|
||||||
if err == nil {
|
|
||||||
arch = native.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(config.Architectures) != 0 && len(config.ArchMap) != 0 {
|
if len(config.Architectures) != 0 && len(config.ArchMap) != 0 {
|
||||||
return nil, errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
|
return nil, errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
|
||||||
}
|
}
|
||||||
|
@ -79,17 +97,17 @@ func setupSeccomp(config *types.Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(config.ArchMap) != 0 {
|
arch := goToNative[runtime.GOARCH]
|
||||||
|
seccompArch, archExists := nativeToSeccomp[arch]
|
||||||
|
|
||||||
|
if len(config.ArchMap) != 0 && archExists {
|
||||||
for _, a := range config.ArchMap {
|
for _, a := range config.ArchMap {
|
||||||
seccompArch, ok := nativeToSeccomp[arch]
|
if a.Arch == seccompArch {
|
||||||
if ok {
|
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a.Arch))
|
||||||
if a.Arch == seccompArch {
|
for _, sa := range a.SubArches {
|
||||||
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a.Arch))
|
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(sa))
|
||||||
for _, sa := range a.SubArches {
|
|
||||||
newConfig.Architectures = append(newConfig.Architectures, specs.Arch(sa))
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -87,7 +87,6 @@ google.golang.org/grpc f495f5b15ae7ccda3b38c53a1bfc
|
||||||
github.com/opencontainers/runc ff819c7e9184c13b7c2607fe6c30ae19403a7aff # v1.0.0-rc92
|
github.com/opencontainers/runc ff819c7e9184c13b7c2607fe6c30ae19403a7aff # v1.0.0-rc92
|
||||||
github.com/opencontainers/runtime-spec 4d89ac9fbff6c455f46a5bb59c6b1bb7184a5e43 # v1.0.3-0.20200728170252-4d89ac9fbff6
|
github.com/opencontainers/runtime-spec 4d89ac9fbff6c455f46a5bb59c6b1bb7184a5e43 # v1.0.3-0.20200728170252-4d89ac9fbff6
|
||||||
github.com/opencontainers/image-spec d60099175f88c47cd379c4738d158884749ed235 # v1.0.1
|
github.com/opencontainers/image-spec d60099175f88c47cd379c4738d158884749ed235 # v1.0.1
|
||||||
github.com/seccomp/libseccomp-golang 689e3c1541a84461afc49c1c87352a6cedf72e9c # v0.9.1
|
|
||||||
github.com/cyphar/filepath-securejoin a261ee33d7a517f054effbf451841abaafe3e0fd # v0.2.2
|
github.com/cyphar/filepath-securejoin a261ee33d7a517f054effbf451841abaafe3e0fd # v0.2.2
|
||||||
|
|
||||||
# go-systemd v17 is required by github.com/coreos/pkg/capnslog/journald_formatter.go
|
# go-systemd v17 is required by github.com/coreos/pkg/capnslog/journald_formatter.go
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
Copyright (c) 2015 Matthew Heon <mheon@redhat.com>
|
|
||||||
Copyright (c) 2015 Paul Moore <pmoore@redhat.com>
|
|
||||||
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.
|
|
|
@ -1,51 +0,0 @@
|
||||||
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
|
|
||||||
|
|
||||||
Documentation is also available at:
|
|
||||||
|
|
||||||
-> https://godoc.org/github.com/seccomp/libseccomp-golang
|
|
||||||
|
|
||||||
* Installing the package
|
|
||||||
|
|
||||||
The libseccomp-golang bindings require at least Go v1.2.1 and GCC v4.8.4;
|
|
||||||
earlier versions may yield unpredictable results. If you meet these
|
|
||||||
requirements you can install this package using the command below:
|
|
||||||
|
|
||||||
$ go get github.com/seccomp/libseccomp-golang
|
|
||||||
|
|
||||||
* Testing the Library
|
|
||||||
|
|
||||||
A number of tests and lint related recipes are provided in the Makefile, if
|
|
||||||
you want to run the standard regression tests, you can excute the following:
|
|
||||||
|
|
||||||
$ make check
|
|
||||||
|
|
||||||
In order to execute the 'make lint' recipe the 'golint' tool is needed, it
|
|
||||||
can be found at:
|
|
||||||
|
|
||||||
-> https://github.com/golang/lint
|
|
||||||
|
|
|
@ -1,935 +0,0 @@
|
||||||
// +build linux
|
|
||||||
|
|
||||||
// Public API specification for libseccomp Go bindings
|
|
||||||
// Contains public API for the bindings
|
|
||||||
|
|
||||||
// Package seccomp provides 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 pkg-config: libseccomp
|
|
||||||
// #include <stdlib.h>
|
|
||||||
// #include <seccomp.h>
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// Exported types
|
|
||||||
|
|
||||||
// VersionError denotes that the system libseccomp version is incompatible
|
|
||||||
// with this package.
|
|
||||||
type VersionError struct {
|
|
||||||
message string
|
|
||||||
minimum string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e VersionError) Error() string {
|
|
||||||
format := "Libseccomp version too low: "
|
|
||||||
if e.message != "" {
|
|
||||||
format += e.message + ": "
|
|
||||||
}
|
|
||||||
format += "minimum supported is "
|
|
||||||
if e.minimum != "" {
|
|
||||||
format += e.minimum + ": "
|
|
||||||
} else {
|
|
||||||
format += "2.2.0: "
|
|
||||||
}
|
|
||||||
format += "detected %d.%d.%d"
|
|
||||||
return fmt.Sprintf(format, verMajor, verMinor, verMicro)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// PowerPC and S390(x) architectures are unavailable below library version
|
|
||||||
// v2.3.0 and will returns errors if used with incompatible libraries
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// ArchPPC represents 32-bit POWERPC syscalls
|
|
||||||
ArchPPC ScmpArch = iota
|
|
||||||
// ArchPPC64 represents 64-bit POWER syscalls (big endian)
|
|
||||||
ArchPPC64 ScmpArch = iota
|
|
||||||
// ArchPPC64LE represents 64-bit POWER syscalls (little endian)
|
|
||||||
ArchPPC64LE ScmpArch = iota
|
|
||||||
// ArchS390 represents 31-bit System z/390 syscalls
|
|
||||||
ArchS390 ScmpArch = iota
|
|
||||||
// ArchS390X represents 64-bit System z/390 syscalls
|
|
||||||
ArchS390X 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
|
|
||||||
// ActLog permits the syscall to continue execution after logging it.
|
|
||||||
// This action is only usable when libseccomp API level 3 or higher is
|
|
||||||
// supported.
|
|
||||||
ActLog 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) {
|
|
||||||
if err := ensureSupportedVersion(); err != nil {
|
|
||||||
return ArchInvalid, err
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
case "ppc":
|
|
||||||
return ArchPPC, nil
|
|
||||||
case "ppc64":
|
|
||||||
return ArchPPC64, nil
|
|
||||||
case "ppc64le":
|
|
||||||
return ArchPPC64LE, nil
|
|
||||||
case "s390":
|
|
||||||
return ArchS390, nil
|
|
||||||
case "s390x":
|
|
||||||
return ArchS390X, nil
|
|
||||||
default:
|
|
||||||
return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %q", 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 ArchPPC:
|
|
||||||
return "ppc"
|
|
||||||
case ArchPPC64:
|
|
||||||
return "ppc64"
|
|
||||||
case ArchPPC64LE:
|
|
||||||
return "ppc64le"
|
|
||||||
case ArchS390:
|
|
||||||
return "s390"
|
|
||||||
case ArchS390X:
|
|
||||||
return "s390x"
|
|
||||||
case ArchNative:
|
|
||||||
return "native"
|
|
||||||
case ArchInvalid:
|
|
||||||
return "Invalid architecture"
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("Unknown architecture %#x", uint(a))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 fmt.Sprintf("Unrecognized comparison operator %#x", uint(a))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 ActLog:
|
|
||||||
return "Action: Log system call"
|
|
||||||
case ActAllow:
|
|
||||||
return "Action: Allow system call"
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("Unrecognized Action %#x", uint(a))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 uint) {
|
|
||||||
return verMajor, verMinor, verMicro
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetApi returns the API level supported by the system.
|
|
||||||
// Returns a positive int containing the API level, or 0 with an error if the
|
|
||||||
// API level could not be detected due to the library being older than v2.4.0.
|
|
||||||
// See the seccomp_api_get(3) man page for details on available API levels:
|
|
||||||
// https://github.com/seccomp/libseccomp/blob/master/doc/man/man3/seccomp_api_get.3
|
|
||||||
func GetApi() (uint, error) {
|
|
||||||
return getApi()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetApi forcibly sets the API level. General use of this function is strongly
|
|
||||||
// discouraged.
|
|
||||||
// Returns an error if the API level could not be set. An error is always
|
|
||||||
// returned if the library is older than v2.4.0
|
|
||||||
// See the seccomp_api_get(3) man page for details on available API levels:
|
|
||||||
// https://github.com/seccomp/libseccomp/blob/master/doc/man/man3/seccomp_api_get.3
|
|
||||||
func SetApi(api uint) error {
|
|
||||||
return setApi(api)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 for %#x", int32(s))
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
if err := ensureSupportedVersion(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
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: %q", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 := ensureSupportedVersion(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
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: %q on %v", name, arch)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 err := ensureSupportedVersion(); err != nil {
|
|
||||||
return condStruct, err
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (%d given)", arg)
|
|
||||||
} else if len(values) > 2 {
|
|
||||||
return condStruct, fmt.Errorf("conditions can have at most 2 arguments (%d given)", len(values))
|
|
||||||
} 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) {
|
|
||||||
if err := ensureSupportedVersion(); err != nil {
|
|
||||||
return ArchInvalid, err
|
|
||||||
}
|
|
||||||
|
|
||||||
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 := ensureSupportedVersion(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
// Enable TSync so all goroutines will receive the same rules
|
|
||||||
// If the kernel does not support TSYNC, allow us to continue without error
|
|
||||||
if err := filter.setFilterAttr(filterAttrTsync, 0x1); err != nil && err != syscall.ENOTSUP {
|
|
||||||
filter.Release()
|
|
||||||
return nil, fmt.Errorf("could not create filter - error setting tsync bit: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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, and No New Privs 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLogBit returns the current state the Log bit will be set to on the filter
|
|
||||||
// being loaded, or an error if an issue was encountered retrieving the value.
|
|
||||||
// The Log bit tells the kernel that all actions taken by the filter, with the
|
|
||||||
// exception of ActAllow, should be logged.
|
|
||||||
// The Log bit is only usable when libseccomp API level 3 or higher is
|
|
||||||
// supported.
|
|
||||||
func (f *ScmpFilter) GetLogBit() (bool, error) {
|
|
||||||
log, err := f.getFilterAttr(filterAttrLog)
|
|
||||||
if err != nil {
|
|
||||||
api, apiErr := getApi()
|
|
||||||
if (apiErr != nil && api == 0) || (apiErr == nil && api < 3) {
|
|
||||||
return false, fmt.Errorf("getting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher")
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if log == 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLogBit sets the state of the Log bit, which will be applied on filter
|
|
||||||
// load, or an error if an issue was encountered setting the value.
|
|
||||||
// The Log bit is only usable when libseccomp API level 3 or higher is
|
|
||||||
// supported.
|
|
||||||
func (f *ScmpFilter) SetLogBit(state bool) error {
|
|
||||||
var toSet C.uint32_t = 0x0
|
|
||||||
|
|
||||||
if state {
|
|
||||||
toSet = 0x1
|
|
||||||
}
|
|
||||||
|
|
||||||
err := f.setFilterAttr(filterAttrLog, toSet)
|
|
||||||
if err != nil {
|
|
||||||
api, apiErr := getApi()
|
|
||||||
if (apiErr != nil && api == 0) || (apiErr == nil && api < 3) {
|
|
||||||
return fmt.Errorf("setting the log bit is only supported in libseccomp 2.4.0 and newer with API level 3 or higher")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,571 +0,0 @@
|
||||||
// +build linux
|
|
||||||
|
|
||||||
// Internal functions for libseccomp Go bindings
|
|
||||||
// No exported functions
|
|
||||||
|
|
||||||
package seccomp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Unexported C wrapping code - provides the C-Golang interface
|
|
||||||
// Get the seccomp header in scope
|
|
||||||
// Need stdlib.h for free() on cstrings
|
|
||||||
|
|
||||||
// #cgo pkg-config: libseccomp
|
|
||||||
/*
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <seccomp.h>
|
|
||||||
|
|
||||||
#if SCMP_VER_MAJOR < 2
|
|
||||||
#error Minimum supported version of Libseccomp is v2.2.0
|
|
||||||
#elif SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 2
|
|
||||||
#error Minimum supported version of Libseccomp is v2.2.0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ARCH_BAD ~0
|
|
||||||
|
|
||||||
const uint32_t C_ARCH_BAD = ARCH_BAD;
|
|
||||||
|
|
||||||
#ifndef SCMP_ARCH_PPC
|
|
||||||
#define SCMP_ARCH_PPC ARCH_BAD
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SCMP_ARCH_PPC64
|
|
||||||
#define SCMP_ARCH_PPC64 ARCH_BAD
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SCMP_ARCH_PPC64LE
|
|
||||||
#define SCMP_ARCH_PPC64LE ARCH_BAD
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SCMP_ARCH_S390
|
|
||||||
#define SCMP_ARCH_S390 ARCH_BAD
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SCMP_ARCH_S390X
|
|
||||||
#define SCMP_ARCH_S390X 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_ARCH_PPC = SCMP_ARCH_PPC;
|
|
||||||
const uint32_t C_ARCH_PPC64 = SCMP_ARCH_PPC64;
|
|
||||||
const uint32_t C_ARCH_PPC64LE = SCMP_ARCH_PPC64LE;
|
|
||||||
const uint32_t C_ARCH_S390 = SCMP_ARCH_S390;
|
|
||||||
const uint32_t C_ARCH_S390X = SCMP_ARCH_S390X;
|
|
||||||
|
|
||||||
#ifndef SCMP_ACT_LOG
|
|
||||||
#define SCMP_ACT_LOG 0x7ffc0000U
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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_LOG = SCMP_ACT_LOG;
|
|
||||||
const uint32_t C_ACT_ALLOW = SCMP_ACT_ALLOW;
|
|
||||||
|
|
||||||
// The libseccomp SCMP_FLTATR_CTL_LOG member of the scmp_filter_attr enum was
|
|
||||||
// added in v2.4.0
|
|
||||||
#if (SCMP_VER_MAJOR < 2) || \
|
|
||||||
(SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 4)
|
|
||||||
#define SCMP_FLTATR_CTL_LOG _SCMP_FLTATR_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 uint32_t C_ATTRIBUTE_LOG = (uint32_t)SCMP_FLTATR_CTL_LOG;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
#if SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR >= 3
|
|
||||||
unsigned int get_major_version()
|
|
||||||
{
|
|
||||||
return seccomp_version()->major;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int get_minor_version()
|
|
||||||
{
|
|
||||||
return seccomp_version()->minor;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int get_micro_version()
|
|
||||||
{
|
|
||||||
return seccomp_version()->micro;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
unsigned int get_major_version()
|
|
||||||
{
|
|
||||||
return (unsigned int)C_VERSION_MAJOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int get_minor_version()
|
|
||||||
{
|
|
||||||
return (unsigned int)C_VERSION_MINOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int get_micro_version()
|
|
||||||
{
|
|
||||||
return (unsigned int)C_VERSION_MICRO;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The libseccomp API level functions were added in v2.4.0
|
|
||||||
#if (SCMP_VER_MAJOR < 2) || \
|
|
||||||
(SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR < 4)
|
|
||||||
const unsigned int seccomp_api_get(void)
|
|
||||||
{
|
|
||||||
// libseccomp-golang requires libseccomp v2.2.0, at a minimum, which
|
|
||||||
// supported API level 2. However, the kernel may not support API level
|
|
||||||
// 2 constructs which are the seccomp() system call and the TSYNC
|
|
||||||
// filter flag. Return the "reserved" value of 0 here to indicate that
|
|
||||||
// proper API level support is not available in libseccomp.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int seccomp_api_set(unsigned int level)
|
|
||||||
{
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct scmp_arg_cmp* scmp_cast_t;
|
|
||||||
|
|
||||||
void* make_arg_cmp_array(unsigned int length)
|
|
||||||
{
|
|
||||||
return calloc(length, sizeof(struct scmp_arg_cmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper to add an scmp_arg_cmp struct to an existing arg_cmp array
|
|
||||||
void add_struct_arg_cmp(
|
|
||||||
struct scmp_arg_cmp* arr,
|
|
||||||
unsigned int pos,
|
|
||||||
unsigned int arg,
|
|
||||||
int compare,
|
|
||||||
uint64_t a,
|
|
||||||
uint64_t b
|
|
||||||
)
|
|
||||||
{
|
|
||||||
arr[pos].arg = arg;
|
|
||||||
arr[pos].op = compare;
|
|
||||||
arr[pos].datum_a = a;
|
|
||||||
arr[pos].datum_b = b;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// Nonexported types
|
|
||||||
type scmpFilterAttr uint32
|
|
||||||
|
|
||||||
// Nonexported constants
|
|
||||||
|
|
||||||
const (
|
|
||||||
filterAttrActDefault scmpFilterAttr = iota
|
|
||||||
filterAttrActBadArch scmpFilterAttr = iota
|
|
||||||
filterAttrNNP scmpFilterAttr = iota
|
|
||||||
filterAttrTsync scmpFilterAttr = iota
|
|
||||||
filterAttrLog 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 = ArchS390X
|
|
||||||
// Comparison boundaries to check for action validity
|
|
||||||
actionStart ScmpAction = ActKill
|
|
||||||
actionEnd ScmpAction = ActLog
|
|
||||||
// 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 = uint(C.get_major_version())
|
|
||||||
verMinor = uint(C.get_minor_version())
|
|
||||||
verMicro = uint(C.get_micro_version())
|
|
||||||
)
|
|
||||||
|
|
||||||
// Nonexported functions
|
|
||||||
|
|
||||||
// Check if library version is greater than or equal to the given one
|
|
||||||
func checkVersionAbove(major, minor, micro uint) bool {
|
|
||||||
return (verMajor > major) ||
|
|
||||||
(verMajor == major && verMinor > minor) ||
|
|
||||||
(verMajor == major && verMinor == minor && verMicro >= micro)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the library is supported, i.e. >= 2.2.0.
|
|
||||||
func ensureSupportedVersion() error {
|
|
||||||
if !checkVersionAbove(2, 2, 0) {
|
|
||||||
return VersionError{}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the API level
|
|
||||||
func getApi() (uint, error) {
|
|
||||||
api := C.seccomp_api_get()
|
|
||||||
if api == 0 {
|
|
||||||
return 0, fmt.Errorf("API level operations are not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
return uint(api), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the API level
|
|
||||||
func setApi(api uint) error {
|
|
||||||
if retCode := C.seccomp_api_set(C.uint(api)); retCode != 0 {
|
|
||||||
if syscall.Errno(-1*retCode) == syscall.EOPNOTSUPP {
|
|
||||||
return fmt.Errorf("API level operations are not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("could not set API level: %v", retCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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, length C.uint, cond C.scmp_cast_t) error {
|
|
||||||
if length != 0 && cond == nil {
|
|
||||||
return fmt.Errorf("null conditions list, but length is nonzero")
|
|
||||||
}
|
|
||||||
|
|
||||||
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 %#x", int32(call))
|
|
||||||
} else if syscall.Errno(-1*retCode) == syscall.EPERM {
|
|
||||||
return fmt.Errorf("requested action matches default action of filter")
|
|
||||||
} else if syscall.Errno(-1*retCode) == syscall.EINVAL {
|
|
||||||
return fmt.Errorf("two checks on same syscall argument")
|
|
||||||
} 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, 0, nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We don't support conditional filtering in library version v2.1
|
|
||||||
if !checkVersionAbove(2, 2, 1) {
|
|
||||||
return VersionError{
|
|
||||||
message: "conditional filtering is not supported",
|
|
||||||
minimum: "2.2.1",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
argsArr := C.make_arg_cmp_array(C.uint(len(conds)))
|
|
||||||
if argsArr == nil {
|
|
||||||
return fmt.Errorf("error allocating memory for conditions")
|
|
||||||
}
|
|
||||||
defer C.free(argsArr)
|
|
||||||
|
|
||||||
for i, cond := range conds {
|
|
||||||
C.add_struct_arg_cmp(C.scmp_cast_t(argsArr), C.uint(i),
|
|
||||||
C.uint(cond.Argument), cond.Op.toNative(),
|
|
||||||
C.uint64_t(cond.Operand1), C.uint64_t(cond.Operand2))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.addRuleWrapper(call, action, exact, C.uint(len(conds)), C.scmp_cast_t(argsArr)); 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 %#x", uint(in))
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.toNative() == C.C_ARCH_BAD {
|
|
||||||
return fmt.Errorf("architecture %v is not supported on this version of the library", in)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func sanitizeAction(in ScmpAction) error {
|
|
||||||
inTmp := in & 0x0000FFFF
|
|
||||||
if inTmp < actionStart || inTmp > actionEnd {
|
|
||||||
return fmt.Errorf("unrecognized action %#x", uint(inTmp))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 %#x", uint(in))
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
case C.C_ARCH_PPC:
|
|
||||||
return ArchPPC, nil
|
|
||||||
case C.C_ARCH_PPC64:
|
|
||||||
return ArchPPC64, nil
|
|
||||||
case C.C_ARCH_PPC64LE:
|
|
||||||
return ArchPPC64LE, nil
|
|
||||||
case C.C_ARCH_S390:
|
|
||||||
return ArchS390, nil
|
|
||||||
case C.C_ARCH_S390X:
|
|
||||||
return ArchS390X, nil
|
|
||||||
default:
|
|
||||||
return 0x0, fmt.Errorf("unrecognized architecture %#x", uint32(a))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 ArchPPC:
|
|
||||||
return C.C_ARCH_PPC
|
|
||||||
case ArchPPC64:
|
|
||||||
return C.C_ARCH_PPC64
|
|
||||||
case ArchPPC64LE:
|
|
||||||
return C.C_ARCH_PPC64LE
|
|
||||||
case ArchS390:
|
|
||||||
return C.C_ARCH_S390
|
|
||||||
case ArchS390X:
|
|
||||||
return C.C_ARCH_S390X
|
|
||||||
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_LOG:
|
|
||||||
return ActLog, nil
|
|
||||||
case C.C_ACT_ALLOW:
|
|
||||||
return ActAllow, nil
|
|
||||||
default:
|
|
||||||
return 0x0, fmt.Errorf("unrecognized action %#x", uint32(a))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 ActLog:
|
|
||||||
return C.C_ACT_LOG
|
|
||||||
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)
|
|
||||||
case filterAttrLog:
|
|
||||||
return uint32(C.C_ATTRIBUTE_LOG)
|
|
||||||
default:
|
|
||||||
return 0x0
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue