| 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; |