Skip to main content

mssql_tls/
config.rs

1//! TLS configuration options.
2
3use std::sync::Arc;
4
5use rustls::pki_types::{CertificateDer, PrivateKeyDer};
6
7/// Client authentication credentials for mutual TLS.
8///
9/// This is wrapped in an Arc because `PrivateKeyDer` doesn't implement Clone.
10#[derive(Clone)]
11pub struct ClientAuth {
12    /// Client certificate chain.
13    pub certificates: Vec<CertificateDer<'static>>,
14    /// Client private key (wrapped in Arc as it doesn't implement Clone).
15    pub key: Arc<PrivateKeyDer<'static>>,
16}
17
18impl ClientAuth {
19    /// Create new client authentication credentials.
20    pub fn new(certificates: Vec<CertificateDer<'static>>, key: PrivateKeyDer<'static>) -> Self {
21        Self {
22            certificates,
23            key: Arc::new(key),
24        }
25    }
26}
27
28impl std::fmt::Debug for ClientAuth {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        f.debug_struct("ClientAuth")
31            .field("certificates_count", &self.certificates.len())
32            .field("has_key", &true)
33            .finish()
34    }
35}
36
37/// TLS configuration for SQL Server connections.
38#[derive(Clone, Debug)]
39pub struct TlsConfig {
40    /// Whether to trust the server certificate without validation.
41    ///
42    /// **Warning:** This is insecure and should only be used for testing.
43    pub trust_server_certificate: bool,
44
45    /// Custom root certificates to trust.
46    ///
47    /// If empty, the bundled webpki (Mozilla) root certificates are used —
48    /// unless the `native-certs` feature is enabled, in which case verification
49    /// is delegated to the OS/platform trust store (so enterprise internal CAs
50    /// installed in the OS store are honored). Setting explicit roots here
51    /// always takes precedence over the OS store.
52    pub root_certificates: Vec<CertificateDer<'static>>,
53
54    /// Client authentication credentials for mutual TLS (TDS 8.0 client cert auth).
55    pub client_auth: Option<ClientAuth>,
56
57    /// Server hostname for certificate validation.
58    ///
59    /// If not set, the connection hostname is used.
60    pub server_name: Option<String>,
61
62    /// Minimum TLS version to accept.
63    pub min_protocol_version: TlsVersion,
64
65    /// Maximum TLS version to accept.
66    pub max_protocol_version: TlsVersion,
67
68    /// Whether to use TDS 8.0 strict mode (TLS before any TDS traffic).
69    pub strict_mode: bool,
70
71    /// Application-layer protocol negotiation (ALPN) protocols.
72    pub alpn_protocols: Vec<Vec<u8>>,
73}
74
75impl Default for TlsConfig {
76    fn default() -> Self {
77        Self {
78            trust_server_certificate: false,
79            root_certificates: Vec::new(),
80            client_auth: None,
81            server_name: None,
82            min_protocol_version: TlsVersion::Tls12,
83            max_protocol_version: TlsVersion::Tls13,
84            strict_mode: false,
85            alpn_protocols: Vec::new(),
86        }
87    }
88}
89
90impl TlsConfig {
91    /// Create a new TLS configuration with default settings.
92    #[must_use]
93    pub fn new() -> Self {
94        Self::default()
95    }
96
97    /// Trust the server certificate without validation.
98    ///
99    /// **Warning:** This is insecure and should only be used for testing.
100    #[must_use]
101    pub fn trust_server_certificate(mut self, trust: bool) -> Self {
102        self.trust_server_certificate = trust;
103        self
104    }
105
106    /// Add a custom root certificate to trust.
107    #[must_use]
108    pub fn add_root_certificate(mut self, cert: CertificateDer<'static>) -> Self {
109        self.root_certificates.push(cert);
110        self
111    }
112
113    /// Set custom root certificates, replacing any existing ones.
114    #[must_use]
115    pub fn with_root_certificates(mut self, certs: Vec<CertificateDer<'static>>) -> Self {
116        self.root_certificates = certs;
117        self
118    }
119
120    /// Set client certificate and key for mutual TLS.
121    #[must_use]
122    pub fn with_client_auth(
123        mut self,
124        certs: Vec<CertificateDer<'static>>,
125        key: PrivateKeyDer<'static>,
126    ) -> Self {
127        self.client_auth = Some(ClientAuth::new(certs, key));
128        self
129    }
130
131    /// Set the server name for certificate validation.
132    #[must_use]
133    pub fn with_server_name(mut self, name: impl Into<String>) -> Self {
134        self.server_name = Some(name.into());
135        self
136    }
137
138    /// Set the minimum TLS version.
139    #[must_use]
140    pub fn min_protocol_version(mut self, version: TlsVersion) -> Self {
141        self.min_protocol_version = version;
142        self
143    }
144
145    /// Set the maximum TLS version.
146    #[must_use]
147    pub fn max_protocol_version(mut self, version: TlsVersion) -> Self {
148        self.max_protocol_version = version;
149        self
150    }
151
152    /// Enable TDS 8.0 strict mode.
153    #[must_use]
154    pub fn strict_mode(mut self, enabled: bool) -> Self {
155        self.strict_mode = enabled;
156        self
157    }
158
159    /// Set ALPN protocols.
160    #[must_use]
161    pub fn with_alpn_protocols(mut self, protocols: Vec<Vec<u8>>) -> Self {
162        self.alpn_protocols = protocols;
163        self
164    }
165
166    /// Check if client certificate authentication is configured.
167    #[must_use]
168    pub fn has_client_auth(&self) -> bool {
169        self.client_auth.is_some()
170    }
171
172    /// Add a root certificate from DER-encoded bytes.
173    ///
174    /// This is a convenience method that avoids requiring a direct
175    /// dependency on the `rustls` crate. For PEM-encoded certificates,
176    /// parse them first using the `rustls-pemfile` crate.
177    #[must_use]
178    pub fn add_root_certificate_der(self, der_bytes: Vec<u8>) -> Self {
179        self.add_root_certificate(CertificateDer::from(der_bytes))
180    }
181
182    /// Set client certificate and key from DER-encoded bytes.
183    ///
184    /// This is a convenience method that avoids requiring a direct
185    /// dependency on the `rustls` crate.
186    ///
187    /// * `cert_chain_der` - DER-encoded certificate chain
188    /// * `private_key_der` - DER-encoded private key (PKCS#8 format)
189    #[must_use]
190    pub fn with_client_auth_der(
191        self,
192        cert_chain_der: Vec<Vec<u8>>,
193        private_key_der: Vec<u8>,
194    ) -> Self {
195        let certs = cert_chain_der
196            .into_iter()
197            .map(CertificateDer::from)
198            .collect();
199        let key = PrivateKeyDer::Pkcs8(private_key_der.into());
200        self.with_client_auth(certs, key)
201    }
202}
203
204/// TLS protocol version.
205#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
206#[non_exhaustive]
207pub enum TlsVersion {
208    /// TLS 1.2
209    #[default]
210    Tls12,
211    /// TLS 1.3
212    Tls13,
213}
214
215impl TlsVersion {
216    /// Convert to rustls protocol version.
217    #[must_use]
218    pub fn to_rustls(&self) -> &'static rustls::SupportedProtocolVersion {
219        match self {
220            Self::Tls12 => &rustls::version::TLS12,
221            Self::Tls13 => &rustls::version::TLS13,
222        }
223    }
224}