2023-12-30 22:08:37 -05:00
//! `starttls::ServerConfig` provides a `ServerConnector` for starttls connections
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 ::borrow ::Cow ;
2024-08-05 15:52:27 +02:00
use std ::error ::Error as StdError ;
use std ::fmt ;
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 {
std ::sync ::Arc ,
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-08-10 15:05:42 +02:00
) -> Result < PendingFeaturesRecv < Self ::Stream > , Error > {
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-08-10 15:05:42 +02:00
let tls_stream = starttls ( xmpp_stream , jid . domain ( ) . as_str ( ) ) . await ? ;
2024-08-06 17:00:53 +02:00
// Encrypted XmppStream
2024-08-10 15:05:42 +02:00
Ok ( initiate_stream (
tokio ::io ::BufStream ::new ( tls_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 ? )
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
}
}
fn channel_binding (
#[ allow(unused_variables) ] stream : & Self ::Stream ,
) -> Result < sasl ::common ::ChannelBinding , Error > {
#[ cfg(feature = " tls-native " ) ]
{
log ::warn! ( " tls-native doesn’ t support channel binding, please use tls-rust if you want this feature! " ) ;
Ok ( ChannelBinding ::None )
}
2024-09-11 20:34:38 +02:00
#[ cfg(all(feature = " tls-rust-ktls " , not(feature = " tls-native " ))) ]
{
log ::warn! ( " Kernel TLS doesn’ t support channel binding yet, we would have to extract the secrets in the rustls TlsStream before converting it into a KtlsStream. " ) ;
Ok ( ChannelBinding ::None )
}
#[ cfg(all(
feature = " tls-rust " ,
not ( feature = " tls-native " ) ,
not ( feature = " tls-rust-ktls " )
) ) ]
2023-12-30 22:08:37 -05:00
{
2024-08-10 15:05:42 +02:00
let ( _ , connection ) = stream . get_ref ( ) . get_ref ( ) ;
2023-12-30 22:08:37 -05:00
Ok ( match connection . protocol_version ( ) {
// TODO: Add support for TLS 1.2 and earlier.
Some ( tokio_rustls ::rustls ::ProtocolVersion ::TLSv1_3 ) = > {
let data = vec! [ 0 u8 ; 32 ] ;
2024-08-04 17:32:12 +02:00
let data = connection
. export_keying_material ( data , b " EXPORTER-Channel-Binding " , None )
. map_err ( | e | StartTlsError ::Tls ( e ) ) ? ;
2023-12-30 22:08:37 -05:00
ChannelBinding ::TlsExporter ( data )
}
_ = > ChannelBinding ::None ,
} )
}
}
}
#[ 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 ,
2023-12-30 22:08:37 -05:00
) -> Result < TlsStream < S > , 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 ) ) ? ;
2023-12-30 22:08:37 -05:00
Ok ( tls_stream )
}
#[ 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 ,
2023-12-30 22:08:37 -05:00
) -> Result < TlsStream < S > , 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-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 ) ? ;
2023-12-30 22:08:37 -05:00
Ok ( tls_stream )
}
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 ,
2023-12-30 22:08:37 -05:00
) -> Result < TlsStream < S > , 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 )
}
}