Age | Commit message (Collapse) | Author |
|
|
|
UDP ICMP errors are reported to us via recvmsg() when we read a reply. Handle this properly, and consider this a lost
packet, and retry the connection.
This also adds some additional logging for invalid incoming packets.
|
|
Previously, when we couldn't connect to a DNS server via TCP we'd abort the whole transaction using a
"connection-failure" state. This change removes that, and counts failed connections as "lost packet" events, so that
we switch back to the UDP protocol again.
|
|
The code to retry transactions has been used over and over again, simplify it by replacing it by a new function.
|
|
|
|
response
This implements RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4:
When we receive a response with an RRset generated from a wildcard we
need to look for one NSEC/NSEC3 RR that proves that there's no explicit RR
around before we accept the wildcard RRset as response.
This patch does a couple of things: the validation calls will now
identify wildcard signatures for us, and let us know the RRSIG used (so
that the RRSIG's signer field let's us know what the wildcard was that
generate the entry). Moreover, when iterating trough the RRsets of a
response we now employ three phases instead of just two.
a) in the first phase we only look for DNSKEYs RRs
b) in the second phase we only look for NSEC RRs
c) in the third phase we look for all kinds of RRs
Phase a) is necessary, since DNSKEYs "unlock" more signatures for us,
hence we shouldn't assume a key is missing until all DNSKEY RRs have
been processed.
Phase b) is necessary since NSECs need to be validated before we can
validate wildcard RRs due to the logic explained above.
Phase c) validates everything else. This phase also handles RRsets that
cannot be fully validated and removes them or lets the transaction fail.
|
|
validated keys list
When validating a transaction we initially collect DNSKEY, DS, SOA RRs
in the "validated_keys" list, that we need for the proofs. This includes
DNSKEY and DS data from our trust anchor database. Quite possibly we
learn that some of these DNSKEY/DS RRs have been revoked between the
time we request and collect those additional RRs and we begin the
validation step. In this case we need to make sure that the respective
DS/DNSKEY RRs are removed again from our list. This patch adds that, and
strips known revoked trust anchor RRs from the validated list before we
begin the actual validation proof, and each time we add more DNSKEY
material to it while we are doing the proof.
|
|
Instead of first iterating through all DNSKEYs in the DnsAnswer in
dns_transaction_check_revoked_trust_anchors(), and
then doing that a second time in dns_trust_anchor_check_revoked(), do so
only once in the former, and pass the dnskey we found directly to the
latter.
|
|
There's not reason to wait for checking for revoked trust anchors until
after validation, after all revoked DNSKEYs only need to be self-signed,
but not have a full trust chain.
This way, we can be sure that all trust anchor lookups we do during
validation already honour that some keys might have been revoked.
|
|
Invert an "if" check, so that we can use "continue" rather than another
code block indentation.
|
|
|
|
After all, when we don't support the algorithm we cannot determine
validity.
|
|
non-DNSSEC mode for them
This adds logic to detect cases like the Fritz!Box routers which serve
a private DNS domain "fritz.box" under the TLD "box" that does not
exist in the root servers. If this is detected DNSSEC validation is
turned off for this private domain, thus improving compatibility with
such private DNS zones.
This should be fairly secure as we first rely on the proof that .box
does not exist before this logic is applied. Nevertheless the logic is
only enabled for DNSSEC=allow-downgrade mode.
This logic does not work for routers that set up a full DNS zone directly
under a non-existing TLD, as in that case we cannot prove
that the domain is truly non-existing according to the root servers.
|
|
We followed the wrong connection. This only worked sometimes at all, because we
also return the wrong error code.
|
|
After discussing this with Tom, we figured out "allow-downgrade" sounds
nicer.
|
|
When storing negative responses, clamp the SOA minimum TTL (as suggested
by RFC2308) to the TTL of the NSEC/NSEC3 RRs we used to prove
non-existance, if it there is any.
This is necessary since otherwise an attacker might put together a faked
negative response for one of our question including a high-ttl SOA RR
for any parent zone, and we'd use trust the TTL.
|
|
Since we honour RFC5011 revoked keys it might happen we end up with an
empty trust anchor, or one where there's no entry for the root left.
With this patch the logic is changed what to do in this case.
Before this patch we'd end up requesting the root DS, which returns with
NODATA but a signed NSEC we cannot verify, since the trust anchor is
empty after all. Thus we'd return a DNSSEC result of "missing-key", as
we lack a verified version of the key.
With this patch in place, look-ups for the root DS are explicitly
recognized, and not passed on to the DNS servers. Instead, if
downgrade-ok mode is on an unsigned NODATA response is synthesized, so
that the validator code continues under the assumption the root zone was
unsigned. If downgrade-ok mode is off a new transaction failure is
generated, that makes this case recognizable.
|
|
We already try hard not to create cyclic transaction dependencies, where
a transaction requires another one for DNSSEC validation purposes, which
in turn (possibly indirectly) pulls in the original transaction again,
thus resulting in a cyclic dependency and ultimately a deadlock since
each transaction waits for another one forever.
So far we wanted to avoid such cyclic dependencies by only going "up the
tree" when requesting auxiliary RRs and only going from one RR type to
another, but never back. However this turned out to be insufficient.
Consider a domain that publishes one or more DNSKEY but which has no DS
for it. A request for the domain's DNSKEY triggers a request for the
domain's DS, which will then fail, but return an NSEC, signed by the
DNSKEY. To validate that we'd request the DNSKEY again. Thus a DNSKEY
request results in a DS request which results in the original DNSKEY
request again. If the original lookup had been a DS lookup we'd end up
in the same cyclic dependency, hence we cannot statically break one of
them, since both requests are of course fully valid. Hence, do full
cyclic dependency checking: each time we are about to add a dependency
to a transaction, check if the transaction is already a dependency of
the dependency (recursively down the tree).
|
|
dns_transaction_request_dnssec_keys() is running
If any of the transactions started by
dns_transaction_request_dnssec_keys() finishes promptly without
requiring asynchronous operation this is reported back to the issuing
transaction from the same stackframe. This might ultimately result in
this transaction to be freed while we are still in its
_request_dnssec_keys() stack frame. To avoid memory corruption block the
transaction GC while in the call, and manually issue a GC after it
returned.
|
|
With this patch resolved will properly handle revoked keys, but not
augment the locally configured trust anchor database with newly learned
keys.
Specifically, resolved now refuses validating RRsets with
revoked keys, and it will remove revoked keys from the configured trust
anchors (only until reboot).
This patch does not add logic for adding new keys to the set of trust
anchors. This is a deliberate decision as this only can work with
persistent disk storage, and would result in a different update logic
for stateful and stateless systems. Since we have to support stateless
systems anyway, and don't want to encourage two independent upgrade
paths we focus on upgrading the trust anchor database via the usual OS
upgrade logic.
Whenever a trust anchor entry is found revoked and removed from the
trust anchor a recognizable log message is written, encouraging the user
to update the trust anchor or update his operating system.
|
|
validation
|
|
Fixes to NSEC3 proof v2
|
|
configuration files
This adds negative trust anchor support and allows reading trust anchor
data from disk, from files
/etc/systemd/dnssec-trust-anchors.d/*.positive and
/etc/systemd/dnssec-trust-anchros.d/*.negative, as well as the matching
counterparts in /usr/lib and /run.
The positive trust anchor files are more or less compatible to normal
DNS zone files containing DNSKEY and DS RRs. The negative trust anchor
files contain only new-line separated hostnames for which to require no
signing.
By default no trust anchor files are installed, in which case the
compiled-in root domain DS RR is used, as before. As soon as at least
one positive root anchor for the root is defined via trust anchor files
this buil-in DS RR is not added though.
|
|
Instead introduce the new return-code DNSSEC_NSEC_CNAME to indicate
this condition. See RFC 6840, Section 4.3.
|
|
traffic
|
|
This renames dns_transaction_stop() to dns_transaction_stop_timeout()
and makes it only about stopping the transaction timeout. This is safe,
as in most occasions we call dns_transaction_stop() at the same time as
dns_transaction_close_connection() anyway, which does the rest of what
dns_transaction_stop() used to do. And in the one where we don't call
it, it's implicitly called by the UDP emission or TCP connection code.
This also closes the connections as we enter the validation phase of a
transaction, so that no further messages may be received then.
|
|
The name "features" suggests an orthogonal bitmap or suchlike, but the
variables really encode only a linear set of feature levels. The type
used is already called DnsServerFeatureLevel, hence fix up the variables
accordingly, too.
|
|
This moves management of the OPT RR out of the scope management and into
the server and packet management. There are now explicit calls for
appending and truncating the OPT RR from a packet
(dns_packet_append_opt() and dns_packet_truncate_opt()) as well as a
call to do the right thing depending on a DnsServer's feature level
(dns_server_adjust_opt()).
This also unifies the code to pick a server between the TCP and UDP code
paths, and makes sure the feature level used for the transaction is
selected at the time the server is picked, and not changed until the
next time we pick a server. The server selction code is now unified in
dns_transaction_pick_server().
This all fixes problems when changing between UDP and TCP communication
for the same server, and makes sure the UDP and TCP codepaths are more
alike. It also makes sure we never keep the UDP port open when switchung
to TCP, so that we don't have to handle incoming datagrams on the latter
we don't expect.
As the new code picks the DNS server at the time we make a connection,
we don't need to invalidate the DNS server anymore when changing to the
next one, thus dns_transaction_next_dns_server() has been removed.
|
|
|
|
This new call unifies how we shut down all connection resources, such as
UDP sockets, event sources, and TCP stream objects.
This patch just adds the basic hook-up, this function will be used more
in later commits.
|
|
|
|
Indicate thar we ignore invalid messages
|
|
Make sure we don't end up processing packets that are truncated.
Instead, actually let the TCP connection do its thing.
|
|
On LLMNR we never want to retry stream connections (since local TCP
connections should work, and we don't want to unnecessarily delay
operation), explicitly remember whether we already tried one, instead of
deriving this from a still stored stream object. This way, we can free
the stream early, without forgetting that we tried it.
|
|
Make sure to GC a transaction after dealing with a reply, even if the
transaction is not complete yet.
|
|
No need to choke on them.
|
|
Let's unify how we reset the answer data we collected, after all pretty
much every time we do it incompletely so far, let's fix it.
|
|
Previously the calls for emitting DNS UDP packets were just called
dns_{transacion|scope}_emit(), but the one to establish a DNS TCP
connection was called dns_transaction_open_tcp(). Clean this up, and
rename them dns_{transaction|scope}_emit_udp() and
dns_transaction_open_tcp().
|
|
This adds a mode that makes resolved automatically downgrade from DNSSEC
support to classic non-DNSSEC resolving if the configured DNS server is
not capable of DNSSEC. Enabling this mode increases compatibility with
crappy network equipment, but of course opens up the system to
downgrading attacks.
The new mode can be enabled by setting DNSSEC=downgrade-ok in
resolved.conf. DNSSEC=yes otoh remains a "strict" mode, where DNS
resolving rather fails then allow downgrading.
Downgrading is done:
- when the server does not support EDNS0+DO
- or when the server supports it but does not augment returned RRs with
RRSIGs. The latter is detected when requesting DS or SOA RRs for the
root domain (which is necessary to do proofs for unsigned data)
|
|
via TCP
Previously, if we couldn't reach a server via UDP we'd generate an
MAX_ATTEMPTS transaction result, but if we couldn't reach it via TCP
we'd generate a RESOURCES transaction result. While it is OK to generate
two different errors I think, "RESOURCES" is certainly a misnomer.
Introduce a new transaction result "CONNECTION_FAILURE" instead.
|
|
Previously, we'd insist on an RRSIG for all DS/NSEC/NSEC3 RRs. With this
change we don't do that anymore, but also allow unsigned DS/NSEC/NSEC3
if we can prove that the zone they are located in is unsigned.
|
|
|
|
This collects statistical data about transactions, dnssec verifications
and the cache, and exposes it over the bus. The systemd-resolve-host
tool learns new options to query these statistics and reset them.
|
|
But keep track that the proof is not authenticated.
|
|
|
|
Be stricter when searching suitable NSEC3 RRs for proof: generalize the
check we use to find suitable NSEC3 RRs, in nsec3_is_good(), and add
additional checks, such as checking whether all NSEC3 RRs use the same
parameters, have the same suffix and so on.
|
|
Let's simplify usage and memory management of DnsResourceRecord's
dns_resource_record_to_string() call: cache the formatted string as
part of the object, and return it on subsequent calls, freeing it when
the DnsResourceRecord itself is freed.
|
|
|
|
Let's make sure we propagate the DNSSEC validation status from an
auxiliary DNSSEC transaction back to the originating transaction, to
improve the error messages we generate.
|
|
the the bus client
It's useful to generate useful errors, so let's do that.
|