diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2008-12-14 23:45:27 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-01-04 15:14:41 -0500 |
commit | 0590b9335a1c72a3f0defcc6231287f7817e07c8 (patch) | |
tree | 289fa4668ae304f79f7484ac31b2cab0ab8894c1 | |
parent | 1a9d0797b8977d413435277bf9661efbbd584693 (diff) |
fixing audit rule ordering mess, part 1
Problem: ordering between the rules on exit chain is currently lost;
all watch and inode rules are listed after everything else _and_
exit,never on one kind doesn't stop exit,always on another from
being matched.
Solution: assign priorities to rules, keep track of the current
highest-priority matching rule and its result (always/never).
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | include/linux/audit.h | 1 | ||||
-rw-r--r-- | kernel/audit.h | 5 | ||||
-rw-r--r-- | kernel/auditfilter.c | 17 | ||||
-rw-r--r-- | kernel/auditsc.c | 79 |
4 files changed, 59 insertions, 43 deletions
diff --git a/include/linux/audit.h b/include/linux/audit.h index 7ddcb6a29eb1..5b47eeb00d53 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h | |||
@@ -373,6 +373,7 @@ struct audit_krule { | |||
373 | struct audit_watch *watch; /* associated watch */ | 373 | struct audit_watch *watch; /* associated watch */ |
374 | struct audit_tree *tree; /* associated watched tree */ | 374 | struct audit_tree *tree; /* associated watched tree */ |
375 | struct list_head rlist; /* entry in audit_{watch,tree}.rules list */ | 375 | struct list_head rlist; /* entry in audit_{watch,tree}.rules list */ |
376 | u64 prio; | ||
376 | }; | 377 | }; |
377 | 378 | ||
378 | struct audit_field { | 379 | struct audit_field { |
diff --git a/kernel/audit.h b/kernel/audit.h index 9d6717412fec..16f18cac661b 100644 --- a/kernel/audit.h +++ b/kernel/audit.h | |||
@@ -159,11 +159,8 @@ static inline int audit_signal_info(int sig, struct task_struct *t) | |||
159 | return __audit_signal_info(sig, t); | 159 | return __audit_signal_info(sig, t); |
160 | return 0; | 160 | return 0; |
161 | } | 161 | } |
162 | extern enum audit_state audit_filter_inodes(struct task_struct *, | 162 | extern void audit_filter_inodes(struct task_struct *, struct audit_context *); |
163 | struct audit_context *); | ||
164 | extern void audit_set_auditable(struct audit_context *); | ||
165 | #else | 163 | #else |
166 | #define audit_signal_info(s,t) AUDIT_DISABLED | 164 | #define audit_signal_info(s,t) AUDIT_DISABLED |
167 | #define audit_filter_inodes(t,c) AUDIT_DISABLED | 165 | #define audit_filter_inodes(t,c) AUDIT_DISABLED |
168 | #define audit_set_auditable(c) | ||
169 | #endif | 166 | #endif |
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 0febaa0f784c..995a2e86808d 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
@@ -919,6 +919,7 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old, | |||
919 | new->action = old->action; | 919 | new->action = old->action; |
920 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) | 920 | for (i = 0; i < AUDIT_BITMASK_SIZE; i++) |
921 | new->mask[i] = old->mask[i]; | 921 | new->mask[i] = old->mask[i]; |
922 | new->prio = old->prio; | ||
922 | new->buflen = old->buflen; | 923 | new->buflen = old->buflen; |
923 | new->inode_f = old->inode_f; | 924 | new->inode_f = old->inode_f; |
924 | new->watch = NULL; | 925 | new->watch = NULL; |
@@ -987,9 +988,8 @@ static void audit_update_watch(struct audit_parent *parent, | |||
987 | 988 | ||
988 | /* If the update involves invalidating rules, do the inode-based | 989 | /* If the update involves invalidating rules, do the inode-based |
989 | * filtering now, so we don't omit records. */ | 990 | * filtering now, so we don't omit records. */ |
990 | if (invalidating && current->audit_context && | 991 | if (invalidating && current->audit_context) |
991 | audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT) | 992 | audit_filter_inodes(current, current->audit_context); |
992 | audit_set_auditable(current->audit_context); | ||
993 | 993 | ||
994 | nwatch = audit_dupe_watch(owatch); | 994 | nwatch = audit_dupe_watch(owatch); |
995 | if (IS_ERR(nwatch)) { | 995 | if (IS_ERR(nwatch)) { |
@@ -1258,6 +1258,9 @@ static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp, | |||
1258 | return ret; | 1258 | return ret; |
1259 | } | 1259 | } |
1260 | 1260 | ||
1261 | static u64 prio_low = ~0ULL/2; | ||
1262 | static u64 prio_high = ~0ULL/2 - 1; | ||
1263 | |||
1261 | /* Add rule to given filterlist if not a duplicate. */ | 1264 | /* Add rule to given filterlist if not a duplicate. */ |
1262 | static inline int audit_add_rule(struct audit_entry *entry, | 1265 | static inline int audit_add_rule(struct audit_entry *entry, |
1263 | struct list_head *list) | 1266 | struct list_head *list) |
@@ -1319,6 +1322,14 @@ static inline int audit_add_rule(struct audit_entry *entry, | |||
1319 | } | 1322 | } |
1320 | } | 1323 | } |
1321 | 1324 | ||
1325 | entry->rule.prio = ~0ULL; | ||
1326 | if (entry->rule.listnr == AUDIT_FILTER_EXIT) { | ||
1327 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) | ||
1328 | entry->rule.prio = ++prio_high; | ||
1329 | else | ||
1330 | entry->rule.prio = --prio_low; | ||
1331 | } | ||
1332 | |||
1322 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { | 1333 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { |
1323 | list_add_rcu(&entry->list, list); | 1334 | list_add_rcu(&entry->list, list); |
1324 | entry->rule.flags &= ~AUDIT_FILTER_PREPEND; | 1335 | entry->rule.flags &= ~AUDIT_FILTER_PREPEND; |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index c76a58215f54..19d2c2747c8d 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -165,14 +165,14 @@ struct audit_tree_refs { | |||
165 | struct audit_context { | 165 | struct audit_context { |
166 | int dummy; /* must be the first element */ | 166 | int dummy; /* must be the first element */ |
167 | int in_syscall; /* 1 if task is in a syscall */ | 167 | int in_syscall; /* 1 if task is in a syscall */ |
168 | enum audit_state state; | 168 | enum audit_state state, current_state; |
169 | unsigned int serial; /* serial number for record */ | 169 | unsigned int serial; /* serial number for record */ |
170 | struct timespec ctime; /* time of syscall entry */ | 170 | struct timespec ctime; /* time of syscall entry */ |
171 | int major; /* syscall number */ | 171 | int major; /* syscall number */ |
172 | unsigned long argv[4]; /* syscall arguments */ | 172 | unsigned long argv[4]; /* syscall arguments */ |
173 | int return_valid; /* return code is valid */ | 173 | int return_valid; /* return code is valid */ |
174 | long return_code;/* syscall return code */ | 174 | long return_code;/* syscall return code */ |
175 | int auditable; /* 1 if record should be written */ | 175 | u64 prio; |
176 | int name_count; | 176 | int name_count; |
177 | struct audit_names names[AUDIT_NAMES]; | 177 | struct audit_names names[AUDIT_NAMES]; |
178 | char * filterkey; /* key for rule that triggered record */ | 178 | char * filterkey; /* key for rule that triggered record */ |
@@ -630,8 +630,16 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
630 | return 0; | 630 | return 0; |
631 | } | 631 | } |
632 | } | 632 | } |
633 | if (rule->filterkey && ctx) | 633 | |
634 | ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC); | 634 | if (ctx) { |
635 | if (rule->prio <= ctx->prio) | ||
636 | return 0; | ||
637 | if (rule->filterkey) { | ||
638 | kfree(ctx->filterkey); | ||
639 | ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC); | ||
640 | } | ||
641 | ctx->prio = rule->prio; | ||
642 | } | ||
635 | switch (rule->action) { | 643 | switch (rule->action) { |
636 | case AUDIT_NEVER: *state = AUDIT_DISABLED; break; | 644 | case AUDIT_NEVER: *state = AUDIT_DISABLED; break; |
637 | case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; | 645 | case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; |
@@ -685,6 +693,7 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, | |||
685 | audit_filter_rules(tsk, &e->rule, ctx, NULL, | 693 | audit_filter_rules(tsk, &e->rule, ctx, NULL, |
686 | &state)) { | 694 | &state)) { |
687 | rcu_read_unlock(); | 695 | rcu_read_unlock(); |
696 | ctx->current_state = state; | ||
688 | return state; | 697 | return state; |
689 | } | 698 | } |
690 | } | 699 | } |
@@ -698,15 +707,14 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, | |||
698 | * buckets applicable to the inode numbers in audit_names[]. | 707 | * buckets applicable to the inode numbers in audit_names[]. |
699 | * Regarding audit_state, same rules apply as for audit_filter_syscall(). | 708 | * Regarding audit_state, same rules apply as for audit_filter_syscall(). |
700 | */ | 709 | */ |
701 | enum audit_state audit_filter_inodes(struct task_struct *tsk, | 710 | void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx) |
702 | struct audit_context *ctx) | ||
703 | { | 711 | { |
704 | int i; | 712 | int i; |
705 | struct audit_entry *e; | 713 | struct audit_entry *e; |
706 | enum audit_state state; | 714 | enum audit_state state; |
707 | 715 | ||
708 | if (audit_pid && tsk->tgid == audit_pid) | 716 | if (audit_pid && tsk->tgid == audit_pid) |
709 | return AUDIT_DISABLED; | 717 | return; |
710 | 718 | ||
711 | rcu_read_lock(); | 719 | rcu_read_lock(); |
712 | for (i = 0; i < ctx->name_count; i++) { | 720 | for (i = 0; i < ctx->name_count; i++) { |
@@ -723,17 +731,20 @@ enum audit_state audit_filter_inodes(struct task_struct *tsk, | |||
723 | if ((e->rule.mask[word] & bit) == bit && | 731 | if ((e->rule.mask[word] & bit) == bit && |
724 | audit_filter_rules(tsk, &e->rule, ctx, n, &state)) { | 732 | audit_filter_rules(tsk, &e->rule, ctx, n, &state)) { |
725 | rcu_read_unlock(); | 733 | rcu_read_unlock(); |
726 | return state; | 734 | ctx->current_state = state; |
735 | return; | ||
727 | } | 736 | } |
728 | } | 737 | } |
729 | } | 738 | } |
730 | rcu_read_unlock(); | 739 | rcu_read_unlock(); |
731 | return AUDIT_BUILD_CONTEXT; | ||
732 | } | 740 | } |
733 | 741 | ||
734 | void audit_set_auditable(struct audit_context *ctx) | 742 | static void audit_set_auditable(struct audit_context *ctx) |
735 | { | 743 | { |
736 | ctx->auditable = 1; | 744 | if (!ctx->prio) { |
745 | ctx->prio = 1; | ||
746 | ctx->current_state = AUDIT_RECORD_CONTEXT; | ||
747 | } | ||
737 | } | 748 | } |
738 | 749 | ||
739 | static inline struct audit_context *audit_get_context(struct task_struct *tsk, | 750 | static inline struct audit_context *audit_get_context(struct task_struct *tsk, |
@@ -764,23 +775,11 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk, | |||
764 | else | 775 | else |
765 | context->return_code = return_code; | 776 | context->return_code = return_code; |
766 | 777 | ||
767 | if (context->in_syscall && !context->dummy && !context->auditable) { | 778 | if (context->in_syscall && !context->dummy) { |
768 | enum audit_state state; | 779 | audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]); |
769 | 780 | audit_filter_inodes(tsk, context); | |
770 | state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]); | ||
771 | if (state == AUDIT_RECORD_CONTEXT) { | ||
772 | context->auditable = 1; | ||
773 | goto get_context; | ||
774 | } | ||
775 | |||
776 | state = audit_filter_inodes(tsk, context); | ||
777 | if (state == AUDIT_RECORD_CONTEXT) | ||
778 | context->auditable = 1; | ||
779 | |||
780 | } | 781 | } |
781 | 782 | ||
782 | get_context: | ||
783 | |||
784 | tsk->audit_context = NULL; | 783 | tsk->audit_context = NULL; |
785 | return context; | 784 | return context; |
786 | } | 785 | } |
@@ -790,8 +789,7 @@ static inline void audit_free_names(struct audit_context *context) | |||
790 | int i; | 789 | int i; |
791 | 790 | ||
792 | #if AUDIT_DEBUG == 2 | 791 | #if AUDIT_DEBUG == 2 |
793 | if (context->auditable | 792 | if (context->put_count + context->ino_count != context->name_count) { |
794 | ||context->put_count + context->ino_count != context->name_count) { | ||
795 | printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d" | 793 | printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d" |
796 | " name_count=%d put_count=%d" | 794 | " name_count=%d put_count=%d" |
797 | " ino_count=%d [NOT freeing]\n", | 795 | " ino_count=%d [NOT freeing]\n", |
@@ -842,6 +840,7 @@ static inline void audit_zero_context(struct audit_context *context, | |||
842 | { | 840 | { |
843 | memset(context, 0, sizeof(*context)); | 841 | memset(context, 0, sizeof(*context)); |
844 | context->state = state; | 842 | context->state = state; |
843 | context->prio = state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0; | ||
845 | } | 844 | } |
846 | 845 | ||
847 | static inline struct audit_context *audit_alloc_context(enum audit_state state) | 846 | static inline struct audit_context *audit_alloc_context(enum audit_state state) |
@@ -1543,7 +1542,7 @@ void audit_free(struct task_struct *tsk) | |||
1543 | * We use GFP_ATOMIC here because we might be doing this | 1542 | * We use GFP_ATOMIC here because we might be doing this |
1544 | * in the context of the idle thread */ | 1543 | * in the context of the idle thread */ |
1545 | /* that can happen only if we are called from do_exit() */ | 1544 | /* that can happen only if we are called from do_exit() */ |
1546 | if (context->in_syscall && context->auditable) | 1545 | if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT) |
1547 | audit_log_exit(context, tsk); | 1546 | audit_log_exit(context, tsk); |
1548 | 1547 | ||
1549 | audit_free_context(context); | 1548 | audit_free_context(context); |
@@ -1627,15 +1626,17 @@ void audit_syscall_entry(int arch, int major, | |||
1627 | 1626 | ||
1628 | state = context->state; | 1627 | state = context->state; |
1629 | context->dummy = !audit_n_rules; | 1628 | context->dummy = !audit_n_rules; |
1630 | if (!context->dummy && (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT)) | 1629 | if (!context->dummy && state == AUDIT_BUILD_CONTEXT) { |
1630 | context->prio = 0; | ||
1631 | state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]); | 1631 | state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]); |
1632 | } | ||
1632 | if (likely(state == AUDIT_DISABLED)) | 1633 | if (likely(state == AUDIT_DISABLED)) |
1633 | return; | 1634 | return; |
1634 | 1635 | ||
1635 | context->serial = 0; | 1636 | context->serial = 0; |
1636 | context->ctime = CURRENT_TIME; | 1637 | context->ctime = CURRENT_TIME; |
1637 | context->in_syscall = 1; | 1638 | context->in_syscall = 1; |
1638 | context->auditable = !!(state == AUDIT_RECORD_CONTEXT); | 1639 | context->current_state = state; |
1639 | context->ppid = 0; | 1640 | context->ppid = 0; |
1640 | } | 1641 | } |
1641 | 1642 | ||
@@ -1643,17 +1644,20 @@ void audit_finish_fork(struct task_struct *child) | |||
1643 | { | 1644 | { |
1644 | struct audit_context *ctx = current->audit_context; | 1645 | struct audit_context *ctx = current->audit_context; |
1645 | struct audit_context *p = child->audit_context; | 1646 | struct audit_context *p = child->audit_context; |
1646 | if (!p || !ctx || !ctx->auditable) | 1647 | if (!p || !ctx) |
1648 | return; | ||
1649 | if (!ctx->in_syscall || ctx->current_state != AUDIT_RECORD_CONTEXT) | ||
1647 | return; | 1650 | return; |
1648 | p->arch = ctx->arch; | 1651 | p->arch = ctx->arch; |
1649 | p->major = ctx->major; | 1652 | p->major = ctx->major; |
1650 | memcpy(p->argv, ctx->argv, sizeof(ctx->argv)); | 1653 | memcpy(p->argv, ctx->argv, sizeof(ctx->argv)); |
1651 | p->ctime = ctx->ctime; | 1654 | p->ctime = ctx->ctime; |
1652 | p->dummy = ctx->dummy; | 1655 | p->dummy = ctx->dummy; |
1653 | p->auditable = ctx->auditable; | ||
1654 | p->in_syscall = ctx->in_syscall; | 1656 | p->in_syscall = ctx->in_syscall; |
1655 | p->filterkey = kstrdup(ctx->filterkey, GFP_KERNEL); | 1657 | p->filterkey = kstrdup(ctx->filterkey, GFP_KERNEL); |
1656 | p->ppid = current->pid; | 1658 | p->ppid = current->pid; |
1659 | p->prio = ctx->prio; | ||
1660 | p->current_state = ctx->current_state; | ||
1657 | } | 1661 | } |
1658 | 1662 | ||
1659 | /** | 1663 | /** |
@@ -1677,11 +1681,11 @@ void audit_syscall_exit(int valid, long return_code) | |||
1677 | if (likely(!context)) | 1681 | if (likely(!context)) |
1678 | return; | 1682 | return; |
1679 | 1683 | ||
1680 | if (context->in_syscall && context->auditable) | 1684 | if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT) |
1681 | audit_log_exit(context, tsk); | 1685 | audit_log_exit(context, tsk); |
1682 | 1686 | ||
1683 | context->in_syscall = 0; | 1687 | context->in_syscall = 0; |
1684 | context->auditable = 0; | 1688 | context->prio = context->state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0; |
1685 | 1689 | ||
1686 | if (context->previous) { | 1690 | if (context->previous) { |
1687 | struct audit_context *new_context = context->previous; | 1691 | struct audit_context *new_context = context->previous; |
@@ -2091,7 +2095,10 @@ int auditsc_get_stamp(struct audit_context *ctx, | |||
2091 | t->tv_sec = ctx->ctime.tv_sec; | 2095 | t->tv_sec = ctx->ctime.tv_sec; |
2092 | t->tv_nsec = ctx->ctime.tv_nsec; | 2096 | t->tv_nsec = ctx->ctime.tv_nsec; |
2093 | *serial = ctx->serial; | 2097 | *serial = ctx->serial; |
2094 | ctx->auditable = 1; | 2098 | if (!ctx->prio) { |
2099 | ctx->prio = 1; | ||
2100 | ctx->current_state = AUDIT_RECORD_CONTEXT; | ||
2101 | } | ||
2095 | return 1; | 2102 | return 1; |
2096 | } | 2103 | } |
2097 | 2104 | ||