From 2463cb80994d63f0d57efc4004cd86a739942721 Mon Sep 17 00:00:00 2001 From: David Engel Date: Wed, 18 Oct 2023 23:44:12 +0000 Subject: [PATCH] Merged PR 4072: [2.1.7] --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 113 ++++---- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 272 +++++++++--------- .../SqlConnectionBasicTests.cs | 20 ++ .../tests/FunctionalTests/TestTdsServer.cs | 16 +- .../tools/TDS/TDS.EndPoint/TDSServerParser.cs | 12 + .../TDS/TDS/PreLogin/TDSPreLoginToken.cs | 5 +- .../TDSPreLoginTokenEncryptionType.cs | 3 +- .../tests/tools/TDS/TDS/TDSEncryptionType.cs | 7 +- .../tests/tools/TDS/TDS/TDSUtilities.cs | 12 + 9 files changed, 272 insertions(+), 188 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 617e4f337c..0d72018bb2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -864,6 +864,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus int payloadOffset = 0; int payloadLength = 0; int option = payload[offset++]; + bool serverSupportsEncryption = false; while (option != (byte)PreLoginOptions.LASTOPT) { @@ -900,18 +901,11 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus LOGIN } */ + // Any response other than NOT_SUP means the server supports encryption. + serverSupportsEncryption = serverOption != EncryptionOptions.NOT_SUP; + switch (_encryptionOption) { - case (EncryptionOptions.ON): - if (serverOption == EncryptionOptions.NOT_SUP) - { - _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); - _physicalStateObj.Dispose(); - ThrowExceptionAndWarning(_physicalStateObj); - } - - break; - case (EncryptionOptions.OFF): if (serverOption == EncryptionOptions.OFF) { @@ -929,6 +923,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus case (EncryptionOptions.NOT_SUP): if (serverOption == EncryptionOptions.REQ) { + // Server requires encryption, but client does not support it. _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); _physicalStateObj.Dispose(); ThrowExceptionAndWarning(_physicalStateObj); @@ -937,49 +932,15 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus break; default: - Debug.Fail("Invalid client encryption option detected"); - break; - } - - if (_encryptionOption == EncryptionOptions.ON || - _encryptionOption == EncryptionOptions.LOGIN) - { - uint error = 0; - - // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || (_connHandler._accessTokenInBytes != null && !trustServerCert); - uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) - | (isYukonOrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); - - if (encrypt && !integratedSecurity) - { - // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI - // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context. - // This applies to Native SNI - info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS; - } - - error = _physicalStateObj.EnableSsl(ref info); - - if (error != TdsEnums.SNI_SUCCESS) - { - _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); - ThrowExceptionAndWarning(_physicalStateObj); - } - - int protocolVersion = 0; - WaitForSSLHandShakeToComplete(ref error, ref protocolVersion); - - SslProtocols protocol = (SslProtocols)protocolVersion; - string warningMessage = protocol.GetProtocolWarning(); - if (!string.IsNullOrEmpty(warningMessage)) - { - // This logs console warning of insecure protocol in use. - _logger.LogWarning(_typeName, MethodBase.GetCurrentMethod().Name, warningMessage); - } + // Any other client option needs encryption + if (serverOption == EncryptionOptions.NOT_SUP) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } - // create a new packet encryption changes the internal packet size - _physicalStateObj.ClearAllWritePackets(); + break; } break; @@ -1062,6 +1023,54 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus } } + if (_encryptionOption == EncryptionOptions.ON || + _encryptionOption == EncryptionOptions.LOGIN) + { + if (!serverSupportsEncryption) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } + + uint error = 0; + + // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || (_connHandler._accessTokenInBytes != null && !trustServerCert); + uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) + | (isYukonOrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); + + if (encrypt && !integratedSecurity) + { + // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI + // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context. + // This applies to Native SNI + info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS; + } + + error = _physicalStateObj.EnableSsl(ref info); + + if (error != TdsEnums.SNI_SUCCESS) + { + _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + ThrowExceptionAndWarning(_physicalStateObj); + } + + int protocolVersion = 0; + WaitForSSLHandShakeToComplete(ref error, ref protocolVersion); + + SslProtocols protocol = (SslProtocols)protocolVersion; + string warningMessage = protocol.GetProtocolWarning(); + if (!string.IsNullOrEmpty(warningMessage)) + { + // This logs console warning of insecure protocol in use. + _logger.LogWarning(_typeName, MethodBase.GetCurrentMethod().Name, warningMessage); + } + + // create a new packet encryption changes the internal packet size + _physicalStateObj.ClearAllWritePackets(); + } + return PreLoginHandshakeStatus.Successful; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index a5bb6c74c6..10d94eabe2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -1159,6 +1159,8 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt, bool clien int payloadOffset = 0; int payloadLength = 0; int option = payload[offset++]; + bool serverSupportsEncryption = false; + bool serverSupportsCTAIP = false; while (option != (byte)PreLoginOptions.LASTOPT) { @@ -1191,20 +1193,17 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt, bool clien ON, NOT_SUP, REQ, - LOGIN - } */ - switch (_encryptionOption & EncryptionOptions.OPTIONS_MASK) - { - case (EncryptionOptions.ON): - if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.NOT_SUP) - { - _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); - _physicalStateObj.Dispose(); - ThrowExceptionAndWarning(_physicalStateObj); - } + LOGIN, + OPTIONS_MASK = 0x3f, + CTAIP = 0x40, + CLIENT_CERT = 0x80, + } */ - break; + // Any response other than NOT_SUP means the server supports encryption. + serverSupportsEncryption = (serverOption & EncryptionOptions.OPTIONS_MASK) != EncryptionOptions.NOT_SUP; + switch (_encryptionOption & EncryptionOptions.OPTIONS_MASK) + { case (EncryptionOptions.OFF): if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.OFF) { @@ -1222,6 +1221,7 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt, bool clien case (EncryptionOptions.NOT_SUP): if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.REQ) { + // Server requires encryption, but client does not support it. _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); _physicalStateObj.Dispose(); ThrowExceptionAndWarning(_physicalStateObj); @@ -1230,129 +1230,21 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt, bool clien break; default: - Debug.Fail("Invalid client encryption option detected"); - break; - } - - // Check if the server will accept CTAIP. - // - if ((_encryptionOption & EncryptionOptions.CTAIP) != 0 && - (serverOption & EncryptionOptions.CTAIP) == 0) - { - _physicalStateObj.AddError(new SqlError(TdsEnums.CTAIP_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.CTAIPNotSupportedByServer(), "", 0)); - _physicalStateObj.Dispose(); - ThrowExceptionAndWarning(_physicalStateObj); - } - - if ((_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.ON || - (_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.LOGIN) - { - - if (serverCallback != null) - { - trustServerCert = true; - } - - UInt32 error = 0; - - // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || _connHandler._accessTokenInBytes != null) && !trustServerCert); - - UInt32 info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) - | (isYukonOrLater && (_encryptionOption & EncryptionOptions.CLIENT_CERT) == 0 ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); - - if (encrypt && !integratedSecurity) - { - // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI - // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context. - info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS; - } - - // Add SSL (Encryption) SNI provider. - SNINativeMethodWrapper.AuthProviderInfo authInfo = new SNINativeMethodWrapper.AuthProviderInfo(); - authInfo.flags = info; - authInfo.certId = null; - authInfo.certHash = false; - authInfo.clientCertificateCallbackContext = IntPtr.Zero; - authInfo.clientCertificateCallback = null; - - if ((_encryptionOption & EncryptionOptions.CLIENT_CERT) != 0) - { - - string certificate = _connHandler.ConnectionOptions.Certificate; - - if (certificate.StartsWith("subject:", StringComparison.OrdinalIgnoreCase)) - { - authInfo.certId = certificate.Substring(8); - } - else if (certificate.StartsWith("sha1:", StringComparison.OrdinalIgnoreCase)) - { - authInfo.certId = certificate.Substring(5); - authInfo.certHash = true; - } - - if (clientCallback != null) - { - authInfo.clientCertificateCallbackContext = clientCallback; - authInfo.clientCertificateCallback = _clientCertificateCallback; - } - } - - error = SNINativeMethodWrapper.SNIAddProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, authInfo); - - if (error != TdsEnums.SNI_SUCCESS) - { - _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); - ThrowExceptionAndWarning(_physicalStateObj); - } - - // in the case where an async connection is made, encryption is used and Windows Authentication is used, - // wait for SSL handshake to complete, so that the SSL context is fully negotiated before we try to use its - // Channel Bindings as part of the Windows Authentication context build (SSL handshake must complete - // before calling SNISecGenClientContext). - error = SNINativeMethodWrapper.SNIWaitForSSLHandshakeToComplete(_physicalStateObj.Handle, _physicalStateObj.GetTimeoutRemaining(), out uint protocolVersion); - - if (error != TdsEnums.SNI_SUCCESS) - { - _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); - ThrowExceptionAndWarning(_physicalStateObj); - } - - string warningMessage = SslProtocolsHelper.GetProtocolWarning(protocolVersion); - if (!string.IsNullOrEmpty(warningMessage)) - { - // This logs console warning of insecure protocol in use. - _logger.LogWarning(_typeName, MethodBase.GetCurrentMethod().Name, warningMessage); - } - - // Validate server certificate - if (serverCallback != null) - { - X509Certificate2 serverCert = null; - - error = SNINativeMethodWrapper.SNISecGetServerCertificate(_physicalStateObj.Handle, ref serverCert); - if (error != TdsEnums.SNI_SUCCESS) + // Any other client option needs encryption + if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.NOT_SUP) { - _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); + _physicalStateObj.Dispose(); ThrowExceptionAndWarning(_physicalStateObj); } - bool valid = serverCallback(serverCert); - if (!valid) - { - throw SQL.InvalidServerCertificate(); - } - } - - // create a new packet encryption changes the internal packet size Bug# 228403 - try - { } // EmptyTry/Finally to avoid FXCop violation - finally - { - _physicalStateObj.ClearAllWritePackets(); - } + break; } + // Check if the server will accept CTAIP. + // + serverSupportsCTAIP = (serverOption & EncryptionOptions.CTAIP) != 0; + break; case (int)PreLoginOptions.INSTANCE: @@ -1434,6 +1326,128 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt, bool clien } } + if ((_encryptionOption & EncryptionOptions.CTAIP) != 0 && !serverSupportsCTAIP) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.CTAIP_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.CTAIPNotSupportedByServer(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } + + if ((_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.ON || + (_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.LOGIN) + { + if (!serverSupportsEncryption) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } + + if (serverCallback != null) + { + trustServerCert = true; + } + + UInt32 error = 0; + + // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || _connHandler._accessTokenInBytes != null) && !trustServerCert); + + UInt32 info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) + | (isYukonOrLater && (_encryptionOption & EncryptionOptions.CLIENT_CERT) == 0 ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); + + if (encrypt && !integratedSecurity) + { + // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI + // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context. + info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS; + } + + // Add SSL (Encryption) SNI provider. + SNINativeMethodWrapper.AuthProviderInfo authInfo = new SNINativeMethodWrapper.AuthProviderInfo(); + authInfo.flags = info; + authInfo.certId = null; + authInfo.certHash = false; + authInfo.clientCertificateCallbackContext = IntPtr.Zero; + authInfo.clientCertificateCallback = null; + + if ((_encryptionOption & EncryptionOptions.CLIENT_CERT) != 0) + { + + string certificate = _connHandler.ConnectionOptions.Certificate; + + if (certificate.StartsWith("subject:", StringComparison.OrdinalIgnoreCase)) + { + authInfo.certId = certificate.Substring(8); + } + else if (certificate.StartsWith("sha1:", StringComparison.OrdinalIgnoreCase)) + { + authInfo.certId = certificate.Substring(5); + authInfo.certHash = true; + } + + if (clientCallback != null) + { + authInfo.clientCertificateCallbackContext = clientCallback; + authInfo.clientCertificateCallback = _clientCertificateCallback; + } + } + + error = SNINativeMethodWrapper.SNIAddProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, authInfo); + + if (error != TdsEnums.SNI_SUCCESS) + { + _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + ThrowExceptionAndWarning(_physicalStateObj); + } + + // in the case where an async connection is made, encryption is used and Windows Authentication is used, + // wait for SSL handshake to complete, so that the SSL context is fully negotiated before we try to use its + // Channel Bindings as part of the Windows Authentication context build (SSL handshake must complete + // before calling SNISecGenClientContext). + error = SNINativeMethodWrapper.SNIWaitForSSLHandshakeToComplete(_physicalStateObj.Handle, _physicalStateObj.GetTimeoutRemaining(), out uint protocolVersion); + + if (error != TdsEnums.SNI_SUCCESS) + { + _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + ThrowExceptionAndWarning(_physicalStateObj); + } + + string warningMessage = SslProtocolsHelper.GetProtocolWarning(protocolVersion); + if (!string.IsNullOrEmpty(warningMessage)) + { + // This logs console warning of insecure protocol in use. + _logger.LogWarning(_typeName, MethodBase.GetCurrentMethod().Name, warningMessage); + } + + // Validate server certificate + if (serverCallback != null) + { + X509Certificate2 serverCert = null; + + error = SNINativeMethodWrapper.SNISecGetServerCertificate(_physicalStateObj.Handle, ref serverCert); + if (error != TdsEnums.SNI_SUCCESS) + { + _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + ThrowExceptionAndWarning(_physicalStateObj); + } + + bool valid = serverCallback(serverCert); + if (!valid) + { + throw SQL.InvalidServerCertificate(); + } + } + + // create a new packet encryption changes the internal packet size Bug# 228403 + try + { } // EmptyTry/Finally to avoid FXCop violation + finally + { + _physicalStateObj.ClearAllWritePackets(); + } + } + return PreLoginHandshakeStatus.Successful; } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index c21275ebde..01469fc003 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -6,6 +6,7 @@ using System.Data.Common; using System.Reflection; using System.Security; +using System.Threading.Tasks; using Xunit; namespace Microsoft.Data.SqlClient.Tests @@ -40,6 +41,25 @@ public void IntegratedAuthConnectionTest() } } + /// + /// Runs a test where TDS Server doesn't send encryption info during pre-login response. + /// The driver is expected to fail when that happens, and terminate the connection during pre-login phase + /// when client enables encryption using Encrypt=true or uses default encryption setting. + /// + [Fact] + public async Task PreLoginEncryptionExcludedTest() + { + using TestTdsServer server = TestTdsServer.StartTestServer(false, false, excludeEncryption: true); + SqlConnectionStringBuilder builder = new(server.ConnectionString) + { + IntegratedSecurity = true + }; + + using SqlConnection connection = new(builder.ConnectionString); + Exception ex = await Assert.ThrowsAsync(async () => await connection.OpenAsync()); + Assert.Contains("The instance of SQL Server you attempted to connect to does not support encryption.", ex.Message, StringComparison.OrdinalIgnoreCase); + } + [Fact] public void SqlConnectionDbProviderFactoryTest() { diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs index 4607a5dc93..e6426b8e0a 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs @@ -23,7 +23,7 @@ public TestTdsServer(QueryEngine engine, TDSServerArguments args) : base(args) this.Engine = engine; } - public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool enableFedAuth = false, bool enableLog = false, [CallerMemberName] string methodName = "") + public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool enableFedAuth = false, bool enableLog = false, bool excludeEncryption = false, [CallerMemberName] string methodName = "") { TDSServerArguments args = new TDSServerArguments() { @@ -35,6 +35,11 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool args.FedAuthRequiredPreLoginOption = Microsoft.SqlServer.TDS.PreLogin.TdsPreLoginFedAuthRequiredOption.FedAuthRequired; } + if (excludeEncryption) + { + args.Encryption = SqlServer.TDS.PreLogin.TDSPreLoginTokenEncryptionType.None; + } + TestTdsServer server = engine == null ? new TestTdsServer(args) : new TestTdsServer(engine, args); server._endpoint = new TDSServerEndPoint(server) { ServerEndPoint = new IPEndPoint(IPAddress.Any, 0) }; server._endpoint.EndpointName = methodName; @@ -43,14 +48,17 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool server._endpoint.Start(); int port = server._endpoint.ServerEndPoint.Port; - server.connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = 5, Encrypt = false }; + server.connectionStringBuilder = excludeEncryption + // Allow encryption to be set when encryption is to be excluded from pre-login response. + ? new SqlConnectionStringBuilder() { DataSource = "localhost," + port, Encrypt = true } + : new SqlConnectionStringBuilder() { DataSource = "localhost," + port, Encrypt = false }; server.ConnectionString = server.connectionStringBuilder.ConnectionString; return server; } - public static TestTdsServer StartTestServer(bool enableFedAuth = false, bool enableLog = false, [CallerMemberName] string methodName = "") + public static TestTdsServer StartTestServer(bool enableFedAuth = false, bool enableLog = false, bool excludeEncryption = false, [CallerMemberName] string methodName = "") { - return StartServerWithQueryEngine(null, false, false, methodName); + return StartServerWithQueryEngine(null, false, false, excludeEncryption, methodName); } public void Dispose() => _endpoint?.Stop(); 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 a637e6bcc4..019fefd907 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 @@ -4,6 +4,7 @@ using System; using System.IO; +using Microsoft.SqlServer.TDS.PreLogin; namespace Microsoft.SqlServer.TDS.EndPoint { @@ -68,8 +69,19 @@ public void Run() { case TDSMessageType.PreLogin: { + if (Session.Encryption == TDSEncryptionType.None) + { + (MessageBeingReceived[0] as TDSPreLoginToken).Encryption = TDSPreLoginTokenEncryptionType.None; + } + // Call into the subscriber to process the packet responseMessages = Server.OnPreLoginRequest(Session, MessageBeingReceived); + + if (Session.Encryption == TDSEncryptionType.None) + { + DisableTransportEncryption(); + } + break; } case TDSMessageType.TDS7Login: diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginToken.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginToken.cs index 7a5288f1c8..f5ca465fc0 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginToken.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginToken.cs @@ -331,7 +331,10 @@ public override void Deflate(Stream destination) options.Add(new TDSPreLoginTokenOption(TDSPreLoginTokenOptionType.NonceOption, (ushort)Nonce.Length)); } - options.Add(new TDSPreLoginTokenOption(TDSPreLoginTokenOptionType.Encryption, 1)); + if (Encryption != TDSPreLoginTokenEncryptionType.None) + { + options.Add(new TDSPreLoginTokenOption(TDSPreLoginTokenOptionType.Encryption, 1)); + } options.Add(new TDSPreLoginTokenOption(TDSPreLoginTokenOptionType.Terminator, 0)); // Calculate the total size of the token metadata diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginTokenEncryptionType.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginTokenEncryptionType.cs index fadb814579..82e69382e5 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginTokenEncryptionType.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/PreLogin/TDSPreLoginTokenEncryptionType.cs @@ -12,6 +12,7 @@ public enum TDSPreLoginTokenEncryptionType : byte Off = 0x00, On = 0x01, NotSupported = 0x02, - Required = 0x03 + Required = 0x03, + None = 0x10 } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSEncryptionType.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSEncryptionType.cs index 348605b894..4716273d73 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSEncryptionType.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSEncryptionType.cs @@ -22,6 +22,11 @@ public enum TDSEncryptionType /// /// Encryption of the entire session /// - Full + Full, + + /// + /// Excludes encryption option in Pre-Login response + /// + None } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSUtilities.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSUtilities.cs index a4bf123d6a..d8a0c292c5 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSUtilities.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSUtilities.cs @@ -261,11 +261,19 @@ public static TDSPreLoginTokenEncryptionType GetEncryptionResponse(TDSPreLoginTo { return TDSPreLoginTokenEncryptionType.On; } + else if (server == TDSPreLoginTokenEncryptionType.None) + { + return TDSPreLoginTokenEncryptionType.None; + } else { throw new ArgumentException("Server is configured to not support encryption", "server"); } } + else if (client == TDSPreLoginTokenEncryptionType.None) + { + return TDSPreLoginTokenEncryptionType.None; + } // This case is not documented so pick a default return TDSPreLoginTokenEncryptionType.Off; @@ -313,6 +321,10 @@ public static TDSEncryptionType ResolveEncryption(TDSPreLoginTokenEncryptionType return TDSEncryptionType.LoginOnly; } } + else if (client == TDSPreLoginTokenEncryptionType.None) + { + return TDSEncryptionType.None; + } // Full encryption is required return TDSEncryptionType.Full;