]>
Commit | Line | Data |
---|---|---|
8befd5cc MG |
1 | # |
2 | # Seccomp Library Python Bindings | |
3 | # | |
4 | # Copyright (c) 2012,2013 Red Hat <pmoore@redhat.com> | |
5 | # Author: Paul Moore <paul@paul-moore.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 | ||
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. | |
30 | ||
31 | Filter 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 | ||
40 | Argument 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 | |
48 | MASKED_EQ - (arg & datum_b) == datum_a | |
49 | ||
50 | ||
51 | Example: | |
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>' | |
71 | __date__ = "7 January 2013" | |
72 | ||
73 | from libc.stdint cimport uint32_t | |
74 | import errno | |
75 | ||
76 | cimport libseccomp | |
77 | ||
78 | KILL = libseccomp.SCMP_ACT_KILL | |
79 | TRAP = libseccomp.SCMP_ACT_TRAP | |
80 | ALLOW = libseccomp.SCMP_ACT_ALLOW | |
81 | def ERRNO(int errno): | |
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 | """ | |
86 | return libseccomp.SCMP_ACT_ERRNO(errno) | |
87 | def TRACE(int value): | |
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 | """ | |
92 | return libseccomp.SCMP_ACT_TRACE(value) | |
93 | ||
94 | NE = libseccomp.SCMP_CMP_NE | |
95 | LT = libseccomp.SCMP_CMP_LT | |
96 | LE = libseccomp.SCMP_CMP_LE | |
97 | EQ = libseccomp.SCMP_CMP_EQ | |
98 | GE = libseccomp.SCMP_CMP_GE | |
99 | GT = libseccomp.SCMP_CMP_GT | |
100 | MASKED_EQ = libseccomp.SCMP_CMP_MASKED_EQ | |
101 | ||
102 | def 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 | ||
110 | def 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 | """ | |
121 | cdef char *ret_str | |
122 | ||
123 | if isinstance(syscall, basestring): | |
124 | return libseccomp.seccomp_syscall_resolve_name_rewrite(arch, syscall) | |
125 | elif isinstance(syscall, int): | |
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 | |
131 | else: | |
132 | raise TypeError("Syscall must either be an int or str type") | |
133 | ||
134 | cdef 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 | |
141 | X32 - 64-bit x86 using the x32 ABI | |
142 | ARM - ARM | |
143 | AARCH64 - 64-bit ARM | |
144 | MIPS - MIPS O32 ABI | |
145 | MIPS64 - MIPS 64-bit ABI | |
146 | MIPS64N32 - MIPS N32 ABI | |
147 | MIPSEL - MIPS little endian O32 ABI | |
148 | MIPSEL64 - MIPS little endian 64-bit ABI | |
149 | MIPSEL64N32 - MIPS little endian N32 ABI | |
150 | PPC64 - 64-bit PowerPC | |
151 | PPC - 32-bit PowerPC | |
152 | """ | |
153 | ||
154 | cdef int _token | |
155 | ||
156 | NATIVE = libseccomp.SCMP_ARCH_NATIVE | |
157 | X86 = libseccomp.SCMP_ARCH_X86 | |
158 | X86_64 = libseccomp.SCMP_ARCH_X86_64 | |
159 | X32 = libseccomp.SCMP_ARCH_X32 | |
160 | ARM = libseccomp.SCMP_ARCH_ARM | |
161 | AARCH64 = libseccomp.SCMP_ARCH_AARCH64 | |
162 | MIPS = libseccomp.SCMP_ARCH_MIPS | |
163 | MIPS64 = libseccomp.SCMP_ARCH_MIPS64 | |
164 | MIPS64N32 = libseccomp.SCMP_ARCH_MIPS64N32 | |
165 | MIPSEL = libseccomp.SCMP_ARCH_MIPSEL | |
166 | MIPSEL64 = libseccomp.SCMP_ARCH_MIPSEL64 | |
167 | MIPSEL64N32 = libseccomp.SCMP_ARCH_MIPSEL64N32 | |
168 | PPC = libseccomp.SCMP_ARCH_PPC | |
169 | PPC64 = libseccomp.SCMP_ARCH_PPC64 | |
170 | PPC64LE = libseccomp.SCMP_ARCH_PPC64LE | |
171 | S390 = libseccomp.SCMP_ARCH_S390 | |
172 | S390X = libseccomp.SCMP_ARCH_S390X | |
173 | ||
174 | def __cinit__(self, arch=libseccomp.SCMP_ARCH_NATIVE): | |
175 | """ Initialize the architecture object. | |
176 | ||
177 | Arguments: | |
178 | arch - the architecture name or token value | |
179 | ||
180 | Description: | |
181 | Create an architecture object using the given name or token value. | |
182 | """ | |
183 | if isinstance(arch, int): | |
184 | if arch == libseccomp.SCMP_ARCH_NATIVE: | |
185 | self._token = libseccomp.seccomp_arch_native() | |
186 | elif arch == libseccomp.SCMP_ARCH_X86: | |
187 | self._token = libseccomp.SCMP_ARCH_X86 | |
188 | elif arch == libseccomp.SCMP_ARCH_X86_64: | |
189 | self._token = libseccomp.SCMP_ARCH_X86_64 | |
190 | elif arch == libseccomp.SCMP_ARCH_X32: | |
191 | self._token = libseccomp.SCMP_ARCH_X32 | |
192 | elif arch == libseccomp.SCMP_ARCH_ARM: | |
193 | self._token = libseccomp.SCMP_ARCH_ARM | |
194 | elif arch == libseccomp.SCMP_ARCH_AARCH64: | |
195 | self._token = libseccomp.SCMP_ARCH_AARCH64 | |
196 | elif arch == libseccomp.SCMP_ARCH_MIPS: | |
197 | self._token = libseccomp.SCMP_ARCH_MIPS | |
198 | elif arch == libseccomp.SCMP_ARCH_MIPS64: | |
199 | self._token = libseccomp.SCMP_ARCH_MIPS64 | |
200 | elif arch == libseccomp.SCMP_ARCH_MIPS64N32: | |
201 | self._token = libseccomp.SCMP_ARCH_MIPS64N32 | |
202 | elif arch == libseccomp.SCMP_ARCH_MIPSEL: | |
203 | self._token = libseccomp.SCMP_ARCH_MIPSEL | |
204 | elif arch == libseccomp.SCMP_ARCH_MIPSEL64: | |
205 | self._token = libseccomp.SCMP_ARCH_MIPSEL64 | |
206 | elif arch == libseccomp.SCMP_ARCH_MIPSEL64N32: | |
207 | self._token = libseccomp.SCMP_ARCH_MIPSEL64N32 | |
208 | elif arch == libseccomp.SCMP_ARCH_PPC: | |
209 | self._token = libseccomp.SCMP_ARCH_PPC | |
210 | elif arch == libseccomp.SCMP_ARCH_PPC64: | |
211 | self._token = libseccomp.SCMP_ARCH_PPC64 | |
212 | elif arch == libseccomp.SCMP_ARCH_PPC64LE: | |
213 | self._token = libseccomp.SCMP_ARCH_PPC64LE | |
214 | elif arch == libseccomp.SCMP_ARCH_S390: | |
215 | self._token = libseccomp.SCMP_ARCH_S390 | |
216 | elif arch == libseccomp.SCMP_ARCH_S390X: | |
217 | self._token = libseccomp.SCMP_ARCH_S390X | |
218 | else: | |
219 | self._token = 0; | |
220 | elif isinstance(arch, basestring): | |
221 | self._token = libseccomp.seccomp_arch_resolve_name(arch) | |
222 | else: | |
223 | raise TypeError("Architecture must be an int or str type") | |
224 | if self._token == 0: | |
225 | raise ValueError("Invalid architecture") | |
226 | ||
227 | def __int__(self): | |
228 | """ Convert the architecture object to a token value. | |
229 | ||
230 | Description: | |
231 | Convert the architecture object to an integer representing the | |
232 | architecture's token value. | |
233 | """ | |
234 | return self._token | |
235 | ||
236 | cdef class Attr: | |
237 | """ Python object representing the SyscallFilter attributes. | |
238 | ||
239 | Data values: | |
240 | ACT_DEFAULT - the filter's default action | |
241 | ACT_BADARCH - the filter's bad architecture action | |
242 | CTL_NNP - the filter's "no new privileges" flag | |
243 | CTL_NNP - the filter's thread sync flag | |
244 | """ | |
245 | ACT_DEFAULT = libseccomp.SCMP_FLTATR_ACT_DEFAULT | |
246 | ACT_BADARCH = libseccomp.SCMP_FLTATR_ACT_BADARCH | |
247 | CTL_NNP = libseccomp.SCMP_FLTATR_CTL_NNP | |
248 | CTL_TSYNC = libseccomp.SCMP_FLTATR_CTL_TSYNC | |
249 | ||
250 | cdef class Arg: | |
251 | """ Python object representing a SyscallFilter syscall argument. | |
252 | """ | |
253 | cdef libseccomp.scmp_arg_cmp _arg | |
254 | ||
255 | def __cinit__(self, arg, op, datum_a, datum_b = 0): | |
256 | """ Initialize the argument comparison. | |
257 | ||
258 | Arguments: | |
259 | arg - the argument number, starting at 0 | |
260 | op - the argument comparison operator, e.g. {NE,LT,LE,...} | |
261 | datum_a - argument value | |
262 | datum_b - argument value, only valid when op == MASKED_EQ | |
263 | ||
264 | Description: | |
265 | Create an argument comparison object for use with SyscallFilter. | |
266 | """ | |
267 | self._arg.arg = arg | |
268 | self._arg.op = op | |
269 | self._arg.datum_a = datum_a | |
270 | self._arg.datum_b = datum_b | |
271 | ||
272 | cdef libseccomp.scmp_arg_cmp to_c(self): | |
273 | """ Convert the object into a C structure. | |
274 | ||
275 | Description: | |
276 | Helper function which should only be used internally by | |
277 | SyscallFilter objects and exists for the sole purpose of making it | |
278 | easier to deal with the varadic functions of the libseccomp API, | |
279 | e.g. seccomp_rule_add(). | |
280 | """ | |
281 | return self._arg | |
282 | ||
283 | cdef class SyscallFilter: | |
284 | """ Python object representing a seccomp syscall filter. """ | |
285 | cdef int _defaction | |
286 | cdef libseccomp.scmp_filter_ctx _ctx | |
287 | ||
288 | def __cinit__(self, int defaction): | |
289 | self._ctx = libseccomp.seccomp_init(defaction) | |
290 | if self._ctx == NULL: | |
291 | raise RuntimeError("Library error") | |
292 | _defaction = defaction | |
293 | ||
294 | def __init__(self, defaction): | |
295 | """ Initialize the filter state | |
296 | ||
297 | Arguments: | |
298 | defaction - the default filter action | |
299 | ||
300 | Description: | |
301 | Initializes the seccomp filter state to the defaults. | |
302 | """ | |
303 | ||
304 | def __dealloc__(self): | |
305 | """ Destroys the filter state and releases any resources. | |
306 | ||
307 | Description: | |
308 | Destroys the seccomp filter state and releases any resources | |
309 | associated with the filter state. This function does not affect | |
310 | any seccomp filters already loaded into the kernel. | |
311 | """ | |
312 | if self._ctx != NULL: | |
313 | libseccomp.seccomp_release(self._ctx) | |
314 | ||
315 | def reset(self, int defaction = -1): | |
316 | """ Reset the filter state. | |
317 | ||
318 | Arguments: | |
319 | defaction - the default filter action | |
320 | ||
321 | Description: | |
322 | Resets the seccomp filter state to an initial default state, if a | |
323 | default filter action is not specified in the reset call the | |
324 | original action will be reused. This function does not affect any | |
325 | seccomp filters alread loaded into the kernel. | |
326 | """ | |
327 | if defaction == -1: | |
328 | defaction = self._defaction | |
329 | rc = libseccomp.seccomp_reset(self._ctx, defaction) | |
330 | if rc == -errno.EINVAL: | |
331 | raise ValueError("Invalid action") | |
332 | if rc != 0: | |
333 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
334 | _defaction = defaction | |
335 | ||
336 | def merge(self, SyscallFilter filter): | |
337 | """ Merge two existing SyscallFilter objects. | |
338 | ||
339 | Arguments: | |
340 | filter - a valid SyscallFilter object | |
341 | ||
342 | Description: | |
343 | Merges a valid SyscallFilter object with the current SyscallFilter | |
344 | object; the passed filter object will be reset on success. In | |
345 | order to successfully merge two seccomp filters they must have the | |
346 | same attribute values and not share any of the same architectures. | |
347 | """ | |
348 | rc = libseccomp.seccomp_merge(self._ctx, filter._ctx) | |
349 | if rc != 0: | |
350 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
351 | filter._ctx = NULL | |
352 | filter = SyscallFilter(filter._defaction) | |
353 | ||
354 | def exist_arch(self, arch): | |
355 | """ Check if the seccomp filter contains a given architecture. | |
356 | ||
357 | Arguments: | |
358 | arch - the architecture value, e.g. Arch.* | |
359 | ||
360 | Description: | |
361 | Test to see if a given architecture is included in the filter. | |
362 | Return True is the architecture exists, False if it does not | |
363 | exist. | |
364 | """ | |
365 | rc = libseccomp.seccomp_arch_exist(self._ctx, arch) | |
366 | if rc == 0: | |
367 | return True | |
368 | elif rc == -errno.EEXIST: | |
369 | return False | |
370 | elif rc == -errno.EINVAL: | |
371 | raise ValueError("Invalid architecture") | |
372 | else: | |
373 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
374 | ||
375 | def add_arch(self, arch): | |
376 | """ Add an architecture to the filter. | |
377 | ||
378 | Arguments: | |
379 | arch - the architecture value, e.g. Arch.* | |
380 | ||
381 | Description: | |
382 | Add the given architecture to the filter. Any new rules added | |
383 | after this method returns successfully will be added to this new | |
384 | architecture, but any existing rules will not be added to the new | |
385 | architecture. | |
386 | """ | |
387 | rc = libseccomp.seccomp_arch_add(self._ctx, arch) | |
388 | if rc == -errno.EINVAL: | |
389 | raise ValueError("Invalid architecture") | |
390 | elif rc != 0: | |
391 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
392 | ||
393 | def remove_arch(self, arch): | |
394 | """ Remove an architecture from the filter. | |
395 | ||
396 | Arguments: | |
397 | arch - the architecture value, e.g. Arch.* | |
398 | ||
399 | Description: | |
400 | Remove the given architecture from the filter. The filter must | |
401 | always contain at least one architecture, so if only one | |
402 | architecture exists in the filter this method will fail. | |
403 | """ | |
404 | rc = libseccomp.seccomp_arch_remove(self._ctx, arch) | |
405 | if rc == -errno.EINVAL: | |
406 | raise ValueError("Invalid architecture") | |
407 | elif rc != 0: | |
408 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
409 | ||
410 | def load(self): | |
411 | """ Load the filter into the Linux Kernel. | |
412 | ||
413 | Description: | |
414 | Load the current filter into the Linux Kernel. As soon as the | |
415 | method returns the filter will be active and enforcing. | |
416 | """ | |
417 | rc = libseccomp.seccomp_load(self._ctx) | |
418 | if rc != 0: | |
419 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
420 | ||
421 | def get_attr(self, attr): | |
422 | """ Get an attribute value from the filter. | |
423 | ||
424 | Arguments: | |
425 | attr - the attribute, e.g. Attr.* | |
426 | ||
427 | Description: | |
428 | Lookup the given attribute in the filter and return the | |
429 | attribute's value to the caller. | |
430 | """ | |
431 | cdef uint32_t value = 0 | |
432 | rc = libseccomp.seccomp_attr_get(self._ctx, | |
433 | attr, <uint32_t *>&value) | |
434 | if rc == -errno.EINVAL: | |
435 | raise ValueError("Invalid attribute") | |
436 | elif rc != 0: | |
437 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
438 | return value | |
439 | ||
440 | def set_attr(self, attr, int value): | |
441 | """ Set a filter attribute. | |
442 | ||
443 | Arguments: | |
444 | attr - the attribute, e.g. Attr.* | |
445 | value - the attribute value | |
446 | ||
447 | Description: | |
448 | Lookup the given attribute in the filter and assign it the given | |
449 | value. | |
450 | """ | |
451 | rc = libseccomp.seccomp_attr_set(self._ctx, attr, value) | |
452 | if rc == -errno.EINVAL: | |
453 | raise ValueError("Invalid attribute") | |
454 | elif rc != 0: | |
455 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
456 | ||
457 | def syscall_priority(self, syscall, int priority): | |
458 | """ Set the filter priority of a syscall. | |
459 | ||
460 | Arguments: | |
461 | syscall - the syscall name or number | |
462 | priority - the priority of the syscall | |
463 | ||
464 | Description: | |
465 | Set the filter priority of the given syscall. A syscall with a | |
466 | higher priority will have less overhead in the generated filter | |
467 | code which is loaded into the system. Priority values can range | |
468 | from 0 to 255 inclusive. | |
469 | """ | |
470 | if priority < 0 or priority > 255: | |
471 | raise ValueError("Syscall priority must be between 0 and 255") | |
472 | if isinstance(syscall, str): | |
473 | syscall_str = syscall.encode() | |
474 | syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str) | |
475 | elif isinstance(syscall, int): | |
476 | syscall_num = syscall | |
477 | else: | |
478 | raise TypeError("Syscall must either be an int or str type") | |
479 | rc = libseccomp.seccomp_syscall_priority(self._ctx, | |
480 | syscall_num, priority) | |
481 | if rc != 0: | |
482 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
483 | ||
484 | def add_rule(self, int action, syscall, *args): | |
485 | """ Add a new rule to filter. | |
486 | ||
487 | Arguments: | |
488 | action - the rule action: KILL, TRAP, ERRNO(), TRACE(), or ALLOW | |
489 | syscall - the syscall name or number | |
490 | args - variable number of Arg objects | |
491 | ||
492 | Description: | |
493 | Add a new rule to the filter, matching on the given syscall and an | |
494 | optional list of argument comparisons. If the rule is triggered | |
495 | the given action will be taken by the kernel. In order for the | |
496 | rule to trigger, the syscall as well as each argument comparison | |
497 | must be true. | |
498 | ||
499 | In the case where the specific rule is not valid on a specific | |
500 | architecture, e.g. socket() on 32-bit x86, this method rewrites | |
501 | the rule to the best possible match. If you don't want this fule | |
502 | rewriting to take place use add_rule_exactly(). | |
503 | """ | |
504 | cdef libseccomp.scmp_arg_cmp c_arg[6] | |
505 | if isinstance(syscall, str): | |
506 | syscall_str = syscall.encode() | |
507 | syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str) | |
508 | elif isinstance(syscall, int): | |
509 | syscall_num = syscall | |
510 | else: | |
511 | raise TypeError("Syscall must either be an int or str type") | |
512 | """ NOTE: the code below exists solely to deal with the varadic | |
513 | nature of seccomp_rule_add() function and the inability of Cython | |
514 | to handle this automatically """ | |
515 | if len(args) > 6: | |
516 | raise RuntimeError("Maximum number of arguments exceeded") | |
517 | cdef Arg arg | |
518 | for i, arg in enumerate(args): | |
519 | c_arg[i] = arg.to_c() | |
520 | if len(args) == 0: | |
521 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, 0) | |
522 | elif len(args) == 1: | |
523 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
524 | len(args), | |
525 | c_arg[0]) | |
526 | elif len(args) == 2: | |
527 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
528 | len(args), | |
529 | c_arg[0], | |
530 | c_arg[1]) | |
531 | elif len(args) == 3: | |
532 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
533 | len(args), | |
534 | c_arg[0], | |
535 | c_arg[1], | |
536 | c_arg[2]) | |
537 | elif len(args) == 4: | |
538 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
539 | len(args), | |
540 | c_arg[0], | |
541 | c_arg[1], | |
542 | c_arg[2], | |
543 | c_arg[3]) | |
544 | elif len(args) == 5: | |
545 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
546 | len(args), | |
547 | c_arg[0], | |
548 | c_arg[1], | |
549 | c_arg[2], | |
550 | c_arg[3], | |
551 | c_arg[4]) | |
552 | elif len(args) == 6: | |
553 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
554 | len(args), | |
555 | c_arg[0], | |
556 | c_arg[1], | |
557 | c_arg[2], | |
558 | c_arg[3], | |
559 | c_arg[4], | |
560 | c_arg[5]) | |
561 | else: | |
562 | raise RuntimeError("Maximum number of arguments exceeded") | |
563 | if rc != 0: | |
564 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
565 | ||
566 | def add_rule_exactly(self, int action, syscall, *args): | |
567 | """ Add a new rule to filter. | |
568 | ||
569 | Arguments: | |
570 | action - the rule action: KILL, TRAP, ERRNO(), TRACE(), or ALLOW | |
571 | syscall - the syscall name or number | |
572 | args - variable number of Arg objects | |
573 | ||
574 | Description: | |
575 | Add a new rule to the filter, matching on the given syscall and an | |
576 | optional list of argument comparisons. If the rule is triggered | |
577 | the given action will be taken by the kernel. In order for the | |
578 | rule to trigger, the syscall as well as each argument comparison | |
579 | must be true. | |
580 | ||
581 | This method attempts to add the filter rule exactly as specified | |
582 | which can cause problems on certain architectures, e.g. socket() | |
583 | on 32-bit x86. For a architecture independent version of this | |
584 | method use add_rule(). | |
585 | """ | |
586 | cdef libseccomp.scmp_arg_cmp c_arg[6] | |
587 | if isinstance(syscall, str): | |
588 | syscall_str = syscall.encode() | |
589 | syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str) | |
590 | elif isinstance(syscall, int): | |
591 | syscall_num = syscall | |
592 | else: | |
593 | raise TypeError("Syscall must either be an int or str type") | |
594 | """ NOTE: the code below exists solely to deal with the varadic | |
595 | nature of seccomp_rule_add_exact() function and the inability of | |
596 | Cython to handle this automatically """ | |
597 | if len(args) > 6: | |
598 | raise RuntimeError("Maximum number of arguments exceeded") | |
599 | cdef Arg arg | |
600 | for i, arg in enumerate(args): | |
601 | c_arg[i] = arg.to_c() | |
602 | if len(args) == 0: | |
603 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
604 | syscall_num, 0) | |
605 | elif len(args) == 1: | |
606 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
607 | syscall_num, len(args), | |
608 | c_arg[0]) | |
609 | elif len(args) == 2: | |
610 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
611 | syscall_num, len(args), | |
612 | c_arg[0], | |
613 | c_arg[1]) | |
614 | elif len(args) == 3: | |
615 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
616 | syscall_num, len(args), | |
617 | c_arg[0], | |
618 | c_arg[1], | |
619 | c_arg[2]) | |
620 | elif len(args) == 4: | |
621 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
622 | syscall_num, len(args), | |
623 | c_arg[0], | |
624 | c_arg[1], | |
625 | c_arg[2], | |
626 | c_arg[3]) | |
627 | elif len(args) == 5: | |
628 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
629 | syscall_num, len(args), | |
630 | c_arg[0], | |
631 | c_arg[1], | |
632 | c_arg[2], | |
633 | c_arg[3], | |
634 | c_arg[4]) | |
635 | elif len(args) == 6: | |
636 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
637 | syscall_num, len(args), | |
638 | c_arg[0], | |
639 | c_arg[1], | |
640 | c_arg[2], | |
641 | c_arg[3], | |
642 | c_arg[4], | |
643 | c_arg[5]) | |
644 | else: | |
645 | raise RuntimeError("Maximum number of arguments exceeded") | |
646 | if rc != 0: | |
647 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
648 | ||
649 | def export_pfc(self, file): | |
650 | """ Export the filter in PFC format. | |
651 | ||
652 | Arguments: | |
653 | file - the output file | |
654 | ||
655 | Description: | |
656 | Output the filter in Pseudo Filter Code (PFC) to the given file. | |
657 | The output is functionally equivalent to the BPF based filter | |
658 | which is loaded into the Linux Kernel. | |
659 | """ | |
660 | rc = libseccomp.seccomp_export_pfc(self._ctx, file.fileno()) | |
661 | if rc != 0: | |
662 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
663 | ||
664 | def export_bpf(self, file): | |
665 | """ Export the filter in BPF format. | |
666 | ||
667 | Arguments: | |
668 | file - the output file | |
669 | ||
670 | Output the filter in Berkley Packet Filter (BPF) to the given | |
671 | file. The output is identical to what is loaded into the | |
672 | Linux Kernel. | |
673 | """ | |
674 | rc = libseccomp.seccomp_export_bpf(self._ctx, file.fileno()) | |
675 | if rc != 0: | |
676 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
677 | ||
678 | # kate: syntax python; | |
679 | # kate: indent-mode python; space-indent on; indent-width 4; mixedindent off; |