Skip to main content

ldap_client/
tls_config.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Convenience types for building a [`rustls::ClientConfig`] without
4//! touching Rustls directly.
5
6use std::sync::Arc;
7
8use rustls::ClientConfig;
9use rustls_pki_types::{CertificateDer, PrivateKeyDer};
10
11/// Where to source trust anchors (root CA certificates).
12pub enum TrustAnchors {
13    /// Use the system/Mozilla roots **plus** any additional certs provided.
14    SystemPlusAdditional(Vec<CertificateDer<'static>>),
15    /// Trust **only** the supplied certificates (no system roots).
16    ExplicitOnly(Vec<CertificateDer<'static>>),
17}
18
19/// Minimum TLS version to accept.
20#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
21pub enum TlsVersion {
22    /// TLS 1.2
23    Tls12,
24    /// TLS 1.3
25    #[default]
26    Tls13,
27}
28
29/// High-level TLS configuration that can be converted into a
30/// [`rustls::ClientConfig`] via [`build`](Self::build).
31pub struct TlsConfig {
32    pub trust_anchors: TrustAnchors,
33    pub client_cert: Option<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)>,
34    pub min_tls_version: TlsVersion,
35    #[cfg(feature = "danger-disable-verify")]
36    pub danger_disable_verification: bool,
37}
38
39impl Default for TlsConfig {
40    fn default() -> Self {
41        Self {
42            trust_anchors: TrustAnchors::SystemPlusAdditional(Vec::new()),
43            client_cert: None,
44            min_tls_version: TlsVersion::default(),
45            #[cfg(feature = "danger-disable-verify")]
46            danger_disable_verification: false,
47        }
48    }
49}
50
51impl TlsConfig {
52    /// Build a [`rustls::ClientConfig`] from these settings.
53    pub fn build(self) -> Result<Arc<ClientConfig>, rustls::Error> {
54        #[cfg(feature = "danger-disable-verify")]
55        if self.danger_disable_verification {
56            let config = crate::conn::danger_no_verify_tls_config();
57            return Ok(Arc::new(config));
58        }
59
60        let root_store = match self.trust_anchors {
61            TrustAnchors::SystemPlusAdditional(extra) => {
62                let mut store = rustls::RootCertStore::from_iter(
63                    webpki_roots::TLS_SERVER_ROOTS.iter().cloned(),
64                );
65                for cert in extra {
66                    store.add(cert)?;
67                }
68                store
69            }
70            TrustAnchors::ExplicitOnly(certs) => {
71                let mut store = rustls::RootCertStore::empty();
72                for cert in certs {
73                    store.add(cert)?;
74                }
75                store
76            }
77        };
78
79        let versions: &[&rustls::SupportedProtocolVersion] = match self.min_tls_version {
80            TlsVersion::Tls13 => &[&rustls::version::TLS13],
81            TlsVersion::Tls12 => &[&rustls::version::TLS12, &rustls::version::TLS13],
82        };
83
84        let builder = ClientConfig::builder_with_protocol_versions(versions)
85            .with_root_certificates(root_store);
86
87        let config = match self.client_cert {
88            Some((certs, key)) => builder.with_client_auth_cert(certs, key)?,
89            None => builder.with_no_client_auth(),
90        };
91
92        Ok(Arc::new(config))
93    }
94}