Skip to content

Commit 32b3888

Browse files
committed
Reject a malformed version message instead of panicking
The server parsed the client's version message with version_bytes[..4], which panics on a body shorter than 4 bytes — a remotely triggerable panic for any TLS-authenticated peer, and process death under panic = "abort". Parse with a checked conversion and reject anything but exactly 4 bytes as Error::ProtocolVersion. This is stricter than the old code in one way: a version message longer than 4 bytes was previously accepted with the trailing bytes ignored. No client has ever sent one (both protocol versions send exactly CURRENT_PROTOCOL_VERSION.to_le_bytes()), so no interoperability change.
1 parent 026a3fa commit 32b3888

2 files changed

Lines changed: 79 additions & 2 deletions

File tree

tls/src/lib.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,77 @@ mod tests {
615615
handle.await.unwrap();
616616
}
617617

618+
// A version message whose body is shorter than the 4-byte version, sent
619+
// by a TLS-authenticated client, is rejected as Error::ProtocolVersion —
620+
// the server task must error, not panic on a short slice.
621+
#[tokio::test]
622+
async fn short_version_message_rejected() {
623+
let log = logger();
624+
let pki_keydir = pki_keydir();
625+
let mock_datadir = mock_datadir();
626+
let addr: SocketAddrV6 = SocketAddrV6::from_str("[::1]:46467").unwrap();
627+
628+
let server_config =
629+
local_config(1, MeasurementConnectionPolicy::Enforced);
630+
let corpus = vec![
631+
mock_datadir.join("corim-rot.cbor"),
632+
mock_datadir.join("corim-sp.cbor"),
633+
];
634+
635+
let log2 = log.clone();
636+
let handle = tokio::spawn(async move {
637+
let server = Server::new(server_config, addr, log2.clone())
638+
.await
639+
.unwrap();
640+
641+
let result = server
642+
.accept(corpus.clone())
643+
.await
644+
.unwrap()
645+
.handshake()
646+
.await;
647+
match result {
648+
Err(Error::ProtocolVersion) => {}
649+
Err(other) => {
650+
panic!("expected ProtocolVersion, got {other:?}")
651+
}
652+
Ok(_) => {
653+
panic!("a malformed version message must not complete")
654+
}
655+
}
656+
});
657+
658+
let client_config = client::Client::new_tls_local_client_config(
659+
pki_keydir.join("test-sprockets-auth-2.key.pem"),
660+
pki_keydir.join("test-sprockets-auth-2.certlist.pem"),
661+
vec![pki_keydir.join("test-root-a.cert.pem")],
662+
log,
663+
)
664+
.unwrap();
665+
666+
let dnsname =
667+
rustls::pki_types::ServerName::try_from("unknown.com").unwrap();
668+
let connector = tokio_rustls::TlsConnector::from(std::sync::Arc::new(
669+
client_config,
670+
));
671+
let stream = loop {
672+
if let Ok(s) = tokio::net::TcpStream::connect(addr).await {
673+
break s;
674+
};
675+
sleep(Duration::from_millis(1)).await;
676+
};
677+
let mut stream = connector.connect(dnsname, stream).await.unwrap();
678+
679+
// A valid length prefix (2) followed by a 2-byte body: the server's
680+
// recv_msg succeeds, but the body is shorter than the 4-byte version
681+
// it must contain.
682+
stream.write_all(&2u32.to_le_bytes()).await.unwrap();
683+
stream.write_all(b"xy").await.unwrap();
684+
stream.shutdown().await.unwrap();
685+
686+
handle.await.unwrap();
687+
}
688+
618689
#[tokio::test]
619690
async fn spawn_accept() {
620691
let log = logger();

tls/src/server.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,14 @@ impl SprocketsAcceptor {
138138

139139
// get version from the client
140140
let version_bytes = recv_msg(&mut stream).await?;
141-
let version =
142-
u32::from_le_bytes(version_bytes[..4].try_into().unwrap());
141+
// Anything but exactly the 4-byte little-endian version is protocol
142+
// garbage from the peer; reject it rather than index past the end of a
143+
// short message.
144+
let version_bytes: [u8; 4] = version_bytes
145+
.as_slice()
146+
.try_into()
147+
.map_err(|_| Error::ProtocolVersion)?;
148+
let version = u32::from_le_bytes(version_bytes);
143149

144150
if version == CURRENT_PROTOCOL_VERSION {
145151
// we're good to go

0 commit comments

Comments
 (0)