2023-12-30 22:08:37 -05:00
|
|
|
|
//! `starttls::ServerConfig` provides a `ServerConnector` for starttls connections
|
|
|
|
|
|
|
2024-12-19 19:34:10 +01:00
|
|
|
|
use alloc::borrow::Cow;
|
|
|
|
|
|
use core::{error::Error as StdError, fmt};
|
2024-08-05 15:52:27 +02:00
|
|
|
|
#[cfg(feature = "tls-native")]
|
|
|
|
|
|
use native_tls::Error as TlsError;
|
2024-08-10 15:05:42 +02:00
|
|
|
|
use std::io;
|
2024-09-11 20:34:38 +02:00
|
|
|
|
use std::os::fd::AsRawFd;
|
2024-08-05 15:52:27 +02:00
|
|
|
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
|
|
|
|
|
use tokio_rustls::rustls::pki_types::InvalidDnsNameError;
|
|
|
|
|
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
|
|
|
|
|
use tokio_rustls::rustls::Error as TlsError;
|
|
|
|
|
|
|
2023-12-30 22:08:37 -05:00
|
|
|
|
use futures::{sink::SinkExt, stream::StreamExt};
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
|
|
|
|
|
use {
|
2024-12-19 19:34:10 +01:00
|
|
|
|
alloc::sync::Arc,
|
2023-12-30 22:08:37 -05:00
|
|
|
|
tokio_rustls::{
|
2024-07-25 20:51:20 +02:00
|
|
|
|
rustls::pki_types::ServerName,
|
|
|
|
|
|
rustls::{ClientConfig, RootCertStore},
|
2023-12-30 22:08:37 -05:00
|
|
|
|
TlsConnector,
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-09-11 20:34:38 +02:00
|
|
|
|
#[cfg(all(
|
|
|
|
|
|
feature = "tls-rust",
|
|
|
|
|
|
not(feature = "tls-native"),
|
|
|
|
|
|
not(feature = "tls-rust-ktls")
|
|
|
|
|
|
))]
|
|
|
|
|
|
use tokio_rustls::client::TlsStream;
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(all(feature = "tls-rust-ktls", not(feature = "tls-native")))]
|
|
|
|
|
|
type TlsStream<S> = ktls::KtlsStream<S>;
|
|
|
|
|
|
|
2023-12-30 22:08:37 -05:00
|
|
|
|
#[cfg(feature = "tls-native")]
|
|
|
|
|
|
use {
|
|
|
|
|
|
native_tls::TlsConnector as NativeTlsConnector,
|
|
|
|
|
|
tokio_native_tls::{TlsConnector, TlsStream},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
use sasl::common::ChannelBinding;
|
|
|
|
|
|
use tokio::{
|
2024-08-10 15:05:42 +02:00
|
|
|
|
io::{AsyncRead, AsyncWrite, BufStream},
|
2023-12-30 22:08:37 -05:00
|
|
|
|
net::TcpStream,
|
|
|
|
|
|
};
|
2024-08-10 15:05:42 +02:00
|
|
|
|
use xmpp_parsers::{
|
|
|
|
|
|
jid::Jid,
|
|
|
|
|
|
starttls::{self, Request},
|
|
|
|
|
|
};
|
2023-12-30 22:08:37 -05:00
|
|
|
|
|
2024-08-05 15:09:59 +02:00
|
|
|
|
use crate::{
|
2024-08-06 20:40:24 +02:00
|
|
|
|
connect::{DnsConfig, ServerConnector, ServerConnectorError},
|
2024-08-05 15:09:59 +02:00
|
|
|
|
error::{Error, ProtocolError},
|
2024-08-10 15:05:42 +02:00
|
|
|
|
xmlstream::{
|
2024-08-18 17:40:39 +02:00
|
|
|
|
initiate_stream, PendingFeaturesRecv, ReadError, StreamHeader, Timeouts, XmppStream,
|
2024-08-10 15:05:42 +02:00
|
|
|
|
XmppStreamElement,
|
|
|
|
|
|
},
|
2024-08-10 17:39:55 +02:00
|
|
|
|
Client,
|
2024-08-05 15:09:59 +02:00
|
|
|
|
};
|
2023-12-30 22:08:37 -05:00
|
|
|
|
|
2024-08-06 20:40:24 +02:00
|
|
|
|
/// Client that connects over StartTls
|
2024-08-20 16:54:48 +02:00
|
|
|
|
#[deprecated(since = "5.0.0", note = "use tokio_xmpp::Client instead")]
|
|
|
|
|
|
pub type StartTlsClient = Client;
|
2024-01-01 01:13:51 -05:00
|
|
|
|
|
2024-08-06 20:40:24 +02:00
|
|
|
|
/// Connect via TCP+StartTLS to an XMPP server
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
|
pub struct StartTlsServerConnector(pub DnsConfig);
|
|
|
|
|
|
|
|
|
|
|
|
impl From<DnsConfig> for StartTlsServerConnector {
|
|
|
|
|
|
fn from(dns_config: DnsConfig) -> StartTlsServerConnector {
|
|
|
|
|
|
Self(dns_config)
|
|
|
|
|
|
}
|
2023-12-30 22:08:37 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-06 20:40:24 +02:00
|
|
|
|
impl ServerConnector for StartTlsServerConnector {
|
2024-08-10 15:05:42 +02:00
|
|
|
|
type Stream = BufStream<TlsStream<TcpStream>>;
|
2023-12-30 22:08:37 -05:00
|
|
|
|
|
2024-08-10 15:05:42 +02:00
|
|
|
|
async fn connect(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
jid: &Jid,
|
|
|
|
|
|
ns: &'static str,
|
2024-08-18 17:40:39 +02:00
|
|
|
|
timeouts: Timeouts,
|
2024-12-18 18:17:39 +01:00
|
|
|
|
) -> Result<(PendingFeaturesRecv<Self::Stream>, ChannelBinding), Error> {
|
2024-08-10 15:05:42 +02:00
|
|
|
|
let tcp_stream = tokio::io::BufStream::new(self.0.resolve().await?);
|
2023-12-30 22:08:37 -05:00
|
|
|
|
|
2024-08-10 15:05:42 +02:00
|
|
|
|
// Unencryped XmppStream
|
|
|
|
|
|
let xmpp_stream = initiate_stream(
|
|
|
|
|
|
tcp_stream,
|
|
|
|
|
|
ns,
|
|
|
|
|
|
StreamHeader {
|
|
|
|
|
|
to: Some(Cow::Borrowed(jid.domain().as_str())),
|
|
|
|
|
|
from: None,
|
|
|
|
|
|
id: None,
|
|
|
|
|
|
},
|
2024-08-18 17:40:39 +02:00
|
|
|
|
timeouts,
|
2024-08-10 15:05:42 +02:00
|
|
|
|
)
|
|
|
|
|
|
.await?;
|
|
|
|
|
|
let (features, xmpp_stream) = xmpp_stream.recv_features().await?;
|
|
|
|
|
|
|
|
|
|
|
|
if features.can_starttls() {
|
2023-12-30 22:08:37 -05:00
|
|
|
|
// TlsStream
|
2024-12-18 18:17:39 +01:00
|
|
|
|
let (tls_stream, channel_binding) =
|
|
|
|
|
|
starttls(xmpp_stream, jid.domain().as_str()).await?;
|
2024-08-06 17:00:53 +02:00
|
|
|
|
// Encrypted XmppStream
|
2024-12-18 18:17:39 +01:00
|
|
|
|
Ok((
|
|
|
|
|
|
initiate_stream(
|
|
|
|
|
|
tokio::io::BufStream::new(tls_stream),
|
|
|
|
|
|
ns,
|
|
|
|
|
|
StreamHeader {
|
|
|
|
|
|
to: Some(Cow::Borrowed(jid.domain().as_str())),
|
|
|
|
|
|
from: None,
|
|
|
|
|
|
id: None,
|
|
|
|
|
|
},
|
|
|
|
|
|
timeouts,
|
|
|
|
|
|
)
|
|
|
|
|
|
.await?,
|
|
|
|
|
|
channel_binding,
|
|
|
|
|
|
))
|
2023-12-30 22:08:37 -05:00
|
|
|
|
} else {
|
2024-08-10 15:05:42 +02:00
|
|
|
|
Err(crate::Error::Protocol(ProtocolError::NoTls).into())
|
2023-12-30 22:08:37 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "tls-native")]
|
|
|
|
|
|
async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin>(
|
2024-08-10 15:05:42 +02:00
|
|
|
|
xmpp_stream: XmppStream<BufStream<S>>,
|
|
|
|
|
|
domain: &str,
|
2024-12-18 18:17:39 +01:00
|
|
|
|
) -> Result<(TlsStream<S>, ChannelBinding), Error> {
|
2024-08-10 15:05:42 +02:00
|
|
|
|
let domain = domain.to_owned();
|
|
|
|
|
|
let stream = xmpp_stream.into_inner().into_inner();
|
2023-12-30 22:08:37 -05:00
|
|
|
|
let tls_stream = TlsConnector::from(NativeTlsConnector::builder().build().unwrap())
|
|
|
|
|
|
.connect(&domain, stream)
|
2024-08-04 17:32:12 +02:00
|
|
|
|
.await
|
|
|
|
|
|
.map_err(|e| StartTlsError::Tls(e))?;
|
2024-12-18 18:17:39 +01:00
|
|
|
|
log::warn!(
|
|
|
|
|
|
"tls-native doesn’t support channel binding, please use tls-rust if you want this feature!"
|
|
|
|
|
|
);
|
|
|
|
|
|
Ok((tls_stream, ChannelBinding::None))
|
2023-12-30 22:08:37 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
2024-09-11 20:34:38 +02:00
|
|
|
|
async fn get_tls_stream<S: AsyncRead + AsyncWrite + Unpin + AsRawFd>(
|
2024-08-10 15:05:42 +02:00
|
|
|
|
xmpp_stream: XmppStream<BufStream<S>>,
|
|
|
|
|
|
domain: &str,
|
2024-12-18 18:17:39 +01:00
|
|
|
|
) -> Result<(TlsStream<S>, ChannelBinding), Error> {
|
2024-08-10 15:05:42 +02:00
|
|
|
|
let domain = ServerName::try_from(domain.to_owned()).map_err(StartTlsError::DnsNameError)?;
|
|
|
|
|
|
let stream = xmpp_stream.into_inner().into_inner();
|
2024-08-22 12:24:58 +02:00
|
|
|
|
let mut root_store = RootCertStore::empty();
|
|
|
|
|
|
#[cfg(feature = "webpki-roots")]
|
|
|
|
|
|
{
|
|
|
|
|
|
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
|
|
|
|
|
}
|
|
|
|
|
|
#[cfg(feature = "rustls-native-certs")]
|
|
|
|
|
|
{
|
|
|
|
|
|
root_store.add_parsable_certificates(rustls_native_certs::load_native_certs()?);
|
|
|
|
|
|
}
|
2024-09-11 20:34:38 +02:00
|
|
|
|
#[allow(unused_mut, reason = "This config is mutable when using ktls")]
|
|
|
|
|
|
let mut config = ClientConfig::builder()
|
2023-12-30 22:08:37 -05:00
|
|
|
|
.with_root_certificates(root_store)
|
|
|
|
|
|
.with_no_client_auth();
|
2024-09-11 20:34:38 +02:00
|
|
|
|
#[cfg(feature = "tls-rust-ktls")]
|
|
|
|
|
|
let stream = {
|
|
|
|
|
|
config.enable_secret_extraction = true;
|
|
|
|
|
|
ktls::CorkStream::new(stream)
|
|
|
|
|
|
};
|
2023-12-30 22:08:37 -05:00
|
|
|
|
let tls_stream = TlsConnector::from(Arc::new(config))
|
|
|
|
|
|
.connect(domain, stream)
|
|
|
|
|
|
.await
|
|
|
|
|
|
.map_err(|e| Error::from(crate::Error::Io(e)))?;
|
2024-12-18 18:17:39 +01:00
|
|
|
|
|
|
|
|
|
|
// Extract the channel-binding information before we hand the stream over to ktls.
|
|
|
|
|
|
let (_, connection) = tls_stream.get_ref();
|
|
|
|
|
|
let channel_binding = match connection.protocol_version() {
|
|
|
|
|
|
// TODO: Add support for TLS 1.2 and earlier.
|
|
|
|
|
|
Some(tokio_rustls::rustls::ProtocolVersion::TLSv1_3) => {
|
|
|
|
|
|
let data = vec![0u8; 32];
|
|
|
|
|
|
let data = connection
|
|
|
|
|
|
.export_keying_material(data, b"EXPORTER-Channel-Binding", None)
|
|
|
|
|
|
.map_err(|e| StartTlsError::Tls(e))?;
|
|
|
|
|
|
ChannelBinding::TlsExporter(data)
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => ChannelBinding::None,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-09-11 20:34:38 +02:00
|
|
|
|
#[cfg(feature = "tls-rust-ktls")]
|
|
|
|
|
|
let tls_stream = ktls::config_ktls_client(tls_stream)
|
|
|
|
|
|
.await
|
|
|
|
|
|
.map_err(StartTlsError::KtlsError)?;
|
2024-12-18 18:17:39 +01:00
|
|
|
|
Ok((tls_stream, channel_binding))
|
2023-12-30 22:08:37 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-06 17:00:53 +02:00
|
|
|
|
/// Performs `<starttls/>` on an XmppStream and returns a binary
|
2023-12-30 22:08:37 -05:00
|
|
|
|
/// TlsStream.
|
2024-09-11 20:34:38 +02:00
|
|
|
|
pub async fn starttls<S: AsyncRead + AsyncWrite + Unpin + AsRawFd>(
|
2024-08-10 15:05:42 +02:00
|
|
|
|
mut stream: XmppStream<BufStream<S>>,
|
|
|
|
|
|
domain: &str,
|
2024-12-18 18:17:39 +01:00
|
|
|
|
) -> Result<(TlsStream<S>, ChannelBinding), Error> {
|
2024-08-10 15:05:42 +02:00
|
|
|
|
stream
|
|
|
|
|
|
.send(&XmppStreamElement::Starttls(starttls::Nonza::Request(
|
|
|
|
|
|
Request,
|
|
|
|
|
|
)))
|
|
|
|
|
|
.await?;
|
2023-12-30 22:08:37 -05:00
|
|
|
|
|
|
|
|
|
|
loop {
|
2024-08-10 15:05:42 +02:00
|
|
|
|
match stream.next().await {
|
|
|
|
|
|
Some(Ok(XmppStreamElement::Starttls(starttls::Nonza::Proceed(_)))) => {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
Some(Ok(_)) => (),
|
|
|
|
|
|
Some(Err(ReadError::SoftTimeout)) => (),
|
|
|
|
|
|
Some(Err(ReadError::HardError(e))) => return Err(e.into()),
|
|
|
|
|
|
Some(Err(ReadError::ParseError(e))) => {
|
|
|
|
|
|
return Err(io::Error::new(io::ErrorKind::InvalidData, e).into())
|
|
|
|
|
|
}
|
|
|
|
|
|
None | Some(Err(ReadError::StreamFooterReceived)) => {
|
|
|
|
|
|
return Err(crate::Error::Disconnected)
|
2023-12-30 22:08:37 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-10 15:05:42 +02:00
|
|
|
|
get_tls_stream(stream, domain).await
|
2023-12-30 22:08:37 -05:00
|
|
|
|
}
|
2024-08-05 15:52:27 +02:00
|
|
|
|
|
|
|
|
|
|
/// StartTLS ServerConnector Error
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
|
pub enum StartTlsError {
|
|
|
|
|
|
/// TLS error
|
|
|
|
|
|
Tls(TlsError),
|
|
|
|
|
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
|
|
|
|
|
/// DNS name parsing error
|
|
|
|
|
|
DnsNameError(InvalidDnsNameError),
|
2024-09-11 20:34:38 +02:00
|
|
|
|
#[cfg(feature = "tls-rust-ktls")]
|
|
|
|
|
|
/// Error while setting up kernel TLS
|
|
|
|
|
|
KtlsError(ktls::Error),
|
2024-08-05 15:52:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl ServerConnectorError for StartTlsError {}
|
|
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for StartTlsError {
|
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
|
match self {
|
|
|
|
|
|
Self::Tls(e) => write!(fmt, "TLS error: {}", e),
|
|
|
|
|
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
|
|
|
|
|
Self::DnsNameError(e) => write!(fmt, "DNS name error: {}", e),
|
2024-09-11 20:34:38 +02:00
|
|
|
|
#[cfg(feature = "tls-rust-ktls")]
|
|
|
|
|
|
Self::KtlsError(e) => write!(fmt, "Kernel TLS error: {}", e),
|
2024-08-05 15:52:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl StdError for StartTlsError {}
|
|
|
|
|
|
|
|
|
|
|
|
impl From<TlsError> for StartTlsError {
|
|
|
|
|
|
fn from(e: TlsError) -> Self {
|
|
|
|
|
|
Self::Tls(e)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(all(feature = "tls-rust", not(feature = "tls-native")))]
|
|
|
|
|
|
impl From<InvalidDnsNameError> for StartTlsError {
|
|
|
|
|
|
fn from(e: InvalidDnsNameError) -> Self {
|
|
|
|
|
|
Self::DnsNameError(e)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|