2009-02-04 09:06:58 -05:00
/*
* Copyright ( C ) 2008 IBM Corporation
*
* Author : 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_api . c
* Implements must_measure , collect_measurement , store_measurement ,
* and store_template .
*/
# include <linux/module.h>
# include "ima.h"
2009-02-11 11:12:28 -05:00
static const char * IMA_TEMPLATE_NAME = " ima " ;
2009-02-04 09:06:58 -05:00
/*
* ima_store_template - store ima template measurements
*
* Calculate the hash of a template entry , add the template entry
* to an ordered list of measurement entries maintained inside the kernel ,
* and also update the aggregate integrity value ( maintained inside the
* configured TPM PCR ) over the hashes of the current list of measurement
* entries .
*
* Applications retrieve the current kernel - held measurement list through
* the securityfs entries in / sys / kernel / security / ima . The signed aggregate
* TPM PCR ( called quote ) can be retrieved using a TPM user space library
* and is used to validate the measurement list .
*
* Returns 0 on success , error code otherwise
*/
int ima_store_template ( struct ima_template_entry * entry ,
int violation , struct inode * inode )
{
const char * op = " add_template_measure " ;
const char * audit_cause = " hashing_error " ;
int result ;
memset ( entry - > digest , 0 , sizeof ( entry - > digest ) ) ;
entry - > template_name = IMA_TEMPLATE_NAME ;
entry - > template_len = sizeof ( entry - > template ) ;
if ( ! violation ) {
result = ima_calc_template_hash ( entry - > template_len ,
& entry - > template ,
entry - > digest ) ;
if ( result < 0 ) {
integrity_audit_msg ( AUDIT_INTEGRITY_PCR , inode ,
entry - > template_name , op ,
audit_cause , result , 0 ) ;
return result ;
}
}
result = ima_add_template_entry ( entry , violation , op , inode ) ;
return result ;
}
/*
* ima_add_violation - add violation to measurement list .
*
* Violations are flagged in the measurement list with zero hash values .
* By extending the PCR with 0xFF ' s instead of with zeroes , the PCR
* value is invalidated .
*/
void ima_add_violation ( struct inode * inode , const unsigned char * filename ,
const char * op , const char * cause )
{
struct ima_template_entry * entry ;
int violation = 1 ;
int result ;
/* can overflow, only indicator */
atomic_long_inc ( & ima_htable . violations ) ;
entry = kmalloc ( sizeof ( * entry ) , GFP_KERNEL ) ;
if ( ! entry ) {
result = - ENOMEM ;
goto err_out ;
}
memset ( & entry - > template , 0 , sizeof ( entry - > template ) ) ;
strncpy ( entry - > template . file_name , filename , IMA_EVENT_NAME_LEN_MAX ) ;
result = ima_store_template ( entry , violation , inode ) ;
if ( result < 0 )
kfree ( entry ) ;
err_out :
integrity_audit_msg ( AUDIT_INTEGRITY_PCR , inode , filename ,
op , cause , result , 0 ) ;
}
/**
* ima_must_measure - measure decision based on policy .
* @ inode : pointer to inode to measure
* @ mask : contains the permission mask ( MAY_READ , MAY_WRITE , MAY_EXECUTE )
* @ function : calling function ( PATH_CHECK , BPRM_CHECK , FILE_MMAP )
*
* The policy is defined in terms of keypairs :
* subj = , obj = , type = , func = , mask = , fsmagic =
* subj , obj , and type : are LSM specific .
* func : PATH_CHECK | BPRM_CHECK | FILE_MMAP
* mask : contains the permission mask
* fsmagic : hex value
*
* Must be called with iint - > mutex held .
*
* Return 0 to measure . Return 1 if already measured .
* For matching a DONT_MEASURE policy , no policy , or other
* error , return an error code .
*/
int ima_must_measure ( struct ima_iint_cache * iint , struct inode * inode ,
int mask , int function )
{
int must_measure ;
if ( iint - > flags & IMA_MEASURED )
return 1 ;
must_measure = ima_match_policy ( inode , function , mask ) ;
return must_measure ? 0 : - EACCES ;
}
/*
* ima_collect_measurement - collect file measurement
*
* Calculate the file hash , if it doesn ' t already exist ,
* storing the measurement and i_version in the iint .
*
* Must be called with iint - > mutex held .
*
* Return 0 on success , error code otherwise
*/
int ima_collect_measurement ( struct ima_iint_cache * iint , struct file * file )
{
int result = - EEXIST ;
if ( ! ( iint - > flags & IMA_MEASURED ) ) {
u64 i_version = file - > f_dentry - > d_inode - > i_version ;
memset ( iint - > digest , 0 , IMA_DIGEST_SIZE ) ;
result = ima_calc_hash ( file , iint - > digest ) ;
if ( ! result )
iint - > version = i_version ;
}
return result ;
}
/*
* ima_store_measurement - store file measurement
*
* Create an " ima " template and then store the template by calling
* ima_store_template .
*
* We only get here if the inode has not already been measured ,
* but the measurement could already exist :
* - multiple copies of the same file on either the same or
* different filesystems .
* - the inode was previously flushed as well as the iint info ,
* containing the hashing info .
*
* Must be called with iint - > mutex held .
*/
void ima_store_measurement ( struct ima_iint_cache * iint , struct file * file ,
const unsigned char * filename )
{
const char * op = " add_template_measure " ;
const char * audit_cause = " ENOMEM " ;
int result = - ENOMEM ;
struct inode * inode = file - > f_dentry - > d_inode ;
struct ima_template_entry * entry ;
int violation = 0 ;
entry = kmalloc ( sizeof ( * entry ) , GFP_KERNEL ) ;
if ( ! entry ) {
integrity_audit_msg ( AUDIT_INTEGRITY_PCR , inode , filename ,
op , audit_cause , result , 0 ) ;
return ;
}
memset ( & entry - > template , 0 , sizeof ( entry - > template ) ) ;
memcpy ( entry - > template . digest , iint - > digest , IMA_DIGEST_SIZE ) ;
strncpy ( entry - > template . file_name , filename , IMA_EVENT_NAME_LEN_MAX ) ;
result = ima_store_template ( entry , violation , inode ) ;
if ( ! result )
iint - > flags | = IMA_MEASURED ;
else
kfree ( entry ) ;
}