Commit graph

46142 commits

Author SHA1 Message Date
Evan Hunt
d2350fc546 fix: usr: Unvalidated opt-out NSEC3 could be accepted in insecurity proof
When determining whether an insecure delegation is legitimate, NSEC3 opt-out records which had not yet passed validation could be used. This has been fixed.

Closes #5970

Merge branch '5970-pending-nsec3' into 'main'

See merge request isc-projects/bind9!12283
2026-07-03 07:01:30 +00:00
Evan Hunt
abd274c1fd Merge "nsec3_wrong_zone" into "dnssec_nsec3"
These tests are similar in structure and topic, and can be merged.
2026-07-02 23:07:26 -07:00
Alessio Podda
fdecacf07a Reproducer for #5970 acceptance of unvalidated NSEC3
Add "dnssec_nsec3" system test.

Co-Authored-By: Evan Hunt <each@isc.org>
2026-07-02 23:07:26 -07:00
Evan Hunt
1bd458d3ba Unvalidated opt-out NSEC3 could be accepted in insecurity proof
Ignore NSEC3 records that failed in the sub-validator when determining
whether an insecure delegation is legitimate.

Fixes: isc-projects/bind9#5970
2026-07-02 23:07:24 -07:00
Ondřej Surý
aefb8570a2 fix: usr: Fix DNSSEC validation failures for names under an apex DNAME
DNSSEC validation could fail with SERVFAIL for names covered by a DNAME
at the apex of a signed zone, unless the zone's keys were already validated
in the cache. This regression was introduced by the recent fix for resolver
stalls on CNAME responses to DS queries.

Closes #6176

Merge branch '6176-validator-apex-dname' into 'main'

See merge request isc-projects/bind9!12353
2026-07-02 16:47:22 +02:00
Ondřej Surý
38992e02bb Add a system test resolving through a signed apex DNAME
The dnssec system test signs a DNAME-at-apex zone but only ever
queried the apex directly; nothing resolved a name under the DNAME
through the validating resolver, so a validator regression on that
path went unnoticed.

Assisted-by: Claude:claude-fable-5
2026-07-02 16:47:14 +02:00
Ondřej Surý
057ae0adb8 Restrict the alias-chain deadlock check to chaining CNAMEs
check_deadlock() aborted any fetch whose name equals the owner of a
chaining rdataset, assuming nothing the validator needs can live at an
alias.  That is true for a CNAME, but a DNAME aliases only the names
below its owner: with a DNAME at a zone apex, the DNSKEY signing the
DNAME lives at the owner name itself, so every answer synthesized from
a signed apex DNAME failed validation whenever that key was not
already validated in the cache.

Chaining CNAMEs, including those synthesized from a DNAME, still cover
the self-join case the check was added for.
2026-07-02 16:47:14 +02:00
Nicki Křížek
c468f9abe1 fix: test: Support serving signed child zone from its parent's nameserver
When a signed zone is served by the same nameserver instance as
its parent, the child's dnssec-signzone has already written
dsset-<child>. into that directory. Don't attempt to copy the dsset if
the destination and source files are the same.

Assisted-by: Claude:claude-opus-4-8

Merge branch 'nicki/serve-signed-child-same-ns' into 'main'

See merge request isc-projects/bind9!12354
2026-07-02 16:42:15 +02:00
Nicki Křížek
246cca357b Support serving signed child zone from its parent's nameserver
When a signed zone is served by the same nameserver instance as
its parent, the child's dnssec-signzone has already written
dsset-<child>. into that directory. Don't attempt to copy the dsset if
the destination and source files are the same.

Assisted-by: Claude:claude-opus-4-8
2026-07-02 16:00:55 +02:00
Michał Kępień
8728b2a999 chg: test: Update AsyncDnsServer-related test cookbook parts
Add practical tips about specific handler classes.  Mention some good
practices and point developers at existing code written in the desired
manner.  Document common pitfalls.  Suggest preferred approaches for
splitting up complex response handling code.

Merge branch 'michal/update-asyncdnsserver-related-test-cookbook-parts' into 'main'

See merge request isc-projects/bind9!12260
2026-07-02 15:09:50 +02:00
Michał Kępień
edd765a092
Move ans.py-related information to README.md
COOKBOOK.md is supposed to be minimal and heavy on examples, so move the
lengthy section about implementing custom ans.py servers from
COOKBOOK.md to README.md.
2026-07-02 15:07:40 +02:00
Michał Kępień
a48e5cd98d
Update AsyncDnsServer-related test cookbook parts
Add practical tips about specific handler classes.  Mention some good
practices and point developers at existing code written in the desired
manner.  Document common pitfalls.  Suggest preferred approaches for
splitting up complex response handling code.
2026-07-02 15:07:40 +02:00
Nicki Křížek
cbc41caafa chg: test: Unify system test key and algorithm handling
This is a followup from https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/11807.

De-duplicates the key material handling and moves algorithm-related things into a dedicated shared module.

Merge branch 'nicki/pytest-key-reconcile' into 'main'

See merge request isc-projects/bind9!12255
2026-07-02 14:51:52 +02:00
Nicki Křížek
4d76b7bd9e Move algorithm definitions into a top-level isctest.algorithms module
The Algorithm type, the per-algorithm constants, and the ALL_ALGORITHMS*
lookup tables are general DNSSEC key definitions used across isctest
package and the tests. Move them into a dedicated module to separate
these from the environment-specific setup that remains in
isctest.vars.algorithms.

Assisted-by: Claude:claude-opus-4-8
2026-07-02 14:51:41 +02:00
Nicki Křížek
ee73ba3bba Add private_key support to all ZoneKey implementations
Extend the ZoneKeyFile to read the file-backed private key and return it
in a format suitable for use with dnspython. Add ZoneKey.private_key
property to unify the interface.
2026-07-02 14:51:41 +02:00
Nicki Křížek
7f69a57021 Replace get_dnsalg() with kasp.Key.algorithm
The get_dnsalg() was just a compatibility layer for 9.18 which lacked
the Algorithm support - remove it in favor of using the .algorithm
property.

In order to properly support private OID algorithms, use the DST value
which is unique across all algorithms.

Also fix private_type_record(): the choice between the 5- and 7-byte
signing record depends on the DST value (256/257 for the private-OID
algorithms), not the on-wire number, which never reaches 256.

Assisted-by: Claude:claude-opus-4-8
2026-07-02 14:51:41 +02:00
Nicki Křížek
596e41553c Add NSEC3RSASHA1 to list of algorithms
This algorithm is deprecated and not currently used in our system tests,
but it should be in the list of all algorithms.
2026-07-02 14:51:41 +02:00
Nicki Křížek
55cd4a1e11 Move dnskey method from kasp.Key to zone.FileZoneKey
Code move with one change - switch dnskey TTL from 300s (DEFAULT_TTL) to
3600s (DNSKEY_TTL).

Assisted-by: Claude:claude-opus-4-8
2026-07-02 14:51:41 +02:00
Nicki Křížek
35fc5ce8b4 Merge kasp.Key functionality into zone.FileZoneKey
Make zone.FileZoneKey the single representation of a file-backed key
(typically generated by dnssec-keygen). Move the common key-related
functionality into zone.FileZoneKey, and extend that functionality in
kasp.Key to also add state and timing related operations on top. Remove
duplicate into_ta() function.

Note that is_ksk() is implemented differently for kasp.Key: with the
metadata file available, the KSK status is loaded from that file, as it
indicates the authoritative policy decision which makes the key a KSK.
In zone.FileZoneKey which doesn't work with the metadata file, the KSK
status if inferred from the DNSKEY SEP flag - the best information
available for that class.

Assisted-by: Claude:claude-opus-4-8
2026-07-02 14:51:41 +02:00
Nicki Křížek
9dc039f735 Read DNSKEY TTL from kasp.Key.dnskey
Remove the redundant ttl() method. The DNSKEY RR already provides the
TTL.
2026-07-02 14:51:41 +02:00
Ondřej Surý
2762fe12a0 chg: dev: Replace query and inner client attribute bitfields with named bools
Replace the unsigned int attributes field in struct ns_query and the unsigned int attributes field in struct ns_client_inner with individual bool bitfields.

Merge branch 'ondrej/query-attributes-to-bitfields' into 'main'

See merge request isc-projects/bind9!11732
2026-07-02 13:27:27 +02:00
Ondřej Surý
430ce335ad Replace query and client attribute bitfield with named bools
Replace the unsigned int attributes field in struct ns_query with
individual bool bitfields.  This removes the NS_QUERYATTR_* constants
and the 12 accessor macros (USECACHE, RECURSIONOK, RECURSING, etc.)
from query.c, replacing all bit manipulation with direct bool access.

And replace the unsigned int attributes field in struct ns_client_inner
with individual bool bitfields.  This removes the NS_CLIENTATTR_*
constants and the accessor macros (TCP, WANTDNSSEC, etc.), replacing
all bit manipulation with direct bool access.
2026-07-02 13:27:11 +02:00
Ondřej Surý
03c495def6 fix: usr: Resolver could terminate unexpectedly when processing a malformed RRSIG
A recursive resolver could terminate unexpectedly when an authoritative
server returned a crafted RRSIG(RRSIG) record for an insecure zone. Such
records are now rejected.

Closes #6184

Merge branch '6184-reject-rrsig-covering-signature' into 'main'

See merge request isc-projects/bind9!12347
2026-07-02 13:27:00 +02:00
Ondřej Surý
6c968be47d Add a regression test for an RRSIG that covers a signature
The qpcache_rrsig_any test enumerated only meta-types as the covered
type, so an RRSIG covering RRSIG -- a non-meta signature type -- slipped
through the earlier meta-type hardening. Probe that case too.

Assisted-by: Claude:claude-opus-4-8
2026-07-02 13:26:48 +02:00
Ondřej Surý
9596a03d21 Reject an RRSIG that covers a signature type
An RRSIG whose Type-Covered field is RRSIG is not a meta-type, so it
passed the message parser, and for an insecure domain the resolver
cached it as a standalone signature. The QP cache pairs every RRSIG
header with the non-signature header it covers and never expects the
covered type to itself be a signature, so a signature covering a
signature broke that invariant and aborted named on a crafted response.
Reject the record in the parser, and tighten the cache precondition so
a positive signature header must cover a non-signature type.
2026-07-02 13:26:48 +02:00
Ondřej Surý
f5e5097fad fix: dev: Correct locator decoding for NID, L64, and L32 records
NID, L64, and L32 records were decoded incorrectly when converted into their
parsed structures, because the preference field was not skipped before the
locator.

Closes #6097

Merge branch '6097-nid-l64-l32-tostruct-consume' into 'main'

See merge request isc-projects/bind9!12348
2026-07-02 12:54:52 +02:00
Ondřej Surý
f28bea94e3
Add NID/L64/L32 round-trip coverage to the rdata unit test
These types had no entries in the rdata test table, so the
tostruct/fromstruct round-trip in check_struct_conversions() never ran
against them -- which is why the missing preference consume in their
tostruct routines went unnoticed for years. Add text and wire vectors
for all three.

Assisted-by: Claude:claude-fable-5
2026-07-02 12:16:27 +02:00
Ondřej Surý
5aa279bbea
Consume the preference before reading the locator in NID/L64/L32 tostruct
tostruct_nid() and tostruct_l64() read the 16-bit preference with the
non-consuming uint16_fromregion() and then memmove()'d the whole
remaining region -- still ten octets, still anchored at the preference
-- into the eight-octet nid[]/l64[] arrays. That folded the preference
into the first two locator octets and stored two octets past the end of
the array. tostruct_l32() shares the root cause: it read the 32-bit
locator from the same unconsumed offset, so the value was built from the
preference plus the first two locator octets.

Consume the two preference octets first, matching the sibling
tostruct_lp(), and assert the expected framing on the fixed-size types.
2026-07-02 12:16:27 +02:00
Evan Hunt
84556763a4 fix: usr: Don't evict DNSSEC-validated cache data on a CD=1 NXDOMAIN
When a client sent a query with the checking-disabled (CD) bit set and the
answer was NXDOMAIN, the resolver cached that unvalidated negative response and
discarded any DNSSEC-validated records it already held for the same name, even
though the validated data was more trustworthy. A single such response -
including a forged one - could flush validated records from the cache and force
the resolver to fetch them again. The resolver now checks the trust level of the
existing data first and leaves the cache unchanged when it is already validated.

Closes #5877

Merge branch '5877-cd-nxdomain' into 'main'

See merge request isc-projects/bind9!11946
2026-07-02 07:44:53 +00:00
Ondřej Surý
ddb6cb5c93 Add a system test for CD=1 NXDOMAIN cache protection
Cache a DNSSEC-validated A record, then make a CD=1 query elicit an
unvalidated NXDOMAIN for the name: the secure RRset must survive, and an
uncached-type query must not get the wrong RRset back.

Assisted-by: Claude:claude-opus-4-8
2026-07-01 23:56:50 -07:00
Evan Hunt
a7a90eb9d8 Check for secure data before caching CD=1 NXDOMAIN
An unvalidated NXDOMAIN (e.g. from a CD=1 query) marked every RRset at
the name ancient without checking trust, evicting DNSSEC-validated data.
Keep the cache unchanged when any existing RRset is already secure.

dns_ncache_add() now returns DNS_R_UNCHANGED for the rejected add;
negcache() serves a matching cached negative or the queried type, else
SERVFAIL (never the unrelated RRset the add bound), and rctx_ncache()
forwards it so the fetch fails fast.
2026-07-01 23:56:50 -07:00
Mark Andrews
b378071e06 fix: usr: Properly detect private records before copying
We were triggering an assertion when trying to copy a private record
to a buffer for modifying.  Extend the private type detection and
copy the contents after we have rejected invalid private records.

Closes #5857

Merge branch '5857-properly-detect-private-records-before-copying' into 'main'

See merge request isc-projects/bind9!11816
2026-07-02 11:10:32 +10:00
Mark Andrews
5d7387b185 Add DNS_PRIVATE_BUFFERSIZE and use it
The size of a private records is 1 byte more than the corresponding
NSEC3PARAM record.
2026-07-02 10:08:52 +10:00
Mark Andrews
7182a03b82 Properly detect private records before copying
We were triggering an assertion when trying to copy a private record
to a buffer for modifying.  Extend the private type detection and
copy the contents after we have rejected invalid private records.
2026-07-02 10:08:52 +10:00
Mark Andrews
5725c4191f Test oversized private record is properly ignored 2026-07-02 10:08:52 +10:00
Ondřej Surý
4fdc83bff0 chg: doc: Add non-IN RR classes to list of unsupported configurations
This adds a note to the security documentation that configuring
resources with non-Internet DNS classes (CHAOS, HESIOD, ...) is not
a supported configuration and could potentially cause issues.

Closes #5805

Merge branch '5805-add-clarification-on-non-IN-classes' into 'main'

See merge request isc-projects/bind9!11667
2026-07-01 13:06:10 +02:00
Nicki Křížek
fc06f799dc amend! Add non-IN RR classes to list of unsupported configurations
Document that non-IN RR class issues are out of CVE scope

CVE-2026-5946 covered assertion failures reachable only through the
handling of resource record classes other than Internet (IN).
Configuring zones or views with such classes is a supported feature;
document in the security assumptions that problems reachable only
through it cannot be the basis for CVE assignment.

Closes #5805

Assisted-by: Claude:claude-opus-4-8
2026-07-01 13:05:11 +02:00
Ondřej Surý
7b34d60833 Add non-IN RR classes to list of unsupported configurations
This adds a note to the security documentation that configuring
resources with non-Internet DNS classes (CHAOS, HESIOD, ...) is not
a supported configuration and could potentially cause issues.
2026-07-01 13:05:11 +02:00
Ondřej Surý
1b8bae505e fix: dev: Fix a memory leak when updating a zone with more than 32 DNSSEC keys
Applying changes to a signed zone — via DNS UPDATE or the inline-signing
raw-to-secure sync — leaked the surplus keys when the zone's key
directory held more than 32, slowly growing named's memory use.

Closes #6051

Merge branch '6051-find-zone-keys-key-leak' into 'main'

See merge request isc-projects/bind9!12328
2026-07-01 09:23:10 +02:00
Ondřej Surý
3d0c452cad Add a regression test for the find_zone_keys() key leak
Generate more than DNS_MAXZONEKEYS distinct matching private keys for a
zone and call find_zone_keys() through it.  The keys past the limit must
be released; the default memory context's leak check, armed with
isc_mem_checkdestroyed(), fails the test if any are abandoned.

Assisted-by: Claude:claude-opus-4-8
2026-07-01 16:45:56 +10:00
Ondřej Surý
a957dd13fa Stop leaking DNSSEC keys past the zone key limit
find_zone_keys() collects every matching private key into a local list,
hands the first DNS_MAXZONEKEYS keys to the caller, and frees the rest.
On overflow it destroyed only the first surplus key before breaking out
of the loop, so any keys after it stayed linked on the local list and
were lost when the function returned.

Unlink and destroy every list entry, transferring a key to the caller
only while under the limit.  No entry is left behind, so a zone with
more than DNS_MAXZONEKEYS matching keys no longer leaks memory on each
signing attempt.
2026-07-01 16:45:56 +10:00
Colin Vidal
6588c5f304 chg: usr: Introduce a minimum TTL for cached delegations
Delegations are now cached with a minimum TTL of 60 seconds by default. Any NS record or A/AAAA glue record with a TTL below this threshold will be raised to 60 seconds.

A new configuration option `min-delegation-ttl` has been added to adjust this limit, or disable it by setting the value to `0`. The corresponding `max-delegation-ttl` option allows the user to configure a maximum TTL for delegations; it is disabled by default.

Closes #6031

Merge branch '6031-delegdb-ttl' into 'main'

See merge request isc-projects/bind9!12102
2026-07-01 08:40:18 +02:00
Colin Vidal
d784ab5cd9 Remove useless TTL override in rctx_referral()
Function `rctx_referral()` used to force the TTL of an NS record with
TTL 0 to be 1.  This has now been removed, as the delegation database
already forces a minimum hard-coded TTL of 1 when the delegation has
a TTL of 0.
2026-07-01 08:40:05 +02:00
Colin Vidal
5dde593aa4 System test for delegation TTL options
Add a system test which covers the behavior of the `min-delegation-ttl`
and `max-delegation-ttl` options (including default, disabling, and
enforcing that min- must be strictly less than max-).
2026-07-01 08:40:05 +02:00
Colin Vidal
ff274e37af Delegations have a minimal TTL of 60 seconds
Delegations are now stored in delegdb with a TTL of at least 60 seconds
by default. A new configuration option `min-delegation-ttl` allows
overriding this value or disabling entirely it with `0`.

This hardens the resolver against misconfigured glue or NS records
with very low TTLs, which would otherwise trigger delegation refetches
too often.

A new option `max-delegation-ttl` (which default to `0`) is also added,
enabling an operator to enforce a maximum TTL check for delegations.
2026-07-01 08:40:05 +02:00
Colin Vidal
dd37b0fdd8 Introduce min/max TTL bounds for delegations
The TTL of cached delegations can now have a minimum bound and a maximum
bound. By default, delegdb does not enable TTL bound checking, but this
can be configured from the caller using `dns_delegdb_config_t`.
2026-07-01 08:40:05 +02:00
Colin Vidal
bec74c2f41 Naming convention fix for delegdb_lookup
As `dns__delegdb_lookup()` is static and thus not used internally by
other modules of `libdns`, it can be renamed `delegdb_lookup()`.
2026-07-01 08:40:05 +02:00
Ondřej Surý
cae79fc166 fix: nil: Remove non-standard integer types
Replace u_char with uint8_t and uint with unsigned int.

Merge branch 'ondrej/replace-u_char-with-uint8_t' into 'main'

See merge request isc-projects/bind9!12331
2026-07-01 08:05:10 +02:00
Ondřej Surý
3e848b2541 Replace uint with unsigned int in the histo.c unit
Similarly, uint is not a standard type, replace it with standard
unsigned int type.
2026-07-01 08:04:48 +02:00
Ondřej Surý
1ada3dc7b0 Replace u_char remnants with uint8_t
The u_char is BSDism that works, but since we have C11 integer types,
uint8_t is a better fit.
2026-07-01 08:04:48 +02:00