[PATCH] Audit: save audit_backlog_limit audit messages in case auditd comes back

Eric Paris eparis at redhat.com
Thu Mar 27 21:37:44 UTC 2008


This patch causes the kernel audit subsystem to store up to
audit_backlog_limit messages for use by auditd if it ever appears
sometime in the future in userspace.  This is useful to collect audit
messages during bootup and even when auditd is stopped.  This is NOT a
reliable mechanism, it does not ever call audit_panic, nor should it.
The messages are still sent to printk/syslog as usual and if too many
messages appear they will be silently discarded.

If auditd never starts the kernel will hold by default up to 64 messages
in memory forever.

Signed-off-by: Eric Paris <eparis at redhat.com>

---

[root at dhcp231-146 ~]# echo > /var/log/audit/audit.log 
[root at dhcp231-146 ~]# service auditd stop
Stopping auditd:                                           [  OK  ]
[root at dhcp231-146 ~]# load_policy
[root at dhcp231-146 ~]# cat /var/log/audit/audit.log 

type=DAEMON_END msg=audit(1206653123.111:2388): auditd normal halt, sending auid=0 pid=6006 subj=unconfined_u:system_r:initrc_t:s0 res=success
[root at dhcp231-146 ~]# service auditd start
Starting auditd:                                           [  OK  ]
[root at dhcp231-146 ~]# cat /var/log/audit/audit.log 

type=DAEMON_END msg=audit(1206653123.111:2388): auditd normal halt, sending auid=0 pid=6006 subj=unconfined_u:system_r:initrc_t:s0 res=success
type=DAEMON_START msg=audit(1206653139.737:8211): auditd start, ver=1.6.9 format=raw kernel=2.6.25-rc7 auid=0 pid=6031 res=success
type=CONFIG_CHANGE msg=audit(1206653123.154:26): audit_pid=0 old=1789 by auid=4294967295 subj=system_u:system_r:auditd_t:s0 res=1
type=MAC_POLICY_LOAD msg=audit(1206653127.614:27): policy loaded auid=0 ses=2
type=SYSCALL msg=audit(1206653127.614:27): arch=c000003e syscall=1 success=yes exit=3758310 a0=4 a1=7f25dd44d000 a2=3958e6 a3=7fffeae26ee0 items=0 ppid=5418 pid=6019 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=2 comm="load_policy" exe="/usr/sbin/load_policy" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
type=USER_AVC msg=audit(1206653130.800:28): user pid=1803 uid=81 auid=4294967295 subj=system_u:system_r:system_dbusd_t:s0-s0:c0.c1023 msg='avc:  received policyload notice (seqno=2) : exe="?" (sauid=81, hostname=?, addr=?, terminal=?)'
type=CONFIG_CHANGE msg=audit(1206653139.738:29): audit_pid=6031 old=0 by auid=0 subj=unconfined_u:system_r:auditd_t:s0 res=1
type=CONFIG_CHANGE msg=audit(1206653139.839:30): audit_enabled=1 old=1 by auid=0 subj=unconfined_u:system_r:auditd_t:s0 res=1
type=CONFIG_CHANGE msg=audit(1206653139.939:31): audit_backlog_limit=8192 old=8192 by auid=0 subj=unconfined_u:system_r:auditctl_t:s0 res=1
type=CONFIG_CHANGE msg=audit(1206653139.940:32): audit_failure=1 old=1 by auid=0 subj=unconfined_u:system_r:auditctl_t:s0 res=1

 kernel/audit.c |   89 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 68 insertions(+), 21 deletions(-)

diff --git a/kernel/audit.c b/kernel/audit.c
index be55cb5..aa2ce91 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -127,6 +127,8 @@ static int	   audit_freelist_count;
 static LIST_HEAD(audit_freelist);
 
 static struct sk_buff_head audit_skb_queue;
+/* queue of skbs to send to auditd when/if it comes back */
+static struct sk_buff_head audit_skb_hold_queue;
 static struct task_struct *kauditd_task;
 static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
 static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
@@ -344,30 +346,70 @@ static int audit_set_failure(int state, uid_t loginuid, u32 sid)
 				      loginuid, sid);
 }
 
+/*
+ * Queue skbs to be sent to auditd when/if it comes back.  These skbs should
+ * already have been sent via prink/syslog and so if these messages are dropped
+ * it is not a huge concern.  This is just nice to get audit messages during
+ * boot before auditd is running or messages generated while auditd is stopped
+ */
+static void audit_hold_skb(struct sk_buff *skb)
+{
+	if (skb_queue_len(&audit_skb_hold_queue) < audit_backlog_limit)
+		skb_queue_tail(&audit_skb_hold_queue, skb);
+	else
+		kfree_skb(skb);
+}
+
+static void kauditd_send_skb(struct sk_buff *skb)
+{
+	int err;
+	/* take a reference in case we can't send it and we want to hold it */
+	skb_get(skb);
+	err = netlink_unicast(audit_sock, skb, audit_nlk_pid, 0);
+	if (err < 0) {
+		BUG_ON(err != -ECONNREFUSED); /* Shoudn't happen */
+		printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid);
+		audit_log_lost("auditd dissapeared\n");
+		audit_pid = 0;
+		/* we might get lucky and get this in the next auditd */
+		audit_hold_skb(skb);
+	} else
+		/* drop the extra reference if sent ok */
+		kfree_skb(skb);
+}
+
 static int kauditd_thread(void *dummy)
 {
 	struct sk_buff *skb;
 
 	set_freezable();
 	while (!kthread_should_stop()) {
+		/*
+		 * if auditd just started drain the queue of messages already
+		 * sent to syslog/printk.
+		 */
+		if (audit_pid) {
+			skb = skb_dequeue(&audit_skb_hold_queue);
+			if (unlikely(skb)) {
+				while(skb && audit_pid) {
+					kauditd_send_skb(skb);
+					skb = skb_dequeue(&audit_skb_hold_queue);
+				}
+			}
+		}
+
 		skb = skb_dequeue(&audit_skb_queue);
 		wake_up(&audit_backlog_wait);
 		if (skb) {
-			if (audit_pid) {
-				int err = netlink_unicast(audit_sock, skb, audit_nlk_pid, 0);
-				if (err < 0) {
-					BUG_ON(err != -ECONNREFUSED); /* Shoudn't happen */
-					printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid);
-					audit_log_lost("auditd dissapeared\n");
-					audit_pid = 0;
-				}
-			} else {
+			if (audit_pid)
+				kauditd_send_skb(skb);
+			else {
 				if (printk_ratelimit())
-					printk(KERN_NOTICE "%s\n", skb->data +
-						NLMSG_SPACE(0));
+					printk(KERN_NOTICE "%s\n", skb->data + NLMSG_SPACE(0));
 				else
 					audit_log_lost("printk limit exceeded\n");
-				kfree_skb(skb);
+
+				audit_hold_skb(skb);
 			}
 		} else {
 			DECLARE_WAITQUEUE(wait, current);
@@ -877,6 +919,7 @@ static int __init audit_init(void)
 		audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
 
 	skb_queue_head_init(&audit_skb_queue);
+	skb_queue_head_init(&audit_skb_hold_queue);
 	audit_initialized = 1;
 	audit_enabled = audit_default;
 	audit_ever_enabled |= !!audit_default;
@@ -1359,19 +1402,23 @@ void audit_log_end(struct audit_buffer *ab)
 		audit_log_lost("rate limit exceeded");
 	} else {
 		struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
+		nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0);
+
 		if (audit_pid) {
-			nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0);
 			skb_queue_tail(&audit_skb_queue, ab->skb);
-			ab->skb = NULL;
 			wake_up_interruptible(&kauditd_wait);
-		} else if (nlh->nlmsg_type != AUDIT_EOE) {
-			if (printk_ratelimit()) {
-				printk(KERN_NOTICE "type=%d %s\n",
-					nlh->nlmsg_type,
-					ab->skb->data + NLMSG_SPACE(0));
-			} else
-				audit_log_lost("printk limit exceeded\n");
+		} else {
+			if (nlh->nlmsg_type != AUDIT_EOE) {
+				if (printk_ratelimit()) {
+					printk(KERN_NOTICE "type=%d %s\n",
+						nlh->nlmsg_type,
+						ab->skb->data + NLMSG_SPACE(0));
+				} else
+					audit_log_lost("printk limit exceeded\n");
+			}
+			audit_hold_skb(ab->skb);
 		}
+		ab->skb = NULL;
 	}
 	audit_buffer_free(ab);
 }





More information about the Linux-audit mailing list