Skip to content

Commit ac796cb

Browse files
authored
[release/6.0] add support for parsing Unified TLS hello (#68425)
* add support for parsing Unified TLS hello * feedback from review * update comment
1 parent dbd2a6b commit ac796cb

File tree

2 files changed

+53
-28
lines changed

2 files changed

+53
-28
lines changed

src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ private enum Framing
4343
private const int FrameOverhead = 64;
4444
private const int ReadBufferSize = 4096 * 4 + FrameOverhead; // We read in 16K chunks + headers.
4545
private const int InitialHandshakeBufferSize = 4096 + FrameOverhead; // try to fit at least 4K ServerCertificate
46+
private const int HandshakeTypeOffsetSsl2 = 2; // Offset of HelloType in Sslv2 and Unified frames
47+
private const int HandshakeTypeOffsetTls = 5; // Offset of HelloType in Sslv3 and TLS frames
4648
private ArrayBuffer _handshakeBuffer;
4749
private bool _receivedEOF;
4850

@@ -475,6 +477,7 @@ private async ValueTask<ProtocolToken> ReceiveBlobAsync<TIOAdapter>(TIOAdapter a
475477
#pragma warning disable 0618
476478
_lastFrame.Header.Version = SslProtocols.Ssl2;
477479
#pragma warning restore 0618
480+
_lastFrame.Header.Type = TlsContentType.Handshake; // Implied. We only call this during handshake and SSL2 does not have framing layer.
478481
_lastFrame.Header.Length = GetFrameSize(_handshakeBuffer.ActiveReadOnlySpan) - TlsFrameHelper.HeaderSize;
479482
}
480483
else
@@ -506,9 +509,11 @@ private async ValueTask<ProtocolToken> ReceiveBlobAsync<TIOAdapter>(TIOAdapter a
506509
}
507510
break;
508511
case TlsContentType.Handshake:
509-
if (!_isRenego && _handshakeBuffer.ActiveReadOnlySpan[TlsFrameHelper.HeaderSize] == (byte)TlsHandshakeType.ClientHello &&
510-
(_sslAuthenticationOptions!.ServerCertSelectionDelegate != null ||
511-
_sslAuthenticationOptions!.ServerOptionDelegate != null))
512+
byte handshakeType = _handshakeBuffer.ActiveReadOnlySpan[_framing == Framing.SinceSSL3 ? HandshakeTypeOffsetTls : HandshakeTypeOffsetSsl2];
513+
514+
if (!_isRenego &&
515+
(_sslAuthenticationOptions!.ServerCertSelectionDelegate != null || _sslAuthenticationOptions!.ServerOptionDelegate != null) &&
516+
(handshakeType == (byte)TlsHandshakeType.ClientHello))
512517
{
513518
TlsFrameHelper.ProcessingOptions options = NetEventSource.Log.IsEnabled() ?
514519
TlsFrameHelper.ProcessingOptions.All :

src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -173,33 +173,41 @@ public override string ToString()
173173

174174
public static bool TryGetFrameHeader(ReadOnlySpan<byte> frame, ref TlsFrameHeader header)
175175
{
176-
bool result = frame.Length > 4;
177-
178-
if (frame.Length >= 1)
176+
header.Type = (TlsContentType)(frame.Length >= 1 ? frame[0] : 0);
177+
if (frame.Length < TlsFrameHelper.HeaderSize)
179178
{
180-
header.Type = (TlsContentType)frame[0];
181-
182-
if (frame.Length >= 3)
183-
{
184-
// SSLv3, TLS or later
185-
if (frame[1] == 3)
186-
{
187-
if (frame.Length > 4)
188-
{
189-
header.Length = ((frame[3] << 8) | frame[4]);
190-
}
179+
header.Length = -1;
180+
header.Version = SslProtocols.None;
181+
return false;
182+
}
191183

192-
header.Version = TlsMinorVersionToProtocol(frame[2]);
193-
}
194-
else
195-
{
196-
header.Length = -1;
197-
header.Version = SslProtocols.None;
198-
}
199-
}
184+
// SSLv3, TLS or later
185+
if (frame[1] == ProtocolVersionTlsMajorValue)
186+
{
187+
header.Length = ((frame[3] << 8) | frame[4]);
188+
header.Version = TlsMinorVersionToProtocol(frame[2]);
189+
}
190+
// Sslv2 or Unified
191+
else if (frame[2] == (byte)TlsHandshakeType.ClientHello &&
192+
frame[3] == ProtocolVersionTlsMajorValue) // SSL3 or above
193+
{
194+
header.Length = (frame[0] & 0x80) != 0 ?
195+
(((frame[0] & 0x7f) << 8) | frame[1]) + 2 : // two bytes
196+
(((frame[0] & 0x3f) << 8) | frame[1]) + 3; // three bytes
197+
#pragma warning disable CS0618 // Ssl2 and Ssl3 are obsolete
198+
header.Version = SslProtocols.Ssl2;
199+
#pragma warning restore CS0618
200+
header.Type = TlsContentType.Handshake;
201+
}
202+
else
203+
{
204+
// neither looks like TLS nor Ssl2 Hello
205+
header.Length = -1;
206+
header.Version = SslProtocols.None;
207+
return false;
200208
}
201209

202-
return result;
210+
return true;
203211
}
204212

205213
// Returns frame size e.g. header + content
@@ -252,6 +260,18 @@ public static bool TryGetFrameInfo(ReadOnlySpan<byte> frame, ref TlsFrameInfo in
252260
}
253261

254262
info.HandshakeType = (TlsHandshakeType)frame[HandshakeTypeOffset];
263+
#pragma warning disable CS0618 // Ssl2 and Ssl3 are obsolete
264+
if (info.Header.Version == SslProtocols.Ssl2)
265+
{
266+
// This is safe. We would not get here if the length is too small.
267+
info.SupportedVersions |= TlsMinorVersionToProtocol(frame[4]);
268+
// We only recognize Unified ClientHello at the moment.
269+
// This is needed to trigger certificate selection callback in SslStream.
270+
info.HandshakeType = TlsHandshakeType.ClientHello;
271+
// There is no more parsing for old protocols.
272+
return true;
273+
}
274+
#pragma warning restore CS0618
255275

256276
// Check if we have full frame.
257277
bool isComplete = frame.Length >= HeaderSize + info.Header.Length;
@@ -404,10 +424,10 @@ private static bool TryParseClientHello(ReadOnlySpan<byte> clientHello, ref TlsF
404424
// Skip compression methods (max size 2^8-1 => size fits in 1 byte)
405425
p = SkipOpaqueType1(p);
406426

407-
// is invalid structure or no extensions?
427+
// no extensions
408428
if (p.IsEmpty)
409429
{
410-
return false;
430+
return true;
411431
}
412432

413433
// client_hello_extension_list (max size 2^16-1 => size fits in 2 bytes)

0 commit comments

Comments
 (0)