[9.20] fix: usr: prevent malicious DNSSEC zones from exhausting validator CPU

A DNSSEC-signed zone could publish a DNSKEY with an unusually large
RSA public exponent and force any validator resolving names in that
zone to spend disproportionate CPU verifying signatures.  The
validator now rejects such DNSKEYs, matching the limit already
applied to keys read from files or HSMs.

Closes #5881

Backport of MR !11917

Merge branch 'backport-5881-rsa-exponent-keytrap-cpu-amplification-9.20' into 'bind-9.20'

See merge request isc-projects/bind9!11923
This commit is contained in:
Ondřej Surý 2026-04-30 13:24:00 +02:00
commit c425827743
2 changed files with 50 additions and 0 deletions

View file

@ -827,6 +827,9 @@ opensslrsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
if (c.e == NULL || c.n == NULL) {
DST_RET(ISC_R_NOMEMORY);
}
if (BN_num_bits(c.e) > RSA_MAX_PUBEXP_BITS) {
DST_RET(ISC_R_RANGE);
}
isc_buffer_forward(data, length);
key->key_size = BN_num_bits(c.n);

View file

@ -225,8 +225,55 @@ ISC_RUN_TEST_IMPL(isc_rsa_verify) {
dst_key_free(&key);
}
/* dst_key_fromdns rejects oversized RSA public exponents */
ISC_RUN_TEST_IMPL(isc_rsa_fromdns_oversized_exponent) {
isc_result_t result;
dns_fixedname_t fname;
dns_name_t *name;
dst_key_t *key = NULL;
isc_buffer_t buf;
unsigned char rdata[300] = { 0 };
size_t i = 0;
UNUSED(state);
name = dns_fixedname_initname(&fname);
isc_buffer_constinit(&buf, "rsa.", 4);
isc_buffer_add(&buf, 4);
result = dns_name_fromtext(name, &buf, NULL, 0, NULL);
assert_int_equal(result, ISC_R_SUCCESS);
/* DNSKEY rdata: flags(2) + proto(1) + alg(1) + key */
rdata[i++] = 0x01; /* flags hi (KSK) */
rdata[i++] = 0x00; /* flags lo */
rdata[i++] = 0x03; /* protocol */
rdata[i++] = DST_ALG_RSASHA256;
/* RSA wire key: e_bytes + e + n. Use a 6-byte (48-bit) e
* with a non-zero leading byte so it exceeds the 35-bit cap. */
rdata[i++] = 6;
rdata[i++] = 0x01;
rdata[i++] = 0x02;
rdata[i++] = 0x03;
rdata[i++] = 0x04;
rdata[i++] = 0x05;
rdata[i++] = 0x06;
/* 256 bytes of arbitrary modulus (2048-bit). */
for (size_t j = 0; j < 256; j++) {
rdata[i++] = 0xAB;
}
isc_buffer_init(&buf, rdata, i);
isc_buffer_add(&buf, i);
result = dst_key_fromdns(name, dns_rdataclass_in, &buf, mctx, &key);
assert_int_equal(result, ISC_R_RANGE);
assert_null(key);
}
ISC_TEST_LIST_START
ISC_TEST_ENTRY_CUSTOM(isc_rsa_verify, setup_test, teardown_test)
ISC_TEST_ENTRY_CUSTOM(isc_rsa_fromdns_oversized_exponent, setup_test,
teardown_test)
ISC_TEST_LIST_END
ISC_TEST_MAIN