Repository navigation
Releases
Releases · sylvinus/recursive-resolver
v0.2.0
A security release. Four defects in DNSSEC validation allowed forged data to be
accepted as authenticated; the most direct required only that the attacker own
a signed domain of their own. Upgrade before relying on a SECURE verdict from
0.1.0. TESTING.md describes how they were found.
Security
- An RRSIG's signer was never required to contain the RRset it covers
(RFC 4035 §5.3.1), so any signed zone could sign for any name. - A wildcard at any ancestor was accepted where only the one at the closest
encloser can have answered (RFC 4035 §5.3.4/§5.4). Three variants: forged
NXDOMAIN, forged NODATA against a nested wildcard, and a replayed wildcard
RRset authenticated under a name a closer wildcard governs. - Denials served from the parent side of a zone cut were accepted as proof
about the child, and a child's own SOA-bearing record could deny its DS
(RFC 6840 §4.1, RFC 4035 §5.2). Likewise a parent-side NSEC3 as the closest
encloser (RFC 5155 §8.3). - An opt-out NSEC3 proved NODATA for any type; it only ever proves a missing
DS (RFC 5155 §8.5, §8.6), and even then unauthenticated (§9.2). - The DNSKEY RRset was authenticated against every published key rather than
the one the parent's DS matched (RFC 4035 §5.2), so a compromised
zone-signing key was enough to introduce a new one. - A DNSKEY with the REVOKE bit was still used to verify signatures
(RFC 5011 §2.1). - Three checks read a field from the first RRSIG rdata rather than the
signature that verified, which an attacker orders. Each was exploitable with
a decoy. - An authenticated RRset could be cached past its signature's expiry
(RFC 4035 §5.3.3), for up to 24 hours; a proven denial for up to an hour. - An NXDOMAIN or NODATA resting on an opt-out cover was reported
authenticated, andrequire_dnssec=Truedid not apply to negative answers
at all, cached or fresh. - NSEC3 records with reserved flag bits were read as Opt-Out (RFC 5155 §8.2).
Fixed
- One unreachable or out-of-sync nameserver no longer condemns a zone. DNSKEY
and DS fetches, bare referrals, failed denials and answers carrying no
signature at all now sweep the NS set (RFC 4035 §5.5); a stale cached
delegation re-resolves its NS names. The last of those is a nameserver
serving a stale unsigned copy of a signed zone: every type it answers
reads as forged and its DNSKEY NODATA proves nothing, so landing on it made
the whole zone intermittently BOGUS. One ofnic.bj's four nameservers is
such a copy. - A single spoofed UDP packet no longer retires a healthy nameserver
(RFC 5452 §9). - DNAME is implemented: CNAME synthesis when the server omits it, and the
redirection inherits the DNAME's verdict rather than reading the unsigned
synthesized CNAME as a stripped signature (RFC 6672 §3.3, §3.4, §8). ANYis refused rather than answered as NODATA and cached as a denial.- An NS set that points sideways or upwards is an error, not a denial.
- The SOA marking a negative answer must be class IN and belong to the zone
queried. - Glue is cached under its own TTL, not the NS set's.
AAAAis queried for glueless NS targets when IPv6 is enabled.- A CNAME chain reports the minimum TTL over its hops.
- Empty non-terminals and wildcard-synthesised NODATA are no longer BOGUS
(RFC 4035 §3.1.3.4.1, §5.4; RFC 5155 §8.7), nor is a NODATA whose only proof
is an opt-out gap. The bit says the range holds no signed delegation, not
that it holds no names, so the answer is returned unauthenticated rather than
refused (RFC 5155 §8.6, §9.2). Ten zones in a 4000-name corpus needed this. - RRSIG validity uses RFC 1982 serial arithmetic, allows configurable slack on
inception (clock_skew, 60s) for signers whose clock runs ahead, and is
checked before any cryptography. - The IPv6 transition prefixes (6to4, Teredo, NAT64, IPv4-compatible) are
refused as nameserver addresses by name. Each carries an IPv4 address
inside it, and CVE-2024-4032 moved where the standard library draws that
line, so the control no longer depends on the interpreter's patch level. edns_payloadoutside 16 bits, and a cachemin_ttlabove itsmax_ttl,
are rejected at construction rather than misbehaving later.--jsonwith--no-dnssecreports"dnssec": "disabled". It said
"insecure", which claims a zone was checked and found unsigned.- A resolution that runs out of budget while resolving a referral's
nameservers reports a timeout, not a server failure.
Changed
- A high NSEC3 iteration count downgrades to insecure rather than failing,
matching the major implementations (RFC 9276 §3.2 permits either). min_ttlno longer floors authenticated answers or delegations, whose TTL
is already capped by their signature.
Added
DNSSECMaterialUnavailableError, raised when a DNSKEY or DS could not be
retrieved. Deliberately not aDNSSECError, so "refuse to use this data"
handling stays correct while retrieval failures fall through to retries.TESTING.mdand the harnesses it describes, including a mutation catalogue
of 52 reintroduced defects that confirms the suite still catches each one.
Install: pip install recursive-resolver==0.2.0