... | ... |
@@ -695,12 +695,12 @@ static int asn1_get_rsa_pubkey(fmap_t *map, const void **asn1data, unsigned int |
695 | 695 |
#define ASN1_GET_X509_UNRECOVERABLE_ERROR 2 |
696 | 696 |
|
697 | 697 |
/* Parse the asn1data associated with an x509 certificate and add the cert |
698 |
- * to the crtmgr other if it doesn't already exist in master or other. |
|
698 |
+ * to the crtmgr certs if it doesn't already exist there. |
|
699 | 699 |
* ASN1_GET_X509_CERT_ERROR will be returned in the case that an invalid x509 |
700 | 700 |
* certificate is encountered but asn1data and size are suitable for continued |
701 | 701 |
* signature parsing. ASN1_GET_X509_UNRECOVERABLE_ERROR will be returned in |
702 | 702 |
* the case where asn1data and size are not suitable for continued use. */ |
703 |
-static int asn1_get_x509(fmap_t *map, const void **asn1data, unsigned int *size, crtmgr *master, crtmgr *other) { |
|
703 |
+static int asn1_get_x509(fmap_t *map, const void **asn1data, unsigned int *size, crtmgr *crts) { |
|
704 | 704 |
struct cli_asn1 crt, tbs, obj; |
705 | 705 |
unsigned int avail, tbssize, issuersize; |
706 | 706 |
cli_crt_hashtype hashtype1, hashtype2; |
... | ... |
@@ -1029,8 +1029,8 @@ static int asn1_get_x509(fmap_t *map, const void **asn1data, unsigned int *size, |
1029 | 1029 |
} |
1030 | 1030 |
|
1031 | 1031 |
|
1032 |
- if(crtmgr_lookup(master, &x509) || crtmgr_lookup(other, &x509)) { |
|
1033 |
- cli_dbgmsg("asn1_get_x509: certificate already exists\n"); |
|
1032 |
+ if(crtmgr_lookup(crts, &x509)) { |
|
1033 |
+ cli_dbgmsg("asn1_get_x509: duplicate embedded certificates detected\n"); |
|
1034 | 1034 |
cli_crt_clear(&x509); |
1035 | 1035 |
return ASN1_GET_X509_SUCCESS; |
1036 | 1036 |
} |
... | ... |
@@ -1076,7 +1076,7 @@ static int asn1_get_x509(fmap_t *map, const void **asn1data, unsigned int *size, |
1076 | 1076 |
|
1077 | 1077 |
} |
1078 | 1078 |
|
1079 |
- if(crtmgr_add(other, &x509)) |
|
1079 |
+ if(crtmgr_add(crts, &x509)) |
|
1080 | 1080 |
break; |
1081 | 1081 |
cli_crt_clear(&x509); |
1082 | 1082 |
return ASN1_GET_X509_SUCCESS; |
... | ... |
@@ -1510,7 +1510,7 @@ static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmg |
1510 | 1510 |
crtmgr newcerts; |
1511 | 1511 |
crtmgr_init(&newcerts); |
1512 | 1512 |
while(dsize) { |
1513 |
- result = asn1_get_x509(map, &asn1.content, &dsize, cmgr, &newcerts); |
|
1513 |
+ result = asn1_get_x509(map, &asn1.content, &dsize, &newcerts); |
|
1514 | 1514 |
if(ASN1_GET_X509_UNRECOVERABLE_ERROR == result) { |
1515 | 1515 |
dsize = 1; |
1516 | 1516 |
break; |
... | ... |
@@ -1526,12 +1526,10 @@ static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmg |
1526 | 1526 |
} |
1527 | 1527 |
if(newcerts.crts) { |
1528 | 1528 |
x509 = newcerts.crts; |
1529 |
- cli_dbgmsg("asn1_parse_mscat: %u new certificates collected\n", newcerts.items); |
|
1530 |
- while(x509) { |
|
1531 |
- cli_crt *parent = crtmgr_verify_crt(cmgr, x509); |
|
1532 |
- |
|
1533 |
- /* Dump the cert if requested before anything happens to it */ |
|
1534 |
- if (engine->engine_options & ENGINE_OPTIONS_PE_DUMPCERTS) { |
|
1529 |
+ cli_dbgmsg("asn1_parse_mscat: %u embedded certificates collected\n", newcerts.items); |
|
1530 |
+ if (engine->engine_options & ENGINE_OPTIONS_PE_DUMPCERTS) { |
|
1531 |
+ /* Dump the certs if requested before anything happens to them */ |
|
1532 |
+ while(x509) { |
|
1535 | 1533 |
char raw_issuer[CRT_RAWMAXLEN*2+1], raw_subject[CRT_RAWMAXLEN*2+1], raw_serial[CRT_RAWMAXLEN*3+1]; |
1536 | 1534 |
char issuer[SHA1_HASH_SIZE*2+1], subject[SHA1_HASH_SIZE*2+1], serial[SHA1_HASH_SIZE*2+1]; |
1537 | 1535 |
char mod[1024+1], exp[1024+1]; |
... | ... |
@@ -1563,8 +1561,29 @@ static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmg |
1563 | 1563 |
cli_dbgmsg_internal(" raw_subject: %s\n", raw_subject); |
1564 | 1564 |
cli_dbgmsg_internal(" raw_serial: %s\n", raw_serial); |
1565 | 1565 |
cli_dbgmsg_internal(" raw_issuer: %s\n", raw_issuer); |
1566 |
+ |
|
1567 |
+ x509 = x509->next; |
|
1568 |
+ } |
|
1569 |
+ x509 = newcerts.crts; |
|
1570 |
+ } |
|
1571 |
+ |
|
1572 |
+ while(x509) { |
|
1573 |
+ cli_crt *parent; |
|
1574 |
+ |
|
1575 |
+ /* If the certificate is in the trust store already, remove |
|
1576 |
+ * it from the newcerts list */ |
|
1577 |
+ if (crtmgr_lookup(cmgr, x509)) { |
|
1578 |
+ cli_crt *tmp = x509->next; |
|
1579 |
+ cli_dbgmsg("asn1_parse_mscat: found embedded certificate matching one in the trust store\n"); |
|
1580 |
+ crtmgr_del(&newcerts, x509); |
|
1581 |
+ x509 = tmp; |
|
1582 |
+ continue; |
|
1566 | 1583 |
} |
1567 | 1584 |
|
1585 |
+ /* Determine whether the cert is signed by one in our trust |
|
1586 |
+ * store or has a blacklist entry */ |
|
1587 |
+ parent = crtmgr_verify_crt(cmgr, x509); |
|
1588 |
+ |
|
1568 | 1589 |
if(parent) { |
1569 | 1590 |
if (parent->isBlacklisted) { |
1570 | 1591 |
// NOTE: In this case, parent is a blacklist entry |
... | ... |
@@ -1572,10 +1591,7 @@ static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmg |
1572 | 1572 |
// entry for this certificate's parent |
1573 | 1573 |
isBlacklisted = 1; |
1574 | 1574 |
cli_dbgmsg("asn1_parse_mscat: Authenticode certificate %s is revoked. Flagging sample as virus.\n", (parent->name ? parent->name : "(no name)")); |
1575 |
- |
|
1576 |
- // TODO In this case cmgr already has a blacklisted |
|
1577 |
- // cert for this x509, so I don't think we need to |
|
1578 |
- // continue on below and try to add it to cmgr |
|
1575 |
+ break; |
|
1579 | 1576 |
} |
1580 | 1577 |
|
1581 | 1578 |
// TODO Why is this done? |
... | ... |
@@ -1587,9 +1603,16 @@ static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmg |
1587 | 1587 |
break; |
1588 | 1588 |
} |
1589 | 1589 |
crtmgr_del(&newcerts, x509); |
1590 |
+ |
|
1591 |
+ /* Start at the beginning of newcerts so that we can see |
|
1592 |
+ * whether adding this new trusted cert causes more |
|
1593 |
+ * certs to be trusted (via chaining). Otherwise we |
|
1594 |
+ * might miss valid certs if the ordering in the binary |
|
1595 |
+ * doesn't align with the chain ordering. */ |
|
1590 | 1596 |
x509 = newcerts.crts; |
1591 | 1597 |
continue; |
1592 | 1598 |
} |
1599 |
+ |
|
1593 | 1600 |
x509 = x509->next; |
1594 | 1601 |
} |
1595 | 1602 |
if(x509) { |
... | ... |
@@ -1895,7 +1918,6 @@ static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmg |
1895 | 1895 |
deep.next = asn1.content; |
1896 | 1896 |
result = 0; |
1897 | 1897 |
while(dsize) { |
1898 |
- struct cli_asn1 cobj; |
|
1899 | 1898 |
int content; |
1900 | 1899 |
if(asn1_expect_objtype(map, deep.next, &dsize, &deep, ASN1_TYPE_SEQUENCE)) { |
1901 | 1900 |
cli_dbgmsg("asn1_parse_mscat: expected SEQUENCE starting an unauthenticatedAttribute\n"); |
... | ... |
@@ -2210,7 +2232,7 @@ int asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsig |
2210 | 2210 |
|
2211 | 2211 |
// Now that we know the hash algorithm, compute the authenticode hash |
2212 | 2212 |
// across the required regions of memory. |
2213 |
- for(int i = 0; i < nregions; i++) { |
|
2213 |
+ for(unsigned int i = 0; i < nregions; i++) { |
|
2214 | 2214 |
const uint8_t *hptr; \ |
2215 | 2215 |
if(!(hptr = fmap_need_off_once(map, regions[i].offset, regions[i].size))){ |
2216 | 2216 |
return CL_VIRUS; |
... | ... |
@@ -2223,7 +2245,7 @@ int asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsig |
2223 | 2223 |
|
2224 | 2224 |
if(cli_debug_flag) { |
2225 | 2225 |
char hashtxt[MAX_HASH_SIZE*2+1]; |
2226 |
- for(int i=0; i<hashsize; i++) |
|
2226 |
+ for(unsigned int i=0; i<hashsize; i++) |
|
2227 | 2227 |
sprintf(&hashtxt[i*2], "%02x", hash[i]); |
2228 | 2228 |
cli_dbgmsg("Authenticode: %s\n", hashtxt); |
2229 | 2229 |
} |
... | ... |
@@ -62,66 +62,85 @@ void cli_crt_clear(cli_crt *x509) { |
62 | 62 |
mp_clear_multi(&x509->n, &x509->e, &x509->sig, NULL); |
63 | 63 |
} |
64 | 64 |
|
65 |
-cli_crt *crtmgr_lookup(crtmgr *m, cli_crt *x509) { |
|
65 |
+/* Look for an existing certificate in the trust store m. This search allows |
|
66 |
+ * the not_before / not_after / certSign / codeSign / timeSign fields to be |
|
67 |
+ * more restrictive than the values associated with a cert in the trust store, |
|
68 |
+ * but not less. It's probably overkill to not do exact matching on those |
|
69 |
+ * fields... TODO Is there a case where this is needed |
|
70 |
+ * |
|
71 |
+ * There are two ways that things get added to the whitelist - through the CRB |
|
72 |
+ * rules, and through embedded signatures / catalog files that we parse. CRB |
|
73 |
+ * rules only specify the subject, serial, public key, and whether the cert |
|
74 |
+ * can be used for cert/code/time signing, so in those cases the issuer and |
|
75 |
+ * hashtype get set to a hardcoded value. Those values are important for |
|
76 |
+ * doing signature verification, though, so we include them when doing this |
|
77 |
+ * lookup. That way, certs with more specific values can get added to the |
|
78 |
+ * whitelist by functions like crtmgr_add and increase the probability of |
|
79 |
+ * successful signature verification. */ |
|
80 |
+cli_crt *crtmgr_whitelist_lookup(crtmgr *m, cli_crt *x509) { |
|
66 | 81 |
cli_crt *i; |
67 | 82 |
for(i = m->crts; i; i = i->next) { |
68 |
- if(x509->not_before >= i->not_before && |
|
83 |
+ if(!i->isBlacklisted && |
|
84 |
+ x509->not_before >= i->not_before && |
|
69 | 85 |
x509->not_after <= i->not_after && |
70 | 86 |
(i->certSign | x509->certSign) == i->certSign && |
71 | 87 |
(i->codeSign | x509->codeSign) == i->codeSign && |
72 | 88 |
(i->timeSign | x509->timeSign) == i->timeSign && |
73 | 89 |
!memcmp(x509->subject, i->subject, sizeof(i->subject)) && |
74 | 90 |
!memcmp(x509->serial, i->serial, sizeof(i->serial)) && |
91 |
+ !memcmp(x509->issuer, i->issuer, sizeof(i->issuer)) && |
|
92 |
+ x509->hashtype == i->hashtype && |
|
75 | 93 |
!mp_cmp(&x509->n, &i->n) && |
76 |
- !mp_cmp(&x509->e, &i->e) && !(i->isBlacklisted)) { |
|
94 |
+ !mp_cmp(&x509->e, &i->e)) { |
|
77 | 95 |
return i; |
78 | 96 |
} |
79 | 97 |
} |
80 | 98 |
return NULL; |
81 | 99 |
} |
82 | 100 |
|
83 |
-int crtmgr_add(crtmgr *m, cli_crt *x509) { |
|
101 |
+cli_crt *crtmgr_blacklist_lookup(crtmgr *m, cli_crt *x509) { |
|
84 | 102 |
cli_crt *i; |
85 |
- int ret = 0; |
|
86 |
- |
|
87 |
- for(i = m->crts; i; i = i->next) { |
|
88 |
- if(!memcmp(x509->subject, i->subject, sizeof(i->subject)) && |
|
89 |
- !memcmp(x509->serial, i->subject, sizeof(i->serial)) && |
|
90 |
- !mp_cmp(&x509->n, &i->n) && |
|
91 |
- !mp_cmp(&x509->e, &i->e)) { |
|
92 |
- if(x509->not_before >= i->not_before && x509->not_after <= i->not_after) { |
|
93 |
- /* Already same or broader */ |
|
94 |
- ret = 1; |
|
95 |
- } |
|
96 |
- if(i->not_before > x509->not_before && i->not_before <= x509->not_after) { |
|
97 |
- /* Extend left */ |
|
98 |
- i->not_before = x509->not_before; |
|
99 |
- ret = 1; |
|
100 |
- } |
|
101 |
- if(i->not_after >= x509->not_before && i->not_after < x509->not_after) { |
|
102 |
- /* Extend right */ |
|
103 |
- i->not_after = x509->not_after; |
|
104 |
- ret = 1; |
|
105 |
- } |
|
106 |
- if(!ret) |
|
107 |
- continue; |
|
108 |
- i->certSign |= x509->certSign; |
|
109 |
- i->codeSign |= x509->codeSign; |
|
110 |
- i->timeSign |= x509->timeSign; |
|
103 |
+ for (i = m->crts; i; i = i->next) { |
|
104 |
+ // The CRB rules are based on subject, serial, and public key, |
|
105 |
+ // so do blacklist queries based on those fields |
|
111 | 106 |
|
112 |
- return 0; |
|
113 |
- } |
|
107 |
+ // TODO Handle the case where these items aren't specified in a CRB |
|
108 |
+ // rule entry - substitute in default values instead. |
|
114 | 109 |
|
115 |
- /* If certs match, we're likely just revoking it */ |
|
116 |
- if (!memcmp(x509->subject, i->subject, sizeof(x509->subject)) && |
|
117 |
- !memcmp(x509->issuer, i->issuer, sizeof(x509->issuer)) && |
|
118 |
- !memcmp(x509->serial, i->serial, sizeof(x509->serial)) && |
|
110 |
+ if (i->isBlacklisted && |
|
111 |
+ !memcmp(i->subject, x509->subject, sizeof(i->subject)) && |
|
112 |
+ !memcmp(i->serial, x509->serial, sizeof(i->serial)) && |
|
119 | 113 |
!mp_cmp(&x509->n, &i->n) && |
120 | 114 |
!mp_cmp(&x509->e, &i->e)) { |
121 |
- if (i->isBlacklisted != x509->isBlacklisted) |
|
122 |
- i->isBlacklisted = x509->isBlacklisted; |
|
115 |
+ return i; |
|
116 |
+ } |
|
117 |
+ } |
|
118 |
+ return NULL; |
|
119 |
+} |
|
123 | 120 |
|
124 |
- return 0; |
|
121 |
+/* Determine whether x509 already exists in m. The fields compared depend on |
|
122 |
+ * whether x509 is a blacklist entry or a trusted certificate */ |
|
123 |
+cli_crt *crtmgr_lookup(crtmgr *m, cli_crt *x509) { |
|
124 |
+ if (x509->isBlacklisted) { |
|
125 |
+ return crtmgr_blacklist_lookup(m, x509); |
|
126 |
+ } else { |
|
127 |
+ return crtmgr_whitelist_lookup(m, x509); |
|
128 |
+ } |
|
129 |
+} |
|
130 |
+ |
|
131 |
+int crtmgr_add(crtmgr *m, cli_crt *x509) { |
|
132 |
+ cli_crt *i; |
|
133 |
+ int ret = 0; |
|
134 |
+ |
|
135 |
+ if (x509->isBlacklisted) { |
|
136 |
+ if (crtmgr_blacklist_lookup(m, x509)) { |
|
137 |
+ cli_dbgmsg("crtmgr_add: duplicate blacklist entry detected - not adding\n"); |
|
138 |
+ return 0; |
|
139 |
+ } |
|
140 |
+ } else { |
|
141 |
+ if (crtmgr_whitelist_lookup(m, x509)) { |
|
142 |
+ cli_dbgmsg("crtmgr_add: duplicate trusted certificate detected - not adding\n"); |
|
143 |
+ return 0; |
|
125 | 144 |
} |
126 | 145 |
} |
127 | 146 |
|
... | ... |
@@ -239,7 +258,7 @@ static int crtmgr_rsa_verify(cli_crt *x509, mp_int *sig, cli_crt_hashtype hashty |
239 | 239 |
cli_dbgmsg("crtmgr_rsa_verify: keylen-1 doesn't match expected size of exptmod result\n"); |
240 | 240 |
break; |
241 | 241 |
} |
242 |
- if(mp_unsigned_bin_size(&x) > sizeof(d)) { |
|
242 |
+ if(((unsigned int) mp_unsigned_bin_size(&x)) > sizeof(d)) { |
|
243 | 243 |
cli_dbgmsg("crtmgr_rsa_verify: exptmod result would overrun working buffer\n"); |
244 | 244 |
break; |
245 | 245 |
} |
... | ... |
@@ -376,29 +395,28 @@ static int crtmgr_rsa_verify(cli_crt *x509, mp_int *sig, cli_crt_hashtype hashty |
376 | 376 |
cli_crt *crtmgr_verify_crt(crtmgr *m, cli_crt *x509) { |
377 | 377 |
cli_crt *i = m->crts, *best = NULL; |
378 | 378 |
int score = 0; |
379 |
+ unsigned int possible = 0; |
|
379 | 380 |
|
380 |
- for (i = m->crts; i; i = i->next) { |
|
381 |
- if (!memcmp(i->subject, x509->subject, sizeof(i->subject)) && |
|
382 |
- !memcmp(i->serial, x509->serial, sizeof(i->serial))) { |
|
383 |
- |
|
384 |
- // TODO Shouldn't we compare public keys in this case as well? I'd |
|
385 |
- // think that it's the public key that really makes a certificate |
|
386 |
- // unique (not subject/serial). Otherwise, you could have malware |
|
387 |
- // use the subject/serial from a popular company's cert and if we |
|
388 |
- // blacklisted that it would cause FPs on the popular software. |
|
389 |
- |
|
390 |
- if (i->isBlacklisted) |
|
391 |
- return i; |
|
392 |
- } |
|
381 |
+ if (NULL != (i = crtmgr_blacklist_lookup(m, x509))) { |
|
382 |
+ return i; |
|
393 | 383 |
} |
394 | 384 |
|
385 |
+ // TODO Technically we should loop through all of the blacklisted certs |
|
386 |
+ // first to see whether one of those is used to sign x509. This case |
|
387 |
+ // will get handled if the blacklisted certificate is embedded, since we |
|
388 |
+ // will call crtmgr_verify_crt on it and match against the blacklist entry |
|
389 |
+ // that way, but the cert doesn't HAVE to be embedded. This case seems |
|
390 |
+ // unlikely enough to ignore, though. |
|
391 |
+ |
|
395 | 392 |
for(i = m->crts; i; i = i->next) { |
396 | 393 |
if(i->certSign && |
394 |
+ !i->isBlacklisted && |
|
397 | 395 |
!memcmp(i->subject, x509->issuer, sizeof(i->subject)) && |
398 | 396 |
!crtmgr_rsa_verify(i, &x509->sig, x509->hashtype, x509->tbshash)) { |
399 | 397 |
int curscore; |
400 | 398 |
if((x509->codeSign & i->codeSign) == x509->codeSign && (x509->timeSign & i->timeSign) == x509->timeSign) |
401 | 399 |
return i; |
400 |
+ possible++; |
|
402 | 401 |
curscore = (x509->codeSign & i->codeSign) + (x509->timeSign & i->timeSign); |
403 | 402 |
if(curscore > score) { |
404 | 403 |
best = i; |
... | ... |
@@ -406,6 +424,12 @@ cli_crt *crtmgr_verify_crt(crtmgr *m, cli_crt *x509) { |
406 | 406 |
} |
407 | 407 |
} |
408 | 408 |
} |
409 |
+ |
|
410 |
+ if (possible > 1) { |
|
411 |
+ // If this is ever triggered, it's probably an indication of an error |
|
412 |
+ // in the CRB being used. |
|
413 |
+ cli_warnmsg("crtmgr_verify_crt: choosing between codeSign cert and timeSign cert without enough info - errors may result\n"); |
|
414 |
+ } |
|
409 | 415 |
return best; |
410 | 416 |
} |
411 | 417 |
|