]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-trust-anchor.c
resolved: look for revoked trust anchors before validating a message
[thirdparty/systemd.git] / src / resolve / resolved-dns-trust-anchor.c
CommitLineData
0d2cd476
LP
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
0c857028
LP
22#include <sd-messages.h>
23
0d2cd476 24#include "alloc-util.h"
8e54f5d9
LP
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"
0d2cd476 32#include "resolved-dns-trust-anchor.h"
0c857028 33#include "resolved-dns-dnssec.h"
8e54f5d9
LP
34#include "set.h"
35#include "string-util.h"
36#include "strv.h"
0d2cd476 37
e7d179ac 38static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d");
8e54f5d9
LP
39
40/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */
0d2cd476
LP
41static 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
86e9cbca
LP
45static 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
30c77809 56static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) {
0d2cd476
LP
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
8e54f5d9 63 r = hashmap_ensure_allocated(&d->positive_by_key, &dns_resource_key_hash_ops);
0d2cd476
LP
64 if (r < 0)
65 return r;
66
86e9cbca
LP
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, "."))
d76f90f1
LP
72 return 0;
73
0d2cd476
LP
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
105e1512 91 r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
0d2cd476
LP
92 if (r < 0)
93 return r;
94
8e54f5d9
LP
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
30c77809
LP
103static 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
8e54f5d9
LP
192static 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);
2a0d751b
LP
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 }
8e54f5d9
LP
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);
0d2cd476 339 if (r < 0)
b3331c39 340 return log_oom();
0d2cd476 341
8e54f5d9
LP
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);
0d2cd476 354 answer = NULL;
8e54f5d9
LP
355
356 return 0;
357}
358
359static 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)
b3331c39 383 return log_oom();
8e54f5d9
LP
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
394static 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
bec69050
LP
444static 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
450static int dns_trust_anchor_dump(DnsTrustAnchor *d) {
8e54f5d9
LP
451 DnsAnswer *a;
452 Iterator i;
453
454 assert(d);
455
105f6c4b
LP
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 }
8e54f5d9
LP
466 }
467
105f6c4b
LP
468 if (set_isempty(d->negative_by_name))
469 log_info("No negative trust anchors defined.");
470 else {
bec69050
LP
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();
8e54f5d9 476
bec69050
LP
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);
8e54f5d9 484 }
bec69050
LP
485
486 return 0;
8e54f5d9
LP
487}
488
489int 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. */
30c77809
LP
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);
8e54f5d9 504 if (r < 0)
30c77809 505 return log_error_errno(r, "Failed to add built-in negative trust anchor: %m");
8e54f5d9
LP
506
507 dns_trust_anchor_dump(d);
508
0d2cd476
LP
509 return 0;
510}
511
512void dns_trust_anchor_flush(DnsTrustAnchor *d) {
513 DnsAnswer *a;
514
515 assert(d);
516
8e54f5d9 517 while ((a = hashmap_steal_first(d->positive_by_key)))
0d2cd476
LP
518 dns_answer_unref(a);
519
8e54f5d9
LP
520 d->positive_by_key = hashmap_free(d->positive_by_key);
521 d->negative_by_name = set_free_free(d->negative_by_name);
0d2cd476
LP
522}
523
8e54f5d9 524int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
0d2cd476
LP
525 DnsAnswer *a;
526
527 assert(d);
528 assert(key);
529 assert(ret);
530
531 /* We only serve DS and DNSKEY RRs. */
532 if (!IN_SET(key->type, DNS_TYPE_DS, DNS_TYPE_DNSKEY))
533 return 0;
534
8e54f5d9 535 a = hashmap_get(d->positive_by_key, key);
0d2cd476
LP
536 if (!a)
537 return 0;
538
539 *ret = dns_answer_ref(a);
540 return 1;
541}
8e54f5d9
LP
542
543int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) {
544 assert(d);
545 assert(name);
546
547 return set_contains(d->negative_by_name, name);
548}
0c857028
LP
549
550static int dns_trust_anchor_remove_revoked(DnsTrustAnchor *d, DnsResourceRecord *rr) {
551 _cleanup_(dns_answer_unrefp) DnsAnswer *new_answer = NULL;
552 DnsAnswer *old_answer;
553 int r;
554
555 old_answer = hashmap_get(d->positive_by_key, rr->key);
556 if (!old_answer)
557 return 0;
558
559 new_answer = dns_answer_ref(old_answer);
560
561 r = dns_answer_remove_by_rr(&new_answer, rr);
562 if (r <= 0)
563 return r;
564
565 /* We found the key! Warn the user */
566 log_struct(LOG_WARNING,
567 LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED),
568 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)),
569 "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr),
570 NULL);
571
572 if (dns_answer_size(new_answer) <= 0) {
573 assert_se(hashmap_remove(d->positive_by_key, rr->key) == old_answer);
574 dns_answer_unref(old_answer);
575 return 1;
576 }
577
578 r = hashmap_replace(d->positive_by_key, new_answer->items[0].rr->key, new_answer);
579 if (r < 0)
580 return r;
581
582 new_answer = NULL;
583 dns_answer_unref(old_answer);
584 return 1;
585}
586
587static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceRecord *revoked_dnskey) {
588 DnsAnswer *a;
589 int r;
590
591 assert(d);
592 assert(revoked_dnskey);
593 assert(revoked_dnskey->key->type == DNS_TYPE_DNSKEY);
594 assert(revoked_dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE);
595
596 a = hashmap_get(d->positive_by_key, revoked_dnskey->key);
597 if (a) {
598 DnsResourceRecord *anchor;
599
600 /* First, look for the precise DNSKEY in our trust anchor database */
601
602 DNS_ANSWER_FOREACH(anchor, a) {
603
604 if (anchor->dnskey.protocol != revoked_dnskey->dnskey.protocol)
605 continue;
606
607 if (anchor->dnskey.algorithm != revoked_dnskey->dnskey.algorithm)
608 continue;
609
610 if (anchor->dnskey.key_size != revoked_dnskey->dnskey.key_size)
611 continue;
612
613 if (((anchor->dnskey.flags ^ revoked_dnskey->dnskey.flags) | DNSKEY_FLAG_REVOKE) != DNSKEY_FLAG_REVOKE)
614 continue;
615
616 if (memcmp(anchor->dnskey.key, revoked_dnskey->dnskey.key, anchor->dnskey.key_size) != 0)
617 continue;
618
619 dns_trust_anchor_remove_revoked(d, anchor);
620 break;
621 }
622 }
623
624 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)));
625 if (a) {
626 DnsResourceRecord *anchor;
627
628 /* Second, look for DS RRs matching this DNSKEY in our trust anchor database */
629
630 DNS_ANSWER_FOREACH(anchor, a) {
631
632 r = dnssec_verify_dnskey(revoked_dnskey, anchor, true);
633 if (r < 0)
634 return r;
635 if (r == 0)
636 continue;
637
638 dns_trust_anchor_remove_revoked(d, anchor);
639 break;
640 }
641 }
642
643 return 0;
644}
645
0c857028
LP
646int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsAnswer *rrs, const DnsResourceKey *key) {
647 DnsResourceRecord *dnskey;
648 int r;
649
650 assert(d);
651 assert(key);
652
653 /* Looks for self-signed DNSKEY RRs in "rrs" that have been revoked. */
654
655 if (key->type != DNS_TYPE_DNSKEY)
656 return 0;
657
658 DNS_ANSWER_FOREACH(dnskey, rrs) {
659 DnsResourceRecord *rrsig;
660 DnssecResult result;
661
662 r = dns_resource_key_equal(key, dnskey->key);
663 if (r < 0)
664 return r;
665 if (r == 0)
666 continue;
667
668 /* Is this DNSKEY revoked? */
669 if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0)
670 continue;
671
672 /* Could this be interesting to us at all? If not,
673 * there's no point in looking for and verifying a
674 * self-signed RRSIG. */
86e9cbca 675 if (!dns_trust_anchor_knows_domain_positive(d, DNS_RESOURCE_KEY_NAME(dnskey->key)))
0c857028
LP
676 continue;
677
678 /* Look for a self-signed RRSIG */
679 DNS_ANSWER_FOREACH(rrsig, rrs) {
680
681 if (rrsig->key->type != DNS_TYPE_RRSIG)
682 continue;
683
684 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true);
685 if (r < 0)
686 return r;
687 if (r == 0)
688 continue;
689
690 r = dnssec_verify_rrset(rrs, key, rrsig, dnskey, USEC_INFINITY, &result);
691 if (r < 0)
692 return r;
693 if (result != DNSSEC_VALIDATED)
694 continue;
695
696 /* Bingo! Now, act! */
697 r = dns_trust_anchor_check_revoked_one(d, dnskey);
698 if (r < 0)
699 return r;
700 }
701 }
702
703 return 0;
704}