Commit 5af75d8d58d0f9f7b7c0515b35786b22892d5f12
1 parent
36c4f1b18c
Exists in
master
and in
4 other branches
audit: validate comparison operations, store them in sane form
Don't store the field->op in the messy (and very inconvenient for e.g. audit_comparator()) form; translate to dense set of values and do full validation of userland-submitted value while we are at it. ->audit_init_rule() and ->audit_match_rule() get new values now; in-tree instances updated. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Showing 5 changed files with 94 additions and 84 deletions Side-by-side Diff
include/linux/audit.h
... | ... | @@ -247,6 +247,18 @@ |
247 | 247 | #define AUDIT_GREATER_THAN_OR_EQUAL (AUDIT_GREATER_THAN|AUDIT_EQUAL) |
248 | 248 | #define AUDIT_OPERATORS (AUDIT_EQUAL|AUDIT_NOT_EQUAL|AUDIT_BIT_MASK) |
249 | 249 | |
250 | +enum { | |
251 | + Audit_equal, | |
252 | + Audit_not_equal, | |
253 | + Audit_bitmask, | |
254 | + Audit_bittest, | |
255 | + Audit_lt, | |
256 | + Audit_gt, | |
257 | + Audit_le, | |
258 | + Audit_ge, | |
259 | + Audit_bad | |
260 | +}; | |
261 | + | |
250 | 262 | /* Status symbols */ |
251 | 263 | /* Mask values */ |
252 | 264 | #define AUDIT_STATUS_ENABLED 0x0001 |
kernel/audit_tree.c
kernel/auditfilter.c
... | ... | @@ -252,7 +252,8 @@ |
252 | 252 | struct audit_field *f) |
253 | 253 | { |
254 | 254 | if (krule->listnr != AUDIT_FILTER_EXIT || |
255 | - krule->watch || krule->inode_f || krule->tree) | |
255 | + krule->watch || krule->inode_f || krule->tree || | |
256 | + (f->op != Audit_equal && f->op != Audit_not_equal)) | |
256 | 257 | return -EINVAL; |
257 | 258 | |
258 | 259 | krule->inode_f = f; |
... | ... | @@ -270,7 +271,7 @@ |
270 | 271 | |
271 | 272 | if (path[0] != '/' || path[len-1] == '/' || |
272 | 273 | krule->listnr != AUDIT_FILTER_EXIT || |
273 | - op & ~AUDIT_EQUAL || | |
274 | + op != Audit_equal || | |
274 | 275 | krule->inode_f || krule->watch || krule->tree) |
275 | 276 | return -EINVAL; |
276 | 277 | |
277 | 278 | |
... | ... | @@ -420,12 +421,32 @@ |
420 | 421 | return ERR_PTR(err); |
421 | 422 | } |
422 | 423 | |
424 | +static u32 audit_ops[] = | |
425 | +{ | |
426 | + [Audit_equal] = AUDIT_EQUAL, | |
427 | + [Audit_not_equal] = AUDIT_NOT_EQUAL, | |
428 | + [Audit_bitmask] = AUDIT_BIT_MASK, | |
429 | + [Audit_bittest] = AUDIT_BIT_TEST, | |
430 | + [Audit_lt] = AUDIT_LESS_THAN, | |
431 | + [Audit_gt] = AUDIT_GREATER_THAN, | |
432 | + [Audit_le] = AUDIT_LESS_THAN_OR_EQUAL, | |
433 | + [Audit_ge] = AUDIT_GREATER_THAN_OR_EQUAL, | |
434 | +}; | |
435 | + | |
436 | +static u32 audit_to_op(u32 op) | |
437 | +{ | |
438 | + u32 n; | |
439 | + for (n = Audit_equal; n < Audit_bad && audit_ops[n] != op; n++) | |
440 | + ; | |
441 | + return n; | |
442 | +} | |
443 | + | |
444 | + | |
423 | 445 | /* Translate struct audit_rule to kernel's rule respresentation. |
424 | 446 | * Exists for backward compatibility with userspace. */ |
425 | 447 | static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) |
426 | 448 | { |
427 | 449 | struct audit_entry *entry; |
428 | - struct audit_field *ino_f; | |
429 | 450 | int err = 0; |
430 | 451 | int i; |
431 | 452 | |
432 | 453 | |
433 | 454 | |
... | ... | @@ -435,12 +456,28 @@ |
435 | 456 | |
436 | 457 | for (i = 0; i < rule->field_count; i++) { |
437 | 458 | struct audit_field *f = &entry->rule.fields[i]; |
459 | + u32 n; | |
438 | 460 | |
439 | - f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); | |
461 | + n = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); | |
462 | + | |
463 | + /* Support for legacy operators where | |
464 | + * AUDIT_NEGATE bit signifies != and otherwise assumes == */ | |
465 | + if (n & AUDIT_NEGATE) | |
466 | + f->op = Audit_not_equal; | |
467 | + else if (!n) | |
468 | + f->op = Audit_equal; | |
469 | + else | |
470 | + f->op = audit_to_op(n); | |
471 | + | |
472 | + entry->rule.vers_ops = (n & AUDIT_OPERATORS) ? 2 : 1; | |
473 | + | |
440 | 474 | f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); |
441 | 475 | f->val = rule->values[i]; |
442 | 476 | |
443 | 477 | err = -EINVAL; |
478 | + if (f->op == Audit_bad) | |
479 | + goto exit_free; | |
480 | + | |
444 | 481 | switch(f->type) { |
445 | 482 | default: |
446 | 483 | goto exit_free; |
447 | 484 | |
... | ... | @@ -462,11 +499,8 @@ |
462 | 499 | case AUDIT_EXIT: |
463 | 500 | case AUDIT_SUCCESS: |
464 | 501 | /* bit ops are only useful on syscall args */ |
465 | - if (f->op == AUDIT_BIT_MASK || | |
466 | - f->op == AUDIT_BIT_TEST) { | |
467 | - err = -EINVAL; | |
502 | + if (f->op == Audit_bitmask || f->op == Audit_bittest) | |
468 | 503 | goto exit_free; |
469 | - } | |
470 | 504 | break; |
471 | 505 | case AUDIT_ARG0: |
472 | 506 | case AUDIT_ARG1: |
473 | 507 | |
... | ... | @@ -475,11 +509,8 @@ |
475 | 509 | break; |
476 | 510 | /* arch is only allowed to be = or != */ |
477 | 511 | case AUDIT_ARCH: |
478 | - if ((f->op != AUDIT_NOT_EQUAL) && (f->op != AUDIT_EQUAL) | |
479 | - && (f->op != AUDIT_NEGATE) && (f->op)) { | |
480 | - err = -EINVAL; | |
512 | + if (f->op != Audit_not_equal && f->op != Audit_equal) | |
481 | 513 | goto exit_free; |
482 | - } | |
483 | 514 | entry->rule.arch_f = f; |
484 | 515 | break; |
485 | 516 | case AUDIT_PERM: |
486 | 517 | |
... | ... | @@ -496,33 +527,10 @@ |
496 | 527 | goto exit_free; |
497 | 528 | break; |
498 | 529 | } |
499 | - | |
500 | - entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; | |
501 | - | |
502 | - /* Support for legacy operators where | |
503 | - * AUDIT_NEGATE bit signifies != and otherwise assumes == */ | |
504 | - if (f->op & AUDIT_NEGATE) | |
505 | - f->op = AUDIT_NOT_EQUAL; | |
506 | - else if (!f->op) | |
507 | - f->op = AUDIT_EQUAL; | |
508 | - else if (f->op == AUDIT_OPERATORS) { | |
509 | - err = -EINVAL; | |
510 | - goto exit_free; | |
511 | - } | |
512 | 530 | } |
513 | 531 | |
514 | - ino_f = entry->rule.inode_f; | |
515 | - if (ino_f) { | |
516 | - switch(ino_f->op) { | |
517 | - case AUDIT_NOT_EQUAL: | |
518 | - entry->rule.inode_f = NULL; | |
519 | - case AUDIT_EQUAL: | |
520 | - break; | |
521 | - default: | |
522 | - err = -EINVAL; | |
523 | - goto exit_free; | |
524 | - } | |
525 | - } | |
532 | + if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal) | |
533 | + entry->rule.inode_f = NULL; | |
526 | 534 | |
527 | 535 | exit_nofree: |
528 | 536 | return entry; |
... | ... | @@ -538,7 +546,6 @@ |
538 | 546 | { |
539 | 547 | int err = 0; |
540 | 548 | struct audit_entry *entry; |
541 | - struct audit_field *ino_f; | |
542 | 549 | void *bufp; |
543 | 550 | size_t remain = datasz - sizeof(struct audit_rule_data); |
544 | 551 | int i; |
545 | 552 | |
... | ... | @@ -554,11 +561,11 @@ |
554 | 561 | struct audit_field *f = &entry->rule.fields[i]; |
555 | 562 | |
556 | 563 | err = -EINVAL; |
557 | - if (!(data->fieldflags[i] & AUDIT_OPERATORS) || | |
558 | - data->fieldflags[i] & ~AUDIT_OPERATORS) | |
564 | + | |
565 | + f->op = audit_to_op(data->fieldflags[i]); | |
566 | + if (f->op == Audit_bad) | |
559 | 567 | goto exit_free; |
560 | 568 | |
561 | - f->op = data->fieldflags[i] & AUDIT_OPERATORS; | |
562 | 569 | f->type = data->fields[i]; |
563 | 570 | f->val = data->values[i]; |
564 | 571 | f->lsm_str = NULL; |
... | ... | @@ -670,18 +677,8 @@ |
670 | 677 | } |
671 | 678 | } |
672 | 679 | |
673 | - ino_f = entry->rule.inode_f; | |
674 | - if (ino_f) { | |
675 | - switch(ino_f->op) { | |
676 | - case AUDIT_NOT_EQUAL: | |
677 | - entry->rule.inode_f = NULL; | |
678 | - case AUDIT_EQUAL: | |
679 | - break; | |
680 | - default: | |
681 | - err = -EINVAL; | |
682 | - goto exit_free; | |
683 | - } | |
684 | - } | |
680 | + if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal) | |
681 | + entry->rule.inode_f = NULL; | |
685 | 682 | |
686 | 683 | exit_nofree: |
687 | 684 | return entry; |
688 | 685 | |
... | ... | @@ -721,10 +718,10 @@ |
721 | 718 | rule->fields[i] = krule->fields[i].type; |
722 | 719 | |
723 | 720 | if (krule->vers_ops == 1) { |
724 | - if (krule->fields[i].op & AUDIT_NOT_EQUAL) | |
721 | + if (krule->fields[i].op == Audit_not_equal) | |
725 | 722 | rule->fields[i] |= AUDIT_NEGATE; |
726 | 723 | } else { |
727 | - rule->fields[i] |= krule->fields[i].op; | |
724 | + rule->fields[i] |= audit_ops[krule->fields[i].op]; | |
728 | 725 | } |
729 | 726 | } |
730 | 727 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i]; |
... | ... | @@ -752,7 +749,7 @@ |
752 | 749 | struct audit_field *f = &krule->fields[i]; |
753 | 750 | |
754 | 751 | data->fields[i] = f->type; |
755 | - data->fieldflags[i] = f->op; | |
752 | + data->fieldflags[i] = audit_ops[f->op]; | |
756 | 753 | switch(f->type) { |
757 | 754 | case AUDIT_SUBJ_USER: |
758 | 755 | case AUDIT_SUBJ_ROLE: |
759 | 756 | |
760 | 757 | |
761 | 758 | |
762 | 759 | |
763 | 760 | |
764 | 761 | |
765 | 762 | |
766 | 763 | |
767 | 764 | |
768 | 765 | |
... | ... | @@ -1626,28 +1623,29 @@ |
1626 | 1623 | return err; |
1627 | 1624 | } |
1628 | 1625 | |
1629 | -int audit_comparator(const u32 left, const u32 op, const u32 right) | |
1626 | +int audit_comparator(u32 left, u32 op, u32 right) | |
1630 | 1627 | { |
1631 | 1628 | switch (op) { |
1632 | - case AUDIT_EQUAL: | |
1629 | + case Audit_equal: | |
1633 | 1630 | return (left == right); |
1634 | - case AUDIT_NOT_EQUAL: | |
1631 | + case Audit_not_equal: | |
1635 | 1632 | return (left != right); |
1636 | - case AUDIT_LESS_THAN: | |
1633 | + case Audit_lt: | |
1637 | 1634 | return (left < right); |
1638 | - case AUDIT_LESS_THAN_OR_EQUAL: | |
1635 | + case Audit_le: | |
1639 | 1636 | return (left <= right); |
1640 | - case AUDIT_GREATER_THAN: | |
1637 | + case Audit_gt: | |
1641 | 1638 | return (left > right); |
1642 | - case AUDIT_GREATER_THAN_OR_EQUAL: | |
1639 | + case Audit_ge: | |
1643 | 1640 | return (left >= right); |
1644 | - case AUDIT_BIT_MASK: | |
1641 | + case Audit_bitmask: | |
1645 | 1642 | return (left & right); |
1646 | - case AUDIT_BIT_TEST: | |
1643 | + case Audit_bittest: | |
1647 | 1644 | return ((left & right) == right); |
1645 | + default: | |
1646 | + BUG(); | |
1647 | + return 0; | |
1648 | 1648 | } |
1649 | - BUG(); | |
1650 | - return 0; | |
1651 | 1649 | } |
1652 | 1650 | |
1653 | 1651 | /* Compare given dentry name with last component in given path, |
security/selinux/ss/services.c
... | ... | @@ -2602,7 +2602,7 @@ |
2602 | 2602 | case AUDIT_OBJ_ROLE: |
2603 | 2603 | case AUDIT_OBJ_TYPE: |
2604 | 2604 | /* only 'equals' and 'not equals' fit user, role, and type */ |
2605 | - if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL) | |
2605 | + if (op != Audit_equal && op != Audit_not_equal) | |
2606 | 2606 | return -EINVAL; |
2607 | 2607 | break; |
2608 | 2608 | case AUDIT_SUBJ_SEN: |
2609 | 2609 | |
... | ... | @@ -2736,10 +2736,10 @@ |
2736 | 2736 | case AUDIT_SUBJ_USER: |
2737 | 2737 | case AUDIT_OBJ_USER: |
2738 | 2738 | switch (op) { |
2739 | - case AUDIT_EQUAL: | |
2739 | + case Audit_equal: | |
2740 | 2740 | match = (ctxt->user == rule->au_ctxt.user); |
2741 | 2741 | break; |
2742 | - case AUDIT_NOT_EQUAL: | |
2742 | + case Audit_not_equal: | |
2743 | 2743 | match = (ctxt->user != rule->au_ctxt.user); |
2744 | 2744 | break; |
2745 | 2745 | } |
2746 | 2746 | |
... | ... | @@ -2747,10 +2747,10 @@ |
2747 | 2747 | case AUDIT_SUBJ_ROLE: |
2748 | 2748 | case AUDIT_OBJ_ROLE: |
2749 | 2749 | switch (op) { |
2750 | - case AUDIT_EQUAL: | |
2750 | + case Audit_equal: | |
2751 | 2751 | match = (ctxt->role == rule->au_ctxt.role); |
2752 | 2752 | break; |
2753 | - case AUDIT_NOT_EQUAL: | |
2753 | + case Audit_not_equal: | |
2754 | 2754 | match = (ctxt->role != rule->au_ctxt.role); |
2755 | 2755 | break; |
2756 | 2756 | } |
2757 | 2757 | |
... | ... | @@ -2758,10 +2758,10 @@ |
2758 | 2758 | case AUDIT_SUBJ_TYPE: |
2759 | 2759 | case AUDIT_OBJ_TYPE: |
2760 | 2760 | switch (op) { |
2761 | - case AUDIT_EQUAL: | |
2761 | + case Audit_equal: | |
2762 | 2762 | match = (ctxt->type == rule->au_ctxt.type); |
2763 | 2763 | break; |
2764 | - case AUDIT_NOT_EQUAL: | |
2764 | + case Audit_not_equal: | |
2765 | 2765 | match = (ctxt->type != rule->au_ctxt.type); |
2766 | 2766 | break; |
2767 | 2767 | } |
2768 | 2768 | |
2769 | 2769 | |
2770 | 2770 | |
2771 | 2771 | |
2772 | 2772 | |
... | ... | @@ -2774,31 +2774,31 @@ |
2774 | 2774 | field == AUDIT_OBJ_LEV_LOW) ? |
2775 | 2775 | &ctxt->range.level[0] : &ctxt->range.level[1]); |
2776 | 2776 | switch (op) { |
2777 | - case AUDIT_EQUAL: | |
2777 | + case Audit_equal: | |
2778 | 2778 | match = mls_level_eq(&rule->au_ctxt.range.level[0], |
2779 | 2779 | level); |
2780 | 2780 | break; |
2781 | - case AUDIT_NOT_EQUAL: | |
2781 | + case Audit_not_equal: | |
2782 | 2782 | match = !mls_level_eq(&rule->au_ctxt.range.level[0], |
2783 | 2783 | level); |
2784 | 2784 | break; |
2785 | - case AUDIT_LESS_THAN: | |
2785 | + case Audit_lt: | |
2786 | 2786 | match = (mls_level_dom(&rule->au_ctxt.range.level[0], |
2787 | 2787 | level) && |
2788 | 2788 | !mls_level_eq(&rule->au_ctxt.range.level[0], |
2789 | 2789 | level)); |
2790 | 2790 | break; |
2791 | - case AUDIT_LESS_THAN_OR_EQUAL: | |
2791 | + case Audit_le: | |
2792 | 2792 | match = mls_level_dom(&rule->au_ctxt.range.level[0], |
2793 | 2793 | level); |
2794 | 2794 | break; |
2795 | - case AUDIT_GREATER_THAN: | |
2795 | + case Audit_gt: | |
2796 | 2796 | match = (mls_level_dom(level, |
2797 | 2797 | &rule->au_ctxt.range.level[0]) && |
2798 | 2798 | !mls_level_eq(level, |
2799 | 2799 | &rule->au_ctxt.range.level[0])); |
2800 | 2800 | break; |
2801 | - case AUDIT_GREATER_THAN_OR_EQUAL: | |
2801 | + case Audit_ge: | |
2802 | 2802 | match = mls_level_dom(level, |
2803 | 2803 | &rule->au_ctxt.range.level[0]); |
2804 | 2804 | break; |
security/smack/smack_lsm.c
... | ... | @@ -2492,7 +2492,7 @@ |
2492 | 2492 | if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER) |
2493 | 2493 | return -EINVAL; |
2494 | 2494 | |
2495 | - if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL) | |
2495 | + if (op != Audit_equal && op != Audit_not_equal) | |
2496 | 2496 | return -EINVAL; |
2497 | 2497 | |
2498 | 2498 | *rule = smk_import(rulestr, 0); |
2499 | 2499 | |
... | ... | @@ -2556,9 +2556,9 @@ |
2556 | 2556 | * both pointers will point to the same smack_known |
2557 | 2557 | * label. |
2558 | 2558 | */ |
2559 | - if (op == AUDIT_EQUAL) | |
2559 | + if (op == Audit_equal) | |
2560 | 2560 | return (rule == smack); |
2561 | - if (op == AUDIT_NOT_EQUAL) | |
2561 | + if (op == Audit_not_equal) | |
2562 | 2562 | return (rule != smack); |
2563 | 2563 | |
2564 | 2564 | return 0; |