Skip to content

Commit

Permalink
Ignore server_name extension containing IP address
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ctz authored and cpu committed Apr 26, 2024
1 parent 7b8d1db commit 75f8857
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 9 deletions.
31 changes: 24 additions & 7 deletions rustls/src/msgs/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -211,6 +213,7 @@ impl TlsListElement for SignatureScheme {
#[derive(Clone, Debug)]
pub enum ServerNamePayload {
HostName(DnsName),
IpAddress(PayloadU16),
Unknown(Payload),
}

Expand All @@ -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))
}
}
}
Expand All @@ -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),
}
}
Expand Down Expand Up @@ -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,
}
}
Expand Down
4 changes: 2 additions & 2 deletions rustls/src/msgs/message_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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]
Expand Down

0 comments on commit 75f8857

Please sign in to comment.