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>
2014-11-05 18:01:12 +03:00
# include <linux/file.h>
# include <linux/uaccess.h>
2011-03-09 22:13:22 +03:00
# 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 ;
/*
* __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 )
{
2013-04-25 11:44:04 +04:00
kfree ( iint - > ima_hash ) ;
iint - > ima_hash = NULL ;
2011-03-09 22:13:22 +03:00
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 ;
2014-03-04 20:04:20 +04:00
memset ( iint , 0 , sizeof ( * iint ) ) ;
2011-03-09 22:13:22 +03:00
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 ) ;
return 0 ;
}
security_initcall ( integrity_iintcache_init ) ;
2014-11-05 18:01:12 +03:00
/*
* integrity_kernel_read - read data from the file
*
* This is a function for reading file content instead of kernel_read ( ) .
* It does not perform locking checks to ensure it cannot be blocked .
* It does not perform security checks because it is irrelevant for IMA .
*
*/
int integrity_kernel_read ( struct file * file , loff_t offset ,
char * addr , unsigned long count )
{
mm_segment_t old_fs ;
char __user * buf = ( char __user * ) addr ;
2014-11-05 18:01:17 +03:00
ssize_t ret ;
2014-11-05 18:01:12 +03:00
if ( ! ( file - > f_mode & FMODE_READ ) )
return - EBADF ;
old_fs = get_fs ( ) ;
set_fs ( get_ds ( ) ) ;
2014-11-05 18:01:17 +03:00
ret = __vfs_read ( file , buf , count , & offset ) ;
2014-11-05 18:01:12 +03:00
set_fs ( old_fs ) ;
2014-11-05 18:01:17 +03:00
2014-11-05 18:01:12 +03:00
return ret ;
}
/*
* integrity_read_file - read entire file content into the buffer
*
* This is function opens a file , allocates the buffer of required
* size , read entire file content to the buffer and closes the file
*
* It is used only by init code .
*
*/
int __init integrity_read_file ( const char * path , char * * data )
{
struct file * file ;
loff_t size ;
char * buf ;
int rc = - EINVAL ;
2014-11-26 17:55:00 +03:00
if ( ! path | | ! * path )
return - EINVAL ;
2014-11-05 18:01:12 +03:00
file = filp_open ( path , O_RDONLY , 0 ) ;
if ( IS_ERR ( file ) ) {
rc = PTR_ERR ( file ) ;
pr_err ( " Unable to open file: %s (%d) " , path , rc ) ;
return rc ;
}
size = i_size_read ( file_inode ( file ) ) ;
if ( size < = 0 )
goto out ;
buf = kmalloc ( size , GFP_KERNEL ) ;
if ( ! buf ) {
rc = - ENOMEM ;
goto out ;
}
rc = integrity_kernel_read ( file , 0 , buf , size ) ;
2015-12-26 01:59:12 +03:00
if ( rc = = size ) {
2014-11-05 18:01:12 +03:00
* data = buf ;
2015-12-26 01:59:12 +03:00
} else {
kfree ( buf ) ;
if ( rc > = 0 )
rc = - EIO ;
}
2014-11-05 18:01:12 +03:00
out :
fput ( file ) ;
return rc ;
}
2014-11-05 18:01:15 +03:00
/*
* integrity_load_keys - load integrity keys hook
*
* Hooks is called from init / main . c : kernel_init_freeable ( )
* when rootfs is ready
*/
void __init integrity_load_keys ( void )
{
ima_load_x509 ( ) ;
2015-10-22 21:26:21 +03:00
evm_load_x509 ( ) ;
2014-11-05 18:01:15 +03:00
}