1 //! TLS configuration
2 //!
3 //! By default, a `Client` will make use of system-native transport layer
4 //! security to connect to HTTPS destinations. This means schannel on Windows,
5 //! Security-Framework on macOS, and OpenSSL on Linux.
6 //!
7 //! - Additional X509 certificates can be configured on a `ClientBuilder` with the
8 //!   [`Certificate`](Certificate) type.
9 //! - Client certificates can be add to a `ClientBuilder` with the
10 //!   [`Identity`][Identity] type.
11 //! - Various parts of TLS can also be configured or even disabled on the
12 //!   `ClientBuilder`.
13 
14 #[cfg(feature = "__rustls")]
15 use rustls::{
16     client::HandshakeSignatureValid, client::ServerCertVerified, client::ServerCertVerifier,
17     internal::msgs::handshake::DigitallySignedStruct, Error as TLSError, ServerName,
18 };
19 use std::fmt;
20 
21 /// Represents a server X509 certificate.
22 #[derive(Clone)]
23 pub struct Certificate {
24     #[cfg(feature = "native-tls-crate")]
25     native: native_tls_crate::Certificate,
26     #[cfg(feature = "__rustls")]
27     original: Cert,
28 }
29 
30 #[cfg(feature = "__rustls")]
31 #[derive(Clone)]
32 enum Cert {
33     Der(Vec<u8>),
34     Pem(Vec<u8>),
35 }
36 
37 /// Represents a private key and X509 cert as a client certificate.
38 #[derive(Clone)]
39 pub struct Identity {
40     #[cfg_attr(not(any(feature = "native-tls", feature = "__rustls")), allow(unused))]
41     inner: ClientCert,
42 }
43 
44 #[derive(Clone)]
45 enum ClientCert {
46     #[cfg(feature = "native-tls")]
47     Pkcs12(native_tls_crate::Identity),
48     #[cfg(feature = "__rustls")]
49     Pem {
50         key: rustls::PrivateKey,
51         certs: Vec<rustls::Certificate>,
52     },
53 }
54 
55 impl Certificate {
56     /// Create a `Certificate` from a binary DER encoded certificate
57     ///
58     /// # Examples
59     ///
60     /// ```
61     /// # use std::fs::File;
62     /// # use std::io::Read;
63     /// # fn cert() -> Result<(), Box<std::error::Error>> {
64     /// let mut buf = Vec::new();
65     /// File::open("my_cert.der")?
66     ///     .read_to_end(&mut buf)?;
67     /// let cert = reqwest::Certificate::from_der(&buf)?;
68     /// # drop(cert);
69     /// # Ok(())
70     /// # }
71     /// ```
from_der(der: &[u8]) -> crate::Result<Certificate>72     pub fn from_der(der: &[u8]) -> crate::Result<Certificate> {
73         Ok(Certificate {
74             #[cfg(feature = "native-tls-crate")]
75             native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?,
76             #[cfg(feature = "__rustls")]
77             original: Cert::Der(der.to_owned()),
78         })
79     }
80 
81     /// Create a `Certificate` from a PEM encoded certificate
82     ///
83     /// # Examples
84     ///
85     /// ```
86     /// # use std::fs::File;
87     /// # use std::io::Read;
88     /// # fn cert() -> Result<(), Box<std::error::Error>> {
89     /// let mut buf = Vec::new();
90     /// File::open("my_cert.pem")?
91     ///     .read_to_end(&mut buf)?;
92     /// let cert = reqwest::Certificate::from_pem(&buf)?;
93     /// # drop(cert);
94     /// # Ok(())
95     /// # }
96     /// ```
from_pem(pem: &[u8]) -> crate::Result<Certificate>97     pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> {
98         Ok(Certificate {
99             #[cfg(feature = "native-tls-crate")]
100             native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?,
101             #[cfg(feature = "__rustls")]
102             original: Cert::Pem(pem.to_owned()),
103         })
104     }
105 
106     #[cfg(feature = "native-tls-crate")]
add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder)107     pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) {
108         tls.add_root_certificate(self.native);
109     }
110 
111     #[cfg(feature = "__rustls")]
add_to_rustls( self, root_cert_store: &mut rustls::RootCertStore, ) -> crate::Result<()>112     pub(crate) fn add_to_rustls(
113         self,
114         root_cert_store: &mut rustls::RootCertStore,
115     ) -> crate::Result<()> {
116         use std::io::Cursor;
117 
118         match self.original {
119             Cert::Der(buf) => root_cert_store
120                 .add(&rustls::Certificate(buf))
121                 .map_err(crate::error::builder)?,
122             Cert::Pem(buf) => {
123                 let mut pem = Cursor::new(buf);
124                 let certs = rustls_pemfile::certs(&mut pem).map_err(|_| {
125                     crate::error::builder(TLSError::General(String::from(
126                         "No valid certificate was found",
127                     )))
128                 })?;
129                 for c in certs {
130                     root_cert_store
131                         .add(&rustls::Certificate(c))
132                         .map_err(crate::error::builder)?;
133                 }
134             }
135         }
136         Ok(())
137     }
138 }
139 
140 impl Identity {
141     /// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key.
142     ///
143     /// The archive should contain a leaf certificate and its private key, as well any intermediate
144     /// certificates that allow clients to build a chain to a trusted root.
145     /// The chain certificates should be in order from the leaf certificate towards the root.
146     ///
147     /// PKCS #12 archives typically have the file extension `.p12` or `.pfx`, and can be created
148     /// with the OpenSSL `pkcs12` tool:
149     ///
150     /// ```bash
151     /// openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem
152     /// ```
153     ///
154     /// # Examples
155     ///
156     /// ```
157     /// # use std::fs::File;
158     /// # use std::io::Read;
159     /// # fn pkcs12() -> Result<(), Box<std::error::Error>> {
160     /// let mut buf = Vec::new();
161     /// File::open("my-ident.pfx")?
162     ///     .read_to_end(&mut buf)?;
163     /// let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?;
164     /// # drop(pkcs12);
165     /// # Ok(())
166     /// # }
167     /// ```
168     ///
169     /// # Optional
170     ///
171     /// This requires the `native-tls` Cargo feature enabled.
172     #[cfg(feature = "native-tls")]
from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity>173     pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> {
174         Ok(Identity {
175             inner: ClientCert::Pkcs12(
176                 native_tls_crate::Identity::from_pkcs12(der, password)
177                     .map_err(crate::error::builder)?,
178             ),
179         })
180     }
181 
182     /// Parses PEM encoded private key and certificate.
183     ///
184     /// The input should contain a PEM encoded private key
185     /// and at least one PEM encoded certificate.
186     ///
187     /// Note: The private key must be in RSA or PKCS#8 format.
188     ///
189     /// # Examples
190     ///
191     /// ```
192     /// # use std::fs::File;
193     /// # use std::io::Read;
194     /// # fn pem() -> Result<(), Box<std::error::Error>> {
195     /// let mut buf = Vec::new();
196     /// File::open("my-ident.pem")?
197     ///     .read_to_end(&mut buf)?;
198     /// let id = reqwest::Identity::from_pem(&buf)?;
199     /// # drop(id);
200     /// # Ok(())
201     /// # }
202     /// ```
203     ///
204     /// # Optional
205     ///
206     /// This requires the `rustls-tls(-...)` Cargo feature enabled.
207     #[cfg(feature = "__rustls")]
from_pem(buf: &[u8]) -> crate::Result<Identity>208     pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> {
209         use std::io::Cursor;
210 
211         let (key, certs) = {
212             let mut pem = Cursor::new(buf);
213             let certs = rustls_pemfile::certs(&mut pem)
214                 .map_err(|_| TLSError::General(String::from("No valid certificate was found")))
215                 .map_err(crate::error::builder)?
216                 .into_iter()
217                 .map(rustls::Certificate)
218                 .collect::<Vec<_>>();
219             pem.set_position(0);
220             let mut sk = rustls_pemfile::pkcs8_private_keys(&mut pem)
221                 .and_then(|pkcs8_keys| {
222                     if pkcs8_keys.is_empty() {
223                         Err(std::io::Error::new(
224                             std::io::ErrorKind::NotFound,
225                             "No valid private key was found",
226                         ))
227                     } else {
228                         Ok(pkcs8_keys)
229                     }
230                 })
231                 .or_else(|_| {
232                     pem.set_position(0);
233                     rustls_pemfile::rsa_private_keys(&mut pem)
234                 })
235                 .map_err(|_| TLSError::General(String::from("No valid private key was found")))
236                 .map_err(crate::error::builder)?
237                 .into_iter()
238                 .map(rustls::PrivateKey)
239                 .collect::<Vec<_>>();
240             if let (Some(sk), false) = (sk.pop(), certs.is_empty()) {
241                 (sk, certs)
242             } else {
243                 return Err(crate::error::builder(TLSError::General(String::from(
244                     "private key or certificate not found",
245                 ))));
246             }
247         };
248 
249         Ok(Identity {
250             inner: ClientCert::Pem { key, certs },
251         })
252     }
253 
254     #[cfg(feature = "native-tls")]
add_to_native_tls( self, tls: &mut native_tls_crate::TlsConnectorBuilder, ) -> crate::Result<()>255     pub(crate) fn add_to_native_tls(
256         self,
257         tls: &mut native_tls_crate::TlsConnectorBuilder,
258     ) -> crate::Result<()> {
259         match self.inner {
260             ClientCert::Pkcs12(id) => {
261                 tls.identity(id);
262                 Ok(())
263             }
264             #[cfg(feature = "__rustls")]
265             ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")),
266         }
267     }
268 
269     #[cfg(feature = "__rustls")]
add_to_rustls( self, config_builder: rustls::ConfigBuilder< rustls::ClientConfig, rustls::client::WantsTransparencyPolicyOrClientCert, >, ) -> crate::Result<rustls::ClientConfig>270     pub(crate) fn add_to_rustls(
271         self,
272         config_builder: rustls::ConfigBuilder<
273             rustls::ClientConfig,
274             rustls::client::WantsTransparencyPolicyOrClientCert,
275         >,
276     ) -> crate::Result<rustls::ClientConfig> {
277         match self.inner {
278             ClientCert::Pem { key, certs } => config_builder
279                 .with_single_cert(certs, key)
280                 .map_err(crate::error::builder),
281             #[cfg(feature = "native-tls")]
282             ClientCert::Pkcs12(..) => Err(crate::error::builder("incompatible TLS identity type")),
283         }
284     }
285 }
286 
287 impl fmt::Debug for Certificate {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result288     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
289         f.debug_struct("Certificate").finish()
290     }
291 }
292 
293 impl fmt::Debug for Identity {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result294     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
295         f.debug_struct("Identity").finish()
296     }
297 }
298 
299 /// A TLS protocol version.
300 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
301 pub struct Version(InnerVersion);
302 
303 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
304 #[non_exhaustive]
305 enum InnerVersion {
306     Tls1_0,
307     Tls1_1,
308     Tls1_2,
309     Tls1_3,
310 }
311 
312 // These could perhaps be From/TryFrom implementations, but those would be
313 // part of the public API so let's be careful
314 impl Version {
315     /// Version 1.0 of the TLS protocol.
316     pub const TLS_1_0: Version = Version(InnerVersion::Tls1_0);
317     /// Version 1.1 of the TLS protocol.
318     pub const TLS_1_1: Version = Version(InnerVersion::Tls1_1);
319     /// Version 1.2 of the TLS protocol.
320     pub const TLS_1_2: Version = Version(InnerVersion::Tls1_2);
321     /// Version 1.3 of the TLS protocol.
322     pub const TLS_1_3: Version = Version(InnerVersion::Tls1_3);
323 
324     #[cfg(feature = "default-tls")]
to_native_tls(self) -> Option<native_tls_crate::Protocol>325     pub(crate) fn to_native_tls(self) -> Option<native_tls_crate::Protocol> {
326         match self.0 {
327             InnerVersion::Tls1_0 => Some(native_tls_crate::Protocol::Tlsv10),
328             InnerVersion::Tls1_1 => Some(native_tls_crate::Protocol::Tlsv11),
329             InnerVersion::Tls1_2 => Some(native_tls_crate::Protocol::Tlsv12),
330             InnerVersion::Tls1_3 => None,
331         }
332     }
333 
334     #[cfg(feature = "__rustls")]
from_rustls(version: rustls::ProtocolVersion) -> Option<Self>335     pub(crate) fn from_rustls(version: rustls::ProtocolVersion) -> Option<Self> {
336         match version {
337             rustls::ProtocolVersion::SSLv2 => None,
338             rustls::ProtocolVersion::SSLv3 => None,
339             rustls::ProtocolVersion::TLSv1_0 => Some(Self(InnerVersion::Tls1_0)),
340             rustls::ProtocolVersion::TLSv1_1 => Some(Self(InnerVersion::Tls1_1)),
341             rustls::ProtocolVersion::TLSv1_2 => Some(Self(InnerVersion::Tls1_2)),
342             rustls::ProtocolVersion::TLSv1_3 => Some(Self(InnerVersion::Tls1_3)),
343             _ => None,
344         }
345     }
346 }
347 
348 pub(crate) enum TlsBackend {
349     #[cfg(feature = "default-tls")]
350     Default,
351     #[cfg(feature = "native-tls")]
352     BuiltNativeTls(native_tls_crate::TlsConnector),
353     #[cfg(feature = "__rustls")]
354     Rustls,
355     #[cfg(feature = "__rustls")]
356     BuiltRustls(rustls::ClientConfig),
357     #[cfg(any(feature = "native-tls", feature = "__rustls",))]
358     UnknownPreconfigured,
359 }
360 
361 impl fmt::Debug for TlsBackend {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result362     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
363         match self {
364             #[cfg(feature = "default-tls")]
365             TlsBackend::Default => write!(f, "Default"),
366             #[cfg(feature = "native-tls")]
367             TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls"),
368             #[cfg(feature = "__rustls")]
369             TlsBackend::Rustls => write!(f, "Rustls"),
370             #[cfg(feature = "__rustls")]
371             TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls"),
372             #[cfg(any(feature = "native-tls", feature = "__rustls",))]
373             TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"),
374         }
375     }
376 }
377 
378 impl Default for TlsBackend {
default() -> TlsBackend379     fn default() -> TlsBackend {
380         #[cfg(feature = "default-tls")]
381         {
382             TlsBackend::Default
383         }
384 
385         #[cfg(all(feature = "__rustls", not(feature = "default-tls")))]
386         {
387             TlsBackend::Rustls
388         }
389     }
390 }
391 
392 #[cfg(feature = "__rustls")]
393 pub(crate) struct NoVerifier;
394 
395 #[cfg(feature = "__rustls")]
396 impl ServerCertVerifier for NoVerifier {
verify_server_cert( &self, _end_entity: &rustls::Certificate, _intermediates: &[rustls::Certificate], _server_name: &ServerName, _scts: &mut dyn Iterator<Item = &[u8]>, _ocsp_response: &[u8], _now: std::time::SystemTime, ) -> Result<ServerCertVerified, TLSError>397     fn verify_server_cert(
398         &self,
399         _end_entity: &rustls::Certificate,
400         _intermediates: &[rustls::Certificate],
401         _server_name: &ServerName,
402         _scts: &mut dyn Iterator<Item = &[u8]>,
403         _ocsp_response: &[u8],
404         _now: std::time::SystemTime,
405     ) -> Result<ServerCertVerified, TLSError> {
406         Ok(ServerCertVerified::assertion())
407     }
408 
verify_tls12_signature( &self, _message: &[u8], _cert: &rustls::Certificate, _dss: &DigitallySignedStruct, ) -> Result<HandshakeSignatureValid, TLSError>409     fn verify_tls12_signature(
410         &self,
411         _message: &[u8],
412         _cert: &rustls::Certificate,
413         _dss: &DigitallySignedStruct,
414     ) -> Result<HandshakeSignatureValid, TLSError> {
415         Ok(HandshakeSignatureValid::assertion())
416     }
417 
verify_tls13_signature( &self, _message: &[u8], _cert: &rustls::Certificate, _dss: &DigitallySignedStruct, ) -> Result<HandshakeSignatureValid, TLSError>418     fn verify_tls13_signature(
419         &self,
420         _message: &[u8],
421         _cert: &rustls::Certificate,
422         _dss: &DigitallySignedStruct,
423     ) -> Result<HandshakeSignatureValid, TLSError> {
424         Ok(HandshakeSignatureValid::assertion())
425     }
426 }
427 
428 #[cfg(test)]
429 mod tests {
430     use super::*;
431 
432     #[cfg(feature = "default-tls")]
433     #[test]
certificate_from_der_invalid()434     fn certificate_from_der_invalid() {
435         Certificate::from_der(b"not der").unwrap_err();
436     }
437 
438     #[cfg(feature = "default-tls")]
439     #[test]
certificate_from_pem_invalid()440     fn certificate_from_pem_invalid() {
441         Certificate::from_pem(b"not pem").unwrap_err();
442     }
443 
444     #[cfg(feature = "native-tls")]
445     #[test]
identity_from_pkcs12_der_invalid()446     fn identity_from_pkcs12_der_invalid() {
447         Identity::from_pkcs12_der(b"not der", "nope").unwrap_err();
448     }
449 
450     #[cfg(feature = "__rustls")]
451     #[test]
identity_from_pem_invalid()452     fn identity_from_pem_invalid() {
453         Identity::from_pem(b"not pem").unwrap_err();
454     }
455 
456     #[cfg(feature = "__rustls")]
457     #[test]
identity_from_pem_pkcs1_key()458     fn identity_from_pem_pkcs1_key() {
459         let pem = b"-----BEGIN CERTIFICATE-----\n\
460             -----END CERTIFICATE-----\n\
461             -----BEGIN RSA PRIVATE KEY-----\n\
462             -----END RSA PRIVATE KEY-----\n";
463 
464         Identity::from_pem(pem).unwrap();
465     }
466 }
467