libclamav/asn1.c
5d49bd06
 /*
  *  Copyright (C) 2011 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>
 
b28a0d6f
 #include "asn1.h"
5d49bd06
 #include "sha1.h"
71da61a7
 #include "md5.h"
b28a0d6f
 #include "bignum.h"
f0a5895b
 #include "matcher-hash.h"
b28a0d6f
 
a616029e
 /* --------------------------------------------------------------------------- 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_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_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_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_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
 
 #define lenof(x) (sizeof((x))-1)
 /* --------------------------------------------------------------------------- OIDS */
 
774120ff
 struct cli_asn1 {
     uint8_t type;
     unsigned int size;
d1fcd16d
     const void *content;
     const void *next;
774120ff
 };
a616029e
 
998f97f1
 static int map_sha1(fmap_t *map, const void *data, unsigned int len, uint8_t sha1[SHA1_HASH_SIZE]) {
e5c6c1aa
     SHA1Context ctx;
     if(!fmap_need_ptr_once(map, data, len)) {
 	cli_dbgmsg("map_sha1: failed to read hash data\n");
 	return 1;
     }
     SHA1Init(&ctx);
765788a8
     while(len) {
 	unsigned int todo = MIN(len, map->pgsz);
 	SHA1Update(&ctx, data, todo);
 	data = (uint8_t *)data + todo;
 	len -= todo;
     }
e5c6c1aa
     SHA1Final(&ctx, sha1);
     return 0;
 }
b28a0d6f
 
d1fcd16d
 static int map_md5(fmap_t *map, const void *data, unsigned int len, uint8_t *md5) {
71da61a7
     cli_md5_ctx ctx;
     if(!fmap_need_ptr_once(map, data, len)) {
 	cli_dbgmsg("map_md5: failed to read hash data\n");
 	return 1;
     }
     cli_md5_init(&ctx);
765788a8
     while(len) {
 	unsigned int todo = MIN(len, map->pgsz);
 	cli_md5_update(&ctx, data, len);
 	data = (uint8_t *)data + todo;
 	len -= todo;
     }
71da61a7
     cli_md5_final(md5, &ctx);
     return 0;
 }
 
c3b355d8
 
d1fcd16d
 static int asn1_get_obj(fmap_t *map, const void *asn1data, unsigned int *asn1len, struct cli_asn1 *obj) {
b28a0d6f
     unsigned int asn1_sz = *asn1len;
     unsigned int readbytes = MIN(6, asn1_sz), i;
d1fcd16d
     const uint8_t *data;
b28a0d6f
 
     if(asn1_sz < 2) {
5b48b665
 	cli_dbgmsg("asn1_get_obj: insufficient data length\n");
b28a0d6f
 	return 1;
     }
     data = fmap_need_ptr_once(map, asn1data, readbytes);
     if(!data) {
5b48b665
 	cli_dbgmsg("asn1_get_obj: obj out of file\n");
b28a0d6f
 	return 1;
     }
 
     obj->type = data[0];
     i = data[1];
     data+=2;
     if(i & 0x80) {
 	if(i == 0x80) {
7bcfb2f3
 	    /* Not allowed in DER */
b28a0d6f
 	    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;
 }
 
d1fcd16d
 static int asn1_expect_objtype(fmap_t *map, const void *asn1data, unsigned int *asn1len, struct cli_asn1 *obj, uint8_t type) {
b28a0d6f
     int ret = asn1_get_obj(map, asn1data, asn1len, obj);
     if(ret)
 	return ret;
5b48b665
     if(obj->type != type) {
 	cli_dbgmsg("asn1_expect_objtype: expected type %02x, got %02x\n", type, obj->type);
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
     return 0;
 }
 
d1fcd16d
 static int asn1_expect_obj(fmap_t *map, const void **asn1data, unsigned int *asn1len, uint8_t type, unsigned int size, const void *content) {
d29b5c5f
     struct cli_asn1 obj;
     int ret = asn1_expect_objtype(map, *asn1data, asn1len, &obj, type);
b28a0d6f
     if(ret)
 	return ret;
d29b5c5f
     if(obj.size != size) {
 	cli_dbgmsg("asn1_expect_obj: expected size %u, got %u\n", size, obj.size);
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
     if(size) {
d29b5c5f
 	if(!fmap_need_ptr_once(map, obj.content, size)) {
5b48b665
 	    cli_dbgmsg("asn1_expect_obj: failed to read content\n");
b28a0d6f
 	    return 1;
5b48b665
 	}
d29b5c5f
 	if(memcmp(obj.content, content, size)) {
5b48b665
 	    cli_dbgmsg("asn1_expect_obj: content mismatch\n");
b28a0d6f
 	    return 1;
5b48b665
 	}
b28a0d6f
     }
d29b5c5f
     *asn1data = obj.next;
b28a0d6f
     return 0;
 }
 
d1fcd16d
 static int asn1_expect_algo(fmap_t *map, const void **asn1data, unsigned int *asn1len, unsigned int algo_size, const void *algo) {
b28a0d6f
     struct cli_asn1 obj;
     unsigned int avail;
     int ret;
     if((ret = asn1_expect_objtype(map, *asn1data, asn1len, &obj, 0x30))) /* SEQUENCE */
 	return ret;
     avail = obj.size;
     *asn1data = obj.next;
 
d29b5c5f
     if((ret = asn1_expect_obj(map, &obj.content, &avail, 0x06, algo_size, algo))) /* ALGO */
b28a0d6f
 	return ret;
d29b5c5f
     if(avail && (ret = asn1_expect_obj(map, &obj.content, &avail, 0x05, 0, NULL))) /* NULL */
b28a0d6f
 	return ret;
5b48b665
     if(avail) {
 	cli_dbgmsg("asn1_expect_algo: extra data found in SEQUENCE\n");
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
     return 0;
 }
 
3c369777
 
d1fcd16d
 static int asn1_expect_rsa(fmap_t *map, const void **asn1data, unsigned int *asn1len, cli_crt_hashtype *hashtype) {
b0357255
     struct cli_asn1 obj;
     unsigned int avail;
     int ret;
     if((ret = asn1_expect_objtype(map, *asn1data, asn1len, &obj, 0x30))) /* SEQUENCE */
 	return ret;
     avail = obj.size;
     *asn1data = obj.next;
 
3a55c60d
     if(asn1_expect_objtype(map, obj.content, &avail, &obj, 0x06))
b0357255
 	return 1;
3a55c60d
     if(obj.size != lenof(OID_sha1WithRSA) && obj.size != lenof(OID_sha1WithRSAEncryption)) { /* lenof(OID_sha1WithRSAEncryption) = lenof(OID_md5WithRSAEncryption) = 9 */
3c369777
 	cli_dbgmsg("asn1_expect_rsa: expecting OID with size 5 or 9, got %02x with size %u\n", obj.type, obj.size);
b0357255
 	return 1;
     }
     if(!fmap_need_ptr_once(map, obj.content, obj.size)) {
3c369777
 	cli_dbgmsg("asn1_expect_rsa: failed to read OID\n");
b0357255
 	return 1;
     }
0f240146
     if(obj.size == lenof(OID_sha1WithRSA) && !memcmp(obj.content, OID_sha1WithRSA, lenof(OID_sha1WithRSA)))
aa09a92c
 	*hashtype = CLI_SHA1RSA; /* Obsolete sha1rsa 1.3.14.3.2.29 */
0f240146
     else if(obj.size == lenof(OID_sha1WithRSAEncryption) && !memcmp(obj.content, OID_sha1WithRSAEncryption, lenof(OID_sha1WithRSAEncryption)))
aa09a92c
 	*hashtype = CLI_SHA1RSA; /* sha1withRSAEncryption 1.2.840.113549.1.1.5 */
0f240146
     else if(obj.size == lenof(OID_md5WithRSAEncryption) && !memcmp(obj.content, OID_md5WithRSAEncryption, lenof(OID_md5WithRSAEncryption)))
aa09a92c
 	*hashtype = CLI_MD5RSA; /* md5withRSAEncryption 1.2.840.113549.1.1.4 */
e5c6c1aa
     else {
3c369777
 	cli_dbgmsg("asn1_expect_rsa: OID mismatch\n");
b0357255
 	return 1;
     }
d29b5c5f
     if((ret = asn1_expect_obj(map, &obj.next, &avail, 0x05, 0, NULL))) /* NULL */
b0357255
 	return ret;
     if(avail) {
3c369777
 	cli_dbgmsg("asn1_expect_rsa: extra data found in SEQUENCE\n");
b0357255
 	return 1;
     }
     return 0;
 }
 
5b48b665
 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]);
b28a0d6f
 	return -1;
5b48b665
     }
b28a0d6f
     return (s[0] - '0')*10 + (s[1] - '0');
 }
 
d1fcd16d
 static int asn1_get_time(fmap_t *map, const void **asn1data, unsigned int *size, time_t *tm) {
b28a0d6f
     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;
5b48b665
     else {
 	cli_dbgmsg("asn1_get_time: expected UTCTime or GeneralizedTime, got %02x\n", obj.type);
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
 
5b48b665
     if(!fmap_need_ptr_once(map, obj.content, len)) {
 	cli_dbgmsg("asn1_get_time: failed to read content\n");
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
 
     memset(&t, 0, sizeof(t));
     ptr = (char *)obj.content;
     if(obj.type == 0x18) {
5b48b665
 	t.tm_year = asn1_getnum(ptr) * 100;
b28a0d6f
 	if(t.tm_year < 0)
 	    return 1;
5b48b665
 	n = asn1_getnum(ptr);
b28a0d6f
 	if(n<0)
 	    return 1;
 	t.tm_year += n;
 	ptr+=4;
     } else {
5b48b665
 	n = asn1_getnum(ptr);
b28a0d6f
 	if(n<0)
 	    return 1;
 	if(n>=50)
 	    t.tm_year = 1900 + n;
 	else
 	    t.tm_year = 2000 + n;
 	ptr += 2;
     }
f05aa165
     t.tm_year -= 1900;
5b48b665
     n = asn1_getnum(ptr);
     if(n<1 || n>12) {
 	cli_dbgmsg("asn1_get_time: invalid month %u\n", n);
b28a0d6f
 	return 1;
5b48b665
     }
f05aa165
     t.tm_mon = n - 1;
b28a0d6f
     ptr+=2;
 
5b48b665
     n = asn1_getnum(ptr);
     if(n<1 || n>31) {
 	cli_dbgmsg("asn1_get_time: invalid day %u\n", n);
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
     t.tm_mday = n;
     ptr+=2;
 
5b48b665
     n = asn1_getnum(ptr);
     if(n<0 || n>23) {
 	cli_dbgmsg("asn1_get_time: invalid hour %u\n", n);
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
     t.tm_hour = n;
     ptr+=2;
 
5b48b665
     n = asn1_getnum(ptr);
     if(n<0 || n>59) {
 	cli_dbgmsg("asn1_get_time: invalid minute %u\n", n);
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
     t.tm_min = n;
     ptr+=2;
 
5b48b665
     n = asn1_getnum(ptr);
     if(n<0 || n>59) {
 	cli_dbgmsg("asn1_get_time: invalid second %u\n", n);
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
     t.tm_sec = n;
     ptr+=2;
 
5b48b665
     if(*ptr != 'Z') {
 	cli_dbgmsg("asn1_get_time: expected UTC time 'Z', got '%c'\n", *ptr);
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
 
f05aa165
     *tm = mktime(&t);
b28a0d6f
     *asn1data = obj.next;
     return 0;
 }
 
d1fcd16d
 static int asn1_get_rsa_pubkey(fmap_t *map, const void **asn1data, unsigned int *size, cli_crt *x509) {
b28a0d6f
     struct cli_asn1 obj;
     unsigned int avail, avail2;
 
     if(asn1_expect_objtype(map, *asn1data, size, &obj, 0x30)) /* subjectPublicKeyInfo */
 	return 1;
     *asn1data = obj.next;
 
     avail = obj.size;
0f240146
     if(asn1_expect_algo(map, &obj.content, &avail, lenof(OID_rsaEncryption), OID_rsaEncryption)) /* rsaEncryption */
b28a0d6f
        return 1;
 
5b48b665
     if(asn1_expect_objtype(map, obj.content, &avail, &obj, 0x03)) /* BIT STRING - subjectPublicKey */
b28a0d6f
 	return 1;
5b48b665
     if(avail) {
 	cli_dbgmsg("asn1_get_rsa_pubkey: found unexpected extra data in subjectPublicKeyInfo\n");
 	return 1;
     }
b28a0d6f
     /* if(obj.size != 141 && obj.size != 271) /\* encoded len of 1024 and 2048 bit public keys *\/ */
     /*	return 1; */
 
5b48b665
     if(!fmap_need_ptr_once(map, obj.content, 1)) {
 	cli_dbgmsg("asn1_get_rsa_pubkey: cannot read public key content\n");
b28a0d6f
 	return 1;
5b48b665
     }
     if(((uint8_t *)obj.content)[0] != 0) { /* no byte fragments */
 	cli_dbgmsg("asn1_get_rsa_pubkey: unexpected byte frags in public key\n");
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
 
     avail = obj.size - 1;
     obj.content = ((uint8_t *)obj.content) + 1;
5b48b665
     if(asn1_expect_objtype(map, obj.content, &avail, &obj, 0x30)) /* SEQUENCE */
 	return 1;
     if(avail) {
 	cli_dbgmsg("asn1_get_rsa_pubkey: found unexpected extra data in public key content\n");
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
 
     avail = obj.size;
     if(asn1_expect_objtype(map, obj.content, &avail, &obj, 0x02)) /* INTEGER - mod */
 	return 1;
5b48b665
     if(obj.size < 1024/8 || obj.size > 4096/8+1) {
10243534
 	cli_dbgmsg("asn1_get_rsa_pubkey: modulus has got an unsupported length (%u)\n",	 obj.size * 8);
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
     avail2 = obj.size;
5b48b665
     if(!fmap_need_ptr_once(map, obj.content, avail2)) {
 	cli_dbgmsg("asn1_get_rsa_pubkey: cannot read n\n");
b28a0d6f
 	return 1;
5b48b665
     }
c3b355d8
     if(mp_read_unsigned_bin(&x509->n, obj.content, avail2)) {
5b48b665
 	cli_dbgmsg("asn1_get_rsa_pubkey: cannot convert n to big number\n");
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
 
5b48b665
     if(asn1_expect_objtype(map, obj.next, &avail, &obj, 0x02)) /* INTEGER - exp */
 	return 1;
     if(avail) {
 	cli_dbgmsg("asn1_get_rsa_pubkey: found unexpected extra data after exp\n");
b28a0d6f
 	return 1;
5b48b665
     }
     if(obj.size < 1 || obj.size > avail2) {
 	cli_dbgmsg("asn1_get_rsa_pubkey: exponent has got an unsupported length (%u)\n",  obj.size * 8);
b28a0d6f
 	return 1;
5b48b665
     }
     if(!fmap_need_ptr_once(map, obj.content, obj.size)) {
 	cli_dbgmsg("asn1_get_rsa_pubkey: cannot read e\n");
b28a0d6f
 	return 1;
5b48b665
     }
c3b355d8
     if(mp_read_unsigned_bin(&x509->e, obj.content, obj.size)) {
5b48b665
 	cli_dbgmsg("asn1_get_rsa_pubkey: cannot convert e to big number\n");
b28a0d6f
 	return 1;
5b48b665
     }
b28a0d6f
     return 0;
 }
 
d1fcd16d
 static int asn1_get_x509(fmap_t *map, const void **asn1data, unsigned int *size, crtmgr *master, crtmgr *other) {
b28a0d6f
     struct cli_asn1 crt, tbs, obj;
7bcfb2f3
     unsigned int avail, tbssize, issuersize;
e5c6c1aa
     cli_crt_hashtype hashtype1, hashtype2;
7bcfb2f3
     cli_crt x509;
998f97f1
     const uint8_t *tbsdata;
d1fcd16d
     const void *next, *issuer;
7bcfb2f3
 
     if(cli_crt_init(&x509))
 	return 1;
 
a616029e
     do {
 	if(asn1_expect_objtype(map, *asn1data, size, &crt, 0x30)) /* SEQUENCE */
 	    break;
 	*asn1data = crt.next;
b28a0d6f
 
a616029e
 	tbsdata = crt.content;
 	if(asn1_expect_objtype(map, crt.content, &crt.size, &tbs, 0x30)) /* SEQUENCE - TBSCertificate */
 	    break;
 	tbssize = (uint8_t *)tbs.next - tbsdata;
b28a0d6f
 
a616029e
 	if(asn1_expect_objtype(map, tbs.content, &tbs.size, &obj, 0xa0)) /* [0] */
 	    break;
 	avail = obj.size;
 	next = obj.next;
 	if(asn1_expect_obj(map, &obj.content, &avail, 0x02, 1, "\x02")) /* version 3 only */
 	    break;
 	if(avail) {
 	    cli_dbgmsg("asn1_get_x509: found unexpected extra data in version\n");
 	    break;
 	}
b28a0d6f
 
a616029e
 	if(asn1_expect_objtype(map, next, &tbs.size, &obj, 0x02)) /* serialNumber */
 	    break;
a8e79d93
 	if(map_sha1(map, obj.content, obj.size, x509.serial))
 	    break;
b28a0d6f
 
a616029e
 	if(asn1_expect_rsa(map, &obj.next, &tbs.size, &hashtype1)) /* algo = sha1WithRSAEncryption | md5WithRSAEncryption */
 	    break;
b28a0d6f
 
a616029e
 	if(asn1_expect_objtype(map, obj.next, &tbs.size, &obj, 0x30)) /* issuer */
 	    break;
 	issuer = obj.content;
 	issuersize = obj.size;
b28a0d6f
 
a616029e
 	if(asn1_expect_objtype(map, obj.next, &tbs.size, &obj, 0x30)) /* validity */
 	    break;
 	avail = obj.size;
 	next = obj.content;
7bcfb2f3
 
a616029e
 	if(asn1_get_time(map, &next, &avail, &x509.not_before)) /* notBefore */
 	    break;
 	if(asn1_get_time(map, &next, &avail, &x509.not_after)) /* notAfter */
 	    break;
f05aa165
 	if(x509.not_before >= x509.not_after) {
 	    cli_dbgmsg("asn1_get_x509: bad validity\n");
 	    break;
 	}
a616029e
 	if(avail) {
 	    cli_dbgmsg("asn1_get_x509: found unexpected extra data in validity\n");
 	    break;
 	}
b28a0d6f
 
a616029e
 	if(asn1_expect_objtype(map, obj.next, &tbs.size, &obj, 0x30)) /* subject */
 	    break;
 	if(map_sha1(map, obj.content, obj.size, x509.subject))
 	    break;
 	if(asn1_get_rsa_pubkey(map, &obj.next, &tbs.size, &x509))
 	    break;
b28a0d6f
 
a616029e
 	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;
10243534
 	    if(obj.type == 0xa3) {
 		struct cli_asn1 exts;
2501f747
 		int have_ext_key = 0;
10243534
 		if(asn1_expect_objtype(map, obj.content, &obj.size, &exts, 0x30)) {
 		    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, 0x30)) {
 			exts.size = 1;
 			break;
 		    }
 		    exts.content = ext.next;
 		    if(asn1_expect_objtype(map, ext.content, &ext.size, &id, 0x06)) {
 			exts.size = 1;
 			break;
 		    }
 		    if(asn1_get_obj(map, id.next, &ext.size, &value)) {
 			exts.size = 1;
 			break;
 		    }
 		    if(value.type == 0x01) {
de8548e6
 			/* critical flag */
10243534
 			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 != 0x04) {
 			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;
 		    }
de8548e6
 		    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;
 			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);
10243534
 			continue;
de8548e6
 		    }
 		    if(!memcmp("\x55\x1d\x25", id.content, 3)) {
 			/* ExtKeyUsage 2.5.29.37 */
 			struct cli_asn1 keypurp;
2501f747
 			have_ext_key = 1;
de8548e6
 			if(asn1_expect_objtype(map, value.content, &value.size, &keypurp, 0x30)) {
 			    exts.size = 1;
 			    break;
 			}
 			if(value.size) {
 			    cli_dbgmsg("asn1_get_x509: extra data in ExtKeyUsage\n");
10243534
 			    exts.size = 1;
 			    break;
 			}
de8548e6
 			ext.next = keypurp.content;
 			while(keypurp.size) {
 			    if(asn1_expect_objtype(map, ext.next, &keypurp.size, &ext, 0x06)) {
10243534
 				exts.size = 1;
 				break;
 			    }
de8548e6
 			    if(ext.size != 8)
 				continue;
01e4650b
 			    if(!fmap_need_ptr_once(map, ext.content, 8)) {
10243534
 				exts.size = 1;
 				break;
 			    }
01e4650b
 			    if(!memcmp("\x2b\x06\x01\x05\x05\x07\x03\x03", ext.content, 8)) /* id_kp_codeSigning */
de8548e6
 				x509.codeSign = 1;
01e4650b
 			    else if(!memcmp("\x2b\x06\x01\x05\x05\x07\x03\x08", ext.content, 8)) /* id_kp_timeStamping */
de8548e6
 				x509.timeSign = 1;
 			}
 			continue;
 		    }
 		    if(!memcmp("\x55\x1d\x13", id.content, 3)) {
 			/* Basic Constraints 2.5.29.19 */
 			struct cli_asn1 constr;
 			if(asn1_expect_objtype(map, value.content, &value.size, &constr, 0x30)) {
 			    exts.size = 1;
 			    break;
 			}
2501f747
 			if(!constr.size)
de8548e6
 			    x509.certSign = 0;
 			else {
 			    if(asn1_expect_objtype(map, constr.content, &constr.size, &ext, 0x01)) {
10243534
 				exts.size = 1;
 				break;
 			    }
de8548e6
 			    if(ext.size != 1) {
 				cli_dbgmsg("asn1_get_x509: wrong bool size in basic constraint %u\n", ext.size);
10243534
 				exts.size = 1;
 				break;
 			    }
de8548e6
 			    if(!fmap_need_ptr_once(map, ext.content, 1)) {
10243534
 				exts.size = 1;
 				break;
 			    }
de8548e6
 			    x509.certSign = (((uint8_t *)(ext.content))[0] != 0);
10243534
 			}
 		    }
 		}
 		if(exts.size) {
 		    tbs.size = 1;
 		    break;
 		}
2501f747
 		if(!have_ext_key)
 		    x509.codeSign = x509.timeSign = 1;
10243534
 	    }
5b48b665
 	}
a616029e
 	if(tbs.size)
 	    break;
b28a0d6f
 
2501f747
 
10243534
 	if(crtmgr_lookup(master, &x509) || crtmgr_lookup(other, &x509)) {
 	    cli_dbgmsg("asn1_get_x509: certificate already exists\n");
 	    cli_crt_clear(&x509);
 	    return 0;
 	}
 
 	if(map_sha1(map, issuer, issuersize, x509.issuer))
 	    break;
 
a616029e
 	if(asn1_expect_rsa(map, &tbs.next, &crt.size, &hashtype2)) /* signature algo = sha1WithRSAEncryption | md5WithRSAEncryption */
 	    break;
e5c6c1aa
 
a616029e
 	if(hashtype1 != hashtype2) {
 	    cli_dbgmsg("asn1_get_x509: found conflicting rsa hash types\n");
 	    break;
 	}
 	x509.hashtype = hashtype1;
b28a0d6f
 
a616029e
 	if(asn1_expect_objtype(map, tbs.next, &crt.size, &obj, 0x03)) /* signature */
 	    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;
 	}
 	if(mp_read_unsigned_bin(&x509.sig, obj.content, obj.size)) {
 	    cli_dbgmsg("asn1_get_x509: cannot convert signature to big number\n");
 	    break;
 	}
 	if(crt.size) {
 	    cli_dbgmsg("asn1_get_x509: found unexpected extra data in signature\n");
 	    break;
 	}
71da61a7
 
a616029e
 	if((x509.hashtype == CLI_SHA1RSA && map_sha1(map, tbsdata, tbssize, x509.tbshash)) || (x509.hashtype == CLI_MD5RSA && (map_md5(map, tbsdata, tbssize, x509.tbshash))))
 	    break;
7bcfb2f3
 
a616029e
 	if(crtmgr_add(other, &x509))
 	    break;
 	cli_crt_clear(&x509);
 	return 0;
     } while(0);
7bcfb2f3
     cli_crt_clear(&x509);
a616029e
     return 1;
b28a0d6f
 }
 
7209997f
 static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmgr *cmgr, int embedded, const void **hashes, unsigned int *hashes_size, struct cl_engine *engine) {
871afd19
     struct cli_asn1 asn1, deep, deeper;
a8e79d93
     uint8_t sha1[SHA1_HASH_SIZE], issuer[SHA1_HASH_SIZE], md[SHA1_HASH_SIZE], serial[SHA1_HASH_SIZE];
998f97f1
     const uint8_t *message, *attrs;
5d49bd06
     unsigned int dsize, message_size, attrs_size;
aa09a92c
     cli_crt_hashtype hashtype;
81eb1d29
     SHA1Context ctx;
f5092717
     cli_crt *x509;
81eb1d29
     int result;
b01b78d1
     int isBlacklisted = 0;
5b48b665
 
a8a99142
     cli_dbgmsg("in asn1_parse_mscat\n");
 
5b48b665
     do {
0393aa56
 	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, 0x30)) /* SEQUENCE */
a8a99142
 	    break;
d4b8e1a6
 	/* if(size) { */
 	/*     cli_dbgmsg("asn1_parse_mscat: found extra data after pkcs#7 %u\n", size); */
 	/*     break; */
 	/* } */
a8a99142
 	size = asn1.size;
0f240146
 	if(asn1_expect_obj(map, &asn1.content, &size, 0x06, lenof(OID_signedData), OID_signedData)) /* OBJECT 1.2.840.113549.1.7.2 - contentType = signedData */
a8a99142
 	    break;
d29b5c5f
 	if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0xa0)) /* [0] - content */
a8a99142
 	    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, 0x30)) /* SEQUENCE */
 	    break;
 	if(size) {
 	    cli_dbgmsg("asn1_parse_mscat: found extra data in signedData\n");
 	    break;
 	}
 	size = asn1.size;
d29b5c5f
 	if(asn1_expect_obj(map, &asn1.content, &size, 0x02, 1, "\x01")) /* INTEGER - VERSION 1 */
a8a99142
 	    break;
5b48b665
 
d29b5c5f
 	if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x31)) /* SET OF DigestAlgorithmIdentifier */
a8a99142
 	    break;
5b48b665
 
0f240146
 	if(asn1_expect_algo(map, &asn1.content, &asn1.size, lenof(OID_sha1), OID_sha1)) /* DigestAlgorithmIdentifier[0] == sha1 */
a8a99142
 	    break;
 	if(asn1.size) {
 	    cli_dbgmsg("asn1_parse_mscat: only one digestAlgorithmIdentifier is allowed\n");
 	    break;
 	}
 
871afd19
 	if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x30)) /* SEQUENCE - contentInfo */
a8a99142
 	    break;
 	/* Here there is either a PKCS #7 ContentType Object Identifier for Certificate Trust List (szOID_CTL)
871afd19
 	 * or a single SPC_INDIRECT_DATA_OBJID */
3a55c60d
 	if(
 	   (!embedded && asn1_expect_obj(map, &asn1.content, &asn1.size, 0x06, lenof(OID_szOID_CTL), OID_szOID_CTL)) ||
 	   (embedded && asn1_expect_obj(map, &asn1.content, &asn1.size, 0x06, lenof(OID_SPC_INDIRECT_DATA_OBJID), OID_SPC_INDIRECT_DATA_OBJID))
 	   )
 	    break;
6bc5d0cb
 
3a55c60d
 	if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0xa0))
 	    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, 0x30))
 	    break;
 	if(dsize) {
 	    cli_dbgmsg("asn1_parse_mscat: found extra data in content\n");
 	    break;
871afd19
 	}
6bc5d0cb
 	*hashes = deep.content;
 	*hashes_size = deep.size;
5b48b665
 
871afd19
 	if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) /* certificates */
 	    break;
5b48b665
 
f1bf58cc
 	dsize = asn1.size;
7bcfb2f3
 	if(dsize) {
 	    crtmgr newcerts;
 	    crtmgr_init(&newcerts);
 	    while(dsize) {
 		if(asn1_get_x509(map, &asn1.content, &dsize, cmgr, &newcerts)) {
 		    dsize = 1;
 		    break;
 		}
 	    }
 	    if(dsize)
f1bf58cc
 		break;
7bcfb2f3
 	    if(newcerts.crts) {
f5092717
 		x509 = newcerts.crts;
f05aa165
 		cli_dbgmsg("asn1_parse_mscat: %u new certificates collected\n", newcerts.items);
7bcfb2f3
 		while(x509) {
50c5d4b5
 		    cli_crt *parent = crtmgr_verify_crt(cmgr, x509);
 
             /* Dump the cert if requested before anything happens to it */
7209997f
             if (engine->dconf->pe & PE_CONF_DUMPCERT) {
                 char issuer[SHA1_HASH_SIZE*2+1], subject[SHA1_HASH_SIZE*2+1], serial[SHA1_HASH_SIZE*2+1];
                 char mod[1024], exp[1024];
                 int j=1024;
 
                 fp_toradix_n(&x509->n, mod, 16, j);
                 fp_toradix_n(&x509->e, exp, 16, j);
                 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]);
                 }
 
                 cli_dbgmsg_internal("cert subject:%s serial:%s pubkey:%s i:%s %lu->%lu %s %s %s\n", subject, serial, mod, issuer, (unsigned long)x509->not_before, (unsigned long)x509->not_after, x509->certSign ? "cert" : "", x509->codeSign ? "code" : "", x509->timeSign ? "time" : "");
             }
50c5d4b5
 
b01b78d1
 		    if(parent) {
5287bbfb
                 if (parent->isBlacklisted) {
b01b78d1
                     isBlacklisted = 1;
c3ca6f36
                     cli_dbgmsg("asn1_parse_mscat: Authenticode certificate %s is revoked. Flagging sample as virus.\n", (parent->name ? parent->name : "(no name)"));
5287bbfb
                 }
b01b78d1
 
2501f747
 			x509->codeSign &= parent->codeSign;
 			x509->timeSign &= parent->timeSign;
b01b78d1
             if(crtmgr_add(cmgr, x509))
                 break;
             crtmgr_del(&newcerts, x509);
7bcfb2f3
 			x509 = newcerts.crts;
 			continue;
 		    }
 		    x509 = x509->next;
 		}
f5092717
 		if(x509)
 		    break;
7bcfb2f3
 		if(newcerts.items)
728b9a44
 		    cli_dbgmsg("asn1_parse_mscat: %u certificates did not verify\n", newcerts.items);
6bc5d0cb
 		crtmgr_free(&newcerts);
f1bf58cc
 	    }
 	}
 
 	if(asn1_get_obj(map, asn1.next, &size, &asn1))
 	    break;
 	if(asn1.type == 0xa1 && asn1_get_obj(map, asn1.next, &size, &asn1)) /* crls - unused shouldn't be present */
 	    break;
 	if(asn1.type != 0x31) { /* signerInfos */
 	    cli_dbgmsg("asn1_parse_mscat: unexpected type %02x for signerInfos\n", asn1.type);
 	    break;
 	}
 	if(size) {
 	    cli_dbgmsg("asn1_parse_mscat: unexpected extra data after signerInfos\n");
 	    break;
 	}
958fb736
 	size = asn1.size;
 	if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30))
 	    break;
 	if(size) {
 	    cli_dbgmsg("asn1_parse_mscat: only one signerInfo shall be present\n");
 	    break;
 	}
 	size = asn1.size;
d29b5c5f
 	if(asn1_expect_obj(map, &asn1.content, &size, 0x02, 1, "\x01")) /* Version = 1 */
958fb736
 	    break;
d29b5c5f
 	if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) /* issuerAndSerialNumber */
958fb736
 	    break;
 	dsize = asn1.size;
 	if(asn1_expect_objtype(map, asn1.content, &dsize, &deep, 0x30)) /* issuer */
 	    break;
29fb5f5c
 	if(map_sha1(map, deep.content, deep.size, issuer))
 	    break;
e5c6c1aa
 
958fb736
 	if(asn1_expect_objtype(map, deep.next, &dsize, &deep, 0x02)) /* serial */
 	    break;
a8e79d93
 	if(map_sha1(map, deep.content, deep.size, serial))
 	    break;
958fb736
 	if(dsize) {
 	    cli_dbgmsg("asn1_parse_mscat: extra data inside issuerAndSerialNumber\n");
 	    break;
 	}
0f240146
 	if(asn1_expect_algo(map, &asn1.next, &size, lenof(OID_sha1), OID_sha1)) /* digestAlgorithm == sha1 */
958fb736
 	    break;
 
29fb5f5c
 	attrs = asn1.next;
958fb736
 	if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) /* authenticatedAttributes */
 	    break;
29fb5f5c
 	attrs_size = (uint8_t *)(asn1.next) - attrs;
81eb1d29
 	if(attrs_size < 2) {
 	    cli_dbgmsg("asn1_parse_mscat: authenticatedAttributes size is too small\n");
 	    break;
 	}
29fb5f5c
 
 	dsize = asn1.size;
 	deep.next = asn1.content;
81eb1d29
 	result = 0;
29fb5f5c
 	while(dsize) {
 	    struct cli_asn1 cobj;
 	    int content;
 	    if(asn1_expect_objtype(map, deep.next, &dsize, &deep, 0x30)) { /* attribute */
 		dsize = 1;
 		break;
 	    }
 	    if(asn1_expect_objtype(map, deep.content, &deep.size, &deeper, 0x06)) { /* attribute type */
 		dsize = 1;
 		break;
 	    }
0f240146
 	    if(deeper.size != lenof(OID_contentType))
29fb5f5c
 		continue;
0f240146
 	    if(!fmap_need_ptr_once(map, deeper.content, lenof(OID_contentType))) {
29fb5f5c
 		cli_dbgmsg("asn1_parse_mscat: failed to read authenticated attribute\n");
 		dsize = 1;
 		break;
 	    }
0f240146
 	    if(!memcmp(deeper.content, OID_contentType, lenof(OID_contentType)))
29fb5f5c
 		content = 0; /* contentType */
0f240146
 	    else if(!memcmp(deeper.content, OID_messageDigest, lenof(OID_messageDigest)))
29fb5f5c
 		content = 1; /* messageDigest */
 	    else
 		continue;
 	    if(asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, 0x31)) { /* set - contents */
 		dsize = 1;
 		break;
 	    }
 	    if(deep.size) {
 		cli_dbgmsg("asn1_parse_mscat: extra data in authenticated attributes\n");
 		dsize = 1;
 		break;
 	    }
 
81eb1d29
 	    if(result & (1<<content)) {
29fb5f5c
 		cli_dbgmsg("asn1_parse_mscat: contentType or messageDigest appear twice\n");
 		dsize = 1;
 		break;
 	    }
 
 	    if(content == 0) { /* contentType */
5d49bd06
 		if(
3a55c60d
 		   (!embedded && asn1_expect_obj(map, &deeper.content, &deeper.size, 0x06, lenof(OID_szOID_CTL), OID_szOID_CTL)) || /* cat file */
 		   (embedded && asn1_expect_obj(map, &deeper.content, &deeper.size, 0x06, lenof(OID_SPC_INDIRECT_DATA_OBJID), OID_SPC_INDIRECT_DATA_OBJID)) /* embedded cat */
5d49bd06
 		  ) {
29fb5f5c
 		    dsize = 1;
 		    break;
 		}
81eb1d29
 		result |= 1;
29fb5f5c
 	    } else { /* messageDigest */
 		if(asn1_expect_objtype(map, deeper.content, &deeper.size, &cobj, 0x04)) {
 		    dsize = 1;
 		    break;
 		}
 		if(cobj.size != SHA1_HASH_SIZE) {
 		    cli_dbgmsg("asn1_parse_mscat: messageDigest attribute has got the wrong size (%u)\n", cobj.size);
 		    dsize = 1;
 		    break;
 		}
 		if(!fmap_need_ptr_once(map, cobj.content, SHA1_HASH_SIZE)) {
 		    cli_dbgmsg("asn1_parse_mscat: failed to read authenticated attribute\n");
 		    dsize = 1;
 		    break;
 		}
 		memcpy(md, cobj.content, SHA1_HASH_SIZE);
81eb1d29
 		result |= 2;
29fb5f5c
 	    }
 	    if(deeper.size) {
 		cli_dbgmsg("asn1_parse_mscat: extra data in authenticated attribute\n");
 		dsize = 1;
 		break;
 	    }
 	}
 	if(dsize)
 	    break;
81eb1d29
 	if(result != 3) {
29fb5f5c
 	    cli_dbgmsg("asn1_parse_mscat: contentType or messageDigest are missing\n");
 	    break;
 	}
958fb736
 
0f240146
 	if(asn1_expect_algo(map, &asn1.next, &size, lenof(OID_rsaEncryption), OID_rsaEncryption)) /* digestEncryptionAlgorithm == sha1 */
958fb736
 	    break;
 
 	if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x04)) /* encryptedDigest */
 	    break;
765788a8
 	if(asn1.size > 513) {
 	    cli_dbgmsg("asn1_parse_mscat: encryptedDigest too long\n");
 	    break;
 	}
6bc5d0cb
 	if(map_sha1(map, *hashes, *hashes_size, sha1))
29fb5f5c
 	    break;
 	if(memcmp(sha1, md, sizeof(sha1))) {
 	    cli_dbgmsg("asn1_parse_mscat: messageDigest mismatch\n");
 	    break;
 	}
 
81eb1d29
 	if(!fmap_need_ptr_once(map, attrs, attrs_size)) {
 	    cli_dbgmsg("asn1_parse_mscat: failed to read authenticatedAttributes\n");
 	    break;
29fb5f5c
 	}
 
81eb1d29
 	SHA1Init(&ctx);
 	SHA1Update(&ctx, "\x31", 1);
 	SHA1Update(&ctx, attrs + 1, attrs_size - 1);
 	SHA1Final(&ctx, sha1);
 
 	if(!fmap_need_ptr_once(map, asn1.content, asn1.size)) {
 	    cli_dbgmsg("asn1_parse_mscat: failed to read encryptedDigest\n");
 	    break;
29fb5f5c
 	}
f5092717
 	if(!(x509 = crtmgr_verify_pkcs7(cmgr, issuer, serial, asn1.content, asn1.size, CLI_SHA1RSA, sha1, VRFY_CODE))) {
81eb1d29
 	    cli_dbgmsg("asn1_parse_mscat: pkcs7 signature verification failed\n");
 	    break;
 	}
aa09a92c
 	message = asn1.content;
 	message_size = asn1.size;
81eb1d29
 
f05aa165
 	if(!size) {
 	    cli_dbgmsg("asn1_parse_mscat: countersignature is missing\n");
 	    break;
 	}
29fb5f5c
 
3c369777
 	if(size && asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa1)) /* unauthenticatedAttributes */
 	    break;
958fb736
 
 	if(size) {
 	    cli_dbgmsg("asn1_parse_mscat: extra data inside signerInfo\n");
 	    break;
 	}
 
81eb1d29
 	size = asn1.size;
 	if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30))
 	    break;
 	if(size) {
 	    cli_dbgmsg("asn1_parse_mscat: extra data inside unauthenticatedAttributes\n");
 	    break;
 	}
 
 	size = asn1.size;
 	/* 1.2.840.113549.1.9.6 - counterSignature */
0f240146
 	if(asn1_expect_obj(map, &asn1.content, &size, 0x06, lenof(OID_countersignature), OID_countersignature))
81eb1d29
 	    break;
d29b5c5f
 	if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x31))
81eb1d29
 	    break;
 	if(size) {
 	    cli_dbgmsg("asn1_parse_mscat: extra data inside counterSignature\n");
 	    break;
 	}
 
 	size = asn1.size;
 	if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30))
 	    break;
 	if(size) {
 	    cli_dbgmsg("asn1_parse_mscat: extra data inside unauthenticatedAttributes\n");
 	    break;
 	}
 
 	size = asn1.size;
d29b5c5f
 	if(asn1_expect_obj(map, &asn1.content, &size, 0x02, 1, "\x01")) /* Version = 1*/
81eb1d29
 	    break;
 
d29b5c5f
 	if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0x30)) /* issuerAndSerialNumber */
81eb1d29
 	    break;
 
 	if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0x30)) /* issuer */
 	    break;
 	if(map_sha1(map, deep.content, deep.size, issuer))
 	    break;
 
 	if(asn1_expect_objtype(map, deep.next, &asn1.size, &deep, 0x02)) /* serial */
 	    break;
a8e79d93
 	if(map_sha1(map, deep.content, deep.size, serial))
 	    break;
 
81eb1d29
 	if(asn1.size) {
 	    cli_dbgmsg("asn1_parse_mscat: extra data inside countersignature issuer\n");
 	    break;
 	}
 
aa09a92c
 	if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x30)) /* digestAlgorithm */
 	    break;
 	if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0x06))
 	    break;
0f240146
 	if(deep.size != lenof(OID_sha1) && deep.size != lenof(OID_md5)) {
aa09a92c
 	    cli_dbgmsg("asn1_parse_mscat: wrong digestAlgorithm size\n");
 	    break;
 	}
 	if(!fmap_need_ptr_once(map, deep.content, deep.size)) {
 	    cli_dbgmsg("asn1_parse_mscat: failed to read digestAlgorithm OID\n");
 	    break;
 	}
0f240146
 	if(deep.size == lenof(OID_sha1) && !memcmp(deep.content, OID_sha1, lenof(OID_sha1))) {
aa09a92c
 	    hashtype = CLI_SHA1RSA;
 	    if(map_sha1(map, message, message_size, md))
 		break;
0f240146
 	} else if(deep.size == lenof(OID_md5) && !memcmp(deep.content, OID_md5, lenof(OID_md5))) {
aa09a92c
 	    hashtype = CLI_MD5RSA;
 	    if(map_md5(map, message, message_size, md))
 		break;
 	} else {
 	    cli_dbgmsg("asn1_parse_mscat: unknown digest oid in countersignature\n");
 	    break;
 	}
d29b5c5f
 	if(asn1.size && asn1_expect_obj(map, &deep.next, &asn1.size, 0x05, 0, NULL))
aa09a92c
 	    break;
 	if(asn1.size) {
 	    cli_dbgmsg("asn1_parse_mscat: extra data in countersignature oid\n");
81eb1d29
 	    break;
 	}
 
 	attrs = asn1.next;
 	if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) /* authenticatedAttributes */
 	    break;
 	attrs_size = (uint8_t *)(asn1.next) - attrs;
 	if(attrs_size < 2) {
 	    cli_dbgmsg("asn1_parse_mscat: 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, 0x30)) { /* attribute */
 		dsize = 1;
 		break;
 	    }
 	    if(asn1_expect_objtype(map, deep.content, &deep.size, &deeper, 0x06)) { /* attribute type */
 		dsize = 1;
 		break;
 	    }
0f240146
 	    if(deeper.size != lenof(OID_contentType)) /* lenof(contentType) = lenof(messageDigest) = lenof(signingTime) = 9 */
81eb1d29
 		continue;
 
0f240146
 	    if(!fmap_need_ptr_once(map, deeper.content, lenof(OID_contentType))) {
81eb1d29
 		dsize = 1;
 		break;
 	    }
0f240146
 	    if(!memcmp(deeper.content, OID_contentType, lenof(OID_contentType)))
81eb1d29
 		content = 0; /* contentType */
0f240146
 	    else if(!memcmp(deeper.content, OID_messageDigest, lenof(OID_messageDigest)))
81eb1d29
 		content = 1; /* messageDigest */
0f240146
 	    else if(!memcmp(deeper.content, OID_signingTime, lenof(OID_signingTime)))
81eb1d29
 		content = 2; /* signingTime */
 	    else
 		continue;
 	    if(result & (1<<content)) {
 		cli_dbgmsg("asn1_parse_mscat: duplicate field in countersignature\n");
 		dsize = 1;
 		break;
 	    }
 	    result |= (1<<content);
 	    if(asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, 0x31)) { /* attribute type */
10243534
 		dsize = 1;
 		break;
81eb1d29
 	    }
 	    if(deep.size) {
 		cli_dbgmsg("asn1_parse_mscat: extra data in countersignature value\n");
 		dsize = 1;
 		break;
 	    }
 	    deep.size = deeper.size;
 	    switch(content) {
 	    case 0:  /* contentType = pkcs7-data */
0f240146
 		if(asn1_expect_obj(map, &deeper.content, &deep.size, 0x06, lenof(OID_pkcs7_data), OID_pkcs7_data))
81eb1d29
 		    deep.size = 1;
 		else if(deep.size)
 		    cli_dbgmsg("asn1_parse_mscat: extra data in countersignature content-type\n");
 		break;
aa09a92c
 	    case 1:  /* messageDigest */
d29b5c5f
 		if(asn1_expect_obj(map, &deeper.content, &deep.size, 0x04, (hashtype == CLI_SHA1RSA) ? SHA1_HASH_SIZE : 16, md)) {
81eb1d29
 		    deep.size = 1;
 		    cli_dbgmsg("asn1_parse_mscat: countersignature hash mismatch\n");
 		} else if(deep.size)
 		    cli_dbgmsg("asn1_parse_mscat: extra data in countersignature message-digest\n");
 		break;
 	    case 2:  /* signingTime */
 		{
aa09a92c
 		    time_t sigdate; /* FIXME shall i use it?! */
81eb1d29
 		    if(asn1_get_time(map, &deeper.content, &deep.size, &sigdate))
 			deep.size = 1;
 		    else if(deep.size)
 			cli_dbgmsg("asn1_parse_mscat: extra data in countersignature signing-time\n");
f5092717
 		    else if(sigdate < x509->not_before || sigdate > x509->not_after) {
 			cli_dbgmsg("asn1_parse_mscat: countersignature timestamp outside cert validity\n");
 			deep.size = 1;
 		    }
81eb1d29
 		    break;
 		}
 	    }
 	    if(deep.size) {
 		dsize = 1;
 		break;
 	    }
 	}
 	if(dsize)
 	    break;
 	if(result != 7) {
 	    cli_dbgmsg("asn1_parse_mscat: some important attributes are missing in countersignature\n");
 	    break;
 	}
 
 	if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x30)) /* digestEncryptionAlgorithm == sha1 */
 	    break;
 	if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0x06)) /* digestEncryptionAlgorithm == sha1 */
 	    break;
0f240146
 	if(deep.size != lenof(OID_rsaEncryption)) { /* lenof(OID_rsaEncryption) = lenof(OID_sha1WithRSAEncryption) = 9 */
81eb1d29
 	    cli_dbgmsg("asn1_parse_mscat: wrong digestEncryptionAlgorithm size in countersignature\n");
 	    break;
 	}
0f240146
 	if(!fmap_need_ptr_once(map, deep.content, lenof(OID_rsaEncryption))) {
81eb1d29
 	    cli_dbgmsg("asn1_parse_mscat: cannot read digestEncryptionAlgorithm in countersignature\n");
 	    break;
 	}
aa09a92c
 	/* rsaEncryption or sha1withRSAEncryption */
0f240146
 	if(memcmp(deep.content, OID_rsaEncryption, lenof(OID_rsaEncryption)) && memcmp(deep.content, OID_sha1WithRSAEncryption, lenof(OID_sha1WithRSAEncryption))) {
81eb1d29
 	    cli_dbgmsg("asn1_parse_mscat: digestEncryptionAlgorithm in countersignature is not sha1\n");
 	    break;
 	}
d29b5c5f
 	if(asn1.size && asn1_expect_obj(map, &deep.next, &asn1.size, 0x05, 0, NULL))
81eb1d29
 	    break;
 	if(asn1.size) {
 	    cli_dbgmsg("asn1_parse_mscat: extra data in digestEncryptionAlgorithm in countersignature\n");
 	    break;
 	}
 
 	if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0x04)) /* encryptedDigest */
 	    break;
765788a8
 	if(asn1.size > 513) {
 	    cli_dbgmsg("asn1_parse_mscat: countersignature encryptedDigest too long\n");
 	    break;
 	}
81eb1d29
 	if(size) {
 	    cli_dbgmsg("asn1_parse_mscat: extra data inside countersignature\n");
 	    break;
 	}
 	if(!fmap_need_ptr_once(map, attrs, attrs_size)) {
 	    cli_dbgmsg("asn1_parse_mscat: failed to read authenticatedAttributes\n");
f05aa165
 	    break;
81eb1d29
 	}
aa09a92c
 
 	if(hashtype == CLI_SHA1RSA) {
 	    SHA1Init(&ctx);
 	    SHA1Update(&ctx, "\x31", 1);
 	    SHA1Update(&ctx, attrs + 1, attrs_size - 1);
 	    SHA1Final(&ctx, sha1);
 	} else {
 	    cli_md5_ctx ctx;
 	    cli_md5_init(&ctx);
 	    cli_md5_update(&ctx, "\x31", 1);
 	    cli_md5_update(&ctx, attrs + 1, attrs_size - 1);
 	    cli_md5_final(sha1, &ctx);
 	}
6bc5d0cb
 
81eb1d29
 	if(!fmap_need_ptr_once(map, asn1.content, asn1.size)) {
 	    cli_dbgmsg("asn1_parse_mscat: failed to read countersignature encryptedDigest\n");
 	    break;
 	}
f5092717
 	if(!crtmgr_verify_pkcs7(cmgr, issuer, serial, asn1.content, asn1.size, hashtype, sha1, VRFY_TIME)) {
81eb1d29
 	    cli_dbgmsg("asn1_parse_mscat: pkcs7 countersignature verification failed\n");
 	    break;
 	}
 
7595e108
 	cli_dbgmsg("asn1_parse_mscat: catalog succesfully parsed\n");
b01b78d1
     if (isBlacklisted) {
         return 1;
     }
b0357255
 	return 0;
a8a99142
     } while(0);
b0357255
 
7595e108
     cli_dbgmsg("asn1_parse_mscat: failed to parse catalog\n");
5b48b665
     return 1;
a8a99142
 }
3a55c60d
 
0393aa56
 int asn1_load_mscat(fmap_t *map, struct cl_engine *engine) {
6bc5d0cb
     struct cli_asn1 c;
0393aa56
     unsigned int size;
f0a5895b
     struct cli_matcher *db;
     int i;
6bc5d0cb
 
7209997f
     if(asn1_parse_mscat(map, 0, map->len, &engine->cmgr, 0, &c.next, &size, engine))
b01b78d1
         return 1;
6bc5d0cb
 
f0a5895b
     if(asn1_expect_objtype(map, c.next, &size, &c, 0x30))
7595e108
 	return 1;
     if(asn1_expect_obj(map, &c.content, &c.size, 0x06, 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;
6bc5d0cb
     }
28fcc245
     if(asn1_expect_objtype(map, c.next, &size, &c, 0x4)) /* List ID */
7595e108
 	return 1;
28fcc245
     if(asn1_expect_objtype(map, c.next, &size, &c, 0x17)) /* Effective date - WTF?! */
7595e108
 	return 1;
28fcc245
     if(asn1_expect_algo(map, &c.next, &size, lenof(OID_szOID_CATALOG_LIST_MEMBER), OID_szOID_CATALOG_LIST_MEMBER)) /* szOID_CATALOG_LIST_MEMBER */
7595e108
 	return 1;
28fcc245
     if(asn1_expect_objtype(map, c.next, &size, &c, 0x30)) /* hashes here */
7595e108
 	return 1;
28fcc245
     /* [0] is next but we don't care as it's really descriptives stuff */
 
     size = c.size;
     c.next = c.content;
     while(size) {
7595e108
 	struct cli_asn1 tag;
7cb86faf
 	if(asn1_expect_objtype(map, c.next, &size, &c, 0x30))
 	    return 1;
 	if(asn1_expect_objtype(map, c.content, &c.size, &tag, 0x04)) /* TAG NAME */
 	    return 1;
 	if(asn1_expect_objtype(map, tag.next, &c.size, &tag, 0x31)) /* set */
 	    return 1;
28fcc245
 	if(c.size) {
 	    cli_dbgmsg("asn1_load_mscat: found extra data in tag\n");
7cb86faf
 	    return 1;
28fcc245
 	}
 	while(tag.size) {
 	    struct cli_asn1 tagval1, tagval2, tagval3;
 	    int hashtype;
 
7cb86faf
 	    if(asn1_expect_objtype(map, tag.content, &tag.size, &tagval1, 0x30))
 		return 1;
28fcc245
 	    tag.content = tagval1.next;
 
7cb86faf
 	    if(asn1_expect_objtype(map, tagval1.content, &tagval1.size, &tagval2, 0x06))
 		return 1;
28fcc245
 	    if(tagval2.size != lenof(OID_SPC_INDIRECT_DATA_OBJID))
 		continue;
 
 	    if(!fmap_need_ptr_once(map, tagval2.content, lenof(OID_SPC_INDIRECT_DATA_OBJID))) {
7cb86faf
 		cli_dbgmsg("asn1_load_mscat: cannot read SPC_INDIRECT_DATA\n");
 		return 1;
28fcc245
 	    }
 	    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).. */
 
7cb86faf
 	    if(asn1_expect_objtype(map, tagval2.next, &tagval1.size, &tagval2, 0x31))
 		return 1;
28fcc245
 	    if(tagval1.size) {
 		cli_dbgmsg("asn1_load_mscat: found extra data in tag value\n");
7cb86faf
 		return 1;
28fcc245
 	    }
 
7cb86faf
 	    if(asn1_expect_objtype(map, tagval2.content, &tagval2.size, &tagval1, 0x30))
 		return 1;
28fcc245
 	    if(tagval2.size) {
 		cli_dbgmsg("asn1_load_mscat: found extra data in SPC_INDIRECT_DATA_OBJID tag\n");
7cb86faf
 		return 1;
28fcc245
 	    }
 
7cb86faf
 	    if(asn1_expect_objtype(map, tagval1.content, &tagval1.size, &tagval2, 0x30))
 		return 1;
28fcc245
 
7cb86faf
 	    if(asn1_expect_objtype(map, tagval2.content, &tagval2.size, &tagval3, 0x06)) /* 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;
28fcc245
 	    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");
7cb86faf
 		return 1;
28fcc245
 	    }
765788a8
 	    if(!fmap_need_ptr_once(map, tagval3.content, lenof(OID_SPC_PE_IMAGE_DATA_OBJID))) {
28fcc245
 		cli_dbgmsg("asn1_load_mscat: cannot read hash type\n");
7cb86faf
 		return 1;
28fcc245
 	    }
f0a5895b
 	    if(!memcmp(tagval3.content, OID_SPC_PE_IMAGE_DATA_OBJID, lenof(OID_SPC_PE_IMAGE_DATA_OBJID)))
 		hashtype = 2;
 	    else if(!memcmp(tagval3.content, OID_SPC_CAB_DATA_OBJID, lenof(OID_SPC_CAB_DATA_OBJID)))
28fcc245
 		hashtype = 1;
f0a5895b
 	    else {
28fcc245
 		cli_dbgmsg("asn1_load_mscat: unexpected hash type\n");
7cb86faf
 		return 1;
28fcc245
 	    }
 
7cb86faf
 	    if(asn1_expect_objtype(map, tagval2.next, &tagval1.size, &tagval2, 0x30))
 		return 1;
28fcc245
 	    if(tagval1.size) {
 		cli_dbgmsg("asn1_load_mscat: found extra data after hash\n");
7cb86faf
 		return 1;
28fcc245
 	    }
 
 	    if(asn1_expect_algo(map, &tagval2.content, &tagval2.size, lenof(OID_sha1), OID_sha1)) /* objid 1.3.14.3.2.26 - sha1 */
7cb86faf
 		return 1;
28fcc245
 
7cb86faf
 	    if(asn1_expect_objtype(map, tagval2.content, &tagval2.size, &tagval3, 0x04))
 		return 1;
28fcc245
 	    if(tagval2.size) {
 		cli_dbgmsg("asn1_load_mscat: found extra data in hash\n");
7cb86faf
 		return 1;
28fcc245
 	    }
 	    if(tagval3.size != SHA1_HASH_SIZE) {
 		cli_dbgmsg("asn1_load_mscat: bad hash size %u\n", tagval3.size);
7cb86faf
 		return 1;
28fcc245
 	    }
 	    if(!fmap_need_ptr_once(map, tagval3.content, SHA1_HASH_SIZE)) {
 		cli_dbgmsg("asn1_load_mscat: cannot read hash\n");
7cb86faf
 		return 1;
28fcc245
 	    }
f0a5895b
 
7cb86faf
 	    if(cli_debug_flag) {
 		char sha1[SHA1_HASH_SIZE*2+1];
765788a8
 		for(i=0;i<SHA1_HASH_SIZE;i++)
7cb86faf
 		    sprintf(&sha1[i*2], "%02x", ((uint8_t *)(tagval3.content))[i]);
 		cli_dbgmsg("asn1_load_mscat: got hash %s (%s)\n", sha1, (hashtype == 2) ? "PE" : "CAB");
f0a5895b
 	    }
 	    if(!engine->hm_fp) {
 		if(!(engine->hm_fp = mpool_calloc(engine->mempool, 1, sizeof(*db)))) {
 		    tag.size = 1;;
7cb86faf
 		    return 1;
f0a5895b
 		}
 #ifdef USE_MPOOL
 		engine->hm_fp->mempool = engine->mempool;
 #endif
 	    }
f5092717
 	    if(hm_addhash_bin(engine->hm_fp, tagval3.content, CLI_HASH_SHA1, hashtype, NULL)) {
728b9a44
 		cli_warnmsg("asn1_load_mscat: failed to add hash\n");
7cb86faf
 		return 1;
28fcc245
 	    }
 	}
7595e108
     }
b01b78d1
 
7595e108
     return 0;
 }
 
56b4f4b0
 int asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, uint8_t *computed_sha1) {
7595e108
     unsigned int content_size;
     struct cli_asn1 c;
d1fcd16d
     const void *content;
7595e108
     crtmgr certs;
     int ret;
 
5cc4cb86
     if (engine->dconf->pe & PE_CONF_DISABLECERT)
         return CL_VIRUS;
 
728b9a44
     cli_dbgmsg("in asn1_check_mscat (offset: %lu)\n", offset);
7595e108
     crtmgr_init(&certs);
56b4f4b0
     if(crtmgr_add_roots(engine, &certs)) {
7595e108
 	crtmgr_free(&certs);
7dfd90ec
 	return CL_VIRUS;
7595e108
     }
7209997f
     ret = asn1_parse_mscat(map, offset, size, &certs, 1, &content, &content_size, engine);
7595e108
     crtmgr_free(&certs);
     if(ret)
7dfd90ec
 	return CL_VIRUS;
7595e108
 
     if(asn1_expect_objtype(map, content, &content_size, &c, 0x30))
 	return CL_VIRUS;
     if(asn1_expect_obj(map, &c.content, &c.size, 0x06, lenof(OID_SPC_PE_IMAGE_DATA_OBJID), OID_SPC_PE_IMAGE_DATA_OBJID))
 	return CL_VIRUS;
     if(asn1_expect_objtype(map, c.next, &content_size, &c, 0x30))
 	return CL_VIRUS;
     if(content_size) {
 	cli_dbgmsg("asn1_check_mscat: extra data in content\n");
 	return CL_VIRUS;
     }
     if(asn1_expect_algo(map, &c.content, &c.size, lenof(OID_sha1), OID_sha1))
 	return CL_VIRUS;
 
     if(asn1_expect_obj(map, &c.content, &c.size, 0x04, SHA1_HASH_SIZE, computed_sha1))
 	return CL_VIRUS;
 
     cli_dbgmsg("asn1_check_mscat: file with valid authenicode signature, whitelisted\n");
     return CL_CLEAN;
 }