Re: [PATCH 05/13] Add primary TSEM implementation file.

Casey Schaufler
2023-08-07T21:00:27Z

On 7/10/2023 3:23 AM, Dr. Greg wrote:
> The tsem.c file is the 'master' file in the TSEM implementation.
> It is responsible for initializing the LSM and providing
> implementions of the security event hooks implemented by TSEM.
>
> In addition to initializing the LSM, the set_ready() function
> implements a secondary initialization that is used to to indicate
> that security event modeling can begin. This is required
> secondary to the fact that the cryptographic API's do not become
> ready until after the 'fs' phase of system initialization is
> complete.
>
> This file also handles the implementation of kernel command-line
> parameters that are used to configure the root security modeling
> namespace.
>
> The 'tsem_mode' parameter, if set to a value of 1, causes modeling
> to be not conducted for the root security modeling namespace.
> One use case is to allow development platform to develop security
> models without the overhead of full platform modeling.
>
> The 'tsem_digest' parameter is used to set the cryptographic hash
> function that is used to generate security state coefficients in
> the root model. TSEM can use any cryptographic hash function
> implemented in the kernel, on a namespace by namespace basis.
> Subordinate modeling namespaces can select their hash function
> as part of the namespace creation process but the 'tsem_digest'
> parameter has to be used to set the function for the root
> modeling namespace.
>
> The hash function used in the root modeling namespace but be
> compiled into the kernel since the function is used before module
> loading becomes available. The TSEM kernel configuration selects
> the SHA256 function to be included as the default cryptographic
> modeling function.
>
> The 'tsem_cache' variable sets the size of the pre-allocated
> structures that are used for security event modeling in the root
> modeling namespace. This cache is used to support the modeling
> and export of events that run in atomic context. The cache size
> can be set independently for each subordinate security modeling
> on a namespace by namespace basis.
>
> This file also contains the implementation of the tsem_names
> array that contains the ASCII text names that are assigned to
> each security event handler. This name is used as one of the
> characteristics in the security state points that are generated.
> This array is also used to provide symbolic names for the export
> of security event descriptions, either through the TSEM control
> plane or for export to external trust orchestrators.
>
> Signed-off-by: Greg Wettstein <greg@enjellic.com>
> ---
> security/tsem/tsem.c | 1987 ++++++++++++++++++++++++++++++++++++++++++

Please use kernel doc comments throughout.
I've made a few minor comments below, but you need to break this up
somehow for review. Also, having the data definitions elsewhere makes
review tough.

> 1 file changed, 1987 insertions(+)
> create mode 100644 security/tsem/tsem.c
>
> diff --git a/security/tsem/tsem.c b/security/tsem/tsem.c
> new file mode 100644
> index 000000000000..8ec630354240
> --- /dev/null
> +++ b/security/tsem/tsem.c
> @@ -0,0 +1,1987 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +/*
> + * Copyright (C) 2023 Enjellic Systems Development, LLC
> + * Author: Dr. Greg Wettstein <greg@enjellic.com>
> + *
> + * TSEM initialization infrastructure.
> + */
> +#define TRAPPED_MSG_LENGTH 128
> +
> +#define LOCKED true
> +#define NOLOCK false
> +
> +#include <linux/magic.h>
> +#include <linux/mman.h>
> +#include <linux/binfmts.h>
> +#include <linux/bpf.h>
> +#include <linux/ipv6.h>
> +
> +#include "tsem.h"
> +
> +struct lsm_blob_sizes tsem_blob_sizes __ro_after_init = {
> + .lbs_task = sizeof(struct tsem_task),

Do you really want this for the task? It would seem you might
want to use the cred blob instead.

> + .lbs_inode = sizeof(struct tsem_inode)
> +};
> +
> +enum tsem_action_type tsem_root_actions[TSEM_EVENT_CNT] = {
> + TSEM_ACTION_EPERM /* Undefined. */
> +};
> +
> +static struct tsem_model root_model = {
> + .point_lock = __SPIN_LOCK_INITIALIZER(root_model.point_lock),
> + .point_list = LIST_HEAD_INIT(root_model.point_list),
> + .point_end_mutex = __MUTEX_INITIALIZER(root_model.point_end_mutex),
> +
> + .trajectory_lock = __SPIN_LOCK_INITIALIZER(root_model.trajectory_lock),
> + .trajectory_list = LIST_HEAD_INIT(root_model.trajectory_list),
> + .trajectory_end_mutex = __MUTEX_INITIALIZER(root_model.trajectory_end_mutex),
> +
> + .forensics_lock = __SPIN_LOCK_INITIALIZER(root_model.forensics_lock),
> + .forensics_list = LIST_HEAD_INIT(root_model.forensics_list),
> + .forensics_end_mutex = __MUTEX_INITIALIZER(root_model.forensics_end_mutex),
> +
> + .pseudonym_mutex = __MUTEX_INITIALIZER(root_model.pseudonym_mutex),
> + .pseudonym_list = LIST_HEAD_INIT(root_model.pseudonym_list)
> +};
> +
> +static struct tsem_context root_context;
> +
> +static int tsem_ready __ro_after_init;
> +
> +static bool tsem_available __ro_after_init;
> +
> +static unsigned int magazine_size __ro_after_init = TSEM_ROOT_MAGAZINE_SIZE;
> +
> +static bool no_root_modeling __ro_after_init;
> +
> +static char *default_hash_function __ro_after_init;
> +
> +static int __init set_magazine_size(char *magazine_value)
> +{
> + if (kstrtouint(magazine_value, 0, &magazine_size))
> + pr_warn("tsem: Failed to parse root cache size.\n");
> +
> + if (!magazine_size) {
> + pr_warn("tsem: Forcing non-zero cache size.\n");
> + magazine_size = TSEM_ROOT_MAGAZINE_SIZE;
> + }
> +
> + pr_info("tsem: Setting default root cache size to %u.\n",
> + magazine_size);
> + return 1;
> +}
> +__setup("tsem_cache=", set_magazine_size);
> +
> +static int __init set_modeling_mode(char *mode_value)
> +{
> + unsigned long mode = 0;
> +
> + if (kstrtoul(mode_value, 0, &mode)) {
> + pr_warn("tsem: Failed to parse modeling mode.\n");
> + return 1;
> + }
> +
> + if (mode == 1)
> + no_root_modeling = true;
> + else
> + pr_warn("tsem: Unknown mode specified.\n");
> + return 1;
> +}
> +__setup("tsem_mode=", set_modeling_mode);
> +
> +static int __init set_default_hash_function(char *hash_function)
> +{
> +
> + default_hash_function = hash_function;
> + return 1;
> +}
> +__setup("tsem_digest=", set_default_hash_function);
> +
> +const char * const tsem_names[TSEM_EVENT_CNT] = {
> + "undefined",
> + "bprm_set_creds",
> + "generic_event",
> + "task_kill",
> + "task_setpgid",
> + "task_getpgid",
> + "task_getsid",
> + "task_setnice",
> + "task_setioprio",
> + "task_getioprio",
> + "task_prlimit",
> + "task_setrlimit",
> + "task_setscheduler",
> + "task_getscheduler",
> + "task_prctl",
> + "file_open",
> + "mmap_file",
> + "file_ioctl",
> + "file_lock",
> + "file_fcntl",
> + "file_receive",
> + "unix_stream_connect",
> + "unix_may_send",
> + "socket_create",
> + "socket_connect",
> + "socket_bind",
> + "socket_accept",
> + "socket_listen",
> + "socket_socketpair",
> + "socket_sendmsg",
> + "socket_recvmsg",
> + "socket_getsockname",
> + "socket_getpeername",
> + "socket_setsockopt",
> + "socket_shutdown",
> + "ptrace_traceme",
> + "kernel_module_request",
> + "kernel_load_data",
> + "kernel_read_file",
> + "sb_mount",
> + "sb_umount",
> + "sb_remount",
> + "sb_pivotroot",
> + "sb_statfs",
> + "move_mount",
> + "shm_associate",
> + "shm_shmctl",
> + "shm_shmat",
> + "sem_associate",
> + "sem_semctl",
> + "sem_semop",
> + "syslog",
> + "settime",
> + "quotactl",
> + "quota_on",
> + "msg_queue_associate",
> + "msg_queue_msgctl",
> + "msg_queue_msgsnd",
> + "msg_queue_msgrcv",
> + "ipc_permission",
> + "key_alloc",
> + "key_permission",
> + "netlink_send",
> + "inode_create",
> + "inode_link",
> + "inode_unlink",
> + "inode_symlink",
> + "inode_mkdir",
> + "inode_rmdir",
> + "inode_mknod",
> + "inode_rename",
> + "inode_setattr",
> + "inode_getattr",
> + "inode_setxattr",
> + "inode_getxattr",
> + "inode_listxattr",
> + "inode_removexattr",
> + "inode_killpriv",
> + "tun_dev_create",
> + "tun_dev_attach_queue",
> + "tun_dev_attach",
> + "tun_dev_open",
> + "bpf",
> + "bpf_map",
> + "bpf_prog"
> +};
> +
> +static const int pseudo_filesystems[] = {
> + PROC_SUPER_MAGIC,
> + SYSFS_MAGIC,
> + DEBUGFS_MAGIC,
> + TMPFS_MAGIC,
> + DEVPTS_SUPER_MAGIC,
> + BINFMTFS_MAGIC,
> + SECURITYFS_MAGIC,
> + SELINUX_MAGIC,
> + SMACK_MAGIC,
> + CGROUP_SUPER_MAGIC,
> + CGROUP2_SUPER_MAGIC,
> + NSFS_MAGIC,
> + EFIVARFS_MAGIC
> +};
> +
> +static bool bypass_inode(struct inode *inode)
> +{
> + bool retn = true;
> +
> + unsigned int lp;
> +
> + if (!S_ISREG(inode->i_mode))
> + goto done;
> +
> + for (lp = 0; lp < ARRAY_SIZE(pseudo_filesystems); ++lp)
> + if (inode->i_sb->s_magic == pseudo_filesystems[lp])
> + goto done;
> + retn = false;
> +
> + done:
> + return retn;
> +}
> +
> +static int event_action(struct tsem_context *ctx, enum tsem_event_type event)
> +{
> + int retn = 0;
> +
> + if (tsem_task_trusted(current))
> + return retn;
> +
> + if (ctx->actions[event] == TSEM_ACTION_EPERM)
> + retn = -EPERM;
> +
> + return retn;
> +}
> +
> +static int return_trapped_task(enum tsem_event_type event, char *msg,

Why isn't this simply "trapped_task()"?

> + bool locked)
> +{
> + int retn;
> + struct tsem_context *ctx = tsem_context(current);
> +
> + pr_warn("Untrusted %s: comm=%s, pid=%d, parameters='%s'\n",
> + tsem_names[event], current->comm, task_pid_nr(current), msg);
> +
> + if (ctx->external) {
> + retn = tsem_export_action(event, locked);
> + if (retn)
> + return retn;
> + }
> +
> + return event_action(ctx, event);
> +}
> +
> +static int return_trapped_inode(enum tsem_event_type event,

Again, really odd function name.

... and that's all I have time for.