]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
resolved: rename "downgrade-ok" mode to "allow-downgrade"
[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
160fbda9 41 * - nxdomain on qname
6f8a2c68 42 * - per-interface DNSSEC setting
2cd87277 43 *
2b442ac8
LP
44 * */
45
46#define VERIFY_RRS_MAX 256
47#define MAX_KEY_SIZE (32*1024)
48
896c5672
LP
49/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
50#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
51
a8f158b9
LP
52/* Maximum number of NSEC3 iterations we'll do. */
53#define NSEC3_ITERATIONS_MAX 2048
54
2b442ac8
LP
55/*
56 * The DNSSEC Chain of trust:
57 *
58 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
59 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
60 * DS RRs are protected like normal RRs
61 *
62 * Example chain:
63 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
64 */
65
0638401a
LP
66static void initialize_libgcrypt(void) {
67 const char *p;
68
69 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
70 return;
71
72 p = gcry_check_version("1.4.5");
73 assert(p);
74
75 gcry_control(GCRYCTL_DISABLE_SECMEM);
76 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
77}
78
0c857028 79uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
2b442ac8 80 const uint8_t *p;
0c857028 81 uint32_t sum, f;
2b442ac8
LP
82 size_t i;
83
84 /* The algorithm from RFC 4034, Appendix B. */
85
86 assert(dnskey);
87 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
88
0c857028
LP
89 f = (uint32_t) dnskey->dnskey.flags;
90
91 if (mask_revoke)
92 f &= ~DNSKEY_FLAG_REVOKE;
93
94 sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
2b442ac8
LP
95
96 p = dnskey->dnskey.key;
97
98 for (i = 0; i < dnskey->dnskey.key_size; i++)
99 sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
100
101 sum += (sum >> 16) & UINT32_C(0xFFFF);
102
103 return sum & UINT32_C(0xFFFF);
104}
105
106static int rr_compare(const void *a, const void *b) {
107 DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
108 size_t m;
109 int r;
110
111 /* Let's order the RRs according to RFC 4034, Section 6.3 */
112
113 assert(x);
114 assert(*x);
115 assert((*x)->wire_format);
116 assert(y);
117 assert(*y);
118 assert((*y)->wire_format);
119
85aeaccc 120 m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x), DNS_RESOURCE_RECORD_RDATA_SIZE(*y));
2b442ac8 121
85aeaccc 122 r = memcmp(DNS_RESOURCE_RECORD_RDATA(*x), DNS_RESOURCE_RECORD_RDATA(*y), m);
2b442ac8
LP
123 if (r != 0)
124 return r;
125
85aeaccc 126 if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
2b442ac8 127 return -1;
85aeaccc 128 else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y))
2b442ac8
LP
129 return 1;
130
131 return 0;
132}
133
ea3a892f 134static int dnssec_rsa_verify_raw(
2b442ac8
LP
135 const char *hash_algorithm,
136 const void *signature, size_t signature_size,
137 const void *data, size_t data_size,
138 const void *exponent, size_t exponent_size,
139 const void *modulus, size_t modulus_size) {
140
141 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
142 gcry_mpi_t n = NULL, e = NULL, s = NULL;
143 gcry_error_t ge;
144 int r;
145
146 assert(hash_algorithm);
147
148 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
149 if (ge != 0) {
150 r = -EIO;
151 goto finish;
152 }
153
154 ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
155 if (ge != 0) {
156 r = -EIO;
157 goto finish;
158 }
159
160 ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
161 if (ge != 0) {
162 r = -EIO;
163 goto finish;
164 }
165
166 ge = gcry_sexp_build(&signature_sexp,
167 NULL,
168 "(sig-val (rsa (s %m)))",
169 s);
170
171 if (ge != 0) {
172 r = -EIO;
173 goto finish;
174 }
175
176 ge = gcry_sexp_build(&data_sexp,
177 NULL,
178 "(data (flags pkcs1) (hash %s %b))",
179 hash_algorithm,
180 (int) data_size,
181 data);
182 if (ge != 0) {
183 r = -EIO;
184 goto finish;
185 }
186
187 ge = gcry_sexp_build(&public_key_sexp,
188 NULL,
189 "(public-key (rsa (n %m) (e %m)))",
190 n,
191 e);
192 if (ge != 0) {
193 r = -EIO;
194 goto finish;
195 }
196
197 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
d12bf2bd 198 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
2b442ac8 199 r = 0;
d12bf2bd
LP
200 else if (ge != 0) {
201 log_debug("RSA signature check failed: %s", gpg_strerror(ge));
2b442ac8 202 r = -EIO;
d12bf2bd 203 } else
2b442ac8
LP
204 r = 1;
205
206finish:
207 if (e)
208 gcry_mpi_release(e);
209 if (n)
210 gcry_mpi_release(n);
211 if (s)
212 gcry_mpi_release(s);
213
214 if (public_key_sexp)
215 gcry_sexp_release(public_key_sexp);
216 if (signature_sexp)
217 gcry_sexp_release(signature_sexp);
218 if (data_sexp)
219 gcry_sexp_release(data_sexp);
220
221 return r;
222}
223
ea3a892f
LP
224static int dnssec_rsa_verify(
225 const char *hash_algorithm,
226 const void *hash, size_t hash_size,
227 DnsResourceRecord *rrsig,
228 DnsResourceRecord *dnskey) {
229
230 size_t exponent_size, modulus_size;
231 void *exponent, *modulus;
232
233 assert(hash_algorithm);
234 assert(hash);
235 assert(hash_size > 0);
236 assert(rrsig);
237 assert(dnskey);
238
239 if (*(uint8_t*) dnskey->dnskey.key == 0) {
240 /* exponent is > 255 bytes long */
241
242 exponent = (uint8_t*) dnskey->dnskey.key + 3;
243 exponent_size =
ac04adbe
TG
244 ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) |
245 ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]);
ea3a892f
LP
246
247 if (exponent_size < 256)
248 return -EINVAL;
249
250 if (3 + exponent_size >= dnskey->dnskey.key_size)
251 return -EINVAL;
252
253 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
254 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
255
256 } else {
257 /* exponent is <= 255 bytes long */
258
259 exponent = (uint8_t*) dnskey->dnskey.key + 1;
260 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
261
262 if (exponent_size <= 0)
263 return -EINVAL;
264
265 if (1 + exponent_size >= dnskey->dnskey.key_size)
266 return -EINVAL;
267
268 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
269 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
270 }
271
272 return dnssec_rsa_verify_raw(
273 hash_algorithm,
274 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
275 hash, hash_size,
276 exponent, exponent_size,
277 modulus, modulus_size);
278}
279
e0240c64
LP
280static int dnssec_ecdsa_verify_raw(
281 const char *hash_algorithm,
282 const char *curve,
283 const void *signature_r, size_t signature_r_size,
284 const void *signature_s, size_t signature_s_size,
285 const void *data, size_t data_size,
286 const void *key, size_t key_size) {
287
288 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
289 gcry_mpi_t q = NULL, r = NULL, s = NULL;
290 gcry_error_t ge;
291 int k;
292
293 assert(hash_algorithm);
294
295 ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
296 if (ge != 0) {
297 k = -EIO;
298 goto finish;
299 }
300
301 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
302 if (ge != 0) {
303 k = -EIO;
304 goto finish;
305 }
306
307 ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
308 if (ge != 0) {
309 k = -EIO;
310 goto finish;
311 }
312
313 ge = gcry_sexp_build(&signature_sexp,
314 NULL,
315 "(sig-val (ecdsa (r %m) (s %m)))",
316 r,
317 s);
318 if (ge != 0) {
319 k = -EIO;
320 goto finish;
321 }
322
323 ge = gcry_sexp_build(&data_sexp,
324 NULL,
325 "(data (flags rfc6979) (hash %s %b))",
326 hash_algorithm,
327 (int) data_size,
328 data);
329 if (ge != 0) {
330 k = -EIO;
331 goto finish;
332 }
333
334 ge = gcry_sexp_build(&public_key_sexp,
335 NULL,
336 "(public-key (ecc (curve %s) (q %m)))",
337 curve,
338 q);
339 if (ge != 0) {
340 k = -EIO;
341 goto finish;
342 }
343
344 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
345 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
346 k = 0;
347 else if (ge != 0) {
348 log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
349 k = -EIO;
350 } else
351 k = 1;
352finish:
353 if (r)
354 gcry_mpi_release(r);
355 if (s)
356 gcry_mpi_release(s);
357 if (q)
358 gcry_mpi_release(q);
359
360 if (public_key_sexp)
361 gcry_sexp_release(public_key_sexp);
362 if (signature_sexp)
363 gcry_sexp_release(signature_sexp);
364 if (data_sexp)
365 gcry_sexp_release(data_sexp);
366
367 return k;
368}
369
370static int dnssec_ecdsa_verify(
371 const char *hash_algorithm,
372 int algorithm,
373 const void *hash, size_t hash_size,
374 DnsResourceRecord *rrsig,
375 DnsResourceRecord *dnskey) {
376
377 const char *curve;
378 size_t key_size;
379 uint8_t *q;
380
381 assert(hash);
382 assert(hash_size);
383 assert(rrsig);
384 assert(dnskey);
385
386 if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
387 key_size = 32;
388 curve = "NIST P-256";
389 } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
390 key_size = 48;
391 curve = "NIST P-384";
392 } else
393 return -EOPNOTSUPP;
394
395 if (dnskey->dnskey.key_size != key_size * 2)
396 return -EINVAL;
397
398 if (rrsig->rrsig.signature_size != key_size * 2)
399 return -EINVAL;
400
401 q = alloca(key_size*2 + 1);
402 q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
403 memcpy(q+1, dnskey->dnskey.key, key_size*2);
404
405 return dnssec_ecdsa_verify_raw(
406 hash_algorithm,
407 curve,
408 rrsig->rrsig.signature, key_size,
409 (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
410 hash, hash_size,
411 q, key_size*2+1);
412}
413
2b442ac8
LP
414static void md_add_uint8(gcry_md_hd_t md, uint8_t v) {
415 gcry_md_write(md, &v, sizeof(v));
416}
417
418static void md_add_uint16(gcry_md_hd_t md, uint16_t v) {
419 v = htobe16(v);
420 gcry_md_write(md, &v, sizeof(v));
421}
422
423static void md_add_uint32(gcry_md_hd_t md, uint32_t v) {
424 v = htobe32(v);
425 gcry_md_write(md, &v, sizeof(v));
426}
427
2a326321
LP
428static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
429 usec_t expiration, inception, skew;
430
431 assert(rrsig);
432 assert(rrsig->key->type == DNS_TYPE_RRSIG);
433
434 if (realtime == USEC_INFINITY)
435 realtime = now(CLOCK_REALTIME);
436
437 expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
438 inception = rrsig->rrsig.inception * USEC_PER_SEC;
439
440 if (inception > expiration)
2a44bec4 441 return -EKEYREJECTED;
2a326321 442
896c5672
LP
443 /* Permit a certain amount of clock skew of 10% of the valid
444 * time range. This takes inspiration from unbound's
445 * resolver. */
2a326321 446 skew = (expiration - inception) / 10;
896c5672
LP
447 if (skew > SKEW_MAX)
448 skew = SKEW_MAX;
2a326321
LP
449
450 if (inception < skew)
451 inception = 0;
452 else
453 inception -= skew;
454
455 if (expiration + skew < expiration)
456 expiration = USEC_INFINITY;
457 else
458 expiration += skew;
459
460 return realtime < inception || realtime > expiration;
461}
462
ca994e85 463static int algorithm_to_gcrypt_md(uint8_t algorithm) {
fbf1a66d 464
6af47493
LP
465 /* Translates a DNSSEC signature algorithm into a gcrypt
466 * digest identifier.
467 *
468 * Note that we implement all algorithms listed as "Must
469 * implement" and "Recommended to Implement" in RFC6944. We
470 * don't implement any algorithms that are listed as
471 * "Optional" or "Must Not Implement". Specifically, we do not
472 * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
473 * GOST-ECC. */
fbf1a66d
LP
474
475 switch (algorithm) {
476
477 case DNSSEC_ALGORITHM_RSASHA1:
478 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
479 return GCRY_MD_SHA1;
480
481 case DNSSEC_ALGORITHM_RSASHA256:
e0240c64 482 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
fbf1a66d
LP
483 return GCRY_MD_SHA256;
484
e0240c64
LP
485 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
486 return GCRY_MD_SHA384;
487
fbf1a66d
LP
488 case DNSSEC_ALGORITHM_RSASHA512:
489 return GCRY_MD_SHA512;
490
491 default:
492 return -EOPNOTSUPP;
493 }
494}
495
2a326321
LP
496int dnssec_verify_rrset(
497 DnsAnswer *a,
0c857028 498 const DnsResourceKey *key,
2a326321
LP
499 DnsResourceRecord *rrsig,
500 DnsResourceRecord *dnskey,
547973de
LP
501 usec_t realtime,
502 DnssecResult *result) {
2a326321 503
2b442ac8 504 uint8_t wire_format_name[DNS_WIRE_FOMAT_HOSTNAME_MAX];
ea3a892f
LP
505 size_t hash_size;
506 void *hash;
2b442ac8
LP
507 DnsResourceRecord **list, *rr;
508 gcry_md_hd_t md = NULL;
ca994e85 509 int r, md_algorithm;
2b442ac8 510 size_t k, n = 0;
2b442ac8
LP
511
512 assert(key);
513 assert(rrsig);
514 assert(dnskey);
547973de 515 assert(result);
2a326321
LP
516 assert(rrsig->key->type == DNS_TYPE_RRSIG);
517 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
2b442ac8
LP
518
519 /* Verifies the the RRSet matching the specified "key" in "a",
520 * using the signature "rrsig" and the key "dnskey". It's
521 * assumed the RRSIG and DNSKEY match. */
522
ca994e85
LP
523 md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm);
524 if (md_algorithm == -EOPNOTSUPP) {
203f1b35
LP
525 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
526 return 0;
527 }
ca994e85
LP
528 if (md_algorithm < 0)
529 return md_algorithm;
2b442ac8 530
2a326321
LP
531 r = dnssec_rrsig_expired(rrsig, realtime);
532 if (r < 0)
533 return r;
547973de
LP
534 if (r > 0) {
535 *result = DNSSEC_SIGNATURE_EXPIRED;
536 return 0;
537 }
2a326321 538
2b442ac8
LP
539 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
540 list = newa(DnsResourceRecord *, a->n_rrs);
541
542 DNS_ANSWER_FOREACH(rr, a) {
543 r = dns_resource_key_equal(key, rr->key);
544 if (r < 0)
545 return r;
546 if (r == 0)
547 continue;
548
549 /* We need the wire format for ordering, and digest calculation */
550 r = dns_resource_record_to_wire_format(rr, true);
551 if (r < 0)
552 return r;
553
554 list[n++] = rr;
935a999f
TG
555
556 if (n > VERIFY_RRS_MAX)
557 return -E2BIG;
2b442ac8
LP
558 }
559
560 if (n <= 0)
561 return -ENODATA;
562
563 /* Bring the RRs into canonical order */
6c5e8fbf 564 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8
LP
565
566 /* OK, the RRs are now in canonical order. Let's calculate the digest */
fbf1a66d 567 initialize_libgcrypt();
2b442ac8 568
ca994e85 569 hash_size = gcry_md_get_algo_dlen(md_algorithm);
fbf1a66d 570 assert(hash_size > 0);
2b442ac8 571
ca994e85 572 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
573 if (!md)
574 return -EIO;
575
576 md_add_uint16(md, rrsig->rrsig.type_covered);
577 md_add_uint8(md, rrsig->rrsig.algorithm);
578 md_add_uint8(md, rrsig->rrsig.labels);
579 md_add_uint32(md, rrsig->rrsig.original_ttl);
580 md_add_uint32(md, rrsig->rrsig.expiration);
581 md_add_uint32(md, rrsig->rrsig.inception);
582 md_add_uint16(md, rrsig->rrsig.key_tag);
583
584 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
585 if (r < 0)
586 goto finish;
587 gcry_md_write(md, wire_format_name, r);
588
589 for (k = 0; k < n; k++) {
e7ff0e0b 590 const char *suffix;
2b442ac8
LP
591 size_t l;
592 rr = list[k];
593
e7ff0e0b
LP
594 r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
595 if (r < 0)
596 goto finish;
597 if (r > 0) /* This is a wildcard! */
598 gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
599
600 r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
2b442ac8
LP
601 if (r < 0)
602 goto finish;
603 gcry_md_write(md, wire_format_name, r);
604
605 md_add_uint16(md, rr->key->type);
606 md_add_uint16(md, rr->key->class);
607 md_add_uint32(md, rrsig->rrsig.original_ttl);
608
85aeaccc 609 l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
2b442ac8
LP
610 assert(l <= 0xFFFF);
611
612 md_add_uint16(md, (uint16_t) l);
85aeaccc 613 gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l);
2b442ac8
LP
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
0c857028 657int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
2b442ac8
LP
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;
0c857028 674 if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
28b8191e 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
0c857028 681 if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
2b442ac8
LP
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,
0c857028 743 const DnsResourceKey *key,
2a326321 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? */
0c857028 782 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
2b442ac8
LP
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
0c857028 962int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
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;
0c857028
LP
980 if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
981 return -EKEYREJECTED;
2b442ac8
LP
982 if (dnskey->dnskey.protocol != 3)
983 return -EKEYREJECTED;
984
2b442ac8
LP
985 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
986 return 0;
0c857028 987 if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
2b442ac8
LP
988 return 0;
989
0638401a
LP
990 initialize_libgcrypt();
991
ca994e85
LP
992 md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
993 if (md_algorithm < 0)
994 return md_algorithm;
2b442ac8 995
ca994e85 996 hash_size = gcry_md_get_algo_dlen(md_algorithm);
a1972a91 997 assert(hash_size > 0);
2b442ac8 998
a1972a91
LP
999 if (ds->ds.digest_size != hash_size)
1000 return 0;
2b442ac8 1001
a1972a91
LP
1002 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
1003 if (r < 0)
1004 return r;
2b442ac8 1005
ca994e85 1006 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
1007 if (!md)
1008 return -EIO;
1009
2b442ac8 1010 gcry_md_write(md, owner_name, r);
0c857028
LP
1011 if (mask_revoke)
1012 md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
1013 else
1014 md_add_uint16(md, dnskey->dnskey.flags);
2b442ac8
LP
1015 md_add_uint8(md, dnskey->dnskey.protocol);
1016 md_add_uint8(md, dnskey->dnskey.algorithm);
1017 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1018
1019 result = gcry_md_read(md, 0);
1020 if (!result) {
1021 r = -EIO;
1022 goto finish;
1023 }
1024
1025 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
1026
1027finish:
1028 gcry_md_close(md);
1029 return r;
1030}
24710c48 1031
547973de
LP
1032int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
1033 DnsResourceRecord *ds;
105e1512 1034 DnsAnswerFlags flags;
547973de
LP
1035 int r;
1036
1037 assert(dnskey);
1038
1039 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1040 return 0;
1041
105e1512
LP
1042 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1043
1044 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1045 continue;
547973de
LP
1046
1047 if (ds->key->type != DNS_TYPE_DS)
1048 continue;
1049
d1c4ee32
LP
1050 if (ds->key->class != dnskey->key->class)
1051 continue;
1052
1053 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
1054 if (r < 0)
1055 return r;
1056 if (r == 0)
1057 continue;
1058
0c857028
LP
1059 r = dnssec_verify_dnskey(dnskey, ds, false);
1060 if (r == -EKEYREJECTED)
1061 return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */
547973de
LP
1062 if (r < 0)
1063 return r;
1064 if (r > 0)
1065 return 1;
1066 }
1067
1068 return 0;
1069}
1070
d15ad742
LP
1071static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
1072
1073 /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
1074
1075 switch (algorithm) {
1076
1077 case NSEC3_ALGORITHM_SHA1:
1078 return GCRY_MD_SHA1;
1079
1080 default:
1081 return -EOPNOTSUPP;
1082 }
1083}
1084
1d3db294 1085int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
72667f08
LP
1086 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
1087 gcry_md_hd_t md = NULL;
1088 size_t hash_size;
1089 int algorithm;
1090 void *result;
1091 unsigned k;
1092 int r;
1093
1094 assert(nsec3);
1095 assert(name);
1096 assert(ret);
1097
1098 if (nsec3->key->type != DNS_TYPE_NSEC3)
1099 return -EINVAL;
1100
1d3db294
LP
1101 if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
1102 log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
a8f158b9 1103 return -EOPNOTSUPP;
1d3db294 1104 }
a8f158b9 1105
d15ad742 1106 algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
72667f08
LP
1107 if (algorithm < 0)
1108 return algorithm;
1109
1110 initialize_libgcrypt();
1111
1112 hash_size = gcry_md_get_algo_dlen(algorithm);
1113 assert(hash_size > 0);
1114
1115 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1116 return -EINVAL;
1117
1118 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1119 if (r < 0)
1120 return r;
1121
1122 gcry_md_open(&md, algorithm, 0);
1123 if (!md)
1124 return -EIO;
1125
1126 gcry_md_write(md, wire_format, r);
1127 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1128
1129 result = gcry_md_read(md, 0);
1130 if (!result) {
1131 r = -EIO;
1132 goto finish;
1133 }
1134
1135 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1136 uint8_t tmp[hash_size];
1137 memcpy(tmp, result, hash_size);
1138
1139 gcry_md_reset(md);
1140 gcry_md_write(md, tmp, hash_size);
1141 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1142
1143 result = gcry_md_read(md, 0);
1144 if (!result) {
1145 r = -EIO;
1146 goto finish;
1147 }
1148 }
1149
1150 memcpy(ret, result, hash_size);
1151 r = (int) hash_size;
1152
1153finish:
1154 gcry_md_close(md);
1155 return r;
1156}
1157
db5b0e92
LP
1158static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourceRecord *nsec3) {
1159 const char *a, *b;
1160 int r;
1161
1162 assert(rr);
1163
db5b0e92
LP
1164 if (rr->key->type != DNS_TYPE_NSEC3)
1165 return 0;
1166
1167 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1168 if (!IN_SET(rr->nsec3.flags, 0, 1))
1169 return 0;
1170
d15ad742
LP
1171 /* Ignore NSEC3 RRs whose algorithm we don't know */
1172 if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
1173 return 0;
a8f158b9
LP
1174 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1175 if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1176 return 0;
d15ad742 1177
db5b0e92
LP
1178 if (!nsec3)
1179 return 1;
1180
1181 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1182
1183 if (nsec3 == rr) /* Shortcut */
1184 return 1;
1185
1186 if (rr->key->class != nsec3->key->class)
1187 return 0;
1188 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1189 return 0;
1190 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1191 return 0;
1192 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1193 return 0;
1194 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1195 return 0;
1196
1197 a = DNS_RESOURCE_KEY_NAME(rr->key);
1198 r = dns_name_parent(&a); /* strip off hash */
1199 if (r < 0)
1200 return r;
1201 if (r == 0)
1202 return 0;
1203
1204 b = DNS_RESOURCE_KEY_NAME(nsec3->key);
1205 r = dns_name_parent(&b); /* strip off hash */
1206 if (r < 0)
1207 return r;
1208 if (r == 0)
1209 return 0;
1210
1211 return dns_name_equal(a, b);
1212}
1213
1d3db294 1214static int nsec3_hashed_domain(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
6f76ec5a 1215 _cleanup_free_ char *l = NULL, *hashed_domain = NULL;
105e1512 1216 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
6f76ec5a
TG
1217 int hashed_size;
1218
1219 assert(nsec3);
1220 assert(domain);
1221 assert(zone);
1222 assert(ret);
1223
1224 hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
1225 if (hashed_size < 0)
1226 return hashed_size;
1227
1228 l = base32hexmem(hashed, hashed_size, false);
1229 if (!l)
1230 return -ENOMEM;
1231
1232 hashed_domain = strjoin(l, ".", zone, NULL);
1233 if (!hashed_domain)
1234 return -ENOMEM;
1235
1236 *ret = hashed_domain;
1237 hashed_domain = NULL;
1238
1239 return hashed_size;
1240}
1241
35ad41d3
TG
1242/* See RFC 5155, Section 8
1243 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1244 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1245 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1246 * matches the wildcard domain.
1247 *
1248 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1249 * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1250 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1251 * to conclude anything we indicate this by returning NO_RR. */
d3760be0 1252static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
35ad41d3 1253 _cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL;
d1511b33 1254 const char *zone, *p, *pp = NULL;
35ad41d3 1255 DnsResourceRecord *rr, *enclosure_rr, *suffix_rr, *wildcard_rr = NULL;
105e1512
LP
1256 DnsAnswerFlags flags;
1257 int hashed_size, r;
35ad41d3 1258 bool a, no_closer = false, no_wildcard = false, optout = false;
72667f08
LP
1259
1260 assert(key);
1261 assert(result);
1262
d1511b33
TG
1263 /* First step, find the zone name and the NSEC3 parameters of the zone.
1264 * it is sufficient to look for the longest common suffix we find with
1265 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1266 * records from a given zone in a response must use the same
1267 * parameters. */
1268 zone = DNS_RESOURCE_KEY_NAME(key);
13b78323 1269 for (;;) {
db5b0e92 1270 DNS_ANSWER_FOREACH_FLAGS(suffix_rr, flags, answer) {
db5b0e92
LP
1271 r = nsec3_is_good(suffix_rr, flags, NULL);
1272 if (r < 0)
1273 return r;
1274 if (r == 0)
13b78323
LP
1275 continue;
1276
d1511b33 1277 r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr->key), 1, zone);
13b78323
LP
1278 if (r < 0)
1279 return r;
1280 if (r > 0)
d1511b33 1281 goto found_zone;
13b78323
LP
1282 }
1283
1284 /* Strip one label from the front */
d1511b33 1285 r = dns_name_parent(&zone);
13b78323
LP
1286 if (r < 0)
1287 return r;
1288 if (r == 0)
1289 break;
1290 }
1291
1292 *result = DNSSEC_NSEC_NO_RR;
1293 return 0;
1294
d1511b33 1295found_zone:
13b78323 1296 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
105e1512
LP
1297 p = DNS_RESOURCE_KEY_NAME(key);
1298 for (;;) {
6f76ec5a 1299 _cleanup_free_ char *hashed_domain = NULL;
72667f08 1300
6f76ec5a 1301 hashed_size = nsec3_hashed_domain(suffix_rr, p, zone, &hashed_domain);
db5b0e92
LP
1302 if (hashed_size == -EOPNOTSUPP) {
1303 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1304 return 0;
1305 }
1306 if (hashed_size < 0)
1307 return hashed_size;
72667f08 1308
d1511b33 1309 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
db5b0e92 1310
d1511b33 1311 r = nsec3_is_good(enclosure_rr, flags, suffix_rr);
72667f08
LP
1312 if (r < 0)
1313 return r;
105e1512
LP
1314 if (r == 0)
1315 continue;
1316
d1511b33 1317 if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
db5b0e92 1318 continue;
105e1512 1319
d1511b33 1320 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
72667f08
LP
1321 if (r < 0)
1322 return r;
ed29bfdc
LP
1323 if (r > 0) {
1324 a = flags & DNS_ANSWER_AUTHENTICATED;
13b78323 1325 goto found_closest_encloser;
ed29bfdc 1326 }
105e1512
LP
1327 }
1328
1329 /* We didn't find the closest encloser with this name,
1330 * but let's remember this domain name, it might be
1331 * the next closer name */
1332
1333 pp = p;
1334
1335 /* Strip one label from the front */
1336 r = dns_name_parent(&p);
1337 if (r < 0)
1338 return r;
1339 if (r == 0)
72667f08 1340 break;
105e1512 1341 }
72667f08 1342
105e1512
LP
1343 *result = DNSSEC_NSEC_NO_RR;
1344 return 0;
72667f08 1345
13b78323 1346found_closest_encloser:
105e1512 1347 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 1348
105e1512 1349 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
d1511b33 1350 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
105e1512 1351 return -EBADMSG;
72667f08 1352
105e1512
LP
1353 /* Ensure that this data is from the delegated domain
1354 * (i.e. originates from the "lower" DNS server), and isn't
1355 * just glue records (i.e. doesn't originate from the "upper"
1356 * DNS server). */
d1511b33
TG
1357 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1358 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
105e1512 1359 return -EBADMSG;
72667f08 1360
105e1512
LP
1361 if (!pp) {
1362 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
146035b3
TG
1363 if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
1364 *result = DNSSEC_NSEC_FOUND;
1365 else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
1366 *result = DNSSEC_NSEC_CNAME;
1367 else
1368 *result = DNSSEC_NSEC_NODATA;
1369
d3760be0
LP
1370 if (authenticated)
1371 *authenticated = a;
1372 if (ttl)
1373 *ttl = enclosure_rr->ttl;
146035b3 1374
105e1512
LP
1375 return 0;
1376 }
72667f08 1377
35ad41d3
TG
1378 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1379
1380 wildcard = strappend("*.", p);
1381 if (!wildcard)
1382 return -ENOMEM;
1383
1384 r = nsec3_hashed_domain(enclosure_rr, wildcard, zone, &wildcard_domain);
105e1512
LP
1385 if (r < 0)
1386 return r;
1387 if (r != hashed_size)
1388 return -EBADMSG;
72667f08 1389
6f76ec5a 1390 r = nsec3_hashed_domain(enclosure_rr, pp, zone, &next_closer_domain);
105e1512
LP
1391 if (r < 0)
1392 return r;
1393 if (r != hashed_size)
1394 return -EBADMSG;
72667f08 1395
105e1512
LP
1396 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1397 _cleanup_free_ char *label = NULL, *next_hashed_domain = NULL;
105e1512 1398
db5b0e92 1399 r = nsec3_is_good(rr, flags, suffix_rr);
105e1512
LP
1400 if (r < 0)
1401 return r;
1402 if (r == 0)
1403 continue;
1404
1405 label = base32hexmem(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, false);
1406 if (!label)
1407 return -ENOMEM;
1408
b2c2a1b9 1409 next_hashed_domain = strjoin(label, ".", zone, NULL);
105e1512
LP
1410 if (!next_hashed_domain)
1411 return -ENOMEM;
1412
1413 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1414 if (r < 0)
1415 return r;
1416 if (r > 0) {
1417 if (rr->nsec3.flags & 1)
35ad41d3 1418 optout = true;
105e1512 1419
35ad41d3
TG
1420 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1421
1422 no_closer = true;
1423 }
1424
1425 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
1426 if (r < 0)
1427 return r;
1428 if (r > 0) {
1429 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1430
1431 wildcard_rr = rr;
1432 }
1433
1434 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
1435 if (r < 0)
1436 return r;
1437 if (r > 0) {
1438 if (rr->nsec3.flags & 1)
1439 /* This only makes sense if we have a wildcard delegation, which is
1440 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1441 * this not happening, so hence cannot simply conclude NXDOMAIN as
1442 * we would wish */
1443 optout = true;
1444
1445 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1446
1447 no_wildcard = true;
105e1512
LP
1448 }
1449 }
1450
35ad41d3
TG
1451 if (wildcard_rr && no_wildcard)
1452 return -EBADMSG;
1453
1454 if (!no_closer) {
1455 *result = DNSSEC_NSEC_NO_RR;
35ad41d3
TG
1456 return 0;
1457 }
1458
1459 if (wildcard_rr) {
1460 /* A wildcard exists that matches our query. */
1461 if (optout)
1462 /* This is not specified in any RFC to the best of my knowledge, but
1463 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1464 * it means that we cannot prove that the source of synthesis is
1465 * correct, as there may be a closer match. */
1466 *result = DNSSEC_NSEC_OPTOUT;
1467 else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1468 *result = DNSSEC_NSEC_FOUND;
1469 else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1470 *result = DNSSEC_NSEC_CNAME;
1471 else
1472 *result = DNSSEC_NSEC_NODATA;
1473 } else {
1474 if (optout)
1475 /* The RFC only specifies that we have to care for optout for NODATA for
1476 * DS records. However, children of an insecure opt-out delegation should
1477 * also be considered opt-out, rather than verified NXDOMAIN.
1478 * Note that we do not require a proof of wildcard non-existence if the
1479 * next closer domain is covered by an opt-out, as that would not provide
1480 * any additional information. */
1481 *result = DNSSEC_NSEC_OPTOUT;
1482 else if (no_wildcard)
1483 *result = DNSSEC_NSEC_NXDOMAIN;
1484 else {
1485 *result = DNSSEC_NSEC_NO_RR;
1486
1487 return 0;
1488 }
1489 }
1490
d3760be0
LP
1491 if (authenticated)
1492 *authenticated = a;
1493
1494 if (ttl)
1495 *ttl = enclosure_rr->ttl;
35ad41d3 1496
105e1512
LP
1497 return 0;
1498}
1499
d3760be0 1500int dnssec_test_nsec(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
105e1512
LP
1501 DnsResourceRecord *rr;
1502 bool have_nsec3 = false;
1503 DnsAnswerFlags flags;
1504 int r;
1505
1506 assert(key);
1507 assert(result);
1508
1509 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1510
1511 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1512
1513 if (rr->key->class != key->class)
1514 continue;
1515
105e1512
LP
1516 switch (rr->key->type) {
1517
1518 case DNS_TYPE_NSEC:
1519
1520 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
1521 if (r < 0)
1522 return r;
1523 if (r > 0) {
146035b3
TG
1524 if (bitmap_isset(rr->nsec.types, key->type))
1525 *result = DNSSEC_NSEC_FOUND;
1526 else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
1527 *result = DNSSEC_NSEC_CNAME;
1528 else
1529 *result = DNSSEC_NSEC_NODATA;
d3760be0
LP
1530
1531 if (authenticated)
1532 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1533 if (ttl)
1534 *ttl = rr->ttl;
1535
72667f08
LP
1536 return 0;
1537 }
1538
105e1512
LP
1539 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
1540 if (r < 0)
1541 return r;
1542 if (r > 0) {
1543 *result = DNSSEC_NSEC_NXDOMAIN;
d3760be0
LP
1544
1545 if (authenticated)
1546 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1547 if (ttl)
1548 *ttl = rr->ttl;
1549
105e1512
LP
1550 return 0;
1551 }
72667f08 1552 break;
72667f08 1553
105e1512
LP
1554 case DNS_TYPE_NSEC3:
1555 have_nsec3 = true;
72667f08
LP
1556 break;
1557 }
1558 }
1559
105e1512
LP
1560 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1561 if (have_nsec3)
d3760be0 1562 return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
105e1512 1563
72667f08
LP
1564 /* No approproate NSEC RR found, report this. */
1565 *result = DNSSEC_NSEC_NO_RR;
1566 return 0;
1567}
1568
24710c48
LP
1569static const char* const dnssec_mode_table[_DNSSEC_MODE_MAX] = {
1570 [DNSSEC_NO] = "no",
1ed8c0fb 1571 [DNSSEC_ALLOW_DOWNGRADE] = "allow-downgrade",
24710c48
LP
1572 [DNSSEC_YES] = "yes",
1573};
1574DEFINE_STRING_TABLE_LOOKUP(dnssec_mode, DnssecMode);
547973de
LP
1575
1576static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
1577 [DNSSEC_VALIDATED] = "validated",
1578 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
1579 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
1580 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
1581 [DNSSEC_NO_SIGNATURE] = "no-signature",
1582 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 1583 [DNSSEC_UNSIGNED] = "unsigned",
547973de 1584 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 1585 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
b652d4a2 1586 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
547973de
LP
1587};
1588DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);