2022-10-22 18:41:00 +08:00
/*
* Copyright ( C ) 2013 - 2022 Cisco Systems , Inc . and / or its affiliates . All rights reserved .
* Copyright ( C ) 2011 - 2013 Sourcefire , Inc .
*
* Authors : aCaB
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 , USA .
*/
# if HAVE_CONFIG_H
# include "clamav-config.h"
# endif
# include <time.h>
# include "clamav.h"
# include "asn1.h"
# include "bignum.h"
# include "matcher-hash.h"
/* --------------------------------------------------------------------------- OIDS */
# define OID_1_3_14_3_2_26 "\x2b\x0e\x03\x02\x1a"
# define OID_sha1 OID_1_3_14_3_2_26
# define OID_1_3_14_3_2_29 "\x2b\x0e\x03\x02\x1d"
# define OID_sha1WithRSA OID_1_3_14_3_2_29
# define OID_1_2_840_113549_1_1_1 "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01"
# define OID_rsaEncryption OID_1_2_840_113549_1_1_1
# define OID_1_2_840_113549_1_1_2 "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x02"
# define OID_md2WithRSAEncryption OID_1_2_840_113549_1_1_2
# define OID_1_2_840_113549_1_1_4 "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x04"
# define OID_md5WithRSAEncryption OID_1_2_840_113549_1_1_4
# define OID_1_2_840_113549_1_1_5 "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05"
# define OID_sha1WithRSAEncryption OID_1_2_840_113549_1_1_5
# define OID_1_2_840_113549_1_1_11 "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b"
# define OID_sha256WithRSAEncryption OID_1_2_840_113549_1_1_11
# define OID_1_2_840_113549_1_1_12 "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0c"
# define OID_sha384WithRSAEncryption OID_1_2_840_113549_1_1_12
# define OID_1_2_840_113549_1_1_13 "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0d"
# define OID_sha512WithRSAEncryption OID_1_2_840_113549_1_1_13
# define OID_1_2_840_113549_1_7_1 "\x2a\x86\x48\x86\xf7\x0d\x01\x07\x01"
# define OID_pkcs7_data OID_1_2_840_113549_1_7_1
# define OID_1_2_840_113549_1_7_2 "\x2a\x86\x48\x86\xf7\x0d\x01\x07\x02"
# define OID_signedData OID_1_2_840_113549_1_7_2
# define OID_1_2_840_113549_1_9_3 "\x2a\x86\x48\x86\xf7\x0d\x01\x09\x03"
# define OID_contentType OID_1_2_840_113549_1_9_3
# define OID_1_2_840_113549_1_9_4 "\x2a\x86\x48\x86\xf7\x0d\x01\x09\x04"
# define OID_messageDigest OID_1_2_840_113549_1_9_4
# define OID_1_2_840_113549_1_9_5 "\x2a\x86\x48\x86\xf7\x0d\x01\x09\x05"
# define OID_signingTime OID_1_2_840_113549_1_9_5
# define OID_1_2_840_113549_2_5 "\x2a\x86\x48\x86\xf7\x0d\x02\x05"
# define OID_md5 OID_1_2_840_113549_2_5
# define OID_1_2_840_113549_1_9_6 "\x2a\x86\x48\x86\xf7\x0d\x01\x09\x06"
# define OID_countersignature OID_1_2_840_113549_1_9_6
# define OID_1_2_840_113549_1_9_16_1_4 "\x2a\x86\x48\x86\xf7\x0d\x01\x09\x10\x01\x04"
# define OID_timestampToken OID_1_2_840_113549_1_9_16_1_4
# define OID_1_3_6_1_4_1_311_2_1_4 "\x2b\x06\x01\x04\x01\x82\x37\x02\x01\x04"
# define OID_SPC_INDIRECT_DATA_OBJID OID_1_3_6_1_4_1_311_2_1_4
# define OID_1_3_6_1_4_1_311_2_1_15 "\x2b\x06\x01\x04\x01\x82\x37\x02\x01\x0f"
# define OID_SPC_PE_IMAGE_DATA_OBJID OID_1_3_6_1_4_1_311_2_1_15
# define OID_1_3_6_1_4_1_311_2_1_25 "\x2b\x06\x01\x04\x01\x82\x37\x02\x01\x19"
# define OID_SPC_CAB_DATA_OBJID OID_1_3_6_1_4_1_311_2_1_25
# define OID_1_3_6_1_4_1_311_2_4_1 "\x2b\x06\x01\x04\x01\x82\x37\x02\x04\x01"
# define OID_nestedSignatures OID_1_3_6_1_4_1_311_2_4_1
2023-01-14 18:28:39 +08:00
# define OID_1_3_6_1_4_1_311_3_3_1 "\x2b\x06\x01\x04\x01\x82\x37\x03\x03\x01"
# define OID_RFC3161_countersignature OID_1_3_6_1_4_1_311_3_3_1
2022-10-22 18:41:00 +08:00
# define OID_1_3_6_1_4_1_311_10_1 "\x2b\x06\x01\x04\x01\x82\x37\x0a\x01"
# define OID_szOID_CTL OID_1_3_6_1_4_1_311_10_1
# define OID_1_3_6_1_4_1_311_12_1_1 "\x2b\x06\x01\x04\x01\x82\x37\x0c\x01\x01"
# define OID_szOID_CATALOG_LIST OID_1_3_6_1_4_1_311_12_1_1
# define OID_1_3_6_1_4_1_311_12_1_2 "\x2b\x06\x01\x04\x01\x82\x37\x0c\x01\x02"
# define OID_szOID_CATALOG_LIST_MEMBER OID_1_3_6_1_4_1_311_12_1_2
2023-01-14 18:28:39 +08:00
/* CATALOG_LIST_MEMBER2 seems to be whats used by the SHA256-based CAT files */
# define OID_1_3_6_1_4_1_311_12_1_3 "\x2b\x06\x01\x04\x01\x82\x37\x0c\x01\x03"
# define OID_szOID_CATALOG_LIST_MEMBER2 OID_1_3_6_1_4_1_311_12_1_3
2022-10-22 18:41:00 +08:00
# define OID_2_16_840_1_101_3_4_2_1 "\x60\x86\x48\x01\x65\x03\x04\x02\x01"
# define OID_sha256 OID_2_16_840_1_101_3_4_2_1
# define OID_2_16_840_1_101_3_4_2_2 "\x60\x86\x48\x01\x65\x03\x04\x02\x02"
# define OID_sha384 OID_2_16_840_1_101_3_4_2_2
# define OID_2_16_840_1_101_3_4_2_3 "\x60\x86\x48\x01\x65\x03\x04\x02\x03"
# define OID_sha512 OID_2_16_840_1_101_3_4_2_3
/* --------------------------------------------------------------------------- OIDS */
# define lenof(x) (sizeof((x)) - 1)
# define ASN1_TYPE_BOOLEAN 0x01
# define ASN1_TYPE_INTEGER 0x02
# define ASN1_TYPE_BIT_STRING 0x03
# define ASN1_TYPE_OCTET_STRING 0x04
# define ASN1_TYPE_NULL 0x05
# define ASN1_TYPE_OBJECT_ID 0x06
# define ASN1_TYPE_SEQUENCE 0x30
# define ASN1_TYPE_SET 0x31
# define MAX_HASH_SIZE SHA512_HASH_SIZE
struct cli_asn1 {
uint8_t type ;
unsigned int size ;
const void * content ;
const void * next ;
} ;
static int map_raw ( fmap_t * map , const void * data , unsigned int len , uint8_t raw [ CRT_RAWMAXLEN ] )
{
unsigned int elen = MIN ( len , CRT_RAWMAXLEN - 1 ) ;
if ( ! fmap_need_ptr_once ( map , data , elen ) ) {
cli_dbgmsg ( " map_raw: failed to read map data \n " ) ;
return 1 ;
}
memset ( raw , 0 , CRT_RAWMAXLEN ) ;
raw [ 0 ] = ( uint8_t ) elen ;
memcpy ( & raw [ 1 ] , data , elen ) ;
return 0 ;
}
static int map_sha512 ( fmap_t * map , const void * data , unsigned int len , uint8_t sha512 [ SHA512_HASH_SIZE ] )
{
if ( ! fmap_need_ptr_once ( map , data , len ) ) {
cli_dbgmsg ( " map_sha512: failed to read hash data \n " ) ;
return 1 ;
}
return ( cl_sha512 ( data , len , sha512 , NULL ) = = NULL ) ;
}
static int map_sha384 ( fmap_t * map , const void * data , unsigned int len , uint8_t sha384 [ SHA384_HASH_SIZE ] )
{
if ( ! fmap_need_ptr_once ( map , data , len ) ) {
cli_dbgmsg ( " map_sha384: failed to read hash data \n " ) ;
return 1 ;
}
return ( cl_sha384 ( data , len , sha384 , NULL ) = = NULL ) ;
}
static int map_sha256 ( fmap_t * map , const void * data , unsigned int len , uint8_t sha256 [ SHA256_HASH_SIZE ] )
{
if ( ! fmap_need_ptr_once ( map , data , len ) ) {
cli_dbgmsg ( " map_sha256: failed to read hash data \n " ) ;
return 1 ;
}
return ( cl_sha256 ( data , len , sha256 , NULL ) = = NULL ) ;
}
static int map_sha1 ( fmap_t * map , const void * data , unsigned int len , uint8_t sha1 [ SHA1_HASH_SIZE ] )
{
if ( ! fmap_need_ptr_once ( map , data , len ) ) {
cli_dbgmsg ( " map_sha1: failed to read hash data \n " ) ;
return 1 ;
}
return ( cl_sha1 ( data , len , sha1 , NULL ) = = NULL ) ;
}
static int map_md5 ( fmap_t * map , const void * data , unsigned int len , uint8_t * md5 )
{
if ( ! fmap_need_ptr_once ( map , data , len ) ) {
cli_dbgmsg ( " map_md5: failed to read hash data \n " ) ;
return 1 ;
}
return ( cl_hash_data ( " md5 " , data , len , md5 , NULL ) = = NULL ) ;
}
static int map_hash ( fmap_t * map , const void * data , unsigned int len , uint8_t * out_hash , cli_crt_hashtype hashtype )
{
if ( hashtype = = CLI_SHA1RSA ) {
if ( map_sha1 ( map , data , len , out_hash ) ) {
return 1 ;
}
} else if ( hashtype = = CLI_MD5RSA ) {
if ( map_md5 ( map , data , len , out_hash ) ) {
return 1 ;
}
} else if ( hashtype = = CLI_SHA256RSA ) {
if ( map_sha256 ( map , data , len , out_hash ) ) {
return 1 ;
}
} else if ( hashtype = = CLI_SHA384RSA ) {
if ( map_sha384 ( map , data , len , out_hash ) ) {
return 1 ;
}
} else if ( hashtype = = CLI_SHA512RSA ) {
if ( map_sha512 ( map , data , len , out_hash ) ) {
return 1 ;
}
} else {
cli_dbgmsg ( " asn1_map_hash: unsupported hashtype \n " ) ;
return 1 ;
}
return 0 ;
}
static void * get_hash_ctx ( cli_crt_hashtype hashtype )
{
void * ctx = NULL ;
if ( hashtype = = CLI_SHA1RSA ) {
ctx = cl_hash_init ( " sha1 " ) ;
} else if ( hashtype = = CLI_MD5RSA ) {
ctx = cl_hash_init ( " md5 " ) ;
} else if ( hashtype = = CLI_SHA256RSA ) {
ctx = cl_hash_init ( " sha256 " ) ;
} else if ( hashtype = = CLI_SHA384RSA ) {
ctx = cl_hash_init ( " sha384 " ) ;
} else if ( hashtype = = CLI_SHA512RSA ) {
ctx = cl_hash_init ( " sha512 " ) ;
} else {
cli_dbgmsg ( " asn1_get_hash_ctx: unsupported hashtype \n " ) ;
}
return ctx ;
}
static int asn1_get_obj ( fmap_t * map , const void * asn1data , unsigned int * asn1len , struct cli_asn1 * obj )
{
unsigned int asn1_sz = * asn1len ;
unsigned int readbytes = MIN ( 6 , asn1_sz ) , i ;
const uint8_t * data ;
if ( asn1_sz < 2 ) {
cli_dbgmsg ( " asn1_get_obj: insufficient data length \n " ) ;
return 1 ;
}
data = fmap_need_ptr_once ( map , asn1data , readbytes ) ;
if ( ! data ) {
cli_dbgmsg ( " asn1_get_obj: obj out of file \n " ) ;
return 1 ;
}
obj - > type = data [ 0 ] ;
i = data [ 1 ] ;
data + = 2 ;
if ( i & 0x80 ) {
if ( i = = 0x80 ) {
/* Not allowed in DER */
cli_dbgmsg ( " asn1_get_obj: unsupported indefinite length object \n " ) ;
return 1 ;
}
i & = ~ 0x80 ;
if ( i > readbytes - 2 ) {
cli_dbgmsg ( " asn1_get_obj: len octets overflow (or just too many) \n " ) ;
return 1 ;
}
obj - > size = 0 ;
while ( i - - ) {
obj - > size < < = 8 ;
obj - > size | = * data ;
data + + ;
}
} else
obj - > size = i ;
asn1_sz - = data - ( uint8_t * ) asn1data ;
if ( obj - > size > asn1_sz ) {
cli_dbgmsg ( " asn1_get_obj: content overflow \n " ) ;
return 1 ;
}
obj - > content = data ;
if ( obj - > size = = asn1_sz )
obj - > next = NULL ;
else
obj - > next = data + obj - > size ;
* asn1len = asn1_sz - obj - > size ;
return 0 ;
}
static int asn1_expect_objtype ( fmap_t * map , const void * asn1data , unsigned int * asn1len , struct cli_asn1 * obj , uint8_t type )
{
int ret = asn1_get_obj ( map , asn1data , asn1len , obj ) ;
if ( ret )
return ret ;
if ( obj - > type ! = type ) {
cli_dbgmsg ( " asn1_expect_objtype: expected type %02x, got %02x \n " , type , obj - > type ) ;
return 1 ;
}
return 0 ;
}
static int asn1_expect_obj ( fmap_t * map , const void * * asn1data , unsigned int * asn1len , uint8_t type , unsigned int size , const void * content )
{
struct cli_asn1 obj ;
int ret = asn1_expect_objtype ( map , * asn1data , asn1len , & obj , type ) ;
if ( ret )
return ret ;
if ( obj . size ! = size ) {
cli_dbgmsg ( " asn1_expect_obj: expected size %u, got %u \n " , size , obj . size ) ;
return 1 ;
}
if ( size ) {
if ( ! fmap_need_ptr_once ( map , obj . content , size ) ) {
cli_dbgmsg ( " asn1_expect_obj: failed to read content \n " ) ;
return 1 ;
}
if ( memcmp ( obj . content , content , size ) ) {
cli_dbgmsg ( " asn1_expect_obj: content mismatch \n " ) ;
return 1 ;
}
}
* asn1data = obj . next ;
return 0 ;
}
static int asn1_expect_algo ( fmap_t * map , const void * * asn1data , unsigned int * asn1len , unsigned int algo_size , const void * algo )
{
struct cli_asn1 obj ;
unsigned int avail ;
int ret ;
if ( ( ret = asn1_expect_objtype ( map , * asn1data , asn1len , & obj , ASN1_TYPE_SEQUENCE ) ) ) /* SEQUENCE */
return ret ;
avail = obj . size ;
* asn1data = obj . next ;
if ( ( ret = asn1_expect_obj ( map , & obj . content , & avail , ASN1_TYPE_OBJECT_ID , algo_size , algo ) ) ) /* ALGO */
return ret ;
// The specification says that the NULL is a required parameter for this
// data type, but in practice it doesn't always exist in the ASN1. If
// there is something after the ALGO OID, assume it's the NULL
if ( avail & & ( ret = asn1_expect_obj ( map , & obj . content , & avail , ASN1_TYPE_NULL , 0 , NULL ) ) ) { /* NULL */
cli_dbgmsg ( " asn1_expect_algo: expected NULL after AlgorithmIdentifier OID \n " ) ;
return ret ;
}
if ( avail ) {
cli_dbgmsg ( " asn1_expect_algo: extra data found in SEQUENCE \n " ) ;
return 1 ;
}
return 0 ;
}
2023-01-14 18:28:39 +08:00
typedef struct _oid_alternative {
const char * oid_bytes ;
const unsigned int oid_bytes_len ;
const int context [ 2 ] ;
} oid_alternative_t ;
static const oid_alternative_t * asn1_expect_algo_multi ( fmap_t * map , const void * * asn1data , unsigned int * asn1len ,
const oid_alternative_t alts [ ] , unsigned int alts_count )
2022-10-22 18:41:00 +08:00
{
struct cli_asn1 obj ;
unsigned int avail ;
2023-01-14 18:28:39 +08:00
unsigned int i ;
const oid_alternative_t * oid_alt_ptr = NULL ;
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
if ( asn1_expect_objtype ( map , * asn1data , asn1len , & obj , ASN1_TYPE_SEQUENCE ) ) { /* SEQUENCE */
cli_dbgmsg ( " asn1_expect_algo_multi: expecting SEQUENCE at the start of the algo \n " ) ;
return NULL ;
2022-10-22 18:41:00 +08:00
}
avail = obj . size ;
* asn1data = obj . next ;
2023-01-14 18:28:39 +08:00
if ( asn1_expect_objtype ( map , obj . content , & avail , & obj , ASN1_TYPE_OBJECT_ID ) ) {
cli_dbgmsg ( " asn1_expect_algo_multi: expected OID in algo \n " ) ;
return NULL ;
2022-10-22 18:41:00 +08:00
}
2023-01-14 18:28:39 +08:00
if ( 0 = = obj . size ) {
cli_dbgmsg ( " asn1_expect_algo_multi: Unexpected length value of zero when parsing OID obj \n " ) ;
return NULL ;
2022-10-22 18:41:00 +08:00
}
2023-01-14 18:28:39 +08:00
2022-10-22 18:41:00 +08:00
if ( ! fmap_need_ptr_once ( map , obj . content , obj . size ) ) {
2023-01-14 18:28:39 +08:00
cli_dbgmsg ( " asn1_expect_algo_multi: failed to read OID \n " ) ;
return NULL ;
2022-10-22 18:41:00 +08:00
}
2023-01-14 18:28:39 +08:00
for ( i = 0 ; i < alts_count ; i + + ) {
oid_alt_ptr = & ( alts [ i ] ) ;
if ( obj . size ! = oid_alt_ptr - > oid_bytes_len ) {
continue ;
}
if ( ! memcmp ( obj . content , oid_alt_ptr - > oid_bytes , obj . size ) ) {
break ;
}
}
if ( NULL = = oid_alt_ptr ) {
cli_dbgmsg ( " asn1_expect_algo_multi: Failed to find valid OID alternative \n " ) ;
return NULL ;
2022-10-22 18:41:00 +08:00
}
2023-01-14 18:28:39 +08:00
2022-10-22 18:41:00 +08:00
// The specification says that the NULL is a required parameter for this
// data type, but in practice it doesn't always exist in the ASN1. If
// there is something after the ALGO OID, assume it's the NULL
2023-01-14 18:28:39 +08:00
if ( avail & & asn1_expect_obj ( map , & obj . next , & avail , ASN1_TYPE_NULL , 0 , NULL ) ) { /* NULL */
cli_dbgmsg ( " asn1_expect_algo_multi: expected NULL following OID \n " ) ;
return NULL ;
2022-10-22 18:41:00 +08:00
}
if ( avail ) {
2023-01-14 18:28:39 +08:00
cli_dbgmsg ( " asn1_expect_algo_multi: extra data found in SEQUENCE \n " ) ;
return NULL ;
}
return oid_alt_ptr ;
}
static int asn1_expect_hash_algo ( fmap_t * map , const void * * asn1data , unsigned int * asn1len , cli_crt_hashtype * hashtype , unsigned int * hashsize )
{
const oid_alternative_t * ret ;
static const oid_alternative_t alts [ ] = {
{ OID_sha1 , lenof ( OID_sha1 ) , { CLI_SHA1RSA , SHA1_HASH_SIZE } } ,
{ OID_sha1WithRSAEncryption , lenof ( OID_sha1WithRSAEncryption ) , { CLI_SHA1RSA , SHA1_HASH_SIZE } } ,
{ OID_md5 , lenof ( OID_md5 ) , { CLI_MD5RSA , MD5_HASH_SIZE } } ,
{ OID_md5WithRSAEncryption , lenof ( OID_md5WithRSAEncryption ) , { CLI_MD5RSA , MD5_HASH_SIZE } } ,
{ OID_sha256 , lenof ( OID_sha256 ) , { CLI_SHA256RSA , SHA256_HASH_SIZE } } ,
{ OID_sha256WithRSAEncryption , lenof ( OID_sha256WithRSAEncryption ) , { CLI_SHA256RSA , SHA256_HASH_SIZE } } ,
{ OID_sha384 , lenof ( OID_sha384 ) , { CLI_SHA384RSA , SHA384_HASH_SIZE } } ,
{ OID_sha384WithRSAEncryption , lenof ( OID_sha384WithRSAEncryption ) , { CLI_SHA384RSA , SHA384_HASH_SIZE } } ,
{ OID_sha512 , lenof ( OID_sha512 ) , { CLI_SHA512RSA , SHA512_HASH_SIZE } } ,
{ OID_sha512WithRSAEncryption , lenof ( OID_sha512WithRSAEncryption ) , { CLI_SHA512RSA , SHA512_HASH_SIZE } } ,
} ;
ret = asn1_expect_algo_multi ( map , asn1data , asn1len , alts , sizeof ( alts ) / sizeof ( alts [ 0 ] ) ) ;
if ( NULL = = ret ) {
cli_dbgmsg ( " asn1_expect_hash_algo: Failed to find valid OID alternative for hash algo \n " ) ;
2022-10-22 18:41:00 +08:00
return 1 ;
}
2023-01-14 18:28:39 +08:00
* hashtype = ret - > context [ 0 ] ;
* hashsize = ret - > context [ 1 ] ;
2022-10-22 18:41:00 +08:00
return 0 ;
}
static int asn1_expect_rsa ( fmap_t * map , const void * * asn1data , unsigned int * asn1len , cli_crt_hashtype * hashtype )
{
2023-01-14 18:28:39 +08:00
const oid_alternative_t * ret ;
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
static const oid_alternative_t alts [ ] = {
/* Obsolete sha1rsa 1.3.14.3.2.29 */
{ OID_sha1WithRSA , lenof ( OID_sha1WithRSA ) , { CLI_SHA1RSA , 0 } } ,
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
/* sha1withRSAEncryption 1.2.840.113549.1.1.5 */
{ OID_sha1WithRSAEncryption , lenof ( OID_sha1WithRSAEncryption ) , { CLI_SHA1RSA , 0 } } ,
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
/* md5withRSAEncryption 1.2.840.113549.1.1.4 */
{ OID_md5WithRSAEncryption , lenof ( OID_md5WithRSAEncryption ) , { CLI_MD5RSA , 0 } } ,
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
/* rsaEncryption 1.2.840.113549.1.1.1 */
{ OID_rsaEncryption , lenof ( OID_rsaEncryption ) , { CLI_RSA , 0 } } ,
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
/* md2withRSAEncryption 1.2.840.113549.1.1.2 */
{ OID_md2WithRSAEncryption , lenof ( OID_md2WithRSAEncryption ) , { CLI_MD2RSA , 0 } } ,
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
/* sha256WithRSAEncryption 1.2.840.113549.1.1.11 */
{ OID_sha256WithRSAEncryption , lenof ( OID_sha256WithRSAEncryption ) , { CLI_SHA256RSA , 0 } } ,
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
/* sha384WithRSAEncryption 1.2.840.113549.1.1.12 */
{ OID_sha384WithRSAEncryption , lenof ( OID_sha384WithRSAEncryption ) , { CLI_SHA384RSA , 0 } } ,
2022-10-22 18:41:00 +08:00
2023-01-14 18:28:39 +08:00
/* sha512WithRSAEncryption 1.2.840.113549.1.1.13 */
{ OID_sha512WithRSAEncryption , lenof ( OID_sha512WithRSAEncryption ) , { CLI_SHA512RSA , 0 } } ,
} ;
ret = asn1_expect_algo_multi ( map , asn1data , asn1len , alts , sizeof ( alts ) / sizeof ( alts [ 0 ] ) ) ;
if ( NULL = = ret ) {
cli_dbgmsg ( " asn1_expect_rsa: Failed to find valid OID alternative for RSA algo \n " ) ;
2022-10-22 18:41:00 +08:00
return 1 ;
}
2023-01-14 18:28:39 +08:00
* hashtype = ret - > context [ 0 ] ;
return 0 ;
}
static int asn1_expect_list_member ( fmap_t * map , const void * * asn1data , unsigned int * asn1len )
{
const oid_alternative_t * ret ;
static const oid_alternative_t alts [ ] = {
{ OID_szOID_CATALOG_LIST_MEMBER , lenof ( OID_szOID_CATALOG_LIST_MEMBER ) , { 0 , 0 } } ,
{ OID_szOID_CATALOG_LIST_MEMBER2 , lenof ( OID_szOID_CATALOG_LIST_MEMBER2 ) , { 0 , 0 } } ,
} ;
ret = asn1_expect_algo_multi ( map , asn1data , asn1len , alts , sizeof ( alts ) / sizeof ( alts [ 0 ] ) ) ;
if ( NULL = = ret ) {
cli_dbgmsg ( " asn1_expect_list_member: Failed to find valid OID alternative for list member algo \n " ) ;
2022-10-22 18:41:00 +08:00
return 1 ;
}
2023-01-14 18:28:39 +08:00
2022-10-22 18:41:00 +08:00
return 0 ;
}
static int asn1_getnum ( const char * s )
{
if ( s [ 0 ] < ' 0 ' | | s [ 0 ] > ' 9 ' | | s [ 1 ] < ' 0 ' | | s [ 1 ] > ' 9 ' ) {
cli_dbgmsg ( " asn1_getnum: expecting digits, found '%c%c' \n " , s [ 0 ] , s [ 1 ] ) ;
return - 1 ;
}
return ( s [ 0 ] - ' 0 ' ) * 10 + ( s [ 1 ] - ' 0 ' ) ;
}
static int asn1_get_time ( fmap_t * map , const void * * asn1data , unsigned int * size , time_t * tm )
{
struct cli_asn1 obj ;
int ret = asn1_get_obj ( map , * asn1data , size , & obj ) ;
unsigned int len ;
char * ptr ;
struct tm t ;
int n ;
if ( ret )
return ret ;
if ( obj . type = = 0x17 ) /* UTCTime - YYMMDDHHMMSSZ */
len = 13 ;
else if ( obj . type = = 0x18 ) /* GeneralizedTime - YYYYMMDDHHMMSSZ */
len = 15 ;
else {
cli_dbgmsg ( " asn1_get_time: expected UTCTime or GeneralizedTime, got %02x \n " , obj . type ) ;
return 1 ;
}
if ( ! fmap_need_ptr_once ( map , obj . content , len ) ) {
cli_dbgmsg ( " asn1_get_time: failed to read content \n " ) ;
return 1 ;
}
memset ( & t , 0 , sizeof ( t ) ) ;
ptr = ( char * ) obj . content ;
if ( obj . type = = 0x18 ) {
t . tm_year = asn1_getnum ( ptr ) * 100 ;
if ( t . tm_year < 0 )
return 1 ;
n = asn1_getnum ( ptr ) ;
if ( n < 0 )
return 1 ;
t . tm_year + = n ;
ptr + = 4 ;
} else {
n = asn1_getnum ( ptr ) ;
if ( n < 0 )
return 1 ;
if ( n > = 50 )
t . tm_year = 1900 + n ;
else
t . tm_year = 2000 + n ;
ptr + = 2 ;
}
t . tm_year - = 1900 ;
n = asn1_getnum ( ptr ) ;
if ( n < 1 | | n > 12 ) {
cli_dbgmsg ( " asn1_get_time: invalid month %u \n " , n ) ;
return 1 ;
}
t . tm_mon = n - 1 ;
ptr + = 2 ;
n = asn1_getnum ( ptr ) ;
if ( n < 1 | | n > 31 ) {
cli_dbgmsg ( " asn1_get_time: invalid day %u \n " , n ) ;
return 1 ;
}
t . tm_mday = n ;
ptr + = 2 ;
n = asn1_getnum ( ptr ) ;
if ( n < 0 | | n > 23 ) {
cli_dbgmsg ( " asn1_get_time: invalid hour %u \n " , n ) ;
return 1 ;
}
t . tm_hour = n ;
ptr + = 2 ;
n = asn1_getnum ( ptr ) ;
if ( n < 0 | | n > 59 ) {
cli_dbgmsg ( " asn1_get_time: invalid minute %u \n " , n ) ;
return 1 ;
}
t . tm_min = n ;
ptr + = 2 ;
if ( * ptr = = ' Z ' ) {
/* NOTE: RFC5280 requires that the UTCDate fields in X509 certs
* include the seconds ( it ' s optional in the UTCDate definition ) ,
* but one CA time - stamping cert used by ~ 1 , 700 samples on VirusTotal
* omits the seconds . These samples still validate successfully ,
* though , so allow it here .
*
* In this case we will have fmap ' d in two extra bytes unrelated to
* the UTCDate ( and failed if there weren ' t two bytes afterward ) ,
* but that shouldn ' t have an affect in practice since there will
* always be more signature data following the UTCDate data that we
* parse . */
t . tm_sec = 0 ;
} else {
n = asn1_getnum ( ptr ) ;
if ( n < 0 | | n > 59 ) {
cli_dbgmsg ( " asn1_get_time: invalid second %u \n " , n ) ;
return 1 ;
}
t . tm_sec = n ;
ptr + = 2 ;
if ( * ptr ! = ' Z ' ) {
cli_dbgmsg ( " asn1_get_time: expected UTC time 'Z', got '%c' \n " , * ptr ) ;
return 1 ;
}
}
* tm = mktime ( & t ) ;
* asn1data = obj . next ;
return 0 ;
}
static int asn1_get_rsa_pubkey ( fmap_t * map , const void * * asn1data , unsigned int * size , cli_crt * x509 )
{
struct cli_asn1 obj ;
unsigned int avail , avail2 ;
if ( asn1_expect_objtype ( map , * asn1data , size , & obj , ASN1_TYPE_SEQUENCE ) ) /* subjectPublicKeyInfo */
return 1 ;
* asn1data = obj . next ;
avail = obj . size ;
if ( asn1_expect_algo ( map , & obj . content , & avail , lenof ( OID_rsaEncryption ) , OID_rsaEncryption ) ) { /* rsaEncryption */
cli_dbgmsg ( " asn1_get_rsa_pubkey: AlgorithmIdentifier other than RSA not yet supported \n " ) ;
return 1 ;
}
if ( asn1_expect_objtype ( map , obj . content , & avail , & obj , ASN1_TYPE_BIT_STRING ) ) /* BIT STRING - subjectPublicKey */
return 1 ;
if ( avail ) {
cli_dbgmsg ( " asn1_get_rsa_pubkey: found unexpected extra data in subjectPublicKeyInfo \n " ) ;
return 1 ;
}
/* if(obj.size != 141 && obj.size != 271) /\* encoded len of 1024 and 2048 bit public keys *\/ */
/* return 1; */
if ( ! fmap_need_ptr_once ( map , obj . content , 1 ) ) {
cli_dbgmsg ( " asn1_get_rsa_pubkey: cannot read public key content \n " ) ;
return 1 ;
}
if ( ( ( uint8_t * ) obj . content ) [ 0 ] ! = 0 ) { /* no byte fragments */
cli_dbgmsg ( " asn1_get_rsa_pubkey: unexpected byte frags in public key \n " ) ;
return 1 ;
}
avail = obj . size - 1 ;
obj . content = ( ( uint8_t * ) obj . content ) + 1 ;
if ( asn1_expect_objtype ( map , obj . content , & avail , & obj , ASN1_TYPE_SEQUENCE ) ) /* SEQUENCE */
return 1 ;
if ( avail ) {
cli_dbgmsg ( " asn1_get_rsa_pubkey: found unexpected extra data in public key content \n " ) ;
return 1 ;
}
avail = obj . size ;
if ( asn1_expect_objtype ( map , obj . content , & avail , & obj , ASN1_TYPE_INTEGER ) ) /* INTEGER - mod */
return 1 ;
if ( obj . size < 1024 / 8 | | obj . size > 4096 / 8 + 1 ) {
cli_dbgmsg ( " asn1_get_rsa_pubkey: modulus has got an unsupported length (%u) \n " , obj . size * 8 ) ;
return 1 ;
}
avail2 = obj . size ;
if ( ! fmap_need_ptr_once ( map , obj . content , avail2 ) ) {
cli_dbgmsg ( " asn1_get_rsa_pubkey: cannot read n \n " ) ;
return 1 ;
}
2023-01-14 18:28:39 +08:00
fp_read_unsigned_bin ( & x509 - > n , obj . content , avail2 ) ;
2022-10-22 18:41:00 +08:00
if ( asn1_expect_objtype ( map , obj . next , & avail , & obj , ASN1_TYPE_INTEGER ) ) /* INTEGER - exp */
return 1 ;
if ( avail ) {
cli_dbgmsg ( " asn1_get_rsa_pubkey: found unexpected extra data after exp \n " ) ;
return 1 ;
}
if ( obj . size < 1 | | obj . size > avail2 ) {
cli_dbgmsg ( " asn1_get_rsa_pubkey: exponent has got an unsupported length (%u) \n " , obj . size * 8 ) ;
return 1 ;
}
if ( ! fmap_need_ptr_once ( map , obj . content , obj . size ) ) {
cli_dbgmsg ( " asn1_get_rsa_pubkey: cannot read e \n " ) ;
return 1 ;
}
2023-01-14 18:28:39 +08:00
fp_read_unsigned_bin ( & x509 - > e , obj . content , obj . size ) ;
2022-10-22 18:41:00 +08:00
return 0 ;
}
# define ASN1_GET_X509_SUCCESS 0
# define ASN1_GET_X509_CERT_ERROR 1
# define ASN1_GET_X509_UNRECOVERABLE_ERROR 2
/* Parse the asn1data associated with an x509 certificate and add the cert
* to the crtmgr certs if it doesn ' t already exist there .
* ASN1_GET_X509_CERT_ERROR will be returned in the case that an invalid x509
* certificate is encountered but asn1data and size are suitable for continued
* signature parsing . ASN1_GET_X509_UNRECOVERABLE_ERROR will be returned in
* the case where asn1data and size are not suitable for continued use . */
static int asn1_get_x509 ( fmap_t * map , const void * * asn1data , unsigned int * size , crtmgr * crts )
{
struct cli_asn1 crt , tbs , obj ;
unsigned int avail , tbssize , issuersize ;
cli_crt_hashtype hashtype1 , hashtype2 ;
cli_crt x509 ;
const uint8_t * tbsdata ;
const void * next , * issuer ;
int ret = ASN1_GET_X509_UNRECOVERABLE_ERROR ;
unsigned int version ;
2023-01-14 18:28:39 +08:00
cli_crt_init ( & x509 ) ;
2022-10-22 18:41:00 +08:00
do {
if ( asn1_expect_objtype ( map , * asn1data , size , & crt , ASN1_TYPE_SEQUENCE ) ) { /* SEQUENCE */
cli_dbgmsg ( " asn1_get_x509: expected SEQUENCE at the x509 start \n " ) ;
break ;
}
* asn1data = crt . next ;
/* After this point, an error is recoverable because asn1data and size
* will be suitable for continued use by the caller , so change ret */
ret = ASN1_GET_X509_CERT_ERROR ;
tbsdata = crt . content ;
if ( asn1_expect_objtype ( map , crt . content , & crt . size , & tbs , ASN1_TYPE_SEQUENCE ) ) { /* SEQUENCE - TBSCertificate */
cli_dbgmsg ( " asn1_get_x509: expected SEQUENCE at the TBSCertificate start \n " ) ;
break ;
}
tbssize = ( uint8_t * ) tbs . next - tbsdata ;
/* The version field of the x509 certificate is optional, defaulting
* to 1 if the field is not present . Version 3 is backward compatible ,
* adding the optional issuerUniqueID , sujectUniqueID , and extensions
* fields . We ' ll try to handle both cases , since the Windows API
* appears to allow for both ( despite the fact that the 2008 spec doc
* says that v3 certificates are used for everything ) */
if ( asn1_get_obj ( map , tbs . content , & tbs . size , & obj ) ) {
cli_dbgmsg ( " asn1_get_x509: failed to get first item in the TBSCertificate \n " ) ;
break ;
}
if ( 0xa0 = = obj . type ) { /* [0] */
avail = obj . size ;
next = obj . next ;
// TODO Should we support v2 certs? Supposedly they are not widely used...
if ( asn1_expect_obj ( map , & obj . content , & avail , ASN1_TYPE_INTEGER , 1 , " \x02 " ) ) { /* version 3 only (indicated by '\x02')*/
cli_dbgmsg ( " asn1_get_x509: unexpected type or value for TBSCertificate version \n " ) ;
break ;
}
if ( avail ) {
cli_dbgmsg ( " asn1_get_x509: found unexpected extra data in version \n " ) ;
break ;
}
version = 3 ;
if ( asn1_expect_objtype ( map , next , & tbs . size , & obj , ASN1_TYPE_INTEGER ) ) { /* serialNumber */
cli_dbgmsg ( " asn1_get_x509: expected x509 serial INTEGER \n " ) ;
break ;
}
} else if ( ASN1_TYPE_INTEGER = = obj . type ) {
/* The version field is missing, so we'll assume that this is a
* version 1 certificate . obj points to the serialNumber
* INTEGER , then , so just continue on to map it . */
version = 1 ;
/* v1 certificates don't have enough information to convey the
* purpose of the certificate . I ' ve only ever seen these used
* in the timestamp signing chain , so set the flags to indicate
* that . */
x509 . certSign = 1 ;
x509 . codeSign = 0 ;
x509 . timeSign = 1 ;
} else {
cli_dbgmsg ( " asn1_get_x509: expected version or serialNumber as the first item in TBSCertificate \n " ) ;
break ;
}
if ( map_raw ( map , obj . content , obj . size , x509 . raw_serial ) )
break ;
if ( map_sha1 ( map , obj . content , obj . size , x509 . serial ) )
break ;
if ( asn1_expect_rsa ( map , & obj . next , & tbs . size , & hashtype1 ) ) { /* algo - Ex: sha1WithRSAEncryption */
cli_dbgmsg ( " asn1_get_x509: unable to parse AlgorithmIdentifier \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , obj . next , & tbs . size , & obj , ASN1_TYPE_SEQUENCE ) ) { /* issuer */
cli_dbgmsg ( " asn1_get_x509: expected SEQUENCE when parsing cert issuer \n " ) ;
break ;
}
issuer = obj . content ;
issuersize = obj . size ;
if ( asn1_expect_objtype ( map , obj . next , & tbs . size , & obj , ASN1_TYPE_SEQUENCE ) ) { /* validity */
cli_dbgmsg ( " asn1_get_x509: expected SEQUENCE when parsing cert validity \n " ) ;
break ;
}
avail = obj . size ;
next = obj . content ;
if ( asn1_get_time ( map , & next , & avail , & x509 . not_before ) ) { /* notBefore */
cli_dbgmsg ( " asn1_get_x509: unable to extract the notBefore time \n " ) ;
break ;
}
if ( asn1_get_time ( map , & next , & avail , & x509 . not_after ) ) { /* notAfter */
cli_dbgmsg ( " asn1_get_x509: unable to extract the notAfter time \n " ) ;
break ;
}
if ( x509 . not_before > = x509 . not_after ) {
cli_dbgmsg ( " asn1_get_x509: bad validity \n " ) ;
break ;
}
if ( avail ) {
cli_dbgmsg ( " asn1_get_x509: found unexpected extra data in validity \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , obj . next , & tbs . size , & obj , ASN1_TYPE_SEQUENCE ) ) { /* subject */
cli_dbgmsg ( " asn1_get_x509: expected SEQUENCE when parsing cert subject \n " ) ;
break ;
}
if ( map_raw ( map , obj . content , obj . size , x509 . raw_subject ) )
break ;
if ( map_sha1 ( map , obj . content , obj . size , x509 . subject ) )
break ;
if ( asn1_get_rsa_pubkey ( map , & obj . next , & tbs . size , & x509 ) ) { /* subjectPublicKeyInfo */
cli_dbgmsg ( " asn1_get_x509: failed to get RSA public key \n " ) ;
break ;
}
if ( 1 = = version & & tbs . size ) {
cli_dbgmsg ( " asn1_get_x509: TBSCertificate should not contain fields beyond subjectPublicKeyInfo if version == 1 \n " ) ;
break ;
}
avail = 0 ;
while ( tbs . size ) {
if ( asn1_get_obj ( map , obj . next , & tbs . size , & obj ) ) {
tbs . size = 1 ;
break ;
}
if ( obj . type < = 0xa0 + avail | | obj . type > 0xa3 ) {
cli_dbgmsg ( " asn1_get_x509: found type %02x in extensions, expecting a1, a2 or a3 \n " , obj . type ) ;
tbs . size = 1 ;
break ;
}
avail = obj . type - 0xa0 ;
if ( obj . type = = 0xa3 ) {
struct cli_asn1 exts ;
int have_key_usage = 0 ;
int have_ext_key = 0 ;
if ( asn1_expect_objtype ( map , obj . content , & obj . size , & exts , ASN1_TYPE_SEQUENCE ) ) {
tbs . size = 1 ;
break ;
}
if ( obj . size ) {
cli_dbgmsg ( " asn1_get_x509: found unexpected extra data in extensions \n " ) ;
break ;
}
while ( exts . size ) {
struct cli_asn1 ext , id , value ;
if ( asn1_expect_objtype ( map , exts . content , & exts . size , & ext , ASN1_TYPE_SEQUENCE ) ) {
exts . size = 1 ;
break ;
}
exts . content = ext . next ;
if ( asn1_expect_objtype ( map , ext . content , & ext . size , & id , ASN1_TYPE_OBJECT_ID ) ) {
exts . size = 1 ;
break ;
}
if ( asn1_get_obj ( map , id . next , & ext . size , & value ) ) {
exts . size = 1 ;
break ;
}
if ( value . type = = ASN1_TYPE_BOOLEAN ) {
/* critical flag */
if ( value . size ! = 1 ) {
cli_dbgmsg ( " asn1_get_x509: found boolean with wrong length \n " ) ;
exts . size = 1 ;
break ;
}
if ( asn1_get_obj ( map , value . next , & ext . size , & value ) ) {
exts . size = 1 ;
break ;
}
}
if ( value . type ! = ASN1_TYPE_OCTET_STRING ) {
cli_dbgmsg ( " asn1_get_x509: bad extension value type %u \n " , value . type ) ;
exts . size = 1 ;
break ;
}
if ( ext . size ) {
cli_dbgmsg ( " asn1_get_x509: extra data in extension \n " ) ;
exts . size = 1 ;
break ;
}
if ( id . size ! = 3 )
continue ;
if ( ! fmap_need_ptr_once ( map , id . content , 3 ) ) {
exts . size = 1 ;
break ;
}
if ( ! memcmp ( " \x55 \x1d \x0f " , id . content , 3 ) ) {
/* KeyUsage 2.5.29.15 */
const uint8_t * keyusage = value . content ;
uint8_t usage ;
have_key_usage = 1 ;
if ( value . size < 4 | | value . size > 5 ) {
cli_dbgmsg ( " asn1_get_x509: bad KeyUsage \n " ) ;
exts . size = 1 ;
break ;
}
if ( ! fmap_need_ptr_once ( map , value . content , value . size ) ) {
exts . size = 1 ;
break ;
}
if ( keyusage [ 0 ] ! = 0x03 | | keyusage [ 1 ] ! = value . size - 2 | | keyusage [ 2 ] > 7 ) {
cli_dbgmsg ( " asn1_get_x509: bad KeyUsage \n " ) ;
exts . size = 1 ;
break ;
}
usage = keyusage [ 3 ] ;
if ( value . size = = 4 )
usage & = ~ ( ( 1 < < keyusage [ 2 ] ) - 1 ) ;
x509 . certSign = ( ( usage & 4 ) ! = 0 ) ;
continue ;
}
if ( ! memcmp ( " \x55 \x1d \x25 " , id . content , 3 ) ) {
/* ExtKeyUsage 2.5.29.37 */
struct cli_asn1 keypurp ;
have_ext_key = 1 ;
if ( asn1_expect_objtype ( map , value . content , & value . size , & keypurp , ASN1_TYPE_SEQUENCE ) ) {
exts . size = 1 ;
break ;
}
if ( value . size ) {
cli_dbgmsg ( " asn1_get_x509: extra data in ExtKeyUsage \n " ) ;
exts . size = 1 ;
break ;
}
ext . next = keypurp . content ;
while ( keypurp . size ) {
if ( asn1_expect_objtype ( map , ext . next , & keypurp . size , & ext , ASN1_TYPE_OBJECT_ID ) ) {
exts . size = 1 ;
break ;
}
if ( ext . size ! = 8 & & ext . size ! = 10 )
continue ;
if ( ! fmap_need_ptr_once ( map , ext . content , ext . size ) ) {
exts . size = 1 ;
break ;
}
if ( ! memcmp ( " \x2b \x06 \x01 \x05 \x05 \x07 \x03 \x03 " , ext . content , 8 ) ) /* id_kp_codeSigning */
x509 . codeSign = 1 ;
else if ( ! memcmp ( " \x2b \x06 \x01 \x05 \x05 \x07 \x03 \x08 " , ext . content , 8 ) ) /* id_kp_timeStamping */
x509 . timeSign = 1 ;
else if ( ! memcmp ( " \x2b \x06 \x01 \x04 \x01 \x82 \x37 \x0a \x03 \x0d " , ext . content , 10 ) ) /* id_kp_lifetimeSigning */
cli_dbgmsg ( " asn1_get_x509: lifetime signing specified but enforcing this is not currently supported \n " ) ;
}
continue ;
}
if ( ! memcmp ( " \x55 \x1d \x13 " , id . content , 3 ) ) {
/* Basic Constraints 2.5.29.19
*
* BasicConstraints : : = SEQUENCE {
* cA BOOLEAN DEFAULT FALSE ,
* pathLenConstraint INTEGER ( 0. . MAX ) OPTIONAL } */
struct cli_asn1 constr ;
if ( asn1_expect_objtype ( map , value . content , & value . size , & constr , ASN1_TYPE_SEQUENCE ) ) {
exts . size = 1 ;
break ;
}
if ( ! constr . size )
x509 . certSign = 0 ;
else {
if ( asn1_get_obj ( map , constr . content , & constr . size , & ext ) ) {
exts . size = 1 ;
break ;
}
if ( ext . type = = ASN1_TYPE_BOOLEAN ) {
if ( ext . size ! = 1 ) {
cli_dbgmsg ( " asn1_get_x509: wrong bool size in basic constraint %u \n " , ext . size ) ;
exts . size = 1 ;
break ;
}
if ( ! fmap_need_ptr_once ( map , ext . content , 1 ) ) {
exts . size = 1 ;
break ;
}
x509 . certSign = ( ( ( uint8_t * ) ( ext . content ) ) [ 0 ] ! = 0 ) ;
} else if ( ext . type = = ASN1_TYPE_INTEGER ) {
/* In this case, assume cA is missing and
* pathLenConstraint is present . Default cA
* to False . */
x509 . certSign = 0 ;
} else {
exts . size = 1 ;
break ;
}
}
}
}
if ( exts . size ) {
tbs . size = 1 ;
break ;
}
/* The 2008 spec doc says that for a certificate to be used for
* code signing , it must either have an EKU indicating code
* signing or the entire certificate chain must not have any
* EKUs .
* TODO We should actually enforce that last check .
* For time stamping , the doc says the EKU must be present , and
* makes no exception for EKUs being missing .
* TODO Should we not set timeSign = 1 in this case , then ? */
if ( ! have_ext_key )
x509 . codeSign = x509 . timeSign = 1 ;
/* RFC 3280 section 4.2.1.3 says that if a certificate is
* used to validate digital signatures on other public key
* certificates , it MUST have a key usage extension with the
* appropriate bits set . However , the MS MD5 root authority
* certificate ( A43489159A520F0D93D032CCAF37E7FE20A8B419 )
* doesn ' t have a KU or any EKUs , and PEs with it in the
* chain validate successfully .
* TODO Flip the certSign bit for now , but revisit if
* a clarification on this becomes available */
if ( ! have_key_usage )
x509 . certSign = 1 ;
}
}
if ( tbs . size ) {
cli_dbgmsg ( " asn1_get_x509: An error occurred when parsing x509 extensions \n " ) ;
break ;
}
if ( ! x509 . certSign & & ! x509 . codeSign & & ! x509 . timeSign ) {
cli_dbgmsg ( " asn1_get_x509: encountered a certificate with no cert, code, or time signing capabilities \n " ) ;
}
if ( map_raw ( map , issuer , issuersize , x509 . raw_issuer ) )
break ;
if ( map_sha1 ( map , issuer , issuersize , x509 . issuer ) )
break ;
if ( asn1_expect_rsa ( map , & tbs . next , & crt . size , & hashtype2 ) ) /* signature algo - Ex: sha1WithRSAEncryption */
break ;
if ( hashtype1 ! = hashtype2 ) {
cli_dbgmsg ( " asn1_get_x509: found conflicting RSA hash types \n " ) ;
break ;
}
x509 . hashtype = hashtype1 ;
if ( crtmgr_lookup ( crts , & x509 ) ) {
cli_dbgmsg ( " asn1_get_x509: duplicate embedded certificates detected \n " ) ;
cli_crt_clear ( & x509 ) ;
return ASN1_GET_X509_SUCCESS ;
}
if ( asn1_expect_objtype ( map , tbs . next , & crt . size , & obj , ASN1_TYPE_BIT_STRING ) ) { /* signature */
cli_dbgmsg ( " asn1_get_x509: Failed to parse x509 signature BIT STRING \n " ) ;
break ;
}
if ( obj . size > 513 ) {
cli_dbgmsg ( " asn1_get_x509: signature too long \n " ) ;
break ;
}
if ( ! fmap_need_ptr_once ( map , obj . content , obj . size ) ) {
cli_dbgmsg ( " asn1_get_x509: cannot read signature \n " ) ;
break ;
}
2023-01-14 18:28:39 +08:00
fp_read_unsigned_bin ( & x509 . sig , obj . content , obj . size ) ;
2022-10-22 18:41:00 +08:00
if ( crt . size ) {
cli_dbgmsg ( " asn1_get_x509: found unexpected extra data in signature \n " ) ;
break ;
}
if ( map_hash ( map , tbsdata , tbssize , x509 . tbshash , x509 . hashtype ) ) {
cli_dbgmsg ( " asn1_get_x509: Unsupported hashtype or hash computation failed \n " ) ;
break ;
}
if ( crtmgr_add ( crts , & x509 ) )
break ;
cli_crt_clear ( & x509 ) ;
return ASN1_GET_X509_SUCCESS ;
} while ( 0 ) ;
cli_crt_clear ( & x509 ) ;
return ret ;
}
static int asn1_parse_countersignature ( fmap_t * map , const void * * asn1data , unsigned int * size , crtmgr * cmgr , const uint8_t * message , const unsigned int message_size , time_t not_before , time_t not_after )
{
struct cli_asn1 asn1 , deep , deeper ;
uint8_t issuer [ SHA1_HASH_SIZE ] , serial [ SHA1_HASH_SIZE ] ;
const uint8_t * attrs ;
unsigned int dsize , attrs_size ;
unsigned int avail ;
uint8_t hash [ MAX_HASH_SIZE ] ;
cli_crt_hashtype hashtype ;
cli_crt_hashtype hashtype2 ;
unsigned int hashsize ;
uint8_t md [ MAX_HASH_SIZE ] ;
int result ;
void * ctx ;
do {
if ( asn1_expect_objtype ( map , * asn1data , size , & asn1 , ASN1_TYPE_SEQUENCE ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: expected SEQUENCE inside counterSignature SET \n " ) ;
break ;
}
avail = asn1 . size ;
if ( asn1_expect_objtype ( map , asn1 . content , & avail , & deep , ASN1_TYPE_INTEGER ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: expected INTEGER for counterSignature version " ) ;
break ;
}
if ( deep . size ! = 1 ) {
cli_dbgmsg ( " asn1_parse_countersignature: expected INTEGER of size 1, got size %u \n " , deep . size ) ;
break ;
}
if ( ! fmap_need_ptr_once ( map , deep . content , 1 ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: failed to read version \n " ) ;
break ;
}
/* Allow either '0' or '1' for the version. The specification says
* that this field must be 1 , but some binaries have 0 here and
* they appear to validate just fine via the Windows API */
if ( memcmp ( deep . content , " \x01 " , 1 ) & & memcmp ( deep . content , " \x00 " , 1 ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: counterSignature version is not 1 or 0 \n " ) ;
break ;
}
asn1 . content = deep . next ;
if ( asn1_expect_objtype ( map , asn1 . content , & avail , & asn1 , ASN1_TYPE_SEQUENCE ) ) { /* issuerAndSerialNumber */
cli_dbgmsg ( " asn1_parse_countersignature: unable to parse issuerAndSerialNumber SEQUENCE in counterSignature \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , asn1 . content , & asn1 . size , & deep , ASN1_TYPE_SEQUENCE ) ) { /* issuer */
cli_dbgmsg ( " asn1_parse_countersignature: unable to parse issuer SEQUENCE in counterSignature \n " ) ;
break ;
}
// Compute the hash of the issuer section
if ( map_sha1 ( map , deep . content , deep . size , issuer ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: error in call to map_sha1 for counterSignature issuer \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , deep . next , & asn1 . size , & deep , ASN1_TYPE_INTEGER ) ) { /* serial */
cli_dbgmsg ( " asn1_parse_countersignature: expected ASN1_TYPE_INTEGER serial for counterSignature \n " ) ;
break ;
}
// Compute the hash of the serial INTEGER
if ( map_sha1 ( map , deep . content , deep . size , serial ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: error in call to map_sha1 for counterSignature serial \n " ) ;
break ;
}
if ( asn1 . size ) {
cli_dbgmsg ( " asn1_parse_countersignature: extra data inside counterSignature issuer \n " ) ;
break ;
}
if ( asn1_expect_hash_algo ( map , & asn1 . next , & avail , & hashtype , & hashsize ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: error parsing counterSignature digestAlgorithm \n " ) ;
break ;
}
if ( map_hash ( map , message , message_size , md , hashtype ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: failed to map in message/compute countersignature hash \n " ) ;
break ;
}
attrs = asn1 . next ;
if ( asn1_expect_objtype ( map , asn1 . next , & avail , & asn1 , 0xa0 ) ) { /* authenticatedAttributes */
cli_dbgmsg ( " asn1_parse_countersignature: unable to parse counterSignature authenticatedAttributes section \n " ) ;
break ;
}
attrs_size = ( uint8_t * ) ( asn1 . next ) - attrs ;
if ( asn1 . next = = NULL & & attrs_size < 2 ) {
cli_dbgmsg ( " asn1_parse_countersignature: counterSignature authenticatedAttributes are too small \n " ) ;
break ;
}
result = 0 ;
dsize = asn1 . size ;
deep . next = asn1 . content ;
while ( dsize ) {
int content ;
if ( asn1_expect_objtype ( map , deep . next , & dsize , & deep , ASN1_TYPE_SEQUENCE ) ) { /* attribute */
cli_dbgmsg ( " asn1_parse_countersignature: expected counterSignature attribute SEQUENCE \n " ) ;
dsize = 1 ;
break ;
}
if ( asn1_expect_objtype ( map , deep . content , & deep . size , & deeper , ASN1_TYPE_OBJECT_ID ) ) { /* attribute type */
cli_dbgmsg ( " asn1_parse_countersignature: expected attribute type inside counterSignature attribute SEQUENCE \n " ) ;
dsize = 1 ;
break ;
}
if ( deeper . size ! = lenof ( OID_contentType ) ) /* lenof(contentType) = lenof(messageDigest) = lenof(signingTime) = 9 */
continue ;
if ( ! fmap_need_ptr_once ( map , deeper . content , lenof ( OID_contentType ) ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: failed to read counterSignature authenticated attribute \n " ) ;
dsize = 1 ;
break ;
}
if ( ! memcmp ( deeper . content , OID_contentType , lenof ( OID_contentType ) ) )
content = 0 ; /* contentType */
else if ( ! memcmp ( deeper . content , OID_messageDigest , lenof ( OID_messageDigest ) ) )
content = 1 ; /* messageDigest */
else if ( ! memcmp ( deeper . content , OID_signingTime , lenof ( OID_signingTime ) ) )
content = 2 ; /* signingTime */
else
continue ;
if ( result & ( 1 < < content ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: duplicate field in countersignature \n " ) ;
dsize = 1 ;
break ;
}
result | = ( 1 < < content ) ;
if ( asn1_expect_objtype ( map , deeper . next , & deep . size , & deeper , ASN1_TYPE_SET ) ) { /* set - contents */
cli_dbgmsg ( " asn1_parse_countersignature: failed to read counterSignature authenticated attribute \n " ) ;
dsize = 1 ;
break ;
}
if ( deep . size ) {
cli_dbgmsg ( " asn1_parse_countersignature: extra data in countersignature value \n " ) ;
dsize = 1 ;
break ;
}
deep . size = deeper . size ;
switch ( content ) {
case 0 : { /* contentType = pkcs7-data */
const void * backupPtr = deeper . content ;
unsigned int backupSize = deep . size ;
if ( asn1_expect_obj ( map , & deeper . content , & deep . size , ASN1_TYPE_OBJECT_ID , lenof ( OID_pkcs7_data ) , OID_pkcs7_data ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: contentType != pkcs7-data, checking for timestampToken instead \n " ) ;
/* Some signatures use OID_timestampToken instead, so allow
2023-01-14 18:28:39 +08:00
* that also ( despite the 2008 spec saying that this value
* must be pkcs7 - data ) */
2022-10-22 18:41:00 +08:00
deeper . content = backupPtr ;
deep . size = backupSize ;
if ( asn1_expect_obj ( map , & deeper . content , & deep . size , ASN1_TYPE_OBJECT_ID , lenof ( OID_timestampToken ) , OID_timestampToken ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: contentType != timestampToken \n " ) ;
deep . size = 1 ;
break ;
}
}
if ( deep . size )
cli_dbgmsg ( " asn1_parse_countersignature: extra data in countersignature content-type \n " ) ;
break ;
}
case 1 : /* messageDigest */
if ( asn1_expect_obj ( map , & deeper . content , & deep . size , ASN1_TYPE_OCTET_STRING , hashsize , md ) ) {
deep . size = 1 ;
cli_dbgmsg ( " asn1_parse_countersignature: countersignature hash mismatch \n " ) ;
} else if ( deep . size )
cli_dbgmsg ( " asn1_parse_countersignature: extra data in countersignature message-digest \n " ) ;
break ;
case 2 : /* signingTime */
{
time_t sigdate ; /* FIXME shall i use it?! */
if ( asn1_get_time ( map , & deeper . content , & deep . size , & sigdate ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: an error occurred when getting the time \n " ) ;
deep . size = 1 ;
} else if ( deep . size )
cli_dbgmsg ( " asn1_parse_countersignature: extra data in countersignature signing-time \n " ) ;
else if ( sigdate < not_before | | sigdate > not_after ) {
cli_dbgmsg ( " asn1_parse_countersignature: countersignature timestamp outside cert validity \n " ) ;
deep . size = 1 ;
}
break ;
}
}
if ( deep . size ) {
dsize = 1 ;
break ;
}
}
if ( dsize )
break ;
if ( result ! = 7 ) {
cli_dbgmsg ( " asn1_parse_countersignature: some important attributes are missing in countersignature \n " ) ;
break ;
}
// TODO For some reason there tends to be more variability here than
// when parsing the regular signature - we have to support at least
// szOID_RSA_RSA and szOID_RSA_SHA1RSA based on samples seen in the
// wild. The spec says this should only be the RSA and DSA OIDs,
// though.
if ( asn1_expect_rsa ( map , & asn1 . next , & avail , & hashtype2 ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: unable to parse the digestEncryptionAlgorithm \n " ) ;
break ;
}
if ( hashtype2 ! = CLI_RSA & & hashtype2 ! = hashtype ) {
cli_dbgmsg ( " asn1_parse_countersignature: digestEncryptionAlgorithm conflicts with digestAlgorithm \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , asn1 . next , & avail , & asn1 , ASN1_TYPE_OCTET_STRING ) ) { /* encryptedDigest */
cli_dbgmsg ( " asn1_parse_countersignature: unexpected encryptedDigest value in counterSignature \n " ) ;
break ;
}
if ( asn1 . size > 513 ) {
cli_dbgmsg ( " asn1_parse_countersignature: countersignature encryptedDigest too long \n " ) ;
break ;
}
if ( avail ) {
cli_dbgmsg ( " asn1_parse_countersignature: extra data inside countersignature \n " ) ;
break ;
}
if ( ! fmap_need_ptr_once ( map , attrs , attrs_size ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: failed to read authenticatedAttributes \n " ) ;
break ;
}
if ( NULL = = ( ctx = get_hash_ctx ( hashtype ) ) ) {
break ;
}
cl_update_hash ( ctx , " \x31 " , 1 ) ;
cl_update_hash ( ctx , ( void * ) ( attrs + 1 ) , attrs_size - 1 ) ;
cl_finish_hash ( ctx , hash ) ;
if ( ! fmap_need_ptr_once ( map , asn1 . content , asn1 . size ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: failed to read countersignature encryptedDigest \n " ) ;
break ;
}
if ( ! crtmgr_verify_pkcs7 ( cmgr , issuer , serial , asn1 . content , asn1 . size , hashtype , hash , VRFY_TIME ) ) {
cli_dbgmsg ( " asn1_parse_countersignature: pkcs7 countersignature verification failed \n " ) ;
break ;
}
cli_dbgmsg ( " asn1_parse_countersignature: countersignature verification completed successfully \n " ) ;
return 0 ;
} while ( 0 ) ;
return 1 ;
}
static cl_error_t asn1_parse_mscat ( struct cl_engine * engine , fmap_t * map , size_t offset , unsigned int size , crtmgr * cmgr , int embedded , const void * * hashes , unsigned int * hashes_size , cli_ctx * ctx )
{
struct cli_asn1 asn1 , deep , deeper ;
uint8_t issuer [ SHA1_HASH_SIZE ] , serial [ SHA1_HASH_SIZE ] ;
const uint8_t * message , * attrs ;
unsigned int dsize , message_size , attrs_size ;
// hash is used to hold the hashes we compute as part of sig verification
uint8_t hash [ MAX_HASH_SIZE ] ;
cli_crt_hashtype hashtype , hashtype2 ;
unsigned int hashsize ;
// md is used to hold the message digest we extract from the signature
uint8_t md [ MAX_HASH_SIZE ] ;
cli_crt * x509 ;
void * hash_ctx ;
int result ;
cl_error_t ret = CL_EPARSE ;
cli_dbgmsg ( " in asn1_parse_mscat \n " ) ;
do {
if ( ! ( message = fmap_need_off_once ( map , offset , 1 ) ) ) {
cli_dbgmsg ( " asn1_parse_mscat: failed to read pkcs#7 entry \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , message , & size , & asn1 , ASN1_TYPE_SEQUENCE ) ) { /* SEQUENCE */
cli_dbgmsg ( " asn1_parse_mscat: expected SEQUENCE at top level \n " ) ;
break ;
}
// Many signatures have zero bytes at the end (padding?)
/* if(size) { */
/* cli_dbgmsg("asn1_parse_mscat: found extra data after pkcs#7 %u\n", size); */
/* break; */
/* } */
size = asn1 . size ;
if ( asn1_expect_obj ( map , & asn1 . content , & size , ASN1_TYPE_OBJECT_ID , lenof ( OID_signedData ) , OID_signedData ) ) { /* OBJECT 1.2.840.113549.1.7.2 - contentType = signedData */
cli_dbgmsg ( " asn1_parse_mscat: expected contentType == signedData \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , asn1 . content , & size , & asn1 , 0xa0 ) ) { /* [0] - content */
cli_dbgmsg ( " asn1_parse_mscat: expected '[0] - content' following signedData contentType \n " ) ;
break ;
}
if ( size ) {
cli_dbgmsg ( " asn1_parse_mscat: found extra data in pkcs#7 \n " ) ;
break ;
}
size = asn1 . size ;
if ( asn1_expect_objtype ( map , asn1 . content , & size , & asn1 , ASN1_TYPE_SEQUENCE ) ) { /* SEQUENCE */
cli_dbgmsg ( " asn1_parse_mscat: expected SEQUENCE inside signedData '[0] - content' \n " ) ;
break ;
}
if ( size ) {
cli_dbgmsg ( " asn1_parse_mscat: found extra data in signedData \n " ) ;
break ;
}
size = asn1 . size ;
if ( asn1_expect_obj ( map , & asn1 . content , & size , ASN1_TYPE_INTEGER , 1 , " \x01 " ) ) { /* INTEGER - VERSION 1 */
cli_dbgmsg ( " asn1_parse_mscat: expected 'INTEGER - VERSION 1' for signedData version \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , asn1 . content , & size , & asn1 , ASN1_TYPE_SET ) ) { /* SET OF DigestAlgorithmIdentifier */
cli_dbgmsg ( " asn1_parse_mscat: expected SET OF DigestAlgorithmIdentifier inside signedData \n " ) ;
break ;
}
// At this point asn1.next points to the SEQUENCE following the
// DigestAlgorithmIdentifier SET, so we'll want to preserve it so
// we can continue parsing laterally. We also want to preserve
// size, since it tracks how much is left in the SignedData section.
if ( asn1_expect_hash_algo ( map , & asn1 . content , & asn1 . size , & hashtype , & hashsize ) ) {
cli_dbgmsg ( " asn1_parse_mscat: error parsing SignedData digestAlgorithm \n " ) ;
break ;
}
if ( asn1 . size ) {
cli_dbgmsg ( " asn1_parse_mscat: found extra data in the SignerData digestAlgorithm SET \n " ) ;
break ;
}
// We've finished parsing the DigestAlgorithmIdentifiers SET, so start
// back parsing the SignedData
if ( asn1_expect_objtype ( map , asn1 . next , & size , & asn1 , ASN1_TYPE_SEQUENCE ) ) { /* SEQUENCE - contentInfo */
cli_dbgmsg ( " asn1_parse_mscat: expected 'SEQUENCE - contentInfo' inside SignedData following DigestAlgorithmIdentifiers \n " ) ;
break ;
}
// Parse the contentInfo SEQUENCE. asn1.next and size point to the
// certificates, so these need to be preserved
/* Here there is either a PKCS #7 ContentType Object Identifier for Certificate Trust List (szOID_CTL)
* or a single SPC_INDIRECT_DATA_OBJID */
if (
( ! embedded & & asn1_expect_obj ( map , & asn1 . content , & asn1 . size , ASN1_TYPE_OBJECT_ID , lenof ( OID_szOID_CTL ) , OID_szOID_CTL ) ) | |
( embedded & & asn1_expect_obj ( map , & asn1 . content , & asn1 . size , ASN1_TYPE_OBJECT_ID , lenof ( OID_SPC_INDIRECT_DATA_OBJID ) , OID_SPC_INDIRECT_DATA_OBJID ) ) ) {
cli_dbgmsg ( " asn1_parse_mscat: unexpected ContentType for embedded mode %d \n " , embedded ) ;
break ;
}
if ( asn1_expect_objtype ( map , asn1 . content , & asn1 . size , & deep , 0xa0 ) ) {
cli_dbgmsg ( " asn1_parse_mscat: expected '[0] - content' following DigestAlgorithmIdentifier contentType \n " ) ;
break ;
}
if ( asn1 . size ) {
cli_dbgmsg ( " asn1_parse_mscat: found extra data in contentInfo \n " ) ;
break ;
}
dsize = deep . size ;
if ( asn1_expect_objtype ( map , deep . content , & dsize , & deep , ASN1_TYPE_SEQUENCE ) ) {
cli_dbgmsg ( " asn1_parse_mscat: expected SEQUENCE in DigestAlgorithmIdentifier '[0] - contentInfo' \n " ) ;
break ;
}
if ( dsize ) {
cli_dbgmsg ( " asn1_parse_mscat: found extra data in content \n " ) ;
break ;
}
/*
* Hashes should look like :
* SEQUENCE ( 2 elem )
* OBJECT IDENTIFIER 1.3 .6 .1 .4 .1 .311 .2 .1 .15 spcPEImageData
* SEQUENCE ( 2 elem )
* BIT STRING ( 0 elem )
* [ 0 ] ( 1 elem )
* [ 2 ] ( 1 elem )
* [ 0 ]
* SEQUENCE ( 2 elem )
* SEQUENCE ( 2 elem )
* OBJECT IDENTIFIER 1.3 .14 .3 .2 .26 sha1 ( OIW )
* NULL
* OCTET STRING ( 20 byte )
*/
* hashes = deep . content ;
* hashes_size = deep . size ;
// Now resume parsing SignedData - certificates
if ( asn1_expect_objtype ( map , asn1 . next , & size , & asn1 , 0xa0 ) ) { /* certificates */
cli_dbgmsg ( " asn1_parse_mscat: expected 0xa0 certificates entry \n " ) ;
break ;
}
dsize = asn1 . size ;
if ( dsize ) {
crtmgr newcerts ;
crtmgr_init ( & newcerts ) ;
while ( dsize ) {
result = asn1_get_x509 ( map , & asn1 . content , & dsize , & newcerts ) ;
if ( ASN1_GET_X509_UNRECOVERABLE_ERROR = = result ) {
dsize = 1 ;
break ;
} else if ( ASN1_GET_X509_CERT_ERROR = = result ) {
cli_dbgmsg ( " asn1_parse_mscat: skipping x509 certificate with errors \n " ) ;
}
}
if ( dsize ) {
crtmgr_free ( & newcerts ) ;
cli_dbgmsg ( " asn1_parse_mscat: an unrecoverable error occurred while extracting x509 certificates \n " ) ;
break ;
}
if ( newcerts . crts ) {
x509 = newcerts . crts ;
cli_dbgmsg ( " asn1_parse_mscat: %u embedded certificates collected \n " , newcerts . items ) ;
if ( engine - > engine_options & ENGINE_OPTIONS_PE_DUMPCERTS ) {
/* Dump the certs if requested before anything happens to them */
while ( x509 ) {
char raw_issuer [ CRT_RAWMAXLEN * 2 + 1 ] , raw_subject [ CRT_RAWMAXLEN * 2 + 1 ] , raw_serial [ CRT_RAWMAXLEN * 3 + 1 ] ;
char issuer [ SHA1_HASH_SIZE * 2 + 1 ] , subject [ SHA1_HASH_SIZE * 2 + 1 ] , serial [ SHA1_HASH_SIZE * 2 + 1 ] ;
char mod [ 1024 + 1 ] , exp [ 1024 + 1 ] ;
int j = 1024 ;
fp_toradix_n ( & x509 - > n , mod , 16 , j + 1 ) ;
fp_toradix_n ( & x509 - > e , exp , 16 , j + 1 ) ;
memset ( raw_issuer , 0 , CRT_RAWMAXLEN * 2 + 1 ) ;
memset ( raw_subject , 0 , CRT_RAWMAXLEN * 2 + 1 ) ;
memset ( raw_serial , 0 , CRT_RAWMAXLEN * 2 + 1 ) ;
for ( j = 0 ; j < x509 - > raw_issuer [ 0 ] ; j + + )
sprintf ( & raw_issuer [ j * 2 ] , " %02x " , x509 - > raw_issuer [ j + 1 ] ) ;
for ( j = 0 ; j < x509 - > raw_subject [ 0 ] ; j + + )
sprintf ( & raw_subject [ j * 2 ] , " %02x " , x509 - > raw_subject [ j + 1 ] ) ;
for ( j = 0 ; j < x509 - > raw_serial [ 0 ] ; j + + )
sprintf ( & raw_serial [ j * 3 ] , " %02x%c " , x509 - > raw_serial [ j + 1 ] , ( j ! = x509 - > raw_serial [ 0 ] - 1 ) ? ' : ' : ' \0 ' ) ;
for ( j = 0 ; j < SHA1_HASH_SIZE ; j + + ) {
sprintf ( & issuer [ j * 2 ] , " %02x " , x509 - > issuer [ j ] ) ;
sprintf ( & subject [ j * 2 ] , " %02x " , x509 - > subject [ j ] ) ;
sprintf ( & serial [ j * 2 ] , " %02x " , x509 - > serial [ j ] ) ;
}
// TODO The raw information we print out here isn't
// very helpful, since it's only the first 64-bytes...
// Change this so that raw is only populated when the
// debug flag is set, and then copy/display the full
// contents.
cli_dbgmsg ( " cert: \n " ) ;
cli_dbgmsg ( " subject: %s \n " , subject ) ;
cli_dbgmsg ( " serial: %s \n " , serial ) ;
cli_dbgmsg ( " pubkey: %s \n " , mod ) ;
cli_dbgmsg ( " i: %s %lu->%lu %s%s%s \n " , issuer , ( unsigned long ) x509 - > not_before , ( unsigned long ) x509 - > not_after , x509 - > codeSign ? " code " : " " , x509 - > timeSign ? " time " : " " , x509 - > certSign ? " cert " : " " ) ;
cli_dbgmsg ( " ==============RAW============== \n " ) ;
cli_dbgmsg ( " raw_subject: %s \n " , raw_subject ) ;
cli_dbgmsg ( " raw_serial: %s \n " , raw_serial ) ;
cli_dbgmsg ( " raw_issuer: %s \n " , raw_issuer ) ;
x509 = x509 - > next ;
}
x509 = newcerts . crts ;
}
2023-01-14 18:28:39 +08:00
/* Determine whether the embedded certificate is blocked or
* trusted . If an embedded cert matches a block list rule ,
2022-10-22 18:41:00 +08:00
* we can return immediately indicating that a sig matched .
2023-01-14 18:28:39 +08:00
* This isn ' t true for allow list matches , since otherwise an
2022-10-22 18:41:00 +08:00
* attacker could just include a known - good certificate in the
* signature and not use it . Instead , for those we will add the
* embedded cert to the trust store and continue on to ensure
* that a trusted cert is used for signing . */
while ( x509 ) {
cli_crt * crt ;
/* Use &(engine->cmgr) for this check, since we don't copy
2023-01-14 18:28:39 +08:00
* block list certs into cmgr and so that if there ' s a
2022-10-22 18:41:00 +08:00
* match , we have a long - lived pointer that we can pass
* back ( via cli_append_virus ) indicating the name of the
* sigs that matched ( we can ' t just malloc new space for
* one , since nothing above here knows to free it . ) */
2023-01-14 18:28:39 +08:00
if ( NULL ! = ( crt = crtmgr_block_list_lookup ( & ( engine - > cmgr ) , x509 ) ) ) {
2022-10-22 18:41:00 +08:00
ret = CL_VIRUS ;
2023-01-14 18:28:39 +08:00
cli_dbgmsg ( " asn1_parse_mscat: Found Authenticode certificate blocked by %s \n " , crt - > name ? crt - > name : " (unnamed CRB rule) " ) ;
2022-10-22 18:41:00 +08:00
if ( NULL ! = ctx ) {
ret = cli_append_virus ( ctx , crt - > name ? crt - > name : " (unnamed CRB rule) " ) ;
2023-01-14 18:28:39 +08:00
if ( ret = = CL_VIRUS ) {
2022-10-22 18:41:00 +08:00
crtmgr_free ( & newcerts ) ;
goto finish ;
}
}
/* In the case where ctx is NULL, we don't care about
2023-01-14 18:28:39 +08:00
* block list matches - we are either using this
2022-10-22 18:41:00 +08:00
* function to parse . cat rules that were loaded in ,
* or it ' s sigtool doing cert printing . */
}
/* NOTE: Since the 'issuer' cli_crt field is required for
* Authenticode validation , we rely on adding embedded
* certs with the ' issuer ' actually set into our trust
* store for doing the time / code digital signature checks .
* This isn ' t required for cert - signing certs that
* we discover this way , since the CRB cli_crts have enough
2023-01-14 18:28:39 +08:00
* info to be able to trust other certs , but executing
2022-10-22 18:41:00 +08:00
* the following code for those has the benefit of removing
* them from newcerts so they aren ' t processed again while
* looking for chained trust . */
2023-01-14 18:28:39 +08:00
if ( NULL ! = ( crt = crtmgr_trust_list_lookup ( cmgr , x509 , 1 ) ) ) {
2022-10-22 18:41:00 +08:00
cli_crt * tmp = x509 - > next ;
2023-01-14 18:28:39 +08:00
cli_dbgmsg ( " asn1_parse_mscat: Directly trusting embedded cert based on %s \n " , ( crt - > name ? crt - > name : " (no name) " ) ) ;
2022-10-22 18:41:00 +08:00
if ( cli_debug_flag & & crt - > name ) {
// Copy the name from the CRB entry for printing below
x509 - > name = strdup ( crt - > name ) ;
}
if ( crtmgr_add ( cmgr , x509 ) ) {
cli_dbgmsg ( " asn1_parse_mscat: adding x509 cert to crtmgr failed \n " ) ;
break ;
}
crtmgr_del ( & newcerts , x509 ) ;
x509 = tmp ;
continue ;
}
x509 = x509 - > next ;
}
if ( x509 ) {
crtmgr_free ( & newcerts ) ;
break ;
}
if ( CL_VIRUS = = ret ) {
crtmgr_free ( & newcerts ) ;
break ;
}
x509 = newcerts . crts ;
/* Now look for cases where embedded certs can be trusted
* indirectly because they are signed by trusted certs */
while ( x509 ) {
cli_crt * parent ;
/* If the certificate is in the trust store already, remove
* it from the newcerts list . This is legacy code that I ' m
* assuming tries to prevent us from doing the expensive
* RSA verification in the case where the same cert is
* embedded multiple times ? Sure , why not */
2023-01-14 18:28:39 +08:00
if ( crtmgr_trust_list_lookup ( cmgr , x509 , 0 ) ) {
2022-10-22 18:41:00 +08:00
cli_crt * tmp = x509 - > next ;
cli_dbgmsg ( " asn1_parse_mscat: found embedded certificate matching one in the trust store \n " ) ;
crtmgr_del ( & newcerts , x509 ) ;
x509 = tmp ;
continue ;
}
/* Determine whether the cert is signed by one in our trust
* store */
parent = crtmgr_verify_crt ( cmgr , x509 ) ;
if ( parent ) {
2023-01-14 18:28:39 +08:00
cli_dbgmsg ( " asn1_parse_mscat: Indirectly trusting embedded cert based on %s \n " , ( parent - > name ? parent - > name : " (no name) " ) ) ;
2022-10-22 18:41:00 +08:00
// TODO Why is this done? It seems like you should be
// able to have a parent cert can only do cert signing
// and have that be able to sign a cert used for
// code-signing...
x509 - > codeSign & = parent - > codeSign ;
x509 - > timeSign & = parent - > timeSign ;
if ( crtmgr_add ( cmgr , x509 ) ) {
cli_dbgmsg ( " asn1_parse_mscat: adding x509 cert to crtmgr failed \n " ) ;
break ;
}
crtmgr_del ( & newcerts , x509 ) ;
/* Start at the beginning of newcerts so that we can see
* whether adding this new trusted cert causes more
* certs to be trusted ( via chaining ) . Otherwise we
* might miss valid certs if the ordering in the binary
* doesn ' t align with the chain ordering . */
x509 = newcerts . crts ;
continue ;
}
x509 = x509 - > next ;
}
if ( x509 ) {
crtmgr_free ( & newcerts ) ;
break ;
}
if ( newcerts . items )
cli_dbgmsg ( " asn1_parse_mscat: %u certificates did not verify \n " , newcerts . items ) ;
crtmgr_free ( & newcerts ) ;
}
}
// Parse the final section in SignedData - SignerInfos
if ( asn1_get_obj ( map , asn1 . next , & size , & asn1 ) ) {
cli_dbgmsg ( " asn1_parse_mscat: failed to get next ASN1 section \n " ) ;
break ;
}
if ( asn1 . type = = 0xa1 & & asn1_get_obj ( map , asn1 . next , & size , & asn1 ) ) { /* crls - unused shouldn't be present */
cli_dbgmsg ( " asn1_parse_mscat: unexpected CRL entries were found \n " ) ;
break ;
}
if ( asn1 . type ! = ASN1_TYPE_SET ) { /* signerInfos */
cli_dbgmsg ( " asn1_parse_mscat: unexpected type %02x for signerInfo \n " , asn1 . type ) ;
break ;
}
if ( size ) {
cli_dbgmsg ( " asn1_parse_mscat: unexpected extra data after signerInfos \n " ) ;
break ;
}
size = asn1 . size ;
if ( asn1_expect_objtype ( map , asn1 . content , & size , & asn1 , ASN1_TYPE_SEQUENCE ) ) {
cli_dbgmsg ( " asn1_parse_mscat: expected SEQUENCE in signerInfos " ) ;
break ;
}
if ( size ) {
cli_dbgmsg ( " asn1_parse_mscat: only one signerInfo shall be present \n " ) ;
break ;
}
size = asn1 . size ;
if ( asn1_expect_obj ( map , & asn1 . content , & size , ASN1_TYPE_INTEGER , 1 , " \x01 " ) ) { /* Version = 1 */
cli_dbgmsg ( " asn1_parse_mscat: expected Version == 1 for signerInfo \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , asn1 . content , & size , & asn1 , ASN1_TYPE_SEQUENCE ) ) { /* issuerAndSerialNumber */
cli_dbgmsg ( " asn1_parse_mscat: expected issuerAndSerialNumber SEQUENCE \n " ) ;
break ;
}
// asn1.next and size must be preserved so we can continue parsing
// SignerInfos, so switch to deep
dsize = asn1 . size ;
if ( asn1_expect_objtype ( map , asn1 . content , & dsize , & deep , ASN1_TYPE_SEQUENCE ) ) { /* issuer */
cli_dbgmsg ( " asn1_parse_mscat: expected issuer SEQUENCE \n " ) ;
break ;
}
/* Make sure the issuer ID is mapped into memory and then compute the
* SHA1 of it so we can use this value in verification later on . This
* will be a hash over all the values in the issuer SEQUENCE , which
* looks something like :
* SET ( 1 elem )
* SEQUENCE ( 2 elem )
* OBJECT IDENTIFIER 2.5 .4 .6 countryName ( X .520 DN component )
* PrintableString
* SET ( 1 elem )
* SEQUENCE ( 2 elem )
* OBJECT IDENTIFIER2 .5 .4 .8 stateOrProvinceName ( X .520 DN component )
* PrintableString
* SET ( 1 elem )
* SEQUENCE ( 2 elem )
* OBJECT IDENTIFIER2 .5 .4 .7 localityName ( X .520 DN component )
* PrintableString
* SET ( 1 elem )
* SEQUENCE ( 2 elem )
* OBJECT IDENTIFIER2 .5 .4 .10 organizationName ( X .520 DN component )
* PrintableString
* SET ( 1 elem )
* SEQUENCE ( 2 elem )
* OBJECT IDENTIFIER2 .5 .4 .3 commonName ( X .520 DN component )
* PrintableString
*/
if ( map_sha1 ( map , deep . content , deep . size , issuer ) ) {
cli_dbgmsg ( " asn1_parse_mscat: error in call to map_sha1 for issuer \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , deep . next , & dsize , & deep , ASN1_TYPE_INTEGER ) ) { /* serial */
cli_dbgmsg ( " asn1_parse_mscat: expected ASN1_TYPE_INTEGER serial \n " ) ;
break ;
}
/* Make sure the serial INTEGER is mapped into memory and compute the
* SHA1 of it so we can use this value in verification later on . */
if ( map_sha1 ( map , deep . content , deep . size , serial ) ) {
cli_dbgmsg ( " asn1_parse_mscat: error in call to map_sha1 for serial \n " ) ;
break ;
}
if ( dsize ) {
cli_dbgmsg ( " asn1_parse_mscat: extra data inside issuerAndSerialNumber \n " ) ;
break ;
}
// Resume parsing the SignerInfos using asn1.next and size
if ( asn1_expect_hash_algo ( map , & asn1 . next , & size , & hashtype2 , & hashsize ) ) {
cli_dbgmsg ( " asn1_parse_mscat: error parsing SignerInfo digestAlgorithm \n " ) ;
break ;
}
// Verify that the SignerInfo digestAlgorithm matches the one from the SignedData section
if ( hashtype ! = hashtype2 ) {
cli_dbgmsg ( " asn1_parse_mscat: SignerInfo digestAlgorithm is not the same as the algorithm in SignedData \n " ) ;
break ;
}
// Continue on to the authenticatedAttributes section within SignerInfo
attrs = asn1 . next ;
if ( asn1_expect_objtype ( map , asn1 . next , & size , & asn1 , 0xa0 ) ) { /* authenticatedAttributes */
cli_dbgmsg ( " asn1_parse_mscat: unable to parse authenticatedAttributes section \n " ) ;
break ;
}
attrs_size = ( uint8_t * ) ( asn1 . next ) - attrs ;
if ( asn1 . next = = NULL | | attrs_size < 2 ) {
cli_dbgmsg ( " asn1_parse_mscat: authenticatedAttributes size is too small \n " ) ;
break ;
}
dsize = asn1 . size ;
deep . next = asn1 . content ;
result = 0 ;
while ( dsize ) {
struct cli_asn1 cobj ;
int content ;
if ( asn1_expect_objtype ( map , deep . next , & dsize , & deep , ASN1_TYPE_SEQUENCE ) ) { /* attribute */
cli_dbgmsg ( " asn1_parse_mscat: expected attribute SEQUENCE \n " ) ;
dsize = 1 ;
break ;
}
if ( asn1_expect_objtype ( map , deep . content , & deep . size , & deeper , ASN1_TYPE_OBJECT_ID ) ) { /* attribute type */
cli_dbgmsg ( " asn1_parse_mscat: expected attribute type inside attribute SEQUENCE \n " ) ;
dsize = 1 ;
break ;
}
if ( deeper . size ! = lenof ( OID_contentType ) )
continue ;
if ( ! fmap_need_ptr_once ( map , deeper . content , lenof ( OID_contentType ) ) ) {
cli_dbgmsg ( " asn1_parse_mscat: failed to read authenticated attribute \n " ) ;
dsize = 1 ;
break ;
}
if ( ! memcmp ( deeper . content , OID_contentType , lenof ( OID_contentType ) ) )
content = 0 ; /* contentType */
else if ( ! memcmp ( deeper . content , OID_messageDigest , lenof ( OID_messageDigest ) ) )
content = 1 ; /* messageDigest */
else
continue ;
if ( asn1_expect_objtype ( map , deeper . next , & deep . size , & deeper , ASN1_TYPE_SET ) ) { /* set - contents */
cli_dbgmsg ( " asn1_parse_mscat: expected 'set - contents' for authenticated attribute \n " ) ;
dsize = 1 ;
break ;
}
if ( deep . size ) {
cli_dbgmsg ( " asn1_parse_mscat: extra data in authenticated attributes \n " ) ;
dsize = 1 ;
break ;
}
if ( result & ( 1 < < content ) ) {
cli_dbgmsg ( " asn1_parse_mscat: contentType or messageDigest appear twice \n " ) ;
dsize = 1 ;
break ;
}
if ( content = = 0 ) { /* contentType */
if (
( ! embedded & & asn1_expect_obj ( map , & deeper . content , & deeper . size , ASN1_TYPE_OBJECT_ID , lenof ( OID_szOID_CTL ) , OID_szOID_CTL ) ) | | /* cat file */
( embedded & & asn1_expect_obj ( map , & deeper . content , & deeper . size , ASN1_TYPE_OBJECT_ID , lenof ( OID_SPC_INDIRECT_DATA_OBJID ) , OID_SPC_INDIRECT_DATA_OBJID ) ) /* embedded cat */
) {
cli_dbgmsg ( " asn1_parse_mscat: unexpected ContentType for embedded mode %d (for authenticated attribute) \n " , embedded ) ;
dsize = 1 ;
break ;
}
result | = 1 ;
} else { /* messageDigest */
if ( asn1_expect_objtype ( map , deeper . content , & deeper . size , & cobj , ASN1_TYPE_OCTET_STRING ) ) {
cli_dbgmsg ( " asn1_parse_mscat: unexpected messageDigest value \n " ) ;
dsize = 1 ;
break ;
}
if ( cobj . size ! = hashsize ) {
cli_dbgmsg ( " asn1_parse_mscat: messageDigest attribute has the wrong size (%u) \n " , cobj . size ) ;
dsize = 1 ;
break ;
}
if ( ! fmap_need_ptr_once ( map , cobj . content , hashsize ) ) {
cli_dbgmsg ( " asn1_parse_mscat: failed to read authenticated attribute \n " ) ;
dsize = 1 ;
break ;
}
memcpy ( md , cobj . content , hashsize ) ;
result | = 2 ;
}
if ( deeper . size ) {
cli_dbgmsg ( " asn1_parse_mscat: extra data in authenticated attribute \n " ) ;
dsize = 1 ;
break ;
}
}
if ( dsize )
break ;
if ( result ! = 3 ) {
cli_dbgmsg ( " asn1_parse_mscat: contentType or messageDigest are missing \n " ) ;
break ;
}
if ( asn1_expect_algo ( map , & asn1 . next , & size , lenof ( OID_rsaEncryption ) , OID_rsaEncryption ) ) { /* digestEncryptionAlgorithm == rsa */
cli_dbgmsg ( " asn1_parse_mscat: digestEncryptionAlgorithms other than RSA are not yet supported \n " ) ;
break ;
}
if ( asn1_expect_objtype ( map , asn1 . next , & size , & asn1 , ASN1_TYPE_OCTET_STRING ) ) { /* encryptedDigest */
cli_dbgmsg ( " asn1_parse_mscat: unexpected encryptedDigest value \n " ) ;
break ;
}
// TODO Make this a #define with the greatest possible length (SHA512)
if ( asn1 . size > 513 ) {
cli_dbgmsg ( " asn1_parse_mscat: encryptedDigest too long \n " ) ;
break ;
}
if ( map_hash ( map , * hashes , * hashes_size , hash , hashtype ) ) {
cli_dbgmsg ( " asn1_parse_mscat: failed to map in message/compute message digest \n " ) ;
break ;
}
if ( memcmp ( hash , md , hashsize ) ) {
cli_dbgmsg ( " asn1_parse_mscat: messageDigest mismatch \n " ) ;
break ;
}
if ( ! fmap_need_ptr_once ( map , attrs , attrs_size ) ) {
cli_dbgmsg ( " asn1_parse_mscat: failed to read authenticatedAttributes \n " ) ;
break ;
}
if ( NULL = = ( hash_ctx = get_hash_ctx ( hashtype ) ) ) {
break ;
}
cl_update_hash ( hash_ctx , " \x31 " , 1 ) ;
cl_update_hash ( hash_ctx , ( void * ) ( attrs + 1 ) , attrs_size - 1 ) ;
cl_finish_hash ( hash_ctx , hash ) ;
if ( ! fmap_need_ptr_once ( map , asn1 . content , asn1 . size ) ) {
cli_dbgmsg ( " asn1_parse_mscat: failed to read encryptedDigest \n " ) ;
break ;
}
// Verify the authenticatedAttributes
if ( ! ( x509 = crtmgr_verify_pkcs7 ( cmgr , issuer , serial , asn1 . content , asn1 . size , hashtype , hash , VRFY_CODE ) ) ) {
cli_dbgmsg ( " asn1_parse_mscat: pkcs7 signature verification failed \n " ) ;
ret = CL_EVERIFY ;
break ;
}
message = asn1 . content ;
message_size = asn1 . size ;
cli_dbgmsg ( " asn1_parse_mscat: authenticatedAttributes successfully parsed and verified \n " ) ;
/* We need to verify the time validity of the certificate. If a
* signature has a time - stamping countersignature , then we just need to
* verify that countersignature . Otherwise , we should determine
* whether the signing certificate is still valid ( time - based , since at
2023-01-14 18:28:39 +08:00
* this point in the code no matching block list rules fired ) . */
2022-10-22 18:41:00 +08:00
if ( ! size ) {
time_t now ;
// No countersignature, so judge validity based on time
now = time ( NULL ) ;
if ( now < x509 - > not_before | | now > x509 - > not_after ) {
cli_dbgmsg ( " asn1_parse_mscat: no countersignature (unauthAttrs missing) and signing certificate has expired \n " ) ;
ret = CL_EVERIFY ;
break ;
}
cli_dbgmsg ( " asn1_parse_mscat: no countersignature (unauthAttrs missing) but the signing certificate is still valid \n " ) ;
ret = CL_CLEAN ;
goto finish ;
}
if ( size & & asn1_expect_objtype ( map , asn1 . next , & size , & asn1 , 0xa1 ) ) { /* unauthenticatedAttributes */
cli_dbgmsg ( " asn1_parse_mscat: unable to find unauthenticatedAttributes section \n " ) ;
break ;
}
if ( size ) {
cli_dbgmsg ( " asn1_parse_mscat: extra data inside signerInfo \n " ) ;
break ;
}
// Parse the unauthenticated attributes
dsize = asn1 . size ;
deep . next = asn1 . content ;
result = 0 ;
while ( dsize ) {
int content ;
if ( asn1_expect_objtype ( map , deep . next , & dsize , & deep , ASN1_TYPE_SEQUENCE ) ) {
cli_dbgmsg ( " asn1_parse_mscat: expected SEQUENCE starting an unauthenticatedAttribute \n " ) ;
dsize = 1 ;
break ;
}
if ( asn1_expect_objtype ( map , deep . content , & deep . size , & deeper , ASN1_TYPE_OBJECT_ID ) ) {
cli_dbgmsg ( " asn1_parse_mscat: expected OID inside unauthenticatedAttribute SEQUENCE \n " ) ;
dsize = 1 ;
break ;
}
// Supported OIDs include:
// - 1.2.840.113549.1.9.6 - counterSignature
// - 1.3.6.1.4.1.311.2.4.1 - nested signatures
// I've seen some other ones like 1.3.6.1.4.1.3845.3.9876.1.1.1,
// and the presence of those doesn't seem to mess up verification
// through the Windows API, so just skip those
2023-01-14 18:28:39 +08:00
if ( deeper . size ! = lenof ( OID_countersignature ) & &
deeper . size ! = lenof ( OID_nestedSignatures ) & &
deeper . size ! = lenof ( OID_RFC3161_countersignature ) ) {
2022-10-22 18:41:00 +08:00
continue ;
}
if ( ! fmap_need_ptr_once ( map , deeper . content , deeper . size ) ) {
cli_dbgmsg ( " asn1_parse_mscat: failed to read unauthenticated attribute OID \n " ) ;
dsize = 1 ;
break ;
}
2023-01-14 18:28:39 +08:00
if ( deeper . size = = lenof ( OID_countersignature ) & &
! memcmp ( deeper . content , OID_countersignature , lenof ( OID_countersignature ) ) ) {
2022-10-22 18:41:00 +08:00
content = 0 ; /* counterSignature */
2023-01-14 18:28:39 +08:00
} else if ( deeper . size = = lenof ( OID_nestedSignatures ) & &
! memcmp ( deeper . content , OID_nestedSignatures , lenof ( OID_nestedSignatures ) ) ) {
2022-10-22 18:41:00 +08:00
content = 1 ; /* nested */
2023-01-14 18:28:39 +08:00
} else if ( deeper . size = = lenof ( OID_RFC3161_countersignature ) & &
! memcmp ( deeper . content , OID_RFC3161_countersignature , lenof ( OID_RFC3161_countersignature ) ) ) {
content = 2 ; /* RFC3161 Counter Signature */
} else {
2022-10-22 18:41:00 +08:00
continue ;
}
if ( asn1_expect_objtype ( map , deeper . next , & deep . size , & deeper , ASN1_TYPE_SET ) ) { /* set - contents */
cli_dbgmsg ( " asn1_parse_mscat: expected 'set - contents' inside unauthenticated attribute \n " ) ;
dsize = 1 ;
break ;
}
if ( deep . size ) {
cli_dbgmsg ( " asn1_parse_mscat: extra data in unauthenticated attribute \n " ) ;
dsize = 1 ;
break ;
}
if ( result & ( 1 < < content ) ) {
cli_dbgmsg ( " asn1_parse_mscat: counterSignature or nestedSignature appear twice \n " ) ;
dsize = 1 ;
break ;
}
if ( content = = 0 ) { /* counterSignature */
if ( asn1_parse_countersignature ( map , & deeper . content , & deeper . size , cmgr , message , message_size , x509 - > not_before , x509 - > not_after ) ) {
dsize = 1 ;
break ;
}
result | = 1 ;
2023-01-14 18:28:39 +08:00
} else if ( content = = 1 ) { /* nestedSignature */
2022-10-22 18:41:00 +08:00
// TODO Support parsing these out in the future
cli_dbgmsg ( " asn1_parse_mscat: nested signatures detected but parsing them is not currently supported \n " ) ;
deeper . size = 0 ;
result | = 2 ;
2023-01-14 18:28:39 +08:00
} else if ( content = = 2 ) {
// TODO Support verifying these in the future
cli_dbgmsg ( " asn1_parse_mscat: RFC3161 timestamping countersignature detected but parsing them is not currently supported \n " ) ;
deeper . size = 0 ;
result | = 4 ;
2022-10-22 18:41:00 +08:00
}
if ( deeper . size ) {
2023-01-14 18:28:39 +08:00
2022-10-22 18:41:00 +08:00
cli_dbgmsg ( " asn1_parse_mscat: extra data in unauthenticated attribute \n " ) ;
dsize = 1 ;
break ;
}
}
if ( dsize )
break ;
cli_dbgmsg ( " asn1_parse_mscat: unauthenticatedAttributes successfully parsed \n " ) ;
if ( 1 ! = ( result & 1 ) ) {
time_t now ;
// No countersignature, so judge validity based on time
now = time ( NULL ) ;
if ( now < x509 - > not_before | | now > x509 - > not_after ) {
cli_dbgmsg ( " asn1_parse_mscat: no countersignature and signing certificate has expired \n " ) ;
ret = CL_EVERIFY ;
break ;
}
cli_dbgmsg ( " asn1_parse_mscat: no countersignature but the signing certificate is still valid \n " ) ;
}
ret = CL_CLEAN ;
} while ( 0 ) ;
finish :
if ( CL_EPARSE = = ret ) {
cli_dbgmsg ( " asn1_parse_mscat: failed to parse authenticode section \n " ) ;
}
return ret ;
}
int asn1_load_mscat ( fmap_t * map , struct cl_engine * engine )
{
struct cli_asn1 c ;
unsigned int size ;
2023-01-14 18:28:39 +08:00
unsigned int i ;
2022-10-22 18:41:00 +08:00
// TODO As currently implemented, loading in a .cat file with -d requires
2023-01-14 18:28:39 +08:00
// an accompanying .crb with trust entries that will cause the .cat
2022-10-22 18:41:00 +08:00
// file signatures to verify successfully. If a user is specifying a .cat
// file to use, though, we should assume they trust it and at least add the
// covered hashes from it to hm_fp
// TODO Since we pass engine->cmgr directly here, the whole chain of trust
// for this .cat file will get added to the global trust store assuming it
// verifies successfully. Is this a bug for a feature?
if ( CL_CLEAN ! = asn1_parse_mscat ( engine , map , 0 , map - > len , & engine - > cmgr , 0 , & c . next , & size , NULL ) )
return 1 ;
if ( asn1_expect_objtype ( map , c . next , & size , & c , ASN1_TYPE_SEQUENCE ) )
return 1 ;
if ( asn1_expect_obj ( map , & c . content , & c . size , ASN1_TYPE_OBJECT_ID , lenof ( OID_szOID_CATALOG_LIST ) , OID_szOID_CATALOG_LIST ) )
return 1 ;
if ( c . size ) {
cli_dbgmsg ( " asn1_load_mscat: found extra data in szOID_CATALOG_LIST content \n " ) ;
return 1 ;
}
if ( asn1_expect_objtype ( map , c . next , & size , & c , 0x4 ) ) /* List ID */
return 1 ;
if ( asn1_expect_objtype ( map , c . next , & size , & c , 0x17 ) ) /* Effective date - WTF?! */
return 1 ;
2023-01-14 18:28:39 +08:00
if ( asn1_expect_list_member ( map , & c . next , & size ) ) /* szOID_CATALOG_LIST_MEMBER or szOID_CATALOG_LIST_MEMBER2 */
2022-10-22 18:41:00 +08:00
return 1 ;
if ( asn1_expect_objtype ( map , c . next , & size , & c , ASN1_TYPE_SEQUENCE ) ) /* hashes here */
return 1 ;
/* [0] is next but we don't care as it's really descriptives stuff */
size = c . size ;
c . next = c . content ;
while ( size ) {
struct cli_asn1 tag ;
if ( asn1_expect_objtype ( map , c . next , & size , & c , ASN1_TYPE_SEQUENCE ) )
return 1 ;
if ( asn1_expect_objtype ( map , c . content , & c . size , & tag , ASN1_TYPE_OCTET_STRING ) ) /* TAG NAME */
return 1 ;
if ( asn1_expect_objtype ( map , tag . next , & c . size , & tag , ASN1_TYPE_SET ) ) /* set */
return 1 ;
if ( c . size ) {
cli_dbgmsg ( " asn1_load_mscat: found extra data in tag \n " ) ;
return 1 ;
}
while ( tag . size ) {
struct cli_asn1 tagval1 , tagval2 , tagval3 ;
2023-01-14 18:28:39 +08:00
int hashed_obj_type ;
cli_crt_hashtype hashtype ;
cli_hash_type_t hm_hashtype ;
unsigned int hashsize ;
2022-10-22 18:41:00 +08:00
if ( asn1_expect_objtype ( map , tag . content , & tag . size , & tagval1 , ASN1_TYPE_SEQUENCE ) )
return 1 ;
tag . content = tagval1 . next ;
if ( asn1_expect_objtype ( map , tagval1 . content , & tagval1 . size , & tagval2 , ASN1_TYPE_OBJECT_ID ) )
return 1 ;
if ( tagval2 . size ! = lenof ( OID_SPC_INDIRECT_DATA_OBJID ) )
continue ;
if ( ! fmap_need_ptr_once ( map , tagval2 . content , lenof ( OID_SPC_INDIRECT_DATA_OBJID ) ) ) {
cli_dbgmsg ( " asn1_load_mscat: cannot read SPC_INDIRECT_DATA \n " ) ;
return 1 ;
}
if ( memcmp ( tagval2 . content , OID_SPC_INDIRECT_DATA_OBJID , lenof ( OID_SPC_INDIRECT_DATA_OBJID ) ) )
continue ; /* stuff like CAT_NAMEVALUE_OBJID(1.3.6.1.4.1.311.12.2.1) and CAT_MEMBERINFO_OBJID(.2).. */
if ( asn1_expect_objtype ( map , tagval2 . next , & tagval1 . size , & tagval2 , ASN1_TYPE_SET ) )
return 1 ;
if ( tagval1 . size ) {
cli_dbgmsg ( " asn1_load_mscat: found extra data in tag value \n " ) ;
return 1 ;
}
if ( asn1_expect_objtype ( map , tagval2 . content , & tagval2 . size , & tagval1 , ASN1_TYPE_SEQUENCE ) )
return 1 ;
if ( tagval2 . size ) {
cli_dbgmsg ( " asn1_load_mscat: found extra data in SPC_INDIRECT_DATA_OBJID tag \n " ) ;
return 1 ;
}
if ( asn1_expect_objtype ( map , tagval1 . content , & tagval1 . size , & tagval2 , ASN1_TYPE_SEQUENCE ) )
return 1 ;
if ( asn1_expect_objtype ( map , tagval2 . content , & tagval2 . size , & tagval3 , ASN1_TYPE_OBJECT_ID ) ) /* shall have an obj 1.3.6.1.4.1.311.2.1.15 or 1.3.6.1.4.1.311.2.1.25 inside */
return 1 ;
if ( tagval3 . size ! = lenof ( OID_SPC_PE_IMAGE_DATA_OBJID ) ) { /* lenof(OID_SPC_PE_IMAGE_DATA_OBJID) = lenof(OID_SPC_CAB_DATA_OBJID) = 10*/
cli_dbgmsg ( " asn1_load_mscat: bad hash type size \n " ) ;
return 1 ;
}
if ( ! fmap_need_ptr_once ( map , tagval3 . content , lenof ( OID_SPC_PE_IMAGE_DATA_OBJID ) ) ) {
cli_dbgmsg ( " asn1_load_mscat: cannot read hash type \n " ) ;
return 1 ;
}
if ( ! memcmp ( tagval3 . content , OID_SPC_PE_IMAGE_DATA_OBJID , lenof ( OID_SPC_PE_IMAGE_DATA_OBJID ) ) )
2023-01-14 18:28:39 +08:00
hashed_obj_type = 2 ;
2022-10-22 18:41:00 +08:00
else if ( ! memcmp ( tagval3 . content , OID_SPC_CAB_DATA_OBJID , lenof ( OID_SPC_CAB_DATA_OBJID ) ) )
2023-01-14 18:28:39 +08:00
hashed_obj_type = 1 ;
2022-10-22 18:41:00 +08:00
else {
cli_dbgmsg ( " asn1_load_mscat: unexpected hash type \n " ) ;
return 1 ;
}
if ( asn1_expect_objtype ( map , tagval2 . next , & tagval1 . size , & tagval2 , ASN1_TYPE_SEQUENCE ) )
return 1 ;
if ( tagval1 . size ) {
cli_dbgmsg ( " asn1_load_mscat: found extra data after hash \n " ) ;
return 1 ;
}
2023-01-14 18:28:39 +08:00
if ( asn1_expect_hash_algo ( map , & tagval2 . content , & tagval2 . size , & hashtype , & hashsize ) ) {
cli_dbgmsg ( " asn1_load_mscat: failed to identify hash algorithm used \n " ) ;
return 1 ;
}
/* Translate hashtype to the enum used by the hasher */
if ( CLI_SHA1RSA = = hashtype ) {
hm_hashtype = CLI_HASH_SHA1 ;
} else if ( CLI_SHA256RSA = = hashtype ) {
hm_hashtype = CLI_HASH_SHA256 ;
} else {
cli_dbgmsg ( " asn1_load_mscat: only SHA1 and SHA256 hashes are supported for .cat file sigs \n " ) ;
2022-10-22 18:41:00 +08:00
return 1 ;
}
if ( asn1_expect_objtype ( map , tagval2 . content , & tagval2 . size , & tagval3 , ASN1_TYPE_OCTET_STRING ) )
return 1 ;
if ( tagval2 . size ) {
cli_dbgmsg ( " asn1_load_mscat: found extra data in hash \n " ) ;
return 1 ;
}
2023-01-14 18:28:39 +08:00
if ( tagval3 . size ! = hashsize ) {
2022-10-22 18:41:00 +08:00
cli_dbgmsg ( " asn1_load_mscat: bad hash size %u \n " , tagval3 . size ) ;
return 1 ;
}
2023-01-14 18:28:39 +08:00
if ( ! fmap_need_ptr_once ( map , tagval3 . content , hashsize ) ) {
2022-10-22 18:41:00 +08:00
cli_dbgmsg ( " asn1_load_mscat: cannot read hash \n " ) ;
return 1 ;
}
if ( cli_debug_flag ) {
2023-01-14 18:28:39 +08:00
char sha [ SHA256_HASH_SIZE * 2 + 1 ] = { 0 } ;
for ( i = 0 ; i < hashsize ; i + + )
sprintf ( & sha [ i * 2 ] , " %02x " , ( ( uint8_t * ) ( tagval3 . content ) ) [ i ] ) ;
cli_dbgmsg ( " asn1_load_mscat: got hash %s (%s) \n " , sha , ( hashed_obj_type = = 2 ) ? " PE " : " CAB " ) ;
2022-10-22 18:41:00 +08:00
}
if ( ! engine - > hm_fp ) {
if ( ! ( engine - > hm_fp = MPOOL_CALLOC ( engine - > mempool , 1 , sizeof ( * ( engine - > hm_fp ) ) ) ) ) {
tag . size = 1 ;
return 1 ;
}
# ifdef USE_MPOOL
engine - > hm_fp - > mempool = engine - > mempool ;
# endif
}
2023-01-14 18:28:39 +08:00
2022-10-22 18:41:00 +08:00
/* Load the trusted hashes into hm_fp, using the size values
* 1 and 2 as sentinel values corresponding to CAB and PE hashes
* from . cat files respectively . */
2023-01-14 18:28:39 +08:00
if ( hm_addhash_bin ( engine - > hm_fp , tagval3 . content , hm_hashtype , hashed_obj_type , NULL ) ) {
2022-10-22 18:41:00 +08:00
cli_warnmsg ( " asn1_load_mscat: failed to add hash \n " ) ;
return 1 ;
}
}
}
return 0 ;
}
/* Check an embedded PE Authenticode section to determine whether it's trusted.
* This will return CL_VERIFIED if the file should be trusted , CL_EPARSE if an
* error occurred while parsing the signature , CL_EVERIFY if parsing was
2023-01-14 18:28:39 +08:00
* successful but there were no trust rules for the signature , and
* CL_VIRUS if a block list rule was found for an embedded certificate .
2022-10-22 18:41:00 +08:00
*
2023-01-14 18:28:39 +08:00
* If CL_VIRUS is returned , certname will be set to the certname of block list
2022-10-22 18:41:00 +08:00
* rule that matched ( unless certname is NULL ) . */
cl_error_t asn1_check_mscat ( struct cl_engine * engine , fmap_t * map , size_t offset , unsigned int size , struct cli_mapped_region * regions , uint32_t nregions , cli_ctx * ctx )
{
unsigned int content_size ;
struct cli_asn1 c ;
cli_crt_hashtype hashtype ;
uint8_t hash [ MAX_HASH_SIZE ] ;
unsigned int hashsize ;
const void * content ;
crtmgr certs ;
int ret ;
void * hash_ctx ;
unsigned int i ;
cli_dbgmsg ( " in asn1_check_mscat (offset: %llu) \n " , ( long long unsigned ) offset ) ;
crtmgr_init ( & certs ) ;
2023-01-14 18:28:39 +08:00
/* Get a copy of all certs in the trust store, excluding block list certs */
2022-10-22 18:41:00 +08:00
if ( crtmgr_add_roots ( engine , & certs , 1 ) ) {
crtmgr_free ( & certs ) ;
return CL_EVERIFY ;
}
ret = asn1_parse_mscat ( engine , map , offset , size , & certs , 1 , & content , & content_size , ctx ) ;
crtmgr_free ( & certs ) ;
if ( CL_CLEAN ! = ret )
return ret ;
if ( asn1_expect_objtype ( map , content , & content_size , & c , ASN1_TYPE_SEQUENCE ) ) {
cli_dbgmsg ( " asn1_check_mscat: expected SEQUENCE at top level of hash container \n " ) ;
return CL_EPARSE ;
}
if ( asn1_expect_obj ( map , & c . content , & c . size , ASN1_TYPE_OBJECT_ID , lenof ( OID_SPC_PE_IMAGE_DATA_OBJID ) , OID_SPC_PE_IMAGE_DATA_OBJID ) ) {
cli_dbgmsg ( " asn1_check_mscat: expected spcPEImageData OID in the first hash SEQUENCE \n " ) ;
return CL_EPARSE ;
}
// TODO Should we do anything with the underlying SEQUENCE and data? From
// the 2008 spec doc it doesn't sound like many of the fields are used, so
// ignoring is probably fine for now
if ( asn1_expect_objtype ( map , c . next , & content_size , & c , ASN1_TYPE_SEQUENCE ) ) {
cli_dbgmsg ( " asn1_check_mscat: expected second hash container object to be a SEQUENCE \n " ) ;
return CL_EPARSE ;
}
if ( content_size ) {
cli_dbgmsg ( " asn1_check_mscat: extra data in hash SEQUENCE \n " ) ;
return CL_EPARSE ;
}
if ( asn1_expect_hash_algo ( map , & c . content , & c . size , & hashtype , & hashsize ) ) {
cli_dbgmsg ( " asn1_check_mscat: unexpected file hash algo \n " ) ;
return CL_EPARSE ;
}
if ( NULL = = ( hash_ctx = get_hash_ctx ( hashtype ) ) ) {
return CL_EPARSE ;
}
// Now that we know the hash algorithm, compute the authenticode hash
// across the required regions of memory.
for ( i = 0 ; i < nregions ; i + + ) {
const uint8_t * hptr ;
if ( 0 = = regions [ i ] . size ) {
continue ;
}
if ( ! ( hptr = fmap_need_off_once ( map , regions [ i ] . offset , regions [ i ] . size ) ) ) {
return CL_EVERIFY ;
}
cl_update_hash ( hash_ctx , hptr , regions [ i ] . size ) ;
}
cl_finish_hash ( hash_ctx , hash ) ;
if ( cli_debug_flag ) {
char hashtxt [ MAX_HASH_SIZE * 2 + 1 ] ;
for ( i = 0 ; i < hashsize ; i + + )
sprintf ( & hashtxt [ i * 2 ] , " %02x " , hash [ i ] ) ;
cli_dbgmsg ( " Authenticode: %s \n " , hashtxt ) ;
}
if ( asn1_expect_obj ( map , & c . content , & c . size , ASN1_TYPE_OCTET_STRING , hashsize , hash ) ) {
cli_dbgmsg ( " asn1_check_mscat: computed authenticode hash did not match stored value \n " ) ;
return CL_EVERIFY ;
}
if ( c . size ) {
cli_dbgmsg ( " asn1_check_mscat: extra data after the stored authenticode hash \n " ) ;
return CL_EPARSE ;
}
2023-01-14 18:28:39 +08:00
cli_dbgmsg ( " asn1_check_mscat: file with valid authenticode signature, trusted \n " ) ;
2022-10-22 18:41:00 +08:00
return CL_VERIFIED ;
}