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;