]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
resolved: some RR types may appear only or not at all in a zone apex
[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];
2b442ac8 511 DnsResourceRecord **list, *rr;
588c53d0 512 const char *source, *name;
2b442ac8 513 gcry_md_hd_t md = NULL;
ca994e85 514 int r, md_algorithm;
2b442ac8 515 size_t k, n = 0;
588c53d0
LP
516 size_t hash_size;
517 void *hash;
7715f91d 518 bool wildcard;
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
588c53d0
LP
547 name = DNS_RESOURCE_KEY_NAME(key);
548
549 /* Some keys may only appear signed in the zone apex, and are invalid anywhere else. (SOA, NS...) */
550 if (dns_type_apex_only(rrsig->rrsig.type_covered)) {
551 r = dns_name_equal(rrsig->rrsig.signer, name);
552 if (r < 0)
553 return r;
554 if (r == 0) {
555 *result = DNSSEC_INVALID;
556 return 0;
557 }
558 }
559
560 /* OTOH DS RRs may not appear in the zone apex, but are valid everywhere else. */
561 if (rrsig->rrsig.type_covered == DNS_TYPE_DS) {
562 r = dns_name_equal(rrsig->rrsig.signer, name);
563 if (r < 0)
564 return r;
565 if (r > 0) {
566 *result = DNSSEC_INVALID;
567 return 0;
568 }
569 }
570
7715f91d 571 /* Determine the "Source of Synthesis" and whether this is a wildcard RRSIG */
588c53d0 572 r = dns_name_suffix(name, rrsig->rrsig.labels, &source);
7715f91d
LP
573 if (r < 0)
574 return r;
e8233bce
LP
575 if (r > 0 && !dns_type_may_wildcard(rrsig->rrsig.type_covered)) {
576 /* We refuse to validate NSEC3 or SOA RRs that are synthesized from wildcards */
577 *result = DNSSEC_INVALID;
578 return 0;
579 }
7160eb1b
LP
580 if (r == 1) {
581 /* If we stripped a single label, then let's see if that maybe was "*". If so, we are not really
582 * synthesized from a wildcard, we are the wildcard itself. Treat that like a normal name. */
588c53d0 583 r = dns_name_startswith(name, "*");
7160eb1b
LP
584 if (r < 0)
585 return r;
586 if (r > 0)
588c53d0 587 source = name;
7160eb1b
LP
588
589 wildcard = r == 0;
590 } else
591 wildcard = r > 0;
7715f91d 592
2b442ac8 593 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
0f23174c 594 list = newa(DnsResourceRecord *, dns_answer_size(a));
2b442ac8
LP
595
596 DNS_ANSWER_FOREACH(rr, a) {
597 r = dns_resource_key_equal(key, rr->key);
598 if (r < 0)
599 return r;
600 if (r == 0)
601 continue;
602
603 /* We need the wire format for ordering, and digest calculation */
604 r = dns_resource_record_to_wire_format(rr, true);
605 if (r < 0)
606 return r;
607
608 list[n++] = rr;
935a999f
TG
609
610 if (n > VERIFY_RRS_MAX)
611 return -E2BIG;
2b442ac8
LP
612 }
613
614 if (n <= 0)
615 return -ENODATA;
616
617 /* Bring the RRs into canonical order */
6c5e8fbf 618 qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare);
2b442ac8
LP
619
620 /* OK, the RRs are now in canonical order. Let's calculate the digest */
fbf1a66d 621 initialize_libgcrypt();
2b442ac8 622
ca994e85 623 hash_size = gcry_md_get_algo_dlen(md_algorithm);
fbf1a66d 624 assert(hash_size > 0);
2b442ac8 625
ca994e85 626 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
627 if (!md)
628 return -EIO;
629
630 md_add_uint16(md, rrsig->rrsig.type_covered);
631 md_add_uint8(md, rrsig->rrsig.algorithm);
632 md_add_uint8(md, rrsig->rrsig.labels);
633 md_add_uint32(md, rrsig->rrsig.original_ttl);
634 md_add_uint32(md, rrsig->rrsig.expiration);
635 md_add_uint32(md, rrsig->rrsig.inception);
636 md_add_uint16(md, rrsig->rrsig.key_tag);
637
638 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
639 if (r < 0)
640 goto finish;
641 gcry_md_write(md, wire_format_name, r);
642
7715f91d
LP
643 /* Convert the source of synthesis into wire format */
644 r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true);
645 if (r < 0)
646 goto finish;
647
2b442ac8
LP
648 for (k = 0; k < n; k++) {
649 size_t l;
7715f91d 650
2b442ac8
LP
651 rr = list[k];
652
7715f91d
LP
653 /* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */
654 if (wildcard)
e7ff0e0b 655 gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
2b442ac8
LP
656 gcry_md_write(md, wire_format_name, r);
657
658 md_add_uint16(md, rr->key->type);
659 md_add_uint16(md, rr->key->class);
660 md_add_uint32(md, rrsig->rrsig.original_ttl);
661
85aeaccc 662 l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
2b442ac8
LP
663 assert(l <= 0xFFFF);
664
665 md_add_uint16(md, (uint16_t) l);
85aeaccc 666 gcry_md_write(md, DNS_RESOURCE_RECORD_RDATA(rr), l);
2b442ac8
LP
667 }
668
669 hash = gcry_md_read(md, 0);
670 if (!hash) {
671 r = -EIO;
672 goto finish;
673 }
674
e0240c64
LP
675 switch (rrsig->rrsig.algorithm) {
676
677 case DNSSEC_ALGORITHM_RSASHA1:
678 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
679 case DNSSEC_ALGORITHM_RSASHA256:
680 case DNSSEC_ALGORITHM_RSASHA512:
681 r = dnssec_rsa_verify(
ca994e85 682 gcry_md_algo_name(md_algorithm),
e0240c64
LP
683 hash, hash_size,
684 rrsig,
685 dnskey);
686 break;
687
688 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
689 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
690 r = dnssec_ecdsa_verify(
ca994e85 691 gcry_md_algo_name(md_algorithm),
e0240c64
LP
692 rrsig->rrsig.algorithm,
693 hash, hash_size,
694 rrsig,
695 dnskey);
696 break;
697 }
698
2b442ac8
LP
699 if (r < 0)
700 goto finish;
701
0c7bff0a
LP
702 if (!r)
703 *result = DNSSEC_INVALID;
704 else if (wildcard)
705 *result = DNSSEC_VALIDATED_WILDCARD;
706 else
707 *result = DNSSEC_VALIDATED;
547973de 708 r = 0;
2b442ac8
LP
709
710finish:
711 gcry_md_close(md);
712 return r;
713}
714
0c857028 715int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
2b442ac8
LP
716
717 assert(rrsig);
718 assert(dnskey);
719
720 /* Checks if the specified DNSKEY RR matches the key used for
721 * the signature in the specified RRSIG RR */
722
723 if (rrsig->key->type != DNS_TYPE_RRSIG)
724 return -EINVAL;
725
726 if (dnskey->key->type != DNS_TYPE_DNSKEY)
727 return 0;
728 if (dnskey->key->class != rrsig->key->class)
729 return 0;
730 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
731 return 0;
0c857028 732 if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
28b8191e 733 return 0;
2b442ac8
LP
734 if (dnskey->dnskey.protocol != 3)
735 return 0;
736 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
737 return 0;
738
0c857028 739 if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
2b442ac8
LP
740 return 0;
741
15accc27 742 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
743}
744
105e1512 745int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
e7ff0e0b
LP
746 int r;
747
2b442ac8
LP
748 assert(key);
749 assert(rrsig);
750
751 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
752
753 if (rrsig->key->type != DNS_TYPE_RRSIG)
754 return 0;
755 if (rrsig->key->class != key->class)
756 return 0;
757 if (rrsig->rrsig.type_covered != key->type)
758 return 0;
759
e7ff0e0b
LP
760 /* Make sure signer is a parent of the RRset */
761 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig->key), rrsig->rrsig.signer);
762 if (r <= 0)
763 return r;
764
765 /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
766 r = dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig->key));
767 if (r < 0)
768 return r;
769 if (r < rrsig->rrsig.labels)
770 return 0;
771
2b442ac8
LP
772 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key));
773}
774
ee3d6aff
LP
775static int dnssec_fix_rrset_ttl(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord *rrsig, usec_t realtime) {
776 DnsResourceRecord *rr;
777 int r;
778
779 assert(key);
780 assert(rrsig);
781
782 DNS_ANSWER_FOREACH(rr, a) {
783 r = dns_resource_key_equal(key, rr->key);
784 if (r < 0)
785 return r;
786 if (r == 0)
787 continue;
788
789 /* Pick the TTL as the minimum of the RR's TTL, the
790 * RR's original TTL according to the RRSIG and the
791 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
792 rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
793 rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
794 }
795
796 return 0;
797}
798
2a326321
LP
799int dnssec_verify_rrset_search(
800 DnsAnswer *a,
0c857028 801 const DnsResourceKey *key,
2a326321 802 DnsAnswer *validated_dnskeys,
547973de 803 usec_t realtime,
0c7bff0a
LP
804 DnssecResult *result,
805 DnsResourceRecord **ret_rrsig) {
2a326321 806
203f1b35 807 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
2b442ac8
LP
808 DnsResourceRecord *rrsig;
809 int r;
810
811 assert(key);
547973de 812 assert(result);
2b442ac8 813
105e1512 814 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
2b442ac8
LP
815
816 if (!a || a->n_rrs <= 0)
817 return -ENODATA;
818
819 /* Iterate through each RRSIG RR. */
820 DNS_ANSWER_FOREACH(rrsig, a) {
821 DnsResourceRecord *dnskey;
105e1512 822 DnsAnswerFlags flags;
2b442ac8 823
203f1b35 824 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
825 r = dnssec_key_match_rrsig(key, rrsig);
826 if (r < 0)
827 return r;
828 if (r == 0)
829 continue;
830
831 found_rrsig = true;
832
547973de 833 /* Look for a matching key */
105e1512 834 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
547973de 835 DnssecResult one_result;
2b442ac8 836
105e1512
LP
837 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
838 continue;
839
203f1b35 840 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
0c857028 841 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
2b442ac8
LP
842 if (r < 0)
843 return r;
844 if (r == 0)
845 continue;
846
2a326321
LP
847 /* Take the time here, if it isn't set yet, so
848 * that we do all validations with the same
849 * time. */
850 if (realtime == USEC_INFINITY)
851 realtime = now(CLOCK_REALTIME);
852
2b442ac8
LP
853 /* Yay, we found a matching RRSIG with a matching
854 * DNSKEY, awesome. Now let's verify all entries of
855 * the RRSet against the RRSIG and DNSKEY
856 * combination. */
857
547973de 858 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 859 if (r < 0)
2b442ac8 860 return r;
203f1b35
LP
861
862 switch (one_result) {
863
864 case DNSSEC_VALIDATED:
0c7bff0a 865 case DNSSEC_VALIDATED_WILDCARD:
203f1b35 866 /* Yay, the RR has been validated,
ee3d6aff
LP
867 * return immediately, but fix up the expiry */
868 r = dnssec_fix_rrset_ttl(a, key, rrsig, realtime);
869 if (r < 0)
870 return r;
871
0c7bff0a
LP
872 if (ret_rrsig)
873 *ret_rrsig = rrsig;
874
875 *result = one_result;
547973de 876 return 0;
2b442ac8 877
203f1b35
LP
878 case DNSSEC_INVALID:
879 /* If the signature is invalid, let's try another
880 key and/or signature. After all they
881 key_tags and stuff are not unique, and
882 might be shared by multiple keys. */
883 found_invalid = true;
884 continue;
885
886 case DNSSEC_UNSUPPORTED_ALGORITHM:
887 /* If the key algorithm is
888 unsupported, try another
889 RRSIG/DNSKEY pair, but remember we
890 encountered this, so that we can
891 return a proper error when we
892 encounter nothing better. */
893 found_unsupported_algorithm = true;
894 continue;
895
896 case DNSSEC_SIGNATURE_EXPIRED:
897 /* If the signature is expired, try
898 another one, but remember it, so
899 that we can return this */
900 found_expired_rrsig = true;
901 continue;
902
903 default:
904 assert_not_reached("Unexpected DNSSEC validation result");
905 }
2b442ac8
LP
906 }
907 }
908
203f1b35
LP
909 if (found_expired_rrsig)
910 *result = DNSSEC_SIGNATURE_EXPIRED;
911 else if (found_unsupported_algorithm)
912 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
913 else if (found_invalid)
547973de
LP
914 *result = DNSSEC_INVALID;
915 else if (found_rrsig)
916 *result = DNSSEC_MISSING_KEY;
917 else
918 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 919
0c7bff0a
LP
920 if (ret_rrsig)
921 *ret_rrsig = NULL;
922
547973de 923 return 0;
2b442ac8
LP
924}
925
105e1512
LP
926int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
927 DnsResourceRecord *rr;
928 int r;
929
930 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
931
932 DNS_ANSWER_FOREACH(rr, a) {
933 r = dnssec_key_match_rrsig(key, rr);
934 if (r < 0)
935 return r;
936 if (r > 0)
937 return 1;
938 }
939
940 return 0;
941}
942
2b442ac8 943int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
2b442ac8
LP
944 size_t c = 0;
945 int r;
946
947 /* Converts the specified hostname into DNSSEC canonicalized
948 * form. */
949
950 if (buffer_max < 2)
951 return -ENOBUFS;
952
953 for (;;) {
2b442ac8
LP
954 r = dns_label_unescape(&n, buffer, buffer_max);
955 if (r < 0)
956 return r;
957 if (r == 0)
958 break;
959 if (r > 0) {
960 int k;
961
962 /* DNSSEC validation is always done on the ASCII version of the label */
963 k = dns_label_apply_idna(buffer, r, buffer, buffer_max);
964 if (k < 0)
965 return k;
966 if (k > 0)
967 r = k;
968 }
969
970 if (buffer_max < (size_t) r + 2)
971 return -ENOBUFS;
972
973 /* The DNSSEC canonical form is not clear on what to
974 * do with dots appearing in labels, the way DNS-SD
975 * does it. Refuse it for now. */
976
977 if (memchr(buffer, '.', r))
978 return -EINVAL;
979
b577e3d5 980 ascii_strlower_n(buffer, (size_t) r);
2b442ac8
LP
981 buffer[r] = '.';
982
983 buffer += r + 1;
984 c += r + 1;
985
986 buffer_max -= r + 1;
987 }
988
989 if (c <= 0) {
990 /* Not even a single label: this is the root domain name */
991
992 assert(buffer_max > 2);
993 buffer[0] = '.';
994 buffer[1] = 0;
995
996 return 1;
997 }
998
999 return (int) c;
1000}
1001
ca994e85 1002static int digest_to_gcrypt_md(uint8_t algorithm) {
a1972a91 1003
fbf1a66d 1004 /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
a1972a91
LP
1005
1006 switch (algorithm) {
1007
1008 case DNSSEC_DIGEST_SHA1:
1009 return GCRY_MD_SHA1;
1010
1011 case DNSSEC_DIGEST_SHA256:
1012 return GCRY_MD_SHA256;
1013
af22c65b
LP
1014 case DNSSEC_DIGEST_SHA384:
1015 return GCRY_MD_SHA384;
1016
a1972a91
LP
1017 default:
1018 return -EOPNOTSUPP;
1019 }
1020}
1021
0c857028 1022int dnssec_verify_dnskey(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
2b442ac8 1023 char owner_name[DNSSEC_CANONICAL_HOSTNAME_MAX];
a1972a91
LP
1024 gcry_md_hd_t md = NULL;
1025 size_t hash_size;
ca994e85 1026 int md_algorithm, r;
2b442ac8 1027 void *result;
2b442ac8
LP
1028
1029 assert(dnskey);
1030 assert(ds);
1031
1032 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
1033
1034 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1035 return -EINVAL;
1036 if (ds->key->type != DNS_TYPE_DS)
1037 return -EINVAL;
1038 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
1039 return -EKEYREJECTED;
0c857028
LP
1040 if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
1041 return -EKEYREJECTED;
2b442ac8
LP
1042 if (dnskey->dnskey.protocol != 3)
1043 return -EKEYREJECTED;
1044
2b442ac8
LP
1045 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
1046 return 0;
0c857028 1047 if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
2b442ac8
LP
1048 return 0;
1049
0638401a
LP
1050 initialize_libgcrypt();
1051
ca994e85
LP
1052 md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type);
1053 if (md_algorithm < 0)
1054 return md_algorithm;
2b442ac8 1055
ca994e85 1056 hash_size = gcry_md_get_algo_dlen(md_algorithm);
a1972a91 1057 assert(hash_size > 0);
2b442ac8 1058
a1972a91
LP
1059 if (ds->ds.digest_size != hash_size)
1060 return 0;
2b442ac8 1061
a1972a91
LP
1062 r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name));
1063 if (r < 0)
1064 return r;
2b442ac8 1065
ca994e85 1066 gcry_md_open(&md, md_algorithm, 0);
2b442ac8
LP
1067 if (!md)
1068 return -EIO;
1069
2b442ac8 1070 gcry_md_write(md, owner_name, r);
0c857028
LP
1071 if (mask_revoke)
1072 md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
1073 else
1074 md_add_uint16(md, dnskey->dnskey.flags);
2b442ac8
LP
1075 md_add_uint8(md, dnskey->dnskey.protocol);
1076 md_add_uint8(md, dnskey->dnskey.algorithm);
1077 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1078
1079 result = gcry_md_read(md, 0);
1080 if (!result) {
1081 r = -EIO;
1082 goto finish;
1083 }
1084
1085 r = memcmp(result, ds->ds.digest, ds->ds.digest_size) != 0;
1086
1087finish:
1088 gcry_md_close(md);
1089 return r;
1090}
24710c48 1091
547973de
LP
1092int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
1093 DnsResourceRecord *ds;
105e1512 1094 DnsAnswerFlags flags;
547973de
LP
1095 int r;
1096
1097 assert(dnskey);
1098
1099 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1100 return 0;
1101
105e1512
LP
1102 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1103
1104 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1105 continue;
547973de
LP
1106
1107 if (ds->key->type != DNS_TYPE_DS)
1108 continue;
1109
d1c4ee32
LP
1110 if (ds->key->class != dnskey->key->class)
1111 continue;
1112
1113 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key));
1114 if (r < 0)
1115 return r;
1116 if (r == 0)
1117 continue;
1118
0c857028
LP
1119 r = dnssec_verify_dnskey(dnskey, ds, false);
1120 if (r == -EKEYREJECTED)
1121 return 0; /* The DNSKEY is revoked or otherwise invalid, we won't bless it */
547973de
LP
1122 if (r < 0)
1123 return r;
1124 if (r > 0)
1125 return 1;
1126 }
1127
1128 return 0;
1129}
1130
d15ad742
LP
1131static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
1132
1133 /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
1134
1135 switch (algorithm) {
1136
1137 case NSEC3_ALGORITHM_SHA1:
1138 return GCRY_MD_SHA1;
1139
1140 default:
1141 return -EOPNOTSUPP;
1142 }
1143}
1144
1d3db294 1145int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
72667f08
LP
1146 uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
1147 gcry_md_hd_t md = NULL;
1148 size_t hash_size;
1149 int algorithm;
1150 void *result;
1151 unsigned k;
1152 int r;
1153
1154 assert(nsec3);
1155 assert(name);
1156 assert(ret);
1157
1158 if (nsec3->key->type != DNS_TYPE_NSEC3)
1159 return -EINVAL;
1160
1d3db294
LP
1161 if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX) {
1162 log_debug("Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3));
a8f158b9 1163 return -EOPNOTSUPP;
1d3db294 1164 }
a8f158b9 1165
d15ad742 1166 algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
72667f08
LP
1167 if (algorithm < 0)
1168 return algorithm;
1169
1170 initialize_libgcrypt();
1171
1172 hash_size = gcry_md_get_algo_dlen(algorithm);
1173 assert(hash_size > 0);
1174
1175 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1176 return -EINVAL;
1177
1178 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1179 if (r < 0)
1180 return r;
1181
1182 gcry_md_open(&md, algorithm, 0);
1183 if (!md)
1184 return -EIO;
1185
1186 gcry_md_write(md, wire_format, r);
1187 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1188
1189 result = gcry_md_read(md, 0);
1190 if (!result) {
1191 r = -EIO;
1192 goto finish;
1193 }
1194
1195 for (k = 0; k < nsec3->nsec3.iterations; k++) {
1196 uint8_t tmp[hash_size];
1197 memcpy(tmp, result, hash_size);
1198
1199 gcry_md_reset(md);
1200 gcry_md_write(md, tmp, hash_size);
1201 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1202
1203 result = gcry_md_read(md, 0);
1204 if (!result) {
1205 r = -EIO;
1206 goto finish;
1207 }
1208 }
1209
1210 memcpy(ret, result, hash_size);
1211 r = (int) hash_size;
1212
1213finish:
1214 gcry_md_close(md);
1215 return r;
1216}
1217
3f5ecaad 1218static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
db5b0e92
LP
1219 const char *a, *b;
1220 int r;
1221
1222 assert(rr);
1223
db5b0e92
LP
1224 if (rr->key->type != DNS_TYPE_NSEC3)
1225 return 0;
1226
1227 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1228 if (!IN_SET(rr->nsec3.flags, 0, 1))
1229 return 0;
1230
d15ad742
LP
1231 /* Ignore NSEC3 RRs whose algorithm we don't know */
1232 if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
1233 return 0;
a8f158b9
LP
1234 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1235 if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1236 return 0;
d15ad742 1237
db5b0e92
LP
1238 if (!nsec3)
1239 return 1;
1240
1241 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1242
1243 if (nsec3 == rr) /* Shortcut */
1244 return 1;
1245
1246 if (rr->key->class != nsec3->key->class)
1247 return 0;
1248 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1249 return 0;
1250 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1251 return 0;
1252 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1253 return 0;
1254 if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
1255 return 0;
1256
1257 a = DNS_RESOURCE_KEY_NAME(rr->key);
1258 r = dns_name_parent(&a); /* strip off hash */
1259 if (r < 0)
1260 return r;
1261 if (r == 0)
1262 return 0;
1263
1264 b = DNS_RESOURCE_KEY_NAME(nsec3->key);
1265 r = dns_name_parent(&b); /* strip off hash */
1266 if (r < 0)
1267 return r;
1268 if (r == 0)
1269 return 0;
1270
1271 return dns_name_equal(a, b);
1272}
1273
cdbffec0
LP
1274static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size, const char *zone, char **ret) {
1275 _cleanup_free_ char *l = NULL;
1276 char *j;
1277
1278 assert(hashed);
1279 assert(hashed_size > 0);
1280 assert(zone);
1281 assert(ret);
1282
1283 l = base32hexmem(hashed, hashed_size, false);
1284 if (!l)
1285 return -ENOMEM;
1286
1287 j = strjoin(l, ".", zone, NULL);
1288 if (!j)
1289 return -ENOMEM;
1290
1291 *ret = j;
1292 return (int) hashed_size;
1293}
1294
1295static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
105e1512 1296 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
6f76ec5a
TG
1297 int hashed_size;
1298
1299 assert(nsec3);
1300 assert(domain);
1301 assert(zone);
1302 assert(ret);
1303
1304 hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
1305 if (hashed_size < 0)
1306 return hashed_size;
1307
cdbffec0 1308 return nsec3_hashed_domain_format(hashed, (size_t) hashed_size, zone, ret);
6f76ec5a
TG
1309}
1310
35ad41d3
TG
1311/* See RFC 5155, Section 8
1312 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1313 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1314 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1315 * matches the wildcard domain.
1316 *
1317 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
1318 * that there is no proof either way. The latter is the case if a the proof of non-existence of a given
1319 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1320 * to conclude anything we indicate this by returning NO_RR. */
d3760be0 1321static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
d41084a5
LP
1322 _cleanup_free_ char *next_closer_domain = NULL, *wildcard_domain = NULL;
1323 const char *zone, *p, *pp = NULL, *wildcard;
7e35195f 1324 DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
105e1512
LP
1325 DnsAnswerFlags flags;
1326 int hashed_size, r;
35ad41d3 1327 bool a, no_closer = false, no_wildcard = false, optout = false;
72667f08
LP
1328
1329 assert(key);
1330 assert(result);
1331
d1511b33
TG
1332 /* First step, find the zone name and the NSEC3 parameters of the zone.
1333 * it is sufficient to look for the longest common suffix we find with
1334 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1335 * records from a given zone in a response must use the same
1336 * parameters. */
1337 zone = DNS_RESOURCE_KEY_NAME(key);
13b78323 1338 for (;;) {
7e35195f 1339 DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
3f5ecaad 1340 r = nsec3_is_good(zone_rr, NULL);
db5b0e92
LP
1341 if (r < 0)
1342 return r;
1343 if (r == 0)
13b78323
LP
1344 continue;
1345
7e35195f 1346 r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(zone_rr->key), 1, zone);
13b78323
LP
1347 if (r < 0)
1348 return r;
1349 if (r > 0)
d1511b33 1350 goto found_zone;
13b78323
LP
1351 }
1352
1353 /* Strip one label from the front */
d1511b33 1354 r = dns_name_parent(&zone);
13b78323
LP
1355 if (r < 0)
1356 return r;
1357 if (r == 0)
1358 break;
1359 }
1360
1361 *result = DNSSEC_NSEC_NO_RR;
1362 return 0;
1363
d1511b33 1364found_zone:
13b78323 1365 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
105e1512
LP
1366 p = DNS_RESOURCE_KEY_NAME(key);
1367 for (;;) {
6f76ec5a 1368 _cleanup_free_ char *hashed_domain = NULL;
72667f08 1369
cdbffec0 1370 hashed_size = nsec3_hashed_domain_make(zone_rr, p, zone, &hashed_domain);
db5b0e92
LP
1371 if (hashed_size == -EOPNOTSUPP) {
1372 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1373 return 0;
1374 }
1375 if (hashed_size < 0)
1376 return hashed_size;
72667f08 1377
d1511b33 1378 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
db5b0e92 1379
3f5ecaad 1380 r = nsec3_is_good(enclosure_rr, zone_rr);
72667f08
LP
1381 if (r < 0)
1382 return r;
105e1512
LP
1383 if (r == 0)
1384 continue;
1385
d1511b33 1386 if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
db5b0e92 1387 continue;
105e1512 1388
d1511b33 1389 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain);
72667f08
LP
1390 if (r < 0)
1391 return r;
ed29bfdc
LP
1392 if (r > 0) {
1393 a = flags & DNS_ANSWER_AUTHENTICATED;
13b78323 1394 goto found_closest_encloser;
ed29bfdc 1395 }
105e1512
LP
1396 }
1397
1398 /* We didn't find the closest encloser with this name,
1399 * but let's remember this domain name, it might be
1400 * the next closer name */
1401
1402 pp = p;
1403
1404 /* Strip one label from the front */
1405 r = dns_name_parent(&p);
1406 if (r < 0)
1407 return r;
1408 if (r == 0)
72667f08 1409 break;
105e1512 1410 }
72667f08 1411
105e1512
LP
1412 *result = DNSSEC_NSEC_NO_RR;
1413 return 0;
72667f08 1414
13b78323 1415found_closest_encloser:
105e1512 1416 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 1417
105e1512 1418 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
d1511b33 1419 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
105e1512 1420 return -EBADMSG;
72667f08 1421
105e1512
LP
1422 /* Ensure that this data is from the delegated domain
1423 * (i.e. originates from the "lower" DNS server), and isn't
1424 * just glue records (i.e. doesn't originate from the "upper"
1425 * DNS server). */
d1511b33
TG
1426 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1427 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
105e1512 1428 return -EBADMSG;
72667f08 1429
105e1512
LP
1430 if (!pp) {
1431 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
146035b3
TG
1432 if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
1433 *result = DNSSEC_NSEC_FOUND;
1434 else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
1435 *result = DNSSEC_NSEC_CNAME;
1436 else
1437 *result = DNSSEC_NSEC_NODATA;
1438
d3760be0
LP
1439 if (authenticated)
1440 *authenticated = a;
1441 if (ttl)
1442 *ttl = enclosure_rr->ttl;
146035b3 1443
105e1512
LP
1444 return 0;
1445 }
72667f08 1446
35ad41d3
TG
1447 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1448
d41084a5 1449 wildcard = strjoina("*.", p);
cdbffec0 1450 r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
105e1512
LP
1451 if (r < 0)
1452 return r;
1453 if (r != hashed_size)
1454 return -EBADMSG;
72667f08 1455
cdbffec0 1456 r = nsec3_hashed_domain_make(enclosure_rr, pp, zone, &next_closer_domain);
105e1512
LP
1457 if (r < 0)
1458 return r;
1459 if (r != hashed_size)
1460 return -EBADMSG;
72667f08 1461
105e1512 1462 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
cdbffec0 1463 _cleanup_free_ char *next_hashed_domain = NULL;
105e1512 1464
3f5ecaad 1465 r = nsec3_is_good(rr, zone_rr);
105e1512
LP
1466 if (r < 0)
1467 return r;
1468 if (r == 0)
1469 continue;
1470
cdbffec0
LP
1471 r = nsec3_hashed_domain_format(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, zone, &next_hashed_domain);
1472 if (r < 0)
1473 return r;
105e1512
LP
1474
1475 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain);
1476 if (r < 0)
1477 return r;
1478 if (r > 0) {
1479 if (rr->nsec3.flags & 1)
35ad41d3 1480 optout = true;
105e1512 1481
35ad41d3
TG
1482 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1483
1484 no_closer = true;
1485 }
1486
1487 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain);
1488 if (r < 0)
1489 return r;
1490 if (r > 0) {
1491 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1492
1493 wildcard_rr = rr;
1494 }
1495
1496 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain);
1497 if (r < 0)
1498 return r;
1499 if (r > 0) {
1500 if (rr->nsec3.flags & 1)
1501 /* This only makes sense if we have a wildcard delegation, which is
1502 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1503 * this not happening, so hence cannot simply conclude NXDOMAIN as
1504 * we would wish */
1505 optout = true;
1506
1507 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1508
1509 no_wildcard = true;
105e1512
LP
1510 }
1511 }
1512
35ad41d3
TG
1513 if (wildcard_rr && no_wildcard)
1514 return -EBADMSG;
1515
1516 if (!no_closer) {
1517 *result = DNSSEC_NSEC_NO_RR;
35ad41d3
TG
1518 return 0;
1519 }
1520
1521 if (wildcard_rr) {
1522 /* A wildcard exists that matches our query. */
1523 if (optout)
1524 /* This is not specified in any RFC to the best of my knowledge, but
1525 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1526 * it means that we cannot prove that the source of synthesis is
1527 * correct, as there may be a closer match. */
1528 *result = DNSSEC_NSEC_OPTOUT;
1529 else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1530 *result = DNSSEC_NSEC_FOUND;
1531 else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1532 *result = DNSSEC_NSEC_CNAME;
1533 else
1534 *result = DNSSEC_NSEC_NODATA;
1535 } else {
1536 if (optout)
1537 /* The RFC only specifies that we have to care for optout for NODATA for
1538 * DS records. However, children of an insecure opt-out delegation should
1539 * also be considered opt-out, rather than verified NXDOMAIN.
1540 * Note that we do not require a proof of wildcard non-existence if the
1541 * next closer domain is covered by an opt-out, as that would not provide
1542 * any additional information. */
1543 *result = DNSSEC_NSEC_OPTOUT;
1544 else if (no_wildcard)
1545 *result = DNSSEC_NSEC_NXDOMAIN;
1546 else {
1547 *result = DNSSEC_NSEC_NO_RR;
1548
1549 return 0;
1550 }
1551 }
1552
d3760be0
LP
1553 if (authenticated)
1554 *authenticated = a;
1555
1556 if (ttl)
1557 *ttl = enclosure_rr->ttl;
35ad41d3 1558
105e1512
LP
1559 return 0;
1560}
1561
0c7bff0a 1562int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
105e1512
LP
1563 DnsResourceRecord *rr;
1564 bool have_nsec3 = false;
1565 DnsAnswerFlags flags;
1566 int r;
1567
1568 assert(key);
1569 assert(result);
1570
1571 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1572
1573 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1574
1575 if (rr->key->class != key->class)
1576 continue;
1577
105e1512
LP
1578 switch (rr->key->type) {
1579
1580 case DNS_TYPE_NSEC:
1581
1582 r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
1583 if (r < 0)
1584 return r;
1585 if (r > 0) {
146035b3
TG
1586 if (bitmap_isset(rr->nsec.types, key->type))
1587 *result = DNSSEC_NSEC_FOUND;
1588 else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
1589 *result = DNSSEC_NSEC_CNAME;
1590 else
1591 *result = DNSSEC_NSEC_NODATA;
d3760be0
LP
1592
1593 if (authenticated)
1594 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1595 if (ttl)
1596 *ttl = rr->ttl;
1597
72667f08
LP
1598 return 0;
1599 }
1600
105e1512
LP
1601 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key), rr->nsec.next_domain_name);
1602 if (r < 0)
1603 return r;
1604 if (r > 0) {
1605 *result = DNSSEC_NSEC_NXDOMAIN;
d3760be0
LP
1606
1607 if (authenticated)
1608 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1609 if (ttl)
1610 *ttl = rr->ttl;
1611
105e1512
LP
1612 return 0;
1613 }
72667f08 1614 break;
72667f08 1615
105e1512
LP
1616 case DNS_TYPE_NSEC3:
1617 have_nsec3 = true;
72667f08
LP
1618 break;
1619 }
1620 }
1621
105e1512
LP
1622 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1623 if (have_nsec3)
d3760be0 1624 return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
105e1512 1625
72667f08
LP
1626 /* No approproate NSEC RR found, report this. */
1627 *result = DNSSEC_NSEC_NO_RR;
1628 return 0;
1629}
1630
e926785a 1631int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
0c7bff0a
LP
1632 DnsResourceRecord *rr;
1633 DnsAnswerFlags flags;
1634 int r;
1635
1636 assert(name);
1637 assert(zone);
1638
1639 /* Checks whether there's an NSEC/NSEC3 that proves that the specified 'name' is non-existing in the specified
1640 * 'zone'. The 'zone' must be a suffix of the 'name'. */
1641
1642 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
1643 bool found = false;
1644
e926785a
LP
1645 if (rr->key->type != type && type != DNS_TYPE_ANY)
1646 continue;
1647
0c7bff0a
LP
1648 r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), zone);
1649 if (r < 0)
1650 return r;
1651 if (r == 0)
1652 continue;
1653
1654 switch (rr->key->type) {
1655
1656 case DNS_TYPE_NSEC:
1657 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name);
1658 if (r < 0)
1659 return r;
1660
1661 found = r > 0;
1662 break;
1663
1664 case DNS_TYPE_NSEC3: {
1665 _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
1666
1667 r = nsec3_is_good(rr, NULL);
1668 if (r < 0)
1669 return r;
1670 if (r == 0)
1671 break;
1672
1673 /* Format the domain we are testing with the NSEC3 RR's hash function */
1674 r = nsec3_hashed_domain_make(
1675 rr,
1676 name,
1677 zone,
1678 &hashed_domain);
1679 if (r < 0)
1680 return r;
1681 if ((size_t) r != rr->nsec3.next_hashed_name_size)
1682 break;
1683
1684 /* Format the NSEC3's next hashed name as proper domain name */
1685 r = nsec3_hashed_domain_format(
1686 rr->nsec3.next_hashed_name,
1687 rr->nsec3.next_hashed_name_size,
1688 zone,
1689 &next_hashed_domain);
1690 if (r < 0)
1691 return r;
1692
1693 r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain, next_hashed_domain);
1694 if (r < 0)
1695 return r;
1696
1697 found = r > 0;
1698 break;
1699 }
1700
1701 default:
1702 continue;
1703 }
1704
1705 if (found) {
1706 if (authenticated)
1707 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
1708 return 1;
1709 }
1710 }
1711
1712 return 0;
1713}
1714
e926785a
LP
1715static int dnssec_test_positive_wildcard_nsec3(
1716 DnsAnswer *answer,
1717 const char *name,
1718 const char *source,
1719 const char *zone,
1720 bool *authenticated) {
1721
1722 const char *next_closer = NULL;
1723 int r;
1724
1725 /* Run a positive NSEC3 wildcard proof. Specifically:
1726 *
1727 * A proof that the the "next closer" of the generating wildcard does not exist.
1728 *
1729 * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for
1730 * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name
1731 * exists for the NSEC3 RR and we are done.
1732 *
1733 * To prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f all we have to check is that
1734 * c.d.e.f does not exist. */
1735
1736 for (;;) {
1737 next_closer = name;
1738 r = dns_name_parent(&name);
1739 if (r < 0)
1740 return r;
1741 if (r == 0)
1742 return 0;
1743
1744 r = dns_name_equal(name, source);
1745 if (r < 0)
1746 return r;
1747 if (r > 0)
1748 break;
1749 }
1750
1751 return dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC3, next_closer, zone, authenticated);
1752}
1753
1754static int dnssec_test_positive_wildcard_nsec(
1755 DnsAnswer *answer,
1756 const char *name,
1757 const char *source,
1758 const char *zone,
1759 bool *_authenticated) {
1760
1761 bool authenticated = true;
1762 int r;
1763
1764 /* Run a positive NSEC wildcard proof. Specifically:
1765 *
1766 * A proof that there's neither a wildcard name nor a non-wildcard name that is a suffix of the name "name" and
1767 * a prefix of the synthesizing source "source" in the zone "zone".
1768 *
1769 * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4
1770 *
1771 * Note that if we want to prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f, then we
1772 * have to prove that none of the following exist:
1773 *
1774 * 1) a.b.c.d.e.f
1775 * 2) *.b.c.d.e.f
1776 * 3) b.c.d.e.f
1777 * 4) *.c.d.e.f
1778 * 5) c.d.e.f
1779 *
1780 */
1781
1782 for (;;) {
1783 _cleanup_free_ char *wc = NULL;
1784 bool a = false;
1785
1786 /* Check if there's an NSEC or NSEC3 RR that proves that the mame we determined is really non-existing,
1787 * i.e between the owner name and the next name of an NSEC RR. */
1788 r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, name, zone, &a);
1789 if (r <= 0)
1790 return r;
1791
1792 authenticated = authenticated && a;
1793
1794 /* Strip one label off */
1795 r = dns_name_parent(&name);
1796 if (r <= 0)
1797 return r;
1798
1799 /* Did we reach the source of synthesis? */
1800 r = dns_name_equal(name, source);
1801 if (r < 0)
1802 return r;
1803 if (r > 0) {
1804 /* Successful exit */
1805 *_authenticated = authenticated;
1806 return 1;
1807 }
1808
1809 /* Safety check, that the source of synthesis is still our suffix */
1810 r = dns_name_endswith(name, source);
1811 if (r < 0)
1812 return r;
1813 if (r == 0)
1814 return -EBADMSG;
1815
1816 /* Replace the label we stripped off with an asterisk */
1817 wc = strappend("*.", name);
1818 if (!wc)
1819 return -ENOMEM;
1820
1821 /* And check if the proof holds for the asterisk name, too */
1822 r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, wc, zone, &a);
1823 if (r <= 0)
1824 return r;
1825
1826 authenticated = authenticated && a;
1827 /* In the next iteration we'll check the non-asterisk-prefixed version */
1828 }
1829}
1830
1831int dnssec_test_positive_wildcard(
1832 DnsAnswer *answer,
1833 const char *name,
1834 const char *source,
1835 const char *zone,
1836 bool *authenticated) {
1837
1838 int r;
1839
1840 assert(name);
1841 assert(source);
1842 assert(zone);
1843 assert(authenticated);
1844
1845 r = dns_answer_contains_zone_nsec3(answer, zone);
1846 if (r < 0)
1847 return r;
1848 if (r > 0)
1849 return dnssec_test_positive_wildcard_nsec3(answer, name, source, zone, authenticated);
1850 else
1851 return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated);
1852}
1853
547973de
LP
1854static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
1855 [DNSSEC_VALIDATED] = "validated",
0c7bff0a 1856 [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
547973de 1857 [DNSSEC_INVALID] = "invalid",
203f1b35
LP
1858 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
1859 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
547973de
LP
1860 [DNSSEC_NO_SIGNATURE] = "no-signature",
1861 [DNSSEC_MISSING_KEY] = "missing-key",
203f1b35 1862 [DNSSEC_UNSIGNED] = "unsigned",
547973de 1863 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
72667f08 1864 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
b652d4a2 1865 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
547973de
LP
1866};
1867DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);