From 1d3db294fca96fff0a7f8cff4eeeb42460ac21ac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 3 Jan 2016 17:54:01 +0100 Subject: resolved: print a log message when we ignore an NSEC3 RR with an excessive amount of iterations --- src/resolve/resolved-dns-dnssec.c | 8 +++++--- src/resolve/resolved-dns-dnssec.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 1182201b7d..6e6e62b132 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -1073,7 +1073,7 @@ static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) { } } -int dnssec_nsec3_hash(const DnsResourceRecord *nsec3, const char *name, void *ret) { +int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX]; gcry_md_hd_t md = NULL; size_t hash_size; @@ -1089,8 +1089,10 @@ int dnssec_nsec3_hash(const DnsResourceRecord *nsec3, const char *name, void *re if (nsec3->key->type != DNS_TYPE_NSEC3) return -EINVAL; - if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) + if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) { + log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3)); return -EOPNOTSUPP; + } algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm); if (algorithm < 0) @@ -1200,7 +1202,7 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourc return dns_name_equal(a, b); } -static int nsec3_hashed_domain(const DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) { +static int nsec3_hashed_domain(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) { _cleanup_free_ char *l = NULL, *hashed_domain = NULL; uint8_t hashed[DNSSEC_HASH_SIZE_MAX]; int hashed_size; diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h index f106875027..57858d0c15 100644 --- a/src/resolve/resolved-dns-dnssec.h +++ b/src/resolve/resolved-dns-dnssec.h @@ -87,7 +87,7 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey); int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max); -int dnssec_nsec3_hash(const DnsResourceRecord *nsec3, const char *name, void *ret); +int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret); typedef enum DnssecNsecResult { DNSSEC_NSEC_NO_RR, /* No suitable NSEC/NSEC3 RR found */ -- cgit v1.2.3-54-g00ecf From 28b8191e2f391f043d380d47eb79ed9ff66f14bd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 3 Jan 2016 17:56:50 +0100 Subject: resolved: never authenticate RRsets with revoked keys --- src/resolve/resolved-dns-dnssec.c | 2 ++ src/resolve/resolved-dns-rr.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 6e6e62b132..606d681779 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -671,6 +671,8 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske return 0; if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0) return 0; + if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE)) + return 0; if (dnskey->dnskey.protocol != 3) return 0; if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm) diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 90c3629166..72bded7d48 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -34,8 +34,9 @@ typedef struct DnsResourceRecord DnsResourceRecord; typedef struct DnsTxtItem DnsTxtItem; /* DNSKEY RR flags */ -#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8) #define DNSKEY_FLAG_SEP (UINT16_C(1) << 0) +#define DNSKEY_FLAG_REVOKE (UINT16_C(1) << 7) +#define DNSKEY_FLAG_ZONE_KEY (UINT16_C(1) << 8) /* mDNS RR flags */ #define MDNS_RR_CACHE_FLUSH (UINT16_C(1) << 15) -- cgit v1.2.3-54-g00ecf From 2a0d751be498be89e18f130ca02b2197f83b6baa Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 3 Jan 2016 17:57:44 +0100 Subject: resolved: refuse revoked DNSKEYs in trust anchor --- src/resolve/resolved-dns-trust-anchor.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 03c5b9406e..bcb31bd426 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -188,6 +188,14 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u r = safe_atou16(flags, &f); if (r < 0) return log_warning_errno(r, "Failed to parse DNSKEY flags field %s on line %s:%u", flags, path, line); + if ((f & DNSKEY_FLAG_ZONE_KEY) == 0) { + log_warning("DNSKEY lacks zone key bit set on line %s:%u", path, line); + return -EINVAL; + } + if ((f & DNSKEY_FLAG_REVOKE)) { + log_warning("DNSKEY is already revoked on line %s:%u", path, line); + return -EINVAL; + } a = dnssec_algorithm_from_string(algorithm); if (a < 0) { -- cgit v1.2.3-54-g00ecf From beef6a5fc5d53be33568c3e4267c540717b791fc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Jan 2016 20:25:55 +0100 Subject: resolved: actually make use of message ID when logging about failed DNSSEC validation --- src/resolve/resolved-dns-transaction.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index f7671e070f..7df526ad43 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -19,6 +19,8 @@ along with systemd; If not, see . ***/ +#include + #include "af-list.h" #include "alloc-util.h" #include "dns-domain.h" @@ -237,6 +239,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { if (state == DNS_TRANSACTION_DNSSEC_FAILED) log_struct(LOG_NOTICE, + LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_FAILURE), LOG_MESSAGE("DNSSEC validation failed for question %s: %s", dns_transaction_key_string(t), dnssec_result_to_string(t->answer_dnssec_result)), "DNS_TRANSACTION=%" PRIu16, t->id, "DNS_QUESTION=%s", dns_transaction_key_string(t), -- cgit v1.2.3-54-g00ecf From 85aeaccc10b111e8d16d3879b7c30a219ee6e10a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Jan 2016 20:27:45 +0100 Subject: resolved: fix DNSSEC canonical ordering logic When applying canonical DNSSEC ordering for an RRset only order by the wire format of the RRs' RDATA, not by the full wire formatting. The RFC isn't particularly clear about this, but this is apparently how it is done. This fixes validation of pentagon.gov's DS RRset. --- src/resolve/resolved-dns-dnssec.c | 13 ++++++------- src/resolve/resolved-dns-rr.h | 23 ++++++++++++++++++++++- 2 files changed, 28 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 606d681779..f2180c3e35 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -116,15 +116,15 @@ static int rr_compare(const void *a, const void *b) { assert(*y); assert((*y)->wire_format); - m = MIN((*x)->wire_format_size, (*y)->wire_format_size); + m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y)); - r = memcmp((*x)->wire_format, (*y)->wire_format, m); + r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m); if (r != 0) return r; - if ((*x)->wire_format_size < (*y)->wire_format_size) + if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y)) return -1; - else if ((*x)->wire_format_size > (*y)->wire_format_size) + else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y)) return 1; return 0; @@ -605,12 +605,11 @@ int dnssec_verify_rrset( md_add_uint16(md, rr->key->class); md_add_uint32(md, rrsig->rrsig.original_ttl); - assert(rr->wire_format_rdata_offset <= rr->wire_format_size); - l = rr->wire_format_size - rr->wire_format_rdata_offset; + l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr); assert(l <= 0xFFFF); md_add_uint16(md, (uint16_t) l); - gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l); + gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l); } hash = gcry_md_read(md, 0); diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 72bded7d48..26ab36401c 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -236,7 +236,7 @@ struct DnsResourceRecord { }; static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) { - if (_unlikely_(!key)) + if (!key) return NULL; if (key->_name) @@ -245,6 +245,27 @@ static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) { return (char*) key + sizeof(DnsResourceKey); } +static inline const void* DNS_RESOURCE_RECORD_RDATA(DnsResourceRecord *rr) { + if (!rr) + return NULL; + + if (!rr->wire_format) + return NULL; + + assert(rr->wire_format_rdata_offset <= rr->wire_format_size); + return (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset; +} + +static inline size_t DNS_RESOURCE_RECORD_RDATA_SIZE(DnsResourceRecord *rr) { + if (!rr) + return 0; + if (!rr->wire_format) + return 0; + + assert(rr->wire_format_rdata_offset <= rr->wire_format_size); + return rr->wire_format_size - rr->wire_format_rdata_offset; +} + DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name); DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname); int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key, char *name); -- cgit v1.2.3-54-g00ecf From 0c8570287400ba57d3705a2f62dd26039121ea6f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Jan 2016 20:38:21 +0100 Subject: resolved: partially implement RFC5011 Trust Anchor support 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. --- src/resolve/resolved-dns-answer.c | 86 ++++++++++++++++ src/resolve/resolved-dns-answer.h | 2 + src/resolve/resolved-dns-dnssec.c | 39 +++++--- src/resolve/resolved-dns-dnssec.h | 10 +- src/resolve/resolved-dns-transaction.c | 10 +- src/resolve/resolved-dns-trust-anchor.c | 170 ++++++++++++++++++++++++++++++++ src/resolve/resolved-dns-trust-anchor.h | 2 + src/resolve/test-dnssec.c | 14 +-- src/systemd/sd-messages.h | 1 + 9 files changed, 307 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 445999f545..b50558e280 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -524,6 +524,92 @@ int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) { return 1; } +int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) { + bool found = false, other = false; + DnsResourceRecord *rr; + unsigned i; + int r; + + assert(a); + assert(rm); + + /* Remove all entries matching the specified RR from *a */ + + DNS_ANSWER_FOREACH(rr, *a) { + r = dns_resource_record_equal(rr, rm); + if (r < 0) + return r; + if (r > 0) + found = true; + else + other = true; + + if (found && other) + break; + } + + if (!found) + return 0; + + if (!other) { + *a = dns_answer_unref(*a); /* Return NULL for the empty answer */ + return 1; + } + + if ((*a)->n_ref > 1) { + _cleanup_(dns_answer_unrefp) DnsAnswer *copy = NULL; + DnsAnswerFlags flags; + int ifindex; + + copy = dns_answer_new((*a)->n_rrs); + if (!copy) + return -ENOMEM; + + DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, *a) { + r = dns_resource_record_equal(rr, rm); + if (r < 0) + return r; + if (r > 0) + continue; + + r = dns_answer_add_raw(copy, rr, ifindex, flags); + if (r < 0) + return r; + } + + dns_answer_unref(*a); + *a = copy; + copy = NULL; + + return 1; + } + + /* Only a single reference, edit in-place */ + + i = 0; + for (;;) { + if (i >= (*a)->n_rrs) + break; + + r = dns_resource_record_equal((*a)->items[i].rr, rm); + if (r < 0) + return r; + if (r > 0) { + /* Kill this entry */ + + dns_resource_record_unref((*a)->items[i].rr); + memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1)); + (*a)->n_rrs --; + continue; + + } else + /* Keep this entry */ + i++; + } + + return 1; +} + int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags) { DnsResourceRecord *rr_source; int ifindex_source, r; diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index 28ded3b252..715e487d94 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -77,6 +77,8 @@ int dns_answer_reserve(DnsAnswer **a, unsigned n_free); int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free); int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key); +int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rr); + int dns_answer_copy_by_key(DnsAnswer **a, DnsAnswer *source, const DnsResourceKey *key, DnsAnswerFlags or_flags); int dns_answer_move_by_key(DnsAnswer **to, DnsAnswer **from, const DnsResourceKey *key, DnsAnswerFlags or_flags); diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index f2180c3e35..f1edfc3970 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -79,9 +79,9 @@ static void initialize_libgcrypt(void) { gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); } -uint16_t dnssec_keytag(DnsResourceRecord *dnskey) { +uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) { const uint8_t *p; - uint32_t sum; + uint32_t sum, f; size_t i; /* The algorithm from RFC 4034, Appendix B. */ @@ -89,8 +89,12 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey) { assert(dnskey); assert(dnskey->key->type == DNS_TYPE_DNSKEY); - sum = (uint32_t) dnskey->dnskey.flags + - ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm); + f = (uint32_t) dnskey->dnskey.flags; + + if (mask_revoke) + f &= ~DNSKEY_FLAG_REVOKE; + + sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm); p = dnskey->dnskey.key; @@ -494,7 +498,7 @@ static int algorithm_to_gcrypt_md(uint8_t algorithm) { int dnssec_verify_rrset( DnsAnswer *a, - DnsResourceKey *key, + const DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, @@ -653,7 +657,7 @@ finish: return r; } -int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) { +int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) { assert(rrsig); assert(dnskey); @@ -670,14 +674,14 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske return 0; if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0) return 0; - if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE)) + if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE)) return 0; if (dnskey->dnskey.protocol != 3) return 0; if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm) return 0; - if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag) + if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag) return 0; return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer); @@ -739,7 +743,7 @@ static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsReso int dnssec_verify_rrset_search( DnsAnswer *a, - DnsResourceKey *key, + const DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result) { @@ -778,7 +782,7 @@ int dnssec_verify_rrset_search( continue; /* Is this a DNSKEY RR that matches they key of our RRSIG? */ - r = dnssec_rrsig_match_dnskey(rrsig, dnskey); + r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false); if (r < 0) return r; if (r == 0) @@ -958,7 +962,7 @@ static int digest_to_gcrypt_md(uint8_t algorithm) { } } -int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { +int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) { char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX]; gcry_md_hd_t md = NULL; size_t hash_size; @@ -976,12 +980,14 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { return -EINVAL; if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0) return -EKEYREJECTED; + if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE)) + return -EKEYREJECTED; if (dnskey->dnskey.protocol != 3) return -EKEYREJECTED; if (dnskey->dnskey.algorithm != ds->ds.algorithm) return 0; - if (dnssec_keytag(dnskey) != ds->ds.key_tag) + if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag) return 0; initialize_libgcrypt(); @@ -1005,7 +1011,10 @@ int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) { return -EIO; gcry_md_write(md, owner_name, r); - md_add_uint16(md, dnskey->dnskey.flags); + if (mask_revoke) + md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE); + else + md_add_uint16(md, dnskey->dnskey.flags); md_add_uint8(md, dnskey->dnskey.protocol); md_add_uint8(md, dnskey->dnskey.algorithm); gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size); @@ -1050,7 +1059,9 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ if (r == 0) continue; - r = dnssec_verify_dnskey(dnskey, ds); + r = dnssec_verify_dnskey(dnskey, ds, false); + if (r == -EKEYREJECTED) + return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */ if (r < 0) return r; if (r > 0) diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h index 57858d0c15..df377c8016 100644 --- a/src/resolve/resolved-dns-dnssec.h +++ b/src/resolve/resolved-dns-dnssec.h @@ -72,18 +72,18 @@ enum DnssecResult { /* The longest digest we'll ever generate, of all digest algorithms we support */ #define DNSSEC_HASH_SIZE_MAX (MAX(20, 32)) -int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey); +int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok); int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig); -int dnssec_verify_rrset(DnsAnswer *answer, DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, DnssecResult *result); -int dnssec_verify_rrset_search(DnsAnswer *answer, DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result); +int dnssec_verify_rrset(DnsAnswer *answer, const DnsResourceKey *key, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, usec_t realtime, DnssecResult *result); +int dnssec_verify_rrset_search(DnsAnswer *answer, const DnsResourceKey *key, DnsAnswer *validated_dnskeys, usec_t realtime, DnssecResult *result); -int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds); +int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke); int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds); int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key); -uint16_t dnssec_keytag(DnsResourceRecord *dnskey); +uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke); int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 7df526ad43..5fe92d701a 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1497,7 +1497,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (!ds) return -ENOMEM; - log_debug("Requesting DS to validate transaction %" PRIu16" (%s, DNSKEY with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dnssec_keytag(rr)); + log_debug("Requesting DS to validate transaction %" PRIu16" (%s, DNSKEY with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dnssec_keytag(rr, false)); r = dns_transaction_request_dnssec_rr(t, ds); if (r < 0) return r; @@ -2119,6 +2119,14 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { r = dns_answer_copy_by_key(&t->validated_keys, t->answer, rr->key, DNS_ANSWER_AUTHENTICATED); if (r < 0) return r; + + /* Maybe warn the user that we + * encountered a revoked + * DNSKEY for a key from our + * trust anchor */ + r = dns_trust_anchor_check_revoked(&t->scope->manager->trust_anchor, t->answer, rr->key); + if (r < 0) + return r; } /* Add the validated RRset to the new diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index bcb31bd426..13083b6d8d 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -19,6 +19,8 @@ along with systemd; If not, see . ***/ +#include + #include "alloc-util.h" #include "conf-files.h" #include "def.h" @@ -28,6 +30,7 @@ #include "hexdecoct.h" #include "parse-util.h" #include "resolved-dns-trust-anchor.h" +#include "resolved-dns-dnssec.h" #include "set.h" #include "string-util.h" #include "strv.h" @@ -413,3 +416,170 @@ int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) { return set_contains(d->negative_by_name, name); } + +static int dns_trust_anchor_remove_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) { + _cleanup_(dns_answer_unrefp) DnsAnswer *new_answer = NULL; + DnsAnswer *old_answer; + int r; + + old_answer = hashmap_get(d->positive_by_key, rr->key); + if (!old_answer) + return 0; + + new_answer = dns_answer_ref(old_answer); + + r = dns_answer_remove_by_rr(&new_answer, rr); + if (r <= 0) + return r; + + /* We found the key! Warn the user */ + log_struct(LOG_WARNING, + LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED), + LOG_MESSAGE("DNSSEC Trust anchor %s has been revoked. Please update the trust anchor, or upgrade your operating system."), strna(dns_resource_record_to_string(rr)), + "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr), + NULL); + + if (dns_answer_size(new_answer) <= 0) { + assert_se(hashmap_remove(d->positive_by_key, rr->key) == old_answer); + dns_answer_unref(old_answer); + return 1; + } + + r = hashmap_replace(d->positive_by_key, new_answer->items[0].rr->key, new_answer); + if (r < 0) + return r; + + new_answer = NULL; + dns_answer_unref(old_answer); + return 1; +} + +static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) { + DnsAnswer *a; + int r; + + assert(d); + assert(revoked_dnskey); + assert(revoked_dnskey->key->type == DNS_TYPE_DNSKEY); + assert(revoked_dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE); + + a = hashmap_get(d->positive_by_key, revoked_dnskey->key); + if (a) { + DnsResourceRecord *anchor; + + /* First, look for the precise DNSKEY in our trust anchor database */ + + DNS_ANSWER_FOREACH(anchor, a) { + + if (anchor->dnskey.protocol != revoked_dnskey->dnskey.protocol) + continue; + + if (anchor->dnskey.algorithm != revoked_dnskey->dnskey.algorithm) + continue; + + if (anchor->dnskey.key_size != revoked_dnskey->dnskey.key_size) + continue; + + if (((anchor->dnskey.flags ^ revoked_dnskey->dnskey.flags) | DNSKEY_FLAG_REVOKE) != DNSKEY_FLAG_REVOKE) + continue; + + if (memcmp(anchor->dnskey.key, revoked_dnskey->dnskey.key, anchor->dnskey.key_size) != 0) + continue; + + dns_trust_anchor_remove_revoked(d, anchor); + break; + } + } + + a = hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(revoked_dnskey->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(revoked_dnskey->key))); + if (a) { + DnsResourceRecord *anchor; + + /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */ + + DNS_ANSWER_FOREACH(anchor, a) { + + r = dnssec_verify_dnskey(revoked_dnskey, anchor, true); + if (r < 0) + return r; + if (r == 0) + continue; + + dns_trust_anchor_remove_revoked(d, anchor); + break; + } + } + + return 0; +} + +static bool dns_trust_anchor_knows_domain(DnsTrustAnchor *d, const char *name) { + assert(d); + + /* Returns true if there's an entry for the specified domain + * name in our trust anchor */ + + return + hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, name)) || + hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name)); +} + +int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsAnswer *rrs, const DnsResourceKey *key) { + DnsResourceRecord *dnskey; + int r; + + assert(d); + assert(key); + + /* Looks for self-signed DNSKEY RRs in "rrs" that have been revoked. */ + + if (key->type != DNS_TYPE_DNSKEY) + return 0; + + DNS_ANSWER_FOREACH(dnskey, rrs) { + DnsResourceRecord *rrsig; + DnssecResult result; + + r = dns_resource_key_equal(key, dnskey->key); + if (r < 0) + return r; + if (r == 0) + continue; + + /* Is this DNSKEY revoked? */ + if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0) + continue; + + /* Could this be interesting to us at all? If not, + * there's no point in looking for and verifying a + * self-signed RRSIG. */ + if (!dns_trust_anchor_knows_domain(d, DNS_RESOURCE_KEY_NAME(dnskey->key))) + continue; + + /* Look for a self-signed RRSIG */ + DNS_ANSWER_FOREACH(rrsig, rrs) { + + if (rrsig->key->type != DNS_TYPE_RRSIG) + continue; + + r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true); + if (r < 0) + return r; + if (r == 0) + continue; + + r = dnssec_verify_rrset(rrs, key, rrsig, dnskey, USEC_INFINITY, &result); + if (r < 0) + return r; + if (result != DNSSEC_VALIDATED) + continue; + + /* Bingo! Now, act! */ + r = dns_trust_anchor_check_revoked_one(d, dnskey); + if (r < 0) + return r; + } + } + + return 0; +} diff --git a/src/resolve/resolved-dns-trust-anchor.h b/src/resolve/resolved-dns-trust-anchor.h index 1140cde041..303c4088d1 100644 --- a/src/resolve/resolved-dns-trust-anchor.h +++ b/src/resolve/resolved-dns-trust-anchor.h @@ -39,3 +39,5 @@ void dns_trust_anchor_flush(DnsTrustAnchor *d); int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey* key, DnsAnswer **answer); int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name); + +int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsAnswer *rrs, const DnsResourceKey *key); diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index 6104d8b4c0..0c9efde1fe 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -107,10 +107,10 @@ static void test_dnssec_verify_rrset2(void) { assert_se(dnskey->dnskey.key); log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); - log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey)); + log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0); - assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey) > 0); + assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); answer = dns_answer_new(1); assert_se(answer); @@ -186,10 +186,10 @@ static void test_dnssec_verify_rrset(void) { assert_se(dnskey->dnskey.key); log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); - log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey)); + log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); assert_se(dnssec_key_match_rrsig(a->key, rrsig) > 0); - assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey) > 0); + assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); answer = dns_answer_new(1); assert_se(answer); @@ -268,10 +268,10 @@ static void test_dnssec_verify_dns_key(void) { assert_se(dnskey->dnskey.key); log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); - log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey)); + log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); - assert_se(dnssec_verify_dnskey(dnskey, ds1) > 0); - assert_se(dnssec_verify_dnskey(dnskey, ds2) > 0); + assert_se(dnssec_verify_dnskey(dnskey, ds1, false) > 0); + assert_se(dnssec_verify_dnskey(dnskey, ds2, false) > 0); } static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) { diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index bc658f62b0..1183df6105 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -87,6 +87,7 @@ _SD_BEGIN_DECLARATIONS; #define SD_MESSAGE_BOOTCHART SD_ID128_MAKE(9f,26,aa,56,2c,f4,40,c2,b1,6c,77,3d,04,79,b5,18) #define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d) +#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65) _SD_END_DECLARATIONS; -- cgit v1.2.3-54-g00ecf From 3347e6a299983557d27b7ff950199f2eda6d9bd9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Jan 2016 20:50:07 +0100 Subject: update RFCs --- src/resolve/RFCs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/resolve/RFCs b/src/resolve/RFCs index ccc7f0d640..33f4dd9cb6 100644 --- a/src/resolve/RFCs +++ b/src/resolve/RFCs @@ -9,7 +9,7 @@ Y https://tools.ietf.org/html/rfc1034 → DOMAIN NAMES - CONCEPTS AND FACILITIES Y https://tools.ietf.org/html/rfc1035 → DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION ? https://tools.ietf.org/html/rfc1101 → DNS Encoding of Network Names and Other Types Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- Application and Support - https://tools.ietf.org/html/rfc1464 → Using the Domain Name System To Store Arbitrary String Attributes +~ https://tools.ietf.org/html/rfc1464 → Using the Domain Name System To Store Arbitrary String Attributes Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System Y https://tools.ietf.org/html/rfc2181 → Clarifications to the DNS Specification @@ -30,9 +30,9 @@ Y https://tools.ietf.org/html/rfc4509 → Use of SHA-256 in DNSSEC Delegation Si ~ https://tools.ietf.org/html/rfc4592 → The Role of Wildcards in the Domain Name System ~ https://tools.ietf.org/html/rfc4697 → Observed DNS Resolution Misbehavior Y https://tools.ietf.org/html/rfc4795 → Link-Local Multicast Name Resolution (LLMNR) -! https://tools.ietf.org/html/rfc5011 → Automated Updates of DNS Security (DNSSEC) Trust Anchors +Y https://tools.ietf.org/html/rfc5011 → Automated Updates of DNS Security (DNSSEC) Trust Anchors https://tools.ietf.org/html/rfc5155 → DNS Security (DNSSEC) Hashed Authenticated Denial of Existence - https://tools.ietf.org/html/rfc5452 → Measures for Making DNS More Resilient against Forged Answers +Y https://tools.ietf.org/html/rfc5452 → Measures for Making DNS More Resilient against Forged Answers Y https://tools.ietf.org/html/rfc5702 → Use of SHA-2 Algorithms with RSA in DNSKEY and RRSIG Resource Records for DNSSEC Y https://tools.ietf.org/html/rfc5890 → Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework Y https://tools.ietf.org/html/rfc5891 → Internationalized Domain Names in Applications (IDNA): Protocol @@ -41,7 +41,7 @@ Y https://tools.ietf.org/html/rfc6303 → Locally Served DNS Zones https://tools.ietf.org/html/rfc6604 → xNAME RCODE and Status Bits Clarification Y https://tools.ietf.org/html/rfc6605 → Elliptic Curve Digital Signature Algorithm (DSA) for DNSSEC https://tools.ietf.org/html/rfc6672 → DNAME Redirection in the DNS - https://tools.ietf.org/html/rfc6731 → Improved Recursive DNS Server Selection for Multi-Interfaced Nodes +! https://tools.ietf.org/html/rfc6731 → Improved Recursive DNS Server Selection for Multi-Interfaced Nodes Y https://tools.ietf.org/html/rfc6761 → Special-Use Domain Names https://tools.ietf.org/html/rfc6762 → Multicast DNS https://tools.ietf.org/html/rfc6763 → DNS-Based Service Discovery @@ -51,7 +51,7 @@ Y https://tools.ietf.org/html/rfc6891 → Extension Mechanisms for DNS (EDNS(0)) Y https://tools.ietf.org/html/rfc6944 → Applicability Statement: DNS Security (DNSSEC) DNSKEY Algorithm Implementation Status Y https://tools.ietf.org/html/rfc6975 → Signaling Cryptographic Algorithm Understanding in DNS Security Extensions (DNSSEC) https://tools.ietf.org/html/rfc7129 → Authenticated Denial of Existence in the DNS -! https://tools.ietf.org/html/rfc7646 → Definition and Use of DNSSEC Negative Trust Anchors +Y https://tools.ietf.org/html/rfc7646 → Definition and Use of DNSSEC Negative Trust Anchors ~ https://tools.ietf.org/html/rfc7719 → DNS Terminology Also relevant: -- cgit v1.2.3-54-g00ecf From 51e399bcebefb27d6b147d90de84d07f010fa170 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Jan 2016 22:22:47 +0100 Subject: resolved: block transaction GC'ing while 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. --- src/resolve/resolved-dns-transaction.c | 27 ++++++++++++++++++++++++--- src/resolve/resolved-dns-transaction.h | 2 +- 2 files changed, 25 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 5fe92d701a..c0626626f2 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -64,6 +64,8 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { if (!t) return NULL; + log_debug("Freeing transaction %" PRIu16 ".", t->id); + dns_transaction_close_connection(t); dns_transaction_stop_timeout(t); @@ -108,16 +110,20 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free); -void dns_transaction_gc(DnsTransaction *t) { +bool dns_transaction_gc(DnsTransaction *t) { assert(t); if (t->block_gc > 0) - return; + return true; if (set_isempty(t->notify_query_candidates) && set_isempty(t->notify_zone_items) && - set_isempty(t->notify_transactions)) + set_isempty(t->notify_transactions)) { dns_transaction_free(t); + return false; + } + + return true; } int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) { @@ -721,7 +727,22 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { t->answer_dnssec_result = _DNSSEC_RESULT_INVALID; t->answer_authenticated = false; + /* Block GC while starting requests for additional DNSSEC RRs */ + t->block_gc++; r = dns_transaction_request_dnssec_keys(t); + t->block_gc--; + + /* Maybe the transaction is ready for GC'ing now? If so, free it and return. */ + if (!dns_transaction_gc(t)) + return; + + /* Requesting additional keys might have resulted in + * this transaction to fail, since the auxiliary + * request failed for some reason. If so, we are not + * in pending state anymore, and we should exit + * quickly. */ + if (t->state != DNS_TRANSACTION_PENDING) + return; if (r < 0) { dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); return; diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index faf3ce6fb9..21e453218a 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -140,7 +140,7 @@ struct DnsTransaction { int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key); DnsTransaction* dns_transaction_free(DnsTransaction *t); -void dns_transaction_gc(DnsTransaction *t); +bool dns_transaction_gc(DnsTransaction *t); int dns_transaction_go(DnsTransaction *t); void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p); -- cgit v1.2.3-54-g00ecf From f2992dc184398c6361273a39d3fde5c605e045e0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Jan 2016 22:25:38 +0100 Subject: resolved: explicitly avoid cyclic transaction dependencies 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). --- src/resolve/resolved-dns-transaction.c | 44 ++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index c0626626f2..ce320e9a97 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1233,6 +1233,28 @@ int dns_transaction_go(DnsTransaction *t) { return 1; } +static int dns_transaction_find_cyclic(DnsTransaction *t, DnsTransaction *aux) { + DnsTransaction *n; + Iterator i; + int r; + + assert(t); + assert(aux); + + /* Try to find cyclic dependencies between transaction objects */ + + if (t == aux) + return 1; + + SET_FOREACH(n, aux->notify_transactions, i) { + r = dns_transaction_find_cyclic(t, n); + if (r != 0) + return r; + } + + return r; +} + static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResourceKey *key, DnsTransaction **ret) { DnsTransaction *aux; int r; @@ -1251,6 +1273,18 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource *ret = aux; return 0; } + + r = dns_transaction_find_cyclic(t, aux); + if (r < 0) + return r; + if (r > 0) { + log_debug("Detected potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).", + aux->id, + strna(dns_transaction_key_string(aux)), + t->id, + strna(dns_transaction_key_string(t))); + return -ELOOP; + } } r = set_ensure_allocated(&t->dnssec_transactions, NULL); @@ -1287,12 +1321,6 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey * assert(t); assert(key); - r = dns_resource_key_equal(t->key, key); - if (r < 0) - return r; - if (r > 0) /* Don't go in circles */ - return 0; - /* Try to get the data from the trust anchor */ r = dns_trust_anchor_lookup_positive(&t->scope->manager->trust_anchor, key, &a); if (r < 0) @@ -1307,6 +1335,8 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey * /* This didn't work, ask for it via the network/cache then. */ r = dns_transaction_add_dnssec_transaction(t, key, &aux); + if (r == -ELOOP) /* This would result in a cyclic dependency */ + return 0; if (r < 0) return r; @@ -1316,7 +1346,7 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey * return r; } - return 0; + return 1; } static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags *flags) { -- cgit v1.2.3-54-g00ecf From a761c1ca851a9397b5a207ef600e077d0f7f4534 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Jan 2016 22:35:17 +0100 Subject: resolved: introduce a proper bus error for DNSSEC validation errors --- src/libsystemd/sd-bus/bus-common-errors.h | 1 + src/resolve/resolved-bus.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 9076993f01..46f3877d01 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -74,6 +74,7 @@ #define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted" #define BUS_ERROR_CONNECTION_FAILURE "org.freedesktop.resolve1.ConnectionFailure" #define BUS_ERROR_NO_SUCH_SERVICE "org.freedesktop.resolve1.NoSuchService" +#define BUS_ERROR_DNSSEC_FAILED "org.freedesktop.resolve1.DnssecFailed" #define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError." #define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer" diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 4d4c1ca014..2c79204f0f 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -64,7 +64,7 @@ static int reply_query_state(DnsQuery *q) { return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted"); case DNS_TRANSACTION_DNSSEC_FAILED: - return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "DNSSEC validation failed: %s", + return sd_bus_reply_method_errorf(q->request, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s", dnssec_result_to_string(q->answer_dnssec_result)); case DNS_TRANSACTION_RCODE_FAILURE: { -- cgit v1.2.3-54-g00ecf From b2b796b8ab5565fbe60b544d2579e2bfca31bf6a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Jan 2016 22:35:54 +0100 Subject: resolved: explicitly handle case when the trust anchor is empty 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. --- src/libsystemd/sd-bus/bus-common-errors.h | 1 + src/resolve/resolved-bus.c | 3 +++ src/resolve/resolved-dns-transaction.c | 36 +++++++++++++++++++++++++++++++ src/resolve/resolved-dns-transaction.h | 1 + 4 files changed, 41 insertions(+) (limited to 'src') diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 46f3877d01..9e49725843 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -75,6 +75,7 @@ #define BUS_ERROR_CONNECTION_FAILURE "org.freedesktop.resolve1.ConnectionFailure" #define BUS_ERROR_NO_SUCH_SERVICE "org.freedesktop.resolve1.NoSuchService" #define BUS_ERROR_DNSSEC_FAILED "org.freedesktop.resolve1.DnssecFailed" +#define BUS_ERROR_NO_TRUST_ANCHOR "org.freedesktop.resolve1.NoTrustAnchor" #define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError." #define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer" diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 2c79204f0f..db180a51a3 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -67,6 +67,9 @@ static int reply_query_state(DnsQuery *q) { return sd_bus_reply_method_errorf(q->request, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s", dnssec_result_to_string(q->answer_dnssec_result)); + case DNS_TRANSACTION_NO_TRUST_ANCHOR: + return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_TRUST_ANCHOR, "No suitable trust anchor known"); + case DNS_TRANSACTION_RCODE_FAILURE: { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index ce320e9a97..677d643463 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -926,6 +926,41 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); return 0; } + + if (dns_name_is_root(DNS_RESOURCE_KEY_NAME(t->key)) && + t->key->type == DNS_TYPE_DS) { + + /* Hmm, this is a request for the root DS? A + * DS RR doesn't exist in the root zone, and + * if our trust anchor didn't know it either, + * this means we cannot do any DNSSEC logic + * anymore. */ + + if (t->scope->dnssec_mode == DNSSEC_DOWNGRADE_OK) { + /* We are in downgrade mode. In this + * case, synthesize an unsigned empty + * response, so that the any lookup + * depending on this one can continue + * assuming there was no DS, and hence + * the root zone was unsigned. */ + + t->answer_rcode = DNS_RCODE_SUCCESS; + t->answer_source = DNS_TRANSACTION_TRUST_ANCHOR; + t->answer_authenticated = false; + dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); + } else + /* If we are not in downgrade mode, + * then fail the lookup, because we + * cannot reasonably answer it. There + * might be DS RRs, but we don't know + * them, and the DNS server won't tell + * them to us (and even if it would, + * we couldn't validate it and trust + * it). */ + dns_transaction_complete(t, DNS_TRANSACTION_NO_TRUST_ANCHOR); + + return 0; + } } /* Check the zone, but only if this transaction is not used @@ -2438,6 +2473,7 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] [DNS_TRANSACTION_CONNECTION_FAILURE] = "connection-failure", [DNS_TRANSACTION_ABORTED] = "aborted", [DNS_TRANSACTION_DNSSEC_FAILED] = "dnssec-failed", + [DNS_TRANSACTION_NO_TRUST_ANCHOR] = "no-trust-anchor", }; DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState); diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 21e453218a..e0f29d95e7 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -39,6 +39,7 @@ enum DnsTransactionState { DNS_TRANSACTION_CONNECTION_FAILURE, DNS_TRANSACTION_ABORTED, DNS_TRANSACTION_DNSSEC_FAILED, + DNS_TRANSACTION_NO_TRUST_ANCHOR, _DNS_TRANSACTION_STATE_MAX, _DNS_TRANSACTION_STATE_INVALID = -1 }; -- cgit v1.2.3-54-g00ecf From 6f8a2c6817e35ca3e76130b31624f7f30e596433 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Jan 2016 22:43:25 +0100 Subject: update DNSSEC TODO --- src/resolve/resolved-dns-dnssec.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index f1edfc3970..ac2362275c 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -38,10 +38,8 @@ * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing) * - multi-label zone compatibility * - cname/dname compatibility - * - per-interface DNSSEC setting * - nxdomain on qname - * - retry on failed validation? - * - DNSSEC key revocation support? https://tools.ietf.org/html/rfc5011 + * - per-interface DNSSEC setting * - when doing negative caching, use NSEC/NSEC3 RR instead of SOA for TTL * * */ -- cgit v1.2.3-54-g00ecf From d3760be01b120df8980c056ecc85a4229d660264 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 01:35:28 +0100 Subject: resolved: when caching negative responses, honour NSEC/NSEC3 TTLs 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. --- src/resolve/resolved-dns-cache.c | 17 ++++++++++------- src/resolve/resolved-dns-cache.h | 2 +- src/resolve/resolved-dns-dnssec.c | 35 +++++++++++++++++++++++----------- src/resolve/resolved-dns-dnssec.h | 2 +- src/resolve/resolved-dns-transaction.c | 5 ++++- src/resolve/resolved-dns-transaction.h | 1 + src/resolve/resolved-mdns.c | 2 +- 7 files changed, 42 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 1c7dd56b3b..301f383809 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -273,13 +273,13 @@ static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) { return NULL; } -static usec_t calculate_until(DnsResourceRecord *rr, usec_t timestamp, bool use_soa_minimum) { +static usec_t calculate_until(DnsResourceRecord *rr, uint32_t nsec_ttl, usec_t timestamp, bool use_soa_minimum) { uint32_t ttl; usec_t u; assert(rr); - ttl = rr->ttl; + ttl = MIN(rr->ttl, nsec_ttl); if (rr->key->type == DNS_TYPE_SOA && use_soa_minimum) { /* If this is a SOA RR, and it is requested, clamp to * the SOA's minimum field. This is used when we do @@ -339,7 +339,7 @@ static void dns_cache_item_update_positive( dns_resource_key_unref(i->key); i->key = dns_resource_key_ref(rr->key); - i->until = calculate_until(rr, timestamp, false); + i->until = calculate_until(rr, (uint32_t) -1, timestamp, false); i->authenticated = authenticated; i->shared_owner = shared_owner; @@ -420,7 +420,7 @@ static int dns_cache_put_positive( i->type = DNS_CACHE_POSITIVE; i->key = dns_resource_key_ref(rr->key); i->rr = dns_resource_record_ref(rr); - i->until = calculate_until(rr, timestamp, false); + i->until = calculate_until(rr, (uint32_t) -1, timestamp, false); i->authenticated = authenticated; i->shared_owner = shared_owner; i->owner_family = owner_family; @@ -448,6 +448,7 @@ static int dns_cache_put_negative( DnsResourceKey *key, int rcode, bool authenticated, + uint32_t nsec_ttl, usec_t timestamp, DnsResourceRecord *soa, int owner_family, @@ -470,13 +471,13 @@ static int dns_cache_put_negative( if (dns_type_is_pseudo(key->type)) return 0; - if (soa->soa.minimum <= 0 || soa->ttl <= 0) { + if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) { if (log_get_max_level() >= LOG_DEBUG) { r = dns_resource_key_to_string(key, &key_str); if (r < 0) return r; - log_debug("Not caching negative entry with zero SOA TTL: %s", key_str); + log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", key_str); } return 0; @@ -496,7 +497,7 @@ static int dns_cache_put_negative( return -ENOMEM; i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN; - i->until = calculate_until(soa, timestamp, true); + i->until = calculate_until(soa, nsec_ttl, timestamp, true); i->authenticated = authenticated; i->owner_family = owner_family; i->owner_address = *owner_address; @@ -571,6 +572,7 @@ int dns_cache_put( int rcode, DnsAnswer *answer, bool authenticated, + uint32_t nsec_ttl, usec_t timestamp, int owner_family, const union in_addr_union *owner_address) { @@ -669,6 +671,7 @@ int dns_cache_put( key, rcode, authenticated, + nsec_ttl, timestamp, soa, owner_family, owner_address); diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index 9c85ca4c58..e61b285df4 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -41,7 +41,7 @@ typedef struct DnsCache { void dns_cache_flush(DnsCache *c); void dns_cache_prune(DnsCache *c); -int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, usec_t timestamp, int owner_family, const union in_addr_union *owner_address); +int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, uint32_t nsec_ttl, usec_t timestamp, int owner_family, const union in_addr_union *owner_address); int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer, bool *authenticated); int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address); diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index ac2362275c..32d4834aa1 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -40,7 +40,6 @@ * - cname/dname compatibility * - nxdomain on qname * - per-interface DNSSEC setting - * - when doing negative caching, use NSEC/NSEC3 RR instead of SOA for TTL * * */ @@ -1250,7 +1249,7 @@ static int nsec3_hashed_domain(DnsResourceRecord *nsec3, const char *domain, con * that there is no proof either way. The latter is the case if a the proof of non-existence of a given * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records * to conclude anything we indicate this by returning NO_RR. */ -static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) { +static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL; const char *zone, *p, *pp = NULL; DnsResourceRecord *rr, *enclosure_rr, *suffix_rr, *wildcard_rr = NULL; @@ -1260,7 +1259,6 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR assert(key); assert(result); - assert(authenticated); /* First step, find the zone name and the NSEC3 parameters of the zone. * it is sufficient to look for the longest common suffix we find with @@ -1369,7 +1367,10 @@ found_closest_encloser: else *result = DNSSEC_NSEC_NODATA; - *authenticated = a; + if (authenticated) + *authenticated = a; + if (ttl) + *ttl = enclosure_rr->ttl; return 0; } @@ -1452,7 +1453,6 @@ found_closest_encloser: if (!no_closer) { *result = DNSSEC_NSEC_NO_RR; - return 0; } @@ -1488,12 +1488,16 @@ found_closest_encloser: } } - *authenticated = a; + if (authenticated) + *authenticated = a; + + if (ttl) + *ttl = enclosure_rr->ttl; return 0; } -int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) { +int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { DnsResourceRecord *rr; bool have_nsec3 = false; DnsAnswerFlags flags; @@ -1501,7 +1505,6 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r assert(key); assert(result); - assert(authenticated); /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */ @@ -1524,7 +1527,12 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r *result = DNSSEC_NSEC_CNAME; else *result = DNSSEC_NSEC_NODATA; - *authenticated = flags & DNS_ANSWER_AUTHENTICATED; + + if (authenticated) + *authenticated = flags & DNS_ANSWER_AUTHENTICATED; + if (ttl) + *ttl = rr->ttl; + return 0; } @@ -1533,7 +1541,12 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r return r; if (r > 0) { *result = DNSSEC_NSEC_NXDOMAIN; - *authenticated = flags & DNS_ANSWER_AUTHENTICATED; + + if (authenticated) + *authenticated = flags & DNS_ANSWER_AUTHENTICATED; + if (ttl) + *ttl = rr->ttl; + return 0; } break; @@ -1546,7 +1559,7 @@ int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r /* OK, this was not sufficient. Let's see if NSEC3 can help. */ if (have_nsec3) - return dnssec_test_nsec3(answer, key, result, authenticated); + return dnssec_test_nsec3(answer, key, result, authenticated, ttl); /* No approproate NSEC RR found, report this. */ *result = DNSSEC_NSEC_NO_RR; diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h index df377c8016..94d0b23f80 100644 --- a/src/resolve/resolved-dns-dnssec.h +++ b/src/resolve/resolved-dns-dnssec.h @@ -99,7 +99,7 @@ typedef enum DnssecNsecResult { DNSSEC_NSEC_OPTOUT, } DnssecNsecResult; -int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated); +int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl); const char* dnssec_mode_to_string(DnssecMode m) _const_; DnssecMode dnssec_mode_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 677d643463..870b7586fd 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -40,6 +40,7 @@ static void dns_transaction_reset_answer(DnsTransaction *t) { t->answer_dnssec_result = _DNSSEC_RESULT_INVALID; t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID; t->answer_authenticated = false; + t->answer_nsec_ttl = (uint32_t) -1; } static void dns_transaction_close_connection(DnsTransaction *t) { @@ -157,6 +158,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) t->dns_udp_fd = -1; t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID; t->answer_dnssec_result = _DNSSEC_RESULT_INVALID; + t->answer_nsec_ttl = (uint32_t) -1; t->key = dns_resource_key_ref(key); /* Find a fresh, unused transaction id */ @@ -482,6 +484,7 @@ static void dns_transaction_cache_answer(DnsTransaction *t) { t->answer_rcode, t->answer, t->answer_authenticated, + t->answer_nsec_ttl, 0, t->received->family, &t->received->sender); @@ -2385,7 +2388,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { bool authenticated = false; /* Bummer! Let's check NSEC/NSEC3 */ - r = dnssec_test_nsec(t->answer, t->key, &nr, &authenticated); + r = dnssec_test_nsec(t->answer, t->key, &nr, &authenticated, &t->answer_nsec_ttl); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index e0f29d95e7..ede33f9547 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -81,6 +81,7 @@ struct DnsTransaction { int answer_rcode; DnssecResult answer_dnssec_result; DnsTransactionSource answer_source; + uint32_t answer_nsec_ttl; /* Indicates whether the primary answer is authenticated, * i.e. whether the RRs from answer which directly match the diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index db23bc9d42..7c1012f4ea 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -122,7 +122,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us dns_transaction_process_reply(t, p); } - dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, 0, p->family, &p->sender); + dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender); } else if (dns_packet_validate_query(p) > 0) { log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p)); -- cgit v1.2.3-54-g00ecf From e7d179acb9f50dec4a16c6c222dd147d7e53c6bb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 14:18:18 +0100 Subject: resolved: move trust anchor files to /etc/dnssec-trust-anchors.d/ These files are not specific to resolved really, and this is then more in-line with how /etc/sysctl.d and suchlike is handled. --- src/resolve/resolved-dns-trust-anchor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 13083b6d8d..432a8a6455 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -35,7 +35,7 @@ #include "string-util.h" #include "strv.h" -static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("systemd/dnssec-trust-anchors.d"); +static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d"); /* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */ static const uint8_t root_digest[] = -- cgit v1.2.3-54-g00ecf From d76f90f1711e55d23ee6c8c0957fa3db17927327 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jan 2016 14:19:05 +0100 Subject: resolved: also skip built-in trust anchor addition of there's a DNSKEY RR for the root domain defined We already skip this when the trust anchor files define a DS RR for the root domain, now also skip it if there's a DNSKEY RR. --- src/resolve/resolved-dns-trust-anchor.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 432a8a6455..53b49b091a 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -56,6 +56,9 @@ static int dns_trust_anchor_add_builtin(DnsTrustAnchor *d) { if (hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, "."))) return 0; + if (hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "."))) + return 0; + /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, ""); if (!rr) -- cgit v1.2.3-54-g00ecf