5d49bd06 |
/* |
e1cbc270 |
* 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
|
c9346ead |
#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
|
5f11b088 |
#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
|
cdd3f2dd |
#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
|
0d23434a |
#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
|
cc9381ae |
#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 */ |
288057e9 |
#define lenof(x) (sizeof((x)) - 1) |
2f622fb8 |
#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 |
|
0f53ea60 |
#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 |
|
288057e9 |
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); |
616c0259 |
|
288057e9 |
if (!fmap_need_ptr_once(map, data, elen)) { |
879c54f7 |
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;
}
|
288057e9 |
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)) { |
125360a7 |
cli_dbgmsg("map_sha512: failed to read hash data\n");
return 1;
}
return (cl_sha512(data, len, sha512, NULL) == NULL);
}
|
288057e9 |
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)) { |
125360a7 |
cli_dbgmsg("map_sha384: failed to read hash data\n");
return 1;
}
return (cl_sha384(data, len, sha384, NULL) == NULL);
}
|
288057e9 |
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)) { |
46e06209 |
cli_dbgmsg("map_sha256: failed to read hash data\n");
return 1;
}
return (cl_sha256(data, len, sha256, NULL) == NULL);
}
|
288057e9 |
static int map_sha1(fmap_t *map, const void *data, unsigned int len, uint8_t sha1[SHA1_HASH_SIZE])
{
if (!fmap_need_ptr_once(map, data, len)) { |
879c54f7 |
cli_dbgmsg("map_sha1: failed to read hash data\n");
return 1; |
e5c6c1aa |
} |
b2e7c931 |
return (cl_sha1(data, len, sha1, NULL) == NULL); |
e5c6c1aa |
} |
b28a0d6f |
|
288057e9 |
static int map_md5(fmap_t *map, const void *data, unsigned int len, uint8_t *md5)
{
if (!fmap_need_ptr_once(map, data, len)) { |
879c54f7 |
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 |
}
|
288057e9 |
static int map_hash(fmap_t *map, const void *data, unsigned int len, uint8_t *out_hash, cli_crt_hashtype hashtype)
{ |
0f53ea60 |
|
288057e9 |
if (hashtype == CLI_SHA1RSA) {
if (map_sha1(map, data, len, out_hash)) { |
0f53ea60 |
return 1;
} |
288057e9 |
} else if (hashtype == CLI_MD5RSA) {
if (map_md5(map, data, len, out_hash)) { |
0f53ea60 |
return 1;
} |
288057e9 |
} else if (hashtype == CLI_SHA256RSA) {
if (map_sha256(map, data, len, out_hash)) { |
0f53ea60 |
return 1;
} |
288057e9 |
} else if (hashtype == CLI_SHA384RSA) {
if (map_sha384(map, data, len, out_hash)) { |
125360a7 |
return 1;
} |
288057e9 |
} else if (hashtype == CLI_SHA512RSA) {
if (map_sha512(map, data, len, out_hash)) { |
125360a7 |
return 1;
} |
0f53ea60 |
} else {
cli_dbgmsg("asn1_map_hash: unsupported hashtype\n");
return 1;
}
return 0;
}
|
288057e9 |
static void *get_hash_ctx(cli_crt_hashtype hashtype)
{ |
125360a7 |
void *ctx = NULL; |
288057e9 |
if (hashtype == CLI_SHA1RSA) { |
125360a7 |
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;
}
|
288057e9 |
static int asn1_get_obj(fmap_t *map, const void *asn1data, unsigned int *asn1len, struct cli_asn1 *obj)
{
unsigned int asn1_sz = *asn1len; |
b28a0d6f |
unsigned int readbytes = MIN(6, asn1_sz), i; |
d1fcd16d |
const uint8_t *data; |
b28a0d6f |
|
288057e9 |
if (asn1_sz < 2) { |
879c54f7 |
cli_dbgmsg("asn1_get_obj: insufficient data length\n");
return 1; |
b28a0d6f |
}
data = fmap_need_ptr_once(map, asn1data, readbytes); |
288057e9 |
if (!data) { |
879c54f7 |
cli_dbgmsg("asn1_get_obj: obj out of file\n");
return 1; |
b28a0d6f |
}
obj->type = data[0]; |
288057e9 |
i = data[1];
data += 2;
if (i & 0x80) {
if (i == 0x80) { |
879c54f7 |
/* Not allowed in DER */
cli_dbgmsg("asn1_get_obj: unsupported indefinite length object\n");
return 1;
}
i &= ~0x80; |
288057e9 |
if (i > readbytes - 2) { |
879c54f7 |
cli_dbgmsg("asn1_get_obj: len octets overflow (or just too many)\n");
return 1;
}
obj->size = 0; |
288057e9 |
while (i--) { |
879c54f7 |
obj->size <<= 8;
obj->size |= *data; |
288057e9 |
data++; |
879c54f7 |
} |
b28a0d6f |
} else |
879c54f7 |
obj->size = i; |
b28a0d6f |
asn1_sz -= data - (uint8_t *)asn1data; |
288057e9 |
if (obj->size > asn1_sz) { |
879c54f7 |
cli_dbgmsg("asn1_get_obj: content overflow\n");
return 1; |
b28a0d6f |
}
obj->content = data; |
288057e9 |
if (obj->size == asn1_sz) |
879c54f7 |
obj->next = NULL; |
b28a0d6f |
else |
879c54f7 |
obj->next = data + obj->size; |
b28a0d6f |
*asn1len = asn1_sz - obj->size;
return 0;
}
|
288057e9 |
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); |
288057e9 |
if (ret) |
879c54f7 |
return ret; |
288057e9 |
if (obj->type != type) { |
879c54f7 |
cli_dbgmsg("asn1_expect_objtype: expected type %02x, got %02x\n", type, obj->type);
return 1; |
5b48b665 |
} |
b28a0d6f |
return 0;
}
|
288057e9 |
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); |
288057e9 |
if (ret) |
879c54f7 |
return ret; |
288057e9 |
if (obj.size != size) { |
879c54f7 |
cli_dbgmsg("asn1_expect_obj: expected size %u, got %u\n", size, obj.size);
return 1; |
5b48b665 |
} |
288057e9 |
if (size) {
if (!fmap_need_ptr_once(map, obj.content, size)) { |
879c54f7 |
cli_dbgmsg("asn1_expect_obj: failed to read content\n");
return 1;
} |
288057e9 |
if (memcmp(obj.content, content, size)) { |
879c54f7 |
cli_dbgmsg("asn1_expect_obj: content mismatch\n");
return 1;
} |
b28a0d6f |
} |
d29b5c5f |
*asn1data = obj.next; |
b28a0d6f |
return 0;
}
|
288057e9 |
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; |
288057e9 |
if ((ret = asn1_expect_objtype(map, *asn1data, asn1len, &obj, ASN1_TYPE_SEQUENCE))) /* SEQUENCE */ |
879c54f7 |
return ret; |
288057e9 |
avail = obj.size; |
b28a0d6f |
*asn1data = obj.next;
|
288057e9 |
if ((ret = asn1_expect_obj(map, &obj.content, &avail, ASN1_TYPE_OBJECT_ID, algo_size, algo))) /* ALGO */ |
879c54f7 |
return ret; |
d26a6fd2 |
// 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 |
288057e9 |
if (avail && (ret = asn1_expect_obj(map, &obj.content, &avail, ASN1_TYPE_NULL, 0, NULL))) { /* NULL */ |
b7f6b615 |
cli_dbgmsg("asn1_expect_algo: expected NULL after AlgorithmIdentifier OID\n"); |
879c54f7 |
return ret; |
b7f6b615 |
} |
288057e9 |
if (avail) { |
879c54f7 |
cli_dbgmsg("asn1_expect_algo: extra data found in SEQUENCE\n");
return 1; |
5b48b665 |
} |
b28a0d6f |
return 0;
}
|
288057e9 |
static int asn1_expect_hash_algo(fmap_t *map, const void **asn1data, unsigned int *asn1len, cli_crt_hashtype *hashtype, unsigned int *hashsize)
{ |
0f53ea60 |
struct cli_asn1 obj;
unsigned int avail;
int ret;
|
02840644 |
if (0 != (ret = asn1_expect_objtype(map, *asn1data, asn1len, &obj, ASN1_TYPE_SEQUENCE))) { |
0f53ea60 |
cli_dbgmsg("asn1_expect_hash_algo: expected SEQUENCE to start AlgorithmIdentifier\n");
return ret;
} |
288057e9 |
avail = obj.size; |
0f53ea60 |
*asn1data = obj.next; |
02840644 |
if (0 != (ret = asn1_expect_objtype(map, obj.content, &avail, &obj, ASN1_TYPE_OBJECT_ID))) { |
0f53ea60 |
cli_dbgmsg("asn1_expect_hash_algo: unexpected object type inside AlgorithmIdentifier SET\n");
return ret;
} |
b7f6b615 |
/* 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 |
cc9381ae |
* - OID_sha384
* - OID_sha384WithRSAEncryption
* - OID_sha512
* - OID_sha512WithRSAEncryption |
b7f6b615 |
*/ |
288057e9 |
if (obj.size != lenof(OID_sha1) && obj.size != lenof(OID_md5) && obj.size != lenof(OID_sha256)) { |
0f53ea60 |
cli_dbgmsg("asn1_expect_hash_algo: unsupported algorithm OID size for AlgorithmIdentifier\n");
return 1;
} |
288057e9 |
if (!fmap_need_ptr_once(map, obj.content, obj.size)) { |
0f53ea60 |
cli_dbgmsg("asn1_expect_hash_algo: failed to get AlgorithmIdentifier OID\n");
return 1;
} |
288057e9 |
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)))) { |
0f53ea60 |
*hashtype = CLI_SHA1RSA;
*hashsize = SHA1_HASH_SIZE; |
288057e9 |
} 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)))) { |
0f53ea60 |
*hashtype = CLI_MD5RSA;
*hashsize = MD5_HASH_SIZE; |
288057e9 |
} 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)))) { |
0f53ea60 |
*hashtype = CLI_SHA256RSA;
*hashsize = SHA256_HASH_SIZE; |
288057e9 |
} 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)))) { |
cc9381ae |
*hashtype = CLI_SHA384RSA;
*hashsize = SHA384_HASH_SIZE; |
288057e9 |
} 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)))) { |
cc9381ae |
*hashtype = CLI_SHA512RSA;
*hashsize = SHA512_HASH_SIZE; |
0f53ea60 |
} else {
cli_dbgmsg("asn1_expect_hash_algo: unknown digest OID in AlgorithmIdentifier\n");
return 1;
} |
d26a6fd2 |
// 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 |
288057e9 |
if (avail && (ret = asn1_expect_obj(map, &obj.next, &avail, ASN1_TYPE_NULL, 0, NULL))) { |
b7f6b615 |
cli_dbgmsg("asn1_expect_hash_algo: expected NULL after AlgorithmIdentifier OID\n"); |
0f53ea60 |
return ret;
} |
288057e9 |
if (avail) { |
0f53ea60 |
cli_dbgmsg("asn1_expect_hash_algo: extra data in AlgorithmIdentifier\n");
return 1;
}
return 0;
}
|
288057e9 |
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; |
288057e9 |
if ((ret = asn1_expect_objtype(map, *asn1data, asn1len, &obj, ASN1_TYPE_SEQUENCE))) { /* SEQUENCE */ |
e3544d19 |
cli_dbgmsg("asn1_expect_rsa: expecting SEQUENCE at the start of the RSA algo\n"); |
879c54f7 |
return ret; |
e3544d19 |
} |
288057e9 |
avail = obj.size; |
b0357255 |
*asn1data = obj.next;
|
288057e9 |
if (asn1_expect_objtype(map, obj.content, &avail, &obj, ASN1_TYPE_OBJECT_ID)) { |
e3544d19 |
cli_dbgmsg("asn1_expect_rsa: expected OID in RSA algo\n"); |
879c54f7 |
return 1; |
e3544d19 |
} |
c9346ead |
// Two cases to check for:
// obj.size == 5:
// - OID_sha1WithRSA
//
// obj.size == 9: |
e3544d19 |
// - OID_rsaEncryption |
c9346ead |
// - OID_md2WithRSAEncryption
// - OID_md5WithRSAEncryption
// - OID_sha1WithRSAEncryption
// - OID_sha256WithRSAEncryption
// - OID_sha384WithRSAEncryption
// - OID_sha512WithRSAEncryption |
288057e9 |
if (obj.size != lenof(OID_sha1WithRSA) && obj.size != lenof(OID_sha1WithRSAEncryption)) { |
879c54f7 |
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 |
} |
288057e9 |
if (!fmap_need_ptr_once(map, obj.content, obj.size)) { |
879c54f7 |
cli_dbgmsg("asn1_expect_rsa: failed to read OID\n");
return 1; |
b0357255 |
} |
288057e9 |
if (obj.size == lenof(OID_sha1WithRSA)) { |
c9346ead |
|
288057e9 |
if (!memcmp(obj.content, OID_sha1WithRSA, lenof(OID_sha1WithRSA))) { |
c9346ead |
*hashtype = CLI_SHA1RSA; /* Obsolete sha1rsa 1.3.14.3.2.29 */ |
288057e9 |
} else { |
e3544d19 |
cli_dbgmsg("asn1_expect_rsa: unknown OID (length 5)\n");
return 1;
} |
c9346ead |
} else if (obj.size == lenof(OID_sha1WithRSAEncryption)) {
|
288057e9 |
if (!memcmp(obj.content, OID_sha1WithRSAEncryption, lenof(OID_sha1WithRSAEncryption))) |
c9346ead |
*hashtype = CLI_SHA1RSA; /* sha1withRSAEncryption 1.2.840.113549.1.1.5 */
|
288057e9 |
else if (!memcmp(obj.content, OID_md5WithRSAEncryption, lenof(OID_md5WithRSAEncryption))) |
c9346ead |
*hashtype = CLI_MD5RSA; /* md5withRSAEncryption 1.2.840.113549.1.1.4 */
|
288057e9 |
else if (!memcmp(obj.content, OID_rsaEncryption, lenof(OID_rsaEncryption))) |
e3544d19 |
*hashtype = CLI_RSA; /* rsaEncryption 1.2.840.113549.1.1.1 */
|
288057e9 |
else if (!memcmp(obj.content, OID_md2WithRSAEncryption, lenof(OID_md2WithRSAEncryption))) { |
e3544d19 |
*hashtype = CLI_MD2RSA; /* md2withRSAEncryption 1.2.840.113549.1.1.2 */ |
288057e9 |
} else if (!memcmp(obj.content, OID_sha256WithRSAEncryption, lenof(OID_sha256WithRSAEncryption))) { |
c9346ead |
*hashtype = CLI_SHA256RSA; /* sha256WithRSAEncryption 1.2.840.113549.1.1.11 */ |
288057e9 |
} else if (!memcmp(obj.content, OID_sha384WithRSAEncryption, lenof(OID_sha384WithRSAEncryption))) { |
c9346ead |
*hashtype = CLI_SHA384RSA; /* sha384WithRSAEncryption 1.2.840.113549.1.1.12 */ |
288057e9 |
} else if (!memcmp(obj.content, OID_sha512WithRSAEncryption, lenof(OID_sha512WithRSAEncryption))) { |
e3544d19 |
*hashtype = CLI_SHA512RSA; /* sha512WithRSAEncryption 1.2.840.113549.1.1.13 */ |
288057e9 |
} else { |
e3544d19 |
cli_dbgmsg("asn1_expect_rsa: unknown OID (length 9)\n"); |
c9346ead |
return 1;
} |
288057e9 |
} else { |
879c54f7 |
cli_dbgmsg("asn1_expect_rsa: OID mismatch (size %u)\n", obj.size);
return 1; |
b0357255 |
} |
d26a6fd2 |
// 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 |
288057e9 |
if (avail && (ret = asn1_expect_obj(map, &obj.next, &avail, ASN1_TYPE_NULL, 0, NULL))) { /* NULL */ |
e3544d19 |
cli_dbgmsg("asn1_expect_rsa: expected NULL following RSA OID\n"); |
879c54f7 |
return ret; |
e3544d19 |
} |
288057e9 |
if (avail) { |
879c54f7 |
cli_dbgmsg("asn1_expect_rsa: extra data found in SEQUENCE\n");
return 1; |
b0357255 |
}
return 0;
}
|
288057e9 |
static int asn1_getnum(const char *s)
{
if (s[0] < '0' || s[0] > '9' || s[1] < '0' || s[1] > '9') { |
879c54f7 |
cli_dbgmsg("asn1_getnum: expecting digits, found '%c%c'\n", s[0], s[1]);
return -1; |
5b48b665 |
} |
288057e9 |
return (s[0] - '0') * 10 + (s[1] - '0'); |
b28a0d6f |
}
|
288057e9 |
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;
|
288057e9 |
if (ret) |
879c54f7 |
return ret; |
b28a0d6f |
|
288057e9 |
if (obj.type == 0x17) /* UTCTime - YYMMDDHHMMSSZ */ |
879c54f7 |
len = 13; |
288057e9 |
else if (obj.type == 0x18) /* GeneralizedTime - YYYYMMDDHHMMSSZ */ |
879c54f7 |
len = 15; |
5b48b665 |
else { |
879c54f7 |
cli_dbgmsg("asn1_get_time: expected UTCTime or GeneralizedTime, got %02x\n", obj.type);
return 1; |
5b48b665 |
} |
b28a0d6f |
|
288057e9 |
if (!fmap_need_ptr_once(map, obj.content, len)) { |
879c54f7 |
cli_dbgmsg("asn1_get_time: failed to read content\n");
return 1; |
5b48b665 |
} |
b28a0d6f |
memset(&t, 0, sizeof(t));
ptr = (char *)obj.content; |
288057e9 |
if (obj.type == 0x18) { |
879c54f7 |
t.tm_year = asn1_getnum(ptr) * 100; |
288057e9 |
if (t.tm_year < 0) |
879c54f7 |
return 1;
n = asn1_getnum(ptr); |
288057e9 |
if (n < 0) |
879c54f7 |
return 1;
t.tm_year += n; |
288057e9 |
ptr += 4; |
b28a0d6f |
} else { |
879c54f7 |
n = asn1_getnum(ptr); |
288057e9 |
if (n < 0) |
879c54f7 |
return 1; |
288057e9 |
if (n >= 50) |
879c54f7 |
t.tm_year = 1900 + n;
else
t.tm_year = 2000 + n;
ptr += 2; |
b28a0d6f |
} |
f05aa165 |
t.tm_year -= 1900; |
5b48b665 |
n = asn1_getnum(ptr); |
288057e9 |
if (n < 1 || n > 12) { |
879c54f7 |
cli_dbgmsg("asn1_get_time: invalid month %u\n", n);
return 1; |
5b48b665 |
} |
f05aa165 |
t.tm_mon = n - 1; |
288057e9 |
ptr += 2; |
b28a0d6f |
|
5b48b665 |
n = asn1_getnum(ptr); |
288057e9 |
if (n < 1 || n > 31) { |
879c54f7 |
cli_dbgmsg("asn1_get_time: invalid day %u\n", n);
return 1; |
5b48b665 |
} |
b28a0d6f |
t.tm_mday = n; |
288057e9 |
ptr += 2; |
b28a0d6f |
|
5b48b665 |
n = asn1_getnum(ptr); |
288057e9 |
if (n < 0 || n > 23) { |
879c54f7 |
cli_dbgmsg("asn1_get_time: invalid hour %u\n", n);
return 1; |
5b48b665 |
} |
b28a0d6f |
t.tm_hour = n; |
288057e9 |
ptr += 2; |
b28a0d6f |
|
5b48b665 |
n = asn1_getnum(ptr); |
288057e9 |
if (n < 0 || n > 59) { |
879c54f7 |
cli_dbgmsg("asn1_get_time: invalid minute %u\n", n);
return 1; |
5b48b665 |
} |
b28a0d6f |
t.tm_min = n; |
288057e9 |
ptr += 2; |
b28a0d6f |
|
5b48b665 |
n = asn1_getnum(ptr); |
288057e9 |
if (n < 0 || n > 59) { |
879c54f7 |
cli_dbgmsg("asn1_get_time: invalid second %u\n", n);
return 1; |
5b48b665 |
} |
b28a0d6f |
t.tm_sec = n; |
288057e9 |
ptr += 2; |
b28a0d6f |
|
288057e9 |
if (*ptr != 'Z') { |
879c54f7 |
cli_dbgmsg("asn1_get_time: expected UTC time 'Z', got '%c'\n", *ptr);
return 1; |
5b48b665 |
} |
b28a0d6f |
|
288057e9 |
*tm = mktime(&t); |
b28a0d6f |
*asn1data = obj.next;
return 0;
}
|
288057e9 |
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;
|
288057e9 |
if (asn1_expect_objtype(map, *asn1data, size, &obj, ASN1_TYPE_SEQUENCE)) /* subjectPublicKeyInfo */ |
879c54f7 |
return 1; |
b28a0d6f |
*asn1data = obj.next;
avail = obj.size; |
288057e9 |
if (asn1_expect_algo(map, &obj.content, &avail, lenof(OID_rsaEncryption), OID_rsaEncryption)) { /* rsaEncryption */
cli_dbgmsg("asn1_get_rsa_pubkey: AlgorithmIdentifier other than RSA not yet supported\n");
return 1; |
d26a6fd2 |
} |
b28a0d6f |
|
288057e9 |
if (asn1_expect_objtype(map, obj.content, &avail, &obj, ASN1_TYPE_BIT_STRING)) /* BIT STRING - subjectPublicKey */ |
879c54f7 |
return 1; |
288057e9 |
if (avail) { |
879c54f7 |
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 *\/ */ |
879c54f7 |
/* return 1; */ |
b28a0d6f |
|
288057e9 |
if (!fmap_need_ptr_once(map, obj.content, 1)) { |
879c54f7 |
cli_dbgmsg("asn1_get_rsa_pubkey: cannot read public key content\n");
return 1; |
5b48b665 |
} |
288057e9 |
if (((uint8_t *)obj.content)[0] != 0) { /* no byte fragments */ |
879c54f7 |
cli_dbgmsg("asn1_get_rsa_pubkey: unexpected byte frags in public key\n");
return 1; |
5b48b665 |
} |
b28a0d6f |
|
288057e9 |
avail = obj.size - 1; |
b28a0d6f |
obj.content = ((uint8_t *)obj.content) + 1; |
288057e9 |
if (asn1_expect_objtype(map, obj.content, &avail, &obj, ASN1_TYPE_SEQUENCE)) /* SEQUENCE */ |
879c54f7 |
return 1; |
288057e9 |
if (avail) { |
879c54f7 |
cli_dbgmsg("asn1_get_rsa_pubkey: found unexpected extra data in public key content\n");
return 1; |
5b48b665 |
} |
b28a0d6f |
avail = obj.size; |
288057e9 |
if (asn1_expect_objtype(map, obj.content, &avail, &obj, ASN1_TYPE_INTEGER)) /* INTEGER - mod */ |
879c54f7 |
return 1; |
288057e9 |
if (obj.size < 1024 / 8 || obj.size > 4096 / 8 + 1) {
cli_dbgmsg("asn1_get_rsa_pubkey: modulus has got an unsupported length (%u)\n", obj.size * 8); |
879c54f7 |
return 1; |
5b48b665 |
} |
b28a0d6f |
avail2 = obj.size; |
288057e9 |
if (!fmap_need_ptr_once(map, obj.content, avail2)) { |
879c54f7 |
cli_dbgmsg("asn1_get_rsa_pubkey: cannot read n\n");
return 1; |
5b48b665 |
} |
288057e9 |
if (mp_read_unsigned_bin(&x509->n, obj.content, avail2)) { |
879c54f7 |
cli_dbgmsg("asn1_get_rsa_pubkey: cannot convert n to big number\n");
return 1; |
5b48b665 |
} |
b28a0d6f |
|
288057e9 |
if (asn1_expect_objtype(map, obj.next, &avail, &obj, ASN1_TYPE_INTEGER)) /* INTEGER - exp */ |
879c54f7 |
return 1; |
288057e9 |
if (avail) { |
879c54f7 |
cli_dbgmsg("asn1_get_rsa_pubkey: found unexpected extra data after exp\n");
return 1; |
5b48b665 |
} |
288057e9 |
if (obj.size < 1 || obj.size > avail2) {
cli_dbgmsg("asn1_get_rsa_pubkey: exponent has got an unsupported length (%u)\n", obj.size * 8); |
879c54f7 |
return 1; |
5b48b665 |
} |
288057e9 |
if (!fmap_need_ptr_once(map, obj.content, obj.size)) { |
879c54f7 |
cli_dbgmsg("asn1_get_rsa_pubkey: cannot read e\n");
return 1; |
5b48b665 |
} |
288057e9 |
if (mp_read_unsigned_bin(&x509->e, obj.content, obj.size)) { |
879c54f7 |
cli_dbgmsg("asn1_get_rsa_pubkey: cannot convert e to big number\n");
return 1; |
5b48b665 |
} |
b28a0d6f |
return 0;
}
|
18869acf |
#define ASN1_GET_X509_SUCCESS 0
#define ASN1_GET_X509_CERT_ERROR 1
#define ASN1_GET_X509_UNRECOVERABLE_ERROR 2
|
18aed36f |
/* Parse the asn1data associated with an x509 certificate and add the cert |
39f735b8 |
* to the crtmgr certs if it doesn't already exist there. |
18aed36f |
* 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. */ |
288057e9 |
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; |
18869acf |
int ret = ASN1_GET_X509_UNRECOVERABLE_ERROR; |
c6c71ef7 |
unsigned int version; |
7bcfb2f3 |
|
288057e9 |
if (cli_crt_init(&x509)) |
18869acf |
return ret; |
7bcfb2f3 |
|
a616029e |
do { |
288057e9 |
if (asn1_expect_objtype(map, *asn1data, size, &crt, ASN1_TYPE_SEQUENCE)) { /* SEQUENCE */ |
c9346ead |
cli_dbgmsg("asn1_get_x509: expected SEQUENCE at the x509 start\n"); |
879c54f7 |
break; |
c9346ead |
} |
879c54f7 |
*asn1data = crt.next;
|
18869acf |
/* 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;
|
879c54f7 |
tbsdata = crt.content; |
288057e9 |
if (asn1_expect_objtype(map, crt.content, &crt.size, &tbs, ASN1_TYPE_SEQUENCE)) { /* SEQUENCE - TBSCertificate */ |
c9346ead |
cli_dbgmsg("asn1_get_x509: expected SEQUENCE at the TBSCertificate start\n"); |
879c54f7 |
break; |
c9346ead |
} |
879c54f7 |
tbssize = (uint8_t *)tbs.next - tbsdata;
|
c6c71ef7 |
/* 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"); |
879c54f7 |
break;
} |
288057e9 |
if (0xa0 == obj.type) { /* [0] */ |
c6c71ef7 |
avail = obj.size; |
288057e9 |
next = obj.next; |
c6c71ef7 |
// TODO Should we support v2 certs? Supposedly they are not widely used... |
288057e9 |
if (asn1_expect_obj(map, &obj.content, &avail, ASN1_TYPE_INTEGER, 1, "\x02")) { /* version 3 only (indicated by '\x02')*/ |
c6c71ef7 |
cli_dbgmsg("asn1_get_x509: unexpected type or value for TBSCertificate version\n");
break;
} |
288057e9 |
if (avail) { |
c6c71ef7 |
cli_dbgmsg("asn1_get_x509: found unexpected extra data in version\n");
break;
}
version = 3; |
879c54f7 |
|
288057e9 |
if (asn1_expect_objtype(map, next, &tbs.size, &obj, ASN1_TYPE_INTEGER)) { /* serialNumber */ |
c6c71ef7 |
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"); |
879c54f7 |
break; |
c9346ead |
} |
c6c71ef7 |
|
288057e9 |
if (map_raw(map, obj.content, obj.size, x509.raw_serial)) |
879c54f7 |
break; |
288057e9 |
if (map_sha1(map, obj.content, obj.size, x509.serial)) |
879c54f7 |
break;
|
288057e9 |
if (asn1_expect_rsa(map, &obj.next, &tbs.size, &hashtype1)) { /* algo - Ex: sha1WithRSAEncryption */ |
c9346ead |
cli_dbgmsg("asn1_get_x509: unable to parse AlgorithmIdentifier\n"); |
879c54f7 |
break; |
c9346ead |
} |
879c54f7 |
|
288057e9 |
if (asn1_expect_objtype(map, obj.next, &tbs.size, &obj, ASN1_TYPE_SEQUENCE)) { /* issuer */ |
18aed36f |
cli_dbgmsg("asn1_get_x509: expected SEQUENCE when parsing cert issuer\n"); |
879c54f7 |
break; |
18aed36f |
} |
288057e9 |
issuer = obj.content; |
879c54f7 |
issuersize = obj.size;
|
288057e9 |
if (asn1_expect_objtype(map, obj.next, &tbs.size, &obj, ASN1_TYPE_SEQUENCE)) { /* validity */ |
18aed36f |
cli_dbgmsg("asn1_get_x509: expected SEQUENCE when parsing cert validity\n"); |
879c54f7 |
break; |
18aed36f |
} |
879c54f7 |
avail = obj.size; |
288057e9 |
next = obj.content; |
879c54f7 |
|
288057e9 |
if (asn1_get_time(map, &next, &avail, &x509.not_before)) { /* notBefore */ |
18aed36f |
cli_dbgmsg("asn1_get_x509: unable to extract the notBefore time\n"); |
879c54f7 |
break; |
18aed36f |
} |
288057e9 |
if (asn1_get_time(map, &next, &avail, &x509.not_after)) { /* notAfter */ |
18aed36f |
cli_dbgmsg("asn1_get_x509: unable to extract the notAfter time\n"); |
879c54f7 |
break; |
18aed36f |
} |
288057e9 |
if (x509.not_before >= x509.not_after) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: bad validity\n");
break;
} |
288057e9 |
if (avail) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: found unexpected extra data in validity\n");
break;
}
|
288057e9 |
if (asn1_expect_objtype(map, obj.next, &tbs.size, &obj, ASN1_TYPE_SEQUENCE)) { /* subject */ |
18aed36f |
cli_dbgmsg("asn1_get_x509: expected SEQUENCE when parsing cert subject\n"); |
879c54f7 |
break; |
18aed36f |
} |
288057e9 |
if (map_raw(map, obj.content, obj.size, x509.raw_subject)) |
879c54f7 |
break; |
288057e9 |
if (map_sha1(map, obj.content, obj.size, x509.subject)) |
879c54f7 |
break; |
288057e9 |
if (asn1_get_rsa_pubkey(map, &obj.next, &tbs.size, &x509)) { /* subjectPublicKeyInfo */ |
d26a6fd2 |
cli_dbgmsg("asn1_get_x509: failed to get RSA public key\n"); |
c6c71ef7 |
break; |
d26a6fd2 |
} |
c6c71ef7 |
if (1 == version && tbs.size) {
cli_dbgmsg("asn1_get_x509: TBSCertificate should not contain fields beyond subjectPublicKeyInfo if version == 1\n"); |
879c54f7 |
break; |
c6c71ef7 |
} |
879c54f7 |
avail = 0; |
288057e9 |
while (tbs.size) {
if (asn1_get_obj(map, obj.next, &tbs.size, &obj)) { |
879c54f7 |
tbs.size = 1;
break;
} |
288057e9 |
if (obj.type <= 0xa0 + avail || obj.type > 0xa3) { |
879c54f7 |
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; |
288057e9 |
if (obj.type == 0xa3) { |
879c54f7 |
struct cli_asn1 exts; |
7c55d4f6 |
int have_key_usage = 0; |
288057e9 |
int have_ext_key = 0;
if (asn1_expect_objtype(map, obj.content, &obj.size, &exts, ASN1_TYPE_SEQUENCE)) { |
879c54f7 |
tbs.size = 1;
break;
} |
288057e9 |
if (obj.size) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: found unexpected extra data in extensions\n");
break;
} |
288057e9 |
while (exts.size) { |
879c54f7 |
struct cli_asn1 ext, id, value; |
288057e9 |
if (asn1_expect_objtype(map, exts.content, &exts.size, &ext, ASN1_TYPE_SEQUENCE)) { |
879c54f7 |
exts.size = 1;
break;
}
exts.content = ext.next; |
288057e9 |
if (asn1_expect_objtype(map, ext.content, &ext.size, &id, ASN1_TYPE_OBJECT_ID)) { |
879c54f7 |
exts.size = 1;
break;
} |
288057e9 |
if (asn1_get_obj(map, id.next, &ext.size, &value)) { |
879c54f7 |
exts.size = 1;
break;
} |
288057e9 |
if (value.type == ASN1_TYPE_BOOLEAN) { |
879c54f7 |
/* critical flag */ |
288057e9 |
if (value.size != 1) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: found boolean with wrong length\n");
exts.size = 1;
break;
} |
288057e9 |
if (asn1_get_obj(map, value.next, &ext.size, &value)) { |
879c54f7 |
exts.size = 1;
break;
}
} |
288057e9 |
if (value.type != ASN1_TYPE_OCTET_STRING) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: bad extension value type %u\n", value.type);
exts.size = 1;
break;
} |
288057e9 |
if (ext.size) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: extra data in extension\n");
exts.size = 1;
break;
} |
288057e9 |
if (id.size != 3) |
879c54f7 |
continue;
|
288057e9 |
if (!fmap_need_ptr_once(map, id.content, 3)) { |
879c54f7 |
exts.size = 1;
break;
} |
288057e9 |
if (!memcmp("\x55\x1d\x0f", id.content, 3)) { |
879c54f7 |
/* KeyUsage 2.5.29.15 */
const uint8_t *keyusage = value.content;
uint8_t usage; |
7c55d4f6 |
have_key_usage = 1; |
288057e9 |
if (value.size < 4 || value.size > 5) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: bad KeyUsage\n");
exts.size = 1;
break;
} |
288057e9 |
if (!fmap_need_ptr_once(map, value.content, value.size)) { |
879c54f7 |
exts.size = 1;
break;
} |
288057e9 |
if (keyusage[0] != 0x03 || keyusage[1] != value.size - 2 || keyusage[2] > 7) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: bad KeyUsage\n");
exts.size = 1;
break;
}
usage = keyusage[3]; |
288057e9 |
if (value.size == 4)
usage &= ~((1 << keyusage[2]) - 1); |
879c54f7 |
x509.certSign = ((usage & 4) != 0);
continue;
} |
288057e9 |
if (!memcmp("\x55\x1d\x25", id.content, 3)) { |
879c54f7 |
/* ExtKeyUsage 2.5.29.37 */
struct cli_asn1 keypurp;
have_ext_key = 1; |
288057e9 |
if (asn1_expect_objtype(map, value.content, &value.size, &keypurp, ASN1_TYPE_SEQUENCE)) { |
879c54f7 |
exts.size = 1;
break;
} |
288057e9 |
if (value.size) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: extra data in ExtKeyUsage\n");
exts.size = 1;
break;
}
ext.next = keypurp.content; |
288057e9 |
while (keypurp.size) {
if (asn1_expect_objtype(map, ext.next, &keypurp.size, &ext, ASN1_TYPE_OBJECT_ID)) { |
879c54f7 |
exts.size = 1;
break;
} |
288057e9 |
if (ext.size != 8 && ext.size != 10) |
879c54f7 |
continue; |
288057e9 |
if (!fmap_need_ptr_once(map, ext.content, ext.size)) { |
879c54f7 |
exts.size = 1;
break;
} |
288057e9 |
if (!memcmp("\x2b\x06\x01\x05\x05\x07\x03\x03", ext.content, 8)) /* id_kp_codeSigning */ |
879c54f7 |
x509.codeSign = 1; |
288057e9 |
else if (!memcmp("\x2b\x06\x01\x05\x05\x07\x03\x08", ext.content, 8)) /* id_kp_timeStamping */ |
879c54f7 |
x509.timeSign = 1; |
288057e9 |
else if (!memcmp("\x2b\x06\x01\x04\x01\x82\x37\x0a\x03\x0d", ext.content, 10)) /* id_kp_lifetimeSigning */ |
c6c71ef7 |
cli_dbgmsg("asn1_get_x509: lifetime signing specified but enforcing this is not currently supported\n"); |
879c54f7 |
}
continue;
} |
288057e9 |
if (!memcmp("\x55\x1d\x13", id.content, 3)) { |
879c54f7 |
/* Basic Constraints 2.5.29.19 */
struct cli_asn1 constr; |
288057e9 |
if (asn1_expect_objtype(map, value.content, &value.size, &constr, ASN1_TYPE_SEQUENCE)) { |
879c54f7 |
exts.size = 1;
break;
} |
288057e9 |
if (!constr.size) |
879c54f7 |
x509.certSign = 0;
else { |
288057e9 |
if (asn1_expect_objtype(map, constr.content, &constr.size, &ext, ASN1_TYPE_BOOLEAN)) { |
879c54f7 |
exts.size = 1;
break;
} |
288057e9 |
if (ext.size != 1) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: wrong bool size in basic constraint %u\n", ext.size);
exts.size = 1;
break;
} |
288057e9 |
if (!fmap_need_ptr_once(map, ext.content, 1)) { |
879c54f7 |
exts.size = 1;
break;
}
x509.certSign = (((uint8_t *)(ext.content))[0] != 0);
}
}
} |
288057e9 |
if (exts.size) { |
879c54f7 |
tbs.size = 1;
break;
} |
7c55d4f6 |
/* 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? */ |
288057e9 |
if (!have_ext_key) |
879c54f7 |
x509.codeSign = x509.timeSign = 1; |
7c55d4f6 |
/* 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 */ |
288057e9 |
if (!have_key_usage) |
7c55d4f6 |
x509.certSign = 1; |
879c54f7 |
}
} |
288057e9 |
if (tbs.size) { |
18aed36f |
cli_dbgmsg("asn1_get_x509: An error occurred when parsing x509 extensions\n"); |
879c54f7 |
break; |
18aed36f |
} |
879c54f7 |
|
a493156d |
if (!x509.certSign && !x509.codeSign && !x509.timeSign) {
cli_dbgmsg("asn1_get_x509: encountered a certificate with no cert, code, or time signing capabilities\n");
}
|
288057e9 |
if (map_raw(map, issuer, issuersize, x509.raw_issuer)) |
879c54f7 |
break; |
288057e9 |
if (map_sha1(map, issuer, issuersize, x509.issuer)) |
879c54f7 |
break;
|
288057e9 |
if (asn1_expect_rsa(map, &tbs.next, &crt.size, &hashtype2)) /* signature algo - Ex: sha1WithRSAEncryption */ |
879c54f7 |
break;
|
288057e9 |
if (hashtype1 != hashtype2) { |
18aed36f |
cli_dbgmsg("asn1_get_x509: found conflicting RSA hash types\n"); |
879c54f7 |
break;
}
x509.hashtype = hashtype1;
|
ab3fe575 |
if (crtmgr_lookup(crts, &x509)) {
cli_dbgmsg("asn1_get_x509: duplicate embedded certificates detected\n");
cli_crt_clear(&x509);
return ASN1_GET_X509_SUCCESS;
}
|
288057e9 |
if (asn1_expect_objtype(map, tbs.next, &crt.size, &obj, ASN1_TYPE_BIT_STRING)) { /* signature */ |
18aed36f |
cli_dbgmsg("asn1_get_x509: Failed to parse x509 signature BIT STRING\n"); |
879c54f7 |
break; |
18aed36f |
} |
288057e9 |
if (obj.size > 513) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: signature too long\n");
break;
} |
288057e9 |
if (!fmap_need_ptr_once(map, obj.content, obj.size)) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: cannot read signature\n");
break;
} |
288057e9 |
if (mp_read_unsigned_bin(&x509.sig, obj.content, obj.size)) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: cannot convert signature to big number\n");
break;
} |
288057e9 |
if (crt.size) { |
879c54f7 |
cli_dbgmsg("asn1_get_x509: found unexpected extra data in signature\n");
break;
}
|
288057e9 |
if (map_hash(map, tbsdata, tbssize, x509.tbshash, x509.hashtype)) { |
18aed36f |
cli_dbgmsg("asn1_get_x509: Unsupported hashtype or hash computation failed\n"); |
879c54f7 |
break; |
18aed36f |
} |
879c54f7 |
|
288057e9 |
if (crtmgr_add(crts, &x509)) |
879c54f7 |
break;
cli_crt_clear(&x509); |
18869acf |
return ASN1_GET_X509_SUCCESS; |
288057e9 |
} while (0); |
7bcfb2f3 |
cli_crt_clear(&x509); |
18869acf |
return ret; |
b28a0d6f |
}
|
288057e9 |
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)
{ |
6b9e6a43 |
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; |
0f53ea60 |
uint8_t hash[MAX_HASH_SIZE]; |
6b9e6a43 |
cli_crt_hashtype hashtype; |
d26a6fd2 |
cli_crt_hashtype hashtype2; |
6b9e6a43 |
unsigned int hashsize; |
0f53ea60 |
uint8_t md[MAX_HASH_SIZE]; |
6b9e6a43 |
int result;
void *ctx;
do { |
288057e9 |
if (asn1_expect_objtype(map, *asn1data, size, &asn1, ASN1_TYPE_SEQUENCE)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: expected SEQUENCE inside counterSignature SET\n"); |
6b9e6a43 |
break;
}
avail = asn1.size; |
72771d9c |
if (asn1_expect_objtype(map, asn1.content, &avail, &deep, ASN1_TYPE_INTEGER)) {
cli_dbgmsg("asn1_parse_countersignature: expected INTEGER for counterSignature version");
break;
}
|
288057e9 |
if (deep.size != 1) { |
72771d9c |
cli_dbgmsg("asn1_parse_countersignature: expected INTEGER of size 1, got size %u\n", deep.size);
break;
}
|
288057e9 |
if (!fmap_need_ptr_once(map, deep.content, 1)) { |
72771d9c |
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 */ |
288057e9 |
if (memcmp(deep.content, "\x01", 1) && memcmp(deep.content, "\x00", 1)) { |
72771d9c |
cli_dbgmsg("asn1_parse_countersignature: counterSignature version is not 1 or 0\n"); |
6b9e6a43 |
break;
} |
72771d9c |
asn1.content = deep.next; |
6b9e6a43 |
|
288057e9 |
if (asn1_expect_objtype(map, asn1.content, &avail, &asn1, ASN1_TYPE_SEQUENCE)) { /* issuerAndSerialNumber */ |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: unable to parse issuerAndSerialNumber SEQUENCE in counterSignature\n"); |
6b9e6a43 |
break;
}
|
288057e9 |
if (asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, ASN1_TYPE_SEQUENCE)) { /* issuer */ |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: unable to parse issuer SEQUENCE in counterSignature\n"); |
6b9e6a43 |
break;
}
// Compute the hash of the issuer section |
288057e9 |
if (map_sha1(map, deep.content, deep.size, issuer)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: error in call to map_sha1 for counterSignature issuer\n"); |
6b9e6a43 |
break;
}
|
288057e9 |
if (asn1_expect_objtype(map, deep.next, &asn1.size, &deep, ASN1_TYPE_INTEGER)) { /* serial */ |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: expected ASN1_TYPE_INTEGER serial for counterSignature\n"); |
6b9e6a43 |
break;
}
// Compute the hash of the serial INTEGER |
288057e9 |
if (map_sha1(map, deep.content, deep.size, serial)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: error in call to map_sha1 for counterSignature serial\n"); |
6b9e6a43 |
break;
}
|
288057e9 |
if (asn1.size) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: extra data inside counterSignature issuer\n"); |
6b9e6a43 |
break;
}
|
288057e9 |
if (asn1_expect_hash_algo(map, &asn1.next, &avail, &hashtype, &hashsize)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: error parsing counterSignature digestAlgorithm\n"); |
6b9e6a43 |
break;
} |
0f53ea60 |
|
288057e9 |
if (map_hash(map, message, message_size, md, hashtype)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: failed to map in message/compute countersignature hash\n"); |
6b9e6a43 |
break;
}
attrs = asn1.next; |
288057e9 |
if (asn1_expect_objtype(map, asn1.next, &avail, &asn1, 0xa0)) { /* authenticatedAttributes */ |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: unable to parse counterSignature authenticatedAttributes section\n"); |
6b9e6a43 |
break;
}
attrs_size = (uint8_t *)(asn1.next) - attrs; |
288057e9 |
if (asn1.next == NULL && attrs_size < 2) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: counterSignature authenticatedAttributes are too small\n"); |
6b9e6a43 |
break;
} |
288057e9 |
result = 0;
dsize = asn1.size; |
6b9e6a43 |
deep.next = asn1.content; |
288057e9 |
while (dsize) { |
6b9e6a43 |
int content; |
288057e9 |
if (asn1_expect_objtype(map, deep.next, &dsize, &deep, ASN1_TYPE_SEQUENCE)) { /* attribute */ |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: expected counterSignature attribute SEQUENCE\n"); |
6b9e6a43 |
dsize = 1;
break;
} |
288057e9 |
if (asn1_expect_objtype(map, deep.content, &deep.size, &deeper, ASN1_TYPE_OBJECT_ID)) { /* attribute type */ |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: expected attribute type inside counterSignature attribute SEQUENCE\n"); |
6b9e6a43 |
dsize = 1;
break;
} |
288057e9 |
if (deeper.size != lenof(OID_contentType)) /* lenof(contentType) = lenof(messageDigest) = lenof(signingTime) = 9 */ |
6b9e6a43 |
continue;
|
288057e9 |
if (!fmap_need_ptr_once(map, deeper.content, lenof(OID_contentType))) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: failed to read counterSignature authenticated attribute\n"); |
6b9e6a43 |
dsize = 1;
break;
} |
288057e9 |
if (!memcmp(deeper.content, OID_contentType, lenof(OID_contentType))) |
6b9e6a43 |
content = 0; /* contentType */ |
288057e9 |
else if (!memcmp(deeper.content, OID_messageDigest, lenof(OID_messageDigest))) |
6b9e6a43 |
content = 1; /* messageDigest */ |
288057e9 |
else if (!memcmp(deeper.content, OID_signingTime, lenof(OID_signingTime))) |
6b9e6a43 |
content = 2; /* signingTime */
else
continue; |
288057e9 |
if (result & (1 << content)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: duplicate field in countersignature\n"); |
6b9e6a43 |
dsize = 1;
break;
} |
288057e9 |
result |= (1 << content);
if (asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, ASN1_TYPE_SET)) { /* set - contents */ |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: failed to read counterSignature authenticated attribute\n"); |
6b9e6a43 |
dsize = 1;
break;
} |
288057e9 |
if (deep.size) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: extra data in countersignature value\n"); |
6b9e6a43 |
dsize = 1;
break;
}
deep.size = deeper.size; |
288057e9 |
switch (content) {
case 0: { /* contentType = pkcs7-data */
const void *backupPtr = deeper.content;
unsigned int backupSize = deep.size;
if (asn1_expect_obj(map, &deeper.content, &deep.size, ASN1_TYPE_OBJECT_ID, lenof(OID_pkcs7_data), OID_pkcs7_data)) {
cli_dbgmsg("asn1_parse_countersignature: contentType != pkcs7-data, checking for timestampToken instead\n");
/* Some signatures use OID_timestampToken instead, so allow |
5f11b088 |
* that also (despite the 2008 spec saying that this value
* must be pkcs7-data) */ |
288057e9 |
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;
} |
5f11b088 |
}
|
288057e9 |
if (deep.size)
cli_dbgmsg("asn1_parse_countersignature: extra data in countersignature content-type\n");
break;
}
case 1: /* messageDigest */
if (asn1_expect_obj(map, &deeper.content, &deep.size, ASN1_TYPE_OCTET_STRING, hashsize, md)) {
deep.size = 1;
cli_dbgmsg("asn1_parse_countersignature: countersignature hash mismatch\n");
} else if (deep.size)
cli_dbgmsg("asn1_parse_countersignature: extra data in countersignature message-digest\n");
break;
case 2: /* signingTime */ |
6b9e6a43 |
{
time_t sigdate; /* FIXME shall i use it?! */ |
288057e9 |
if (asn1_get_time(map, &deeper.content, &deep.size, &sigdate)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: an error occurred when getting the time\n"); |
6b9e6a43 |
deep.size = 1; |
288057e9 |
} else if (deep.size) |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: extra data in countersignature signing-time\n"); |
288057e9 |
else if (sigdate < not_before || sigdate > not_after) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: countersignature timestamp outside cert validity\n"); |
6b9e6a43 |
deep.size = 1;
}
break;
}
} |
288057e9 |
if (deep.size) { |
6b9e6a43 |
dsize = 1;
break;
}
} |
288057e9 |
if (dsize) |
6b9e6a43 |
break; |
288057e9 |
if (result != 7) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: some important attributes are missing in countersignature\n"); |
6b9e6a43 |
break;
}
|
d26a6fd2 |
// 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"); |
6b9e6a43 |
break;
} |
d26a6fd2 |
if (hashtype2 != CLI_RSA && hashtype2 != hashtype) {
cli_dbgmsg("asn1_parse_countersignature: digestEncryptionAlgorithm conflicts with digestAlgorithm\n"); |
6b9e6a43 |
break;
}
|
288057e9 |
if (asn1_expect_objtype(map, asn1.next, &avail, &asn1, ASN1_TYPE_OCTET_STRING)) { /* encryptedDigest */ |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: unexpected encryptedDigest value in counterSignature\n"); |
6b9e6a43 |
break;
} |
288057e9 |
if (asn1.size > 513) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: countersignature encryptedDigest too long\n"); |
6b9e6a43 |
break;
} |
288057e9 |
if (avail) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: extra data inside countersignature\n"); |
6b9e6a43 |
break;
} |
288057e9 |
if (!fmap_need_ptr_once(map, attrs, attrs_size)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: failed to read authenticatedAttributes\n"); |
6b9e6a43 |
break;
}
|
125360a7 |
if (NULL == (ctx = get_hash_ctx(hashtype))) { |
6b9e6a43 |
break;
}
cl_update_hash(ctx, "\x31", 1);
cl_update_hash(ctx, (void *)(attrs + 1), attrs_size - 1);
cl_finish_hash(ctx, hash);
|
288057e9 |
if (!fmap_need_ptr_once(map, asn1.content, asn1.size)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: failed to read countersignature encryptedDigest\n"); |
6b9e6a43 |
break;
} |
288057e9 |
if (!crtmgr_verify_pkcs7(cmgr, issuer, serial, asn1.content, asn1.size, hashtype, hash, VRFY_TIME)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: pkcs7 countersignature verification failed\n"); |
6b9e6a43 |
break;
}
|
0f53ea60 |
cli_dbgmsg("asn1_parse_countersignature: countersignature verification completed successfully\n"); |
6b9e6a43 |
return 0;
|
288057e9 |
} while (0); |
6b9e6a43 |
return 1;
}
|
d92c0129 |
static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, crtmgr *cmgr, int embedded, const void **hashes, unsigned int *hashes_size, cli_ctx *ctx) |
288057e9 |
{ |
871afd19 |
struct cli_asn1 asn1, deep, deeper; |
0d23434a |
uint8_t issuer[SHA1_HASH_SIZE], serial[SHA1_HASH_SIZE]; |
6b9e6a43 |
const uint8_t *message, *attrs;
unsigned int dsize, message_size, attrs_size; |
0d23434a |
// hash is used to hold the hashes we compute as part of sig verification |
0f53ea60 |
uint8_t hash[MAX_HASH_SIZE];
cli_crt_hashtype hashtype, hashtype2; |
e8c03d81 |
unsigned int hashsize; |
0d23434a |
// md is used to hold the message digest we extract from the signature |
0f53ea60 |
uint8_t md[MAX_HASH_SIZE]; |
f5092717 |
cli_crt *x509; |
d92c0129 |
void *hash_ctx; |
81eb1d29 |
int result; |
b9c3525b |
cl_error_t ret = CL_EPARSE; |
5b48b665 |
|
a8a99142 |
cli_dbgmsg("in asn1_parse_mscat\n");
|
5b48b665 |
do { |
288057e9 |
if (!(message = fmap_need_off_once(map, offset, 1))) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: failed to read pkcs#7 entry\n");
break;
}
|
288057e9 |
if (asn1_expect_objtype(map, message, &size, &asn1, ASN1_TYPE_SEQUENCE)) { /* SEQUENCE */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected SEQUENCE at top level\n"); |
879c54f7 |
break; |
e8c03d81 |
}
|
0d23434a |
// Many signatures have zero bytes at the end (padding?) |
879c54f7 |
/* if(size) { */
/* cli_dbgmsg("asn1_parse_mscat: found extra data after pkcs#7 %u\n", size); */
/* break; */
/* } */
size = asn1.size; |
288057e9 |
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 */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected contentType == signedData\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
288057e9 |
if (asn1_expect_objtype(map, asn1.content, &size, &asn1, 0xa0)) { /* [0] - content */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected '[0] - content' following signedData contentType\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
288057e9 |
if (size) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: found extra data in pkcs#7\n");
break;
}
size = asn1.size; |
288057e9 |
if (asn1_expect_objtype(map, asn1.content, &size, &asn1, ASN1_TYPE_SEQUENCE)) { /* SEQUENCE */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected SEQUENCE inside signedData '[0] - content'\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
288057e9 |
if (size) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: found extra data in signedData\n"); |
cf3138e1 |
break;
} |
6b9e6a43 |
|
879c54f7 |
size = asn1.size; |
288057e9 |
if (asn1_expect_obj(map, &asn1.content, &size, ASN1_TYPE_INTEGER, 1, "\x01")) { /* INTEGER - VERSION 1 */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected 'INTEGER - VERSION 1' for signedData version\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
879c54f7 |
|
288057e9 |
if (asn1_expect_objtype(map, asn1.content, &size, &asn1, ASN1_TYPE_SET)) { /* SET OF DigestAlgorithmIdentifier */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected SET OF DigestAlgorithmIdentifier inside signedData\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
879c54f7 |
|
0d23434a |
// 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. |
0f53ea60 |
if (asn1_expect_hash_algo(map, &asn1.content, &asn1.size, &hashtype, &hashsize)) {
cli_dbgmsg("asn1_parse_mscat: error parsing SignedData digestAlgorithm\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
0d23434a |
if (asn1.size) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_mscat: found extra data in the SignerData digestAlgorithm SET\n"); |
0d23434a |
break;
}
// We've finished parsing the DigestAlgorithmIdentifiers SET, so start
// back parsing the SignedData |
288057e9 |
if (asn1_expect_objtype(map, asn1.next, &size, &asn1, ASN1_TYPE_SEQUENCE)) { /* SEQUENCE - contentInfo */ |
0d23434a |
cli_dbgmsg("asn1_parse_mscat: expected 'SEQUENCE - contentInfo' inside SignedData following DigestAlgorithmIdentifiers\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
0d23434a |
// Parse the contentInfo SEQUENCE. asn1.next and size point to the
// certificates, so these need to be preserved
|
879c54f7 |
/* Here there is either a PKCS #7 ContentType Object Identifier for Certificate Trust List (szOID_CTL)
* or a single SPC_INDIRECT_DATA_OBJID */ |
288057e9 |
if (
(!embedded && asn1_expect_obj(map, &asn1.content, &asn1.size, ASN1_TYPE_OBJECT_ID, lenof(OID_szOID_CTL), OID_szOID_CTL)) ||
(embedded && asn1_expect_obj(map, &asn1.content, &asn1.size, ASN1_TYPE_OBJECT_ID, lenof(OID_SPC_INDIRECT_DATA_OBJID), OID_SPC_INDIRECT_DATA_OBJID))) { |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: unexpected ContentType for embedded mode %d\n", embedded); |
879c54f7 |
break; |
e8c03d81 |
} |
879c54f7 |
|
288057e9 |
if (asn1_expect_objtype(map, asn1.content, &asn1.size, &deep, 0xa0)) { |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected '[0] - content' following DigestAlgorithmIdentifier contentType\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
288057e9 |
if (asn1.size) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: found extra data in contentInfo\n");
break;
}
dsize = deep.size; |
288057e9 |
if (asn1_expect_objtype(map, deep.content, &dsize, &deep, ASN1_TYPE_SEQUENCE)) { |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected SEQUENCE in DigestAlgorithmIdentifier '[0] - contentInfo'\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
288057e9 |
if (dsize) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: found extra data in content\n");
break;
} |
e8c03d81 |
/* |
0d23434a |
* Hashes should look like: |
e8c03d81 |
* 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)
*/
|
288057e9 |
*hashes = deep.content; |
879c54f7 |
*hashes_size = deep.size;
|
0d23434a |
// Now resume parsing SignedData - certificates
|
288057e9 |
if (asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) { /* certificates */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected 0xa0 certificates entry\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
879c54f7 |
dsize = asn1.size; |
288057e9 |
if (dsize) { |
879c54f7 |
crtmgr newcerts;
crtmgr_init(&newcerts); |
288057e9 |
while (dsize) { |
39f735b8 |
result = asn1_get_x509(map, &asn1.content, &dsize, &newcerts); |
288057e9 |
if (ASN1_GET_X509_UNRECOVERABLE_ERROR == result) { |
879c54f7 |
dsize = 1;
break; |
288057e9 |
} else if (ASN1_GET_X509_CERT_ERROR == result) { |
18869acf |
cli_dbgmsg("asn1_parse_mscat: skipping x509 certificate with errors\n");
} |
879c54f7 |
} |
288057e9 |
if (dsize) { |
879c54f7 |
crtmgr_free(&newcerts); |
18869acf |
cli_dbgmsg("asn1_parse_mscat: an unrecoverable error occurred while extracting x509 certificates\n"); |
879c54f7 |
break;
} |
288057e9 |
if (newcerts.crts) { |
879c54f7 |
x509 = newcerts.crts; |
39f735b8 |
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 */ |
288057e9 |
while (x509) {
char raw_issuer[CRT_RAWMAXLEN * 2 + 1], raw_subject[CRT_RAWMAXLEN * 2 + 1], raw_serial[CRT_RAWMAXLEN * 3 + 1];
char issuer[SHA1_HASH_SIZE * 2 + 1], subject[SHA1_HASH_SIZE * 2 + 1], serial[SHA1_HASH_SIZE * 2 + 1];
char mod[1024 + 1], exp[1024 + 1];
int j = 1024;
fp_toradix_n(&x509->n, mod, 16, j + 1);
fp_toradix_n(&x509->e, exp, 16, j + 1);
memset(raw_issuer, 0, CRT_RAWMAXLEN * 2 + 1);
memset(raw_subject, 0, CRT_RAWMAXLEN * 2 + 1);
memset(raw_serial, 0, CRT_RAWMAXLEN * 2 + 1);
for (j = 0; j < x509->raw_issuer[0]; j++)
sprintf(&raw_issuer[j * 2], "%02x", x509->raw_issuer[j + 1]);
for (j = 0; j < x509->raw_subject[0]; j++)
sprintf(&raw_subject[j * 2], "%02x", x509->raw_subject[j + 1]);
for (j = 0; j < x509->raw_serial[0]; j++)
sprintf(&raw_serial[j * 3], "%02x%c", x509->raw_serial[j + 1], (j != x509->raw_serial[0] - 1) ? ':' : '\0');
for (j = 0; j < SHA1_HASH_SIZE; j++) {
sprintf(&issuer[j * 2], "%02x", x509->issuer[j]);
sprintf(&subject[j * 2], "%02x", x509->subject[j]);
sprintf(&serial[j * 2], "%02x", x509->serial[j]); |
879c54f7 |
}
|
1d7f6b27 |
// TODO The raw information we print out here isn't
// very helpful, since it's only the first 64-bytes...
// Change this so that raw is only populated when the
// debug flag is set, and then copy/display the full
// contents. |
edb23158 |
cli_dbgmsg("cert:\n");
cli_dbgmsg(" subject: %s\n", subject);
cli_dbgmsg(" serial: %s\n", serial);
cli_dbgmsg(" pubkey: %s\n", mod);
cli_dbgmsg(" i: %s %lu->%lu %s%s%s\n", issuer, (unsigned long)x509->not_before, (unsigned long)x509->not_after, x509->codeSign ? "code " : "", x509->timeSign ? "time " : "", x509->certSign ? "cert " : "");
cli_dbgmsg(" ==============RAW==============\n");
cli_dbgmsg(" raw_subject: %s\n", raw_subject);
cli_dbgmsg(" raw_serial: %s\n", raw_serial);
cli_dbgmsg(" raw_issuer: %s\n", raw_issuer); |
39f735b8 |
x509 = x509->next;
}
x509 = newcerts.crts;
}
|
a133cd8e |
/* Determine whether the embedded certificate is blacklisted or
* whitelisted. If an embedded cert matches a blacklist rule,
* we can return immediately indicating that a sig matched.
* This isn't true for whitelist matches, since otherwise an
* attacker could just include a known-good certificate in the
* signature and not use it. Instead, for those we will add the
* embedded cert to the trust store and continue on to ensure
* that a trusted cert is used for signing. */
while (x509) {
cli_crt *crt;
/* Use &(engine->cmgr) for this check, since we don't copy
* blacklist certs into cmgr and so that if there's a
* match, we have a long-lived pointer that we can pass |
d92c0129 |
* back (via cli_append_virus) indicating the name of the
* sigs that matched (we can't just malloc new space for
* one, since nothing above here knows to free it.) */ |
a133cd8e |
if (NULL != (crt = crtmgr_blacklist_lookup(&(engine->cmgr), x509))) {
ret = CL_VIRUS; |
d92c0129 |
cli_dbgmsg("asn1_parse_mscat: Found Authenticode certificate blacklisted by %s\n", crt->name ? crt->name : "(unnamed CRB rule)");
if (NULL != ctx) {
ret = cli_append_virus(ctx, crt->name ? crt->name : "(unnamed CRB rule)");
if ((ret == CL_VIRUS) && !SCAN_ALLMATCHES) {
crtmgr_free(&newcerts);
goto finish;
} |
a133cd8e |
} |
d92c0129 |
/* In the case where ctx is NULL, we don't care about
* blacklist matches - we are either using this
* function to parse .cat rules that were loaded in,
* or it's sigtool doing cert printing. */ |
a133cd8e |
}
/* NOTE: Since the 'issuer' cli_crt field is required for
* Authenticode validation, we rely on adding embedded
* certs with the 'issuer' actually set into our trust
* store for doing the time/code digital signature checks.
* This isn't required for cert-signing certs that
* we discover this way, since the CRB cli_crts have enough
* info to be able to whitelist other certs, but executing
* the following code for those has the benefit of removing
* them from newcerts so they aren't processed again while
* looking for chained trust. */
if (NULL != (crt = crtmgr_whitelist_lookup(cmgr, x509, 1))) {
cli_crt *tmp = x509->next;
cli_dbgmsg("asn1_parse_mscat: Directly whitelisting embedded cert based on %s\n", (crt->name ? crt->name : "(no name)")); |
ca417755 |
if (cli_debug_flag && crt->name) {
// Copy the name from the CRB entry for printing below
x509->name = strdup(crt->name);
} |
a133cd8e |
if (crtmgr_add(cmgr, x509)) {
cli_dbgmsg("asn1_parse_mscat: adding x509 cert to crtmgr failed\n");
break;
}
crtmgr_del(&newcerts, x509);
x509 = tmp;
continue;
}
x509 = x509->next;
} |
ca417755 |
if (x509) {
crtmgr_free(&newcerts);
break;
} |
d92c0129 |
/* In the SCAN_ALLMATCHES case, we'd get here with
* ret == CL_VIRUS if a match occurred but we wanted
* to keep looping to look for other matches. In that
* case, bail here. */
if (CL_VIRUS == ret) {
crtmgr_free(&newcerts);
break;
}
|
a133cd8e |
x509 = newcerts.crts;
/* Now look for cases where embedded certs can be trusted
* indirectly because they are signed by trusted certs */ |
288057e9 |
while (x509) { |
39f735b8 |
cli_crt *parent;
/* If the certificate is in the trust store already, remove |
a133cd8e |
* it from the newcerts list. This is legacy code that I'm
* assuming tries to prevent us from doing the expensive
* RSA verification in the case where the same cert is
* embedded multiple times? Sure, why not */
if (crtmgr_whitelist_lookup(cmgr, x509, 0)) { |
39f735b8 |
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; |
879c54f7 |
}
|
39f735b8 |
/* Determine whether the cert is signed by one in our trust |
a133cd8e |
* store */ |
39f735b8 |
parent = crtmgr_verify_crt(cmgr, x509);
|
288057e9 |
if (parent) { |
879c54f7 |
|
a133cd8e |
cli_dbgmsg("asn1_parse_mscat: Indirectly whitelisting embedded cert based on %s\n", (parent->name ? parent->name : "(no name)"));
// TODO Why is this done? It seems like you should be
// able to have a parent cert can only do cert signing
// and have that be able to sign a cert used for
// code-signing... |
879c54f7 |
x509->codeSign &= parent->codeSign;
x509->timeSign &= parent->timeSign; |
18aed36f |
|
288057e9 |
if (crtmgr_add(cmgr, x509)) { |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: adding x509 cert to crtmgr failed\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
879c54f7 |
crtmgr_del(&newcerts, x509); |
39f735b8 |
/* 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. */ |
879c54f7 |
x509 = newcerts.crts;
continue;
} |
39f735b8 |
|
879c54f7 |
x509 = x509->next;
} |
288057e9 |
if (x509) { |
879c54f7 |
crtmgr_free(&newcerts);
break; |
7209997f |
} |
288057e9 |
if (newcerts.items) |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: %u certificates did not verify\n", newcerts.items);
crtmgr_free(&newcerts);
}
}
|
0d23434a |
// Parse the final section in SignedData - SignerInfos |
288057e9 |
if (asn1_get_obj(map, asn1.next, &size, &asn1)) { |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: failed to get next ASN1 section\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
288057e9 |
if (asn1.type == 0xa1 && asn1_get_obj(map, asn1.next, &size, &asn1)) { /* crls - unused shouldn't be present */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: unexpected CRL entries were found\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
288057e9 |
if (asn1.type != ASN1_TYPE_SET) { /* signerInfos */ |
0d23434a |
cli_dbgmsg("asn1_parse_mscat: unexpected type %02x for signerInfo\n", asn1.type); |
879c54f7 |
break;
} |
288057e9 |
if (size) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: unexpected extra data after signerInfos\n");
break;
}
size = asn1.size; |
288057e9 |
if (asn1_expect_objtype(map, asn1.content, &size, &asn1, ASN1_TYPE_SEQUENCE)) { |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected SEQUENCE in signerInfos"); |
879c54f7 |
break; |
e8c03d81 |
} |
288057e9 |
if (size) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: only one signerInfo shall be present\n");
break;
}
size = asn1.size; |
288057e9 |
if (asn1_expect_obj(map, &asn1.content, &size, ASN1_TYPE_INTEGER, 1, "\x01")) { /* Version = 1 */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected Version == 1 for signerInfo\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
288057e9 |
if (asn1_expect_objtype(map, asn1.content, &size, &asn1, ASN1_TYPE_SEQUENCE)) { /* issuerAndSerialNumber */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected issuerAndSerialNumber SEQUENCE\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
0d23434a |
// asn1.next and size must be preserved so we can continue parsing
// SignerInfos, so switch to deep |
879c54f7 |
dsize = asn1.size; |
288057e9 |
if (asn1_expect_objtype(map, asn1.content, &dsize, &deep, ASN1_TYPE_SEQUENCE)) { /* issuer */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected issuer SEQUENCE\n"); |
879c54f7 |
break; |
e8c03d81 |
}
/* 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
*/ |
288057e9 |
if (map_sha1(map, deep.content, deep.size, issuer)) { |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: error in call to map_sha1 for issuer\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
879c54f7 |
|
288057e9 |
if (asn1_expect_objtype(map, deep.next, &dsize, &deep, ASN1_TYPE_INTEGER)) { /* serial */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected ASN1_TYPE_INTEGER serial\n"); |
879c54f7 |
break; |
e8c03d81 |
}
/* 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. */ |
288057e9 |
if (map_sha1(map, deep.content, deep.size, serial)) { |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: error in call to map_sha1 for serial\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
288057e9 |
if (dsize) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: extra data inside issuerAndSerialNumber\n");
break;
} |
0d23434a |
// Resume parsing the SignerInfos using asn1.next and size |
0f53ea60 |
if (asn1_expect_hash_algo(map, &asn1.next, &size, &hashtype2, &hashsize)) {
cli_dbgmsg("asn1_parse_mscat: error parsing SignerInfo digestAlgorithm\n"); |
0d23434a |
break;
}
// Verify that the SignerInfo digestAlgorithm matches the one from the SignedData section |
0f53ea60 |
if (hashtype != hashtype2) { |
288057e9 |
cli_dbgmsg("asn1_parse_mscat: SignerInfo digestAlgorithm is not the same as the algorithm in SignedData\n");
break; |
e8c03d81 |
} |
7209997f |
|
0d23434a |
// Continue on to the authenticatedAttributes section within SignerInfo |
879c54f7 |
attrs = asn1.next; |
288057e9 |
if (asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa0)) { /* authenticatedAttributes */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: unable to parse authenticatedAttributes section\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
879c54f7 |
attrs_size = (uint8_t *)(asn1.next) - attrs; |
288057e9 |
if (asn1.next == NULL || attrs_size < 2) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: authenticatedAttributes size is too small\n");
break;
}
|
288057e9 |
dsize = asn1.size; |
879c54f7 |
deep.next = asn1.content; |
288057e9 |
result = 0;
while (dsize) { |
879c54f7 |
struct cli_asn1 cobj;
int content; |
288057e9 |
if (asn1_expect_objtype(map, deep.next, &dsize, &deep, ASN1_TYPE_SEQUENCE)) { /* attribute */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected attribute SEQUENCE\n"); |
879c54f7 |
dsize = 1;
break;
} |
288057e9 |
if (asn1_expect_objtype(map, deep.content, &deep.size, &deeper, ASN1_TYPE_OBJECT_ID)) { /* attribute type */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected attribute type inside attribute SEQUENCE\n"); |
879c54f7 |
dsize = 1;
break;
} |
288057e9 |
if (deeper.size != lenof(OID_contentType)) |
879c54f7 |
continue; |
288057e9 |
if (!fmap_need_ptr_once(map, deeper.content, lenof(OID_contentType))) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: failed to read authenticated attribute\n");
dsize = 1;
break;
} |
288057e9 |
if (!memcmp(deeper.content, OID_contentType, lenof(OID_contentType))) |
879c54f7 |
content = 0; /* contentType */ |
288057e9 |
else if (!memcmp(deeper.content, OID_messageDigest, lenof(OID_messageDigest))) |
879c54f7 |
content = 1; /* messageDigest */
else
continue; |
288057e9 |
if (asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, ASN1_TYPE_SET)) { /* set - contents */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: expected 'set - contents' for authenticated attribute\n"); |
879c54f7 |
dsize = 1;
break;
} |
288057e9 |
if (deep.size) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: extra data in authenticated attributes\n");
dsize = 1;
break; |
7209997f |
} |
50c5d4b5 |
|
288057e9 |
if (result & (1 << content)) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: contentType or messageDigest appear twice\n");
dsize = 1;
break;
} |
b01b78d1 |
|
288057e9 |
if (content == 0) { /* contentType */
if (
(!embedded && asn1_expect_obj(map, &deeper.content, &deeper.size, ASN1_TYPE_OBJECT_ID, lenof(OID_szOID_CTL), OID_szOID_CTL)) || /* cat file */
(embedded && asn1_expect_obj(map, &deeper.content, &deeper.size, ASN1_TYPE_OBJECT_ID, lenof(OID_SPC_INDIRECT_DATA_OBJID), OID_SPC_INDIRECT_DATA_OBJID)) /* embedded cat */
) { |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: unexpected ContentType for embedded mode %d (for authenticated attribute)\n", embedded); |
879c54f7 |
dsize = 1;
break;
}
result |= 1;
} else { /* messageDigest */ |
288057e9 |
if (asn1_expect_objtype(map, deeper.content, &deeper.size, &cobj, ASN1_TYPE_OCTET_STRING)) { |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: unexpected messageDigest value\n"); |
879c54f7 |
dsize = 1;
break;
} |
288057e9 |
if (cobj.size != hashsize) { |
0d23434a |
cli_dbgmsg("asn1_parse_mscat: messageDigest attribute has the wrong size (%u)\n", cobj.size); |
879c54f7 |
dsize = 1;
break;
} |
288057e9 |
if (!fmap_need_ptr_once(map, cobj.content, hashsize)) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: failed to read authenticated attribute\n");
dsize = 1;
break;
} |
0d23434a |
memcpy(md, cobj.content, hashsize); |
879c54f7 |
result |= 2;
} |
288057e9 |
if (deeper.size) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: extra data in authenticated attribute\n");
dsize = 1; |
b01b78d1 |
break; |
879c54f7 |
}
} |
288057e9 |
if (dsize) |
879c54f7 |
break; |
288057e9 |
if (result != 3) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: contentType or messageDigest are missing\n");
break;
}
|
288057e9 |
if (asn1_expect_algo(map, &asn1.next, &size, lenof(OID_rsaEncryption), OID_rsaEncryption)) { /* digestEncryptionAlgorithm == rsa */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: digestEncryptionAlgorithms other than RSA are not yet supported\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
879c54f7 |
|
288057e9 |
if (asn1_expect_objtype(map, asn1.next, &size, &asn1, ASN1_TYPE_OCTET_STRING)) { /* encryptedDigest */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: unexpected encryptedDigest value\n"); |
879c54f7 |
break; |
e8c03d81 |
}
|
0d23434a |
// TODO Make this a #define with the greatest possible length (SHA512) |
288057e9 |
if (asn1.size > 513) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: encryptedDigest too long\n");
break; |
cf3138e1 |
} |
e8c03d81 |
|
288057e9 |
if (map_hash(map, *hashes, *hashes_size, hash, hashtype)) { |
0f53ea60 |
cli_dbgmsg("asn1_parse_mscat: failed to map in message/compute message digest\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
0d23434a |
|
288057e9 |
if (memcmp(hash, md, hashsize)) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: messageDigest mismatch\n");
break;
}
|
288057e9 |
if (!fmap_need_ptr_once(map, attrs, attrs_size)) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: failed to read authenticatedAttributes\n");
break;
}
|
d92c0129 |
if (NULL == (hash_ctx = get_hash_ctx(hashtype))) { |
0d23434a |
break;
}
|
d92c0129 |
cl_update_hash(hash_ctx, "\x31", 1);
cl_update_hash(hash_ctx, (void *)(attrs + 1), attrs_size - 1);
cl_finish_hash(hash_ctx, hash); |
879c54f7 |
|
288057e9 |
if (!fmap_need_ptr_once(map, asn1.content, asn1.size)) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: failed to read encryptedDigest\n");
break;
} |
e8c03d81 |
// Verify the authenticatedAttributes |
288057e9 |
if (!(x509 = crtmgr_verify_pkcs7(cmgr, issuer, serial, asn1.content, asn1.size, hashtype, hash, VRFY_CODE))) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: pkcs7 signature verification failed\n"); |
b9c3525b |
ret = CL_EVERIFY; |
f077c617 |
break; |
879c54f7 |
} |
288057e9 |
message = asn1.content; |
879c54f7 |
message_size = asn1.size; |
f077c617 |
|
18aed36f |
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). */
|
288057e9 |
if (!size) { |
18aed36f |
time_t now;
// No countersignature, so judge validity based on time
now = time(NULL);
|
288057e9 |
if (now < x509->not_before || now > x509->not_after) { |
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: no countersignature (unauthAttrs missing) and signing certificate has expired\n"); |
b9c3525b |
ret = CL_EVERIFY; |
18aed36f |
break;
}
|
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: no countersignature (unauthAttrs missing) but the signing certificate is still valid\n"); |
b9c3525b |
ret = CL_CLEAN; |
6b9e6a43 |
goto finish; |
879c54f7 |
}
|
288057e9 |
if (size && asn1_expect_objtype(map, asn1.next, &size, &asn1, 0xa1)) { /* unauthenticatedAttributes */ |
e8c03d81 |
cli_dbgmsg("asn1_parse_mscat: unable to find unauthenticatedAttributes section\n"); |
879c54f7 |
break; |
e8c03d81 |
} |
879c54f7 |
|
288057e9 |
if (size) { |
879c54f7 |
cli_dbgmsg("asn1_parse_mscat: extra data inside signerInfo\n");
break;
}
|
6b9e6a43 |
// Parse the unauthenticated attributes |
879c54f7 |
|
288057e9 |
dsize = asn1.size; |
879c54f7 |
deep.next = asn1.content; |
288057e9 |
result = 0;
while (dsize) { |
879c54f7 |
int content; |
288057e9 |
if (asn1_expect_objtype(map, deep.next, &dsize, &deep, ASN1_TYPE_SEQUENCE)) { |
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: expected SEQUENCE starting an unauthenticatedAttribute\n"); |
879c54f7 |
dsize = 1;
break;
} |
288057e9 |
if (asn1_expect_objtype(map, deep.content, &deep.size, &deeper, ASN1_TYPE_OBJECT_ID)) { |
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: expected OID inside unauthenticatedAttribute SEQUENCE\n"); |
879c54f7 |
dsize = 1;
break;
} |
6b9e6a43 |
// 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
|
288057e9 |
if (deeper.size != lenof(OID_countersignature) && deeper.size != lenof(OID_nestedSignatures)) { |
879c54f7 |
continue; |
6b9e6a43 |
} |
879c54f7 |
|
288057e9 |
if (!fmap_need_ptr_once(map, deeper.content, deeper.size)) { |
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: failed to read unauthenticated attribute OID\n"); |
879c54f7 |
dsize = 1;
break;
} |
6b9e6a43 |
|
288057e9 |
if (!memcmp(deeper.content, OID_countersignature, deeper.size)) |
6b9e6a43 |
content = 0; /* counterSignature */ |
288057e9 |
else if (!memcmp(deeper.content, OID_nestedSignatures, deeper.size)) |
6b9e6a43 |
content = 1; /* nested */
else { |
879c54f7 |
continue;
} |
6b9e6a43 |
|
288057e9 |
if (asn1_expect_objtype(map, deeper.next, &deep.size, &deeper, ASN1_TYPE_SET)) { /* set - contents */ |
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: expected 'set - contents' inside unauthenticated attribute\n"); |
879c54f7 |
dsize = 1;
break;
} |
288057e9 |
if (deep.size) { |
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: extra data in unauthenticated attribute\n"); |
879c54f7 |
dsize = 1;
break;
} |
6b9e6a43 |
|
288057e9 |
if (result & (1 << content)) { |
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: counterSignature or nestedSignature appear twice\n"); |
879c54f7 |
dsize = 1;
break;
}
|
288057e9 |
if (content == 0) { /* counterSignature */ |
879c54f7 |
|
288057e9 |
if (asn1_parse_countersignature(map, &deeper.content, &deeper.size, cmgr, message, message_size, x509->not_before, x509->not_after)) { |
6b9e6a43 |
dsize = 1;
break;
} |
879c54f7 |
|
6b9e6a43 |
result |= 1; |
879c54f7 |
|
6b9e6a43 |
} else { /* nestedSignature */ |
e8c03d81 |
|
6b9e6a43 |
// TODO Support parsing these out in the future
cli_dbgmsg("asn1_parse_mscat: nested signatures detected but parsing them is not currently supported\n"); |
e8c03d81 |
|
6b9e6a43 |
deeper.size = 0;
result |= 2;
} |
288057e9 |
if (deeper.size) { |
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: extra data in unauthenticated attribute\n");
dsize = 1;
break;
} |
879c54f7 |
} |
288057e9 |
if (dsize) |
879c54f7 |
break; |
cdd3f2dd |
|
b9c3525b |
cli_dbgmsg("asn1_parse_mscat: unauthenticatedAttributes successfully parsed\n");
|
a26ed932 |
if (1 != (result & 1)) { |
6b9e6a43 |
time_t now; |
cdd3f2dd |
|
6b9e6a43 |
// No countersignature, so judge validity based on time
now = time(NULL); |
cdd3f2dd |
|
288057e9 |
if (now < x509->not_before || now > x509->not_after) { |
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: no countersignature and signing certificate has expired\n"); |
b9c3525b |
ret = CL_EVERIFY; |
cdd3f2dd |
break;
}
|
6b9e6a43 |
cli_dbgmsg("asn1_parse_mscat: no countersignature but the signing certificate is still valid\n"); |
cdd3f2dd |
}
|
b9c3525b |
ret = CL_CLEAN; |
e8c03d81 |
|
288057e9 |
} while (0); |
b0357255 |
|
b9c3525b |
finish:
if (CL_EPARSE == ret) {
cli_dbgmsg("asn1_parse_mscat: failed to parse authenticode section\n");
}
return ret; |
a8a99142 |
} |
3a55c60d |
|
288057e9 |
int asn1_load_mscat(fmap_t *map, struct cl_engine *engine)
{ |
6bc5d0cb |
struct cli_asn1 c; |
0393aa56 |
unsigned int size; |
f0a5895b |
int i; |
6bc5d0cb |
|
b9c3525b |
// 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? |
1d7f6b27 |
if (CL_CLEAN != asn1_parse_mscat(engine, map, 0, map->len, &engine->cmgr, 0, &c.next, &size, NULL)) |
b01b78d1 |
return 1; |
6bc5d0cb |
|
288057e9 |
if (asn1_expect_objtype(map, c.next, &size, &c, ASN1_TYPE_SEQUENCE)) |
879c54f7 |
return 1; |
288057e9 |
if (asn1_expect_obj(map, &c.content, &c.size, ASN1_TYPE_OBJECT_ID, lenof(OID_szOID_CATALOG_LIST), OID_szOID_CATALOG_LIST)) |
879c54f7 |
return 1; |
288057e9 |
if (c.size) { |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: found extra data in szOID_CATALOG_LIST content\n");
return 1; |
6bc5d0cb |
} |
288057e9 |
if (asn1_expect_objtype(map, c.next, &size, &c, 0x4)) /* List ID */ |
879c54f7 |
return 1; |
288057e9 |
if (asn1_expect_objtype(map, c.next, &size, &c, 0x17)) /* Effective date - WTF?! */ |
879c54f7 |
return 1; |
288057e9 |
if (asn1_expect_algo(map, &c.next, &size, lenof(OID_szOID_CATALOG_LIST_MEMBER), OID_szOID_CATALOG_LIST_MEMBER)) /* szOID_CATALOG_LIST_MEMBER */ |
879c54f7 |
return 1; |
288057e9 |
if (asn1_expect_objtype(map, c.next, &size, &c, ASN1_TYPE_SEQUENCE)) /* hashes here */ |
879c54f7 |
return 1; |
28fcc245 |
/* [0] is next but we don't care as it's really descriptives stuff */
|
288057e9 |
size = c.size; |
28fcc245 |
c.next = c.content; |
288057e9 |
while (size) { |
879c54f7 |
struct cli_asn1 tag; |
288057e9 |
if (asn1_expect_objtype(map, c.next, &size, &c, ASN1_TYPE_SEQUENCE)) |
879c54f7 |
return 1; |
288057e9 |
if (asn1_expect_objtype(map, c.content, &c.size, &tag, ASN1_TYPE_OCTET_STRING)) /* TAG NAME */ |
879c54f7 |
return 1; |
288057e9 |
if (asn1_expect_objtype(map, tag.next, &c.size, &tag, ASN1_TYPE_SET)) /* set */ |
879c54f7 |
return 1; |
288057e9 |
if (c.size) { |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: found extra data in tag\n");
return 1;
} |
288057e9 |
while (tag.size) { |
879c54f7 |
struct cli_asn1 tagval1, tagval2, tagval3;
int hashtype;
|
288057e9 |
if (asn1_expect_objtype(map, tag.content, &tag.size, &tagval1, ASN1_TYPE_SEQUENCE)) |
879c54f7 |
return 1;
tag.content = tagval1.next;
|
288057e9 |
if (asn1_expect_objtype(map, tagval1.content, &tagval1.size, &tagval2, ASN1_TYPE_OBJECT_ID)) |
879c54f7 |
return 1; |
288057e9 |
if (tagval2.size != lenof(OID_SPC_INDIRECT_DATA_OBJID)) |
879c54f7 |
continue;
|
288057e9 |
if (!fmap_need_ptr_once(map, tagval2.content, lenof(OID_SPC_INDIRECT_DATA_OBJID))) { |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: cannot read SPC_INDIRECT_DATA\n");
return 1;
} |
288057e9 |
if (memcmp(tagval2.content, OID_SPC_INDIRECT_DATA_OBJID, lenof(OID_SPC_INDIRECT_DATA_OBJID))) |
879c54f7 |
continue; /* stuff like CAT_NAMEVALUE_OBJID(1.3.6.1.4.1.311.12.2.1) and CAT_MEMBERINFO_OBJID(.2).. */
|
288057e9 |
if (asn1_expect_objtype(map, tagval2.next, &tagval1.size, &tagval2, ASN1_TYPE_SET)) |
879c54f7 |
return 1; |
288057e9 |
if (tagval1.size) { |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: found extra data in tag value\n");
return 1;
}
|
288057e9 |
if (asn1_expect_objtype(map, tagval2.content, &tagval2.size, &tagval1, ASN1_TYPE_SEQUENCE)) |
879c54f7 |
return 1; |
288057e9 |
if (tagval2.size) { |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: found extra data in SPC_INDIRECT_DATA_OBJID tag\n");
return 1;
}
|
288057e9 |
if (asn1_expect_objtype(map, tagval1.content, &tagval1.size, &tagval2, ASN1_TYPE_SEQUENCE)) |
879c54f7 |
return 1;
|
288057e9 |
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 */ |
879c54f7 |
return 1; |
288057e9 |
if (tagval3.size != lenof(OID_SPC_PE_IMAGE_DATA_OBJID)) { /* lenof(OID_SPC_PE_IMAGE_DATA_OBJID) = lenof(OID_SPC_CAB_DATA_OBJID) = 10*/ |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: bad hash type size\n");
return 1;
} |
288057e9 |
if (!fmap_need_ptr_once(map, tagval3.content, lenof(OID_SPC_PE_IMAGE_DATA_OBJID))) { |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: cannot read hash type\n");
return 1;
} |
288057e9 |
if (!memcmp(tagval3.content, OID_SPC_PE_IMAGE_DATA_OBJID, lenof(OID_SPC_PE_IMAGE_DATA_OBJID))) |
879c54f7 |
hashtype = 2; |
288057e9 |
else if (!memcmp(tagval3.content, OID_SPC_CAB_DATA_OBJID, lenof(OID_SPC_CAB_DATA_OBJID))) |
879c54f7 |
hashtype = 1;
else {
cli_dbgmsg("asn1_load_mscat: unexpected hash type\n");
return 1;
}
|
288057e9 |
if (asn1_expect_objtype(map, tagval2.next, &tagval1.size, &tagval2, ASN1_TYPE_SEQUENCE)) |
879c54f7 |
return 1; |
288057e9 |
if (tagval1.size) { |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: found extra data after hash\n");
return 1;
}
|
288057e9 |
if (asn1_expect_algo(map, &tagval2.content, &tagval2.size, lenof(OID_sha1), OID_sha1)) { /* objid 1.3.14.3.2.26 - sha1 */ |
d26a6fd2 |
cli_dbgmsg("asn1_load_mscat: currently only SHA1 hashes are supported for .cat file signatures\n"); |
879c54f7 |
return 1; |
d26a6fd2 |
} |
879c54f7 |
|
288057e9 |
if (asn1_expect_objtype(map, tagval2.content, &tagval2.size, &tagval3, ASN1_TYPE_OCTET_STRING)) |
879c54f7 |
return 1; |
288057e9 |
if (tagval2.size) { |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: found extra data in hash\n");
return 1;
} |
288057e9 |
if (tagval3.size != SHA1_HASH_SIZE) { |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: bad hash size %u\n", tagval3.size);
return 1;
} |
288057e9 |
if (!fmap_need_ptr_once(map, tagval3.content, SHA1_HASH_SIZE)) { |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: cannot read hash\n");
return 1;
}
|
288057e9 |
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]); |
879c54f7 |
cli_dbgmsg("asn1_load_mscat: got hash %s (%s)\n", sha1, (hashtype == 2) ? "PE" : "CAB");
} |
288057e9 |
if (!engine->hm_fp) { |
544fa973 |
if (!(engine->hm_fp = MPOOL_CALLOC(engine->mempool, 1, sizeof(*(engine->hm_fp))))) { |
27948a03 |
tag.size = 1; |
879c54f7 |
return 1;
} |
f0a5895b |
#ifdef USE_MPOOL |
879c54f7 |
engine->hm_fp->mempool = engine->mempool; |
f0a5895b |
#endif |
879c54f7 |
} |
1d7f6b27 |
/* Load the trusted hashes into hm_fp, using the size values
* 1 and 2 as sentinel values corresponding to CAB and PE hashes
* from .cat files respectively. */ |
288057e9 |
if (hm_addhash_bin(engine->hm_fp, tagval3.content, CLI_HASH_SHA1, hashtype, NULL)) { |
879c54f7 |
cli_warnmsg("asn1_load_mscat: failed to add hash\n");
return 1;
}
} |
7595e108 |
} |
b01b78d1 |
|
7595e108 |
return 0;
}
|
b9c3525b |
/* Check an embedded PE Authenticode section to determine whether it's trusted. |
1d7f6b27 |
* This will return CL_VERIFIED if the file should be trusted, CL_EPARSE if an |
b9c3525b |
* error occurred while parsing the signature, CL_EVERIFY if parsing was
* successful but there were no whitelist rules for the signature, and |
1d7f6b27 |
* CL_VIRUS if a blacklist rule was found for an embedded certificate.
*
* If CL_VIRUS is returned, certname will be set to the certname of blacklist
* rule that matched (unless certname is NULL). */ |
d92c0129 |
cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, struct cli_mapped_region *regions, uint32_t nregions, cli_ctx *ctx) |
288057e9 |
{ |
7595e108 |
unsigned int content_size;
struct cli_asn1 c; |
0f53ea60 |
cli_crt_hashtype hashtype;
uint8_t hash[MAX_HASH_SIZE];
unsigned int hashsize; |
d1fcd16d |
const void *content; |
7595e108 |
crtmgr certs;
int ret; |
d92c0129 |
void *hash_ctx; |
4d62654c |
unsigned int i; |
7595e108 |
|
059ca614 |
cli_dbgmsg("in asn1_check_mscat (offset: %llu)\n", (long long unsigned)offset); |
7595e108 |
crtmgr_init(&certs); |
a133cd8e |
/* Get a copy of all certs in the trust store, excluding blacklist certs */
if (crtmgr_add_roots(engine, &certs, 1)) { |
879c54f7 |
crtmgr_free(&certs); |
b9c3525b |
return CL_EVERIFY; |
7595e108 |
} |
d92c0129 |
ret = asn1_parse_mscat(engine, map, offset, size, &certs, 1, &content, &content_size, ctx); |
7595e108 |
crtmgr_free(&certs); |
288057e9 |
if (CL_CLEAN != ret) |
b9c3525b |
return ret; |
7595e108 |
|
288057e9 |
if (asn1_expect_objtype(map, content, &content_size, &c, ASN1_TYPE_SEQUENCE)) { |
ae8b411e |
cli_dbgmsg("asn1_check_mscat: expected SEQUENCE at top level of hash container\n"); |
b9c3525b |
return CL_EPARSE; |
ae8b411e |
} |
288057e9 |
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)) { |
ae8b411e |
cli_dbgmsg("asn1_check_mscat: expected spcPEImageData OID in the first hash SEQUENCE\n"); |
b9c3525b |
return CL_EPARSE; |
ae8b411e |
}
// 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
|
288057e9 |
if (asn1_expect_objtype(map, c.next, &content_size, &c, ASN1_TYPE_SEQUENCE)) { |
ae8b411e |
cli_dbgmsg("asn1_check_mscat: expected second hash container object to be a SEQUENCE\n"); |
b9c3525b |
return CL_EPARSE; |
ae8b411e |
} |
288057e9 |
if (content_size) { |
ae8b411e |
cli_dbgmsg("asn1_check_mscat: extra data in hash SEQUENCE\n"); |
b9c3525b |
return CL_EPARSE; |
7595e108 |
} |
ae8b411e |
|
288057e9 |
if (asn1_expect_hash_algo(map, &c.content, &c.size, &hashtype, &hashsize)) { |
0f53ea60 |
cli_dbgmsg("asn1_check_mscat: unexpected file hash algo\n"); |
b9c3525b |
return CL_EPARSE; |
0f53ea60 |
}
|
d92c0129 |
if (NULL == (hash_ctx = get_hash_ctx(hashtype))) { |
b9c3525b |
return CL_EPARSE; |
ae8b411e |
} |
7595e108 |
|
0f53ea60 |
// Now that we know the hash algorithm, compute the authenticode hash
// across the required regions of memory. |
288057e9 |
for (i = 0; i < nregions; i++) { |
b9c3525b |
const uint8_t *hptr;
if (0 == regions[i].size) {
continue;
} |
288057e9 |
if (!(hptr = fmap_need_off_once(map, regions[i].offset, regions[i].size))) { |
b9c3525b |
return CL_EVERIFY; |
1b3395ff |
}
|
d92c0129 |
cl_update_hash(hash_ctx, hptr, regions[i].size); |
0f53ea60 |
}
|
d92c0129 |
cl_finish_hash(hash_ctx, hash); |
0f53ea60 |
|
288057e9 |
if (cli_debug_flag) {
char hashtxt[MAX_HASH_SIZE * 2 + 1];
for (i = 0; i < hashsize; i++)
sprintf(&hashtxt[i * 2], "%02x", hash[i]); |
0f53ea60 |
cli_dbgmsg("Authenticode: %s\n", hashtxt);
}
|
288057e9 |
if (asn1_expect_obj(map, &c.content, &c.size, ASN1_TYPE_OCTET_STRING, hashsize, hash)) { |
ae8b411e |
cli_dbgmsg("asn1_check_mscat: computed authenticode hash did not match stored value\n"); |
b9c3525b |
return CL_EVERIFY; |
ae8b411e |
} |
288057e9 |
if (c.size) { |
ae8b411e |
cli_dbgmsg("asn1_check_mscat: extra data after the stored authenticode hash\n"); |
b9c3525b |
return CL_EPARSE; |
ae8b411e |
} |
7595e108 |
|
7cd9337a |
cli_dbgmsg("asn1_check_mscat: file with valid authenticode signature, whitelisted\n"); |
1d7f6b27 |
return CL_VERIFIED; |
7595e108 |
} |