@ -185,6 +185,192 @@ static int pefile_strip_sig_wrapper(const void *pebuf,
return - ELIBBAD ;
}
/*
* Compare two sections for canonicalisation .
*/
static int pefile_compare_shdrs ( const void * a , const void * b )
{
const struct section_header * shdra = a ;
const struct section_header * shdrb = b ;
int rc ;
if ( shdra - > data_addr > shdrb - > data_addr )
return 1 ;
if ( shdrb - > data_addr > shdra - > data_addr )
return - 1 ;
if ( shdra - > virtual_address > shdrb - > virtual_address )
return 1 ;
if ( shdrb - > virtual_address > shdra - > virtual_address )
return - 1 ;
rc = strcmp ( shdra - > name , shdrb - > name ) ;
if ( rc ! = 0 )
return rc ;
if ( shdra - > virtual_size > shdrb - > virtual_size )
return 1 ;
if ( shdrb - > virtual_size > shdra - > virtual_size )
return - 1 ;
if ( shdra - > raw_data_size > shdrb - > raw_data_size )
return 1 ;
if ( shdrb - > raw_data_size > shdra - > raw_data_size )
return - 1 ;
return 0 ;
}
/*
* Load the contents of the PE binary into the digest , leaving out the image
* checksum and the certificate data block .
*/
static int pefile_digest_pe_contents ( const void * pebuf , unsigned int pelen ,
struct pefile_context * ctx ,
struct shash_desc * desc )
{
unsigned * canon , tmp , loop , i , hashed_bytes ;
int ret ;
/* Digest the header and data directory, but leave out the image
* checksum and the data dirent for the signature .
*/
ret = crypto_shash_update ( desc , pebuf , ctx - > image_checksum_offset ) ;
if ( ret < 0 )
return ret ;
tmp = ctx - > image_checksum_offset + sizeof ( uint32_t ) ;
ret = crypto_shash_update ( desc , pebuf + tmp ,
ctx - > cert_dirent_offset - tmp ) ;
if ( ret < 0 )
return ret ;
tmp = ctx - > cert_dirent_offset + sizeof ( struct data_dirent ) ;
ret = crypto_shash_update ( desc , pebuf + tmp , ctx - > header_size - tmp ) ;
if ( ret < 0 )
return ret ;
canon = kcalloc ( ctx - > n_sections , sizeof ( unsigned ) , GFP_KERNEL ) ;
if ( ! canon )
return - ENOMEM ;
/* We have to canonicalise the section table, so we perform an
* insertion sort .
*/
canon [ 0 ] = 0 ;
for ( loop = 1 ; loop < ctx - > n_sections ; loop + + ) {
for ( i = 0 ; i < loop ; i + + ) {
if ( pefile_compare_shdrs ( & ctx - > secs [ canon [ i ] ] ,
& ctx - > secs [ loop ] ) > 0 ) {
memmove ( & canon [ i + 1 ] , & canon [ i ] ,
( loop - i ) * sizeof ( canon [ 0 ] ) ) ;
break ;
}
}
canon [ i ] = loop ;
}
hashed_bytes = ctx - > header_size ;
for ( loop = 0 ; loop < ctx - > n_sections ; loop + + ) {
i = canon [ loop ] ;
if ( ctx - > secs [ i ] . raw_data_size = = 0 )
continue ;
ret = crypto_shash_update ( desc ,
pebuf + ctx - > secs [ i ] . data_addr ,
ctx - > secs [ i ] . raw_data_size ) ;
if ( ret < 0 ) {
kfree ( canon ) ;
return ret ;
}
hashed_bytes + = ctx - > secs [ i ] . raw_data_size ;
}
kfree ( canon ) ;
if ( pelen > hashed_bytes ) {
tmp = hashed_bytes + ctx - > certs_size ;
ret = crypto_shash_update ( desc ,
pebuf + hashed_bytes ,
pelen - tmp ) ;
if ( ret < 0 )
return ret ;
}
return 0 ;
}
/*
* Digest the contents of the PE binary , leaving out the image checksum and the
* certificate data block .
*/
static int pefile_digest_pe ( const void * pebuf , unsigned int pelen ,
struct pefile_context * ctx )
{
struct crypto_shash * tfm ;
struct shash_desc * desc ;
size_t digest_size , desc_size ;
void * digest ;
int ret ;
kenter ( " ,%u " , ctx - > digest_algo ) ;
/* Allocate the hashing algorithm we're going to need and find out how
* big the hash operational data will be .
*/
tfm = crypto_alloc_shash ( hash_algo_name [ ctx - > digest_algo ] , 0 , 0 ) ;
if ( IS_ERR ( tfm ) )
return ( PTR_ERR ( tfm ) = = - ENOENT ) ? - ENOPKG : PTR_ERR ( tfm ) ;
desc_size = crypto_shash_descsize ( tfm ) + sizeof ( * desc ) ;
digest_size = crypto_shash_digestsize ( tfm ) ;
if ( digest_size ! = ctx - > digest_len ) {
pr_debug ( " Digest size mismatch (%zx != %x) \n " ,
digest_size , ctx - > digest_len ) ;
ret = - EBADMSG ;
goto error_no_desc ;
}
pr_debug ( " Digest: desc=%zu size=%zu \n " , desc_size , digest_size ) ;
ret = - ENOMEM ;
desc = kzalloc ( desc_size + digest_size , GFP_KERNEL ) ;
if ( ! desc )
goto error_no_desc ;
desc - > tfm = tfm ;
desc - > flags = CRYPTO_TFM_REQ_MAY_SLEEP ;
ret = crypto_shash_init ( desc ) ;
if ( ret < 0 )
goto error ;
ret = pefile_digest_pe_contents ( pebuf , pelen , ctx , desc ) ;
if ( ret < 0 )
goto error ;
digest = ( void * ) desc + desc_size ;
ret = crypto_shash_final ( desc , digest ) ;
if ( ret < 0 )
goto error ;
pr_debug ( " Digest calc = [%*ph] \n " , ctx - > digest_len , digest ) ;
/* Check that the PE file digest matches that in the MSCODE part of the
* PKCS # 7 certificate .
*/
if ( memcmp ( digest , ctx - > digest , ctx - > digest_len ) ! = 0 ) {
pr_debug ( " Digest mismatch \n " ) ;
ret = - EKEYREJECTED ;
} else {
pr_debug ( " The digests match! \n " ) ;
}
error :
kfree ( desc ) ;
error_no_desc :
crypto_free_shash ( tfm ) ;
kleave ( " = %d " , ret ) ;
return ret ;
}
/**
* verify_pefile_signature - Verify the signature on a PE binary image
* @ pebuf : Buffer containing the PE binary image
@ -252,6 +438,17 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
pr_debug ( " Digest: %u [%*ph] \n " ,
ctx . digest_len , ctx . digest_len , ctx . digest ) ;
/* Generate the digest and check against the PKCS7 certificate
* contents .
*/
ret = pefile_digest_pe ( pebuf , pelen , & ctx ) ;
if ( ret < 0 )
goto error ;
ret = pkcs7_verify ( pkcs7 ) ;
if ( ret < 0 )
goto error ;
ret = - ENOANO ; // Not yet complete
error :