Skip to content

Commit 82ed796

Browse files
committed
WIP
1 parent 1701ef8 commit 82ed796

File tree

5 files changed

+56
-34
lines changed

5 files changed

+56
-34
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: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn usable_for_rustls(uses: schannel::cert_context::ValidUses) -> bool {
1515
}
1616
}
1717

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

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

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

3939
if let Some(err) = first_error {
40-
if store.is_empty() {
41-
Err((None, err))
42-
} else {
43-
Err((Some(store), err))
44-
}
40+
Err(err)
4541
} else {
46-
Ok(store)
42+
Ok(())
4743
}
4844
}

0 commit comments

Comments
 (0)