Skip to main content

mail_send/smtp/
tls.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use super::AssertReply;
8use crate::{Error, SmtpClient};
9use rustls::{
10    ClientConfig, ClientConnection, SignatureScheme,
11    client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
12};
13use rustls_pki_types::ServerName;
14use rustls_platform_verifier::BuilderVerifierExt;
15use std::{convert::TryFrom, io, sync::Arc};
16use tokio::net::TcpStream;
17use tokio_rustls::{TlsConnector, client::TlsStream};
18
19impl SmtpClient<TcpStream> {
20    /// Upgrade the connection to TLS.
21    pub async fn start_tls(
22        mut self,
23        tls_connector: &TlsConnector,
24        hostname: &str,
25    ) -> crate::Result<SmtpClient<TlsStream<TcpStream>>> {
26        // Send STARTTLS command
27        self.cmd(b"STARTTLS\r\n")
28            .await?
29            .assert_positive_completion()?;
30
31        self.into_tls(tls_connector, hostname).await
32    }
33
34    pub async fn into_tls(
35        self,
36        tls_connector: &TlsConnector,
37        hostname: &str,
38    ) -> crate::Result<SmtpClient<TlsStream<TcpStream>>> {
39        tokio::time::timeout(self.timeout, async {
40            Ok(SmtpClient {
41                stream: tls_connector
42                    .connect(
43                        ServerName::try_from(hostname)
44                            .map_err(|_| crate::Error::InvalidTLSName)?
45                            .to_owned(),
46                        self.stream,
47                    )
48                    .await
49                    .map_err(|err| {
50                        let kind = err.kind();
51                        if let Some(inner) = err.into_inner() {
52                            match inner.downcast::<rustls::Error>() {
53                                Ok(error) => Error::Tls(error),
54                                Err(error) => Error::Io(io::Error::new(kind, error)),
55                            }
56                        } else {
57                            Error::Io(io::Error::new(kind, "Unspecified"))
58                        }
59                    })?,
60                timeout: self.timeout,
61            })
62        })
63        .await
64        .map_err(|_| crate::Error::Timeout)?
65    }
66}
67
68impl SmtpClient<TlsStream<TcpStream>> {
69    pub fn tls_connection(&self) -> &ClientConnection {
70        self.stream.get_ref().1
71    }
72}
73
74pub fn build_tls_connector(allow_invalid_certs: bool) -> Result<TlsConnector, String> {
75    let config = if !allow_invalid_certs {
76        ClientConfig::builder()
77            .with_platform_verifier()
78            .map(|config| config.with_no_client_auth())
79            .map_err(|err| format!("Failed to build platform verifier: {err}"))?
80    } else {
81        ClientConfig::builder()
82            .dangerous()
83            .with_custom_certificate_verifier(Arc::new(DummyVerifier {}))
84            .with_no_client_auth()
85    };
86
87    Ok(TlsConnector::from(Arc::new(config)))
88}
89
90#[doc(hidden)]
91#[derive(Debug)]
92struct DummyVerifier;
93
94impl ServerCertVerifier for DummyVerifier {
95    fn verify_server_cert(
96        &self,
97        _end_entity: &rustls_pki_types::CertificateDer<'_>,
98        _intermediates: &[rustls_pki_types::CertificateDer<'_>],
99        _server_name: &rustls_pki_types::ServerName<'_>,
100        _ocsp_response: &[u8],
101        _now: rustls_pki_types::UnixTime,
102    ) -> Result<ServerCertVerified, rustls::Error> {
103        Ok(ServerCertVerified::assertion())
104    }
105
106    fn verify_tls12_signature(
107        &self,
108        _message: &[u8],
109        _cert: &rustls_pki_types::CertificateDer<'_>,
110        _dss: &rustls::DigitallySignedStruct,
111    ) -> Result<HandshakeSignatureValid, rustls::Error> {
112        Ok(HandshakeSignatureValid::assertion())
113    }
114
115    fn verify_tls13_signature(
116        &self,
117        _message: &[u8],
118        _cert: &rustls_pki_types::CertificateDer<'_>,
119        _dss: &rustls::DigitallySignedStruct,
120    ) -> Result<HandshakeSignatureValid, rustls::Error> {
121        Ok(HandshakeSignatureValid::assertion())
122    }
123
124    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
125        vec![
126            SignatureScheme::RSA_PKCS1_SHA1,
127            SignatureScheme::ECDSA_SHA1_Legacy,
128            SignatureScheme::RSA_PKCS1_SHA256,
129            SignatureScheme::ECDSA_NISTP256_SHA256,
130            SignatureScheme::RSA_PKCS1_SHA384,
131            SignatureScheme::ECDSA_NISTP384_SHA384,
132            SignatureScheme::RSA_PKCS1_SHA512,
133            SignatureScheme::ECDSA_NISTP521_SHA512,
134            SignatureScheme::RSA_PSS_SHA256,
135            SignatureScheme::RSA_PSS_SHA384,
136            SignatureScheme::RSA_PSS_SHA512,
137            SignatureScheme::ED25519,
138            SignatureScheme::ED448,
139        ]
140    }
141}