hickory_proto/xfer/
dnssec_dns_handle.rs

1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! The `DnssecDnsHandle` is used to validate all DNS responses for correct DNSSEC signatures.
9
10use std::{clone::Clone, collections::HashSet, error::Error, pin::Pin, sync::Arc};
11
12use futures_util::{
13    future::{self, Future, FutureExt, TryFutureExt},
14    stream::{self, Stream, TryStreamExt},
15};
16use tracing::{debug, trace};
17
18use crate::{
19    error::{ProtoError, ProtoErrorKind, ProtoResult},
20    op::{Edns, OpCode, Query},
21    rr::{
22        dnssec::{
23            rdata::{DNSSECRData, DNSKEY, RRSIG},
24            Algorithm, SupportedAlgorithms, TrustAnchor,
25        },
26        rdata::opt::EdnsOption,
27        DNSClass, Name, RData, Record, RecordData, RecordType,
28    },
29    xfer::{dns_handle::DnsHandle, DnsRequest, DnsRequestOptions, DnsResponse, FirstAnswer},
30};
31
32#[cfg(feature = "dnssec")]
33use crate::rr::dnssec::Verifier;
34
35#[derive(Debug)]
36struct Rrset {
37    pub(crate) name: Name,
38    pub(crate) record_type: RecordType,
39    pub(crate) record_class: DNSClass,
40    pub(crate) records: Vec<Record>,
41}
42
43/// Performs DNSSEC validation of all DNS responses from the wrapped DnsHandle
44///
45/// This wraps a DnsHandle, changing the implementation `send()` to validate all
46///  message responses for Query operations. Update operation responses are not validated by
47///  this process.
48#[derive(Clone)]
49#[must_use = "queries can only be sent through a DnsHandle"]
50pub struct DnssecDnsHandle<H>
51where
52    H: DnsHandle + Unpin + 'static,
53{
54    handle: H,
55    trust_anchor: Arc<TrustAnchor>,
56    request_depth: usize,
57    minimum_key_len: usize,
58    minimum_algorithm: Algorithm, // used to prevent down grade attacks...
59}
60
61impl<H> DnssecDnsHandle<H>
62where
63    H: DnsHandle + Unpin + 'static,
64{
65    /// Create a new DnssecDnsHandle wrapping the specified handle.
66    ///
67    /// This uses the compiled in TrustAnchor default trusted keys.
68    ///
69    /// # Arguments
70    /// * `handle` - handle to use for all connections to a remote server.
71    pub fn new(handle: H) -> Self {
72        Self::with_trust_anchor(handle, TrustAnchor::default())
73    }
74
75    /// Create a new DnssecDnsHandle wrapping the specified handle.
76    ///
77    /// This allows a custom TrustAnchor to be define.
78    ///
79    /// # Arguments
80    /// * `handle` - handle to use for all connections to a remote server.
81    /// * `trust_anchor` - custom DNSKEYs that will be trusted, can be used to pin trusted keys.
82    pub fn with_trust_anchor(handle: H, trust_anchor: TrustAnchor) -> Self {
83        Self {
84            handle,
85            trust_anchor: Arc::new(trust_anchor),
86            request_depth: 0,
87            minimum_key_len: 0,
88            minimum_algorithm: Algorithm::RSASHA256,
89        }
90    }
91
92    /// An internal function used to clone the handle, but maintain some information back to the
93    ///  original handle, such as the request_depth such that infinite recursion does
94    ///  not occur.
95    fn clone_with_context(&self) -> Self {
96        Self {
97            handle: self.handle.clone(),
98            trust_anchor: Arc::clone(&self.trust_anchor),
99            request_depth: self.request_depth + 1,
100            minimum_key_len: self.minimum_key_len,
101            minimum_algorithm: self.minimum_algorithm,
102        }
103    }
104}
105
106impl<H> DnsHandle for DnssecDnsHandle<H>
107where
108    H: DnsHandle + Sync + Unpin,
109{
110    type Response = Pin<Box<dyn Stream<Item = Result<DnsResponse, Self::Error>> + Send>>;
111    type Error = <H as DnsHandle>::Error;
112
113    fn is_verifying_dnssec(&self) -> bool {
114        // This handler is always verifying...
115        true
116    }
117
118    fn send<R: Into<DnsRequest>>(&self, request: R) -> Self::Response {
119        let mut request = request.into();
120
121        // backstop
122        if self.request_depth > request.options().max_request_depth {
123            return Box::pin(stream::once(future::err(Self::Error::from(
124                ProtoError::from("exceeded max validation depth"),
125            ))));
126        }
127
128        // dnssec only matters on queries.
129        if let OpCode::Query = request.op_code() {
130            // This will panic on no queries, that is a very odd type of request, isn't it?
131            // TODO: with mDNS there can be multiple queries
132            let query = request
133                .queries()
134                .first()
135                .cloned()
136                .expect("no queries in request");
137            let handle: Self = self.clone_with_context();
138
139            // TODO: cache response of the server about understood algorithms
140            #[cfg(feature = "dnssec")]
141            {
142                let edns = request.extensions_mut().get_or_insert_with(Edns::new);
143                edns.set_dnssec_ok(true);
144
145                // send along the algorithms which are supported by this handle
146                let mut algorithms = SupportedAlgorithms::new();
147                #[cfg(feature = "ring")]
148                {
149                    algorithms.set(Algorithm::ED25519);
150                }
151                algorithms.set(Algorithm::ECDSAP256SHA256);
152                algorithms.set(Algorithm::ECDSAP384SHA384);
153                algorithms.set(Algorithm::RSASHA256);
154
155                let dau = EdnsOption::DAU(algorithms);
156                let dhu = EdnsOption::DHU(algorithms);
157
158                edns.options_mut().insert(dau);
159                edns.options_mut().insert(dhu);
160            }
161
162            request.set_authentic_data(true);
163            request.set_checking_disabled(false);
164            let dns_class = request
165                .queries()
166                .first()
167                .map_or(DNSClass::IN, Query::query_class);
168            let options = *request.options();
169
170            return Box::pin(
171                self.handle
172                    .send(request)
173                    .and_then(move |message_response| {
174                        // group the record sets by name and type
175                        //  each rrset type needs to validated independently
176                        debug!(
177                            "validating message_response: {}, with {} trust_anchors",
178                            message_response.id(),
179                            handle.trust_anchor.len(),
180                        );
181                        verify_rrsets(handle.clone(), message_response, dns_class, options)
182                    })
183                    .and_then(move |verified_message| {
184                        // at this point all of the message is verified.
185                        //  This is where NSEC (and possibly NSEC3) validation occurs
186                        // As of now, only NSEC is supported.
187                        if verified_message.answers().is_empty() {
188                            // get SOA name
189                            let soa_name = if let Some(soa_name) = verified_message
190                                .name_servers()
191                                .iter()
192                                // there should only be one
193                                .find(|rr| rr.record_type() == RecordType::SOA)
194                                .map(Record::name)
195                            {
196                                soa_name
197                            } else {
198                                return future::err(Self::Error::from(ProtoError::from(
199                                    "could not validate negative response missing SOA",
200                                )));
201                            };
202
203                            let nsecs = verified_message
204                                .name_servers()
205                                .iter()
206                                .filter(|rr| is_dnssec(rr, RecordType::NSEC))
207                                .collect::<Vec<_>>();
208
209                            if !verify_nsec(&query, soa_name, nsecs.as_slice()) {
210                                // TODO change this to remove the NSECs, like we do for the others?
211                                return future::err(Self::Error::from(ProtoError::from(
212                                    "could not validate negative response with NSEC",
213                                )));
214                            }
215                        }
216
217                        future::ok(verified_message)
218                    }),
219            );
220        }
221
222        Box::pin(self.handle.send(request))
223    }
224}
225
226/// this pulls all records returned in a Message response and returns a future which will
227///  validate all of them.
228#[allow(clippy::type_complexity)]
229async fn verify_rrsets<H, E>(
230    handle: DnssecDnsHandle<H>,
231    message_result: DnsResponse,
232    dns_class: DNSClass,
233    options: DnsRequestOptions,
234) -> Result<DnsResponse, E>
235where
236    H: DnsHandle<Error = E> + Sync + Unpin,
237    E: From<ProtoError> + Error + Clone + Send + Unpin + 'static,
238{
239    let mut rrset_types: HashSet<(Name, RecordType)> = HashSet::new();
240    for rrset in message_result
241        .answers()
242        .iter()
243        .chain(message_result.name_servers())
244        .filter(|rr| {
245            !is_dnssec(rr, RecordType::RRSIG) &&
246                             // if we are at a depth greater than 1, we are only interested in proving evaluation chains
247                             //   this means that only DNSKEY and DS are interesting at that point.
248                             //   this protects against looping over things like NS records and DNSKEYs in responses.
249                             // TODO: is there a cleaner way to prevent cycles in the evaluations?
250                                          (handle.request_depth <= 1 ||
251                                           is_dnssec(rr, RecordType::DNSKEY) ||
252                                           is_dnssec(rr, RecordType::DS))
253        })
254        .map(|rr| (rr.name().clone(), rr.record_type()))
255    {
256        rrset_types.insert(rrset);
257    }
258
259    // there was no data returned in that message
260    if rrset_types.is_empty() {
261        let mut message_result = message_result.into_message();
262
263        // there were no returned results, double check by dropping all the results
264        message_result.take_answers();
265        message_result.take_name_servers();
266        message_result.take_additionals();
267
268        return Err(E::from(ProtoError::from(ProtoErrorKind::Message(
269            "no results to verify",
270        ))));
271    }
272
273    // collect all the rrsets to verify
274    // TODO: is there a way to get rid of this clone() safely?
275    let mut rrsets_to_verify = Vec::with_capacity(rrset_types.len());
276    for (name, record_type) in rrset_types {
277        // TODO: should we evaluate the different sections (answers and name_servers) separately?
278        let records: Vec<Record> = message_result
279            .answers()
280            .iter()
281            .chain(message_result.name_servers())
282            .chain(message_result.additionals())
283            .filter(|rr| rr.record_type() == record_type && rr.name() == &name)
284            .cloned()
285            .collect();
286
287        let rrsigs: Vec<Record<RRSIG>> = message_result
288            .answers()
289            .iter()
290            .chain(message_result.name_servers())
291            .chain(message_result.additionals())
292            .filter(|rr| is_dnssec(rr, RecordType::RRSIG))
293            .filter(|rr| {
294                if let Some(RData::DNSSEC(DNSSECRData::RRSIG(ref rrsig))) = rr.data() {
295                    rrsig.type_covered() == record_type
296                } else {
297                    false
298                }
299            })
300            .cloned()
301            .map(|rr| Record::<RRSIG>::try_from(rr).expect("the record type was checked above"))
302            .collect();
303
304        // if there is already an active validation going on, assume the other validation will
305        //  complete properly or error if it is invalid
306        let rrset = Rrset {
307            name,
308            record_type,
309            record_class: dns_class,
310            records,
311        };
312
313        // TODO: support non-IN classes?
314        debug!(
315            "verifying: {}, record_type: {:?}, rrsigs: {}",
316            rrset.name,
317            record_type,
318            rrsigs.len()
319        );
320        rrsets_to_verify
321            .push(verify_rrset(handle.clone_with_context(), rrset, rrsigs, options).boxed());
322    }
323
324    // spawn a select_all over this vec, these are the individual RRSet validators
325    verify_all_rrsets(message_result, rrsets_to_verify).await
326}
327
328// TODO: is this method useful/necessary?
329fn is_dnssec<D: RecordData>(rr: &Record<D>, dnssec_type: RecordType) -> bool {
330    rr.record_type().is_dnssec() && dnssec_type.is_dnssec() && rr.record_type() == dnssec_type
331}
332
333async fn verify_all_rrsets<F, E>(
334    message_result: DnsResponse,
335    rrsets: Vec<F>,
336) -> Result<DnsResponse, E>
337where
338    F: Future<Output = Result<Rrset, E>> + Send + Unpin,
339    E: From<ProtoError> + Error + Clone + Send + Unpin + 'static,
340{
341    let mut verified_rrsets: HashSet<(Name, RecordType)> = HashSet::new();
342    let mut rrsets = future::select_all(rrsets);
343    let mut last_validation_err: Option<E> = None;
344
345    // loop through all the rrset evaluations, filter all the rrsets in the Message
346    //  down to just the ones that were able to be validated
347    loop {
348        let (rrset, _, remaining) = rrsets.await;
349        match rrset {
350            Ok(rrset) => {
351                debug!(
352                    "an rrset was verified: {}, {:?}",
353                    rrset.name, rrset.record_type
354                );
355                verified_rrsets.insert((rrset.name, rrset.record_type));
356            }
357            // TODO: should we return the Message on errors? Allow the consumer to decide what to do
358            //       on a validation failure?
359            // any error, is an error for all
360            Err(e) => {
361                if tracing::enabled!(tracing::Level::DEBUG) {
362                    let mut query = message_result
363                        .queries()
364                        .iter()
365                        .map(|q| q.to_string())
366                        .fold(String::new(), |s, q| format!("{q},{s}"));
367
368                    query.truncate(query.len() - 1);
369                    debug!("an rrset failed to verify ({}): {:?}", query, e);
370                }
371
372                last_validation_err = Some(e);
373            }
374        };
375
376        if !remaining.is_empty() {
377            // continue the evaluation
378            rrsets = future::select_all(remaining);
379        } else {
380            break;
381        }
382    }
383
384    // check if any are valid, otherwise return whatever error caused it to fail
385    if verified_rrsets.is_empty() {
386        if let Some(last_validation_err) = last_validation_err {
387            return Err(last_validation_err);
388        }
389    }
390
391    // validated not none above...
392    let (mut message_result, message_buffer) = message_result.into_parts();
393
394    // take all the rrsets from the Message, filter down each set to the validated rrsets
395    // TODO: does the section in the message matter here?
396    //       we could probably end up with record_types in any section.
397    //       track the section in the rrset evaluation?
398    let answers = message_result
399        .take_answers()
400        .into_iter()
401        .chain(message_result.take_additionals().into_iter())
402        .filter(|record| verified_rrsets.contains(&(record.name().clone(), record.record_type())))
403        .collect::<Vec<Record>>();
404
405    let name_servers = message_result
406        .take_name_servers()
407        .into_iter()
408        .filter(|record| verified_rrsets.contains(&(record.name().clone(), record.record_type())))
409        .collect::<Vec<Record>>();
410
411    let additionals = message_result
412        .take_additionals()
413        .into_iter()
414        .filter(|record| verified_rrsets.contains(&(record.name().clone(), record.record_type())))
415        .collect::<Vec<Record>>();
416
417    // add the filtered records back to the message
418    message_result.insert_answers(answers);
419    message_result.insert_name_servers(name_servers);
420    message_result.insert_additionals(additionals);
421
422    // breaks out of the loop... and returns the filtered Message.
423    Ok(DnsResponse::new(message_result, message_buffer))
424}
425
426/// Generic entrypoint to verify any RRset against the provided signatures.
427///
428/// Generally, the RRset will be validated by `verify_default_rrset()`. In the case of DNSKEYs, the
429/// RRset will be validated by `verify_dnskey_rrset()`. If it's an NSEC record, then the NSEC
430/// record will be validated to prove it's correctness.
431async fn verify_rrset<H, E>(
432    handle: DnssecDnsHandle<H>,
433    rrset: Rrset,
434    rrsigs: Vec<Record<RRSIG>>,
435    options: DnsRequestOptions,
436) -> Result<Rrset, E>
437where
438    H: DnsHandle<Error = E> + Sync + Unpin,
439    E: From<ProtoError> + Error + Clone + Send + Unpin + 'static,
440{
441    match rrset.record_type {
442        RecordType::DNSKEY => verify_dnskey_rrset(handle, rrset, rrsigs, options).await,
443        _ => verify_default_rrset(&handle.clone_with_context(), rrset, rrsigs, options).await,
444    }
445}
446
447/// Verifies a DNSKEY RRset
448///
449/// This first checks to see if any key is in the set of trust_anchors. If not, a query is sent to
450/// get the DS record, and each DNSKEY is validated against the DS record. Then, the DNSKEY RRset is
451/// validated using signatures made by authenticated keys.
452async fn verify_dnskey_rrset<H, E>(
453    handle: DnssecDnsHandle<H>,
454    rrset: Rrset,
455    rrsigs: Vec<Record<RRSIG>>,
456    options: DnsRequestOptions,
457) -> Result<Rrset, E>
458where
459    H: DnsHandle<Error = E> + Sync + Unpin,
460    E: From<ProtoError> + Error + Clone + Send + Unpin + 'static,
461{
462    trace!(
463        "dnskey validation {}, record_type: {:?}",
464        rrset.name,
465        rrset.record_type
466    );
467
468    // check the DNSKEYS against the trust_anchor, if it's approved allow it.
469    {
470        let anchored_keys = rrset
471            .records
472            .iter()
473            .enumerate()
474            .filter(|&(_, rr)| is_dnssec(rr, RecordType::DNSKEY))
475            .filter_map(|(i, rr)| rr.data().map(|rr| (i, rr)))
476            .filter_map(|(i, rr)| DNSKEY::try_borrow(rr).map(|rr| (i, rr)))
477            .filter_map(|(i, rdata)| {
478                if handle
479                    .trust_anchor
480                    .contains_dnskey_bytes(rdata.public_key())
481                {
482                    debug!(
483                        "validated dnskey with trust_anchor: {}, {}",
484                        rrset.name, rdata
485                    );
486
487                    Some(i)
488                } else {
489                    None
490                }
491            })
492            .collect::<Vec<usize>>();
493
494        // Verify the self-signature over the DNSKEY RRset.
495        for dnskey_index in anchored_keys.iter().copied() {
496            let dnskey_record = &rrset.records[dnskey_index];
497            let Some(RData::DNSSEC(DNSSECRData::DNSKEY(dnskey))) = dnskey_record.data() else {
498                continue;
499            };
500            for rrsig_record in rrsigs.iter() {
501                let Some(rrsig) = rrsig_record.data() else {
502                    continue;
503                };
504                let verify_result = verify_rrset_with_dnskey(&rrset.name, dnskey, rrsig, &rrset);
505                if verify_result.is_ok() {
506                    return Ok(rrset);
507                }
508            }
509        }
510
511        if anchored_keys.len() == rrset.records.len() {
512            // Special case: allow a zone consisting of only trust anchor keys without a
513            // self-signature.
514            return Ok(rrset);
515        }
516    }
517
518    // need to get DS records for each DNSKEY
519    let ds_message = handle
520        .lookup(Query::query(rrset.name.clone(), RecordType::DS), options)
521        .first_answer()
522        .await?;
523    let valid_keys = rrset
524        .records
525        .iter()
526        .enumerate()
527        .filter(|&(_, rr)| is_dnssec(rr, RecordType::DNSKEY))
528        .filter_map(|(i, rr)| {
529            if let Some(RData::DNSSEC(DNSSECRData::DNSKEY(ref rdata))) = rr.data() {
530                Some((i, rdata))
531            } else {
532                None
533            }
534        })
535        .filter(|&(_, key_rdata)| {
536            ds_message
537                .answers()
538                .iter()
539                .filter(|ds| is_dnssec(ds, RecordType::DS))
540                .filter_map(|ds| {
541                    if let Some(RData::DNSSEC(DNSSECRData::DS(ref ds_rdata))) = ds.data() {
542                        Some((ds.name(), ds_rdata))
543                    } else {
544                        None
545                    }
546                })
547                // must be covered by at least one DS record
548                .any(|(ds_name, ds_rdata)| {
549                    if ds_rdata.covers(&rrset.name, key_rdata).unwrap_or(false) {
550                        debug!(
551                            "validated dnskey ({}, {}) with {} {}",
552                            rrset.name, key_rdata, ds_name, ds_rdata
553                        );
554
555                        true
556                    } else {
557                        false
558                    }
559                })
560        })
561        .map(|(i, _)| i)
562        .collect::<Vec<usize>>();
563
564    if !valid_keys.is_empty() {
565        trace!("validated dnskey: {}", rrset.name);
566    }
567
568    for dnskey_index in valid_keys {
569        let dnskey_record = &rrset.records[dnskey_index];
570        let Some(RData::DNSSEC(DNSSECRData::DNSKEY(dnskey))) = dnskey_record.data() else {
571            continue;
572        };
573        for rrsig_record in rrsigs.iter() {
574            let Some(rrsig) = rrsig_record.data() else {
575                continue;
576            };
577            let verify_result = verify_rrset_with_dnskey(&rrset.name, dnskey, rrsig, &rrset);
578            if verify_result.is_ok() {
579                return Ok(rrset);
580            }
581        }
582    }
583
584    Err(E::from(ProtoError::from(ProtoErrorKind::Message(
585        "Could not validate all DNSKEYs",
586    ))))
587}
588
589/// Verifies that a given RRSET is validly signed by any of the specified RRSIGs.
590///
591/// Invalid RRSIGs will be ignored. RRSIGs will only be validated against DNSKEYs which can
592///  be validated through a chain back to the `trust_anchor`. As long as one RRSIG is valid,
593///  then the RRSET will be valid.
594#[allow(clippy::blocks_in_conditions)]
595async fn verify_default_rrset<H, E>(
596    handle: &DnssecDnsHandle<H>,
597    rrset: Rrset,
598    rrsigs: Vec<Record<RRSIG>>,
599    options: DnsRequestOptions,
600) -> Result<Rrset, E>
601where
602    H: DnsHandle<Error = E> + Sync + Unpin,
603    E: From<ProtoError> + Error + Clone + Send + Unpin + 'static,
604{
605    // the record set is going to be shared across a bunch of futures, Arc for that.
606    let rrset = Arc::new(rrset);
607    trace!(
608        "default validation {}, record_type: {:?}",
609        rrset.name,
610        rrset.record_type
611    );
612
613    // we can validate with any of the rrsigs...
614    //  i.e. the first that validates is good enough
615    //  TODO: could there be a cert downgrade attack here with a MITM stripping stronger RRSIGs?
616    //         we could check for the strongest RRSIG and only use that...
617    //         though, since the entire package isn't signed any RRSIG could have been injected,
618    //         right? meaning if there is an attack on any of the acceptable algorithms, we'd be
619    //         susceptible until that algorithm is removed as an option.
620    //        dns over TLS will mitigate this.
621    //  TODO: strip RRSIGS to accepted algorithms and make algorithms configurable.
622    let verifications = rrsigs.into_iter()
623        // this filter is technically unnecessary, can probably remove it...
624        .filter(|rrsig| is_dnssec(rrsig, RecordType::RRSIG))
625        .filter_map(|rrsig|rrsig.into_data())
626        .map(|sig| {
627            let rrset = Arc::clone(&rrset);
628            let handle = handle.clone_with_context();
629
630            handle
631                .lookup(
632                    Query::query(sig.signer_name().clone(), RecordType::DNSKEY),
633                    options,
634                )
635                .first_answer()
636                .and_then(move |message|
637                    // DNSKEYs are validated by the inner query
638                    future::ready(message
639                        .answers()
640                        .iter()
641                        .filter(|r| is_dnssec(r, RecordType::DNSKEY))
642                        .filter_map(|r| r.data().map(|data| (r.name(), data)))
643                        .filter_map(|(dnskey_name, data)|
644                           DNSKEY::try_borrow(data).map(|data| (dnskey_name, data)))
645                        .find(|(dnskey_name, dnskey)|
646                                verify_rrset_with_dnskey(dnskey_name, dnskey, &sig, &rrset).is_ok()
647                        )
648                        .map(|_| ())
649                        .ok_or_else(|| E::from(ProtoError::from(ProtoErrorKind::Message("validation failed")))))
650                )
651        })
652        .collect::<Vec<_>>();
653
654    // if there are no available verifications, then we are in a failed state.
655    if verifications.is_empty() {
656        return Err(E::from(ProtoError::from(
657            ProtoErrorKind::RrsigsNotPresent {
658                name: rrset.name.clone(),
659                record_type: rrset.record_type,
660            },
661        )));
662    }
663
664    // as long as any of the verifications is good, then the RRSET is valid.
665    let select = future::select_ok(verifications)
666        // getting here means at least one of the rrsigs succeeded...
667        .map_ok(move |((), rest)| {
668            drop(rest); // drop all others, should free up Arc
669            Arc::try_unwrap(rrset).expect("unable to unwrap Arc")
670        });
671
672    select.await
673}
674
675/// Verifies the given SIG of the RRSET with the DNSKEY.
676#[cfg(feature = "dnssec")]
677fn verify_rrset_with_dnskey(
678    dnskey_name: &Name,
679    dnskey: &DNSKEY,
680    sig: &RRSIG,
681    rrset: &Rrset,
682) -> ProtoResult<()> {
683    if dnskey.revoke() {
684        debug!("revoked");
685        return Err(ProtoErrorKind::Message("revoked").into());
686    } // TODO: does this need to be validated? RFC 5011
687    if !dnskey.zone_key() {
688        return Err(ProtoErrorKind::Message("is not a zone key").into());
689    }
690    if dnskey.algorithm() != sig.algorithm() {
691        return Err(ProtoErrorKind::Message("mismatched algorithm").into());
692    }
693
694    dnskey
695        .verify_rrsig(&rrset.name, rrset.record_class, sig, &rrset.records)
696        .map(|r| {
697            debug!(
698                "validated ({}, {:?}) with ({}, {})",
699                rrset.name, rrset.record_type, dnskey_name, dnskey
700            );
701            r
702        })
703        .map_err(Into::into)
704        .map_err(|e| {
705            debug!(
706                "failed validation of ({}, {:?}) with ({}, {})",
707                rrset.name, rrset.record_type, dnskey_name, dnskey
708            );
709            e
710        })
711}
712
713/// Will always return an error. To enable record verification compile with the openssl feature.
714#[cfg(not(feature = "dnssec"))]
715fn verify_rrset_with_dnskey(_: &DNSKEY, _: &RRSIG, _: &Rrset) -> ProtoResult<()> {
716    Err(ProtoErrorKind::Message("openssl or ring feature(s) not enabled").into())
717}
718
719/// Verifies NSEC records
720///
721/// ```text
722/// RFC 4035             DNSSEC Protocol Modifications            March 2005
723///
724/// 5.4.  Authenticated Denial of Existence
725///
726///  A resolver can use authenticated NSEC RRs to prove that an RRset is
727///  not present in a signed zone.  Security-aware name servers should
728///  automatically include any necessary NSEC RRs for signed zones in
729///  their responses to security-aware resolvers.
730///
731///  Denial of existence is determined by the following rules:
732///
733///  o  If the requested RR name matches the owner name of an
734///     authenticated NSEC RR, then the NSEC RR's type bit map field lists
735///     all RR types present at that owner name, and a resolver can prove
736///     that the requested RR type does not exist by checking for the RR
737///     type in the bit map.  If the number of labels in an authenticated
738///     NSEC RR's owner name equals the Labels field of the covering RRSIG
739///     RR, then the existence of the NSEC RR proves that wildcard
740///     expansion could not have been used to match the request.
741///
742///  o  If the requested RR name would appear after an authenticated NSEC
743///     RR's owner name and before the name listed in that NSEC RR's Next
744///     Domain Name field according to the canonical DNS name order
745///     defined in [RFC4034], then no RRsets with the requested name exist
746///     in the zone.  However, it is possible that a wildcard could be
747///     used to match the requested RR owner name and type, so proving
748///     that the requested RRset does not exist also requires proving that
749///     no possible wildcard RRset exists that could have been used to
750///     generate a positive response.
751///
752///  In addition, security-aware resolvers MUST authenticate the NSEC
753///  RRsets that comprise the non-existence proof as described in Section
754///  5.3.
755///
756///  To prove the non-existence of an RRset, the resolver must be able to
757///  verify both that the queried RRset does not exist and that no
758///  relevant wildcard RRset exists.  Proving this may require more than
759///  one NSEC RRset from the zone.  If the complete set of necessary NSEC
760///  RRsets is not present in a response (perhaps due to message
761///  truncation), then a security-aware resolver MUST resend the query in
762///  order to attempt to obtain the full collection of NSEC RRs necessary
763///  to verify the non-existence of the requested RRset.  As with all DNS
764///  operations, however, the resolver MUST bound the work it puts into
765///  answering any particular query.
766///
767///  Since a validated NSEC RR proves the existence of both itself and its
768///  corresponding RRSIG RR, a validator MUST ignore the settings of the
769///  NSEC and RRSIG bits in an NSEC RR.
770/// ```
771#[allow(clippy::blocks_in_conditions)]
772#[doc(hidden)]
773pub fn verify_nsec(query: &Query, soa_name: &Name, nsecs: &[&Record]) -> bool {
774    // TODO: consider converting this to Result, and giving explicit reason for the failure
775
776    // first look for a record with the same name
777    //  if they are, then the query_type should not exist in the NSEC record.
778    //  if we got an NSEC record of the same name, but it is listed in the NSEC types,
779    //    WTF? is that bad server, bad record
780    if let Some(nsec) = nsecs.iter().find(|nsec| query.name() == nsec.name()) {
781        return nsec
782            .data()
783            .and_then(RData::as_dnssec)
784            .and_then(DNSSECRData::as_nsec)
785            .is_some_and(|rdata| {
786                // this should not be in the covered list
787                !rdata.type_bit_maps().contains(&query.query_type())
788            });
789    }
790
791    let verify_nsec_coverage = |name: &Name| -> bool {
792        nsecs.iter().any(|nsec| {
793            // the query name must be greater than nsec's label (or equal in the case of wildcard)
794            name >= nsec.name() && {
795                nsec.data()
796                    .and_then(RData::as_dnssec)
797                    .and_then(DNSSECRData::as_nsec)
798                    .is_some_and(|rdata| {
799                        // the query name is less than the next name
800                        // or this record wraps the end, i.e. is the last record
801                        name < rdata.next_domain_name() || rdata.next_domain_name() < nsec.name()
802                    })
803            }
804        })
805    };
806
807    if !verify_nsec_coverage(query.name()) {
808        // continue to validate there is no wildcard
809        return false;
810    }
811
812    // validate ANY or *.domain record existence
813
814    // we need the wildcard proof, but make sure that it's still part of the zone.
815    let wildcard = query.name().base_name();
816    let wildcard = if soa_name.zone_of(&wildcard) {
817        wildcard
818    } else {
819        soa_name.clone()
820    };
821
822    // don't need to validate the same name again
823    if wildcard == *query.name() {
824        // this was validated by the nsec coverage over the query.name()
825        true
826    } else {
827        // this is the final check, return it's value
828        //  if there is wildcard coverage, we're good.
829        verify_nsec_coverage(&wildcard)
830    }
831}