@@ -2,8 +2,9 @@ use super::log_server_cert;
2
2
use once_cell:: sync:: OnceCell ;
3
3
use rustls:: {
4
4
client:: { ServerCertVerifier , WebPkiVerifier } ,
5
- CertificateError , Error as TlsError , RootCertStore ,
5
+ CertificateError , Error as TlsError ,
6
6
} ;
7
+ use std:: sync:: Mutex ;
7
8
8
9
/// A TLS certificate verifier that uses the system's root store and WebPKI.
9
10
#[ derive( Default ) ]
@@ -17,17 +18,34 @@ pub struct Verifier {
17
18
// that might have been made since.
18
19
inner : OnceCell < WebPkiVerifier > ,
19
20
21
+ // Extra trust anchors to add to the verifier above and beyond those provided by the
22
+ // platform via rustls-native-certs.
23
+ extra_roots : Mutex < Vec < rustls:: OwnedTrustAnchor > > ,
24
+
20
25
/// Testing only: an additional root CA certificate to trust.
21
26
#[ cfg( any( test, feature = "ffi-testing" , feature = "dbg" ) ) ]
22
27
test_only_root_ca_override : Option < Vec < u8 > > ,
23
28
}
24
29
25
30
impl Verifier {
26
31
/// Creates a new verifier whose certificate validation is provided by
27
- /// WebPKI.
32
+ /// WebPKI, using root certificates provided by the platform .
28
33
pub fn new ( ) -> Self {
29
34
Self {
30
35
inner : OnceCell :: new ( ) ,
36
+ extra_roots : Vec :: new ( ) . into ( ) ,
37
+ #[ cfg( any( test, feature = "ffi-testing" , feature = "dbg" ) ) ]
38
+ test_only_root_ca_override : None ,
39
+ }
40
+ }
41
+
42
+ /// Creates a new verifier whose certificate validation is provided by
43
+ /// WebPKI, using root certificates provided by the platform and augmented by
44
+ /// the provided extra root certificates.
45
+ pub fn new_with_extra_roots ( roots : impl IntoIterator < Item = rustls:: OwnedTrustAnchor > ) -> Self {
46
+ Self {
47
+ inner : OnceCell :: new ( ) ,
48
+ extra_roots : roots. into_iter ( ) . collect :: < Vec < _ > > ( ) . into ( ) ,
31
49
#[ cfg( any( test, feature = "ffi-testing" , feature = "dbg" ) ) ]
32
50
test_only_root_ca_override : None ,
33
51
}
@@ -38,6 +56,7 @@ impl Verifier {
38
56
pub ( crate ) fn new_with_fake_root ( root : & [ u8 ] ) -> Self {
39
57
Self {
40
58
inner : OnceCell :: new ( ) ,
59
+ extra_roots : Vec :: new ( ) . into ( ) ,
41
60
test_only_root_ca_override : Some ( root. into ( ) ) ,
42
61
}
43
62
}
@@ -68,35 +87,42 @@ impl Verifier {
68
87
log:: warn!( "Some CA root certificates were ignored due to errors" ) ;
69
88
}
70
89
71
- // While we load webpki-roots anyway, this can be helpful to know for troubleshooting.
72
90
if root_store. is_empty ( ) {
73
91
log:: error!( "No CA certificates were loaded from the system" ) ;
92
+ } else {
93
+ log:: debug!( "Loaded {added} CA certificates from the system" ) ;
74
94
}
75
95
76
- // Finding TLS roots on Linux is not reliable enough to always depend on it
77
- // across various distributions. Instead, we always load the WebPKI roots in
78
- // addition so that a valid trust anchor is more likely to be available.
79
- load_webpki_roots ( & mut root_store) ;
80
-
81
- log:: debug!(
82
- "Loaded WebPKI roots in addition to {} roots from the system" ,
83
- added
84
- ) ;
96
+ // Safety: There's no way for the mutex to be locked multiple times, so this is
97
+ // an infallible operation.
98
+ let mut extra_roots = self . extra_roots . try_lock ( ) . unwrap ( ) ;
99
+ if !extra_roots. is_empty ( ) {
100
+ let count = extra_roots. len ( ) ;
101
+ root_store. add_trust_anchors ( & mut extra_roots. drain ( ..) ) ;
102
+ log:: debug!(
103
+ "Loaded {count} extra CA certificates in addition to roots from the system" ,
104
+ ) ;
105
+ }
85
106
}
86
107
Err ( err) => {
87
108
// This only contains a path to a system directory:
88
- // https://github.com/rustls/rustls-native-certs/blob/main/src/lib.rs#L71
89
- log:: error!(
90
- "No CA certificates were loaded: {}. Falling back to WebPKI roots" ,
91
- err,
92
- ) ;
93
- load_webpki_roots ( & mut root_store) ;
109
+ // https://github.com/rustls/rustls-native-certs/blob/bc13b9a6bfc2e1eec881597055ca49accddd972a/src/lib.rs#L91-L94
110
+ return Err ( rustls:: Error :: General ( format ! (
111
+ "failed to load system root certificates: {}" ,
112
+ err
113
+ ) ) ) ;
94
114
}
95
115
} ;
96
116
97
117
#[ cfg( target_arch = "wasm32" ) ]
98
118
{
99
- load_webpki_roots ( & mut root_store) ;
119
+ root_store. add_trust_anchors ( webpki_roots:: TLS_SERVER_ROOTS . iter ( ) . map ( |root| {
120
+ rustls:: OwnedTrustAnchor :: from_subject_spki_name_constraints (
121
+ root. subject ,
122
+ root. spki ,
123
+ root. name_constraints ,
124
+ )
125
+ } ) ) ;
100
126
} ;
101
127
102
128
Ok ( WebPkiVerifier :: new ( root_store, None ) )
@@ -152,16 +178,3 @@ fn map_webpki_errors(err: TlsError) -> TlsError {
152
178
153
179
err
154
180
}
155
-
156
- /// Loads the static `webpki-roots` into the provided certificate store.
157
- fn load_webpki_roots ( store : & mut RootCertStore ) {
158
- use rustls:: OwnedTrustAnchor ;
159
-
160
- store. add_trust_anchors ( webpki_roots:: TLS_SERVER_ROOTS . iter ( ) . map ( |root| {
161
- OwnedTrustAnchor :: from_subject_spki_name_constraints (
162
- root. subject ,
163
- root. spki ,
164
- root. name_constraints ,
165
- )
166
- } ) ) ;
167
- }
0 commit comments