From f1641ff5d8b5c1a03576551697b99d4e980c9e0e 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 audit's skb queue buffer, which 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>
---
 defs.h   |   14 +++++++
 help.c   |   34 +++++++++++++++-
 kernel.c |  133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 179 insertions(+), 2 deletions(-)

diff --git a/defs.h b/defs.h
index edccdc7..64b1a47 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_data;
+	long sk_buff_next;
+	long nlmsghdr_nlmsg_len;
+	long nlmsghdr_nlmsg_type;
 };
 
 struct size_table {         /* stash of commonly-used sizes */
@@ -2126,6 +2132,13 @@ struct size_table {         /* stash of commonly-used sizes */
 	long task_struct_flags;
 	long timer_base;
 	long taint_flag;
+	long nlmsghdr;
+	long nlmsghdr_nlmsg_len;
+	long nlmsghdr_nlmsg_type;
+	long sk_buff_head_next;
+	long sk_buff_head_qlen;
+	long sk_buff_data;
+	long sk_buff_next;
 };
 
 struct array_table {
@@ -5316,6 +5329,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..00f9d6e 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 void dump_audit_skb_queue(ulong);
+static void __dump_audit(char *);
+static void dump_audit(void);
 
 
 /*
@@ -782,6 +785,23 @@ 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_len, "nlmsghdr", "nlmsg_len");
+	MEMBER_OFFSET_INIT(nlmsghdr_nlmsg_type, "nlmsghdr", "nlmsg_type");
+	MEMBER_SIZE_INIT(nlmsghdr_nlmsg_len, "nlmsghdr", "nlmsg_len");
+	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_next, "sk_buff_head", "next");
+	MEMBER_SIZE_INIT(sk_buff_head_qlen, "sk_buff_head", "qlen");
+	MEMBER_OFFSET_INIT(sk_buff_next, "sk_buff", "next");
+	MEMBER_OFFSET_INIT(sk_buff_data, "sk_buff", "data");
+	MEMBER_SIZE_INIT(sk_buff_next, "sk_buff", "next");
+	MEMBER_SIZE_INIT(sk_buff_data, "sk_buff", "data");
 }
 
 /*
@@ -4760,7 +4780,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 +4792,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 +4804,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 +10780,106 @@ dump_dmi_info(void)
 
 	close_tmpfile();
 }
+
+#define NLMSG_ALIGNTO 4
+#define NLMSG_DATA(nlh) (nlh + roundup(SIZE(nlmsghdr), NLMSG_ALIGNTO))
+
+static void
+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;
+
+	readmem(audit_skb_queue + OFFSET(sk_buff_head_next),
+		KVADDR,
+		&skb_buff_head_next,
+		SIZE(sk_buff_head_next),
+		"audit_skb_queue.next",
+		FAULT_ON_ERROR);
+
+	if (!skb_buff_head_next)
+		return;
+
+	p = skb_buff_head_next;
+	do {
+		ulong data;
+
+		if (CRASHDEBUG(2))
+			fprintf(fp, "%#016lx\n", p);
+
+		readmem(p + OFFSET(sk_buff_data),
+			KVADDR,
+			&data,
+			SIZE(sk_buff_data),
+			"sk_buff.data",
+			FAULT_ON_ERROR);
+
+		if (data) {
+			uint32_t nlmsg_len;
+			uint16_t nlmsg_type;
+			char *buf = NULL;
+
+			readmem(data + OFFSET(nlmsghdr_nlmsg_len),
+				KVADDR,
+				&nlmsg_len,
+				SIZE(nlmsghdr_nlmsg_len),
+				"nlmsghdr.nlmsg_len",
+				FAULT_ON_ERROR);
+			readmem(data + OFFSET(nlmsghdr_nlmsg_type),
+				KVADDR,
+				&nlmsg_type,
+				SIZE(nlmsghdr_nlmsg_type),
+				"nlmsghdr.nlmsg_type",
+				FAULT_ON_ERROR);
+
+			buf = GETBUF(nlmsg_len + 1);
+			readmem(NLMSG_DATA(data),
+				KVADDR,
+				buf,
+				nlmsg_len,
+				"sk_buff.data + sizeof(struct nlmsghdr)",
+				FAULT_ON_ERROR);
+			buf[nlmsg_len] = '\0';
+
+			fprintf(fp, "type=%u %s\n", nlmsg_type, buf);
+			FREEBUF(buf);
+		}
+
+		readmem(p + OFFSET(sk_buff_next),
+			KVADDR,
+			&p,
+			SIZE(sk_buff_next),
+			"skb_buff.next",
+			FAULT_ON_ERROR);
+	} while (p != audit_skb_queue);
+}
+
+static void
+__dump_audit(char *symname)
+{
+	if (symbol_exists(symname)) {
+		if (CRASHDEBUG(1))
+			fprintf(fp, "# %s:\n", symname);
+		dump_audit_skb_queue(symbol_value(symname));
+	}
+}
+
+static void
+dump_audit(void)
+{
+	if (!symbol_exists("audit_skb_queue"))
+		option_not_supported('a');
+
+	__dump_audit("audit_skb_hold_queue");
+	__dump_audit("audit_skb_queue");
+}
-- 
1.7.1

