]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-trust-anchor.c
resolved: fix the rcode to SUCCESS if we find at least one matching RR in a DNS response
[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;
c9c72065 514 DnsResourceRecord *rr;
0d2cd476
LP
515
516 assert(d);
517
8e54f5d9 518 while ((a = hashmap_steal_first(d->positive_by_key)))
0d2cd476 519 dns_answer_unref(a);
8e54f5d9 520 d->positive_by_key = hashmap_free(d->positive_by_key);
c9c72065
LP
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
8e54f5d9 526 d->negative_by_name = set_free_free(d->negative_by_name);
0d2cd476
LP
527}
528
8e54f5d9 529int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *key, DnsAnswer **ret) {
0d2cd476
LP
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
8e54f5d9 540 a = hashmap_get(d->positive_by_key, key);
0d2cd476
LP
541 if (!a)
542 return 0;
543
544 *ret = dns_answer_ref(a);
545 return 1;
546}
8e54f5d9
LP
547
548int 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}
0c857028 554
c9c72065
LP
555static 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
0c857028
LP
573static 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
c9c72065
LP
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 */
0c857028
LP
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
616static 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
c9c72065
LP
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 */
0c857028
LP
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
c9c72065
LP
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. */
96bb7673 668 r = dnssec_verify_dnskey_by_ds(revoked_dnskey, anchor, true);
0c857028
LP
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
d424da2a
LP
682int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, DnsAnswer *rrs) {
683 DnsResourceRecord *rrsig;
0c857028
LP
684 int r;
685
686 assert(d);
d424da2a
LP
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. */
0c857028 692
d424da2a
LP
693 if (dnskey->key->type != DNS_TYPE_DNSKEY)
694 return 0;
0c857028 695
d424da2a
LP
696 /* Is this DNSKEY revoked? */
697 if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE) == 0)
0c857028
LP
698 return 0;
699
d424da2a
LP
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) {
0c857028
LP
708 DnssecResult result;
709
d424da2a
LP
710 if (rrsig->key->type != DNS_TYPE_RRSIG)
711 continue;
712
713 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, true);
0c857028
LP
714 if (r < 0)
715 return r;
716 if (r == 0)
717 continue;
718
d424da2a
LP
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)
0c857028
LP
723 continue;
724
d424da2a
LP
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;
0c857028 731
d424da2a 732 return 1;
0c857028
LP
733 }
734
735 return 0;
736}
c9c72065
LP
737
738int 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}