From: Krzysztof Struczynski <krzysztof.struczyn...@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.struczyn...@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 --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
-- 
2.20.1

Reply via email to