move existing test into RFC4035 section 3.2.2

This commit is contained in:
Jorge Aparicio 2024-05-10 15:07:54 +02:00
parent 8f414879b8
commit b6e97bf3b6
6 changed files with 101 additions and 55 deletions

View File

@ -1,4 +1,5 @@
//! DNSSEC functionality
mod fixtures;
mod rfc4035;
mod scenarios;

View File

@ -0,0 +1,50 @@
use std::net::Ipv4Addr;
use base64::prelude::*;
use dns_test::{
name_server::{Graph, NameServer, Sign},
record::Record,
Network, Resolver, Result, FQDN,
};
pub fn bad_signature_in_leaf_nameserver(
leaf_fqdn: &FQDN,
leaf_ipv4_addr: Ipv4Addr,
) -> Result<(Resolver, Graph)> {
assert_eq!(Some(FQDN::NAMESERVERS), leaf_fqdn.parent());
let network = Network::new()?;
let mut leaf_ns = NameServer::new(&dns_test::PEER, FQDN::NAMESERVERS, &network)?;
leaf_ns.add(Record::a(leaf_fqdn.clone(), leaf_ipv4_addr));
let graph = Graph::build(
leaf_ns,
Sign::AndAmend(&|zone, records| {
if zone == &FQDN::NAMESERVERS {
let mut modified = 0;
for record in records {
if let Record::RRSIG(rrsig) = record {
if rrsig.fqdn == *leaf_fqdn {
let mut signature = BASE64_STANDARD.decode(&rrsig.signature).unwrap();
let last = signature.last_mut().expect("empty signature");
*last = !*last;
rrsig.signature = BASE64_STANDARD.encode(&signature);
modified += 1;
}
}
}
assert_eq!(modified, 1, "sanity check");
}
}),
)?;
let trust_anchor = graph.trust_anchor.as_ref().unwrap();
let resolver = Resolver::new(&network, graph.root.clone())
.trust_anchor(trust_anchor)
.start(&dns_test::SUBJECT)?;
Ok((resolver, graph))
}

View File

@ -1,3 +1,5 @@
use std::net::Ipv4Addr;
use dns_test::{
client::{Client, DigSettings},
name_server::NameServer,
@ -6,6 +8,8 @@ use dns_test::{
Network, Resolver, Result, FQDN,
};
use crate::resolver::dnssec::fixtures;
#[test]
fn copies_cd_bit_from_query_to_response() -> Result<()> {
let network = &Network::new()?;
@ -21,3 +25,33 @@ fn copies_cd_bit_from_query_to_response() -> Result<()> {
Ok(())
}
#[test]
fn if_cd_bit_is_set_then_respond_with_data_that_fails_authentication() -> Result<()> {
let needle_fqdn = FQDN("example.nameservers.com.")?;
let needle_ipv4_addr = Ipv4Addr::new(1, 2, 3, 4);
let (resolver, _graph) =
fixtures::bad_signature_in_leaf_nameserver(&needle_fqdn, needle_ipv4_addr)?;
let resolver_addr = resolver.ipv4_addr();
let client = Client::new(resolver.network())?;
let settings = *DigSettings::default()
.recurse()
.authentic_data()
.checking_disabled();
let output = client.dig(settings, resolver_addr, RecordType::A, &needle_fqdn)?;
assert!(output.status.is_noerror());
assert!(!output.flags.authenticated_data);
let [record] = output.answer.try_into().unwrap();
let record = record.try_into_a().unwrap();
assert_eq!(needle_fqdn, record.fqdn);
assert_eq!(needle_ipv4_addr, record.ipv4_addr);
Ok(())
}

View File

@ -1,73 +1,29 @@
use std::net::Ipv4Addr;
use base64::prelude::*;
use dns_test::client::{Client, DigSettings};
use dns_test::name_server::{Graph, NameServer, Sign};
use dns_test::record::{Record, RecordType};
use dns_test::{Network, Resolver, Result, FQDN};
use dns_test::record::RecordType;
use dns_test::{Result, FQDN};
use crate::resolver::dnssec::fixtures;
// TODO find out which RFC section states this
#[ignore]
#[test]
fn bad_signature_in_leaf_nameserver() -> Result<()> {
let expected_ipv4_addr = Ipv4Addr::new(1, 2, 3, 4);
fn if_cd_bit_is_clear_and_data_is_not_authentic_then_respond_with_servfail() -> Result<()> {
let needle_fqdn = FQDN("example.nameservers.com.")?;
let needle_ipv4_addr = Ipv4Addr::new(1, 2, 3, 4);
let network = Network::new()?;
let (resolver, _graph) =
fixtures::bad_signature_in_leaf_nameserver(&needle_fqdn, needle_ipv4_addr)?;
let mut leaf_ns = NameServer::new(&dns_test::PEER, FQDN::NAMESERVERS, &network)?;
leaf_ns.add(Record::a(needle_fqdn.clone(), expected_ipv4_addr));
let Graph {
nameservers: _nameservers,
root,
trust_anchor,
} = Graph::build(
leaf_ns,
Sign::AndAmend(&|zone, records| {
if zone == &FQDN::NAMESERVERS {
let mut modified = 0;
for record in records {
if let Record::RRSIG(rrsig) = record {
if rrsig.fqdn == needle_fqdn {
let mut signature = BASE64_STANDARD.decode(&rrsig.signature).unwrap();
let last = signature.last_mut().expect("empty signature");
*last = !*last;
rrsig.signature = BASE64_STANDARD.encode(&signature);
modified += 1;
}
}
}
assert_eq!(modified, 1, "sanity check");
}
}),
)?;
let trust_anchor = &trust_anchor.unwrap();
let resolver = Resolver::new(&network, root)
.trust_anchor(trust_anchor)
.start(&dns_test::SUBJECT)?;
let resolver_addr = resolver.ipv4_addr();
let client = Client::new(&network)?;
let client = Client::new(resolver.network())?;
let mut settings = *DigSettings::default().recurse().authentic_data();
let settings = *DigSettings::default().recurse().authentic_data();
let output = client.dig(settings, resolver_addr, RecordType::A, &needle_fqdn)?;
// the resolver will try to validate the chain of trust; the validation fails so it responds
// with SERVFAIL
assert!(output.status.is_servfail());
// avoids a SERVFAIL response
settings.checking_disabled();
let output = client.dig(settings, resolver_addr, RecordType::A, &needle_fqdn)?;
// when the CD (Checking Disabled) bit is set the server won't respond with SERVFAIL on
// validation errors. the outcome of the validation process is reported in the AD bit
assert!(output.status.is_noerror());
assert!(!output.flags.authenticated_data);
Ok(())
}

View File

@ -31,6 +31,10 @@ impl Resolver {
self.container.eavesdrop()
}
pub fn network(&self) -> &Network {
self.container.network()
}
pub fn container_id(&self) -> &str {
self.container.id()
}

View File

@ -92,6 +92,7 @@ impl FromStr for ZoneFile {
}
/// A root (server) hint
#[derive(Clone)]
pub struct Root {
pub ipv4_addr: Ipv4Addr,
pub ns: FQDN,