aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2008-12-16 05:59:26 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2009-01-04 15:14:42 -0500
commit5af75d8d58d0f9f7b7c0515b35786b22892d5f12 (patch)
tree65707c5309133a33140c39145ae91b7c1679a877
parent36c4f1b18c8a7d0adb4085e7f531860b837bb6b0 (diff)
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>
-rw-r--r--include/linux/audit.h12
-rw-r--r--kernel/audit_tree.c2
-rw-r--r--kernel/auditfilter.c132
-rw-r--r--security/selinux/ss/services.c26
-rw-r--r--security/smack/smack_lsm.c6
5 files changed, 94 insertions, 84 deletions
diff --git a/include/linux/audit.h b/include/linux/audit.h
index cc71fdb56ae2..67e5dbfc2961 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -247,6 +247,18 @@
247#define AUDIT_GREATER_THAN_OR_EQUAL (AUDIT_GREATER_THAN|AUDIT_EQUAL) 247#define AUDIT_GREATER_THAN_OR_EQUAL (AUDIT_GREATER_THAN|AUDIT_EQUAL)
248#define AUDIT_OPERATORS (AUDIT_EQUAL|AUDIT_NOT_EQUAL|AUDIT_BIT_MASK) 248#define AUDIT_OPERATORS (AUDIT_EQUAL|AUDIT_NOT_EQUAL|AUDIT_BIT_MASK)
249 249
250enum {
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/* Status symbols */ 262/* Status symbols */
251 /* Mask values */ 263 /* Mask values */
252#define AUDIT_STATUS_ENABLED 0x0001 264#define AUDIT_STATUS_ENABLED 0x0001
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
index 48bddad2a3dc..8ad9545b8db9 100644
--- a/kernel/audit_tree.c
+++ b/kernel/audit_tree.c
@@ -618,7 +618,7 @@ int audit_make_tree(struct audit_krule *rule, char *pathname, u32 op)
618 618
619 if (pathname[0] != '/' || 619 if (pathname[0] != '/' ||
620 rule->listnr != AUDIT_FILTER_EXIT || 620 rule->listnr != AUDIT_FILTER_EXIT ||
621 op & ~AUDIT_EQUAL || 621 op != Audit_equal ||
622 rule->inode_f || rule->watch || rule->tree) 622 rule->inode_f || rule->watch || rule->tree)
623 return -EINVAL; 623 return -EINVAL;
624 rule->tree = alloc_tree(pathname); 624 rule->tree = alloc_tree(pathname);
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index e6e3829cadd1..fbf24d121d97 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -252,7 +252,8 @@ static inline int audit_to_inode(struct audit_krule *krule,
252 struct audit_field *f) 252 struct audit_field *f)
253{ 253{
254 if (krule->listnr != AUDIT_FILTER_EXIT || 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 return -EINVAL; 257 return -EINVAL;
257 258
258 krule->inode_f = f; 259 krule->inode_f = f;
@@ -270,7 +271,7 @@ static int audit_to_watch(struct audit_krule *krule, char *path, int len,
270 271
271 if (path[0] != '/' || path[len-1] == '/' || 272 if (path[0] != '/' || path[len-1] == '/' ||
272 krule->listnr != AUDIT_FILTER_EXIT || 273 krule->listnr != AUDIT_FILTER_EXIT ||
273 op & ~AUDIT_EQUAL || 274 op != Audit_equal ||
274 krule->inode_f || krule->watch || krule->tree) 275 krule->inode_f || krule->watch || krule->tree)
275 return -EINVAL; 276 return -EINVAL;
276 277
@@ -420,12 +421,32 @@ exit_err:
420 return ERR_PTR(err); 421 return ERR_PTR(err);
421} 422}
422 423
424static 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
436static 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/* Translate struct audit_rule to kernel's rule respresentation. 445/* Translate struct audit_rule to kernel's rule respresentation.
424 * Exists for backward compatibility with userspace. */ 446 * Exists for backward compatibility with userspace. */
425static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) 447static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
426{ 448{
427 struct audit_entry *entry; 449 struct audit_entry *entry;
428 struct audit_field *ino_f;
429 int err = 0; 450 int err = 0;
430 int i; 451 int i;
431 452
@@ -435,12 +456,28 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
435 456
436 for (i = 0; i < rule->field_count; i++) { 457 for (i = 0; i < rule->field_count; i++) {
437 struct audit_field *f = &entry->rule.fields[i]; 458 struct audit_field *f = &entry->rule.fields[i];
459 u32 n;
460
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;
438 473
439 f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
440 f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); 474 f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
441 f->val = rule->values[i]; 475 f->val = rule->values[i];
442 476
443 err = -EINVAL; 477 err = -EINVAL;
478 if (f->op == Audit_bad)
479 goto exit_free;
480
444 switch(f->type) { 481 switch(f->type) {
445 default: 482 default:
446 goto exit_free; 483 goto exit_free;
@@ -462,11 +499,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
462 case AUDIT_EXIT: 499 case AUDIT_EXIT:
463 case AUDIT_SUCCESS: 500 case AUDIT_SUCCESS:
464 /* bit ops are only useful on syscall args */ 501 /* bit ops are only useful on syscall args */
465 if (f->op == AUDIT_BIT_MASK || 502 if (f->op == Audit_bitmask || f->op == Audit_bittest)
466 f->op == AUDIT_BIT_TEST) {
467 err = -EINVAL;
468 goto exit_free; 503 goto exit_free;
469 }
470 break; 504 break;
471 case AUDIT_ARG0: 505 case AUDIT_ARG0:
472 case AUDIT_ARG1: 506 case AUDIT_ARG1:
@@ -475,11 +509,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
475 break; 509 break;
476 /* arch is only allowed to be = or != */ 510 /* arch is only allowed to be = or != */
477 case AUDIT_ARCH: 511 case AUDIT_ARCH:
478 if ((f->op != AUDIT_NOT_EQUAL) && (f->op != AUDIT_EQUAL) 512 if (f->op != Audit_not_equal && f->op != Audit_equal)
479 && (f->op != AUDIT_NEGATE) && (f->op)) {
480 err = -EINVAL;
481 goto exit_free; 513 goto exit_free;
482 }
483 entry->rule.arch_f = f; 514 entry->rule.arch_f = f;
484 break; 515 break;
485 case AUDIT_PERM: 516 case AUDIT_PERM:
@@ -496,33 +527,10 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
496 goto exit_free; 527 goto exit_free;
497 break; 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; 532 if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal)
515 if (ino_f) { 533 entry->rule.inode_f = NULL;
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 }
526 534
527exit_nofree: 535exit_nofree:
528 return entry; 536 return entry;
@@ -538,7 +546,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
538{ 546{
539 int err = 0; 547 int err = 0;
540 struct audit_entry *entry; 548 struct audit_entry *entry;
541 struct audit_field *ino_f;
542 void *bufp; 549 void *bufp;
543 size_t remain = datasz - sizeof(struct audit_rule_data); 550 size_t remain = datasz - sizeof(struct audit_rule_data);
544 int i; 551 int i;
@@ -554,11 +561,11 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
554 struct audit_field *f = &entry->rule.fields[i]; 561 struct audit_field *f = &entry->rule.fields[i];
555 562
556 err = -EINVAL; 563 err = -EINVAL;
557 if (!(data->fieldflags[i] & AUDIT_OPERATORS) || 564
558 data->fieldflags[i] & ~AUDIT_OPERATORS) 565 f->op = audit_to_op(data->fieldflags[i]);
566 if (f->op == Audit_bad)
559 goto exit_free; 567 goto exit_free;
560 568
561 f->op = data->fieldflags[i] & AUDIT_OPERATORS;
562 f->type = data->fields[i]; 569 f->type = data->fields[i];
563 f->val = data->values[i]; 570 f->val = data->values[i];
564 f->lsm_str = NULL; 571 f->lsm_str = NULL;
@@ -670,18 +677,8 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
670 } 677 }
671 } 678 }
672 679
673 ino_f = entry->rule.inode_f; 680 if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal)
674 if (ino_f) { 681 entry->rule.inode_f = NULL;
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 }
685 682
686exit_nofree: 683exit_nofree:
687 return entry; 684 return entry;
@@ -721,10 +718,10 @@ static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
721 rule->fields[i] = krule->fields[i].type; 718 rule->fields[i] = krule->fields[i].type;
722 719
723 if (krule->vers_ops == 1) { 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 rule->fields[i] |= AUDIT_NEGATE; 722 rule->fields[i] |= AUDIT_NEGATE;
726 } else { 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 for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i]; 727 for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i];
@@ -752,7 +749,7 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
752 struct audit_field *f = &krule->fields[i]; 749 struct audit_field *f = &krule->fields[i];
753 750
754 data->fields[i] = f->type; 751 data->fields[i] = f->type;
755 data->fieldflags[i] = f->op; 752 data->fieldflags[i] = audit_ops[f->op];
756 switch(f->type) { 753 switch(f->type) {
757 case AUDIT_SUBJ_USER: 754 case AUDIT_SUBJ_USER:
758 case AUDIT_SUBJ_ROLE: 755 case AUDIT_SUBJ_ROLE:
@@ -1626,28 +1623,29 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
1626 return err; 1623 return err;
1627} 1624}
1628 1625
1629int audit_comparator(const u32 left, const u32 op, const u32 right) 1626int audit_comparator(u32 left, u32 op, u32 right)
1630{ 1627{
1631 switch (op) { 1628 switch (op) {
1632 case AUDIT_EQUAL: 1629 case Audit_equal:
1633 return (left == right); 1630 return (left == right);
1634 case AUDIT_NOT_EQUAL: 1631 case Audit_not_equal:
1635 return (left != right); 1632 return (left != right);
1636 case AUDIT_LESS_THAN: 1633 case Audit_lt:
1637 return (left < right); 1634 return (left < right);
1638 case AUDIT_LESS_THAN_OR_EQUAL: 1635 case Audit_le:
1639 return (left <= right); 1636 return (left <= right);
1640 case AUDIT_GREATER_THAN: 1637 case Audit_gt:
1641 return (left > right); 1638 return (left > right);
1642 case AUDIT_GREATER_THAN_OR_EQUAL: 1639 case Audit_ge:
1643 return (left >= right); 1640 return (left >= right);
1644 case AUDIT_BIT_MASK: 1641 case Audit_bitmask:
1645 return (left & right); 1642 return (left & right);
1646 case AUDIT_BIT_TEST: 1643 case Audit_bittest:
1647 return ((left & right) == right); 1644 return ((left & right) == right);
1645 default:
1646 BUG();
1647 return 0;
1648 } 1648 }
1649 BUG();
1650 return 0;
1651} 1649}
1652 1650
1653/* Compare given dentry name with last component in given path, 1651/* Compare given dentry name with last component in given path,
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 343c8ab14af0..c65e4fe4a0f1 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2602,7 +2602,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
2602 case AUDIT_OBJ_ROLE: 2602 case AUDIT_OBJ_ROLE:
2603 case AUDIT_OBJ_TYPE: 2603 case AUDIT_OBJ_TYPE:
2604 /* only 'equals' and 'not equals' fit user, role, and type */ 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 return -EINVAL; 2606 return -EINVAL;
2607 break; 2607 break;
2608 case AUDIT_SUBJ_SEN: 2608 case AUDIT_SUBJ_SEN:
@@ -2736,10 +2736,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
2736 case AUDIT_SUBJ_USER: 2736 case AUDIT_SUBJ_USER:
2737 case AUDIT_OBJ_USER: 2737 case AUDIT_OBJ_USER:
2738 switch (op) { 2738 switch (op) {
2739 case AUDIT_EQUAL: 2739 case Audit_equal:
2740 match = (ctxt->user == rule->au_ctxt.user); 2740 match = (ctxt->user == rule->au_ctxt.user);
2741 break; 2741 break;
2742 case AUDIT_NOT_EQUAL: 2742 case Audit_not_equal:
2743 match = (ctxt->user != rule->au_ctxt.user); 2743 match = (ctxt->user != rule->au_ctxt.user);
2744 break; 2744 break;
2745 } 2745 }
@@ -2747,10 +2747,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
2747 case AUDIT_SUBJ_ROLE: 2747 case AUDIT_SUBJ_ROLE:
2748 case AUDIT_OBJ_ROLE: 2748 case AUDIT_OBJ_ROLE:
2749 switch (op) { 2749 switch (op) {
2750 case AUDIT_EQUAL: 2750 case Audit_equal:
2751 match = (ctxt->role == rule->au_ctxt.role); 2751 match = (ctxt->role == rule->au_ctxt.role);
2752 break; 2752 break;
2753 case AUDIT_NOT_EQUAL: 2753 case Audit_not_equal:
2754 match = (ctxt->role != rule->au_ctxt.role); 2754 match = (ctxt->role != rule->au_ctxt.role);
2755 break; 2755 break;
2756 } 2756 }
@@ -2758,10 +2758,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
2758 case AUDIT_SUBJ_TYPE: 2758 case AUDIT_SUBJ_TYPE:
2759 case AUDIT_OBJ_TYPE: 2759 case AUDIT_OBJ_TYPE:
2760 switch (op) { 2760 switch (op) {
2761 case AUDIT_EQUAL: 2761 case Audit_equal:
2762 match = (ctxt->type == rule->au_ctxt.type); 2762 match = (ctxt->type == rule->au_ctxt.type);
2763 break; 2763 break;
2764 case AUDIT_NOT_EQUAL: 2764 case Audit_not_equal:
2765 match = (ctxt->type != rule->au_ctxt.type); 2765 match = (ctxt->type != rule->au_ctxt.type);
2766 break; 2766 break;
2767 } 2767 }
@@ -2774,31 +2774,31 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
2774 field == AUDIT_OBJ_LEV_LOW) ? 2774 field == AUDIT_OBJ_LEV_LOW) ?
2775 &ctxt->range.level[0] : &ctxt->range.level[1]); 2775 &ctxt->range.level[0] : &ctxt->range.level[1]);
2776 switch (op) { 2776 switch (op) {
2777 case AUDIT_EQUAL: 2777 case Audit_equal:
2778 match = mls_level_eq(&rule->au_ctxt.range.level[0], 2778 match = mls_level_eq(&rule->au_ctxt.range.level[0],
2779 level); 2779 level);
2780 break; 2780 break;
2781 case AUDIT_NOT_EQUAL: 2781 case Audit_not_equal:
2782 match = !mls_level_eq(&rule->au_ctxt.range.level[0], 2782 match = !mls_level_eq(&rule->au_ctxt.range.level[0],
2783 level); 2783 level);
2784 break; 2784 break;
2785 case AUDIT_LESS_THAN: 2785 case Audit_lt:
2786 match = (mls_level_dom(&rule->au_ctxt.range.level[0], 2786 match = (mls_level_dom(&rule->au_ctxt.range.level[0],
2787 level) && 2787 level) &&
2788 !mls_level_eq(&rule->au_ctxt.range.level[0], 2788 !mls_level_eq(&rule->au_ctxt.range.level[0],
2789 level)); 2789 level));
2790 break; 2790 break;
2791 case AUDIT_LESS_THAN_OR_EQUAL: 2791 case Audit_le:
2792 match = mls_level_dom(&rule->au_ctxt.range.level[0], 2792 match = mls_level_dom(&rule->au_ctxt.range.level[0],
2793 level); 2793 level);
2794 break; 2794 break;
2795 case AUDIT_GREATER_THAN: 2795 case Audit_gt:
2796 match = (mls_level_dom(level, 2796 match = (mls_level_dom(level,
2797 &rule->au_ctxt.range.level[0]) && 2797 &rule->au_ctxt.range.level[0]) &&
2798 !mls_level_eq(level, 2798 !mls_level_eq(level,
2799 &rule->au_ctxt.range.level[0])); 2799 &rule->au_ctxt.range.level[0]));
2800 break; 2800 break;
2801 case AUDIT_GREATER_THAN_OR_EQUAL: 2801 case Audit_ge:
2802 match = mls_level_dom(level, 2802 match = mls_level_dom(level,
2803 &rule->au_ctxt.range.level[0]); 2803 &rule->au_ctxt.range.level[0]);
2804 break; 2804 break;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 1b5551dfc1f7..848212fd4845 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -2492,7 +2492,7 @@ static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
2492 if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER) 2492 if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER)
2493 return -EINVAL; 2493 return -EINVAL;
2494 2494
2495 if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL) 2495 if (op != Audit_equal && op != Audit_not_equal)
2496 return -EINVAL; 2496 return -EINVAL;
2497 2497
2498 *rule = smk_import(rulestr, 0); 2498 *rule = smk_import(rulestr, 0);
@@ -2556,9 +2556,9 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule,
2556 * both pointers will point to the same smack_known 2556 * both pointers will point to the same smack_known
2557 * label. 2557 * label.
2558 */ 2558 */
2559 if (op == AUDIT_EQUAL) 2559 if (op == Audit_equal)
2560 return (rule == smack); 2560 return (rule == smack);
2561 if (op == AUDIT_NOT_EQUAL) 2561 if (op == Audit_not_equal)
2562 return (rule != smack); 2562 return (rule != smack);
2563 2563
2564 return 0; 2564 return 0;