summaryrefslogtreecommitdiffstats
path: root/src/python/seccomp.pyx
diff options
context:
space:
mode:
Diffstat (limited to 'src/python/seccomp.pyx')
-rw-r--r--src/python/seccomp.pyx1048
1 files changed, 1048 insertions, 0 deletions
diff --git a/src/python/seccomp.pyx b/src/python/seccomp.pyx
new file mode 100644
index 0000000..2eeabc1
--- /dev/null
+++ b/src/python/seccomp.pyx
@@ -0,0 +1,1048 @@
+#
+# Seccomp Library Python Bindings
+#
+# Copyright (c) 2012,2013,2017 Red Hat <pmoore@redhat.com>
+# Author: Paul Moore <paul@paul-moore.com>
+#
+
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of version 2.1 of the GNU Lesser General Public License as
+# published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+# for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, see <http://www.gnu.org/licenses>.
+#
+
+# cython: language_level = 3str
+
+""" Python bindings for the libseccomp library
+
+The libseccomp library provides and easy to use, platform independent,
+interface to the Linux Kernel's syscall filtering mechanism: seccomp. 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.
+
+Filter action values:
+ KILL_PROCESS - kill the process
+ KILL - kill the thread
+ LOG - allow the syscall to be executed after the action has been logged
+ ALLOW - allow the syscall to execute
+ TRAP - a SIGSYS signal will be thrown
+ NOTIFY - a notification event will be sent via the notification API
+ ERRNO(x) - syscall will return (x)
+ TRACE(x) - if the process is being traced, (x) will be returned to the
+ tracing process via PTRACE_EVENT_SECCOMP and the
+ PTRACE_GETEVENTMSG option
+
+Argument comparison values (see the Arg class):
+
+ NE - arg != datum_a
+ LT - arg < datum_a
+ LE - arg <= datum_a
+ EQ - arg == datum_a
+ GT - arg > datum_a
+ GE - arg >= datum_a
+ MASKED_EQ - (arg & datum_a) == datum_b
+
+
+Example:
+
+ import sys
+ from seccomp import *
+
+ # create a filter object with a default KILL action
+ f = SyscallFilter(defaction=KILL)
+
+ # add some basic syscalls which python typically wants
+ f.add_rule(ALLOW, "rt_sigaction")
+ f.add_rule(ALLOW, "rt_sigreturn")
+ f.add_rule(ALLOW, "exit_group")
+ f.add_rule(ALLOW, "brk")
+
+ # add syscall filter rules to allow certain syscalls
+ f.add_rule(ALLOW, "open")
+ f.add_rule(ALLOW, "close")
+ f.add_rule(ALLOW, "read", Arg(0, EQ, sys.stdin.fileno()))
+ f.add_rule(ALLOW, "write", Arg(0, EQ, sys.stdout.fileno()))
+ f.add_rule(ALLOW, "write", Arg(0, EQ, sys.stderr.fileno()))
+
+ # load the filter into the kernel
+ f.load()
+"""
+__author__ = 'Paul Moore <paul@paul-moore.com>'
+__date__ = "3 February 2017"
+
+from cpython.version cimport PY_MAJOR_VERSION
+from libc.stdint cimport int8_t, int16_t, int32_t, int64_t
+from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t
+from libc.stdlib cimport free
+import errno
+
+cimport libseccomp
+
+def c_str(string):
+ """ Convert a Python string to a C string.
+
+ Arguments:
+ string - the Python string
+
+ Description:
+ Convert the Python string into a form usable by C taking into consideration
+ the Python major version, e.g. Python 2.x or Python 3.x.
+ See http://docs.cython.org/en/latest/src/tutorial/strings.html for more
+ information.
+ """
+ if PY_MAJOR_VERSION < 3:
+ return string
+ else:
+ return bytes(string, "ascii")
+
+KILL_PROCESS = libseccomp.SCMP_ACT_KILL_PROCESS
+KILL = libseccomp.SCMP_ACT_KILL
+TRAP = libseccomp.SCMP_ACT_TRAP
+LOG = libseccomp.SCMP_ACT_LOG
+ALLOW = libseccomp.SCMP_ACT_ALLOW
+NOTIFY = libseccomp.SCMP_ACT_NOTIFY
+def ERRNO(int errno):
+ """The action ERRNO(x) means that the syscall will return (x).
+ To conform to Linux syscall calling conventions, the syscall return
+ value should almost always be a negative number.
+ """
+ return libseccomp.SCMP_ACT_ERRNO(errno)
+def TRACE(int value):
+ """The action TRACE(x) means that, if the process is being traced, (x)
+ will be returned to the tracing process via PTRACE_EVENT_SECCOMP
+ and the PTRACE_GETEVENTMSG option.
+ """
+ return libseccomp.SCMP_ACT_TRACE(value)
+
+NE = libseccomp.SCMP_CMP_NE
+LT = libseccomp.SCMP_CMP_LT
+LE = libseccomp.SCMP_CMP_LE
+EQ = libseccomp.SCMP_CMP_EQ
+GE = libseccomp.SCMP_CMP_GE
+GT = libseccomp.SCMP_CMP_GT
+MASKED_EQ = libseccomp.SCMP_CMP_MASKED_EQ
+
+def system_arch():
+ """ Return the system architecture value.
+
+ Description:
+ Returns the native system architecture value.
+ """
+ return libseccomp.seccomp_arch_native()
+
+def resolve_syscall(arch, syscall):
+ """ Resolve the syscall.
+
+ Arguments:
+ arch - the architecture value, e.g. Arch.*
+ syscall - the syscall name or number
+
+ Description:
+ Resolve an architecture's syscall name to the correct number or the
+ syscall number to the correct name.
+ """
+ cdef char *ret_str
+
+ if isinstance(syscall, basestring):
+ return libseccomp.seccomp_syscall_resolve_name_rewrite(arch,
+ c_str(syscall))
+ elif isinstance(syscall, int):
+ ret_str = libseccomp.seccomp_syscall_resolve_num_arch(arch, syscall)
+ if ret_str is NULL:
+ raise ValueError('Unknown syscall %d on arch %d' % (syscall, arch))
+ else:
+ return ret_str
+ else:
+ raise TypeError("Syscall must either be an int or str type")
+
+def get_api():
+ """ Query the level of API support
+
+ Description:
+ Returns the API level value indicating the current supported
+ functionality.
+ """
+ level = libseccomp.seccomp_api_get()
+ if level < 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", level))
+
+ return level
+
+def set_api(unsigned int level):
+ """ Set the level of API support
+
+ Arguments:
+ level - the API level
+
+ Description:
+ This function forcibly sets the API level at runtime. General use
+ of this function is strongly discouraged.
+ """
+ rc = libseccomp.seccomp_api_set(level)
+ if rc == -errno.EINVAL:
+ raise ValueError("Invalid level")
+ elif rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+cdef class Arch:
+ """ Python object representing the SyscallFilter architecture values.
+
+ Data values:
+ NATIVE - the native architecture
+ X86 - 32-bit x86
+ X86_64 - 64-bit x86
+ X32 - 64-bit x86 using the x32 ABI
+ ARM - ARM
+ AARCH64 - 64-bit ARM
+ MIPS - MIPS O32 ABI
+ MIPS64 - MIPS 64-bit ABI
+ MIPS64N32 - MIPS N32 ABI
+ MIPSEL - MIPS little endian O32 ABI
+ MIPSEL64 - MIPS little endian 64-bit ABI
+ MIPSEL64N32 - MIPS little endian N32 ABI
+ PARISC - 32-bit PA-RISC
+ PARISC64 - 64-bit PA-RISC
+ PPC64 - 64-bit PowerPC
+ PPC - 32-bit PowerPC
+ RISCV64 - 64-bit RISC-V
+ """
+
+ cdef int _token
+
+ NATIVE = libseccomp.SCMP_ARCH_NATIVE
+ X86 = libseccomp.SCMP_ARCH_X86
+ X86_64 = libseccomp.SCMP_ARCH_X86_64
+ X32 = libseccomp.SCMP_ARCH_X32
+ ARM = libseccomp.SCMP_ARCH_ARM
+ AARCH64 = libseccomp.SCMP_ARCH_AARCH64
+ MIPS = libseccomp.SCMP_ARCH_MIPS
+ MIPS64 = libseccomp.SCMP_ARCH_MIPS64
+ MIPS64N32 = libseccomp.SCMP_ARCH_MIPS64N32
+ MIPSEL = libseccomp.SCMP_ARCH_MIPSEL
+ MIPSEL64 = libseccomp.SCMP_ARCH_MIPSEL64
+ MIPSEL64N32 = libseccomp.SCMP_ARCH_MIPSEL64N32
+ PARISC = libseccomp.SCMP_ARCH_PARISC
+ PARISC64 = libseccomp.SCMP_ARCH_PARISC64
+ PPC = libseccomp.SCMP_ARCH_PPC
+ PPC64 = libseccomp.SCMP_ARCH_PPC64
+ PPC64LE = libseccomp.SCMP_ARCH_PPC64LE
+ S390 = libseccomp.SCMP_ARCH_S390
+ S390X = libseccomp.SCMP_ARCH_S390X
+ RISCV64 = libseccomp.SCMP_ARCH_RISCV64
+
+ def __cinit__(self, arch=libseccomp.SCMP_ARCH_NATIVE):
+ """ Initialize the architecture object.
+
+ Arguments:
+ arch - the architecture name or token value
+
+ Description:
+ Create an architecture object using the given name or token value.
+ """
+ if isinstance(arch, int):
+ if arch == libseccomp.SCMP_ARCH_NATIVE:
+ self._token = libseccomp.seccomp_arch_native()
+ elif arch == libseccomp.SCMP_ARCH_X86:
+ self._token = libseccomp.SCMP_ARCH_X86
+ elif arch == libseccomp.SCMP_ARCH_X86_64:
+ self._token = libseccomp.SCMP_ARCH_X86_64
+ elif arch == libseccomp.SCMP_ARCH_X32:
+ self._token = libseccomp.SCMP_ARCH_X32
+ elif arch == libseccomp.SCMP_ARCH_ARM:
+ self._token = libseccomp.SCMP_ARCH_ARM
+ elif arch == libseccomp.SCMP_ARCH_AARCH64:
+ self._token = libseccomp.SCMP_ARCH_AARCH64
+ elif arch == libseccomp.SCMP_ARCH_MIPS:
+ self._token = libseccomp.SCMP_ARCH_MIPS
+ elif arch == libseccomp.SCMP_ARCH_MIPS64:
+ self._token = libseccomp.SCMP_ARCH_MIPS64
+ elif arch == libseccomp.SCMP_ARCH_MIPS64N32:
+ self._token = libseccomp.SCMP_ARCH_MIPS64N32
+ elif arch == libseccomp.SCMP_ARCH_MIPSEL:
+ self._token = libseccomp.SCMP_ARCH_MIPSEL
+ elif arch == libseccomp.SCMP_ARCH_MIPSEL64:
+ self._token = libseccomp.SCMP_ARCH_MIPSEL64
+ elif arch == libseccomp.SCMP_ARCH_MIPSEL64N32:
+ self._token = libseccomp.SCMP_ARCH_MIPSEL64N32
+ elif arch == libseccomp.SCMP_ARCH_PARISC:
+ self._token = libseccomp.SCMP_ARCH_PARISC
+ elif arch == libseccomp.SCMP_ARCH_PARISC64:
+ self._token = libseccomp.SCMP_ARCH_PARISC64
+ elif arch == libseccomp.SCMP_ARCH_PPC:
+ self._token = libseccomp.SCMP_ARCH_PPC
+ elif arch == libseccomp.SCMP_ARCH_PPC64:
+ self._token = libseccomp.SCMP_ARCH_PPC64
+ elif arch == libseccomp.SCMP_ARCH_PPC64LE:
+ self._token = libseccomp.SCMP_ARCH_PPC64LE
+ elif arch == libseccomp.SCMP_ARCH_S390:
+ self._token = libseccomp.SCMP_ARCH_S390
+ elif arch == libseccomp.SCMP_ARCH_S390X:
+ self._token = libseccomp.SCMP_ARCH_S390X
+ else:
+ self._token = 0;
+ elif isinstance(arch, basestring):
+ self._token = libseccomp.seccomp_arch_resolve_name(c_str(arch))
+ else:
+ raise TypeError("Architecture must be an int or str type")
+ if self._token == 0:
+ raise ValueError("Invalid architecture")
+
+ def __int__(self):
+ """ Convert the architecture object to a token value.
+
+ Description:
+ Convert the architecture object to an integer representing the
+ architecture's token value.
+ """
+ return self._token
+
+cdef class Attr:
+ """ Python object representing the SyscallFilter attributes.
+
+ Data values:
+ ACT_DEFAULT - the filter's default action
+ ACT_BADARCH - the filter's bad architecture action
+ CTL_NNP - the filter's "no new privileges" flag
+ CTL_NNP - the filter's thread sync flag
+ CTL_TSYNC - sync threads on filter load
+ CTL_TSKIP - allow rules with a -1 syscall number
+ CTL_LOG - log not-allowed actions
+ CTL_SSB - disable SSB mitigations
+ CTL_OPTIMIZE - the filter's optimization level:
+ 0: currently unused
+ 1: rules weighted by priority and complexity (DEFAULT)
+ 2: binary tree sorted by syscall number
+ API_SYSRAWRC - return the raw syscall codes
+ """
+ ACT_DEFAULT = libseccomp.SCMP_FLTATR_ACT_DEFAULT
+ ACT_BADARCH = libseccomp.SCMP_FLTATR_ACT_BADARCH
+ CTL_NNP = libseccomp.SCMP_FLTATR_CTL_NNP
+ CTL_TSYNC = libseccomp.SCMP_FLTATR_CTL_TSYNC
+ API_TSKIP = libseccomp.SCMP_FLTATR_API_TSKIP
+ CTL_LOG = libseccomp.SCMP_FLTATR_CTL_LOG
+ CTL_SSB = libseccomp.SCMP_FLTATR_CTL_SSB
+ CTL_OPTIMIZE = libseccomp.SCMP_FLTATR_CTL_OPTIMIZE
+ API_SYSRAWRC = libseccomp.SCMP_FLTATR_API_SYSRAWRC
+
+cdef class Arg:
+ """ Python object representing a SyscallFilter syscall argument.
+ """
+ cdef libseccomp.scmp_arg_cmp _arg
+
+ def __cinit__(self, arg, op, datum_a, datum_b = 0):
+ """ Initialize the argument comparison.
+
+ Arguments:
+ arg - the argument number, starting at 0
+ op - the argument comparison operator, e.g. {NE,LT,LE,...}
+ datum_a - argument value
+ datum_b - argument value, only valid when op == MASKED_EQ
+
+ Description:
+ Create an argument comparison object for use with SyscallFilter.
+ """
+ self._arg.arg = arg
+ self._arg.op = op
+ self._arg.datum_a = datum_a
+ self._arg.datum_b = datum_b
+
+ cdef libseccomp.scmp_arg_cmp to_c(self):
+ """ Convert the object into a C structure.
+
+ Description:
+ Helper function which should only be used internally by
+ SyscallFilter objects and exists for the sole purpose of making it
+ easier to deal with the varadic functions of the libseccomp API,
+ e.g. seccomp_rule_add().
+ """
+ return self._arg
+
+cdef class Notification:
+ """ Python object representing a seccomp notification.
+ """
+ cdef uint64_t _id
+ cdef uint32_t _pid
+ cdef uint32_t _flags
+ cdef int _syscall
+ cdef uint32_t _syscall_arch
+ cdef uint64_t _syscall_ip
+ cdef uint64_t _syscall_args[6]
+
+ def __cinit__(self, id, pid, flags, syscall, arch, ip, args):
+ """ Initialize the notification.
+
+ Arguments:
+ id - the notification ID
+ pid - the process ID
+ flags - the notification flags
+ syscall - the syscall number
+ ip - the instruction pointer
+ args - list of the six syscall arguments
+
+ Description:
+ Create a seccomp Notification object.
+ """
+ self._id = id
+ self._pid = pid
+ self._flags = flags
+ self._syscall = syscall
+ self._syscall_arch = arch
+ self._syscall_ip = ip
+ self._syscall_args[0] = args[0]
+ self._syscall_args[1] = args[1]
+ self._syscall_args[2] = args[2]
+ self._syscall_args[3] = args[3]
+ self._syscall_args[4] = args[4]
+ self._syscall_args[5] = args[5]
+
+ @property
+ def id(self):
+ """ Get the seccomp notification ID.
+
+ Description:
+ Get the seccomp notification ID.
+ """
+ return self._id
+
+ @property
+ def pid(self):
+ """ Get the seccomp notification process ID.
+
+ Description:
+ Get the seccomp notification process ID.
+ """
+ return self._pid
+
+ @property
+ def flags(self):
+ """ Get the seccomp notification flags.
+
+ Description:
+ Get the seccomp notification flags.
+ """
+ return self._flags
+
+ @property
+ def syscall(self):
+ """ Get the seccomp notification syscall.
+
+ Description:
+ Get the seccomp notification syscall.
+ """
+ return self._syscall
+
+ @property
+ def syscall_arch(self):
+ """ Get the seccomp notification syscall architecture.
+
+ Description:
+ Get the seccomp notification syscall architecture.
+ """
+ return self._syscall_arch
+
+ @property
+ def syscall_ip(self):
+ """ Get the seccomp notification syscall instruction pointer.
+
+ Description:
+ Get the seccomp notification syscall instruction pointer.
+ """
+ return self._syscall_ip
+
+ @property
+ def syscall_args(self):
+ """ Get the seccomp notification syscall arguments.
+
+ Description:
+ Get the seccomp notification syscall arguments in a six element list.
+ """
+ return [self._syscall_args[0], self._syscall_args[1],
+ self._syscall_args[2], self._syscall_args[3],
+ self._syscall_args[4], self._syscall_args[5]]
+
+cdef class NotificationResponse:
+ """ Python object representing a seccomp notification response.
+ """
+ cdef uint64_t _id
+ cdef int64_t _val
+ cdef int32_t _error
+ cdef uint32_t _flags
+
+ def __cinit__(self, notify, val = 0, error = 0, flags = 0):
+ """ Initialize the notification response.
+
+ Arguments:
+ notify - a Notification object
+ val - the notification response value
+ error - the notification response error
+ flags - the notification response flags
+
+ Description:
+ Create a seccomp NotificationResponse object.
+ """
+ self._id = notify.id
+ self._val = val
+ self._error = error
+ self._flags = flags
+
+ @property
+ def id(self):
+ """ Get the seccomp notification response ID.
+
+ Description:
+ Get the seccomp notification response ID.
+ """
+ return self._id
+
+ @id.setter
+ def id(self, value):
+ """ Set the seccomp notification response ID.
+
+ Arguments:
+ id - the notification response ID
+
+ Description:
+ Set the seccomp notification response ID.
+ """
+ self._id = value
+
+ @property
+ def val(self):
+ """ Get the seccomp notification response value.
+
+ Description:
+ Get the seccomp notification response value.
+ """
+ return self._val
+
+ @val.setter
+ def val(self, value):
+ """ Set the seccomp notification response value.
+
+ Arguments:
+ val - the notification response value
+
+ Description:
+ Set the seccomp notification response value.
+ """
+ self._val = value
+
+ @property
+ def error(self):
+ """ Get the seccomp notification response error.
+
+ Description:
+ Get the seccomp notification response error.
+ """
+ return self._error
+
+ @error.setter
+ def error(self, value):
+ """ Set the seccomp notification response error.
+
+ Arguments:
+ error - the notification response error
+
+ Description:
+ Set the seccomp notification response error.
+ """
+ self._error = value
+
+ @property
+ def flags(self):
+ """ Get the seccomp notification response flags.
+
+ Description:
+ Get the seccomp notification response flags.
+ """
+ return self._flags
+
+ @flags.setter
+ def flags(self, value):
+ """ Set the seccomp notification response flags.
+
+ Arguments:
+ flags - the notification response flags
+
+ Description:
+ Set the seccomp notification response flags.
+ """
+ self._flags = value
+
+cdef class SyscallFilter:
+ """ Python object representing a seccomp syscall filter. """
+ cdef int _defaction
+ cdef libseccomp.scmp_filter_ctx _ctx
+
+ def __cinit__(self, int defaction):
+ self._ctx = libseccomp.seccomp_init(defaction)
+ if self._ctx == NULL:
+ raise RuntimeError("Library error")
+ _defaction = defaction
+
+ def __init__(self, defaction):
+ """ Initialize the filter state
+
+ Arguments:
+ defaction - the default filter action
+
+ Description:
+ Initializes the seccomp filter state to the defaults.
+ """
+
+ def __dealloc__(self):
+ """ Destroys the filter state and releases any resources.
+
+ Description:
+ Destroys the seccomp filter state and releases any resources
+ associated with the filter state. This function does not affect
+ any seccomp filters already loaded into the kernel.
+ """
+ if self._ctx != NULL:
+ libseccomp.seccomp_release(self._ctx)
+
+ def reset(self, int defaction = -1):
+ """ Reset the filter state.
+
+ Arguments:
+ defaction - the default filter action
+
+ Description:
+ Resets the seccomp filter state to an initial default state, if a
+ default filter action is not specified in the reset call the
+ original action will be reused. This function does not affect any
+ seccomp filters alread loaded into the kernel.
+ """
+ if defaction == -1:
+ defaction = self._defaction
+ rc = libseccomp.seccomp_reset(self._ctx, defaction)
+ if rc == -errno.EINVAL:
+ raise ValueError("Invalid action")
+ if rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+ _defaction = defaction
+
+ def merge(self, SyscallFilter filter):
+ """ Merge two existing SyscallFilter objects.
+
+ Arguments:
+ filter - a valid SyscallFilter object
+
+ Description:
+ Merges a valid SyscallFilter object with the current SyscallFilter
+ object; the passed filter object will be reset on success. In
+ order to successfully merge two seccomp filters they must have the
+ same attribute values and not share any of the same architectures.
+ """
+ rc = libseccomp.seccomp_merge(self._ctx, filter._ctx)
+ if rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+ filter._ctx = NULL
+ filter = SyscallFilter(filter._defaction)
+
+ def exist_arch(self, arch):
+ """ Check if the seccomp filter contains a given architecture.
+
+ Arguments:
+ arch - the architecture value, e.g. Arch.*
+
+ Description:
+ Test to see if a given architecture is included in the filter.
+ Return True is the architecture exists, False if it does not
+ exist.
+ """
+ rc = libseccomp.seccomp_arch_exist(self._ctx, arch)
+ if rc == 0:
+ return True
+ elif rc == -errno.EEXIST:
+ return False
+ elif rc == -errno.EINVAL:
+ raise ValueError("Invalid architecture")
+ else:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+ def add_arch(self, arch):
+ """ Add an architecture to the filter.
+
+ Arguments:
+ arch - the architecture value, e.g. Arch.*
+
+ Description:
+ Add the given architecture to the filter. Any new rules added
+ after this method returns successfully will be added to this new
+ architecture, but any existing rules will not be added to the new
+ architecture.
+ """
+ rc = libseccomp.seccomp_arch_add(self._ctx, arch)
+ if rc == -errno.EINVAL:
+ raise ValueError("Invalid architecture")
+ elif rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+ def remove_arch(self, arch):
+ """ Remove an architecture from the filter.
+
+ Arguments:
+ arch - the architecture value, e.g. Arch.*
+
+ Description:
+ Remove the given architecture from the filter. The filter must
+ always contain at least one architecture, so if only one
+ architecture exists in the filter this method will fail.
+ """
+ rc = libseccomp.seccomp_arch_remove(self._ctx, arch)
+ if rc == -errno.EINVAL:
+ raise ValueError("Invalid architecture")
+ elif rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+ def load(self):
+ """ Load the filter into the Linux Kernel.
+
+ Description:
+ Load the current filter into the Linux Kernel. As soon as the
+ method returns the filter will be active and enforcing.
+ """
+ rc = libseccomp.seccomp_load(self._ctx)
+ if rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+ def get_attr(self, attr):
+ """ Get an attribute value from the filter.
+
+ Arguments:
+ attr - the attribute, e.g. Attr.*
+
+ Description:
+ Lookup the given attribute in the filter and return the
+ attribute's value to the caller.
+ """
+ cdef uint32_t value = 0
+ rc = libseccomp.seccomp_attr_get(self._ctx,
+ attr, <uint32_t *>&value)
+ if rc == -errno.EINVAL:
+ raise ValueError("Invalid attribute")
+ elif rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+ return value
+
+ def set_attr(self, attr, int value):
+ """ Set a filter attribute.
+
+ Arguments:
+ attr - the attribute, e.g. Attr.*
+ value - the attribute value
+
+ Description:
+ Lookup the given attribute in the filter and assign it the given
+ value.
+ """
+ rc = libseccomp.seccomp_attr_set(self._ctx, attr, value)
+ if rc == -errno.EINVAL:
+ raise ValueError("Invalid attribute")
+ elif rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+ def syscall_priority(self, syscall, int priority):
+ """ Set the filter priority of a syscall.
+
+ Arguments:
+ syscall - the syscall name or number
+ priority - the priority of the syscall
+
+ Description:
+ Set the filter priority of the given syscall. A syscall with a
+ higher priority will have less overhead in the generated filter
+ code which is loaded into the system. Priority values can range
+ from 0 to 255 inclusive.
+ """
+ if priority < 0 or priority > 255:
+ raise ValueError("Syscall priority must be between 0 and 255")
+ if isinstance(syscall, str):
+ syscall_str = syscall.encode()
+ syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
+ elif isinstance(syscall, int):
+ syscall_num = syscall
+ else:
+ raise TypeError("Syscall must either be an int or str type")
+ rc = libseccomp.seccomp_syscall_priority(self._ctx,
+ syscall_num, priority)
+ if rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+ def add_rule(self, int action, syscall, *args):
+ """ Add a new rule to filter.
+
+ Arguments:
+ action - the rule action: KILL_PROCESS, KILL, TRAP, ERRNO(), TRACE(),
+ LOG, or ALLOW
+ syscall - the syscall name or number
+ args - variable number of Arg objects
+
+ Description:
+ Add a new rule to the filter, matching on the given syscall and an
+ optional list of argument comparisons. If the rule is triggered
+ the given action will be taken by the kernel. In order for the
+ rule to trigger, the syscall as well as each argument comparison
+ must be true.
+
+ In the case where the specific rule is not valid on a specific
+ architecture, e.g. socket() on 32-bit x86, this method rewrites
+ the rule to the best possible match. If you don't want this fule
+ rewriting to take place use add_rule_exactly().
+ """
+ cdef libseccomp.scmp_arg_cmp c_arg[6]
+ if isinstance(syscall, str):
+ syscall_str = syscall.encode()
+ syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
+ elif isinstance(syscall, int):
+ syscall_num = syscall
+ else:
+ raise TypeError("Syscall must either be an int or str type")
+ """ NOTE: the code below exists solely to deal with the varadic
+ nature of seccomp_rule_add() function and the inability of Cython
+ to handle this automatically """
+ if len(args) > 6:
+ raise RuntimeError("Maximum number of arguments exceeded")
+ cdef Arg arg
+ for i, arg in enumerate(args):
+ c_arg[i] = arg.to_c()
+ if len(args) == 0:
+ rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, 0)
+ elif len(args) == 1:
+ rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
+ len(args),
+ c_arg[0])
+ elif len(args) == 2:
+ rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
+ len(args),
+ c_arg[0],
+ c_arg[1])
+ elif len(args) == 3:
+ rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
+ len(args),
+ c_arg[0],
+ c_arg[1],
+ c_arg[2])
+ elif len(args) == 4:
+ rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
+ len(args),
+ c_arg[0],
+ c_arg[1],
+ c_arg[2],
+ c_arg[3])
+ elif len(args) == 5:
+ rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
+ len(args),
+ c_arg[0],
+ c_arg[1],
+ c_arg[2],
+ c_arg[3],
+ c_arg[4])
+ elif len(args) == 6:
+ rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
+ len(args),
+ c_arg[0],
+ c_arg[1],
+ c_arg[2],
+ c_arg[3],
+ c_arg[4],
+ c_arg[5])
+ else:
+ raise RuntimeError("Maximum number of arguments exceeded")
+ if rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+ def add_rule_exactly(self, int action, syscall, *args):
+ """ Add a new rule to filter.
+
+ Arguments:
+ action - the rule action: KILL_PROCESS, KILL, TRAP, ERRNO(), TRACE(),
+ LOG, or ALLOW
+ syscall - the syscall name or number
+ args - variable number of Arg objects
+
+ Description:
+ Add a new rule to the filter, matching on the given syscall and an
+ optional list of argument comparisons. If the rule is triggered
+ the given action will be taken by the kernel. In order for the
+ rule to trigger, the syscall as well as each argument comparison
+ must be true.
+
+ This method attempts to add the filter rule exactly as specified
+ which can cause problems on certain architectures, e.g. socket()
+ on 32-bit x86. For a architecture independent version of this
+ method use add_rule().
+ """
+ cdef libseccomp.scmp_arg_cmp c_arg[6]
+ if isinstance(syscall, str):
+ syscall_str = syscall.encode()
+ syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
+ elif isinstance(syscall, int):
+ syscall_num = syscall
+ else:
+ raise TypeError("Syscall must either be an int or str type")
+ """ NOTE: the code below exists solely to deal with the varadic
+ nature of seccomp_rule_add_exact() function and the inability of
+ Cython to handle this automatically """
+ if len(args) > 6:
+ raise RuntimeError("Maximum number of arguments exceeded")
+ cdef Arg arg
+ for i, arg in enumerate(args):
+ c_arg[i] = arg.to_c()
+ if len(args) == 0:
+ rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
+ syscall_num, 0)
+ elif len(args) == 1:
+ rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
+ syscall_num, len(args),
+ c_arg[0])
+ elif len(args) == 2:
+ rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
+ syscall_num, len(args),
+ c_arg[0],
+ c_arg[1])
+ elif len(args) == 3:
+ rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
+ syscall_num, len(args),
+ c_arg[0],
+ c_arg[1],
+ c_arg[2])
+ elif len(args) == 4:
+ rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
+ syscall_num, len(args),
+ c_arg[0],
+ c_arg[1],
+ c_arg[2],
+ c_arg[3])
+ elif len(args) == 5:
+ rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
+ syscall_num, len(args),
+ c_arg[0],
+ c_arg[1],
+ c_arg[2],
+ c_arg[3],
+ c_arg[4])
+ elif len(args) == 6:
+ rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
+ syscall_num, len(args),
+ c_arg[0],
+ c_arg[1],
+ c_arg[2],
+ c_arg[3],
+ c_arg[4],
+ c_arg[5])
+ else:
+ raise RuntimeError("Maximum number of arguments exceeded")
+ if rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+ def receive_notify(self):
+ """ Receive seccomp notifications.
+
+ Description:
+ Receive a seccomp notification from the system, requires the use of
+ the NOTIFY action.
+ """
+ cdef libseccomp.seccomp_notif *req
+
+ fd = libseccomp.seccomp_notify_fd(self._ctx)
+ if fd < 0:
+ raise RuntimeError("Notifications not enabled/active")
+ rc = libseccomp.seccomp_notify_alloc(&req, NULL)
+ if rc < 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+ rc = libseccomp.seccomp_notify_receive(fd, req)
+ if rc < 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+ rc = libseccomp.seccomp_notify_id_valid(fd, req.id)
+ if rc < 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+ notify = Notification(req.id, req.pid, req.flags, req.data.nr,
+ req.data.arch, req.data.instruction_pointer,
+ [req.data.args[0], req.data.args[1],
+ req.data.args[2], req.data.args[3],
+ req.data.args[4], req.data.args[5]])
+ free(req)
+ return notify
+
+ def respond_notify(self, response):
+ """ Send a seccomp notification response.
+
+ Arguments:
+ response - the response to send to the system
+
+ Description:
+ Respond to a seccomp notification.
+ """
+ cdef libseccomp.seccomp_notif_resp *resp
+
+ fd = libseccomp.seccomp_notify_fd(self._ctx)
+ if fd < 0:
+ raise RuntimeError("Notifications not enabled/active")
+ rc = libseccomp.seccomp_notify_alloc(NULL, &resp)
+ if rc < 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+ resp.id = response.id
+ resp.val = response.val
+ resp.error = response.error
+ resp.flags = response.flags
+ rc = libseccomp.seccomp_notify_respond(fd, resp)
+ if rc < 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+ def get_notify_fd(self):
+ """ Get the seccomp notification file descriptor
+
+ Description:
+ Returns the seccomp listener file descriptor that was generated when
+ the seccomp policy was loaded. This is only valid after load() with a
+ filter that makes use of the NOTIFY action.
+ """
+ fd = libseccomp.seccomp_notify_fd(self._ctx)
+ if fd < 0:
+ raise RuntimeError("Notifications not enabled/active")
+ return fd
+
+ def export_pfc(self, file):
+ """ Export the filter in PFC format.
+
+ Arguments:
+ file - the output file
+
+ Description:
+ Output the filter in Pseudo Filter Code (PFC) to the given file.
+ The output is functionally equivalent to the BPF based filter
+ which is loaded into the Linux Kernel.
+ """
+ rc = libseccomp.seccomp_export_pfc(self._ctx, file.fileno())
+ if rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+ def export_bpf(self, file):
+ """ Export the filter in BPF format.
+
+ Arguments:
+ file - the output file
+
+ Description:
+ Output the filter in Berkeley Packet Filter (BPF) to the given
+ file. The output is identical to what is loaded into the
+ Linux Kernel.
+ """
+ rc = libseccomp.seccomp_export_bpf(self._ctx, file.fileno())
+ if rc != 0:
+ raise RuntimeError(str.format("Library error (errno = {0})", rc))
+
+# kate: syntax python;
+# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off;