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