[PATCH] audit: Emit history of thread's comm name.

Tetsuo Handa penguin-kernel at I-love.SAKURA.ne.jp
Sun Sep 28 10:24:52 UTC 2014


Thank you, Steve. Here is a revised patch.
----------
>From 2beb93e870e9c1a6391d8706aa84a608b8353c2a Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel at I-love.SAKURA.ne.jp>
Date: Sun, 28 Sep 2014 19:20:16 +0900
Subject: [PATCH] audit: Emit history of thread's comm name.

When an unexpected system event (e.g. reboot) occurs, the administrator may
want to identify which application triggered the event. System call auditing
could be used for recording such event. However, the audit log may not be
able to provide sufficient information for identifying the application
because the audit log does not reflect how the program was executed.

This patch adds ability to trace how the program was executed and emit it
as an auxiliary record in the form of comm name and time stamp pairs as of
execve().

  type=UNKNOWN[1329] msg=audit(1403741314.019:22): history='
  swapper/0(2014/06/26-09:06:04)=>init(2014/06/26-09:06:10)=>
  switch_root(2014/06/26-09:06:13)=>init(2014/06/26-09:06:13)=>
  sh(2014/06/26-00:06:27)=>rc(2014/06/26-00:06:27)=>
  S55sshd(2014/06/26-00:06:35)=>sshd(2014/06/26-00:06:35)=>
  sshd(2014/06/26-00:06:40)=>bash(2014/06/26-00:06:43)=>
  tail(2014/06/26-00:08:34)'

Note that only char < 0x21, char > 0x7e, '\'', '\\' and '=' are escaped
using \ooo style octal value rather than converting all characters to XX
style hexadecimal value.

Signed-off-by: Tetsuo Handa <penguin-kernel at I-love.SAKURA.ne.jp>
---
 fs/exec.c                  |    1 +
 include/linux/audit.h      |    4 ++
 include/linux/init_task.h  |    5 ++
 include/linux/sched.h      |    3 +
 include/uapi/linux/audit.h |    1 +
 kernel/audit.c             |    1 +
 kernel/auditsc.c           |  113 ++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 128 insertions(+), 0 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index a2b42a9..1e81709 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1191,6 +1191,7 @@ void install_exec_creds(struct linux_binprm *bprm)
 	commit_creds(bprm->cred);
 	bprm->cred = NULL;
 
+	audit_update_history();
 	/*
 	 * Disable monitoring for regular users
 	 * when executing setuid binaries. Must
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 22cfddb..97d08e1 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -206,6 +206,8 @@ static inline void audit_ptrace(struct task_struct *t)
 		__audit_ptrace(t);
 }
 
+extern void audit_update_history(void);
+
 				/* Private API (for audit.c only) */
 extern unsigned int audit_serial(void);
 extern int auditsc_get_stamp(struct audit_context *ctx,
@@ -419,6 +421,8 @@ static inline void audit_mmap_fd(int fd, int flags)
 { }
 static inline void audit_ptrace(struct task_struct *t)
 { }
+static inline void audit_update_history(void)
+{ }
 #define audit_n_rules 0
 #define audit_signals 0
 #endif /* CONFIG_AUDITSYSCALL */
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index 2bb4c4f..7a5695b 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -98,8 +98,12 @@ extern struct group_info init_groups;
 #define INIT_IDS \
 	.loginuid = INVALID_UID, \
 	.sessionid = (unsigned int)-1,
+extern char init_task_history[];
+#define INIT_THREAD_HISTORY		\
+	.comm_history = init_task_history,
 #else
 #define INIT_IDS
+#define INIT_THREAD_HISTORY
 #endif
 
 #ifdef CONFIG_TREE_PREEMPT_RCU
@@ -227,6 +231,7 @@ extern struct task_group root_task_group;
 	INIT_CPUSET_SEQ(tsk)						\
 	INIT_RT_MUTEXES(tsk)						\
 	INIT_VTIME(tsk)							\
+	INIT_THREAD_HISTORY						\
 }
 
 
diff --git a/include/linux/sched.h b/include/linux/sched.h
index b867a4d..fd3cdaf 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1644,6 +1644,9 @@ struct task_struct {
 	unsigned int	sequential_io;
 	unsigned int	sequential_io_avg;
 #endif
+#ifdef CONFIG_AUDITSYSCALL
+	char *comm_history;
+#endif
 };
 
 /* Future-safe accessor for struct task_struct's cpus_allowed. */
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 3b9ff33..a6a8ee8 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -110,6 +110,7 @@
 #define AUDIT_SECCOMP		1326	/* Secure Computing event */
 #define AUDIT_PROCTITLE		1327	/* Proctitle emit event */
 #define AUDIT_FEATURE_CHANGE	1328	/* audit log listing feature changes */
+#define AUDIT_PROCHISTORY	1329	/* Commname history emit event */
 
 #define AUDIT_AVC		1400	/* SE Linux avc denial or grant */
 #define AUDIT_SELINUX_ERR	1401	/* Internal SE Linux Errors */
diff --git a/kernel/audit.c b/kernel/audit.c
index ba2ff5a..252544a 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1163,6 +1163,7 @@ static int __init audit_init(void)
 {
 	int i;
 
+	audit_update_history();
 	if (audit_initialized == AUDIT_DISABLED)
 		return 0;
 
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 21eae3c..2e5ee14 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -85,6 +85,9 @@
 /* max length to print of cmdline/proctitle value during audit */
 #define MAX_PROCTITLE_AUDIT_LEN 128
 
+/* thread's comm name history length */
+#define COMM_HISTORY_SIZE 1024
+
 /* number of audit rules */
 int audit_n_rules;
 
@@ -950,6 +953,11 @@ int audit_alloc(struct task_struct *tsk)
 	enum audit_state     state;
 	char *key = NULL;
 
+	tsk->comm_history = kmemdup(current->comm_history, COMM_HISTORY_SIZE,
+				    GFP_KERNEL);
+	if (!tsk->comm_history)
+		return -ENOMEM;
+
 	if (likely(!audit_ever_enabled))
 		return 0; /* Return if not auditing. */
 
@@ -960,6 +968,8 @@ int audit_alloc(struct task_struct *tsk)
 	}
 
 	if (!(context = audit_alloc_context(state))) {
+		kfree(tsk->comm_history);
+		tsk->comm_history = NULL;
 		kfree(key);
 		audit_log_lost("out of memory in audit_alloc");
 		return -ENOMEM;
@@ -1349,6 +1359,17 @@ out:
 	audit_log_end(ab);
 }
 
+static void audit_log_history(struct audit_context *context)
+{
+	struct audit_buffer *ab;
+
+	ab = audit_log_start(context, GFP_KERNEL, AUDIT_PROCHISTORY);
+	if (!ab)
+		return;	/* audit_panic or being filtered */
+	audit_log_format(ab, "history='%s'", current->comm_history);
+	audit_log_end(ab);
+}
+
 static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
 {
 	int i, call_panic = 0;
@@ -1467,6 +1488,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
 	}
 
 	audit_log_proctitle(tsk, context);
+	audit_log_history(context);
 
 	/* Send end of event record to help user space know we are finished */
 	ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE);
@@ -1486,6 +1508,8 @@ void __audit_free(struct task_struct *tsk)
 {
 	struct audit_context *context;
 
+	kfree(tsk->comm_history);
+	tsk->comm_history = NULL;
 	context = audit_take_context(tsk, 0, 0);
 	if (!context)
 		return;
@@ -2503,3 +2527,92 @@ struct list_head *audit_killed_trees(void)
 		return NULL;
 	return &ctx->killed_trees;
 }
+
+char init_task_history[COMM_HISTORY_SIZE];
+
+/**
+ * audit_update_history - Update current->comm_history field.
+ *
+ * Returns nothing.
+ *
+ * Update is done locklessly because current thread's history is updated by
+ * only current thread upon boot up and successful execve() operation, and
+ * we don't read other thread's history.
+ */
+void audit_update_history(void)
+{
+	static const u16 eom[2][12] = {
+		{ 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+		{ 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+	};
+	u16 year = 1970;
+	u16 day;
+	u8 month;
+	u8 hour;
+	u8 minute;
+	u8 second;
+	bool r;
+	time_t now = get_seconds();
+	char *history = current->comm_history;
+	int pos = strlen(history);
+
+	/* Make some room by truncating old history. */
+	while (pos >= COMM_HISTORY_SIZE - (TASK_COMM_LEN * 4 + 30)) {
+		char *cp = strchr(history + 1, '=');
+
+		if (unlikely(!cp))
+			return;
+		pos -= cp - history;
+		memmove(history, cp, pos + 1);
+	}
+	if (pos) {
+		history += pos;
+		*history++ = '=';
+		*history++ = '>';
+	}
+	/*
+	 * Read locklessly because this is current thread and being
+	 * unexpectedly modified by other thread is not a fatal problem.
+	 */
+	for (pos = 0; pos < TASK_COMM_LEN; pos++) {
+		const unsigned char c = current->comm[pos];
+
+		if (!c)
+			break;
+		else if (c == '\'' || c == '\\' || c == '=' || c < 0x21 ||
+			 c > 0x7e) {
+			*history++ = '\\';
+			*history++ = (c >> 6) + '0';
+			*history++ = ((c >> 3) & 7) + '0';
+			*history++ = (c & 7) + '0';
+		} else
+			*history++ = c;
+	}
+	/* Append current time in "(YYYY/MM/DD-hh:mm:ss)" format. */
+	second = now % 60;
+	now /= 60;
+	minute = now % 60;
+	now /= 60;
+	hour = now % 24;
+	day = now / 24;
+	if (day >= 16071) {
+		/* Start from 2014/01/01 rather than 1970/01/01. */
+		day -= 16071;
+		year += 44;
+	}
+	while (1) {
+		const u16 days = (year & 3) ? 365 : 366;
+
+		if (day < days)
+			break;
+		day -= days;
+		year++;
+	}
+	r = (year & 3) == 0;
+	for (month = 0; month < 11 && day >= eom[r][month]; month++)
+		;
+	if (month)
+		day -= eom[r][month - 1];
+	snprintf(history, 22, "(%04u/%02u/%02u-%02u:%02u:%02u)", year,
+		 month + 1, day + 1, hour, minute, second);
+}
-- 
1.7.1




More information about the Linux-audit mailing list