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
;
304 rr
->ds
.digest
= TAKE_PTR(dd
);
306 } else if (strcaseeq(type
, "DNSKEY")) {
307 _cleanup_free_
char *flags
= NULL
, *protocol
= NULL
, *algorithm
= NULL
, *key
= NULL
;
308 _cleanup_free_
void *k
= NULL
;
313 r
= extract_many_words(&p
, NULL
, 0, &flags
, &protocol
, &algorithm
, &key
, NULL
);
315 return log_warning_errno(r
, "Failed to parse DNSKEY parameters on line %s:%u: %m", path
, line
);
317 log_warning("Missing DNSKEY parameters on line %s:%u", path
, line
);
321 if (!streq(protocol
, "3")) {
322 log_warning("DNSKEY Protocol is not 3 on line %s:%u", path
, line
);
326 r
= safe_atou16(flags
, &f
);
328 return log_warning_errno(r
, "Failed to parse DNSKEY flags field %s on line %s:%u", flags
, path
, line
);
329 if ((f
& DNSKEY_FLAG_ZONE_KEY
) == 0) {
330 log_warning("DNSKEY lacks zone key bit set on line %s:%u", path
, line
);
333 if ((f
& DNSKEY_FLAG_REVOKE
)) {
334 log_warning("DNSKEY is already revoked on line %s:%u", path
, line
);
338 a
= dnssec_algorithm_from_string(algorithm
);
340 log_warning("Failed to parse DNSKEY algorithm %s on line %s:%u", algorithm
, path
, line
);
344 r
= unbase64mem(key
, strlen(key
), &k
, &l
);
346 return log_warning_errno(r
, "Failed to parse DNSKEY key data %s on line %s:%u", key
, path
, line
);
348 rr
= dns_resource_record_new_full(DNS_CLASS_IN
, DNS_TYPE_DNSKEY
, domain
);
352 rr
->dnskey
.flags
= f
;
353 rr
->dnskey
.protocol
= 3;
354 rr
->dnskey
.algorithm
= a
;
355 rr
->dnskey
.key_size
= l
;
356 rr
->dnskey
.key
= TAKE_PTR(k
);
359 log_warning("RR type %s is not supported, ignoring line %s:%u.", type
, path
, line
);
364 log_warning("Trailing garbage on line %s:%u, ignoring line.", path
, line
);
368 r
= hashmap_ensure_allocated(&d
->positive_by_key
, &dns_resource_key_hash_ops
);
372 old_answer
= hashmap_get(d
->positive_by_key
, rr
->key
);
373 answer
= dns_answer_ref(old_answer
);
375 r
= dns_answer_add_extend(&answer
, rr
, 0, DNS_ANSWER_AUTHENTICATED
);
377 return log_error_errno(r
, "Failed to add trust anchor RR: %m");
379 r
= hashmap_replace(d
->positive_by_key
, rr
->key
, answer
);
381 return log_error_errno(r
, "Failed to add answer to trust anchor: %m");
383 old_answer
= dns_answer_unref(old_answer
);
389 static int dns_trust_anchor_load_negative(DnsTrustAnchor
*d
, const char *path
, unsigned line
, const char *s
) {
390 _cleanup_free_
char *domain
= NULL
;
397 r
= extract_first_word(&p
, &domain
, NULL
, EXTRACT_QUOTES
);
399 return log_warning_errno(r
, "Unable to parse line %s:%u: %m", path
, line
);
401 if (!dns_name_is_valid(domain
)) {
402 log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain
, path
, line
);
407 log_warning("Trailing garbage at line %s:%u, ignoring line.", path
, line
);
411 r
= set_ensure_allocated(&d
->negative_by_name
, &dns_name_hash_ops
);
415 r
= set_put(d
->negative_by_name
, domain
);
424 static int dns_trust_anchor_load_files(
427 int (*loader
)(DnsTrustAnchor
*d
, const char *path
, unsigned n
, const char *line
)) {
429 _cleanup_strv_free_
char **files
= NULL
;
437 r
= conf_files_list_nulstr(&files
, suffix
, NULL
, 0, trust_anchor_dirs
);
439 return log_error_errno(r
, "Failed to enumerate %s trust anchor files: %m", suffix
);
441 STRV_FOREACH(f
, files
) {
442 _cleanup_fclose_
FILE *g
= NULL
;
451 log_warning_errno(errno
, "Failed to open %s: %m", *f
);
455 FOREACH_LINE(line
, g
, log_warning_errno(errno
, "Failed to read %s, ignoring: %m", *f
)) {
467 (void) loader(d
, *f
, n
, l
);
474 static int domain_name_cmp(const void *a
, const void *b
) {
475 char **x
= (char**) a
, **y
= (char**) b
;
477 return dns_name_compare_func(*x
, *y
);
480 static int dns_trust_anchor_dump(DnsTrustAnchor
*d
) {
486 if (hashmap_isempty(d
->positive_by_key
))
487 log_info("No positive trust anchors defined.");
489 log_info("Positive Trust Anchors:");
490 HASHMAP_FOREACH(a
, d
->positive_by_key
, i
) {
491 DnsResourceRecord
*rr
;
493 DNS_ANSWER_FOREACH(rr
, a
)
494 log_info("%s", dns_resource_record_to_string(rr
));
498 if (set_isempty(d
->negative_by_name
))
499 log_info("No negative trust anchors defined.");
501 _cleanup_free_
char **l
= NULL
, *j
= NULL
;
503 l
= set_get_strv(d
->negative_by_name
);
507 qsort_safe(l
, set_size(d
->negative_by_name
), sizeof(char*), domain_name_cmp
);
509 j
= strv_join(l
, " ");
513 log_info("Negative trust anchors: %s", j
);
519 int dns_trust_anchor_load(DnsTrustAnchor
*d
) {
524 /* If loading things from disk fails, we don't consider this fatal */
525 (void) dns_trust_anchor_load_files(d
, ".positive", dns_trust_anchor_load_positive
);
526 (void) dns_trust_anchor_load_files(d
, ".negative", dns_trust_anchor_load_negative
);
528 /* However, if the built-in DS fails, then we have a problem. */
529 r
= dns_trust_anchor_add_builtin_positive(d
);
531 return log_error_errno(r
, "Failed to add built-in positive trust anchor: %m");
533 r
= dns_trust_anchor_add_builtin_negative(d
);
535 return log_error_errno(r
, "Failed to add built-in negative trust anchor: %m");
537 dns_trust_anchor_dump(d
);
542 void dns_trust_anchor_flush(DnsTrustAnchor
*d
) {
545 d
->positive_by_key
= hashmap_free_with_destructor(d
->positive_by_key
, dns_answer_unref
);
546 d
->revoked_by_rr
= set_free_with_destructor(d
->revoked_by_rr
, dns_resource_record_unref
);
547 d
->negative_by_name
= set_free_free(d
->negative_by_name
);
550 int dns_trust_anchor_lookup_positive(DnsTrustAnchor
*d
, const DnsResourceKey
*key
, DnsAnswer
**ret
) {
557 /* We only serve DS and DNSKEY RRs. */
558 if (!IN_SET(key
->type
, DNS_TYPE_DS
, DNS_TYPE_DNSKEY
))
561 a
= hashmap_get(d
->positive_by_key
, key
);
565 *ret
= dns_answer_ref(a
);
569 int dns_trust_anchor_lookup_negative(DnsTrustAnchor
*d
, const char *name
) {
576 /* If the domain is listed as-is in the NTA database, then that counts */
577 if (set_contains(d
->negative_by_name
, name
))
580 /* If the domain isn't listed as NTA, but is listed as positive trust anchor, then that counts. See RFC
581 * 7646, section 1.1 */
582 if (hashmap_contains(d
->positive_by_key
, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN
, DNS_TYPE_DS
, name
)))
585 if (hashmap_contains(d
->positive_by_key
, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN
, DNS_TYPE_KEY
, name
)))
588 /* And now, let's look at the parent, and check that too */
589 r
= dns_name_parent(&name
);
599 static int dns_trust_anchor_revoked_put(DnsTrustAnchor
*d
, DnsResourceRecord
*rr
) {
604 r
= set_ensure_allocated(&d
->revoked_by_rr
, &dns_resource_record_hash_ops
);
608 r
= set_put(d
->revoked_by_rr
, rr
);
612 dns_resource_record_ref(rr
);
617 static int dns_trust_anchor_remove_revoked(DnsTrustAnchor
*d
, DnsResourceRecord
*rr
) {
618 _cleanup_(dns_answer_unrefp
) DnsAnswer
*new_answer
= NULL
;
619 DnsAnswer
*old_answer
;
622 /* Remember that this is a revoked trust anchor RR */
623 r
= dns_trust_anchor_revoked_put(d
, rr
);
627 /* Remove this from the positive trust anchor */
628 old_answer
= hashmap_get(d
->positive_by_key
, rr
->key
);
632 new_answer
= dns_answer_ref(old_answer
);
634 r
= dns_answer_remove_by_rr(&new_answer
, rr
);
638 /* We found the key! Warn the user */
639 log_struct(LOG_WARNING
,
640 "MESSAGE_ID=" SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR
,
641 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
)),
642 "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr
),
645 if (dns_answer_size(new_answer
) <= 0) {
646 assert_se(hashmap_remove(d
->positive_by_key
, rr
->key
) == old_answer
);
647 dns_answer_unref(old_answer
);
651 r
= hashmap_replace(d
->positive_by_key
, new_answer
->items
[0].rr
->key
, new_answer
);
656 dns_answer_unref(old_answer
);
660 static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor
*d
, DnsResourceRecord
*revoked_dnskey
) {
665 assert(revoked_dnskey
);
666 assert(revoked_dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
667 assert(revoked_dnskey
->dnskey
.flags
& DNSKEY_FLAG_REVOKE
);
669 a
= hashmap_get(d
->positive_by_key
, revoked_dnskey
->key
);
671 DnsResourceRecord
*anchor
;
673 /* First, look for the precise DNSKEY in our trust anchor database */
675 DNS_ANSWER_FOREACH(anchor
, a
) {
677 if (anchor
->dnskey
.protocol
!= revoked_dnskey
->dnskey
.protocol
)
680 if (anchor
->dnskey
.algorithm
!= revoked_dnskey
->dnskey
.algorithm
)
683 if (anchor
->dnskey
.key_size
!= revoked_dnskey
->dnskey
.key_size
)
686 /* Note that we allow the REVOKE bit to be
687 * different! It will be set in the revoked
688 * key, but unset in our version of it */
689 if (((anchor
->dnskey
.flags
^ revoked_dnskey
->dnskey
.flags
) | DNSKEY_FLAG_REVOKE
) != DNSKEY_FLAG_REVOKE
)
692 if (memcmp(anchor
->dnskey
.key
, revoked_dnskey
->dnskey
.key
, anchor
->dnskey
.key_size
) != 0)
695 dns_trust_anchor_remove_revoked(d
, anchor
);
700 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
)));
702 DnsResourceRecord
*anchor
;
704 /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
706 DNS_ANSWER_FOREACH(anchor
, a
) {
708 /* We set mask_revoke to true here, since our
709 * DS fingerprint will be the one of the
710 * unrevoked DNSKEY, but the one we got passed
711 * here has the bit set. */
712 r
= dnssec_verify_dnskey_by_ds(revoked_dnskey
, anchor
, true);
718 dns_trust_anchor_remove_revoked(d
, anchor
);
726 int dns_trust_anchor_check_revoked(DnsTrustAnchor
*d
, DnsResourceRecord
*dnskey
, DnsAnswer
*rrs
) {
727 DnsResourceRecord
*rrsig
;
733 /* Looks if "dnskey" is a self-signed RR that has been revoked
734 * and matches one of our trust anchor entries. If so, removes
735 * it from the trust anchor and returns > 0. */
737 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
740 /* Is this DNSKEY revoked? */
741 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_REVOKE
) == 0)
744 /* Could this be interesting to us at all? If not,
745 * there's no point in looking for and verifying a
746 * self-signed RRSIG. */
747 if (!dns_trust_anchor_knows_domain_positive(d
, dns_resource_key_name(dnskey
->key
)))
750 /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
751 DNS_ANSWER_FOREACH(rrsig
, rrs
) {
754 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
757 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
, true);
763 r
= dnssec_verify_rrset(rrs
, dnskey
->key
, rrsig
, dnskey
, USEC_INFINITY
, &result
);
766 if (result
!= DNSSEC_VALIDATED
)
769 /* Bingo! This is a revoked self-signed DNSKEY. Let's
770 * see if this precise one exists in our trust anchor
772 r
= dns_trust_anchor_check_revoked_one(d
, dnskey
);
782 int dns_trust_anchor_is_revoked(DnsTrustAnchor
*d
, DnsResourceRecord
*rr
) {
785 if (!IN_SET(rr
->key
->type
, DNS_TYPE_DS
, DNS_TYPE_DNSKEY
))
788 return set_contains(d
->revoked_by_rr
, rr
);