Detect stateless resets with malformed headers

A valid stateless reset packet might not be large enough to contain
the expected connection ID, which can cause header decoding to fail.
This commit is contained in:
Benjamin Saunders
2022-11-19 18:11:45 -08:00
committed by Dirkjan Ochtman
parent 1d390af2fa
commit fbccb656cc
2 changed files with 29 additions and 24 deletions
+26 -18
View File
@@ -1865,8 +1865,14 @@ impl Connection {
None
};
let packet = partial_decode.data();
let stateless_reset = packet.len() >= RESET_TOKEN_SIZE + 5
&& self.peer_params.stateless_reset_token.as_deref()
== Some(&packet[packet.len() - RESET_TOKEN_SIZE..]);
match partial_decode.finish(header_crypto) {
Ok(packet) => self.handle_packet(now, remote, ecn, packet),
Ok(packet) => self.handle_packet(now, remote, ecn, Some(packet), stateless_reset),
Err(_) if stateless_reset => self.handle_packet(now, remote, ecn, None, true),
Err(e) => {
trace!("unable to complete packet decoding: {}", e);
}
@@ -1878,15 +1884,18 @@ impl Connection {
now: Instant,
remote: SocketAddr,
ecn: Option<EcnCodepoint>,
mut packet: Packet,
packet: Option<Packet>,
stateless_reset: bool,
) {
trace!(
"got {:?} packet ({} bytes) from {} using id {}",
packet.header.space(),
packet.payload.len() + packet.header_data.len(),
remote,
packet.header.dst_cid(),
);
if let Some(ref packet) = packet {
trace!(
"got {:?} packet ({} bytes) from {} using id {}",
packet.header.space(),
packet.payload.len() + packet.header_data.len(),
remote,
packet.header.dst_cid(),
);
}
if self.is_handshaking() && remote != self.path.remote {
debug!("discarding packet with unexpected remote during handshake");
@@ -1895,15 +1904,14 @@ impl Connection {
let was_closed = self.state.is_closed();
let was_drained = self.state.is_drained();
let stateless_reset = self
.peer_params
.stateless_reset_token
.map_or(false, |token| {
packet.payload.len() >= RESET_TOKEN_SIZE
&& packet.payload[packet.payload.len() - RESET_TOKEN_SIZE..] == token[..]
});
let result = match self.decrypt_packet(now, &mut packet) {
let decrypted = match packet {
None => Err(None),
Some(mut packet) => self
.decrypt_packet(now, &mut packet)
.map(move |number| (packet, number)),
};
let result = match decrypted {
Err(Some(e)) => {
warn!("illegal packet: {}", e);
Err(e.into())
@@ -1929,7 +1937,7 @@ impl Connection {
}
}
}
Ok(number) => {
Ok((packet, number)) => {
let span = match number {
Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn),
None => trace_span!("recv", space = ?packet.header.space()),
+3 -6
View File
@@ -175,13 +175,10 @@ fn server_stateless_reset() {
let mut pair = Pair::new(endpoint_config.clone(), server_config());
let (client_ch, _) = pair.connect();
pair.drive(); // Flush any post-handshake frames
pair.server.endpoint = Endpoint::new(endpoint_config, Some(Arc::new(server_config())));
// Send something big enough to allow room for a smaller stateless reset.
pair.client.connections.get_mut(&client_ch).unwrap().close(
pair.time,
VarInt(42),
(&[0xab; 128][..]).into(),
);
// Force the server to generate the smallest possible stateless reset
pair.client.connections.get_mut(&client_ch).unwrap().ping();
info!("resetting");
pair.drive();
assert_matches!(