]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-dnssec.c
resolved: make sure the NSEC proof-of-non-existance check also looks for wildcard...
[thirdparty/systemd.git] / src / resolve / resolved-dns-dnssec.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 <gcrypt.h>
23
24 #include "alloc-util.h"
25 #include "dns-domain.h"
26 #include "hexdecoct.h"
27 #include "resolved-dns-dnssec.h"
28 #include "resolved-dns-packet.h"
29 #include "string-table.h"
30
31 /* Open question:
32 *
33 * How does the DNSSEC canonical form of a hostname with a label
34 * containing a dot look like, the way DNS-SD does it?
35 *
36 * TODO:
37 *
38 * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing)
39 * - multi-label zone compatibility
40 * - cname/dname compatibility
41 * - nxdomain on qname
42 * - bus calls to override DNSEC setting per interface
43 * - log all DNSSEC downgrades
44 * - enable by default
45 *
46 * - RFC 4035, Section 5.3.4 (When receiving a positive wildcard reply, use NSEC to ensure it actually really applies)
47 * - RFC 6840, Section 4.1 (ensure we don't get fed a glue NSEC from the parent zone)
48 * - RFC 6840, Section 4.3 (check for CNAME on NSEC too)
49 * */
50
51 #define VERIFY_RRS_MAX 256
52 #define MAX_KEY_SIZE (32*1024)
53
54 /* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
55 #define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
56
57 /* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value */
58 #define NSEC3_ITERATIONS_MAX 2500
59
60 /*
61 * The DNSSEC Chain of trust:
62 *
63 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
64 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
65 * DS RRs are protected like normal RRs
66 *
67 * Example chain:
68 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
69 */
70
71 static void initialize_libgcrypt(void) {
72 const char *p;
73
74 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
75 return;
76
77 p = gcry_check_version("1.4.5");
78 assert(p);
79
80 gcry_control(GCRYCTL_DISABLE_SECMEM);
81 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
82 }
83
84 uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
85 const uint8_t *p;
86 uint32_t sum, f;
87 size_t i;
88
89 /* The algorithm from RFC 4034, Appendix B. */
90
91 assert(dnskey);
92 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
93
94 f = (uint32_t) dnskey->dnskey.flags;
95
96 if (mask_revoke)
97 f &= ~DNSKEY_FLAG_REVOKE;
98
99 sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
100
101 p = dnskey->dnskey.key;
102
103 for (i = 0; i < dnskey->dnskey.key_size; i++)
104 sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
105
106 sum += (sum >> 16) & UINT32_C(0xFFFF);
107
108 return sum & UINT32_C(0xFFFF);
109 }
110
111 static int rr_compare(const void *a, const void *b) {
112 DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
113 size_t m;
114 int r;
115
116 /* Let's order the RRs according to RFC 4034, Section 6.3 */
117
118 assert(x);
119 assert(*x);
120 assert((*x)->wire_format);
121 assert(y);
122 assert(*y);
123 assert((*y)->wire_format);
124
125 m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y));
126
127 r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m);
128 if (r != 0)
129 return r;
130
131 if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
132 return -1;
133 else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
134 return 1;
135
136 return 0;
137 }
138
139 static int dnssec_rsa_verify_raw(
140 const char *hash_algorithm,
141 const void *signature, size_t signature_size,
142 const void *data, size_t data_size,
143 const void *exponent, size_t exponent_size,
144 const void *modulus, size_t modulus_size) {
145
146 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
147 gcry_mpi_t n = NULL, e = NULL, s = NULL;
148 gcry_error_t ge;
149 int r;
150
151 assert(hash_algorithm);
152
153 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
154 if (ge != 0) {
155 r = -EIO;
156 goto finish;
157 }
158
159 ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
160 if (ge != 0) {
161 r = -EIO;
162 goto finish;
163 }
164
165 ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
166 if (ge != 0) {
167 r = -EIO;
168 goto finish;
169 }
170
171 ge = gcry_sexp_build(&signature_sexp,
172 NULL,
173 "(sig-val (rsa (s %m)))",
174 s);
175
176 if (ge != 0) {
177 r = -EIO;
178 goto finish;
179 }
180
181 ge = gcry_sexp_build(&data_sexp,
182 NULL,
183 "(data (flags pkcs1) (hash %s %b))",
184 hash_algorithm,
185 (int) data_size,
186 data);
187 if (ge != 0) {
188 r = -EIO;
189 goto finish;
190 }
191
192 ge = gcry_sexp_build(&public_key_sexp,
193 NULL,
194 "(public-key (rsa (n %m) (e %m)))",
195 n,
196 e);
197 if (ge != 0) {
198 r = -EIO;
199 goto finish;
200 }
201
202 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
203 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
204 r = 0;
205 else if (ge != 0) {
206 log_debug("RSA signature check failed: %s", gpg_strerror(ge));
207 r = -EIO;
208 } else
209 r = 1;
210
211 finish:
212 if (e)
213 gcry_mpi_release(e);
214 if (n)
215 gcry_mpi_release(n);
216 if (s)
217 gcry_mpi_release(s);
218
219 if (public_key_sexp)
220 gcry_sexp_release(public_key_sexp);
221 if (signature_sexp)
222 gcry_sexp_release(signature_sexp);
223 if (data_sexp)
224 gcry_sexp_release(data_sexp);
225
226 return r;
227 }
228
229 static int dnssec_rsa_verify(
230 const char *hash_algorithm,
231 const void *hash, size_t hash_size,
232 DnsResourceRecord *rrsig,
233 DnsResourceRecord *dnskey) {
234
235 size_t exponent_size, modulus_size;
236 void *exponent, *modulus;
237
238 assert(hash_algorithm);
239 assert(hash);
240 assert(hash_size > 0);
241 assert(rrsig);
242 assert(dnskey);
243
244 if (*(uint8_t*) dnskey->dnskey.key == 0) {
245 /* exponent is > 255 bytes long */
246
247 exponent = (uint8_t*) dnskey->dnskey.key + 3;
248 exponent_size =
249 ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) |
250 ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]);
251
252 if (exponent_size < 256)
253 return -EINVAL;
254
255 if (3 + exponent_size >= dnskey->dnskey.key_size)
256 return -EINVAL;
257
258 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
259 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
260
261 } else {
262 /* exponent is <= 255 bytes long */
263
264 exponent = (uint8_t*) dnskey->dnskey.key + 1;
265 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
266
267 if (exponent_size <= 0)
268 return -EINVAL;
269
270 if (1 + exponent_size >= dnskey->dnskey.key_size)
271 return -EINVAL;
272
273 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
274 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
275 }
276
277 return dnssec_rsa_verify_raw(
278 hash_algorithm,
279 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
280 hash, hash_size,
281 exponent, exponent_size,
282 modulus, modulus_size);
283 }
284
285 static int dnssec_ecdsa_verify_raw(
286 const char *hash_algorithm,
287 const char *curve,
288 const void *signature_r, size_t signature_r_size,
289 const void *signature_s, size_t signature_s_size,
290 const void *data, size_t data_size,
291 const void *key, size_t key_size) {
292
293 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
294 gcry_mpi_t q = NULL, r = NULL, s = NULL;
295 gcry_error_t ge;
296 int k;
297
298 assert(hash_algorithm);
299
300 ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
301 if (ge != 0) {
302 k = -EIO;
303 goto finish;
304 }
305
306 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
307 if (ge != 0) {
308 k = -EIO;
309 goto finish;
310 }
311
312 ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
313 if (ge != 0) {
314 k = -EIO;
315 goto finish;
316 }
317
318 ge = gcry_sexp_build(&signature_sexp,
319 NULL,
320 "(sig-val (ecdsa (r %m) (s %m)))",
321 r,
322 s);
323 if (ge != 0) {
324 k = -EIO;
325 goto finish;
326 }
327
328 ge = gcry_sexp_build(&data_sexp,
329 NULL,
330 "(data (flags rfc6979) (hash %s %b))",
331 hash_algorithm,
332 (int) data_size,
333 data);
334 if (ge != 0) {
335 k = -EIO;
336 goto finish;
337 }
338
339 ge = gcry_sexp_build(&public_key_sexp,
340 NULL,
341 "(public-key (ecc (curve %s) (q %m)))",
342 curve,
343 q);
344 if (ge != 0) {
345 k = -EIO;
346 goto finish;
347 }
348
349 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
350 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
351 k = 0;
352 else if (ge != 0) {
353 log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
354 k = -EIO;
355 } else
356 k = 1;
357 finish:
358 if (r)
359 gcry_mpi_release(r);
360 if (s)
361 gcry_mpi_release(s);
362 if (q)
363 gcry_mpi_release(q);
364
365 if (public_key_sexp)
366 gcry_sexp_release(public_key_sexp);
367 if (signature_sexp)
368 gcry_sexp_release(signature_sexp);
369 if (data_sexp)
370 gcry_sexp_release(data_sexp);
371
372 return k;
373 }
374
375 static int dnssec_ecdsa_verify(
376 const char *hash_algorithm,
377 int algorithm,
378 const void *hash, size_t hash_size,
379 DnsResourceRecord *rrsig,
380 DnsResourceRecord *dnskey) {
381
382 const char *curve;
383 size_t key_size;
384 uint8_t *q;
385
386 assert(hash);
387 assert(hash_size);
388 assert(rrsig);
389 assert(dnskey);
390
391 if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
392 key_size = 32;
393 curve = "NIST P-256";
394 } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
395 key_size = 48;
396 curve = "NIST P-384";
397 } else
398 return -EOPNOTSUPP;
399
400 if (dnskey->dnskey.key_size != key_size * 2)
401 return -EINVAL;
402
403 if (rrsig->rrsig.signature_size != key_size * 2)
404 return -EINVAL;
405
406 q = alloca(key_size*2 + 1);
407 q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
408 memcpy(q+1, dnskey->dnskey.key, key_size*2);
409
410 return dnssec_ecdsa_verify_raw(
411 hash_algorithm,
412 curve,
413 rrsig->rrsig.signature, key_size,
414 (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
415 hash, hash_size,
416 q, key_size*2+1);
417 }
418
419 static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
420 gcry_md_write(md, &v, sizeof(v));
421 }
422
423 static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
424 v = htobe16(v);
425 gcry_md_write(md, &v, sizeof(v));
426 }
427
428 static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
429 v = htobe32(v);
430 gcry_md_write(md, &v, sizeof(v));
431 }
432
433 static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) {
434 int n_key_labels, n_signer_labels;
435 const char *name;
436 int r;
437
438 /* Checks whether the specified RRSIG RR is somewhat valid, and initializes the .n_skip_labels_source and
439 * .n_skip_labels_signer fields so that we can use them later on. */
440
441 assert(rrsig);
442 assert(rrsig->key->type == DNS_TYPE_RRSIG);
443
444 /* Check if this RRSIG RR is already prepared */
445 if (rrsig->n_skip_labels_source != (unsigned) -1)
446 return 0;
447
448 if (rrsig->rrsig.inception > rrsig->rrsig.expiration)
449 return -EINVAL;
450
451 name = DNS_RESOURCE_KEY_NAME(rrsig->key);
452
453 n_key_labels = dns_name_count_labels(name);
454 if (n_key_labels < 0)
455 return n_key_labels;
456 if (rrsig->rrsig.labels > n_key_labels)
457 return -EINVAL;
458
459 n_signer_labels = dns_name_count_labels(rrsig->rrsig.signer);
460 if (n_signer_labels < 0)
461 return n_signer_labels;
462 if (n_signer_labels > rrsig->rrsig.labels)
463 return -EINVAL;
464
465 r = dns_name_skip(name, n_key_labels - n_signer_labels, &name);
466 if (r < 0)
467 return r;
468 if (r == 0)
469 return -EINVAL;
470
471 /* Check if the signer is really a suffix of us */
472 r = dns_name_equal(name, rrsig->rrsig.signer);
473 if (r < 0)
474 return r;
475 if (r == 0)
476 return -EINVAL;
477
478 rrsig->n_skip_labels_source = n_key_labels - rrsig->rrsig.labels;
479 rrsig->n_skip_labels_signer = n_key_labels - n_signer_labels;
480
481 return 0;
482 }
483
484 static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
485 usec_t expiration, inception, skew;
486
487 assert(rrsig);
488 assert(rrsig->key->type == DNS_TYPE_RRSIG);
489
490 if (realtime == USEC_INFINITY)
491 realtime = now(CLOCK_REALTIME);
492
493 expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
494 inception = rrsig->rrsig.inception * USEC_PER_SEC;
495
496 /* Consider inverted validity intervals as expired */
497 if (inception > expiration)
498 return true;
499
500 /* Permit a certain amount of clock skew of 10% of the valid
501 * time range. This takes inspiration from unbound's
502 * resolver. */
503 skew = (expiration - inception) / 10;
504 if (skew > SKEW_MAX)
505 skew = SKEW_MAX;
506
507 if (inception < skew)
508 inception = 0;
509 else
510 inception -= skew;
511
512 if (expiration + skew < expiration)
513 expiration = USEC_INFINITY;
514 else
515 expiration += skew;
516
517 return realtime < inception || realtime > expiration;
518 }
519
520 static int algorithm_to_gcrypt_md(uint8_t algorithm) {
521
522 /* Translates a DNSSEC signature algorithm into a gcrypt
523 * digest identifier.
524 *
525 * Note that we implement all algorithms listed as "Must
526 * implement" and "Recommended to Implement" in RFC6944. We
527 * don't implement any algorithms that are listed as
528 * "Optional" or "Must Not Implement". Specifically, we do not
529 * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
530 * GOST-ECC. */
531
532 switch (algorithm) {
533
534 case DNSSEC_ALGORITHM_RSASHA1:
535 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
536 return GCRY_MD_SHA1;
537
538 case DNSSEC_ALGORITHM_RSASHA256:
539 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
540 return GCRY_MD_SHA256;
541
542 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
543 return GCRY_MD_SHA384;
544
545 case DNSSEC_ALGORITHM_RSASHA512:
546 return GCRY_MD_SHA512;
547
548 default:
549 return -EOPNOTSUPP;
550 }
551 }
552
553 static void dnssec_fix_rrset_ttl(
554 DnsResourceRecord *list[],
555 unsigned n,
556 DnsResourceRecord *rrsig,
557 usec_t realtime) {
558
559 unsigned k;
560
561 assert(list);
562 assert(n > 0);
563 assert(rrsig);
564
565 for (k = 0; k < n; k++) {
566 DnsResourceRecord *rr = list[k];
567
568 /* Pick the TTL as the minimum of the RR's TTL, the
569 * RR's original TTL according to the RRSIG and the
570 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
571 rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
572 rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
573
574 /* Copy over information about the signer and wildcard source of synthesis */
575 rr->n_skip_labels_source = rrsig->n_skip_labels_source;
576 rr->n_skip_labels_signer = rrsig->n_skip_labels_signer;
577 }
578
579 rrsig->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
580 }
581
582 int dnssec_verify_rrset(
583 DnsAnswer *a,
584 const DnsResourceKey *key,
585 DnsResourceRecord *rrsig,
586 DnsResourceRecord *dnskey,
587 usec_t realtime,
588 DnssecResult *result) {
589
590 uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
591 DnsResourceRecord **list, *rr;
592 const char *source, *name;
593 gcry_md_hd_t md = NULL;
594 int r, md_algorithm;
595 size_t k, n = 0;
596 size_t hash_size;
597 void *hash;
598 bool wildcard;
599
600 assert(key);
601 assert(rrsig);
602 assert(dnskey);
603 assert(result);
604 assert(rrsig->key->type == DNS_TYPE_RRSIG);
605 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
606
607 /* Verifies the the RRSet matching the specified "key" in "a",
608 * using the signature "rrsig" and the key "dnskey". It's
609 * assumed the RRSIG and DNSKEY match. */
610
611 md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
612 if (md_algorithm == -EOPNOTSUPP) {
613 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
614 return 0;
615 }
616 if (md_algorithm < 0)
617 return md_algorithm;
618
619 r = dnssec_rrsig_prepare(rrsig);
620 if (r == -EINVAL) {
621 *result = DNSSEC_INVALID;
622 return r;
623 }
624 if (r < 0)
625 return r;
626
627 r = dnssec_rrsig_expired(rrsig, realtime);
628 if (r < 0)
629 return r;
630 if (r > 0) {
631 *result = DNSSEC_SIGNATURE_EXPIRED;
632 return 0;
633 }
634
635 name = DNS_RESOURCE_KEY_NAME(key);
636
637 /* Some keys may only appear signed in the zone apex, and are invalid anywhere else. (SOA, NS...) */
638 if (dns_type_apex_only(rrsig->rrsig.type_covered)) {
639 r = dns_name_equal(rrsig->rrsig.signer, name);
640 if (r < 0)
641 return r;
642 if (r == 0) {
643 *result = DNSSEC_INVALID;
644 return 0;
645 }
646 }
647
648 /* OTOH DS RRs may not appear in the zone apex, but are valid everywhere else. */
649 if (rrsig->rrsig.type_covered == DNS_TYPE_DS) {
650 r = dns_name_equal(rrsig->rrsig.signer, name);
651 if (r < 0)
652 return r;
653 if (r > 0) {
654 *result = DNSSEC_INVALID;
655 return 0;
656 }
657 }
658
659 /* Determine the "Source of Synthesis" and whether this is a wildcard RRSIG */
660 r = dns_name_suffix(name, rrsig->rrsig.labels, &source);
661 if (r < 0)
662 return r;
663 if (r > 0 && !dns_type_may_wildcard(rrsig->rrsig.type_covered)) {
664 /* We refuse to validate NSEC3 or SOA RRs that are synthesized from wildcards */
665 *result = DNSSEC_INVALID;
666 return 0;
667 }
668 if (r == 1) {
669 /* If we stripped a single label, then let's see if that maybe was "*". If so, we are not really
670 * synthesized from a wildcard, we are the wildcard itself. Treat that like a normal name. */
671 r = dns_name_startswith(name, "*");
672 if (r < 0)
673 return r;
674 if (r > 0)
675 source = name;
676
677 wildcard = r == 0;
678 } else
679 wildcard = r > 0;
680
681 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
682 list = newa(DnsResourceRecord *, dns_answer_size(a));
683
684 DNS_ANSWER_FOREACH(rr, a) {
685 r = dns_resource_key_equal(key, rr->key);
686 if (r < 0)
687 return r;
688 if (r == 0)
689 continue;
690
691 /* We need the wire format for ordering, and digest calculation */
692 r = dns_resource_record_to_wire_format(rr, true);
693 if (r < 0)
694 return r;
695
696 list[n++] = rr;
697
698 if (n > VERIFY_RRS_MAX)
699 return -E2BIG;
700 }
701
702 if (n <= 0)
703 return -ENODATA;
704
705 /* Bring the RRs into canonical order */
706 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
707
708 /* OK, the RRs are now in canonical order. Let's calculate the digest */
709 initialize_libgcrypt();
710
711 hash_size = gcry_md_get_algo_dlen(md_algorithm);
712 assert(hash_size > 0);
713
714 gcry_md_open(&md, md_algorithm, 0);
715 if (!md)
716 return -EIO;
717
718 md_add_uint16(md, rrsig->rrsig.type_covered);
719 md_add_uint8(md, rrsig->rrsig.algorithm);
720 md_add_uint8(md, rrsig->rrsig.labels);
721 md_add_uint32(md, rrsig->rrsig.original_ttl);
722 md_add_uint32(md, rrsig->rrsig.expiration);
723 md_add_uint32(md, rrsig->rrsig.inception);
724 md_add_uint16(md, rrsig->rrsig.key_tag);
725
726 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
727 if (r < 0)
728 goto finish;
729 gcry_md_write(md, wire_format_name, r);
730
731 /* Convert the source of synthesis into wire format */
732 r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true);
733 if (r < 0)
734 goto finish;
735
736 for (k = 0; k < n; k++) {
737 size_t l;
738
739 rr = list[k];
740
741 /* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */
742 if (wildcard)
743 gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
744 gcry_md_write(md, wire_format_name, r);
745
746 md_add_uint16(md, rr->key->type);
747 md_add_uint16(md, rr->key->class);
748 md_add_uint32(md, rrsig->rrsig.original_ttl);
749
750 l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
751 assert(l <= 0xFFFF);
752
753 md_add_uint16(md, (uint16_t) l);
754 gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l);
755 }
756
757 hash = gcry_md_read(md, 0);
758 if (!hash) {
759 r = -EIO;
760 goto finish;
761 }
762
763 switch (rrsig->rrsig.algorithm) {
764
765 case DNSSEC_ALGORITHM_RSASHA1:
766 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
767 case DNSSEC_ALGORITHM_RSASHA256:
768 case DNSSEC_ALGORITHM_RSASHA512:
769 r = dnssec_rsa_verify(
770 gcry_md_algo_name(md_algorithm),
771 hash, hash_size,
772 rrsig,
773 dnskey);
774 break;
775
776 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
777 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
778 r = dnssec_ecdsa_verify(
779 gcry_md_algo_name(md_algorithm),
780 rrsig->rrsig.algorithm,
781 hash, hash_size,
782 rrsig,
783 dnskey);
784 break;
785 }
786
787 if (r < 0)
788 goto finish;
789
790 /* Now, fix the ttl, expiry, and remember the synthesizing source and the signer */
791 if (r > 0)
792 dnssec_fix_rrset_ttl(list, n, rrsig, realtime);
793
794 if (r == 0)
795 *result = DNSSEC_INVALID;
796 else if (wildcard)
797 *result = DNSSEC_VALIDATED_WILDCARD;
798 else
799 *result = DNSSEC_VALIDATED;
800
801 r = 0;
802
803 finish:
804 gcry_md_close(md);
805 return r;
806 }
807
808 int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
809
810 assert(rrsig);
811 assert(dnskey);
812
813 /* Checks if the specified DNSKEY RR matches the key used for
814 * the signature in the specified RRSIG RR */
815
816 if (rrsig->key->type != DNS_TYPE_RRSIG)
817 return -EINVAL;
818
819 if (dnskey->key->type != DNS_TYPE_DNSKEY)
820 return 0;
821 if (dnskey->key->class != rrsig->key->class)
822 return 0;
823 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
824 return 0;
825 if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
826 return 0;
827 if (dnskey->dnskey.protocol != 3)
828 return 0;
829 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
830 return 0;
831
832 if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
833 return 0;
834
835 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
836 }
837
838 int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
839 assert(key);
840 assert(rrsig);
841
842 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
843
844 if (rrsig->key->type != DNS_TYPE_RRSIG)
845 return 0;
846 if (rrsig->key->class != key->class)
847 return 0;
848 if (rrsig->rrsig.type_covered != key->type)
849 return 0;
850
851 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
852 }
853
854 int dnssec_verify_rrset_search(
855 DnsAnswer *a,
856 const DnsResourceKey *key,
857 DnsAnswer *validated_dnskeys,
858 usec_t realtime,
859 DnssecResult *result,
860 DnsResourceRecord **ret_rrsig) {
861
862 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
863 DnsResourceRecord *rrsig;
864 int r;
865
866 assert(key);
867 assert(result);
868
869 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
870
871 if (!a || a->n_rrs <= 0)
872 return -ENODATA;
873
874 /* Iterate through each RRSIG RR. */
875 DNS_ANSWER_FOREACH(rrsig, a) {
876 DnsResourceRecord *dnskey;
877 DnsAnswerFlags flags;
878
879 /* Is this an RRSIG RR that applies to RRs matching our key? */
880 r = dnssec_key_match_rrsig(key, rrsig);
881 if (r < 0)
882 return r;
883 if (r == 0)
884 continue;
885
886 found_rrsig = true;
887
888 /* Look for a matching key */
889 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
890 DnssecResult one_result;
891
892 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
893 continue;
894
895 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
896 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
897 if (r < 0)
898 return r;
899 if (r == 0)
900 continue;
901
902 /* Take the time here, if it isn't set yet, so
903 * that we do all validations with the same
904 * time. */
905 if (realtime == USEC_INFINITY)
906 realtime = now(CLOCK_REALTIME);
907
908 /* Yay, we found a matching RRSIG with a matching
909 * DNSKEY, awesome. Now let's verify all entries of
910 * the RRSet against the RRSIG and DNSKEY
911 * combination. */
912
913 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
914 if (r < 0)
915 return r;
916
917 switch (one_result) {
918
919 case DNSSEC_VALIDATED:
920 case DNSSEC_VALIDATED_WILDCARD:
921 /* Yay, the RR has been validated,
922 * return immediately, but fix up the expiry */
923 if (ret_rrsig)
924 *ret_rrsig = rrsig;
925
926 *result = one_result;
927 return 0;
928
929 case DNSSEC_INVALID:
930 /* If the signature is invalid, let's try another
931 key and/or signature. After all they
932 key_tags and stuff are not unique, and
933 might be shared by multiple keys. */
934 found_invalid = true;
935 continue;
936
937 case DNSSEC_UNSUPPORTED_ALGORITHM:
938 /* If the key algorithm is
939 unsupported, try another
940 RRSIG/DNSKEY pair, but remember we
941 encountered this, so that we can
942 return a proper error when we
943 encounter nothing better. */
944 found_unsupported_algorithm = true;
945 continue;
946
947 case DNSSEC_SIGNATURE_EXPIRED:
948 /* If the signature is expired, try
949 another one, but remember it, so
950 that we can return this */
951 found_expired_rrsig = true;
952 continue;
953
954 default:
955 assert_not_reached("Unexpected DNSSEC validation result");
956 }
957 }
958 }
959
960 if (found_expired_rrsig)
961 *result = DNSSEC_SIGNATURE_EXPIRED;
962 else if (found_unsupported_algorithm)
963 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
964 else if (found_invalid)
965 *result = DNSSEC_INVALID;
966 else if (found_rrsig)
967 *result = DNSSEC_MISSING_KEY;
968 else
969 *result = DNSSEC_NO_SIGNATURE;
970
971 if (ret_rrsig)
972 *ret_rrsig = NULL;
973
974 return 0;
975 }
976
977 int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
978 DnsResourceRecord *rr;
979 int r;
980
981 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
982
983 DNS_ANSWER_FOREACH(rr, a) {
984 r = dnssec_key_match_rrsig(key, rr);
985 if (r < 0)
986 return r;
987 if (r > 0)
988 return 1;
989 }
990
991 return 0;
992 }
993
994 int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
995 size_t c = 0;
996 int r;
997
998 /* Converts the specified hostname into DNSSEC canonicalized
999 * form. */
1000
1001 if (buffer_max < 2)
1002 return -ENOBUFS;
1003
1004 for (;;) {
1005 r = dns_label_unescape(&n, buffer, buffer_max);
1006 if (r < 0)
1007 return r;
1008 if (r == 0)
1009 break;
1010 if (r > 0) {
1011 int k;
1012
1013 /* DNSSEC validation is always done on the ASCII version of the label */
1014 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
1015 if (k < 0)
1016 return k;
1017 if (k > 0)
1018 r = k;
1019 }
1020
1021 if (buffer_max < (size_t) r + 2)
1022 return -ENOBUFS;
1023
1024 /* The DNSSEC canonical form is not clear on what to
1025 * do with dots appearing in labels, the way DNS-SD
1026 * does it. Refuse it for now. */
1027
1028 if (memchr(buffer, '.', r))
1029 return -EINVAL;
1030
1031 ascii_strlower_n(buffer, (size_t) r);
1032 buffer[r] = '.';
1033
1034 buffer += r + 1;
1035 c += r + 1;
1036
1037 buffer_max -= r + 1;
1038 }
1039
1040 if (c <= 0) {
1041 /* Not even a single label: this is the root domain name */
1042
1043 assert(buffer_max > 2);
1044 buffer[0] = '.';
1045 buffer[1] = 0;
1046
1047 return 1;
1048 }
1049
1050 return (int) c;
1051 }
1052
1053 static int digest_to_gcrypt_md(uint8_t algorithm) {
1054
1055 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
1056
1057 switch (algorithm) {
1058
1059 case DNSSEC_DIGEST_SHA1:
1060 return GCRY_MD_SHA1;
1061
1062 case DNSSEC_DIGEST_SHA256:
1063 return GCRY_MD_SHA256;
1064
1065 case DNSSEC_DIGEST_SHA384:
1066 return GCRY_MD_SHA384;
1067
1068 default:
1069 return -EOPNOTSUPP;
1070 }
1071 }
1072
1073 int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
1074 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
1075 gcry_md_hd_t md = NULL;
1076 size_t hash_size;
1077 int md_algorithm, r;
1078 void *result;
1079
1080 assert(dnskey);
1081 assert(ds);
1082
1083 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
1084
1085 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1086 return -EINVAL;
1087 if (ds->key->type != DNS_TYPE_DS)
1088 return -EINVAL;
1089 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
1090 return -EKEYREJECTED;
1091 if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
1092 return -EKEYREJECTED;
1093 if (dnskey->dnskey.protocol != 3)
1094 return -EKEYREJECTED;
1095
1096 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
1097 return 0;
1098 if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
1099 return 0;
1100
1101 initialize_libgcrypt();
1102
1103 md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
1104 if (md_algorithm < 0)
1105 return md_algorithm;
1106
1107 hash_size = gcry_md_get_algo_dlen(md_algorithm);
1108 assert(hash_size > 0);
1109
1110 if (ds->ds.digest_size != hash_size)
1111 return 0;
1112
1113 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
1114 if (r < 0)
1115 return r;
1116
1117 gcry_md_open(&md, md_algorithm, 0);
1118 if (!md)
1119 return -EIO;
1120
1121 gcry_md_write(md, owner_name, r);
1122 if (mask_revoke)
1123 md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
1124 else
1125 md_add_uint16(md, dnskey->dnskey.flags);
1126 md_add_uint8(md, dnskey->dnskey.protocol);
1127 md_add_uint8(md, dnskey->dnskey.algorithm);
1128 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1129
1130 result = gcry_md_read(md, 0);
1131 if (!result) {
1132 r = -EIO;
1133 goto finish;
1134 }
1135
1136 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
1137
1138 finish:
1139 gcry_md_close(md);
1140 return r;
1141 }
1142
1143 int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
1144 DnsResourceRecord *ds;
1145 DnsAnswerFlags flags;
1146 int r;
1147
1148 assert(dnskey);
1149
1150 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1151 return 0;
1152
1153 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1154
1155 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1156 continue;
1157
1158 if (ds->key->type != DNS_TYPE_DS)
1159 continue;
1160 if (ds->key->class != dnskey->key->class)
1161 continue;
1162
1163 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
1164 if (r < 0)
1165 return r;
1166 if (r == 0)
1167 continue;
1168
1169 r = dnssec_verify_dnskey_by_ds(dnskey, ds, false);
1170 if (IN_SET(r, -EKEYREJECTED, -EOPNOTSUPP))
1171 return 0; /* The DNSKEY is revoked or otherwise invalid, or we don't support the digest algorithm */
1172 if (r < 0)
1173 return r;
1174 if (r > 0)
1175 return 1;
1176 }
1177
1178 return 0;
1179 }
1180
1181 static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
1182
1183 /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
1184
1185 switch (algorithm) {
1186
1187 case NSEC3_ALGORITHM_SHA1:
1188 return GCRY_MD_SHA1;
1189
1190 default:
1191 return -EOPNOTSUPP;
1192 }
1193 }
1194
1195 int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
1196 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
1197 gcry_md_hd_t md = NULL;
1198 size_t hash_size;
1199 int algorithm;
1200 void *result;
1201 unsigned k;
1202 int r;
1203
1204 assert(nsec3);
1205 assert(name);
1206 assert(ret);
1207
1208 if (nsec3->key->type != DNS_TYPE_NSEC3)
1209 return -EINVAL;
1210
1211 if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
1212 log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
1213 return -EOPNOTSUPP;
1214 }
1215
1216 algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
1217 if (algorithm < 0)
1218 return algorithm;
1219
1220 initialize_libgcrypt();
1221
1222 hash_size = gcry_md_get_algo_dlen(algorithm);
1223 assert(hash_size > 0);
1224
1225 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1226 return -EINVAL;
1227
1228 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1229 if (r < 0)
1230 return r;
1231
1232 gcry_md_open(&md, algorithm, 0);
1233 if (!md)
1234 return -EIO;
1235
1236 gcry_md_write(md, wire_format, r);
1237 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1238
1239 result = gcry_md_read(md, 0);
1240 if (!result) {
1241 r = -EIO;
1242 goto finish;
1243 }
1244
1245 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1246 uint8_t tmp[hash_size];
1247 memcpy(tmp, result, hash_size);
1248
1249 gcry_md_reset(md);
1250 gcry_md_write(md, tmp, hash_size);
1251 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1252
1253 result = gcry_md_read(md, 0);
1254 if (!result) {
1255 r = -EIO;
1256 goto finish;
1257 }
1258 }
1259
1260 memcpy(ret, result, hash_size);
1261 r = (int) hash_size;
1262
1263 finish:
1264 gcry_md_close(md);
1265 return r;
1266 }
1267
1268 static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
1269 const char *a, *b;
1270 int r;
1271
1272 assert(rr);
1273
1274 if (rr->key->type != DNS_TYPE_NSEC3)
1275 return 0;
1276
1277 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1278 if (!IN_SET(rr->nsec3.flags, 0, 1))
1279 return 0;
1280
1281 /* Ignore NSEC3 RRs whose algorithm we don't know */
1282 if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
1283 return 0;
1284 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1285 if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1286 return 0;
1287
1288 /* Ignore NSEC3 RRs generated from wildcards */
1289 if (rr->n_skip_labels_source != 0)
1290 return 0;
1291 /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */
1292 if (rr->n_skip_labels_signer != 1)
1293 return 0;
1294
1295 if (!nsec3)
1296 return 1;
1297
1298 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1299
1300 if (nsec3 == rr) /* Shortcut */
1301 return 1;
1302
1303 if (rr->key->class != nsec3->key->class)
1304 return 0;
1305 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1306 return 0;
1307 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1308 return 0;
1309 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1310 return 0;
1311 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1312 return 0;
1313
1314 a = DNS_RESOURCE_KEY_NAME(rr->key);
1315 r = dns_name_parent(&a); /* strip off hash */
1316 if (r < 0)
1317 return r;
1318 if (r == 0)
1319 return 0;
1320
1321 b = DNS_RESOURCE_KEY_NAME(nsec3->key);
1322 r = dns_name_parent(&b); /* strip off hash */
1323 if (r < 0)
1324 return r;
1325 if (r == 0)
1326 return 0;
1327
1328 /* Make sure both have the same parent */
1329 return dns_name_equal(a, b);
1330 }
1331
1332 static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size, const char *zone, char **ret) {
1333 _cleanup_free_ char *l = NULL;
1334 char *j;
1335
1336 assert(hashed);
1337 assert(hashed_size > 0);
1338 assert(zone);
1339 assert(ret);
1340
1341 l = base32hexmem(hashed, hashed_size, false);
1342 if (!l)
1343 return -ENOMEM;
1344
1345 j = strjoin(l, ".", zone, NULL);
1346 if (!j)
1347 return -ENOMEM;
1348
1349 *ret = j;
1350 return (int) hashed_size;
1351 }
1352
1353 static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
1354 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
1355 int hashed_size;
1356
1357 assert(nsec3);
1358 assert(domain);
1359 assert(zone);
1360 assert(ret);
1361
1362 hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
1363 if (hashed_size < 0)
1364 return hashed_size;
1365
1366 return nsec3_hashed_domain_format(hashed, (size_t) hashed_size, zone, ret);
1367 }
1368
1369 /* See RFC 5155, Section 8
1370 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1371 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1372 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1373 * matches the wildcard domain.
1374 *
1375 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1376 * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1377 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1378 * to conclude anything we indicate this by returning NO_RR. */
1379 static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
1380 _cleanup_free_ char *next_closer_domain = NULL, *wildcard_domain = NULL;
1381 const char *zone, *p, *pp = NULL, *wildcard;
1382 DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
1383 DnsAnswerFlags flags;
1384 int hashed_size, r;
1385 bool a, no_closer = false, no_wildcard = false, optout = false;
1386
1387 assert(key);
1388 assert(result);
1389
1390 /* First step, find the zone name and the NSEC3 parameters of the zone.
1391 * it is sufficient to look for the longest common suffix we find with
1392 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1393 * records from a given zone in a response must use the same
1394 * parameters. */
1395 zone = DNS_RESOURCE_KEY_NAME(key);
1396 for (;;) {
1397 DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
1398 r = nsec3_is_good(zone_rr, NULL);
1399 if (r < 0)
1400 return r;
1401 if (r == 0)
1402 continue;
1403
1404 r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(zone_rr->key), 1, zone);
1405 if (r < 0)
1406 return r;
1407 if (r > 0)
1408 goto found_zone;
1409 }
1410
1411 /* Strip one label from the front */
1412 r = dns_name_parent(&zone);
1413 if (r < 0)
1414 return r;
1415 if (r == 0)
1416 break;
1417 }
1418
1419 *result = DNSSEC_NSEC_NO_RR;
1420 return 0;
1421
1422 found_zone:
1423 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
1424 p = DNS_RESOURCE_KEY_NAME(key);
1425 for (;;) {
1426 _cleanup_free_ char *hashed_domain = NULL;
1427
1428 hashed_size = nsec3_hashed_domain_make(zone_rr, p, zone, &hashed_domain);
1429 if (hashed_size == -EOPNOTSUPP) {
1430 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1431 return 0;
1432 }
1433 if (hashed_size < 0)
1434 return hashed_size;
1435
1436 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
1437
1438 r = nsec3_is_good(enclosure_rr, zone_rr);
1439 if (r < 0)
1440 return r;
1441 if (r == 0)
1442 continue;
1443
1444 if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
1445 continue;
1446
1447 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
1448 if (r < 0)
1449 return r;
1450 if (r > 0) {
1451 a = flags & DNS_ANSWER_AUTHENTICATED;
1452 goto found_closest_encloser;
1453 }
1454 }
1455
1456 /* We didn't find the closest encloser with this name,
1457 * but let's remember this domain name, it might be
1458 * the next closer name */
1459
1460 pp = p;
1461
1462 /* Strip one label from the front */
1463 r = dns_name_parent(&p);
1464 if (r < 0)
1465 return r;
1466 if (r == 0)
1467 break;
1468 }
1469
1470 *result = DNSSEC_NSEC_NO_RR;
1471 return 0;
1472
1473 found_closest_encloser:
1474 /* We found a closest encloser in 'p'; next closer is 'pp' */
1475
1476 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
1477 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
1478 return -EBADMSG;
1479
1480 /* Ensure that this data is from the delegated domain
1481 * (i.e. originates from the "lower" DNS server), and isn't
1482 * just glue records (i.e. doesn't originate from the "upper"
1483 * DNS server). */
1484 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1485 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
1486 return -EBADMSG;
1487
1488 if (!pp) {
1489 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
1490 if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
1491 *result = DNSSEC_NSEC_FOUND;
1492 else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
1493 *result = DNSSEC_NSEC_CNAME;
1494 else
1495 *result = DNSSEC_NSEC_NODATA;
1496
1497 if (authenticated)
1498 *authenticated = a;
1499 if (ttl)
1500 *ttl = enclosure_rr->ttl;
1501
1502 return 0;
1503 }
1504
1505 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1506
1507 wildcard = strjoina("*.", p);
1508 r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
1509 if (r < 0)
1510 return r;
1511 if (r != hashed_size)
1512 return -EBADMSG;
1513
1514 r = nsec3_hashed_domain_make(enclosure_rr, pp, zone, &next_closer_domain);
1515 if (r < 0)
1516 return r;
1517 if (r != hashed_size)
1518 return -EBADMSG;
1519
1520 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1521 _cleanup_free_ char *next_hashed_domain = NULL;
1522
1523 r = nsec3_is_good(rr, zone_rr);
1524 if (r < 0)
1525 return r;
1526 if (r == 0)
1527 continue;
1528
1529 r = nsec3_hashed_domain_format(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, zone, &next_hashed_domain);
1530 if (r < 0)
1531 return r;
1532
1533 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1534 if (r < 0)
1535 return r;
1536 if (r > 0) {
1537 if (rr->nsec3.flags & 1)
1538 optout = true;
1539
1540 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1541
1542 no_closer = true;
1543 }
1544
1545 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
1546 if (r < 0)
1547 return r;
1548 if (r > 0) {
1549 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1550
1551 wildcard_rr = rr;
1552 }
1553
1554 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
1555 if (r < 0)
1556 return r;
1557 if (r > 0) {
1558 if (rr->nsec3.flags & 1)
1559 /* This only makes sense if we have a wildcard delegation, which is
1560 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1561 * this not happening, so hence cannot simply conclude NXDOMAIN as
1562 * we would wish */
1563 optout = true;
1564
1565 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1566
1567 no_wildcard = true;
1568 }
1569 }
1570
1571 if (wildcard_rr && no_wildcard)
1572 return -EBADMSG;
1573
1574 if (!no_closer) {
1575 *result = DNSSEC_NSEC_NO_RR;
1576 return 0;
1577 }
1578
1579 if (wildcard_rr) {
1580 /* A wildcard exists that matches our query. */
1581 if (optout)
1582 /* This is not specified in any RFC to the best of my knowledge, but
1583 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1584 * it means that we cannot prove that the source of synthesis is
1585 * correct, as there may be a closer match. */
1586 *result = DNSSEC_NSEC_OPTOUT;
1587 else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1588 *result = DNSSEC_NSEC_FOUND;
1589 else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1590 *result = DNSSEC_NSEC_CNAME;
1591 else
1592 *result = DNSSEC_NSEC_NODATA;
1593 } else {
1594 if (optout)
1595 /* The RFC only specifies that we have to care for optout for NODATA for
1596 * DS records. However, children of an insecure opt-out delegation should
1597 * also be considered opt-out, rather than verified NXDOMAIN.
1598 * Note that we do not require a proof of wildcard non-existence if the
1599 * next closer domain is covered by an opt-out, as that would not provide
1600 * any additional information. */
1601 *result = DNSSEC_NSEC_OPTOUT;
1602 else if (no_wildcard)
1603 *result = DNSSEC_NSEC_NXDOMAIN;
1604 else {
1605 *result = DNSSEC_NSEC_NO_RR;
1606
1607 return 0;
1608 }
1609 }
1610
1611 if (authenticated)
1612 *authenticated = a;
1613
1614 if (ttl)
1615 *ttl = enclosure_rr->ttl;
1616
1617 return 0;
1618 }
1619
1620 static int dnssec_nsec_test_in_path(DnsResourceRecord *rr, const char *name) {
1621 const char *nn, *common_suffix;
1622 int r;
1623
1624 assert(rr);
1625 assert(rr->key->type == DNS_TYPE_NSEC);
1626
1627 /* Checks whether the specified nsec RR indicates that name is an empty non-terminal (ENT)
1628 *
1629 * A couple of examples:
1630 *
1631 * NSEC bar → waldo.foo.bar: indicates that foo.bar exists and is an ENT
1632 * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that xoo.bar and zzz.xoo.bar exist and are ENTs
1633 * NSEC yyy.zzz.xoo.bar → bar: indicates pretty much nothing about ENTs
1634 */
1635
1636 /* First, determine parent of next domain. */
1637 nn = rr->nsec.next_domain_name;
1638 r = dns_name_parent(&nn);
1639 if (r <= 0)
1640 return r;
1641
1642 /* If the name we just determined is not equal or child of the name we are interested in, then we can't say
1643 * anything at all. */
1644 r = dns_name_endswith(nn, name);
1645 if (r <= 0)
1646 return r;
1647
1648 /* If the name we we are interested in is not a prefix of the common suffix of the NSEC RR's owner and next domain names, then we can't say anything either. */
1649 r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
1650 if (r < 0)
1651 return r;
1652
1653 return dns_name_endswith(name, common_suffix);
1654 }
1655
1656 static int dns_dnssec_test_wildcard_at_closest_encloser(DnsResourceRecord *rr, const char *name) {
1657 const char *common_suffix, *wc;
1658 int r;
1659
1660 assert(rr);
1661 assert(rr->key->type == DNS_TYPE_NSEC);
1662
1663 /* Checks whether the "Wildcard at the Closest Encloser" is within the space covered by the specified
1664 * RR. Specifically, checks whether 'name' has the common suffix of the NSEC RR's owner and next names as
1665 * suffix, and whether the NSEC covers the name generated by that suffix prepended with an asterisk label.
1666 *
1667 * NSEC bar → waldo.foo.bar: indicates that *.bar and *.foo.bar do not exist
1668 * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that *.xoo.bar and *.zzz.xoo.bar do not exist (and more ...)
1669 * NSEC yyy.zzz.xoo.bar → bar: indicates that a number of wildcards don#t exist either...
1670 */
1671
1672 r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix);
1673 if (r < 0)
1674 return r;
1675
1676 /* If the common suffix is not shared by the name we are interested in, it has nothing to say for us. */
1677 r = dns_name_endswith(name, common_suffix);
1678 if (r <= 0)
1679 return r;
1680
1681 wc = strjoina("*.", common_suffix, NULL);
1682 return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wc, rr->nsec.next_domain_name);
1683 }
1684
1685 int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
1686 bool have_nsec3 = false, covering_rr_authenticated = false, wildcard_rr_authenticated = false;
1687 DnsResourceRecord *rr, *covering_rr = NULL, *wildcard_rr = NULL;
1688 DnsAnswerFlags flags;
1689 const char *name;
1690 int r;
1691
1692 assert(key);
1693 assert(result);
1694
1695 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1696
1697 name = DNS_RESOURCE_KEY_NAME(key);
1698
1699 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1700
1701 if (rr->key->class != key->class)
1702 continue;
1703
1704 switch (rr->key->type) {
1705
1706 case DNS_TYPE_NSEC:
1707
1708 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), name);
1709 if (r < 0)
1710 return r;
1711 if (r > 0) {
1712 if (key->type == DNS_TYPE_DS) {
1713 /* If we look for a DS RR and the server sent us the NSEC RR of the child zone
1714 * we have a problem. For DS RRs we want the NSEC RR from the parent */
1715 if (bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
1716 continue;
1717 } else {
1718 /* For all RR types, ensure that if NS is set SOA is set too, so that we know
1719 * we got the child's NSEC. */
1720 if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) &&
1721 !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
1722 continue;
1723 }
1724
1725 if (bitmap_isset(rr->nsec.types, key->type))
1726 *result = DNSSEC_NSEC_FOUND;
1727 else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
1728 *result = DNSSEC_NSEC_CNAME;
1729 else
1730 *result = DNSSEC_NSEC_NODATA;
1731
1732 if (authenticated)
1733 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1734 if (ttl)
1735 *ttl = rr->ttl;
1736
1737 return 0;
1738 }
1739
1740 /* The following three checks only make sense for NSEC RRs that are not expanded from a wildcard */
1741 if (rr->n_skip_labels_source != 0)
1742 continue;
1743
1744 /* Check if the name we are looking for is an empty non-terminal within the owner or next name
1745 * of the NSEC RR. */
1746 r = dnssec_nsec_test_in_path(rr, name);
1747 if (r < 0)
1748 return r;
1749 if (r > 0) {
1750 *result = DNSSEC_NSEC_NODATA;
1751
1752 if (authenticated)
1753 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1754 if (ttl)
1755 *ttl = rr->ttl;
1756
1757 return 0;
1758 }
1759
1760 /* Check if this NSEC RR proves the absence of an explicit RR under this name */
1761 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
1762 if (r < 0)
1763 return r;
1764 if (r > 0 && (!covering_rr || !covering_rr_authenticated)) {
1765 covering_rr = rr;
1766 covering_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1767 }
1768
1769 /* Check if this NSEC RR proves the absence of a wildcard RR under this name */
1770 r = dns_dnssec_test_wildcard_at_closest_encloser(rr, name);
1771 if (r < 0)
1772 return r;
1773 if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) {
1774 wildcard_rr = rr;
1775 wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1776 }
1777
1778 break;
1779
1780 case DNS_TYPE_NSEC3:
1781 have_nsec3 = true;
1782 break;
1783 }
1784 }
1785
1786 if (covering_rr && wildcard_rr) {
1787 /* If we could prove that neither the name itself, nor the wildcard at the closest encloser exists, we
1788 * proved the NXDOMAIN case. */
1789 *result = DNSSEC_NSEC_NXDOMAIN;
1790
1791 if (authenticated)
1792 *authenticated = covering_rr_authenticated && wildcard_rr_authenticated;
1793 if (ttl)
1794 *ttl = MIN(covering_rr->ttl, wildcard_rr->ttl);
1795
1796 return 0;
1797 }
1798
1799 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1800 if (have_nsec3)
1801 return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
1802
1803 /* No approproate NSEC RR found, report this. */
1804 *result = DNSSEC_NSEC_NO_RR;
1805 return 0;
1806 }
1807
1808 int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
1809 DnsResourceRecord *rr;
1810 DnsAnswerFlags flags;
1811 int r;
1812
1813 assert(name);
1814 assert(zone);
1815
1816 /* Checks whether there's an NSEC/NSEC3 that proves that the specified 'name' is non-existing in the specified
1817 * 'zone'. The 'zone' must be a suffix of the 'name'. */
1818
1819 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1820 bool found = false;
1821
1822 if (rr->key->type != type && type != DNS_TYPE_ANY)
1823 continue;
1824
1825 switch (rr->key->type) {
1826
1827 case DNS_TYPE_NSEC:
1828
1829 /* We only care for NSEC RRs from the indicated zone */
1830 r = dns_resource_record_is_signer(rr, zone);
1831 if (r < 0)
1832 return r;
1833 if (r == 0)
1834 continue;
1835
1836 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
1837 if (r < 0)
1838 return r;
1839
1840 found = r > 0;
1841 break;
1842
1843 case DNS_TYPE_NSEC3: {
1844 _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
1845
1846 /* We only care for NSEC3 RRs from the indicated zone */
1847 r = dns_resource_record_is_signer(rr, zone);
1848 if (r < 0)
1849 return r;
1850 if (r == 0)
1851 continue;
1852
1853 r = nsec3_is_good(rr, NULL);
1854 if (r < 0)
1855 return r;
1856 if (r == 0)
1857 break;
1858
1859 /* Format the domain we are testing with the NSEC3 RR's hash function */
1860 r = nsec3_hashed_domain_make(
1861 rr,
1862 name,
1863 zone,
1864 &hashed_domain);
1865 if (r < 0)
1866 return r;
1867 if ((size_t) r != rr->nsec3.next_hashed_name_size)
1868 break;
1869
1870 /* Format the NSEC3's next hashed name as proper domain name */
1871 r = nsec3_hashed_domain_format(
1872 rr->nsec3.next_hashed_name,
1873 rr->nsec3.next_hashed_name_size,
1874 zone,
1875 &next_hashed_domain);
1876 if (r < 0)
1877 return r;
1878
1879 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain, next_hashed_domain);
1880 if (r < 0)
1881 return r;
1882
1883 found = r > 0;
1884 break;
1885 }
1886
1887 default:
1888 continue;
1889 }
1890
1891 if (found) {
1892 if (authenticated)
1893 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1894 return 1;
1895 }
1896 }
1897
1898 return 0;
1899 }
1900
1901 static int dnssec_test_positive_wildcard_nsec3(
1902 DnsAnswer *answer,
1903 const char *name,
1904 const char *source,
1905 const char *zone,
1906 bool *authenticated) {
1907
1908 const char *next_closer = NULL;
1909 int r;
1910
1911 /* Run a positive NSEC3 wildcard proof. Specifically:
1912 *
1913 * A proof that the the "next closer" of the generating wildcard does not exist.
1914 *
1915 * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for
1916 * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name
1917 * exists for the NSEC3 RR and we are done.
1918 *
1919 * To prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f all we have to check is that
1920 * c.d.e.f does not exist. */
1921
1922 for (;;) {
1923 next_closer = name;
1924 r = dns_name_parent(&name);
1925 if (r < 0)
1926 return r;
1927 if (r == 0)
1928 return 0;
1929
1930 r = dns_name_equal(name, source);
1931 if (r < 0)
1932 return r;
1933 if (r > 0)
1934 break;
1935 }
1936
1937 return dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC3, next_closer, zone, authenticated);
1938 }
1939
1940 static int dnssec_test_positive_wildcard_nsec(
1941 DnsAnswer *answer,
1942 const char *name,
1943 const char *source,
1944 const char *zone,
1945 bool *_authenticated) {
1946
1947 bool authenticated = true;
1948 int r;
1949
1950 /* Run a positive NSEC wildcard proof. Specifically:
1951 *
1952 * A proof that there's neither a wildcard name nor a non-wildcard name that is a suffix of the name "name" and
1953 * a prefix of the synthesizing source "source" in the zone "zone".
1954 *
1955 * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4
1956 *
1957 * Note that if we want to prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f, then we
1958 * have to prove that none of the following exist:
1959 *
1960 * 1) a.b.c.d.e.f
1961 * 2) *.b.c.d.e.f
1962 * 3) b.c.d.e.f
1963 * 4) *.c.d.e.f
1964 * 5) c.d.e.f
1965 *
1966 */
1967
1968 for (;;) {
1969 _cleanup_free_ char *wc = NULL;
1970 bool a = false;
1971
1972 /* Check if there's an NSEC or NSEC3 RR that proves that the mame we determined is really non-existing,
1973 * i.e between the owner name and the next name of an NSEC RR. */
1974 r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, name, zone, &a);
1975 if (r <= 0)
1976 return r;
1977
1978 authenticated = authenticated && a;
1979
1980 /* Strip one label off */
1981 r = dns_name_parent(&name);
1982 if (r <= 0)
1983 return r;
1984
1985 /* Did we reach the source of synthesis? */
1986 r = dns_name_equal(name, source);
1987 if (r < 0)
1988 return r;
1989 if (r > 0) {
1990 /* Successful exit */
1991 *_authenticated = authenticated;
1992 return 1;
1993 }
1994
1995 /* Safety check, that the source of synthesis is still our suffix */
1996 r = dns_name_endswith(name, source);
1997 if (r < 0)
1998 return r;
1999 if (r == 0)
2000 return -EBADMSG;
2001
2002 /* Replace the label we stripped off with an asterisk */
2003 wc = strappend("*.", name);
2004 if (!wc)
2005 return -ENOMEM;
2006
2007 /* And check if the proof holds for the asterisk name, too */
2008 r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, wc, zone, &a);
2009 if (r <= 0)
2010 return r;
2011
2012 authenticated = authenticated && a;
2013 /* In the next iteration we'll check the non-asterisk-prefixed version */
2014 }
2015 }
2016
2017 int dnssec_test_positive_wildcard(
2018 DnsAnswer *answer,
2019 const char *name,
2020 const char *source,
2021 const char *zone,
2022 bool *authenticated) {
2023
2024 int r;
2025
2026 assert(name);
2027 assert(source);
2028 assert(zone);
2029 assert(authenticated);
2030
2031 r = dns_answer_contains_zone_nsec3(answer, zone);
2032 if (r < 0)
2033 return r;
2034 if (r > 0)
2035 return dnssec_test_positive_wildcard_nsec3(answer, name, source, zone, authenticated);
2036 else
2037 return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated);
2038 }
2039
2040 static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
2041 [DNSSEC_VALIDATED] = "validated",
2042 [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
2043 [DNSSEC_INVALID] = "invalid",
2044 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
2045 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
2046 [DNSSEC_NO_SIGNATURE] = "no-signature",
2047 [DNSSEC_MISSING_KEY] = "missing-key",
2048 [DNSSEC_UNSIGNED] = "unsigned",
2049 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
2050 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
2051 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
2052 };
2053 DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);