Compare commits

...

5 Commits

Author SHA1 Message Date
Daniel McCarney 3633152cc5 Cargo: v0.21.11 -> v0.21.12 2024-04-26 14:44:21 +00:00
Daniel McCarney 0baaeba7a8 proj: MSRV 1.61 -> 1.63
We're seeing more of our deps move to this MSRV or higher (e.g.
`webpki`, `rustls-platform-verifier`) and it's shipped in Debian stable.
Time to move our MSRV to 1.63.
2024-04-26 14:44:21 +00:00
Daniel McCarney 6fd691a101 tls13: fix clippy::unnecessary_lazy_evaluations finding
```
error: unnecessary closure used with `bool::then`
  --> rustls/src/tls13/mod.rs:87:9
   |
87 |         (prev.hash_algorithm() == self.hash_algorithm()).then(|| prev)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------------
   |                                                          |
   |                                                          help: use `then_some(..)` instead: `then_some(prev)`
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
   = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
   = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]`

```
2024-04-26 14:44:21 +00:00
Joseph Birr-Pixton 6da53375a2 Test for illegal IP address in server name extension 2024-04-26 14:44:21 +00:00
Joseph Birr-Pixton 75f8857db7 Ignore server_name extension containing IP address
This works around quality-of-implementation issues in OpenSSL and
Apple SecureTransport: they send `server_name` extensions containing
IP addresses.  RFC6066 specifically disallows that.

It is a similar work-around to that adopted by LibreSSL: ignore
SNI contents if they can be parsed as an IP address.
2024-04-26 14:44:21 +00:00
8 changed files with 134 additions and 42 deletions

View File

@ -57,7 +57,7 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.61"
toolchain: "1.63"
- run: cargo check --lib --all-features -p rustls

View File

@ -92,7 +92,7 @@ x86, x86-64, LoongArch64, 32-bit & 64-bit Little Endian MIPS, 32-bit PowerPC (Bi
support WebAssembly.
For more information, see [the supported `ring` target platforms][ring-target-platforms].
Rustls requires Rust 1.61 or later.
Rustls requires Rust 1.63 or later.
[ring-target-platforms]: https://github.com/briansmith/ring/blob/2e8363b433fa3b3962c877d9ed2e9145612f3160/include/ring-core/target.h#L18-L64

View File

@ -1,8 +1,8 @@
[package]
name = "rustls"
version = "0.21.11"
version = "0.21.12"
edition = "2021"
rust-version = "1.61"
rust-version = "1.63"
license = "Apache-2.0 OR ISC OR MIT"
readme = "../README.md"
description = "Rustls is a modern TLS library written in Rust."

View File

@ -65,7 +65,7 @@
//! support WebAssembly.
//! For more information, see [the supported `ring` target platforms][ring-target-platforms].
//!
//! Rustls requires Rust 1.61 or later.
//! Rustls requires Rust 1.63 or later.
//!
//! [ring-target-platforms]: https://github.com/briansmith/ring/blob/2e8363b433fa3b3962c877d9ed2e9145612f3160/include/ring-core/target.h#L18-L64
//!

View File

@ -16,6 +16,8 @@ use crate::verify::DigitallySignedStruct;
use std::collections;
use std::fmt;
use std::net::IpAddr;
use std::str::FromStr;
/// Create a newtype wrapper around a given type.
///
@ -211,6 +213,7 @@ impl TlsListElement for SignatureScheme {
#[derive(Clone, Debug)]
pub enum ServerNamePayload {
HostName(DnsName),
IpAddress(PayloadU16),
Unknown(Payload),
}
@ -221,15 +224,12 @@ impl ServerNamePayload {
fn read_hostname(r: &mut Reader) -> Result<Self, InvalidMessage> {
let raw = PayloadU16::read(r)?;
match DnsName::try_from_ascii(&raw.0) {
Ok(dns_name) => Ok(Self::HostName(dns_name)),
Err(_) => {
warn!(
"Illegal SNI hostname received {:?}",
String::from_utf8_lossy(&raw.0)
);
Err(InvalidMessage::InvalidServerName)
let _ = IpAddr::from_str(&String::from_utf8_lossy(&raw.0))
.map_err(|_| InvalidMessage::InvalidServerName)?;
Ok(Self::IpAddress(raw))
}
}
}
@ -240,6 +240,7 @@ impl ServerNamePayload {
(name.as_ref().len() as u16).encode(bytes);
bytes.extend_from_slice(name.as_ref().as_bytes());
}
Self::IpAddress(ref r) => r.encode(bytes),
Self::Unknown(ref r) => r.encode(bytes),
}
}
@ -897,7 +898,23 @@ impl ClientHelloPayload {
pub fn get_sni_extension(&self) -> Option<&[ServerName]> {
let ext = self.find_extension(ExtensionType::ServerName)?;
match *ext {
ClientExtension::ServerName(ref req) => Some(req),
// Does this comply with RFC6066?
//
// [RFC6066][] specifies that literal IP addresses are illegal in
// `ServerName`s with a `name_type` of `host_name`.
//
// Some clients incorrectly send such extensions: we choose to
// successfully parse these (into `ServerNamePayload::IpAddress`)
// but then act like the client sent no `server_name` extension.
//
// [RFC6066]: https://datatracker.ietf.org/doc/html/rfc6066#section-3
ClientExtension::ServerName(ref req)
if !req
.iter()
.any(|name| matches!(name.payload, ServerNamePayload::IpAddress(_))) =>
{
Some(req)
}
_ => None,
}
}

View File

@ -48,7 +48,7 @@ fn test_read_fuzz_corpus() {
}
#[test]
fn can_read_safari_client_hello() {
fn can_read_safari_client_hello_with_ip_address_in_sni_extension() {
let _ = env_logger::Builder::new()
.filter(None, log::LevelFilter::Trace)
.try_init();
@ -72,7 +72,7 @@ fn can_read_safari_client_hello() {
let mut rd = Reader::init(bytes);
let m = OpaqueMessage::read(&mut rd).unwrap();
println!("m = {:?}", m);
assert!(Message::try_from(m.into_plain_message()).is_err());
Message::try_from(m.into_plain_message()).unwrap();
}
#[test]

View File

@ -84,7 +84,7 @@ impl Tls13CipherSuite {
/// Can a session using suite self resume from suite prev?
pub fn can_resume_from(&self, prev: &'static Self) -> Option<&'static Self> {
(prev.hash_algorithm() == self.hash_algorithm()).then(|| prev)
(prev.hash_algorithm() == self.hash_algorithm()).then_some(prev)
}
}

View File

@ -13,12 +13,13 @@ use std::sync::Mutex;
use rustls::client::{ResolvesClientCert, Resumption};
use rustls::internal::msgs::base::Payload;
use rustls::internal::msgs::codec::Codec;
use rustls::internal::msgs::handshake::ServerName as ServerNameExtensionItem;
use rustls::server::{AllowAnyAnonymousOrAuthenticatedClient, ClientHello, ResolvesServerCert};
#[cfg(feature = "secret_extraction")]
use rustls::ConnectionTrafficSecrets;
use rustls::{
sign, CertificateError, ConnectionCommon, Error, KeyLog, PeerIncompatible, PeerMisbehaved,
SideData,
sign, CertificateError, ConnectionCommon, Error, InvalidMessage, KeyLog, PeerIncompatible,
PeerMisbehaved, SideData,
};
use rustls::{CipherSuite, ProtocolVersion, SignatureScheme};
use rustls::{ClientConfig, ClientConnection};
@ -803,6 +804,92 @@ fn client_trims_terminating_dot() {
}
}
#[test]
fn server_ignores_sni_with_ip_address() {
fn insert_ip_address_server_name(msg: &mut Message) -> Altered {
alter_sni_extension(
msg,
|snr| {
snr.clear();
snr.push(ServerNameExtensionItem::read_bytes(b"\x00\x00\x071.1.1.1").unwrap());
},
|parsed, _encoded| Payload::new(parsed.get_encoding()),
)
}
check_sni_error(
insert_ip_address_server_name,
Error::General("no server certificate chain resolved".to_string()),
);
}
#[test]
fn server_rejects_sni_with_illegal_dns_name() {
fn insert_illegal_server_name(msg: &mut Message) -> Altered {
alter_sni_extension(
msg,
|_| (),
|_, encoded| {
// replace "localhost" with invalid DNS name
let mut altered = encoded.clone().0;
let needle = b"localhost";
let index = altered
.windows(needle.len())
.position(|window| window == needle)
.unwrap();
altered[index..index + needle.len()].copy_from_slice(b"ab@cd.com");
Payload::new(altered)
},
)
}
check_sni_error(
insert_illegal_server_name,
Error::InvalidMessage(InvalidMessage::InvalidServerName),
);
}
fn alter_sni_extension(
msg: &mut Message,
alter_inner: impl Fn(&mut Vec<ServerNameExtensionItem>),
alter_encoding: impl Fn(&mut HandshakeMessagePayload, &mut Payload) -> Payload,
) -> Altered {
if let MessagePayload::Handshake { parsed, encoded } = &mut msg.payload {
if let HandshakePayload::ClientHello(ch) = &mut parsed.payload {
for mut ext in ch.extensions.iter_mut() {
if let ClientExtension::ServerName(snr) = &mut ext {
alter_inner(snr);
}
}
*encoded = alter_encoding(parsed, encoded);
}
}
Altered::InPlace
}
fn check_sni_error(alteration: impl Fn(&mut Message) -> Altered, expected_error: Error) {
for kt in ALL_KEY_TYPES {
let client_config = make_client_config(kt);
let mut server_config = make_server_config(kt);
server_config.cert_resolver = Arc::new(ServerCheckNoSNI {});
let client = ClientConnection::new(Arc::new(client_config), dns_name("localhost")).unwrap();
let server = ServerConnection::new(Arc::new(server_config)).unwrap();
let (mut client, mut server) = (client.into(), server.into());
transfer_altered(&mut client, &alteration, &mut server);
assert_eq!(server.process_new_packets(), Err(expected_error.clone()),);
let server_inner = match server {
rustls::Connection::Server(server) => server,
_ => unreachable!(),
};
assert_eq!(None, server_inner.server_name());
}
}
#[cfg(feature = "tls12")]
fn check_sigalgs_reduced_by_ciphersuite(
kt: KeyType,
@ -4327,6 +4414,7 @@ fn connection_types_are_not_huge() {
assert_lt(mem::size_of::<ClientConnection>(), 1600);
}
use rustls::internal::msgs::handshake::HandshakeMessagePayload;
use rustls::internal::msgs::{
handshake::ClientExtension, handshake::HandshakePayload, message::Message,
message::MessagePayload,
@ -4335,18 +4423,13 @@ use rustls::internal::msgs::{
#[test]
fn test_server_rejects_duplicate_sni_names() {
fn duplicate_sni_payload(msg: &mut Message) -> Altered {
if let MessagePayload::Handshake { parsed, encoded } = &mut msg.payload {
if let HandshakePayload::ClientHello(ch) = &mut parsed.payload {
for mut ext in ch.extensions.iter_mut() {
if let ClientExtension::ServerName(snr) = &mut ext {
snr.push(snr[0].clone());
}
}
}
*encoded = Payload::new(parsed.get_encoding());
}
Altered::InPlace
alter_sni_extension(
msg,
|snr| {
snr.push(snr[0].clone());
},
|parsed, _encoded| Payload::new(parsed.get_encoding()),
)
}
let (client, server) = make_pair(KeyType::Rsa);
@ -4363,19 +4446,11 @@ fn test_server_rejects_duplicate_sni_names() {
#[test]
fn test_server_rejects_empty_sni_extension() {
fn empty_sni_payload(msg: &mut Message) -> Altered {
if let MessagePayload::Handshake { parsed, encoded } = &mut msg.payload {
if let HandshakePayload::ClientHello(ch) = &mut parsed.payload {
for mut ext in ch.extensions.iter_mut() {
if let ClientExtension::ServerName(snr) = &mut ext {
snr.clear();
}
}
}
*encoded = Payload::new(parsed.get_encoding());
}
Altered::InPlace
alter_sni_extension(
msg,
|snr| snr.clear(),
|parsed, _encoded| Payload::new(parsed.get_encoding()),
)
}
let (client, server) = make_pair(KeyType::Rsa);