+++ /dev/null
-#
-# Seccomp Library Python Bindings
-#
-# Copyright (c) 2012,2013 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>.
-#
-
-""" 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 - kill the process
- ALLOW - allow the syscall to execute
- TRAP - a SIGSYS signal will be thrown
- 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_b) == datum_a
-
-
-Example:
-
- import sys
- from seccomp import *
-
- # create a filter object with a default KILL action
- f = SyscallFilter(defaction=KILL)
-
- # 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))
- f.add_rule(ALLOW, "write", Arg(0, EQ, sys.stdout))
- f.add_rule(ALLOW, "write", Arg(0, EQ, sys.stderr))
- f.add_rule(ALLOW, "rt_sigreturn")
-
- # load the filter into the kernel
- f.load()
-"""
-__author__ = 'Paul Moore <paul@paul-moore.com>'
-__date__ = "7 January 2013"
-
-from libc.stdint cimport uint32_t
-import errno
-
-cimport libseccomp
-
-KILL = libseccomp.SCMP_ACT_KILL
-TRAP = libseccomp.SCMP_ACT_TRAP
-ALLOW = libseccomp.SCMP_ACT_ALLOW
-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, 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")
-
-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
- PPC64 - 64-bit PowerPC
- PPC - 32-bit PowerPC
- """
-
- 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
- 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
-
- 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_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(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
- """
- 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
-
-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 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, TRAP, ERRNO(), TRACE(), 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, TRAP, ERRNO(), TRACE(), 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 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
-
- Output the filter in Berkley 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;