use super::anchor::{TrustAnchor, TrustAnchors};
use super::base::{supported_algorithm, supported_digest, DnskeyExt};
use super::group::{Group, GroupSet, SigCache, ValidatedGroup};
use super::nsec::{
cached_nsec3_hash, nsec3_for_nodata, nsec3_for_nodata_wildcard,
nsec3_for_nxdomain, nsec3_in_range, nsec3_label_to_hash, nsec_for_nodata,
nsec_for_nodata_wildcard, nsec_for_nxdomain, nsec_in_range,
supported_nsec3_hash,
};
use super::nsec::{
Nsec3Cache, Nsec3NXState, Nsec3State, NsecNXState, NsecState,
};
use super::utilities::{
check_not_exists_for_wildcard, do_cname_dname, get_answer_state,
get_soa_state, make_ede, map_maybe_secure, rebuild_msg,
star_closest_encloser, ttl_for_sig,
};
use crate::base::iana::{ExtendedErrorCode, OptRcode};
use crate::base::message::ShortMessage;
use crate::base::name::{Chain, Label};
use crate::base::opt::ExtendedError;
use crate::base::{name, wire};
use crate::base::{
Message, MessageBuilder, Name, ParsedName, Record, RelativeName, Rtype,
ToName,
};
use crate::dep::octseq::{Octets, OctetsFrom, OctetsInto};
use crate::net::client::request::{
ComposeRequest, RequestMessage, SendRequest,
};
use crate::rdata::{AllRecordData, Dnskey, Ds, ZoneRecordData};
use crate::utils::config::DefMinMax;
use crate::zonefile::inplace;
use bytes::Bytes;
use moka::future::Cache;
use std::cmp::min;
use std::collections::VecDeque;
use std::fmt::Debug;
use std::string::ToString;
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::vec::Vec;
use std::{error, fmt};
const MAX_NODE_CACHE: DefMinMax<u64> = DefMinMax::new(100, 1, 1_000_000_000);
const MAX_NSEC3_CACHE: DefMinMax<u64> = DefMinMax::new(100, 1, 1_000_000_000);
const MAX_ISIG_CACHE: DefMinMax<u64> = DefMinMax::new(1000, 1, 1_000_000_000);
const MAX_USIG_CACHE: DefMinMax<u64> = DefMinMax::new(1000, 1, 1_000_000_000);
const MAX_NODE_VALIDITY: DefMinMax<Duration> = DefMinMax::new(
Duration::from_secs(604800),
Duration::from_secs(60),
Duration::from_secs(6048000),
);
const MAX_BOGUS_VALIDITY: DefMinMax<Duration> = DefMinMax::new(
Duration::from_secs(30),
Duration::from_secs(1),
Duration::from_secs(5 * 60),
);
const MAX_BAD_SIGNATURES: DefMinMax<u8> = DefMinMax::new(1, 1, 8);
const NSEC3_ITER_INSECURE: DefMinMax<u16> = DefMinMax::new(100, 0, 500);
const NSEC3_ITER_BOGUS: DefMinMax<u16> = DefMinMax::new(500, 0, 500);
const MAX_CNAME_DNAME: DefMinMax<u8> = DefMinMax::new(11, 0, 100);
#[derive(Clone, Debug)]
pub struct Config {
max_node_cache: u64,
max_nsec3_cache: u64,
max_isig_cache: u64,
max_usig_cache: u64,
max_node_validity: Duration,
max_bogus_validity: Duration,
max_bad_signatures: u8,
nsec3_iter_insecure: u16,
nsec3_iter_bogus: u16,
max_cname_dname: u8,
}
impl Config {
pub fn new() -> Self {
Default::default()
}
pub fn set_max_node_cache(&mut self, value: u64) {
self.max_node_cache = MAX_NODE_CACHE.limit(value)
}
pub fn set_max_nsec3_cache(&mut self, value: u64) {
self.max_node_cache = MAX_NSEC3_CACHE.limit(value)
}
pub fn set_max_isig_cache(&mut self, value: u64) {
self.max_isig_cache = MAX_ISIG_CACHE.limit(value)
}
pub fn set_max_usig_cache(&mut self, value: u64) {
self.max_usig_cache = MAX_USIG_CACHE.limit(value)
}
pub fn set_max_validity(&mut self, value: Duration) {
self.max_node_validity = MAX_NODE_VALIDITY.limit(value)
}
pub(crate) fn max_bogus_validity(&self) -> Duration {
self.max_bogus_validity
}
pub fn set_max_bogus_validity(&mut self, value: Duration) {
self.max_bogus_validity = MAX_BOGUS_VALIDITY.limit(value)
}
pub(crate) fn max_bad_signatures(&self) -> u8 {
self.max_bad_signatures
}
pub fn set_bad_signatures(&mut self, value: u8) {
self.max_bad_signatures = MAX_BAD_SIGNATURES.limit(value)
}
pub(crate) fn nsec3_iter_insecure(&self) -> u16 {
self.nsec3_iter_insecure
}
pub fn set_nsec3_iter_insecure(&mut self, value: u16) {
self.nsec3_iter_insecure = NSEC3_ITER_INSECURE.limit(value)
}
pub(crate) fn nsec3_iter_bogus(&self) -> u16 {
self.nsec3_iter_bogus
}
pub fn set_nsec3_iter_bogus(&mut self, value: u16) {
self.nsec3_iter_bogus = NSEC3_ITER_INSECURE.limit(value)
}
pub(crate) fn max_cname_dname(&self) -> u8 {
self.max_cname_dname
}
pub fn set_max_cname_dname(&mut self, value: u8) {
self.max_cname_dname = MAX_CNAME_DNAME.limit(value)
}
}
impl Default for Config {
fn default() -> Self {
Self {
max_node_cache: MAX_NODE_CACHE.default(),
max_nsec3_cache: MAX_NSEC3_CACHE.default(),
max_isig_cache: MAX_ISIG_CACHE.default(),
max_usig_cache: MAX_USIG_CACHE.default(),
max_node_validity: MAX_NODE_VALIDITY.default(),
max_bogus_validity: MAX_BOGUS_VALIDITY.default(),
max_bad_signatures: MAX_BAD_SIGNATURES.default(),
nsec3_iter_insecure: NSEC3_ITER_INSECURE.default(),
nsec3_iter_bogus: NSEC3_ITER_BOGUS.default(),
max_cname_dname: MAX_CNAME_DNAME.default(),
}
}
}
pub struct ValidationContext<Upstream> {
ta: TrustAnchors,
upstream: Upstream,
config: Config,
node_cache: Cache<Name<Bytes>, Arc<Node>>,
nsec3_cache: Nsec3Cache,
isig_cache: SigCache,
usig_cache: SigCache,
}
impl<Upstream> ValidationContext<Upstream> {
pub fn new(ta: TrustAnchors, upstream: Upstream) -> Self {
Self::with_config(ta, upstream, Default::default())
}
pub fn with_config(
ta: TrustAnchors,
upstream: Upstream,
config: Config,
) -> Self {
Self {
ta,
upstream,
node_cache: Cache::new(config.max_node_cache),
nsec3_cache: Nsec3Cache::new(config.max_nsec3_cache),
isig_cache: SigCache::new(config.max_isig_cache),
usig_cache: SigCache::new(config.max_usig_cache),
config,
}
}
pub async fn validate_msg<'a, MsgOcts, USOcts>(
&self,
msg: &'a mut Message<MsgOcts>,
) -> Result<(ValidationState, Option<ExtendedError<Vec<u8>>>), Error>
where
MsgOcts: Clone + Debug + Octets + OctetsFrom<Vec<u8>> + 'a,
<MsgOcts as Octets>::Range<'a>: Debug,
USOcts:
AsRef<[u8]> + Debug + Octets + OctetsFrom<Vec<u8>> + Send + Sync,
Upstream: SendRequest<RequestMessage<USOcts>>,
{
let bytes = Bytes::copy_from_slice(msg.as_slice());
let bytes_msg = Message::from_octets(bytes)?;
let mut answers = GroupSet::new();
for rr in bytes_msg.answer()? {
answers.add(rr?)?;
}
let mut authorities = GroupSet::new();
for rr in bytes_msg.authority()? {
authorities.add(rr?)?;
}
answers.move_redundant_cnames();
let mut fix_reply = false;
let mut answers = match self.validate_groups(&mut answers).await {
VGResult::Groups(vgs, needs_fix) => {
fix_reply |= needs_fix;
vgs
}
VGResult::Bogus(ede) => return Ok((ValidationState::Bogus, ede)),
VGResult::Err(err) => return Err(err),
};
let mut authorities = match self
.validate_groups(&mut authorities)
.await
{
VGResult::Groups(vgs, needs_fix) => {
fix_reply |= needs_fix;
vgs
}
VGResult::Bogus(ede) => return Ok((ValidationState::Bogus, ede)),
VGResult::Err(err) => return Err(err),
};
if fix_reply {
*msg = rebuild_msg(&bytes_msg, &answers, &authorities)?;
}
let mut question_section = bytes_msg.question();
let question = match question_section.next() {
None => {
return Err(Error::FormError);
}
Some(question) => question?,
};
if question_section.next().is_some() {
return Err(Error::FormError);
}
let qname: Name<Bytes> = question.qname().to_name();
let qclass = question.qclass();
let qtype = question.qtype();
let maybe_secure = ValidationState::Secure;
let (sname, state, ede) = do_cname_dname(
qname,
qclass,
qtype,
&mut answers,
&mut authorities,
self.nsec3_cache(),
&self.config,
)
.await;
let maybe_secure = map_maybe_secure(state, maybe_secure);
if maybe_secure == ValidationState::Bogus {
return Ok((maybe_secure, ede));
}
if bytes_msg.opt_rcode() == OptRcode::NOERROR {
let opt_state =
get_answer_state(&sname, qclass, qtype, &mut answers);
if let Some((state, signer_name, closest_encloser, ede)) =
opt_state
{
if state != ValidationState::Secure {
return Ok((map_maybe_secure(state, maybe_secure), ede));
}
let closest_encloser = match closest_encloser {
None => {
return Ok((
map_maybe_secure(state, maybe_secure),
ede,
));
}
Some(ce) => ce,
};
let star_name = match star_closest_encloser(&closest_encloser)
{
Ok(name) => name,
Err(_) => {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"cannot create wildcard record",
);
return Ok((ValidationState::Bogus, ede));
}
};
if sname == star_name {
return Ok((map_maybe_secure(state, maybe_secure), ede));
}
let (check, state, ede) = check_not_exists_for_wildcard(
&sname,
&mut authorities,
&signer_name,
&closest_encloser,
self.nsec3_cache(),
&self.config,
)
.await;
if check {
return Ok((map_maybe_secure(state, maybe_secure), ede));
}
return Ok((ValidationState::Bogus, ede));
}
}
let signer_name =
match get_soa_state(&sname, qclass, &mut authorities) {
(None, ede) => {
let ede = match ede {
Some(ede) => Some(ede),
None => make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"Missing SOA record for NODATA or NXDOMAIN",
),
};
return Ok((ValidationState::Bogus, ede)); }
(Some((state, signer_name)), ede) => match state {
ValidationState::Secure => signer_name, ValidationState::Insecure
| ValidationState::Bogus
| ValidationState::Indeterminate => {
return Ok((state, ede));
}
},
};
if bytes_msg.opt_rcode() == OptRcode::NOERROR {
let (state, ede) = nsec_for_nodata(
&sname,
&mut authorities,
qtype,
&signer_name,
);
match state {
NsecState::NoData => {
return Ok((
map_maybe_secure(
ValidationState::Secure,
maybe_secure,
),
ede,
))
}
NsecState::Nothing => (), }
let (state, ede) = nsec_for_nodata_wildcard(
&sname,
&mut authorities,
qtype,
&signer_name,
);
match state {
NsecState::NoData => {
return Ok((
map_maybe_secure(
ValidationState::Secure,
maybe_secure,
),
ede,
))
}
NsecState::Nothing => (), }
let (state, ede) = nsec3_for_nodata(
&sname,
&mut authorities,
qtype,
&signer_name,
self.nsec3_cache(),
&self.config,
)
.await;
match state {
Nsec3State::NoData => {
return Ok((
map_maybe_secure(
ValidationState::Secure,
maybe_secure,
),
None,
))
}
Nsec3State::Nothing => (), Nsec3State::NoDataInsecure =>
{
return Ok((ValidationState::Insecure, ede))
}
Nsec3State::Bogus => {
return Ok((ValidationState::Bogus, ede))
}
}
let (state, ede) = nsec3_for_nodata_wildcard(
&sname,
&mut authorities,
qtype,
&signer_name,
self.nsec3_cache(),
&self.config,
)
.await;
match state {
Nsec3State::NoData => {
return Ok((
map_maybe_secure(
ValidationState::Secure,
maybe_secure,
),
None,
));
}
Nsec3State::Nothing =>
{
return Ok((ValidationState::Bogus, ede))
}
Nsec3State::NoDataInsecure => {
return Ok((ValidationState::Insecure, ede))
}
Nsec3State::Bogus => {
return Ok((ValidationState::Bogus, ede))
}
}
}
let (state, ede) =
nsec_for_nxdomain(&sname, &mut authorities, &signer_name);
match state {
NsecNXState::Exists => {
return Ok((ValidationState::Bogus, ede));
}
NsecNXState::DoesNotExist(_) => {
return Ok((
map_maybe_secure(ValidationState::Secure, maybe_secure),
ede,
))
}
NsecNXState::Nothing => (), }
let (state, mut ede) = nsec3_for_nxdomain(
&sname,
&mut authorities,
&signer_name,
self.nsec3_cache(),
&self.config,
)
.await;
match state {
Nsec3NXState::DoesNotExist(_) => {
return Ok((
map_maybe_secure(ValidationState::Secure, maybe_secure),
None,
))
}
Nsec3NXState::DoesNotExistInsecure(_) => {
return Ok((ValidationState::Insecure, ede));
}
Nsec3NXState::Bogus => return Ok((ValidationState::Bogus, ede)),
Nsec3NXState::Insecure => {
return Ok((ValidationState::Insecure, ede))
}
Nsec3NXState::Nothing => (), }
if ede.is_none() {
ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"No NEC/NSEC3 proof for non-existance",
);
}
Ok((ValidationState::Bogus, ede))
}
pub(crate) async fn get_node<Octs>(
&self,
name: &Name<Bytes>,
) -> Result<Arc<Node>, Error>
where
Octs:
AsRef<[u8]> + Debug + Octets + OctetsFrom<Vec<u8>> + Send + Sync,
Upstream: SendRequest<RequestMessage<Octs>>,
{
if let Some(node) = self.cache_lookup(name).await {
return Ok(node);
}
let Some(ta) = self.ta.find(name) else {
let node = Node::indeterminate(
Name::root(),
make_ede(
ExtendedErrorCode::DNSSEC_INDETERMINATE,
"No trust anchor for root.",
),
self.config.max_node_validity,
);
let node = Arc::new(node);
self.node_cache.insert(Name::root(), node.clone()).await;
return Ok(node);
};
let ta_owner = ta.owner();
if ta_owner.name_eq(name) {
let node = Node::trust_anchor(
ta,
&self.upstream,
&self.isig_cache,
&self.config,
)
.await?;
let node = Arc::new(node);
self.node_cache.insert(name.clone(), node.clone()).await;
return Ok(node);
}
let (mut node, mut names) =
self.find_closest_node(name, ta, ta_owner).await?;
let mut signer_node = node.clone();
loop {
match node.validation_state() {
ValidationState::Secure => (), ValidationState::Insecure | ValidationState::Bogus => {
return Ok(node)
}
ValidationState::Indeterminate => {
return Ok(node);
}
}
let child_name = match names.pop_front() {
Some(name) => name,
None => {
return Ok(node);
}
};
node = Arc::new(
self.create_child_node(child_name.clone(), &signer_node)
.await?,
);
self.node_cache.insert(child_name, node.clone()).await;
if !node.intermediate() {
signer_node = node.clone();
}
if names.is_empty() {
return Ok(node);
}
}
}
async fn find_closest_node<Octs>(
&self,
name: &Name<Bytes>,
ta: &TrustAnchor,
ta_owner: Name<Bytes>,
) -> Result<(Arc<Node>, VecDeque<Name<Bytes>>), Error>
where
Octs:
AsRef<[u8]> + Debug + Octets + OctetsFrom<Vec<u8>> + Send + Sync,
Upstream: SendRequest<RequestMessage<Octs>>,
{
let mut names = VecDeque::new();
names.push_front(name.clone());
let mut curr = name
.parent()
.expect("name has to be a decendent of ta_owner");
loop {
if ta_owner.name_eq(&curr) {
let node = Node::trust_anchor(
ta,
&self.upstream,
&self.isig_cache,
&self.config,
)
.await?;
let node = Arc::new(node);
self.node_cache.insert(curr, node.clone()).await;
return Ok((node, names));
}
if let Some(node) = self.cache_lookup(&curr).await {
return Ok((node, names));
}
names.push_front(curr.clone());
curr = curr
.parent()
.expect("curr has to be a decendent of ta_owner");
}
}
async fn create_child_node<Octs>(
&self,
name: Name<Bytes>,
node: &Node,
) -> Result<Node, Error>
where
Octs:
AsRef<[u8]> + Debug + Octets + OctetsFrom<Vec<u8>> + Send + Sync,
Upstream: SendRequest<RequestMessage<Octs>>,
{
let (mut answers, mut authorities, ede) =
request_as_groups(&self.upstream, &name, Rtype::DS).await?;
if ede.is_some() {
return Ok(Node::new_delegation(
name,
ValidationState::Bogus,
Vec::new(),
ede,
self.config.max_bogus_validity,
));
}
let parent_ttl = node.ttl();
let ds_group = match answers
.iter()
.find(|g| g.rtype() == Rtype::DS && g.owner() == name)
{
Some(g) => g,
None => {
for g in answers.iter().filter(|g| g.rtype() == Rtype::CNAME)
{
if g.owner() != name {
continue;
}
let g_ttl = g.min_ttl().into_duration();
let ttl = min(parent_ttl, g_ttl);
let (state, _wildcard, ede, sig_ttl, _) = g
.validate_with_node(
node,
&self.isig_cache,
&self.config,
)
.await;
let ttl = min(ttl, sig_ttl);
match state {
ValidationState::Secure => (),
ValidationState::Insecure
| ValidationState::Indeterminate => {
return Ok(Node::new_intermediate(
name,
ValidationState::Insecure,
node.signer_name().clone(),
ede,
ttl,
));
}
ValidationState::Bogus => {
return Ok(Node::new_delegation(
name,
ValidationState::Bogus,
Vec::new(),
ede,
ttl,
));
}
}
return Ok(Node::new_intermediate(
name,
ValidationState::Secure,
node.signer_name().clone(),
ede,
ttl,
));
}
let (state, ttl, ede) = nsec_for_ds(
&name,
&mut authorities,
node,
&self.isig_cache,
&self.config,
)
.await;
match state {
CNsecState::InsecureDelegation => {
let ttl = min(parent_ttl, ttl);
return Ok(Node::new_delegation(
name,
ValidationState::Insecure,
Vec::new(),
ede,
ttl,
));
}
CNsecState::SecureIntermediate => {
return Ok(Node::new_intermediate(
name,
ValidationState::Secure,
node.signer_name().clone(),
ede,
ttl,
))
}
CNsecState::Bogus => {
return Ok(Node::new_delegation(
name,
ValidationState::Bogus,
Vec::new(),
ede,
ttl,
))
}
CNsecState::Nothing => (), }
let (state, ede, ttl) = nsec3_for_ds(
&name,
&mut authorities,
node,
self.nsec3_cache(),
&self.isig_cache,
&self.config,
)
.await;
match state {
CNsecState::InsecureDelegation => {
return Ok(Node::new_delegation(
name,
ValidationState::Insecure,
Vec::new(),
ede,
ttl,
))
}
CNsecState::SecureIntermediate => {
return Ok(Node::new_intermediate(
name,
ValidationState::Secure,
node.signer_name().clone(),
ede,
ttl,
))
}
CNsecState::Bogus => {
return Ok(Node::new_delegation(
name,
ValidationState::Bogus,
Vec::new(),
ede,
ttl,
))
}
CNsecState::Nothing => (),
}
return Ok(Node::new_delegation(
name,
ValidationState::Bogus,
Vec::new(),
ede,
ttl,
));
}
};
let ds_ttl = ds_group.min_ttl().into_duration();
let ttl = min(parent_ttl, ds_ttl);
let (state, _wildcard, ede, sig_ttl, _) = ds_group
.validate_with_node(node, &self.isig_cache, &self.config)
.await;
let ttl = min(ttl, sig_ttl);
match state {
ValidationState::Secure => (),
ValidationState::Insecure
| ValidationState::Indeterminate
| ValidationState::Bogus => {
return Ok(Node::new_delegation(
name,
ValidationState::Bogus,
Vec::new(),
ede,
ttl,
));
}
}
let mut tmp_group = ds_group.clone();
let valid_algs = tmp_group
.rr_iter()
.map(|r| {
if let AllRecordData::Ds(ds) = r.data() {
(ds.algorithm(), ds.digest_type())
} else {
panic!("DS record expected");
}
})
.any(|(alg, dig)| {
supported_algorithm(&alg) && supported_digest(&dig)
});
if !valid_algs {
let ede = make_ede(
ExtendedErrorCode::OTHER,
"No supported algorithm in DS RRset",
);
return Ok(Node::new_delegation(
name,
ValidationState::Insecure,
Vec::new(),
ede,
ttl,
));
}
let (mut answers, _, ede) =
request_as_groups(&self.upstream, &name, Rtype::DNSKEY).await?;
if ede.is_some() {
return Ok(Node::new_delegation(
name,
ValidationState::Bogus,
Vec::new(),
ede,
self.config.max_bogus_validity,
));
}
let dnskey_group =
match answers.iter().find(|g| g.rtype() == Rtype::DNSKEY) {
Some(g) => g,
None => {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"No DNSKEY found",
);
return Ok(Node::new_delegation(
name,
ValidationState::Bogus,
Vec::new(),
ede,
self.config.max_bogus_validity,
));
}
};
let dnskey_ttl = dnskey_group.min_ttl().into_duration();
let ttl = min(ttl, dnskey_ttl);
let mut bad_sigs = 0;
let mut ede = None;
for ds in tmp_group
.rr_iter()
.map(|r| {
if let AllRecordData::Ds(ds) = r.data() {
ds
} else {
panic!("DS record expected");
}
})
.filter(|ds| {
supported_algorithm(&ds.algorithm())
&& supported_digest(&ds.digest_type())
})
{
let r_dnskey = match find_key_for_ds(ds, dnskey_group) {
None => continue,
Some(r) => r,
};
let dnskey =
if let AllRecordData::Dnskey(dnskey) = r_dnskey.data() {
dnskey
} else {
panic!("expected DNSKEY");
};
let key_tag = dnskey.key_tag();
let key_name = r_dnskey.owner().to_name();
for sig in (*dnskey_group).clone().sig_iter() {
if sig.data().key_tag() != key_tag {
continue; }
if dnskey_group
.check_sig_cached(
sig,
&key_name,
dnskey,
&key_name,
key_tag,
&self.isig_cache,
)
.await
{
let dnskey_vec: Vec<_> = dnskey_group
.clone()
.rr_iter()
.map(|r| {
if let AllRecordData::Dnskey(key) = r.data() {
key
} else {
panic!("Dnskey expected");
}
})
.cloned()
.collect();
let sig_ttl = ttl_for_sig(sig).into_duration();
let ttl = min(ttl, sig_ttl);
return Ok(Node::new_delegation(
key_name,
ValidationState::Secure,
dnskey_vec,
None,
ttl,
));
} else {
bad_sigs += 1;
if bad_sigs > self.config.max_bad_signatures {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"too many bad signatures for DNSKEY",
);
return Ok(Node::new_delegation(
name,
ValidationState::Bogus,
Vec::new(),
ede,
self.config.max_bogus_validity,
));
}
if ede.is_none() {
ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"too many bad signature for DNSKEY",
);
}
}
}
}
if ede.is_none() {
ede = make_ede(ExtendedErrorCode::DNSSEC_BOGUS, "No signature");
}
Ok(Node::new_delegation(
name,
ValidationState::Bogus,
Vec::new(),
ede,
self.config.max_bogus_validity,
))
}
async fn cache_lookup(&self, name: &Name<Bytes>) -> Option<Arc<Node>> {
let ce = self.node_cache.get(name).await?;
if ce.expired() {
return None;
}
Some(ce)
}
pub(crate) fn nsec3_cache(&self) -> &Nsec3Cache {
&self.nsec3_cache
}
pub(crate) fn usig_cache(&self) -> &SigCache {
&self.usig_cache
}
async fn validate_groups<Octs>(&self, groups: &mut GroupSet) -> VGResult
where
Octs:
AsRef<[u8]> + Debug + Octets + OctetsFrom<Vec<u8>> + Send + Sync,
Upstream: SendRequest<RequestMessage<Octs>>,
{
let mut fix_reply = false;
let mut vgs = Vec::new();
for g in groups.iter() {
let vg = match g.validated(self, &self.config).await {
Ok(vg) => vg,
Err(err) => return VGResult::Err(err),
};
if let ValidationState::Bogus = vg.state() {
return VGResult::Bogus(vg.ede());
}
if vg.adjust_ttl().is_some() || vg.found_duplicate() {
fix_reply = true;
}
vgs.push(vg);
}
VGResult::Groups(vgs, fix_reply)
}
}
enum VGResult {
Groups(Vec<ValidatedGroup>, bool),
Bogus(Option<ExtendedError<Vec<u8>>>),
Err(Error),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ValidationState {
Secure,
Insecure,
Bogus,
Indeterminate,
}
#[derive(Clone)]
pub(crate) struct Node {
state: ValidationState,
keys: Vec<Dnskey<Bytes>>,
signer_name: Name<Bytes>,
intermediate: bool,
ede: Option<ExtendedError<Vec<u8>>>,
created_at: Instant,
valid_for: Duration,
}
impl Node {
fn indeterminate(
name: Name<Bytes>,
ede: Option<ExtendedError<Vec<u8>>>,
valid_for: Duration,
) -> Self {
Self {
state: ValidationState::Indeterminate,
keys: Vec::new(),
signer_name: name,
intermediate: false,
ede,
created_at: Instant::now(),
valid_for,
}
}
async fn trust_anchor<Octs, Upstream>(
ta: &TrustAnchor,
upstream: &Upstream,
sig_cache: &SigCache,
config: &Config,
) -> Result<Self, Error>
where
Octs:
AsRef<[u8]> + Debug + Octets + OctetsFrom<Vec<u8>> + Send + Sync,
Upstream: SendRequest<RequestMessage<Octs>>,
{
let ta_owner = ta.owner();
let (mut answers, _, _ede) =
request_as_groups(upstream, &ta_owner, Rtype::DNSKEY).await?;
let dnskeys =
match answers.iter().find(|g| g.rtype() == Rtype::DNSKEY) {
Some(dnskeys) => dnskeys,
None => {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"No DNSKEY RRset for trust anchor",
);
return Ok(Node::new_delegation(
ta_owner,
ValidationState::Bogus,
Vec::new(),
ede,
config.max_bogus_validity,
));
}
};
let mut bad_sigs = 0;
let mut opt_ede: Option<ExtendedError<Vec<u8>>> = None;
for ta_rr in (*ta).clone().iter() {
let opt_dnskey_rr = if ta_rr.rtype() == Rtype::DNSKEY {
has_key(dnskeys, ta_rr)
} else if ta_rr.rtype() == Rtype::DS {
has_ds(dnskeys, ta_rr)
} else {
None
};
let dnskey_rr = if let Some(dnskey_rr) = opt_dnskey_rr {
dnskey_rr
} else {
continue;
};
let ttl = config.max_node_validity;
let dnskey_ttl = dnskeys.min_ttl().into_duration();
let ttl = min(ttl, dnskey_ttl);
let dnskey =
if let AllRecordData::Dnskey(dnskey) = dnskey_rr.data() {
dnskey
} else {
continue;
};
let key_tag = dnskey.key_tag();
let key_name = dnskey_rr.owner().to_name();
for sig in (*dnskeys).clone().sig_iter() {
if sig.data().key_tag() != key_tag {
continue; }
if dnskeys
.check_sig_cached(
sig, &ta_owner, dnskey, &key_name, key_tag, sig_cache,
)
.await
{
let sig_ttl = ttl_for_sig(sig).into_duration();
let ttl = min(ttl, sig_ttl);
let mut new_node = Self {
state: ValidationState::Secure,
keys: Vec::new(),
signer_name: ta_owner,
intermediate: false,
ede: None,
created_at: Instant::now(),
valid_for: ttl,
};
for key_rec in dnskeys.clone().rr_iter() {
if let AllRecordData::Dnskey(key) = key_rec.data() {
new_node.keys.push(key.clone());
}
}
return Ok(new_node);
} else {
bad_sigs += 1;
if bad_sigs > config.max_bad_signatures {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"too many bad signatures for DNSKEY",
);
return Ok(Node::new_delegation(
ta_owner,
ValidationState::Bogus,
Vec::new(),
ede,
config.max_bogus_validity,
));
}
if opt_ede.is_none() {
opt_ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"Bad signature",
);
}
}
}
}
if opt_ede.is_none() {
opt_ede =
make_ede(ExtendedErrorCode::DNSSEC_BOGUS, "No signature");
}
Ok(Node::new_delegation(
ta_owner,
ValidationState::Bogus,
Vec::new(),
opt_ede,
config.max_bogus_validity,
))
}
pub fn new_delegation(
signer_name: Name<Bytes>,
state: ValidationState,
keys: Vec<Dnskey<Bytes>>,
ede: Option<ExtendedError<Vec<u8>>>,
valid_for: Duration,
) -> Self {
Self {
state,
signer_name,
keys,
intermediate: false,
ede,
created_at: Instant::now(),
valid_for,
}
}
pub fn new_intermediate(
_name: Name<Bytes>,
state: ValidationState,
signer_name: Name<Bytes>,
ede: Option<ExtendedError<Vec<u8>>>,
valid_for: Duration,
) -> Self {
Self {
state,
signer_name,
keys: Vec::new(),
intermediate: true,
ede,
created_at: Instant::now(),
valid_for,
}
}
pub fn validation_state(&self) -> ValidationState {
self.state
}
pub fn extended_error(&self) -> Option<ExtendedError<Vec<u8>>> {
self.ede.clone()
}
pub fn keys(&self) -> &[Dnskey<Bytes>] {
&self.keys
}
pub fn signer_name(&self) -> &Name<Bytes> {
&self.signer_name
}
pub fn intermediate(&self) -> bool {
self.intermediate
}
pub fn expired(&self) -> bool {
let elapsed = self.created_at.elapsed();
elapsed > self.valid_for
}
pub fn ttl(&self) -> Duration {
self.valid_for - self.created_at.elapsed()
}
}
#[allow(clippy::type_complexity)]
fn has_key(
dnskeys: &Group,
tkey: &Record<
Chain<RelativeName<Bytes>, Name<Bytes>>,
ZoneRecordData<Bytes, Chain<RelativeName<Bytes>, Name<Bytes>>>,
>,
) -> Option<Record<Name<Bytes>, AllRecordData<Bytes, ParsedName<Bytes>>>> {
let tkey_dnskey = if let ZoneRecordData::Dnskey(dnskey) = tkey.data() {
dnskey
} else {
return None;
};
for key in (*dnskeys).clone().rr_iter() {
let AllRecordData::Dnskey(key_dnskey) = key.data() else {
continue;
};
if tkey.owner().to_name::<Bytes>() != key.owner() {
continue;
}
if tkey.class() != key.class() {
continue;
}
if tkey.rtype() != key.rtype() {
continue;
}
if tkey_dnskey != key_dnskey {
continue;
}
return Some(key.clone());
}
None
}
#[allow(clippy::type_complexity)]
fn has_ds(
dnskeys: &Group,
ta_rr: &Record<
Chain<RelativeName<Bytes>, Name<Bytes>>,
ZoneRecordData<Bytes, Chain<RelativeName<Bytes>, Name<Bytes>>>,
>,
) -> Option<Record<Name<Bytes>, AllRecordData<Bytes, ParsedName<Bytes>>>> {
let ds = if let ZoneRecordData::Ds(ds) = ta_rr.data() {
ds
} else {
return None;
};
find_key_for_ds(ds, dnskeys)
}
#[allow(clippy::type_complexity)]
fn find_key_for_ds(
ds: &Ds<Bytes>,
dnskey_group: &Group,
) -> Option<Record<Name<Bytes>, AllRecordData<Bytes, ParsedName<Bytes>>>> {
let ds_alg = ds.algorithm();
let ds_tag = ds.key_tag();
let digest_type = ds.digest_type();
for key in dnskey_group.clone().rr_iter() {
let AllRecordData::Dnskey(dnskey) = key.data() else {
panic!("Dnskey expected");
};
if dnskey.algorithm() != ds_alg {
continue;
}
if dnskey.key_tag() != ds_tag {
continue;
}
let digest = match dnskey.digest(key.owner(), digest_type) {
Ok(d) => d,
Err(_) => {
continue;
}
};
if ds.digest() == digest.as_ref() {
return Some(key.clone());
}
}
None
}
#[derive(Debug)]
enum CNsecState {
InsecureDelegation,
SecureIntermediate,
Nothing,
Bogus,
}
async fn nsec_for_ds(
target: &Name<Bytes>,
groups: &mut GroupSet,
node: &Node,
sig_cache: &SigCache,
config: &Config,
) -> (CNsecState, Duration, Option<ExtendedError<Vec<u8>>>) {
let mut ede = None;
for g in groups.iter() {
if g.rtype() != Rtype::NSEC {
continue;
}
let owner = g.owner();
let rrs = g.rr_set();
let AllRecordData::Nsec(nsec) = rrs[0].data() else {
panic!("NSEC expected");
};
if target.name_eq(&owner) {
let (state, wildcard, ede, ttl, _) =
g.validate_with_node(node, sig_cache, config).await;
match state {
ValidationState::Insecure
| ValidationState::Bogus
| ValidationState::Indeterminate =>
{
return (CNsecState::Bogus, ttl, ede)
}
ValidationState::Secure => (),
}
if wildcard.is_some() {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"NSEC for DS is wildcard",
);
return (CNsecState::Bogus, config.max_bogus_validity, ede);
}
let types = nsec.types();
if types.contains(Rtype::DS) {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"NSEC proves DS",
);
return (CNsecState::Bogus, config.max_bogus_validity, ede);
}
if types.contains(Rtype::SOA) {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"NSEC for DS from apex",
);
return (CNsecState::Bogus, config.max_bogus_validity, ede);
}
if types.contains(Rtype::NS) {
return (CNsecState::InsecureDelegation, ttl, None);
}
return (CNsecState::SecureIntermediate, ttl, None);
}
if target.ends_with(&owner) {
let (state, wildcard, ede, ttl, _) =
g.validate_with_node(node, sig_cache, config).await;
match state {
ValidationState::Insecure
| ValidationState::Indeterminate
| ValidationState::Bogus => {
return (CNsecState::Bogus, ttl, ede);
}
ValidationState::Secure => (),
}
if wildcard.is_some() {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"prefix NSEC for DS is wildcard",
);
return (CNsecState::Bogus, config.max_bogus_validity, ede);
}
let types = nsec.types();
if types.contains(Rtype::DNAME) {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"prefix NSEC for DS is DNAME",
);
return (CNsecState::Bogus, config.max_bogus_validity, ede);
}
if types.contains(Rtype::NS) && !types.contains(Rtype::SOA) {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"prefix NSEC for DS is delegation",
);
return (CNsecState::Bogus, config.max_bogus_validity, ede);
}
}
if nsec_in_range(target, &owner, &nsec.next_name())
&& nsec.next_name().ends_with(target)
{
let (state, wildcard, ede, ttl, _) =
g.validate_with_node(node, sig_cache, config).await;
match state {
ValidationState::Insecure
| ValidationState::Indeterminate
| ValidationState::Bogus => {
return (CNsecState::Bogus, ttl, ede);
}
ValidationState::Secure => (),
}
if let Some(wildcard) = wildcard {
if *target != wildcard {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"ENT NSEC for DS is expanded wildcard",
);
return (
CNsecState::Bogus,
config.max_bogus_validity,
ede,
);
}
}
return (CNsecState::SecureIntermediate, ttl, None);
}
ede = make_ede(ExtendedErrorCode::OTHER, "NSEC found but not usable");
}
(CNsecState::Nothing, config.max_node_validity, ede)
}
async fn nsec3_for_ds(
target: &Name<Bytes>,
groups: &mut GroupSet,
node: &Node,
nsec3_cache: &Nsec3Cache,
sig_cache: &SigCache,
config: &Config,
) -> (CNsecState, Option<ExtendedError<Vec<u8>>>, Duration) {
let mut ede = None;
for g in groups.iter() {
if g.rtype() != Rtype::NSEC3 {
continue;
}
let rrs = g.rr_set();
let AllRecordData::Nsec3(nsec3) = rrs[0].data() else {
panic!("NSEC3 expected");
};
let iterations = nsec3.iterations();
if iterations > config.nsec3_iter_insecure
|| iterations > config.nsec3_iter_bogus
{
let (state, _wildcard, ede, ttl, _) =
g.validate_with_node(node, sig_cache, config).await;
match state {
ValidationState::Insecure
| ValidationState::Indeterminate
| ValidationState::Bogus => {
return (CNsecState::Bogus, ede, ttl);
}
ValidationState::Secure => (),
}
if iterations > config.nsec3_iter_bogus {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"NSEC3 with too high iteration count",
);
return (CNsecState::Bogus, ede, ttl);
}
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"NSEC3 with too high iteration count",
);
return (CNsecState::InsecureDelegation, ede, ttl);
}
if !supported_nsec3_hash(nsec3.hash_algorithm()) {
ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"NSEC3 with unsupported hash algorithm",
);
continue;
}
let hash = cached_nsec3_hash(
target,
nsec3.hash_algorithm(),
iterations,
nsec3.salt(),
nsec3_cache,
)
.await;
let owner = g.owner();
let first = owner.first();
let ownerhash = match nsec3_label_to_hash(owner.first()) {
Ok(hash) => hash,
Err(_) => {
ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"NSEC3 with bad owner hash",
);
continue;
}
};
if !target.ends_with(&owner.parent().unwrap_or_else(Name::root)) {
ede = make_ede(ExtendedErrorCode::OTHER, "NSEC3 from wrong zone");
continue;
}
if first
== Label::from_slice(hash.to_string().as_ref())
.expect("hash is expected to fit in a label")
{
let (state, _, ede, ttl, _) =
g.validate_with_node(node, sig_cache, config).await;
match state {
ValidationState::Insecure
| ValidationState::Indeterminate
| ValidationState::Bogus => {
return (CNsecState::Bogus, ede, ttl);
}
ValidationState::Secure => (),
}
let types = nsec3.types();
if types.contains(Rtype::DS) {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"NSEC3 proves DS",
);
return (CNsecState::Bogus, ede, config.max_bogus_validity);
}
if types.contains(Rtype::SOA) {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"NSEC3 for DS from apex",
);
return (CNsecState::Bogus, ede, config.max_bogus_validity);
}
if types.contains(Rtype::NS) {
return (CNsecState::InsecureDelegation, None, ttl);
}
return (CNsecState::SecureIntermediate, None, ttl);
}
if nsec3_in_range(hash.as_ref(), &ownerhash, nsec3.next_owner()) {
let (state, _, ede, ttl, _) =
g.validate_with_node(node, sig_cache, config).await;
match state {
ValidationState::Insecure
| ValidationState::Indeterminate
| ValidationState::Bogus => {
return (CNsecState::Bogus, ede, ttl);
}
ValidationState::Secure => (),
}
if !nsec3.opt_out() {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"NSEC3 proves name does not exist for DS",
);
return (CNsecState::Bogus, ede, config.max_bogus_validity);
}
return (CNsecState::InsecureDelegation, None, ttl);
}
}
(CNsecState::Nothing, ede, config.max_node_validity)
}
async fn request_as_groups<Octs, Upstream>(
upstream: &Upstream,
name: &Name<Bytes>,
rtype: Rtype,
) -> Result<(GroupSet, GroupSet, Option<ExtendedError<Vec<u8>>>), Error>
where
Octs: AsRef<[u8]> + Debug + Octets + OctetsFrom<Vec<u8>> + Send + Sync,
Upstream: SendRequest<RequestMessage<Octs>>,
{
let mut msg = MessageBuilder::new_vec();
msg.header_mut().set_cd(true);
msg.header_mut().set_rd(true);
let mut msg = msg.question();
msg.push((&name, rtype)).expect("should not fail");
let msg_as_octets = msg.into_message().into_octets();
let octs: Octs = match msg_as_octets.try_octets_into() {
Ok(octs) => octs,
Err(_) => {
return Err(Error::OctetsConversion);
}
};
let msg = Message::from_octets(octs).expect("should not fail");
let mut req = RequestMessage::new(msg).expect("should not fail");
req.set_dnssec_ok(true);
let mut request = upstream.send_request(req);
let reply = request.get_response().await;
let mut ede = None;
let mut answers = GroupSet::new();
let mut authorities = GroupSet::new();
let parse_error_ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"request for DS or DNSKEY failed, parse error",
);
if let Ok(reply) = reply {
if let Ok(answer) = reply.answer() {
for rr in answer {
if let Some(e) = rr.map_or_else(
|_e| parse_error_ede.clone(),
|rr| {
answers.add(rr).map_or_else(
|_e| parse_error_ede.clone(),
|_| None,
)
},
) {
ede = Some(e);
}
}
} else {
ede.clone_from(&parse_error_ede);
}
if let Ok(authority) = reply.authority() {
for rr in authority {
if let Some(e) = rr.map_or_else(
|_e| parse_error_ede.clone(),
|rr| {
authorities.add(rr).map_or_else(
|_e| parse_error_ede.clone(),
|_| None,
)
},
) {
ede = Some(e);
}
}
} else {
ede = parse_error_ede;
}
} else {
ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"request for DS or DNSKEY failed",
);
}
Ok((answers, authorities, ede))
}
#[derive(Clone, Debug)]
pub enum Error {
FormError,
InplaceError(inplace::Error),
OctetsConversion,
ParseError,
PushError,
PushNameError,
ReadError(Arc<std::io::Error>),
ShortMessage,
}
impl From<inplace::Error> for Error {
fn from(e: inplace::Error) -> Self {
Error::InplaceError(e)
}
}
impl From<name::PushError> for Error {
fn from(_: name::PushError) -> Self {
Error::PushError
}
}
impl From<name::PushNameError> for Error {
fn from(_: name::PushNameError) -> Self {
Error::PushNameError
}
}
impl From<wire::ParseError> for Error {
fn from(_: wire::ParseError) -> Self {
Error::ParseError
}
}
impl From<ShortMessage> for Error {
fn from(_: ShortMessage) -> Self {
Error::ShortMessage
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::FormError => write!(f, "FormError"),
Error::InplaceError(_) => write!(f, "InplaceError"),
Error::OctetsConversion => write!(f, "OctetsConversion"),
Error::ParseError => write!(f, "ParseError"),
Error::PushError => write!(f, "PushError"),
Error::PushNameError => write!(f, "PushNameError"),
Error::ReadError(_) => write!(f, "FormError"),
Error::ShortMessage => write!(f, "ShortMEssage"),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::FormError => None,
Error::InplaceError(err) => Some(err),
Error::OctetsConversion => None,
Error::ParseError => None,
Error::PushError => None,
Error::PushNameError => None,
Error::ReadError(err) => Some(err),
Error::ShortMessage => None,
}
}
}