1use alloc::{borrow::ToOwned, boxed::Box, string::ToString, sync::Arc, vec::Vec};
11use core::{clone::Clone, pin::Pin};
12use std::{
13 collections::{HashMap, HashSet},
14 time::{SystemTime, UNIX_EPOCH},
15};
16
17use futures_util::{
18 future::{self, TryFutureExt},
19 stream::{self, Stream, TryStreamExt},
20};
21use tracing::{debug, error, trace, warn};
22
23use crate::{
24 dnssec::{
25 Algorithm, Proof, ProofError, ProofErrorKind, TrustAnchors, Verifier,
26 rdata::{DNSKEY, DS, RRSIG},
27 },
28 error::{ProtoError, ProtoErrorKind},
29 op::{Edns, Message, OpCode, Query},
30 rr::{Name, Record, RecordData, RecordType, SerialNumber, resource::RecordRef},
31 xfer::{DnsRequest, DnsRequestOptions, DnsResponse, FirstAnswer, dns_handle::DnsHandle},
32};
33
34use self::rrset::Rrset;
35
36mod nsec3_validation;
37use nsec3_validation::verify_nsec3;
38
39use super::rdata::NSEC;
40
41#[derive(Clone)]
47#[must_use = "queries can only be sent through a DnsHandle"]
48pub struct DnssecDnsHandle<H>
49where
50 H: DnsHandle + Unpin + 'static,
51{
52 handle: H,
53 trust_anchor: Arc<TrustAnchors>,
54 request_depth: usize,
55 minimum_key_len: usize,
56 minimum_algorithm: Algorithm, }
58
59impl<H> DnssecDnsHandle<H>
60where
61 H: DnsHandle + Unpin + 'static,
62{
63 pub fn new(handle: H) -> Self {
70 Self::with_trust_anchor(handle, Arc::new(TrustAnchors::default()))
71 }
72
73 pub fn with_trust_anchor(handle: H, trust_anchor: Arc<TrustAnchors>) -> Self {
81 Self {
82 handle,
83 trust_anchor,
84 request_depth: 0,
85 minimum_key_len: 0,
86 minimum_algorithm: Algorithm::RSASHA256,
87 }
88 }
89
90 fn clone_with_context(&self) -> Self {
94 Self {
95 handle: self.handle.clone(),
96 trust_anchor: Arc::clone(&self.trust_anchor),
97 request_depth: self.request_depth + 1,
98 minimum_key_len: self.minimum_key_len,
99 minimum_algorithm: self.minimum_algorithm,
100 }
101 }
102}
103
104#[cfg(any(feature = "std", feature = "no-std-rand"))]
105impl<H> DnsHandle for DnssecDnsHandle<H>
106where
107 H: DnsHandle + Sync + Unpin,
108{
109 type Response = Pin<Box<dyn Stream<Item = Result<DnsResponse, ProtoError>> + Send>>;
110
111 fn is_verifying_dnssec(&self) -> bool {
112 true
114 }
115
116 fn send<R: Into<DnsRequest>>(&self, request: R) -> Self::Response {
117 let mut request = request.into();
118
119 if self.request_depth > request.options().max_request_depth {
121 error!("exceeded max validation depth");
122 return Box::pin(stream::once(future::err(ProtoError::from(
123 "exceeded max validation depth",
124 ))));
125 }
126
127 match request.op_code() {
129 OpCode::Query => {}
130 _ => return Box::pin(self.handle.send(request)),
131 }
132
133 let query = if let Some(query) = request.queries().first().cloned() {
136 query
137 } else {
138 return Box::pin(stream::once(future::err(ProtoError::from(
139 "no query in request",
140 ))));
141 };
142
143 let handle = self.clone_with_context();
144 request
145 .extensions_mut()
146 .get_or_insert_with(Edns::new)
147 .enable_dnssec();
148
149 request.set_authentic_data(true);
150 request.set_checking_disabled(false);
151 let options = *request.options();
152
153 Box::pin(
154 self.handle
155 .send(request)
156 .or_else(move |res| {
157 match res.kind() {
160 ProtoErrorKind::NoRecordsFound {
161 query,
162 authorities,
163 response_code,
164 ..
165 } => {
166 let mut msg = Message::new();
167
168 debug!("translating NoRecordsFound to DnsResponse for {query}");
169
170 msg.add_query(*query.clone());
171
172 msg.set_response_code(*response_code);
173
174 if let Some(authorities) = authorities {
175 for ns in authorities.iter() {
176 msg.add_name_server(ns.clone());
177 }
178 }
179
180 match DnsResponse::from_message(msg) {
181 Ok(res) => future::ok(res),
182 Err(_e) => future::err(ProtoError::from(
183 "unable to construct DnsResponse: {_e:?}",
184 )),
185 }
186 }
187 _ => future::err(ProtoError::from(res.to_string())),
188 }
189 })
190 .and_then(move |message_response| {
191 verify_response(handle.clone(), message_response, options)
192 })
193 .and_then(move |verified_message| {
194 future::ready(check_nsec(verified_message, &query))
195 }),
196 )
197 }
198}
199
200fn check_nsec(verified_message: DnsResponse, query: &Query) -> Result<DnsResponse, ProtoError> {
206 if !verified_message.answers().is_empty() {
207 return Ok(verified_message);
208 }
209
210 if !verified_message.name_servers().is_empty()
211 && verified_message
212 .name_servers()
213 .iter()
214 .all(|x| x.proof() == Proof::Insecure)
215 {
216 return Ok(verified_message);
217 }
218
219 let soa_name = if let Some(soa_name) = verified_message
221 .name_servers()
222 .iter()
223 .find(|rr| rr.record_type() == RecordType::SOA)
225 .map(Record::name)
226 {
227 soa_name
228 } else {
229 return Err(ProtoError::from(
230 "could not validate negative response missing SOA",
231 ));
232 };
233
234 let nsec3s = verified_message
235 .name_servers()
236 .iter()
237 .filter_map(|rr| {
238 rr.data()
239 .as_dnssec()?
240 .as_nsec3()
241 .map(|data| (rr.name(), data))
242 })
243 .collect::<Vec<_>>();
244
245 let nsecs = verified_message
246 .name_servers()
247 .iter()
248 .filter_map(|rr| {
249 rr.data()
250 .as_dnssec()?
251 .as_nsec()
252 .map(|data| (rr.name(), data))
253 })
254 .collect::<Vec<_>>();
255
256 let nsec_proof = match (!nsec3s.is_empty(), !nsecs.is_empty()) {
260 (true, false) => verify_nsec3(
261 query,
262 soa_name,
263 verified_message.response_code(),
264 verified_message.answers(),
265 &nsec3s,
266 ),
267 (false, true) => verify_nsec(query, soa_name, nsecs.as_slice()),
268 (true, true) => {
269 warn!(
270 "response contains both NSEC and NSEC3 records\nQuery:\n{query:?}\nResponse:\n{verified_message:?}"
271 );
272 Proof::Bogus
273 }
274 (false, false) => {
275 warn!(
276 "response does not contain NSEC or NSEC3 records. Query: {query:?} response: {verified_message:?}"
277 );
278 Proof::Bogus
279 }
280 };
281
282 if !nsec_proof.is_secure() {
283 debug!("returning Nsec error for {} {nsec_proof}", query.name());
284 return Err(ProtoError::from(ProtoErrorKind::Nsec {
286 query: Box::new(query.clone()),
287 proof: nsec_proof,
288 }));
289 }
290
291 Ok(verified_message)
292}
293
294async fn verify_response<H>(
296 handle: DnssecDnsHandle<H>,
297 mut message: DnsResponse,
298 options: DnsRequestOptions,
299) -> Result<DnsResponse, ProtoError>
300where
301 H: DnsHandle + Sync + Unpin,
302{
303 debug!(
304 "validating message_response: {}, with {} trust_anchors",
305 message.id(),
306 handle.trust_anchor.len(),
307 );
308
309 let answers = message.take_answers();
312 let nameservers = message.take_name_servers();
313 let additionals = message.take_additionals();
314
315 let answers = verify_rrsets(&handle, answers, options).await;
316 let nameservers = verify_rrsets(&handle, nameservers, options).await;
317 let additionals = verify_rrsets(&handle, additionals, options).await;
318
319 message.insert_answers(answers);
320 message.insert_name_servers(nameservers);
321 message.insert_additionals(additionals);
322
323 Ok(message)
324}
325
326#[allow(clippy::type_complexity)]
329async fn verify_rrsets<H>(
330 handle: &DnssecDnsHandle<H>,
331 records: Vec<Record>,
332 options: DnsRequestOptions,
333) -> Vec<Record>
334where
335 H: DnsHandle + Sync + Unpin,
336{
337 let mut rrset_types: HashSet<(Name, RecordType)> = HashSet::new();
338
339 for rrset in records
340 .iter()
341 .filter(|rr| {
342 !is_dnssec(rr, RecordType::RRSIG) &&
343 (handle.request_depth <= 1 ||
348 is_dnssec(rr, RecordType::DNSKEY) ||
349 is_dnssec(rr, RecordType::DS))
350 })
351 .map(|rr| (rr.name().clone(), rr.record_type()))
352 {
353 rrset_types.insert(rrset);
354 }
355
356 if rrset_types.is_empty() {
358 return records;
359 }
360
361 let mut return_records = Vec::with_capacity(records.len());
363
364 let (mut rrsigs, mut records) = records
367 .into_iter()
368 .partition::<Vec<_>, _>(|r| r.record_type().is_rrsig());
369
370 for (name, record_type) in rrset_types {
371 let current_rrset;
373 (current_rrset, records) = records
374 .into_iter()
375 .partition::<Vec<_>, _>(|rr| rr.record_type() == record_type && rr.name() == &name);
376
377 let current_rrsigs;
378 (current_rrsigs, rrsigs) = rrsigs.into_iter().partition::<Vec<_>, _>(|rr| {
379 rr.try_borrow::<RRSIG>()
380 .map(|rr| rr.name() == &name && rr.data().type_covered() == record_type)
381 .unwrap_or_default()
382 });
383
384 let mut rrs_to_verify = current_rrset.iter();
386 let mut rrset = Rrset::new(rrs_to_verify.next().unwrap());
387 rrs_to_verify.for_each(|rr| rrset.add(rr));
388
389 let rrsigs: Vec<_> = current_rrsigs
391 .iter()
392 .filter_map(|rr| rr.try_borrow::<RRSIG>())
393 .filter(|rr| rr.name() == &name)
394 .filter(|rrsig| rrsig.data().type_covered() == record_type)
395 .collect();
396
397 debug!(
402 "verifying: {name} record_type: {record_type}, rrsigs: {rrsig_len}",
403 rrsig_len = rrsigs.len()
404 );
405
406 let proof = verify_rrset(handle.clone(), &rrset, rrsigs, options).await;
408
409 let proof = match proof {
410 Ok(proof) => {
411 debug!("verified: {name} record_type: {record_type}",);
412 proof
413 }
414 Err(ProofError { proof, kind }) => {
415 match kind {
416 ProofErrorKind::DsResponseNsec { .. } => {
417 debug!("verified insecure {name}/{record_type}")
418 }
419 _ => debug!("failed to verify: {name} record_type: {record_type}: {kind}"),
420 }
421 (proof, None, None)
422 }
423 };
424
425 let (proof, adjusted_ttl, rrsig_idx) = proof;
426 for mut record in current_rrset {
427 record.set_proof(proof);
428 if let (Proof::Secure, Some(ttl)) = (proof, adjusted_ttl) {
429 record.set_ttl(ttl);
430 }
431
432 return_records.push(record);
433 }
434
435 let mut current_rrsigs = current_rrsigs;
437 if let Some(rrsig_idx) = rrsig_idx {
438 if let Some(rrsig) = current_rrsigs.get_mut(rrsig_idx) {
439 rrsig.set_proof(proof);
440 if let (Proof::Secure, Some(ttl)) = (proof, adjusted_ttl) {
441 rrsig.set_ttl(ttl);
442 }
443 } else {
444 warn!(
445 "bad rrsig index {rrsig_idx} rrsigs.len = {}",
446 current_rrsigs.len()
447 );
448 }
449 }
450
451 return_records.extend(current_rrsigs);
453 }
454
455 return_records.extend(rrsigs);
457 return_records.extend(records);
458 return_records
459}
460
461fn is_dnssec<D: RecordData>(rr: &Record<D>, dnssec_type: RecordType) -> bool {
463 rr.record_type().is_dnssec() && dnssec_type.is_dnssec() && rr.record_type() == dnssec_type
464}
465
466async fn verify_rrset<H>(
480 handle: DnssecDnsHandle<H>,
481 rrset: &Rrset<'_>,
482 rrsigs: Vec<RecordRef<'_, RRSIG>>,
483 options: DnsRequestOptions,
484) -> Result<(Proof, Option<u32>, Option<usize>), ProofError>
485where
486 H: DnsHandle + Sync + Unpin,
487{
488 let current_time = current_time();
490
491 if matches!(rrset.record_type(), RecordType::DNSKEY) {
493 let proof = verify_dnskey_rrset(handle, rrset, &rrsigs, current_time, options).await?;
494
495 return Ok(proof);
496 }
497
498 verify_default_rrset(&handle, rrset, &rrsigs, current_time, options).await
499}
500
501async fn verify_dnskey_rrset<H>(
519 handle: DnssecDnsHandle<H>,
520 rrset: &Rrset<'_>,
521 rrsigs: &Vec<RecordRef<'_, RRSIG>>,
522 current_time: u32,
523 options: DnsRequestOptions,
524) -> Result<(Proof, Option<u32>, Option<usize>), ProofError>
525where
526 H: DnsHandle + Sync + Unpin,
527{
528 if RecordType::DNSKEY != rrset.record_type() {
530 panic!("All other RRSETs must use verify_default_rrset");
531 }
532
533 debug!(
534 "dnskey validation {}, record_type: {:?}",
535 rrset.name(),
536 rrset.record_type()
537 );
538
539 let mut dnskey_proofs =
540 Vec::<(Proof, Option<u32>, Option<usize>)>::with_capacity(rrset.records().len());
541 dnskey_proofs.resize(rrset.records().len(), (Proof::Bogus, None, None));
542
543 for (r, proof) in rrset.records().iter().zip(dnskey_proofs.iter_mut()) {
545 let Some(dnskey) = r.try_borrow::<DNSKEY>() else {
546 continue;
547 };
548
549 proof.0 = is_dnskey_in_root_store(&handle, &dnskey);
550 }
551
552 let ds_records = if !dnskey_proofs.iter().all(|p| p.0.is_secure()) && !rrset.name().is_root() {
554 fetch_ds_records(&handle, rrset.name().clone(), options).await?
557 } else {
558 debug!("ignoring DS lookup for root zone or registered keys");
559 Vec::default()
560 };
561
562 if ds_records
565 .iter()
566 .filter(|ds| ds.proof().is_secure() || ds.proof().is_insecure())
567 .all(|ds| !ds.data().algorithm().is_supported() || !ds.data().digest_type().is_supported())
568 && !ds_records.is_empty()
569 {
570 debug!(
571 "all dnskeys use unsupported algorithms and there are no supported DS records in the parent zone"
572 );
573 return Err(ProofError::new(
575 Proof::Insecure,
576 ProofErrorKind::UnsupportedKeyAlgorithm,
577 ));
578 }
579
580 for (r, proof) in rrset.records().iter().zip(dnskey_proofs.iter_mut()) {
582 let Some(dnskey) = r.try_borrow() else {
583 continue;
584 };
585
586 if proof.0.is_secure() {
587 continue;
588 }
589
590 match verify_dnskey(&dnskey, &ds_records) {
592 Ok(pf) => {
593 *proof = (pf, None, None);
594 }
595 Err(err) => {
596 *proof = (err.proof, None, None);
597 }
598 }
599 }
600
601 for (i, rrsig) in rrsigs.iter().enumerate() {
604 let signer_name = rrsig.data().signer_name();
606
607 let rrset_proof = rrset
608 .records()
609 .iter()
610 .zip(dnskey_proofs.iter())
611 .filter(|(_, (proof, ..))| proof.is_secure())
612 .filter(|(r, _)| r.name() == signer_name)
613 .filter_map(|(r, (proof, ..))| {
614 RecordRef::<'_, DNSKEY>::try_from(*r)
615 .ok()
616 .map(|r| (r, proof))
617 })
618 .find_map(|(dnskey, proof)| {
619 verify_rrset_with_dnskey(dnskey, *proof, rrsig, rrset, current_time).ok()
620 });
621
622 if let Some(rrset_proof) = rrset_proof {
623 return Ok((rrset_proof.0, rrset_proof.1, Some(i)));
624 }
625 }
626
627 if dnskey_proofs.iter().all(|(proof, ..)| proof.is_secure()) {
629 return Ok(dnskey_proofs.pop().unwrap());
630 }
631
632 if !ds_records.is_empty() {
633 trace!("bogus dnskey: {}", rrset.name());
635 return Err(ProofError::new(
636 Proof::Bogus,
637 ProofErrorKind::DsRecordsButNoDnskey {
638 name: rrset.name().clone(),
639 },
640 ));
641 }
642
643 trace!("no dnskey found: {}", rrset.name());
647 Err(ProofError::new(
648 Proof::Bogus,
649 ProofErrorKind::DnskeyNotFound {
650 name: rrset.name().clone(),
651 },
652 ))
653}
654
655fn is_dnskey_in_root_store<H>(handle: &DnssecDnsHandle<H>, rr: &RecordRef<'_, DNSKEY>) -> Proof
661where
662 H: DnsHandle + Sync + Unpin,
663{
664 let dns_key = rr.data();
665 let pub_key = dns_key.public_key();
666
667 if handle.trust_anchor.contains(pub_key) {
669 debug!(
670 "validated dnskey with trust_anchor: {}, {dns_key}",
671 rr.name(),
672 );
673
674 Proof::Secure
675 } else {
676 Proof::Bogus
677 }
678}
679
680fn verify_dnskey(
682 rr: &RecordRef<'_, DNSKEY>,
683 ds_records: &[Record<DS>],
684) -> Result<Proof, ProofError> {
685 let key_rdata = rr.data();
686 let key_tag = key_rdata.calculate_key_tag().map_err(|_| {
687 ProofError::new(
688 Proof::Insecure,
689 ProofErrorKind::ErrorComputingKeyTag {
690 name: rr.name().clone(),
691 },
692 )
693 })?;
694 let key_algorithm = key_rdata.algorithm();
695
696 if !key_algorithm.is_supported() {
697 return Err(ProofError::new(
698 Proof::Insecure,
699 ProofErrorKind::UnsupportedKeyAlgorithm,
700 ));
701 }
702
703 let mut key_authentication_attempts = 0;
705 for r in ds_records.iter().filter(|ds| ds.proof().is_secure()) {
706 if r.data().algorithm() != key_algorithm {
707 trace!(
708 "skipping DS record due to algorithm mismatch, expected algorithm {}: ({}, {})",
709 key_algorithm,
710 r.name(),
711 r.data(),
712 );
713
714 continue;
715 }
716
717 if r.data().key_tag() != key_tag {
718 trace!(
719 "skipping DS record due to key tag mismatch, expected tag {key_tag}: ({}, {})",
720 r.name(),
721 r.data(),
722 );
723
724 continue;
725 }
726
727 key_authentication_attempts += 1;
731 if key_authentication_attempts > MAX_KEY_TAG_COLLISIONS {
732 warn!(
733 key_tag,
734 attempts = key_authentication_attempts,
735 "too many DS records with same key tag; skipping"
736 );
737 continue;
738 }
739
740 if !r.data().covers(rr.name(), key_rdata).unwrap_or(false) {
741 continue;
742 }
743
744 debug!(
745 "validated dnskey ({}, {key_rdata}) with {} {}",
746 rr.name(),
747 r.name(),
748 r.data(),
749 );
750
751 return Ok(Proof::Secure);
753 }
754
755 trace!("bogus dnskey: {}", rr.name());
756 Err(ProofError::new(
757 Proof::Bogus,
758 ProofErrorKind::DnsKeyHasNoDs {
759 name: rr.name().clone(),
760 },
761 ))
762}
763
764async fn fetch_ds_records<H>(
766 handle: &DnssecDnsHandle<H>,
767 zone: Name,
768 options: DnsRequestOptions,
769) -> Result<Vec<Record<DS>>, ProofError>
770where
771 H: DnsHandle + Sync + Unpin,
772{
773 let ds_message = handle
774 .lookup(Query::query(zone.clone(), RecordType::DS), options)
775 .first_answer()
776 .await;
777
778 let error = match ds_message {
779 Ok(mut ds_message)
780 if ds_message
781 .answers()
782 .iter()
783 .filter(|r| r.record_type() == RecordType::DS)
784 .any(|r| r.proof().is_secure()) =>
785 {
786 let all_records = ds_message
789 .take_answers()
790 .into_iter()
791 .filter_map(|r| Record::<DS>::try_from(r).ok());
792
793 let mut supported_records = vec![];
794 let mut all_unknown = None;
795 for record in all_records {
796 if (!record.data().algorithm().is_supported()
799 || !record.data().digest_type().is_supported())
800 && (record.proof().is_secure() || record.proof().is_insecure())
801 {
802 all_unknown.get_or_insert(true);
803 continue;
804 }
805 all_unknown = Some(false);
806
807 supported_records.push(record);
808 }
809
810 if all_unknown.unwrap_or(false) {
811 return Err(ProofError::new(
812 Proof::Insecure,
813 ProofErrorKind::UnknownKeyAlgorithm,
814 ));
815 } else if !supported_records.is_empty() {
816 return Ok(supported_records);
817 } else {
818 ProtoError::from(ProtoErrorKind::NoError)
819 }
820 }
821 Ok(response) => {
822 let any_ds_rr = response
823 .answers()
824 .iter()
825 .any(|r| r.record_type() == RecordType::DS);
826 if any_ds_rr {
827 ProtoError::from(ProtoErrorKind::NoError)
828 } else {
829 debug!("marking {zone} as insecure based on secure NSEC/NSEC3 proof");
832 return Err(ProofError::new(
833 Proof::Insecure,
834 ProofErrorKind::DsResponseNsec { name: zone },
835 ));
836 }
837 }
838 Err(error) => error,
839 };
840
841 if let Some((query, _proof)) = error
843 .kind()
844 .as_nsec()
845 .filter(|(_query, proof)| proof.is_insecure())
846 {
847 debug!(
848 "marking {} as insecure based on insecure NSEC/NSEC3 proof",
849 query.name()
850 );
851 return Err(ProofError::new(
852 Proof::Insecure,
853 ProofErrorKind::DsResponseNsec {
854 name: query.name().to_owned(),
855 },
856 ));
857 }
858
859 Err(ProofError::ds_should_exist(zone))
860}
861
862async fn find_ds_records<H>(
864 handle: &DnssecDnsHandle<H>,
865 zone: Name,
866 options: DnsRequestOptions,
867) -> Result<(), ProofError>
868where
869 H: DnsHandle + Sync + Unpin,
870{
871 match fetch_ds_records(handle, zone.clone(), options).await {
872 Ok(_) => return Ok(()),
873 Err(ProofError {
874 proof: _,
875 kind: ProofErrorKind::DsRecordShouldExist { .. },
876 }) => {}
877 Err(err) => return Err(err),
878 }
879
880 let mut parent = zone.base_name();
883 loop {
884 match fetch_ds_records(handle, parent.clone(), options).await {
885 Ok(_) => {
886 return Err(ProofError::ds_should_exist(zone));
887 }
888 Err(ProofError {
889 proof: _,
890 kind: ProofErrorKind::DsRecordShouldExist { .. },
891 }) => {}
892 Err(err) => return Err(err),
893 }
894 parent = match parent.is_root() {
895 true => return Err(ProofError::ds_should_exist(zone)),
896 false => parent.base_name(),
897 };
898 }
899}
900
901#[allow(clippy::blocks_in_conditions)]
917async fn verify_default_rrset<H>(
918 handle: &DnssecDnsHandle<H>,
919 rrset: &Rrset<'_>,
920 rrsigs: &Vec<RecordRef<'_, RRSIG>>,
921 current_time: u32,
922 options: DnsRequestOptions,
923) -> Result<(Proof, Option<u32>, Option<usize>), ProofError>
924where
925 H: DnsHandle + Sync + Unpin,
926{
927 if RecordType::DNSKEY == rrset.record_type() {
929 panic!("DNSKEYs must be validated with verify_dnskey_rrset");
930 }
931
932 if rrsigs.is_empty() {
933 find_ds_records(handle, rrset.name().clone(), options).await?; return Err(ProofError::new(
939 Proof::Bogus,
940 ProofErrorKind::RrsigsNotPresent {
941 name: rrset.name().clone(),
942 record_type: rrset.record_type(),
943 },
944 ));
945 }
946
947 trace!(
949 "default validation {}, record_type: {:?}",
950 rrset.name(),
951 rrset.record_type()
952 );
953
954 let verifications = rrsigs
964 .iter()
965 .enumerate()
966 .filter_map(|(i, rrsig)| {
967 let query = Query::query(rrsig.data().signer_name().clone(), RecordType::DNSKEY);
968
969 if i > MAX_RRSIGS_PER_RRSET {
970 warn!("too many ({i}) RRSIGs for rrset {rrset:?}; skipping");
971 return None;
972 }
973
974 Some(handle
976 .lookup(query.clone(), options)
977 .first_answer()
978 .map_err(|proto| {
979 ProofError::new(Proof::Bogus, ProofErrorKind::Proto { query, proto })
980 })
981 .map_ok(move |message| {
982 let mut tag_count = HashMap::<u16, usize>::new();
983
984 let dnskeys = message
986 .answers()
987 .iter()
988 .filter_map(|r| {
989 let dnskey = r.try_borrow::<DNSKEY>()?;
990
991 let tag = match dnskey.data().calculate_key_tag() {
992 Ok(tag) => tag,
993 Err(e) => {
994 warn!("unable to calculate key tag: {e:?}; skipping key");
995 return None;
996 }
997 };
998
999 match tag_count.get_mut(&tag) {
1000 Some(n_keys) => {
1001 *n_keys += 1;
1002 if *n_keys > MAX_KEY_TAG_COLLISIONS {
1003 warn!("too many ({n_keys}) DNSKEYs with key tag {tag}; skipping");
1004 return None;
1005 }
1006 }
1007 None => _ = tag_count.insert(tag, 1),
1008 }
1009
1010 Some(dnskey)
1011 });
1012
1013 let mut all_insecure = None;
1014 for dnskey in dnskeys {
1015 match dnskey.proof() {
1016 Proof::Secure => {
1017 all_insecure = Some(false);
1018 if let Ok(proof) =
1019 verify_rrset_with_dnskey(dnskey, dnskey.proof(), rrsig, rrset, current_time)
1020 {
1021 return Some((proof.0, proof.1, Some(i)));
1022 }
1023 }
1024 Proof::Insecure => {
1025 all_insecure.get_or_insert(true);
1026 }
1027 _ => all_insecure = Some(false),
1028 }
1029 }
1030
1031 if all_insecure.unwrap_or(false) {
1032 Some((Proof::Insecure, None, None))
1034 } else {
1035 None
1036 }
1037 }))
1038 })
1039 .collect::<Vec<_>>();
1040
1041 if verifications.is_empty() {
1043 return Err(ProofError::new(
1044 Proof::Bogus,
1045 ProofErrorKind::RrsigsNotPresent {
1046 name: rrset.name().clone(),
1047 record_type: rrset.record_type(),
1048 },
1049 ));
1050 }
1051
1052 let select = future::select_ok(verifications);
1054
1055 let (proof, rest) = select.await?;
1057 drop(rest);
1058
1059 proof.ok_or_else(||
1060 ProofError::new(Proof::Bogus, ProofErrorKind::RrsigsUnverified{name: rrset.name().clone(), record_type: rrset.record_type()})
1062 )
1063}
1064
1065fn verify_rrset_with_dnskey(
1067 dnskey: RecordRef<'_, DNSKEY>,
1068 dnskey_proof: Proof,
1069 rrsig: &RecordRef<'_, RRSIG>,
1070 rrset: &Rrset<'_>,
1071 current_time: u32,
1072) -> Result<(Proof, Option<u32>), ProofError> {
1073 match dnskey_proof {
1074 Proof::Secure => (),
1075 proof => {
1076 debug!("insecure dnskey {} {}", dnskey.name(), dnskey.data());
1077 return Err(ProofError::new(
1078 proof,
1079 ProofErrorKind::InsecureDnsKey {
1080 name: dnskey.name().clone(),
1081 key_tag: rrsig.data().key_tag(),
1082 },
1083 ));
1084 }
1085 }
1086
1087 if dnskey.data().revoke() {
1088 debug!("revoked dnskey {} {}", dnskey.name(), dnskey.data());
1089 return Err(ProofError::new(
1090 Proof::Bogus,
1091 ProofErrorKind::DnsKeyRevoked {
1092 name: dnskey.name().clone(),
1093 key_tag: rrsig.data().key_tag(),
1094 },
1095 ));
1096 } if !dnskey.data().zone_key() {
1098 return Err(ProofError::new(
1099 Proof::Bogus,
1100 ProofErrorKind::NotZoneDnsKey {
1101 name: dnskey.name().clone(),
1102 key_tag: rrsig.data().key_tag(),
1103 },
1104 ));
1105 }
1106 if dnskey.data().algorithm() != rrsig.data().algorithm() {
1107 return Err(ProofError::new(
1108 Proof::Bogus,
1109 ProofErrorKind::AlgorithmMismatch {
1110 rrsig: rrsig.data().algorithm(),
1111 dnskey: dnskey.data().algorithm(),
1112 },
1113 ));
1114 }
1115
1116 let validity = check_rrsig_validity(*rrsig, rrset, dnskey, current_time);
1117 if !matches!(validity, RrsigValidity::ValidRrsig) {
1118 return Err(ProofError::new(
1121 Proof::Bogus,
1122 ProofErrorKind::Msg(format!("{:?}", validity)),
1123 ));
1124 }
1125
1126 dnskey
1127 .data()
1128 .verify_rrsig(
1129 rrset.name(),
1130 rrset.record_class(),
1131 rrsig.data(),
1132 rrset.records().iter().copied(),
1133 )
1134 .map(|_| {
1135 debug!(
1136 "validated ({}, {:?}) with ({}, {})",
1137 rrset.name(),
1138 rrset.record_type(),
1139 dnskey.name(),
1140 dnskey.data()
1141 );
1142 (
1143 Proof::Secure,
1144 Some(rrsig.data().authenticated_ttl(rrset.record(), current_time)),
1145 )
1146 })
1147 .map_err(|e| {
1148 debug!(
1149 "failed validation of ({}, {:?}) with ({}, {})",
1150 rrset.name(),
1151 rrset.record_type(),
1152 dnskey.name(),
1153 dnskey.data()
1154 );
1155 ProofError::new(
1156 Proof::Bogus,
1157 ProofErrorKind::DnsKeyVerifyRrsig {
1158 name: dnskey.name().clone(),
1159 key_tag: rrsig.data().key_tag(),
1160 error: e,
1161 },
1162 )
1163 })
1164}
1165
1166fn check_rrsig_validity(
1168 rrsig: RecordRef<'_, RRSIG>,
1169 rrset: &Rrset<'_>,
1170 dnskey: RecordRef<'_, DNSKEY>,
1171 current_time: u32,
1172) -> RrsigValidity {
1173 let current_time = SerialNumber(current_time);
1174 let expiration = rrsig.data().sig_expiration();
1175 let inception = rrsig.data().sig_inception();
1176
1177 let Ok(dnskey_key_tag) = dnskey.data().calculate_key_tag() else {
1178 return RrsigValidity::WrongDnskey;
1179 };
1180
1181 if !(
1182 rrsig.name() == rrset.name() &&
1184 rrsig.dns_class() == rrset.record_class() &&
1185
1186 rrsig.data().type_covered() == rrset.record_type() &&
1191
1192 rrset.name().num_labels() >= rrsig.data().num_labels()
1195 ) {
1196 return RrsigValidity::WrongRrsig;
1197 }
1198
1199 if !(
1202 current_time <= expiration &&
1205
1206 current_time >= inception
1209 ) {
1210 return RrsigValidity::ExpiredRrsig;
1211 }
1212
1213 if !(
1214 rrsig.data().signer_name() == dnskey.name() &&
1217 rrsig.data().algorithm() == dnskey.data().algorithm() &&
1218 rrsig.data().key_tag() == dnskey_key_tag &&
1219
1220 dnskey.data().zone_key()
1223 ) {
1224 return RrsigValidity::WrongDnskey;
1225 }
1226
1227 RrsigValidity::ValidRrsig
1228}
1229
1230#[derive(Clone, Copy, Debug)]
1231enum RrsigValidity {
1232 ExpiredRrsig,
1234 ValidRrsig,
1236 WrongDnskey,
1238 WrongRrsig,
1240}
1241
1242#[allow(clippy::blocks_in_conditions)]
1295#[doc(hidden)]
1296pub fn verify_nsec(query: &Query, soa_name: &Name, nsecs: &[(&Name, &NSEC)]) -> Proof {
1297 if let Some((_, nsec_data)) = nsecs.iter().find(|(name, _)| query.name() == *name) {
1304 if !nsec_data.type_set().contains(query.query_type()) {
1305 return proof_log_yield(Proof::Secure, query.name(), "nsec1", "direct match");
1306 } else {
1307 return proof_log_yield(Proof::Bogus, query.name(), "nsec1", "direct match");
1308 }
1309 }
1310
1311 let verify_nsec_coverage = |query_name: &Name| -> bool {
1312 nsecs.iter().any(|(nsec_name, nsec_data)| {
1313 query_name >= nsec_name && {
1315 query_name < nsec_data.next_domain_name()
1318 || nsec_data.next_domain_name() < nsec_name
1319 }
1320 })
1321 };
1322
1323 if !verify_nsec_coverage(query.name()) {
1325 return proof_log_yield(Proof::Bogus, query.name(), "nsec1", "no wildcard");
1326 }
1327
1328 let wildcard = query.name().base_name();
1332 let wildcard = if soa_name.zone_of(&wildcard) {
1333 wildcard
1334 } else {
1335 soa_name.clone()
1336 };
1337
1338 if wildcard == *query.name() {
1340 proof_log_yield(
1342 Proof::Secure,
1343 query.name(),
1344 "nsec1",
1345 "direct wildcard match",
1346 )
1347 } else {
1348 if verify_nsec_coverage(&wildcard) {
1351 proof_log_yield(
1352 Proof::Secure,
1353 query.name(),
1354 "nsec1",
1355 "covering wildcard match",
1356 )
1357 } else {
1358 proof_log_yield(
1359 Proof::Bogus,
1360 query.name(),
1361 "nsec1",
1362 "covering wildcard match",
1363 )
1364 }
1365 }
1366}
1367
1368fn current_time() -> u32 {
1370 SystemTime::now()
1371 .duration_since(UNIX_EPOCH)
1372 .unwrap_or_default()
1373 .as_secs() as u32
1374}
1375
1376fn proof_log_yield(proof: Proof, name: &Name, nsec_type: &str, msg: &str) -> Proof {
1378 debug!("{nsec_type} proof for {name}, returning {proof}: {msg}");
1379 proof
1380}
1381
1382mod rrset {
1383 use alloc::vec::Vec;
1384
1385 use crate::rr::{DNSClass, Name, Record, RecordType};
1386
1387 #[derive(Debug)]
1389 pub(super) struct Rrset<'r> {
1390 name: Name,
1391 record_class: DNSClass,
1392 record_type: RecordType,
1393 records: Vec<&'r Record>,
1394 }
1395
1396 impl<'r> Rrset<'r> {
1397 pub(super) fn new(record: &'r Record) -> Self {
1398 Self {
1399 name: record.name().clone(),
1400 record_class: record.dns_class(),
1401 record_type: record.record_type(),
1402 records: vec![record],
1403 }
1404 }
1405
1406 pub(super) fn add(&mut self, record: &'r Record) {
1408 if self.name == *record.name()
1409 && self.record_type == record.record_type()
1410 && self.record_class == record.dns_class()
1411 {
1412 self.records.push(record);
1413 }
1414 }
1415
1416 pub(super) fn record(&self) -> &Record {
1418 self.records[0]
1419 }
1420
1421 pub(super) fn name(&self) -> &Name {
1422 &self.name
1423 }
1424
1425 pub(super) fn record_class(&self) -> DNSClass {
1426 self.record_class
1427 }
1428
1429 pub(super) fn record_type(&self) -> RecordType {
1430 self.record_type
1431 }
1432
1433 pub(super) fn records(&self) -> &[&Record] {
1434 &self.records
1435 }
1436 }
1437}
1438
1439const MAX_KEY_TAG_COLLISIONS: usize = 2;
1446
1447const MAX_RRSIGS_PER_RRSET: usize = 8;