Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
911 changes: 526 additions & 385 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion fuzz/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ fn main() {
}
Err(e) => {
// more data is required, stanzafilter should never let this happen, let's panic
panic!("more data required? e: {:?}\nstanza: {}", e, String::from_utf8_lossy(stanza));
panic!(
"more data required? e: {:?}\nstanza: {}",
e,
String::from_utf8_lossy(stanza)
);
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/common/ca_roots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ static ROOT_STORE: LazyLock<Arc<RootCertStore>> = LazyLock::new(|| {
roots.into()
});

pub static SERVER_VERIFIER: LazyLock<Arc<WebPkiServerVerifier>> = LazyLock::new(|| WebPkiServerVerifier::builder(ROOT_STORE.clone()).build().expect("couldn't create webpki verifier"));
pub static SERVER_VERIFIER: LazyLock<Arc<WebPkiServerVerifier>> = LazyLock::new(|| {
WebPkiServerVerifier::builder(ROOT_STORE.clone())
.build()
.expect("couldn't create webpki verifier")
});

pub static TLS_SERVER_ROOTS: LazyLock<&[TrustAnchor<'static>]> = LazyLock::new(|| &ROOT_STORE.roots);
pub static TLS_SERVER_ROOTS: LazyLock<&[TrustAnchor<'static>]> =
LazyLock::new(|| &ROOT_STORE.roots);
10 changes: 8 additions & 2 deletions src/common/certs_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,20 @@ impl CertsKey {
#[cfg(feature = "rustls-pemfile")]
impl rustls::server::ResolvesServerCert for CertsKey {
fn resolve(&self, _: rustls::server::ClientHello) -> Option<Arc<rustls::sign::CertifiedKey>> {
self.inner.as_ref().map(|rwl| rwl.read().expect("CertKey poisoned?").clone()).ok()
self.inner
.as_ref()
.map(|rwl| rwl.read().expect("CertKey poisoned?").clone())
.ok()
}
}

#[cfg(feature = "rustls-pemfile")]
impl rustls::client::ResolvesClientCert for CertsKey {
fn resolve(&self, _: &[&[u8]], _: &[SignatureScheme]) -> Option<Arc<CertifiedKey>> {
self.inner.as_ref().map(|rwl| rwl.read().expect("CertKey poisoned?").clone()).ok()
self.inner
.as_ref()
.map(|rwl| rwl.read().expect("CertKey poisoned?").clone())
.ok()
}

fn has_certs(&self) -> bool {
Expand Down
106 changes: 91 additions & 15 deletions src/common/incoming.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::{
common::{c2s, certs_key::CertsKey, shuffle_rd_wr_filter_only, stream_preamble, to_str, SocketAddrPath, ALPN_XMPP_CLIENT, ALPN_XMPP_SERVER},
common::{
c2s, certs_key::CertsKey, shuffle_rd_wr_filter_only, stream_preamble, to_str,
SocketAddrPath, ALPN_XMPP_CLIENT, ALPN_XMPP_SERVER,
},
context::Context,
in_out::{StanzaRead, StanzaWrite},
slicesubsequence::SliceSubsequence,
Expand Down Expand Up @@ -64,23 +67,39 @@ impl From<&ServerConnection> for ServerCerts {
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(),
inner: InnerServerCerts {
peer_certificates,
sni,
alpn,
}
.into(),
is_tls: true,
}
}
}

#[cfg(all(feature = "quic", any(feature = "s2s-incoming", feature = "webtransport")))]
#[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<CertificateDer<'static>>>().ok()).map(|v| v.to_vec());
let peer_certificates = conn
.peer_identity()
.and_then(|v| v.downcast::<Vec<CertificateDer<'static>>>().ok())
.map(|v| v.to_vec());
let (sni, alpn) = conn
.handshake_data()
.and_then(|v| v.downcast::<quinn::crypto::rustls::HandshakeData>().ok())
.map(|h| (h.server_name, h.protocol))
.unwrap_or_default();
Self {
inner: InnerServerCerts { peer_certificates, sni, alpn }.into(),
inner: InnerServerCerts {
peer_certificates,
sni,
alpn,
}
.into(),
is_tls: false,
}
}
Expand All @@ -105,9 +124,25 @@ impl ServerCerts {
}
}

pub async fn shuffle_rd_wr(in_rd: StanzaRead, in_wr: StanzaWrite, config: Arc<IncomingConfig>, server_certs: ServerCerts, local_addr: SocketAddr, client_addr: &mut Context<'_>) -> Result<()> {
pub async fn shuffle_rd_wr(
in_rd: StanzaRead,
in_wr: StanzaWrite,
config: Arc<IncomingConfig>,
server_certs: ServerCerts,
local_addr: SocketAddr,
client_addr: &mut Context<'_>,
) -> Result<()> {
let filter = StanzaFilter::new(config.max_stanza_size_bytes);
shuffle_rd_wr_filter(in_rd, in_wr, config, server_certs, local_addr, client_addr, filter).await
shuffle_rd_wr_filter(
in_rd,
in_wr,
config,
server_certs,
local_addr,
client_addr,
filter,
)
.await
}

pub async fn shuffle_rd_wr_filter(
Expand All @@ -120,7 +155,13 @@ pub async fn shuffle_rd_wr_filter(
mut in_filter: StanzaFilter,
) -> Result<()> {
// now read to figure out client vs server
let (stream_open, is_c2s) = stream_preamble(&mut in_rd, &mut in_wr, client_addr.log_from(), &mut in_filter).await?;
let (stream_open, is_c2s) = stream_preamble(
&mut in_rd,
&mut in_wr,
client_addr.log_from(),
&mut in_filter,
)
.await?;
client_addr.set_c2s_stream_open(is_c2s, &stream_open);

#[cfg(feature = "s2s-incoming")]
Expand All @@ -129,7 +170,9 @@ pub async fn shuffle_rd_wr_filter(
"{} connected: sni: {:?}, alpn: {:?}, tls-not-quic: {}",
client_addr.log_from(),
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(),
);

Expand All @@ -140,17 +183,37 @@ pub async fn shuffle_rd_wr_filter(
.or_else(|_| stream_open.extract_between(b" from=\"", b"\""))
.and_then(|b| Ok(std::str::from_utf8(b)?))?;
let (_, cert_verifier) = crate::srv::get_xmpp_connections(domain, is_c2s).await?;
let certs = server_certs.peer_certificates().ok_or_else(|| anyhow!("no client cert auth for s2s incoming from {}", domain))?;
let certs = server_certs
.peer_certificates()
.ok_or_else(|| anyhow!("no client cert auth for s2s incoming from {}", domain))?;
// todo: send stream error saying cert is invalid
cert_verifier.verify_cert(&certs[0], &certs[1..], UnixTime::now())?;
}
drop(server_certs);
}

let (out_rd, out_wr) = open_incoming(&config, local_addr, client_addr, &stream_open, is_c2s, &mut in_filter).await?;
let (out_rd, out_wr) = open_incoming(
&config,
local_addr,
client_addr,
&stream_open,
is_c2s,
&mut in_filter,
)
.await?;
drop(stream_open);

shuffle_rd_wr_filter_only(in_rd, in_wr, out_rd, out_wr, is_c2s, config.max_stanza_size_bytes, client_addr, in_filter).await
shuffle_rd_wr_filter_only(
in_rd,
in_wr,
out_rd,
out_wr,
is_c2s,
config.max_stanza_size_bytes,
client_addr,
in_filter,
)
.await
}

async fn open_incoming(
Expand All @@ -172,7 +235,12 @@ async fn open_incoming(
#[cfg(feature = "s2s-incoming")]
&config.s2s_target
};
let target = target.as_ref().ok_or_else(|| anyhow!("incoming connection but `{}_target` not defined", c2s(is_c2s)))?;
let target = target.as_ref().ok_or_else(|| {
anyhow!(
"incoming connection but `{}_target` not defined",
c2s(is_c2s)
)
})?;
client_addr.set_to_addr(target.to_string());

let (out_rd, mut out_wr) = target.connect().await?;
Expand All @@ -190,14 +258,22 @@ async fn open_incoming(
write!(
&mut in_filter.buf[0..],
"PROXY TCP{} {} {} {} {}\r\n",
if client_addr.client_addr().is_ipv4() { '4' } else { '6' },
if client_addr.client_addr().is_ipv4() {
'4'
} else {
'6'
},
client_addr.client_addr().ip(),
local_addr.ip(),
client_addr.client_addr().port(),
local_addr.port()
)?;
let end_idx = &(&in_filter.buf[0..]).first_index_of(b"\n")? + 1;
trace!("{} '{}'", client_addr.log_from(), to_str(&in_filter.buf[0..end_idx]));
trace!(
"{} '{}'",
client_addr.log_from(),
to_str(&in_filter.buf[0..end_idx])
);
out_wr.write_all(&in_filter.buf[0..end_idx]).await?;
}
trace!("{} '{}'", client_addr.log_from(), to_str(stream_open));
Expand Down
Loading