Commit graph

44693 commits

Author SHA1 Message Date
Petr Špaček
e223ee7097
Test that spoofed DNAME is not accepted via spoofable transport
A single spoofed DNAME answer can impact many names, and because of the
nature of DNAME, the attacker can use randomized query names to get
unlimited number of tries to spoof the answer.  To limit impact, we
should not be accepting DNAME over insecure transport, like UDP without
cookies etc.

In short, the attacker tries to spoof at least one answer that has the
following form:

    opcode QUERY
    rcode NOERROR
    flags QR AA
    ;QUESTION
    trigger$RANDOM.test. IN A
    ;ANSWER
    trigger$RANDOM.test. 3600 IN CNAME trigger$RANDOM.attacker.net.
    test. 3600 IN DNAME attacker.net.
    ;AUTHORITY
    ;ADDITIONAL

This has been discovered internally.

Co-authored-by: Michał Kępień <michal@isc.org>
2025-12-22 11:58:39 +01:00
Petr Špaček
b5dc46fe6e
Test that fake child delegation cannot overwrite parent's glue RR
In short, the attacker tries to spoof at least one answer that has the
following form:

    rcode NOERROR
    flags QR
    ;QUESTION
    trigger$RANDOM.victim. IN TXT
    ;ANSWER
    ;AUTHORITY
    trigger$RANDOM.victim. 3600 IN NS ns.victim.
    ;ADDITIONAL
    ns.victim. 3600 IN A 10.53.0.3

This attack was originally reported as "test case 2".

Co-authored-by: Michał Kępień <michal@isc.org>
2025-12-22 11:58:39 +01:00
Petr Špaček
658d2e9f8e
Test that unsolicited NS in positive answer cannot overwrite current NS
Before the fixes for CVE-2025-40778, an unsolicited in-bailiwick NS
record was accepted from a (spoofed) answer, enabling a single spoofed A
query/response to redirect traffic for a whole delegation.

In short, the attacker tries to spoof at least one answer that has the
following form:

    rcode NOERROR
    flags QR AA
    ;QUESTION
    trigger$RANDOM.victim. IN TXT
    ;ANSWER
    trigger$RANDOM.victim. 3600 IN TXT "spoofed answer with extra NS"
    ;AUTHORITY
    victim. 3600 IN NS ns.attacker.
    ;ADDITIONAL

This attack was originally reported as "test case 1".

Co-authored-by: Michał Kępień <michal@isc.org>
2025-12-22 11:58:39 +01:00
Petr Špaček
26eed16d61
Test that positive answer cannot overwrite sibling NS RRs
Before the fixes for CVE-2025-40778, a positive answer was allowed to
overwrite sibling NS RRs.  The answer had to be a positive AA=1 answer
with a fake NS along with it.  This combination of conditions avoided
the code path with "unrelated <RRTYPE>" detection logic.

If it were some other answer, named from the main branch would detect
the attempt and log:

    DNS format error from 10.53.0.1#16386 resolving trigger/A for <unknown>: unrelated NS victim in trigger authority section

In short, the attacker tries to spoof at least one answer that has the
following form:

    opcode QUERY
    rcode NOERROR
    flags QR AA
    ;QUESTION
    trigger$RANDOM. IN A
    ;ANSWER
    trigger$RANDOM. 3600 IN A 10.53.0.3
    ;AUTHORITY
    victim. 3600 IN NS ns.attacker.
    ;ADDITIONAL
    ns.attacker. 3600 IN A 10.53.0.3

This attack was originally reported as "test case 1c".

Co-authored-by: Michał Kępień <michal@isc.org>
2025-12-22 11:58:39 +01:00
Petr Špaček
607974b1bc
Add a common base for CVE-2025-40778 tests
Add the zone files, configuration, and code that will be reused by all
tests related to CVE-2025-40778.

Co-authored-by: Michał Kępień <michal@isc.org>
2025-12-22 11:58:39 +01:00
Michał Kępień
440e510f75
Add a reusable, bare-bones AsyncDnsServer
Add bin/tests/system/ans.py, a bare-bones DNS server that can be used in
system tests instead of full-blown named instances when a server is only
required to return zone-based data.  Where applicable, this reduces load
on the test host and the amount of generated logs.
2025-12-22 11:58:39 +01:00
Matthijs Mekking
ab9899455b chg: test: Add isctest.kasp.Key.SettimeOptions
Merge branch 'matthijs-pytest-settime' into 'main'

See merge request isc-projects/bind9!11388
2025-12-22 09:06:14 +00:00
Matthijs Mekking
f6749a432b Add isctest.kasp.SettimeOptions
This Class sets settime parameters and these can be called with key.settime()
that runs dnssec-settime on the given key with the given parameters.
2025-12-22 09:04:46 +00:00
Mark Andrews
ffc02b12c9 fix: nil: "Don't use dns_name_fromstring in dsyncfetch_start"
Closes #5697

Merge branch '5697-don-t-use-dns_name_fromstring-in-dsyncfetch_start' into 'main'

See merge request isc-projects/bind9!11405
2025-12-22 19:55:46 +11:00
Mark Andrews
d1f3e92ffa Tidy up (fixed)names in dsyncfetch_start
Use a static dns_name_t for the "_dsync" label.  Remove some
unnecessary dns_fixedname_t variables.  Remove unnecessary dsyncname
dns_name_t from dns_dsyncfetch and rename dns_fixedname_t fname to
dsyncname.
2025-12-22 13:31:09 +11:00
Michał Kępień
ad4b8a1694 fix: test: AsyncServer: low-level fixes
Merge branch 'michal/asyncserver-low-level-fixes' into 'main'

See merge request isc-projects/bind9!11398
2025-12-21 07:46:38 +01:00
Michał Kępień
1acde358ea
Prevent garbage-collecting ignored TCP connections
Due to the way various asyncio-related objects (tasks, streams,
transports, selectors) are referencing each other, pausing reads for a
TCP transport (which in practice means removing the client socket from
the set of descriptors monitored by a selector) can cause the client
task (AsyncDnsServer._handle_tcp()) to be prematurely garbage-collected,
causing asyncio code to raise a "Task was destroyed but it is pending!"
exception.  Who knew that solutions as elegant as the one introduced by
e407888507 could cause unexpected trouble?

Fix by making a horrible hack even more horrible, specifically by
keeping a reference to each incoming TCP connection to protect its
related asyncio objects from getting garbage-collected.  This prevents
AsyncDnsServer from closing any of the ignored TCP connections
indefinitely, which is obviously a pretty brain-dead idea for a
production-grade DNS server, but AsyncDnsServer was never meant to be
one and this hack reliably solves the problem at hand.

Only apply this change for the IgnoreAllConnections handler as the
ConnectionReset handler triggers a connection reset immediately after
pausing reads for an incoming TCP connection.

As pointed out in e407888507, the proper
solution would require implementing a custom asyncio transport from
scratch and that is still deemed to be too much work for the purpose at
hand.  Let's see how much longer we can limp along with the existing
approach.
2025-12-21 06:25:56 +01:00
Michał Kępień
0ec94e501a
Make exception/signal handlers idempotent
Calling asyncio.Future.set_exception() or asyncio.Future.set_result()
more than once for a given Future object raises an
asyncio.InvalidStateError exception.

In the case of AsyncServer:

  - it is enough to capture the first exception raised by higher-level
    logic as no exceptions at all are expected to be raised in the first
    place,

  - no distinction is made between SIGINT and SIGTERM; the only purpose
    of the signal handler is to make the server exit cleanly.

Given the above, make both AsyncServer._handle_exception() and
AsyncServer._signal_done() idempotent by ignoring
asyncio.InvalidStateError exceptions raised by the relevant
asyncio.Future.set_*() calls.
2025-12-21 06:25:56 +01:00
Štěpán Balážik
412862ce30 chg: ci: Use CMocka generated JUnit reports where possible
Where applicable, use the more detailed CMocka generated JUnit
reports which include subtest results and timings instead of the
one generated by Meson.

Prerequisites:
- bind9-qa!137

Closes #5511

Merge branch '5511-cmocka-junit-ouput' into 'main'

See merge request isc-projects/bind9!11100
2025-12-19 19:02:17 +00:00
Štěpán Balážik
237489caf4 Use CMocka generated JUnit reports where possible
Where applicable, use the more detailed CMocka generated JUnit
reports which include subtest results and timings instead of the
one generated by Meson.

Flaky tests also require retrying, so use a wrapper and mark them
with a environment variable. This is done to avoid the need to compute
an intersection of suites in Meson which is not supported out-of-the-box
(`meson test --suite=foo,bar` runs the union of foo and bar).
2025-12-19 18:26:22 +00:00
Štěpán Balážik
abecddb13a Replace check_for_junit_xml anchor with a Python script
This allows checking of multiple files with variable filenames rather
than insisting on harcoded `junit.xml`.
2025-12-19 18:26:22 +00:00
Štěpán Balážik
5528ed7a5b Check the validity of cross-version-config-test's XML output
This was overlooked before.
2025-12-19 18:26:22 +00:00
Štěpán Balážik
179c101bf0 Put CMocka unit tests in a suite
Distinguish them for JUnit report collection.
2025-12-19 18:26:22 +00:00
Štěpán Balážik
5126f782b1 Set unit test group name in CMocka tests
CMocka uses group names in the JUnit output.

Use dirname_filename as the group name, as there duplicate testnames
(e.g. time exists both in isc/ and dns/)
2025-12-19 18:26:22 +00:00
Štěpán Balážik
c3f5db6dbc Factor the cloning of the QA repo into an anchor
The unusual hyphen in the anchor name is used for symmetry with the
bind9-qa repo and directory name.
2025-12-19 18:26:22 +00:00
Matthijs Mekking
65592874bd fix: usr: Reconfigure NSEC3 opt-out zone to NSEC causes zone to be invalid
A zone that is signed with NSEC3, opt-out enabled, and then reconfigured to use NSEC, causes the zone to be published with missing NSEC records. This has been fixed.

Closes #5679

Merge branch '5679-nsec3-optout-to-nsec' into 'main'

See merge request isc-projects/bind9!11359
2025-12-19 16:33:53 +00:00
Matthijs Mekking
ae151a7a76 Refactor code that checks if records are seen
There are three places that do roughly the same. Refactor the code to
a helper function.
2025-12-19 16:55:34 +01:00
Matthijs Mekking
6f285bff6a Add NSEC for opt-out names
When switching from NSEC3 opt-out to NSEC, add NSEC records if we saw an
RR. This corrects a mistake in style cleanups done in commit
308ab1b4a5.
2025-12-19 16:55:34 +01:00
Matthijs Mekking
780e8e8f1c Nit fix removing a newline in the logs 2025-12-19 16:55:18 +01:00
Matthijs Mekking
3679bd4888 Update optout test to reconfig to NSEC
If we change from NSEC3 to NSEC we should not produce a zone with
missing NSEC records.

The code only considered having seen a record if there was previously
a signature present at the owner name. However with opt-out, insecure
delegations don't have a RRSIG record. Reconfiguring to NSEC causes
all insecure delegations to have a missing NSEC record.

Add a DNAME record to the test zone to also cover DNAME delegations.
2025-12-19 16:55:18 +01:00
Michal Nowak
17c5537c26 fix: test: Revert "Add ans6 blackhole server to notify system test"
Merge branch 'mnowak/revert-add-ans6-blackhole-server-to-notify-system-test' into 'main'

See merge request isc-projects/bind9!11400
2025-12-19 16:15:48 +01:00
Michal Nowak
9e7b5a4ad4 Revert "Add ans6 blackhole server to notify system test"
This reverts commit 21295bc188.

In a sense, the ans6 black holeserver, based on asyncserver, "does
nothing". In our case, it won't respond to any query, and if the
IgnoreAllConnections connection handler was installed, it would not read
anything from the client socket.

Previously, sending notifications to an unconfigured address resulted in
no communication from the target (10.53.10.53); hence, the ns3
configuration comment requested a "non-responsive notify recipient (no
reply, no ICMP errors)".

However, examining the PCAP of ans6 reveals some communication from the
10.53.0.6 server to the 10.53.0.3 client, including ICMP Destination
Unreachable (Port Unreachable), and TCP SYN/ACK.

The ans6 communication seems to be sufficiently different to touch
different code paths in named, resulting in the BIND 9.20 backport
failing in the "checking notify retries expire within 30 seconds" test.
But we better revert it from "main" as well.
2025-12-19 16:15:32 +01:00
Matthijs Mekking
9696da5f24 new: usr: Add support for Generalized DNS Notifications
A new configuration option, ``notify-cfg CDS``, is added to enable Generalized DNS Notifications for CDS and/or CDNSKEY RRset changes, as specified in RFC 9859.

Closes #5611

Merge branch '5611-generalized-dns-notifications-rfc-9859' into 'main'

See merge request isc-projects/bind9!11315
2025-12-19 14:46:23 +00:00
Matthijs Mekking
e69eb0528a Test invalid DSYNC RRset is rejected
The RFC says There MUST NOT be more than one DSYNC record for each
combination of RRtype and Scheme. If we encounter more we should drop
the response, as the DSYNC RRset is invalid.
2025-12-19 15:01:49 +01:00
Matthijs Mekking
35a7024e8c Test sending NOTIFY(CDS) messages during rollover
When doing rollover and the CDS/CDNSKEY RRset is updated, test that a
NOTIFY(CDS) message is sent. For other steps in the rollover, prohibit
any dsyncfetch activity.
2025-12-19 15:01:36 +01:00
Matthijs Mekking
e344fe18bc Test sending NOTIFY(CDS) messages
When starting up the services, send notifies for the existing CDS RRset.
This requires setting up a chain of trust for the test, so the DSYNC
records can be retrieved and validated.

This feature requires enabling 'notify-cds' and 'dnssec-validation'.

In this test, the scanner is pointed to ns2. Since there is no code
for receiving NOTIFY(CDS) messages for delegations, this is treated
as "not authoritative". Checking for this log message ensures us that
the NOTIFY(CDS) message was actually sent.
2025-12-19 15:01:36 +01:00
Matthijs Mekking
c8253a0a7a Implement NOTIFY(CDS) logic
When the CDS/CDNSKEY RRset gets updated, schedule a NOTIFY(CDS) to be
sent to the parental agent. The parental agent is published in the
parent zone as a DSYNC RRset, so first we need to figure out the
parent owner name. This is done by finding the zonecut (querying for
NS RRset until we find a postive answer).

In nsfetch_dsync, we then schedule a zone fetch for the DSYNC record
at <child-labels>._dsync.<parent-labels>. Then we queue the notify
for each target in the DSYNC records that matches the NOTIFY scheme
and CDS RRtype.
2025-12-19 14:08:15 +01:00
Matthijs Mekking
fa5f67fffe Add a function to set NOTIFY(CDS) endpoints
This is similar to setting remote endpoints for primaries, secondaries
(NOTIFY(SOA)), and parental agents.
2025-12-19 14:08:15 +01:00
Matthijs Mekking
9fa0493a94 Adjust tests to new notify logs
Now that we log the type of the notify, some expected log messages
in the system tests need to be adjusted accordingly.

The bin/tests/system/nsec3/tests_nsec3_retransfer.py log is changed
to zone_needdump because it is more reliable.  Other tests were
adjusted similar in MR !11265, but !11226 introduced a new
"sending notify" log line.
2025-12-19 14:08:15 +01:00
Matthijs Mekking
97b245c24c Use notify type in logging and for getting context
Add the notify type to the log messages for clarity, and use it to
retrieve the right notify context.
2025-12-19 14:08:15 +01:00
Matthijs Mekking
dc0437e518 Add type parameter to dns_notify_create()
With Generalized DNS Notifications, a zone may need to send different
type of NOTIFY messages for different reasons. When creating a new
notify, allow for specifying the type.
2025-12-19 14:08:15 +01:00
Matthijs Mekking
121d372236 Add port parameter to dns_notify_create()
The DSYNC record has a Port rdata field, so NOTIFY(CDS) messages may be
configured at different ports. When creating a new notify, allow for
specifying the port.
2025-12-19 14:08:15 +01:00
Matthijs Mekking
dda2e99c36 Document 'notify-cds' configuration option
Add text about the 'notify-cds' option in the ARM reference.
2025-12-19 14:08:15 +01:00
Matthijs Mekking
6554a5f9f7 Add new 'notify-cds' configuration option
Add a new configuration option to enable/disable sending NOTIFY(CDS)
messages.
2025-12-19 14:08:15 +01:00
Matthijs Mekking
8d83fbaf85 Maintain separate notify contexts for SOA and CDS
With Generalized DNS Notifications, a zone may need to send different
NOTIFY messages for different reasons. Introduce a method to
initialize a notify context and maintain a notify contexts per RRtype.
2025-12-19 14:08:15 +01:00
Matthijs Mekking
f1b2646f2b Update dns_dnssec_sync(update|delete) return code
Update the functions 'dns_dnssec_syncupdate()' and
'dns_dnssec_syncdelete()' to make a distinction between a changed RRset
and no changes made.

The return code will be used later to determine if we need to send a
NOTIFY(CDS) to DSYNC endpoints.
2025-12-19 14:08:15 +01:00
Matthijs Mekking
f3cb582b30 chg: test: Update rollover system tests with chain of trust
Merge branch 'matthijs-trust-chain-rollover-system-tests' into 'main'

See merge request isc-projects/bind9!11309
2025-12-19 12:02:16 +00:00
Matthijs Mekking
594ff0816a Drop and replace CmdHelper with EnvCmd
A generic helper that calls environment-specified binaries has been added,
drop and replace the introduced CmdHelper for the more generic method.
2025-12-19 11:49:00 +01:00
Matthijs Mekking
e172b4ff1a rollover-zsk-prepub: From setup.sh to pytest bootstrap
Symlink ns1 and ns2 to rollover/ns1 and rollover/ns2.
Symlink ns3/template.db.j2.manual to rollover/ns3/template.db.j2.manual.

Since the bootstrapping is done before the templates are rendered
automatically, replace @DEFAULT_ALGORITHM@ in ns3/kasp.conf.j2 to
ecdsa256 and rename to ns3/kasp.conf.
2025-12-19 11:47:50 +01:00
Matthijs Mekking
da04c75cec rollover-straight2none: From setup.sh to pytest bootstrap
Similar to rollover-going-insecure.
2025-12-19 11:47:50 +01:00
Matthijs Mekking
0016791c91 rollover-lifetime: Update templates
This test does not require a trust chain. Merely update the template
zone files to not point to the common template.
2025-12-19 11:47:50 +01:00
Matthijs Mekking
b6c091d113 rollover-multisigner: Update templates
This test does not require a trust chain. However, it does have a setup
script. Rewrite the setup shell script to a pytest bootstrap method.
2025-12-19 11:47:50 +01:00
Matthijs Mekking
4ed35f02b1 rollover-ksk-3crowd: From setup.sh to pytest bootstrap
Similar to rollover-ksk-doubleksk.
2025-12-19 11:47:50 +01:00
Matthijs Mekking
08236f4bd6 rollover-ksk-doubleksk: From setup.sh to pytest bootstrap
Symlink ns1 and ns2 to rollover/ns1 and rollover/ns2.
Symlink ns3/template.db.j2.manual to rollover/ns3/template.db.j2.manual.

Since the bootstrapping is done before the templates are rendered
automatically, replace @DEFAULT_ALGORITHM@ in ns3/kasp.conf.j2 to
ecdsa256 and rename to ns3/kasp.conf.
2025-12-19 11:47:50 +01:00
Matthijs Mekking
cc4244f384 rollover-going-insecure: From setup.sh to pytest bootstrap
Symlink ns1 and ns2 to rollover/ns1 and rollover/ns2.
Symlink ns3/template.db.j2.manual to rollover/ns3/template.db.j2.manual.

Since the bootstrapping is done before the templates are rendered
automatically, replace @DEFAULT_ALGORITHM@ in ns3/kasp.conf.j2 to
ecdsa256 and rename to ns3/kasp.conf.

Now we have to fake different lifetimes, so adjust fake_lifetime
to update a single key.

Note that we have changed the setup slightly: We also sign the
step2 zones, but with post validation disabled. This is more
accurate because we need to test that the public keys and signatures
are being removed from the zone.
2025-12-19 11:47:50 +01:00