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