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 * - Iterative validation
39 * - NSEC proof of non-existance
40 * - NSEC3 proof of non-existance
41 * - Make trust anchor store read additional DS+DNSKEY data from disk
42 * - wildcard zones compatibility
43 * - multi-label zone compatibility
44 * - DNSSEC cname/dname compatibility
45 * - per-interface DNSSEC setting
46 * - retry on failed validation
47 * - fix TTL for cache entries to match RRSIG TTL
53 #define VERIFY_RRS_MAX 256
54 #define MAX_KEY_SIZE (32*1024)
56 /* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
57 #define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
60 * The DNSSEC Chain of trust:
62 * Normal RRs are protected via RRSIG RRs in combination with DNSKEY RRs, all in the same zone
63 * DNSKEY RRs are either protected like normal RRs, or via a DS from a zone "higher" up the tree
64 * DS RRs are protected like normal RRs
67 * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
70 static void initialize_libgcrypt(void) {
73 if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P
))
76 p
= gcry_check_version("1.4.5");
79 gcry_control(GCRYCTL_DISABLE_SECMEM
);
80 gcry_control(GCRYCTL_INITIALIZATION_FINISHED
, 0);
83 static bool dnssec_algorithm_supported(int algorithm
) {
84 return IN_SET(algorithm
,
85 DNSSEC_ALGORITHM_RSASHA1
,
86 DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
,
87 DNSSEC_ALGORITHM_RSASHA256
,
88 DNSSEC_ALGORITHM_RSASHA512
);
91 uint16_t dnssec_keytag(DnsResourceRecord
*dnskey
) {
96 /* The algorithm from RFC 4034, Appendix B. */
99 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
101 sum
= (uint32_t) dnskey
->dnskey
.flags
+
102 ((((uint32_t) dnskey
->dnskey
.protocol
) << 8) + (uint32_t) dnskey
->dnskey
.algorithm
);
104 p
= dnskey
->dnskey
.key
;
106 for (i
= 0; i
< dnskey
->dnskey
.key_size
; i
++)
107 sum
+= (i
& 1) == 0 ? (uint32_t) p
[i
] << 8 : (uint32_t) p
[i
];
109 sum
+= (sum
>> 16) & UINT32_C(0xFFFF);
111 return sum
& UINT32_C(0xFFFF);
114 static int rr_compare(const void *a
, const void *b
) {
115 DnsResourceRecord
**x
= (DnsResourceRecord
**) a
, **y
= (DnsResourceRecord
**) b
;
119 /* Let's order the RRs according to RFC 4034, Section 6.3 */
123 assert((*x
)->wire_format
);
126 assert((*y
)->wire_format
);
128 m
= MIN((*x
)->wire_format_size
, (*y
)->wire_format_size
);
130 r
= memcmp((*x
)->wire_format
, (*y
)->wire_format
, m
);
134 if ((*x
)->wire_format_size
< (*y
)->wire_format_size
)
136 else if ((*x
)->wire_format_size
> (*y
)->wire_format_size
)
142 static int dnssec_rsa_verify(
143 const char *hash_algorithm
,
144 const void *signature
, size_t signature_size
,
145 const void *data
, size_t data_size
,
146 const void *exponent
, size_t exponent_size
,
147 const void *modulus
, size_t modulus_size
) {
149 gcry_sexp_t public_key_sexp
= NULL
, data_sexp
= NULL
, signature_sexp
= NULL
;
150 gcry_mpi_t n
= NULL
, e
= NULL
, s
= NULL
;
154 assert(hash_algorithm
);
156 ge
= gcry_mpi_scan(&s
, GCRYMPI_FMT_USG
, signature
, signature_size
, NULL
);
162 ge
= gcry_mpi_scan(&e
, GCRYMPI_FMT_USG
, exponent
, exponent_size
, NULL
);
168 ge
= gcry_mpi_scan(&n
, GCRYMPI_FMT_USG
, modulus
, modulus_size
, NULL
);
174 ge
= gcry_sexp_build(&signature_sexp
,
176 "(sig-val (rsa (s %m)))",
184 ge
= gcry_sexp_build(&data_sexp
,
186 "(data (flags pkcs1) (hash %s %b))",
195 ge
= gcry_sexp_build(&public_key_sexp
,
197 "(public-key (rsa (n %m) (e %m)))",
205 ge
= gcry_pk_verify(signature_sexp
, data_sexp
, public_key_sexp
);
206 if (gpg_err_code(ge
) == GPG_ERR_BAD_SIGNATURE
)
209 log_debug("RSA signature check failed: %s", gpg_strerror(ge
));
223 gcry_sexp_release(public_key_sexp
);
225 gcry_sexp_release(signature_sexp
);
227 gcry_sexp_release(data_sexp
);
232 static void md_add_uint8(gcry_md_hd_t md
, uint8_t v
) {
233 gcry_md_write(md
, &v
, sizeof(v
));
236 static void md_add_uint16(gcry_md_hd_t md
, uint16_t v
) {
238 gcry_md_write(md
, &v
, sizeof(v
));
241 static void md_add_uint32(gcry_md_hd_t md
, uint32_t v
) {
243 gcry_md_write(md
, &v
, sizeof(v
));
246 static int dnssec_rrsig_expired(DnsResourceRecord
*rrsig
, usec_t realtime
) {
247 usec_t expiration
, inception
, skew
;
250 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
252 if (realtime
== USEC_INFINITY
)
253 realtime
= now(CLOCK_REALTIME
);
255 expiration
= rrsig
->rrsig
.expiration
* USEC_PER_SEC
;
256 inception
= rrsig
->rrsig
.inception
* USEC_PER_SEC
;
258 if (inception
> expiration
)
259 return -EKEYREJECTED
;
261 /* Permit a certain amount of clock skew of 10% of the valid
262 * time range. This takes inspiration from unbound's
264 skew
= (expiration
- inception
) / 10;
268 if (inception
< skew
)
273 if (expiration
+ skew
< expiration
)
274 expiration
= USEC_INFINITY
;
278 return realtime
< inception
|| realtime
> expiration
;
281 int dnssec_verify_rrset(
284 DnsResourceRecord
*rrsig
,
285 DnsResourceRecord
*dnskey
,
287 DnssecResult
*result
) {
289 uint8_t wire_format_name
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
290 size_t exponent_size
, modulus_size
, hash_size
;
291 void *exponent
, *modulus
, *hash
;
292 DnsResourceRecord
**list
, *rr
;
293 gcry_md_hd_t md
= NULL
;
301 assert(rrsig
->key
->type
== DNS_TYPE_RRSIG
);
302 assert(dnskey
->key
->type
== DNS_TYPE_DNSKEY
);
304 /* Verifies the the RRSet matching the specified "key" in "a",
305 * using the signature "rrsig" and the key "dnskey". It's
306 * assumed the RRSIG and DNSKEY match. */
308 if (!dnssec_algorithm_supported(rrsig
->rrsig
.algorithm
)) {
309 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
313 if (a
->n_rrs
> VERIFY_RRS_MAX
)
316 r
= dnssec_rrsig_expired(rrsig
, realtime
);
320 *result
= DNSSEC_SIGNATURE_EXPIRED
;
324 /* Collect all relevant RRs in a single array, so that we can look at the RRset */
325 list
= newa(DnsResourceRecord
*, a
->n_rrs
);
327 DNS_ANSWER_FOREACH(rr
, a
) {
328 r
= dns_resource_key_equal(key
, rr
->key
);
334 /* We need the wire format for ordering, and digest calculation */
335 r
= dns_resource_record_to_wire_format(rr
, true);
345 /* Bring the RRs into canonical order */
346 qsort_safe(list
, n
, sizeof(DnsResourceRecord
*), rr_compare
);
348 initialize_libgcrypt();
350 /* OK, the RRs are now in canonical order. Let's calculate the digest */
351 switch (rrsig
->rrsig
.algorithm
) {
353 case DNSSEC_ALGORITHM_RSASHA1
:
354 case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1
:
355 gcry_md_open(&md
, GCRY_MD_SHA1
, 0);
359 case DNSSEC_ALGORITHM_RSASHA256
:
360 gcry_md_open(&md
, GCRY_MD_SHA256
, 0);
364 case DNSSEC_ALGORITHM_RSASHA512
:
365 gcry_md_open(&md
, GCRY_MD_SHA512
, 0);
370 assert_not_reached("Unknown digest");
376 md_add_uint16(md
, rrsig
->rrsig
.type_covered
);
377 md_add_uint8(md
, rrsig
->rrsig
.algorithm
);
378 md_add_uint8(md
, rrsig
->rrsig
.labels
);
379 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
380 md_add_uint32(md
, rrsig
->rrsig
.expiration
);
381 md_add_uint32(md
, rrsig
->rrsig
.inception
);
382 md_add_uint16(md
, rrsig
->rrsig
.key_tag
);
384 r
= dns_name_to_wire_format(rrsig
->rrsig
.signer
, wire_format_name
, sizeof(wire_format_name
), true);
387 gcry_md_write(md
, wire_format_name
, r
);
389 for (k
= 0; k
< n
; k
++) {
393 r
= dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr
->key
), wire_format_name
, sizeof(wire_format_name
), true);
396 gcry_md_write(md
, wire_format_name
, r
);
398 md_add_uint16(md
, rr
->key
->type
);
399 md_add_uint16(md
, rr
->key
->class);
400 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
402 assert(rr
->wire_format_rdata_offset
<= rr
->wire_format_size
);
403 l
= rr
->wire_format_size
- rr
->wire_format_rdata_offset
;
406 md_add_uint16(md
, (uint16_t) l
);
407 gcry_md_write(md
, (uint8_t*) rr
->wire_format
+ rr
->wire_format_rdata_offset
, l
);
410 hash
= gcry_md_read(md
, 0);
416 if (*(uint8_t*) dnskey
->dnskey
.key
== 0) {
417 /* exponent is > 255 bytes long */
419 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 3;
421 ((size_t) (((uint8_t*) dnskey
->dnskey
.key
)[0]) << 8) |
422 ((size_t) ((uint8_t*) dnskey
->dnskey
.key
)[1]);
424 if (exponent_size
< 256) {
429 if (3 + exponent_size
>= dnskey
->dnskey
.key_size
) {
434 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 3 + exponent_size
;
435 modulus_size
= dnskey
->dnskey
.key_size
- 3 - exponent_size
;
438 /* exponent is <= 255 bytes long */
440 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 1;
441 exponent_size
= (size_t) ((uint8_t*) dnskey
->dnskey
.key
)[0];
443 if (exponent_size
<= 0) {
448 if (1 + exponent_size
>= dnskey
->dnskey
.key_size
) {
453 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 1 + exponent_size
;
454 modulus_size
= dnskey
->dnskey
.key_size
- 1 - exponent_size
;
457 r
= dnssec_rsa_verify(
458 gcry_md_algo_name(gcry_md_get_algo(md
)),
459 rrsig
->rrsig
.signature
, rrsig
->rrsig
.signature_size
,
461 exponent
, exponent_size
,
462 modulus
, modulus_size
);
466 *result
= r
? DNSSEC_VALIDATED
: DNSSEC_INVALID
;
474 int dnssec_rrsig_match_dnskey(DnsResourceRecord
*rrsig
, DnsResourceRecord
*dnskey
) {
479 /* Checks if the specified DNSKEY RR matches the key used for
480 * the signature in the specified RRSIG RR */
482 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
485 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
487 if (dnskey
->key
->class != rrsig
->key
->class)
489 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
491 if (dnskey
->dnskey
.protocol
!= 3)
493 if (dnskey
->dnskey
.algorithm
!= rrsig
->rrsig
.algorithm
)
496 if (dnssec_keytag(dnskey
) != rrsig
->rrsig
.key_tag
)
499 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), rrsig
->rrsig
.signer
);
502 int dnssec_key_match_rrsig(const DnsResourceKey
*key
, DnsResourceRecord
*rrsig
) {
506 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
508 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
510 if (rrsig
->key
->class != key
->class)
512 if (rrsig
->rrsig
.type_covered
!= key
->type
)
515 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig
->key
), DNS_RESOURCE_KEY_NAME(key
));
518 int dnssec_verify_rrset_search(
521 DnsAnswer
*validated_dnskeys
,
523 DnssecResult
*result
) {
525 bool found_rrsig
= false, found_invalid
= false, found_expired_rrsig
= false, found_unsupported_algorithm
= false;
526 DnsResourceRecord
*rrsig
;
532 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
534 if (!a
|| a
->n_rrs
<= 0)
537 /* Iterate through each RRSIG RR. */
538 DNS_ANSWER_FOREACH(rrsig
, a
) {
539 DnsResourceRecord
*dnskey
;
540 DnsAnswerFlags flags
;
542 /* Is this an RRSIG RR that applies to RRs matching our key? */
543 r
= dnssec_key_match_rrsig(key
, rrsig
);
551 /* Look for a matching key */
552 DNS_ANSWER_FOREACH_FLAGS(dnskey
, flags
, validated_dnskeys
) {
553 DnssecResult one_result
;
555 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
558 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
559 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
);
565 /* Take the time here, if it isn't set yet, so
566 * that we do all validations with the same
568 if (realtime
== USEC_INFINITY
)
569 realtime
= now(CLOCK_REALTIME
);
571 /* Yay, we found a matching RRSIG with a matching
572 * DNSKEY, awesome. Now let's verify all entries of
573 * the RRSet against the RRSIG and DNSKEY
576 r
= dnssec_verify_rrset(a
, key
, rrsig
, dnskey
, realtime
, &one_result
);
580 switch (one_result
) {
582 case DNSSEC_VALIDATED
:
583 /* Yay, the RR has been validated,
584 * return immediately. */
585 *result
= DNSSEC_VALIDATED
;
589 /* If the signature is invalid, let's try another
590 key and/or signature. After all they
591 key_tags and stuff are not unique, and
592 might be shared by multiple keys. */
593 found_invalid
= true;
596 case DNSSEC_UNSUPPORTED_ALGORITHM
:
597 /* If the key algorithm is
598 unsupported, try another
599 RRSIG/DNSKEY pair, but remember we
600 encountered this, so that we can
601 return a proper error when we
602 encounter nothing better. */
603 found_unsupported_algorithm
= true;
606 case DNSSEC_SIGNATURE_EXPIRED
:
607 /* If the signature is expired, try
608 another one, but remember it, so
609 that we can return this */
610 found_expired_rrsig
= true;
614 assert_not_reached("Unexpected DNSSEC validation result");
619 if (found_expired_rrsig
)
620 *result
= DNSSEC_SIGNATURE_EXPIRED
;
621 else if (found_unsupported_algorithm
)
622 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
623 else if (found_invalid
)
624 *result
= DNSSEC_INVALID
;
625 else if (found_rrsig
)
626 *result
= DNSSEC_MISSING_KEY
;
628 *result
= DNSSEC_NO_SIGNATURE
;
633 int dnssec_has_rrsig(DnsAnswer
*a
, const DnsResourceKey
*key
) {
634 DnsResourceRecord
*rr
;
637 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
639 DNS_ANSWER_FOREACH(rr
, a
) {
640 r
= dnssec_key_match_rrsig(key
, rr
);
650 int dnssec_canonicalize(const char *n
, char *buffer
, size_t buffer_max
) {
654 /* Converts the specified hostname into DNSSEC canonicalized
663 r
= dns_label_unescape(&n
, buffer
, buffer_max
);
671 /* DNSSEC validation is always done on the ASCII version of the label */
672 k
= dns_label_apply_idna(buffer
, r
, buffer
, buffer_max
);
679 if (buffer_max
< (size_t) r
+ 2)
682 /* The DNSSEC canonical form is not clear on what to
683 * do with dots appearing in labels, the way DNS-SD
684 * does it. Refuse it for now. */
686 if (memchr(buffer
, '.', r
))
689 for (i
= 0; i
< (size_t) r
; i
++) {
690 if (buffer
[i
] >= 'A' && buffer
[i
] <= 'Z')
691 buffer
[i
] = buffer
[i
] - 'A' + 'a';
703 /* Not even a single label: this is the root domain name */
705 assert(buffer_max
> 2);
715 static int digest_to_gcrypt(uint8_t algorithm
) {
717 /* Translates a DNSSEC digest algorithm into a gcrypt digest iedntifier */
721 case DNSSEC_DIGEST_SHA1
:
724 case DNSSEC_DIGEST_SHA256
:
725 return GCRY_MD_SHA256
;
732 int dnssec_verify_dnskey(DnsResourceRecord
*dnskey
, DnsResourceRecord
*ds
) {
733 char owner_name
[DNSSEC_CANONICAL_HOSTNAME_MAX
];
734 gcry_md_hd_t md
= NULL
;
743 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
745 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
747 if (ds
->key
->type
!= DNS_TYPE_DS
)
749 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
750 return -EKEYREJECTED
;
751 if (dnskey
->dnskey
.protocol
!= 3)
752 return -EKEYREJECTED
;
754 if (dnskey
->dnskey
.algorithm
!= ds
->ds
.algorithm
)
756 if (dnssec_keytag(dnskey
) != ds
->ds
.key_tag
)
759 initialize_libgcrypt();
761 algorithm
= digest_to_gcrypt(ds
->ds
.digest_type
);
765 hash_size
= gcry_md_get_algo_dlen(algorithm
);
766 assert(hash_size
> 0);
768 if (ds
->ds
.digest_size
!= hash_size
)
771 r
= dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey
->key
), owner_name
, sizeof(owner_name
));
775 gcry_md_open(&md
, algorithm
, 0);
779 gcry_md_write(md
, owner_name
, r
);
780 md_add_uint16(md
, dnskey
->dnskey
.flags
);
781 md_add_uint8(md
, dnskey
->dnskey
.protocol
);
782 md_add_uint8(md
, dnskey
->dnskey
.algorithm
);
783 gcry_md_write(md
, dnskey
->dnskey
.key
, dnskey
->dnskey
.key_size
);
785 result
= gcry_md_read(md
, 0);
791 r
= memcmp(result
, ds
->ds
.digest
, ds
->ds
.digest_size
) != 0;
798 int dnssec_verify_dnskey_search(DnsResourceRecord
*dnskey
, DnsAnswer
*validated_ds
) {
799 DnsResourceRecord
*ds
;
800 DnsAnswerFlags flags
;
805 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
808 DNS_ANSWER_FOREACH_FLAGS(ds
, flags
, validated_ds
) {
810 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
813 if (ds
->key
->type
!= DNS_TYPE_DS
)
816 r
= dnssec_verify_dnskey(dnskey
, ds
);
826 int dnssec_nsec3_hash(DnsResourceRecord
*nsec3
, const char *name
, void *ret
) {
827 uint8_t wire_format
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
828 gcry_md_hd_t md
= NULL
;
839 if (nsec3
->key
->type
!= DNS_TYPE_NSEC3
)
842 algorithm
= digest_to_gcrypt(nsec3
->nsec3
.algorithm
);
846 initialize_libgcrypt();
848 hash_size
= gcry_md_get_algo_dlen(algorithm
);
849 assert(hash_size
> 0);
851 if (nsec3
->nsec3
.next_hashed_name_size
!= hash_size
)
854 r
= dns_name_to_wire_format(name
, wire_format
, sizeof(wire_format
), true);
858 gcry_md_open(&md
, algorithm
, 0);
862 gcry_md_write(md
, wire_format
, r
);
863 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
865 result
= gcry_md_read(md
, 0);
871 for (k
= 0; k
< nsec3
->nsec3
.iterations
; k
++) {
872 uint8_t tmp
[hash_size
];
873 memcpy(tmp
, result
, hash_size
);
876 gcry_md_write(md
, tmp
, hash_size
);
877 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
879 result
= gcry_md_read(md
, 0);
886 memcpy(ret
, result
, hash_size
);
894 static int dnssec_test_nsec3(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
) {
895 _cleanup_free_
char *next_closer_domain
= NULL
, *l
= NULL
;
896 uint8_t hashed
[DNSSEC_HASH_SIZE_MAX
];
897 const char *p
, *pp
= NULL
;
898 DnsResourceRecord
*rr
;
899 DnsAnswerFlags flags
;
905 /* First step, look for the closest encloser NSEC3 RR in 'answer' that matches 'key' */
906 p
= DNS_RESOURCE_KEY_NAME(key
);
908 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
909 _cleanup_free_
char *hashed_domain
= NULL
, *label
= NULL
;
911 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
914 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
917 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
918 if (!IN_SET(rr
->nsec3
.flags
, 0, 1))
921 r
= dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr
->key
), p
);
927 hashed_size
= dnssec_nsec3_hash(rr
, p
, hashed
);
928 if (hashed_size
== -EOPNOTSUPP
) {
929 *result
= DNSSEC_NSEC_UNSUPPORTED_ALGORITHM
;
934 if (rr
->nsec3
.next_hashed_name_size
!= (size_t) hashed_size
)
937 label
= base32hexmem(hashed
, hashed_size
, false);
941 hashed_domain
= strjoin(label
, ".", p
, NULL
);
945 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), hashed_domain
);
952 /* We didn't find the closest encloser with this name,
953 * but let's remember this domain name, it might be
954 * the next closer name */
958 /* Strip one label from the front */
959 r
= dns_name_parent(&p
);
966 *result
= DNSSEC_NSEC_NO_RR
;
970 /* We found a closest encloser in 'p'; next closer is 'pp' */
972 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
973 if (bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_DNAME
))
976 /* Ensure that this data is from the delegated domain
977 * (i.e. originates from the "lower" DNS server), and isn't
978 * just glue records (i.e. doesn't originate from the "upper"
980 if (bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_NS
) &&
981 !bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_SOA
))
985 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
986 *result
= bitmap_isset(rr
->nsec3
.types
, key
->type
) ? DNSSEC_NSEC_FOUND
: DNSSEC_NSEC_NODATA
;
990 r
= dnssec_nsec3_hash(rr
, pp
, hashed
);
993 if (r
!= hashed_size
)
996 l
= base32hexmem(hashed
, hashed_size
, false);
1000 next_closer_domain
= strjoin(l
, ".", p
, NULL
);
1001 if (!next_closer_domain
)
1004 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1005 _cleanup_free_
char *label
= NULL
, *next_hashed_domain
= NULL
;
1006 const char *nsec3_parent
;
1008 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
1011 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
1014 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1015 if (!IN_SET(rr
->nsec3
.flags
, 0, 1))
1018 nsec3_parent
= DNS_RESOURCE_KEY_NAME(rr
->key
);
1019 r
= dns_name_parent(&nsec3_parent
);
1025 r
= dns_name_equal(p
, nsec3_parent
);
1031 label
= base32hexmem(rr
->nsec3
.next_hashed_name
, rr
->nsec3
.next_hashed_name_size
, false);
1035 next_hashed_domain
= strjoin(label
, ".", p
, NULL
);
1036 if (!next_hashed_domain
)
1039 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), next_closer_domain
, next_hashed_domain
);
1043 if (rr
->nsec3
.flags
& 1)
1044 *result
= DNSSEC_NSEC_OPTOUT
;
1046 *result
= DNSSEC_NSEC_NXDOMAIN
;
1052 *result
= DNSSEC_NSEC_NO_RR
;
1056 int dnssec_test_nsec(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
) {
1057 DnsResourceRecord
*rr
;
1058 bool have_nsec3
= false;
1059 DnsAnswerFlags flags
;
1065 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1067 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1069 if (rr
->key
->class != key
->class)
1072 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
1075 switch (rr
->key
->type
) {
1079 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
));
1083 *result
= bitmap_isset(rr
->nsec
.types
, key
->type
) ? DNSSEC_NSEC_FOUND
: DNSSEC_NSEC_NODATA
;
1087 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
), rr
->nsec
.next_domain_name
);
1091 *result
= DNSSEC_NSEC_NXDOMAIN
;
1096 case DNS_TYPE_NSEC3
:
1102 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1104 return dnssec_test_nsec3(answer
, key
, result
);
1106 /* No approproate NSEC RR found, report this. */
1107 *result
= DNSSEC_NSEC_NO_RR
;
1111 static const char* const dnssec_mode_table
[_DNSSEC_MODE_MAX
] = {
1113 [DNSSEC_YES
] = "yes",
1115 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode
, DnssecMode
);
1117 static const char* const dnssec_result_table
[_DNSSEC_RESULT_MAX
] = {
1118 [DNSSEC_VALIDATED
] = "validated",
1119 [DNSSEC_INVALID
] = "invalid",
1120 [DNSSEC_SIGNATURE_EXPIRED
] = "signature-expired",
1121 [DNSSEC_UNSUPPORTED_ALGORITHM
] = "unsupported-algorithm",
1122 [DNSSEC_NO_SIGNATURE
] = "no-signature",
1123 [DNSSEC_MISSING_KEY
] = "missing-key",
1124 [DNSSEC_UNSIGNED
] = "unsigned",
1125 [DNSSEC_FAILED_AUXILIARY
] = "failed-auxiliary",
1126 [DNSSEC_NSEC_MISMATCH
] = "nsec-mismatch",
1128 DEFINE_STRING_TABLE_LOOKUP(dnssec_result
, DnssecResult
);