Skip to content

Commit ec25593

Browse files
committed
WIP
1 parent 1701ef8 commit ec25593

File tree

5 files changed

+58
-40
lines changed

5 files changed

+58
-40
lines changed

src/lib.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ mod macos;
2424
use macos as platform;
2525

2626
use rustls::RootCertStore;
27-
use std::io::Error;
27+
use std::io::{Error, ErrorKind};
28+
use std::io::BufRead;
29+
30+
pub trait RootStoreBuilder {
31+
fn load_der(&mut self, der: Vec<u8>) -> Result<(), Error>;
32+
fn load_pem_file(&mut self, rd: &mut dyn BufRead) -> Result<(), Error>;
33+
}
2834

2935
/// Loads root certificates found in the platform's native certificate
3036
/// store.
@@ -37,5 +43,38 @@ use std::io::Error;
3743
/// and parsing a ~300KB disk file. It's therefore prudent to call
3844
/// this sparingly.
3945
pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
40-
platform::load_native_certs()
46+
struct RootCertStoreLoader {
47+
store: RootCertStore,
48+
};
49+
impl RootStoreBuilder for RootCertStoreLoader {
50+
fn load_der(&mut self, der: Vec<u8>) -> Result<(), Error> {
51+
self.store.add(&rustls::Certificate(der))
52+
.map_err(|err| Error::new(ErrorKind::InvalidData, err))
53+
}
54+
fn load_pem_file(&mut self, rd: &mut dyn BufRead) -> Result<(), Error> {
55+
self.store.add_pem_file(rd)
56+
.map(|_| ())
57+
.map_err(|()| Error::new(ErrorKind::InvalidData, format!("could not load PEM file")))
58+
}
59+
}
60+
let mut loader = RootCertStoreLoader {
61+
store: RootCertStore::empty(),
62+
};
63+
match build_native_certs(&mut loader) {
64+
Err(err) if loader.store.is_empty() => Err((None, err)),
65+
Err(err) => Err((Some(loader.store), err)),
66+
Ok(()) => Ok(loader.store),
67+
}
68+
}
69+
70+
/// Loads root certificates found in the platform's native certificate
71+
/// store, executing callbacks on the provided builder.
72+
///
73+
/// This function fails in a platform-specific way, expressed in a `std::io::Error`.
74+
///
75+
/// This function can be expensive: on some platforms it involves loading
76+
/// and parsing a ~300KB disk file. It's therefore prudent to call
77+
/// this sparingly.
78+
pub fn build_native_certs<B: RootStoreBuilder>(builder: &mut B) -> Result<(), Error> {
79+
platform::build_native_certs(builder)
4180
}

src/macos.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ use std::collections::HashMap;
99

1010
use crate::PartialResult;
1111

12-
pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
13-
let mut store = RootCertStore::empty();
14-
12+
pub fn build_native_certs<B: RootStoreBuilder>(builder: &mut B) -> Result<(), Error> {
1513
// The various domains are designed to interact like this:
1614
//
1715
// "Per-user Trust Settings override locally administered
@@ -56,7 +54,7 @@ pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
5654
match trusted {
5755
TrustSettingsForCertificate::TrustRoot |
5856
TrustSettingsForCertificate::TrustAsRoot => {
59-
match store.add(&rustls::Certificate(der)) {
57+
match builder.load_der(der) {
6058
Err(err) => {
6159
first_error = first_error
6260
.or_else(|| Some(Error::new(ErrorKind::InvalidData, err)));
@@ -69,12 +67,8 @@ pub fn load_native_certs() -> PartialResult<RootCertStore, Error> {
6967
}
7068

7169
if let Some(err) = first_error {
72-
if store.is_empty() {
73-
Err((None, err))
74-
} else {
75-
Err((Some(store), err))
76-
}
70+
Err(err)
7771
} else {
78-
Ok(store)
72+
Ok(())
7973
}
8074
}

src/rustls.rs

Whitespace-only changes.

src/unix.rs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
1+
use crate::RootStoreBuilder;
12
use openssl_probe;
2-
use rustls::RootCertStore;
33
use std::io::{Error, ErrorKind};
44
use std::io::BufReader;
55
use std::fs::File;
66
use std::path::Path;
77

8-
use crate::PartialResult;
9-
10-
fn load_file(store: &mut RootCertStore, path: &Path) -> Result<(), Error> {
8+
fn load_file(builder: &mut impl RootStoreBuilder, path: &Path) -> Result<(), Error> {
119
let f = File::open(&path)?;
1210
let mut f = BufReader::new(f);
13-
if store.add_pem_file(&mut f).is_err() {
11+
if builder.load_pem_file(&mut f).is_err() {
1412
Err(Error::new(ErrorKind::InvalidData,
1513
format!("Could not load PEM file {:?}", path)))
1614
} else {
1715
Ok(())
1816
}
1917
}
2018

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

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

3532
if let Some(err) = first_error {
36-
if store.is_empty() {
37-
Err((None, err))
38-
} else {
39-
Err((Some(store), err))
40-
}
33+
Err(err)
4134
} else {
42-
Ok(store)
35+
Ok(())
4336
}
4437
}

src/windows.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1+
use crate::RootStoreBuilder;
12
use schannel;
2-
use rustls::RootCertStore;
33
use std::io::{Error, ErrorKind};
44

5-
use crate::PartialResult;
6-
75
static PKIX_SERVER_AUTH: &str = "1.3.6.1.5.5.7.3.1";
86

97
fn usable_for_rustls(uses: schannel::cert_context::ValidUses) -> bool {
@@ -15,19 +13,17 @@ fn usable_for_rustls(uses: schannel::cert_context::ValidUses) -> bool {
1513
}
1614
}
1715

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

22-
let current_user_store = schannel::cert_store::CertStore::open_current_user("ROOT")
23-
.map_err(|err| (None, err))?;
19+
let current_user_store = schannel::cert_store::CertStore::open_current_user("ROOT")?;
2420

2521
for cert in current_user_store.certs() {
2622
if !usable_for_rustls(cert.valid_uses().unwrap()) {
2723
continue;
2824
}
2925

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

3935
if let Some(err) = first_error {
40-
if store.is_empty() {
41-
Err((None, err))
42-
} else {
43-
Err((Some(store), err))
44-
}
36+
Err(err)
4537
} else {
46-
Ok(store)
38+
Ok(())
4739
}
4840
}

0 commit comments

Comments
 (0)