From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
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 Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@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 2ea0f2f65ab6..4a38310e6e7e 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, @@ -22,8 +26,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 * @@ -32,14 +43,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 8953ac6412c3..48716d058ed1 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,13 +111,36 @@ 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; @@ -106,7 +154,7 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode) if (!iint_cache) panic("%s: lsm=integrity required.\n", __func__);
- iint = integrity_iint_find(inode); + iint = integrity_iint_rb_find(iint_tree, inode); if (iint) return iint;
@@ -114,9 +162,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, @@ -131,33 +179,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 77e6819e8db8..05f6eabef9fe 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -184,11 +184,20 @@ static inline void ima_load_digest_lists(void) } #endif
+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);
@@ -199,6 +208,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