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. */ |
