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