2013-06-07 14:16:30 +04:00
/*
* Copyright ( C ) 2013 Politecnico di Torino , Italy
* TORSEC group - - http : //security.polito.it
*
* Author : Roberto Sassu < roberto . sassu @ polito . it >
*
* 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_template_lib . c
* Library of supported template fields .
*/
2013-06-07 14:16:32 +04:00
# include <crypto/hash_info.h>
2013-06-07 14:16:30 +04:00
# include "ima_template_lib.h"
2013-06-07 14:16:32 +04:00
static bool ima_template_hash_algo_allowed ( u8 algo )
{
if ( algo = = HASH_ALGO_SHA1 | | algo = = HASH_ALGO_MD5 )
return true ;
return false ;
}
enum data_formats {
DATA_FMT_DIGEST = 0 ,
DATA_FMT_DIGEST_WITH_ALGO ,
DATA_FMT_EVENT_NAME ,
2013-07-23 19:15:00 +04:00
DATA_FMT_STRING ,
DATA_FMT_HEX
2013-06-07 14:16:32 +04:00
} ;
2013-06-07 14:16:30 +04:00
static int ima_write_template_field_data ( const void * data , const u32 datalen ,
enum data_formats datafmt ,
struct ima_field_data * field_data )
{
u8 * buf , * buf_ptr ;
u32 buflen ;
switch ( datafmt ) {
case DATA_FMT_EVENT_NAME :
buflen = IMA_EVENT_NAME_LEN_MAX + 1 ;
break ;
case DATA_FMT_STRING :
buflen = datalen + 1 ;
break ;
default :
buflen = datalen ;
}
buf = kzalloc ( buflen , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
memcpy ( buf , data , datalen ) ;
/*
* Replace all space characters with underscore for event names and
* strings . This avoid that , during the parsing of a measurements list ,
* filenames with spaces or that end with the suffix ' ( deleted ) ' are
* split into multiple template fields ( the space is the delimitator
* character for measurements lists in ASCII format ) .
*/
if ( datafmt = = DATA_FMT_EVENT_NAME | | datafmt = = DATA_FMT_STRING ) {
for ( buf_ptr = buf ; buf_ptr - buf < datalen ; buf_ptr + + )
if ( * buf_ptr = = ' ' )
* buf_ptr = ' _ ' ;
}
field_data - > data = buf ;
field_data - > len = buflen ;
return 0 ;
}
static void ima_show_template_data_ascii ( struct seq_file * m ,
enum ima_show_type show ,
enum data_formats datafmt ,
struct ima_field_data * field_data )
{
2013-06-07 14:16:32 +04:00
u8 * buf_ptr = field_data - > data , buflen = field_data - > len ;
2013-06-07 14:16:30 +04:00
switch ( datafmt ) {
2013-06-07 14:16:32 +04:00
case DATA_FMT_DIGEST_WITH_ALGO :
buf_ptr = strnchr ( field_data - > data , buflen , ' : ' ) ;
if ( buf_ptr ! = field_data - > data )
seq_printf ( m , " %s " , field_data - > data ) ;
/* skip ':' and '\0' */
buf_ptr + = 2 ;
buflen - = buf_ptr - field_data - > data ;
2013-06-07 14:16:30 +04:00
case DATA_FMT_DIGEST :
2013-07-23 19:15:00 +04:00
case DATA_FMT_HEX :
if ( ! buflen )
break ;
2013-06-07 14:16:32 +04:00
ima_print_digest ( m , buf_ptr , buflen ) ;
2013-06-07 14:16:30 +04:00
break ;
case DATA_FMT_STRING :
2013-06-07 14:16:32 +04:00
seq_printf ( m , " %s " , buf_ptr ) ;
2013-06-07 14:16:30 +04:00
break ;
default :
break ;
}
}
static void ima_show_template_data_binary ( struct seq_file * m ,
enum ima_show_type show ,
enum data_formats datafmt ,
struct ima_field_data * field_data )
{
2014-02-03 16:56:04 +04:00
u32 len = ( show = = IMA_SHOW_BINARY_OLD_STRING_FMT ) ?
strlen ( field_data - > data ) : field_data - > len ;
2013-11-08 22:21:40 +04:00
if ( show ! = IMA_SHOW_BINARY_NO_FIELD_LEN )
2014-02-03 16:56:04 +04:00
ima_putc ( m , & len , sizeof ( len ) ) ;
2013-11-08 22:21:40 +04:00
2014-02-03 16:56:04 +04:00
if ( ! len )
2013-06-07 14:16:30 +04:00
return ;
2013-11-08 22:21:40 +04:00
2014-02-03 16:56:04 +04:00
ima_putc ( m , field_data - > data , len ) ;
2013-06-07 14:16:30 +04:00
}
static void ima_show_template_field_data ( struct seq_file * m ,
enum ima_show_type show ,
enum data_formats datafmt ,
struct ima_field_data * field_data )
{
switch ( show ) {
case IMA_SHOW_ASCII :
ima_show_template_data_ascii ( m , show , datafmt , field_data ) ;
break ;
case IMA_SHOW_BINARY :
2013-11-08 22:21:40 +04:00
case IMA_SHOW_BINARY_NO_FIELD_LEN :
2014-02-03 16:56:04 +04:00
case IMA_SHOW_BINARY_OLD_STRING_FMT :
2013-06-07 14:16:30 +04:00
ima_show_template_data_binary ( m , show , datafmt , field_data ) ;
break ;
default :
break ;
}
}
void ima_show_template_digest ( struct seq_file * m , enum ima_show_type show ,
struct ima_field_data * field_data )
{
ima_show_template_field_data ( m , show , DATA_FMT_DIGEST , field_data ) ;
}
2013-06-07 14:16:32 +04:00
void ima_show_template_digest_ng ( struct seq_file * m , enum ima_show_type show ,
struct ima_field_data * field_data )
{
ima_show_template_field_data ( m , show , DATA_FMT_DIGEST_WITH_ALGO ,
field_data ) ;
}
2013-06-07 14:16:30 +04:00
void ima_show_template_string ( struct seq_file * m , enum ima_show_type show ,
struct ima_field_data * field_data )
{
ima_show_template_field_data ( m , show , DATA_FMT_STRING , field_data ) ;
}
2013-07-23 19:15:00 +04:00
void ima_show_template_sig ( struct seq_file * m , enum ima_show_type show ,
struct ima_field_data * field_data )
{
ima_show_template_field_data ( m , show , DATA_FMT_HEX , field_data ) ;
}
2013-06-07 14:16:32 +04:00
static int ima_eventdigest_init_common ( u8 * digest , u32 digestsize , u8 hash_algo ,
2013-11-08 22:21:37 +04:00
struct ima_field_data * field_data )
2013-06-07 14:16:32 +04:00
{
/*
* digest formats :
* - DATA_FMT_DIGEST : digest
* - DATA_FMT_DIGEST_WITH_ALGO : [ < hash algo > ] + ' : ' + ' \0 ' + digest ,
* where < hash algo > is provided if the hash algoritm is not
* SHA1 or MD5
*/
u8 buffer [ CRYPTO_MAX_ALG_NAME + 2 + IMA_MAX_DIGEST_SIZE ] = { 0 } ;
enum data_formats fmt = DATA_FMT_DIGEST ;
u32 offset = 0 ;
2013-11-08 22:21:37 +04:00
if ( hash_algo < HASH_ALGO__LAST ) {
2013-06-07 14:16:32 +04:00
fmt = DATA_FMT_DIGEST_WITH_ALGO ;
2013-11-08 22:21:37 +04:00
offset + = snprintf ( buffer , CRYPTO_MAX_ALG_NAME + 1 , " %s " ,
hash_algo_name [ hash_algo ] ) ;
2013-06-07 14:16:32 +04:00
buffer [ offset ] = ' : ' ;
offset + = 2 ;
}
if ( digest )
memcpy ( buffer + offset , digest , digestsize ) ;
else
/*
* If digest is NULL , the event being recorded is a violation .
* Make room for the digest by increasing the offset of
* IMA_DIGEST_SIZE .
*/
offset + = IMA_DIGEST_SIZE ;
return ima_write_template_field_data ( buffer , offset + digestsize ,
fmt , field_data ) ;
}
2013-06-07 14:16:30 +04:00
/*
2013-06-07 14:16:32 +04:00
* This function writes the digest of an event ( with size limit ) .
2013-06-07 14:16:30 +04:00
*/
int ima_eventdigest_init ( struct integrity_iint_cache * iint , struct file * file ,
const unsigned char * filename ,
2013-07-23 19:15:00 +04:00
struct evm_ima_xattr_data * xattr_value , int xattr_len ,
2013-06-07 14:16:30 +04:00
struct ima_field_data * field_data )
{
struct {
struct ima_digest_data hdr ;
char digest [ IMA_MAX_DIGEST_SIZE ] ;
} hash ;
2013-06-07 14:16:32 +04:00
u8 * cur_digest = NULL ;
u32 cur_digestsize = 0 ;
2013-06-07 14:16:30 +04:00
struct inode * inode ;
int result ;
memset ( & hash , 0 , sizeof ( hash ) ) ;
if ( ! iint ) /* recording a violation. */
goto out ;
2013-06-07 14:16:32 +04:00
if ( ima_template_hash_algo_allowed ( iint - > ima_hash - > algo ) ) {
2013-06-07 14:16:30 +04:00
cur_digest = iint - > ima_hash - > digest ;
cur_digestsize = iint - > ima_hash - > length ;
goto out ;
}
if ( ! file ) /* missing info to re-calculate the digest */
return - EINVAL ;
inode = file_inode ( file ) ;
2013-06-07 14:16:32 +04:00
hash . hdr . algo = ima_template_hash_algo_allowed ( ima_hash_algo ) ?
ima_hash_algo : HASH_ALGO_SHA1 ;
2013-06-07 14:16:30 +04:00
result = ima_calc_file_hash ( file , & hash . hdr ) ;
if ( result ) {
integrity_audit_msg ( AUDIT_INTEGRITY_DATA , inode ,
filename , " collect_data " ,
" failed " , result , 0 ) ;
return result ;
}
2013-06-07 14:16:32 +04:00
cur_digest = hash . hdr . digest ;
cur_digestsize = hash . hdr . length ;
2013-06-07 14:16:30 +04:00
out :
2013-11-08 22:21:36 +04:00
return ima_eventdigest_init_common ( cur_digest , cur_digestsize ,
2013-11-08 22:21:37 +04:00
HASH_ALGO__LAST , field_data ) ;
2013-06-07 14:16:30 +04:00
}
/*
2013-06-07 14:16:32 +04:00
* This function writes the digest of an event ( without size limit ) .
2013-06-07 14:16:30 +04:00
*/
2013-06-07 14:16:32 +04:00
int ima_eventdigest_ng_init ( struct integrity_iint_cache * iint ,
struct file * file , const unsigned char * filename ,
2013-07-23 19:15:00 +04:00
struct evm_ima_xattr_data * xattr_value ,
int xattr_len , struct ima_field_data * field_data )
2013-06-07 14:16:32 +04:00
{
2013-11-08 22:21:35 +04:00
u8 * cur_digest = NULL , hash_algo = HASH_ALGO_SHA1 ;
2013-06-07 14:16:32 +04:00
u32 cur_digestsize = 0 ;
/* If iint is NULL, we are recording a violation. */
if ( ! iint )
goto out ;
cur_digest = iint - > ima_hash - > digest ;
cur_digestsize = iint - > ima_hash - > length ;
hash_algo = iint - > ima_hash - > algo ;
out :
return ima_eventdigest_init_common ( cur_digest , cur_digestsize ,
2013-11-08 22:21:37 +04:00
hash_algo , field_data ) ;
2013-06-07 14:16:32 +04:00
}
static int ima_eventname_init_common ( struct integrity_iint_cache * iint ,
struct file * file ,
const unsigned char * filename ,
struct ima_field_data * field_data ,
bool size_limit )
2013-06-07 14:16:30 +04:00
{
const char * cur_filename = NULL ;
u32 cur_filename_len = 0 ;
2013-06-07 14:16:32 +04:00
enum data_formats fmt = size_limit ?
DATA_FMT_EVENT_NAME : DATA_FMT_STRING ;
2013-06-07 14:16:30 +04:00
BUG_ON ( filename = = NULL & & file = = NULL ) ;
if ( filename ) {
cur_filename = filename ;
cur_filename_len = strlen ( filename ) ;
2013-06-07 14:16:32 +04:00
if ( ! size_limit | | cur_filename_len < = IMA_EVENT_NAME_LEN_MAX )
2013-06-07 14:16:30 +04:00
goto out ;
}
if ( file ) {
cur_filename = file - > f_dentry - > d_name . name ;
cur_filename_len = strlen ( cur_filename ) ;
} else
/*
* Truncate filename if the latter is too long and
* the file descriptor is not available .
*/
cur_filename_len = IMA_EVENT_NAME_LEN_MAX ;
out :
return ima_write_template_field_data ( cur_filename , cur_filename_len ,
2013-06-07 14:16:32 +04:00
fmt , field_data ) ;
}
/*
* This function writes the name of an event ( with size limit ) .
*/
int ima_eventname_init ( struct integrity_iint_cache * iint , struct file * file ,
const unsigned char * filename ,
2013-07-23 19:15:00 +04:00
struct evm_ima_xattr_data * xattr_value , int xattr_len ,
2013-06-07 14:16:32 +04:00
struct ima_field_data * field_data )
{
return ima_eventname_init_common ( iint , file , filename ,
field_data , true ) ;
}
/*
* This function writes the name of an event ( without size limit ) .
*/
int ima_eventname_ng_init ( struct integrity_iint_cache * iint , struct file * file ,
const unsigned char * filename ,
2013-07-23 19:15:00 +04:00
struct evm_ima_xattr_data * xattr_value , int xattr_len ,
2013-06-07 14:16:32 +04:00
struct ima_field_data * field_data )
{
return ima_eventname_init_common ( iint , file , filename ,
field_data , false ) ;
2013-06-07 14:16:30 +04:00
}
2013-07-23 19:15:00 +04:00
/*
* ima_eventsig_init - include the file signature as part of the template data
*/
int ima_eventsig_init ( struct integrity_iint_cache * iint , struct file * file ,
const unsigned char * filename ,
struct evm_ima_xattr_data * xattr_value , int xattr_len ,
struct ima_field_data * field_data )
{
enum data_formats fmt = DATA_FMT_HEX ;
int rc = 0 ;
if ( ( ! xattr_value ) | | ( xattr_value - > type ! = EVM_IMA_XATTR_DIGSIG ) )
goto out ;
rc = ima_write_template_field_data ( xattr_value , xattr_len , fmt ,
field_data ) ;
out :
return rc ;
}