1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "alloc-util.h"
25 #include "dns-domain.h"
26 #include "hexdecoct.h"
27 #include "resolved-dns-dnssec.h"
28 #include "resolved-dns-packet.h"
29 #include "string-table.h"
33 * How does the DNSSEC canonical form of a hostname with a label
34 * containing a dot look like, the way DNS-SD does it?
38 * - Make trust anchor store read additional DS+DNSKEY data from disk
39 * - wildcard zones compatibility (NSEC/NSEC3 wildcard check is missing)
40 * - multi-label zone compatibility
41 * - cname/dname compatibility
42 * - per-interface DNSSEC setting
43 * - fix TTL for cache entries to match RRSIG TTL
44 * - retry on failed validation?
50 #define VERIFY_RRS_MAX 256
51 #define MAX_KEY_SIZE (32*1024)
53 /* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
54 #define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
57 * The DNSSEC Chain of trust:
59 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
60 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
61 * DS RRs are protected like normal RRs
64 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
67 static void initialize_libgcrypt(void) {
70 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P
))
73 p
= gcry_check_version("1.4.5");
76 gcry_control(GCRYCTL_DISABLE_SECMEM
);
77 gcry_control(GCRYCTL_INITIALIZATION_FINISHED
, 0);
80 static bool dnssec_algorithm_supported(int algorithm
) {
81 return IN_SET(algorithm
,
82 DNSSEC_ALGORITHM_RSASHA1
,
83 DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
,
84 DNSSEC_ALGORITHM_RSASHA256
,
85 DNSSEC_ALGORITHM_RSASHA512
);
88 uint16_t dnssec_keytag(DnsResourceRecord
*dnskey
) {
93 /* The algorithm from RFC 4034, Appendix B. */
96 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
98 sum
= (uint32_t) dnskey
->dnskey
.flags
+
99 ((((uint32_t) dnskey
->dnskey
.protocol
) << 8) + (uint32_t) dnskey
->dnskey
.algorithm
);
101 p
= dnskey
->dnskey
.key
;
103 for (i
= 0; i
< dnskey
->dnskey
.key_size
; i
++)
104 sum
+= (i
& 1) == 0 ? (uint32_t) p
[i
] << 8 : (uint32_t) p
[i
];
106 sum
+= (sum
>> 16) & UINT32_C(0xFFFF);
108 return sum
& UINT32_C(0xFFFF);
111 static int rr_compare(const void *a
, const void *b
) {
112 DnsResourceRecord
**x
= (DnsResourceRecord
**) a
, **y
= (DnsResourceRecord
**) b
;
116 /* Let's order the RRs according to RFC 4034, Section 6.3 */
120 assert((*x
)->wire_format
);
123 assert((*y
)->wire_format
);
125 m
= MIN((*x
)->wire_format_size
, (*y
)->wire_format_size
);
127 r
= memcmp((*x
)->wire_format
, (*y
)->wire_format
, m
);
131 if ((*x
)->wire_format_size
< (*y
)->wire_format_size
)
133 else if ((*x
)->wire_format_size
> (*y
)->wire_format_size
)
139 static int dnssec_rsa_verify(
140 const char *hash_algorithm
,
141 const void *signature
, size_t signature_size
,
142 const void *data
, size_t data_size
,
143 const void *exponent
, size_t exponent_size
,
144 const void *modulus
, size_t modulus_size
) {
146 gcry_sexp_t public_key_sexp
= NULL
, data_sexp
= NULL
, signature_sexp
= NULL
;
147 gcry_mpi_t n
= NULL
, e
= NULL
, s
= NULL
;
151 assert(hash_algorithm
);
153 ge
= gcry_mpi_scan(&s
, GCRYMPI_FMT_USG
, signature
, signature_size
, NULL
);
159 ge
= gcry_mpi_scan(&e
, GCRYMPI_FMT_USG
, exponent
, exponent_size
, NULL
);
165 ge
= gcry_mpi_scan(&n
, GCRYMPI_FMT_USG
, modulus
, modulus_size
, NULL
);
171 ge
= gcry_sexp_build(&signature_sexp
,
173 "(sig-val (rsa (s %m)))",
181 ge
= gcry_sexp_build(&data_sexp
,
183 "(data (flags pkcs1) (hash %s %b))",
192 ge
= gcry_sexp_build(&public_key_sexp
,
194 "(public-key (rsa (n %m) (e %m)))",
202 ge
= gcry_pk_verify(signature_sexp
, data_sexp
, public_key_sexp
);
203 if (gpg_err_code(ge
) == GPG_ERR_BAD_SIGNATURE
)
206 log_debug("RSA signature check failed: %s", gpg_strerror(ge
));
220 gcry_sexp_release(public_key_sexp
);
222 gcry_sexp_release(signature_sexp
);
224 gcry_sexp_release(data_sexp
);
229 static void md_add_uint8(gcry_md_hd_t md
, uint8_t v
) {
230 gcry_md_write(md
, &v
, sizeof(v
));
233 static void md_add_uint16(gcry_md_hd_t md
, uint16_t v
) {
235 gcry_md_write(md
, &v
, sizeof(v
));
238 static void md_add_uint32(gcry_md_hd_t md
, uint32_t v
) {
240 gcry_md_write(md
, &v
, sizeof(v
));
243 static int dnssec_rrsig_expired(DnsResourceRecord
*rrsig
, usec_t realtime
) {
244 usec_t expiration
, inception
, skew
;
247 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
249 if (realtime
== USEC_INFINITY
)
250 realtime
= now(CLOCK_REALTIME
);
252 expiration
= rrsig
->rrsig
.expiration
* USEC_PER_SEC
;
253 inception
= rrsig
->rrsig
.inception
* USEC_PER_SEC
;
255 if (inception
> expiration
)
256 return -EKEYREJECTED
;
258 /* Permit a certain amount of clock skew of 10% of the valid
259 * time range. This takes inspiration from unbound's
261 skew
= (expiration
- inception
) / 10;
265 if (inception
< skew
)
270 if (expiration
+ skew
< expiration
)
271 expiration
= USEC_INFINITY
;
275 return realtime
< inception
|| realtime
> expiration
;
278 int dnssec_verify_rrset(
281 DnsResourceRecord
*rrsig
,
282 DnsResourceRecord
*dnskey
,
284 DnssecResult
*result
) {
286 uint8_t wire_format_name
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
287 size_t exponent_size
, modulus_size
, hash_size
;
288 void *exponent
, *modulus
, *hash
;
289 DnsResourceRecord
**list
, *rr
;
290 gcry_md_hd_t md
= NULL
;
298 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
299 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
301 /* Verifies the the RRSet matching the specified "key" in "a",
302 * using the signature "rrsig" and the key "dnskey". It's
303 * assumed the RRSIG and DNSKEY match. */
305 if (!dnssec_algorithm_supported(rrsig
->rrsig
.algorithm
)) {
306 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
310 if (a
->n_rrs
> VERIFY_RRS_MAX
)
313 r
= dnssec_rrsig_expired(rrsig
, realtime
);
317 *result
= DNSSEC_SIGNATURE_EXPIRED
;
321 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
322 list
= newa(DnsResourceRecord
*, a
->n_rrs
);
324 DNS_ANSWER_FOREACH(rr
, a
) {
325 r
= dns_resource_key_equal(key
, rr
->key
);
331 /* We need the wire format for ordering, and digest calculation */
332 r
= dns_resource_record_to_wire_format(rr
, true);
342 /* Bring the RRs into canonical order */
343 qsort_safe(list
, n
, sizeof(DnsResourceRecord
*), rr_compare
);
345 initialize_libgcrypt();
347 /* OK, the RRs are now in canonical order. Let's calculate the digest */
348 switch (rrsig
->rrsig
.algorithm
) {
350 case DNSSEC_ALGORITHM_RSASHA1
:
351 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
:
352 gcry_md_open(&md
, GCRY_MD_SHA1
, 0);
356 case DNSSEC_ALGORITHM_RSASHA256
:
357 gcry_md_open(&md
, GCRY_MD_SHA256
, 0);
361 case DNSSEC_ALGORITHM_RSASHA512
:
362 gcry_md_open(&md
, GCRY_MD_SHA512
, 0);
367 assert_not_reached("Unknown digest");
373 md_add_uint16(md
, rrsig
->rrsig
.type_covered
);
374 md_add_uint8(md
, rrsig
->rrsig
.algorithm
);
375 md_add_uint8(md
, rrsig
->rrsig
.labels
);
376 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
377 md_add_uint32(md
, rrsig
->rrsig
.expiration
);
378 md_add_uint32(md
, rrsig
->rrsig
.inception
);
379 md_add_uint16(md
, rrsig
->rrsig
.key_tag
);
381 r
= dns_name_to_wire_format(rrsig
->rrsig
.signer
, wire_format_name
, sizeof(wire_format_name
), true);
384 gcry_md_write(md
, wire_format_name
, r
);
386 for (k
= 0; k
< n
; k
++) {
391 r
= dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr
->key
), rrsig
->rrsig
.labels
, &suffix
);
394 if (r
> 0) /* This is a wildcard! */
395 gcry_md_write(md
, (uint8_t[]) { 1, '*'}, 2);
397 r
= dns_name_to_wire_format(suffix
, wire_format_name
, sizeof(wire_format_name
), true);
400 gcry_md_write(md
, wire_format_name
, r
);
402 md_add_uint16(md
, rr
->key
->type
);
403 md_add_uint16(md
, rr
->key
->class);
404 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
406 assert(rr
->wire_format_rdata_offset
<= rr
->wire_format_size
);
407 l
= rr
->wire_format_size
- rr
->wire_format_rdata_offset
;
410 md_add_uint16(md
, (uint16_t) l
);
411 gcry_md_write(md
, (uint8_t*) rr
->wire_format
+ rr
->wire_format_rdata_offset
, l
);
414 hash
= gcry_md_read(md
, 0);
420 if (*(uint8_t*) dnskey
->dnskey
.key
== 0) {
421 /* exponent is > 255 bytes long */
423 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 3;
425 ((size_t) (((uint8_t*) dnskey
->dnskey
.key
)[0]) << 8) |
426 ((size_t) ((uint8_t*) dnskey
->dnskey
.key
)[1]);
428 if (exponent_size
< 256) {
433 if (3 + exponent_size
>= dnskey
->dnskey
.key_size
) {
438 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 3 + exponent_size
;
439 modulus_size
= dnskey
->dnskey
.key_size
- 3 - exponent_size
;
442 /* exponent is <= 255 bytes long */
444 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 1;
445 exponent_size
= (size_t) ((uint8_t*) dnskey
->dnskey
.key
)[0];
447 if (exponent_size
<= 0) {
452 if (1 + exponent_size
>= dnskey
->dnskey
.key_size
) {
457 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 1 + exponent_size
;
458 modulus_size
= dnskey
->dnskey
.key_size
- 1 - exponent_size
;
461 r
= dnssec_rsa_verify(
462 gcry_md_algo_name(gcry_md_get_algo(md
)),
463 rrsig
->rrsig
.signature
, rrsig
->rrsig
.signature_size
,
465 exponent
, exponent_size
,
466 modulus
, modulus_size
);
470 *result
= r
? DNSSEC_VALIDATED
: DNSSEC_INVALID
;
478 int dnssec_rrsig_match_dnskey(DnsResourceRecord
*rrsig
, DnsResourceRecord
*dnskey
) {
483 /* Checks if the specified DNSKEY RR matches the key used for
484 * the signature in the specified RRSIG RR */
486 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
489 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
491 if (dnskey
->key
->class != rrsig
->key
->class)
493 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
495 if (dnskey
->dnskey
.protocol
!= 3)
497 if (dnskey
->dnskey
.algorithm
!= rrsig
->rrsig
.algorithm
)
500 if (dnssec_keytag(dnskey
) != rrsig
->rrsig
.key_tag
)
503 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), rrsig
->rrsig
.signer
);
506 int dnssec_key_match_rrsig(const DnsResourceKey
*key
, DnsResourceRecord
*rrsig
) {
512 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
514 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
516 if (rrsig
->key
->class != key
->class)
518 if (rrsig
->rrsig
.type_covered
!= key
->type
)
521 /* Make sure signer is a parent of the RRset */
522 r
= dns_name_endswith(DNS_RESOURCE_KEY_NAME(rrsig
->key
), rrsig
->rrsig
.signer
);
526 /* Make sure the owner name has at least as many labels as the "label" fields indicates. */
527 r
= dns_name_count_labels(DNS_RESOURCE_KEY_NAME(rrsig
->key
));
530 if (r
< rrsig
->rrsig
.labels
)
533 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig
->key
), DNS_RESOURCE_KEY_NAME(key
));
536 int dnssec_verify_rrset_search(
539 DnsAnswer
*validated_dnskeys
,
541 DnssecResult
*result
) {
543 bool found_rrsig
= false, found_invalid
= false, found_expired_rrsig
= false, found_unsupported_algorithm
= false;
544 DnsResourceRecord
*rrsig
;
550 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
552 if (!a
|| a
->n_rrs
<= 0)
555 /* Iterate through each RRSIG RR. */
556 DNS_ANSWER_FOREACH(rrsig
, a
) {
557 DnsResourceRecord
*dnskey
;
558 DnsAnswerFlags flags
;
560 /* Is this an RRSIG RR that applies to RRs matching our key? */
561 r
= dnssec_key_match_rrsig(key
, rrsig
);
569 /* Look for a matching key */
570 DNS_ANSWER_FOREACH_FLAGS(dnskey
, flags
, validated_dnskeys
) {
571 DnssecResult one_result
;
573 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
576 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
577 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
);
583 /* Take the time here, if it isn't set yet, so
584 * that we do all validations with the same
586 if (realtime
== USEC_INFINITY
)
587 realtime
= now(CLOCK_REALTIME
);
589 /* Yay, we found a matching RRSIG with a matching
590 * DNSKEY, awesome. Now let's verify all entries of
591 * the RRSet against the RRSIG and DNSKEY
594 r
= dnssec_verify_rrset(a
, key
, rrsig
, dnskey
, realtime
, &one_result
);
598 switch (one_result
) {
600 case DNSSEC_VALIDATED
:
601 /* Yay, the RR has been validated,
602 * return immediately. */
603 *result
= DNSSEC_VALIDATED
;
607 /* If the signature is invalid, let's try another
608 key and/or signature. After all they
609 key_tags and stuff are not unique, and
610 might be shared by multiple keys. */
611 found_invalid
= true;
614 case DNSSEC_UNSUPPORTED_ALGORITHM
:
615 /* If the key algorithm is
616 unsupported, try another
617 RRSIG/DNSKEY pair, but remember we
618 encountered this, so that we can
619 return a proper error when we
620 encounter nothing better. */
621 found_unsupported_algorithm
= true;
624 case DNSSEC_SIGNATURE_EXPIRED
:
625 /* If the signature is expired, try
626 another one, but remember it, so
627 that we can return this */
628 found_expired_rrsig
= true;
632 assert_not_reached("Unexpected DNSSEC validation result");
637 if (found_expired_rrsig
)
638 *result
= DNSSEC_SIGNATURE_EXPIRED
;
639 else if (found_unsupported_algorithm
)
640 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
641 else if (found_invalid
)
642 *result
= DNSSEC_INVALID
;
643 else if (found_rrsig
)
644 *result
= DNSSEC_MISSING_KEY
;
646 *result
= DNSSEC_NO_SIGNATURE
;
651 int dnssec_has_rrsig(DnsAnswer
*a
, const DnsResourceKey
*key
) {
652 DnsResourceRecord
*rr
;
655 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
657 DNS_ANSWER_FOREACH(rr
, a
) {
658 r
= dnssec_key_match_rrsig(key
, rr
);
668 int dnssec_canonicalize(const char *n
, char *buffer
, size_t buffer_max
) {
672 /* Converts the specified hostname into DNSSEC canonicalized
681 r
= dns_label_unescape(&n
, buffer
, buffer_max
);
689 /* DNSSEC validation is always done on the ASCII version of the label */
690 k
= dns_label_apply_idna(buffer
, r
, buffer
, buffer_max
);
697 if (buffer_max
< (size_t) r
+ 2)
700 /* The DNSSEC canonical form is not clear on what to
701 * do with dots appearing in labels, the way DNS-SD
702 * does it. Refuse it for now. */
704 if (memchr(buffer
, '.', r
))
707 for (i
= 0; i
< (size_t) r
; i
++) {
708 if (buffer
[i
] >= 'A' && buffer
[i
] <= 'Z')
709 buffer
[i
] = buffer
[i
] - 'A' + 'a';
721 /* Not even a single label: this is the root domain name */
723 assert(buffer_max
> 2);
733 static int digest_to_gcrypt(uint8_t algorithm
) {
735 /* Translates a DNSSEC digest algorithm into a gcrypt digest iedntifier */
739 case DNSSEC_DIGEST_SHA1
:
742 case DNSSEC_DIGEST_SHA256
:
743 return GCRY_MD_SHA256
;
750 int dnssec_verify_dnskey(DnsResourceRecord
*dnskey
, DnsResourceRecord
*ds
) {
751 char owner_name
[DNSSEC_CANONICAL_HOSTNAME_MAX
];
752 gcry_md_hd_t md
= NULL
;
761 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
763 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
765 if (ds
->key
->type
!= DNS_TYPE_DS
)
767 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
768 return -EKEYREJECTED
;
769 if (dnskey
->dnskey
.protocol
!= 3)
770 return -EKEYREJECTED
;
772 if (dnskey
->dnskey
.algorithm
!= ds
->ds
.algorithm
)
774 if (dnssec_keytag(dnskey
) != ds
->ds
.key_tag
)
777 initialize_libgcrypt();
779 algorithm
= digest_to_gcrypt(ds
->ds
.digest_type
);
783 hash_size
= gcry_md_get_algo_dlen(algorithm
);
784 assert(hash_size
> 0);
786 if (ds
->ds
.digest_size
!= hash_size
)
789 r
= dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey
->key
), owner_name
, sizeof(owner_name
));
793 gcry_md_open(&md
, algorithm
, 0);
797 gcry_md_write(md
, owner_name
, r
);
798 md_add_uint16(md
, dnskey
->dnskey
.flags
);
799 md_add_uint8(md
, dnskey
->dnskey
.protocol
);
800 md_add_uint8(md
, dnskey
->dnskey
.algorithm
);
801 gcry_md_write(md
, dnskey
->dnskey
.key
, dnskey
->dnskey
.key_size
);
803 result
= gcry_md_read(md
, 0);
809 r
= memcmp(result
, ds
->ds
.digest
, ds
->ds
.digest_size
) != 0;
816 int dnssec_verify_dnskey_search(DnsResourceRecord
*dnskey
, DnsAnswer
*validated_ds
) {
817 DnsResourceRecord
*ds
;
818 DnsAnswerFlags flags
;
823 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
826 DNS_ANSWER_FOREACH_FLAGS(ds
, flags
, validated_ds
) {
828 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
831 if (ds
->key
->type
!= DNS_TYPE_DS
)
834 if (ds
->key
->class != dnskey
->key
->class)
837 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), DNS_RESOURCE_KEY_NAME(ds
->key
));
843 r
= dnssec_verify_dnskey(dnskey
, ds
);
853 int dnssec_nsec3_hash(DnsResourceRecord
*nsec3
, const char *name
, void *ret
) {
854 uint8_t wire_format
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
855 gcry_md_hd_t md
= NULL
;
866 if (nsec3
->key
->type
!= DNS_TYPE_NSEC3
)
869 algorithm
= digest_to_gcrypt(nsec3
->nsec3
.algorithm
);
873 initialize_libgcrypt();
875 hash_size
= gcry_md_get_algo_dlen(algorithm
);
876 assert(hash_size
> 0);
878 if (nsec3
->nsec3
.next_hashed_name_size
!= hash_size
)
881 r
= dns_name_to_wire_format(name
, wire_format
, sizeof(wire_format
), true);
885 gcry_md_open(&md
, algorithm
, 0);
889 gcry_md_write(md
, wire_format
, r
);
890 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
892 result
= gcry_md_read(md
, 0);
898 for (k
= 0; k
< nsec3
->nsec3
.iterations
; k
++) {
899 uint8_t tmp
[hash_size
];
900 memcpy(tmp
, result
, hash_size
);
903 gcry_md_write(md
, tmp
, hash_size
);
904 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
906 result
= gcry_md_read(md
, 0);
913 memcpy(ret
, result
, hash_size
);
921 static int nsec3_is_good(DnsResourceRecord
*rr
, DnsAnswerFlags flags
, DnsResourceRecord
*nsec3
) {
927 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
930 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
933 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
934 if (!IN_SET(rr
->nsec3
.flags
, 0, 1))
940 /* If a second NSEC3 RR is specified, also check if they are from the same zone. */
942 if (nsec3
== rr
) /* Shortcut */
945 if (rr
->key
->class != nsec3
->key
->class)
947 if (rr
->nsec3
.algorithm
!= nsec3
->nsec3
.algorithm
)
949 if (rr
->nsec3
.iterations
!= nsec3
->nsec3
.iterations
)
951 if (rr
->nsec3
.salt_size
!= nsec3
->nsec3
.salt_size
)
953 if (memcmp(rr
->nsec3
.salt
, nsec3
->nsec3
.salt
, rr
->nsec3
.salt_size
) != 0)
956 a
= DNS_RESOURCE_KEY_NAME(rr
->key
);
957 r
= dns_name_parent(&a
); /* strip off hash */
963 b
= DNS_RESOURCE_KEY_NAME(nsec3
->key
);
964 r
= dns_name_parent(&b
); /* strip off hash */
970 return dns_name_equal(a
, b
);
973 static int dnssec_test_nsec3(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
) {
974 _cleanup_free_
char *next_closer_domain
= NULL
, *l
= NULL
;
975 uint8_t hashed
[DNSSEC_HASH_SIZE_MAX
];
976 const char *suffix
, *p
, *pp
= NULL
;
977 DnsResourceRecord
*rr
, *suffix_rr
;
978 DnsAnswerFlags flags
;
984 /* First step, look for the longest common suffix we find with any NSEC3 RR in the response. */
985 suffix
= DNS_RESOURCE_KEY_NAME(key
);
987 DNS_ANSWER_FOREACH_FLAGS(suffix_rr
, flags
, answer
) {
988 _cleanup_free_
char *hashed_domain
= NULL
, *label
= NULL
;
990 r
= nsec3_is_good(suffix_rr
, flags
, NULL
);
996 r
= dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(suffix_rr
->key
), 1, suffix
);
1003 /* Strip one label from the front */
1004 r
= dns_name_parent(&suffix
);
1011 *result
= DNSSEC_NSEC_NO_RR
;
1015 /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */
1016 p
= DNS_RESOURCE_KEY_NAME(key
);
1018 _cleanup_free_
char *hashed_domain
= NULL
, *label
= NULL
;
1020 hashed_size
= dnssec_nsec3_hash(suffix_rr
, p
, hashed
);
1021 if (hashed_size
== -EOPNOTSUPP
) {
1022 *result
= DNSSEC_NSEC_UNSUPPORTED_ALGORITHM
;
1025 if (hashed_size
< 0)
1028 label
= base32hexmem(hashed
, hashed_size
, false);
1032 hashed_domain
= strjoin(label
, ".", suffix
, NULL
);
1036 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1038 r
= nsec3_is_good(rr
, flags
, suffix_rr
);
1044 if (rr
->nsec3
.next_hashed_name_size
!= (size_t) hashed_size
)
1047 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), hashed_domain
);
1051 goto found_closest_encloser
;
1054 /* We didn't find the closest encloser with this name,
1055 * but let's remember this domain name, it might be
1056 * the next closer name */
1060 /* Strip one label from the front */
1061 r
= dns_name_parent(&p
);
1068 *result
= DNSSEC_NSEC_NO_RR
;
1071 found_closest_encloser
:
1072 /* We found a closest encloser in 'p'; next closer is 'pp' */
1074 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
1075 if (bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_DNAME
))
1078 /* Ensure that this data is from the delegated domain
1079 * (i.e. originates from the "lower" DNS server), and isn't
1080 * just glue records (i.e. doesn't originate from the "upper"
1082 if (bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_NS
) &&
1083 !bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_SOA
))
1087 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
1088 *result
= bitmap_isset(rr
->nsec3
.types
, key
->type
) ? DNSSEC_NSEC_FOUND
: DNSSEC_NSEC_NODATA
;
1092 r
= dnssec_nsec3_hash(rr
, pp
, hashed
);
1095 if (r
!= hashed_size
)
1098 l
= base32hexmem(hashed
, hashed_size
, false);
1102 next_closer_domain
= strjoin(l
, ".", p
, NULL
);
1103 if (!next_closer_domain
)
1106 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1107 _cleanup_free_
char *label
= NULL
, *next_hashed_domain
= NULL
;
1109 r
= nsec3_is_good(rr
, flags
, suffix_rr
);
1115 label
= base32hexmem(rr
->nsec3
.next_hashed_name
, rr
->nsec3
.next_hashed_name_size
, false);
1119 next_hashed_domain
= strjoin(label
, ".", p
, NULL
);
1120 if (!next_hashed_domain
)
1123 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), next_closer_domain
, next_hashed_domain
);
1127 if (rr
->nsec3
.flags
& 1)
1128 *result
= DNSSEC_NSEC_OPTOUT
;
1130 *result
= DNSSEC_NSEC_NXDOMAIN
;
1136 *result
= DNSSEC_NSEC_NO_RR
;
1140 int dnssec_test_nsec(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
) {
1141 DnsResourceRecord
*rr
;
1142 bool have_nsec3
= false;
1143 DnsAnswerFlags flags
;
1149 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1151 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1153 if (rr
->key
->class != key
->class)
1156 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
1159 switch (rr
->key
->type
) {
1163 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
));
1167 *result
= bitmap_isset(rr
->nsec
.types
, key
->type
) ? DNSSEC_NSEC_FOUND
: DNSSEC_NSEC_NODATA
;
1171 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
), rr
->nsec
.next_domain_name
);
1175 *result
= DNSSEC_NSEC_NXDOMAIN
;
1180 case DNS_TYPE_NSEC3
:
1186 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1188 return dnssec_test_nsec3(answer
, key
, result
);
1190 /* No approproate NSEC RR found, report this. */
1191 *result
= DNSSEC_NSEC_NO_RR
;
1195 static const char* const dnssec_mode_table
[_DNSSEC_MODE_MAX
] = {
1197 [DNSSEC_YES
] = "yes",
1199 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode
, DnssecMode
);
1201 static const char* const dnssec_result_table
[_DNSSEC_RESULT_MAX
] = {
1202 [DNSSEC_VALIDATED
] = "validated",
1203 [DNSSEC_INVALID
] = "invalid",
1204 [DNSSEC_SIGNATURE_EXPIRED
] = "signature-expired",
1205 [DNSSEC_UNSUPPORTED_ALGORITHM
] = "unsupported-algorithm",
1206 [DNSSEC_NO_SIGNATURE
] = "no-signature",
1207 [DNSSEC_MISSING_KEY
] = "missing-key",
1208 [DNSSEC_UNSIGNED
] = "unsigned",
1209 [DNSSEC_FAILED_AUXILIARY
] = "failed-auxiliary",
1210 [DNSSEC_NSEC_MISMATCH
] = "nsec-mismatch",
1212 DEFINE_STRING_TABLE_LOOKUP(dnssec_result
, DnssecResult
);