diff options
author | Chris Wright <chrisw@osdl.org> | 2005-05-06 10:54:17 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-05-06 10:54:17 -0400 |
commit | 8fc6115c2a04099a6e846dc0b2d85cba43821b54 (patch) | |
tree | 6dc6bf0f59f6ada9ed42c79b0e641f8668a9bf0b | |
parent | 16e1904e694d459ec2ca9b33c22b818eaaa4c63f (diff) |
AUDIT: expand audit tmp buffer as needed
Introduce audit_expand and make the audit_buffer use a dynamic buffer
which can be resized. When audit buffer is moved to skb it will not
be fragmented across skb's, so we can eliminate the sklist in the
audit_buffer. During audit_log_move, we simply copy the full buffer
into a single skb, and then audit_log_drain sends it on.
Signed-off-by: Chris Wright <chrisw@osdl.org>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r-- | kernel/audit.c | 139 |
1 files changed, 79 insertions, 60 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index e5bdba3e3ae1..c6e31d209c41 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -136,14 +136,11 @@ static DECLARE_MUTEX(audit_netlink_sem); | |||
136 | * use simultaneously. */ | 136 | * use simultaneously. */ |
137 | struct audit_buffer { | 137 | struct audit_buffer { |
138 | struct list_head list; | 138 | struct list_head list; |
139 | struct sk_buff_head sklist; /* formatted skbs ready to send */ | 139 | struct sk_buff *skb; /* formatted skb ready to send */ |
140 | struct audit_context *ctx; /* NULL or associated context */ | 140 | struct audit_context *ctx; /* NULL or associated context */ |
141 | int len; /* used area of tmp */ | 141 | int len; /* used area of tmp */ |
142 | char tmp[AUDIT_BUFSIZ]; | 142 | int size; /* size of tmp */ |
143 | 143 | char *tmp; | |
144 | /* Pointer to header and contents */ | ||
145 | struct nlmsghdr *nlh; | ||
146 | int total; | ||
147 | int type; | 144 | int type; |
148 | int pid; | 145 | int pid; |
149 | }; | 146 | }; |
@@ -488,55 +485,47 @@ static void audit_receive(struct sock *sk, int length) | |||
488 | static void audit_log_move(struct audit_buffer *ab) | 485 | static void audit_log_move(struct audit_buffer *ab) |
489 | { | 486 | { |
490 | struct sk_buff *skb; | 487 | struct sk_buff *skb; |
488 | struct nlmsghdr *nlh; | ||
491 | char *start; | 489 | char *start; |
492 | int extra = ab->nlh ? 0 : NLMSG_SPACE(0); | 490 | int len = NLMSG_SPACE(0) + ab->len + 1; |
493 | 491 | ||
494 | /* possible resubmission */ | 492 | /* possible resubmission */ |
495 | if (ab->len == 0) | 493 | if (ab->skb) |
496 | return; | 494 | return; |
497 | 495 | ||
498 | skb = skb_peek_tail(&ab->sklist); | 496 | skb = alloc_skb(len, GFP_ATOMIC); |
499 | if (!skb || skb_tailroom(skb) <= ab->len + extra) { | 497 | if (!skb) { |
500 | skb = alloc_skb(2 * ab->len + extra, GFP_ATOMIC); | 498 | /* Lose information in ab->tmp */ |
501 | if (!skb) { | 499 | audit_log_lost("out of memory in audit_log_move"); |
502 | ab->len = 0; /* Lose information in ab->tmp */ | 500 | return; |
503 | audit_log_lost("out of memory in audit_log_move"); | ||
504 | return; | ||
505 | } | ||
506 | __skb_queue_tail(&ab->sklist, skb); | ||
507 | if (!ab->nlh) | ||
508 | ab->nlh = (struct nlmsghdr *)skb_put(skb, | ||
509 | NLMSG_SPACE(0)); | ||
510 | } | 501 | } |
502 | ab->skb = skb; | ||
503 | nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(0)); | ||
504 | nlh->nlmsg_type = ab->type; | ||
505 | nlh->nlmsg_len = ab->len; | ||
506 | nlh->nlmsg_flags = 0; | ||
507 | nlh->nlmsg_pid = ab->pid; | ||
508 | nlh->nlmsg_seq = 0; | ||
511 | start = skb_put(skb, ab->len); | 509 | start = skb_put(skb, ab->len); |
512 | memcpy(start, ab->tmp, ab->len); | 510 | memcpy(start, ab->tmp, ab->len); |
513 | ab->len = 0; | ||
514 | } | 511 | } |
515 | 512 | ||
516 | /* Iterate over the skbuff in the audit_buffer, sending their contents | 513 | /* Iterate over the skbuff in the audit_buffer, sending their contents |
517 | * to user space. */ | 514 | * to user space. */ |
518 | static inline int audit_log_drain(struct audit_buffer *ab) | 515 | static inline int audit_log_drain(struct audit_buffer *ab) |
519 | { | 516 | { |
520 | struct sk_buff *skb; | 517 | struct sk_buff *skb = ab->skb; |
521 | 518 | ||
522 | while ((skb = skb_dequeue(&ab->sklist))) { | 519 | if (skb) { |
523 | int retval = 0; | 520 | int retval = 0; |
524 | 521 | ||
525 | if (audit_pid) { | 522 | if (audit_pid) { |
526 | if (ab->nlh) { | ||
527 | ab->nlh->nlmsg_len = ab->total; | ||
528 | ab->nlh->nlmsg_type = ab->type; | ||
529 | ab->nlh->nlmsg_flags = 0; | ||
530 | ab->nlh->nlmsg_seq = 0; | ||
531 | ab->nlh->nlmsg_pid = ab->pid; | ||
532 | } | ||
533 | skb_get(skb); /* because netlink_* frees */ | 523 | skb_get(skb); /* because netlink_* frees */ |
534 | retval = netlink_unicast(audit_sock, skb, audit_pid, | 524 | retval = netlink_unicast(audit_sock, skb, audit_pid, |
535 | MSG_DONTWAIT); | 525 | MSG_DONTWAIT); |
536 | } | 526 | } |
537 | if (retval == -EAGAIN && | 527 | if (retval == -EAGAIN && |
538 | (atomic_read(&audit_backlog)) < audit_backlog_limit) { | 528 | (atomic_read(&audit_backlog)) < audit_backlog_limit) { |
539 | skb_queue_head(&ab->sklist, skb); | ||
540 | audit_log_end_irq(ab); | 529 | audit_log_end_irq(ab); |
541 | return 1; | 530 | return 1; |
542 | } | 531 | } |
@@ -550,13 +539,12 @@ static inline int audit_log_drain(struct audit_buffer *ab) | |||
550 | audit_log_lost("netlink socket too busy"); | 539 | audit_log_lost("netlink socket too busy"); |
551 | } | 540 | } |
552 | if (!audit_pid) { /* No daemon */ | 541 | if (!audit_pid) { /* No daemon */ |
553 | int offset = ab->nlh ? NLMSG_SPACE(0) : 0; | 542 | int offset = NLMSG_SPACE(0); |
554 | int len = skb->len - offset; | 543 | int len = skb->len - offset; |
555 | skb->data[offset + len] = '\0'; | 544 | skb->data[offset + len] = '\0'; |
556 | printk(KERN_ERR "%s\n", skb->data + offset); | 545 | printk(KERN_ERR "%s\n", skb->data + offset); |
557 | } | 546 | } |
558 | kfree_skb(skb); | 547 | kfree_skb(skb); |
559 | ab->nlh = NULL; | ||
560 | } | 548 | } |
561 | return 0; | 549 | return 0; |
562 | } | 550 | } |
@@ -624,6 +612,10 @@ static void audit_buffer_free(struct audit_buffer *ab) | |||
624 | { | 612 | { |
625 | unsigned long flags; | 613 | unsigned long flags; |
626 | 614 | ||
615 | if (!ab) | ||
616 | return; | ||
617 | |||
618 | kfree(ab->tmp); | ||
627 | atomic_dec(&audit_backlog); | 619 | atomic_dec(&audit_backlog); |
628 | spin_lock_irqsave(&audit_freelist_lock, flags); | 620 | spin_lock_irqsave(&audit_freelist_lock, flags); |
629 | if (++audit_freelist_count > AUDIT_MAXFREE) | 621 | if (++audit_freelist_count > AUDIT_MAXFREE) |
@@ -633,7 +625,8 @@ static void audit_buffer_free(struct audit_buffer *ab) | |||
633 | spin_unlock_irqrestore(&audit_freelist_lock, flags); | 625 | spin_unlock_irqrestore(&audit_freelist_lock, flags); |
634 | } | 626 | } |
635 | 627 | ||
636 | static struct audit_buffer * audit_buffer_alloc(int gfp_mask) | 628 | static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, |
629 | int gfp_mask) | ||
637 | { | 630 | { |
638 | unsigned long flags; | 631 | unsigned long flags; |
639 | struct audit_buffer *ab = NULL; | 632 | struct audit_buffer *ab = NULL; |
@@ -650,11 +643,24 @@ static struct audit_buffer * audit_buffer_alloc(int gfp_mask) | |||
650 | if (!ab) { | 643 | if (!ab) { |
651 | ab = kmalloc(sizeof(*ab), GFP_ATOMIC); | 644 | ab = kmalloc(sizeof(*ab), GFP_ATOMIC); |
652 | if (!ab) | 645 | if (!ab) |
653 | goto out; | 646 | goto err; |
654 | } | 647 | } |
655 | atomic_inc(&audit_backlog); | 648 | atomic_inc(&audit_backlog); |
656 | out: | 649 | |
650 | ab->tmp = kmalloc(AUDIT_BUFSIZ, GFP_ATOMIC); | ||
651 | if (!ab->tmp) | ||
652 | goto err; | ||
653 | |||
654 | ab->skb = NULL; | ||
655 | ab->ctx = ctx; | ||
656 | ab->len = 0; | ||
657 | ab->size = AUDIT_BUFSIZ; | ||
658 | ab->type = AUDIT_KERNEL; | ||
659 | ab->pid = 0; | ||
657 | return ab; | 660 | return ab; |
661 | err: | ||
662 | audit_buffer_free(ab); | ||
663 | return NULL; | ||
658 | } | 664 | } |
659 | 665 | ||
660 | /* Obtain an audit buffer. This routine does locking to obtain the | 666 | /* Obtain an audit buffer. This routine does locking to obtain the |
@@ -684,21 +690,12 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) | |||
684 | return NULL; | 690 | return NULL; |
685 | } | 691 | } |
686 | 692 | ||
687 | ab = audit_buffer_alloc(GFP_ATOMIC); | 693 | ab = audit_buffer_alloc(ctx, GFP_ATOMIC); |
688 | if (!ab) { | 694 | if (!ab) { |
689 | audit_log_lost("out of memory in audit_log_start"); | 695 | audit_log_lost("out of memory in audit_log_start"); |
690 | return NULL; | 696 | return NULL; |
691 | } | 697 | } |
692 | 698 | ||
693 | skb_queue_head_init(&ab->sklist); | ||
694 | |||
695 | ab->ctx = ctx; | ||
696 | ab->len = 0; | ||
697 | ab->nlh = NULL; | ||
698 | ab->total = 0; | ||
699 | ab->type = AUDIT_KERNEL; | ||
700 | ab->pid = 0; | ||
701 | |||
702 | #ifdef CONFIG_AUDITSYSCALL | 699 | #ifdef CONFIG_AUDITSYSCALL |
703 | if (ab->ctx) | 700 | if (ab->ctx) |
704 | audit_get_stamp(ab->ctx, &t, &serial); | 701 | audit_get_stamp(ab->ctx, &t, &serial); |
@@ -713,6 +710,27 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) | |||
713 | return ab; | 710 | return ab; |
714 | } | 711 | } |
715 | 712 | ||
713 | /** | ||
714 | * audit_expand - expand tmp buffer in the audit buffer | ||
715 | * @ab: audit_buffer | ||
716 | * | ||
717 | * Returns 0 (no space) on failed expansion, or available space if | ||
718 | * successful. | ||
719 | */ | ||
720 | static inline int audit_expand(struct audit_buffer *ab) | ||
721 | { | ||
722 | char *tmp; | ||
723 | int len = ab->size + AUDIT_BUFSIZ; | ||
724 | |||
725 | tmp = kmalloc(len, GFP_ATOMIC); | ||
726 | if (!tmp) | ||
727 | return 0; | ||
728 | memcpy(tmp, ab->tmp, ab->len); | ||
729 | kfree(ab->tmp); | ||
730 | ab->tmp = tmp; | ||
731 | ab->size = len; | ||
732 | return ab->size - ab->len; | ||
733 | } | ||
716 | 734 | ||
717 | /* Format an audit message into the audit buffer. If there isn't enough | 735 | /* Format an audit message into the audit buffer. If there isn't enough |
718 | * room in the audit buffer, more room will be allocated and vsnprint | 736 | * room in the audit buffer, more room will be allocated and vsnprint |
@@ -726,22 +744,25 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, | |||
726 | if (!ab) | 744 | if (!ab) |
727 | return; | 745 | return; |
728 | 746 | ||
729 | avail = sizeof(ab->tmp) - ab->len; | 747 | avail = ab->size - ab->len; |
730 | if (avail <= 0) { | 748 | if (avail <= 0) { |
731 | audit_log_move(ab); | 749 | avail = audit_expand(ab); |
732 | avail = sizeof(ab->tmp) - ab->len; | 750 | if (!avail) |
751 | goto out; | ||
733 | } | 752 | } |
734 | len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); | 753 | len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); |
735 | if (len >= avail) { | 754 | if (len >= avail) { |
736 | /* The printk buffer is 1024 bytes long, so if we get | 755 | /* The printk buffer is 1024 bytes long, so if we get |
737 | * here and AUDIT_BUFSIZ is at least 1024, then we can | 756 | * here and AUDIT_BUFSIZ is at least 1024, then we can |
738 | * log everything that printk could have logged. */ | 757 | * log everything that printk could have logged. */ |
739 | audit_log_move(ab); | 758 | avail = audit_expand(ab); |
740 | avail = sizeof(ab->tmp) - ab->len; | 759 | if (!avail) |
741 | len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); | 760 | goto out; |
761 | len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); | ||
742 | } | 762 | } |
743 | ab->len += (len < avail) ? len : avail; | 763 | ab->len += (len < avail) ? len : avail; |
744 | ab->total += (len < avail) ? len : avail; | 764 | out: |
765 | return; | ||
745 | } | 766 | } |
746 | 767 | ||
747 | /* Format a message into the audit buffer. All the work is done in | 768 | /* Format a message into the audit buffer. All the work is done in |
@@ -789,21 +810,19 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix, | |||
789 | char *p; | 810 | char *p; |
790 | int len, avail; | 811 | int len, avail; |
791 | 812 | ||
792 | if (prefix) audit_log_format(ab, " %s", prefix); | 813 | if (prefix) |
814 | audit_log_format(ab, " %s", prefix); | ||
793 | 815 | ||
794 | if (ab->len > 128) | 816 | avail = ab->size - ab->len; |
795 | audit_log_move(ab); | ||
796 | avail = sizeof(ab->tmp) - ab->len; | ||
797 | p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail); | 817 | p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail); |
798 | if (IS_ERR(p)) { | 818 | if (IS_ERR(p)) { |
799 | /* FIXME: can we save some information here? */ | 819 | /* FIXME: can we save some information here? */ |
800 | audit_log_format(ab, "<toolong>"); | 820 | audit_log_format(ab, "<toolong>"); |
801 | } else { | 821 | } else { |
802 | /* path isn't at start of buffer */ | 822 | /* path isn't at start of buffer */ |
803 | len = (ab->tmp + sizeof(ab->tmp) - 1) - p; | 823 | len = (ab->tmp + ab->size - 1) - p; |
804 | memmove(ab->tmp + ab->len, p, len); | 824 | memmove(ab->tmp + ab->len, p, len); |
805 | ab->len += len; | 825 | ab->len += len; |
806 | ab->total += len; | ||
807 | } | 826 | } |
808 | } | 827 | } |
809 | 828 | ||