Re: [PATCH v3 04/12] Add primary TSEM implementation file.

From: Casey Schaufler
Date: Tue Apr 02 2024 - 17:35:54 EST


On 4/1/2024 3:50 AM, Greg Wettstein wrote:
> From: "Dr. Greg" <greg@xxxxxxxxxxxx>
>
> 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@xxxxxxxxxxxx>
> + *
> + * 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,
> +};