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 |
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, > +};
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.
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. >
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.
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
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.
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.
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.
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.
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 --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, +};
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