1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include "sd-messages.h"
23 #include "alloc-util.h"
24 #include "conf-files.h"
26 #include "dns-domain.h"
29 #include "hexdecoct.h"
30 #include "parse-util.h"
31 #include "resolved-dns-trust-anchor.h"
32 #include "resolved-dns-dnssec.h"
34 #include "string-util.h"
37 static const char trust_anchor_dirs
[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
39 /* The first DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
40 static const uint8_t root_digest1
[] =
41 { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
42 0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
44 /* The second DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved February 2017 */
45 static const uint8_t root_digest2
[] =
46 { 0xE0, 0x6D, 0x44, 0xB8, 0x0B, 0x8F, 0x1D, 0x39, 0xA9, 0x5C, 0x0B, 0x0D, 0x7C, 0x65, 0xD0, 0x84,
47 0x58, 0xE8, 0x80, 0x40, 0x9B, 0xBC, 0x68, 0x34, 0x57, 0x10, 0x42, 0x37, 0xC7, 0xF8, 0xEC, 0x8D };
49 static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor
*d
, const char *name
) {
52 /* Returns true if there's an entry for the specified domain
53 * name in our trust anchor */
56 hashmap_contains(d
->positive_by_key
, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN
, DNS_TYPE_DNSKEY
, name
)) ||
57 hashmap_contains(d
->positive_by_key
, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN
, DNS_TYPE_DS
, name
));
60 static int add_root_ksk(
69 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*rr
= NULL
;
72 rr
= dns_resource_record_new(key
);
76 rr
->ds
.key_tag
= key_tag
;
77 rr
->ds
.algorithm
= algorithm
;
78 rr
->ds
.digest_type
= digest_type
;
79 rr
->ds
.digest_size
= digest_size
;
80 rr
->ds
.digest
= memdup(digest
, rr
->ds
.digest_size
);
84 r
= dns_answer_add(answer
, rr
, 0, DNS_ANSWER_AUTHENTICATED
);
91 static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor
*d
) {
92 _cleanup_(dns_answer_unrefp
) DnsAnswer
*answer
= NULL
;
93 _cleanup_(dns_resource_key_unrefp
) DnsResourceKey
*key
= NULL
;
98 r
= hashmap_ensure_allocated(&d
->positive_by_key
, &dns_resource_key_hash_ops
);
102 /* Only add the built-in trust anchor if there's neither a DS nor a DNSKEY defined for the root domain. That
103 * way users have an easy way to override the root domain DS/DNSKEY data. */
104 if (dns_trust_anchor_knows_domain_positive(d
, "."))
107 key
= dns_resource_key_new(DNS_CLASS_IN
, DNS_TYPE_DS
, "");
111 answer
= dns_answer_new(2);
115 /* Add the two RRs from https://data.iana.org/root-anchors/root-anchors.xml */
116 r
= add_root_ksk(answer
, key
, 19036, DNSSEC_ALGORITHM_RSASHA256
, DNSSEC_DIGEST_SHA256
, root_digest1
, sizeof(root_digest1
));
120 r
= add_root_ksk(answer
, key
, 20326, DNSSEC_ALGORITHM_RSASHA256
, DNSSEC_DIGEST_SHA256
, root_digest2
, sizeof(root_digest2
));
124 r
= hashmap_put(d
->positive_by_key
, key
, answer
);
132 static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor
*d
) {
134 static const char private_domains
[] =
135 /* RFC 6761 says that .test is a special domain for
136 * testing and not to be installed in the root zone */
139 /* RFC 6761 says that these reverse IP lookup ranges
140 * are for private addresses, and hence should not
141 * show up in the root zone */
143 "16.172.in-addr.arpa\0"
144 "17.172.in-addr.arpa\0"
145 "18.172.in-addr.arpa\0"
146 "19.172.in-addr.arpa\0"
147 "20.172.in-addr.arpa\0"
148 "21.172.in-addr.arpa\0"
149 "22.172.in-addr.arpa\0"
150 "23.172.in-addr.arpa\0"
151 "24.172.in-addr.arpa\0"
152 "25.172.in-addr.arpa\0"
153 "26.172.in-addr.arpa\0"
154 "27.172.in-addr.arpa\0"
155 "28.172.in-addr.arpa\0"
156 "29.172.in-addr.arpa\0"
157 "30.172.in-addr.arpa\0"
158 "31.172.in-addr.arpa\0"
159 "168.192.in-addr.arpa\0"
161 /* The same, but for IPv6. */
164 /* RFC 6762 reserves the .local domain for Multicast
165 * DNS, it hence cannot appear in the root zone. (Note
166 * that we by default do not route .local traffic to
167 * DNS anyway, except when a configured search domain
171 /* These two are well known, popular private zone
172 * TLDs, that are blocked from delegation, according
174 * http://icannwiki.com/Name_Collision#NGPC_Resolution
176 * There's also ongoing work on making this official
178 * https://www.ietf.org/archive/id/draft-chapin-additional-reserved-tlds-02.txt */
182 /* The following four TLDs are suggested for private
183 * zones in RFC 6762, Appendix G, and are hence very
184 * unlikely to be made official TLDs any day soon */
195 /* Only add the built-in trust anchor if there's no negative
196 * trust anchor defined at all. This enables easy overriding
197 * of negative trust anchors. */
199 if (set_size(d
->negative_by_name
) > 0)
202 r
= set_ensure_allocated(&d
->negative_by_name
, &dns_name_hash_ops
);
206 /* We add a couple of domains as default negative trust
207 * anchors, where it's very unlikely they will be installed in
208 * the root zone. If they exist they must be private, and thus
211 NULSTR_FOREACH(name
, private_domains
) {
213 if (dns_trust_anchor_knows_domain_positive(d
, name
))
216 r
= set_put_strdup(d
->negative_by_name
, name
);
224 static int dns_trust_anchor_load_positive(DnsTrustAnchor
*d
, const char *path
, unsigned line
, const char *s
) {
225 _cleanup_(dns_resource_record_unrefp
) DnsResourceRecord
*rr
= NULL
;
226 _cleanup_free_
char *domain
= NULL
, *class = NULL
, *type
= NULL
;
227 _cleanup_(dns_answer_unrefp
) DnsAnswer
*answer
= NULL
;
228 DnsAnswer
*old_answer
= NULL
;
235 r
= extract_first_word(&p
, &domain
, NULL
, EXTRACT_QUOTES
);
237 return log_warning_errno(r
, "Unable to parse domain in line %s:%u: %m", path
, line
);
239 if (!dns_name_is_valid(domain
)) {
240 log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain
, path
, line
);
244 r
= extract_many_words(&p
, NULL
, 0, &class, &type
, NULL
);
246 return log_warning_errno(r
, "Unable to parse class and type in line %s:%u: %m", path
, line
);
248 log_warning("Missing class or type in line %s:%u", path
, line
);
252 if (!strcaseeq(class, "IN")) {
253 log_warning("RR class %s is not supported, ignoring line %s:%u.", class, path
, line
);
257 if (strcaseeq(type
, "DS")) {
258 _cleanup_free_
char *key_tag
= NULL
, *algorithm
= NULL
, *digest_type
= NULL
, *digest
= NULL
;
259 _cleanup_free_
void *dd
= NULL
;
264 r
= extract_many_words(&p
, NULL
, 0, &key_tag
, &algorithm
, &digest_type
, &digest
, NULL
);
266 log_warning_errno(r
, "Failed to parse DS parameters on line %s:%u: %m", path
, line
);
270 log_warning("Missing DS parameters on line %s:%u", path
, line
);
274 r
= safe_atou16(key_tag
, &kt
);
276 return log_warning_errno(r
, "Failed to parse DS key tag %s on line %s:%u: %m", key_tag
, path
, line
);
278 a
= dnssec_algorithm_from_string(algorithm
);
280 log_warning("Failed to parse DS algorithm %s on line %s:%u", algorithm
, path
, line
);
284 dt
= dnssec_digest_from_string(digest_type
);
286 log_warning("Failed to parse DS digest type %s on line %s:%u", digest_type
, path
, line
);
290 r
= unhexmem(digest
, strlen(digest
), &dd
, &l
);
292 log_warning("Failed to parse DS digest %s on line %s:%u", digest
, path
, line
);
296 rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_DS
, domain
);
301 rr
->ds
.algorithm
= a
;
302 rr
->ds
.digest_type
= dt
;
303 rr
->ds
.digest_size
= l
;
307 } else if (strcaseeq(type
, "DNSKEY")) {
308 _cleanup_free_
char *flags
= NULL
, *protocol
= NULL
, *algorithm
= NULL
, *key
= NULL
;
309 _cleanup_free_
void *k
= NULL
;
314 r
= extract_many_words(&p
, NULL
, 0, &flags
, &protocol
, &algorithm
, &key
, NULL
);
316 return log_warning_errno(r
, "Failed to parse DNSKEY parameters on line %s:%u: %m", path
, line
);
318 log_warning("Missing DNSKEY parameters on line %s:%u", path
, line
);
322 if (!streq(protocol
, "3")) {
323 log_warning("DNSKEY Protocol is not 3 on line %s:%u", path
, line
);
327 r
= safe_atou16(flags
, &f
);
329 return log_warning_errno(r
, "Failed to parse DNSKEY flags field %s on line %s:%u", flags
, path
, line
);
330 if ((f
& DNSKEY_FLAG_ZONE_KEY
) == 0) {
331 log_warning("DNSKEY lacks zone key bit set on line %s:%u", path
, line
);
334 if ((f
& DNSKEY_FLAG_REVOKE
)) {
335 log_warning("DNSKEY is already revoked on line %s:%u", path
, line
);
339 a
= dnssec_algorithm_from_string(algorithm
);
341 log_warning("Failed to parse DNSKEY algorithm %s on line %s:%u", algorithm
, path
, line
);
345 r
= unbase64mem(key
, strlen(key
), &k
, &l
);
347 return log_warning_errno(r
, "Failed to parse DNSKEY key data %s on line %s:%u", key
, path
, line
);
349 rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_DNSKEY
, domain
);
353 rr
->dnskey
.flags
= f
;
354 rr
->dnskey
.protocol
= 3;
355 rr
->dnskey
.algorithm
= a
;
356 rr
->dnskey
.key_size
= l
;
361 log_warning("RR type %s is not supported, ignoring line %s:%u.", type
, path
, line
);
366 log_warning("Trailing garbage on line %s:%u, ignoring line.", path
, line
);
370 r
= hashmap_ensure_allocated(&d
->positive_by_key
, &dns_resource_key_hash_ops
);
374 old_answer
= hashmap_get(d
->positive_by_key
, rr
->key
);
375 answer
= dns_answer_ref(old_answer
);
377 r
= dns_answer_add_extend(&answer
, rr
, 0, DNS_ANSWER_AUTHENTICATED
);
379 return log_error_errno(r
, "Failed to add trust anchor RR: %m");
381 r
= hashmap_replace(d
->positive_by_key
, rr
->key
, answer
);
383 return log_error_errno(r
, "Failed to add answer to trust anchor: %m");
385 old_answer
= dns_answer_unref(old_answer
);
391 static int dns_trust_anchor_load_negative(DnsTrustAnchor
*d
, const char *path
, unsigned line
, const char *s
) {
392 _cleanup_free_
char *domain
= NULL
;
399 r
= extract_first_word(&p
, &domain
, NULL
, EXTRACT_QUOTES
);
401 return log_warning_errno(r
, "Unable to parse line %s:%u: %m", path
, line
);
403 if (!dns_name_is_valid(domain
)) {
404 log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain
, path
, line
);
409 log_warning("Trailing garbage at line %s:%u, ignoring line.", path
, line
);
413 r
= set_ensure_allocated(&d
->negative_by_name
, &dns_name_hash_ops
);
417 r
= set_put(d
->negative_by_name
, domain
);
426 static int dns_trust_anchor_load_files(
429 int (*loader
)(DnsTrustAnchor
*d
, const char *path
, unsigned n
, const char *line
)) {
431 _cleanup_strv_free_
char **files
= NULL
;
439 r
= conf_files_list_nulstr(&files
, suffix
, NULL
, 0, trust_anchor_dirs
);
441 return log_error_errno(r
, "Failed to enumerate %s trust anchor files: %m", suffix
);
443 STRV_FOREACH(f
, files
) {
444 _cleanup_fclose_
FILE *g
= NULL
;
453 log_warning_errno(errno
, "Failed to open %s: %m", *f
);
457 FOREACH_LINE(line
, g
, log_warning_errno(errno
, "Failed to read %s, ignoring: %m", *f
)) {
469 (void) loader(d
, *f
, n
, l
);
476 static int domain_name_cmp(const void *a
, const void *b
) {
477 char **x
= (char**) a
, **y
= (char**) b
;
479 return dns_name_compare_func(*x
, *y
);
482 static int dns_trust_anchor_dump(DnsTrustAnchor
*d
) {
488 if (hashmap_isempty(d
->positive_by_key
))
489 log_info("No positive trust anchors defined.");
491 log_info("Positive Trust Anchors:");
492 HASHMAP_FOREACH(a
, d
->positive_by_key
, i
) {
493 DnsResourceRecord
*rr
;
495 DNS_ANSWER_FOREACH(rr
, a
)
496 log_info("%s", dns_resource_record_to_string(rr
));
500 if (set_isempty(d
->negative_by_name
))
501 log_info("No negative trust anchors defined.");
503 _cleanup_free_
char **l
= NULL
, *j
= NULL
;
505 l
= set_get_strv(d
->negative_by_name
);
509 qsort_safe(l
, set_size(d
->negative_by_name
), sizeof(char*), domain_name_cmp
);
511 j
= strv_join(l
, " ");
515 log_info("Negative trust anchors: %s", j
);
521 int dns_trust_anchor_load(DnsTrustAnchor
*d
) {
526 /* If loading things from disk fails, we don't consider this fatal */
527 (void) dns_trust_anchor_load_files(d
, ".positive", dns_trust_anchor_load_positive
);
528 (void) dns_trust_anchor_load_files(d
, ".negative", dns_trust_anchor_load_negative
);
530 /* However, if the built-in DS fails, then we have a problem. */
531 r
= dns_trust_anchor_add_builtin_positive(d
);
533 return log_error_errno(r
, "Failed to add built-in positive trust anchor: %m");
535 r
= dns_trust_anchor_add_builtin_negative(d
);
537 return log_error_errno(r
, "Failed to add built-in negative trust anchor: %m");
539 dns_trust_anchor_dump(d
);
544 void dns_trust_anchor_flush(DnsTrustAnchor
*d
) {
547 d
->positive_by_key
= hashmap_free_with_destructor(d
->positive_by_key
, dns_answer_unref
);
548 d
->revoked_by_rr
= set_free_with_destructor(d
->revoked_by_rr
, dns_resource_record_unref
);
549 d
->negative_by_name
= set_free_free(d
->negative_by_name
);
552 int dns_trust_anchor_lookup_positive(DnsTrustAnchor
*d
, const DnsResourceKey
*key
, DnsAnswer
**ret
) {
559 /* We only serve DS and DNSKEY RRs. */
560 if (!IN_SET(key
->type
, DNS_TYPE_DS
, DNS_TYPE_DNSKEY
))
563 a
= hashmap_get(d
->positive_by_key
, key
);
567 *ret
= dns_answer_ref(a
);
571 int dns_trust_anchor_lookup_negative(DnsTrustAnchor
*d
, const char *name
) {
578 /* If the domain is listed as-is in the NTA database, then that counts */
579 if (set_contains(d
->negative_by_name
, name
))
582 /* If the domain isn't listed as NTA, but is listed as positive trust anchor, then that counts. See RFC
583 * 7646, section 1.1 */
584 if (hashmap_contains(d
->positive_by_key
, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN
, DNS_TYPE_DS
, name
)))
587 if (hashmap_contains(d
->positive_by_key
, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN
, DNS_TYPE_KEY
, name
)))
590 /* And now, let's look at the parent, and check that too */
591 r
= dns_name_parent(&name
);
601 static int dns_trust_anchor_revoked_put(DnsTrustAnchor
*d
, DnsResourceRecord
*rr
) {
606 r
= set_ensure_allocated(&d
->revoked_by_rr
, &dns_resource_record_hash_ops
);
610 r
= set_put(d
->revoked_by_rr
, rr
);
614 dns_resource_record_ref(rr
);
619 static int dns_trust_anchor_remove_revoked(DnsTrustAnchor
*d
, DnsResourceRecord
*rr
) {
620 _cleanup_(dns_answer_unrefp
) DnsAnswer
*new_answer
= NULL
;
621 DnsAnswer
*old_answer
;
624 /* Remember that this is a revoked trust anchor RR */
625 r
= dns_trust_anchor_revoked_put(d
, rr
);
629 /* Remove this from the positive trust anchor */
630 old_answer
= hashmap_get(d
->positive_by_key
, rr
->key
);
634 new_answer
= dns_answer_ref(old_answer
);
636 r
= dns_answer_remove_by_rr(&new_answer
, rr
);
640 /* We found the key! Warn the user */
641 log_struct(LOG_WARNING
,
642 "MESSAGE_ID=" SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR
,
643 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
)),
644 "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr
),
647 if (dns_answer_size(new_answer
) <= 0) {
648 assert_se(hashmap_remove(d
->positive_by_key
, rr
->key
) == old_answer
);
649 dns_answer_unref(old_answer
);
653 r
= hashmap_replace(d
->positive_by_key
, new_answer
->items
[0].rr
->key
, new_answer
);
658 dns_answer_unref(old_answer
);
662 static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor
*d
, DnsResourceRecord
*revoked_dnskey
) {
667 assert(revoked_dnskey
);
668 assert(revoked_dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
669 assert(revoked_dnskey
->dnskey
.flags
& DNSKEY_FLAG_REVOKE
);
671 a
= hashmap_get(d
->positive_by_key
, revoked_dnskey
->key
);
673 DnsResourceRecord
*anchor
;
675 /* First, look for the precise DNSKEY in our trust anchor database */
677 DNS_ANSWER_FOREACH(anchor
, a
) {
679 if (anchor
->dnskey
.protocol
!= revoked_dnskey
->dnskey
.protocol
)
682 if (anchor
->dnskey
.algorithm
!= revoked_dnskey
->dnskey
.algorithm
)
685 if (anchor
->dnskey
.key_size
!= revoked_dnskey
->dnskey
.key_size
)
688 /* Note that we allow the REVOKE bit to be
689 * different! It will be set in the revoked
690 * key, but unset in our version of it */
691 if (((anchor
->dnskey
.flags
^ revoked_dnskey
->dnskey
.flags
) | DNSKEY_FLAG_REVOKE
) != DNSKEY_FLAG_REVOKE
)
694 if (memcmp(anchor
->dnskey
.key
, revoked_dnskey
->dnskey
.key
, anchor
->dnskey
.key_size
) != 0)
697 dns_trust_anchor_remove_revoked(d
, anchor
);
702 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
)));
704 DnsResourceRecord
*anchor
;
706 /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
708 DNS_ANSWER_FOREACH(anchor
, a
) {
710 /* We set mask_revoke to true here, since our
711 * DS fingerprint will be the one of the
712 * unrevoked DNSKEY, but the one we got passed
713 * here has the bit set. */
714 r
= dnssec_verify_dnskey_by_ds(revoked_dnskey
, anchor
, true);
720 dns_trust_anchor_remove_revoked(d
, anchor
);
728 int dns_trust_anchor_check_revoked(DnsTrustAnchor
*d
, DnsResourceRecord
*dnskey
, DnsAnswer
*rrs
) {
729 DnsResourceRecord
*rrsig
;
735 /* Looks if "dnskey" is a self-signed RR that has been revoked
736 * and matches one of our trust anchor entries. If so, removes
737 * it from the trust anchor and returns > 0. */
739 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
742 /* Is this DNSKEY revoked? */
743 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_REVOKE
) == 0)
746 /* Could this be interesting to us at all? If not,
747 * there's no point in looking for and verifying a
748 * self-signed RRSIG. */
749 if (!dns_trust_anchor_knows_domain_positive(d
, dns_resource_key_name(dnskey
->key
)))
752 /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
753 DNS_ANSWER_FOREACH(rrsig
, rrs
) {
756 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
759 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
, true);
765 r
= dnssec_verify_rrset(rrs
, dnskey
->key
, rrsig
, dnskey
, USEC_INFINITY
, &result
);
768 if (result
!= DNSSEC_VALIDATED
)
771 /* Bingo! This is a revoked self-signed DNSKEY. Let's
772 * see if this precise one exists in our trust anchor
774 r
= dns_trust_anchor_check_revoked_one(d
, dnskey
);
784 int dns_trust_anchor_is_revoked(DnsTrustAnchor
*d
, DnsResourceRecord
*rr
) {
787 if (!IN_SET(rr
->key
->type
, DNS_TYPE_DS
, DNS_TYPE_DNSKEY
))
790 return set_contains(d
->revoked_by_rr
, rr
);