diff options
author | Lennart Poettering <lennart@poettering.net> | 2015-12-25 15:05:46 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2015-12-26 19:09:11 +0100 |
commit | b652d4a2099d1c167584dcc1d179d47c58dc38a2 (patch) | |
tree | 3d597bd8be7cb96040d2d4a2cf8f08a7e685272e /src/resolve/resolved-dns-transaction.c | |
parent | 0e4fb6b2dedf590741220b806c92e9e68857b457 (diff) |
resolved: add an automatic downgrade to non-DNSSEC mode
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)
Diffstat (limited to 'src/resolve/resolved-dns-transaction.c')
-rw-r--r-- | src/resolve/resolved-dns-transaction.c | 75 |
1 files changed, 67 insertions, 8 deletions
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 2875330812..5933e0e462 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -478,10 +478,19 @@ static void dns_transaction_process_dnssec(DnsTransaction *t) { return; } + if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER && + t->scope->dnssec_mode == DNSSEC_YES) { + /* We are not in automatic downgrade mode, and the + * server is bad, refuse operation. */ + dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED); + return; + } + if (!IN_SET(t->answer_dnssec_result, - _DNSSEC_RESULT_INVALID, /* No DNSSEC validation enabled */ - DNSSEC_VALIDATED, /* Answer is signed and validated successfully */ - DNSSEC_UNSIGNED)) { /* Answer is right-fully unsigned */ + _DNSSEC_RESULT_INVALID, /* No DNSSEC validation enabled */ + DNSSEC_VALIDATED, /* Answer is signed and validated successfully */ + DNSSEC_UNSIGNED, /* Answer is right-fully unsigned */ + DNSSEC_INCOMPATIBLE_SERVER)) { /* Server does not do DNSSEC (Yay, we are downgrade attack vulnerable!) */ dns_transaction_complete(t, DNS_TRANSACTION_DNSSEC_FAILED); return; } @@ -1001,7 +1010,7 @@ static int dns_transaction_make_packet(DnsTransaction *t) { if (t->sent) return 0; - r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode == DNSSEC_YES); + r = dns_packet_new_query(&p, t->scope->protocol, 0, t->scope->dnssec_mode != DNSSEC_NO); if (r < 0) return r; @@ -1336,9 +1345,14 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR */ - if (t->scope->dnssec_mode != DNSSEC_YES) + if (t->scope->dnssec_mode == DNSSEC_NO) return 0; + if (t->current_features < DNS_SERVER_FEATURE_LEVEL_DO) + return 0; /* Server doesn't do DNSSEC, there's no point in requesting any RRs then. */ + if (t->server && t->server->rrsig_missing) + return 0; /* Server handles DNSSEC requests, but isn't augmenting responses with RRSIGs. No point in trying DNSSEC then. */ + DNS_ANSWER_FOREACH(rr, t->answer) { if (dns_type_is_pseudo(rr->key->type)) @@ -1682,7 +1696,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * /* Checks if the RR we are looking for must be signed with an * RRSIG. This is used for positive responses. */ - if (t->scope->dnssec_mode != DNSSEC_YES) + if (t->scope->dnssec_mode == DNSSEC_NO) return false; if (dns_type_is_pseudo(rr->key->type)) @@ -1819,7 +1833,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { /* Checks if we need to insist on NSEC/NSEC3 RRs for proving * this negative reply */ - if (t->scope->dnssec_mode != DNSSEC_YES) + if (t->scope->dnssec_mode == DNSSEC_NO) return false; if (dns_type_is_pseudo(t->key->type)) @@ -1932,6 +1946,17 @@ static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRe return found ? false : -ENXIO; } +static int dns_transaction_known_signed(DnsTransaction *t, DnsResourceRecord *rr) { + assert(t); + assert(rr); + + /* We know that the root domain is signed, hence if it appears + * not to be signed, there's a problem with the DNS server */ + + return rr->key->class == DNS_CLASS_IN && + dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key)); +} + int dns_transaction_validate_dnssec(DnsTransaction *t) { _cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL; bool dnskeys_finalized = false; @@ -1945,7 +1970,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { * t->validated_keys, let's see which RRs we can now * authenticate with that. */ - if (t->scope->dnssec_mode != DNSSEC_YES) + if (t->scope->dnssec_mode == DNSSEC_NO) return 0; /* Already validated */ @@ -1963,6 +1988,13 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { if (t->answer_source != DNS_TRANSACTION_NETWORK) return 0; + if (t->current_features < DNS_SERVER_FEATURE_LEVEL_DO || + (t->server && t->server->rrsig_missing)) { + /* The server does not support DNSSEC, or doesn't augment responses with RRSIGs. */ + t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER; + return 0; + } + log_debug("Validating response from transaction %" PRIu16 " (%s).", t->id, dns_transaction_key_string(t)); /* First see if there are DNSKEYs we already known a validated DS for. */ @@ -2037,6 +2069,33 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { changed = true; break; } + + r = dns_transaction_known_signed(t, rr); + if (r < 0) + return r; + if (r > 0) { + /* This is an RR we know has to be signed. If it isn't this means + * the server is not attaching RRSIGs, hence complain. */ + + dns_server_packet_rrsig_missing(t->server); + + if (t->scope->dnssec_mode == DNSSEC_DOWNGRADE_OK) { + + /* Downgrading is OK? If so, just consider the information unsigned */ + + r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0); + if (r < 0) + return r; + + t->scope->manager->n_dnssec_insecure++; + changed = true; + break; + } + + /* Otherwise, fail */ + t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER; + return 0; + } } if (IN_SET(result, |