libclamav: Use OpenSSL' BN instead tomfastmath.

Use OpenSSL's big number/ multiprecision integer arithmetics
functionality to replace tomfastmath.

This is a first shot at doing just this. Further improvement could be
use more RSA-signature verification from OpenSSL in crtmgr_rsa_verify()
and less self parsing.
_padding_check_PKCS1_type_1() has been borrowed from OpenSSL to make
further replacments easier.

Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
This commit is contained in:
Sebastian Andrzej Siewior 2023-02-18 10:47:53 +01:00 committed by Micah Snyder
parent 3f56b55eb3
commit 12f4d18ce0
10 changed files with 441 additions and 237 deletions

View file

@ -250,8 +250,7 @@ target_sources( tomsfastmath
tomsfastmath/sqr/fp_sqr_comba_generic.c
tomsfastmath/sqr/fp_sqr_comba_small_set.c
tomsfastmath/sqr/fp_sqrmod.c
PUBLIC
bignum.h )
)
target_include_directories( tomsfastmath
PRIVATE
${CMAKE_BINARY_DIR}

View file

@ -24,10 +24,10 @@
#endif
#include <time.h>
#include <openssl/bn.h>
#include "clamav.h"
#include "asn1.h"
#include "bignum.h"
#include "matcher-hash.h"
/* --------------------------------------------------------------------------- OIDS */
@ -695,7 +695,8 @@ static int asn1_get_rsa_pubkey(fmap_t *map, const void **asn1data, unsigned int
return 1;
}
fp_read_unsigned_bin(&x509->n, obj.content, avail2);
if (!BN_bin2bn(obj.content, avail2, x509->n))
return 1;
if (asn1_expect_objtype(map, obj.next, &avail, &obj, ASN1_TYPE_INTEGER)) /* INTEGER - exp */
return 1;
@ -712,7 +713,8 @@ static int asn1_get_rsa_pubkey(fmap_t *map, const void **asn1data, unsigned int
return 1;
}
fp_read_unsigned_bin(&x509->e, obj.content, obj.size);
if (!BN_bin2bn(obj.content, obj.size, x509->e))
return 1;
return 0;
}
@ -738,9 +740,12 @@ static int asn1_get_x509(fmap_t *map, const void **asn1data, unsigned int *size,
int ret = ASN1_GET_X509_UNRECOVERABLE_ERROR;
unsigned int version;
cli_crt_init(&x509);
do {
if (cli_crt_init(&x509) < 0) {
cli_dbgmsg("asn1_get_x509: failed to initialize x509.\n");
break;
}
if (asn1_expect_objtype(map, *asn1data, size, &crt, ASN1_TYPE_SEQUENCE)) { /* SEQUENCE */
cli_dbgmsg("asn1_get_x509: expected SEQUENCE at the x509 start\n");
break;
@ -1107,7 +1112,8 @@ static int asn1_get_x509(fmap_t *map, const void **asn1data, unsigned int *size,
break;
}
fp_read_unsigned_bin(&x509.sig, obj.content, obj.size);
if (!BN_bin2bn(obj.content, obj.size, x509.sig))
break;
if (crt.size) {
cli_dbgmsg("asn1_get_x509: found unexpected extra data in signature\n");
@ -1404,6 +1410,8 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
void *hash_ctx;
int result;
cl_error_t ret = CL_EPARSE;
char *mod = NULL;
char *exp = NULL;
cli_dbgmsg("in asn1_parse_mscat\n");
@ -1558,11 +1566,10 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
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;
int j;
fp_toradix_n(&x509->n, mod, 16, j + 1);
fp_toradix_n(&x509->e, exp, 16, j + 1);
mod = BN_bn2hex(x509->n);
exp = BN_bn2hex(x509->e);
memset(raw_issuer, 0, CRT_RAWMAXLEN * 2 + 1);
memset(raw_subject, 0, CRT_RAWMAXLEN * 2 + 1);
memset(raw_serial, 0, CRT_RAWMAXLEN * 2 + 1);
@ -1594,6 +1601,10 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
cli_dbgmsg(" raw_issuer: %s\n", raw_issuer);
x509 = x509->next;
OPENSSL_free(mod);
OPENSSL_free(exp);
mod = NULL;
exp = NULL;
}
x509 = newcerts.crts;
}
@ -2149,6 +2160,8 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
} while (0);
finish:
OPENSSL_free(mod);
OPENSSL_free(exp);
if (CL_EPARSE == ret) {
cli_dbgmsg("asn1_parse_mscat: failed to parse authenticode section\n");
}

View file

@ -1,14 +0,0 @@
#ifndef BIGNUM_H_
#define BIGNUM_H_
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#if HAVE_SYSTEM_TOMSFASTMATH
#include <tfm.h>
#else
#include "tomsfastmath/headers/tfm.h"
#endif
#endif

View file

@ -42,20 +42,39 @@
#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
#define FP_INIT_MULTI(a, b, c) (fp_init(a), fp_init(b), fp_init(c))
#define FP_CLEAR_MULTI(...)
static int cli_crt_init_fps(cli_crt *x509)
{
x509->n = BN_new();
x509->e = BN_new();
x509->sig = BN_new();
void cli_crt_init(cli_crt *x509)
if (!x509->n || !x509->e || !x509->sig) {
BN_free(x509->n);
BN_free(x509->e);
BN_free(x509->sig);
x509->n = NULL;
x509->e = NULL;
x509->sig = NULL;
return -1;
}
return 0;
}
int cli_crt_init(cli_crt *x509)
{
memset(x509, 0, sizeof(*x509));
// FP_INIT_MULTI is a memset for each and cannot fail.
FP_INIT_MULTI(&x509->n, &x509->e, &x509->sig);
return cli_crt_init_fps(x509);
}
void cli_crt_clear(cli_crt *x509)
{
FP_CLEAR_MULTI(&x509->n, &x509->e, &x509->sig);
BN_free(x509->n);
BN_free(x509->e);
BN_free(x509->sig);
x509->n = NULL;
x509->e = NULL;
x509->sig = NULL;
}
/* Look for an existing certificate in the trust store `m`. This search allows
@ -118,7 +137,7 @@ cli_crt *crtmgr_trust_list_lookup(crtmgr *m, cli_crt *x509, int crb_crts_only)
if (x509->hashtype != i->hashtype ||
memcmp(x509->issuer, i->issuer, sizeof(i->issuer)) ||
x509->ignore_serial != i->ignore_serial ||
fp_cmp(&x509->e, &i->e)) {
BN_cmp(x509->e, i->e)) {
continue;
}
}
@ -135,7 +154,7 @@ cli_crt *crtmgr_trust_list_lookup(crtmgr *m, cli_crt *x509, int crb_crts_only)
(i->codeSign | x509->codeSign) == i->codeSign &&
(i->timeSign | x509->timeSign) == i->timeSign &&
!memcmp(x509->subject, i->subject, sizeof(i->subject)) &&
!fp_cmp(&x509->n, &i->n)) {
!BN_cmp(x509->n, i->n)) {
return i;
}
}
@ -166,7 +185,7 @@ cli_crt *crtmgr_block_list_lookup(crtmgr *m, cli_crt *x509)
if (!i->isBlocked ||
memcmp(i->subject, x509->subject, sizeof(i->subject)) ||
fp_cmp(&x509->n, &i->n)) {
BN_cmp(x509->n, i->n)) {
continue;
}
@ -191,37 +210,51 @@ cli_crt *crtmgr_lookup(crtmgr *m, cli_crt *x509)
}
}
int crtmgr_add(crtmgr *m, cli_crt *x509)
bool crtmgr_add(crtmgr *m, cli_crt *x509)
{
cli_crt *i;
bool failed = true;
cli_crt *i = NULL;
if (x509->isBlocked) {
if (crtmgr_block_list_lookup(m, x509)) {
cli_dbgmsg("crtmgr_add: duplicate blocked certificate detected - not adding\n");
return 0;
failed = false;
goto done;
}
} else {
if (crtmgr_trust_list_lookup(m, x509, 0)) {
cli_dbgmsg("crtmgr_add: duplicate trusted certificate detected - not adding\n");
return 0;
failed = false;
goto done;
}
}
i = cli_malloc(sizeof(*i));
if (!i)
return 1;
if (i == NULL) {
goto done;
}
// FP_INIT_MULTI is a memset for each and cannot fail.
FP_INIT_MULTI(&i->n, &i->e, &i->sig);
if (cli_crt_init_fps(i) < 0) {
goto done;
}
fp_copy(&x509->n, &i->n);
fp_copy(&x509->e, &i->e);
fp_copy(&x509->sig, &i->sig);
if (!BN_copy(i->n, x509->n)) {
goto done;
}
if (!BN_copy(i->e, x509->e)) {
goto done;
}
if (!BN_copy(i->sig, x509->sig)) {
goto done;
}
if ((x509->name))
if (x509->name) {
i->name = strdup(x509->name);
else
if (!i->name)
goto done;
} else {
i->name = NULL;
}
memcpy(i->raw_subject, x509->raw_subject, sizeof(i->raw_subject));
memcpy(i->raw_issuer, x509->raw_issuer, sizeof(i->raw_issuer));
@ -240,12 +273,23 @@ int crtmgr_add(crtmgr *m, cli_crt *x509)
i->isBlocked = x509->isBlocked;
i->next = m->crts;
i->prev = NULL;
if (m->crts)
if (m->crts) {
m->crts->prev = i;
}
m->crts = i;
m->items++;
return 0;
failed = false;
i = NULL;
done:
if (i != NULL) {
cli_crt_clear(i);
free(i);
}
return failed;
}
void crtmgr_init(crtmgr *m)
@ -281,12 +325,133 @@ void crtmgr_free(crtmgr *m)
crtmgr_del(m, m->crts);
}
static int crtmgr_rsa_verify(cli_crt *x509, fp_int *sig, cli_crt_hashtype hashtype, const uint8_t *refhash)
static cl_error_t _padding_check_PKCS1_type_1(uint8_t **to, int *tlen,
uint8_t *from, unsigned int flen,
unsigned int num)
{
int keylen = fp_unsigned_bin_size(&x509->n), siglen = fp_unsigned_bin_size(sig);
int ret, j, objlen, hashlen;
uint8_t d[513];
fp_int x;
int i, j;
unsigned char *p;
p = from;
/*
* The format is
* 00 || 01 || PS || 00 || D
* PS - padding string, at least 8 bytes of FF
* D - data.
*/
if (num < 11) /* RSA_PKCS1_PADDING_SIZE */
return CL_EPARSE;
/* Accept inputs with and without the leading 0-byte. */
if (num == flen) {
if ((*p++) != 0x00) {
cli_dbgmsg("%s: Bad padding\n", __func__);
return CL_EPARSE;
}
flen--;
}
if ((num != (flen + 1)) || (*(p++) != 0x01)) {
cli_dbgmsg("%s: Bad block type\n", __func__);
return CL_EPARSE;
}
/* scan over padding data */
j = flen - 1; /* one for type. */
for (i = 0; i < j; i++) {
if (*p != 0xff) { /* should decrypt to 0xff */
if (*p == 0) {
p++;
break;
} else {
cli_dbgmsg("%s: Bad header\n", __func__);
return CL_EPARSE;
}
}
p++;
}
if (i == j) {
cli_dbgmsg("%s: Bad header\n", __func__);
return CL_EPARSE;
}
if (i < 8) {
cli_dbgmsg("%s: Bad padding\n", __func__);
return CL_EPARSE;
}
i++; /* Skip over the '\0' */
j -= i;
*tlen = j;
*to = p;
return CL_SUCCESS;
}
static cl_error_t crtmgr_get_recov_data(BIGNUM *sig, cli_crt *x509,
uint8_t **buffer, uint8_t **payload,
int *payload_len)
{
BN_CTX *bnctx;
int pad_size;
int keylen;
uint8_t *d;
BIGNUM *x;
cl_error_t ret;
*buffer = NULL;
*payload = NULL;
*payload_len = 0;
ret = CL_ERROR;
keylen = BN_num_bytes(x509->n);
bnctx = BN_CTX_new();
if (!bnctx)
goto done;
x = BN_new();
if (!x)
goto done;
MALLOC(d, keylen);
if (!BN_mod_exp(x, sig, x509->e, x509->n, bnctx)) {
cli_warnmsg("crtmgr_rsa_verify: verification failed: BN_mod_exp failed.\n");
goto done;
}
pad_size = BN_bn2bin(x, d);
if (pad_size < 0) {
cli_dbgmsg("crtmgr_rsa_verify: buffer too small.\n");
goto done;
}
ret = _padding_check_PKCS1_type_1(payload, payload_len, d, pad_size, keylen);
if (ret != CL_SUCCESS) {
cli_dbgmsg("crtmgr_rsa_verify: RSA_padding_check_PKCS1_type_1() failed\n");
goto done;
}
*buffer = d;
d = NULL;
ret = CL_SUCCESS;
done:
BN_CTX_free(bnctx);
BN_free(x);
free(d);
return ret;
}
static int crtmgr_rsa_verify(cli_crt *x509, BIGNUM *sig, cli_crt_hashtype hashtype, const uint8_t *refhash)
{
int keylen = BN_num_bytes(x509->n), siglen = BN_num_bytes(sig);
int j, objlen, hashlen;
uint8_t *d;
uint8_t *buff;
int len;
cl_error_t ret;
if (hashtype == CLI_SHA1RSA) {
hashlen = SHA1_HASH_SIZE;
@ -303,132 +468,100 @@ static int crtmgr_rsa_verify(cli_crt *x509, fp_int *sig, cli_crt_hashtype hashty
return 1;
}
fp_init(&x);
if (MAX(keylen, siglen) - MIN(keylen, siglen) > 1) {
cli_dbgmsg("crtmgr_rsa_verify: keylen and siglen differ by more than one\n");
return 1;
}
ret = crtmgr_get_recov_data(sig, x509, &buff, &d, &len);
if (ret != CL_SUCCESS)
return 1;
do {
if (MAX(keylen, siglen) - MIN(keylen, siglen) > 1) {
cli_dbgmsg("crtmgr_rsa_verify: keylen and siglen differ by more than one\n");
j = 0;
if (len <= hashlen) {
cli_dbgmsg("crtmgr_rsa_verify: encountered len less than hashlen\n");
break;
}
if ((ret = fp_exptmod(sig, &x509->e, &x509->n, &x))) {
cli_warnmsg("crtmgr_rsa_verify: verification failed: fp_exptmod failed with %d\n", ret);
/* hash is asn1 der encoded */
/* SEQ { SEQ { OID, NULL }, OCTET STRING */
if (len < 2 || d[j] != 0x30 || d[j + 1] != len - 2) {
cli_dbgmsg("crtmgr_rsa_verify: unexpected hash to be ASN1 DER encoded.\n");
break;
}
if (fp_unsigned_bin_size(&x) != keylen - 1) {
cli_dbgmsg("crtmgr_rsa_verify: keylen-1 doesn't match expected size of exptmod result\n");
break;
}
if (((unsigned int)fp_unsigned_bin_size(&x)) > sizeof(d)) {
cli_dbgmsg("crtmgr_rsa_verify: exptmod result would overrun working buffer\n");
len -= 2;
j += 2;
if (len < 2 || d[j] != 0x30) {
cli_dbgmsg("crtmgr_rsa_verify: expected SEQUENCE at beginning of cert AlgorithmIdentifier\n");
break;
}
fp_to_unsigned_bin(&x, d);
objlen = d[j + 1];
if (*d != 1) { /* block type 1 */
cli_dbgmsg("crtmgr_rsa_verify: expected block type 1 at d[0]\n");
len -= 2;
j += 2;
if (len < objlen) {
cli_dbgmsg("crtmgr_rsa_verify: key length mismatch in ASN1 DER hash encoding\n");
break;
}
keylen -= 1; /* 0xff padding */
for (j = 1; j < keylen - 2; j++)
if (d[j] != 0xff)
break;
if (j == keylen - 2) {
cli_dbgmsg("crtmgr_rsa_verify: only encountered 0xFF padding parsing cert\n");
break;
}
if (d[j] != 0) { /* 0x00 separator */
cli_dbgmsg("crtmgr_rsa_verify: expected 0x00 separator\n");
break;
}
j++;
keylen -= j; /* asn1 size */
if (keylen < hashlen) {
cli_dbgmsg("crtmgr_rsa_verify: encountered keylen less than hashlen\n");
break;
}
if (keylen > hashlen) {
/* hash is asn1 der encoded */
/* SEQ { SEQ { OID, NULL }, OCTET STRING */
if (keylen < 2 || d[j] != 0x30 || d[j + 1] + 2 != keylen) {
cli_dbgmsg("crtmgr_rsa_verify: unexpected hash to be ASN1 DER encoded\n");
if (objlen == 9) {
// Check for OID type indicating a length of 5, OID_sha1, and the NULL type/value
if (hashtype != CLI_SHA1RSA || memcmp(&d[j], "\x06\x05" OID_sha1 "\x05\x00", 9)) {
cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
break;
}
keylen -= 2;
j += 2;
if (keylen < 2 || d[j] != 0x30) {
cli_dbgmsg("crtmgr_rsa_verify: expected SEQUENCE at beginning of cert AlgorithmIdentifier\n");
} else if (objlen == 12) {
// Check for OID type indicating a length of 8, OID_md5, and the NULL type/value
if (hashtype != CLI_MD5RSA || memcmp(&d[j], "\x06\x08" OID_md5 "\x05\x00", 12)) {
cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
break;
}
objlen = d[j + 1];
keylen -= 2;
j += 2;
if (keylen < objlen) {
cli_dbgmsg("crtmgr_rsa_verify: key length mismatch in ASN1 DER hash encoding\n");
break;
}
if (objlen == 9) {
// Check for OID type indicating a length of 5, OID_sha1, and the NULL type/value
if (hashtype != CLI_SHA1RSA || memcmp(&d[j], "\x06\x05" OID_sha1 "\x05\x00", 9)) {
cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
} else if (objlen == 13) {
if (hashtype == CLI_SHA256RSA) {
// Check for OID type indicating a length of 9, OID_sha256, and the NULL type/value
if (0 != memcmp(&d[j], "\x06\x09" OID_sha256 "\x05\x00", 13)) {
cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA256 hash\n");
break;
}
} else if (objlen == 12) {
// Check for OID type indicating a length of 8, OID_md5, and the NULL type/value
if (hashtype != CLI_MD5RSA || memcmp(&d[j], "\x06\x08" OID_md5 "\x05\x00", 12)) {
cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
} else if (hashtype == CLI_SHA384RSA) {
// Check for OID type indicating a length of 9, OID_sha384, and the NULL type/value
if (0 != memcmp(&d[j], "\x06\x09" OID_sha384 "\x05\x00", 13)) {
cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA384 hash\n");
break;
}
} else if (objlen == 13) {
if (hashtype == CLI_SHA256RSA) {
// Check for OID type indicating a length of 9, OID_sha256, and the NULL type/value
if (0 != memcmp(&d[j], "\x06\x09" OID_sha256 "\x05\x00", 13)) {
cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA256 hash\n");
break;
}
} else if (hashtype == CLI_SHA384RSA) {
// Check for OID type indicating a length of 9, OID_sha384, and the NULL type/value
if (0 != memcmp(&d[j], "\x06\x09" OID_sha384 "\x05\x00", 13)) {
cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA384 hash\n");
break;
}
} else if (hashtype == CLI_SHA512RSA) {
// Check for OID type indicating a length of 9, OID_sha512, and the NULL type/value
if (0 != memcmp(&d[j], "\x06\x09" OID_sha512 "\x05\x00", 13)) {
cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA512 hash\n");
break;
}
} else {
cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
} else if (hashtype == CLI_SHA512RSA) {
// Check for OID type indicating a length of 9, OID_sha512, and the NULL type/value
if (0 != memcmp(&d[j], "\x06\x09" OID_sha512 "\x05\x00", 13)) {
cli_dbgmsg("crtmgr_rsa_verify: invalid AlgorithmIdentifier block for SHA512 hash\n");
break;
}
} else {
cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
break;
}
keylen -= objlen;
j += objlen;
if (keylen < 2 || d[j] != 0x04 || d[j + 1] != hashlen) {
cli_dbgmsg("crtmgr_rsa_verify: hash length mismatch in ASN1 DER hash encoding\n");
break;
}
keylen -= 2;
j += 2;
if (keylen != hashlen) {
cli_dbgmsg("crtmgr_rsa_verify: extra data in the ASN1 DER hash encoding\n");
break;
}
} else {
cli_errmsg("crtmgr_rsa_verify: FIXME ACAB - CRYPTO MISSING?\n");
break;
}
len -= objlen;
j += objlen;
if (len < 2 || d[j] != 0x04 || d[j + 1] != hashlen) {
cli_dbgmsg("crtmgr_rsa_verify: hash length mismatch in ASN1 DER hash encoding\n");
break;
}
j += 2;
len -= 2;
if (len != hashlen) {
cli_dbgmsg("crtmgr_rsa_verify: extra data in the ASN1 DER hash encoding\n");
break;
}
if (memcmp(&d[j], refhash, hashlen)) {
// This is a common error case if we are using crtmgr_rsa_verify to
// determine whether we've found the right issuer certificate based
@ -438,10 +571,12 @@ static int crtmgr_rsa_verify(cli_crt *x509, fp_int *sig, cli_crt_hashtype hashty
break;
}
free(buff);
return 0;
} while (0);
free(buff);
return 1;
}
@ -469,7 +604,7 @@ cli_crt *crtmgr_verify_crt(crtmgr *m, cli_crt *x509)
if (i->certSign &&
!i->isBlocked &&
!memcmp(i->subject, x509->issuer, sizeof(i->subject)) &&
!crtmgr_rsa_verify(i, &x509->sig, x509->hashtype, x509->tbshash)) {
!crtmgr_rsa_verify(i, x509->sig, x509->hashtype, x509->tbshash)) {
int curscore;
if ((x509->codeSign & i->codeSign) == x509->codeSign && (x509->timeSign & i->timeSign) == x509->timeSign)
return i;
@ -493,16 +628,18 @@ 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)
{
cli_crt *i;
fp_int sig;
BIGNUM *sig;
if (signature_len < 1024 / 8 || signature_len > 4096 / 8 + 1) {
cli_dbgmsg("crtmgr_verify_pkcs7: unsupported sig len: %u\n", signature_len);
return NULL;
}
fp_init(&sig);
sig = BN_new();
if (!sig)
return NULL;
fp_read_unsigned_bin(&sig, signature, signature_len);
BN_bin2bn(signature, signature_len, sig);
for (i = m->crts; i; i = i->next) {
if (vrfytype == VRFY_CODE && !i->codeSign)
@ -511,13 +648,13 @@ 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))) {
if (!crtmgr_rsa_verify(i, &sig, hashtype, refhash)) {
if (!crtmgr_rsa_verify(i, sig, hashtype, refhash)) {
break;
}
cli_dbgmsg("crtmgr_verify_pkcs7: found cert with matching issuer and serial but RSA verification failed\n");
}
}
BN_free(sig);
return i;
}

View file

@ -23,8 +23,8 @@
#define __CRTMGR_H
#include <time.h>
#include "bignum.h"
#include <stdbool.h>
#include <openssl/bn.h>
typedef enum { CLI_HASHTYPE_ANY, /* used by crts added from .CRB rules */
CLI_SHA1RSA,
@ -63,9 +63,9 @@ typedef struct cli_crt_t {
* so it must have at least enough space for the largest hash in
* cli_crt_hashtype */
uint8_t tbshash[SHA512_HASH_SIZE];
fp_int n;
fp_int e;
fp_int sig;
BIGNUM *n;
BIGNUM *e;
BIGNUM *sig;
time_t not_before;
time_t not_after;
cli_crt_hashtype hashtype;
@ -82,11 +82,11 @@ typedef struct {
unsigned int items;
} crtmgr;
void cli_crt_init(cli_crt *x509);
int cli_crt_init(cli_crt *x509);
void cli_crt_clear(cli_crt *x509);
void crtmgr_init(crtmgr *m);
void crtmgr_free(crtmgr *m);
int crtmgr_add(crtmgr *m, cli_crt *x509);
bool crtmgr_add(crtmgr *m, cli_crt *x509);
cli_crt *crtmgr_lookup(crtmgr *m, cli_crt *x509);
cli_crt *crtmgr_block_list_lookup(crtmgr *m, cli_crt *x509);
cli_crt *crtmgr_trust_list_lookup(crtmgr *m, cli_crt *x509, int crb_crts_only);

View file

@ -30,12 +30,12 @@
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <openssl/bn.h>
#include "clamav.h"
#include "others.h"
#include "dsig.h"
#include "str.h"
#include "bignum.h"
#ifndef _WIN32
#include <sys/socket.h>
@ -81,37 +81,83 @@ static char cli_ndecode(unsigned char value)
return -1;
}
static unsigned char *cli_decodesig(const char *sig, unsigned int plen, fp_int e, fp_int n)
static unsigned char *cli_decodesig(const char *sig, unsigned int plen, BIGNUM *e, BIGNUM *n)
{
int i, slen = strlen(sig), dec;
unsigned char *plain;
fp_int r, p, c;
unsigned char *plain = NULL, *ret_sig = NULL;
BIGNUM *r = NULL, *p = NULL, *c = NULL;
BN_CTX *bn_ctx;
unsigned int bn_bytes;
;
fp_init(&r);
fp_init(&c);
r = BN_new();
if (!r) {
goto done;
}
p = BN_new();
if (!p) {
goto done;
}
c = BN_new();
if (!c) {
goto done;
}
bn_ctx = BN_CTX_new();
if (!bn_ctx) {
goto done;
}
BN_zero(c);
for (i = 0; i < slen; i++) {
if ((dec = cli_ndecode(sig[i])) < 0) {
return NULL;
goto done;
}
if (!BN_set_word(r, dec)) {
goto done;
}
if (!BN_lshift(r, r, 6 * i)) {
goto done;
}
fp_set(&r, dec);
fp_mul_2d(&r, 6 * i, &r);
fp_add(&r, &c, &c);
}
plain = (unsigned char *)cli_calloc(plen + 1, sizeof(unsigned char));
if (!BN_add(c, c, r)) {
goto done;
}
}
if (!BN_mod_exp(p, c, e, n, bn_ctx)) {
goto done;
}
bn_bytes = BN_num_bytes(p);
/* Sometimes the size of the resulting BN (128) is larger than the expected
* length (16). The result does not match in this case. Instead of
* allocating memory and filling it, we fail early.
*/
if (plen < bn_bytes) {
cli_errmsg("cli_decodesig: Resulting signature too large (%d vs %d).\n",
bn_bytes, plen);
goto done;
}
plain = cli_calloc(plen, sizeof(unsigned char));
if (!plain) {
cli_errmsg("cli_decodesig: Can't allocate memory for 'plain'\n");
return NULL;
goto done;
}
fp_init(&p);
fp_exptmod(&c, &e, &n, &p); /* plain = cipher^e mod n */
fp_set(&c, 256);
for (i = plen - 1; i >= 0; i--) { /* reverse */
fp_div(&p, &c, &p, &r);
plain[i] = MP_GET(&r);
if (!BN_bn2bin(p, plain)) {
goto done;
}
return plain;
ret_sig = plain;
plain = NULL;
done:
BN_free(r);
BN_free(p);
BN_free(c);
BN_CTX_free(bn_ctx);
free(plain);
return ret_sig;
}
char *cli_getdsig(const char *host, const char *user, const unsigned char *data, unsigned int datalen, unsigned short mode)
@ -228,41 +274,55 @@ char *cli_getdsig(const char *host, const char *user, const unsigned char *data,
return strdup(pt);
}
int cli_versig(const char *md5, const char *dsig)
cl_error_t cli_versig(const char *md5, const char *dsig)
{
fp_int n, e;
char *pt, *pt2;
BIGNUM *n = NULL, *e = NULL;
char *pt = NULL, *pt2 = NULL;
int ret;
ret = CL_EMEM;
n = BN_new();
if (!n)
goto done;
e = BN_new();
if (!e)
goto done;
ret = CL_EVERIFY;
if (!BN_dec2bn(&e, CLI_ESTR))
goto done;
if (!BN_dec2bn(&n, CLI_NSTR))
goto done;
if (strlen(md5) != 32 || !isalnum(md5[0])) {
/* someone is trying to fool us with empty/malformed MD5 ? */
cli_errmsg("SECURITY WARNING: MD5 basic test failure.\n");
return CL_EVERIFY;
goto done;
}
fp_init(&n);
fp_read_radix(&n, CLI_NSTR, 10);
fp_init(&e);
fp_read_radix(&e, CLI_ESTR, 10);
if (!(pt = (char *)cli_decodesig(dsig, 16, e, n))) {
return CL_EVERIFY;
}
if (!(pt = (char *)cli_decodesig(dsig, 16, e, n)))
goto done;
pt2 = cli_str2hex(pt, 16);
free(pt);
cli_dbgmsg("cli_versig: Decoded signature: %s\n", pt2);
if (strncmp(md5, pt2, 32)) {
cli_dbgmsg("cli_versig: Signature doesn't match.\n");
free(pt2);
return CL_EVERIFY;
goto done;
}
free(pt2);
cli_dbgmsg("cli_versig: Digital signature is correct.\n");
return CL_SUCCESS;
ret = CL_SUCCESS;
done:
free(pt);
free(pt2);
BN_free(n);
BN_free(e);
return ret;
}
#define HASH_LEN 32
@ -275,21 +335,39 @@ int cli_versig2(const unsigned char *sha256, const char *dsig_str, const char *n
unsigned char mask[BLK_LEN], data[BLK_LEN], final[8 + 2 * HASH_LEN], c[4];
unsigned int i, rounds;
void *ctx;
fp_int n, e;
BIGNUM *n, *e;
int ret;
fp_init(&e);
fp_read_radix(&e, e_str, 10);
fp_init(&n);
fp_read_radix(&n, n_str, 10);
n = BN_new();
e = BN_new();
if (!n || !e) {
ret = CL_EMEM;
goto done;
}
ret = CL_EVERIFY;
if (!BN_dec2bn(&e, e_str))
goto done;
if (!BN_dec2bn(&n, n_str))
goto done;
decoded = cli_decodesig(dsig_str, PAD_LEN, e, n);
if (!decoded)
return CL_EVERIFY;
if (!decoded) {
ret = CL_EVERIFY;
goto done;
}
if (decoded[PAD_LEN - 1] != 0xbc) {
free(decoded);
return CL_EVERIFY;
ret = CL_EVERIFY;
}
BN_free(n);
BN_free(e);
n = NULL;
e = NULL;
memcpy(mask, decoded, BLK_LEN);
memcpy(digest2, &decoded[BLK_LEN], HASH_LEN);
@ -337,4 +415,9 @@ int cli_versig2(const unsigned char *sha256, const char *dsig_str, const char *n
cl_finish_hash(ctx, digest1);
return memcmp(digest1, digest2, HASH_LEN) ? CL_EVERIFY : CL_SUCCESS;
done:
BN_free(n);
BN_free(e);
return ret;
}

View file

@ -29,7 +29,7 @@
#include "clamav-config.h"
#endif
int cli_versig(const char *md5, const char *dsig);
cl_error_t cli_versig(const char *md5, const char *dsig);
int cli_versig2(const unsigned char *sha256, const char *dsig_str, const char *n_str, const char *e_str);
/**

View file

@ -3319,9 +3319,7 @@ static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio
char *tokens[CRT_TOKENS + 1];
size_t line = 0, tokens_count;
cli_crt ca;
int ret = CL_SUCCESS;
char *pubkey = NULL;
const uint8_t exp[] = "\x01\x00\x01";
int ret = CL_SUCCESS;
if (!(engine->dconf->pe & PE_CONF_CERTS)) {
cli_dbgmsg("cli_loadcrt: Ignoring .crb sigs due to DCONF configuration\n");
@ -3333,7 +3331,10 @@ static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio
return ret;
}
cli_crt_init(&ca);
if (cli_crt_init(&ca) < 0) {
cli_dbgmsg("cli_loadcrt: No mem for CA init.\n");
return CL_EMEM;
}
memset(ca.issuer, 0xca, sizeof(ca.issuer));
while (cli_dbgets(buffer, FILEBUFF, fs, dbio)) {
@ -3411,25 +3412,16 @@ static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio
goto done;
}
pubkey = cli_hex2str(tokens[4]);
if (!pubkey) {
if (BN_hex2bn(&ca.n, tokens[4]) == 0) {
cli_errmsg("cli_loadcrt: line %u: Cannot convert public key to binary string\n", (unsigned int)line);
ret = CL_EMALFDB;
goto done;
}
/*
* tokens[4] is the public key. having a length that is too
* long causes an out of bounds read in the this call.
*/
if ((strlen(tokens[4]) / 2) >= (FP_MAX_SIZE / 8)) {
cli_errmsg("cli_loadcrt: line %u: Public key too long.\nNOTE: If this is actually a valid key length, recompile with a larger FP_MAX_SIZE (currently %d).\n", (unsigned int)line, FP_MAX_SIZE);
ret = CL_EMALFDB;
/* Set the RSA exponent of 65537 */
if (!BN_set_word(ca.e, 65537)) {
cli_errmsg("cli_loadcrt: Cannot set the exponent.\n");
goto done;
}
fp_read_unsigned_bin(&(ca.n), (const unsigned char *)pubkey, strlen(tokens[4]) / 2);
fp_read_unsigned_bin(&(ca.e), exp, sizeof(exp) - 1);
switch (tokens[6][0]) {
case '1':
@ -3481,13 +3473,9 @@ static int cli_loadcrt(FILE *fs, struct cl_engine *engine, struct cli_dbio *dbio
ca.hashtype = CLI_HASHTYPE_ANY;
crtmgr_add(&(engine->cmgr), &ca);
FREE(pubkey);
}
done:
FREE(pubkey);
cli_dbgmsg("Number of certs: %d\n", engine->cmgr.items);
cli_crt_clear(&ca);
return ret;

View file

@ -30,7 +30,6 @@
#include <ctype.h>
#include "clamav.h"
#include "textnorm.h"
#include "bignum.h"
int text_normalize_init(struct text_norm_state *state, unsigned char *out, size_t out_len)
{

View file

@ -52,7 +52,6 @@
#include "scanners.h"
#include "conv.h"
#include "xdp.h"
#include "bignum.h"
#include "filetypes.h"
static char *dump_xdp(cli_ctx *ctx, const char *start, size_t sz);