Skip to content

Commit 0c997c0

Browse files
nnmkhangcomplexspaces
authored andcommitted
Changing default settings for CertGetCertificateChain
- Changing default settings for CertGetCertificateChain. - Changing flags - Decreasing timeout - Remove error bit checking - Moving Verifier error handling
1 parent 0dce480 commit 0c997c0

File tree

1 file changed

+24
-98
lines changed
  • rustls-platform-verifier/src/verification

1 file changed

+24
-98
lines changed

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

Lines changed: 24 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,25 @@ use crate::windows::{
2525
use rustls::{client::ServerCertVerifier, CertificateError, Error as TlsError};
2626
use winapi::{
2727
shared::{
28-
minwindef::{DWORD, FILETIME, TRUE},
28+
minwindef::{FILETIME, TRUE},
2929
ntdef::{LPSTR, VOID},
30-
winerror::{CERT_E_CN_NO_MATCH, CERT_E_INVALID_NAME, CRYPT_E_REVOKED},
30+
winerror::{
31+
CERT_E_CN_NO_MATCH, CERT_E_EXPIRED, CERT_E_INVALID_NAME, CERT_E_UNTRUSTEDROOT,
32+
CERT_E_WRONG_USAGE, CRYPT_E_REVOKED,
33+
},
3134
},
3235
um::wincrypt::{
33-
self, CertAddEncodedCertificateToStore, CertCloseStore, CertFreeCertificateChain,
36+
CertAddEncodedCertificateToStore, CertCloseStore, CertFreeCertificateChain,
3437
CertFreeCertificateChainEngine, CertFreeCertificateContext, CertGetCertificateChain,
3538
CertOpenStore, CertSetCertificateContextProperty, CertVerifyCertificateChainPolicy,
36-
AUTHTYPE_SERVER, CERT_CHAIN_CACHE_END_CERT, CERT_CHAIN_CONTEXT, CERT_CHAIN_PARA,
39+
AUTHTYPE_SERVER, CERT_CHAIN_CONTEXT, CERT_CHAIN_PARA,
3740
CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS, CERT_CHAIN_POLICY_PARA,
38-
CERT_CHAIN_POLICY_SSL, CERT_CHAIN_POLICY_STATUS, CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY,
39-
CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, CERT_CONTEXT, CERT_OCSP_RESPONSE_PROP_ID,
40-
CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, CERT_STORE_ADD_ALWAYS,
41-
CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, CERT_STORE_PROV_MEMORY, CERT_USAGE_MATCH,
42-
CRYPT_DATA_BLOB, CTL_USAGE, SSL_EXTRA_CERT_CHAIN_POLICY_PARA, USAGE_MATCH_TYPE_AND,
43-
X509_ASN_ENCODING,
41+
CERT_CHAIN_POLICY_SSL, CERT_CHAIN_POLICY_STATUS,
42+
CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT, CERT_CHAIN_REVOCATION_CHECK_END_CERT,
43+
CERT_CONTEXT, CERT_OCSP_RESPONSE_PROP_ID, CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG,
44+
CERT_STORE_ADD_ALWAYS, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, CERT_STORE_PROV_MEMORY,
45+
CERT_USAGE_MATCH, CRYPT_DATA_BLOB, CTL_USAGE, SSL_EXTRA_CERT_CHAIN_POLICY_PARA,
46+
USAGE_MATCH_TYPE_AND, X509_ASN_ENCODING,
4447
},
4548
};
4649

@@ -364,13 +367,14 @@ impl CertificateStore {
364367
}
365368
};
366369

367-
// `CERT_CHAIN_CACHE_END_CERT` is somewhat cargo-culted from Chromium.
368-
// `CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY` prevents fetching of OCSP
369-
// and CRLs. `CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT` enables
370-
// revocation checking of the entire chain.
371-
const FLAGS: u32 = CERT_CHAIN_CACHE_END_CERT
372-
| CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY
373-
| CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
370+
// `CERT_CHAIN_REVOCATION_CHECK_END_CERT` only checks revocation for end cert.
371+
// `CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT` accumulates network retrievals timeouts
372+
// to limit network time and improve performance.
373+
const FLAGS: u32 =
374+
CERT_CHAIN_REVOCATION_CHECK_END_CERT | CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT;
375+
376+
// Lowering URL retrieval timeout from default 15s to 10s to account for higher internet speeds
377+
parameters.dwUrlRetrievalTimeout = 10 * 1000; // milliseconds
374378

375379
// SAFETY: `cert` points to a valid certificate context, parameters is valid for reads, `cert_chain` is valid
376380
// for writes, and the certificate store is valid and initialized.
@@ -392,12 +396,6 @@ impl CertificateStore {
392396
_ => None,
393397
})?;
394398

395-
{
396-
// SAFETY: `cert_chain` was just initialized with a valid pointer.
397-
let cert_chain: &_ = unsafe { cert_chain.as_ref() };
398-
map_trust_error_status(cert_chain.TrustStatus.dwErrorStatus)?;
399-
}
400-
401399
Ok(CertChain { inner: cert_chain })
402400
}
403401
}
@@ -412,81 +410,6 @@ fn call_with_last_error<T, F: FnMut() -> Option<T>>(mut call: F) -> Result<T, Tl
412410
}
413411
}
414412

415-
// Maps a valid `cert_chain.TrustStatus.dwErrorStatus` to a `TLSError`.
416-
//
417-
// See Chromium's `MapCertChainErrorStatusToCertStatus` in
418-
// https://chromium.googlesource.com/chromium/src/net/+/refs/heads/main/cert/cert_verify_proc_win.cc.
419-
fn map_trust_error_status(unfiltered_status: DWORD) -> Result<(), TlsError> {
420-
// Returns true if the only bitflags set in |status| are in |flags|
421-
// and at least one of the bitflags in |flags| is set in |status|.
422-
#[must_use]
423-
fn only_flags_set(status: DWORD, flags: DWORD) -> bool {
424-
((status & !flags) == 0) && ((status & flags) != 0)
425-
}
426-
427-
// Ignore errors related to missing revocation info, so that a network
428-
// partition between the client and the CA's OCSP/CRL servers does not
429-
// cause the connection to fail.
430-
//
431-
// Unlike Chromium, we don't differentiate between "no revocation mechanism"
432-
// and "unable to check revocation."
433-
const UNABLE_TO_CHECK_REVOCATION: DWORD =
434-
wincrypt::CERT_TRUST_REVOCATION_STATUS_UNKNOWN | wincrypt::CERT_TRUST_IS_OFFLINE_REVOCATION;
435-
let status = unfiltered_status & !UNABLE_TO_CHECK_REVOCATION;
436-
437-
// If there are no errors, then we're done.
438-
if status == wincrypt::CERT_TRUST_NO_ERROR {
439-
return Ok(());
440-
}
441-
442-
// Windows may return multiple errors (webpki only returns one). Rustls
443-
// only allows a single error to be returned. Group the errors into
444-
// classes roughly similar to the ones used by Chromium, and then
445-
// choose what to do based on the class.
446-
447-
// If the certificate is revoked, then return that, ignoring other errors,
448-
// as we consider revocation to be super critical.
449-
if (status & wincrypt::CERT_TRUST_IS_REVOKED) != 0 {
450-
return Err(InvalidCertificate(CertificateError::Revoked));
451-
}
452-
453-
if only_flags_set(
454-
status,
455-
wincrypt::CERT_TRUST_IS_NOT_VALID_FOR_USAGE
456-
| wincrypt::CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE,
457-
) {
458-
return Err(InvalidCertificate(CertificateError::Other(
459-
std::sync::Arc::new(super::EkuError),
460-
)));
461-
}
462-
463-
// Otherwise, if there is only one class of error, map that class to
464-
// a well-known error string (for testing and debugging).
465-
if only_flags_set(
466-
status,
467-
wincrypt::CERT_TRUST_IS_NOT_TIME_VALID | wincrypt::CERT_TRUST_CTL_IS_NOT_TIME_VALID,
468-
) {
469-
return Err(InvalidCertificate(CertificateError::Expired));
470-
}
471-
472-
// XXX: winapi doesn't expose this.
473-
const CERT_TRUST_IS_EXPLICIT_DISTRUST: DWORD = 0x04000000;
474-
if only_flags_set(
475-
status,
476-
wincrypt::CERT_TRUST_IS_UNTRUSTED_ROOT
477-
| CERT_TRUST_IS_EXPLICIT_DISTRUST
478-
| wincrypt::CERT_TRUST_IS_PARTIAL_CHAIN,
479-
) {
480-
return Err(InvalidCertificate(CertificateError::UnknownIssuer));
481-
}
482-
483-
// Return an error that contains exactly what Windows told us.
484-
Err(invalid_certificate(format!(
485-
"Bad certificate chain with Windows error status {}",
486-
unfiltered_status
487-
)))
488-
}
489-
490413
/// A TLS certificate verifier that utilizes the Windows certificate facilities.
491414
#[derive(Default)]
492415
pub struct Verifier {
@@ -579,6 +502,9 @@ impl Verifier {
579502
InvalidCertificate(CertificateError::NotValidForName)
580503
}
581504
CRYPT_E_REVOKED => InvalidCertificate(CertificateError::Revoked),
505+
CERT_E_EXPIRED => InvalidCertificate(CertificateError::Expired),
506+
CERT_E_UNTRUSTEDROOT => InvalidCertificate(CertificateError::UnknownIssuer),
507+
CERT_E_WRONG_USAGE => InvalidCertificate(CertificateError::InvalidPurpose),
582508
error_num => {
583509
let err = std::io::Error::from_raw_os_error(error_num);
584510
// The included error message has both the description and raw OS error code.

0 commit comments

Comments
 (0)