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
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
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
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.
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
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
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
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.
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.
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
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.
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
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
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
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.
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
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
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.
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
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
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.
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
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
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.
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
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.
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
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
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.
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
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
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.
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
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.
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-).
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.
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`.
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