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.
This commit is contained in:
Joseph Birr-Pixton 2024-03-29 09:20:24 +00:00 committed by Joe Birr-Pixton
parent d8d438aecc
commit f0d33d13a8
3 changed files with 28 additions and 7 deletions

View File

@ -443,7 +443,8 @@ pub mod internal {
pub use crate::msgs::handshake::{
CertificateChain, ClientExtension, ClientHelloPayload, DistinguishedName,
EchConfig, EchConfigContents, HandshakeMessagePayload, HandshakePayload,
HpkeKeyConfig, HpkeSymmetricCipherSuite, KeyShareEntry, Random, SessionId,
HpkeKeyConfig, HpkeSymmetricCipherSuite, KeyShareEntry, Random, ServerName,
SessionId,
};
}
pub mod message {

View File

@ -216,6 +216,7 @@ impl TlsListElement for SignatureScheme {
#[derive(Clone, Debug)]
pub(crate) enum ServerNamePayload {
HostName(DnsName<'static>),
IpAddress(PayloadU16),
Unknown(Payload<'static>),
}
@ -225,11 +226,13 @@ impl ServerNamePayload {
}
fn read_hostname(r: &mut Reader) -> Result<Self, InvalidMessage> {
use pki_types::ServerName;
let raw = PayloadU16::read(r)?;
match DnsName::try_from(raw.0.as_slice()) {
Ok(dns_name) => Ok(Self::HostName(dns_name.to_owned())),
Err(_) => {
match ServerName::try_from(raw.0.as_slice()) {
Ok(ServerName::DnsName(d)) => Ok(Self::HostName(d.to_owned())),
Ok(ServerName::IpAddress(_)) => Ok(Self::IpAddress(raw)),
Ok(_) | Err(_) => {
warn!(
"Illegal SNI hostname received {:?}",
String::from_utf8_lossy(&raw.0)
@ -245,6 +248,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),
}
}
@ -864,7 +868,23 @@ impl ClientHelloPayload {
pub(crate) fn 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 = OutboundOpaqueMessage::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]