Commit 5af75d8d58d0f9f7b7c0515b35786b22892d5f12

Authored by Al Viro
1 parent 36c4f1b18c

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
... ... @@ -618,7 +618,7 @@
618 618  
619 619 if (pathname[0] != '/' ||
620 620 rule->listnr != AUDIT_FILTER_EXIT ||
621   - op & ~AUDIT_EQUAL ||
  621 + op != Audit_equal ||
622 622 rule->inode_f || rule->watch || rule->tree)
623 623 return -EINVAL;
624 624 rule->tree = alloc_tree(pathname);
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;