Skip to content

Commit 2fd9646

Browse files
author
Davoud Eshtehari
committed
Fix encryption NOT_SUP issue(#1210)
1 parent f59df96 commit 2fd9646

File tree

7 files changed

+119
-95
lines changed

7 files changed

+119
-95
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,11 @@ public EncryptionOptions Options
5656
return _encryptionOption;
5757
}
5858
}
59+
60+
/// <summary>
61+
/// Verify client encryption possibility
62+
/// </summary>
63+
// TODO: by adding support ENCRYPT_NOT_SUP, it could be calculated.
64+
public bool ClientOSEncryptionSupport => true;
5965
}
6066
}

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ internal sealed partial class TdsParser
4747
internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount);
4848
internal int ObjectID => _objectID;
4949

50+
/// <summary>
51+
/// Verify client encryption possibility.
52+
/// </summary>
53+
private bool ClientOSEncryptionSupport => TdsParserStateObjectFactory.Singleton.ClientOSEncryptionSupport;
54+
5055
// Default state object for parser
5156
internal TdsParserStateObject _physicalStateObj = null; // Default stateObj and connection for Dbnetlib and non-MARS SNI.
5257

@@ -486,6 +491,18 @@ internal void Connect(
486491
_physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject);
487492
}
488493

494+
if (!ClientOSEncryptionSupport)
495+
{
496+
//If encryption is required, an error will be thrown.
497+
if (encrypt)
498+
{
499+
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
500+
_physicalStateObj.Dispose();
501+
ThrowExceptionAndWarning(_physicalStateObj);
502+
}
503+
_encryptionOption = EncryptionOptions.NOT_SUP;
504+
}
505+
489506
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.Connect|SEC> Sending prelogin handshake");
490507
SendPreLoginHandshake(instanceName, encrypt);
491508

@@ -695,7 +712,7 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt)
695712
case (int)PreLoginOptions.ENCRYPT:
696713
if (_encryptionOption == EncryptionOptions.NOT_SUP)
697714
{
698-
// If OS doesn't support encryption, inform server not supported.
715+
//If OS doesn't support encryption and encryption is not required, inform server "not supported" by client.
699716
payload[payloadLength] = (byte)EncryptionOptions.NOT_SUP;
700717
}
701718
else
@@ -906,7 +923,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus
906923
// Encrypt all.
907924
_encryptionOption = EncryptionOptions.ON;
908925
}
909-
926+
// NOT_SUP: No encryption.
910927
break;
911928

912929
case (EncryptionOptions.NOT_SUP):

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

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ internal sealed partial class SNILoadHandle : SafeHandle
1818
internal readonly SNINativeMethodWrapper.SqlAsyncCallbackDelegate WriteAsyncCallbackDispatcher = new SNINativeMethodWrapper.SqlAsyncCallbackDelegate(WriteDispatcher);
1919

2020
private readonly uint _sniStatus = TdsEnums.SNI_UNINITIALIZED;
21-
private readonly EncryptionOptions _encryptionOption;
21+
private readonly EncryptionOptions _encryptionOption = EncryptionOptions.OFF;
22+
private bool? _clientOSEncryptionSupport = null;
2223

2324
private SNILoadHandle() : base(IntPtr.Zero, true)
2425
{
@@ -30,30 +31,41 @@ private SNILoadHandle() : base(IntPtr.Zero, true)
3031
finally
3132
{
3233
_sniStatus = SNINativeMethodWrapper.SNIInitialize();
33-
34-
uint value = 0;
35-
36-
// VSDevDiv 479597: If initialize fails, don't call QueryInfo.
37-
if (TdsEnums.SNI_SUCCESS == _sniStatus)
38-
{
39-
// Query OS to find out whether encryption is supported.
40-
SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_CLIENT_ENCRYPT_POSSIBLE, ref value);
41-
}
42-
43-
_encryptionOption = (value == 0) ? EncryptionOptions.NOT_SUP : EncryptionOptions.OFF;
44-
4534
base.handle = (IntPtr)1; // Initialize to non-zero dummy variable.
4635
}
4736
}
4837

49-
public override bool IsInvalid
38+
/// <summary>
39+
/// Verify client encryption possibility.
40+
/// </summary>
41+
public bool ClientOSEncryptionSupport
5042
{
5143
get
5244
{
53-
return (IntPtr.Zero == base.handle);
45+
if (_clientOSEncryptionSupport is null)
46+
{
47+
// VSDevDiv 479597: If initialize fails, don't call QueryInfo.
48+
if (TdsEnums.SNI_SUCCESS == _sniStatus)
49+
{
50+
try
51+
{
52+
UInt32 value = 0;
53+
// Query OS to find out whether encryption is supported.
54+
SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_CLIENT_ENCRYPT_POSSIBLE, ref value);
55+
_clientOSEncryptionSupport = value != 0;
56+
}
57+
catch (Exception e)
58+
{
59+
SqlClientEventSource.Log.TryTraceEvent("<sc.SNILoadHandle.EncryptClientPossible|SEC> Exception occurs during resolving encryption possibility: {0}", e.Message);
60+
}
61+
}
62+
}
63+
return _clientOSEncryptionSupport.Value;
5464
}
5565
}
5666

67+
public override bool IsInvalid => (IntPtr.Zero == base.handle);
68+
5769
override protected bool ReleaseHandle()
5870
{
5971
if (base.handle != IntPtr.Zero)
@@ -69,21 +81,9 @@ override protected bool ReleaseHandle()
6981
return true;
7082
}
7183

72-
public uint Status
73-
{
74-
get
75-
{
76-
return _sniStatus;
77-
}
78-
}
84+
public uint Status => _sniStatus;
7985

80-
public EncryptionOptions Options
81-
{
82-
get
83-
{
84-
return _encryptionOption;
85-
}
86-
}
86+
public EncryptionOptions Options => _encryptionOption;
8787

8888
private static void ReadDispatcher(IntPtr key, IntPtr packet, uint error)
8989
{

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Managed.cs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,14 @@ internal sealed class TdsParserStateObjectFactory
1313

1414
public static readonly TdsParserStateObjectFactory Singleton = new TdsParserStateObjectFactory();
1515

16-
public EncryptionOptions EncryptionOptions
17-
{
18-
get
19-
{
20-
return SNI.SNILoadHandle.SingletonInstance.Options;
21-
}
22-
}
16+
public EncryptionOptions EncryptionOptions => SNI.SNILoadHandle.SingletonInstance.Options;
2317

24-
public uint SNIStatus
25-
{
26-
get
27-
{
28-
return SNI.SNILoadHandle.SingletonInstance.Status;
29-
}
30-
}
18+
public uint SNIStatus => SNI.SNILoadHandle.SingletonInstance.Status;
19+
20+
/// <summary>
21+
/// Verify client encryption possibility.
22+
/// </summary>
23+
public bool ClientOSEncryptionSupport => SNI.SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport;
3124

3225
public TdsParserStateObject CreateTdsParserStateObject(TdsParser parser)
3326
{

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Windows.cs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,17 @@ internal sealed class TdsParserStateObjectFactory
1616
private static bool shouldUseManagedSNI;
1717

1818
// If the appcontext switch is set then Use Managed SNI based on the value. Otherwise Native SNI.dll will be used by default.
19-
public static bool UseManagedSNI { get; } =
19+
public static bool UseManagedSNI =>
2020
AppContext.TryGetSwitch(UseManagedNetworkingOnWindows, out shouldUseManagedSNI) ? shouldUseManagedSNI : false;
2121

22-
public EncryptionOptions EncryptionOptions
23-
{
24-
get
25-
{
26-
return UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.Options : SNILoadHandle.SingletonInstance.Options;
27-
}
28-
}
22+
public EncryptionOptions EncryptionOptions => UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.Options : SNILoadHandle.SingletonInstance.Options;
2923

30-
public uint SNIStatus
31-
{
32-
get
33-
{
34-
return UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.Status : SNILoadHandle.SingletonInstance.Status;
35-
}
36-
}
24+
public uint SNIStatus => UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.Status : SNILoadHandle.SingletonInstance.Status;
25+
26+
/// <summary>
27+
/// Verify client encryption possibility.
28+
/// </summary>
29+
public bool ClientOSEncryptionSupport => UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport : SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport;
3730

3831
public TdsParserStateObject CreateTdsParserStateObject(TdsParser parser)
3932
{

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ internal int ObjectID
5858
}
5959
}
6060

61+
/// <summary>
62+
/// Verify client encryption possibility.
63+
/// </summary>
64+
private bool ClientOSEncryptionSupport => SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport;
6165

6266
// ReliabilitySection Usage:
6367
//
@@ -636,6 +640,18 @@ internal void Connect(ServerInfo serverInfo,
636640
// for DNS Caching phase 1
637641
AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce);
638642

643+
if(!ClientOSEncryptionSupport)
644+
{
645+
//If encryption is required, an error will be thrown.
646+
if (encrypt)
647+
{
648+
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0));
649+
_physicalStateObj.Dispose();
650+
ThrowExceptionAndWarning(_physicalStateObj);
651+
}
652+
_encryptionOption = EncryptionOptions.NOT_SUP;
653+
}
654+
639655
// UNDONE - send "" for instance now, need to fix later
640656
SqlClientEventSource.Log.TryTraceEvent("<sc.TdsParser.Connect|SEC> Sending prelogin handshake");
641657
SendPreLoginHandshake(instanceName, encrypt, !string.IsNullOrEmpty(certificate), useOriginalAddressInfo);
@@ -674,8 +690,8 @@ internal void Connect(ServerInfo serverInfo,
674690
AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce);
675691

676692
SendPreLoginHandshake(instanceName, encrypt, !string.IsNullOrEmpty(certificate), useOriginalAddressInfo);
677-
status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, clientCallback, out marsCapable,
678-
out _connHandler._fedAuthRequired);
693+
status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, clientCallback,
694+
out marsCapable, out _connHandler._fedAuthRequired);
679695

680696
// Don't need to check for Sphinx failure, since we've already consumed
681697
// one pre-login packet and know we are connecting to Shiloh.
@@ -974,7 +990,7 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt, bool clien
974990
case (int)PreLoginOptions.ENCRYPT:
975991
if (_encryptionOption == EncryptionOptions.NOT_SUP)
976992
{
977-
// If OS doesn't support encryption, inform server not supported.
993+
//If OS doesn't support encryption and encryption is not required, inform server "not supported" by client.
978994
payload[payloadLength] = (byte)EncryptionOptions.NOT_SUP;
979995
}
980996
else
@@ -1180,7 +1196,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod
11801196
switch (_encryptionOption & EncryptionOptions.OPTIONS_MASK)
11811197
{
11821198
case (EncryptionOptions.ON):
1183-
if (serverOption == EncryptionOptions.NOT_SUP)
1199+
if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.NOT_SUP)
11841200
{
11851201
_physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByServer(), "", 0));
11861202
_physicalStateObj.Dispose();
@@ -1200,7 +1216,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod
12001216
// Encrypt all.
12011217
_encryptionOption = EncryptionOptions.ON | (_encryptionOption & ~EncryptionOptions.OPTIONS_MASK);
12021218
}
1203-
1219+
// NOT_SUP: No encryption.
12041220
break;
12051221

12061222
case (EncryptionOptions.NOT_SUP):

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

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ internal sealed class SNILoadHandle : SafeHandle
2020
internal readonly SNINativeMethodWrapper.SqlAsyncCallbackDelegate WriteAsyncCallbackDispatcher = new SNINativeMethodWrapper.SqlAsyncCallbackDelegate(WriteDispatcher);
2121

2222
private readonly UInt32 _sniStatus = TdsEnums.SNI_UNINITIALIZED;
23-
private readonly EncryptionOptions _encryptionOption;
23+
private readonly EncryptionOptions _encryptionOption = EncryptionOptions.OFF;
24+
private bool? _clientOSEncryptionSupport = null;
2425

2526
private SNILoadHandle() : base(IntPtr.Zero, true)
2627
{
@@ -32,32 +33,42 @@ private SNILoadHandle() : base(IntPtr.Zero, true)
3233
{ }
3334
finally
3435
{
35-
3636
_sniStatus = SNINativeMethodWrapper.SNIInitialize();
37-
38-
UInt32 value = 0;
39-
40-
// VSDevDiv 479597: If initialize fails, don't call QueryInfo.
41-
if (TdsEnums.SNI_SUCCESS == _sniStatus)
42-
{
43-
// Query OS to find out whether encryption is supported.
44-
SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_CLIENT_ENCRYPT_POSSIBLE, ref value);
45-
}
46-
47-
_encryptionOption = (value == 0) ? EncryptionOptions.NOT_SUP : EncryptionOptions.OFF;
48-
4937
base.handle = (IntPtr)1; // Initialize to non-zero dummy variable.
5038
}
5139
}
5240

53-
public override bool IsInvalid
41+
/// <summary>
42+
/// Verify client encryption possibility.
43+
/// </summary>
44+
public bool ClientOSEncryptionSupport
5445
{
5546
get
5647
{
57-
return (IntPtr.Zero == base.handle);
48+
if (_clientOSEncryptionSupport is null)
49+
{
50+
// VSDevDiv 479597: If initialize fails, don't call QueryInfo.
51+
if (TdsEnums.SNI_SUCCESS == _sniStatus)
52+
{
53+
try
54+
{
55+
UInt32 value = 0;
56+
// Query OS to find out whether encryption is supported.
57+
SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_CLIENT_ENCRYPT_POSSIBLE, ref value);
58+
_clientOSEncryptionSupport = value != 0;
59+
}
60+
catch (Exception e)
61+
{
62+
SqlClientEventSource.Log.TryTraceEvent("<sc.SNILoadHandle.EncryptClientPossible|SEC> Exception occurs during resolving encryption possibility: {0}", e.Message);
63+
}
64+
}
65+
}
66+
return _clientOSEncryptionSupport.Value;
5867
}
5968
}
6069

70+
public override bool IsInvalid => (IntPtr.Zero == base.handle);
71+
6172
override protected bool ReleaseHandle()
6273
{
6374
if (base.handle != IntPtr.Zero)
@@ -73,21 +84,9 @@ override protected bool ReleaseHandle()
7384
return true;
7485
}
7586

76-
public UInt32 SNIStatus
77-
{
78-
get
79-
{
80-
return _sniStatus;
81-
}
82-
}
87+
public UInt32 SNIStatus => _sniStatus;
8388

84-
public EncryptionOptions Options
85-
{
86-
get
87-
{
88-
return _encryptionOption;
89-
}
90-
}
89+
public EncryptionOptions Options => _encryptionOption;
9190

9291
static private void ReadDispatcher(IntPtr key, IntPtr packet, UInt32 error)
9392
{

0 commit comments

Comments
 (0)