new file mode 100644
@@ -0,0 +1,1846 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (C) 2024 Enjellic Systems Development, LLC
+ * Author: Dr. Greg Wettstein <greg@enjellic.com>
+ *
+ * This file handles the creation and population of the tsem_event
+ * structure that describes each security event that occurs. The
+ * structures that are held in the CELL union of the tsem_event
+ * structure are used to deliver the characterizing parameters of a
+ * security into the tsem_event_init() function.
+ *
+ * Most of the structures used to characterize a security event use
+ * a strategy where a union is used to enclose an 'in' and 'out'
+ * structure. The parameters that are relevant to the TSEM
+ * characterization of an event are placed in the 'in' structure. The
+ * routines in this function are responsible for propagating these
+ * characteristics into the 'out' structure for the lifetime of the
+ * structure.
+ */
+
+#include <linux/iversion.h>
+#include <linux/user_namespace.h>
+#include <linux/base64.h>
+#include <linux/ipv6.h>
+#include <uapi/linux/prctl.h>
+
+#include "../integrity/integrity.h"
+#include "tsem.h"
+
+static struct kmem_cache *event_cachep;
+static bool created_inode(struct tsem_inode *tsip)
+{
+ return tsip->created && tsip->creator == tsem_context(current)->id;
+}
+
+static int register_directory(struct tsem_inode *tsip)
+{
+ int retn = 0;
+ struct tsem_inode_entry *entry = NULL;
+ struct tsem_context *ctx = tsem_context(current);
+
+ mutex_lock(&ctx->inode_mutex);
+ list_for_each_entry(entry, &ctx->inode_list, list) {
+ if (entry->tsip == tsip)
+ goto done;
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ retn = -ENOMEM;
+ goto done;
+ }
+
+ entry->tsip = tsip;
+ list_add_tail(&entry->list, &ctx->inode_list);
+
+ done:
+ mutex_unlock(&ctx->inode_mutex);
+ return retn;
+}
+
+static int register_inode_create(struct inode *dir, u64 instance,
+ char *pathname)
+{
+ char *p;
+ int retn;
+ struct tsem_inode_instance *tio = NULL;
+ struct tsem_inode *tsip = tsem_inode(dir);
+
+ p = strrchr(pathname, '/');
+ if (!p)
+ return -EINVAL;
+
+ if (tsem_context(current)->id) {
+ retn = register_directory(tsip);
+ if (retn)
+ return retn;
+ }
+
+ tio = kzalloc(sizeof(*tio), GFP_KERNEL);
+ if (!tio)
+ return -ENOMEM;
+
+ tio->pathname = ++p;
+ tio->creator = tsem_context(current)->id;
+ tio->instance = instance;
+ memcpy(tio->owner, tsem_task(current)->task_id, tsem_digestsize());
+
+ mutex_lock(&tsip->create_mutex);
+ list_add_tail(&tio->list, &tsip->create_list);
+ mutex_unlock(&tsip->create_mutex);
+
+ return 0;
+}
+
+static int get_root(struct dentry *dentry, struct tsem_path *path)
+{
+ int size, retn = 0;
+ char *p, *p1, *pathbuffer = NULL;
+ struct super_block *sb = dentry->d_sb;
+ struct inode *inode = d_backing_inode(sb->s_root);
+
+ if (MAJOR(sb->s_dev) || inode->i_op->rename)
+ path->dev = sb->s_dev;
+ else {
+ if (dentry->d_op && dentry->d_op->d_dname) {
+ pathbuffer = __getname();
+ p = dentry->d_op->d_dname(dentry, pathbuffer, 4096);
+ p1 = strchr(p, ':');
+ if (p1)
+ *p1 = '\0';
+
+ size = strlen(p) + 3;
+ path->pathname = kmalloc(size, GFP_KERNEL);
+ if (IS_ERR(path->pathname)) {
+ retn = PTR_ERR(path->pathname);
+ goto done;
+ }
+
+ strscpy(path->pathname, p, size);
+ strcat(path->pathname, ":/");
+ __putname(pathbuffer);
+ } else {
+ p = "nodev:/";
+ size = strlen(p) + 1;
+
+ path->pathname = kmalloc(size, GFP_KERNEL);
+ if (IS_ERR(path->pathname)) {
+ retn = PTR_ERR(path->pathname);
+ goto done;
+ }
+
+ strscpy(path->pathname, p, size);
+ }
+ }
+
+ done:
+ return retn;
+}
+
+static char *get_path(const struct path *path)
+{
+ int retn = 0;
+ const char *pathname = NULL;
+ char *retpath, *pathbuffer = NULL;
+
+ pathbuffer = __getname();
+ if (pathbuffer) {
+ pathname = d_absolute_path(path, pathbuffer, PATH_MAX);
+ if (IS_ERR(pathname)) {
+ __putname(pathbuffer);
+ pathbuffer = NULL;
+ pathname = NULL;
+ }
+ }
+
+ if (pathname)
+ retpath = kstrdup(pathname, GFP_KERNEL);
+ else
+ retpath = kstrdup(path->dentry->d_name.name, GFP_KERNEL);
+ if (!retpath)
+ retn = -ENOMEM;
+
+ if (pathbuffer)
+ __putname(pathbuffer);
+ if (retn)
+ retpath = ERR_PTR(retn);
+ return retpath;
+}
+
+static char *get_path_dentry(const struct dentry *dentry)
+{
+ int retn = 0;
+ const char *pathname = NULL;
+ char *retpath, *pathbuffer = NULL;
+
+ pathbuffer = __getname();
+ if (pathbuffer) {
+ pathname = dentry_path_raw(dentry, pathbuffer, PATH_MAX);
+ if (IS_ERR(pathname)) {
+ __putname(pathbuffer);
+ pathbuffer = NULL;
+ pathname = NULL;
+ }
+ }
+
+ if (pathname)
+ retpath = kstrdup(pathname, GFP_KERNEL);
+ else
+ retpath = kstrdup(dentry->d_name.name, GFP_KERNEL);
+ if (!retpath)
+ retn = -ENOMEM;
+
+ if (pathbuffer)
+ __putname(pathbuffer);
+ if (retn)
+ retpath = ERR_PTR(retn);
+ return retpath;
+}
+
+static void _fill_mount_path(struct tsem_inode *tsip, struct tsem_path *path)
+{
+ struct tsem_inode_instance *entry, *retn = NULL;
+
+ mutex_lock(&tsem_model(current)->mount_mutex);
+ list_for_each_entry(entry, &tsem_model(current)->mount_list, list) {
+ if (!strcmp(entry->pathname, path->pathname)) {
+ retn = entry;
+ break;
+ }
+ }
+
+ if (retn) {
+ path->created = true;
+ path->creator = retn->creator;
+ path->instance = retn->instance;
+ memcpy(path->owner, retn->owner, tsem_digestsize());
+ }
+ mutex_unlock(&tsem_model(current)->mount_mutex);
+}
+
+static int fill_path(const struct path *in, struct tsem_path *path)
+{
+ int retn;
+ struct tsem_model *model = tsem_model(current);
+ struct tsem_inode *tsip = tsem_inode(d_backing_inode(in->dentry));
+
+ if (created_inode(tsip)) {
+ path->created = tsip->created;
+ path->creator = tsip->creator;
+ path->instance = tsip->instance;
+ memcpy(path->owner, tsip->owner, tsem_digestsize());
+ }
+
+ retn = get_root(in->dentry, path);
+ if (retn)
+ goto done;
+
+ if (path->dev) {
+ path->pathname = get_path(in);
+ if (IS_ERR(path->pathname))
+ retn = PTR_ERR(path->pathname);
+ }
+
+ if (model && !list_empty(&model->mount_list))
+ _fill_mount_path(tsip, path);
+
+ done:
+ return retn;
+}
+
+static int fill_path_dentry(struct dentry *dentry, struct tsem_path *path)
+{
+ int retn;
+
+ retn = get_root(dentry, path);
+ if (retn)
+ goto done;
+
+ path->pathname = get_path_dentry(dentry);
+ if (IS_ERR(path->pathname))
+ retn = PTR_ERR(path->pathname);
+
+ done:
+ return retn;
+}
+
+static struct tsem_inode_digest *find_digest(struct tsem_inode *tsip)
+{
+ struct tsem_inode_digest *digest;
+
+ list_for_each_entry(digest, &tsip->digest_list, list) {
+ if (!strcmp(digest->name, tsem_context(current)->digestname))
+ return digest;
+ }
+
+ return NULL;
+}
+
+static struct tsem_inode_digest *add_digest(struct tsem_context *ctx,
+ struct tsem_inode *tsip)
+{
+ struct tsem_inode_digest *digest;
+
+ digest = kzalloc(sizeof(*digest), GFP_KERNEL);
+ if (!digest)
+ return NULL;
+
+ digest->name = kstrdup(tsem_context(current)->digestname, GFP_KERNEL);
+ if (!digest->name)
+ return NULL;
+
+ list_add(&digest->list, &tsip->digest_list);
+
+ return digest;
+}
+
+static struct file *open_event_file(struct file *file, unsigned int *status)
+{
+ int flags;
+ struct file *alt_file;
+
+ if (!(file->f_mode & FMODE_CAN_READ)) {
+ file->f_mode |= FMODE_CAN_READ;
+ *status |= 4;
+ }
+ if (file->f_mode & FMODE_READ)
+ return file;
+
+ flags = file->f_flags & ~(O_WRONLY | O_APPEND | O_TRUNC | O_CREAT |
+ O_NOCTTY | O_EXCL);
+ flags |= O_RDONLY;
+
+ alt_file = dentry_open(&file->f_path, flags, file->f_cred);
+ if (!IS_ERR(alt_file)) {
+ *status |= 1;
+ return alt_file;
+ }
+
+ file->f_flags |= FMODE_READ;
+ *status |= 2;
+ return file;
+}
+
+static int get_file_digest(struct file *file, struct inode *inode,
+ loff_t size, u8 *digest)
+{
+ u8 *bufr;
+ int retn = 0, rsize;
+ unsigned int open_status = 0;
+ loff_t posn = 0;
+ struct file *read_file;
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ shash->tfm = tsem_digest();
+ retn = crypto_shash_init(shash);
+ if (retn)
+ goto done;
+
+ bufr = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!bufr) {
+ retn = -ENOMEM;
+ goto done;
+ }
+
+ if (!likely(file->f_op->read || file->f_op->read_iter)) {
+ retn = -EINVAL;
+ goto done;
+ }
+ read_file = open_event_file(file, &open_status);
+
+ while (posn < size) {
+ rsize = integrity_kernel_read(read_file, posn, bufr, 4096);
+ if (rsize < 0) {
+ retn = rsize;
+ break;
+ }
+ if (rsize == 0)
+ break;
+
+ posn += rsize;
+ retn = crypto_shash_update(shash, bufr, rsize);
+ if (retn)
+ break;
+ }
+
+ kfree(bufr);
+ if (!retn)
+ retn = crypto_shash_final(shash, digest);
+
+ done:
+ if (open_status & 1)
+ fput(read_file);
+ if (open_status & 2)
+ file->f_flags &= ~FMODE_READ;
+ if (open_status & 4)
+ file->f_flags &= ~FMODE_CAN_READ;
+ return retn;
+}
+
+static int add_file_digest(struct file *file, bool pseudo_file,
+ struct tsem_file_args *args)
+{
+ int retn = 0;
+ u8 measurement[HASH_MAX_DIGESTSIZE];
+ loff_t size;
+ struct inode *inode;
+ struct tsem_inode *tsip;
+ struct tsem_inode_digest *digest;
+ struct tsem_context *ctx = tsem_context(current);
+
+ inode = file_inode(file);
+ tsip = tsem_inode(inode);
+
+ if (created_inode(tsip) || pseudo_file || !S_ISREG(inode->i_mode)) {
+ memcpy(args->out.digest, ctx->zero_digest, tsem_digestsize());
+ return 0;
+ }
+
+ mutex_lock(&tsip->digest_mutex);
+ if (!ctx->external) {
+ retn = tsem_model_has_pseudonym(tsip, args->out.path.pathname);
+ if (retn < 0)
+ goto done;
+ if (retn) {
+ memcpy(args->out.digest, ctx->zero_digest,
+ tsem_digestsize());
+ retn = 0;
+ goto done;
+ }
+ }
+
+ size = i_size_read(inode);
+ if (!size) {
+ memcpy(args->out.digest, ctx->zero_digest, tsem_digestsize());
+ goto done;
+ }
+
+ digest = find_digest(tsip);
+
+ if (digest && inode_eq_iversion(inode, digest->version) &&
+ tsip->status == TSEM_INODE_COLLECTED) {
+ memcpy(args->out.digest, digest->value, tsem_digestsize());
+ goto done;
+ }
+
+ tsip->status = TSEM_INODE_COLLECTING;
+ retn = get_file_digest(file, inode, size, measurement);
+ if (retn) {
+ tsip->status = 0;
+ goto done;
+ }
+
+ if (!digest) {
+ digest = add_digest(ctx, tsip);
+ if (!digest) {
+ retn = -ENOMEM;
+ goto done;
+ }
+ }
+
+ memcpy(args->out.digest, measurement, tsem_digestsize());
+ memcpy(digest->value, measurement, tsem_digestsize());
+ digest->version = inode_query_iversion(inode);
+ tsip->status = TSEM_INODE_COLLECTED;
+
+ done:
+ mutex_unlock(&tsip->digest_mutex);
+ return retn;
+}
+
+static void fill_inode(struct inode *inode, struct tsem_inode_cell *ip)
+{
+ struct user_namespace *ns;
+
+ if (tsem_context(current)->use_current_ns)
+ ns = current_user_ns();
+ else
+ ns = &init_user_ns;
+
+ ip->uid = from_kuid(ns, inode->i_uid);
+ ip->gid = from_kgid(ns, inode->i_gid);
+ ip->mode = inode->i_mode;
+ ip->s_magic = inode->i_sb->s_magic;
+ memcpy(ip->s_id, inode->i_sb->s_id, sizeof(ip->s_id));
+ memcpy(ip->s_uuid, inode->i_sb->s_uuid.b, sizeof(ip->s_uuid));
+}
+
+static void fill_creds(const struct cred *cp, struct tsem_COE *tcp)
+{
+ struct user_namespace *ns;
+
+ if (tsem_context(current)->use_current_ns)
+ ns = current_user_ns();
+ else
+ ns = &init_user_ns;
+
+ tcp->uid = from_kuid(ns, cp->uid);
+ tcp->euid = from_kuid(ns, cp->euid);
+ tcp->suid = from_kuid(ns, cp->suid);
+
+ tcp->gid = from_kgid(ns, cp->gid);
+ tcp->egid = from_kgid(ns, cp->egid);
+ tcp->sgid = from_kgid(ns, cp->sgid);
+
+ tcp->fsuid = from_kuid(ns, cp->fsuid);
+ tcp->fsgid = from_kgid(ns, cp->fsgid);
+
+ tcp->capeff.mask = cp->cap_effective;
+
+ tcp->securebits = cp->securebits;
+}
+
+static int fill_dentry(struct dentry *dp, struct tsem_dentry *dentry)
+{
+ int retn;
+ struct tsem_inode *tsip;
+
+ retn = fill_path_dentry(dp, &dentry->path);
+ if (retn)
+ return retn;
+
+ if (d_backing_inode(dp)) {
+ dentry->have_inode = true;
+ fill_inode(d_backing_inode(dp), &dentry->inode);
+
+ tsip = tsem_inode(d_backing_inode(dp));
+ dentry->path.created = tsip->created;
+ dentry->path.creator = tsip->creator;
+ dentry->path.instance = tsip->instance;
+ memcpy(dentry->path.owner, tsem_task(current)->task_id,
+ tsem_digestsize());
+ }
+
+ return 0;
+}
+
+static int fill_dentry_path(const struct path *path,
+ struct tsem_dentry *dentry)
+{
+ int retn;
+ struct tsem_inode *tsip;
+
+ retn = fill_path(path, &dentry->path);
+ if (retn)
+ return retn;
+
+ if (d_backing_inode(path->dentry)) {
+ dentry->have_inode = true;
+ fill_inode(d_backing_inode(path->dentry), &dentry->inode);
+
+ tsip = tsem_inode(d_backing_inode(path->dentry));
+ dentry->path.created = tsip->created;
+ dentry->path.creator = tsip->creator;
+ dentry->path.instance = tsip->instance;
+ memcpy(dentry->path.owner, tsem_task(current)->task_id,
+ tsem_digestsize());
+ }
+
+ return 0;
+}
+
+static u64 _get_task_inode_instance(struct tsem_inode *tsip)
+{
+ u64 instance = 0;
+ u8 *task_id = tsem_task(current)->task_id;
+ struct tsem_inode_instance *entry, *owner = NULL;
+
+ mutex_lock(&tsip->instance_mutex);
+ list_for_each_entry(entry, &tsip->instance_list, list) {
+ if (entry->creator == tsem_context(current)->id &&
+ !memcmp(entry->owner, task_id, tsem_digestsize())) {
+ owner = entry;
+ break;
+ }
+ }
+
+ if (owner)
+ instance = ++owner->instance;
+ else {
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (entry) {
+ instance = 1;
+ entry->instance = instance;
+ entry->creator = tsem_context(current)->id;
+ memcpy(entry->owner, task_id, tsem_digestsize());
+ list_add_tail(&entry->list, &tsip->instance_list);
+ }
+ }
+
+ mutex_unlock(&tsip->instance_mutex);
+ return instance;
+}
+
+static int get_inode(struct tsem_inode_args *args)
+{
+ int retn;
+ struct inode *dir = args->in.dir;
+ struct dentry *dentry = args->in.dentry;
+ struct tsem_inode *tsip = tsem_inode(d_backing_inode(dentry));
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ retn = fill_dentry(dentry, &args->out.dentry);
+ if (retn)
+ return retn;
+ args->out.dentry.path.created = tsip->created;
+ args->out.dentry.path.creator = tsip->creator;
+ args->out.dentry.path.instance = tsip->instance;
+ memcpy(args->out.dentry.path.owner, tsip->owner, tsem_digestsize());
+
+ fill_inode(dir, &args->out.dir);
+
+ return retn;
+}
+
+static int get_inode_create(struct tsem_inode_args *args)
+{
+ int retn;
+ u64 instance;
+ struct inode *dir = args->in.dir;
+ struct dentry *dentry = args->in.dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ instance = _get_task_inode_instance(tsem_inode(dir));
+ if (!instance)
+ return -ENOMEM;
+
+ retn = fill_dentry(dentry, &args->out.dentry);
+ if (retn)
+ return retn;
+
+ args->out.dentry.path.created = true;
+ args->out.dentry.path.creator = tsem_context(current)->id;
+ args->out.dentry.path.instance = instance;
+ memcpy(args->out.dentry.path.owner, tsem_task(current)->task_id,
+ tsem_digestsize());
+
+ fill_inode(dir, &args->out.dir);
+
+ retn = register_inode_create(dir, instance,
+ args->out.dentry.path.pathname);
+ if (retn)
+ kfree(args->out.dentry.path.pathname);
+
+ return retn;
+}
+
+static int get_inode_link(struct tsem_inode_args *args)
+{
+ int retn;
+ struct inode *dir = args->in.dir;
+ struct dentry *dentry = args->in.dentry;
+ struct dentry *new_dentry = args->in.new_dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ retn = fill_dentry(dentry, &args->out.dentry);
+ if (retn)
+ goto done;
+
+ retn = fill_dentry(new_dentry, &args->out.new_dentry);
+ if (retn)
+ goto done;
+
+ fill_inode(dir, &args->out.dir);
+
+ done:
+ if (retn) {
+ kfree(args->out.dentry.path.pathname);
+ kfree(args->out.new_dentry.path.pathname);
+ }
+
+ return retn;
+}
+
+static int get_inode_symlink(struct tsem_inode_args *args)
+{
+ int retn;
+ const char *old_name = args->in.old_name;
+ struct inode *dir = args->in.dir;
+ struct dentry *dentry = args->in.dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ retn = fill_dentry(dentry, &args->out.dentry);
+ if (retn)
+ return retn;
+
+ fill_inode(dir, &args->out.dir);
+
+ args->out.old_name = kstrdup(old_name, GFP_KERNEL);
+ if (!args->out.old_name) {
+ kfree(args->out.dentry.path.pathname);
+ retn = -ENOMEM;
+ }
+
+ return retn;
+}
+
+static int get_inode_mknod(struct tsem_inode_args *args)
+{
+ int retn;
+ struct inode *dir = args->in.dir;
+ struct dentry *dentry = args->in.dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ retn = fill_dentry(dentry, &args->out.dentry);
+ if (retn)
+ return retn;
+
+ fill_inode(dir, &args->out.dir);
+
+ return 0;
+}
+
+static int get_inode_rename(struct tsem_inode_args *args)
+{
+ int retn;
+ struct inode *old_dir = args->in.dir;
+ struct inode *new_dir = args->in.new_dir;
+ struct dentry *dentry = args->in.dentry;
+ struct dentry *new_dentry = args->in.new_dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ retn = fill_dentry(dentry, &args->out.dentry);
+ if (retn)
+ return retn;
+
+ retn = fill_dentry(new_dentry, &args->out.new_dentry);
+ if (retn) {
+ kfree(args->out.dentry.path.pathname);
+ return retn;
+ }
+
+ fill_inode(old_dir, &args->out.dir);
+ fill_inode(new_dir, &args->out.new_dir);
+
+ return 0;
+}
+
+static int get_inode_killpriv(struct tsem_inode_args *args)
+{
+ struct dentry *dentry = args->in.dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ return fill_dentry(dentry, &args->out.dentry);
+}
+
+static int get_file_cell(struct tsem_file_args *args)
+{
+ int retn = 1;
+ bool pseudo_file = args->in.pseudo_file;
+ struct file *file = args->in.file;
+ struct inode *inode = file_inode(file);
+
+ memset(&args->out, '\0', sizeof(args->out));
+ inode_lock(inode);
+
+ retn = fill_path(&file->f_path, &args->out.path);
+ if (retn)
+ goto done;
+
+ args->out.flags = file->f_flags;
+ fill_inode(inode, &args->out.inode);
+
+ retn = add_file_digest(file, pseudo_file, args);
+ if (retn)
+ kfree(args->out.path.pathname);
+
+ done:
+ inode_unlock(inode);
+ return retn;
+}
+
+static void get_prlimit(struct tsem_task_prlimit_args *args)
+{
+ const struct cred *cred = args->in.cred;
+ const struct cred *tcred = args->in.tcred;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ fill_creds(cred, &args->out.cred);
+ fill_creds(tcred, &args->out.tcred);
+}
+
+static void get_prctl(struct tsem_task_prctl_args *args)
+{
+ if (args->option == PR_SET_NAME || args->option == PR_GET_NAME)
+ args->arg2 = 0;
+}
+
+static void get_socket(struct sock *sock, struct tsem_socket *args)
+{
+ args->family = sock->sk_family;
+ args->type = sock->sk_type;
+ args->protocol = sock->sk_protocol;
+ args->kern = sock->sk_kern_sock;
+ memcpy(args->owner, tsem_inode(SOCK_INODE(sock->sk_socket))->owner,
+ tsem_digestsize());
+}
+
+static int get_socket_address(struct tsem_socket_args *args,
+ struct sockaddr *addr)
+{
+ u8 *p;
+ int retn, size;
+ SHASH_DESC_ON_STACK(shash, tfm);
+
+ shash->tfm = tsem_digest();
+ retn = crypto_shash_init(shash);
+ if (retn)
+ goto done;
+
+ p = (u8 *) addr->sa_data;
+ size = args->value - offsetof(struct sockaddr, sa_data);
+ retn = crypto_shash_digest(shash, p, size, args->out.mapping);
+
+ done:
+ return retn;
+}
+
+static int get_socket_cell(struct tsem_socket_args *args)
+
+{
+ int size, retn = 0;
+ struct sock *socka = args->in.socka;
+ struct sockaddr *addr = args->in.addr;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ get_socket(socka, &args->out.socka);
+
+ switch (args->out.socka.family) {
+ case AF_INET:
+ memcpy(&args->out.ipv4, addr, sizeof(args->out.ipv4));
+ break;
+ case AF_INET6:
+ memcpy(&args->out.ipv6, addr, sizeof(args->out.ipv6));
+ break;
+ case AF_UNIX:
+ memset(args->out.path, '\0', sizeof(args->out.path));
+ size = args->value - offsetof(struct sockaddr_un, sun_path);
+ strscpy(args->out.path, addr->sa_data, size);
+ break;
+ default:
+ retn = get_socket_address(args, addr);
+ break;
+ }
+
+ return retn;
+}
+
+static void get_socket_accept(struct tsem_socket_args *args)
+{
+ char *p;
+ int size;
+ const struct in6_addr *ipv6;
+ struct sock *socka = args->in.socka;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ get_socket(socka, &args->out.socka);
+
+ switch (args->out.socka.family) {
+ case AF_INET:
+ args->out.ipv4.sin_port = socka->sk_num;
+ args->out.ipv4.sin_addr.s_addr = socka->sk_rcv_saddr;
+ break;
+
+ case AF_INET6:
+ ipv6 = inet6_rcv_saddr(socka);
+ if (ipv6) {
+ args->out.ipv6.sin6_port = socka->sk_num;
+ args->out.ipv6.sin6_addr = *ipv6;
+ }
+ break;
+
+ case AF_UNIX:
+ memset(args->out.path, '\0', sizeof(args->out.path));
+ p = unix_sk(socka)->addr->name->sun_path;
+ size = unix_sk(socka)->addr->len -
+ offsetof(struct sockaddr_un, sun_path);
+ strscpy(args->out.path, p, size);
+ break;
+
+ default:
+ memcpy(args->out.mapping, tsem_context(current)->zero_digest,
+ tsem_digestsize());
+ break;
+ }
+}
+
+static void get_socket_pair(struct tsem_socket_args *args)
+{
+ struct sock *socka = args->in.socka;
+ struct sock *sockb = args->in.sockb;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ get_socket(socka, &args->out.socka);
+ get_socket(sockb, &args->out.sockb);
+}
+
+static void get_socket_msg(struct tsem_socket_args *args)
+
+{
+ struct sock *socka = args->in.socka;
+ void *addr = args->in.addr;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ get_socket(socka, &args->out.socka);
+ if (addr) {
+ if (args->out.socka.family == AF_INET) {
+ args->out.have_addr = true;
+ args->out.ipv4 = *(struct sockaddr_in *) addr;
+ }
+ if (args->out.socka.family == AF_INET6) {
+ args->out.have_addr = true;
+ args->out.ipv6 = *(struct sockaddr_in6 *) addr;
+ }
+ }
+}
+
+static void get_socket_argument(struct tsem_socket_args *args)
+
+{
+ struct sock *socka = args->in.socka;
+
+ memset(&args->out, '\0', sizeof(args->out));
+ get_socket(socka, &args->out.socka);
+}
+
+static int get_inode_getattr(struct tsem_inode_attr_args *args)
+{
+ const struct path *path = args->in.path;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ return fill_dentry_path(path, &args->out.dentry);
+}
+
+static int get_inode_setattr(struct tsem_inode_attr_args *args)
+{
+ int retn;
+ struct user_namespace *ns;
+ struct dentry *dentry = args->in.dentry;
+ struct iattr *iattr = args->in.iattr;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ retn = fill_dentry(dentry, &args->out.dentry);
+ if (retn)
+ goto done;
+
+ if (tsem_context(current)->use_current_ns)
+ ns = current_user_ns();
+ else
+ ns = &init_user_ns;
+
+ args->out.valid = iattr->ia_valid;
+ if (args->out.valid & ATTR_MODE)
+ args->out.mode = iattr->ia_mode;
+ if (args->out.valid & ATTR_UID)
+ args->out.uid = from_kuid(ns, iattr->ia_uid);
+ if (args->out.valid & ATTR_GID)
+ args->out.gid = from_kgid(ns, iattr->ia_gid);
+ if (args->out.valid & ATTR_SIZE)
+ args->out.size = iattr->ia_size;
+
+ done:
+ return retn;
+}
+
+static int get_inode_setxattr(struct tsem_inode_xattr_args *args)
+{
+ int retn;
+ const char *name = args->in.name;
+ struct dentry *dentry = args->in.dentry;
+ const void *value = args->in.value;
+ size_t size = args->in.size;
+ int flags = args->in.flags;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ args->out.size = size;
+ args->out.flags = flags;
+
+ retn = fill_dentry(dentry, &args->out.dentry);
+ if (retn)
+ return retn;
+
+ args->out.name = kstrdup(name, GFP_KERNEL);
+ if (!args->out.name) {
+ retn = -ENOMEM;
+ goto done;
+ }
+
+ args->out.value = kmalloc(size, GFP_KERNEL);
+ if (!args->out.value)
+ retn = -ENOMEM;
+ memcpy(args->out.value, value, size);
+
+ args->out.encoded_value = kzalloc(BASE64_CHARS(size) + 1, GFP_KERNEL);
+ if (!args->out.encoded_value) {
+ retn = -ENOMEM;
+ goto done;
+ }
+ base64_encode(args->out.value, size, args->out.encoded_value);
+
+ done:
+ if (retn) {
+ kfree(args->out.name);
+ kfree(args->out.value);
+ kfree(args->out.encoded_value);
+ }
+ return retn;
+}
+
+static int get_inode_getxattr(struct tsem_inode_xattr_args *args)
+{
+ int retn;
+ const char *name = args->in.name;
+ struct dentry *dentry = args->in.dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ retn = fill_dentry(dentry, &args->out.dentry);
+ if (retn)
+ return retn;
+
+ args->out.name = kstrdup(name, GFP_KERNEL);
+ if (!args->out.name)
+ retn = -ENOMEM;
+
+ if (retn)
+ kfree(args->out.name);
+ return retn;
+}
+
+static int get_inode_listxattr(struct tsem_inode_xattr_args *args)
+{
+ struct dentry *dentry = args->in.dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ return fill_dentry(dentry, &args->out.dentry);
+}
+
+static int get_kernel_module(struct tsem_kernel_args *args)
+{
+ int retn = 0;
+ char *kmod_name = args->in.kmod_name;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ args->out.kmod_name = kstrdup(kmod_name, GFP_KERNEL);
+ if (!args->out.kmod_name)
+ retn = -ENOMEM;
+
+ args->out.kmod_name = kstrdup(kmod_name, GFP_KERNEL);
+ if (!args->out.kmod_name)
+ retn = -ENOMEM;
+
+ return retn;
+}
+
+static int get_kernel_file(struct tsem_kernel_args *args)
+{
+ int retn = 0;
+ struct file *file = args->in.file;
+ struct inode *inode = file_inode(file);
+
+ memset(&args->out, '\0', sizeof(args->out));
+ inode_lock(inode);
+
+ retn = fill_path(&file->f_path, &args->out.file.out.path);
+ if (retn)
+ goto done;
+
+ args->out.file.out.flags = file->f_flags;
+ fill_inode(inode, &args->out.file.out.inode);
+
+ if (!S_ISREG(inode->i_mode))
+ goto done;
+
+ retn = add_file_digest(file, false, &args->out.file);
+ if (retn)
+ kfree(args->out.file.out.path.pathname);
+
+ done:
+ inode_unlock(inode);
+ return retn;
+}
+
+static int get_sb_mount(struct tsem_sb_args *args)
+{
+ int retn = -ENOMEM;
+ struct tsem_model *model = tsem_model(current);
+ struct tsem_inode_instance *tsio = NULL;
+ const char *dev_name = args->in.dev_name;
+ const char *type = args->in.type;
+ const struct path *path = args->in.path;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ if (dev_name) {
+ args->out.dev_name = kstrdup(dev_name, GFP_KERNEL);
+ if (!args->out.dev_name)
+ goto done;
+ }
+
+ if (type) {
+ args->out.type = kstrdup(type, GFP_KERNEL);
+ if (!args->out.type)
+ goto done;
+ }
+
+ retn = fill_path(path, &args->out.path);
+ if (retn)
+ goto done;
+
+ if (model && args->out.path.created) {
+ tsio = kzalloc(sizeof(*tsio), GFP_KERNEL);
+ if (!tsio) {
+ retn = -ENOMEM;
+ goto done;
+ }
+
+ tsio->creator = args->out.path.creator;
+ tsio->instance = args->out.path.instance;
+ tsio->pathname = args->out.path.pathname;
+ memcpy(tsio->owner, args->out.path.owner, tsem_digestsize());
+
+ mutex_lock(&model->mount_mutex);
+ list_add_tail(&tsio->list, &model->mount_list);
+ mutex_unlock(&model->mount_mutex);
+ }
+
+ done:
+ if (retn) {
+ kfree(args->out.dev_name);
+ kfree(args->out.type);
+ kfree(tsio);
+ }
+ return retn;
+}
+
+static int get_sb_umount(struct tsem_sb_args *args)
+{
+ struct dentry *dentry = args->in.dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ return fill_dentry(dentry, &args->out.dentry);
+}
+
+static int get_sb_remount(struct tsem_sb_args *args)
+{
+ int retn = -ENOMEM;
+ struct super_block *sb = args->in.sb;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ args->flags = sb->s_flags;
+
+ args->out.type = kstrdup(sb->s_type->name, GFP_KERNEL);
+ if (!args->out.type)
+ goto done;
+
+ retn = fill_dentry(sb->s_root, &args->out.dentry);
+
+ done:
+ if (retn) {
+ kfree(args->out.type);
+ kfree(args->out.dentry.path.pathname);
+ }
+
+ return retn;
+}
+
+static void get_netlink(struct tsem_netlink_args *args)
+{
+ struct sock *sock = args->in.sock;
+ struct netlink_skb_parms *np = args->in.parms;
+ struct user_namespace *ns;
+
+ memset(&args->out, '\0', sizeof(args->out));
+ get_socket(sock, &args->out.sock);
+
+ if (tsem_context(current)->use_current_ns)
+ ns = current_user_ns();
+ else
+ ns = &init_user_ns;
+ args->out.uid = from_kuid(ns, np->creds.uid);
+ args->out.gid = from_kgid(ns, np->creds.gid);
+
+ args->out.portid = np->portid;
+ args->out.dst_group = np->dst_group;
+ args->out.flags = np->flags;
+ args->out.nsid_set = np->nsid_is_set;
+ args->out.nsid = np->nsid;
+}
+
+static void get_ipc_cred(struct kern_ipc_perm *perm, struct tsem_ipc_perm *tp)
+{
+ struct user_namespace *ns;
+
+ if (tsem_context(current)->use_current_ns)
+ ns = current_user_ns();
+ else
+ ns = &init_user_ns;
+
+ tp->uid = from_kuid(ns, perm->uid);
+ tp->gid = from_kgid(ns, perm->gid);
+ tp->cuid = from_kuid(ns, perm->cuid);
+ tp->cgid = from_kgid(ns, perm->cgid);
+ tp->mode = perm->mode;
+}
+
+static void get_ipc_permission(struct tsem_ipc_args *args)
+{
+ struct kern_ipc_perm *perm = args->in.perm;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ get_ipc_cred(perm, &args->out.perm);
+ memcpy(args->out.owner, tsem_ipc(perm)->owner, tsem_digestsize());
+}
+
+static void get_msg_queue_msgrcv(struct tsem_ipc_args *args)
+{
+ struct kern_ipc_perm *perm = args->in.perm;
+ struct tsem_task *target = tsem_task(args->in.target);
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ get_ipc_cred(perm, &args->out.perm);
+ memcpy(args->out.owner, tsem_ipc(perm)->owner, tsem_digestsize());
+ memcpy(args->out.target, target->task_id, tsem_digestsize());
+}
+
+static void get_key_alloc(struct tsem_key_args *args)
+{
+ const struct cred *cred = args->in.cred;
+
+ memset(&args->out, '\0', sizeof(args->out));
+ fill_creds(cred, &args->out.cred);
+}
+
+static void get_key_perm(struct tsem_key_args *args)
+{
+ const struct cred *cred = args->in.cred;
+ struct user_namespace *ns;
+ const key_ref_t ref = args->in.ref;
+ struct key *key = key_ref_to_ptr(ref);
+
+ memset(&args->out, '\0', sizeof(args->out));
+ fill_creds(cred, &args->out.cred);
+
+ args->out.possessed = is_key_possessed(ref);
+
+ if (tsem_context(current)->use_current_ns)
+ ns = current_user_ns();
+ else
+ ns = &init_user_ns;
+ args->out.uid = from_kuid(ns, key->uid);
+ args->out.gid = from_kgid(ns, key->gid);
+
+ args->out.perm = key->perm;
+ args->out.flags = key->flags;
+}
+
+static int get_sb_statfs(struct tsem_sb_args *args)
+{
+ struct dentry *dentry = args->in.dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ return fill_dentry(dentry, &args->out.dentry);
+}
+
+static int get_move_mount(struct tsem_sb_args *args)
+{
+ int retn;
+ const struct path *old_path = args->in.path;
+ const struct path *new_path = args->in.path2;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ retn = fill_path(old_path, &args->out.path);
+ if (!retn)
+ retn = fill_path(new_path, &args->out.path2);
+
+ return retn;
+}
+
+static int get_quotactl(struct tsem_quota_args *args)
+{
+ int retn;
+ const struct super_block *sb = args->in.sb;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ args->out.s_flags = sb->s_flags;
+
+ args->out.fstype = kstrdup(sb->s_type->name, GFP_KERNEL);
+ if (!args->out.fstype)
+ goto done;
+
+ retn = fill_dentry(sb->s_root, &args->out.dentry);
+
+ done:
+ if (retn)
+ kfree(args->out.fstype);
+ return retn;
+}
+
+static int get_quotaon(struct tsem_quota_args *args)
+{
+ struct dentry *dentry = args->in.dentry;
+
+ memset(&args->out, '\0', sizeof(args->out));
+
+ return fill_dentry(dentry, &args->out.dentry);
+}
+
+static void event_free(struct tsem_event *ep)
+{
+ switch (ep->event) {
+ case TSEM_KERNEL_MODULE_REQUEST:
+ kfree(ep->CELL.kernel.out.kmod_name);
+ break;
+ case TSEM_KERNEL_READ_FILE:
+ kfree(ep->CELL.kernel.out.file.out.path.pathname);
+ break;
+ case TSEM_INODE_CREATE:
+ case TSEM_INODE_MKDIR:
+ case TSEM_INODE_RMDIR:
+ case TSEM_INODE_UNLINK:
+ case TSEM_INODE_MKNOD:
+ case TSEM_INODE_KILLPRIV:
+ kfree(ep->CELL.inode.out.dentry.path.pathname);
+ break;
+ case TSEM_INODE_LINK:
+ kfree(ep->CELL.inode.out.dentry.path.pathname);
+ kfree(ep->CELL.inode.out.new_dentry.path.pathname);
+ break;
+ case TSEM_INODE_SYMLINK:
+ kfree(ep->CELL.inode.out.old_name);
+ kfree(ep->CELL.inode.out.dentry.path.pathname);
+ break;
+ case TSEM_INODE_RENAME:
+ kfree(ep->CELL.inode.out.dentry.path.pathname);
+ kfree(ep->CELL.inode.out.new_dentry.path.pathname);
+ break;
+ case TSEM_FILE_OPEN:
+ case TSEM_BPRM_COMMITTED_CREDS:
+ case TSEM_FILE_IOCTL:
+ case TSEM_FILE_LOCK:
+ case TSEM_FILE_FCNTL:
+ case TSEM_FILE_RECEIVE:
+ kfree(ep->CELL.file.out.path.pathname);
+ break;
+ case TSEM_MMAP_FILE:
+ kfree(ep->CELL.mmap_file.file.out.path.pathname);
+ break;
+ case TSEM_INODE_GETATTR:
+ case TSEM_INODE_SETATTR:
+ kfree(ep->CELL.inode_attr.out.dentry.path.pathname);
+ break;
+ case TSEM_INODE_SETXATTR:
+ case TSEM_INODE_GETXATTR:
+ case TSEM_INODE_REMOVEXATTR:
+ case TSEM_INODE_LISTXATTR:
+ kfree(ep->CELL.inode_xattr.out.dentry.path.pathname);
+ kfree(ep->CELL.inode_xattr.out.name);
+ kfree(ep->CELL.inode_xattr.out.value);
+ kfree(ep->CELL.inode_xattr.out.encoded_value);
+ break;
+ case TSEM_SB_MOUNT:
+ kfree(ep->CELL.sb.out.dev_name);
+ kfree(ep->CELL.sb.out.type);
+ kfree(ep->CELL.sb.out.path.pathname);
+ break;
+ case TSEM_SB_UMOUNT:
+ kfree(ep->CELL.sb.out.dentry.path.pathname);
+ break;
+ case TSEM_SB_REMOUNT:
+ kfree(ep->CELL.sb.out.type);
+ kfree(ep->CELL.sb.out.dentry.path.pathname);
+ break;
+ case TSEM_SB_PIVOTROOT:
+ case TSEM_MOVE_MOUNT:
+ kfree(ep->CELL.sb.out.path.pathname);
+ kfree(ep->CELL.sb.out.path2.pathname);
+ break;
+ case TSEM_QUOTACTL:
+ kfree(ep->CELL.quota.out.fstype);
+ kfree(ep->CELL.quota.out.dentry.path.pathname);
+ break;
+ case TSEM_QUOTA_ON:
+ kfree(ep->CELL.quota.out.dentry.path.pathname);
+ break;
+ case TSEM_SB_STATFS:
+ kfree(ep->CELL.sb.out.dentry.path.pathname);
+ break;
+ default:
+ break;
+ }
+}
+
+static void get_COE(struct tsem_COE *COE)
+{
+ struct user_namespace *ns;
+
+ if (tsem_context(current)->use_current_ns)
+ ns = current_user_ns();
+ else
+ ns = &init_user_ns;
+
+ COE->uid = from_kuid(ns, current_uid());
+ COE->euid = from_kuid(ns, current_euid());
+ COE->suid = from_kuid(ns, current_suid());
+
+ COE->gid = from_kgid(ns, current_gid());
+ COE->egid = from_kgid(ns, current_egid());
+ COE->sgid = from_kgid(ns, current_sgid());
+
+ COE->fsuid = from_kuid(ns, current_fsuid());
+ COE->fsgid = from_kgid(ns, current_fsgid());
+
+ COE->capeff.mask = current_cred()->cap_effective;
+}
+
+/**
+ * tsem_event_generate() - Generate the default event description.
+ * @ep: A pointer to the structure that describes the event parameters.
+ *
+ * This function is responsible for initializing the data that describes
+ * the security event and is persisted for the lifetime that the event
+ * description is maintained.
+ *
+ * Return: In the event of an error this function returns an error code
+ * as a negative return value. A value of zero indicates that
+ * the event should be bypassed. A positive value indicates
+ * the event should be modeled.
+ */
+
+int tsem_event_generate(struct tsem_event *ep)
+{
+ int retn = 0;
+
+ switch (ep->event) {
+ case TSEM_NETLINK_SEND:
+ get_netlink(&ep->CELL.netlink);
+ break;
+ case TSEM_IPC_PERMISSION:
+ case TSEM_SHM_ASSOCIATE:
+ case TSEM_SHM_SHMCTL:
+ case TSEM_SHM_SHMAT:
+ case TSEM_SEM_ASSOCIATE:
+ case TSEM_SEM_SEMCTL:
+ case TSEM_SEM_SEMOP:
+ case TSEM_MSG_QUEUE_ASSOCIATE:
+ case TSEM_MSG_QUEUE_MSGCTL:
+ case TSEM_MSG_QUEUE_MSGSND:
+ get_ipc_permission(&ep->CELL.ipc);
+ break;
+ case TSEM_MSG_QUEUE_MSGRCV:
+ get_msg_queue_msgrcv(&ep->CELL.ipc);
+ break;
+ case TSEM_KEY_ALLOC:
+ get_key_alloc(&ep->CELL.key);
+ break;
+ case TSEM_KEY_PERMISSION:
+ get_key_perm(&ep->CELL.key);
+ break;
+ case TSEM_INODE_RMDIR:
+ case TSEM_INODE_UNLINK:
+ retn = get_inode(&ep->CELL.inode);
+ break;
+ case TSEM_INODE_CREATE:
+ case TSEM_INODE_MKDIR:
+ retn = get_inode_create(&ep->CELL.inode);
+ break;
+ case TSEM_INODE_LINK:
+ retn = get_inode_link(&ep->CELL.inode);
+ break;
+ case TSEM_INODE_SYMLINK:
+ retn = get_inode_symlink(&ep->CELL.inode);
+ break;
+ case TSEM_INODE_MKNOD:
+ retn = get_inode_mknod(&ep->CELL.inode);
+ break;
+ case TSEM_INODE_RENAME:
+ retn = get_inode_rename(&ep->CELL.inode);
+ break;
+ case TSEM_INODE_KILLPRIV:
+ retn = get_inode_killpriv(&ep->CELL.inode);
+ break;
+ case TSEM_FILE_OPEN:
+ case TSEM_BPRM_COMMITTED_CREDS:
+ case TSEM_FILE_IOCTL:
+ case TSEM_FILE_LOCK:
+ case TSEM_FILE_FCNTL:
+ case TSEM_FILE_RECEIVE:
+ retn = get_file_cell(&ep->CELL.file);
+ break;
+ case TSEM_MMAP_FILE:
+ if (!ep->CELL.mmap_file.anonymous)
+ retn = get_file_cell(&ep->CELL.mmap_file.file);
+ break;
+ case TSEM_TASK_PRLIMIT:
+ get_prlimit(&ep->CELL.task_prlimit);
+ break;
+ case TSEM_TASK_PRCTL:
+ get_prctl(&ep->CELL.task_prctl);
+ break;
+ case TSEM_UNIX_STREAM_CONNECT:
+ case TSEM_UNIX_MAY_SEND:
+ case TSEM_SOCKET_SOCKETPAIR:
+ get_socket_pair(&ep->CELL.socket);
+ break;
+ case TSEM_SOCKET_CONNECT:
+ case TSEM_SOCKET_BIND:
+ retn = get_socket_cell(&ep->CELL.socket);
+ break;
+ case TSEM_SOCKET_ACCEPT:
+ get_socket_accept(&ep->CELL.socket);
+ break;
+ case TSEM_SOCKET_LISTEN:
+ case TSEM_SOCKET_GETSOCKNAME:
+ case TSEM_SOCKET_GETPEERNAME:
+ case TSEM_SOCKET_SETSOCKOPT:
+ case TSEM_SOCKET_SHUTDOWN:
+ case TSEM_TUN_DEV_ATTACH_QUEUE:
+ get_socket_argument(&ep->CELL.socket);
+ break;
+ case TSEM_SOCKET_SENDMSG:
+ case TSEM_SOCKET_RECVMSG:
+ get_socket_msg(&ep->CELL.socket);
+ break;
+ case TSEM_INODE_GETATTR:
+ retn = get_inode_getattr(&ep->CELL.inode_attr);
+ break;
+ case TSEM_INODE_SETATTR:
+ retn = get_inode_setattr(&ep->CELL.inode_attr);
+ break;
+ case TSEM_INODE_SETXATTR:
+ retn = get_inode_setxattr(&ep->CELL.inode_xattr);
+ break;
+ case TSEM_INODE_GETXATTR:
+ case TSEM_INODE_REMOVEXATTR:
+ retn = get_inode_getxattr(&ep->CELL.inode_xattr);
+ break;
+ case TSEM_INODE_LISTXATTR:
+ retn = get_inode_listxattr(&ep->CELL.inode_xattr);
+ break;
+ case TSEM_KERNEL_MODULE_REQUEST:
+ retn = get_kernel_module(&ep->CELL.kernel);
+ break;
+ case TSEM_KERNEL_READ_FILE:
+ retn = get_kernel_file(&ep->CELL.kernel);
+ break;
+ case TSEM_SB_MOUNT:
+ retn = get_sb_mount(&ep->CELL.sb);
+ break;
+ case TSEM_SB_UMOUNT:
+ retn = get_sb_umount(&ep->CELL.sb);
+ break;
+ case TSEM_SB_REMOUNT:
+ retn = get_sb_remount(&ep->CELL.sb);
+ break;
+ case TSEM_SB_STATFS:
+ retn = get_sb_statfs(&ep->CELL.sb);
+ break;
+ case TSEM_SB_PIVOTROOT:
+ case TSEM_MOVE_MOUNT:
+ retn = get_move_mount(&ep->CELL.sb);
+ break;
+ case TSEM_QUOTACTL:
+ retn = get_quotactl(&ep->CELL.quota);
+ break;
+ case TSEM_QUOTA_ON:
+ retn = get_quotaon(&ep->CELL.quota);
+ break;
+ default:
+ break;
+ }
+
+ if (!retn) {
+ ep->event_free = event_free;
+ return 1;
+ }
+ return retn;
+}
+EXPORT_SYMBOL_GPL(tsem_event_generate);
+
+/**
+ * tsem_event_init() - Initialize a security event description structure.
+ * @ep: A pointer to the tsem_event structure that describes the
+ * security event.
+ *
+ * This function is responsible for initializing the tsem_event structure
+ * and populating it based on the event type.
+ *
+ * Return: In the event of an error this function returns an error code
+ * as a negative return value. A value of zero indicates that
+ * the event should be bypassed. A positive value indicates
+ * the event should be modeled.
+ */
+int tsem_event_init(struct tsem_event *ep)
+{
+ int retn = 1;
+ u64 timestamp = ktime_get_boottime_ns();
+ struct tsem_task *task = tsem_task(current);
+
+ ep->pid = task_pid_nr(current);
+ ep->context = tsem_context(current)->id;
+ ep->instance = task->instance;
+ ep->p_instance = task->p_instance;
+ ep->timestamp = timestamp - tsem_context(current)->timestamp;
+ memcpy(ep->comm, current->comm, sizeof(ep->comm));
+ memcpy(ep->task_id, task->task_id, tsem_digestsize());
+ memcpy(ep->p_task_id, task->p_task_id, tsem_digestsize());
+
+ get_COE(&ep->COE);
+
+ if (ep->event == TSEM_BPRM_COMMITTED_CREDS)
+ return tsem_event_generate(ep);
+
+ if (!ep->no_params)
+ retn = tsem_context(current)->ops->init(ep);
+
+ if (retn > 0)
+ ep->event_number = ++tsem_context(current)->event_number;
+ return retn;
+}
+
+/**
+ * tsem_free_event() - Free a security event description.
+ * @ep: A pointer to the security event description that is to be freed.
+ *
+ * This function is responsible for freeing the resources that were
+ * allocated by the tsem_event_allocate() function.
+ */
+static void tsem_event_free(struct kref *kref)
+{
+ struct tsem_event *ep;
+
+ ep = container_of(kref, struct tsem_event, kref);
+
+ if (ep->event_free)
+ ep->event_free(ep);
+ kmem_cache_free(event_cachep, ep);
+}
+
+/**
+ * tsem_event_put() - Release a referenceto a TSEM event description.
+ *
+ * This function is called each time the use of a TSEM event description
+ * is dropped.
+ */
+void tsem_event_put(struct tsem_event *ep)
+{
+ kref_put(&ep->kref, tsem_event_free);
+}
+
+static void refill_event_magazine(struct work_struct *work)
+{
+ unsigned int index;
+ struct tsem_event *ep;
+ struct tsem_work *ws;
+
+ ws = container_of(work, struct tsem_work, work);
+
+ ep = kmem_cache_zalloc(event_cachep, GFP_KERNEL);
+ if (!ep) {
+ pr_warn("tsem: Cannot refill event magazine.\n");
+ return;
+ }
+
+ spin_lock(&ws->u.ctx->magazine_lock);
+ ws->u.ctx->magazine[ws->index] = ep;
+ clear_bit(ws->index, ws->u.ctx->magazine_index);
+
+ /*
+ * The following memory barrier is used to cause the magazine
+ * index to be visible after the refill of the cache slot.
+ */
+ smp_mb__after_atomic();
+ spin_unlock(&ws->u.ctx->magazine_lock);
+
+ if (index >= ws->u.ctx->magazine_size) {
+ kmem_cache_free(event_cachep, ep);
+ WARN_ONCE(true, "Refilling event magazine with no slots.\n");
+ }
+}
+
+/**
+ * tsem_event_get() - Obtain a reference to a TSEM event description.
+ *
+ * This function is called on each invocation of the tsem_task_free
+ * function to release one of the references on the TMA modeling
+ * structure.
+ */
+void tsem_event_get(struct tsem_event *ep)
+{
+ kref_get(&ep->kref);
+}
+
+/**
+ * tsem_event_allocate() - Allocate a TSEM event description structure.
+ * @locked: A boolean flag used to indicate if the allocation is being
+ * done in atomic context and must be serviced from the
+ * pre-allocated event description structures.
+ *
+ * Return: This function returns a pointer to the allocated structure or
+ * a NULL pointer in the event of an allocation failure.
+ */
+struct tsem_event *tsem_event_allocate(enum tsem_event_type event, bool locked)
+{
+ unsigned int index;
+ struct tsem_event *ep = NULL;
+ struct tsem_context *ctx = tsem_context(current);
+
+ if (!locked) {
+ ep = kmem_cache_zalloc(event_cachep, GFP_KERNEL);
+ if (ep) {
+ ep->event = event;
+ kref_init(&ep->kref);
+ }
+ return ep;
+ }
+
+ spin_lock(&ctx->magazine_lock);
+ index = find_first_zero_bit(ctx->magazine_index, ctx->magazine_size);
+ if (index < ctx->magazine_size) {
+ ep = ctx->magazine[index];
+ ctx->ws[index].index = index;
+ ctx->ws[index].u.ctx = ctx;
+ set_bit(index, ctx->magazine_index);
+
+ /*
+ * Similar to the issue noted in the refill_event_magazine()
+ * function, this barrier is used to cause the consumption
+ * of the cache entry to become visible.
+
+ */
+ smp_mb__after_atomic();
+ }
+
+ spin_unlock(&ctx->magazine_lock);
+
+ if (ep) {
+ INIT_WORK(&ctx->ws[index].work, refill_event_magazine);
+ queue_work(system_highpri_wq, &ctx->ws[index].work);
+ ep->event = event;
+ ep->locked = true;
+ kref_init(&ep->kref);
+ return ep;
+ }
+
+ pr_warn("tsem: Fail event allocation comm %s ns %llu cs %u.\n",
+ current->comm, tsem_context(current)->id, ctx->magazine_size);
+ return NULL;
+}
+
+/**
+ * tsem event_magazine_allocate() - Allocate a TSEM event magazine.
+ * @ctx: A pointer to the modeling context that the magazine is
+ * to be allocated for.
+ * @size: The number of entries to be created in the magazine.
+
+ * The security modeling event magazine is an array of tsem_event
+ * structures that are used to service security hooks that are called
+ * in atomic context. Each modeling domain/namespace has a magazine
+ * allocated to it and this function allocates and initializes the
+ * memory structures needed to manage that magazine.
+
+ * Return: This function returns a value of zero on success and a negative
+ * error code on failure.
+ */
+int tsem_event_magazine_allocate(struct tsem_context *ctx, size_t size)
+{
+ unsigned int lp;
+ int retn = -ENOMEM;
+
+ ctx->magazine_size = size;
+
+ spin_lock_init(&ctx->magazine_lock);
+
+ ctx->magazine_index = bitmap_zalloc(ctx->magazine_size, GFP_KERNEL);
+ if (!ctx->magazine_index)
+ return retn;
+
+ ctx->magazine = kcalloc(ctx->magazine_size, sizeof(*ctx->magazine),
+ GFP_KERNEL);
+ if (!ctx->magazine)
+ goto done;
+
+ for (lp = 0; lp < ctx->magazine_size; ++lp) {
+ ctx->magazine[lp] = kmem_cache_zalloc(event_cachep,
+ GFP_KERNEL);
+ if (!ctx->magazine[lp])
+ goto done;
+ }
+
+ ctx->ws = kcalloc(ctx->magazine_size, sizeof(*ctx->ws), GFP_KERNEL);
+ if (ctx->ws)
+ retn = 0;
+
+ done:
+ if (retn)
+ tsem_event_magazine_free(ctx);
+
+ return retn;
+}
+
+/**
+ * tsem event_magazine_free() - Releases a TSEM event magazine.
+ * @ctx: A pointer to the modeling context whose magazine is to be
+ * released.
+ *
+ * The function is used to free the memory that was allocated by
+ * the tsem_event_magazine_allocate() function for a security
+ * modeling context.
+ */
+void tsem_event_magazine_free(struct tsem_context *ctx)
+{
+ unsigned int lp;
+
+ for (lp = 0; lp < ctx->magazine_size; ++lp)
+ kmem_cache_free(event_cachep, ctx->magazine[lp]);
+
+ bitmap_free(ctx->magazine_index);
+ kfree(ctx->ws);
+ kfree(ctx->magazine);
+}
+
+/**
+ * tsem event_cache_init() - Initialize the TSEM event cache.
+ *
+ * This function is called by the TSEM initialization function and sets
+ * up the cache that will be used to allocate tsem_event structures.
+ *
+ * Return: This function returns a value of zero on success and a negative
+ * error code on failure.
+ */
+int __init tsem_event_cache_init(void)
+{
+ event_cachep = kmem_cache_create("tsem_event_cache",
+ sizeof(struct tsem_event), 0,
+ SLAB_PANIC, 0);
+ if (!event_cachep)
+ return -ENOMEM;
+
+ return 0;
+}