@@ -25,22 +25,25 @@ use crate::windows::{
25
25
use rustls:: { client:: ServerCertVerifier , CertificateError , Error as TlsError } ;
26
26
use winapi:: {
27
27
shared:: {
28
- minwindef:: { DWORD , FILETIME , TRUE } ,
28
+ minwindef:: { FILETIME , TRUE } ,
29
29
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
+ } ,
31
34
} ,
32
35
um:: wincrypt:: {
33
- self , CertAddEncodedCertificateToStore , CertCloseStore , CertFreeCertificateChain ,
36
+ CertAddEncodedCertificateToStore , CertCloseStore , CertFreeCertificateChain ,
34
37
CertFreeCertificateChainEngine , CertFreeCertificateContext , CertGetCertificateChain ,
35
38
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 ,
37
40
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 ,
44
47
} ,
45
48
} ;
46
49
@@ -364,13 +367,14 @@ impl CertificateStore {
364
367
}
365
368
} ;
366
369
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
374
378
375
379
// SAFETY: `cert` points to a valid certificate context, parameters is valid for reads, `cert_chain` is valid
376
380
// for writes, and the certificate store is valid and initialized.
@@ -392,12 +396,6 @@ impl CertificateStore {
392
396
_ => None ,
393
397
} ) ?;
394
398
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
-
401
399
Ok ( CertChain { inner : cert_chain } )
402
400
}
403
401
}
@@ -412,81 +410,6 @@ fn call_with_last_error<T, F: FnMut() -> Option<T>>(mut call: F) -> Result<T, Tl
412
410
}
413
411
}
414
412
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
-
490
413
/// A TLS certificate verifier that utilizes the Windows certificate facilities.
491
414
#[ derive( Default ) ]
492
415
pub struct Verifier {
@@ -579,6 +502,9 @@ impl Verifier {
579
502
InvalidCertificate ( CertificateError :: NotValidForName )
580
503
}
581
504
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 ) ,
582
508
error_num => {
583
509
let err = std:: io:: Error :: from_raw_os_error ( error_num) ;
584
510
// The included error message has both the description and raw OS error code.
0 commit comments