assert(s);
if (features == DNS_SERVER_FEATURE_LEVEL_LARGE) {
- /* even if we successfully receive a reply to a request announcing
- support for large packets, that does not mean we can necessarily
- receive large packets. */
+ /* Even if we successfully receive a reply to a
+ request announcing support for large packets, that
+ does not mean we can necessarily receive large
+ packets. */
+
if (s->verified_features < DNS_SERVER_FEATURE_LEVEL_LARGE - 1) {
s->verified_features = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
s->n_failed_attempts = (unsigned) -1;
}
+void dns_server_packet_rrsig_missing(DnsServer *s) {
+ _cleanup_free_ char *ip = NULL;
+ assert(s);
+ assert(s->manager);
+
+ in_addr_to_string(s->family, &s->address, &ip);
+ log_warning("DNS server %s does not augment replies with RRSIG records, DNSSEC not available.", strna(ip));
+
+ s->rrsig_missing = true;
+}
+
static bool dns_server_grace_period_expired(DnsServer *s) {
usec_t ts;
s->possible_features = DNS_SERVER_FEATURE_LEVEL_BEST;
s->n_failed_attempts = 0;
s->verified_usec = 0;
+ s->rrsig_missing = false;
in_addr_to_string(s->family, &s->address, &ip);
log_info("Grace period over, resuming full feature set for DNS server %s", strna(ip));
int family;
union in_addr_union address;
- bool marked:1;
-
usec_t resend_timeout;
usec_t max_rtt;
usec_t verified_usec;
usec_t features_grace_period_usec;
+ /* Indicates whether responses are augmented with RRSIG by
+ * server or not. Note that this is orthogonal to the feature
+ * level stuff, as it's only information describing responses,
+ * and has no effect on how the questions are asked. */
+ bool rrsig_missing:1;
+
+ /* Used when GC'ing old DNS servers when configuration changes. */
+ bool marked:1;
+
/* If linked is set, then this server appears in the servers linked list */
bool linked:1;
LIST_FIELDS(DnsServer, servers);
void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel features, usec_t rtt, size_t size);
void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel features, usec_t usec);
void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel features);
+void dns_server_packet_rrsig_missing(DnsServer *s);
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr);
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;
}
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;
* - 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))
/* 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))
/* 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))
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;
* 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 */
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. */
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,