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