diff mbox series

[v3,04/12] Add primary TSEM implementation file.

Message ID 20240401105015.27614-5-greg@enjellic.com (mailing list archive)
State New
Delegated to: Paul Moore
Headers show
Series Implement Trusted Security Event Modeling. | expand

Commit Message

Dr. Greg April 1, 2024, 10:50 a.m. UTC
From: "Dr. Greg" <greg@enjellic.com>

The tsem.c file is the 'master' file in the TSEM implementation.
It is responsible for initializing the LSM and providing
the implementation of the security event handlers.
---
 security/tsem/tsem.c | 2422 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 2422 insertions(+)
 create mode 100644 security/tsem/tsem.c

Comments

Casey Schaufler April 2, 2024, 9:15 p.m. UTC | #1
On 4/1/2024 3:50 AM, Greg Wettstein wrote:
> From: "Dr. Greg" <greg@enjellic.com>
>
> The tsem.c file is the 'master' file in the TSEM implementation.
> It is responsible for initializing the LSM and providing
> the implementation of the security event handlers.
> ---
>  security/tsem/tsem.c | 2422 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 2422 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..876ef1fa8012
> --- /dev/null
> +++ b/security/tsem/tsem.c
> @@ -0,0 +1,2422 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +/*
> + * Copyright (C) 2024 Enjellic Systems Development, LLC
> + * Author: Dr. Greg Wettstein <greg@enjellic.com>
> + *
> + * This file is the primary implementation file for the tsem LSM.
> + *
> + * It implements initialization and setup functions that interpret
> + * kernel command-line arguments and prepares TSEM for operation.
> + *
> + * In addition it contains all of the TSEM specific security event
> + * handlers that are responsible for handling the LSM events that TSEM
> + * models.
> + *
> + * Each TSEM event handler calls the tsem_allocate_event() function to
> + * allocate a structure that will be used to describe the event.  The
> + * CELL union of this structure contains various structures that are
> + * used to hold these parameters.
> + *
> + * Since the event characterization parameters need to be retained for
> + * the lifetime of the tsem_event structure that is allocated.  In the
> + * case of internally modeled namespaces this lifespan is the lifetime
> + * of the security modeling namespace.  In the case of externally
> + * modeled namespaces, the lifespan is until the security event
> + * description is exported to an external trust orchestrator.
> + *
> + * In order to support this model, the event description structures
> + * are typically composed of a union over 'in' and 'out' structures.
> + * The 'in' structures are used to hold arguments to the event handler
> + * that may only be relevant for the duration of the call.  These
> + * values are translated into members of the 'out' structure that
> + * retain the values until the end of the lifetime of the tsem_event
> + * structure.
> + *
> + * Each TSEM event handler is responsible for allocating a tsem_event
> + * structure and populating the appropriate CELL structure with the
> + * input characteristics of the event.  The dispatch_event() function
> + * is called to handle the modeling of the event.  This function
> + * returns the permission value that is returned as the result of the
> + * LSM event handler.
> + *
> + * The dispatch_event() calls the tsem_event_init() function that is
> + * responsible for translating the input parameters into values that
> + * will be retained for the lifetime of the security event
> + * description.  The populated event description is then dispatched to
> + * either the tsem_model_event() or the tsem_export_event() for
> + * modeling by either the internal TMA or by a TMA associated with an
> + * external trust orchestrator.

I'm not in favor of this level of indirection. Funneling all the LSM hooks
into a dispatcher that fans out to a 1 to 1 (or near enough it doesn't make
a difference) set of event handlers is just wasteful of resources.

> + */
> +
> +#define LOCKED true
> +#define NOLOCK false

As you have these constants defined the first would indicate that
a lock is set, whereas the second indicates there is not a lock.
You should either pair LOCKED with UNLOCKED or LOCK with NOLOCK.
Or better yet, name your variables such that "true" and "false"
are reasonable descriptions of their states.

> +
> +#include <linux/magic.h>
> +#include <linux/mman.h>
> +#include <linux/binfmts.h>
> +#include <linux/bpf.h>
> +#include <linux/mount.h>
> +#include <linux/security.h>
> +
> +#include "tsem.h"
> +
> +static const struct lsm_id tsem_lsmid = {
> +	.name = "tsem",
> +	.id = LSM_ID_TSEM,
> +};
> +
> +struct lsm_blob_sizes tsem_blob_sizes __ro_after_init = {
> +	.lbs_task = sizeof(struct tsem_task),
> +	.lbs_inode = sizeof(struct tsem_inode),
> +	.lbs_ipc = sizeof(struct tsem_ipc),
> +	.lbs_xattr_count = 1
> +};
> +
> +enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT] = {
> +	TSEM_ACTION_EPERM	/* Undefined. */
> +};
> +
> +static atomic64_t task_instance;
> +
> +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),
> +
> +	.mount_mutex = __MUTEX_INITIALIZER(root_model.mount_mutex),
> +	.mount_list = LIST_HEAD_INIT(root_model.mount_list)
> +};
> +
> +static struct tsem_context root_context;
> +
> +DEFINE_STATIC_KEY_TRUE(tsem_not_ready);
> +
> +static bool tsem_available __ro_after_init;
> +
> +static unsigned int magazine_size __ro_after_init = TSEM_ROOT_MAGAZINE_SIZE;
> +
> +static enum mode_type {
> +	FULL_MODELING,
> +	NO_ROOT_MODELING,
> +	EXPORT_ONLY
> +} tsem_mode __ro_after_init;
> +
> +static char *default_hash_function __ro_after_init;
> +
> +const char * const tsem_names[TSEM_EVENT_CNT] = {
> +	"undefined",
> +	"bprm_committed_creds",
> +	"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",
> +	"ptrace_access_check",
> +	"capable",
> +	"capget",
> +	"capset"
> +};

You can't seriously be counting on this being a mapping to tsem event numbers,
with the event numbers being defined elsewhere. You'll never be able to keep them
in sync. You probably want a structure here, with entries like

	{ "hook_name",	TSEM_EVENT_HOOK_NAME },


> +
> +static const unsigned long 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 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;

Why isn't this a void function?

> +}
> +__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)
> +		tsem_mode = NO_ROOT_MODELING;
> +	else if (mode == 2)
> +		tsem_mode = EXPORT_ONLY;
> +	else
> +		pr_warn("tsem: Unknown mode specified.\n");

If you're ever going to have additional "modes" you probably want
a switch() rather than cascading if()s.
You seem generally aggressive with defining meaningful constants.
It seems out of character that you're using integer mode values instead
of TSEM_MODE_CLOCKWISE and TSEM_MODE_WIDDERSHINS, or whatever the modes
are.

> +	return 1;

Repeating myself the once. If a function can only ever return
one value it should be a void function.

> +}
> +__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);
> +
> +static bool bypass_event(void)
> +{
> +	if (tsem_mode == NO_ROOT_MODELING && !tsem_context(current)->id)
> +		return true;
> +	return false;
> +}
> +
> +static bool pseudo_filesystem(struct inode *inode)
> +{
> +	unsigned int lp;
> +
> +	for (lp = 0; lp < ARRAY_SIZE(pseudo_filesystems); ++lp)
> +		if (inode->i_sb->s_magic == pseudo_filesystems[lp])
> +			return true;
> +	return false;
> +}
> +
> +static int untrusted_task(struct tsem_event *ep)
> +{
> +	int retn = 0;
> +	struct tsem_context *ctx = tsem_context(current);
> +
> +	if (ctx->external) {
> +		retn = tsem_export_action(ep->event, ep->locked);
> +		if (retn)
> +			return retn;
> +	} else
> +		pr_warn("Untrusted event %s: model_ns=%lld, comm=%s, pid=%d\n",
> +			tsem_names[ep->event], ctx->id, current->comm,
> +			task_pid_nr(current));
> +
> +	if (ctx->actions[ep->event] == TSEM_ACTION_EPERM)
> +		retn = -EPERM;
> +	return retn;
> +}
> +
> +static int dispatch_event(struct tsem_event *ep)
> +{
> +	int retn = 0;
> +	struct tsem_context *ctx = tsem_context(current);
> +
> +	retn = tsem_event_init(ep);
> +	if (retn)
> +		return retn;
> +
> +	if (unlikely(tsem_task_untrusted(current)))
> +		return untrusted_task(ep);
> +
> +	if (!ctx->external)
> +		retn = tsem_model_event(ep);
> +	else
> +		retn = tsem_export_event(ep);
> +
> +	tsem_event_put(ep);
> +	return retn;
> +}
> +
> +static int tsem_file_open(struct file *file)
> +{
> +	struct inode *inode = file_inode(file);
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +	if (unlikely(tsem_inode(inode)->status == TSEM_INODE_CONTROL_PLANE)) {
> +		if (capable(CAP_MAC_ADMIN))
> +			return 0;
> +		else
> +			return -EPERM;
> +	}
> +
> +	if (!S_ISREG(inode->i_mode))
> +		return 0;
> +	if (tsem_inode(inode)->status == TSEM_INODE_COLLECTING)
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_FILE_OPEN, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.file.in.file = file;
> +	ep->CELL.file.in.pseudo_file = pseudo_filesystem(inode);
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_mmap_file(struct file *file, unsigned long prot,
> +			  unsigned long flags, unsigned long extra)
> +{
> +	struct inode *inode = NULL;
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	if (!file && !(prot & PROT_EXEC))
> +		return 0;
> +	if (file) {
> +		inode = file_inode(file);
> +		if (!S_ISREG(inode->i_mode))
> +			return 0;
> +		if (pseudo_filesystem(inode))
> +			return 0;
> +	}
> +
> +	ep = tsem_event_allocate(TSEM_MMAP_FILE, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.mmap_file.anonymous = file == NULL ? 1 : 0;
> +	ep->CELL.mmap_file.file.in.file = file;
> +	ep->CELL.mmap_file.prot = prot;
> +	ep->CELL.mmap_file.flags = flags;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_file_ioctl(struct file *file, unsigned int cmd,
> +			   unsigned long arg)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_FILE_IOCTL, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.file.cmd = cmd;
> +	ep->CELL.file.in.file = file;
> +	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_file_lock(struct file *file, unsigned int cmd)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_FILE_LOCK, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.file.cmd = cmd;
> +	ep->CELL.file.in.file = file;
> +	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_file_fcntl(struct file *file, unsigned int cmd,
> +			   unsigned long arg)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_FILE_FCNTL, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.file.cmd = cmd;
> +	ep->CELL.file.in.file = file;
> +	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_file_receive(struct file *file)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_FILE_RECEIVE, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.file.in.file = file;
> +	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
> +
> +	return dispatch_event(ep);
> +}
> +
> +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->instance = old_task->instance;
> +	new_task->p_instance = old_task->instance;
> +
> +	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);
> +	memcpy(new_task->p_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);

	if (ctx->id)
		tsem_ne_put(ctx);

One less line of code.

> +}
> +
> +static int tsem_task_kill(struct task_struct *target,
> +			  struct kernel_siginfo *info, int sig,
> +			  const struct cred *cred)
> +{
> +	bool cross_model;
> +	struct tsem_event *ep;
> +	struct tsem_context *src_ctx = tsem_context(current);
> +	struct tsem_context *tgt_ctx = tsem_context(target);
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	cross_model = src_ctx->id != tgt_ctx->id;
> +
> +	if (info != SEND_SIG_NOINFO && SI_FROMKERNEL(info))
> +		return 0;
> +	if (sig == SIGURG)
> +		return 0;
> +	if (!capable(CAP_MAC_ADMIN) &&
> +	    has_capability_noaudit(target, CAP_MAC_ADMIN))
> +		return -EPERM;
> +	if (!capable(CAP_MAC_ADMIN) && cross_model)
> +		return -EPERM;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_KILL, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.task_kill.signal = sig;
> +	ep->CELL.task_kill.cross_model = cross_model;
> +	memcpy(ep->CELL.task_kill.target, tsem_task(target)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_ptrace_access_check(struct task_struct *child,
> +				    unsigned int mode)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_PTRACE_ACCESS_CHECK, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.task_kill.u.resource = mode;
> +	memcpy(ep->CELL.task_kill.target, tsem_task(child)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_ptrace_traceme(struct task_struct *parent)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_PTRACE_TRACEME, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	memcpy(ep->CELL.task_kill.source, tsem_task(parent)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_capget(const struct task_struct *target,
> +		       kernel_cap_t *effective, kernel_cap_t *inheritable,
> +		       kernel_cap_t *permitted)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_CAPGET, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.capability.effective = *effective;
> +	ep->CELL.capability.inheritable = *inheritable;
> +	ep->CELL.capability.permitted = *permitted;
> +	memcpy(ep->CELL.capability.target, tsem_task(target)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_capset(struct cred *new, const struct cred *old,
> +		       const kernel_cap_t *effective,
> +		       const kernel_cap_t *inheritable,
> +		       const kernel_cap_t *permitted)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_CAPSET, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.capability.effective = *effective;
> +	ep->CELL.capability.inheritable = *inheritable;
> +	ep->CELL.capability.permitted = *permitted;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_capable(const struct cred *cred, struct user_namespace *ns,
> +			int cap, unsigned int opts)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_CAPABLE, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.capability.cap = cap;
> +	ep->CELL.capability.opts = opts;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_setpgid(struct task_struct *p, pid_t pgid)
> +{
> +	struct tsem_event *ep;
> +	struct task_struct *src;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_SETPGID, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
> +	       tsem_digestsize());
> +
> +	if (!pgid)
> +		memcpy(ep->CELL.task_kill.source, tsem_task(p)->task_id,
> +		       tsem_digestsize());
> +	else {
> +		rcu_read_lock();
> +		src = find_task_by_vpid(pgid);
> +		rcu_read_unlock();
> +		if (src)
> +			memcpy(ep->CELL.task_kill.source,
> +			       tsem_task(src)->task_id, tsem_digestsize());
> +	}
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_getpgid(struct task_struct *p)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_GETPGID, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_getsid(struct task_struct *p)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_GETSID, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_setnice(struct task_struct *p, int nice)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_SETNICE, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.task_kill.u.value = nice;
> +	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_setioprio(struct task_struct *p, int ioprio)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_SETIOPRIO, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.task_kill.u.value = ioprio;
> +	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_getioprio(struct task_struct *p)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_GETIOPRIO, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_prlimit(const struct cred *cred, const struct cred *tcred,
> +			     unsigned int flags)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_PRLIMIT, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.task_prlimit.flags = flags;
> +	ep->CELL.task_prlimit.in.cred = cred;
> +	ep->CELL.task_prlimit.in.tcred = tcred;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_setrlimit(struct task_struct *p, unsigned int resource,
> +			       struct rlimit *new_rlim)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_SETRLIMIT, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.task_kill.u.resource = resource;
> +	ep->CELL.task_kill.cur = new_rlim->rlim_cur;
> +	ep->CELL.task_kill.max = new_rlim->rlim_max;
> +	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_setscheduler(struct task_struct *p)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_SETSCHEDULER, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_getscheduler(struct task_struct *p)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_GETSCHEDULER, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
> +	       tsem_digestsize());
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_task_prctl(int option, unsigned long arg2, unsigned long arg3,
> +			   unsigned long arg4, unsigned long arg5)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TASK_PRCTL, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.task_prctl.option = option;
> +	ep->CELL.task_prctl.arg2 = arg2;
> +	ep->CELL.task_prctl.arg3 = arg3;
> +	ep->CELL.task_prctl.arg4 = arg4;
> +	ep->CELL.task_prctl.arg5 = arg5;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static void tsem_bprm_committed_creds(const struct linux_binprm *bprm)
> +{
> +	u8 task_id[HASH_MAX_DIGESTSIZE];
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return;
> +
> +	if (tsem_map_task(bprm->file, task_id))
> +		memset(task_id, 0xff, sizeof(task_id));
> +
> +	tsem_task(current)->instance = atomic64_inc_return(&task_instance);
> +	memcpy(tsem_task(current)->task_id, task_id, tsem_digestsize());
> +}
> +
> +static int tsem_inode_alloc_security(struct inode *inode)
> +{
> +	struct tsem_inode *tsip = tsem_inode(inode);
> +
> +	mutex_init(&tsip->digest_mutex);
> +	INIT_LIST_HEAD(&tsip->digest_list);
> +
> +	mutex_init(&tsip->create_mutex);
> +	INIT_LIST_HEAD(&tsip->create_list);
> +
> +	mutex_init(&tsip->instance_mutex);
> +	INIT_LIST_HEAD(&tsip->instance_list);
> +
> +	return 0;
> +}
> +
> +static int tsem_inode_init_security(struct inode *inode, struct inode *dir,
> +				    const struct qstr *qstr,
> +				    struct xattr *xattrs, int *xattr_count)
> +{
> +	u8 *owner = tsem_task(current)->task_id;
> +	struct tsem_inode *tsip = tsem_inode(inode);
> +	struct tsem_inode_instance *entry, *retn = NULL;
> +
> +	mutex_lock(&tsem_inode(dir)->create_mutex);
> +	list_for_each_entry(entry, &tsem_inode(dir)->create_list, list) {
> +		if (!memcmp(entry->owner, owner, tsem_digestsize()) &&
> +		    !strcmp(qstr->name, entry->pathname)) {
> +			retn = entry;
> +			break;
> +		}
> +	}
> +
> +	if (retn) {
> +		tsip->created = true;
> +		tsip->creator = retn->creator;
> +		tsip->instance = retn->instance;
> +		memcpy(tsip->owner, retn->owner, tsem_digestsize());
> +		list_del(&retn->list);
> +	}
> +	mutex_unlock(&tsem_inode(dir)->create_mutex);
> +
> +	if (!retn && S_ISREG(inode->i_mode))
> +		WARN_ONCE(true, "Cannot find inode ownership information.");
> +
> +	return -EOPNOTSUPP;
> +}
> +
> +static void _release_inode_instances(u64 id, struct tsem_inode *tsip)
> +{
> +	struct tsem_inode_instance *owner, *tmp_owner;
> +
> +	mutex_lock(&tsip->instance_mutex);
> +	list_for_each_entry_safe(owner, tmp_owner, &tsip->instance_list,
> +				 list) {
> +		if (id == owner->creator) {
> +			list_del(&owner->list);
> +			kfree(owner);
> +		}
> +	}
> +	mutex_unlock(&tsip->instance_mutex);
> +}
> +
> +static void tsem_inode_free_security(struct inode *inode)
> +{
> +	struct tsem_inode_instance *owner, *tmp_owner;
> +	struct tsem_inode_digest *digest, *tmp_digest;
> +	struct tsem_inode_entry *entry, *tmp_entry;
> +	struct tsem_context *ctx = tsem_context(current);
> +
> +	mutex_lock(&ctx->inode_mutex);
> +	list_for_each_entry_safe(entry, tmp_entry, &ctx->inode_list, list) {
> +		if (entry->tsip == tsem_inode(inode)) {
> +			list_del(&entry->list);
> +			_release_inode_instances(ctx->id, entry->tsip);
> +			kfree(entry);
> +		}
> +	}
> +	mutex_unlock(&ctx->inode_mutex);
> +
> +	list_for_each_entry_safe(digest, tmp_digest,
> +				 &tsem_inode(inode)->digest_list, list) {
> +		list_del(&digest->list);
> +		kfree(digest->name);
> +		kfree(digest);
> +	}
> +
> +	list_for_each_entry_safe(owner, tmp_owner,
> +				 &tsem_inode(inode)->create_list, list) {
> +		list_del(&owner->list);
> +		kfree(owner);
> +	}
> +
> +	list_for_each_entry_safe(owner, tmp_owner,
> +				 &tsem_inode(inode)->instance_list, list) {
> +		list_del(&owner->list);
> +		kfree(owner);
> +	}
> +}
> +
> +static int tsem_unix_stream_connect(struct sock *sock, struct sock *other,
> +				    struct sock *newsk)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_UNIX_STREAM_CONNECT, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = sock;
> +	ep->CELL.socket.in.sockb = other;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_unix_may_send(struct socket *sock, struct socket *other)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_UNIX_MAY_SEND, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = sock->sk;
> +	ep->CELL.socket.in.sockb = other->sk;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_post_create(struct socket *sock, int family, int type,
> +				   int protocol, int kern)
> +{
> +	struct tsem_inode *tsip = tsem_inode(SOCK_INODE(sock));
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +
> +	memcpy(tsip->owner, tsem_task(current)->task_id, tsem_digestsize());
> +	return 0;
> +}
> +
> +static int tsem_socket_create(int family, int type, int protocol, int kern)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_CREATE, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.out.socka.family = family;
> +	ep->CELL.socket.out.socka.type = type;
> +	ep->CELL.socket.out.socka.protocol = protocol;
> +	ep->CELL.socket.out.socka.kern = kern;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_connect(struct socket *sock, struct sockaddr *addr,
> +			     int addr_len)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_CONNECT, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = sock->sk;
> +	ep->CELL.socket.in.addr = addr;
> +	ep->CELL.socket.value = addr_len;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_bind(struct socket *sock, struct sockaddr *addr,
> +			    int addr_len)
> +
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_BIND, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = sock->sk;
> +	ep->CELL.socket.in.addr = addr;
> +	ep->CELL.socket.value = addr_len;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_accept(struct socket *sock, struct socket *newsock)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_ACCEPT, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = sock->sk;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_listen(struct socket *sock, int backlog)
> +
> +{
> +	struct sock *sk = sock->sk;
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_LISTEN, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.value = backlog;
> +	ep->CELL.socket.in.socka = sk;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_socketpair(struct socket *socka, struct socket *sockb)
> +{
> +	struct sock *ska = socka->sk, *skb = sockb->sk;
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_SOCKETPAIR, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = ska;
> +	ep->CELL.socket.in.sockb = skb;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_sendmsg(struct socket *sock, struct msghdr *msgmsg,
> +			       int size)
> +{
> +	struct sock *sk = sock->sk;
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_SENDMSG, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = sk;
> +	ep->CELL.socket.in.addr = msgmsg->msg_name;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_recvmsg(struct socket *sock, struct msghdr *msgmsg,
> +			       int size, int flags)
> +{
> +	struct sock *sk = sock->sk;
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_RECVMSG, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = sk;
> +	if (msgmsg->msg_name && msgmsg->msg_namelen > 0)
> +		ep->CELL.socket.in.addr = msgmsg->msg_name;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_getsockname(struct socket *sock)
> +{
> +	struct sock *sk = sock->sk;
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_GETSOCKNAME, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = sk;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_getpeername(struct socket *sock)
> +{
> +	struct sock *sk = sock->sk;
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_GETPEERNAME, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = sk;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_setsockopt(struct socket *sock, int level, int optname)
> +{
> +	struct sock *sk = sock->sk;
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_SETSOCKOPT, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.value = level;
> +	ep->CELL.socket.optname = optname;
> +	ep->CELL.socket.in.socka = sk;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_socket_shutdown(struct socket *sock, int how)
> +{
> +	struct sock *sk = sock->sk;
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SOCKET_SHUTDOWN, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.value = how;
> +	ep->CELL.socket.in.socka = sk;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_kernel_module_request(char *kmod_name)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_KERNEL_MODULE_REQUEST, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.kernel.in.kmod_name = kmod_name;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_kernel_load_data(enum kernel_load_data_id id, bool contents)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_KERNEL_LOAD_DATA, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.kernel.id = id;
> +	ep->CELL.kernel.contents = contents;
> +
> +	return dispatch_event(ep);
> +}
> +
> +
> +static int tsem_kernel_read_file(struct file *file,
> +				 enum kernel_read_file_id id, bool contents)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_KERNEL_READ_FILE, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.kernel.id = id;
> +	ep->CELL.kernel.contents = contents;
> +	ep->CELL.kernel.in.file = file;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_sb_mount(const char *dev_name, const struct path *path,
> +			 const char *type, unsigned long flags, void *data)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SB_MOUNT, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.sb.flags = flags;
> +	ep->CELL.sb.in.dev_name = dev_name;
> +	ep->CELL.sb.in.path = path;
> +	ep->CELL.sb.in.type = type;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static	int tsem_sb_umount(struct vfsmount *mnt, int flags)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SB_UMOUNT, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.sb.flags = flags;
> +	ep->CELL.sb.in.dentry = mnt->mnt_root;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_sb_remount(struct super_block *sb, void *mnt_opts)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SB_REMOUNT, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.sb.in.sb = sb;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_sb_pivotroot(const struct path *old_path,
> +			     const struct path *new_path)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SB_PIVOTROOT, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.sb.in.path = old_path;
> +	ep->CELL.sb.in.path2 = new_path;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_sb_statfs(struct dentry *dentry)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SB_STATFS, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.sb.in.dentry = dentry;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_move_mount(const struct path *from_path,
> +			   const struct path *to_path)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_MOVE_MOUNT, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.sb.in.path = from_path;
> +	ep->CELL.sb.in.path2 = to_path;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_shm_associate(struct kern_ipc_perm *perm, int shmflg)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SHM_ASSOCIATE, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.in.perm = perm;
> +	ep->CELL.ipc.value = shmflg;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_shm_shmctl(struct kern_ipc_perm *perm, int cmd)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SHM_SHMCTL, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.in.perm = perm;
> +	ep->CELL.ipc.value = cmd;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_shm_shmat(struct kern_ipc_perm *perm, char __user *shmaddr,
> +			  int shmflg)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SHM_SHMAT, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.in.perm = perm;
> +	ep->CELL.ipc.value = shmflg;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_sem_associate(struct kern_ipc_perm *perm, int semflg)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SEM_ASSOCIATE, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.in.perm = perm;
> +	ep->CELL.ipc.value = semflg;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_sem_semctl(struct kern_ipc_perm *perm, int cmd)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SEM_SEMCTL, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.in.perm = perm;
> +	ep->CELL.ipc.value = cmd;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_sem_semop(struct kern_ipc_perm *perm, struct sembuf *sops,
> +			  unsigned int nsops, int alter)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SEM_SEMOP, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.in.perm = perm;
> +	ep->CELL.ipc.nsops = nsops;
> +	ep->CELL.ipc.value = alter;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_syslog(int type)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SYSLOG, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.value = type;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_settime(const struct timespec64 *ts, const struct timezone *tz)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_SETTIME, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	if (ts) {
> +		ep->CELL.time.have_ts = true;
> +		ep->CELL.time.seconds = ts->tv_sec;
> +		ep->CELL.time.nsecs = ts->tv_nsec;
> +	}
> +	if (tz) {
> +		ep->CELL.time.have_tz = true;
> +		ep->CELL.time.minuteswest = tz->tz_minuteswest;
> +		ep->CELL.time.dsttime = tz->tz_dsttime;
> +	}
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_quotactl(int cmds, int type, int id,
> +			 const struct super_block *sb)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_QUOTACTL, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.quota.cmds = cmds;
> +	ep->CELL.quota.type = type;
> +	ep->CELL.quota.id = id;
> +	ep->CELL.quota.in.sb = sb;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_quota_on(struct dentry *dentry)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_QUOTA_ON, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.quota.in.dentry = dentry;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_msg_queue_associate(struct kern_ipc_perm *perm, int msqflg)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_MSG_QUEUE_ASSOCIATE, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.in.perm = perm;
> +	ep->CELL.ipc.value = msqflg;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_msg_queue_msgsnd(struct kern_ipc_perm *perm,
> +				 struct msg_msg *msgmsg, int msqflg)
> +
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_MSG_QUEUE_MSGSND, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.in.perm = perm;
> +	ep->CELL.ipc.value = msqflg;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_msg_queue_msgctl(struct kern_ipc_perm *perm, int cmd)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_MSG_QUEUE_MSGCTL, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.in.perm = perm;
> +	ep->CELL.ipc.value = cmd;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_msg_queue_msgrcv(struct kern_ipc_perm *perm,
> +				 struct msg_msg *msgmsg,
> +				 struct task_struct *target, long type,
> +				 int mode)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_MSG_QUEUE_MSGRCV, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.in.perm = perm;
> +	ep->CELL.ipc.in.target = target;
> +	ep->CELL.ipc.type = type;
> +	ep->CELL.ipc.value = mode;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_ipc_alloc(struct kern_ipc_perm *kipc)
> +{
> +	struct tsem_ipc *tipc = tsem_ipc(kipc);
> +
> +	memcpy(tipc->owner, tsem_task(current)->task_id, tsem_digestsize());
> +	return 0;
> +}
> +
> +static int tsem_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_IPC_PERMISSION, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.ipc.perm_flag = flag;
> +	ep->CELL.ipc.in.perm = ipcp;
> +
> +	return dispatch_event(ep);
> +}
> +
> +#ifdef CONFIG_KEYS
> +static int tsem_key_alloc(struct key *key, const struct cred *cred,
> +			  unsigned long flags)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_KEY_ALLOC, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.key.flags = flags;
> +	ep->CELL.key.in.cred = cred;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_key_permission(key_ref_t key_ref, const struct cred *cred,
> +			       unsigned int perm)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_KEY_PERMISSION, LOCKED);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.key.flags = perm;
> +	ep->CELL.key.in.cred = cred;
> +	ep->CELL.key.in.ref = key_ref;
> +
> +	return dispatch_event(ep);
> +}
> +#endif
> +
> +static int tsem_netlink_send(struct sock *sk, struct sk_buff *skb)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_NETLINK_SEND, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.netlink.in.sock = sk;
> +	ep->CELL.netlink.in.parms = (struct netlink_skb_parms *) skb->cb;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_create(struct inode *dir, struct dentry *dentry,
> +			     umode_t mode)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_CREATE, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode.in.dir = dir;
> +	ep->CELL.inode.in.dentry = dentry;
> +	ep->CELL.inode.mode = mode;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_link(struct dentry *old_dentry, struct inode *dir,
> +			   struct dentry *new_dentry)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_LINK, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode.in.dir = dir;
> +	ep->CELL.inode.in.dentry = old_dentry;
> +	ep->CELL.inode.in.new_dentry = new_dentry;
> +	ep->CELL.inode.mode = 0;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_unlink(struct inode *dir, struct dentry *dentry)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_UNLINK, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode.in.dir = dir;
> +	ep->CELL.inode.in.dentry = dentry;
> +	ep->CELL.inode.mode = 0;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_symlink(struct inode *dir, struct dentry *dentry,
> +			      const char *old_name)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_SYMLINK, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode.in.dir = dir;
> +	ep->CELL.inode.in.dentry = dentry;
> +	ep->CELL.inode.in.old_name = old_name;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_mkdir(struct inode *dir, struct dentry *dentry,
> +			    umode_t mode)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_MKDIR, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode.in.dir = dir;
> +	ep->CELL.inode.in.dentry = dentry;
> +	ep->CELL.inode.mode = mode;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_rmdir(struct inode *dir, struct dentry *dentry)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_RMDIR, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode.in.dir = dir;
> +	ep->CELL.inode.in.dentry = dentry;
> +	ep->CELL.inode.mode = 0;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
> +			     struct inode *new_dir, struct dentry *new_dentry)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_RENAME, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode.in.dir = old_dir;
> +	ep->CELL.inode.in.new_dir = new_dir;
> +	ep->CELL.inode.in.dentry = old_dentry;
> +	ep->CELL.inode.in.new_dentry = new_dentry;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_mknod(struct inode *dir, struct dentry *dentry,
> +			    umode_t mode, dev_t dev)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_MKNOD, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode.in.dir = dir;
> +	ep->CELL.inode.in.dentry = dentry;
> +	ep->CELL.inode.mode = mode;
> +	ep->CELL.inode.dev = dev;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_setattr(struct mnt_idmap *idmap,
> +			      struct dentry *dentry, struct iattr *attr)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_SETATTR, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode_attr.in.dentry = dentry;
> +	ep->CELL.inode_attr.in.iattr = attr;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_getattr(const struct path *path)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_GETATTR, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode_attr.in.path = path;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_setxattr(struct mnt_idmap *idmap,
> +			       struct dentry *dentry, const char *name,
> +			       const void *value, size_t size, int flags)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_SETXATTR, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode_xattr.in.dentry = dentry;
> +	ep->CELL.inode_xattr.in.name = name;
> +	ep->CELL.inode_xattr.in.value = value;
> +	ep->CELL.inode_xattr.in.size = size;
> +	ep->CELL.inode_xattr.in.flags = flags;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_getxattr(struct dentry *dentry, const char *name)
> +{
> +	struct tsem_event *ep = NULL;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_GETXATTR, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode_xattr.in.dentry = dentry;
> +	ep->CELL.inode_xattr.in.name = name;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_listxattr(struct dentry *dentry)
> +{
> +	struct tsem_event *ep;
> +
> +	if (static_branch_unlikely(&tsem_not_ready))
> +		return 0;
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_LISTXATTR, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode_xattr.in.dentry = dentry;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_removexattr(struct mnt_idmap *idmap,
> +				  struct dentry *dentry, const char *name)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_REMOVEXATTR, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode_xattr.in.dentry = dentry;
> +	ep->CELL.inode_xattr.in.name = name;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_INODE_KILLPRIV, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.inode.in.dentry = dentry;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_tun_dev_create(void)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TUN_DEV_CREATE, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +	ep->no_params = true;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_tun_dev_attach_queue(void *security)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TUN_DEV_ATTACH_QUEUE, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +	ep->no_params = true;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_tun_dev_attach(struct sock *sk, void *security)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TUN_DEV_ATTACH, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.socket.in.socka = sk;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_tun_dev_open(void *security)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_TUN_DEV_OPEN, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +	ep->no_params = true;
> +
> +	return dispatch_event(ep);
> +}
> +
> +#ifdef CONFIG_BPF_SYSCALL
> +static int tsem_bpf(int cmd, union bpf_attr *attr, unsigned int size)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_BPF, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.bpf.bpf.cmd = cmd;
> +	ep->CELL.bpf.bpf.size = size;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_bpf_map(struct bpf_map *map, fmode_t fmode)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_BPF_MAP, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.bpf.map.fmode = fmode;
> +	ep->CELL.bpf.map.map_type = map->map_type;
> +
> +	return dispatch_event(ep);
> +}
> +
> +static int tsem_bpf_prog(struct bpf_prog *prog)
> +{
> +	struct tsem_event *ep;
> +
> +	if (bypass_event())
> +		return 0;
> +
> +	ep = tsem_event_allocate(TSEM_BPF_PROG, NOLOCK);
> +	if (!ep)
> +		return -ENOMEM;
> +
> +	ep->CELL.bpf.prog.type = prog->type;
> +	ep->CELL.bpf.prog.attach_type = prog->expected_attach_type;
> +
> +	return dispatch_event(ep);
> +}
> +#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_access_check, tsem_ptrace_access_check),
> +	LSM_HOOK_INIT(ptrace_traceme, tsem_ptrace_traceme),
> +
> +	LSM_HOOK_INIT(capget, tsem_capget),
> +	LSM_HOOK_INIT(capset, tsem_capset),
> +	LSM_HOOK_INIT(capable, tsem_capable),
> +
> +	LSM_HOOK_INIT(bprm_committed_creds, tsem_bprm_committed_creds),
> +
> +	LSM_HOOK_INIT(inode_alloc_security, tsem_inode_alloc_security),
> +	LSM_HOOK_INIT(inode_init_security, tsem_inode_init_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_post_create, tsem_socket_post_create),
> +	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_alloc_security, tsem_ipc_alloc),
> +	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_alloc_security, tsem_ipc_alloc),
> +	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_alloc_security, tsem_ipc_alloc),
> +	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];
> +	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,
> +	       crypto_shash_digestsize(tfm));
> +
> + 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;
> +
> +	if (tsem_mode == EXPORT_ONLY) {
> +		retn = tsem_ns_export_root(magazine_size);
> +		if (retn)
> +			goto done;
> +	}
> +
> +	pr_info("tsem: Now active.\n");
> +	static_branch_disable(&tsem_not_ready);
> +
> + 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;
> +	char *msg;
> +	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_lsmid);
> +
> +	tsk->context = ctx;
> +	kref_init(&ctx->kref);
> +	kref_get(&ctx->kref);
> +
> +	mutex_init(&ctx->inode_mutex);
> +	INIT_LIST_HEAD(&ctx->inode_list);
> +
> +	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;
> +
> +	switch (tsem_mode) {
> +	case FULL_MODELING:
> +		msg = "full";
> +		break;
> +	case NO_ROOT_MODELING:
> +		msg = "namespace only";
> +		break;
> +	case EXPORT_ONLY:
> +		msg = "export";
> +		break;
> +	}
> +	pr_info("tsem: Initialized %s modeling.\n", msg);
> +
> +	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,
> +};
Dr. Greg April 11, 2024, 6:13 p.m. UTC | #2
On Tue, Apr 02, 2024 at 02:15:18PM -0700, Casey Schaufler wrote:

Good morning, I hope the day is going well.

> On 4/1/2024 3:50 AM, Greg Wettstein wrote:
> > From: "Dr. Greg" <greg@enjellic.com>
> >
> > The tsem.c file is the 'master' file in the TSEM implementation.
> > It is responsible for initializing the LSM and providing
> > the implementation of the security event handlers.
> > ---
> >  security/tsem/tsem.c | 2422 ++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 2422 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..876ef1fa8012
> > --- /dev/null
> > +++ b/security/tsem/tsem.c
> > @@ -0,0 +1,2422 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +
> > +/*
> > + * Copyright (C) 2024 Enjellic Systems Development, LLC
> > + * Author: Dr. Greg Wettstein <greg@enjellic.com>
> > + *
> > + * This file is the primary implementation file for the tsem LSM.
> > + *
> > + * It implements initialization and setup functions that interpret
> > + * kernel command-line arguments and prepares TSEM for operation.
> > + *
> > + * In addition it contains all of the TSEM specific security event
> > + * handlers that are responsible for handling the LSM events that TSEM
> > + * models.
> > + *
> > + * Each TSEM event handler calls the tsem_allocate_event() function to
> > + * allocate a structure that will be used to describe the event.  The
> > + * CELL union of this structure contains various structures that are
> > + * used to hold these parameters.
> > + *
> > + * Since the event characterization parameters need to be retained for
> > + * the lifetime of the tsem_event structure that is allocated.  In the
> > + * case of internally modeled namespaces this lifespan is the lifetime
> > + * of the security modeling namespace.  In the case of externally
> > + * modeled namespaces, the lifespan is until the security event
> > + * description is exported to an external trust orchestrator.
> > + *
> > + * In order to support this model, the event description structures
> > + * are typically composed of a union over 'in' and 'out' structures.
> > + * The 'in' structures are used to hold arguments to the event handler
> > + * that may only be relevant for the duration of the call.  These
> > + * values are translated into members of the 'out' structure that
> > + * retain the values until the end of the lifetime of the tsem_event
> > + * structure.
> > + *
> > + * Each TSEM event handler is responsible for allocating a tsem_event
> > + * structure and populating the appropriate CELL structure with the
> > + * input characteristics of the event.  The dispatch_event() function
> > + * is called to handle the modeling of the event.  This function
> > + * returns the permission value that is returned as the result of the
> > + * LSM event handler.
> > + *
> > + * The dispatch_event() calls the tsem_event_init() function that is
> > + * responsible for translating the input parameters into values that
> > + * will be retained for the lifetime of the security event
> > + * description.  The populated event description is then dispatched to
> > + * either the tsem_model_event() or the tsem_export_event() for
> > + * modeling by either the internal TMA or by a TMA associated with an
> > + * external trust orchestrator.

> I'm not in favor of this level of indirection. Funneling all the LSM
> hooks into a dispatcher that fans out to a 1 to 1 (or near enough it
> doesn't make a difference) set of event handlers is just wasteful of
> resources.

You may have written that based on the documentation and before you
got to the dispatch_event() function, here it is for the benefit of
everyone following along at home:

static int dispatch_event(struct tsem_event *ep)
{
	int retn = 0;
	struct tsem_context *ctx = tsem_context(current);

	retn = tsem_event_init(ep);
	if (retn)
		return retn;

	if (unlikely(tsem_task_untrusted(current)))
		return untrusted_task(ep);

	if (!ctx->external)
		retn = tsem_model_event(ep);
	else
		retn = tsem_export_event(ep);

	tsem_event_put(ep);
	return retn;
}

We didn't implement a separate function in order to implement the
fanout, we implemented a separate dispatch function to consolidate
common functionality so that we didn't have to replicate it 86 times.

Would you advocate that we cut and paste the above into every handler?

We have no objections to doing so but issues about code size and the
like have been raised previously.

> > + */
> > +
> > +#define LOCKED true
> > +#define NOLOCK false

> As you have these constants defined the first would indicate that a
> lock is set, whereas the second indicates there is not a lock.  You
> should either pair LOCKED with UNLOCKED or LOCK with NOLOCK.  Or
> better yet, name your variables such that "true" and "false" are
> reasonable descriptions of their states.

We will change the constants to LOCK and NOLOCK, makes more sense.

> > +
> > +#include <linux/magic.h>
> > +#include <linux/mman.h>
> > +#include <linux/binfmts.h>
> > +#include <linux/bpf.h>
> > +#include <linux/mount.h>
> > +#include <linux/security.h>
> > +
> > +#include "tsem.h"
> > +
> > +static const struct lsm_id tsem_lsmid = {
> > +	.name = "tsem",
> > +	.id = LSM_ID_TSEM,
> > +};
> > +
> > +struct lsm_blob_sizes tsem_blob_sizes __ro_after_init = {
> > +	.lbs_task = sizeof(struct tsem_task),
> > +	.lbs_inode = sizeof(struct tsem_inode),
> > +	.lbs_ipc = sizeof(struct tsem_ipc),
> > +	.lbs_xattr_count = 1
> > +};
> > +
> > +enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT] = {
> > +	TSEM_ACTION_EPERM	/* Undefined. */
> > +};
> > +
> > +static atomic64_t task_instance;
> > +
> > +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),
> > +
> > +	.mount_mutex = __MUTEX_INITIALIZER(root_model.mount_mutex),
> > +	.mount_list = LIST_HEAD_INIT(root_model.mount_list)
> > +};
> > +
> > +static struct tsem_context root_context;
> > +
> > +DEFINE_STATIC_KEY_TRUE(tsem_not_ready);
> > +
> > +static bool tsem_available __ro_after_init;
> > +
> > +static unsigned int magazine_size __ro_after_init = TSEM_ROOT_MAGAZINE_SIZE;
> > +
> > +static enum mode_type {
> > +	FULL_MODELING,
> > +	NO_ROOT_MODELING,
> > +	EXPORT_ONLY
> > +} tsem_mode __ro_after_init;
> > +
> > +static char *default_hash_function __ro_after_init;
> > +
> > +const char * const tsem_names[TSEM_EVENT_CNT] = {
> > +	"undefined",
> > +	"bprm_committed_creds",
> > +	"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",
> > +	"ptrace_access_check",
> > +	"capable",
> > +	"capget",
> > +	"capset"
> > +};

> You can't seriously be counting on this being a mapping to tsem
> event numbers, with the event numbers being defined
> elsewhere. You'll never be able to keep them in sync. You probably
> want a structure here, with entries like
>
> 	{ "hook_name",	TSEM_EVENT_HOOK_NAME },

It is clumsy but it doesn't seem like there is a good fix for the
issue, other than documentation.

The tsem_event_type enumeration constants are used to index the array
of event names, so it doesn't seem like including the event type as
part of a structure, in that array, buys us much, we would never have
a purpose for referencing the type value in the array.

We will place the enumerated event name as a comment alongside each
event name.  We will also stick a comment on both the enumerated type
and the description name indicating the requirement that if one
changes the other needs to change as well.

> > +
> > +static const unsigned long 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 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);
> > +

> Why isn't this a void function?

Change:
static int __init set_magazine_size(char *magazine_value)
To:
static void __init set_magazine_size(char *magazine_value)

And delete 'return 1';

Then:

make security/tsem/tsem.o
  CALL    scripts/checksyscalls.sh
  Descend objtool
  INSTALL libsubcmd_headers
  CC      security/tsem/tsem.o
In file included from ./include/linux/printk.h:6,
                 from ./include/asm-generic/bug.h:22,
                 from ./arch/x86/include/asm/bug.h:87,
                 from ./include/linux/bug.h:5,
                 from ./include/linux/mmdebug.h:5,
                 from ./include/linux/mm.h:6,
                 from ./include/linux/mman.h:5,
                 from security/tsem/tsem.c:56:
security/tsem/tsem.c:238:24: error: initialization of 'int (*)(char *)' from incompatible pointer type 'void (*)(char *)' [-Werror=incompatible-pointer-types]
  238 | __setup("tsem_cache=", set_magazine_size);
      |                        ^~~~~~~~~~~~~~~~~
./include/linux/init.h:343:32: note: in definition of macro '__setup_param'
  343 |   = { __setup_str_##unique_id, fn, early }
      |                                ^~
security/tsem/tsem.c:238:1: note: in expansion of macro '__setup'
  238 | __setup("tsem_cache=", set_magazine_size);
      | ^~~~~~~
security/tsem/tsem.c:238:24: note: (near initialization for '__setup_set_magazine_size.setup_func')
  238 | __setup("tsem_cache=", set_magazine_size);
      |                        ^~~~~~~~~~~~~~~~~
./include/linux/init.h:343:32: note: in definition of macro '__setup_param'
  343 |   = { __setup_str_##unique_id, fn, early }
      |                                ^~
security/tsem/tsem.c:238:1: note: in expansion of macro '__setup'
  238 | __setup("tsem_cache=", set_magazine_size);
      | ^~~~~~~
cc1: some warnings being treated as errors
make[4]: *** [security/tsem/tsem.o] Error 1
make[3]: *** [security/tsem] Error 2
make[2]: *** [security] Error 2
make[1]: *** [.] Error 2
make: *** [__sub-make] Error 2

Compilation exited abnormally with code 2 at Wed Apr 10 12:12:46

> > +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)
> > +		tsem_mode = NO_ROOT_MODELING;
> > +	else if (mode == 2)
> > +		tsem_mode = EXPORT_ONLY;
> > +	else
> > +		pr_warn("tsem: Unknown mode specified.\n");
> > +}
> > +__setup("tsem_mode=", set_modeling_mode);
> > +

> If you're ever going to have additional "modes" you probably want a
> switch() rather than cascading if()s.  You seem generally aggressive
> with defining meaningful constants.  It seems out of character that
> you're using integer mode values instead of TSEM_MODE_CLOCKWISE and
> TSEM_MODE_WIDDERSHINS, or whatever the modes are.

After thinking a bit, the following may simplify things even more,
with the addition of NO_MODE to the mode_type enumeration.

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 >= NO_MODE)
		pr_warn("tsem: Unknown mode specified.\n");
	else
		tsem_mode = mode;
	return 1;
}

> Repeating myself the once. If a function can only ever return
> one value it should be a void function.

It would appear, from above, that the __setup() macro assumes a value
returning function.

> > +static int __init set_default_hash_function(char *hash_function)
> > +{
> > +
> > +	default_hash_function = hash_function;
> > +	return 1;
> > +}
> > +__setup("tsem_digest=", set_default_hash_function);
> > +
> > +static bool bypass_event(void)
> > +{
> > +	if (tsem_mode == NO_ROOT_MODELING && !tsem_context(current)->id)
> > +		return true;
> > +	return false;
> > +}
> > +
> > +static bool pseudo_filesystem(struct inode *inode)
> > +{
> > +	unsigned int lp;
> > +
> > +	for (lp = 0; lp < ARRAY_SIZE(pseudo_filesystems); ++lp)
> > +		if (inode->i_sb->s_magic == pseudo_filesystems[lp])
> > +			return true;
> > +	return false;
> > +}
> > +
> > +static int untrusted_task(struct tsem_event *ep)
> > +{
> > +	int retn = 0;
> > +	struct tsem_context *ctx = tsem_context(current);
> > +
> > +	if (ctx->external) {
> > +		retn = tsem_export_action(ep->event, ep->locked);
> > +		if (retn)
> > +			return retn;
> > +	} else
> > +		pr_warn("Untrusted event %s: model_ns=%lld, comm=%s, pid=%d\n",
> > +			tsem_names[ep->event], ctx->id, current->comm,
> > +			task_pid_nr(current));
> > +
> > +	if (ctx->actions[ep->event] == TSEM_ACTION_EPERM)
> > +		retn = -EPERM;
> > +	return retn;
> > +}
> > +
> > +static int dispatch_event(struct tsem_event *ep)
> > +{
> > +	int retn = 0;
> > +	struct tsem_context *ctx = tsem_context(current);
> > +
> > +	retn = tsem_event_init(ep);
> > +	if (retn)
> > +		return retn;
> > +
> > +	if (unlikely(tsem_task_untrusted(current)))
> > +		return untrusted_task(ep);
> > +
> > +	if (!ctx->external)
> > +		retn = tsem_model_event(ep);
> > +	else
> > +		retn = tsem_export_event(ep);
> > +
> > +	tsem_event_put(ep);
> > +	return retn;
> > +}
> > +
> > +static int tsem_file_open(struct file *file)
> > +{
> > +	struct inode *inode = file_inode(file);
> > +	struct tsem_event *ep;
> > +
> > +	if (static_branch_unlikely(&tsem_not_ready))
> > +		return 0;
> > +	if (bypass_event())
> > +		return 0;
> > +	if (unlikely(tsem_inode(inode)->status == TSEM_INODE_CONTROL_PLANE)) {
> > +		if (capable(CAP_MAC_ADMIN))
> > +			return 0;
> > +		else
> > +			return -EPERM;
> > +	}
> > +
> > +	if (!S_ISREG(inode->i_mode))
> > +		return 0;
> > +	if (tsem_inode(inode)->status == TSEM_INODE_COLLECTING)
> > +		return 0;
> > +
> > +	ep = tsem_event_allocate(TSEM_FILE_OPEN, NOLOCK);
> > +	if (!ep)
> > +		return -ENOMEM;
> > +
> > +	ep->CELL.file.in.file = file;
> > +	ep->CELL.file.in.pseudo_file = pseudo_filesystem(inode);
> > +
> > +	return dispatch_event(ep);
> > +}
> > +
> > +static int tsem_mmap_file(struct file *file, unsigned long prot,
> > +			  unsigned long flags, unsigned long extra)
> > +{
> > +	struct inode *inode = NULL;
> > +	struct tsem_event *ep;
> > +
> > +	if (static_branch_unlikely(&tsem_not_ready))
> > +		return 0;
> > +	if (bypass_event())
> > +		return 0;
> > +
> > +	if (!file && !(prot & PROT_EXEC))
> > +		return 0;
> > +	if (file) {
> > +		inode = file_inode(file);
> > +		if (!S_ISREG(inode->i_mode))
> > +			return 0;
> > +		if (pseudo_filesystem(inode))
> > +			return 0;
> > +	}
> > +
> > +	ep = tsem_event_allocate(TSEM_MMAP_FILE, NOLOCK);
> > +	if (!ep)
> > +		return -ENOMEM;
> > +
> > +	ep->CELL.mmap_file.anonymous = file == NULL ? 1 : 0;
> > +	ep->CELL.mmap_file.file.in.file = file;
> > +	ep->CELL.mmap_file.prot = prot;
> > +	ep->CELL.mmap_file.flags = flags;
> > +
> > +	return dispatch_event(ep);
> > +}
> > +
> > +static int tsem_file_ioctl(struct file *file, unsigned int cmd,
> > +			   unsigned long arg)
> > +{
> > +	struct tsem_event *ep;
> > +
> > +	if (bypass_event())
> > +		return 0;
> > +
> > +	ep = tsem_event_allocate(TSEM_FILE_IOCTL, NOLOCK);
> > +	if (!ep)
> > +		return -ENOMEM;
> > +
> > +	ep->CELL.file.cmd = cmd;
> > +	ep->CELL.file.in.file = file;
> > +	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
> > +
> > +	return dispatch_event(ep);
> > +}
> > +
> > +static int tsem_file_lock(struct file *file, unsigned int cmd)
> > +{
> > +	struct tsem_event *ep;
> > +
> > +	if (bypass_event())
> > +		return 0;
> > +
> > +	ep = tsem_event_allocate(TSEM_FILE_LOCK, NOLOCK);
> > +	if (!ep)
> > +		return -ENOMEM;
> > +
> > +	ep->CELL.file.cmd = cmd;
> > +	ep->CELL.file.in.file = file;
> > +	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
> > +
> > +	return dispatch_event(ep);
> > +}
> > +
> > +static int tsem_file_fcntl(struct file *file, unsigned int cmd,
> > +			   unsigned long arg)
> > +{
> > +	struct tsem_event *ep;
> > +
> > +	if (static_branch_unlikely(&tsem_not_ready))
> > +		return 0;
> > +	if (bypass_event())
> > +		return 0;
> > +
> > +	ep = tsem_event_allocate(TSEM_FILE_FCNTL, NOLOCK);
> > +	if (!ep)
> > +		return -ENOMEM;
> > +
> > +	ep->CELL.file.cmd = cmd;
> > +	ep->CELL.file.in.file = file;
> > +	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
> > +
> > +	return dispatch_event(ep);
> > +}
> > +
> > +static int tsem_file_receive(struct file *file)
> > +{
> > +	struct tsem_event *ep;
> > +
> > +	if (bypass_event())
> > +		return 0;
> > +
> > +	ep = tsem_event_allocate(TSEM_FILE_RECEIVE, NOLOCK);
> > +	if (!ep)
> > +		return -ENOMEM;
> > +
> > +	ep->CELL.file.in.file = file;
> > +	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
> > +
> > +	return dispatch_event(ep);
> > +}
> > +
> > +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->instance = old_task->instance;
> > +	new_task->p_instance = old_task->instance;
> > +
> > +	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);
> > +	memcpy(new_task->p_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);

> 	if (ctx->id)
> 		tsem_ne_put(ctx);
> 
> One less line of code.

Good catch, we usually try to spot those opportunities.

... [ code lacking review comments removed ] ...

Again, thank you for the comments, we appreciate them.

Have a good remainder of the week.

As always,
Dr. Greg

   The Quixote Project - Flailing at the Travails of Cybersecurity
		  https://github.com/Quixote-Project
diff mbox series

Patch

diff --git a/security/tsem/tsem.c b/security/tsem/tsem.c
new file mode 100644
index 000000000000..876ef1fa8012
--- /dev/null
+++ b/security/tsem/tsem.c
@@ -0,0 +1,2422 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (C) 2024 Enjellic Systems Development, LLC
+ * Author: Dr. Greg Wettstein <greg@enjellic.com>
+ *
+ * This file is the primary implementation file for the tsem LSM.
+ *
+ * It implements initialization and setup functions that interpret
+ * kernel command-line arguments and prepares TSEM for operation.
+ *
+ * In addition it contains all of the TSEM specific security event
+ * handlers that are responsible for handling the LSM events that TSEM
+ * models.
+ *
+ * Each TSEM event handler calls the tsem_allocate_event() function to
+ * allocate a structure that will be used to describe the event.  The
+ * CELL union of this structure contains various structures that are
+ * used to hold these parameters.
+ *
+ * Since the event characterization parameters need to be retained for
+ * the lifetime of the tsem_event structure that is allocated.  In the
+ * case of internally modeled namespaces this lifespan is the lifetime
+ * of the security modeling namespace.  In the case of externally
+ * modeled namespaces, the lifespan is until the security event
+ * description is exported to an external trust orchestrator.
+ *
+ * In order to support this model, the event description structures
+ * are typically composed of a union over 'in' and 'out' structures.
+ * The 'in' structures are used to hold arguments to the event handler
+ * that may only be relevant for the duration of the call.  These
+ * values are translated into members of the 'out' structure that
+ * retain the values until the end of the lifetime of the tsem_event
+ * structure.
+ *
+ * Each TSEM event handler is responsible for allocating a tsem_event
+ * structure and populating the appropriate CELL structure with the
+ * input characteristics of the event.  The dispatch_event() function
+ * is called to handle the modeling of the event.  This function
+ * returns the permission value that is returned as the result of the
+ * LSM event handler.
+ *
+ * The dispatch_event() calls the tsem_event_init() function that is
+ * responsible for translating the input parameters into values that
+ * will be retained for the lifetime of the security event
+ * description.  The populated event description is then dispatched to
+ * either the tsem_model_event() or the tsem_export_event() for
+ * modeling by either the internal TMA or by a TMA associated with an
+ * external trust orchestrator.
+ */
+
+#define LOCKED true
+#define NOLOCK false
+
+#include <linux/magic.h>
+#include <linux/mman.h>
+#include <linux/binfmts.h>
+#include <linux/bpf.h>
+#include <linux/mount.h>
+#include <linux/security.h>
+
+#include "tsem.h"
+
+static const struct lsm_id tsem_lsmid = {
+	.name = "tsem",
+	.id = LSM_ID_TSEM,
+};
+
+struct lsm_blob_sizes tsem_blob_sizes __ro_after_init = {
+	.lbs_task = sizeof(struct tsem_task),
+	.lbs_inode = sizeof(struct tsem_inode),
+	.lbs_ipc = sizeof(struct tsem_ipc),
+	.lbs_xattr_count = 1
+};
+
+enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT] = {
+	TSEM_ACTION_EPERM	/* Undefined. */
+};
+
+static atomic64_t task_instance;
+
+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),
+
+	.mount_mutex = __MUTEX_INITIALIZER(root_model.mount_mutex),
+	.mount_list = LIST_HEAD_INIT(root_model.mount_list)
+};
+
+static struct tsem_context root_context;
+
+DEFINE_STATIC_KEY_TRUE(tsem_not_ready);
+
+static bool tsem_available __ro_after_init;
+
+static unsigned int magazine_size __ro_after_init = TSEM_ROOT_MAGAZINE_SIZE;
+
+static enum mode_type {
+	FULL_MODELING,
+	NO_ROOT_MODELING,
+	EXPORT_ONLY
+} tsem_mode __ro_after_init;
+
+static char *default_hash_function __ro_after_init;
+
+const char * const tsem_names[TSEM_EVENT_CNT] = {
+	"undefined",
+	"bprm_committed_creds",
+	"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",
+	"ptrace_access_check",
+	"capable",
+	"capget",
+	"capset"
+};
+
+static const unsigned long 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 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)
+		tsem_mode = NO_ROOT_MODELING;
+	else if (mode == 2)
+		tsem_mode = EXPORT_ONLY;
+	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);
+
+static bool bypass_event(void)
+{
+	if (tsem_mode == NO_ROOT_MODELING && !tsem_context(current)->id)
+		return true;
+	return false;
+}
+
+static bool pseudo_filesystem(struct inode *inode)
+{
+	unsigned int lp;
+
+	for (lp = 0; lp < ARRAY_SIZE(pseudo_filesystems); ++lp)
+		if (inode->i_sb->s_magic == pseudo_filesystems[lp])
+			return true;
+	return false;
+}
+
+static int untrusted_task(struct tsem_event *ep)
+{
+	int retn = 0;
+	struct tsem_context *ctx = tsem_context(current);
+
+	if (ctx->external) {
+		retn = tsem_export_action(ep->event, ep->locked);
+		if (retn)
+			return retn;
+	} else
+		pr_warn("Untrusted event %s: model_ns=%lld, comm=%s, pid=%d\n",
+			tsem_names[ep->event], ctx->id, current->comm,
+			task_pid_nr(current));
+
+	if (ctx->actions[ep->event] == TSEM_ACTION_EPERM)
+		retn = -EPERM;
+	return retn;
+}
+
+static int dispatch_event(struct tsem_event *ep)
+{
+	int retn = 0;
+	struct tsem_context *ctx = tsem_context(current);
+
+	retn = tsem_event_init(ep);
+	if (retn)
+		return retn;
+
+	if (unlikely(tsem_task_untrusted(current)))
+		return untrusted_task(ep);
+
+	if (!ctx->external)
+		retn = tsem_model_event(ep);
+	else
+		retn = tsem_export_event(ep);
+
+	tsem_event_put(ep);
+	return retn;
+}
+
+static int tsem_file_open(struct file *file)
+{
+	struct inode *inode = file_inode(file);
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+	if (unlikely(tsem_inode(inode)->status == TSEM_INODE_CONTROL_PLANE)) {
+		if (capable(CAP_MAC_ADMIN))
+			return 0;
+		else
+			return -EPERM;
+	}
+
+	if (!S_ISREG(inode->i_mode))
+		return 0;
+	if (tsem_inode(inode)->status == TSEM_INODE_COLLECTING)
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_FILE_OPEN, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.file.in.file = file;
+	ep->CELL.file.in.pseudo_file = pseudo_filesystem(inode);
+
+	return dispatch_event(ep);
+}
+
+static int tsem_mmap_file(struct file *file, unsigned long prot,
+			  unsigned long flags, unsigned long extra)
+{
+	struct inode *inode = NULL;
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	if (!file && !(prot & PROT_EXEC))
+		return 0;
+	if (file) {
+		inode = file_inode(file);
+		if (!S_ISREG(inode->i_mode))
+			return 0;
+		if (pseudo_filesystem(inode))
+			return 0;
+	}
+
+	ep = tsem_event_allocate(TSEM_MMAP_FILE, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.mmap_file.anonymous = file == NULL ? 1 : 0;
+	ep->CELL.mmap_file.file.in.file = file;
+	ep->CELL.mmap_file.prot = prot;
+	ep->CELL.mmap_file.flags = flags;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_file_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_FILE_IOCTL, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.file.cmd = cmd;
+	ep->CELL.file.in.file = file;
+	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
+
+	return dispatch_event(ep);
+}
+
+static int tsem_file_lock(struct file *file, unsigned int cmd)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_FILE_LOCK, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.file.cmd = cmd;
+	ep->CELL.file.in.file = file;
+	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
+
+	return dispatch_event(ep);
+}
+
+static int tsem_file_fcntl(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_FILE_FCNTL, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.file.cmd = cmd;
+	ep->CELL.file.in.file = file;
+	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
+
+	return dispatch_event(ep);
+}
+
+static int tsem_file_receive(struct file *file)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_FILE_RECEIVE, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.file.in.file = file;
+	ep->CELL.file.in.pseudo_file = pseudo_filesystem(file_inode(file));
+
+	return dispatch_event(ep);
+}
+
+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->instance = old_task->instance;
+	new_task->p_instance = old_task->instance;
+
+	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);
+	memcpy(new_task->p_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)
+{
+	bool cross_model;
+	struct tsem_event *ep;
+	struct tsem_context *src_ctx = tsem_context(current);
+	struct tsem_context *tgt_ctx = tsem_context(target);
+
+	if (bypass_event())
+		return 0;
+
+	cross_model = src_ctx->id != tgt_ctx->id;
+
+	if (info != SEND_SIG_NOINFO && SI_FROMKERNEL(info))
+		return 0;
+	if (sig == SIGURG)
+		return 0;
+	if (!capable(CAP_MAC_ADMIN) &&
+	    has_capability_noaudit(target, CAP_MAC_ADMIN))
+		return -EPERM;
+	if (!capable(CAP_MAC_ADMIN) && cross_model)
+		return -EPERM;
+
+	ep = tsem_event_allocate(TSEM_TASK_KILL, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.task_kill.signal = sig;
+	ep->CELL.task_kill.cross_model = cross_model;
+	memcpy(ep->CELL.task_kill.target, tsem_task(target)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_ptrace_access_check(struct task_struct *child,
+				    unsigned int mode)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_PTRACE_ACCESS_CHECK, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.task_kill.u.resource = mode;
+	memcpy(ep->CELL.task_kill.target, tsem_task(child)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_ptrace_traceme(struct task_struct *parent)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_PTRACE_TRACEME, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	memcpy(ep->CELL.task_kill.source, tsem_task(parent)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_capget(const struct task_struct *target,
+		       kernel_cap_t *effective, kernel_cap_t *inheritable,
+		       kernel_cap_t *permitted)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_CAPGET, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.capability.effective = *effective;
+	ep->CELL.capability.inheritable = *inheritable;
+	ep->CELL.capability.permitted = *permitted;
+	memcpy(ep->CELL.capability.target, tsem_task(target)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_capset(struct cred *new, const struct cred *old,
+		       const kernel_cap_t *effective,
+		       const kernel_cap_t *inheritable,
+		       const kernel_cap_t *permitted)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_CAPSET, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.capability.effective = *effective;
+	ep->CELL.capability.inheritable = *inheritable;
+	ep->CELL.capability.permitted = *permitted;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_capable(const struct cred *cred, struct user_namespace *ns,
+			int cap, unsigned int opts)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_CAPABLE, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.capability.cap = cap;
+	ep->CELL.capability.opts = opts;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_setpgid(struct task_struct *p, pid_t pgid)
+{
+	struct tsem_event *ep;
+	struct task_struct *src;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_SETPGID, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
+	       tsem_digestsize());
+
+	if (!pgid)
+		memcpy(ep->CELL.task_kill.source, tsem_task(p)->task_id,
+		       tsem_digestsize());
+	else {
+		rcu_read_lock();
+		src = find_task_by_vpid(pgid);
+		rcu_read_unlock();
+		if (src)
+			memcpy(ep->CELL.task_kill.source,
+			       tsem_task(src)->task_id, tsem_digestsize());
+	}
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_getpgid(struct task_struct *p)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_GETPGID, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_getsid(struct task_struct *p)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_GETSID, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_setnice(struct task_struct *p, int nice)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_SETNICE, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.task_kill.u.value = nice;
+	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_setioprio(struct task_struct *p, int ioprio)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_SETIOPRIO, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.task_kill.u.value = ioprio;
+	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_getioprio(struct task_struct *p)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_GETIOPRIO, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_prlimit(const struct cred *cred, const struct cred *tcred,
+			     unsigned int flags)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_PRLIMIT, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.task_prlimit.flags = flags;
+	ep->CELL.task_prlimit.in.cred = cred;
+	ep->CELL.task_prlimit.in.tcred = tcred;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_setrlimit(struct task_struct *p, unsigned int resource,
+			       struct rlimit *new_rlim)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_SETRLIMIT, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.task_kill.u.resource = resource;
+	ep->CELL.task_kill.cur = new_rlim->rlim_cur;
+	ep->CELL.task_kill.max = new_rlim->rlim_max;
+	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_setscheduler(struct task_struct *p)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_SETSCHEDULER, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_getscheduler(struct task_struct *p)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_GETSCHEDULER, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	memcpy(ep->CELL.task_kill.target, tsem_task(p)->task_id,
+	       tsem_digestsize());
+
+	return dispatch_event(ep);
+}
+
+static int tsem_task_prctl(int option, unsigned long arg2, unsigned long arg3,
+			   unsigned long arg4, unsigned long arg5)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TASK_PRCTL, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.task_prctl.option = option;
+	ep->CELL.task_prctl.arg2 = arg2;
+	ep->CELL.task_prctl.arg3 = arg3;
+	ep->CELL.task_prctl.arg4 = arg4;
+	ep->CELL.task_prctl.arg5 = arg5;
+
+	return dispatch_event(ep);
+}
+
+static void tsem_bprm_committed_creds(const struct linux_binprm *bprm)
+{
+	u8 task_id[HASH_MAX_DIGESTSIZE];
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return;
+
+	if (tsem_map_task(bprm->file, task_id))
+		memset(task_id, 0xff, sizeof(task_id));
+
+	tsem_task(current)->instance = atomic64_inc_return(&task_instance);
+	memcpy(tsem_task(current)->task_id, task_id, tsem_digestsize());
+}
+
+static int tsem_inode_alloc_security(struct inode *inode)
+{
+	struct tsem_inode *tsip = tsem_inode(inode);
+
+	mutex_init(&tsip->digest_mutex);
+	INIT_LIST_HEAD(&tsip->digest_list);
+
+	mutex_init(&tsip->create_mutex);
+	INIT_LIST_HEAD(&tsip->create_list);
+
+	mutex_init(&tsip->instance_mutex);
+	INIT_LIST_HEAD(&tsip->instance_list);
+
+	return 0;
+}
+
+static int tsem_inode_init_security(struct inode *inode, struct inode *dir,
+				    const struct qstr *qstr,
+				    struct xattr *xattrs, int *xattr_count)
+{
+	u8 *owner = tsem_task(current)->task_id;
+	struct tsem_inode *tsip = tsem_inode(inode);
+	struct tsem_inode_instance *entry, *retn = NULL;
+
+	mutex_lock(&tsem_inode(dir)->create_mutex);
+	list_for_each_entry(entry, &tsem_inode(dir)->create_list, list) {
+		if (!memcmp(entry->owner, owner, tsem_digestsize()) &&
+		    !strcmp(qstr->name, entry->pathname)) {
+			retn = entry;
+			break;
+		}
+	}
+
+	if (retn) {
+		tsip->created = true;
+		tsip->creator = retn->creator;
+		tsip->instance = retn->instance;
+		memcpy(tsip->owner, retn->owner, tsem_digestsize());
+		list_del(&retn->list);
+	}
+	mutex_unlock(&tsem_inode(dir)->create_mutex);
+
+	if (!retn && S_ISREG(inode->i_mode))
+		WARN_ONCE(true, "Cannot find inode ownership information.");
+
+	return -EOPNOTSUPP;
+}
+
+static void _release_inode_instances(u64 id, struct tsem_inode *tsip)
+{
+	struct tsem_inode_instance *owner, *tmp_owner;
+
+	mutex_lock(&tsip->instance_mutex);
+	list_for_each_entry_safe(owner, tmp_owner, &tsip->instance_list,
+				 list) {
+		if (id == owner->creator) {
+			list_del(&owner->list);
+			kfree(owner);
+		}
+	}
+	mutex_unlock(&tsip->instance_mutex);
+}
+
+static void tsem_inode_free_security(struct inode *inode)
+{
+	struct tsem_inode_instance *owner, *tmp_owner;
+	struct tsem_inode_digest *digest, *tmp_digest;
+	struct tsem_inode_entry *entry, *tmp_entry;
+	struct tsem_context *ctx = tsem_context(current);
+
+	mutex_lock(&ctx->inode_mutex);
+	list_for_each_entry_safe(entry, tmp_entry, &ctx->inode_list, list) {
+		if (entry->tsip == tsem_inode(inode)) {
+			list_del(&entry->list);
+			_release_inode_instances(ctx->id, entry->tsip);
+			kfree(entry);
+		}
+	}
+	mutex_unlock(&ctx->inode_mutex);
+
+	list_for_each_entry_safe(digest, tmp_digest,
+				 &tsem_inode(inode)->digest_list, list) {
+		list_del(&digest->list);
+		kfree(digest->name);
+		kfree(digest);
+	}
+
+	list_for_each_entry_safe(owner, tmp_owner,
+				 &tsem_inode(inode)->create_list, list) {
+		list_del(&owner->list);
+		kfree(owner);
+	}
+
+	list_for_each_entry_safe(owner, tmp_owner,
+				 &tsem_inode(inode)->instance_list, list) {
+		list_del(&owner->list);
+		kfree(owner);
+	}
+}
+
+static int tsem_unix_stream_connect(struct sock *sock, struct sock *other,
+				    struct sock *newsk)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_UNIX_STREAM_CONNECT, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = sock;
+	ep->CELL.socket.in.sockb = other;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_unix_may_send(struct socket *sock, struct socket *other)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_UNIX_MAY_SEND, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = sock->sk;
+	ep->CELL.socket.in.sockb = other->sk;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_post_create(struct socket *sock, int family, int type,
+				   int protocol, int kern)
+{
+	struct tsem_inode *tsip = tsem_inode(SOCK_INODE(sock));
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+
+	memcpy(tsip->owner, tsem_task(current)->task_id, tsem_digestsize());
+	return 0;
+}
+
+static int tsem_socket_create(int family, int type, int protocol, int kern)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_CREATE, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.out.socka.family = family;
+	ep->CELL.socket.out.socka.type = type;
+	ep->CELL.socket.out.socka.protocol = protocol;
+	ep->CELL.socket.out.socka.kern = kern;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_connect(struct socket *sock, struct sockaddr *addr,
+			     int addr_len)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_CONNECT, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = sock->sk;
+	ep->CELL.socket.in.addr = addr;
+	ep->CELL.socket.value = addr_len;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_bind(struct socket *sock, struct sockaddr *addr,
+			    int addr_len)
+
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_BIND, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = sock->sk;
+	ep->CELL.socket.in.addr = addr;
+	ep->CELL.socket.value = addr_len;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_accept(struct socket *sock, struct socket *newsock)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_ACCEPT, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = sock->sk;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_listen(struct socket *sock, int backlog)
+
+{
+	struct sock *sk = sock->sk;
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_LISTEN, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.value = backlog;
+	ep->CELL.socket.in.socka = sk;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_socketpair(struct socket *socka, struct socket *sockb)
+{
+	struct sock *ska = socka->sk, *skb = sockb->sk;
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_SOCKETPAIR, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = ska;
+	ep->CELL.socket.in.sockb = skb;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_sendmsg(struct socket *sock, struct msghdr *msgmsg,
+			       int size)
+{
+	struct sock *sk = sock->sk;
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_SENDMSG, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = sk;
+	ep->CELL.socket.in.addr = msgmsg->msg_name;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_recvmsg(struct socket *sock, struct msghdr *msgmsg,
+			       int size, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_RECVMSG, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = sk;
+	if (msgmsg->msg_name && msgmsg->msg_namelen > 0)
+		ep->CELL.socket.in.addr = msgmsg->msg_name;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_getsockname(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_GETSOCKNAME, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = sk;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_getpeername(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_GETPEERNAME, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = sk;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_setsockopt(struct socket *sock, int level, int optname)
+{
+	struct sock *sk = sock->sk;
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_SETSOCKOPT, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.value = level;
+	ep->CELL.socket.optname = optname;
+	ep->CELL.socket.in.socka = sk;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_socket_shutdown(struct socket *sock, int how)
+{
+	struct sock *sk = sock->sk;
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SOCKET_SHUTDOWN, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.value = how;
+	ep->CELL.socket.in.socka = sk;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_kernel_module_request(char *kmod_name)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_KERNEL_MODULE_REQUEST, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.kernel.in.kmod_name = kmod_name;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_kernel_load_data(enum kernel_load_data_id id, bool contents)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_KERNEL_LOAD_DATA, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.kernel.id = id;
+	ep->CELL.kernel.contents = contents;
+
+	return dispatch_event(ep);
+}
+
+
+static int tsem_kernel_read_file(struct file *file,
+				 enum kernel_read_file_id id, bool contents)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_KERNEL_READ_FILE, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.kernel.id = id;
+	ep->CELL.kernel.contents = contents;
+	ep->CELL.kernel.in.file = file;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_sb_mount(const char *dev_name, const struct path *path,
+			 const char *type, unsigned long flags, void *data)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SB_MOUNT, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.sb.flags = flags;
+	ep->CELL.sb.in.dev_name = dev_name;
+	ep->CELL.sb.in.path = path;
+	ep->CELL.sb.in.type = type;
+
+	return dispatch_event(ep);
+}
+
+static	int tsem_sb_umount(struct vfsmount *mnt, int flags)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SB_UMOUNT, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.sb.flags = flags;
+	ep->CELL.sb.in.dentry = mnt->mnt_root;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_sb_remount(struct super_block *sb, void *mnt_opts)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SB_REMOUNT, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.sb.in.sb = sb;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_sb_pivotroot(const struct path *old_path,
+			     const struct path *new_path)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SB_PIVOTROOT, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.sb.in.path = old_path;
+	ep->CELL.sb.in.path2 = new_path;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_sb_statfs(struct dentry *dentry)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SB_STATFS, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.sb.in.dentry = dentry;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_move_mount(const struct path *from_path,
+			   const struct path *to_path)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_MOVE_MOUNT, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.sb.in.path = from_path;
+	ep->CELL.sb.in.path2 = to_path;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_shm_associate(struct kern_ipc_perm *perm, int shmflg)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SHM_ASSOCIATE, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.in.perm = perm;
+	ep->CELL.ipc.value = shmflg;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_shm_shmctl(struct kern_ipc_perm *perm, int cmd)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SHM_SHMCTL, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.in.perm = perm;
+	ep->CELL.ipc.value = cmd;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_shm_shmat(struct kern_ipc_perm *perm, char __user *shmaddr,
+			  int shmflg)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SHM_SHMAT, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.in.perm = perm;
+	ep->CELL.ipc.value = shmflg;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_sem_associate(struct kern_ipc_perm *perm, int semflg)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SEM_ASSOCIATE, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.in.perm = perm;
+	ep->CELL.ipc.value = semflg;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_sem_semctl(struct kern_ipc_perm *perm, int cmd)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SEM_SEMCTL, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.in.perm = perm;
+	ep->CELL.ipc.value = cmd;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_sem_semop(struct kern_ipc_perm *perm, struct sembuf *sops,
+			  unsigned int nsops, int alter)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SEM_SEMOP, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.in.perm = perm;
+	ep->CELL.ipc.nsops = nsops;
+	ep->CELL.ipc.value = alter;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_syslog(int type)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SYSLOG, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.value = type;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_settime(const struct timespec64 *ts, const struct timezone *tz)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_SETTIME, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	if (ts) {
+		ep->CELL.time.have_ts = true;
+		ep->CELL.time.seconds = ts->tv_sec;
+		ep->CELL.time.nsecs = ts->tv_nsec;
+	}
+	if (tz) {
+		ep->CELL.time.have_tz = true;
+		ep->CELL.time.minuteswest = tz->tz_minuteswest;
+		ep->CELL.time.dsttime = tz->tz_dsttime;
+	}
+
+	return dispatch_event(ep);
+}
+
+static int tsem_quotactl(int cmds, int type, int id,
+			 const struct super_block *sb)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_QUOTACTL, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.quota.cmds = cmds;
+	ep->CELL.quota.type = type;
+	ep->CELL.quota.id = id;
+	ep->CELL.quota.in.sb = sb;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_quota_on(struct dentry *dentry)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_QUOTA_ON, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.quota.in.dentry = dentry;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_msg_queue_associate(struct kern_ipc_perm *perm, int msqflg)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_MSG_QUEUE_ASSOCIATE, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.in.perm = perm;
+	ep->CELL.ipc.value = msqflg;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_msg_queue_msgsnd(struct kern_ipc_perm *perm,
+				 struct msg_msg *msgmsg, int msqflg)
+
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_MSG_QUEUE_MSGSND, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.in.perm = perm;
+	ep->CELL.ipc.value = msqflg;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_msg_queue_msgctl(struct kern_ipc_perm *perm, int cmd)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_MSG_QUEUE_MSGCTL, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.in.perm = perm;
+	ep->CELL.ipc.value = cmd;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_msg_queue_msgrcv(struct kern_ipc_perm *perm,
+				 struct msg_msg *msgmsg,
+				 struct task_struct *target, long type,
+				 int mode)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_MSG_QUEUE_MSGRCV, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.in.perm = perm;
+	ep->CELL.ipc.in.target = target;
+	ep->CELL.ipc.type = type;
+	ep->CELL.ipc.value = mode;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_ipc_alloc(struct kern_ipc_perm *kipc)
+{
+	struct tsem_ipc *tipc = tsem_ipc(kipc);
+
+	memcpy(tipc->owner, tsem_task(current)->task_id, tsem_digestsize());
+	return 0;
+}
+
+static int tsem_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_IPC_PERMISSION, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.ipc.perm_flag = flag;
+	ep->CELL.ipc.in.perm = ipcp;
+
+	return dispatch_event(ep);
+}
+
+#ifdef CONFIG_KEYS
+static int tsem_key_alloc(struct key *key, const struct cred *cred,
+			  unsigned long flags)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_KEY_ALLOC, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.key.flags = flags;
+	ep->CELL.key.in.cred = cred;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_key_permission(key_ref_t key_ref, const struct cred *cred,
+			       unsigned int perm)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_KEY_PERMISSION, LOCKED);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.key.flags = perm;
+	ep->CELL.key.in.cred = cred;
+	ep->CELL.key.in.ref = key_ref;
+
+	return dispatch_event(ep);
+}
+#endif
+
+static int tsem_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_NETLINK_SEND, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.netlink.in.sock = sk;
+	ep->CELL.netlink.in.parms = (struct netlink_skb_parms *) skb->cb;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_create(struct inode *dir, struct dentry *dentry,
+			     umode_t mode)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_CREATE, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode.in.dir = dir;
+	ep->CELL.inode.in.dentry = dentry;
+	ep->CELL.inode.mode = mode;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_link(struct dentry *old_dentry, struct inode *dir,
+			   struct dentry *new_dentry)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_LINK, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode.in.dir = dir;
+	ep->CELL.inode.in.dentry = old_dentry;
+	ep->CELL.inode.in.new_dentry = new_dentry;
+	ep->CELL.inode.mode = 0;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_UNLINK, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode.in.dir = dir;
+	ep->CELL.inode.in.dentry = dentry;
+	ep->CELL.inode.mode = 0;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_symlink(struct inode *dir, struct dentry *dentry,
+			      const char *old_name)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_SYMLINK, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode.in.dir = dir;
+	ep->CELL.inode.in.dentry = dentry;
+	ep->CELL.inode.in.old_name = old_name;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_mkdir(struct inode *dir, struct dentry *dentry,
+			    umode_t mode)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_MKDIR, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode.in.dir = dir;
+	ep->CELL.inode.in.dentry = dentry;
+	ep->CELL.inode.mode = mode;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_RMDIR, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode.in.dir = dir;
+	ep->CELL.inode.in.dentry = dentry;
+	ep->CELL.inode.mode = 0;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
+			     struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_RENAME, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode.in.dir = old_dir;
+	ep->CELL.inode.in.new_dir = new_dir;
+	ep->CELL.inode.in.dentry = old_dentry;
+	ep->CELL.inode.in.new_dentry = new_dentry;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_mknod(struct inode *dir, struct dentry *dentry,
+			    umode_t mode, dev_t dev)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_MKNOD, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode.in.dir = dir;
+	ep->CELL.inode.in.dentry = dentry;
+	ep->CELL.inode.mode = mode;
+	ep->CELL.inode.dev = dev;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_setattr(struct mnt_idmap *idmap,
+			      struct dentry *dentry, struct iattr *attr)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_SETATTR, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode_attr.in.dentry = dentry;
+	ep->CELL.inode_attr.in.iattr = attr;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_getattr(const struct path *path)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_GETATTR, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode_attr.in.path = path;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_setxattr(struct mnt_idmap *idmap,
+			       struct dentry *dentry, const char *name,
+			       const void *value, size_t size, int flags)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_SETXATTR, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode_xattr.in.dentry = dentry;
+	ep->CELL.inode_xattr.in.name = name;
+	ep->CELL.inode_xattr.in.value = value;
+	ep->CELL.inode_xattr.in.size = size;
+	ep->CELL.inode_xattr.in.flags = flags;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_getxattr(struct dentry *dentry, const char *name)
+{
+	struct tsem_event *ep = NULL;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_GETXATTR, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode_xattr.in.dentry = dentry;
+	ep->CELL.inode_xattr.in.name = name;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_listxattr(struct dentry *dentry)
+{
+	struct tsem_event *ep;
+
+	if (static_branch_unlikely(&tsem_not_ready))
+		return 0;
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_LISTXATTR, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode_xattr.in.dentry = dentry;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_removexattr(struct mnt_idmap *idmap,
+				  struct dentry *dentry, const char *name)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_REMOVEXATTR, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode_xattr.in.dentry = dentry;
+	ep->CELL.inode_xattr.in.name = name;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_inode_killpriv(struct mnt_idmap *idmap, struct dentry *dentry)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_INODE_KILLPRIV, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.inode.in.dentry = dentry;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_tun_dev_create(void)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TUN_DEV_CREATE, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+	ep->no_params = true;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_tun_dev_attach_queue(void *security)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TUN_DEV_ATTACH_QUEUE, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+	ep->no_params = true;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_tun_dev_attach(struct sock *sk, void *security)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TUN_DEV_ATTACH, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.socket.in.socka = sk;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_tun_dev_open(void *security)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_TUN_DEV_OPEN, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+	ep->no_params = true;
+
+	return dispatch_event(ep);
+}
+
+#ifdef CONFIG_BPF_SYSCALL
+static int tsem_bpf(int cmd, union bpf_attr *attr, unsigned int size)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_BPF, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.bpf.bpf.cmd = cmd;
+	ep->CELL.bpf.bpf.size = size;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_bpf_map(struct bpf_map *map, fmode_t fmode)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_BPF_MAP, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.bpf.map.fmode = fmode;
+	ep->CELL.bpf.map.map_type = map->map_type;
+
+	return dispatch_event(ep);
+}
+
+static int tsem_bpf_prog(struct bpf_prog *prog)
+{
+	struct tsem_event *ep;
+
+	if (bypass_event())
+		return 0;
+
+	ep = tsem_event_allocate(TSEM_BPF_PROG, NOLOCK);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->CELL.bpf.prog.type = prog->type;
+	ep->CELL.bpf.prog.attach_type = prog->expected_attach_type;
+
+	return dispatch_event(ep);
+}
+#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_access_check, tsem_ptrace_access_check),
+	LSM_HOOK_INIT(ptrace_traceme, tsem_ptrace_traceme),
+
+	LSM_HOOK_INIT(capget, tsem_capget),
+	LSM_HOOK_INIT(capset, tsem_capset),
+	LSM_HOOK_INIT(capable, tsem_capable),
+
+	LSM_HOOK_INIT(bprm_committed_creds, tsem_bprm_committed_creds),
+
+	LSM_HOOK_INIT(inode_alloc_security, tsem_inode_alloc_security),
+	LSM_HOOK_INIT(inode_init_security, tsem_inode_init_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_post_create, tsem_socket_post_create),
+	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_alloc_security, tsem_ipc_alloc),
+	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_alloc_security, tsem_ipc_alloc),
+	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_alloc_security, tsem_ipc_alloc),
+	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];
+	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,
+	       crypto_shash_digestsize(tfm));
+
+ 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;
+
+	if (tsem_mode == EXPORT_ONLY) {
+		retn = tsem_ns_export_root(magazine_size);
+		if (retn)
+			goto done;
+	}
+
+	pr_info("tsem: Now active.\n");
+	static_branch_disable(&tsem_not_ready);
+
+ 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;
+	char *msg;
+	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_lsmid);
+
+	tsk->context = ctx;
+	kref_init(&ctx->kref);
+	kref_get(&ctx->kref);
+
+	mutex_init(&ctx->inode_mutex);
+	INIT_LIST_HEAD(&ctx->inode_list);
+
+	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;
+
+	switch (tsem_mode) {
+	case FULL_MODELING:
+		msg = "full";
+		break;
+	case NO_ROOT_MODELING:
+		msg = "namespace only";
+		break;
+	case EXPORT_ONLY:
+		msg = "export";
+		break;
+	}
+	pr_info("tsem: Initialized %s modeling.\n", msg);
+
+	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,
+};