]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
resolved: include GOST in list of DNSSEC algorithms
[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
160fbda9 43 * - nxdomain on qname
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
LP
79uint16_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
102static 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
ea3a892f 130static int dnssec_rsa_verify_raw(
2b442ac8
LP
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);
d12bf2bd 194 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
2b442ac8 195 r = 0;
d12bf2bd
LP
196 else if (ge != 0) {
197 log_debug("RSA signature check failed: %s", gpg_strerror(ge));
2b442ac8 198 r = -EIO;
d12bf2bd 199 } else
2b442ac8
LP
200 r = 1;
201
202finish:
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
ea3a892f
LP
220static 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
e0240c64
LP
276static 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;
348finish:
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
366static 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
2b442ac8
LP
410static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
411 gcry_md_write(md, &v, sizeof(v));
412}
413
414static 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
419static 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
2a326321
LP
424static 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)
2a44bec4 437 return -EKEYREJECTED;
2a326321 438
896c5672
LP
439 /* Permit a certain amount of clock skew of 10% of the valid
440 * time range. This takes inspiration from unbound's
441 * resolver. */
2a326321 442 skew = (expiration - inception) / 10;
896c5672
LP
443 if (skew > SKEW_MAX)
444 skew = SKEW_MAX;
2a326321
LP
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
ca994e85 459static int algorithm_to_gcrypt_md(uint8_t algorithm) {
fbf1a66d
LP
460
461 /* Translates a DNSSEC signature algorithm into a gcrypt digest identifier */
462
463 switch (algorithm) {
464
465 case DNSSEC_ALGORITHM_RSASHA1:
466 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
467 return GCRY_MD_SHA1;
468
469 case DNSSEC_ALGORITHM_RSASHA256:
e0240c64 470 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
fbf1a66d
LP
471 return GCRY_MD_SHA256;
472
e0240c64
LP
473 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
474 return GCRY_MD_SHA384;
475
fbf1a66d
LP
476 case DNSSEC_ALGORITHM_RSASHA512:
477 return GCRY_MD_SHA512;
478
479 default:
480 return -EOPNOTSUPP;
481 }
482}
483
2a326321
LP
484int dnssec_verify_rrset(
485 DnsAnswer *a,
486 DnsResourceKey *key,
487 DnsResourceRecord *rrsig,
488 DnsResourceRecord *dnskey,
547973de
LP
489 usec_t realtime,
490 DnssecResult *result) {
2a326321 491
2b442ac8 492 uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
ea3a892f
LP
493 size_t hash_size;
494 void *hash;
2b442ac8
LP
495 DnsResourceRecord **list, *rr;
496 gcry_md_hd_t md = NULL;
ca994e85 497 int r, md_algorithm;
2b442ac8 498 size_t k, n = 0;
2b442ac8
LP
499
500 assert(key);
501 assert(rrsig);
502 assert(dnskey);
547973de 503 assert(result);
2a326321
LP
504 assert(rrsig->key->type == DNS_TYPE_RRSIG);
505 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
2b442ac8
LP
506
507 /* Verifies the the RRSet matching the specified "key" in "a",
508 * using the signature "rrsig" and the key "dnskey". It's
509 * assumed the RRSIG and DNSKEY match. */
510
ca994e85
LP
511 md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
512 if (md_algorithm == -EOPNOTSUPP) {
203f1b35
LP
513 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
514 return 0;
515 }
ca994e85
LP
516 if (md_algorithm < 0)
517 return md_algorithm;
2b442ac8
LP
518
519 if (a->n_rrs > VERIFY_RRS_MAX)
520 return -E2BIG;
521
2a326321
LP
522 r = dnssec_rrsig_expired(rrsig, realtime);
523 if (r < 0)
524 return r;
547973de
LP
525 if (r > 0) {
526 *result = DNSSEC_SIGNATURE_EXPIRED;
527 return 0;
528 }
2a326321 529
2b442ac8
LP
530 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
531 list = newa(DnsResourceRecord *, a->n_rrs);
532
533 DNS_ANSWER_FOREACH(rr, a) {
534 r = dns_resource_key_equal(key, rr->key);
535 if (r < 0)
536 return r;
537 if (r == 0)
538 continue;
539
540 /* We need the wire format for ordering, and digest calculation */
541 r = dns_resource_record_to_wire_format(rr, true);
542 if (r < 0)
543 return r;
544
545 list[n++] = rr;
546 }
547
548 if (n <= 0)
549 return -ENODATA;
550
551 /* Bring the RRs into canonical order */
6c5e8fbf 552 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8
LP
553
554 /* OK, the RRs are now in canonical order. Let's calculate the digest */
fbf1a66d 555 initialize_libgcrypt();
2b442ac8 556
ca994e85 557 hash_size = gcry_md_get_algo_dlen(md_algorithm);
fbf1a66d 558 assert(hash_size > 0);
2b442ac8 559
ca994e85 560 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
561 if (!md)
562 return -EIO;
563
564 md_add_uint16(md, rrsig->rrsig.type_covered);
565 md_add_uint8(md, rrsig->rrsig.algorithm);
566 md_add_uint8(md, rrsig->rrsig.labels);
567 md_add_uint32(md, rrsig->rrsig.original_ttl);
568 md_add_uint32(md, rrsig->rrsig.expiration);
569 md_add_uint32(md, rrsig->rrsig.inception);
570 md_add_uint16(md, rrsig->rrsig.key_tag);
571
572 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
573 if (r < 0)
574 goto finish;
575 gcry_md_write(md, wire_format_name, r);
576
577 for (k = 0; k < n; k++) {
e7ff0e0b 578 const char *suffix;
2b442ac8
LP
579 size_t l;
580 rr = list[k];
581
e7ff0e0b
LP
582 r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
583 if (r < 0)
584 goto finish;
585 if (r > 0) /* This is a wildcard! */
586 gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
587
588 r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
2b442ac8
LP
589 if (r < 0)
590 goto finish;
591 gcry_md_write(md, wire_format_name, r);
592
593 md_add_uint16(md, rr->key->type);
594 md_add_uint16(md, rr->key->class);
595 md_add_uint32(md, rrsig->rrsig.original_ttl);
596
597 assert(rr->wire_format_rdata_offset <= rr->wire_format_size);
598 l = rr->wire_format_size - rr->wire_format_rdata_offset;
599 assert(l <= 0xFFFF);
600
601 md_add_uint16(md, (uint16_t) l);
602 gcry_md_write(md, (uint8_t*) rr->wire_format + rr->wire_format_rdata_offset, l);
603 }
604
605 hash = gcry_md_read(md, 0);
606 if (!hash) {
607 r = -EIO;
608 goto finish;
609 }
610
e0240c64
LP
611 switch (rrsig->rrsig.algorithm) {
612
613 case DNSSEC_ALGORITHM_RSASHA1:
614 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
615 case DNSSEC_ALGORITHM_RSASHA256:
616 case DNSSEC_ALGORITHM_RSASHA512:
617 r = dnssec_rsa_verify(
ca994e85 618 gcry_md_algo_name(md_algorithm),
e0240c64
LP
619 hash, hash_size,
620 rrsig,
621 dnskey);
622 break;
623
624 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
625 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
626 r = dnssec_ecdsa_verify(
ca994e85 627 gcry_md_algo_name(md_algorithm),
e0240c64
LP
628 rrsig->rrsig.algorithm,
629 hash, hash_size,
630 rrsig,
631 dnskey);
632 break;
633 }
634
2b442ac8
LP
635 if (r < 0)
636 goto finish;
637
547973de
LP
638 *result = r ? DNSSEC_VALIDATED : DNSSEC_INVALID;
639 r = 0;
2b442ac8
LP
640
641finish:
642 gcry_md_close(md);
643 return r;
644}
645
646int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) {
647
648 assert(rrsig);
649 assert(dnskey);
650
651 /* Checks if the specified DNSKEY RR matches the key used for
652 * the signature in the specified RRSIG RR */
653
654 if (rrsig->key->type != DNS_TYPE_RRSIG)
655 return -EINVAL;
656
657 if (dnskey->key->type != DNS_TYPE_DNSKEY)
658 return 0;
659 if (dnskey->key->class != rrsig->key->class)
660 return 0;
661 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
662 return 0;
663 if (dnskey->dnskey.protocol != 3)
664 return 0;
665 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
666 return 0;
667
668 if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag)
669 return 0;
670
15accc27 671 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
672}
673
105e1512 674int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
e7ff0e0b
LP
675 int r;
676
2b442ac8
LP
677 assert(key);
678 assert(rrsig);
679
680 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
681
682 if (rrsig->key->type != DNS_TYPE_RRSIG)
683 return 0;
684 if (rrsig->key->class != key->class)
685 return 0;
686 if (rrsig->rrsig.type_covered != key->type)
687 return 0;
688
e7ff0e0b
LP
689 /* Make sure signer is a parent of the RRset */
690 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
691 if (r <= 0)
692 return r;
693
694 /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
695 r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
696 if (r < 0)
697 return r;
698 if (r < rrsig->rrsig.labels)
699 return 0;
700
2b442ac8
LP
701 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
702}
703
ee3d6aff
LP
704static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
705 DnsResourceRecord *rr;
706 int r;
707
708 assert(key);
709 assert(rrsig);
710
711 DNS_ANSWER_FOREACH(rr, a) {
712 r = dns_resource_key_equal(key, rr->key);
713 if (r < 0)
714 return r;
715 if (r == 0)
716 continue;
717
718 /* Pick the TTL as the minimum of the RR's TTL, the
719 * RR's original TTL according to the RRSIG and the
720 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
721 rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
722 rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
723 }
724
725 return 0;
726}
727
2a326321
LP
728int dnssec_verify_rrset_search(
729 DnsAnswer *a,
730 DnsResourceKey *key,
731 DnsAnswer *validated_dnskeys,
547973de
LP
732 usec_t realtime,
733 DnssecResult *result) {
2a326321 734
203f1b35 735 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
2b442ac8
LP
736 DnsResourceRecord *rrsig;
737 int r;
738
739 assert(key);
547973de 740 assert(result);
2b442ac8 741
105e1512 742 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
2b442ac8
LP
743
744 if (!a || a->n_rrs <= 0)
745 return -ENODATA;
746
747 /* Iterate through each RRSIG RR. */
748 DNS_ANSWER_FOREACH(rrsig, a) {
749 DnsResourceRecord *dnskey;
105e1512 750 DnsAnswerFlags flags;
2b442ac8 751
203f1b35 752 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
753 r = dnssec_key_match_rrsig(key, rrsig);
754 if (r < 0)
755 return r;
756 if (r == 0)
757 continue;
758
759 found_rrsig = true;
760
547973de 761 /* Look for a matching key */
105e1512 762 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
547973de 763 DnssecResult one_result;
2b442ac8 764
105e1512
LP
765 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
766 continue;
767
203f1b35 768 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
2b442ac8
LP
769 r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
770 if (r < 0)
771 return r;
772 if (r == 0)
773 continue;
774
2a326321
LP
775 /* Take the time here, if it isn't set yet, so
776 * that we do all validations with the same
777 * time. */
778 if (realtime == USEC_INFINITY)
779 realtime = now(CLOCK_REALTIME);
780
2b442ac8
LP
781 /* Yay, we found a matching RRSIG with a matching
782 * DNSKEY, awesome. Now let's verify all entries of
783 * the RRSet against the RRSIG and DNSKEY
784 * combination. */
785
547973de 786 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 787 if (r < 0)
2b442ac8 788 return r;
203f1b35
LP
789
790 switch (one_result) {
791
792 case DNSSEC_VALIDATED:
793 /* Yay, the RR has been validated,
ee3d6aff
LP
794 * return immediately, but fix up the expiry */
795 r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
796 if (r < 0)
797 return r;
798
547973de
LP
799 *result = DNSSEC_VALIDATED;
800 return 0;
2b442ac8 801
203f1b35
LP
802 case DNSSEC_INVALID:
803 /* If the signature is invalid, let's try another
804 key and/or signature. After all they
805 key_tags and stuff are not unique, and
806 might be shared by multiple keys. */
807 found_invalid = true;
808 continue;
809
810 case DNSSEC_UNSUPPORTED_ALGORITHM:
811 /* If the key algorithm is
812 unsupported, try another
813 RRSIG/DNSKEY pair, but remember we
814 encountered this, so that we can
815 return a proper error when we
816 encounter nothing better. */
817 found_unsupported_algorithm = true;
818 continue;
819
820 case DNSSEC_SIGNATURE_EXPIRED:
821 /* If the signature is expired, try
822 another one, but remember it, so
823 that we can return this */
824 found_expired_rrsig = true;
825 continue;
826
827 default:
828 assert_not_reached("Unexpected DNSSEC validation result");
829 }
2b442ac8
LP
830 }
831 }
832
203f1b35
LP
833 if (found_expired_rrsig)
834 *result = DNSSEC_SIGNATURE_EXPIRED;
835 else if (found_unsupported_algorithm)
836 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
837 else if (found_invalid)
547973de
LP
838 *result = DNSSEC_INVALID;
839 else if (found_rrsig)
840 *result = DNSSEC_MISSING_KEY;
841 else
842 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 843
547973de 844 return 0;
2b442ac8
LP
845}
846
105e1512
LP
847int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
848 DnsResourceRecord *rr;
849 int r;
850
851 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
852
853 DNS_ANSWER_FOREACH(rr, a) {
854 r = dnssec_key_match_rrsig(key, rr);
855 if (r < 0)
856 return r;
857 if (r > 0)
858 return 1;
859 }
860
861 return 0;
862}
863
2b442ac8 864int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
2b442ac8
LP
865 size_t c = 0;
866 int r;
867
868 /* Converts the specified hostname into DNSSEC canonicalized
869 * form. */
870
871 if (buffer_max < 2)
872 return -ENOBUFS;
873
874 for (;;) {
875 size_t i;
876
877 r = dns_label_unescape(&n, buffer, buffer_max);
878 if (r < 0)
879 return r;
880 if (r == 0)
881 break;
882 if (r > 0) {
883 int k;
884
885 /* DNSSEC validation is always done on the ASCII version of the label */
886 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
887 if (k < 0)
888 return k;
889 if (k > 0)
890 r = k;
891 }
892
893 if (buffer_max < (size_t) r + 2)
894 return -ENOBUFS;
895
896 /* The DNSSEC canonical form is not clear on what to
897 * do with dots appearing in labels, the way DNS-SD
898 * does it. Refuse it for now. */
899
900 if (memchr(buffer, '.', r))
901 return -EINVAL;
902
903 for (i = 0; i < (size_t) r; i ++) {
904 if (buffer[i] >= 'A' && buffer[i] <= 'Z')
905 buffer[i] = buffer[i] - 'A' + 'a';
906 }
907
908 buffer[r] = '.';
909
910 buffer += r + 1;
911 c += r + 1;
912
913 buffer_max -= r + 1;
914 }
915
916 if (c <= 0) {
917 /* Not even a single label: this is the root domain name */
918
919 assert(buffer_max > 2);
920 buffer[0] = '.';
921 buffer[1] = 0;
922
923 return 1;
924 }
925
926 return (int) c;
927}
928
ca994e85 929static int digest_to_gcrypt_md(uint8_t algorithm) {
a1972a91 930
fbf1a66d 931 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
a1972a91
LP
932
933 switch (algorithm) {
934
935 case DNSSEC_DIGEST_SHA1:
936 return GCRY_MD_SHA1;
937
938 case DNSSEC_DIGEST_SHA256:
939 return GCRY_MD_SHA256;
940
af22c65b
LP
941 case DNSSEC_DIGEST_SHA384:
942 return GCRY_MD_SHA384;
943
a1972a91
LP
944 default:
945 return -EOPNOTSUPP;
946 }
947}
948
2b442ac8 949int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
2b442ac8 950 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
a1972a91
LP
951 gcry_md_hd_t md = NULL;
952 size_t hash_size;
ca994e85 953 int md_algorithm, r;
2b442ac8 954 void *result;
2b442ac8
LP
955
956 assert(dnskey);
957 assert(ds);
958
959 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
960
961 if (dnskey->key->type != DNS_TYPE_DNSKEY)
962 return -EINVAL;
963 if (ds->key->type != DNS_TYPE_DS)
964 return -EINVAL;
965 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
966 return -EKEYREJECTED;
967 if (dnskey->dnskey.protocol != 3)
968 return -EKEYREJECTED;
969
2b442ac8
LP
970 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
971 return 0;
972 if (dnssec_keytag(dnskey) != ds->ds.key_tag)
973 return 0;
974
0638401a
LP
975 initialize_libgcrypt();
976
ca994e85
LP
977 md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
978 if (md_algorithm < 0)
979 return md_algorithm;
2b442ac8 980
ca994e85 981 hash_size = gcry_md_get_algo_dlen(md_algorithm);
a1972a91 982 assert(hash_size > 0);
2b442ac8 983
a1972a91
LP
984 if (ds->ds.digest_size != hash_size)
985 return 0;
2b442ac8 986
a1972a91
LP
987 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
988 if (r < 0)
989 return r;
2b442ac8 990
ca994e85 991 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
992 if (!md)
993 return -EIO;
994
2b442ac8
LP
995 gcry_md_write(md, owner_name, r);
996 md_add_uint16(md, dnskey->dnskey.flags);
997 md_add_uint8(md, dnskey->dnskey.protocol);
998 md_add_uint8(md, dnskey->dnskey.algorithm);
999 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1000
1001 result = gcry_md_read(md, 0);
1002 if (!result) {
1003 r = -EIO;
1004 goto finish;
1005 }
1006
1007 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
1008
1009finish:
1010 gcry_md_close(md);
1011 return r;
1012}
24710c48 1013
547973de
LP
1014int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
1015 DnsResourceRecord *ds;
105e1512 1016 DnsAnswerFlags flags;
547973de
LP
1017 int r;
1018
1019 assert(dnskey);
1020
1021 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1022 return 0;
1023
105e1512
LP
1024 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1025
1026 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1027 continue;
547973de
LP
1028
1029 if (ds->key->type != DNS_TYPE_DS)
1030 continue;
1031
d1c4ee32
LP
1032 if (ds->key->class != dnskey->key->class)
1033 continue;
1034
1035 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
1036 if (r < 0)
1037 return r;
1038 if (r == 0)
1039 continue;
1040
547973de
LP
1041 r = dnssec_verify_dnskey(dnskey, ds);
1042 if (r < 0)
1043 return r;
1044 if (r > 0)
1045 return 1;
1046 }
1047
1048 return 0;
1049}
1050
72667f08
LP
1051int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
1052 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
1053 gcry_md_hd_t md = NULL;
1054 size_t hash_size;
1055 int algorithm;
1056 void *result;
1057 unsigned k;
1058 int r;
1059
1060 assert(nsec3);
1061 assert(name);
1062 assert(ret);
1063
1064 if (nsec3->key->type != DNS_TYPE_NSEC3)
1065 return -EINVAL;
1066
ca994e85 1067 algorithm = digest_to_gcrypt_md(nsec3->nsec3.algorithm);
72667f08
LP
1068 if (algorithm < 0)
1069 return algorithm;
1070
1071 initialize_libgcrypt();
1072
1073 hash_size = gcry_md_get_algo_dlen(algorithm);
1074 assert(hash_size > 0);
1075
1076 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1077 return -EINVAL;
1078
1079 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1080 if (r < 0)
1081 return r;
1082
1083 gcry_md_open(&md, algorithm, 0);
1084 if (!md)
1085 return -EIO;
1086
1087 gcry_md_write(md, wire_format, r);
1088 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1089
1090 result = gcry_md_read(md, 0);
1091 if (!result) {
1092 r = -EIO;
1093 goto finish;
1094 }
1095
1096 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1097 uint8_t tmp[hash_size];
1098 memcpy(tmp, result, hash_size);
1099
1100 gcry_md_reset(md);
1101 gcry_md_write(md, tmp, hash_size);
1102 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1103
1104 result = gcry_md_read(md, 0);
1105 if (!result) {
1106 r = -EIO;
1107 goto finish;
1108 }
1109 }
1110
1111 memcpy(ret, result, hash_size);
1112 r = (int) hash_size;
1113
1114finish:
1115 gcry_md_close(md);
1116 return r;
1117}
1118
db5b0e92
LP
1119static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourceRecord *nsec3) {
1120 const char *a, *b;
1121 int r;
1122
1123 assert(rr);
1124
db5b0e92
LP
1125 if (rr->key->type != DNS_TYPE_NSEC3)
1126 return 0;
1127
1128 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1129 if (!IN_SET(rr->nsec3.flags, 0, 1))
1130 return 0;
1131
1132 if (!nsec3)
1133 return 1;
1134
1135 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1136
1137 if (nsec3 == rr) /* Shortcut */
1138 return 1;
1139
1140 if (rr->key->class != nsec3->key->class)
1141 return 0;
1142 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1143 return 0;
1144 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1145 return 0;
1146 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1147 return 0;
1148 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1149 return 0;
1150
1151 a = DNS_RESOURCE_KEY_NAME(rr->key);
1152 r = dns_name_parent(&a); /* strip off hash */
1153 if (r < 0)
1154 return r;
1155 if (r == 0)
1156 return 0;
1157
1158 b = DNS_RESOURCE_KEY_NAME(nsec3->key);
1159 r = dns_name_parent(&b); /* strip off hash */
1160 if (r < 0)
1161 return r;
1162 if (r == 0)
1163 return 0;
1164
1165 return dns_name_equal(a, b);
1166}
1167
ed29bfdc 1168static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
105e1512
LP
1169 _cleanup_free_ char *next_closer_domain = NULL, *l = NULL;
1170 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
13b78323 1171 const char *suffix, *p, *pp = NULL;
db5b0e92 1172 DnsResourceRecord *rr, *suffix_rr;
105e1512
LP
1173 DnsAnswerFlags flags;
1174 int hashed_size, r;
ed29bfdc 1175 bool a;
72667f08
LP
1176
1177 assert(key);
1178 assert(result);
ed29bfdc 1179 assert(authenticated);
72667f08 1180
13b78323
LP
1181 /* First step, look for the longest common suffix we find with any NSEC3 RR in the response. */
1182 suffix = DNS_RESOURCE_KEY_NAME(key);
1183 for (;;) {
db5b0e92 1184 DNS_ANSWER_FOREACH_FLAGS(suffix_rr, flags, answer) {
db5b0e92
LP
1185 r = nsec3_is_good(suffix_rr, flags, NULL);
1186 if (r < 0)
1187 return r;
1188 if (r == 0)
13b78323
LP
1189 continue;
1190
db5b0e92 1191 r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, suffix);
13b78323
LP
1192 if (r < 0)
1193 return r;
1194 if (r > 0)
1195 goto found_suffix;
1196 }
1197
1198 /* Strip one label from the front */
1199 r = dns_name_parent(&suffix);
1200 if (r < 0)
1201 return r;
1202 if (r == 0)
1203 break;
1204 }
1205
1206 *result = DNSSEC_NSEC_NO_RR;
1207 return 0;
1208
1209found_suffix:
1210 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
105e1512
LP
1211 p = DNS_RESOURCE_KEY_NAME(key);
1212 for (;;) {
db5b0e92 1213 _cleanup_free_ char *hashed_domain = NULL, *label = NULL;
72667f08 1214
db5b0e92
LP
1215 hashed_size = dnssec_nsec3_hash(suffix_rr, p, hashed);
1216 if (hashed_size == -EOPNOTSUPP) {
1217 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1218 return 0;
1219 }
1220 if (hashed_size < 0)
1221 return hashed_size;
72667f08 1222
db5b0e92
LP
1223 label = base32hexmem(hashed, hashed_size, false);
1224 if (!label)
1225 return -ENOMEM;
72667f08 1226
db5b0e92
LP
1227 hashed_domain = strjoin(label, ".", suffix, NULL);
1228 if (!hashed_domain)
1229 return -ENOMEM;
1230
1231 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
72667f08 1232
db5b0e92 1233 r = nsec3_is_good(rr, flags, suffix_rr);
72667f08
LP
1234 if (r < 0)
1235 return r;
105e1512
LP
1236 if (r == 0)
1237 continue;
1238
105e1512 1239 if (rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
db5b0e92 1240 continue;
105e1512
LP
1241
1242 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain);
72667f08
LP
1243 if (r < 0)
1244 return r;
ed29bfdc
LP
1245 if (r > 0) {
1246 a = flags & DNS_ANSWER_AUTHENTICATED;
13b78323 1247 goto found_closest_encloser;
ed29bfdc 1248 }
105e1512
LP
1249 }
1250
1251 /* We didn't find the closest encloser with this name,
1252 * but let's remember this domain name, it might be
1253 * the next closer name */
1254
1255 pp = p;
1256
1257 /* Strip one label from the front */
1258 r = dns_name_parent(&p);
1259 if (r < 0)
1260 return r;
1261 if (r == 0)
72667f08 1262 break;
105e1512 1263 }
72667f08 1264
105e1512
LP
1265 *result = DNSSEC_NSEC_NO_RR;
1266 return 0;
72667f08 1267
13b78323 1268found_closest_encloser:
105e1512 1269 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 1270
105e1512
LP
1271 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
1272 if (bitmap_isset(rr->nsec3.types, DNS_TYPE_DNAME))
1273 return -EBADMSG;
72667f08 1274
105e1512
LP
1275 /* Ensure that this data is from the delegated domain
1276 * (i.e. originates from the "lower" DNS server), and isn't
1277 * just glue records (i.e. doesn't originate from the "upper"
1278 * DNS server). */
1279 if (bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) &&
1280 !bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA))
1281 return -EBADMSG;
72667f08 1282
105e1512
LP
1283 if (!pp) {
1284 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
1285 *result = bitmap_isset(rr->nsec3.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
ed29bfdc 1286 *authenticated = a;
105e1512
LP
1287 return 0;
1288 }
72667f08 1289
105e1512
LP
1290 r = dnssec_nsec3_hash(rr, pp, hashed);
1291 if (r < 0)
1292 return r;
1293 if (r != hashed_size)
1294 return -EBADMSG;
72667f08 1295
105e1512
LP
1296 l = base32hexmem(hashed, hashed_size, false);
1297 if (!l)
1298 return -ENOMEM;
72667f08 1299
105e1512
LP
1300 next_closer_domain = strjoin(l, ".", p, NULL);
1301 if (!next_closer_domain)
1302 return -ENOMEM;
72667f08 1303
105e1512
LP
1304 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1305 _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL;
105e1512 1306
db5b0e92 1307 r = nsec3_is_good(rr, flags, suffix_rr);
105e1512
LP
1308 if (r < 0)
1309 return r;
1310 if (r == 0)
1311 continue;
1312
1313 label = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
1314 if (!label)
1315 return -ENOMEM;
1316
1317 next_hashed_domain = strjoin(label, ".", p, NULL);
1318 if (!next_hashed_domain)
1319 return -ENOMEM;
1320
1321 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1322 if (r < 0)
1323 return r;
1324 if (r > 0) {
1325 if (rr->nsec3.flags & 1)
1326 *result = DNSSEC_NSEC_OPTOUT;
1327 else
72667f08 1328 *result = DNSSEC_NSEC_NXDOMAIN;
105e1512 1329
ed29bfdc 1330 *authenticated = a && (flags & DNS_ANSWER_AUTHENTICATED);
105e1512
LP
1331 return 1;
1332 }
1333 }
1334
1335 *result = DNSSEC_NSEC_NO_RR;
1336 return 0;
1337}
1338
ed29bfdc 1339int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
105e1512
LP
1340 DnsResourceRecord *rr;
1341 bool have_nsec3 = false;
1342 DnsAnswerFlags flags;
1343 int r;
1344
1345 assert(key);
1346 assert(result);
ed29bfdc 1347 assert(authenticated);
105e1512
LP
1348
1349 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1350
1351 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1352
1353 if (rr->key->class != key->class)
1354 continue;
1355
105e1512
LP
1356 switch (rr->key->type) {
1357
1358 case DNS_TYPE_NSEC:
1359
1360 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
1361 if (r < 0)
1362 return r;
1363 if (r > 0) {
1364 *result = bitmap_isset(rr->nsec.types, key->type) ? DNSSEC_NSEC_FOUND : DNSSEC_NSEC_NODATA;
ed29bfdc 1365 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
72667f08
LP
1366 return 0;
1367 }
1368
105e1512
LP
1369 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
1370 if (r < 0)
1371 return r;
1372 if (r > 0) {
1373 *result = DNSSEC_NSEC_NXDOMAIN;
ed29bfdc 1374 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
105e1512
LP
1375 return 0;
1376 }
72667f08 1377 break;
72667f08 1378
105e1512
LP
1379 case DNS_TYPE_NSEC3:
1380 have_nsec3 = true;
72667f08
LP
1381 break;
1382 }
1383 }
1384
105e1512
LP
1385 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1386 if (have_nsec3)
ed29bfdc 1387 return dnssec_test_nsec3(answer, key, result, authenticated);
105e1512 1388
72667f08
LP
1389 /* No approproate NSEC RR found, report this. */
1390 *result = DNSSEC_NSEC_NO_RR;
1391 return 0;
1392}
1393
24710c48
LP
1394static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
1395 [DNSSEC_NO] = "no",
b652d4a2 1396 [DNSSEC_DOWNGRADE_OK] = "downgrade-ok",
24710c48
LP
1397 [DNSSEC_YES] = "yes",
1398};
1399DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
547973de
LP
1400
1401static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
1402 [DNSSEC_VALIDATED] = "validated",
1403 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
1404 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
1405 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
1406 [DNSSEC_NO_SIGNATURE] = "no-signature",
1407 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 1408 [DNSSEC_UNSIGNED] = "unsigned",
547973de 1409 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 1410 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
b652d4a2 1411 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
547973de
LP
1412};
1413DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);