2 # Seccomp Library Python Bindings
4 # Copyright (c) 2012,2013,2017 Red Hat <pmoore@redhat.com>
5 # Author: Paul Moore <paul@paul-moore.com>
9 # This library is free software; you can redistribute it and/or modify it
10 # under the terms of version 2.1 of the GNU Lesser General Public License as
11 # published by the Free Software Foundation.
13 # This library is distributed in the hope that it will be useful, but WITHOUT
14 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
18 # You should have received a copy of the GNU Lesser General Public License
19 # along with this library; if not, see <http://www.gnu.org/licenses>.
22 """ Python bindings for the libseccomp library
24 The libseccomp library provides and easy to use, platform independent,
25 interface to the Linux Kernel's syscall filtering mechanism: seccomp. The
26 libseccomp API is designed to abstract away the underlying BPF based
27 syscall filter language and present a more conventional function-call
28 based filtering interface that should be familiar to, and easily adopted
29 by application developers.
32 KILL_PROCESS - kill the process
33 KILL - kill the thread
34 LOG - allow the syscall to be executed after the action has been logged
35 ALLOW - allow the syscall to execute
36 TRAP - a SIGSYS signal will be thrown
37 ERRNO(x) - syscall will return (x)
38 TRACE(x) - if the process is being traced, (x) will be returned to the
39 tracing process via PTRACE_EVENT_SECCOMP and the
40 PTRACE_GETEVENTMSG option
42 Argument comparison values (see the Arg class):
50 MASKED_EQ - (arg & datum_a) == datum_b
58 # create a filter object with a default KILL action
59 f = SyscallFilter(defaction=KILL)
61 # add syscall filter rules to allow certain syscalls
62 f.add_rule(ALLOW, "open")
63 f.add_rule(ALLOW, "close")
64 f.add_rule(ALLOW, "read", Arg(0, EQ, sys.stdin))
65 f.add_rule(ALLOW, "write", Arg(0, EQ, sys.stdout))
66 f.add_rule(ALLOW, "write", Arg(0, EQ, sys.stderr))
67 f.add_rule(ALLOW, "rt_sigreturn")
69 # load the filter into the kernel
72 __author__ = 'Paul Moore <paul@paul-moore.com>'
73 __date__ = "3 February 2017"
75 from cpython.version cimport PY_MAJOR_VERSION
76 from libc.stdint cimport uint32_t
82 """ Convert a Python string to a C string.
85 string - the Python string
88 Convert the Python string into a form usable by C taking into consideration
89 the Python major version, e.g. Python 2.x or Python 3.x.
90 See http://docs.cython.org/en/latest/src/tutorial/strings.html for more
93 if PY_MAJOR_VERSION < 3:
96 return bytes(string, "ascii")
98 KILL_PROCESS = libseccomp.SCMP_ACT_KILL_PROCESS
99 KILL = libseccomp.SCMP_ACT_KILL
100 TRAP = libseccomp.SCMP_ACT_TRAP
101 LOG = libseccomp.SCMP_ACT_LOG
102 ALLOW = libseccomp.SCMP_ACT_ALLOW
103 def ERRNO(int errno):
104 """The action ERRNO(x) means that the syscall will return (x).
105 To conform to Linux syscall calling conventions, the syscall return
106 value should almost always be a negative number.
108 return libseccomp.SCMP_ACT_ERRNO(errno)
109 def TRACE(int value):
110 """The action TRACE(x) means that, if the process is being traced, (x)
111 will be returned to the tracing process via PTRACE_EVENT_SECCOMP
112 and the PTRACE_GETEVENTMSG option.
114 return libseccomp.SCMP_ACT_TRACE(value)
116 NE = libseccomp.SCMP_CMP_NE
117 LT = libseccomp.SCMP_CMP_LT
118 LE = libseccomp.SCMP_CMP_LE
119 EQ = libseccomp.SCMP_CMP_EQ
120 GE = libseccomp.SCMP_CMP_GE
121 GT = libseccomp.SCMP_CMP_GT
122 MASKED_EQ = libseccomp.SCMP_CMP_MASKED_EQ
125 """ Return the system architecture value.
128 Returns the native system architecture value.
130 return libseccomp.seccomp_arch_native()
132 def resolve_syscall(arch, syscall):
133 """ Resolve the syscall.
136 arch - the architecture value, e.g. Arch.*
137 syscall - the syscall name or number
140 Resolve an architecture's syscall name to the correct number or the
141 syscall number to the correct name.
145 if isinstance(syscall, basestring):
146 return libseccomp.seccomp_syscall_resolve_name_rewrite(arch,
148 elif isinstance(syscall, int):
149 ret_str = libseccomp.seccomp_syscall_resolve_num_arch(arch, syscall)
151 raise ValueError('Unknown syscall %d on arch %d' % (syscall, arch))
155 raise TypeError("Syscall must either be an int or str type")
158 """ Query the level of API support
161 Returns the API level value indicating the current supported
164 level = libseccomp.seccomp_api_get()
166 raise RuntimeError(str.format("Library error (errno = {0})", level))
170 def set_api(unsigned int level):
171 """ Set the level of API support
174 level - the API level
177 This function forcibly sets the API level at runtime. General use
178 of this function is strongly discouraged.
180 rc = libseccomp.seccomp_api_set(level)
181 if rc == -errno.EINVAL:
182 raise ValueError("Invalid level")
184 raise RuntimeError(str.format("Library error (errno = {0})", rc))
187 """ Python object representing the SyscallFilter architecture values.
190 NATIVE - the native architecture
193 X32 - 64-bit x86 using the x32 ABI
197 MIPS64 - MIPS 64-bit ABI
198 MIPS64N32 - MIPS N32 ABI
199 MIPSEL - MIPS little endian O32 ABI
200 MIPSEL64 - MIPS little endian 64-bit ABI
201 MIPSEL64N32 - MIPS little endian N32 ABI
202 PARISC - 32-bit PA-RISC
203 PARISC64 - 64-bit PA-RISC
204 PPC64 - 64-bit PowerPC
210 NATIVE = libseccomp.SCMP_ARCH_NATIVE
211 X86 = libseccomp.SCMP_ARCH_X86
212 X86_64 = libseccomp.SCMP_ARCH_X86_64
213 X32 = libseccomp.SCMP_ARCH_X32
214 ARM = libseccomp.SCMP_ARCH_ARM
215 AARCH64 = libseccomp.SCMP_ARCH_AARCH64
216 MIPS = libseccomp.SCMP_ARCH_MIPS
217 MIPS64 = libseccomp.SCMP_ARCH_MIPS64
218 MIPS64N32 = libseccomp.SCMP_ARCH_MIPS64N32
219 MIPSEL = libseccomp.SCMP_ARCH_MIPSEL
220 MIPSEL64 = libseccomp.SCMP_ARCH_MIPSEL64
221 MIPSEL64N32 = libseccomp.SCMP_ARCH_MIPSEL64N32
222 PARISC = libseccomp.SCMP_ARCH_PARISC
223 PARISC64 = libseccomp.SCMP_ARCH_PARISC64
224 PPC = libseccomp.SCMP_ARCH_PPC
225 PPC64 = libseccomp.SCMP_ARCH_PPC64
226 PPC64LE = libseccomp.SCMP_ARCH_PPC64LE
227 S390 = libseccomp.SCMP_ARCH_S390
228 S390X = libseccomp.SCMP_ARCH_S390X
230 def __cinit__(self, arch=libseccomp.SCMP_ARCH_NATIVE):
231 """ Initialize the architecture object.
234 arch - the architecture name or token value
237 Create an architecture object using the given name or token value.
239 if isinstance(arch, int):
240 if arch == libseccomp.SCMP_ARCH_NATIVE:
241 self._token = libseccomp.seccomp_arch_native()
242 elif arch == libseccomp.SCMP_ARCH_X86:
243 self._token = libseccomp.SCMP_ARCH_X86
244 elif arch == libseccomp.SCMP_ARCH_X86_64:
245 self._token = libseccomp.SCMP_ARCH_X86_64
246 elif arch == libseccomp.SCMP_ARCH_X32:
247 self._token = libseccomp.SCMP_ARCH_X32
248 elif arch == libseccomp.SCMP_ARCH_ARM:
249 self._token = libseccomp.SCMP_ARCH_ARM
250 elif arch == libseccomp.SCMP_ARCH_AARCH64:
251 self._token = libseccomp.SCMP_ARCH_AARCH64
252 elif arch == libseccomp.SCMP_ARCH_MIPS:
253 self._token = libseccomp.SCMP_ARCH_MIPS
254 elif arch == libseccomp.SCMP_ARCH_MIPS64:
255 self._token = libseccomp.SCMP_ARCH_MIPS64
256 elif arch == libseccomp.SCMP_ARCH_MIPS64N32:
257 self._token = libseccomp.SCMP_ARCH_MIPS64N32
258 elif arch == libseccomp.SCMP_ARCH_MIPSEL:
259 self._token = libseccomp.SCMP_ARCH_MIPSEL
260 elif arch == libseccomp.SCMP_ARCH_MIPSEL64:
261 self._token = libseccomp.SCMP_ARCH_MIPSEL64
262 elif arch == libseccomp.SCMP_ARCH_MIPSEL64N32:
263 self._token = libseccomp.SCMP_ARCH_MIPSEL64N32
264 elif arch == libseccomp.SCMP_ARCH_PARISC:
265 self._token = libseccomp.SCMP_ARCH_PARISC
266 elif arch == libseccomp.SCMP_ARCH_PARISC64:
267 self._token = libseccomp.SCMP_ARCH_PARISC64
268 elif arch == libseccomp.SCMP_ARCH_PPC:
269 self._token = libseccomp.SCMP_ARCH_PPC
270 elif arch == libseccomp.SCMP_ARCH_PPC64:
271 self._token = libseccomp.SCMP_ARCH_PPC64
272 elif arch == libseccomp.SCMP_ARCH_PPC64LE:
273 self._token = libseccomp.SCMP_ARCH_PPC64LE
274 elif arch == libseccomp.SCMP_ARCH_S390:
275 self._token = libseccomp.SCMP_ARCH_S390
276 elif arch == libseccomp.SCMP_ARCH_S390X:
277 self._token = libseccomp.SCMP_ARCH_S390X
280 elif isinstance(arch, basestring):
281 self._token = libseccomp.seccomp_arch_resolve_name(c_str(arch))
283 raise TypeError("Architecture must be an int or str type")
285 raise ValueError("Invalid architecture")
288 """ Convert the architecture object to a token value.
291 Convert the architecture object to an integer representing the
292 architecture's token value.
297 """ Python object representing the SyscallFilter attributes.
300 ACT_DEFAULT - the filter's default action
301 ACT_BADARCH - the filter's bad architecture action
302 CTL_NNP - the filter's "no new privileges" flag
303 CTL_NNP - the filter's thread sync flag
305 ACT_DEFAULT = libseccomp.SCMP_FLTATR_ACT_DEFAULT
306 ACT_BADARCH = libseccomp.SCMP_FLTATR_ACT_BADARCH
307 CTL_NNP = libseccomp.SCMP_FLTATR_CTL_NNP
308 CTL_TSYNC = libseccomp.SCMP_FLTATR_CTL_TSYNC
309 API_TSKIP = libseccomp.SCMP_FLTATR_API_TSKIP
310 CTL_LOG = libseccomp.SCMP_FLTATR_CTL_LOG
311 CTL_SSB = libseccomp.SCMP_FLTATR_CTL_SSB
314 """ Python object representing a SyscallFilter syscall argument.
316 cdef libseccomp.scmp_arg_cmp _arg
318 def __cinit__(self, arg, op, datum_a, datum_b = 0):
319 """ Initialize the argument comparison.
322 arg - the argument number, starting at 0
323 op - the argument comparison operator, e.g. {NE,LT,LE,...}
324 datum_a - argument value
325 datum_b - argument value, only valid when op == MASKED_EQ
328 Create an argument comparison object for use with SyscallFilter.
332 self._arg.datum_a = datum_a
333 self._arg.datum_b = datum_b
335 cdef libseccomp.scmp_arg_cmp to_c(self):
336 """ Convert the object into a C structure.
339 Helper function which should only be used internally by
340 SyscallFilter objects and exists for the sole purpose of making it
341 easier to deal with the varadic functions of the libseccomp API,
342 e.g. seccomp_rule_add().
346 cdef class SyscallFilter:
347 """ Python object representing a seccomp syscall filter. """
349 cdef libseccomp.scmp_filter_ctx _ctx
351 def __cinit__(self, int defaction):
352 self._ctx = libseccomp.seccomp_init(defaction)
353 if self._ctx == NULL:
354 raise RuntimeError("Library error")
355 _defaction = defaction
357 def __init__(self, defaction):
358 """ Initialize the filter state
361 defaction - the default filter action
364 Initializes the seccomp filter state to the defaults.
367 def __dealloc__(self):
368 """ Destroys the filter state and releases any resources.
371 Destroys the seccomp filter state and releases any resources
372 associated with the filter state. This function does not affect
373 any seccomp filters already loaded into the kernel.
375 if self._ctx != NULL:
376 libseccomp.seccomp_release(self._ctx)
378 def reset(self, int defaction = -1):
379 """ Reset the filter state.
382 defaction - the default filter action
385 Resets the seccomp filter state to an initial default state, if a
386 default filter action is not specified in the reset call the
387 original action will be reused. This function does not affect any
388 seccomp filters alread loaded into the kernel.
391 defaction = self._defaction
392 rc = libseccomp.seccomp_reset(self._ctx, defaction)
393 if rc == -errno.EINVAL:
394 raise ValueError("Invalid action")
396 raise RuntimeError(str.format("Library error (errno = {0})", rc))
397 _defaction = defaction
399 def merge(self, SyscallFilter filter):
400 """ Merge two existing SyscallFilter objects.
403 filter - a valid SyscallFilter object
406 Merges a valid SyscallFilter object with the current SyscallFilter
407 object; the passed filter object will be reset on success. In
408 order to successfully merge two seccomp filters they must have the
409 same attribute values and not share any of the same architectures.
411 rc = libseccomp.seccomp_merge(self._ctx, filter._ctx)
413 raise RuntimeError(str.format("Library error (errno = {0})", rc))
415 filter = SyscallFilter(filter._defaction)
417 def exist_arch(self, arch):
418 """ Check if the seccomp filter contains a given architecture.
421 arch - the architecture value, e.g. Arch.*
424 Test to see if a given architecture is included in the filter.
425 Return True is the architecture exists, False if it does not
428 rc = libseccomp.seccomp_arch_exist(self._ctx, arch)
431 elif rc == -errno.EEXIST:
433 elif rc == -errno.EINVAL:
434 raise ValueError("Invalid architecture")
436 raise RuntimeError(str.format("Library error (errno = {0})", rc))
438 def add_arch(self, arch):
439 """ Add an architecture to the filter.
442 arch - the architecture value, e.g. Arch.*
445 Add the given architecture to the filter. Any new rules added
446 after this method returns successfully will be added to this new
447 architecture, but any existing rules will not be added to the new
450 rc = libseccomp.seccomp_arch_add(self._ctx, arch)
451 if rc == -errno.EINVAL:
452 raise ValueError("Invalid architecture")
454 raise RuntimeError(str.format("Library error (errno = {0})", rc))
456 def remove_arch(self, arch):
457 """ Remove an architecture from the filter.
460 arch - the architecture value, e.g. Arch.*
463 Remove the given architecture from the filter. The filter must
464 always contain at least one architecture, so if only one
465 architecture exists in the filter this method will fail.
467 rc = libseccomp.seccomp_arch_remove(self._ctx, arch)
468 if rc == -errno.EINVAL:
469 raise ValueError("Invalid architecture")
471 raise RuntimeError(str.format("Library error (errno = {0})", rc))
474 """ Load the filter into the Linux Kernel.
477 Load the current filter into the Linux Kernel. As soon as the
478 method returns the filter will be active and enforcing.
480 rc = libseccomp.seccomp_load(self._ctx)
482 raise RuntimeError(str.format("Library error (errno = {0})", rc))
484 def get_attr(self, attr):
485 """ Get an attribute value from the filter.
488 attr - the attribute, e.g. Attr.*
491 Lookup the given attribute in the filter and return the
492 attribute's value to the caller.
494 cdef uint32_t value = 0
495 rc = libseccomp.seccomp_attr_get(self._ctx,
496 attr, <uint32_t *>&value)
497 if rc == -errno.EINVAL:
498 raise ValueError("Invalid attribute")
500 raise RuntimeError(str.format("Library error (errno = {0})", rc))
503 def set_attr(self, attr, int value):
504 """ Set a filter attribute.
507 attr - the attribute, e.g. Attr.*
508 value - the attribute value
511 Lookup the given attribute in the filter and assign it the given
514 rc = libseccomp.seccomp_attr_set(self._ctx, attr, value)
515 if rc == -errno.EINVAL:
516 raise ValueError("Invalid attribute")
518 raise RuntimeError(str.format("Library error (errno = {0})", rc))
520 def syscall_priority(self, syscall, int priority):
521 """ Set the filter priority of a syscall.
524 syscall - the syscall name or number
525 priority - the priority of the syscall
528 Set the filter priority of the given syscall. A syscall with a
529 higher priority will have less overhead in the generated filter
530 code which is loaded into the system. Priority values can range
531 from 0 to 255 inclusive.
533 if priority < 0 or priority > 255:
534 raise ValueError("Syscall priority must be between 0 and 255")
535 if isinstance(syscall, str):
536 syscall_str = syscall.encode()
537 syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
538 elif isinstance(syscall, int):
539 syscall_num = syscall
541 raise TypeError("Syscall must either be an int or str type")
542 rc = libseccomp.seccomp_syscall_priority(self._ctx,
543 syscall_num, priority)
545 raise RuntimeError(str.format("Library error (errno = {0})", rc))
547 def add_rule(self, int action, syscall, *args):
548 """ Add a new rule to filter.
551 action - the rule action: KILL_PROCESS, KILL, TRAP, ERRNO(), TRACE(),
553 syscall - the syscall name or number
554 args - variable number of Arg objects
557 Add a new rule to the filter, matching on the given syscall and an
558 optional list of argument comparisons. If the rule is triggered
559 the given action will be taken by the kernel. In order for the
560 rule to trigger, the syscall as well as each argument comparison
563 In the case where the specific rule is not valid on a specific
564 architecture, e.g. socket() on 32-bit x86, this method rewrites
565 the rule to the best possible match. If you don't want this fule
566 rewriting to take place use add_rule_exactly().
568 cdef libseccomp.scmp_arg_cmp c_arg[6]
569 if isinstance(syscall, str):
570 syscall_str = syscall.encode()
571 syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
572 elif isinstance(syscall, int):
573 syscall_num = syscall
575 raise TypeError("Syscall must either be an int or str type")
576 """ NOTE: the code below exists solely to deal with the varadic
577 nature of seccomp_rule_add() function and the inability of Cython
578 to handle this automatically """
580 raise RuntimeError("Maximum number of arguments exceeded")
582 for i, arg in enumerate(args):
583 c_arg[i] = arg.to_c()
585 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, 0)
587 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
591 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
596 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
602 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
609 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
617 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
626 raise RuntimeError("Maximum number of arguments exceeded")
628 raise RuntimeError(str.format("Library error (errno = {0})", rc))
630 def add_rule_exactly(self, int action, syscall, *args):
631 """ Add a new rule to filter.
634 action - the rule action: KILL_PROCESS, KILL, TRAP, ERRNO(), TRACE(),
636 syscall - the syscall name or number
637 args - variable number of Arg objects
640 Add a new rule to the filter, matching on the given syscall and an
641 optional list of argument comparisons. If the rule is triggered
642 the given action will be taken by the kernel. In order for the
643 rule to trigger, the syscall as well as each argument comparison
646 This method attempts to add the filter rule exactly as specified
647 which can cause problems on certain architectures, e.g. socket()
648 on 32-bit x86. For a architecture independent version of this
649 method use add_rule().
651 cdef libseccomp.scmp_arg_cmp c_arg[6]
652 if isinstance(syscall, str):
653 syscall_str = syscall.encode()
654 syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
655 elif isinstance(syscall, int):
656 syscall_num = syscall
658 raise TypeError("Syscall must either be an int or str type")
659 """ NOTE: the code below exists solely to deal with the varadic
660 nature of seccomp_rule_add_exact() function and the inability of
661 Cython to handle this automatically """
663 raise RuntimeError("Maximum number of arguments exceeded")
665 for i, arg in enumerate(args):
666 c_arg[i] = arg.to_c()
668 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
671 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
672 syscall_num, len(args),
675 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
676 syscall_num, len(args),
680 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
681 syscall_num, len(args),
686 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
687 syscall_num, len(args),
693 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
694 syscall_num, len(args),
701 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
702 syscall_num, len(args),
710 raise RuntimeError("Maximum number of arguments exceeded")
712 raise RuntimeError(str.format("Library error (errno = {0})", rc))
714 def export_pfc(self, file):
715 """ Export the filter in PFC format.
718 file - the output file
721 Output the filter in Pseudo Filter Code (PFC) to the given file.
722 The output is functionally equivalent to the BPF based filter
723 which is loaded into the Linux Kernel.
725 rc = libseccomp.seccomp_export_pfc(self._ctx, file.fileno())
727 raise RuntimeError(str.format("Library error (errno = {0})", rc))
729 def export_bpf(self, file):
730 """ Export the filter in BPF format.
733 file - the output file
735 Output the filter in Berkley Packet Filter (BPF) to the given
736 file. The output is identical to what is loaded into the
739 rc = libseccomp.seccomp_export_bpf(self._ctx, file.fileno())
741 raise RuntimeError(str.format("Library error (errno = {0})", rc))
743 # kate: syntax python;
744 # kate: indent-mode python; space-indent on; indent-width 4; mixedindent off;