diff options
author | Dustin Kirkland <dustin.kirkland@us.ibm.com> | 2005-11-03 10:41:46 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2006-03-20 14:08:53 -0500 |
commit | b63862f46547487388e582e8ac9083830d34f058 (patch) | |
tree | 5aa0173c02535fdd9dfe302e9c8a8a225091ed56 /kernel | |
parent | b0dd25a8263dde3c30b0d7d72a8bd92d7ba0e3f5 (diff) |
[PATCH] Filter rule comparators
Currently, audit only supports the "=" and "!=" operators in the -F
filter rules.
This patch reworks the support for "=" and "!=", and adds support
for ">", ">=", "<", and "<=".
This turned out to be a pretty clean, and simply process. I ended up
using the high order bits of the "field", as suggested by Steve and Amy.
This allowed for no changes whatsoever to the netlink communications.
See the documentation within the patch in the include/linux/audit.h
area, where there is a table that explains the reasoning of the bitmask
assignments clearly.
The patch adds a new function, audit_comparator(left, op, right).
This function will perform the specified comparison (op, which defaults
to "==" for backward compatibility) between two values (left and right).
If the negate bit is on, it will negate whatever that result was. This
value is returned.
Signed-off-by: Dustin Kirkland <dustin.kirkland@us.ibm.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/auditsc.c | 117 |
1 files changed, 75 insertions, 42 deletions
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 51a4f58a4d81..95076fa12202 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Handles all system-call specific auditing features. | 2 | * Handles all system-call specific auditing features. |
3 | * | 3 | * |
4 | * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. | 4 | * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. |
5 | * Copyright (C) 2005 IBM Corporation | ||
5 | * All Rights Reserved. | 6 | * All Rights Reserved. |
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
@@ -27,6 +28,9 @@ | |||
27 | * this file -- see entry.S) is based on a GPL'd patch written by | 28 | * this file -- see entry.S) is based on a GPL'd patch written by |
28 | * okir@suse.de and Copyright 2003 SuSE Linux AG. | 29 | * okir@suse.de and Copyright 2003 SuSE Linux AG. |
29 | * | 30 | * |
31 | * The support of additional filter rules compares (>, <, >=, <=) was | ||
32 | * added by Dustin Kirkland <dustin.kirkland@us.ibm.com>, 2005. | ||
33 | * | ||
30 | */ | 34 | */ |
31 | 35 | ||
32 | #include <linux/init.h> | 36 | #include <linux/init.h> |
@@ -252,6 +256,7 @@ static inline int audit_add_rule(struct audit_rule *rule, | |||
252 | struct list_head *list) | 256 | struct list_head *list) |
253 | { | 257 | { |
254 | struct audit_entry *entry; | 258 | struct audit_entry *entry; |
259 | int i; | ||
255 | 260 | ||
256 | /* Do not use the _rcu iterator here, since this is the only | 261 | /* Do not use the _rcu iterator here, since this is the only |
257 | * addition routine. */ | 262 | * addition routine. */ |
@@ -261,6 +266,16 @@ static inline int audit_add_rule(struct audit_rule *rule, | |||
261 | } | 266 | } |
262 | } | 267 | } |
263 | 268 | ||
269 | for (i = 0; i < rule->field_count; i++) { | ||
270 | if (rule->fields[i] & AUDIT_UNUSED_BITS) | ||
271 | return -EINVAL; | ||
272 | if ( rule->fields[i] & AUDIT_NEGATE ) | ||
273 | rule->fields[i] |= AUDIT_NOT_EQUAL; | ||
274 | else if ( (rule->fields[i] & AUDIT_OPERATORS) == 0 ) | ||
275 | rule->fields[i] |= AUDIT_EQUAL; | ||
276 | rule->fields[i] &= (~AUDIT_NEGATE); | ||
277 | } | ||
278 | |||
264 | if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) | 279 | if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) |
265 | return -ENOMEM; | 280 | return -ENOMEM; |
266 | if (audit_copy_rule(&entry->rule, rule)) { | 281 | if (audit_copy_rule(&entry->rule, rule)) { |
@@ -394,6 +409,26 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
394 | return err; | 409 | return err; |
395 | } | 410 | } |
396 | 411 | ||
412 | static int audit_comparator(const u32 left, const u32 op, const u32 right) | ||
413 | { | ||
414 | switch (op) { | ||
415 | case AUDIT_EQUAL: | ||
416 | return (left == right); | ||
417 | case AUDIT_NOT_EQUAL: | ||
418 | return (left != right); | ||
419 | case AUDIT_LESS_THAN: | ||
420 | return (left < right); | ||
421 | case AUDIT_LESS_THAN_OR_EQUAL: | ||
422 | return (left <= right); | ||
423 | case AUDIT_GREATER_THAN: | ||
424 | return (left > right); | ||
425 | case AUDIT_GREATER_THAN_OR_EQUAL: | ||
426 | return (left >= right); | ||
427 | default: | ||
428 | return -EINVAL; | ||
429 | } | ||
430 | } | ||
431 | |||
397 | /* Compare a task_struct with an audit_rule. Return 1 on match, 0 | 432 | /* Compare a task_struct with an audit_rule. Return 1 on match, 0 |
398 | * otherwise. */ | 433 | * otherwise. */ |
399 | static int audit_filter_rules(struct task_struct *tsk, | 434 | static int audit_filter_rules(struct task_struct *tsk, |
@@ -404,62 +439,63 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
404 | int i, j; | 439 | int i, j; |
405 | 440 | ||
406 | for (i = 0; i < rule->field_count; i++) { | 441 | for (i = 0; i < rule->field_count; i++) { |
407 | u32 field = rule->fields[i] & ~AUDIT_NEGATE; | 442 | u32 field = rule->fields[i] & ~AUDIT_OPERATORS; |
443 | u32 op = rule->fields[i] & AUDIT_OPERATORS; | ||
408 | u32 value = rule->values[i]; | 444 | u32 value = rule->values[i]; |
409 | int result = 0; | 445 | int result = 0; |
410 | 446 | ||
411 | switch (field) { | 447 | switch (field) { |
412 | case AUDIT_PID: | 448 | case AUDIT_PID: |
413 | result = (tsk->pid == value); | 449 | result = audit_comparator(tsk->pid, op, value); |
414 | break; | 450 | break; |
415 | case AUDIT_UID: | 451 | case AUDIT_UID: |
416 | result = (tsk->uid == value); | 452 | result = audit_comparator(tsk->uid, op, value); |
417 | break; | 453 | break; |
418 | case AUDIT_EUID: | 454 | case AUDIT_EUID: |
419 | result = (tsk->euid == value); | 455 | result = audit_comparator(tsk->euid, op, value); |
420 | break; | 456 | break; |
421 | case AUDIT_SUID: | 457 | case AUDIT_SUID: |
422 | result = (tsk->suid == value); | 458 | result = audit_comparator(tsk->suid, op, value); |
423 | break; | 459 | break; |
424 | case AUDIT_FSUID: | 460 | case AUDIT_FSUID: |
425 | result = (tsk->fsuid == value); | 461 | result = audit_comparator(tsk->fsuid, op, value); |
426 | break; | 462 | break; |
427 | case AUDIT_GID: | 463 | case AUDIT_GID: |
428 | result = (tsk->gid == value); | 464 | result = audit_comparator(tsk->gid, op, value); |
429 | break; | 465 | break; |
430 | case AUDIT_EGID: | 466 | case AUDIT_EGID: |
431 | result = (tsk->egid == value); | 467 | result = audit_comparator(tsk->egid, op, value); |
432 | break; | 468 | break; |
433 | case AUDIT_SGID: | 469 | case AUDIT_SGID: |
434 | result = (tsk->sgid == value); | 470 | result = audit_comparator(tsk->sgid, op, value); |
435 | break; | 471 | break; |
436 | case AUDIT_FSGID: | 472 | case AUDIT_FSGID: |
437 | result = (tsk->fsgid == value); | 473 | result = audit_comparator(tsk->fsgid, op, value); |
438 | break; | 474 | break; |
439 | case AUDIT_PERS: | 475 | case AUDIT_PERS: |
440 | result = (tsk->personality == value); | 476 | result = audit_comparator(tsk->personality, op, value); |
441 | break; | 477 | break; |
442 | case AUDIT_ARCH: | 478 | case AUDIT_ARCH: |
443 | if (ctx) | 479 | if (ctx) |
444 | result = (ctx->arch == value); | 480 | result = audit_comparator(ctx->arch, op, value); |
445 | break; | 481 | break; |
446 | 482 | ||
447 | case AUDIT_EXIT: | 483 | case AUDIT_EXIT: |
448 | if (ctx && ctx->return_valid) | 484 | if (ctx && ctx->return_valid) |
449 | result = (ctx->return_code == value); | 485 | result = audit_comparator(ctx->return_code, op, value); |
450 | break; | 486 | break; |
451 | case AUDIT_SUCCESS: | 487 | case AUDIT_SUCCESS: |
452 | if (ctx && ctx->return_valid) { | 488 | if (ctx && ctx->return_valid) { |
453 | if (value) | 489 | if (value) |
454 | result = (ctx->return_valid == AUDITSC_SUCCESS); | 490 | result = audit_comparator(ctx->return_valid, op, AUDITSC_SUCCESS); |
455 | else | 491 | else |
456 | result = (ctx->return_valid == AUDITSC_FAILURE); | 492 | result = audit_comparator(ctx->return_valid, op, AUDITSC_FAILURE); |
457 | } | 493 | } |
458 | break; | 494 | break; |
459 | case AUDIT_DEVMAJOR: | 495 | case AUDIT_DEVMAJOR: |
460 | if (ctx) { | 496 | if (ctx) { |
461 | for (j = 0; j < ctx->name_count; j++) { | 497 | for (j = 0; j < ctx->name_count; j++) { |
462 | if (MAJOR(ctx->names[j].dev)==value) { | 498 | if (audit_comparator(MAJOR(ctx->names[j].dev), op, value)) { |
463 | ++result; | 499 | ++result; |
464 | break; | 500 | break; |
465 | } | 501 | } |
@@ -469,7 +505,7 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
469 | case AUDIT_DEVMINOR: | 505 | case AUDIT_DEVMINOR: |
470 | if (ctx) { | 506 | if (ctx) { |
471 | for (j = 0; j < ctx->name_count; j++) { | 507 | for (j = 0; j < ctx->name_count; j++) { |
472 | if (MINOR(ctx->names[j].dev)==value) { | 508 | if (audit_comparator(MINOR(ctx->names[j].dev), op, value)) { |
473 | ++result; | 509 | ++result; |
474 | break; | 510 | break; |
475 | } | 511 | } |
@@ -479,7 +515,7 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
479 | case AUDIT_INODE: | 515 | case AUDIT_INODE: |
480 | if (ctx) { | 516 | if (ctx) { |
481 | for (j = 0; j < ctx->name_count; j++) { | 517 | for (j = 0; j < ctx->name_count; j++) { |
482 | if (ctx->names[j].ino == value) { | 518 | if (audit_comparator(ctx->names[j].ino, op, value)) { |
483 | ++result; | 519 | ++result; |
484 | break; | 520 | break; |
485 | } | 521 | } |
@@ -489,19 +525,17 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
489 | case AUDIT_LOGINUID: | 525 | case AUDIT_LOGINUID: |
490 | result = 0; | 526 | result = 0; |
491 | if (ctx) | 527 | if (ctx) |
492 | result = (ctx->loginuid == value); | 528 | result = audit_comparator(ctx->loginuid, op, value); |
493 | break; | 529 | break; |
494 | case AUDIT_ARG0: | 530 | case AUDIT_ARG0: |
495 | case AUDIT_ARG1: | 531 | case AUDIT_ARG1: |
496 | case AUDIT_ARG2: | 532 | case AUDIT_ARG2: |
497 | case AUDIT_ARG3: | 533 | case AUDIT_ARG3: |
498 | if (ctx) | 534 | if (ctx) |
499 | result = (ctx->argv[field-AUDIT_ARG0]==value); | 535 | result = audit_comparator(ctx->argv[field-AUDIT_ARG0], op, value); |
500 | break; | 536 | break; |
501 | } | 537 | } |
502 | 538 | ||
503 | if (rule->fields[i] & AUDIT_NEGATE) | ||
504 | result = !result; | ||
505 | if (!result) | 539 | if (!result) |
506 | return 0; | 540 | return 0; |
507 | } | 541 | } |
@@ -550,49 +584,48 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, | |||
550 | 584 | ||
551 | rcu_read_lock(); | 585 | rcu_read_lock(); |
552 | if (!list_empty(list)) { | 586 | if (!list_empty(list)) { |
553 | int word = AUDIT_WORD(ctx->major); | 587 | int word = AUDIT_WORD(ctx->major); |
554 | int bit = AUDIT_BIT(ctx->major); | 588 | int bit = AUDIT_BIT(ctx->major); |
555 | 589 | ||
556 | list_for_each_entry_rcu(e, list, list) { | 590 | list_for_each_entry_rcu(e, list, list) { |
557 | if ((e->rule.mask[word] & bit) == bit | 591 | if ((e->rule.mask[word] & bit) == bit |
558 | && audit_filter_rules(tsk, &e->rule, ctx, &state)) { | 592 | && audit_filter_rules(tsk, &e->rule, ctx, &state)) { |
559 | rcu_read_unlock(); | 593 | rcu_read_unlock(); |
560 | return state; | 594 | return state; |
561 | } | 595 | } |
562 | } | 596 | } |
563 | } | 597 | } |
564 | rcu_read_unlock(); | 598 | rcu_read_unlock(); |
565 | return AUDIT_BUILD_CONTEXT; | 599 | return AUDIT_BUILD_CONTEXT; |
566 | } | 600 | } |
567 | 601 | ||
568 | static int audit_filter_user_rules(struct netlink_skb_parms *cb, | 602 | static int audit_filter_user_rules(struct netlink_skb_parms *cb, |
569 | struct audit_rule *rule, | 603 | struct audit_rule *rule, |
570 | enum audit_state *state) | 604 | enum audit_state *state) |
571 | { | 605 | { |
572 | int i; | 606 | int i; |
573 | 607 | ||
574 | for (i = 0; i < rule->field_count; i++) { | 608 | for (i = 0; i < rule->field_count; i++) { |
575 | u32 field = rule->fields[i] & ~AUDIT_NEGATE; | 609 | u32 field = rule->fields[i] & ~AUDIT_OPERATORS; |
610 | u32 op = rule->fields[i] & AUDIT_OPERATORS; | ||
576 | u32 value = rule->values[i]; | 611 | u32 value = rule->values[i]; |
577 | int result = 0; | 612 | int result = 0; |
578 | 613 | ||
579 | switch (field) { | 614 | switch (field) { |
580 | case AUDIT_PID: | 615 | case AUDIT_PID: |
581 | result = (cb->creds.pid == value); | 616 | result = audit_comparator(cb->creds.pid, op, value); |
582 | break; | 617 | break; |
583 | case AUDIT_UID: | 618 | case AUDIT_UID: |
584 | result = (cb->creds.uid == value); | 619 | result = audit_comparator(cb->creds.uid, op, value); |
585 | break; | 620 | break; |
586 | case AUDIT_GID: | 621 | case AUDIT_GID: |
587 | result = (cb->creds.gid == value); | 622 | result = audit_comparator(cb->creds.gid, op, value); |
588 | break; | 623 | break; |
589 | case AUDIT_LOGINUID: | 624 | case AUDIT_LOGINUID: |
590 | result = (cb->loginuid == value); | 625 | result = audit_comparator(cb->loginuid, op, value); |
591 | break; | 626 | break; |
592 | } | 627 | } |
593 | 628 | ||
594 | if (rule->fields[i] & AUDIT_NEGATE) | ||
595 | result = !result; | ||
596 | if (!result) | 629 | if (!result) |
597 | return 0; | 630 | return 0; |
598 | } | 631 | } |