Skip to content

Commit

Permalink
add a method to collect the DNS names from a certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
Geal committed Jul 26, 2018
1 parent 68fcf3d commit baa2fec
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 3 deletions.
27 changes: 24 additions & 3 deletions src/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ use std;
#[cfg(feature = "std")]
use std::string::String;

#[cfg(feature = "std")]
use std::vec::Vec;

/// A DNS Name suitable for use in the TLS Server Name Indication (SNI)
/// extension and/or for use as the reference hostname for which to verify a
/// certificate.
Expand Down Expand Up @@ -136,6 +139,24 @@ pub fn verify_cert_dns_name(cert: &super::EndEntityCert,
})
}


#[cfg(feature = "std")]
pub fn list_cert_dns_names<'names>(cert: &super::EndEntityCert<'names>)
-> Result<Vec<DNSNameRef<'names>>, Error> {
let cert = &cert.inner;
let names = std::cell::RefCell::new(Vec::new());

iterate_names(cert.subject, cert.subject_alt_name, Ok(()), &|name| {
match name {
GeneralName::DNSName(presented_id) => {
names.borrow_mut().push(DNSNameRef(presented_id).clone());
},
_ => ()
}
NameIteration::KeepGoing
}).map(|_| names.into_inner())
}

// https://tools.ietf.org/html/rfc5280#section-4.2.1.10
pub fn check_name_constraints<'a>(input: Option<&mut untrusted::Reader<'a>>,
subordinate_certs: &Cert)
Expand Down Expand Up @@ -358,10 +379,10 @@ enum NameIteration {
Stop(Result<(), Error>)
}

fn iterate_names(subject: untrusted::Input,
subject_alt_name: Option<untrusted::Input>,
fn iterate_names<'input>(subject: untrusted::Input<'input>,
subject_alt_name: Option<untrusted::Input<'input>>,
result_if_never_stopped_early: Result<(), Error>,
f: &Fn(GeneralName) -> NameIteration) -> Result<(), Error> {
f: &Fn(GeneralName<'input>) -> NameIteration) -> Result<(), Error> {
match subject_alt_name {
Some(subject_alt_name) => {
let mut subject_alt_name = untrusted::Reader::new(subject_alt_name);
Expand Down
13 changes: 13 additions & 0 deletions src/webpki.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,19 @@ impl <'a> EndEntityCert<'a> {
signed_data::verify_signature(signature_alg, self.inner.spki, msg,
signature)
}

/// Returns a list of the DNS names provided in the subject alternative names extension
///
/// This function must not be used to implement custom DNS name verification.
/// Verification functions are already provided as `verify_is_valid_for_dns_name`
/// and `verify_is_valid_for_at_least_one_dns_name`.
///
/// Requires the `std` default feature; i.e. this isn't available in
/// `#![no_std]` configurations.
#[cfg(feature = "std")]
pub fn dns_names(&self) -> Result<std::vec::Vec<DNSNameRef<'a>>, Error> {
name::list_cert_dns_names(&self)
}
}


Expand Down
49 changes: 49 additions & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,52 @@ fn time_constructor() {

let _ = webpki::Time::try_from(std::time::SystemTime::now()).unwrap();
}

#[allow(box_pointers)]
#[cfg(feature = "std")]
#[test]
pub fn list_netflix_names()
{
use std::iter::FromIterator;

let ee = include_bytes!("netflix/ee.der");

let ee_input = untrusted::Input::from(ee);
let cert = webpki::EndEntityCert::from(ee_input)
.expect("should parse netflix end entity certificate correctly");

let expected_names = {
const EXPECTED_NAMES: &'static [&'static str] = &[
"account.netflix.com",
"ca.netflix.com",
"netflix.ca",
"netflix.com",
"signup.netflix.com",
"www.netflix.ca",
"www1.netflix.com",
"www2.netflix.com",
"www3.netflix.com",
"develop-stage.netflix.com",
"release-stage.netflix.com",
"www.netflix.com",
];

std::collections::HashSet::from_iter(EXPECTED_NAMES.iter().map(|s| s.to_string()))
};

let mut actual_names = cert.dns_names()
.expect("should get all DNS names correctly for netflix end entity cert");

// Ensure that converting the list to a set doesn't throw away
// any duplicates that aren't supposed to be there
assert_eq!(actual_names.len(), expected_names.len());

let actual_names: std::collections::HashSet<String> = actual_names.drain(..).map(|name| {
let n: webpki::DNSName = name.into();
let s: &str = n.as_ref().into();
s.to_string()
}).collect();


assert_eq!(actual_names, expected_names);
}

0 comments on commit baa2fec

Please sign in to comment.