From ed04a0803a93525f4344356819080a6e373b0bc5 Mon Sep 17 00:00:00 2001 From: James Mayclin Date: Thu, 27 Jun 2024 18:02:18 -0700 Subject: [PATCH] feat(bindings/s2n-tls): add client_hello_version (#4609) --- bindings/rust/s2n-tls/src/connection.rs | 15 ++++++ bindings/rust/s2n-tls/src/testing/s2n_tls.rs | 57 +++++++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/bindings/rust/s2n-tls/src/connection.rs b/bindings/rust/s2n-tls/src/connection.rs index 56bab8f9c88..d2e3fda5588 100644 --- a/bindings/rust/s2n-tls/src/connection.rs +++ b/bindings/rust/s2n-tls/src/connection.rs @@ -857,6 +857,7 @@ impl Connection { Ok(()) } + /// Access the protocol version selected for the connection. pub fn actual_protocol_version(&self) -> Result { let version = unsafe { s2n_connection_get_actual_protocol_version(self.connection.as_ptr()).into_result()? @@ -864,6 +865,20 @@ impl Connection { version.try_into() } + /// Detects if the client hello is using the SSLv2 format. + /// + /// s2n-tls will not negotiate SSLv2, but will accept SSLv2 ClientHellos + /// advertising a higher protocol version like SSLv3 or TLS1.0. + /// [Connection::actual_protocol_version()] can be used to retrieve the + /// protocol version that is actually used on the connection. + pub fn client_hello_is_sslv2(&self) -> Result { + let version = unsafe { + s2n_connection_get_client_hello_version(self.connection.as_ptr()).into_result()? + }; + let version: Version = version.try_into()?; + Ok(version == Version::SSLV2) + } + pub fn handshake_type(&self) -> Result<&str, Error> { let handshake = unsafe { s2n_connection_get_handshake_type_name(self.connection.as_ptr()).into_result()? diff --git a/bindings/rust/s2n-tls/src/testing/s2n_tls.rs b/bindings/rust/s2n-tls/src/testing/s2n_tls.rs index 312464a24b1..e66d8046efc 100644 --- a/bindings/rust/s2n-tls/src/testing/s2n_tls.rs +++ b/bindings/rust/s2n-tls/src/testing/s2n_tls.rs @@ -236,7 +236,7 @@ mod tests { callbacks::{ClientHelloCallback, ConnectionFuture, ConnectionFutureResult}, enums::ClientAuthType, error::ErrorType, - testing::{client_hello::*, *}, + testing::{self, client_hello::*, s2n_tls::*, *}, }; use alloc::sync::Arc; use core::sync::atomic::Ordering; @@ -894,4 +894,59 @@ mod tests { assert_eq!(protocol, b"h2"); Ok(()) } + + #[test] + fn client_hello_sslv2_negative() -> Result<(), testing::Error> { + let config = testing::build_config(&security::DEFAULT_TLS13)?; + let mut pair = TestPair::from_config(&config); + pair.handshake()?; + assert!(!pair.server.client_hello_is_sslv2()?); + Ok(()) + } + + #[test] + fn client_hello_sslv2_positive() -> Result<(), testing::Error> { + // copy-pasted from s2n-tls/tests/testlib/s2n_sslv2_client_hello.h + // by concatenating these fields together, a valid SSLv2 formatted client hello + // can be assembled + const SSLV2_CLIENT_HELLO_HEADER: &[u8] = &[0x80, 0xb3, 0x01, 0x03, 0x03]; + const SSLV2_CLIENT_HELLO_PREFIX: &[u8] = &[0x00, 0x8a, 0x00, 0x00, 0x00, 0x20]; + const SSLV2_CLIENT_HELLO_CIPHER_SUITES: &[u8] = &[ + 0x00, 0xc0, 0x24, 0x00, 0xc0, 0x28, 0x00, 0x00, 0x3d, 0x00, 0xc0, 0x26, 0x00, 0xc0, + 0x2a, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x6a, 0x00, 0xc0, 0x0a, 0x07, 0x00, 0xc0, 0x00, + 0xc0, 0x14, 0x00, 0x00, 0x35, 0x00, 0xc0, 0x05, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x39, + 0x00, 0x00, 0x38, 0x00, 0xc0, 0x23, 0x00, 0xc0, 0x27, 0x00, 0x00, 0x3c, 0x00, 0xc0, + 0x25, 0x00, 0xc0, 0x29, 0x00, 0x00, 0x67, 0x00, 0x00, 0x40, 0x00, 0xc0, 0x09, 0x06, + 0x00, 0x40, 0x00, 0xc0, 0x13, 0x00, 0x00, 0x2f, 0x00, 0xc0, 0x04, 0x01, 0x00, 0x80, + 0x00, 0xc0, 0x0e, 0x00, 0x00, 0x33, 0x00, 0x00, 0x32, 0x00, 0xc0, 0x2c, 0x00, 0xc0, + 0x2b, 0x00, 0xc0, 0x30, 0x00, 0x00, 0x9d, 0x00, 0xc0, 0x2e, 0x00, 0xc0, 0x32, 0x00, + 0x00, 0x9f, 0x00, 0x00, 0xa3, 0x00, 0xc0, 0x2f, 0x00, 0x00, 0x9c, 0x00, 0xc0, 0x2d, + 0x00, 0xc0, 0x31, 0x00, 0x00, 0x9e, 0x00, 0x00, 0xa2, 0x00, 0x00, 0xff, + ]; + const SSLV2_CLIENT_HELLO_CHALLENGE: &[u8] = &[ + 0x5b, 0xe9, 0xcc, 0xad, 0xd6, 0xa5, 0x20, 0xac, 0xa3, 0xf4, 0x8e, 0x88, 0x06, 0xb5, + 0x95, 0x53, 0x2d, 0x53, 0xfe, 0xd7, 0xa1, 0x00, 0x57, 0xc0, 0x53, 0x9d, 0x84, 0x71, + 0x80, 0x7f, 0x30, 0x7e, + ]; + + let config = testing::build_config(&security::Policy::from_version("test_all")?)?; + // we use the pair to setup IO, but we don't want the client to write anything. + // So we drop the client and just directly write the SSLv2 header to the + // client_tx_stream + let mut pair = TestPair::from_config(&config); + drop(pair.client); + + let mut client_tx_stream = pair.client_tx_stream.borrow_mut(); + client_tx_stream.write_all(SSLV2_CLIENT_HELLO_HEADER)?; + client_tx_stream.write_all(SSLV2_CLIENT_HELLO_PREFIX)?; + client_tx_stream.write_all(SSLV2_CLIENT_HELLO_CIPHER_SUITES)?; + client_tx_stream.write_all(SSLV2_CLIENT_HELLO_CHALLENGE)?; + // end the exclusive borrow + drop(client_tx_stream); + + // the first server.poll_negotiate causes the server to read in the client hello + assert!(pair.server.poll_negotiate()?.is_pending()); + assert!(pair.server.client_hello_is_sslv2()?); + Ok(()) + } }