libclamav/asn1.c
5d49bd06
 /*
c442ca9c
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2011-2013 Sourcefire, Inc.
5d49bd06
  *
  *  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>
 
60d8d2c3
 #include "clamav.h"
b28a0d6f
 #include "asn1.h"
 #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
 
38beaece
 #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
 
a616029e
 #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
 
38beaece
 #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
 
4cd3d284
 #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
 
38beaece
 #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
 
a616029e
 #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
 
d28779ec
 #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
 
a616029e
 
 #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
 
7438ffdd
 #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
 
a616029e
 #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
 
5ee2fc53
 #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
 
0973eb97
 #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
 
a616029e
 /* --------------------------------------------------------------------------- OIDS */
ee769cc7
 #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
a616029e
 
c7145595
 #define MAX_HASH_SIZE SHA512_HASH_SIZE
 
774120ff
 struct cli_asn1 {
     uint8_t type;
     unsigned int size;
d1fcd16d
     const void *content;
     const void *next;
774120ff
 };
a616029e
 
616c0259
 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)) {
937c42e1
         cli_dbgmsg("map_raw: failed to read map data\n");
         return 1;
616c0259
     }
     memset(raw, 0, CRT_RAWMAXLEN);
     raw[0] = (uint8_t)elen;
     memcpy(&raw[1], data, elen);
     return 0;
 }
 
a9a7122b
 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);
 }
 
1b5c9f72
 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);
 }
 
998f97f1
 static int map_sha1(fmap_t *map, const void *data, unsigned int len, uint8_t sha1[SHA1_HASH_SIZE]) {
e5c6c1aa
     if(!fmap_need_ptr_once(map, data, len)) {
937c42e1
         cli_dbgmsg("map_sha1: failed to read hash data\n");
         return 1;
e5c6c1aa
     }
b2e7c931
     return (cl_sha1(data, len, sha1, NULL) == NULL);
e5c6c1aa
 }
b28a0d6f
 
d1fcd16d
 static int map_md5(fmap_t *map, const void *data, unsigned int len, uint8_t *md5) {
71da61a7
     if(!fmap_need_ptr_once(map, data, len)) {
937c42e1
         cli_dbgmsg("map_md5: failed to read hash data\n");
         return 1;
71da61a7
     }
b2e7c931
     return (cl_hash_data("md5", data, len, md5, NULL) == NULL);
71da61a7
 }
 
c7145595
 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;
         }
a9a7122b
     } 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;
         }
c7145595
     } else {
         cli_dbgmsg("asn1_map_hash: unsupported hashtype\n");
         return 1;
     }
     return 0;
 }
 
a9a7122b
 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;
 }
 
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) {
937c42e1
         cli_dbgmsg("asn1_get_obj: insufficient data length\n");
         return 1;
b28a0d6f
     }
     data = fmap_need_ptr_once(map, asn1data, readbytes);
     if(!data) {
937c42e1
         cli_dbgmsg("asn1_get_obj: obj out of file\n");
         return 1;
b28a0d6f
     }
 
     obj->type = data[0];
     i = data[1];
     data+=2;
     if(i & 0x80) {
937c42e1
         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 ++;
         }
b28a0d6f
     } else
937c42e1
         obj->size = i;
b28a0d6f
 
     asn1_sz -= data - (uint8_t *)asn1data;
     if(obj->size > asn1_sz) {
937c42e1
         cli_dbgmsg("asn1_get_obj: content overflow\n");
         return 1;
b28a0d6f
     }
 
     obj->content = data;
     if(obj->size == asn1_sz)
937c42e1
         obj->next = NULL;
b28a0d6f
     else
937c42e1
         obj->next = data + obj->size;
b28a0d6f
     *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)
937c42e1
         return ret;
5b48b665
     if(obj->type != type) {
937c42e1
         cli_dbgmsg("asn1_expect_objtype: expected type %02x, got %02x\n", type, obj->type);
         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)
937c42e1
         return ret;
d29b5c5f
     if(obj.size != size) {
937c42e1
         cli_dbgmsg("asn1_expect_obj: expected size %u, got %u\n", size, obj.size);
         return 1;
5b48b665
     }
b28a0d6f
     if(size) {
937c42e1
         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;
         }
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;
ee769cc7
     if((ret = asn1_expect_objtype(map, *asn1data, asn1len, &obj, ASN1_TYPE_SEQUENCE))) /* SEQUENCE */
937c42e1
         return ret;
b28a0d6f
     avail = obj.size;
     *asn1data = obj.next;
 
ee769cc7
     if((ret = asn1_expect_obj(map, &obj.content, &avail, ASN1_TYPE_OBJECT_ID, algo_size, algo))) /* ALGO */
937c42e1
         return ret;
7f465035
 
     // 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
15c54ab6
     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");
937c42e1
         return ret;
15c54ab6
     }
5b48b665
     if(avail) {
937c42e1
         cli_dbgmsg("asn1_expect_algo: extra data found in SEQUENCE\n");
         return 1;
5b48b665
     }
b28a0d6f
     return 0;
 }
 
c7145595
 static int asn1_expect_hash_algo(fmap_t *map, const void **asn1data, unsigned int *asn1len, cli_crt_hashtype *hashtype, unsigned int *hashsize) {
     struct cli_asn1 obj;
     unsigned int avail;
     int ret;
 
     if(ret = asn1_expect_objtype(map, *asn1data, asn1len, &obj, ASN1_TYPE_SEQUENCE)) {
         cli_dbgmsg("asn1_expect_hash_algo: expected SEQUENCE to start AlgorithmIdentifier\n");
         return ret;
     }
     avail = obj.size;
     *asn1data = obj.next;
     if(ret = asn1_expect_objtype(map, obj.content, &avail, &obj, ASN1_TYPE_OBJECT_ID)) {
         cli_dbgmsg("asn1_expect_hash_algo: unexpected object type inside AlgorithmIdentifier SET\n");
         return ret;
     }
15c54ab6
     /* Cases to consider for the length check:
      *  - obj.size == 5:
      *     - OID_sha1
      *  - obj.size == 8:
      *     - OID_md5
      *  - obj.size == 9:
      *     - OID_sha256
      *     - OID_sha1WithRSAEncryption
      *     - OID_md5WithRSAEncryption
      *     - OID_sha256WithRSAEncryption
0973eb97
      *     - OID_sha384
      *     - OID_sha384WithRSAEncryption
      *     - OID_sha512
      *     - OID_sha512WithRSAEncryption
15c54ab6
      */
c7145595
     if(obj.size != lenof(OID_sha1) && obj.size != lenof(OID_md5) && obj.size != lenof(OID_sha256)) {
         cli_dbgmsg("asn1_expect_hash_algo: unsupported algorithm OID size for AlgorithmIdentifier\n");
         return 1;
     }
     if(!fmap_need_ptr_once(map, obj.content, obj.size)) {
         cli_dbgmsg("asn1_expect_hash_algo: failed to get AlgorithmIdentifier OID\n");
         return 1;
     }
15c54ab6
     if((obj.size == lenof(OID_sha1) && !memcmp(obj.content, OID_sha1, lenof(OID_sha1))) ||
        (obj.size == lenof(OID_sha1WithRSAEncryption) && !memcmp(obj.content, OID_sha1WithRSAEncryption, lenof(OID_sha1WithRSAEncryption)))) {
c7145595
         *hashtype = CLI_SHA1RSA;
         *hashsize = SHA1_HASH_SIZE;
15c54ab6
     } else if((obj.size == lenof(OID_md5) && !memcmp(obj.content, OID_md5, lenof(OID_md5))) ||
               (obj.size == lenof(OID_md5WithRSAEncryption) && !memcmp(obj.content, OID_md5WithRSAEncryption, lenof(OID_md5WithRSAEncryption)))) {
c7145595
         *hashtype = CLI_MD5RSA;
         *hashsize = MD5_HASH_SIZE;
15c54ab6
     } else if((obj.size == lenof(OID_sha256) && !memcmp(obj.content, OID_sha256, lenof(OID_sha256))) ||
               (obj.size == lenof(OID_sha256WithRSAEncryption) && !memcmp(obj.content, OID_sha256WithRSAEncryption, lenof(OID_sha256WithRSAEncryption)))) {
c7145595
         *hashtype = CLI_SHA256RSA;
         *hashsize = SHA256_HASH_SIZE;
0973eb97
     } else if((obj.size == lenof(OID_sha384) && !memcmp(obj.content, OID_sha384, lenof(OID_sha384))) ||
               (obj.size == lenof(OID_sha384WithRSAEncryption) && !memcmp(obj.content, OID_sha384WithRSAEncryption, lenof(OID_sha384WithRSAEncryption)))) {
         *hashtype = CLI_SHA384RSA;
         *hashsize = SHA384_HASH_SIZE;
     } else if((obj.size == lenof(OID_sha512) && !memcmp(obj.content, OID_sha512, lenof(OID_sha512))) ||
               (obj.size == lenof(OID_sha512WithRSAEncryption) && !memcmp(obj.content, OID_sha512WithRSAEncryption, lenof(OID_sha512WithRSAEncryption)))) {
         *hashtype = CLI_SHA512RSA;
         *hashsize = SHA512_HASH_SIZE;
c7145595
     } else {
         cli_dbgmsg("asn1_expect_hash_algo: unknown digest OID in AlgorithmIdentifier\n");
         return 1;
     }
7f465035
     // 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.next, &avail, ASN1_TYPE_NULL, 0, NULL))) {
15c54ab6
         cli_dbgmsg("asn1_expect_hash_algo: expected NULL after AlgorithmIdentifier OID\n");
c7145595
         return ret;
     }
     if(avail) {
         cli_dbgmsg("asn1_expect_hash_algo: extra data in AlgorithmIdentifier\n");
         return 1;
     }
     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;
144148f1
     if((ret = asn1_expect_objtype(map, *asn1data, asn1len, &obj, ASN1_TYPE_SEQUENCE))) { /* SEQUENCE */
         cli_dbgmsg("asn1_expect_rsa: expecting SEQUENCE at the start of the RSA algo\n");
937c42e1
         return ret;
144148f1
     }
b0357255
     avail = obj.size;
     *asn1data = obj.next;
 
144148f1
     if(asn1_expect_objtype(map, obj.content, &avail, &obj, ASN1_TYPE_OBJECT_ID)) {
         cli_dbgmsg("asn1_expect_rsa: expected OID in RSA algo\n");
937c42e1
         return 1;
144148f1
     }
4cd3d284
 
     // Two cases to check for:
     // obj.size == 5:
     //  - OID_sha1WithRSA
     //
     // obj.size == 9:
144148f1
     //  - OID_rsaEncryption
4cd3d284
     //  - OID_md2WithRSAEncryption
     //  - OID_md5WithRSAEncryption
     //  - OID_sha1WithRSAEncryption
     //  - OID_sha256WithRSAEncryption
     //  - OID_sha384WithRSAEncryption
     //  - OID_sha512WithRSAEncryption
     if(obj.size != lenof(OID_sha1WithRSA) && obj.size != lenof(OID_sha1WithRSAEncryption)) {
937c42e1
         cli_dbgmsg("asn1_expect_rsa: expecting OID with size 5 or 9, got %02x with size %u\n", obj.type, obj.size);
         return 1;
b0357255
     }
     if(!fmap_need_ptr_once(map, obj.content, obj.size)) {
937c42e1
         cli_dbgmsg("asn1_expect_rsa: failed to read OID\n");
         return 1;
b0357255
     }
4cd3d284
     if(obj.size == lenof(OID_sha1WithRSA)) {
 
144148f1
         if(!memcmp(obj.content, OID_sha1WithRSA, lenof(OID_sha1WithRSA))) {
4cd3d284
             *hashtype = CLI_SHA1RSA; /* Obsolete sha1rsa 1.3.14.3.2.29 */
144148f1
         }
         else {
             cli_dbgmsg("asn1_expect_rsa: unknown OID (length 5)\n");
             return 1;
         }
4cd3d284
 
     } else if (obj.size == lenof(OID_sha1WithRSAEncryption)) {
 
         if(!memcmp(obj.content, OID_sha1WithRSAEncryption, lenof(OID_sha1WithRSAEncryption)))
             *hashtype = CLI_SHA1RSA; /* sha1withRSAEncryption 1.2.840.113549.1.1.5 */
 
         else if(!memcmp(obj.content, OID_md5WithRSAEncryption, lenof(OID_md5WithRSAEncryption)))
             *hashtype = CLI_MD5RSA; /* md5withRSAEncryption 1.2.840.113549.1.1.4 */
 
144148f1
         else if(!memcmp(obj.content, OID_rsaEncryption, lenof(OID_rsaEncryption)))
             *hashtype = CLI_RSA; /* rsaEncryption 1.2.840.113549.1.1.1 */
 
4cd3d284
         else if(!memcmp(obj.content, OID_md2WithRSAEncryption, lenof(OID_md2WithRSAEncryption))) {
144148f1
             *hashtype = CLI_MD2RSA; /* md2withRSAEncryption 1.2.840.113549.1.1.2 */
4cd3d284
         }
         else if(!memcmp(obj.content, OID_sha256WithRSAEncryption, lenof(OID_sha256WithRSAEncryption))) {
             *hashtype = CLI_SHA256RSA; /* sha256WithRSAEncryption 1.2.840.113549.1.1.11 */
         }
         else if(!memcmp(obj.content, OID_sha384WithRSAEncryption, lenof(OID_sha384WithRSAEncryption))) {
             *hashtype = CLI_SHA384RSA; /* sha384WithRSAEncryption 1.2.840.113549.1.1.12 */
         }
         else if(!memcmp(obj.content, OID_sha512WithRSAEncryption, lenof(OID_sha512WithRSAEncryption))) {
144148f1
             *hashtype = CLI_SHA512RSA; /* sha512WithRSAEncryption 1.2.840.113549.1.1.13 */
         }
         else {
             cli_dbgmsg("asn1_expect_rsa: unknown OID (length 9)\n");
4cd3d284
             return 1;
         }
38beaece
     }
e5c6c1aa
     else {
937c42e1
         cli_dbgmsg("asn1_expect_rsa: OID mismatch (size %u)\n", obj.size);
         return 1;
b0357255
     }
7f465035
     // 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.next, &avail, ASN1_TYPE_NULL, 0, NULL))) { /* NULL */
144148f1
         cli_dbgmsg("asn1_expect_rsa: expected NULL following RSA OID\n");
937c42e1
         return ret;
144148f1
     }
b0357255
     if(avail) {
937c42e1
         cli_dbgmsg("asn1_expect_rsa: extra data found in SEQUENCE\n");
         return 1;
b0357255
     }
     return 0;
 }
 
5b48b665
 static int asn1_getnum(const char *s) {
     if(s[0] < '0' || s[0] >'9' || s[1] < '0' || s[1] > '9') {
937c42e1
         cli_dbgmsg("asn1_getnum: expecting digits, found '%c%c'\n", s[0], s[1]);
         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)
937c42e1
         return ret;
b28a0d6f
 
     if(obj.type == 0x17) /* UTCTime - YYMMDDHHMMSSZ */
937c42e1
         len = 13;
b28a0d6f
     else if(obj.type == 0x18) /* GeneralizedTime - YYYYMMDDHHMMSSZ */
937c42e1
         len = 15;
5b48b665
     else {
937c42e1
         cli_dbgmsg("asn1_get_time: expected UTCTime or GeneralizedTime, got %02x\n", obj.type);
         return 1;
5b48b665
     }
b28a0d6f
 
5b48b665
     if(!fmap_need_ptr_once(map, obj.content, len)) {
937c42e1
         cli_dbgmsg("asn1_get_time: failed to read content\n");
         return 1;
5b48b665
     }
b28a0d6f
 
     memset(&t, 0, sizeof(t));
     ptr = (char *)obj.content;
     if(obj.type == 0x18) {
937c42e1
         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;
b28a0d6f
     } else {
937c42e1
         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;
b28a0d6f
     }
f05aa165
     t.tm_year -= 1900;
5b48b665
     n = asn1_getnum(ptr);
     if(n<1 || n>12) {
937c42e1
         cli_dbgmsg("asn1_get_time: invalid month %u\n", n);
         return 1;
5b48b665
     }
f05aa165
     t.tm_mon = n - 1;
b28a0d6f
     ptr+=2;
 
5b48b665
     n = asn1_getnum(ptr);
     if(n<1 || n>31) {
937c42e1
         cli_dbgmsg("asn1_get_time: invalid day %u\n", n);
         return 1;
5b48b665
     }
b28a0d6f
     t.tm_mday = n;
     ptr+=2;
 
5b48b665
     n = asn1_getnum(ptr);
     if(n<0 || n>23) {
937c42e1
         cli_dbgmsg("asn1_get_time: invalid hour %u\n", n);
         return 1;
5b48b665
     }
b28a0d6f
     t.tm_hour = n;
     ptr+=2;
 
5b48b665
     n = asn1_getnum(ptr);
     if(n<0 || n>59) {
937c42e1
         cli_dbgmsg("asn1_get_time: invalid minute %u\n", n);
         return 1;
5b48b665
     }
b28a0d6f
     t.tm_min = n;
     ptr+=2;
 
5b48b665
     n = asn1_getnum(ptr);
     if(n<0 || n>59) {
937c42e1
         cli_dbgmsg("asn1_get_time: invalid second %u\n", n);
         return 1;
5b48b665
     }
b28a0d6f
     t.tm_sec = n;
     ptr+=2;
 
5b48b665
     if(*ptr != 'Z') {
937c42e1
         cli_dbgmsg("asn1_get_time: expected UTC time 'Z', got '%c'\n", *ptr);
         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;
 
ee769cc7
     if(asn1_expect_objtype(map, *asn1data, size, &obj, ASN1_TYPE_SEQUENCE)) /* subjectPublicKeyInfo */
937c42e1
         return 1;
b28a0d6f
     *asn1data = obj.next;
 
     avail = obj.size;
7f465035
     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");
b28a0d6f
        return 1;
7f465035
     }
b28a0d6f
 
ee769cc7
     if(asn1_expect_objtype(map, obj.content, &avail, &obj, ASN1_TYPE_BIT_STRING)) /* BIT STRING - subjectPublicKey */
937c42e1
         return 1;
5b48b665
     if(avail) {
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: found unexpected extra data in subjectPublicKeyInfo\n");
         return 1;
5b48b665
     }
b28a0d6f
     /* if(obj.size != 141 && obj.size != 271) /\* encoded len of 1024 and 2048 bit public keys *\/ */
937c42e1
     /*  return 1; */
b28a0d6f
 
5b48b665
     if(!fmap_need_ptr_once(map, obj.content, 1)) {
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: cannot read public key content\n");
         return 1;
5b48b665
     }
     if(((uint8_t *)obj.content)[0] != 0) { /* no byte fragments */
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: unexpected byte frags in public key\n");
         return 1;
5b48b665
     }
b28a0d6f
 
     avail = obj.size - 1;
     obj.content = ((uint8_t *)obj.content) + 1;
ee769cc7
     if(asn1_expect_objtype(map, obj.content, &avail, &obj, ASN1_TYPE_SEQUENCE)) /* SEQUENCE */
937c42e1
         return 1;
5b48b665
     if(avail) {
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: found unexpected extra data in public key content\n");
         return 1;
5b48b665
     }
b28a0d6f
 
     avail = obj.size;
ee769cc7
     if(asn1_expect_objtype(map, obj.content, &avail, &obj, ASN1_TYPE_INTEGER)) /* INTEGER - mod */
937c42e1
         return 1;
5b48b665
     if(obj.size < 1024/8 || obj.size > 4096/8+1) {
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: modulus has got an unsupported length (%u)\n",  obj.size * 8);
         return 1;
5b48b665
     }
b28a0d6f
     avail2 = obj.size;
5b48b665
     if(!fmap_need_ptr_once(map, obj.content, avail2)) {
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: cannot read n\n");
         return 1;
5b48b665
     }
c3b355d8
     if(mp_read_unsigned_bin(&x509->n, obj.content, avail2)) {
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: cannot convert n to big number\n");
         return 1;
5b48b665
     }
b28a0d6f
 
ee769cc7
     if(asn1_expect_objtype(map, obj.next, &avail, &obj, ASN1_TYPE_INTEGER)) /* INTEGER - exp */
937c42e1
         return 1;
5b48b665
     if(avail) {
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: found unexpected extra data after exp\n");
         return 1;
5b48b665
     }
     if(obj.size < 1 || obj.size > avail2) {
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: exponent has got an unsupported length (%u)\n",  obj.size * 8);
         return 1;
5b48b665
     }
     if(!fmap_need_ptr_once(map, obj.content, obj.size)) {
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: cannot read e\n");
         return 1;
5b48b665
     }
c3b355d8
     if(mp_read_unsigned_bin(&x509->e, obj.content, obj.size)) {
937c42e1
         cli_dbgmsg("asn1_get_rsa_pubkey: cannot convert e to big number\n");
         return 1;
5b48b665
     }
b28a0d6f
     return 0;
 }
 
b851a649
 
 #define ASN1_GET_X509_SUCCESS 0
 #define ASN1_GET_X509_CERT_ERROR 1
 #define ASN1_GET_X509_UNRECOVERABLE_ERROR 2
 
a2bb4cdf
 /* Parse the asn1data associated with an x509 certificate and add the cert
50d1a0b6
  * to the crtmgr certs if it doesn't already exist there.
a2bb4cdf
  * 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. */
50d1a0b6
 static int asn1_get_x509(fmap_t *map, const void **asn1data, unsigned int *size, crtmgr *crts) {
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;
b851a649
     int ret = ASN1_GET_X509_UNRECOVERABLE_ERROR;
b1c13539
     unsigned int version;
7bcfb2f3
 
     if(cli_crt_init(&x509))
b851a649
         return ret;
7bcfb2f3
 
a616029e
     do {
4cd3d284
         if(asn1_expect_objtype(map, *asn1data, size, &crt, ASN1_TYPE_SEQUENCE)) { /* SEQUENCE */
             cli_dbgmsg("asn1_get_x509: expected SEQUENCE at the x509 start\n");
937c42e1
             break;
4cd3d284
         }
937c42e1
         *asn1data = crt.next;
 
b851a649
         /* 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;
 
937c42e1
         tbsdata = crt.content;
4cd3d284
         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");
937c42e1
             break;
4cd3d284
         }
937c42e1
         tbssize = (uint8_t *)tbs.next - tbsdata;
 
b1c13539
         /* 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");
937c42e1
             break;
         }
b1c13539
         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;
937c42e1
 
b1c13539
             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");
937c42e1
             break;
4cd3d284
         }
b1c13539
 
937c42e1
         if(map_raw(map, obj.content, obj.size, x509.raw_serial))
             break;
         if(map_sha1(map, obj.content, obj.size, x509.serial))
             break;
 
4cd3d284
         if(asn1_expect_rsa(map, &obj.next, &tbs.size, &hashtype1)) { /* algo - Ex: sha1WithRSAEncryption */
             cli_dbgmsg("asn1_get_x509: unable to parse AlgorithmIdentifier\n");
937c42e1
             break;
4cd3d284
         }
937c42e1
 
a2bb4cdf
         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");
937c42e1
             break;
a2bb4cdf
         }
937c42e1
         issuer = obj.content;
         issuersize = obj.size;
 
a2bb4cdf
         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");
937c42e1
             break;
a2bb4cdf
         }
937c42e1
         avail = obj.size;
         next = obj.content;
 
a2bb4cdf
         if(asn1_get_time(map, &next, &avail, &x509.not_before)) { /* notBefore */
             cli_dbgmsg("asn1_get_x509: unable to extract the notBefore time\n");
937c42e1
             break;
a2bb4cdf
         }
         if(asn1_get_time(map, &next, &avail, &x509.not_after)) { /* notAfter */
             cli_dbgmsg("asn1_get_x509: unable to extract the notAfter time\n");
937c42e1
             break;
a2bb4cdf
         }
937c42e1
         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;
         }
 
a2bb4cdf
         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");
937c42e1
             break;
a2bb4cdf
         }
937c42e1
         if(map_raw(map, obj.content, obj.size, x509.raw_subject))
             break;
         if(map_sha1(map, obj.content, obj.size, x509.subject))
             break;
7f465035
         if(asn1_get_rsa_pubkey(map, &obj.next, &tbs.size, &x509)) { /* subjectPublicKeyInfo */
             cli_dbgmsg("asn1_get_x509: failed to get RSA public key\n");
b1c13539
             break;
7f465035
         }
b1c13539
 
         if (1 == version && tbs.size) {
             cli_dbgmsg("asn1_get_x509: TBSCertificate should not contain fields beyond subjectPublicKeyInfo if version == 1\n");
937c42e1
             break;
b1c13539
         }
937c42e1
 
         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;
5df252e7
                 int have_key_usage = 0;
937c42e1
                 int have_ext_key = 0;
ee769cc7
                 if(asn1_expect_objtype(map, obj.content, &obj.size, &exts, ASN1_TYPE_SEQUENCE)) {
937c42e1
                     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;
ee769cc7
                     if(asn1_expect_objtype(map, exts.content, &exts.size, &ext, ASN1_TYPE_SEQUENCE)) {
937c42e1
                         exts.size = 1;
                         break;
                     }
                     exts.content = ext.next;
ee769cc7
                     if(asn1_expect_objtype(map, ext.content, &ext.size, &id, ASN1_TYPE_OBJECT_ID)) {
937c42e1
                         exts.size = 1;
                         break;
                     }
                     if(asn1_get_obj(map, id.next, &ext.size, &value)) {
                         exts.size = 1;
                         break;
                     }
ee769cc7
                     if(value.type == ASN1_TYPE_BOOLEAN) {
937c42e1
                         /* 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;
                         }
                     }
ee769cc7
                     if(value.type != ASN1_TYPE_OCTET_STRING) {
937c42e1
                         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;
5df252e7
                         have_key_usage = 1;
937c42e1
                         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;
ee769cc7
                         if(asn1_expect_objtype(map, value.content, &value.size, &keypurp, ASN1_TYPE_SEQUENCE)) {
937c42e1
                             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) {
ee769cc7
                             if(asn1_expect_objtype(map, ext.next, &keypurp.size, &ext, ASN1_TYPE_OBJECT_ID)) {
937c42e1
                                 exts.size = 1;
                                 break;
                             }
b1c13539
                             if(ext.size != 8 && ext.size != 10)
937c42e1
                                 continue;
b1c13539
                             if(!fmap_need_ptr_once(map, ext.content, ext.size)) {
937c42e1
                                 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;
b1c13539
                             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");
937c42e1
                         }
                         continue;
                     }
                     if(!memcmp("\x55\x1d\x13", id.content, 3)) {
                         /* Basic Constraints 2.5.29.19 */
                         struct cli_asn1 constr;
ee769cc7
                         if(asn1_expect_objtype(map, value.content, &value.size, &constr, ASN1_TYPE_SEQUENCE)) {
937c42e1
                             exts.size = 1;
                             break;
                         }
                         if(!constr.size)
                             x509.certSign = 0;
                         else {
ee769cc7
                             if(asn1_expect_objtype(map, constr.content, &constr.size, &ext, ASN1_TYPE_BOOLEAN)) {
937c42e1
                                 exts.size = 1;
                                 break;
                             }
                             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);
                         }
                     }
                 }
                 if(exts.size) {
                     tbs.size = 1;
                     break;
                 }
5df252e7
 
                 /* 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? */
937c42e1
                 if(!have_ext_key)
                     x509.codeSign = x509.timeSign = 1;
5df252e7
 
                 /* 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;
937c42e1
             }
         }
a2bb4cdf
         if(tbs.size) {
             cli_dbgmsg("asn1_get_x509: An error occurred when parsing x509 extensions\n");
937c42e1
             break;
a2bb4cdf
         }
937c42e1
 
db39ba2a
         if (!x509.certSign && !x509.codeSign && !x509.timeSign) {
             cli_dbgmsg("asn1_get_x509: encountered a certificate with no cert, code, or time signing capabilities\n");
         }
 
937c42e1
 
50d1a0b6
         if(crtmgr_lookup(crts, &x509)) {
             cli_dbgmsg("asn1_get_x509: duplicate embedded certificates detected\n");
937c42e1
             cli_crt_clear(&x509);
b851a649
             return ASN1_GET_X509_SUCCESS;
937c42e1
         }
 
         if(map_raw(map, issuer, issuersize, x509.raw_issuer))
             break;
         if(map_sha1(map, issuer, issuersize, x509.issuer))
             break;
 
12341e15
         if(asn1_expect_rsa(map, &tbs.next, &crt.size, &hashtype2)) /* signature algo - Ex: sha1WithRSAEncryption */
937c42e1
             break;
 
         if(hashtype1 != hashtype2) {
a2bb4cdf
             cli_dbgmsg("asn1_get_x509: found conflicting RSA hash types\n");
937c42e1
             break;
         }
         x509.hashtype = hashtype1;
 
a2bb4cdf
         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");
937c42e1
             break;
a2bb4cdf
         }
937c42e1
         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;
         }
 
c7145595
         if(map_hash(map, tbsdata, tbssize, x509.tbshash, x509.hashtype)) {
a2bb4cdf
             cli_dbgmsg("asn1_get_x509: Unsupported hashtype or hash computation failed\n");
937c42e1
             break;
c7145595
 
a2bb4cdf
         }
937c42e1
 
50d1a0b6
         if(crtmgr_add(crts, &x509))
937c42e1
             break;
         cli_crt_clear(&x509);
b851a649
         return ASN1_GET_X509_SUCCESS;
a616029e
     } while(0);
7bcfb2f3
     cli_crt_clear(&x509);
b851a649
     return ret;
b28a0d6f
 }
 
604cc8ed
 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;
c7145595
     uint8_t hash[MAX_HASH_SIZE];
604cc8ed
     cli_crt_hashtype hashtype;
7f465035
     cli_crt_hashtype hashtype2;
604cc8ed
     unsigned int hashsize;
c7145595
     uint8_t md[MAX_HASH_SIZE];
604cc8ed
     int result;
     void *ctx;
 
     do {
         if(asn1_expect_objtype(map, *asn1data, size, &asn1, ASN1_TYPE_SEQUENCE)) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: expected SEQUENCE inside counterSignature SET\n");
604cc8ed
             break;
         }
 
         avail = asn1.size;
796cf4ce
 
         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");
604cc8ed
             break;
         }
796cf4ce
         asn1.content = deep.next;
604cc8ed
 
         if(asn1_expect_objtype(map, asn1.content, &avail, &asn1, ASN1_TYPE_SEQUENCE)) { /* issuerAndSerialNumber */
c7145595
             cli_dbgmsg("asn1_parse_countersignature: unable to parse issuerAndSerialNumber SEQUENCE in counterSignature\n");
604cc8ed
             break;
         }
 
         if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, ASN1_TYPE_SEQUENCE)) { /* issuer */
c7145595
             cli_dbgmsg("asn1_parse_countersignature: unable to parse issuer SEQUENCE in counterSignature\n");
604cc8ed
             break;
         }
         // Compute the hash of the issuer section
         if(map_sha1(map, deep.content, deep.size, issuer)) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: error in call to map_sha1 for counterSignature issuer\n");
604cc8ed
             break;
         }
 
         if(asn1_expect_objtype(map, deep.next, &asn1.size, &deep, ASN1_TYPE_INTEGER)) { /* serial */
c7145595
             cli_dbgmsg("asn1_parse_countersignature: expected ASN1_TYPE_INTEGER serial for counterSignature\n");
604cc8ed
             break;
         }
 
         // Compute the hash of the serial INTEGER
         if(map_sha1(map, deep.content, deep.size, serial)) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: error in call to map_sha1 for counterSignature serial\n");
604cc8ed
             break;
         }
 
         if(asn1.size) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: extra data inside counterSignature issuer\n");
604cc8ed
             break;
         }
 
c7145595
         if(asn1_expect_hash_algo(map, &asn1.next, &avail, &hashtype, &hashsize)) {
             cli_dbgmsg("asn1_parse_countersignature: error parsing counterSignature digestAlgorithm\n");
604cc8ed
             break;
         }
c7145595
 
         if(map_hash(map, message, message_size, md, hashtype)) {
             cli_dbgmsg("asn1_parse_countersignature: failed to map in message/compute countersignature hash\n");
604cc8ed
             break;
c7145595
 
604cc8ed
         }
 
         attrs = asn1.next;
         if(asn1_expect_objtype(map, asn1.next, &avail, &asn1, 0xa0)) { /* authenticatedAttributes */
c7145595
             cli_dbgmsg("asn1_parse_countersignature: unable to parse counterSignature authenticatedAttributes section\n");
604cc8ed
             break;
         }
         attrs_size = (uint8_t *)(asn1.next) - attrs;
         if(asn1.next == NULL && attrs_size < 2) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: counterSignature authenticatedAttributes are too small\n");
604cc8ed
             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 */
c7145595
                 cli_dbgmsg("asn1_parse_countersignature: expected counterSignature attribute SEQUENCE\n");
604cc8ed
                 dsize = 1;
                 break;
             }
             if(asn1_expect_objtype(map, deep.content, &deep.size, &deeper, ASN1_TYPE_OBJECT_ID)) { /* attribute type */
c7145595
                 cli_dbgmsg("asn1_parse_countersignature: expected attribute type inside counterSignature attribute SEQUENCE\n");
604cc8ed
                 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))) {
c7145595
                 cli_dbgmsg("asn1_parse_countersignature: failed to read counterSignature authenticated attribute\n");
604cc8ed
                 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)) {
c7145595
                 cli_dbgmsg("asn1_parse_countersignature: duplicate field in countersignature\n");
604cc8ed
                 dsize = 1;
                 break;
             }
             result |= (1<<content);
             if(asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, ASN1_TYPE_SET)) { /* set - contents */
c7145595
                 cli_dbgmsg("asn1_parse_countersignature: failed to read counterSignature authenticated attribute\n");
604cc8ed
                 dsize = 1;
                 break;
             }
             if(deep.size) {
c7145595
                 cli_dbgmsg("asn1_parse_countersignature: extra data in countersignature value\n");
604cc8ed
                 dsize = 1;
                 break;
             }
             deep.size = deeper.size;
             switch(content) {
d28779ec
             case 0:
             {  /* contentType = pkcs7-data */
                 const void *backupPtr = deeper.content;
                 unsigned int backupSize = deep.size;
604cc8ed
                 if(asn1_expect_obj(map, &deeper.content, &deep.size, ASN1_TYPE_OBJECT_ID, lenof(OID_pkcs7_data), OID_pkcs7_data)) {
d28779ec
                     cli_dbgmsg("asn1_parse_countersignature: contentType != pkcs7-data, checking for timestampToken instead\n");
                     /* Some signatures use OID_timestampToken instead, so allow
                      * that also (despite the 2008 spec saying that this value
                      * must be pkcs7-data) */
                     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)
c7145595
                     cli_dbgmsg("asn1_parse_countersignature: extra data in countersignature content-type\n");
604cc8ed
                 break;
d28779ec
             }
604cc8ed
             case 1:  /* messageDigest */
                 if(asn1_expect_obj(map, &deeper.content, &deep.size, ASN1_TYPE_OCTET_STRING, hashsize, md)) {
                     deep.size = 1;
c7145595
                     cli_dbgmsg("asn1_parse_countersignature: countersignature hash mismatch\n");
604cc8ed
                 } else if(deep.size)
c7145595
                     cli_dbgmsg("asn1_parse_countersignature: extra data in countersignature message-digest\n");
604cc8ed
                 break;
             case 2:  /* signingTime */
                 {
                     time_t sigdate; /* FIXME shall i use it?! */
                     if(asn1_get_time(map, &deeper.content, &deep.size, &sigdate)) {
c7145595
                         cli_dbgmsg("asn1_parse_countersignature: an error occurred when getting the time\n");
604cc8ed
                         deep.size = 1;
                     } else if(deep.size)
c7145595
                         cli_dbgmsg("asn1_parse_countersignature: extra data in countersignature signing-time\n");
604cc8ed
                     else if(sigdate < not_before || sigdate > not_after) {
c7145595
                         cli_dbgmsg("asn1_parse_countersignature: countersignature timestamp outside cert validity\n");
604cc8ed
                         deep.size = 1;
                     }
                     break;
                 }
             }
             if(deep.size) {
                 dsize = 1;
                 break;
             }
         }
         if(dsize)
             break;
         if(result != 7) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: some important attributes are missing in countersignature\n");
604cc8ed
             break;
         }
 
7f465035
         // 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");
604cc8ed
             break;
         }
7f465035
 
         if (hashtype2 != CLI_RSA && hashtype2 != hashtype) {
             cli_dbgmsg("asn1_parse_countersignature: digestEncryptionAlgorithm conflicts with digestAlgorithm\n");
604cc8ed
             break;
         }
 
         if(asn1_expect_objtype(map, asn1.next, &avail, &asn1, ASN1_TYPE_OCTET_STRING)) { /* encryptedDigest */
c7145595
             cli_dbgmsg("asn1_parse_countersignature: unexpected encryptedDigest value in counterSignature\n");
604cc8ed
             break;
         }
         if(asn1.size > 513) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: countersignature encryptedDigest too long\n");
604cc8ed
             break;
         }
         if(avail) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: extra data inside countersignature\n");
604cc8ed
             break;
         }
         if(!fmap_need_ptr_once(map, attrs, attrs_size)) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: failed to read authenticatedAttributes\n");
604cc8ed
             break;
         }
 
a9a7122b
         if (NULL == (ctx = get_hash_ctx(hashtype))) {
604cc8ed
             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)) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: failed to read countersignature encryptedDigest\n");
604cc8ed
             break;
         }
         if(!crtmgr_verify_pkcs7(cmgr, issuer, serial, asn1.content, asn1.size, hashtype, hash, VRFY_TIME)) {
c7145595
             cli_dbgmsg("asn1_parse_countersignature: pkcs7 countersignature verification failed\n");
604cc8ed
             break;
         }
 
c7145595
         cli_dbgmsg("asn1_parse_countersignature: countersignature verification completed successfully\n");
604cc8ed
 
         return 0;
 
     } while(0);
 
     return 1;
 }
 
64ecd109
 static cl_error_t 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;
5ee2fc53
     uint8_t issuer[SHA1_HASH_SIZE], serial[SHA1_HASH_SIZE];
604cc8ed
     const uint8_t *message, *attrs;
     unsigned int dsize, message_size, attrs_size;
5ee2fc53
     // hash is used to hold the hashes we compute as part of sig verification
c7145595
     uint8_t hash[MAX_HASH_SIZE];
     cli_crt_hashtype hashtype, hashtype2;
12341e15
     unsigned int hashsize;
5ee2fc53
     // md is used to hold the message digest we extract from the signature
c7145595
     uint8_t md[MAX_HASH_SIZE];
f5092717
     cli_crt *x509;
da6e06dd
     void *ctx;
81eb1d29
     int result;
64ecd109
     cl_error_t ret = CL_EPARSE;
5b48b665
 
a8a99142
     cli_dbgmsg("in asn1_parse_mscat\n");
 
5b48b665
     do {
937c42e1
         if(!(message = fmap_need_off_once(map, offset, 1))) {
             cli_dbgmsg("asn1_parse_mscat: failed to read pkcs#7 entry\n");
             break;
         }
 
12341e15
         if(asn1_expect_objtype(map, message, &size, &asn1, ASN1_TYPE_SEQUENCE)){ /* SEQUENCE */
             cli_dbgmsg("asn1_parse_mscat: expected SEQUENCE at top level\n");
937c42e1
             break;
12341e15
         }
 
5ee2fc53
         // Many signatures have zero bytes at the end (padding?)
937c42e1
         /* if(size) { */
         /*     cli_dbgmsg("asn1_parse_mscat: found extra data after pkcs#7 %u\n", size); */
         /*     break; */
         /* } */
         size = asn1.size;
12341e15
         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");
937c42e1
             break;
12341e15
         }
         if(asn1_expect_objtype(map, asn1.content, &size, &asn1, 0xa0)){ /* [0] - content */
             cli_dbgmsg("asn1_parse_mscat: expected '[0] - content' following signedData contentType\n");
937c42e1
             break;
12341e15
         }
937c42e1
         if(size) {
             cli_dbgmsg("asn1_parse_mscat: found extra data in pkcs#7\n");
             break;
         }
         size = asn1.size;
12341e15
         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");
937c42e1
             break;
12341e15
         }
937c42e1
         if(size) {
             cli_dbgmsg("asn1_parse_mscat: found extra data in signedData\n");
cf3138e1
             break;
         }
604cc8ed
 
937c42e1
         size = asn1.size;
12341e15
         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");
937c42e1
             break;
12341e15
         }
937c42e1
 
12341e15
         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");
937c42e1
             break;
12341e15
         }
937c42e1
 
5ee2fc53
         // 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.
c7145595
         if (asn1_expect_hash_algo(map, &asn1.content, &asn1.size, &hashtype, &hashsize)) {
             cli_dbgmsg("asn1_parse_mscat: error parsing SignedData digestAlgorithm\n");
937c42e1
             break;
12341e15
         }
5ee2fc53
         if (asn1.size) {
c7145595
             cli_dbgmsg("asn1_parse_mscat: found extra data in the SignerData digestAlgorithm SET\n");
5ee2fc53
             break;
         }
 
         // We've finished parsing the DigestAlgorithmIdentifiers SET, so start
         // back parsing the SignedData
12341e15
         if(asn1_expect_objtype(map, asn1.next, &size, &asn1, ASN1_TYPE_SEQUENCE)){ /* SEQUENCE - contentInfo */
5ee2fc53
             cli_dbgmsg("asn1_parse_mscat: expected 'SEQUENCE - contentInfo' inside SignedData following DigestAlgorithmIdentifiers\n");
937c42e1
             break;
12341e15
         }
5ee2fc53
         // Parse the contentInfo SEQUENCE.  asn1.next and size point to the
         // certificates, so these need to be preserved
 
937c42e1
         /* Here there is either a PKCS #7 ContentType Object Identifier for Certificate Trust List (szOID_CTL)
          * or a single SPC_INDIRECT_DATA_OBJID */
         if(
ee769cc7
            (!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))
12341e15
            ){
             cli_dbgmsg("asn1_parse_mscat: unexpected ContentType for embedded mode %d\n", embedded);
937c42e1
             break;
12341e15
         }
937c42e1
 
12341e15
         if(asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0xa0)){
             cli_dbgmsg("asn1_parse_mscat: expected '[0] - content' following DigestAlgorithmIdentifier contentType\n");
937c42e1
             break;
12341e15
         }
937c42e1
         if(asn1.size) {
             cli_dbgmsg("asn1_parse_mscat: found extra data in contentInfo\n");
             break;
         }
         dsize = deep.size;
ee769cc7
         if(asn1_expect_objtype(map, deep.content, &dsize, &deep, ASN1_TYPE_SEQUENCE))
12341e15
         {
             cli_dbgmsg("asn1_parse_mscat: expected SEQUENCE in DigestAlgorithmIdentifier '[0] - contentInfo'\n");
937c42e1
             break;
12341e15
         }
937c42e1
         if(dsize) {
             cli_dbgmsg("asn1_parse_mscat: found extra data in content\n");
             break;
         }
12341e15
 
         /*
5ee2fc53
          * Hashes should look like:
12341e15
          * 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)
          */
 
937c42e1
         *hashes = deep.content;
         *hashes_size = deep.size;
 
5ee2fc53
         // Now resume parsing SignedData - certificates
 
12341e15
         if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)){ /* certificates */
             cli_dbgmsg("asn1_parse_mscat: expected 0xa0 certificates entry\n");
937c42e1
             break;
12341e15
         }
937c42e1
 
         dsize = asn1.size;
         if(dsize) {
             crtmgr newcerts;
             crtmgr_init(&newcerts);
             while(dsize) {
50d1a0b6
                 result = asn1_get_x509(map, &asn1.content, &dsize, &newcerts);
b851a649
                 if(ASN1_GET_X509_UNRECOVERABLE_ERROR == result) {
937c42e1
                     dsize = 1;
                     break;
                 }
b851a649
                 else if(ASN1_GET_X509_CERT_ERROR == result) {
                     cli_dbgmsg("asn1_parse_mscat: skipping x509 certificate with errors\n");
                 }
937c42e1
             }
             if(dsize) {
                 crtmgr_free(&newcerts);
b851a649
                 cli_dbgmsg("asn1_parse_mscat: an unrecoverable error occurred while extracting x509 certificates\n");
937c42e1
                 break;
             }
             if(newcerts.crts) {
                 x509 = newcerts.crts;
50d1a0b6
                 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) {
937c42e1
                         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];
8df3fab2
                         char mod[1024+1], exp[1024+1];
937c42e1
                         int j=1024;
 
8df3fab2
                         fp_toradix_n(&x509->n, mod, 16, j+1);
                         fp_toradix_n(&x509->e, exp, 16, j+1);
937c42e1
                         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]);
                         }
 
                         cli_dbgmsg_internal("cert:\n");
                         cli_dbgmsg_internal("  subject: %s\n", subject);
                         cli_dbgmsg_internal("  serial: %s\n", serial);
                         cli_dbgmsg_internal("  pubkey: %s\n", mod);
                         cli_dbgmsg_internal("  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_internal("  ==============RAW==============\n");
                         cli_dbgmsg_internal("  raw_subject: %s\n", raw_subject);
                         cli_dbgmsg_internal("  raw_serial: %s\n", raw_serial);
                         cli_dbgmsg_internal("  raw_issuer: %s\n", raw_issuer);
50d1a0b6
 
                         x509 = x509->next;
                     }
                     x509 = newcerts.crts;
                 }
 
                 while(x509) {
                     cli_crt *parent;
 
                     /* If the certificate is in the trust store already, remove
                      * it from the newcerts list */
                     if (crtmgr_lookup(cmgr, x509)) {
                         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;
937c42e1
                     }
 
50d1a0b6
                     /* Determine whether the cert is signed by one in our trust
                      * store or has a blacklist entry */
                     parent = crtmgr_verify_crt(cmgr, x509);
 
937c42e1
                     if(parent) {
                         if (parent->isBlacklisted) {
a2bb4cdf
                             // NOTE: In this case, parent is a blacklist entry
                             // in cmgr for this certificate, not a blacklist
                             // entry for this certificate's parent
64ecd109
                             ret = CL_VIRUS;
937c42e1
                             cli_dbgmsg("asn1_parse_mscat: Authenticode certificate %s is revoked. Flagging sample as virus.\n", (parent->name ? parent->name : "(no name)"));
64ecd109
                             crtmgr_free(&newcerts);
                             goto finish;
937c42e1
                         }
 
a2bb4cdf
                         // TODO Why is this done?
937c42e1
                         x509->codeSign &= parent->codeSign;
                         x509->timeSign &= parent->timeSign;
a2bb4cdf
 
12341e15
                         if(crtmgr_add(cmgr, x509)) {
                             cli_dbgmsg("asn1_parse_mscat: adding x509 cert to crtmgr failed\n");
937c42e1
                             break;
12341e15
                         }
937c42e1
                         crtmgr_del(&newcerts, x509);
50d1a0b6
 
                         /* 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. */
937c42e1
                         x509 = newcerts.crts;
                         continue;
                     }
50d1a0b6
 
937c42e1
                     x509 = x509->next;
                 }
                 if(x509) {
                     crtmgr_free(&newcerts);
                     break;
7209997f
                 }
937c42e1
                 if(newcerts.items)
                     cli_dbgmsg("asn1_parse_mscat: %u certificates did not verify\n", newcerts.items);
                 crtmgr_free(&newcerts);
             }
         }
 
5ee2fc53
         // Parse the final section in SignedData - SignerInfos
12341e15
         if(asn1_get_obj(map, asn1.next, &size, &asn1)) {
             cli_dbgmsg("asn1_parse_mscat: failed to get next ASN1 section\n");
937c42e1
             break;
12341e15
         }
         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");
937c42e1
             break;
12341e15
         }
ee769cc7
         if(asn1.type != ASN1_TYPE_SET) { /* signerInfos */
5ee2fc53
             cli_dbgmsg("asn1_parse_mscat: unexpected type %02x for signerInfo\n", asn1.type);
937c42e1
             break;
         }
         if(size) {
             cli_dbgmsg("asn1_parse_mscat: unexpected extra data after signerInfos\n");
             break;
         }
         size = asn1.size;
12341e15
         if(asn1_expect_objtype(map, asn1.content, &size, &asn1, ASN1_TYPE_SEQUENCE)) {
             cli_dbgmsg("asn1_parse_mscat: expected SEQUENCE in signerInfos");
937c42e1
             break;
12341e15
         }
937c42e1
         if(size) {
             cli_dbgmsg("asn1_parse_mscat: only one signerInfo shall be present\n");
             break;
         }
         size = asn1.size;
12341e15
         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");
937c42e1
             break;
12341e15
         }
         if(asn1_expect_objtype(map, asn1.content, &size, &asn1, ASN1_TYPE_SEQUENCE)){ /* issuerAndSerialNumber */
             cli_dbgmsg("asn1_parse_mscat: expected issuerAndSerialNumber SEQUENCE\n");
937c42e1
             break;
12341e15
         }
5ee2fc53
         // asn1.next and size must be preserved so we can continue parsing
         // SignerInfos, so switch to deep
937c42e1
         dsize = asn1.size;
12341e15
         if(asn1_expect_objtype(map, asn1.content, &dsize, &deep, ASN1_TYPE_SEQUENCE)){ /* issuer */
             cli_dbgmsg("asn1_parse_mscat: expected issuer SEQUENCE\n");
937c42e1
             break;
12341e15
         }
 
         /* 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.3commonName(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");
937c42e1
             break;
12341e15
         }
937c42e1
 
12341e15
         if(asn1_expect_objtype(map, deep.next, &dsize, &deep, ASN1_TYPE_INTEGER)){ /* serial */
             cli_dbgmsg("asn1_parse_mscat: expected ASN1_TYPE_INTEGER serial\n");
937c42e1
             break;
12341e15
         }
 
         /* 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");
937c42e1
             break;
12341e15
         }
937c42e1
         if(dsize) {
             cli_dbgmsg("asn1_parse_mscat: extra data inside issuerAndSerialNumber\n");
             break;
         }
5ee2fc53
 
         // Resume parsing the SignerInfos using asn1.next and size
c7145595
         if (asn1_expect_hash_algo(map, &asn1.next, &size, &hashtype2, &hashsize)) {
             cli_dbgmsg("asn1_parse_mscat: error parsing SignerInfo digestAlgorithm\n");
5ee2fc53
             break;
         }
 
         // Verify that the SignerInfo digestAlgorithm matches the one from the SignedData section
c7145595
         if (hashtype != hashtype2) {
                 cli_dbgmsg("asn1_parse_mscat: SignerInfo digestAlgorithm is not the same as the algorithm in SignedData\n");
5ee2fc53
                 break;
12341e15
         }
7209997f
 
5ee2fc53
         // Continue on to the authenticatedAttributes section within SignerInfo
937c42e1
         attrs = asn1.next;
12341e15
         if(asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)){ /* authenticatedAttributes */
             cli_dbgmsg("asn1_parse_mscat: unable to parse authenticatedAttributes section\n");
937c42e1
             break;
12341e15
         }
937c42e1
         attrs_size = (uint8_t *)(asn1.next) - attrs;
5ee2fc53
         if(asn1.next == NULL || attrs_size < 2) {
937c42e1
             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;
ee769cc7
             if(asn1_expect_objtype(map, deep.next, &dsize, &deep, ASN1_TYPE_SEQUENCE)) { /* attribute */
12341e15
                 cli_dbgmsg("asn1_parse_mscat: expected attribute SEQUENCE\n");
937c42e1
                 dsize = 1;
                 break;
             }
ee769cc7
             if(asn1_expect_objtype(map, deep.content, &deep.size, &deeper, ASN1_TYPE_OBJECT_ID)) { /* attribute type */
12341e15
                 cli_dbgmsg("asn1_parse_mscat: expected attribute type inside attribute SEQUENCE\n");
937c42e1
                 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;
ee769cc7
             if(asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, ASN1_TYPE_SET)) { /* set - contents */
12341e15
                 cli_dbgmsg("asn1_parse_mscat: expected 'set - contents' for authenticated attribute\n");
937c42e1
                 dsize = 1;
                 break;
             }
             if(deep.size) {
                 cli_dbgmsg("asn1_parse_mscat: extra data in authenticated attributes\n");
                 dsize = 1;
                 break;
7209997f
             }
50c5d4b5
 
937c42e1
             if(result & (1<<content)) {
                 cli_dbgmsg("asn1_parse_mscat: contentType or messageDigest appear twice\n");
                 dsize = 1;
                 break;
             }
b01b78d1
 
937c42e1
             if(content == 0) { /* contentType */
                 if(
ee769cc7
                    (!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 */
937c42e1
                   ) {
12341e15
                     cli_dbgmsg("asn1_parse_mscat: unexpected ContentType for embedded mode %d (for authenticated attribute)\n", embedded);
937c42e1
                     dsize = 1;
                     break;
                 }
                 result |= 1;
             } else { /* messageDigest */
ee769cc7
                 if(asn1_expect_objtype(map, deeper.content, &deeper.size, &cobj, ASN1_TYPE_OCTET_STRING)) {
12341e15
                     cli_dbgmsg("asn1_parse_mscat: unexpected messageDigest value\n");
937c42e1
                     dsize = 1;
                     break;
                 }
5ee2fc53
                 if(cobj.size != hashsize) {
                     cli_dbgmsg("asn1_parse_mscat: messageDigest attribute has the wrong size (%u)\n", cobj.size);
937c42e1
                     dsize = 1;
                     break;
                 }
5ee2fc53
                 if(!fmap_need_ptr_once(map, cobj.content, hashsize)) {
937c42e1
                     cli_dbgmsg("asn1_parse_mscat: failed to read authenticated attribute\n");
                     dsize = 1;
                     break;
                 }
5ee2fc53
                 memcpy(md, cobj.content, hashsize);
937c42e1
                 result |= 2;
             }
             if(deeper.size) {
                 cli_dbgmsg("asn1_parse_mscat: extra data in authenticated attribute\n");
                 dsize = 1;
b01b78d1
                 break;
937c42e1
             }
         }
         if(dsize)
             break;
         if(result != 3) {
             cli_dbgmsg("asn1_parse_mscat: contentType or messageDigest are missing\n");
             break;
         }
 
12341e15
         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");
937c42e1
             break;
12341e15
         }
937c42e1
 
12341e15
         if(asn1_expect_objtype(map, asn1.next, &size, &asn1, ASN1_TYPE_OCTET_STRING)) { /* encryptedDigest */
             cli_dbgmsg("asn1_parse_mscat: unexpected encryptedDigest value\n");
937c42e1
             break;
12341e15
         }
 
5ee2fc53
         // TODO Make this a #define with the greatest possible length (SHA512)
937c42e1
         if(asn1.size > 513) {
             cli_dbgmsg("asn1_parse_mscat: encryptedDigest too long\n");
             break;
cf3138e1
         }
12341e15
 
c7145595
         if(map_hash(map, *hashes, *hashes_size, hash, hashtype)) {
             cli_dbgmsg("asn1_parse_mscat: failed to map in message/compute message digest\n");
937c42e1
             break;
c7145595
 
12341e15
         }
5ee2fc53
 
         if(memcmp(hash, md, hashsize)) {
937c42e1
             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;
         }
 
a9a7122b
         if (NULL == (ctx = get_hash_ctx(hashtype))) {
5ee2fc53
             break;
         }
 
da6e06dd
         cl_update_hash(ctx, "\x31", 1);
cd94be7a
         cl_update_hash(ctx, (void *)(attrs + 1), attrs_size - 1);
5ee2fc53
         cl_finish_hash(ctx, hash);
937c42e1
 
         if(!fmap_need_ptr_once(map, asn1.content, asn1.size)) {
             cli_dbgmsg("asn1_parse_mscat: failed to read encryptedDigest\n");
             break;
         }
12341e15
 
         // Verify the authenticatedAttributes
5ee2fc53
         if(!(x509 = crtmgr_verify_pkcs7(cmgr, issuer, serial, asn1.content, asn1.size, hashtype, hash, VRFY_CODE))) {
937c42e1
             cli_dbgmsg("asn1_parse_mscat: pkcs7 signature verification failed\n");
64ecd109
             ret = CL_EVERIFY;
f077c617
             break;
937c42e1
         }
         message = asn1.content;
         message_size = asn1.size;
f077c617
 
a2bb4cdf
         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
          * this point in the code no matching blacklist rules fired). */
 
937c42e1
         if(!size) {
a2bb4cdf
             time_t now;
 
             // No countersignature, so judge validity based on time
             now = time(NULL);
 
             if(now < x509->not_before || now > x509->not_after) {
604cc8ed
                 cli_dbgmsg("asn1_parse_mscat: no countersignature (unauthAttrs missing) and signing certificate has expired\n");
64ecd109
                 ret = CL_EVERIFY;
a2bb4cdf
                 break;
             }
 
604cc8ed
             cli_dbgmsg("asn1_parse_mscat: no countersignature (unauthAttrs missing) but the signing certificate is still valid\n");
64ecd109
             ret = CL_CLEAN;
604cc8ed
             goto finish;
937c42e1
         }
 
12341e15
         if(size && asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa1)) { /* unauthenticatedAttributes */
             cli_dbgmsg("asn1_parse_mscat: unable to find unauthenticatedAttributes section\n");
937c42e1
             break;
12341e15
         }
937c42e1
 
         if(size) {
             cli_dbgmsg("asn1_parse_mscat: extra data inside signerInfo\n");
             break;
         }
 
604cc8ed
         // Parse the unauthenticated attributes
937c42e1
 
         dsize = asn1.size;
         deep.next = asn1.content;
604cc8ed
         result = 0;
937c42e1
         while(dsize) {
             int content;
604cc8ed
             if(asn1_expect_objtype(map, deep.next, &dsize, &deep, ASN1_TYPE_SEQUENCE)) {
                 cli_dbgmsg("asn1_parse_mscat: expected SEQUENCE starting an unauthenticatedAttribute\n");
937c42e1
                 dsize = 1;
                 break;
             }
604cc8ed
             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");
937c42e1
                 dsize = 1;
                 break;
             }
604cc8ed
             // 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
 
             if(deeper.size != lenof(OID_countersignature) && deeper.size != lenof(OID_nestedSignatures)) {
937c42e1
                 continue;
604cc8ed
             }
937c42e1
 
604cc8ed
             if(!fmap_need_ptr_once(map, deeper.content, deeper.size)) {
                 cli_dbgmsg("asn1_parse_mscat: failed to read unauthenticated attribute OID\n");
937c42e1
                 dsize = 1;
                 break;
             }
604cc8ed
 
             if(!memcmp(deeper.content, OID_countersignature, deeper.size))
                 content = 0; /* counterSignature */
             else if(!memcmp(deeper.content, OID_nestedSignatures, deeper.size))
                 content = 1; /* nested */
             else {
937c42e1
                 continue;
             }
604cc8ed
 
12341e15
             if(asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, ASN1_TYPE_SET)) { /* set - contents */
604cc8ed
                 cli_dbgmsg("asn1_parse_mscat: expected 'set - contents' inside unauthenticated attribute\n");
937c42e1
                 dsize = 1;
                 break;
             }
             if(deep.size) {
604cc8ed
                 cli_dbgmsg("asn1_parse_mscat: extra data in unauthenticated attribute\n");
937c42e1
                 dsize = 1;
                 break;
             }
604cc8ed
 
             if(result & (1<<content)) {
                 cli_dbgmsg("asn1_parse_mscat: counterSignature or nestedSignature appear twice\n");
937c42e1
                 dsize = 1;
                 break;
             }
 
604cc8ed
             if(content == 0) { /* counterSignature */
937c42e1
 
604cc8ed
                 if(asn1_parse_countersignature(map, &deeper.content, &deeper.size, cmgr, message, message_size, x509->not_before, x509->not_after)) {
                     dsize = 1;
                     break;
                 }
937c42e1
 
604cc8ed
                 result |= 1;
937c42e1
 
604cc8ed
             } else { /* nestedSignature */
12341e15
 
604cc8ed
                 // TODO Support parsing these out in the future
                 cli_dbgmsg("asn1_parse_mscat: nested signatures detected but parsing them is not currently supported\n");
12341e15
 
604cc8ed
                 deeper.size = 0;
                 result |= 2;
             }
             if(deeper.size) {
                 cli_dbgmsg("asn1_parse_mscat: extra data in unauthenticated attribute\n");
                 dsize = 1;
                 break;
             }
937c42e1
         }
604cc8ed
         if(dsize)
937c42e1
             break;
7438ffdd
 
64ecd109
         cli_dbgmsg("asn1_parse_mscat: unauthenticatedAttributes successfully parsed\n");
 
0bbf4e13
         if (1 != (result & 1)) {
604cc8ed
             time_t now;
7438ffdd
 
604cc8ed
             // No countersignature, so judge validity based on time
             now = time(NULL);
7438ffdd
 
604cc8ed
             if(now < x509->not_before || now > x509->not_after) {
                 cli_dbgmsg("asn1_parse_mscat: no countersignature and signing certificate has expired\n");
64ecd109
                 ret = CL_EVERIFY;
7438ffdd
                 break;
             }
 
604cc8ed
             cli_dbgmsg("asn1_parse_mscat: no countersignature but the signing certificate is still valid\n");
7438ffdd
         }
 
64ecd109
         ret = CL_CLEAN;
12341e15
 
a8a99142
     } while(0);
b0357255
 
64ecd109
 finish:
     if (CL_EPARSE == ret) {
         cli_dbgmsg("asn1_parse_mscat: failed to parse authenticode section\n");
     }
     return ret;
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
 
64ecd109
     // TODO As currently implemented, loading in a .cat file with -d requires
     // an accompanying .crb with whitelist entries that will cause the .cat
     // 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(map, 0, map->len, &engine->cmgr, 0, &c.next, &size, engine))
b01b78d1
         return 1;
6bc5d0cb
 
ee769cc7
     if(asn1_expect_objtype(map, c.next, &size, &c, ASN1_TYPE_SEQUENCE))
937c42e1
         return 1;
ee769cc7
     if(asn1_expect_obj(map, &c.content, &c.size, ASN1_TYPE_OBJECT_ID, lenof(OID_szOID_CATALOG_LIST), OID_szOID_CATALOG_LIST))
937c42e1
         return 1;
7595e108
     if(c.size) {
937c42e1
         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 */
937c42e1
         return 1;
28fcc245
     if(asn1_expect_objtype(map, c.next, &size, &c, 0x17)) /* Effective date - WTF?! */
937c42e1
         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 */
937c42e1
         return 1;
ee769cc7
     if(asn1_expect_objtype(map, c.next, &size, &c, ASN1_TYPE_SEQUENCE)) /* hashes here */
937c42e1
         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) {
937c42e1
         struct cli_asn1 tag;
ee769cc7
         if(asn1_expect_objtype(map, c.next, &size, &c, ASN1_TYPE_SEQUENCE))
937c42e1
             return 1;
ee769cc7
         if(asn1_expect_objtype(map, c.content, &c.size, &tag, ASN1_TYPE_OCTET_STRING)) /* TAG NAME */
937c42e1
             return 1;
ee769cc7
         if(asn1_expect_objtype(map, tag.next, &c.size, &tag, ASN1_TYPE_SET)) /* set */
937c42e1
             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;
             int hashtype;
 
ee769cc7
             if(asn1_expect_objtype(map, tag.content, &tag.size, &tagval1, ASN1_TYPE_SEQUENCE))
937c42e1
                 return 1;
             tag.content = tagval1.next;
 
ee769cc7
             if(asn1_expect_objtype(map, tagval1.content, &tagval1.size, &tagval2, ASN1_TYPE_OBJECT_ID))
937c42e1
                 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).. */
 
ee769cc7
             if(asn1_expect_objtype(map, tagval2.next, &tagval1.size, &tagval2, ASN1_TYPE_SET))
937c42e1
                 return 1;
             if(tagval1.size) {
                 cli_dbgmsg("asn1_load_mscat: found extra data in tag value\n");
                 return 1;
             }
 
ee769cc7
             if(asn1_expect_objtype(map, tagval2.content, &tagval2.size, &tagval1, ASN1_TYPE_SEQUENCE))
937c42e1
                 return 1;
             if(tagval2.size) {
                 cli_dbgmsg("asn1_load_mscat: found extra data in SPC_INDIRECT_DATA_OBJID tag\n");
                 return 1;
             }
 
ee769cc7
             if(asn1_expect_objtype(map, tagval1.content, &tagval1.size, &tagval2, ASN1_TYPE_SEQUENCE))
937c42e1
                 return 1;
 
ee769cc7
             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 */
937c42e1
                 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)))
                 hashtype = 2;
             else if(!memcmp(tagval3.content, OID_SPC_CAB_DATA_OBJID, lenof(OID_SPC_CAB_DATA_OBJID)))
                 hashtype = 1;
             else {
                 cli_dbgmsg("asn1_load_mscat: unexpected hash type\n");
                 return 1;
             }
 
ee769cc7
             if(asn1_expect_objtype(map, tagval2.next, &tagval1.size, &tagval2, ASN1_TYPE_SEQUENCE))
937c42e1
                 return 1;
             if(tagval1.size) {
                 cli_dbgmsg("asn1_load_mscat: found extra data after hash\n");
                 return 1;
             }
 
7f465035
             if(asn1_expect_algo(map, &tagval2.content, &tagval2.size, lenof(OID_sha1), OID_sha1)) { /* objid 1.3.14.3.2.26 - sha1 */
                 cli_dbgmsg("asn1_load_mscat: currently only SHA1 hashes are supported for .cat file signatures\n");
937c42e1
                 return 1;
7f465035
             }
937c42e1
 
ee769cc7
             if(asn1_expect_objtype(map, tagval2.content, &tagval2.size, &tagval3, ASN1_TYPE_OCTET_STRING))
937c42e1
                 return 1;
             if(tagval2.size) {
                 cli_dbgmsg("asn1_load_mscat: found extra data in hash\n");
                 return 1;
             }
             if(tagval3.size != SHA1_HASH_SIZE) {
                 cli_dbgmsg("asn1_load_mscat: bad hash size %u\n", tagval3.size);
                 return 1;
             }
             if(!fmap_need_ptr_once(map, tagval3.content, SHA1_HASH_SIZE)) {
                 cli_dbgmsg("asn1_load_mscat: cannot read hash\n");
                 return 1;
             }
 
             if(cli_debug_flag) {
                 char sha1[SHA1_HASH_SIZE*2+1];
                 for(i=0;i<SHA1_HASH_SIZE;i++)
                     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");
             }
             if(!engine->hm_fp) {
                 if(!(engine->hm_fp = mpool_calloc(engine->mempool, 1, sizeof(*db)))) {
                     tag.size = 1;;
                     return 1;
                 }
f0a5895b
 #ifdef USE_MPOOL
937c42e1
                 engine->hm_fp->mempool = engine->mempool;
f0a5895b
 #endif
937c42e1
             }
             if(hm_addhash_bin(engine->hm_fp, tagval3.content, CLI_HASH_SHA1, hashtype, NULL)) {
                 cli_warnmsg("asn1_load_mscat: failed to add hash\n");
                 return 1;
             }
         }
7595e108
     }
b01b78d1
 
7595e108
     return 0;
 }
 
64ecd109
 /* Check an embedded PE Authenticode section to determine whether it's trusted.
  * This will return CL_CLEAN if the file should be trusted, CL_EPARSE if an
  * error occurred while parsing the signature, CL_EVERIFY if parsing was
  * successful but there were no whitelist rules for the signature, and
  * CL_VIRUS if a blacklist rule was found for an embedded certificate. */
 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) {
7595e108
     unsigned int content_size;
     struct cli_asn1 c;
c7145595
     cli_crt_hashtype hashtype;
     uint8_t hash[MAX_HASH_SIZE];
     unsigned int hashsize;
d1fcd16d
     const void *content;
7595e108
     crtmgr certs;
     int ret;
c7145595
     void *ctx;
ebea41ed
     unsigned int i;
7595e108
 
64ecd109
     // TODO Move these into cli_checkfp_pe
5eaf0b32
     if (!(engine->dconf->pe & PE_CONF_CERTS))
64ecd109
         return CL_EVERIFY;
5eaf0b32
     if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS)
64ecd109
         return CL_EVERIFY;
5cc4cb86
 
059ca614
     cli_dbgmsg("in asn1_check_mscat (offset: %llu)\n", (long long unsigned)offset);
7595e108
     crtmgr_init(&certs);
56b4f4b0
     if(crtmgr_add_roots(engine, &certs)) {
937c42e1
         crtmgr_free(&certs);
64ecd109
         return CL_EVERIFY;
7595e108
     }
7209997f
     ret = asn1_parse_mscat(map, offset, size, &certs, 1, &content, &content_size, engine);
7595e108
     crtmgr_free(&certs);
64ecd109
     if(CL_CLEAN != ret)
         return ret;
7595e108
 
0a2492de
     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");
64ecd109
         return CL_EPARSE;
0a2492de
     }
     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");
64ecd109
         return CL_EPARSE;
0a2492de
     }
 
     // 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");
64ecd109
         return CL_EPARSE;
0a2492de
     }
7595e108
     if(content_size) {
0a2492de
         cli_dbgmsg("asn1_check_mscat: extra data in hash SEQUENCE\n");
64ecd109
         return CL_EPARSE;
7595e108
     }
0a2492de
 
c7145595
     if(asn1_expect_hash_algo(map, &c.content, &c.size, &hashtype, &hashsize)) {
         cli_dbgmsg("asn1_check_mscat: unexpected file hash algo\n");
64ecd109
         return CL_EPARSE;
c7145595
     }
 
a9a7122b
     if (NULL == (ctx = get_hash_ctx(hashtype))) {
64ecd109
         return CL_EPARSE;
0a2492de
     }
7595e108
 
c7145595
     // Now that we know the hash algorithm, compute the authenticode hash
     // across the required regions of memory.
ebea41ed
     for(i = 0; i < nregions; i++) {
64ecd109
         const uint8_t *hptr;
         if (0 == regions[i].size) {
             continue;
         }
ecae7f19
         if(!(hptr = fmap_need_off_once(map, regions[i].offset, regions[i].size))){
64ecd109
             return CL_EVERIFY;
ecae7f19
         }
 
         cl_update_hash(ctx, hptr, regions[i].size);
c7145595
     }
 
     cl_finish_hash(ctx, hash);
 
     if(cli_debug_flag) {
         char hashtxt[MAX_HASH_SIZE*2+1];
ebea41ed
         for(i=0; i<hashsize; i++)
c7145595
             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)) {
0a2492de
         cli_dbgmsg("asn1_check_mscat: computed authenticode hash did not match stored value\n");
64ecd109
         return CL_EVERIFY;
0a2492de
     }
     if(c.size) {
         cli_dbgmsg("asn1_check_mscat: extra data after the stored authenticode hash\n");
64ecd109
         return CL_EPARSE;
0a2492de
     }
7595e108
 
7cd9337a
     cli_dbgmsg("asn1_check_mscat: file with valid authenticode signature, whitelisted\n");
7595e108
     return CL_CLEAN;
 }