]>
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 | ||
d390edad PM |
22 | # cython: language_level = 3str |
23 | ||
62c6598e PM |
24 | """ Python bindings for the libseccomp library |
25 | ||
26 | The libseccomp library provides and easy to use, platform independent, | |
27 | interface to the Linux Kernel's syscall filtering mechanism: seccomp. The | |
28 | libseccomp API is designed to abstract away the underlying BPF based | |
29 | syscall filter language and present a more conventional function-call | |
30 | based filtering interface that should be familiar to, and easily adopted | |
31 | by application developers. | |
32 | ||
33 | Filter action values: | |
b2f15f3d TH |
34 | KILL_PROCESS - kill the process |
35 | KILL - kill the thread | |
3b22b153 | 36 | LOG - allow the syscall to be executed after the action has been logged |
62c6598e PM |
37 | ALLOW - allow the syscall to execute |
38 | TRAP - a SIGSYS signal will be thrown | |
e15f4157 | 39 | NOTIFY - a notification event will be sent via the notification API |
62c6598e PM |
40 | ERRNO(x) - syscall will return (x) |
41 | TRACE(x) - if the process is being traced, (x) will be returned to the | |
42 | tracing process via PTRACE_EVENT_SECCOMP and the | |
43 | PTRACE_GETEVENTMSG option | |
44 | ||
70c89434 PM |
45 | Argument comparison values (see the Arg class): |
46 | ||
47 | NE - arg != datum_a | |
48 | LT - arg < datum_a | |
49 | LE - arg <= datum_a | |
50 | EQ - arg == datum_a | |
51 | GT - arg > datum_a | |
52 | GE - arg >= datum_a | |
0f589d15 | 53 | MASKED_EQ - (arg & datum_a) == datum_b |
62c6598e PM |
54 | |
55 | ||
56 | Example: | |
57 | ||
58 | import sys | |
59 | from seccomp import * | |
60 | ||
61 | # create a filter object with a default KILL action | |
62 | f = SyscallFilter(defaction=KILL) | |
63 | ||
853a24c8 JP |
64 | # add some basic syscalls which python typically wants |
65 | f.add_rule(ALLOW, "rt_sigaction") | |
66 | f.add_rule(ALLOW, "rt_sigreturn") | |
67 | f.add_rule(ALLOW, "exit_group") | |
68 | f.add_rule(ALLOW, "brk") | |
69 | ||
62c6598e PM |
70 | # add syscall filter rules to allow certain syscalls |
71 | f.add_rule(ALLOW, "open") | |
72 | f.add_rule(ALLOW, "close") | |
853a24c8 JP |
73 | f.add_rule(ALLOW, "read", Arg(0, EQ, sys.stdin.fileno())) |
74 | f.add_rule(ALLOW, "write", Arg(0, EQ, sys.stdout.fileno())) | |
75 | f.add_rule(ALLOW, "write", Arg(0, EQ, sys.stderr.fileno())) | |
62c6598e PM |
76 | |
77 | # load the filter into the kernel | |
78 | f.load() | |
79 | """ | |
80 | __author__ = 'Paul Moore <paul@paul-moore.com>' | |
ce5aea6a | 81 | __date__ = "3 February 2017" |
62c6598e | 82 | |
ce5aea6a | 83 | from cpython.version cimport PY_MAJOR_VERSION |
e15f4157 PM |
84 | from libc.stdint cimport int8_t, int16_t, int32_t, int64_t |
85 | from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t | |
86 | from libc.stdlib cimport free | |
62c6598e PM |
87 | import errno |
88 | ||
89 | cimport libseccomp | |
90 | ||
ce5aea6a PM |
91 | def c_str(string): |
92 | """ Convert a Python string to a C string. | |
93 | ||
94 | Arguments: | |
95 | string - the Python string | |
96 | ||
97 | Description: | |
98 | Convert the Python string into a form usable by C taking into consideration | |
99 | the Python major version, e.g. Python 2.x or Python 3.x. | |
100 | See http://docs.cython.org/en/latest/src/tutorial/strings.html for more | |
101 | information. | |
102 | """ | |
103 | if PY_MAJOR_VERSION < 3: | |
104 | return string | |
105 | else: | |
106 | return bytes(string, "ascii") | |
107 | ||
b2f15f3d | 108 | KILL_PROCESS = libseccomp.SCMP_ACT_KILL_PROCESS |
62c6598e PM |
109 | KILL = libseccomp.SCMP_ACT_KILL |
110 | TRAP = libseccomp.SCMP_ACT_TRAP | |
3b22b153 | 111 | LOG = libseccomp.SCMP_ACT_LOG |
62c6598e | 112 | ALLOW = libseccomp.SCMP_ACT_ALLOW |
e15f4157 | 113 | NOTIFY = libseccomp.SCMP_ACT_NOTIFY |
62c6598e | 114 | def ERRNO(int errno): |
28a57d88 AL |
115 | """The action ERRNO(x) means that the syscall will return (x). |
116 | To conform to Linux syscall calling conventions, the syscall return | |
117 | value should almost always be a negative number. | |
118 | """ | |
62c6598e PM |
119 | return libseccomp.SCMP_ACT_ERRNO(errno) |
120 | def TRACE(int value): | |
28a57d88 AL |
121 | """The action TRACE(x) means that, if the process is being traced, (x) |
122 | will be returned to the tracing process via PTRACE_EVENT_SECCOMP | |
123 | and the PTRACE_GETEVENTMSG option. | |
124 | """ | |
62c6598e PM |
125 | return libseccomp.SCMP_ACT_TRACE(value) |
126 | ||
127 | NE = libseccomp.SCMP_CMP_NE | |
128 | LT = libseccomp.SCMP_CMP_LT | |
129 | LE = libseccomp.SCMP_CMP_LE | |
130 | EQ = libseccomp.SCMP_CMP_EQ | |
131 | GE = libseccomp.SCMP_CMP_GE | |
132 | GT = libseccomp.SCMP_CMP_GT | |
133 | MASKED_EQ = libseccomp.SCMP_CMP_MASKED_EQ | |
134 | ||
aeac2736 PM |
135 | def system_arch(): |
136 | """ Return the system architecture value. | |
137 | ||
138 | Description: | |
139 | Returns the native system architecture value. | |
140 | """ | |
141 | return libseccomp.seccomp_arch_native() | |
142 | ||
6220c8c0 PM |
143 | def resolve_syscall(arch, syscall): |
144 | """ Resolve the syscall. | |
145 | ||
146 | Arguments: | |
147 | arch - the architecture value, e.g. Arch.* | |
148 | syscall - the syscall name or number | |
149 | ||
150 | Description: | |
151 | Resolve an architecture's syscall name to the correct number or the | |
152 | syscall number to the correct name. | |
153 | """ | |
f7c11d67 AL |
154 | cdef char *ret_str |
155 | ||
f05fc7cb | 156 | if isinstance(syscall, basestring): |
ce5aea6a PM |
157 | return libseccomp.seccomp_syscall_resolve_name_rewrite(arch, |
158 | c_str(syscall)) | |
f05fc7cb | 159 | elif isinstance(syscall, int): |
f7c11d67 AL |
160 | ret_str = libseccomp.seccomp_syscall_resolve_num_arch(arch, syscall) |
161 | if ret_str is NULL: | |
162 | raise ValueError('Unknown syscall %d on arch %d' % (syscall, arch)) | |
163 | else: | |
164 | return ret_str | |
6220c8c0 PM |
165 | else: |
166 | raise TypeError("Syscall must either be an int or str type") | |
167 | ||
4f16fe20 TH |
168 | def get_api(): |
169 | """ Query the level of API support | |
170 | ||
171 | Description: | |
172 | Returns the API level value indicating the current supported | |
173 | functionality. | |
174 | """ | |
175 | level = libseccomp.seccomp_api_get() | |
176 | if level < 0: | |
177 | raise RuntimeError(str.format("Library error (errno = {0})", level)) | |
178 | ||
179 | return level | |
180 | ||
181 | def set_api(unsigned int level): | |
182 | """ Set the level of API support | |
183 | ||
184 | Arguments: | |
185 | level - the API level | |
186 | ||
187 | Description: | |
188 | This function forcibly sets the API level at runtime. General use | |
189 | of this function is strongly discouraged. | |
190 | """ | |
191 | rc = libseccomp.seccomp_api_set(level) | |
192 | if rc == -errno.EINVAL: | |
193 | raise ValueError("Invalid level") | |
194 | elif rc != 0: | |
195 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
196 | ||
62c6598e PM |
197 | cdef class Arch: |
198 | """ Python object representing the SyscallFilter architecture values. | |
199 | ||
200 | Data values: | |
201 | NATIVE - the native architecture | |
202 | X86 - 32-bit x86 | |
203 | X86_64 - 64-bit x86 | |
e9b5a6eb | 204 | X32 - 64-bit x86 using the x32 ABI |
db440d1e | 205 | ARM - ARM |
ab63dc7f | 206 | AARCH64 - 64-bit ARM |
5a703f60 PM |
207 | MIPS - MIPS O32 ABI |
208 | MIPS64 - MIPS 64-bit ABI | |
209 | MIPS64N32 - MIPS N32 ABI | |
210 | MIPSEL - MIPS little endian O32 ABI | |
211 | MIPSEL64 - MIPS little endian 64-bit ABI | |
212 | MIPSEL64N32 - MIPS little endian N32 ABI | |
c86e1f56 HD |
213 | PARISC - 32-bit PA-RISC |
214 | PARISC64 - 64-bit PA-RISC | |
daed219e | 215 | PPC64 - 64-bit PowerPC |
a2ad734f | 216 | PPC - 32-bit PowerPC |
62c6598e | 217 | """ |
aeac2736 | 218 | |
f05fc7cb PM |
219 | cdef int _token |
220 | ||
62c6598e PM |
221 | NATIVE = libseccomp.SCMP_ARCH_NATIVE |
222 | X86 = libseccomp.SCMP_ARCH_X86 | |
223 | X86_64 = libseccomp.SCMP_ARCH_X86_64 | |
e9b5a6eb | 224 | X32 = libseccomp.SCMP_ARCH_X32 |
db440d1e | 225 | ARM = libseccomp.SCMP_ARCH_ARM |
ab63dc7f | 226 | AARCH64 = libseccomp.SCMP_ARCH_AARCH64 |
2b9c637c | 227 | MIPS = libseccomp.SCMP_ARCH_MIPS |
5a703f60 PM |
228 | MIPS64 = libseccomp.SCMP_ARCH_MIPS64 |
229 | MIPS64N32 = libseccomp.SCMP_ARCH_MIPS64N32 | |
2b9c637c | 230 | MIPSEL = libseccomp.SCMP_ARCH_MIPSEL |
5a703f60 PM |
231 | MIPSEL64 = libseccomp.SCMP_ARCH_MIPSEL64 |
232 | MIPSEL64N32 = libseccomp.SCMP_ARCH_MIPSEL64N32 | |
c86e1f56 HD |
233 | PARISC = libseccomp.SCMP_ARCH_PARISC |
234 | PARISC64 = libseccomp.SCMP_ARCH_PARISC64 | |
fc886cbe | 235 | PPC = libseccomp.SCMP_ARCH_PPC |
daed219e | 236 | PPC64 = libseccomp.SCMP_ARCH_PPC64 |
a2ad734f | 237 | PPC64LE = libseccomp.SCMP_ARCH_PPC64LE |
c5d3e1d6 PM |
238 | S390 = libseccomp.SCMP_ARCH_S390 |
239 | S390X = libseccomp.SCMP_ARCH_S390X | |
62c6598e | 240 | |
f05fc7cb PM |
241 | def __cinit__(self, arch=libseccomp.SCMP_ARCH_NATIVE): |
242 | """ Initialize the architecture object. | |
243 | ||
244 | Arguments: | |
245 | arch - the architecture name or token value | |
246 | ||
247 | Description: | |
248 | Create an architecture object using the given name or token value. | |
249 | """ | |
250 | if isinstance(arch, int): | |
251 | if arch == libseccomp.SCMP_ARCH_NATIVE: | |
252 | self._token = libseccomp.seccomp_arch_native() | |
253 | elif arch == libseccomp.SCMP_ARCH_X86: | |
254 | self._token = libseccomp.SCMP_ARCH_X86 | |
255 | elif arch == libseccomp.SCMP_ARCH_X86_64: | |
256 | self._token = libseccomp.SCMP_ARCH_X86_64 | |
257 | elif arch == libseccomp.SCMP_ARCH_X32: | |
258 | self._token = libseccomp.SCMP_ARCH_X32 | |
259 | elif arch == libseccomp.SCMP_ARCH_ARM: | |
260 | self._token = libseccomp.SCMP_ARCH_ARM | |
ab63dc7f MJ |
261 | elif arch == libseccomp.SCMP_ARCH_AARCH64: |
262 | self._token = libseccomp.SCMP_ARCH_AARCH64 | |
f05fc7cb PM |
263 | elif arch == libseccomp.SCMP_ARCH_MIPS: |
264 | self._token = libseccomp.SCMP_ARCH_MIPS | |
5a703f60 PM |
265 | elif arch == libseccomp.SCMP_ARCH_MIPS64: |
266 | self._token = libseccomp.SCMP_ARCH_MIPS64 | |
267 | elif arch == libseccomp.SCMP_ARCH_MIPS64N32: | |
268 | self._token = libseccomp.SCMP_ARCH_MIPS64N32 | |
f05fc7cb PM |
269 | elif arch == libseccomp.SCMP_ARCH_MIPSEL: |
270 | self._token = libseccomp.SCMP_ARCH_MIPSEL | |
5a703f60 PM |
271 | elif arch == libseccomp.SCMP_ARCH_MIPSEL64: |
272 | self._token = libseccomp.SCMP_ARCH_MIPSEL64 | |
273 | elif arch == libseccomp.SCMP_ARCH_MIPSEL64N32: | |
274 | self._token = libseccomp.SCMP_ARCH_MIPSEL64N32 | |
c86e1f56 HD |
275 | elif arch == libseccomp.SCMP_ARCH_PARISC: |
276 | self._token = libseccomp.SCMP_ARCH_PARISC | |
277 | elif arch == libseccomp.SCMP_ARCH_PARISC64: | |
278 | self._token = libseccomp.SCMP_ARCH_PARISC64 | |
fc886cbe PM |
279 | elif arch == libseccomp.SCMP_ARCH_PPC: |
280 | self._token = libseccomp.SCMP_ARCH_PPC | |
daed219e PM |
281 | elif arch == libseccomp.SCMP_ARCH_PPC64: |
282 | self._token = libseccomp.SCMP_ARCH_PPC64 | |
283 | elif arch == libseccomp.SCMP_ARCH_PPC64LE: | |
284 | self._token = libseccomp.SCMP_ARCH_PPC64LE | |
c5d3e1d6 PM |
285 | elif arch == libseccomp.SCMP_ARCH_S390: |
286 | self._token = libseccomp.SCMP_ARCH_S390 | |
287 | elif arch == libseccomp.SCMP_ARCH_S390X: | |
288 | self._token = libseccomp.SCMP_ARCH_S390X | |
f05fc7cb PM |
289 | else: |
290 | self._token = 0; | |
291 | elif isinstance(arch, basestring): | |
ce5aea6a | 292 | self._token = libseccomp.seccomp_arch_resolve_name(c_str(arch)) |
f05fc7cb PM |
293 | else: |
294 | raise TypeError("Architecture must be an int or str type") | |
295 | if self._token == 0: | |
296 | raise ValueError("Invalid architecture") | |
297 | ||
298 | def __int__(self): | |
299 | """ Convert the architecture object to a token value. | |
300 | ||
301 | Description: | |
302 | Convert the architecture object to an integer representing the | |
303 | architecture's token value. | |
304 | """ | |
305 | return self._token | |
306 | ||
62c6598e PM |
307 | cdef class Attr: |
308 | """ Python object representing the SyscallFilter attributes. | |
309 | ||
310 | Data values: | |
311 | ACT_DEFAULT - the filter's default action | |
312 | ACT_BADARCH - the filter's bad architecture action | |
313 | CTL_NNP - the filter's "no new privileges" flag | |
4d29f586 | 314 | CTL_NNP - the filter's thread sync flag |
e15f4157 PM |
315 | CTL_TSYNC - sync threads on filter load |
316 | CTL_TSKIP - allow rules with a -1 syscall number | |
317 | CTL_LOG - log not-allowed actions | |
318 | CTL_SSB - disable SSB mitigations | |
62c6598e PM |
319 | """ |
320 | ACT_DEFAULT = libseccomp.SCMP_FLTATR_ACT_DEFAULT | |
321 | ACT_BADARCH = libseccomp.SCMP_FLTATR_ACT_BADARCH | |
322 | CTL_NNP = libseccomp.SCMP_FLTATR_CTL_NNP | |
4d29f586 | 323 | CTL_TSYNC = libseccomp.SCMP_FLTATR_CTL_TSYNC |
dc879990 | 324 | API_TSKIP = libseccomp.SCMP_FLTATR_API_TSKIP |
d0e11951 | 325 | CTL_LOG = libseccomp.SCMP_FLTATR_CTL_LOG |
a15a87c3 | 326 | CTL_SSB = libseccomp.SCMP_FLTATR_CTL_SSB |
62c6598e PM |
327 | |
328 | cdef class Arg: | |
329 | """ Python object representing a SyscallFilter syscall argument. | |
330 | """ | |
331 | cdef libseccomp.scmp_arg_cmp _arg | |
332 | ||
333 | def __cinit__(self, arg, op, datum_a, datum_b = 0): | |
334 | """ Initialize the argument comparison. | |
335 | ||
336 | Arguments: | |
46a0ab2f | 337 | arg - the argument number, starting at 0 |
62c6598e PM |
338 | op - the argument comparison operator, e.g. {NE,LT,LE,...} |
339 | datum_a - argument value | |
340 | datum_b - argument value, only valid when op == MASKED_EQ | |
341 | ||
342 | Description: | |
343 | Create an argument comparison object for use with SyscallFilter. | |
344 | """ | |
345 | self._arg.arg = arg | |
346 | self._arg.op = op | |
8e1b4634 AL |
347 | self._arg.datum_a = datum_a |
348 | self._arg.datum_b = datum_b | |
62c6598e | 349 | |
dd673b51 | 350 | cdef libseccomp.scmp_arg_cmp to_c(self): |
62c6598e PM |
351 | """ Convert the object into a C structure. |
352 | ||
353 | Description: | |
354 | Helper function which should only be used internally by | |
355 | SyscallFilter objects and exists for the sole purpose of making it | |
356 | easier to deal with the varadic functions of the libseccomp API, | |
357 | e.g. seccomp_rule_add(). | |
358 | """ | |
359 | return self._arg | |
360 | ||
e15f4157 PM |
361 | cdef class Notification: |
362 | """ Python object representing a seccomp notification. | |
363 | """ | |
364 | cdef uint64_t _id | |
365 | cdef uint32_t _pid | |
366 | cdef uint32_t _flags | |
367 | cdef int _syscall | |
368 | cdef uint32_t _syscall_arch | |
369 | cdef uint64_t _syscall_ip | |
370 | cdef uint64_t _syscall_args[6] | |
371 | ||
372 | def __cinit__(self, id, pid, flags, syscall, arch, ip, args): | |
373 | """ Initialize the notification. | |
374 | ||
375 | Arguments: | |
376 | id - the notification ID | |
377 | pid - the process ID | |
378 | flags - the notification flags | |
379 | syscall - the syscall number | |
380 | ip - the instruction pointer | |
381 | args - list of the six syscall arguments | |
382 | ||
383 | Description: | |
384 | Create a seccomp Notification object. | |
385 | """ | |
386 | self._id = id | |
387 | self._pid = pid | |
388 | self._flags = flags | |
389 | self._syscall = syscall | |
390 | self._syscall_arch = arch | |
391 | self._syscall_ip = ip | |
392 | self._syscall_args[0] = args[0] | |
393 | self._syscall_args[1] = args[1] | |
394 | self._syscall_args[2] = args[2] | |
395 | self._syscall_args[3] = args[3] | |
396 | self._syscall_args[4] = args[4] | |
397 | self._syscall_args[5] = args[5] | |
398 | ||
399 | @property | |
400 | def id(self): | |
401 | """ Get the seccomp notification ID. | |
402 | ||
403 | Description: | |
404 | Get the seccomp notification ID. | |
405 | """ | |
406 | return self._id | |
407 | ||
408 | @property | |
409 | def pid(self): | |
410 | """ Get the seccomp notification process ID. | |
411 | ||
412 | Description: | |
413 | Get the seccomp notification process ID. | |
414 | """ | |
415 | return self._pid | |
416 | ||
417 | @property | |
418 | def flags(self): | |
419 | """ Get the seccomp notification flags. | |
420 | ||
421 | Description: | |
422 | Get the seccomp notification flags. | |
423 | """ | |
424 | return self._flags | |
425 | ||
426 | @property | |
427 | def syscall(self): | |
428 | """ Get the seccomp notification syscall. | |
429 | ||
430 | Description: | |
431 | Get the seccomp notification syscall. | |
432 | """ | |
433 | return self._syscall | |
434 | ||
435 | @property | |
436 | def syscall_arch(self): | |
437 | """ Get the seccomp notification syscall architecture. | |
438 | ||
439 | Description: | |
440 | Get the seccomp notification syscall architecture. | |
441 | """ | |
442 | return self._syscall_arch | |
443 | ||
444 | @property | |
445 | def syscall_ip(self): | |
446 | """ Get the seccomp notification syscall instruction pointer. | |
447 | ||
448 | Description: | |
449 | Get the seccomp notification syscall instruction pointer. | |
450 | """ | |
451 | return self._syscall_ip | |
452 | ||
453 | @property | |
454 | def syscall_args(self): | |
455 | """ Get the seccomp notification syscall arguments. | |
456 | ||
457 | Description: | |
458 | Get the seccomp notification syscall arguments in a six element list. | |
459 | """ | |
460 | return [self._syscall_args[0], self._syscall_args[1], | |
461 | self._syscall_args[2], self._syscall_args[3], | |
462 | self._syscall_args[4], self._syscall_args[5]] | |
463 | ||
464 | cdef class NotificationResponse: | |
465 | """ Python object representing a seccomp notification response. | |
466 | """ | |
467 | cdef uint64_t _id | |
468 | cdef int64_t _val | |
469 | cdef int32_t _error | |
470 | cdef uint32_t _flags | |
471 | ||
472 | def __cinit__(self, notify, val = 0, error = 0, flags = 0): | |
473 | """ Initialize the notification response. | |
474 | ||
475 | Arguments: | |
476 | notify - a Notification object | |
477 | val - the notification response value | |
478 | error - the notification response error | |
479 | flags - the notification response flags | |
480 | ||
481 | Description: | |
482 | Create a seccomp NotificationResponse object. | |
483 | """ | |
484 | self._id = notify.id | |
485 | self._val = val | |
486 | self._error = error | |
487 | self._flags = flags | |
488 | ||
489 | @property | |
490 | def id(self): | |
491 | """ Get the seccomp notification response ID. | |
492 | ||
493 | Description: | |
494 | Get the seccomp notification response ID. | |
495 | """ | |
496 | return self._id | |
497 | ||
498 | @id.setter | |
499 | def id(self, value): | |
500 | """ Set the seccomp notification response ID. | |
501 | ||
502 | Arguments: | |
503 | id - the notification response ID | |
504 | ||
505 | Description: | |
506 | Set the seccomp notification response ID. | |
507 | """ | |
508 | self._id = value | |
509 | ||
510 | @property | |
511 | def val(self): | |
512 | """ Get the seccomp notification response value. | |
513 | ||
514 | Description: | |
515 | Get the seccomp notification response value. | |
516 | """ | |
517 | return self._val | |
518 | ||
519 | @val.setter | |
520 | def val(self, value): | |
521 | """ Set the seccomp notification response value. | |
522 | ||
523 | Arguments: | |
524 | val - the notification response value | |
525 | ||
526 | Description: | |
527 | Set the seccomp notification response value. | |
528 | """ | |
529 | self._val = value | |
530 | ||
531 | @property | |
532 | def error(self): | |
533 | """ Get the seccomp notification response error. | |
534 | ||
535 | Description: | |
536 | Get the seccomp notification response error. | |
537 | """ | |
538 | return self._error | |
539 | ||
540 | @error.setter | |
541 | def error(self, value): | |
542 | """ Set the seccomp notification response error. | |
543 | ||
544 | Arguments: | |
545 | error - the notification response error | |
546 | ||
547 | Description: | |
548 | Set the seccomp notification response error. | |
549 | """ | |
550 | self._error = value | |
551 | ||
552 | @property | |
553 | def flags(self): | |
554 | """ Get the seccomp notification response flags. | |
555 | ||
556 | Description: | |
557 | Get the seccomp notification response flags. | |
558 | """ | |
559 | return self._flags | |
560 | ||
561 | @flags.setter | |
562 | def flags(self, value): | |
563 | """ Set the seccomp notification response flags. | |
564 | ||
565 | Arguments: | |
566 | flags - the notification response flags | |
567 | ||
568 | Description: | |
569 | Set the seccomp notification response flags. | |
570 | """ | |
571 | self._flags = value | |
572 | ||
62c6598e PM |
573 | cdef class SyscallFilter: |
574 | """ Python object representing a seccomp syscall filter. """ | |
575 | cdef int _defaction | |
576 | cdef libseccomp.scmp_filter_ctx _ctx | |
577 | ||
578 | def __cinit__(self, int defaction): | |
4021195b AL |
579 | self._ctx = libseccomp.seccomp_init(defaction) |
580 | if self._ctx == NULL: | |
581 | raise RuntimeError("Library error") | |
582 | _defaction = defaction | |
583 | ||
584 | def __init__(self, defaction): | |
62c6598e PM |
585 | """ Initialize the filter state |
586 | ||
587 | Arguments: | |
588 | defaction - the default filter action | |
589 | ||
590 | Description: | |
591 | Initializes the seccomp filter state to the defaults. | |
592 | """ | |
62c6598e PM |
593 | |
594 | def __dealloc__(self): | |
595 | """ Destroys the filter state and releases any resources. | |
596 | ||
597 | Description: | |
598 | Destroys the seccomp filter state and releases any resources | |
599 | associated with the filter state. This function does not affect | |
600 | any seccomp filters already loaded into the kernel. | |
601 | """ | |
602 | if self._ctx != NULL: | |
603 | libseccomp.seccomp_release(self._ctx) | |
604 | ||
605 | def reset(self, int defaction = -1): | |
606 | """ Reset the filter state. | |
607 | ||
608 | Arguments: | |
609 | defaction - the default filter action | |
610 | ||
611 | Description: | |
612 | Resets the seccomp filter state to an initial default state, if a | |
613 | default filter action is not specified in the reset call the | |
614 | original action will be reused. This function does not affect any | |
615 | seccomp filters alread loaded into the kernel. | |
616 | """ | |
617 | if defaction == -1: | |
618 | defaction = self._defaction | |
619 | rc = libseccomp.seccomp_reset(self._ctx, defaction) | |
620 | if rc == -errno.EINVAL: | |
621 | raise ValueError("Invalid action") | |
622 | if rc != 0: | |
623 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
624 | _defaction = defaction | |
625 | ||
626 | def merge(self, SyscallFilter filter): | |
627 | """ Merge two existing SyscallFilter objects. | |
628 | ||
629 | Arguments: | |
630 | filter - a valid SyscallFilter object | |
631 | ||
632 | Description: | |
633 | Merges a valid SyscallFilter object with the current SyscallFilter | |
634 | object; the passed filter object will be reset on success. In | |
635 | order to successfully merge two seccomp filters they must have the | |
636 | same attribute values and not share any of the same architectures. | |
637 | """ | |
638 | rc = libseccomp.seccomp_merge(self._ctx, filter._ctx) | |
639 | if rc != 0: | |
640 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
641 | filter._ctx = NULL | |
642 | filter = SyscallFilter(filter._defaction) | |
643 | ||
644 | def exist_arch(self, arch): | |
645 | """ Check if the seccomp filter contains a given architecture. | |
646 | ||
647 | Arguments: | |
648 | arch - the architecture value, e.g. Arch.* | |
649 | ||
650 | Description: | |
651 | Test to see if a given architecture is included in the filter. | |
652 | Return True is the architecture exists, False if it does not | |
653 | exist. | |
654 | """ | |
655 | rc = libseccomp.seccomp_arch_exist(self._ctx, arch) | |
656 | if rc == 0: | |
657 | return True | |
658 | elif rc == -errno.EEXIST: | |
659 | return False | |
660 | elif rc == -errno.EINVAL: | |
661 | raise ValueError("Invalid architecture") | |
662 | else: | |
663 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
664 | ||
665 | def add_arch(self, arch): | |
666 | """ Add an architecture to the filter. | |
667 | ||
668 | Arguments: | |
669 | arch - the architecture value, e.g. Arch.* | |
670 | ||
671 | Description: | |
672 | Add the given architecture to the filter. Any new rules added | |
673 | after this method returns successfully will be added to this new | |
674 | architecture, but any existing rules will not be added to the new | |
675 | architecture. | |
676 | """ | |
677 | rc = libseccomp.seccomp_arch_add(self._ctx, arch) | |
678 | if rc == -errno.EINVAL: | |
679 | raise ValueError("Invalid architecture") | |
680 | elif rc != 0: | |
681 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
682 | ||
683 | def remove_arch(self, arch): | |
684 | """ Remove an architecture from the filter. | |
685 | ||
686 | Arguments: | |
687 | arch - the architecture value, e.g. Arch.* | |
688 | ||
689 | Description: | |
690 | Remove the given architecture from the filter. The filter must | |
691 | always contain at least one architecture, so if only one | |
692 | architecture exists in the filter this method will fail. | |
693 | """ | |
694 | rc = libseccomp.seccomp_arch_remove(self._ctx, arch) | |
695 | if rc == -errno.EINVAL: | |
696 | raise ValueError("Invalid architecture") | |
697 | elif rc != 0: | |
698 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
699 | ||
700 | def load(self): | |
701 | """ Load the filter into the Linux Kernel. | |
702 | ||
703 | Description: | |
704 | Load the current filter into the Linux Kernel. As soon as the | |
705 | method returns the filter will be active and enforcing. | |
706 | """ | |
707 | rc = libseccomp.seccomp_load(self._ctx) | |
708 | if rc != 0: | |
709 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
710 | ||
711 | def get_attr(self, attr): | |
712 | """ Get an attribute value from the filter. | |
713 | ||
714 | Arguments: | |
715 | attr - the attribute, e.g. Attr.* | |
716 | ||
717 | Description: | |
718 | Lookup the given attribute in the filter and return the | |
719 | attribute's value to the caller. | |
720 | """ | |
4367b1b4 | 721 | cdef uint32_t value = 0 |
62c6598e PM |
722 | rc = libseccomp.seccomp_attr_get(self._ctx, |
723 | attr, <uint32_t *>&value) | |
724 | if rc == -errno.EINVAL: | |
725 | raise ValueError("Invalid attribute") | |
726 | elif rc != 0: | |
727 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
728 | return value | |
729 | ||
730 | def set_attr(self, attr, int value): | |
731 | """ Set a filter attribute. | |
732 | ||
733 | Arguments: | |
734 | attr - the attribute, e.g. Attr.* | |
735 | value - the attribute value | |
736 | ||
737 | Description: | |
738 | Lookup the given attribute in the filter and assign it the given | |
739 | value. | |
740 | """ | |
741 | rc = libseccomp.seccomp_attr_set(self._ctx, attr, value) | |
742 | if rc == -errno.EINVAL: | |
743 | raise ValueError("Invalid attribute") | |
744 | elif rc != 0: | |
745 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
746 | ||
747 | def syscall_priority(self, syscall, int priority): | |
748 | """ Set the filter priority of a syscall. | |
749 | ||
750 | Arguments: | |
751 | syscall - the syscall name or number | |
752 | priority - the priority of the syscall | |
753 | ||
754 | Description: | |
755 | Set the filter priority of the given syscall. A syscall with a | |
756 | higher priority will have less overhead in the generated filter | |
757 | code which is loaded into the system. Priority values can range | |
758 | from 0 to 255 inclusive. | |
759 | """ | |
760 | if priority < 0 or priority > 255: | |
6220c8c0 | 761 | raise ValueError("Syscall priority must be between 0 and 255") |
62c6598e PM |
762 | if isinstance(syscall, str): |
763 | syscall_str = syscall.encode() | |
764 | syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str) | |
765 | elif isinstance(syscall, int): | |
766 | syscall_num = syscall | |
767 | else: | |
6220c8c0 | 768 | raise TypeError("Syscall must either be an int or str type") |
62c6598e PM |
769 | rc = libseccomp.seccomp_syscall_priority(self._ctx, |
770 | syscall_num, priority) | |
771 | if rc != 0: | |
772 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
773 | ||
774 | def add_rule(self, int action, syscall, *args): | |
775 | """ Add a new rule to filter. | |
776 | ||
777 | Arguments: | |
b2f15f3d TH |
778 | action - the rule action: KILL_PROCESS, KILL, TRAP, ERRNO(), TRACE(), |
779 | LOG, or ALLOW | |
62c6598e PM |
780 | syscall - the syscall name or number |
781 | args - variable number of Arg objects | |
782 | ||
783 | Description: | |
784 | Add a new rule to the filter, matching on the given syscall and an | |
785 | optional list of argument comparisons. If the rule is triggered | |
786 | the given action will be taken by the kernel. In order for the | |
787 | rule to trigger, the syscall as well as each argument comparison | |
788 | must be true. | |
789 | ||
790 | In the case where the specific rule is not valid on a specific | |
791 | architecture, e.g. socket() on 32-bit x86, this method rewrites | |
792 | the rule to the best possible match. If you don't want this fule | |
793 | rewriting to take place use add_rule_exactly(). | |
794 | """ | |
795 | cdef libseccomp.scmp_arg_cmp c_arg[6] | |
796 | if isinstance(syscall, str): | |
797 | syscall_str = syscall.encode() | |
798 | syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str) | |
799 | elif isinstance(syscall, int): | |
800 | syscall_num = syscall | |
801 | else: | |
6220c8c0 | 802 | raise TypeError("Syscall must either be an int or str type") |
62c6598e PM |
803 | """ NOTE: the code below exists solely to deal with the varadic |
804 | nature of seccomp_rule_add() function and the inability of Cython | |
805 | to handle this automatically """ | |
2f65c7f4 PM |
806 | if len(args) > 6: |
807 | raise RuntimeError("Maximum number of arguments exceeded") | |
dd673b51 | 808 | cdef Arg arg |
62c6598e PM |
809 | for i, arg in enumerate(args): |
810 | c_arg[i] = arg.to_c() | |
811 | if len(args) == 0: | |
812 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, 0) | |
813 | elif len(args) == 1: | |
814 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
815 | len(args), | |
816 | c_arg[0]) | |
817 | elif len(args) == 2: | |
818 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
819 | len(args), | |
820 | c_arg[0], | |
821 | c_arg[1]) | |
822 | elif len(args) == 3: | |
823 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
824 | len(args), | |
825 | c_arg[0], | |
826 | c_arg[1], | |
827 | c_arg[2]) | |
828 | elif len(args) == 4: | |
829 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
830 | len(args), | |
831 | c_arg[0], | |
832 | c_arg[1], | |
833 | c_arg[2], | |
834 | c_arg[3]) | |
835 | elif len(args) == 5: | |
836 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
837 | len(args), | |
838 | c_arg[0], | |
839 | c_arg[1], | |
840 | c_arg[2], | |
841 | c_arg[3], | |
842 | c_arg[4]) | |
843 | elif len(args) == 6: | |
844 | rc = libseccomp.seccomp_rule_add(self._ctx, action, syscall_num, | |
845 | len(args), | |
846 | c_arg[0], | |
847 | c_arg[1], | |
848 | c_arg[2], | |
849 | c_arg[3], | |
850 | c_arg[4], | |
851 | c_arg[5]) | |
852 | else: | |
853 | raise RuntimeError("Maximum number of arguments exceeded") | |
854 | if rc != 0: | |
855 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
856 | ||
857 | def add_rule_exactly(self, int action, syscall, *args): | |
858 | """ Add a new rule to filter. | |
859 | ||
860 | Arguments: | |
b2f15f3d TH |
861 | action - the rule action: KILL_PROCESS, KILL, TRAP, ERRNO(), TRACE(), |
862 | LOG, or ALLOW | |
62c6598e PM |
863 | syscall - the syscall name or number |
864 | args - variable number of Arg objects | |
865 | ||
866 | Description: | |
867 | Add a new rule to the filter, matching on the given syscall and an | |
868 | optional list of argument comparisons. If the rule is triggered | |
869 | the given action will be taken by the kernel. In order for the | |
870 | rule to trigger, the syscall as well as each argument comparison | |
871 | must be true. | |
872 | ||
873 | This method attempts to add the filter rule exactly as specified | |
874 | which can cause problems on certain architectures, e.g. socket() | |
875 | on 32-bit x86. For a architecture independent version of this | |
876 | method use add_rule(). | |
877 | """ | |
878 | cdef libseccomp.scmp_arg_cmp c_arg[6] | |
879 | if isinstance(syscall, str): | |
880 | syscall_str = syscall.encode() | |
881 | syscall_num = libseccomp.seccomp_syscall_resolve_name(syscall_str) | |
882 | elif isinstance(syscall, int): | |
883 | syscall_num = syscall | |
884 | else: | |
6220c8c0 | 885 | raise TypeError("Syscall must either be an int or str type") |
62c6598e PM |
886 | """ NOTE: the code below exists solely to deal with the varadic |
887 | nature of seccomp_rule_add_exact() function and the inability of | |
888 | Cython to handle this automatically """ | |
2f65c7f4 PM |
889 | if len(args) > 6: |
890 | raise RuntimeError("Maximum number of arguments exceeded") | |
dd673b51 | 891 | cdef Arg arg |
62c6598e PM |
892 | for i, arg in enumerate(args): |
893 | c_arg[i] = arg.to_c() | |
894 | if len(args) == 0: | |
895 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
896 | syscall_num, 0) | |
897 | elif len(args) == 1: | |
898 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
899 | syscall_num, len(args), | |
900 | c_arg[0]) | |
901 | elif len(args) == 2: | |
902 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
903 | syscall_num, len(args), | |
904 | c_arg[0], | |
905 | c_arg[1]) | |
906 | elif len(args) == 3: | |
907 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
908 | syscall_num, len(args), | |
909 | c_arg[0], | |
910 | c_arg[1], | |
911 | c_arg[2]) | |
912 | elif len(args) == 4: | |
913 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
914 | syscall_num, len(args), | |
915 | c_arg[0], | |
916 | c_arg[1], | |
917 | c_arg[2], | |
918 | c_arg[3]) | |
919 | elif len(args) == 5: | |
920 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
921 | syscall_num, len(args), | |
922 | c_arg[0], | |
923 | c_arg[1], | |
924 | c_arg[2], | |
925 | c_arg[3], | |
926 | c_arg[4]) | |
927 | elif len(args) == 6: | |
928 | rc = libseccomp.seccomp_rule_add_exact(self._ctx, action, | |
929 | syscall_num, len(args), | |
930 | c_arg[0], | |
931 | c_arg[1], | |
932 | c_arg[2], | |
933 | c_arg[3], | |
934 | c_arg[4], | |
935 | c_arg[5]) | |
936 | else: | |
937 | raise RuntimeError("Maximum number of arguments exceeded") | |
938 | if rc != 0: | |
939 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
940 | ||
e15f4157 PM |
941 | def receive_notify(self): |
942 | """ Receive seccomp notifications. | |
943 | ||
944 | Description: | |
945 | Receive a seccomp notification from the system, requires the use of | |
946 | the NOTIFY action. | |
947 | """ | |
948 | cdef libseccomp.seccomp_notif *req | |
949 | ||
950 | fd = libseccomp.seccomp_notify_fd(self._ctx) | |
951 | if fd < 0: | |
952 | raise RuntimeError("Notifications not enabled/active") | |
953 | rc = libseccomp.seccomp_notify_alloc(&req, NULL) | |
954 | if rc < 0: | |
955 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
956 | rc = libseccomp.seccomp_notify_receive(fd, req) | |
957 | if rc < 0: | |
958 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
959 | rc = libseccomp.seccomp_notify_id_valid(fd, req.id) | |
960 | if rc < 0: | |
961 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
962 | notify = Notification(req.id, req.pid, req.flags, req.data.nr, | |
963 | req.data.arch, req.data.instruction_pointer, | |
964 | [req.data.args[0], req.data.args[1], | |
965 | req.data.args[2], req.data.args[3], | |
966 | req.data.args[4], req.data.args[5]]) | |
967 | free(req) | |
968 | return notify | |
969 | ||
970 | def respond_notify(self, response): | |
971 | """ Send a seccomp notification response. | |
972 | ||
973 | Arguments: | |
974 | response - the response to send to the system | |
975 | ||
976 | Description: | |
977 | Respond to a seccomp notification. | |
978 | """ | |
979 | cdef libseccomp.seccomp_notif_resp *resp | |
980 | ||
981 | fd = libseccomp.seccomp_notify_fd(self._ctx) | |
982 | if fd < 0: | |
983 | raise RuntimeError("Notifications not enabled/active") | |
984 | rc = libseccomp.seccomp_notify_alloc(NULL, &resp) | |
985 | if rc < 0: | |
986 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
987 | resp.id = response.id | |
988 | resp.val = response.val | |
989 | resp.error = response.error | |
990 | resp.flags = response.flags | |
991 | rc = libseccomp.seccomp_notify_respond(fd, resp) | |
992 | if rc < 0: | |
993 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
994 | ||
62c6598e PM |
995 | def export_pfc(self, file): |
996 | """ Export the filter in PFC format. | |
997 | ||
998 | Arguments: | |
999 | file - the output file | |
1000 | ||
1001 | Description: | |
1002 | Output the filter in Pseudo Filter Code (PFC) to the given file. | |
1003 | The output is functionally equivalent to the BPF based filter | |
1004 | which is loaded into the Linux Kernel. | |
1005 | """ | |
1006 | rc = libseccomp.seccomp_export_pfc(self._ctx, file.fileno()) | |
1007 | if rc != 0: | |
1008 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
1009 | ||
1010 | def export_bpf(self, file): | |
1011 | """ Export the filter in BPF format. | |
1012 | ||
1013 | Arguments: | |
1014 | file - the output file | |
1015 | ||
e15f4157 | 1016 | Description: |
62c6598e PM |
1017 | Output the filter in Berkley Packet Filter (BPF) to the given |
1018 | file. The output is identical to what is loaded into the | |
1019 | Linux Kernel. | |
1020 | """ | |
1021 | rc = libseccomp.seccomp_export_bpf(self._ctx, file.fileno()) | |
1022 | if rc != 0: | |
1023 | raise RuntimeError(str.format("Library error (errno = {0})", rc)) | |
1024 | ||
1025 | # kate: syntax python; | |
1026 | # kate: indent-mode python; space-indent on; indent-width 4; mixedindent off; |