]> git.proxmox.com Git - mirror_libseccomp.git/blame - src/python/seccomp.pyx
build: add support for Coverity scanning
[mirror_libseccomp.git] / src / python / seccomp.pyx
CommitLineData
62c6598e
PM
1#
2# Seccomp Library Python Bindings
3#
6220c8c0 4# Copyright (c) 2012,2013 Red Hat <pmoore@redhat.com>
62c6598e
PM
5# Author: Paul Moore <pmoore@redhat.com>
6#
7
8#
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.
12#
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
16# for more details.
17#
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>.
20#
21
22""" Python bindings for the libseccomp library
23
24The libseccomp library provides and easy to use, platform independent,
25interface to the Linux Kernel's syscall filtering mechanism: seccomp. The
26libseccomp API is designed to abstract away the underlying BPF based
27syscall filter language and present a more conventional function-call
28based filtering interface that should be familiar to, and easily adopted
29by application developers.
30
31Filter action values:
32 KILL - kill the process
33 ALLOW - allow the syscall to execute
34 TRAP - a SIGSYS signal will be thrown
35 ERRNO(x) - syscall will return (x)
36 TRACE(x) - if the process is being traced, (x) will be returned to the
37 tracing process via PTRACE_EVENT_SECCOMP and the
38 PTRACE_GETEVENTMSG option
39
70c89434
PM
40Argument comparison values (see the Arg class):
41
42 NE - arg != datum_a
43 LT - arg < datum_a
44 LE - arg <= datum_a
45 EQ - arg == datum_a
46 GT - arg > datum_a
47 GE - arg >= datum_a
8af9ba44 48 MASKED_EQ - (arg & datum_b) == datum_a
62c6598e
PM
49
50
51Example:
52
53 import sys
54 from seccomp import *
55
56 # create a filter object with a default KILL action
57 f = SyscallFilter(defaction=KILL)
58
59 # add syscall filter rules to allow certain syscalls
60 f.add_rule(ALLOW, "open")
61 f.add_rule(ALLOW, "close")
62 f.add_rule(ALLOW, "read", Arg(0, EQ, sys.stdin))
63 f.add_rule(ALLOW, "write", Arg(0, EQ, sys.stdout))
64 f.add_rule(ALLOW, "write", Arg(0, EQ, sys.stderr))
65 f.add_rule(ALLOW, "rt_sigreturn")
66
67 # load the filter into the kernel
68 f.load()
69"""
70__author__ = 'Paul Moore <paul@paul-moore.com>'
6220c8c0 71__date__ = "7 January 2013"
62c6598e
PM
72
73from libc.stdint cimport uint32_t
74import errno
75
76cimport libseccomp
77
78KILL = libseccomp.SCMP_ACT_KILL
79TRAP = libseccomp.SCMP_ACT_TRAP
80ALLOW = libseccomp.SCMP_ACT_ALLOW
81def ERRNO(int errno):
28a57d88
AL
82 """The action ERRNO(x) means that the syscall will return (x).
83 To conform to Linux syscall calling conventions, the syscall return
84 value should almost always be a negative number.
85 """
62c6598e
PM
86 return libseccomp.SCMP_ACT_ERRNO(errno)
87def TRACE(int value):
28a57d88
AL
88 """The action TRACE(x) means that, if the process is being traced, (x)
89 will be returned to the tracing process via PTRACE_EVENT_SECCOMP
90 and the PTRACE_GETEVENTMSG option.
91 """
62c6598e
PM
92 return libseccomp.SCMP_ACT_TRACE(value)
93
94NE = libseccomp.SCMP_CMP_NE
95LT = libseccomp.SCMP_CMP_LT
96LE = libseccomp.SCMP_CMP_LE
97EQ = libseccomp.SCMP_CMP_EQ
98GE = libseccomp.SCMP_CMP_GE
99GT = libseccomp.SCMP_CMP_GT
100MASKED_EQ = libseccomp.SCMP_CMP_MASKED_EQ
101
aeac2736
PM
102def system_arch():
103 """ Return the system architecture value.
104
105 Description:
106 Returns the native system architecture value.
107 """
108 return libseccomp.seccomp_arch_native()
109
6220c8c0
PM
110def resolve_syscall(arch, syscall):
111 """ Resolve the syscall.
112
113 Arguments:
114 arch - the architecture value, e.g. Arch.*
115 syscall - the syscall name or number
116
117 Description:
118 Resolve an architecture's syscall name to the correct number or the
119 syscall number to the correct name.
120 """
f7c11d67
AL
121 cdef char *ret_str
122
f05fc7cb 123 if isinstance(syscall, basestring):
7eb16b2b 124 return libseccomp.seccomp_syscall_resolve_name_rewrite(arch, syscall)
f05fc7cb 125 elif isinstance(syscall, int):
f7c11d67
AL
126 ret_str = libseccomp.seccomp_syscall_resolve_num_arch(arch, syscall)
127 if ret_str is NULL:
128 raise ValueError('Unknown syscall %d on arch %d' % (syscall, arch))
129 else:
130 return ret_str
6220c8c0
PM
131 else:
132 raise TypeError("Syscall must either be an int or str type")
133
62c6598e
PM
134cdef class Arch:
135 """ Python object representing the SyscallFilter architecture values.
136
137 Data values:
138 NATIVE - the native architecture
139 X86 - 32-bit x86
140 X86_64 - 64-bit x86
e9b5a6eb 141 X32 - 64-bit x86 using the x32 ABI
db440d1e 142 ARM - ARM
2b9c637c
PM
143 MIPS - MIPS
144 MIPSEL - MIPS little endian
62c6598e 145 """
aeac2736 146
f05fc7cb
PM
147 cdef int _token
148
62c6598e
PM
149 NATIVE = libseccomp.SCMP_ARCH_NATIVE
150 X86 = libseccomp.SCMP_ARCH_X86
151 X86_64 = libseccomp.SCMP_ARCH_X86_64
e9b5a6eb 152 X32 = libseccomp.SCMP_ARCH_X32
db440d1e 153 ARM = libseccomp.SCMP_ARCH_ARM
2b9c637c
PM
154 MIPS = libseccomp.SCMP_ARCH_MIPS
155 MIPSEL = libseccomp.SCMP_ARCH_MIPSEL
62c6598e 156
f05fc7cb
PM
157 def __cinit__(self, arch=libseccomp.SCMP_ARCH_NATIVE):
158 """ Initialize the architecture object.
159
160 Arguments:
161 arch - the architecture name or token value
162
163 Description:
164 Create an architecture object using the given name or token value.
165 """
166 if isinstance(arch, int):
167 if arch == libseccomp.SCMP_ARCH_NATIVE:
168 self._token = libseccomp.seccomp_arch_native()
169 elif arch == libseccomp.SCMP_ARCH_X86:
170 self._token = libseccomp.SCMP_ARCH_X86
171 elif arch == libseccomp.SCMP_ARCH_X86_64:
172 self._token = libseccomp.SCMP_ARCH_X86_64
173 elif arch == libseccomp.SCMP_ARCH_X32:
174 self._token = libseccomp.SCMP_ARCH_X32
175 elif arch == libseccomp.SCMP_ARCH_ARM:
176 self._token = libseccomp.SCMP_ARCH_ARM
177 elif arch == libseccomp.SCMP_ARCH_MIPS:
178 self._token = libseccomp.SCMP_ARCH_MIPS
179 elif arch == libseccomp.SCMP_ARCH_MIPSEL:
180 self._token = libseccomp.SCMP_ARCH_MIPSEL
181 else:
182 self._token = 0;
183 elif isinstance(arch, basestring):
184 self._token = libseccomp.seccomp_arch_resolve_name(arch)
185 else:
186 raise TypeError("Architecture must be an int or str type")
187 if self._token == 0:
188 raise ValueError("Invalid architecture")
189
190 def __int__(self):
191 """ Convert the architecture object to a token value.
192
193 Description:
194 Convert the architecture object to an integer representing the
195 architecture's token value.
196 """
197 return self._token
198
62c6598e
PM
199cdef class Attr:
200 """ Python object representing the SyscallFilter attributes.
201
202 Data values:
203 ACT_DEFAULT - the filter's default action
204 ACT_BADARCH - the filter's bad architecture action
205 CTL_NNP - the filter's "no new privileges" flag
206 """
207 ACT_DEFAULT = libseccomp.SCMP_FLTATR_ACT_DEFAULT
208 ACT_BADARCH = libseccomp.SCMP_FLTATR_ACT_BADARCH
209 CTL_NNP = libseccomp.SCMP_FLTATR_CTL_NNP
210
211cdef class Arg:
212 """ Python object representing a SyscallFilter syscall argument.
213 """
214 cdef libseccomp.scmp_arg_cmp _arg
215
216 def __cinit__(self, arg, op, datum_a, datum_b = 0):
217 """ Initialize the argument comparison.
218
219 Arguments:
46a0ab2f 220 arg - the argument number, starting at 0
62c6598e
PM
221 op - the argument comparison operator, e.g. {NE,LT,LE,...}
222 datum_a - argument value
223 datum_b - argument value, only valid when op == MASKED_EQ
224
225 Description:
226 Create an argument comparison object for use with SyscallFilter.
227 """
228 self._arg.arg = arg
229 self._arg.op = op
8e1b4634
AL
230 self._arg.datum_a = datum_a
231 self._arg.datum_b = datum_b
62c6598e 232
dd673b51 233 cdef libseccomp.scmp_arg_cmp to_c(self):
62c6598e
PM
234 """ Convert the object into a C structure.
235
236 Description:
237 Helper function which should only be used internally by
238 SyscallFilter objects and exists for the sole purpose of making it
239 easier to deal with the varadic functions of the libseccomp API,
240 e.g. seccomp_rule_add().
241 """
242 return self._arg
243
244cdef class SyscallFilter:
245 """ Python object representing a seccomp syscall filter. """
246 cdef int _defaction
247 cdef libseccomp.scmp_filter_ctx _ctx
248
249 def __cinit__(self, int defaction):
4021195b
AL
250 self._ctx = libseccomp.seccomp_init(defaction)
251 if self._ctx == NULL:
252 raise RuntimeError("Library error")
253 _defaction = defaction
254
255 def __init__(self, defaction):
62c6598e
PM
256 """ Initialize the filter state
257
258 Arguments:
259 defaction - the default filter action
260
261 Description:
262 Initializes the seccomp filter state to the defaults.
263 """
62c6598e
PM
264
265 def __dealloc__(self):
266 """ Destroys the filter state and releases any resources.
267
268 Description:
269 Destroys the seccomp filter state and releases any resources
270 associated with the filter state. This function does not affect
271 any seccomp filters already loaded into the kernel.
272 """
273 if self._ctx != NULL:
274 libseccomp.seccomp_release(self._ctx)
275
276 def reset(self, int defaction = -1):
277 """ Reset the filter state.
278
279 Arguments:
280 defaction - the default filter action
281
282 Description:
283 Resets the seccomp filter state to an initial default state, if a
284 default filter action is not specified in the reset call the
285 original action will be reused. This function does not affect any
286 seccomp filters alread loaded into the kernel.
287 """
288 if defaction == -1:
289 defaction = self._defaction
290 rc = libseccomp.seccomp_reset(self._ctx, defaction)
291 if rc == -errno.EINVAL:
292 raise ValueError("Invalid action")
293 if rc != 0:
294 raise RuntimeError(str.format("Library error (errno = {0})", rc))
295 _defaction = defaction
296
297 def merge(self, SyscallFilter filter):
298 """ Merge two existing SyscallFilter objects.
299
300 Arguments:
301 filter - a valid SyscallFilter object
302
303 Description:
304 Merges a valid SyscallFilter object with the current SyscallFilter
305 object; the passed filter object will be reset on success. In
306 order to successfully merge two seccomp filters they must have the
307 same attribute values and not share any of the same architectures.
308 """
309 rc = libseccomp.seccomp_merge(self._ctx, filter._ctx)
310 if rc != 0:
311 raise RuntimeError(str.format("Library error (errno = {0})", rc))
312 filter._ctx = NULL
313 filter = SyscallFilter(filter._defaction)
314
315 def exist_arch(self, arch):
316 """ Check if the seccomp filter contains a given architecture.
317
318 Arguments:
319 arch - the architecture value, e.g. Arch.*
320
321 Description:
322 Test to see if a given architecture is included in the filter.
323 Return True is the architecture exists, False if it does not
324 exist.
325 """
326 rc = libseccomp.seccomp_arch_exist(self._ctx, arch)
327 if rc == 0:
328 return True
329 elif rc == -errno.EEXIST:
330 return False
331 elif rc == -errno.EINVAL:
332 raise ValueError("Invalid architecture")
333 else:
334 raise RuntimeError(str.format("Library error (errno = {0})", rc))
335
336 def add_arch(self, arch):
337 """ Add an architecture to the filter.
338
339 Arguments:
340 arch - the architecture value, e.g. Arch.*
341
342 Description:
343 Add the given architecture to the filter. Any new rules added
344 after this method returns successfully will be added to this new
345 architecture, but any existing rules will not be added to the new
346 architecture.
347 """
348 rc = libseccomp.seccomp_arch_add(self._ctx, arch)
349 if rc == -errno.EINVAL:
350 raise ValueError("Invalid architecture")
351 elif rc != 0:
352 raise RuntimeError(str.format("Library error (errno = {0})", rc))
353
354 def remove_arch(self, arch):
355 """ Remove an architecture from the filter.
356
357 Arguments:
358 arch - the architecture value, e.g. Arch.*
359
360 Description:
361 Remove the given architecture from the filter. The filter must
362 always contain at least one architecture, so if only one
363 architecture exists in the filter this method will fail.
364 """
365 rc = libseccomp.seccomp_arch_remove(self._ctx, arch)
366 if rc == -errno.EINVAL:
367 raise ValueError("Invalid architecture")
368 elif rc != 0:
369 raise RuntimeError(str.format("Library error (errno = {0})", rc))
370
371 def load(self):
372 """ Load the filter into the Linux Kernel.
373
374 Description:
375 Load the current filter into the Linux Kernel. As soon as the
376 method returns the filter will be active and enforcing.
377 """
378 rc = libseccomp.seccomp_load(self._ctx)
379 if rc != 0:
380 raise RuntimeError(str.format("Library error (errno = {0})", rc))
381
382 def get_attr(self, attr):
383 """ Get an attribute value from the filter.
384
385 Arguments:
386 attr - the attribute, e.g. Attr.*
387
388 Description:
389 Lookup the given attribute in the filter and return the
390 attribute's value to the caller.
391 """
392 value = 0
393 rc = libseccomp.seccomp_attr_get(self._ctx,
394 attr, <uint32_t *>&value)
395 if rc == -errno.EINVAL:
396 raise ValueError("Invalid attribute")
397 elif rc != 0:
398 raise RuntimeError(str.format("Library error (errno = {0})", rc))
399 return value
400
401 def set_attr(self, attr, int value):
402 """ Set a filter attribute.
403
404 Arguments:
405 attr - the attribute, e.g. Attr.*
406 value - the attribute value
407
408 Description:
409 Lookup the given attribute in the filter and assign it the given
410 value.
411 """
412 rc = libseccomp.seccomp_attr_set(self._ctx, attr, value)
413 if rc == -errno.EINVAL:
414 raise ValueError("Invalid attribute")
415 elif rc != 0:
416 raise RuntimeError(str.format("Library error (errno = {0})", rc))
417
418 def syscall_priority(self, syscall, int priority):
419 """ Set the filter priority of a syscall.
420
421 Arguments:
422 syscall - the syscall name or number
423 priority - the priority of the syscall
424
425 Description:
426 Set the filter priority of the given syscall. A syscall with a
427 higher priority will have less overhead in the generated filter
428 code which is loaded into the system. Priority values can range
429 from 0 to 255 inclusive.
430 """
431 if priority < 0 or priority > 255:
6220c8c0 432 raise ValueError("Syscall priority must be between 0 and 255")
62c6598e
PM
433 if isinstance(syscall, str):
434 syscall_str = syscall.encode()
435 syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
436 elif isinstance(syscall, int):
437 syscall_num = syscall
438 else:
6220c8c0 439 raise TypeError("Syscall must either be an int or str type")
62c6598e
PM
440 rc = libseccomp.seccomp_syscall_priority(self._ctx,
441 syscall_num, priority)
442 if rc != 0:
443 raise RuntimeError(str.format("Library error (errno = {0})", rc))
444
445 def add_rule(self, int action, syscall, *args):
446 """ Add a new rule to filter.
447
448 Arguments:
449 action - the rule action: KILL, TRAP, ERRNO(), TRACE(), or ALLOW
450 syscall - the syscall name or number
451 args - variable number of Arg objects
452
453 Description:
454 Add a new rule to the filter, matching on the given syscall and an
455 optional list of argument comparisons. If the rule is triggered
456 the given action will be taken by the kernel. In order for the
457 rule to trigger, the syscall as well as each argument comparison
458 must be true.
459
460 In the case where the specific rule is not valid on a specific
461 architecture, e.g. socket() on 32-bit x86, this method rewrites
462 the rule to the best possible match. If you don't want this fule
463 rewriting to take place use add_rule_exactly().
464 """
465 cdef libseccomp.scmp_arg_cmp c_arg[6]
466 if isinstance(syscall, str):
467 syscall_str = syscall.encode()
468 syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
469 elif isinstance(syscall, int):
470 syscall_num = syscall
471 else:
6220c8c0 472 raise TypeError("Syscall must either be an int or str type")
62c6598e
PM
473 """ NOTE: the code below exists solely to deal with the varadic
474 nature of seccomp_rule_add() function and the inability of Cython
475 to handle this automatically """
2f65c7f4
PM
476 if len(args) > 6:
477 raise RuntimeError("Maximum number of arguments exceeded")
dd673b51 478 cdef Arg arg
62c6598e
PM
479 for i, arg in enumerate(args):
480 c_arg[i] = arg.to_c()
481 if len(args) == 0:
482 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, 0)
483 elif len(args) == 1:
484 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
485 len(args),
486 c_arg[0])
487 elif len(args) == 2:
488 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
489 len(args),
490 c_arg[0],
491 c_arg[1])
492 elif len(args) == 3:
493 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
494 len(args),
495 c_arg[0],
496 c_arg[1],
497 c_arg[2])
498 elif len(args) == 4:
499 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
500 len(args),
501 c_arg[0],
502 c_arg[1],
503 c_arg[2],
504 c_arg[3])
505 elif len(args) == 5:
506 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
507 len(args),
508 c_arg[0],
509 c_arg[1],
510 c_arg[2],
511 c_arg[3],
512 c_arg[4])
513 elif len(args) == 6:
514 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
515 len(args),
516 c_arg[0],
517 c_arg[1],
518 c_arg[2],
519 c_arg[3],
520 c_arg[4],
521 c_arg[5])
522 else:
523 raise RuntimeError("Maximum number of arguments exceeded")
524 if rc != 0:
525 raise RuntimeError(str.format("Library error (errno = {0})", rc))
526
527 def add_rule_exactly(self, int action, syscall, *args):
528 """ Add a new rule to filter.
529
530 Arguments:
531 action - the rule action: KILL, TRAP, ERRNO(), TRACE(), or ALLOW
532 syscall - the syscall name or number
533 args - variable number of Arg objects
534
535 Description:
536 Add a new rule to the filter, matching on the given syscall and an
537 optional list of argument comparisons. If the rule is triggered
538 the given action will be taken by the kernel. In order for the
539 rule to trigger, the syscall as well as each argument comparison
540 must be true.
541
542 This method attempts to add the filter rule exactly as specified
543 which can cause problems on certain architectures, e.g. socket()
544 on 32-bit x86. For a architecture independent version of this
545 method use add_rule().
546 """
547 cdef libseccomp.scmp_arg_cmp c_arg[6]
548 if isinstance(syscall, str):
549 syscall_str = syscall.encode()
550 syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
551 elif isinstance(syscall, int):
552 syscall_num = syscall
553 else:
6220c8c0 554 raise TypeError("Syscall must either be an int or str type")
62c6598e
PM
555 """ NOTE: the code below exists solely to deal with the varadic
556 nature of seccomp_rule_add_exact() function and the inability of
557 Cython to handle this automatically """
2f65c7f4
PM
558 if len(args) > 6:
559 raise RuntimeError("Maximum number of arguments exceeded")
dd673b51 560 cdef Arg arg
62c6598e
PM
561 for i, arg in enumerate(args):
562 c_arg[i] = arg.to_c()
563 if len(args) == 0:
564 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
565 syscall_num, 0)
566 elif len(args) == 1:
567 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
568 syscall_num, len(args),
569 c_arg[0])
570 elif len(args) == 2:
571 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
572 syscall_num, len(args),
573 c_arg[0],
574 c_arg[1])
575 elif len(args) == 3:
576 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
577 syscall_num, len(args),
578 c_arg[0],
579 c_arg[1],
580 c_arg[2])
581 elif len(args) == 4:
582 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
583 syscall_num, len(args),
584 c_arg[0],
585 c_arg[1],
586 c_arg[2],
587 c_arg[3])
588 elif len(args) == 5:
589 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
590 syscall_num, len(args),
591 c_arg[0],
592 c_arg[1],
593 c_arg[2],
594 c_arg[3],
595 c_arg[4])
596 elif len(args) == 6:
597 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
598 syscall_num, len(args),
599 c_arg[0],
600 c_arg[1],
601 c_arg[2],
602 c_arg[3],
603 c_arg[4],
604 c_arg[5])
605 else:
606 raise RuntimeError("Maximum number of arguments exceeded")
607 if rc != 0:
608 raise RuntimeError(str.format("Library error (errno = {0})", rc))
609
610 def export_pfc(self, file):
611 """ Export the filter in PFC format.
612
613 Arguments:
614 file - the output file
615
616 Description:
617 Output the filter in Pseudo Filter Code (PFC) to the given file.
618 The output is functionally equivalent to the BPF based filter
619 which is loaded into the Linux Kernel.
620 """
621 rc = libseccomp.seccomp_export_pfc(self._ctx, file.fileno())
622 if rc != 0:
623 raise RuntimeError(str.format("Library error (errno = {0})", rc))
624
625 def export_bpf(self, file):
626 """ Export the filter in BPF format.
627
628 Arguments:
629 file - the output file
630
631 Output the filter in Berkley Packet Filter (BPF) to the given
632 file. The output is identical to what is loaded into the
633 Linux Kernel.
634 """
635 rc = libseccomp.seccomp_export_bpf(self._ctx, file.fileno())
636 if rc != 0:
637 raise RuntimeError(str.format("Library error (errno = {0})", rc))
638
639# kate: syntax python;
640# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off;