]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-dnssec.c
docs: update link for SUSE whitepaper
[thirdparty/systemd.git] / src / resolve / resolved-dns-dnssec.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2b442ac8 2
2b442ac8
LP
3#include "alloc-util.h"
4#include "dns-domain.h"
cb9eeb06
MCO
5#include "fd-util.h"
6#include "fileio.h"
91e023d8 7#include "gcrypt-util.h"
72667f08 8#include "hexdecoct.h"
0a970718 9#include "memory-util.h"
2485b7e2 10#include "memstream-util.h"
0351cbb9 11#include "openssl-util.h"
2b442ac8
LP
12#include "resolved-dns-dnssec.h"
13#include "resolved-dns-packet.h"
760877e9 14#include "sort-util.h"
24710c48 15#include "string-table.h"
2b442ac8 16
dcec950c 17#if PREFER_OPENSSL && OPENSSL_VERSION_MAJOR >= 3
acfdfb86
ZJS
18# pragma GCC diagnostic push
19# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
20DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(RSA*, RSA_free, NULL);
21DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_KEY*, EC_KEY_free, NULL);
22# pragma GCC diagnostic pop
23#endif
24
2b442ac8
LP
25#define VERIFY_RRS_MAX 256
26#define MAX_KEY_SIZE (32*1024)
27
896c5672
LP
28/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
29#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
30
eba29112
RP
31/* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value, but
32 * RFC9276 § 3.2 says that we should reduce the acceptable iteration count */
33#define NSEC3_ITERATIONS_MAX 100
a8f158b9 34
2b442ac8
LP
35/*
36 * The DNSSEC Chain of trust:
37 *
38 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
39 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
40 * DS RRs are protected like normal RRs
41 *
42 * Example chain:
43 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
44 */
45
0c857028 46uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
2b442ac8 47 const uint8_t *p;
0c857028 48 uint32_t sum, f;
2b442ac8
LP
49
50 /* The algorithm from RFC 4034, Appendix B. */
51
52 assert(dnskey);
53 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
54
0c857028
LP
55 f = (uint32_t) dnskey->dnskey.flags;
56
57 if (mask_revoke)
58 f &= ~DNSKEY_FLAG_REVOKE;
59
60 sum = f + ((((uint32_t) dnskey->dnskey.protocol) << 8) + (uint32_t) dnskey->dnskey.algorithm);
2b442ac8
LP
61
62 p = dnskey->dnskey.key;
63
6f1d18ae 64 for (size_t i = 0; i < dnskey->dnskey.key_size; i++)
2b442ac8
LP
65 sum += (i & 1) == 0 ? (uint32_t) p[i] << 8 : (uint32_t) p[i];
66
67 sum += (sum >> 16) & UINT32_C(0xFFFF);
68
69 return sum & UINT32_C(0xFFFF);
70}
71
0351cbb9 72#if HAVE_OPENSSL_OR_GCRYPT
47091522 73
93bab288
YW
74static int rr_compare(DnsResourceRecord * const *a, DnsResourceRecord * const *b) {
75 const DnsResourceRecord *x = *a, *y = *b;
2b442ac8
LP
76 size_t m;
77 int r;
78
79 /* Let's order the RRs according to RFC 4034, Section 6.3 */
80
81 assert(x);
93bab288 82 assert(x->wire_format);
2b442ac8 83 assert(y);
93bab288 84 assert(y->wire_format);
2b442ac8 85
93bab288 86 m = MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(x), DNS_RESOURCE_RECORD_RDATA_SIZE(y));
2b442ac8 87
93bab288 88 r = memcmp(DNS_RESOURCE_RECORD_RDATA(x), DNS_RESOURCE_RECORD_RDATA(y), m);
2b442ac8
LP
89 if (r != 0)
90 return r;
91
93bab288 92 return CMP(DNS_RESOURCE_RECORD_RDATA_SIZE(x), DNS_RESOURCE_RECORD_RDATA_SIZE(y));
2b442ac8
LP
93}
94
ea3a892f 95static int dnssec_rsa_verify_raw(
0351cbb9 96 hash_algorithm_t hash_algorithm,
2b442ac8
LP
97 const void *signature, size_t signature_size,
98 const void *data, size_t data_size,
99 const void *exponent, size_t exponent_size,
100 const void *modulus, size_t modulus_size) {
acfdfb86 101 int r;
2b442ac8 102
0351cbb9 103#if PREFER_OPENSSL
acfdfb86
ZJS
104# pragma GCC diagnostic push
105# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
0351cbb9
KK
106 _cleanup_(RSA_freep) RSA *rpubkey = NULL;
107 _cleanup_(EVP_PKEY_freep) EVP_PKEY *epubkey = NULL;
108 _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL;
109 _cleanup_(BN_freep) BIGNUM *e = NULL, *m = NULL;
0351cbb9
KK
110
111 assert(hash_algorithm);
112
113 e = BN_bin2bn(exponent, exponent_size, NULL);
114 if (!e)
115 return -EIO;
116
117 m = BN_bin2bn(modulus, modulus_size, NULL);
118 if (!m)
119 return -EIO;
120
121 rpubkey = RSA_new();
122 if (!rpubkey)
123 return -ENOMEM;
124
6e732313 125 if (RSA_set0_key(rpubkey, m, e, NULL) <= 0)
0351cbb9 126 return -EIO;
6e732313 127 e = m = NULL;
0351cbb9
KK
128
129 assert((size_t) RSA_size(rpubkey) == signature_size);
130
131 epubkey = EVP_PKEY_new();
132 if (!epubkey)
133 return -ENOMEM;
134
135 if (EVP_PKEY_assign_RSA(epubkey, RSAPublicKey_dup(rpubkey)) <= 0)
136 return -EIO;
137
138 ctx = EVP_PKEY_CTX_new(epubkey, NULL);
139 if (!ctx)
140 return -ENOMEM;
141
142 if (EVP_PKEY_verify_init(ctx) <= 0)
143 return -EIO;
144
145 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
146 return -EIO;
147
148 if (EVP_PKEY_CTX_set_signature_md(ctx, hash_algorithm) <= 0)
149 return -EIO;
150
151 r = EVP_PKEY_verify(ctx, signature, signature_size, data, data_size);
152 if (r < 0)
153 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
154 "Signature verification failed: 0x%lx", ERR_get_error());
155
acfdfb86 156# pragma GCC diagnostic pop
0351cbb9 157#else
2b442ac8
LP
158 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
159 gcry_mpi_t n = NULL, e = NULL, s = NULL;
160 gcry_error_t ge;
2b442ac8
LP
161
162 assert(hash_algorithm);
163
164 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature, signature_size, NULL);
165 if (ge != 0) {
166 r = -EIO;
167 goto finish;
168 }
169
170 ge = gcry_mpi_scan(&e, GCRYMPI_FMT_USG, exponent, exponent_size, NULL);
171 if (ge != 0) {
172 r = -EIO;
173 goto finish;
174 }
175
176 ge = gcry_mpi_scan(&n, GCRYMPI_FMT_USG, modulus, modulus_size, NULL);
177 if (ge != 0) {
178 r = -EIO;
179 goto finish;
180 }
181
182 ge = gcry_sexp_build(&signature_sexp,
183 NULL,
184 "(sig-val (rsa (s %m)))",
185 s);
186
187 if (ge != 0) {
188 r = -EIO;
189 goto finish;
190 }
191
192 ge = gcry_sexp_build(&data_sexp,
193 NULL,
194 "(data (flags pkcs1) (hash %s %b))",
195 hash_algorithm,
196 (int) data_size,
197 data);
198 if (ge != 0) {
199 r = -EIO;
200 goto finish;
201 }
202
203 ge = gcry_sexp_build(&public_key_sexp,
204 NULL,
205 "(public-key (rsa (n %m) (e %m)))",
206 n,
207 e);
208 if (ge != 0) {
209 r = -EIO;
210 goto finish;
211 }
212
213 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
d12bf2bd 214 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
2b442ac8 215 r = 0;
0351cbb9
KK
216 else if (ge != 0)
217 r = log_debug_errno(SYNTHETIC_ERRNO(EIO),
218 "RSA signature check failed: %s", gpg_strerror(ge));
219 else
2b442ac8
LP
220 r = 1;
221
222finish:
223 if (e)
224 gcry_mpi_release(e);
225 if (n)
226 gcry_mpi_release(n);
227 if (s)
228 gcry_mpi_release(s);
229
230 if (public_key_sexp)
231 gcry_sexp_release(public_key_sexp);
232 if (signature_sexp)
233 gcry_sexp_release(signature_sexp);
234 if (data_sexp)
235 gcry_sexp_release(data_sexp);
0351cbb9 236#endif
acfdfb86 237 return r;
2b442ac8
LP
238}
239
ea3a892f 240static int dnssec_rsa_verify(
0351cbb9 241 hash_algorithm_t hash_algorithm,
ea3a892f
LP
242 const void *hash, size_t hash_size,
243 DnsResourceRecord *rrsig,
244 DnsResourceRecord *dnskey) {
245
246 size_t exponent_size, modulus_size;
247 void *exponent, *modulus;
248
249 assert(hash_algorithm);
250 assert(hash);
251 assert(hash_size > 0);
252 assert(rrsig);
253 assert(dnskey);
254
255 if (*(uint8_t*) dnskey->dnskey.key == 0) {
256 /* exponent is > 255 bytes long */
257
258 exponent = (uint8_t*) dnskey->dnskey.key + 3;
259 exponent_size =
ac04adbe
TG
260 ((size_t) (((uint8_t*) dnskey->dnskey.key)[1]) << 8) |
261 ((size_t) ((uint8_t*) dnskey->dnskey.key)[2]);
ea3a892f
LP
262
263 if (exponent_size < 256)
264 return -EINVAL;
265
266 if (3 + exponent_size >= dnskey->dnskey.key_size)
267 return -EINVAL;
268
269 modulus = (uint8_t*) dnskey->dnskey.key + 3 + exponent_size;
270 modulus_size = dnskey->dnskey.key_size - 3 - exponent_size;
271
272 } else {
273 /* exponent is <= 255 bytes long */
274
275 exponent = (uint8_t*) dnskey->dnskey.key + 1;
276 exponent_size = (size_t) ((uint8_t*) dnskey->dnskey.key)[0];
277
278 if (exponent_size <= 0)
279 return -EINVAL;
280
281 if (1 + exponent_size >= dnskey->dnskey.key_size)
282 return -EINVAL;
283
284 modulus = (uint8_t*) dnskey->dnskey.key + 1 + exponent_size;
285 modulus_size = dnskey->dnskey.key_size - 1 - exponent_size;
286 }
287
288 return dnssec_rsa_verify_raw(
289 hash_algorithm,
290 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
291 hash, hash_size,
292 exponent, exponent_size,
293 modulus, modulus_size);
294}
295
e0240c64 296static int dnssec_ecdsa_verify_raw(
0351cbb9
KK
297 hash_algorithm_t hash_algorithm,
298 elliptic_curve_t curve,
e0240c64
LP
299 const void *signature_r, size_t signature_r_size,
300 const void *signature_s, size_t signature_s_size,
301 const void *data, size_t data_size,
302 const void *key, size_t key_size) {
acfdfb86 303 int k;
e0240c64 304
0351cbb9 305#if PREFER_OPENSSL
acfdfb86
ZJS
306# pragma GCC diagnostic push
307# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
0351cbb9
KK
308 _cleanup_(EC_GROUP_freep) EC_GROUP *ec_group = NULL;
309 _cleanup_(EC_POINT_freep) EC_POINT *p = NULL;
310 _cleanup_(EC_KEY_freep) EC_KEY *eckey = NULL;
311 _cleanup_(BN_CTX_freep) BN_CTX *bctx = NULL;
312 _cleanup_(BN_freep) BIGNUM *r = NULL, *s = NULL;
313 _cleanup_(ECDSA_SIG_freep) ECDSA_SIG *sig = NULL;
0351cbb9
KK
314
315 assert(hash_algorithm);
316
317 ec_group = EC_GROUP_new_by_curve_name(curve);
318 if (!ec_group)
319 return -ENOMEM;
320
321 p = EC_POINT_new(ec_group);
322 if (!p)
323 return -ENOMEM;
324
325 bctx = BN_CTX_new();
326 if (!bctx)
327 return -ENOMEM;
328
329 if (EC_POINT_oct2point(ec_group, p, key, key_size, bctx) <= 0)
330 return -EIO;
331
332 eckey = EC_KEY_new();
333 if (!eckey)
334 return -ENOMEM;
335
336 if (EC_KEY_set_group(eckey, ec_group) <= 0)
337 return -EIO;
338
339 if (EC_KEY_set_public_key(eckey, p) <= 0)
340 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
341 "EC_POINT_bn2point failed: 0x%lx", ERR_get_error());
342
343 assert(EC_KEY_check_key(eckey) == 1);
344
345 r = BN_bin2bn(signature_r, signature_r_size, NULL);
346 if (!r)
347 return -EIO;
348
349 s = BN_bin2bn(signature_s, signature_s_size, NULL);
350 if (!s)
351 return -EIO;
352
30fd9a2d 353 /* TODO: We should eventually use the EVP API once it supports ECDSA signature verification */
0351cbb9
KK
354
355 sig = ECDSA_SIG_new();
356 if (!sig)
357 return -ENOMEM;
358
6e732313 359 if (ECDSA_SIG_set0(sig, r, s) <= 0)
0351cbb9 360 return -EIO;
6e732313 361 r = s = NULL;
0351cbb9
KK
362
363 k = ECDSA_do_verify(data, data_size, sig, eckey);
364 if (k < 0)
365 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
366 "Signature verification failed: 0x%lx", ERR_get_error());
367
acfdfb86 368# pragma GCC diagnostic pop
0351cbb9 369#else
e0240c64
LP
370 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
371 gcry_mpi_t q = NULL, r = NULL, s = NULL;
372 gcry_error_t ge;
e0240c64
LP
373
374 assert(hash_algorithm);
375
376 ge = gcry_mpi_scan(&r, GCRYMPI_FMT_USG, signature_r, signature_r_size, NULL);
377 if (ge != 0) {
378 k = -EIO;
379 goto finish;
380 }
381
382 ge = gcry_mpi_scan(&s, GCRYMPI_FMT_USG, signature_s, signature_s_size, NULL);
383 if (ge != 0) {
384 k = -EIO;
385 goto finish;
386 }
387
388 ge = gcry_mpi_scan(&q, GCRYMPI_FMT_USG, key, key_size, NULL);
389 if (ge != 0) {
390 k = -EIO;
391 goto finish;
392 }
393
394 ge = gcry_sexp_build(&signature_sexp,
395 NULL,
396 "(sig-val (ecdsa (r %m) (s %m)))",
397 r,
398 s);
399 if (ge != 0) {
400 k = -EIO;
401 goto finish;
402 }
403
404 ge = gcry_sexp_build(&data_sexp,
405 NULL,
406 "(data (flags rfc6979) (hash %s %b))",
407 hash_algorithm,
408 (int) data_size,
409 data);
410 if (ge != 0) {
411 k = -EIO;
412 goto finish;
413 }
414
415 ge = gcry_sexp_build(&public_key_sexp,
416 NULL,
417 "(public-key (ecc (curve %s) (q %m)))",
418 curve,
419 q);
420 if (ge != 0) {
421 k = -EIO;
422 goto finish;
423 }
424
425 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
426 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
427 k = 0;
428 else if (ge != 0) {
429 log_debug("ECDSA signature check failed: %s", gpg_strerror(ge));
430 k = -EIO;
431 } else
432 k = 1;
433finish:
434 if (r)
435 gcry_mpi_release(r);
436 if (s)
437 gcry_mpi_release(s);
438 if (q)
439 gcry_mpi_release(q);
440
441 if (public_key_sexp)
442 gcry_sexp_release(public_key_sexp);
443 if (signature_sexp)
444 gcry_sexp_release(signature_sexp);
445 if (data_sexp)
446 gcry_sexp_release(data_sexp);
0351cbb9 447#endif
acfdfb86 448 return k;
e0240c64
LP
449}
450
451static int dnssec_ecdsa_verify(
0351cbb9 452 hash_algorithm_t hash_algorithm,
e0240c64
LP
453 int algorithm,
454 const void *hash, size_t hash_size,
455 DnsResourceRecord *rrsig,
456 DnsResourceRecord *dnskey) {
457
0351cbb9 458 elliptic_curve_t curve;
e0240c64
LP
459 size_t key_size;
460 uint8_t *q;
461
462 assert(hash);
463 assert(hash_size);
464 assert(rrsig);
465 assert(dnskey);
466
467 if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) {
0351cbb9 468 curve = OPENSSL_OR_GCRYPT(NID_X9_62_prime256v1, "NIST P-256"); /* NIST P-256 */
e0240c64 469 key_size = 32;
e0240c64 470 } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) {
0351cbb9 471 curve = OPENSSL_OR_GCRYPT(NID_secp384r1, "NIST P-384"); /* NIST P-384 */
e0240c64 472 key_size = 48;
e0240c64
LP
473 } else
474 return -EOPNOTSUPP;
475
476 if (dnskey->dnskey.key_size != key_size * 2)
477 return -EINVAL;
478
479 if (rrsig->rrsig.signature_size != key_size * 2)
480 return -EINVAL;
481
6e9417f5 482 q = newa(uint8_t, key_size*2 + 1);
e0240c64
LP
483 q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
484 memcpy(q+1, dnskey->dnskey.key, key_size*2);
485
486 return dnssec_ecdsa_verify_raw(
487 hash_algorithm,
488 curve,
489 rrsig->rrsig.signature, key_size,
490 (uint8_t*) rrsig->rrsig.signature + key_size, key_size,
491 hash, hash_size,
492 q, key_size*2+1);
493}
494
cb9eeb06 495static int dnssec_eddsa_verify_raw(
0351cbb9
KK
496 elliptic_curve_t curve,
497 const uint8_t *signature, size_t signature_size,
498 const uint8_t *data, size_t data_size,
499 const uint8_t *key, size_t key_size) {
500
501#if PREFER_OPENSSL
502 _cleanup_(EVP_PKEY_freep) EVP_PKEY *evkey = NULL;
503 _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *pctx = NULL;
504 _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = NULL;
505 int r;
506
507 assert(curve == NID_ED25519);
508 assert(signature_size == key_size * 2);
509
510 uint8_t *q = newa(uint8_t, signature_size + 1);
511 q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */
512 memcpy(q+1, signature, signature_size);
513
514 evkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, key, key_size);
515 if (!evkey)
516 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
517 "EVP_PKEY_new_raw_public_key failed: 0x%lx", ERR_get_error());
cb9eeb06 518
0351cbb9
KK
519 pctx = EVP_PKEY_CTX_new(evkey, NULL);
520 if (!pctx)
521 return -ENOMEM;
522
523 ctx = EVP_MD_CTX_new();
524 if (!ctx)
525 return -ENOMEM;
526
527 /* This prevents EVP_DigestVerifyInit from managing pctx and complicating our free logic. */
528 EVP_MD_CTX_set_pkey_ctx(ctx, pctx);
529
530 /* One might be tempted to use EVP_PKEY_verify_init, but see Ed25519(7ssl). */
531 if (EVP_DigestVerifyInit(ctx, &pctx, NULL, NULL, evkey) <= 0)
532 return -EIO;
533
534 r = EVP_DigestVerify(ctx, signature, signature_size, data, data_size);
535 if (r < 0)
536 return log_debug_errno(SYNTHETIC_ERRNO(EIO),
537 "Signature verification failed: 0x%lx", ERR_get_error());
538
539 return r;
540
541#elif GCRYPT_VERSION_NUMBER >= 0x010600
cb9eeb06
MCO
542 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL;
543 gcry_error_t ge;
544 int k;
545
0351cbb9
KK
546 assert(signature_size == key_size * 2);
547
cb9eeb06
MCO
548 ge = gcry_sexp_build(&signature_sexp,
549 NULL,
550 "(sig-val (eddsa (r %b) (s %b)))",
0351cbb9
KK
551 (int) key_size,
552 signature,
553 (int) key_size,
554 signature + key_size);
cb9eeb06
MCO
555 if (ge != 0) {
556 k = -EIO;
557 goto finish;
558 }
559
560 ge = gcry_sexp_build(&data_sexp,
561 NULL,
562 "(data (flags eddsa) (hash-algo sha512) (value %b))",
563 (int) data_size,
564 data);
565 if (ge != 0) {
566 k = -EIO;
567 goto finish;
568 }
569
570 ge = gcry_sexp_build(&public_key_sexp,
571 NULL,
572 "(public-key (ecc (curve %s) (flags eddsa) (q %b)))",
573 curve,
574 (int) key_size,
575 key);
576 if (ge != 0) {
577 k = -EIO;
578 goto finish;
579 }
580
581 ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp);
582 if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE)
583 k = 0;
0351cbb9
KK
584 else if (ge != 0)
585 k = log_debug_errno(SYNTHETIC_ERRNO(EIO),
586 "EdDSA signature check failed: %s", gpg_strerror(ge));
587 else
cb9eeb06
MCO
588 k = 1;
589finish:
590 if (public_key_sexp)
591 gcry_sexp_release(public_key_sexp);
592 if (signature_sexp)
593 gcry_sexp_release(signature_sexp);
594 if (data_sexp)
595 gcry_sexp_release(data_sexp);
596
597 return k;
0351cbb9
KK
598#else
599 return -EOPNOTSUPP;
600#endif
cb9eeb06
MCO
601}
602
603static int dnssec_eddsa_verify(
604 int algorithm,
605 const void *data, size_t data_size,
606 DnsResourceRecord *rrsig,
607 DnsResourceRecord *dnskey) {
0351cbb9 608 elliptic_curve_t curve;
cb9eeb06
MCO
609 size_t key_size;
610
611 if (algorithm == DNSSEC_ALGORITHM_ED25519) {
0351cbb9 612 curve = OPENSSL_OR_GCRYPT(NID_ED25519, "Ed25519");
cb9eeb06
MCO
613 key_size = 32;
614 } else
615 return -EOPNOTSUPP;
616
617 if (dnskey->dnskey.key_size != key_size)
618 return -EINVAL;
619
620 if (rrsig->rrsig.signature_size != key_size * 2)
621 return -EINVAL;
622
623 return dnssec_eddsa_verify_raw(
624 curve,
0351cbb9 625 rrsig->rrsig.signature, rrsig->rrsig.signature_size,
cb9eeb06
MCO
626 data, data_size,
627 dnskey->dnskey.key, key_size);
628}
cb9eeb06 629
1cd7a2c1
KK
630static int md_add_uint8(hash_context_t ctx, uint8_t v) {
631#if PREFER_OPENSSL
632 return EVP_DigestUpdate(ctx, &v, sizeof(v));
633#else
634 gcry_md_write(ctx, &v, sizeof(v));
635 return 0;
636#endif
2b442ac8
LP
637}
638
1cd7a2c1 639static int md_add_uint16(hash_context_t ctx, uint16_t v) {
2b442ac8 640 v = htobe16(v);
1cd7a2c1
KK
641#if PREFER_OPENSSL
642 return EVP_DigestUpdate(ctx, &v, sizeof(v));
643#else
644 gcry_md_write(ctx, &v, sizeof(v));
645 return 0;
646#endif
2b442ac8
LP
647}
648
cb9eeb06
MCO
649static void fwrite_uint8(FILE *fp, uint8_t v) {
650 fwrite(&v, sizeof(v), 1, fp);
651}
652
653static void fwrite_uint16(FILE *fp, uint16_t v) {
654 v = htobe16(v);
655 fwrite(&v, sizeof(v), 1, fp);
656}
657
658static void fwrite_uint32(FILE *fp, uint32_t v) {
2b442ac8 659 v = htobe32(v);
cb9eeb06 660 fwrite(&v, sizeof(v), 1, fp);
2b442ac8
LP
661}
662
97c67192
LP
663static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) {
664 int n_key_labels, n_signer_labels;
665 const char *name;
666 int r;
667
98e80bf9
ZJS
668 /* Checks whether the specified RRSIG RR is somewhat valid, and initializes the .n_skip_labels_source
669 * and .n_skip_labels_signer fields so that we can use them later on. */
97c67192
LP
670
671 assert(rrsig);
672 assert(rrsig->key->type == DNS_TYPE_RRSIG);
673
674 /* Check if this RRSIG RR is already prepared */
98e80bf9 675 if (rrsig->n_skip_labels_source != UINT8_MAX)
97c67192
LP
676 return 0;
677
678 if (rrsig->rrsig.inception > rrsig->rrsig.expiration)
679 return -EINVAL;
680
1c02e7ba 681 name = dns_resource_key_name(rrsig->key);
97c67192
LP
682
683 n_key_labels = dns_name_count_labels(name);
684 if (n_key_labels < 0)
685 return n_key_labels;
686 if (rrsig->rrsig.labels > n_key_labels)
687 return -EINVAL;
688
689 n_signer_labels = dns_name_count_labels(rrsig->rrsig.signer);
690 if (n_signer_labels < 0)
691 return n_signer_labels;
692 if (n_signer_labels > rrsig->rrsig.labels)
693 return -EINVAL;
694
695 r = dns_name_skip(name, n_key_labels - n_signer_labels, &name);
696 if (r < 0)
697 return r;
698 if (r == 0)
699 return -EINVAL;
700
701 /* Check if the signer is really a suffix of us */
702 r = dns_name_equal(name, rrsig->rrsig.signer);
703 if (r < 0)
704 return r;
705 if (r == 0)
706 return -EINVAL;
707
98e80bf9 708 assert(n_key_labels < UINT8_MAX); /* UINT8_MAX/-1 means unsigned. */
97c67192
LP
709 rrsig->n_skip_labels_source = n_key_labels - rrsig->rrsig.labels;
710 rrsig->n_skip_labels_signer = n_key_labels - n_signer_labels;
711
712 return 0;
713}
714
2a326321
LP
715static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
716 usec_t expiration, inception, skew;
717
718 assert(rrsig);
719 assert(rrsig->key->type == DNS_TYPE_RRSIG);
720
721 if (realtime == USEC_INFINITY)
722 realtime = now(CLOCK_REALTIME);
723
724 expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
725 inception = rrsig->rrsig.inception * USEC_PER_SEC;
726
5ae5cd40 727 /* Consider inverted validity intervals as expired */
2a326321 728 if (inception > expiration)
5ae5cd40 729 return true;
2a326321 730
896c5672
LP
731 /* Permit a certain amount of clock skew of 10% of the valid
732 * time range. This takes inspiration from unbound's
733 * resolver. */
2a326321 734 skew = (expiration - inception) / 10;
896c5672
LP
735 if (skew > SKEW_MAX)
736 skew = SKEW_MAX;
2a326321
LP
737
738 if (inception < skew)
739 inception = 0;
740 else
741 inception -= skew;
742
743 if (expiration + skew < expiration)
744 expiration = USEC_INFINITY;
745 else
746 expiration += skew;
747
748 return realtime < inception || realtime > expiration;
749}
750
0351cbb9 751static hash_md_t algorithm_to_implementation_id(uint8_t algorithm) {
fbf1a66d 752
0351cbb9 753 /* Translates a DNSSEC signature algorithm into an openssl/gcrypt digest identifier.
6af47493 754 *
0351cbb9
KK
755 * Note that we implement all algorithms listed as "Must implement" and "Recommended to Implement" in
756 * RFC6944. We don't implement any algorithms that are listed as "Optional" or "Must Not Implement".
757 * Specifically, we do not implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and GOST-ECC. */
fbf1a66d
LP
758
759 switch (algorithm) {
760
761 case DNSSEC_ALGORITHM_RSASHA1:
762 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
0351cbb9 763 return OPENSSL_OR_GCRYPT(EVP_sha1(), GCRY_MD_SHA1);
fbf1a66d
LP
764
765 case DNSSEC_ALGORITHM_RSASHA256:
e0240c64 766 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
0351cbb9 767 return OPENSSL_OR_GCRYPT(EVP_sha256(), GCRY_MD_SHA256);
fbf1a66d 768
e0240c64 769 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
0351cbb9 770 return OPENSSL_OR_GCRYPT(EVP_sha384(), GCRY_MD_SHA384);
e0240c64 771
fbf1a66d 772 case DNSSEC_ALGORITHM_RSASHA512:
0351cbb9 773 return OPENSSL_OR_GCRYPT(EVP_sha512(), GCRY_MD_SHA512);
fbf1a66d
LP
774
775 default:
0351cbb9 776 return OPENSSL_OR_GCRYPT(NULL, -EOPNOTSUPP);
fbf1a66d
LP
777 }
778}
779
97c67192
LP
780static void dnssec_fix_rrset_ttl(
781 DnsResourceRecord *list[],
782 unsigned n,
cd2cdba2 783 DnsResourceRecord *rrsig) {
97c67192 784
97c67192
LP
785 assert(list);
786 assert(n > 0);
787 assert(rrsig);
788
6f1d18ae 789 for (unsigned k = 0; k < n; k++) {
97c67192
LP
790 DnsResourceRecord *rr = list[k];
791
792 /* Pick the TTL as the minimum of the RR's TTL, the
793 * RR's original TTL according to the RRSIG and the
794 * RRSIG's own TTL, see RFC 4035, Section 5.3.3 */
795 rr->ttl = MIN3(rr->ttl, rrsig->rrsig.original_ttl, rrsig->ttl);
796 rr->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
797
798 /* Copy over information about the signer and wildcard source of synthesis */
799 rr->n_skip_labels_source = rrsig->n_skip_labels_source;
800 rr->n_skip_labels_signer = rrsig->n_skip_labels_signer;
801 }
802
803 rrsig->expiry = rrsig->rrsig.expiration * USEC_PER_SEC;
804}
805
cc1ecbaa
ZJS
806static int dnssec_rrset_serialize_sig(
807 DnsResourceRecord *rrsig,
808 const char *source,
809 DnsResourceRecord **list,
810 size_t list_len,
811 bool wildcard,
812 char **ret_sig_data,
813 size_t *ret_sig_size) {
814
2485b7e2 815 _cleanup_(memstream_done) MemStream m = {};
cc1ecbaa
ZJS
816 uint8_t wire_format_name[DNS_WIRE_FORMAT_HOSTNAME_MAX];
817 DnsResourceRecord *rr;
2485b7e2 818 FILE *f;
cc1ecbaa
ZJS
819 int r;
820
821 assert(rrsig);
822 assert(source);
823 assert(list || list_len == 0);
824 assert(ret_sig_data);
825 assert(ret_sig_size);
826
2485b7e2 827 f = memstream_init(&m);
cc1ecbaa
ZJS
828 if (!f)
829 return -ENOMEM;
830
831 fwrite_uint16(f, rrsig->rrsig.type_covered);
832 fwrite_uint8(f, rrsig->rrsig.algorithm);
833 fwrite_uint8(f, rrsig->rrsig.labels);
834 fwrite_uint32(f, rrsig->rrsig.original_ttl);
835 fwrite_uint32(f, rrsig->rrsig.expiration);
836 fwrite_uint32(f, rrsig->rrsig.inception);
837 fwrite_uint16(f, rrsig->rrsig.key_tag);
838
839 r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true);
840 if (r < 0)
841 return r;
842 fwrite(wire_format_name, 1, r, f);
843
844 /* Convert the source of synthesis into wire format */
845 r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true);
846 if (r < 0)
847 return r;
848
849 for (size_t k = 0; k < list_len; k++) {
850 size_t l;
851
852 rr = list[k];
853
854 /* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */
855 if (wildcard)
856 fwrite((uint8_t[]) { 1, '*'}, sizeof(uint8_t), 2, f);
857 fwrite(wire_format_name, 1, r, f);
858
859 fwrite_uint16(f, rr->key->type);
860 fwrite_uint16(f, rr->key->class);
861 fwrite_uint32(f, rrsig->rrsig.original_ttl);
862
863 l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr);
864 assert(l <= 0xFFFF);
865
866 fwrite_uint16(f, (uint16_t) l);
867 fwrite(DNS_RESOURCE_RECORD_RDATA(rr), 1, l, f);
868 }
869
2485b7e2 870 return memstream_finalize(&m, ret_sig_data, ret_sig_size);
cc1ecbaa
ZJS
871}
872
667dac6e
ZJS
873static int dnssec_rrset_verify_sig(
874 DnsResourceRecord *rrsig,
875 DnsResourceRecord *dnskey,
876 const char *sig_data,
877 size_t sig_size) {
878
879 assert(rrsig);
880 assert(dnskey);
881 assert(sig_data);
882 assert(sig_size > 0);
883
0351cbb9
KK
884 hash_md_t md_algorithm;
885
886#if PREFER_OPENSSL
887 uint8_t hash[EVP_MAX_MD_SIZE];
888 unsigned hash_size;
889#else
667dac6e
ZJS
890 _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
891 void *hash;
892 size_t hash_size;
8707c9b2 893 int r;
667dac6e 894
8707c9b2
LB
895 r = initialize_libgcrypt(false);
896 if (r < 0)
897 return r;
0351cbb9 898#endif
667dac6e
ZJS
899
900 switch (rrsig->rrsig.algorithm) {
901 case DNSSEC_ALGORITHM_ED25519:
0351cbb9 902#if PREFER_OPENSSL || GCRYPT_VERSION_NUMBER >= 0x010600
667dac6e
ZJS
903 return dnssec_eddsa_verify(
904 rrsig->rrsig.algorithm,
905 sig_data, sig_size,
906 rrsig,
907 dnskey);
908#endif
909 case DNSSEC_ALGORITHM_ED448:
910 return -EOPNOTSUPP;
911 default:
912 /* OK, the RRs are now in canonical order. Let's calculate the digest */
0351cbb9
KK
913 md_algorithm = algorithm_to_implementation_id(rrsig->rrsig.algorithm);
914#if PREFER_OPENSSL
915 if (!md_algorithm)
916 return -EOPNOTSUPP;
917
918 _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = EVP_MD_CTX_new();
919 if (!ctx)
920 return -ENOMEM;
921
922 if (EVP_DigestInit_ex(ctx, md_algorithm, NULL) <= 0)
923 return -EIO;
924
925 if (EVP_DigestUpdate(ctx, sig_data, sig_size) <= 0)
926 return -EIO;
927
928 if (EVP_DigestFinal_ex(ctx, hash, &hash_size) <= 0)
929 return -EIO;
930
931 assert(hash_size > 0);
932
933#else
667dac6e
ZJS
934 if (md_algorithm < 0)
935 return md_algorithm;
936
937 gcry_error_t err = gcry_md_open(&md, md_algorithm, 0);
938 if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md)
939 return -EIO;
940
941 hash_size = gcry_md_get_algo_dlen(md_algorithm);
942 assert(hash_size > 0);
943
944 gcry_md_write(md, sig_data, sig_size);
945
946 hash = gcry_md_read(md, 0);
947 if (!hash)
948 return -EIO;
0351cbb9 949#endif
667dac6e
ZJS
950 }
951
952 switch (rrsig->rrsig.algorithm) {
953
954 case DNSSEC_ALGORITHM_RSASHA1:
955 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1:
956 case DNSSEC_ALGORITHM_RSASHA256:
957 case DNSSEC_ALGORITHM_RSASHA512:
958 return dnssec_rsa_verify(
0351cbb9 959 OPENSSL_OR_GCRYPT(md_algorithm, gcry_md_algo_name(md_algorithm)),
667dac6e
ZJS
960 hash, hash_size,
961 rrsig,
962 dnskey);
963
964 case DNSSEC_ALGORITHM_ECDSAP256SHA256:
965 case DNSSEC_ALGORITHM_ECDSAP384SHA384:
966 return dnssec_ecdsa_verify(
0351cbb9 967 OPENSSL_OR_GCRYPT(md_algorithm, gcry_md_algo_name(md_algorithm)),
667dac6e
ZJS
968 rrsig->rrsig.algorithm,
969 hash, hash_size,
970 rrsig,
971 dnskey);
972
973 default:
974 assert_not_reached();
975 }
976}
977
2a326321
LP
978int dnssec_verify_rrset(
979 DnsAnswer *a,
0c857028 980 const DnsResourceKey *key,
2a326321
LP
981 DnsResourceRecord *rrsig,
982 DnsResourceRecord *dnskey,
547973de
LP
983 usec_t realtime,
984 DnssecResult *result) {
2a326321 985
2b442ac8 986 DnsResourceRecord **list, *rr;
588c53d0 987 const char *source, *name;
cb9eeb06 988 _cleanup_free_ char *sig_data = NULL;
d1b8e56a
YW
989 size_t sig_size = 0; /* avoid false maybe-uninitialized warning */
990 size_t n = 0;
7715f91d 991 bool wildcard;
667dac6e 992 int r;
2b442ac8
LP
993
994 assert(key);
995 assert(rrsig);
996 assert(dnskey);
547973de 997 assert(result);
2a326321
LP
998 assert(rrsig->key->type == DNS_TYPE_RRSIG);
999 assert(dnskey->key->type == DNS_TYPE_DNSKEY);
2b442ac8 1000
c629ff58 1001 /* Verifies that the RRSet matches the specified "key" in "a",
2b442ac8 1002 * using the signature "rrsig" and the key "dnskey". It's
c629ff58 1003 * assumed that RRSIG and DNSKEY match. */
2b442ac8 1004
97c67192
LP
1005 r = dnssec_rrsig_prepare(rrsig);
1006 if (r == -EINVAL) {
1007 *result = DNSSEC_INVALID;
1008 return r;
1009 }
1010 if (r < 0)
1011 return r;
1012
2a326321
LP
1013 r = dnssec_rrsig_expired(rrsig, realtime);
1014 if (r < 0)
1015 return r;
547973de
LP
1016 if (r > 0) {
1017 *result = DNSSEC_SIGNATURE_EXPIRED;
1018 return 0;
1019 }
2a326321 1020
1c02e7ba 1021 name = dns_resource_key_name(key);
588c53d0
LP
1022
1023 /* Some keys may only appear signed in the zone apex, and are invalid anywhere else. (SOA, NS...) */
1024 if (dns_type_apex_only(rrsig->rrsig.type_covered)) {
1025 r = dns_name_equal(rrsig->rrsig.signer, name);
1026 if (r < 0)
1027 return r;
1028 if (r == 0) {
1029 *result = DNSSEC_INVALID;
1030 return 0;
1031 }
1032 }
1033
1034 /* OTOH DS RRs may not appear in the zone apex, but are valid everywhere else. */
1035 if (rrsig->rrsig.type_covered == DNS_TYPE_DS) {
1036 r = dns_name_equal(rrsig->rrsig.signer, name);
1037 if (r < 0)
1038 return r;
1039 if (r > 0) {
1040 *result = DNSSEC_INVALID;
1041 return 0;
1042 }
1043 }
1044
7715f91d 1045 /* Determine the "Source of Synthesis" and whether this is a wildcard RRSIG */
588c53d0 1046 r = dns_name_suffix(name, rrsig->rrsig.labels, &source);
7715f91d
LP
1047 if (r < 0)
1048 return r;
e8233bce
LP
1049 if (r > 0 && !dns_type_may_wildcard(rrsig->rrsig.type_covered)) {
1050 /* We refuse to validate NSEC3 or SOA RRs that are synthesized from wildcards */
1051 *result = DNSSEC_INVALID;
1052 return 0;
1053 }
7160eb1b
LP
1054 if (r == 1) {
1055 /* If we stripped a single label, then let's see if that maybe was "*". If so, we are not really
1056 * synthesized from a wildcard, we are the wildcard itself. Treat that like a normal name. */
588c53d0 1057 r = dns_name_startswith(name, "*");
7160eb1b
LP
1058 if (r < 0)
1059 return r;
1060 if (r > 0)
588c53d0 1061 source = name;
7160eb1b
LP
1062
1063 wildcard = r == 0;
1064 } else
1065 wildcard = r > 0;
7715f91d 1066
2b442ac8 1067 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
0f23174c 1068 list = newa(DnsResourceRecord *, dns_answer_size(a));
2b442ac8
LP
1069
1070 DNS_ANSWER_FOREACH(rr, a) {
1071 r = dns_resource_key_equal(key, rr->key);
1072 if (r < 0)
1073 return r;
1074 if (r == 0)
1075 continue;
1076
1077 /* We need the wire format for ordering, and digest calculation */
1078 r = dns_resource_record_to_wire_format(rr, true);
1079 if (r < 0)
1080 return r;
1081
1082 list[n++] = rr;
935a999f
TG
1083
1084 if (n > VERIFY_RRS_MAX)
1085 return -E2BIG;
2b442ac8
LP
1086 }
1087
1088 if (n <= 0)
1089 return -ENODATA;
1090
1091 /* Bring the RRs into canonical order */
93bab288 1092 typesafe_qsort(list, n, rr_compare);
2b442ac8 1093
cc1ecbaa
ZJS
1094 r = dnssec_rrset_serialize_sig(rrsig, source, list, n, wildcard,
1095 &sig_data, &sig_size);
cb9eeb06
MCO
1096 if (r < 0)
1097 return r;
1098
667dac6e
ZJS
1099 r = dnssec_rrset_verify_sig(rrsig, dnskey, sig_data, sig_size);
1100 if (r == -EOPNOTSUPP) {
cb9eeb06 1101 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
8530efc1 1102 return 0;
e0240c64 1103 }
2b442ac8 1104 if (r < 0)
8530efc1 1105 return r;
2b442ac8 1106
97c67192
LP
1107 /* Now, fix the ttl, expiry, and remember the synthesizing source and the signer */
1108 if (r > 0)
cd2cdba2 1109 dnssec_fix_rrset_ttl(list, n, rrsig);
97c67192
LP
1110
1111 if (r == 0)
0c7bff0a
LP
1112 *result = DNSSEC_INVALID;
1113 else if (wildcard)
1114 *result = DNSSEC_VALIDATED_WILDCARD;
1115 else
1116 *result = DNSSEC_VALIDATED;
97c67192 1117
8530efc1 1118 return 0;
2b442ac8
LP
1119}
1120
0c857028 1121int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
2b442ac8
LP
1122
1123 assert(rrsig);
1124 assert(dnskey);
1125
1126 /* Checks if the specified DNSKEY RR matches the key used for
1127 * the signature in the specified RRSIG RR */
1128
1129 if (rrsig->key->type != DNS_TYPE_RRSIG)
1130 return -EINVAL;
1131
1132 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1133 return 0;
1134 if (dnskey->key->class != rrsig->key->class)
1135 return 0;
1136 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
1137 return 0;
0c857028 1138 if (!revoked_ok && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
28b8191e 1139 return 0;
2b442ac8
LP
1140 if (dnskey->dnskey.protocol != 3)
1141 return 0;
1142 if (dnskey->dnskey.algorithm != rrsig->rrsig.algorithm)
1143 return 0;
1144
0c857028 1145 if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag)
2b442ac8
LP
1146 return 0;
1147
1c02e7ba 1148 return dns_name_equal(dns_resource_key_name(dnskey->key), rrsig->rrsig.signer);
2b442ac8
LP
1149}
1150
105e1512 1151int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
2b442ac8
LP
1152 assert(key);
1153 assert(rrsig);
1154
1155 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
1156
1157 if (rrsig->key->type != DNS_TYPE_RRSIG)
1158 return 0;
1159 if (rrsig->key->class != key->class)
1160 return 0;
1161 if (rrsig->rrsig.type_covered != key->type)
1162 return 0;
1163
1c02e7ba 1164 return dns_name_equal(dns_resource_key_name(rrsig->key), dns_resource_key_name(key));
2b442ac8
LP
1165}
1166
2a326321
LP
1167int dnssec_verify_rrset_search(
1168 DnsAnswer *a,
0c857028 1169 const DnsResourceKey *key,
2a326321 1170 DnsAnswer *validated_dnskeys,
547973de 1171 usec_t realtime,
0c7bff0a
LP
1172 DnssecResult *result,
1173 DnsResourceRecord **ret_rrsig) {
2a326321 1174
203f1b35 1175 bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
67d0ce88 1176 unsigned nvalidations = 0;
2b442ac8
LP
1177 DnsResourceRecord *rrsig;
1178 int r;
1179
1180 assert(key);
547973de 1181 assert(result);
2b442ac8 1182
105e1512 1183 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
2b442ac8 1184
6edf21db 1185 if (dns_answer_isempty(a))
2b442ac8
LP
1186 return -ENODATA;
1187
1188 /* Iterate through each RRSIG RR. */
1189 DNS_ANSWER_FOREACH(rrsig, a) {
1190 DnsResourceRecord *dnskey;
105e1512 1191 DnsAnswerFlags flags;
2b442ac8 1192
203f1b35 1193 /* Is this an RRSIG RR that applies to RRs matching our key? */
2b442ac8
LP
1194 r = dnssec_key_match_rrsig(key, rrsig);
1195 if (r < 0)
1196 return r;
1197 if (r == 0)
1198 continue;
1199
1200 found_rrsig = true;
1201
547973de 1202 /* Look for a matching key */
105e1512 1203 DNS_ANSWER_FOREACH_FLAGS(dnskey, flags, validated_dnskeys) {
547973de 1204 DnssecResult one_result;
2b442ac8 1205
105e1512
LP
1206 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1207 continue;
1208
203f1b35 1209 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
0c857028 1210 r = dnssec_rrsig_match_dnskey(rrsig, dnskey, false);
2b442ac8
LP
1211 if (r < 0)
1212 return r;
1213 if (r == 0)
1214 continue;
1215
2a326321
LP
1216 /* Take the time here, if it isn't set yet, so
1217 * that we do all validations with the same
1218 * time. */
1219 if (realtime == USEC_INFINITY)
1220 realtime = now(CLOCK_REALTIME);
1221
67d0ce88
RP
1222 /* Have we seen an unreasonable number of invalid signaures? */
1223 if (nvalidations > DNSSEC_INVALID_MAX) {
1224 if (ret_rrsig)
1225 *ret_rrsig = NULL;
1226 *result = DNSSEC_TOO_MANY_VALIDATIONS;
1227 return (int) nvalidations;
1228 }
1229
2b442ac8
LP
1230 /* Yay, we found a matching RRSIG with a matching
1231 * DNSKEY, awesome. Now let's verify all entries of
1232 * the RRSet against the RRSIG and DNSKEY
1233 * combination. */
1234
547973de 1235 r = dnssec_verify_rrset(a, key, rrsig, dnskey, realtime, &one_result);
203f1b35 1236 if (r < 0)
2b442ac8 1237 return r;
203f1b35 1238
67d0ce88
RP
1239 nvalidations++;
1240
203f1b35
LP
1241 switch (one_result) {
1242
1243 case DNSSEC_VALIDATED:
0c7bff0a 1244 case DNSSEC_VALIDATED_WILDCARD:
203f1b35 1245 /* Yay, the RR has been validated,
ee3d6aff 1246 * return immediately, but fix up the expiry */
0c7bff0a
LP
1247 if (ret_rrsig)
1248 *ret_rrsig = rrsig;
1249
1250 *result = one_result;
67d0ce88 1251 return (int) nvalidations;
2b442ac8 1252
203f1b35
LP
1253 case DNSSEC_INVALID:
1254 /* If the signature is invalid, let's try another
1255 key and/or signature. After all they
1256 key_tags and stuff are not unique, and
1257 might be shared by multiple keys. */
1258 found_invalid = true;
1259 continue;
1260
1261 case DNSSEC_UNSUPPORTED_ALGORITHM:
1262 /* If the key algorithm is
1263 unsupported, try another
1264 RRSIG/DNSKEY pair, but remember we
1265 encountered this, so that we can
1266 return a proper error when we
1267 encounter nothing better. */
1268 found_unsupported_algorithm = true;
1269 continue;
1270
1271 case DNSSEC_SIGNATURE_EXPIRED:
1272 /* If the signature is expired, try
1273 another one, but remember it, so
1274 that we can return this */
1275 found_expired_rrsig = true;
1276 continue;
1277
1278 default:
04499a70 1279 assert_not_reached();
203f1b35 1280 }
2b442ac8
LP
1281 }
1282 }
1283
203f1b35
LP
1284 if (found_expired_rrsig)
1285 *result = DNSSEC_SIGNATURE_EXPIRED;
1286 else if (found_unsupported_algorithm)
1287 *result = DNSSEC_UNSUPPORTED_ALGORITHM;
1288 else if (found_invalid)
547973de
LP
1289 *result = DNSSEC_INVALID;
1290 else if (found_rrsig)
1291 *result = DNSSEC_MISSING_KEY;
1292 else
1293 *result = DNSSEC_NO_SIGNATURE;
2b442ac8 1294
0c7bff0a
LP
1295 if (ret_rrsig)
1296 *ret_rrsig = NULL;
1297
67d0ce88 1298 return (int) nvalidations;
2b442ac8
LP
1299}
1300
105e1512
LP
1301int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
1302 DnsResourceRecord *rr;
1303 int r;
1304
5238e957 1305 /* Checks whether there's at least one RRSIG in 'a' that protects RRs of the specified key */
105e1512
LP
1306
1307 DNS_ANSWER_FOREACH(rr, a) {
1308 r = dnssec_key_match_rrsig(key, rr);
1309 if (r < 0)
1310 return r;
1311 if (r > 0)
1312 return 1;
1313 }
1314
1315 return 0;
1316}
1317
1cd7a2c1 1318static hash_md_t digest_to_hash_md(uint8_t algorithm) {
a1972a91 1319
1cd7a2c1 1320 /* Translates a DNSSEC digest algorithm into an openssl/gcrypt digest identifier */
a1972a91
LP
1321
1322 switch (algorithm) {
1323
1324 case DNSSEC_DIGEST_SHA1:
1cd7a2c1 1325 return OPENSSL_OR_GCRYPT(EVP_sha1(), GCRY_MD_SHA1);
a1972a91
LP
1326
1327 case DNSSEC_DIGEST_SHA256:
1cd7a2c1 1328 return OPENSSL_OR_GCRYPT(EVP_sha256(), GCRY_MD_SHA256);
a1972a91 1329
af22c65b 1330 case DNSSEC_DIGEST_SHA384:
1cd7a2c1 1331 return OPENSSL_OR_GCRYPT(EVP_sha384(), GCRY_MD_SHA384);
af22c65b 1332
a1972a91 1333 default:
1cd7a2c1 1334 return OPENSSL_OR_GCRYPT(NULL, -EOPNOTSUPP);
a1972a91
LP
1335 }
1336}
1337
96bb7673 1338int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
5e55cde9 1339 uint8_t wire_format[DNS_WIRE_FORMAT_HOSTNAME_MAX];
72270f29 1340 size_t encoded_length;
1cd7a2c1 1341 int r;
2b442ac8
LP
1342
1343 assert(dnskey);
1344 assert(ds);
1345
1346 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
1347
1348 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1349 return -EINVAL;
1350 if (ds->key->type != DNS_TYPE_DS)
1351 return -EINVAL;
1352 if ((dnskey->dnskey.flags & DNSKEY_FLAG_ZONE_KEY) == 0)
1353 return -EKEYREJECTED;
0c857028
LP
1354 if (!mask_revoke && (dnskey->dnskey.flags & DNSKEY_FLAG_REVOKE))
1355 return -EKEYREJECTED;
2b442ac8
LP
1356 if (dnskey->dnskey.protocol != 3)
1357 return -EKEYREJECTED;
1358
2b442ac8
LP
1359 if (dnskey->dnskey.algorithm != ds->ds.algorithm)
1360 return 0;
0c857028 1361 if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag)
2b442ac8
LP
1362 return 0;
1363
1cd7a2c1
KK
1364 r = dns_name_to_wire_format(dns_resource_key_name(dnskey->key), wire_format, sizeof wire_format, true);
1365 if (r < 0)
1366 return r;
72270f29 1367 encoded_length = r;
0638401a 1368
1cd7a2c1 1369 hash_md_t md_algorithm = digest_to_hash_md(ds->ds.digest_type);
2b442ac8 1370
1cd7a2c1
KK
1371#if PREFER_OPENSSL
1372 if (!md_algorithm)
1373 return -EOPNOTSUPP;
1374
1375 _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = NULL;
1376 uint8_t result[EVP_MAX_MD_SIZE];
1377
1378 unsigned hash_size = EVP_MD_size(md_algorithm);
a1972a91 1379 assert(hash_size > 0);
2b442ac8 1380
a1972a91
LP
1381 if (ds->ds.digest_size != hash_size)
1382 return 0;
2b442ac8 1383
1cd7a2c1
KK
1384 ctx = EVP_MD_CTX_new();
1385 if (!ctx)
1386 return -ENOMEM;
1387
1388 if (EVP_DigestInit_ex(ctx, md_algorithm, NULL) <= 0)
1389 return -EIO;
1390
72270f29 1391 if (EVP_DigestUpdate(ctx, wire_format, encoded_length) <= 0)
1cd7a2c1
KK
1392 return -EIO;
1393
1394 if (mask_revoke)
1395 md_add_uint16(ctx, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
1396 else
1397 md_add_uint16(ctx, dnskey->dnskey.flags);
1398
1399 r = md_add_uint8(ctx, dnskey->dnskey.protocol);
1400 if (r <= 0)
a1972a91 1401 return r;
1cd7a2c1
KK
1402 r = md_add_uint8(ctx, dnskey->dnskey.algorithm);
1403 if (r <= 0)
1404 return r;
1405 if (EVP_DigestUpdate(ctx, dnskey->dnskey.key, dnskey->dnskey.key_size) <= 0)
1406 return -EIO;
1407
1408 if (EVP_DigestFinal_ex(ctx, result, NULL) <= 0)
1409 return -EIO;
1410
1411#else
1412 if (md_algorithm < 0)
1413 return -EOPNOTSUPP;
1414
8707c9b2
LB
1415 r = initialize_libgcrypt(false);
1416 if (r < 0)
1417 return r;
e28df392 1418
1cd7a2c1 1419 _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
2b442ac8 1420
1cd7a2c1
KK
1421 size_t hash_size = gcry_md_get_algo_dlen(md_algorithm);
1422 assert(hash_size > 0);
1423
1424 if (ds->ds.digest_size != hash_size)
1425 return 0;
1426
1427 gcry_error_t err = gcry_md_open(&md, md_algorithm, 0);
248b1e0a 1428 if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md)
2b442ac8
LP
1429 return -EIO;
1430
72270f29 1431 gcry_md_write(md, wire_format, encoded_length);
0c857028
LP
1432 if (mask_revoke)
1433 md_add_uint16(md, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE);
1434 else
1435 md_add_uint16(md, dnskey->dnskey.flags);
2b442ac8
LP
1436 md_add_uint8(md, dnskey->dnskey.protocol);
1437 md_add_uint8(md, dnskey->dnskey.algorithm);
1438 gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size);
1439
1cd7a2c1 1440 void *result = gcry_md_read(md, 0);
15c53310
ZJS
1441 if (!result)
1442 return -EIO;
1cd7a2c1 1443#endif
2b442ac8 1444
c910c520 1445 return memcmp(result, ds->ds.digest, ds->ds.digest_size) == 0;
2b442ac8 1446}
24710c48 1447
96bb7673 1448int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
547973de 1449 DnsResourceRecord *ds;
105e1512 1450 DnsAnswerFlags flags;
547973de
LP
1451 int r;
1452
1453 assert(dnskey);
1454
1455 if (dnskey->key->type != DNS_TYPE_DNSKEY)
1456 return 0;
1457
105e1512
LP
1458 DNS_ANSWER_FOREACH_FLAGS(ds, flags, validated_ds) {
1459
1460 if ((flags & DNS_ANSWER_AUTHENTICATED) == 0)
1461 continue;
547973de
LP
1462
1463 if (ds->key->type != DNS_TYPE_DS)
1464 continue;
d1c4ee32
LP
1465 if (ds->key->class != dnskey->key->class)
1466 continue;
1467
1c02e7ba 1468 r = dns_name_equal(dns_resource_key_name(dnskey->key), dns_resource_key_name(ds->key));
d1c4ee32
LP
1469 if (r < 0)
1470 return r;
1471 if (r == 0)
1472 continue;
1473
96bb7673 1474 r = dnssec_verify_dnskey_by_ds(dnskey, ds, false);
54b778e7
LP
1475 if (IN_SET(r, -EKEYREJECTED, -EOPNOTSUPP))
1476 return 0; /* The DNSKEY is revoked or otherwise invalid, or we don't support the digest algorithm */
547973de
LP
1477 if (r < 0)
1478 return r;
1479 if (r > 0)
1480 return 1;
1481 }
1482
1483 return 0;
1484}
1485
1736344e 1486static hash_md_t nsec3_hash_to_hash_md(uint8_t algorithm) {
d15ad742 1487
1736344e 1488 /* Translates a DNSSEC NSEC3 hash algorithm into an openssl/gcrypt digest identifier */
d15ad742
LP
1489
1490 switch (algorithm) {
1491
1492 case NSEC3_ALGORITHM_SHA1:
1736344e 1493 return OPENSSL_OR_GCRYPT(EVP_sha1(), GCRY_MD_SHA1);
d15ad742
LP
1494
1495 default:
1736344e 1496 return OPENSSL_OR_GCRYPT(NULL, -EOPNOTSUPP);
d15ad742
LP
1497 }
1498}
1499
1d3db294 1500int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
5e55cde9 1501 uint8_t wire_format[DNS_WIRE_FORMAT_HOSTNAME_MAX];
72667f08
LP
1502 int r;
1503
1504 assert(nsec3);
1505 assert(name);
1506 assert(ret);
1507
1508 if (nsec3->key->type != DNS_TYPE_NSEC3)
1509 return -EINVAL;
1510
baaa35ad
ZJS
1511 if (nsec3->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1512 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1513 "Ignoring NSEC3 RR %s with excessive number of iterations.",
1514 dns_resource_record_to_string(nsec3));
a8f158b9 1515
1736344e
KK
1516 hash_md_t algorithm = nsec3_hash_to_hash_md(nsec3->nsec3.algorithm);
1517#if PREFER_OPENSSL
1518 if (!algorithm)
1519 return -EOPNOTSUPP;
1520
1521 size_t hash_size = EVP_MD_size(algorithm);
1522 assert(hash_size > 0);
1523
1524 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1525 return -EINVAL;
1526
1527 _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = EVP_MD_CTX_new();
1528 if (!ctx)
1529 return -ENOMEM;
1530
1531 if (EVP_DigestInit_ex(ctx, algorithm, NULL) <= 0)
1532 return -EIO;
1533
1534 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1535 if (r < 0)
1536 return r;
1537
1538 if (EVP_DigestUpdate(ctx, wire_format, r) <= 0)
1539 return -EIO;
1540 if (EVP_DigestUpdate(ctx, nsec3->nsec3.salt, nsec3->nsec3.salt_size) <= 0)
1541 return -EIO;
1542
1543 uint8_t result[EVP_MAX_MD_SIZE];
1544 if (EVP_DigestFinal_ex(ctx, result, NULL) <= 0)
1545 return -EIO;
1546
1547 for (unsigned k = 0; k < nsec3->nsec3.iterations; k++) {
1548 if (EVP_DigestInit_ex(ctx, algorithm, NULL) <= 0)
1549 return -EIO;
1550 if (EVP_DigestUpdate(ctx, result, hash_size) <= 0)
1551 return -EIO;
1552 if (EVP_DigestUpdate(ctx, nsec3->nsec3.salt, nsec3->nsec3.salt_size) <= 0)
1553 return -EIO;
1554
1555 if (EVP_DigestFinal_ex(ctx, result, NULL) <= 0)
1556 return -EIO;
1557 }
1558#else
72667f08
LP
1559 if (algorithm < 0)
1560 return algorithm;
1561
8707c9b2
LB
1562 r = initialize_libgcrypt(false);
1563 if (r < 0)
1564 return r;
72667f08 1565
72270f29 1566 size_t encoded_length;
1736344e 1567 unsigned hash_size = gcry_md_get_algo_dlen(algorithm);
72667f08
LP
1568 assert(hash_size > 0);
1569
1570 if (nsec3->nsec3.next_hashed_name_size != hash_size)
1571 return -EINVAL;
1572
1573 r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true);
1574 if (r < 0)
1575 return r;
72270f29 1576 encoded_length = r;
72667f08 1577
1736344e
KK
1578 _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL;
1579 gcry_error_t err = gcry_md_open(&md, algorithm, 0);
248b1e0a 1580 if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md)
72667f08
LP
1581 return -EIO;
1582
72270f29 1583 gcry_md_write(md, wire_format, encoded_length);
72667f08
LP
1584 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1585
1736344e 1586 void *result = gcry_md_read(md, 0);
677ba9d0
LB
1587 if (!result)
1588 return -EIO;
72667f08 1589
6f1d18ae 1590 for (unsigned k = 0; k < nsec3->nsec3.iterations; k++) {
72667f08
LP
1591 uint8_t tmp[hash_size];
1592 memcpy(tmp, result, hash_size);
1593
1594 gcry_md_reset(md);
1595 gcry_md_write(md, tmp, hash_size);
1596 gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size);
1597
1598 result = gcry_md_read(md, 0);
677ba9d0
LB
1599 if (!result)
1600 return -EIO;
72667f08 1601 }
1736344e 1602#endif
72667f08
LP
1603
1604 memcpy(ret, result, hash_size);
677ba9d0 1605 return (int) hash_size;
72667f08
LP
1606}
1607
3f5ecaad 1608static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
db5b0e92
LP
1609 const char *a, *b;
1610 int r;
1611
1612 assert(rr);
1613
db5b0e92
LP
1614 if (rr->key->type != DNS_TYPE_NSEC3)
1615 return 0;
1616
1f133e0d 1617 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
db5b0e92
LP
1618 if (!IN_SET(rr->nsec3.flags, 0, 1))
1619 return 0;
1620
d15ad742 1621 /* Ignore NSEC3 RRs whose algorithm we don't know */
1736344e
KK
1622#if PREFER_OPENSSL
1623 if (!nsec3_hash_to_hash_md(rr->nsec3.algorithm))
1624 return 0;
1625#else
1626 if (nsec3_hash_to_hash_md(rr->nsec3.algorithm) < 0)
d15ad742 1627 return 0;
1736344e
KK
1628#endif
1629
a8f158b9
LP
1630 /* Ignore NSEC3 RRs with an excessive number of required iterations */
1631 if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
1632 return 0;
d15ad742 1633
cbd100ac
LP
1634 /* Ignore NSEC3 RRs generated from wildcards. If these NSEC3 RRs weren't correctly signed we can't make this
1635 * check (since rr->n_skip_labels_source is -1), but that's OK, as we won't trust them anyway in that case. */
98e80bf9 1636 if (!IN_SET(rr->n_skip_labels_source, 0, UINT8_MAX))
93a3b929
LP
1637 return 0;
1638 /* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */
98e80bf9 1639 if (!IN_SET(rr->n_skip_labels_signer, 1, UINT8_MAX))
93a3b929
LP
1640 return 0;
1641
db5b0e92
LP
1642 if (!nsec3)
1643 return 1;
1644
1645 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
1646
1647 if (nsec3 == rr) /* Shortcut */
1648 return 1;
1649
1650 if (rr->key->class != nsec3->key->class)
1651 return 0;
1652 if (rr->nsec3.algorithm != nsec3->nsec3.algorithm)
1653 return 0;
1654 if (rr->nsec3.iterations != nsec3->nsec3.iterations)
1655 return 0;
1656 if (rr->nsec3.salt_size != nsec3->nsec3.salt_size)
1657 return 0;
1f66559c 1658 if (memcmp_safe(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0)
db5b0e92
LP
1659 return 0;
1660
1c02e7ba 1661 a = dns_resource_key_name(rr->key);
db5b0e92 1662 r = dns_name_parent(&a); /* strip off hash */
f20db199 1663 if (r <= 0)
db5b0e92 1664 return r;
db5b0e92 1665
1c02e7ba 1666 b = dns_resource_key_name(nsec3->key);
db5b0e92 1667 r = dns_name_parent(&b); /* strip off hash */
f20db199 1668 if (r <= 0)
db5b0e92 1669 return r;
db5b0e92 1670
93a3b929 1671 /* Make sure both have the same parent */
db5b0e92
LP
1672 return dns_name_equal(a, b);
1673}
1674
cdbffec0
LP
1675static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size, const char *zone, char **ret) {
1676 _cleanup_free_ char *l = NULL;
1677 char *j;
1678
1679 assert(hashed);
1680 assert(hashed_size > 0);
1681 assert(zone);
1682 assert(ret);
1683
1684 l = base32hexmem(hashed, hashed_size, false);
1685 if (!l)
1686 return -ENOMEM;
1687
605405c6 1688 j = strjoin(l, ".", zone);
cdbffec0
LP
1689 if (!j)
1690 return -ENOMEM;
1691
1692 *ret = j;
1693 return (int) hashed_size;
1694}
1695
1696static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain, const char *zone, char **ret) {
105e1512 1697 uint8_t hashed[DNSSEC_HASH_SIZE_MAX];
6f76ec5a
TG
1698 int hashed_size;
1699
1700 assert(nsec3);
1701 assert(domain);
1702 assert(zone);
1703 assert(ret);
1704
1705 hashed_size = dnssec_nsec3_hash(nsec3, domain, hashed);
1706 if (hashed_size < 0)
1707 return hashed_size;
1708
cdbffec0 1709 return nsec3_hashed_domain_format(hashed, (size_t) hashed_size, zone, ret);
6f76ec5a
TG
1710}
1711
35ad41d3
TG
1712/* See RFC 5155, Section 8
1713 * First try to find a NSEC3 record that matches our query precisely, if that fails, find the closest
1714 * enclosure. Secondly, find a proof that there is no closer enclosure and either a proof that there
1715 * is no wildcard domain as a direct descendant of the closest enclosure, or find an NSEC3 record that
1716 * matches the wildcard domain.
1717 *
1718 * Based on this we can prove either the existence of the record in @key, or NXDOMAIN or NODATA, or
d51c4fca 1719 * that there is no proof either way. The latter is the case if a proof of non-existence of a given
35ad41d3
TG
1720 * name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
1721 * to conclude anything we indicate this by returning NO_RR. */
d3760be0 1722static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
d41084a5
LP
1723 _cleanup_free_ char *next_closer_domain = NULL, *wildcard_domain = NULL;
1724 const char *zone, *p, *pp = NULL, *wildcard;
7e35195f 1725 DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
105e1512
LP
1726 DnsAnswerFlags flags;
1727 int hashed_size, r;
35ad41d3 1728 bool a, no_closer = false, no_wildcard = false, optout = false;
72667f08
LP
1729
1730 assert(key);
1731 assert(result);
1732
d1511b33
TG
1733 /* First step, find the zone name and the NSEC3 parameters of the zone.
1734 * it is sufficient to look for the longest common suffix we find with
1735 * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3
1736 * records from a given zone in a response must use the same
1737 * parameters. */
1c02e7ba 1738 zone = dns_resource_key_name(key);
13b78323 1739 for (;;) {
7e35195f 1740 DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) {
3f5ecaad 1741 r = nsec3_is_good(zone_rr, NULL);
db5b0e92
LP
1742 if (r < 0)
1743 return r;
1744 if (r == 0)
13b78323
LP
1745 continue;
1746
1c02e7ba 1747 r = dns_name_equal_skip(dns_resource_key_name(zone_rr->key), 1, zone);
13b78323
LP
1748 if (r < 0)
1749 return r;
1750 if (r > 0)
d1511b33 1751 goto found_zone;
13b78323
LP
1752 }
1753
1754 /* Strip one label from the front */
d1511b33 1755 r = dns_name_parent(&zone);
13b78323
LP
1756 if (r < 0)
1757 return r;
1758 if (r == 0)
1759 break;
1760 }
1761
1762 *result = DNSSEC_NSEC_NO_RR;
1763 return 0;
1764
d1511b33 1765found_zone:
13b78323 1766 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
1c02e7ba 1767 p = dns_resource_key_name(key);
105e1512 1768 for (;;) {
6f76ec5a 1769 _cleanup_free_ char *hashed_domain = NULL;
72667f08 1770
cdbffec0 1771 hashed_size = nsec3_hashed_domain_make(zone_rr, p, zone, &hashed_domain);
db5b0e92
LP
1772 if (hashed_size == -EOPNOTSUPP) {
1773 *result = DNSSEC_NSEC_UNSUPPORTED_ALGORITHM;
1774 return 0;
1775 }
1776 if (hashed_size < 0)
1777 return hashed_size;
72667f08 1778
d1511b33 1779 DNS_ANSWER_FOREACH_FLAGS(enclosure_rr, flags, answer) {
db5b0e92 1780
3f5ecaad 1781 r = nsec3_is_good(enclosure_rr, zone_rr);
72667f08
LP
1782 if (r < 0)
1783 return r;
105e1512
LP
1784 if (r == 0)
1785 continue;
1786
d1511b33 1787 if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size)
db5b0e92 1788 continue;
105e1512 1789
1c02e7ba 1790 r = dns_name_equal(dns_resource_key_name(enclosure_rr->key), hashed_domain);
72667f08
LP
1791 if (r < 0)
1792 return r;
ed29bfdc
LP
1793 if (r > 0) {
1794 a = flags & DNS_ANSWER_AUTHENTICATED;
13b78323 1795 goto found_closest_encloser;
ed29bfdc 1796 }
105e1512
LP
1797 }
1798
1799 /* We didn't find the closest encloser with this name,
1800 * but let's remember this domain name, it might be
1801 * the next closer name */
1802
1803 pp = p;
1804
1805 /* Strip one label from the front */
1806 r = dns_name_parent(&p);
1807 if (r < 0)
1808 return r;
1809 if (r == 0)
72667f08 1810 break;
105e1512 1811 }
72667f08 1812
105e1512
LP
1813 *result = DNSSEC_NSEC_NO_RR;
1814 return 0;
72667f08 1815
13b78323 1816found_closest_encloser:
105e1512 1817 /* We found a closest encloser in 'p'; next closer is 'pp' */
72667f08 1818
105e1512 1819 if (!pp) {
352af308
LP
1820 /* We have an exact match! If we area looking for a DS RR, then we must insist that we got the NSEC3 RR
1821 * from the parent. Otherwise the one from the child. Do so, by checking whether SOA and NS are
1822 * appropriately set. */
1823
1824 if (key->type == DNS_TYPE_DS) {
1825 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
1826 return -EBADMSG;
1827 } else {
1828 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1829 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
1830 return -EBADMSG;
1831 }
1832
105e1512 1833 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
146035b3
TG
1834 if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
1835 *result = DNSSEC_NSEC_FOUND;
1836 else if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_CNAME))
1837 *result = DNSSEC_NSEC_CNAME;
1838 else
1839 *result = DNSSEC_NSEC_NODATA;
1840
d3760be0
LP
1841 if (authenticated)
1842 *authenticated = a;
1843 if (ttl)
1844 *ttl = enclosure_rr->ttl;
146035b3 1845
105e1512
LP
1846 return 0;
1847 }
72667f08 1848
352af308
LP
1849 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
1850 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
1851 return -EBADMSG;
1852
1853 /* Ensure that this data is from the delegated domain
1854 * (i.e. originates from the "lower" DNS server), and isn't
1855 * just glue records (i.e. doesn't originate from the "upper"
1856 * DNS server). */
1857 if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
1858 !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
1859 return -EBADMSG;
1860
35ad41d3
TG
1861 /* Prove that there is no next closer and whether or not there is a wildcard domain. */
1862
d41084a5 1863 wildcard = strjoina("*.", p);
cdbffec0 1864 r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
105e1512
LP
1865 if (r < 0)
1866 return r;
1867 if (r != hashed_size)
1868 return -EBADMSG;
72667f08 1869
cdbffec0 1870 r = nsec3_hashed_domain_make(enclosure_rr, pp, zone, &next_closer_domain);
105e1512
LP
1871 if (r < 0)
1872 return r;
1873 if (r != hashed_size)
1874 return -EBADMSG;
72667f08 1875
105e1512 1876 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
cdbffec0 1877 _cleanup_free_ char *next_hashed_domain = NULL;
105e1512 1878
3f5ecaad 1879 r = nsec3_is_good(rr, zone_rr);
105e1512
LP
1880 if (r < 0)
1881 return r;
1882 if (r == 0)
1883 continue;
1884
cdbffec0
LP
1885 r = nsec3_hashed_domain_format(rr->nsec3.next_hashed_name, rr->nsec3.next_hashed_name_size, zone, &next_hashed_domain);
1886 if (r < 0)
1887 return r;
105e1512 1888
1c02e7ba 1889 r = dns_name_between(dns_resource_key_name(rr->key), next_closer_domain, next_hashed_domain);
105e1512
LP
1890 if (r < 0)
1891 return r;
1892 if (r > 0) {
1893 if (rr->nsec3.flags & 1)
35ad41d3 1894 optout = true;
105e1512 1895
35ad41d3
TG
1896 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1897
1898 no_closer = true;
1899 }
1900
1c02e7ba 1901 r = dns_name_equal(dns_resource_key_name(rr->key), wildcard_domain);
35ad41d3
TG
1902 if (r < 0)
1903 return r;
1904 if (r > 0) {
1905 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1906
1907 wildcard_rr = rr;
1908 }
1909
1c02e7ba 1910 r = dns_name_between(dns_resource_key_name(rr->key), wildcard_domain, next_hashed_domain);
35ad41d3
TG
1911 if (r < 0)
1912 return r;
1913 if (r > 0) {
1914 if (rr->nsec3.flags & 1)
1915 /* This only makes sense if we have a wildcard delegation, which is
1916 * very unlikely, see RFC 4592, Section 4.2, but we cannot rely on
1917 * this not happening, so hence cannot simply conclude NXDOMAIN as
1918 * we would wish */
1919 optout = true;
1920
1921 a = a && (flags & DNS_ANSWER_AUTHENTICATED);
1922
1923 no_wildcard = true;
105e1512
LP
1924 }
1925 }
1926
35ad41d3
TG
1927 if (wildcard_rr && no_wildcard)
1928 return -EBADMSG;
1929
1930 if (!no_closer) {
1931 *result = DNSSEC_NSEC_NO_RR;
35ad41d3
TG
1932 return 0;
1933 }
1934
1935 if (wildcard_rr) {
1936 /* A wildcard exists that matches our query. */
1937 if (optout)
1938 /* This is not specified in any RFC to the best of my knowledge, but
1939 * if the next closer enclosure is covered by an opt-out NSEC3 RR
1940 * it means that we cannot prove that the source of synthesis is
1941 * correct, as there may be a closer match. */
1942 *result = DNSSEC_NSEC_OPTOUT;
1943 else if (bitmap_isset(wildcard_rr->nsec3.types, key->type))
1944 *result = DNSSEC_NSEC_FOUND;
1945 else if (bitmap_isset(wildcard_rr->nsec3.types, DNS_TYPE_CNAME))
1946 *result = DNSSEC_NSEC_CNAME;
1947 else
1948 *result = DNSSEC_NSEC_NODATA;
1949 } else {
1950 if (optout)
1951 /* The RFC only specifies that we have to care for optout for NODATA for
1952 * DS records. However, children of an insecure opt-out delegation should
1953 * also be considered opt-out, rather than verified NXDOMAIN.
1954 * Note that we do not require a proof of wildcard non-existence if the
1955 * next closer domain is covered by an opt-out, as that would not provide
1956 * any additional information. */
1957 *result = DNSSEC_NSEC_OPTOUT;
1958 else if (no_wildcard)
1959 *result = DNSSEC_NSEC_NXDOMAIN;
1960 else {
1961 *result = DNSSEC_NSEC_NO_RR;
1962
1963 return 0;
1964 }
1965 }
1966
d3760be0
LP
1967 if (authenticated)
1968 *authenticated = a;
1969
1970 if (ttl)
1971 *ttl = enclosure_rr->ttl;
35ad41d3 1972
105e1512
LP
1973 return 0;
1974}
1975
ab481675 1976static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) {
fd7e9887 1977 char label[DNS_LABEL_MAX+1];
ab481675
LP
1978 const char *n;
1979 int r;
1980
1981 assert(rr);
1982 assert(rr->key->type == DNS_TYPE_NSEC);
1983
1984 /* Checks whether the specified RR has a name beginning in "*.", and if the rest is a suffix of our name */
1985
1986 if (rr->n_skip_labels_source != 1)
1987 return 0;
1988
1c02e7ba 1989 n = dns_resource_key_name(rr->key);
7470cc4c 1990 r = dns_label_unescape(&n, label, sizeof label, 0);
ab481675
LP
1991 if (r <= 0)
1992 return r;
1993 if (r != 1 || label[0] != '*')
1994 return 0;
1995
1996 return dns_name_endswith(name, n);
1997}
1998
1999static int dnssec_nsec_in_path(DnsResourceRecord *rr, const char *name) {
b9282bc1
LP
2000 const char *nn, *common_suffix;
2001 int r;
2002
2003 assert(rr);
2004 assert(rr->key->type == DNS_TYPE_NSEC);
2005
2006 /* Checks whether the specified nsec RR indicates that name is an empty non-terminal (ENT)
2007 *
2008 * A couple of examples:
2009 *
2010 * NSEC bar → waldo.foo.bar: indicates that foo.bar exists and is an ENT
2011 * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that xoo.bar and zzz.xoo.bar exist and are ENTs
2012 * NSEC yyy.zzz.xoo.bar → bar: indicates pretty much nothing about ENTs
2013 */
2014
2015 /* First, determine parent of next domain. */
2016 nn = rr->nsec.next_domain_name;
2017 r = dns_name_parent(&nn);
2018 if (r <= 0)
2019 return r;
2020
2021 /* If the name we just determined is not equal or child of the name we are interested in, then we can't say
2022 * anything at all. */
2023 r = dns_name_endswith(nn, name);
2024 if (r <= 0)
2025 return r;
2026
61233823 2027 /* If the name we are interested in is not a prefix of the common suffix of the NSEC RR's owner and next domain names, then we can't say anything either. */
1c02e7ba 2028 r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix);
b9282bc1
LP
2029 if (r < 0)
2030 return r;
2031
2032 return dns_name_endswith(name, common_suffix);
2033}
2034
ab481675
LP
2035static int dnssec_nsec_from_parent_zone(DnsResourceRecord *rr, const char *name) {
2036 int r;
2037
2038 assert(rr);
2039 assert(rr->key->type == DNS_TYPE_NSEC);
2040
2041 /* Checks whether this NSEC originates to the parent zone or the child zone. */
2042
2043 r = dns_name_parent(&name);
2044 if (r <= 0)
2045 return r;
2046
1c02e7ba 2047 r = dns_name_equal(name, dns_resource_key_name(rr->key));
ab481675
LP
2048 if (r <= 0)
2049 return r;
2050
2051 /* DNAME, and NS without SOA is an indication for a delegation. */
2052 if (bitmap_isset(rr->nsec.types, DNS_TYPE_DNAME))
2053 return 1;
2054
2055 if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) && !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
2056 return 1;
2057
2058 return 0;
2059}
2060
2061static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) {
0b491556 2062 const char *signer;
ab481675
LP
2063 int r;
2064
2065 assert(rr);
2066 assert(rr->key->type == DNS_TYPE_NSEC);
2067
0b491556
LP
2068 /* Checks whether the name is covered by this NSEC RR. This means, that the name is somewhere below the NSEC's
2069 * signer name, and between the NSEC's two names. */
ab481675 2070
0b491556 2071 r = dns_resource_record_signer(rr, &signer);
ab481675
LP
2072 if (r < 0)
2073 return r;
2074
0b491556
LP
2075 r = dns_name_endswith(name, signer); /* this NSEC isn't suitable the name is not in the signer's domain */
2076 if (r <= 0)
2077 return r;
ab481675 2078
0b491556 2079 return dns_name_between(dns_resource_key_name(rr->key), name, rr->nsec.next_domain_name);
ab481675
LP
2080}
2081
13e6f383
AT
2082static int dnssec_nsec_generate_wildcard(DnsResourceRecord *rr, const char *name, char **wc) {
2083 const char *common_suffix1, *common_suffix2, *signer;
2084 int r, labels1, labels2;
d86c982a
LP
2085
2086 assert(rr);
2087 assert(rr->key->type == DNS_TYPE_NSEC);
2088
13e6f383 2089 /* Generates "Wildcard at the Closest Encloser" for the given name and NSEC RR. */
d86c982a 2090
0b491556 2091 r = dns_resource_record_signer(rr, &signer);
d86c982a
LP
2092 if (r < 0)
2093 return r;
2094
0b491556 2095 r = dns_name_endswith(name, signer); /* this NSEC isn't suitable the name is not in the signer's domain */
d86c982a
LP
2096 if (r <= 0)
2097 return r;
2098
13e6f383 2099 r = dns_name_common_suffix(name, dns_resource_key_name(rr->key), &common_suffix1);
0b491556
LP
2100 if (r < 0)
2101 return r;
0b491556 2102
13e6f383
AT
2103 r = dns_name_common_suffix(name, rr->nsec.next_domain_name, &common_suffix2);
2104 if (r < 0)
2105 return r;
2106
2107 labels1 = dns_name_count_labels(common_suffix1);
2108 if (labels1 < 0)
2109 return labels1;
2110
2111 labels2 = dns_name_count_labels(common_suffix2);
2112 if (labels2 < 0)
2113 return labels2;
2114
2115 if (labels1 > labels2)
2116 r = dns_name_concat("*", common_suffix1, 0, wc);
2117 else
2118 r = dns_name_concat("*", common_suffix2, 0, wc);
2119
97c2ea26
LP
2120 if (r < 0)
2121 return r;
2122
13e6f383 2123 return 0;
d86c982a
LP
2124}
2125
0c7bff0a 2126int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
d86c982a
LP
2127 bool have_nsec3 = false, covering_rr_authenticated = false, wildcard_rr_authenticated = false;
2128 DnsResourceRecord *rr, *covering_rr = NULL, *wildcard_rr = NULL;
105e1512 2129 DnsAnswerFlags flags;
b9282bc1 2130 const char *name;
105e1512
LP
2131 int r;
2132
2133 assert(key);
2134 assert(result);
2135
2136 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
2137
1c02e7ba 2138 name = dns_resource_key_name(key);
b9282bc1 2139
105e1512
LP
2140 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
2141
2142 if (rr->key->class != key->class)
2143 continue;
2144
ab481675 2145 have_nsec3 = have_nsec3 || (rr->key->type == DNS_TYPE_NSEC3);
105e1512 2146
ab481675
LP
2147 if (rr->key->type != DNS_TYPE_NSEC)
2148 continue;
2149
2150 /* The following checks only make sense for NSEC RRs that are not expanded from a wildcard */
2151 r = dns_resource_record_is_synthetic(rr);
2f4c2db2
LP
2152 if (r == -ENODATA) /* No signing RR known. */
2153 continue;
ab481675
LP
2154 if (r < 0)
2155 return r;
2156 if (r > 0)
2157 continue;
105e1512 2158
ab481675 2159 /* Check if this is a direct match. If so, we have encountered a NODATA case */
1c02e7ba 2160 r = dns_name_equal(dns_resource_key_name(rr->key), name);
ab481675
LP
2161 if (r < 0)
2162 return r;
2163 if (r == 0) {
2164 /* If it's not a direct match, maybe it's a wild card match? */
2165 r = dnssec_nsec_wildcard_equal(rr, name);
105e1512
LP
2166 if (r < 0)
2167 return r;
ab481675
LP
2168 }
2169 if (r > 0) {
2170 if (key->type == DNS_TYPE_DS) {
2171 /* If we look for a DS RR and the server sent us the NSEC RR of the child zone
2172 * we have a problem. For DS RRs we want the NSEC RR from the parent */
2173 if (bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
2174 continue;
2175 } else {
2176 /* For all RR types, ensure that if NS is set SOA is set too, so that we know
2177 * we got the child's NSEC. */
2178 if (bitmap_isset(rr->nsec.types, DNS_TYPE_NS) &&
2179 !bitmap_isset(rr->nsec.types, DNS_TYPE_SOA))
2180 continue;
72667f08
LP
2181 }
2182
ab481675
LP
2183 if (bitmap_isset(rr->nsec.types, key->type))
2184 *result = DNSSEC_NSEC_FOUND;
2185 else if (bitmap_isset(rr->nsec.types, DNS_TYPE_CNAME))
2186 *result = DNSSEC_NSEC_CNAME;
2187 else
b9282bc1 2188 *result = DNSSEC_NSEC_NODATA;
d3760be0 2189
ab481675
LP
2190 if (authenticated)
2191 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
2192 if (ttl)
2193 *ttl = rr->ttl;
d3760be0 2194
ab481675
LP
2195 return 0;
2196 }
b9282bc1 2197
ab481675
LP
2198 /* Check if the name we are looking for is an empty non-terminal within the owner or next name
2199 * of the NSEC RR. */
2200 r = dnssec_nsec_in_path(rr, name);
2201 if (r < 0)
2202 return r;
2203 if (r > 0) {
2204 *result = DNSSEC_NSEC_NODATA;
b9282bc1 2205
ab481675
LP
2206 if (authenticated)
2207 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
2208 if (ttl)
2209 *ttl = rr->ttl;
b9282bc1 2210
ab481675
LP
2211 return 0;
2212 }
72667f08 2213
ab481675
LP
2214 /* The following two "covering" checks, are not useful if the NSEC is from the parent */
2215 r = dnssec_nsec_from_parent_zone(rr, name);
2216 if (r < 0)
2217 return r;
2218 if (r > 0)
2219 continue;
2220
2221 /* Check if this NSEC RR proves the absence of an explicit RR under this name */
2222 r = dnssec_nsec_covers(rr, name);
2223 if (r < 0)
2224 return r;
2225 if (r > 0 && (!covering_rr || !covering_rr_authenticated)) {
2226 covering_rr = rr;
2227 covering_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
2228 }
13e6f383 2229 }
ab481675 2230
13e6f383
AT
2231 if (covering_rr) {
2232 _cleanup_free_ char *wc = NULL;
2233 r = dnssec_nsec_generate_wildcard(covering_rr, name, &wc);
ab481675
LP
2234 if (r < 0)
2235 return r;
13e6f383
AT
2236
2237 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
2238
2239 if (rr->key->class != key->class)
2240 continue;
2241
2242 if (rr->key->type != DNS_TYPE_NSEC)
2243 continue;
2244
2245 /* Check if this NSEC RR proves the nonexistence of the wildcard */
2246 r = dnssec_nsec_covers(rr, wc);
2247 if (r < 0)
2248 return r;
2249 if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) {
2250 wildcard_rr = rr;
2251 wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED;
2252 }
72667f08
LP
2253 }
2254 }
2255
d86c982a
LP
2256 if (covering_rr && wildcard_rr) {
2257 /* If we could prove that neither the name itself, nor the wildcard at the closest encloser exists, we
2258 * proved the NXDOMAIN case. */
2259 *result = DNSSEC_NSEC_NXDOMAIN;
2260
2261 if (authenticated)
2262 *authenticated = covering_rr_authenticated && wildcard_rr_authenticated;
2263 if (ttl)
2264 *ttl = MIN(covering_rr->ttl, wildcard_rr->ttl);
2265
2266 return 0;
2267 }
2268
105e1512
LP
2269 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
2270 if (have_nsec3)
d3760be0 2271 return dnssec_test_nsec3(answer, key, result, authenticated, ttl);
105e1512 2272
5238e957 2273 /* No appropriate NSEC RR found, report this. */
72667f08
LP
2274 *result = DNSSEC_NSEC_NO_RR;
2275 return 0;
2276}
2277
421cc89d 2278static int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
0c7bff0a
LP
2279 DnsResourceRecord *rr;
2280 DnsAnswerFlags flags;
2281 int r;
2282
2283 assert(name);
2284 assert(zone);
2285
2286 /* Checks whether there's an NSEC/NSEC3 that proves that the specified 'name' is non-existing in the specified
2287 * 'zone'. The 'zone' must be a suffix of the 'name'. */
2288
2289 DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
2290 bool found = false;
2291
e926785a
LP
2292 if (rr->key->type != type && type != DNS_TYPE_ANY)
2293 continue;
2294
0c7bff0a
LP
2295 switch (rr->key->type) {
2296
2297 case DNS_TYPE_NSEC:
97c67192
LP
2298
2299 /* We only care for NSEC RRs from the indicated zone */
2300 r = dns_resource_record_is_signer(rr, zone);
2301 if (r < 0)
2302 return r;
2303 if (r == 0)
2304 continue;
2305
1c02e7ba 2306 r = dns_name_between(dns_resource_key_name(rr->key), name, rr->nsec.next_domain_name);
0c7bff0a
LP
2307 if (r < 0)
2308 return r;
2309
2310 found = r > 0;
2311 break;
2312
2313 case DNS_TYPE_NSEC3: {
2314 _cleanup_free_ char *hashed_domain = NULL, *next_hashed_domain = NULL;
2315
97c67192
LP
2316 /* We only care for NSEC3 RRs from the indicated zone */
2317 r = dns_resource_record_is_signer(rr, zone);
2318 if (r < 0)
2319 return r;
2320 if (r == 0)
2321 continue;
2322
0c7bff0a
LP
2323 r = nsec3_is_good(rr, NULL);
2324 if (r < 0)
2325 return r;
2326 if (r == 0)
2327 break;
2328
2329 /* Format the domain we are testing with the NSEC3 RR's hash function */
2330 r = nsec3_hashed_domain_make(
2331 rr,
2332 name,
2333 zone,
2334 &hashed_domain);
2335 if (r < 0)
2336 return r;
2337 if ((size_t) r != rr->nsec3.next_hashed_name_size)
2338 break;
2339
2340 /* Format the NSEC3's next hashed name as proper domain name */
2341 r = nsec3_hashed_domain_format(
2342 rr->nsec3.next_hashed_name,
2343 rr->nsec3.next_hashed_name_size,
2344 zone,
2345 &next_hashed_domain);
2346 if (r < 0)
2347 return r;
2348
1c02e7ba 2349 r = dns_name_between(dns_resource_key_name(rr->key), hashed_domain, next_hashed_domain);
0c7bff0a
LP
2350 if (r < 0)
2351 return r;
2352
2353 found = r > 0;
2354 break;
2355 }
2356
2357 default:
2358 continue;
2359 }
2360
2361 if (found) {
2362 if (authenticated)
2363 *authenticated = flags & DNS_ANSWER_AUTHENTICATED;
2364 return 1;
2365 }
2366 }
2367
2368 return 0;
2369}
2370
e926785a
LP
2371static int dnssec_test_positive_wildcard_nsec3(
2372 DnsAnswer *answer,
2373 const char *name,
2374 const char *source,
2375 const char *zone,
2376 bool *authenticated) {
2377
2378 const char *next_closer = NULL;
2379 int r;
2380
2381 /* Run a positive NSEC3 wildcard proof. Specifically:
2382 *
c629ff58 2383 * A proof that the "next closer" of the generating wildcard does not exist.
e926785a
LP
2384 *
2385 * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for
2386 * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name
2387 * exists for the NSEC3 RR and we are done.
2388 *
2389 * 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
2390 * c.d.e.f does not exist. */
2391
2392 for (;;) {
2393 next_closer = name;
2394 r = dns_name_parent(&name);
f20db199 2395 if (r <= 0)
e926785a 2396 return r;
e926785a
LP
2397
2398 r = dns_name_equal(name, source);
2399 if (r < 0)
2400 return r;
2401 if (r > 0)
2402 break;
2403 }
2404
2405 return dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC3, next_closer, zone, authenticated);
2406}
2407
2408static int dnssec_test_positive_wildcard_nsec(
2409 DnsAnswer *answer,
2410 const char *name,
2411 const char *source,
2412 const char *zone,
2413 bool *_authenticated) {
2414
2415 bool authenticated = true;
2416 int r;
2417
2418 /* Run a positive NSEC wildcard proof. Specifically:
2419 *
2420 * A proof that there's neither a wildcard name nor a non-wildcard name that is a suffix of the name "name" and
2421 * a prefix of the synthesizing source "source" in the zone "zone".
2422 *
2423 * See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4
2424 *
2425 * 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
2426 * have to prove that none of the following exist:
2427 *
2428 * 1) a.b.c.d.e.f
2429 * 2) *.b.c.d.e.f
2430 * 3) b.c.d.e.f
2431 * 4) *.c.d.e.f
2432 * 5) c.d.e.f
e926785a
LP
2433 */
2434
2435 for (;;) {
2436 _cleanup_free_ char *wc = NULL;
2437 bool a = false;
2438
2439 /* Check if there's an NSEC or NSEC3 RR that proves that the mame we determined is really non-existing,
2440 * i.e between the owner name and the next name of an NSEC RR. */
2441 r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, name, zone, &a);
2442 if (r <= 0)
2443 return r;
2444
2445 authenticated = authenticated && a;
2446
2447 /* Strip one label off */
2448 r = dns_name_parent(&name);
2449 if (r <= 0)
2450 return r;
2451
2452 /* Did we reach the source of synthesis? */
2453 r = dns_name_equal(name, source);
2454 if (r < 0)
2455 return r;
2456 if (r > 0) {
2457 /* Successful exit */
2458 *_authenticated = authenticated;
2459 return 1;
2460 }
2461
2462 /* Safety check, that the source of synthesis is still our suffix */
2463 r = dns_name_endswith(name, source);
2464 if (r < 0)
2465 return r;
2466 if (r == 0)
2467 return -EBADMSG;
2468
2469 /* Replace the label we stripped off with an asterisk */
b910cc72 2470 wc = strjoin("*.", name);
e926785a
LP
2471 if (!wc)
2472 return -ENOMEM;
2473
2474 /* And check if the proof holds for the asterisk name, too */
2475 r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, wc, zone, &a);
2476 if (r <= 0)
2477 return r;
2478
2479 authenticated = authenticated && a;
2480 /* In the next iteration we'll check the non-asterisk-prefixed version */
2481 }
2482}
2483
2484int dnssec_test_positive_wildcard(
2485 DnsAnswer *answer,
2486 const char *name,
2487 const char *source,
2488 const char *zone,
2489 bool *authenticated) {
2490
2491 int r;
2492
2493 assert(name);
2494 assert(source);
2495 assert(zone);
2496 assert(authenticated);
2497
2498 r = dns_answer_contains_zone_nsec3(answer, zone);
2499 if (r < 0)
2500 return r;
2501 if (r > 0)
2502 return dnssec_test_positive_wildcard_nsec3(answer, name, source, zone, authenticated);
2503 else
2504 return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated);
2505}
2506
47091522
MO
2507#else
2508
2509int dnssec_verify_rrset(
2510 DnsAnswer *a,
2511 const DnsResourceKey *key,
2512 DnsResourceRecord *rrsig,
2513 DnsResourceRecord *dnskey,
2514 usec_t realtime,
2515 DnssecResult *result) {
2516
2517 return -EOPNOTSUPP;
2518}
2519
2520int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
2521
2522 return -EOPNOTSUPP;
2523}
2524
2525int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
2526
2527 return -EOPNOTSUPP;
2528}
2529
2530int dnssec_verify_rrset_search(
2531 DnsAnswer *a,
2532 const DnsResourceKey *key,
2533 DnsAnswer *validated_dnskeys,
2534 usec_t realtime,
2535 DnssecResult *result,
2536 DnsResourceRecord **ret_rrsig) {
2537
2538 return -EOPNOTSUPP;
2539}
2540
2541int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
2542
2543 return -EOPNOTSUPP;
2544}
2545
2546int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
2547
2548 return -EOPNOTSUPP;
2549}
2550
2551int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
2552
2553 return -EOPNOTSUPP;
2554}
2555
2556int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
2557
2558 return -EOPNOTSUPP;
2559}
2560
2561int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
2562
2563 return -EOPNOTSUPP;
2564}
2565
2566int dnssec_test_positive_wildcard(
2567 DnsAnswer *answer,
2568 const char *name,
2569 const char *source,
2570 const char *zone,
2571 bool *authenticated) {
2572
2573 return -EOPNOTSUPP;
2574}
2575
2576#endif
2577
547973de 2578static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
e3e64a1a
ZJS
2579 [DNSSEC_VALIDATED] = "validated",
2580 [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
2581 [DNSSEC_INVALID] = "invalid",
2582 [DNSSEC_SIGNATURE_EXPIRED] = "signature-expired",
203f1b35 2583 [DNSSEC_UNSUPPORTED_ALGORITHM] = "unsupported-algorithm",
e3e64a1a
ZJS
2584 [DNSSEC_NO_SIGNATURE] = "no-signature",
2585 [DNSSEC_MISSING_KEY] = "missing-key",
2586 [DNSSEC_UNSIGNED] = "unsigned",
2587 [DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
2588 [DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
2589 [DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
a72cf22d 2590 [DNSSEC_UPSTREAM_FAILURE] = "upstream-failure",
67d0ce88 2591 [DNSSEC_TOO_MANY_VALIDATIONS] = "too-many-validations",
547973de
LP
2592};
2593DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
59c5b597
LP
2594
2595static const char* const dnssec_verdict_table[_DNSSEC_VERDICT_MAX] = {
e3e64a1a
ZJS
2596 [DNSSEC_SECURE] = "secure",
2597 [DNSSEC_INSECURE] = "insecure",
2598 [DNSSEC_BOGUS] = "bogus",
59c5b597
LP
2599 [DNSSEC_INDETERMINATE] = "indeterminate",
2600};
2601DEFINE_STRING_TABLE_LOOKUP(dnssec_verdict, DnssecVerdict);