From ba45dadc2c7d9cee5eeeed84d01ab353445cad38 Mon Sep 17 00:00:00 2001 From: wfurt Date: Mon, 27 Apr 2020 17:05:18 -0700 Subject: [PATCH 1/9] improve handling of handshake failure --- .../src/Resources/Strings.resx | 3 + .../src/System.Net.Security.csproj | 2 + .../src/System/Net/Security/SecureChannel.cs | 12 +- .../src/System/Net/Security/SniHelper.cs | 18 +- .../Net/Security/SslStream.Implementation.cs | 312 +++--------------- .../src/System/Net/Security/TlsFrameHelper.cs | 241 ++++++++++++++ .../ClientAsyncAuthenticateTest.cs | 6 +- .../ServerAsyncAuthenticateTest.cs | 6 +- .../tests/FunctionalTests/SslStreamSniTest.cs | 5 +- .../System.Net.Security.Tests.csproj | 2 + .../FunctionalTests/TestConfiguration.cs | 2 - .../src/System.Net.Sockets.csproj | 1 + 12 files changed, 318 insertions(+), 292 deletions(-) create mode 100644 src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs diff --git a/src/libraries/System.Net.Security/src/Resources/Strings.resx b/src/libraries/System.Net.Security/src/Resources/Strings.resx index 7270ea80faefd..e3ca7031be22a 100644 --- a/src/libraries/System.Net.Security/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Security/src/Resources/Strings.resx @@ -212,6 +212,9 @@ Authentication failed because the remote party has closed the transport stream. + + Authentication failed because the remote party has sent TLS alert {0}. + Authentication failed on the remote side (the stream might still be available for additional authentication attempts). diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index 868faddb5727a..f3ae437fcddcb 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -34,6 +34,7 @@ + @@ -360,6 +361,7 @@ + diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs index 82aab9fe95196..1fcf2dc28d51a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs @@ -625,7 +625,7 @@ private bool AcquireClientCredentials(ref byte[]? thumbPrint) // // Acquire Server Side Certificate information and set it on the class. // - private bool AcquireServerCredentials(ref byte[]? thumbPrint, ReadOnlySpan clientHello) + private bool AcquireServerCredentials(ref byte[]? thumbPrint) { if (NetEventSource.IsEnabled) NetEventSource.Enter(this); @@ -639,20 +639,20 @@ private bool AcquireServerCredentials(ref byte[]? thumbPrint, ReadOnlySpan // with .NET Framework), and if neither is set we fall back to using ServerCertificate. if (_sslAuthenticationOptions.ServerCertSelectionDelegate != null) { - string? serverIdentity = SniHelper.GetServerName(clientHello); - localCertificate = _sslAuthenticationOptions.ServerCertSelectionDelegate(serverIdentity); - + localCertificate = _sslAuthenticationOptions.ServerCertSelectionDelegate(_sslAuthenticationOptions.TargetHost); if (localCertificate == null) { throw new AuthenticationException(SR.net_ssl_io_no_server_cert); } + if (NetEventSource.IsEnabled) + NetEventSource.Info(this, "Use delegate selected Cert"); } else if (_sslAuthenticationOptions.CertSelectionDelegate != null) { X509CertificateCollection tempCollection = new X509CertificateCollection(); tempCollection.Add(_sslAuthenticationOptions.ServerCertificate!); // We pass string.Empty here to maintain strict compatability with .NET Framework. - localCertificate = _sslAuthenticationOptions.CertSelectionDelegate(string.Empty, tempCollection, null, Array.Empty()); + localCertificate = _sslAuthenticationOptions.CertSelectionDelegate(_sslAuthenticationOptions.TargetHost ?? string.Empty, tempCollection, null, Array.Empty()); if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Use delegate selected Cert"); } @@ -784,7 +784,7 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte if (_refreshCredentialNeeded) { cachedCreds = _sslAuthenticationOptions.IsServer - ? AcquireServerCredentials(ref thumbPrint, inputBuffer) + ? AcquireServerCredentials(ref thumbPrint) : AcquireClientCredentials(ref thumbPrint); } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SniHelper.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SniHelper.cs index ffbf730e7d971..db682188997e1 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SniHelper.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SniHelper.cs @@ -26,13 +26,13 @@ internal class SniHelper // opaque fragment[SSLPlaintext.length]; // } SSLPlaintext; const int ContentTypeOffset = 0; - const int ProtocolVersionOffset = ContentTypeOffset + sizeof(ContentType); + const int ProtocolVersionOffset = ContentTypeOffset + sizeof(TlsContentType); const int LengthOffset = ProtocolVersionOffset + ProtocolVersionSize; const int HandshakeOffset = LengthOffset + sizeof(ushort); // SSL v2's ContentType has 0x80 bit set. // We do not care about SSL v2 here because it does not support client hello extensions - if (sslPlainText.Length < HandshakeOffset || (ContentType)sslPlainText[ContentTypeOffset] != ContentType.Handshake) + if (sslPlainText.Length < HandshakeOffset || (TlsContentType)sslPlainText[ContentTypeOffset] != TlsContentType.Handshake) { return null; } @@ -62,10 +62,10 @@ internal class SniHelper // } body; // } Handshake; const int HandshakeTypeOffset = 0; - const int ClientHelloLengthOffset = HandshakeTypeOffset + sizeof(HandshakeType); + const int ClientHelloLengthOffset = HandshakeTypeOffset + sizeof(TlsHandshakeType); const int ClientHelloOffset = ClientHelloLengthOffset + UInt24Size; - if (sslHandshake.Length < ClientHelloOffset || (HandshakeType)sslHandshake[HandshakeTypeOffset] != HandshakeType.ClientHello) + if (sslHandshake.Length < ClientHelloOffset || (TlsHandshakeType)sslHandshake[HandshakeTypeOffset] != TlsHandshakeType.ClientHello) { return null; } @@ -363,16 +363,6 @@ private static Encoding CreateEncoding() return Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback()); } - private enum ContentType : byte - { - Handshake = 0x16 - } - - private enum HandshakeType : byte - { - ClientHello = 0x01 - } - private enum ExtensionType : ushort { ServerName = 0x00 diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs index 600163be36290..2fec66f8af711 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs @@ -22,26 +22,8 @@ public partial class SslStream private int _nestedAuth; - private enum Framing - { - Unknown = 0, - BeforeSSL3, - SinceSSL3, - Unified, - Invalid - } - - // This is set on the first packet to figure out the framing style. - private Framing _framing = Framing.Unknown; - - // SSL3/TLS protocol frames definitions. - private enum FrameType : byte - { - ChangeCipherSpec = 20, - Alert = 21, - Handshake = 22, - AppData = 23 - } + private TlsAlertDescription _lastAlertDescription; + private TlsFrameHandshakeInfo _lastFrame; private readonly object _handshakeLock = new object(); private volatile TaskCompletionSource? _handshakeWaiter; @@ -274,7 +256,6 @@ private async Task ForceAuthenticationAsync(TIOAdapter adapter, bool { // get ready to receive first frame _handshakeBuffer = new ArrayBuffer(InitialHandshakeBufferSize); - _framing = Framing.Unknown; } while (!handshakeCompleted) @@ -288,6 +269,19 @@ private async Task ForceAuthenticationAsync(TIOAdapter adapter, bool if (message.Failed) { + if (_lastFrame.Header.Type == TlsContentType.Handshake && message.Size == 0) + { + // If we failed without OS sending out alert, inject one here to be consisnet across platforms. + // Console.WriteLine("Alert fixup for {0}", message.Status.ErrorCode); + byte[] alert = TlsFrameHelper.CreateAlertFrame(_lastFrame.Header.Version, TlsAlertDescription.ProtocolVersion); + await adapter.WriteAsync(alert, 0, alert.Length).ConfigureAwait(false); + } + else if (_lastFrame.Header.Type == TlsContentType.Alert && _lastAlertDescription != TlsAlertDescription.CloseNotify && + message.Status.ErrorCode == SecurityStatusPalErrorCode.IllegalMessage) + { + throw new AuthenticationException(SR.Format(SR.net_auth_tls_alert, _lastAlertDescription.ToString()), message.GetException()); + } + throw new AuthenticationException(SR.net_auth_SSPI, message.GetException()); } else if (message.Status.ErrorCode == SecurityStatusPalErrorCode.OK) @@ -341,22 +335,40 @@ private async ValueTask ReceiveBlobAsync(TIOAdapter a throw new IOException(SR.net_io_eof); } - if (_framing == Framing.Unified || _framing == Framing.Unknown) - { - _framing = DetectFraming(_handshakeBuffer.ActiveReadOnlySpan); - } + TlsFrameHelper.TryGetFrameHeader(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame.Header); - int frameSize = GetFrameSize(_handshakeBuffer.ActiveReadOnlySpan); - if (frameSize < 0) + if (_lastFrame.Header.Length < 0) { throw new IOException(SR.net_frame_read_size); } + // Header length is content only so we must add header size as well. + int frameSize = _lastFrame.Header.Length + TlsFrameHelper.HeaderSize; if (_handshakeBuffer.ActiveLength < frameSize) { await FillHandshakeBufferAsync(adapter, frameSize).ConfigureAwait(false); } + // At this point, we have at least one TLS frame. + if (_lastFrame.Header.Type == TlsContentType.Alert) + { + TlsAlertLevel level = 0; + if (TlsFrameHelper.TryGetAlertInfo(_handshakeBuffer.ActiveReadOnlySpan, ref level, ref _lastAlertDescription)) + { + if (NetEventSource.IsEnabled && _lastAlertDescription != TlsAlertDescription.CloseNotify) NetEventSource.Fail(this, $"Received TLS alert {_lastAlertDescription}"); + } + } + else if (_lastFrame.Header.Type == TlsContentType.Handshake) + { + TlsFrameHelper.TryGetHandshakeInfo(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame); + + if (_lastFrame.HandshakeType == TlsHandshakeType.ClientHello && + _sslAuthenticationOptions!.ServerCertSelectionDelegate != null) + { + // Process SNI from Client Hello message + _sslAuthenticationOptions.TargetHost = _lastFrame.TargetName; + } + } return ProcessBlob(frameSize); } @@ -372,23 +384,24 @@ private ProtocolToken ProcessBlob(int frameSize) _handshakeBuffer.Discard(frameSize); // Often more TLS messages fit into same packet. Get as many complete frames as we can. - while (_handshakeBuffer.ActiveLength > SecureChannel.ReadHeaderSize) + while (_handshakeBuffer.ActiveLength > TlsFrameHelper.HeaderSize) { - ReadOnlySpan remainingData = _handshakeBuffer.ActiveReadOnlySpan; - if (remainingData[0] >= (int)FrameType.AppData) + TlsFrameHeader nextHeader = default; + + if (!TlsFrameHelper.TryGetFrameHeader(_handshakeBuffer.ActiveReadOnlySpan, ref nextHeader)) { break; } - frameSize = GetFrameSize(remainingData); - if (_handshakeBuffer.ActiveLength >= frameSize) + frameSize = nextHeader.Length + TlsFrameHelper.HeaderSize; + if (nextHeader.Type == TlsContentType.AppData || frameSize > _handshakeBuffer.ActiveLength) { - chunkSize += frameSize; - _handshakeBuffer.Discard(frameSize); - continue; + // We don't have full frame left or we already have app data which needs to be processed by decrypt. + break; } - break; + chunkSize += frameSize; + _handshakeBuffer.Discard(frameSize); } return _context!.NextMessage(availableData.Slice(0, chunkSize)); @@ -645,7 +658,7 @@ private async ValueTask ReadAsyncInternal(TIOAdapter adapter, M Debug.Assert(_internalBufferCount >= SecureChannel.ReadHeaderSize); // Parse the frame header to determine the payload size (which includes the header size). - int payloadBytes = GetFrameSize(_internalBuffer.AsSpan(_internalOffset)); + int payloadBytes = TlsFrameHelper.GetFrameSize(_internalBuffer.AsSpan(_internalOffset)); if (payloadBytes < 0) { throw new IOException(SR.net_frame_read_size); @@ -913,231 +926,8 @@ private static byte[] EnsureBufferSize(byte[] buffer, int copyCount, int size) Buffer.BlockCopy(saved, 0, buffer, 0, copyCount); } } - return buffer; - } - - // We need at least 5 bytes to determine what we have. - private Framing DetectFraming(ReadOnlySpan bytes) - { - /* PCTv1.0 Hello starts with - * RECORD_LENGTH_MSB (ignore) - * RECORD_LENGTH_LSB (ignore) - * PCT1_CLIENT_HELLO (must be equal) - * PCT1_CLIENT_VERSION_MSB (if version greater than PCTv1) - * PCT1_CLIENT_VERSION_LSB (if version greater than PCTv1) - * - * ... PCT hello ... - */ - - /* Microsoft Unihello starts with - * RECORD_LENGTH_MSB (ignore) - * RECORD_LENGTH_LSB (ignore) - * SSL2_CLIENT_HELLO (must be equal) - * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3) - * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3) - * - * ... SSLv2 Compatible Hello ... - */ - - /* SSLv2 CLIENT_HELLO starts with - * RECORD_LENGTH_MSB (ignore) - * RECORD_LENGTH_LSB (ignore) - * SSL2_CLIENT_HELLO (must be equal) - * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3) - * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3) - * - * ... SSLv2 CLIENT_HELLO ... - */ - - /* SSLv2 SERVER_HELLO starts with - * RECORD_LENGTH_MSB (ignore) - * RECORD_LENGTH_LSB (ignore) - * SSL2_SERVER_HELLO (must be equal) - * SSL2_SESSION_ID_HIT (ignore) - * SSL2_CERTIFICATE_TYPE (ignore) - * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3) - * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3) - * - * ... SSLv2 SERVER_HELLO ... - */ - - /* SSLv3 Type 2 Hello starts with - * RECORD_LENGTH_MSB (ignore) - * RECORD_LENGTH_LSB (ignore) - * SSL2_CLIENT_HELLO (must be equal) - * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv3) - * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv3) - * - * ... SSLv2 Compatible Hello ... - */ - - /* SSLv3 Type 3 Hello starts with - * 22 (HANDSHAKE MESSAGE) - * VERSION MSB - * VERSION LSB - * RECORD_LENGTH_MSB (ignore) - * RECORD_LENGTH_LSB (ignore) - * HS TYPE (CLIENT_HELLO) - * 3 bytes HS record length - * HS Version - * HS Version - */ - - /* SSLv2 message codes - * SSL_MT_ERROR 0 - * SSL_MT_CLIENT_HELLO 1 - * SSL_MT_CLIENT_MASTER_KEY 2 - * SSL_MT_CLIENT_FINISHED 3 - * SSL_MT_SERVER_HELLO 4 - * SSL_MT_SERVER_VERIFY 5 - * SSL_MT_SERVER_FINISHED 6 - * SSL_MT_REQUEST_CERTIFICATE 7 - * SSL_MT_CLIENT_CERTIFICATE 8 - */ - - int version = -1; - - if (bytes.Length == 0) - { - NetEventSource.Fail(this, "Header buffer is not allocated."); - } - - // If the first byte is SSL3 HandShake, then check if we have a SSLv3 Type3 client hello. - if (bytes[0] == (byte)FrameType.Handshake || bytes[0] == (byte)FrameType.AppData - || bytes[0] == (byte)FrameType.Alert) - { - if (bytes.Length < 3) - { - return Framing.Invalid; - } - -#if TRACE_VERBOSE - if (bytes[1] != 3 && NetEventSource.IsEnabled) - { - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"WARNING: SslState::DetectFraming() SSL protocol is > 3, trying SSL3 framing in retail = {bytes[1]:x}"); - } -#endif - - version = (bytes[1] << 8) | bytes[2]; - if (version < 0x300 || version >= 0x500) - { - return Framing.Invalid; - } - - // - // This is an SSL3 Framing - // - return Framing.SinceSSL3; - } -#if TRACE_VERBOSE - if ((bytes[0] & 0x80) == 0 && NetEventSource.IsEnabled) - { - // We have a three-byte header format - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"WARNING: SslState::DetectFraming() SSL v <=2 HELLO has no high bit set for 3 bytes header, we are broken, received byte = {bytes[0]:x}"); - } -#endif - - if (bytes.Length < 3) - { - return Framing.Invalid; - } - - if (bytes[2] > 8) - { - return Framing.Invalid; - } - - if (bytes[2] == 0x1) // SSL_MT_CLIENT_HELLO - { - if (bytes.Length >= 5) - { - version = (bytes[3] << 8) | bytes[4]; - } - } - else if (bytes[2] == 0x4) // SSL_MT_SERVER_HELLO - { - if (bytes.Length >= 7) - { - version = (bytes[5] << 8) | bytes[6]; - } - } - - if (version != -1) - { - // If this is the first packet, the client may start with an SSL2 packet - // but stating that the version is 3.x, so check the full range. - // For the subsequent packets we assume that an SSL2 packet should have a 2.x version. - if (_framing == Framing.Unknown) - { - if (version != 0x0002 && (version < 0x200 || version >= 0x500)) - { - return Framing.Invalid; - } - } - else - { - if (version != 0x0002) - { - return Framing.Invalid; - } - } - } - - // When server has replied the framing is already fixed depending on the prior client packet - if (!_context!.IsServer || _framing == Framing.Unified) - { - return Framing.BeforeSSL3; - } - - return Framing.Unified; // Will use Ssl2 just for this frame. - } - - // Returns TLS Frame size. - private int GetFrameSize(ReadOnlySpan buffer) - { - if (NetEventSource.IsEnabled) - NetEventSource.Enter(this, buffer.Length); - - int payloadSize = -1; - switch (_framing) - { - case Framing.Unified: - case Framing.BeforeSSL3: - if (buffer.Length < 2) - { - throw new IOException(SR.net_ssl_io_frame); - } - // Note: Cannot detect version mismatch for <= SSL2 - - if ((buffer[0] & 0x80) != 0) - { - // Two bytes - payloadSize = (((buffer[0] & 0x7f) << 8) | buffer[1]) + 2; - } - else - { - // Three bytes - payloadSize = (((buffer[0] & 0x3f) << 8) | buffer[1]) + 3; - } - - break; - case Framing.SinceSSL3: - if (buffer.Length < 5) - { - throw new IOException(SR.net_ssl_io_frame); - } - - payloadSize = ((buffer[3] << 8) | buffer[4]) + 5; - break; - default: - break; - } - - if (NetEventSource.IsEnabled) - NetEventSource.Exit(this, payloadSize); - - return payloadSize; + return buffer; } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs new file mode 100644 index 0000000000000..4a4d036b8b50f --- /dev/null +++ b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs @@ -0,0 +1,241 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Security.Authentication; + +namespace System.Net.Security +{ + // SSL3/TLS protocol frames definitions. + internal enum TlsContentType : byte + { + ChangeCipherSpec = 20, + Alert = 21, + Handshake = 22, + AppData = 23 + } + + internal enum TlsHandshakeType : byte + { + HelloRequest = 0, + ClientHello = 1, + ServerHello = 2, + NewSessionTicket = 4, + EndOfEarlyData = 5, + EncryptedExtensions = 8, + Certificate = 11, + ServerKeyExchange = 12, + CertificateRequest = 13, + ServerHelloDone = 14, + CertificateVerify = 15, + ClientKeyExchange = 16, + Finished = 20, + KeyEpdate = 24, + MessageHash = 254 + } + + internal enum TlsAlertLevel : byte + { + Warning = 1, + Fatal = 2, + } + + internal enum TlsAlertDescription : byte + { + CloseNotify = 0, // warning + UnexpectedMessage = 10, // error + BadRecordMac = 20, // error + DecryptionFailed = 21, // reserved + RecordOverflow = 22, // error + DecompressionFail = 30, // error + HandshakeFailure = 40, // error + BadCertificate = 42, // warning or error + UnsupportedCert = 43, // warning or error + CertificateRevoked = 44, // warning or error + CertificateExpired = 45, // warning or error + CertificateUnknown = 46, // warning or error + IllegalParameter = 47, // error + UnknownCA = 48, // error + AccessDenied = 49, // error + DecodeError = 50, // error + DecryptError = 51, // error + ExportRestriction = 60, // reserved + ProtocolVersion = 70, // error + InsuffientSecurity = 71, // error + InternalError = 80, // error + UserCanceled = 90, // warning or error + NoRenegotiation = 100, // warning + UnsupportedExt = 110, // error + } + + internal struct TlsFrameHeader + { + public TlsContentType Type; + public SslProtocols Version; + public int Length; + } + + internal struct TlsFrameHandshakeInfo + { + public TlsFrameHeader Header; + public TlsHandshakeType HandshakeType; + public SslProtocols SupportedVersions; + public string? TargetName; + } + + internal class TlsFrameHelper + { + public const int HeaderSize = 5; + + private static byte[] _protocolMismatch13 = new byte[] { (byte)TlsContentType.Alert, 3, 4, 0, 2, 2, 70 }; + private static byte[] _protocolMismatch12 = new byte[] { (byte)TlsContentType.Alert, 3, 3, 0, 2, 2, 70 }; + private static byte[] _protocolMismatch11 = new byte[] { (byte)TlsContentType.Alert, 3, 2, 0, 2, 2, 70 }; + private static byte[] _protocolMismatch10 = new byte[] { (byte)TlsContentType.Alert, 3, 1, 0, 2, 2, 70 }; + + public static bool TryGetFrameHeader(ReadOnlySpan frame, ref TlsFrameHeader header) + { + bool result = frame.Length > 4; + + if (frame.Length >= 1) + { + header.Type = (TlsContentType)frame[0]; + + if (frame.Length >= 3) + { + // SSLv3, TLS or later + if (frame[1] == 3) + { + if (frame.Length > 4) + { + header.Length = ((frame[3] << 8) | frame[4]); + } + + switch (frame[2]) + { + case 4: + header.Version = SslProtocols.Tls13; + break; + case 3: + header.Version = SslProtocols.Tls12; + break; + case 2: + header.Version = SslProtocols.Tls11; + break; + case 1: + header.Version = SslProtocols.Tls; + break; + case 0: +#pragma warning disable 0618 + header.Version = SslProtocols.Ssl3; +#pragma warning restore 0618 + break; + default: + header.Version = SslProtocols.None; + break; + } + } + else + { + header.Length = -1; + header.Version = SslProtocols.None; + } + } + } + + return result; + } + + // Returns frame size e.g. header + content + public static int GetFrameSize(ReadOnlySpan frame) + { + if (frame.Length < 5 || frame[1] < 3) + { + return - 1; + } + + return ((frame[3] << 8) | frame[4]) + 5; + } + + public static bool TryGetHandshakeInfo(ReadOnlySpan frame, ref TlsFrameHandshakeInfo info) + { + if (frame.Length < 6 || frame[0] != (byte)TlsContentType.Handshake) + { + return false; + } + + // info = default; + + // This will not fail since we have enough data. + TryGetFrameHeader(frame, ref info.Header); + info.SupportedVersions = info.Header.Version; + + info.HandshakeType = (TlsHandshakeType)frame[5]; + + if (info.HandshakeType == TlsHandshakeType.ClientHello) + { + info.TargetName = SniHelper.GetServerName(frame); + } + + return true; + } + + public static bool TryGetAlertInfo(ReadOnlySpan frame, ref TlsAlertLevel level, ref TlsAlertDescription description) + { + if (frame.Length < 7 || frame[0] != (byte)TlsContentType.Alert) + { + return false; + } + + level = (TlsAlertLevel)frame[5]; + description = (TlsAlertDescription)frame[6]; + + return true; + } + + private static byte[] CreateProtocolVersionAlert(SslProtocols version) + { + switch (version) + { + case SslProtocols.Tls13: + return _protocolMismatch13; + case SslProtocols.Tls12: + return _protocolMismatch12; + case SslProtocols.Tls11: + return _protocolMismatch11; + case SslProtocols.Tls: + return _protocolMismatch10; + default: + return Array.Empty(); + } + } + + public static byte[] CreateAlertFrame(SslProtocols version, TlsAlertDescription reason) + { + if (reason == TlsAlertDescription.ProtocolVersion) + { + return CreateProtocolVersionAlert(version); + } + else if ((int)version > (int)SslProtocols.Tls) + { + // Create TLS1.2 alert + byte[] buffer = new byte[] { (byte)TlsContentType.Alert, 3, 3, 0, 2, 2, (byte)reason }; + switch (version) + { + case SslProtocols.Tls13: + buffer[2] = 4; + break; + case SslProtocols.Tls11: + buffer[2] = 2; + break; + case SslProtocols.Tls: + buffer[2] = 1; + break; + } + + return buffer; + } + + return Array.Empty(); + } + } +} diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs index 69c96735809da..302c100b362f4 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs @@ -120,12 +120,12 @@ public static IEnumerable ProtocolMismatchData() yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls12, typeof(Exception) }; yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls12, typeof(Exception) }; #pragma warning restore 0618 - yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(IOException) }; - yield return new object[] { SslProtocols.Tls, SslProtocols.Tls12, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(IOException) }; + yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(AuthenticationException) }; + yield return new object[] { SslProtocols.Tls, SslProtocols.Tls12, typeof(AuthenticationException) }; yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls, typeof(AuthenticationException) }; yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls, typeof(AuthenticationException) }; yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls11, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(IOException) }; + yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, typeof(AuthenticationException) }; } #region Helpers diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs index 5ecb21d630a9a..74fcd970861c3 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs @@ -80,10 +80,10 @@ public static IEnumerable ProtocolMismatchData() #pragma warning restore 0618 yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(AuthenticationException) }; yield return new object[] { SslProtocols.Tls, SslProtocols.Tls12, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(TimeoutException) }; + yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls, typeof(AuthenticationException) }; yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(TimeoutException) }; - yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls11, TestConfiguration.SupportsVersionAlerts ? typeof(AuthenticationException) : typeof(TimeoutException) }; + yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls, typeof(AuthenticationException) }; + yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls11, typeof(AuthenticationException) }; } #region Helpers diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs index a54624100da2a..b98a9ea992bde 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.IO; using System.Net.Test.Common; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; @@ -201,9 +202,7 @@ private static SslServerAuthenticationOptions DefaultServerOptions() private async Task WithVirtualConnection(Func serverClientConnection, RemoteCertificateValidationCallback clientCertValidate) { - VirtualNetwork vn = new VirtualNetwork(); - using (VirtualNetworkStream serverStream = new VirtualNetworkStream(vn, isServer: true), - clientStream = new VirtualNetworkStream(vn, isServer: false)) + (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams(); using (SslStream server = new SslStream(serverStream, leaveInnerStreamOpen: false), client = new SslStream(clientStream, leaveInnerStreamOpen: false, clientCertValidate)) { diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index ef6656e25ae68..5d6c539aedfe3 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -68,6 +68,8 @@ + diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs index 9769590181342..e903bed34cd7d 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs @@ -34,8 +34,6 @@ internal static class TestConfiguration public static bool SupportsAlpnAlerts { get { return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && PlatformDetection.OpenSslVersion.CompareTo(new Version(1,1,0)) >= 0); } } - public static bool SupportsVersionAlerts { get { return RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && PlatformDetection.OpenSslVersion.CompareTo(new Version(1,1,0)) >= 0; } } - public static Task WhenAllOrAnyFailedWithTimeout(params Task[] tasks) => tasks.WhenAllOrAnyFailed(PassingTestTimeoutMilliseconds); diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index 6c3c50e660b1f..ba96b3ad1c342 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -311,5 +311,6 @@ + From 80df4d7a59cad74800d8d8d9be899a1d4f182585 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Mon, 27 Apr 2020 17:54:21 -0700 Subject: [PATCH 2/9] more cleanup --- .../src/System/Net/Security/SecureChannel.cs | 2 +- .../System/Net/Security/SslStream.Implementation.cs | 10 ++++------ .../src/System/Net/Security/SslStreamPal.OSX.cs | 3 +-- .../src/System/Net/Security/TlsFrameHelper.cs | 6 ++---- .../FunctionalTests/ClientAsyncAuthenticateTest.cs | 6 +----- .../FunctionalTests/ClientDefaultEncryptionTest.cs | 2 +- .../tests/FunctionalTests/ServerNoEncryptionTest.cs | 2 +- .../System.Net.Sockets/src/System.Net.Sockets.csproj | 1 - 8 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs index 1fcf2dc28d51a..b770ef26fb78a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs @@ -652,7 +652,7 @@ private bool AcquireServerCredentials(ref byte[]? thumbPrint) X509CertificateCollection tempCollection = new X509CertificateCollection(); tempCollection.Add(_sslAuthenticationOptions.ServerCertificate!); // We pass string.Empty here to maintain strict compatability with .NET Framework. - localCertificate = _sslAuthenticationOptions.CertSelectionDelegate(_sslAuthenticationOptions.TargetHost ?? string.Empty, tempCollection, null, Array.Empty()); + localCertificate = _sslAuthenticationOptions.CertSelectionDelegate(string.Empty, tempCollection, null, Array.Empty()); if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Use delegate selected Cert"); } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs index 2fec66f8af711..62c2513d31a8b 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs @@ -271,14 +271,14 @@ private async Task ForceAuthenticationAsync(TIOAdapter adapter, bool { if (_lastFrame.Header.Type == TlsContentType.Handshake && message.Size == 0) { - // If we failed without OS sending out alert, inject one here to be consisnet across platforms. - // Console.WriteLine("Alert fixup for {0}", message.Status.ErrorCode); + // If we failed without OS sending out alert, inject one here to be consistent across platforms. byte[] alert = TlsFrameHelper.CreateAlertFrame(_lastFrame.Header.Version, TlsAlertDescription.ProtocolVersion); await adapter.WriteAsync(alert, 0, alert.Length).ConfigureAwait(false); } else if (_lastFrame.Header.Type == TlsContentType.Alert && _lastAlertDescription != TlsAlertDescription.CloseNotify && message.Status.ErrorCode == SecurityStatusPalErrorCode.IllegalMessage) { + // Improve generic message and show details if we failed because of TLS Alert. throw new AuthenticationException(SR.Format(SR.net_auth_tls_alert, _lastAlertDescription.ToString()), message.GetException()); } @@ -336,7 +336,6 @@ private async ValueTask ReceiveBlobAsync(TIOAdapter a } TlsFrameHelper.TryGetFrameHeader(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame.Header); - if (_lastFrame.Header.Length < 0) { throw new IOException(SR.net_frame_read_size); @@ -360,12 +359,11 @@ private async ValueTask ReceiveBlobAsync(TIOAdapter a } else if (_lastFrame.Header.Type == TlsContentType.Handshake) { - TlsFrameHelper.TryGetHandshakeInfo(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame); - - if (_lastFrame.HandshakeType == TlsHandshakeType.ClientHello && + if (_handshakeBuffer.ActiveReadOnlySpan[TlsFrameHelper.HeaderSize] == (byte)TlsHandshakeType.ClientHello && _sslAuthenticationOptions!.ServerCertSelectionDelegate != null) { // Process SNI from Client Hello message + TlsFrameHelper.TryGetHandshakeInfo(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame); _sslAuthenticationOptions.TargetHost = _lastFrame.TargetName; } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs index b72e549355fdc..3cf26e783a9d0 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs @@ -237,9 +237,8 @@ private static SecurityStatusPal HandshakeInternal( sslContext = new SafeDeleteSslContext((credential as SafeFreeSslCredentials)!, sslAuthenticationOptions); context = sslContext; - if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost)) + if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) && !sslAuthenticationOptions.IsServer) { - Debug.Assert(!sslAuthenticationOptions.IsServer, "targetName should not be set for server-side handshakes"); Interop.AppleCrypto.SslSetTargetName(sslContext.SslContext, sslAuthenticationOptions.TargetHost); } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs index 4a4d036b8b50f..56f58bb24f30c 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs @@ -6,7 +6,7 @@ namespace System.Net.Security { - // SSL3/TLS protocol frames definitions. + // SSL3/TLS protocol frames definitions. internal enum TlsContentType : byte { ChangeCipherSpec = 20, @@ -153,7 +153,7 @@ public static int GetFrameSize(ReadOnlySpan frame) return - 1; } - return ((frame[3] << 8) | frame[4]) + 5; + return ((frame[3] << 8) | frame[4]) + HeaderSize; } public static bool TryGetHandshakeInfo(ReadOnlySpan frame, ref TlsFrameHandshakeInfo info) @@ -163,8 +163,6 @@ public static bool TryGetHandshakeInfo(ReadOnlySpan frame, ref TlsFrameHan return false; } - // info = default; - // This will not fail since we have enough data. TryGetFrameHeader(frame, ref info.Header); info.SupportedVersions = info.Header.Version; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs index 302c100b362f4..5c227192f2448 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs @@ -44,11 +44,7 @@ public async Task ClientAsyncAuthenticate_ServerRequireEncryption_ConnectWithEnc public async Task ClientAsyncAuthenticate_ServerNoEncryption_NoConnect() { // Don't use Tls13 since we are trying to use NullEncryption - Type expectedExceptionType = TestConfiguration.SupportsHandshakeAlerts && TestConfiguration.SupportsNullEncryption ? - typeof(AuthenticationException) : - typeof(IOException); - - await Assert.ThrowsAsync(expectedExceptionType, + await Assert.ThrowsAsync( () => ClientAsyncSslHelper( EncryptionPolicy.NoEncryption, SslProtocolSupport.DefaultSslProtocols, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 )); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ClientDefaultEncryptionTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ClientDefaultEncryptionTest.cs index 4a57ca9063161..ed3a2c635e692 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ClientDefaultEncryptionTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ClientDefaultEncryptionTest.cs @@ -84,7 +84,7 @@ public async Task ClientDefaultEncryption_ServerNoEncryption_NoConnect() using (var sslStream = new SslStream(client.GetStream(), false, AllowAnyServerCertificate, null)) { - await Assert.ThrowsAsync(TestConfiguration.SupportsHandshakeAlerts ? typeof(AuthenticationException) : typeof(IOException), () => + await Assert.ThrowsAsync(() => sslStream.AuthenticateAsClientAsync("localhost", null, SslProtocolSupport.DefaultSslProtocols, false)); } } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerNoEncryptionTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerNoEncryptionTest.cs index 65efb78767f9c..d062fac791c1d 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerNoEncryptionTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerNoEncryptionTest.cs @@ -43,7 +43,7 @@ public async Task ServerNoEncryption_ClientRequireEncryption_NoConnect() using (var sslStream = new SslStream(client.GetStream(), false, AllowAnyServerCertificate, null, EncryptionPolicy.RequireEncryption)) { - await Assert.ThrowsAsync(TestConfiguration.SupportsHandshakeAlerts ? typeof(AuthenticationException) : typeof(IOException), () => + await Assert.ThrowsAsync(() => sslStream.AuthenticateAsClientAsync("localhost", null, SslProtocolSupport.DefaultSslProtocols, false)); } } diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index ba96b3ad1c342..6c3c50e660b1f 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -311,6 +311,5 @@ - From c610a8aa9be2b9f423effccd8e48ab0b5afab51c Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Mon, 27 Apr 2020 18:06:40 -0700 Subject: [PATCH 3/9] remove console reference --- src/libraries/System.Net.Security/src/System.Net.Security.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index f3ae437fcddcb..067df9b00fcf6 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -361,7 +361,6 @@ - From f3c8e6dbad3228e1aed7a10fd0d9601c3a0ce7f3 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Tue, 28 Apr 2020 10:30:19 -0700 Subject: [PATCH 4/9] add quotes around parameter --- src/libraries/System.Net.Security/src/Resources/Strings.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Security/src/Resources/Strings.resx b/src/libraries/System.Net.Security/src/Resources/Strings.resx index e3ca7031be22a..64fe5cc24bb87 100644 --- a/src/libraries/System.Net.Security/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Security/src/Resources/Strings.resx @@ -213,7 +213,7 @@ Authentication failed because the remote party has closed the transport stream. - Authentication failed because the remote party has sent TLS alert {0}. + Authentication failed because the remote party has sent TLS alert '{0}'. Authentication failed on the remote side (the stream might still be available for additional authentication attempts). From eb6acb2678544bd149c70d6a00a89b8200081e9b Mon Sep 17 00:00:00 2001 From: wfurt Date: Thu, 30 Apr 2020 13:41:02 -0700 Subject: [PATCH 5/9] enable ServerAsyncAuthenticate_MismatchProtocols_Fails --- .../tests/FunctionalTests/ServerAsyncAuthenticateTest.cs | 1 - .../tests/FunctionalTests/TestConfiguration.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs index 74fcd970861c3..9bf0afb5b2290 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs @@ -44,7 +44,6 @@ public async Task ServerAsyncAuthenticate_EachSupportedProtocol_Success(SslProto [Theory] [MemberData(nameof(ProtocolMismatchData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/29642")] public async Task ServerAsyncAuthenticate_MismatchProtocols_Fails( SslProtocols serverProtocol, SslProtocols clientProtocol, diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs index e903bed34cd7d..577d2ce1f58ef 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs @@ -32,7 +32,7 @@ internal static class TestConfiguration public static bool SupportsHandshakeAlerts { get { return RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.Windows); } } - public static bool SupportsAlpnAlerts { get { return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && PlatformDetection.OpenSslVersion.CompareTo(new Version(1,1,0)) >= 0); } } + public static bool SupportsAlpnAlerts { get { return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && PlatformDetection.OpenSslVersion.CompareTo(new Version(1,0,2)) >= 0); } } public static Task WhenAllOrAnyFailedWithTimeout(params Task[] tasks) => tasks.WhenAllOrAnyFailed(PassingTestTimeoutMilliseconds); From db8ffba8daf26d828a1d323d5be086f98e9d5b10 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sun, 3 May 2020 18:46:57 -0700 Subject: [PATCH 6/9] cleanup ssl2 test --- .../ClientAsyncAuthenticateTest.cs | 16 -------------- .../ServerAsyncAuthenticateTest.cs | 2 -- .../SslStreamNegotiatedCipherSuiteTest.cs | 5 ++--- .../SslStreamSystemDefaultsTest.cs | 22 ------------------- 4 files changed, 2 insertions(+), 43 deletions(-) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs index 5c227192f2448..9b6191ae66d36 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs @@ -57,20 +57,6 @@ public async Task ClientAsyncAuthenticate_EachSupportedProtocol_Success(SslProto await ClientAsyncSslHelper(protocol, protocol); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public async Task ClientAsyncAuthenticate_Ssl2WithSelf_Success() - { - // Test Ssl2 against itself. This is a standalone test as even on versions where Windows supports Ssl2, - // it appears to have rules around not using it when other protocols are mentioned. - if (!PlatformDetection.IsWindows10Version1607OrGreater) - { -#pragma warning disable 0618 - await ClientAsyncSslHelper(SslProtocols.Ssl2, SslProtocols.Ssl2); -#pragma warning restore 0618 - } - } - [Theory] [MemberData(nameof(ProtocolMismatchData))] public async Task ClientAsyncAuthenticate_MismatchProtocols_Fails( @@ -112,8 +98,6 @@ public async Task ClientAsyncAuthenticate_IndividualServerVsAllClientSupportedPr public static IEnumerable ProtocolMismatchData() { #pragma warning disable 0618 - yield return new object[] { SslProtocols.Ssl2, SslProtocols.Ssl3, typeof(Exception) }; - yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls12, typeof(Exception) }; yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls12, typeof(Exception) }; #pragma warning restore 0618 yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(AuthenticationException) }; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs index 9bf0afb5b2290..5fa008c87db09 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs @@ -73,8 +73,6 @@ public async Task ServerAsyncAuthenticate_AllClientVsIndividualServerSupportedPr public static IEnumerable ProtocolMismatchData() { #pragma warning disable 0618 - yield return new object[] { SslProtocols.Ssl2, SslProtocols.Ssl3, typeof(Exception) }; - yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls12, typeof(Exception) }; yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls12, typeof(Exception) }; #pragma warning restore 0618 yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(AuthenticationException) }; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNegotiatedCipherSuiteTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNegotiatedCipherSuiteTest.cs index f4921f68ff7fb..3ffe83bc3dd98 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNegotiatedCipherSuiteTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNegotiatedCipherSuiteTest.cs @@ -20,9 +20,8 @@ namespace System.Net.Security.Tests public class NegotiatedCipherSuiteTest { -#pragma warning disable CS0618 // Ssl2 and Ssl3 are obsolete - public const SslProtocols AllProtocols = - SslProtocols.Ssl2 | SslProtocols.Ssl3 | +#pragma warning disable CS0618 // Ssl3 are obsolete + public const SslProtocols AllProtocols = SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13; #pragma warning restore CS0618 diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs index 4943401bc51ee..87b21e109fb06 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs @@ -76,28 +76,6 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( } } - [ConditionalTheory(nameof(IsNotWindows7))] -#pragma warning disable 0618 - [InlineData(null, SslProtocols.Ssl2)] - [InlineData(SslProtocols.None, SslProtocols.Ssl2)] - [InlineData(SslProtocols.Ssl2, null)] - [InlineData(SslProtocols.Ssl2, SslProtocols.None)] -#pragma warning restore 0618 - public async Task ClientAndServer_OneUsesDefault_OtherUsesLowerProtocol_Fails( - SslProtocols? clientProtocols, SslProtocols? serverProtocols) - { - using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate()) - using (X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate()) - { - string serverHost = serverCertificate.GetNameInfo(X509NameType.SimpleName, false); - var clientCertificates = new X509CertificateCollection() { clientCertificate }; - - await Assert.ThrowsAnyAsync(() => TestConfiguration.WhenAllOrAnyFailedWithTimeout( - AuthenticateClientAsync(serverHost, clientCertificates, checkCertificateRevocation: false, protocols: clientProtocols), - AuthenticateServerAsync(serverCertificate, clientCertificateRequired: true, checkCertificateRevocation: false, protocols: serverProtocols))); - } - } - private bool ClientCertCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { switch (sslPolicyErrors) From 364093a6f368fec55fab935f96cd443b0bb84f0f Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Sun, 3 May 2020 20:24:41 -0700 Subject: [PATCH 7/9] fix http2 --- .../System/Net/Http/HttpClientHandlerTest.SslProtocols.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs index b264aa6bc6c5d..6565b851af10c 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs @@ -96,10 +96,6 @@ public static IEnumerable GetAsync_AllowedSSLVersion_Succeeds_MemberDa yield return new object[] { SslProtocols.Ssl3, true }; #endif } - if (PlatformDetection.IsWindows && !PlatformDetection.IsWindows10Version1607OrGreater) - { - yield return new object[] { SslProtocols.Ssl2, true }; - } #pragma warning restore 0618 // These protocols are new, and might not be enabled everywhere yet if (PlatformDetection.IsUbuntu1810OrHigher) From 92459194f208edf25e41fd6caa6ac76cec07af38 Mon Sep 17 00:00:00 2001 From: wfurt Date: Wed, 6 May 2020 23:35:24 -0700 Subject: [PATCH 8/9] feedback from review --- .../HttpClientHandlerTest.SslProtocols.cs | 4 + .../src/Resources/Strings.resx | 2 +- .../Net/Security/SslStream.Implementation.cs | 252 +++++++++++++++++- .../src/System/Net/Security/TlsFrameHelper.cs | 36 ++- .../ClientAsyncAuthenticateTest.cs | 16 ++ .../ServerAsyncAuthenticateTest.cs | 2 + .../SslStreamNegotiatedCipherSuiteTest.cs | 5 +- .../SslStreamSystemDefaultsTest.cs | 28 +- 8 files changed, 317 insertions(+), 28 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs index 6565b851af10c..b264aa6bc6c5d 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs @@ -96,6 +96,10 @@ public static IEnumerable GetAsync_AllowedSSLVersion_Succeeds_MemberDa yield return new object[] { SslProtocols.Ssl3, true }; #endif } + if (PlatformDetection.IsWindows && !PlatformDetection.IsWindows10Version1607OrGreater) + { + yield return new object[] { SslProtocols.Ssl2, true }; + } #pragma warning restore 0618 // These protocols are new, and might not be enabled everywhere yet if (PlatformDetection.IsUbuntu1810OrHigher) diff --git a/src/libraries/System.Net.Security/src/Resources/Strings.resx b/src/libraries/System.Net.Security/src/Resources/Strings.resx index 64fe5cc24bb87..8af05ebe134c8 100644 --- a/src/libraries/System.Net.Security/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Security/src/Resources/Strings.resx @@ -213,7 +213,7 @@ Authentication failed because the remote party has closed the transport stream. - Authentication failed because the remote party has sent TLS alert '{0}'. + Authentication failed because the remote party sent a TLS alert: '{0}'. Authentication failed on the remote side (the stream might still be available for additional authentication attempts). diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs index 62c2513d31a8b..cd7147fd77b2b 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs @@ -22,6 +22,18 @@ public partial class SslStream private int _nestedAuth; + private enum Framing + { + Unknown = 0, + BeforeSSL3, + SinceSSL3, + Unified, + Invalid + } + + // This is set on the first packet to figure out the framing style. + private Framing _framing = Framing.Unknown; + private TlsAlertDescription _lastAlertDescription; private TlsFrameHandshakeInfo _lastFrame; @@ -335,7 +347,23 @@ private async ValueTask ReceiveBlobAsync(TIOAdapter a throw new IOException(SR.net_io_eof); } - TlsFrameHelper.TryGetFrameHeader(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame.Header); + if (_framing == Framing.Unified || _framing == Framing.Unknown) + { + _framing = DetectFraming(_handshakeBuffer.ActiveReadOnlySpan); + } + + if (_framing == Framing.BeforeSSL3) + { +#pragma warning disable 0618 + _lastFrame.Header.Version = SslProtocols.Ssl2; +#pragma warning restore 0618 + _lastFrame.Header.Length = GetFrameSize(_handshakeBuffer.ActiveReadOnlySpan); + } + else + { + TlsFrameHelper.TryGetFrameHeader(_handshakeBuffer.ActiveReadOnlySpan, ref _lastFrame.Header); + } + if (_lastFrame.Header.Length < 0) { throw new IOException(SR.net_frame_read_size); @@ -927,5 +955,227 @@ private static byte[] EnsureBufferSize(byte[] buffer, int copyCount, int size) return buffer; } + + // We need at least 5 bytes to determine what we have. + private Framing DetectFraming(ReadOnlySpan bytes) + { + /* PCTv1.0 Hello starts with + * RECORD_LENGTH_MSB (ignore) + * RECORD_LENGTH_LSB (ignore) + * PCT1_CLIENT_HELLO (must be equal) + * PCT1_CLIENT_VERSION_MSB (if version greater than PCTv1) + * PCT1_CLIENT_VERSION_LSB (if version greater than PCTv1) + * + * ... PCT hello ... + */ + + /* Microsoft Unihello starts with + * RECORD_LENGTH_MSB (ignore) + * RECORD_LENGTH_LSB (ignore) + * SSL2_CLIENT_HELLO (must be equal) + * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3) + * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3) + * + * ... SSLv2 Compatible Hello ... + */ + + /* SSLv2 CLIENT_HELLO starts with + * RECORD_LENGTH_MSB (ignore) + * RECORD_LENGTH_LSB (ignore) + * SSL2_CLIENT_HELLO (must be equal) + * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3) + * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3) + * + * ... SSLv2 CLIENT_HELLO ... + */ + /* SSLv2 SERVER_HELLO starts with + * RECORD_LENGTH_MSB (ignore) + * RECORD_LENGTH_LSB (ignore) + * SSL2_SERVER_HELLO (must be equal) + * SSL2_SESSION_ID_HIT (ignore) + * SSL2_CERTIFICATE_TYPE (ignore) + * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv2) ( or v3) + * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv2) ( or v3) + * + * ... SSLv2 SERVER_HELLO ... + */ + + /* SSLv3 Type 2 Hello starts with + * RECORD_LENGTH_MSB (ignore) + * RECORD_LENGTH_LSB (ignore) + * SSL2_CLIENT_HELLO (must be equal) + * SSL2_CLIENT_VERSION_MSB (if version greater than SSLv3) + * SSL2_CLIENT_VERSION_LSB (if version greater than SSLv3) + * + * ... SSLv2 Compatible Hello ... + */ + + /* SSLv3 Type 3 Hello starts with + * 22 (HANDSHAKE MESSAGE) + * VERSION MSB + * VERSION LSB + * RECORD_LENGTH_MSB (ignore) + * RECORD_LENGTH_LSB (ignore) + * HS TYPE (CLIENT_HELLO) + * 3 bytes HS record length + * HS Version + * HS Version + */ + + /* SSLv2 message codes + * SSL_MT_ERROR 0 + * SSL_MT_CLIENT_HELLO 1 + * SSL_MT_CLIENT_MASTER_KEY 2 + * SSL_MT_CLIENT_FINISHED 3 + * SSL_MT_SERVER_HELLO 4 + * SSL_MT_SERVER_VERIFY 5 + * SSL_MT_SERVER_FINISHED 6 + * SSL_MT_REQUEST_CERTIFICATE 7 + * SSL_MT_CLIENT_CERTIFICATE 8 + */ + int version = -1; + + if (bytes.Length == 0) + { + NetEventSource.Fail(this, "Header buffer is not allocated."); + } + + // If the first byte is SSL3 HandShake, then check if we have a SSLv3 Type3 client hello. + if (bytes[0] == (byte)TlsContentType.Handshake || bytes[0] == (byte)TlsContentType.AppData + || bytes[0] == (byte)TlsContentType.Alert) + { + if (bytes.Length < 3) + { + return Framing.Invalid; + } + +#if TRACE_VERBOSE + if (bytes[1] != 3 && NetEventSource.IsEnabled) + { + if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"WARNING: SslState::DetectFraming() SSL protocol is > 3, trying SSL3 framing in retail = {bytes[1]:x}"); + } +#endif + + version = (bytes[1] << 8) | bytes[2]; + if (version < 0x300 || version >= 0x500) + { + return Framing.Invalid; + } + + // + // This is an SSL3 Framing + // + return Framing.SinceSSL3; + } + +#if TRACE_VERBOSE + if ((bytes[0] & 0x80) == 0 && NetEventSource.IsEnabled) + { + // We have a three-byte header format + if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"WARNING: SslState::DetectFraming() SSL v <=2 HELLO has no high bit set for 3 bytes header, we are broken, received byte = {bytes[0]:x}"); + } +#endif + + if (bytes.Length < 3) + { + return Framing.Invalid; + } + + if (bytes[2] > 8) + { + return Framing.Invalid; + } + + if (bytes[2] == 0x1) // SSL_MT_CLIENT_HELLO + { + if (bytes.Length >= 5) + { + version = (bytes[3] << 8) | bytes[4]; + } + } + else if (bytes[2] == 0x4) // SSL_MT_SERVER_HELLO + { + if (bytes.Length >= 7) + { + version = (bytes[5] << 8) | bytes[6]; + } + } + + if (version != -1) + { + // If this is the first packet, the client may start with an SSL2 packet + // but stating that the version is 3.x, so check the full range. + // For the subsequent packets we assume that an SSL2 packet should have a 2.x version. + if (_framing == Framing.Unknown) + { + if (version != 0x0002 && (version < 0x200 || version >= 0x500)) + { + return Framing.Invalid; + } + } + else + { + if (version != 0x0002) + { + return Framing.Invalid; + } + } + } + + // When server has replied the framing is already fixed depending on the prior client packet + if (!_context!.IsServer || _framing == Framing.Unified) + { + return Framing.BeforeSSL3; + } + + return Framing.Unified; // Will use Ssl2 just for this frame. + } + + // Returns TLS Frame size. + private int GetFrameSize(ReadOnlySpan buffer) + { + if (NetEventSource.IsEnabled) + NetEventSource.Enter(this, buffer.Length); + + int payloadSize = -1; + switch (_framing) + { + case Framing.Unified: + case Framing.BeforeSSL3: + if (buffer.Length < 2) + { + throw new IOException(SR.net_ssl_io_frame); + } + // Note: Cannot detect version mismatch for <= SSL2 + + if ((buffer[0] & 0x80) != 0) + { + // Two bytes + payloadSize = (((buffer[0] & 0x7f) << 8) | buffer[1]) + 2; + } + else + { + // Three bytes + payloadSize = (((buffer[0] & 0x3f) << 8) | buffer[1]) + 3; + } + + break; + case Framing.SinceSSL3: + if (buffer.Length < 5) + { + throw new IOException(SR.net_ssl_io_frame); + } + + payloadSize = ((buffer[3] << 8) | buffer[4]) + 5; + break; + default: + break; + } + + if (NetEventSource.IsEnabled) + NetEventSource.Exit(this, payloadSize); + + return payloadSize; + } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs index 56f58bb24f30c..ec18be1b8e374 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; using System.Security.Authentication; namespace System.Net.Security @@ -87,10 +88,10 @@ internal class TlsFrameHelper { public const int HeaderSize = 5; - private static byte[] _protocolMismatch13 = new byte[] { (byte)TlsContentType.Alert, 3, 4, 0, 2, 2, 70 }; - private static byte[] _protocolMismatch12 = new byte[] { (byte)TlsContentType.Alert, 3, 3, 0, 2, 2, 70 }; - private static byte[] _protocolMismatch11 = new byte[] { (byte)TlsContentType.Alert, 3, 2, 0, 2, 2, 70 }; - private static byte[] _protocolMismatch10 = new byte[] { (byte)TlsContentType.Alert, 3, 1, 0, 2, 2, 70 }; + private static byte[] s_protocolMismatch13 = new byte[] { (byte)TlsContentType.Alert, 3, 4, 0, 2, 2, 70 }; + private static byte[] s_protocolMismatch12 = new byte[] { (byte)TlsContentType.Alert, 3, 3, 0, 2, 2, 70 }; + private static byte[] s_protocolMismatch11 = new byte[] { (byte)TlsContentType.Alert, 3, 2, 0, 2, 2, 70 }; + private static byte[] s_protocolMismatch10 = new byte[] { (byte)TlsContentType.Alert, 3, 1, 0, 2, 2, 70 }; public static bool TryGetFrameHeader(ReadOnlySpan frame, ref TlsFrameHeader header) { @@ -164,7 +165,9 @@ public static bool TryGetHandshakeInfo(ReadOnlySpan frame, ref TlsFrameHan } // This will not fail since we have enough data. - TryGetFrameHeader(frame, ref info.Header); + bool gotHeader = TryGetFrameHeader(frame, ref info.Header); + Debug.Assert(gotHeader); + info.SupportedVersions = info.Header.Version; info.HandshakeType = (TlsHandshakeType)frame[5]; @@ -190,22 +193,15 @@ public static bool TryGetAlertInfo(ReadOnlySpan frame, ref TlsAlertLevel l return true; } - private static byte[] CreateProtocolVersionAlert(SslProtocols version) - { - switch (version) + private static byte[] CreateProtocolVersionAlert(SslProtocols version) => + version switch { - case SslProtocols.Tls13: - return _protocolMismatch13; - case SslProtocols.Tls12: - return _protocolMismatch12; - case SslProtocols.Tls11: - return _protocolMismatch11; - case SslProtocols.Tls: - return _protocolMismatch10; - default: - return Array.Empty(); - } - } + SslProtocols.Tls13 => s_protocolMismatch13, + SslProtocols.Tls12 => s_protocolMismatch12, + SslProtocols.Tls11 => s_protocolMismatch11, + SslProtocols.Tls => s_protocolMismatch10, + _ => Array.Empty(), + }; public static byte[] CreateAlertFrame(SslProtocols version, TlsAlertDescription reason) { diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs index 9b6191ae66d36..5c227192f2448 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs @@ -57,6 +57,20 @@ public async Task ClientAsyncAuthenticate_EachSupportedProtocol_Success(SslProto await ClientAsyncSslHelper(protocol, protocol); } + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public async Task ClientAsyncAuthenticate_Ssl2WithSelf_Success() + { + // Test Ssl2 against itself. This is a standalone test as even on versions where Windows supports Ssl2, + // it appears to have rules around not using it when other protocols are mentioned. + if (!PlatformDetection.IsWindows10Version1607OrGreater) + { +#pragma warning disable 0618 + await ClientAsyncSslHelper(SslProtocols.Ssl2, SslProtocols.Ssl2); +#pragma warning restore 0618 + } + } + [Theory] [MemberData(nameof(ProtocolMismatchData))] public async Task ClientAsyncAuthenticate_MismatchProtocols_Fails( @@ -98,6 +112,8 @@ public async Task ClientAsyncAuthenticate_IndividualServerVsAllClientSupportedPr public static IEnumerable ProtocolMismatchData() { #pragma warning disable 0618 + yield return new object[] { SslProtocols.Ssl2, SslProtocols.Ssl3, typeof(Exception) }; + yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls12, typeof(Exception) }; yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls12, typeof(Exception) }; #pragma warning restore 0618 yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(AuthenticationException) }; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs index 5fa008c87db09..9bf0afb5b2290 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs @@ -73,6 +73,8 @@ public async Task ServerAsyncAuthenticate_AllClientVsIndividualServerSupportedPr public static IEnumerable ProtocolMismatchData() { #pragma warning disable 0618 + yield return new object[] { SslProtocols.Ssl2, SslProtocols.Ssl3, typeof(Exception) }; + yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls12, typeof(Exception) }; yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls12, typeof(Exception) }; #pragma warning restore 0618 yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(AuthenticationException) }; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNegotiatedCipherSuiteTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNegotiatedCipherSuiteTest.cs index 3ffe83bc3dd98..f4921f68ff7fb 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNegotiatedCipherSuiteTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNegotiatedCipherSuiteTest.cs @@ -20,8 +20,9 @@ namespace System.Net.Security.Tests public class NegotiatedCipherSuiteTest { -#pragma warning disable CS0618 // Ssl3 are obsolete - public const SslProtocols AllProtocols = SslProtocols.Ssl3 | +#pragma warning disable CS0618 // Ssl2 and Ssl3 are obsolete + public const SslProtocols AllProtocols = + SslProtocols.Ssl2 | SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13; #pragma warning restore CS0618 diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs index 87b21e109fb06..bc704fae67fda 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.IO; using System.Net.Http; using System.Net.Test.Common; using System.Security.Cryptography.X509Certificates; @@ -21,10 +22,7 @@ public abstract class SslStreamSystemDefaultTest public SslStreamSystemDefaultTest() { - var network = new VirtualNetwork(); - var clientNet = new VirtualNetworkStream(network, isServer:false); - var serverNet = new VirtualNetworkStream(network, isServer: true); - + (Stream clientNet, Stream serverNet) = TestHelper.GetConnectedTcpStreams(); _clientStream = new SslStream(clientNet, false, ClientCertCallback); _serverStream = new SslStream(serverNet, false, ServerCertCallback); } @@ -76,6 +74,28 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( } } + [ConditionalTheory(nameof(IsNotWindows7))] +#pragma warning disable 0618 + [InlineData(null, SslProtocols.Ssl2)] + [InlineData(SslProtocols.None, SslProtocols.Ssl2)] + [InlineData(SslProtocols.Ssl2, null)] + [InlineData(SslProtocols.Ssl2, SslProtocols.None)] +#pragma warning restore 0618 + public async Task ClientAndServer_OneUsesDefault_OtherUsesLowerProtocol_Fails( + SslProtocols? clientProtocols, SslProtocols? serverProtocols) + { + using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate()) + using (X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate()) + { + string serverHost = serverCertificate.GetNameInfo(X509NameType.SimpleName, false); + var clientCertificates = new X509CertificateCollection() { clientCertificate }; + + await Assert.ThrowsAnyAsync(() => TestConfiguration.WhenAllOrAnyFailedWithTimeout( + AuthenticateClientAsync(serverHost, clientCertificates, checkCertificateRevocation: false, protocols: clientProtocols), + AuthenticateServerAsync(serverCertificate, clientCertificateRequired: true, checkCertificateRevocation: false, protocols: serverProtocols))); + } + } + private bool ClientCertCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { switch (sslPolicyErrors) From 5b98319ec8d48c40b52ad894ca3da05031654658 Mon Sep 17 00:00:00 2001 From: wfurt Date: Thu, 7 May 2020 08:41:41 -0700 Subject: [PATCH 9/9] add back two missing empty lines --- .../src/System/Net/Security/SslStream.Implementation.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs index cd7147fd77b2b..d9ff52dfeba8f 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs @@ -988,6 +988,7 @@ private Framing DetectFraming(ReadOnlySpan bytes) * * ... SSLv2 CLIENT_HELLO ... */ + /* SSLv2 SERVER_HELLO starts with * RECORD_LENGTH_MSB (ignore) * RECORD_LENGTH_LSB (ignore) @@ -1033,6 +1034,7 @@ private Framing DetectFraming(ReadOnlySpan bytes) * SSL_MT_REQUEST_CERTIFICATE 7 * SSL_MT_CLIENT_CERTIFICATE 8 */ + int version = -1; if (bytes.Length == 0)