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:
parent
48eb58230b
commit
7d5995f08c
2 changed files with 72 additions and 61 deletions
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue