Send a valid DoH query followed by a flood of SETTINGS frames to
trigger a use-after-free in the write buffer. Under ASan, named
will abort if the bug is present.
(cherry picked from commit 680e57c8c7110828da22cbc94115a10e8dec2523)
Add an 'rd' parameter (default True) to isctest.query.create() so
that non-recursive queries can be sent with rd=False.
(cherry picked from commit 12e5113100)
Send update messages for zones with CLASS0, ANY and NONE. The class
ANY UPDATE also attempts to delete a KX record in an existing IN
class zone to trigger a REQUIRE.
Test that the server is still running.
(cherry picked from commit 1fa1e84d286d5a6d9d3b72ed1c2c29142f40c81d)
Send various UPDATE requests that are known to have caused
crashes previously with deliberately misconfigured non-IN
zones; confirm that UPDATE is not processed.
(cherry picked from commit e2f7ba2a4b6e7e5dba2fb1a2c9b2f0323e9a88be)
Check that recursive and forward queries to views of type CHAOS
are REFUSED, but that authoritative queries are answered correctly.
(cherry picked from commit f33927cd3dd1195f3e70f5798ff7c384f265867e)
Reject requests with unsupported or misused CLASS values before
further processing. Only IN, CH, HS, RESERVED0 (for DNS Cookies),
ANY (for TKEY negotiation), and NONE (for DNS UPDATE) are accepted;
all other classes return NOTIMP. Misuse of NONE or ANY outside
their allowed contexts returns FORMERR.
This adds further protection against bugs of the same general class
as YWH-PGM40640-70 and YWH-PGM40640-73.
(cherry picked from commit 0a687451505037e9f9a850c9cb113aed4995b03f)
Return NOTIMP for UPDATE and NOTIFY requests received for views with a
class other than IN. Only QUERY is now supported for non-IN views such
as CHAOS.
When running dns dns_rdata_tostruct() with types that are only defined
for class IN, ensure that the class is correct before proceeding.
Add an assertion that any zone being updated is of class IN. (Note
that previously, a DLZ zone could have its class value set incorrectly
to NONE; this has been fixed.)
This addresses YWH-PGM40640-70 and YWH-PGM40640-73 (as well as any
similar problems that might have occurred in the future) by minimizing
the code paths that can be reached by rdata classes other than IN, so it
is safe for the implementation to assume that rdatatypes that are only
defined for class IN, such as SVCB or WKS, have been parsed and
validated, and not accepted as unknown/opaque data.
Fixes: isc-projects/bind9#5777Fixes: isc-projects/bind9#5779
(cherry picked from commit a6d8e330ed6cf0021bff3f00aa1dc7a296f5aec0)
Force recursion off, and set allow-recursion/allow-recursion-on ACLs
to none, for views with a class other than IN. Log a configuration
warning if recursion is explicitly enabled for a non-IN view.
This addresses YWH-PGM40640-74 and YWH-PGM40640-75 by preventing any
attempt at recursive processing in a class-CHAOS view, ensuring that
server addresses used for recursive queries and received in recursive
responses are of the expected format.
Fixes: isc-projects/bind9#5780Fixes: isc-projects/bind9#5781
(cherry picked from commit 7becff1a14684a68208c92b3b0315c045c05ad75)
Send crafted SPNEGO NegTokenInit tokens that propose the krb5
mechanism without a mechToken. This causes gss_accept_sec_context()
to return GSS_S_CONTINUE_NEEDED, which on unfixed code leaks the
GSS context handle (~520 bytes per query).
The test verifies that the server rejects the negotiation (TKEY
error != 0, no continuation token) rather than returning a CONTINUE
response (error=0 with output token).
(cherry picked from commit 2f2fb32d737e12c817880d584145cdf85dbc8d06)
Verify that the resolver selects authoritative servers in increasing
SRTT order. Four servers are configured with increasing response
delays. 100 queries are sent, expecting most to go to the fastest
server (ns2). Then ns2 stops responding, another 100 queries are
sent and should go to ns3 (the next fastest), and so on through
ns4 and ns5. Each query uses a unique name to avoid cache hits.
(cherry picked from commit a8d11e14f5b4e4d53219ba751d1b741162b0b84b)
Test the resolver's behavior with self-pointed glue where each NS
has the same set of addresses. Verify that addresses are
deduplicated and each unique IP is only queried once.
Also test the ADB address limit knob (-T adbaddrslimit=).
(cherry picked from commit c21fc6cb95d77312d6fb891f17ce9df41a25af6d)
Add a hard limit on the number of addresses that ADB returns from a
single NS lookup (dns_adbfind_t). This mitigates a flood attack
where an attacker controls a zone with many addresses for a
nameserver, each returning an invalid response. The global
max-query count (default 50) also limits this, but significant harm
can be done before that limit is reached.
The default limit is now 6 (v4 and/or v6) addresses for an ADB find (so,
ADB looking up for A/AAAA addresses of a name server name). It can be
overridden for testing via 'named -T adbaddrslimit=N'.
(cherry picked from commit 3ec37fc69356ee682bee7f67940613ac31d93d7b)
Run malicious server: resend_loop/ans3/ans.py
Start BIND: ns4
Send single query to test.example
The resolver will repeatedly resend queries until the fetch timeout
expires, resulting in resulting in thousands of qrysent while the quota
counter remains 0.
(cherry picked from commit 7eeb463bc58cbd71419aaf189d7829f2dfd8d055)
Add a starvation test that tries to starve the XFR quota with
unautorized requests.
(cherry picked from commit 53135592b7ff6c272b6577b2e7747258628442e3)
This new check ads a catalog member zone with both variants of
the labeled primaries/masters property. This should not cause
any issues.
(cherry picked from commit 4f5f4b77c7)
TCP dispentries no longer use the global QID hash table at all.
Responses are matched by scanning disp->active, and sequential
per-dispatch IDs (bounded by the pipelining limit) are unique
within a single dispatch by construction. Since TCP delivers
only data we asked for on a specific connection, the per-peer
uniqueness that the global table enforced was never actually
needed for TCP.
DNS_DISPATCHOPT_FIXEDID is plumbed through dns_request_createraw
-> get_dispatch -> dns_dispatch_createtcp so FIXEDID TCP requests
always get a fresh isolated dispatch — the caller-supplied ID
then cannot collide with any other in-flight query either.
(cherry picked from commit 3e364aec2b)
Cap the number of in-flight queries on a single shared TCP dispatch.
When the limit is reached, the dispatch is removed from the hash
table so subsequent queries get a fresh connection. The existing
dispatch continues serving its queries until they complete.
This bounds the blast radius of a connection drop: at most N queries
fail simultaneously instead of all queries to that server.
The default limit is 256. It can be overridden for testing via
'named -T tcppipelining=N'.
(cherry picked from commit 385ceabe8f)
Set tcppipelining=1 on recursive servers in the system tests to
restore one-query-per-connection behavior. The tests relies on
specific connection and query counting that breaks with TCP
connection sharing.
(cherry picked from commit 05e8b58307)
The DNS64 state information stored in client->query.dns64_aaaaok
could cause an assertion failure in query_respond() if the server
was configured in such a way as to trigger a new recursion before
the query had been reset - for example, by using the filter-aaaa
plugin, which may need to recurse to find out whether an A record
exists.
This has been addressed by clearing DNS64 state information
immediately after the call to query_filter64().
(cherry picked from commit 7213b038f0)
run_resolve allocates dns_client_t late, but the cleanup epilogue
called dns_client_detach() unconditionally. When convert_name() or
dns_client_create() failed first, the detach hit a NULL client and
the REQUIRE(DNS_CLIENT_VALID) inside it aborted the process with
SIGABRT instead of a clean error exit.
Guard the detach with a NULL check. Add a digdelv test that runs
delv on a query name whose first label exceeds 63 octets and
asserts the process does not exit 134.
Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit dba102ef16)
The bare return left conn->secret, conn->response, conn->request, and
conn->text pinned until the connection itself was torn down — every
other error in the function reaches conn_cleanup via goto, and the
success path falls into the same label, so the towire-failure return
was the lone outlier. Send it through the existing cleanup path.
Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit 1b681dfa2f)
For a query whose qname is the root, the labels==1 branch in
redirect2() called dns_name_copy(redirectname, view->redirectzone)
with arguments reversed, overwriting the view-global
nxdomain-redirect target with the empty redirectname rather than
copying the configured target into the per-query lookup name. After
the corruption, view->redirectzone names the root, so
dns_name_issubdomain() makes redirect2() short-circuit for every
subsequent query and the nxdomain-redirect feature stops working
until named is restarted.
Triggering this needs the resolver to receive an NXDOMAIN for the
root from upstream, which does not happen in normal DNS operation.
Swap the arguments to match the dns_name_copy(source, dest)
signature. Add a system test that issues a root query through the
nxdomain-redirect resolver and verifies the redirect feature still
works for a normal NXDOMAIN-producing query afterwards.
Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit c62f24f7ee)
hmac_generate() declared its on-stack nonce buffer as
unsigned char data[ISC_MAX_MD_SIZE], i.e. 64 bytes. That is the maximum
digest size, but the buffer is filled up to the algorithm's HMAC block
size, which is 128 bytes for SHA-384 and SHA-512. Asking rndc-confgen
for an HMAC-SHA-384 or HMAC-SHA-512 key with -b > 512 (the documented
range allows up to 1024) wrote past the end of the stack buffer; on
hardened builds this aborted with a stack-smash detector firing
instead of producing a key.
Use the existing ISC_MAX_BLOCK_SIZE (128) for the buffer so the full
1..1024 range advertised by -A hmac-sha{384,512} works as documented.
The matching key_rawsecret[64] in confgen's generate_key() is enlarged
the same way so the generated key fits when dumped to the buffer.
Add a system test that exercises rndc-confgen across the previously
overflowing keysizes; with -Db_sanitize=address it caught the abort
before the fix.
Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit 46f6bb6364)
Both constants were defined as 5. The symbol table used by checkns() to
deduplicate log messages keys on (name, error_code), so logging an
extra-AAAA error caused logged() to also return true for the
missing-glue check, silently skipping the entire missing-glue block for
the same name in named-checkzone and named-checkconf -z.
Convert the ERR_* defines to an auto-numbered enum so the compiler
guarantees the values stay pairwise distinct.
Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit 72d21a0806)
Make sure the nameserver correctly handles SIG records in the
prerequisites of the dynamic update. The first check is to ensure that
the prerequisites are not examined prior to checking the credentials.
The second test case checks that the SIG present prerequisite is
examined and therefore refuses the update. Also this should not trigger
an assertion failure in dns__db_findrdataset() (due to the REQUIRE()
only accepted dns_rdatatype_rrsig when the covers parameter was set).
(cherry picked from commit 51f27fda46)
diff.c rdata_covers() runs on both dns_diff_apply (IXFR, ns/update.c
dynamic updates) and dns_diff_load (AXFR). After the previous commit
refused SIG and NXT in dynamic updates, the AXFR path remains the
most natural way to drive legacy SIG records into a secondary's zone
DB and regression-gate the rdata_covers() fix.
The test adds ans11 as an AsyncDnsServer primary for a small zone
whose AXFR carries two SIG rdatas at the same owner with different
covered types (A, MX) and different TTLs (600, 1200), and declares
ns6 a secondary of that zone. With the bug present, dns_diff_load
groups both tuples at typepair (SIG, 0) and the MX-covering record
inherits the first-seen TTL (600); the fix keeps them at (SIG, A)
and (SIG, MX) with their original TTLs.
rndc dumpdb -zones on the secondary is used to inspect stored state
directly, because the wire-level SIG query response merges
same-(owner,type,class) RRs and masks the per-rdataset TTLs.
(cherry picked from commit e9f880c78f)
SIG (24) and NXT (30) are obsolete DNSSEC record types, superseded by
RRSIG and NSEC in RFC 3755. Allowing them through dynamic update
exposes two distinct bugs that the surrounding GL#5818 work already
fixes as defense-in-depth:
- dns__db_findrdataset() used to REQUIRE that (covers == 0 ||
type == RRSIG), which aborts named when a SIG update reaches the
prescan foreach_rr() call. Fixed to accept dns_rdatatype_issig().
- diff.c rdata_covers() used to test only RRSIG, dropping the
covered-type field for SIG rdatas; the zone DB then filed every
SIG rdataset under typepair (SIG, 0) instead of
(SIG, covered_type) and follow-up adds collided at that bucket.
Fixed to use dns_rdatatype_issig().
Both underlying bugs are still reachable via inbound zone transfer
(diff.c rdata_covers() runs from both dns_diff_apply on the IXFR path
and dns_diff_load on the AXFR path), so the type-helper fixes above
remain necessary. For the dynamic-update path, the simplest and
safest posture is to refuse SIG and NXT outright at the front door in
ns/update.c, alongside the existing NSEC/NSEC3/non-apex-RRSIG
refusals. KEY remains permitted because it is still used to carry
public keys for SIG(0) transaction authentication.
The existing tcp-self SIG regression test is repointed to assert
REFUSED on the SIG add, a symmetric NXT test is added, and the
SIG-via-dyn-update covers-bucket test is removed because it is no
longer reachable through this entry point; AXFR-based coverage of
diff.c rdata_covers() follows in a separate commit.
(cherry picked from commit 3a44a13232)
rdata_covers() in lib/dns/diff.c tests `type == dns_rdatatype_rrsig`
instead of dns_rdatatype_issig(), so for a legacy SIG (24) rdata it
returns 0 and the covered type is discarded on the dynamic-update /
IXFR path. The zone DB then files every SIG rdataset under typepair
(SIG, 0) instead of (SIG, covered_type), and a follow-up add with a
different covers field but a different TTL collides at that bucket,
trips DNS_DBADD_EXACTTTL in qpzone, returns DNS_R_NOTEXACT, and comes
back to the client as SERVFAIL.
The new test adds a PTR to establish the node (tcp-self requires the
client IP's reverse form to equal the owner), then two SIG updates
with different covers and different TTLs; on a buggy build the second
update is SERVFAIL and named logs `dns_diff_apply: .../SIG/IN: add
not exact`. The test is expected to pass once rdata_covers() is
switched to dns_rdatatype_issig(), matching the fix already adopted
for dns__db_findrdataset() on this branch and the helper pattern used
in master.c, xfrout.c, and qpcache.c.
(cherry picked from commit b9fc0e595b)
Verify that a SIG record sent via TCP dynamic update is accepted
by the tcp-self update-policy and correctly stored in the zone.
(cherry picked from commit ecddeab696)
When named_os_gethostname() was replaced with raw gethostname(), the
success/failure polarity was flipped: the fallback to "localhost" now
runs on success and the hostname buffer is left uninitialized on
failure. In the failure path, snprintf() then reads the uninitialized
stack buffer, disclosing stack contents via the rndc status reply.
(cherry picked from commit f7859247df)
Ensure that the actual packet contents are logged including the TSIG
record which is calculated on `to_wire` call.
(cherry picked from commit 37b0502cf4)
Replace the less obvious and less explicit `struct.unpack()` and
`struct.pack()` calls with calls to `int.from_bytes()` and
`int.to_bytes()`, respectively.
(cherry picked from commit e43fa1dc5a)
All the other subclasses AxfrHandler send three messages.
This oversight was inherited from the original Perl implementation of
the server and was not fixed in 46ecbbe where it was rewritten.
This allows refactoring and sharing of the superclass.
(cherry picked from commit 93a8079489)
Add records that will be at end of each compressed bitmap less 1
of the NSEC3 record. Zone verification should still work.
(cherry picked from commit e9a58de251)
Nsupdate does not distinguish between a non-existing RDATA field
and an empty RDATA field when determining which action is desired
when the RDATA field is empty. This only affects a few data types,
like APL, which allow an empty RDATA field. Document a workaround
of using the '\# 0' form for entering these specific records. e.g.
# delete the APL RRset
update delete IN APL
# delete the APL record with a zero length rdata
update delete IN APL \# 0
(cherry picked from commit f45762a5be)
Add missing `|| ret=1` to six dig and nsupdate invocations in
tests.sh so that command failures are properly caught instead
of silently ignored.
(cherry picked from commit 9ba03a1848)
The system test was also subject to the same off by one bug that also
existed in the code. That is: if the inception time of the signature
is exactly equal to the inactive time of the key, we still have to
expect the signature.
(cherry picked from commit d9b6036e0f)
This specific test case triggered a bug where the SKR included bundles
with unsigned DNSKEY RRsets (signatures where omitted because the
inception time was equal to the inactive time of the key).
(cherry picked from commit 784a441e2d)
If the inception time of the signature is exactly equal to the
inactive time of the key, still include the signature. Otherwise there
may be corner cases where signatures are omitted erroneously.
(cherry picked from commit bc6dad585d)
Replace the hand-rolled threaded socket server with the standard
AsyncDnsServer framework used by other ans.py servers in the test suite.
The DNS wire-format message builders (IXFR diff, AXFR, SOA, SERVFAIL)
are retained unchanged since they produce carefully crafted messages
needed to trigger the IXFR->AXFR race condition. The server
infrastructure is replaced:
- Manual TCP/UDP socket management and threading replaced by
AsyncDnsServer, which handles both protocols, pidfile lifecycle,
and signal handling.
- Query parsing replaced by the framework's dns.message-based parser;
query dispatch moved into IxfrRaceHandler.get_responses().
- The axfr_done_event threading.Event replaced by a boolean instance
variable on IxfrRaceHandler, safe within the single asyncio event
loop.
- For IXFR over TCP, the handler yields two BytesResponseSend actions
(msg1 then msg2) so the framework sends both with TCP length prefixes,
preserving the race-triggering sequence.
- For IXFR over UDP, the TC flag is set on the response to force TCP
retry.
- Unused encode_name_compressed() and parse_dns_query() removed.
Also fix a timing issue that might result in the initial transfer not
being done by the time the test is executed -- since ns11 is started
after ns6. Ensure the initial transfer has happened before running the
ixfr_race test.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 187e571f4d)
The test initiates a zone transfer with IXFR, which produces
a big amount of differences and then generates an error. The
secondary should be able to gracefully shutdown the ongoing
IXFR transfer and retry with AXFR without race conditions
between them.
This test checks for an issue (GL#5767) but since a race
condition is usually time-sensitive it might require several
attempts before it reproduces the issue.
(cherry picked from commit 5c248e7d1a)