diff options
author | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-06-21 16:22:01 +0100 |
---|---|---|
committer | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-06-21 16:22:01 +0100 |
commit | f6a789d19858a951e7ff9e297a44b377c21b6c33 (patch) | |
tree | 5e54f1460bc048706ad6df8c5cb5bf748f067f13 /kernel/auditsc.c | |
parent | ae7b961b1c943367dfe179411f120d7bf8eaba89 (diff) |
AUDIT: Spawn kernel thread to list filter rules.
If we have enough rules to fill the netlink buffer space, it'll
deadlock because auditctl isn't ever actually going to read from the
socket until we return, and we aren't going to return until it
reads... so we spawn a kernel thread to spew out the list and then
exit.
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'kernel/auditsc.c')
-rw-r--r-- | kernel/auditsc.c | 53 |
1 files changed, 45 insertions, 8 deletions
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 031f979019d..cb8a4494515 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -39,6 +39,7 @@ #include <linux/audit.h> #include <linux/personality.h> #include <linux/time.h> +#include <linux/kthread.h> #include <asm/unistd.h> /* 0 = no checking @@ -281,24 +282,60 @@ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) return 0; } +static int audit_list_rules(void *_dest) +{ + int pid, seq; + int *dest = _dest; + struct audit_entry *entry; + int i; + + pid = dest[0]; + seq = dest[1]; + kfree(dest); + + down(&audit_netlink_sem); + + /* The *_rcu iterators not needed here because we are + always called with audit_netlink_sem held. */ + for (i=0; i<AUDIT_NR_FILTERS; i++) { + list_for_each_entry(entry, &audit_filter_list[i], list) + audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, + &entry->rule, sizeof(entry->rule)); + } + audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); + + up(&audit_netlink_sem); + return 0; +} + int audit_receive_filter(int type, int pid, int uid, int seq, void *data, uid_t loginuid) { struct audit_entry *entry; + struct task_struct *tsk; + int *dest; int err = 0; - int i; unsigned listnr; switch (type) { case AUDIT_LIST: - /* The *_rcu iterators not needed here because we are - always called with audit_netlink_sem held. */ - for (i=0; i<AUDIT_NR_FILTERS; i++) { - list_for_each_entry(entry, &audit_filter_list[i], list) - audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, - &entry->rule, sizeof(entry->rule)); + /* We can't just spew out the rules here because we might fill + * the available socket buffer space and deadlock waiting for + * auditctl to read from it... which isn't ever going to + * happen if we're actually running in the context of auditctl + * trying to _send_ the stuff */ + + dest = kmalloc(2 * sizeof(int), GFP_KERNEL); + if (!dest) + return -ENOMEM; + dest[0] = pid; + dest[1] = seq; + + tsk = kthread_run(audit_list_rules, dest, "audit_list_rules"); + if (IS_ERR(tsk)) { + kfree(dest); + err = PTR_ERR(tsk); } - audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); break; case AUDIT_ADD: if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) |