All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] TaskTracker : Simplified thread information tracker.
@ 2014-05-23 12:44 Tetsuo Handa
  2014-06-23 12:14 ` Tetsuo Handa
  0 siblings, 1 reply; 20+ messages in thread
From: Tetsuo Handa @ 2014-05-23 12:44 UTC (permalink / raw)
  To: linux-security-module; +Cc: linux-audit, linux-kernel

Yesterday I went to LinuxCon Japan 2014 and stopped at Red Hat's booth
and Oracle's booth. I explained about this module ( using page 92 of
http://I-love.SAKURA.ne.jp/tomoyo/LCJ2014-en.pdf ) and got positive
responses from persons who have experienced troubleshooting jobs.
I was convinced that I am not the only person who is bothered by lack of
process history information in the logs. Therefore, I repost this module
toward inclusion into mainline Linux kernel.

Changes from previous version ( http://lwn.net/Articles/575044/ ):

  (1) Assign a value to "u32 *seclen" in addition to "char *secdata"
      at security_task_getsecid() hook.

  (2) Make calculation of time stamp a bit faster.

Background:

  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.

  I sometimes receive "which application triggered the event" questions on RHEL
  systems. TOMOYO security module can track how the program was executed, but
  TOMOYO is not yet available in Fedora/RHEL distributions.

  Although subj= field is added to the audit log if SELinux is not disabled,
  SELinux is too difficult to customize as fine grained as I expect in order to
  reflect how the program was executed. Therefore, I'm currently using AKARI
  and SystemTap for emulating TOMOYO-like tracing.

  But AKARI and SystemTap do not help unless the kernel module is loaded before
  the unexpected system event occurs. Generally, the administrator is failing
  to record the first event, and has to wait for the same event to occur again
  after loading the kernel module and/or configuring auditing. I came to think
  that we want a built-in kernel routine which is automatically started upon
  boot so that we don't fail to record the first event.

What I did:

  Assuming that multiple concurrent LSM support comes in the near future,
  I wrote a trivial LSM module which emits TOMOYO-like information into the
  audit logs.

Usage:

  Just register this LSM module. No configuration is needed. You will get
  history of current thread in the form of comm name and time stamp pairs
  in the subj= field of audit logs like examples shown in the patch
  description.
----------
>From ff68d3a4cd496bd263d2939848777fffc30cbc0b Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Fri, 23 May 2014 21:31:56 +0900
Subject: [PATCH] TaskTracker : Simplified thread information tracker.

Existing audit logs generated via system call auditing functionality include
current thread's comm name. But it is not always sufficient for identifying
which application has requested specific operations because comm name does not
reflect history of current thread.

This security module adds functionality for adding current thread's history
information like TOMOYO security module does, expecting that this module can
help us getting more information from system call auditing functionality.

  type=USER_LOGIN msg=audit(1400879947.084:24): pid=4308 uid=0 auid=0 ses=2
  subj="swapper/0(2014/05/23-21:17:30)=>init(2014/05/23-21:17:33)=>
  switch_root(2014/05/23-21:17:34)=>init(2014/05/23-21:17:34)=>
  sh(2014/05/23-21:17:56)=>mingetty(2014/05/23-21:17:56)=>
  login(2014/05/23-21:19:05)" msg='op=login id=0 exe="/bin/login" hostname=?
  addr=? terminal=tty1 res=success'

  type=SYSCALL msg=audit(1400880014.444:26): arch=40000003 syscall=11
  success=yes exit=0 a0=8140f78 a1=812b7d8 a2=812b248 a3=812b7d8 items=2
  ppid=4323 pid=4355 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0
  fsgid=0 tty=pts0 ses=1 comm="tail" exe="/usr/bin/tail"
  subj="swapper/0(2014/05/23-21:17:30)=>init(2014/05/23-21:17:33)=>
  switch_root(2014/05/23-21:17:34)=>init(2014/05/23-21:17:34)=>
  sh(2014/05/23-21:17:37)=>rc(2014/05/23-21:17:37)=>
  S55sshd(2014/05/23-21:17:53)=>sshd(2014/05/23-21:17:53)=>
  sshd(2014/05/23-21:18:17)=>bash(2014/05/23-21:18:21)=>
  tail(2014/05/23-21:20:14)" key=(null)

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/Kconfig                   |    6 +
 security/Makefile                  |    2 +
 security/tasktracker/Kconfig       |   35 +++++
 security/tasktracker/Makefile      |    1 +
 security/tasktracker/tasktracker.c |  282 ++++++++++++++++++++++++++++++++++++
 5 files changed, 326 insertions(+), 0 deletions(-)
 create mode 100644 security/tasktracker/Kconfig
 create mode 100644 security/tasktracker/Makefile
 create mode 100644 security/tasktracker/tasktracker.c

diff --git a/security/Kconfig b/security/Kconfig
index beb86b5..14e7d27 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -122,6 +122,7 @@ source security/smack/Kconfig
 source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 source security/yama/Kconfig
+source security/tasktracker/Kconfig
 
 source security/integrity/Kconfig
 
@@ -132,6 +133,7 @@ choice
 	default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
 	default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
 	default DEFAULT_SECURITY_YAMA if SECURITY_YAMA
+	default DEFAULT_SECURITY_TT if SECURITY_TT
 	default DEFAULT_SECURITY_DAC
 
 	help
@@ -153,6 +155,9 @@ choice
 	config DEFAULT_SECURITY_YAMA
 		bool "Yama" if SECURITY_YAMA=y
 
+	config DEFAULT_SECURITY_TT
+		bool "TaskTracker" if SECURITY_TT=y
+
 	config DEFAULT_SECURITY_DAC
 		bool "Unix Discretionary Access Controls"
 
@@ -165,6 +170,7 @@ config DEFAULT_SECURITY
 	default "tomoyo" if DEFAULT_SECURITY_TOMOYO
 	default "apparmor" if DEFAULT_SECURITY_APPARMOR
 	default "yama" if DEFAULT_SECURITY_YAMA
+	default "tt" if DEFAULT_SECURITY_TT
 	default "" if DEFAULT_SECURITY_DAC
 
 endmenu
diff --git a/security/Makefile b/security/Makefile
index 05f1c93..28a90ed 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK)		+= smack
 subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
 subdir-$(CONFIG_SECURITY_YAMA)		+= yama
+subdir-$(CONFIG_SECURITY_TT)		+= tasktracker
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT)			+= lsm_audit.o
 obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/
 obj-$(CONFIG_SECURITY_YAMA)		+= yama/
+obj-$(CONFIG_SECURITY_TT)		+= tasktracker/
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff --git a/security/tasktracker/Kconfig b/security/tasktracker/Kconfig
new file mode 100644
index 0000000..6de5354
--- /dev/null
+++ b/security/tasktracker/Kconfig
@@ -0,0 +1,35 @@
+config SECURITY_TT
+	bool "TaskTracker support"
+	depends on SECURITY
+	default n
+	help
+	  Existing audit logs generated via system call auditing functionality
+	  include current thread's comm name. But it is not always sufficient
+	  for identifying which application has requested specific operations
+	  because comm name does not reflect history of current thread.
+	  
+	  This security module adds functionality for adding current thread's
+	  history information like TOMOYO security module does, expecting that
+	  this module can help us getting more information from system call
+	  auditing functionality.
+	  
+	  If you are unsure how to answer this question, answer N.
+	  
+	  Usage:
+	  
+	  Just register this module. No configuration is needed.
+	  
+	  You will get history of current thread in the form of
+	  comm name and time stamp pairs in the subj= field of audit logs
+	  like an example shown below.
+	  
+	    type=SYSCALL msg=audit(1400880014.444:26): arch=40000003 syscall=11
+	    success=yes exit=0 a0=8140f78 a1=812b7d8 a2=812b248 a3=812b7d8
+	    items=2 ppid=4323 pid=4355 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0
+	    egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="tail"
+	    exe="/usr/bin/tail" subj="swapper/0(2014/05/23-21:17:30)=>
+	    init(2014/05/23-21:17:33)=>switch_root(2014/05/23-21:17:34)=>
+	    init(2014/05/23-21:17:34)=>sh(2014/05/23-21:17:37)=>
+	    rc(2014/05/23-21:17:37)=>S55sshd(2014/05/23-21:17:53)=>
+	    sshd(2014/05/23-21:17:53)=>sshd(2014/05/23-21:18:17)=>
+	    bash(2014/05/23-21:18:21)=>tail(2014/05/23-21:20:14)" key=(null)
diff --git a/security/tasktracker/Makefile b/security/tasktracker/Makefile
new file mode 100644
index 0000000..15d03ce
--- /dev/null
+++ b/security/tasktracker/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SECURITY_TT) := tasktracker.o
diff --git a/security/tasktracker/tasktracker.c b/security/tasktracker/tasktracker.c
new file mode 100644
index 0000000..ec4eb0c
--- /dev/null
+++ b/security/tasktracker/tasktracker.c
@@ -0,0 +1,282 @@
+/*
+ * tasktracker.c - Simplified thread information tracker.
+ *
+ * Copyright (C) 2010-2014  Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+ */
+#include <linux/security.h>
+#include <linux/binfmts.h>
+
+/* Wrapper structure for passing string buffer. */
+struct tt_record {
+	char history[1024];
+};
+
+/* Structure for representing YYYY/MM/DD hh/mm/ss. */
+struct tt_time {
+	u16 year;
+	u8 month;
+	u8 day;
+	u8 hour;
+	u8 min;
+	u8 sec;
+};
+
+/**
+ * tt_get_time - Get current time in YYYY/MM/DD hh/mm/ss format.
+ *
+ * @stamp: Pointer to "struct tt_time".
+ *
+ * Returns nothing.
+ *
+ * This function does not handle Y2038 problem.
+ */
+static void tt_get_time(struct tt_time *stamp)
+{
+	struct timeval tv;
+	static const u16 tt_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 y = 1970;
+	u8 m;
+	bool r;
+	time_t time;
+	do_gettimeofday(&tv);
+	time = tv.tv_sec;
+	stamp->sec = time % 60;
+	time /= 60;
+	stamp->min = time % 60;
+	time /= 60;
+	stamp->hour = time % 24;
+	time /= 24;
+	if (time >= 16071) {
+		/* Start from 2014/01/01 rather than 1970/01/01. */
+		time -= 16071;
+		y += 44;
+	}
+	while (1) {
+		const unsigned short days = (y & 3) ? 365 : 366;
+		if (time < days)
+			break;
+		time -= days;
+		y++;
+	}
+	r = (y & 3) == 0;
+	for (m = 0; m < 11 && time >= tt_eom[r][m]; m++)
+		;
+	if (m)
+		time -= tt_eom[r][m - 1];
+	stamp->year = y;
+	stamp->month = ++m;
+	stamp->day = ++time;
+}
+
+/**
+ * tt_update_record - Update "struct tt_record" for given credential.
+ *
+ * @record: Pointer to "struct tt_record".
+ *
+ * Returns nothing.
+ */
+static void tt_update_record(struct tt_record *record)
+{
+	char *cp;
+	int i;
+	struct tt_time stamp;
+	tt_get_time(&stamp);
+	/*
+	 * Lockless update because current thread's record is not concurrently
+	 * accessible, for "struct cred"->security is not visible from other
+	 * threads because this function is called upon only boot up and
+	 * successful execve() operation.
+	 */
+	cp = record->history;
+	i = strlen(cp);
+	while (i >= sizeof(record->history) - (TASK_COMM_LEN * 4 + 30)) {
+		/*
+		 * Since this record is not for making security decision,
+		 * I don't care by-chance matching "=>" in task's commname.
+		 */
+		char *cp2 = strstr(cp + 2, "=>");
+		if (!cp2)
+			return;
+		memmove(cp + 1, cp2, strlen(cp2) + 1);
+		i = strlen(cp);
+	}
+	if (!i)
+		*cp++ = '"';
+	else {
+		cp += i - 1;
+		*cp++ = '=';
+		*cp++ = '>';
+	}
+	/*
+	 * Lockless read because this is current thread and being unexpectedly
+	 * modified by other thread is not a fatal problem.
+	 */
+	for (i = 0; i < TASK_COMM_LEN; i++) {
+		const unsigned char c = current->comm[i];
+		if (!c)
+			break;
+		else if (c == '"' || c == '\\' || c < 0x21 || c > 0x7e) {
+			*cp++ = '\\';
+			*cp++ = (c >> 6) + '0';
+			*cp++ = ((c >> 3) & 7) + '0';
+			*cp++ = (c & 7) + '0';
+		} else
+			*cp++ = c;
+	}
+	sprintf(cp, "(%04u/%02u/%02u-%02u:%02u:%02u)\"", stamp.year,
+		stamp.month, stamp.day, stamp.hour, stamp.min, stamp.sec);
+}
+
+/**
+ * tt_find_record - Find "struct tt_record" for given credential.
+ *
+ * @cred: Pointer to "struct cred".
+ *
+ * Returns pointer to "struct tt_record".
+ */
+static inline struct tt_record *tt_find_record(const struct cred *cred)
+{
+	return cred->security;
+}
+
+/**
+ * tt_cred_alloc_blank - Allocate memory for new credentials.
+ *
+ * @new: Pointer to "struct cred".
+ * @gfp: Memory allocation flags.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tt_cred_alloc_blank(struct cred *new, gfp_t gfp)
+{
+	new->security = kzalloc(sizeof(struct tt_record), gfp);
+	return new->security ? 0 : -ENOMEM;
+}
+
+/**
+ * tt_cred_prepare - Allocate memory for new credentials.
+ *
+ * @new: Pointer to "struct cred".
+ * @old: Pointer to "struct cred".
+ * @gfp: Memory allocation flags.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tt_cred_prepare(struct cred *new, const struct cred *old,
+			   gfp_t gfp)
+{
+	if (tt_cred_alloc_blank(new, gfp))
+		return -ENOMEM;
+	strcpy(tt_find_record(new)->history, tt_find_record(old)->history);
+	return 0;
+}
+
+/**
+ * tt_cred_free - Release memory used by credentials.
+ *
+ * @cred: Pointer to "struct cred".
+ *
+ * Returns nothing.
+ */
+static void tt_cred_free(struct cred *cred)
+{
+	kfree(cred->security);
+}
+
+/**
+ * tt_cred_transfer - Transfer "struct tt_record" between credentials.
+ *
+ * @new: Pointer to "struct cred".
+ * @old: Pointer to "struct cred".
+ *
+ * Returns nothing.
+ */
+static void tt_cred_transfer(struct cred *new, const struct cred *old)
+{
+	strcpy(tt_find_record(new)->history, tt_find_record(old)->history);
+}
+
+/**
+ * tt_bprm_committing_creds - A hook which is called when do_execve() succeeded.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns nothing.
+ */
+static void tt_bprm_committing_creds(struct linux_binprm *bprm)
+{
+	tt_update_record(tt_find_record(bprm->cred));
+}
+
+/**
+ * tt_task_getsecid - Check whether to audit or not.
+ *
+ * @p:     Pointer to "struct task_struct".
+ * @secid: Pointer to flag.
+ */
+static void tt_task_getsecid(struct task_struct *p, u32 *secid)
+{
+	*secid = (p == current);
+}
+
+/**
+ * tt_secid_to_secctx - Allocate memory used for auditing.
+ *
+ * @secid:   Bool flag to allocate.
+ * @secdata: Pointer to allocate memory.
+ * @seclen:  Size of allocated memory.
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+static int tt_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+	struct tt_record *record;
+	/* Ignore unless current thread's record is requested. */
+	if (secid != 1)
+		return -EINVAL;
+	/*
+	 * We don't need to duplicate the string because current thread's
+	 * record is updated upon only boot up and successful execve()
+	 * operation, even if current thread's record is shared between
+	 * multiple threads.
+	 */
+	record = tt_find_record(current->real_cred);
+	*secdata = record->history;
+	*seclen = strlen(record->history);
+	return 0;
+}
+
+/* List of hooks. */
+static struct security_operations tasktracker_ops = {
+	.name                  = "tt",
+	.secid_to_secctx       = tt_secid_to_secctx,
+	.task_getsecid         = tt_task_getsecid,
+	.cred_prepare          = tt_cred_prepare,
+	.cred_free             = tt_cred_free,
+	.cred_alloc_blank      = tt_cred_alloc_blank,
+	.cred_transfer         = tt_cred_transfer,
+	.bprm_committing_creds = tt_bprm_committing_creds,
+};
+
+/**
+ * tt_init - Initialize this module.
+ *
+ * Returns 0 on success, panic otherwise.
+ */
+static int __init tt_init(void)
+{
+	struct cred *cred = (struct cred *) current_cred();
+	if (!security_module_enable(&tasktracker_ops))
+		return 0;
+	if (tt_cred_alloc_blank(cred, GFP_ATOMIC) ||
+	    register_security(&tasktracker_ops))
+		panic("Failure registering TaskTracker");
+	tt_update_record(tt_find_record(cred));
+	pr_info("TaskTracker initialized\n");
+	return 0;
+}
+
+security_initcall(tt_init);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-05-23 12:44 [PATCH] TaskTracker : Simplified thread information tracker Tetsuo Handa
@ 2014-06-23 12:14 ` Tetsuo Handa
  2014-06-24 14:00   ` Steve Grubb
  0 siblings, 1 reply; 20+ messages in thread
From: Tetsuo Handa @ 2014-06-23 12:14 UTC (permalink / raw)
  To: linux-security-module; +Cc: linux-audit

Any comments on this proposal?

For now this module is occupying the exclusive LSM hook. But since this
module is for tracking per a "struct task_struct" attributes rather than
for making security decisions, I expect that this module can co-exist with
other LSM modules by not occupying the exclusive LSM hook.

Tetsuo Handa wrote:
> Yesterday I went to LinuxCon Japan 2014 and stopped at Red Hat's booth
> and Oracle's booth. I explained about this module ( using page 92 of
> http://I-love.SAKURA.ne.jp/tomoyo/LCJ2014-en.pdf ) and got positive
> responses from persons who have experienced troubleshooting jobs.
> I was convinced that I am not the only person who is bothered by lack of
> process history information in the logs. Therefore, I repost this module
> toward inclusion into mainline Linux kernel.
> 
> Changes from previous version ( http://lwn.net/Articles/575044/ ):
> 
>   (1) Assign a value to "u32 *seclen" in addition to "char *secdata"
>       at security_task_getsecid() hook.
> 
>   (2) Make calculation of time stamp a bit faster.
> 
> Background:
> 
>   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.
> 
>   I sometimes receive "which application triggered the event" questions on RHEL
>   systems. TOMOYO security module can track how the program was executed, but
>   TOMOYO is not yet available in Fedora/RHEL distributions.
> 
>   Although subj= field is added to the audit log if SELinux is not disabled,
>   SELinux is too difficult to customize as fine grained as I expect in order to
>   reflect how the program was executed. Therefore, I'm currently using AKARI
>   and SystemTap for emulating TOMOYO-like tracing.
> 
>   But AKARI and SystemTap do not help unless the kernel module is loaded before
>   the unexpected system event occurs. Generally, the administrator is failing
>   to record the first event, and has to wait for the same event to occur again
>   after loading the kernel module and/or configuring auditing. I came to think
>   that we want a built-in kernel routine which is automatically started upon
>   boot so that we don't fail to record the first event.
> 
> What I did:
> 
>   Assuming that multiple concurrent LSM support comes in the near future,
>   I wrote a trivial LSM module which emits TOMOYO-like information into the
>   audit logs.
> 
> Usage:
> 
>   Just register this LSM module. No configuration is needed. You will get
>   history of current thread in the form of comm name and time stamp pairs
>   in the subj= field of audit logs like examples shown in the patch
>   description.
> ----------
> >From ff68d3a4cd496bd263d2939848777fffc30cbc0b Mon Sep 17 00:00:00 2001
> From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Date: Fri, 23 May 2014 21:31:56 +0900
> Subject: [PATCH] TaskTracker : Simplified thread information tracker.
> 
> Existing audit logs generated via system call auditing functionality include
> current thread's comm name. But it is not always sufficient for identifying
> which application has requested specific operations because comm name does not
> reflect history of current thread.
> 
> This security module adds functionality for adding current thread's history
> information like TOMOYO security module does, expecting that this module can
> help us getting more information from system call auditing functionality.
> 
>   type=USER_LOGIN msg=audit(1400879947.084:24): pid=4308 uid=0 auid=0 ses=2
>   subj="swapper/0(2014/05/23-21:17:30)=>init(2014/05/23-21:17:33)=>
>   switch_root(2014/05/23-21:17:34)=>init(2014/05/23-21:17:34)=>
>   sh(2014/05/23-21:17:56)=>mingetty(2014/05/23-21:17:56)=>
>   login(2014/05/23-21:19:05)" msg='op=login id=0 exe="/bin/login" hostname=?
>   addr=? terminal=tty1 res=success'
> 
>   type=SYSCALL msg=audit(1400880014.444:26): arch=40000003 syscall=11
>   success=yes exit=0 a0=8140f78 a1=812b7d8 a2=812b248 a3=812b7d8 items=2
>   ppid=4323 pid=4355 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0
>   fsgid=0 tty=pts0 ses=1 comm="tail" exe="/usr/bin/tail"
>   subj="swapper/0(2014/05/23-21:17:30)=>init(2014/05/23-21:17:33)=>
>   switch_root(2014/05/23-21:17:34)=>init(2014/05/23-21:17:34)=>
>   sh(2014/05/23-21:17:37)=>rc(2014/05/23-21:17:37)=>
>   S55sshd(2014/05/23-21:17:53)=>sshd(2014/05/23-21:17:53)=>
>   sshd(2014/05/23-21:18:17)=>bash(2014/05/23-21:18:21)=>
>   tail(2014/05/23-21:20:14)" key=(null)
> 
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
>  security/Kconfig                   |    6 +
>  security/Makefile                  |    2 +
>  security/tasktracker/Kconfig       |   35 +++++
>  security/tasktracker/Makefile      |    1 +
>  security/tasktracker/tasktracker.c |  282 ++++++++++++++++++++++++++++++++++++
>  5 files changed, 326 insertions(+), 0 deletions(-)
>  create mode 100644 security/tasktracker/Kconfig
>  create mode 100644 security/tasktracker/Makefile
>  create mode 100644 security/tasktracker/tasktracker.c
> 
> diff --git a/security/Kconfig b/security/Kconfig
> index beb86b5..14e7d27 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -122,6 +122,7 @@ source security/smack/Kconfig
>  source security/tomoyo/Kconfig
>  source security/apparmor/Kconfig
>  source security/yama/Kconfig
> +source security/tasktracker/Kconfig
>  
>  source security/integrity/Kconfig
>  
> @@ -132,6 +133,7 @@ choice
>  	default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
>  	default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
>  	default DEFAULT_SECURITY_YAMA if SECURITY_YAMA
> +	default DEFAULT_SECURITY_TT if SECURITY_TT
>  	default DEFAULT_SECURITY_DAC
>  
>  	help
> @@ -153,6 +155,9 @@ choice
>  	config DEFAULT_SECURITY_YAMA
>  		bool "Yama" if SECURITY_YAMA=y
>  
> +	config DEFAULT_SECURITY_TT
> +		bool "TaskTracker" if SECURITY_TT=y
> +
>  	config DEFAULT_SECURITY_DAC
>  		bool "Unix Discretionary Access Controls"
>  
> @@ -165,6 +170,7 @@ config DEFAULT_SECURITY
>  	default "tomoyo" if DEFAULT_SECURITY_TOMOYO
>  	default "apparmor" if DEFAULT_SECURITY_APPARMOR
>  	default "yama" if DEFAULT_SECURITY_YAMA
> +	default "tt" if DEFAULT_SECURITY_TT
>  	default "" if DEFAULT_SECURITY_DAC
>  
>  endmenu
> diff --git a/security/Makefile b/security/Makefile
> index 05f1c93..28a90ed 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK)		+= smack
>  subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
>  subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
>  subdir-$(CONFIG_SECURITY_YAMA)		+= yama
> +subdir-$(CONFIG_SECURITY_TT)		+= tasktracker
>  
>  # always enable default capabilities
>  obj-y					+= commoncap.o
> @@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT)			+= lsm_audit.o
>  obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/
>  obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/
>  obj-$(CONFIG_SECURITY_YAMA)		+= yama/
> +obj-$(CONFIG_SECURITY_TT)		+= tasktracker/
>  obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
>  
>  # Object integrity file lists
> diff --git a/security/tasktracker/Kconfig b/security/tasktracker/Kconfig
> new file mode 100644
> index 0000000..6de5354
> --- /dev/null
> +++ b/security/tasktracker/Kconfig
> @@ -0,0 +1,35 @@
> +config SECURITY_TT
> +	bool "TaskTracker support"
> +	depends on SECURITY
> +	default n
> +	help
> +	  Existing audit logs generated via system call auditing functionality
> +	  include current thread's comm name. But it is not always sufficient
> +	  for identifying which application has requested specific operations
> +	  because comm name does not reflect history of current thread.
> +	  
> +	  This security module adds functionality for adding current thread's
> +	  history information like TOMOYO security module does, expecting that
> +	  this module can help us getting more information from system call
> +	  auditing functionality.
> +	  
> +	  If you are unsure how to answer this question, answer N.
> +	  
> +	  Usage:
> +	  
> +	  Just register this module. No configuration is needed.
> +	  
> +	  You will get history of current thread in the form of
> +	  comm name and time stamp pairs in the subj= field of audit logs
> +	  like an example shown below.
> +	  
> +	    type=SYSCALL msg=audit(1400880014.444:26): arch=40000003 syscall=11
> +	    success=yes exit=0 a0=8140f78 a1=812b7d8 a2=812b248 a3=812b7d8
> +	    items=2 ppid=4323 pid=4355 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0
> +	    egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="tail"
> +	    exe="/usr/bin/tail" subj="swapper/0(2014/05/23-21:17:30)=>
> +	    init(2014/05/23-21:17:33)=>switch_root(2014/05/23-21:17:34)=>
> +	    init(2014/05/23-21:17:34)=>sh(2014/05/23-21:17:37)=>
> +	    rc(2014/05/23-21:17:37)=>S55sshd(2014/05/23-21:17:53)=>
> +	    sshd(2014/05/23-21:17:53)=>sshd(2014/05/23-21:18:17)=>
> +	    bash(2014/05/23-21:18:21)=>tail(2014/05/23-21:20:14)" key=(null)
> diff --git a/security/tasktracker/Makefile b/security/tasktracker/Makefile
> new file mode 100644
> index 0000000..15d03ce
> --- /dev/null
> +++ b/security/tasktracker/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_SECURITY_TT) := tasktracker.o
> diff --git a/security/tasktracker/tasktracker.c b/security/tasktracker/tasktracker.c
> new file mode 100644
> index 0000000..ec4eb0c
> --- /dev/null
> +++ b/security/tasktracker/tasktracker.c
> @@ -0,0 +1,282 @@
> +/*
> + * tasktracker.c - Simplified thread information tracker.
> + *
> + * Copyright (C) 2010-2014  Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> + */
> +#include <linux/security.h>
> +#include <linux/binfmts.h>
> +
> +/* Wrapper structure for passing string buffer. */
> +struct tt_record {
> +	char history[1024];
> +};
> +
> +/* Structure for representing YYYY/MM/DD hh/mm/ss. */
> +struct tt_time {
> +	u16 year;
> +	u8 month;
> +	u8 day;
> +	u8 hour;
> +	u8 min;
> +	u8 sec;
> +};
> +
> +/**
> + * tt_get_time - Get current time in YYYY/MM/DD hh/mm/ss format.
> + *
> + * @stamp: Pointer to "struct tt_time".
> + *
> + * Returns nothing.
> + *
> + * This function does not handle Y2038 problem.
> + */
> +static void tt_get_time(struct tt_time *stamp)
> +{
> +	struct timeval tv;
> +	static const u16 tt_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 y = 1970;
> +	u8 m;
> +	bool r;
> +	time_t time;
> +	do_gettimeofday(&tv);
> +	time = tv.tv_sec;
> +	stamp->sec = time % 60;
> +	time /= 60;
> +	stamp->min = time % 60;
> +	time /= 60;
> +	stamp->hour = time % 24;
> +	time /= 24;
> +	if (time >= 16071) {
> +		/* Start from 2014/01/01 rather than 1970/01/01. */
> +		time -= 16071;
> +		y += 44;
> +	}
> +	while (1) {
> +		const unsigned short days = (y & 3) ? 365 : 366;
> +		if (time < days)
> +			break;
> +		time -= days;
> +		y++;
> +	}
> +	r = (y & 3) == 0;
> +	for (m = 0; m < 11 && time >= tt_eom[r][m]; m++)
> +		;
> +	if (m)
> +		time -= tt_eom[r][m - 1];
> +	stamp->year = y;
> +	stamp->month = ++m;
> +	stamp->day = ++time;
> +}
> +
> +/**
> + * tt_update_record - Update "struct tt_record" for given credential.
> + *
> + * @record: Pointer to "struct tt_record".
> + *
> + * Returns nothing.
> + */
> +static void tt_update_record(struct tt_record *record)
> +{
> +	char *cp;
> +	int i;
> +	struct tt_time stamp;
> +	tt_get_time(&stamp);
> +	/*
> +	 * Lockless update because current thread's record is not concurrently
> +	 * accessible, for "struct cred"->security is not visible from other
> +	 * threads because this function is called upon only boot up and
> +	 * successful execve() operation.
> +	 */
> +	cp = record->history;
> +	i = strlen(cp);
> +	while (i >= sizeof(record->history) - (TASK_COMM_LEN * 4 + 30)) {
> +		/*
> +		 * Since this record is not for making security decision,
> +		 * I don't care by-chance matching "=>" in task's commname.
> +		 */
> +		char *cp2 = strstr(cp + 2, "=>");
> +		if (!cp2)
> +			return;
> +		memmove(cp + 1, cp2, strlen(cp2) + 1);
> +		i = strlen(cp);
> +	}
> +	if (!i)
> +		*cp++ = '"';
> +	else {
> +		cp += i - 1;
> +		*cp++ = '=';
> +		*cp++ = '>';
> +	}
> +	/*
> +	 * Lockless read because this is current thread and being unexpectedly
> +	 * modified by other thread is not a fatal problem.
> +	 */
> +	for (i = 0; i < TASK_COMM_LEN; i++) {
> +		const unsigned char c = current->comm[i];
> +		if (!c)
> +			break;
> +		else if (c == '"' || c == '\\' || c < 0x21 || c > 0x7e) {
> +			*cp++ = '\\';
> +			*cp++ = (c >> 6) + '0';
> +			*cp++ = ((c >> 3) & 7) + '0';
> +			*cp++ = (c & 7) + '0';
> +		} else
> +			*cp++ = c;
> +	}
> +	sprintf(cp, "(%04u/%02u/%02u-%02u:%02u:%02u)\"", stamp.year,
> +		stamp.month, stamp.day, stamp.hour, stamp.min, stamp.sec);
> +}
> +
> +/**
> + * tt_find_record - Find "struct tt_record" for given credential.
> + *
> + * @cred: Pointer to "struct cred".
> + *
> + * Returns pointer to "struct tt_record".
> + */
> +static inline struct tt_record *tt_find_record(const struct cred *cred)
> +{
> +	return cred->security;
> +}
> +
> +/**
> + * tt_cred_alloc_blank - Allocate memory for new credentials.
> + *
> + * @new: Pointer to "struct cred".
> + * @gfp: Memory allocation flags.
> + *
> + * Returns 0 on success, negative value otherwise.
> + */
> +static int tt_cred_alloc_blank(struct cred *new, gfp_t gfp)
> +{
> +	new->security = kzalloc(sizeof(struct tt_record), gfp);
> +	return new->security ? 0 : -ENOMEM;
> +}
> +
> +/**
> + * tt_cred_prepare - Allocate memory for new credentials.
> + *
> + * @new: Pointer to "struct cred".
> + * @old: Pointer to "struct cred".
> + * @gfp: Memory allocation flags.
> + *
> + * Returns 0 on success, negative value otherwise.
> + */
> +static int tt_cred_prepare(struct cred *new, const struct cred *old,
> +			   gfp_t gfp)
> +{
> +	if (tt_cred_alloc_blank(new, gfp))
> +		return -ENOMEM;
> +	strcpy(tt_find_record(new)->history, tt_find_record(old)->history);
> +	return 0;
> +}
> +
> +/**
> + * tt_cred_free - Release memory used by credentials.
> + *
> + * @cred: Pointer to "struct cred".
> + *
> + * Returns nothing.
> + */
> +static void tt_cred_free(struct cred *cred)
> +{
> +	kfree(cred->security);
> +}
> +
> +/**
> + * tt_cred_transfer - Transfer "struct tt_record" between credentials.
> + *
> + * @new: Pointer to "struct cred".
> + * @old: Pointer to "struct cred".
> + *
> + * Returns nothing.
> + */
> +static void tt_cred_transfer(struct cred *new, const struct cred *old)
> +{
> +	strcpy(tt_find_record(new)->history, tt_find_record(old)->history);
> +}
> +
> +/**
> + * tt_bprm_committing_creds - A hook which is called when do_execve() succeeded.
> + *
> + * @bprm: Pointer to "struct linux_binprm".
> + *
> + * Returns nothing.
> + */
> +static void tt_bprm_committing_creds(struct linux_binprm *bprm)
> +{
> +	tt_update_record(tt_find_record(bprm->cred));
> +}
> +
> +/**
> + * tt_task_getsecid - Check whether to audit or not.
> + *
> + * @p:     Pointer to "struct task_struct".
> + * @secid: Pointer to flag.
> + */
> +static void tt_task_getsecid(struct task_struct *p, u32 *secid)
> +{
> +	*secid = (p == current);
> +}
> +
> +/**
> + * tt_secid_to_secctx - Allocate memory used for auditing.
> + *
> + * @secid:   Bool flag to allocate.
> + * @secdata: Pointer to allocate memory.
> + * @seclen:  Size of allocated memory.
> + *
> + * Returns 0 on success, -EINVAL otherwise.
> + */
> +static int tt_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
> +{
> +	struct tt_record *record;
> +	/* Ignore unless current thread's record is requested. */
> +	if (secid != 1)
> +		return -EINVAL;
> +	/*
> +	 * We don't need to duplicate the string because current thread's
> +	 * record is updated upon only boot up and successful execve()
> +	 * operation, even if current thread's record is shared between
> +	 * multiple threads.
> +	 */
> +	record = tt_find_record(current->real_cred);
> +	*secdata = record->history;
> +	*seclen = strlen(record->history);
> +	return 0;
> +}
> +
> +/* List of hooks. */
> +static struct security_operations tasktracker_ops = {
> +	.name                  = "tt",
> +	.secid_to_secctx       = tt_secid_to_secctx,
> +	.task_getsecid         = tt_task_getsecid,
> +	.cred_prepare          = tt_cred_prepare,
> +	.cred_free             = tt_cred_free,
> +	.cred_alloc_blank      = tt_cred_alloc_blank,
> +	.cred_transfer         = tt_cred_transfer,
> +	.bprm_committing_creds = tt_bprm_committing_creds,
> +};
> +
> +/**
> + * tt_init - Initialize this module.
> + *
> + * Returns 0 on success, panic otherwise.
> + */
> +static int __init tt_init(void)
> +{
> +	struct cred *cred = (struct cred *) current_cred();
> +	if (!security_module_enable(&tasktracker_ops))
> +		return 0;
> +	if (tt_cred_alloc_blank(cred, GFP_ATOMIC) ||
> +	    register_security(&tasktracker_ops))
> +		panic("Failure registering TaskTracker");
> +	tt_update_record(tt_find_record(cred));
> +	pr_info("TaskTracker initialized\n");
> +	return 0;
> +}
> +
> +security_initcall(tt_init);
> -- 
> 1.7.1
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-06-23 12:14 ` Tetsuo Handa
@ 2014-06-24 14:00   ` Steve Grubb
  2014-06-26 11:40     ` Tetsuo Handa
  0 siblings, 1 reply; 20+ messages in thread
From: Steve Grubb @ 2014-06-24 14:00 UTC (permalink / raw)
  To: linux-audit; +Cc: Tetsuo Handa, linux-security-module

On Monday, June 23, 2014 09:14:35 PM Tetsuo Handa wrote:
> Any comments on this proposal?

subj= is the wrong way to record this. The subj field name is for process 
labels. When field names get re-used for different purposes, it causes lots of 
problems in being able to assign meaning and correctly use it in analysis. I 
would suggest using phist= for process history or something like that. Please 
don't re-use subj for this.

Also, the comm file is under control of the user. What if they create a program 
"sshd=>crond"? Would that throw off the analysis? How do you ensure user 
supplied names do not contain symbols that you are using to denote parentage?

Also, would you consider adding this information as a auxiliary record rather 
than as part of a syscall record? The advantage is it can be filtered or 
searched for. We recently did this for PROCTITLE information. Perhaps this fits 
better as a PROCHIST auxiliary record?

-Steve


> For now this module is occupying the exclusive LSM hook. But since this
> module is for tracking per a "struct task_struct" attributes rather than
> for making security decisions, I expect that this module can co-exist with
> other LSM modules by not occupying the exclusive LSM hook.
> 
> Tetsuo Handa wrote:
> > Yesterday I went to LinuxCon Japan 2014 and stopped at Red Hat's booth
> > and Oracle's booth. I explained about this module ( using page 92 of
> > http://I-love.SAKURA.ne.jp/tomoyo/LCJ2014-en.pdf ) and got positive
> > responses from persons who have experienced troubleshooting jobs.
> > I was convinced that I am not the only person who is bothered by lack of
> > process history information in the logs. Therefore, I repost this module
> > toward inclusion into mainline Linux kernel.
> > 
> > Changes from previous version ( http://lwn.net/Articles/575044/ ):
> >   (1) Assign a value to "u32 *seclen" in addition to "char *secdata"
> >   
> >       at security_task_getsecid() hook.
> >   
> >   (2) Make calculation of time stamp a bit faster.
> > 
> > Background:
> >   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.
> >   
> >   I sometimes receive "which application triggered the event" questions on
> >   RHEL systems. TOMOYO security module can track how the program was
> >   executed, but TOMOYO is not yet available in Fedora/RHEL distributions.
> >   
> >   Although subj= field is added to the audit log if SELinux is not
> >   disabled,
> >   SELinux is too difficult to customize as fine grained as I expect in
> >   order to reflect how the program was executed. Therefore, I'm currently
> >   using AKARI and SystemTap for emulating TOMOYO-like tracing.
> >   
> >   But AKARI and SystemTap do not help unless the kernel module is loaded
> >   before the unexpected system event occurs. Generally, the administrator
> >   is failing to record the first event, and has to wait for the same
> >   event to occur again after loading the kernel module and/or configuring
> >   auditing. I came to think that we want a built-in kernel routine which
> >   is automatically started upon boot so that we don't fail to record the
> >   first event.
> > 
> > What I did:
> >   Assuming that multiple concurrent LSM support comes in the near future,
> >   I wrote a trivial LSM module which emits TOMOYO-like information into
> >   the
> >   audit logs.
> > 
> > Usage:
> >   Just register this LSM module. No configuration is needed. You will get
> >   history of current thread in the form of comm name and time stamp pairs
> >   in the subj= field of audit logs like examples shown in the patch
> >   description.
> > 
> > ----------
> > 
> > >From ff68d3a4cd496bd263d2939848777fffc30cbc0b Mon Sep 17 00:00:00 2001
> > 
> > From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> > Date: Fri, 23 May 2014 21:31:56 +0900
> > Subject: [PATCH] TaskTracker : Simplified thread information tracker.
> > 
> > Existing audit logs generated via system call auditing functionality
> > include current thread's comm name. But it is not always sufficient for
> > identifying which application has requested specific operations because
> > comm name does not reflect history of current thread.
> > 
> > This security module adds functionality for adding current thread's
> > history
> > information like TOMOYO security module does, expecting that this module
> > can help us getting more information from system call auditing
> > functionality.> 
> >   type=USER_LOGIN msg=audit(1400879947.084:24): pid=4308 uid=0 auid=0
> >   ses=2
> >   subj="swapper/0(2014/05/23-21:17:30)=>init(2014/05/23-21:17:33)=>
> >   switch_root(2014/05/23-21:17:34)=>init(2014/05/23-21:17:34)=>
> >   sh(2014/05/23-21:17:56)=>mingetty(2014/05/23-21:17:56)=>
> >   login(2014/05/23-21:19:05)" msg='op=login id=0 exe="/bin/login"
> >   hostname=?
> >   addr=? terminal=tty1 res=success'
> >   
> >   type=SYSCALL msg=audit(1400880014.444:26): arch=40000003 syscall=11
> >   success=yes exit=0 a0=8140f78 a1=812b7d8 a2=812b248 a3=812b7d8 items=2
> >   ppid=4323 pid=4355 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0
> >   sgid=0
> >   fsgid=0 tty=pts0 ses=1 comm="tail" exe="/usr/bin/tail"
> >   subj="swapper/0(2014/05/23-21:17:30)=>init(2014/05/23-21:17:33)=>
> >   switch_root(2014/05/23-21:17:34)=>init(2014/05/23-21:17:34)=>
> >   sh(2014/05/23-21:17:37)=>rc(2014/05/23-21:17:37)=>
> >   S55sshd(2014/05/23-21:17:53)=>sshd(2014/05/23-21:17:53)=>
> >   sshd(2014/05/23-21:18:17)=>bash(2014/05/23-21:18:21)=>
> >   tail(2014/05/23-21:20:14)" key=(null)
> > 
> > Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> > ---
> > 
> >  security/Kconfig                   |    6 +
> >  security/Makefile                  |    2 +
> >  security/tasktracker/Kconfig       |   35 +++++
> >  security/tasktracker/Makefile      |    1 +
> >  security/tasktracker/tasktracker.c |  282
> >  ++++++++++++++++++++++++++++++++++++ 5 files changed, 326 insertions(+),
> >  0 deletions(-)
> >  create mode 100644 security/tasktracker/Kconfig
> >  create mode 100644 security/tasktracker/Makefile
> >  create mode 100644 security/tasktracker/tasktracker.c
> > 
> > diff --git a/security/Kconfig b/security/Kconfig
> > index beb86b5..14e7d27 100644
> > --- a/security/Kconfig
> > +++ b/security/Kconfig
> > @@ -122,6 +122,7 @@ source security/smack/Kconfig
> > 
> >  source security/tomoyo/Kconfig
> >  source security/apparmor/Kconfig
> >  source security/yama/Kconfig
> > 
> > +source security/tasktracker/Kconfig
> > 
> >  source security/integrity/Kconfig
> > 
> > @@ -132,6 +133,7 @@ choice
> > 
> >  	default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
> >  	default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
> >  	default DEFAULT_SECURITY_YAMA if SECURITY_YAMA
> > 
> > +	default DEFAULT_SECURITY_TT if SECURITY_TT
> > 
> >  	default DEFAULT_SECURITY_DAC
> >  	
> >  	help
> > 
> > @@ -153,6 +155,9 @@ choice
> > 
> >  	config DEFAULT_SECURITY_YAMA
> >  	
> >  		bool "Yama" if SECURITY_YAMA=y
> > 
> > +	config DEFAULT_SECURITY_TT
> > +		bool "TaskTracker" if SECURITY_TT=y
> > +
> > 
> >  	config DEFAULT_SECURITY_DAC
> >  	
> >  		bool "Unix Discretionary Access Controls"
> > 
> > @@ -165,6 +170,7 @@ config DEFAULT_SECURITY
> > 
> >  	default "tomoyo" if DEFAULT_SECURITY_TOMOYO
> >  	default "apparmor" if DEFAULT_SECURITY_APPARMOR
> >  	default "yama" if DEFAULT_SECURITY_YAMA
> > 
> > +	default "tt" if DEFAULT_SECURITY_TT
> > 
> >  	default "" if DEFAULT_SECURITY_DAC
> >  
> >  endmenu
> > 
> > diff --git a/security/Makefile b/security/Makefile
> > index 05f1c93..28a90ed 100644
> > --- a/security/Makefile
> > +++ b/security/Makefile
> > @@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK)		+= smack
> > 
> >  subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
> >  subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
> >  subdir-$(CONFIG_SECURITY_YAMA)		+= yama
> > 
> > +subdir-$(CONFIG_SECURITY_TT)		+= tasktracker
> > 
> >  # always enable default capabilities
> >  obj-y					+= commoncap.o
> > 
> > @@ -22,6 +23,7 @@ obj-$(CONFIG_AUDIT)			+= lsm_audit.o
> > 
> >  obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/
> >  obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/
> >  obj-$(CONFIG_SECURITY_YAMA)		+= yama/
> > 
> > +obj-$(CONFIG_SECURITY_TT)		+= tasktracker/
> > 
> >  obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
> >  
> >  # Object integrity file lists
> > 
> > diff --git a/security/tasktracker/Kconfig b/security/tasktracker/Kconfig
> > new file mode 100644
> > index 0000000..6de5354
> > --- /dev/null
> > +++ b/security/tasktracker/Kconfig
> > @@ -0,0 +1,35 @@
> > +config SECURITY_TT
> > +	bool "TaskTracker support"
> > +	depends on SECURITY
> > +	default n
> > +	help
> > +	  Existing audit logs generated via system call auditing 
functionality
> > +	  include current thread's comm name. But it is not always sufficient
> > +	  for identifying which application has requested specific operations
> > +	  because comm name does not reflect history of current thread.
> > +
> > +	  This security module adds functionality for adding current thread's
> > +	  history information like TOMOYO security module does, expecting 
that
> > +	  this module can help us getting more information from system call
> > +	  auditing functionality.
> > +
> > +	  If you are unsure how to answer this question, answer N.
> > +
> > +	  Usage:
> > +
> > +	  Just register this module. No configuration is needed.
> > +
> > +	  You will get history of current thread in the form of
> > +	  comm name and time stamp pairs in the subj= field of audit logs
> > +	  like an example shown below.
> > +
> > +	    type=SYSCALL msg=audit(1400880014.444:26): arch=40000003 
syscall=11
> > +	    success=yes exit=0 a0=8140f78 a1=812b7d8 a2=812b248 a3=812b7d8
> > +	    items=2 ppid=4323 pid=4355 auid=0 uid=0 gid=0 euid=0 suid=0 
fsuid=0
> > +	    egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="tail"
> > +	    exe="/usr/bin/tail" subj="swapper/0(2014/05/23-21:17:30)=>
> > +	    init(2014/05/23-21:17:33)=>switch_root(2014/05/23-21:17:34)=>
> > +	    init(2014/05/23-21:17:34)=>sh(2014/05/23-21:17:37)=>
> > +	    rc(2014/05/23-21:17:37)=>S55sshd(2014/05/23-21:17:53)=>
> > +	    sshd(2014/05/23-21:17:53)=>sshd(2014/05/23-21:18:17)=>
> > +	    bash(2014/05/23-21:18:21)=>tail(2014/05/23-21:20:14)" key=(null)
> > diff --git a/security/tasktracker/Makefile b/security/tasktracker/Makefile
> > new file mode 100644
> > index 0000000..15d03ce
> > --- /dev/null
> > +++ b/security/tasktracker/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_SECURITY_TT) := tasktracker.o
> > diff --git a/security/tasktracker/tasktracker.c
> > b/security/tasktracker/tasktracker.c new file mode 100644
> > index 0000000..ec4eb0c
> > --- /dev/null
> > +++ b/security/tasktracker/tasktracker.c
> > @@ -0,0 +1,282 @@
> > +/*
> > + * tasktracker.c - Simplified thread information tracker.
> > + *
> > + * Copyright (C) 2010-2014  Tetsuo Handa
> > <penguin-kernel@I-love.SAKURA.ne.jp> + */
> > +#include <linux/security.h>
> > +#include <linux/binfmts.h>
> > +
> > +/* Wrapper structure for passing string buffer. */
> > +struct tt_record {
> > +	char history[1024];
> > +};
> > +
> > +/* Structure for representing YYYY/MM/DD hh/mm/ss. */
> > +struct tt_time {
> > +	u16 year;
> > +	u8 month;
> > +	u8 day;
> > +	u8 hour;
> > +	u8 min;
> > +	u8 sec;
> > +};
> > +
> > +/**
> > + * tt_get_time - Get current time in YYYY/MM/DD hh/mm/ss format.
> > + *
> > + * @stamp: Pointer to "struct tt_time".
> > + *
> > + * Returns nothing.
> > + *
> > + * This function does not handle Y2038 problem.
> > + */
> > +static void tt_get_time(struct tt_time *stamp)
> > +{
> > +	struct timeval tv;
> > +	static const u16 tt_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 y = 1970;
> > +	u8 m;
> > +	bool r;
> > +	time_t time;
> > +	do_gettimeofday(&tv);
> > +	time = tv.tv_sec;
> > +	stamp->sec = time % 60;
> > +	time /= 60;
> > +	stamp->min = time % 60;
> > +	time /= 60;
> > +	stamp->hour = time % 24;
> > +	time /= 24;
> > +	if (time >= 16071) {
> > +		/* Start from 2014/01/01 rather than 1970/01/01. */
> > +		time -= 16071;
> > +		y += 44;
> > +	}
> > +	while (1) {
> > +		const unsigned short days = (y & 3) ? 365 : 366;
> > +		if (time < days)
> > +			break;
> > +		time -= days;
> > +		y++;
> > +	}
> > +	r = (y & 3) == 0;
> > +	for (m = 0; m < 11 && time >= tt_eom[r][m]; m++)
> > +		;
> > +	if (m)
> > +		time -= tt_eom[r][m - 1];
> > +	stamp->year = y;
> > +	stamp->month = ++m;
> > +	stamp->day = ++time;
> > +}
> > +
> > +/**
> > + * tt_update_record - Update "struct tt_record" for given credential.
> > + *
> > + * @record: Pointer to "struct tt_record".
> > + *
> > + * Returns nothing.
> > + */
> > +static void tt_update_record(struct tt_record *record)
> > +{
> > +	char *cp;
> > +	int i;
> > +	struct tt_time stamp;
> > +	tt_get_time(&stamp);
> > +	/*
> > +	 * Lockless update because current thread's record is not 
concurrently
> > +	 * accessible, for "struct cred"->security is not visible from other
> > +	 * threads because this function is called upon only boot up and
> > +	 * successful execve() operation.
> > +	 */
> > +	cp = record->history;
> > +	i = strlen(cp);
> > +	while (i >= sizeof(record->history) - (TASK_COMM_LEN * 4 + 30)) {
> > +		/*
> > +		 * Since this record is not for making security decision,
> > +		 * I don't care by-chance matching "=>" in task's commname.
> > +		 */
> > +		char *cp2 = strstr(cp + 2, "=>");
> > +		if (!cp2)
> > +			return;
> > +		memmove(cp + 1, cp2, strlen(cp2) + 1);
> > +		i = strlen(cp);
> > +	}
> > +	if (!i)
> > +		*cp++ = '"';
> > +	else {
> > +		cp += i - 1;
> > +		*cp++ = '=';
> > +		*cp++ = '>';
> > +	}
> > +	/*
> > +	 * Lockless read because this is current thread and being 
unexpectedly
> > +	 * modified by other thread is not a fatal problem.
> > +	 */
> > +	for (i = 0; i < TASK_COMM_LEN; i++) {
> > +		const unsigned char c = current->comm[i];
> > +		if (!c)
> > +			break;
> > +		else if (c == '"' || c == '\\' || c < 0x21 || c > 0x7e) {
> > +			*cp++ = '\\';
> > +			*cp++ = (c >> 6) + '0';
> > +			*cp++ = ((c >> 3) & 7) + '0';
> > +			*cp++ = (c & 7) + '0';
> > +		} else
> > +			*cp++ = c;
> > +	}
> > +	sprintf(cp, "(%04u/%02u/%02u-%02u:%02u:%02u)\"", stamp.year,
> > +		stamp.month, stamp.day, stamp.hour, stamp.min, stamp.sec);
> > +}
> > +
> > +/**
> > + * tt_find_record - Find "struct tt_record" for given credential.
> > + *
> > + * @cred: Pointer to "struct cred".
> > + *
> > + * Returns pointer to "struct tt_record".
> > + */
> > +static inline struct tt_record *tt_find_record(const struct cred *cred)
> > +{
> > +	return cred->security;
> > +}
> > +
> > +/**
> > + * tt_cred_alloc_blank - Allocate memory for new credentials.
> > + *
> > + * @new: Pointer to "struct cred".
> > + * @gfp: Memory allocation flags.
> > + *
> > + * Returns 0 on success, negative value otherwise.
> > + */
> > +static int tt_cred_alloc_blank(struct cred *new, gfp_t gfp)
> > +{
> > +	new->security = kzalloc(sizeof(struct tt_record), gfp);
> > +	return new->security ? 0 : -ENOMEM;
> > +}
> > +
> > +/**
> > + * tt_cred_prepare - Allocate memory for new credentials.
> > + *
> > + * @new: Pointer to "struct cred".
> > + * @old: Pointer to "struct cred".
> > + * @gfp: Memory allocation flags.
> > + *
> > + * Returns 0 on success, negative value otherwise.
> > + */
> > +static int tt_cred_prepare(struct cred *new, const struct cred *old,
> > +			   gfp_t gfp)
> > +{
> > +	if (tt_cred_alloc_blank(new, gfp))
> > +		return -ENOMEM;
> > +	strcpy(tt_find_record(new)->history, tt_find_record(old)->history);
> > +	return 0;
> > +}
> > +
> > +/**
> > + * tt_cred_free - Release memory used by credentials.
> > + *
> > + * @cred: Pointer to "struct cred".
> > + *
> > + * Returns nothing.
> > + */
> > +static void tt_cred_free(struct cred *cred)
> > +{
> > +	kfree(cred->security);
> > +}
> > +
> > +/**
> > + * tt_cred_transfer - Transfer "struct tt_record" between credentials.
> > + *
> > + * @new: Pointer to "struct cred".
> > + * @old: Pointer to "struct cred".
> > + *
> > + * Returns nothing.
> > + */
> > +static void tt_cred_transfer(struct cred *new, const struct cred *old)
> > +{
> > +	strcpy(tt_find_record(new)->history, tt_find_record(old)->history);
> > +}
> > +
> > +/**
> > + * tt_bprm_committing_creds - A hook which is called when do_execve()
> > succeeded. + *
> > + * @bprm: Pointer to "struct linux_binprm".
> > + *
> > + * Returns nothing.
> > + */
> > +static void tt_bprm_committing_creds(struct linux_binprm *bprm)
> > +{
> > +	tt_update_record(tt_find_record(bprm->cred));
> > +}
> > +
> > +/**
> > + * tt_task_getsecid - Check whether to audit or not.
> > + *
> > + * @p:     Pointer to "struct task_struct".
> > + * @secid: Pointer to flag.
> > + */
> > +static void tt_task_getsecid(struct task_struct *p, u32 *secid)
> > +{
> > +	*secid = (p == current);
> > +}
> > +
> > +/**
> > + * tt_secid_to_secctx - Allocate memory used for auditing.
> > + *
> > + * @secid:   Bool flag to allocate.
> > + * @secdata: Pointer to allocate memory.
> > + * @seclen:  Size of allocated memory.
> > + *
> > + * Returns 0 on success, -EINVAL otherwise.
> > + */
> > +static int tt_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
> > +{
> > +	struct tt_record *record;
> > +	/* Ignore unless current thread's record is requested. */
> > +	if (secid != 1)
> > +		return -EINVAL;
> > +	/*
> > +	 * We don't need to duplicate the string because current thread's
> > +	 * record is updated upon only boot up and successful execve()
> > +	 * operation, even if current thread's record is shared between
> > +	 * multiple threads.
> > +	 */
> > +	record = tt_find_record(current->real_cred);
> > +	*secdata = record->history;
> > +	*seclen = strlen(record->history);
> > +	return 0;
> > +}
> > +
> > +/* List of hooks. */
> > +static struct security_operations tasktracker_ops = {
> > +	.name                  = "tt",
> > +	.secid_to_secctx       = tt_secid_to_secctx,
> > +	.task_getsecid         = tt_task_getsecid,
> > +	.cred_prepare          = tt_cred_prepare,
> > +	.cred_free             = tt_cred_free,
> > +	.cred_alloc_blank      = tt_cred_alloc_blank,
> > +	.cred_transfer         = tt_cred_transfer,
> > +	.bprm_committing_creds = tt_bprm_committing_creds,
> > +};
> > +
> > +/**
> > + * tt_init - Initialize this module.
> > + *
> > + * Returns 0 on success, panic otherwise.
> > + */
> > +static int __init tt_init(void)
> > +{
> > +	struct cred *cred = (struct cred *) current_cred();
> > +	if (!security_module_enable(&tasktracker_ops))
> > +		return 0;
> > +	if (tt_cred_alloc_blank(cred, GFP_ATOMIC) ||
> > +	    register_security(&tasktracker_ops))
> > +		panic("Failure registering TaskTracker");
> > +	tt_update_record(tt_find_record(cred));
> > +	pr_info("TaskTracker initialized\n");
> > +	return 0;
> > +}
> > +
> > +security_initcall(tt_init);
> 
> --
> Linux-audit mailing list
> Linux-audit@redhat.com
> https://www.redhat.com/mailman/listinfo/linux-audit


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-06-24 14:00   ` Steve Grubb
@ 2014-06-26 11:40     ` Tetsuo Handa
  2014-09-27  1:02       ` Tetsuo Handa
  0 siblings, 1 reply; 20+ messages in thread
From: Tetsuo Handa @ 2014-06-26 11:40 UTC (permalink / raw)
  To: sgrubb, linux-audit; +Cc: linux-security-module

Thank you for your comment, Steve.

Steve Grubb wrote:
> On Monday, June 23, 2014 09:14:35 PM Tetsuo Handa wrote:
>> Any comments on this proposal?
>
> subj= is the wrong way to record this. The subj field name is for process 
> labels. When field names get re-used for different purposes, it causes lots of 
> problems in being able to assign meaning and correctly use it in analysis. I 
> would suggest using phist= for process history or something like that. Please 
> don't re-use subj for this.

This was just a sample implementation. If this proposal is acceptable as a
patch to auditing subsystem, I'm happy to update not to re-use subj= field
and not to occupy LSM. An updated version is attached.

> Also, the comm file is under control of the user. What if they create a program 
> "sshd=>crond"? Would that throw off the analysis? How do you ensure user 
> supplied names do not contain symbols that you are using to denote parentage?

OK. I added '=' in comm name to the list of need-to-escape bytes.

By the way, audit_string_contains_control() treats *p == '"' || *p < 0x21 ||
*p > 0x7e as need-to-escape bytes. Thus, 0x20 from audit_log_untrustedstring()
is a need-to-escape byte. However, I can see that 0x20 from userspace programs
is emitted without escaping.

  type=USER_START msg=audit(1403741835.270:16): user pid=1870 uid=0 auid=0 ses=1
  msg='op=login id=0 exe="/usr/sbin/sshd" hostname=192.168.0.1 addr=192.168.0.1 
  terminal=/dev/pts/0 res=success'

Where can I find which bytes in $value need to be escaped when emitting
a record like name='$value' ? Is 0x20 in $value permitted?

> Also, would you consider adding this information as a auxiliary record rather 
> than as part of a syscall record? The advantage is it can be filtered or 
> searched for. We recently did this for PROCTITLE information. Perhaps this fits 
> better as a PROCHIST auxiliary record?

I changed to use auxiliary record and noticed a difference.
The previous version emitted the history for type=USER_LOGIN case

  type=USER_LOGIN msg=audit(1400879947.084:24): pid=4308 uid=0 auid=0 ses=2
  subj="swapper/0(2014/05/23-21:17:30)=>init(2014/05/23-21:17:33)=>
  switch_root(2014/05/23-21:17:34)=>init(2014/05/23-21:17:34)=>
  sh(2014/05/23-21:17:56)=>mingetty(2014/05/23-21:17:56)=>
  login(2014/05/23-21:19:05)" msg='op=login id=0 exe="/bin/login" hostname=?
  addr=? terminal=tty1 res=success'

but current version does not emit it for type=USER_LOGIN case.
Does auxiliary record work with only type=SYSCALL case (and therefore
I should use CONFIG_AUDITSYSCALL rather than CONFIG_AUDIT in the patch
below) ?

Regards.
----------
>From d015533ce544feb8922fcbf023017c82bd79a9ac Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Thu, 26 Jun 2014 09:39:14 +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)'

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 fs/exec.c                  |    1 +
 include/linux/audit.h      |   23 +++++++++++-
 include/linux/init_task.h  |    9 ++++
 include/linux/sched.h      |    5 ++
 include/uapi/linux/audit.h |    1 +
 kernel/audit.c             |   90 ++++++++++++++++++++++++++++++++++++++++++++
 kernel/auditsc.c           |   19 +++++++++
 7 files changed, 147 insertions(+), 1 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index a3d33fe..fcda589 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1195,6 +1195,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..526525b 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -138,6 +138,9 @@ static inline int audit_dummy_context(void)
 }
 static inline void audit_free(struct task_struct *task)
 {
+	extern void kfree(const void *);
+	kfree(task->comm_history);
+	task->comm_history = NULL;
 	if (unlikely(task->audit_context))
 		__audit_free(task);
 }
@@ -318,10 +321,22 @@ extern int audit_signals;
 #else /* CONFIG_AUDITSYSCALL */
 static inline int audit_alloc(struct task_struct *task)
 {
+#ifdef CONFIG_AUDIT
+	task->comm_history = kmemdup(current->comm_history, COMM_HISTORY_SIZE,
+				     GFP_KERNEL);
+	return task->comm_history ? 0 : -ENOMEM;
+#else
 	return 0;
+#endif
 }
 static inline void audit_free(struct task_struct *task)
-{ }
+{
+#ifdef CONFIG_AUDIT
+	extern void kfree(const void *);
+	kfree(task->comm_history);
+	task->comm_history = NULL;
+#endif
+}
 static inline void audit_syscall_entry(int arch, int major, unsigned long a0,
 				       unsigned long a1, unsigned long a2,
 				       unsigned long a3)
@@ -532,5 +547,11 @@ static inline void audit_log_string(struct audit_buffer *ab, const char *buf)
 {
 	audit_log_n_string(ab, buf, strlen(buf));
 }
+#ifdef CONFIG_AUDIT
+extern void audit_update_history(void);
+#else
+static inline void audit_update_history(void)
+{ }
+#endif
 
 #endif
diff --git a/include/linux/init_task.h b/include/linux/init_task.h
index 6df7f9f..3bad194 100644
--- a/include/linux/init_task.h
+++ b/include/linux/init_task.h
@@ -164,6 +164,14 @@ extern struct task_group root_task_group;
 # define INIT_RT_MUTEXES(tsk)
 #endif
 
+#ifdef CONFIG_AUDIT
+extern char init_task_history[COMM_HISTORY_SIZE];
+#define INIT_THREAD_HISTORY(tsk)		\
+	.comm_history = init_task_history,
+#else
+#define INIT_THREAD_HISTORY(tsk)
+#endif
+
 /*
  *  INIT_TASK is used to set up the first task table, touch at
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
@@ -234,6 +242,7 @@ extern struct task_group root_task_group;
 	INIT_CPUSET_SEQ(tsk)						\
 	INIT_RT_MUTEXES(tsk)						\
 	INIT_VTIME(tsk)							\
+	INIT_THREAD_HISTORY(tsk)					\
 }
 
 
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 306f4f0..f23fd73 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -264,6 +264,8 @@ extern char ___assert_task_state[1 - 2*!!(
 
 /* Task command name length */
 #define TASK_COMM_LEN 16
+/* Task command name history length */
+#define COMM_HISTORY_SIZE 1024
 
 #include <linux/spinlock.h>
 
@@ -1655,6 +1657,9 @@ struct task_struct {
 	unsigned int	sequential_io;
 	unsigned int	sequential_io_avg;
 #endif
+#ifdef CONFIG_AUDIT
+	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 cf67147..b9e051c 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 3ef2e0e..20441c9 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1158,11 +1158,101 @@ static struct pernet_operations audit_net_ops __net_initdata = {
 	.size = sizeof(struct audit_net),
 };
 
+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 = strstr(history + 2, "=>");
+
+		if (!cp)
+			return;
+		memmove(history, cp, strlen(cp) + 1);
+		pos = strlen(history);
+	}
+	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];
+	sprintf(history, "(%04u/%02u/%02u-%02u:%02u:%02u)", year, month + 1,
+		day + 1, hour, minute, second);
+}
+
 /* Initialize audit support at boot time. */
 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..4172633 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -950,6 +950,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 +965,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 +1356,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 +1485,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);
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-06-26 11:40     ` Tetsuo Handa
@ 2014-09-27  1:02       ` Tetsuo Handa
  2014-09-27 13:14         ` Steve Grubb
  0 siblings, 1 reply; 20+ messages in thread
From: Tetsuo Handa @ 2014-09-27  1:02 UTC (permalink / raw)
  To: sgrubb, linux-audit; +Cc: linux-security-module

May I continue proposing this functionality? I want to identify where bash is
executed from in the RHEL servers in order to assess possibility of damage
caused by CVE-2014-6271.

Tetsuo Handa wrote:
> Thank you for your comment, Steve.
> 
> Steve Grubb wrote:
> > On Monday, June 23, 2014 09:14:35 PM Tetsuo Handa wrote:
> >> Any comments on this proposal?
> >
> > subj= is the wrong way to record this. The subj field name is for process 
> > labels. When field names get re-used for different purposes, it causes lots of 
> > problems in being able to assign meaning and correctly use it in analysis. I 
> > would suggest using phist= for process history or something like that. Please 
> > don't re-use subj for this.
> 
> This was just a sample implementation. If this proposal is acceptable as a
> patch to auditing subsystem, I'm happy to update not to re-use subj= field
> and not to occupy LSM. An updated version is attached.
> 
> > Also, the comm file is under control of the user. What if they create a program 
> > "sshd=>crond"? Would that throw off the analysis? How do you ensure user 
> > supplied names do not contain symbols that you are using to denote parentage?
> 
> OK. I added '=' in comm name to the list of need-to-escape bytes.
> 
> By the way, audit_string_contains_control() treats *p == '"' || *p < 0x21 ||
> *p > 0x7e as need-to-escape bytes. Thus, 0x20 from audit_log_untrustedstring()
> is a need-to-escape byte. However, I can see that 0x20 from userspace programs
> is emitted without escaping.
> 
>   type=USER_START msg=audit(1403741835.270:16): user pid=1870 uid=0 auid=0 ses=1
>   msg='op=login id=0 exe="/usr/sbin/sshd" hostname=192.168.0.1 addr=192.168.0.1 
>   terminal=/dev/pts/0 res=success'
> 
> Where can I find which bytes in $value need to be escaped when emitting
> a record like name='$value' ? Is 0x20 in $value permitted?
> 
> > Also, would you consider adding this information as a auxiliary record rather 
> > than as part of a syscall record? The advantage is it can be filtered or 
> > searched for. We recently did this for PROCTITLE information. Perhaps this fits 
> > better as a PROCHIST auxiliary record?
> 
> I changed to use auxiliary record and noticed a difference.
> The previous version emitted the history for type=USER_LOGIN case
> 
>   type=USER_LOGIN msg=audit(1400879947.084:24): pid=4308 uid=0 auid=0 ses=2
>   subj="swapper/0(2014/05/23-21:17:30)=>init(2014/05/23-21:17:33)=>
>   switch_root(2014/05/23-21:17:34)=>init(2014/05/23-21:17:34)=>
>   sh(2014/05/23-21:17:56)=>mingetty(2014/05/23-21:17:56)=>
>   login(2014/05/23-21:19:05)" msg='op=login id=0 exe="/bin/login" hostname=?
>   addr=? terminal=tty1 res=success'
> 
> but current version does not emit it for type=USER_LOGIN case.
> Does auxiliary record work with only type=SYSCALL case (and therefore
> I should use CONFIG_AUDITSYSCALL rather than CONFIG_AUDIT in the patch
> below) ?
> 
> Regards.
> ----------
> >From d015533ce544feb8922fcbf023017c82bd79a9ac Mon Sep 17 00:00:00 2001
> From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Date: Thu, 26 Jun 2014 09:39:14 +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)'
> 
> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
>  fs/exec.c                  |    1 +
>  include/linux/audit.h      |   23 +++++++++++-
>  include/linux/init_task.h  |    9 ++++
>  include/linux/sched.h      |    5 ++
>  include/uapi/linux/audit.h |    1 +
>  kernel/audit.c             |   90 ++++++++++++++++++++++++++++++++++++++++++++
>  kernel/auditsc.c           |   19 +++++++++
>  7 files changed, 147 insertions(+), 1 deletions(-)
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-09-27  1:02       ` Tetsuo Handa
@ 2014-09-27 13:14         ` Steve Grubb
  2014-09-27 15:13           ` Tetsuo Handa
  0 siblings, 1 reply; 20+ messages in thread
From: Steve Grubb @ 2014-09-27 13:14 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: linux-audit, linux-security-module

On Sat, 27 Sep 2014 10:02:44 +0900
Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:

> May I continue proposing this functionality?

>From the audit perspective, sure. I think we were expecting a revised
patch after the comments. Other groups may have different thoughts,
though.

-Steve

> I want to identify where
> bash is executed from in the RHEL servers in order to assess
> possibility of damage caused by CVE-2014-6271.
> 
> Tetsuo Handa wrote:
> > Thank you for your comment, Steve.
> > 
> > Steve Grubb wrote:
> > > On Monday, June 23, 2014 09:14:35 PM Tetsuo Handa wrote:
> > >> Any comments on this proposal?
> > >
> > > subj= is the wrong way to record this. The subj field name is for
> > > process labels. When field names get re-used for different
> > > purposes, it causes lots of problems in being able to assign
> > > meaning and correctly use it in analysis. I would suggest using
> > > phist= for process history or something like that. Please don't
> > > re-use subj for this.
> > 
> > This was just a sample implementation. If this proposal is
> > acceptable as a patch to auditing subsystem, I'm happy to update
> > not to re-use subj= field and not to occupy LSM. An updated version
> > is attached.
> > 
> > > Also, the comm file is under control of the user. What if they
> > > create a program "sshd=>crond"? Would that throw off the
> > > analysis? How do you ensure user supplied names do not contain
> > > symbols that you are using to denote parentage?
> > 
> > OK. I added '=' in comm name to the list of need-to-escape bytes.
> > 
> > By the way, audit_string_contains_control() treats *p == '"' || *p
> > < 0x21 || *p > 0x7e as need-to-escape bytes. Thus, 0x20 from
> > audit_log_untrustedstring() is a need-to-escape byte. However, I
> > can see that 0x20 from userspace programs is emitted without
> > escaping.
> > 
> >   type=USER_START msg=audit(1403741835.270:16): user pid=1870 uid=0
> > auid=0 ses=1 msg='op=login id=0 exe="/usr/sbin/sshd"
> > hostname=192.168.0.1 addr=192.168.0.1 terminal=/dev/pts/0
> > res=success'
> > 
> > Where can I find which bytes in $value need to be escaped when
> > emitting a record like name='$value' ? Is 0x20 in $value permitted?
> > 
> > > Also, would you consider adding this information as a auxiliary
> > > record rather than as part of a syscall record? The advantage is
> > > it can be filtered or searched for. We recently did this for
> > > PROCTITLE information. Perhaps this fits better as a PROCHIST
> > > auxiliary record?
> > 
> > I changed to use auxiliary record and noticed a difference.
> > The previous version emitted the history for type=USER_LOGIN case
> > 
> >   type=USER_LOGIN msg=audit(1400879947.084:24): pid=4308 uid=0
> > auid=0 ses=2
> > subj="swapper/0(2014/05/23-21:17:30)=>init(2014/05/23-21:17:33)=>
> > switch_root(2014/05/23-21:17:34)=>init(2014/05/23-21:17:34)=>
> > sh(2014/05/23-21:17:56)=>mingetty(2014/05/23-21:17:56)=>
> > login(2014/05/23-21:19:05)" msg='op=login id=0 exe="/bin/login"
> > hostname=? addr=? terminal=tty1 res=success'
> > 
> > but current version does not emit it for type=USER_LOGIN case.
> > Does auxiliary record work with only type=SYSCALL case (and
> > therefore I should use CONFIG_AUDITSYSCALL rather than CONFIG_AUDIT
> > in the patch below) ?
> > 
> > Regards.
> > ----------
> > >From d015533ce544feb8922fcbf023017c82bd79a9ac Mon Sep 17 00:00:00
> > >2001
> > From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> > Date: Thu, 26 Jun 2014 09:39:14 +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)'
> > 
> > Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> > ---
> >  fs/exec.c                  |    1 +
> >  include/linux/audit.h      |   23 +++++++++++-
> >  include/linux/init_task.h  |    9 ++++
> >  include/linux/sched.h      |    5 ++
> >  include/uapi/linux/audit.h |    1 +
> >  kernel/audit.c             |   90
> > ++++++++++++++++++++++++++++++++++++++++++++
> > kernel/auditsc.c           |   19 +++++++++ 7 files changed, 147
> > insertions(+), 1 deletions(-)
> > 


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-09-27 13:14         ` Steve Grubb
@ 2014-09-27 15:13           ` Tetsuo Handa
  2014-09-27 15:40             ` Steve Grubb
  2014-10-07 21:30             ` [PATCH] TaskTracker : Simplified thread information tracker Richard Guy Briggs
  0 siblings, 2 replies; 20+ messages in thread
From: Tetsuo Handa @ 2014-09-27 15:13 UTC (permalink / raw)
  To: sgrubb; +Cc: linux-audit, linux-security-module

Steve Grubb wrote:
> On Sat, 27 Sep 2014 10:02:44 +0900
> Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
> 
> > May I continue proposing this functionality?
> 
> From the audit perspective, sure. I think we were expecting a revised
> patch after the comments. Other groups may have different thoughts,
> though.
> 
> -Steve

OK, thank you. Before posting a revised patch, can I hear answers about
specification questions listed below?

(Q1) Where can I find which bytes in $value need to be escaped when
     emitting a record like name='$value' ? Is 0x20 in $value permitted?

(Q2) Does auxiliary record work with only type=SYSCALL case?

Regards.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-09-27 15:13           ` Tetsuo Handa
@ 2014-09-27 15:40             ` Steve Grubb
  2014-09-28 10:24               ` [PATCH] audit: Emit history of thread's comm name Tetsuo Handa
  2014-10-07 21:30             ` [PATCH] TaskTracker : Simplified thread information tracker Richard Guy Briggs
  1 sibling, 1 reply; 20+ messages in thread
From: Steve Grubb @ 2014-09-27 15:40 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: linux-audit, linux-security-module

Hello,

On Sun, 28 Sep 2014 00:13:14 +0900
Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
> Steve Grubb wrote:
> > On Sat, 27 Sep 2014 10:02:44 +0900
> > Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
> > 
> > > May I continue proposing this functionality?
> > 
> > From the audit perspective, sure. I think we were expecting a
> > revised patch after the comments. Other groups may have different
> > thoughts, though.
> > 
> > -Steve
> 
> OK, thank you. Before posting a revised patch, can I hear answers
> about specification questions listed below?

Sure.

> (Q1) Where can I find which bytes in $value need to be escaped when
>      emitting a record like name='$value' ?

I have written a specification that describes how to write well formed
audit events to help with questions like this. You can find it here:

http://people.redhat.com/sgrubb/audit/audit-events.txt

If you know that a field is under user control, it must be escaped so
that they cannot try to trick the parser.

> Is 0x20 in $value permitted?

No. That is the separator between fields, so it cannot be allowed. What
we suggest is to use a dash or hyphen between if you are logging a
phrase that cannot be altered by the user. For example, you may have an
op field saying it deleted a rule. You would do it as op=rule-deleted.
However, we do not suggest that for user controlled fields. Just escape
it by calling audit_log_untrustedstring() if in the kernel. There are
examples in the page I mention above.

> (Q2) Does auxiliary record work with only type=SYSCALL case?

Auxiliary records compliment a syscall record by adding extra
information. PATH, IPC, CWD, and EXECVE are some examples. They get
emitted in audit_log_exit() if you wanted to look at them in more
detail.

HTH...

-Steve

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH] audit: Emit history of thread's comm name.
  2014-09-27 15:40             ` Steve Grubb
@ 2014-09-28 10:24               ` Tetsuo Handa
  0 siblings, 0 replies; 20+ messages in thread
From: Tetsuo Handa @ 2014-09-28 10:24 UTC (permalink / raw)
  To: sgrubb; +Cc: linux-audit, linux-security-module

Thank you, Steve. Here is a revised patch.
----------
>From 2beb93e870e9c1a6391d8706aa84a608b8353c2a Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@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@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

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-09-27 15:13           ` Tetsuo Handa
  2014-09-27 15:40             ` Steve Grubb
@ 2014-10-07 21:30             ` Richard Guy Briggs
  2014-10-10 12:40               ` Tetsuo Handa
  1 sibling, 1 reply; 20+ messages in thread
From: Richard Guy Briggs @ 2014-10-07 21:30 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: sgrubb, linux-security-module, linux-audit

On 14/09/28, Tetsuo Handa wrote:
> (Q2) Does auxiliary record work with only type=SYSCALL case?

Auxiliary records don't work with AUDIT_LOGIN because that record has a
NULL context.  Similarly for core dumps (AUDIT_ANOM_ABEND), AUDIT_SECCOMP,
configuration changes (AUDIT_CONFIG_CHANGE, AUDIT_FEATURE_CHANGE), most
(all?) AUDIT_USER_* messages.

> Regards.

- RGB

--
Richard Guy Briggs <rbriggs@redhat.com>
Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat
Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-10-07 21:30             ` [PATCH] TaskTracker : Simplified thread information tracker Richard Guy Briggs
@ 2014-10-10 12:40               ` Tetsuo Handa
  2014-10-10 12:49                 ` Richard Guy Briggs
  0 siblings, 1 reply; 20+ messages in thread
From: Tetsuo Handa @ 2014-10-10 12:40 UTC (permalink / raw)
  To: rgb, sgrubb; +Cc: linux-security-module, linux-audit, linux-kernel

Steve Grubb wrote:
> Hello,
> 
> On Sun, 28 Sep 2014 00:13:14 +0900
> Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
> > Steve Grubb wrote:
> > > On Sat, 27 Sep 2014 10:02:44 +0900
> > > Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
> > > 
> > > > May I continue proposing this functionality?
> > > 
> > > From the audit perspective, sure. I think we were expecting a
> > > revised patch after the comments. Other groups may have different
> > > thoughts, though.
> > > 
> > > -Steve
> > 
> > OK, thank you. Before posting a revised patch, can I hear answers
> > about specification questions listed below?
> 
> Sure.
> 
> > (Q1) Where can I find which bytes in $value need to be escaped when
> >      emitting a record like name='$value' ?
> 
> I have written a specification that describes how to write well formed
> audit events to help with questions like this. You can find it here:
> 
> http://people.redhat.com/sgrubb/audit/audit-events.txt
> 
> If you know that a field is under user control, it must be escaped so
> that they cannot try to trick the parser.
> 
> > Is 0x20 in $value permitted?
> 
> No. That is the separator between fields, so it cannot be allowed. What
> we suggest is to use a dash or hyphen between if you are logging a
> phrase that cannot be altered by the user. For example, you may have an
> op field saying it deleted a rule. You would do it as op=rule-deleted.
> However, we do not suggest that for user controlled fields. Just escape
> it by calling audit_log_untrustedstring() if in the kernel. There are
> examples in the page I mention above.
> 
> > (Q2) Does auxiliary record work with only type=SYSCALL case?
> 
> Auxiliary records compliment a syscall record by adding extra
> information. PATH, IPC, CWD, and EXECVE are some examples. They get
> emitted in audit_log_exit() if you wanted to look at them in more
> detail.
> 
> HTH...

Richard Guy Briggs wrote:
> On 14/09/28, Tetsuo Handa wrote:
> > (Q2) Does auxiliary record work with only type=SYSCALL case?
> 
> Auxiliary records don't work with AUDIT_LOGIN because that record has a
> NULL context.  Similarly for core dumps (AUDIT_ANOM_ABEND), AUDIT_SECCOMP,
> configuration changes (AUDIT_CONFIG_CHANGE, AUDIT_FEATURE_CHANGE), most
> (all?) AUDIT_USER_* messages.
> 
I see, thank you.

Although I feel that, from the point of view of troubleshooting, emitting
history of thread's comm name into NULL-context records would help sysadmin
to map login session and operations a user did from that login session,
I'm OK with starting history of thread's comm name as auxiliary records
(i.e. not emitted into NULL-context records).

Adding LKML for reviewers. What else can I do for merging this patch?
----------
>From 2beb93e870e9c1a6391d8706aa84a608b8353c2a Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@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@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

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-10-10 12:40               ` Tetsuo Handa
@ 2014-10-10 12:49                 ` Richard Guy Briggs
  2015-01-04 11:50                   ` Tetsuo Handa
  0 siblings, 1 reply; 20+ messages in thread
From: Richard Guy Briggs @ 2014-10-10 12:49 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: sgrubb, linux-security-module, linux-audit, linux-kernel

On 14/10/10, Tetsuo Handa wrote:
> Steve Grubb wrote:
> > Hello,
> > 
> > On Sun, 28 Sep 2014 00:13:14 +0900
> > Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
> > > Steve Grubb wrote:
> > > > On Sat, 27 Sep 2014 10:02:44 +0900
> > > > Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> wrote:
> > > > 
> > > > > May I continue proposing this functionality?
> > > > 
> > > > From the audit perspective, sure. I think we were expecting a
> > > > revised patch after the comments. Other groups may have different
> > > > thoughts, though.
> > > > 
> > > > -Steve
> > > 
> > > OK, thank you. Before posting a revised patch, can I hear answers
> > > about specification questions listed below?
> > 
> > Sure.
> > 
> > > (Q1) Where can I find which bytes in $value need to be escaped when
> > >      emitting a record like name='$value' ?
> > 
> > I have written a specification that describes how to write well formed
> > audit events to help with questions like this. You can find it here:
> > 
> > http://people.redhat.com/sgrubb/audit/audit-events.txt
> > 
> > If you know that a field is under user control, it must be escaped so
> > that they cannot try to trick the parser.
> > 
> > > Is 0x20 in $value permitted?
> > 
> > No. That is the separator between fields, so it cannot be allowed. What
> > we suggest is to use a dash or hyphen between if you are logging a
> > phrase that cannot be altered by the user. For example, you may have an
> > op field saying it deleted a rule. You would do it as op=rule-deleted.
> > However, we do not suggest that for user controlled fields. Just escape
> > it by calling audit_log_untrustedstring() if in the kernel. There are
> > examples in the page I mention above.
> > 
> > > (Q2) Does auxiliary record work with only type=SYSCALL case?
> > 
> > Auxiliary records compliment a syscall record by adding extra
> > information. PATH, IPC, CWD, and EXECVE are some examples. They get
> > emitted in audit_log_exit() if you wanted to look at them in more
> > detail.
> > 
> > HTH...
> 
> Richard Guy Briggs wrote:
> > On 14/09/28, Tetsuo Handa wrote:
> > > (Q2) Does auxiliary record work with only type=SYSCALL case?
> > 
> > Auxiliary records don't work with AUDIT_LOGIN because that record has a
> > NULL context.  Similarly for core dumps (AUDIT_ANOM_ABEND), AUDIT_SECCOMP,
> > configuration changes (AUDIT_CONFIG_CHANGE, AUDIT_FEATURE_CHANGE), most
> > (all?) AUDIT_USER_* messages.
> > 
> I see, thank you.
> 
> Although I feel that, from the point of view of troubleshooting, emitting
> history of thread's comm name into NULL-context records would help sysadmin
> to map login session and operations a user did from that login session,
> I'm OK with starting history of thread's comm name as auxiliary records
> (i.e. not emitted into NULL-context records).
> 
> Adding LKML for reviewers. What else can I do for merging this patch?

I'm willing to take it with some reflection and no significant
objections, in particular from userspace audit.  I'll have a closer look
at it.

> ----------
> >From 2beb93e870e9c1a6391d8706aa84a608b8353c2a Mon Sep 17 00:00:00 2001
> From: Tetsuo Handa <penguin-kernel@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@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

- RGB

--
Richard Guy Briggs <rbriggs@redhat.com>
Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat
Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2014-10-10 12:49                 ` Richard Guy Briggs
@ 2015-01-04 11:50                   ` Tetsuo Handa
  2015-01-05 18:07                     ` Richard Guy Briggs
  2015-01-19 15:25                     ` Richard Guy Briggs
  0 siblings, 2 replies; 20+ messages in thread
From: Tetsuo Handa @ 2015-01-04 11:50 UTC (permalink / raw)
  To: rgb; +Cc: sgrubb, linux-security-module, linux-audit, linux-kernel

Hello.

Richard Guy Briggs wrote:
> > Richard Guy Briggs wrote:
> > > On 14/09/28, Tetsuo Handa wrote:
> > > > (Q2) Does auxiliary record work with only type=SYSCALL case?
> > > 
> > > Auxiliary records don't work with AUDIT_LOGIN because that record has a
> > > NULL context.  Similarly for core dumps (AUDIT_ANOM_ABEND), AUDIT_SECCOMP,
> > > configuration changes (AUDIT_CONFIG_CHANGE, AUDIT_FEATURE_CHANGE), most
> > > (all?) AUDIT_USER_* messages.
> > > 
> > I see, thank you.
> > 
> > Although I feel that, from the point of view of troubleshooting, emitting
> > history of thread's comm name into NULL-context records would help sysadmin
> > to map login session and operations a user did from that login session,
> > I'm OK with starting history of thread's comm name as auxiliary records
> > (i.e. not emitted into NULL-context records).
> > 
> > Adding LKML for reviewers. What else can I do for merging this patch?
> 
> I'm willing to take it with some reflection and no significant
> objections, in particular from userspace audit.  I'll have a closer look
> at it.
> 
Any comments on this patch?

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2015-01-04 11:50                   ` Tetsuo Handa
@ 2015-01-05 18:07                     ` Richard Guy Briggs
  2015-01-12  6:13                       ` Tetsuo Handa
  2015-01-19 15:25                     ` Richard Guy Briggs
  1 sibling, 1 reply; 20+ messages in thread
From: Richard Guy Briggs @ 2015-01-05 18:07 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel, linux-audit

On 15/01/04, Tetsuo Handa wrote:
> Hello.
> 
> Richard Guy Briggs wrote:
> > > Richard Guy Briggs wrote:
> > > > On 14/09/28, Tetsuo Handa wrote:
> > > > > (Q2) Does auxiliary record work with only type=SYSCALL case?
> > > > 
> > > > Auxiliary records don't work with AUDIT_LOGIN because that record has a
> > > > NULL context.  Similarly for core dumps (AUDIT_ANOM_ABEND), AUDIT_SECCOMP,
> > > > configuration changes (AUDIT_CONFIG_CHANGE, AUDIT_FEATURE_CHANGE), most
> > > > (all?) AUDIT_USER_* messages.
> > > > 
> > > I see, thank you.
> > > 
> > > Although I feel that, from the point of view of troubleshooting, emitting
> > > history of thread's comm name into NULL-context records would help sysadmin
> > > to map login session and operations a user did from that login session,
> > > I'm OK with starting history of thread's comm name as auxiliary records
> > > (i.e. not emitted into NULL-context records).
> > > 
> > > Adding LKML for reviewers. What else can I do for merging this patch?
> > 
> > I'm willing to take it with some reflection and no significant
> > objections, in particular from userspace audit.  I'll have a closer look
> > at it.
> 
> Any comments on this patch?

Steve already mentioned any user-influenced fields need to be escaped,
so I'd recommend audit_log_untrustedstring() as being much simpler from
your perspective and much better tested and understood from audit
maintainer's perspective.  At least use the existing 'o' printf format
specifier instead of inventing your own.  I do acknowledge that the
resulting output from your function is easier to read in its raw format
passed from the kernel, however, it makes your code harder to maintain.

As for the date-stamping bits, they seem to be the majority of the code
in audit_update_history().  I'd just emit a number and punt that to
userspace for decoding.  Alternatively, I'd use an existing service in
the kernel to do that date formatting, or at least call a new function
to format that date string should a suitable one not already exist, so
you can remove that complexity from audit_update_history().

- RGB

--
Richard Guy Briggs <rbriggs@redhat.com>
Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat
Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2015-01-05 18:07                     ` Richard Guy Briggs
@ 2015-01-12  6:13                       ` Tetsuo Handa
  2015-01-12 15:14                         ` Steve Grubb
  2015-01-12 15:21                         ` Richard Guy Briggs
  0 siblings, 2 replies; 20+ messages in thread
From: Tetsuo Handa @ 2015-01-12  6:13 UTC (permalink / raw)
  To: rgb; +Cc: linux-security-module, linux-kernel, linux-audit

Thank you for comments.

Richard Guy Briggs wrote:
> Steve already mentioned any user-influenced fields need to be escaped,
> so I'd recommend audit_log_untrustedstring() as being much simpler from
> your perspective and much better tested and understood from audit
> maintainer's perspective.  At least use the existing 'o' printf format
> specifier instead of inventing your own.  I do acknowledge that the
> resulting output from your function is easier to read in its raw format
> passed from the kernel, however, it makes your code harder to maintain.

I'm not sure whether I should use audit_log_untrustedstring().

This record contains multiple user-influenced comm names. If I use
audit_log_untrustedstring(), I would need to split this record into
multiple records like history[0]='...' history[1]='...' history[2]='...'
in order to avoid matching delimiters (i.e. ';', '=' and '>') used in
this record. This would also change from "char *" in "struct task_struct"
to array of struct { "comm name", "pid", "stamp" } in "struct task_struct".
I don't know whether such change makes easier to maintain than now.

> As for the date-stamping bits, they seem to be the majority of the code
> in audit_update_history().  I'd just emit a number and punt that to
> userspace for decoding.  Alternatively, I'd use an existing service in
> the kernel to do that date formatting, or at least call a new function
> to format that date string should a suitable one not already exist, so
> you can remove that complexity from audit_update_history().

Since I don't know existing functions for date formatting, I split it as
a new function. If it is acceptable, I'd like to make that function public
and replace tomoyo_convert_time() in security/tomoyo/util.c with that
function.

Regards.
----------------------------------------
>From 50d59b5640a7501b8d5f843fb57283fcb62b1118 Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Mon, 12 Jan 2015 14:45:23 +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, pid and time stamp pairs
as of execve().

  type=UNKNOWN[1329] msg=audit(1421039813.810:3693): history='
  name=swapper\0570;pid=1;start=20150112140720=>name=init;pid=1;
  start=20150112140721=>name=systemd;pid=1;start=20150112140722=>
  name=sshd;pid=2473;start=20150112050733=>name=sshd;pid=9838;
  start=20150112051105=>name=bash;pid=9840;start=20150112051108=>
  name=tail;pid=9876;start=20150112051653'

Since converting all bytes using audit_log_untrustedstring() makes this record
unparsable because this record includes multiple user-influenced comm names
which may match delimiters used in this record (i.e. ';', '=' and '>'), only
bytes which are not alphabets, numbers, '.', '_' nor '-' are escaped.

Signed-off-by: Tetsuo Handa <penguin-kernel@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           | 155 +++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 170 insertions(+)

diff --git a/fs/exec.c b/fs/exec.c
index ad8798e..5e92651 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1200,6 +1200,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 af84234..74310a7 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -221,6 +221,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,
@@ -437,6 +439,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 3037fc0..078823a 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_PREEMPT_RCU
@@ -247,6 +251,7 @@ extern struct task_group root_task_group;
 	INIT_RT_MUTEXES(tsk)						\
 	INIT_VTIME(tsk)							\
 	INIT_NUMA_BALANCING(tsk)					\
+	INIT_THREAD_HISTORY						\
 }
 
 
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 8db31ef..77539e4 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1701,6 +1701,9 @@ struct task_struct {
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 	unsigned long	task_state_change;
 #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 d3475e1..93ad58c 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 72ab759..d45397e 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1154,6 +1154,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 072566d..2edeba2 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -88,6 +88,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;
 
@@ -945,6 +948,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. */
 
@@ -955,6 +963,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;
@@ -1344,6 +1354,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;
@@ -1462,6 +1483,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);
@@ -1481,6 +1503,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;
@@ -2535,3 +2559,134 @@ struct list_head *audit_killed_trees(void)
 		return NULL;
 	return &ctx->killed_trees;
 }
+
+char init_task_history[COMM_HISTORY_SIZE];
+
+/* Structure for representing YYYY/MM/DD hh/mm/ss. */
+struct yyyymmdd_hhmmss {
+	u16 year;
+	u8 month;
+	u8 day;
+	u8 hour;
+	u8 min;
+	u8 sec;
+};
+
+/**
+ * tt_get_time - Get current time in YYYY/MM/DD hh/mm/ss format.
+ *
+ * @stamp: Pointer to "struct yyyymmdd_hhmmss".
+ *
+ * Returns nothing.
+ *
+ * This function does not handle Y2038 problem.
+ */
+static void tt_get_time(struct yyyymmdd_hhmmss *stamp)
+{
+	static const u16 days_until_end_of_month[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 y = 1970;
+	u8 m;
+	bool r;
+	time_t time = get_seconds();
+
+	stamp->sec = time % 60;
+	time /= 60;
+	stamp->min = time % 60;
+	time /= 60;
+	stamp->hour = time % 24;
+	time /= 24;
+	if (time >= 16436) {
+		/* Start from 2015/01/01 rather than 1970/01/01. */
+		time -= 16436;
+		y += 45;
+	}
+	while (1) {
+		const unsigned short days = (y & 3) ? 365 : 366;
+
+		if (time < days)
+			break;
+		time -= days;
+		y++;
+	}
+	r = (y & 3) == 0;
+	for (m = 0; m < 11 && time >= days_until_end_of_month[r][m]; m++)
+		;
+	if (m)
+		time -= days_until_end_of_month[r][m - 1];
+	stamp->year = y;
+	stamp->month = ++m;
+	stamp->day = ++time;
+}
+
+/**
+ * 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)
+{
+	char *cp;
+	int i;
+	int required;
+	char buf[256];
+
+	/*
+	 * Lockless read because this is current thread and being unexpectedly
+	 * modified by other thread is not a fatal problem.
+	 */
+	cp = buf;
+	cp += snprintf(buf, sizeof(buf) - 1, "name=");
+	for (i = 0; i < TASK_COMM_LEN; i++) {
+		const unsigned char c = current->comm[i];
+
+		if (!c)
+			break;
+		if (isalnum(c) || c == '.' || c == '_' || c == '-') {
+			*cp++ = c;
+			continue;
+		}
+		*cp++ = '\\';
+		*cp++ = (c >> 6) + '0';
+		*cp++ = ((c >> 3) & 7) + '0';
+		*cp++ = (c & 7) + '0';
+	}
+	/* Append PID. */
+	cp += snprintf(cp, buf - cp + sizeof(buf) - 1, ";pid=%u",
+		       current->pid);
+	/* Append timestamp. */
+	{
+		struct yyyymmdd_hhmmss stamp;
+
+		tt_get_time(&stamp);
+		cp += snprintf(cp, buf - cp + sizeof(buf) - 1,
+			       ";start=%04u%02u%02u%02u%02u%02u", stamp.year,
+			       stamp.month, stamp.day, stamp.hour, stamp.min,
+			       stamp.sec);
+	}
+	/* Terminate the buffer. */
+	if (cp >= buf + sizeof(buf))
+		cp = buf + sizeof(buf) - 1;
+	*cp = '\0';
+	required = cp - buf;
+	/* Make some room by truncating old history. */
+	cp = current->comm_history;
+	while (i = strlen(cp), i + required >= COMM_HISTORY_SIZE - 10) {
+		char *cp2 = memchr(cp + 2, '>', i - 2);
+
+		BUG_ON(!cp2);
+		cp2--;
+		memmove(cp, cp2, strlen(cp2) + 1);
+	}
+	/* Emit the buffer. */
+	if (!i)
+		sprintf(cp, "%s", buf);
+	else
+		sprintf(cp + i, "=>%s", buf);
+}
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2015-01-12  6:13                       ` Tetsuo Handa
@ 2015-01-12 15:14                         ` Steve Grubb
  2015-01-12 20:51                           ` Paul Moore
  2015-01-20 13:20                           ` Tetsuo Handa
  2015-01-12 15:21                         ` Richard Guy Briggs
  1 sibling, 2 replies; 20+ messages in thread
From: Steve Grubb @ 2015-01-12 15:14 UTC (permalink / raw)
  To: linux-audit; +Cc: Tetsuo Handa, rgb, linux-security-module, linux-kernel

On Monday, January 12, 2015 03:13:12 PM Tetsuo Handa wrote:
> Thank you for comments.
> 
> Richard Guy Briggs wrote:
> > Steve already mentioned any user-influenced fields need to be escaped,
> > so I'd recommend audit_log_untrustedstring() as being much simpler from
> > your perspective and much better tested and understood from audit
> > maintainer's perspective.  At least use the existing 'o' printf format
> > specifier instead of inventing your own.  I do acknowledge that the
> > resulting output from your function is easier to read in its raw format
> > passed from the kernel, however, it makes your code harder to maintain.
> 
> I'm not sure whether I should use audit_log_untrustedstring().

That is the accepted encoding. We do not want 2 different kinds of encoding 
functions.

> This record contains multiple user-influenced comm names. If I use
> audit_log_untrustedstring(), I would need to split this record into
> multiple records like history[0]='...' history[1]='...' history[2]='...'
> in order to avoid matching delimiters (i.e. ';', '=' and '>') used in
> this record. 

That sounds like a good change to me. Audit records are always name=value with 
a space between fields. We need this to always stay like this because the 
tooling expects that format. There is nowhere in the audit logs we use =>.


> This would also change from "char *" in "struct task_struct"
> to array of struct { "comm name", "pid", "stamp" } in "struct task_struct".
> I don't know whether such change makes easier to maintain than now.

You can still use char *, just use history[x]= to append with. We faced the 
same issue regarding argv[] logging. You might look at the execve record 
generation to get some ideas how that was handled.


> > As for the date-stamping bits, they seem to be the majority of the code
> > in audit_update_history().  I'd just emit a number and punt that to
> > userspace for decoding.  Alternatively, I'd use an existing service in
> > the kernel to do that date formatting, or at least call a new function
> > to format that date string should a suitable one not already exist, so
> > you can remove that complexity from audit_update_history().
> 
> Since I don't know existing functions for date formatting,

All time in the audit system is "%lu.%03lu", t.tv_sec, t.tv_nsec/1000000. User 
space tooling expects this.

-Steve


> I split it as a new function. If it is acceptable, I'd like to make that
> function public and replace tomoyo_convert_time() in security/tomoyo/util.c
> with that function.
> 
> Regards.
> ----------------------------------------
> 
> >From 50d59b5640a7501b8d5f843fb57283fcb62b1118 Mon Sep 17 00:00:00 2001
> 
> From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
> Date: Mon, 12 Jan 2015 14:45:23 +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, pid and time stamp pairs
> as of execve().
> 
>   type=UNKNOWN[1329] msg=audit(1421039813.810:3693): history='
>   name=swapper\0570;pid=1;start=20150112140720=>name=init;pid=1;
>   start=20150112140721=>name=systemd;pid=1;start=20150112140722=>
>   name=sshd;pid=2473;start=20150112050733=>name=sshd;pid=9838;
>   start=20150112051105=>name=bash;pid=9840;start=20150112051108=>
>   name=tail;pid=9876;start=20150112051653'
> 
> Since converting all bytes using audit_log_untrustedstring() makes this
> record unparsable because this record includes multiple user-influenced
> comm names which may match delimiters used in this record (i.e. ';', '='
> and '>'), only bytes which are not alphabets, numbers, '.', '_' nor '-' are
> escaped.
> 
> Signed-off-by: Tetsuo Handa <penguin-kernel@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           | 155
> +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 170
> insertions(+)
> 
> diff --git a/fs/exec.c b/fs/exec.c
> index ad8798e..5e92651 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -1200,6 +1200,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 af84234..74310a7 100644
> --- a/include/linux/audit.h
> +++ b/include/linux/audit.h
> @@ -221,6 +221,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,
> @@ -437,6 +439,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 3037fc0..078823a 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_PREEMPT_RCU
> @@ -247,6 +251,7 @@ extern struct task_group root_task_group;
>  	INIT_RT_MUTEXES(tsk)						\
>  	INIT_VTIME(tsk)							\
>  	INIT_NUMA_BALANCING(tsk)					\
> +	INIT_THREAD_HISTORY						\
>  }
> 
> 
> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index 8db31ef..77539e4 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -1701,6 +1701,9 @@ struct task_struct {
>  #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
>  	unsigned long	task_state_change;
>  #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 d3475e1..93ad58c 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 72ab759..d45397e 100644
> --- a/kernel/audit.c
> +++ b/kernel/audit.c
> @@ -1154,6 +1154,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 072566d..2edeba2 100644
> --- a/kernel/auditsc.c
> +++ b/kernel/auditsc.c
> @@ -88,6 +88,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;
> 
> @@ -945,6 +948,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. */
> 
> @@ -955,6 +963,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;
> @@ -1344,6 +1354,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;
> @@ -1462,6 +1483,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);
> @@ -1481,6 +1503,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;
> @@ -2535,3 +2559,134 @@ struct list_head *audit_killed_trees(void)
>  		return NULL;
>  	return &ctx->killed_trees;
>  }
> +
> +char init_task_history[COMM_HISTORY_SIZE];
> +
> +/* Structure for representing YYYY/MM/DD hh/mm/ss. */
> +struct yyyymmdd_hhmmss {
> +	u16 year;
> +	u8 month;
> +	u8 day;
> +	u8 hour;
> +	u8 min;
> +	u8 sec;
> +};
> +
> +/**
> + * tt_get_time - Get current time in YYYY/MM/DD hh/mm/ss format.
> + *
> + * @stamp: Pointer to "struct yyyymmdd_hhmmss".
> + *
> + * Returns nothing.
> + *
> + * This function does not handle Y2038 problem.
> + */
> +static void tt_get_time(struct yyyymmdd_hhmmss *stamp)
> +{
> +	static const u16 days_until_end_of_month[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 y = 1970;
> +	u8 m;
> +	bool r;
> +	time_t time = get_seconds();
> +
> +	stamp->sec = time % 60;
> +	time /= 60;
> +	stamp->min = time % 60;
> +	time /= 60;
> +	stamp->hour = time % 24;
> +	time /= 24;
> +	if (time >= 16436) {
> +		/* Start from 2015/01/01 rather than 1970/01/01. */
> +		time -= 16436;
> +		y += 45;
> +	}
> +	while (1) {
> +		const unsigned short days = (y & 3) ? 365 : 366;
> +
> +		if (time < days)
> +			break;
> +		time -= days;
> +		y++;
> +	}
> +	r = (y & 3) == 0;
> +	for (m = 0; m < 11 && time >= days_until_end_of_month[r][m]; m++)
> +		;
> +	if (m)
> +		time -= days_until_end_of_month[r][m - 1];
> +	stamp->year = y;
> +	stamp->month = ++m;
> +	stamp->day = ++time;
> +}
> +
> +/**
> + * 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)
> +{
> +	char *cp;
> +	int i;
> +	int required;
> +	char buf[256];
> +
> +	/*
> +	 * Lockless read because this is current thread and being unexpectedly
> +	 * modified by other thread is not a fatal problem.
> +	 */
> +	cp = buf;
> +	cp += snprintf(buf, sizeof(buf) - 1, "name=");
> +	for (i = 0; i < TASK_COMM_LEN; i++) {
> +		const unsigned char c = current->comm[i];
> +
> +		if (!c)
> +			break;
> +		if (isalnum(c) || c == '.' || c == '_' || c == '-') {
> +			*cp++ = c;
> +			continue;
> +		}
> +		*cp++ = '\\';
> +		*cp++ = (c >> 6) + '0';
> +		*cp++ = ((c >> 3) & 7) + '0';
> +		*cp++ = (c & 7) + '0';
> +	}
> +	/* Append PID. */
> +	cp += snprintf(cp, buf - cp + sizeof(buf) - 1, ";pid=%u",
> +		       current->pid);
> +	/* Append timestamp. */
> +	{
> +		struct yyyymmdd_hhmmss stamp;
> +
> +		tt_get_time(&stamp);
> +		cp += snprintf(cp, buf - cp + sizeof(buf) - 1,
> +			       ";start=%04u%02u%02u%02u%02u%02u", stamp.year,
> +			       stamp.month, stamp.day, stamp.hour, stamp.min,
> +			       stamp.sec);
> +	}
> +	/* Terminate the buffer. */
> +	if (cp >= buf + sizeof(buf))
> +		cp = buf + sizeof(buf) - 1;
> +	*cp = '\0';
> +	required = cp - buf;
> +	/* Make some room by truncating old history. */
> +	cp = current->comm_history;
> +	while (i = strlen(cp), i + required >= COMM_HISTORY_SIZE - 10) {
> +		char *cp2 = memchr(cp + 2, '>', i - 2);
> +
> +		BUG_ON(!cp2);
> +		cp2--;
> +		memmove(cp, cp2, strlen(cp2) + 1);
> +	}
> +	/* Emit the buffer. */
> +	if (!i)
> +		sprintf(cp, "%s", buf);
> +	else
> +		sprintf(cp + i, "=>%s", buf);
> +}


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2015-01-12  6:13                       ` Tetsuo Handa
  2015-01-12 15:14                         ` Steve Grubb
@ 2015-01-12 15:21                         ` Richard Guy Briggs
  1 sibling, 0 replies; 20+ messages in thread
From: Richard Guy Briggs @ 2015-01-12 15:21 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel, linux-audit

On 15/01/12, Tetsuo Handa wrote:
> Thank you for comments.
> 
> Richard Guy Briggs wrote:
> > Steve already mentioned any user-influenced fields need to be escaped,
> > so I'd recommend audit_log_untrustedstring() as being much simpler from
> > your perspective and much better tested and understood from audit
> > maintainer's perspective.  At least use the existing 'o' printf format
> > specifier instead of inventing your own.  I do acknowledge that the
> > resulting output from your function is easier to read in its raw format
> > passed from the kernel, however, it makes your code harder to maintain.
> 
> I'm not sure whether I should use audit_log_untrustedstring().
> 
> This record contains multiple user-influenced comm names. If I use
> audit_log_untrustedstring(), I would need to split this record into
> multiple records like history[0]='...' history[1]='...' history[2]='...'
> in order to avoid matching delimiters (i.e. ';', '=' and '>') used in
> this record. This would also change from "char *" in "struct task_struct"
> to array of struct { "comm name", "pid", "stamp" } in "struct task_struct".
> I don't know whether such change makes easier to maintain than now.

This will end up producing a varying number of fields.  The userspace
tools are looking for a constant number of fields per record type.

> > As for the date-stamping bits, they seem to be the majority of the code
> > in audit_update_history().  I'd just emit a number and punt that to
> > userspace for decoding.  Alternatively, I'd use an existing service in
> > the kernel to do that date formatting, or at least call a new function
> > to format that date string should a suitable one not already exist, so
> > you can remove that complexity from audit_update_history().
> 
> Since I don't know existing functions for date formatting, I split it as
> a new function. If it is acceptable, I'd like to make that function public
> and replace tomoyo_convert_time() in security/tomoyo/util.c with that
> function.

That is an improvement, but would still like to see existing functions
used or punt to userspace.

> diff --git a/kernel/auditsc.c b/kernel/auditsc.c
> index 072566d..2edeba2 100644
> --- a/kernel/auditsc.c
> +++ b/kernel/auditsc.c
> @@ -1344,6 +1354,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);

This is where I would seperate them to:

	audit_log_format(ab, "history=");
	audit_log_untrustedstring(ab, current->comm_history);

Making sure, of course, that there are no NULLs printed from any of the
comm fields (which should be impossible due to your "if (!c) break;").

> +	audit_log_end(ab);
> +}
> +
>  static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
>  {
...
> +void audit_update_history(void)
> +		*cp++ = '\\';
> +		*cp++ = (c >> 6) + '0';
> +		*cp++ = ((c >> 3) & 7) + '0';
> +		*cp++ = (c & 7) + '0';

Is there a reason you are not using the printf 'o' octal converter?

> +	/* Append timestamp. */
> +	{
> +		struct yyyymmdd_hhmmss stamp;
> +
> +		tt_get_time(&stamp);
> +		cp += snprintf(cp, buf - cp + sizeof(buf) - 1,
> +			       ";start=%04u%02u%02u%02u%02u%02u", stamp.year,
> +			       stamp.month, stamp.day, stamp.hour, stamp.min,
> +			       stamp.sec);
> +	}

Why not just return a string?  Better yet, punt to userspace.

- RGB

--
Richard Guy Briggs <rbriggs@redhat.com>
Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat
Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2015-01-12 15:14                         ` Steve Grubb
@ 2015-01-12 20:51                           ` Paul Moore
  2015-01-20 13:20                           ` Tetsuo Handa
  1 sibling, 0 replies; 20+ messages in thread
From: Paul Moore @ 2015-01-12 20:51 UTC (permalink / raw)
  To: Steve Grubb, linux-audit, Tetsuo Handa, rgb
  Cc: linux-security-module, linux-kernel

On Monday, January 12, 2015 10:14:34 AM Steve Grubb wrote:
> On Monday, January 12, 2015 03:13:12 PM Tetsuo Handa wrote:
> > This record contains multiple user-influenced comm names. If I use
> > audit_log_untrustedstring(), I would need to split this record into
> > multiple records like history[0]='...' history[1]='...' history[2]='...'
> > in order to avoid matching delimiters (i.e. ';', '=' and '>') used in
> > this record.
> 
> That sounds like a good change to me. Audit records are always name=value
> with a space between fields. We need this to always stay like this because
> the tooling expects that format. There is nowhere in the audit logs we use
> =>.

As a FYI, I'm putting a hold on any new audit messages for the time being so 
we can evaluate the current kernel audit API to determine the possibility of 
transitioning to a less ugly API.  I'll leave the door open for messages 
needed to fix bugs, but that's it.  If we do end up providing a new API and 
record format, I want to limit the number of message types in the existing 
API.

My apologies, but this is long overdue and the longer we wait the more 
difficult it will become.

-- 
paul moore
www.paul-moore.com


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2015-01-04 11:50                   ` Tetsuo Handa
  2015-01-05 18:07                     ` Richard Guy Briggs
@ 2015-01-19 15:25                     ` Richard Guy Briggs
  1 sibling, 0 replies; 20+ messages in thread
From: Richard Guy Briggs @ 2015-01-19 15:25 UTC (permalink / raw)
  To: Tetsuo Handa; +Cc: linux-security-module, linux-kernel, linux-audit

On 15/01/04, Tetsuo Handa wrote:
> Hello.
> 
> Richard Guy Briggs wrote:
> > > Richard Guy Briggs wrote:
> > > > On 14/09/28, Tetsuo Handa wrote:
> > > > > (Q2) Does auxiliary record work with only type=SYSCALL case?
> > > > 
> > > > Auxiliary records don't work with AUDIT_LOGIN because that record has a
> > > > NULL context.  Similarly for core dumps (AUDIT_ANOM_ABEND), AUDIT_SECCOMP,
> > > > configuration changes (AUDIT_CONFIG_CHANGE, AUDIT_FEATURE_CHANGE), most
> > > > (all?) AUDIT_USER_* messages.
> > > > 
> > > I see, thank you.
> > > 
> > > Although I feel that, from the point of view of troubleshooting, emitting
> > > history of thread's comm name into NULL-context records would help sysadmin
> > > to map login session and operations a user did from that login session,
> > > I'm OK with starting history of thread's comm name as auxiliary records
> > > (i.e. not emitted into NULL-context records).
> > > 
> > > Adding LKML for reviewers. What else can I do for merging this patch?
> > 
> > I'm willing to take it with some reflection and no significant
> > objections, in particular from userspace audit.  I'll have a closer look
> > at it.
> 
> Any comments on this patch?

It also occurs to me that the pid_t of that process in init PID
namespace would be useful for each entry.

- RGB

--
Richard Guy Briggs <rbriggs@redhat.com>
Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat
Remote, Ottawa, Canada
Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] TaskTracker : Simplified thread information tracker.
  2015-01-12 15:14                         ` Steve Grubb
  2015-01-12 20:51                           ` Paul Moore
@ 2015-01-20 13:20                           ` Tetsuo Handa
  1 sibling, 0 replies; 20+ messages in thread
From: Tetsuo Handa @ 2015-01-20 13:20 UTC (permalink / raw)
  To: sgrubb, rgb, linux-audit; +Cc: linux-security-module, linux-kernel

Richard Guy Briggs wrote:
> It also occurs to me that the pid_t of that process in init PID
> namespace would be useful for each entry.

Me too. I added it.

Steve Grubb wrote:
> > > As for the date-stamping bits, they seem to be the majority of the code
> > > in audit_update_history().  I'd just emit a number and punt that to
> > > userspace for decoding.  Alternatively, I'd use an existing service in
> > > the kernel to do that date formatting, or at least call a new function
> > > to format that date string should a suitable one not already exist, so
> > > you can remove that complexity from audit_update_history().
> > 
> > Since I don't know existing functions for date formatting,
> 
> All time in the audit system is "%lu.%03lu", t.tv_sec, t.tv_nsec/1000000. User 
> space tooling expects this.

What about printing uptime than current system clock for this record? Since
system clock changes during boot upon applying timezone setting, time stamp
within this record goes backwards. On the other hand, uptime does not go
backwards and can serve for knowing when the past execve() had been called.

Steve Grubb wrote:
> > This record contains multiple user-influenced comm names. If I use
> > audit_log_untrustedstring(), I would need to split this record into
> > multiple records like history[0]='...' history[1]='...' history[2]='...'
> > in order to avoid matching delimiters (i.e. ';', '=' and '>') used in
> > this record. 
> 
> That sounds like a good change to me. Audit records are always name=value with 
> a space between fields. We need this to always stay like this because the 
> tooling expects that format. There is nowhere in the audit logs we use =>.

Steve Grubb wrote:
> > This would also change from "char *" in "struct task_struct"
> > to array of struct { "comm name", "pid", "stamp" } in "struct task_struct".
> > I don't know whether such change makes easier to maintain than now.
> 
> You can still use char *, just use history[x]= to append with. We faced the 
> same issue regarding argv[] logging. You might look at the execve record 
> generation to get some ideas how that was handled.

Excuse me, but

Richard Guy Briggs wrote:
> This will end up producing a varying number of fields.  The userspace
> tools are looking for a constant number of fields per record type.

what does Richard's comment mean? We are using

   argc=$argc a0=$argv[0] a1=$argv[1] a2=$argv[2] ...

format for type=EXECVE record. Are the userspace tools really looking for
a constant number of fields per record type?

If the userspace tools are not looking for a constant number of fields
per record type, does Steve's comment suggest something like

  history_name[0]="swapper/0" history_pid[0]=1 history_start[0]=1.10
  history_name[1]="init" history_pid[1]=1 history_start[1]=1.50
  history_name[2]="systemd" history_pid[2]=1 history_start[2]=2.68
  history_name[3]="sshd" history_pid[3]=1210 history_start[3]=29.28
  history_name[4]="sshd" history_pid[4]=9185 history_start[4]=1609.00
  history_name[5]="bash" history_pid[5]=9187 history_start[5]=1611.63
  history_name[6]="tail" history_pid[6]=9230 history_start[6]=1616.51

than

  history="name=swapper/0;pid=1;start=1.10=>name=init;pid=1;start=1.50=>
  name=systemd;pid=1;start=2.68=>name=sshd;pid=1210;start=29.28=>name=sshd;
  pid=9185;start=1609.00=>name=bash;pid=9187;start=1611.63=>name=tail;
  pid=9230;start=1616.51"

for this record? This would indeed avoid => in the $value field, but
does this space-consuming format better than using => ?

Steve Grubb wrote:
> On Monday, January 12, 2015 03:13:12 PM Tetsuo Handa wrote:
> > Thank you for comments.
> > 
> > Richard Guy Briggs wrote:
> > > Steve already mentioned any user-influenced fields need to be escaped,
> > > so I'd recommend audit_log_untrustedstring() as being much simpler from
> > > your perspective and much better tested and understood from audit
> > > maintainer's perspective.  At least use the existing 'o' printf format
> > > specifier instead of inventing your own.  I do acknowledge that the
> > > resulting output from your function is easier to read in its raw format
> > > passed from the kernel, however, it makes your code harder to maintain.
> > 
> > I'm not sure whether I should use audit_log_untrustedstring().
> 
> That is the accepted encoding. We do not want 2 different kinds of encoding 
> functions.

Richard Guy Briggs wrote:
> This is where I would seperate them to:
> 
> 	audit_log_format(ab, "history=");
> 	audit_log_untrustedstring(ab, current->comm_history);

OK. I changed to use audit_log_untrustedstring(), though
audit_string_contains_control() is always false for this record because
I already escaped user-influenced comm name in order to keep the $value
field parsable.

Patch follows.
------------------------------------------------------------
>From 637299dfae899270f7b4b2fbae35af680d19704d Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Tue, 20 Jan 2015 13:13:39 +0900
Subject: [PATCH v2] 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, pid and uptime pairs as of
execve().

  type=UNKNOWN[1329] msg=audit(1421727157.228:5757): history="name=swapper/0;
  pid=1;start=1.10=>name=init;pid=1;start=1.50=>name=systemd;pid=1;start=2.68=>
  name=sshd;pid=1210;start=29.28=>name=sshd;pid=9185;start=1609.00=>name=bash;
  pid=9187;start=1611.63=>name=tail;pid=9230;start=1616.51"

Signed-off-by: Tetsuo Handa <penguin-kernel@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           | 89 ++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 104 insertions(+)

diff --git a/fs/exec.c b/fs/exec.c
index ad8798e..5e92651 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1200,6 +1200,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 af84234..74310a7 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -221,6 +221,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,
@@ -437,6 +439,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 3037fc0..078823a 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_PREEMPT_RCU
@@ -247,6 +251,7 @@ extern struct task_group root_task_group;
 	INIT_RT_MUTEXES(tsk)						\
 	INIT_VTIME(tsk)							\
 	INIT_NUMA_BALANCING(tsk)					\
+	INIT_THREAD_HISTORY						\
 }
 
 
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 8db31ef..77539e4 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1701,6 +1701,9 @@ struct task_struct {
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 	unsigned long	task_state_change;
 #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 d3475e1..93ad58c 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 72ab759..d45397e 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1154,6 +1154,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 072566d..7a3d8f4 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -88,6 +88,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;
 
@@ -945,6 +948,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. */
 
@@ -955,6 +963,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;
@@ -1344,6 +1354,18 @@ 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=");
+	audit_log_untrustedstring(ab, 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;
@@ -1462,6 +1484,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);
@@ -1481,6 +1504,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;
@@ -2535,3 +2560,67 @@ 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)
+{
+	char *cp;
+	int pos;
+	int required;
+	char buf[256];
+	const unsigned long long now = nsec_to_clock_t(ktime_get_boot_ns());
+
+	/*
+	 * Lockless read because this is current thread and being unexpectedly
+	 * modified by other thread is not a fatal problem.
+	 */
+	cp = buf;
+	cp += snprintf(buf, sizeof(buf) - 1, "name=");
+	for (pos = 0; pos < TASK_COMM_LEN; pos++) {
+		const unsigned char c = current->comm[pos];
+
+		if (!c)
+			break;
+		if (isalnum(c) || c == '.' || c == '_' || c == '-' || c == '/')
+			cp += snprintf(cp, buf - cp + sizeof(buf) - 1, "%c",
+				       c);
+		else
+			cp += snprintf(cp, buf - cp + sizeof(buf) - 1,
+				       "\\%03o", c);
+	}
+	/* Append PID of init namespace. */
+	cp += snprintf(cp, buf - cp + sizeof(buf) - 1, ";pid=%u",
+		       current->pid);
+	/* Append uptime. */
+	cp += snprintf(cp, buf - cp + sizeof(buf) - 1, ";start=%llu.%02llu",
+		       now / 100, now % 100);
+	/* Terminate the buffer. */
+	if (cp >= buf + sizeof(buf))
+		cp = buf + sizeof(buf) - 1;
+	*cp = '\0';
+	required = cp - buf;
+	/* Make some room by truncating old history. */
+	cp = current->comm_history;
+	pos = strlen(cp);
+	while (pos + required >= COMM_HISTORY_SIZE - 10) {
+		char *cp2 = memchr(cp, '>', pos) + 1;
+
+		pos -= cp2 - cp;
+		memmove(cp, cp2, pos + 1);
+	}
+	/* Emit the buffer. */
+	if (pos)
+		sprintf(cp + pos, "=>%s", buf);
+	else
+		sprintf(cp, "%s", buf);
+}
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2015-01-20 13:21 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-05-23 12:44 [PATCH] TaskTracker : Simplified thread information tracker Tetsuo Handa
2014-06-23 12:14 ` Tetsuo Handa
2014-06-24 14:00   ` Steve Grubb
2014-06-26 11:40     ` Tetsuo Handa
2014-09-27  1:02       ` Tetsuo Handa
2014-09-27 13:14         ` Steve Grubb
2014-09-27 15:13           ` Tetsuo Handa
2014-09-27 15:40             ` Steve Grubb
2014-09-28 10:24               ` [PATCH] audit: Emit history of thread's comm name Tetsuo Handa
2014-10-07 21:30             ` [PATCH] TaskTracker : Simplified thread information tracker Richard Guy Briggs
2014-10-10 12:40               ` Tetsuo Handa
2014-10-10 12:49                 ` Richard Guy Briggs
2015-01-04 11:50                   ` Tetsuo Handa
2015-01-05 18:07                     ` Richard Guy Briggs
2015-01-12  6:13                       ` Tetsuo Handa
2015-01-12 15:14                         ` Steve Grubb
2015-01-12 20:51                           ` Paul Moore
2015-01-20 13:20                           ` Tetsuo Handa
2015-01-12 15:21                         ` Richard Guy Briggs
2015-01-19 15:25                     ` Richard Guy Briggs

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.