Repository: seanmonstar/reqwest
Pull request #1150 (2021-01-21 23:16:30)

Add a use_built_in_root_certificates option for Client
Allows not using built-in certificates, but only certificates provided with `add_root_certificate`.

amousset: I started from the option's name in `native-tls`, but `tls_built_in_root_certs` will be more consistent with other reqwest options!

Patch:
diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs
index b6adc64..a2ea20b 100644
--- a/src/async_impl/client.rs
+++ b/src/async_impl/client.rs
@@ -94,6 +94,8 @@ struct Config {
     #[cfg(feature = "__tls")]
     root_certs: Vec<Certificate>,
     #[cfg(feature = "__tls")]
+    tls_built_in_root_certs: bool,
+    #[cfg(feature = "__tls")]
     tls: TlsBackend,
     http2_only: bool,
     http1_title_case_headers: bool,
@@ -146,6 +148,8 @@ impl ClientBuilder {
                 #[cfg(feature = "__tls")]
                 root_certs: Vec::new(),
                 #[cfg(feature = "__tls")]
+                tls_built_in_root_certs: true,
+                #[cfg(feature = "__tls")]
                 identity: None,
                 #[cfg(feature = "__tls")]
                 tls: TlsBackend::default(),
@@ -209,6 +213,8 @@ impl ClientBuilder {
 
                     tls.danger_accept_invalid_certs(!config.certs_verification);
 
+                    tls.disable_built_in_roots(!config.tls_built_in_root_certs);
+
                     for cert in config.root_certs {
                         cert.add_to_native_tls(&mut tls);
                     }
@@ -261,10 +267,12 @@ impl ClientBuilder {
                         tls.set_protocols(&["h2".into(), "http/1.1".into()]);
                     }
                     #[cfg(feature = "rustls-tls-webpki-roots")]
+                    if config.tls_built_in_root_certs {
                     tls.root_store
                         .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
+                    }
                     #[cfg(feature = "rustls-tls-native-roots")]
-                    {
+                    if config.tls_built_in_root_certs {
                         let roots_slice = NATIVE_ROOTS.as_ref().unwrap().roots.as_slice();
                         tls.root_store.roots.extend_from_slice(roots_slice);
                     }
@@ -719,6 +727,23 @@ impl ClientBuilder {
         self
     }
 
+    /// Controls the use of built-in/preloaded certificates during certificate validation.
+    ///
+    /// Defaults to `true` -- built-in system certs will be used.
+    ///
+    /// # Optional
+    ///
+    /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
+    /// feature to be enabled.
+    #[cfg(feature = "__tls")]
+    pub fn tls_built_in_root_certs(
+        mut self,
+        tls_built_in_root_certs: bool,
+    ) -> ClientBuilder {
+        self.config.tls_built_in_root_certs = tls_built_in_root_certs;
+        self
+    }
+
     /// Sets the identity to be used for client certificate authentication.
     ///
     /// # Optional
diff --git a/src/blocking/client.rs b/src/blocking/client.rs
index de4772d..ea0766e 100644
--- a/src/blocking/client.rs
+++ b/src/blocking/client.rs
@@ -433,6 +433,22 @@ impl ClientBuilder {
         self.with_inner(move |inner| inner.add_root_certificate(cert))
     }
 
+    /// Controls the use of built-in system certificates during certificate validation.
+    ///         
+    /// Defaults to `true` -- built-in system certs will be used.
+    ///
+    /// # Optional
+    ///
+    /// This requires the optional `default-tls`, `native-tls`, or `rustls-tls(-...)`
+    /// feature to be enabled.
+    #[cfg(feature = "__tls")]
+    pub fn tls_built_in_root_certs(
+        self,
+        tls_built_in_root_certs: bool,
+    ) -> ClientBuilder {
+        self.with_inner(move |inner| inner.tls_built_in_root_certs(tls_built_in_root_certs))
+    }
+
     /// Sets the identity to be used for client certificate authentication.
     #[cfg(feature = "__tls")]
     pub fn identity(self, identity: Identity) -> ClientBuilder {


Test patch:
diff --git a/tests/badssl.rs b/tests/badssl.rs
index 8eec030..9b001d0 100644
--- a/tests/badssl.rs
+++ b/tests/badssl.rs
@@ -59,6 +59,21 @@ async fn test_badssl_self_signed() {
     assert!(text.contains("<title>self-signed.badssl.com</title>"));
 }
 
+#[cfg(feature = "__tls")]
+#[tokio::test]
+async fn test_badssl_no_built_in_roots() {
+    let result = reqwest::Client::builder()
+        .tls_built_in_root_certs(false)
+        .no_proxy()
+        .build()
+        .unwrap()
+        .get("https://mozilla-modern.badssl.com/")
+        .send()
+        .await;
+
+    assert!(result.is_err());
+}
+
 #[cfg(feature = "native-tls")]
 #[tokio::test]
 async fn test_badssl_wrong_host() {