mirror of
https://github.com/Cisco-Talos/clamav.git
synced 2026-05-19 05:46:50 -04:00
Merge branch 'bb5638'
This commit is contained in:
@@ -580,6 +580,32 @@ FileDescription
|
||||
Entertainment Pack FreeCell Game
|
||||
\end{verbatim}
|
||||
|
||||
\subsection{Trusted and Revoked Certificates}
|
||||
Clamav 0.98 checks signed PE files for certificates and verifies each
|
||||
certificate in the chain against a database of trusted and revoked
|
||||
certificates. The sinagure format is
|
||||
\begin{verbatim}
|
||||
Name;Trusted;Subject;Pubkey;Exponent;CodeSign;TimeSign;NotBefore;Comment
|
||||
[;minFL[;maxFL]]
|
||||
\end{verbatim}
|
||||
where the corresponding fields are:
|
||||
\begin{itemize}
|
||||
\item \verb+Name:+ name of the entry
|
||||
\item \verb+Trusted:+ bit field, specifying whether the cert is
|
||||
trusted. 1 for trusted. 0 for revoked
|
||||
\item \verb+Subject:+ sha1 of the Subject field in hex
|
||||
\item \verb+Pubkey:+ the public key in hex
|
||||
\item \verb+Exponent:+ the exponent in hex. Currently ignored and
|
||||
hardcoded to 010001 (in hex)
|
||||
\item \verb+CodeSign:+ bit field, specifying whether this cert
|
||||
can sign code. 1 for true, 0 for false
|
||||
\item \verb+TimeSign:+ bit field. 1 for true, 0 for false
|
||||
\item \verb+NotBefore:+ integer, cert should not be added before
|
||||
this variable. Defaults to 0 if left empty
|
||||
\item \verb+Comment:+ comments for this entry
|
||||
\end{itemize}
|
||||
The signatures for certs are stored inside \verb+.crt+ files.
|
||||
|
||||
\subsection{Signatures based on container metadata}
|
||||
ClamAV 0.96 allows creating generic signatures matching files stored
|
||||
inside different container types which meet specific conditions.
|
||||
|
||||
@@ -749,6 +749,7 @@ static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmg
|
||||
SHA1Context ctx;
|
||||
cli_crt *x509;
|
||||
int result;
|
||||
int isBlacklisted = 0;
|
||||
|
||||
cli_dbgmsg("in asn1_parse_mscat\n");
|
||||
|
||||
@@ -841,11 +842,14 @@ static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmg
|
||||
while(x509) {
|
||||
cli_crt *parent = crtmgr_verify_crt(cmgr, x509);
|
||||
if(parent) {
|
||||
if (parent->isBlacklisted)
|
||||
isBlacklisted = 1;
|
||||
|
||||
x509->codeSign &= parent->codeSign;
|
||||
x509->timeSign &= parent->timeSign;
|
||||
if(crtmgr_add(cmgr, x509))
|
||||
break;
|
||||
crtmgr_del(&newcerts, x509);
|
||||
if(crtmgr_add(cmgr, x509))
|
||||
break;
|
||||
crtmgr_del(&newcerts, x509);
|
||||
x509 = newcerts.crts;
|
||||
continue;
|
||||
}
|
||||
@@ -1276,6 +1280,10 @@ static int asn1_parse_mscat(fmap_t *map, size_t offset, unsigned int size, crtmg
|
||||
}
|
||||
|
||||
cli_dbgmsg("asn1_parse_mscat: catalog succesfully parsed\n");
|
||||
if (isBlacklisted) {
|
||||
cli_dbgmsg("asn1_parse_mscat: executable containes revoked cert.\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
} while(0);
|
||||
|
||||
@@ -1290,7 +1298,7 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine) {
|
||||
int i;
|
||||
|
||||
if(asn1_parse_mscat(map, 0, map->len, &engine->cmgr, 0, &c.next, &size))
|
||||
return 1;
|
||||
return 1;
|
||||
|
||||
if(asn1_expect_objtype(map, c.next, &size, &c, 0x30))
|
||||
return 1;
|
||||
@@ -1426,10 +1434,11 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asn1_check_mscat(fmap_t *map, size_t offset, unsigned int size, uint8_t *computed_sha1) {
|
||||
int asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, uint8_t *computed_sha1) {
|
||||
unsigned int content_size;
|
||||
struct cli_asn1 c;
|
||||
const void *content;
|
||||
@@ -1438,7 +1447,7 @@ int asn1_check_mscat(fmap_t *map, size_t offset, unsigned int size, uint8_t *com
|
||||
|
||||
cli_dbgmsg("in asn1_check_mscat (offset: %lu)\n", offset);
|
||||
crtmgr_init(&certs);
|
||||
if(crtmgr_add_roots(&certs)) {
|
||||
if(crtmgr_add_roots(engine, &certs)) {
|
||||
crtmgr_free(&certs);
|
||||
return CL_VIRUS;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,6 @@
|
||||
#include "fmap.h"
|
||||
|
||||
int asn1_load_mscat(fmap_t *map, struct cl_engine *engine);
|
||||
int asn1_check_mscat(fmap_t *map, size_t offset, unsigned int size, uint8_t *computed_sha1);
|
||||
int asn1_check_mscat(struct cl_engine *engine, fmap_t *map, size_t offset, unsigned int size, uint8_t *computed_sha1);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
#include "clamav-config.h"
|
||||
#endif
|
||||
|
||||
#include "crtmgr.h"
|
||||
#include "others.h"
|
||||
#include "crtmgr.h"
|
||||
|
||||
int cli_crt_init(cli_crt *x509) {
|
||||
int ret;
|
||||
@@ -31,6 +31,7 @@ int cli_crt_init(cli_crt *x509) {
|
||||
cli_errmsg("cli_crt_init: mp_init_multi failed with %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
x509->isBlacklisted = 0;
|
||||
x509->not_before = x509->not_after = 0;
|
||||
x509->prev = x509->next = NULL;
|
||||
x509->certSign = x509->codeSign = x509->timeSign = 0;
|
||||
@@ -87,8 +88,21 @@ int crtmgr_add(crtmgr *m, cli_crt *x509) {
|
||||
i->certSign |= x509->certSign;
|
||||
i->codeSign |= x509->codeSign;
|
||||
i->timeSign |= x509->timeSign;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If certs match, we're likely just revoking it */
|
||||
if (!memcmp(x509->subject, i->subject, sizeof(x509->subject)) &&
|
||||
!memcmp(x509->issuer, i->issuer, sizeof(x509->issuer)) &&
|
||||
!memcmp(x509->serial, i->serial, sizeof(x509->serial)) &&
|
||||
!mp_cmp(&x509->n, &i->n) &&
|
||||
!mp_cmp(&x509->e, &i->e)) {
|
||||
if (i->isBlacklisted != x509->isBlacklisted)
|
||||
i->isBlacklisted = x509->isBlacklisted;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
i = cli_malloc(sizeof(*i));
|
||||
@@ -116,6 +130,7 @@ int crtmgr_add(crtmgr *m, cli_crt *x509) {
|
||||
i->certSign = x509->certSign;
|
||||
i->codeSign = x509->codeSign;
|
||||
i->timeSign = x509->timeSign;
|
||||
i->isBlacklisted = x509->isBlacklisted;
|
||||
i->next = m->crts;
|
||||
i->prev = NULL;
|
||||
if(m->crts)
|
||||
@@ -314,8 +329,9 @@ cli_crt *crtmgr_verify_pkcs7(crtmgr *m, const uint8_t *issuer, const uint8_t *se
|
||||
continue;
|
||||
if(!memcmp(i->issuer, issuer, sizeof(i->issuer)) &&
|
||||
!memcmp(i->serial, serial, sizeof(i->serial)) &&
|
||||
!crtmgr_rsa_verify(i, &sig, hashtype, refhash))
|
||||
!crtmgr_rsa_verify(i, &sig, hashtype, refhash)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
mp_clear(&sig);
|
||||
return i;
|
||||
@@ -415,57 +431,23 @@ static const uint8_t VER_MOD[] = "\
|
||||
static const uint8_t VER_EXP[] = "\x01\x00\x01";
|
||||
|
||||
|
||||
int crtmgr_add_roots(crtmgr *m) {
|
||||
int crtmgr_add_roots(struct cl_engine *engine, crtmgr *m) {
|
||||
cli_crt ca;
|
||||
if(cli_crt_init(&ca))
|
||||
return 1;
|
||||
cli_crt *crt, *new_crt;
|
||||
|
||||
do {
|
||||
memset(ca.issuer, '\xca', sizeof(ca.issuer));
|
||||
memcpy(ca.subject, MSCA_SUBJECT, sizeof(ca.subject));
|
||||
memset(ca.serial, '\xca', sizeof(ca.serial));
|
||||
if(mp_read_unsigned_bin(&ca.n, MSCA_MOD, sizeof(MSCA_MOD)-1) || mp_read_unsigned_bin(&ca.e, MSCA_EXP, sizeof(MSCA_EXP)-1)) {
|
||||
cli_errmsg("crtmgr_add_roots: failed to read MSCA key\n");
|
||||
break;
|
||||
}
|
||||
ca.not_before = 0;
|
||||
ca.not_after = (-1U)>>1;
|
||||
ca.certSign = 1;
|
||||
ca.codeSign = 1;
|
||||
ca.timeSign = 1;
|
||||
if(crtmgr_add(m, &ca))
|
||||
break;
|
||||
/*
|
||||
* Certs are cached in engine->cmgr. Copy from there.
|
||||
*/
|
||||
if (m != &(engine->cmgr)) {
|
||||
for (crt = engine->cmgr.crts; crt != NULL; crt = crt->next) {
|
||||
if (crtmgr_add(m, crt)) {
|
||||
crtmgr_free(m);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ca.subject, MSA_SUBJECT, sizeof(ca.subject));
|
||||
if(mp_read_unsigned_bin(&ca.n, MSA_MOD, sizeof(MSA_MOD)-1) || mp_read_unsigned_bin(&ca.e, MSA_EXP, sizeof(MSA_EXP)-1)) {
|
||||
cli_errmsg("crtmgr_add_roots: failed to read MSA key\n");
|
||||
break;
|
||||
}
|
||||
if(crtmgr_add(m, &ca))
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(ca.subject, VER_SUBJECT, sizeof(ca.subject));
|
||||
if(mp_read_unsigned_bin(&ca.n, VER_MOD, sizeof(VER_MOD)-1) || mp_read_unsigned_bin(&ca.e, VER_EXP, sizeof(VER_EXP)-1)) {
|
||||
cli_errmsg("crtmgr_add_roots: failed to read VER key\n");
|
||||
break;
|
||||
}
|
||||
ca.timeSign = 0;
|
||||
if(crtmgr_add(m, &ca))
|
||||
break;
|
||||
|
||||
memcpy(ca.subject, THAW_SUBJECT, sizeof(ca.subject));
|
||||
if(mp_read_unsigned_bin(&ca.n, THAW_MOD, sizeof(THAW_MOD)-1) || mp_read_unsigned_bin(&ca.e, THAW_EXP, sizeof(THAW_EXP)-1)) {
|
||||
cli_errmsg("crtmgr_add_roots: failed to read THAW key\n");
|
||||
break;
|
||||
}
|
||||
ca.codeSign = 0;
|
||||
ca.timeSign = 1;
|
||||
if(crtmgr_add(m, &ca))
|
||||
break;
|
||||
return 0;
|
||||
} while(0);
|
||||
|
||||
cli_crt_clear(&ca);
|
||||
crtmgr_free(m);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ typedef struct cli_crt_t {
|
||||
int certSign;
|
||||
int codeSign;
|
||||
int timeSign;
|
||||
int isBlacklisted;
|
||||
struct cli_crt_t *prev;
|
||||
struct cli_crt_t *next;
|
||||
} cli_crt;
|
||||
@@ -62,7 +63,7 @@ cli_crt *crtmgr_lookup(crtmgr *m, cli_crt *x509);
|
||||
void crtmgr_del(crtmgr *m, cli_crt *x509);
|
||||
cli_crt *crtmgr_verify_crt(crtmgr *m, cli_crt *x509);
|
||||
cli_crt *crtmgr_verify_pkcs7(crtmgr *m, const uint8_t *issuer, const uint8_t *serial, const void *signature, unsigned int signature_len, cli_crt_hashtype hashtype, const uint8_t *refhash, cli_vrfy_type vrfytype);
|
||||
int crtmgr_add_roots(crtmgr *m);
|
||||
int crtmgr_add_roots(struct cl_engine *engine, crtmgr *m);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -351,8 +351,8 @@ struct cl_engine *cl_engine_new(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
crtmgr_init(&new->cmgr);
|
||||
if(crtmgr_add_roots(&new->cmgr)) {
|
||||
crtmgr_init(&(new->cmgr));
|
||||
if(crtmgr_add_roots(new, &(new->cmgr))) {
|
||||
cli_errmsg("cl_engine_new: Can't initialize root certificates\n");
|
||||
mpool_free(new->mempool, new->dconf);
|
||||
mpool_free(new->mempool, new->root);
|
||||
|
||||
@@ -2858,5 +2858,5 @@ int cli_checkfp_pe(cli_ctx *ctx, uint8_t *authsha1) {
|
||||
if(hlen < 8)
|
||||
return CL_VIRUS;
|
||||
hlen -= 8;
|
||||
return asn1_check_mscat(map, at + 8, hlen, authsha1);
|
||||
return asn1_check_mscat((struct cl_engine *)(ctx->engine), map, at + 8, hlen, authsha1);
|
||||
}
|
||||
|
||||
@@ -2362,6 +2362,142 @@ static int cli_loadcdb(FILE *fs, struct cl_engine *engine, unsigned int *signo,
|
||||
return CL_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* name;trusted;subject;pubkey;exp;codesign;timesign;notbefore;comment[;minFL[;maxFL]]
|
||||
* Name and comment are ignored. They're just for the end user.
|
||||
* Exponent is ignored for now and hardcoded to \x01\x00\x01.
|
||||
*/
|
||||
#define CRT_TOKENS 11
|
||||
static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio) {
|
||||
char buffer[FILEBUFF];
|
||||
char *tokens[CRT_TOKENS+1];
|
||||
size_t line=0, tokens_count, i, j;
|
||||
cli_crt ca;
|
||||
int ret=CL_SUCCESS;
|
||||
char *subject, *pubkey, *exponent;
|
||||
const uint8_t exp[] = "\x01\x00\x01";
|
||||
char c;
|
||||
|
||||
cli_crt_init(&ca);
|
||||
memset(ca.issuer, '\xca', sizeof(ca.issuer));
|
||||
memset(ca.serial, '\xca', sizeof(ca.serial));
|
||||
|
||||
while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
|
||||
line++;
|
||||
|
||||
if (buffer[0] == '#')
|
||||
continue;
|
||||
|
||||
cli_chomp(buffer);
|
||||
if (!strlen(buffer))
|
||||
continue;
|
||||
|
||||
tokens_count = cli_strtokenize(buffer, ';', CRT_TOKENS + 1, (const char **)tokens);
|
||||
if (tokens_count > CRT_TOKENS || tokens_count < CRT_TOKENS - 2) {
|
||||
cli_errmsg("cli_loadcrt: line %u: Invalid number of tokens: %u\n", line, tokens_count);
|
||||
ret = CL_EMALFDB;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (tokens_count > CRT_TOKENS - 2) {
|
||||
if (!cli_isnumber(tokens[CRT_TOKENS-1])) {
|
||||
cli_errmsg("cli_loadcrt: line %u: Invalid minimum feature level\n", line);
|
||||
ret = CL_EMALFDB;
|
||||
goto end;
|
||||
}
|
||||
if ((unsigned int)atoi(tokens[CRT_TOKENS-1]) > cl_retflevel()) {
|
||||
cli_dbgmsg("cli_loadcrt: Cert %s not loaded (required f-level: %u)\n", tokens[0], cl_retflevel());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokens_count == CRT_TOKENS) {
|
||||
if (!cli_isnumber(tokens[CRT_TOKENS])) {
|
||||
cli_errmsg("cli_loadcrt: line %u: Invalid maximum feature level\n", line);
|
||||
ret = CL_EMALFDB;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((unsigned int)atoi(tokens[CRT_TOKENS]) < cl_retflevel()) {
|
||||
cli_dbgmsg("cli_ladcrt: Cert %s not loaded (maximum f-level: %s)\n", tokens[0], tokens[CRT_TOKENS]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (tokens[1][0]) {
|
||||
case '1':
|
||||
ca.isBlacklisted = 0;
|
||||
break;
|
||||
case '0':
|
||||
ca.isBlacklisted = 1;
|
||||
break;
|
||||
default:
|
||||
cli_errmsg("cli_loadcrt: line %u: Invalid trust specification. Expected 0 or 1\n", line);
|
||||
ret = CL_EMALFDB;
|
||||
goto end;
|
||||
}
|
||||
|
||||
subject = cli_hex2str(tokens[2]);
|
||||
pubkey = cli_hex2str(tokens[3]);
|
||||
|
||||
if (!subject) {
|
||||
cli_errmsg("cli_loadcrt: line %u: Cannot convert subject to binary string\n", line);
|
||||
ret = CL_EMALFDB;
|
||||
goto end;
|
||||
}
|
||||
if (!pubkey) {
|
||||
cli_errmsg("cli_loadcrt: line %u: Cannot convert public key to binary string\n", line);
|
||||
ret = CL_EMALFDB;
|
||||
goto end;
|
||||
}
|
||||
|
||||
memcpy(ca.subject, subject, sizeof(ca.subject));
|
||||
if (mp_read_unsigned_bin(&(ca.n), pubkey, strlen(tokens[3])/2) || mp_read_unsigned_bin(&(ca.e), exp, sizeof(exp)-1)) {
|
||||
cli_errmsg("cli_loadcrt: line %u: Cannot convert exponent to binary data\n", line);
|
||||
ret = CL_EMALFDB;
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch (tokens[5][0]) {
|
||||
case '1':
|
||||
ca.codeSign = 1;
|
||||
break;
|
||||
case '0':
|
||||
ca.codeSign = 0;
|
||||
break;
|
||||
default:
|
||||
cli_errmsg("cli_loadcrt: line %u: Invalid code sign specification. Expected 0 or 1\n", line);
|
||||
ret = CL_EMALFDB;
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch (tokens[6][0]) {
|
||||
case '1':
|
||||
ca.timeSign = 1;
|
||||
break;
|
||||
case '0':
|
||||
ca.timeSign = 0;
|
||||
break;
|
||||
default:
|
||||
cli_errmsg("cli_loadcrt: line %u: Invalid time sign specification. Expected 0 or 1\n", line);
|
||||
ret = CL_EMALFDB;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strlen(tokens[7]))
|
||||
ca.not_before = atoi(tokens[7]);
|
||||
ca.not_after = (-1U)>>1;
|
||||
ca.certSign = 1;
|
||||
|
||||
crtmgr_add(&(engine->cmgr), &ca);
|
||||
}
|
||||
|
||||
end:
|
||||
cli_dbgmsg("Number of certs: %d\n", engine->cmgr.items);
|
||||
cli_crt_clear(&ca);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cli_loadmscat(FILE *fs, const char *dbname, struct cl_engine *engine, unsigned int options, struct cli_dbio *dbio) {
|
||||
fmap_t *map;
|
||||
|
||||
@@ -2422,6 +2558,9 @@ int cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo
|
||||
} else if(cli_strbcasestr(dbname, ".cud")) {
|
||||
ret = cli_cvdload(fs, engine, signo, options, 2, filename, 0);
|
||||
|
||||
} else if (cli_strbcasestr(dbname, ".crt")) {
|
||||
ret = cli_loadcrt(fs, engine, dbio);
|
||||
|
||||
} else if(cli_strbcasestr(dbname, ".hdb") || cli_strbcasestr(dbname, ".hsb")) {
|
||||
ret = cli_loadhash(fs, engine, signo, MD5_HDB, options, dbio, dbname);
|
||||
} else if(cli_strbcasestr(dbname, ".hdu") || cli_strbcasestr(dbname, ".hsu")) {
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
cli_strbcasestr(ext, ".cud") || \
|
||||
cli_strbcasestr(ext, ".cdb") || \
|
||||
cli_strbcasestr(ext, ".cat") || \
|
||||
cli_strbcasestr(ext, ".crt") || \
|
||||
cli_strbcasestr(ext, ".idb") \
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user