From 6471f3b731dfe3b30f6cc8c37fb31f00d546aefd Mon Sep 17 00:00:00 2001 From: Cristian Pop Date: Thu, 12 Nov 2015 16:35:32 -0800 Subject: [PATCH] Removing SSLv2 and SSLv3 support from System.Net.Security. APIs will throw NotSupportedException if SSLv2 or v3 is used. This behavior is different from .NET Desktop. Tests have been changed to pin this new behavior. Increased passing-test timeouts to 15s. Fix #3114 (partial), #4467 --- .../Windows/SChannel/Interop.SchProtocols.cs | 18 +- .../src/Resources/Strings.resx | 3 + .../System/Net/SecureProtocols/SslStream.cs | 18 ++ .../CertificateValidationClientServer.cs | 4 +- .../ClientAsyncAuthenticateTest.cs | 221 +++++++----------- .../tests/FunctionalTests/MockNetwork.cs | 2 +- .../ServerAsyncAuthenticateTest.cs | 219 +++++++++++++++++ .../SslStreamStreamToStreamTest.cs | 5 +- .../System.Net.Security.Tests.csproj | 1 + .../FunctionalTests/TestConfiguration.cs | 14 +- 10 files changed, 344 insertions(+), 161 deletions(-) create mode 100644 src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs diff --git a/src/Common/src/Interop/Windows/SChannel/Interop.SchProtocols.cs b/src/Common/src/Interop/Windows/SChannel/Interop.SchProtocols.cs index c8856c3f2375..8493eb08f3ab 100644 --- a/src/Common/src/Interop/Windows/SChannel/Interop.SchProtocols.cs +++ b/src/Common/src/Interop/Windows/SChannel/Interop.SchProtocols.cs @@ -8,10 +8,8 @@ internal static partial class SChannel // Most constants below are taken from schannel.h; those that are not are // called out explicitly. - public const int SP_PROT_PCT1_SERVER = 0x00000001; - public const int SP_PROT_PCT1_CLIENT = 0x00000002; - public const int SP_PROT_PCT1 = (SP_PROT_PCT1_SERVER | SP_PROT_PCT1_CLIENT); - + // IMPORTANT: SSL2 and SSL3 definitions are required for System.Net.Primitives enum definitions only. + // These values should NOT be used in Schannel setup. public const int SP_PROT_SSL2_SERVER = 0x00000004; public const int SP_PROT_SSL2_CLIENT = 0x00000008; public const int SP_PROT_SSL2 = (SP_PROT_SSL2_SERVER | SP_PROT_SSL2_CLIENT); @@ -32,18 +30,10 @@ internal static partial class SChannel public const int SP_PROT_TLS1_2_CLIENT = 0x00000800; public const int SP_PROT_TLS1_2 = (SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_2_CLIENT); - public const int SP_PROT_SSL3TLS1_CLIENTS = (SP_PROT_TLS1_0_CLIENT | SP_PROT_SSL3_CLIENT); - public const int SP_PROT_SSL3TLS1_SERVERS = (SP_PROT_TLS1_0_SERVER | SP_PROT_SSL3_SERVER); - public const int SP_PROT_SSL3TLS1 = (SP_PROT_SSL3 | SP_PROT_TLS1_0); - - public const int SP_PROT_UNI_SERVER = 0x40000000; - public const int SP_PROT_UNI_CLIENT = unchecked((int)0x80000000); - public const int SP_PROT_UNI = (SP_PROT_UNI_SERVER | SP_PROT_UNI_CLIENT); - public const int SP_PROT_NONE = 0; // These two constants are not taken from schannel.h. - public const int ClientProtocolMask = (SP_PROT_PCT1_CLIENT | SP_PROT_SSL2_CLIENT | SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT | SP_PROT_UNI_CLIENT); - public const int ServerProtocolMask = (SP_PROT_PCT1_SERVER | SP_PROT_SSL2_SERVER | SP_PROT_SSL3_SERVER | SP_PROT_TLS1_0_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER | SP_PROT_UNI_SERVER); + public const int ClientProtocolMask = (SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT); + public const int ServerProtocolMask = (SP_PROT_TLS1_0_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER); } } diff --git a/src/System.Net.Security/src/Resources/Strings.resx b/src/System.Net.Security/src/Resources/Strings.resx index 071ad9a0b16a..05c6143f2f7a 100644 --- a/src/System.Net.Security/src/Resources/Strings.resx +++ b/src/System.Net.Security/src/Resources/Strings.resx @@ -123,6 +123,9 @@ The requested security package is not supported. + + The requested security protocol is not supported. + This method is not implemented by this class. diff --git a/src/System.Net.Security/src/System/Net/SecureProtocols/SslStream.cs b/src/System.Net.Security/src/System/Net/SecureProtocols/SslStream.cs index 6e1813f7d2a6..8e57d12ee604 100644 --- a/src/System.Net.Security/src/System/Net/SecureProtocols/SslStream.cs +++ b/src/System.Net.Security/src/System/Net/SecureProtocols/SslStream.cs @@ -110,6 +110,8 @@ public virtual void AuthenticateAsClient(string targetHost) public virtual void AuthenticateAsClient(string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation) { + ValidateSecurityProtocol(enabledSslProtocols); + _sslState.ValidateCreateContext(false, targetHost, enabledSslProtocols, null, clientCertificates, true, checkCertificateRevocation); _sslState.ProcessAuthentication(null); } @@ -147,6 +149,8 @@ public virtual void AuthenticateAsServer(X509Certificate serverCertificate) public virtual void AuthenticateAsServer(X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation) { + ValidateSecurityProtocol(enabledSslProtocols); + _sslState.ValidateCreateContext(true, string.Empty, enabledSslProtocols, serverCertificate, null, clientCertificateRequired, checkCertificateRevocation); _sslState.ProcessAuthentication(null); } @@ -196,6 +200,8 @@ public virtual Task AuthenticateAsClientAsync(string targetHost) public virtual Task AuthenticateAsClientAsync(string targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, bool checkCertificateRevocation) { + ValidateSecurityProtocol(enabledSslProtocols); + return Task.Factory.FromAsync((callback, state) => BeginAuthenticateAsClient(targetHost, clientCertificates, enabledSslProtocols, checkCertificateRevocation, callback, state), EndAuthenticateAsClient, null); } @@ -206,6 +212,8 @@ public virtual Task AuthenticateAsServerAsync(X509Certificate serverCertificate) public virtual Task AuthenticateAsServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, SslProtocols enabledSslProtocols, bool checkCertificateRevocation) { + ValidateSecurityProtocol(enabledSslProtocols); + return Task.Factory.FromAsync((callback, state) => BeginAuthenticateAsServer(serverCertificate, clientCertificateRequired, enabledSslProtocols, checkCertificateRevocation, callback, state), EndAuthenticateAsServer, null); } #endregion @@ -460,5 +468,15 @@ public override void Write(byte[] buffer, int offset, int count) { _sslState.SecureStream.Write(buffer, offset, count); } + + private static void ValidateSecurityProtocol(SslProtocols protocols) + { + SslProtocols allowedProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12; + + if ((protocols == SslProtocols.None) || ((protocols & ~allowedProtocols) != 0)) + { + throw new NotSupportedException(SR.net_securityprotocolnotsupported); + } + } } } diff --git a/src/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs b/src/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs index 0f8345502694..fcf55fd3f4dd 100644 --- a/src/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs +++ b/src/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs @@ -45,7 +45,7 @@ public async Task CertificateValidationClientServer_EndToEnd_Ok() Assert.True( Task.WaitAll( new Task[] { clientConnect, serverAccept }, - TestConfiguration.TestTimeoutSeconds * 1000), + TestConfiguration.PassingTestTimeoutMilliseconds), "Client/Server TCP Connect timed out."); using (TcpClient serverConnection = await serverAccept) @@ -80,7 +80,7 @@ public async Task CertificateValidationClientServer_EndToEnd_Ok() Assert.True( Task.WaitAll( new Task[] { clientAuthentication, serverAuthentication }, - TestConfiguration.TestTimeoutSeconds * 1000), + TestConfiguration.PassingTestTimeoutMilliseconds), "Client/Server Authentication timed out."); } } diff --git a/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs b/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs index 7b0caec086c9..4cd970f70182 100644 --- a/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs @@ -2,13 +2,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; -using System.ComponentModel; using System.IO; using System.Net.Sockets; using System.Net.Test.Common; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; + using Xunit; using Xunit.Abstractions; @@ -17,72 +17,7 @@ namespace System.Net.Security.Tests public class ClientAsyncAuthenticateTest { private readonly ITestOutputHelper _log; - - private const SslProtocols AllSslProtocols = - SslProtocols.Ssl2 - | SslProtocols.Ssl3 - | SslProtocols.Tls - | SslProtocols.Tls11 - | SslProtocols.Tls12; - - private static readonly SslProtocols[] s_eachSslProtocol = new SslProtocols[] - { - SslProtocols.Ssl3, - SslProtocols.Tls, - SslProtocols.Tls11, - SslProtocols.Tls12, - }; - - private static IEnumerable ProtocolMismatchData() - { - yield return new object[] { SslProtocols.Ssl3, SslProtocols.Ssl2, typeof(IOException) }; - yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls, typeof(IOException) }; - yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls11, typeof(IOException) }; - yield return new object[] { SslProtocols.Ssl3, SslProtocols.Tls12, typeof(IOException) }; - - yield return new object[] { SslProtocols.Tls, SslProtocols.Ssl2, typeof(IOException) }; - yield return new object[] { SslProtocols.Tls, SslProtocols.Ssl3, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(IOException) }; - yield return new object[] { SslProtocols.Tls, SslProtocols.Tls12, typeof(IOException) }; - - yield return new object[] { SslProtocols.Tls11, SslProtocols.Ssl3, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls, typeof(AuthenticationException) }; - - yield return new object[] { SslProtocols.Tls12, SslProtocols.Ssl3, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls11, typeof(AuthenticationException) }; - } - - private static IEnumerable ProtocolMismatchDataSsl2SpecificWindows() - { - yield return new object[] {SslProtocols.Ssl2, SslProtocols.Ssl3, typeof (IOException)}; - yield return new object[] {SslProtocols.Ssl2, SslProtocols.Tls, typeof (IOException)}; - yield return new object[] {SslProtocols.Ssl2, SslProtocols.Tls11, typeof (IOException)}; - yield return new object[] {SslProtocols.Ssl2, SslProtocols.Tls12, typeof (IOException)}; - } - - private static IEnumerable ProtocolMismatchDataSsl2SpecificLinux() - { - yield return new object[] { SslProtocols.Ssl2, SslProtocols.Ssl3, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls11, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Ssl2, SslProtocols.Tls12, typeof(AuthenticationException) }; - } - - private static IEnumerable ProtocolMismatchData_Tls11_Tls12_Windows_Linux() - { - yield return new object[] { SslProtocols.Tls11, SslProtocols.Ssl2, typeof(IOException) }; - yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, typeof(IOException) }; - yield return new object[] { SslProtocols.Tls12, SslProtocols.Ssl2, typeof(IOException) }; - } - - private static IEnumerable ProtocolMismatchData_Tls11_Tls12_OSX() - { - yield return new object[] { SslProtocols.Tls11, SslProtocols.Ssl2, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, typeof(AuthenticationException) }; - yield return new object[] { SslProtocols.Tls12, SslProtocols.Ssl2, typeof(AuthenticationException) }; - } - + public ClientAsyncAuthenticateTest() { _log = TestLogging.GetInstance(); @@ -100,127 +35,131 @@ public async Task ClientAsyncAuthenticate_ServerNoEncryption_NoConnect() await Assert.ThrowsAsync(() => ClientAsyncSslHelper(EncryptionPolicy.NoEncryption)); } - [Fact] - public async Task ClientAsyncAuthenticate_EachProtocol_Success() - { - foreach (SslProtocols protocol in s_eachSslProtocol) - { - await ClientAsyncSslHelper(protocol, protocol); - } - } - [Theory] - [MemberData("ProtocolMismatchData")] - public async Task ClientAsyncAuthenticate_MismatchProtocols_Fails(SslProtocols server, SslProtocols client, Type expected) + [MemberData("SupportedSslProtocols")] + public async Task ClientAsyncAuthenticate_EachSupportedProtocol_Success(SslProtocols protocol) { - await Assert.ThrowsAsync(expected, () => ClientAsyncSslHelper(server, client)); + await ClientAsyncSslHelper(protocol, protocol); } [Theory] - [MemberData("ProtocolMismatchDataSsl2SpecificWindows")] - [PlatformSpecific(PlatformID.Windows)] - public async Task ClientAsyncAuthenticate_MismatchProtocols_Ssl2_Fails_Windows(SslProtocols server, SslProtocols client, Type expected) + [MemberData("UnsupportedSslProtocols")] + public async Task ClientAsyncAuthenticate_EachClientUnsupportedProtocol_Fail(SslProtocols protocol) { - await Assert.ThrowsAsync(expected, () => ClientAsyncSslHelper(server, client)); + await Assert.ThrowsAsync( () => { + return ClientAsyncSslHelper(protocol, TestConfiguration.SupportedSslProtocols); + }); } [Theory] - [MemberData("ProtocolMismatchDataSsl2SpecificLinux")] - [PlatformSpecific(PlatformID.Linux)] - public async Task ClientAsyncAuthenticate_MismatchProtocols_Ssl2_Fails_Linux(SslProtocols server, SslProtocols client, Type expected) + [MemberData("ProtocolMismatchData")] + public async Task ClientAsyncAuthenticate_MismatchProtocols_Fails( + SslProtocols serverProtocol, + SslProtocols clientProtocol, + Type expectedException) { - await Assert.ThrowsAsync(expected, () => ClientAsyncSslHelper(server, client)); + await Assert.ThrowsAsync(expectedException, () => ClientAsyncSslHelper(serverProtocol, clientProtocol)); } [Theory] [MemberData("ProtocolMismatchData_Tls11_Tls12_Windows_Linux")] [PlatformSpecific(PlatformID.Windows | PlatformID.Linux)] - public async Task ClientAsyncAuthenticate_MismatchProtocols_Tls11_Tls12_Fails_Linux_Windows(SslProtocols server, SslProtocols client, Type expected) + public async Task ClientAsyncAuthenticate_MismatchProtocols_Tls11_Tls12_Fails_Linux_Windows( + SslProtocols serverProtocol, + SslProtocols clientProtocol, + Type expectedException) { - await Assert.ThrowsAsync(expected, () => ClientAsyncSslHelper(server, client)); + await Assert.ThrowsAsync(expectedException, () => ClientAsyncSslHelper(serverProtocol, clientProtocol)); } [Theory] [MemberData("ProtocolMismatchData_Tls11_Tls12_OSX")] [PlatformSpecific(PlatformID.OSX)] - public async Task ClientAsyncAuthenticate_MismatchProtocols_Tls11_Tls12_Fails_OSX(SslProtocols server, SslProtocols client, Type expected) - { - await Assert.ThrowsAsync(expected, () => ClientAsyncSslHelper(server, client)); - } - - - [Fact] - [PlatformSpecific(PlatformID.Windows)] - public async Task ClientAsyncAuthenticate_EachProtocol_Ssl2_Success() - { - await ClientAsyncSslHelper(SslProtocols.Ssl2, SslProtocols.Ssl2); - } - - [Fact] - [PlatformSpecific(PlatformID.Windows)] - public async Task ClientAsyncAuthenticate_Ssl2Tls12ServerSsl2Client_Fails() + public async Task ClientAsyncAuthenticate_MismatchProtocols_Tls11_Tls12_Fails_OSX( + SslProtocols serverProtocols, + SslProtocols clientProtocols, + Type expectedException) { - // Ssl2 and Tls 1.2 are mutually exclusive. - await Assert.ThrowsAsync(() => ClientAsyncSslHelper(SslProtocols.Ssl2 | SslProtocols.Tls12, SslProtocols.Ssl2)); + await Assert.ThrowsAsync(expectedException, () => ClientAsyncSslHelper(serverProtocols, clientProtocols)); } [Fact] - [PlatformSpecific(PlatformID.Windows)] - public async Task ClientAsyncAuthenticate_Ssl2Tls12ServerTls12Client_Fails() + public async Task ClientAsyncAuthenticate_AllServerAllClient_Success() { - // Ssl2 and Tls 1.2 are mutually exclusive. - await Assert.ThrowsAsync(() => ClientAsyncSslHelper(SslProtocols.Ssl2 | SslProtocols.Tls12, SslProtocols.Tls12)); + await ClientAsyncSslHelper( + TestConfiguration.SupportedSslProtocols, + TestConfiguration.SupportedSslProtocols); } [Fact] - [PlatformSpecific(PlatformID.Windows)] - public async Task ClientAsyncAuthenticate_Ssl2ServerSsl2Tls12Client_Success() + public async Task ClientAsyncAuthenticate_UnsuportedAllClient_Fail() { - await ClientAsyncSslHelper(SslProtocols.Ssl2, SslProtocols.Ssl2 | SslProtocols.Tls12); + await Assert.ThrowsAsync(() => { + return ClientAsyncSslHelper( + TestConfiguration.UnsupportedSslProtocols, + TestConfiguration.SupportedSslProtocols); + }); } - - [Fact] - [PlatformSpecific(PlatformID.Windows | PlatformID.Linux)] - public async Task ClientAsyncAuthenticate_Tls12ServerSsl2Tls12Client_Success() + + [Theory] + [MemberData("SupportedSslProtocols")] + public async Task ClientAsyncAuthenticate_AllServerVsIndividualClientSupportedProtocols_Success( + SslProtocols clientProtocol) { - await ClientAsyncSslHelper(SslProtocols.Tls12, SslProtocols.Ssl2 | SslProtocols.Tls12); + await ClientAsyncSslHelper(clientProtocol, TestConfiguration.SupportedSslProtocols); } - [Fact] - public async Task ClientAsyncAuthenticate_AllServerAllClient_Success() + [Theory] + [MemberData("SupportedSslProtocols")] + public async Task ClientAsyncAuthenticate_IndividualServerVsAllClientSupportedProtocols_Success( + SslProtocols serverProtocol) { - // Drop Ssl2, it's incompatible with Tls 1.2 - SslProtocols sslProtocols = AllSslProtocols & ~SslProtocols.Ssl2; - await ClientAsyncSslHelper(sslProtocols, sslProtocols); + await ClientAsyncSslHelper(TestConfiguration.SupportedSslProtocols, serverProtocol); + // Cached Tls creds fail when used against Tls servers of higher versions. + // Servers are not expected to dynamically change versions. } - [Fact] - public async Task ClientAsyncAuthenticate_AllServerVsIndividualClientProtocols_Success() + private static IEnumerable SupportedSslProtocols() { - foreach (SslProtocols clientProtocol in s_eachSslProtocol) + foreach (SslProtocols protocol in Enum.GetValues(typeof(SslProtocols))) { - if (clientProtocol != SslProtocols.Ssl2) // Incompatible with Tls 1.2 + if ((protocol & TestConfiguration.SupportedSslProtocols) != 0) { - await ClientAsyncSslHelper(clientProtocol, AllSslProtocols); + yield return new object[] { protocol }; } } } - [Fact] - public async Task ClientAsyncAuthenticate_IndividualServerVsAllClientProtocols_Success() + private static IEnumerable UnsupportedSslProtocols() { - SslProtocols clientProtocols = AllSslProtocols & ~SslProtocols.Ssl2; // Incompatible with Tls 1.2 - foreach (SslProtocols serverProtocol in s_eachSslProtocol) + foreach (SslProtocols protocol in Enum.GetValues(typeof(SslProtocols))) { - if (serverProtocol != SslProtocols.Ssl2) // Incompatible with Tls 1.2 + if ((protocol & TestConfiguration.UnsupportedSslProtocols) != 0) { - await ClientAsyncSslHelper(clientProtocols, serverProtocol); - // Cached Tls creds fail when used against Tls servers of higher versions. - // Servers are not expected to dynamically change versions. + yield return new object[] { protocol }; } } } + private static IEnumerable ProtocolMismatchData() + { + yield return new object[] { SslProtocols.Tls, SslProtocols.Tls11, typeof(IOException) }; + yield return new object[] { SslProtocols.Tls, SslProtocols.Tls12, typeof(IOException) }; + 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) }; + } + + private static IEnumerable ProtocolMismatchData_Tls11_Tls12_Windows_Linux() + { + yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, typeof(IOException) }; + } + + private static IEnumerable ProtocolMismatchData_Tls11_Tls12_OSX() + { + yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, typeof(AuthenticationException) }; + } + #region Helpers private Task ClientAsyncSslHelper(EncryptionPolicy encryptionPolicy) @@ -233,7 +172,9 @@ private Task ClientAsyncSslHelper(SslProtocols clientSslProtocols, SslProtocols return ClientAsyncSslHelper(EncryptionPolicy.RequireEncryption, clientSslProtocols, serverSslProtocols); } - private async Task ClientAsyncSslHelper(EncryptionPolicy encryptionPolicy, SslProtocols clientSslProtocols, + private async Task ClientAsyncSslHelper( + EncryptionPolicy encryptionPolicy, + SslProtocols clientSslProtocols, SslProtocols serverSslProtocols) { _log.WriteLine("Server: " + serverSslProtocols + "; Client: " + clientSslProtocols); @@ -248,7 +189,7 @@ private async Task ClientAsyncSslHelper(EncryptionPolicy encryptionPolicy, SslPr using (SslStream sslStream = new SslStream(client.GetStream(), false, AllowAnyServerCertificate, null)) { Task async = sslStream.AuthenticateAsClientAsync("localhost", null, clientSslProtocols, false); - Assert.True(((IAsyncResult)async).AsyncWaitHandle.WaitOne(TestConfiguration.TestTimeoutSeconds * 1000), "Timed Out"); + Assert.True(((IAsyncResult)async).AsyncWaitHandle.WaitOne(TestConfiguration.PassingTestTimeoutMilliseconds), "Timed Out"); async.GetAwaiter().GetResult(); _log.WriteLine("Client({0}) authenticated to server({1}) with encryption cipher: {2} {3}-bit strength", @@ -261,7 +202,7 @@ private async Task ClientAsyncSslHelper(EncryptionPolicy encryptionPolicy, SslPr } // The following method is invoked by the RemoteCertificateValidationDelegate. - public bool AllowAnyServerCertificate( + private bool AllowAnyServerCertificate( object sender, X509Certificate certificate, X509Chain chain, diff --git a/src/System.Net.Security/tests/FunctionalTests/MockNetwork.cs b/src/System.Net.Security/tests/FunctionalTests/MockNetwork.cs index 7f05627e5c4f..184a68feb976 100644 --- a/src/System.Net.Security/tests/FunctionalTests/MockNetwork.cs +++ b/src/System.Net.Security/tests/FunctionalTests/MockNetwork.cs @@ -30,7 +30,7 @@ public void ReadFrame(bool server, out byte[] buffer) packetQueue = _serverWriteQueue; } - semaphore.Wait(TestConfiguration.TestTimeoutSeconds * 1000); + semaphore.Wait(TestConfiguration.PassingTestTimeoutMilliseconds); buffer = packetQueue.Dequeue(); } diff --git a/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs new file mode 100644 index 000000000000..7145e3be8995 --- /dev/null +++ b/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs @@ -0,0 +1,219 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.Net.Sockets; +using System.Net.Test.Common; +using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; + +using Xunit; +using Xunit.Abstractions; + +namespace System.Net.Security.Tests +{ + public class ServerAsyncAuthenticateTest + { + private readonly ITestOutputHelper _log; + private readonly X509Certificate2 _serverCertificate; + + public ServerAsyncAuthenticateTest() + { + _log = TestLogging.GetInstance(); + _serverCertificate = TestConfiguration.GetServerCertificate(); + } + + [Theory] + [MemberData("SupportedSslProtocols")] + public async Task ServerAsyncAuthenticate_EachSupportedProtocol_Success(SslProtocols protocol) + { + await ServerAsyncSslHelper(protocol, protocol); + } + + [Theory] + [MemberData("UnsupportedSslProtocols")] + public async Task ServerAsyncAuthenticate_EachServerUnsupportedProtocol_Fail(SslProtocols protocol) + { + await Assert.ThrowsAsync(() => { + return ServerAsyncSslHelper( + TestConfiguration.SupportedSslProtocols, + protocol, + expectedToFail: true); + }); + } + + [Theory] + [MemberData("ProtocolMismatchData")] + public async Task ServerAsyncAuthenticate_MismatchProtocols_Fails( + SslProtocols serverProtocol, + SslProtocols clientProtocol, + Type expectedException) + { + await Assert.ThrowsAsync( + expectedException, + () => { + return ServerAsyncSslHelper( + serverProtocol, + clientProtocol, + expectedToFail: true); + }); + } + + [Fact] + public async Task ServerAsyncAuthenticate_UnsuportedAllServer_Fail() + { + await Assert.ThrowsAsync(() => { + return ServerAsyncSslHelper( + TestConfiguration.SupportedSslProtocols, + TestConfiguration.UnsupportedSslProtocols, + expectedToFail: true); + }); + } + + [Theory] + [MemberData("SupportedSslProtocols")] + public async Task ServerAsyncAuthenticate_AllClientVsIndividualServerSupportedProtocols_Success( + SslProtocols serverProtocol) + { + await ServerAsyncSslHelper(TestConfiguration.SupportedSslProtocols, serverProtocol); + } + + private static IEnumerable SupportedSslProtocols() + { + foreach (SslProtocols protocol in Enum.GetValues(typeof(SslProtocols))) + { + if ((protocol & TestConfiguration.SupportedSslProtocols) != 0) + { + yield return new object[] { protocol }; + } + } + } + + private static IEnumerable UnsupportedSslProtocols() + { + foreach (SslProtocols protocol in Enum.GetValues(typeof(SslProtocols))) + { + if ((protocol & TestConfiguration.UnsupportedSslProtocols) != 0) + { + yield return new object[] { protocol }; + } + } + } + + private static IEnumerable ProtocolMismatchData() + { + 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(TimeoutException) }; + yield return new object[] { SslProtocols.Tls11, SslProtocols.Tls12, typeof(AuthenticationException) }; + yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls, typeof(TimeoutException) }; + yield return new object[] { SslProtocols.Tls12, SslProtocols.Tls11, typeof(TimeoutException) }; + } + + #region Helpers + + private async Task ServerAsyncSslHelper( + SslProtocols clientSslProtocols, + SslProtocols serverSslProtocols, + bool expectedToFail = false) + { + _log.WriteLine( + "Server: " + serverSslProtocols + "; Client: " + clientSslProtocols + + " expectedToFail: " + expectedToFail); + + int timeOut = expectedToFail ? TestConfiguration.FailingTestTimeoutMiliseconds + : TestConfiguration.PassingTestTimeoutMilliseconds; + + IPEndPoint endPoint = new IPEndPoint(IPAddress.IPv6Loopback, 0); + var server = new TcpListener(endPoint); + server.Start(); + + using (var clientConnection = new TcpClient(AddressFamily.InterNetworkV6)) + { + IPEndPoint serverEndPoint = (IPEndPoint)server.LocalEndpoint; + + Task clientConnect = clientConnection.ConnectAsync(serverEndPoint.Address, serverEndPoint.Port); + Task serverAccept = server.AcceptTcpClientAsync(); + + // We expect that the network-level connect will always complete. + Task.WaitAll( + new Task[] { clientConnect, serverAccept }, + TestConfiguration.PassingTestTimeoutMilliseconds); + + using (TcpClient serverConnection = await serverAccept) + using (SslStream sslClientStream = new SslStream(clientConnection.GetStream())) + using (SslStream sslServerStream = new SslStream( + serverConnection.GetStream(), + false, + AllowAnyServerCertificate)) + { + string serverName = _serverCertificate.GetNameInfo(X509NameType.SimpleName, false); + + Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync( + serverName, + null, + clientSslProtocols, + false); + + Task serverAuthentication = sslServerStream.AuthenticateAsServerAsync( + _serverCertificate, + true, + serverSslProtocols, + false); + + try + { + clientAuthentication.Wait(timeOut); + } + catch (AggregateException ex) + { + // Ignore client-side errors: we're only interested in server-side behavior. + _log.WriteLine("Client exception: " + ex.InnerException); + } + + bool serverAuthenticationCompleted = false; + + try + { + serverAuthenticationCompleted = serverAuthentication.Wait(timeOut); + } + catch (AggregateException ex) + { + throw ex.InnerException; + } + + if (!serverAuthenticationCompleted) + { + throw new TimeoutException(); + } + + _log.WriteLine( + "Server({0}) authenticated client({1}) with encryption cipher: {2} {3}-bit strength", + serverConnection.Client.LocalEndPoint, + serverConnection.Client.RemoteEndPoint, + sslServerStream.CipherAlgorithm, + sslServerStream.CipherStrength); + + Assert.True( + sslServerStream.CipherAlgorithm != CipherAlgorithmType.Null, + "Cipher algorithm should not be NULL"); + + Assert.True(sslServerStream.CipherStrength > 0, "Cipher strength should be greater than 0"); + } + } + } + + // The following method is invoked by the RemoteCertificateValidationDelegate. + private bool AllowAnyServerCertificate( + object sender, + X509Certificate certificate, + X509Chain chain, + SslPolicyErrors sslPolicyErrors) + { + return true; // allow everything + } + + #endregion Helpers + } +} diff --git a/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs b/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs index 81b18073a410..d745de80ba00 100644 --- a/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs @@ -15,7 +15,6 @@ namespace System.Net.Security.Tests public class SslStreamStreamToStreamTest { private readonly byte[] sampleMsg = Encoding.UTF8.GetBytes("Sample Test Message"); - private readonly TimeSpan TestTimeoutSpan = TimeSpan.FromSeconds(TestConfiguration.TestTimeoutSeconds); [Fact] public void SslStream_StreamToStream_Authentication_Success() @@ -32,7 +31,7 @@ public void SslStream_StreamToStream_Authentication_Success() auth[0] = client.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false)); auth[1] = server.AuthenticateAsServerAsync(certificate); - bool finished = Task.WaitAll(auth, TestTimeoutSpan); + bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); Assert.True(finished, "Handshake completed in the allotted time"); } } @@ -127,7 +126,7 @@ private bool DoHandshake(SslStream clientSslStream, SslStream serverSslStream) auth[0] = clientSslStream.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false)); auth[1] = serverSslStream.AuthenticateAsServerAsync(certificate); - bool finished = Task.WaitAll(auth, TestTimeoutSpan); + bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds); return finished; } } diff --git a/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index 5d534cd799bf..3a25157e4820 100644 --- a/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -22,6 +22,7 @@ + diff --git a/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs b/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs index 52967cab76d7..d22523f9b128 100644 --- a/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs +++ b/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs @@ -11,8 +11,20 @@ namespace System.Net.Security.Tests { internal static class TestConfiguration { - public const int TestTimeoutSeconds = 10; + public const int PassingTestTimeoutMilliseconds = 15 * 1000; + public const int FailingTestTimeoutMiliseconds = 250; + public const SslProtocols DefaultSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; + + public const SslProtocols SupportedSslProtocols = + SslProtocols.Tls + | SslProtocols.Tls11 + | SslProtocols.Tls12; + + public const SslProtocols UnsupportedSslProtocols = + SslProtocols.Ssl2 + | SslProtocols.Ssl3; + public const string HttpsTestServer = "corefx-networking.azurewebsites.net"; private const string CertificatePassword = "testcertificate";