Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
est31 committed Oct 16, 2020
1 parent 1701ef8 commit 82ed796
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 34 deletions.
43 changes: 41 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ mod macos;
use macos as platform;

use rustls::RootCertStore;
use std::io::Error;
use std::io::{Error, ErrorKind};
use std::io::BufRead;

pub trait RootStoreBuilder {
fn load_der(&mut self, der: Vec<u8>) -> Result<(), Error>;
fn load_pem_file(&mut self, rd: &mut dyn BufRead) -> Result<(), Error>;
}

/// Loads root certificates found in the platform's native certificate
/// store.
Expand All @@ -37,5 +43,38 @@ use std::io::Error;
/// and parsing a ~300KB disk file. It's therefore prudent to call
/// this sparingly.
pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
platform::load_native_certs()
struct RootCertStoreLoader {
store: RootCertStore,
};
impl RootStoreBuilder for RootCertStoreLoader {
fn load_der(&mut self, der: Vec<u8>) -> Result<(), Error> {
self.store.add(&rustls::Certificate(der))
.map_err(|err| Error::new(ErrorKind::InvalidData, err))
}
fn load_pem_file(&mut self, rd: &mut dyn BufRead) -> Result<(), Error> {
self.store.add_pem_file(rd)
.map(|_| ())
.map_err(|()| Error::new(ErrorKind::InvalidData, format!("could not load PEM file")))
}
}
let mut loader = RootCertStoreLoader {
store: RootCertStore::empty(),
};
match build_native_certs(&mut loader) {
Err(err) if loader.store.is_empty() => Err((None, err)),
Err(err) => Err((Some(loader.store), err)),
Ok(()) => Ok(loader.store),
}
}

/// Loads root certificates found in the platform's native certificate
/// store, executing callbacks on the provided builder.
///
/// This function fails in a platform-specific way, expressed in a `std::io::Error`.
///
/// This function can be expensive: on some platforms it involves loading
/// and parsing a ~300KB disk file. It's therefore prudent to call
/// this sparingly.
pub fn build_native_certs<B: RootStoreBuilder>(builder: &mut B) -> Result<(), Error> {
platform::build_native_certs(builder)
}
14 changes: 4 additions & 10 deletions src/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ use std::collections::HashMap;

use crate::PartialResult;

pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
let mut store = RootCertStore::empty();

pub fn build_native_certs<B: RootStoreBuilder>(builder: &mut B) -> Result<(), Error> {
// The various domains are designed to interact like this:
//
// "Per-user Trust Settings override locally administered
Expand Down Expand Up @@ -56,7 +54,7 @@ pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
match trusted {
TrustSettingsForCertificate::TrustRoot |
TrustSettingsForCertificate::TrustAsRoot => {
match store.add(&rustls::Certificate(der)) {
match builder.load_der(der) {
Err(err) => {
first_error = first_error
.or_else(|| Some(Error::new(ErrorKind::InvalidData, err)));
Expand All @@ -69,12 +67,8 @@ pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
}

if let Some(err) = first_error {
if store.is_empty() {
Err((None, err))
} else {
Err((Some(store), err))
}
Err(err)
} else {
Ok(store)
Ok(())
}
}
Empty file added src/rustls.rs
Empty file.
21 changes: 7 additions & 14 deletions src/unix.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
use crate::RootStoreBuilder;
use openssl_probe;
use rustls::RootCertStore;
use std::io::{Error, ErrorKind};
use std::io::BufReader;
use std::fs::File;
use std::path::Path;

use crate::PartialResult;

fn load_file(store: &mut RootCertStore, path: &Path) -> Result<(), Error> {
fn load_file(builder: &mut impl RootStoreBuilder, path: &Path) -> Result<(), Error> {
let f = File::open(&path)?;
let mut f = BufReader::new(f);
if store.add_pem_file(&mut f).is_err() {
if builder.load_pem_file(&mut f).is_err() {
Err(Error::new(ErrorKind::InvalidData,
format!("Could not load PEM file {:?}", path)))
} else {
Ok(())
}
}

pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
pub fn build_native_certs<B: RootStoreBuilder>(builder: &mut B) -> Result<(), Error> {
let likely_locations = openssl_probe::probe();
let mut store = RootCertStore::empty();
let mut first_error = None;

if let Some(file) = likely_locations.cert_file {
match load_file(&mut store, &file) {
match load_file(builder, &file) {
Err(err) => {
first_error = first_error.or_else(|| Some(err));
}
Expand All @@ -33,12 +30,8 @@ pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
}

if let Some(err) = first_error {
if store.is_empty() {
Err((None, err))
} else {
Err((Some(store), err))
}
Err(err)
} else {
Ok(store)
Ok(())
}
}
12 changes: 4 additions & 8 deletions src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn usable_for_rustls(uses: schannel::cert_context::ValidUses) -> bool {
}
}

pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
pub fn build_native_certs<B: RootStoreBuilder>(builder: &mut B) -> Result<(), Error> {
let mut store = RootCertStore::empty();
let mut first_error = None;

Expand All @@ -27,7 +27,7 @@ pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
continue;
}

match store.add(&rustls::Certificate(cert.to_der().to_vec())) {
match builder.load_der(cert.to_der().to_vec()) {
Err(err) => {
first_error = first_error
.or_else(|| Some(Error::new(ErrorKind::InvalidData, err)));
Expand All @@ -37,12 +37,8 @@ pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
}

if let Some(err) = first_error {
if store.is_empty() {
Err((None, err))
} else {
Err((Some(store), err))
}
Err(err)
} else {
Ok(store)
Ok(())
}
}

0 comments on commit 82ed796

Please sign in to comment.