2021-04-27 02:02:02 -04:00
|
|
|
use crate::*;
|
2022-01-19 01:09:32 -05:00
|
|
|
use std::convert::TryFrom;
|
2021-04-27 02:02:02 -04:00
|
|
|
|
|
|
|
#[cfg(any(feature = "incoming", feature = "outgoing"))]
|
2022-01-19 01:09:32 -05:00
|
|
|
use tokio_rustls::{
|
|
|
|
rustls::{client::ClientConfig, ServerName},
|
|
|
|
TlsConnector,
|
|
|
|
};
|
2021-04-27 02:02:02 -04:00
|
|
|
|
|
|
|
#[cfg(feature = "outgoing")]
|
|
|
|
lazy_static::lazy_static! {
|
|
|
|
static ref CLIENT_TLS_CONFIG: TlsConnector = {
|
2022-01-19 01:09:32 -05:00
|
|
|
let mut config = ClientConfig::builder()
|
|
|
|
.with_safe_defaults()
|
|
|
|
.with_root_certificates(root_cert_store())
|
|
|
|
.with_no_client_auth();
|
|
|
|
config.alpn_protocols.push(ALPN_XMPP_CLIENT.to_vec());
|
2021-06-08 00:41:15 -04:00
|
|
|
TlsConnector::from(Arc::new(config))
|
2021-04-27 02:02:02 -04:00
|
|
|
};
|
|
|
|
static ref SERVER_TLS_CONFIG: TlsConnector = {
|
2022-01-19 01:09:32 -05:00
|
|
|
let mut config = ClientConfig::builder()
|
|
|
|
.with_safe_defaults()
|
|
|
|
.with_root_certificates(root_cert_store())
|
|
|
|
.with_no_client_auth(); // todo: do client auth...
|
|
|
|
config.alpn_protocols.push(ALPN_XMPP_SERVER.to_vec());
|
2021-06-08 00:41:15 -04:00
|
|
|
TlsConnector::from(Arc::new(config))
|
2021-04-27 02:02:02 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "outgoing")]
|
2022-02-10 01:18:35 -05:00
|
|
|
pub async fn tls_connect(target: SocketAddr, server_name: &str, is_c2s: bool) -> Result<(StanzaWrite, StanzaRead)> {
|
2022-01-19 01:09:32 -05:00
|
|
|
let dnsname = ServerName::try_from(server_name)?;
|
2021-04-27 02:02:02 -04:00
|
|
|
let stream = tokio::net::TcpStream::connect(target).await?;
|
|
|
|
let stream = if is_c2s {
|
|
|
|
CLIENT_TLS_CONFIG.connect(dnsname, stream).await?
|
|
|
|
} else {
|
|
|
|
SERVER_TLS_CONFIG.connect(dnsname, stream).await?
|
|
|
|
};
|
|
|
|
let (rd, wrt) = tokio::io::split(stream);
|
2022-02-10 01:18:35 -05:00
|
|
|
Ok((StanzaWrite::AsyncWrite(Box::new(wrt)), StanzaRead::new(Box::new(rd))))
|
2021-04-27 02:02:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "outgoing")]
|
2022-02-10 01:18:35 -05:00
|
|
|
pub async fn starttls_connect(target: SocketAddr, server_name: &str, is_c2s: bool, stream_open: &[u8], in_filter: &mut StanzaFilter) -> Result<(StanzaWrite, StanzaRead)> {
|
2022-01-19 01:09:32 -05:00
|
|
|
let dnsname = ServerName::try_from(server_name)?;
|
2021-04-27 02:02:02 -04:00
|
|
|
let mut stream = tokio::net::TcpStream::connect(target).await?;
|
|
|
|
let (in_rd, mut in_wr) = stream.split();
|
|
|
|
|
|
|
|
// send the stream_open
|
2022-01-19 01:14:39 -05:00
|
|
|
trace!("starttls sending: {} '{}'", server_name, to_str(stream_open));
|
|
|
|
in_wr.write_all(stream_open).await?;
|
2021-04-27 02:02:02 -04:00
|
|
|
in_wr.flush().await?;
|
|
|
|
|
|
|
|
// we naively read 1 byte at a time, which buffering significantly speeds up
|
|
|
|
let in_rd = tokio::io::BufReader::with_capacity(IN_BUFFER_SIZE, in_rd);
|
|
|
|
let mut in_rd = StanzaReader(in_rd);
|
|
|
|
let mut proceed_received = false;
|
|
|
|
|
2021-06-08 00:14:22 -04:00
|
|
|
trace!("starttls reading stream open {}", server_name);
|
2022-01-19 01:14:39 -05:00
|
|
|
while let Ok(Some(buf)) = in_rd.next(in_filter).await {
|
|
|
|
trace!("received pre-tls stanza: {} '{}'", server_name, to_str(buf));
|
2021-06-08 00:41:15 -04:00
|
|
|
if buf.starts_with(b"<?xml ") || buf.starts_with(b"<stream:stream ") {
|
2021-04-27 02:02:02 -04:00
|
|
|
// ignore this
|
|
|
|
} else if buf.starts_with(b"<stream:features") {
|
|
|
|
// we send starttls regardless, it could have been stripped out, we don't do plaintext
|
|
|
|
let buf = br###"<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"###;
|
2021-06-08 00:14:22 -04:00
|
|
|
trace!("> {} '{}'", server_name, to_str(buf));
|
2021-04-27 02:02:02 -04:00
|
|
|
in_wr.write_all(buf).await?;
|
|
|
|
in_wr.flush().await?;
|
|
|
|
} else if buf.starts_with(b"<proceed ") {
|
|
|
|
proceed_received = true;
|
|
|
|
break;
|
|
|
|
} else {
|
2022-01-19 01:14:39 -05:00
|
|
|
bail!("bad pre-tls stanza: {}", to_str(buf));
|
2021-04-27 02:02:02 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if !proceed_received {
|
|
|
|
bail!("stream ended before proceed");
|
|
|
|
}
|
|
|
|
|
|
|
|
debug!("starttls starting TLS {}", server_name);
|
|
|
|
let stream = if is_c2s {
|
|
|
|
CLIENT_TLS_CONFIG.connect(dnsname, stream).await?
|
|
|
|
} else {
|
|
|
|
SERVER_TLS_CONFIG.connect(dnsname, stream).await?
|
|
|
|
};
|
|
|
|
let (rd, wrt) = tokio::io::split(stream);
|
2022-02-10 01:18:35 -05:00
|
|
|
Ok((StanzaWrite::AsyncWrite(Box::new(wrt)), StanzaRead::new(Box::new(rd))))
|
2021-04-27 02:02:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "incoming")]
|
|
|
|
pub fn spawn_tls_listener(local_addr: SocketAddr, config: CloneableConfig, acceptor: TlsAcceptor) -> JoinHandle<Result<()>> {
|
|
|
|
tokio::spawn(async move {
|
|
|
|
let listener = TcpListener::bind(&local_addr).await.die("cannot listen on port/interface");
|
|
|
|
loop {
|
|
|
|
let (stream, client_addr) = listener.accept().await?;
|
|
|
|
let config = config.clone();
|
|
|
|
let acceptor = acceptor.clone();
|
|
|
|
tokio::spawn(async move {
|
2021-07-24 01:53:00 -04:00
|
|
|
let mut client_addr = Context::new("tcp-in", client_addr);
|
|
|
|
if let Err(e) = handle_tls_connection(stream, &mut client_addr, local_addr, config, acceptor).await {
|
|
|
|
error!("{} {}", client_addr.log_from(), e);
|
2021-04-27 02:02:02 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
#[allow(unreachable_code)]
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "incoming")]
|
2021-07-24 01:53:00 -04:00
|
|
|
async fn handle_tls_connection(mut stream: tokio::net::TcpStream, client_addr: &mut Context<'_>, local_addr: SocketAddr, config: CloneableConfig, acceptor: TlsAcceptor) -> Result<()> {
|
|
|
|
info!("{} connected", client_addr.log_from());
|
2021-04-27 02:02:02 -04:00
|
|
|
|
|
|
|
let mut in_filter = StanzaFilter::new(config.max_stanza_size_bytes);
|
|
|
|
|
|
|
|
let direct_tls = {
|
|
|
|
// sooo... I don't think peek here can be used for > 1 byte without this timer
|
|
|
|
// craziness... can it? this could be switched to only peek 1 byte and assume
|
|
|
|
// a leading 0x16 is TLS, it would *probably* be ok ?
|
|
|
|
//let mut p = [0u8; 3];
|
2022-01-19 01:14:39 -05:00
|
|
|
let p = &mut in_filter.buf[0..3];
|
2021-04-27 02:02:02 -04:00
|
|
|
// wait up to 10 seconds until 3 bytes have been read
|
|
|
|
use std::time::{Duration, Instant};
|
|
|
|
let duration = Duration::from_secs(10);
|
|
|
|
let now = Instant::now();
|
|
|
|
loop {
|
2022-01-19 01:14:39 -05:00
|
|
|
let n = stream.peek(p).await?;
|
2021-04-27 02:02:02 -04:00
|
|
|
if n == 3 {
|
|
|
|
break; // success
|
|
|
|
}
|
|
|
|
if n == 0 {
|
|
|
|
bail!("not enough bytes");
|
|
|
|
}
|
|
|
|
if Instant::now() - now > duration {
|
|
|
|
bail!("less than 3 bytes in 10 seconds, closed connection?");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TLS packet starts with a record "Hello" (0x16), followed by version
|
|
|
|
* (0x03 0x00-0x03) (RFC6101 A.1)
|
|
|
|
* This means we reject SSLv2 and lower, which is actually a good thing (RFC6176)
|
|
|
|
*/
|
|
|
|
p[0] == 0x16 && p[1] == 0x03 && p[2] <= 0x03
|
|
|
|
};
|
|
|
|
|
2021-07-24 01:53:00 -04:00
|
|
|
client_addr.set_proto(if direct_tls { "directtls-in" } else { "starttls-in" });
|
|
|
|
info!("{} direct_tls sniffed", client_addr.log_from());
|
2021-04-27 02:02:02 -04:00
|
|
|
|
|
|
|
// starttls
|
|
|
|
if !direct_tls {
|
|
|
|
let mut proceed_sent = false;
|
|
|
|
|
|
|
|
let (in_rd, mut in_wr) = stream.split();
|
|
|
|
// we naively read 1 byte at a time, which buffering significantly speeds up
|
|
|
|
let in_rd = tokio::io::BufReader::with_capacity(IN_BUFFER_SIZE, in_rd);
|
|
|
|
let mut in_rd = StanzaReader(in_rd);
|
|
|
|
|
|
|
|
while let Ok(Some(buf)) = in_rd.next(&mut in_filter).await {
|
2022-01-19 01:14:39 -05:00
|
|
|
trace!("{} received pre-tls stanza: '{}'", client_addr.log_from(), to_str(buf));
|
2021-04-27 02:02:02 -04:00
|
|
|
if buf.starts_with(b"<?xml ") {
|
2022-01-19 01:14:39 -05:00
|
|
|
trace!("{} '{}'", client_addr.log_to(), to_str(buf));
|
|
|
|
in_wr.write_all(buf).await?;
|
2021-04-27 02:02:02 -04:00
|
|
|
in_wr.flush().await?;
|
|
|
|
} else if buf.starts_with(b"<stream:stream ") {
|
|
|
|
// gajim seems to REQUIRE an id here...
|
|
|
|
let buf = if buf.contains_seq(b"id=") {
|
|
|
|
buf.replace_first(b" id='", b" id='xmpp-proxy")
|
|
|
|
.replace_first(br#" id=""#, br#" id="xmpp-proxy"#)
|
|
|
|
.replace_first(b" to=", br#" bla toblala="#)
|
|
|
|
.replace_first(b" from=", b" to=")
|
|
|
|
.replace_first(br#" bla toblala="#, br#" from="#)
|
|
|
|
} else {
|
|
|
|
buf.replace_first(b" to=", br#" bla toblala="#)
|
|
|
|
.replace_first(b" from=", b" to=")
|
|
|
|
.replace_first(br#" bla toblala="#, br#" id='xmpp-proxy' from="#)
|
|
|
|
};
|
|
|
|
|
2021-07-24 01:53:00 -04:00
|
|
|
trace!("{} '{}'", client_addr.log_to(), to_str(&buf));
|
2021-04-27 02:02:02 -04:00
|
|
|
in_wr.write_all(&buf).await?;
|
|
|
|
|
|
|
|
// ejabberd never sends <starttls/> with the first, only the second?
|
|
|
|
//let buf = br###"<features xmlns="http://etherx.jabber.org/streams"><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls></features>"###;
|
|
|
|
let buf = br###"<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls></stream:features>"###;
|
2021-07-24 01:53:00 -04:00
|
|
|
trace!("{} '{}'", client_addr.log_to(), to_str(buf));
|
2021-04-27 02:02:02 -04:00
|
|
|
in_wr.write_all(buf).await?;
|
|
|
|
in_wr.flush().await?;
|
|
|
|
} else if buf.starts_with(b"<starttls ") {
|
|
|
|
let buf = br###"<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls" />"###;
|
2021-07-24 01:53:00 -04:00
|
|
|
trace!("{} '{}'", client_addr.log_to(), to_str(buf));
|
2021-04-27 02:02:02 -04:00
|
|
|
in_wr.write_all(buf).await?;
|
|
|
|
in_wr.flush().await?;
|
|
|
|
proceed_sent = true;
|
|
|
|
break;
|
|
|
|
} else {
|
2022-01-19 01:14:39 -05:00
|
|
|
bail!("bad pre-tls stanza: {}", to_str(buf));
|
2021-04-27 02:02:02 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if !proceed_sent {
|
|
|
|
bail!("stream ended before open");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let stream = acceptor.accept(stream).await?;
|
|
|
|
|
2021-07-28 02:24:08 -04:00
|
|
|
// todo: try to peek stream here and handle websocket on these ports too?
|
|
|
|
|
2021-04-27 02:02:02 -04:00
|
|
|
let (in_rd, in_wr) = tokio::io::split(stream);
|
|
|
|
|
2022-02-10 01:18:35 -05:00
|
|
|
shuffle_rd_wr_filter(StanzaRead::new(Box::new(in_rd)), StanzaWrite::new(Box::new(in_wr)), config, local_addr, client_addr, in_filter).await
|
2021-04-27 02:02:02 -04:00
|
|
|
}
|