diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs index 1fa6c58a3f..9f50b85a7d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNILoadHandle.cs @@ -55,5 +55,11 @@ public EncryptionOptions Options return _encryptionOption; } } + + /// + /// Verify client encryption possibility + /// + // TODO: by adding support ENCRYPT_NOT_SUP, it could be calculated. + public bool ClientOSEncryptionSupport => true; } } 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 04ec52be41..1e55b8626a 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 @@ -47,6 +47,11 @@ internal sealed partial class TdsParser internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); internal int ObjectID => _objectID; + /// + /// Verify client encryption possibility. + /// + private bool ClientOSEncryptionSupport => TdsParserStateObjectFactory.Singleton.ClientOSEncryptionSupport; + // Default state object for parser internal TdsParserStateObject _physicalStateObj = null; // Default stateObj and connection for Dbnetlib and non-MARS SNI. @@ -464,6 +469,18 @@ internal void Connect( _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject); } + if (!ClientOSEncryptionSupport) + { + //If encryption is required, an error will be thrown. + if (encrypt) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } + _encryptionOption = EncryptionOptions.NOT_SUP; + } + SqlClientEventSource.Log.TryTraceEvent(" Sending prelogin handshake"); SendPreLoginHandshake(instanceName, encrypt); @@ -674,7 +691,7 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) case (int)PreLoginOptions.ENCRYPT: if (_encryptionOption == EncryptionOptions.NOT_SUP) { - // If OS doesn't support encryption, inform server not supported. + //If OS doesn't support encryption and encryption is not required, inform server "not supported" by client. payload[payloadLength] = (byte)EncryptionOptions.NOT_SUP; } else @@ -885,7 +902,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus // Encrypt all. _encryptionOption = EncryptionOptions.ON; } - + // NOT_SUP: No encryption. break; case (EncryptionOptions.NOT_SUP): diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs index 62411969ff..980e0d556c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs @@ -18,7 +18,8 @@ internal sealed partial class SNILoadHandle : SafeHandle internal readonly SNINativeMethodWrapper.SqlAsyncCallbackDelegate WriteAsyncCallbackDispatcher = new SNINativeMethodWrapper.SqlAsyncCallbackDelegate(WriteDispatcher); private readonly uint _sniStatus = TdsEnums.SNI_UNINITIALIZED; - private readonly EncryptionOptions _encryptionOption; + private readonly EncryptionOptions _encryptionOption = EncryptionOptions.OFF; + private bool? _clientOSEncryptionSupport = null; private SNILoadHandle() : base(IntPtr.Zero, true) { @@ -30,30 +31,41 @@ private SNILoadHandle() : base(IntPtr.Zero, true) finally { _sniStatus = SNINativeMethodWrapper.SNIInitialize(); - - uint value = 0; - - // VSDevDiv 479597: If initialize fails, don't call QueryInfo. - if (TdsEnums.SNI_SUCCESS == _sniStatus) - { - // Query OS to find out whether encryption is supported. - SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_CLIENT_ENCRYPT_POSSIBLE, ref value); - } - - _encryptionOption = (value == 0) ? EncryptionOptions.NOT_SUP : EncryptionOptions.OFF; - base.handle = (IntPtr)1; // Initialize to non-zero dummy variable. } } - public override bool IsInvalid + /// + /// Verify client encryption possibility. + /// + public bool ClientOSEncryptionSupport { get { - return (IntPtr.Zero == base.handle); + if (_clientOSEncryptionSupport is null) + { + // VSDevDiv 479597: If initialize fails, don't call QueryInfo. + if (TdsEnums.SNI_SUCCESS == _sniStatus) + { + try + { + UInt32 value = 0; + // Query OS to find out whether encryption is supported. + SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_CLIENT_ENCRYPT_POSSIBLE, ref value); + _clientOSEncryptionSupport = value != 0; + } + catch (Exception e) + { + SqlClientEventSource.Log.TryTraceEvent(" Exception occurs during resolving encryption possibility: {0}", e.Message); + } + } + } + return _clientOSEncryptionSupport.Value; } } + public override bool IsInvalid => (IntPtr.Zero == base.handle); + override protected bool ReleaseHandle() { if (base.handle != IntPtr.Zero) @@ -69,21 +81,9 @@ override protected bool ReleaseHandle() return true; } - public uint Status - { - get - { - return _sniStatus; - } - } + public uint Status => _sniStatus; - public EncryptionOptions Options - { - get - { - return _encryptionOption; - } - } + public EncryptionOptions Options => _encryptionOption; private static void ReadDispatcher(IntPtr key, IntPtr packet, uint error) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Managed.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Managed.cs index 5c6a463d7c..86bc3013a9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Managed.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Managed.cs @@ -13,21 +13,14 @@ internal sealed class TdsParserStateObjectFactory public static readonly TdsParserStateObjectFactory Singleton = new TdsParserStateObjectFactory(); - public EncryptionOptions EncryptionOptions - { - get - { - return SNI.SNILoadHandle.SingletonInstance.Options; - } - } + public EncryptionOptions EncryptionOptions => SNI.SNILoadHandle.SingletonInstance.Options; - public uint SNIStatus - { - get - { - return SNI.SNILoadHandle.SingletonInstance.Status; - } - } + public uint SNIStatus => SNI.SNILoadHandle.SingletonInstance.Status; + + /// + /// Verify client encryption possibility. + /// + public bool ClientOSEncryptionSupport => SNI.SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport; public TdsParserStateObject CreateTdsParserStateObject(TdsParser parser) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Windows.cs index add6430499..6785d7c2fd 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.Windows.cs @@ -16,24 +16,17 @@ internal sealed class TdsParserStateObjectFactory private static bool shouldUseManagedSNI; // If the appcontext switch is set then Use Managed SNI based on the value. Otherwise Native SNI.dll will be used by default. - public static bool UseManagedSNI { get; } = + public static bool UseManagedSNI => AppContext.TryGetSwitch(UseManagedNetworkingOnWindows, out shouldUseManagedSNI) ? shouldUseManagedSNI : false; - public EncryptionOptions EncryptionOptions - { - get - { - return UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.Options : SNILoadHandle.SingletonInstance.Options; - } - } + public EncryptionOptions EncryptionOptions => UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.Options : SNILoadHandle.SingletonInstance.Options; - public uint SNIStatus - { - get - { - return UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.Status : SNILoadHandle.SingletonInstance.Status; - } - } + public uint SNIStatus => UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.Status : SNILoadHandle.SingletonInstance.Status; + + /// + /// Verify client encryption possibility. + /// + public bool ClientOSEncryptionSupport => UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport : SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport; public TdsParserStateObject CreateTdsParserStateObject(TdsParser parser) { 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 f1a714f319..550065ba52 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 @@ -58,6 +58,10 @@ internal int ObjectID } } + /// + /// Verify client encryption possibility. + /// + private bool ClientOSEncryptionSupport => SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport; // ReliabilitySection Usage: // @@ -644,6 +648,18 @@ internal void Connect(ServerInfo serverInfo, // for DNS Caching phase 1 AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce); + if(!ClientOSEncryptionSupport) + { + //If encryption is required, an error will be thrown. + if (encrypt) + { + _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); + _physicalStateObj.Dispose(); + ThrowExceptionAndWarning(_physicalStateObj); + } + _encryptionOption = EncryptionOptions.NOT_SUP; + } + // UNDONE - send "" for instance now, need to fix later SqlClientEventSource.Log.TryTraceEvent(" Sending prelogin handshake"); SendPreLoginHandshake(instanceName, encrypt, !string.IsNullOrEmpty(certificate), useOriginalAddressInfo); @@ -683,8 +699,8 @@ internal void Connect(ServerInfo serverInfo, AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce); SendPreLoginHandshake(instanceName, encrypt, !string.IsNullOrEmpty(certificate), useOriginalAddressInfo); - status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, clientCallback, out marsCapable, - out _connHandler._fedAuthRequired); + status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, clientCallback, + out marsCapable, out _connHandler._fedAuthRequired); // Don't need to check for Sphinx failure, since we've already consumed // one pre-login packet and know we are connecting to Shiloh. @@ -983,7 +999,7 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt, bool clien case (int)PreLoginOptions.ENCRYPT: if (_encryptionOption == EncryptionOptions.NOT_SUP) { - // If OS doesn't support encryption, inform server not supported. + //If OS doesn't support encryption and encryption is not required, inform server "not supported" by client. payload[payloadLength] = (byte)EncryptionOptions.NOT_SUP; } else @@ -1189,7 +1205,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod switch (_encryptionOption & EncryptionOptions.OPTIONS_MASK) { case (EncryptionOptions.ON): - if (serverOption == EncryptionOptions.NOT_SUP) + 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(); @@ -1209,7 +1225,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod // Encrypt all. _encryptionOption = EncryptionOptions.ON | (_encryptionOption & ~EncryptionOptions.OPTIONS_MASK); } - + // NOT_SUP: No encryption. break; case (EncryptionOptions.NOT_SUP): diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs index b61ed1dd34..591bd4f0a7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs @@ -20,7 +20,8 @@ internal sealed class SNILoadHandle : SafeHandle internal readonly SNINativeMethodWrapper.SqlAsyncCallbackDelegate WriteAsyncCallbackDispatcher = new SNINativeMethodWrapper.SqlAsyncCallbackDelegate(WriteDispatcher); private readonly UInt32 _sniStatus = TdsEnums.SNI_UNINITIALIZED; - private readonly EncryptionOptions _encryptionOption; + private readonly EncryptionOptions _encryptionOption = EncryptionOptions.OFF; + private bool? _clientOSEncryptionSupport = null; private SNILoadHandle() : base(IntPtr.Zero, true) { @@ -32,32 +33,42 @@ private SNILoadHandle() : base(IntPtr.Zero, true) { } finally { - _sniStatus = SNINativeMethodWrapper.SNIInitialize(); - - UInt32 value = 0; - - // VSDevDiv 479597: If initialize fails, don't call QueryInfo. - if (TdsEnums.SNI_SUCCESS == _sniStatus) - { - // Query OS to find out whether encryption is supported. - SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_CLIENT_ENCRYPT_POSSIBLE, ref value); - } - - _encryptionOption = (value == 0) ? EncryptionOptions.NOT_SUP : EncryptionOptions.OFF; - base.handle = (IntPtr)1; // Initialize to non-zero dummy variable. } } - public override bool IsInvalid + /// + /// Verify client encryption possibility. + /// + public bool ClientOSEncryptionSupport { get { - return (IntPtr.Zero == base.handle); + if (_clientOSEncryptionSupport is null) + { + // VSDevDiv 479597: If initialize fails, don't call QueryInfo. + if (TdsEnums.SNI_SUCCESS == _sniStatus) + { + try + { + UInt32 value = 0; + // Query OS to find out whether encryption is supported. + SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_CLIENT_ENCRYPT_POSSIBLE, ref value); + _clientOSEncryptionSupport = value != 0; + } + catch (Exception e) + { + SqlClientEventSource.Log.TryTraceEvent(" Exception occurs during resolving encryption possibility: {0}", e.Message); + } + } + } + return _clientOSEncryptionSupport.Value; } } + public override bool IsInvalid => (IntPtr.Zero == base.handle); + override protected bool ReleaseHandle() { if (base.handle != IntPtr.Zero) @@ -73,21 +84,9 @@ override protected bool ReleaseHandle() return true; } - public UInt32 SNIStatus - { - get - { - return _sniStatus; - } - } + public UInt32 SNIStatus => _sniStatus; - public EncryptionOptions Options - { - get - { - return _encryptionOption; - } - } + public EncryptionOptions Options => _encryptionOption; static private void ReadDispatcher(IntPtr key, IntPtr packet, UInt32 error) {