Also, move the cert-related DCONF cfg checks to more
appropriate locations. One change in behavior:
PE_CONF_CATALOG will disable loading trusted hashes from
.cat files, but won't disable Authenticode hash checking
completely (PE_CONF_CERTS does this).
... | ... |
@@ -1365,7 +1365,7 @@ static int asn1_parse_countersignature(fmap_t *map, const void **asn1data, unsig |
1365 | 1365 |
return 1; |
1366 | 1366 |
} |
1367 | 1367 |
|
1368 |
-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, char **certname) |
|
1368 |
+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) |
|
1369 | 1369 |
{ |
1370 | 1370 |
struct cli_asn1 asn1, deep, deeper; |
1371 | 1371 |
uint8_t issuer[SHA1_HASH_SIZE], serial[SHA1_HASH_SIZE]; |
... | ... |
@@ -1378,7 +1378,7 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t |
1378 | 1378 |
// md is used to hold the message digest we extract from the signature |
1379 | 1379 |
uint8_t md[MAX_HASH_SIZE]; |
1380 | 1380 |
cli_crt *x509; |
1381 |
- void *ctx; |
|
1381 |
+ void *hash_ctx; |
|
1382 | 1382 |
int result; |
1383 | 1383 |
cl_error_t ret = CL_EPARSE; |
1384 | 1384 |
|
... | ... |
@@ -1589,17 +1589,23 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t |
1589 | 1589 |
/* Use &(engine->cmgr) for this check, since we don't copy |
1590 | 1590 |
* blacklist certs into cmgr and so that if there's a |
1591 | 1591 |
* match, we have a long-lived pointer that we can pass |
1592 |
- * back indicating the name of the sig that matched (we |
|
1593 |
- * can't just malloc new space for one, since nothing above |
|
1594 |
- * here knows to free it.) */ |
|
1592 |
+ * back (via cli_append_virus) indicating the name of the |
|
1593 |
+ * sigs that matched (we can't just malloc new space for |
|
1594 |
+ * one, since nothing above here knows to free it.) */ |
|
1595 | 1595 |
if (NULL != (crt = crtmgr_blacklist_lookup(&(engine->cmgr), x509))) { |
1596 | 1596 |
ret = CL_VIRUS; |
1597 |
- cli_dbgmsg("asn1_parse_mscat: Found Authenticode certificate blacklisted by %s\n", (crt->name ? crt->name : "(no name)")); |
|
1598 |
- if (NULL != certname) { |
|
1599 |
- *certname = crt->name ? crt->name : "(no name)"; |
|
1597 |
+ cli_dbgmsg("asn1_parse_mscat: Found Authenticode certificate blacklisted by %s\n", crt->name ? crt->name : "(unnamed CRB rule)"); |
|
1598 |
+ if (NULL != ctx) { |
|
1599 |
+ ret = cli_append_virus(ctx, crt->name ? crt->name : "(unnamed CRB rule)"); |
|
1600 |
+ if ((ret == CL_VIRUS) && !SCAN_ALLMATCHES) { |
|
1601 |
+ crtmgr_free(&newcerts); |
|
1602 |
+ goto finish; |
|
1603 |
+ } |
|
1600 | 1604 |
} |
1601 |
- crtmgr_free(&newcerts); |
|
1602 |
- goto finish; |
|
1605 |
+ /* In the case where ctx is NULL, we don't care about |
|
1606 |
+ * blacklist matches - we are either using this |
|
1607 |
+ * function to parse .cat rules that were loaded in, |
|
1608 |
+ * or it's sigtool doing cert printing. */ |
|
1603 | 1609 |
} |
1604 | 1610 |
|
1605 | 1611 |
/* NOTE: Since the 'issuer' cli_crt field is required for |
... | ... |
@@ -1634,6 +1640,16 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t |
1634 | 1634 |
crtmgr_free(&newcerts); |
1635 | 1635 |
break; |
1636 | 1636 |
} |
1637 |
+ |
|
1638 |
+ /* In the SCAN_ALLMATCHES case, we'd get here with |
|
1639 |
+ * ret == CL_VIRUS if a match occurred but we wanted |
|
1640 |
+ * to keep looping to look for other matches. In that |
|
1641 |
+ * case, bail here. */ |
|
1642 |
+ if (CL_VIRUS == ret) { |
|
1643 |
+ crtmgr_free(&newcerts); |
|
1644 |
+ break; |
|
1645 |
+ } |
|
1646 |
+ |
|
1637 | 1647 |
x509 = newcerts.crts; |
1638 | 1648 |
|
1639 | 1649 |
/* Now look for cases where embedded certs can be trusted |
... | ... |
@@ -1928,13 +1944,13 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t |
1928 | 1928 |
break; |
1929 | 1929 |
} |
1930 | 1930 |
|
1931 |
- if (NULL == (ctx = get_hash_ctx(hashtype))) { |
|
1931 |
+ if (NULL == (hash_ctx = get_hash_ctx(hashtype))) { |
|
1932 | 1932 |
break; |
1933 | 1933 |
} |
1934 | 1934 |
|
1935 |
- cl_update_hash(ctx, "\x31", 1); |
|
1936 |
- cl_update_hash(ctx, (void *)(attrs + 1), attrs_size - 1); |
|
1937 |
- cl_finish_hash(ctx, hash); |
|
1935 |
+ cl_update_hash(hash_ctx, "\x31", 1); |
|
1936 |
+ cl_update_hash(hash_ctx, (void *)(attrs + 1), attrs_size - 1); |
|
1937 |
+ cl_finish_hash(hash_ctx, hash); |
|
1938 | 1938 |
|
1939 | 1939 |
if (!fmap_need_ptr_once(map, asn1.content, asn1.size)) { |
1940 | 1940 |
cli_dbgmsg("asn1_parse_mscat: failed to read encryptedDigest\n"); |
... | ... |
@@ -2267,7 +2283,7 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine) |
2267 | 2267 |
* |
2268 | 2268 |
* If CL_VIRUS is returned, certname will be set to the certname of blacklist |
2269 | 2269 |
* rule that matched (unless certname is NULL). */ |
2270 |
-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, char **certname) |
|
2270 |
+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) |
|
2271 | 2271 |
{ |
2272 | 2272 |
unsigned int content_size; |
2273 | 2273 |
struct cli_asn1 c; |
... | ... |
@@ -2277,15 +2293,9 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset |
2277 | 2277 |
const void *content; |
2278 | 2278 |
crtmgr certs; |
2279 | 2279 |
int ret; |
2280 |
- void *ctx; |
|
2280 |
+ void *hash_ctx; |
|
2281 | 2281 |
unsigned int i; |
2282 | 2282 |
|
2283 |
- // TODO Move these into cli_check_auth_header |
|
2284 |
- if (!(engine->dconf->pe & PE_CONF_CERTS)) |
|
2285 |
- return CL_EVERIFY; |
|
2286 |
- if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS) |
|
2287 |
- return CL_EVERIFY; |
|
2288 |
- |
|
2289 | 2283 |
cli_dbgmsg("in asn1_check_mscat (offset: %llu)\n", (long long unsigned)offset); |
2290 | 2284 |
crtmgr_init(&certs); |
2291 | 2285 |
/* Get a copy of all certs in the trust store, excluding blacklist certs */ |
... | ... |
@@ -2293,7 +2303,7 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset |
2293 | 2293 |
crtmgr_free(&certs); |
2294 | 2294 |
return CL_EVERIFY; |
2295 | 2295 |
} |
2296 |
- ret = asn1_parse_mscat(engine, map, offset, size, &certs, 1, &content, &content_size, certname); |
|
2296 |
+ ret = asn1_parse_mscat(engine, map, offset, size, &certs, 1, &content, &content_size, ctx); |
|
2297 | 2297 |
crtmgr_free(&certs); |
2298 | 2298 |
if (CL_CLEAN != ret) |
2299 | 2299 |
return ret; |
... | ... |
@@ -2325,7 +2335,7 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset |
2325 | 2325 |
return CL_EPARSE; |
2326 | 2326 |
} |
2327 | 2327 |
|
2328 |
- if (NULL == (ctx = get_hash_ctx(hashtype))) { |
|
2328 |
+ if (NULL == (hash_ctx = get_hash_ctx(hashtype))) { |
|
2329 | 2329 |
return CL_EPARSE; |
2330 | 2330 |
} |
2331 | 2331 |
|
... | ... |
@@ -2340,10 +2350,10 @@ cl_error_t asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset |
2340 | 2340 |
return CL_EVERIFY; |
2341 | 2341 |
} |
2342 | 2342 |
|
2343 |
- cl_update_hash(ctx, hptr, regions[i].size); |
|
2343 |
+ cl_update_hash(hash_ctx, hptr, regions[i].size); |
|
2344 | 2344 |
} |
2345 | 2345 |
|
2346 |
- cl_finish_hash(ctx, hash); |
|
2346 |
+ cl_finish_hash(hash_ctx, hash); |
|
2347 | 2347 |
|
2348 | 2348 |
if (cli_debug_flag) { |
2349 | 2349 |
char hashtxt[MAX_HASH_SIZE * 2 + 1]; |
... | ... |
@@ -31,6 +31,6 @@ struct cli_mapped_region { |
31 | 31 |
}; |
32 | 32 |
|
33 | 33 |
int asn1_load_mscat(fmap_t *map, struct cl_engine *engine); |
34 |
-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, char **certname); |
|
34 |
+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); |
|
35 | 35 |
|
36 | 36 |
#endif |
... | ... |
@@ -983,12 +983,7 @@ int cli_fmap_scandesc(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli |
983 | 983 |
* If we want to add support for more signature parsing in the future |
984 | 984 |
* (Ex: MachO sigs), do that here too. */ |
985 | 985 |
if (1 == info.status && i == 1) { |
986 |
- char *certname = NULL; |
|
987 |
- ret = cli_check_auth_header(ctx, &(info.exeinfo), &certname); |
|
988 |
- |
|
989 |
- if (CL_VIRUS == ret) { |
|
990 |
- ret = cli_append_virus(ctx, certname); |
|
991 |
- } |
|
986 |
+ ret = cli_check_auth_header(ctx, &(info.exeinfo)); |
|
992 | 987 |
|
993 | 988 |
if ((ret == CL_VIRUS || ret == CL_VERIFIED) && !SCAN_ALLMATCHES) { |
994 | 989 |
cli_targetinfo_destroy(&info); |
... | ... |
@@ -5510,11 +5510,11 @@ static int sort_sects(const void *first, const void *second) |
5510 | 5510 |
* |
5511 | 5511 |
* CL_VERIFIED will be returned if the file was whitelisted based on its |
5512 | 5512 |
* signature. CL_VIRUS will be returned if the file was blacklisted based on |
5513 |
- * its signature. Otherwise, an cl_error_t error value will be returned. |
|
5513 |
+ * its signature. Otherwise, a cl_error_t error value will be returned. |
|
5514 | 5514 |
* |
5515 |
- * If CL_VIRUS is returned, certname will be set to the certname of blacklist |
|
5516 |
- * rule that matched (unless certname is NULL). */ |
|
5517 |
-cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo, char **certname) |
|
5515 |
+ * If CL_VIRUS is returned, cli_append_virus will get called, adding the |
|
5516 |
+ * name associated with the blacklist CRB rules to the list of found viruses.*/ |
|
5517 |
+cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo) |
|
5518 | 5518 |
{ |
5519 | 5519 |
size_t at; |
5520 | 5520 |
unsigned int i, hlen; |
... | ... |
@@ -5530,12 +5530,13 @@ cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo, char |
5530 | 5530 |
uint32_t sec_dir_size; |
5531 | 5531 |
struct cli_exe_info _peinfo; |
5532 | 5532 |
|
5533 |
- // If Authenticode parsing has been disabled via DCONF, then don't |
|
5534 |
- // continue on. |
|
5535 |
- // TODO This should probably be named PE_CONF_AUTHENTICODE instead |
|
5536 |
- // of PE_CONF_CATALOG |
|
5537 |
- if (!(DCONF & PE_CONF_CATALOG)) |
|
5538 |
- return CL_EFORMAT; |
|
5533 |
+ // If Authenticode parsing has been disabled via DCONF or an engine |
|
5534 |
+ // option, then don't continue on. |
|
5535 |
+ if (!(DCONF & PE_CONF_CERTS)) |
|
5536 |
+ return CL_EVERIFY; |
|
5537 |
+ |
|
5538 |
+ if (ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS) |
|
5539 |
+ return CL_EVERIFY; |
|
5539 | 5540 |
|
5540 | 5541 |
// If peinfo is NULL, initialize one. This makes it so that this function |
5541 | 5542 |
// can be used easily by sigtool |
... | ... |
@@ -5659,7 +5660,7 @@ cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo, char |
5659 | 5659 |
at = sec_dir_offset + sizeof(cert_hdr); |
5660 | 5660 |
hlen -= sizeof(cert_hdr); |
5661 | 5661 |
|
5662 |
- ret = asn1_check_mscat((struct cl_engine *)(ctx->engine), map, at, hlen, regions, nregions, certname); |
|
5662 |
+ ret = asn1_check_mscat((struct cl_engine *)(ctx->engine), map, at, hlen, regions, nregions, ctx); |
|
5663 | 5663 |
|
5664 | 5664 |
if (CL_VERIFIED == ret) { |
5665 | 5665 |
// We validated the embedded signature. Hooray! |
... | ... |
@@ -98,7 +98,7 @@ enum { |
98 | 98 |
int cli_pe_targetinfo(fmap_t *map, struct cli_exe_info *peinfo); |
99 | 99 |
int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ctx *ctx); |
100 | 100 |
|
101 |
-cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo, char **certname); |
|
101 |
+cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo); |
|
102 | 102 |
int cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type, stats_section_t *hashes); |
103 | 103 |
|
104 | 104 |
uint32_t cli_rawaddr(uint32_t, const struct cli_exe_section *, uint16_t, unsigned int *, size_t, uint32_t); |
... | ... |
@@ -2939,6 +2939,16 @@ static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio |
2939 | 2939 |
char *subject = NULL, *pubkey = NULL, *serial = NULL; |
2940 | 2940 |
const uint8_t exp[] = "\x01\x00\x01"; |
2941 | 2941 |
|
2942 |
+ if (!(engine->dconf->pe & PE_CONF_CERTS)) { |
|
2943 |
+ cli_dbgmsg("cli_loadcrt: Ignoring .crb sigs due to DCONF configuration\n"); |
|
2944 |
+ return ret; |
|
2945 |
+ } |
|
2946 |
+ |
|
2947 |
+ if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS) { |
|
2948 |
+ cli_dbgmsg("cli_loadcrt: Ignoring .crb sigs due to engine options\n"); |
|
2949 |
+ return ret; |
|
2950 |
+ } |
|
2951 |
+ |
|
2942 | 2952 |
cli_crt_init(&ca); |
2943 | 2953 |
memset(ca.issuer, 0xca, sizeof(ca.issuer)); |
2944 | 2954 |
|
... | ... |
@@ -3105,6 +3115,19 @@ static int cli_loadmscat(FILE *fs, const char *dbname, struct cl_engine *engine, |
3105 | 3105 |
UNUSEDPARAM(options); |
3106 | 3106 |
UNUSEDPARAM(dbio); |
3107 | 3107 |
|
3108 |
+ /* If loading in signatures stored in .cat files is disabled, then skip. |
|
3109 |
+ * If Authenticoded signature parsing in general is disabled, then also |
|
3110 |
+ * skip. */ |
|
3111 |
+ if (!(engine->dconf->pe & PE_CONF_CATALOG) || !(engine->dconf->pe & PE_CONF_CERTS)) { |
|
3112 |
+ cli_dbgmsg("cli_loadmscat: Ignoring .cat sigs due to DCONF configuration\n"); |
|
3113 |
+ return 0; |
|
3114 |
+ } |
|
3115 |
+ |
|
3116 |
+ if (engine->engine_options & ENGINE_OPTIONS_DISABLE_PE_CERTS) { |
|
3117 |
+ cli_dbgmsg("cli_loadmscat: Ignoring .cat sigs due to engine options\n"); |
|
3118 |
+ return 0; |
|
3119 |
+ } |
|
3120 |
+ |
|
3108 | 3121 |
if (!(map = fmap(fileno(fs), 0, 0))) { |
3109 | 3122 |
cli_dbgmsg("Can't map cat: %s\n", dbname); |
3110 | 3123 |
return 0; |