Good morning, I hope this note finds the week starting well for
everyone.
This is the second release of a patch series that implements
Trusted Security Event Modeling (TSEM) as a new Linux Security
Module (LSM) architecture.
TSEM provides kernel infrastructure for implementing security
controls based on either deterministic or machine learning
models. It also provides a framework for implementing Host Based
Intrusion Detection (HIDS) and/or anomaly detection architectures
without the need to write kernel code or implement kernel
modules.
TSEM is designed to support the concept of a Trust Orchestration
System (TOS). Trust orchestration involves the process of
modeling the security behavior of a workload, or a platform at
large, and defining whether or not a process is to be trusted,
based on whether or not the security events produced are
consistent with a security model that has been unit tested for a
workload or platform.
TSEM operates under the premise, that security, like all other
physical phenomena in science and engineering, can be
mathematically modeled. TSEM notionally treats the LSM security
event hooks as a basis set of parameters that are capable of
generating a functional value for the security state of a system.
TSEM, in and of itself, does not implement security policy. That
capability is invested in an entity known as a Trusted Modeling
Agent (TMA). A TMA can be implemented in the kernel itself or
the security event descriptions can be exported to userspace for
processing in a non-kernel based TMA.
To support this architecture, TSEM implements entirely within the
context of the LSM architecture, the concept of security modeling
namespaces that are akin to other resource namespaces. A
security modeling namespace is paired with a TMA that implements
the root of trust for a security modeling namespace.
A TMA implementation uses the characteristics of the calling
process and the descriptive parameters of a security event to
compute coefficients for each security event that occurs in a
security modeling namespace. The sum of these coefficients,
represents the security 'state' of the model.
TSEM supports any number of security modeling namespaces that act
independently of one another and of the root security modeling
namespace. Each namespace can be configured with its own unique
security model definition that can be configured to be modeled
internally or externally. The cryptographic hash function used
to generate the security state coefficients can be configured at
the namespace level as can the source of resolution for UID/GID
values.
Security models to be enforced by a trust orchestrator in a
security modeling namespace are developed by unit testing of a
workload. These security models are designed to be distributed
as a manifest that defines the desired security behavior of a
workload.
TSEM represents a security architecture that is designed to be
driven by modern software development strategies that embrace
resource containerization and continuous integration and delivery
principles. The objective of TSEM, along with the Quixote TOS
implementation, is to bring to Linux security architecture what
Docker brought to Linux resource namespaces.
Included in the patch series is an extensive documentation file that
can be found in the following location after the series is applied:
Documentation/admin-guide/LSM/tsem.rst
Reviewers, and others who are interested, are referred to this
document for a more extensive discussion into the rationale,
design and implementation of the TSEM architecture.
Control of TSEM is surfaced entirely in the securityfs filesystem
through the following directory heirarchy:
/sys/kernel/security/tsem
TSEM is designed to be largely self-contained and independent of
the kernel at large and with other LSM's with which it stacks.
It operates without the need for filesystem labeling or
cryptographic integrity protection of filesystem metadata.
The TSEM in-kernel TMA implements a very simple deterministic
security model. Moving forward, the TSEM architecture is
designed to provide a flexible framework to support the
implementation of more advanced models that use stochastics,
inference and generative machine learning.
Version 1.5 of the Quixote userspace utilities that support this
TSEM kernel release can be obtained from the following URL:
ftp://ftp.enjellic.com/pub/Quixote
In addition to source there is a binary utility package that has
been compiled and statically linked with MUSL libc that should
install and run, without dependencies, on any TSEM enabled
kernel.
Included are implementations of trust orchestrator's for the
following TMA trust roots:
Kernel.
Userspace process.
SGX enclave.
Xen hypervisor stub-domain.
Hardware based security coprocessors.
Among the objectives of Quixote/TSEM is to provide a framework
for developing next generation hardware assisted security
co-processor technology that extends beyond what is currently
represented in the form of Trusted Platform Modules.
Included with the Quixote TOS is an implementation of a security
co-processor based on the Nordic NRF52840-DONGLE
micro-controller. This is a 32-bit ARM based USB form factor
device that is currently being used for projects such as GOOGLE's
OpenSK security key initiative and 3mdeb's Fobnail attestation
server project.
As always,
Dr. Greg
The Quixote Project - Flailing at the Travails of Cybersecurity
V1:
- Initial release.
V2:
- V1: https://lore.kernel.org/linux-security-module/[email protected]/T/#t
- Allow compile time configuration of the Platform Configuration
Register used to extend security coefficients in the root
security modeling namespace.
- Allow both internal and external modeling of security events
that are called in atomic context.
- Use JSON to encode security event characteristics.
- Use securityfs for TSEM control plane rooted at
/sys/kernel/security/tsem.
- Use framework of separate directories for internal TMA's to
protect ABI compatability.
- Use key=value arguments for control plane commands.
- Allow cryptographic hash function used for coefficient
generation to be configured on a namespace by namespace basis.
- Allow selection of initial or current user namespace as the
reference for UID/GID resolution to be configured on a namespace
by namespace basis.
- Allow the size of modeling and export structures to be configured
on a namespace by namespace basis.
- Extensively document all globally visible enumerations and structures.
- Use CAP_ML rather than CAP_TRUST for modeling capability bit.
- Implement orchestrator<->process mutual authentication.
- Implement occupancy counts for security state coefficients.
- Move TSEM to the first LSM in the LSM list.
Dr. Greg (13):
Update MAINTAINERS file.
Add TSEM specific documentation.
Implement CAP_TRUST capability.
Add TSEM master header file.
Add primary TSEM implementation file.
Add root domain trust implementation.
Implement TSEM control plane.
Add namespace implementation.
Add security event description export facility.
Add event description implementation.
Implement security event mapping.
Implement an internal Trusted Modeling Agent.
Activate the configuration and build of the TSEM LSM.
Documentation/ABI/testing/tsem | 828 +++++++
Documentation/admin-guide/LSM/index.rst | 1 +
Documentation/admin-guide/LSM/tsem.rst | 1526 +++++++++++++
.../admin-guide/kernel-parameters.txt | 18 +
MAINTAINERS | 8 +
include/uapi/linux/capability.h | 6 +-
security/Kconfig | 11 +-
security/Makefile | 1 +
security/selinux/include/classmap.h | 2 +-
security/tsem/Kconfig | 36 +
security/tsem/Makefile | 2 +
security/tsem/event.c | 669 ++++++
security/tsem/export.c | 394 ++++
security/tsem/fs.c | 1336 +++++++++++
security/tsem/map.c | 531 +++++
security/tsem/model.c | 714 ++++++
security/tsem/namespace.c | 347 +++
security/tsem/trust.c | 220 ++
security/tsem/tsem.c | 1987 +++++++++++++++++
security/tsem/tsem.h | 1516 +++++++++++++
20 files changed, 10146 insertions(+), 7 deletions(-)
create mode 100644 Documentation/ABI/testing/tsem
create mode 100644 Documentation/admin-guide/LSM/tsem.rst
create mode 100644 security/tsem/Kconfig
create mode 100644 security/tsem/Makefile
create mode 100644 security/tsem/event.c
create mode 100644 security/tsem/export.c
create mode 100644 security/tsem/fs.c
create mode 100644 security/tsem/map.c
create mode 100644 security/tsem/model.c
create mode 100644 security/tsem/namespace.c
create mode 100644 security/tsem/trust.c
create mode 100644 security/tsem/tsem.c
create mode 100644 security/tsem/tsem.h
--
2.39.1
Add an entry to the MAINTAINERS file to document the maintainer's
address and files relevant to the Trusted Security Event Modeling
system (TSEM).
Signed-off-by: Greg Wettstein <[email protected]>
---
MAINTAINERS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 35e19594640d..4660c972d5e3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19026,6 +19026,14 @@ F: include/uapi/linux/selinux_netlink.h
F: scripts/selinux/
F: security/selinux/
+TSEM SECURITY MODULE
+M: Greg Wettstein <[email protected]>
+S: Maintained
+L: [email protected]
+F: Documentation/admin-guide/LSM/tsem.rst
+F: Documentation/ABI/testing/tsemfs
+F: security/tsem/
+
SENSABLE PHANTOM
M: Jiri Slaby <[email protected]>
S: Maintained
--
2.39.1
The event.c file implements support for packaging the description
of an event into its Context Of Execution (COE) and CELL
identities for subsequent modeling by an internal Trusted
Modeling Agent (TMA) or export to a an external trust
orchestrator and TMA.
The tsem_event_allocate() function is called by every security
event handler that determines that an event is to be modeled,
either generically or explicitly. For externally modeled
security modeling namespaces the event description is released
after the export of the event is completed.
For internally modeled domains the event description is retained
in order to support retention and surfacing of the security event
descriptions until the security modeling namespace is terminated.
The event description structures are allocated from a TSEM
event description cache named 'tsem_event_cache'. This cache is
created by an initialization function exported from this file
that is called as part of the TSEM LSM initialization process.
In the case of a security event that acts on a file, ie. is
called with a 'struct file' pointer, one of the components of the
CELL value is a digest of the contents of the file. This file
uses the integrity_kernel_read() function supplied by the
integrity infrastructure to compute the file digest value using
the cryptographic hash function that has been selected for use by
the security modeling namespace.
In a manner similar to the Integrity Measurement Architecture the
file digest processing functionality needs to temporarily alter
the file mode characteristics if the file is not readable. The
characteristics are returned to their normal file after reading
of the digest is complete.
The TSEM LSM uses the LSM 'blob' infrastructure to allocate a
TSEM specific inode structure when an inode is allocated. The
digest value for the value is stored in this structure in order
to eliminate subsequent re-computation of the digest value if the
file has not changed.
The inode 'iversion' value is used to detect changes to an inode
in order to trigger the re-computation of the digest value if the
file has changed.
One of the subtle issues that needs to be addressed is to handle
re-entrancy of the file_open security event hook that is caused
by the integrity_kernel_read() function opening the file. The
TSEM specific inode structure contains a member that is used to
indicate whether or not a digest is being computed for a file.
The tsem_file_open() event handler checks for the presence of
this flag and allows permission for the open if this flag is
detected.
For IPV6 and IPV6 sockets relevant socket information is
collected to be used in the CELL computation.
For a UNIX domain socket (AF_UNIX) the digest of the pathname for
the socket is used for the CELL value.
Other socket types are generically modeled by computing the
digest of the address field supplied when the socket was created
or bound.
Signed-off-by: Greg Wettstein <[email protected]>
---
security/tsem/event.c | 669 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 669 insertions(+)
create mode 100644 security/tsem/event.c
diff --git a/security/tsem/event.c b/security/tsem/event.c
new file mode 100644
index 000000000000..2e01702817ca
--- /dev/null
+++ b/security/tsem/event.c
@@ -0,0 +1,669 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (C) 2023 Enjellic Systems Development, LLC
+ * Author: Dr. Greg Wettstein <[email protected]>
+ *
+ * This file manages the data structures used to define a security event.
+ */
+
+#include <linux/iversion.h>
+#include <linux/user_namespace.h>
+
+#include "tsem.h"
+#include "../integrity/integrity.h"
+
+static struct kmem_cache *event_cachep;
+
+static void refill_event_magazine(struct work_struct *work)
+{
+ unsigned int index;
+ struct tsem_event *ep;
+ struct tsem_work *ws;
+
+ ws = container_of(work, struct tsem_work, work);
+
+ ep = kmem_cache_zalloc(event_cachep, GFP_KERNEL);
+ if (!ep) {
+ pr_warn("tsem: Cannot refill event magazine.\n");
+ return;
+ }
+
+ spin_lock(&ws->u.ctx->magazine_lock);
+ ws->u.ctx->magazine[ws->index] = ep;
+ clear_bit(ws->index, ws->u.ctx->magazine_index);
+
+ /*
+ * The following memory barrier is used to cause the magazine
+ * index to be visible after the refill of the cache slot.
+ */
+ smp_mb__after_atomic();
+ spin_unlock(&ws->u.ctx->magazine_lock);
+
+ if (index >= ws->u.ctx->magazine_size) {
+ kmem_cache_free(event_cachep, ep);
+ WARN_ONCE(true, "Refilling event magazine with no slots.\n");
+ }
+}
+
+static void get_COE(struct tsem_COE *COE)
+
+{
+ struct user_namespace *ns;
+
+ if (tsem_context(current)->use_current_ns)
+ ns = current_user_ns();
+ else
+ ns = &init_user_ns;
+
+ COE->uid = from_kuid(ns, current_uid());
+ COE->euid = from_kuid(ns, current_euid());
+ COE->suid = from_kuid(ns, current_suid());
+
+ COE->gid = from_kgid(ns, current_gid());
+ COE->egid = from_kgid(ns, current_egid());
+ COE->sgid = from_kgid(ns, current_sgid());
+
+ COE->fsuid = from_kuid(ns, current_fsuid());
+ COE->fsgid = from_kgid(ns, current_fsgid());
+
+ COE->capeff.mask = current_cred()->cap_effective;
+}
+
+static char *get_path(struct file *file)
+{
+ int retn = 0;
+ const char *pathname = NULL;
+ char *path, *pathbuffer = NULL;
+
+ pathbuffer = __getname();
+ if (pathbuffer) {
+ pathname = d_absolute_path(&file->f_path, pathbuffer,
+ PATH_MAX);
+ if (IS_ERR(pathname)) {
+ __putname(pathbuffer);
+ pathbuffer = NULL;
+ pathname = NULL;
+ }
+ }
+
+ if (pathname)
+ path = kstrdup(pathname, GFP_KERNEL);
+ else
+ path = kstrdup(file->f_path.dentry->d_name.name, GFP_KERNEL);
+ if (!path)
+ retn = -ENOMEM;
+
+ if (pathbuffer)
+ __putname(pathbuffer);
+ if (retn)
+ path = ERR_PTR(retn);
+ return path;
+}
+
+static int add_file_name(struct tsem_event *ep)
+{
+ int retn;
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ shash->tfm = tsem_digest();
+ retn = crypto_shash_init(shash);
+ if (retn)
+ goto done;
+
+ ep->file.name_length = strlen(ep->pathname);
+ retn = crypto_shash_finup(shash, ep->pathname, ep->file.name_length,
+ ep->file.name);
+
+ done:
+ return retn;
+}
+
+static struct tsem_inode_digest *find_digest(struct tsem_inode *tsip)
+{
+ struct tsem_inode_digest *digest;
+
+ list_for_each_entry(digest, &tsip->digest_list, list) {
+ if (!strcmp(digest->name, tsem_context(current)->digestname))
+ return digest;
+ }
+
+ return NULL;
+}
+
+static struct tsem_inode_digest *add_digest(struct tsem_context *ctx,
+ struct tsem_inode *tsip)
+{
+ struct tsem_inode_digest *digest;
+
+ digest = kzalloc(sizeof(*digest), GFP_KERNEL);
+ if (!digest)
+ return NULL;
+
+ digest->name = kstrdup(tsem_context(current)->digestname, GFP_KERNEL);
+ if (!digest->name)
+ return NULL;
+
+ list_add(&digest->list, &tsip->digest_list);
+
+ return digest;
+}
+
+static struct file *open_event_file(struct file *file, unsigned int *status)
+{
+ int flags;
+ struct file *alt_file;
+
+ if (!(file->f_mode & FMODE_CAN_READ)) {
+ file->f_mode |= FMODE_CAN_READ;
+ *status |= 4;
+ }
+ if (file->f_mode & FMODE_READ)
+ return file;
+
+ flags = file->f_flags & ~(O_WRONLY | O_APPEND | O_TRUNC | O_CREAT |
+ O_NOCTTY | O_EXCL);
+ flags |= O_RDONLY;
+
+ alt_file = dentry_open(&file->f_path, flags, file->f_cred);
+ if (!IS_ERR(alt_file)) {
+ *status |= 1;
+ return alt_file;
+ }
+
+ file->f_flags |= FMODE_READ;
+ *status |= 2;
+ return file;
+}
+
+static int get_file_digest(struct file *file, struct inode *inode,
+ loff_t size, u8 *digest)
+{
+ u8 *bufr;
+ int retn = 0, rsize;
+ unsigned int open_status = 0;
+ loff_t posn = 0;
+ struct file *read_file;
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ shash->tfm = tsem_digest();
+ retn = crypto_shash_init(shash);
+ if (retn)
+ goto done;
+
+ bufr = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!bufr) {
+ retn = -ENOMEM;
+ goto done;
+ }
+
+ if (!likely(file->f_op->read || file->f_op->read_iter)) {
+ retn = -EINVAL;
+ goto done;
+ }
+ read_file = open_event_file(file, &open_status);
+
+ while (posn < size) {
+ rsize = integrity_kernel_read(read_file, posn, bufr, 4096);
+ if (rsize < 0) {
+ retn = rsize;
+ break;
+ }
+ if (rsize == 0)
+ break;
+
+ posn += rsize;
+ retn = crypto_shash_update(shash, bufr, rsize);
+ if (retn)
+ break;
+ }
+
+ kfree(bufr);
+ if (!retn)
+ retn = crypto_shash_final(shash, digest);
+
+ done:
+ if (open_status & 1)
+ fput(read_file);
+ if (open_status & 2)
+ file->f_flags &= ~FMODE_READ;
+ if (open_status & 4)
+ file->f_flags &= ~FMODE_CAN_READ;
+ return retn;
+}
+
+static int add_file_digest(struct file *file, struct tsem_file *tfp)
+{
+ int retn = 0;
+ u8 measurement[HASH_MAX_DIGESTSIZE];
+ loff_t size;
+ struct inode *inode;
+ struct tsem_inode *tsip;
+ struct tsem_inode_digest *digest;
+ struct tsem_context *ctx = tsem_context(current);
+
+ inode = file_inode(file);
+ tsip = tsem_inode(inode);
+
+ mutex_lock(&tsip->mutex);
+ if (!ctx->external) {
+ retn = tsem_model_has_pseudonym(tsip, tfp);
+ if (retn < 0)
+ goto done;
+ if (retn) {
+ memcpy(tfp->digest, ctx->zero_digest,
+ tsem_digestsize());
+ retn = 0;
+ goto done;
+ }
+ }
+
+ size = i_size_read(inode);
+ if (!size) {
+ memcpy(tfp->digest, ctx->zero_digest, tsem_digestsize());
+ goto done;
+ }
+
+ digest = find_digest(tsip);
+
+ if (digest && inode_eq_iversion(inode, digest->version) &&
+ tsip->status == TSEM_INODE_COLLECTED) {
+ memcpy(tfp->digest, digest->value, tsem_digestsize());
+ goto done;
+ }
+
+ tsip->status = TSEM_INODE_COLLECTING;
+ retn = get_file_digest(file, inode, size, measurement);
+ if (retn) {
+ tsip->status = 0;
+ goto done;
+ }
+
+ if (!digest) {
+ digest = add_digest(ctx, tsip);
+ if (!digest) {
+ retn = -ENOMEM;
+ goto done;
+ }
+ }
+
+ memcpy(tfp->digest, measurement, tsem_digestsize());
+ memcpy(digest->value, measurement, tsem_digestsize());
+ digest->version = inode_query_iversion(inode);
+ tsip->status = TSEM_INODE_COLLECTED;
+
+ done:
+ mutex_unlock(&tsip->mutex);
+ return retn;
+}
+
+static int get_file_cell(struct file *file, struct tsem_event *ep)
+{
+ int retn = 1;
+ struct inode *inode;
+ struct user_namespace *ns;
+
+ inode = file_inode(file);
+ inode_lock(inode);
+
+ ep->pathname = get_path(file);
+ if (IS_ERR(ep->pathname)) {
+ retn = PTR_ERR(ep->pathname);
+ goto done;
+ }
+
+ retn = add_file_name(ep);
+ if (retn)
+ goto done;
+
+ retn = add_file_digest(file, &ep->file);
+ if (retn)
+ goto done;
+
+ if (tsem_context(current)->use_current_ns)
+ ns = current_user_ns();
+ else
+ ns = &init_user_ns;
+
+ ep->file.flags = file->f_flags;
+
+ ep->file.uid = from_kuid(ns, inode->i_uid);
+ ep->file.gid = from_kgid(ns, inode->i_gid);
+ ep->file.mode = inode->i_mode;
+ ep->file.s_magic = inode->i_sb->s_magic;
+ memcpy(ep->file.s_id, inode->i_sb->s_id, sizeof(ep->file.s_id));
+ memcpy(ep->file.s_uuid, inode->i_sb->s_uuid.b,
+ sizeof(ep->file.s_uuid));
+
+ done:
+ inode_unlock(inode);
+ return retn;
+}
+
+static int get_socket_accept(struct tsem_event *ep)
+{
+ char *p, path[UNIX_PATH_MAX + 1];
+ int size, retn = 0;
+ struct tsem_socket_accept_args *sap = &ep->CELL.socket_accept;
+
+ if (sap->family == AF_INET || sap->family == AF_INET6)
+ return retn;
+
+ if (sap->family != AF_UNIX) {
+ memcpy(sap->u.mapping, tsem_context(current)->zero_digest,
+ tsem_digestsize());
+ return retn;
+ }
+
+ memset(path, '\0', sizeof(path));
+ p = sap->u.af_unix->addr->name->sun_path;
+ size = sap->u.af_unix->addr->len -
+ offsetof(struct sockaddr_un, sun_path);
+ strncpy(path, p, size);
+ memcpy(sap->u.path, path, sizeof(sap->u.path));
+
+ return retn;
+}
+
+static int get_socket_connect(struct tsem_socket_connect_args *scp)
+{
+ u8 *p;
+ int retn, size;
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ shash->tfm = tsem_digest();
+ retn = crypto_shash_init(shash);
+ if (retn)
+ goto done;
+
+ p = (u8 *) scp->addr->sa_data;
+ size = scp->addr_len - offsetof(struct sockaddr, sa_data);
+ retn = crypto_shash_digest(shash, p, size, scp->u.mapping);
+
+ done:
+ return retn;
+}
+
+static int get_socket_cell(struct tsem_event *ep)
+
+{
+ int size, retn = 0;
+ struct tsem_socket_connect_args *scp = &ep->CELL.socket_connect;
+
+ scp->family = scp->addr->sa_family;
+
+ switch (scp->family) {
+ case AF_INET:
+ memcpy(&scp->u.ipv4, scp->addr, sizeof(scp->u.ipv4));
+ break;
+ case AF_INET6:
+ memcpy(&scp->u.ipv6, scp->addr, sizeof(scp->u.ipv6));
+ break;
+ case AF_UNIX:
+ memset(scp->u.path, '\0', sizeof(scp->u.path));
+ size = scp->addr_len - offsetof(struct sockaddr_un, sun_path);
+ strncpy(scp->u.path, scp->addr->sa_data, size);
+ break;
+ default:
+ retn = get_socket_connect(scp);
+ break;
+ }
+
+ return retn;
+}
+
+/**
+ * tsem_event_init() - Initialize a security event description structure.
+ * @event: The security event number for which the structure is being
+ * initialized.
+ * @params: A pointer to the aggregation structure used to hold the
+ * parameters that describe the function.
+ * @locked: A boolean flag used to indicate if the event to be
+ * initialized is running in atomic context.
+ *
+ * This function is responsible for allocating and initializing the
+ * primary tsem_event structure and populating it based on the event type.
+ *
+ * Return: This function returns a pointer to the allocated structure which
+ * on failure will have an error return code embedded in it.
+ */
+struct tsem_event *tsem_event_init(enum tsem_event_type event,
+ struct tsem_event_parameters *params,
+ bool locked)
+{
+ int retn = 0;
+ struct tsem_event *ep = NULL;
+ struct tsem_task *task = tsem_task(current);
+
+ ep = tsem_event_allocate(locked);
+ if (!ep)
+ return ERR_PTR(-ENOMEM);
+
+ ep->event = event;
+ ep->locked = locked;
+ ep->pid = task_pid_nr(current);
+ memcpy(ep->comm, current->comm, sizeof(ep->comm));
+ memcpy(ep->task_id, task->task_id, tsem_digestsize());
+
+ get_COE(&ep->COE);
+ switch (event) {
+ case TSEM_FILE_OPEN:
+ case TSEM_BPRM_SET_CREDS:
+ retn = get_file_cell(params->u.file, ep);
+ break;
+ case TSEM_MMAP_FILE:
+ ep->CELL.mmap_file = *params->u.mmap_file;
+ if (!ep->CELL.mmap_file.anonymous)
+ retn = get_file_cell(ep->CELL.mmap_file.file, ep);
+ break;
+ case TSEM_SOCKET_CREATE:
+ ep->CELL.socket_create = *params->u.socket_create;
+ break;
+ case TSEM_SOCKET_CONNECT:
+ case TSEM_SOCKET_BIND:
+ ep->CELL.socket_connect = *params->u.socket_connect;
+ retn = get_socket_cell(ep);
+ break;
+ case TSEM_SOCKET_ACCEPT:
+ ep->CELL.socket_accept = *params->u.socket_accept;
+ retn = get_socket_accept(ep);
+ break;
+ case TSEM_TASK_KILL:
+ ep->CELL.task_kill = *params->u.task_kill;
+ break;
+ case TSEM_GENERIC_EVENT:
+ ep->CELL.event_type = params->u.event_type;
+ break;
+ default:
+ WARN_ONCE(true, "Unhandled event type: %d\n", event);
+ break;
+ }
+
+ if (retn) {
+ kmem_cache_free(event_cachep, ep);
+ ep = ERR_PTR(retn);
+ } else
+ kref_init(&ep->kref);
+
+ return ep;
+}
+
+/**
+ * tsem_free_event() - Free a security event description.
+ * @ep: A pointer to the security event description that is to be freed.
+ *
+ * This function is responsible for freeing the resources that were
+ * allocated by the tsem_event_allocate() function.
+ */
+static void tsem_event_free(struct kref *kref)
+{
+ struct tsem_event *ep;
+
+ ep = container_of(kref, struct tsem_event, kref);
+ if (ep)
+ kfree(ep->pathname);
+ kmem_cache_free(event_cachep, ep);
+}
+
+/**
+ * tsem_event_put() - Release a referenceto a TSEM event description.
+ *
+ * This function is called each time the use of a TSEM event description
+ * is dropped.
+ */
+void tsem_event_put(struct tsem_event *ep)
+{
+ kref_put(&ep->kref, tsem_event_free);
+}
+
+/**
+ * tsem_event_get() - Obtain a reference to a TSEM event description.
+ *
+ * This function is called on each invocation of the tsem_task_free
+ * function to release one of the references on the TMA modeling
+ * structure.
+ */
+void tsem_event_get(struct tsem_event *ep)
+{
+ kref_get(&ep->kref);
+}
+
+/**
+ * tsem_event_allocate() - Allocate a TSEM event description structure.
+ * @locked: A boolean flag used to indicate if the allocation is being
+ * done in atomic context and must be serviced from the
+ * pre-allocated event description structures.
+ *
+ * Return: This function returns a pointer to the allocated structure or
+ * a NULL pointer in the event of an allocation failure.
+ */
+struct tsem_event *tsem_event_allocate(bool locked)
+{
+ unsigned int index;
+ struct tsem_event *ep = NULL;
+ struct tsem_context *ctx = tsem_context(current);
+
+ if (!locked)
+ return kmem_cache_zalloc(event_cachep, GFP_KERNEL);
+
+ spin_lock(&ctx->magazine_lock);
+ index = find_first_zero_bit(ctx->magazine_index, ctx->magazine_size);
+ if (index < ctx->magazine_size) {
+ ep = ctx->magazine[index];
+ ctx->ws[index].index = index;
+ ctx->ws[index].u.ctx = ctx;
+ set_bit(index, ctx->magazine_index);
+
+ /*
+ * Similar to the issue noted in the refill_event_magazine()
+ * function, this barrier is used to cause the consumption
+ * of the cache entry to become visible.
+
+ */
+ smp_mb__after_atomic();
+ }
+
+ spin_unlock(&ctx->magazine_lock);
+
+ if (ep) {
+ INIT_WORK(&ctx->ws[index].work, refill_event_magazine);
+ queue_work(system_wq, &ctx->ws[index].work);
+ return ep;
+ }
+
+ pr_warn("tsem: %s in %llu failed event allocation, cache size=%u.\n",
+ current->comm, tsem_context(current)->id, ctx->magazine_size);
+ return NULL;
+}
+
+/**
+ * tsem event_magazine_allocate() - Allocate a TSEM event magazine.
+ * @ctx: A pointer to the modeling context that the magazine is
+ * to be allocated for.
+ * @size: The number of entries to be created in the magazine.
+
+ * The security modeling event magazine is an array of tsem_event
+ * structures that are used to service security hooks that are called
+ * in atomic context. Each modeling domain/namespace has a magazine
+ * allocated to it and this function allocates and initializes the
+ * memory structures needed to manage that magazine.
+
+ * Return: This function returns a value of zero on success and a negative
+ * error code on failure.
+ */
+int tsem_event_magazine_allocate(struct tsem_context *ctx, size_t size)
+{
+ unsigned int lp;
+ int retn = -ENOMEM;
+
+ ctx->magazine_size = size;
+
+ spin_lock_init(&ctx->magazine_lock);
+
+ ctx->magazine_index = bitmap_zalloc(ctx->magazine_size, GFP_KERNEL);
+ if (!ctx->magazine_index)
+ return retn;
+
+ ctx->magazine = kcalloc(ctx->magazine_size, sizeof(*ctx->magazine),
+ GFP_KERNEL);
+ if (!ctx->magazine)
+ goto done;
+
+ for (lp = 0; lp < ctx->magazine_size; ++lp) {
+ ctx->magazine[lp] = kmem_cache_zalloc(event_cachep,
+ GFP_KERNEL);
+ if (!ctx->magazine[lp])
+ goto done;
+ }
+
+ ctx->ws = kcalloc(ctx->magazine_size, sizeof(*ctx->ws), GFP_KERNEL);
+ if (ctx->ws)
+ retn = 0;
+
+ done:
+ if (retn)
+ tsem_event_magazine_free(ctx);
+
+ return retn;
+}
+
+/**
+ * tsem event_magazine_free() - Releases a TSEM event magazine.
+ * @ctx: A pointer to the modeling context whose magazine is to be
+ * released.
+ *
+ * The function is used to free the memory that was allocated by
+ * the tsem_event_magazine_allocate() function for a security
+ * modeling context.
+ */
+void tsem_event_magazine_free(struct tsem_context *ctx)
+{
+ unsigned int lp;
+
+ for (lp = 0; lp < ctx->magazine_size; ++lp)
+ kmem_cache_free(event_cachep, ctx->magazine[lp]);
+
+ bitmap_free(ctx->magazine_index);
+ kfree(ctx->ws);
+ kfree(ctx->magazine);
+}
+
+/**
+ * tsem event_cache_init() - Initialize the TSEM event cache.
+ *
+ * This function is called by the TSEM initialization function and sets
+ * up the cache that will be used to allocate tsem_event structures.
+ *
+ * Return: This function returns a value of zero on success and a negative
+ * error code on failure.
+ */
+int __init tsem_event_cache_init(void)
+{
+ event_cachep = kmem_cache_create("tsem_event_cache",
+ sizeof(struct tsem_event), 0,
+ SLAB_PANIC, 0);
+ if (!event_cachep)
+ return -ENOMEM;
+
+ return 0;
+}
--
2.39.1
The map.c file is responsible for implenting the description of a
security event into a security state coefficient. The following
documentation file provided as a part of the TSEM implementation
contains a description of this mapping process:
Documentation/admin-guide/LSM/tsem.rst
The mapping process takes a security event description, that was
described in the event.c file, and uses that to drive the mapping
process. The allocation and mapping of the event is unified
through the tsem_map_event() function provided in this file.
The function for a security event state coefficient mapping is as
follows:
Sp = HF(HF(EVENT_ID) || TASK_ID || HF(COE) || HF(CELL))
Where HF is the cryptographic hash function that has been
designated for use by a security modeling namespace.
This function is fully described in the previously noted
documentation file.
The TASK_ID is the security state coefficient for the
bprm_creds_for_exec security event hook. It is generated by the
tsem_map_task() function that is implemented in this file.
The TASK_ID mapping function uses the same functional expression
as the security state coefficient mapping but substitutes a
TASK_ID that consists of a buffer of null bytes equal in size to
the digest size of the cryptographic hash function being used in
the model.
Signed-off-by: Greg Wettstein <[email protected]>
---
security/tsem/map.c | 531 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 531 insertions(+)
create mode 100644 security/tsem/map.c
diff --git a/security/tsem/map.c b/security/tsem/map.c
new file mode 100644
index 000000000000..45f8ee6eca89
--- /dev/null
+++ b/security/tsem/map.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (C) 2023 Enjellic Systems Development, LLC
+ * Author: Dr. Greg Wettstein <[email protected]>
+ *
+ * This file implements mapping of events into security event points.
+ */
+
+#include "tsem.h"
+
+static int get_COE_mapping(struct tsem_event *ep, u8 *mapping)
+{
+ int retn = 0, size;
+ u8 *p;
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ shash->tfm = tsem_digest();
+ retn = crypto_shash_init(shash);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->COE.uid;
+ size = sizeof(ep->COE.uid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->COE.euid;
+ size = sizeof(ep->COE.euid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->COE.suid;
+ size = sizeof(ep->COE.suid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->COE.gid;
+ size = sizeof(ep->COE.gid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->COE.egid;
+ size = sizeof(ep->COE.egid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->COE.sgid;
+ size = sizeof(ep->COE.sgid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->COE.fsuid;
+ size = sizeof(ep->COE.fsuid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->COE.fsgid;
+ size = sizeof(ep->COE.fsgid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->COE.capeff;
+ size = sizeof(ep->COE.capeff);
+ retn = crypto_shash_finup(shash, p, size, mapping);
+
+ done:
+ return retn;
+}
+
+static int get_cell_mapping(struct tsem_event *ep, u8 *mapping)
+{
+ int retn = 0, size;
+ u8 *p;
+ struct sockaddr_in *ipv4;
+ struct sockaddr_in6 *ipv6;
+ struct tsem_mmap_file_args *mm_args = &ep->CELL.mmap_file;
+ struct tsem_socket_connect_args *scp = &ep->CELL.socket_connect;
+ struct tsem_socket_accept_args *sap = &ep->CELL.socket_accept;
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ shash->tfm = tsem_digest();
+ retn = crypto_shash_init(shash);
+ if (retn)
+ goto done;
+
+ if (ep->event == TSEM_MMAP_FILE) {
+ p = (u8 *) &mm_args->reqprot;
+ size = sizeof(mm_args->reqprot);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &mm_args->prot;
+ size = sizeof(mm_args->prot);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &mm_args->flags;
+ size = sizeof(mm_args->flags);
+ if (!mm_args->file) {
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ goto done;
+ }
+
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+ }
+
+ switch (ep->event) {
+ case TSEM_FILE_OPEN:
+ case TSEM_MMAP_FILE:
+ case TSEM_BPRM_SET_CREDS:
+ p = (u8 *) &ep->file.flags;
+ size = sizeof(ep->file.flags);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->file.uid;
+ size = sizeof(ep->file.uid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->file.gid;
+ size = sizeof(ep->file.gid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->file.mode;
+ size = sizeof(ep->file.mode);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->file.name_length;
+ size = sizeof(ep->file.name_length);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->file.name;
+ size = tsem_digestsize();
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->file.s_magic;
+ size = sizeof(ep->file.s_magic);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->file.s_id;
+ size = sizeof(ep->file.s_id);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->file.s_uuid;
+ size = sizeof(ep->file.s_uuid);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->file.digest;
+ size = tsem_digestsize();
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ break;
+
+ case TSEM_SOCKET_CREATE:
+ p = (u8 *) &ep->CELL.socket_create.family;
+ size = sizeof(ep->CELL.socket_create.family);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->CELL.socket_create.type;
+ size = sizeof(ep->CELL.socket_create.type);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->CELL.socket_create.protocol;
+ size = sizeof(ep->CELL.socket_create.protocol);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->CELL.socket_create.kern;
+ size = sizeof(ep->CELL.socket_create.kern);
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ if (retn)
+ goto done;
+ break;
+
+ case TSEM_SOCKET_CONNECT:
+ case TSEM_SOCKET_BIND:
+ p = (u8 *) &scp->family;
+ size = sizeof(scp->family);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ switch (scp->family) {
+ case AF_INET:
+ ipv4 = (struct sockaddr_in *) &scp->u.ipv4;
+ p = (u8 *) &ipv4->sin_port;
+ size = sizeof(ipv4->sin_port);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ipv4->sin_addr.s_addr;
+ size = sizeof(ipv4->sin_addr.s_addr);
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ break;
+
+ case AF_INET6:
+ ipv6 = (struct sockaddr_in6 *) &scp->u.ipv6;
+ p = (u8 *) &ipv6->sin6_port;
+ size = sizeof(ipv6->sin6_port);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) ipv6->sin6_addr.in6_u.u6_addr8;
+ size = sizeof(ipv6->sin6_addr.in6_u.u6_addr8);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ipv6->sin6_flowinfo;
+ size = sizeof(ipv6->sin6_flowinfo);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ipv6->sin6_scope_id;
+ size = sizeof(ipv6->sin6_scope_id);
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ if (retn)
+ goto done;
+ break;
+
+ case AF_UNIX:
+ p = scp->u.path;
+ size = strlen(scp->u.path);
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ if (retn)
+ goto done;
+ break;
+
+ default:
+ p = (u8 *) scp->u.mapping;
+ size = tsem_digestsize();
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ if (retn)
+ goto done;
+ break;
+ }
+ break;
+
+ case TSEM_SOCKET_ACCEPT:
+ p = (u8 *) &sap->family;
+ size = sizeof(sap->family);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &sap->type;
+ size = sizeof(sap->type);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &sap->port;
+ size = sizeof(sap->port);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ switch (sap->family) {
+ case AF_INET:
+ p = (u8 *) &sap->u.ipv4;
+ size = sizeof(sap->u.ipv4);
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ if (retn)
+ goto done;
+ break;
+
+ case AF_INET6:
+ p = (u8 *) sap->u.ipv6.in6_u.u6_addr8;
+ size = sizeof(sap->u.ipv6.in6_u.u6_addr8);
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ if (retn)
+ goto done;
+ break;
+
+ case AF_UNIX:
+ p = sap->u.path;
+ size = strlen(sap->u.path);
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ if (retn)
+ goto done;
+ break;
+
+ default:
+ p = sap->u.mapping;
+ size = tsem_digestsize();
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ if (retn)
+ goto done;
+ break;
+ }
+ break;
+
+ case TSEM_TASK_KILL:
+ p = (u8 *) &ep->CELL.task_kill.cross_model;
+ size = sizeof(ep->CELL.task_kill.cross_model);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->CELL.task_kill.signal;
+ size = sizeof(ep->CELL.task_kill.signal);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = (u8 *) &ep->CELL.task_kill.target;
+ size = sizeof(ep->CELL.task_kill.target);
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ if (retn)
+ goto done;
+ break;
+
+ case TSEM_GENERIC_EVENT:
+ p = (u8 *) tsem_names[ep->CELL.event_type];
+ size = strlen(tsem_names[ep->CELL.event_type]);
+ retn = crypto_shash_update(shash, p, size);
+ if (retn)
+ goto done;
+
+ p = tsem_context(current)->zero_digest;
+ size = tsem_digestsize();
+ retn = crypto_shash_finup(shash, p, size, mapping);
+ if (retn)
+ goto done;
+ break;
+
+ default:
+ break;
+ }
+
+ done:
+ return retn;
+}
+
+static int get_event_mapping(int event, u8 *task_id, u8 *COE_id, u8 *cell_id,
+ u8 *mapping)
+{
+ int retn = 0;
+ u32 event_id = (u32) event;
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ shash->tfm = tsem_digest();
+ retn = crypto_shash_init(shash);
+ if (retn)
+ goto done;
+
+ retn = crypto_shash_update(shash, tsem_names[event_id],
+ strlen(tsem_names[event_id]));
+ if (retn)
+ goto done;
+ if (task_id) {
+ retn = crypto_shash_update(shash, task_id, tsem_digestsize());
+ if (retn)
+ goto done;
+ }
+ retn = crypto_shash_update(shash, COE_id, tsem_digestsize());
+ if (retn)
+ goto done;
+ retn = crypto_shash_finup(shash, cell_id, tsem_digestsize(), mapping);
+
+ done:
+ return retn;
+}
+
+static int map_event(enum tsem_event_type event, struct tsem_event *ep,
+ u8 *task_id, u8 *event_mapping)
+{
+ int retn;
+ u8 COE_mapping[HASH_MAX_DIGESTSIZE];
+ u8 cell_mapping[HASH_MAX_DIGESTSIZE];
+
+ retn = get_COE_mapping(ep, COE_mapping);
+ if (retn)
+ goto done;
+
+ retn = get_cell_mapping(ep, cell_mapping);
+ if (retn)
+ goto done;
+
+ retn = get_event_mapping(event, task_id, COE_mapping, cell_mapping,
+ event_mapping);
+ done:
+ return retn;
+}
+
+/**
+ * tsem_map_task() - Create the task identity description structure.
+ * @file: A pointer to the file structure defining the executable.
+ * @task_id: Pointer to the buffer that the task id will be copied to.
+ *
+ * This function creates the security event state point that will be used
+ * as the task identifier for the generation of security state points
+ * that are created by the process that task identifier is assigned to.
+ *
+ * Return: This function returns 0 if the mapping was successfully
+ * created and an error value otherwise.
+ */
+int tsem_map_task(struct file *file, u8 *task_id)
+{
+ int retn = 0;
+ u8 null_taskid[HASH_MAX_DIGESTSIZE];
+ struct tsem_event *ep;
+ struct tsem_event_parameters params;
+
+ params.u.file = file;
+ ep = tsem_event_init(TSEM_BPRM_SET_CREDS, ¶ms, false);
+ if (IS_ERR(ep)) {
+ retn = PTR_ERR(ep);
+ ep = NULL;
+ goto done;
+ }
+
+ memset(null_taskid, '\0', tsem_digestsize());
+ retn = map_event(TSEM_BPRM_SET_CREDS, ep, null_taskid, task_id);
+ tsem_event_put(ep);
+
+ done:
+ return retn;
+}
+
+/**
+ * tsem_map_event() - Create a security event mapping.
+ * @event: The number of the event to be mapped.
+ * @params: A pointer to the structure containing the event description
+ * parameters.
+ *
+ * This function creates a structure to describe a security event
+ * and maps the event into a security state coefficient.
+ *
+ * Return: On success the function returns a pointer to the tsem_event
+ * structure that describes the event. If an error is encountered
+ * an error return value is encoded in the pointer.
+ */
+struct tsem_event *tsem_map_event(enum tsem_event_type event,
+ struct tsem_event_parameters *params)
+{
+ int retn = 0;
+ struct tsem_event *ep;
+ struct tsem_task *task = tsem_task(current);
+
+ ep = tsem_event_init(event, params, false);
+ if (IS_ERR(ep))
+ goto done;
+
+ if (task->context->external)
+ goto done;
+
+ retn = map_event(event, ep, task->task_id, ep->mapping);
+ if (retn) {
+ tsem_event_put(ep);
+ ep = ERR_PTR(retn);
+ }
+
+ done:
+ return ep;
+}
+
+
+/**
+ * tsem_map_event_locked() - Create a security event mapping while atomic.
+ * @event: The number of the event to be mapped.
+ * @params: A pointer to the structure containing the event description
+ * parameters.
+ *
+ * This function creates a structure to describe a security event
+ * and maps the event into a security state coefficient.
+ *
+ * Return: On success the function returns a pointer to the tsem_event
+ * structure that describes the event. If an error is encountered
+ * an error return value is encoded in the pointer.
+ */
+struct tsem_event *tsem_map_event_locked(enum tsem_event_type event,
+ struct tsem_event_parameters *params)
+{
+ int retn = 0;
+ struct tsem_event *ep;
+ struct tsem_task *task = tsem_task(current);
+
+ ep = tsem_event_init(event, params, true);
+ if (IS_ERR(ep))
+ goto done;
+
+ if (task->context->external)
+ goto done;
+
+ retn = map_event(event, ep, task->task_id, ep->mapping);
+ if (retn) {
+ tsem_event_put(ep);
+ ep = ERR_PTR(retn);
+ }
+
+ done:
+ return ep;
+}
--
2.39.1
TSEM is designed, from a functional perspective, to be contained
entirely in its own directory.
The tsem.h header file defines the enumeration types, structure
definitions and externally visiable functions that are referenced
by all of the compilation units of the TSEM LSM implementation in
that directory.
The structure and enumeration types are extensively documented
and are the recommended starting point for understanding TSEM
implementation and functionality.
Signed-off-by: Greg Wettstein <[email protected]>
---
security/tsem/tsem.h | 1516 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1516 insertions(+)
create mode 100644 security/tsem/tsem.h
diff --git a/security/tsem/tsem.h b/security/tsem/tsem.h
new file mode 100644
index 000000000000..03915f47529b
--- /dev/null
+++ b/security/tsem/tsem.h
@@ -0,0 +1,1516 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * Copyright (C) 2023 Enjellic Systems Development, LLC
+ * Author: Dr. Greg Wettstein <[email protected]>
+ *
+ * This is the single include file that documents all of the externally
+ * visible types and functions that are used by TSEM. This file is
+ * currently organized into four major sections in the following order;
+ *
+ * includes used by all compilation units
+ * CPP definitions
+ * enumeration types
+ * structure definitions
+ * function declarations
+ * inline encapsulation functions.
+ *
+ * Include files that are referenced by more than a single compilation
+ * should be included in this file. Includes that are needed to
+ * satisfy compilation requirements for only a single file should be
+ * included in the file needing that include.
+ *
+ * Understanding the overall implementation and architecture of TSEM
+ * will be facilitated by reviewing the documentation in this file.
+ */
+
+#include <uapi/linux/in.h>
+#include <uapi/linux/in6.h>
+#include <linux/wait.h>
+#include <linux/kref.h>
+#include <linux/lsm_hooks.h>
+#include <linux/capability.h>
+#include <crypto/hash.h>
+#include <crypto/hash_info.h>
+#include <net/af_unix.h>
+
+/* The capability needed to manage TSEM. */
+#define TSEM_CONTROL_CAPABILITY CAP_ML
+
+/*
+ * The number of 'slots' in the structure magazines that are used to
+ * satisfy modeling of security events that are called in atomic context.
+ */
+#define TSEM_ROOT_MAGAZINE_SIZE 96
+#define TSEM_MAGAZINE_SIZE_INTERNAL 16
+#define TSEM_MAGAZINE_SIZE_EXTERNAL 96
+
+/**
+ * enum tsem_event_type - Ordinal value for a security event.
+ * @TSEM_BPRM_SET_CREDS: Ordinal value for bprm_creds_for_exec.
+ * @TSEM_GENERIC_EVENT: Ordinal value for a generically modeled event.
+ * @TSEM_TASK_KILL: Ordinal value for task kill.
+ * @....: Remainder follows with a similar naming format that has
+ * TSEM_ prep ended to the raw LSM security hook name.
+ * @TSEM_EVENT_CNT: The final ordinal value is used to define the
+ * length of the following arrays that are indexed
+ * by the ordinal value of the hook:
+ *
+ * This enumeration is used to designate an ordinal value for each
+ * security event, ie. LSM hook, that TSEM is implementing modeling
+ * for. This value is used to identify the hook that is either having
+ * its event description being exported to an external Trusted Modeling
+ * Agent (TMA) or modeled by the internal TMA implementation.
+ *
+ * The primary use of this enumeration is to conditionalize code paths
+ * based on the security hook being processed and to index the
+ * tsem_names array and the array that defines the action that is to
+ * be taken in response to an event that generates a permissions
+ * violation.
+ */
+enum tsem_event_type {
+ TSEM_BPRM_SET_CREDS = 1,
+ TSEM_GENERIC_EVENT,
+ TSEM_TASK_KILL,
+ TSEM_TASK_SETPGID,
+ TSEM_TASK_GETPGID,
+ TSEM_TASK_GETSID,
+ TSEM_TASK_SETNICE,
+ TSEM_TASK_SETIOPRIO,
+ TSEM_TASK_GETIOPRIO,
+ TSEM_TASK_PRLIMIT,
+ TSEM_TASK_SETRLIMIT,
+ TSEM_TASK_SETSCHEDULER,
+ TSEM_TASK_GETSCHEDULER,
+ TSEM_TASK_PRCTL,
+ TSEM_FILE_OPEN,
+ TSEM_MMAP_FILE,
+ TSEM_FILE_IOCTL,
+ TSEM_FILE_LOCK,
+ TSEM_FILE_FCNTL,
+ TSEM_FILE_RECEIVE,
+ TSEM_UNIX_STREAM_CONNECT,
+ TSEM_UNIX_MAY_SEND,
+ TSEM_SOCKET_CREATE,
+ TSEM_SOCKET_CONNECT,
+ TSEM_SOCKET_BIND,
+ TSEM_SOCKET_ACCEPT,
+ TSEM_SOCKET_LISTEN,
+ TSEM_SOCKET_SOCKETPAIR,
+ TSEM_SOCKET_SENDMSG,
+ TSEM_SOCKET_RECVMSG,
+ TSEM_SOCKET_GETSOCKNAME,
+ TSEM_SOCKET_GETPEERNAME,
+ TSEM_SOCKET_SETSOCKOPT,
+ TSEM_SOCKET_SHUTDOWN,
+ TSEM_PTRACE_TRACEME,
+ TSEM_KERNEL_MODULE_REQUEST,
+ TSEM_KERNEL_LOAD_DATA,
+ TSEM_KERNEL_READ_FILE,
+ TSEM_SB_MOUNT,
+ TSEM_SB_UMOUNT,
+ TSEM_SB_REMOUNT,
+ TSEM_SB_PIVOTROOT,
+ TSEM_SB_STATFS,
+ TSEM_MOVE_MOUNT,
+ TSEM_SHM_ASSOCIATE,
+ TSEM_SHM_SHMCTL,
+ TSEM_SHM_SHMAT,
+ TSEM_SEM_ASSOCIATE,
+ TSEM_SEM_SEMCTL,
+ TSEM_SEM_SEMOP,
+ TSEM_SYSLOG,
+ TSEM_SETTIME,
+ TSEM_QUOTACTL,
+ TSEM_QUOTA_ON,
+ TSEM_MSG_QUEUE_ASSOCIATE,
+ TSEM_MSG_QUEUE_MSGCTL,
+ TSEM_MSG_QUEUE_MSGSND,
+ TSEM_MSG_QUEUE_MSGRCV,
+ TSEM_IPC_PERMISSION,
+ TSEM_KEY_ALLOC,
+ TSEM_KEY_PERMISSION,
+ TSEM_NETLINK_SEND,
+ TSEM_INODE_CREATE,
+ TSEM_INODE_LINK,
+ TSEM_INODE_UNLINK,
+ TSEM_INODE_SYMLINK,
+ TSEM_INODE_MKDIR,
+ TSEM_INODE_RMDIR,
+ TSEM_INODE_MKNOD,
+ TSEM_INODE_RENAME,
+ TSEM_INODE_SETATTR,
+ TSEM_INODE_GETATTR,
+ TSEM_INODE_SETXATTR,
+ TSEM_INODE_GETXATTR,
+ TSEM_INODE_LISTXATTR,
+ TSEM_INODE_REMOVEXATTR,
+ TSEM_INODE_KILLPRIV,
+ TSEM_TUN_DEV_CREATE,
+ TSEM_TUN_DEV_ATTACH_QUEUE,
+ TSEM_TUN_DEV_ATTACH,
+ TSEM_TUN_DEV_OPEN,
+ TSEM_BPF,
+ TSEM_BPF_MAP,
+ TSEM_BPF_PROG,
+ TSEM_EVENT_CNT
+};
+
+/**
+ * enum tsem_action_type - Ordinal value for security responses.
+ * @TSEM_ACTION_LOG: Ordinal value to indicate that a security event
+ * that results in a model permissions violation
+ * should be logged.
+ * @TSEM_ACTION_EPERM: Ordinal value to indicate that a security event
+ * generating a model permissions violation should
+ * return -EPERM to the caller.
+ *
+ * This enumeration type is used to designate what type of action is
+ * to be taken when the processing of a security event hook results in
+ * a model violation. The TSEM_ACTION_LOG and TSEM_ACTION_EPERM
+ * translate into the classical concepts of logging or enforcing
+ * actions used by other mandatory access control architectures.
+ */
+enum tsem_action_type {
+ TSEM_ACTION_LOG = 0,
+ TSEM_ACTION_EPERM,
+ TSEM_ACTION_CNT
+};
+
+/**
+ * enum tsem_control_type - Ordinal values for TSEM control actions.
+ * @TSEM_CONTROL_INTERNAL: This ordinal value is set when the first
+ * word of an argument string written to the
+ * control file is the word 'internal'. This
+ * designates that the security namespace will
+ * be modeled by the internal TMA.
+ * @TSEM_CONTROL_EXTERNAL: This ordinal value is set when the first
+ * word of an argument string written to the
+ * control file is the word 'external'. This
+ * designates that the security namespace will
+ * be model by an external TMA.
+ * @TSEM_CONTROL_ENFORCE: This ordinal value is set when the word
+ * 'enforce' is written to the control file.
+ * This indicates that model is to be placed
+ * in 'enforcing' mode and security events that
+ * result in model violations will return EPERM.
+ * @TSEM_CONTROL_SEAL: This ordinal value is set when the word 'seal'
+ * is written to the control file. This indicates
+ * that the model for security domain will treat
+ * all security events that do not conform to the
+ * model as 'forensics' events.
+ * @TSEM_CONTROL_TRUSTED: This ordinal value is used when the first
+ * word of an argument string written to the
+ * control file is the word 'trusted'. This
+ * is interpreted as a directive to set the
+ * trust status of the task that executed the
+ * security event to be trusted.
+ * @TSEM_CONTROL_UNTRUSTED: This ordinal value is used when the first
+ * word of an argument string written to the
+ * control file is the word 'untrusted'.
+ * This is interpreted as a directive to set
+ * the trust status of the task that executed
+ * the security event to be untrusted.
+ * @TSEM_CONTROL_MAP_STATE: This ordinal value is used when the first
+ * word of an argument string written to the
+ * control file is the word 'state'. The
+ * argument to this directive will be an
+ * ASCII hexadecimally encoded string of the
+ * current model's digest size that will be
+ * treated as a security state point for
+ * inclusion in the security model for the
+ * security domain/namespace.
+ * @TSEM_CONTROL_MAP_PSEUDONYM: This ordinal value is used when the
+ * first word of an argument string
+ * written to the control file is the
+ * word 'pseudonym'. The argument to
+ * this directive will be an ASCII
+ * hexadecimally encoded string of the
+ * current model's digest size that will
+ * be treated as a pseudonym directive
+ * for the security domain/namespace.
+ * TSEM_CONTROL_MAP_BASE: This ordinal value is used when the first
+ * word of an argument string written to the
+ * control file is the word 'base'. The
+ * argument to this directive will be an ASCII
+ * hexadecimally encoded string of the current
+ * model's digest size that will be treated as
+ * the base value for the computation of the
+ * functional values (measurement and state) of
+ * the security domain/namespace.
+
+ * This enumeration type is used to designate what type of control
+ * action is to be implemented when arguments are written to the TSEM
+ * control file (/sys/kernel/security/tsem/control). The ordinal
+ * values govern the processing of the command and the interpretation
+ * of the rest of the command argument string.
+ */
+enum tsem_control_type {
+ TSEM_CONTROL_INTERNAL = 0,
+ TSEM_CONTROL_EXTERNAL,
+ TSEM_CONTROL_ENFORCE,
+ TSEM_CONTROL_SEAL,
+ TSEM_CONTROL_TRUSTED,
+ TSEM_CONTROL_UNTRUSTED,
+ TSEM_CONTROL_MAP_STATE,
+ TSEM_CONTROL_MAP_PSEUDONYM,
+ TSEM_CONTROL_MAP_BASE
+};
+
+/**
+ * enum tsem_ns_reference - Ordinal value for DAC namespace reference.
+ * @TSEM_NS_INITIAL: This ordinal value indicates that the uid/gid
+ * values should be interpreted against the initial
+ * user namespace.
+ * @TSEM_NS_CURRENT: This ordinal value indicates that the uid/gid
+ * values should be interpreted against the user
+ * namespace that is in effect for the process being
+ * modeled.
+ *
+ * This enumeration type is used to indicate what user namespace
+ * should be referenced when the uid/gid values are interpreted for
+ * the creation of either the COE or CELL identities. The enumeration
+ * ordinal passed to the tsem_ns_create() function, to configure the
+ * security domain/namespace, is set by the nsref argument to either
+ * the 'internal' or 'external' control commands.
+ */
+enum tsem_ns_reference {
+ TSEM_NS_INITIAL = 1,
+ TSEM_NS_CURRENT
+};
+
+/**
+ * enum tsem_task_trust - Ordinal value describing task trust status.
+ * @TSEM_TASK_TRUSTED: This ordinal value indicates that the task has
+ * not executed a security event that has resulted
+ * in a security behavior not described by the
+ * security model the task is being governed by.
+ * @TSEM_TASK_UNTRUSTED: This ordinal value indicates that the task
+ * has requested the execution of a security event
+ * that resulted in a security behavior not
+ * permitted by the security model the task is
+ * being governed by.
+ * @TSEM_TASK_TRUST_PENDING: This ordinal value indicates that the setting
+ * of the task trust status is pending a response
+ * from an external TMA.
+ *
+ * This enumeration type is used to specify the three different trust
+ * states that a task can be in. The trust status of a task is
+ * regulated by the trust_status member of struct tsem_task. A task
+ * carrying the status of TSEM_TASK_TRUSTED means that it has
+ * not requested the execution of any security events that are
+ * inconsistent with the security model that the task is running in.
+ *
+ * If a task requests execution of a security event that is
+ * inconsistent with the security model it is operating in, and the
+ * domain is running in 'sealed' mode, the task trust status is set to
+ * TSEM_TASK_UNTRUSTED. This value is 'sticky' in that it will be
+ * propagated to any child tasks that are spawned from an untrusted
+ * task.
+ *
+ * In the case of an externally modeled security domain/namespace, the
+ * task trust status cannot be determined until the modeling of the
+ * security event has been completed. The tsem_export_event()
+ * function sets the trust status TSEM_TASK_TRUST_PENDING and then
+ * places the task into an interruptible sleep state.
+ *
+ * Only two events will cause the task to be removed from sleep state.
+ * Either the task is killed or a control message is written to the
+ * TSEM control file that specifies the trust status of the task. See
+ * the description of the TSEM_CONTROL_TRUSTED and
+ * TSEM_CONTROL_UNTRUSTED enumeration types.
+ */
+enum tsem_task_trust {
+ TSEM_TASK_TRUSTED = 1,
+ TSEM_TASK_UNTRUSTED = 2,
+ TSEM_TASK_TRUST_PENDING = 4
+};
+
+/**
+ * enum tsem_inode_state - Ordinal value for inode reference state.
+ * @TSEM_INODE_COLLECTING: This ordinal value indicates that the uid/gid
+ * values should be interpreted against the initial
+ * user namespace.
+ * @TSEM_INODE_COLLECTED: This ordinal value indicates that the uid/gid
+ * values should be interpreted against the user
+ * namespace that is in effect for the process being
+ * modeled.
+ *
+ * This enumeration type is used to specify the status of the inode
+ * that is having a digest value computed on the file that it is
+ * referencing. The purpose of this enumeration is so that the
+ * recursive call to the TSEM_FILE_OPEN hook, caused by the kernel
+ * opening the file to compute the checksum, can be bypassed.
+ *
+ * The state value of the inode is carried in struct tsem_inode and is
+ * set and interrogated by the add_file_digest() function. If the
+ * status of the inode is TSEM_INODE_COLLECTED and the iversion of the
+ * inode is the same as the collection time, the cached value for
+ * currently active model digest is returned.
+
+ * If the test for the relevancy of the cached digest value fails the
+ * status of the inode is set to TSEM_INODE_COLLECTING. The
+ * tsem_file_open() function will check the inode status when it is
+ * invoked by the integrity_kernel_read() function and if it is
+ * set to 'collecting', a successful permissions check is returned so
+ * that the kernel can open the file and compute its digest.
+ */
+enum tsem_inode_state {
+ TSEM_INODE_COLLECTING = 1,
+ TSEM_INODE_COLLECTED
+};
+
+/**
+ * struct tsem_task - TSEM task control structure.
+ * @tma_for_ns: The context identity number of the namespace that
+ * the task has control over if any.
+ * @trust_status: The enumeration type that specifies the trust state of
+ * the process.
+ * @task_id: The hash specific digest that identifies the process.
+ * @task_key: A hash specific digest value that is used to
+ * authenticate a task that is running as a trust
+ * orchestrator to a task that is under the control of the
+ * orchestrator.
+ * @context: A pointer to the tsem_context structure that defines the
+ * modeling context that the task is running under.
+ *
+ * This structure is one of the two primary control structures that
+ * are implemented through the LSM blob functionality. It is
+ * automatically created when the task control structure is allocated
+ * for a new task that is being created. It's role is to control the
+ * status of the task with respect to its security model.
+ *
+ * The trust_status member of structure determines whether or not the
+ * task is in a condition to be trusted. It represents whether or not
+ * the task has requested execution of a security event that is
+ * inconsistent with the model that the task is running under.
+ * Reference the tsem_trust_status enumeration type for more
+ * information on this member. The trust status value is propagated
+ * to any child tasks that are spawned from a task.
+ *
+ * The digest value that the task_id member contains is generated by
+ * the tsem_tsem_bprm_creds_for_exec() function that computes the
+ * task identity based on the COE identity and the CELL identity of
+ * the executable that is being started. This task_id value is used
+ * in the computation of the security state point values in
+ * combination with the COE and CELL identities for this event. The
+ * task_id digest creates security state points that are specific to
+ * the executable file that was used to start the task.
+ *
+ * The task_key member holds the authentication key that will be used
+ * to authenticate a process that is requesting the ability to set the
+ * trust status of a process. This value is generated for the task
+ * structure of the trust orchestrator when a security modeling
+ * namespace is created by the orchestrator.
+ *
+ * As an additional protection, the creation of a namespace causes the
+ * context id of the created namespace to be placed in the task that
+ * will serve as the trust orchestrator for the namespace. This
+ * context id must match the context id of a process that a trust
+ * control request is being sent to. Like the authentication key
+ * this value is not propagated on task allocation so only the task
+ * that has nominated the security modeling namespace will have
+ * possession of the necessary credentials to control it.
+ *
+ * The context member of the structure contains a pointer to the
+ * tsem_context structure allocated when a security modeling namespace
+ * is created by the tsem_ns_create() function. This structure will
+ * contain all of the information needed to define how the task is to
+ * have its security behavior modeled.
+ */
+struct tsem_task {
+ u64 tma_for_ns;
+ enum tsem_task_trust trust_status;
+ u8 task_id[HASH_MAX_DIGESTSIZE];
+ u8 task_key[HASH_MAX_DIGESTSIZE];
+ struct tsem_context *context;
+};
+
+/**
+ * struct tsem_context - TSEM modeling context description.
+ * @kref: Reference count for the context.
+ * @work: Work structure for delayed release of the context.
+ * @id: The index number of the context.
+ * @sealed: Status variable indicating whether or not the
+ * modeling context can be modified.
+ * @use_current_ns: Status variable indicating which user namespace
+ * should be used for resolution of uid/gid values.
+ * @actions: An array of enum tsem_action_type variables indicating
+ * the type of response that should be returned in
+ * response to the modeling of a security event that
+ * is inconsistent with the model being used for the
+ * security context.
+ * @digestname: A pointer to a null-terminated buffer containing the
+ * name of the digest function that is to be used for
+ * this security context.
+ * @zero_digest: The digest value for a 'zero-length' digest value.
+ * @tfm: A pointer to the digest transformation structure that is to
+ * be used for this context.
+ * @magazine_size: The number of struct tsem_event structures that
+ * are held in reserve for security event hooks that
+ * are called in atomic context.
+ * @magazine_lock: The spinlock that protects access to the event
+ * magazine for the security context.
+ * @magazine_index: The bitmap that is used to track the magazine slots
+ * that have been allocated.
+ * @ws: An array of work structures that are used to refill the magazine
+ * slots.
+ * @magazine: An array of pointers to struct tsem_event structures that
+ * are pre-allocated for security hooks called in atomic
+ * context.
+ * @model: If the modeling context is implemented with a kernel based
+ * trusted model agent this pointer will point to the struct
+ * tsem_model structure that maintains the state of the
+ * security model.
+ * @external: If the modeling context is implemented with an external
+ * modeling agent this pointer will point to the struct
+ * tsem_external structure that implements the interface to
+ * the external trusted modeling agent.
+
+ * This structure is used to represent the state of a TSEM security
+ * modeling namespace. A pointer to this structure is stored in the
+ * struct tsem_task structure.
+ *
+ * This structure is allocated by the tsem_ns_create() function in
+ * response to a TSEM control request. This structure maintains all
+ * of the information that describes the security modeling namespace
+ * that is not specific to the type of namespace, ie. external or
+ * internal that is being implemented.
+
+ * The id member is a 64-bit counter that cannot feasibly be
+ * overflowed and that is incremented for each namespace creation that
+ * is created. The root modeling namespace has a value of zero so the
+ * TSEM code uses a pattern of testing this value for non-zero status
+ * as an indication of whether or not the task is running in a
+ * subordinate modeling namespace.
+
+ * Each security modeling namespace can have an independent
+ * cryptographic digest function that is used as the compression
+ * function for generating the digest values that are used to model
+ * the security events that occur in a namespace. A single struct tfm
+ * is allocated for this digest function at the time that the
+ * tsem_context structure is created and is maintained in this
+ * structure for subsequent use during event processing.
+
+ * Each cryptographic digest function has a 'zero message' value that
+ * is the result of the initialization and closure of a hash function
+ * that has no other input. This zero digest value is computed at the
+ * time of the creation of the array. This digest value is returned
+ * for files with zero sizes or that have pseudonyms declared for
+ * them.
+ *
+ * The actions array contains a specification of how each security
+ * event should be handled in the event that the model detects a
+ * security event consistent with the model designated for the
+ * namespace. This array allows the specification of whether the
+ * model should be enforcing or logging. Currently the specification
+ * is all or nothing for all of the events, with plans to make the
+ * actions individually configurable.
+
+ * Each security event that is processed requires a struct tsem_event
+ * structure that drives either the internal modeling of an event or
+ * its export to an external modeling agent. Some security event
+ * hooks are called while a task is running in atomic context. Since
+ * memory cannot be allocated while a process is in atomic context, a
+ * magazine of these structures is maintained by this structure for
+ * security events that run in atomic context. The size of this
+ * magazine is dynamic and is configurable for each security modeling
+ * namespace that is created.
+ *
+ * When a tsem_event structure is allocated for an atomic event a
+ * request for the refill of the slot that is vacated is dispatched to
+ * an asynchronous workqueue. The ws member of this structure points
+ * to an array of work structures for this refill capability, one for
+ * each slot in the magazine.
+ *
+ * All of this infrastructure is generic for each security modeling
+ * namespace. How the security modeling is done is governed by the
+ * model and external members of this structure. These members point
+ * to data structures that either maintain the security model state
+ * for an in kernel trusted modeling agent or handle the export of the
+ * event to an external trusted modeling agent.
+ *
+ * Each task that is created in a non-root security modeling namespace
+ * increments the reference count maintained in the kref member of
+ * this structure in the tsem_task_alloc() function. The
+ * tsem_task_free() function decrements this reference count. When
+ * the reference count expires, ie. when the last task using the
+ * modeling namespace exits, an asynchronous workqueue request is
+ * dispatched to dispose of the context. The work member of this
+ * structure is used to reference that workqueue request.
+ */
+struct tsem_context {
+ struct kref kref;
+ struct work_struct work;
+
+ u64 id;
+ bool sealed;
+ bool use_current_ns;
+
+ enum tsem_action_type actions[TSEM_EVENT_CNT];
+
+ char *digestname;
+ u8 zero_digest[HASH_MAX_DIGESTSIZE];
+ struct crypto_shash *tfm;
+
+ unsigned int magazine_size;
+ spinlock_t magazine_lock;
+ unsigned long *magazine_index;
+ struct tsem_work *ws;
+ struct tsem_event **magazine;
+
+ struct tsem_model *model;
+ struct tsem_external *external;
+};
+
+/**
+ * struct tsem_model - TSEM internal TMA description.
+ * @have_aggregate: Flag variable to indicate whether or not the
+ * hardware aggregate value has been injected into
+ * the mode.
+ * @base: The base value that is to be used in computing the
+ * measurement and state values of the model.
+ * @measurement: The time dependent linear extension state of the
+ * model.
+ * @state: The time independent linear extension state of the model.
+ * @point_lock: The spinlock that protects access to the list of
+ * security state coefficients in the model.
+ * @point_list: A pointer to the list of security state coefficients
+ * in the model.
+ * @point_end_mutex: The mutex that is used to protect the end of the
+ * list of security state coefficients for the
+ * model.
+ * @point_end: A pointer to the end of the list of security state
+ * coefficients that will be traversed by a call to the
+ * control plane.
+ * @trajectory_lock: The spinlock used to protect the list of security
+ * event descriptions in the model.
+ * @trajectory_list: A pointer to the list of descriptions of the
+ * security events that have been recorded in this
+ * model.
+ * @trajectory_end_mutex: The mutex that protects the end of the list
+ * of security event descriptions.
+ * @trajectory_end: A pointer to the end of the list of security event
+ * descriptions that will be traversed by a call to
+ * the control plane.
+ * @forensics_lock: The spinlock used to protect the list of security
+ * event descriptions that are considered invalid by
+ * the model being enforced.
+ * @forensics_list: A pointer to the list of descriptions of security
+ * events that are considered invalid by the security
+ * model being enforced.
+ * @forensics_end_mutex: The mutex that protects the end of the list
+ * of security event descriptions that are
+ * considered invalid by the current model.
+ * @forensics_end: A pointer to the end of the list of security event
+ * descriptions, that are considered invalid, that are
+ * to be traversed by a call to the control plane.
+ * @pseudonym_mutex: The mutex lock that protects the list of file
+ * digest pseudonyms for the current model.
+ * @pseudonum_list: A pointer to the list of file digest pseudonyms
+ * that have been declared for the current model.
+ * @magazine_size: The number of struct tsem_event_point structures that
+ * are held in reserve for security event hooks that
+ * are called in atomic context.
+ * @magazine_lock: The spinlock that protects access to the event
+ * magazine for the security context.
+ * @magazine_index: The bitmap that is used to track the magazine slots
+ * that have been allocated.
+ * @ws: An array of work structures that are used to refill the magazine
+ * slots.
+ * @magazine: An array of pointers to struct tsem_event_point structures that
+ * are pre-allocated for security hooks called in atomic
+ * context.
+ *
+ * If a call to the tsem_ns_create() function specifies that a kernel
+ * based trusted modeling agent is to be used to implement the
+ * security namespace model, a pointer to this structure is placed in
+ * the struct tsem_context structure. This structure is used to
+ * maintain the state of the kernel based model.
+ *
+ * There are two primary functional values that are maintained by the
+ * model. The measurement member of this structure represents the
+ * time dependent linear extension sum of the security state
+ * coefficients that have been assigned to security events that have
+ * occurred in the context of the model.
+ *
+ * This classic integrity measurement is subject to scheduling
+ * dependencies and may be invariant from run to run of the model. It
+ * is of primary use in verifying the order of security events that
+ * have occurred in the model.
+ *
+ * The state member of this structure represents a time independent
+ * linear extension sum of the security state coefficients that have
+ * been generated in the model. It represents the linear extension
+ * sum of the security state coefficients that have been sorted in
+ * natural (big-endian) form.
+ *
+ * Both of these measurements are dependent on the platform hardware
+ * aggregate value and the base point that has been defined for the
+ * define.
+ *
+ * A non-zero hardware aggregate value is only available if the
+ * platform has a TPM. The have_aggregate member of this structure is
+ * a flag variable that indicates whether or not the aggregate value
+ * has been injected into the model.
+ *
+ * The base member of this structure contains a model specific
+ * coefficient that is used to perturb each security state coefficient
+ * generated in the model. This value is designed to serve as a
+ * 'freshness' value for a verifying party to the model.
+ *
+ * There are three primary model lists maintain by this structure:
+ *
+ * * security state points
+ * * security trajectory events
+ * * security forensics events
+ *
+ * Similar members are maintained in this structure to support each of
+ * these lists.
+ *
+ * All three lists are extension only and are protected by a spinlock
+ * that can be held in atomic context. This spinlock is only held for
+ * the period of time required to extend the list.
+ *
+ * Calls by the control plane to interrogate the lists require the
+ * traversal of the list that is ill-suited for a spinlock. As a
+ * result each list type has a mutex associated with it that protects
+ * a pointer to the end of the list, an endpoint that is determined at
+ * the start of a call to the control plane.
+ *
+ * The list spinlock is used at the start of the control plane call to
+ * capture the end of the list that is then protected by the mutex.
+ * In essence this is used to transition protection of the list from
+ * the spinlock to the mutex.
+ *
+ * The kernel based modeling agent has support for maintaining a
+ * constant digest value for files, that by function, do not have a
+ * fixed digest value, such as log files. The pseudonym_list member
+ * of this structure points to the list of these designations. The
+ * pseudonym_mutex structure protects this list.
+ *
+ * Like the struct tsem_context structure the tsem_model structure
+ * maintains a magazine of structures that are used to service
+ * security events that are called in atomic context. The magazine
+ * maintained by this structure is a list of struct tsem_event_point
+ * structures that are used to describe the security state
+ * coefficients held by the model.
+ *
+ * The description of struct tsem_context details the implementation
+ * of the magazine which is identical to the implementation for this
+ * structure, with the exception of the type of structures that are
+ * held in reserve.
+ */
+struct tsem_model {
+ bool have_aggregate;
+
+ u8 base[HASH_MAX_DIGESTSIZE];
+ u8 measurement[HASH_MAX_DIGESTSIZE];
+ u8 state[HASH_MAX_DIGESTSIZE];
+
+ spinlock_t point_lock;
+ struct list_head point_list;
+ struct mutex point_end_mutex;
+ unsigned int point_count;
+ struct list_head *point_end;
+
+ spinlock_t trajectory_lock;
+ struct list_head trajectory_list;
+ struct mutex trajectory_end_mutex;
+ struct list_head *trajectory_end;
+
+ spinlock_t forensics_lock;
+ struct list_head forensics_list;
+ struct mutex forensics_end_mutex;
+ struct list_head *forensics_end;
+
+ struct mutex pseudonym_mutex;
+ struct list_head pseudonym_list;
+
+ unsigned int magazine_size;
+ spinlock_t magazine_lock;
+ unsigned long *magazine_index;
+ struct tsem_work *ws;
+ struct tsem_event_point **magazine;
+};
+
+/**
+ * struct tsem_external - TSEM external TMA description.
+ * @export_lock: The spinlock that protects access to the export_list
+ * member of this structure.
+ * @export_list: A pointer to the list of events waiting to be
+ * exported to the trust orchestrator for the security
+ * modeling namespace. The structure type that is
+ * linked by this list is the struct export_event
+ * structure that is private to the export.c compilation
+ * unit.
+ * @dentry: A pointer to the dentry describing the pseudo-file in the
+ * /sys/kernel/security/tsem/ExternalTMA directory that is
+ * being used to export security event descriptions to the
+ * external trust orchestrator for the security modeling
+ * domain described by this structure.
+ * @have_event: A flag variable to indicate that there is work queued
+ * on the export pseudo-file for the security modeling
+ * namespace.
+ * @wq: The work queue used to implement polling for the security
+ * event export file for the security modeling namespace.
+ * @magazine_size: The number of struct export_event structures that
+ * are held in reserve for security event hooks that
+ * are called in atomic context.
+ * @magazine_lock: The spinlock that protects access to the event
+ * magazine for the security modeling domain.
+ * @magazine_index: The bitmap that is used to track the magazine slots
+ * that have been allocated.
+ * @ws: An array of work structures that are used to refill the magazine
+ * slots.
+ * @magazine: An array of pointers to struct export_event structures that
+ * are pre-allocated for security hooks called in atomic
+ * context.
+ *
+ * If an externally modeled security modeling namespace is created
+ * a structure of this type is allocated for the namespace and placed
+ * in the struct tsem_context structure.
+ *
+ * The primary purpose of this structure is to manage event
+ * descriptions that are being transmitted to the trust orchestrator
+ * associated with the security modeling namespace. The pseudo-file
+ * will be as follows:
+ *
+ * /sys/kernel/security/tsem/ExternalTMA/N
+ *
+ * Where N is the context id number of the modeling namespace.
+ *
+ * The dentry member of this structure is used to represent the
+ * pseudo-file that is created when the external modeled namespace is
+ * created.
+ *
+ * This list of events waiting to be received by the trust
+ * orchestrator is maintained in the export_list member of this
+ * structure. Additions or removals from the list hold the spinlock
+ * described by the export_lock member of this structure.
+ *
+ * The wq member of this structure is used to implement a workqueue
+ * to support polling for events on the export control file. The
+ * have_event flag is set to indicate to the polling call that
+ * security events are available for export.
+ *
+ * When a security event description is exported the calling task is
+ * scheduled away to allow the trust orchestrator to process the
+ * event. This obviously creates issues for security events that are
+ * called in atomic context.
+ *
+ * Security events in atomic context are exported as an async_event
+ * rather than a simple event. The trust orchestrator has the option
+ * of killing the workload that deviated from the security model or
+ * signaling a violation of the model.
+ *
+ * To support the export of asynchronous events a magazine, similar to
+ * the event and model structure magazines, is maintained by this
+ * structure for the external modeling namespace.
+ */
+struct tsem_external {
+ spinlock_t export_lock;
+ struct list_head export_list;
+ struct dentry *dentry;
+ bool have_event;
+ wait_queue_head_t wq;
+
+ unsigned int magazine_size;
+ spinlock_t magazine_lock;
+ unsigned long *magazine_index;
+ struct tsem_work *ws;
+ struct export_event **magazine;
+};
+
+/**
+ * struct tsem_work - TSEM magazine refill work structure.
+ * @index: The index number of the slot in the structure magazine that
+ * is being refilled.
+ * @u: A union that holds pointers to the structure whose magazine is
+ * being refilled.
+ * @work: The work structure that manages the workqueue being used to
+ * refill the magazine entry.
+ *
+ * As has been previously documented for the struct tsem_context,
+ * struct tsem_model and struct tsem_external structures, there is a
+ * need to maintain a magazine of these structures in order to allow
+ * the processing of security events that are called in atomic
+ * context. An array of this structure type is embedded in each of
+ * these structures to manage the asynchronous refill of the slot in
+ * the magazine that was used to handle an atomic security event.
+ *
+ * The index member of this structure points to the slot in the
+ * magazine that this work item is referencing.
+ *
+ * The structure that the refill work is being done for is maintained
+ * in the respective structure pointer in the u member of this
+ * structure.
+ *
+ * The work member of this structure is used to reference the
+ * asynchronous work request that is being submitted for the refill.
+ */
+struct tsem_work {
+ unsigned int index;
+ union {
+ struct tsem_context *ctx;
+ struct tsem_model *model;
+ struct tsem_external *ext;
+ } u;
+ struct work_struct work;
+};
+
+/**
+ * struct tsem_COE - TSEM context of execution definition structure.
+ * @uid: The numeric user identity that the COE is running with.
+ * @euid: The effective user identity that the COE is running with.
+ * @suid: The saved user identity possessed by the COE.
+ * @gid: The group identity that the COE is running with.
+ * @egid: The effective group identity that the COE possesses.
+ * @sgid: The saved group identity of the COE.
+ * @fsuid: The filesystem user identity that the COE is running with.
+ * @fsgid: The filesystem group identity that the COE is running with.
+ * @capeff: This union is used to implement access to the effective
+ * capability set the COE is running with. The mask value
+ * is used to assign to the structure with the value member
+ * used to extract the 64 bit value for export and
+ * computation.
+ *
+ * A security state coefficient is computed from two primary entities:
+ * the COE and the CELL identities. This structure is used to carry
+ * and encapsulate the characteristics of the context of execution
+ * (COE) that will be used to generate the COE identity.
+ *
+ * The numeric values for discretionary access controls, ie. uid, gid,
+ * are determined by which user namespace the security modeling
+ * namespace is configured to reference. The reference will be either
+ * the initial user namespace or the user namespace that the context
+ * of execution is running in.
+ */
+struct tsem_COE {
+ uid_t uid;
+ uid_t euid;
+ uid_t suid;
+
+ gid_t gid;
+ gid_t egid;
+ gid_t sgid;
+
+ uid_t fsuid;
+ gid_t fsgid;
+
+ union {
+ kernel_cap_t mask;
+ u64 value;
+ } capeff;
+};
+
+/**
+ * struct tsem_COE - TSEM file description.
+ * @uid: The numeric user identity of the file.
+ * @gid: The numeric group identity of the file.
+ * @mode: The discretionary access mode for the file.
+ * @flags: The file control flags.
+ * @name_length: The length of the pathname of the file.
+ * @name: The digest value of the pathname of the file using the
+ * hash function defined for the security modeling namespace.
+ * @s_magic: The magic number of the filesystem that the file resides
+ * in.
+ * @s_id: The name of the block device supporting the filesystem.
+ * @s_uuid: The uuid of the filesystem that the file resides in.
+ * @digest: The digest value of the contents of the file using the
+ * hash function defined for the security modeling namespace.
+ *
+ * This structure and the structures that follow up to the struct
+ * tsem_event structure are used to identify the various entities that
+ * are involved in the definition of the CELL identity for a security
+ * event.
+ *
+ * The tsem_file structure is used to encapsulate the characteristics
+ * of a file that is used as an entity in the CELL definition of an
+ * event.
+ *
+ * Since a pathname can be up to PATH_MAX (4096 bytes) in length the
+ * cryptographic digest value is used rather than the pathname of the
+ * file itself.
+ */
+struct tsem_file {
+ uid_t uid;
+ gid_t gid;
+ umode_t mode;
+ u32 flags;
+
+ u32 name_length;
+ u8 name[HASH_MAX_DIGESTSIZE];
+
+ u32 s_magic;
+ u8 s_id[32];
+ u8 s_uuid[16];
+
+ u8 digest[HASH_MAX_DIGESTSIZE];
+};
+
+/**
+ * struct tsem_COE - TSEM memory mapped file characteristics.
+ * @file: The struct file definition for the file that is being
+ * mapped. This pointer will be null in the case of an
+ * anonymous mapping.
+ * @anonymous: A flag variable to indicate whether or not the mapping
+ * is file backed or anonymous.
+ * @reqprot: The memory protection flags that are requested by the
+ * memory mapping system call.
+ * @prot: The protections that will be applied to the mapping.
+ * @flags: The control flags of the memory mapping call.
+ *
+ * This structure is used to encapsulate the arguments provided to the
+ * tsem_mmap_file security event handler.
+ */
+struct tsem_mmap_file_args {
+ struct file *file;
+ u32 anonymous;
+ u32 reqprot;
+ u32 prot;
+ u32 flags;
+};
+
+/**
+ * struct tsem_socket_create_args - TSEM socket creation arguments.
+ * @family: The family name of the socket whose creation is being
+ * requested.
+ * @type: The type of the socket being created.
+ * @protocol: The protocol family of the socket being created.
+ * @kern: A flag variable to indicate whether or not the socket being
+ * created is kernel or userspace based.
+ *
+ * This structure is used to encapsulate the arguments provided to the
+ * tsem_socket_create security event handler.
+ */
+struct tsem_socket_create_args {
+ int family;
+ int type;
+ int protocol;
+ int kern;
+};
+
+/**
+ * struct tsem_socket_connection_args - TSEM socket connection arguments.
+ * @tsip: A pointer to the struct tsem_inode structure that describes
+ * the TSEM inode characteristics of the inode representing
+ * the socket.
+ * @addr: A pointer to the structure describing the socket address
+ * that is being connected.
+ * @addr_len: The length of the socket address description structure.
+ * @family: The family number of the socket.
+ *
+ * @protocol: The protocol family of the socket being created.
+ * @kern: A flag variable to indicate whether or not the socket being
+ * created is kernel or userspace based.
+ * @u: A union that is used to hold the family specific address
+ * characteristics of the socket connection.
+ * @u.ipv4: If the connection is IPV4 based this structure will be
+ * populated with the IPV4 address information.
+ * @u.ipv6: If the connection is IPV6 based this structure will be
+ * populated with the IPV6 address information.
+ * @u.path: If the socket connection is an AF_UNIX based socket
+ * address this buffer will contain the pathname of the
+ * socket address.
+ * @u.mapping: If the socket represents an address protocol other
+ * than IPV4, IPV6 or UNIX domain this buffer will contain
+ * the cryptographic value of the socket address
+ * information using the hash function that has been
+ * specified for the security modeling namespace.
+ *
+ * This structure is used to encapsulate the arguments provided to the
+ * tsem_socket_create security event handler.
+ */
+struct tsem_socket_connect_args {
+ struct tsem_inode *tsip;
+ struct sockaddr *addr;
+ int addr_len;
+ u16 family;
+ union {
+ struct sockaddr_in ipv4;
+ struct sockaddr_in6 ipv6;
+ char path[UNIX_PATH_MAX + 1];
+ u8 mapping[HASH_MAX_DIGESTSIZE];
+ } u;
+};
+
+/**
+ * struct tsem_socket_accept_args - TSEM socket accept parameters.
+ * @family: The socket family identifier for the connection being
+ * accepted.
+ * @type: The type of socket connection being accepted.
+ * @port: The port number of the connection being accepted.
+ * @ipv4: The IPV4 address of the connection being accepted if the
+ * socket is representing an IPV4 connection
+ * @ipv6: The IPV6 address of the connection being accepted if the
+ * socket is representing an IPV6 connection.
+ * @af_unix: The UNIX domain socket address if the socket is
+ * representing a UNIX domain connection.
+ * @path: The pathname of the UNIX domain socket.
+ * @mapping: A cryptographic hash of description of the socket
+ * connection being accepted if the socket is representing
+ * a connection other than an IPV4, IPV6 or UNIX domain
+ * socket.
+ *
+ * This structure is used to encapsulate the arguments provided to the
+ * tsem_socket_accept security event handler.
+ */
+struct tsem_socket_accept_args {
+ u16 family;
+ u16 type;
+ __be16 port;
+ union {
+ __be32 ipv4;
+ struct in6_addr ipv6;
+ struct unix_sock *af_unix;
+ char path[UNIX_PATH_MAX + 1];
+ u8 mapping[HASH_MAX_DIGESTSIZE];
+ } u;
+};
+
+/**
+ * struct tsem_task_kill_args - TSEM task kill arguments.
+ * @cross_model: A flag variable used to indicate whether or not the
+ * signal is originating from a security modeling
+ * namespace other than the namespace of the target process.
+ * @signal: The number of the signal being sent.
+ * @source: The task identifier of the process sending the signal
+ * @target: The task identifier of the target process.
+ *
+ * This structure is used to encapsulate the arguments provided to the
+ * tsem_task_kill security event handler.
+ */
+struct tsem_task_kill_args {
+ u32 cross_model;
+ u32 signal;
+ u8 source[HASH_MAX_DIGESTSIZE];
+ u8 target[HASH_MAX_DIGESTSIZE];
+};
+
+/**
+ * struct tsem_event - TSEM security event description.
+ * @index: The index number of the slot in the structure magazine that
+ * is being refilled.
+ * @u: A union that holds pointers to the structure whose magazine is
+ * being refilled.
+ * @work: The work structure that manages the workqueue being used to
+ * refill the magazine entry.
+ * @event: The enumeration type describing the security event that the
+ * structure is defining.
+ * @locked: A boolean flag used to indicate whether or not the
+ * security event is running in atomic context.
+ * @pid: The process id number, in the global pid namespace, of the
+ * task that is requesting approval for a security event.
+ * @pathname: If the event is referencing a file this pointer will
+ * point to a null-terminated buffer containing the
+ * pathname to the file in the mount namespace that the
+ * process is running in.
+ * @comm: A pointer to a null terminated buffer containing the name of
+ * the process that is requesting the security event.
+ * @digestsize: The size in bytes of the cryptographic hash function
+ * that is being used in the namespace in which the event
+ * is being recorded.
+ * @task_id: The TSEM task identifier of the process that generated the
+ * security event described by an instance of this
+ * structure.
+ * @mapping: The security state coefficient that the event described
+ * by this structure generates.
+ * @COE: The struct tsem_COE structure that describes the Context Of
+ * Execution that generated the event described by this
+ * structure.
+ * @file: If the security event references a file this structure will
+ * contain the struct tsem_file structure that describes the
+ * characteristics of the file.
+ * @CELL: The CELL union is used to hold the data structures that
+ * characterize the CELL identity of the event.
+ * @CELL.event_type: In the case of a generically modeled event this
+ * member will contain the enumeration value
+ * identifying the event.
+ * @CELL.mmap_file: The structure describing the characteristics of
+ * a mmap_file security event.
+ * @CELL.socket_create: The structure describing the characteristics
+ * of a socket_create security event.
+ * @CELL.socket_connect: The structure describing the characteristics
+ * of a socket_connect security event.
+ * @CELL.socket_accept: The structure describing the characteristics
+ * of a socket accept security event.
+ * @CELL.task_kill: The structure describing the characteristics of a
+ * task_kill security event.
+ *
+ * This structure is the primary data structure for describing
+ * security events that are registered in a security modeling
+ * namespace. Each unique security coefficient in the namespace will
+ * have one of these structures attached to it.
+ *
+ * This structure encapsulates the following three major sources of
+ * information about the event:
+ *
+ * * A description of the process initiating the event.
+ * * The characteristics that form the COE identity of the event.
+ * * The characteristics that form the CELL identity of the event.
+ *
+ * Since one event description has to ultimately characterize any
+ * security event that can occur the strategy is to use a union that
+ * contains security event specific structures that describe the
+ * characteristics of the event.
+ *
+ * The kref member of this structure is used to signal when the
+ * structure is to be deleted. For example, in the case of an
+ * externally modeled event, when the export of the event description
+ * is complete. In the case of an internally modeled namespace the
+ * structure will be released if it represents a security state
+ * coefficient that is already present in the model.
+ *
+ * The work member of this structure is used to support asynchronous
+ * updates to a TPM for the root modeling domain. Asynchronous
+ * updates are used to improve the performance of modeling and to
+ * handle security events that are running in atomic context and
+ * cannot be scheduled away while the TPM transaction completes.
+ *
+ * The tsem_event_allocate() function is called by a TSEM security
+ * event handler to allocate and populate this structure. The struct
+ * tsem_event_parameters structure is used to encapsulate all of the
+ * different structure types that are needed to characterize all of
+ * the different security events that occur.
+ *
+ * The tsem_event_allocate() function is called by either the
+ * tsem_map_event() or tsem_map_event_locked() functions. After
+ * allocating and populating an event description structure the
+ * mapping functions generate a security state coefficient from the
+ * information in this structure.
+ *
+ * The two separate function call points for mapping are to allow the
+ * security event handlers to indicate the context in which the
+ * security event is occurring, ie. sleeping or atomic context. After
+ * this point the context of the security event is represented by the
+ * locked member of this structure.
+ *
+ * After the event is mapped this structure is either passed to the
+ * internal trusted modeling agent or the contents of this structure
+ * is exported to the trust orchestrator attached to the namespace for
+ * modeling by an external trust modeling agent.
+ */
+struct tsem_event {
+ struct kref kref;
+ struct list_head list;
+ struct work_struct work;
+
+ enum tsem_event_type event;
+ bool locked;
+ pid_t pid;
+ char *pathname;
+ char comm[TASK_COMM_LEN];
+
+ unsigned int digestsize;
+ u8 task_id[HASH_MAX_DIGESTSIZE];
+ u8 mapping[HASH_MAX_DIGESTSIZE];
+
+ struct tsem_COE COE;
+ struct tsem_file file;
+
+ union {
+ u32 event_type;
+ struct tsem_mmap_file_args mmap_file;
+ struct tsem_socket_create_args socket_create;
+ struct tsem_socket_connect_args socket_connect;
+ struct tsem_socket_accept_args socket_accept;
+ struct tsem_task_kill_args task_kill;
+ } CELL;
+};
+
+/**
+ * struct tsem_event_parameters - Security event argument descriptions
+ * @u: A union that encapsulates all of the different structures that
+ * are used to characterize the argument so the TSEM security
+ * event handlers.
+ * @u.event_type: This structure member holds the enum tsem_event_type
+ * enumeration value of the event whose characteristics
+ * are encapsulated in the function.
+ * @u.file: If the security event references a VFS file this member
+ * hold a pointer to the description of the file.
+ _file event.
+ * @u.socket_create: This member will point to a structure that
+ * describes the characteristics of a socket_create
+ * event.
+ * @u.socket_connect: This member will point to a structure that
+ * describes the characteristics of a socket_connect
+ * event.
+ * @u.socket_accept: This member will point to a structure that
+ * describes the characteristics of a socket_accept
+ * event.
+ * @u.task_kill: This member will point to a structure that describes
+ * the characteristics of a task_kill function.
+ *
+ * The purpose of this structure is to provide a common encapsulation
+ * method for passing the CELL characteristics of a security event
+ * into the tsem_event_init() function. The characteristics passed in
+ * this event will be used to create and populate the struct
+ * tsem_event structure that will go on to be used to characterize
+ * the event either an internal or external modeling agent.
+ *
+ * The strategy followed is to allocate one of these structures on the
+ * stack for a security event call along with a call specific
+ * characteristics description structure, both of which will no longer
+ * be needed after completion of the call since the requisite
+ * information has been transferred to a struct tsem_event structure.
+ */
+struct tsem_event_parameters {
+ union {
+ u32 event_type;
+ struct file *file;
+ struct tsem_mmap_file_args *mmap_file;
+ struct tsem_socket_create_args *socket_create;
+ struct tsem_socket_connect_args *socket_connect;
+ struct tsem_socket_accept_args *socket_accept;
+ struct tsem_task_kill_args *task_kill;
+ } u;
+};
+
+/**
+ * struct tsem_event_point - TSEM security coefficient characteristics.
+ * @list: The list structure used to link together all of the security
+ * state coefficients for a modeling namespace.
+ * @valid: A boolean value use to indicate whether or not the security
+ * state point is a valid coefficient in the model.
+ * @count: The number of times this coefficient has been expressed by
+ * security model for the namespace.
+ * @point: The security state coefficient for the point created by
+ * the cryptographic hash function being used for the modeling
+ * namespace.
+ *
+ * This structure is used by internal trusted modeling agents to
+ * represent each unique state point in a security model. Security
+ * state coefficients are unique within a model so only one struct
+ * tsem_event_point structure will be generated regardless of how many
+ * times the security event that generates the point occurs. The
+ * count member of this structure represents the total number of
+ * security events that have occurred that have generated the point.
+ *
+ * The valid member of this structure is used to flag whether this
+ * is consistent with the model for the namespace or was generated by
+ * a 'forensic', ie. out of model, event.
+ *
+ * Within each security namespace these structures are linked together
+ * in a list that describes the functional value of the security model
+ * assigned to the namespace. Entries are only added to this list and
+ * never removed.
+ *
+ * The desired state of a security model is created by using the TSEM
+ * control plane to inject a list of acceptable security state
+ * coefficients into the model. Sealing a model causes any security
+ * events that produce a coefficient different from those already in
+ * the model to be rejected as an invalid security event and logged as
+ * a forensic event for the model.
+ */
+struct tsem_event_point {
+ struct list_head list;
+ bool valid;
+ u64 count;
+ u8 point[HASH_MAX_DIGESTSIZE];
+};
+
+/**
+ * struct tsem_inode - TSEM inode status structure.
+ * @mutex: The mutex that will protect the list of struct
+ * tsem_inode_digest structures that have been created for the
+ * inode containing a struct tsem_inode structure.
+ * @list: The list structure that points to the list of struct
+ * tsem_inode_structures.
+ * @status: The digest collection state of the inode. See the
+ * discussion of enum tsem_inode_state for what information
+ * is conveyed by the value of this structure member.
+
+ * This structure is the second of the two primary control structures
+ * that are implemented through the LSM blob functionality. It is
+ * automatically created when the inode structure is created for
+ * system resources that are referenced by a struct inode structure.
+ *
+ * This structure has two primary purposes. The status member is used
+ * to signal that the tsem_file_open() function should return that
+ * permission to access the file is returned when the security hook is
+ * invoked by the integrity_kernel_read() function. See the
+ * discussion on enum tsem_inode_state to more details.
+ *
+ * The second purpose of this structure is to maintain a list of
+ * digest values that have been computed by the inode that this
+ * structure references. Maintenance of multiple digest values is
+ * required since there is no concept of a fixed digest function for
+ * TSEM as each modeling namespace can have its own digest function.
+ *
+ * Each digest value in use has a struct tsem_inode_digest structure
+ * allocated for it. The digest_list member of this structure points
+ * to a list of these structures.
+ *
+ * The mutex implemented in this structure should be held by any
+ * process that is accessing the list.
+ */
+struct tsem_inode {
+ struct mutex mutex;
+ struct list_head digest_list;
+ enum tsem_inode_state status;
+};
+
+/**
+ * struct tsem_inode_digest - Digest specific file checksum.
+ * @list: The list structure used to link multiple digest values
+ * for an inode.
+ * @version: The version number of the inode that generated the digest
+ * value that is currently represented.
+ * @name: A pointer to a null-terminated character buffer containing
+ * the name of the hash function that generated the current
+ * digest value.
+ * @value: The digest value of the file.
+ *
+ * A linked list of these structures is maintained for each inode that
+ * is modeled by TSEM and is used to support multiple hash specific
+ * digest values for a file represented by the inode. The tsem_inode
+ * structure that represents the TSEM security status of the inode
+ * contains the pointer to this list of structures.
+ *
+ * The version member of the structure contains the inode version number
+ * that was in effect when the last digest value of this type was computed.
+ * This version number value is used to detect changes and to trigger an
+ * update of the digest value.
+ *
+ * The name member of structure contains the name of the hash function
+ * that generated the checksum value. This name is used to locate the
+ * correct structure by comparing its value against the hash function
+ * that is being used for the modeling domain that is accessing the
+ * inode.
+ */
+struct tsem_inode_digest {
+ struct list_head list;
+ char *name;
+ u64 version;
+ u8 value[HASH_MAX_DIGESTSIZE];
+};
+
+/*
+ * The following three variables are the only globally visible
+ * variables used in the TSEM implementation.
+ *
+ * The tsem_blob_sizes variable is used by the LSM infrastructure to
+ * describe the amount of space that will be needed by the struct
+ * tsem_task and struct tsem_inode structures.
+ *
+ * The tsem_names array is defined in the tsem.c file and contains an
+ * array of pointers to the strings that define the names for each of
+ * the TSEM security event handles. The enum tsem_event_type
+ * enumeration indexes this array.
+ *
+ * The tsem_root_actions array is also indexed by the enum
+ * tsem_event_type enumeration and is used to determine the type of
+ * response that a TSEM security event handler is to return to the
+ * caller, ie. either logging or enforcing. The contents of this
+ * array is inherited by copying the array into the struct
+ * tsem_context structure for modeling namespaces that are subordinate
+ * to the root model.
+ */
+extern struct lsm_blob_sizes tsem_blob_sizes;
+extern const char * const tsem_names[TSEM_EVENT_CNT];
+extern enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT];
+
+/*
+ * The following section of the file contains the definitions for the
+ * externally visible functions in each of the TSEM compilation units.
+ */
+extern struct dentry *tsem_fs_create_external(const char *name);
+extern void tsem_fs_show_trajectory(struct seq_file *c, struct tsem_event *ep);
+extern void tsem_fs_show_field(struct seq_file *c, const char *field);
+extern void tsem_fs_show_key(struct seq_file *c, char *term, char *key,
+ char *fmt, ...);
+extern int tsem_fs_init(void);
+
+extern struct tsem_model *tsem_model_allocate(size_t size);
+extern void tsem_model_free(struct tsem_context *ctx);
+extern int tsem_model_event(struct tsem_event *ep);
+extern int tsem_model_load_point(u8 *point);
+extern int tsem_model_load_pseudonym(u8 *mapping);
+extern int tsem_model_has_pseudonym(struct tsem_inode *tsip,
+ struct tsem_file *ep);
+extern void tsem_model_load_base(u8 *mapping);
+extern int tsem_model_add_aggregate(void);
+extern void tsem_model_compute_state(void);
+extern void tsem_model_magazine_free(struct tsem_model *model);
+extern int tsem_model_cache_init(struct tsem_model *model, size_t size);
+
+extern void tsem_ns_put(struct tsem_context *ctx);
+extern int tsem_ns_event_key(u8 *task_key, const char *keystr, u8 *key);
+extern int tsem_ns_create(const enum tsem_control_type type,
+ const char *digest, const enum tsem_ns_reference ns,
+ const char *key, const unsigned int cache_size);
+
+extern int tsem_export_show(struct seq_file *m, void *v);
+extern int tsem_export_event(struct tsem_event *ep);
+extern int tsem_export_action(enum tsem_event_type event, bool locked);
+extern int tsem_export_aggregate(void);
+extern int tsem_export_magazine_allocate(struct tsem_external *ext,
+ size_t size);
+extern void tsem_export_magazine_free(struct tsem_external *ext);
+extern int tsem_export_cache_init(void);
+
+extern int tsem_map_task(struct file *file, u8 *mapping);
+struct tsem_event *tsem_map_event(enum tsem_event_type event,
+ struct tsem_event_parameters *param);
+struct tsem_event *tsem_map_event_locked(enum tsem_event_type event,
+ struct tsem_event_parameters *param);
+
+extern struct tsem_event *tsem_event_allocate(bool locked);
+extern struct tsem_event *tsem_event_init(enum tsem_event_type event,
+ struct tsem_event_parameters *params,
+ bool locked);
+extern void tsem_event_put(struct tsem_event *ep);
+extern void tsem_event_get(struct tsem_event *ep);
+extern int tsem_event_magazine_allocate(struct tsem_context *ctx, size_t size);
+extern void tsem_event_magazine_free(struct tsem_context *ctx);
+extern int tsem_event_cache_init(void);
+
+extern u8 *tsem_trust_aggregate(void);
+extern int tsem_trust_add_event(struct tsem_event *ep);
+
+/*
+ * The remaining inline function declarations follow the design
+ * pattern of the other LSM's and implement functions that return
+ * various TSEM characteristics of tasks, modeling contexts and
+ * inodes.
+ */
+static inline struct tsem_task *tsem_task(struct task_struct *task)
+{
+ return task->security + tsem_blob_sizes.lbs_task;
+}
+
+static inline bool tsem_task_trusted(struct task_struct *task)
+{
+ return tsem_task(task)->trust_status & TSEM_TASK_TRUSTED;
+}
+
+static inline bool tsem_task_untrusted(struct task_struct *task)
+{
+ return tsem_task(task)->trust_status & ~TSEM_TASK_TRUSTED;
+}
+
+static inline struct tsem_context *tsem_context(struct task_struct *task)
+{
+ return tsem_task(task)->context;
+}
+
+static inline struct tsem_model *tsem_model(struct task_struct *task)
+{
+ return tsem_task(task)->context->model;
+}
+
+static inline struct tsem_inode *tsem_inode(struct inode *inode)
+{
+ return inode->i_security + tsem_blob_sizes.lbs_inode;
+}
+
+static inline struct crypto_shash *tsem_digest(void)
+{
+ return tsem_context(current)->tfm;
+}
+
+static inline unsigned int tsem_digestsize(void)
+{
+ return crypto_shash_digestsize(tsem_digest());
+}
--
2.39.1
The tsem.c file is the 'master' file in the TSEM implementation.
It is responsible for initializing the LSM and providing
implementions of the security event hooks implemented by TSEM.
In addition to initializing the LSM, the set_ready() function
implements a secondary initialization that is used to to indicate
that security event modeling can begin. This is required
secondary to the fact that the cryptographic API's do not become
ready until after the 'fs' phase of system initialization is
complete.
This file also handles the implementation of kernel command-line
parameters that are used to configure the root security modeling
namespace.
The 'tsem_mode' parameter, if set to a value of 1, causes modeling
to be not conducted for the root security modeling namespace.
One use case is to allow development platform to develop security
models without the overhead of full platform modeling.
The 'tsem_digest' parameter is used to set the cryptographic hash
function that is used to generate security state coefficients in
the root model. TSEM can use any cryptographic hash function
implemented in the kernel, on a namespace by namespace basis.
Subordinate modeling namespaces can select their hash function
as part of the namespace creation process but the 'tsem_digest'
parameter has to be used to set the function for the root
modeling namespace.
The hash function used in the root modeling namespace but be
compiled into the kernel since the function is used before module
loading becomes available. The TSEM kernel configuration selects
the SHA256 function to be included as the default cryptographic
modeling function.
The 'tsem_cache' variable sets the size of the pre-allocated
structures that are used for security event modeling in the root
modeling namespace. This cache is used to support the modeling
and export of events that run in atomic context. The cache size
can be set independently for each subordinate security modeling
on a namespace by namespace basis.
This file also contains the implementation of the tsem_names
array that contains the ASCII text names that are assigned to
each security event handler. This name is used as one of the
characteristics in the security state points that are generated.
This array is also used to provide symbolic names for the export
of security event descriptions, either through the TSEM control
plane or for export to external trust orchestrators.
Signed-off-by: Greg Wettstein <[email protected]>
---
security/tsem/tsem.c | 1987 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1987 insertions(+)
create mode 100644 security/tsem/tsem.c
diff --git a/security/tsem/tsem.c b/security/tsem/tsem.c
new file mode 100644
index 000000000000..8ec630354240
--- /dev/null
+++ b/security/tsem/tsem.c
@@ -0,0 +1,1987 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (C) 2023 Enjellic Systems Development, LLC
+ * Author: Dr. Greg Wettstein <[email protected]>
+ *
+ * TSEM initialization infrastructure.
+ */
+#define TRAPPED_MSG_LENGTH 128
+
+#define LOCKED true
+#define NOLOCK false
+
+#include <linux/magic.h>
+#include <linux/mman.h>
+#include <linux/binfmts.h>
+#include <linux/bpf.h>
+#include <linux/ipv6.h>
+
+#include "tsem.h"
+
+struct lsm_blob_sizes tsem_blob_sizes __ro_after_init = {
+ .lbs_task = sizeof(struct tsem_task),
+ .lbs_inode = sizeof(struct tsem_inode)
+};
+
+enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT] = {
+ TSEM_ACTION_EPERM /* Undefined. */
+};
+
+static struct tsem_model root_model = {
+ .point_lock = __SPIN_LOCK_INITIALIZER(root_model.point_lock),
+ .point_list = LIST_HEAD_INIT(root_model.point_list),
+ .point_end_mutex = __MUTEX_INITIALIZER(root_model.point_end_mutex),
+
+ .trajectory_lock = __SPIN_LOCK_INITIALIZER(root_model.trajectory_lock),
+ .trajectory_list = LIST_HEAD_INIT(root_model.trajectory_list),
+ .trajectory_end_mutex = __MUTEX_INITIALIZER(root_model.trajectory_end_mutex),
+
+ .forensics_lock = __SPIN_LOCK_INITIALIZER(root_model.forensics_lock),
+ .forensics_list = LIST_HEAD_INIT(root_model.forensics_list),
+ .forensics_end_mutex = __MUTEX_INITIALIZER(root_model.forensics_end_mutex),
+
+ .pseudonym_mutex = __MUTEX_INITIALIZER(root_model.pseudonym_mutex),
+ .pseudonym_list = LIST_HEAD_INIT(root_model.pseudonym_list)
+};
+
+static struct tsem_context root_context;
+
+static int tsem_ready __ro_after_init;
+
+static bool tsem_available __ro_after_init;
+
+static unsigned int magazine_size __ro_after_init = TSEM_ROOT_MAGAZINE_SIZE;
+
+static bool no_root_modeling __ro_after_init;
+
+static char *default_hash_function __ro_after_init;
+
+static int __init set_magazine_size(char *magazine_value)
+{
+ if (kstrtouint(magazine_value, 0, &magazine_size))
+ pr_warn("tsem: Failed to parse root cache size.\n");
+
+ if (!magazine_size) {
+ pr_warn("tsem: Forcing non-zero cache size.\n");
+ magazine_size = TSEM_ROOT_MAGAZINE_SIZE;
+ }
+
+ pr_info("tsem: Setting default root cache size to %u.\n",
+ magazine_size);
+ return 1;
+}
+__setup("tsem_cache=", set_magazine_size);
+
+static int __init set_modeling_mode(char *mode_value)
+{
+ unsigned long mode = 0;
+
+ if (kstrtoul(mode_value, 0, &mode)) {
+ pr_warn("tsem: Failed to parse modeling mode.\n");
+ return 1;
+ }
+
+ if (mode == 1)
+ no_root_modeling = true;
+ else
+ pr_warn("tsem: Unknown mode specified.\n");
+ return 1;
+}
+__setup("tsem_mode=", set_modeling_mode);
+
+static int __init set_default_hash_function(char *hash_function)
+{
+
+ default_hash_function = hash_function;
+ return 1;
+}
+__setup("tsem_digest=", set_default_hash_function);
+
+const char * const tsem_names[TSEM_EVENT_CNT] = {
+ "undefined",
+ "bprm_set_creds",
+ "generic_event",
+ "task_kill",
+ "task_setpgid",
+ "task_getpgid",
+ "task_getsid",
+ "task_setnice",
+ "task_setioprio",
+ "task_getioprio",
+ "task_prlimit",
+ "task_setrlimit",
+ "task_setscheduler",
+ "task_getscheduler",
+ "task_prctl",
+ "file_open",
+ "mmap_file",
+ "file_ioctl",
+ "file_lock",
+ "file_fcntl",
+ "file_receive",
+ "unix_stream_connect",
+ "unix_may_send",
+ "socket_create",
+ "socket_connect",
+ "socket_bind",
+ "socket_accept",
+ "socket_listen",
+ "socket_socketpair",
+ "socket_sendmsg",
+ "socket_recvmsg",
+ "socket_getsockname",
+ "socket_getpeername",
+ "socket_setsockopt",
+ "socket_shutdown",
+ "ptrace_traceme",
+ "kernel_module_request",
+ "kernel_load_data",
+ "kernel_read_file",
+ "sb_mount",
+ "sb_umount",
+ "sb_remount",
+ "sb_pivotroot",
+ "sb_statfs",
+ "move_mount",
+ "shm_associate",
+ "shm_shmctl",
+ "shm_shmat",
+ "sem_associate",
+ "sem_semctl",
+ "sem_semop",
+ "syslog",
+ "settime",
+ "quotactl",
+ "quota_on",
+ "msg_queue_associate",
+ "msg_queue_msgctl",
+ "msg_queue_msgsnd",
+ "msg_queue_msgrcv",
+ "ipc_permission",
+ "key_alloc",
+ "key_permission",
+ "netlink_send",
+ "inode_create",
+ "inode_link",
+ "inode_unlink",
+ "inode_symlink",
+ "inode_mkdir",
+ "inode_rmdir",
+ "inode_mknod",
+ "inode_rename",
+ "inode_setattr",
+ "inode_getattr",
+ "inode_setxattr",
+ "inode_getxattr",
+ "inode_listxattr",
+ "inode_removexattr",
+ "inode_killpriv",
+ "tun_dev_create",
+ "tun_dev_attach_queue",
+ "tun_dev_attach",
+ "tun_dev_open",
+ "bpf",
+ "bpf_map",
+ "bpf_prog"
+};
+
+static const int pseudo_filesystems[] = {
+ PROC_SUPER_MAGIC,
+ SYSFS_MAGIC,
+ DEBUGFS_MAGIC,
+ TMPFS_MAGIC,
+ DEVPTS_SUPER_MAGIC,
+ BINFMTFS_MAGIC,
+ SECURITYFS_MAGIC,
+ SELINUX_MAGIC,
+ SMACK_MAGIC,
+ CGROUP_SUPER_MAGIC,
+ CGROUP2_SUPER_MAGIC,
+ NSFS_MAGIC,
+ EFIVARFS_MAGIC
+};
+
+static bool bypass_inode(struct inode *inode)
+{
+ bool retn = true;
+
+ unsigned int lp;
+
+ if (!S_ISREG(inode->i_mode))
+ goto done;
+
+ for (lp = 0; lp < ARRAY_SIZE(pseudo_filesystems); ++lp)
+ if (inode->i_sb->s_magic == pseudo_filesystems[lp])
+ goto done;
+ retn = false;
+
+ done:
+ return retn;
+}
+
+static int event_action(struct tsem_context *ctx, enum tsem_event_type event)
+{
+ int retn = 0;
+
+ if (tsem_task_trusted(current))
+ return retn;
+
+ if (ctx->actions[event] == TSEM_ACTION_EPERM)
+ retn = -EPERM;
+
+ return retn;
+}
+
+static int return_trapped_task(enum tsem_event_type event, char *msg,
+ bool locked)
+{
+ int retn;
+ struct tsem_context *ctx = tsem_context(current);
+
+ pr_warn("Untrusted %s: comm=%s, pid=%d, parameters='%s'\n",
+ tsem_names[event], current->comm, task_pid_nr(current), msg);
+
+ if (ctx->external) {
+ retn = tsem_export_action(event, locked);
+ if (retn)
+ return retn;
+ }
+
+ return event_action(ctx, event);
+}
+
+static int return_trapped_inode(enum tsem_event_type event,
+ struct inode *inode, char *inode_msg,
+ bool locked)
+{
+ const char *dname;
+ char msg[TRAPPED_MSG_LENGTH];
+ struct dentry *dird;
+
+ dird = d_find_alias(inode);
+ if (dird == NULL)
+ dname = "not available";
+ else
+ dname = dird->d_name.name;
+ scnprintf(msg, sizeof(msg), "parent=%s, %s", dname, inode_msg);
+
+ return return_trapped_task(event, msg, locked);
+}
+
+static int model_event(struct tsem_event *ep)
+{
+ int retn;
+ struct tsem_context *ctx = tsem_context(current);
+
+ if (!ctx->id && no_root_modeling)
+ return 0;
+
+ if (!ctx->external)
+ retn = tsem_model_event(ep);
+ else
+ retn = tsem_export_event(ep);
+
+ if (!retn)
+ retn = event_action(ctx, ep->event);
+ return retn;
+}
+
+static int model_generic_event(enum tsem_event_type event, bool locked)
+{
+ int retn;
+ struct tsem_event *ep;
+ struct tsem_event_parameters params;
+
+ if (!tsem_context(current)->id && no_root_modeling)
+ return 0;
+
+ params.u.event_type = event;
+
+ if (locked)
+ ep = tsem_map_event_locked(TSEM_GENERIC_EVENT, ¶ms);
+ else
+ ep = tsem_map_event(TSEM_GENERIC_EVENT, ¶ms);
+ if (IS_ERR(ep))
+ return PTR_ERR(ep);
+
+ retn = model_event(ep);
+
+ tsem_event_put(ep);
+ return retn;
+}
+
+static int tsem_file_open(struct file *file)
+{
+ int retn = 0;
+ char msg[TRAPPED_MSG_LENGTH];
+ struct inode *inode = file_inode(file);
+ struct tsem_event *ep = NULL;
+ struct tsem_event_parameters params;
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "filename=%s, flags=0x%x",
+ file->f_path.dentry->d_name.name, file->f_flags);
+ return return_trapped_task(TSEM_FILE_OPEN, msg, NOLOCK);
+ }
+
+ if (bypass_inode(inode))
+ goto done;
+ if (tsem_inode(inode)->status == TSEM_INODE_COLLECTING)
+ goto done;
+
+ params.u.file = file;
+ ep = tsem_map_event(TSEM_FILE_OPEN, ¶ms);
+ if (IS_ERR(ep)) {
+ retn = PTR_ERR(ep);
+ goto done;
+ }
+
+ retn = model_event(ep);
+ tsem_event_put(ep);
+
+ done:
+ return retn;
+}
+
+static int tsem_mmap_file(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
+{
+ int retn = 0;
+ const char *p;
+ char msg[TRAPPED_MSG_LENGTH];
+ struct inode *inode = NULL;
+ struct tsem_event *ep = NULL;
+ struct tsem_event_parameters params;
+ struct tsem_mmap_file_args args;
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ p = "anonymous mapping";
+ if (file)
+ p = file->f_path.dentry->d_name.name;
+ scnprintf(msg, sizeof(msg),
+ "filename=%s, rprot=0x%lx, prot=0x%lx, flags=0x%lx",
+ p, reqprot, prot, flags);
+ return return_trapped_task(TSEM_MMAP_FILE, msg, NOLOCK);
+ }
+
+ if (!file && !(prot & PROT_EXEC))
+ goto done;
+ if (file) {
+ inode = file_inode(file);
+ if (bypass_inode(inode))
+ goto done;
+ }
+
+ args.file = file;
+ args.anonymous = file == NULL ? 1 : 0;
+ args.reqprot = reqprot;
+ args.prot = prot;
+ args.flags = flags;
+ params.u.mmap_file = &args;
+ ep = tsem_map_event(TSEM_MMAP_FILE, ¶ms);
+ if (IS_ERR(ep)) {
+ retn = PTR_ERR(ep);
+ goto done;
+ }
+
+ retn = model_event(ep);
+ tsem_event_put(ep);
+
+ done:
+ return retn;
+}
+
+static int tsem_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "name=%s, cmd=%u",
+ file->f_path.dentry->d_name.name, cmd);
+ return return_trapped_task(TSEM_FILE_IOCTL, msg, NOLOCK);
+ }
+
+ if (bypass_inode(file_inode(file)))
+ return 0;
+
+ return model_generic_event(TSEM_FILE_IOCTL, NOLOCK);
+}
+
+static int tsem_file_lock(struct file *file, unsigned int cmd)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "name=%s, cmd=%u",
+ file->f_path.dentry->d_name.name, cmd);
+ return return_trapped_task(TSEM_FILE_LOCK, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_FILE_LOCK, NOLOCK);
+}
+
+static int tsem_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "name=%s, cmd=%u",
+ file->f_path.dentry->d_name.name, cmd);
+ return return_trapped_task(TSEM_FILE_FCNTL, msg, NOLOCK);
+ }
+
+ if (bypass_inode(file_inode(file)))
+ return 0;
+
+ return model_generic_event(TSEM_FILE_FCNTL, NOLOCK);
+}
+
+static int tsem_file_receive(struct file *file)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "name=%s, flags=%u",
+ file->f_path.dentry->d_name.name, file->f_flags);
+ return return_trapped_task(TSEM_FILE_RECEIVE, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_FILE_RECEIVE, NOLOCK);
+}
+
+static int tsem_task_alloc(struct task_struct *new, unsigned long flags)
+{
+ struct tsem_task *old_task = tsem_task(current);
+ struct tsem_task *new_task = tsem_task(new);
+
+ new_task->trust_status = old_task->trust_status;
+ new_task->context = old_task->context;
+ memcpy(new_task->task_id, old_task->task_id, HASH_MAX_DIGESTSIZE);
+
+ if (!new_task->context->id)
+ return 0;
+
+ kref_get(&new_task->context->kref);
+ memcpy(new_task->task_key, old_task->task_key, HASH_MAX_DIGESTSIZE);
+ return 0;
+}
+
+static void tsem_task_free(struct task_struct *task)
+{
+ struct tsem_context *ctx = tsem_context(task);
+
+ if (!ctx->id)
+ return;
+ tsem_ns_put(ctx);
+}
+
+static int tsem_task_kill(struct task_struct *target,
+ struct kernel_siginfo *info, int sig,
+ const struct cred *cred)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ int retn = 0;
+ struct tsem_event *ep;
+ struct tsem_event_parameters params;
+ struct tsem_task_kill_args args;
+ struct tsem_context *src_ctx = tsem_context(current);
+ struct tsem_context *tgt_ctx = tsem_context(target);
+
+ if (tsem_task_untrusted(current)) {
+ snprintf(msg, sizeof(msg),
+ "target=%s, pid=%d, signal=%d", target->comm,
+ task_pid_nr(target), sig);
+ return return_trapped_task(TSEM_TASK_KILL, msg, true);
+ }
+
+ args.cross_model = src_ctx->id != tgt_ctx->id;
+
+ if (SI_FROMKERNEL(info))
+ return retn;
+ if (sig == SIGURG)
+ return retn;
+ if (!capable(TSEM_CONTROL_CAPABILITY) &&
+ has_capability_noaudit(target, TSEM_CONTROL_CAPABILITY))
+ return -EPERM;
+ if (!capable(TSEM_CONTROL_CAPABILITY) && args.cross_model)
+ return -EPERM;
+
+ args.signal = sig;
+ memcpy(args.target, tsem_task(target)->task_id, tsem_digestsize());
+ params.u.task_kill = &args;
+
+ ep = tsem_map_event_locked(TSEM_TASK_KILL, ¶ms);
+ if (IS_ERR(ep)) {
+ retn = PTR_ERR(ep);
+ goto done;
+ }
+
+ retn = model_event(ep);
+ tsem_event_put(ep);
+
+ done:
+ return retn;
+}
+
+static int tsem_ptrace_traceme(struct task_struct *parent)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "parent=%s", parent->comm);
+ return return_trapped_task(TSEM_PTRACE_TRACEME, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_PTRACE_TRACEME, LOCKED);
+}
+
+static int tsem_task_setpgid(struct task_struct *p, pid_t pgid)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct tsem_context *ctx = tsem_context(current);
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s", p->comm);
+ pr_warn("Untrusted %s: comm=%s, pid=%d, parameters='%s'\n",
+ tsem_names[TSEM_TASK_SETPGID], current->comm,
+ task_pid_nr(current), msg);
+ return event_action(ctx, TSEM_TASK_SETPGID);
+ }
+
+ return model_generic_event(TSEM_TASK_SETPGID, true);
+}
+
+static int tsem_task_getpgid(struct task_struct *p)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s", p->comm);
+ return return_trapped_task(TSEM_TASK_GETPGID, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_TASK_GETPGID, LOCKED);
+}
+
+static int tsem_task_getsid(struct task_struct *p)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s", p->comm);
+ return return_trapped_task(TSEM_TASK_GETSID, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_TASK_GETSID, LOCKED);
+}
+
+static int tsem_task_setnice(struct task_struct *p, int nice)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s, nice=%d",
+ p->comm, nice);
+ return return_trapped_task(TSEM_TASK_SETNICE, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_TASK_SETNICE, LOCKED);
+}
+
+static int tsem_task_setioprio(struct task_struct *p, int ioprio)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s, ioprio=%d",
+ p->comm, ioprio);
+ return return_trapped_task(TSEM_TASK_SETIOPRIO, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_TASK_SETIOPRIO, NOLOCK);
+}
+
+static int tsem_task_getioprio(struct task_struct *p)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s", p->comm);
+ return return_trapped_task(TSEM_TASK_GETIOPRIO, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_TASK_GETIOPRIO, NOLOCK);
+}
+
+static int tsem_task_prlimit(const struct cred *cred, const struct cred *tcred,
+ unsigned int flags)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "uid=%d, gid=%d, euid=%d, egid=%d, flags=%u",
+ from_kuid(&init_user_ns, tcred->uid),
+ from_kgid(&init_user_ns, tcred->gid),
+ from_kuid(&init_user_ns, tcred->euid),
+ from_kgid(&init_user_ns, tcred->egid), flags);
+ return return_trapped_task(TSEM_TASK_PRLIMIT, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_TASK_PRLIMIT, LOCKED);
+}
+
+static int tsem_task_setrlimit(struct task_struct *p, unsigned int resource,
+ struct rlimit *new_rlim)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "target=%s, res=%u, cur=%lu, max=%lu",
+ p->comm, resource, new_rlim->rlim_cur,
+ new_rlim->rlim_max);
+ return return_trapped_task(TSEM_TASK_SETRLIMIT, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_TASK_SETRLIMIT, LOCKED);
+}
+
+static int tsem_task_setscheduler(struct task_struct *p)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s", p->comm);
+ return return_trapped_task(TSEM_TASK_SETSCHEDULER, msg,
+ LOCKED);
+ }
+
+ return model_generic_event(TSEM_TASK_SETSCHEDULER, LOCKED);
+}
+
+static int tsem_task_getscheduler(struct task_struct *p)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s", p->comm);
+ return return_trapped_task(TSEM_TASK_GETSCHEDULER, msg,
+ LOCKED);
+ }
+
+ return model_generic_event(TSEM_TASK_GETSCHEDULER, LOCKED);
+}
+
+static int tsem_task_prctl(int option, unsigned long arg2, unsigned long arg3,
+ unsigned long arg4, unsigned long arg5)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "option=%d", option);
+ return return_trapped_task(TSEM_TASK_PRCTL, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_TASK_PRCTL, LOCKED);
+}
+
+static int tsem_bprm_creds_for_exec(struct linux_binprm *bprm)
+{
+ struct tsem_task *task = tsem_task(current);
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ return tsem_map_task(bprm->file, task->task_id);
+}
+
+static int tsem_inode_alloc_security(struct inode *inode)
+{
+ struct tsem_inode *tsip = tsem_inode(inode);
+
+ mutex_init(&tsip->mutex);
+ INIT_LIST_HEAD(&tsip->digest_list);
+
+ return 0;
+}
+
+static void tsem_inode_free_security(struct inode *inode)
+{
+ struct tsem_inode_digest *digest, *tmp_digest;
+
+ if (bypass_inode(inode))
+ return;
+
+ list_for_each_entry_safe(digest, tmp_digest,
+ &tsem_inode(inode)->digest_list, list) {
+ list_del(&digest->list);
+ kfree(digest->name);
+ kfree(digest);
+ }
+}
+
+static int tsem_unix_stream_connect(struct sock *sock, struct sock *other,
+ struct sock *newsk)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u/%u, ",
+ sock->sk_family, other->sk_family);
+ return return_trapped_task(TSEM_UNIX_STREAM_CONNECT, msg,
+ LOCKED);
+ }
+
+ return model_generic_event(TSEM_UNIX_STREAM_CONNECT, LOCKED);
+}
+
+static int tsem_unix_may_send(struct socket *sock, struct socket *other)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct sock *sk = sock->sk;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u, type=%u",
+ sk->sk_family, sock->type);
+ return return_trapped_task(TSEM_UNIX_MAY_SEND, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_UNIX_MAY_SEND, LOCKED);
+}
+
+static int tsem_socket_create(int family, int type, int protocol, int kern)
+{
+ int retn;
+ char msg[TRAPPED_MSG_LENGTH];
+ struct tsem_event *ep;
+ struct tsem_event_parameters params;
+ struct tsem_socket_create_args args;
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "family=%d, type=%d, protocol=%d, kern=%d", family,
+ type, protocol, kern);
+ return return_trapped_task(TSEM_SOCKET_CREATE, msg, NOLOCK);
+ }
+
+ args.family = family;
+ args.type = type;
+ args.protocol = protocol;
+ args.kern = kern;
+ params.u.socket_create = &args;
+
+ ep = tsem_map_event(TSEM_SOCKET_CREATE, ¶ms);
+ if (IS_ERR(ep)) {
+ retn = PTR_ERR(ep);
+ goto done;
+ }
+
+ retn = model_event(ep);
+ tsem_event_put(ep);
+
+ done:
+ return retn;
+}
+
+static int tsem_socket_connect(struct socket *sock, struct sockaddr *addr,
+ int addr_len)
+
+{
+ int retn;
+ char msg[TRAPPED_MSG_LENGTH];
+ struct tsem_event *ep;
+ struct tsem_event_parameters params;
+ struct tsem_socket_connect_args args;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u", addr->sa_family);
+ return return_trapped_task(TSEM_SOCKET_CONNECT, msg, NOLOCK);
+ }
+
+ args.tsip = tsem_inode(SOCK_INODE(sock));
+ args.addr = addr;
+ args.addr_len = addr_len;
+ params.u.socket_connect = &args;
+
+ ep = tsem_map_event(TSEM_SOCKET_CONNECT, ¶ms);
+ if (IS_ERR(ep)) {
+ retn = PTR_ERR(ep);
+ goto done;
+ }
+
+ retn = model_event(ep);
+ tsem_event_put(ep);
+
+ done:
+ return retn;
+
+}
+
+static int tsem_socket_bind(struct socket *sock, struct sockaddr *addr,
+ int addr_len)
+
+{
+ int retn;
+ char msg[TRAPPED_MSG_LENGTH];
+ struct tsem_event *ep;
+ struct tsem_event_parameters params;
+ struct tsem_socket_connect_args args;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u", addr->sa_family);
+ return return_trapped_task(TSEM_SOCKET_BIND, msg, NOLOCK);
+ }
+
+ args.tsip = tsem_inode(SOCK_INODE(sock));
+ args.addr = addr;
+ args.addr_len = addr_len;
+ params.u.socket_connect = &args;
+
+ ep = tsem_map_event(TSEM_SOCKET_BIND, ¶ms);
+ if (IS_ERR(ep)) {
+ retn = PTR_ERR(ep);
+ goto done;
+ }
+
+ retn = model_event(ep);
+ tsem_event_put(ep);
+
+ done:
+ return retn;
+
+}
+
+static int tsem_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ int retn;
+ char msg[TRAPPED_MSG_LENGTH];
+ struct sock *sk = sock->sk;
+ const struct in6_addr *ipv6;
+ struct tsem_event *ep;
+ struct tsem_event_parameters params;
+ struct tsem_socket_accept_args args;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u", sk->sk_family);
+ return return_trapped_task(TSEM_SOCKET_ACCEPT, msg, NOLOCK);
+ }
+
+ args.family = sk->sk_family;
+ args.type = sock->type;
+ args.port = sk->sk_num;
+ args.u.ipv4 = sk->sk_rcv_saddr;
+ if (args.family == AF_UNIX)
+ args.u.af_unix = unix_sk(sk);
+ ipv6 = inet6_rcv_saddr(sk);
+ if (ipv6)
+ args.u.ipv6 = *ipv6;
+
+ params.u.socket_accept = &args;
+
+ ep = tsem_map_event(TSEM_SOCKET_ACCEPT, ¶ms);
+ if (IS_ERR(ep)) {
+ retn = PTR_ERR(ep);
+ goto done;
+ }
+
+ retn = model_event(ep);
+ tsem_event_put(ep);
+
+ done:
+ return retn;
+}
+
+static int tsem_socket_listen(struct socket *sock, int backlog)
+
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct sock *sk = sock->sk;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u, type=%u, port=%u",
+ sk->sk_family, sock->type, sk->sk_num);
+ return return_trapped_task(TSEM_SOCKET_LISTEN, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SOCKET_LISTEN, NOLOCK);
+}
+
+static int tsem_socket_socketpair(struct socket *socka, struct socket *sockb)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct sock *ska = socka->sk, *skb = sockb->sk;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family a=%u, family b=%u",
+ ska->sk_family, skb->sk_family);
+ return return_trapped_task(TSEM_SOCKET_SOCKETPAIR, msg,
+ NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SOCKET_SOCKETPAIR, NOLOCK);
+}
+
+static int tsem_socket_sendmsg(struct socket *sock, struct msghdr *msgmsg,
+ int size)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct sock *sk = sock->sk;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u, size=%d",
+ sk->sk_family, size);
+ return return_trapped_task(TSEM_SOCKET_SENDMSG, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SOCKET_SENDMSG, NOLOCK);
+}
+
+static int tsem_socket_recvmsg(struct socket *sock, struct msghdr *msgmsg,
+ int size, int flags)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct sock *sk = sock->sk;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u, size=%d, flags=%d",
+ sk->sk_family, size, flags);
+ return return_trapped_task(TSEM_SOCKET_RECVMSG, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SOCKET_RECVMSG, NOLOCK);
+}
+
+static int tsem_socket_getsockname(struct socket *sock)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct sock *sk = sock->sk;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u", sk->sk_family);
+ return return_trapped_task(TSEM_SOCKET_GETSOCKNAME, msg,
+ NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SOCKET_GETSOCKNAME, NOLOCK);
+}
+
+static int tsem_socket_getpeername(struct socket *sock)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct sock *sk = sock->sk;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u", sk->sk_family);
+ return return_trapped_task(TSEM_SOCKET_GETPEERNAME, msg,
+ NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SOCKET_GETPEERNAME, NOLOCK);
+}
+
+static int tsem_socket_setsockopt(struct socket *sock, int level, int optname)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct sock *sk = sock->sk;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u, level=%d, optname=%d",
+ sk->sk_family, level, optname);
+ return return_trapped_task(TSEM_SOCKET_SETSOCKOPT, msg,
+ NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SOCKET_SETSOCKOPT, NOLOCK);
+}
+
+static int tsem_socket_shutdown(struct socket *sock, int how)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct sock *sk = sock->sk;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u, how=%d",
+ sk->sk_family, how);
+ return return_trapped_task(TSEM_SOCKET_SHUTDOWN, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SOCKET_SHUTDOWN, NOLOCK);
+}
+
+static int tsem_kernel_module_request(char *kmod_name)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "module=%s", kmod_name);
+ return return_trapped_task(TSEM_KERNEL_MODULE_REQUEST, msg,
+ NOLOCK);
+ }
+
+ return model_generic_event(TSEM_KERNEL_MODULE_REQUEST, NOLOCK);
+}
+
+static int tsem_kernel_load_data(enum kernel_load_data_id id, bool contents)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "id=%d, contents=%d", id,
+ contents);
+ return return_trapped_task(TSEM_KERNEL_LOAD_DATA, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_KERNEL_LOAD_DATA, NOLOCK);
+}
+
+static int tsem_kernel_read_file(struct file *file,
+ enum kernel_read_file_id id, bool contents)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "filename=%s, flags=0x%x, id=%d, contents=%d",
+ file->f_path.dentry->d_name.name, file->f_flags,
+ id, contents);
+ return return_trapped_task(TSEM_KERNEL_READ_FILE, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_KERNEL_READ_FILE, NOLOCK);
+}
+
+static int tsem_sb_mount(const char *dev_name, const struct path *path,
+ const char *type, unsigned long flags, void *data)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "device=%s, type=%s, flags=%lu",
+ dev_name, type, flags);
+ return return_trapped_task(TSEM_SB_MOUNT, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SB_MOUNT, NOLOCK);
+}
+
+static int tsem_sb_umount(struct vfsmount *mnt, int flags)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "root=%s, flags=%d",
+ mnt->mnt_root->d_name.name, flags);
+ return return_trapped_task(TSEM_SB_UMOUNT, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SB_UMOUNT, NOLOCK);
+}
+
+static int tsem_sb_remount(struct super_block *sb, void *mnt_opts)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "fstype=%s, type=%s",
+ sb->s_type->name, sb->s_root->d_name.name);
+ return return_trapped_task(TSEM_SB_REMOUNT, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SB_REMOUNT, NOLOCK);
+}
+
+static int tsem_sb_pivotroot(const struct path *old_path,
+ const struct path *new_path)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "%s -> %s",
+ old_path->dentry->d_name.name,
+ new_path->dentry->d_name.name);
+ return return_trapped_task(TSEM_SB_PIVOTROOT, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SB_PIVOTROOT, NOLOCK);
+}
+
+static int tsem_sb_statfs(struct dentry *dentry)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "name=%s", dentry->d_name.name);
+ return return_trapped_task(TSEM_SB_STATFS, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SB_STATFS, NOLOCK);
+}
+
+static int tsem_move_mount(const struct path *from_path,
+ const struct path *to_path)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "%s -> %s",
+ from_path->dentry->d_name.name,
+ to_path->dentry->d_name.name);
+ return return_trapped_task(TSEM_MOVE_MOUNT, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_MOVE_MOUNT, NOLOCK);
+}
+
+static int tsem_shm_associate(struct kern_ipc_perm *perm, int shmflg)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "id=%d, mode=%u, flags=%d",
+ perm->id, perm->mode, shmflg);
+ return return_trapped_task(TSEM_SHM_ASSOCIATE, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_SHM_ASSOCIATE, LOCKED);
+}
+
+static int tsem_shm_shmctl(struct kern_ipc_perm *perm, int cmd)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "id=%d, mode=%u, cmd=%d",
+ perm->id, perm->mode, cmd);
+ return return_trapped_task(TSEM_SHM_SHMCTL, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_SHM_SHMCTL, LOCKED);
+}
+
+static int tsem_shm_shmat(struct kern_ipc_perm *perm, char __user *shmaddr,
+ int shmflg)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "id=%d, mode=%u, flag=%d",
+ perm->id, perm->mode, shmflg);
+ return return_trapped_task(TSEM_SHM_SHMAT, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_SHM_SHMAT, LOCKED);
+}
+
+static int tsem_sem_associate(struct kern_ipc_perm *perm, int semflg)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "id=%d, mode=%u, flag=%d",
+ perm->id, perm->mode, semflg);
+ return return_trapped_task(TSEM_SEM_ASSOCIATE, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_SEM_ASSOCIATE, LOCKED);
+}
+
+static int tsem_sem_semctl(struct kern_ipc_perm *perm, int cmd)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "id=%d, mode=%u, cmd=%d",
+ perm->id, perm->mode, cmd);
+ return return_trapped_task(TSEM_SEM_SEMCTL, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_SEM_SEMCTL, LOCKED);
+}
+
+static int tsem_sem_semop(struct kern_ipc_perm *perm, struct sembuf *sops,
+ unsigned int nsops, int alter)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "id=%d, mode=%u, nsops=%u, alter=%d", perm->id,
+ perm->mode, nsops, alter);
+ return return_trapped_task(TSEM_SEM_SEMOP, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_SEM_SEMOP, LOCKED);
+}
+
+static int tsem_syslog(int type)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "type=%d", type);
+ return return_trapped_task(TSEM_SYSLOG, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SYSLOG, NOLOCK);
+}
+
+static int tsem_settime(const struct timespec64 *ts, const struct timezone *tz)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "secs=%lld, nsecs=%ld, mwest=%d, dsttime=%d",
+ ts->tv_sec, ts->tv_nsec, tz->tz_minuteswest,
+ tz->tz_dsttime);
+ return return_trapped_task(TSEM_SETTIME, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_SETTIME, NOLOCK);
+}
+
+static int tsem_quotactl(int cmds, int type, int id, struct super_block *sb)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "cmds=%d, type=%d, id=%d, fstype=%s, type=%s", cmds,
+ type, id, sb->s_type->name, sb->s_root->d_name.name);
+ return return_trapped_task(TSEM_QUOTACTL, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_QUOTACTL, NOLOCK);
+}
+
+static int tsem_quota_on(struct dentry *dentry)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "name=%s", dentry->d_name.name);
+ return return_trapped_task(TSEM_QUOTA_ON, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_QUOTA_ON, NOLOCK);
+}
+
+static int tsem_msg_queue_associate(struct kern_ipc_perm *perm, int msqflg)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "id=%d, mode=%u, msqflg=%d", perm->id, perm->mode,
+ msqflg);
+ return return_trapped_task(TSEM_MSG_QUEUE_ASSOCIATE, msg,
+ LOCKED);
+ }
+
+ return model_generic_event(TSEM_MSG_QUEUE_ASSOCIATE, LOCKED);
+}
+
+static int tsem_msg_queue_msgsnd(struct kern_ipc_perm *perm,
+ struct msg_msg *msgmsg, int msqflg)
+
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "id=%d, mode=%u, msqflg=%d", perm->id, perm->mode,
+ msqflg);
+ return return_trapped_task(TSEM_MSG_QUEUE_MSGSND, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_MSG_QUEUE_MSGSND, LOCKED);
+}
+
+static int tsem_msg_queue_msgctl(struct kern_ipc_perm *perm, int cmd)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "id=%d, mode=%u, cmd=%d", perm->id, perm->mode,
+ cmd);
+ return return_trapped_task(TSEM_MSG_QUEUE_MSGCTL, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_MSG_QUEUE_MSGCTL, LOCKED);
+}
+
+static int tsem_msg_queue_msgrcv(struct kern_ipc_perm *perm,
+ struct msg_msg *msgmsg,
+ struct task_struct *target, long type,
+ int mode)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "id=%d, mode=%u, target=%s, type=%ld, mode=%d",
+ perm->id, perm->mode, target->comm, type, mode);
+ return return_trapped_task(TSEM_MSG_QUEUE_MSGRCV, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_MSG_QUEUE_MSGRCV, LOCKED);
+}
+
+static int tsem_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "uid=%d, gid=%d, mode=%u, flag=%u",
+ from_kuid(&init_user_ns, ipcp->uid),
+ from_kgid(&init_user_ns, ipcp->gid), ipcp->mode,
+ flag);
+ return return_trapped_task(TSEM_IPC_PERMISSION, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_IPC_PERMISSION, LOCKED);
+}
+
+#ifdef CONFIG_KEYS
+static int tsem_key_alloc(struct key *key, const struct cred *cred,
+ unsigned long flags)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "uid=%d, gid=%d, euid=%d, egid=%d, flags=%lu",
+ from_kuid(&init_user_ns, cred->uid),
+ from_kgid(&init_user_ns, cred->gid),
+ from_kuid(&init_user_ns, cred->euid),
+ from_kgid(&init_user_ns, cred->egid), flags);
+ return return_trapped_task(TSEM_KEY_ALLOC, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_KEY_ALLOC, NOLOCK);
+}
+
+static int tsem_key_permission(key_ref_t key_ref, const struct cred *cred,
+ unsigned int perm)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "uid=%d, gid=%d, euid=%d, egid=%d, perm=%u",
+ from_kuid(&init_user_ns, cred->uid),
+ from_kgid(&init_user_ns, cred->gid),
+ from_kuid(&init_user_ns, cred->euid),
+ from_kgid(&init_user_ns, cred->egid), perm);
+ return return_trapped_task(TSEM_KEY_PERMISSION, msg, LOCKED);
+ }
+
+ return model_generic_event(TSEM_KEY_PERMISSION, LOCKED);
+}
+#endif
+
+static int tsem_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+ struct scm_creds *cred;
+
+ if (tsem_task_untrusted(current)) {
+ cred = NETLINK_CREDS(skb);
+ scnprintf(msg, sizeof(msg),
+ "uid=%d, gid=%d",
+ from_kuid(&init_user_ns, cred->uid),
+ from_kgid(&init_user_ns, cred->gid));
+ return return_trapped_task(TSEM_NETLINK_SEND, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_NETLINK_SEND, NOLOCK);
+}
+
+static int tsem_inode_create(struct inode *dir,
+ struct dentry *dentry, umode_t mode)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "name=%s, mode=%u",
+ dentry->d_name.name, mode);
+ return return_trapped_inode(TSEM_INODE_CREATE, dir, msg,
+ NOLOCK);
+ }
+
+ if (bypass_inode(dir))
+ return 0;
+ return model_generic_event(TSEM_INODE_CREATE, NOLOCK);
+}
+
+static int tsem_inode_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "old_name=%s, new_name=%s",
+ old_dentry->d_name.name, new_dentry->d_name.name);
+ return return_trapped_task(TSEM_INODE_LINK, msg, NOLOCK);
+ }
+
+ if (bypass_inode(dir))
+ return 0;
+ return model_generic_event(TSEM_INODE_LINK, NOLOCK);
+}
+
+static int tsem_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s", dentry->d_name.name);
+ return return_trapped_inode(TSEM_INODE_UNLINK, dir, msg,
+ NOLOCK);
+ }
+
+ if (bypass_inode(dir))
+ return 0;
+ return model_generic_event(TSEM_INODE_UNLINK, NOLOCK);
+}
+
+static int tsem_inode_symlink(struct inode *dir, struct dentry *dentry,
+ const char *old_name)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s", dentry->d_name.name);
+ return return_trapped_task(TSEM_INODE_SYMLINK, msg, NOLOCK);
+ }
+
+ if (bypass_inode(dir))
+ return 0;
+ return model_generic_event(TSEM_INODE_SYMLINK, NOLOCK);
+}
+
+static int tsem_inode_mkdir(struct inode *dir, struct dentry *dentry,
+ umode_t mode)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "target=%s, mode=%u",
+ dentry->d_name.name, mode);
+ return return_trapped_task(TSEM_INODE_MKDIR, msg, NOLOCK);
+ }
+
+ if (bypass_inode(dir))
+ return 0;
+ return model_generic_event(TSEM_INODE_MKDIR, NOLOCK);
+}
+
+static int tsem_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "name=%s", dentry->d_name.name);
+ return return_trapped_task(TSEM_INODE_RMDIR, msg, NOLOCK);
+ }
+
+ if (bypass_inode(dir))
+ return 0;
+ return model_generic_event(TSEM_INODE_RMDIR, NOLOCK);
+}
+
+static int tsem_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "old=%s, new=%s",
+ old_dentry->d_name.name, new_dentry->d_name.name);
+ return return_trapped_task(TSEM_INODE_RENAME, msg, NOLOCK);
+ }
+
+ if (bypass_inode(old_dir))
+ return 0;
+ return model_generic_event(TSEM_INODE_RENAME, NOLOCK);
+}
+
+static int tsem_inode_mknod(struct inode *dir, struct dentry *dentry,
+ umode_t mode, dev_t dev)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "name=%s, mode=%u, dev=%u",
+ dentry->d_name.name, mode, dev);
+ return return_trapped_task(TSEM_INODE_MKNOD, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_INODE_MKNOD, NOLOCK);
+}
+
+static int tsem_inode_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "name=%s, mode=%u, uid=%d, gid=%d, size=%llu",
+ dentry->d_name.name, attr->ia_mode,
+ from_kuid(&init_user_ns, attr->ia_uid),
+ from_kgid(&init_user_ns, attr->ia_gid),
+ attr->ia_size);
+ return return_trapped_task(TSEM_INODE_SETATTR, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_INODE_SETATTR, NOLOCK);
+}
+
+static int tsem_inode_getattr(const struct path *path)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (unlikely(!tsem_ready))
+ return 0;
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "name=%s",
+ path->dentry->d_name.name);
+ return return_trapped_task(TSEM_INODE_GETATTR, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_INODE_GETATTR, NOLOCK);
+}
+
+static int tsem_inode_setxattr(struct mnt_idmap *idmap,
+ struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "fname=%s, name=%s, size=%lu, flags=%d",
+ dentry->d_name.name, name, size, flags);
+ return return_trapped_task(TSEM_INODE_SETXATTR, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_INODE_SETXATTR, NOLOCK);
+}
+
+static int tsem_inode_getxattr(struct dentry *dentry, const char *name)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg),
+ "fname=%s, name=%s", dentry->d_name.name, name);
+ return return_trapped_task(TSEM_INODE_GETXATTR, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_INODE_GETXATTR, NOLOCK);
+}
+
+static int tsem_inode_listxattr(struct dentry *dentry)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "fname=%s", dentry->d_name.name);
+ return return_trapped_task(TSEM_INODE_LISTXATTR, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_INODE_LISTXATTR, NOLOCK);
+}
+
+static int tsem_inode_removexattr(struct mnt_idmap *idmap,
+ struct dentry *dentry, const char *name)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "fname=%s, name=%s",
+ dentry->d_name.name, name);
+ return return_trapped_task(TSEM_INODE_REMOVEXATTR, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_INODE_REMOVEXATTR, NOLOCK);
+}
+
+static int tsem_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "fname=%s", dentry->d_name.name);
+ return return_trapped_task(TSEM_INODE_KILLPRIV, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_INODE_KILLPRIV, NOLOCK);
+}
+
+static int tsem_tun_dev_create(void)
+{
+ if (tsem_task_untrusted(current))
+ return return_trapped_task(TSEM_TUN_DEV_CREATE, "none",
+ NOLOCK);
+
+ return model_generic_event(TSEM_TUN_DEV_CREATE, NOLOCK);
+}
+
+static int tsem_tun_dev_attach_queue(void *security)
+{
+ if (tsem_task_untrusted(current))
+ return return_trapped_task(TSEM_TUN_DEV_ATTACH_QUEUE, "none",
+ NOLOCK);
+
+ return model_generic_event(TSEM_TUN_DEV_ATTACH_QUEUE, NOLOCK);
+}
+
+static int tsem_tun_dev_attach(struct sock *sk, void *security)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "family=%u", sk->sk_family);
+ return return_trapped_task(TSEM_TUN_DEV_ATTACH, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_TUN_DEV_ATTACH, NOLOCK);
+}
+
+static int tsem_tun_dev_open(void *security)
+{
+ if (tsem_task_untrusted(current))
+ return return_trapped_task(TSEM_TUN_DEV_OPEN, "none", NOLOCK);
+
+ return model_generic_event(TSEM_TUN_DEV_OPEN, NOLOCK);
+}
+
+#ifdef CONFIG_BPF_SYSCALL
+static int tsem_bpf(int cmd, union bpf_attr *attr, unsigned int size)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "cmd=%d, size=%u", cmd, size);
+ return return_trapped_task(TSEM_BPF, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_BPF, NOLOCK);
+}
+
+static int tsem_bpf_map(struct bpf_map *map, fmode_t fmode)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "type=%d, size=%u", map->map_type,
+ fmode);
+ return return_trapped_task(TSEM_BPF_MAP, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_BPF_MAP, NOLOCK);
+}
+
+static int tsem_bpf_prog(struct bpf_prog *prog)
+{
+ char msg[TRAPPED_MSG_LENGTH];
+
+ if (tsem_task_untrusted(current)) {
+ scnprintf(msg, sizeof(msg), "type=%d", prog->type);
+ return return_trapped_task(TSEM_BPF_PROG, msg, NOLOCK);
+ }
+
+ return model_generic_event(TSEM_BPF_PROG, NOLOCK);
+}
+#endif
+
+static struct security_hook_list tsem_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(task_alloc, tsem_task_alloc),
+ LSM_HOOK_INIT(task_free, tsem_task_free),
+ LSM_HOOK_INIT(task_kill, tsem_task_kill),
+ LSM_HOOK_INIT(task_setpgid, tsem_task_setpgid),
+ LSM_HOOK_INIT(task_getpgid, tsem_task_getpgid),
+ LSM_HOOK_INIT(task_getsid, tsem_task_getsid),
+ LSM_HOOK_INIT(task_setnice, tsem_task_setnice),
+ LSM_HOOK_INIT(task_setioprio, tsem_task_setioprio),
+ LSM_HOOK_INIT(task_getioprio, tsem_task_getioprio),
+ LSM_HOOK_INIT(task_prlimit, tsem_task_prlimit),
+ LSM_HOOK_INIT(task_setrlimit, tsem_task_setrlimit),
+ LSM_HOOK_INIT(task_setscheduler, tsem_task_setscheduler),
+ LSM_HOOK_INIT(task_getscheduler, tsem_task_getscheduler),
+ LSM_HOOK_INIT(task_prctl, tsem_task_prctl),
+
+ LSM_HOOK_INIT(ptrace_traceme, tsem_ptrace_traceme),
+
+ LSM_HOOK_INIT(bprm_creds_for_exec, tsem_bprm_creds_for_exec),
+ LSM_HOOK_INIT(inode_alloc_security, tsem_inode_alloc_security),
+ LSM_HOOK_INIT(inode_free_security, tsem_inode_free_security),
+
+ LSM_HOOK_INIT(file_open, tsem_file_open),
+ LSM_HOOK_INIT(mmap_file, tsem_mmap_file),
+ LSM_HOOK_INIT(file_ioctl, tsem_file_ioctl),
+ LSM_HOOK_INIT(file_lock, tsem_file_lock),
+ LSM_HOOK_INIT(file_fcntl, tsem_file_fcntl),
+ LSM_HOOK_INIT(file_receive, tsem_file_receive),
+
+ LSM_HOOK_INIT(unix_stream_connect, tsem_unix_stream_connect),
+ LSM_HOOK_INIT(unix_may_send, tsem_unix_may_send),
+
+ LSM_HOOK_INIT(socket_create, tsem_socket_create),
+ LSM_HOOK_INIT(socket_connect, tsem_socket_connect),
+ LSM_HOOK_INIT(socket_bind, tsem_socket_bind),
+ LSM_HOOK_INIT(socket_accept, tsem_socket_accept),
+ LSM_HOOK_INIT(socket_listen, tsem_socket_listen),
+ LSM_HOOK_INIT(socket_socketpair, tsem_socket_socketpair),
+ LSM_HOOK_INIT(socket_sendmsg, tsem_socket_sendmsg),
+ LSM_HOOK_INIT(socket_recvmsg, tsem_socket_recvmsg),
+ LSM_HOOK_INIT(socket_getsockname, tsem_socket_getsockname),
+ LSM_HOOK_INIT(socket_getpeername, tsem_socket_getpeername),
+ LSM_HOOK_INIT(socket_setsockopt, tsem_socket_setsockopt),
+ LSM_HOOK_INIT(socket_shutdown, tsem_socket_shutdown),
+
+ LSM_HOOK_INIT(kernel_module_request, tsem_kernel_module_request),
+ LSM_HOOK_INIT(kernel_load_data, tsem_kernel_load_data),
+ LSM_HOOK_INIT(kernel_read_file, tsem_kernel_read_file),
+
+ LSM_HOOK_INIT(sb_mount, tsem_sb_mount),
+ LSM_HOOK_INIT(sb_umount, tsem_sb_umount),
+ LSM_HOOK_INIT(sb_remount, tsem_sb_remount),
+ LSM_HOOK_INIT(sb_pivotroot, tsem_sb_pivotroot),
+ LSM_HOOK_INIT(sb_statfs, tsem_sb_statfs),
+ LSM_HOOK_INIT(move_mount, tsem_move_mount),
+
+ LSM_HOOK_INIT(shm_associate, tsem_shm_associate),
+ LSM_HOOK_INIT(shm_shmctl, tsem_shm_shmctl),
+ LSM_HOOK_INIT(shm_shmat, tsem_shm_shmat),
+ LSM_HOOK_INIT(sem_associate, tsem_sem_associate),
+ LSM_HOOK_INIT(sem_semctl, tsem_sem_semctl),
+ LSM_HOOK_INIT(sem_semop, tsem_sem_semop),
+
+ LSM_HOOK_INIT(syslog, tsem_syslog),
+ LSM_HOOK_INIT(settime, tsem_settime),
+
+ LSM_HOOK_INIT(quotactl, tsem_quotactl),
+ LSM_HOOK_INIT(quota_on, tsem_quota_on),
+
+ LSM_HOOK_INIT(msg_queue_associate, tsem_msg_queue_associate),
+ LSM_HOOK_INIT(msg_queue_msgctl, tsem_msg_queue_msgctl),
+ LSM_HOOK_INIT(msg_queue_msgsnd, tsem_msg_queue_msgsnd),
+ LSM_HOOK_INIT(msg_queue_msgrcv, tsem_msg_queue_msgrcv),
+
+ LSM_HOOK_INIT(ipc_permission, tsem_ipc_permission),
+
+#ifdef CONFIG_KEYS
+ LSM_HOOK_INIT(key_alloc, tsem_key_alloc),
+ LSM_HOOK_INIT(key_permission, tsem_key_permission),
+#endif
+
+ LSM_HOOK_INIT(netlink_send, tsem_netlink_send),
+
+ LSM_HOOK_INIT(inode_create, tsem_inode_create),
+ LSM_HOOK_INIT(inode_link, tsem_inode_link),
+ LSM_HOOK_INIT(inode_unlink, tsem_inode_unlink),
+ LSM_HOOK_INIT(inode_symlink, tsem_inode_symlink),
+ LSM_HOOK_INIT(inode_mkdir, tsem_inode_mkdir),
+ LSM_HOOK_INIT(inode_rmdir, tsem_inode_rmdir),
+ LSM_HOOK_INIT(inode_mknod, tsem_inode_mknod),
+ LSM_HOOK_INIT(inode_rename, tsem_inode_rename),
+ LSM_HOOK_INIT(inode_setattr, tsem_inode_setattr),
+ LSM_HOOK_INIT(inode_getattr, tsem_inode_getattr),
+ LSM_HOOK_INIT(inode_setxattr, tsem_inode_setxattr),
+ LSM_HOOK_INIT(inode_getxattr, tsem_inode_getxattr),
+ LSM_HOOK_INIT(inode_listxattr, tsem_inode_listxattr),
+ LSM_HOOK_INIT(inode_removexattr, tsem_inode_removexattr),
+ LSM_HOOK_INIT(inode_killpriv, tsem_inode_killpriv),
+
+ LSM_HOOK_INIT(tun_dev_create, tsem_tun_dev_create),
+ LSM_HOOK_INIT(tun_dev_attach_queue, tsem_tun_dev_attach_queue),
+ LSM_HOOK_INIT(tun_dev_attach, tsem_tun_dev_attach),
+ LSM_HOOK_INIT(tun_dev_open, tsem_tun_dev_open),
+
+#ifdef CONFIG_BPF_SYSCALL
+ LSM_HOOK_INIT(bpf, tsem_bpf),
+ LSM_HOOK_INIT(bpf_map, tsem_bpf_map),
+ LSM_HOOK_INIT(bpf_prog, tsem_bpf_prog)
+#endif
+};
+
+static int configure_root_digest(void)
+{
+ int retn = 0;
+ char *digest = NULL;
+ u8 zero_digest[HASH_MAX_DIGESTSIZE];
+ unsigned int digestsize;
+ struct crypto_shash *tfm;
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ if (default_hash_function && crypto_has_shash(default_hash_function,
+ 0, 0)) {
+ digest = default_hash_function;
+ pr_warn("tsem: Using digest %s from command-line.\n", digest);
+ }
+ if (!digest && default_hash_function)
+ pr_warn("tsem: Unknown root digest %s, using sha256.\n",
+ default_hash_function);
+ if (!digest)
+ digest = "sha256";
+
+ tsem_context(current)->digestname = kstrdup(digest, GFP_KERNEL);
+ if (!tsem_context(current)->digestname)
+ return -ENOMEM;
+
+ tfm = crypto_alloc_shash(digest, 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ shash->tfm = tfm;
+ retn = crypto_shash_digest(shash, NULL, 0, zero_digest);
+ if (retn)
+ goto done;
+
+ tsem_context(current)->tfm = tfm;
+ memcpy(root_context.zero_digest, zero_digest, digestsize);
+
+ done:
+ if (retn) {
+ kfree(tsem_context(current)->digestname);
+ crypto_free_shash(tfm);
+ }
+
+ return retn;
+}
+
+static int __init set_ready(void)
+{
+ int retn;
+
+ if (!tsem_available)
+ return 0;
+
+ retn = configure_root_digest();
+ if (retn)
+ goto done;
+
+ retn = tsem_model_add_aggregate();
+ if (retn)
+ goto done;
+
+ retn = tsem_fs_init();
+ if (retn)
+ goto done;
+
+ tsem_ready = 1;
+ pr_info("tsem: Now ready for modeling.\n");
+
+ done:
+ return retn;
+}
+
+late_initcall(set_ready);
+
+/**
+ * tesm_init() - Register Trusted Security Event Modeling LSM.
+ *
+ * This function is responsible for initializing the TSEM LSM. It is
+ * invoked at the fs_initcall level. In addition to configuring the
+ * LSM hooks this function initializes the Trusted Modeling Agent
+ * context including the event actions. The cache from which
+ * the tsem_event description structures is also initialized.
+ *
+ * Return: If the TSEM LSM is successfully initialized a value of zero
+ * is returned. A non-zero error code is returned if
+ * initialization fails. Currently the only failure mode can
+ * come from the initialization of the tsem_event cache.
+ */
+static int __init tsem_init(void)
+{
+ int retn;
+ struct tsem_task *tsk = tsem_task(current);
+ struct tsem_context *ctx = &root_context;
+ struct tsem_model *model = &root_model;
+
+ security_add_hooks(tsem_hooks, ARRAY_SIZE(tsem_hooks), "tsem");
+
+ tsk->context = ctx;
+ kref_init(&ctx->kref);
+ kref_get(&ctx->kref);
+
+ root_context.model = &root_model;
+
+ retn = tsem_event_cache_init();
+ if (retn)
+ return retn;
+ retn = tsem_event_magazine_allocate(ctx, magazine_size);
+ if (retn)
+ goto done;
+
+ memcpy(ctx->actions, tsem_root_actions, sizeof(tsem_root_actions));
+
+ retn = tsem_model_cache_init(model, magazine_size);
+ if (retn)
+ goto done;
+
+ retn = tsem_export_cache_init();
+ if (retn)
+ goto done;
+
+ pr_info("tsem: Initialized %s modeling.\n",
+ no_root_modeling ? "domain only" : "full");
+ tsem_available = true;
+ tsk->trust_status = TSEM_TASK_TRUSTED;
+ retn = 0;
+
+ done:
+ if (retn) {
+ tsem_event_magazine_free(ctx);
+ tsem_model_magazine_free(model);
+ }
+ return retn;
+}
+
+DEFINE_LSM(tsem) = {
+ .name = "tsem",
+ .init = tsem_init,
+ .blobs = &tsem_blob_sizes,
+};
--
2.39.1
The fs.c file contains the implementation of the TSEM control
plane that is rooted in the following directory in the securityfs
filesystem:
/sys/kernel/security/tsem
The following file documents the interface provided by the
control plane:
Documentation/ABI/testing/tsem
The pseudo-files act on the modeling context of the process that
is reading or writing to the control plane files. For example,
reading the 'id' pseudo-file, returns the security modeling
namespace identifier that the process is running in.
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.
The ExternalTMA directory is used to segregate 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 following directory:
/sys/kernel/security/tsem/InternalTMA
Holds directories that are used to surface the characteristics of
internal Trusted Modeling agents. There is currently only a
single internal TMA implemented and its characteristics are in
the following directory:
/sys/kernel/security/tsem/InternalTMA/model0
This model is used to allow new TMA's to be implemented and to
maintain ABI compatibility if changes are needed in a TMA.
Signed-off-by: Greg Wettstein <[email protected]>
---
security/tsem/fs.c | 1336 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1336 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..9752aca13afa
--- /dev/null
+++ b/security/tsem/fs.c
@@ -0,0 +1,1336 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (C) 2023 Enjellic Systems Development, LLC
+ * Author: Dr. Greg Wettstein <[email protected]>
+ *
+ * Implements the securityfs based control plane.
+ */
+
+#include <linux/seq_file.h>
+#include <linux/poll.h>
+
+#include "tsem.h"
+
+static struct dentry *tsem_dir;
+static struct dentry *control;
+static struct dentry *id;
+static struct dentry *aggregate;
+static struct dentry *internal_tma;
+static struct dentry *model;
+static struct dentry *forensics;
+static struct dentry *forensics_counts;
+static struct dentry *forensics_coeff;
+static struct dentry *trajectory;
+static struct dentry *trajectory_counts;
+static struct dentry *trajectory_coeff;
+static struct dentry *measurement;
+static struct dentry *state;
+static struct dentry *external_tma;
+
+struct control_commands {
+ char *cmd;
+ enum tsem_control_type type;
+};
+
+static const char * const control_commands[] = {
+ "internal",
+ "external",
+ "enforce",
+ "seal",
+ "trusted",
+ "untrusted",
+ "state",
+ "pseudonym",
+ "base"
+};
+
+enum namespace_argument_type {
+ NS_REF = 0,
+ NS_DIGEST,
+ NS_KEY,
+ NS_CACHE
+};
+
+static const char * const namespace_arguments[] = {
+ "nsref",
+ "digest",
+ "key",
+ "cache"
+};
+
+enum control_argument_type {
+ CONTROL_KEY = 0,
+ CONTROL_PID
+};
+
+static const char * const control_arguments[] = {
+ "key",
+ "pid"
+};
+
+static bool can_access_fs(void)
+{
+ struct tsem_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(unsigned long cmd, pid_t pid, char *keystr)
+{
+ bool wakeup = false;
+ int retn = -ESRCH;
+ u8 event_key[HASH_MAX_DIGESTSIZE];
+ struct task_struct *COE;
+ struct tsem_task *task;
+ struct tsem_task *tma = tsem_task(current);
+
+ rcu_read_lock();
+ COE = find_task_by_vpid(pid);
+ if (COE != NULL) {
+ task = tsem_task(COE);
+ if (tsem_context(COE)->id != tma->tma_for_ns) {
+ retn = -EINVAL;
+ goto done;
+ }
+
+ retn = tsem_ns_event_key(task->task_key, keystr, event_key);
+ if (retn)
+ goto done;
+
+ if (memcmp(tma->task_key, event_key, tsem_digestsize())) {
+ retn = -EINVAL;
+ goto done;
+ }
+
+ 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;
+ }
+
+ done:
+ rcu_read_unlock();
+
+ if (retn == -EINVAL)
+ pr_warn("tsem: Invalid process release request.\n");
+
+ if (wakeup)
+ wake_up_process(COE);
+
+ return retn;
+}
+
+static int config_COE(unsigned long cmd, char *arg)
+{
+ char **argv, *argp, *key = NULL;
+ int argc, retn = -EINVAL;
+ unsigned int lp;
+ long pid = 0;
+ enum control_argument_type control_arg;
+
+ if (!*arg)
+ return retn;
+
+ argv = argv_split(GFP_KERNEL, arg, &argc);
+ if (!argv)
+ return -ENOMEM;
+
+ for (lp = 0; lp < argc; ++lp) {
+ argp = strchr(argv[lp], '=');
+ if (!argp)
+ goto done;
+ *argp++ = '\0';
+
+ control_arg = match_string(control_arguments,
+ ARRAY_SIZE(control_arguments),
+ argv[lp]);
+ if (control_arg < 0)
+ goto done;
+
+ switch (control_arg) {
+ case CONTROL_KEY:
+ key = argp;
+ if (strlen(key) != tsem_digestsize()*2)
+ goto done;
+ break;
+ case CONTROL_PID:
+ if (kstrtol(argp, 0, &pid))
+ goto done;
+ break;
+ }
+ }
+
+ if (!key || !pid)
+ goto done;
+ retn = control_COE(cmd, pid, key);
+
+ done:
+ argv_free(argv);
+ return retn;
+}
+
+static int config_context(unsigned long cmd, char *bufr)
+{
+ int retn = -EINVAL;
+ unsigned int lp;
+ struct tsem_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, char *arg)
+{
+ char *argp;
+ int retn = -EINVAL;
+ u8 mapping[HASH_MAX_DIGESTSIZE];
+
+ if (!arg)
+ goto done;
+
+ argp = strchr(arg, '=');
+ if (!argp)
+ goto done;
+ *argp++ = '\0';
+
+ if (strcmp(arg, "value"))
+ goto done;
+
+ if (strlen(argp) != tsem_digestsize()*2)
+ goto done;
+ if (hex2bin(mapping, argp, tsem_digestsize()))
+ 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 int config_namespace(enum tsem_control_type type, const char *arg)
+{
+ char **argv, *argp, *digest = "sha256", *key = NULL;
+ int argc, retn = -EINVAL;
+ unsigned int lp, cache_size = TSEM_MAGAZINE_SIZE_INTERNAL;
+ enum namespace_argument_type ns_arg;
+ enum tsem_ns_reference ns_ref = TSEM_NS_INITIAL;
+
+ if (type == TSEM_CONTROL_EXTERNAL)
+ cache_size = TSEM_MAGAZINE_SIZE_EXTERNAL;
+
+ if (!arg) {
+ if (type == TSEM_CONTROL_EXTERNAL)
+ return retn;
+ return tsem_ns_create(type, digest, ns_ref, key, cache_size);
+ }
+
+ argv = argv_split(GFP_KERNEL, arg, &argc);
+ if (!argv)
+ return -ENOMEM;
+
+ for (lp = 0; lp < argc; ++lp) {
+ argp = strchr(argv[lp], '=');
+ if (!argp)
+ goto done;
+ *argp++ = '\0';
+
+ ns_arg = match_string(namespace_arguments,
+ ARRAY_SIZE(namespace_arguments), argv[lp]);
+ if (ns_arg < 0)
+ goto done;
+
+ switch (ns_arg) {
+ case NS_REF:
+ if (!strcmp(argp, "current"))
+ ns_ref = TSEM_NS_CURRENT;
+ else if (!strcmp(argp, "initial"))
+ ns_ref = TSEM_NS_INITIAL;
+ else
+ goto done;
+ break;
+ case NS_DIGEST:
+ digest = argp;
+ if (!crypto_has_shash(digest, 0, 0))
+ goto done;
+ break;
+ case NS_KEY:
+ key = argp;
+ if (strlen(key) % 2)
+ goto done;
+ break;
+ case NS_CACHE:
+ if (kstrtouint(argp, 0, &cache_size))
+ goto done;
+ if (!cache_size)
+ goto done;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (type == TSEM_CONTROL_EXTERNAL && !key)
+ goto done;
+
+ retn = tsem_ns_create(type, digest, ns_ref, key, cache_size);
+
+ done:
+ argv_free(argv);
+ return retn;
+}
+
+static void show_event(struct seq_file *c, struct tsem_event *ep)
+{
+ tsem_fs_show_field(c, "event");
+ if (ep->pid)
+ tsem_fs_show_key(c, ",", "pid", "%u", ep->pid);
+ tsem_fs_show_key(c, ",", "process", "%s", ep->comm);
+ tsem_fs_show_key(c, ",", "type", "%s", tsem_names[ep->event]);
+ tsem_fs_show_key(c, "}, ", "task_id", "%*phN", tsem_digestsize(),
+ ep->task_id);
+
+ tsem_fs_show_field(c, "COE");
+ tsem_fs_show_key(c, ",", "uid", "%u", ep->COE.uid);
+ tsem_fs_show_key(c, ",", "euid", "%u", ep->COE.euid);
+ tsem_fs_show_key(c, ",", "suid", "%u", ep->COE.suid);
+ tsem_fs_show_key(c, ",", "gid", "%u", ep->COE.gid);
+ tsem_fs_show_key(c, ",", "egid", "%u", ep->COE.egid);
+ tsem_fs_show_key(c, ",", "sgid", "%u", ep->COE.sgid);
+ tsem_fs_show_key(c, ",", "fsuid", "%u", ep->COE.fsuid);
+ tsem_fs_show_key(c, ",", "fsgid", "%u", ep->COE.fsgid);
+ tsem_fs_show_key(c, "}, ", "capeff", "0x%llx", ep->COE.capeff.value);
+}
+
+static void show_file(struct seq_file *c, struct tsem_event *ep)
+{
+ if (ep->event == TSEM_FILE_OPEN)
+ tsem_fs_show_field(c, "file_open");
+ else
+ tsem_fs_show_field(c, "file");
+
+ tsem_fs_show_key(c, ",", "flags", "%u", ep->file.flags);
+ tsem_fs_show_key(c, ",", "uid", "%u", ep->file.uid);
+ tsem_fs_show_key(c, ",", "gid", "%u", ep->file.gid);
+ tsem_fs_show_key(c, ",", "mode", "0%o", ep->file.mode);
+ tsem_fs_show_key(c, ",", "path", "%s", ep->pathname);
+ tsem_fs_show_key(c, ",", "s_magic", "0x%0x", ep->file.s_magic);
+ tsem_fs_show_key(c, ",", "s_id", "%s", ep->file.s_id);
+ tsem_fs_show_key(c, ",", "s_uuid", "%*phN", sizeof(ep->file.s_uuid),
+ ep->file.s_uuid);
+ tsem_fs_show_key(c, "}", "digest", "%*phN", tsem_digestsize(),
+ 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);
+
+ tsem_fs_show_field(c, tsem_names[ep->event]);
+ tsem_fs_show_key(c, ",", "type", "%u", args->file == NULL);
+ tsem_fs_show_key(c, ",", "reqprot", "%u", args->reqprot);
+ tsem_fs_show_key(c, ",", "prot", "%u", args->prot);
+
+ if (args->file) {
+ tsem_fs_show_key(c, ",", "flags", "%u", args->flags);
+ show_file(c, ep);
+ seq_putc(c, '}');
+ } else
+ tsem_fs_show_key(c, "}", "flags", "%u", args->flags);
+}
+
+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);
+
+ tsem_fs_show_field(c, tsem_names[ep->event]);
+ tsem_fs_show_key(c, ",", "family", "%u", args->family);
+ tsem_fs_show_key(c, ",", "type", "%u", args->type);
+ tsem_fs_show_key(c, ",", "protocol", "%u", args->protocol);
+ tsem_fs_show_key(c, "}", "kern", "%u", 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);
+
+ tsem_fs_show_field(c, tsem_names[ep->event]);
+ tsem_fs_show_key(c, ",", "family", "%u", scp->family);
+
+ switch (scp->family) {
+ case AF_INET:
+ ipv4 = (struct sockaddr_in *) &scp->u.ipv4;
+ tsem_fs_show_key(c, ",", "port", "%u", ipv4->sin_port);
+ tsem_fs_show_key(c, "}", "addr", "%u", ipv4->sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ ipv6 = (struct sockaddr_in6 *) &scp->u.ipv6;
+ tsem_fs_show_key(c, ",", "port", "%u", ipv6->sin6_port);
+ tsem_fs_show_key(c, ",", "flow", "%u", ipv6->sin6_flowinfo);
+ tsem_fs_show_key(c, ",", "scope", "%u", ipv6->sin6_scope_id);
+ tsem_fs_show_key(c, "}", "addr", "%*phN",
+ (int) sizeof(ipv6->sin6_addr.in6_u.u6_addr8),
+ ipv6->sin6_addr.in6_u.u6_addr8);
+ break;
+ case AF_UNIX:
+ tsem_fs_show_key(c, "}", "addr", "%s", scp->u.path);
+ break;
+ default:
+ tsem_fs_show_key(c, "}", "addr", "%*phN", tsem_digestsize(),
+ 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);
+
+ tsem_fs_show_field(c, tsem_names[ep->event]);
+ tsem_fs_show_key(c, ",", "family", "%u", sap->family);
+ tsem_fs_show_key(c, ",", "type", "%u", sap->type);
+ tsem_fs_show_key(c, ",", "port", "%u", sap->port);
+
+ switch (sap->family) {
+ case AF_INET:
+ tsem_fs_show_key(c, "}", "addr", "%u", sap->u.ipv4);
+ break;
+ case AF_INET6:
+ tsem_fs_show_key(c, "}", "addr", "%*phN",
+ (int) sizeof(sap->u.ipv6.in6_u.u6_addr8),
+ sap->u.ipv6.in6_u.u6_addr8);
+ break;
+ case AF_UNIX:
+ tsem_fs_show_key(c, "}", "addr", "%s", sap->u.path);
+ break;
+ default:
+ tsem_fs_show_key(c, "}", "addr", "%*phN", tsem_digestsize(),
+ sap->u.mapping);
+ 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);
+
+ tsem_fs_show_field(c, tsem_names[ep->event]);
+ tsem_fs_show_key(c, ",", "cross", "%u", args->cross_model);
+ tsem_fs_show_key(c, ",", "signal", "%u", args->signal);
+ tsem_fs_show_key(c, "}", "target", "%*phN", tsem_digestsize(),
+ args->target);
+}
+
+static void show_event_generic(struct seq_file *c, struct tsem_event *ep)
+{
+ show_event(c, ep);
+
+ tsem_fs_show_field(c, tsem_names[ep->event]);
+ tsem_fs_show_key(c, "}", "type", "%s",
+ tsem_names[ep->CELL.event_type]);
+}
+
+static void *trajectory_start(struct seq_file *c, loff_t *pos)
+{
+ struct list_head *end;
+ struct tsem_model *model = tsem_model(current);
+
+ spin_lock(&model->trajectory_lock);
+ end = model->trajectory_list.prev;
+ spin_unlock(&model->trajectory_lock);
+
+ mutex_lock(&model->trajectory_end_mutex);
+ model->trajectory_end = end;
+
+ return seq_list_start(&model->trajectory_list, *pos);
+}
+
+static void *trajectory_next(struct seq_file *c, void *p, loff_t *pos)
+{
+ struct list_head *next = ((struct list_head *) p)->next;
+ struct tsem_model *model = tsem_model(current);
+
+ if (!model->trajectory_end) {
+ ++*pos;
+ return NULL;
+ }
+
+ if (next == model->trajectory_end)
+ model->trajectory_end = NULL;
+
+ 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_end_mutex);
+}
+
+static int trajectory_show(struct seq_file *c, void *trajectory)
+{
+ struct tsem_event *ep;
+
+ ep = list_entry(trajectory, struct tsem_event, list);
+
+ seq_putc(c, '{');
+ tsem_fs_show_trajectory(c, ep);
+ seq_puts(c, "}\n");
+
+ 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 *trajectory_count_start(struct seq_file *c, loff_t *pos)
+{
+ struct list_head *end;
+ struct tsem_model *model = tsem_model(current);
+
+ spin_lock(&model->point_lock);
+ end = model->point_list.prev;
+ spin_unlock(&model->point_lock);
+
+ mutex_lock(&model->point_end_mutex);
+ model->point_end = end;
+
+ return seq_list_start(&model->point_list, *pos);
+}
+
+static void *trajectory_count_next(struct seq_file *c, void *p, loff_t *pos)
+{
+ struct list_head *next = ((struct list_head *) p)->next;
+ struct tsem_model *model = tsem_model(current);
+
+ if (!model->point_end) {
+ ++*pos;
+ return NULL;
+ }
+
+ if (next == model->point_end)
+ model->point_end = NULL;
+
+ return seq_list_next(p, &model->point_list, pos);
+}
+
+static void trajectory_count_stop(struct seq_file *c, void *pos)
+{
+ struct tsem_model *model = tsem_model(current);
+
+ mutex_unlock(&model->point_end_mutex);
+}
+
+static int trajectory_count_show(struct seq_file *c, void *point)
+{
+ struct tsem_event_point *pt;
+
+ pt = list_entry(point, struct tsem_event_point, list);
+ if (!pt->valid)
+ return 0;
+
+ seq_printf(c, "%llu\n", pt->count);
+ return 0;
+}
+
+static const struct seq_operations trajectory_count_seqops = {
+ .start = trajectory_count_start,
+ .next = trajectory_count_next,
+ .stop = trajectory_count_stop,
+ .show = trajectory_count_show
+};
+
+static int trajectory_count_open(struct inode *inode, struct file *file)
+{
+ if (!can_access_fs())
+ return -EACCES;
+ return seq_open(file, &trajectory_count_seqops);
+}
+
+static const struct file_operations trajectory_count_ops = {
+ .open = trajectory_count_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static void *trajectory_point_start(struct seq_file *c, loff_t *pos)
+{
+ struct list_head *end;
+ struct tsem_model *model = tsem_model(current);
+
+ spin_lock(&model->point_lock);
+ end = model->point_list.prev;
+ spin_unlock(&model->point_lock);
+
+ mutex_lock(&model->point_end_mutex);
+ model->point_end = end;
+
+ return seq_list_start(&model->point_list, *pos);
+}
+
+static void *trajectory_point_next(struct seq_file *c, void *p, loff_t *pos)
+{
+ struct list_head *next = ((struct list_head *) p)->next;
+ struct tsem_model *model = tsem_model(current);
+
+ if (!model->point_end) {
+ ++*pos;
+ return NULL;
+ }
+
+ if (next == model->point_end)
+ model->point_end = NULL;
+
+ return seq_list_next(p, &model->point_list, pos);
+}
+
+static void trajectory_point_stop(struct seq_file *c, void *pos)
+{
+ struct tsem_model *model = tsem_model(current);
+
+ mutex_unlock(&model->point_end_mutex);
+}
+
+static int trajectory_point_show(struct seq_file *c, void *point)
+{
+ struct tsem_event_point *pt;
+
+ pt = list_entry(point, struct tsem_event_point, list);
+ if (!pt->valid)
+ return 0;
+
+ seq_printf(c, "%*phN\n", tsem_digestsize(), pt->point);
+ return 0;
+}
+
+static const struct seq_operations trajectory_point_seqops = {
+ .start = trajectory_point_start,
+ .next = trajectory_point_next,
+ .stop = trajectory_point_stop,
+ .show = trajectory_point_show
+};
+
+static int trajectory_point_open(struct inode *inode, struct file *file)
+{
+ if (!can_access_fs())
+ return -EACCES;
+ return seq_open(file, &trajectory_point_seqops);
+}
+
+static const struct file_operations trajectory_point_ops = {
+ .open = trajectory_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[128];
+ ssize_t retn = -EINVAL;
+ 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, ' ');
+ if (arg != NULL) {
+ *arg = '\0';
+ ++arg;
+ }
+
+ type = match_string(control_commands, ARRAY_SIZE(control_commands),
+ cmdbufr);
+ if (type < 0)
+ goto done;
+
+ switch (type) {
+ case TSEM_CONTROL_EXTERNAL:
+ case TSEM_CONTROL_INTERNAL:
+ retn = config_namespace(type, arg);
+ break;
+ case TSEM_CONTROL_ENFORCE:
+ case TSEM_CONTROL_SEAL:
+ retn = config_context(type, cmdbufr);
+ break;
+ case TSEM_CONTROL_TRUSTED:
+ case TSEM_CONTROL_UNTRUSTED:
+ retn = config_COE(type, arg);
+ 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 list_head *end;
+ struct tsem_model *model = tsem_model(current);
+
+ spin_lock(&model->forensics_lock);
+ end = model->forensics_list.prev;
+ spin_unlock(&model->forensics_lock);
+
+ mutex_lock(&model->forensics_end_mutex);
+ model->forensics_end = end;
+
+ return seq_list_start(&model->forensics_list, *pos);
+}
+
+static void *forensics_next(struct seq_file *c, void *p, loff_t *pos)
+{
+ struct list_head *next = ((struct list_head *) p)->next;
+ struct tsem_model *model = tsem_model(current);
+
+ if (!model->forensics_end) {
+ ++*pos;
+ return NULL;
+ }
+
+ if (next == model->forensics_end)
+ model->forensics_end = NULL;
+
+ 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_end_mutex);
+}
+
+static int forensics_show(struct seq_file *c, void *event)
+{
+ struct tsem_event *ep;
+
+ ep = list_entry(event, struct tsem_event, list);
+
+ seq_putc(c, '{');
+ tsem_fs_show_trajectory(c, ep);
+ seq_puts(c, "}\n");
+
+ 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 void *forensics_point_start(struct seq_file *c, loff_t *pos)
+{
+ struct list_head *end;
+ struct tsem_model *model = tsem_model(current);
+
+ spin_lock(&model->point_lock);
+ end = model->point_list.prev;
+ spin_unlock(&model->point_lock);
+
+ mutex_lock(&model->point_end_mutex);
+ model->point_end = end;
+
+ return seq_list_start(&model->point_list, *pos);
+}
+
+static void *forensics_point_next(struct seq_file *c, void *p, loff_t *pos)
+{
+ struct list_head *next = ((struct list_head *) p)->next;
+ struct tsem_model *model = tsem_model(current);
+
+ if (!model->point_end) {
+ ++*pos;
+ return NULL;
+ }
+
+ if (next == model->point_end)
+ model->point_end = NULL;
+
+ return seq_list_next(p, &model->point_list, pos);
+}
+
+static void forensics_point_stop(struct seq_file *c, void *pos)
+{
+ struct tsem_model *model = tsem_model(current);
+
+ mutex_unlock(&model->point_end_mutex);
+}
+
+static int forensics_point_show(struct seq_file *c, void *point)
+{
+ struct tsem_event_point *pt;
+
+ pt = list_entry(point, struct tsem_event_point, list);
+ if (pt->valid)
+ return 0;
+
+ seq_printf(c, "%*phN\n", tsem_digestsize(), pt->point);
+ return 0;
+}
+
+static const struct seq_operations forensics_point_seqops = {
+ .start = forensics_point_start,
+ .next = forensics_point_next,
+ .stop = forensics_point_stop,
+ .show = forensics_point_show
+};
+
+static int forensics_point_open(struct inode *inode, struct file *file)
+{
+ if (!can_access_fs())
+ return -EACCES;
+ return seq_open(file, &forensics_point_seqops);
+}
+
+static const struct file_operations forensics_point_ops = {
+ .open = forensics_point_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static void *forensics_count_start(struct seq_file *c, loff_t *pos)
+{
+ struct list_head *end;
+ struct tsem_model *model = tsem_model(current);
+
+ spin_lock(&model->point_lock);
+ end = model->point_list.prev;
+ spin_unlock(&model->point_lock);
+
+ mutex_lock(&model->point_end_mutex);
+ model->point_end = end;
+
+ return seq_list_start(&model->point_list, *pos);
+}
+
+static void *forensics_count_next(struct seq_file *c, void *p, loff_t *pos)
+{
+ struct list_head *next = ((struct list_head *) p)->next;
+ struct tsem_model *model = tsem_model(current);
+
+ if (!model->point_end) {
+ ++*pos;
+ return NULL;
+ }
+
+ if (next == model->point_end)
+ model->point_end = NULL;
+
+ return seq_list_next(p, &model->point_list, pos);
+}
+
+static void forensics_count_stop(struct seq_file *c, void *pos)
+{
+ struct tsem_model *model = tsem_model(current);
+
+ mutex_unlock(&model->point_end_mutex);
+}
+
+static int forensics_count_show(struct seq_file *c, void *point)
+{
+ struct tsem_event_point *pt;
+
+ pt = list_entry(point, struct tsem_event_point, list);
+ if (pt->valid)
+ return 0;
+
+ seq_printf(c, "%llu\n", pt->count);
+ return 0;
+}
+
+static const struct seq_operations forensics_count_seqops = {
+ .start = forensics_count_start,
+ .next = forensics_count_next,
+ .stop = forensics_count_stop,
+ .show = forensics_count_show
+};
+
+static int forensics_count_open(struct inode *inode, struct file *file)
+{
+ if (!can_access_fs())
+ return -EACCES;
+ return seq_open(file, &forensics_count_seqops);
+}
+
+static const struct file_operations forensics_count_ops = {
+ .open = forensics_count_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", tsem_digestsize(), 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_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", tsem_digestsize(), 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", tsem_digestsize(), 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 __poll_t export_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct tsem_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, &tsem_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,
+};
+
+/**
+ * tsem_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/kernel/security/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
+ * the pointer with an encoded code will be returned.
+ */
+struct dentry *tsem_fs_create_external(const char *name)
+{
+
+ return securityfs_create_file(name, 0400, external_tma, NULL,
+ &export_ops);
+}
+
+/**
+ * tsem_fs_show_trajectory() - Generate the output of a security event.
+ * @sf: A pointer to the seq_file structure to which output will
+ * be set.
+ * @ep: A pointer to the event description that is to be output.
+ *
+ * This function is used to generate a record that will be output to
+ * the pseudo-file that outputs the security events for the
+ * domain being modeled.
+ */
+void tsem_fs_show_trajectory(struct seq_file *c, struct tsem_event *ep)
+{
+ switch (ep->event) {
+ case TSEM_FILE_OPEN:
+ show_event(c, ep);
+ 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;
+ }
+}
+
+/**
+ * tesm_fs_show_field() - Output a JSON field description
+ * @sf: A pointer to the seq_file structure that the field description
+ * is to be written to.
+ * @f: A pointer to null terminated character buffer containing the
+ * name of the field to encode
+ *
+ * This function is used to generate a JSON field description that
+ * is used to name a sequence of key/value pairs describing the
+ * characteristcis of the field.
+ */
+void tsem_fs_show_field(struct seq_file *c, const char *field)
+{
+ seq_printf(c, "\"%s\": {", field);
+}
+
+/**
+ * tesm_fs_tsem_fs_show_key() - Output a JSON key/value pair
+ * @sf: A pointer to the seq_file structure that the field description
+ * is to be written to.
+ * @term: A pointer to a null-terminated character buffer containing
+ * the string that is to be used for terminating the key/value
+ * pair.
+ * @key: A pointer to the null-terminated character buffer containing
+ * the key description.
+ * @fmt: The printf format that is to be used for formatting the
+ * value of the key.
+ *
+ * This function is a variadic function that is used to encode a
+ * JSON key/value pair that provides one of characteristics of an
+ * event description field.
+ */
+void tsem_fs_show_key(struct seq_file *c, char *term, char *key,
+ char *fmt, ...)
+{
+ va_list args;
+
+ seq_printf(c, "\"%s\": \"", key);
+
+ va_start(args, fmt);
+ seq_vprintf(c, fmt, args);
+ va_end(args);
+
+ if (term[0] == ',')
+ seq_printf(c, "\"%s ", term);
+ else
+ seq_printf(c, "\"%s", term);
+}
+
+/**
+ * tesm_fs_init() - Initialize the TSEM control filesystem heirarchy
+ *
+ * This function is called as part of the TSEM LSM initialization
+ * process. The purpose of this function is to create the TSEM
+ * control plane, based on the securityfs filesystem, by creating the
+ * /sys/kernel/security/tsem directory and populating that directory
+ * with the control plane files and internal TMA model information
+ * files. The /sys/kernel/security/tsem/ExternalTMA directory is
+ * also created. This directory will be used to hold the modeling
+ * domain specific files that will emit the security event descriptions
+ * for the domain.
+ *
+ * Return: If filesystem initialization is successful a return code of 0
+ * is returned. A negative return value is returned if an error
+ * is encountered.
+ */
+int __init tsem_fs_init(void)
+{
+ int retn = -1;
+
+ tsem_dir = securityfs_create_dir("tsem", NULL);
+ if (tsem_dir == NULL)
+ goto done;
+
+ control = securityfs_create_file("control", 0200, tsem_dir, NULL,
+ &control_ops);
+ if (IS_ERR(control))
+ goto err;
+
+ id = securityfs_create_file("id", 0400, tsem_dir, NULL, &id_ops);
+ if (IS_ERR(control))
+ goto err;
+
+ aggregate = securityfs_create_file("aggregate", 0400, tsem_dir, NULL,
+ &aggregate_ops);
+ if (IS_ERR(aggregate))
+ goto err;
+
+ internal_tma = securityfs_create_dir("InternalTMA", tsem_dir);
+ if (IS_ERR(internal_tma))
+ goto err;
+
+ model = securityfs_create_dir("model0", internal_tma);
+ if (IS_ERR(model))
+ goto err;
+
+ forensics = securityfs_create_file("forensics", 0400, model, NULL,
+ &forensics_ops);
+ if (IS_ERR(forensics))
+ goto err;
+
+ forensics_counts = securityfs_create_file("forensics_counts", 0400,
+ model, NULL,
+ &forensics_count_ops);
+ if (IS_ERR(forensics_counts))
+ goto err;
+
+ forensics_coeff = securityfs_create_file("forensics_coefficients",
+ 0400, model, NULL,
+ &forensics_point_ops);
+ if (IS_ERR(forensics_coeff))
+ goto err;
+
+ trajectory = securityfs_create_file("trajectory", 0400, model, NULL,
+ &trajectory_ops);
+ if (IS_ERR(trajectory))
+ goto err;
+
+ trajectory_counts = securityfs_create_file("trajectory_counts", 0400,
+ model, NULL,
+ &trajectory_count_ops);
+ if (IS_ERR(trajectory_counts))
+ goto err;
+
+ trajectory_coeff = securityfs_create_file("trajectory_coefficients",
+ 0400, model, NULL,
+ &trajectory_point_ops);
+ if (IS_ERR(trajectory_coeff))
+ goto err;
+
+ measurement = securityfs_create_file("measurement", 0400,
+ model, NULL,
+ &measurement_ops);
+ if (IS_ERR(measurement))
+ goto err;
+
+ state = securityfs_create_file("state", 0400, model, NULL,
+ &state_ops);
+ if (IS_ERR(state))
+ goto err;
+
+ external_tma = securityfs_create_dir("ExternalTMA", tsem_dir);
+ if (IS_ERR(external_tma))
+ goto err;
+
+ retn = 0;
+
+ done:
+ return retn;
+
+ err:
+ securityfs_remove(tsem_dir);
+ securityfs_remove(control);
+ securityfs_remove(id);
+ securityfs_remove(aggregate);
+ securityfs_remove(internal_tma);
+ securityfs_remove(model);
+ securityfs_remove(forensics);
+ securityfs_remove(forensics_counts);
+ securityfs_remove(forensics_coeff);
+ securityfs_remove(trajectory);
+ securityfs_remove(trajectory_counts);
+ securityfs_remove(trajectory_coeff);
+ securityfs_remove(measurement);
+ securityfs_remove(state);
+ securityfs_remove(external_tma);
+
+ return retn;
+}
--
2.39.1
Hi,
On 7/10/23 03:23, Dr. Greg wrote:
> Add an entry to the MAINTAINERS file to document the maintainer's
> address and files relevant to the Trusted Security Event Modeling
> system (TSEM).
>
> Signed-off-by: Greg Wettstein <[email protected]>
> ---
> MAINTAINERS | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 35e19594640d..4660c972d5e3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -19026,6 +19026,14 @@ F: include/uapi/linux/selinux_netlink.h
> F: scripts/selinux/
> F: security/selinux/
>
> +TSEM SECURITY MODULE
> +M: Greg Wettstein <[email protected]>
> +S: Maintained
> +L: [email protected]
> +F: Documentation/admin-guide/LSM/tsem.rst
> +F: Documentation/ABI/testing/tsemfs
> +F: security/tsem/
> +
Please insert entries in the MAINTAINERS file in alphabetical order
(as stated in that file).
Thanks.
> SENSABLE PHANTOM
> M: Jiri Slaby <[email protected]>
> S: Maintained
--
~Randy
On Mon, Jul 10, 2023 at 01:00:58PM -0700, Randy Dunlap wrote:
> Hi,
Hi Randy, I hope your are having a good weekend.
> On 7/10/23 03:23, Dr. Greg wrote:
> > Add an entry to the MAINTAINERS file to document the maintainer's
> > address and files relevant to the Trusted Security Event Modeling
> > system (TSEM).
> >
> > Signed-off-by: Greg Wettstein <[email protected]>
> > ---
> > MAINTAINERS | 8 ++++++++
> > 1 file changed, 8 insertions(+)
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 35e19594640d..4660c972d5e3 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -19026,6 +19026,14 @@ F: include/uapi/linux/selinux_netlink.h
> > F: scripts/selinux/
> > F: security/selinux/
> >
> > +TSEM SECURITY MODULE
> > +M: Greg Wettstein <[email protected]>
> > +S: Maintained
> > +L: [email protected]
> > +F: Documentation/admin-guide/LSM/tsem.rst
> > +F: Documentation/ABI/testing/tsemfs
> > +F: security/tsem/
> > +
> Please insert entries in the MAINTAINERS file in alphabetical order
> (as stated in that file).
Fixed, my apologies for missing the note in the MAINTAINERS file.
> Thanks.
> --
> ~Randy
Have a good remainder of the weekend.
As always,
Dr. Greg
The Quixote Project - Flailing at the Travails of Cybersecurity
On 7/10/2023 3:23 AM, Dr. Greg wrote:
> TSEM is designed, from a functional perspective, to be contained
> entirely in its own directory.
>
> The tsem.h header file defines the enumeration types, structure
> definitions and externally visiable functions that are referenced
> by all of the compilation units of the TSEM LSM implementation in
> that directory.
Extensive documentation notwithstanding, it's impossible to review
the data structures and constants without the code that goes along
with them.
>
> The structure and enumeration types are extensively documented
> and are the recommended starting point for understanding TSEM
> implementation and functionality.
>
> Signed-off-by: Greg Wettstein <[email protected]>
> ---
> security/tsem/tsem.h | 1516 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 1516 insertions(+)
> create mode 100644 security/tsem/tsem.h
>
> diff --git a/security/tsem/tsem.h b/security/tsem/tsem.h
> new file mode 100644
> index 000000000000..03915f47529b
> --- /dev/null
> +++ b/security/tsem/tsem.h
> @@ -0,0 +1,1516 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +/*
> + * Copyright (C) 2023 Enjellic Systems Development, LLC
> + * Author: Dr. Greg Wettstein <[email protected]>
> + *
> + * This is the single include file that documents all of the externally
> + * visible types and functions that are used by TSEM. This file is
> + * currently organized into four major sections in the following order;
> + *
> + * includes used by all compilation units
> + * CPP definitions
> + * enumeration types
> + * structure definitions
> + * function declarations
> + * inline encapsulation functions.
> + *
> + * Include files that are referenced by more than a single compilation
> + * should be included in this file. Includes that are needed to
> + * satisfy compilation requirements for only a single file should be
> + * included in the file needing that include.
> + *
> + * Understanding the overall implementation and architecture of TSEM
> + * will be facilitated by reviewing the documentation in this file.
> + */
> +
> +#include <uapi/linux/in.h>
> +#include <uapi/linux/in6.h>
> +#include <linux/wait.h>
> +#include <linux/kref.h>
> +#include <linux/lsm_hooks.h>
> +#include <linux/capability.h>
> +#include <crypto/hash.h>
> +#include <crypto/hash_info.h>
> +#include <net/af_unix.h>
> +
> +/* The capability needed to manage TSEM. */
> +#define TSEM_CONTROL_CAPABILITY CAP_ML
Why would you do this? You gave the capability a name that even you
don't want to use.
> +
> +/*
> + * The number of 'slots' in the structure magazines that are used to
> + * satisfy modeling of security events that are called in atomic context.
> + */
> +#define TSEM_ROOT_MAGAZINE_SIZE 96
> +#define TSEM_MAGAZINE_SIZE_INTERNAL 16
> +#define TSEM_MAGAZINE_SIZE_EXTERNAL 96
> +
> +/**
> + * enum tsem_event_type - Ordinal value for a security event.
> + * @TSEM_BPRM_SET_CREDS: Ordinal value for bprm_creds_for_exec.
> + * @TSEM_GENERIC_EVENT: Ordinal value for a generically modeled event.
> + * @TSEM_TASK_KILL: Ordinal value for task kill.
> + * @....: Remainder follows with a similar naming format that has
> + * TSEM_ prep ended to the raw LSM security hook name.
> + * @TSEM_EVENT_CNT: The final ordinal value is used to define the
> + * length of the following arrays that are indexed
> + * by the ordinal value of the hook:
> + *
> + * This enumeration is used to designate an ordinal value for each
> + * security event, ie. LSM hook, that TSEM is implementing modeling
> + * for. This value is used to identify the hook that is either having
> + * its event description being exported to an external Trusted Modeling
> + * Agent (TMA) or modeled by the internal TMA implementation.
> + *
> + * The primary use of this enumeration is to conditionalize code paths
> + * based on the security hook being processed and to index the
> + * tsem_names array and the array that defines the action that is to
> + * be taken in response to an event that generates a permissions
> + * violation.
> + */
> +enum tsem_event_type {
> + TSEM_BPRM_SET_CREDS = 1,
> + TSEM_GENERIC_EVENT,
> + TSEM_TASK_KILL,
> + TSEM_TASK_SETPGID,
> + TSEM_TASK_GETPGID,
> + TSEM_TASK_GETSID,
> + TSEM_TASK_SETNICE,
> + TSEM_TASK_SETIOPRIO,
> + TSEM_TASK_GETIOPRIO,
> + TSEM_TASK_PRLIMIT,
> + TSEM_TASK_SETRLIMIT,
> + TSEM_TASK_SETSCHEDULER,
> + TSEM_TASK_GETSCHEDULER,
> + TSEM_TASK_PRCTL,
> + TSEM_FILE_OPEN,
> + TSEM_MMAP_FILE,
> + TSEM_FILE_IOCTL,
> + TSEM_FILE_LOCK,
> + TSEM_FILE_FCNTL,
> + TSEM_FILE_RECEIVE,
> + TSEM_UNIX_STREAM_CONNECT,
> + TSEM_UNIX_MAY_SEND,
> + TSEM_SOCKET_CREATE,
> + TSEM_SOCKET_CONNECT,
> + TSEM_SOCKET_BIND,
> + TSEM_SOCKET_ACCEPT,
> + TSEM_SOCKET_LISTEN,
> + TSEM_SOCKET_SOCKETPAIR,
> + TSEM_SOCKET_SENDMSG,
> + TSEM_SOCKET_RECVMSG,
> + TSEM_SOCKET_GETSOCKNAME,
> + TSEM_SOCKET_GETPEERNAME,
> + TSEM_SOCKET_SETSOCKOPT,
> + TSEM_SOCKET_SHUTDOWN,
> + TSEM_PTRACE_TRACEME,
> + TSEM_KERNEL_MODULE_REQUEST,
> + TSEM_KERNEL_LOAD_DATA,
> + TSEM_KERNEL_READ_FILE,
> + TSEM_SB_MOUNT,
> + TSEM_SB_UMOUNT,
> + TSEM_SB_REMOUNT,
> + TSEM_SB_PIVOTROOT,
> + TSEM_SB_STATFS,
> + TSEM_MOVE_MOUNT,
> + TSEM_SHM_ASSOCIATE,
> + TSEM_SHM_SHMCTL,
> + TSEM_SHM_SHMAT,
> + TSEM_SEM_ASSOCIATE,
> + TSEM_SEM_SEMCTL,
> + TSEM_SEM_SEMOP,
> + TSEM_SYSLOG,
> + TSEM_SETTIME,
> + TSEM_QUOTACTL,
> + TSEM_QUOTA_ON,
> + TSEM_MSG_QUEUE_ASSOCIATE,
> + TSEM_MSG_QUEUE_MSGCTL,
> + TSEM_MSG_QUEUE_MSGSND,
> + TSEM_MSG_QUEUE_MSGRCV,
> + TSEM_IPC_PERMISSION,
> + TSEM_KEY_ALLOC,
> + TSEM_KEY_PERMISSION,
> + TSEM_NETLINK_SEND,
> + TSEM_INODE_CREATE,
> + TSEM_INODE_LINK,
> + TSEM_INODE_UNLINK,
> + TSEM_INODE_SYMLINK,
> + TSEM_INODE_MKDIR,
> + TSEM_INODE_RMDIR,
> + TSEM_INODE_MKNOD,
> + TSEM_INODE_RENAME,
> + TSEM_INODE_SETATTR,
> + TSEM_INODE_GETATTR,
> + TSEM_INODE_SETXATTR,
> + TSEM_INODE_GETXATTR,
> + TSEM_INODE_LISTXATTR,
> + TSEM_INODE_REMOVEXATTR,
> + TSEM_INODE_KILLPRIV,
> + TSEM_TUN_DEV_CREATE,
> + TSEM_TUN_DEV_ATTACH_QUEUE,
> + TSEM_TUN_DEV_ATTACH,
> + TSEM_TUN_DEV_OPEN,
> + TSEM_BPF,
> + TSEM_BPF_MAP,
> + TSEM_BPF_PROG,
> + TSEM_EVENT_CNT
> +};
> +
> +/**
> + * enum tsem_action_type - Ordinal value for security responses.
> + * @TSEM_ACTION_LOG: Ordinal value to indicate that a security event
> + * that results in a model permissions violation
> + * should be logged.
> + * @TSEM_ACTION_EPERM: Ordinal value to indicate that a security event
> + * generating a model permissions violation should
> + * return -EPERM to the caller.
> + *
> + * This enumeration type is used to designate what type of action is
> + * to be taken when the processing of a security event hook results in
> + * a model violation. The TSEM_ACTION_LOG and TSEM_ACTION_EPERM
> + * translate into the classical concepts of logging or enforcing
> + * actions used by other mandatory access control architectures.
> + */
> +enum tsem_action_type {
> + TSEM_ACTION_LOG = 0,
> + TSEM_ACTION_EPERM,
> + TSEM_ACTION_CNT
> +};
> +
> +/**
> + * enum tsem_control_type - Ordinal values for TSEM control actions.
> + * @TSEM_CONTROL_INTERNAL: This ordinal value is set when the first
> + * word of an argument string written to the
> + * control file is the word 'internal'. This
> + * designates that the security namespace will
> + * be modeled by the internal TMA.
> + * @TSEM_CONTROL_EXTERNAL: This ordinal value is set when the first
> + * word of an argument string written to the
> + * control file is the word 'external'. This
> + * designates that the security namespace will
> + * be model by an external TMA.
> + * @TSEM_CONTROL_ENFORCE: This ordinal value is set when the word
> + * 'enforce' is written to the control file.
> + * This indicates that model is to be placed
> + * in 'enforcing' mode and security events that
> + * result in model violations will return EPERM.
> + * @TSEM_CONTROL_SEAL: This ordinal value is set when the word 'seal'
> + * is written to the control file. This indicates
> + * that the model for security domain will treat
> + * all security events that do not conform to the
> + * model as 'forensics' events.
> + * @TSEM_CONTROL_TRUSTED: This ordinal value is used when the first
> + * word of an argument string written to the
> + * control file is the word 'trusted'. This
> + * is interpreted as a directive to set the
> + * trust status of the task that executed the
> + * security event to be trusted.
> + * @TSEM_CONTROL_UNTRUSTED: This ordinal value is used when the first
> + * word of an argument string written to the
> + * control file is the word 'untrusted'.
> + * This is interpreted as a directive to set
> + * the trust status of the task that executed
> + * the security event to be untrusted.
> + * @TSEM_CONTROL_MAP_STATE: This ordinal value is used when the first
> + * word of an argument string written to the
> + * control file is the word 'state'. The
> + * argument to this directive will be an
> + * ASCII hexadecimally encoded string of the
> + * current model's digest size that will be
> + * treated as a security state point for
> + * inclusion in the security model for the
> + * security domain/namespace.
> + * @TSEM_CONTROL_MAP_PSEUDONYM: This ordinal value is used when the
> + * first word of an argument string
> + * written to the control file is the
> + * word 'pseudonym'. The argument to
> + * this directive will be an ASCII
> + * hexadecimally encoded string of the
> + * current model's digest size that will
> + * be treated as a pseudonym directive
> + * for the security domain/namespace.
> + * TSEM_CONTROL_MAP_BASE: This ordinal value is used when the first
> + * word of an argument string written to the
> + * control file is the word 'base'. The
> + * argument to this directive will be an ASCII
> + * hexadecimally encoded string of the current
> + * model's digest size that will be treated as
> + * the base value for the computation of the
> + * functional values (measurement and state) of
> + * the security domain/namespace.
> +
> + * This enumeration type is used to designate what type of control
> + * action is to be implemented when arguments are written to the TSEM
> + * control file (/sys/kernel/security/tsem/control). The ordinal
> + * values govern the processing of the command and the interpretation
> + * of the rest of the command argument string.
> + */
> +enum tsem_control_type {
> + TSEM_CONTROL_INTERNAL = 0,
> + TSEM_CONTROL_EXTERNAL,
> + TSEM_CONTROL_ENFORCE,
> + TSEM_CONTROL_SEAL,
> + TSEM_CONTROL_TRUSTED,
> + TSEM_CONTROL_UNTRUSTED,
> + TSEM_CONTROL_MAP_STATE,
> + TSEM_CONTROL_MAP_PSEUDONYM,
> + TSEM_CONTROL_MAP_BASE
> +};
> +
> +/**
> + * enum tsem_ns_reference - Ordinal value for DAC namespace reference.
> + * @TSEM_NS_INITIAL: This ordinal value indicates that the uid/gid
> + * values should be interpreted against the initial
> + * user namespace.
> + * @TSEM_NS_CURRENT: This ordinal value indicates that the uid/gid
> + * values should be interpreted against the user
> + * namespace that is in effect for the process being
> + * modeled.
> + *
> + * This enumeration type is used to indicate what user namespace
> + * should be referenced when the uid/gid values are interpreted for
> + * the creation of either the COE or CELL identities. The enumeration
> + * ordinal passed to the tsem_ns_create() function, to configure the
> + * security domain/namespace, is set by the nsref argument to either
> + * the 'internal' or 'external' control commands.
> + */
> +enum tsem_ns_reference {
> + TSEM_NS_INITIAL = 1,
> + TSEM_NS_CURRENT
> +};
> +
> +/**
> + * enum tsem_task_trust - Ordinal value describing task trust status.
> + * @TSEM_TASK_TRUSTED: This ordinal value indicates that the task has
> + * not executed a security event that has resulted
> + * in a security behavior not described by the
> + * security model the task is being governed by.
> + * @TSEM_TASK_UNTRUSTED: This ordinal value indicates that the task
> + * has requested the execution of a security event
> + * that resulted in a security behavior not
> + * permitted by the security model the task is
> + * being governed by.
> + * @TSEM_TASK_TRUST_PENDING: This ordinal value indicates that the setting
> + * of the task trust status is pending a response
> + * from an external TMA.
> + *
> + * This enumeration type is used to specify the three different trust
> + * states that a task can be in. The trust status of a task is
> + * regulated by the trust_status member of struct tsem_task. A task
> + * carrying the status of TSEM_TASK_TRUSTED means that it has
> + * not requested the execution of any security events that are
> + * inconsistent with the security model that the task is running in.
> + *
> + * If a task requests execution of a security event that is
> + * inconsistent with the security model it is operating in, and the
> + * domain is running in 'sealed' mode, the task trust status is set to
> + * TSEM_TASK_UNTRUSTED. This value is 'sticky' in that it will be
> + * propagated to any child tasks that are spawned from an untrusted
> + * task.
> + *
> + * In the case of an externally modeled security domain/namespace, the
> + * task trust status cannot be determined until the modeling of the
> + * security event has been completed. The tsem_export_event()
> + * function sets the trust status TSEM_TASK_TRUST_PENDING and then
> + * places the task into an interruptible sleep state.
> + *
> + * Only two events will cause the task to be removed from sleep state.
> + * Either the task is killed or a control message is written to the
> + * TSEM control file that specifies the trust status of the task. See
> + * the description of the TSEM_CONTROL_TRUSTED and
> + * TSEM_CONTROL_UNTRUSTED enumeration types.
> + */
> +enum tsem_task_trust {
> + TSEM_TASK_TRUSTED = 1,
> + TSEM_TASK_UNTRUSTED = 2,
> + TSEM_TASK_TRUST_PENDING = 4
What happened to 3?
> +};
> +
> +/**
> + * enum tsem_inode_state - Ordinal value for inode reference state.
> + * @TSEM_INODE_COLLECTING: This ordinal value indicates that the uid/gid
> + * values should be interpreted against the initial
> + * user namespace.
> + * @TSEM_INODE_COLLECTED: This ordinal value indicates that the uid/gid
> + * values should be interpreted against the user
> + * namespace that is in effect for the process being
> + * modeled.
> + *
> + * This enumeration type is used to specify the status of the inode
> + * that is having a digest value computed on the file that it is
> + * referencing. The purpose of this enumeration is so that the
> + * recursive call to the TSEM_FILE_OPEN hook, caused by the kernel
> + * opening the file to compute the checksum, can be bypassed.
> + *
> + * The state value of the inode is carried in struct tsem_inode and is
> + * set and interrogated by the add_file_digest() function. If the
> + * status of the inode is TSEM_INODE_COLLECTED and the iversion of the
> + * inode is the same as the collection time, the cached value for
> + * currently active model digest is returned.
> +
> + * If the test for the relevancy of the cached digest value fails the
> + * status of the inode is set to TSEM_INODE_COLLECTING. The
> + * tsem_file_open() function will check the inode status when it is
> + * invoked by the integrity_kernel_read() function and if it is
> + * set to 'collecting', a successful permissions check is returned so
> + * that the kernel can open the file and compute its digest.
> + */
> +enum tsem_inode_state {
> + TSEM_INODE_COLLECTING = 1,
> + TSEM_INODE_COLLECTED
> +};
> +
> +/**
> + * struct tsem_task - TSEM task control structure.
> + * @tma_for_ns: The context identity number of the namespace that
> + * the task has control over if any.
> + * @trust_status: The enumeration type that specifies the trust state of
> + * the process.
> + * @task_id: The hash specific digest that identifies the process.
> + * @task_key: A hash specific digest value that is used to
> + * authenticate a task that is running as a trust
> + * orchestrator to a task that is under the control of the
> + * orchestrator.
> + * @context: A pointer to the tsem_context structure that defines the
> + * modeling context that the task is running under.
> + *
> + * This structure is one of the two primary control structures that
> + * are implemented through the LSM blob functionality. It is
> + * automatically created when the task control structure is allocated
> + * for a new task that is being created. It's role is to control the
> + * status of the task with respect to its security model.
> + *
> + * The trust_status member of structure determines whether or not the
> + * task is in a condition to be trusted. It represents whether or not
> + * the task has requested execution of a security event that is
> + * inconsistent with the model that the task is running under.
> + * Reference the tsem_trust_status enumeration type for more
> + * information on this member. The trust status value is propagated
> + * to any child tasks that are spawned from a task.
> + *
> + * The digest value that the task_id member contains is generated by
> + * the tsem_tsem_bprm_creds_for_exec() function that computes the
> + * task identity based on the COE identity and the CELL identity of
> + * the executable that is being started. This task_id value is used
> + * in the computation of the security state point values in
> + * combination with the COE and CELL identities for this event. The
> + * task_id digest creates security state points that are specific to
> + * the executable file that was used to start the task.
> + *
> + * The task_key member holds the authentication key that will be used
> + * to authenticate a process that is requesting the ability to set the
> + * trust status of a process. This value is generated for the task
> + * structure of the trust orchestrator when a security modeling
> + * namespace is created by the orchestrator.
> + *
> + * As an additional protection, the creation of a namespace causes the
> + * context id of the created namespace to be placed in the task that
> + * will serve as the trust orchestrator for the namespace. This
> + * context id must match the context id of a process that a trust
> + * control request is being sent to. Like the authentication key
> + * this value is not propagated on task allocation so only the task
> + * that has nominated the security modeling namespace will have
> + * possession of the necessary credentials to control it.
> + *
> + * The context member of the structure contains a pointer to the
> + * tsem_context structure allocated when a security modeling namespace
> + * is created by the tsem_ns_create() function. This structure will
> + * contain all of the information needed to define how the task is to
> + * have its security behavior modeled.
> + */
> +struct tsem_task {
> + u64 tma_for_ns;
> + enum tsem_task_trust trust_status;
> + u8 task_id[HASH_MAX_DIGESTSIZE];
> + u8 task_key[HASH_MAX_DIGESTSIZE];
> + struct tsem_context *context;
> +};
> +
> +/**
> + * struct tsem_context - TSEM modeling context description.
> + * @kref: Reference count for the context.
> + * @work: Work structure for delayed release of the context.
> + * @id: The index number of the context.
> + * @sealed: Status variable indicating whether or not the
> + * modeling context can be modified.
> + * @use_current_ns: Status variable indicating which user namespace
> + * should be used for resolution of uid/gid values.
> + * @actions: An array of enum tsem_action_type variables indicating
> + * the type of response that should be returned in
> + * response to the modeling of a security event that
> + * is inconsistent with the model being used for the
> + * security context.
> + * @digestname: A pointer to a null-terminated buffer containing the
> + * name of the digest function that is to be used for
> + * this security context.
> + * @zero_digest: The digest value for a 'zero-length' digest value.
> + * @tfm: A pointer to the digest transformation structure that is to
> + * be used for this context.
> + * @magazine_size: The number of struct tsem_event structures that
> + * are held in reserve for security event hooks that
> + * are called in atomic context.
> + * @magazine_lock: The spinlock that protects access to the event
> + * magazine for the security context.
> + * @magazine_index: The bitmap that is used to track the magazine slots
> + * that have been allocated.
> + * @ws: An array of work structures that are used to refill the magazine
> + * slots.
> + * @magazine: An array of pointers to struct tsem_event structures that
> + * are pre-allocated for security hooks called in atomic
> + * context.
> + * @model: If the modeling context is implemented with a kernel based
> + * trusted model agent this pointer will point to the struct
> + * tsem_model structure that maintains the state of the
> + * security model.
> + * @external: If the modeling context is implemented with an external
> + * modeling agent this pointer will point to the struct
> + * tsem_external structure that implements the interface to
> + * the external trusted modeling agent.
> +
> + * This structure is used to represent the state of a TSEM security
> + * modeling namespace. A pointer to this structure is stored in the
> + * struct tsem_task structure.
> + *
> + * This structure is allocated by the tsem_ns_create() function in
> + * response to a TSEM control request. This structure maintains all
> + * of the information that describes the security modeling namespace
> + * that is not specific to the type of namespace, ie. external or
> + * internal that is being implemented.
> +
> + * The id member is a 64-bit counter that cannot feasibly be
> + * overflowed and that is incremented for each namespace creation that
> + * is created. The root modeling namespace has a value of zero so the
> + * TSEM code uses a pattern of testing this value for non-zero status
> + * as an indication of whether or not the task is running in a
> + * subordinate modeling namespace.
> +
> + * Each security modeling namespace can have an independent
> + * cryptographic digest function that is used as the compression
> + * function for generating the digest values that are used to model
> + * the security events that occur in a namespace. A single struct tfm
> + * is allocated for this digest function at the time that the
> + * tsem_context structure is created and is maintained in this
> + * structure for subsequent use during event processing.
> +
> + * Each cryptographic digest function has a 'zero message' value that
> + * is the result of the initialization and closure of a hash function
> + * that has no other input. This zero digest value is computed at the
> + * time of the creation of the array. This digest value is returned
> + * for files with zero sizes or that have pseudonyms declared for
> + * them.
> + *
> + * The actions array contains a specification of how each security
> + * event should be handled in the event that the model detects a
> + * security event consistent with the model designated for the
> + * namespace. This array allows the specification of whether the
> + * model should be enforcing or logging. Currently the specification
> + * is all or nothing for all of the events, with plans to make the
> + * actions individually configurable.
> +
> + * Each security event that is processed requires a struct tsem_event
> + * structure that drives either the internal modeling of an event or
> + * its export to an external modeling agent. Some security event
> + * hooks are called while a task is running in atomic context. Since
> + * memory cannot be allocated while a process is in atomic context, a
> + * magazine of these structures is maintained by this structure for
> + * security events that run in atomic context. The size of this
> + * magazine is dynamic and is configurable for each security modeling
> + * namespace that is created.
> + *
> + * When a tsem_event structure is allocated for an atomic event a
> + * request for the refill of the slot that is vacated is dispatched to
> + * an asynchronous workqueue. The ws member of this structure points
> + * to an array of work structures for this refill capability, one for
> + * each slot in the magazine.
> + *
> + * All of this infrastructure is generic for each security modeling
> + * namespace. How the security modeling is done is governed by the
> + * model and external members of this structure. These members point
> + * to data structures that either maintain the security model state
> + * for an in kernel trusted modeling agent or handle the export of the
> + * event to an external trusted modeling agent.
> + *
> + * Each task that is created in a non-root security modeling namespace
> + * increments the reference count maintained in the kref member of
> + * this structure in the tsem_task_alloc() function. The
> + * tsem_task_free() function decrements this reference count. When
> + * the reference count expires, ie. when the last task using the
> + * modeling namespace exits, an asynchronous workqueue request is
> + * dispatched to dispose of the context. The work member of this
> + * structure is used to reference that workqueue request.
> + */
> +struct tsem_context {
> + struct kref kref;
> + struct work_struct work;
> +
> + u64 id;
> + bool sealed;
> + bool use_current_ns;
> +
> + enum tsem_action_type actions[TSEM_EVENT_CNT];
> +
> + char *digestname;
> + u8 zero_digest[HASH_MAX_DIGESTSIZE];
> + struct crypto_shash *tfm;
> +
> + unsigned int magazine_size;
> + spinlock_t magazine_lock;
> + unsigned long *magazine_index;
> + struct tsem_work *ws;
> + struct tsem_event **magazine;
> +
> + struct tsem_model *model;
> + struct tsem_external *external;
> +};
Odd use of whitespace in the structure definition.
> +
> +/**
> + * struct tsem_model - TSEM internal TMA description.
> + * @have_aggregate: Flag variable to indicate whether or not the
> + * hardware aggregate value has been injected into
> + * the mode.
> + * @base: The base value that is to be used in computing the
> + * measurement and state values of the model.
> + * @measurement: The time dependent linear extension state of the
> + * model.
> + * @state: The time independent linear extension state of the model.
> + * @point_lock: The spinlock that protects access to the list of
> + * security state coefficients in the model.
> + * @point_list: A pointer to the list of security state coefficients
> + * in the model.
> + * @point_end_mutex: The mutex that is used to protect the end of the
> + * list of security state coefficients for the
> + * model.
> + * @point_end: A pointer to the end of the list of security state
> + * coefficients that will be traversed by a call to the
> + * control plane.
> + * @trajectory_lock: The spinlock used to protect the list of security
> + * event descriptions in the model.
> + * @trajectory_list: A pointer to the list of descriptions of the
> + * security events that have been recorded in this
> + * model.
> + * @trajectory_end_mutex: The mutex that protects the end of the list
> + * of security event descriptions.
> + * @trajectory_end: A pointer to the end of the list of security event
> + * descriptions that will be traversed by a call to
> + * the control plane.
> + * @forensics_lock: The spinlock used to protect the list of security
> + * event descriptions that are considered invalid by
> + * the model being enforced.
> + * @forensics_list: A pointer to the list of descriptions of security
> + * events that are considered invalid by the security
> + * model being enforced.
> + * @forensics_end_mutex: The mutex that protects the end of the list
> + * of security event descriptions that are
> + * considered invalid by the current model.
> + * @forensics_end: A pointer to the end of the list of security event
> + * descriptions, that are considered invalid, that are
> + * to be traversed by a call to the control plane.
> + * @pseudonym_mutex: The mutex lock that protects the list of file
> + * digest pseudonyms for the current model.
> + * @pseudonum_list: A pointer to the list of file digest pseudonyms
> + * that have been declared for the current model.
> + * @magazine_size: The number of struct tsem_event_point structures that
> + * are held in reserve for security event hooks that
> + * are called in atomic context.
> + * @magazine_lock: The spinlock that protects access to the event
> + * magazine for the security context.
> + * @magazine_index: The bitmap that is used to track the magazine slots
> + * that have been allocated.
> + * @ws: An array of work structures that are used to refill the magazine
> + * slots.
> + * @magazine: An array of pointers to struct tsem_event_point structures that
> + * are pre-allocated for security hooks called in atomic
> + * context.
> + *
> + * If a call to the tsem_ns_create() function specifies that a kernel
> + * based trusted modeling agent is to be used to implement the
> + * security namespace model, a pointer to this structure is placed in
> + * the struct tsem_context structure. This structure is used to
> + * maintain the state of the kernel based model.
> + *
> + * There are two primary functional values that are maintained by the
> + * model. The measurement member of this structure represents the
> + * time dependent linear extension sum of the security state
> + * coefficients that have been assigned to security events that have
> + * occurred in the context of the model.
> + *
> + * This classic integrity measurement is subject to scheduling
> + * dependencies and may be invariant from run to run of the model. It
> + * is of primary use in verifying the order of security events that
> + * have occurred in the model.
> + *
> + * The state member of this structure represents a time independent
> + * linear extension sum of the security state coefficients that have
> + * been generated in the model. It represents the linear extension
> + * sum of the security state coefficients that have been sorted in
> + * natural (big-endian) form.
> + *
> + * Both of these measurements are dependent on the platform hardware
> + * aggregate value and the base point that has been defined for the
> + * define.
> + *
> + * A non-zero hardware aggregate value is only available if the
> + * platform has a TPM. The have_aggregate member of this structure is
> + * a flag variable that indicates whether or not the aggregate value
> + * has been injected into the model.
> + *
> + * The base member of this structure contains a model specific
> + * coefficient that is used to perturb each security state coefficient
> + * generated in the model. This value is designed to serve as a
> + * 'freshness' value for a verifying party to the model.
> + *
> + * There are three primary model lists maintain by this structure:
> + *
> + * * security state points
> + * * security trajectory events
> + * * security forensics events
> + *
> + * Similar members are maintained in this structure to support each of
> + * these lists.
> + *
> + * All three lists are extension only and are protected by a spinlock
> + * that can be held in atomic context. This spinlock is only held for
> + * the period of time required to extend the list.
> + *
> + * Calls by the control plane to interrogate the lists require the
> + * traversal of the list that is ill-suited for a spinlock. As a
> + * result each list type has a mutex associated with it that protects
> + * a pointer to the end of the list, an endpoint that is determined at
> + * the start of a call to the control plane.
> + *
> + * The list spinlock is used at the start of the control plane call to
> + * capture the end of the list that is then protected by the mutex.
> + * In essence this is used to transition protection of the list from
> + * the spinlock to the mutex.
> + *
> + * The kernel based modeling agent has support for maintaining a
> + * constant digest value for files, that by function, do not have a
> + * fixed digest value, such as log files. The pseudonym_list member
> + * of this structure points to the list of these designations. The
> + * pseudonym_mutex structure protects this list.
> + *
> + * Like the struct tsem_context structure the tsem_model structure
> + * maintains a magazine of structures that are used to service
> + * security events that are called in atomic context. The magazine
> + * maintained by this structure is a list of struct tsem_event_point
> + * structures that are used to describe the security state
> + * coefficients held by the model.
> + *
> + * The description of struct tsem_context details the implementation
> + * of the magazine which is identical to the implementation for this
> + * structure, with the exception of the type of structures that are
> + * held in reserve.
> + */
> +struct tsem_model {
> + bool have_aggregate;
> +
> + u8 base[HASH_MAX_DIGESTSIZE];
> + u8 measurement[HASH_MAX_DIGESTSIZE];
> + u8 state[HASH_MAX_DIGESTSIZE];
> +
> + spinlock_t point_lock;
> + struct list_head point_list;
> + struct mutex point_end_mutex;
> + unsigned int point_count;
> + struct list_head *point_end;
> +
> + spinlock_t trajectory_lock;
> + struct list_head trajectory_list;
> + struct mutex trajectory_end_mutex;
> + struct list_head *trajectory_end;
> +
> + spinlock_t forensics_lock;
> + struct list_head forensics_list;
> + struct mutex forensics_end_mutex;
> + struct list_head *forensics_end;
> +
> + struct mutex pseudonym_mutex;
> + struct list_head pseudonym_list;
> +
> + unsigned int magazine_size;
> + spinlock_t magazine_lock;
> + unsigned long *magazine_index;
> + struct tsem_work *ws;
> + struct tsem_event_point **magazine;
> +};
> +
> +/**
> + * struct tsem_external - TSEM external TMA description.
> + * @export_lock: The spinlock that protects access to the export_list
> + * member of this structure.
> + * @export_list: A pointer to the list of events waiting to be
> + * exported to the trust orchestrator for the security
> + * modeling namespace. The structure type that is
> + * linked by this list is the struct export_event
> + * structure that is private to the export.c compilation
> + * unit.
> + * @dentry: A pointer to the dentry describing the pseudo-file in the
> + * /sys/kernel/security/tsem/ExternalTMA directory that is
> + * being used to export security event descriptions to the
> + * external trust orchestrator for the security modeling
> + * domain described by this structure.
> + * @have_event: A flag variable to indicate that there is work queued
> + * on the export pseudo-file for the security modeling
> + * namespace.
> + * @wq: The work queue used to implement polling for the security
> + * event export file for the security modeling namespace.
> + * @magazine_size: The number of struct export_event structures that
> + * are held in reserve for security event hooks that
> + * are called in atomic context.
> + * @magazine_lock: The spinlock that protects access to the event
> + * magazine for the security modeling domain.
> + * @magazine_index: The bitmap that is used to track the magazine slots
> + * that have been allocated.
> + * @ws: An array of work structures that are used to refill the magazine
> + * slots.
> + * @magazine: An array of pointers to struct export_event structures that
> + * are pre-allocated for security hooks called in atomic
> + * context.
> + *
> + * If an externally modeled security modeling namespace is created
> + * a structure of this type is allocated for the namespace and placed
> + * in the struct tsem_context structure.
> + *
> + * The primary purpose of this structure is to manage event
> + * descriptions that are being transmitted to the trust orchestrator
> + * associated with the security modeling namespace. The pseudo-file
> + * will be as follows:
> + *
> + * /sys/kernel/security/tsem/ExternalTMA/N
> + *
> + * Where N is the context id number of the modeling namespace.
> + *
> + * The dentry member of this structure is used to represent the
> + * pseudo-file that is created when the external modeled namespace is
> + * created.
> + *
> + * This list of events waiting to be received by the trust
> + * orchestrator is maintained in the export_list member of this
> + * structure. Additions or removals from the list hold the spinlock
> + * described by the export_lock member of this structure.
> + *
> + * The wq member of this structure is used to implement a workqueue
> + * to support polling for events on the export control file. The
> + * have_event flag is set to indicate to the polling call that
> + * security events are available for export.
> + *
> + * When a security event description is exported the calling task is
> + * scheduled away to allow the trust orchestrator to process the
> + * event. This obviously creates issues for security events that are
> + * called in atomic context.
> + *
> + * Security events in atomic context are exported as an async_event
> + * rather than a simple event. The trust orchestrator has the option
> + * of killing the workload that deviated from the security model or
> + * signaling a violation of the model.
> + *
> + * To support the export of asynchronous events a magazine, similar to
> + * the event and model structure magazines, is maintained by this
> + * structure for the external modeling namespace.
> + */
> +struct tsem_external {
> + spinlock_t export_lock;
> + struct list_head export_list;
> + struct dentry *dentry;
> + bool have_event;
> + wait_queue_head_t wq;
> +
> + unsigned int magazine_size;
> + spinlock_t magazine_lock;
> + unsigned long *magazine_index;
> + struct tsem_work *ws;
> + struct export_event **magazine;
> +};
> +
> +/**
> + * struct tsem_work - TSEM magazine refill work structure.
> + * @index: The index number of the slot in the structure magazine that
> + * is being refilled.
> + * @u: A union that holds pointers to the structure whose magazine is
> + * being refilled.
> + * @work: The work structure that manages the workqueue being used to
> + * refill the magazine entry.
> + *
> + * As has been previously documented for the struct tsem_context,
> + * struct tsem_model and struct tsem_external structures, there is a
> + * need to maintain a magazine of these structures in order to allow
> + * the processing of security events that are called in atomic
> + * context. An array of this structure type is embedded in each of
> + * these structures to manage the asynchronous refill of the slot in
> + * the magazine that was used to handle an atomic security event.
> + *
> + * The index member of this structure points to the slot in the
> + * magazine that this work item is referencing.
> + *
> + * The structure that the refill work is being done for is maintained
> + * in the respective structure pointer in the u member of this
> + * structure.
> + *
> + * The work member of this structure is used to reference the
> + * asynchronous work request that is being submitted for the refill.
> + */
> +struct tsem_work {
> + unsigned int index;
> + union {
> + struct tsem_context *ctx;
> + struct tsem_model *model;
> + struct tsem_external *ext;
> + } u;
> + struct work_struct work;
> +};
> +
> +/**
> + * struct tsem_COE - TSEM context of execution definition structure.
> + * @uid: The numeric user identity that the COE is running with.
> + * @euid: The effective user identity that the COE is running with.
> + * @suid: The saved user identity possessed by the COE.
> + * @gid: The group identity that the COE is running with.
> + * @egid: The effective group identity that the COE possesses.
> + * @sgid: The saved group identity of the COE.
> + * @fsuid: The filesystem user identity that the COE is running with.
> + * @fsgid: The filesystem group identity that the COE is running with.
> + * @capeff: This union is used to implement access to the effective
> + * capability set the COE is running with. The mask value
> + * is used to assign to the structure with the value member
> + * used to extract the 64 bit value for export and
> + * computation.
> + *
> + * A security state coefficient is computed from two primary entities:
> + * the COE and the CELL identities. This structure is used to carry
> + * and encapsulate the characteristics of the context of execution
> + * (COE) that will be used to generate the COE identity.
> + *
> + * The numeric values for discretionary access controls, ie. uid, gid,
> + * are determined by which user namespace the security modeling
> + * namespace is configured to reference. The reference will be either
> + * the initial user namespace or the user namespace that the context
> + * of execution is running in.
> + */
> +struct tsem_COE {
> + uid_t uid;
> + uid_t euid;
> + uid_t suid;
> +
> + gid_t gid;
> + gid_t egid;
> + gid_t sgid;
> +
> + uid_t fsuid;
> + gid_t fsgid;
> +
> + union {
> + kernel_cap_t mask;
> + u64 value;
> + } capeff;
> +};
> +
> +/**
> + * struct tsem_COE - TSEM file description.
> + * @uid: The numeric user identity of the file.
> + * @gid: The numeric group identity of the file.
> + * @mode: The discretionary access mode for the file.
> + * @flags: The file control flags.
> + * @name_length: The length of the pathname of the file.
> + * @name: The digest value of the pathname of the file using the
> + * hash function defined for the security modeling namespace.
> + * @s_magic: The magic number of the filesystem that the file resides
> + * in.
> + * @s_id: The name of the block device supporting the filesystem.
> + * @s_uuid: The uuid of the filesystem that the file resides in.
> + * @digest: The digest value of the contents of the file using the
> + * hash function defined for the security modeling namespace.
> + *
> + * This structure and the structures that follow up to the struct
> + * tsem_event structure are used to identify the various entities that
> + * are involved in the definition of the CELL identity for a security
> + * event.
> + *
> + * The tsem_file structure is used to encapsulate the characteristics
> + * of a file that is used as an entity in the CELL definition of an
> + * event.
> + *
> + * Since a pathname can be up to PATH_MAX (4096 bytes) in length the
> + * cryptographic digest value is used rather than the pathname of the
> + * file itself.
> + */
> +struct tsem_file {
> + uid_t uid;
> + gid_t gid;
> + umode_t mode;
> + u32 flags;
> +
> + u32 name_length;
> + u8 name[HASH_MAX_DIGESTSIZE];
> +
> + u32 s_magic;
> + u8 s_id[32];
> + u8 s_uuid[16];
> +
> + u8 digest[HASH_MAX_DIGESTSIZE];
> +};
> +
> +/**
> + * struct tsem_COE - TSEM memory mapped file characteristics.
> + * @file: The struct file definition for the file that is being
> + * mapped. This pointer will be null in the case of an
> + * anonymous mapping.
> + * @anonymous: A flag variable to indicate whether or not the mapping
> + * is file backed or anonymous.
> + * @reqprot: The memory protection flags that are requested by the
> + * memory mapping system call.
> + * @prot: The protections that will be applied to the mapping.
> + * @flags: The control flags of the memory mapping call.
> + *
> + * This structure is used to encapsulate the arguments provided to the
> + * tsem_mmap_file security event handler.
> + */
> +struct tsem_mmap_file_args {
> + struct file *file;
> + u32 anonymous;
> + u32 reqprot;
> + u32 prot;
> + u32 flags;
> +};
> +
> +/**
> + * struct tsem_socket_create_args - TSEM socket creation arguments.
> + * @family: The family name of the socket whose creation is being
> + * requested.
> + * @type: The type of the socket being created.
> + * @protocol: The protocol family of the socket being created.
> + * @kern: A flag variable to indicate whether or not the socket being
> + * created is kernel or userspace based.
> + *
> + * This structure is used to encapsulate the arguments provided to the
> + * tsem_socket_create security event handler.
> + */
> +struct tsem_socket_create_args {
> + int family;
> + int type;
> + int protocol;
> + int kern;
> +};
> +
> +/**
> + * struct tsem_socket_connection_args - TSEM socket connection arguments.
> + * @tsip: A pointer to the struct tsem_inode structure that describes
> + * the TSEM inode characteristics of the inode representing
> + * the socket.
> + * @addr: A pointer to the structure describing the socket address
> + * that is being connected.
> + * @addr_len: The length of the socket address description structure.
> + * @family: The family number of the socket.
> + *
> + * @protocol: The protocol family of the socket being created.
> + * @kern: A flag variable to indicate whether or not the socket being
> + * created is kernel or userspace based.
> + * @u: A union that is used to hold the family specific address
> + * characteristics of the socket connection.
> + * @u.ipv4: If the connection is IPV4 based this structure will be
> + * populated with the IPV4 address information.
> + * @u.ipv6: If the connection is IPV6 based this structure will be
> + * populated with the IPV6 address information.
> + * @u.path: If the socket connection is an AF_UNIX based socket
> + * address this buffer will contain the pathname of the
> + * socket address.
> + * @u.mapping: If the socket represents an address protocol other
> + * than IPV4, IPV6 or UNIX domain this buffer will contain
> + * the cryptographic value of the socket address
> + * information using the hash function that has been
> + * specified for the security modeling namespace.
> + *
> + * This structure is used to encapsulate the arguments provided to the
> + * tsem_socket_create security event handler.
> + */
> +struct tsem_socket_connect_args {
> + struct tsem_inode *tsip;
> + struct sockaddr *addr;
> + int addr_len;
> + u16 family;
> + union {
> + struct sockaddr_in ipv4;
> + struct sockaddr_in6 ipv6;
> + char path[UNIX_PATH_MAX + 1];
> + u8 mapping[HASH_MAX_DIGESTSIZE];
> + } u;
> +};
> +
> +/**
> + * struct tsem_socket_accept_args - TSEM socket accept parameters.
> + * @family: The socket family identifier for the connection being
> + * accepted.
> + * @type: The type of socket connection being accepted.
> + * @port: The port number of the connection being accepted.
> + * @ipv4: The IPV4 address of the connection being accepted if the
> + * socket is representing an IPV4 connection
> + * @ipv6: The IPV6 address of the connection being accepted if the
> + * socket is representing an IPV6 connection.
> + * @af_unix: The UNIX domain socket address if the socket is
> + * representing a UNIX domain connection.
> + * @path: The pathname of the UNIX domain socket.
> + * @mapping: A cryptographic hash of description of the socket
> + * connection being accepted if the socket is representing
> + * a connection other than an IPV4, IPV6 or UNIX domain
> + * socket.
> + *
> + * This structure is used to encapsulate the arguments provided to the
> + * tsem_socket_accept security event handler.
> + */
> +struct tsem_socket_accept_args {
> + u16 family;
> + u16 type;
> + __be16 port;
> + union {
> + __be32 ipv4;
> + struct in6_addr ipv6;
> + struct unix_sock *af_unix;
> + char path[UNIX_PATH_MAX + 1];
> + u8 mapping[HASH_MAX_DIGESTSIZE];
> + } u;
> +};
> +
> +/**
> + * struct tsem_task_kill_args - TSEM task kill arguments.
> + * @cross_model: A flag variable used to indicate whether or not the
> + * signal is originating from a security modeling
> + * namespace other than the namespace of the target process.
> + * @signal: The number of the signal being sent.
> + * @source: The task identifier of the process sending the signal
> + * @target: The task identifier of the target process.
> + *
> + * This structure is used to encapsulate the arguments provided to the
> + * tsem_task_kill security event handler.
> + */
> +struct tsem_task_kill_args {
> + u32 cross_model;
> + u32 signal;
> + u8 source[HASH_MAX_DIGESTSIZE];
> + u8 target[HASH_MAX_DIGESTSIZE];
> +};
> +
> +/**
> + * struct tsem_event - TSEM security event description.
> + * @index: The index number of the slot in the structure magazine that
> + * is being refilled.
> + * @u: A union that holds pointers to the structure whose magazine is
> + * being refilled.
> + * @work: The work structure that manages the workqueue being used to
> + * refill the magazine entry.
> + * @event: The enumeration type describing the security event that the
> + * structure is defining.
> + * @locked: A boolean flag used to indicate whether or not the
> + * security event is running in atomic context.
> + * @pid: The process id number, in the global pid namespace, of the
> + * task that is requesting approval for a security event.
> + * @pathname: If the event is referencing a file this pointer will
> + * point to a null-terminated buffer containing the
> + * pathname to the file in the mount namespace that the
> + * process is running in.
> + * @comm: A pointer to a null terminated buffer containing the name of
> + * the process that is requesting the security event.
> + * @digestsize: The size in bytes of the cryptographic hash function
> + * that is being used in the namespace in which the event
> + * is being recorded.
> + * @task_id: The TSEM task identifier of the process that generated the
> + * security event described by an instance of this
> + * structure.
> + * @mapping: The security state coefficient that the event described
> + * by this structure generates.
> + * @COE: The struct tsem_COE structure that describes the Context Of
> + * Execution that generated the event described by this
> + * structure.
> + * @file: If the security event references a file this structure will
> + * contain the struct tsem_file structure that describes the
> + * characteristics of the file.
> + * @CELL: The CELL union is used to hold the data structures that
> + * characterize the CELL identity of the event.
> + * @CELL.event_type: In the case of a generically modeled event this
> + * member will contain the enumeration value
> + * identifying the event.
> + * @CELL.mmap_file: The structure describing the characteristics of
> + * a mmap_file security event.
> + * @CELL.socket_create: The structure describing the characteristics
> + * of a socket_create security event.
> + * @CELL.socket_connect: The structure describing the characteristics
> + * of a socket_connect security event.
> + * @CELL.socket_accept: The structure describing the characteristics
> + * of a socket accept security event.
> + * @CELL.task_kill: The structure describing the characteristics of a
> + * task_kill security event.
> + *
> + * This structure is the primary data structure for describing
> + * security events that are registered in a security modeling
> + * namespace. Each unique security coefficient in the namespace will
> + * have one of these structures attached to it.
> + *
> + * This structure encapsulates the following three major sources of
> + * information about the event:
> + *
> + * * A description of the process initiating the event.
> + * * The characteristics that form the COE identity of the event.
> + * * The characteristics that form the CELL identity of the event.
> + *
> + * Since one event description has to ultimately characterize any
> + * security event that can occur the strategy is to use a union that
> + * contains security event specific structures that describe the
> + * characteristics of the event.
> + *
> + * The kref member of this structure is used to signal when the
> + * structure is to be deleted. For example, in the case of an
> + * externally modeled event, when the export of the event description
> + * is complete. In the case of an internally modeled namespace the
> + * structure will be released if it represents a security state
> + * coefficient that is already present in the model.
> + *
> + * The work member of this structure is used to support asynchronous
> + * updates to a TPM for the root modeling domain. Asynchronous
> + * updates are used to improve the performance of modeling and to
> + * handle security events that are running in atomic context and
> + * cannot be scheduled away while the TPM transaction completes.
> + *
> + * The tsem_event_allocate() function is called by a TSEM security
> + * event handler to allocate and populate this structure. The struct
> + * tsem_event_parameters structure is used to encapsulate all of the
> + * different structure types that are needed to characterize all of
> + * the different security events that occur.
> + *
> + * The tsem_event_allocate() function is called by either the
> + * tsem_map_event() or tsem_map_event_locked() functions. After
> + * allocating and populating an event description structure the
> + * mapping functions generate a security state coefficient from the
> + * information in this structure.
> + *
> + * The two separate function call points for mapping are to allow the
> + * security event handlers to indicate the context in which the
> + * security event is occurring, ie. sleeping or atomic context. After
> + * this point the context of the security event is represented by the
> + * locked member of this structure.
> + *
> + * After the event is mapped this structure is either passed to the
> + * internal trusted modeling agent or the contents of this structure
> + * is exported to the trust orchestrator attached to the namespace for
> + * modeling by an external trust modeling agent.
> + */
> +struct tsem_event {
> + struct kref kref;
> + struct list_head list;
> + struct work_struct work;
> +
> + enum tsem_event_type event;
> + bool locked;
> + pid_t pid;
> + char *pathname;
> + char comm[TASK_COMM_LEN];
> +
> + unsigned int digestsize;
> + u8 task_id[HASH_MAX_DIGESTSIZE];
> + u8 mapping[HASH_MAX_DIGESTSIZE];
> +
> + struct tsem_COE COE;
> + struct tsem_file file;
> +
> + union {
> + u32 event_type;
> + struct tsem_mmap_file_args mmap_file;
> + struct tsem_socket_create_args socket_create;
> + struct tsem_socket_connect_args socket_connect;
> + struct tsem_socket_accept_args socket_accept;
> + struct tsem_task_kill_args task_kill;
> + } CELL;
> +};
> +
> +/**
> + * struct tsem_event_parameters - Security event argument descriptions
> + * @u: A union that encapsulates all of the different structures that
> + * are used to characterize the argument so the TSEM security
> + * event handlers.
> + * @u.event_type: This structure member holds the enum tsem_event_type
> + * enumeration value of the event whose characteristics
> + * are encapsulated in the function.
> + * @u.file: If the security event references a VFS file this member
> + * hold a pointer to the description of the file.
> + _file event.
> + * @u.socket_create: This member will point to a structure that
> + * describes the characteristics of a socket_create
> + * event.
> + * @u.socket_connect: This member will point to a structure that
> + * describes the characteristics of a socket_connect
> + * event.
> + * @u.socket_accept: This member will point to a structure that
> + * describes the characteristics of a socket_accept
> + * event.
> + * @u.task_kill: This member will point to a structure that describes
> + * the characteristics of a task_kill function.
> + *
> + * The purpose of this structure is to provide a common encapsulation
> + * method for passing the CELL characteristics of a security event
> + * into the tsem_event_init() function. The characteristics passed in
> + * this event will be used to create and populate the struct
> + * tsem_event structure that will go on to be used to characterize
> + * the event either an internal or external modeling agent.
> + *
> + * The strategy followed is to allocate one of these structures on the
> + * stack for a security event call along with a call specific
> + * characteristics description structure, both of which will no longer
> + * be needed after completion of the call since the requisite
> + * information has been transferred to a struct tsem_event structure.
> + */
> +struct tsem_event_parameters {
> + union {
> + u32 event_type;
> + struct file *file;
> + struct tsem_mmap_file_args *mmap_file;
> + struct tsem_socket_create_args *socket_create;
> + struct tsem_socket_connect_args *socket_connect;
> + struct tsem_socket_accept_args *socket_accept;
> + struct tsem_task_kill_args *task_kill;
> + } u;
> +};
> +
> +/**
> + * struct tsem_event_point - TSEM security coefficient characteristics.
> + * @list: The list structure used to link together all of the security
> + * state coefficients for a modeling namespace.
> + * @valid: A boolean value use to indicate whether or not the security
> + * state point is a valid coefficient in the model.
> + * @count: The number of times this coefficient has been expressed by
> + * security model for the namespace.
> + * @point: The security state coefficient for the point created by
> + * the cryptographic hash function being used for the modeling
> + * namespace.
> + *
> + * This structure is used by internal trusted modeling agents to
> + * represent each unique state point in a security model. Security
> + * state coefficients are unique within a model so only one struct
> + * tsem_event_point structure will be generated regardless of how many
> + * times the security event that generates the point occurs. The
> + * count member of this structure represents the total number of
> + * security events that have occurred that have generated the point.
> + *
> + * The valid member of this structure is used to flag whether this
> + * is consistent with the model for the namespace or was generated by
> + * a 'forensic', ie. out of model, event.
> + *
> + * Within each security namespace these structures are linked together
> + * in a list that describes the functional value of the security model
> + * assigned to the namespace. Entries are only added to this list and
> + * never removed.
> + *
> + * The desired state of a security model is created by using the TSEM
> + * control plane to inject a list of acceptable security state
> + * coefficients into the model. Sealing a model causes any security
> + * events that produce a coefficient different from those already in
> + * the model to be rejected as an invalid security event and logged as
> + * a forensic event for the model.
> + */
> +struct tsem_event_point {
> + struct list_head list;
> + bool valid;
> + u64 count;
> + u8 point[HASH_MAX_DIGESTSIZE];
> +};
> +
> +/**
> + * struct tsem_inode - TSEM inode status structure.
> + * @mutex: The mutex that will protect the list of struct
> + * tsem_inode_digest structures that have been created for the
> + * inode containing a struct tsem_inode structure.
> + * @list: The list structure that points to the list of struct
> + * tsem_inode_structures.
> + * @status: The digest collection state of the inode. See the
> + * discussion of enum tsem_inode_state for what information
> + * is conveyed by the value of this structure member.
> +
> + * This structure is the second of the two primary control structures
> + * that are implemented through the LSM blob functionality. It is
> + * automatically created when the inode structure is created for
> + * system resources that are referenced by a struct inode structure.
> + *
> + * This structure has two primary purposes. The status member is used
> + * to signal that the tsem_file_open() function should return that
> + * permission to access the file is returned when the security hook is
> + * invoked by the integrity_kernel_read() function. See the
> + * discussion on enum tsem_inode_state to more details.
> + *
> + * The second purpose of this structure is to maintain a list of
> + * digest values that have been computed by the inode that this
> + * structure references. Maintenance of multiple digest values is
> + * required since there is no concept of a fixed digest function for
> + * TSEM as each modeling namespace can have its own digest function.
> + *
> + * Each digest value in use has a struct tsem_inode_digest structure
> + * allocated for it. The digest_list member of this structure points
> + * to a list of these structures.
> + *
> + * The mutex implemented in this structure should be held by any
> + * process that is accessing the list.
> + */
> +struct tsem_inode {
> + struct mutex mutex;
> + struct list_head digest_list;
> + enum tsem_inode_state status;
> +};
> +
> +/**
> + * struct tsem_inode_digest - Digest specific file checksum.
> + * @list: The list structure used to link multiple digest values
> + * for an inode.
> + * @version: The version number of the inode that generated the digest
> + * value that is currently represented.
> + * @name: A pointer to a null-terminated character buffer containing
> + * the name of the hash function that generated the current
> + * digest value.
> + * @value: The digest value of the file.
> + *
> + * A linked list of these structures is maintained for each inode that
> + * is modeled by TSEM and is used to support multiple hash specific
> + * digest values for a file represented by the inode. The tsem_inode
> + * structure that represents the TSEM security status of the inode
> + * contains the pointer to this list of structures.
> + *
> + * The version member of the structure contains the inode version number
> + * that was in effect when the last digest value of this type was computed.
> + * This version number value is used to detect changes and to trigger an
> + * update of the digest value.
> + *
> + * The name member of structure contains the name of the hash function
> + * that generated the checksum value. This name is used to locate the
> + * correct structure by comparing its value against the hash function
> + * that is being used for the modeling domain that is accessing the
> + * inode.
> + */
> +struct tsem_inode_digest {
> + struct list_head list;
> + char *name;
> + u64 version;
> + u8 value[HASH_MAX_DIGESTSIZE];
> +};
> +
> +/*
> + * The following three variables are the only globally visible
> + * variables used in the TSEM implementation.
> + *
> + * The tsem_blob_sizes variable is used by the LSM infrastructure to
> + * describe the amount of space that will be needed by the struct
> + * tsem_task and struct tsem_inode structures.
> + *
> + * The tsem_names array is defined in the tsem.c file and contains an
> + * array of pointers to the strings that define the names for each of
> + * the TSEM security event handles. The enum tsem_event_type
> + * enumeration indexes this array.
> + *
> + * The tsem_root_actions array is also indexed by the enum
> + * tsem_event_type enumeration and is used to determine the type of
> + * response that a TSEM security event handler is to return to the
> + * caller, ie. either logging or enforcing. The contents of this
> + * array is inherited by copying the array into the struct
> + * tsem_context structure for modeling namespaces that are subordinate
> + * to the root model.
> + */
> +extern struct lsm_blob_sizes tsem_blob_sizes;
> +extern const char * const tsem_names[TSEM_EVENT_CNT];
> +extern enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT];
> +
> +/*
> + * The following section of the file contains the definitions for the
> + * externally visible functions in each of the TSEM compilation units.
> + */
> +extern struct dentry *tsem_fs_create_external(const char *name);
> +extern void tsem_fs_show_trajectory(struct seq_file *c, struct tsem_event *ep);
> +extern void tsem_fs_show_field(struct seq_file *c, const char *field);
> +extern void tsem_fs_show_key(struct seq_file *c, char *term, char *key,
> + char *fmt, ...);
> +extern int tsem_fs_init(void);
> +
> +extern struct tsem_model *tsem_model_allocate(size_t size);
> +extern void tsem_model_free(struct tsem_context *ctx);
> +extern int tsem_model_event(struct tsem_event *ep);
> +extern int tsem_model_load_point(u8 *point);
> +extern int tsem_model_load_pseudonym(u8 *mapping);
> +extern int tsem_model_has_pseudonym(struct tsem_inode *tsip,
> + struct tsem_file *ep);
> +extern void tsem_model_load_base(u8 *mapping);
> +extern int tsem_model_add_aggregate(void);
> +extern void tsem_model_compute_state(void);
> +extern void tsem_model_magazine_free(struct tsem_model *model);
> +extern int tsem_model_cache_init(struct tsem_model *model, size_t size);
> +
> +extern void tsem_ns_put(struct tsem_context *ctx);
> +extern int tsem_ns_event_key(u8 *task_key, const char *keystr, u8 *key);
> +extern int tsem_ns_create(const enum tsem_control_type type,
> + const char *digest, const enum tsem_ns_reference ns,
> + const char *key, const unsigned int cache_size);
> +
> +extern int tsem_export_show(struct seq_file *m, void *v);
> +extern int tsem_export_event(struct tsem_event *ep);
> +extern int tsem_export_action(enum tsem_event_type event, bool locked);
> +extern int tsem_export_aggregate(void);
> +extern int tsem_export_magazine_allocate(struct tsem_external *ext,
> + size_t size);
> +extern void tsem_export_magazine_free(struct tsem_external *ext);
> +extern int tsem_export_cache_init(void);
> +
> +extern int tsem_map_task(struct file *file, u8 *mapping);
> +struct tsem_event *tsem_map_event(enum tsem_event_type event,
> + struct tsem_event_parameters *param);
> +struct tsem_event *tsem_map_event_locked(enum tsem_event_type event,
> + struct tsem_event_parameters *param);
> +
> +extern struct tsem_event *tsem_event_allocate(bool locked);
> +extern struct tsem_event *tsem_event_init(enum tsem_event_type event,
> + struct tsem_event_parameters *params,
> + bool locked);
> +extern void tsem_event_put(struct tsem_event *ep);
> +extern void tsem_event_get(struct tsem_event *ep);
> +extern int tsem_event_magazine_allocate(struct tsem_context *ctx, size_t size);
> +extern void tsem_event_magazine_free(struct tsem_context *ctx);
> +extern int tsem_event_cache_init(void);
> +
> +extern u8 *tsem_trust_aggregate(void);
> +extern int tsem_trust_add_event(struct tsem_event *ep);
> +
> +/*
> + * The remaining inline function declarations follow the design
> + * pattern of the other LSM's and implement functions that return
> + * various TSEM characteristics of tasks, modeling contexts and
> + * inodes.
> + */
> +static inline struct tsem_task *tsem_task(struct task_struct *task)
> +{
> + return task->security + tsem_blob_sizes.lbs_task;
> +}
> +
> +static inline bool tsem_task_trusted(struct task_struct *task)
> +{
> + return tsem_task(task)->trust_status & TSEM_TASK_TRUSTED;
> +}
> +
> +static inline bool tsem_task_untrusted(struct task_struct *task)
> +{
> + return tsem_task(task)->trust_status & ~TSEM_TASK_TRUSTED;
> +}
> +
> +static inline struct tsem_context *tsem_context(struct task_struct *task)
> +{
> + return tsem_task(task)->context;
> +}
> +
> +static inline struct tsem_model *tsem_model(struct task_struct *task)
> +{
> + return tsem_task(task)->context->model;
> +}
> +
> +static inline struct tsem_inode *tsem_inode(struct inode *inode)
> +{
> + return inode->i_security + tsem_blob_sizes.lbs_inode;
> +}
> +
> +static inline struct crypto_shash *tsem_digest(void)
> +{
> + return tsem_context(current)->tfm;
> +}
> +
> +static inline unsigned int tsem_digestsize(void)
> +{
> + return crypto_shash_digestsize(tsem_digest());
> +}
On 7/10/2023 3:23 AM, Dr. Greg wrote:
> The tsem.c file is the 'master' file in the TSEM implementation.
> It is responsible for initializing the LSM and providing
> implementions of the security event hooks implemented by TSEM.
>
> In addition to initializing the LSM, the set_ready() function
> implements a secondary initialization that is used to to indicate
> that security event modeling can begin. This is required
> secondary to the fact that the cryptographic API's do not become
> ready until after the 'fs' phase of system initialization is
> complete.
>
> This file also handles the implementation of kernel command-line
> parameters that are used to configure the root security modeling
> namespace.
>
> The 'tsem_mode' parameter, if set to a value of 1, causes modeling
> to be not conducted for the root security modeling namespace.
> One use case is to allow development platform to develop security
> models without the overhead of full platform modeling.
>
> The 'tsem_digest' parameter is used to set the cryptographic hash
> function that is used to generate security state coefficients in
> the root model. TSEM can use any cryptographic hash function
> implemented in the kernel, on a namespace by namespace basis.
> Subordinate modeling namespaces can select their hash function
> as part of the namespace creation process but the 'tsem_digest'
> parameter has to be used to set the function for the root
> modeling namespace.
>
> The hash function used in the root modeling namespace but be
> compiled into the kernel since the function is used before module
> loading becomes available. The TSEM kernel configuration selects
> the SHA256 function to be included as the default cryptographic
> modeling function.
>
> The 'tsem_cache' variable sets the size of the pre-allocated
> structures that are used for security event modeling in the root
> modeling namespace. This cache is used to support the modeling
> and export of events that run in atomic context. The cache size
> can be set independently for each subordinate security modeling
> on a namespace by namespace basis.
>
> This file also contains the implementation of the tsem_names
> array that contains the ASCII text names that are assigned to
> each security event handler. This name is used as one of the
> characteristics in the security state points that are generated.
> This array is also used to provide symbolic names for the export
> of security event descriptions, either through the TSEM control
> plane or for export to external trust orchestrators.
>
> Signed-off-by: Greg Wettstein <[email protected]>
> ---
> security/tsem/tsem.c | 1987 ++++++++++++++++++++++++++++++++++++++++++
Please use kernel doc comments throughout.
I've made a few minor comments below, but you need to break this up
somehow for review. Also, having the data definitions elsewhere makes
review tough.
> 1 file changed, 1987 insertions(+)
> create mode 100644 security/tsem/tsem.c
>
> diff --git a/security/tsem/tsem.c b/security/tsem/tsem.c
> new file mode 100644
> index 000000000000..8ec630354240
> --- /dev/null
> +++ b/security/tsem/tsem.c
> @@ -0,0 +1,1987 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +/*
> + * Copyright (C) 2023 Enjellic Systems Development, LLC
> + * Author: Dr. Greg Wettstein <[email protected]>
> + *
> + * TSEM initialization infrastructure.
> + */
> +#define TRAPPED_MSG_LENGTH 128
> +
> +#define LOCKED true
> +#define NOLOCK false
> +
> +#include <linux/magic.h>
> +#include <linux/mman.h>
> +#include <linux/binfmts.h>
> +#include <linux/bpf.h>
> +#include <linux/ipv6.h>
> +
> +#include "tsem.h"
> +
> +struct lsm_blob_sizes tsem_blob_sizes __ro_after_init = {
> + .lbs_task = sizeof(struct tsem_task),
Do you really want this for the task? It would seem you might
want to use the cred blob instead.
> + .lbs_inode = sizeof(struct tsem_inode)
> +};
> +
> +enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT] = {
> + TSEM_ACTION_EPERM /* Undefined. */
> +};
> +
> +static struct tsem_model root_model = {
> + .point_lock = __SPIN_LOCK_INITIALIZER(root_model.point_lock),
> + .point_list = LIST_HEAD_INIT(root_model.point_list),
> + .point_end_mutex = __MUTEX_INITIALIZER(root_model.point_end_mutex),
> +
> + .trajectory_lock = __SPIN_LOCK_INITIALIZER(root_model.trajectory_lock),
> + .trajectory_list = LIST_HEAD_INIT(root_model.trajectory_list),
> + .trajectory_end_mutex = __MUTEX_INITIALIZER(root_model.trajectory_end_mutex),
> +
> + .forensics_lock = __SPIN_LOCK_INITIALIZER(root_model.forensics_lock),
> + .forensics_list = LIST_HEAD_INIT(root_model.forensics_list),
> + .forensics_end_mutex = __MUTEX_INITIALIZER(root_model.forensics_end_mutex),
> +
> + .pseudonym_mutex = __MUTEX_INITIALIZER(root_model.pseudonym_mutex),
> + .pseudonym_list = LIST_HEAD_INIT(root_model.pseudonym_list)
> +};
> +
> +static struct tsem_context root_context;
> +
> +static int tsem_ready __ro_after_init;
> +
> +static bool tsem_available __ro_after_init;
> +
> +static unsigned int magazine_size __ro_after_init = TSEM_ROOT_MAGAZINE_SIZE;
> +
> +static bool no_root_modeling __ro_after_init;
> +
> +static char *default_hash_function __ro_after_init;
> +
> +static int __init set_magazine_size(char *magazine_value)
> +{
> + if (kstrtouint(magazine_value, 0, &magazine_size))
> + pr_warn("tsem: Failed to parse root cache size.\n");
> +
> + if (!magazine_size) {
> + pr_warn("tsem: Forcing non-zero cache size.\n");
> + magazine_size = TSEM_ROOT_MAGAZINE_SIZE;
> + }
> +
> + pr_info("tsem: Setting default root cache size to %u.\n",
> + magazine_size);
> + return 1;
> +}
> +__setup("tsem_cache=", set_magazine_size);
> +
> +static int __init set_modeling_mode(char *mode_value)
> +{
> + unsigned long mode = 0;
> +
> + if (kstrtoul(mode_value, 0, &mode)) {
> + pr_warn("tsem: Failed to parse modeling mode.\n");
> + return 1;
> + }
> +
> + if (mode == 1)
> + no_root_modeling = true;
> + else
> + pr_warn("tsem: Unknown mode specified.\n");
> + return 1;
> +}
> +__setup("tsem_mode=", set_modeling_mode);
> +
> +static int __init set_default_hash_function(char *hash_function)
> +{
> +
> + default_hash_function = hash_function;
> + return 1;
> +}
> +__setup("tsem_digest=", set_default_hash_function);
> +
> +const char * const tsem_names[TSEM_EVENT_CNT] = {
> + "undefined",
> + "bprm_set_creds",
> + "generic_event",
> + "task_kill",
> + "task_setpgid",
> + "task_getpgid",
> + "task_getsid",
> + "task_setnice",
> + "task_setioprio",
> + "task_getioprio",
> + "task_prlimit",
> + "task_setrlimit",
> + "task_setscheduler",
> + "task_getscheduler",
> + "task_prctl",
> + "file_open",
> + "mmap_file",
> + "file_ioctl",
> + "file_lock",
> + "file_fcntl",
> + "file_receive",
> + "unix_stream_connect",
> + "unix_may_send",
> + "socket_create",
> + "socket_connect",
> + "socket_bind",
> + "socket_accept",
> + "socket_listen",
> + "socket_socketpair",
> + "socket_sendmsg",
> + "socket_recvmsg",
> + "socket_getsockname",
> + "socket_getpeername",
> + "socket_setsockopt",
> + "socket_shutdown",
> + "ptrace_traceme",
> + "kernel_module_request",
> + "kernel_load_data",
> + "kernel_read_file",
> + "sb_mount",
> + "sb_umount",
> + "sb_remount",
> + "sb_pivotroot",
> + "sb_statfs",
> + "move_mount",
> + "shm_associate",
> + "shm_shmctl",
> + "shm_shmat",
> + "sem_associate",
> + "sem_semctl",
> + "sem_semop",
> + "syslog",
> + "settime",
> + "quotactl",
> + "quota_on",
> + "msg_queue_associate",
> + "msg_queue_msgctl",
> + "msg_queue_msgsnd",
> + "msg_queue_msgrcv",
> + "ipc_permission",
> + "key_alloc",
> + "key_permission",
> + "netlink_send",
> + "inode_create",
> + "inode_link",
> + "inode_unlink",
> + "inode_symlink",
> + "inode_mkdir",
> + "inode_rmdir",
> + "inode_mknod",
> + "inode_rename",
> + "inode_setattr",
> + "inode_getattr",
> + "inode_setxattr",
> + "inode_getxattr",
> + "inode_listxattr",
> + "inode_removexattr",
> + "inode_killpriv",
> + "tun_dev_create",
> + "tun_dev_attach_queue",
> + "tun_dev_attach",
> + "tun_dev_open",
> + "bpf",
> + "bpf_map",
> + "bpf_prog"
> +};
> +
> +static const int pseudo_filesystems[] = {
> + PROC_SUPER_MAGIC,
> + SYSFS_MAGIC,
> + DEBUGFS_MAGIC,
> + TMPFS_MAGIC,
> + DEVPTS_SUPER_MAGIC,
> + BINFMTFS_MAGIC,
> + SECURITYFS_MAGIC,
> + SELINUX_MAGIC,
> + SMACK_MAGIC,
> + CGROUP_SUPER_MAGIC,
> + CGROUP2_SUPER_MAGIC,
> + NSFS_MAGIC,
> + EFIVARFS_MAGIC
> +};
> +
> +static bool bypass_inode(struct inode *inode)
> +{
> + bool retn = true;
> +
> + unsigned int lp;
> +
> + if (!S_ISREG(inode->i_mode))
> + goto done;
> +
> + for (lp = 0; lp < ARRAY_SIZE(pseudo_filesystems); ++lp)
> + if (inode->i_sb->s_magic == pseudo_filesystems[lp])
> + goto done;
> + retn = false;
> +
> + done:
> + return retn;
> +}
> +
> +static int event_action(struct tsem_context *ctx, enum tsem_event_type event)
> +{
> + int retn = 0;
> +
> + if (tsem_task_trusted(current))
> + return retn;
> +
> + if (ctx->actions[event] == TSEM_ACTION_EPERM)
> + retn = -EPERM;
> +
> + return retn;
> +}
> +
> +static int return_trapped_task(enum tsem_event_type event, char *msg,
Why isn't this simply "trapped_task()"?
> + bool locked)
> +{
> + int retn;
> + struct tsem_context *ctx = tsem_context(current);
> +
> + pr_warn("Untrusted %s: comm=%s, pid=%d, parameters='%s'\n",
> + tsem_names[event], current->comm, task_pid_nr(current), msg);
> +
> + if (ctx->external) {
> + retn = tsem_export_action(event, locked);
> + if (retn)
> + return retn;
> + }
> +
> + return event_action(ctx, event);
> +}
> +
> +static int return_trapped_inode(enum tsem_event_type event,
Again, really odd function name.
... and that's all I have time for.
On Mon, Aug 07, 2023 at 01:39:22PM -0700, Casey Schaufler wrote:
Hi Casey, thank you for the review comments.
> On 7/10/2023 3:23 AM, Dr. Greg wrote:
> > TSEM is designed, from a functional perspective, to be contained
> > entirely in its own directory.
> >
> > The tsem.h header file defines the enumeration types, structure
> > definitions and externally visiable functions that are referenced
> > by all of the compilation units of the TSEM LSM implementation in
> > that directory.
> Extensive documentation notwithstanding, it's impossible to review
> the data structures and constants without the code that goes along
> with them.
Big picture, I couldn't resist passing along an actual quote from my
mentor in Computer Science and modeling theory. An algebraic
topologist by training who had Robert Ellis once briefly convinced
that it could be proven that it was possible to comb the hair on a
coconut, who also did significant work in data structures:
"If one carefully studies and understands the data structures
acted upon, including a description of their character, role
and importance; the code itself, upon simple examination,
will speak clearly, eloquently and stridently with respect to
its intent and purpose for existence."
:-)
We approached the patch series from the perspective of tsem.h being a
reference document for the compilation units.
The rules are pretty simple. Any structure, variable or function with
a tsem_ prefix, in some form of appropriate capitalization, can be
found quickly in tsem.h, along with what we hope is a complete
description on how and why the entity is implemented.
We were somewhat influenced by SMACK that uses a single include file
for its globally visible state.
Other LSM's seem to use multiple include files, is that a preferred
approach?
The tsem_event structure is the poster child of the challenges
associated with the latter model. It gets used by all eight compilation
units so its declaration is going to be isolated from its use in most
cases.
This model seems to inevitably come down to something like 'common.h'
which puts you sort of back to where things started.
We may have missed it but I don't see anything in the kernel
developers guide with respect best practices for introducing blocks of
entire sub-system code.
Recommendations welcome.
> > The structure and enumeration types are extensively documented
> > and are the recommended starting point for understanding TSEM
> > implementation and functionality.
> >
> > Signed-off-by: Greg Wettstein <[email protected]>
> > ---
> > security/tsem/tsem.h | 1516 ++++++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 1516 insertions(+)
> > create mode 100644 security/tsem/tsem.h
> >
> > diff --git a/security/tsem/tsem.h b/security/tsem/tsem.h
> > new file mode 100644
> > index 000000000000..03915f47529b
> > --- /dev/null
> > +++ b/security/tsem/tsem.h
> > @@ -0,0 +1,1516 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +
> > +/*
> > + * Copyright (C) 2023 Enjellic Systems Development, LLC
> > + * Author: Dr. Greg Wettstein <[email protected]>
> > + *
> > + * This is the single include file that documents all of the externally
> > + * visible types and functions that are used by TSEM. This file is
> > + * currently organized into four major sections in the following order;
> > + *
> > + * includes used by all compilation units
> > + * CPP definitions
> > + * enumeration types
> > + * structure definitions
> > + * function declarations
> > + * inline encapsulation functions.
> > + *
> > + * Include files that are referenced by more than a single compilation
> > + * should be included in this file. Includes that are needed to
> > + * satisfy compilation requirements for only a single file should be
> > + * included in the file needing that include.
> > + *
> > + * Understanding the overall implementation and architecture of TSEM
> > + * will be facilitated by reviewing the documentation in this file.
> > + */
> > +
> > +#include <uapi/linux/in.h>
> > +#include <uapi/linux/in6.h>
> > +#include <linux/wait.h>
> > +#include <linux/kref.h>
> > +#include <linux/lsm_hooks.h>
> > +#include <linux/capability.h>
> > +#include <crypto/hash.h>
> > +#include <crypto/hash_info.h>
> > +#include <net/af_unix.h>
> > +
> > +/* The capability needed to manage TSEM. */
> > +#define TSEM_CONTROL_CAPABILITY CAP_ML
> Why would you do this? You gave the capability a name that even you
> don't want to use.
A simple placeholder that allows the capability used to control
security modeling to be set in one place, and one place only, until
the debate over the capability issue is settled.
We never envisioned this define lasting beyond the initial reviews.
> > +
> > +/*
> > + * The number of 'slots' in the structure magazines that are used to
> > + * satisfy modeling of security events that are called in atomic context.
> > + */
> > +#define TSEM_ROOT_MAGAZINE_SIZE 96
> > +#define TSEM_MAGAZINE_SIZE_INTERNAL 16
> > +#define TSEM_MAGAZINE_SIZE_EXTERNAL 96
> > +
> > +/**
> > + * enum tsem_event_type - Ordinal value for a security event.
> > + * @TSEM_BPRM_SET_CREDS: Ordinal value for bprm_creds_for_exec.
> > + * @TSEM_GENERIC_EVENT: Ordinal value for a generically modeled event.
> > + * @TSEM_TASK_KILL: Ordinal value for task kill.
> > + * @....: Remainder follows with a similar naming format that has
> > + * TSEM_ prep ended to the raw LSM security hook name.
> > + * @TSEM_EVENT_CNT: The final ordinal value is used to define the
> > + * length of the following arrays that are indexed
> > + * by the ordinal value of the hook:
> > + *
> > + * This enumeration is used to designate an ordinal value for each
> > + * security event, ie. LSM hook, that TSEM is implementing modeling
> > + * for. This value is used to identify the hook that is either having
> > + * its event description being exported to an external Trusted Modeling
> > + * Agent (TMA) or modeled by the internal TMA implementation.
> > + *
> > + * The primary use of this enumeration is to conditionalize code paths
> > + * based on the security hook being processed and to index the
> > + * tsem_names array and the array that defines the action that is to
> > + * be taken in response to an event that generates a permissions
> > + * violation.
> > + */
> > +enum tsem_event_type {
> > + TSEM_BPRM_SET_CREDS = 1,
> > + TSEM_GENERIC_EVENT,
> > + TSEM_TASK_KILL,
> > + TSEM_TASK_SETPGID,
> > + TSEM_TASK_GETPGID,
> > + TSEM_TASK_GETSID,
> > + TSEM_TASK_SETNICE,
> > + TSEM_TASK_SETIOPRIO,
> > + TSEM_TASK_GETIOPRIO,
> > + TSEM_TASK_PRLIMIT,
> > + TSEM_TASK_SETRLIMIT,
> > + TSEM_TASK_SETSCHEDULER,
> > + TSEM_TASK_GETSCHEDULER,
> > + TSEM_TASK_PRCTL,
> > + TSEM_FILE_OPEN,
> > + TSEM_MMAP_FILE,
> > + TSEM_FILE_IOCTL,
> > + TSEM_FILE_LOCK,
> > + TSEM_FILE_FCNTL,
> > + TSEM_FILE_RECEIVE,
> > + TSEM_UNIX_STREAM_CONNECT,
> > + TSEM_UNIX_MAY_SEND,
> > + TSEM_SOCKET_CREATE,
> > + TSEM_SOCKET_CONNECT,
> > + TSEM_SOCKET_BIND,
> > + TSEM_SOCKET_ACCEPT,
> > + TSEM_SOCKET_LISTEN,
> > + TSEM_SOCKET_SOCKETPAIR,
> > + TSEM_SOCKET_SENDMSG,
> > + TSEM_SOCKET_RECVMSG,
> > + TSEM_SOCKET_GETSOCKNAME,
> > + TSEM_SOCKET_GETPEERNAME,
> > + TSEM_SOCKET_SETSOCKOPT,
> > + TSEM_SOCKET_SHUTDOWN,
> > + TSEM_PTRACE_TRACEME,
> > + TSEM_KERNEL_MODULE_REQUEST,
> > + TSEM_KERNEL_LOAD_DATA,
> > + TSEM_KERNEL_READ_FILE,
> > + TSEM_SB_MOUNT,
> > + TSEM_SB_UMOUNT,
> > + TSEM_SB_REMOUNT,
> > + TSEM_SB_PIVOTROOT,
> > + TSEM_SB_STATFS,
> > + TSEM_MOVE_MOUNT,
> > + TSEM_SHM_ASSOCIATE,
> > + TSEM_SHM_SHMCTL,
> > + TSEM_SHM_SHMAT,
> > + TSEM_SEM_ASSOCIATE,
> > + TSEM_SEM_SEMCTL,
> > + TSEM_SEM_SEMOP,
> > + TSEM_SYSLOG,
> > + TSEM_SETTIME,
> > + TSEM_QUOTACTL,
> > + TSEM_QUOTA_ON,
> > + TSEM_MSG_QUEUE_ASSOCIATE,
> > + TSEM_MSG_QUEUE_MSGCTL,
> > + TSEM_MSG_QUEUE_MSGSND,
> > + TSEM_MSG_QUEUE_MSGRCV,
> > + TSEM_IPC_PERMISSION,
> > + TSEM_KEY_ALLOC,
> > + TSEM_KEY_PERMISSION,
> > + TSEM_NETLINK_SEND,
> > + TSEM_INODE_CREATE,
> > + TSEM_INODE_LINK,
> > + TSEM_INODE_UNLINK,
> > + TSEM_INODE_SYMLINK,
> > + TSEM_INODE_MKDIR,
> > + TSEM_INODE_RMDIR,
> > + TSEM_INODE_MKNOD,
> > + TSEM_INODE_RENAME,
> > + TSEM_INODE_SETATTR,
> > + TSEM_INODE_GETATTR,
> > + TSEM_INODE_SETXATTR,
> > + TSEM_INODE_GETXATTR,
> > + TSEM_INODE_LISTXATTR,
> > + TSEM_INODE_REMOVEXATTR,
> > + TSEM_INODE_KILLPRIV,
> > + TSEM_TUN_DEV_CREATE,
> > + TSEM_TUN_DEV_ATTACH_QUEUE,
> > + TSEM_TUN_DEV_ATTACH,
> > + TSEM_TUN_DEV_OPEN,
> > + TSEM_BPF,
> > + TSEM_BPF_MAP,
> > + TSEM_BPF_PROG,
> > + TSEM_EVENT_CNT
> > +};
> > +
> > +/**
> > + * enum tsem_action_type - Ordinal value for security responses.
> > + * @TSEM_ACTION_LOG: Ordinal value to indicate that a security event
> > + * that results in a model permissions violation
> > + * should be logged.
> > + * @TSEM_ACTION_EPERM: Ordinal value to indicate that a security event
> > + * generating a model permissions violation should
> > + * return -EPERM to the caller.
> > + *
> > + * This enumeration type is used to designate what type of action is
> > + * to be taken when the processing of a security event hook results in
> > + * a model violation. The TSEM_ACTION_LOG and TSEM_ACTION_EPERM
> > + * translate into the classical concepts of logging or enforcing
> > + * actions used by other mandatory access control architectures.
> > + */
> > +enum tsem_action_type {
> > + TSEM_ACTION_LOG = 0,
> > + TSEM_ACTION_EPERM,
> > + TSEM_ACTION_CNT
> > +};
> > +
> > +/**
> > + * enum tsem_control_type - Ordinal values for TSEM control actions.
> > + * @TSEM_CONTROL_INTERNAL: This ordinal value is set when the first
> > + * word of an argument string written to the
> > + * control file is the word 'internal'. This
> > + * designates that the security namespace will
> > + * be modeled by the internal TMA.
> > + * @TSEM_CONTROL_EXTERNAL: This ordinal value is set when the first
> > + * word of an argument string written to the
> > + * control file is the word 'external'. This
> > + * designates that the security namespace will
> > + * be model by an external TMA.
> > + * @TSEM_CONTROL_ENFORCE: This ordinal value is set when the word
> > + * 'enforce' is written to the control file.
> > + * This indicates that model is to be placed
> > + * in 'enforcing' mode and security events that
> > + * result in model violations will return EPERM.
> > + * @TSEM_CONTROL_SEAL: This ordinal value is set when the word 'seal'
> > + * is written to the control file. This indicates
> > + * that the model for security domain will treat
> > + * all security events that do not conform to the
> > + * model as 'forensics' events.
> > + * @TSEM_CONTROL_TRUSTED: This ordinal value is used when the first
> > + * word of an argument string written to the
> > + * control file is the word 'trusted'. This
> > + * is interpreted as a directive to set the
> > + * trust status of the task that executed the
> > + * security event to be trusted.
> > + * @TSEM_CONTROL_UNTRUSTED: This ordinal value is used when the first
> > + * word of an argument string written to the
> > + * control file is the word 'untrusted'.
> > + * This is interpreted as a directive to set
> > + * the trust status of the task that executed
> > + * the security event to be untrusted.
> > + * @TSEM_CONTROL_MAP_STATE: This ordinal value is used when the first
> > + * word of an argument string written to the
> > + * control file is the word 'state'. The
> > + * argument to this directive will be an
> > + * ASCII hexadecimally encoded string of the
> > + * current model's digest size that will be
> > + * treated as a security state point for
> > + * inclusion in the security model for the
> > + * security domain/namespace.
> > + * @TSEM_CONTROL_MAP_PSEUDONYM: This ordinal value is used when the
> > + * first word of an argument string
> > + * written to the control file is the
> > + * word 'pseudonym'. The argument to
> > + * this directive will be an ASCII
> > + * hexadecimally encoded string of the
> > + * current model's digest size that will
> > + * be treated as a pseudonym directive
> > + * for the security domain/namespace.
> > + * TSEM_CONTROL_MAP_BASE: This ordinal value is used when the first
> > + * word of an argument string written to the
> > + * control file is the word 'base'. The
> > + * argument to this directive will be an ASCII
> > + * hexadecimally encoded string of the current
> > + * model's digest size that will be treated as
> > + * the base value for the computation of the
> > + * functional values (measurement and state) of
> > + * the security domain/namespace.
> > +
> > + * This enumeration type is used to designate what type of control
> > + * action is to be implemented when arguments are written to the TSEM
> > + * control file (/sys/kernel/security/tsem/control). The ordinal
> > + * values govern the processing of the command and the interpretation
> > + * of the rest of the command argument string.
> > + */
> > +enum tsem_control_type {
> > + TSEM_CONTROL_INTERNAL = 0,
> > + TSEM_CONTROL_EXTERNAL,
> > + TSEM_CONTROL_ENFORCE,
> > + TSEM_CONTROL_SEAL,
> > + TSEM_CONTROL_TRUSTED,
> > + TSEM_CONTROL_UNTRUSTED,
> > + TSEM_CONTROL_MAP_STATE,
> > + TSEM_CONTROL_MAP_PSEUDONYM,
> > + TSEM_CONTROL_MAP_BASE
> > +};
> > +
> > +/**
> > + * enum tsem_ns_reference - Ordinal value for DAC namespace reference.
> > + * @TSEM_NS_INITIAL: This ordinal value indicates that the uid/gid
> > + * values should be interpreted against the initial
> > + * user namespace.
> > + * @TSEM_NS_CURRENT: This ordinal value indicates that the uid/gid
> > + * values should be interpreted against the user
> > + * namespace that is in effect for the process being
> > + * modeled.
> > + *
> > + * This enumeration type is used to indicate what user namespace
> > + * should be referenced when the uid/gid values are interpreted for
> > + * the creation of either the COE or CELL identities. The enumeration
> > + * ordinal passed to the tsem_ns_create() function, to configure the
> > + * security domain/namespace, is set by the nsref argument to either
> > + * the 'internal' or 'external' control commands.
> > + */
> > +enum tsem_ns_reference {
> > + TSEM_NS_INITIAL = 1,
> > + TSEM_NS_CURRENT
> > +};
> > +
> > +/**
> > + * enum tsem_task_trust - Ordinal value describing task trust status.
> > + * @TSEM_TASK_TRUSTED: This ordinal value indicates that the task has
> > + * not executed a security event that has resulted
> > + * in a security behavior not described by the
> > + * security model the task is being governed by.
> > + * @TSEM_TASK_UNTRUSTED: This ordinal value indicates that the task
> > + * has requested the execution of a security event
> > + * that resulted in a security behavior not
> > + * permitted by the security model the task is
> > + * being governed by.
> > + * @TSEM_TASK_TRUST_PENDING: This ordinal value indicates that the setting
> > + * of the task trust status is pending a response
> > + * from an external TMA.
> > + *
> > + * This enumeration type is used to specify the three different trust
> > + * states that a task can be in. The trust status of a task is
> > + * regulated by the trust_status member of struct tsem_task. A task
> > + * carrying the status of TSEM_TASK_TRUSTED means that it has
> > + * not requested the execution of any security events that are
> > + * inconsistent with the security model that the task is running in.
> > + *
> > + * If a task requests execution of a security event that is
> > + * inconsistent with the security model it is operating in, and the
> > + * domain is running in 'sealed' mode, the task trust status is set to
> > + * TSEM_TASK_UNTRUSTED. This value is 'sticky' in that it will be
> > + * propagated to any child tasks that are spawned from an untrusted
> > + * task.
> > + *
> > + * In the case of an externally modeled security domain/namespace, the
> > + * task trust status cannot be determined until the modeling of the
> > + * security event has been completed. The tsem_export_event()
> > + * function sets the trust status TSEM_TASK_TRUST_PENDING and then
> > + * places the task into an interruptible sleep state.
> > + *
> > + * Only two events will cause the task to be removed from sleep state.
> > + * Either the task is killed or a control message is written to the
> > + * TSEM control file that specifies the trust status of the task. See
> > + * the description of the TSEM_CONTROL_TRUSTED and
> > + * TSEM_CONTROL_UNTRUSTED enumeration types.
> > + */
> > +enum tsem_task_trust {
> > + TSEM_TASK_TRUSTED = 1,
> > + TSEM_TASK_UNTRUSTED = 2,
> > + TSEM_TASK_TRUST_PENDING = 4
> What happened to 3?
The enumerations represent bit values used in the 'trust_status'
member of the tsem_task structure.
If a task thinks that it has a trust status value of three it would
indicate the task needs massive doses of chlorpromazine.
> > +};
> > +
> > +/**
> > + * enum tsem_inode_state - Ordinal value for inode reference state.
> > + * @TSEM_INODE_COLLECTING: This ordinal value indicates that the uid/gid
> > + * values should be interpreted against the initial
> > + * user namespace.
> > + * @TSEM_INODE_COLLECTED: This ordinal value indicates that the uid/gid
> > + * values should be interpreted against the user
> > + * namespace that is in effect for the process being
> > + * modeled.
> > + *
> > + * This enumeration type is used to specify the status of the inode
> > + * that is having a digest value computed on the file that it is
> > + * referencing. The purpose of this enumeration is so that the
> > + * recursive call to the TSEM_FILE_OPEN hook, caused by the kernel
> > + * opening the file to compute the checksum, can be bypassed.
> > + *
> > + * The state value of the inode is carried in struct tsem_inode and is
> > + * set and interrogated by the add_file_digest() function. If the
> > + * status of the inode is TSEM_INODE_COLLECTED and the iversion of the
> > + * inode is the same as the collection time, the cached value for
> > + * currently active model digest is returned.
> > +
> > + * If the test for the relevancy of the cached digest value fails the
> > + * status of the inode is set to TSEM_INODE_COLLECTING. The
> > + * tsem_file_open() function will check the inode status when it is
> > + * invoked by the integrity_kernel_read() function and if it is
> > + * set to 'collecting', a successful permissions check is returned so
> > + * that the kernel can open the file and compute its digest.
> > + */
> > +enum tsem_inode_state {
> > + TSEM_INODE_COLLECTING = 1,
> > + TSEM_INODE_COLLECTED
> > +};
> > +
> > +/**
> > + * struct tsem_task - TSEM task control structure.
> > + * @tma_for_ns: The context identity number of the namespace that
> > + * the task has control over if any.
> > + * @trust_status: The enumeration type that specifies the trust state of
> > + * the process.
> > + * @task_id: The hash specific digest that identifies the process.
> > + * @task_key: A hash specific digest value that is used to
> > + * authenticate a task that is running as a trust
> > + * orchestrator to a task that is under the control of the
> > + * orchestrator.
> > + * @context: A pointer to the tsem_context structure that defines the
> > + * modeling context that the task is running under.
> > + *
> > + * This structure is one of the two primary control structures that
> > + * are implemented through the LSM blob functionality. It is
> > + * automatically created when the task control structure is allocated
> > + * for a new task that is being created. It's role is to control the
> > + * status of the task with respect to its security model.
> > + *
> > + * The trust_status member of structure determines whether or not the
> > + * task is in a condition to be trusted. It represents whether or not
> > + * the task has requested execution of a security event that is
> > + * inconsistent with the model that the task is running under.
> > + * Reference the tsem_trust_status enumeration type for more
> > + * information on this member. The trust status value is propagated
> > + * to any child tasks that are spawned from a task.
> > + *
> > + * The digest value that the task_id member contains is generated by
> > + * the tsem_tsem_bprm_creds_for_exec() function that computes the
> > + * task identity based on the COE identity and the CELL identity of
> > + * the executable that is being started. This task_id value is used
> > + * in the computation of the security state point values in
> > + * combination with the COE and CELL identities for this event. The
> > + * task_id digest creates security state points that are specific to
> > + * the executable file that was used to start the task.
> > + *
> > + * The task_key member holds the authentication key that will be used
> > + * to authenticate a process that is requesting the ability to set the
> > + * trust status of a process. This value is generated for the task
> > + * structure of the trust orchestrator when a security modeling
> > + * namespace is created by the orchestrator.
> > + *
> > + * As an additional protection, the creation of a namespace causes the
> > + * context id of the created namespace to be placed in the task that
> > + * will serve as the trust orchestrator for the namespace. This
> > + * context id must match the context id of a process that a trust
> > + * control request is being sent to. Like the authentication key
> > + * this value is not propagated on task allocation so only the task
> > + * that has nominated the security modeling namespace will have
> > + * possession of the necessary credentials to control it.
> > + *
> > + * The context member of the structure contains a pointer to the
> > + * tsem_context structure allocated when a security modeling namespace
> > + * is created by the tsem_ns_create() function. This structure will
> > + * contain all of the information needed to define how the task is to
> > + * have its security behavior modeled.
> > + */
> > +struct tsem_task {
> > + u64 tma_for_ns;
> > + enum tsem_task_trust trust_status;
> > + u8 task_id[HASH_MAX_DIGESTSIZE];
> > + u8 task_key[HASH_MAX_DIGESTSIZE];
> > + struct tsem_context *context;
> > +};
> > +
> > +/**
> > + * struct tsem_context - TSEM modeling context description.
> > + * @kref: Reference count for the context.
> > + * @work: Work structure for delayed release of the context.
> > + * @id: The index number of the context.
> > + * @sealed: Status variable indicating whether or not the
> > + * modeling context can be modified.
> > + * @use_current_ns: Status variable indicating which user namespace
> > + * should be used for resolution of uid/gid values.
> > + * @actions: An array of enum tsem_action_type variables indicating
> > + * the type of response that should be returned in
> > + * response to the modeling of a security event that
> > + * is inconsistent with the model being used for the
> > + * security context.
> > + * @digestname: A pointer to a null-terminated buffer containing the
> > + * name of the digest function that is to be used for
> > + * this security context.
> > + * @zero_digest: The digest value for a 'zero-length' digest value.
> > + * @tfm: A pointer to the digest transformation structure that is to
> > + * be used for this context.
> > + * @magazine_size: The number of struct tsem_event structures that
> > + * are held in reserve for security event hooks that
> > + * are called in atomic context.
> > + * @magazine_lock: The spinlock that protects access to the event
> > + * magazine for the security context.
> > + * @magazine_index: The bitmap that is used to track the magazine slots
> > + * that have been allocated.
> > + * @ws: An array of work structures that are used to refill the magazine
> > + * slots.
> > + * @magazine: An array of pointers to struct tsem_event structures that
> > + * are pre-allocated for security hooks called in atomic
> > + * context.
> > + * @model: If the modeling context is implemented with a kernel based
> > + * trusted model agent this pointer will point to the struct
> > + * tsem_model structure that maintains the state of the
> > + * security model.
> > + * @external: If the modeling context is implemented with an external
> > + * modeling agent this pointer will point to the struct
> > + * tsem_external structure that implements the interface to
> > + * the external trusted modeling agent.
> > +
> > + * This structure is used to represent the state of a TSEM security
> > + * modeling namespace. A pointer to this structure is stored in the
> > + * struct tsem_task structure.
> > + *
> > + * This structure is allocated by the tsem_ns_create() function in
> > + * response to a TSEM control request. This structure maintains all
> > + * of the information that describes the security modeling namespace
> > + * that is not specific to the type of namespace, ie. external or
> > + * internal that is being implemented.
> > +
> > + * The id member is a 64-bit counter that cannot feasibly be
> > + * overflowed and that is incremented for each namespace creation that
> > + * is created. The root modeling namespace has a value of zero so the
> > + * TSEM code uses a pattern of testing this value for non-zero status
> > + * as an indication of whether or not the task is running in a
> > + * subordinate modeling namespace.
> > +
> > + * Each security modeling namespace can have an independent
> > + * cryptographic digest function that is used as the compression
> > + * function for generating the digest values that are used to model
> > + * the security events that occur in a namespace. A single struct tfm
> > + * is allocated for this digest function at the time that the
> > + * tsem_context structure is created and is maintained in this
> > + * structure for subsequent use during event processing.
> > +
> > + * Each cryptographic digest function has a 'zero message' value that
> > + * is the result of the initialization and closure of a hash function
> > + * that has no other input. This zero digest value is computed at the
> > + * time of the creation of the array. This digest value is returned
> > + * for files with zero sizes or that have pseudonyms declared for
> > + * them.
> > + *
> > + * The actions array contains a specification of how each security
> > + * event should be handled in the event that the model detects a
> > + * security event consistent with the model designated for the
> > + * namespace. This array allows the specification of whether the
> > + * model should be enforcing or logging. Currently the specification
> > + * is all or nothing for all of the events, with plans to make the
> > + * actions individually configurable.
> > +
> > + * Each security event that is processed requires a struct tsem_event
> > + * structure that drives either the internal modeling of an event or
> > + * its export to an external modeling agent. Some security event
> > + * hooks are called while a task is running in atomic context. Since
> > + * memory cannot be allocated while a process is in atomic context, a
> > + * magazine of these structures is maintained by this structure for
> > + * security events that run in atomic context. The size of this
> > + * magazine is dynamic and is configurable for each security modeling
> > + * namespace that is created.
> > + *
> > + * When a tsem_event structure is allocated for an atomic event a
> > + * request for the refill of the slot that is vacated is dispatched to
> > + * an asynchronous workqueue. The ws member of this structure points
> > + * to an array of work structures for this refill capability, one for
> > + * each slot in the magazine.
> > + *
> > + * All of this infrastructure is generic for each security modeling
> > + * namespace. How the security modeling is done is governed by the
> > + * model and external members of this structure. These members point
> > + * to data structures that either maintain the security model state
> > + * for an in kernel trusted modeling agent or handle the export of the
> > + * event to an external trusted modeling agent.
> > + *
> > + * Each task that is created in a non-root security modeling namespace
> > + * increments the reference count maintained in the kref member of
> > + * this structure in the tsem_task_alloc() function. The
> > + * tsem_task_free() function decrements this reference count. When
> > + * the reference count expires, ie. when the last task using the
> > + * modeling namespace exits, an asynchronous workqueue request is
> > + * dispatched to dispose of the context. The work member of this
> > + * structure is used to reference that workqueue request.
> > + */
> > +struct tsem_context {
> > + struct kref kref;
> > + struct work_struct work;
> > +
> > + u64 id;
> > + bool sealed;
> > + bool use_current_ns;
> > +
> > + enum tsem_action_type actions[TSEM_EVENT_CNT];
> > +
> > + char *digestname;
> > + u8 zero_digest[HASH_MAX_DIGESTSIZE];
> > + struct crypto_shash *tfm;
> > +
> > + unsigned int magazine_size;
> > + spinlock_t magazine_lock;
> > + unsigned long *magazine_index;
> > + struct tsem_work *ws;
> > + struct tsem_event **magazine;
> > +
> > + struct tsem_model *model;
> > + struct tsem_external *external;
> > +};
> Odd use of whitespace in the structure definition.
It is a carry over from our userspace coding style. As a team we have
found it helpful to visually segregate members of a structure that
have a common and distinct purpose.
FWIW, it would seem that include/linux/sched.h seems to embrace
a similar concept for 'struct task_struct'.
[ ... rest of tsem.h trimmed ... ]
As always,
Dr. Greg
The Quixote Project - Flailing at the Travails of Cybersecurity
On 8/9/2023 7:57 PM, Dr. Greg wrote:
> On Mon, Aug 07, 2023 at 01:39:22PM -0700, Casey Schaufler wrote:
>
> Hi Casey, thank you for the review comments.
>
>> On 7/10/2023 3:23 AM, Dr. Greg wrote:
>>> TSEM is designed, from a functional perspective, to be contained
>>> entirely in its own directory.
>>>
>>> The tsem.h header file defines the enumeration types, structure
>>> definitions and externally visiable functions that are referenced
>>> by all of the compilation units of the TSEM LSM implementation in
>>> that directory.
>> Extensive documentation notwithstanding, it's impossible to review
>> the data structures and constants without the code that goes along
>> with them.
> ...
>
> We may have missed it but I don't see anything in the kernel
> developers guide with respect best practices for introducing blocks of
> entire sub-system code.
>
> Recommendations welcome.
You've broken the code into progressive units. Include the data structure
definitions as required in those progressive units. Include the .h's as
they are needed by the .c's.
>
>>> The structure and enumeration types are extensively documented
>>> and are the recommended starting point for understanding TSEM
>>> implementation and functionality.
>>>
>>> Signed-off-by: Greg Wettstein <[email protected]>
>>> ---
>>> security/tsem/tsem.h | 1516 ++++++++++++++++++++++++++++++++++++++++++
>>> 1 file changed, 1516 insertions(+)
>>> create mode 100644 security/tsem/tsem.h
>>>
>>> diff --git a/security/tsem/tsem.h b/security/tsem/tsem.h
>>> new file mode 100644
>>> index 000000000000..03915f47529b
>>> --- /dev/null
>>> +++ b/security/tsem/tsem.h
>>> @@ -0,0 +1,1516 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-only */
>>> +
>>> +/*
>>> + * Copyright (C) 2023 Enjellic Systems Development, LLC
>>> + * Author: Dr. Greg Wettstein <[email protected]>
>>> + *
>>> + * This is the single include file that documents all of the externally
>>> + * visible types and functions that are used by TSEM. This file is
>>> + * currently organized into four major sections in the following order;
>>> + *
>>> + * includes used by all compilation units
>>> + * CPP definitions
>>> + * enumeration types
>>> + * structure definitions
>>> + * function declarations
>>> + * inline encapsulation functions.
>>> + *
>>> + * Include files that are referenced by more than a single compilation
>>> + * should be included in this file. Includes that are needed to
>>> + * satisfy compilation requirements for only a single file should be
>>> + * included in the file needing that include.
>>> + *
>>> + * Understanding the overall implementation and architecture of TSEM
>>> + * will be facilitated by reviewing the documentation in this file.
>>> + */
>>> +
>>> +#include <uapi/linux/in.h>
>>> +#include <uapi/linux/in6.h>
>>> +#include <linux/wait.h>
>>> +#include <linux/kref.h>
>>> +#include <linux/lsm_hooks.h>
>>> +#include <linux/capability.h>
>>> +#include <crypto/hash.h>
>>> +#include <crypto/hash_info.h>
>>> +#include <net/af_unix.h>
>>> +
>>> +/* The capability needed to manage TSEM. */
>>> +#define TSEM_CONTROL_CAPABILITY CAP_ML
>> Why would you do this? You gave the capability a name that even you
>> don't want to use.
> A simple placeholder that allows the capability used to control
> security modeling to be set in one place, and one place only, until
> the debate over the capability issue is settled.
>
> We never envisioned this define lasting beyond the initial reviews.
Intentional obfuscation is never going to make the review process
easier. I have enough trouble following clear and obvious code. I'm
going to search for CAP_ during a review. You just made that harder.
> ...
>
> +
> +/**
> + * enum tsem_task_trust - Ordinal value describing task trust status.
> + * @TSEM_TASK_TRUSTED: This ordinal value indicates that the task has
> + * not executed a security event that has resulted
> + * in a security behavior not described by the
> + * security model the task is being governed by.
> + * @TSEM_TASK_UNTRUSTED: This ordinal value indicates that the task
> + * has requested the execution of a security event
> + * that resulted in a security behavior not
> + * permitted by the security model the task is
> + * being governed by.
> + * @TSEM_TASK_TRUST_PENDING: This ordinal value indicates that the setting
> + * of the task trust status is pending a response
> + * from an external TMA.
> + *
> + * This enumeration type is used to specify the three different trust
> + * states that a task can be in. The trust status of a task is
> + * regulated by the trust_status member of struct tsem_task. A task
> + * carrying the status of TSEM_TASK_TRUSTED means that it has
> + * not requested the execution of any security events that are
> + * inconsistent with the security model that the task is running in.
> + *
> + * If a task requests execution of a security event that is
> + * inconsistent with the security model it is operating in, and the
> + * domain is running in 'sealed' mode, the task trust status is set to
> + * TSEM_TASK_UNTRUSTED. This value is 'sticky' in that it will be
> + * propagated to any child tasks that are spawned from an untrusted
> + * task.
> + *
> + * In the case of an externally modeled security domain/namespace, the
> + * task trust status cannot be determined until the modeling of the
> + * security event has been completed. The tsem_export_event()
> + * function sets the trust status TSEM_TASK_TRUST_PENDING and then
> + * places the task into an interruptible sleep state.
> + *
> + * Only two events will cause the task to be removed from sleep state.
> + * Either the task is killed or a control message is written to the
> + * TSEM control file that specifies the trust status of the task. See
> + * the description of the TSEM_CONTROL_TRUSTED and
> + * TSEM_CONTROL_UNTRUSTED enumeration types.
> + */
> +enum tsem_task_trust {
> + TSEM_TASK_TRUSTED = 1,
> + TSEM_TASK_UNTRUSTED = 2,
> + TSEM_TASK_TRUST_PENDING = 4
>> What happened to 3?
> The enumerations represent bit values used in the 'trust_status'
> member of the tsem_task structure.
Someone can correct me if I'm wrong, but I think that's a misuse
of enum.
>
> If a task thinks that it has a trust status value of three it would
> indicate the task needs massive doses of chlorpromazine.
If you're not using logical operations (&, |) why do you care if
they're "bit values"?
> ...
>
> + *
> + * Each task that is created in a non-root security modeling namespace
> + * increments the reference count maintained in the kref member of
> + * this structure in the tsem_task_alloc() function. The
> + * tsem_task_free() function decrements this reference count. When
> + * the reference count expires, ie. when the last task using the
> + * modeling namespace exits, an asynchronous workqueue request is
> + * dispatched to dispose of the context. The work member of this
> + * structure is used to reference that workqueue request.
> + */
> +struct tsem_context {
> + struct kref kref;
> + struct work_struct work;
> +
> + u64 id;
> + bool sealed;
> + bool use_current_ns;
> +
> + enum tsem_action_type actions[TSEM_EVENT_CNT];
> +
> + char *digestname;
> + u8 zero_digest[HASH_MAX_DIGESTSIZE];
> + struct crypto_shash *tfm;
> +
> + unsigned int magazine_size;
> + spinlock_t magazine_lock;
> + unsigned long *magazine_index;
> + struct tsem_work *ws;
> + struct tsem_event **magazine;
> +
> + struct tsem_model *model;
> + struct tsem_external *external;
> +};
>> Odd use of whitespace in the structure definition.
> It is a carry over from our userspace coding style. As a team we have
> found it helpful to visually segregate members of a structure that
> have a common and distinct purpose.
>
> FWIW, it would seem that include/linux/sched.h seems to embrace
> a similar concept for 'struct task_struct'.
I'm not formally objecting to it, but there's no obvious reason for it.
>
> [ ... rest of tsem.h trimmed ... ]
>
> As always,
> Dr. Greg
>
> The Quixote Project - Flailing at the Travails of Cybersecurity
On Mon, Aug 07, 2023 at 02:00:12PM -0700, Casey Schaufler wrote:
Thank you for this review.
> On 7/10/2023 3:23 AM, Dr. Greg wrote:
> > The tsem.c file is the 'master' file in the TSEM implementation.
> > It is responsible for initializing the LSM and providing
> > implementions of the security event hooks implemented by TSEM.
> >
> > In addition to initializing the LSM, the set_ready() function
> > implements a secondary initialization that is used to to indicate
> > that security event modeling can begin. This is required
> > secondary to the fact that the cryptographic API's do not become
> > ready until after the 'fs' phase of system initialization is
> > complete.
> >
> > This file also handles the implementation of kernel command-line
> > parameters that are used to configure the root security modeling
> > namespace.
> >
> > The 'tsem_mode' parameter, if set to a value of 1, causes modeling
> > to be not conducted for the root security modeling namespace.
> > One use case is to allow development platform to develop security
> > models without the overhead of full platform modeling.
> >
> > The 'tsem_digest' parameter is used to set the cryptographic hash
> > function that is used to generate security state coefficients in
> > the root model. TSEM can use any cryptographic hash function
> > implemented in the kernel, on a namespace by namespace basis.
> > Subordinate modeling namespaces can select their hash function
> > as part of the namespace creation process but the 'tsem_digest'
> > parameter has to be used to set the function for the root
> > modeling namespace.
> >
> > The hash function used in the root modeling namespace but be
> > compiled into the kernel since the function is used before module
> > loading becomes available. The TSEM kernel configuration selects
> > the SHA256 function to be included as the default cryptographic
> > modeling function.
> >
> > The 'tsem_cache' variable sets the size of the pre-allocated
> > structures that are used for security event modeling in the root
> > modeling namespace. This cache is used to support the modeling
> > and export of events that run in atomic context. The cache size
> > can be set independently for each subordinate security modeling
> > on a namespace by namespace basis.
> >
> > This file also contains the implementation of the tsem_names
> > array that contains the ASCII text names that are assigned to
> > each security event handler. This name is used as one of the
> > characteristics in the security state points that are generated.
> > This array is also used to provide symbolic names for the export
> > of security event descriptions, either through the TSEM control
> > plane or for export to external trust orchestrators.
> >
> > Signed-off-by: Greg Wettstein <[email protected]>
> > ---
> > security/tsem/tsem.c | 1987 ++++++++++++++++++++++++++++++++++++++++++
> Please use kernel doc comments throughout.
At this point we have standardized on using kernel doc comments for
externally visible entities only but we will take a gander at this
file to see if we have missed anything.
> I've made a few minor comments below, but you need to break this up
> somehow for review. Also, having the data definitions elsewhere
> makes review tough.
We will defer to this issue being covered elsewhere.
> > 1 file changed, 1987 insertions(+)
> > create mode 100644 security/tsem/tsem.c
> >
> > diff --git a/security/tsem/tsem.c b/security/tsem/tsem.c
> > new file mode 100644
> > index 000000000000..8ec630354240
> > --- /dev/null
> > +++ b/security/tsem/tsem.c
> > @@ -0,0 +1,1987 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +/*
> > + * Copyright (C) 2023 Enjellic Systems Development, LLC
> > + * Author: Dr. Greg Wettstein <[email protected]>
> > + *
> > + * TSEM initialization infrastructure.
> > + */
> > +#define TRAPPED_MSG_LENGTH 128
> > +
> > +#define LOCKED true
> > +#define NOLOCK false
> > +
> > +#include <linux/magic.h>
> > +#include <linux/mman.h>
> > +#include <linux/binfmts.h>
> > +#include <linux/bpf.h>
> > +#include <linux/ipv6.h>
> > +
> > +#include "tsem.h"
> > +
> > +struct lsm_blob_sizes tsem_blob_sizes __ro_after_init = {
> > + .lbs_task = sizeof(struct tsem_task),
> Do you really want this for the task? It would seem you might want
> to use the cred blob instead.
We would be interested in why you believe the cred blob to be superior.
For those reading along at home the 'struct tsem_task' is as follows:
struct tsem_task {
u64 tma_for_ns;
enum tsem_task_trust trust_status;
u8 task_id[HASH_MAX_DIGESTSIZE];
u8 task_key[HASH_MAX_DIGESTSIZE];
struct tsem_context *context;
};
Credentials seem to imply some type of permission. We look at this
basket of information as relevant to determining how the task will be
modeled and whether or not the task has deviated from its model definition.
> > + .lbs_inode = sizeof(struct tsem_inode)
> > +};
> > +
> > +enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT] = {
> > + TSEM_ACTION_EPERM /* Undefined. */
> > +};
> > +
> > +static struct tsem_model root_model = {
> > + .point_lock = __SPIN_LOCK_INITIALIZER(root_model.point_lock),
> > + .point_list = LIST_HEAD_INIT(root_model.point_list),
> > + .point_end_mutex = __MUTEX_INITIALIZER(root_model.point_end_mutex),
> > +
> > + .trajectory_lock = __SPIN_LOCK_INITIALIZER(root_model.trajectory_lock),
> > + .trajectory_list = LIST_HEAD_INIT(root_model.trajectory_list),
> > + .trajectory_end_mutex = __MUTEX_INITIALIZER(root_model.trajectory_end_mutex),
> > +
> > + .forensics_lock = __SPIN_LOCK_INITIALIZER(root_model.forensics_lock),
> > + .forensics_list = LIST_HEAD_INIT(root_model.forensics_list),
> > + .forensics_end_mutex = __MUTEX_INITIALIZER(root_model.forensics_end_mutex),
> > +
> > + .pseudonym_mutex = __MUTEX_INITIALIZER(root_model.pseudonym_mutex),
> > + .pseudonym_list = LIST_HEAD_INIT(root_model.pseudonym_list)
> > +};
> > +
> > +static struct tsem_context root_context;
> > +
> > +static int tsem_ready __ro_after_init;
> > +
> > +static bool tsem_available __ro_after_init;
> > +
> > +static unsigned int magazine_size __ro_after_init = TSEM_ROOT_MAGAZINE_SIZE;
> > +
> > +static bool no_root_modeling __ro_after_init;
> > +
> > +static char *default_hash_function __ro_after_init;
> > +
> > +static int __init set_magazine_size(char *magazine_value)
> > +{
> > + if (kstrtouint(magazine_value, 0, &magazine_size))
> > + pr_warn("tsem: Failed to parse root cache size.\n");
> > +
> > + if (!magazine_size) {
> > + pr_warn("tsem: Forcing non-zero cache size.\n");
> > + magazine_size = TSEM_ROOT_MAGAZINE_SIZE;
> > + }
> > +
> > + pr_info("tsem: Setting default root cache size to %u.\n",
> > + magazine_size);
> > + return 1;
> > +}
> > +__setup("tsem_cache=", set_magazine_size);
> > +
> > +static int __init set_modeling_mode(char *mode_value)
> > +{
> > + unsigned long mode = 0;
> > +
> > + if (kstrtoul(mode_value, 0, &mode)) {
> > + pr_warn("tsem: Failed to parse modeling mode.\n");
> > + return 1;
> > + }
> > +
> > + if (mode == 1)
> > + no_root_modeling = true;
> > + else
> > + pr_warn("tsem: Unknown mode specified.\n");
> > + return 1;
> > +}
> > +__setup("tsem_mode=", set_modeling_mode);
> > +
> > +static int __init set_default_hash_function(char *hash_function)
> > +{
> > +
> > + default_hash_function = hash_function;
> > + return 1;
> > +}
> > +__setup("tsem_digest=", set_default_hash_function);
> > +
> > +const char * const tsem_names[TSEM_EVENT_CNT] = {
> > + "undefined",
> > + "bprm_set_creds",
> > + "generic_event",
> > + "task_kill",
> > + "task_setpgid",
> > + "task_getpgid",
> > + "task_getsid",
> > + "task_setnice",
> > + "task_setioprio",
> > + "task_getioprio",
> > + "task_prlimit",
> > + "task_setrlimit",
> > + "task_setscheduler",
> > + "task_getscheduler",
> > + "task_prctl",
> > + "file_open",
> > + "mmap_file",
> > + "file_ioctl",
> > + "file_lock",
> > + "file_fcntl",
> > + "file_receive",
> > + "unix_stream_connect",
> > + "unix_may_send",
> > + "socket_create",
> > + "socket_connect",
> > + "socket_bind",
> > + "socket_accept",
> > + "socket_listen",
> > + "socket_socketpair",
> > + "socket_sendmsg",
> > + "socket_recvmsg",
> > + "socket_getsockname",
> > + "socket_getpeername",
> > + "socket_setsockopt",
> > + "socket_shutdown",
> > + "ptrace_traceme",
> > + "kernel_module_request",
> > + "kernel_load_data",
> > + "kernel_read_file",
> > + "sb_mount",
> > + "sb_umount",
> > + "sb_remount",
> > + "sb_pivotroot",
> > + "sb_statfs",
> > + "move_mount",
> > + "shm_associate",
> > + "shm_shmctl",
> > + "shm_shmat",
> > + "sem_associate",
> > + "sem_semctl",
> > + "sem_semop",
> > + "syslog",
> > + "settime",
> > + "quotactl",
> > + "quota_on",
> > + "msg_queue_associate",
> > + "msg_queue_msgctl",
> > + "msg_queue_msgsnd",
> > + "msg_queue_msgrcv",
> > + "ipc_permission",
> > + "key_alloc",
> > + "key_permission",
> > + "netlink_send",
> > + "inode_create",
> > + "inode_link",
> > + "inode_unlink",
> > + "inode_symlink",
> > + "inode_mkdir",
> > + "inode_rmdir",
> > + "inode_mknod",
> > + "inode_rename",
> > + "inode_setattr",
> > + "inode_getattr",
> > + "inode_setxattr",
> > + "inode_getxattr",
> > + "inode_listxattr",
> > + "inode_removexattr",
> > + "inode_killpriv",
> > + "tun_dev_create",
> > + "tun_dev_attach_queue",
> > + "tun_dev_attach",
> > + "tun_dev_open",
> > + "bpf",
> > + "bpf_map",
> > + "bpf_prog"
> > +};
> > +
> > +static const int pseudo_filesystems[] = {
> > + PROC_SUPER_MAGIC,
> > + SYSFS_MAGIC,
> > + DEBUGFS_MAGIC,
> > + TMPFS_MAGIC,
> > + DEVPTS_SUPER_MAGIC,
> > + BINFMTFS_MAGIC,
> > + SECURITYFS_MAGIC,
> > + SELINUX_MAGIC,
> > + SMACK_MAGIC,
> > + CGROUP_SUPER_MAGIC,
> > + CGROUP2_SUPER_MAGIC,
> > + NSFS_MAGIC,
> > + EFIVARFS_MAGIC
> > +};
> > +
> > +static bool bypass_inode(struct inode *inode)
> > +{
> > + bool retn = true;
> > +
> > + unsigned int lp;
> > +
> > + if (!S_ISREG(inode->i_mode))
> > + goto done;
> > +
> > + for (lp = 0; lp < ARRAY_SIZE(pseudo_filesystems); ++lp)
> > + if (inode->i_sb->s_magic == pseudo_filesystems[lp])
> > + goto done;
> > + retn = false;
> > +
> > + done:
> > + return retn;
> > +}
> > +
> > +static int event_action(struct tsem_context *ctx, enum tsem_event_type event)
> > +{
> > + int retn = 0;
> > +
> > + if (tsem_task_trusted(current))
> > + return retn;
> > +
> > + if (ctx->actions[event] == TSEM_ACTION_EPERM)
> > + retn = -EPERM;
> > +
> > + return retn;
> > +}
> > +
> > +static int return_trapped_task(enum tsem_event_type event, char *msg,
> Why isn't this simply "trapped_task()"?
Probably because at the time the code was written we were thinking of
it as a declarative name for what the function did, ie. it returns the
permission value for a task that has been trapped by a security event
handler because the task was considered to be in an untrusted state.
Since it is consistently used as an argument to the return command we
will drop the return_ prefix.
> > + bool locked)
> > +{
> > + int retn;
> > + struct tsem_context *ctx = tsem_context(current);
> > +
> > + pr_warn("Untrusted %s: comm=%s, pid=%d, parameters='%s'\n",
> > + tsem_names[event], current->comm, task_pid_nr(current), msg);
> > +
> > + if (ctx->external) {
> > + retn = tsem_export_action(event, locked);
> > + if (retn)
> > + return retn;
> > + }
> > +
> > + return event_action(ctx, event);
> > +}
> > +
> > +static int return_trapped_inode(enum tsem_event_type event,
> Again, really odd function name.
Same reasoning as above and we will drop the return_ prefix.
> ... and that's all I have time for.
We do appreciate all modicum's of time that people are willing to
extend to this.
Have a good day.
As always,
Dr. Greg
The Quixote Project - Flailing at the Travails of Cybersecurity