diff mbox series

[RFC,07/30] ima: Extend the APIs in the integrity subsystem

Message ID 20200818152037.11869-8-krzysztof.struczynski@huawei.com
State New
Headers show
Series ima: Introduce IMA namespace | expand

Commit Message

Krzysztof Struczynski Aug. 18, 2020, 3:20 p.m. UTC
From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Inode integrity cache will be maintained per ima namespace. Add new
functions that allow to specify the iint tree to use.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/integrity.h      |  31 ++++++++
 security/integrity/iint.c      | 126 ++++++++++++++++++++++++++-------
 security/integrity/integrity.h |  11 +++
 3 files changed, 144 insertions(+), 24 deletions(-)
diff mbox series

Patch

diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index 2271939c5c31..5019fedaa17a 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -8,6 +8,10 @@ 
 #define _LINUX_INTEGRITY_H
 
 #include <linux/fs.h>
+#include <linux/rwlock_types.h>
+
+struct rb_root;
+struct integrity_iint_tree;
 
 enum integrity_status {
 	INTEGRITY_PASS = 0,
@@ -21,8 +25,15 @@  enum integrity_status {
 /* List of EVM protected security xattrs */
 #ifdef CONFIG_INTEGRITY
 extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode);
+extern struct integrity_iint_cache *integrity_inode_rb_get(struct
+							   integrity_iint_tree
+							   *iint_tree,
+							   struct inode *inode);
 extern void integrity_inode_free(struct inode *inode);
+extern void integrity_inode_rb_free(struct integrity_iint_tree *iint_tree,
+				    struct inode *inode);
 extern void __init integrity_load_keys(void);
+extern void integrity_iint_tree_free(struct integrity_iint_tree *iint_tree);
 
 #else
 static inline struct integrity_iint_cache *
@@ -31,14 +42,34 @@  static inline struct integrity_iint_cache *
 	return NULL;
 }
 
+static inline struct integrity_iint_cache *
+				integrity_inode_rb_get(struct
+						       integrity_iint_tree
+						       *iint_tree,
+						       struct inode *inode)
+{
+	return NULL;
+}
+
 static inline void integrity_inode_free(struct inode *inode)
 {
 	return;
 }
 
+static inline void integrity_inode_rb_free(struct integrity_iint_tree
+					   *iint_tree,
+					   struct inode *inode)
+{
+}
+
 static inline void integrity_load_keys(void)
 {
 }
+
+static inline void integrity_iint_tree_free(struct integrity_iint_tree
+					    *iint_tree)
+{
+}
 #endif /* CONFIG_INTEGRITY */
 
 #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 1d20003243c3..34a36f298f92 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -21,19 +21,29 @@ 
 #include <linux/lsm_hooks.h>
 #include "integrity.h"
 
-static struct rb_root integrity_iint_tree = RB_ROOT;
-static DEFINE_RWLOCK(integrity_iint_lock);
+struct integrity_iint_tree init_iint_tree = {
+	.lock = __RW_LOCK_UNLOCKED(init_iint_tree.lock),
+	.root = RB_ROOT
+};
+
 static struct kmem_cache *iint_cache __read_mostly;
 
 struct dentry *integrity_dir;
 
 /*
- * __integrity_iint_find - return the iint associated with an inode
+ * __integrity_iint_rb_find - return the iint associated with an inode
+ * @iint_rb_root: pointer to the root of the iint tree
+ * @inode: pointer to the inode
+ * @return: pointer to the iint if found, NULL otherwise
  */
-static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
+static struct integrity_iint_cache *
+				__integrity_iint_rb_find(const struct rb_root
+							 *iint_rb_root,
+							 const struct inode
+							 *inode)
 {
 	struct integrity_iint_cache *iint;
-	struct rb_node *n = integrity_iint_tree.rb_node;
+	struct rb_node *n = iint_rb_root->rb_node;
 
 	while (n) {
 		iint = rb_entry(n, struct integrity_iint_cache, rb_node);
@@ -52,22 +62,37 @@  static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
 }
 
 /*
- * integrity_iint_find - return the iint associated with an inode
+ * integrity_iint_rb_find - return the iint associated with an inode
+ * @iint_tree: pointer to the iint tree root node and the associated lock
+ * @inode: pointer to the inode
+ * @return: pointer to the iint if found, NULL otherwise
  */
-struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
+struct integrity_iint_cache *integrity_iint_rb_find(struct integrity_iint_tree
+						    *iint_tree,
+						    const struct inode *inode)
 {
 	struct integrity_iint_cache *iint;
 
 	if (!IS_IMA(inode))
 		return NULL;
 
-	read_lock(&integrity_iint_lock);
-	iint = __integrity_iint_find(inode);
-	read_unlock(&integrity_iint_lock);
+	read_lock(&iint_tree->lock);
+	iint = __integrity_iint_rb_find(&iint_tree->root, inode);
+	read_unlock(&iint_tree->lock);
 
 	return iint;
 }
 
+/*
+ * integrity_iint_find - return the iint associated with an inode
+ * @inode: pointer to the inode
+ * @return: pointer to the iint if found, NULL otherwise
+ */
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
+{
+	return integrity_iint_rb_find(&init_iint_tree, inode);
+}
+
 static void iint_free(struct integrity_iint_cache *iint)
 {
 	kfree(iint->ima_hash);
@@ -86,19 +111,42 @@  static void iint_free(struct integrity_iint_cache *iint)
 }
 
 /**
- * integrity_inode_get - find or allocate an iint associated with an inode
+ * integrity_iint_tree_free - traverse the tree and free all nodes
+ * @iint_tree: pointer to the iint tree root node and the associated lock
+ *
+ * The tree cannot be in use. This function should be called only from the
+ * destructor when no locks are required.
+ */
+void integrity_iint_tree_free(struct integrity_iint_tree *iint_tree)
+{
+	struct rb_root *root = &iint_tree->root;
+	struct integrity_iint_cache *iint, *tmp;
+
+	rbtree_postorder_for_each_entry_safe(iint, tmp, root, rb_node) {
+		iint_free(iint);
+	}
+
+	iint_tree->root = RB_ROOT;
+}
+
+/**
+ * integrity_inode_rb_get - find or allocate an iint associated with an inode
+ * @iint_tree: pointer to the iint tree root node and the associated lock
  * @inode: pointer to the inode
- * @return: allocated iint
+ * @return: pointer to the existing iint if found, pointer to the allocated iint
+ * if it didn't exist, NULL in case of error
  *
  * Caller must lock i_mutex
  */
-struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
+struct integrity_iint_cache *integrity_inode_rb_get(struct integrity_iint_tree
+						    *iint_tree,
+						    struct inode *inode)
 {
 	struct rb_node **p;
 	struct rb_node *node, *parent = NULL;
 	struct integrity_iint_cache *iint, *test_iint;
 
-	iint = integrity_iint_find(inode);
+	iint = integrity_iint_rb_find(iint_tree, inode);
 	if (iint)
 		return iint;
 
@@ -106,9 +154,9 @@  struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
 	if (!iint)
 		return NULL;
 
-	write_lock(&integrity_iint_lock);
+	write_lock(&iint_tree->lock);
 
-	p = &integrity_iint_tree.rb_node;
+	p = &iint_tree->root.rb_node;
 	while (*p) {
 		parent = *p;
 		test_iint = rb_entry(parent, struct integrity_iint_cache,
@@ -123,33 +171,63 @@  struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
 	node = &iint->rb_node;
 	inode->i_flags |= S_IMA;
 	rb_link_node(node, parent, p);
-	rb_insert_color(node, &integrity_iint_tree);
+	rb_insert_color(node, &iint_tree->root);
 
-	write_unlock(&integrity_iint_lock);
+	write_unlock(&iint_tree->lock);
 	return iint;
 }
 
 /**
- * integrity_inode_free - called on security_inode_free
+ * integrity_inode_get - find or allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ * @return: pointer to the existing iint if found, pointer to the allocated iint
+ * if it didn't exist, NULL in case of error
+ *
+ * Caller must lock i_mutex
+ */
+struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
+{
+	return integrity_inode_rb_get(&init_iint_tree, inode);
+}
+
+/**
+ * integrity_inode_rb_free - called on security_inode_free
+ * @iint_tree: pointer to the iint tree root node and the associated lock
  * @inode: pointer to the inode
  *
  * Free the integrity information(iint) associated with an inode.
  */
-void integrity_inode_free(struct inode *inode)
+void integrity_inode_rb_free(struct integrity_iint_tree *iint_tree,
+			     struct inode *inode)
 {
 	struct integrity_iint_cache *iint;
 
 	if (!IS_IMA(inode))
 		return;
 
-	write_lock(&integrity_iint_lock);
-	iint = __integrity_iint_find(inode);
-	rb_erase(&iint->rb_node, &integrity_iint_tree);
-	write_unlock(&integrity_iint_lock);
+	write_lock(&iint_tree->lock);
+	iint = __integrity_iint_rb_find(&iint_tree->root, inode);
+	if (!iint) {
+		write_unlock(&iint_tree->lock);
+		return;
+	}
+	rb_erase(&iint->rb_node, &iint_tree->root);
+	write_unlock(&iint_tree->lock);
 
 	iint_free(iint);
 }
 
+/**
+ * integrity_inode_free - called on security_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void integrity_inode_free(struct inode *inode)
+{
+	integrity_inode_rb_free(&init_iint_tree, inode);
+}
+
 static void init_once(void *foo)
 {
 	struct integrity_iint_cache *iint = foo;
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 413c803c5208..721d1850e4f9 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -140,11 +140,20 @@  struct integrity_iint_cache {
 	struct ima_digest_data *ima_hash;
 };
 
+struct integrity_iint_tree {
+	rwlock_t lock;
+	struct rb_root root;
+};
+
 /* rbtree tree calls to lookup, insert, delete
  * integrity data associated with an inode.
  */
 struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
 
+struct integrity_iint_cache *integrity_iint_rb_find(struct integrity_iint_tree
+						    *iint_tree,
+						    const struct inode *inode);
+
 int integrity_kernel_read(struct file *file, loff_t offset,
 			  void *addr, unsigned long count);
 
@@ -155,6 +164,8 @@  int integrity_kernel_read(struct file *file, loff_t offset,
 
 extern struct dentry *integrity_dir;
 
+extern struct integrity_iint_tree init_iint_tree;
+
 struct modsig;
 
 #ifdef CONFIG_INTEGRITY_SIGNATURE