From d92016b305c9c99dd84b8e4b9d37b25db58b8f08 Mon Sep 17 00:00:00 2001 From: Edward Neal <55035479+edwardneal@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:57:17 -0600 Subject: [PATCH] Fix down-level SSL/TLS version warnings (#3126) * Added test for downlevel connectivity warning * Correctly test bit flags for legacy SSL protocol warning * Corrected warning disablement/restore. (cherry picked from commit 198b906489097dfe87b89228a4b404819b0842fe) --- .../Data/SqlClient/TdsParserHelperClasses.cs | 10 +++---- .../DataCommon/ConnectionTestParameters.cs | 7 +++++ .../ConnectionTestParametersData.cs | 29 ++++++++++++++----- .../CertificateTestWithTdsServer.cs | 1 + .../ManualTests/TracingTests/TestTdsServer.cs | 8 +++-- .../TDS/TDS.EndPoint/ITDSServerSession.cs | 6 ++++ .../tests/tools/TDS/TDS.EndPoint/TDSParser.cs | 19 ++---------- .../tools/TDS/TDS.EndPoint/TDSServerParser.cs | 2 +- .../tools/TDS/TDS.Servers/GenericTDSServer.cs | 3 +- .../TDS.Servers/GenericTDSServerSession.cs | 6 ++++ .../TDS/TDS.Servers/TDSServerArguments.cs | 7 +++++ 11 files changed, 64 insertions(+), 34 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs index 3f4186186b..0effda9eaa 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs @@ -768,18 +768,18 @@ private static string ToFriendlyName(this SslProtocols protocol) name = "TLS 1.3"; }*/ #pragma warning disable CA5398 // Avoid hardcoded SslProtocols values - if ((protocol & SslProtocols.Tls12) == SslProtocols.Tls12) + if ((protocol & SslProtocols.Tls12) != SslProtocols.None) { name = "TLS 1.2"; } #if NET #pragma warning disable SYSLIB0039 // Type or member is obsolete: TLS 1.0 & 1.1 are deprecated #endif - else if ((protocol & SslProtocols.Tls11) == SslProtocols.Tls11) + else if ((protocol & SslProtocols.Tls11) != SslProtocols.None) { name = "TLS 1.1"; } - else if ((protocol & SslProtocols.Tls) == SslProtocols.Tls) + else if ((protocol & SslProtocols.Tls) != SslProtocols.None) { name = "TLS 1.0"; } @@ -787,11 +787,11 @@ private static string ToFriendlyName(this SslProtocols protocol) #pragma warning restore SYSLIB0039 // Type or member is obsolete: SSL and TLS 1.0 & 1.1 is deprecated #endif #pragma warning disable CS0618 // Type or member is obsolete: SSL is deprecated - else if ((protocol & SslProtocols.Ssl3) == SslProtocols.Ssl3) + else if ((protocol & SslProtocols.Ssl3) != SslProtocols.None) { name = "SSL 3.0"; } - else if ((protocol & SslProtocols.Ssl2) == SslProtocols.Ssl2) + else if ((protocol & SslProtocols.Ssl2) != SslProtocols.None) #pragma warning restore CS0618 // Type or member is obsolete: SSL and TLS 1.0 & 1.1 is deprecated { name = "SSL 2.0"; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ConnectionTestParameters.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ConnectionTestParameters.cs index 4b6e7b087b..a1e3134762 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ConnectionTestParameters.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ConnectionTestParameters.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Authentication; using System.Text; using System.Threading.Tasks; using Microsoft.SqlServer.TDS.PreLogin; @@ -26,8 +27,13 @@ public class ConnectionTestParameters public string HostNameInCertificate => _hnic; public bool TestResult => _result; public TDSPreLoginTokenEncryptionType TdsEncryptionType => _encryptionType; + public SslProtocols EncryptionProtocols { get; } public ConnectionTestParameters(TDSPreLoginTokenEncryptionType tdsEncryptionType, SqlConnectionEncryptOption encryptOption, bool trustServerCert, string cert, string hnic, bool result) + : this(tdsEncryptionType, encryptOption, trustServerCert, cert, hnic, SslProtocols.Tls12, result) + { } + + public ConnectionTestParameters(TDSPreLoginTokenEncryptionType tdsEncryptionType, SqlConnectionEncryptOption encryptOption, bool trustServerCert, string cert, string hnic, SslProtocols sslProtocols, bool result) { _encryptionOption = encryptOption; _trustServerCert = trustServerCert; @@ -35,6 +41,7 @@ public ConnectionTestParameters(TDSPreLoginTokenEncryptionType tdsEncryptionType _hnic = hnic; _result = result; _encryptionType = tdsEncryptionType; + EncryptionProtocols = sslProtocols; } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ConnectionTestParametersData.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ConnectionTestParametersData.cs index 5a2e01a77c..e35818e0e2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ConnectionTestParametersData.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/ConnectionTestParametersData.cs @@ -4,13 +4,13 @@ using System.Collections.Generic; using System.IO; +using System.Security.Authentication; using Microsoft.SqlServer.TDS.PreLogin; namespace Microsoft.Data.SqlClient.ManualTesting.Tests.DataCommon { public class ConnectionTestParametersData { - private const int CASES = 30; private string _empty = string.Empty; // It was advised to store the client certificate in its own folder. private static readonly string s_fullPathToCer = Path.Combine(Directory.GetCurrentDirectory(), "clientcert", "localhostcert.cer"); @@ -22,7 +22,7 @@ public class ConnectionTestParametersData public static IEnumerable GetConnectionTestParameters() { - for (int i = 0; i < CASES; i++) + for (int i = 0; i < Data.ConnectionTestParametersList.Count; i++) { yield return new object[] { Data.ConnectionTestParametersList[i] }; } @@ -33,11 +33,11 @@ public ConnectionTestParametersData() // Test cases possible field values for connection parameters: // These combinations are based on the possible values of Encrypt, TrustServerCertificate, Certificate, HostNameInCertificate /* - * TDSEncryption | Encrypt | TrustServerCertificate | Certificate | HNIC | TestResults - * ---------------------------------------------------------------------------------------------- - * Off | Optional | true | valid | valid name | true - * On | Mandatory | false | mismatched | empty | false - * Required | | x | ChainError? | wrong name? | + * TDSEncryption | Encrypt | TrustServerCertificate | Certificate | HNIC | SSL Protocols | TestResults + * --------------------------------------------------------------------------------------------------------------- + * Off | Optional | true | valid | valid name | TLS 1.2 | true + * On | Mandatory | false | mismatched | empty | TLS 1.0, TLS 1.1 | false + * Required | | x | ChainError? | wrong name? | | */ ConnectionTestParametersList = new List { @@ -79,6 +79,21 @@ public ConnectionTestParametersData() new(TDSPreLoginTokenEncryptionType.On, SqlConnectionEncryptOption.Mandatory, true, s_mismatchedcert, _empty, true), new(TDSPreLoginTokenEncryptionType.Required, SqlConnectionEncryptOption.Mandatory, false, s_mismatchedcert, _empty, false), new(TDSPreLoginTokenEncryptionType.Required, SqlConnectionEncryptOption.Mandatory, true, s_mismatchedcert, _empty, true), + + // Multiple SSL protocols test +#pragma warning disable CA5397 // Do not use deprecated SslProtocols values +#pragma warning disable CA5398 // Avoid hardcoded SslProtocols values +#if NET +#pragma warning disable SYSLIB0039 // Type or member is obsolete: TLS 1.0 & 1.1 are deprecated +#endif + new(TDSPreLoginTokenEncryptionType.Off, SqlConnectionEncryptOption.Mandatory, false, s_fullPathToCer, _empty, SslProtocols.Tls | SslProtocols.Tls11, true), + new(TDSPreLoginTokenEncryptionType.On, SqlConnectionEncryptOption.Mandatory, false, s_fullPathToCer, _empty, SslProtocols.Tls | SslProtocols.Tls11, true), + new(TDSPreLoginTokenEncryptionType.Required, SqlConnectionEncryptOption.Mandatory, false, s_fullPathToCer, _empty, SslProtocols.Tls | SslProtocols.Tls11, true), +#if NET +#pragma warning restore SYSLIB0039 // Type or member is obsolete: TLS 1.0 & 1.1 are deprecated +#endif +#pragma warning restore CA5397 // Do not use deprecated SslProtocols values +#pragma warning restore CA5398 // Avoid hardcoded SslProtocols values }; } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionTestWithSSLCert/CertificateTestWithTdsServer.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionTestWithSSLCert/CertificateTestWithTdsServer.cs index 361f1b0538..385390c188 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionTestWithSSLCert/CertificateTestWithTdsServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionTestWithSSLCert/CertificateTestWithTdsServer.cs @@ -127,6 +127,7 @@ private void ConnectionTest(ConnectionTestParameters connectionTestParameters) #else new X509Certificate2(s_fullPathToPfx, "nopassword", X509KeyStorageFlags.UserKeySet), #endif + encryptionProtocols: connectionTestParameters.EncryptionProtocols, encryptionType: connectionTestParameters.TdsEncryptionType); builder = new(server.ConnectionString) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs index a4557d72b6..27904fdfef 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs @@ -7,6 +7,7 @@ using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; +using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using Microsoft.SqlServer.TDS.EndPoint; using Microsoft.SqlServer.TDS.PreLogin; @@ -31,7 +32,7 @@ public TestTdsServer(QueryEngine engine, TDSServerArguments args) : base(args) public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, [CallerMemberName] string methodName = "", - X509Certificate2 encryptionCertificate = null, TDSPreLoginTokenEncryptionType encryptionType = TDSPreLoginTokenEncryptionType.NotSupported) + X509Certificate2 encryptionCertificate = null, SslProtocols encryptionProtocols = SslProtocols.Tls12, TDSPreLoginTokenEncryptionType encryptionType = TDSPreLoginTokenEncryptionType.NotSupported) { TDSServerArguments args = new TDSServerArguments() { @@ -48,6 +49,7 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool args.EncryptionCertificate = encryptionCertificate; } + args.EncryptionProtocols = encryptionProtocols; args.Encryption = encryptionType; TestTdsServer server = engine == null ? new TestTdsServer(args) : new TestTdsServer(engine, args); @@ -83,9 +85,9 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool public static TestTdsServer StartTestServer(bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, [CallerMemberName] string methodName = "", - X509Certificate2 encryptionCertificate = null, TDSPreLoginTokenEncryptionType encryptionType = TDSPreLoginTokenEncryptionType.NotSupported) + X509Certificate2 encryptionCertificate = null, SslProtocols encryptionProtocols = SslProtocols.Tls12, TDSPreLoginTokenEncryptionType encryptionType = TDSPreLoginTokenEncryptionType.NotSupported) { - return StartServerWithQueryEngine(null, enableFedAuth, enableLog, connectionTimeout, methodName, encryptionCertificate, encryptionType); + return StartServerWithQueryEngine(null, enableFedAuth, enableLog, connectionTimeout, methodName, encryptionCertificate, encryptionProtocols, encryptionType); } public void Dispose() => _endpoint?.Stop(); diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/ITDSServerSession.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/ITDSServerSession.cs index bbe039d426..ee4c7f169f 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/ITDSServerSession.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/ITDSServerSession.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using Microsoft.SqlServer.TDS.EndPoint.SSPI; @@ -68,6 +69,11 @@ public interface ITDSServerSession /// X509Certificate EncryptionCertificate { get; } + /// + /// SSL/TLS protocols to use for transport encryption + /// + SslProtocols EncryptionProtocols { get; } + /// /// Counter of connection reset requests for this session /// diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSParser.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSParser.cs index 2027c7bd7f..14ff37c7c5 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSParser.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSParser.cs @@ -25,11 +25,6 @@ public class TDSParser /// public TextWriter EventLog { get; set; } - /// - /// Encryption protocol for server to use with AuthenticateAsServer - /// - public static SslProtocols ServerSslProtocol { get; set; } - /// /// Protocol stream between the client and the server /// @@ -43,8 +38,6 @@ public TDSParser(Stream transport) // Save original transport _originalTransport = transport; - ServerSslProtocol = SslProtocols.Tls12; - // Wrap transport layer with TDS Transport = new TDSStream(transport, false); } @@ -57,14 +50,6 @@ public void SetTDSStreamPreWriteCallback(Func funcTDSS Transport.PreWriteCallBack = funcTDSStreamPreWriteCallBack; } - /// - /// Resets the targeted encryption protocol for the server. - /// - public static void ResetTargetProtocol() - { - ServerSslProtocol = SslProtocols.Tls12; - } - /// /// Enable transport encryption /// @@ -105,7 +90,7 @@ protected void EnableClientTransportEncryption(string server) /// /// Enable transport encryption /// - protected void EnableServerTransportEncryption(X509Certificate certificate) + protected void EnableServerTransportEncryption(X509Certificate certificate, SslProtocols encryptionProtocols) { // Check if transport encryption is applied if (Transport.InnerStream is SslStream) @@ -128,7 +113,7 @@ protected void EnableServerTransportEncryption(X509Certificate certificate) SslStream ssl = new SslStream(multiplexer, true); // Secure the channel - ssl.AuthenticateAsServer(certificate, false, ServerSslProtocol, false); + ssl.AuthenticateAsServer(certificate, false, encryptionProtocols, false); // Replace TDS stream with raw transport stream in multiplexer multiplexer.InnerStream = Transport.InnerStream; diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerParser.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerParser.cs index 019fefd907..05078d7e96 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerParser.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.EndPoint/TDSServerParser.cs @@ -146,7 +146,7 @@ public void Run() if (Session.Encryption == TDSEncryptionType.LoginOnly || Session.Encryption == TDSEncryptionType.Full) { // Enable server side encryption - EnableServerTransportEncryption(Session.EncryptionCertificate); + EnableServerTransportEncryption(Session.EncryptionCertificate, Session.EncryptionProtocols); } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs index 06cc2b7401..a214c51f88 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs @@ -85,8 +85,9 @@ public virtual ITDSServerSession OpenSession() // Create a new session GenericTDSServerSession session = new GenericTDSServerSession(this, (uint)_sessionCount); - // Use configured encryption certificate + // Use configured encryption certificate and protocols session.EncryptionCertificate = Arguments.EncryptionCertificate; + session.EncryptionProtocols = Arguments.EncryptionProtocols; return session; } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServerSession.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServerSession.cs index 3272036e15..c2cfde2e29 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServerSession.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServerSession.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using Microsoft.SqlServer.TDS.EndPoint; using Microsoft.SqlServer.TDS.EndPoint.SSPI; @@ -78,6 +79,11 @@ public class GenericTDSServerSession : ITDSServerSession /// public X509Certificate EncryptionCertificate { get; set; } + /// + /// SSL/TLS protocols to use for transport encryption + /// + public SslProtocols EncryptionProtocols { get; set; } + /// /// Nonce option sent by client /// diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs index 1543ebde63..d49e10416a 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/TDSServerArguments.cs @@ -4,6 +4,7 @@ using System; using System.IO; +using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using Microsoft.SqlServer.TDS.PreLogin; @@ -69,6 +70,11 @@ public class TDSServerArguments /// public X509Certificate EncryptionCertificate { get; set; } + /// + /// SSL/TLS protocols to use for transport encryption + /// + public SslProtocols EncryptionProtocols { get; set; } + /// /// Initialization constructor /// @@ -88,6 +94,7 @@ public TDSServerArguments() FedAuthRequiredPreLoginOption = TdsPreLoginFedAuthRequiredOption.FedAuthNotRequired; EncryptionCertificate = new X509Certificate2("TdsServerCertificate.pfx", "SecretPassword123456"); + EncryptionProtocols = SslProtocols.Tls12; ServerPrincipalName = AzureADServicePrincipalName; StsUrl = AzureADProductionTokenEndpoint;