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