1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 08:17:45 +00:00

LibTLS: Support empty SNI data in ServerHello

According to RFC6066, empty extension_data for an SNI extension is
absolutely one of the possibilities - so let's support this instead of
spamming the debug log.
This commit is contained in:
Jelle Raaijmakers 2021-04-10 00:32:59 +02:00 committed by Andreas Kling
parent 48eb58230b
commit 7d5995f08c
2 changed files with 72 additions and 61 deletions

View file

@ -117,27 +117,24 @@ ssize_t TLSv12::handle_hello(ReadonlyBytes buffer, WritePacketStage& write_packe
// The handshake hash function is _always_ SHA256 // The handshake hash function is _always_ SHA256
m_context.handshake_hash.initialize(Crypto::Hash::HashKind::SHA256); m_context.handshake_hash.initialize(Crypto::Hash::HashKind::SHA256);
if (buffer.size() - res < 1) { // Compression method
dbgln("not enough data for compression spec"); if (buffer.size() - res < 1)
return (i8)Error::NeedMoreData; return (i8)Error::NeedMoreData;
}
u8 compression = buffer[res++]; u8 compression = buffer[res++];
if (compression != 0) { if (compression != 0)
dbgln("Server told us to compress, we will not!");
return (i8)Error::CompressionNotSupported; return (i8)Error::CompressionNotSupported;
if (m_context.connection_status != ConnectionStatus::Renegotiating)
m_context.connection_status = ConnectionStatus::Negotiating;
if (m_context.is_server) {
dbgln("unsupported: server mode");
write_packets = WritePacketStage::ServerHandshake;
} }
if (res > 0) { // Presence of extensions is determined by availability of bytes after compression_method
if (m_context.connection_status != ConnectionStatus::Renegotiating) if (buffer.size() - res >= 2) {
m_context.connection_status = ConnectionStatus::Negotiating; auto extensions_bytes_total = AK::convert_between_host_and_network_endian(*(const u16*)buffer.offset_pointer(res += 2));
if (m_context.is_server) { dbgln_if(TLS_DEBUG, "Extensions bytes total: {}", extensions_bytes_total);
dbgln("unsupported: server mode");
write_packets = WritePacketStage::ServerHandshake;
}
}
if (res > 2) {
res += 2;
} }
while ((ssize_t)buffer.size() - res >= 4) { while ((ssize_t)buffer.size() - res >= 4) {
@ -146,60 +143,70 @@ ssize_t TLSv12::handle_hello(ReadonlyBytes buffer, WritePacketStage& write_packe
u16 extension_length = AK::convert_between_host_and_network_endian(*(const u16*)buffer.offset_pointer(res)); u16 extension_length = AK::convert_between_host_and_network_endian(*(const u16*)buffer.offset_pointer(res));
res += 2; res += 2;
dbgln_if(TLS_DEBUG, "extension {} with length {}", (u16)extension_type, extension_length); dbgln_if(TLS_DEBUG, "Extension {} with length {}", (u16)extension_type, extension_length);
if (extension_length) { if (buffer.size() - res < extension_length)
if (buffer.size() - res < extension_length) { return (i8)Error::NeedMoreData;
dbgln("not enough data for extension");
return (i8)Error::NeedMoreData;
}
dbgln("Encountered extension {} with length {}", (u16)extension_type, extension_length); if (extension_type == HandshakeExtension::ServerName) {
// SNI // RFC6066 section 3: SNI extension_data can be empty in the server hello
if (extension_type == HandshakeExtension::ServerName) { if (extension_length > 0) {
u16 sni_host_length = AK::convert_between_host_and_network_endian(*(const u16*)buffer.offset_pointer(res + 3)); // ServerNameList total size
if (buffer.size() - res - 5 < sni_host_length) { if (buffer.size() - res < 2)
dbgln("Not enough data for sni {} < {}", (buffer.size() - res - 5), sni_host_length);
return (i8)Error::NeedMoreData; return (i8)Error::NeedMoreData;
} auto sni_name_list_bytes = AK::convert_between_host_and_network_endian(*(const u16*)buffer.offset_pointer(res += 2));
dbgln_if(TLS_DEBUG, "SNI: expecting ServerNameList of {} bytes", sni_name_list_bytes);
if (sni_host_length) { // Exactly one ServerName should be present
m_context.extensions.SNI = String { (const char*)buffer.offset_pointer(res + 5), sni_host_length }; if (buffer.size() - res < 3)
dbgln("server name indicator: {}", m_context.extensions.SNI); return (i8)Error::NeedMoreData;
} auto sni_name_type = (NameType)(*(const u8*)buffer.offset_pointer(res++));
} else if (extension_type == HandshakeExtension::ApplicationLayerProtocolNegotiation && m_context.alpn.size()) { auto sni_name_length = AK::convert_between_host_and_network_endian(*(const u16*)buffer.offset_pointer(res += 2));
if (buffer.size() - res > 2) {
auto alpn_length = AK::convert_between_host_and_network_endian(*(const u16*)buffer.offset_pointer(res)); if (sni_name_type != NameType::HostName)
if (alpn_length && alpn_length <= extension_length - 2) { return (i8)Error::NotUnderstood;
const u8* alpn = buffer.offset_pointer(res + 2);
size_t alpn_position = 0; if (sizeof(sni_name_type) + sizeof(sni_name_length) + sni_name_length != sni_name_list_bytes)
while (alpn_position < alpn_length) { return (i8)Error::BrokenPacket;
u8 alpn_size = alpn[alpn_position++];
if (alpn_size + alpn_position >= extension_length) // Read out the host_name
break; if (buffer.size() - res < sni_name_length)
String alpn_str { (const char*)alpn + alpn_position, alpn_length }; return (i8)Error::NeedMoreData;
if (alpn_size && m_context.alpn.contains_slow(alpn_str)) { m_context.extensions.SNI = String { (const char*)buffer.offset_pointer(res), sni_name_length };
m_context.negotiated_alpn = alpn_str; res += sni_name_length;
dbgln("negotiated alpn: {}", alpn_str); dbgln("SNI host_name: {}", m_context.extensions.SNI);
break; }
} } else if (extension_type == HandshakeExtension::ApplicationLayerProtocolNegotiation && m_context.alpn.size()) {
alpn_position += alpn_length; if (buffer.size() - res > 2) {
if (!m_context.is_server) // server hello must contain one ALPN auto alpn_length = AK::convert_between_host_and_network_endian(*(const u16*)buffer.offset_pointer(res));
break; if (alpn_length && alpn_length <= extension_length - 2) {
const u8* alpn = buffer.offset_pointer(res + 2);
size_t alpn_position = 0;
while (alpn_position < alpn_length) {
u8 alpn_size = alpn[alpn_position++];
if (alpn_size + alpn_position >= extension_length)
break;
String alpn_str { (const char*)alpn + alpn_position, alpn_length };
if (alpn_size && m_context.alpn.contains_slow(alpn_str)) {
m_context.negotiated_alpn = alpn_str;
dbgln("negotiated alpn: {}", alpn_str);
break;
} }
alpn_position += alpn_length;
if (!m_context.is_server) // server hello must contain one ALPN
break;
} }
} }
} else if (extension_type == HandshakeExtension::SignatureAlgorithms) {
dbgln("supported signatures: ");
print_buffer(buffer.slice(res, extension_length));
// FIXME: what are we supposed to do here?
} else {
dbgln("Encountered unknown extension {} with length {}", (u16)extension_type, extension_length);
} }
res += extension_length; res += extension_length;
} else if (extension_type == HandshakeExtension::SignatureAlgorithms) {
dbgln("supported signatures: ");
print_buffer(buffer.slice(res, extension_length));
res += extension_length;
// FIXME: what are we supposed to do here?
} else { } else {
// Zero-length extensions.
dbgln("Encountered unknown extension {} with length {}", (u16)extension_type, extension_length); dbgln("Encountered unknown extension {} with length {}", (u16)extension_type, extension_length);
res += extension_length;
} }
} }
@ -372,9 +379,8 @@ ssize_t TLSv12::handle_payload(ReadonlyBytes vbuffer)
if (m_context.is_server) { if (m_context.is_server) {
dbgln("unsupported: server mode"); dbgln("unsupported: server mode");
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} else {
payload_res = handle_hello(buffer.slice(1, payload_size), write_packets);
} }
payload_res = handle_hello(buffer.slice(1, payload_size), write_packets);
break; break;
case HelloVerifyRequest: case HelloVerifyRequest:
dbgln("unsupported: DTLS"); dbgln("unsupported: DTLS");
@ -585,6 +591,7 @@ ssize_t TLSv12::handle_payload(ReadonlyBytes vbuffer)
} }
case Error::NeedMoreData: case Error::NeedMoreData:
// Ignore this, as it's not an "error" // Ignore this, as it's not an "error"
dbgln_if(TLS_DEBUG, "More data needed");
break; break;
default: default:
dbgln("Unknown TLS::Error with value {}", payload_res); dbgln("Unknown TLS::Error with value {}", payload_res);

View file

@ -175,6 +175,10 @@ enum class HandshakeExtension : u16 {
SignatureAlgorithms = 0x0d, SignatureAlgorithms = 0x0d,
}; };
enum class NameType : u8 {
HostName = 0x00,
};
enum class WritePacketStage { enum class WritePacketStage {
Initial = 0, Initial = 0,
ClientHandshake = 1, ClientHandshake = 1,