Skip to content

Commit 1589a63

Browse files
committed
Eagerly build root store on miscellaneous Unix platforms
1 parent ff3c6c6 commit 1589a63

File tree

10 files changed

+88
-112
lines changed

10 files changed

+88
-112
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ permissions:
1212
contents: read
1313

1414
env:
15-
RUSTFLAGS: -D warnings -F unused_must_use
15+
RUSTFLAGS: -D warnings
1616

1717
jobs:
1818
clippy-build-std:

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ let config = ClientConfig::builder_with_provider(arc_crypto_provider)
106106
.with_safe_default_protocol_versions()
107107
.unwrap()
108108
.with_platform_verifier()
109+
.unwrap()
109110
.with_no_client_auth();
110111
```
111112

rustls-platform-verifier/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ rustls = { version = "0.23.27", default-features = false, features = ["std"] }
3333
log = { version = "0.4" }
3434
base64 = { version = "0.22", optional = true } # Only used when the `cert-logging` feature is enabled.
3535
jni = { version = "0.21", default-features = false, optional = true } # Only used during doc generation
36-
once_cell = "1.9"
3736

3837
[target.'cfg(all(unix, not(target_os = "android"), not(target_vendor = "apple"), not(target_arch = "wasm32")))'.dependencies]
3938
rustls-native-certs = "0.8"
4039
webpki = { package = "rustls-webpki", version = "0.103", default-features = false }
4140

4241
[target.'cfg(target_os = "android")'.dependencies]
42+
once_cell = "1.9"
4343
rustls-platform-verifier-android = { path = "../android-release-support", version = "0.1.0" }
4444
jni = { version = "0.21", default-features = false }
4545
webpki = { package = "rustls-webpki", version = "0.103", default-features = false }

rustls-platform-verifier/src/lib.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,22 @@ pub trait BuilderVerifierExt {
5252
/// use rustls_platform_verifier::BuilderVerifierExt;
5353
/// let config = ClientConfig::builder()
5454
/// .with_platform_verifier()
55+
/// .unwrap()
5556
/// .with_no_client_auth();
5657
/// ```
57-
fn with_platform_verifier(self) -> ConfigBuilder<ClientConfig, WantsClientCert>;
58+
fn with_platform_verifier(
59+
self,
60+
) -> Result<ConfigBuilder<ClientConfig, WantsClientCert>, rustls::Error>;
5861
}
5962

6063
impl BuilderVerifierExt for ConfigBuilder<ClientConfig, WantsVerifier> {
61-
fn with_platform_verifier(self) -> ConfigBuilder<ClientConfig, WantsClientCert> {
62-
let provider = self.crypto_provider().clone();
63-
self.dangerous()
64-
.with_custom_certificate_verifier(Arc::new(Verifier::new(provider)))
64+
fn with_platform_verifier(
65+
self,
66+
) -> Result<ConfigBuilder<ClientConfig, WantsClientCert>, rustls::Error> {
67+
let verifier = Verifier::new(self.crypto_provider().clone())?;
68+
Ok(self
69+
.dangerous()
70+
.with_custom_certificate_verifier(Arc::new(verifier)))
6571
}
6672
}
6773

@@ -74,13 +80,13 @@ pub trait ConfigVerifierExt {
7480
/// use rustls_platform_verifier::ConfigVerifierExt;
7581
/// let config = ClientConfig::with_platform_verifier();
7682
/// ```
77-
fn with_platform_verifier() -> ClientConfig;
83+
fn with_platform_verifier() -> Result<ClientConfig, rustls::Error>;
7884
}
7985

8086
impl ConfigVerifierExt for ClientConfig {
81-
fn with_platform_verifier() -> ClientConfig {
82-
ClientConfig::builder()
83-
.with_platform_verifier()
84-
.with_no_client_auth()
87+
fn with_platform_verifier() -> Result<ClientConfig, rustls::Error> {
88+
Ok(ClientConfig::builder()
89+
.with_platform_verifier()?
90+
.with_no_client_auth())
8591
}
8692
}

rustls-platform-verifier/src/tests/verification_mock/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ pub(super) fn verification_without_mock_root() {
108108
.unwrap();
109109

110110
#[cfg(not(target_os = "freebsd"))]
111-
let verifier = Verifier::new(crypto_provider);
111+
let verifier = Verifier::new(crypto_provider).unwrap();
112112

113113
let server_name = pki_types::ServerName::try_from(EXAMPLE_COM).unwrap();
114114
let end_entity = pki_types::CertificateDer::from(ROOT1_INT1_EXAMPLE_COM_GOOD);

rustls-platform-verifier/src/tests/verification_real_world/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ fn real_world_test<E: std::error::Error>(test_case: &TestCase<E>) {
138138
.unwrap();
139139

140140
#[cfg(not(target_os = "freebsd"))]
141-
let verifier = Verifier::new(crypto_provider);
141+
let verifier = Verifier::new(crypto_provider).unwrap();
142142

143143
let mut chain = test_case
144144
.chain

rustls-platform-verifier/src/verification/android.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ impl Drop for Verifier {
6464
impl Verifier {
6565
/// Creates a new instance of a TLS certificate verifier that utilizes the
6666
/// Android certificate facilities.
67-
pub fn new(crypto_provider: Arc<CryptoProvider>) -> Self {
68-
Self {
67+
pub fn new(crypto_provider: Arc<CryptoProvider>) -> Result<Self, TlsError> {
68+
Ok(Self {
6969
#[cfg(any(test, feature = "ffi-testing"))]
7070
test_only_root_ca_override: None,
7171
crypto_provider,
72-
}
72+
})
7373
}
7474

7575
/// Creates a test-only TLS certificate verifier which trusts our fake root CA cert.

rustls-platform-verifier/src/verification/apple.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ pub struct Verifier {
5555
impl Verifier {
5656
/// Creates a new instance of a TLS certificate verifier that utilizes the Apple certificate
5757
/// facilities.
58-
pub fn new(crypto_provider: Arc<CryptoProvider>) -> Self {
59-
Self {
58+
pub fn new(crypto_provider: Arc<CryptoProvider>) -> Result<Self, TlsError> {
59+
Ok(Self {
6060
extra_roots: Vec::new(),
6161
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
6262
test_only_root_ca_override: None,
6363
crypto_provider,
64-
}
64+
})
6565
}
6666

6767
/// Creates a new instance of a TLS certificate verifier that utilizes the Apple certificate

rustls-platform-verifier/src/verification/others.rs

Lines changed: 58 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
use super::log_server_cert;
2-
use once_cell::sync::OnceCell;
1+
use std::fmt::Debug;
2+
use std::sync::Arc;
3+
34
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
45
use rustls::client::WebPkiServerVerifier;
6+
use rustls::pki_types;
57
use rustls::{
68
crypto::CryptoProvider, CertificateError, DigitallySignedStruct, Error as TlsError, OtherError,
79
SignatureScheme,
810
};
9-
use std::fmt::Debug;
10-
use std::sync::{Arc, Mutex};
11+
12+
use super::log_server_cert;
1113

1214
/// A TLS certificate verifier that uses the system's root store and WebPKI.
1315
#[derive(Debug)]
@@ -19,52 +21,24 @@ pub struct Verifier {
1921
// locking and unlocking the application will pull fresh root
2022
// certificates from disk, picking up on any changes
2123
// that might have been made since.
22-
inner: OnceCell<Arc<WebPkiServerVerifier>>,
23-
24-
// Extra trust anchors to add to the verifier above and beyond those provided by the
25-
// platform via rustls-native-certs.
26-
extra_roots: Mutex<Vec<pki_types::TrustAnchor<'static>>>,
27-
28-
/// Testing only: an additional root CA certificate to trust.
29-
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
30-
test_only_root_ca_override: Option<pki_types::CertificateDer<'static>>,
31-
32-
crypto_provider: Arc<CryptoProvider>,
24+
inner: Arc<WebPkiServerVerifier>,
3325
}
3426

3527
impl Verifier {
3628
/// Creates a new verifier whose certificate validation is provided by
3729
/// WebPKI, using root certificates provided by the platform.
38-
pub fn new(crypto_provider: Arc<CryptoProvider>) -> Self {
39-
Self {
40-
inner: OnceCell::new(),
41-
extra_roots: Vec::new().into(),
42-
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
43-
test_only_root_ca_override: None,
44-
crypto_provider,
45-
}
30+
pub fn new(crypto_provider: Arc<CryptoProvider>) -> Result<Self, TlsError> {
31+
Self::new_inner([], None, crypto_provider)
4632
}
4733

4834
/// Creates a new verifier whose certificate validation is provided by
4935
/// WebPKI, using root certificates provided by the platform and augmented by
5036
/// the provided extra root certificates.
5137
pub fn new_with_extra_roots(
52-
roots: impl IntoIterator<Item = pki_types::CertificateDer<'static>>,
38+
extra_roots: impl IntoIterator<Item = pki_types::CertificateDer<'static>>,
5339
crypto_provider: Arc<CryptoProvider>,
5440
) -> Result<Self, TlsError> {
55-
Ok(Self {
56-
inner: OnceCell::new(),
57-
extra_roots: roots
58-
.into_iter()
59-
.flat_map(|root| {
60-
webpki::anchor_from_trusted_cert(&root).map(|anchor| anchor.to_owned())
61-
})
62-
.collect::<Vec<_>>()
63-
.into(),
64-
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
65-
test_only_root_ca_override: None,
66-
crypto_provider,
67-
})
41+
Self::new_inner(extra_roots, None, crypto_provider)
6842
}
6943

7044
/// Creates a test-only TLS certificate verifier which trusts our fake root CA cert.
@@ -73,51 +47,49 @@ impl Verifier {
7347
root: pki_types::CertificateDer<'static>,
7448
crypto_provider: Arc<CryptoProvider>,
7549
) -> Self {
76-
Self {
77-
inner: OnceCell::new(),
78-
extra_roots: Vec::new().into(),
79-
test_only_root_ca_override: Some(root),
80-
crypto_provider,
81-
}
50+
Self::new_inner([], Some(root), crypto_provider)
51+
.expect("failed to create verifier with fake root")
8252
}
8353

84-
fn get_or_init_verifier(&self) -> Result<&Arc<WebPkiServerVerifier>, TlsError> {
85-
self.inner.get_or_try_init(|| self.init_verifier())
86-
}
87-
88-
// Attempt to load CA root certificates present on system, fallback to WebPKI roots if error
89-
fn init_verifier(&self) -> Result<Arc<WebPkiServerVerifier>, TlsError> {
54+
/// Creates a new verifier whose certificate validation is provided by
55+
/// WebPKI, using root certificates provided by the platform and augmented by
56+
/// the provided extra root certificates.
57+
fn new_inner(
58+
extra_roots: impl IntoIterator<Item = pki_types::CertificateDer<'static>>,
59+
#[allow(unused)] // test_root is only used in tests
60+
test_root: Option<pki_types::CertificateDer<'static>>,
61+
crypto_provider: Arc<CryptoProvider>,
62+
) -> Result<Self, TlsError> {
9063
let mut root_store = rustls::RootCertStore::empty();
9164

9265
// For testing only: load fake root cert, instead of native/WebPKI roots
9366
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
9467
{
95-
if let Some(test_root) = &self.test_only_root_ca_override {
96-
let (added, ignored) =
97-
root_store.add_parsable_certificates([pki_types::CertificateDer::from(
98-
test_root.as_ref(),
99-
)]);
100-
if (added != 1) || (ignored != 0) {
101-
panic!("Failed to insert fake, test-only root trust anchor");
102-
}
103-
return Ok(WebPkiServerVerifier::builder_with_provider(
104-
root_store.into(),
105-
self.crypto_provider.clone(),
106-
)
107-
.build()
108-
.unwrap());
68+
if let Some(test_root) = test_root {
69+
root_store.add(test_root)?;
70+
return Ok(Self {
71+
inner: WebPkiServerVerifier::builder_with_provider(
72+
root_store.into(),
73+
crypto_provider.clone(),
74+
)
75+
.build()
76+
.map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?,
77+
});
10978
}
11079
}
11180

112-
// Safety: There's no way for the mutex to be locked multiple times, so this is
113-
// an infallible operation.
114-
let mut extra_roots = self.extra_roots.try_lock().unwrap();
115-
if !extra_roots.is_empty() {
116-
let count = extra_roots.len();
117-
root_store.extend(extra_roots.drain(..));
118-
log::debug!(
119-
"Loaded {count} extra CA certificates in addition to possible system roots",
120-
);
81+
let mut num_extra_roots = 0;
82+
let (added, ignored) =
83+
root_store.add_parsable_certificates(extra_roots.into_iter().inspect(|_| {
84+
num_extra_roots += 1;
85+
}));
86+
87+
if num_extra_roots > 0 {
88+
if ignored > 0 {
89+
log::warn!("{ignored} extra CA root certificates were ignored due to errors");
90+
} else {
91+
log::debug!("Loaded {added} CA root certificates from extra roots");
92+
}
12193
}
12294

12395
#[cfg(all(
@@ -129,8 +101,8 @@ impl Verifier {
129101
{
130102
let result = rustls_native_certs::load_native_certs();
131103
let (added, ignored) = root_store.add_parsable_certificates(result.certs);
132-
if ignored != 0 {
133-
log::warn!("Some CA root certificates were ignored due to errors");
104+
if ignored > 0 {
105+
log::warn!("{ignored} platform CA root certificates were ignored due to errors");
134106
}
135107

136108
for error in result.errors {
@@ -144,7 +116,7 @@ impl Verifier {
144116
"No CA certificates were loaded from the system".to_owned(),
145117
));
146118
} else {
147-
log::debug!("Loaded {added} CA certificates from the system");
119+
log::debug!("Loaded {added} CA root certificates from the system");
148120
}
149121
}
150122

@@ -155,9 +127,14 @@ impl Verifier {
155127
);
156128
};
157129

158-
WebPkiServerVerifier::builder_with_provider(root_store.into(), self.crypto_provider.clone())
130+
Ok(Self {
131+
inner: WebPkiServerVerifier::builder_with_provider(
132+
root_store.into(),
133+
crypto_provider.clone(),
134+
)
159135
.build()
160-
.map_err(|e| TlsError::Other(OtherError(Arc::new(e))))
136+
.map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?,
137+
})
161138
}
162139
}
163140

@@ -172,7 +149,7 @@ impl ServerCertVerifier for Verifier {
172149
) -> Result<ServerCertVerified, TlsError> {
173150
log_server_cert(end_entity);
174151

175-
self.get_or_init_verifier()?
152+
self.inner
176153
.verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)
177154
.map_err(map_webpki_errors)
178155
// This only contains information from the system or other public
@@ -189,8 +166,7 @@ impl ServerCertVerifier for Verifier {
189166
cert: &pki_types::CertificateDer<'_>,
190167
dss: &DigitallySignedStruct,
191168
) -> Result<HandshakeSignatureValid, TlsError> {
192-
self.get_or_init_verifier()?
193-
.verify_tls12_signature(message, cert, dss)
169+
self.inner.verify_tls12_signature(message, cert, dss)
194170
}
195171

196172
fn verify_tls13_signature(
@@ -199,18 +175,11 @@ impl ServerCertVerifier for Verifier {
199175
cert: &pki_types::CertificateDer<'_>,
200176
dss: &DigitallySignedStruct,
201177
) -> Result<HandshakeSignatureValid, TlsError> {
202-
self.get_or_init_verifier()?
203-
.verify_tls13_signature(message, cert, dss)
178+
self.inner.verify_tls13_signature(message, cert, dss)
204179
}
205180

206181
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
207-
// XXX: Don't go through `self.verifier` here: It introduces extra failure
208-
// cases and is strictly unneeded because `get_provider` is the same provider and
209-
// set of algorithms passed into the wrapped `WebPkiServerVerifier`. Given this,
210-
// the list of schemes are identical.
211-
self.crypto_provider
212-
.signature_verification_algorithms
213-
.supported_schemes()
182+
self.inner.supported_verify_schemes()
214183
}
215184
}
216185

rustls-platform-verifier/src/verification/windows.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -497,13 +497,13 @@ pub struct Verifier {
497497
impl Verifier {
498498
/// Creates a new instance of a TLS certificate verifier that utilizes the
499499
/// Windows certificate facilities.
500-
pub fn new(crypto_provider: Arc<CryptoProvider>) -> Self {
501-
Self {
500+
pub fn new(crypto_provider: Arc<CryptoProvider>) -> Result<Self, TlsError> {
501+
Ok(Self {
502502
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
503503
test_only_root_ca_override: None,
504504
crypto_provider,
505505
extra_roots: None,
506-
}
506+
})
507507
}
508508

509509
/// Creates a new instance of a TLS certificate verifier that utilizes the

0 commit comments

Comments
 (0)