From: Luca Boccassi Date: Wed, 27 May 2026 22:16:08 +0000 (+0100) Subject: resolve: validate short RSA DNSKEY blobs X-Git-Tag: v261-rc3~60 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=004401fd061b222f4ee374263f5fa65d08609fc4;p=thirdparty%2Fsystemd.git resolve: validate short RSA DNSKEY blobs Reject malformed RSA DNSKEY data before reading the extended exponent header, and add a regression test. Co-developed-by: GitHub Copilot (GPT 5.5) --- diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 017c370b0eb..f33bba28ff1 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -141,10 +141,15 @@ static int dnssec_rsa_verify( assert(rrsig); assert(dnskey); + if (dnskey->dnskey.key_size < 1) + return -EINVAL; + if (*(uint8_t*) dnskey->dnskey.key == 0) { /* exponent is > 255 bytes long */ - exponent = (uint8_t*) dnskey->dnskey.key + 3; + if (dnskey->dnskey.key_size < 3) + return -EINVAL; + exponent_size = ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) | ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]); @@ -155,13 +160,12 @@ static int dnssec_rsa_verify( if (3 + exponent_size >= dnskey->dnskey.key_size) return -EINVAL; + exponent = (uint8_t*) dnskey->dnskey.key + 3; modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size; modulus_size = dnskey->dnskey.key_size - 3 - exponent_size; } else { /* exponent is <= 255 bytes long */ - - exponent = (uint8_t*) dnskey->dnskey.key + 1; exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0]; if (exponent_size <= 0) @@ -170,6 +174,7 @@ static int dnssec_rsa_verify( if (1 + exponent_size >= dnskey->dnskey.key_size) return -EINVAL; + exponent = (uint8_t*) dnskey->dnskey.key + 1; modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size; modulus_size = dnskey->dnskey.key_size - 1 - exponent_size; } diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index eaa4ef99598..f6ea6214f42 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -521,6 +521,59 @@ TEST(dnssec_verify_rrset) { assert_se(result == DNSSEC_VALIDATED); } +TEST(dnssec_verify_rrset_invalid_rsa_dnskey) { + static const uint8_t signature_blob[] = { 0x00 }; + static const uint8_t dnskey_blob[] = { 0x00 }; + + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *a = NULL, *rrsig = NULL, *dnskey = NULL; + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + DnssecResult result; + + ASSERT_NOT_NULL(a = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "example.com.")); + + a->a.in_addr.s_addr = inet_addr("192.0.2.1"); + + ASSERT_NOT_NULL(dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "example.com.")); + + dnskey->dnskey.flags = DNSKEY_FLAG_ZONE_KEY; + dnskey->dnskey.protocol = 3; + dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256; + dnskey->dnskey.key_size = sizeof(dnskey_blob); + ASSERT_NOT_NULL(dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob))); + + ASSERT_NOT_NULL(rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "example.com.")); + + rrsig->rrsig.type_covered = DNS_TYPE_A; + rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256; + rrsig->rrsig.labels = 2; + rrsig->rrsig.original_ttl = 3600; + rrsig->rrsig.expiration = 2000000000; + rrsig->rrsig.inception = 1000000000; + rrsig->rrsig.key_tag = dnssec_keytag(dnskey, false); + ASSERT_NOT_NULL(rrsig->rrsig.signer = strdup("example.com.")); + rrsig->rrsig.signature_size = sizeof(signature_blob); + ASSERT_NOT_NULL(rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size)); + + ASSERT_GT(dnssec_key_match_rrsig(a->key, rrsig), 0); + ASSERT_GT(dnssec_rrsig_match_dnskey(rrsig, dnskey, false), 0); + + ASSERT_NOT_NULL(answer = dns_answer_new(1)); + ASSERT_OK(dns_answer_add(answer, a, 0, DNS_ANSWER_AUTHENTICATED, NULL)); + + ASSERT_ERROR(dnssec_verify_rrset(answer, a->key, rrsig, dnskey, + rrsig->rrsig.inception * USEC_PER_SEC, &result), + EINVAL); + + dnskey->dnskey.key = mfree(dnskey->dnskey.key); + dnskey->dnskey.key_size = 0; + rrsig->rrsig.key_tag = dnssec_keytag(dnskey, false); + + ASSERT_GT(dnssec_rrsig_match_dnskey(rrsig, dnskey, false), 0); + ASSERT_ERROR(dnssec_verify_rrset(answer, a->key, rrsig, dnskey, + rrsig->rrsig.inception * USEC_PER_SEC, &result), + EINVAL); +} + TEST(dnssec_verify_rrset2) { static const uint8_t signature_blob[] = { 0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11,