Skip to content

Commit

Permalink
other: make webpki-roots optional for Linux.
Browse files Browse the repository at this point in the history
This commit removes the Linux dependency on the `webpki-roots` crate. In
its place a new `Verifier::with_extra_roots` constructor is provided.
The new constructor allows the caller to provide a set of extra
`OwnedTrustAnchors`s that will be loaded in addition to the platform
provided trust anchors. Callers that expect to need to augment the
platform roots can use this (with `webpki-roots` or otherwise) to
achieve their goal.

The wasm32 target continues to depend on `webpki-roots` since there is
no platform provider for trust anchors.
  • Loading branch information
cpu committed Aug 31, 2023
1 parent 9e8ce31 commit 5351caa
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 33 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ once_cell = { version = "1.9", optional = true } # Only used during doc generati
[target.'cfg(target_os = "linux")'.dependencies]
rustls-native-certs = "0.6"
once_cell = "1.9"
webpki-roots = "0.25" # Augmentation to `openssl-probe` due to inconsistencies on Linux distros.
webpki = { package = "rustls-webpki", version = "0.101", features = ["alloc", "std"] }

[target.'cfg(target_os = "android")'.dependencies]
Expand Down
77 changes: 45 additions & 32 deletions src/verification/others.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use super::log_server_cert;
use once_cell::sync::OnceCell;
use rustls::{
client::{ServerCertVerifier, WebPkiVerifier},
CertificateError, Error as TlsError, RootCertStore,
CertificateError, Error as TlsError,
};
use std::sync::Mutex;

/// A TLS certificate verifier that uses the system's root store and WebPKI.
#[derive(Default)]
Expand All @@ -17,17 +18,34 @@ pub struct Verifier {
// that might have been made since.
inner: OnceCell<WebPkiVerifier>,

// Extra trust anchors to add to the verifier above and beyond those provided by the
// platform via rustls-native-certs.
extra_roots: Mutex<Vec<rustls::OwnedTrustAnchor>>,

/// Testing only: an additional root CA certificate to trust.
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
test_only_root_ca_override: Option<Vec<u8>>,
}

impl Verifier {
/// Creates a new verifier whose certificate validation is provided by
/// WebPKI.
/// WebPKI, using root certificates provided by the platform.
pub fn new() -> Self {
Self {
inner: OnceCell::new(),
extra_roots: Vec::new().into(),
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
test_only_root_ca_override: None,
}
}

/// Creates a new verifier whose certificate validation is provided by
/// WebPKI, using root certificates provided by the platform and augmented by
/// the provided extra root certificates.
pub fn new_with_extra_roots(roots: impl IntoIterator<Item = rustls::OwnedTrustAnchor>) -> Self {
Self {
inner: OnceCell::new(),
extra_roots: roots.into_iter().collect::<Vec<_>>().into(),
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
test_only_root_ca_override: None,
}
Expand All @@ -38,6 +56,7 @@ impl Verifier {
pub(crate) fn new_with_fake_root(root: &[u8]) -> Self {
Self {
inner: OnceCell::new(),
extra_roots: Vec::new().into(),
test_only_root_ca_override: Some(root.into()),
}
}
Expand Down Expand Up @@ -68,35 +87,42 @@ impl Verifier {
log::warn!("Some CA root certificates were ignored due to errors");
}

// While we load webpki-roots anyway, this can be helpful to know for troubleshooting.
if root_store.is_empty() {
log::error!("No CA certificates were loaded from the system");
} else {
log::debug!("Loaded {added} CA certificates from the system");
}

// Finding TLS roots on Linux is not reliable enough to always depend on it
// across various distributions. Instead, we always load the WebPKI roots in
// addition so that a valid trust anchor is more likely to be available.
load_webpki_roots(&mut root_store);

log::debug!(
"Loaded WebPKI roots in addition to {} roots from the system",
added
);
// Safety: There's no way for the mutex to be locked multiple times, so this is
// an infallible operation.
let mut extra_roots = self.extra_roots.try_lock().unwrap();
if !extra_roots.is_empty() {
let count = extra_roots.len();
root_store.add_trust_anchors(&mut extra_roots.drain(..));
log::debug!(
"Loaded {count} extra CA certificates in addition to roots from the system",
);
}
}
Err(err) => {
// This only contains a path to a system directory:
// https://github.com/rustls/rustls-native-certs/blob/main/src/lib.rs#L71
log::error!(
"No CA certificates were loaded: {}. Falling back to WebPKI roots",
err,
);
load_webpki_roots(&mut root_store);
// https://github.com/rustls/rustls-native-certs/blob/bc13b9a6bfc2e1eec881597055ca49accddd972a/src/lib.rs#L91-L94
return Err(rustls::Error::General(format!(
"failed to load system root certificates: {}",
err
)));
}
};

#[cfg(target_arch = "wasm32")]
{
load_webpki_roots(&mut root_store);
root_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|root| {
rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
root.subject,
root.spki,
root.name_constraints,
)
}));
};

Ok(WebPkiVerifier::new(root_store, None))
Expand Down Expand Up @@ -152,16 +178,3 @@ fn map_webpki_errors(err: TlsError) -> TlsError {

err
}

/// Loads the static `webpki-roots` into the provided certificate store.
fn load_webpki_roots(store: &mut RootCertStore) {
use rustls::OwnedTrustAnchor;

store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|root| {
OwnedTrustAnchor::from_subject_spki_name_constraints(
root.subject,
root.spki,
root.name_constraints,
)
}));
}

0 comments on commit 5351caa

Please sign in to comment.