[PATCH] TaskTracker : Simplified thread information tracker.
Tetsuo Handa
penguin-kernel at I-love.SAKURA.ne.jp
Mon Jun 23 12:14:35 UTC 2014
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 at 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 at 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 at 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
>
More information about the Linux-audit
mailing list