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>
This commit is contained in:
David Woodhouse 2005-06-21 16:22:01 +01:00
parent ae7b961b1c
commit f6a789d198
3 changed files with 47 additions and 9 deletions

View File

@ -281,6 +281,7 @@ extern void audit_send_reply(int pid, int seq, int type,
int done, int multi, int done, int multi,
void *payload, int size); void *payload, int size);
extern void audit_log_lost(const char *message); extern void audit_log_lost(const char *message);
extern struct semaphore audit_netlink_sem;
#else #else
#define audit_log(c,t,f,...) do { ; } while (0) #define audit_log(c,t,f,...) do { ; } while (0)
#define audit_log_start(c,t) ({ NULL; }) #define audit_log_start(c,t) ({ NULL; })

View File

@ -110,7 +110,7 @@ static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
/* The netlink socket is only to be read by 1 CPU, which lets us assume /* The netlink socket is only to be read by 1 CPU, which lets us assume
* that list additions and deletions never happen simultaneously in * that list additions and deletions never happen simultaneously in
* auditsc.c */ * auditsc.c */
static DECLARE_MUTEX(audit_netlink_sem); DECLARE_MUTEX(audit_netlink_sem);
/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
* audit records. Since printk uses a 1024 byte buffer, this buffer * audit records. Since printk uses a 1024 byte buffer, this buffer

View File

@ -39,6 +39,7 @@
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/kthread.h>
#include <asm/unistd.h> #include <asm/unistd.h>
/* 0 = no checking /* 0 = no checking
@ -281,24 +282,60 @@ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
return 0; 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, int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
uid_t loginuid) uid_t loginuid)
{ {
struct audit_entry *entry; struct audit_entry *entry;
struct task_struct *tsk;
int *dest;
int err = 0; int err = 0;
int i;
unsigned listnr; unsigned listnr;
switch (type) { switch (type) {
case AUDIT_LIST: case AUDIT_LIST:
/* The *_rcu iterators not needed here because we are /* We can't just spew out the rules here because we might fill
always called with audit_netlink_sem held. */ * the available socket buffer space and deadlock waiting for
for (i=0; i<AUDIT_NR_FILTERS; i++) { * auditctl to read from it... which isn't ever going to
list_for_each_entry(entry, &audit_filter_list[i], list) * happen if we're actually running in the context of auditctl
audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, * trying to _send_ the stuff */
&entry->rule, sizeof(entry->rule));
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; break;
case AUDIT_ADD: case AUDIT_ADD:
if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL))) if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))