]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-trust-anchor.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / resolve / resolved-dns-trust-anchor.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6
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.
11
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.
16
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/>.
19 ***/
20
21 #include "sd-messages.h"
22
23 #include "alloc-util.h"
24 #include "conf-files.h"
25 #include "def.h"
26 #include "dns-domain.h"
27 #include "fd-util.h"
28 #include "fileio.h"
29 #include "hexdecoct.h"
30 #include "parse-util.h"
31 #include "resolved-dns-trust-anchor.h"
32 #include "resolved-dns-dnssec.h"
33 #include "set.h"
34 #include "string-util.h"
35 #include "strv.h"
36
37 static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
38
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 };
43
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 };
48
49 static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char *name) {
50 assert(d);
51
52 /* Returns true if there's an entry for the specified domain
53 * name in our trust anchor */
54
55 return
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));
58 }
59
60 static int add_root_ksk(
61 DnsAnswer *answer,
62 DnsResourceKey *key,
63 uint16_t key_tag,
64 uint8_t algorithm,
65 uint8_t digest_type,
66 const void *digest,
67 size_t digest_size) {
68
69 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
70 int r;
71
72 rr = dns_resource_record_new(key);
73 if (!rr)
74 return -ENOMEM;
75
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);
81 if (!rr->ds.digest)
82 return -ENOMEM;
83
84 r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
85 if (r < 0)
86 return r;
87
88 return 0;
89 }
90
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;
94 int r;
95
96 assert(d);
97
98 r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
99 if (r < 0)
100 return r;
101
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, "."))
105 return 0;
106
107 key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_DS, "");
108 if (!key)
109 return -ENOMEM;
110
111 answer = dns_answer_new(2);
112 if (!answer)
113 return -ENOMEM;
114
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));
117 if (r < 0)
118 return r;
119
120 r = add_root_ksk(answer, key, 20326, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_DIGEST_SHA256, root_digest2, sizeof(root_digest2));
121 if (r < 0)
122 return r;
123
124 r = hashmap_put(d->positive_by_key, key, answer);
125 if (r < 0)
126 return r;
127
128 answer = NULL;
129 return 0;
130 }
131
132 static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
133
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 */
137 "test\0"
138
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 */
142 "10.in-addr.arpa\0"
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"
160
161 /* The same, but for IPv6. */
162 "d.f.ip6.arpa\0"
163
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
168 * suggests so.) */
169 "local\0"
170
171 /* These two are well known, popular private zone
172 * TLDs, that are blocked from delegation, according
173 * to:
174 * http://icannwiki.com/Name_Collision#NGPC_Resolution
175 *
176 * There's also ongoing work on making this official
177 * in an RRC:
178 * https://www.ietf.org/archive/id/draft-chapin-additional-reserved-tlds-02.txt */
179 "home\0"
180 "corp\0"
181
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 */
185 "lan\0"
186 "intranet\0"
187 "internal\0"
188 "private\0";
189
190 const char *name;
191 int r;
192
193 assert(d);
194
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. */
198
199 if (set_size(d->negative_by_name) > 0)
200 return 0;
201
202 r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
203 if (r < 0)
204 return r;
205
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
209 * unsigned. */
210
211 NULSTR_FOREACH(name, private_domains) {
212
213 if (dns_trust_anchor_knows_domain_positive(d, name))
214 continue;
215
216 r = set_put_strdup(d->negative_by_name, name);
217 if (r < 0)
218 return r;
219 }
220
221 return 0;
222 }
223
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;
229 const char *p = s;
230 int r;
231
232 assert(d);
233 assert(line);
234
235 r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
236 if (r < 0)
237 return log_warning_errno(r, "Unable to parse domain in line %s:%u: %m", path, line);
238
239 if (!dns_name_is_valid(domain)) {
240 log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
241 return -EINVAL;
242 }
243
244 r = extract_many_words(&p, NULL, 0, &class, &type, NULL);
245 if (r < 0)
246 return log_warning_errno(r, "Unable to parse class and type in line %s:%u: %m", path, line);
247 if (r != 2) {
248 log_warning("Missing class or type in line %s:%u", path, line);
249 return -EINVAL;
250 }
251
252 if (!strcaseeq(class, "IN")) {
253 log_warning("RR class %s is not supported, ignoring line %s:%u.", class, path, line);
254 return -EINVAL;
255 }
256
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;
260 uint16_t kt;
261 int a, dt;
262 size_t l;
263
264 r = extract_many_words(&p, NULL, 0, &key_tag, &algorithm, &digest_type, &digest, NULL);
265 if (r < 0) {
266 log_warning_errno(r, "Failed to parse DS parameters on line %s:%u: %m", path, line);
267 return -EINVAL;
268 }
269 if (r != 4) {
270 log_warning("Missing DS parameters on line %s:%u", path, line);
271 return -EINVAL;
272 }
273
274 r = safe_atou16(key_tag, &kt);
275 if (r < 0)
276 return log_warning_errno(r, "Failed to parse DS key tag %s on line %s:%u: %m", key_tag, path, line);
277
278 a = dnssec_algorithm_from_string(algorithm);
279 if (a < 0) {
280 log_warning("Failed to parse DS algorithm %s on line %s:%u", algorithm, path, line);
281 return -EINVAL;
282 }
283
284 dt = dnssec_digest_from_string(digest_type);
285 if (dt < 0) {
286 log_warning("Failed to parse DS digest type %s on line %s:%u", digest_type, path, line);
287 return -EINVAL;
288 }
289
290 r = unhexmem(digest, strlen(digest), &dd, &l);
291 if (r < 0) {
292 log_warning("Failed to parse DS digest %s on line %s:%u", digest, path, line);
293 return -EINVAL;
294 }
295
296 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, domain);
297 if (!rr)
298 return log_oom();
299
300 rr->ds.key_tag = kt;
301 rr->ds.algorithm = a;
302 rr->ds.digest_type = dt;
303 rr->ds.digest_size = l;
304 rr->ds.digest = dd;
305 dd = NULL;
306
307 } else if (strcaseeq(type, "DNSKEY")) {
308 _cleanup_free_ char *flags = NULL, *protocol = NULL, *algorithm = NULL, *key = NULL;
309 _cleanup_free_ void *k = NULL;
310 uint16_t f;
311 size_t l;
312 int a;
313
314 r = extract_many_words(&p, NULL, 0, &flags, &protocol, &algorithm, &key, NULL);
315 if (r < 0)
316 return log_warning_errno(r, "Failed to parse DNSKEY parameters on line %s:%u: %m", path, line);
317 if (r != 4) {
318 log_warning("Missing DNSKEY parameters on line %s:%u", path, line);
319 return -EINVAL;
320 }
321
322 if (!streq(protocol, "3")) {
323 log_warning("DNSKEY Protocol is not 3 on line %s:%u", path, line);
324 return -EINVAL;
325 }
326
327 r = safe_atou16(flags, &f);
328 if (r < 0)
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);
332 return -EINVAL;
333 }
334 if ((f & DNSKEY_FLAG_REVOKE)) {
335 log_warning("DNSKEY is already revoked on line %s:%u", path, line);
336 return -EINVAL;
337 }
338
339 a = dnssec_algorithm_from_string(algorithm);
340 if (a < 0) {
341 log_warning("Failed to parse DNSKEY algorithm %s on line %s:%u", algorithm, path, line);
342 return -EINVAL;
343 }
344
345 r = unbase64mem(key, strlen(key), &k, &l);
346 if (r < 0)
347 return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", key, path, line);
348
349 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, domain);
350 if (!rr)
351 return log_oom();
352
353 rr->dnskey.flags = f;
354 rr->dnskey.protocol = 3;
355 rr->dnskey.algorithm = a;
356 rr->dnskey.key_size = l;
357 rr->dnskey.key = k;
358 k = NULL;
359
360 } else {
361 log_warning("RR type %s is not supported, ignoring line %s:%u.", type, path, line);
362 return -EINVAL;
363 }
364
365 if (!isempty(p)) {
366 log_warning("Trailing garbage on line %s:%u, ignoring line.", path, line);
367 return -EINVAL;
368 }
369
370 r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
371 if (r < 0)
372 return log_oom();
373
374 old_answer = hashmap_get(d->positive_by_key, rr->key);
375 answer = dns_answer_ref(old_answer);
376
377 r = dns_answer_add_extend(&answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
378 if (r < 0)
379 return log_error_errno(r, "Failed to add trust anchor RR: %m");
380
381 r = hashmap_replace(d->positive_by_key, rr->key, answer);
382 if (r < 0)
383 return log_error_errno(r, "Failed to add answer to trust anchor: %m");
384
385 old_answer = dns_answer_unref(old_answer);
386 answer = NULL;
387
388 return 0;
389 }
390
391 static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
392 _cleanup_free_ char *domain = NULL;
393 const char *p = s;
394 int r;
395
396 assert(d);
397 assert(line);
398
399 r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
400 if (r < 0)
401 return log_warning_errno(r, "Unable to parse line %s:%u: %m", path, line);
402
403 if (!dns_name_is_valid(domain)) {
404 log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
405 return -EINVAL;
406 }
407
408 if (!isempty(p)) {
409 log_warning("Trailing garbage at line %s:%u, ignoring line.", path, line);
410 return -EINVAL;
411 }
412
413 r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
414 if (r < 0)
415 return log_oom();
416
417 r = set_put(d->negative_by_name, domain);
418 if (r < 0)
419 return log_oom();
420 if (r > 0)
421 domain = NULL;
422
423 return 0;
424 }
425
426 static int dns_trust_anchor_load_files(
427 DnsTrustAnchor *d,
428 const char *suffix,
429 int (*loader)(DnsTrustAnchor *d, const char *path, unsigned n, const char *line)) {
430
431 _cleanup_strv_free_ char **files = NULL;
432 char **f;
433 int r;
434
435 assert(d);
436 assert(suffix);
437 assert(loader);
438
439 r = conf_files_list_nulstr(&files, suffix, NULL, 0, trust_anchor_dirs);
440 if (r < 0)
441 return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix);
442
443 STRV_FOREACH(f, files) {
444 _cleanup_fclose_ FILE *g = NULL;
445 char line[LINE_MAX];
446 unsigned n = 0;
447
448 g = fopen(*f, "r");
449 if (!g) {
450 if (errno == ENOENT)
451 continue;
452
453 log_warning_errno(errno, "Failed to open %s: %m", *f);
454 continue;
455 }
456
457 FOREACH_LINE(line, g, log_warning_errno(errno, "Failed to read %s, ignoring: %m", *f)) {
458 char *l;
459
460 n++;
461
462 l = strstrip(line);
463 if (isempty(l))
464 continue;
465
466 if (*l == ';')
467 continue;
468
469 (void) loader(d, *f, n, l);
470 }
471 }
472
473 return 0;
474 }
475
476 static int domain_name_cmp(const void *a, const void *b) {
477 char **x = (char**) a, **y = (char**) b;
478
479 return dns_name_compare_func(*x, *y);
480 }
481
482 static int dns_trust_anchor_dump(DnsTrustAnchor *d) {
483 DnsAnswer *a;
484 Iterator i;
485
486 assert(d);
487
488 if (hashmap_isempty(d->positive_by_key))
489 log_info("No positive trust anchors defined.");
490 else {
491 log_info("Positive Trust Anchors:");
492 HASHMAP_FOREACH(a, d->positive_by_key, i) {
493 DnsResourceRecord *rr;
494
495 DNS_ANSWER_FOREACH(rr, a)
496 log_info("%s", dns_resource_record_to_string(rr));
497 }
498 }
499
500 if (set_isempty(d->negative_by_name))
501 log_info("No negative trust anchors defined.");
502 else {
503 _cleanup_free_ char **l = NULL, *j = NULL;
504
505 l = set_get_strv(d->negative_by_name);
506 if (!l)
507 return log_oom();
508
509 qsort_safe(l, set_size(d->negative_by_name), sizeof(char*), domain_name_cmp);
510
511 j = strv_join(l, " ");
512 if (!j)
513 return log_oom();
514
515 log_info("Negative trust anchors: %s", j);
516 }
517
518 return 0;
519 }
520
521 int dns_trust_anchor_load(DnsTrustAnchor *d) {
522 int r;
523
524 assert(d);
525
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);
529
530 /* However, if the built-in DS fails, then we have a problem. */
531 r = dns_trust_anchor_add_builtin_positive(d);
532 if (r < 0)
533 return log_error_errno(r, "Failed to add built-in positive trust anchor: %m");
534
535 r = dns_trust_anchor_add_builtin_negative(d);
536 if (r < 0)
537 return log_error_errno(r, "Failed to add built-in negative trust anchor: %m");
538
539 dns_trust_anchor_dump(d);
540
541 return 0;
542 }
543
544 void dns_trust_anchor_flush(DnsTrustAnchor *d) {
545 DnsAnswer *a;
546 DnsResourceRecord *rr;
547
548 assert(d);
549
550 while ((a = hashmap_steal_first(d->positive_by_key)))
551 dns_answer_unref(a);
552 d->positive_by_key = hashmap_free(d->positive_by_key);
553
554 while ((rr = set_steal_first(d->revoked_by_rr)))
555 dns_resource_record_unref(rr);
556 d->revoked_by_rr = set_free(d->revoked_by_rr);
557
558 d->negative_by_name = set_free_free(d->negative_by_name);
559 }
560
561 int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
562 DnsAnswer *a;
563
564 assert(d);
565 assert(key);
566 assert(ret);
567
568 /* We only serve DS and DNSKEY RRs. */
569 if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
570 return 0;
571
572 a = hashmap_get(d->positive_by_key, key);
573 if (!a)
574 return 0;
575
576 *ret = dns_answer_ref(a);
577 return 1;
578 }
579
580 int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
581 int r;
582
583 assert(d);
584 assert(name);
585
586 for (;;) {
587 /* If the domain is listed as-is in the NTA database, then that counts */
588 if (set_contains(d->negative_by_name, name))
589 return true;
590
591 /* If the domain isn't listed as NTA, but is listed as positive trust anchor, then that counts. See RFC
592 * 7646, section 1.1 */
593 if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name)))
594 return false;
595
596 if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_KEY, name)))
597 return false;
598
599 /* And now, let's look at the parent, and check that too */
600 r = dns_name_parent(&name);
601 if (r < 0)
602 return r;
603 if (r == 0)
604 break;
605 }
606
607 return false;
608 }
609
610 static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {
611 int r;
612
613 assert(d);
614
615 r = set_ensure_allocated(&d->revoked_by_rr, &dns_resource_record_hash_ops);
616 if (r < 0)
617 return r;
618
619 r = set_put(d->revoked_by_rr, rr);
620 if (r < 0)
621 return r;
622 if (r > 0)
623 dns_resource_record_ref(rr);
624
625 return r;
626 }
627
628 static int dns_trust_anchor_remove_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
629 _cleanup_(dns_answer_unrefp) DnsAnswer *new_answer = NULL;
630 DnsAnswer *old_answer;
631 int r;
632
633 /* Remember that this is a revoked trust anchor RR */
634 r = dns_trust_anchor_revoked_put(d, rr);
635 if (r < 0)
636 return r;
637
638 /* Remove this from the positive trust anchor */
639 old_answer = hashmap_get(d->positive_by_key, rr->key);
640 if (!old_answer)
641 return 0;
642
643 new_answer = dns_answer_ref(old_answer);
644
645 r = dns_answer_remove_by_rr(&new_answer, rr);
646 if (r <= 0)
647 return r;
648
649 /* We found the key! Warn the user */
650 log_struct(LOG_WARNING,
651 "MESSAGE_ID=" SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR,
652 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)),
653 "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr),
654 NULL);
655
656 if (dns_answer_size(new_answer) <= 0) {
657 assert_se(hashmap_remove(d->positive_by_key, rr->key) == old_answer);
658 dns_answer_unref(old_answer);
659 return 1;
660 }
661
662 r = hashmap_replace(d->positive_by_key, new_answer->items[0].rr->key, new_answer);
663 if (r < 0)
664 return r;
665
666 new_answer = NULL;
667 dns_answer_unref(old_answer);
668 return 1;
669 }
670
671 static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) {
672 DnsAnswer *a;
673 int r;
674
675 assert(d);
676 assert(revoked_dnskey);
677 assert(revoked_dnskey->key->type == DNS_TYPE_DNSKEY);
678 assert(revoked_dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE);
679
680 a = hashmap_get(d->positive_by_key, revoked_dnskey->key);
681 if (a) {
682 DnsResourceRecord *anchor;
683
684 /* First, look for the precise DNSKEY in our trust anchor database */
685
686 DNS_ANSWER_FOREACH(anchor, a) {
687
688 if (anchor->dnskey.protocol != revoked_dnskey->dnskey.protocol)
689 continue;
690
691 if (anchor->dnskey.algorithm != revoked_dnskey->dnskey.algorithm)
692 continue;
693
694 if (anchor->dnskey.key_size != revoked_dnskey->dnskey.key_size)
695 continue;
696
697 /* Note that we allow the REVOKE bit to be
698 * different! It will be set in the revoked
699 * key, but unset in our version of it */
700 if (((anchor->dnskey.flags ^ revoked_dnskey->dnskey.flags) | DNSKEY_FLAG_REVOKE) != DNSKEY_FLAG_REVOKE)
701 continue;
702
703 if (memcmp(anchor->dnskey.key, revoked_dnskey->dnskey.key, anchor->dnskey.key_size) != 0)
704 continue;
705
706 dns_trust_anchor_remove_revoked(d, anchor);
707 break;
708 }
709 }
710
711 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)));
712 if (a) {
713 DnsResourceRecord *anchor;
714
715 /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
716
717 DNS_ANSWER_FOREACH(anchor, a) {
718
719 /* We set mask_revoke to true here, since our
720 * DS fingerprint will be the one of the
721 * unrevoked DNSKEY, but the one we got passed
722 * here has the bit set. */
723 r = dnssec_verify_dnskey_by_ds(revoked_dnskey, anchor, true);
724 if (r < 0)
725 return r;
726 if (r == 0)
727 continue;
728
729 dns_trust_anchor_remove_revoked(d, anchor);
730 break;
731 }
732 }
733
734 return 0;
735 }
736
737 int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs) {
738 DnsResourceRecord *rrsig;
739 int r;
740
741 assert(d);
742 assert(dnskey);
743
744 /* Looks if "dnskey" is a self-signed RR that has been revoked
745 * and matches one of our trust anchor entries. If so, removes
746 * it from the trust anchor and returns > 0. */
747
748 if (dnskey->key->type != DNS_TYPE_DNSKEY)
749 return 0;
750
751 /* Is this DNSKEY revoked? */
752 if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0)
753 return 0;
754
755 /* Could this be interesting to us at all? If not,
756 * there's no point in looking for and verifying a
757 * self-signed RRSIG. */
758 if (!dns_trust_anchor_knows_domain_positive(d, dns_resource_key_name(dnskey->key)))
759 return 0;
760
761 /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
762 DNS_ANSWER_FOREACH(rrsig, rrs) {
763 DnssecResult result;
764
765 if (rrsig->key->type != DNS_TYPE_RRSIG)
766 continue;
767
768 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true);
769 if (r < 0)
770 return r;
771 if (r == 0)
772 continue;
773
774 r = dnssec_verify_rrset(rrs, dnskey->key, rrsig, dnskey, USEC_INFINITY, &result);
775 if (r < 0)
776 return r;
777 if (result != DNSSEC_VALIDATED)
778 continue;
779
780 /* Bingo! This is a revoked self-signed DNSKEY. Let's
781 * see if this precise one exists in our trust anchor
782 * database, too. */
783 r = dns_trust_anchor_check_revoked_one(d, dnskey);
784 if (r < 0)
785 return r;
786
787 return 1;
788 }
789
790 return 0;
791 }
792
793 int dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
794 assert(d);
795
796 if (!IN_SET(rr->key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
797 return 0;
798
799 return set_contains(d->revoked_by_rr, rr);
800 }