]> git.proxmox.com Git - mirror_libseccomp.git/blame - src/python/seccomp.pyx
arch: add basic x32 support
[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
40Argument comparison values:
41
42 NE - argument not equal the value
43 LT - argument less than the value
44 LE - argument less than, or equal to, the value
45 EQ - argument equal the value
46 GT - argument greater than the value
47 GE - argument greater than, or equal to, the value
48 MASKED_EQ - masked argument is equal to the value
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):
82 return libseccomp.SCMP_ACT_ERRNO(errno)
83def TRACE(int value):
84 return libseccomp.SCMP_ACT_TRACE(value)
85
86NE = libseccomp.SCMP_CMP_NE
87LT = libseccomp.SCMP_CMP_LT
88LE = libseccomp.SCMP_CMP_LE
89EQ = libseccomp.SCMP_CMP_EQ
90GE = libseccomp.SCMP_CMP_GE
91GT = libseccomp.SCMP_CMP_GT
92MASKED_EQ = libseccomp.SCMP_CMP_MASKED_EQ
93
aeac2736
PM
94def system_arch():
95 """ Return the system architecture value.
96
97 Description:
98 Returns the native system architecture value.
99 """
100 return libseccomp.seccomp_arch_native()
101
6220c8c0
PM
102def resolve_syscall(arch, syscall):
103 """ Resolve the syscall.
104
105 Arguments:
106 arch - the architecture value, e.g. Arch.*
107 syscall - the syscall name or number
108
109 Description:
110 Resolve an architecture's syscall name to the correct number or the
111 syscall number to the correct name.
112 """
113 if (isinstance(syscall, basestring)):
114 return libseccomp.seccomp_syscall_resolve_name_arch(arch, syscall)
115 elif (isinstance(syscall, int)):
116 return libseccomp.seccomp_syscall_resolve_num_arch(arch, syscall)
117 else:
118 raise TypeError("Syscall must either be an int or str type")
119
62c6598e
PM
120cdef class Arch:
121 """ Python object representing the SyscallFilter architecture values.
122
123 Data values:
124 NATIVE - the native architecture
125 X86 - 32-bit x86
126 X86_64 - 64-bit x86
e9b5a6eb 127 X32 - 64-bit x86 using the x32 ABI
62c6598e 128 """
aeac2736 129
62c6598e
PM
130 NATIVE = libseccomp.SCMP_ARCH_NATIVE
131 X86 = libseccomp.SCMP_ARCH_X86
132 X86_64 = libseccomp.SCMP_ARCH_X86_64
e9b5a6eb 133 X32 = libseccomp.SCMP_ARCH_X32
62c6598e
PM
134
135cdef class Attr:
136 """ Python object representing the SyscallFilter attributes.
137
138 Data values:
139 ACT_DEFAULT - the filter's default action
140 ACT_BADARCH - the filter's bad architecture action
141 CTL_NNP - the filter's "no new privileges" flag
142 """
143 ACT_DEFAULT = libseccomp.SCMP_FLTATR_ACT_DEFAULT
144 ACT_BADARCH = libseccomp.SCMP_FLTATR_ACT_BADARCH
145 CTL_NNP = libseccomp.SCMP_FLTATR_CTL_NNP
146
147cdef class Arg:
148 """ Python object representing a SyscallFilter syscall argument.
149 """
150 cdef libseccomp.scmp_arg_cmp _arg
151
152 def __cinit__(self, arg, op, datum_a, datum_b = 0):
153 """ Initialize the argument comparison.
154
155 Arguments:
156 arg - the arguement number, starting at 0
157 op - the argument comparison operator, e.g. {NE,LT,LE,...}
158 datum_a - argument value
159 datum_b - argument value, only valid when op == MASKED_EQ
160
161 Description:
162 Create an argument comparison object for use with SyscallFilter.
163 """
164 self._arg.arg = arg
165 self._arg.op = op
166 if isinstance(datum_a, file):
167 self._arg.datum_a = datum_a.fileno()
168 else:
169 self._arg.datum_a = datum_a
170 if isinstance(datum_b, file):
171 self._arg.datum_b = datum_b.fileno()
172 else:
173 self._arg.datum_b = datum_b
174
175 def to_c(self):
176 """ Convert the object into a C structure.
177
178 Description:
179 Helper function which should only be used internally by
180 SyscallFilter objects and exists for the sole purpose of making it
181 easier to deal with the varadic functions of the libseccomp API,
182 e.g. seccomp_rule_add().
183 """
184 return self._arg
185
186cdef class SyscallFilter:
187 """ Python object representing a seccomp syscall filter. """
188 cdef int _defaction
189 cdef libseccomp.scmp_filter_ctx _ctx
190
191 def __cinit__(self, int defaction):
192 """ Initialize the filter state
193
194 Arguments:
195 defaction - the default filter action
196
197 Description:
198 Initializes the seccomp filter state to the defaults.
199 """
200 self._ctx = libseccomp.seccomp_init(defaction)
201 if self._ctx == NULL:
202 raise RuntimeError("Library error")
203 _defaction = defaction
204
205 def __dealloc__(self):
206 """ Destroys the filter state and releases any resources.
207
208 Description:
209 Destroys the seccomp filter state and releases any resources
210 associated with the filter state. This function does not affect
211 any seccomp filters already loaded into the kernel.
212 """
213 if self._ctx != NULL:
214 libseccomp.seccomp_release(self._ctx)
215
216 def reset(self, int defaction = -1):
217 """ Reset the filter state.
218
219 Arguments:
220 defaction - the default filter action
221
222 Description:
223 Resets the seccomp filter state to an initial default state, if a
224 default filter action is not specified in the reset call the
225 original action will be reused. This function does not affect any
226 seccomp filters alread loaded into the kernel.
227 """
228 if defaction == -1:
229 defaction = self._defaction
230 rc = libseccomp.seccomp_reset(self._ctx, defaction)
231 if rc == -errno.EINVAL:
232 raise ValueError("Invalid action")
233 if rc != 0:
234 raise RuntimeError(str.format("Library error (errno = {0})", rc))
235 _defaction = defaction
236
237 def merge(self, SyscallFilter filter):
238 """ Merge two existing SyscallFilter objects.
239
240 Arguments:
241 filter - a valid SyscallFilter object
242
243 Description:
244 Merges a valid SyscallFilter object with the current SyscallFilter
245 object; the passed filter object will be reset on success. In
246 order to successfully merge two seccomp filters they must have the
247 same attribute values and not share any of the same architectures.
248 """
249 rc = libseccomp.seccomp_merge(self._ctx, filter._ctx)
250 if rc != 0:
251 raise RuntimeError(str.format("Library error (errno = {0})", rc))
252 filter._ctx = NULL
253 filter = SyscallFilter(filter._defaction)
254
255 def exist_arch(self, arch):
256 """ Check if the seccomp filter contains a given architecture.
257
258 Arguments:
259 arch - the architecture value, e.g. Arch.*
260
261 Description:
262 Test to see if a given architecture is included in the filter.
263 Return True is the architecture exists, False if it does not
264 exist.
265 """
266 rc = libseccomp.seccomp_arch_exist(self._ctx, arch)
267 if rc == 0:
268 return True
269 elif rc == -errno.EEXIST:
270 return False
271 elif rc == -errno.EINVAL:
272 raise ValueError("Invalid architecture")
273 else:
274 raise RuntimeError(str.format("Library error (errno = {0})", rc))
275
276 def add_arch(self, arch):
277 """ Add an architecture to the filter.
278
279 Arguments:
280 arch - the architecture value, e.g. Arch.*
281
282 Description:
283 Add the given architecture to the filter. Any new rules added
284 after this method returns successfully will be added to this new
285 architecture, but any existing rules will not be added to the new
286 architecture.
287 """
288 rc = libseccomp.seccomp_arch_add(self._ctx, arch)
289 if rc == -errno.EINVAL:
290 raise ValueError("Invalid architecture")
291 elif rc != 0:
292 raise RuntimeError(str.format("Library error (errno = {0})", rc))
293
294 def remove_arch(self, arch):
295 """ Remove an architecture from the filter.
296
297 Arguments:
298 arch - the architecture value, e.g. Arch.*
299
300 Description:
301 Remove the given architecture from the filter. The filter must
302 always contain at least one architecture, so if only one
303 architecture exists in the filter this method will fail.
304 """
305 rc = libseccomp.seccomp_arch_remove(self._ctx, arch)
306 if rc == -errno.EINVAL:
307 raise ValueError("Invalid architecture")
308 elif rc != 0:
309 raise RuntimeError(str.format("Library error (errno = {0})", rc))
310
311 def load(self):
312 """ Load the filter into the Linux Kernel.
313
314 Description:
315 Load the current filter into the Linux Kernel. As soon as the
316 method returns the filter will be active and enforcing.
317 """
318 rc = libseccomp.seccomp_load(self._ctx)
319 if rc != 0:
320 raise RuntimeError(str.format("Library error (errno = {0})", rc))
321
322 def get_attr(self, attr):
323 """ Get an attribute value from the filter.
324
325 Arguments:
326 attr - the attribute, e.g. Attr.*
327
328 Description:
329 Lookup the given attribute in the filter and return the
330 attribute's value to the caller.
331 """
332 value = 0
333 rc = libseccomp.seccomp_attr_get(self._ctx,
334 attr, <uint32_t *>&value)
335 if rc == -errno.EINVAL:
336 raise ValueError("Invalid attribute")
337 elif rc != 0:
338 raise RuntimeError(str.format("Library error (errno = {0})", rc))
339 return value
340
341 def set_attr(self, attr, int value):
342 """ Set a filter attribute.
343
344 Arguments:
345 attr - the attribute, e.g. Attr.*
346 value - the attribute value
347
348 Description:
349 Lookup the given attribute in the filter and assign it the given
350 value.
351 """
352 rc = libseccomp.seccomp_attr_set(self._ctx, attr, value)
353 if rc == -errno.EINVAL:
354 raise ValueError("Invalid attribute")
355 elif rc != 0:
356 raise RuntimeError(str.format("Library error (errno = {0})", rc))
357
358 def syscall_priority(self, syscall, int priority):
359 """ Set the filter priority of a syscall.
360
361 Arguments:
362 syscall - the syscall name or number
363 priority - the priority of the syscall
364
365 Description:
366 Set the filter priority of the given syscall. A syscall with a
367 higher priority will have less overhead in the generated filter
368 code which is loaded into the system. Priority values can range
369 from 0 to 255 inclusive.
370 """
371 if priority < 0 or priority > 255:
6220c8c0 372 raise ValueError("Syscall priority must be between 0 and 255")
62c6598e
PM
373 if isinstance(syscall, str):
374 syscall_str = syscall.encode()
375 syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
376 elif isinstance(syscall, int):
377 syscall_num = syscall
378 else:
6220c8c0 379 raise TypeError("Syscall must either be an int or str type")
62c6598e
PM
380 rc = libseccomp.seccomp_syscall_priority(self._ctx,
381 syscall_num, priority)
382 if rc != 0:
383 raise RuntimeError(str.format("Library error (errno = {0})", rc))
384
385 def add_rule(self, int action, syscall, *args):
386 """ Add a new rule to filter.
387
388 Arguments:
389 action - the rule action: KILL, TRAP, ERRNO(), TRACE(), or ALLOW
390 syscall - the syscall name or number
391 args - variable number of Arg objects
392
393 Description:
394 Add a new rule to the filter, matching on the given syscall and an
395 optional list of argument comparisons. If the rule is triggered
396 the given action will be taken by the kernel. In order for the
397 rule to trigger, the syscall as well as each argument comparison
398 must be true.
399
400 In the case where the specific rule is not valid on a specific
401 architecture, e.g. socket() on 32-bit x86, this method rewrites
402 the rule to the best possible match. If you don't want this fule
403 rewriting to take place use add_rule_exactly().
404 """
405 cdef libseccomp.scmp_arg_cmp c_arg[6]
406 if isinstance(syscall, str):
407 syscall_str = syscall.encode()
408 syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
409 elif isinstance(syscall, int):
410 syscall_num = syscall
411 else:
6220c8c0 412 raise TypeError("Syscall must either be an int or str type")
62c6598e
PM
413 """ NOTE: the code below exists solely to deal with the varadic
414 nature of seccomp_rule_add() function and the inability of Cython
415 to handle this automatically """
416 for i, arg in enumerate(args):
417 c_arg[i] = arg.to_c()
418 if len(args) == 0:
419 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, 0)
420 elif len(args) == 1:
421 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
422 len(args),
423 c_arg[0])
424 elif len(args) == 2:
425 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
426 len(args),
427 c_arg[0],
428 c_arg[1])
429 elif len(args) == 3:
430 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
431 len(args),
432 c_arg[0],
433 c_arg[1],
434 c_arg[2])
435 elif len(args) == 4:
436 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
437 len(args),
438 c_arg[0],
439 c_arg[1],
440 c_arg[2],
441 c_arg[3])
442 elif len(args) == 5:
443 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
444 len(args),
445 c_arg[0],
446 c_arg[1],
447 c_arg[2],
448 c_arg[3],
449 c_arg[4])
450 elif len(args) == 6:
451 rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num,
452 len(args),
453 c_arg[0],
454 c_arg[1],
455 c_arg[2],
456 c_arg[3],
457 c_arg[4],
458 c_arg[5])
459 else:
460 raise RuntimeError("Maximum number of arguments exceeded")
461 if rc != 0:
462 raise RuntimeError(str.format("Library error (errno = {0})", rc))
463
464 def add_rule_exactly(self, int action, syscall, *args):
465 """ Add a new rule to filter.
466
467 Arguments:
468 action - the rule action: KILL, TRAP, ERRNO(), TRACE(), or ALLOW
469 syscall - the syscall name or number
470 args - variable number of Arg objects
471
472 Description:
473 Add a new rule to the filter, matching on the given syscall and an
474 optional list of argument comparisons. If the rule is triggered
475 the given action will be taken by the kernel. In order for the
476 rule to trigger, the syscall as well as each argument comparison
477 must be true.
478
479 This method attempts to add the filter rule exactly as specified
480 which can cause problems on certain architectures, e.g. socket()
481 on 32-bit x86. For a architecture independent version of this
482 method use add_rule().
483 """
484 cdef libseccomp.scmp_arg_cmp c_arg[6]
485 if isinstance(syscall, str):
486 syscall_str = syscall.encode()
487 syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str)
488 elif isinstance(syscall, int):
489 syscall_num = syscall
490 else:
6220c8c0 491 raise TypeError("Syscall must either be an int or str type")
62c6598e
PM
492 """ NOTE: the code below exists solely to deal with the varadic
493 nature of seccomp_rule_add_exact() function and the inability of
494 Cython to handle this automatically """
495 for i, arg in enumerate(args):
496 c_arg[i] = arg.to_c()
497 if len(args) == 0:
498 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
499 syscall_num, 0)
500 elif len(args) == 1:
501 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
502 syscall_num, len(args),
503 c_arg[0])
504 elif len(args) == 2:
505 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
506 syscall_num, len(args),
507 c_arg[0],
508 c_arg[1])
509 elif len(args) == 3:
510 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
511 syscall_num, len(args),
512 c_arg[0],
513 c_arg[1],
514 c_arg[2])
515 elif len(args) == 4:
516 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
517 syscall_num, len(args),
518 c_arg[0],
519 c_arg[1],
520 c_arg[2],
521 c_arg[3])
522 elif len(args) == 5:
523 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
524 syscall_num, len(args),
525 c_arg[0],
526 c_arg[1],
527 c_arg[2],
528 c_arg[3],
529 c_arg[4])
530 elif len(args) == 6:
531 rc = libseccomp.seccomp_rule_add_exact(self._ctx, action,
532 syscall_num, len(args),
533 c_arg[0],
534 c_arg[1],
535 c_arg[2],
536 c_arg[3],
537 c_arg[4],
538 c_arg[5])
539 else:
540 raise RuntimeError("Maximum number of arguments exceeded")
541 if rc != 0:
542 raise RuntimeError(str.format("Library error (errno = {0})", rc))
543
544 def export_pfc(self, file):
545 """ Export the filter in PFC format.
546
547 Arguments:
548 file - the output file
549
550 Description:
551 Output the filter in Pseudo Filter Code (PFC) to the given file.
552 The output is functionally equivalent to the BPF based filter
553 which is loaded into the Linux Kernel.
554 """
555 rc = libseccomp.seccomp_export_pfc(self._ctx, file.fileno())
556 if rc != 0:
557 raise RuntimeError(str.format("Library error (errno = {0})", rc))
558
559 def export_bpf(self, file):
560 """ Export the filter in BPF format.
561
562 Arguments:
563 file - the output file
564
565 Output the filter in Berkley Packet Filter (BPF) to the given
566 file. The output is identical to what is loaded into the
567 Linux Kernel.
568 """
569 rc = libseccomp.seccomp_export_bpf(self._ctx, file.fileno())
570 if rc != 0:
571 raise RuntimeError(str.format("Library error (errno = {0})", rc))
572
573# kate: syntax python;
574# kate: indent-mode python; space-indent on; indent-width 4; mixedindent off;