]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-trust-anchor.c
Merge pull request #2222 from snakeroot/eventsplat
[thirdparty/systemd.git] / src / resolve / resolved-dns-trust-anchor.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sd-messages.h>
23
24 #include "alloc-util.h"
25 #include "conf-files.h"
26 #include "def.h"
27 #include "dns-domain.h"
28 #include "fd-util.h"
29 #include "fileio.h"
30 #include "hexdecoct.h"
31 #include "parse-util.h"
32 #include "resolved-dns-trust-anchor.h"
33 #include "resolved-dns-dnssec.h"
34 #include "set.h"
35 #include "string-util.h"
36 #include "strv.h"
37
38 static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
39
40 /* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
41 static const uint8_t root_digest[] =
42 { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60,
43 0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 };
44
45 static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char *name) {
46 assert(d);
47
48 /* Returns true if there's an entry for the specified domain
49 * name in our trust anchor */
50
51 return
52 hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DNSKEY, name)) ||
53 hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name));
54 }
55
56 static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) {
57 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
58 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
59 int r;
60
61 assert(d);
62
63 r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
64 if (r < 0)
65 return r;
66
67 /* Only add the built-in trust anchor if there's neither a DS
68 * nor a DNSKEY defined for the root domain. That way users
69 * have an easy way to override the root domain DS/DNSKEY
70 * data. */
71 if (dns_trust_anchor_knows_domain_positive(d, "."))
72 return 0;
73
74 /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */
75 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "");
76 if (!rr)
77 return -ENOMEM;
78
79 rr->ds.key_tag = 19036;
80 rr->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256;
81 rr->ds.digest_type = DNSSEC_DIGEST_SHA256;
82 rr->ds.digest_size = sizeof(root_digest);
83 rr->ds.digest = memdup(root_digest, rr->ds.digest_size);
84 if (!rr->ds.digest)
85 return -ENOMEM;
86
87 answer = dns_answer_new(1);
88 if (!answer)
89 return -ENOMEM;
90
91 r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
92 if (r < 0)
93 return r;
94
95 r = hashmap_put(d->positive_by_key, rr->key, answer);
96 if (r < 0)
97 return r;
98
99 answer = NULL;
100 return 0;
101 }
102
103 static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
104
105 static const char private_domains[] =
106 /* RFC 6761 says that .test is a special domain for
107 * testing and not to be installed in the root zone */
108 "test\0"
109
110 /* RFC 6761 says that these reverse IP lookup ranges
111 * are for private addresses, and hence should not
112 * show up in the root zone */
113 "10.in-addr.arpa\0"
114 "16.172.in-addr.arpa\0"
115 "17.172.in-addr.arpa\0"
116 "18.172.in-addr.arpa\0"
117 "19.172.in-addr.arpa\0"
118 "20.172.in-addr.arpa\0"
119 "21.172.in-addr.arpa\0"
120 "22.172.in-addr.arpa\0"
121 "23.172.in-addr.arpa\0"
122 "24.172.in-addr.arpa\0"
123 "25.172.in-addr.arpa\0"
124 "26.172.in-addr.arpa\0"
125 "27.172.in-addr.arpa\0"
126 "28.172.in-addr.arpa\0"
127 "29.172.in-addr.arpa\0"
128 "30.172.in-addr.arpa\0"
129 "31.172.in-addr.arpa\0"
130 "168.192.in-addr.arpa\0"
131
132 /* RFC 6762 reserves the .local domain for Multicast
133 * DNS, it hence cannot appear in the root zone. (Note
134 * that we by default do not route .local traffic to
135 * DNS anyway, except when a configured search domain
136 * suggests so.) */
137 "local\0"
138
139 /* These two are well known, popular private zone
140 * TLDs, that are blocked from delegation, according
141 * to:
142 * http://icannwiki.com/Name_Collision#NGPC_Resolution
143 *
144 * There's also ongoing work on making this official
145 * in an RRC:
146 * https://www.ietf.org/archive/id/draft-chapin-additional-reserved-tlds-02.txt */
147 "home\0"
148 "corp\0"
149
150 /* The following four TLDs are suggested for private
151 * zones in RFC 6762, Appendix G, and are hence very
152 * unlikely to be made official TLDs any day soon */
153 "lan\0"
154 "intranet\0"
155 "internal\0"
156 "private\0";
157
158 const char *name;
159 int r;
160
161 assert(d);
162
163 /* Only add the built-in trust anchor if there's no negative
164 * trust anchor defined at all. This enables easy overriding
165 * of negative trust anchors. */
166
167 if (set_size(d->negative_by_name) > 0)
168 return 0;
169
170 r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
171 if (r < 0)
172 return r;
173
174 /* We add a couple of domains as default negative trust
175 * anchors, where it's very unlikely they will be installed in
176 * the root zone. If they exist they must be private, and thus
177 * unsigned. */
178
179 NULSTR_FOREACH(name, private_domains) {
180
181 if (dns_trust_anchor_knows_domain_positive(d, name))
182 continue;
183
184 r = set_put_strdup(d->negative_by_name, name);
185 if (r < 0)
186 return r;
187 }
188
189 return 0;
190 }
191
192 static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
193 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
194 _cleanup_free_ char *domain = NULL, *class = NULL, *type = NULL;
195 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
196 DnsAnswer *old_answer = NULL;
197 const char *p = s;
198 int r;
199
200 assert(d);
201 assert(line);
202
203 r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
204 if (r < 0)
205 return log_warning_errno(r, "Unable to parse domain in line %s:%u: %m", path, line);
206
207 if (!dns_name_is_valid(domain)) {
208 log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
209 return -EINVAL;
210 }
211
212 r = extract_many_words(&p, NULL, 0, &class, &type, NULL);
213 if (r < 0)
214 return log_warning_errno(r, "Unable to parse class and type in line %s:%u: %m", path, line);
215 if (r != 2) {
216 log_warning("Missing class or type in line %s:%u", path, line);
217 return -EINVAL;
218 }
219
220 if (!strcaseeq(class, "IN")) {
221 log_warning("RR class %s is not supported, ignoring line %s:%u.", class, path, line);
222 return -EINVAL;
223 }
224
225 if (strcaseeq(type, "DS")) {
226 _cleanup_free_ char *key_tag = NULL, *algorithm = NULL, *digest_type = NULL, *digest = NULL;
227 _cleanup_free_ void *dd = NULL;
228 uint16_t kt;
229 int a, dt;
230 size_t l;
231
232 r = extract_many_words(&p, NULL, 0, &key_tag, &algorithm, &digest_type, &digest, NULL);
233 if (r < 0) {
234 log_warning_errno(r, "Failed to parse DS parameters on line %s:%u: %m", path, line);
235 return -EINVAL;
236 }
237 if (r != 4) {
238 log_warning("Missing DS parameters on line %s:%u", path, line);
239 return -EINVAL;
240 }
241
242 r = safe_atou16(key_tag, &kt);
243 if (r < 0)
244 return log_warning_errno(r, "Failed to parse DS key tag %s on line %s:%u: %m", key_tag, path, line);
245
246 a = dnssec_algorithm_from_string(algorithm);
247 if (a < 0) {
248 log_warning("Failed to parse DS algorithm %s on line %s:%u", algorithm, path, line);
249 return -EINVAL;
250 }
251
252 dt = dnssec_digest_from_string(digest_type);
253 if (dt < 0) {
254 log_warning("Failed to parse DS digest type %s on line %s:%u", digest_type, path, line);
255 return -EINVAL;
256 }
257
258 r = unhexmem(digest, strlen(digest), &dd, &l);
259 if (r < 0) {
260 log_warning("Failed to parse DS digest %s on line %s:%u", digest, path, line);
261 return -EINVAL;
262 }
263
264 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, domain);
265 if (!rr)
266 return log_oom();
267
268 rr->ds.key_tag = kt;
269 rr->ds.algorithm = a;
270 rr->ds.digest_type = dt;
271 rr->ds.digest_size = l;
272 rr->ds.digest = dd;
273 dd = NULL;
274
275 } else if (strcaseeq(type, "DNSKEY")) {
276 _cleanup_free_ char *flags = NULL, *protocol = NULL, *algorithm = NULL, *key = NULL;
277 _cleanup_free_ void *k = NULL;
278 uint16_t f;
279 size_t l;
280 int a;
281
282 r = extract_many_words(&p, NULL, 0, &flags, &protocol, &algorithm, &key, NULL);
283 if (r < 0)
284 return log_warning_errno(r, "Failed to parse DNSKEY parameters on line %s:%u: %m", path, line);
285 if (r != 4) {
286 log_warning("Missing DNSKEY parameters on line %s:%u", path, line);
287 return -EINVAL;
288 }
289
290 if (!streq(protocol, "3")) {
291 log_warning("DNSKEY Protocol is not 3 on line %s:%u", path, line);
292 return -EINVAL;
293 }
294
295 r = safe_atou16(flags, &f);
296 if (r < 0)
297 return log_warning_errno(r, "Failed to parse DNSKEY flags field %s on line %s:%u", flags, path, line);
298 if ((f & DNSKEY_FLAG_ZONE_KEY) == 0) {
299 log_warning("DNSKEY lacks zone key bit set on line %s:%u", path, line);
300 return -EINVAL;
301 }
302 if ((f & DNSKEY_FLAG_REVOKE)) {
303 log_warning("DNSKEY is already revoked on line %s:%u", path, line);
304 return -EINVAL;
305 }
306
307 a = dnssec_algorithm_from_string(algorithm);
308 if (a < 0) {
309 log_warning("Failed to parse DNSKEY algorithm %s on line %s:%u", algorithm, path, line);
310 return -EINVAL;
311 }
312
313 r = unbase64mem(key, strlen(key), &k, &l);
314 if (r < 0)
315 return log_warning_errno(r, "Failed to parse DNSKEY key data %s on line %s:%u", key, path, line);
316
317 rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, domain);
318 if (!rr)
319 return log_oom();
320
321 rr->dnskey.flags = f;
322 rr->dnskey.protocol = 3;
323 rr->dnskey.algorithm = a;
324 rr->dnskey.key_size = l;
325 rr->dnskey.key = k;
326 k = NULL;
327
328 } else {
329 log_warning("RR type %s is not supported, ignoring line %s:%u.", type, path, line);
330 return -EINVAL;
331 }
332
333 if (!isempty(p)) {
334 log_warning("Trailing garbage on line %s:%u, ignoring line.", path, line);
335 return -EINVAL;
336 }
337
338 r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
339 if (r < 0)
340 return log_oom();
341
342 old_answer = hashmap_get(d->positive_by_key, rr->key);
343 answer = dns_answer_ref(old_answer);
344
345 r = dns_answer_add_extend(&answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
346 if (r < 0)
347 return log_error_errno(r, "Failed to add trust anchor RR: %m");
348
349 r = hashmap_replace(d->positive_by_key, rr->key, answer);
350 if (r < 0)
351 return log_error_errno(r, "Failed to add answer to trust anchor: %m");
352
353 old_answer = dns_answer_unref(old_answer);
354 answer = NULL;
355
356 return 0;
357 }
358
359 static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, unsigned line, const char *s) {
360 _cleanup_free_ char *domain = NULL;
361 const char *p = s;
362 int r;
363
364 assert(d);
365 assert(line);
366
367 r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
368 if (r < 0)
369 return log_warning_errno(r, "Unable to parse line %s:%u: %m", path, line);
370
371 if (!dns_name_is_valid(domain)) {
372 log_warning("Domain name %s is invalid, at line %s:%u, ignoring line.", domain, path, line);
373 return -EINVAL;
374 }
375
376 if (!isempty(p)) {
377 log_warning("Trailing garbage at line %s:%u, ignoring line.", path, line);
378 return -EINVAL;
379 }
380
381 r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
382 if (r < 0)
383 return log_oom();
384
385 r = set_put(d->negative_by_name, domain);
386 if (r < 0)
387 return log_oom();
388 if (r > 0)
389 domain = NULL;
390
391 return 0;
392 }
393
394 static int dns_trust_anchor_load_files(
395 DnsTrustAnchor *d,
396 const char *suffix,
397 int (*loader)(DnsTrustAnchor *d, const char *path, unsigned n, const char *line)) {
398
399 _cleanup_strv_free_ char **files = NULL;
400 char **f;
401 int r;
402
403 assert(d);
404 assert(suffix);
405 assert(loader);
406
407 r = conf_files_list_nulstr(&files, suffix, NULL, trust_anchor_dirs);
408 if (r < 0)
409 return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix);
410
411 STRV_FOREACH(f, files) {
412 _cleanup_fclose_ FILE *g = NULL;
413 char line[LINE_MAX];
414 unsigned n = 0;
415
416 g = fopen(*f, "r");
417 if (!g) {
418 if (errno == ENOENT)
419 continue;
420
421 log_warning_errno(errno, "Failed to open %s: %m", *f);
422 continue;
423 }
424
425 FOREACH_LINE(line, g, log_warning_errno(errno, "Failed to read %s, ignoring: %m", *f)) {
426 char *l;
427
428 n++;
429
430 l = strstrip(line);
431 if (isempty(l))
432 continue;
433
434 if (*l == ';')
435 continue;
436
437 (void) loader(d, *f, n, l);
438 }
439 }
440
441 return 0;
442 }
443
444 static int domain_name_cmp(const void *a, const void *b) {
445 char **x = (char**) a, **y = (char**) b;
446
447 return dns_name_compare_func(*x, *y);
448 }
449
450 static int dns_trust_anchor_dump(DnsTrustAnchor *d) {
451 DnsAnswer *a;
452 Iterator i;
453
454 assert(d);
455
456 if (hashmap_isempty(d->positive_by_key))
457 log_info("No positive trust anchors defined.");
458 else {
459 log_info("Positive Trust Anchors:");
460 HASHMAP_FOREACH(a, d->positive_by_key, i) {
461 DnsResourceRecord *rr;
462
463 DNS_ANSWER_FOREACH(rr, a)
464 log_info("%s", dns_resource_record_to_string(rr));
465 }
466 }
467
468 if (set_isempty(d->negative_by_name))
469 log_info("No negative trust anchors defined.");
470 else {
471 _cleanup_free_ char **l = NULL, *j = NULL;
472
473 l = set_get_strv(d->negative_by_name);
474 if (!l)
475 return log_oom();
476
477 qsort_safe(l, set_size(d->negative_by_name), sizeof(char*), domain_name_cmp);
478
479 j = strv_join(l, " ");
480 if (!j)
481 return log_oom();
482
483 log_info("Negative trust anchors: %s", j);
484 }
485
486 return 0;
487 }
488
489 int dns_trust_anchor_load(DnsTrustAnchor *d) {
490 int r;
491
492 assert(d);
493
494 /* If loading things from disk fails, we don't consider this fatal */
495 (void) dns_trust_anchor_load_files(d, ".positive", dns_trust_anchor_load_positive);
496 (void) dns_trust_anchor_load_files(d, ".negative", dns_trust_anchor_load_negative);
497
498 /* However, if the built-in DS fails, then we have a problem. */
499 r = dns_trust_anchor_add_builtin_positive(d);
500 if (r < 0)
501 return log_error_errno(r, "Failed to add built-in positive trust anchor: %m");
502
503 r = dns_trust_anchor_add_builtin_negative(d);
504 if (r < 0)
505 return log_error_errno(r, "Failed to add built-in negative trust anchor: %m");
506
507 dns_trust_anchor_dump(d);
508
509 return 0;
510 }
511
512 void dns_trust_anchor_flush(DnsTrustAnchor *d) {
513 DnsAnswer *a;
514 DnsResourceRecord *rr;
515
516 assert(d);
517
518 while ((a = hashmap_steal_first(d->positive_by_key)))
519 dns_answer_unref(a);
520 d->positive_by_key = hashmap_free(d->positive_by_key);
521
522 while ((rr = set_steal_first(d->revoked_by_rr)))
523 dns_resource_record_unref(rr);
524 d->revoked_by_rr = set_free(d->revoked_by_rr);
525
526 d->negative_by_name = set_free_free(d->negative_by_name);
527 }
528
529 int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
530 DnsAnswer *a;
531
532 assert(d);
533 assert(key);
534 assert(ret);
535
536 /* We only serve DS and DNSKEY RRs. */
537 if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
538 return 0;
539
540 a = hashmap_get(d->positive_by_key, key);
541 if (!a)
542 return 0;
543
544 *ret = dns_answer_ref(a);
545 return 1;
546 }
547
548 int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
549 assert(d);
550 assert(name);
551
552 return set_contains(d->negative_by_name, name);
553 }
554
555 static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) {
556 int r;
557
558 assert(d);
559
560 r = set_ensure_allocated(&d->revoked_by_rr, &dns_resource_record_hash_ops);
561 if (r < 0)
562 return r;
563
564 r = set_put(d->revoked_by_rr, rr);
565 if (r < 0)
566 return r;
567 if (r > 0)
568 dns_resource_record_ref(rr);
569
570 return r;
571 }
572
573 static int dns_trust_anchor_remove_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
574 _cleanup_(dns_answer_unrefp) DnsAnswer *new_answer = NULL;
575 DnsAnswer *old_answer;
576 int r;
577
578 /* Remember that this is a revoked trust anchor RR */
579 r = dns_trust_anchor_revoked_put(d, rr);
580 if (r < 0)
581 return r;
582
583 /* Remove this from the positive trust anchor */
584 old_answer = hashmap_get(d->positive_by_key, rr->key);
585 if (!old_answer)
586 return 0;
587
588 new_answer = dns_answer_ref(old_answer);
589
590 r = dns_answer_remove_by_rr(&new_answer, rr);
591 if (r <= 0)
592 return r;
593
594 /* We found the key! Warn the user */
595 log_struct(LOG_WARNING,
596 LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED),
597 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)),
598 "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr),
599 NULL);
600
601 if (dns_answer_size(new_answer) <= 0) {
602 assert_se(hashmap_remove(d->positive_by_key, rr->key) == old_answer);
603 dns_answer_unref(old_answer);
604 return 1;
605 }
606
607 r = hashmap_replace(d->positive_by_key, new_answer->items[0].rr->key, new_answer);
608 if (r < 0)
609 return r;
610
611 new_answer = NULL;
612 dns_answer_unref(old_answer);
613 return 1;
614 }
615
616 static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) {
617 DnsAnswer *a;
618 int r;
619
620 assert(d);
621 assert(revoked_dnskey);
622 assert(revoked_dnskey->key->type == DNS_TYPE_DNSKEY);
623 assert(revoked_dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE);
624
625 a = hashmap_get(d->positive_by_key, revoked_dnskey->key);
626 if (a) {
627 DnsResourceRecord *anchor;
628
629 /* First, look for the precise DNSKEY in our trust anchor database */
630
631 DNS_ANSWER_FOREACH(anchor, a) {
632
633 if (anchor->dnskey.protocol != revoked_dnskey->dnskey.protocol)
634 continue;
635
636 if (anchor->dnskey.algorithm != revoked_dnskey->dnskey.algorithm)
637 continue;
638
639 if (anchor->dnskey.key_size != revoked_dnskey->dnskey.key_size)
640 continue;
641
642 /* Note that we allow the REVOKE bit to be
643 * different! It will be set in the revoked
644 * key, but unset in our version of it */
645 if (((anchor->dnskey.flags ^ revoked_dnskey->dnskey.flags) | DNSKEY_FLAG_REVOKE) != DNSKEY_FLAG_REVOKE)
646 continue;
647
648 if (memcmp(anchor->dnskey.key, revoked_dnskey->dnskey.key, anchor->dnskey.key_size) != 0)
649 continue;
650
651 dns_trust_anchor_remove_revoked(d, anchor);
652 break;
653 }
654 }
655
656 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)));
657 if (a) {
658 DnsResourceRecord *anchor;
659
660 /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
661
662 DNS_ANSWER_FOREACH(anchor, a) {
663
664 /* We set mask_revoke to true here, since our
665 * DS fingerprint will be the one of the
666 * unrevoked DNSKEY, but the one we got passed
667 * here has the bit set. */
668 r = dnssec_verify_dnskey_by_ds(revoked_dnskey, anchor, true);
669 if (r < 0)
670 return r;
671 if (r == 0)
672 continue;
673
674 dns_trust_anchor_remove_revoked(d, anchor);
675 break;
676 }
677 }
678
679 return 0;
680 }
681
682 int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs) {
683 DnsResourceRecord *rrsig;
684 int r;
685
686 assert(d);
687 assert(dnskey);
688
689 /* Looks if "dnskey" is a self-signed RR that has been revoked
690 * and matches one of our trust anchor entries. If so, removes
691 * it from the trust anchor and returns > 0. */
692
693 if (dnskey->key->type != DNS_TYPE_DNSKEY)
694 return 0;
695
696 /* Is this DNSKEY revoked? */
697 if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0)
698 return 0;
699
700 /* Could this be interesting to us at all? If not,
701 * there's no point in looking for and verifying a
702 * self-signed RRSIG. */
703 if (!dns_trust_anchor_knows_domain_positive(d, DNS_RESOURCE_KEY_NAME(dnskey->key)))
704 return 0;
705
706 /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */
707 DNS_ANSWER_FOREACH(rrsig, rrs) {
708 DnssecResult result;
709
710 if (rrsig->key->type != DNS_TYPE_RRSIG)
711 continue;
712
713 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true);
714 if (r < 0)
715 return r;
716 if (r == 0)
717 continue;
718
719 r = dnssec_verify_rrset(rrs, dnskey->key, rrsig, dnskey, USEC_INFINITY, &result);
720 if (r < 0)
721 return r;
722 if (result != DNSSEC_VALIDATED)
723 continue;
724
725 /* Bingo! This is a revoked self-signed DNSKEY. Let's
726 * see if this precise one exists in our trust anchor
727 * database, too. */
728 r = dns_trust_anchor_check_revoked_one(d, dnskey);
729 if (r < 0)
730 return r;
731
732 return 1;
733 }
734
735 return 0;
736 }
737
738 int dns_trust_anchor_is_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
739 assert(d);
740
741 if (!IN_SET(rr->key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
742 return 0;
743
744 return set_contains(d->revoked_by_rr, rr);
745 }