Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Release 3.0] Fix encryption NOT_SUP issue using Native SNI #1233

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,11 @@ public EncryptionOptions Options
return _encryptionOption;
}
}

/// <summary>
/// Verify client encryption possibility
/// </summary>
// TODO: by adding support ENCRYPT_NOT_SUP, it could be calculated.
public bool ClientOSEncryptionSupport => true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ internal sealed partial class TdsParser
internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount);
internal int ObjectID => _objectID;

/// <summary>
/// Verify client encryption possibility.
/// </summary>
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.

Expand Down Expand Up @@ -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("<sc.TdsParser.Connect|SEC> Sending prelogin handshake");
SendPreLoginHandshake(instanceName, encrypt);

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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
/// <summary>
/// Verify client encryption possibility.
/// </summary>
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("<sc.SNILoadHandle.EncryptClientPossible|SEC> 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)
Expand All @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// Verify client encryption possibility.
/// </summary>
public bool ClientOSEncryptionSupport => SNI.SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport;

public TdsParserStateObject CreateTdsParserStateObject(TdsParser parser)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// Verify client encryption possibility.
/// </summary>
public bool ClientOSEncryptionSupport => UseManagedSNI ? SNI.SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport : SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport;

public TdsParserStateObject CreateTdsParserStateObject(TdsParser parser)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ internal int ObjectID
}
}

/// <summary>
/// Verify client encryption possibility.
/// </summary>
private bool ClientOSEncryptionSupport => SNILoadHandle.SingletonInstance.ClientOSEncryptionSupport;

// ReliabilitySection Usage:
//
Expand Down Expand Up @@ -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("<sc.TdsParser.Connect|SEC> Sending prelogin handshake");
SendPreLoginHandshake(instanceName, encrypt, !string.IsNullOrEmpty(certificate), useOriginalAddressInfo);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand All @@ -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):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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
/// <summary>
/// Verify client encryption possibility.
/// </summary>
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("<sc.SNILoadHandle.EncryptClientPossible|SEC> 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)
Expand All @@ -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)
{
Expand Down