]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - security/security.c
UBUNTU: Ubuntu-5.15.0-39.42
[mirror_ubuntu-jammy-kernel.git] / security / security.c
index 9ffa9e9c5c554a6939cf175560a617414ce38a00..8ecd012e0bffe0da5e0ccc344978c1de5556f1f6 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/string.h>
 #include <linux/msg.h>
 #include <net/flow.h>
+#include <net/sock.h>
 
 #define MAX_LSM_EVM_XATTR      2
 
@@ -59,6 +60,7 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
        [LOCKDOWN_DEBUGFS] = "debugfs access",
        [LOCKDOWN_XMON_WR] = "xmon write access",
        [LOCKDOWN_BPF_WRITE_USER] = "use of bpf to write user RAM",
+       [LOCKDOWN_KGDB] = "KDB or KGDB access",
        [LOCKDOWN_INTEGRITY_MAX] = "integrity",
        [LOCKDOWN_KCORE] = "/proc/kcore access",
        [LOCKDOWN_KPROBES] = "use of kprobes",
@@ -77,7 +79,14 @@ static struct kmem_cache *lsm_file_cache;
 static struct kmem_cache *lsm_inode_cache;
 
 char *lsm_names;
-static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init;
+
+/*
+ * The task blob includes the "display" slot used for
+ * chosing which module presents contexts.
+ */
+static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init = {
+       .lbs_task = sizeof(int),
+};
 
 /* Boot-time LSM user choice */
 static __initdata const char *chosen_lsm_order;
@@ -205,6 +214,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
        lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
        lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
        lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
+       lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
        lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
 }
 
@@ -341,7 +351,9 @@ static void __init ordered_lsm_init(void)
        init_debug("ipc blob size        = %d\n", blob_sizes.lbs_ipc);
        init_debug("msg_msg blob size    = %d\n", blob_sizes.lbs_msg_msg);
        init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
+       init_debug("sock blob size       = %d\n", blob_sizes.lbs_sock);
        init_debug("task blob size       = %d\n", blob_sizes.lbs_task);
+       init_debug("lsmblob size       = %zu\n", sizeof(struct lsmblob));
 
        /*
         * Create any kmem_caches needed for blobs
@@ -469,21 +481,63 @@ static int lsm_append(const char *new, char **result)
        return 0;
 }
 
+/*
+ * Current index to use while initializing the lsmblob secid list.
+ * Pointers to the LSM id structures for local use.
+ */
+static int lsm_slot __lsm_ro_after_init;
+static struct lsm_id *lsm_slotlist[LSMBLOB_ENTRIES] __lsm_ro_after_init;
+
+/**
+ * security_lsm_slot_name - Get the name of the security module in a slot
+ * @slot: index into the "display" slot list.
+ *
+ * Provide the name of the security module associated with
+ * a display slot.
+ *
+ * If @slot is LSMBLOB_INVALID return the value
+ * for slot 0 if it has been set, otherwise NULL.
+ *
+ * Returns a pointer to the name string or NULL.
+ */
+const char *security_lsm_slot_name(int slot)
+{
+       if (slot == LSMBLOB_INVALID)
+               slot = 0;
+       else if (slot >= LSMBLOB_ENTRIES || slot < 0)
+               return NULL;
+
+       if (lsm_slotlist[slot] == NULL)
+               return NULL;
+       return lsm_slotlist[slot]->lsm;
+}
+
 /**
  * security_add_hooks - Add a modules hooks to the hook lists.
  * @hooks: the hooks to add
  * @count: the number of hooks to add
- * @lsm: the name of the security module
+ * @lsmid: the the identification information for the security module
  *
  * Each LSM has to register its hooks with the infrastructure.
+ * If the LSM is using hooks that export secids allocate a slot
+ * for it in the lsmblob.
  */
 void __init security_add_hooks(struct security_hook_list *hooks, int count,
-                               char *lsm)
+                              struct lsm_id *lsmid)
 {
        int i;
 
+       if (lsmid->slot == LSMBLOB_NEEDED) {
+               if (lsm_slot >= LSMBLOB_ENTRIES)
+                       panic("%s Too many LSMs registered.\n", __func__);
+               lsm_slotlist[lsm_slot] = lsmid;
+               lsmid->slot = lsm_slot++;
+               init_debug("%s assigned lsmblob slot %d\n", lsmid->lsm,
+                          lsmid->slot);
+       }
+
        for (i = 0; i < count; i++) {
-               hooks[i].lsm = lsm;
+               hooks[i].lsmid = lsmid;
                hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);
        }
 
@@ -492,7 +546,7 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
         * and fix this up afterwards.
         */
        if (slab_is_available()) {
-               if (lsm_append(lsm, &lsm_names) < 0)
+               if (lsm_append(lsmid->lsm, &lsm_names) < 0)
                        panic("%s - Cannot get early memory.\n", __func__);
        }
 }
@@ -606,6 +660,8 @@ int lsm_inode_alloc(struct inode *inode)
  */
 static int lsm_task_alloc(struct task_struct *task)
 {
+       int *display;
+
        if (blob_sizes.lbs_task == 0) {
                task->security = NULL;
                return 0;
@@ -614,6 +670,15 @@ static int lsm_task_alloc(struct task_struct *task)
        task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL);
        if (task->security == NULL)
                return -ENOMEM;
+
+       /*
+        * The start of the task blob contains the "display" LSM slot number.
+        * Start with it set to the invalid slot number, indicating that the
+        * default first registered LSM be displayed.
+        */
+       display = task->security;
+       *display = LSMBLOB_INVALID;
+
        return 0;
 }
 
@@ -659,6 +724,28 @@ static int lsm_msg_msg_alloc(struct msg_msg *mp)
        return 0;
 }
 
+/**
+ * lsm_sock_alloc - allocate a composite sock blob
+ * @sock: the sock that needs a blob
+ * @priority: allocation mode
+ *
+ * Allocate the sock blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_sock_alloc(struct sock *sock, gfp_t priority)
+{
+       if (blob_sizes.lbs_sock == 0) {
+               sock->sk_security = NULL;
+               return 0;
+       }
+
+       sock->sk_security = kzalloc(blob_sizes.lbs_sock, priority);
+       if (sock->sk_security == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
 /**
  * lsm_early_task - during initialization allocate a composite task blob
  * @task: the task that needs a blob
@@ -673,6 +760,57 @@ static void __init lsm_early_task(struct task_struct *task)
                panic("%s: Early task alloc failed.\n", __func__);
 }
 
+/**
+ * append_ctx - append a lsm/context pair to a compound context
+ * @ctx: the existing compound context
+ * @ctxlen: size of the old context, including terminating nul byte
+ * @lsm: new lsm name, nul terminated
+ * @new: new context, possibly nul terminated
+ * @newlen: maximum size of @new
+ *
+ * replace @ctx with a new compound context, appending @newlsm and @new
+ * to @ctx. On exit the new data replaces the old, which is freed.
+ * @ctxlen is set to the new size, which includes a trailing nul byte.
+ *
+ * Returns 0 on success, -ENOMEM if no memory is available.
+ */
+static int append_ctx(char **ctx, int *ctxlen, const char *lsm, char *new,
+                     int newlen)
+{
+       char *final;
+       size_t llen;
+       size_t nlen;
+       size_t flen;
+
+       llen = strlen(lsm) + 1;
+       /*
+        * A security module may or may not provide a trailing nul on
+        * when returning a security context. There is no definition
+        * of which it should be, and there are modules that do it
+        * each way.
+        */
+       nlen = strnlen(new, newlen);
+
+       flen = *ctxlen + llen + nlen + 1;
+       final = kzalloc(flen, GFP_KERNEL);
+
+       if (final == NULL)
+               return -ENOMEM;
+
+       if (*ctxlen)
+               memcpy(final, *ctx, *ctxlen);
+
+       memcpy(final + *ctxlen, lsm, llen);
+       memcpy(final + *ctxlen + llen, new, nlen);
+
+       kfree(*ctx);
+
+       *ctx = final;
+       *ctxlen = flen;
+
+       return 0;
+}
+
 /**
  * lsm_superblock_alloc - allocate a composite superblock blob
  * @sb: the superblock that needs a blob
@@ -747,28 +885,61 @@ static int lsm_superblock_alloc(struct super_block *sb)
 
 /* Security operations */
 
-int security_binder_set_context_mgr(struct task_struct *mgr)
+int security_binder_set_context_mgr(const struct cred *mgr)
 {
        return call_int_hook(binder_set_context_mgr, 0, mgr);
 }
+EXPORT_SYMBOL(security_binder_set_context_mgr);
 
-int security_binder_transaction(struct task_struct *from,
-                               struct task_struct *to)
+/**
+ * security_binder_transaction - Binder driver transaction check
+ * @from: source of the transaction
+ * @to: destination of the transaction
+ *
+ * Verify that the creds have the same LSM "display", then
+ * call the security module hooks.
+ *
+ * Returns -EINVAL if the displays don't match, or the
+ * result of the security module checks.
+ */
+int security_binder_transaction(const struct cred *from,
+                               const struct cred *to)
 {
+       int from_display = lsm_cred_display(from);
+       int to_display = lsm_cred_display(to);
+
+       /*
+        * If the display is LSMBLOB_INVALID the first module that has
+        * an entry is used. This will be in the 0 slot.
+        *
+        * This is currently only required if the server has requested
+        * peer contexts, but it would be unwieldly to have too much of
+        * the binder driver detail here.
+        */
+       if (from_display == LSMBLOB_INVALID)
+               from_display = 0;
+       if (to_display == LSMBLOB_INVALID)
+               to_display = 0;
+       if (from_display != to_display)
+               return -EINVAL;
+
        return call_int_hook(binder_transaction, 0, from, to);
 }
+EXPORT_SYMBOL(security_binder_transaction);
 
-int security_binder_transfer_binder(struct task_struct *from,
-                                   struct task_struct *to)
+int security_binder_transfer_binder(const struct cred *from,
+                                   const struct cred *to)
 {
        return call_int_hook(binder_transfer_binder, 0, from, to);
 }
+EXPORT_SYMBOL(security_binder_transfer_binder);
 
-int security_binder_transfer_file(struct task_struct *from,
-                                 struct task_struct *to, struct file *file)
+int security_binder_transfer_file(const struct cred *from,
+                                 const struct cred *to, struct file *file)
 {
        return call_int_hook(binder_transfer_file, 0, from, to, file);
 }
+EXPORT_SYMBOL(security_binder_transfer_file);
 
 int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
 {
@@ -884,9 +1055,22 @@ int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
        return call_int_hook(fs_context_dup, 0, fc, src_fc);
 }
 
-int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param)
+int security_fs_context_parse_param(struct fs_context *fc,
+                                   struct fs_parameter *param)
 {
-       return call_int_hook(fs_context_parse_param, -ENOPARAM, fc, param);
+       struct security_hook_list *hp;
+       int trc;
+       int rc = -ENOPARAM;
+
+       hlist_for_each_entry(hp, &security_hook_heads.fs_context_parse_param,
+                            list) {
+               trc = hp->hook.fs_context_parse_param(fc, param);
+               if (trc == 0)
+                       rc = 0;
+               else if (trc != -ENOPARAM)
+                       return trc;
+       }
+       return rc;
 }
 
 int security_sb_alloc(struct super_block *sb)
@@ -1147,6 +1331,7 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry)
                return 0;
        return call_int_hook(path_rmdir, 0, dir, dentry);
 }
+EXPORT_SYMBOL_GPL(security_path_rmdir);
 
 int security_path_unlink(const struct path *dir, struct dentry *dentry)
 {
@@ -1163,6 +1348,7 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry,
                return 0;
        return call_int_hook(path_symlink, 0, dir, dentry, old_name);
 }
+EXPORT_SYMBOL_GPL(security_path_symlink);
 
 int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
                       struct dentry *new_dentry)
@@ -1171,6 +1357,7 @@ int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
                return 0;
        return call_int_hook(path_link, 0, old_dentry, new_dir, new_dentry);
 }
+EXPORT_SYMBOL_GPL(security_path_link);
 
 int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
                         const struct path *new_dir, struct dentry *new_dentry,
@@ -1198,6 +1385,7 @@ int security_path_truncate(const struct path *path)
                return 0;
        return call_int_hook(path_truncate, 0, path);
 }
+EXPORT_SYMBOL_GPL(security_path_truncate);
 
 int security_path_chmod(const struct path *path, umode_t mode)
 {
@@ -1205,6 +1393,7 @@ int security_path_chmod(const struct path *path, umode_t mode)
                return 0;
        return call_int_hook(path_chmod, 0, path, mode);
 }
+EXPORT_SYMBOL_GPL(security_path_chmod);
 
 int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
 {
@@ -1212,6 +1401,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
                return 0;
        return call_int_hook(path_chown, 0, path, uid, gid);
 }
+EXPORT_SYMBOL_GPL(security_path_chown);
 
 int security_path_chroot(const struct path *path)
 {
@@ -1312,6 +1502,7 @@ int security_inode_permission(struct inode *inode, int mask)
                return 0;
        return call_int_hook(inode_permission, 0, inode, mask);
 }
+EXPORT_SYMBOL_GPL(security_inode_permission);
 
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
 {
@@ -1461,9 +1652,16 @@ int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer
 }
 EXPORT_SYMBOL(security_inode_listsecurity);
 
-void security_inode_getsecid(struct inode *inode, u32 *secid)
+void security_inode_getsecid(struct inode *inode, struct lsmblob *blob)
 {
-       call_void_hook(inode_getsecid, inode, secid);
+       struct security_hook_list *hp;
+
+       lsmblob_init(blob, 0);
+       hlist_for_each_entry(hp, &security_hook_heads.inode_getsecid, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               hp->hook.inode_getsecid(inode, &blob->secid[hp->lsmid->slot]);
+       }
 }
 
 int security_inode_copy_up(struct dentry *src, struct cred **new)
@@ -1509,6 +1707,7 @@ int security_file_permission(struct file *file, int mask)
 
        return fsnotify_perm(file, mask);
 }
+EXPORT_SYMBOL_GPL(security_file_permission);
 
 int security_file_alloc(struct file *file)
 {
@@ -1640,14 +1839,26 @@ int security_file_open(struct file *file)
 
 int security_task_alloc(struct task_struct *task, unsigned long clone_flags)
 {
+       int *odisplay = current->security;
+       int *ndisplay;
        int rc = lsm_task_alloc(task);
 
-       if (rc)
+       if (unlikely(rc))
                return rc;
+
        rc = call_int_hook(task_alloc, 0, task, clone_flags);
-       if (unlikely(rc))
+       if (unlikely(rc)) {
                security_task_free(task);
-       return rc;
+               return rc;
+       }
+
+       if (odisplay) {
+               ndisplay = task->security;
+               if (ndisplay)
+                       *ndisplay = *odisplay;
+       }
+
+       return 0;
 }
 
 void security_task_free(struct task_struct *task)
@@ -1704,16 +1915,32 @@ void security_transfer_creds(struct cred *new, const struct cred *old)
        call_void_hook(cred_transfer, new, old);
 }
 
-void security_cred_getsecid(const struct cred *c, u32 *secid)
+void security_cred_getsecid(const struct cred *c, struct lsmblob *blob)
 {
-       *secid = 0;
-       call_void_hook(cred_getsecid, c, secid);
+       struct security_hook_list *hp;
+
+       lsmblob_init(blob, 0);
+       hlist_for_each_entry(hp, &security_hook_heads.cred_getsecid, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               hp->hook.cred_getsecid(c, &blob->secid[hp->lsmid->slot]);
+       }
 }
 EXPORT_SYMBOL(security_cred_getsecid);
 
-int security_kernel_act_as(struct cred *new, u32 secid)
+int security_kernel_act_as(struct cred *new, struct lsmblob *blob)
 {
-       return call_int_hook(kernel_act_as, 0, new, secid);
+       struct security_hook_list *hp;
+       int rc;
+
+       hlist_for_each_entry(hp, &security_hook_heads.kernel_act_as, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               rc = hp->hook.kernel_act_as(new, blob->secid[hp->lsmid->slot]);
+               if (rc != 0)
+                       return rc;
+       }
+       return 0;
 }
 
 int security_kernel_create_files_as(struct cred *new, struct inode *inode)
@@ -1807,17 +2034,29 @@ int security_task_getsid(struct task_struct *p)
        return call_int_hook(task_getsid, 0, p);
 }
 
-void security_task_getsecid_subj(struct task_struct *p, u32 *secid)
+void security_task_getsecid_subj(struct task_struct *p, struct lsmblob *blob)
 {
-       *secid = 0;
-       call_void_hook(task_getsecid_subj, p, secid);
+       struct security_hook_list *hp;
+
+       lsmblob_init(blob, 0);
+       hlist_for_each_entry(hp, &security_hook_heads.task_getsecid_subj, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               hp->hook.task_getsecid_subj(p, &blob->secid[hp->lsmid->slot]);
+       }
 }
 EXPORT_SYMBOL(security_task_getsecid_subj);
 
-void security_task_getsecid_obj(struct task_struct *p, u32 *secid)
+void security_task_getsecid_obj(struct task_struct *p, struct lsmblob *blob)
 {
-       *secid = 0;
-       call_void_hook(task_getsecid_obj, p, secid);
+       struct security_hook_list *hp;
+
+       lsmblob_init(blob, 0);
+       hlist_for_each_entry(hp, &security_hook_heads.task_getsecid_obj, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               hp->hook.task_getsecid_obj(p, &blob->secid[hp->lsmid->slot]);
+       }
 }
 EXPORT_SYMBOL(security_task_getsecid_obj);
 
@@ -1897,10 +2136,16 @@ int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
        return call_int_hook(ipc_permission, 0, ipcp, flag);
 }
 
-void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
+void security_ipc_getsecid(struct kern_ipc_perm *ipcp, struct lsmblob *blob)
 {
-       *secid = 0;
-       call_void_hook(ipc_getsecid, ipcp, secid);
+       struct security_hook_list *hp;
+
+       lsmblob_init(blob, 0);
+       hlist_for_each_entry(hp, &security_hook_heads.ipc_getsecid, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               hp->hook.ipc_getsecid(ipcp, &blob->secid[hp->lsmid->slot]);
+       }
 }
 
 int security_msg_msg_alloc(struct msg_msg *msg)
@@ -2044,22 +2289,137 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
                                char **value)
 {
        struct security_hook_list *hp;
+       char *final = NULL;
+       char *cp;
+       int rc = 0;
+       int finallen = 0;
+       int display = lsm_task_display(current);
+       int slot = 0;
+
+       if (!strcmp(name, "display")) {
+               /*
+                * lsm_slot will be 0 if there are no displaying modules.
+                */
+               if (lsm_slot == 0)
+                       return -EINVAL;
+
+               /*
+                * Only allow getting the current process' display.
+                * There are too few reasons to get another process'
+                * display and too many LSM policy issues.
+                */
+               if (current != p)
+                       return -EINVAL;
+
+               display = lsm_task_display(p);
+               if (display != LSMBLOB_INVALID)
+                       slot = display;
+               *value = kstrdup(lsm_slotlist[slot]->lsm, GFP_KERNEL);
+               if (*value)
+                       return strlen(*value);
+               return -ENOMEM;
+       }
+
+       if (!strcmp(name, "context")) {
+               hlist_for_each_entry(hp, &security_hook_heads.getprocattr,
+                                    list) {
+                       rc = hp->hook.getprocattr(p, "context", &cp);
+                       if (rc == -EINVAL)
+                               continue;
+                       if (rc < 0) {
+                               kfree(final);
+                               return rc;
+                       }
+                       rc = append_ctx(&final, &finallen, hp->lsmid->lsm,
+                                       cp, rc);
+                       kfree(cp);
+                       if (rc < 0) {
+                               kfree(final);
+                               return rc;
+                       }
+               }
+               if (final == NULL)
+                       return -EINVAL;
+               *value = final;
+               return finallen;
+       }
 
        hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
-               if (lsm != NULL && strcmp(lsm, hp->lsm))
+               if (lsm != NULL && strcmp(lsm, hp->lsmid->lsm))
+                       continue;
+               if (lsm == NULL && display != LSMBLOB_INVALID &&
+                   display != hp->lsmid->slot)
                        continue;
                return hp->hook.getprocattr(p, name, value);
        }
        return LSM_RET_DEFAULT(getprocattr);
 }
 
+/**
+ * security_setprocattr - Set process attributes via /proc
+ * @lsm: name of module involved, or NULL
+ * @name: name of the attribute
+ * @value: value to set the attribute to
+ * @size: size of the value
+ *
+ * Set the process attribute for the specified security module
+ * to the specified value. Note that this can only be used to set
+ * the process attributes for the current, or "self" process.
+ * The /proc code has already done this check.
+ *
+ * Returns 0 on success, an appropriate code otherwise.
+ */
 int security_setprocattr(const char *lsm, const char *name, void *value,
                         size_t size)
 {
        struct security_hook_list *hp;
+       char *termed;
+       char *copy;
+       int *display = current->security;
+       int rc = -EINVAL;
+       int slot = 0;
+
+       if (!strcmp(name, "display")) {
+               /*
+                * Change the "display" value only if all the security
+                * modules that support setting a procattr allow it.
+                * It is assumed that all such security modules will be
+                * cooperative.
+                */
+               if (size == 0)
+                       return -EINVAL;
+
+               hlist_for_each_entry(hp, &security_hook_heads.setprocattr,
+                                    list) {
+                       rc = hp->hook.setprocattr(name, value, size);
+                       if (rc < 0 && rc != -EINVAL)
+                               return rc;
+               }
+
+               rc = -EINVAL;
+
+               copy = kmemdup_nul(value, size, GFP_KERNEL);
+               if (copy == NULL)
+                       return -ENOMEM;
+
+               termed = strsep(&copy, " \n");
+
+               for (slot = 0; slot < lsm_slot; slot++)
+                       if (!strcmp(termed, lsm_slotlist[slot]->lsm)) {
+                               *display = lsm_slotlist[slot]->slot;
+                               rc = size;
+                               break;
+                       }
+
+               kfree(termed);
+               return rc;
+       }
 
        hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
-               if (lsm != NULL && strcmp(lsm, hp->lsm))
+               if (lsm != NULL && strcmp(lsm, hp->lsmid->lsm))
+                       continue;
+               if (lsm == NULL && *display != LSMBLOB_INVALID &&
+                   *display != hp->lsmid->slot)
                        continue;
                return hp->hook.setprocattr(name, value, size);
        }
@@ -2077,35 +2437,76 @@ int security_ismaclabel(const char *name)
 }
 EXPORT_SYMBOL(security_ismaclabel);
 
-int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+int security_secid_to_secctx(struct lsmblob *blob, struct lsmcontext *cp,
+                            int display)
 {
        struct security_hook_list *hp;
-       int rc;
+
+       memset(cp, 0, sizeof(*cp));
 
        /*
-        * Currently, only one LSM can implement secid_to_secctx (i.e this
-        * LSM hook is not "stackable").
+        * display either is the slot number use for formatting
+        * or an instruction on which relative slot to use.
         */
+       if (display == LSMBLOB_DISPLAY)
+               display = lsm_task_display(current);
+       else if (display == LSMBLOB_FIRST)
+               display = LSMBLOB_INVALID;
+       else if (display < 0) {
+               WARN_ONCE(true,
+                       "LSM: %s unknown display\n", __func__);
+               display = LSMBLOB_INVALID;
+       } else if (display >= lsm_slot) {
+               WARN_ONCE(true,
+                       "LSM: %s invalid display\n", __func__);
+               display = LSMBLOB_INVALID;
+       }
+
+
        hlist_for_each_entry(hp, &security_hook_heads.secid_to_secctx, list) {
-               rc = hp->hook.secid_to_secctx(secid, secdata, seclen);
-               if (rc != LSM_RET_DEFAULT(secid_to_secctx))
-                       return rc;
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               if (display == LSMBLOB_INVALID || display == hp->lsmid->slot) {
+                       cp->slot = hp->lsmid->slot;
+                       return hp->hook.secid_to_secctx(
+                                       blob->secid[hp->lsmid->slot],
+                                       &cp->context, &cp->len);
+               }
        }
 
        return LSM_RET_DEFAULT(secid_to_secctx);
 }
 EXPORT_SYMBOL(security_secid_to_secctx);
 
-int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
+int security_secctx_to_secid(const char *secdata, u32 seclen,
+                            struct lsmblob *blob)
 {
-       *secid = 0;
-       return call_int_hook(secctx_to_secid, 0, secdata, seclen, secid);
+       struct security_hook_list *hp;
+       int display = lsm_task_display(current);
+
+       lsmblob_init(blob, 0);
+       hlist_for_each_entry(hp, &security_hook_heads.secctx_to_secid, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               if (display == LSMBLOB_INVALID || display == hp->lsmid->slot)
+                       return hp->hook.secctx_to_secid(secdata, seclen,
+                                               &blob->secid[hp->lsmid->slot]);
+       }
+       return -EOPNOTSUPP;
 }
 EXPORT_SYMBOL(security_secctx_to_secid);
 
-void security_release_secctx(char *secdata, u32 seclen)
+void security_release_secctx(struct lsmcontext *cp)
 {
-       call_void_hook(release_secctx, secdata, seclen);
+       struct security_hook_list *hp;
+
+       hlist_for_each_entry(hp, &security_hook_heads.release_secctx, list)
+               if (cp->slot == hp->lsmid->slot) {
+                       hp->hook.release_secctx(cp->context, cp->len);
+                       break;
+               }
+
+       memset(cp, 0, sizeof(*cp));
 }
 EXPORT_SYMBOL(security_release_secctx);
 
@@ -2127,9 +2528,18 @@ int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
 }
 EXPORT_SYMBOL(security_inode_setsecctx);
 
-int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+int security_inode_getsecctx(struct inode *inode, struct lsmcontext *cp)
 {
-       return call_int_hook(inode_getsecctx, -EOPNOTSUPP, inode, ctx, ctxlen);
+       struct security_hook_list *hp;
+
+       memset(cp, 0, sizeof(*cp));
+
+       hlist_for_each_entry(hp, &security_hook_heads.inode_getsecctx, list) {
+               cp->slot = hp->lsmid->slot;
+               return hp->hook.inode_getsecctx(inode, (void **)&cp->context,
+                                               &cp->len);
+       }
+       return -EOPNOTSUPP;
 }
 EXPORT_SYMBOL(security_inode_getsecctx);
 
@@ -2246,25 +2656,53 @@ EXPORT_SYMBOL(security_sock_rcv_skb);
 int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
                                      int __user *optlen, unsigned len)
 {
-       return call_int_hook(socket_getpeersec_stream, -ENOPROTOOPT, sock,
-                               optval, optlen, len);
+       int display = lsm_task_display(current);
+       struct security_hook_list *hp;
+
+       hlist_for_each_entry(hp, &security_hook_heads.socket_getpeersec_stream,
+                            list)
+               if (display == LSMBLOB_INVALID || display == hp->lsmid->slot)
+                       return hp->hook.socket_getpeersec_stream(sock, optval,
+                                                                optlen, len);
+       return -ENOPROTOOPT;
 }
 
-int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
+int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb,
+                                    struct lsmblob *blob)
 {
-       return call_int_hook(socket_getpeersec_dgram, -ENOPROTOOPT, sock,
-                            skb, secid);
+       struct security_hook_list *hp;
+       int rc = -ENOPROTOOPT;
+
+       hlist_for_each_entry(hp, &security_hook_heads.socket_getpeersec_dgram,
+                            list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               rc = hp->hook.socket_getpeersec_dgram(sock, skb,
+                                               &blob->secid[hp->lsmid->slot]);
+               if (rc != 0)
+                       break;
+       }
+       return rc;
 }
 EXPORT_SYMBOL(security_socket_getpeersec_dgram);
 
 int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
 {
-       return call_int_hook(sk_alloc_security, 0, sk, family, priority);
+       int rc = lsm_sock_alloc(sk, priority);
+
+       if (unlikely(rc))
+               return rc;
+       rc = call_int_hook(sk_alloc_security, 0, sk, family, priority);
+       if (unlikely(rc))
+               security_sk_free(sk);
+       return rc;
 }
 
 void security_sk_free(struct sock *sk)
 {
        call_void_hook(sk_free_security, sk);
+       kfree(sk->sk_security);
+       sk->sk_security = NULL;
 }
 
 void security_sk_clone(const struct sock *sk, struct sock *newsk)
@@ -2312,9 +2750,21 @@ void security_inet_conn_established(struct sock *sk,
 }
 EXPORT_SYMBOL(security_inet_conn_established);
 
-int security_secmark_relabel_packet(u32 secid)
+int security_secmark_relabel_packet(struct lsmblob *blob)
 {
-       return call_int_hook(secmark_relabel_packet, 0, secid);
+       struct security_hook_list *hp;
+       int rc = 0;
+
+       hlist_for_each_entry(hp, &security_hook_heads.secmark_relabel_packet,
+                            list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               rc = hp->hook.secmark_relabel_packet(
+                                               blob->secid[hp->lsmid->slot]);
+               if (rc != 0)
+                       break;
+       }
+       return rc;
 }
 EXPORT_SYMBOL(security_secmark_relabel_packet);
 
@@ -2543,7 +2993,18 @@ int security_key_getsecurity(struct key *key, char **_buffer)
 
 int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
 {
-       return call_int_hook(audit_rule_init, 0, field, op, rulestr, lsmrule);
+       struct security_hook_list *hp;
+       int display = lsm_task_display(current);
+
+       hlist_for_each_entry(hp, &security_hook_heads.audit_rule_init, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               if (display != LSMBLOB_INVALID && display != hp->lsmid->slot)
+                       continue;
+               return hp->hook.audit_rule_init(field, op, rulestr,
+                                               &lsmrule[hp->lsmid->slot]);
+       }
+       return 0;
 }
 
 int security_audit_rule_known(struct audit_krule *krule)
@@ -2551,14 +3012,39 @@ int security_audit_rule_known(struct audit_krule *krule)
        return call_int_hook(audit_rule_known, 0, krule);
 }
 
-void security_audit_rule_free(void *lsmrule)
+void security_audit_rule_free(void **lsmrule)
 {
-       call_void_hook(audit_rule_free, lsmrule);
+       struct security_hook_list *hp;
+
+       hlist_for_each_entry(hp, &security_hook_heads.audit_rule_free, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               if (lsmrule[hp->lsmid->slot] == NULL)
+                       continue;
+               hp->hook.audit_rule_free(lsmrule[hp->lsmid->slot]);
+       }
 }
 
-int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule)
+int security_audit_rule_match(struct lsmblob *blob, u32 field, u32 op,
+                             void **lsmrule)
 {
-       return call_int_hook(audit_rule_match, 0, secid, field, op, lsmrule);
+       struct security_hook_list *hp;
+       int rc;
+
+       hlist_for_each_entry(hp, &security_hook_heads.audit_rule_match, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               if (lsmrule[hp->lsmid->slot] == NULL)
+                       continue;
+               if (lsmrule[hp->lsmid->slot] == NULL)
+                       continue;
+               rc = hp->hook.audit_rule_match(blob->secid[hp->lsmid->slot],
+                                              field, op,
+                                              &lsmrule[hp->lsmid->slot]);
+               if (rc)
+                       return rc;
+       }
+       return 0;
 }
 #endif /* CONFIG_AUDIT */
 
@@ -2599,6 +3085,12 @@ int security_locked_down(enum lockdown_reason what)
 }
 EXPORT_SYMBOL(security_locked_down);
 
+int security_lock_kernel_down(const char *where, enum lockdown_reason level)
+{
+       return call_int_hook(lock_kernel_down, 0, where, level);
+}
+EXPORT_SYMBOL(security_lock_kernel_down);
+
 #ifdef CONFIG_PERF_EVENTS
 int security_perf_event_open(struct perf_event_attr *attr, int type)
 {