use super::context::{
Config, Error, Node, ValidationContext, ValidationState,
};
use super::utilities::{make_ede, map_dname, ttl_for_sig};
use crate::base::cmp::CanonicalOrd;
use crate::base::iana::class::Class;
use crate::base::iana::ExtendedErrorCode;
use crate::base::name::ToName;
use crate::base::opt::exterr::ExtendedError;
use crate::base::rdata::ComposeRecordData;
use crate::base::{Name, ParsedName, ParsedRecord, Record, Rtype, Ttl};
use crate::crypto::common::{DigestBuilder, DigestType};
use crate::dep::octseq::builder::with_infallible;
use crate::dep::octseq::{Octets, OctetsFrom};
use crate::dnssec::validator::base::RrsigExt;
use crate::net::client::request::{RequestMessage, SendRequest};
use crate::rdata::dnssec::Timestamp;
use crate::rdata::{AllRecordData, Dnskey, Rrsig};
use bytes::Bytes;
use moka::future::Cache;
use std::cmp::{max, min};
use std::fmt::Debug;
use std::slice::Iter;
use std::time::Duration;
use std::vec::Vec;
#[derive(Clone, Debug)]
pub struct Group {
rr_set: Vec<RrType>,
sig_set: Vec<SigType>,
extra_set: Vec<RrType>,
found_duplicate: bool,
}
type RrType = Record<Name<Bytes>, AllRecordData<Bytes, ParsedName<Bytes>>>;
type SigType = Record<Name<Bytes>, Rrsig<Bytes, Name<Bytes>>>;
impl Group {
fn new(rr: ParsedRecord<'_, Bytes>) -> Result<Self, Error> {
let sig_record = match rr.to_record::<Rrsig<_, _>>()? {
None => {
return Ok(Self {
rr_set: vec![to_bytes_record(&rr)?],
sig_set: Vec::new(),
extra_set: Vec::new(),
found_duplicate: false,
});
}
Some(record) => record,
};
let rrsig = sig_record.data();
let rrsig: Rrsig<Bytes, Name<Bytes>> =
Rrsig::<Bytes, Name<Bytes>>::new(
rrsig.type_covered(),
rrsig.algorithm(),
rrsig.labels(),
rrsig.original_ttl(),
rrsig.expiration(),
rrsig.inception(),
rrsig.key_tag(),
rrsig.signer_name().to_name::<Bytes>(),
Bytes::copy_from_slice(rrsig.signature().as_ref()),
)
.expect("should not fail");
let record: Record<Name<Bytes>, _> = Record::new(
sig_record.owner().to_name::<Bytes>(),
sig_record.class(),
sig_record.ttl(),
rrsig,
);
Ok(Self {
rr_set: Vec::new(),
sig_set: vec![record],
extra_set: Vec::new(),
found_duplicate: false,
})
}
fn add(&mut self, rr: &ParsedRecord<'_, Bytes>) -> Result<(), ()> {
if let Some(frr) = self.rr_set.first() {
if frr.owner() != &rr.owner().to_name::<Bytes>() {
return Err(());
}
} else if *self.sig_set[0].owner() != rr.owner() {
return Err(());
}
let (curr_class, curr_rtype) = if let Some(rr) = self.rr_set.first() {
(rr.class(), rr.rtype())
} else {
(
self.sig_set[0].class(),
self.sig_set[0].data().type_covered(),
)
};
let opt_record = match rr.to_record::<Rrsig<_, _>>() {
Ok(opt_record) => opt_record,
Err(_) => {
return Err(());
}
};
if let Some(record) = opt_record {
let rrsig = record.data();
if curr_class == rr.class() && curr_rtype == rrsig.type_covered()
{
let rrsig: Rrsig<Bytes, Name<Bytes>> =
Rrsig::<Bytes, Name<Bytes>>::new(
rrsig.type_covered(),
rrsig.algorithm(),
rrsig.labels(),
rrsig.original_ttl(),
rrsig.expiration(),
rrsig.inception(),
rrsig.key_tag(),
rrsig.signer_name().to_name::<Bytes>(),
Bytes::copy_from_slice(rrsig.signature().as_ref()),
)
.expect("should not fail");
let record: Record<Name<Bytes>, _> = Record::new(
record.owner().to_name::<Bytes>(),
curr_class,
record.ttl(),
rrsig,
);
for r in &self.sig_set {
if *r == record {
self.found_duplicate = true;
return Ok(());
}
}
self.sig_set.push(record);
return Ok(());
}
return Err(());
}
if curr_class == rr.class() && curr_rtype == rr.rtype() {
let rr = match to_bytes_record(rr) {
Ok(rr) => rr,
Err(_) => {
return Err(());
}
};
for r in &self.rr_set {
if *r == rr {
self.found_duplicate = true;
return Ok(());
}
}
self.rr_set.push(rr);
return Ok(());
}
Err(())
}
fn add_extra(
&mut self,
rr: &Record<Name<Bytes>, AllRecordData<Bytes, ParsedName<Bytes>>>,
) {
self.extra_set.push(rr.clone());
}
pub async fn validated<Octs, Upstream>(
&self,
vc: &ValidationContext<Upstream>,
config: &Config,
) -> Result<ValidatedGroup, Error>
where
Octs:
AsRef<[u8]> + Debug + Octets + OctetsFrom<Vec<u8>> + Send + Sync,
Upstream: SendRequest<RequestMessage<Octs>>,
{
let (state, signer_name, wildcard, ede, adjust_ttl) =
self.validate_with_vc(vc, config).await?;
Ok(ValidatedGroup::new(
self.rr_set.clone(),
self.sig_set.clone(),
self.extra_set.clone(),
state,
signer_name,
wildcard,
ede,
adjust_ttl,
self.found_duplicate,
))
}
pub fn owner(&self) -> Name<Bytes> {
if let Some(rr) = self.rr_set.first() {
return rr.owner().to_bytes();
}
self.sig_set[0].owner().to_bytes()
}
pub fn class(&self) -> Class {
if let Some(rr) = self.rr_set.first() {
return rr.class();
}
self.sig_set[0].class()
}
pub fn rtype(&self) -> Rtype {
if let Some(rr) = self.rr_set.first() {
return rr.rtype();
}
Rtype::RRSIG
}
pub fn rr_set(&self) -> Vec<RrType> {
self.rr_set.clone()
}
pub fn rr_iter(&mut self) -> Iter<'_, RrType> {
self.rr_set.iter()
}
pub fn sig_set_len(&self) -> usize {
self.sig_set.len()
}
pub fn sig_iter(&mut self) -> Iter<'_, SigType> {
self.sig_set.iter()
}
pub async fn validate_with_vc<Octs, Upstream>(
&self,
vc: &ValidationContext<Upstream>,
config: &Config,
) -> Result<
(
ValidationState,
Name<Bytes>,
Option<Name<Bytes>>,
Option<ExtendedError<Vec<u8>>>,
Option<Ttl>,
),
Error,
>
where
Octs:
AsRef<[u8]> + Debug + Octets + OctetsFrom<Vec<u8>> + Send + Sync,
Upstream: SendRequest<RequestMessage<Octs>>,
{
if self.rr_set.is_empty() {
return Ok((
ValidationState::Insecure,
Name::root(),
None,
make_ede(
ExtendedErrorCode::DNSSEC_INDETERMINATE,
"RRSIG without RRset",
),
None,
));
}
let target = if let Some(sig_rr) = self.sig_set.first() {
sig_rr.data().signer_name()
} else {
self.rr_set[0].owner()
};
let node = vc.get_node(target).await?;
let state = node.validation_state();
match state {
ValidationState::Secure => (), ValidationState::Insecure
| ValidationState::Bogus
| ValidationState::Indeterminate => {
return Ok((
state,
target.clone(),
None,
node.extended_error(),
None,
))
}
}
let (state, wildcard, ede, _ttl, adjust_ttl) = self
.validate_with_node(&node, vc.usig_cache(), config)
.await;
Ok((state, target.clone(), wildcard, ede, adjust_ttl))
}
pub(crate) async fn validate_with_node(
&self,
node: &Node,
sig_cache: &SigCache,
config: &Config,
) -> (
ValidationState,
Option<Name<Bytes>>,
Option<ExtendedError<Vec<u8>>>,
Duration,
Option<Ttl>,
) {
let mut opt_ede = None;
let state = node.validation_state();
match state {
ValidationState::Insecure
| ValidationState::Bogus
| ValidationState::Indeterminate => {
return (state, None, node.extended_error(), node.ttl(), None)
}
ValidationState::Secure => (),
}
let keys = node.keys();
let ttl = node.ttl();
let group_ttl = self.min_ttl();
let group_max_ttl = self.max_ttl();
let group_dur = group_ttl.into_duration();
let ttl = min(ttl, group_dur);
let mut bad_sigs = 0;
for sig_rec in self.clone().sig_iter() {
let sig = sig_rec.data();
for key in keys {
if key.algorithm() != sig.algorithm() {
continue;
}
let key_tag = key.key_tag();
if key_tag != sig.key_tag() {
continue;
}
if self
.check_sig_cached(
sig_rec,
node.signer_name(),
key,
node.signer_name(),
key_tag,
sig_cache,
)
.await
{
let wildcard =
sig.wildcard_closest_encloser(&self.rr_set[0]);
let sig_ttl = ttl_for_sig(sig_rec);
let adjust_ttl = if sig_ttl < group_max_ttl {
Some(sig_ttl)
} else {
None
};
let ttl = min(ttl, sig_ttl.into_duration());
return (
ValidationState::Secure,
wildcard,
None,
ttl,
adjust_ttl,
);
} else {
bad_sigs += 1;
if bad_sigs > config.max_bad_signatures() {
let ede = make_ede(
ExtendedErrorCode::DNSSEC_BOGUS,
"too many bad signatures",
);
return (
ValidationState::Bogus,
None,
ede,
config.max_bogus_validity(),
None,
);
}
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");
}
(
ValidationState::Bogus,
None,
opt_ede,
config.max_bogus_validity(),
None,
)
}
fn check_sig(
&self,
sig: &Record<Name<Bytes>, Rrsig<Bytes, Name<Bytes>>>,
signer_name: &Name<Bytes>,
key: &Dnskey<Bytes>,
key_name: &Name<Bytes>,
key_tag: u16,
) -> bool {
let ts_now = Timestamp::now();
let rtype = self.rtype();
let owner = self.owner();
let labels = owner.iter().count() - 1;
let rrsig = sig.data();
if !sig.owner().name_eq(&owner) || sig.class() != self.class() {
return false;
}
if !owner.ends_with(&signer_name) {
return false;
}
if rrsig.type_covered() != rtype {
return false;
}
if labels < rrsig.labels() as usize {
return false;
}
if ts_now.canonical_gt(&rrsig.expiration())
|| ts_now.canonical_lt(&rrsig.inception())
{
return false;
}
if signer_name != key_name
|| rrsig.algorithm() != key.algorithm()
|| rrsig.key_tag() != key_tag
{
return false;
}
if !key.is_zone_key() {
return false;
}
let mut signed_data = Vec::<u8>::new();
rrsig
.signed_data(&mut signed_data, &mut self.rr_set())
.expect("infallible");
let res = rrsig.verify_signed_data(key, &signed_data);
res.is_ok()
}
pub async fn check_sig_cached(
&self,
sig: &Record<Name<Bytes>, Rrsig<Bytes, Name<Bytes>>>,
signer_name: &Name<Bytes>,
key: &Dnskey<Bytes>,
key_name: &Name<Bytes>,
key_tag: u16,
cache: &SigCache,
) -> bool {
let mut signed_data = Vec::<u8>::new();
sig.data()
.signed_data(&mut signed_data, &mut self.rr_set())
.expect("infallible");
let mut buf: Vec<u8> = Vec::new();
with_infallible(|| key.compose_canonical_rdata(&mut buf));
let mut ctx = DigestBuilder::new(DigestType::Sha256);
ctx.update(&buf);
let key_hash = ctx.finish();
let mut buf: Vec<u8> = Vec::new();
with_infallible(|| sig.data().compose_canonical_rdata(&mut buf));
let mut ctx = DigestBuilder::new(DigestType::Sha256);
ctx.update(&buf);
let sig_hash = ctx.finish();
let cache_key = SigKey(
signed_data,
sig_hash.as_ref().to_vec(),
key_hash.as_ref().to_vec(),
);
if let Some(ce) = cache.cache.get(&cache_key).await {
return ce;
}
let res = self.check_sig(sig, signer_name, key, key_name, key_tag);
cache.cache.insert(cache_key, res).await;
res
}
pub fn min_ttl(&self) -> Ttl {
if self.rr_set.is_empty() {
return Ttl::ZERO;
}
let mut ttl = self.rr_set[0].ttl();
for rr in &self.rr_set[1..] {
ttl = min(ttl, rr.ttl());
}
ttl
}
pub fn max_ttl(&self) -> Ttl {
let mut ttl = Ttl::ZERO;
for rr in &self.rr_set {
ttl = max(ttl, rr.ttl());
}
for rr in &self.sig_set {
ttl = max(ttl, rr.ttl());
}
for rr in &self.extra_set {
ttl = max(ttl, rr.ttl());
}
ttl
}
}
#[derive(Clone, Debug)]
pub struct GroupSet(Vec<Group>);
impl GroupSet {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn add(&mut self, rr: ParsedRecord<'_, Bytes>) -> Result<(), Error> {
if self.0.is_empty() {
self.0.push(Group::new(rr)?);
return Ok(());
}
let len = self.0.len();
let res = self.0[len - 1].add(&rr);
if res.is_ok() {
return Ok(());
}
for g in &mut self.0[..len - 1] {
let res = g.add(&rr);
if res.is_ok() {
return Ok(());
}
}
self.0.push(Group::new(rr)?);
Ok(())
}
pub fn move_redundant_cnames(&mut self) {
for cname_ind in (0..self.0.len()).rev() {
if self.0[cname_ind].rtype() != Rtype::CNAME {
continue;
}
let rr_set = self.0[cname_ind].rr_set();
if rr_set.len() != 1 {
continue; }
if self.0[cname_ind].sig_set_len() != 0 {
continue;
}
if self
.moved_to_dname(&rr_set[0], self.0[cname_ind].found_duplicate)
{
let _ = self.0.remove(cname_ind);
}
}
}
fn moved_to_dname(
&mut self,
cname_rr: &Record<
Name<Bytes>,
AllRecordData<Bytes, ParsedName<Bytes>>,
>,
found_duplicate: bool,
) -> bool {
let cname_name = cname_rr.owner();
for g in &mut self.0 {
if g.rtype() != Rtype::DNAME {
continue;
}
let rr_set = g.rr_set();
for rr in rr_set {
let owner = rr.owner();
if !cname_name.ends_with(owner) {
continue;
}
if cname_name == owner {
continue;
}
let result_name =
if let AllRecordData::Dname(dname) = rr.data() {
match map_dname(owner, dname, cname_name) {
Ok(name) => name,
Err(_) => {
return false;
}
}
} else {
panic!("DNAME expected");
};
if let AllRecordData::Cname(cname) = cname_rr.data() {
if cname.cname().to_name::<Bytes>() == result_name {
g.add_extra(cname_rr);
g.found_duplicate |= found_duplicate;
return true;
}
}
}
}
false
}
pub fn iter(&mut self) -> Iter<'_, Group> {
self.0.iter()
}
}
#[derive(Debug)]
pub struct ValidatedGroup {
rr_set: Vec<RrType>,
sig_set: Vec<SigType>,
extra_set: Vec<RrType>,
state: ValidationState,
signer_name: Name<Bytes>,
closest_encloser: Option<Name<Bytes>>,
ede: Option<ExtendedError<Vec<u8>>>,
adjust_ttl: Option<Ttl>,
found_duplicate: bool,
}
#[allow(clippy::too_many_arguments)]
impl ValidatedGroup {
fn new(
rr_set: Vec<RrType>,
sig_set: Vec<SigType>,
extra_set: Vec<RrType>,
state: ValidationState,
signer_name: Name<Bytes>,
closest_encloser: Option<Name<Bytes>>,
ede: Option<ExtendedError<Vec<u8>>>,
adjust_ttl: Option<Ttl>,
found_duplicate: bool,
) -> ValidatedGroup {
ValidatedGroup {
rr_set,
sig_set,
extra_set,
state,
signer_name,
closest_encloser,
ede,
adjust_ttl,
found_duplicate,
}
}
pub fn class(&self) -> Class {
if let Some(rr) = self.rr_set.first() {
return rr.class();
}
self.sig_set[0].class()
}
pub fn rtype(&self) -> Rtype {
if let Some(rr) = self.rr_set.first() {
return rr.rtype();
}
Rtype::RRSIG
}
pub fn owner(&self) -> Name<Bytes> {
if let Some(rr) = self.rr_set.first() {
return rr.owner().to_bytes();
}
self.sig_set[0].owner().to_bytes()
}
pub fn state(&self) -> ValidationState {
self.state
}
pub fn signer_name(&self) -> Name<Bytes> {
self.signer_name.clone()
}
pub fn closest_encloser(&self) -> Option<Name<Bytes>> {
self.closest_encloser.clone()
}
pub fn ede(&self) -> Option<ExtendedError<Vec<u8>>> {
self.ede.clone()
}
pub fn rr_set(&self) -> Vec<RrType> {
self.rr_set.clone()
}
pub fn sig_set(&self) -> Vec<SigType> {
self.sig_set.clone()
}
pub fn extra_set(&self) -> Vec<RrType> {
self.extra_set.clone()
}
pub fn adjust_ttl(&self) -> Option<Ttl> {
self.adjust_ttl
}
pub fn found_duplicate(&self) -> bool {
self.found_duplicate
}
}
#[allow(clippy::type_complexity)]
fn to_bytes_record(
rr: &ParsedRecord<'_, Bytes>,
) -> Result<Record<Name<Bytes>, AllRecordData<Bytes, ParsedName<Bytes>>>, Error>
{
let record = rr
.to_record::<AllRecordData<_, _>>()?
.expect("should not fail");
Ok(
Record::<Name<Bytes>, AllRecordData<Bytes, ParsedName<Bytes>>>::new(
rr.owner().to_name::<Bytes>(),
rr.class(),
rr.ttl(),
record.data().clone(),
),
)
}
#[derive(Eq, Hash, PartialEq)]
struct SigKey(Vec<u8>, Vec<u8>, Vec<u8>);
pub struct SigCache {
cache: Cache<SigKey, bool>,
}
impl SigCache {
pub fn new(size: u64) -> Self {
Self {
cache: Cache::new(size),
}
}
}