diff options
Diffstat (limited to 'security/integrity/iint.c')
| -rw-r--r-- | security/integrity/iint.c | 172 | 
1 files changed, 172 insertions, 0 deletions
diff --git a/security/integrity/iint.c b/security/integrity/iint.c new file mode 100644 index 00000000000..a521edf4cbd --- /dev/null +++ b/security/integrity/iint.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2008 IBM Corporation + * + * Authors: + * Mimi Zohar <zohar@us.ibm.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * File: integrity_iint.c + *	- implements the integrity hooks: integrity_inode_alloc, + *	  integrity_inode_free + *	- cache integrity information associated with an inode + *	  using a rbtree tree. + */ +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/rbtree.h> +#include "integrity.h" + +static struct rb_root integrity_iint_tree = RB_ROOT; +static DEFINE_RWLOCK(integrity_iint_lock); +static struct kmem_cache *iint_cache __read_mostly; + +int iint_initialized; + +/* + * __integrity_iint_find - return the iint associated with an inode + */ +static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) +{ +	struct integrity_iint_cache *iint; +	struct rb_node *n = integrity_iint_tree.rb_node; + +	while (n) { +		iint = rb_entry(n, struct integrity_iint_cache, rb_node); + +		if (inode < iint->inode) +			n = n->rb_left; +		else if (inode > iint->inode) +			n = n->rb_right; +		else +			break; +	} +	if (!n) +		return NULL; + +	return iint; +} + +/* + * integrity_iint_find - return the iint associated with an inode + */ +struct integrity_iint_cache *integrity_iint_find(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); + +	return iint; +} + +static void iint_free(struct integrity_iint_cache *iint) +{ +	kfree(iint->ima_hash); +	iint->ima_hash = NULL; +	iint->version = 0; +	iint->flags = 0UL; +	iint->ima_file_status = INTEGRITY_UNKNOWN; +	iint->ima_mmap_status = INTEGRITY_UNKNOWN; +	iint->ima_bprm_status = INTEGRITY_UNKNOWN; +	iint->ima_module_status = INTEGRITY_UNKNOWN; +	iint->evm_status = INTEGRITY_UNKNOWN; +	kmem_cache_free(iint_cache, iint); +} + +/** + * integrity_inode_get - find or allocate an iint associated with an inode + * @inode: pointer to the inode + * @return: allocated iint + * + * Caller must lock i_mutex + */ +struct integrity_iint_cache *integrity_inode_get(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); +	if (iint) +		return iint; + +	iint = kmem_cache_alloc(iint_cache, GFP_NOFS); +	if (!iint) +		return NULL; + +	write_lock(&integrity_iint_lock); + +	p = &integrity_iint_tree.rb_node; +	while (*p) { +		parent = *p; +		test_iint = rb_entry(parent, struct integrity_iint_cache, +				     rb_node); +		if (inode < test_iint->inode) +			p = &(*p)->rb_left; +		else +			p = &(*p)->rb_right; +	} + +	iint->inode = inode; +	node = &iint->rb_node; +	inode->i_flags |= S_IMA; +	rb_link_node(node, parent, p); +	rb_insert_color(node, &integrity_iint_tree); + +	write_unlock(&integrity_iint_lock); +	return 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) +{ +	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); + +	iint_free(iint); +} + +static void init_once(void *foo) +{ +	struct integrity_iint_cache *iint = foo; + +	memset(iint, 0, sizeof(*iint)); +	iint->version = 0; +	iint->flags = 0UL; +	iint->ima_file_status = INTEGRITY_UNKNOWN; +	iint->ima_mmap_status = INTEGRITY_UNKNOWN; +	iint->ima_bprm_status = INTEGRITY_UNKNOWN; +	iint->ima_module_status = INTEGRITY_UNKNOWN; +	iint->evm_status = INTEGRITY_UNKNOWN; +} + +static int __init integrity_iintcache_init(void) +{ +	iint_cache = +	    kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache), +			      0, SLAB_PANIC, init_once); +	iint_initialized = 1; +	return 0; +} +security_initcall(integrity_iintcache_init);  | 
