diff --git a/libclamav/CMakeLists.txt b/libclamav/CMakeLists.txt index 7defedcbd..df8636990 100644 --- a/libclamav/CMakeLists.txt +++ b/libclamav/CMakeLists.txt @@ -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} diff --git a/libclamav/asn1.c b/libclamav/asn1.c index 6bee8f01e..721297f8f 100644 --- a/libclamav/asn1.c +++ b/libclamav/asn1.c @@ -24,10 +24,10 @@ #endif #include +#include #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"); } diff --git a/libclamav/bignum.h b/libclamav/bignum.h deleted file mode 100644 index a1c6d6e7c..000000000 --- a/libclamav/bignum.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef BIGNUM_H_ -#define BIGNUM_H_ - -#if HAVE_CONFIG_H -#include "clamav-config.h" -#endif - -#if HAVE_SYSTEM_TOMSFASTMATH -#include -#else -#include "tomsfastmath/headers/tfm.h" -#endif - -#endif diff --git a/libclamav/crtmgr.c b/libclamav/crtmgr.c index 5a0c5085b..51c681dee 100644 --- a/libclamav/crtmgr.c +++ b/libclamav/crtmgr.c @@ -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; } diff --git a/libclamav/crtmgr.h b/libclamav/crtmgr.h index c9a680a91..89c7d29a6 100644 --- a/libclamav/crtmgr.h +++ b/libclamav/crtmgr.h @@ -23,8 +23,8 @@ #define __CRTMGR_H #include - -#include "bignum.h" +#include +#include 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); diff --git a/libclamav/dsig.c b/libclamav/dsig.c index d692212f7..6daec4293 100644 --- a/libclamav/dsig.c +++ b/libclamav/dsig.c @@ -30,12 +30,12 @@ #include #include #include +#include #include "clamav.h" #include "others.h" #include "dsig.h" #include "str.h" -#include "bignum.h" #ifndef _WIN32 #include @@ -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; } diff --git a/libclamav/dsig.h b/libclamav/dsig.h index 9a1c04d92..4a38aea59 100644 --- a/libclamav/dsig.h +++ b/libclamav/dsig.h @@ -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); /** diff --git a/libclamav/readdb.c b/libclamav/readdb.c index 84ffb3295..007aa4ea2 100644 --- a/libclamav/readdb.c +++ b/libclamav/readdb.c @@ -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; diff --git a/libclamav/textnorm.c b/libclamav/textnorm.c index fa7075f29..55ef778ee 100644 --- a/libclamav/textnorm.c +++ b/libclamav/textnorm.c @@ -30,7 +30,6 @@ #include #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) { diff --git a/libclamav/xdp.c b/libclamav/xdp.c index f0e2fdde2..97eeecfc3 100644 --- a/libclamav/xdp.c +++ b/libclamav/xdp.c @@ -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);