diff mbox series

[v2] TaskTracker : Simplified thread information tracker.

Message ID 41d03271-ff8a-9888-11de-a7f53da47328@I-love.SAKURA.ne.jp (mailing list archive)
State Handled Elsewhere
Delegated to: Paul Moore
Headers show
Series [v2] TaskTracker : Simplified thread information tracker. | expand

Commit Message

Tetsuo Handa Aug. 6, 2023, 1:04 p.m. UTC
When an unexpected system event occurs, the administrator may want to
identify which application triggered the event. For example, unexpected
process termination is still a real concern enough to write articles
like https://access.redhat.com/solutions/165993 . TaskTracker is a
trivial LSM module which emits TOMOYO-like information into the audit
logs for better understanding of unexpected system events.

I suggested TaskTracker about 10 years ago [1]. Compared to that time,
security_task_alloc()/security_task_free() hooks have been revived, but
the multiple concurrent LSM patches have not completed yet.

When I proposed TaskTracker as an LSM module [2], there was a comment that
this module should not reuse the subj= field and instead add new fields to
audit logs. But that thread died for unknown reason, and there is an effort
for making it possible to enable SELinux and Smack at the same time.
Thus, retrying as an LSM module based on an assumption that the multiple
concurrent LSM patches will address how to share the subj= field. I think
that passing whole history in one string is easier for those who want to
avoid bloating audit log files to control history size and fields to
include. Also, I think that userspace tools won't try to tokenize
this history in order to perform more than fgrep matching.

But now that LSM people are about to require an LSM ID for registering an
LSM module, I can't wait till it becomes possible to enable SELinux and
Smack at the same time. I have to send TaskTracker upstream in order to
assign an LSM ID for TaskTracker.

Link: https://marc.info/?l=linux-security-module&m=138547679621695 [1]
Link: https://lkml.kernel.org/r/201405232144.JFB30480.OVMOOSFtQJHLFF@I-love.SAKURA.ne.jp [2]
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/Kconfig                   |   1 +
 security/Makefile                  |   1 +
 security/tasktracker/Kconfig       |  24 +++++
 security/tasktracker/Makefile      |   2 +
 security/tasktracker/tasktracker.c | 160 +++++++++++++++++++++++++++++
 5 files changed, 188 insertions(+)
 create mode 100644 security/tasktracker/Kconfig
 create mode 100644 security/tasktracker/Makefile
 create mode 100644 security/tasktracker/tasktracker.c

Comments

Steve Grubb Aug. 6, 2023, 10:01 p.m. UTC | #1
Hello,

Paul can probably give you more feedback on ths from a technical PoV. BUt the 
overall approach I can give some feedback.

On Sunday, August 6, 2023 9:04:55 AM EDT Tetsuo Handa wrote:
> When an unexpected system event occurs, the administrator may want to
> identify which application triggered the event. For example, unexpected
> process termination is still a real concern enough to write articles
> like https://access.redhat.com/solutions/165993 . TaskTracker is a
> trivial LSM module which emits TOMOYO-like information into the audit
> logs for better understanding of unexpected system events.
> 
> I suggested TaskTracker about 10 years ago [1]. Compared to that time,
> security_task_alloc()/security_task_free() hooks have been revived, but
> the multiple concurrent LSM patches have not completed yet.
> 
> When I proposed TaskTracker as an LSM module [2], there was a comment that
> this module should not reuse the subj= field and instead add new fields to
> audit logs. But that thread died for unknown reason, and there is an effort
> for making it possible to enable SELinux and Smack at the same time. Thus,
> retrying as an LSM module based on an assumption that the multiple
> concurrent LSM patches will address how to share the subj= field. I think
> that passing whole history in one string is easier for those who want to
> avoid bloating audit log files to control history size and fields to
> include. Also, I think that userspace tools won't try to tokenize this
> history in order to perform more than fgrep matching.
> 
> But now that LSM people are about to require an LSM ID for registering an
> LSM module, I can't wait till it becomes possible to enable SELinux and
> Smack at the same time. I have to send TaskTracker upstream in order to
> assign an LSM ID for TaskTracker.
> 
> Link: https://marc.info/?l=linux-security-module&m=138547679621695 [1]
> Link:
> https://lkml.kernel.org/r/201405232144.JFB30480.OVMOOSFtQJHLFF@I-love.SAKU
> RA.ne.jp [2] Signed-off-by: Tetsuo Handa
> <penguin-kernel@I-love.SAKURA.ne.jp>
> ---
>  security/Kconfig                   |   1 +
>  security/Makefile                  |   1 +
>  security/tasktracker/Kconfig       |  24 +++++
>  security/tasktracker/Makefile      |   2 +
>  security/tasktracker/tasktracker.c | 160 +++++++++++++++++++++++++++++
>  5 files changed, 188 insertions(+)
>  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 52c9af08ad35..aea0ac2b24a1 100644
> --- a/security/Kconfig
> +++ b/security/Kconfig
> @@ -194,6 +194,7 @@ source "security/yama/Kconfig"
>  source "security/safesetid/Kconfig"
>  source "security/lockdown/Kconfig"
>  source "security/landlock/Kconfig"
> +source "security/tasktracker/Kconfig"
> 
>  source "security/integrity/Kconfig"
> 
> diff --git a/security/Makefile b/security/Makefile
> index 18121f8f85cd..86ae43be3207 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -24,6 +24,7 @@ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM)	+= lockdown/
>  obj-$(CONFIG_CGROUPS)			+= device_cgroup.o
>  obj-$(CONFIG_BPF_LSM)			+= bpf/
>  obj-$(CONFIG_SECURITY_LANDLOCK)		+= landlock/
> +obj-$(CONFIG_SECURITY_TASKTRACKER)	+= tasktracker/
> 
>  # Object integrity file lists
>  obj-$(CONFIG_INTEGRITY)			+= integrity/
> diff --git a/security/tasktracker/Kconfig b/security/tasktracker/Kconfig
> new file mode 100644
> index 000000000000..6b294bf18878
> --- /dev/null
> +++ b/security/tasktracker/Kconfig
> @@ -0,0 +1,24 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config SECURITY_TASKTRACKER
> +	bool "TaskTracker Support"
> +	depends on SECURITY && AUDIT
> +	default n
> +	help
> +	  This selects TaskTracker, a module which provides a thread's
> +	  history for better understanding of audit logs.
> +
> +          If you enable this module, you will find history of current
> +          thread in the subj= field of audit logs in the form of
> +          name=$commname;pid=$pid;start=$YYYYMMDDhhmmss delimited by =>
> +          like an example shown below.
> +
> +          [root@localhost ~]# auditctl -a exit,always -F arch=b64 -S kill
> +          [root@localhost ~]# bash
> +          [root@localhost ~]# kill -9 $$
> +          Killed
> +          [root@localhost ~]# ausearch -sc kill
> +          ----
> +          time->Sun Aug  6 15:36:17 2023
> +          type=PROCTITLE msg=audit(1691303777.054:117): proctitle="(null)"
> +          type=OBJ_PID msg=audit(1691303777.054:117): opid=3787 oauid=0
> ouid=0 oses=1 ocomm="bash" +          
> type=SYSCALL
> msg=audit(1691303777.054:117): arch=c000003e syscall=62 success=yes exit=0
> a0=ecb a1=9 a2=0 a3=7ffc779be760 items=0 ppid=3766 pid=3787 auid=0 uid=0
> gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1
> comm="bash" exe="/usr/bin/bash"

This is where the problem begins. We like to have normalized audit records. 
Meaning that a type of event defines the fields it contains. In this case 
subject would be a process label. and there is already a precedent for what 
fields belong in a syscall record.

What I would suggest is to make a separate record: AUDIT_PROC_TREE that 
describes process tree from the one killed up to the last known parent. This 
way you can define your own format and SYSCALL can stay as everyone expects it 
to look. In the EXECVE audit record, there is a precedent of using agv[0]=xx 
argv[1]=xx argv[2]=yy  and so on. If you want to make these generally 
parsable without special knowledge of the record format, I'd suggest 
something like it.

-Steve

> subj="name=swapper/0;pid=0;start=20230806153345=>name=init;pid=1;start=202
> 30806153400=>name=systemd;pid=1;start=20230806153457=>name=sshd;pid=3661;st
> art=20230806063525=>name=sshd;pid=3764;start=20230806063543=>name=bash;pid=
> 3766;start=20230806063549=>name=bash;pid=3787;start=20230806063612"
> key=(null) 


> diff --git a/security/tasktracker/Makefile
> b/security/tasktracker/Makefile new file mode 100644
> index 000000000000..1c11673c7684
> --- /dev/null
> +++ b/security/tasktracker/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-y = tasktracker.o
> diff --git a/security/tasktracker/tasktracker.c
> b/security/tasktracker/tasktracker.c new file mode 100644
> index 000000000000..bd76d7e2b42b
> --- /dev/null
> +++ b/security/tasktracker/tasktracker.c
> @@ -0,0 +1,160 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * tt.c - Simplified thread information tracker.
> + */
> +
> +#include <linux/lsm_hooks.h>
> +#include <linux/ctype.h>
> +
> +#define HISTORY_BUFFER_SIZE 1024
> +
> +struct lsm_blob_sizes tt_blob_sizes __ro_after_init = {
> +	.lbs_task = HISTORY_BUFFER_SIZE,
> +};
> +
> +/**
> + * tt_task - Get history of specified thread.
> + *
> + * @task - Pointer to "struct task_struct".
> + *
> + * Returns history of specified thread.
> + */
> +static char *tt_task(struct task_struct *task)
> +{
> +	return task->security + tt_blob_sizes.lbs_task;
> +}
> +
> +/**
> + * tt_update_history - Update history of current thread.
> + *
> + * Returns nothing.
> + */
> +static void tt_update_history(void)
> +{
> +	int i;
> +	int required;
> +	struct tm tm;
> +	char buf[256];
> +	char *cp = buf;
> +	char *history = tt_task(current);
> +
> +	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 == '-' || 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. */
> +	time64_to_tm(ktime_get_real_seconds(), 0, &tm);
> +	cp += snprintf(cp, buf - cp + sizeof(buf) - 1,
> +		       ";start=%04u%02u%02u%02u%02u%02u", (int) tm.tm_year + 1900,
> +		       tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min,
> +		       tm.tm_sec);
> +	/* Terminate the buffer. */
> +	if (cp >= buf + sizeof(buf))
> +		cp = buf + sizeof(buf) - 1;
> +	*cp = '\0';
> +	required = cp - buf;
> +	/* Truncate history if history is too long to append. */
> +	cp = history;
> +	while (i = strlen(cp), i + required >= HISTORY_BUFFER_SIZE - 10) {
> +		char *cp2 = memchr(cp + 3, '>', i - 3);
> +
> +		if (WARN_ON_ONCE(!cp2))
> +			return;
> +		cp2--;
> +		memmove(cp + 1, cp2, strlen(cp2) + 1);
> +	}
> +	/* Create or append history. */
> +	if (!i)
> +		sprintf(cp, "\"%s\"", buf);
> +	else
> +		sprintf(cp + i - 1, "=>%s\"", buf);
> +}
> +
> +static void tt_current_getsecid_subj(u32 *secid)
> +{
> +	*secid = 1;
> +}
> +
> +/**
> + * tt_secid_to_secctx - Allocate memory used for auditing.
> + *
> + * @secid:   Bool flag to allocate.
> + * @secdata: Pointer to allocate memory.
> + * @seclen:  Unused.
> + *
> + * Returns 0 on success, -EINVAL otherwise.
> + */
> +static int tt_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
> +{
> +	char *history = tt_task(current);
> +
> +	/*
> +	 * This module avoids returning -ENOMEM in order to avoid 
audit_panic(),
> +	 * by returning only current thread's history. Since current thread's
> +	 * history is updated and read by only current thread, we don't need to
> +	 * copy the history for reading.
> +	 */
> +	if (secid != 1)
> +		return -EINVAL;
> +	if (secdata)
> +		*secdata = history;
> +	*seclen = strlen(history);
> +	return 0;
> +}
> +
> +static int tt_task_alloc(struct task_struct *task, unsigned long
> clone_flags) +{
> +	/* Copy from current thread's history upon clone(). */
> +	strscpy(tt_task(task), tt_task(current), HISTORY_BUFFER_SIZE);
> +	return 0;
> +}
> +
> +static void tt_bprm_committing_creds(struct linux_binprm *bprm)
> +{
> +	/* Update current thread's history upon successful execve(). */
> +	tt_update_history();
> +}
> +
> +static struct security_hook_list tt_hooks[] __ro_after_init = {
> +	LSM_HOOK_INIT(current_getsecid_subj, tt_current_getsecid_subj),
> +	LSM_HOOK_INIT(secid_to_secctx, tt_secid_to_secctx),
> +	LSM_HOOK_INIT(task_alloc, tt_task_alloc),
> +	LSM_HOOK_INIT(bprm_committing_creds, tt_bprm_committing_creds),
> +};
> +
> +/**
> + * tt_init - Register TaskTracker as a LSM module.
> + *
> + * Returns 0.
> + */
> +static int __init tt_init(void)
> +{
> +	char *history = tt_task(current);
> +
> +	memset(history, 0, HISTORY_BUFFER_SIZE);
> +	tt_update_history();
> +	security_add_hooks(tt_hooks, ARRAY_SIZE(tt_hooks), "tt");
> +	pr_info("TaskTracker initialized\n");
> +	return 0;
> +}
> +
> +DEFINE_LSM(tt) = {
> +	.name = "tt",
> +	.flags = LSM_FLAG_EXCLUSIVE, /* Due to use of "u32 *secid' argument. */
> +	.blobs = &tt_blob_sizes,
> +	.init = tt_init,
> +};
Tetsuo Handa Aug. 7, 2023, 2:24 p.m. UTC | #2
On 2023/08/07 7:01, Steve Grubb wrote:
> This is where the problem begins. We like to have normalized audit records. 
> Meaning that a type of event defines the fields it contains. In this case 
> subject would be a process label. and there is already a precedent for what 
> fields belong in a syscall record.

What is the definition of "a process label"? SELinux / Smack / AppArmor are using
security_secid_to_secctx() hook for providing string data for the subj= field.
I don't think that they are restricting characters that can be included.
Then, what is wrong with returning subset of ASCII printable characters from
tt_secid_to_secctx() ?



static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
{
	return security_sid_to_context(secid,
				       secdata, seclen);
}

static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
{
	struct smack_known *skp = smack_from_secid(secid);

	if (secdata)
		*secdata = skp->smk_known;
	*seclen = strlen(skp->smk_known);
	return 0;
}

int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
{
	/* TODO: cache secctx and ref count so we don't have to recreate */
	struct aa_label *label = aa_secid_to_label(secid);
	int flags = FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT;
	int len;

	AA_BUG(!seclen);

	if (!label)
		return -EINVAL;

	if (apparmor_display_secid_mode)
		flags |= FLAG_SHOW_MODE;

	if (secdata)
		len = aa_label_asxprint(secdata, root_ns, label,
					flags, GFP_ATOMIC);
	else
		len = aa_label_snxprint(NULL, 0, root_ns, label, flags);

	if (len < 0)
		return -ENOMEM;

	*seclen = len;

	return 0;
}

> 
> What I would suggest is to make a separate record: AUDIT_PROC_TREE that 
> describes process tree from the one killed up to the last known parent. This 
> way you can define your own format and SYSCALL can stay as everyone expects it 
> to look. In the EXECVE audit record, there is a precedent of using agv[0]=xx 
> argv[1]=xx argv[2]=yy  and so on. If you want to make these generally 
> parsable without special knowledge of the record format, I'd suggest 
> something like it.

Yes, https://lkml.kernel.org/r/201501202220.DJJ34834.OLJOHFMQOFtSVF@I-love.SAKURA.ne.jp
used AUDIT_PROCHISTORY instead of LSM hooks, but that thread died there.
Casey Schaufler Aug. 7, 2023, 5:25 p.m. UTC | #3
On 8/7/2023 7:24 AM, Tetsuo Handa wrote:
> On 2023/08/07 7:01, Steve Grubb wrote:
>> This is where the problem begins. We like to have normalized audit records. 
>> Meaning that a type of event defines the fields it contains. In this case 
>> subject would be a process label. and there is already a precedent for what 
>> fields belong in a syscall record.
> What is the definition of "a process label"? SELinux / Smack / AppArmor are using
> security_secid_to_secctx() hook for providing string data for the subj= field.
> I don't think that they are restricting characters that can be included.
> Then, what is wrong with returning subset of ASCII printable characters from
> tt_secid_to_secctx() ?

I would say that a "process label" is the information about the process used
in an access control decision. I agree with Steve that putting the process
history in the subj= field is the wrong approach. I also agree that a separate
record is the way to go.

>
>
>
> static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
> {
> 	return security_sid_to_context(secid,
> 				       secdata, seclen);
> }
>
> static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
> {
> 	struct smack_known *skp = smack_from_secid(secid);
>
> 	if (secdata)
> 		*secdata = skp->smk_known;
> 	*seclen = strlen(skp->smk_known);
> 	return 0;
> }
>
> int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
> {
> 	/* TODO: cache secctx and ref count so we don't have to recreate */
> 	struct aa_label *label = aa_secid_to_label(secid);
> 	int flags = FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT;
> 	int len;
>
> 	AA_BUG(!seclen);
>
> 	if (!label)
> 		return -EINVAL;
>
> 	if (apparmor_display_secid_mode)
> 		flags |= FLAG_SHOW_MODE;
>
> 	if (secdata)
> 		len = aa_label_asxprint(secdata, root_ns, label,
> 					flags, GFP_ATOMIC);
> 	else
> 		len = aa_label_snxprint(NULL, 0, root_ns, label, flags);
>
> 	if (len < 0)
> 		return -ENOMEM;
>
> 	*seclen = len;
>
> 	return 0;
> }
>
>> What I would suggest is to make a separate record: AUDIT_PROC_TREE that 
>> describes process tree from the one killed up to the last known parent. This 
>> way you can define your own format and SYSCALL can stay as everyone expects it 
>> to look. In the EXECVE audit record, there is a precedent of using agv[0]=xx 
>> argv[1]=xx argv[2]=yy  and so on. If you want to make these generally 
>> parsable without special knowledge of the record format, I'd suggest 
>> something like it.
> Yes, https://lkml.kernel.org/r/201501202220.DJJ34834.OLJOHFMQOFtSVF@I-love.SAKURA.ne.jp
> used AUDIT_PROCHISTORY instead of LSM hooks, but that thread died there.
>
Paul Moore Aug. 7, 2023, 6:53 p.m. UTC | #4
On Sun, Aug 6, 2023 at 9:05 AM Tetsuo Handa
<penguin-kernel@i-love.sakura.ne.jp> wrote:
>
> When an unexpected system event occurs, the administrator may want to
> identify which application triggered the event. For example, unexpected
> process termination is still a real concern enough to write articles
> like https://access.redhat.com/solutions/165993 . TaskTracker is a
> trivial LSM module which emits TOMOYO-like information into the audit
> logs for better understanding of unexpected system events.

Help me understand why all of this information isn't already available
via some combination of Audit and TOMOYO, or simply audit itself?

In the case of an audit-only design you would likely need to do some
processing of the audit log to determine the full historical process
tree of the process being killed, but all of the information should be
there if you configure audit properly.  I'm less familiar with TOMOYO,
but your comment about this LSM recording "TOMOYO-like" information
makes me believe that TOMOYO already records this information.
Steve Grubb Aug. 7, 2023, 6:54 p.m. UTC | #5
On Monday, August 7, 2023 10:24:51 AM EDT Tetsuo Handa wrote:
> On 2023/08/07 7:01, Steve Grubb wrote:
> > This is where the problem begins. We like to have normalized audit
> > records. Meaning that a type of event defines the fields it contains. In
> > this case subject would be a process label. and there is already a
> > precedent for what fields belong in a syscall record.
> 
> What is the definition of "a process label"? SELinux / Smack / AppArmor are
> using security_secid_to_secctx() hook for providing string data for the
> subj= field. I don't think that they are restricting characters that can
> be included. Then, what is wrong with returning subset of ASCII printable
> characters from tt_secid_to_secctx() ?

Typically the label is used for access control decisions. But processes have 
other attributes such as a list of open files. I think adding this information 
will be useful - I'm not opposed to the idea. I am just thinking about how to 
present the information where it is more useful. 

<snip>

> > What I would suggest is to make a separate record: AUDIT_PROC_TREE that
> > describes process tree from the one killed up to the last known parent.
> > This way you can define your own format and SYSCALL can stay as everyone
> > expects it to look. In the EXECVE audit record, there is a precedent of
> > using agv[0]=xx argv[1]=xx argv[2]=yy  and so on. If you want to make
> > these generally parsable without special knowledge of the record format,
> > I'd suggest something like it.
> 
> Yes,
> https://lkml.kernel.org/r/201501202220.DJJ34834.OLJOHFMQOFtSVF@I-love.SAKU
> RA.ne.jp used AUDIT_PROCHISTORY instead of LSM hooks, but that thread died
> there.

I do not read that mail list. AUDIT_PROC_HIST or AUDIT_PROC_CHAIN or some 
thing like that would be the better way to go. If someone wanted to see if 
they have process history for a segfault, how would they do it with the 
proposed record?

ausearch --subject sshd

That just doesn't seem right. If you had a record dedicated to this 
information, then you can do:

ausearch -m PROC_HIST

and it would give you that information. And if you had the data split up 
like: p[0]=xx p[1]=xx p[2]=yy

Then  someone could do this to make a report specific to this:

import auparser as aup
au = aup.AuParser(aup.AUSOURCE_FILE, "audit.log")

au.search_add_expression("type r= PROC_HIST", aup.AUSEARCH_RULE_CLEAR)
au.search_set_stop(aup.AUSEARCH_STOP_RECORD)
while au.search_next_event():
    print("Call chain: ", end="")
    while True:
        print(au.interpret_field(), end = "")
        if au.next_field() == False:
            break
        print("->", end="")

au = None
sys.exit(0)

This would be more programmer friendly.

-Steve
Steve Grubb Aug. 7, 2023, 7:03 p.m. UTC | #6
On Monday, August 7, 2023 2:53:40 PM EDT Paul Moore wrote:
> On Sun, Aug 6, 2023 at 9:05 AM Tetsuo Handa
> 
> <penguin-kernel@i-love.sakura.ne.jp> wrote:
> > When an unexpected system event occurs, the administrator may want to
> > identify which application triggered the event. For example, unexpected
> > process termination is still a real concern enough to write articles
> > like https://access.redhat.com/solutions/165993 . TaskTracker is a
> > trivial LSM module which emits TOMOYO-like information into the audit
> > logs for better understanding of unexpected system events.
> 
> Help me understand why all of this information isn't already available
> via some combination of Audit and TOMOYO, or simply audit itself?

Usually when you want this kind of information, you are investigating an 
incident. You wouldn't place a syscall audit for every execve and then 
reconstruct the call chain from that. In the case of long running daemons, 
the information could have been rotated away. But typically you want to see 
what the entry point is. A sudden shell from bind would be suspicious while a 
shell from sshd is not.

-Steve

> In the case of an audit-only design you would likely need to do some
> processing of the audit log to determine the full historical process
> tree of the process being killed, but all of the information should be
> there if you configure audit properly.  I'm less familiar with TOMOYO,
> but your comment about this LSM recording "TOMOYO-like" information
> makes me believe that TOMOYO already records this information.
Paul Moore Aug. 7, 2023, 8:13 p.m. UTC | #7
On Mon, Aug 7, 2023 at 3:03 PM Steve Grubb <sgrubb@redhat.com> wrote:
> On Monday, August 7, 2023 2:53:40 PM EDT Paul Moore wrote:
> > On Sun, Aug 6, 2023 at 9:05 AM Tetsuo Handa
> >
> > <penguin-kernel@i-love.sakura.ne.jp> wrote:
> > > When an unexpected system event occurs, the administrator may want to
> > > identify which application triggered the event. For example, unexpected
> > > process termination is still a real concern enough to write articles
> > > like https://access.redhat.com/solutions/165993 . TaskTracker is a
> > > trivial LSM module which emits TOMOYO-like information into the audit
> > > logs for better understanding of unexpected system events.
> >
> > Help me understand why all of this information isn't already available
> > via some combination of Audit and TOMOYO, or simply audit itself?
>
> Usually when you want this kind of information, you are investigating an
> incident. You wouldn't place a syscall audit for every execve and then
> reconstruct the call chain from that. In the case of long running daemons,
> the information could have been rotated away. But typically you want to see
> what the entry point is. A sudden shell from bind would be suspicious while a
> shell from sshd is not.

Once again, why not use the existing audit and/or TOMOYO capabilities.
Tetsuo Handa Aug. 8, 2023, 10:07 a.m. UTC | #8
On 2023/08/08 5:13, Paul Moore wrote:
> On Mon, Aug 7, 2023 at 3:03 PM Steve Grubb <sgrubb@redhat.com> wrote:
>> On Monday, August 7, 2023 2:53:40 PM EDT Paul Moore wrote:
>>> On Sun, Aug 6, 2023 at 9:05 AM Tetsuo Handa
>>>
>>> <penguin-kernel@i-love.sakura.ne.jp> wrote:
>>>> When an unexpected system event occurs, the administrator may want to
>>>> identify which application triggered the event. For example, unexpected
>>>> process termination is still a real concern enough to write articles
>>>> like https://access.redhat.com/solutions/165993 . TaskTracker is a
>>>> trivial LSM module which emits TOMOYO-like information into the audit
>>>> logs for better understanding of unexpected system events.
>>>
>>> Help me understand why all of this information isn't already available
>>> via some combination of Audit and TOMOYO, or simply audit itself?
>>
>> Usually when you want this kind of information, you are investigating an
>> incident. You wouldn't place a syscall audit for every execve and then
>> reconstruct the call chain from that. In the case of long running daemons,
>> the information could have been rotated away. But typically you want to see
>> what the entry point is. A sudden shell from bind would be suspicious while a
>> shell from sshd is not.
> 
> Once again, why not use the existing audit and/or TOMOYO capabilities.
> 

Can't, for Fedora/RHEL does not enable TOMOYO.
I need a way that can be used by RHEL users running with selinux=0.

This history information is expected to become available for those who
might want to investigate unexpected system events, but should not become
unconditionally available for those who do not want to bloat audit logs.
Switching via lsm=tt is a handy way for satisfying both.
Tetsuo Handa Aug. 8, 2023, 10:25 a.m. UTC | #9
On 2023/08/08 3:54, Steve Grubb wrote:
>>> What I would suggest is to make a separate record: AUDIT_PROC_TREE that
>>> describes process tree from the one killed up to the last known parent.
>>> This way you can define your own format and SYSCALL can stay as everyone
>>> expects it to look. In the EXECVE audit record, there is a precedent of
>>> using agv[0]=xx argv[1]=xx argv[2]=yy  and so on. If you want to make
>>> these generally parsable without special knowledge of the record format,
>>> I'd suggest something like it.
>>
>> Yes,
>> https://lkml.kernel.org/r/201501202220.DJJ34834.OLJOHFMQOFtSVF@I-love.SAKU
>> RA.ne.jp used AUDIT_PROCHISTORY instead of LSM hooks, but that thread died
>> there.
> 
> I do not read that mail list. AUDIT_PROC_HIST or AUDIT_PROC_CHAIN or some 
> thing like that would be the better way to go. If someone wanted to see if 
> they have process history for a segfault, how would they do it with the 
> proposed record?

Avoid bloating of audit log files could be done when saving into audit log
files, but avoiding overhead of tracking/recording this history information
would need to be done using kernel command line options.

Is there a kernel command line option that can configure whether to include
(and what to be included into) this history information or not?

If an LSM is used, a kernel command line option like lsm=tt can be used for
telling the kernel to include this history information and kernel command
line options like tt.size=512 tt.fields=name,stamp for telling the kernel
max history length and fields to include.
Paul Moore Aug. 8, 2023, 2:38 p.m. UTC | #10
On Tue, Aug 8, 2023 at 6:07 AM Tetsuo Handa
<penguin-kernel@i-love.sakura.ne.jp> wrote:
> On 2023/08/08 5:13, Paul Moore wrote:
> > On Mon, Aug 7, 2023 at 3:03 PM Steve Grubb <sgrubb@redhat.com> wrote:
> >> On Monday, August 7, 2023 2:53:40 PM EDT Paul Moore wrote:
> >>> On Sun, Aug 6, 2023 at 9:05 AM Tetsuo Handa
> >>>
> >>> <penguin-kernel@i-love.sakura.ne.jp> wrote:
> >>>> When an unexpected system event occurs, the administrator may want to
> >>>> identify which application triggered the event. For example, unexpected
> >>>> process termination is still a real concern enough to write articles
> >>>> like https://access.redhat.com/solutions/165993 . TaskTracker is a
> >>>> trivial LSM module which emits TOMOYO-like information into the audit
> >>>> logs for better understanding of unexpected system events.
> >>>
> >>> Help me understand why all of this information isn't already available
> >>> via some combination of Audit and TOMOYO, or simply audit itself?
> >>
> >> Usually when you want this kind of information, you are investigating an
> >> incident. You wouldn't place a syscall audit for every execve and then
> >> reconstruct the call chain from that. In the case of long running daemons,
> >> the information could have been rotated away. But typically you want to see
> >> what the entry point is. A sudden shell from bind would be suspicious while a
> >> shell from sshd is not.
> >
> > Once again, why not use the existing audit and/or TOMOYO capabilities.
> >
>
> Can't, for Fedora/RHEL does not enable TOMOYO.
> I need a way that can be used by RHEL users running with selinux=0.

What makes you think your distribution of choice would enable this new
LSM?  I'm sorry, but this sounds like more of an issue with the
choices made by a distro rather than something missing upstream.
diff mbox series

Patch

diff --git a/security/Kconfig b/security/Kconfig
index 52c9af08ad35..aea0ac2b24a1 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -194,6 +194,7 @@  source "security/yama/Kconfig"
 source "security/safesetid/Kconfig"
 source "security/lockdown/Kconfig"
 source "security/landlock/Kconfig"
+source "security/tasktracker/Kconfig"
 
 source "security/integrity/Kconfig"
 
diff --git a/security/Makefile b/security/Makefile
index 18121f8f85cd..86ae43be3207 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -24,6 +24,7 @@  obj-$(CONFIG_SECURITY_LOCKDOWN_LSM)	+= lockdown/
 obj-$(CONFIG_CGROUPS)			+= device_cgroup.o
 obj-$(CONFIG_BPF_LSM)			+= bpf/
 obj-$(CONFIG_SECURITY_LANDLOCK)		+= landlock/
+obj-$(CONFIG_SECURITY_TASKTRACKER)	+= tasktracker/
 
 # Object integrity file lists
 obj-$(CONFIG_INTEGRITY)			+= integrity/
diff --git a/security/tasktracker/Kconfig b/security/tasktracker/Kconfig
new file mode 100644
index 000000000000..6b294bf18878
--- /dev/null
+++ b/security/tasktracker/Kconfig
@@ -0,0 +1,24 @@ 
+# SPDX-License-Identifier: GPL-2.0-only
+config SECURITY_TASKTRACKER
+	bool "TaskTracker Support"
+	depends on SECURITY && AUDIT
+	default n
+	help
+	  This selects TaskTracker, a module which provides a thread's
+	  history for better understanding of audit logs.
+
+          If you enable this module, you will find history of current
+          thread in the subj= field of audit logs in the form of
+          name=$commname;pid=$pid;start=$YYYYMMDDhhmmss delimited by =>
+          like an example shown below.
+
+          [root@localhost ~]# auditctl -a exit,always -F arch=b64 -S kill
+          [root@localhost ~]# bash
+          [root@localhost ~]# kill -9 $$
+          Killed
+          [root@localhost ~]# ausearch -sc kill
+          ----
+          time->Sun Aug  6 15:36:17 2023
+          type=PROCTITLE msg=audit(1691303777.054:117): proctitle="(null)"
+          type=OBJ_PID msg=audit(1691303777.054:117): opid=3787 oauid=0 ouid=0 oses=1 ocomm="bash"
+          type=SYSCALL msg=audit(1691303777.054:117): arch=c000003e syscall=62 success=yes exit=0 a0=ecb a1=9 a2=0 a3=7ffc779be760 items=0 ppid=3766 pid=3787 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="bash" exe="/usr/bin/bash" subj="name=swapper/0;pid=0;start=20230806153345=>name=init;pid=1;start=20230806153400=>name=systemd;pid=1;start=20230806153457=>name=sshd;pid=3661;start=20230806063525=>name=sshd;pid=3764;start=20230806063543=>name=bash;pid=3766;start=20230806063549=>name=bash;pid=3787;start=20230806063612" key=(null)
diff --git a/security/tasktracker/Makefile b/security/tasktracker/Makefile
new file mode 100644
index 000000000000..1c11673c7684
--- /dev/null
+++ b/security/tasktracker/Makefile
@@ -0,0 +1,2 @@ 
+# SPDX-License-Identifier: GPL-2.0
+obj-y = tasktracker.o
diff --git a/security/tasktracker/tasktracker.c b/security/tasktracker/tasktracker.c
new file mode 100644
index 000000000000..bd76d7e2b42b
--- /dev/null
+++ b/security/tasktracker/tasktracker.c
@@ -0,0 +1,160 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * tt.c - Simplified thread information tracker.
+ */
+
+#include <linux/lsm_hooks.h>
+#include <linux/ctype.h>
+
+#define HISTORY_BUFFER_SIZE 1024
+
+struct lsm_blob_sizes tt_blob_sizes __ro_after_init = {
+	.lbs_task = HISTORY_BUFFER_SIZE,
+};
+
+/**
+ * tt_task - Get history of specified thread.
+ *
+ * @task - Pointer to "struct task_struct".
+ *
+ * Returns history of specified thread.
+ */
+static char *tt_task(struct task_struct *task)
+{
+	return task->security + tt_blob_sizes.lbs_task;
+}
+
+/**
+ * tt_update_history - Update history of current thread.
+ *
+ * Returns nothing.
+ */
+static void tt_update_history(void)
+{
+	int i;
+	int required;
+	struct tm tm;
+	char buf[256];
+	char *cp = buf;
+	char *history = tt_task(current);
+
+	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 == '-' || 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. */
+	time64_to_tm(ktime_get_real_seconds(), 0, &tm);
+	cp += snprintf(cp, buf - cp + sizeof(buf) - 1,
+		       ";start=%04u%02u%02u%02u%02u%02u", (int) tm.tm_year + 1900,
+		       tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min,
+		       tm.tm_sec);
+	/* Terminate the buffer. */
+	if (cp >= buf + sizeof(buf))
+		cp = buf + sizeof(buf) - 1;
+	*cp = '\0';
+	required = cp - buf;
+	/* Truncate history if history is too long to append. */
+	cp = history;
+	while (i = strlen(cp), i + required >= HISTORY_BUFFER_SIZE - 10) {
+		char *cp2 = memchr(cp + 3, '>', i - 3);
+
+		if (WARN_ON_ONCE(!cp2))
+			return;
+		cp2--;
+		memmove(cp + 1, cp2, strlen(cp2) + 1);
+	}
+	/* Create or append history. */
+	if (!i)
+		sprintf(cp, "\"%s\"", buf);
+	else
+		sprintf(cp + i - 1, "=>%s\"", buf);
+}
+
+static void tt_current_getsecid_subj(u32 *secid)
+{
+	*secid = 1;
+}
+
+/**
+ * tt_secid_to_secctx - Allocate memory used for auditing.
+ *
+ * @secid:   Bool flag to allocate.
+ * @secdata: Pointer to allocate memory.
+ * @seclen:  Unused.
+ *
+ * Returns 0 on success, -EINVAL otherwise.
+ */
+static int tt_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+	char *history = tt_task(current);
+
+	/*
+	 * This module avoids returning -ENOMEM in order to avoid audit_panic(),
+	 * by returning only current thread's history. Since current thread's
+	 * history is updated and read by only current thread, we don't need to
+	 * copy the history for reading.
+	 */
+	if (secid != 1)
+		return -EINVAL;
+	if (secdata)
+		*secdata = history;
+	*seclen = strlen(history);
+	return 0;
+}
+
+static int tt_task_alloc(struct task_struct *task, unsigned long clone_flags)
+{
+	/* Copy from current thread's history upon clone(). */
+	strscpy(tt_task(task), tt_task(current), HISTORY_BUFFER_SIZE);
+	return 0;
+}
+
+static void tt_bprm_committing_creds(struct linux_binprm *bprm)
+{
+	/* Update current thread's history upon successful execve(). */
+	tt_update_history();
+}
+
+static struct security_hook_list tt_hooks[] __ro_after_init = {
+	LSM_HOOK_INIT(current_getsecid_subj, tt_current_getsecid_subj),
+	LSM_HOOK_INIT(secid_to_secctx, tt_secid_to_secctx),
+	LSM_HOOK_INIT(task_alloc, tt_task_alloc),
+	LSM_HOOK_INIT(bprm_committing_creds, tt_bprm_committing_creds),
+};
+
+/**
+ * tt_init - Register TaskTracker as a LSM module.
+ *
+ * Returns 0.
+ */
+static int __init tt_init(void)
+{
+	char *history = tt_task(current);
+
+	memset(history, 0, HISTORY_BUFFER_SIZE);
+	tt_update_history();
+	security_add_hooks(tt_hooks, ARRAY_SIZE(tt_hooks), "tt");
+	pr_info("TaskTracker initialized\n");
+	return 0;
+}
+
+DEFINE_LSM(tt) = {
+	.name = "tt",
+	.flags = LSM_FLAG_EXCLUSIVE, /* Due to use of "u32 *secid' argument. */
+	.blobs = &tt_blob_sizes,
+	.init = tt_init,
+};