diff options
author | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-05-19 05:56:58 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-05-19 05:56:58 -0400 |
commit | b7d1125817c9a46cc46f57db89d9c195e7af22f8 (patch) | |
tree | c1096ff7ae35b77bf8108c3a60b856551c50a9d7 | |
parent | 168b7173959f80d20720dd1f7ec909a88ef2689d (diff) |
AUDIT: Send netlink messages from a separate kernel thread
netlink_unicast() will attempt to reallocate and will free messages if
the socket's rcvbuf limit is reached unless we give it an infinite
timeout. So do that, from a kernel thread which is dedicated to spewing
stuff up the netlink socket.
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r-- | kernel/audit.c | 191 |
1 files changed, 70 insertions, 121 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index dae3570b3a3b..bbc6f542c8f7 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -46,6 +46,8 @@ | |||
46 | #include <asm/types.h> | 46 | #include <asm/types.h> |
47 | #include <linux/mm.h> | 47 | #include <linux/mm.h> |
48 | #include <linux/module.h> | 48 | #include <linux/module.h> |
49 | #include <linux/err.h> | ||
50 | #include <linux/kthread.h> | ||
49 | 51 | ||
50 | #include <linux/audit.h> | 52 | #include <linux/audit.h> |
51 | 53 | ||
@@ -77,7 +79,6 @@ static int audit_rate_limit; | |||
77 | 79 | ||
78 | /* Number of outstanding audit_buffers allowed. */ | 80 | /* Number of outstanding audit_buffers allowed. */ |
79 | static int audit_backlog_limit = 64; | 81 | static int audit_backlog_limit = 64; |
80 | static atomic_t audit_backlog = ATOMIC_INIT(0); | ||
81 | 82 | ||
82 | /* The identity of the user shutting down the audit system. */ | 83 | /* The identity of the user shutting down the audit system. */ |
83 | uid_t audit_sig_uid = -1; | 84 | uid_t audit_sig_uid = -1; |
@@ -95,19 +96,17 @@ static atomic_t audit_lost = ATOMIC_INIT(0); | |||
95 | /* The netlink socket. */ | 96 | /* The netlink socket. */ |
96 | static struct sock *audit_sock; | 97 | static struct sock *audit_sock; |
97 | 98 | ||
98 | /* There are two lists of audit buffers. The txlist contains audit | 99 | /* The audit_freelist is a list of pre-allocated audit buffers (if more |
99 | * buffers that cannot be sent immediately to the netlink device because | ||
100 | * we are in an irq context (these are sent later in a tasklet). | ||
101 | * | ||
102 | * The second list is a list of pre-allocated audit buffers (if more | ||
103 | * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of | 100 | * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of |
104 | * being placed on the freelist). */ | 101 | * being placed on the freelist). */ |
105 | static DEFINE_SPINLOCK(audit_txlist_lock); | ||
106 | static DEFINE_SPINLOCK(audit_freelist_lock); | 102 | static DEFINE_SPINLOCK(audit_freelist_lock); |
107 | static int audit_freelist_count = 0; | 103 | static int audit_freelist_count = 0; |
108 | static LIST_HEAD(audit_txlist); | ||
109 | static LIST_HEAD(audit_freelist); | 104 | static LIST_HEAD(audit_freelist); |
110 | 105 | ||
106 | static struct sk_buff_head audit_skb_queue; | ||
107 | static struct task_struct *kauditd_task; | ||
108 | static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); | ||
109 | |||
111 | /* There are three lists of rules -- one to search at task creation | 110 | /* There are three lists of rules -- one to search at task creation |
112 | * time, one to search at syscall entry time, and another to search at | 111 | * time, one to search at syscall entry time, and another to search at |
113 | * syscall exit time. */ | 112 | * syscall exit time. */ |
@@ -151,9 +150,6 @@ struct audit_entry { | |||
151 | struct audit_rule rule; | 150 | struct audit_rule rule; |
152 | }; | 151 | }; |
153 | 152 | ||
154 | static void audit_log_end_irq(struct audit_buffer *ab); | ||
155 | static void audit_log_end_fast(struct audit_buffer *ab); | ||
156 | |||
157 | static void audit_panic(const char *message) | 153 | static void audit_panic(const char *message) |
158 | { | 154 | { |
159 | switch (audit_failure) | 155 | switch (audit_failure) |
@@ -224,10 +220,8 @@ void audit_log_lost(const char *message) | |||
224 | 220 | ||
225 | if (print) { | 221 | if (print) { |
226 | printk(KERN_WARNING | 222 | printk(KERN_WARNING |
227 | "audit: audit_lost=%d audit_backlog=%d" | 223 | "audit: audit_lost=%d audit_rate_limit=%d audit_backlog_limit=%d\n", |
228 | " audit_rate_limit=%d audit_backlog_limit=%d\n", | ||
229 | atomic_read(&audit_lost), | 224 | atomic_read(&audit_lost), |
230 | atomic_read(&audit_backlog), | ||
231 | audit_rate_limit, | 225 | audit_rate_limit, |
232 | audit_backlog_limit); | 226 | audit_backlog_limit); |
233 | audit_panic(message); | 227 | audit_panic(message); |
@@ -281,6 +275,38 @@ static int audit_set_failure(int state, uid_t loginuid) | |||
281 | return old; | 275 | return old; |
282 | } | 276 | } |
283 | 277 | ||
278 | int kauditd_thread(void *dummy) | ||
279 | { | ||
280 | struct sk_buff *skb; | ||
281 | |||
282 | while (1) { | ||
283 | skb = skb_dequeue(&audit_skb_queue); | ||
284 | if (skb) { | ||
285 | if (audit_pid) { | ||
286 | int err = netlink_unicast(audit_sock, skb, audit_pid, 0); | ||
287 | if (err < 0) { | ||
288 | BUG_ON(err != -ECONNREFUSED); /* Shoudn't happen */ | ||
289 | printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid); | ||
290 | audit_pid = 0; | ||
291 | } | ||
292 | } else { | ||
293 | printk(KERN_ERR "%s\n", skb->data + NLMSG_SPACE(0)); | ||
294 | kfree_skb(skb); | ||
295 | } | ||
296 | } else { | ||
297 | DECLARE_WAITQUEUE(wait, current); | ||
298 | set_current_state(TASK_INTERRUPTIBLE); | ||
299 | add_wait_queue(&kauditd_wait, &wait); | ||
300 | |||
301 | if (!skb_queue_len(&audit_skb_queue)) | ||
302 | schedule(); | ||
303 | |||
304 | __set_current_state(TASK_RUNNING); | ||
305 | remove_wait_queue(&kauditd_wait, &wait); | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
284 | void audit_send_reply(int pid, int seq, int type, int done, int multi, | 310 | void audit_send_reply(int pid, int seq, int type, int done, int multi, |
285 | void *payload, int size) | 311 | void *payload, int size) |
286 | { | 312 | { |
@@ -293,13 +319,16 @@ void audit_send_reply(int pid, int seq, int type, int done, int multi, | |||
293 | 319 | ||
294 | skb = alloc_skb(len, GFP_KERNEL); | 320 | skb = alloc_skb(len, GFP_KERNEL); |
295 | if (!skb) | 321 | if (!skb) |
296 | goto nlmsg_failure; | 322 | return; |
297 | 323 | ||
298 | nlh = NLMSG_PUT(skb, pid, seq, t, len - sizeof(*nlh)); | 324 | nlh = NLMSG_PUT(skb, pid, seq, t, size); |
299 | nlh->nlmsg_flags = flags; | 325 | nlh->nlmsg_flags = flags; |
300 | data = NLMSG_DATA(nlh); | 326 | data = NLMSG_DATA(nlh); |
301 | memcpy(data, payload, size); | 327 | memcpy(data, payload, size); |
302 | netlink_unicast(audit_sock, skb, pid, MSG_DONTWAIT); | 328 | |
329 | /* Ignore failure. It'll only happen if the sender goes away, | ||
330 | because our timeout is set to infinite. */ | ||
331 | netlink_unicast(audit_sock, skb, pid, 0); | ||
303 | return; | 332 | return; |
304 | 333 | ||
305 | nlmsg_failure: /* Used by NLMSG_PUT */ | 334 | nlmsg_failure: /* Used by NLMSG_PUT */ |
@@ -351,6 +380,15 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
351 | if (err) | 380 | if (err) |
352 | return err; | 381 | return err; |
353 | 382 | ||
383 | /* As soon as there's any sign of userspace auditd, start kauditd to talk to it */ | ||
384 | if (!kauditd_task) | ||
385 | kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd"); | ||
386 | if (IS_ERR(kauditd_task)) { | ||
387 | err = PTR_ERR(kauditd_task); | ||
388 | kauditd_task = NULL; | ||
389 | return err; | ||
390 | } | ||
391 | |||
354 | pid = NETLINK_CREDS(skb)->pid; | 392 | pid = NETLINK_CREDS(skb)->pid; |
355 | uid = NETLINK_CREDS(skb)->uid; | 393 | uid = NETLINK_CREDS(skb)->uid; |
356 | loginuid = NETLINK_CB(skb).loginuid; | 394 | loginuid = NETLINK_CB(skb).loginuid; |
@@ -365,7 +403,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
365 | status_set.rate_limit = audit_rate_limit; | 403 | status_set.rate_limit = audit_rate_limit; |
366 | status_set.backlog_limit = audit_backlog_limit; | 404 | status_set.backlog_limit = audit_backlog_limit; |
367 | status_set.lost = atomic_read(&audit_lost); | 405 | status_set.lost = atomic_read(&audit_lost); |
368 | status_set.backlog = atomic_read(&audit_backlog); | 406 | status_set.backlog = skb_queue_len(&audit_skb_queue); |
369 | audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0, | 407 | audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0, |
370 | &status_set, sizeof(status_set)); | 408 | &status_set, sizeof(status_set)); |
371 | break; | 409 | break; |
@@ -471,44 +509,6 @@ static void audit_receive(struct sock *sk, int length) | |||
471 | up(&audit_netlink_sem); | 509 | up(&audit_netlink_sem); |
472 | } | 510 | } |
473 | 511 | ||
474 | /* Grab skbuff from the audit_buffer and send to user space. */ | ||
475 | static inline int audit_log_drain(struct audit_buffer *ab) | ||
476 | { | ||
477 | struct sk_buff *skb = ab->skb; | ||
478 | |||
479 | if (skb) { | ||
480 | int retval = 0; | ||
481 | |||
482 | if (audit_pid) { | ||
483 | struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; | ||
484 | nlh->nlmsg_len = skb->len - NLMSG_SPACE(0); | ||
485 | skb_get(skb); /* because netlink_* frees */ | ||
486 | retval = netlink_unicast(audit_sock, skb, audit_pid, | ||
487 | MSG_DONTWAIT); | ||
488 | } | ||
489 | if (retval == -EAGAIN && | ||
490 | (atomic_read(&audit_backlog)) < audit_backlog_limit) { | ||
491 | audit_log_end_irq(ab); | ||
492 | return 1; | ||
493 | } | ||
494 | if (retval < 0) { | ||
495 | if (retval == -ECONNREFUSED) { | ||
496 | printk(KERN_ERR | ||
497 | "audit: *NO* daemon at audit_pid=%d\n", | ||
498 | audit_pid); | ||
499 | audit_pid = 0; | ||
500 | } else | ||
501 | audit_log_lost("netlink socket too busy"); | ||
502 | } | ||
503 | if (!audit_pid) { /* No daemon */ | ||
504 | int offset = NLMSG_SPACE(0); | ||
505 | int len = skb->len - offset; | ||
506 | skb->data[offset + len] = '\0'; | ||
507 | printk(KERN_ERR "%s\n", skb->data + offset); | ||
508 | } | ||
509 | } | ||
510 | return 0; | ||
511 | } | ||
512 | 512 | ||
513 | /* Initialize audit support at boot time. */ | 513 | /* Initialize audit support at boot time. */ |
514 | static int __init audit_init(void) | 514 | static int __init audit_init(void) |
@@ -519,6 +519,8 @@ static int __init audit_init(void) | |||
519 | if (!audit_sock) | 519 | if (!audit_sock) |
520 | audit_panic("cannot initialize netlink socket"); | 520 | audit_panic("cannot initialize netlink socket"); |
521 | 521 | ||
522 | audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; | ||
523 | skb_queue_head_init(&audit_skb_queue); | ||
522 | audit_initialized = 1; | 524 | audit_initialized = 1; |
523 | audit_enabled = audit_default; | 525 | audit_enabled = audit_default; |
524 | audit_log(NULL, AUDIT_KERNEL, "initialized"); | 526 | audit_log(NULL, AUDIT_KERNEL, "initialized"); |
@@ -549,7 +551,7 @@ static void audit_buffer_free(struct audit_buffer *ab) | |||
549 | 551 | ||
550 | if (ab->skb) | 552 | if (ab->skb) |
551 | kfree_skb(ab->skb); | 553 | kfree_skb(ab->skb); |
552 | atomic_dec(&audit_backlog); | 554 | |
553 | spin_lock_irqsave(&audit_freelist_lock, flags); | 555 | spin_lock_irqsave(&audit_freelist_lock, flags); |
554 | if (++audit_freelist_count > AUDIT_MAXFREE) | 556 | if (++audit_freelist_count > AUDIT_MAXFREE) |
555 | kfree(ab); | 557 | kfree(ab); |
@@ -579,13 +581,12 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, | |||
579 | if (!ab) | 581 | if (!ab) |
580 | goto err; | 582 | goto err; |
581 | } | 583 | } |
582 | atomic_inc(&audit_backlog); | ||
583 | 584 | ||
584 | ab->skb = alloc_skb(AUDIT_BUFSIZ, gfp_mask); | 585 | ab->skb = alloc_skb(AUDIT_BUFSIZ, gfp_mask); |
585 | if (!ab->skb) | 586 | if (!ab->skb) |
586 | goto err; | 587 | goto err; |
587 | 588 | ||
588 | ab->ctx = ctx; | 589 | ab->ctx = ctx; |
589 | nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); | 590 | nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); |
590 | nlh->nlmsg_type = type; | 591 | nlh->nlmsg_type = type; |
591 | nlh->nlmsg_flags = 0; | 592 | nlh->nlmsg_flags = 0; |
@@ -612,18 +613,6 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, int type) | |||
612 | if (!audit_initialized) | 613 | if (!audit_initialized) |
613 | return NULL; | 614 | return NULL; |
614 | 615 | ||
615 | if (audit_backlog_limit | ||
616 | && atomic_read(&audit_backlog) > audit_backlog_limit) { | ||
617 | if (audit_rate_check()) | ||
618 | printk(KERN_WARNING | ||
619 | "audit: audit_backlog=%d > " | ||
620 | "audit_backlog_limit=%d\n", | ||
621 | atomic_read(&audit_backlog), | ||
622 | audit_backlog_limit); | ||
623 | audit_log_lost("backlog limit exceeded"); | ||
624 | return NULL; | ||
625 | } | ||
626 | |||
627 | ab = audit_buffer_alloc(ctx, GFP_ATOMIC, type); | 616 | ab = audit_buffer_alloc(ctx, GFP_ATOMIC, type); |
628 | if (!ab) { | 617 | if (!ab) { |
629 | audit_log_lost("out of memory in audit_log_start"); | 618 | audit_log_lost("out of memory in audit_log_start"); |
@@ -784,70 +773,30 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, | |||
784 | kfree(path); | 773 | kfree(path); |
785 | } | 774 | } |
786 | 775 | ||
787 | /* Remove queued messages from the audit_txlist and send them to user space. */ | ||
788 | static void audit_tasklet_handler(unsigned long arg) | ||
789 | { | ||
790 | LIST_HEAD(list); | ||
791 | struct audit_buffer *ab; | ||
792 | unsigned long flags; | ||
793 | |||
794 | spin_lock_irqsave(&audit_txlist_lock, flags); | ||
795 | list_splice_init(&audit_txlist, &list); | ||
796 | spin_unlock_irqrestore(&audit_txlist_lock, flags); | ||
797 | |||
798 | while (!list_empty(&list)) { | ||
799 | ab = list_entry(list.next, struct audit_buffer, list); | ||
800 | list_del(&ab->list); | ||
801 | audit_log_end_fast(ab); | ||
802 | } | ||
803 | } | ||
804 | |||
805 | static DECLARE_TASKLET(audit_tasklet, audit_tasklet_handler, 0); | ||
806 | |||
807 | /* The netlink_* functions cannot be called inside an irq context, so | 776 | /* The netlink_* functions cannot be called inside an irq context, so |
808 | * the audit buffer is places on a queue and a tasklet is scheduled to | 777 | * the audit buffer is places on a queue and a tasklet is scheduled to |
809 | * remove them from the queue outside the irq context. May be called in | 778 | * remove them from the queue outside the irq context. May be called in |
810 | * any context. */ | 779 | * any context. */ |
811 | static void audit_log_end_irq(struct audit_buffer *ab) | 780 | void audit_log_end(struct audit_buffer *ab) |
812 | { | ||
813 | unsigned long flags; | ||
814 | |||
815 | if (!ab) | ||
816 | return; | ||
817 | spin_lock_irqsave(&audit_txlist_lock, flags); | ||
818 | list_add_tail(&ab->list, &audit_txlist); | ||
819 | spin_unlock_irqrestore(&audit_txlist_lock, flags); | ||
820 | |||
821 | tasklet_schedule(&audit_tasklet); | ||
822 | } | ||
823 | |||
824 | /* Send the message in the audit buffer directly to user space. May not | ||
825 | * be called in an irq context. */ | ||
826 | static void audit_log_end_fast(struct audit_buffer *ab) | ||
827 | { | 781 | { |
828 | BUG_ON(in_irq()); | ||
829 | if (!ab) | 782 | if (!ab) |
830 | return; | 783 | return; |
831 | if (!audit_rate_check()) { | 784 | if (!audit_rate_check()) { |
832 | audit_log_lost("rate limit exceeded"); | 785 | audit_log_lost("rate limit exceeded"); |
833 | } else { | 786 | } else { |
834 | if (audit_log_drain(ab)) | 787 | if (audit_pid) { |
835 | return; | 788 | struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data; |
789 | nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0); | ||
790 | skb_queue_tail(&audit_skb_queue, ab->skb); | ||
791 | ab->skb = NULL; | ||
792 | wake_up_interruptible(&kauditd_wait); | ||
793 | } else { | ||
794 | printk("%s\n", ab->skb->data + NLMSG_SPACE(0)); | ||
795 | } | ||
836 | } | 796 | } |
837 | audit_buffer_free(ab); | 797 | audit_buffer_free(ab); |
838 | } | 798 | } |
839 | 799 | ||
840 | /* Send or queue the message in the audit buffer, depending on the | ||
841 | * current context. (A convenience function that may be called in any | ||
842 | * context.) */ | ||
843 | void audit_log_end(struct audit_buffer *ab) | ||
844 | { | ||
845 | if (in_irq()) | ||
846 | audit_log_end_irq(ab); | ||
847 | else | ||
848 | audit_log_end_fast(ab); | ||
849 | } | ||
850 | |||
851 | /* Log an audit record. This is a convenience function that calls | 800 | /* Log an audit record. This is a convenience function that calls |
852 | * audit_log_start, audit_log_vformat, and audit_log_end. It may be | 801 | * audit_log_start, audit_log_vformat, and audit_log_end. It may be |
853 | * called in any context. */ | 802 | * called in any context. */ |