Re-factor ServerCert
moparisthebest/xmpp-proxy/pipeline/head This commit looks good Details

This commit is contained in:
Travis Burtrum 2024-01-05 01:59:26 -05:00
parent bf6500538e
commit 37df79a933
Signed by: moparisthebest
GPG Key ID: 88C93BFE27BC8229
5 changed files with 50 additions and 51 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@
fuzz/target/ fuzz/target/
*.txt *.txt
conflict/ conflict/
*.test.toml

View File

@ -44,55 +44,63 @@ pub type ServerCerts = ();
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))] #[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
#[derive(Clone)] #[derive(Clone)]
pub enum ServerCerts { pub struct ServerCerts {
Tls(&'static ServerConnection), inner: Arc<InnerServerCerts>,
#[cfg(feature = "quic")] is_tls: bool,
Quic(Option<Vec<Certificate>>, Option<String>, Option<Vec<u8>>), // todo: wrap this in arc or something now
} }
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))] #[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
impl ServerCerts { struct InnerServerCerts {
#[cfg(feature = "quic")] peer_certificates: Option<Vec<Certificate>>,
pub fn quic(conn: &quinn::Connection) -> ServerCerts { sni: Option<String>,
let certs = conn.peer_identity().and_then(|v| v.downcast::<Vec<Certificate>>().ok()).map(|v| v.to_vec()); alpn: Option<Vec<u8>>,
}
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
impl From<&ServerConnection> for ServerCerts {
fn from(conn: &ServerConnection) -> Self {
let peer_certificates = conn.peer_certificates().map(|c| c.to_vec());
let sni = conn.server_name().map(|s| s.to_string());
let alpn = conn.alpn_protocol().map(|s| s.to_vec());
Self {
inner: InnerServerCerts { peer_certificates, sni, alpn }.into(),
is_tls: true,
}
}
}
#[cfg(all(feature = "quic", any(feature = "s2s-incoming", feature = "webtransport")))]
impl From<&quinn::Connection> for ServerCerts {
fn from(conn: &quinn::Connection) -> Self {
let peer_certificates = conn.peer_identity().and_then(|v| v.downcast::<Vec<Certificate>>().ok()).map(|v| v.to_vec());
let (sni, alpn) = conn let (sni, alpn) = conn
.handshake_data() .handshake_data()
.and_then(|v| v.downcast::<quinn::crypto::rustls::HandshakeData>().ok()) .and_then(|v| v.downcast::<quinn::crypto::rustls::HandshakeData>().ok())
.map(|h| (h.server_name, h.protocol)) .map(|h| (h.server_name, h.protocol))
.unwrap_or_default(); .unwrap_or_default();
ServerCerts::Quic(certs, sni, alpn) Self {
} inner: InnerServerCerts { peer_certificates, sni, alpn }.into(),
is_tls: false,
pub fn peer_certificates(&self) -> Option<Vec<Certificate>> {
match self {
ServerCerts::Tls(c) => c.peer_certificates().map(|c| c.to_vec()),
#[cfg(feature = "quic")]
ServerCerts::Quic(certs, _, _) => certs.clone(),
} }
} }
}
pub fn sni(&self) -> Option<String> { #[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
match self { impl ServerCerts {
ServerCerts::Tls(c) => c.server_name().map(|s| s.to_string()), pub fn peer_certificates(&self) -> Option<&Vec<Certificate>> {
#[cfg(feature = "quic")] self.inner.peer_certificates.as_ref()
ServerCerts::Quic(_, sni, _) => sni.clone(),
}
} }
pub fn alpn(&self) -> Option<Vec<u8>> { pub fn sni(&self) -> Option<&str> {
match self { self.inner.sni.as_deref()
ServerCerts::Tls(c) => c.alpn_protocol().map(|s| s.to_vec()), }
#[cfg(feature = "quic")]
ServerCerts::Quic(_, _, alpn) => alpn.clone(), pub fn alpn(&self) -> Option<&Vec<u8>> {
} self.inner.alpn.as_ref()
} }
pub fn is_tls(&self) -> bool { pub fn is_tls(&self) -> bool {
match self { self.is_tls
ServerCerts::Tls(_) => true,
#[cfg(feature = "quic")]
ServerCerts::Quic(_, _, _) => false,
}
} }
} }
@ -120,7 +128,7 @@ pub async fn shuffle_rd_wr_filter(
"{} connected: sni: {:?}, alpn: {:?}, tls-not-quic: {}", "{} connected: sni: {:?}, alpn: {:?}, tls-not-quic: {}",
client_addr.log_from(), client_addr.log_from(),
server_certs.sni(), server_certs.sni(),
server_certs.alpn().map(|a| String::from_utf8_lossy(&a).to_string()), server_certs.alpn().map(|a| String::from_utf8_lossy(a).to_string()),
server_certs.is_tls(), server_certs.is_tls(),
); );

View File

@ -40,7 +40,7 @@ fn internal_spawn_quic_listener(incoming: Endpoint, local_addr: SocketAddr, conf
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))] #[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
let server_certs = { let server_certs = {
let server_certs = ServerCerts::quic(&new_conn); let server_certs = ServerCerts::from(&new_conn);
#[cfg(feature = "webtransport")] #[cfg(feature = "webtransport")]
if server_certs.alpn().map(|a| a == webtransport_quinn::ALPN).unwrap_or(false) { if server_certs.alpn().map(|a| a == webtransport_quinn::ALPN).unwrap_or(false) {
return crate::webtransport::incoming::handle_webtransport_session(new_conn, config, server_certs, local_addr, client_addr).await; return crate::webtransport::incoming::handle_webtransport_session(new_conn, config, server_certs, local_addr, client_addr).await;

View File

@ -12,7 +12,7 @@ use crate::{
}; };
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use log::{error, info, trace, warn}; use log::{error, info, trace, warn};
use rustls::{ServerConfig, ServerConnection}; use rustls::ServerConfig;
use std::{net::SocketAddr, sync::Arc}; use std::{net::SocketAddr, sync::Arc};
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt, BufStream}, io::{AsyncReadExt, AsyncWriteExt, BufStream},
@ -117,7 +117,7 @@ pub async fn handle_tls_connection<S: AsyncReadWritePeekSplit>(mut stream: S, cl
// until we get to the first byte of the TLS handshake... // until we get to the first byte of the TLS handshake...
while stream.first_bytes_match(&mut in_filter.buf[0..1], |p| p[0] != 0x16).await? { while stream.first_bytes_match(&mut in_filter.buf[0..1], |p| p[0] != 0x16).await? {
warn!("{} buggy software connecting, sent byte after <starttls: {}", client_addr.log_to(), &in_filter.buf[0]); warn!("{} buggy software connecting, sent byte after <starttls: {}", client_addr.log_to(), &in_filter.buf[0]);
stream.read(&mut in_filter.buf[0..1]).await?; stream.read_exact(&mut in_filter.buf[0..1]).await?;
} }
stream stream
} else { } else {
@ -127,19 +127,9 @@ pub async fn handle_tls_connection<S: AsyncReadWritePeekSplit>(mut stream: S, cl
let stream = acceptor.accept(stream).await?; let stream = acceptor.accept(stream).await?;
let (_, server_connection) = stream.get_ref(); let (_, server_connection) = stream.get_ref();
// todo: find better way to do this, might require different tokio_rustls API, the problem is I can't hold this
// past stream.into() below, and I can't get it back out after, now I *could* read sni+alpn+peer_certs
// *here* instead and pass them on, but since I haven't read anything from the stream yet, I'm
// not guaranteed that the handshake is complete and these are available, yes I can call is_handshaking()
// but there is no async API to complete the handshake, so I really need to pass it down to under
// where we read the first stanza, where we are guaranteed the handshake is complete, but I can't
// do that without ignoring the lifetime and just pulling a C programmer and pinky promising to be
// *very careful* that this reference doesn't outlive stream...
#[cfg(any(feature = "s2s-incoming", feature = "webtransport"))] #[cfg(any(feature = "s2s-incoming", feature = "webtransport"))]
let server_certs = { let server_certs = ServerCerts::from(server_connection);
let server_connection: &'static ServerConnection = unsafe { std::mem::transmute(server_connection) };
ServerCerts::Tls(server_connection)
};
#[cfg(not(any(feature = "s2s-incoming", feature = "webtransport")))] #[cfg(not(any(feature = "s2s-incoming", feature = "webtransport")))]
let server_certs = (); let server_certs = ();

View File

@ -1,8 +1,8 @@
# interfaces to listen for reverse proxy STARTTLS/Direct TLS/TLS WebSocket (wss) XMPP connections on, should be open to the internet # interfaces to listen for reverse proxy STARTTLS/Direct TLS/TLS WebSocket (wss) XMPP connections on, should be open to the internet
incoming_listen = [ "0.0.0.0:5222", "0.0.0.0:5269", "0.0.0.0:443" ] incoming_listen = [ "[::]:5222", "[::]:5269", "[::]:443" ]
# interfaces to listen for reverse proxy QUIC/WebTransport XMPP connections on, should be open to the internet # interfaces to listen for reverse proxy QUIC/WebTransport XMPP connections on, should be open to the internet
quic_listen = [ "0.0.0.0:443" ] quic_listen = [ "[::]:443" ]
# interfaces to listen for outgoing proxy TCP or WebSocket XMPP connections on, should be localhost or a path for a unix socket # interfaces to listen for outgoing proxy TCP or WebSocket XMPP connections on, should be localhost or a path for a unix socket
outgoing_listen = [ "127.0.0.1:15270" ] outgoing_listen = [ "127.0.0.1:15270" ]