]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
Merge pull request #2245 from ssahani/socket1
[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
6f76ec5a 1076int dnssec_nsec3_hash(const 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
a8f158b9
LP
1092 if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1093 return -EOPNOTSUPP;
1094
d15ad742 1095 algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
72667f08
LP
1096 if (algorithm < 0)
1097 return algorithm;
1098
1099 initialize_libgcrypt();
1100
1101 hash_size = gcry_md_get_algo_dlen(algorithm);
1102 assert(hash_size > 0);
1103
1104 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1105 return -EINVAL;
1106
1107 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1108 if (r < 0)
1109 return r;
1110
1111 gcry_md_open(&md, algorithm, 0);
1112 if (!md)
1113 return -EIO;
1114
1115 gcry_md_write(md, wire_format, r);
1116 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1117
1118 result = gcry_md_read(md, 0);
1119 if (!result) {
1120 r = -EIO;
1121 goto finish;
1122 }
1123
1124 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1125 uint8_t tmp[hash_size];
1126 memcpy(tmp, result, hash_size);
1127
1128 gcry_md_reset(md);
1129 gcry_md_write(md, tmp, hash_size);
1130 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1131
1132 result = gcry_md_read(md, 0);
1133 if (!result) {
1134 r = -EIO;
1135 goto finish;
1136 }
1137 }
1138
1139 memcpy(ret, result, hash_size);
1140 r = (int) hash_size;
1141
1142finish:
1143 gcry_md_close(md);
1144 return r;
1145}
1146
db5b0e92
LP
1147static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourceRecord *nsec3) {
1148 const char *a, *b;
1149 int r;
1150
1151 assert(rr);
1152
db5b0e92
LP
1153 if (rr->key->type != DNS_TYPE_NSEC3)
1154 return 0;
1155
1156 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1157 if (!IN_SET(rr->nsec3.flags, 0, 1))
1158 return 0;
1159
d15ad742
LP
1160 /* Ignore NSEC3 RRs whose algorithm we don't know */
1161 if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
1162 return 0;
a8f158b9
LP
1163 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1164 if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1165 return 0;
d15ad742 1166
db5b0e92
LP
1167 if (!nsec3)
1168 return 1;
1169
1170 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1171
1172 if (nsec3 == rr) /* Shortcut */
1173 return 1;
1174
1175 if (rr->key->class != nsec3->key->class)
1176 return 0;
1177 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1178 return 0;
1179 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1180 return 0;
1181 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1182 return 0;
1183 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1184 return 0;
1185
1186 a = DNS_RESOURCE_KEY_NAME(rr->key);
1187 r = dns_name_parent(&a); /* strip off hash */
1188 if (r < 0)
1189 return r;
1190 if (r == 0)
1191 return 0;
1192
1193 b = DNS_RESOURCE_KEY_NAME(nsec3->key);
1194 r = dns_name_parent(&b); /* strip off hash */
1195 if (r < 0)
1196 return r;
1197 if (r == 0)
1198 return 0;
1199
1200 return dns_name_equal(a, b);
1201}
1202
6f76ec5a
TG
1203static int nsec3_hashed_domain(const DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
1204 _cleanup_free_ char *l = NULL, *hashed_domain = NULL;
105e1512 1205 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
6f76ec5a
TG
1206 int hashed_size;
1207
1208 assert(nsec3);
1209 assert(domain);
1210 assert(zone);
1211 assert(ret);
1212
1213 hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
1214 if (hashed_size < 0)
1215 return hashed_size;
1216
1217 l = base32hexmem(hashed, hashed_size, false);
1218 if (!l)
1219 return -ENOMEM;
1220
1221 hashed_domain = strjoin(l, ".", zone, NULL);
1222 if (!hashed_domain)
1223 return -ENOMEM;
1224
1225 *ret = hashed_domain;
1226 hashed_domain = NULL;
1227
1228 return hashed_size;
1229}
1230
35ad41d3
TG
1231/* See RFC 5155, Section 8
1232 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1233 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1234 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1235 * matches the wildcard domain.
1236 *
1237 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1238 * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1239 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1240 * to conclude anything we indicate this by returning NO_RR. */
ed29bfdc 1241static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
35ad41d3 1242 _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL;
d1511b33 1243 const char *zone, *p, *pp = NULL;
35ad41d3 1244 DnsResourceRecord *rr, *enclosure_rr, *suffix_rr, *wildcard_rr = NULL;
105e1512
LP
1245 DnsAnswerFlags flags;
1246 int hashed_size, r;
35ad41d3 1247 bool a, no_closer = false, no_wildcard = false, optout = false;
72667f08
LP
1248
1249 assert(key);
1250 assert(result);
ed29bfdc 1251 assert(authenticated);
72667f08 1252
d1511b33
TG
1253 /* First step, find the zone name and the NSEC3 parameters of the zone.
1254 * it is sufficient to look for the longest common suffix we find with
1255 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1256 * records from a given zone in a response must use the same
1257 * parameters. */
1258 zone = DNS_RESOURCE_KEY_NAME(key);
13b78323 1259 for (;;) {
db5b0e92 1260 DNS_ANSWER_FOREACH_FLAGS(suffix_rr, flags, answer) {
db5b0e92
LP
1261 r = nsec3_is_good(suffix_rr, flags, NULL);
1262 if (r < 0)
1263 return r;
1264 if (r == 0)
13b78323
LP
1265 continue;
1266
d1511b33 1267 r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, zone);
13b78323
LP
1268 if (r < 0)
1269 return r;
1270 if (r > 0)
d1511b33 1271 goto found_zone;
13b78323
LP
1272 }
1273
1274 /* Strip one label from the front */
d1511b33 1275 r = dns_name_parent(&zone);
13b78323
LP
1276 if (r < 0)
1277 return r;
1278 if (r == 0)
1279 break;
1280 }
1281
1282 *result = DNSSEC_NSEC_NO_RR;
1283 return 0;
1284
d1511b33 1285found_zone:
13b78323 1286 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
105e1512
LP
1287 p = DNS_RESOURCE_KEY_NAME(key);
1288 for (;;) {
6f76ec5a 1289 _cleanup_free_ char *hashed_domain = NULL;
72667f08 1290
6f76ec5a 1291 hashed_size = nsec3_hashed_domain(suffix_rr, p, zone, &hashed_domain);
db5b0e92
LP
1292 if (hashed_size == -EOPNOTSUPP) {
1293 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1294 return 0;
1295 }
1296 if (hashed_size < 0)
1297 return hashed_size;
72667f08 1298
d1511b33 1299 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
db5b0e92 1300
d1511b33 1301 r = nsec3_is_good(enclosure_rr, flags, suffix_rr);
72667f08
LP
1302 if (r < 0)
1303 return r;
105e1512
LP
1304 if (r == 0)
1305 continue;
1306
d1511b33 1307 if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
db5b0e92 1308 continue;
105e1512 1309
d1511b33 1310 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
72667f08
LP
1311 if (r < 0)
1312 return r;
ed29bfdc
LP
1313 if (r > 0) {
1314 a = flags & DNS_ANSWER_AUTHENTICATED;
13b78323 1315 goto found_closest_encloser;
ed29bfdc 1316 }
105e1512
LP
1317 }
1318
1319 /* We didn't find the closest encloser with this name,
1320 * but let's remember this domain name, it might be
1321 * the next closer name */
1322
1323 pp = p;
1324
1325 /* Strip one label from the front */
1326 r = dns_name_parent(&p);
1327 if (r < 0)
1328 return r;
1329 if (r == 0)
72667f08 1330 break;
105e1512 1331 }
72667f08 1332
105e1512
LP
1333 *result = DNSSEC_NSEC_NO_RR;
1334 return 0;
72667f08 1335
13b78323 1336found_closest_encloser:
105e1512 1337 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 1338
105e1512 1339 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
d1511b33 1340 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
105e1512 1341 return -EBADMSG;
72667f08 1342
105e1512
LP
1343 /* Ensure that this data is from the delegated domain
1344 * (i.e. originates from the "lower" DNS server), and isn't
1345 * just glue records (i.e. doesn't originate from the "upper"
1346 * DNS server). */
d1511b33
TG
1347 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1348 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
105e1512 1349 return -EBADMSG;
72667f08 1350
105e1512
LP
1351 if (!pp) {
1352 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
146035b3
TG
1353 if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
1354 *result = DNSSEC_NSEC_FOUND;
1355 else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
1356 *result = DNSSEC_NSEC_CNAME;
1357 else
1358 *result = DNSSEC_NSEC_NODATA;
1359
ed29bfdc 1360 *authenticated = a;
146035b3 1361
105e1512
LP
1362 return 0;
1363 }
72667f08 1364
35ad41d3
TG
1365 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1366
1367 wildcard = strappend("*.", p);
1368 if (!wildcard)
1369 return -ENOMEM;
1370
1371 r = nsec3_hashed_domain(enclosure_rr, wildcard, zone, &wildcard_domain);
105e1512
LP
1372 if (r < 0)
1373 return r;
1374 if (r != hashed_size)
1375 return -EBADMSG;
72667f08 1376
6f76ec5a 1377 r = nsec3_hashed_domain(enclosure_rr, pp, zone, &next_closer_domain);
105e1512
LP
1378 if (r < 0)
1379 return r;
1380 if (r != hashed_size)
1381 return -EBADMSG;
72667f08 1382
105e1512
LP
1383 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1384 _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL;
105e1512 1385
db5b0e92 1386 r = nsec3_is_good(rr, flags, suffix_rr);
105e1512
LP
1387 if (r < 0)
1388 return r;
1389 if (r == 0)
1390 continue;
1391
1392 label = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
1393 if (!label)
1394 return -ENOMEM;
1395
b2c2a1b9 1396 next_hashed_domain = strjoin(label, ".", zone, NULL);
105e1512
LP
1397 if (!next_hashed_domain)
1398 return -ENOMEM;
1399
1400 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1401 if (r < 0)
1402 return r;
1403 if (r > 0) {
1404 if (rr->nsec3.flags & 1)
35ad41d3 1405 optout = true;
105e1512 1406
35ad41d3
TG
1407 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1408
1409 no_closer = true;
1410 }
1411
1412 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
1413 if (r < 0)
1414 return r;
1415 if (r > 0) {
1416 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1417
1418 wildcard_rr = rr;
1419 }
1420
1421 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
1422 if (r < 0)
1423 return r;
1424 if (r > 0) {
1425 if (rr->nsec3.flags & 1)
1426 /* This only makes sense if we have a wildcard delegation, which is
1427 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1428 * this not happening, so hence cannot simply conclude NXDOMAIN as
1429 * we would wish */
1430 optout = true;
1431
1432 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1433
1434 no_wildcard = true;
105e1512
LP
1435 }
1436 }
1437
35ad41d3
TG
1438 if (wildcard_rr && no_wildcard)
1439 return -EBADMSG;
1440
1441 if (!no_closer) {
1442 *result = DNSSEC_NSEC_NO_RR;
1443
1444 return 0;
1445 }
1446
1447 if (wildcard_rr) {
1448 /* A wildcard exists that matches our query. */
1449 if (optout)
1450 /* This is not specified in any RFC to the best of my knowledge, but
1451 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1452 * it means that we cannot prove that the source of synthesis is
1453 * correct, as there may be a closer match. */
1454 *result = DNSSEC_NSEC_OPTOUT;
1455 else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1456 *result = DNSSEC_NSEC_FOUND;
1457 else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1458 *result = DNSSEC_NSEC_CNAME;
1459 else
1460 *result = DNSSEC_NSEC_NODATA;
1461 } else {
1462 if (optout)
1463 /* The RFC only specifies that we have to care for optout for NODATA for
1464 * DS records. However, children of an insecure opt-out delegation should
1465 * also be considered opt-out, rather than verified NXDOMAIN.
1466 * Note that we do not require a proof of wildcard non-existence if the
1467 * next closer domain is covered by an opt-out, as that would not provide
1468 * any additional information. */
1469 *result = DNSSEC_NSEC_OPTOUT;
1470 else if (no_wildcard)
1471 *result = DNSSEC_NSEC_NXDOMAIN;
1472 else {
1473 *result = DNSSEC_NSEC_NO_RR;
1474
1475 return 0;
1476 }
1477 }
1478
1479 *authenticated = a;
1480
105e1512
LP
1481 return 0;
1482}
1483
ed29bfdc 1484int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated) {
105e1512
LP
1485 DnsResourceRecord *rr;
1486 bool have_nsec3 = false;
1487 DnsAnswerFlags flags;
1488 int r;
1489
1490 assert(key);
1491 assert(result);
ed29bfdc 1492 assert(authenticated);
105e1512
LP
1493
1494 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1495
1496 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1497
1498 if (rr->key->class != key->class)
1499 continue;
1500
105e1512
LP
1501 switch (rr->key->type) {
1502
1503 case DNS_TYPE_NSEC:
1504
1505 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
1506 if (r < 0)
1507 return r;
1508 if (r > 0) {
146035b3
TG
1509 if (bitmap_isset(rr->nsec.types, key->type))
1510 *result = DNSSEC_NSEC_FOUND;
1511 else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
1512 *result = DNSSEC_NSEC_CNAME;
1513 else
1514 *result = DNSSEC_NSEC_NODATA;
ed29bfdc 1515 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
72667f08
LP
1516 return 0;
1517 }
1518
105e1512
LP
1519 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
1520 if (r < 0)
1521 return r;
1522 if (r > 0) {
1523 *result = DNSSEC_NSEC_NXDOMAIN;
ed29bfdc 1524 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
105e1512
LP
1525 return 0;
1526 }
72667f08 1527 break;
72667f08 1528
105e1512
LP
1529 case DNS_TYPE_NSEC3:
1530 have_nsec3 = true;
72667f08
LP
1531 break;
1532 }
1533 }
1534
105e1512
LP
1535 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1536 if (have_nsec3)
ed29bfdc 1537 return dnssec_test_nsec3(answer, key, result, authenticated);
105e1512 1538
72667f08
LP
1539 /* No approproate NSEC RR found, report this. */
1540 *result = DNSSEC_NSEC_NO_RR;
1541 return 0;
1542}
1543
24710c48
LP
1544static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
1545 [DNSSEC_NO] = "no",
b652d4a2 1546 [DNSSEC_DOWNGRADE_OK] = "downgrade-ok",
24710c48
LP
1547 [DNSSEC_YES] = "yes",
1548};
1549DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
547973de
LP
1550
1551static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
1552 [DNSSEC_VALIDATED] = "validated",
1553 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
1554 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
1555 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
1556 [DNSSEC_NO_SIGNATURE] = "no-signature",
1557 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 1558 [DNSSEC_UNSIGNED] = "unsigned",
547973de 1559 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 1560 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
b652d4a2 1561 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
547973de
LP
1562};
1563DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);