From e350b1d3316ae449161b8a5b631afaf89c642703 Mon Sep 17 00:00:00 2001
From: HATAYAMA Daisuke <d.hatayama@jp.fujitsu.com>
Date: Mon, 13 Mar 2017 14:42:38 +0900
Subject: [PATCH] Add a feature to dump audit logs in log command as -a option

This commits adds a feature tu dump audit logs in log command as -a
option. Here is an output for example:

    crash> log -a
    type=1320 audit(1489384479.809:4342):
    type=1300 audit(1489384479.809:4343): arch=c000003e syscall=0 success=yes exit=0 a0=4 a1=7f84154a2000 a2=400 a3=22 items=0 ppid=2560 pid=2591 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 comm="pidof" exe="/usr/sbin/killall5" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
    type=1320 audit(1489384479.809:4343):
    type=1300 audit(1489384479.809:4344): arch=c000003e syscall=3 success=yes exit=0 a0=4 a1=1 a2=8 a3=0 items=0 ppid=2560 pid=2591 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 comm="pidof" exe="/usr/sbin/killall5" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
    type=1320 audit(1489384479.809:4344):
    type=1300 audit(1489384479.809:4345): arch=c000003e syscall=11 success=yes exit=0 a0=7f84154a2000 a1=1000 a2=0 a3=0 items=0 ppid=2560 pid=2591 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 comm="pidof" exe="/usr/sbin/killall5" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
    type=1320 audit(1489384479.809:4345):
    type=1300 audit(1489384479.809:4346): arch=c000003e syscall=2 success=yes exit=4 a0=7ffcfd20f5a0 a1=0 a2=1b6 a3=24 items=1 ppid=2560 pid=2591 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 comm="pidof" exe="/usr/sbin/killall5" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
    type=1307 audit(1489384479.809:4346):  cwd="/proc"
    ...

What's done here is simply to dump three kinds of audit's skb queue
buffer: skb queue, skb hold queue and skb retry queue.

The skb queue was introduced by the following commit merged in linux
v2.6.13 as part of implementing kauditd kernel thread, so this command
should work well on the kernel versions equal to or larger than
v2.6.13.

    # git describe b7d1125817c9a46cc46f57db89d9c195e7af22f8
    v2.6.12-rc4-48-gb7d1125
    # git log -1 -p b7d1125817c9a46cc46f57db89d9c195e7af22f8
    commit b7d1125817c9a46cc46f57db89d9c195e7af22f8
    Author: David Woodhouse <dwmw2@shinybook.infradead.org>
    Date:   Thu May 19 10:56:58 2005 +0100

        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>

The skb hold queue was introduced by:

    # git log -1 -p f3d357b0
    commit f3d357b092956959563398b59ef2fdd10aea387d
    Author: Eric Paris <eparis@redhat.com>
    Date:   Fri Apr 18 10:02:28 2008 -0400

        Audit: save audit_backlog_limit audit messages in case auditd comes back

and the skb retry queue was by:

    # git log -1 -p c6480207
    commit c6480207fdf7b61de216ee23e93eac0a6878fa74
    Author: Paul Moore <paul@paul-moore.com>
    Date:   Tue Nov 29 16:53:25 2016 -0500

        audit: rework the audit queue handling

Both are used to keep skb buffers temporarily when kauditd picks up
skb from the skb queue but fails to send it to user-space auditd
process due to some issue.

The contents in each queue is displayed in the order:

   skb hold queue => skb retry queue => skb queue

This is the same logic as the kernel does.
---
 defs.h    |   11 +++++
 help.c    |   34 ++++++++++++++-
 kernel.c  |  144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 symbols.c |   18 ++++++++
 4 files changed, 205 insertions(+), 2 deletions(-)

diff --git a/defs.h b/defs.h
index edccdc7..d8e4284 100644
--- a/defs.h
+++ b/defs.h
@@ -1982,6 +1982,12 @@ struct offset_table {                    /* stash of commonly-used offsets */
 	long radix_tree_node_shift;
 	long kmem_cache_red_left_pad;
 	long inactive_task_frame_ret_addr;
+	long sk_buff_head_next;
+	long sk_buff_head_qlen;
+	long sk_buff_next;
+	long sk_buff_len;
+	long sk_buff_data;
+	long nlmsghdr_nlmsg_type;
 };
 
 struct size_table {         /* stash of commonly-used sizes */
@@ -2126,6 +2132,10 @@ struct size_table {         /* stash of commonly-used sizes */
 	long task_struct_flags;
 	long timer_base;
 	long taint_flag;
+	long nlmsghdr;
+	long nlmsghdr_nlmsg_type;
+	long sk_buff_head_qlen;
+	long sk_buff_len;
 };
 
 struct array_table {
@@ -5316,6 +5326,7 @@ void dump_log(int);
 #define SHOW_LOG_LEVEL (0x1)
 #define SHOW_LOG_DICT  (0x2)
 #define SHOW_LOG_TEXT  (0x4)
+#define SHOW_LOG_AUDIT (0x8)
 void set_cpu(int);
 void clear_machdep_cache(void);
 struct stack_hook *gather_text_list(struct bt_info *);
diff --git a/help.c b/help.c
index b43ef50..b2f708f 100644
--- a/help.c
+++ b/help.c
@@ -3387,7 +3387,7 @@ NULL
 char *help_log[] = {
 "log",
 "dump system message buffer",
-"[-tdm]",
+"[-tdma]",
 "  This command dumps the kernel log_buf contents in chronological order.  The",
 "  command supports the older log_buf formats, which may or may not contain a",
 "  timestamp inserted prior to each message, as well as the newer variable-length", 
@@ -3402,6 +3402,8 @@ char *help_log[] = {
 "        the variable-length record format, the level will be displayed in ",
 "        hexadecimal, and depending upon the kernel version, also contains the",
 "        facility or flags bits.",
+"    -a  Dump the audit logs left in the kernel audit buffers without being",
+"        written out to user-space audit process.",
 " ",        
 "\nEXAMPLES",
 "  Dump the kernel message buffer:\n",
@@ -3494,6 +3496,36 @@ char *help_log[] = {
 "    SUBSYSTEM=pci",
 "    DEVICE=+pci:0000:ff:03.1",
 "    ...",
+" ",
+"  Dump the kernel audit logs:\n",
+"    %s> log -a",
+"    type=1320 audit(1489384479.809:4342):",
+"    type=1300 audit(1489384479.809:4343): arch=c000003e syscall=0 success=yes "
+"exit=0 a0=4 a1=7f84154a2000 a2=400 a3=22 items=0 ppid=2560 pid=2591 auid=0 "
+"uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 "
+"comm=\"pidof\" exe=\"/usr/sbin/killall5\" "
+"subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)",
+"    type=1320 audit(1489384479.809:4343):",
+"    type=1300 audit(1489384479.809:4344): arch=c000003e syscall=3 success=yes "
+"exit=0 a0=4 a1=1 a2=8 a3=0 items=0 ppid=2560 pid=2591 auid=0 uid=0 gid=0 "
+"euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 comm=\"pidof\" "
+"exe=\"/usr/sbin/killall5\" "
+"subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)",
+"    type=1320 audit(1489384479.809:4344):",
+"    type=1300 audit(1489384479.809:4345): arch=c000003e syscall=11 "
+"success=yes exit=0 a0=7f84154a2000 a1=1000 a2=0 a3=0 items=0 ppid=2560 "
+"pid=2591 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 "
+"tty=ttyS0 ses=1 comm=\"pidof\" exe=\"/usr/sbin/killall5\" "
+"subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)",
+"    type=1320 audit(1489384479.809:4345):",
+"    type=1300 audit(1489384479.809:4346): arch=c000003e syscall=2 success=yes "
+"exit=4 a0=7ffcfd20f5a0 a1=0 a2=1b6 a3=24 items=1 ppid=2560 pid=2591 auid=0 "
+"uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=ttyS0 ses=1 "
+"comm=\"pidof\" exe=\"/usr/sbin/killall5\" "
+"subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)",
+"    type=1307 audit(1489384479.809:4346):  cwd=\"/proc\"",
+"    ...",
+" ",
 NULL               
 };
 
diff --git a/kernel.c b/kernel.c
index 0b1a499..074998d 100644
--- a/kernel.c
+++ b/kernel.c
@@ -86,6 +86,9 @@ static void show_kernel_taints(char *, int);
 static void dump_dmi_info(void);
 static void list_source_code(struct gnu_request *, int);
 static void source_tree_init(void);
+static ulong dump_audit_skb_queue(ulong);
+static ulong __dump_audit(char *);
+static void dump_audit(void);
 
 
 /*
@@ -782,6 +785,20 @@ kernel_init()
 		source_tree_init();
 
 	kt->flags &= ~PRE_KERNEL_INIT;
+
+	/*
+	 * For dumping audit log by log -a:
+	 */
+	STRUCT_SIZE_INIT(nlmsghdr, "nlmsghdr");
+	MEMBER_OFFSET_INIT(nlmsghdr_nlmsg_type, "nlmsghdr", "nlmsg_type");
+	MEMBER_SIZE_INIT(nlmsghdr_nlmsg_type, "nlmsghdr", "nlmsg_type");
+	MEMBER_OFFSET_INIT(sk_buff_head_next, "sk_buff_head", "next");
+	MEMBER_OFFSET_INIT(sk_buff_head_qlen, "sk_buff_head", "qlen");
+	MEMBER_SIZE_INIT(sk_buff_head_qlen, "sk_buff_head", "qlen");
+	MEMBER_OFFSET_INIT(sk_buff_data, "sk_buff", "data");
+	MEMBER_OFFSET_INIT(sk_buff_len, "sk_buff", "len");
+	MEMBER_OFFSET_INIT(sk_buff_next, "sk_buff", "next");
+	MEMBER_SIZE_INIT(sk_buff_len, "sk_buff", "len");
 }
 
 /*
@@ -4760,7 +4777,7 @@ cmd_log(void)
 
 	msg_flags = 0;
 
-        while ((c = getopt(argcnt, args, "tdm")) != EOF) {
+        while ((c = getopt(argcnt, args, "tdma")) != EOF) {
                 switch(c)
                 {
 		case 't':
@@ -4772,6 +4789,9 @@ cmd_log(void)
                 case 'm':
                         msg_flags |= SHOW_LOG_LEVEL;
                         break;
+		case 'a':
+			msg_flags |= SHOW_LOG_AUDIT;
+			break;
                 default:
                         argerrs++;
                         break;
@@ -4781,6 +4801,11 @@ cmd_log(void)
         if (argerrs)
                 cmd_usage(pc->curcmd, SYNOPSIS);
 
+	if (msg_flags & SHOW_LOG_AUDIT) {
+		dump_audit();
+		return;
+	}
+
 	dump_log(msg_flags);
 }
 
@@ -10752,3 +10777,120 @@ dump_dmi_info(void)
 
 	close_tmpfile();
 }
+
+#define NLMSG_ALIGNTO 4
+#define NLMSG_DATA(nlh) (nlh + roundup(SIZE(nlmsghdr), NLMSG_ALIGNTO))
+
+static ulong
+dump_audit_skb_queue(ulong audit_skb_queue)
+{
+	ulong skb_buff_head_next = 0, p;
+	uint32_t qlen = 0;
+
+	readmem(audit_skb_queue + OFFSET(sk_buff_head_qlen),
+		KVADDR,
+		&qlen,
+		SIZE(sk_buff_head_qlen),
+		"audit_skb_queue.qlen",
+		FAULT_ON_ERROR);
+
+	if (!qlen)
+		return 0;
+
+	readmem(audit_skb_queue + OFFSET(sk_buff_head_next),
+		KVADDR,
+		&skb_buff_head_next,
+		sizeof(void *),
+		"audit_skb_queue.next",
+		FAULT_ON_ERROR);
+
+	if (!skb_buff_head_next)
+		error(FATAL, "audit_skb_queue.next: NULL\n");
+
+	p = skb_buff_head_next;
+	do {
+		ulong data, len, data_len;
+		uint16_t nlmsg_type;
+		char *buf = NULL;
+
+		if (CRASHDEBUG(2))
+			fprintf(fp, "%#016lx\n", p);
+
+		readmem(p + OFFSET(sk_buff_len),
+			KVADDR,
+			&len,
+			SIZE(sk_buff_len),
+			"sk_buff.data",
+			FAULT_ON_ERROR);
+
+		data_len = len - roundup(SIZE(nlmsghdr), NLMSG_ALIGNTO);
+
+		readmem(p + OFFSET(sk_buff_data),
+			KVADDR,
+			&data,
+			sizeof(void *),
+			"sk_buff.data",
+			FAULT_ON_ERROR);
+
+		if (!data)
+			error(FATAL, "sk_buff.data: NULL\n");
+
+		readmem(data + OFFSET(nlmsghdr_nlmsg_type),
+			KVADDR,
+			&nlmsg_type,
+			SIZE(nlmsghdr_nlmsg_type),
+			"nlmsghdr.nlmsg_type",
+			FAULT_ON_ERROR);
+
+		buf = GETBUF(data_len + 1);
+		readmem(NLMSG_DATA(data),
+			KVADDR,
+			buf,
+			data_len,
+			"sk_buff.data + sizeof(struct nlmsghdr)",
+			FAULT_ON_ERROR);
+		buf[data_len] = '\0';
+
+		fprintf(fp, "type=%u %s\n", nlmsg_type, buf);
+		FREEBUF(buf);
+
+		readmem(p + OFFSET(sk_buff_next),
+			KVADDR,
+			&p,
+			sizeof(void *),
+			"skb_buff.next",
+			FAULT_ON_ERROR);
+	} while (p != audit_skb_queue);
+
+	return qlen;
+}
+
+static ulong
+__dump_audit(char *symname)
+{
+	if (symbol_exists(symname)) {
+		if (CRASHDEBUG(1))
+			fprintf(fp, "# %s:\n", symname);
+		return dump_audit_skb_queue(symbol_value(symname));
+	}
+	return 0;
+}
+
+static void
+dump_audit(void)
+{
+	ulong qlen = 0;
+
+	if (symbol_exists("audit_skb_queue")) {
+		qlen += __dump_audit("audit_skb_hold_queue");
+		qlen += __dump_audit("audit_skb_queue");
+	} else if (symbol_exists("audit_queue")) {
+		qlen += __dump_audit("audit_hold_queue");
+		qlen += __dump_audit("audit_retry_queue");
+		qlen += __dump_audit("audit_queue");
+	} else
+		option_not_supported('a');
+
+	if (!qlen)
+		error(INFO, "kernel audit log is empty\n");
+}
diff --git a/symbols.c b/symbols.c
index b8d965b..0077ca2 100644
--- a/symbols.c
+++ b/symbols.c
@@ -10130,6 +10130,18 @@ dump_offset_table(char *spec, ulong makestruct)
 		OFFSET(atomic_t_counter));
 	fprintf(fp, "          percpu_counter_count: %ld\n",
 		OFFSET(percpu_counter_count));
+	fprintf(fp, "             sk_buff_head_next: %ld\n",
+		OFFSET(sk_buff_head_next));
+	fprintf(fp, "             sk_buff_head_qlen: %ld\n",
+		OFFSET(sk_buff_head_qlen));
+	fprintf(fp, "                  sk_buff_next: %ld\n",
+		OFFSET(sk_buff_next));
+	fprintf(fp, "                   sk_buff_len: %ld\n",
+		OFFSET(sk_buff_len));
+	fprintf(fp, "                  sk_buff_data: %ld\n",
+		OFFSET(sk_buff_data));
+	fprintf(fp, "           nlmsghdr_nlmsg_type: %ld\n",
+		OFFSET(nlmsghdr_nlmsg_type));
 
 	fprintf(fp, "\n                    size_table:\n");
 	fprintf(fp, "                          page: %ld\n", SIZE(page));
@@ -10363,6 +10375,12 @@ dump_offset_table(char *spec, ulong makestruct)
 		SIZE(tnt));
 	fprintf(fp, "                    taint_flag: %ld\n",
 		SIZE(taint_flag));
+	fprintf(fp, "                      nlmsghdr: %ld\n",
+		SIZE(nlmsghdr));
+	fprintf(fp, "           nlmsghdr_nlmsg_type: %ld\n",
+		SIZE(nlmsghdr_nlmsg_type));
+	fprintf(fp, "             sk_buff_head_qlen: %ld\n",
+		SIZE(sk_buff_head_qlen));
 
         fprintf(fp, "\n                   array_table:\n");
 	/*
-- 
1.7.1

