2011-03-09 22:13:22 +03:00
/*
* 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 ;
2012-02-08 23:15:42 +04:00
static DEFINE_RWLOCK ( integrity_iint_lock ) ;
2011-03-09 22:13:22 +03:00
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 ;
2012-02-08 23:15:42 +04:00
read_lock ( & integrity_iint_lock ) ;
2011-03-09 22:13:22 +03:00
iint = __integrity_iint_find ( inode ) ;
2012-02-08 23:15:42 +04:00
read_unlock ( & integrity_iint_lock ) ;
2011-03-09 22:13:22 +03:00
return iint ;
}
static void iint_free ( struct integrity_iint_cache * iint )
{
iint - > version = 0 ;
iint - > flags = 0UL ;
2012-12-04 02:08:11 +04:00
iint - > ima_file_status = INTEGRITY_UNKNOWN ;
iint - > ima_mmap_status = INTEGRITY_UNKNOWN ;
iint - > ima_bprm_status = INTEGRITY_UNKNOWN ;
iint - > ima_module_status = INTEGRITY_UNKNOWN ;
2011-08-15 16:30:11 +04:00
iint - > evm_status = INTEGRITY_UNKNOWN ;
2011-03-09 22:13:22 +03:00
kmem_cache_free ( iint_cache , iint ) ;
}
/**
2011-10-19 13:04:40 +04:00
* integrity_inode_get - find or allocate an iint associated with an inode
2011-03-09 22:13:22 +03:00
* @ inode : pointer to the inode
2011-10-19 13:04:40 +04:00
* @ return : allocated iint
*
* Caller must lock i_mutex
2011-03-09 22:13:22 +03:00
*/
2011-10-19 13:04:40 +04:00
struct integrity_iint_cache * integrity_inode_get ( struct inode * inode )
2011-03-09 22:13:22 +03:00
{
struct rb_node * * p ;
2011-10-19 13:04:40 +04:00
struct rb_node * node , * parent = NULL ;
struct integrity_iint_cache * iint , * test_iint ;
2011-03-09 22:13:22 +03:00
2011-10-19 13:04:40 +04:00
iint = integrity_iint_find ( inode ) ;
if ( iint )
return iint ;
2011-03-09 22:13:22 +03:00
2011-10-19 13:04:40 +04:00
iint = kmem_cache_alloc ( iint_cache , GFP_NOFS ) ;
if ( ! iint )
return NULL ;
2011-03-09 22:13:22 +03:00
2012-02-08 23:15:42 +04:00
write_lock ( & integrity_iint_lock ) ;
2011-03-09 22:13:22 +03:00
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
2011-10-19 13:04:40 +04:00
p = & ( * p ) - > rb_right ;
2011-03-09 22:13:22 +03:00
}
2011-10-19 13:04:40 +04:00
iint - > inode = inode ;
node = & iint - > rb_node ;
2011-03-09 22:13:22 +03:00
inode - > i_flags | = S_IMA ;
2011-10-19 13:04:40 +04:00
rb_link_node ( node , parent , p ) ;
rb_insert_color ( node , & integrity_iint_tree ) ;
2011-03-09 22:13:22 +03:00
2012-02-08 23:15:42 +04:00
write_unlock ( & integrity_iint_lock ) ;
2011-10-19 13:04:40 +04:00
return iint ;
2011-03-09 22:13:22 +03:00
}
/**
* 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 ;
2012-02-08 23:15:42 +04:00
write_lock ( & integrity_iint_lock ) ;
2011-03-09 22:13:22 +03:00
iint = __integrity_iint_find ( inode ) ;
rb_erase ( & iint - > rb_node , & integrity_iint_tree ) ;
2012-02-08 23:15:42 +04:00
write_unlock ( & integrity_iint_lock ) ;
2011-03-09 22:13:22 +03:00
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 ;
2012-12-04 02:08:11 +04:00
iint - > ima_file_status = INTEGRITY_UNKNOWN ;
iint - > ima_mmap_status = INTEGRITY_UNKNOWN ;
iint - > ima_bprm_status = INTEGRITY_UNKNOWN ;
iint - > ima_module_status = INTEGRITY_UNKNOWN ;
2011-05-06 12:34:17 +04:00
iint - > evm_status = INTEGRITY_UNKNOWN ;
2011-03-09 22:13:22 +03:00
}
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 ) ;