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