diff options
author | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-06-22 10:04:33 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-06-22 10:04:33 -0400 |
commit | 9ad9ad385be27fcc7c16d290d972c6173e780a61 (patch) | |
tree | bbca700c2d88ba421a6c9c348de367eaf4de0e2c /kernel | |
parent | 177bbc733a1d9c935bc3d6efd776a6699b29b1ca (diff) |
AUDIT: Wait for backlog to clear when generating messages.
Add a gfp_mask to audit_log_start() and audit_log(), to reduce the
amount of GFP_ATOMIC allocation -- most of it doesn't need to be
GFP_ATOMIC. Also if the mask includes __GFP_WAIT, then wait up to
60 seconds for the auditd backlog to clear instead of immediately
abandoning the message.
The timeout should probably be made configurable, but for now it'll
suffice that it only happens if auditd is actually running.
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/audit.c | 60 | ||||
-rw-r--r-- | kernel/auditsc.c | 14 |
2 files changed, 52 insertions, 22 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index 09a37581213b..644ab825118b 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -106,6 +106,7 @@ static LIST_HEAD(audit_freelist); | |||
106 | static struct sk_buff_head audit_skb_queue; | 106 | static struct sk_buff_head audit_skb_queue; |
107 | static struct task_struct *kauditd_task; | 107 | static struct task_struct *kauditd_task; |
108 | static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); | 108 | static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); |
109 | static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait); | ||
109 | 110 | ||
110 | /* The netlink socket is only to be read by 1 CPU, which lets us assume | 111 | /* The netlink socket is only to be read by 1 CPU, which lets us assume |
111 | * that list additions and deletions never happen simultaneously in | 112 | * that list additions and deletions never happen simultaneously in |
@@ -130,6 +131,7 @@ struct audit_buffer { | |||
130 | struct list_head list; | 131 | struct list_head list; |
131 | struct sk_buff *skb; /* formatted skb ready to send */ | 132 | struct sk_buff *skb; /* formatted skb ready to send */ |
132 | struct audit_context *ctx; /* NULL or associated context */ | 133 | struct audit_context *ctx; /* NULL or associated context */ |
134 | int gfp_mask; | ||
133 | }; | 135 | }; |
134 | 136 | ||
135 | static void audit_set_pid(struct audit_buffer *ab, pid_t pid) | 137 | static void audit_set_pid(struct audit_buffer *ab, pid_t pid) |
@@ -226,7 +228,7 @@ static int audit_set_rate_limit(int limit, uid_t loginuid) | |||
226 | { | 228 | { |
227 | int old = audit_rate_limit; | 229 | int old = audit_rate_limit; |
228 | audit_rate_limit = limit; | 230 | audit_rate_limit = limit; |
229 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 231 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
230 | "audit_rate_limit=%d old=%d by auid=%u", | 232 | "audit_rate_limit=%d old=%d by auid=%u", |
231 | audit_rate_limit, old, loginuid); | 233 | audit_rate_limit, old, loginuid); |
232 | return old; | 234 | return old; |
@@ -236,7 +238,7 @@ static int audit_set_backlog_limit(int limit, uid_t loginuid) | |||
236 | { | 238 | { |
237 | int old = audit_backlog_limit; | 239 | int old = audit_backlog_limit; |
238 | audit_backlog_limit = limit; | 240 | audit_backlog_limit = limit; |
239 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 241 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
240 | "audit_backlog_limit=%d old=%d by auid=%u", | 242 | "audit_backlog_limit=%d old=%d by auid=%u", |
241 | audit_backlog_limit, old, loginuid); | 243 | audit_backlog_limit, old, loginuid); |
242 | return old; | 244 | return old; |
@@ -248,7 +250,7 @@ static int audit_set_enabled(int state, uid_t loginuid) | |||
248 | if (state != 0 && state != 1) | 250 | if (state != 0 && state != 1) |
249 | return -EINVAL; | 251 | return -EINVAL; |
250 | audit_enabled = state; | 252 | audit_enabled = state; |
251 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 253 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
252 | "audit_enabled=%d old=%d by auid=%u", | 254 | "audit_enabled=%d old=%d by auid=%u", |
253 | audit_enabled, old, loginuid); | 255 | audit_enabled, old, loginuid); |
254 | return old; | 256 | return old; |
@@ -262,7 +264,7 @@ static int audit_set_failure(int state, uid_t loginuid) | |||
262 | && state != AUDIT_FAIL_PANIC) | 264 | && state != AUDIT_FAIL_PANIC) |
263 | return -EINVAL; | 265 | return -EINVAL; |
264 | audit_failure = state; | 266 | audit_failure = state; |
265 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 267 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
266 | "audit_failure=%d old=%d by auid=%u", | 268 | "audit_failure=%d old=%d by auid=%u", |
267 | audit_failure, old, loginuid); | 269 | audit_failure, old, loginuid); |
268 | return old; | 270 | return old; |
@@ -274,6 +276,7 @@ int kauditd_thread(void *dummy) | |||
274 | 276 | ||
275 | while (1) { | 277 | while (1) { |
276 | skb = skb_dequeue(&audit_skb_queue); | 278 | skb = skb_dequeue(&audit_skb_queue); |
279 | wake_up(&audit_backlog_wait); | ||
277 | if (skb) { | 280 | if (skb) { |
278 | if (audit_pid) { | 281 | if (audit_pid) { |
279 | int err = netlink_unicast(audit_sock, skb, audit_pid, 0); | 282 | int err = netlink_unicast(audit_sock, skb, audit_pid, 0); |
@@ -417,7 +420,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
417 | if (status_get->mask & AUDIT_STATUS_PID) { | 420 | if (status_get->mask & AUDIT_STATUS_PID) { |
418 | int old = audit_pid; | 421 | int old = audit_pid; |
419 | audit_pid = status_get->pid; | 422 | audit_pid = status_get->pid; |
420 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 423 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
421 | "audit_pid=%d old=%d by auid=%u", | 424 | "audit_pid=%d old=%d by auid=%u", |
422 | audit_pid, old, loginuid); | 425 | audit_pid, old, loginuid); |
423 | } | 426 | } |
@@ -435,7 +438,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
435 | err = audit_filter_user(pid, msg_type); | 438 | err = audit_filter_user(pid, msg_type); |
436 | if (err == 1) { | 439 | if (err == 1) { |
437 | err = 0; | 440 | err = 0; |
438 | ab = audit_log_start(NULL, msg_type); | 441 | ab = audit_log_start(NULL, GFP_KERNEL, msg_type); |
439 | if (ab) { | 442 | if (ab) { |
440 | audit_log_format(ab, | 443 | audit_log_format(ab, |
441 | "user pid=%d uid=%u auid=%u msg='%.1024s'", | 444 | "user pid=%d uid=%u auid=%u msg='%.1024s'", |
@@ -522,7 +525,7 @@ static int __init audit_init(void) | |||
522 | skb_queue_head_init(&audit_skb_queue); | 525 | skb_queue_head_init(&audit_skb_queue); |
523 | audit_initialized = 1; | 526 | audit_initialized = 1; |
524 | audit_enabled = audit_default; | 527 | audit_enabled = audit_default; |
525 | audit_log(NULL, AUDIT_KERNEL, "initialized"); | 528 | audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized"); |
526 | return 0; | 529 | return 0; |
527 | } | 530 | } |
528 | __initcall(audit_init); | 531 | __initcall(audit_init); |
@@ -586,6 +589,7 @@ static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, | |||
586 | goto err; | 589 | goto err; |
587 | 590 | ||
588 | ab->ctx = ctx; | 591 | ab->ctx = ctx; |
592 | ab->gfp_mask = gfp_mask; | ||
589 | nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); | 593 | nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0)); |
590 | nlh->nlmsg_type = type; | 594 | nlh->nlmsg_type = type; |
591 | nlh->nlmsg_flags = 0; | 595 | nlh->nlmsg_flags = 0; |
@@ -644,17 +648,42 @@ static inline void audit_get_stamp(struct audit_context *ctx, | |||
644 | * syscall, then the syscall is marked as auditable and an audit record | 648 | * syscall, then the syscall is marked as auditable and an audit record |
645 | * will be written at syscall exit. If there is no associated task, tsk | 649 | * will be written at syscall exit. If there is no associated task, tsk |
646 | * should be NULL. */ | 650 | * should be NULL. */ |
647 | struct audit_buffer *audit_log_start(struct audit_context *ctx, int type) | 651 | |
652 | struct audit_buffer *audit_log_start(struct audit_context *ctx, int gfp_mask, | ||
653 | int type) | ||
648 | { | 654 | { |
649 | struct audit_buffer *ab = NULL; | 655 | struct audit_buffer *ab = NULL; |
650 | struct timespec t; | 656 | struct timespec t; |
651 | unsigned int serial; | 657 | unsigned int serial; |
658 | int reserve; | ||
652 | 659 | ||
653 | if (!audit_initialized) | 660 | if (!audit_initialized) |
654 | return NULL; | 661 | return NULL; |
655 | 662 | ||
656 | if (audit_backlog_limit | 663 | if (gfp_mask & __GFP_WAIT) |
657 | && skb_queue_len(&audit_skb_queue) > audit_backlog_limit) { | 664 | reserve = 0; |
665 | else | ||
666 | reserve = 5; /* Allow atomic callers to go up to five | ||
667 | entries over the normal backlog limit */ | ||
668 | |||
669 | while (audit_backlog_limit | ||
670 | && skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) { | ||
671 | if (gfp_mask & __GFP_WAIT) { | ||
672 | int ret = 1; | ||
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 | ret = schedule_timeout(HZ * 60); | ||
681 | |||
682 | __set_current_state(TASK_RUNNING); | ||
683 | remove_wait_queue(&audit_backlog_wait, &wait); | ||
684 | if (ret) | ||
685 | continue; | ||
686 | } | ||
658 | if (audit_rate_check()) | 687 | if (audit_rate_check()) |
659 | printk(KERN_WARNING | 688 | printk(KERN_WARNING |
660 | "audit: audit_backlog=%d > " | 689 | "audit: audit_backlog=%d > " |
@@ -665,7 +694,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, int type) | |||
665 | return NULL; | 694 | return NULL; |
666 | } | 695 | } |
667 | 696 | ||
668 | ab = audit_buffer_alloc(ctx, GFP_ATOMIC, type); | 697 | ab = audit_buffer_alloc(ctx, gfp_mask, type); |
669 | if (!ab) { | 698 | if (!ab) { |
670 | audit_log_lost("out of memory in audit_log_start"); | 699 | audit_log_lost("out of memory in audit_log_start"); |
671 | return NULL; | 700 | return NULL; |
@@ -689,7 +718,7 @@ static inline int audit_expand(struct audit_buffer *ab, int extra) | |||
689 | { | 718 | { |
690 | struct sk_buff *skb = ab->skb; | 719 | struct sk_buff *skb = ab->skb; |
691 | int ret = pskb_expand_head(skb, skb_headroom(skb), extra, | 720 | int ret = pskb_expand_head(skb, skb_headroom(skb), extra, |
692 | GFP_ATOMIC); | 721 | ab->gfp_mask); |
693 | if (ret < 0) { | 722 | if (ret < 0) { |
694 | audit_log_lost("out of memory in audit_expand"); | 723 | audit_log_lost("out of memory in audit_expand"); |
695 | return 0; | 724 | return 0; |
@@ -808,7 +837,7 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, | |||
808 | audit_log_format(ab, " %s", prefix); | 837 | audit_log_format(ab, " %s", prefix); |
809 | 838 | ||
810 | /* We will allow 11 spaces for ' (deleted)' to be appended */ | 839 | /* We will allow 11 spaces for ' (deleted)' to be appended */ |
811 | path = kmalloc(PATH_MAX+11, GFP_KERNEL); | 840 | path = kmalloc(PATH_MAX+11, ab->gfp_mask); |
812 | if (!path) { | 841 | if (!path) { |
813 | audit_log_format(ab, "<no memory>"); | 842 | audit_log_format(ab, "<no memory>"); |
814 | return; | 843 | return; |
@@ -849,12 +878,13 @@ void audit_log_end(struct audit_buffer *ab) | |||
849 | /* Log an audit record. This is a convenience function that calls | 878 | /* Log an audit record. This is a convenience function that calls |
850 | * audit_log_start, audit_log_vformat, and audit_log_end. It may be | 879 | * audit_log_start, audit_log_vformat, and audit_log_end. It may be |
851 | * called in any context. */ | 880 | * called in any context. */ |
852 | void audit_log(struct audit_context *ctx, int type, const char *fmt, ...) | 881 | void audit_log(struct audit_context *ctx, int gfp_mask, int type, |
882 | const char *fmt, ...) | ||
853 | { | 883 | { |
854 | struct audit_buffer *ab; | 884 | struct audit_buffer *ab; |
855 | va_list args; | 885 | va_list args; |
856 | 886 | ||
857 | ab = audit_log_start(ctx, type); | 887 | ab = audit_log_start(ctx, gfp_mask, type); |
858 | if (ab) { | 888 | if (ab) { |
859 | va_start(args, fmt); | 889 | va_start(args, fmt); |
860 | audit_log_vformat(ab, fmt, args); | 890 | audit_log_vformat(ab, fmt, args); |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index fc858b0c044a..f463fd230846 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -346,7 +346,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
346 | } | 346 | } |
347 | listnr = entry->rule.flags & ~AUDIT_FILTER_PREPEND; | 347 | listnr = entry->rule.flags & ~AUDIT_FILTER_PREPEND; |
348 | audit_add_rule(entry, &audit_filter_list[listnr]); | 348 | audit_add_rule(entry, &audit_filter_list[listnr]); |
349 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 349 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
350 | "auid=%u added an audit rule\n", loginuid); | 350 | "auid=%u added an audit rule\n", loginuid); |
351 | break; | 351 | break; |
352 | case AUDIT_DEL: | 352 | case AUDIT_DEL: |
@@ -356,7 +356,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
356 | 356 | ||
357 | err = audit_del_rule(data, &audit_filter_list[listnr]); | 357 | err = audit_del_rule(data, &audit_filter_list[listnr]); |
358 | if (!err) | 358 | if (!err) |
359 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 359 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, |
360 | "auid=%u removed an audit rule\n", loginuid); | 360 | "auid=%u removed an audit rule\n", loginuid); |
361 | break; | 361 | break; |
362 | default: | 362 | default: |
@@ -756,7 +756,7 @@ static void audit_log_exit(struct audit_context *context) | |||
756 | struct audit_buffer *ab; | 756 | struct audit_buffer *ab; |
757 | struct audit_aux_data *aux; | 757 | struct audit_aux_data *aux; |
758 | 758 | ||
759 | ab = audit_log_start(context, AUDIT_SYSCALL); | 759 | ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL); |
760 | if (!ab) | 760 | if (!ab) |
761 | return; /* audit_panic has been called */ | 761 | return; /* audit_panic has been called */ |
762 | audit_log_format(ab, "arch=%x syscall=%d", | 762 | audit_log_format(ab, "arch=%x syscall=%d", |
@@ -788,7 +788,7 @@ static void audit_log_exit(struct audit_context *context) | |||
788 | 788 | ||
789 | for (aux = context->aux; aux; aux = aux->next) { | 789 | for (aux = context->aux; aux; aux = aux->next) { |
790 | 790 | ||
791 | ab = audit_log_start(context, aux->type); | 791 | ab = audit_log_start(context, GFP_KERNEL, aux->type); |
792 | if (!ab) | 792 | if (!ab) |
793 | continue; /* audit_panic has been called */ | 793 | continue; /* audit_panic has been called */ |
794 | 794 | ||
@@ -825,14 +825,14 @@ static void audit_log_exit(struct audit_context *context) | |||
825 | } | 825 | } |
826 | 826 | ||
827 | if (context->pwd && context->pwdmnt) { | 827 | if (context->pwd && context->pwdmnt) { |
828 | ab = audit_log_start(context, AUDIT_CWD); | 828 | ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD); |
829 | if (ab) { | 829 | if (ab) { |
830 | audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt); | 830 | audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt); |
831 | audit_log_end(ab); | 831 | audit_log_end(ab); |
832 | } | 832 | } |
833 | } | 833 | } |
834 | for (i = 0; i < context->name_count; i++) { | 834 | for (i = 0; i < context->name_count; i++) { |
835 | ab = audit_log_start(context, AUDIT_PATH); | 835 | ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH); |
836 | if (!ab) | 836 | if (!ab) |
837 | continue; /* audit_panic has been called */ | 837 | continue; /* audit_panic has been called */ |
838 | 838 | ||
@@ -1118,7 +1118,7 @@ int audit_set_loginuid(struct task_struct *task, uid_t loginuid) | |||
1118 | if (task->audit_context) { | 1118 | if (task->audit_context) { |
1119 | struct audit_buffer *ab; | 1119 | struct audit_buffer *ab; |
1120 | 1120 | ||
1121 | ab = audit_log_start(NULL, AUDIT_LOGIN); | 1121 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN); |
1122 | if (ab) { | 1122 | if (ab) { |
1123 | audit_log_format(ab, "login pid=%d uid=%u " | 1123 | audit_log_format(ab, "login pid=%d uid=%u " |
1124 | "old auid=%u new auid=%u", | 1124 | "old auid=%u new auid=%u", |