From: Ondřej Surý Date: Thu, 30 Apr 2026 05:01:53 +0000 (+0200) Subject: Reject RSA DNSKEYs with oversize public exponents at parse time X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ca6ea809b0faff3ea59aeae2a00d580842f70f4b;p=thirdparty%2Fbind9.git Reject RSA DNSKEYs with oversize public exponents at parse time The wire-format RSA DNSKEY parser was the only key path with no upper bound on the public exponent — opensslrsa_parse and opensslrsa_fromlabel already cap at RSA_MAX_PUBEXP_BITS. An attacker-controlled DNSKEY could therefore force a validator to compute s^e mod n with e up to ~|n| bits, amplifying every verify by ~120x for typical 2048-bit moduli (OpenSSL itself only caps the exponent for moduli above 3072 bits). Apply the same bit-count cap to wire-format keys. Assisted-by: Claude:claude-opus-4-7 (cherry picked from commit ab8c1a77e06bf7fc969ad1fac20c3ae5a96257e5) --- diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c index b92e1bfc889..8595f13f684 100644 --- a/lib/dns/opensslrsa_link.c +++ b/lib/dns/opensslrsa_link.c @@ -711,6 +711,9 @@ opensslrsa_fromdns(dst_key_t *key, isc_buffer_t *data) { if (e == NULL || n == NULL) { DST_RET(ISC_R_NOMEMORY); } + if (BN_num_bits(e) > RSA_MAX_PUBEXP_BITS) { + DST_RET(ISC_R_RANGE); + } key->key_size = BN_num_bits(n); diff --git a/tests/dns/rsa_test.c b/tests/dns/rsa_test.c index 7d84512bacf..147f5c85f1f 100644 --- a/tests/dns/rsa_test.c +++ b/tests/dns/rsa_test.c @@ -226,8 +226,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