11use anyhow:: { bail, Context , Result } ;
22use common:: encoding:: encode_utf16le;
33use hex:: ToHex ;
4- use log:: { debug, info} ;
4+ use log:: { debug, info, warn } ;
55use sha1:: { Digest , Sha1 } ;
6+ use std:: collections:: HashMap ;
67use std:: fs;
78use std:: io:: BufReader ;
89use std:: sync:: Arc ;
@@ -66,7 +67,7 @@ pub fn compute_thumbprint(cert_content: &[u8]) -> String {
6667
6768pub struct TlsConfig {
6869 pub server : Arc < ServerConfig > ,
69- pub thumbprint : String ,
70+ pub thumbprints : HashMap < String , String > ,
7071}
7172
7273/// Create configuration for TLS connection
@@ -77,18 +78,35 @@ pub fn make_config(args: &common::settings::Tls) -> Result<TlsConfig> {
7778 load_priv_key ( args. server_private_key ( ) ) . context ( "Could not load private key" ) ?;
7879 let ca_certs = load_certs ( args. ca_certificate ( ) ) . context ( "Could not load CA certificate" ) ?;
7980
80- let ca_cert_content: & [ u8 ] = ca_certs
81- . first ( )
82- . context ( "CA certificate should contain at least one certificate" ) ?
83- . as_ref ( ) ;
84- let thumbprint = compute_thumbprint ( ca_cert_content) ;
85-
86- debug ! ( "CA Thumbprint from certificate : {}" , thumbprint) ;
81+ // Ensure at least one CA is present
82+ if ca_certs. is_empty ( ) {
83+ bail ! ( "CA certificate should contain at least one certificate" ) ;
84+ }
8785
8886 let mut client_auth_roots = RootCertStore :: empty ( ) ;
87+ let mut ca_thumbprints = HashMap :: new ( ) ;
8988
90- // Put all certificates from given CA certificate file into certificate store
89+ // Compute thumbprint for each CA and add to trust store
9190 for root in ca_certs {
91+ let subject = subject_from_cert ( root. as_ref ( ) ) ?;
92+ let thumbprint = compute_thumbprint ( root. as_ref ( ) ) ;
93+ debug ! ( "CA Thumbprint from certificate: {}" , & thumbprint) ;
94+ if let Some ( existing) = ca_thumbprints. get ( & subject) {
95+ if existing != & thumbprint {
96+ bail ! (
97+ "Duplicate CA subject with different thumbprints: {}" ,
98+ & subject
99+ ) ;
100+ }
101+ // already present, skip, but warn
102+ warn ! (
103+ "Duplicate CA certificate found for subject: {}, thumbprint: {}" ,
104+ & subject, & thumbprint
105+ ) ;
106+ continue ;
107+ }
108+ ca_thumbprints. insert ( subject, thumbprint) ;
109+
92110 client_auth_roots
93111 . add ( root)
94112 . context ( "Could not add certificate to root of trust" ) ?;
@@ -107,20 +125,21 @@ pub fn make_config(args: &common::settings::Tls) -> Result<TlsConfig> {
107125 . with_protocol_versions ( ALL_VERSIONS )
108126 . context ( "Could not build configuration defaults" ) ?
109127 . with_client_cert_verifier ( client_cert_verifier) // add verifier
110- . with_single_cert ( cert, priv_key) // add server vertification
128+ . with_single_cert ( cert, priv_key) // add server verification
111129 . context ( "Bad configuration certificate or key" ) ?;
112130
113131 // any http version is ok
114132 config. alpn_protocols = vec ! [ b"h2" . to_vec( ) , b"http/1.1" . to_vec( ) , b"http/1.0" . to_vec( ) ] ;
115133
116134 info ! (
117- "Loaded TLS configuration with server certificate {}" ,
118- args. server_certificate( )
135+ "Loaded TLS configuration with server certificate {} and {} CA(s)" ,
136+ args. server_certificate( ) ,
137+ ca_thumbprints. len( )
119138 ) ;
120139
121140 Ok ( TlsConfig {
122141 server : Arc :: new ( config) ,
123- thumbprint ,
142+ thumbprints : ca_thumbprints ,
124143 } )
125144}
126145
@@ -158,6 +177,57 @@ pub fn subject_from_cert(cert: &[u8]) -> Result<String> {
158177 bail ! ( "CommonName not found" )
159178}
160179
180+ pub fn issuer_from_cert ( cert : & [ u8 ] ) -> Result < String > {
181+ // load certificate to decompose its content
182+ let cert = X509Certificate :: from_der ( cert) ?. 1 ;
183+
184+ let oid_registry = OidRegistry :: default ( ) . with_x509 ( ) ; // registry of OIDs we will need
185+ let rdn_iter = cert. issuer . iter_rdn ( ) ; // iterator on RDNs
186+
187+ for subject_attribute in rdn_iter {
188+ // Each entry contains a list of AttributeTypeAndValue objects,
189+ // so we fetch their attribute type (= the OID representing each of them)
190+ let sn = subject_attribute. iter ( ) ;
191+
192+ for set in sn {
193+ // OID of the sub-entry
194+ let typ = set. attr_type ( ) ;
195+
196+ // get the SN corresponding to the OID (None if it does not exist)
197+ let oid_reg = oid_registry. get ( typ) . map ( |oid| oid. sn ( ) ) ;
198+
199+ // the value we are interested in is only contained where the commonName is
200+ if oid_reg == Some ( "commonName" ) {
201+ // get data as text => FQDN of the client
202+ if let Ok ( name) = set. as_str ( ) {
203+ return Ok ( name. to_string ( ) ) ;
204+ } else {
205+ bail ! ( "CommonName is empty" )
206+ }
207+ }
208+ }
209+ }
210+ bail ! ( "CommonName not found" )
211+ }
212+
213+ pub fn find_matching_ca (
214+ peer_certs : & [ CertificateDer ] ,
215+ ca_thumbprints : & HashMap < String , String > ,
216+ ) -> Result < String > {
217+ peer_certs
218+ . iter ( )
219+ . find_map ( |cert| {
220+ let issuer = issuer_from_cert ( cert. as_ref ( ) ) . ok ( ) ?;
221+ debug ! ( "Checking issuer '{}'" , & issuer) ;
222+
223+ ca_thumbprints. get ( & issuer) . map ( |ca_entry| {
224+ debug ! ( "Found matching CA for issuer '{}'" , & issuer) ;
225+ ca_entry. clone ( )
226+ } )
227+ } )
228+ . context ( "No trusted CA found in certificate chain" )
229+ }
230+
161231/// Read and decode request payload
162232pub async fn get_request_payload (
163233 parts : hyper:: http:: request:: Parts ,
0 commit comments