Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -457,16 +457,27 @@ private async ValueTask<int> ReceiveHandshakeFrameAsync<TIOAdapter>(Cancellation
_sslAuthenticationOptions!.IsServer) // guard against malicious endpoints. We should not see ClientHello on client.
#pragma warning restore CS0618
{
TlsFrameHelper.ProcessingOptions options = NetEventSource.Log.IsEnabled() ?
TlsFrameHelper.ProcessingOptions.All :
TlsFrameHelper.ProcessingOptions.ServerName;
TlsFrameHelper.ProcessingOptions options = TlsFrameHelper.ProcessingOptions.ServerName;

if (OperatingSystem.IsMacOS() && _sslAuthenticationOptions.IsServer)
{
// macOS cannot process ALPN on server at the moment.
// We fallback to our own process similar to SNI bellow.
// This will allocate.
options |= TlsFrameHelper.ProcessingOptions.RawApplicationProtocol;
}

if (NetEventSource.Log.IsEnabled())
{
options |= TlsFrameHelper.ProcessingOptions.ApplicationProtocol | TlsFrameHelper.ProcessingOptions.Versions;
}

if (_sslAuthenticationOptions.ServerOptionDelegate != null)
{
// We need to process supported versions extension to pass it to user callback.
options |= TlsFrameHelper.ProcessingOptions.Versions;
Comment thread
wfurt marked this conversation as resolved.
}

// Process SNI from Client Hello message
if (!TlsFrameHelper.TryGetFrameInfo(_buffer.EncryptedReadOnlySpan, ref _lastFrame, options))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ internal static class TlsFrameHelper
[Flags]
public enum ProcessingOptions
{
All = 0,
Comment thread
rzikm marked this conversation as resolved.
ServerName = 0x1,
ApplicationProtocol = 0x2,
Versions = 0x4,
Expand Down Expand Up @@ -224,7 +223,7 @@ public static bool TryGetFrameHeader(ReadOnlySpan<byte> frame, ref TlsFrameHeade
// It is OK to call it again if more data becomes available.
// It is also possible to limit what information is processed.
// If callback delegate is provided, it will be called on ALL extensions.
public static bool TryGetFrameInfo(ReadOnlySpan<byte> frame, ref TlsFrameInfo info, ProcessingOptions options = ProcessingOptions.All, HelloExtensionCallback? callback = null)
public static bool TryGetFrameInfo(ReadOnlySpan<byte> frame, ref TlsFrameInfo info, ProcessingOptions options = ProcessingOptions.ServerName, HelloExtensionCallback? callback = null)
{
const int HandshakeTypeOffset = 5;
if (frame.Length < HeaderSize)
Expand Down Expand Up @@ -515,8 +514,7 @@ private static bool TryParseHelloExtensions(ReadOnlySpan<byte> extensions, ref T

ReadOnlySpan<byte> extensionData = extensions.Slice(0, extensionLength);

if (extensionType == ExtensionType.ServerName && (options == ProcessingOptions.All ||
(options & ProcessingOptions.ServerName) == ProcessingOptions.ServerName))
if (extensionType == ExtensionType.ServerName && (options & ProcessingOptions.ServerName) != 0)
{
if (!TryGetSniFromServerNameList(extensionData, out string? sni))
{
Expand All @@ -525,8 +523,7 @@ private static bool TryParseHelloExtensions(ReadOnlySpan<byte> extensions, ref T

info.TargetName = sni!;
}
else if (extensionType == ExtensionType.SupportedVersions && (options == ProcessingOptions.All ||
(options & ProcessingOptions.Versions) == ProcessingOptions.Versions))
else if (extensionType == ExtensionType.SupportedVersions && (options & ProcessingOptions.Versions) != 0)
{
if (!TryGetSupportedVersionsFromExtension(extensionData, out SslProtocols versions))
{
Expand All @@ -535,8 +532,8 @@ private static bool TryParseHelloExtensions(ReadOnlySpan<byte> extensions, ref T

info.SupportedVersions |= versions;
}
else if (extensionType == ExtensionType.ApplicationProtocols && (options == ProcessingOptions.All ||
(options.HasFlag(ProcessingOptions.ApplicationProtocol) || options.HasFlag(ProcessingOptions.RawApplicationProtocol))))
else if (extensionType == ExtensionType.ApplicationProtocols &&
(options & (ProcessingOptions.ApplicationProtocol | ProcessingOptions.RawApplicationProtocol)) != 0)
{
if (!TryGetApplicationProtocolsFromExtension(extensionData, out ApplicationProtocolInfo alpn))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,44 @@ public async Task ServerAsyncAuthenticate_SniSetVersion_Success(SslProtocols ver
}
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.SupportsTls13))]
public async Task ServerAsyncAuthenticate_SniCallback_ReceivesSupportedVersionsFromExtension()
{
// Regression test: ensure that when a ServerOptionDelegate (SNI callback) is used,
// the supported_versions extension from the ClientHello is parsed and exposed via
// SslClientHelloInfo.SslProtocols. TLS 1.3 advertises its version only via that
// extension (the record-layer / legacy_version field still reports TLS 1.2), so
// without parsing the extension the callback would not see Tls13.
var serverOptions = new SslServerAuthenticationOptions() { ServerCertificate = _serverCertificate, EnabledSslProtocols = SslProtocols.Tls13 };
var clientOptions = new SslClientAuthenticationOptions()
{
TargetHost = _serverCertificate.GetNameInfo(X509NameType.SimpleName, forIssuer: false),
EnabledSslProtocols = SslProtocols.Tls13,
};
clientOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;

SslProtocols observedProtocols = SslProtocols.None;

(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
using (client)
using (server)
{
Task t1 = client.AuthenticateAsClientAsync(clientOptions, CancellationToken.None);
Task t2 = server.AuthenticateAsServerAsync(
(stream, clientHelloInfo, userState, cancellationToken) =>
{
observedProtocols = clientHelloInfo.SslProtocols;
return new ValueTask<SslServerAuthenticationOptions>(serverOptions);
},
null, CancellationToken.None);

await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2);
}

Assert.True((observedProtocols & SslProtocols.Tls13) == SslProtocols.Tls13,
$"Expected SslClientHelloInfo.SslProtocols to include Tls13, got '{observedProtocols}'.");
Comment thread
wfurt marked this conversation as resolved.
}

private async Task<SslServerAuthenticationOptions> FailedTask()
{
await Task.Yield();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,16 @@ private void InvalidClientHello(byte[] clientHello, int id, bool shouldPass)
Assert.Null(ret);
}

private const TlsFrameHelper.ProcessingOptions AllExtensions =
TlsFrameHelper.ProcessingOptions.ServerName |
TlsFrameHelper.ProcessingOptions.ApplicationProtocol |
TlsFrameHelper.ProcessingOptions.Versions;

[Fact]
public void TlsFrameHelper_ValidData_Ok()
{
TlsFrameHelper.TlsFrameInfo info = default;
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_validClientHello, ref info));
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_validClientHello, ref info, AllExtensions));

Assert.Equal(SslProtocols.Tls12, info.Header.Version);
Assert.Equal(208, info.Header.Length);
Expand All @@ -55,7 +60,7 @@ public void TlsFrameHelper_ValidData_Ok()
public void TlsFrameHelper_Tls12ClientHello_Ok()
{
TlsFrameHelper.TlsFrameInfo info = default;
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_Tls12ClientHello, ref info));
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_Tls12ClientHello, ref info, AllExtensions));

#pragma warning disable SYSLIB0039
Assert.Equal(SslProtocols.Tls, info.Header.Version);
Expand All @@ -68,7 +73,7 @@ public void TlsFrameHelper_Tls12ClientHello_Ok()
public void TlsFrameHelper_Tls13ClientHello_Ok()
{
TlsFrameHelper.TlsFrameInfo info = default;
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_Tls13ClientHello, ref info));
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_Tls13ClientHello, ref info, AllExtensions));

#pragma warning disable SYSLIB0039
Assert.Equal(SslProtocols.Tls, info.Header.Version);
Expand All @@ -81,7 +86,7 @@ public void TlsFrameHelper_Tls13ClientHello_Ok()
public void TlsFrameHelper_Tls12ServerHello_Ok()
{
TlsFrameHelper.TlsFrameInfo info = default;
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_Tls12ServerHello, ref info));
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_Tls12ServerHello, ref info, AllExtensions));

Assert.Equal(SslProtocols.Tls12, info.Header.Version);
Assert.Equal(SslProtocols.Tls12, info.SupportedVersions);
Expand All @@ -95,7 +100,7 @@ public void TlsFrameHelper_UnifiedClientHello_Ok()
Assert.True(TlsFrameHelper.TryGetFrameHeader(s_UnifiedHello, ref info.Header));
Assert.Equal(75, info.Header.Length);

Assert.True(TlsFrameHelper.TryGetFrameInfo(s_UnifiedHello, ref info));
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_UnifiedHello, ref info, AllExtensions));
#pragma warning disable CS0618 // Ssl2 and Ssl3 are obsolete
#pragma warning disable SYSLIB0039 // Tls is obsolete
Assert.Equal(SslProtocols.Ssl2, info.Header.Version);
Expand All @@ -111,7 +116,7 @@ public void TlsFrameHelper_UnifiedClientHello_Ok()
public void TlsFrameHelper_TlsClientHelloNoExtensions_Ok()
{
TlsFrameHelper.TlsFrameInfo info = default;
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_TlsClientHelloNoExtensions, ref info));
Assert.True(TlsFrameHelper.TryGetFrameInfo(s_TlsClientHelloNoExtensions, ref info, AllExtensions));
Assert.Equal(SslProtocols.Tls12, info.Header.Version);
Assert.Equal(SslProtocols.Tls12, info.SupportedVersions);
Assert.Equal(TlsContentType.Handshake, info.Header.Type);
Expand Down
Loading