]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Reject RSA DNSKEYs with oversize public exponents at parse time
authorOndřej Surý <ondrej@isc.org>
Thu, 30 Apr 2026 05:01:53 +0000 (07:01 +0200)
committerOndřej Surý <ondrej@isc.org>
Thu, 30 Apr 2026 08:55:42 +0000 (10:55 +0200)
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
lib/dns/opensslrsa_link.c
tests/dns/rsa_test.c

index 6b322ff3b395c0c47a9733742e8a7250f4863dac..c3843190bfc944d2595aec0c63ee85e49c90c336 100644 (file)
@@ -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);
index fde2211356b3a07835b89048fef95b474a0e2769..83364e1e9125cb0817cfe0e59c76b8e61c0a4c29 100644 (file)
@@ -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