diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2005-09-13 12:47:30 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-09-13 12:47:30 -0400 |
commit | 5d54e69c68c05b162a56f9914cae72afd7e6f40a (patch) | |
tree | c5933858c4861bc3e358559f64ef459a1f56ab75 /kernel/audit.c | |
parent | 63f3d1df1ad276a30b75339dd682a6e1f9d0c181 (diff) | |
parent | b6ddc518520887a62728b0414efbf802a9dfdd55 (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/dwmw2/audit-2.6
Diffstat (limited to 'kernel/audit.c')
-rw-r--r-- | kernel/audit.c | 128 |
1 files changed, 79 insertions, 49 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index 7f0699790d46..83096b67510a 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -79,6 +79,8 @@ static int audit_rate_limit; | |||
79 | 79 | ||
80 | /* Number of outstanding audit_buffers allowed. */ | 80 | /* Number of outstanding audit_buffers allowed. */ |
81 | static int audit_backlog_limit = 64; | 81 | static int audit_backlog_limit = 64; |
82 | static int audit_backlog_wait_time = 60 * HZ; | ||
83 | static int audit_backlog_wait_overflow = 0; | ||
82 | 84 | ||
83 | /* The identity of the user shutting down the audit system. */ | 85 | /* The identity of the user shutting down the audit system. */ |
84 | uid_t audit_sig_uid = -1; | 86 | uid_t audit_sig_uid = -1; |
@@ -106,18 +108,12 @@ static LIST_HEAD(audit_freelist); | |||
106 | static struct sk_buff_head audit_skb_queue; | 108 | static struct sk_buff_head audit_skb_queue; |
107 | static struct task_struct *kauditd_task; | 109 | static struct task_struct *kauditd_task; |
108 | static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); | 110 | static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); |
109 | 111 | static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait); | |
110 | /* There are three lists of rules -- one to search at task creation | ||
111 | * time, one to search at syscall entry time, and another to search at | ||
112 | * syscall exit time. */ | ||
113 | static LIST_HEAD(audit_tsklist); | ||
114 | static LIST_HEAD(audit_entlist); | ||
115 | static LIST_HEAD(audit_extlist); | ||
116 | 112 | ||
117 | /* The netlink socket is only to be read by 1 CPU, which lets us assume | 113 | /* The netlink socket is only to be read by 1 CPU, which lets us assume |
118 | * that list additions and deletions never happen simultaneously in | 114 | * that list additions and deletions never happen simultaneously in |
119 | * auditsc.c */ | 115 | * auditsc.c */ |
120 | static DECLARE_MUTEX(audit_netlink_sem); | 116 | DECLARE_MUTEX(audit_netlink_sem); |
121 | 117 | ||
122 | /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting | 118 | /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting |
123 | * audit records. Since printk uses a 1024 byte buffer, this buffer | 119 | * audit records. Since printk uses a 1024 byte buffer, this buffer |
@@ -137,6 +133,7 @@ struct audit_buffer { | |||
137 | struct list_head list; | 133 | struct list_head list; |
138 | struct sk_buff *skb; /* formatted skb ready to send */ | 134 | struct sk_buff *skb; /* formatted skb ready to send */ |
139 | struct audit_context *ctx; /* NULL or associated context */ | 135 | struct audit_context *ctx; /* NULL or associated context */ |
136 | int gfp_mask; | ||
140 | }; | 137 | }; |
141 | 138 | ||
142 | static void audit_set_pid(struct audit_buffer *ab, pid_t pid) | 139 | static void audit_set_pid(struct audit_buffer *ab, pid_t pid) |
@@ -145,11 +142,6 @@ static void audit_set_pid(struct audit_buffer *ab, pid_t pid) | |||
145 | nlh->nlmsg_pid = pid; | 142 | nlh->nlmsg_pid = pid; |
146 | } | 143 | } |
147 | 144 | ||
148 | struct audit_entry { | ||
149 | struct list_head list; | ||
150 | struct audit_rule rule; | ||
151 | }; | ||
152 | |||
153 | static void audit_panic(const char *message) | 145 | static void audit_panic(const char *message) |
154 | { | 146 | { |
155 | switch (audit_failure) | 147 | switch (audit_failure) |
@@ -233,7 +225,7 @@ static int audit_set_rate_limit(int limit, uid_t loginuid) | |||
233 | { | 225 | { |
234 | int old = audit_rate_limit; | 226 | int old = audit_rate_limit; |
235 | audit_rate_limit = limit; | 227 | audit_rate_limit = limit; |
236 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 228 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
237 | "audit_rate_limit=%d old=%d by auid=%u", | 229 | "audit_rate_limit=%d old=%d by auid=%u", |
238 | audit_rate_limit, old, loginuid); | 230 | audit_rate_limit, old, loginuid); |
239 | return old; | 231 | return old; |
@@ -243,7 +235,7 @@ static int audit_set_backlog_limit(int limit, uid_t loginuid) | |||
243 | { | 235 | { |
244 | int old = audit_backlog_limit; | 236 | int old = audit_backlog_limit; |
245 | audit_backlog_limit = limit; | 237 | audit_backlog_limit = limit; |
246 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 238 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
247 | "audit_backlog_limit=%d old=%d by auid=%u", | 239 | "audit_backlog_limit=%d old=%d by auid=%u", |
248 | audit_backlog_limit, old, loginuid); | 240 | audit_backlog_limit, old, loginuid); |
249 | return old; | 241 | return old; |
@@ -255,7 +247,7 @@ static int audit_set_enabled(int state, uid_t loginuid) | |||
255 | if (state != 0 && state != 1) | 247 | if (state != 0 && state != 1) |
256 | return -EINVAL; | 248 | return -EINVAL; |
257 | audit_enabled = state; | 249 | audit_enabled = state; |
258 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 250 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
259 | "audit_enabled=%d old=%d by auid=%u", | 251 | "audit_enabled=%d old=%d by auid=%u", |
260 | audit_enabled, old, loginuid); | 252 | audit_enabled, old, loginuid); |
261 | return old; | 253 | return old; |
@@ -269,7 +261,7 @@ static int audit_set_failure(int state, uid_t loginuid) | |||
269 | && state != AUDIT_FAIL_PANIC) | 261 | && state != AUDIT_FAIL_PANIC) |
270 | return -EINVAL; | 262 | return -EINVAL; |
271 | audit_failure = state; | 263 | audit_failure = state; |
272 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 264 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
273 | "audit_failure=%d old=%d by auid=%u", | 265 | "audit_failure=%d old=%d by auid=%u", |
274 | audit_failure, old, loginuid); | 266 | audit_failure, old, loginuid); |
275 | return old; | 267 | return old; |
@@ -281,6 +273,7 @@ int kauditd_thread(void *dummy) | |||
281 | 273 | ||
282 | while (1) { | 274 | while (1) { |
283 | skb = skb_dequeue(&audit_skb_queue); | 275 | skb = skb_dequeue(&audit_skb_queue); |
276 | wake_up(&audit_backlog_wait); | ||
284 | if (skb) { | 277 | if (skb) { |
285 | if (audit_pid) { | 278 | if (audit_pid) { |
286 | int err = netlink_unicast(audit_sock, skb, audit_pid, 0); | 279 | int err = netlink_unicast(audit_sock, skb, audit_pid, 0); |
@@ -290,7 +283,7 @@ int kauditd_thread(void *dummy) | |||
290 | audit_pid = 0; | 283 | audit_pid = 0; |
291 | } | 284 | } |
292 | } else { | 285 | } else { |
293 | printk(KERN_ERR "%s\n", skb->data + NLMSG_SPACE(0)); | 286 | printk(KERN_NOTICE "%s\n", skb->data + NLMSG_SPACE(0)); |
294 | kfree_skb(skb); | 287 | kfree_skb(skb); |
295 | } | 288 | } |
296 | } else { | 289 | } else { |
@@ -423,7 +416,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
423 | if (status_get->mask & AUDIT_STATUS_PID) { | 416 | if (status_get->mask & AUDIT_STATUS_PID) { |
424 | int old = audit_pid; | 417 | int old = audit_pid; |
425 | audit_pid = status_get->pid; | 418 | audit_pid = status_get->pid; |
426 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 419 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
427 | "audit_pid=%d old=%d by auid=%u", | 420 | "audit_pid=%d old=%d by auid=%u", |
428 | audit_pid, old, loginuid); | 421 | audit_pid, old, loginuid); |
429 | } | 422 | } |
@@ -435,15 +428,21 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
435 | break; | 428 | break; |
436 | case AUDIT_USER: | 429 | case AUDIT_USER: |
437 | case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: | 430 | case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: |
438 | ab = audit_log_start(NULL, msg_type); | 431 | if (!audit_enabled && msg_type != AUDIT_USER_AVC) |
439 | if (!ab) | 432 | return 0; |
440 | break; /* audit_panic has been called */ | 433 | |
441 | audit_log_format(ab, | 434 | err = audit_filter_user(&NETLINK_CB(skb), msg_type); |
442 | "user pid=%d uid=%u auid=%u" | 435 | if (err == 1) { |
443 | " msg='%.1024s'", | 436 | err = 0; |
444 | pid, uid, loginuid, (char *)data); | 437 | ab = audit_log_start(NULL, GFP_KERNEL, msg_type); |
445 | audit_set_pid(ab, pid); | 438 | if (ab) { |
446 | audit_log_end(ab); | 439 | audit_log_format(ab, |
440 | "user pid=%d uid=%u auid=%u msg='%.1024s'", | ||
441 | pid, uid, loginuid, (char *)data); | ||
442 | audit_set_pid(ab, pid); | ||
443 | audit_log_end(ab); | ||
444 | } | ||
445 | } | ||
447 | break; | 446 | break; |
448 | case AUDIT_ADD: | 447 | case AUDIT_ADD: |
449 | case AUDIT_DEL: | 448 | case AUDIT_DEL: |
@@ -523,7 +522,7 @@ static int __init audit_init(void) | |||
523 | skb_queue_head_init(&audit_skb_queue); | 522 | skb_queue_head_init(&audit_skb_queue); |
524 | audit_initialized = 1; | 523 | audit_initialized = 1; |
525 | audit_enabled = audit_default; | 524 | audit_enabled = audit_default; |
526 | audit_log(NULL, AUDIT_KERNEL, "initialized"); | 525 | audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized"); |
527 | return 0; | 526 | return 0; |
528 | } | 527 | } |
529 | __initcall(audit_init); | 528 | __initcall(audit_init); |
@@ -561,7 +560,7 @@ static void audit_buffer_free(struct audit_buffer *ab) | |||
561 | } | 560 | } |
562 | 561 | ||
563 | static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, | 562 | static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, |
564 | int gfp_mask, int type) | 563 | unsigned int __nocast gfp_mask, int type) |
565 | { | 564 | { |
566 | unsigned long flags; | 565 | unsigned long flags; |
567 | struct audit_buffer *ab = NULL; | 566 | struct audit_buffer *ab = NULL; |
@@ -587,6 +586,7 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, | |||
587 | goto err; | 586 | goto err; |
588 | 587 | ||
589 | ab->ctx = ctx; | 588 | ab->ctx = ctx; |
589 | ab->gfp_mask = gfp_mask; | ||
590 | nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); | 590 | nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); |
591 | nlh->nlmsg_type = type; | 591 | nlh->nlmsg_type = type; |
592 | nlh->nlmsg_flags = 0; | 592 | nlh->nlmsg_flags = 0; |
@@ -606,26 +606,27 @@ err: | |||
606 | * (timestamp,serial) tuple is unique for each syscall and is live from | 606 | * (timestamp,serial) tuple is unique for each syscall and is live from |
607 | * syscall entry to syscall exit. | 607 | * syscall entry to syscall exit. |
608 | * | 608 | * |
609 | * Atomic values are only guaranteed to be 24-bit, so we count down. | ||
610 | * | ||
611 | * NOTE: Another possibility is to store the formatted records off the | 609 | * NOTE: Another possibility is to store the formatted records off the |
612 | * audit context (for those records that have a context), and emit them | 610 | * audit context (for those records that have a context), and emit them |
613 | * all at syscall exit. However, this could delay the reporting of | 611 | * all at syscall exit. However, this could delay the reporting of |
614 | * significant errors until syscall exit (or never, if the system | 612 | * significant errors until syscall exit (or never, if the system |
615 | * halts). */ | 613 | * halts). */ |
614 | |||
616 | unsigned int audit_serial(void) | 615 | unsigned int audit_serial(void) |
617 | { | 616 | { |
618 | static atomic_t serial = ATOMIC_INIT(0xffffff); | 617 | static spinlock_t serial_lock = SPIN_LOCK_UNLOCKED; |
619 | unsigned int a, b; | 618 | static unsigned int serial = 0; |
619 | |||
620 | unsigned long flags; | ||
621 | unsigned int ret; | ||
620 | 622 | ||
623 | spin_lock_irqsave(&serial_lock, flags); | ||
621 | do { | 624 | do { |
622 | a = atomic_read(&serial); | 625 | ret = ++serial; |
623 | if (atomic_dec_and_test(&serial)) | 626 | } while (unlikely(!ret)); |
624 | atomic_set(&serial, 0xffffff); | 627 | spin_unlock_irqrestore(&serial_lock, flags); |
625 | b = atomic_read(&serial); | ||
626 | } while (b != a - 1); | ||
627 | 628 | ||
628 | return 0xffffff - b; | 629 | return ret; |
629 | } | 630 | } |
630 | 631 | ||
631 | static inline void audit_get_stamp(struct audit_context *ctx, | 632 | static inline void audit_get_stamp(struct audit_context *ctx, |
@@ -645,17 +646,43 @@ static inline void audit_get_stamp(struct audit_context *ctx, | |||
645 | * syscall, then the syscall is marked as auditable and an audit record | 646 | * syscall, then the syscall is marked as auditable and an audit record |
646 | * will be written at syscall exit. If there is no associated task, tsk | 647 | * will be written at syscall exit. If there is no associated task, tsk |
647 | * should be NULL. */ | 648 | * should be NULL. */ |
648 | struct audit_buffer *audit_log_start(struct audit_context *ctx, int type) | 649 | |
650 | struct audit_buffer *audit_log_start(struct audit_context *ctx, int gfp_mask, | ||
651 | int type) | ||
649 | { | 652 | { |
650 | struct audit_buffer *ab = NULL; | 653 | struct audit_buffer *ab = NULL; |
651 | struct timespec t; | 654 | struct timespec t; |
652 | unsigned int serial; | 655 | unsigned int serial; |
656 | int reserve; | ||
657 | unsigned long timeout_start = jiffies; | ||
653 | 658 | ||
654 | if (!audit_initialized) | 659 | if (!audit_initialized) |
655 | return NULL; | 660 | return NULL; |
656 | 661 | ||
657 | if (audit_backlog_limit | 662 | if (gfp_mask & __GFP_WAIT) |
658 | && skb_queue_len(&audit_skb_queue) > audit_backlog_limit) { | 663 | reserve = 0; |
664 | else | ||
665 | reserve = 5; /* Allow atomic callers to go up to five | ||
666 | entries over the normal backlog limit */ | ||
667 | |||
668 | while (audit_backlog_limit | ||
669 | && skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) { | ||
670 | if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time | ||
671 | && time_before(jiffies, timeout_start + audit_backlog_wait_time)) { | ||
672 | |||
673 | /* Wait for auditd to drain the queue a little */ | ||
674 | DECLARE_WAITQUEUE(wait, current); | ||
675 | set_current_state(TASK_INTERRUPTIBLE); | ||
676 | add_wait_queue(&audit_backlog_wait, &wait); | ||
677 | |||
678 | if (audit_backlog_limit && | ||
679 | skb_queue_len(&audit_skb_queue) > audit_backlog_limit) | ||
680 | schedule_timeout(timeout_start + audit_backlog_wait_time - jiffies); | ||
681 | |||
682 | __set_current_state(TASK_RUNNING); | ||
683 | remove_wait_queue(&audit_backlog_wait, &wait); | ||
684 | continue; | ||
685 | } | ||
659 | if (audit_rate_check()) | 686 | if (audit_rate_check()) |
660 | printk(KERN_WARNING | 687 | printk(KERN_WARNING |
661 | "audit: audit_backlog=%d > " | 688 | "audit: audit_backlog=%d > " |
@@ -663,10 +690,12 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, int type) | |||
663 | skb_queue_len(&audit_skb_queue), | 690 | skb_queue_len(&audit_skb_queue), |
664 | audit_backlog_limit); | 691 | audit_backlog_limit); |
665 | audit_log_lost("backlog limit exceeded"); | 692 | audit_log_lost("backlog limit exceeded"); |
693 | audit_backlog_wait_time = audit_backlog_wait_overflow; | ||
694 | wake_up(&audit_backlog_wait); | ||
666 | return NULL; | 695 | return NULL; |
667 | } | 696 | } |
668 | 697 | ||
669 | ab = audit_buffer_alloc(ctx, GFP_ATOMIC, type); | 698 | ab = audit_buffer_alloc(ctx, gfp_mask, type); |
670 | if (!ab) { | 699 | if (!ab) { |
671 | audit_log_lost("out of memory in audit_log_start"); | 700 | audit_log_lost("out of memory in audit_log_start"); |
672 | return NULL; | 701 | return NULL; |
@@ -690,7 +719,7 @@ static inline int audit_expand(struct audit_buffer *ab, int extra) | |||
690 | { | 719 | { |
691 | struct sk_buff *skb = ab->skb; | 720 | struct sk_buff *skb = ab->skb; |
692 | int ret = pskb_expand_head(skb, skb_headroom(skb), extra, | 721 | int ret = pskb_expand_head(skb, skb_headroom(skb), extra, |
693 | GFP_ATOMIC); | 722 | ab->gfp_mask); |
694 | if (ret < 0) { | 723 | if (ret < 0) { |
695 | audit_log_lost("out of memory in audit_expand"); | 724 | audit_log_lost("out of memory in audit_expand"); |
696 | return 0; | 725 | return 0; |
@@ -809,7 +838,7 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, | |||
809 | audit_log_format(ab, " %s", prefix); | 838 | audit_log_format(ab, " %s", prefix); |
810 | 839 | ||
811 | /* We will allow 11 spaces for ' (deleted)' to be appended */ | 840 | /* We will allow 11 spaces for ' (deleted)' to be appended */ |
812 | path = kmalloc(PATH_MAX+11, GFP_KERNEL); | 841 | path = kmalloc(PATH_MAX+11, ab->gfp_mask); |
813 | if (!path) { | 842 | if (!path) { |
814 | audit_log_format(ab, "<no memory>"); | 843 | audit_log_format(ab, "<no memory>"); |
815 | return; | 844 | return; |
@@ -841,7 +870,7 @@ void audit_log_end(struct audit_buffer *ab) | |||
841 | ab->skb = NULL; | 870 | ab->skb = NULL; |
842 | wake_up_interruptible(&kauditd_wait); | 871 | wake_up_interruptible(&kauditd_wait); |
843 | } else { | 872 | } else { |
844 | printk("%s\n", ab->skb->data + NLMSG_SPACE(0)); | 873 | printk(KERN_NOTICE "%s\n", ab->skb->data + NLMSG_SPACE(0)); |
845 | } | 874 | } |
846 | } | 875 | } |
847 | audit_buffer_free(ab); | 876 | audit_buffer_free(ab); |
@@ -850,12 +879,13 @@ void audit_log_end(struct audit_buffer *ab) | |||
850 | /* Log an audit record. This is a convenience function that calls | 879 | /* Log an audit record. This is a convenience function that calls |
851 | * audit_log_start, audit_log_vformat, and audit_log_end. It may be | 880 | * audit_log_start, audit_log_vformat, and audit_log_end. It may be |
852 | * called in any context. */ | 881 | * called in any context. */ |
853 | void audit_log(struct audit_context *ctx, int type, const char *fmt, ...) | 882 | void audit_log(struct audit_context *ctx, int gfp_mask, int type, |
883 | const char *fmt, ...) | ||
854 | { | 884 | { |
855 | struct audit_buffer *ab; | 885 | struct audit_buffer *ab; |
856 | va_list args; | 886 | va_list args; |
857 | 887 | ||
858 | ab = audit_log_start(ctx, type); | 888 | ab = audit_log_start(ctx, gfp_mask, type); |
859 | if (ab) { | 889 | if (ab) { |
860 | va_start(args, fmt); | 890 | va_start(args, fmt); |
861 | audit_log_vformat(ab, fmt, args); | 891 | audit_log_vformat(ab, fmt, args); |