]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-trust-anchor.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[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 = TAKE_PTR(dd);
305
306 } else if (strcaseeq(type, "DNSKEY")) {
307 _cleanup_free_ char *flags = NULL, *protocol = NULL, *algorithm = NULL, *key = NULL;
308 _cleanup_free_ void *k = NULL;
309 uint16_t f;
310 size_t l;
311 int a;
312
313 r = extract_many_words(&p, NULL, 0, &flags, &protocol, &algorithm, &key, NULL);
314 if (r < 0)
315 return log_warning_errno(r, "Failed to parse DNSKEY parameters on line %s:%u: %m", path, line);
316 if (r != 4) {
317 log_warning("Missing DNSKEY parameters on line %s:%u", path, line);
318 return -EINVAL;
319 }
320
321 if (!streq(protocol, "3")) {
322 log_warning("DNSKEY Protocol is not 3 on line %s:%u", path, line);
323 return -EINVAL;
324 }
325
326 r = safe_atou16(flags, &f);
327 if (r < 0)
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);
331 return -EINVAL;
332 }
333 if ((f & DNSKEY_FLAG_REVOKE)) {
334 log_warning("DNSKEY is already revoked on line %s:%u", path, line);
335 return -EINVAL;
336 }
337
338 a = dnssec_algorithm_from_string(algorithm);
339 if (a < 0) {
340 log_warning("Failed to parse DNSKEY algorithm %s on line %s:%u", algorithm, path, line);
341 return -EINVAL;
342 }
343
344 r = unbase64mem(key, strlen(key), &k, &l);
345 if (r < 0)
346 return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", key, path, line);
347
348 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, domain);
349 if (!rr)
350 return log_oom();
351
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);
357
358 } else {
359 log_warning("RR type %s is not supported, ignoring line %s:%u.", type, path, line);
360 return -EINVAL;
361 }
362
363 if (!isempty(p)) {
364 log_warning("Trailing garbage on line %s:%u, ignoring line.", path, line);
365 return -EINVAL;
366 }
367
368 r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
369 if (r < 0)
370 return log_oom();
371
372 old_answer = hashmap_get(d->positive_by_key, rr->key);
373 answer = dns_answer_ref(old_answer);
374
375 r = dns_answer_add_extend(&answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
376 if (r < 0)
377 return log_error_errno(r, "Failed to add trust anchor RR: %m");
378
379 r = hashmap_replace(d->positive_by_key, rr->key, answer);
380 if (r < 0)
381 return log_error_errno(r, "Failed to add answer to trust anchor: %m");
382
383 old_answer = dns_answer_unref(old_answer);
384 answer = NULL;
385
386 return 0;
387 }
388
389 static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
390 _cleanup_free_ char *domain = NULL;
391 const char *p = s;
392 int r;
393
394 assert(d);
395 assert(line);
396
397 r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
398 if (r < 0)
399 return log_warning_errno(r, "Unable to parse line %s:%u: %m", path, line);
400
401 if (!dns_name_is_valid(domain)) {
402 log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
403 return -EINVAL;
404 }
405
406 if (!isempty(p)) {
407 log_warning("Trailing garbage at line %s:%u, ignoring line.", path, line);
408 return -EINVAL;
409 }
410
411 r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
412 if (r < 0)
413 return log_oom();
414
415 r = set_put(d->negative_by_name, domain);
416 if (r < 0)
417 return log_oom();
418 if (r > 0)
419 domain = NULL;
420
421 return 0;
422 }
423
424 static int dns_trust_anchor_load_files(
425 DnsTrustAnchor *d,
426 const char *suffix,
427 int (*loader)(DnsTrustAnchor *d, const char *path, unsigned n, const char *line)) {
428
429 _cleanup_strv_free_ char **files = NULL;
430 char **f;
431 int r;
432
433 assert(d);
434 assert(suffix);
435 assert(loader);
436
437 r = conf_files_list_nulstr(&files, suffix, NULL, 0, trust_anchor_dirs);
438 if (r < 0)
439 return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix);
440
441 STRV_FOREACH(f, files) {
442 _cleanup_fclose_ FILE *g = NULL;
443 char line[LINE_MAX];
444 unsigned n = 0;
445
446 g = fopen(*f, "r");
447 if (!g) {
448 if (errno == ENOENT)
449 continue;
450
451 log_warning_errno(errno, "Failed to open %s: %m", *f);
452 continue;
453 }
454
455 FOREACH_LINE(line, g, log_warning_errno(errno, "Failed to read %s, ignoring: %m", *f)) {
456 char *l;
457
458 n++;
459
460 l = strstrip(line);
461 if (isempty(l))
462 continue;
463
464 if (*l == ';')
465 continue;
466
467 (void) loader(d, *f, n, l);
468 }
469 }
470
471 return 0;
472 }
473
474 static int domain_name_cmp(const void *a, const void *b) {
475 char **x = (char**) a, **y = (char**) b;
476
477 return dns_name_compare_func(*x, *y);
478 }
479
480 static int dns_trust_anchor_dump(DnsTrustAnchor *d) {
481 DnsAnswer *a;
482 Iterator i;
483
484 assert(d);
485
486 if (hashmap_isempty(d->positive_by_key))
487 log_info("No positive trust anchors defined.");
488 else {
489 log_info("Positive Trust Anchors:");
490 HASHMAP_FOREACH(a, d->positive_by_key, i) {
491 DnsResourceRecord *rr;
492
493 DNS_ANSWER_FOREACH(rr, a)
494 log_info("%s", dns_resource_record_to_string(rr));
495 }
496 }
497
498 if (set_isempty(d->negative_by_name))
499 log_info("No negative trust anchors defined.");
500 else {
501 _cleanup_free_ char **l = NULL, *j = NULL;
502
503 l = set_get_strv(d->negative_by_name);
504 if (!l)
505 return log_oom();
506
507 qsort_safe(l, set_size(d->negative_by_name), sizeof(char*), domain_name_cmp);
508
509 j = strv_join(l, " ");
510 if (!j)
511 return log_oom();
512
513 log_info("Negative trust anchors: %s", j);
514 }
515
516 return 0;
517 }
518
519 int dns_trust_anchor_load(DnsTrustAnchor *d) {
520 int r;
521
522 assert(d);
523
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);
527
528 /* However, if the built-in DS fails, then we have a problem. */
529 r = dns_trust_anchor_add_builtin_positive(d);
530 if (r < 0)
531 return log_error_errno(r, "Failed to add built-in positive trust anchor: %m");
532
533 r = dns_trust_anchor_add_builtin_negative(d);
534 if (r < 0)
535 return log_error_errno(r, "Failed to add built-in negative trust anchor: %m");
536
537 dns_trust_anchor_dump(d);
538
539 return 0;
540 }
541
542 void dns_trust_anchor_flush(DnsTrustAnchor *d) {
543 assert(d);
544
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);
548 }
549
550 int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
551 DnsAnswer *a;
552
553 assert(d);
554 assert(key);
555 assert(ret);
556
557 /* We only serve DS and DNSKEY RRs. */
558 if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
559 return 0;
560
561 a = hashmap_get(d->positive_by_key, key);
562 if (!a)
563 return 0;
564
565 *ret = dns_answer_ref(a);
566 return 1;
567 }
568
569 int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
570 int r;
571
572 assert(d);
573 assert(name);
574
575 for (;;) {
576 /* If the domain is listed as-is in the NTA database, then that counts */
577 if (set_contains(d->negative_by_name, name))
578 return true;
579
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)))
583 return false;
584
585 if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_KEY, name)))
586 return false;
587
588 /* And now, let's look at the parent, and check that too */
589 r = dns_name_parent(&name);
590 if (r < 0)
591 return r;
592 if (r == 0)
593 break;
594 }
595
596 return false;
597 }
598
599 static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {
600 int r;
601
602 assert(d);
603
604 r = set_ensure_allocated(&d->revoked_by_rr, &dns_resource_record_hash_ops);
605 if (r < 0)
606 return r;
607
608 r = set_put(d->revoked_by_rr, rr);
609 if (r < 0)
610 return r;
611 if (r > 0)
612 dns_resource_record_ref(rr);
613
614 return r;
615 }
616
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;
620 int r;
621
622 /* Remember that this is a revoked trust anchor RR */
623 r = dns_trust_anchor_revoked_put(d, rr);
624 if (r < 0)
625 return r;
626
627 /* Remove this from the positive trust anchor */
628 old_answer = hashmap_get(d->positive_by_key, rr->key);
629 if (!old_answer)
630 return 0;
631
632 new_answer = dns_answer_ref(old_answer);
633
634 r = dns_answer_remove_by_rr(&new_answer, rr);
635 if (r <= 0)
636 return r;
637
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),
643 NULL);
644
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);
648 return 1;
649 }
650
651 r = hashmap_replace(d->positive_by_key, new_answer->items[0].rr->key, new_answer);
652 if (r < 0)
653 return r;
654
655 new_answer = NULL;
656 dns_answer_unref(old_answer);
657 return 1;
658 }
659
660 static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) {
661 DnsAnswer *a;
662 int r;
663
664 assert(d);
665 assert(revoked_dnskey);
666 assert(revoked_dnskey->key->type == DNS_TYPE_DNSKEY);
667 assert(revoked_dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE);
668
669 a = hashmap_get(d->positive_by_key, revoked_dnskey->key);
670 if (a) {
671 DnsResourceRecord *anchor;
672
673 /* First, look for the precise DNSKEY in our trust anchor database */
674
675 DNS_ANSWER_FOREACH(anchor, a) {
676
677 if (anchor->dnskey.protocol != revoked_dnskey->dnskey.protocol)
678 continue;
679
680 if (anchor->dnskey.algorithm != revoked_dnskey->dnskey.algorithm)
681 continue;
682
683 if (anchor->dnskey.key_size != revoked_dnskey->dnskey.key_size)
684 continue;
685
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)
690 continue;
691
692 if (memcmp(anchor->dnskey.key, revoked_dnskey->dnskey.key, anchor->dnskey.key_size) != 0)
693 continue;
694
695 dns_trust_anchor_remove_revoked(d, anchor);
696 break;
697 }
698 }
699
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)));
701 if (a) {
702 DnsResourceRecord *anchor;
703
704 /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
705
706 DNS_ANSWER_FOREACH(anchor, a) {
707
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);
713 if (r < 0)
714 return r;
715 if (r == 0)
716 continue;
717
718 dns_trust_anchor_remove_revoked(d, anchor);
719 break;
720 }
721 }
722
723 return 0;
724 }
725
726 int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs) {
727 DnsResourceRecord *rrsig;
728 int r;
729
730 assert(d);
731 assert(dnskey);
732
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. */
736
737 if (dnskey->key->type != DNS_TYPE_DNSKEY)
738 return 0;
739
740 /* Is this DNSKEY revoked? */
741 if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0)
742 return 0;
743
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)))
748 return 0;
749
750 /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
751 DNS_ANSWER_FOREACH(rrsig, rrs) {
752 DnssecResult result;
753
754 if (rrsig->key->type != DNS_TYPE_RRSIG)
755 continue;
756
757 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true);
758 if (r < 0)
759 return r;
760 if (r == 0)
761 continue;
762
763 r = dnssec_verify_rrset(rrs, dnskey->key, rrsig, dnskey, USEC_INFINITY, &result);
764 if (r < 0)
765 return r;
766 if (result != DNSSEC_VALIDATED)
767 continue;
768
769 /* Bingo! This is a revoked self-signed DNSKEY. Let's
770 * see if this precise one exists in our trust anchor
771 * database, too. */
772 r = dns_trust_anchor_check_revoked_one(d, dnskey);
773 if (r < 0)
774 return r;
775
776 return 1;
777 }
778
779 return 0;
780 }
781
782 int dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
783 assert(d);
784
785 if (!IN_SET(rr->key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
786 return 0;
787
788 return set_contains(d->revoked_by_rr, rr);
789 }