| 1 | /** |
| 2 | * Enhanced Seccomp Architecture/Machine Specific Code |
| 3 | * |
| 4 | * Copyright (c) 2012 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 | #include <elf.h> |
| 23 | #include <errno.h> |
| 24 | #include <stdlib.h> |
| 25 | #include <string.h> |
| 26 | #include <asm/bitsperlong.h> |
| 27 | #include <linux/audit.h> |
| 28 | #include <stdbool.h> |
| 29 | |
| 30 | #include <seccomp.h> |
| 31 | |
| 32 | #include "arch.h" |
| 33 | #include "arch-x86.h" |
| 34 | #include "arch-x86_64.h" |
| 35 | #include "arch-x32.h" |
| 36 | #include "arch-arm.h" |
| 37 | #include "arch-aarch64.h" |
| 38 | #include "arch-mips.h" |
| 39 | #include "arch-mips64.h" |
| 40 | #include "arch-mips64n32.h" |
| 41 | #include "arch-ppc.h" |
| 42 | #include "arch-ppc64.h" |
| 43 | #include "arch-s390.h" |
| 44 | #include "arch-s390x.h" |
| 45 | #include "db.h" |
| 46 | #include "system.h" |
| 47 | |
| 48 | #define default_arg_count_max 6 |
| 49 | |
| 50 | #define default_arg_offset(x) (offsetof(struct seccomp_data, args[x])) |
| 51 | |
| 52 | #if __i386__ |
| 53 | const struct arch_def *arch_def_native = &arch_def_x86; |
| 54 | #elif __x86_64__ |
| 55 | #ifdef __ILP32__ |
| 56 | const struct arch_def *arch_def_native = &arch_def_x32; |
| 57 | #else |
| 58 | const struct arch_def *arch_def_native = &arch_def_x86_64; |
| 59 | #endif /* __ILP32__ */ |
| 60 | #elif __arm__ |
| 61 | const struct arch_def *arch_def_native = &arch_def_arm; |
| 62 | #elif __aarch64__ |
| 63 | const struct arch_def *arch_def_native = &arch_def_aarch64; |
| 64 | #elif __mips__ && _MIPS_SIM == _MIPS_SIM_ABI32 |
| 65 | #if __MIPSEB__ |
| 66 | const struct arch_def *arch_def_native = &arch_def_mips; |
| 67 | #elif __MIPSEL__ |
| 68 | const struct arch_def *arch_def_native = &arch_def_mipsel; |
| 69 | #endif /* _MIPS_SIM_ABI32 */ |
| 70 | #elif __mips__ && _MIPS_SIM == _MIPS_SIM_ABI64 |
| 71 | #if __MIPSEB__ |
| 72 | const struct arch_def *arch_def_native = &arch_def_mips64; |
| 73 | #elif __MIPSEL__ |
| 74 | const struct arch_def *arch_def_native = &arch_def_mipsel64; |
| 75 | #endif /* _MIPS_SIM_ABI64 */ |
| 76 | #elif __mips__ && _MIPS_SIM == _MIPS_SIM_NABI32 |
| 77 | #if __MIPSEB__ |
| 78 | const struct arch_def *arch_def_native = &arch_def_mips64n32; |
| 79 | #elif __MIPSEL__ |
| 80 | const struct arch_def *arch_def_native = &arch_def_mipsel64n32; |
| 81 | #endif /* _MIPS_SIM_NABI32 */ |
| 82 | #elif __PPC64__ |
| 83 | #ifdef __BIG_ENDIAN__ |
| 84 | const struct arch_def *arch_def_native = &arch_def_ppc64; |
| 85 | #else |
| 86 | const struct arch_def *arch_def_native = &arch_def_ppc64le; |
| 87 | #endif |
| 88 | #elif __PPC__ |
| 89 | const struct arch_def *arch_def_native = &arch_def_ppc; |
| 90 | #elif __s390x__ /* s390x must be checked before s390 */ |
| 91 | const struct arch_def *arch_def_native = &arch_def_s390x; |
| 92 | #elif __s390__ |
| 93 | const struct arch_def *arch_def_native = &arch_def_s390; |
| 94 | #else |
| 95 | #error the arch code needs to know about your machine type |
| 96 | #endif /* machine type guess */ |
| 97 | |
| 98 | /** |
| 99 | * Validate the architecture token |
| 100 | * @param arch the architecture token |
| 101 | * |
| 102 | * Verify the given architecture token; return zero if valid, -EINVAL if not. |
| 103 | * |
| 104 | */ |
| 105 | int arch_valid(uint32_t arch) |
| 106 | { |
| 107 | return (arch_def_lookup(arch) ? 0 : -EINVAL); |
| 108 | } |
| 109 | |
| 110 | /** |
| 111 | * Lookup the architecture definition |
| 112 | * @param token the architecure token |
| 113 | * |
| 114 | * Return the matching architecture definition, returns NULL on failure. |
| 115 | * |
| 116 | */ |
| 117 | const struct arch_def *arch_def_lookup(uint32_t token) |
| 118 | { |
| 119 | switch (token) { |
| 120 | case SCMP_ARCH_X86: |
| 121 | return &arch_def_x86; |
| 122 | case SCMP_ARCH_X86_64: |
| 123 | return &arch_def_x86_64; |
| 124 | case SCMP_ARCH_X32: |
| 125 | return &arch_def_x32; |
| 126 | case SCMP_ARCH_ARM: |
| 127 | return &arch_def_arm; |
| 128 | case SCMP_ARCH_AARCH64: |
| 129 | return &arch_def_aarch64; |
| 130 | case SCMP_ARCH_MIPS: |
| 131 | return &arch_def_mips; |
| 132 | case SCMP_ARCH_MIPSEL: |
| 133 | return &arch_def_mipsel; |
| 134 | case SCMP_ARCH_MIPS64: |
| 135 | return &arch_def_mips64; |
| 136 | case SCMP_ARCH_MIPSEL64: |
| 137 | return &arch_def_mipsel64; |
| 138 | case SCMP_ARCH_MIPS64N32: |
| 139 | return &arch_def_mips64n32; |
| 140 | case SCMP_ARCH_MIPSEL64N32: |
| 141 | return &arch_def_mipsel64n32; |
| 142 | case SCMP_ARCH_PPC: |
| 143 | return &arch_def_ppc; |
| 144 | case SCMP_ARCH_PPC64: |
| 145 | return &arch_def_ppc64; |
| 146 | case SCMP_ARCH_PPC64LE: |
| 147 | return &arch_def_ppc64le; |
| 148 | case SCMP_ARCH_S390: |
| 149 | return &arch_def_s390; |
| 150 | case SCMP_ARCH_S390X: |
| 151 | return &arch_def_s390x; |
| 152 | } |
| 153 | |
| 154 | return NULL; |
| 155 | } |
| 156 | |
| 157 | /** |
| 158 | * Lookup the architecture definition by name |
| 159 | * @param arch_name the architecure name |
| 160 | * |
| 161 | * Return the matching architecture definition, returns NULL on failure. |
| 162 | * |
| 163 | */ |
| 164 | const struct arch_def *arch_def_lookup_name(const char *arch_name) |
| 165 | { |
| 166 | if (strcmp(arch_name, "x86") == 0) |
| 167 | return &arch_def_x86; |
| 168 | else if (strcmp(arch_name, "x86_64") == 0) |
| 169 | return &arch_def_x86_64; |
| 170 | else if (strcmp(arch_name, "x32") == 0) |
| 171 | return &arch_def_x32; |
| 172 | else if (strcmp(arch_name, "arm") == 0) |
| 173 | return &arch_def_arm; |
| 174 | else if (strcmp(arch_name, "aarch64") == 0) |
| 175 | return &arch_def_aarch64; |
| 176 | else if (strcmp(arch_name, "mips") == 0) |
| 177 | return &arch_def_mips; |
| 178 | else if (strcmp(arch_name, "mipsel") == 0) |
| 179 | return &arch_def_mipsel; |
| 180 | else if (strcmp(arch_name, "mips64") == 0) |
| 181 | return &arch_def_mips64; |
| 182 | else if (strcmp(arch_name, "mipsel64") == 0) |
| 183 | return &arch_def_mipsel64; |
| 184 | else if (strcmp(arch_name, "mips64n32") == 0) |
| 185 | return &arch_def_mips64n32; |
| 186 | else if (strcmp(arch_name, "mipsel64n32") == 0) |
| 187 | return &arch_def_mipsel64n32; |
| 188 | else if (strcmp(arch_name, "ppc") == 0) |
| 189 | return &arch_def_ppc; |
| 190 | else if (strcmp(arch_name, "ppc64") == 0) |
| 191 | return &arch_def_ppc64; |
| 192 | else if (strcmp(arch_name, "ppc64le") == 0) |
| 193 | return &arch_def_ppc64le; |
| 194 | else if (strcmp(arch_name, "s390") == 0) |
| 195 | return &arch_def_s390; |
| 196 | else if (strcmp(arch_name, "s390x") == 0) |
| 197 | return &arch_def_s390x; |
| 198 | |
| 199 | return NULL; |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * Determine the maximum number of syscall arguments |
| 204 | * @param arch the architecture definition |
| 205 | * |
| 206 | * Determine the maximum number of syscall arguments for the given architecture. |
| 207 | * Returns the number of arguments on success, negative values on failure. |
| 208 | * |
| 209 | */ |
| 210 | int arch_arg_count_max(const struct arch_def *arch) |
| 211 | { |
| 212 | return (arch_valid(arch->token) == 0 ? default_arg_count_max : -EDOM); |
| 213 | } |
| 214 | |
| 215 | /** |
| 216 | * Determine the argument offset for the lower 32 bits |
| 217 | * @param arch the architecture definition |
| 218 | * @param arg the argument number |
| 219 | * |
| 220 | * Determine the correct offset for the low 32 bits of the given argument based |
| 221 | * on the architecture definition. Returns the offset on success, negative |
| 222 | * values on failure. |
| 223 | * |
| 224 | */ |
| 225 | int arch_arg_offset_lo(const struct arch_def *arch, unsigned int arg) |
| 226 | { |
| 227 | if (arch_valid(arch->token) < 0) |
| 228 | return -EDOM; |
| 229 | |
| 230 | switch (arch->endian) { |
| 231 | case ARCH_ENDIAN_LITTLE: |
| 232 | return default_arg_offset(arg); |
| 233 | break; |
| 234 | case ARCH_ENDIAN_BIG: |
| 235 | return default_arg_offset(arg) + 4; |
| 236 | break; |
| 237 | default: |
| 238 | return -EDOM; |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | /** |
| 243 | * Determine the argument offset for the high 32 bits |
| 244 | * @param arch the architecture definition |
| 245 | * @param arg the argument number |
| 246 | * |
| 247 | * Determine the correct offset for the high 32 bits of the given argument |
| 248 | * based on the architecture definition. Returns the offset on success, |
| 249 | * negative values on failure. |
| 250 | * |
| 251 | */ |
| 252 | int arch_arg_offset_hi(const struct arch_def *arch, unsigned int arg) |
| 253 | { |
| 254 | if (arch_valid(arch->token) < 0 || arch->size != ARCH_SIZE_64) |
| 255 | return -EDOM; |
| 256 | |
| 257 | switch (arch->endian) { |
| 258 | case ARCH_ENDIAN_LITTLE: |
| 259 | return default_arg_offset(arg) + 4; |
| 260 | break; |
| 261 | case ARCH_ENDIAN_BIG: |
| 262 | return default_arg_offset(arg); |
| 263 | break; |
| 264 | default: |
| 265 | return -EDOM; |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | /** |
| 270 | * Determine the argument offset |
| 271 | * @param arch the architecture definition |
| 272 | * @param arg the argument number |
| 273 | * |
| 274 | * Determine the correct offset for the given argument based on the |
| 275 | * architecture definition. Returns the offset on success, negative values on |
| 276 | * failure. |
| 277 | * |
| 278 | */ |
| 279 | int arch_arg_offset(const struct arch_def *arch, unsigned int arg) |
| 280 | { |
| 281 | return arch_arg_offset_lo(arch, arg); |
| 282 | } |
| 283 | |
| 284 | /** |
| 285 | * Resolve a syscall name to a number |
| 286 | * @param arch the architecture definition |
| 287 | * @param name the syscall name |
| 288 | * |
| 289 | * Resolve the given syscall name to the syscall number based on the given |
| 290 | * architecture. Returns the syscall number on success, including negative |
| 291 | * pseudo syscall numbers; returns __NR_SCMP_ERROR on failure. |
| 292 | * |
| 293 | */ |
| 294 | int arch_syscall_resolve_name(const struct arch_def *arch, const char *name) |
| 295 | { |
| 296 | if (arch->syscall_resolve_name) |
| 297 | return (*arch->syscall_resolve_name)(name); |
| 298 | |
| 299 | return __NR_SCMP_ERROR; |
| 300 | } |
| 301 | |
| 302 | /** |
| 303 | * Resolve a syscall number to a name |
| 304 | * @param arch the architecture definition |
| 305 | * @param num the syscall number |
| 306 | * |
| 307 | * Resolve the given syscall number to the syscall name based on the given |
| 308 | * architecture. Returns a pointer to the syscall name string on success, |
| 309 | * including pseudo syscall names; returns NULL on failure. |
| 310 | * |
| 311 | */ |
| 312 | const char *arch_syscall_resolve_num(const struct arch_def *arch, int num) |
| 313 | { |
| 314 | if (arch->syscall_resolve_num) |
| 315 | return (*arch->syscall_resolve_num)(num); |
| 316 | |
| 317 | return NULL; |
| 318 | } |
| 319 | |
| 320 | /** |
| 321 | * Translate the syscall number |
| 322 | * @param arch the architecture definition |
| 323 | * @param syscall the syscall number |
| 324 | * |
| 325 | * Translate the syscall number, in the context of the native architecure, to |
| 326 | * the provided architecure. Returns zero on success, negative values on |
| 327 | * failure. |
| 328 | * |
| 329 | */ |
| 330 | int arch_syscall_translate(const struct arch_def *arch, int *syscall) |
| 331 | { |
| 332 | int sc_num; |
| 333 | const char *sc_name; |
| 334 | |
| 335 | if (arch->token != arch_def_native->token) { |
| 336 | sc_name = arch_syscall_resolve_num(arch_def_native, *syscall); |
| 337 | if (sc_name == NULL) |
| 338 | return -EFAULT; |
| 339 | |
| 340 | sc_num = arch_syscall_resolve_name(arch, sc_name); |
| 341 | if (sc_num == __NR_SCMP_ERROR) |
| 342 | return -EFAULT; |
| 343 | |
| 344 | *syscall = sc_num; |
| 345 | } |
| 346 | |
| 347 | return 0; |
| 348 | } |
| 349 | |
| 350 | /** |
| 351 | * Rewrite a syscall value to match the architecture |
| 352 | * @param arch the architecture definition |
| 353 | * @param syscall the syscall number |
| 354 | * |
| 355 | * Syscalls can vary across different architectures so this function rewrites |
| 356 | * the syscall into the correct value for the specified architecture. Returns |
| 357 | * zero on success, -EDOM if the syscall is not defined for @arch, and negative |
| 358 | * values on failure. |
| 359 | * |
| 360 | */ |
| 361 | int arch_syscall_rewrite(const struct arch_def *arch, int *syscall) |
| 362 | { |
| 363 | int sys = *syscall; |
| 364 | |
| 365 | if (sys >= 0) { |
| 366 | /* we shouldn't be here - no rewrite needed */ |
| 367 | return 0; |
| 368 | } else if (sys < 0 && sys > -100) { |
| 369 | /* reserved values */ |
| 370 | return -EINVAL; |
| 371 | } else if (sys <= -100 && sys > -10000) { |
| 372 | /* rewritable syscalls */ |
| 373 | if (arch->syscall_rewrite) |
| 374 | (*arch->syscall_rewrite)(syscall); |
| 375 | } |
| 376 | |
| 377 | /* syscalls not defined on this architecture */ |
| 378 | if ((*syscall) < 0) |
| 379 | return -EDOM; |
| 380 | return 0; |
| 381 | } |
| 382 | |
| 383 | /** |
| 384 | * Add a new rule to the specified filter |
| 385 | * @param col the filter collection |
| 386 | * @param db the seccomp filter db |
| 387 | * @param strict the strict flag |
| 388 | * @param action the filter action |
| 389 | * @param syscall the syscall number |
| 390 | * @param chain_len the number of argument filters in the argument filter chain |
| 391 | * @param chain the argument filter chain |
| 392 | * |
| 393 | * This function adds a new argument/comparison/value to the seccomp filter for |
| 394 | * a syscall; multiple arguments can be specified and they will be chained |
| 395 | * together (essentially AND'd together) in the filter. When the strict flag |
| 396 | * is true the function will fail if the exact rule can not be added to the |
| 397 | * filter, if the strict flag is false the function will not fail if the |
| 398 | * function needs to adjust the rule due to architecture specifics. Returns |
| 399 | * zero on success, negative values on failure. |
| 400 | * |
| 401 | */ |
| 402 | int arch_filter_rule_add(struct db_filter_col *col, struct db_filter *db, |
| 403 | bool strict, uint32_t action, int syscall, |
| 404 | unsigned int chain_len, struct db_api_arg *chain) |
| 405 | { |
| 406 | int rc; |
| 407 | size_t chain_size = sizeof(*chain) * chain_len; |
| 408 | struct db_api_rule_list *rule, *rule_tail; |
| 409 | |
| 410 | /* ensure we aren't using any reserved syscall values */ |
| 411 | if (syscall < 0 && syscall > -100) |
| 412 | return -EINVAL; |
| 413 | |
| 414 | /* translate the syscall */ |
| 415 | rc = arch_syscall_translate(db->arch, &syscall); |
| 416 | if (rc < 0) |
| 417 | return rc; |
| 418 | |
| 419 | /* copy of the chain for each filter in the collection */ |
| 420 | rule = malloc(sizeof(*rule)); |
| 421 | if (rule == NULL) |
| 422 | return -ENOMEM; |
| 423 | rule->args = malloc(chain_size); |
| 424 | if (rule->args == NULL) { |
| 425 | free(rule); |
| 426 | return -ENOMEM; |
| 427 | } |
| 428 | rule->action = action; |
| 429 | rule->syscall = syscall; |
| 430 | rule->args_cnt = chain_len; |
| 431 | memcpy(rule->args, chain, chain_size); |
| 432 | rule->prev = NULL; |
| 433 | rule->next = NULL; |
| 434 | |
| 435 | /* add the new rule to the existing filter */ |
| 436 | if (db->arch->rule_add == NULL) { |
| 437 | /* negative syscalls require a db->arch->rule_add() function */ |
| 438 | if (syscall < 0 && strict) { |
| 439 | rc = -EDOM; |
| 440 | goto rule_add_failure; |
| 441 | } |
| 442 | rc = db_rule_add(db, rule); |
| 443 | } else |
| 444 | rc = (db->arch->rule_add)(col, db, strict, rule); |
| 445 | if (rc == 0) { |
| 446 | /* insert the chain to the end of the filter's rule list */ |
| 447 | rule_tail = rule; |
| 448 | while (rule_tail->next) |
| 449 | rule_tail = rule_tail->next; |
| 450 | if (db->rules != NULL) { |
| 451 | rule->prev = db->rules->prev; |
| 452 | rule_tail->next = db->rules; |
| 453 | db->rules->prev->next = rule; |
| 454 | db->rules->prev = rule_tail; |
| 455 | } else { |
| 456 | rule->prev = rule_tail; |
| 457 | rule_tail->next = rule; |
| 458 | db->rules = rule; |
| 459 | } |
| 460 | } else |
| 461 | goto rule_add_failure; |
| 462 | |
| 463 | return 0; |
| 464 | |
| 465 | rule_add_failure: |
| 466 | do { |
| 467 | rule_tail = rule; |
| 468 | rule = rule->next; |
| 469 | free(rule_tail->args); |
| 470 | free(rule_tail); |
| 471 | } while (rule); |
| 472 | return rc; |
| 473 | } |