2009-02-04 09:06:59 -05:00
/*
* Copyright ( C ) 2005 , 2006 , 2007 , 2008 IBM Corporation
*
* Authors :
* Kylene Hall < kjhall @ us . ibm . com >
* Reiner Sailer < sailer @ us . ibm . com >
* 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 : ima_fs . c
* implemenents security file system for reporting
* current measurement list and IMA statistics
*/
2009-05-12 15:13:55 -04:00
# include <linux/fcntl.h>
2009-02-04 09:06:59 -05:00
# include <linux/module.h>
# include <linux/seq_file.h>
# include <linux/rculist.h>
# include <linux/rcupdate.h>
2009-02-04 09:07:00 -05:00
# include <linux/parser.h>
2009-02-04 09:06:59 -05:00
# include "ima.h"
2009-02-04 09:07:00 -05:00
static int valid_policy = 1 ;
2009-02-04 09:06:59 -05:00
# define TMPBUFLEN 12
static ssize_t ima_show_htable_value ( char __user * buf , size_t count ,
loff_t * ppos , atomic_long_t * val )
{
char tmpbuf [ TMPBUFLEN ] ;
ssize_t len ;
len = scnprintf ( tmpbuf , TMPBUFLEN , " %li \n " , atomic_long_read ( val ) ) ;
return simple_read_from_buffer ( buf , count , ppos , tmpbuf , len ) ;
}
static ssize_t ima_show_htable_violations ( struct file * filp ,
char __user * buf ,
size_t count , loff_t * ppos )
{
return ima_show_htable_value ( buf , count , ppos , & ima_htable . violations ) ;
}
2009-10-01 15:43:56 -07:00
static const struct file_operations ima_htable_violations_ops = {
2009-02-04 09:06:59 -05:00
. read = ima_show_htable_violations
} ;
static ssize_t ima_show_measurements_count ( struct file * filp ,
char __user * buf ,
size_t count , loff_t * ppos )
{
return ima_show_htable_value ( buf , count , ppos , & ima_htable . len ) ;
}
2009-10-01 15:43:56 -07:00
static const struct file_operations ima_measurements_count_ops = {
2009-02-04 09:06:59 -05:00
. read = ima_show_measurements_count
} ;
/* returns pointer to hlist_node */
static void * ima_measurements_start ( struct seq_file * m , loff_t * pos )
{
loff_t l = * pos ;
struct ima_queue_entry * qe ;
/* we need a lock since pos could point beyond last element */
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( qe , & ima_measurements , later ) {
if ( ! l - - ) {
rcu_read_unlock ( ) ;
return qe ;
}
}
rcu_read_unlock ( ) ;
return NULL ;
}
static void * ima_measurements_next ( struct seq_file * m , void * v , loff_t * pos )
{
struct ima_queue_entry * qe = v ;
/* lock protects when reading beyond last element
* against concurrent list - extension
*/
rcu_read_lock ( ) ;
2009-04-14 20:17:16 +02:00
qe = list_entry_rcu ( qe - > later . next ,
struct ima_queue_entry , later ) ;
2009-02-04 09:06:59 -05:00
rcu_read_unlock ( ) ;
( * pos ) + + ;
return ( & qe - > later = = & ima_measurements ) ? NULL : qe ;
}
static void ima_measurements_stop ( struct seq_file * m , void * v )
{
}
static void ima_putc ( struct seq_file * m , void * data , int datalen )
{
while ( datalen - - )
seq_putc ( m , * ( char * ) data + + ) ;
}
/* print format:
* 32 bit - le = pcr #
* char [ 20 ] = template digest
* 32 bit - le = template name size
* char [ n ] = template name
* eventdata [ n ] = template specific data
*/
static int ima_measurements_show ( struct seq_file * m , void * v )
{
/* the list never shrinks, so we don't need a lock here */
struct ima_queue_entry * qe = v ;
struct ima_template_entry * e ;
int namelen ;
u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX ;
/* get entry */
e = qe - > entry ;
if ( e = = NULL )
return - 1 ;
/*
* 1 st : PCRIndex
* PCR used is always the same ( config option ) in
* little - endian format
*/
ima_putc ( m , & pcr , sizeof pcr ) ;
/* 2nd: template digest */
ima_putc ( m , e - > digest , IMA_DIGEST_SIZE ) ;
/* 3rd: template name size */
namelen = strlen ( e - > template_name ) ;
ima_putc ( m , & namelen , sizeof namelen ) ;
/* 4th: template name */
2009-02-11 11:12:28 -05:00
ima_putc ( m , ( void * ) e - > template_name , namelen ) ;
2009-02-04 09:06:59 -05:00
/* 5th: template specific data */
ima_template_show ( m , ( struct ima_template_data * ) & e - > template ,
IMA_SHOW_BINARY ) ;
return 0 ;
}
2009-09-22 16:43:43 -07:00
static const struct seq_operations ima_measurments_seqops = {
2009-02-04 09:06:59 -05:00
. start = ima_measurements_start ,
. next = ima_measurements_next ,
. stop = ima_measurements_stop ,
. show = ima_measurements_show
} ;
static int ima_measurements_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & ima_measurments_seqops ) ;
}
2009-10-01 15:43:56 -07:00
static const struct file_operations ima_measurements_ops = {
2009-02-04 09:06:59 -05:00
. open = ima_measurements_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
static void ima_print_digest ( struct seq_file * m , u8 * digest )
{
int i ;
for ( i = 0 ; i < IMA_DIGEST_SIZE ; i + + )
seq_printf ( m , " %02x " , * ( digest + i ) ) ;
}
void ima_template_show ( struct seq_file * m , void * e , enum ima_show_type show )
{
struct ima_template_data * entry = e ;
int namelen ;
switch ( show ) {
case IMA_SHOW_ASCII :
ima_print_digest ( m , entry - > digest ) ;
seq_printf ( m , " %s \n " , entry - > file_name ) ;
break ;
case IMA_SHOW_BINARY :
ima_putc ( m , entry - > digest , IMA_DIGEST_SIZE ) ;
namelen = strlen ( entry - > file_name ) ;
ima_putc ( m , & namelen , sizeof namelen ) ;
ima_putc ( m , entry - > file_name , namelen ) ;
default :
break ;
}
}
/* print in ascii */
static int ima_ascii_measurements_show ( struct seq_file * m , void * v )
{
/* the list never shrinks, so we don't need a lock here */
struct ima_queue_entry * qe = v ;
struct ima_template_entry * e ;
/* get entry */
e = qe - > entry ;
if ( e = = NULL )
return - 1 ;
/* 1st: PCR used (config option) */
seq_printf ( m , " %2d " , CONFIG_IMA_MEASURE_PCR_IDX ) ;
/* 2nd: SHA1 template hash */
ima_print_digest ( m , e - > digest ) ;
/* 3th: template name */
seq_printf ( m , " %s " , e - > template_name ) ;
/* 4th: template specific data */
ima_template_show ( m , ( struct ima_template_data * ) & e - > template ,
IMA_SHOW_ASCII ) ;
return 0 ;
}
2009-09-22 16:43:43 -07:00
static const struct seq_operations ima_ascii_measurements_seqops = {
2009-02-04 09:06:59 -05:00
. start = ima_measurements_start ,
. next = ima_measurements_next ,
. stop = ima_measurements_stop ,
. show = ima_ascii_measurements_show
} ;
static int ima_ascii_measurements_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & ima_ascii_measurements_seqops ) ;
}
2009-10-01 15:43:56 -07:00
static const struct file_operations ima_ascii_measurements_ops = {
2009-02-04 09:06:59 -05:00
. open = ima_ascii_measurements_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
2009-02-04 09:07:00 -05:00
static ssize_t ima_write_policy ( struct file * file , const char __user * buf ,
size_t datalen , loff_t * ppos )
{
char * data ;
int rc ;
if ( datalen > = PAGE_SIZE )
return - ENOMEM ;
if ( * ppos ! = 0 ) {
/* No partial writes. */
return - EINVAL ;
}
data = kmalloc ( datalen + 1 , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
if ( copy_from_user ( data , buf , datalen ) ) {
kfree ( data ) ;
return - EFAULT ;
}
* ( data + datalen ) = ' \0 ' ;
rc = ima_parse_add_rule ( data ) ;
if ( rc < 0 ) {
datalen = - EINVAL ;
valid_policy = 0 ;
}
kfree ( data ) ;
return datalen ;
}
2009-02-04 09:06:59 -05:00
static struct dentry * ima_dir ;
static struct dentry * binary_runtime_measurements ;
static struct dentry * ascii_runtime_measurements ;
static struct dentry * runtime_measurements_count ;
static struct dentry * violations ;
2009-02-04 09:07:00 -05:00
static struct dentry * ima_policy ;
2009-02-04 09:07:01 -05:00
static atomic_t policy_opencount = ATOMIC_INIT ( 1 ) ;
/*
* ima_open_policy : sequentialize access to the policy file
*/
int ima_open_policy ( struct inode * inode , struct file * filp )
{
2009-05-12 15:13:55 -04:00
/* No point in being allowed to open it if you aren't going to write */
if ( ! ( filp - > f_flags & O_WRONLY ) )
return - EACCES ;
2009-02-04 09:07:01 -05:00
if ( atomic_dec_and_test ( & policy_opencount ) )
return 0 ;
return - EBUSY ;
}
2009-02-04 09:07:00 -05:00
/*
* ima_release_policy - start using the new measure policy rules .
*
* Initially , ima_measure points to the default policy rules , now
2009-02-04 09:07:01 -05:00
* point to the new policy rules , and remove the securityfs policy file ,
* assuming a valid policy .
2009-02-04 09:07:00 -05:00
*/
static int ima_release_policy ( struct inode * inode , struct file * file )
{
if ( ! valid_policy ) {
ima_delete_rules ( ) ;
2009-02-04 09:07:01 -05:00
valid_policy = 1 ;
atomic_set ( & policy_opencount , 1 ) ;
2009-02-04 09:07:00 -05:00
return 0 ;
}
ima_update_policy ( ) ;
securityfs_remove ( ima_policy ) ;
ima_policy = NULL ;
return 0 ;
}
2009-10-01 15:43:56 -07:00
static const struct file_operations ima_measure_policy_ops = {
2009-02-04 09:07:01 -05:00
. open = ima_open_policy ,
2009-02-04 09:07:00 -05:00
. write = ima_write_policy ,
. release = ima_release_policy
} ;
2009-02-04 09:06:59 -05:00
2009-05-21 15:43:32 -04:00
int __init ima_fs_init ( void )
2009-02-04 09:06:59 -05:00
{
ima_dir = securityfs_create_dir ( " ima " , NULL ) ;
if ( IS_ERR ( ima_dir ) )
return - 1 ;
binary_runtime_measurements =
securityfs_create_file ( " binary_runtime_measurements " ,
S_IRUSR | S_IRGRP , ima_dir , NULL ,
& ima_measurements_ops ) ;
if ( IS_ERR ( binary_runtime_measurements ) )
goto out ;
ascii_runtime_measurements =
securityfs_create_file ( " ascii_runtime_measurements " ,
S_IRUSR | S_IRGRP , ima_dir , NULL ,
& ima_ascii_measurements_ops ) ;
if ( IS_ERR ( ascii_runtime_measurements ) )
goto out ;
runtime_measurements_count =
securityfs_create_file ( " runtime_measurements_count " ,
S_IRUSR | S_IRGRP , ima_dir , NULL ,
& ima_measurements_count_ops ) ;
if ( IS_ERR ( runtime_measurements_count ) )
goto out ;
violations =
securityfs_create_file ( " violations " , S_IRUSR | S_IRGRP ,
ima_dir , NULL , & ima_htable_violations_ops ) ;
if ( IS_ERR ( violations ) )
goto out ;
2009-02-04 09:07:00 -05:00
ima_policy = securityfs_create_file ( " policy " ,
2009-05-12 15:13:55 -04:00
S_IWUSR ,
2009-02-04 09:07:00 -05:00
ima_dir , NULL ,
& ima_measure_policy_ops ) ;
if ( IS_ERR ( ima_policy ) )
goto out ;
2009-02-04 09:06:59 -05:00
2009-02-04 09:07:00 -05:00
return 0 ;
2009-02-04 09:06:59 -05:00
out :
securityfs_remove ( runtime_measurements_count ) ;
securityfs_remove ( ascii_runtime_measurements ) ;
securityfs_remove ( binary_runtime_measurements ) ;
securityfs_remove ( ima_dir ) ;
2009-02-04 09:07:00 -05:00
securityfs_remove ( ima_policy ) ;
2009-02-04 09:06:59 -05:00
return - 1 ;
}
void __exit ima_fs_cleanup ( void )
{
securityfs_remove ( violations ) ;
securityfs_remove ( runtime_measurements_count ) ;
securityfs_remove ( ascii_runtime_measurements ) ;
securityfs_remove ( binary_runtime_measurements ) ;
securityfs_remove ( ima_dir ) ;
2009-02-04 09:07:00 -05:00
securityfs_remove ( ima_policy ) ;
2009-02-04 09:06:59 -05:00
}