Message ID | 20230204050954.11583-9-greg@enjellic.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Paul Moore |
Headers | show |
Series | Implement Trusted Security Event Modeling. | expand |
On Fri, Feb 03, 2023 at 11:09:48PM -0600, Dr. Greg wrote: > The fs.c file contains the implementation of the TSEM control > plane that is in the form of a pseudo-filesystem mounted on the > following directory: > > /sys/fs/tsem Why are you using sysfs to mount this? > > The following file documents the interface provided by the > control plane: > > Documentation/ABI/testing/tsemfs > > The pseudo-files act on the modeling context of the process that > is acting on the file. For example, reading the 'id' > pseudo-file, returns the modeling domain identifier that the > process is running in. > > The ExternalTMA directory is used to segreate the pseudo-files > that are created in order to surface security event descriptions > to an external trust orchestrator. The files in this directory > appear as the numeric value of the modeling domain they were > created for. > > The 'control' pseudo-file is the only writable file in the plane > and is used to control the TSEM implementation. The most > important and primary roles are to create namespaces and set the > trust status of a process modeled by an external TMA. > > Signed-off-by: Greg Wettstein <greg@enjellic.com> > --- > security/tsem/fs.c | 894 +++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 894 insertions(+) > create mode 100644 security/tsem/fs.c > > diff --git a/security/tsem/fs.c b/security/tsem/fs.c > new file mode 100644 > index 000000000000..2898a1cc8c97 > --- /dev/null > +++ b/security/tsem/fs.c > @@ -0,0 +1,894 @@ > +// SPDX-License-Identifier: GPL-2.0-only > + > +/* > + * Copyright (C) 2022 Enjellic Systems Development, LLC It's 2023 :) > + * Author: Dr. Greg Wettstein <greg@enjellic.com> > + * > + * Implements the sysfs based control plane. This is NOT sysfs, it is your own filesystem, please don't confuse the two. > + */ > + > +#include <linux/seq_file.h> > +#include <linux/sysfs.h> > +#include <linux/fs_context.h> > +#include <linux/namei.h> > +#include <linux/poll.h> > +#include <uapi/linux/magic.h> > + > +#include "tsem.h" > + > +static int fs_init_context(struct fs_context *context); > +static int fs_get_tree(struct fs_context *context); > + > +static struct file_system_type fs_definition = { > + .name = "tsemfs", > + .init_fs_context = fs_init_context, > + .kill_sb = kill_litter_super > +}; > + > +static struct fs_context_operations fs_operations = { > + .get_tree = fs_get_tree > +}; > + > +static struct vfsmount *fs_mount; > + > +static int fs_mount_cnt; > + > +static struct dentry *external_dentry; > + > +struct control_commands { > + char *cmd; > + enum tsem_control_type type; > +}; > + > +static struct control_commands commands[9] = { > + {"internal", TSEM_CONTROL_INTERNAL}, > + {"external", TSEM_CONTROL_EXTERNAL}, > + {"enforce", TSEM_CONTROL_ENFORCE}, > + {"seal", TSEM_CONTROL_SEAL}, > + {"trusted ", TSEM_CONTROL_TRUSTED}, > + {"untrusted ", TSEM_CONTROL_UNTRUSTED}, > + {"state ", TSEM_CONTROL_MAP_STATE}, > + {"pseudonym ", TSEM_CONTROL_MAP_PSEUDONYM}, > + {"base ", TSEM_CONTROL_MAP_BASE} > +}; > + > +static bool can_access_fs(void) > +{ > + struct tsem_TMA_context *ctx = tsem_context(current); > + > + if (ctx->external) > + return false; > + if (capable(TSEM_CONTROL_CAPABILITY)) > + return true; > + if (ctx->sealed) > + return false; > + return true; > +} > + > +static int control_COE(pid_t pid, unsigned long cmd) > +{ > + bool wakeup = false; > + int retn = -ESRCH; > + struct task_struct *COE; > + struct tsem_task *task; > + > + rcu_read_lock(); > + COE = find_task_by_vpid(pid); > + if (COE != NULL) { > + task = tsem_task(COE); > + if (cmd == TSEM_CONTROL_UNTRUSTED) > + task->trust_status = TSEM_TASK_UNTRUSTED; > + if (cmd == TSEM_CONTROL_TRUSTED) { > + task->trust_status &= ~TSEM_TASK_TRUST_PENDING; > + if (tsem_task_trusted(COE)) > + task->trust_status = TSEM_TASK_TRUSTED; > + } > + > + retn = 0; > + wakeup = true; > + } > + rcu_read_unlock(); > + > + if (wakeup) > + wake_up_process(COE); > + > + return retn; > +} > + > +static int config_context(unsigned long cmd, char *bufr) > +{ > + int retn = -EINVAL; > + unsigned int lp; > + struct tsem_TMA_context *ctx = tsem_context(current); > + > + if (ctx->sealed) > + return -EPERM; > + > + if (cmd == TSEM_CONTROL_SEAL) { > + ctx->sealed = true; > + retn = 0; > + } > + > + if (cmd == TSEM_CONTROL_ENFORCE) { > + for (lp = 0; lp < ARRAY_SIZE(tsem_root_actions); ++lp) > + ctx->actions[lp] = TSEM_ACTION_EPERM; > + retn = 0; > + } > + > + return retn; > +} > + > +static int config_point(enum tsem_control_type type, u8 *arg) > +{ > + int retn = -EINVAL; > + u8 mapping[WP256_DIGEST_SIZE]; > + > + if (*++arg == '\0') > + goto done; > + if (strlen(arg) != sizeof(mapping) * 2) > + goto done; > + if (hex2bin(mapping, arg, sizeof(mapping))) > + goto done; > + > + if (type == TSEM_CONTROL_MAP_STATE) > + retn = tsem_model_load_point(mapping); > + else if (type == TSEM_CONTROL_MAP_PSEUDONYM) > + retn = tsem_model_load_pseudonym(mapping); > + else { > + tsem_model_load_base(mapping); > + retn = 0; > + } > + > + done: > + return retn; > +} > + > +static void show_event(struct seq_file *c, struct tsem_event *ep, char *file) > +{ > + seq_printf(c, "event{process=%s, filename=%s, type=%s, task_id=%*phN}", > + ep->comm, file ? file : "none", tsem_names[ep->event], > + WP256_DIGEST_SIZE, ep->task_id); > + seq_printf(c, " COE{uid=%d, euid=%d, suid=%d, gid=%d, egid=%d, sgid=%d, fsuid=%d, fsgid=%d, cap=0x%llx} ", > + ep->COE.uid, ep->COE.euid, ep->COE.suid, ep->COE.gid, > + ep->COE.egid, ep->COE.sgid, ep->COE.fsuid, ep->COE.fsgid, > + ep->COE.capability.value); > +} You are printing out a lot of specific data in a specific format that is documented no where that I can see :( Also, you are making the same mistake that /proc made decades ago, creating files that must be parsed and can NEVER be changed in the future. Why not make this a one-value-per-file filesystem instead so that you have flexibility going forward? > +static void show_file(struct seq_file *c, struct tsem_event *ep) > +{ > + seq_printf(c, "file{flags=%u, uid=%d, gid=%d, mode=0%o, name_length=%u, name=%*phN, s_magic=0x%0x, s_id=%s, s_uuid=%*phN, digest=%*phN}\n", > + ep->file.flags, ep->file.uid, ep->file.gid, ep->file.mode, > + ep->file.name_length, WP256_DIGEST_SIZE, ep->file.name, > + ep->file.s_magic, ep->file.s_id, > + (int) sizeof(ep->file.s_uuid), ep->file.s_uuid, > + WP256_DIGEST_SIZE, ep->file.digest); Same here, where is the documentation for all of this and what userspace tools parse it? thanks, greg k-h
On Thu, Feb 09, 2023 at 12:30:51PM +0100, Greg KH wrote: Good afternoon Greg, thanks for taking the time to review the patches and pass along comments. > On Fri, Feb 03, 2023 at 11:09:48PM -0600, Dr. Greg wrote: > > The fs.c file contains the implementation of the TSEM control > > plane that is in the form of a pseudo-filesystem mounted on the > > following directory: > > > > /sys/fs/tsem > Why are you using sysfs to mount this? We followed the lead of the SMACK and SeLinux LSM's, both of which create the mount points for their control plane filesystems in /sys/fs. In addition, as a filesystem, we chose to have tsemfs closely follow their design for continuity across the LSM's. So they share similar functionality and design, modulo of course, the event description and trajectory export files that we will chat about below. We can't use securityfs, secondary to the fact that it doesn't implement pollable files, which are a requirement for trust orchestrators based on external Trusted Modeling Agents. So the question is, where would you like us to put the control plane, I'm assuming on a mount point created in /sys/kernel/security? Which would seem to leave the question of why some LSM's put their control planes in /sys/fs and others elsewhere. > > The following file documents the interface provided by the > > control plane: > > > > Documentation/ABI/testing/tsemfs > > > > The pseudo-files act on the modeling context of the process that > > is acting on the file. For example, reading the 'id' > > pseudo-file, returns the modeling domain identifier that the > > process is running in. > > > > The ExternalTMA directory is used to segreate the pseudo-files > > that are created in order to surface security event descriptions > > to an external trust orchestrator. The files in this directory > > appear as the numeric value of the modeling domain they were > > created for. > > > > The 'control' pseudo-file is the only writable file in the plane > > and is used to control the TSEM implementation. The most > > important and primary roles are to create namespaces and set the > > trust status of a process modeled by an external TMA. > > > > Signed-off-by: Greg Wettstein <greg@enjellic.com> > > --- > > security/tsem/fs.c | 894 +++++++++++++++++++++++++++++++++++++++++++++ > > 1 file changed, 894 insertions(+) > > create mode 100644 security/tsem/fs.c > > > > diff --git a/security/tsem/fs.c b/security/tsem/fs.c > > new file mode 100644 > > index 000000000000..2898a1cc8c97 > > --- /dev/null > > +++ b/security/tsem/fs.c > > @@ -0,0 +1,894 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > + > > +/* > > + * Copyright (C) 2022 Enjellic Systems Development, LLC > It's 2023 :) Already fixed... :-) > > + * Author: Dr. Greg Wettstein <greg@enjellic.com> > > + * > > + * Implements the sysfs based control plane. > This is NOT sysfs, it is your own filesystem, please don't confuse > the two. Mea culpa of course, the code now reads; 'Implements the tsemfs based control plane'. I think that sysfs tends to fall prey to the 'Kleenex' problem, with control plane filesystems being generically referred to as 'sysfs' filesystems, which of course, as you indicate, is technically incorrect. > > + */ > > + > > +#include <linux/seq_file.h> > > +#include <linux/sysfs.h> > > +#include <linux/fs_context.h> > > +#include <linux/namei.h> > > +#include <linux/poll.h> > > +#include <uapi/linux/magic.h> > > + > > +#include "tsem.h" > > + > > +static int fs_init_context(struct fs_context *context); > > +static int fs_get_tree(struct fs_context *context); > > + > > +static struct file_system_type fs_definition = { > > + .name = "tsemfs", > > + .init_fs_context = fs_init_context, > > + .kill_sb = kill_litter_super > > +}; > > + > > +static struct fs_context_operations fs_operations = { > > + .get_tree = fs_get_tree > > +}; > > + > > +static struct vfsmount *fs_mount; > > + > > +static int fs_mount_cnt; > > + > > +static struct dentry *external_dentry; > > + > > +struct control_commands { > > + char *cmd; > > + enum tsem_control_type type; > > +}; > > + > > +static struct control_commands commands[9] = { > > + {"internal", TSEM_CONTROL_INTERNAL}, > > + {"external", TSEM_CONTROL_EXTERNAL}, > > + {"enforce", TSEM_CONTROL_ENFORCE}, > > + {"seal", TSEM_CONTROL_SEAL}, > > + {"trusted ", TSEM_CONTROL_TRUSTED}, > > + {"untrusted ", TSEM_CONTROL_UNTRUSTED}, > > + {"state ", TSEM_CONTROL_MAP_STATE}, > > + {"pseudonym ", TSEM_CONTROL_MAP_PSEUDONYM}, > > + {"base ", TSEM_CONTROL_MAP_BASE} > > +}; > > + > > +static bool can_access_fs(void) > > +{ > > + struct tsem_TMA_context *ctx = tsem_context(current); > > + > > + if (ctx->external) > > + return false; > > + if (capable(TSEM_CONTROL_CAPABILITY)) > > + return true; > > + if (ctx->sealed) > > + return false; > > + return true; > > +} > > + > > +static int control_COE(pid_t pid, unsigned long cmd) > > +{ > > + bool wakeup = false; > > + int retn = -ESRCH; > > + struct task_struct *COE; > > + struct tsem_task *task; > > + > > + rcu_read_lock(); > > + COE = find_task_by_vpid(pid); > > + if (COE != NULL) { > > + task = tsem_task(COE); > > + if (cmd == TSEM_CONTROL_UNTRUSTED) > > + task->trust_status = TSEM_TASK_UNTRUSTED; > > + if (cmd == TSEM_CONTROL_TRUSTED) { > > + task->trust_status &= ~TSEM_TASK_TRUST_PENDING; > > + if (tsem_task_trusted(COE)) > > + task->trust_status = TSEM_TASK_TRUSTED; > > + } > > + > > + retn = 0; > > + wakeup = true; > > + } > > + rcu_read_unlock(); > > + > > + if (wakeup) > > + wake_up_process(COE); > > + > > + return retn; > > +} > > + > > +static int config_context(unsigned long cmd, char *bufr) > > +{ > > + int retn = -EINVAL; > > + unsigned int lp; > > + struct tsem_TMA_context *ctx = tsem_context(current); > > + > > + if (ctx->sealed) > > + return -EPERM; > > + > > + if (cmd == TSEM_CONTROL_SEAL) { > > + ctx->sealed = true; > > + retn = 0; > > + } > > + > > + if (cmd == TSEM_CONTROL_ENFORCE) { > > + for (lp = 0; lp < ARRAY_SIZE(tsem_root_actions); ++lp) > > + ctx->actions[lp] = TSEM_ACTION_EPERM; > > + retn = 0; > > + } > > + > > + return retn; > > +} > > + > > +static int config_point(enum tsem_control_type type, u8 *arg) > > +{ > > + int retn = -EINVAL; > > + u8 mapping[WP256_DIGEST_SIZE]; > > + > > + if (*++arg == '\0') > > + goto done; > > + if (strlen(arg) != sizeof(mapping) * 2) > > + goto done; > > + if (hex2bin(mapping, arg, sizeof(mapping))) > > + goto done; > > + > > + if (type == TSEM_CONTROL_MAP_STATE) > > + retn = tsem_model_load_point(mapping); > > + else if (type == TSEM_CONTROL_MAP_PSEUDONYM) > > + retn = tsem_model_load_pseudonym(mapping); > > + else { > > + tsem_model_load_base(mapping); > > + retn = 0; > > + } > > + > > + done: > > + return retn; > > +} > > + > > +static void show_event(struct seq_file *c, struct tsem_event *ep, char *file) > > +{ > > + seq_printf(c, "event{process=%s, filename=%s, type=%s, task_id=%*phN}", > > + ep->comm, file ? file : "none", tsem_names[ep->event], > > + WP256_DIGEST_SIZE, ep->task_id); > > + seq_printf(c, " COE{uid=%d, euid=%d, suid=%d, gid=%d, egid=%d, sgid=%d, fsuid=%d, fsgid=%d, cap=0x%llx} ", > > + ep->COE.uid, ep->COE.euid, ep->COE.suid, ep->COE.gid, > > + ep->COE.egid, ep->COE.sgid, ep->COE.fsuid, ep->COE.fsgid, > > + ep->COE.capability.value); > > +} > You are printing out a lot of specific data in a specific format > that is documented no where that I can see :( It appears you found the ABI documentation in the second patch that was posted in the series, hopefully it was found to be complete, we are currently in 13th position out of 494 files with respect to quantity of ABI documentation... :-) For the sake of 'lore': After applying the patch series, the control plane API is documented in the following file: Documentation/ABI/testing/tsemfs The TSEM LSM documentation proper will be in the following file: Documentation/admin-guide/LSM/tsem.rst Where we currently hold the poll position. > Also, you are making the same mistake that /proc made decades ago, > creating files that must be parsed and can NEVER be changed in the > future. Why not make this a one-value-per-file filesystem instead > so that you have flexibility going forward? Personally, I've been working on Linux since late 1991, so we, as a team, are well aware, and sensitive, to the issues and discussions surrounding the file formatting decisions that were made with the /proc filesystem. So we did not make the decision with respect to formatted output lightly. In fact, I presumed that the top two things we would hear about with respect to TSEM, would be about the introduction of CAP_TRUST from Casey and the format of the event export and execution trajectory files from you. The issue is that the kernel needs to present to trust orchestrators, a description of each LSM security hook, in the form of an atomic update or transaction. To help clarify the discussion, here are examples of records that are output for a file_open and socket_bind hook event: pid{1186} event{process=quixote-us, filename=/opt/Quixote/bin/runc, type=file_open, task_id=0000000000000000000000000000000000000000000000000000000000000000} COE{uid=0, euid=0, suid=0, gid=0, egid=0, sgid=0, fsuid=0, fsgid=0, cap=0x3ffffffffff} file{flags=32800, uid=2, gid=2, mode=0100755, name_length=21, name=4829ead93d026770746f9cdebc76cc4d2a27f45db2d3ac436aa6fce4e2640415, s_magic=0xef53, s_id=xvda, s_uuid=feadbeaffeadbeaffeadbeaffeadbeaf, digest=7c1a43eb99fa739056d6554001d450ca1c9c184ca7e2d8a785bd1e5fd53bad8c} pid{1204} event{process=ncat, filename=none, type=socket_bind, task_id=a5bc0eb22141657132f29e80ca6ea9b211e8443ead1f4b6b766935a14995040f} COE{uid=0, euid=0, suid=0, gid=0, egid=0, sgid=0, fsuid=0, fsgid=0, cap=0x20000420} socket_bind{family=2, port=28695, addr=0} TSEM is currently modeling 84 LSM hooks. We would certainly be open to suggestions as to how we would architect the one-value-per-file filesystem model in a manner that would scale, both with respect to pseudo-filesystem complexity and system call overhead. > > +static void show_file(struct seq_file *c, struct tsem_event *ep) > > +{ > > + seq_printf(c, "file{flags=%u, uid=%d, gid=%d, mode=0%o, name_length=%u, name=%*phN, s_magic=0x%0x, s_id=%s, s_uuid=%*phN, digest=%*phN}\n", > > + ep->file.flags, ep->file.uid, ep->file.gid, ep->file.mode, > > + ep->file.name_length, WP256_DIGEST_SIZE, ep->file.name, > > + ep->file.s_magic, ep->file.s_id, > > + (int) sizeof(ep->file.s_uuid), ep->file.s_uuid, > > + WP256_DIGEST_SIZE, ep->file.digest); > Same here, where is the documentation for all of this and what > userspace tools parse it? We will presume the documentation issue to be addressed. The cover letter for the patch series, and the LSM documentation itself, has the URL for the directory on our FTP server that has the source code for the Quixote trust orchestration system as well as compiled trust orchestration utilities and an SGX enclave, a Xen stubdomain executable and micro-controller firmware for the SanchoSGX, SachoXen and SanchoMCU implementations. More specifically, the following URL's will, at the time of this writing, February 2023... :-), pull the source and binaries: ftp://ftp.enjellic.com/pub/Quixote/Quixote-1.4.tar.gz ftp://ftp.enjellic.com/pub/Quixote/Quixote-1.4-1.x86_64.rpm ftp://ftp.enjellic.com/pub/Quixote/quixote_1.4-2_amd64.deb For some reason 'alien' bumps the release number when it creates the .deb from the rpm.... :-( After unpacking the source archive, the code that handles the parsing of an exported security event description will be found in the following files: SecurityModel/EventParser.c SecurityModel/SecurityEvent.c SecurityModel/COE.c SecurityModel/Cell.c The formatted output was designed to be self-descriptive, extensible and extremely low cost to parse, without the use of an external library. The same codebase runs in everything from a userspace process, to a Xen stubdomin, through an SGX enclave and multiple 32-bit ARM micro-controller implementations. The trust orchestration utilities that use the above code, and implement drivers for the various Sancho trust roots, are in the 'Quixote' directory in the source tree. > thanks, > > greg k-h As I indicated above, we are all ears if someone has a better idea on how to handle the security event description export issue. Have a good weekend. As always, Dr. Greg The Quixote Project - Flailing at the Travails of Cybersecurity
On Fri, Feb 10, 2023 at 06:18:06PM -0600, Dr. Greg wrote: > On Thu, Feb 09, 2023 at 12:30:51PM +0100, Greg KH wrote: > > Good afternoon Greg, thanks for taking the time to review the patches > and pass along comments. > > > On Fri, Feb 03, 2023 at 11:09:48PM -0600, Dr. Greg wrote: > > > The fs.c file contains the implementation of the TSEM control > > > plane that is in the form of a pseudo-filesystem mounted on the > > > following directory: > > > > > > /sys/fs/tsem > > > Why are you using sysfs to mount this? > > We followed the lead of the SMACK and SeLinux LSM's, both of which > create the mount points for their control plane filesystems in > /sys/fs. > > In addition, as a filesystem, we chose to have tsemfs closely follow > their design for continuity across the LSM's. So they share similar > functionality and design, modulo of course, the event description and > trajectory export files that we will chat about below. > > We can't use securityfs, secondary to the fact that it doesn't > implement pollable files, which are a requirement for trust > orchestrators based on external Trusted Modeling Agents. Why not fix securityfs to provide pollable files? Other than that, why can't you just use securityfs? > > > +static void show_event(struct seq_file *c, struct tsem_event *ep, char *file) > > > +{ > > > + seq_printf(c, "event{process=%s, filename=%s, type=%s, task_id=%*phN}", > > > + ep->comm, file ? file : "none", tsem_names[ep->event], > > > + WP256_DIGEST_SIZE, ep->task_id); > > > + seq_printf(c, " COE{uid=%d, euid=%d, suid=%d, gid=%d, egid=%d, sgid=%d, fsuid=%d, fsgid=%d, cap=0x%llx} ", > > > + ep->COE.uid, ep->COE.euid, ep->COE.suid, ep->COE.gid, > > > + ep->COE.egid, ep->COE.sgid, ep->COE.fsuid, ep->COE.fsgid, > > > + ep->COE.capability.value); > > > +} > > > You are printing out a lot of specific data in a specific format > > that is documented no where that I can see :( > > It appears you found the ABI documentation in the second patch that > was posted in the series, hopefully it was found to be complete, we > are currently in 13th position out of 494 files with respect to > quantity of ABI documentation... :-) > > For the sake of 'lore': > > After applying the patch series, the control plane API is documented in the > following file: > > Documentation/ABI/testing/tsemfs > > The TSEM LSM documentation proper will be in the following file: > > Documentation/admin-guide/LSM/tsem.rst > > Where we currently hold the poll position. > > > Also, you are making the same mistake that /proc made decades ago, > > creating files that must be parsed and can NEVER be changed in the > > future. Why not make this a one-value-per-file filesystem instead > > so that you have flexibility going forward? > > Personally, I've been working on Linux since late 1991, so we, as a > team, are well aware, and sensitive, to the issues and discussions > surrounding the file formatting decisions that were made with the > /proc filesystem. So we did not make the decision with respect to > formatted output lightly. > > In fact, I presumed that the top two things we would hear about with > respect to TSEM, would be about the introduction of CAP_TRUST from > Casey and the format of the event export and execution trajectory > files from you. > > The issue is that the kernel needs to present to trust orchestrators, > a description of each LSM security hook, in the form of an atomic > update or transaction. > > To help clarify the discussion, here are examples of records that are > output for a file_open and socket_bind hook event: > > pid{1186} event{process=quixote-us, filename=/opt/Quixote/bin/runc, type=file_open, task_id=0000000000000000000000000000000000000000000000000000000000000000} COE{uid=0, euid=0, suid=0, gid=0, egid=0, sgid=0, fsuid=0, fsgid=0, cap=0x3ffffffffff} file{flags=32800, uid=2, gid=2, mode=0100755, name_length=21, name=4829ead93d026770746f9cdebc76cc4d2a27f45db2d3ac436aa6fce4e2640415, s_magic=0xef53, s_id=xvda, s_uuid=feadbeaffeadbeaffeadbeaffeadbeaf, digest=7c1a43eb99fa739056d6554001d450ca1c9c184ca7e2d8a785bd1e5fd53bad8c} > > pid{1204} event{process=ncat, filename=none, type=socket_bind, task_id=a5bc0eb22141657132f29e80ca6ea9b211e8443ead1f4b6b766935a14995040f} COE{uid=0, euid=0, suid=0, gid=0, egid=0, sgid=0, fsuid=0, fsgid=0, cap=0x20000420} socket_bind{family=2, port=28695, addr=0} > > TSEM is currently modeling 84 LSM hooks. We would certainly be open > to suggestions as to how we would architect the one-value-per-file > filesystem model in a manner that would scale, both with respect to > pseudo-filesystem complexity and system call overhead. You are creating a new structure-type-api here, why not use a already-designed protocol instead like varlink if you need userspace to parse events in an atomic way? Or heck even json would be better as there are universal userspace tools for that today. Or use the audit interface, this feels very close to that, why not just tie into that subsystem instead? thanks, greg k-h
On Sat, Feb 11, 2023 at 11:59:19AM +0100, Greg KH wrote: Looping in Paul Moore in order to get his thoughts. > On Fri, Feb 10, 2023 at 06:18:06PM -0600, Dr. Greg wrote: > > On Thu, Feb 09, 2023 at 12:30:51PM +0100, Greg KH wrote: > > > > Good afternoon Greg, thanks for taking the time to review the patches > > and pass along comments. > > > > > On Fri, Feb 03, 2023 at 11:09:48PM -0600, Dr. Greg wrote: > > > > The fs.c file contains the implementation of the TSEM control > > > > plane that is in the form of a pseudo-filesystem mounted on the > > > > following directory: > > > > > > > > /sys/fs/tsem > > > > > Why are you using sysfs to mount this? > > We followed the lead of the SMACK and SeLinux LSM's, both of which > > create the mount points for their control plane filesystems in > > /sys/fs. > > > > In addition, as a filesystem, we chose to have tsemfs closely follow > > their design for continuity across the LSM's. So they share similar > > functionality and design, modulo of course, the event description and > > trajectory export files that we will chat about below. > > > > We can't use securityfs, secondary to the fact that it doesn't > > implement pollable files, which are a requirement for trust > > orchestrators based on external Trusted Modeling Agents. > Why not fix securityfs to provide pollable files? Other than that, > why can't you just use securityfs? Now that we have had some additional bandwidth to look at issues after the first round release, it may be more straight forward to implement the pollable files in securityfs than we thought. We will take another run at this and see what is possible without having to meddle with the internals of securityfs proper. As the diffstat for the patch series indicates, we spent considerable time working to implement TSEM without touching anything outside its implementation directory. I think this is something that anyone who has tried to upstream functionality into the mainline kernel would understand the merit of. Paul, I know you are busy, but if you can, weigh in briefly with any thoughts you may have on where we put the control plane so we can optimize everyone's time with the second spin of the patch set. > > > > +static void show_event(struct seq_file *c, struct tsem_event *ep, char *file) > > > > +{ > > > > + seq_printf(c, "event{process=%s, filename=%s, type=%s, task_id=%*phN}", > > > > + ep->comm, file ? file : "none", tsem_names[ep->event], > > > > + WP256_DIGEST_SIZE, ep->task_id); > > > > + seq_printf(c, " COE{uid=%d, euid=%d, suid=%d, gid=%d, egid=%d, sgid=%d, fsuid=%d, fsgid=%d, cap=0x%llx} ", > > > > + ep->COE.uid, ep->COE.euid, ep->COE.suid, ep->COE.gid, > > > > + ep->COE.egid, ep->COE.sgid, ep->COE.fsuid, ep->COE.fsgid, > > > > + ep->COE.capability.value); > > > > +} > > > > > You are printing out a lot of specific data in a specific format > > > that is documented no where that I can see :( > > > > It appears you found the ABI documentation in the second patch that > > was posted in the series, hopefully it was found to be complete, we > > are currently in 13th position out of 494 files with respect to > > quantity of ABI documentation... :-) > > > > For the sake of 'lore': > > > > After applying the patch series, the control plane API is documented in the > > following file: > > > > Documentation/ABI/testing/tsemfs > > > > The TSEM LSM documentation proper will be in the following file: > > > > Documentation/admin-guide/LSM/tsem.rst > > > > Where we currently hold the poll position. > > > > > Also, you are making the same mistake that /proc made decades ago, > > > creating files that must be parsed and can NEVER be changed in the > > > future. Why not make this a one-value-per-file filesystem instead > > > so that you have flexibility going forward? > > > > Personally, I've been working on Linux since late 1991, so we, as a > > team, are well aware, and sensitive, to the issues and discussions > > surrounding the file formatting decisions that were made with the > > /proc filesystem. So we did not make the decision with respect to > > formatted output lightly. > > > > In fact, I presumed that the top two things we would hear about with > > respect to TSEM, would be about the introduction of CAP_TRUST from > > Casey and the format of the event export and execution trajectory > > files from you. > > > > The issue is that the kernel needs to present to trust orchestrators, > > a description of each LSM security hook, in the form of an atomic > > update or transaction. > > > > To help clarify the discussion, here are examples of records that are > > output for a file_open and socket_bind hook event: > > > > pid{1186} event{process=quixote-us, filename=/opt/Quixote/bin/runc, type=file_open, task_id=0000000000000000000000000000000000000000000000000000000000000000} COE{uid=0, euid=0, suid=0, gid=0, egid=0, sgid=0, fsuid=0, fsgid=0, cap=0x3ffffffffff} file{flags=32800, uid=2, gid=2, mode=0100755, name_length=21, name=4829ead93d026770746f9cdebc76cc4d2a27f45db2d3ac436aa6fce4e2640415, s_magic=0xef53, s_id=xvda, s_uuid=feadbeaffeadbeaffeadbeaffeadbeaf, digest=7c1a43eb99fa739056d6554001d450ca1c9c184ca7e2d8a785bd1e5fd53bad8c} > > > > pid{1204} event{process=ncat, filename=none, type=socket_bind, task_id=a5bc0eb22141657132f29e80ca6ea9b211e8443ead1f4b6b766935a14995040f} COE{uid=0, euid=0, suid=0, gid=0, egid=0, sgid=0, fsuid=0, fsgid=0, cap=0x20000420} socket_bind{family=2, port=28695, addr=0} > > > > TSEM is currently modeling 84 LSM hooks. We would certainly be open > > to suggestions as to how we would architect the one-value-per-file > > filesystem model in a manner that would scale, both with respect to > > pseudo-filesystem complexity and system call overhead. > You are creating a new structure-type-api here, why not use a > already-designed protocol instead like varlink if you need userspace > to parse events in an atomic way? Or heck even json would be better > as there are universal userspace tools for that today. As an industry, we are in the middle of a software supply chain security crisis. In a trust orchestrated architecture, the trust orchestrators, and their Sancho TMA implementations, are the most security critical components on the system. Our objective is to keep the supply chain footprint for Quixote as small as possible. To that point: size /usr/local/lib/libyajl.so.2.1.1: text data bss dec hex filename 33333 784 16 34133 8555 /usr/local/lib/libyajl.so.2.1.1 size /u/usr/sources/quixote-1.4/SecurityModel/EventParser.o text data bss dec hex filename 2520 0 0 2520 9d8 /u/usr/sources/quixote-1.4/SecurityModel/EventParser.o If we were to use JSON, we would use yajl, it is probably as light as anything out there. Given that, on face value, this would represent over an order of magnitude increase in code size to achieve the same objective, plus add an external dependency. We can't claim to have dealt with varlink very much, but that actually may make sense, down the road, given that TSEM can be conceptualized as being about advertising resources that a client application needs to provide a security service to the kernel. Hopefully our concerns about software dependency issues make sense, given the context that we wrote, and released, as part of Quixote, a complete SGX Platform Software Implementation in order to avoid exposing the SanchoSGX implementation to the 20+ shared library dependencies that come with the Intel SGX software stack. > Or use the audit interface, this feels very close to that, why not > just tie into that subsystem instead? I'm not sure the audit system is a good fit for all of this, but I will defer to Paul on that, given that he stewards over audit as well as the security sub-system. So, I was doing some thinking, while I was cross-country skiing for about 10 miles across what the weather service is calling the glacial icepack that is currently covering our wheat and soybean fields out here in eastern North Dakota. How about this for an idea, why don't we let the trust orchestrators select the output format for the security event description interfaces? We can start with the format that we are currently using and then add things like varlink or other alternatives down the road. That allows different trust orchestration implementations to select what they feel is the most desirable output format and doesn't lock the kernel into a specific ABI format until we are actually covered by the next ice age. Unless I miss my guess, in 25 years, if we have a reunion of trust orchestration architectects, it will be pretty cheap to buy a round of drinks, even if everyone is drinking 25 year old McAllen. Paul, thoughts, given that if we move this to securityfs it will be completely in your domain? > thanks, > > greg k-h Have a good remainder of the weekend. As always, Dr. Greg The Quixote Project - Flailing at the Travails of Cybersecurity
On Sun, Feb 12, 2023 at 12:54:40AM -0600, Dr. Greg wrote: > On Sat, Feb 11, 2023 at 11:59:19AM +0100, Greg KH wrote: > > Looping in Paul Moore in order to get his thoughts. > > > On Fri, Feb 10, 2023 at 06:18:06PM -0600, Dr. Greg wrote: > > > On Thu, Feb 09, 2023 at 12:30:51PM +0100, Greg KH wrote: > > > > > > Good afternoon Greg, thanks for taking the time to review the patches > > > and pass along comments. > > > > > > > On Fri, Feb 03, 2023 at 11:09:48PM -0600, Dr. Greg wrote: > > > > > The fs.c file contains the implementation of the TSEM control > > > > > plane that is in the form of a pseudo-filesystem mounted on the > > > > > following directory: > > > > > > > > > > /sys/fs/tsem > > > > > > > Why are you using sysfs to mount this? > > > > We followed the lead of the SMACK and SeLinux LSM's, both of which > > > create the mount points for their control plane filesystems in > > > /sys/fs. > > > > > > In addition, as a filesystem, we chose to have tsemfs closely follow > > > their design for continuity across the LSM's. So they share similar > > > functionality and design, modulo of course, the event description and > > > trajectory export files that we will chat about below. > > > > > > We can't use securityfs, secondary to the fact that it doesn't > > > implement pollable files, which are a requirement for trust > > > orchestrators based on external Trusted Modeling Agents. > > > Why not fix securityfs to provide pollable files? Other than that, > > why can't you just use securityfs? > > Now that we have had some additional bandwidth to look at issues after > the first round release, it may be more straight forward to implement > the pollable files in securityfs than we thought. We will take > another run at this and see what is possible without having to meddle > with the internals of securityfs proper. It's ok to mess around with securityfs to get it to work properly for your use case, there's no reason to create a whole new filesystem just because of one missing functionality. > As the diffstat for the patch series indicates, we spent considerable > time working to implement TSEM without touching anything outside its > implementation directory. I think this is something that anyone who > has tried to upstream functionality into the mainline kernel would > understand the merit of. No, that's not how kernel development works, it's ok to touch other portions when needed, otherwise you duplicate lots of extra code and functionality as you are doing here. Please do not do that. > > You are creating a new structure-type-api here, why not use a > > already-designed protocol instead like varlink if you need userspace > > to parse events in an atomic way? Or heck even json would be better > > as there are universal userspace tools for that today. > > As an industry, we are in the middle of a software supply chain > security crisis. That has nothing to do with the kernel, sorry. > In a trust orchestrated architecture, the trust > orchestrators, and their Sancho TMA implementations, are the most > security critical components on the system. Our objective is to keep > the supply chain footprint for Quixote as small as possible. > > To that point: > > size /usr/local/lib/libyajl.so.2.1.1: > text data bss dec hex filename > 33333 784 16 34133 8555 /usr/local/lib/libyajl.so.2.1.1 > > size /u/usr/sources/quixote-1.4/SecurityModel/EventParser.o > text data bss dec hex filename > 2520 0 0 2520 9d8 /u/usr/sources/quixote-1.4/SecurityModel/EventParser.o > > If we were to use JSON, we would use yajl, it is probably as light as > anything out there. Given that, on face value, this would represent > over an order of magnitude increase in code size to achieve the same > objective, plus add an external dependency. So you require people to trust your custom parser and format just because you don't want to rely on a trusted tool that the whole world depends on? Again, not a valid argument, sorry, please use common parsing tools otherwise you are guaranteed to make mistakes and everyone will have to rely on your custom tools only, which is not something that you would accept from any other kernel change. And I don't see a link to the userspace tools anywhere, did I miss it? thanks, greg k-h
On Thu, Feb 16, 2023 at 07:53:28AM +0100, Greg KH wrote: > On Sun, Feb 12, 2023 at 12:54:40AM -0600, Dr. Greg wrote: > > On Sat, Feb 11, 2023 at 11:59:19AM +0100, Greg KH wrote: > > > > Looping in Paul Moore in order to get his thoughts. > > > > > On Fri, Feb 10, 2023 at 06:18:06PM -0600, Dr. Greg wrote: > > > > On Thu, Feb 09, 2023 at 12:30:51PM +0100, Greg KH wrote: > > > > > > > > Good afternoon Greg, thanks for taking the time to review the patches > > > > and pass along comments. > > > > > > > > > On Fri, Feb 03, 2023 at 11:09:48PM -0600, Dr. Greg wrote: > > > > > > The fs.c file contains the implementation of the TSEM control > > > > > > plane that is in the form of a pseudo-filesystem mounted on the > > > > > > following directory: > > > > > > > > > > > > /sys/fs/tsem > > > > > > > > > Why are you using sysfs to mount this? > > > > > > We followed the lead of the SMACK and SeLinux LSM's, both of which > > > > create the mount points for their control plane filesystems in > > > > /sys/fs. > > > > > > > > In addition, as a filesystem, we chose to have tsemfs closely follow > > > > their design for continuity across the LSM's. So they share similar > > > > functionality and design, modulo of course, the event description and > > > > trajectory export files that we will chat about below. > > > > > > > > We can't use securityfs, secondary to the fact that it doesn't > > > > implement pollable files, which are a requirement for trust > > > > orchestrators based on external Trusted Modeling Agents. > > > > > Why not fix securityfs to provide pollable files? Other than that, > > > why can't you just use securityfs? > > > > Now that we have had some additional bandwidth to look at issues after > > the first round release, it may be more straight forward to implement > > the pollable files in securityfs than we thought. We will take > > another run at this and see what is possible without having to meddle > > with the internals of securityfs proper. > It's ok to mess around with securityfs to get it to work properly > for your use case, there's no reason to create a whole new > filesystem just because of one missing functionality. As I noted before, we followed the lead of SMACK and SeLinux, both of which have their filesystems planted in /sys/fs. Paul, FWIW, it may be worthwhile to add something to the LSM authors guidance indicating that, moving forward, any control plane filesystem functionality should be placed in securityfs, in order to avoid wasting both developer and reviewer bandwidth. > > As the diffstat for the patch series indicates, we spent considerable > > time working to implement TSEM without touching anything outside its > > implementation directory. I think this is something that anyone who > > has tried to upstream functionality into the mainline kernel would > > understand the merit of. > No, that's not how kernel development works, it's ok to touch other > portions when needed, otherwise you duplicate lots of extra code and > functionality as you are doing here. Please do not do that. Not the place for it here, but there is an interesting conversation regarding Linux kernel development to be had, regarding the economic barriers to mainstreaming code, that is posed by the 'Serving multiple masters dilemma'. > > > You are creating a new structure-type-api here, why not use a > > > already-designed protocol instead like varlink if you need userspace > > > to parse events in an atomic way? Or heck even json would be better > > > as there are universal userspace tools for that today. > > > > As an industry, we are in the middle of a software supply chain > > security crisis. > That has nothing to do with the kernel, sorry. A quick response to that, just for the historical record. In a trust orchestrated system, the most security critical code running are the TMA's, much more so than a TPM, since the TMA's determine the security state of the kernel. It has thus far seemed wise to us to minimize the security scope and range of the implementations as much as possible. > > In a trust orchestrated architecture, the trust > > orchestrators, and their Sancho TMA implementations, are the most > > security critical components on the system. Our objective is to keep > > the supply chain footprint for Quixote as small as possible. > > > > To that point: > > > > size /usr/local/lib/libyajl.so.2.1.1: > > text data bss dec hex filename > > 33333 784 16 34133 8555 /usr/local/lib/libyajl.so.2.1.1 > > > > size /u/usr/sources/quixote-1.4/SecurityModel/EventParser.o > > text data bss dec hex filename > > 2520 0 0 2520 9d8 /u/usr/sources/quixote-1.4/SecurityModel/EventParser.o > > > > If we were to use JSON, we would use yajl, it is probably as light as > > anything out there. Given that, on face value, this would represent > > over an order of magnitude increase in code size to achieve the same > > objective, plus add an external dependency. > So you require people to trust your custom parser and format just > because you don't want to rely on a trusted tool that the whole > world depends on? We are actually not advocating making the world use our format. In my last reply we had suggested making the output format an option, selected by the trust orchestrator at the time a modeling domain was setup, given that by definition, the only thing that is going to be reading that file is the trust orchestrator that sets it up. That puts the compatibility onus on userspace and would provide a mechanism for the kernel to deprecate formats. Given the history of this industry, JSON may be the rage now for the world's trusted parsing tool, but who knows what the rage is going to be in five years. It wasn't too long ago that XML was thought to be all the rage... :-) > Again, not a valid argument, sorry, please use common parsing tools > otherwise you are guaranteed to make mistakes and everyone will have to > rely on your custom tools only, which is not something that you would > accept from any other kernel change. JSON it will be than, unless the community wants the userspace selection option. Paul, since securityfs is officially something that is in your approval domain, do you have any issues with having pseudo-files in that filesystem urping out JSON? In addition, Paul or anyone else reading this; does anyone have any recommendations or preferences with respect to how the data is marshalled? Unless we hear otherwise we will proceed with the following implementation: - All of the JSON encoded information will be output as a single linefeed delimited record. - The record will be a JSON array with named structures that use the same names as the field description names in the current event description format. - All of the existing key=value pairs will be used in their current documented form but in 'key : value' format. - JSON encoding will be applied only to the trajectory, forensics and event description pseudo-files. All other pseudo-files will continue outputting a single ASCII value. > And I don't see a link to the userspace tools anywhere, did I miss it? The links were in the second to the last paragraph in the cover letter for the patch series and in the TBDHTTRAD section of the TSEM LSM documentation file: Documentation/admin-guide/LSM/tsem.rst The project download directory is as follows: ftp://ftp.enjellic.com/pub/Quixote At the current time, the URL for the source is as follows: ftp://ftp.enjellic.com/pub/Quixote/Quixote-1.4.tar.gz Pre-compiled binaries, that should run on any x86_64 platform, with no dependencies, are available in the following forms: ftp://ftp.enjellic.com/pub/Quixote/Quixote-1.4-1.x86_64.rpm ftp://ftp.enjellic.com/pub/Quixote/quixote_1.4-2_amd64.deb The -1 and -2 differences are meaningless, we missed the -k argument to alien. The code will compile on Ubuntu 22.04 with GCC plus the Cortex R/M ARM-GCC compiler. The trust orchestrators will build with MUSL but will probably require the presence of some MUSL compiled libraries. > thanks, > > greg k-h Have a good weekend. As always, Dr. Greg The Quixote Project - Flailing at the Travails of Cybersecurity
diff --git a/security/tsem/fs.c b/security/tsem/fs.c new file mode 100644 index 000000000000..2898a1cc8c97 --- /dev/null +++ b/security/tsem/fs.c @@ -0,0 +1,894 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (C) 2022 Enjellic Systems Development, LLC + * Author: Dr. Greg Wettstein <greg@enjellic.com> + * + * Implements the sysfs based control plane. + */ + +#include <linux/seq_file.h> +#include <linux/sysfs.h> +#include <linux/fs_context.h> +#include <linux/namei.h> +#include <linux/poll.h> +#include <uapi/linux/magic.h> + +#include "tsem.h" + +static int fs_init_context(struct fs_context *context); +static int fs_get_tree(struct fs_context *context); + +static struct file_system_type fs_definition = { + .name = "tsemfs", + .init_fs_context = fs_init_context, + .kill_sb = kill_litter_super +}; + +static struct fs_context_operations fs_operations = { + .get_tree = fs_get_tree +}; + +static struct vfsmount *fs_mount; + +static int fs_mount_cnt; + +static struct dentry *external_dentry; + +struct control_commands { + char *cmd; + enum tsem_control_type type; +}; + +static struct control_commands commands[9] = { + {"internal", TSEM_CONTROL_INTERNAL}, + {"external", TSEM_CONTROL_EXTERNAL}, + {"enforce", TSEM_CONTROL_ENFORCE}, + {"seal", TSEM_CONTROL_SEAL}, + {"trusted ", TSEM_CONTROL_TRUSTED}, + {"untrusted ", TSEM_CONTROL_UNTRUSTED}, + {"state ", TSEM_CONTROL_MAP_STATE}, + {"pseudonym ", TSEM_CONTROL_MAP_PSEUDONYM}, + {"base ", TSEM_CONTROL_MAP_BASE} +}; + +static bool can_access_fs(void) +{ + struct tsem_TMA_context *ctx = tsem_context(current); + + if (ctx->external) + return false; + if (capable(TSEM_CONTROL_CAPABILITY)) + return true; + if (ctx->sealed) + return false; + return true; +} + +static int control_COE(pid_t pid, unsigned long cmd) +{ + bool wakeup = false; + int retn = -ESRCH; + struct task_struct *COE; + struct tsem_task *task; + + rcu_read_lock(); + COE = find_task_by_vpid(pid); + if (COE != NULL) { + task = tsem_task(COE); + if (cmd == TSEM_CONTROL_UNTRUSTED) + task->trust_status = TSEM_TASK_UNTRUSTED; + if (cmd == TSEM_CONTROL_TRUSTED) { + task->trust_status &= ~TSEM_TASK_TRUST_PENDING; + if (tsem_task_trusted(COE)) + task->trust_status = TSEM_TASK_TRUSTED; + } + + retn = 0; + wakeup = true; + } + rcu_read_unlock(); + + if (wakeup) + wake_up_process(COE); + + return retn; +} + +static int config_context(unsigned long cmd, char *bufr) +{ + int retn = -EINVAL; + unsigned int lp; + struct tsem_TMA_context *ctx = tsem_context(current); + + if (ctx->sealed) + return -EPERM; + + if (cmd == TSEM_CONTROL_SEAL) { + ctx->sealed = true; + retn = 0; + } + + if (cmd == TSEM_CONTROL_ENFORCE) { + for (lp = 0; lp < ARRAY_SIZE(tsem_root_actions); ++lp) + ctx->actions[lp] = TSEM_ACTION_EPERM; + retn = 0; + } + + return retn; +} + +static int config_point(enum tsem_control_type type, u8 *arg) +{ + int retn = -EINVAL; + u8 mapping[WP256_DIGEST_SIZE]; + + if (*++arg == '\0') + goto done; + if (strlen(arg) != sizeof(mapping) * 2) + goto done; + if (hex2bin(mapping, arg, sizeof(mapping))) + goto done; + + if (type == TSEM_CONTROL_MAP_STATE) + retn = tsem_model_load_point(mapping); + else if (type == TSEM_CONTROL_MAP_PSEUDONYM) + retn = tsem_model_load_pseudonym(mapping); + else { + tsem_model_load_base(mapping); + retn = 0; + } + + done: + return retn; +} + +static void show_event(struct seq_file *c, struct tsem_event *ep, char *file) +{ + seq_printf(c, "event{process=%s, filename=%s, type=%s, task_id=%*phN}", + ep->comm, file ? file : "none", tsem_names[ep->event], + WP256_DIGEST_SIZE, ep->task_id); + seq_printf(c, " COE{uid=%d, euid=%d, suid=%d, gid=%d, egid=%d, sgid=%d, fsuid=%d, fsgid=%d, cap=0x%llx} ", + ep->COE.uid, ep->COE.euid, ep->COE.suid, ep->COE.gid, + ep->COE.egid, ep->COE.sgid, ep->COE.fsuid, ep->COE.fsgid, + ep->COE.capability.value); +} + +static void show_file(struct seq_file *c, struct tsem_event *ep) +{ + seq_printf(c, "file{flags=%u, uid=%d, gid=%d, mode=0%o, name_length=%u, name=%*phN, s_magic=0x%0x, s_id=%s, s_uuid=%*phN, digest=%*phN}\n", + ep->file.flags, ep->file.uid, ep->file.gid, ep->file.mode, + ep->file.name_length, WP256_DIGEST_SIZE, ep->file.name, + ep->file.s_magic, ep->file.s_id, + (int) sizeof(ep->file.s_uuid), ep->file.s_uuid, + WP256_DIGEST_SIZE, ep->file.digest); +} + +static void show_mmap(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_mmap_file_args *args = &ep->CELL.mmap_file; + + show_event(c, ep, args->file ? ep->pathname : NULL); + seq_printf(c, "%s{type=%u, reqprot=%u, prot=%u, flags=%u} ", + tsem_names[ep->event], args->file == NULL, + args->reqprot, args->prot, args->flags); + + if (!args->file) + seq_puts(c, "\n"); + else + show_file(c, ep); +} + +static void show_socket_create(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_socket_create_args *args = &ep->CELL.socket_create; + + show_event(c, ep, NULL); + seq_printf(c, "%s{family=%u, type=%u, protocol=%u, kern=%u}\n", + tsem_names[ep->event], args->family, args->type, + args->protocol, args->kern); +} + +static void show_socket(struct seq_file *c, struct tsem_event *ep) +{ + struct sockaddr_in *ipv4; + struct sockaddr_in6 *ipv6; + struct tsem_socket_connect_args *scp = &ep->CELL.socket_connect; + + show_event(c, ep, NULL); + seq_printf(c, "%s{family=%u, ", tsem_names[ep->event], scp->family); + + switch (scp->family) { + case AF_INET: + ipv4 = (struct sockaddr_in *) &scp->u.ipv4; + seq_printf(c, "port=%u, addr=%u}\n", ipv4->sin_port, + ipv4->sin_addr.s_addr); + break; + case AF_INET6: + ipv6 = (struct sockaddr_in6 *) &scp->u.ipv6; + seq_printf(c, "port=%u, flow=%u, scope=%u, addr=%*phN}\n", + ipv6->sin6_port, ipv6->sin6_flowinfo, + ipv6->sin6_scope_id, + (int) sizeof(ipv6->sin6_addr.in6_u.u6_addr8), + ipv6->sin6_addr.in6_u.u6_addr8); + break; + default: + seq_printf(c, "addr=%*phN}\n", WP256_DIGEST_SIZE, + scp->u.mapping); + break; + } +} + +static void show_socket_accept(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_socket_accept_args *sap = &ep->CELL.socket_accept; + + show_event(c, ep, NULL); + seq_printf(c, "%s{family=%u, type=%u, port=%u, addr=", + tsem_names[ep->event], sap->family, sap->type, sap->port); + + switch (sap->family) { + case AF_INET: + seq_printf(c, "%u}\n", sap->ipv4); + break; + case AF_INET6: + seq_printf(c, "%*phN}\n", + (int) sizeof(sap->ipv6.in6_u.u6_addr8), + sap->ipv6.in6_u.u6_addr8); + break; + default: + seq_printf(c, "%*phN}\n", (int) sizeof(sap->tsip->digest), + sap->tsip->digest); + break; + } +} + +static void show_task_kill(struct seq_file *c, struct tsem_event *ep) +{ + struct tsem_task_kill_args *args = &ep->CELL.task_kill; + + show_event(c, ep, NULL); + seq_printf(c, "%s{cross=%u, signal=%u, target=%*phN}\n", + tsem_names[ep->event], args->cross_model, args->signal, + WP256_DIGEST_SIZE, args->target); +} + +static void show_event_generic(struct seq_file *c, struct tsem_event *ep) +{ + show_event(c, ep, NULL); + seq_printf(c, "%s{type=%s}\n", tsem_names[ep->event], + tsem_names[ep->CELL.event_type]); +} + +static void show_trajectory_event(struct seq_file *c, struct tsem_event *ep) +{ + switch (ep->event) { + case TSEM_FILE_OPEN: + show_event(c, ep, ep->pathname); + show_file(c, ep); + break; + case TSEM_MMAP_FILE: + show_mmap(c, ep); + break; + case TSEM_SOCKET_CREATE: + show_socket_create(c, ep); + break; + case TSEM_SOCKET_CONNECT: + case TSEM_SOCKET_BIND: + show_socket(c, ep); + break; + case TSEM_SOCKET_ACCEPT: + show_socket_accept(c, ep); + break; + case TSEM_TASK_KILL: + show_task_kill(c, ep); + break; + case TSEM_GENERIC_EVENT: + show_event_generic(c, ep); + break; + default: + break; + } +} + +static void *trajectory_start(struct seq_file *c, loff_t *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_lock(&model->trajectory_mutex); + return seq_list_start(&model->trajectory_list, *pos); +} + +static void *trajectory_next(struct seq_file *c, void *p, loff_t *pos) +{ + struct tsem_model *model = tsem_model(current); + + return seq_list_next(p, &model->trajectory_list, pos); +} + +static void trajectory_stop(struct seq_file *c, void *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_unlock(&model->trajectory_mutex); +} + +static int trajectory_show(struct seq_file *c, void *trajectory) +{ + struct tsem_trajectory *pt; + struct tsem_event *ep; + + pt = list_entry(trajectory, struct tsem_trajectory, list); + ep = pt->ep; + + show_trajectory_event(c, ep); + + return 0; +} + +static const struct seq_operations trajectory_seqops = { + .start = trajectory_start, + .next = trajectory_next, + .stop = trajectory_stop, + .show = trajectory_show +}; + +static int trajectory_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return seq_open(file, &trajectory_seqops); +} + +static const struct file_operations trajectory_ops = { + .open = trajectory_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void *point_start(struct seq_file *c, loff_t *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_lock(&model->point_mutex); + return seq_list_start(&model->point_list, *pos); +} + +static void *point_next(struct seq_file *c, void *p, loff_t *pos) +{ + struct tsem_model *model = tsem_model(current); + + return seq_list_next(p, &model->point_list, pos); +} + +static void point_stop(struct seq_file *c, void *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_unlock(&model->point_mutex); +} + +static int point_show(struct seq_file *c, void *point) +{ + struct tsem_event_point *id; + + id = list_entry(point, struct tsem_event_point, list); + seq_printf(c, "%*phN\n", WP256_DIGEST_SIZE, id->point); + return 0; +} + +static const struct seq_operations point_seqops = { + .start = point_start, + .next = point_next, + .stop = point_stop, + .show = point_show +}; + +static int point_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return seq_open(file, &point_seqops); +} + +static const struct file_operations point_ops = { + .open = point_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int open_control(struct inode *inode, struct file *filp) +{ + if (!capable(TSEM_CONTROL_CAPABILITY)) + return -EACCES; + if (!(filp->f_flags & O_WRONLY)) + return -EACCES; + return 0; +} + +static ssize_t write_control(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) +{ + char *p, *arg, cmdbufr[76]; + unsigned int lp; + ssize_t retn = -EINVAL; + long pid; + enum tsem_control_type type; + + if (*ppos != 0) + goto done; + if (datalen > sizeof(cmdbufr)-1) + goto done; + + memset(cmdbufr, '\0', sizeof(cmdbufr)); + if (copy_from_user(cmdbufr, buf, datalen)) { + retn = -EFAULT; + goto done; + } + + p = strchr(cmdbufr, '\n'); + if (!p) + goto done; + *p = '\0'; + + arg = strchr(cmdbufr, ' '); + + for (lp = 0; lp < ARRAY_SIZE(commands); ++lp) { + if (!strncmp(cmdbufr, commands[lp].cmd, + strlen(commands[lp].cmd))) { + type = commands[lp].type; + break; + } + } + + switch (type) { + case TSEM_CONTROL_EXTERNAL: + case TSEM_CONTROL_INTERNAL: + retn = tsem_ns_create(type); + break; + case TSEM_CONTROL_ENFORCE: + case TSEM_CONTROL_SEAL: + retn = config_context(type, cmdbufr); + break; + case TSEM_CONTROL_TRUSTED: + case TSEM_CONTROL_UNTRUSTED: + p = strchr(cmdbufr, ' '); + if (!p) + goto done; + *p++ = '\0'; + if (kstrtol(p, 0, &pid)) + goto done; + retn = control_COE(pid, type); + break; + case TSEM_CONTROL_MAP_STATE: + case TSEM_CONTROL_MAP_PSEUDONYM: + case TSEM_CONTROL_MAP_BASE: + retn = config_point(type, arg); + break; + } + +done: + if (!retn) + retn = datalen; + return retn; +} + +static int release_control(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations control_ops = { + .open = open_control, + .write = write_control, + .release = release_control, + .llseek = generic_file_llseek, +}; + +static void *forensics_start(struct seq_file *c, loff_t *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_lock(&model->forensics_mutex); + return seq_list_start(&model->forensics_list, *pos); +} + +static void *forensics_next(struct seq_file *c, void *p, loff_t *pos) +{ + struct tsem_model *model = tsem_model(current); + + return seq_list_next(p, &model->forensics_list, pos); +} + +static void forensics_stop(struct seq_file *c, void *pos) +{ + struct tsem_model *model = tsem_model(current); + + mutex_unlock(&model->forensics_mutex); +} + +static int forensics_show(struct seq_file *c, void *event) +{ + struct tsem_trajectory *pt; + struct tsem_event *ep; + + pt = list_entry(event, struct tsem_trajectory, list); + ep = pt->ep; + + show_trajectory_event(c, ep); + + return 0; +} + +static const struct seq_operations forensics_seqops = { + .start = forensics_start, + .next = forensics_next, + .stop = forensics_stop, + .show = forensics_show +}; + +static int forensics_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return seq_open(file, &forensics_seqops); +} + +static const struct file_operations forensics_ops = { + .open = forensics_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int measurement_show(struct seq_file *c, void *event) +{ + struct tsem_model *model = tsem_model(current); + + seq_printf(c, "%*phN\n", (int) sizeof(model->measurement), + model->measurement); + return 0; +} + +static int measurement_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return single_open(file, &measurement_show, NULL); +} + +static const struct file_operations measurement_ops = { + .open = measurement_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int id_show(struct seq_file *c, void *event) +{ + seq_printf(c, "%llu\n", tsem_context(current)->id); + return 0; +} + +static int id_open(struct inode *inode, struct file *file) +{ + struct tsem_TMA_context *ctx = tsem_context(current); + + if (ctx->sealed) + return -EACCES; + return single_open(file, &id_show, NULL); +} + +static const struct file_operations id_ops = { + .open = id_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int state_show(struct seq_file *m, void *v) +{ + struct tsem_model *model = tsem_model(current); + + tsem_model_compute_state(); + seq_printf(m, "%*phN\n", WP256_DIGEST_SIZE, model->state); + return 0; +} + +static int state_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return single_open(file, &state_show, NULL); +} + +static const struct file_operations state_ops = { + .open = state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int aggregate_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%*phN\n", WP256_DIGEST_SIZE, tsem_trust_aggregate()); + return 0; +} + +static int aggregate_open(struct inode *inode, struct file *file) +{ + if (!can_access_fs()) + return -EACCES; + return single_open(file, &aggregate_show, NULL); +} + +static const struct file_operations aggregate_ops = { + .open = aggregate_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int export_show(struct seq_file *m, void *v) +{ + return tsem_export_show(m); +} + +static __poll_t export_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tsem_TMA_context *ctx = tsem_context(current); + + if (!ctx->external) + return -ENOENT; + + poll_wait(file, &ctx->external->wq, wait); + + if (ctx->external->have_event) { + ctx->external->have_event = false; + return EPOLLIN | EPOLLRDNORM; + } + return 0; +} + +static int export_open(struct inode *inode, struct file *file) +{ + if (!capable(TSEM_CONTROL_CAPABILITY)) + return -EACCES; + return single_open(file, &export_show, NULL); +} + +static const struct file_operations export_ops = { + .open = export_open, + .poll = export_poll, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int fs_fill(struct super_block *sb, struct fs_context *fc) +{ + int retn; + + static const struct tree_descr root_files[] = { + [2] = {"control", &control_ops, 0200}, + [3] = {"id", &id_ops, 0400}, + [4] = {"trajectory", &trajectory_ops, 0400}, + [5] = {"forensics", &forensics_ops, 0400}, + [6] = {"points", &point_ops, 0400}, + [7] = {"measurement", &measurement_ops, 0400}, + [8] = {"state", &state_ops, 0400}, + [9] = {"aggregate", &aggregate_ops, 0400}, + {""} + }; + + retn = simple_fill_super(sb, TSEMFS_MAGIC, root_files); + if (retn) + pr_warn("Unable to create TSEM root filesystem.\n"); + + return retn; +} + +static int fs_init_context(struct fs_context *fc) +{ + fc->ops = &fs_operations; + return 0; +} + +static int fs_get_tree(struct fs_context *fc) +{ + return get_tree_single(fc, fs_fill); +} + +static int create_update_directory(void) +{ + int retn = 0; + struct dentry *root, *dentry; + struct inode *root_dir, *inode; + static const char *name = "ExternalTMA"; + + root = fs_mount->mnt_root; + root_dir = d_inode(root); + + inode_lock(root_dir); + dentry = lookup_one_len(name, root, strlen(name)); + if (IS_ERR(dentry)) { + retn = PTR_ERR(dentry); + goto done; + } + + if (d_really_is_positive(dentry)) { + retn = -EEXIST; + goto done_dentry; + } + + inode = new_inode(root_dir->i_sb); + if (!inode) { + retn = -ENOMEM; + goto done_dentry; + } + + inode->i_ino = get_next_ino(); + inode->i_mode = 0755 | S_IFDIR; + inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); + inode->i_private = NULL; + inode->i_fop = &export_ops; + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + inc_nlink(inode); + inc_nlink(root_dir); + + d_instantiate(dentry, inode); + dget(dentry); + external_dentry = dentry; + inode_unlock(root_dir); + return 0; + + done_dentry: + dput(dentry); + + done: + inode_unlock(root_dir); + return retn; +} + +/** + * tesm_fs_init() - Initialize the TSEM control filesystem. + * + * This function is called as part of the TSEM LSM initialization + * process. It creates the /sys/fs/tsem mount point and populates + * the filesystem to be mounted there with the control plane file and + * internal TMA model information files. + * + * Return: If filesystem initialization is successful a return code of 0 + * is returned. A negative return value is returned if an error + * is encoutnered. + */ +int __init tsem_fs_init(void) +{ + int retn; + + retn = sysfs_create_mount_point(fs_kobj, "tsem"); + if (retn) { + pr_warn("Unable to create TSEM filesystem mount point.\n"); + return retn; + } + + retn = register_filesystem(&fs_definition); + if (retn) { + pr_warn("Unable to register TSEM filesystem.\n"); + goto done; + } + + fs_mount = kern_mount(&fs_definition); + if (IS_ERR(fs_mount)) { + pr_warn("Unable to mount TSEM filesystem.\n"); + retn = PTR_ERR(fs_mount); + fs_mount = NULL; + } + + retn = create_update_directory(); + + done: + if (retn) + sysfs_remove_mount_point(fs_kobj, "tsem"); + return retn; +} + +/** + * tesm_fs_create_external() - Create an external TMA update file. + * @id: A pointer to the ASCII representation of the modeling domain + * that the export file is being created for. + * + * This function is used to create a pseudo-file that will output security + * event descriptions for a namespace. This routine will create the + * following file: + * + * /sys/fs/tsem/ExternalTMA/N + * + * Where N is replaced with the security model context identifier. + * + * Return: If creation of the update file is successful a pointer to the + * dentry of the file is returned. If an error was encountered + * an error code is encoded in the pointer. + */ +struct dentry *tsem_fs_create_external(const char *name) +{ + int retn = 0; + struct dentry *dentry; + struct inode *root_dir, *inode; + + retn = simple_pin_fs(&fs_definition, &fs_mount, &fs_mount_cnt); + if (retn) + return ERR_PTR(retn); + + root_dir = d_inode(external_dentry); + inode_lock(root_dir); + + dentry = lookup_one_len(name, external_dentry, strlen(name)); + if (IS_ERR(dentry)) { + retn = PTR_ERR(dentry); + goto done; + } + if (d_really_is_positive(dentry)) { + WARN_ON_ONCE(1); + retn = -EEXIST; + goto done_dentry; + } + + inode = new_inode(root_dir->i_sb); + if (!inode) { + retn = -ENOMEM; + goto done_dentry; + } + + inode->i_ino = get_next_ino(); + inode->i_mode = 0400 | S_IFREG; + inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); + inode->i_private = NULL; + inode->i_fop = &export_ops; + + d_instantiate(dentry, inode); + dget(dentry); + inode_unlock(root_dir); + return dentry; + + done_dentry: + dput(dentry); + + done: + inode_unlock(root_dir); + simple_release_fs(&fs_mount, &fs_mount_cnt); + if (retn) + dentry = ERR_PTR(retn); + return dentry; +} + +/** + * tesm_fs_remove_external() - Remove an external modeling update file. + * @dentry: A pointer to the dentry of the file to be removed. + * + * This function is used to remove the update file for an externally + * modeled security domain. + */ +void tsem_fs_remove_external(struct dentry *dentry) +{ + struct inode *root_dir; + + if (!dentry || IS_ERR(dentry)) { + WARN_ON_ONCE(1); + return; + } + + root_dir = d_inode(dentry->d_parent); + + inode_lock(root_dir); + if (simple_positive(dentry)) { + simple_unlink(root_dir, dentry); + dput(dentry); + } + inode_unlock(root_dir); + + simple_release_fs(&fs_mount, &fs_mount_cnt); +}
The fs.c file contains the implementation of the TSEM control plane that is in the form of a pseudo-filesystem mounted on the following directory: /sys/fs/tsem The following file documents the interface provided by the control plane: Documentation/ABI/testing/tsemfs The pseudo-files act on the modeling context of the process that is acting on the file. For example, reading the 'id' pseudo-file, returns the modeling domain identifier that the process is running in. The ExternalTMA directory is used to segreate the pseudo-files that are created in order to surface security event descriptions to an external trust orchestrator. The files in this directory appear as the numeric value of the modeling domain they were created for. The 'control' pseudo-file is the only writable file in the plane and is used to control the TSEM implementation. The most important and primary roles are to create namespaces and set the trust status of a process modeled by an external TMA. Signed-off-by: Greg Wettstein <greg@enjellic.com> --- security/tsem/fs.c | 894 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 894 insertions(+) create mode 100644 security/tsem/fs.c