1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 */ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 10 #include "syscall_filter.h" 11 12 #include "util.h" 13 14 /* clang-format off */ 15 #define MAX_POLICY_LINE_LENGTH 1024 16 17 #define ONE_INSTR 1 18 #define TWO_INSTRS 2 19 /* clang-format on */ 20 21 int seccomp_can_softfail(void) 22 { 23 #if defined(USE_SECCOMP_SOFTFAIL) 24 return 1; 25 #endif 26 return 0; 27 } 28 29 int str_to_op(const char *op_str) 30 { 31 if (!strcmp(op_str, "==")) { 32 return EQ; 33 } else if (!strcmp(op_str, "!=")) { 34 return NE; 35 } else if (!strcmp(op_str, "&")) { 36 return SET; 37 } else if (!strcmp(op_str, "in")) { 38 return IN; 39 } else { 40 return 0; 41 } 42 } 43 44 struct sock_filter *new_instr_buf(size_t count) 45 { 46 struct sock_filter *buf = calloc(count, sizeof(struct sock_filter)); 47 if (!buf) 48 die("could not allocate BPF instruction buffer"); 49 50 return buf; 51 } 52 53 struct filter_block *new_filter_block(void) 54 { 55 struct filter_block *block = calloc(1, sizeof(struct filter_block)); 56 if (!block) 57 die("could not allocate BPF filter block"); 58 59 block->instrs = NULL; 60 block->last = block->next = NULL; 61 62 return block; 63 } 64 65 void append_filter_block(struct filter_block *head, struct sock_filter *instrs, 66 size_t len) 67 { 68 struct filter_block *new_last; 69 70 /* 71 * If |head| has no filter assigned yet, 72 * we don't create a new node. 73 */ 74 if (head->instrs == NULL) { 75 new_last = head; 76 } else { 77 new_last = new_filter_block(); 78 if (head->next != NULL) { 79 head->last->next = new_last; 80 head->last = new_last; 81 } else { 82 head->last = head->next = new_last; 83 } 84 head->total_len += len; 85 } 86 87 new_last->instrs = instrs; 88 new_last->total_len = new_last->len = len; 89 new_last->last = new_last->next = NULL; 90 } 91 92 void extend_filter_block_list(struct filter_block *list, 93 struct filter_block *another) 94 { 95 if (list->last != NULL) { 96 list->last->next = another; 97 list->last = another->last; 98 } else { 99 list->next = another; 100 list->last = another->last; 101 } 102 list->total_len += another->total_len; 103 } 104 105 void append_ret_kill(struct filter_block *head) 106 { 107 struct sock_filter *filter = new_instr_buf(ONE_INSTR); 108 set_bpf_ret_kill(filter); 109 append_filter_block(head, filter, ONE_INSTR); 110 } 111 112 void append_ret_trap(struct filter_block *head) 113 { 114 struct sock_filter *filter = new_instr_buf(ONE_INSTR); 115 set_bpf_ret_trap(filter); 116 append_filter_block(head, filter, ONE_INSTR); 117 } 118 119 void append_ret_errno(struct filter_block *head, int errno_val) 120 { 121 struct sock_filter *filter = new_instr_buf(ONE_INSTR); 122 set_bpf_ret_errno(filter, errno_val); 123 append_filter_block(head, filter, ONE_INSTR); 124 } 125 126 void append_allow_syscall(struct filter_block *head, int nr) 127 { 128 struct sock_filter *filter = new_instr_buf(ALLOW_SYSCALL_LEN); 129 size_t len = bpf_allow_syscall(filter, nr); 130 if (len != ALLOW_SYSCALL_LEN) 131 die("error building syscall number comparison"); 132 133 append_filter_block(head, filter, len); 134 } 135 136 void allow_logging_syscalls(struct filter_block *head) 137 { 138 unsigned int i; 139 for (i = 0; i < log_syscalls_len; i++) { 140 warn("allowing syscall: %s", log_syscalls[i]); 141 append_allow_syscall(head, lookup_syscall(log_syscalls[i])); 142 } 143 } 144 145 unsigned int get_label_id(struct bpf_labels *labels, const char *label_str) 146 { 147 int label_id = bpf_label_id(labels, label_str); 148 if (label_id < 0) 149 die("could not allocate BPF label string"); 150 return label_id; 151 } 152 153 unsigned int group_end_lbl(struct bpf_labels *labels, int nr, int idx) 154 { 155 char lbl_str[MAX_BPF_LABEL_LEN]; 156 snprintf(lbl_str, MAX_BPF_LABEL_LEN, "%d_%d_end", nr, idx); 157 return get_label_id(labels, lbl_str); 158 } 159 160 unsigned int success_lbl(struct bpf_labels *labels, int nr) 161 { 162 char lbl_str[MAX_BPF_LABEL_LEN]; 163 snprintf(lbl_str, MAX_BPF_LABEL_LEN, "%d_success", nr); 164 return get_label_id(labels, lbl_str); 165 } 166 167 int is_implicit_relative_path(const char *filename) 168 { 169 return filename[0] != '/' && (filename[0] != '.' || filename[1] != '/'); 170 } 171 172 int compile_atom(struct filter_block *head, char *atom, 173 struct bpf_labels *labels, int nr, int grp_idx) 174 { 175 /* Splits the atom. */ 176 char *atom_ptr; 177 char *argidx_str = strtok_r(atom, " ", &atom_ptr); 178 if (argidx_str == NULL) { 179 warn("empty atom"); 180 return -1; 181 } 182 183 char *operator_str = strtok_r(NULL, " ", &atom_ptr); 184 if (operator_str == NULL) { 185 warn("invalid atom '%s'", argidx_str); 186 return -1; 187 } 188 189 char *constant_str = strtok_r(NULL, " ", &atom_ptr); 190 if (constant_str == NULL) { 191 warn("invalid atom '%s %s'", argidx_str, operator_str); 192 return -1; 193 } 194 195 /* Checks that there are no extra tokens. */ 196 const char *extra = strtok_r(NULL, " ", &atom_ptr); 197 if (extra != NULL) { 198 warn("extra token '%s'", extra); 199 return -1; 200 } 201 202 if (strncmp(argidx_str, "arg", 3)) { 203 warn("invalid argument token '%s'", argidx_str); 204 return -1; 205 } 206 207 char *argidx_ptr; 208 long int argidx = strtol(argidx_str + 3, &argidx_ptr, 10); 209 /* 210 * Checks that an actual argument index was parsed, 211 * and that there was nothing left after the index. 212 */ 213 if (argidx_ptr == argidx_str + 3 || *argidx_ptr != '\0') { 214 warn("invalid argument index '%s'", argidx_str + 3); 215 return -1; 216 } 217 218 int op = str_to_op(operator_str); 219 if (op < MIN_OPERATOR) { 220 warn("invalid operator '%s'", operator_str); 221 return -1; 222 } 223 224 char *constant_str_ptr; 225 long int c = parse_constant(constant_str, &constant_str_ptr); 226 if (constant_str_ptr == constant_str) { 227 warn("invalid constant '%s'", constant_str); 228 return -1; 229 } 230 231 /* 232 * Looks up the label for the end of the AND statement 233 * this atom belongs to. 234 */ 235 unsigned int id = group_end_lbl(labels, nr, grp_idx); 236 237 /* 238 * Builds a BPF comparison between a syscall argument 239 * and a constant. 240 * The comparison lives inside an AND statement. 241 * If the comparison succeeds, we continue 242 * to the next comparison. 243 * If this comparison fails, the whole AND statement 244 * will fail, so we jump to the end of this AND statement. 245 */ 246 struct sock_filter *comp_block; 247 size_t len = bpf_arg_comp(&comp_block, op, argidx, c, id); 248 if (len == 0) 249 return -1; 250 251 append_filter_block(head, comp_block, len); 252 return 0; 253 } 254 255 int compile_errno(struct filter_block *head, char *ret_errno, int use_ret_trap) 256 { 257 char *errno_ptr; 258 259 /* Splits the 'return' keyword and the actual errno value. */ 260 char *ret_str = strtok_r(ret_errno, " ", &errno_ptr); 261 if (strncmp(ret_str, "return", strlen("return"))) 262 return -1; 263 264 char *errno_val_str = strtok_r(NULL, " ", &errno_ptr); 265 266 if (errno_val_str) { 267 char *errno_val_ptr; 268 int errno_val = parse_constant(errno_val_str, &errno_val_ptr); 269 /* Checks to see if we parsed an actual errno. */ 270 if (errno_val_ptr == errno_val_str || errno_val == -1) { 271 warn("invalid errno value '%s'", errno_val_ptr); 272 return -1; 273 } 274 275 append_ret_errno(head, errno_val); 276 } else { 277 if (!use_ret_trap) 278 append_ret_kill(head); 279 else 280 append_ret_trap(head); 281 } 282 return 0; 283 } 284 285 struct filter_block *compile_policy_line(int nr, const char *policy_line, 286 unsigned int entry_lbl_id, 287 struct bpf_labels *labels, 288 int use_ret_trap) 289 { 290 /* 291 * |policy_line| should be an expression of the form: 292 * "arg0 == 3 && arg1 == 5 || arg0 == 0x8" 293 * 294 * This is, an expression in DNF (disjunctive normal form); 295 * a disjunction ('||') of one or more conjunctions ('&&') 296 * of one or more atoms. 297 * 298 * Atoms are of the form "arg{DNUM} {OP} {NUM}" 299 * where: 300 * - DNUM is a decimal number. 301 * - OP is an operator: ==, !=, & (flags set), or 'in' (inclusion). 302 * - NUM is an octal, decimal, or hexadecimal number. 303 * 304 * When the syscall arguments make the expression true, 305 * the syscall is allowed. If not, the process is killed. 306 * 307 * To block a syscall without killing the process, 308 * |policy_line| can be of the form: 309 * "return <errno>" 310 * 311 * This "return {NUM}" policy line will block the syscall, 312 * make it return -1 and set |errno| to NUM. 313 * 314 * A regular policy line can also include a "return <errno>" clause, 315 * separated by a semicolon (';'): 316 * "arg0 == 3 && arg1 == 5 || arg0 == 0x8; return {NUM}" 317 * 318 * If the syscall arguments don't make the expression true, 319 * the syscall will be blocked as above instead of killing the process. 320 */ 321 322 size_t len = 0; 323 int grp_idx = 0; 324 325 /* Checks for empty policy lines. */ 326 if (strlen(policy_line) == 0) { 327 warn("empty policy line"); 328 return NULL; 329 } 330 331 /* Checks for overly long policy lines. */ 332 if (strlen(policy_line) >= MAX_POLICY_LINE_LENGTH) 333 return NULL; 334 335 /* We will modify |policy_line|, so let's make a copy. */ 336 char *line = strndup(policy_line, MAX_POLICY_LINE_LENGTH); 337 if (!line) 338 return NULL; 339 340 /* 341 * We build the filter section as a collection of smaller 342 * "filter blocks" linked together in a singly-linked list. 343 */ 344 struct filter_block *head = new_filter_block(); 345 346 /* 347 * Filter sections begin with a label where the main filter 348 * will jump after checking the syscall number. 349 */ 350 struct sock_filter *entry_label = new_instr_buf(ONE_INSTR); 351 set_bpf_lbl(entry_label, entry_lbl_id); 352 append_filter_block(head, entry_label, ONE_INSTR); 353 354 /* Checks whether we're unconditionally blocking this syscall. */ 355 if (strncmp(line, "return", strlen("return")) == 0) { 356 if (compile_errno(head, line, use_ret_trap) < 0) { 357 free_block_list(head); 358 free(line); 359 return NULL; 360 } 361 free(line); 362 return head; 363 } 364 365 /* Splits the optional "return <errno>" part. */ 366 char *line_ptr; 367 char *arg_filter = strtok_r(line, ";", &line_ptr); 368 char *ret_errno = strtok_r(NULL, ";", &line_ptr); 369 370 /* 371 * Splits the policy line by '||' into conjunctions and each conjunction 372 * by '&&' into atoms. 373 */ 374 char *arg_filter_str = arg_filter; 375 char *group; 376 while ((group = tokenize(&arg_filter_str, "||")) != NULL) { 377 char *group_str = group; 378 char *comp; 379 while ((comp = tokenize(&group_str, "&&")) != NULL) { 380 /* Compiles each atom into a BPF block. */ 381 if (compile_atom(head, comp, labels, nr, grp_idx) < 0) { 382 free_block_list(head); 383 free(line); 384 return NULL; 385 } 386 } 387 /* 388 * If the AND statement succeeds, we're done, 389 * so jump to SUCCESS line. 390 */ 391 unsigned int id = success_lbl(labels, nr); 392 struct sock_filter *group_end_block = new_instr_buf(TWO_INSTRS); 393 len = set_bpf_jump_lbl(group_end_block, id); 394 /* 395 * The end of each AND statement falls after the 396 * jump to SUCCESS. 397 */ 398 id = group_end_lbl(labels, nr, grp_idx++); 399 len += set_bpf_lbl(group_end_block + len, id); 400 append_filter_block(head, group_end_block, len); 401 } 402 403 /* 404 * If no AND statements succeed, we end up here, 405 * because we never jumped to SUCCESS. 406 * If we have to return an errno, do it, 407 * otherwise just kill the task. 408 */ 409 if (ret_errno) { 410 if (compile_errno(head, ret_errno, use_ret_trap) < 0) { 411 free_block_list(head); 412 free(line); 413 return NULL; 414 } 415 } else { 416 if (!use_ret_trap) 417 append_ret_kill(head); 418 else 419 append_ret_trap(head); 420 } 421 422 /* 423 * Every time the filter succeeds we jump to a predefined SUCCESS 424 * label. Add that label and BPF RET_ALLOW code now. 425 */ 426 unsigned int id = success_lbl(labels, nr); 427 struct sock_filter *success_block = new_instr_buf(TWO_INSTRS); 428 len = set_bpf_lbl(success_block, id); 429 len += set_bpf_ret_allow(success_block + len); 430 append_filter_block(head, success_block, len); 431 432 free(line); 433 return head; 434 } 435 436 int parse_include_statement(char *policy_line, unsigned int include_level, 437 const char **ret_filename) 438 { 439 if (strncmp("@include", policy_line, strlen("@include")) != 0) { 440 warn("invalid statement '%s'", policy_line); 441 return -1; 442 } 443 444 if (policy_line[strlen("@include")] != ' ') { 445 warn("invalid include statement '%s'", policy_line); 446 return -1; 447 } 448 449 /* 450 * Disallow nested includes: only the initial policy file can have 451 * @include statements. 452 * Nested includes are not currently necessary and make the policy 453 * harder to understand. 454 */ 455 if (include_level > 0) { 456 warn("@include statement nested too deep"); 457 return -1; 458 } 459 460 char *statement = policy_line; 461 /* Discard "@include" token. */ 462 (void)strsep(&statement, " "); 463 464 /* 465 * compile_filter() below receives a FILE*, so it's not trivial to open 466 * included files relative to the initial policy filename. 467 * To avoid mistakes, force the included file path to be absolute 468 * (start with '/'), or to explicitly load the file relative to CWD by 469 * using './'. 470 */ 471 const char *filename = statement; 472 if (is_implicit_relative_path(filename)) { 473 warn("compile_file: implicit relative path '%s' not supported, " 474 "use './%s'", 475 filename, filename); 476 return -1; 477 } 478 479 *ret_filename = filename; 480 return 0; 481 } 482 483 int compile_file(FILE *policy_file, struct filter_block *head, 484 struct filter_block **arg_blocks, struct bpf_labels *labels, 485 int use_ret_trap, int allow_logging, 486 unsigned int include_level) 487 { 488 /* 489 * Loop through all the lines in the policy file. 490 * Build a jump table for the syscall number. 491 * If the policy line has an arg filter, build the arg filter 492 * as well. 493 * Chain the filter sections together and dump them into 494 * the final buffer at the end. 495 */ 496 char *line = NULL; 497 size_t len = 0; 498 int ret = 0; 499 500 while (getline(&line, &len, policy_file) != -1) { 501 char *policy_line = line; 502 policy_line = strip(policy_line); 503 504 /* Allow comments and empty lines. */ 505 if (*policy_line == '#' || *policy_line == '\0') { 506 /* Reuse |line| in the next getline() call. */ 507 continue; 508 } 509 510 /* Allow @include statements. */ 511 if (*policy_line == '@') { 512 const char *filename = NULL; 513 if (parse_include_statement(policy_line, include_level, 514 &filename) != 0) { 515 warn("compile_file: failed to parse include " 516 "statement"); 517 ret = -1; 518 goto free_line; 519 } 520 521 FILE *included_file = fopen(filename, "re"); 522 if (included_file == NULL) { 523 pwarn("compile_file: fopen('%s') failed", 524 filename); 525 ret = -1; 526 goto free_line; 527 } 528 if (compile_file(included_file, head, arg_blocks, 529 labels, use_ret_trap, allow_logging, 530 ++include_level) == -1) { 531 warn("compile_file: '@include %s' failed", 532 filename); 533 fclose(included_file); 534 ret = -1; 535 goto free_line; 536 } 537 fclose(included_file); 538 continue; 539 } 540 541 /* 542 * If it's not a comment, or an empty line, or an @include 543 * statement, treat |policy_line| as a regular policy line. 544 */ 545 char *syscall_name = strsep(&policy_line, ":"); 546 policy_line = strip(policy_line); 547 if (*policy_line == '\0') { 548 warn("compile_file: empty policy line"); 549 ret = -1; 550 goto free_line; 551 } 552 553 int nr = lookup_syscall(syscall_name); 554 if (nr < 0) { 555 warn("compile_file: nonexistent syscall '%s'", 556 syscall_name); 557 if (allow_logging) { 558 /* 559 * If we're logging failures, assume we're in a 560 * debugging case and continue. 561 * This is not super risky because an invalid 562 * syscall name is likely caused by a typo or by 563 * leftover lines from a different architecture. 564 * In either case, not including a policy line 565 * is equivalent to killing the process if the 566 * syscall is made, so there's no added attack 567 * surface. 568 */ 569 /* Reuse |line| in the next getline() call. */ 570 continue; 571 } 572 ret = -1; 573 goto free_line; 574 } 575 576 /* 577 * For each syscall, add either a simple ALLOW, 578 * or an arg filter block. 579 */ 580 if (strcmp(policy_line, "1") == 0) { 581 /* Add simple ALLOW. */ 582 append_allow_syscall(head, nr); 583 } else { 584 /* 585 * Create and jump to the label that will hold 586 * the arg filter block. 587 */ 588 unsigned int id = bpf_label_id(labels, syscall_name); 589 struct sock_filter *nr_comp = 590 new_instr_buf(ALLOW_SYSCALL_LEN); 591 bpf_allow_syscall_args(nr_comp, nr, id); 592 append_filter_block(head, nr_comp, ALLOW_SYSCALL_LEN); 593 594 /* Build the arg filter block. */ 595 struct filter_block *block = compile_policy_line( 596 nr, policy_line, id, labels, use_ret_trap); 597 598 if (!block) { 599 if (*arg_blocks) { 600 free_block_list(*arg_blocks); 601 } 602 ret = -1; 603 goto free_line; 604 } 605 606 if (*arg_blocks) { 607 extend_filter_block_list(*arg_blocks, block); 608 } else { 609 *arg_blocks = block; 610 } 611 } 612 /* Reuse |line| in the next getline() call. */ 613 } 614 615 free_line: 616 free(line); 617 return ret; 618 } 619 620 int compile_filter(FILE *initial_file, struct sock_fprog *prog, 621 int use_ret_trap, int allow_logging) 622 { 623 struct bpf_labels labels; 624 labels.count = 0; 625 626 if (!initial_file) { 627 warn("compile_filter: |initial_file| is NULL"); 628 return -1; 629 } 630 631 struct filter_block *head = new_filter_block(); 632 struct filter_block *arg_blocks = NULL; 633 634 /* Start filter by validating arch. */ 635 struct sock_filter *valid_arch = new_instr_buf(ARCH_VALIDATION_LEN); 636 size_t len = bpf_validate_arch(valid_arch); 637 append_filter_block(head, valid_arch, len); 638 639 /* Load syscall number. */ 640 struct sock_filter *load_nr = new_instr_buf(ONE_INSTR); 641 len = bpf_load_syscall_nr(load_nr); 642 append_filter_block(head, load_nr, len); 643 644 /* If logging failures, allow the necessary syscalls first. */ 645 if (allow_logging) 646 allow_logging_syscalls(head); 647 648 if (compile_file(initial_file, head, &arg_blocks, &labels, use_ret_trap, 649 allow_logging, 0 /* include_level */) != 0) { 650 warn("compile_filter: compile_file() failed"); 651 free_block_list(head); 652 free_block_list(arg_blocks); 653 free_label_strings(&labels); 654 return -1; 655 } 656 657 /* 658 * If none of the syscalls match, either fall through to KILL, 659 * or return TRAP. 660 */ 661 if (!use_ret_trap) 662 append_ret_kill(head); 663 else 664 append_ret_trap(head); 665 666 /* Allocate the final buffer, now that we know its size. */ 667 size_t final_filter_len = 668 head->total_len + (arg_blocks ? arg_blocks->total_len : 0); 669 if (final_filter_len > BPF_MAXINSNS) 670 return -1; 671 672 struct sock_filter *final_filter = 673 calloc(final_filter_len, sizeof(struct sock_filter)); 674 675 if (flatten_block_list(head, final_filter, 0, final_filter_len) < 0) 676 return -1; 677 678 if (flatten_block_list(arg_blocks, final_filter, head->total_len, 679 final_filter_len) < 0) 680 return -1; 681 682 free_block_list(head); 683 free_block_list(arg_blocks); 684 685 if (bpf_resolve_jumps(&labels, final_filter, final_filter_len) < 0) 686 return -1; 687 688 free_label_strings(&labels); 689 690 prog->filter = final_filter; 691 prog->len = final_filter_len; 692 return 0; 693 } 694 695 int flatten_block_list(struct filter_block *head, struct sock_filter *filter, 696 size_t index, size_t cap) 697 { 698 size_t _index = index; 699 700 struct filter_block *curr; 701 size_t i; 702 703 for (curr = head; curr; curr = curr->next) { 704 for (i = 0; i < curr->len; i++) { 705 if (_index >= cap) 706 return -1; 707 filter[_index++] = curr->instrs[i]; 708 } 709 } 710 return 0; 711 } 712 713 void free_block_list(struct filter_block *head) 714 { 715 struct filter_block *current, *prev; 716 717 current = head; 718 while (current) { 719 free(current->instrs); 720 prev = current; 721 current = current->next; 722 free(prev); 723 } 724 } 725