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