Skip to content

Commit

Permalink
Merge f118cdc into 2e04575
Browse files Browse the repository at this point in the history
  • Loading branch information
bluejekyll committed Apr 24, 2017
2 parents 2e04575 + f118cdc commit 1d22738
Show file tree
Hide file tree
Showing 11 changed files with 1,343 additions and 316 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Added `From<IpAddr>` for Name (reverse DNS) #105
- AppVeyor support #103
- rustls client tls support (seperate crate)
- full support for KEY RR in client

### Changed
- Fixed TLS documentation, added more elsewhere, docs required; fixes #102
Expand Down
105 changes: 63 additions & 42 deletions client/src/client/secure_client_handle.rs
Expand Up @@ -103,10 +103,7 @@ impl<H> ClientHandle for SecureClientHandle<H>
if let OpCode::Query = message.op_code() {
// This will panic on no queries, that is a very odd type of request, isn't it?
// TODO: there should only be one
let query = message.queries()
.first()
.cloned()
.unwrap();
let query = message.queries().first().cloned().unwrap();
let client: SecureClientHandle<H> = self.clone_with_context();

// TODO: cache response of the server about understood algorithms
Expand Down Expand Up @@ -136,22 +133,27 @@ impl<H> ClientHandle for SecureClientHandle<H>

message.set_authentic_data(true);
message.set_checking_disabled(false);
let dns_class = message.queries().first().map_or(DNSClass::IN, |q| q.query_class());
let dns_class = message
.queries()
.first()
.map_or(DNSClass::IN, |q| q.query_class());

return Box::new(self.client
.send(message)
.and_then(move |message_response| {
// group the record sets by name and type
// each rrset type needs to validated independently
debug!("validating message_response: {}", message_response.id());
verify_rrsets(client, message_response, dns_class)
})
// group the record sets by name and type
// each rrset type needs to validated independently
debug!("validating message_response: {}",
message_response.id());
verify_rrsets(client, message_response, dns_class)
})
.and_then(move |verified_message| {
// at this point all of the message is verified.
// This is where NSEC (and possibly NSEC3) validation occurs
// As of now, only NSEC is supported.
if verified_message.answers().is_empty() {
let nsecs = verified_message.name_servers()
let nsecs = verified_message
.name_servers()
.iter()
.filter(|rr| rr.rr_type() == RecordType::NSEC)
.collect::<Vec<_>>();
Expand Down Expand Up @@ -223,15 +225,17 @@ fn verify_rrsets<H>(client: SecureClientHandle<H>,
Vec::with_capacity(rrset_types.len());
for (name, record_type) in rrset_types {
// TODO: should we evaluate the different sections (answers and name_servers) separately?
let rrset: Vec<Record> = message_result.answers()
let rrset: Vec<Record> = message_result
.answers()
.iter()
.chain(message_result.name_servers())
.chain(message_result.additionals())
.filter(|rr| rr.rr_type() == record_type && rr.name() == &name)
.cloned()
.collect();

let rrsigs: Vec<Record> = message_result.answers()
let rrsigs: Vec<Record> = message_result
.answers()
.iter()
.chain(message_result.name_servers())
.chain(message_result.additionals())
Expand Down Expand Up @@ -292,7 +296,8 @@ impl Future for VerifyRrsetsFuture {
debug!("an rrset was verified: {}, {:?}",
rrset.name,
rrset.record_type);
self.verified_rrsets.insert((rrset.name, rrset.record_type));
self.verified_rrsets
.insert((rrset.name, rrset.record_type));
remaining
}
// TODO, should we return the Message on errors? Allow the consumer to decide what to do
Expand All @@ -318,27 +323,30 @@ impl Future for VerifyRrsetsFuture {
// TODO: does the section in the message matter here?
// we could probably end up with record_types in any section.
// track the section in the rrset evaluation?
let answers = message_result.take_answers()
let answers = message_result
.take_answers()
.into_iter()
.filter(|record| {
self.verified_rrsets.contains(&(record.name().clone(),
record.rr_type()))
self.verified_rrsets
.contains(&(record.name().clone(), record.rr_type()))
})
.collect::<Vec<Record>>();

let name_servers = message_result.take_name_servers()
let name_servers = message_result
.take_name_servers()
.into_iter()
.filter(|record| {
self.verified_rrsets.contains(&(record.name().clone(),
record.rr_type()))
self.verified_rrsets
.contains(&(record.name().clone(), record.rr_type()))
})
.collect::<Vec<Record>>();

let additionals = message_result.take_additionals()
let additionals = message_result
.take_additionals()
.into_iter()
.filter(|record| {
self.verified_rrsets.contains(&(record.name().clone(),
record.rr_type()))
self.verified_rrsets
.contains(&(record.name().clone(), record.rr_type()))
})
.collect::<Vec<Record>>();

Expand Down Expand Up @@ -413,7 +421,8 @@ fn verify_dnskey_rrset<H>(mut client: SecureClientHandle<H>,

// check the DNSKEYS against the trust_anchor, if it's approved allow it.
{
let anchored_keys = rrset.records
let anchored_keys = rrset
.records
.iter()
.enumerate()
.filter(|&(_, rr)| rr.rr_type() == RecordType::DNSKEY)
Expand Down Expand Up @@ -442,9 +451,11 @@ fn verify_dnskey_rrset<H>(mut client: SecureClientHandle<H>,
}

// need to get DS records for each DNSKEY
let valid_dnskey = client.query(rrset.name.clone(), rrset.record_class, RecordType::DS)
let valid_dnskey = client
.query(rrset.name.clone(), rrset.record_class, RecordType::DS)
.and_then(move |ds_message| {
let valid_keys = rrset.records
let valid_keys = rrset
.records
.iter()
.enumerate()
.filter(|&(_, rr)| rr.rr_type() == RecordType::DNSKEY)
Expand Down Expand Up @@ -559,13 +570,15 @@ fn verify_default_rrset<H>(client: SecureClientHandle<H>,
rrset.record_type);

// Special case for self-signed DNSKEYS, validate with itself...
if rrsigs.iter().filter(|rrsig| rrsig.rr_type() == RecordType::RRSIG).any(|rrsig| {
if let &RData::SIG(ref sig) = rrsig.rdata() {
return RecordType::DNSKEY == rrset.record_type && sig.signer_name() == &rrset.name;
} else {
panic!("expected a SIG here");
}
}) {
if rrsigs
.iter()
.filter(|rrsig| rrsig.rr_type() == RecordType::RRSIG)
.any(|rrsig| if let &RData::SIG(ref sig) = rrsig.rdata() {
return RecordType::DNSKEY == rrset.record_type &&
sig.signer_name() == &rrset.name;
} else {
panic!("expected a SIG here");
}) {
// in this case it was looks like a self-signed key, first validate the signature
// then return rrset. Like the standard case below, the DNSKEY is validated
// after this function. This function is only responsible for validating the signature
Expand Down Expand Up @@ -689,16 +702,19 @@ fn verify_rrset_with_dnskey(dnskey: &DNSKEY, sig: &SIG, rrset: &Rrset) -> Client
}
let pkey = pkey.unwrap();

let signer: Signer = Signer::new_verifier(*dnskey.algorithm(),
pkey,
sig.signer_name().clone(),
dnskey.zone_key(),
false);
let signer: Signer = Signer::dnssec_verifier(dnskey.clone(),
*dnskey.algorithm(),
pkey,
sig.signer_name().clone(),
dnskey.zone_key(),
false);

signer.hash_rrset_with_sig(&rrset.name, rrset.record_class, sig, &rrset.records)
signer
.hash_rrset_with_sig(&rrset.name, rrset.record_class, sig, &rrset.records)
.map_err(|e| e.into())
.and_then(|rrset_hash| {
signer.verify(&rrset_hash, sig.sig())
signer
.verify(&rrset_hash, sig.sig())
.map(|_| {
debug!("verified rrset: {}, type: {:?}",
rrset.name,
Expand Down Expand Up @@ -773,7 +789,9 @@ fn verify_nsec(query: &Query, nsecs: Vec<&Record>) -> bool {
// if they are, then the query_type should not exist in the NSEC record.
// if we got an NSEC record of the same name, but it is listed in the NSEC types,
// WTF? is that bad server, bad record
if nsecs.iter().any(|r| {
if nsecs
.iter()
.any(|r| {
query.name() == r.name() &&
{
if let &RData::NSEC(ref rdata) = r.rdata() {
Expand All @@ -787,7 +805,10 @@ fn verify_nsec(query: &Query, nsecs: Vec<&Record>) -> bool {
}

// based on the WTF? above, we will ignore any NSEC records of the same name
if nsecs.iter().filter(|r| query.name() != r.name()).any(|r| {
if nsecs
.iter()
.filter(|r| query.name() != r.name())
.any(|r| {
query.name() > r.name() &&
{
if let &RData::NSEC(ref rdata) = r.rdata() {
Expand Down
3 changes: 3 additions & 0 deletions client/src/error/dnssec_error.rs
Expand Up @@ -24,6 +24,8 @@ use ring::error::Unspecified;
#[cfg(not(feature = "ring"))]
use self::not_ring::Unspecified;

use error::{EncodeError, EncodeErrorKind};

error_chain! {
// The type defined for this error. These are the conventional
// and recommended names, but they can be arbitrarily chosen.
Expand All @@ -39,6 +41,7 @@ error_chain! {
//
// This section can be empty.
links {
EncodeError, EncodeErrorKind, Encode;
}

// Automatic conversions between this error chain and other
Expand Down
52 changes: 44 additions & 8 deletions client/src/rr/dnssec/signer.rs
Expand Up @@ -26,7 +26,7 @@ use rr::{DNSClass, Name, Record, RecordType, RData};
use rr::dnssec::{Algorithm, DigestType, DnsSecErrorKind, DnsSecResult};
use rr::dnssec::KeyPair;
#[cfg(any(feature = "openssl", feature = "ring"))]
use rr::rdata::{sig, SIG};
use rr::rdata::{DNSKEY, KEY, sig, SIG};
#[cfg(any(feature = "openssl", feature = "ring"))]
use serialize::binary::{BinEncoder, BinSerializable, EncodeMode};

Expand Down Expand Up @@ -231,6 +231,8 @@ use serialize::binary::{BinEncoder, BinSerializable, EncodeMode};
/// ```
#[cfg(any(feature = "openssl", feature = "ring"))]
pub struct Signer {
// TODO: this should really be a trait and generic struct over KEY and DNSKEY
key_rdata: RData,
key: KeyPair,
algorithm: Algorithm,
signer_name: Name,
Expand All @@ -246,13 +248,34 @@ pub struct Signer;
#[cfg(any(feature = "openssl", feature = "ring"))]
impl Signer {
/// Version of Signer for verifying RRSIGs and SIG0 records.
pub fn new_verifier(algorithm: Algorithm,
key: KeyPair,
signer_name: Name,
is_zone_signing_key: bool,
is_zone_update_auth: bool)
-> Self {
pub fn dnssec_verifier(key_rdata: DNSKEY,
algorithm: Algorithm,
key: KeyPair,
signer_name: Name,
is_zone_signing_key: bool,
is_zone_update_auth: bool)
-> Self {
Signer {
key_rdata: key_rdata.into(),
key: key,
algorithm: algorithm,
signer_name: signer_name,
sig_duration: Duration::zero(),
is_zone_signing_key: is_zone_signing_key,
is_zone_update_auth: is_zone_update_auth,
}
}

/// Version of Signer for verifying RRSIGs and SIG0 records.
pub fn sig0_verifier(key_rdata: KEY,
algorithm: Algorithm,
key: KeyPair,
signer_name: Name,
is_zone_signing_key: bool,
is_zone_update_auth: bool)
-> Self {
Signer {
key_rdata: key_rdata.into(),
key: key,
algorithm: algorithm,
signer_name: signer_name,
Expand All @@ -263,14 +286,20 @@ impl Signer {
}

/// Version of Signer for signing RRSIGs and SIG0 records.
#[deprecated="use SIG0 or DNSSec constructors"]
pub fn new(algorithm: Algorithm,
key: KeyPair,
signer_name: Name,
sig_duration: Duration,
is_zone_signing_key: bool,
is_zone_update_auth: bool)
-> Self {
let dnskey =
key.to_dnskey(algorithm)
.expect("something went wrong, use one of the SIG0 or DNSSec constructors");

Signer {
key_rdata: dnskey.into(),
key: key,
algorithm: algorithm,
signer_name: signer_name,
Expand Down Expand Up @@ -368,7 +397,14 @@ impl Signer {
pub fn calculate_key_tag(&self) -> DnsSecResult<u16> {
let mut ac: usize = 0;

for (i, k) in try!(self.key.to_public_bytes()).iter().enumerate() {
// TODO:
let mut bytes: Vec<u8> = Vec::with_capacity(512);
{
let mut e = BinEncoder::new(&mut bytes);
try!(self.key_rdata.emit(&mut e));
}

for (i, k) in bytes.iter().enumerate() {
ac += if i & 0x0001 == 0x0001 {
*k as usize
} else {
Expand Down

0 comments on commit 1d22738

Please sign in to comment.