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