]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - security/security.c
x86/cpu: Make alternative_msr_write work for 32-bit code
[mirror_ubuntu-artful-kernel.git] / security / security.c
index 55859bfc19714708c627771aea1a7889d825bb79..95d6b85a951c30c55b08fbd70fd6d86a487709da 100644 (file)
 #include <linux/personality.h>
 #include <linux/backing-dev.h>
 #include <linux/string.h>
-#include <linux/msg.h>
+#include <linux/prctl.h>
 #include <net/flow.h>
-#include <net/sock.h>
 
 #define MAX_LSM_EVM_XATTR      2
 
 /* Maximum number of letters for an LSM name string */
 #define SECURITY_NAME_MAX      10
+#define SECURITY_CHOSEN_NAMES_MAX (SECURITY_NAME_MAX * LSM_MAX_MAJOR)
 #define MODULE_STACK           "(stacking)"
 
 struct security_hook_heads security_hook_heads __lsm_ro_after_init;
 static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
 
 char *lsm_names;
-static struct lsm_blob_sizes blob_sizes;
+
+/*
+ * If stacking is enabled the task blob will always
+ * include an indicator of what security module data
+ * should be displayed. This is set with PR_SET_DISPLAY_LSM.
+ */
+static struct lsm_blob_sizes blob_sizes = {
+#ifdef CONFIG_SECURITY_STACKING
+       .lbs_task = SECURITY_NAME_MAX + 6,
+#endif
+};
 
 /* Boot-time LSM user choice */
-static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
+static __initdata char chosen_lsms[SECURITY_CHOSEN_NAMES_MAX + 1] =
 #ifdef CONFIG_SECURITY_STACKING
        MODULE_STACK;
 #else
        CONFIG_DEFAULT_SECURITY;
 #endif
+static __initdata char chosen_display_lsm[SECURITY_NAME_MAX + 1]
+#ifdef CONFIG_SECURITY_STACKING
+       = CONFIG_SECURITY_DEFAULT_DISPLAY_NAME
+#endif
+;
+static char default_display_lsm[SECURITY_NAME_MAX + 1];
 
 static void __init do_security_initcalls(void)
 {
@@ -99,16 +115,8 @@ int __init security_init(void)
 #ifdef CONFIG_SECURITY_LSM_DEBUG
        pr_info("LSM: cred blob size       = %d\n", blob_sizes.lbs_cred);
        pr_info("LSM: file blob size       = %d\n", blob_sizes.lbs_file);
-       pr_info("LSM: inode blob size      = %d\n", blob_sizes.lbs_inode);
-       pr_info("LSM: ipc blob size        = %d\n", blob_sizes.lbs_ipc);
-#ifdef CONFIG_KEYS
-       pr_info("LSM: key blob size        = %d\n", blob_sizes.lbs_key);
-#endif /* CONFIG_KEYS */
-       pr_info("LSM: msg_msg blob size    = %d\n", blob_sizes.lbs_msg_msg);
-       pr_info("LSM: sock blob size       = %d\n", blob_sizes.lbs_sock);
-       pr_info("LSM: superblock blob size = %d\n", blob_sizes.lbs_superblock);
        pr_info("LSM: task blob size       = %d\n", blob_sizes.lbs_task);
-#endif /* CONFIG_SECURITY_LSM_DEBUG */
+#endif
 
        return 0;
 }
@@ -116,11 +124,22 @@ int __init security_init(void)
 /* Save user chosen LSM */
 static int __init choose_lsm(char *str)
 {
-       strncpy(chosen_lsm, str, SECURITY_NAME_MAX);
+       strncpy(chosen_lsms, str, SECURITY_CHOSEN_NAMES_MAX);
+       pr_info("LSM: command line set '%s' security module(s).\n",
+               chosen_lsms);
        return 1;
 }
 __setup("security=", choose_lsm);
 
+static int __init choose_display_lsm(char *str)
+{
+       strncpy(chosen_display_lsm, str, SECURITY_NAME_MAX);
+       pr_info("LSM: command line set default display lsm %s'\n",
+               chosen_display_lsm);
+       return 1;
+}
+__setup("security.display=", choose_display_lsm);
+
 static bool match_last_lsm(const char *list, const char *lsm)
 {
        const char *last;
@@ -174,16 +193,43 @@ static int lsm_append(char *new, char **result)
  *
  * Otherwise, return false.
  */
+#ifdef CONFIG_SECURITY_STACKING
+static bool __init cmp_lsms(const char *lsm)
+{
+       const char *str = chosen_lsms;
+       const char *split;
+       int len = strlen(lsm);
+
+       if (len > SECURITY_NAME_MAX) {
+               pr_info("LSM: security module name '%s' exceeds limit\n", lsm);
+               return false;
+       }
+       for (split = strchr(str, ','); split; split = strchr(str, ',')) {
+               if ((len == split - str) && !strncmp(lsm, str, split - str))
+                       return true;
+               str = split + 1;
+       }
+       if ((len == strlen(str)) && !strncmp(lsm, str, strlen(str)))
+               return true;
+       return false;
+}
+#endif
+
 bool __init security_module_enable(const char *lsm, const bool stacked)
 {
 #ifdef CONFIG_SECURITY_STACKING
        /*
         * Module defined on the command line security=XXXX
         */
-       if (strcmp(chosen_lsm, MODULE_STACK)) {
-               if (!strcmp(lsm, chosen_lsm)) {
-                       pr_info("Command line sets the %s security module.\n",
-                               lsm);
+       if (strcmp(chosen_lsms, MODULE_STACK)) {
+               if (cmp_lsms(lsm)) {
+                       /* set to first LSM registered and then override */
+                       if (!*default_display_lsm)
+                               strcpy(default_display_lsm, lsm);
+                       else if (*chosen_display_lsm && !strcmp(chosen_display_lsm, lsm)) {
+                               strcpy(default_display_lsm, lsm);
+                               pr_info("LSM: default display lsm '%s'\n", default_display_lsm);
+                       }
                        return true;
                }
                return false;
@@ -191,14 +237,28 @@ bool __init security_module_enable(const char *lsm, const bool stacked)
        /*
         * Module configured as stacked.
         */
+       if (stacked && !*default_display_lsm)
+               strcpy(default_display_lsm, lsm);
+       else if (stacked && *chosen_display_lsm && !strcmp(chosen_display_lsm, lsm)) {
+               strcpy(default_display_lsm, lsm);
+               pr_info("LSM: default display lsm '%s'\n", default_display_lsm);
+       }
+
        return stacked;
 #else
-       if (strcmp(lsm, chosen_lsm) == 0)
+       if (strcmp(lsm, chosen_lsms) == 0) {
+               strcpy(default_display_lsm, lsm);
                return true;
+       }
        return false;
 #endif
 }
 
+/*
+ * Keep the order of major modules for mapping secids.
+ */
+static int lsm_next_major;
+
 /**
  * security_add_hooks - Add a modules hooks to the hook lists.
  * @hooks: the hooks to add
@@ -211,9 +271,14 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
                                char *lsm)
 {
        int i;
+       int lsm_index = lsm_next_major++;
 
+#ifdef CONFIG_SECURITY_LSM_DEBUG
+       pr_info("LSM: Security module %s gets index %d\n", lsm, lsm_index);
+#endif
        for (i = 0; i < count; i++) {
                hooks[i].lsm = lsm;
+               hooks[i].lsm_index = lsm_index;
                list_add_tail_rcu(&hooks[i].list, hooks[i].head);
        }
        if (lsm_append(lsm, &lsm_names) < 0)
@@ -306,19 +371,7 @@ void __init security_add_blobs(struct lsm_blob_sizes *needed)
 {
        lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
        lsm_set_size(&needed->lbs_file, &blob_sizes.lbs_file);
-       lsm_set_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
-       lsm_set_size(&needed->lbs_key, &blob_sizes.lbs_key);
-       lsm_set_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
-       lsm_set_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
-       lsm_set_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
        lsm_set_size(&needed->lbs_task, &blob_sizes.lbs_task);
-       /*
-        * The inode blob gets an rcu_head in addition to
-        * what the modules might need.
-        */
-       if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
-               blob_sizes.lbs_inode = sizeof(struct rcu_head);
-       lsm_set_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
 }
 
 /**
@@ -344,186 +397,92 @@ int lsm_file_alloc(struct file *file)
        return 0;
 }
 
-/**
- * lsm_task_alloc - allocate a composite task blob
- * @task: the task that needs a blob
- *
- * Allocate the task blob for all the modules
- *
- * Returns 0, or -ENOMEM if memory can't be allocated.
- */
-int lsm_task_alloc(struct task_struct *task)
+#ifdef CONFIG_SECURITY_STACKING
+static inline char *lsm_of_task(struct task_struct *task)
 {
 #ifdef CONFIG_SECURITY_LSM_DEBUG
-       if (task->security)
-               pr_info("%s: Inbound task blob is not NULL.\n", __func__);
-#endif
-       if (blob_sizes.lbs_task == 0)
-               return 0;
-
-       task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL);
        if (task->security == NULL)
-               return -ENOMEM;
-       return 0;
-}
-
-/**
- * lsm_inode_alloc - allocate a composite inode blob
- * @inode: the inode that needs a blob
- *
- * Allocate the inode blob for all the modules
- *
- * Returns 0, or -ENOMEM if memory can't be allocated.
- */
-int lsm_inode_alloc(struct inode *inode)
-{
-#ifdef CONFIG_SECURITY_LSM_DEBUG
-       if (inode->i_security)
-               pr_info("%s: Inbound inode blob is not NULL.\n", __func__);
+               pr_info("%s: task has no lsm name.\n", __func__);
 #endif
-       if (blob_sizes.lbs_inode == 0)
-               return 0;
-
-       inode->i_security = kzalloc(blob_sizes.lbs_inode, GFP_KERNEL);
-       if (inode->i_security == NULL)
-               return -ENOMEM;
-       return 0;
+       return task->security;
 }
-
-/**
- * lsm_early_inode - during initialization allocate a composite inode blob
- * @inode: the inode that needs a blob
- *
- * Allocate the inode blob for all the modules if it's not already there
- */
-void lsm_early_inode(struct inode *inode)
-{
-       int rc;
-
-       if (inode == NULL)
-               panic("%s: NULL inode.\n", __func__);
-       if (inode->i_security != NULL)
-               return;
-       rc = lsm_inode_alloc(inode);
-       if (rc)
-               panic("%s: Early inode alloc failed.\n", __func__);
-}
-
-/**
- * lsm_ipc_alloc - allocate a composite ipc blob
- * @kip: the ipc that needs a blob
- *
- * Allocate the ipc blob for all the modules
- *
- * Returns 0, or -ENOMEM if memory can't be allocated.
- */
-int lsm_ipc_alloc(struct kern_ipc_perm *kip)
-{
-#ifdef CONFIG_SECURITY_LSM_DEBUG
-       if (kip->security)
-               pr_info("%s: Inbound ipc blob is not NULL.\n", __func__);
 #endif
-       if (blob_sizes.lbs_ipc == 0)
-               return 0;
 
-       kip->security = kzalloc(blob_sizes.lbs_ipc, GFP_KERNEL);
-       if (kip->security == NULL)
-               return -ENOMEM;
-       return 0;
-}
+#ifdef CONFIG_SECURITY_STACKING
+struct lsm_value {
+       char *lsm;
+       char *data;
+};
 
-#ifdef CONFIG_KEYS
 /**
- * lsm_key_alloc - allocate a composite key blob
- * @key: the key that needs a blob
- *
- * Allocate the key blob for all the modules
+ * lsm_parse_context - break a compound "context" into module data
+ * @cxt: the initial data, which will be modified
+ * @vlist: an array to receive the results
  *
- * Returns 0, or -ENOMEM if memory can't be allocated.
+ * Returns the number of entries, or -EINVAL if the cxt is unworkable.
  */
-int lsm_key_alloc(struct key *key)
+static int lsm_parse_context(char *cxt, struct lsm_value *vlist)
 {
-#ifdef CONFIG_SECURITY_LSM_DEBUG
-       if (key->security)
-               pr_info("%s: Inbound key blob is not NULL.\n", __func__);
-#endif
-       if (blob_sizes.lbs_key == 0)
-               return 0;
+       char *lsm;
+       char *data;
+       char *cp;
+       int i;
 
-       key->security = kzalloc(blob_sizes.lbs_key, GFP_KERNEL);
-       if (key->security == NULL)
-               return -ENOMEM;
-       return 0;
+       lsm = cxt;
+       for (i = 0; i < LSM_MAX_MAJOR; i++) {
+               data = strstr(lsm, "='");
+               if (!data)
+                       break;
+               *data = '\0';
+               data += 2;
+               cp = strchr(data, '\'');
+               if (!cp)
+                       return -EINVAL;
+               *cp++ = '\0';
+               vlist[i].lsm = lsm;
+               vlist[i].data = data;
+               if (*cp == '\0') {
+                       i++;
+                       break;
+               }
+               if (*cp == ',')
+                       cp++;
+               else
+                       return -EINVAL;
+               lsm = cp;
+       }
+       return i;
 }
-#endif /* CONFIG_KEYS */
+#endif /* CONFIG_SECURITY_STACKING */
 
 /**
- * lsm_msg_msg_alloc - allocate a composite msg_msg blob
- * @mp: the msg_msg that needs a blob
+ * lsm_task_alloc - allocate a composite task blob
+ * @task: the task that needs a blob
  *
- * Allocate the ipc blob for all the modules
+ * Allocate the task blob for all the modules
  *
  * Returns 0, or -ENOMEM if memory can't be allocated.
  */
-int lsm_msg_msg_alloc(struct msg_msg *mp)
+int lsm_task_alloc(struct task_struct *task)
 {
 #ifdef CONFIG_SECURITY_LSM_DEBUG
-       if (mp->security)
-               pr_info("%s: Inbound msg_msg blob is not NULL.\n", __func__);
+       if (task->security)
+               pr_info("%s: Inbound task blob is not NULL.\n", __func__);
 #endif
-       if (blob_sizes.lbs_msg_msg == 0)
+       if (blob_sizes.lbs_task == 0)
                return 0;
 
-       mp->security = kzalloc(blob_sizes.lbs_msg_msg, GFP_KERNEL);
-       if (mp->security == NULL)
+       task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL);
+       if (task->security == NULL)
                return -ENOMEM;
-       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.
- */
-int lsm_sock_alloc(struct sock *sock, gfp_t priority)
-{
-#ifdef CONFIG_SECURITY_LSM_DEBUG
-       if (sock->sk_security)
-               pr_info("%s: Inbound sock blob is not NULL.\n", __func__);
-#endif
-       if (blob_sizes.lbs_sock == 0)
-               return 0;
 
-       sock->sk_security = kzalloc(blob_sizes.lbs_sock, priority);
-       if (sock->sk_security == NULL)
-               return -ENOMEM;
-       return 0;
-}
-
-/**
- * lsm_superblock_alloc - allocate a composite superblock blob
- * @sb: the superblock that needs a blob
- *
- * Allocate the superblock blob for all the modules
- *
- * Returns 0, or -ENOMEM if memory can't be allocated.
- */
-int lsm_superblock_alloc(struct super_block *sb)
-{
-#ifdef CONFIG_SECURITY_LSM_DEBUG
-       if (sb->s_security)
-               pr_info("%s: Inbound superblock blob is not NULL.\n", __func__);
+       /* inherit current display lsm */
+#ifdef CONFIG_SECURITY_STACKING
+       if (current->security)
+               strcpy(task->security, lsm_of_task(current));
+       else
+               strcpy(task->security, default_display_lsm);
 #endif
-       if (blob_sizes.lbs_superblock == 0)
-               return 0;
-
-       sb->s_security = kzalloc(blob_sizes.lbs_superblock, GFP_KERNEL);
-       if (sb->s_security == NULL)
-               return -ENOMEM;
        return 0;
 }
 
@@ -699,18 +658,12 @@ int security_bprm_secureexec(struct linux_binprm *bprm)
 
 int security_sb_alloc(struct super_block *sb)
 {
-       int rc = lsm_superblock_alloc(sb);
-
-       if (rc)
-               return rc;
        return call_int_hook(sb_alloc_security, 0, sb);
 }
 
 void security_sb_free(struct super_block *sb)
 {
        call_void_hook(sb_free_security, sb);
-       kfree(sb->s_security);
-       sb->s_security = NULL;
 }
 
 int security_sb_copy_data(char *orig, char *copy)
@@ -784,39 +737,14 @@ EXPORT_SYMBOL(security_sb_parse_opts_str);
 
 int security_inode_alloc(struct inode *inode)
 {
-       int rc = lsm_inode_alloc(inode);
-
-       if (rc)
-               return rc;
+       inode->i_security = NULL;
        return call_int_hook(inode_alloc_security, 0, inode);
 }
 
-static void inode_free_by_rcu(struct rcu_head *head)
-{
-       /*
-        * The rcu head is at the start of the inode blob
-        */
-       kfree(head);
-}
-
 void security_inode_free(struct inode *inode)
 {
        integrity_inode_free(inode);
        call_void_hook(inode_free_security, inode);
-       /*
-        * The inode may still be referenced in a path walk and
-        * a call to security_inode_permission() can be made
-        * after inode_free_security() is called. Ideally, the VFS
-        * wouldn't do this, but fixing that is a much harder
-        * job. For now, simply free the i_security via RCU, and
-        * leave the current inode->i_security pointer intact.
-        * The inode will be freed after the RCU grace period too.
-        */
-       if (inode->i_security != NULL) {
-               call_rcu((struct rcu_head *)inode->i_security,
-                               inode_free_by_rcu);
-               inode->i_security = NULL;
-       }
 }
 
 int security_dentry_init_security(struct dentry *dentry, int mode,
@@ -1375,6 +1303,10 @@ int security_task_create(unsigned long clone_flags)
 
 int security_task_alloc(struct task_struct *task, unsigned long clone_flags)
 {
+       int rc = lsm_task_alloc(task);
+
+       if (rc)
+               return rc;
        return call_int_hook(task_alloc, 0, task, clone_flags);
 }
 
@@ -1533,6 +1465,80 @@ int security_task_kill(struct task_struct *p, struct siginfo *info,
        return call_int_hook(task_kill, 0, p, info, sig, secid);
 }
 
+#ifdef CONFIG_SECURITY_STACKING
+static char *nolsm = "-default";
+#define NOLSMLEN       9
+
+static bool is_registered_lsm(const char *str, size_t size)
+{
+       struct security_hook_list *hp;
+
+       list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
+               if (size == strlen(hp->lsm) && !strncmp(str, hp->lsm, size))
+                       return true;
+       }
+
+       return false;
+}
+
+static bool set_lsm_of_current(const char *str, size_t size)
+{
+       char *lsm = lsm_of_task(current);
+
+       if (is_registered_lsm(str, size)) {
+               strncpy(lsm, str, size);
+               lsm[size] = '\0';
+       } else if (size == NOLSMLEN && !strncmp(str, nolsm, size)) {
+               lsm[0] = '\0';
+       } else {
+               return false;
+       }
+       return true;
+}
+
+static int lsm_task_prctl(int option, unsigned long arg2, unsigned long arg3,
+                               unsigned long arg4, unsigned long arg5)
+{
+       char *lsm = lsm_of_task(current);
+       char buffer[SECURITY_NAME_MAX + 1];
+       __user char *optval = (__user char *)arg2;
+       __user int *optlen = (__user int *)arg3;
+       int dlen;
+       int len;
+
+       switch (option) {
+       case PR_GET_DISPLAY_LSM:
+               len = arg4;
+               if (lsm[0] == '\0') {
+                       lsm = nolsm;
+                       dlen = NOLSMLEN;
+               } else
+                       dlen = strlen(lsm) + 1;
+               if (dlen > len)
+                       return -ERANGE;
+               if (copy_to_user(optval, lsm, dlen))
+                       return -EFAULT;
+               if (put_user(dlen, optlen))
+                       return -EFAULT;
+               break;
+       case PR_SET_DISPLAY_LSM:
+               len = arg3;
+               if (len > SECURITY_NAME_MAX)
+                       return -EINVAL;
+               if (copy_from_user(buffer, optval, len))
+                       return -EFAULT;
+               buffer[len] = '\0';
+               /* verify the requested LSM is registered */
+               if (!set_lsm_of_current(buffer, len))
+                       return -ENOENT;
+               break;
+       default:
+               return -ENOSYS;
+       }
+       return 0;
+}
+#endif
+
 int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                         unsigned long arg4, unsigned long arg5)
 {
@@ -1540,6 +1546,12 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
        int rc = -ENOSYS;
        struct security_hook_list *hp;
 
+#ifdef CONFIG_SECURITY_STACKING
+       rc = lsm_task_prctl(option, arg2, arg3, arg4, arg5);
+       if (rc != -ENOSYS)
+               return rc;
+#endif
+
        list_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
                thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
                if (thisrc != -ENOSYS) {
@@ -1569,36 +1581,22 @@ void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
 
 int security_msg_msg_alloc(struct msg_msg *msg)
 {
-       int rc = lsm_msg_msg_alloc(msg);
-
-       if (rc)
-               return rc;
        return call_int_hook(msg_msg_alloc_security, 0, msg);
 }
 
 void security_msg_msg_free(struct msg_msg *msg)
 {
        call_void_hook(msg_msg_free_security, msg);
-       kfree(msg->security);
-       msg->security = NULL;
 }
 
 int security_msg_queue_alloc(struct msg_queue *msq)
 {
-       int rc = lsm_ipc_alloc(&msq->q_perm);
-
-       if (rc)
-               return rc;
        return call_int_hook(msg_queue_alloc_security, 0, msq);
 }
 
 void security_msg_queue_free(struct msg_queue *msq)
 {
-       struct kern_ipc_perm *kip = &msq->q_perm;
-
        call_void_hook(msg_queue_free_security, msq);
-       kfree(kip->security);
-       kip->security = NULL;
 }
 
 int security_msg_queue_associate(struct msg_queue *msq, int msqflg)
@@ -1625,20 +1623,12 @@ int security_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
 
 int security_shm_alloc(struct shmid_kernel *shp)
 {
-       int rc = lsm_ipc_alloc(&shp->shm_perm);
-
-       if (rc)
-               return rc;
        return call_int_hook(shm_alloc_security, 0, shp);
 }
 
 void security_shm_free(struct shmid_kernel *shp)
 {
-       struct kern_ipc_perm *kip = &shp->shm_perm;
-
        call_void_hook(shm_free_security, shp);
-       kfree(kip->security);
-       kip->security = NULL;
 }
 
 int security_shm_associate(struct shmid_kernel *shp, int shmflg)
@@ -1658,20 +1648,12 @@ int security_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmfl
 
 int security_sem_alloc(struct sem_array *sma)
 {
-       int rc = lsm_ipc_alloc(&sma->sem_perm);
-
-       if (rc)
-               return rc;
        return call_int_hook(sem_alloc_security, 0, sma);
 }
 
 void security_sem_free(struct sem_array *sma)
 {
-       struct kern_ipc_perm *kip = &sma->sem_perm;
-
        call_void_hook(sem_free_security, sma);
-       kfree(kip->security);
-       kip->security = NULL;
 }
 
 int security_sem_associate(struct sem_array *sma, int semflg)
@@ -1701,6 +1683,9 @@ EXPORT_SYMBOL(security_d_instantiate);
 int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
                                char **value)
 {
+#ifdef CONFIG_SECURITY_STACKING
+       char *speclsm = lsm_of_task(p);
+#endif
        struct security_hook_list *hp;
        char *vp;
        char *cp = NULL;
@@ -1743,13 +1728,22 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
                if (rc > 0)
                        return strlen(*value);
                return rc;
+       } else if (strcmp(name, "display_lsm") == 0) {
+               *value = kstrdup(current->security, GFP_KERNEL);
+               if (*value == NULL)
+                       return -ENOMEM;
+               return strlen(*value);
        }
 
        list_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
                if (lsm != NULL && strcmp(lsm, hp->lsm))
                        continue;
+#ifdef CONFIG_SECURITY_STACKING
+               if (!lsm && speclsm && speclsm[0] && strcmp(speclsm, hp->lsm))
+                       continue;
+#endif
                rc = hp->hook.getprocattr(p, name, value);
-               if (rc != -ENOENT)
+               if (rc != -ENOSYS)
                        return rc;
        }
        return -EINVAL;
@@ -1758,12 +1752,20 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
 int security_setprocattr(const char *lsm, const char *name, void *value,
                         size_t size)
 {
+#ifdef CONFIG_SECURITY_STACKING
+       char *speclsm = lsm_of_task(current);
+       struct lsm_value *lsm_value = NULL;
+       int count;
+#else
+       char *tvalue;
+#endif
        struct security_hook_list *hp;
        int rc;
-       char *local;
+       char *temp;
        char *cp;
-       int slen;
-       int failed = 0;
+
+       if (!size)
+               return -EINVAL;
 
        /*
         * If lsm is NULL look at all the modules to find one
@@ -1773,70 +1775,86 @@ int security_setprocattr(const char *lsm, const char *name, void *value,
         * "context" is handled directly here.
         */
        if (strcmp(name, "context") == 0) {
-               /*
-                * First verify that the input is acceptable.
-                * lsm1='v1'lsm2='v2'lsm3='v3'
-                *
-                * A note on the use of strncmp() below.
-                * The check is for the substring at the beginning of cp.
-                * The kzalloc of size + 1 ensures a terminated string.
-                */
                rc = -EINVAL;
-               local = kzalloc(size + 1, GFP_KERNEL);
-               memcpy(local, value, size);
-               cp = local;
-               list_for_each_entry(hp, &security_hook_heads.setprocattr,
-                                       list) {
-                       if (lsm != NULL && strcmp(lsm, hp->lsm))
-                               continue;
-                       if (cp[0] == ',') {
-                               if (cp == local)
-                                       goto free_out;
-                               cp++;
-                       }
-                       slen = strlen(hp->lsm);
-                       if (strncmp(cp, hp->lsm, slen))
-                               goto free_out;
-                       cp += slen;
-                       if (cp[0] != '=' || cp[1] != '\'' || cp[2] == '\'')
-                               goto free_out;
-                       for (cp += 2; cp[0] != '\''; cp++)
-                               if (cp[0] == '\0')
-                                       goto free_out;
-                       cp++;
+               temp = kmemdup(value, size + 1, GFP_KERNEL);
+               if (!temp)
+                       return -ENOMEM;
+
+               temp[size] = '\0';
+               cp = strrchr(temp, '\'');
+               if (!cp)
+                       goto free_out;
+
+               cp[1] = '\0';
+#ifdef CONFIG_SECURITY_STACKING
+               lsm_value = kzalloc(sizeof(*lsm_value) * LSM_MAX_MAJOR,
+                                       GFP_KERNEL);
+               if (!lsm_value) {
+                       rc = -ENOMEM;
+                       goto free_out;
                }
 
-               cp = local;
+               count = lsm_parse_context(temp, lsm_value);
+               if (count <= 0)
+                       goto free_out;
+
+               for (count--; count >= 0; count--) {
+                       list_for_each_entry(hp,
+                               &security_hook_heads.setprocattr, list) {
+
+                               if (lsm && strcmp(lsm, hp->lsm))
+                                       continue;
+                               if (!strcmp(hp->lsm, lsm_value[count].lsm)) {
+                                       rc = hp->hook.setprocattr("context",
+                                               lsm_value[count].data,
+                                               strlen(lsm_value[count].data));
+                                       break;
+                               }
+                       }
+                       if (rc < 0 || (lsm && rc >0))
+                               break;
+               }
+#else /* CONFIG_SECURITY_STACKING */
+               cp = strstr(temp, "='");
+               if (!cp)
+                       goto free_out;
+               *cp = '\0';
+               tvalue = strchr(cp + 2, '\'');
+               if (!tvalue)
+                       goto free_out;
                list_for_each_entry(hp, &security_hook_heads.setprocattr,
-                                       list) {
-                       if (lsm != NULL && strcmp(lsm, hp->lsm))
-                               continue;
-                       if (cp[0] == ',')
-                               cp++;
-                       cp += strlen(hp->lsm) + 2;
-                       for (slen = 0; cp[slen] != '\''; slen++)
-                               ;
-                       cp[slen] = '\0';
-
-                       rc = hp->hook.setprocattr("context", cp, slen);
-                       if (rc < 0)
-                               failed = rc;
-                       cp += slen + 1;
+                                                               list) {
+                       if (lsm == NULL || !strcmp(lsm, hp->lsm)) {
+                               rc = hp->hook.setprocattr(name, tvalue, size);
+                               break;
+                       }
                }
-               if (failed != 0)
-                       rc = failed;
-               else
-                       rc = size;
+#endif /* CONFIG_SECURITY_STACKING */
 free_out:
-               kfree(local);
+               kfree(temp);
+#ifdef CONFIG_SECURITY_STACKING
+               kfree(lsm_value);
+#endif
+               if (rc >= 0)
+                       return size;
                return rc;
+       } else if (strcmp(name, "display_lsm") == 0) {
+#ifdef CONFIG_SECURITY_STACKING
+               if (set_lsm_of_current(value, size))
+                       return size;
+#endif
+               return -EINVAL;
        }
 
        list_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
-               if (lsm != NULL && strcmp(lsm, hp->lsm))
+               if (lsm && strcmp(lsm, hp->lsm))
                        continue;
+#ifdef CONFIG_SECURITY_STACKING
+               if (!lsm && speclsm && speclsm[0] && strcmp(speclsm, hp->lsm))
+                       continue;
+#endif
                rc = hp->hook.setprocattr(name, value, size);
-               if (rc != -ENOENT)
+               if (rc)
                        return rc;
        }
        return -EINVAL;
@@ -1869,7 +1887,19 @@ EXPORT_SYMBOL(security_secctx_to_secid);
 
 void security_release_secctx(char *secdata, u32 seclen)
 {
-       call_void_hook(release_secctx, secdata, seclen);
+#ifdef CONFIG_SECURITY_STACKING
+       char *speclsm = lsm_of_task(current);
+#endif
+       struct security_hook_list *hp;
+
+       list_for_each_entry(hp, &security_hook_heads.release_secctx, list) {
+#ifdef CONFIG_SECURITY_STACKING
+               if (speclsm[0] && strcmp(hp->lsm, speclsm))
+                       continue;
+#endif
+               hp->hook.release_secctx(secdata, seclen);
+               break;
+       }
 }
 EXPORT_SYMBOL(security_release_secctx);
 
@@ -1988,8 +2018,21 @@ EXPORT_SYMBOL(security_sock_rcv_skb);
 int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
                                      int __user *optlen, unsigned len)
 {
+#ifdef CONFIG_SECURITY_STACKING
+       struct security_hook_list *hp;
+       char *lsm = lsm_of_task(current);
+
+       list_for_each_entry(hp, &security_hook_heads.socket_getpeersec_stream,
+                                                                       list) {
+               if (!lsm || !lsm[0] || !strcmp(lsm, hp->lsm))
+                       return hp->hook.socket_getpeersec_stream(sock, optval,
+                                               optlen, len);
+       }
+       return -ENOPROTOOPT;
+#else
        return call_int_hook(socket_getpeersec_stream, -ENOPROTOOPT, sock,
                                optval, optlen, len);
+#endif
 }
 
 int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
@@ -2001,18 +2044,12 @@ EXPORT_SYMBOL(security_socket_getpeersec_dgram);
 
 int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
 {
-       int rc = lsm_sock_alloc(sk, priority);
-
-       if (rc)
-               return rc;
        return call_int_hook(sk_alloc_security, 0, sk, family, priority);
 }
 
 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)
@@ -2242,18 +2279,12 @@ EXPORT_SYMBOL(security_skb_classify_flow);
 int security_key_alloc(struct key *key, const struct cred *cred,
                       unsigned long flags)
 {
-       int rc = lsm_key_alloc(key);
-
-       if (rc)
-               return rc;
        return call_int_hook(key_alloc, 0, key, cred, flags);
 }
 
 void security_key_free(struct key *key)
 {
        call_void_hook(key_free, key);
-       kfree(key->security);
-       key->security = NULL;
 }
 
 int security_key_permission(key_ref_t key_ref,