Skip to content

Commit 65e694c

Browse files
Add extra roots on Windows
1 parent c35587a commit 65e694c

File tree

1 file changed

+75
-12
lines changed
  • rustls-platform-verifier/src/verification

1 file changed

+75
-12
lines changed

Diff for: rustls-platform-verifier/src/verification/windows.rs

+75-12
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,18 @@ use windows_sys::Win32::{
4040
CERT_E_WRONG_USAGE, CRYPT_E_REVOKED, FILETIME, TRUE,
4141
},
4242
Security::Cryptography::{
43-
CertAddEncodedCertificateToStore, CertCloseStore, CertFreeCertificateChain,
44-
CertFreeCertificateChainEngine, CertFreeCertificateContext, CertGetCertificateChain,
45-
CertOpenStore, CertSetCertificateContextProperty, CertVerifyCertificateChainPolicy,
46-
HTTPSPolicyCallbackData, AUTHTYPE_SERVER, CERT_CHAIN_CACHE_END_CERT, CERT_CHAIN_CONTEXT,
43+
CertAddEncodedCertificateToStore, CertCloseStore, CertCreateCertificateChainEngine,
44+
CertFreeCertificateChain, CertFreeCertificateChainEngine, CertFreeCertificateContext,
45+
CertGetCertificateChain, CertOpenStore, CertSetCertificateContextProperty,
46+
CertVerifyCertificateChainPolicy, HTTPSPolicyCallbackData, AUTHTYPE_SERVER,
47+
CERT_CHAIN_CACHE_END_CERT, CERT_CHAIN_CONTEXT, CERT_CHAIN_ENGINE_CONFIG,
4748
CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS, CERT_CHAIN_POLICY_PARA,
4849
CERT_CHAIN_POLICY_SSL, CERT_CHAIN_POLICY_STATUS,
4950
CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT, CERT_CHAIN_REVOCATION_CHECK_END_CERT,
5051
CERT_CONTEXT, CERT_OCSP_RESPONSE_PROP_ID, CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG,
5152
CERT_STORE_ADD_ALWAYS, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, CERT_STORE_PROV_MEMORY,
52-
CERT_STRONG_SIGN_PARA, CERT_USAGE_MATCH, CRYPT_INTEGER_BLOB, CTL_USAGE,
53-
USAGE_MATCH_TYPE_AND, X509_ASN_ENCODING,
53+
CERT_STRONG_SIGN_PARA, CERT_TRUST_IS_PARTIAL_CHAIN, CERT_USAGE_MATCH, CRYPT_INTEGER_BLOB,
54+
CTL_USAGE, USAGE_MATCH_TYPE_AND, X509_ASN_ENCODING,
5455
},
5556
};
5657

@@ -76,8 +77,6 @@ struct CERT_CHAIN_PARA {
7677
}
7778

7879
use crate::verification::invalid_certificate;
79-
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
80-
use windows_sys::Win32::Security::Cryptography::CERT_CHAIN_ENGINE_CONFIG;
8180

8281
// SAFETY: see method implementation
8382
unsafe impl ZeroedWithSize for CERT_CHAIN_PARA {
@@ -110,7 +109,6 @@ unsafe impl ZeroedWithSize for CERT_CHAIN_POLICY_PARA {
110109
}
111110
}
112111

113-
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
114112
// SAFETY: see method implementation
115113
unsafe impl ZeroedWithSize for CERT_CHAIN_ENGINE_CONFIG {
116114
fn zeroed_with_size() -> Self {
@@ -273,11 +271,36 @@ impl CertificateStore {
273271
self.engine.map(|e| e.as_ptr() as isize).unwrap_or(0)
274272
}
275273

274+
fn new_with_extra_roots(
275+
roots: &[pki_types::CertificateDer<'static>],
276+
) -> Result<Self, TlsError> {
277+
let mut inner = Self::new()?;
278+
let mut exclusive_store = CertificateStore::new()?;
279+
for root in roots {
280+
exclusive_store.add_cert(root)?;
281+
}
282+
283+
let mut config = CERT_CHAIN_ENGINE_CONFIG::zeroed_with_size();
284+
config.hExclusiveRoot = exclusive_store.inner.as_ptr();
285+
286+
let mut engine = 0;
287+
// SAFETY: `engine` is valid to be written to and the config is valid to be read.
288+
let res = unsafe { CertCreateCertificateChainEngine(&config, &mut engine) };
289+
290+
#[allow(clippy::as_conversions)]
291+
let engine = call_with_last_error(|| match NonNull::new(engine as *mut c_void) {
292+
Some(c) if res == TRUE => Some(c),
293+
_ => None,
294+
})?;
295+
inner.engine = Some(engine);
296+
297+
Ok(inner)
298+
}
299+
276300
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
277301
fn new_with_fake_root(root: &[u8]) -> Result<Self, TlsError> {
278302
use windows_sys::Win32::Security::Cryptography::{
279-
CertCreateCertificateChainEngine, CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL,
280-
CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE,
303+
CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL, CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE,
281304
};
282305

283306
let mut inner = Self::new()?;
@@ -442,6 +465,9 @@ pub struct Verifier {
442465
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
443466
test_only_root_ca_override: Option<Vec<u8>>,
444467
pub(super) crypto_provider: OnceCell<Arc<CryptoProvider>>,
468+
/// Extra trust anchors to add to the verifier above and beyond those provided by
469+
/// the system-provided trust stores.
470+
extra_roots: Vec<pki_types::CertificateDer<'static>>,
445471
}
446472

447473
impl Verifier {
@@ -456,6 +482,22 @@ impl Verifier {
456482
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
457483
test_only_root_ca_override: None,
458484
crypto_provider: OnceCell::new(),
485+
extra_roots: Vec::new(),
486+
}
487+
}
488+
489+
/// Creates a new instance of a TLS certificate verifier that utilizes the
490+
/// Windows certificate facilities and augmented by the provided extra root certificates.
491+
///
492+
/// A [`CryptoProvider`] must be set with
493+
/// [`set_provider`][Verifier::set_provider]/[`with_provider`][Verifier::with_provider] or
494+
/// [`CryptoProvider::install_default`] before the verifier can be used.
495+
pub fn new_with_extra_roots(roots: Vec<pki_types::CertificateDer<'static>>) -> Self {
496+
Self {
497+
#[cfg(any(test, feature = "ffi-testing", feature = "dbg"))]
498+
test_only_root_ca_override: None,
499+
crypto_provider: OnceCell::new(),
500+
extra_roots: roots.into_iter().map(Into::into).collect::<Vec<_>>(),
459501
}
460502
}
461503

@@ -465,6 +507,7 @@ impl Verifier {
465507
Self {
466508
test_only_root_ca_override: Some(root.into()),
467509
crypto_provider: OnceCell::new(),
510+
extra_roots: Vec::new(),
468511
}
469512
}
470513

@@ -521,7 +564,27 @@ impl Verifier {
521564
.chain(Some(0))
522565
.collect();
523566

524-
let cert_chain = store.new_chain_in(&primary_cert, now)?;
567+
let mut cert_chain = store.new_chain_in(&primary_cert, now)?;
568+
569+
// We only use `TrustStatus` here because it hasn't had verification performed on it.
570+
// SAFETY: The pointer is guaranteed to be non-null.
571+
let is_partial_chain = unsafe { *cert_chain.inner.as_ptr() }
572+
.TrustStatus
573+
.dwErrorStatus
574+
& CERT_TRUST_IS_PARTIAL_CHAIN
575+
!= 0;
576+
577+
// If we have extra roots and building the chain gave us an error, we try to build a
578+
// new one with the extra roots.
579+
if !self.extra_roots.is_empty() && is_partial_chain {
580+
let mut store = CertificateStore::new_with_extra_roots(&self.extra_roots)?;
581+
582+
for cert in intermediate_certs.iter().copied() {
583+
store.add_cert(cert)?;
584+
}
585+
586+
cert_chain = store.new_chain_in(&primary_cert, now)?;
587+
}
525588

526589
let status = cert_chain.verify_chain_policy(server)?;
527590

0 commit comments

Comments
 (0)