]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
resolved: actually make use of message ID when logging about failed DNSSEC validation
[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;
28b8191e
LP
674 if ((dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
675 return 0;
2b442ac8
LP
676 if (dnskey->dnskey.protocol != 3)
677 return 0;
678 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
679 return 0;
680
681 if (dnssec_keytag(dnskey) != rrsig->rrsig.key_tag)
682 return 0;
683
15accc27 684 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
685}
686
105e1512 687int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
e7ff0e0b
LP
688 int r;
689
2b442ac8
LP
690 assert(key);
691 assert(rrsig);
692
693 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
694
695 if (rrsig->key->type != DNS_TYPE_RRSIG)
696 return 0;
697 if (rrsig->key->class != key->class)
698 return 0;
699 if (rrsig->rrsig.type_covered != key->type)
700 return 0;
701
e7ff0e0b
LP
702 /* Make sure signer is a parent of the RRset */
703 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
704 if (r <= 0)
705 return r;
706
707 /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
708 r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
709 if (r < 0)
710 return r;
711 if (r < rrsig->rrsig.labels)
712 return 0;
713
2b442ac8
LP
714 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
715}
716
ee3d6aff
LP
717static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
718 DnsResourceRecord *rr;
719 int r;
720
721 assert(key);
722 assert(rrsig);
723
724 DNS_ANSWER_FOREACH(rr, a) {
725 r = dns_resource_key_equal(key, rr->key);
726 if (r < 0)
727 return r;
728 if (r == 0)
729 continue;
730
731 /* Pick the TTL as the minimum of the RR's TTL, the
732 * RR's original TTL according to the RRSIG and the
733 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
734 rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
735 rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
736 }
737
738 return 0;
739}
740
2a326321
LP
741int dnssec_verify_rrset_search(
742 DnsAnswer *a,
743 DnsResourceKey *key,
744 DnsAnswer *validated_dnskeys,
547973de
LP
745 usec_t realtime,
746 DnssecResult *result) {
2a326321 747
203f1b35 748 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
2b442ac8
LP
749 DnsResourceRecord *rrsig;
750 int r;
751
752 assert(key);
547973de 753 assert(result);
2b442ac8 754
105e1512 755 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
2b442ac8
LP
756
757 if (!a || a->n_rrs <= 0)
758 return -ENODATA;
759
760 /* Iterate through each RRSIG RR. */
761 DNS_ANSWER_FOREACH(rrsig, a) {
762 DnsResourceRecord *dnskey;
105e1512 763 DnsAnswerFlags flags;
2b442ac8 764
203f1b35 765 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
766 r = dnssec_key_match_rrsig(key, rrsig);
767 if (r < 0)
768 return r;
769 if (r == 0)
770 continue;
771
772 found_rrsig = true;
773
547973de 774 /* Look for a matching key */
105e1512 775 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
547973de 776 DnssecResult one_result;
2b442ac8 777
105e1512
LP
778 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
779 continue;
780
203f1b35 781 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
2b442ac8
LP
782 r = dnssec_rrsig_match_dnskey(rrsig, dnskey);
783 if (r < 0)
784 return r;
785 if (r == 0)
786 continue;
787
2a326321
LP
788 /* Take the time here, if it isn't set yet, so
789 * that we do all validations with the same
790 * time. */
791 if (realtime == USEC_INFINITY)
792 realtime = now(CLOCK_REALTIME);
793
2b442ac8
LP
794 /* Yay, we found a matching RRSIG with a matching
795 * DNSKEY, awesome. Now let's verify all entries of
796 * the RRSet against the RRSIG and DNSKEY
797 * combination. */
798
547973de 799 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 800 if (r < 0)
2b442ac8 801 return r;
203f1b35
LP
802
803 switch (one_result) {
804
805 case DNSSEC_VALIDATED:
806 /* Yay, the RR has been validated,
ee3d6aff
LP
807 * return immediately, but fix up the expiry */
808 r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
809 if (r < 0)
810 return r;
811
547973de
LP
812 *result = DNSSEC_VALIDATED;
813 return 0;
2b442ac8 814
203f1b35
LP
815 case DNSSEC_INVALID:
816 /* If the signature is invalid, let's try another
817 key and/or signature. After all they
818 key_tags and stuff are not unique, and
819 might be shared by multiple keys. */
820 found_invalid = true;
821 continue;
822
823 case DNSSEC_UNSUPPORTED_ALGORITHM:
824 /* If the key algorithm is
825 unsupported, try another
826 RRSIG/DNSKEY pair, but remember we
827 encountered this, so that we can
828 return a proper error when we
829 encounter nothing better. */
830 found_unsupported_algorithm = true;
831 continue;
832
833 case DNSSEC_SIGNATURE_EXPIRED:
834 /* If the signature is expired, try
835 another one, but remember it, so
836 that we can return this */
837 found_expired_rrsig = true;
838 continue;
839
840 default:
841 assert_not_reached("Unexpected DNSSEC validation result");
842 }
2b442ac8
LP
843 }
844 }
845
203f1b35
LP
846 if (found_expired_rrsig)
847 *result = DNSSEC_SIGNATURE_EXPIRED;
848 else if (found_unsupported_algorithm)
849 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
850 else if (found_invalid)
547973de
LP
851 *result = DNSSEC_INVALID;
852 else if (found_rrsig)
853 *result = DNSSEC_MISSING_KEY;
854 else
855 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 856
547973de 857 return 0;
2b442ac8
LP
858}
859
105e1512
LP
860int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
861 DnsResourceRecord *rr;
862 int r;
863
864 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
865
866 DNS_ANSWER_FOREACH(rr, a) {
867 r = dnssec_key_match_rrsig(key, rr);
868 if (r < 0)
869 return r;
870 if (r > 0)
871 return 1;
872 }
873
874 return 0;
875}
876
2b442ac8 877int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
2b442ac8
LP
878 size_t c = 0;
879 int r;
880
881 /* Converts the specified hostname into DNSSEC canonicalized
882 * form. */
883
884 if (buffer_max < 2)
885 return -ENOBUFS;
886
887 for (;;) {
888 size_t i;
889
890 r = dns_label_unescape(&n, buffer, buffer_max);
891 if (r < 0)
892 return r;
893 if (r == 0)
894 break;
895 if (r > 0) {
896 int k;
897
898 /* DNSSEC validation is always done on the ASCII version of the label */
899 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
900 if (k < 0)
901 return k;
902 if (k > 0)
903 r = k;
904 }
905
906 if (buffer_max < (size_t) r + 2)
907 return -ENOBUFS;
908
909 /* The DNSSEC canonical form is not clear on what to
910 * do with dots appearing in labels, the way DNS-SD
911 * does it. Refuse it for now. */
912
913 if (memchr(buffer, '.', r))
914 return -EINVAL;
915
916 for (i = 0; i < (size_t) r; i ++) {
917 if (buffer[i] >= 'A' && buffer[i] <= 'Z')
918 buffer[i] = buffer[i] - 'A' + 'a';
919 }
920
921 buffer[r] = '.';
922
923 buffer += r + 1;
924 c += r + 1;
925
926 buffer_max -= r + 1;
927 }
928
929 if (c <= 0) {
930 /* Not even a single label: this is the root domain name */
931
932 assert(buffer_max > 2);
933 buffer[0] = '.';
934 buffer[1] = 0;
935
936 return 1;
937 }
938
939 return (int) c;
940}
941
ca994e85 942static int digest_to_gcrypt_md(uint8_t algorithm) {
a1972a91 943
fbf1a66d 944 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
a1972a91
LP
945
946 switch (algorithm) {
947
948 case DNSSEC_DIGEST_SHA1:
949 return GCRY_MD_SHA1;
950
951 case DNSSEC_DIGEST_SHA256:
952 return GCRY_MD_SHA256;
953
af22c65b
LP
954 case DNSSEC_DIGEST_SHA384:
955 return GCRY_MD_SHA384;
956
a1972a91
LP
957 default:
958 return -EOPNOTSUPP;
959 }
960}
961
2b442ac8 962int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds) {
2b442ac8 963 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
a1972a91
LP
964 gcry_md_hd_t md = NULL;
965 size_t hash_size;
ca994e85 966 int md_algorithm, r;
2b442ac8 967 void *result;
2b442ac8
LP
968
969 assert(dnskey);
970 assert(ds);
971
972 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
973
974 if (dnskey->key->type != DNS_TYPE_DNSKEY)
975 return -EINVAL;
976 if (ds->key->type != DNS_TYPE_DS)
977 return -EINVAL;
978 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
979 return -EKEYREJECTED;
980 if (dnskey->dnskey.protocol != 3)
981 return -EKEYREJECTED;
982
2b442ac8
LP
983 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
984 return 0;
985 if (dnssec_keytag(dnskey) != ds->ds.key_tag)
986 return 0;
987
0638401a
LP
988 initialize_libgcrypt();
989
ca994e85
LP
990 md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
991 if (md_algorithm < 0)
992 return md_algorithm;
2b442ac8 993
ca994e85 994 hash_size = gcry_md_get_algo_dlen(md_algorithm);
a1972a91 995 assert(hash_size > 0);
2b442ac8 996
a1972a91
LP
997 if (ds->ds.digest_size != hash_size)
998 return 0;
2b442ac8 999
a1972a91
LP
1000 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
1001 if (r < 0)
1002 return r;
2b442ac8 1003
ca994e85 1004 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
1005 if (!md)
1006 return -EIO;
1007
2b442ac8
LP
1008 gcry_md_write(md, owner_name, r);
1009 md_add_uint16(md, dnskey->dnskey.flags);
1010 md_add_uint8(md, dnskey->dnskey.protocol);
1011 md_add_uint8(md, dnskey->dnskey.algorithm);
1012 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1013
1014 result = gcry_md_read(md, 0);
1015 if (!result) {
1016 r = -EIO;
1017 goto finish;
1018 }
1019
1020 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
1021
1022finish:
1023 gcry_md_close(md);
1024 return r;
1025}
24710c48 1026
547973de
LP
1027int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
1028 DnsResourceRecord *ds;
105e1512 1029 DnsAnswerFlags flags;
547973de
LP
1030 int r;
1031
1032 assert(dnskey);
1033
1034 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1035 return 0;
1036
105e1512
LP
1037 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1038
1039 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1040 continue;
547973de
LP
1041
1042 if (ds->key->type != DNS_TYPE_DS)
1043 continue;
1044
d1c4ee32
LP
1045 if (ds->key->class != dnskey->key->class)
1046 continue;
1047
1048 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
1049 if (r < 0)
1050 return r;
1051 if (r == 0)
1052 continue;
1053
547973de
LP
1054 r = dnssec_verify_dnskey(dnskey, ds);
1055 if (r < 0)
1056 return r;
1057 if (r > 0)
1058 return 1;
1059 }
1060
1061 return 0;
1062}
1063
d15ad742
LP
1064static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
1065
1066 /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
1067
1068 switch (algorithm) {
1069
1070 case NSEC3_ALGORITHM_SHA1:
1071 return GCRY_MD_SHA1;
1072
1073 default:
1074 return -EOPNOTSUPP;
1075 }
1076}
1077
1d3db294 1078int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
72667f08
LP
1079 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
1080 gcry_md_hd_t md = NULL;
1081 size_t hash_size;
1082 int algorithm;
1083 void *result;
1084 unsigned k;
1085 int r;
1086
1087 assert(nsec3);
1088 assert(name);
1089 assert(ret);
1090
1091 if (nsec3->key->type != DNS_TYPE_NSEC3)
1092 return -EINVAL;
1093
1d3db294
LP
1094 if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
1095 log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
a8f158b9 1096 return -EOPNOTSUPP;
1d3db294 1097 }
a8f158b9 1098
d15ad742 1099 algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
72667f08
LP
1100 if (algorithm < 0)
1101 return algorithm;
1102
1103 initialize_libgcrypt();
1104
1105 hash_size = gcry_md_get_algo_dlen(algorithm);
1106 assert(hash_size > 0);
1107
1108 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1109 return -EINVAL;
1110
1111 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1112 if (r < 0)
1113 return r;
1114
1115 gcry_md_open(&md, algorithm, 0);
1116 if (!md)
1117 return -EIO;
1118
1119 gcry_md_write(md, wire_format, r);
1120 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1121
1122 result = gcry_md_read(md, 0);
1123 if (!result) {
1124 r = -EIO;
1125 goto finish;
1126 }
1127
1128 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1129 uint8_t tmp[hash_size];
1130 memcpy(tmp, result, hash_size);
1131
1132 gcry_md_reset(md);
1133 gcry_md_write(md, tmp, hash_size);
1134 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1135
1136 result = gcry_md_read(md, 0);
1137 if (!result) {
1138 r = -EIO;
1139 goto finish;
1140 }
1141 }
1142
1143 memcpy(ret, result, hash_size);
1144 r = (int) hash_size;
1145
1146finish:
1147 gcry_md_close(md);
1148 return r;
1149}
1150
db5b0e92
LP
1151static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourceRecord *nsec3) {
1152 const char *a, *b;
1153 int r;
1154
1155 assert(rr);
1156
db5b0e92
LP
1157 if (rr->key->type != DNS_TYPE_NSEC3)
1158 return 0;
1159
1160 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1161 if (!IN_SET(rr->nsec3.flags, 0, 1))
1162 return 0;
1163
d15ad742
LP
1164 /* Ignore NSEC3 RRs whose algorithm we don't know */
1165 if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
1166 return 0;
a8f158b9
LP
1167 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1168 if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1169 return 0;
d15ad742 1170
db5b0e92
LP
1171 if (!nsec3)
1172 return 1;
1173
1174 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1175
1176 if (nsec3 == rr) /* Shortcut */
1177 return 1;
1178
1179 if (rr->key->class != nsec3->key->class)
1180 return 0;
1181 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1182 return 0;
1183 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1184 return 0;
1185 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1186 return 0;
1187 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1188 return 0;
1189
1190 a = DNS_RESOURCE_KEY_NAME(rr->key);
1191 r = dns_name_parent(&a); /* strip off hash */
1192 if (r < 0)
1193 return r;
1194 if (r == 0)
1195 return 0;
1196
1197 b = DNS_RESOURCE_KEY_NAME(nsec3->key);
1198 r = dns_name_parent(&b); /* strip off hash */
1199 if (r < 0)
1200 return r;
1201 if (r == 0)
1202 return 0;
1203
1204 return dns_name_equal(a, b);
1205}
1206
1d3db294 1207static int nsec3_hashed_domain(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
6f76ec5a 1208 _cleanup_free_ char *l = NULL, *hashed_domain = NULL;
105e1512 1209 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
6f76ec5a
TG
1210 int hashed_size;
1211
1212 assert(nsec3);
1213 assert(domain);
1214 assert(zone);
1215 assert(ret);
1216
1217 hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
1218 if (hashed_size < 0)
1219 return hashed_size;
1220
1221 l = base32hexmem(hashed, hashed_size, false);
1222 if (!l)
1223 return -ENOMEM;
1224
1225 hashed_domain = strjoin(l, ".", zone, NULL);
1226 if (!hashed_domain)
1227 return -ENOMEM;
1228
1229 *ret = hashed_domain;
1230 hashed_domain = NULL;
1231
1232 return hashed_size;
1233}
1234
35ad41d3
TG
1235/* See RFC 5155, Section 8
1236 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1237 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1238 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1239 * matches the wildcard domain.
1240 *
1241 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1242 * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1243 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1244 * to conclude anything we indicate this by returning NO_RR. */
ed29bfdc 1245static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
35ad41d3 1246 _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL;
d1511b33 1247 const char *zone, *p, *pp = NULL;
35ad41d3 1248 DnsResourceRecord *rr, *enclosure_rr, *suffix_rr, *wildcard_rr = NULL;
105e1512
LP
1249 DnsAnswerFlags flags;
1250 int hashed_size, r;
35ad41d3 1251 bool a, no_closer = false, no_wildcard = false, optout = false;
72667f08
LP
1252
1253 assert(key);
1254 assert(result);
ed29bfdc 1255 assert(authenticated);
72667f08 1256
d1511b33
TG
1257 /* First step, find the zone name and the NSEC3 parameters of the zone.
1258 * it is sufficient to look for the longest common suffix we find with
1259 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1260 * records from a given zone in a response must use the same
1261 * parameters. */
1262 zone = DNS_RESOURCE_KEY_NAME(key);
13b78323 1263 for (;;) {
db5b0e92 1264 DNS_ANSWER_FOREACH_FLAGS(suffix_rr, flags, answer) {
db5b0e92
LP
1265 r = nsec3_is_good(suffix_rr, flags, NULL);
1266 if (r < 0)
1267 return r;
1268 if (r == 0)
13b78323
LP
1269 continue;
1270
d1511b33 1271 r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, zone);
13b78323
LP
1272 if (r < 0)
1273 return r;
1274 if (r > 0)
d1511b33 1275 goto found_zone;
13b78323
LP
1276 }
1277
1278 /* Strip one label from the front */
d1511b33 1279 r = dns_name_parent(&zone);
13b78323
LP
1280 if (r < 0)
1281 return r;
1282 if (r == 0)
1283 break;
1284 }
1285
1286 *result = DNSSEC_NSEC_NO_RR;
1287 return 0;
1288
d1511b33 1289found_zone:
13b78323 1290 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
105e1512
LP
1291 p = DNS_RESOURCE_KEY_NAME(key);
1292 for (;;) {
6f76ec5a 1293 _cleanup_free_ char *hashed_domain = NULL;
72667f08 1294
6f76ec5a 1295 hashed_size = nsec3_hashed_domain(suffix_rr, p, zone, &hashed_domain);
db5b0e92
LP
1296 if (hashed_size == -EOPNOTSUPP) {
1297 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1298 return 0;
1299 }
1300 if (hashed_size < 0)
1301 return hashed_size;
72667f08 1302
d1511b33 1303 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
db5b0e92 1304
d1511b33 1305 r = nsec3_is_good(enclosure_rr, flags, suffix_rr);
72667f08
LP
1306 if (r < 0)
1307 return r;
105e1512
LP
1308 if (r == 0)
1309 continue;
1310
d1511b33 1311 if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
db5b0e92 1312 continue;
105e1512 1313
d1511b33 1314 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
72667f08
LP
1315 if (r < 0)
1316 return r;
ed29bfdc
LP
1317 if (r > 0) {
1318 a = flags & DNS_ANSWER_AUTHENTICATED;
13b78323 1319 goto found_closest_encloser;
ed29bfdc 1320 }
105e1512
LP
1321 }
1322
1323 /* We didn't find the closest encloser with this name,
1324 * but let's remember this domain name, it might be
1325 * the next closer name */
1326
1327 pp = p;
1328
1329 /* Strip one label from the front */
1330 r = dns_name_parent(&p);
1331 if (r < 0)
1332 return r;
1333 if (r == 0)
72667f08 1334 break;
105e1512 1335 }
72667f08 1336
105e1512
LP
1337 *result = DNSSEC_NSEC_NO_RR;
1338 return 0;
72667f08 1339
13b78323 1340found_closest_encloser:
105e1512 1341 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 1342
105e1512 1343 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
d1511b33 1344 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
105e1512 1345 return -EBADMSG;
72667f08 1346
105e1512
LP
1347 /* Ensure that this data is from the delegated domain
1348 * (i.e. originates from the "lower" DNS server), and isn't
1349 * just glue records (i.e. doesn't originate from the "upper"
1350 * DNS server). */
d1511b33
TG
1351 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1352 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
105e1512 1353 return -EBADMSG;
72667f08 1354
105e1512
LP
1355 if (!pp) {
1356 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
146035b3
TG
1357 if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
1358 *result = DNSSEC_NSEC_FOUND;
1359 else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
1360 *result = DNSSEC_NSEC_CNAME;
1361 else
1362 *result = DNSSEC_NSEC_NODATA;
1363
ed29bfdc 1364 *authenticated = a;
146035b3 1365
105e1512
LP
1366 return 0;
1367 }
72667f08 1368
35ad41d3
TG
1369 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1370
1371 wildcard = strappend("*.", p);
1372 if (!wildcard)
1373 return -ENOMEM;
1374
1375 r = nsec3_hashed_domain(enclosure_rr, wildcard, zone, &wildcard_domain);
105e1512
LP
1376 if (r < 0)
1377 return r;
1378 if (r != hashed_size)
1379 return -EBADMSG;
72667f08 1380
6f76ec5a 1381 r = nsec3_hashed_domain(enclosure_rr, pp, zone, &next_closer_domain);
105e1512
LP
1382 if (r < 0)
1383 return r;
1384 if (r != hashed_size)
1385 return -EBADMSG;
72667f08 1386
105e1512
LP
1387 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1388 _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL;
105e1512 1389
db5b0e92 1390 r = nsec3_is_good(rr, flags, suffix_rr);
105e1512
LP
1391 if (r < 0)
1392 return r;
1393 if (r == 0)
1394 continue;
1395
1396 label = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
1397 if (!label)
1398 return -ENOMEM;
1399
b2c2a1b9 1400 next_hashed_domain = strjoin(label, ".", zone, NULL);
105e1512
LP
1401 if (!next_hashed_domain)
1402 return -ENOMEM;
1403
1404 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1405 if (r < 0)
1406 return r;
1407 if (r > 0) {
1408 if (rr->nsec3.flags & 1)
35ad41d3 1409 optout = true;
105e1512 1410
35ad41d3
TG
1411 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1412
1413 no_closer = true;
1414 }
1415
1416 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
1417 if (r < 0)
1418 return r;
1419 if (r > 0) {
1420 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1421
1422 wildcard_rr = rr;
1423 }
1424
1425 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
1426 if (r < 0)
1427 return r;
1428 if (r > 0) {
1429 if (rr->nsec3.flags & 1)
1430 /* This only makes sense if we have a wildcard delegation, which is
1431 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1432 * this not happening, so hence cannot simply conclude NXDOMAIN as
1433 * we would wish */
1434 optout = true;
1435
1436 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1437
1438 no_wildcard = true;
105e1512
LP
1439 }
1440 }
1441
35ad41d3
TG
1442 if (wildcard_rr && no_wildcard)
1443 return -EBADMSG;
1444
1445 if (!no_closer) {
1446 *result = DNSSEC_NSEC_NO_RR;
1447
1448 return 0;
1449 }
1450
1451 if (wildcard_rr) {
1452 /* A wildcard exists that matches our query. */
1453 if (optout)
1454 /* This is not specified in any RFC to the best of my knowledge, but
1455 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1456 * it means that we cannot prove that the source of synthesis is
1457 * correct, as there may be a closer match. */
1458 *result = DNSSEC_NSEC_OPTOUT;
1459 else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1460 *result = DNSSEC_NSEC_FOUND;
1461 else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1462 *result = DNSSEC_NSEC_CNAME;
1463 else
1464 *result = DNSSEC_NSEC_NODATA;
1465 } else {
1466 if (optout)
1467 /* The RFC only specifies that we have to care for optout for NODATA for
1468 * DS records. However, children of an insecure opt-out delegation should
1469 * also be considered opt-out, rather than verified NXDOMAIN.
1470 * Note that we do not require a proof of wildcard non-existence if the
1471 * next closer domain is covered by an opt-out, as that would not provide
1472 * any additional information. */
1473 *result = DNSSEC_NSEC_OPTOUT;
1474 else if (no_wildcard)
1475 *result = DNSSEC_NSEC_NXDOMAIN;
1476 else {
1477 *result = DNSSEC_NSEC_NO_RR;
1478
1479 return 0;
1480 }
1481 }
1482
1483 *authenticated = a;
1484
105e1512
LP
1485 return 0;
1486}
1487
ed29bfdc 1488int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
105e1512
LP
1489 DnsResourceRecord *rr;
1490 bool have_nsec3 = false;
1491 DnsAnswerFlags flags;
1492 int r;
1493
1494 assert(key);
1495 assert(result);
ed29bfdc 1496 assert(authenticated);
105e1512
LP
1497
1498 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1499
1500 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1501
1502 if (rr->key->class != key->class)
1503 continue;
1504
105e1512
LP
1505 switch (rr->key->type) {
1506
1507 case DNS_TYPE_NSEC:
1508
1509 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
1510 if (r < 0)
1511 return r;
1512 if (r > 0) {
146035b3
TG
1513 if (bitmap_isset(rr->nsec.types, key->type))
1514 *result = DNSSEC_NSEC_FOUND;
1515 else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
1516 *result = DNSSEC_NSEC_CNAME;
1517 else
1518 *result = DNSSEC_NSEC_NODATA;
ed29bfdc 1519 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
72667f08
LP
1520 return 0;
1521 }
1522
105e1512
LP
1523 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
1524 if (r < 0)
1525 return r;
1526 if (r > 0) {
1527 *result = DNSSEC_NSEC_NXDOMAIN;
ed29bfdc 1528 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
105e1512
LP
1529 return 0;
1530 }
72667f08 1531 break;
72667f08 1532
105e1512
LP
1533 case DNS_TYPE_NSEC3:
1534 have_nsec3 = true;
72667f08
LP
1535 break;
1536 }
1537 }
1538
105e1512
LP
1539 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1540 if (have_nsec3)
ed29bfdc 1541 return dnssec_test_nsec3(answer, key, result, authenticated);
105e1512 1542
72667f08
LP
1543 /* No approproate NSEC RR found, report this. */
1544 *result = DNSSEC_NSEC_NO_RR;
1545 return 0;
1546}
1547
24710c48
LP
1548static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
1549 [DNSSEC_NO] = "no",
b652d4a2 1550 [DNSSEC_DOWNGRADE_OK] = "downgrade-ok",
24710c48
LP
1551 [DNSSEC_YES] = "yes",
1552};
1553DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
547973de
LP
1554
1555static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
1556 [DNSSEC_VALIDATED] = "validated",
1557 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
1558 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
1559 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
1560 [DNSSEC_NO_SIGNATURE] = "no-signature",
1561 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 1562 [DNSSEC_UNSIGNED] = "unsigned",
547973de 1563 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 1564 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
b652d4a2 1565 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
547973de
LP
1566};
1567DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);