1#![allow(clippy::missing_errors_doc)]
2
3use std::{fmt::Debug, net::SocketAddr, num::TryFromIntError, path::PathBuf, time::Duration};
4
5use openssl::{
6 error::ErrorStack,
7 ssl::{ConnectConfiguration, SslConnector, SslConnectorBuilder, SslMethod},
8};
9use snafu::{ResultExt, Snafu};
10use tokio::net::TcpStream;
11use tokio_openssl::SslStream;
12
13use crate::tcp::{self, TcpKeepaliveConfig};
14
15mod incoming;
16mod maybe_tls;
17mod outgoing;
18mod settings;
19
20pub use incoming::{CertificateMetadata, MaybeTlsIncomingStream, MaybeTlsListener};
21pub use maybe_tls::MaybeTls;
22pub use settings::{
23 MaybeTlsSettings, PEM_START_MARKER, TEST_PEM_CA_PATH, TEST_PEM_CLIENT_CRT_PATH,
24 TEST_PEM_CLIENT_KEY_PATH, TEST_PEM_CRT_PATH, TEST_PEM_INTERMEDIATE_CA_PATH, TEST_PEM_KEY_PATH,
25 TlsConfig, TlsEnableableConfig, TlsSettings, TlsSourceConfig,
26};
27
28pub type Result<T> = std::result::Result<T, TlsError>;
29
30pub type MaybeTlsStream<S> = MaybeTls<S, SslStream<S>>;
31
32#[derive(Debug, Snafu)]
33pub enum TlsError {
34 #[snafu(display("Could not open {} file {:?}: {}", note, filename, source))]
35 FileOpenFailed {
36 note: &'static str,
37 filename: PathBuf,
38 source: std::io::Error,
39 },
40 #[snafu(display("Could not read {} file {:?}: {}", note, filename, source))]
41 FileReadFailed {
42 note: &'static str,
43 filename: PathBuf,
44 source: std::io::Error,
45 },
46 #[snafu(display("Could not build TLS connector: {}", source))]
47 TlsBuildConnector { source: ErrorStack },
48 #[snafu(display("Could not set TCP TLS identity: {}", source))]
49 TlsIdentityError { source: ErrorStack },
50 #[snafu(display("Could not export identity to DER: {}", source))]
51 DerExportError { source: ErrorStack },
52 #[snafu(display("Identity certificate is missing a key"))]
53 MissingKey,
54 #[snafu(display("Certificate file contains no certificates"))]
55 MissingCertificate,
56 #[snafu(display("Could not parse certificate in {:?}: {}", filename, source))]
57 CertificateParseError {
58 filename: PathBuf,
59 source: ErrorStack,
60 },
61 #[snafu(display("Must specify both TLS key_file and crt_file"))]
62 MissingCrtKeyFile,
63 #[snafu(display("Could not parse X509 certificate in {:?}: {}", filename, source))]
64 X509ParseError {
65 filename: PathBuf,
66 source: ErrorStack,
67 },
68 #[snafu(display("Could not parse private key in {:?}: {}", filename, source))]
69 PrivateKeyParseError {
70 filename: PathBuf,
71 source: ErrorStack,
72 },
73 #[snafu(display("Could not build PKCS#12 archive for identity: {}", source))]
74 Pkcs12Error { source: ErrorStack },
75 #[snafu(display("Could not parse identity in {:?}: {}", filename, source))]
76 IdentityParseError {
77 filename: PathBuf,
78 source: ErrorStack,
79 },
80 #[snafu(display("TLS configuration requires a certificate when enabled"))]
81 MissingRequiredIdentity,
82 #[snafu(display("TLS handshake failed: {}", source))]
83 Handshake { source: openssl::ssl::Error },
84 #[snafu(display("Incoming listener failed: {}", source))]
85 IncomingListener { source: tokio::io::Error },
86 #[snafu(display("Creating the TLS acceptor failed: {}", source))]
87 CreateAcceptor { source: ErrorStack },
88 #[snafu(display("Error building SSL context: {}", source))]
89 SslBuildError { source: openssl::error::ErrorStack },
90 #[snafu(display("Error setting up the TLS certificate: {}", source))]
91 SetCertificate { source: ErrorStack },
92 #[snafu(display("Error setting up the TLS private key: {}", source))]
93 SetPrivateKey { source: ErrorStack },
94 #[snafu(display("Error setting up the TLS chain certificates: {}", source))]
95 AddExtraChainCert { source: ErrorStack },
96 #[snafu(display("Error creating a certificate store: {}", source))]
97 NewStoreBuilder { source: ErrorStack },
98 #[snafu(display("Error adding a certificate to a store: {}", source))]
99 AddCertToStore { source: ErrorStack },
100 #[snafu(display("Error setting up the verification certificate: {}", source))]
101 SetVerifyCert { source: ErrorStack },
102 #[snafu(display("Error setting SNI: {}", source))]
103 SetSni { source: ErrorStack },
104 #[snafu(display("Error setting ALPN protocols: {}", source))]
105 SetAlpnProtocols { source: ErrorStack },
106 #[snafu(display(
107 "Error encoding ALPN protocols, could not encode length as u8: {}",
108 source
109 ))]
110 EncodeAlpnProtocols { source: TryFromIntError },
111 #[snafu(display("PKCS#12 parse failed: {}", source))]
112 ParsePkcs12 { source: ErrorStack },
113 #[snafu(display("TCP bind failed: {}", source))]
114 TcpBind { source: tokio::io::Error },
115 #[snafu(display("{}", source))]
116 Connect { source: tokio::io::Error },
117 #[snafu(display("Could not get peer address: {}", source))]
118 PeerAddress { source: std::io::Error },
119 #[snafu(display("Security Framework Error: {}", source))]
120 #[cfg(target_os = "macos")]
121 SecurityFramework {
122 source: security_framework::base::Error,
123 },
124 #[snafu(display("Schannel Error: {}", source))]
125 #[cfg(windows)]
126 Schannel { source: std::io::Error },
127 #[cfg(any(windows, target_os = "macos"))]
128 #[snafu(display("Unable to parse X509 from system cert: {}", source))]
129 X509SystemParseError { source: ErrorStack },
130 #[snafu(display("Creating an empty CA stack failed"))]
131 NewCaStack { source: ErrorStack },
132 #[snafu(display("Could not push intermediate certificate onto stack"))]
133 CaStackPush { source: ErrorStack },
134}
135
136impl MaybeTlsStream<TcpStream> {
137 pub fn peer_addr(&self) -> std::result::Result<SocketAddr, std::io::Error> {
138 match self {
139 Self::Raw(raw) => raw.peer_addr(),
140 Self::Tls(tls) => tls.get_ref().peer_addr(),
141 }
142 }
143
144 pub fn set_keepalive(&mut self, keepalive: TcpKeepaliveConfig) -> std::io::Result<()> {
145 let stream = match self {
146 Self::Raw(raw) => raw,
147 Self::Tls(tls) => tls.get_ref(),
148 };
149
150 if let Some(time_secs) = keepalive.time_secs {
151 let config = socket2::TcpKeepalive::new().with_time(Duration::from_secs(time_secs));
152
153 tcp::set_keepalive(stream, &config)?;
154 }
155
156 Ok(())
157 }
158
159 pub fn set_send_buffer_bytes(&mut self, bytes: usize) -> std::io::Result<()> {
160 let stream = match self {
161 Self::Raw(raw) => raw,
162 Self::Tls(tls) => tls.get_ref(),
163 };
164
165 tcp::set_send_buffer_size(stream, bytes)
166 }
167
168 pub fn set_receive_buffer_bytes(&mut self, bytes: usize) -> std::io::Result<()> {
169 let stream = match self {
170 Self::Raw(raw) => raw,
171 Self::Tls(tls) => tls.get_ref(),
172 };
173
174 tcp::set_receive_buffer_size(stream, bytes)
175 }
176}
177
178pub fn tls_connector_builder(settings: &MaybeTlsSettings) -> Result<SslConnectorBuilder> {
179 let mut builder = SslConnector::builder(SslMethod::tls()).context(TlsBuildConnectorSnafu)?;
180 if let Some(settings) = settings.tls() {
181 settings.apply_context(&mut builder)?;
182 }
183 Ok(builder)
184}
185
186fn tls_connector(settings: &MaybeTlsSettings) -> Result<ConnectConfiguration> {
187 let mut configure = tls_connector_builder(settings)?
188 .build()
189 .configure()
190 .context(TlsBuildConnectorSnafu)?;
191 let tls_setting = settings.tls().cloned();
192 if let Some(tls_setting) = &tls_setting {
193 tls_setting
194 .apply_connect_configuration(&mut configure)
195 .context(SetSniSnafu)?;
196 }
197 Ok(configure)
198}