Skip to content

Commit 5af75d8

Browse files
author
Al Viro
committed
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>
1 parent 36c4f1b commit 5af75d8

File tree

5 files changed

+94
-84
lines changed

5 files changed

+94
-84
lines changed

include/linux/audit.h

+12
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,18 @@
247247
#define AUDIT_GREATER_THAN_OR_EQUAL (AUDIT_GREATER_THAN|AUDIT_EQUAL)
248248
#define AUDIT_OPERATORS (AUDIT_EQUAL|AUDIT_NOT_EQUAL|AUDIT_BIT_MASK)
249249

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+
250262
/* Status symbols */
251263
/* Mask values */
252264
#define AUDIT_STATUS_ENABLED 0x0001

kernel/audit_tree.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ int audit_make_tree(struct audit_krule *rule, char *pathname, u32 op)
618618

619619
if (pathname[0] != '/' ||
620620
rule->listnr != AUDIT_FILTER_EXIT ||
621-
op & ~AUDIT_EQUAL ||
621+
op != Audit_equal ||
622622
rule->inode_f || rule->watch || rule->tree)
623623
return -EINVAL;
624624
rule->tree = alloc_tree(pathname);

kernel/auditfilter.c

+65-67
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ static inline int audit_to_inode(struct audit_krule *krule,
252252
struct audit_field *f)
253253
{
254254
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))
256257
return -EINVAL;
257258

258259
krule->inode_f = f;
@@ -270,7 +271,7 @@ static int audit_to_watch(struct audit_krule *krule, char *path, int len,
270271

271272
if (path[0] != '/' || path[len-1] == '/' ||
272273
krule->listnr != AUDIT_FILTER_EXIT ||
273-
op & ~AUDIT_EQUAL ||
274+
op != Audit_equal ||
274275
krule->inode_f || krule->watch || krule->tree)
275276
return -EINVAL;
276277

@@ -420,12 +421,32 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
420421
return ERR_PTR(err);
421422
}
422423

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+
423445
/* Translate struct audit_rule to kernel's rule respresentation.
424446
* Exists for backward compatibility with userspace. */
425447
static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
426448
{
427449
struct audit_entry *entry;
428-
struct audit_field *ino_f;
429450
int err = 0;
430451
int i;
431452

@@ -435,12 +456,28 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
435456

436457
for (i = 0; i < rule->field_count; i++) {
437458
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;
438473

439-
f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
440474
f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
441475
f->val = rule->values[i];
442476

443477
err = -EINVAL;
478+
if (f->op == Audit_bad)
479+
goto exit_free;
480+
444481
switch(f->type) {
445482
default:
446483
goto exit_free;
@@ -462,11 +499,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
462499
case AUDIT_EXIT:
463500
case AUDIT_SUCCESS:
464501
/* 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)
468503
goto exit_free;
469-
}
470504
break;
471505
case AUDIT_ARG0:
472506
case AUDIT_ARG1:
@@ -475,11 +509,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
475509
break;
476510
/* arch is only allowed to be = or != */
477511
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)
481513
goto exit_free;
482-
}
483514
entry->rule.arch_f = f;
484515
break;
485516
case AUDIT_PERM:
@@ -496,33 +527,10 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
496527
goto exit_free;
497528
break;
498529
}
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-
}
512530
}
513531

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;
526534

527535
exit_nofree:
528536
return entry;
@@ -538,7 +546,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
538546
{
539547
int err = 0;
540548
struct audit_entry *entry;
541-
struct audit_field *ino_f;
542549
void *bufp;
543550
size_t remain = datasz - sizeof(struct audit_rule_data);
544551
int i;
@@ -554,11 +561,11 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
554561
struct audit_field *f = &entry->rule.fields[i];
555562

556563
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)
559567
goto exit_free;
560568

561-
f->op = data->fieldflags[i] & AUDIT_OPERATORS;
562569
f->type = data->fields[i];
563570
f->val = data->values[i];
564571
f->lsm_str = NULL;
@@ -670,18 +677,8 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
670677
}
671678
}
672679

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;
685682

686683
exit_nofree:
687684
return entry;
@@ -721,10 +718,10 @@ static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
721718
rule->fields[i] = krule->fields[i].type;
722719

723720
if (krule->vers_ops == 1) {
724-
if (krule->fields[i].op & AUDIT_NOT_EQUAL)
721+
if (krule->fields[i].op == Audit_not_equal)
725722
rule->fields[i] |= AUDIT_NEGATE;
726723
} else {
727-
rule->fields[i] |= krule->fields[i].op;
724+
rule->fields[i] |= audit_ops[krule->fields[i].op];
728725
}
729726
}
730727
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)
752749
struct audit_field *f = &krule->fields[i];
753750

754751
data->fields[i] = f->type;
755-
data->fieldflags[i] = f->op;
752+
data->fieldflags[i] = audit_ops[f->op];
756753
switch(f->type) {
757754
case AUDIT_SUBJ_USER:
758755
case AUDIT_SUBJ_ROLE:
@@ -1626,28 +1623,29 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
16261623
return err;
16271624
}
16281625

1629-
int audit_comparator(const u32 left, const u32 op, const u32 right)
1626+
int audit_comparator(u32 left, u32 op, u32 right)
16301627
{
16311628
switch (op) {
1632-
case AUDIT_EQUAL:
1629+
case Audit_equal:
16331630
return (left == right);
1634-
case AUDIT_NOT_EQUAL:
1631+
case Audit_not_equal:
16351632
return (left != right);
1636-
case AUDIT_LESS_THAN:
1633+
case Audit_lt:
16371634
return (left < right);
1638-
case AUDIT_LESS_THAN_OR_EQUAL:
1635+
case Audit_le:
16391636
return (left <= right);
1640-
case AUDIT_GREATER_THAN:
1637+
case Audit_gt:
16411638
return (left > right);
1642-
case AUDIT_GREATER_THAN_OR_EQUAL:
1639+
case Audit_ge:
16431640
return (left >= right);
1644-
case AUDIT_BIT_MASK:
1641+
case Audit_bitmask:
16451642
return (left & right);
1646-
case AUDIT_BIT_TEST:
1643+
case Audit_bittest:
16471644
return ((left & right) == right);
1645+
default:
1646+
BUG();
1647+
return 0;
16481648
}
1649-
BUG();
1650-
return 0;
16511649
}
16521650

16531651
/* Compare given dentry name with last component in given path,

security/selinux/ss/services.c

+13-13
Original file line numberDiff line numberDiff line change
@@ -2602,7 +2602,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
26022602
case AUDIT_OBJ_ROLE:
26032603
case AUDIT_OBJ_TYPE:
26042604
/* 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)
26062606
return -EINVAL;
26072607
break;
26082608
case AUDIT_SUBJ_SEN:
@@ -2736,32 +2736,32 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
27362736
case AUDIT_SUBJ_USER:
27372737
case AUDIT_OBJ_USER:
27382738
switch (op) {
2739-
case AUDIT_EQUAL:
2739+
case Audit_equal:
27402740
match = (ctxt->user == rule->au_ctxt.user);
27412741
break;
2742-
case AUDIT_NOT_EQUAL:
2742+
case Audit_not_equal:
27432743
match = (ctxt->user != rule->au_ctxt.user);
27442744
break;
27452745
}
27462746
break;
27472747
case AUDIT_SUBJ_ROLE:
27482748
case AUDIT_OBJ_ROLE:
27492749
switch (op) {
2750-
case AUDIT_EQUAL:
2750+
case Audit_equal:
27512751
match = (ctxt->role == rule->au_ctxt.role);
27522752
break;
2753-
case AUDIT_NOT_EQUAL:
2753+
case Audit_not_equal:
27542754
match = (ctxt->role != rule->au_ctxt.role);
27552755
break;
27562756
}
27572757
break;
27582758
case AUDIT_SUBJ_TYPE:
27592759
case AUDIT_OBJ_TYPE:
27602760
switch (op) {
2761-
case AUDIT_EQUAL:
2761+
case Audit_equal:
27622762
match = (ctxt->type == rule->au_ctxt.type);
27632763
break;
2764-
case AUDIT_NOT_EQUAL:
2764+
case Audit_not_equal:
27652765
match = (ctxt->type != rule->au_ctxt.type);
27662766
break;
27672767
}
@@ -2774,31 +2774,31 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
27742774
field == AUDIT_OBJ_LEV_LOW) ?
27752775
&ctxt->range.level[0] : &ctxt->range.level[1]);
27762776
switch (op) {
2777-
case AUDIT_EQUAL:
2777+
case Audit_equal:
27782778
match = mls_level_eq(&rule->au_ctxt.range.level[0],
27792779
level);
27802780
break;
2781-
case AUDIT_NOT_EQUAL:
2781+
case Audit_not_equal:
27822782
match = !mls_level_eq(&rule->au_ctxt.range.level[0],
27832783
level);
27842784
break;
2785-
case AUDIT_LESS_THAN:
2785+
case Audit_lt:
27862786
match = (mls_level_dom(&rule->au_ctxt.range.level[0],
27872787
level) &&
27882788
!mls_level_eq(&rule->au_ctxt.range.level[0],
27892789
level));
27902790
break;
2791-
case AUDIT_LESS_THAN_OR_EQUAL:
2791+
case Audit_le:
27922792
match = mls_level_dom(&rule->au_ctxt.range.level[0],
27932793
level);
27942794
break;
2795-
case AUDIT_GREATER_THAN:
2795+
case Audit_gt:
27962796
match = (mls_level_dom(level,
27972797
&rule->au_ctxt.range.level[0]) &&
27982798
!mls_level_eq(level,
27992799
&rule->au_ctxt.range.level[0]));
28002800
break;
2801-
case AUDIT_GREATER_THAN_OR_EQUAL:
2801+
case Audit_ge:
28022802
match = mls_level_dom(level,
28032803
&rule->au_ctxt.range.level[0]);
28042804
break;

security/smack/smack_lsm.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -2492,7 +2492,7 @@ static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
24922492
if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER)
24932493
return -EINVAL;
24942494

2495-
if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL)
2495+
if (op != Audit_equal && op != Audit_not_equal)
24962496
return -EINVAL;
24972497

24982498
*rule = smk_import(rulestr, 0);
@@ -2556,9 +2556,9 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule,
25562556
* both pointers will point to the same smack_known
25572557
* label.
25582558
*/
2559-
if (op == AUDIT_EQUAL)
2559+
if (op == Audit_equal)
25602560
return (rule == smack);
2561-
if (op == AUDIT_NOT_EQUAL)
2561+
if (op == Audit_not_equal)
25622562
return (rule != smack);
25632563

25642564
return 0;

0 commit comments

Comments
 (0)