secure_client_future complete

This commit is contained in:
Benjamin Fry 2016-10-25 00:09:47 -07:00
parent 8c4466bc64
commit 34519e24ba
6 changed files with 320 additions and 127 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
.vscode/
# Compiled files
*.o
*.so

View File

@ -21,7 +21,8 @@ mod client;
mod client_connection;
mod client_future;
mod secure_client_future;
mod select_any;
mod select_all;
mod select_ok;
pub use self::client::Client;
pub use self::client_connection::ClientConnection;

View File

@ -5,15 +5,16 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::cell::RefCell;
use std::clone::Clone;
use std::collections::HashSet;
use std::mem;
use std::rc::Rc;
use futures::*;
use futures::{Async, done, failed, finished, Future, Poll};
use ::client::{BasicClientHandle, ClientHandle};
use ::client::select_any::select_any;
use ::client::select_all::{select_all, SelectAll};
use ::client::select_ok::select_ok;
use ::error::*;
use ::op::{Message, OpCode, Query, ResponseCode};
use ::rr::{domain, DNSClass, RData, Record, RecordType};
@ -32,12 +33,12 @@ struct Rrset {
/// A ClientHandle which will return DNSSec validating futures.
///
/// This wraps a ClientHandle, changing the implementation `send()` to validate all
/// message responses for Query operations. Update operations are not validated.
/// message responses for Query operations. Update operation responses are not validated by
/// this process.
pub struct SecureClientHandle {
client: BasicClientHandle,
trust_anchor: Rc<TrustAnchor>,
request_depth: usize,
active_validations: Rc<RefCell<HashSet<(domain::Name, RecordType, DNSClass)>>>,
}
impl SecureClientHandle {
@ -63,19 +64,17 @@ impl SecureClientHandle {
client: client,
trust_anchor: Rc::new(trust_anchor),
request_depth: 0,
active_validations: Rc::new(RefCell::new(HashSet::new()))
}
}
/// An internal function used to clone the client, but maintain some information back to the
/// original client, such as the set of active_validations such that infinite recurssion does
/// original client, such as the request_depth such that infinite recurssion does
/// not occur.
fn clone_with_context(&self) -> Self {
SecureClientHandle {
client: self.client.clone(),
trust_anchor: self.trust_anchor.clone(),
request_depth: self.request_depth + 1,
active_validations: self.active_validations.clone()
}
}
}
@ -93,8 +92,6 @@ impl ClientHandle for SecureClientHandle {
// TODO: there should only be one
let query = message.get_queries().first().cloned().unwrap();
let client: SecureClientHandle = self.clone_with_context();
let active_validations = self.active_validations.clone();
let request_depth = self.request_depth;
{
let edns = message.get_edns_mut();
@ -111,40 +108,25 @@ impl ClientHandle for SecureClientHandle {
// group the record sets by name and type
// each rrset type needs to validated independently
debug!("validating message_response: {}", message_response.get_id());
VerifyRrsetsFuture::new(
client,
message_response,
dns_class,
)
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.get_response_code() == ResponseCode::NXDomain ||
verified_message.get_answers().is_empty() {
if verified_message.get_response_code() == ResponseCode::NXDomain {
let nsecs = verified_message.get_name_servers()
.iter()
.filter(|rr| rr.get_rr_type() == RecordType::NSEC)
.collect::<Vec<_>>();
if !verify_nsec(&query, nsecs) {
// FIXME change this to remove the NSECs, like we do for the others?
return Err(ClientErrorKind::Message("could not validate nxdomain with NSEC").into())
}
}
Ok(verified_message)
})
.then(move |verified_message| {
// TODO: this feels dirty, is there a cleaner way?
// if our request depth is zero then this is the top of the stack, clear
// active_validations
if request_depth == 0 {
active_validations.borrow_mut().clear();
}
verified_message
})
)
}
@ -153,80 +135,86 @@ impl ClientHandle for SecureClientHandle {
}
/// A future to verify all RRSets in a returned Message.
pub struct VerifyRrsetsFuture {
struct VerifyRrsetsFuture {
message_result: Option<Message>,
rrsets: Collect<Vec<Box<Future<Item=Rrset, Error=ClientError>>>>,
rrsets: SelectAll<Box<Future<Item=Rrset, Error=ClientError>>>,
verified_rrsets: HashSet<(domain::Name, RecordType)>,
}
impl VerifyRrsetsFuture {
/// this pulls all records returned in a Message respons and returns a future which will
/// validate all of them.
fn new(
client: SecureClientHandle,
message_result: Message,
dns_class: DNSClass,
) -> VerifyRrsetsFuture {
let mut rrset_types: HashSet<(domain::Name, RecordType)> = HashSet::new();
for rrset in message_result.get_answers()
.iter()
.chain(message_result.get_name_servers())
.filter(|rr| rr.get_rr_type() != RecordType::RRSIG)
.map(|rr| (rr.get_name().clone(), rr.get_rr_type())) {
rrset_types.insert(rrset);
}
// collect all the rrsets to verify
// TODO: is there a way to get rid of this clone() safely?
let mut rrsets = Vec::with_capacity(rrset_types.len());
for (name, record_type) in rrset_types {
// if there is already an active validation going on, assume the other validation will
// complete properly or error if it is invalid
let request_key = (name.clone(), record_type, dns_class);
if client.active_validations.borrow().contains(&request_key) {
debug!("skipping active validation: {}, {:?}, {:?}", name, record_type, dns_class);
continue
}
let rrset: Vec<Record> = message_result.get_answers()
.iter()
.chain(message_result.get_name_servers())
.filter(|rr| rr.get_rr_type() == record_type &&
rr.get_name() == &name)
.cloned()
.collect();
let rrsigs: Vec<Record> = message_result.get_answers()
.iter()
.chain(message_result.get_name_servers())
.filter(|rr| rr.get_rr_type() == RecordType::RRSIG)
.filter(|rr| if let &RData::SIG(ref rrsig) = rr.get_rdata() {
rrsig.get_type_covered() == record_type
} else {
false
})
.cloned()
.collect();
// TODO: support non-IN classes?
debug!("verifying: {}, record_type: {:?}", name, record_type);
let rrset = Rrset { name: name, record_type: record_type, record_class: dns_class, records: rrset };
rrsets.push(verify_rrset(client.clone_with_context(), rrset, rrsigs));
client.active_validations.borrow_mut().insert(request_key);
// rrsets.push(VerifyRrsetFuture{ client: client.clone(), name: name, record_type: record_type,
// record_class: DNSClass::IN, rrset: rrset, rrsigs: rrsigs });
}
// spawn a select_all over this vec, these are the individual RRSet validators
let rrsets_to_verify = collect(rrsets);
// return the full Message validator
VerifyRrsetsFuture{
message_result: Some(message_result),
rrsets: rrsets_to_verify,
}
/// this pulls all records returned in a Message respons and returns a future which will
/// validate all of them.
fn verify_rrsets(
client: SecureClientHandle,
message_result: Message,
dns_class: DNSClass,
) -> Box<Future<Item=Message, Error=ClientError>> {
let mut rrset_types: HashSet<(domain::Name, RecordType)> = HashSet::new();
for rrset in message_result.get_answers()
.iter()
.chain(message_result.get_name_servers())
.filter(|rr| rr.get_rr_type() != RecordType::RRSIG)
.map(|rr| (rr.get_name().clone(), rr.get_rr_type())) {
rrset_types.insert(rrset);
}
}
// there was no data returned in that message
if rrset_types.is_empty() {
let mut message_result = message_result;
// there were no returned results, double check by dropping all the results
message_result.take_answers();
message_result.take_name_servers();
message_result.take_additionals();
return Box::new(failed(ClientErrorKind::Message("no results to verify").into()))
}
// collect all the rrsets to verify
// TODO: is there a way to get rid of this clone() safely?
let mut rrsets: Vec<Box<Future<Item=Rrset, Error=ClientError>>> = 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.get_answers()
.iter()
.chain(message_result.get_name_servers())
//.chain(message_result.get_additionals())
.filter(|rr| rr.get_rr_type() == record_type &&
rr.get_name() == &name)
.cloned()
.collect();
let rrsigs: Vec<Record> = message_result.get_answers()
.iter()
.chain(message_result.get_name_servers())
//.chain(message_result.get_additionals())
.filter(|rr| rr.get_rr_type() == RecordType::RRSIG)
.filter(|rr| if let &RData::SIG(ref rrsig) = rr.get_rdata() {
rrsig.get_type_covered() == record_type
} else {
false
})
.cloned()
.collect();
// if there is already an active validation going on, assume the other validation will
// complete properly or error if it is invalid
let rrset = Rrset { name: name, record_type: record_type, record_class: dns_class, records: rrset };
// TODO: support non-IN classes?
debug!("verifying: {}, record_type: {:?}, rrsigs: {}", rrset.name, record_type, rrsigs.len());
rrsets.push(verify_rrset(client.clone_with_context(), rrset, rrsigs));
}
// spawn a select_all over this vec, these are the individual RRSet validators
let rrsets_to_verify = select_all(rrsets);
// return the full Message validator
Box::new(VerifyRrsetsFuture{
message_result: Some(message_result),
rrsets: rrsets_to_verify,
verified_rrsets: HashSet::new(),
})
}
impl Future for VerifyRrsetsFuture {
type Item = Message;
@ -237,20 +225,64 @@ impl Future for VerifyRrsetsFuture {
return Err(ClientErrorKind::Message("message is none").into())
}
// TODO: Can we do this in parallel?
// HINT: use select_all
// FIXME: strip unvalidated records from the message
match self.rrsets.poll() {
Ok(Async::NotReady) => Ok(Async::NotReady),
// all rrsets verified! woop!
Ok(Async::Ready(_)) => {
let message_result = mem::replace(&mut self.message_result, None);
Ok(Async::Ready(message_result.unwrap())) // validated not none above...
},
// TODO, should we return the Message on errors? Allow the consumer to decide what to do
// on a validation failure?
// any error, is an error for all
Err(e) => Err(e),
// loop through all the rrset evaluations, filter all the rrsets in the Message
// down to just the ones that were able to be validated
loop {
let remaining = match self.rrsets.poll() {
// one way the loop will stop, nothing is ready...
Ok(Async::NotReady) => return Ok(Async::NotReady),
// all rrsets verified! woop!
Ok(Async::Ready((rrset, _, remaining))) => {
debug!("an rrset was verified: {}, {:?}", 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
// on a validation failure?
// any error, is an error for all
Err((e, _, remaining)) => {
debug!("an rrset failed to verify: {}", e);
if remaining.is_empty() { return Err(e) }
remaining
},
};
if !remaining.is_empty() {
// continue the evaluation
drop(mem::replace(&mut self.rrsets, select_all(remaining)));
} else {
// validated not none above...
let mut message_result = mem::replace(&mut self.message_result, None).unwrap();
// take all the rrsets from the Message, filter down each set to the validated rrsets
// FIXME: 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()
.into_iter()
.filter(|record| self.verified_rrsets.contains(&(record.get_name().clone(), record.get_rr_type())))
.collect::<Vec<Record>>();
let name_servers = message_result.take_name_servers()
.into_iter()
.filter(|record| self.verified_rrsets.contains(&(record.get_name().clone(), record.get_rr_type())))
.collect::<Vec<Record>>();
// FIXME: add additional validation...
// let additionals = message_result.take_additionals()
// .into_iter()
// .filter(|record| self.verified_rrsets.contains(&(record.get_name().clone(), record.get_rr_type())))
// .collect::<Vec<Record>>();
// add the filtered records back to the message
message_result.insert_answers(answers);
message_result.insert_name_servers(name_servers);
// message_result.insert_additionals(additionals);
// breaks out of the loop... and returns the filtered Message.
return Ok(Async::Ready(message_result))
}
}
}
}
@ -270,9 +302,9 @@ fn verify_rrset(client: SecureClientHandle,
// Special case for unsigned DNSKEYs, it's valid for a DNSKEY to be bare in the zone if
// it's a trust_anchor, though some DNS servers choose to self-sign in this case,
// for self-signed KEYS they will drop through to the standard validation logic.
if rrsigs.is_empty() {
debug!("unsigned key: {}, {:?}", rrset.name, rrset.record_type);
if let RecordType::DNSKEY = rrset.record_type {
if let RecordType::DNSKEY = rrset.record_type {
if rrsigs.is_empty() {
debug!("unsigned key: {}, {:?}", rrset.name, rrset.record_type);
return verify_dnskey_rrset(client.clone_with_context(), rrset)
}
}
@ -496,6 +528,55 @@ fn verify_default_rrset(
// the record set is going to be shared across a bunch of futures, Rc for that.
let rrset = Rc::new(rrset);
debug!("default validation {}, record_type: {:?}", rrset.name, rrset.record_type);
// Special case for self-signed DNSKEYS, validate with itself...
if rrsigs.iter()
.filter(|rrsig| rrsig.get_rr_type() == RecordType::RRSIG)
.any(|rrsig|
if let &RData::SIG(ref sig) = rrsig.get_rdata() {
return RecordType::DNSKEY == rrset.record_type && sig.get_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
// the DNSKey validation should come after, see verify_rrset().
return Box::new(done(
rrsigs.into_iter()
// this filter is technically unnecessary, can probably remove it...
.filter(|rrsig| rrsig.get_rr_type() == RecordType::RRSIG)
.map(|rrsig|
if let RData::SIG(sig) = rrsig.unwrap_rdata() {
// setting up the context explicitly.
sig
} else {
panic!("expected a SIG here");
}
)
.filter_map(|sig| {
let rrset = rrset.clone();
if rrset.records.iter()
.any(|r| {
if let &RData::DNSKEY(ref dnskey) = r.get_rdata() {
verify_rrset_with_dnskey(dnskey, &sig, &rrset).is_ok()
} else {
panic!("expected a DNSKEY here: {:?}", r.get_rdata());
}
}) {
Some(rrset)
} else {
None
}
})
.next()
.ok_or(ClientErrorKind::Message("self-signed dnskey is invalid").into())
).map(move |rrset| Rc::try_unwrap(rrset).expect("unable to unwrap Rc"))
)
}
// we can validate with any of the rrsigs...
// i.e. the first that validates is good enough
// FIXME: could there be a cert downgrade attack here?
@ -521,7 +602,7 @@ fn verify_default_rrset(
client.query(sig.get_signer_name().clone(), rrset.record_class, RecordType::DNSKEY)
.and_then(move |message|
// FIXME: only use validated DNSKEYs
// DNSKEYs are validated by the inner query
message.get_answers()
.iter()
.filter(|r| r.get_rr_type() == RecordType::DNSKEY)
@ -544,7 +625,7 @@ fn verify_default_rrset(
}
// as long as any of the verifcations is good, then the RRSET is valid.
let select = select_any(verifications)
let select = select_ok(verifications)
// getting here means at least one of the rrsigs succeeded...
.map(move |(rrset, rest)| {
drop(rest); // drop all others, should free up Rc
@ -699,6 +780,7 @@ pub mod test {
println!("response records: {:?}", response);
assert!(response.get_edns().expect("edns not here").is_dnssec_ok());
assert!(!response.get_answers().is_empty());
let record = &response.get_answers()[0];
assert_eq!(record.get_name(), &name);
assert_eq!(record.get_rr_type(), RecordType::A);

68
src/client/select_all.rs Normal file
View File

@ -0,0 +1,68 @@
use std::mem;
use futures::{Future, IntoFuture, Poll, Async};
// FIXME: delete this file once futures 0.1.3 completes.
/// Future for the `select_all` combinator, waiting for one of any of a list of
/// futures to complete.
///
/// This is created by this `select_all` function.
#[must_use = "futures do nothing unless polled"]
pub struct SelectAll<A> where A: Future {
inner: Vec<A>,
}
#[doc(hidden)]
pub type SelectAllNext<A> = A;
/// Creates a new future which will select over a list of futures.
///
/// The returned future will wait for any future within `list` to be ready. Upon
/// completion or failure the item resolved will be returned, along with the
/// index of the future that was ready and the list of all the remaining
/// futures.
///
/// # Panics
///
/// This function will panic if the iterator specified contains no items.
pub fn select_all<I>(iter: I) -> SelectAll<<I::Item as IntoFuture>::Future>
where I: IntoIterator,
I::Item: IntoFuture,
{
let ret = SelectAll {
inner: iter.into_iter()
.map(|a| a.into_future())
.collect(),
};
assert!(ret.inner.len() > 0);
ret
}
impl<A> Future for SelectAll<A>
where A: Future,
{
type Item = (A::Item, usize, Vec<A>);
type Error = (A::Error, usize, Vec<A>);
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let item = self.inner.iter_mut().enumerate().filter_map(|(i, f)| {
match f.poll() {
Ok(Async::NotReady) => None,
Ok(Async::Ready(e)) => Some((i, Ok(e))),
Err(e) => Some((i, Err(e))),
}
}).next();
match item {
Some((idx, res)) => {
self.inner.remove(idx);
let rest = mem::replace(&mut self.inner, Vec::new());
match res {
Ok(e) => Ok(Async::Ready((e, idx, rest))),
Err(e) => Err((e, idx, rest)),
}
}
None => Ok(Async::NotReady),
}
}
}

View File

@ -1,15 +1,15 @@
use std::mem;
use futures::{Future, IntoFuture, Poll, Async};
// TODO: drop this inner class once Futures.rs gets the final impl which can replace this.
// FIXME: delete this file once futures 0.1.3 completes.
/// Future for the `select_any` combinator, waiting for one of any of a list of
/// Future for the `select_ok` combinator, waiting for one of any of a list of
/// futures to succesfully complete. unlike `select_all`, this future ignores all
/// but the last error, if there are any.
///
/// This is created by this `select_any` function.
/// This is created by this `select_ok` function.
#[must_use = "futures do nothing unless polled"]
pub struct SelectAny<A> where A: Future {
pub struct SelectOk<A> where A: Future {
inner: Vec<A>,
}
@ -23,11 +23,11 @@ pub struct SelectAny<A> where A: Future {
/// # Panics
///
/// This function will panic if the iterator specified contains no items.
pub fn select_any<I>(iter: I) -> SelectAny<<I::Item as IntoFuture>::Future>
pub fn select_ok<I>(iter: I) -> SelectOk<<I::Item as IntoFuture>::Future>
where I: IntoIterator,
I::Item: IntoFuture,
{
let ret = SelectAny {
let ret = SelectOk {
inner: iter.into_iter()
.map(|a| a.into_future())
.collect(),
@ -36,7 +36,7 @@ pub fn select_any<I>(iter: I) -> SelectAny<<I::Item as IntoFuture>::Future>
ret
}
impl<A> Future for SelectAny<A> where A: Future {
impl<A> Future for SelectOk<A> where A: Future {
type Item = (A::Item, Vec<A>);
type Error = A::Error;

View File

@ -17,6 +17,7 @@
//! Basic protocol message for DNS
use std::fmt::Debug;
use std::mem;
use ::error::*;
use ::rr::resource::Record;
@ -128,6 +129,17 @@ impl Message {
}
self
}
/// Sets the answers to the specified set of Records.
///
/// # Panics
///
/// Will panic if answer records are already associated to the message.
pub fn insert_answers(&mut self, records: Vec<Record>) {
assert!(self.answers.is_empty());
self.answers = records;
}
pub fn add_name_server(&mut self, record: Record) -> &mut Self { self.name_servers.push(record); self }
pub fn add_all_name_servers(&mut self, vector: &[&Record]) -> &mut Self {
for &r in vector {
@ -137,7 +149,29 @@ impl Message {
}
self
}
/// Sets the name_servers to the specified set of Records.
///
/// # Panics
///
/// Will panic if name_servers records are already associated to the message.
pub fn insert_name_servers(&mut self, records: Vec<Record>) {
assert!(self.name_servers.is_empty());
self.name_servers = records;
}
pub fn add_additional(&mut self, record: Record) -> &mut Self { self.additionals.push(record); self }
/// Sets the additional to the specified set of Records.
///
/// # Panics
///
/// Will panic if additional records are already associated to the message.
pub fn insert_additionals(&mut self, records: Vec<Record>) {
assert!(self.additionals.is_empty());
self.additionals = records;
}
pub fn set_edns(&mut self, edns: Edns) -> &mut Self { self.edns = Some(edns); self }
pub fn add_sig0(&mut self, record: Record) -> &mut Self {
@ -189,6 +223,8 @@ impl Message {
/// ```
pub fn get_answers(&self) -> &[Record] { &self.answers }
pub fn take_answers(&mut self) -> Vec<Record> { mem::replace(&mut self.answers, vec![]) }
/// ```text
/// Authority Carries RRs which describe other authoritative servers.
/// May optionally carry the SOA RR for the authoritative
@ -196,11 +232,15 @@ impl Message {
/// ```
pub fn get_name_servers(&self) -> &[Record] { &self.name_servers }
pub fn take_name_servers(&mut self) -> Vec<Record> { mem::replace(&mut self.name_servers, vec![]) }
/// ```text
/// Additional Carries RRs which may be helpful in using the RRs in the
/// other sections.
/// ```
pub fn get_additional(&self) -> &[Record] { &self.additionals }
pub fn get_additionals(&self) -> &[Record] { &self.additionals }
pub fn take_additionals(&mut self) -> Vec<Record> { mem::replace(&mut self.additionals, vec![]) }
/// [RFC 6891, EDNS(0) Extensions, April 2013](https://tools.ietf.org/html/rfc6891#section-6.1.1)
///
@ -422,7 +462,7 @@ pub trait UpdateMessage: Debug {
fn get_zones(&self) -> &[Query];
fn get_pre_requisites(&self) -> &[Record];
fn get_updates(&self) -> &[Record];
fn get_additional(&self) -> &[Record];
fn get_additionals(&self) -> &[Record];
/// This is used to authenticate update messages.
///
@ -446,7 +486,7 @@ impl UpdateMessage for Message {
fn get_zones(&self) -> &[Query] { self.get_queries() }
fn get_pre_requisites(&self) -> &[Record] { self.get_answers() }
fn get_updates(&self) -> &[Record] { self.get_name_servers() }
fn get_additional(&self) -> &[Record] { self.get_additional() }
fn get_additionals(&self) -> &[Record] { self.get_additionals() }
fn get_sig0(&self) -> &[Record] { self.get_sig0() }