]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-trust-anchor.c
Merge pull request #7492 from keszybz/coverity-fixes
[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 assert(d);
546
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);
550 }
551
552 int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
553 DnsAnswer *a;
554
555 assert(d);
556 assert(key);
557 assert(ret);
558
559 /* We only serve DS and DNSKEY RRs. */
560 if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
561 return 0;
562
563 a = hashmap_get(d->positive_by_key, key);
564 if (!a)
565 return 0;
566
567 *ret = dns_answer_ref(a);
568 return 1;
569 }
570
571 int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
572 int r;
573
574 assert(d);
575 assert(name);
576
577 for (;;) {
578 /* If the domain is listed as-is in the NTA database, then that counts */
579 if (set_contains(d->negative_by_name, name))
580 return true;
581
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)))
585 return false;
586
587 if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_KEY, name)))
588 return false;
589
590 /* And now, let's look at the parent, and check that too */
591 r = dns_name_parent(&name);
592 if (r < 0)
593 return r;
594 if (r == 0)
595 break;
596 }
597
598 return false;
599 }
600
601 static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {
602 int r;
603
604 assert(d);
605
606 r = set_ensure_allocated(&d->revoked_by_rr, &dns_resource_record_hash_ops);
607 if (r < 0)
608 return r;
609
610 r = set_put(d->revoked_by_rr, rr);
611 if (r < 0)
612 return r;
613 if (r > 0)
614 dns_resource_record_ref(rr);
615
616 return r;
617 }
618
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;
622 int r;
623
624 /* Remember that this is a revoked trust anchor RR */
625 r = dns_trust_anchor_revoked_put(d, rr);
626 if (r < 0)
627 return r;
628
629 /* Remove this from the positive trust anchor */
630 old_answer = hashmap_get(d->positive_by_key, rr->key);
631 if (!old_answer)
632 return 0;
633
634 new_answer = dns_answer_ref(old_answer);
635
636 r = dns_answer_remove_by_rr(&new_answer, rr);
637 if (r <= 0)
638 return r;
639
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),
645 NULL);
646
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);
650 return 1;
651 }
652
653 r = hashmap_replace(d->positive_by_key, new_answer->items[0].rr->key, new_answer);
654 if (r < 0)
655 return r;
656
657 new_answer = NULL;
658 dns_answer_unref(old_answer);
659 return 1;
660 }
661
662 static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) {
663 DnsAnswer *a;
664 int r;
665
666 assert(d);
667 assert(revoked_dnskey);
668 assert(revoked_dnskey->key->type == DNS_TYPE_DNSKEY);
669 assert(revoked_dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE);
670
671 a = hashmap_get(d->positive_by_key, revoked_dnskey->key);
672 if (a) {
673 DnsResourceRecord *anchor;
674
675 /* First, look for the precise DNSKEY in our trust anchor database */
676
677 DNS_ANSWER_FOREACH(anchor, a) {
678
679 if (anchor->dnskey.protocol != revoked_dnskey->dnskey.protocol)
680 continue;
681
682 if (anchor->dnskey.algorithm != revoked_dnskey->dnskey.algorithm)
683 continue;
684
685 if (anchor->dnskey.key_size != revoked_dnskey->dnskey.key_size)
686 continue;
687
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)
692 continue;
693
694 if (memcmp(anchor->dnskey.key, revoked_dnskey->dnskey.key, anchor->dnskey.key_size) != 0)
695 continue;
696
697 dns_trust_anchor_remove_revoked(d, anchor);
698 break;
699 }
700 }
701
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)));
703 if (a) {
704 DnsResourceRecord *anchor;
705
706 /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
707
708 DNS_ANSWER_FOREACH(anchor, a) {
709
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);
715 if (r < 0)
716 return r;
717 if (r == 0)
718 continue;
719
720 dns_trust_anchor_remove_revoked(d, anchor);
721 break;
722 }
723 }
724
725 return 0;
726 }
727
728 int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs) {
729 DnsResourceRecord *rrsig;
730 int r;
731
732 assert(d);
733 assert(dnskey);
734
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. */
738
739 if (dnskey->key->type != DNS_TYPE_DNSKEY)
740 return 0;
741
742 /* Is this DNSKEY revoked? */
743 if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0)
744 return 0;
745
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)))
750 return 0;
751
752 /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
753 DNS_ANSWER_FOREACH(rrsig, rrs) {
754 DnssecResult result;
755
756 if (rrsig->key->type != DNS_TYPE_RRSIG)
757 continue;
758
759 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true);
760 if (r < 0)
761 return r;
762 if (r == 0)
763 continue;
764
765 r = dnssec_verify_rrset(rrs, dnskey->key, rrsig, dnskey, USEC_INFINITY, &result);
766 if (r < 0)
767 return r;
768 if (result != DNSSEC_VALIDATED)
769 continue;
770
771 /* Bingo! This is a revoked self-signed DNSKEY. Let's
772 * see if this precise one exists in our trust anchor
773 * database, too. */
774 r = dns_trust_anchor_check_revoked_one(d, dnskey);
775 if (r < 0)
776 return r;
777
778 return 1;
779 }
780
781 return 0;
782 }
783
784 int dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
785 assert(d);
786
787 if (!IN_SET(rr->key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
788 return 0;
789
790 return set_contains(d->revoked_by_rr, rr);
791 }