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
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
++) {
390 r
= dns_name_to_wire_format(DNS_RESOURCE_KEY_NAME(rr
->key
), wire_format_name
, sizeof(wire_format_name
), true);
393 gcry_md_write(md
, wire_format_name
, r
);
395 md_add_uint16(md
, rr
->key
->type
);
396 md_add_uint16(md
, rr
->key
->class);
397 md_add_uint32(md
, rrsig
->rrsig
.original_ttl
);
399 assert(rr
->wire_format_rdata_offset
<= rr
->wire_format_size
);
400 l
= rr
->wire_format_size
- rr
->wire_format_rdata_offset
;
403 md_add_uint16(md
, (uint16_t) l
);
404 gcry_md_write(md
, (uint8_t*) rr
->wire_format
+ rr
->wire_format_rdata_offset
, l
);
407 hash
= gcry_md_read(md
, 0);
413 if (*(uint8_t*) dnskey
->dnskey
.key
== 0) {
414 /* exponent is > 255 bytes long */
416 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 3;
418 ((size_t) (((uint8_t*) dnskey
->dnskey
.key
)[0]) << 8) |
419 ((size_t) ((uint8_t*) dnskey
->dnskey
.key
)[1]);
421 if (exponent_size
< 256) {
426 if (3 + exponent_size
>= dnskey
->dnskey
.key_size
) {
431 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 3 + exponent_size
;
432 modulus_size
= dnskey
->dnskey
.key_size
- 3 - exponent_size
;
435 /* exponent is <= 255 bytes long */
437 exponent
= (uint8_t*) dnskey
->dnskey
.key
+ 1;
438 exponent_size
= (size_t) ((uint8_t*) dnskey
->dnskey
.key
)[0];
440 if (exponent_size
<= 0) {
445 if (1 + exponent_size
>= dnskey
->dnskey
.key_size
) {
450 modulus
= (uint8_t*) dnskey
->dnskey
.key
+ 1 + exponent_size
;
451 modulus_size
= dnskey
->dnskey
.key_size
- 1 - exponent_size
;
454 r
= dnssec_rsa_verify(
455 gcry_md_algo_name(gcry_md_get_algo(md
)),
456 rrsig
->rrsig
.signature
, rrsig
->rrsig
.signature_size
,
458 exponent
, exponent_size
,
459 modulus
, modulus_size
);
463 *result
= r
? DNSSEC_VALIDATED
: DNSSEC_INVALID
;
471 int dnssec_rrsig_match_dnskey(DnsResourceRecord
*rrsig
, DnsResourceRecord
*dnskey
) {
476 /* Checks if the specified DNSKEY RR matches the key used for
477 * the signature in the specified RRSIG RR */
479 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
482 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
484 if (dnskey
->key
->class != rrsig
->key
->class)
486 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
488 if (dnskey
->dnskey
.protocol
!= 3)
490 if (dnskey
->dnskey
.algorithm
!= rrsig
->rrsig
.algorithm
)
493 if (dnssec_keytag(dnskey
) != rrsig
->rrsig
.key_tag
)
496 return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey
->key
), rrsig
->rrsig
.signer
);
499 int dnssec_key_match_rrsig(const DnsResourceKey
*key
, DnsResourceRecord
*rrsig
) {
503 /* Checks if the specified RRSIG RR protects the RRSet of the specified RR key. */
505 if (rrsig
->key
->type
!= DNS_TYPE_RRSIG
)
507 if (rrsig
->key
->class != key
->class)
509 if (rrsig
->rrsig
.type_covered
!= key
->type
)
512 return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig
->key
), DNS_RESOURCE_KEY_NAME(key
));
515 int dnssec_verify_rrset_search(
518 DnsAnswer
*validated_dnskeys
,
520 DnssecResult
*result
) {
522 bool found_rrsig
= false, found_invalid
= false, found_expired_rrsig
= false, found_unsupported_algorithm
= false;
523 DnsResourceRecord
*rrsig
;
529 /* Verifies all RRs from "a" that match the key "key" against DNSKEYs in "validated_dnskeys" */
531 if (!a
|| a
->n_rrs
<= 0)
534 /* Iterate through each RRSIG RR. */
535 DNS_ANSWER_FOREACH(rrsig
, a
) {
536 DnsResourceRecord
*dnskey
;
537 DnsAnswerFlags flags
;
539 /* Is this an RRSIG RR that applies to RRs matching our key? */
540 r
= dnssec_key_match_rrsig(key
, rrsig
);
548 /* Look for a matching key */
549 DNS_ANSWER_FOREACH_FLAGS(dnskey
, flags
, validated_dnskeys
) {
550 DnssecResult one_result
;
552 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
555 /* Is this a DNSKEY RR that matches they key of our RRSIG? */
556 r
= dnssec_rrsig_match_dnskey(rrsig
, dnskey
);
562 /* Take the time here, if it isn't set yet, so
563 * that we do all validations with the same
565 if (realtime
== USEC_INFINITY
)
566 realtime
= now(CLOCK_REALTIME
);
568 /* Yay, we found a matching RRSIG with a matching
569 * DNSKEY, awesome. Now let's verify all entries of
570 * the RRSet against the RRSIG and DNSKEY
573 r
= dnssec_verify_rrset(a
, key
, rrsig
, dnskey
, realtime
, &one_result
);
577 switch (one_result
) {
579 case DNSSEC_VALIDATED
:
580 /* Yay, the RR has been validated,
581 * return immediately. */
582 *result
= DNSSEC_VALIDATED
;
586 /* If the signature is invalid, let's try another
587 key and/or signature. After all they
588 key_tags and stuff are not unique, and
589 might be shared by multiple keys. */
590 found_invalid
= true;
593 case DNSSEC_UNSUPPORTED_ALGORITHM
:
594 /* If the key algorithm is
595 unsupported, try another
596 RRSIG/DNSKEY pair, but remember we
597 encountered this, so that we can
598 return a proper error when we
599 encounter nothing better. */
600 found_unsupported_algorithm
= true;
603 case DNSSEC_SIGNATURE_EXPIRED
:
604 /* If the signature is expired, try
605 another one, but remember it, so
606 that we can return this */
607 found_expired_rrsig
= true;
611 assert_not_reached("Unexpected DNSSEC validation result");
616 if (found_expired_rrsig
)
617 *result
= DNSSEC_SIGNATURE_EXPIRED
;
618 else if (found_unsupported_algorithm
)
619 *result
= DNSSEC_UNSUPPORTED_ALGORITHM
;
620 else if (found_invalid
)
621 *result
= DNSSEC_INVALID
;
622 else if (found_rrsig
)
623 *result
= DNSSEC_MISSING_KEY
;
625 *result
= DNSSEC_NO_SIGNATURE
;
630 int dnssec_has_rrsig(DnsAnswer
*a
, const DnsResourceKey
*key
) {
631 DnsResourceRecord
*rr
;
634 /* Checks whether there's at least one RRSIG in 'a' that proctects RRs of the specified key */
636 DNS_ANSWER_FOREACH(rr
, a
) {
637 r
= dnssec_key_match_rrsig(key
, rr
);
647 int dnssec_canonicalize(const char *n
, char *buffer
, size_t buffer_max
) {
651 /* Converts the specified hostname into DNSSEC canonicalized
660 r
= dns_label_unescape(&n
, buffer
, buffer_max
);
668 /* DNSSEC validation is always done on the ASCII version of the label */
669 k
= dns_label_apply_idna(buffer
, r
, buffer
, buffer_max
);
676 if (buffer_max
< (size_t) r
+ 2)
679 /* The DNSSEC canonical form is not clear on what to
680 * do with dots appearing in labels, the way DNS-SD
681 * does it. Refuse it for now. */
683 if (memchr(buffer
, '.', r
))
686 for (i
= 0; i
< (size_t) r
; i
++) {
687 if (buffer
[i
] >= 'A' && buffer
[i
] <= 'Z')
688 buffer
[i
] = buffer
[i
] - 'A' + 'a';
700 /* Not even a single label: this is the root domain name */
702 assert(buffer_max
> 2);
712 static int digest_to_gcrypt(uint8_t algorithm
) {
714 /* Translates a DNSSEC digest algorithm into a gcrypt digest iedntifier */
718 case DNSSEC_DIGEST_SHA1
:
721 case DNSSEC_DIGEST_SHA256
:
722 return GCRY_MD_SHA256
;
729 int dnssec_verify_dnskey(DnsResourceRecord
*dnskey
, DnsResourceRecord
*ds
) {
730 char owner_name
[DNSSEC_CANONICAL_HOSTNAME_MAX
];
731 gcry_md_hd_t md
= NULL
;
740 /* Implements DNSKEY verification by a DS, according to RFC 4035, section 5.2 */
742 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
744 if (ds
->key
->type
!= DNS_TYPE_DS
)
746 if ((dnskey
->dnskey
.flags
& DNSKEY_FLAG_ZONE_KEY
) == 0)
747 return -EKEYREJECTED
;
748 if (dnskey
->dnskey
.protocol
!= 3)
749 return -EKEYREJECTED
;
751 if (dnskey
->dnskey
.algorithm
!= ds
->ds
.algorithm
)
753 if (dnssec_keytag(dnskey
) != ds
->ds
.key_tag
)
756 initialize_libgcrypt();
758 algorithm
= digest_to_gcrypt(ds
->ds
.digest_type
);
762 hash_size
= gcry_md_get_algo_dlen(algorithm
);
763 assert(hash_size
> 0);
765 if (ds
->ds
.digest_size
!= hash_size
)
768 r
= dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey
->key
), owner_name
, sizeof(owner_name
));
772 gcry_md_open(&md
, algorithm
, 0);
776 gcry_md_write(md
, owner_name
, r
);
777 md_add_uint16(md
, dnskey
->dnskey
.flags
);
778 md_add_uint8(md
, dnskey
->dnskey
.protocol
);
779 md_add_uint8(md
, dnskey
->dnskey
.algorithm
);
780 gcry_md_write(md
, dnskey
->dnskey
.key
, dnskey
->dnskey
.key_size
);
782 result
= gcry_md_read(md
, 0);
788 r
= memcmp(result
, ds
->ds
.digest
, ds
->ds
.digest_size
) != 0;
795 int dnssec_verify_dnskey_search(DnsResourceRecord
*dnskey
, DnsAnswer
*validated_ds
) {
796 DnsResourceRecord
*ds
;
797 DnsAnswerFlags flags
;
802 if (dnskey
->key
->type
!= DNS_TYPE_DNSKEY
)
805 DNS_ANSWER_FOREACH_FLAGS(ds
, flags
, validated_ds
) {
807 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
810 if (ds
->key
->type
!= DNS_TYPE_DS
)
813 r
= dnssec_verify_dnskey(dnskey
, ds
);
823 int dnssec_nsec3_hash(DnsResourceRecord
*nsec3
, const char *name
, void *ret
) {
824 uint8_t wire_format
[DNS_WIRE_FOMAT_HOSTNAME_MAX
];
825 gcry_md_hd_t md
= NULL
;
836 if (nsec3
->key
->type
!= DNS_TYPE_NSEC3
)
839 algorithm
= digest_to_gcrypt(nsec3
->nsec3
.algorithm
);
843 initialize_libgcrypt();
845 hash_size
= gcry_md_get_algo_dlen(algorithm
);
846 assert(hash_size
> 0);
848 if (nsec3
->nsec3
.next_hashed_name_size
!= hash_size
)
851 r
= dns_name_to_wire_format(name
, wire_format
, sizeof(wire_format
), true);
855 gcry_md_open(&md
, algorithm
, 0);
859 gcry_md_write(md
, wire_format
, r
);
860 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
862 result
= gcry_md_read(md
, 0);
868 for (k
= 0; k
< nsec3
->nsec3
.iterations
; k
++) {
869 uint8_t tmp
[hash_size
];
870 memcpy(tmp
, result
, hash_size
);
873 gcry_md_write(md
, tmp
, hash_size
);
874 gcry_md_write(md
, nsec3
->nsec3
.salt
, nsec3
->nsec3
.salt_size
);
876 result
= gcry_md_read(md
, 0);
883 memcpy(ret
, result
, hash_size
);
891 static int dnssec_test_nsec3(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
) {
892 _cleanup_free_
char *next_closer_domain
= NULL
, *l
= NULL
;
893 uint8_t hashed
[DNSSEC_HASH_SIZE_MAX
];
894 const char *p
, *pp
= NULL
;
895 DnsResourceRecord
*rr
;
896 DnsAnswerFlags flags
;
902 /* First step, look for the closest encloser NSEC3 RR in 'answer' that matches 'key' */
903 p
= DNS_RESOURCE_KEY_NAME(key
);
905 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
906 _cleanup_free_
char *hashed_domain
= NULL
, *label
= NULL
;
908 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
911 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
914 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
915 if (!IN_SET(rr
->nsec3
.flags
, 0, 1))
918 r
= dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr
->key
), p
);
924 hashed_size
= dnssec_nsec3_hash(rr
, p
, hashed
);
925 if (hashed_size
== -EOPNOTSUPP
) {
926 *result
= DNSSEC_NSEC_UNSUPPORTED_ALGORITHM
;
931 if (rr
->nsec3
.next_hashed_name_size
!= (size_t) hashed_size
)
934 label
= base32hexmem(hashed
, hashed_size
, false);
938 hashed_domain
= strjoin(label
, ".", p
, NULL
);
942 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), hashed_domain
);
949 /* We didn't find the closest encloser with this name,
950 * but let's remember this domain name, it might be
951 * the next closer name */
955 /* Strip one label from the front */
956 r
= dns_name_parent(&p
);
963 *result
= DNSSEC_NSEC_NO_RR
;
967 /* We found a closest encloser in 'p'; next closer is 'pp' */
969 /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
970 if (bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_DNAME
))
973 /* Ensure that this data is from the delegated domain
974 * (i.e. originates from the "lower" DNS server), and isn't
975 * just glue records (i.e. doesn't originate from the "upper"
977 if (bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_NS
) &&
978 !bitmap_isset(rr
->nsec3
.types
, DNS_TYPE_SOA
))
982 /* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
983 *result
= bitmap_isset(rr
->nsec3
.types
, key
->type
) ? DNSSEC_NSEC_FOUND
: DNSSEC_NSEC_NODATA
;
987 r
= dnssec_nsec3_hash(rr
, pp
, hashed
);
990 if (r
!= hashed_size
)
993 l
= base32hexmem(hashed
, hashed_size
, false);
997 next_closer_domain
= strjoin(l
, ".", p
, NULL
);
998 if (!next_closer_domain
)
1001 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1002 _cleanup_free_
char *label
= NULL
, *next_hashed_domain
= NULL
;
1003 const char *nsec3_parent
;
1005 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
1008 if (rr
->key
->type
!= DNS_TYPE_NSEC3
)
1011 /* RFC 5155, Section 8.2 says we MUST ignore NSEC3 RRs with flags != 0 or 1 */
1012 if (!IN_SET(rr
->nsec3
.flags
, 0, 1))
1015 nsec3_parent
= DNS_RESOURCE_KEY_NAME(rr
->key
);
1016 r
= dns_name_parent(&nsec3_parent
);
1022 r
= dns_name_equal(p
, nsec3_parent
);
1028 label
= base32hexmem(rr
->nsec3
.next_hashed_name
, rr
->nsec3
.next_hashed_name_size
, false);
1032 next_hashed_domain
= strjoin(label
, ".", p
, NULL
);
1033 if (!next_hashed_domain
)
1036 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), next_closer_domain
, next_hashed_domain
);
1040 if (rr
->nsec3
.flags
& 1)
1041 *result
= DNSSEC_NSEC_OPTOUT
;
1043 *result
= DNSSEC_NSEC_NXDOMAIN
;
1049 *result
= DNSSEC_NSEC_NO_RR
;
1053 int dnssec_test_nsec(DnsAnswer
*answer
, DnsResourceKey
*key
, DnssecNsecResult
*result
) {
1054 DnsResourceRecord
*rr
;
1055 bool have_nsec3
= false;
1056 DnsAnswerFlags flags
;
1062 /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */
1064 DNS_ANSWER_FOREACH_FLAGS(rr
, flags
, answer
) {
1066 if (rr
->key
->class != key
->class)
1069 if ((flags
& DNS_ANSWER_AUTHENTICATED
) == 0)
1072 switch (rr
->key
->type
) {
1076 r
= dns_name_equal(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
));
1080 *result
= bitmap_isset(rr
->nsec
.types
, key
->type
) ? DNSSEC_NSEC_FOUND
: DNSSEC_NSEC_NODATA
;
1084 r
= dns_name_between(DNS_RESOURCE_KEY_NAME(rr
->key
), DNS_RESOURCE_KEY_NAME(key
), rr
->nsec
.next_domain_name
);
1088 *result
= DNSSEC_NSEC_NXDOMAIN
;
1093 case DNS_TYPE_NSEC3
:
1099 /* OK, this was not sufficient. Let's see if NSEC3 can help. */
1101 return dnssec_test_nsec3(answer
, key
, result
);
1103 /* No approproate NSEC RR found, report this. */
1104 *result
= DNSSEC_NSEC_NO_RR
;
1108 static const char* const dnssec_mode_table
[_DNSSEC_MODE_MAX
] = {
1110 [DNSSEC_YES
] = "yes",
1112 DEFINE_STRING_TABLE_LOOKUP(dnssec_mode
, DnssecMode
);
1114 static const char* const dnssec_result_table
[_DNSSEC_RESULT_MAX
] = {
1115 [DNSSEC_VALIDATED
] = "validated",
1116 [DNSSEC_INVALID
] = "invalid",
1117 [DNSSEC_SIGNATURE_EXPIRED
] = "signature-expired",
1118 [DNSSEC_UNSUPPORTED_ALGORITHM
] = "unsupported-algorithm",
1119 [DNSSEC_NO_SIGNATURE
] = "no-signature",
1120 [DNSSEC_MISSING_KEY
] = "missing-key",
1121 [DNSSEC_UNSIGNED
] = "unsigned",
1122 [DNSSEC_FAILED_AUXILIARY
] = "failed-auxiliary",
1123 [DNSSEC_NSEC_MISMATCH
] = "nsec-mismatch",
1125 DEFINE_STRING_TABLE_LOOKUP(dnssec_result
, DnssecResult
);