Skip to content

Commit 4deb800

Browse files
committed
Merged PR 3992: [5.1.3]
1 parent 3fd439b commit 4deb800

File tree

9 files changed

+169
-72
lines changed

9 files changed

+169
-72
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs

+39-22
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(
973973
int payloadOffset = 0;
974974
int payloadLength = 0;
975975
int option = payload[offset++];
976+
bool serverSupportsEncryption = false;
976977

977978
while (option != (byte)PreLoginOptions.LASTOPT)
978979
{
@@ -996,6 +997,13 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(
996997
break;
997998

998999
case (int)PreLoginOptions.ENCRYPT:
1000+
if (tlsFirst)
1001+
{
1002+
// Can skip/ignore this option if we are doing TDS 8.
1003+
offset += 4;
1004+
break;
1005+
}
1006+
9991007
payloadOffset = payload[offset++] << 8 | payload[offset++];
10001008
payloadLength = payload[offset++] << 8 | payload[offset++];
10011009

@@ -1009,16 +1017,11 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(
10091017
LOGIN
10101018
} */
10111019

1020+
// Any response other than NOT_SUP means the server supports encryption.
1021+
serverSupportsEncryption = serverOption != EncryptionOptions.NOT_SUP;
1022+
10121023
switch (_encryptionOption)
10131024
{
1014-
case (EncryptionOptions.ON):
1015-
if (serverOption == EncryptionOptions.NOT_SUP)
1016-
{
1017-
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
1018-
_physicalStateObj.Dispose();
1019-
ThrowExceptionAndWarning(_physicalStateObj);
1020-
}
1021-
break;
10221025
case (EncryptionOptions.OFF):
10231026
if (serverOption == EncryptionOptions.OFF)
10241027
{
@@ -1034,31 +1037,26 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(
10341037
break;
10351038

10361039
case (EncryptionOptions.NOT_SUP):
1037-
if (!tlsFirst && serverOption == EncryptionOptions.REQ)
1040+
if (serverOption == EncryptionOptions.REQ)
10381041
{
1042+
// Server requires encryption, but client does not support it.
10391043
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
10401044
_physicalStateObj.Dispose();
10411045
ThrowExceptionAndWarning(_physicalStateObj);
10421046
}
10431047

10441048
break;
10451049
default:
1046-
Debug.Fail("Invalid client encryption option detected");
1050+
// Any other client option needs encryption
1051+
if (serverOption == EncryptionOptions.NOT_SUP)
1052+
{
1053+
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
1054+
_physicalStateObj.Dispose();
1055+
ThrowExceptionAndWarning(_physicalStateObj);
1056+
}
10471057
break;
10481058
}
10491059

1050-
if (_encryptionOption == EncryptionOptions.ON ||
1051-
_encryptionOption == EncryptionOptions.LOGIN)
1052-
{
1053-
// Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server.
1054-
bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) ||
1055-
(_connHandler._accessTokenInBytes != null && !trustServerCert);
1056-
uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0)
1057-
| (is2005OrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0);
1058-
1059-
EnableSsl(info, encrypt, integratedSecurity, serverCert);
1060-
}
1061-
10621060
break;
10631061

10641062
case (int)PreLoginOptions.INSTANCE:
@@ -1139,6 +1137,25 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(
11391137
}
11401138
}
11411139

1140+
if (_encryptionOption == EncryptionOptions.ON ||
1141+
_encryptionOption == EncryptionOptions.LOGIN)
1142+
{
1143+
if (!serverSupportsEncryption)
1144+
{
1145+
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
1146+
_physicalStateObj.Dispose();
1147+
ThrowExceptionAndWarning(_physicalStateObj);
1148+
}
1149+
1150+
// Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server.
1151+
bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) ||
1152+
(_connHandler._accessTokenInBytes != null && !trustServerCert);
1153+
uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0)
1154+
| (is2005OrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0);
1155+
1156+
EnableSsl(info, encrypt, integratedSecurity, serverCert);
1157+
}
1158+
11421159
return PreLoginHandshakeStatus.Successful;
11431160
}
11441161

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs

+64-43
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,8 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(
13921392
int payloadOffset = 0;
13931393
int payloadLength = 0;
13941394
int option = payload[offset++];
1395+
bool serverSupportsEncryption = false;
1396+
bool serverSupportsCTAIP = false;
13951397

13961398
while (option != (byte)PreLoginOptions.LASTOPT)
13971399
{
@@ -1415,29 +1417,33 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(
14151417
break;
14161418

14171419
case (int)PreLoginOptions.ENCRYPT:
1420+
if (tlsFirst)
1421+
{
1422+
// Can skip/ignore this option if we are doing TDS 8.
1423+
offset += 4;
1424+
break;
1425+
}
1426+
14181427
payloadOffset = payload[offset++] << 8 | payload[offset++];
14191428
payloadLength = payload[offset++] << 8 | payload[offset++];
14201429

14211430
EncryptionOptions serverOption = (EncryptionOptions)payload[payloadOffset];
14221431
/* internal enum EncryptionOptions {
1423-
OFF,
1424-
ON,
1425-
NOT_SUP,
1426-
REQ,
1427-
LOGIN
1428-
} */
1432+
OFF,
1433+
ON,
1434+
NOT_SUP,
1435+
REQ,
1436+
LOGIN,
1437+
OPTIONS_MASK = 0x3f,
1438+
CTAIP = 0x40,
1439+
CLIENT_CERT = 0x80,
1440+
} */
1441+
1442+
// Any response other than NOT_SUP means the server supports encryption.
1443+
serverSupportsEncryption = (serverOption & EncryptionOptions.OPTIONS_MASK) != EncryptionOptions.NOT_SUP;
1444+
14291445
switch (_encryptionOption & EncryptionOptions.OPTIONS_MASK)
14301446
{
1431-
case (EncryptionOptions.ON):
1432-
if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.NOT_SUP)
1433-
{
1434-
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
1435-
_physicalStateObj.Dispose();
1436-
ThrowExceptionAndWarning(_physicalStateObj);
1437-
}
1438-
1439-
break;
1440-
14411447
case (EncryptionOptions.OFF):
14421448
if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.OFF)
14431449
{
@@ -1453,8 +1459,9 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(
14531459
break;
14541460

14551461
case (EncryptionOptions.NOT_SUP):
1456-
if (!tlsFirst && (serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.REQ)
1462+
if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.REQ)
14571463
{
1464+
// Server requires encryption, but client does not support it.
14581465
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
14591466
_physicalStateObj.Dispose();
14601467
ThrowExceptionAndWarning(_physicalStateObj);
@@ -1463,37 +1470,20 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(
14631470
break;
14641471

14651472
default:
1466-
Debug.Fail("Invalid client encryption option detected");
1473+
// Any other client option needs encryption
1474+
if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.NOT_SUP)
1475+
{
1476+
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
1477+
_physicalStateObj.Dispose();
1478+
ThrowExceptionAndWarning(_physicalStateObj);
1479+
}
1480+
14671481
break;
14681482
}
14691483

14701484
// Check if the server will accept CTAIP.
14711485
//
1472-
if ((_encryptionOption & EncryptionOptions.CTAIP) != 0 &&
1473-
(serverOption & EncryptionOptions.CTAIP) == 0)
1474-
{
1475-
_physicalStateObj.AddError(new SqlError(TdsEnums.CTAIP_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.CTAIPNotSupportedByServer(), "", 0));
1476-
_physicalStateObj.Dispose();
1477-
ThrowExceptionAndWarning(_physicalStateObj);
1478-
}
1479-
1480-
if ((_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.ON ||
1481-
(_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.LOGIN)
1482-
{
1483-
1484-
if (serverCallback != null)
1485-
{
1486-
trustServerCert = true;
1487-
}
1488-
1489-
// Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server.
1490-
bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || _connHandler._accessTokenInBytes != null) && !trustServerCert);
1491-
1492-
UInt32 info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0)
1493-
| (is2005OrLater && (_encryptionOption & EncryptionOptions.CLIENT_CERT) == 0 ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0);
1494-
1495-
EnableSsl(info, encrypt, integratedSecurity, serverCertificateFilename, serverCallback, clientCallback);
1496-
}
1486+
serverSupportsCTAIP = (serverOption & EncryptionOptions.CTAIP) != 0;
14971487

14981488
break;
14991489

@@ -1576,6 +1566,37 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(
15761566
}
15771567
}
15781568

1569+
if ((_encryptionOption & EncryptionOptions.CTAIP) != 0 && !serverSupportsCTAIP)
1570+
{
1571+
_physicalStateObj.AddError(new SqlError(TdsEnums.CTAIP_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.CTAIPNotSupportedByServer(), "", 0));
1572+
_physicalStateObj.Dispose();
1573+
ThrowExceptionAndWarning(_physicalStateObj);
1574+
}
1575+
1576+
if ((_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.ON ||
1577+
(_encryptionOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.LOGIN)
1578+
{
1579+
if (!serverSupportsEncryption)
1580+
{
1581+
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
1582+
_physicalStateObj.Dispose();
1583+
ThrowExceptionAndWarning(_physicalStateObj);
1584+
}
1585+
1586+
if (serverCallback != null)
1587+
{
1588+
trustServerCert = true;
1589+
}
1590+
1591+
// Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server.
1592+
bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || _connHandler._accessTokenInBytes != null) && !trustServerCert);
1593+
1594+
uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0)
1595+
| (is2005OrLater && (_encryptionOption & EncryptionOptions.CLIENT_CERT) == 0 ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0);
1596+
1597+
EnableSsl(info, encrypt, integratedSecurity, serverCertificateFilename, serverCallback, clientCallback);
1598+
}
1599+
15791600
return PreLoginHandshakeStatus.Successful;
15801601
}
15811602

src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs

+19
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,25 @@ public void IntegratedAuthConnectionTest()
3535
connection.Open();
3636
}
3737

38+
/// <summary>
39+
/// Runs a test where TDS Server doesn't send encryption info during pre-login response.
40+
/// The driver is expected to fail when that happens, and terminate the connection during pre-login phase
41+
/// when client enables encryption using Encrypt=true or uses default encryption setting.
42+
/// </summary>
43+
[Fact]
44+
public async Task PreLoginEncryptionExcludedTest()
45+
{
46+
using TestTdsServer server = TestTdsServer.StartTestServer(false, false, 5, excludeEncryption: true);
47+
SqlConnectionStringBuilder builder = new(server.ConnectionString)
48+
{
49+
IntegratedSecurity = true
50+
};
51+
52+
using SqlConnection connection = new(builder.ConnectionString);
53+
Exception ex = await Assert.ThrowsAsync<SqlException>(async () => await connection.OpenAsync());
54+
Assert.Contains("The instance of SQL Server you attempted to connect to does not support encryption.", ex.Message, StringComparison.OrdinalIgnoreCase);
55+
}
56+
3857
[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotArmProcess))]
3958
[InlineData(40613)]
4059
[InlineData(42108)]

src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs

+11-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public TestTdsServer(QueryEngine engine, TDSServerArguments args) : base(args)
2525
Engine = engine;
2626
}
2727

28-
public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, [CallerMemberName] string methodName = "")
28+
public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, bool excludeEncryption = false, [CallerMemberName] string methodName = "")
2929
{
3030
TDSServerArguments args = new TDSServerArguments()
3131
{
@@ -36,6 +36,10 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool
3636
{
3737
args.FedAuthRequiredPreLoginOption = SqlServer.TDS.PreLogin.TdsPreLoginFedAuthRequiredOption.FedAuthRequired;
3838
}
39+
if (excludeEncryption)
40+
{
41+
args.Encryption = SqlServer.TDS.PreLogin.TDSPreLoginTokenEncryptionType.None;
42+
}
3943

4044
TestTdsServer server = engine == null ? new TestTdsServer(args) : new TestTdsServer(engine, args);
4145
server._endpoint = new TDSServerEndPoint(server) { ServerEndPoint = new IPEndPoint(IPAddress.Any, 0) };
@@ -45,14 +49,17 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool
4549
server._endpoint.Start();
4650

4751
int port = server._endpoint.ServerEndPoint.Port;
48-
server._connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = connectionTimeout, Encrypt = SqlConnectionEncryptOption.Optional };
52+
server._connectionStringBuilder = excludeEncryption
53+
// Allow encryption to be set when encryption is to be excluded from pre-login response.
54+
? new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = connectionTimeout, Encrypt = SqlConnectionEncryptOption.Mandatory }
55+
: new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = connectionTimeout, Encrypt = SqlConnectionEncryptOption.Optional };
4956
server.ConnectionString = server._connectionStringBuilder.ConnectionString;
5057
return server;
5158
}
5259

53-
public static TestTdsServer StartTestServer(bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, [CallerMemberName] string methodName = "")
60+
public static TestTdsServer StartTestServer(bool enableFedAuth = false, bool enableLog = false, int connectionTimeout = DefaultConnectionTimeout, bool excludeEncryption = false, [CallerMemberName] string methodName = "")
5461
{
55-
return StartServerWithQueryEngine(null, enableFedAuth, enableLog, connectionTimeout, methodName);
62+
return StartServerWithQueryEngine(null, enableFedAuth, enableLog, connectionTimeout, excludeEncryption, methodName);
5663
}
5764

5865
public void Dispose() => _endpoint?.Stop();

0 commit comments

Comments
 (0)