]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-dnssec.c
resolved: add comments referencing various RFCs to various places
[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 * - Make trust anchor store read additional DS+DNSKEY data from disk
39 * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing)
40 * - multi-label zone compatibility
41 * - cname/dname compatibility
42 * - per-interface DNSSEC setting
43 * - nxdomain on qname
44 * - retry on failed validation?
45 * - DSA support?
46 *
47 * */
48
49 #define VERIFY_RRS_MAX 256
50 #define MAX_KEY_SIZE (32*1024)
51
52 /* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
53 #define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
54
55 /*
56 * The DNSSEC Chain of trust:
57 *
58 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
59 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
60 * DS RRs are protected like normal RRs
61 *
62 * Example chain:
63 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
64 */
65
66 static void initialize_libgcrypt(void) {
67 const char *p;
68
69 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
70 return;
71
72 p = gcry_check_version("1.4.5");
73 assert(p);
74
75 gcry_control(GCRYCTL_DISABLE_SECMEM);
76 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
77 }
78
79 uint16_t dnssec_keytag(DnsResourceRecord *dnskey) {
80 const uint8_t *p;
81 uint32_t sum;
82 size_t i;
83
84 /* The algorithm from RFC 4034, Appendix B. */
85
86 assert(dnskey);
87 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
88
89 sum = (uint32_t) dnskey->dnskey.flags +
90 ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
91
92 p = dnskey->dnskey.key;
93
94 for (i = 0; i < dnskey->dnskey.key_size; i++)
95 sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
96
97 sum += (sum >> 16) & UINT32_C(0xFFFF);
98
99 return sum & UINT32_C(0xFFFF);
100 }
101
102 static int rr_compare(const void *a, const void *b) {
103 DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
104 size_t m;
105 int r;
106
107 /* Let's order the RRs according to RFC 4034, Section 6.3 */
108
109 assert(x);
110 assert(*x);
111 assert((*x)->wire_format);
112 assert(y);
113 assert(*y);
114 assert((*y)->wire_format);
115
116 m = MIN((*x)->wire_format_size, (*y)->wire_format_size);
117
118 r = memcmp((*x)->wire_format, (*y)->wire_format, m);
119 if (r != 0)
120 return r;
121
122 if ((*x)->wire_format_size < (*y)->wire_format_size)
123 return -1;
124 else if ((*x)->wire_format_size > (*y)->wire_format_size)
125 return 1;
126
127 return 0;
128 }
129
130 static int dnssec_rsa_verify_raw(
131 const char *hash_algorithm,
132 const void *signature, size_t signature_size,
133 const void *data, size_t data_size,
134 const void *exponent, size_t exponent_size,
135 const void *modulus, size_t modulus_size) {
136
137 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
138 gcry_mpi_t n = NULL, e = NULL, s = NULL;
139 gcry_error_t ge;
140 int r;
141
142 assert(hash_algorithm);
143
144 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
145 if (ge != 0) {
146 r = -EIO;
147 goto finish;
148 }
149
150 ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
151 if (ge != 0) {
152 r = -EIO;
153 goto finish;
154 }
155
156 ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
157 if (ge != 0) {
158 r = -EIO;
159 goto finish;
160 }
161
162 ge = gcry_sexp_build(&signature_sexp,
163 NULL,
164 "(sig-val (rsa (s %m)))",
165 s);
166
167 if (ge != 0) {
168 r = -EIO;
169 goto finish;
170 }
171
172 ge = gcry_sexp_build(&data_sexp,
173 NULL,
174 "(data (flags pkcs1) (hash %s %b))",
175 hash_algorithm,
176 (int) data_size,
177 data);
178 if (ge != 0) {
179 r = -EIO;
180 goto finish;
181 }
182
183 ge = gcry_sexp_build(&public_key_sexp,
184 NULL,
185 "(public-key (rsa (n %m) (e %m)))",
186 n,
187 e);
188 if (ge != 0) {
189 r = -EIO;
190 goto finish;
191 }
192
193 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
194 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
195 r = 0;
196 else if (ge != 0) {
197 log_debug("RSA signature check failed: %s", gpg_strerror(ge));
198 r = -EIO;
199 } else
200 r = 1;
201
202 finish:
203 if (e)
204 gcry_mpi_release(e);
205 if (n)
206 gcry_mpi_release(n);
207 if (s)
208 gcry_mpi_release(s);
209
210 if (public_key_sexp)
211 gcry_sexp_release(public_key_sexp);
212 if (signature_sexp)
213 gcry_sexp_release(signature_sexp);
214 if (data_sexp)
215 gcry_sexp_release(data_sexp);
216
217 return r;
218 }
219
220 static int dnssec_rsa_verify(
221 const char *hash_algorithm,
222 const void *hash, size_t hash_size,
223 DnsResourceRecord *rrsig,
224 DnsResourceRecord *dnskey) {
225
226 size_t exponent_size, modulus_size;
227 void *exponent, *modulus;
228
229 assert(hash_algorithm);
230 assert(hash);
231 assert(hash_size > 0);
232 assert(rrsig);
233 assert(dnskey);
234
235 if (*(uint8_t*) dnskey->dnskey.key == 0) {
236 /* exponent is > 255 bytes long */
237
238 exponent = (uint8_t*) dnskey->dnskey.key + 3;
239 exponent_size =
240 ((size_t) (((uint8_t*) dnskey->dnskey.key)[0]) << 8) |
241 ((size_t) ((uint8_t*) dnskey->dnskey.key)[1]);
242
243 if (exponent_size < 256)
244 return -EINVAL;
245
246 if (3 + exponent_size >= dnskey->dnskey.key_size)
247 return -EINVAL;
248
249 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
250 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
251
252 } else {
253 /* exponent is <= 255 bytes long */
254
255 exponent = (uint8_t*) dnskey->dnskey.key + 1;
256 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
257
258 if (exponent_size <= 0)
259 return -EINVAL;
260
261 if (1 + exponent_size >= dnskey->dnskey.key_size)
262 return -EINVAL;
263
264 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
265 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
266 }
267
268 return dnssec_rsa_verify_raw(
269 hash_algorithm,
270 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
271 hash, hash_size,
272 exponent, exponent_size,
273 modulus, modulus_size);
274 }
275
276 static int dnssec_ecdsa_verify_raw(
277 const char *hash_algorithm,
278 const char *curve,
279 const void *signature_r, size_t signature_r_size,
280 const void *signature_s, size_t signature_s_size,
281 const void *data, size_t data_size,
282 const void *key, size_t key_size) {
283
284 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
285 gcry_mpi_t q = NULL, r = NULL, s = NULL;
286 gcry_error_t ge;
287 int k;
288
289 assert(hash_algorithm);
290
291 ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
292 if (ge != 0) {
293 k = -EIO;
294 goto finish;
295 }
296
297 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
298 if (ge != 0) {
299 k = -EIO;
300 goto finish;
301 }
302
303 ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
304 if (ge != 0) {
305 k = -EIO;
306 goto finish;
307 }
308
309 ge = gcry_sexp_build(&signature_sexp,
310 NULL,
311 "(sig-val (ecdsa (r %m) (s %m)))",
312 r,
313 s);
314 if (ge != 0) {
315 k = -EIO;
316 goto finish;
317 }
318
319 ge = gcry_sexp_build(&data_sexp,
320 NULL,
321 "(data (flags rfc6979) (hash %s %b))",
322 hash_algorithm,
323 (int) data_size,
324 data);
325 if (ge != 0) {
326 k = -EIO;
327 goto finish;
328 }
329
330 ge = gcry_sexp_build(&public_key_sexp,
331 NULL,
332 "(public-key (ecc (curve %s) (q %m)))",
333 curve,
334 q);
335 if (ge != 0) {
336 k = -EIO;
337 goto finish;
338 }
339
340 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
341 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
342 k = 0;
343 else if (ge != 0) {
344 log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
345 k = -EIO;
346 } else
347 k = 1;
348 finish:
349 if (r)
350 gcry_mpi_release(r);
351 if (s)
352 gcry_mpi_release(s);
353 if (q)
354 gcry_mpi_release(q);
355
356 if (public_key_sexp)
357 gcry_sexp_release(public_key_sexp);
358 if (signature_sexp)
359 gcry_sexp_release(signature_sexp);
360 if (data_sexp)
361 gcry_sexp_release(data_sexp);
362
363 return k;
364 }
365
366 static int dnssec_ecdsa_verify(
367 const char *hash_algorithm,
368 int algorithm,
369 const void *hash, size_t hash_size,
370 DnsResourceRecord *rrsig,
371 DnsResourceRecord *dnskey) {
372
373 const char *curve;
374 size_t key_size;
375 uint8_t *q;
376
377 assert(hash);
378 assert(hash_size);
379 assert(rrsig);
380 assert(dnskey);
381
382 if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
383 key_size = 32;
384 curve = "NIST P-256";
385 } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
386 key_size = 48;
387 curve = "NIST P-384";
388 } else
389 return -EOPNOTSUPP;
390
391 if (dnskey->dnskey.key_size != key_size * 2)
392 return -EINVAL;
393
394 if (rrsig->rrsig.signature_size != key_size * 2)
395 return -EINVAL;
396
397 q = alloca(key_size*2 + 1);
398 q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
399 memcpy(q+1, dnskey->dnskey.key, key_size*2);
400
401 return dnssec_ecdsa_verify_raw(
402 hash_algorithm,
403 curve,
404 rrsig->rrsig.signature, key_size,
405 (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
406 hash, hash_size,
407 q, key_size*2+1);
408 }
409
410 static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
411 gcry_md_write(md, &v, sizeof(v));
412 }
413
414 static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
415 v = htobe16(v);
416 gcry_md_write(md, &v, sizeof(v));
417 }
418
419 static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
420 v = htobe32(v);
421 gcry_md_write(md, &v, sizeof(v));
422 }
423
424 static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
425 usec_t expiration, inception, skew;
426
427 assert(rrsig);
428 assert(rrsig->key->type == DNS_TYPE_RRSIG);
429
430 if (realtime == USEC_INFINITY)
431 realtime = now(CLOCK_REALTIME);
432
433 expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
434 inception = rrsig->rrsig.inception * USEC_PER_SEC;
435
436 if (inception > expiration)
437 return -EKEYREJECTED;
438
439 /* Permit a certain amount of clock skew of 10% of the valid
440 * time range. This takes inspiration from unbound's
441 * resolver. */
442 skew = (expiration - inception) / 10;
443 if (skew > SKEW_MAX)
444 skew = SKEW_MAX;
445
446 if (inception < skew)
447 inception = 0;
448 else
449 inception -= skew;
450
451 if (expiration + skew < expiration)
452 expiration = USEC_INFINITY;
453 else
454 expiration += skew;
455
456 return realtime < inception || realtime > expiration;
457 }
458
459 static int algorithm_to_gcrypt_md(uint8_t algorithm) {
460
461 /* Translates a DNSSEC signature algorithm into a gcrypt
462 * digest identifier.
463 *
464 * Note that we implement all algorithms listed as "Must
465 * implement" and "Recommended to Implement" in RFC6944. We
466 * don't implement any algorithms that are listed as
467 * "Optional" or "Must Not Implement". Specifically, we do not
468 * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
469 * GOST-ECC. */
470
471 switch (algorithm) {
472
473 case DNSSEC_ALGORITHM_RSASHA1:
474 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
475 return GCRY_MD_SHA1;
476
477 case DNSSEC_ALGORITHM_RSASHA256:
478 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
479 return GCRY_MD_SHA256;
480
481 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
482 return GCRY_MD_SHA384;
483
484 case DNSSEC_ALGORITHM_RSASHA512:
485 return GCRY_MD_SHA512;
486
487 default:
488 return -EOPNOTSUPP;
489 }
490 }
491
492 int dnssec_verify_rrset(
493 DnsAnswer *a,
494 DnsResourceKey *key,
495 DnsResourceRecord *rrsig,
496 DnsResourceRecord *dnskey,
497 usec_t realtime,
498 DnssecResult *result) {
499
500 uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
501 size_t hash_size;
502 void *hash;
503 DnsResourceRecord **list, *rr;
504 gcry_md_hd_t md = NULL;
505 int r, md_algorithm;
506 size_t k, n = 0;
507
508 assert(key);
509 assert(rrsig);
510 assert(dnskey);
511 assert(result);
512 assert(rrsig->key->type == DNS_TYPE_RRSIG);
513 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
514
515 /* Verifies the the RRSet matching the specified "key" in "a",
516 * using the signature "rrsig" and the key "dnskey". It's
517 * assumed the RRSIG and DNSKEY match. */
518
519 md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
520 if (md_algorithm == -EOPNOTSUPP) {
521 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
522 return 0;
523 }
524 if (md_algorithm < 0)
525 return md_algorithm;
526
527 if (a->n_rrs > VERIFY_RRS_MAX)
528 return -E2BIG;
529
530 r = dnssec_rrsig_expired(rrsig, realtime);
531 if (r < 0)
532 return r;
533 if (r > 0) {
534 *result = DNSSEC_SIGNATURE_EXPIRED;
535 return 0;
536 }
537
538 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
539 list = newa(DnsResourceRecord *, a->n_rrs);
540
541 DNS_ANSWER_FOREACH(rr, a) {
542 r = dns_resource_key_equal(key, rr->key);
543 if (r < 0)
544 return r;
545 if (r == 0)
546 continue;
547
548 /* We need the wire format for ordering, and digest calculation */
549 r = dns_resource_record_to_wire_format(rr, true);
550 if (r < 0)
551 return r;
552
553 list[n++] = rr;
554 }
555
556 if (n <= 0)
557 return -ENODATA;
558
559 /* Bring the RRs into canonical order */
560 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
561
562 /* OK, the RRs are now in canonical order. Let's calculate the digest */
563 initialize_libgcrypt();
564
565 hash_size = gcry_md_get_algo_dlen(md_algorithm);
566 assert(hash_size > 0);
567
568 gcry_md_open(&md, md_algorithm, 0);
569 if (!md)
570 return -EIO;
571
572 md_add_uint16(md, rrsig->rrsig.type_covered);
573 md_add_uint8(md, rrsig->rrsig.algorithm);
574 md_add_uint8(md, rrsig->rrsig.labels);
575 md_add_uint32(md, rrsig->rrsig.original_ttl);
576 md_add_uint32(md, rrsig->rrsig.expiration);
577 md_add_uint32(md, rrsig->rrsig.inception);
578 md_add_uint16(md, rrsig->rrsig.key_tag);
579
580 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
581 if (r < 0)
582 goto finish;
583 gcry_md_write(md, wire_format_name, r);
584
585 for (k = 0; k < n; k++) {
586 const char *suffix;
587 size_t l;
588 rr = list[k];
589
590 r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
591 if (r < 0)
592 goto finish;
593 if (r > 0) /* This is a wildcard! */
594 gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
595
596 r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
597 if (r < 0)
598 goto finish;
599 gcry_md_write(md, wire_format_name, r);
600
601 md_add_uint16(md, rr->key->type);
602 md_add_uint16(md, rr->key->class);
603 md_add_uint32(md, rrsig->rrsig.original_ttl);
604
605 assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
606 l = rr->wire_format_size - rr->wire_format_rdata_offset;
607 assert(l <= 0xFFFF);
608
609 md_add_uint16(md, (uint16_t) l);
610 gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l);
611 }
612
613 hash = gcry_md_read(md, 0);
614 if (!hash) {
615 r = -EIO;
616 goto finish;
617 }
618
619 switch (rrsig->rrsig.algorithm) {
620
621 case DNSSEC_ALGORITHM_RSASHA1:
622 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
623 case DNSSEC_ALGORITHM_RSASHA256:
624 case DNSSEC_ALGORITHM_RSASHA512:
625 r = dnssec_rsa_verify(
626 gcry_md_algo_name(md_algorithm),
627 hash, hash_size,
628 rrsig,
629 dnskey);
630 break;
631
632 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
633 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
634 r = dnssec_ecdsa_verify(
635 gcry_md_algo_name(md_algorithm),
636 rrsig->rrsig.algorithm,
637 hash, hash_size,
638 rrsig,
639 dnskey);
640 break;
641 }
642
643 if (r < 0)
644 goto finish;
645
646 *result = r ? DNSSEC_VALIDATED : DNSSEC_INVALID;
647 r = 0;
648
649 finish:
650 gcry_md_close(md);
651 return r;
652 }
653
654 int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) {
655
656 assert(rrsig);
657 assert(dnskey);
658
659 /* Checks if the specified DNSKEY RR matches the key used for
660 * the signature in the specified RRSIG RR */
661
662 if (rrsig->key->type != DNS_TYPE_RRSIG)
663 return -EINVAL;
664
665 if (dnskey->key->type != DNS_TYPE_DNSKEY)
666 return 0;
667 if (dnskey->key->class != rrsig->key->class)
668 return 0;
669 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
670 return 0;
671 if (dnskey->dnskey.protocol != 3)
672 return 0;
673 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
674 return 0;
675
676 if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag)
677 return 0;
678
679 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
680 }
681
682 int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
683 int r;
684
685 assert(key);
686 assert(rrsig);
687
688 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
689
690 if (rrsig->key->type != DNS_TYPE_RRSIG)
691 return 0;
692 if (rrsig->key->class != key->class)
693 return 0;
694 if (rrsig->rrsig.type_covered != key->type)
695 return 0;
696
697 /* Make sure signer is a parent of the RRset */
698 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
699 if (r <= 0)
700 return r;
701
702 /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
703 r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
704 if (r < 0)
705 return r;
706 if (r < rrsig->rrsig.labels)
707 return 0;
708
709 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
710 }
711
712 static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
713 DnsResourceRecord *rr;
714 int r;
715
716 assert(key);
717 assert(rrsig);
718
719 DNS_ANSWER_FOREACH(rr, a) {
720 r = dns_resource_key_equal(key, rr->key);
721 if (r < 0)
722 return r;
723 if (r == 0)
724 continue;
725
726 /* Pick the TTL as the minimum of the RR's TTL, the
727 * RR's original TTL according to the RRSIG and the
728 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
729 rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
730 rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
731 }
732
733 return 0;
734 }
735
736 int dnssec_verify_rrset_search(
737 DnsAnswer *a,
738 DnsResourceKey *key,
739 DnsAnswer *validated_dnskeys,
740 usec_t realtime,
741 DnssecResult *result) {
742
743 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
744 DnsResourceRecord *rrsig;
745 int r;
746
747 assert(key);
748 assert(result);
749
750 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
751
752 if (!a || a->n_rrs <= 0)
753 return -ENODATA;
754
755 /* Iterate through each RRSIG RR. */
756 DNS_ANSWER_FOREACH(rrsig, a) {
757 DnsResourceRecord *dnskey;
758 DnsAnswerFlags flags;
759
760 /* Is this an RRSIG RR that applies to RRs matching our key? */
761 r = dnssec_key_match_rrsig(key, rrsig);
762 if (r < 0)
763 return r;
764 if (r == 0)
765 continue;
766
767 found_rrsig = true;
768
769 /* Look for a matching key */
770 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
771 DnssecResult one_result;
772
773 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
774 continue;
775
776 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
777 r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
778 if (r < 0)
779 return r;
780 if (r == 0)
781 continue;
782
783 /* Take the time here, if it isn't set yet, so
784 * that we do all validations with the same
785 * time. */
786 if (realtime == USEC_INFINITY)
787 realtime = now(CLOCK_REALTIME);
788
789 /* Yay, we found a matching RRSIG with a matching
790 * DNSKEY, awesome. Now let's verify all entries of
791 * the RRSet against the RRSIG and DNSKEY
792 * combination. */
793
794 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
795 if (r < 0)
796 return r;
797
798 switch (one_result) {
799
800 case DNSSEC_VALIDATED:
801 /* Yay, the RR has been validated,
802 * return immediately, but fix up the expiry */
803 r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
804 if (r < 0)
805 return r;
806
807 *result = DNSSEC_VALIDATED;
808 return 0;
809
810 case DNSSEC_INVALID:
811 /* If the signature is invalid, let's try another
812 key and/or signature. After all they
813 key_tags and stuff are not unique, and
814 might be shared by multiple keys. */
815 found_invalid = true;
816 continue;
817
818 case DNSSEC_UNSUPPORTED_ALGORITHM:
819 /* If the key algorithm is
820 unsupported, try another
821 RRSIG/DNSKEY pair, but remember we
822 encountered this, so that we can
823 return a proper error when we
824 encounter nothing better. */
825 found_unsupported_algorithm = true;
826 continue;
827
828 case DNSSEC_SIGNATURE_EXPIRED:
829 /* If the signature is expired, try
830 another one, but remember it, so
831 that we can return this */
832 found_expired_rrsig = true;
833 continue;
834
835 default:
836 assert_not_reached("Unexpected DNSSEC validation result");
837 }
838 }
839 }
840
841 if (found_expired_rrsig)
842 *result = DNSSEC_SIGNATURE_EXPIRED;
843 else if (found_unsupported_algorithm)
844 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
845 else if (found_invalid)
846 *result = DNSSEC_INVALID;
847 else if (found_rrsig)
848 *result = DNSSEC_MISSING_KEY;
849 else
850 *result = DNSSEC_NO_SIGNATURE;
851
852 return 0;
853 }
854
855 int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
856 DnsResourceRecord *rr;
857 int r;
858
859 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
860
861 DNS_ANSWER_FOREACH(rr, a) {
862 r = dnssec_key_match_rrsig(key, rr);
863 if (r < 0)
864 return r;
865 if (r > 0)
866 return 1;
867 }
868
869 return 0;
870 }
871
872 int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
873 size_t c = 0;
874 int r;
875
876 /* Converts the specified hostname into DNSSEC canonicalized
877 * form. */
878
879 if (buffer_max < 2)
880 return -ENOBUFS;
881
882 for (;;) {
883 size_t i;
884
885 r = dns_label_unescape(&n, buffer, buffer_max);
886 if (r < 0)
887 return r;
888 if (r == 0)
889 break;
890 if (r > 0) {
891 int k;
892
893 /* DNSSEC validation is always done on the ASCII version of the label */
894 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
895 if (k < 0)
896 return k;
897 if (k > 0)
898 r = k;
899 }
900
901 if (buffer_max < (size_t) r + 2)
902 return -ENOBUFS;
903
904 /* The DNSSEC canonical form is not clear on what to
905 * do with dots appearing in labels, the way DNS-SD
906 * does it. Refuse it for now. */
907
908 if (memchr(buffer, '.', r))
909 return -EINVAL;
910
911 for (i = 0; i < (size_t) r; i ++) {
912 if (buffer[i] >= 'A' && buffer[i] <= 'Z')
913 buffer[i] = buffer[i] - 'A' + 'a';
914 }
915
916 buffer[r] = '.';
917
918 buffer += r + 1;
919 c += r + 1;
920
921 buffer_max -= r + 1;
922 }
923
924 if (c <= 0) {
925 /* Not even a single label: this is the root domain name */
926
927 assert(buffer_max > 2);
928 buffer[0] = '.';
929 buffer[1] = 0;
930
931 return 1;
932 }
933
934 return (int) c;
935 }
936
937 static int digest_to_gcrypt_md(uint8_t algorithm) {
938
939 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
940
941 switch (algorithm) {
942
943 case DNSSEC_DIGEST_SHA1:
944 return GCRY_MD_SHA1;
945
946 case DNSSEC_DIGEST_SHA256:
947 return GCRY_MD_SHA256;
948
949 case DNSSEC_DIGEST_SHA384:
950 return GCRY_MD_SHA384;
951
952 default:
953 return -EOPNOTSUPP;
954 }
955 }
956
957 int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
958 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
959 gcry_md_hd_t md = NULL;
960 size_t hash_size;
961 int md_algorithm, r;
962 void *result;
963
964 assert(dnskey);
965 assert(ds);
966
967 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
968
969 if (dnskey->key->type != DNS_TYPE_DNSKEY)
970 return -EINVAL;
971 if (ds->key->type != DNS_TYPE_DS)
972 return -EINVAL;
973 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
974 return -EKEYREJECTED;
975 if (dnskey->dnskey.protocol != 3)
976 return -EKEYREJECTED;
977
978 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
979 return 0;
980 if (dnssec_keytag(dnskey) != ds->ds.key_tag)
981 return 0;
982
983 initialize_libgcrypt();
984
985 md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
986 if (md_algorithm < 0)
987 return md_algorithm;
988
989 hash_size = gcry_md_get_algo_dlen(md_algorithm);
990 assert(hash_size > 0);
991
992 if (ds->ds.digest_size != hash_size)
993 return 0;
994
995 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
996 if (r < 0)
997 return r;
998
999 gcry_md_open(&md, md_algorithm, 0);
1000 if (!md)
1001 return -EIO;
1002
1003 gcry_md_write(md, owner_name, r);
1004 md_add_uint16(md, dnskey->dnskey.flags);
1005 md_add_uint8(md, dnskey->dnskey.protocol);
1006 md_add_uint8(md, dnskey->dnskey.algorithm);
1007 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1008
1009 result = gcry_md_read(md, 0);
1010 if (!result) {
1011 r = -EIO;
1012 goto finish;
1013 }
1014
1015 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
1016
1017 finish:
1018 gcry_md_close(md);
1019 return r;
1020 }
1021
1022 int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
1023 DnsResourceRecord *ds;
1024 DnsAnswerFlags flags;
1025 int r;
1026
1027 assert(dnskey);
1028
1029 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1030 return 0;
1031
1032 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1033
1034 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1035 continue;
1036
1037 if (ds->key->type != DNS_TYPE_DS)
1038 continue;
1039
1040 if (ds->key->class != dnskey->key->class)
1041 continue;
1042
1043 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
1044 if (r < 0)
1045 return r;
1046 if (r == 0)
1047 continue;
1048
1049 r = dnssec_verify_dnskey(dnskey, ds);
1050 if (r < 0)
1051 return r;
1052 if (r > 0)
1053 return 1;
1054 }
1055
1056 return 0;
1057 }
1058
1059 int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
1060 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
1061 gcry_md_hd_t md = NULL;
1062 size_t hash_size;
1063 int algorithm;
1064 void *result;
1065 unsigned k;
1066 int r;
1067
1068 assert(nsec3);
1069 assert(name);
1070 assert(ret);
1071
1072 if (nsec3->key->type != DNS_TYPE_NSEC3)
1073 return -EINVAL;
1074
1075 algorithm = digest_to_gcrypt_md(nsec3->nsec3.algorithm);
1076 if (algorithm < 0)
1077 return algorithm;
1078
1079 initialize_libgcrypt();
1080
1081 hash_size = gcry_md_get_algo_dlen(algorithm);
1082 assert(hash_size > 0);
1083
1084 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1085 return -EINVAL;
1086
1087 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1088 if (r < 0)
1089 return r;
1090
1091 gcry_md_open(&md, algorithm, 0);
1092 if (!md)
1093 return -EIO;
1094
1095 gcry_md_write(md, wire_format, r);
1096 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1097
1098 result = gcry_md_read(md, 0);
1099 if (!result) {
1100 r = -EIO;
1101 goto finish;
1102 }
1103
1104 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1105 uint8_t tmp[hash_size];
1106 memcpy(tmp, result, hash_size);
1107
1108 gcry_md_reset(md);
1109 gcry_md_write(md, tmp, hash_size);
1110 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1111
1112 result = gcry_md_read(md, 0);
1113 if (!result) {
1114 r = -EIO;
1115 goto finish;
1116 }
1117 }
1118
1119 memcpy(ret, result, hash_size);
1120 r = (int) hash_size;
1121
1122 finish:
1123 gcry_md_close(md);
1124 return r;
1125 }
1126
1127 static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourceRecord *nsec3) {
1128 const char *a, *b;
1129 int r;
1130
1131 assert(rr);
1132
1133 if (rr->key->type != DNS_TYPE_NSEC3)
1134 return 0;
1135
1136 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1137 if (!IN_SET(rr->nsec3.flags, 0, 1))
1138 return 0;
1139
1140 if (!nsec3)
1141 return 1;
1142
1143 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1144
1145 if (nsec3 == rr) /* Shortcut */
1146 return 1;
1147
1148 if (rr->key->class != nsec3->key->class)
1149 return 0;
1150 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1151 return 0;
1152 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1153 return 0;
1154 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1155 return 0;
1156 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1157 return 0;
1158
1159 a = DNS_RESOURCE_KEY_NAME(rr->key);
1160 r = dns_name_parent(&a); /* strip off hash */
1161 if (r < 0)
1162 return r;
1163 if (r == 0)
1164 return 0;
1165
1166 b = DNS_RESOURCE_KEY_NAME(nsec3->key);
1167 r = dns_name_parent(&b); /* strip off hash */
1168 if (r < 0)
1169 return r;
1170 if (r == 0)
1171 return 0;
1172
1173 return dns_name_equal(a, b);
1174 }
1175
1176 static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
1177 _cleanup_free_ char *next_closer_domain = NULL, *l = NULL;
1178 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
1179 const char *suffix, *p, *pp = NULL;
1180 DnsResourceRecord *rr, *suffix_rr;
1181 DnsAnswerFlags flags;
1182 int hashed_size, r;
1183 bool a;
1184
1185 assert(key);
1186 assert(result);
1187 assert(authenticated);
1188
1189 /* First step, look for the longest common suffix we find with any NSEC3 RR in the response. */
1190 suffix = DNS_RESOURCE_KEY_NAME(key);
1191 for (;;) {
1192 DNS_ANSWER_FOREACH_FLAGS(suffix_rr, flags, answer) {
1193 r = nsec3_is_good(suffix_rr, flags, NULL);
1194 if (r < 0)
1195 return r;
1196 if (r == 0)
1197 continue;
1198
1199 r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, suffix);
1200 if (r < 0)
1201 return r;
1202 if (r > 0)
1203 goto found_suffix;
1204 }
1205
1206 /* Strip one label from the front */
1207 r = dns_name_parent(&suffix);
1208 if (r < 0)
1209 return r;
1210 if (r == 0)
1211 break;
1212 }
1213
1214 *result = DNSSEC_NSEC_NO_RR;
1215 return 0;
1216
1217 found_suffix:
1218 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
1219 p = DNS_RESOURCE_KEY_NAME(key);
1220 for (;;) {
1221 _cleanup_free_ char *hashed_domain = NULL, *label = NULL;
1222
1223 hashed_size = dnssec_nsec3_hash(suffix_rr, p, hashed);
1224 if (hashed_size == -EOPNOTSUPP) {
1225 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1226 return 0;
1227 }
1228 if (hashed_size < 0)
1229 return hashed_size;
1230
1231 label = base32hexmem(hashed, hashed_size, false);
1232 if (!label)
1233 return -ENOMEM;
1234
1235 hashed_domain = strjoin(label, ".", suffix, NULL);
1236 if (!hashed_domain)
1237 return -ENOMEM;
1238
1239 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1240
1241 r = nsec3_is_good(rr, flags, suffix_rr);
1242 if (r < 0)
1243 return r;
1244 if (r == 0)
1245 continue;
1246
1247 if (rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
1248 continue;
1249
1250 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain);
1251 if (r < 0)
1252 return r;
1253 if (r > 0) {
1254 a = flags & DNS_ANSWER_AUTHENTICATED;
1255 goto found_closest_encloser;
1256 }
1257 }
1258
1259 /* We didn't find the closest encloser with this name,
1260 * but let's remember this domain name, it might be
1261 * the next closer name */
1262
1263 pp = p;
1264
1265 /* Strip one label from the front */
1266 r = dns_name_parent(&p);
1267 if (r < 0)
1268 return r;
1269 if (r == 0)
1270 break;
1271 }
1272
1273 *result = DNSSEC_NSEC_NO_RR;
1274 return 0;
1275
1276 found_closest_encloser:
1277 /* We found a closest encloser in 'p'; next closer is 'pp' */
1278
1279 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
1280 if (bitmap_isset(rr->nsec3.types, DNS_TYPE_DNAME))
1281 return -EBADMSG;
1282
1283 /* Ensure that this data is from the delegated domain
1284 * (i.e. originates from the "lower" DNS server), and isn't
1285 * just glue records (i.e. doesn't originate from the "upper"
1286 * DNS server). */
1287 if (bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) &&
1288 !bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA))
1289 return -EBADMSG;
1290
1291 if (!pp) {
1292 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
1293 *result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
1294 *authenticated = a;
1295 return 0;
1296 }
1297
1298 r = dnssec_nsec3_hash(rr, pp, hashed);
1299 if (r < 0)
1300 return r;
1301 if (r != hashed_size)
1302 return -EBADMSG;
1303
1304 l = base32hexmem(hashed, hashed_size, false);
1305 if (!l)
1306 return -ENOMEM;
1307
1308 next_closer_domain = strjoin(l, ".", p, NULL);
1309 if (!next_closer_domain)
1310 return -ENOMEM;
1311
1312 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1313 _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL;
1314
1315 r = nsec3_is_good(rr, flags, suffix_rr);
1316 if (r < 0)
1317 return r;
1318 if (r == 0)
1319 continue;
1320
1321 label = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
1322 if (!label)
1323 return -ENOMEM;
1324
1325 next_hashed_domain = strjoin(label, ".", p, NULL);
1326 if (!next_hashed_domain)
1327 return -ENOMEM;
1328
1329 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1330 if (r < 0)
1331 return r;
1332 if (r > 0) {
1333 if (rr->nsec3.flags & 1)
1334 *result = DNSSEC_NSEC_OPTOUT;
1335 else
1336 *result = DNSSEC_NSEC_NXDOMAIN;
1337
1338 *authenticated = a && (flags & DNS_ANSWER_AUTHENTICATED);
1339 return 1;
1340 }
1341 }
1342
1343 *result = DNSSEC_NSEC_NO_RR;
1344 return 0;
1345 }
1346
1347 int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
1348 DnsResourceRecord *rr;
1349 bool have_nsec3 = false;
1350 DnsAnswerFlags flags;
1351 int r;
1352
1353 assert(key);
1354 assert(result);
1355 assert(authenticated);
1356
1357 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1358
1359 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1360
1361 if (rr->key->class != key->class)
1362 continue;
1363
1364 switch (rr->key->type) {
1365
1366 case DNS_TYPE_NSEC:
1367
1368 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
1369 if (r < 0)
1370 return r;
1371 if (r > 0) {
1372 *result = bitmap_isset(rr->nsec.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
1373 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1374 return 0;
1375 }
1376
1377 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
1378 if (r < 0)
1379 return r;
1380 if (r > 0) {
1381 *result = DNSSEC_NSEC_NXDOMAIN;
1382 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1383 return 0;
1384 }
1385 break;
1386
1387 case DNS_TYPE_NSEC3:
1388 have_nsec3 = true;
1389 break;
1390 }
1391 }
1392
1393 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1394 if (have_nsec3)
1395 return dnssec_test_nsec3(answer, key, result, authenticated);
1396
1397 /* No approproate NSEC RR found, report this. */
1398 *result = DNSSEC_NSEC_NO_RR;
1399 return 0;
1400 }
1401
1402 static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
1403 [DNSSEC_NO] = "no",
1404 [DNSSEC_DOWNGRADE_OK] = "downgrade-ok",
1405 [DNSSEC_YES] = "yes",
1406 };
1407 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
1408
1409 static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
1410 [DNSSEC_VALIDATED] = "validated",
1411 [DNSSEC_INVALID] = "invalid",
1412 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
1413 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
1414 [DNSSEC_NO_SIGNATURE] = "no-signature",
1415 [DNSSEC_MISSING_KEY] = "missing-key",
1416 [DNSSEC_UNSIGNED] = "unsigned",
1417 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
1418 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
1419 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
1420 };
1421 DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);