diff mbox

[18/25] LSM: Infrastructure managed inode security blob

Message ID 94da3ca1-340f-e980-8330-f5c72c0b47ea@schaufler-ca.com
State New
Headers show

Commit Message

Casey Schaufler Aug. 13, 2016, 8:37 p.m. UTC
Subject: [PATCH 18/25] LSM: Infrastructure managed inode security blob

Move management of the inode security blob from the security
modules to the LSM infrastructure. This requires that the modules
declare the blob size they require, so the module registration
process has to include that. A function lsm_inode_alloc() is
provided so that modules can do early allocation of inode blobs
during module initialization. Module hooks that are no longer
required are removed.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 include/linux/lsm_hooks.h  |  2 ++
 security/security.c        | 34 ++++++++++++++++++++++++++-
 security/selinux/hooks.c   | 32 ++------------------------
 security/smack/smack_lsm.c | 57 ++++++++++++----------------------------------
 4 files changed, 51 insertions(+), 74 deletions(-)
diff mbox

Patch

diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index acc7507..9cc0e49 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1850,6 +1850,7 @@  struct security_hook_list {
 struct lsm_blob_sizes {
 	int	lbs_cred;
 	int	lbs_file;
+	int	lbs_inode;
 };
 
 /*
@@ -1905,6 +1906,7 @@  static inline void loadpin_add_hooks(void) { };
 #endif
 
 extern int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
+extern int lsm_inode_alloc(struct inode *inode);
 
 #ifdef CONFIG_SECURITY
 static inline void lsm_early_cred(struct cred *cred)
diff --git a/security/security.c b/security/security.c
index 8a0c14a..9fc5967 100644
--- a/security/security.c
+++ b/security/security.c
@@ -86,6 +86,7 @@  int __init security_init(void)
 #ifdef CONFIG_SECURITY_STACKING_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);
 #endif
 
 	return 0;
@@ -225,6 +226,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_inode, &blob_sizes.lbs_inode);
 }
 
 /**
@@ -252,6 +254,31 @@  int lsm_file_alloc(struct file *file)
 	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_STACKING_DEBUG
+	if (inode->i_security) {
+		pr_info("%s: Inbound inode blob is not NULL.\n", __func__);
+		return 0;
+	}
+#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;
+}
+
 /*
  * Hook list operation macros.
  *
@@ -500,7 +527,10 @@  EXPORT_SYMBOL(security_sb_parse_opts_str);
 
 int security_inode_alloc(struct inode *inode)
 {
-	inode->i_security = NULL;
+	int rc = lsm_inode_alloc(inode);
+
+	if (rc)
+		return rc;
 	return call_int_hook(inode_alloc_security, 0, inode);
 }
 
@@ -508,6 +538,8 @@  void security_inode_free(struct inode *inode)
 {
 	integrity_inode_free(inode);
 	call_void_hook(inode_free_security, inode);
+	kfree(inode->i_security);
+	inode->i_security = NULL;
 }
 
 int security_dentry_init_security(struct dentry *dentry, int mode,
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d6e3b25..7219a6a 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -125,8 +125,6 @@  __setup("selinux=", selinux_enabled_setup);
 int selinux_enabled = 1;
 #endif
 
-static struct kmem_cache *sel_inode_cache;
-
 /**
  * selinux_secmark_enabled - Check to see if SECMARK is currently enabled
  *
@@ -220,20 +218,15 @@  static inline u32 current_sid(void)
 
 static int inode_alloc_security(struct inode *inode)
 {
-	struct inode_security_struct *isec;
+	struct inode_security_struct *isec = selinux_inode(inode);
 	u32 sid = current_sid();
 
-	isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
-	if (!isec)
-		return -ENOMEM;
-
 	mutex_init(&isec->lock);
 	INIT_LIST_HEAD(&isec->list);
 	isec->inode = inode;
 	isec->sid = SECINITSID_UNLABELED;
 	isec->sclass = SECCLASS_FILE;
 	isec->task_sid = sid;
-	inode->i_security = isec;
 
 	return 0;
 }
@@ -311,14 +304,6 @@  static struct inode_security_struct *backing_inode_security(struct dentry *dentr
 	return selinux_inode(inode);
 }
 
-static void inode_free_rcu(struct rcu_head *head)
-{
-	struct inode_security_struct *isec;
-
-	isec = container_of(head, struct inode_security_struct, rcu);
-	kmem_cache_free(sel_inode_cache, isec);
-}
-
 static void inode_free_security(struct inode *inode)
 {
 	struct inode_security_struct *isec = selinux_inode(inode);
@@ -340,17 +325,6 @@  static void inode_free_security(struct inode *inode)
 		list_del_init(&isec->list);
 		spin_unlock(&sbsec->isec_lock);
 	}
-
-	/*
-	 * The inode may still be referenced in a path walk and
-	 * a call to selinux_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 selinux_inode(inode) pointer intact.
-	 * The inode will be freed after the RCU grace period too.
-	 */
-	call_rcu(&isec->rcu, inode_free_rcu);
 }
 
 static int file_alloc_security(struct file *file)
@@ -5991,6 +5965,7 @@  static int selinux_key_getsecurity(struct key *key, char **_buffer)
 struct lsm_blob_sizes selinux_blob_sizes = {
 	.lbs_cred = sizeof(struct task_security_struct),
 	.lbs_file = sizeof(struct file_security_struct),
+	.lbs_inode = sizeof(struct inode_security_struct),
 };
 
 static struct security_hook_list selinux_hooks[] = {
@@ -6235,9 +6210,6 @@  static __init int selinux_init(void)
 
 	default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
 
-	sel_inode_cache = kmem_cache_create("selinux_inode_security",
-					    sizeof(struct inode_security_struct),
-					    0, SLAB_PANIC, NULL);
 	avc_init();
 
 	security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index c078933..6dea0a7 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -291,33 +291,16 @@  static struct smack_known *smk_fetch(const char *name, struct inode *ip,
  * @skp: a pointer to the Smack label entry to use in the blob
  *
  */
-static void init_inode_smack(struct inode_smack *isp, struct smack_known *skp)
+static void init_inode_smack(struct inode *inode, struct smack_known *skp)
 {
+	struct inode_smack *isp = smack_inode(inode);
+
 	isp->smk_inode = skp;
 	isp->smk_flags = 0;
 	mutex_init(&isp->smk_lock);
 }
 
 /**
- * new_inode_smack - allocate an inode security blob
- * @skp: a pointer to the Smack label entry to use in the blob
- *
- * Returns the new blob or NULL if there's no memory available
- */
-static struct inode_smack *new_inode_smack(struct smack_known *skp)
-{
-	struct inode_smack *isp;
-
-	isp = kmem_cache_zalloc(smack_inode_cache, GFP_NOFS);
-	if (isp == NULL)
-		return NULL;
-
-	init_inode_smack(isp, skp);
-
-	return isp;
-}
-
-/**
  * init_task_smack - initialize a task security blob
  * @tsp: blob to initialize
  * @task: a pointer to the Smack label for the running task
@@ -841,14 +824,16 @@  static int smack_set_mnt_opts(struct super_block *sb,
 	/*
 	 * Initialize the root inode.
 	 */
-	isp = smack_inode(inode);
-	if (isp == NULL) {
-		isp = new_inode_smack(sp->smk_root);
-		if (isp == NULL)
-			return -ENOMEM;
-		inode->i_security = isp;
-	} else
+	if (inode->i_security == NULL) {
+		i = lsm_inode_alloc(inode);
+		if (i)
+			return i;
+		isp = smack_inode(inode);
+		init_inode_smack(inode, sp->smk_root);
+	} else {
+		isp = smack_inode(inode);
 		isp->smk_inode = sp->smk_root;
+	}
 
 	if (transmute)
 		isp->smk_flags |= SMK_INODE_TRANSMUTE;
@@ -1006,25 +991,11 @@  static int smack_inode_alloc_security(struct inode *inode)
 {
 	struct smack_known *skp = smk_of_current();
 
-	inode->i_security = new_inode_smack(skp);
-	if (inode->i_security == NULL)
-		return -ENOMEM;
+	init_inode_smack(inode, skp);
 	return 0;
 }
 
 /**
- * smack_inode_free_security - free an inode blob
- * @inode: the inode with a blob
- *
- * Clears the blob pointer in inode
- */
-static void smack_inode_free_security(struct inode *inode)
-{
-	kmem_cache_free(smack_inode_cache, smack_inode(inode));
-	inode->i_security = NULL;
-}
-
-/**
  * smack_inode_init_security - copy out the smack from an inode
  * @inode: the newly created inode
  * @dir: containing directory object
@@ -4597,6 +4568,7 @@  static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
 struct lsm_blob_sizes smack_blob_sizes = {
 	.lbs_cred = sizeof(struct task_smack),
 	.lbs_file = sizeof(struct smack_known *),
+	.lbs_inode = sizeof(struct inode_smack),
 };
 
 static struct security_hook_list smack_hooks[] = {
@@ -4617,7 +4589,6 @@  static struct security_hook_list smack_hooks[] = {
 	LSM_HOOK_INIT(bprm_secureexec, smack_bprm_secureexec),
 
 	LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security),
-	LSM_HOOK_INIT(inode_free_security, smack_inode_free_security),
 	LSM_HOOK_INIT(inode_init_security, smack_inode_init_security),
 	LSM_HOOK_INIT(inode_link, smack_inode_link),
 	LSM_HOOK_INIT(inode_unlink, smack_inode_unlink),