This patch adds Python bindings using Cython, see http://www.cython.org for more information. There are also some minor tweaks to the build macros while we are mucking around in macros.mk; nothing major.
Signed-off-by: Paul Moore <[email protected]> --- configure | 25 ++ macros.mk | 32 ++- src/Makefile | 26 ++ src/python/.gitignore | 2 src/python/Makefile | 54 ++++ src/python/libseccomp.pxd | 93 ++++++++ src/python/seccomp.pyx | 546 +++++++++++++++++++++++++++++++++++++++++++++ src/python/setup.py | 45 ++++ 8 files changed, 816 insertions(+), 7 deletions(-) create mode 100644 src/python/.gitignore create mode 100644 src/python/Makefile create mode 100644 src/python/libseccomp.pxd create mode 100644 src/python/seccomp.pyx create mode 100644 src/python/setup.py diff --git a/configure b/configure index e11dea6..8866fda 100755 --- a/configure +++ b/configure @@ -25,6 +25,7 @@ opt_prefix="/usr/local" opt_libdir="" opt_sysinc_seccomp="yes" +opt_bindings_python="no" # output files cnf_mk_file="configure.mk" @@ -33,9 +34,15 @@ cnf_h_file="configure.h" #### # functions +function test_deps() { + [[ -z "$1" ]] && return 0 + which "$1" >& /dev/null && return 0 + return 1 +} + function verify_deps() { [[ -z "$1" ]] && return - if ! which "$1" >& /dev/null; then + if ! test_deps "$1"; then echo "error: install \"$1\" and include it in your \$PATH" exit 1 fi @@ -54,6 +61,8 @@ Options: * installation configuration --prefix=PREFIX installation base [/usr/local] --libdir=DIR library directory [/usr/local/lib] +* build options + --enable-python build the python bindings, requires cython EOF } @@ -64,6 +73,7 @@ function msg_summary() { installation base: $opt_prefix library directory: $opt_libdir use system includes: $opt_sysinc_seccomp + python bindings: $opt_bindings_python EOF } @@ -162,7 +172,7 @@ verify_deps getopt # parse the command line options opt_str="$@" -opt=$(getopt -n "$0" --options "h" --longoptions "help,prefix:,libdir:" -- "$@") +opt=$(getopt -n "$0" --options "h" --longoptions "help,prefix:,libdir:,enable-python" -- "$@") eval set -- "$opt" while [[ $# -gt 0 ]]; do case "$1" in @@ -174,6 +184,10 @@ while [[ $# -gt 0 ]]; do opt_libdir="$2" shift 2 ;; + --enable-python) + opt_bindings_python="yes" + shift + ;; -h|--help) msg_usage exit 0 @@ -199,6 +213,12 @@ if [[ -e "$opt_libdir" && ! -d "$opt_libdir" ]]; then msg_error "libdir ($opt_libdir) is not a directory" exit 1 fi +if [[ "$opt_bindings_python" = "yes" ]]; then + if ! test_deps cython; then + msg_error "python bindings require the cython package" + exit 1 + fi +fi # # automatic configuration @@ -243,6 +263,7 @@ cnf_header cnf_mk_entry "CONF_INSTALL_PREFIX" "$opt_prefix" cnf_mk_entry "CONF_INSTALL_LIBDIR" "$opt_libdir" cnf_entry "CONF_SYSINC_SECCOMP" "$opt_sysinc_seccomp" +cnf_entry "CONF_BINDINGS_PYTHON" "$opt_bindings_python" # configuration footer cnf_footer diff --git a/macros.mk b/macros.mk index 6162900..5989e05 100644 --- a/macros.mk +++ b/macros.mk @@ -63,6 +63,8 @@ MKDIR ?= mkdir SED ?= sed AWK ?= awk +PYTHON ?= /usr/bin/env python + # we require gcc specific functionality GCC ?= gcc @@ -96,10 +98,28 @@ VERSION_HDR = version.h # build macros # +PY_DISTUTILS = \ + VERSION_RELEASE="$(VERSION_RELEASE)" \ + CFLAGS="$(CFLAGS) $(CPPFLAGS)" LDFLAGS="$(LDFLAGS)" \ + $(PYTHON) ./setup.py + ifeq ($(V),0) - ARCHIVE = @echo " AR $@ (add/update: $?)"; + PY_BUILD = @echo " PYTHON build"; endif -ARCHIVE += $(AR) -cru $@ $?; +PY_BUILD += $(PY_DISTUTILS) +ifeq ($(V),0) + PY_BUILD += -q +endif +PY_BUILD += build + +ifeq ($(V),0) + PY_INSTALL = @echo " PYTHON install"; +endif +PY_INSTALL += $(PY_DISTUTILS) +ifeq ($(V),0) + PY_INSTALL += -q +endif +PY_INSTALL += install ifeq ($(V),0) COMPILE = @echo " CC $@"; @@ -112,6 +132,11 @@ endif COMPILE_EXEC += $(GCC) $(CFLAGS) $(CPPFLAGS) -o $@ $< $(LDFLAGS); ifeq ($(V),0) + ARCHIVE = @echo " AR $@"; +endif +ARCHIVE += $(AR) -cru $@ $?; + +ifeq ($(V),0) LINK_EXEC = @echo " LD $@"; endif LINK_EXEC += $(GCC) $(LDFLAGS) -o $@ $^ $(LIBFLAGS); @@ -164,7 +189,8 @@ INSTALL_INC_MACRO += \ $^ "$(INSTALL_INC_DIR)"; ifeq ($(V),0) - INSTALL_MAN3_MACRO = @echo " INSTALL manpages ($(INSTALL_MAN_DIR)/man3)"; + INSTALL_MAN3_MACRO = \ + @echo " INSTALL manpages ($(INSTALL_MAN_DIR)/man3)"; endif INSTALL_MAN3_MACRO += \ $(INSTALL) -o $(INSTALL_OWNER) -g $(INSTALL_GROUP) \ diff --git a/src/Makefile b/src/Makefile index 394e21a..f3a192c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -46,12 +46,22 @@ OBJS = \ DEPS = $(OBJS:%.o=%.d) # +# bindings configuration +# + +BINDINGS = + +ifeq ($(CONF_BINDINGS_PYTHON), 1) + BINDINGS += python +endif + +# # targets # -.PHONY: all install clean +.PHONY: all install clean python -all: $(LIB_STATIC) $(LIB_SHARED) +all: $(LIB_STATIC) $(LIB_SHARED) $(BINDINGS) -include $(DEPS) @@ -61,8 +71,20 @@ $(LIB_STATIC): $(OBJS) $(LIB_SHARED): $(OBJS) $(LINK_LIB) +python: $(LIB_STATIC) + @$(ECHO) ">> INFO: building in directory $@/ ..." + @$(MAKE) -C $@ + install: $(LIB_SHARED) $(INSTALL_LIB_MACRO) + @for dir in $(BINDINGS); do \ + $(ECHO) ">> INFO: installing from $$dir/"; \ + $(MAKE) -C $$dir install; \ + done clean: $(RM) $(DEPS) $(OBJS) $(LIB_STATIC) $(LIB_SHARED) + @for dir in $(BINDINGS); do \ + $(MAKE) -C $$dir clean; \ + done + diff --git a/src/python/.gitignore b/src/python/.gitignore new file mode 100644 index 0000000..fc8966d --- /dev/null +++ b/src/python/.gitignore @@ -0,0 +1,2 @@ +build +seccomp.c diff --git a/src/python/Makefile b/src/python/Makefile new file mode 100644 index 0000000..3543a65 --- /dev/null +++ b/src/python/Makefile @@ -0,0 +1,54 @@ +# +# Enhanced Seccomp Library Python Bindings Makefile +# +# Copyright (c) 2012 Red Hat <[email protected]> +# Author: Paul Moore <[email protected]> +# + +# +# 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>. +# + +# +# macros +# + +include ../../macros.mk + +# +# configuration +# + +include $(TOPDIR)/version_info.mk +include $(TOPDIR)/configure.mk +include $(TOPDIR)/install.mk + +LIB_STATIC = ../libseccomp.a + +# +# targets +# + +.PHONY: all install clean + +all: build + +build: $(LIB_STATIC) libseccomp.pxd seccomp.pyx + @$(RM) seccomp.c + $(PY_BUILD) && touch build + +install: build + $(PY_INSTALL) install --prefix=$(DESTDIR)/$(INSTALL_PREFIX) + +clean: + $(RM) -rf build seccomp.c diff --git a/src/python/libseccomp.pxd b/src/python/libseccomp.pxd new file mode 100644 index 0000000..4faed03 --- /dev/null +++ b/src/python/libseccomp.pxd @@ -0,0 +1,93 @@ +# +# Seccomp Library Python Bindings +# +# Copyright (c) 2012 Red Hat <[email protected]> +# Author: Paul Moore <[email protected]> +# + +# +# 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>. +# + +from libc.stdint cimport uint8_t, uint32_t, uint64_t + +cdef extern from "seccomp.h": + + ctypedef void* scmp_filter_ctx + + cdef enum: + SCMP_ARCH_NATIVE + SCMP_ARCH_X86 + SCMP_ARCH_X86_64 + + cdef enum scmp_filter_attr: + SCMP_FLTATR_ACT_DEFAULT + SCMP_FLTATR_ACT_BADARCH + SCMP_FLTATR_CTL_NNP + + cdef enum scmp_compare: + SCMP_CMP_NE + SCMP_CMP_LT + SCMP_CMP_LE + SCMP_CMP_EQ + SCMP_CMP_GE + SCMP_CMP_GT + SCMP_CMP_MASKED_EQ + + cdef enum: + SCMP_ACT_KILL + SCMP_ACT_TRAP + SCMP_ACT_ALLOW + unsigned int SCMP_ACT_ERRNO(int errno) + unsigned int SCMP_ACT_TRACE(int value) + + ctypedef uint64_t scmp_datum_t + + cdef struct scmp_arg_cmp: + unsigned int arg + scmp_compare op + scmp_datum_t datum_a + scmp_datum_t datum_b + + scmp_filter_ctx seccomp_init(uint32_t def_action) + int seccomp_reset(scmp_filter_ctx ctx, uint32_t def_action) + void seccomp_release(scmp_filter_ctx ctx) + + int seccomp_merge(scmp_filter_ctx ctx_dst, scmp_filter_ctx ctx_src) + + int seccomp_arch_exist(scmp_filter_ctx ctx, uint32_t arch_token) + int seccomp_arch_add(scmp_filter_ctx ctx, uint32_t arch_token) + int seccomp_arch_remove(scmp_filter_ctx ctx, uint32_t arch_token) + + int seccomp_load(scmp_filter_ctx ctx) + + int seccomp_attr_get(scmp_filter_ctx ctx, + scmp_filter_attr attr, uint32_t* value) + int seccomp_attr_set(scmp_filter_ctx ctx, + scmp_filter_attr attr, uint32_t value) + + int seccomp_syscall_resolve_name(char *name) + int seccomp_syscall_priority(scmp_filter_ctx ctx, + int syscall, uint8_t priority) + + int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, + int syscall, unsigned int arg_cnt, ...) + + int seccomp_rule_add_exact(scmp_filter_ctx ctx, uint32_t action, + int syscall, unsigned int arg_cnt, ...) + + int seccomp_export_pfc(scmp_filter_ctx ctx, int fd) + int seccomp_export_bpf(scmp_filter_ctx ctx, int fd) + +# kate: syntax python; +# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off; diff --git a/src/python/seccomp.pyx b/src/python/seccomp.pyx new file mode 100644 index 0000000..30e28a0 --- /dev/null +++ b/src/python/seccomp.pyx @@ -0,0 +1,546 @@ +# +# Seccomp Library Python Bindings +# +# Copyright (c) 2012 Red Hat <[email protected]> +# Author: Paul Moore <[email protected]> +# + +# +# 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: + + NE - argument not equal the value + LT - argument less than the value + LE - argument less than, or equal to, the value + EQ - argument equal the value + GT - argument greater than the value + GE - argument greater than, or equal to, the value + MASKED_EQ - masked argument is equal to the value + + +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 <[email protected]>' +__date__ = "31 October 2012" + +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): + return libseccomp.SCMP_ACT_ERRNO(errno) +def TRACE(int value): + 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 + +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 + """ + + NATIVE = libseccomp.SCMP_ARCH_NATIVE + X86 = libseccomp.SCMP_ARCH_X86 + X86_64 = libseccomp.SCMP_ARCH_X86_64 + +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 + """ + ACT_DEFAULT = libseccomp.SCMP_FLTATR_ACT_DEFAULT + ACT_BADARCH = libseccomp.SCMP_FLTATR_ACT_BADARCH + CTL_NNP = libseccomp.SCMP_FLTATR_CTL_NNP + +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 arguement 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 + if isinstance(datum_a, file): + self._arg.datum_a = datum_a.fileno() + else: + self._arg.datum_a = datum_a + if isinstance(datum_b, file): + self._arg.datum_b = datum_b.fileno() + else: + self._arg.datum_b = datum_b + + def 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): + """ Initialize the filter state + + Arguments: + defaction - the default filter action + + Description: + Initializes the seccomp filter state to the defaults. + """ + self._ctx = libseccomp.seccomp_init(defaction) + if self._ctx == NULL: + raise RuntimeError("Library error") + _defaction = defaction + + 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. + """ + 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 """ + 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 """ + 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; diff --git a/src/python/setup.py b/src/python/setup.py new file mode 100644 index 0000000..872642e --- /dev/null +++ b/src/python/setup.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# +# Enhanced Seccomp Library Python Module Build Script +# +# Copyright (c) 2012 Red Hat <[email protected]> +# Author: Paul Moore <[email protected]> +# + +# +# 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>. +# + +import os + +from distutils.core import setup +from distutils.extension import Extension +from Cython.Distutils import build_ext + +setup( + name = "seccomp", + version = os.environ["VERSION_RELEASE"], + description = "Python binding for libseccomp", + long_description = "Python API for the Linux Kernel's syscall filtering capability, seccomp.", + url = "http://libseccomp.sf.net", + maintainer = "Paul Moore", + maintainer_email = "[email protected]", + license = "LGPLv2.1", + platforms = "Linux", + cmdclass = {'build_ext': build_ext}, + ext_modules = [ + Extension("seccomp", ["seccomp.pyx"], + extra_objects=["../libseccomp.a"]) + ] +) ------------------------------------------------------------------------------ Everyone hates slow websites. So do we. Make your web apps faster with AppDynamics Download AppDynamics Lite for free today: http://p.sf.net/sfu/appdyn_sfd2d_oct _______________________________________________ libseccomp-discuss mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/libseccomp-discuss
