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=ab8c1a77e06bf7fc969ad1fac20c3ae5a96257e5;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 --- diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c index 6b322ff3b39..c3843190bfc 100644 --- a/lib/dns/opensslrsa_link.c +++ b/lib/dns/opensslrsa_link.c @@ -462,6 +462,9 @@ opensslrsa_fromdns(dst_key_t *key, isc_buffer_t *data) { if (c.e == NULL || c.n == NULL) { CLEANUP(ISC_R_NOMEMORY); } + if (BN_num_bits(c.e) > RSA_MAX_PUBEXP_BITS) { + CLEANUP(ISC_R_RANGE); + } isc_buffer_forward(data, length); key->key_size = BN_num_bits(c.n); diff --git a/tests/dns/rsa_test.c b/tests/dns/rsa_test.c index fde2211356b..83364e1e912 100644 --- a/tests/dns/rsa_test.c +++ b/tests/dns/rsa_test.c @@ -204,8 +204,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); + 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, isc_g_mctx, + &key); + assert_int_equal(result, ISC_R_RANGE); + assert_null(key); +} + ISC_TEST_LIST_START ISC_TEST_ENTRY(isc_rsa_verify) +ISC_TEST_ENTRY(isc_rsa_fromdns_oversized_exponent) ISC_TEST_LIST_END ISC_TEST_MAIN