Merge branch 'bb5638'

pull/25/head
Shawn Webb 13 years ago
commit 09171f5203
  1. 26
      docs/signatures.tex
  2. 21
      libclamav/asn1.c
  3. 2
      libclamav/asn1.h
  4. 88
      libclamav/crtmgr.c
  5. 3
      libclamav/crtmgr.h
  6. 4
      libclamav/others.c
  7. 2
      libclamav/pe.c
  8. 139
      libclamav/readdb.c
  9. 1
      libclamav/readdb.h

@ -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;
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;
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;
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 *crt, *new_crt;
/*
* 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;
}
}
return 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") \
)

Loading…
Cancel
Save