diff mbox series

[08/14] Implement TSEM control plane.

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

Commit Message

Dr. Greg Feb. 4, 2023, 5:09 a.m. UTC
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

Comments

Greg Kroah-Hartman Feb. 9, 2023, 11:30 a.m. UTC | #1
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
Dr. Greg Feb. 11, 2023, 12:18 a.m. UTC | #2
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
Greg Kroah-Hartman Feb. 11, 2023, 10:59 a.m. UTC | #3
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
Dr. Greg Feb. 12, 2023, 6:54 a.m. UTC | #4
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
Greg Kroah-Hartman Feb. 16, 2023, 6:53 a.m. UTC | #5
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
Dr. Greg Feb. 18, 2023, 6:03 p.m. UTC | #6
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 mbox series

Patch

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);
+}