From b7167d562469df4a618030a3d03f1944d3d85915 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 7 Feb 2020 13:37:58 -0800 Subject: [PATCH 1/2] Update error messages for Secure Enclave connection properties --- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 10 + .../src/Microsoft/Data/SqlClient/TdsParser.cs | 219 ++++++++++-------- .../netcore/src/Resources/SR.Designer.cs | 26 ++- .../netcore/src/Resources/SR.resx | 8 +- .../src/Microsoft/Data/SqlClient/SqlUtil.cs | 10 + .../src/Microsoft/Data/SqlClient/TdsParser.cs | 147 +++++++----- .../netfx/src/Resources/Strings.Designer.cs | 22 +- .../netfx/src/Resources/Strings.resx | 6 + 8 files changed, 282 insertions(+), 166 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index 47a1401562..69102c3619 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1712,6 +1712,16 @@ internal static Exception EnclaveComputationsNotSupported() return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_EnclaveComputationsNotSupported)); } + internal static Exception AttestationURLNotSupported() + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_AttestationURLNotSupported)); + } + + internal static Exception AttestationProtocolNotSupported() + { + return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_AttestationProtocolNotSupported)); + } + internal static Exception EnclaveTypeNotReturned() { return ADP.InvalidOperation(System.SRHelper.GetString(SR.TCE_EnclaveTypeNotReturned)); 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 ccb91ccef5..c4eb3107af 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 @@ -2986,30 +2986,43 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) throw SQL.TceNotSupported(); } - // Check if enclave attestation url was specified and server does not support enclave computations and we aren't going to be routed to another server. - if (Connection.RoutingInfo == null - && (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) - && (TceVersionSupported < TdsEnums.MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT)) - { - throw SQL.EnclaveComputationsNotSupported(); - } - - // Check if enclave attestation url was specified and server does not return an enclave type and we aren't going to be routed to another server. - if (Connection.RoutingInfo == null - && (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) - && string.IsNullOrWhiteSpace(EnclaveType)) + // Check if server does not support Enclave Computations and we aren't going to be routed to another server. + if (Connection.RoutingInfo == null) { - throw SQL.EnclaveTypeNotReturned(); - } + SqlConnectionAttestationProtocol attestationProtocol = _connHandler.ConnectionOptions.AttestationProtocol; - // Check if enclave attestation url was specified and the attestation protocol supports the enclave type. - SqlConnectionAttestationProtocol attestationProtocol = _connHandler.ConnectionOptions.AttestationProtocol; - if (this.Connection.RoutingInfo == null - && (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) - && (!string.IsNullOrWhiteSpace(EnclaveType)) - && (!IsValidAttestationProtocol(attestationProtocol, EnclaveType))) - { - throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + if (TceVersionSupported < TdsEnums.MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT) + { + // Check if enclave attestation url was specified and server does not support enclave computations and we aren't going to be routed to another server. + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol) + { + throw SQL.EnclaveComputationsNotSupported(); + } + else if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + { + throw SQL.AttestationURLNotSupported(); + } + else if (SqlConnectionAttestationProtocol.NotSpecified != _connHandler.ConnectionOptions.AttestationProtocol) + { + throw SQL.AttestationProtocolNotSupported(); + } + } + // Check if enclave attestation url was specified and server does not return an enclave type and we aren't going to be routed to another server. + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + { + if (string.IsNullOrWhiteSpace(EnclaveType)) + { + throw SQL.EnclaveTypeNotReturned(); + } + else + { + // Check if the attestation protocol is specified and supports the enclave type. + if (SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol && !IsValidAttestationProtocol(attestationProtocol, EnclaveType)) + { + throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + } + } + } } return true; @@ -4087,8 +4100,8 @@ internal int GetCodePage(SqlCollation collation, TdsParserStateObject stateObj) case 0x11404: // zh-MO case 0x10411: // ja-JP case 0x10412: // ko-KR - // If one of the following special cases, mask out sortId and - // retry. + // If one of the following special cases, mask out sortId and + // retry. cultureId = cultureId & 0x03fff; try @@ -6411,7 +6424,7 @@ internal Task WriteSqlVariantValue(object value, int length, int offset, TdsPars Debug.Fail("unknown tds type for sqlvariant!"); break; } // switch - // return point for accumulated writes, note: non-accumulated writes returned from their case statements + // return point for accumulated writes, note: non-accumulated writes returned from their case statements return null; } @@ -6578,7 +6591,7 @@ internal Task WriteSqlVariantDataRowValue(object value, TdsParserStateObject sta Debug.Fail("unknown tds type for sqlvariant!"); break; } // switch - // return point for accumulated writes, note: non-accumulated writes returned from their case statements + // return point for accumulated writes, note: non-accumulated writes returned from their case statements return null; } @@ -9095,89 +9108,89 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet maxsize = 1; } - WriteParameterVarLen(mt, maxsize, false /*IsNull*/, stateObj); - } - } - else - { - // If type timestamp - treat as fixed type and always send over timestamp length, which is 8. - // For fixed types, we either send null or fixed length for type length. We want to match that - // behavior for timestamps. However, in the case of null, we still must send 8 because if we - // send null we will not receive a output val. You can send null for fixed types and still - // receive a output value, but not for variable types. So, always send 8 for timestamp because - // while the user sees it as a fixed type, we are actually representing it as a bigbinary which - // is variable. - if (mt.SqlDbType == SqlDbType.Timestamp) - { - WriteParameterVarLen(mt, TdsEnums.TEXT_TIME_STAMP_LEN, false, stateObj); - } - else if (mt.SqlDbType == SqlDbType.Udt) - { - Debug.Assert(_isYukon, "Invalid DataType UDT for non-Yukon or later server!"); + WriteParameterVarLen(mt, maxsize, false /*IsNull*/, stateObj); + } + } + else + { + // If type timestamp - treat as fixed type and always send over timestamp length, which is 8. + // For fixed types, we either send null or fixed length for type length. We want to match that + // behavior for timestamps. However, in the case of null, we still must send 8 because if we + // send null we will not receive a output val. You can send null for fixed types and still + // receive a output value, but not for variable types. So, always send 8 for timestamp because + // while the user sees it as a fixed type, we are actually representing it as a bigbinary which + // is variable. + if (mt.SqlDbType == SqlDbType.Timestamp) + { + WriteParameterVarLen(mt, TdsEnums.TEXT_TIME_STAMP_LEN, false, stateObj); + } + else if (mt.SqlDbType == SqlDbType.Udt) + { + Debug.Assert(_isYukon, "Invalid DataType UDT for non-Yukon or later server!"); - int maxSupportedSize = IsKatmaiOrNewer ? int.MaxValue : short.MaxValue; - byte[] udtVal = null; - Format format = Format.Native; + int maxSupportedSize = IsKatmaiOrNewer ? int.MaxValue : short.MaxValue; + byte[] udtVal = null; + Format format = Format.Native; - if (string.IsNullOrEmpty(param.UdtTypeName)) - { - throw SQL.MustSetUdtTypeNameForUdtParams(); - } + if (string.IsNullOrEmpty(param.UdtTypeName)) + { + throw SQL.MustSetUdtTypeNameForUdtParams(); + } - if (!isNull) - { - // When writing UDT parameter values to the TDS stream, allow sending byte[] or SqlBytes - // directly to the server and not reject them as invalid. This allows users to handle - // serialization and deserialization logic without having to have SqlClient be aware of - // the types and without using inefficient text representations. - if (value is byte[] rawBytes) - { - udtVal = rawBytes; - } - else if (value is SqlBytes sqlBytes) - { - switch (sqlBytes.Storage) - { - case StorageState.Buffer: - // use the buffer directly, the only way to create it is with the correctly sized byte array - udtVal = sqlBytes.Buffer; - break; - case StorageState.Stream: - case StorageState.UnmanagedBuffer: - // allocate a new byte array to store the data - udtVal = sqlBytes.Value; - break; - } - } - else - { - udtVal = _connHandler.Connection.GetBytes(value, out format, out maxsize); - } + if (!isNull) + { + // When writing UDT parameter values to the TDS stream, allow sending byte[] or SqlBytes + // directly to the server and not reject them as invalid. This allows users to handle + // serialization and deserialization logic without having to have SqlClient be aware of + // the types and without using inefficient text representations. + if (value is byte[] rawBytes) + { + udtVal = rawBytes; + } + else if (value is SqlBytes sqlBytes) + { + switch (sqlBytes.Storage) + { + case StorageState.Buffer: + // use the buffer directly, the only way to create it is with the correctly sized byte array + udtVal = sqlBytes.Buffer; + break; + case StorageState.Stream: + case StorageState.UnmanagedBuffer: + // allocate a new byte array to store the data + udtVal = sqlBytes.Value; + break; + } + } + else + { + udtVal = _connHandler.Connection.GetBytes(value, out format, out maxsize); + } Debug.Assert(null != udtVal, "GetBytes returned null instance. Make sure that it always returns non-null value"); size = udtVal.Length; - if (size >= maxSupportedSize && maxsize != -1) - { - throw SQL.UDTInvalidSize(maxsize, maxSupportedSize); - } - } + if (size >= maxSupportedSize && maxsize != -1) + { + throw SQL.UDTInvalidSize(maxsize, maxSupportedSize); + } + } - // Split the input name. TypeName is returned as single 3 part name during DeriveParameters. - // NOTE: ParseUdtTypeName throws if format is incorrect - string[] names = SqlParameter.ParseTypeName(param.UdtTypeName, isUdtTypeName: true); - if (!string.IsNullOrEmpty(names[0]) && TdsEnums.MAX_SERVERNAME < names[0].Length) - { - throw ADP.ArgumentOutOfRange(nameof(names)); - } - if (!string.IsNullOrEmpty(names[1]) && TdsEnums.MAX_SERVERNAME < names[names.Length - 2].Length) - { - throw ADP.ArgumentOutOfRange(nameof(names)); - } - if (TdsEnums.MAX_SERVERNAME < names[2].Length) - { - throw ADP.ArgumentOutOfRange(nameof(names)); - } + // Split the input name. TypeName is returned as single 3 part name during DeriveParameters. + // NOTE: ParseUdtTypeName throws if format is incorrect + string[] names = SqlParameter.ParseTypeName(param.UdtTypeName, isUdtTypeName: true); + if (!string.IsNullOrEmpty(names[0]) && TdsEnums.MAX_SERVERNAME < names[0].Length) + { + throw ADP.ArgumentOutOfRange(nameof(names)); + } + if (!string.IsNullOrEmpty(names[1]) && TdsEnums.MAX_SERVERNAME < names[names.Length - 2].Length) + { + throw ADP.ArgumentOutOfRange(nameof(names)); + } + if (TdsEnums.MAX_SERVERNAME < names[2].Length) + { + throw ADP.ArgumentOutOfRange(nameof(names)); + } WriteUDTMetaData(value, names[0], names[1], names[2], stateObj); @@ -10886,7 +10899,7 @@ private Task WriteUnterminatedSqlValue(object value, MetaType type, int actualLe Debug.Fail("Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null)); break; } // switch - // return point for accumulated writes, note: non-accumulated writes returned from their case statements + // return point for accumulated writes, note: non-accumulated writes returned from their case statements return null; } @@ -11574,7 +11587,7 @@ private Task WriteUnterminatedValue(object value, MetaType type, byte scale, int Debug.Fail("Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null)); break; } // switch - // return point for accumulated writes, note: non-accumulated writes returned from their case statements + // return point for accumulated writes, note: non-accumulated writes returned from their case statements return null; // Debug.WriteLine("value: " + value.ToString(CultureInfo.InvariantCulture)); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs index e039abf6fe..4454cb49df 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.Designer.cs @@ -10,8 +10,8 @@ namespace System { using System; - - + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,7 +19,7 @@ namespace System { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class SR { @@ -4092,6 +4092,15 @@ internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePa } } + /// + /// Looks up a localized string similar to You have specified the attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// + internal static string TCE_AttestationProtocolNotSupported { + get { + return ResourceManager.GetString("TCE_AttestationProtocolNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'.. /// @@ -4101,6 +4110,15 @@ internal static string TCE_AttestationProtocolNotSupportEnclaveType { } } + /// + /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// + internal static string TCE_AttestationURLNotSupported { + get { + return ResourceManager.GetString("TCE_AttestationURLNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} should be identical on all commands ({1}, {2}, {3}, {4}) when doing batch updates.. /// @@ -4363,7 +4381,7 @@ internal static string TCE_EmptyProviderName { } /// - /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// Looks up a localized string similar to You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations.. /// internal static string TCE_EnclaveComputationsNotSupported { get { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx index e38fd1c865..a2fdb29cfd 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/SR.resx @@ -1654,8 +1654,14 @@ Invalid key store provider name specified. Key store provider names cannot be null or empty. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations. + + You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations. + + You have specified the attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations. + No enclave provider found for enclave type '{0}' and attestation protocol '{1}'. Please specify the correct attestation protocol in the connection string. @@ -1854,4 +1860,4 @@ UDT size must be less than {1}, size: {0} - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index 24ed62d023..86826ee9bb 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -1730,6 +1730,16 @@ static internal Exception EnclaveComputationsNotSupported() return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveComputationsNotSupported)); } + internal static Exception AttestationURLNotSupported() + { + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_AttestationURLNotSupported)); + } + + internal static Exception AttestationProtocolNotSupported() + { + return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_AttestationProtocolNotSupported)); + } + static internal Exception EnclaveTypeNotReturned() { return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_EnclaveTypeNotReturned)); 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 1c906042f1..5f5ddc62e0 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 @@ -92,9 +92,11 @@ internal void Start() Debug.Assert(!m_started); RuntimeHelpers.PrepareConstrainedRegions(); - try { + try + { } - finally { + finally + { ++s_reliabilityCount; m_started = true; } @@ -107,13 +109,16 @@ internal void Stop() #if DEBUG // cannot assert m_started - ThreadAbortException can be raised before Start is called - if (m_started) { + if (m_started) + { Debug.Assert(s_reliabilityCount > 0); RuntimeHelpers.PrepareConstrainedRegions(); - try { + try + { } - finally { + finally + { --s_reliabilityCount; m_started = false; } @@ -1586,7 +1591,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj) { #if DEBUG // There is an exception here for MARS as its possible that another thread has closed the connection just as we see an error - Debug.Assert(SniContext.Undefined!=stateObj.DebugOnlyCopyOfSniContext || ((_fMARS) && ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken))), "SniContext must not be None"); + Debug.Assert(SniContext.Undefined != stateObj.DebugOnlyCopyOfSniContext || ((_fMARS) && ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken))), "SniContext must not be None"); #endif SNINativeMethodWrapper.SNI_Error sniError = new SNINativeMethodWrapper.SNI_Error(); SNINativeMethodWrapper.SNIGetLastError(out sniError); @@ -1774,7 +1779,8 @@ internal void CheckResetConnection(TdsParserStateObject stateObj) } } #if DEBUG - else { + else + { Debug.Assert(!_fResetConnection || (_fResetConnection && stateObj._fResetConnectionSent && stateObj._fResetEventOwned), "Unexpected state on else ResetConnection block in WritePacket"); @@ -2048,13 +2054,15 @@ internal bool RunReliably(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDat #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); - try { + try + { tdsReliabilitySection.Start(); #endif //DEBUG - return Run(runBehavior, cmdHandler, dataStream, bulkCopyHandler, stateObj); + return Run(runBehavior, cmdHandler, dataStream, bulkCopyHandler, stateObj); #if DEBUG } - finally { + finally + { tdsReliabilitySection.Stop(); } #endif //DEBUG @@ -2478,10 +2486,11 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead if (null != _currentTransaction) { #if DEBUG - // Check null for case where Begin and Rollback obtained in the same message. - if (SqlInternalTransaction.NullTransactionId != _currentTransaction.TransactionId) { - Debug.Assert(_currentTransaction.TransactionId != env[ii].newLongValue, "transaction id's are not equal!"); - } + // Check null for case where Begin and Rollback obtained in the same message. + if (SqlInternalTransaction.NullTransactionId != _currentTransaction.TransactionId) + { + Debug.Assert(_currentTransaction.TransactionId != env[ii].newLongValue, "transaction id's are not equal!"); + } #endif if (TdsEnums.ENV_COMMITTRAN == env[ii].type) @@ -2747,9 +2756,11 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead (!stateObj._pendingData && stateObj._attentionSent && !stateObj._attentionReceived)); #if DEBUG - if ((stateObj._pendingData) && (!dataReady)) { + if ((stateObj._pendingData) && (!dataReady)) + { byte token; - if (!stateObj.TryPeekByte(out token)) { + if (!stateObj.TryPeekByte(out token)) + { return false; } Debug.Assert(IsValidTdsToken(token), $"DataReady is false, but next token is not valid: {token,-2:X2}"); @@ -3454,30 +3465,43 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) throw SQL.TceNotSupported(); } - // Check if enclave attestation url was specified and server does not support enclave computations and we aren't going to be routed to another server. - if (this.Connection.RoutingInfo == null - && (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) - && (TceVersionSupported < TdsEnums.MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT)) + // Check if server does not support Enclave Computations and we aren't going to be routed to another server. + if (Connection.RoutingInfo == null) { - throw SQL.EnclaveComputationsNotSupported(); - } + SqlConnectionAttestationProtocol attestationProtocol = _connHandler.ConnectionOptions.AttestationProtocol; - // Check if enclave attestation url was specified and server does not return an enclave type and we aren't going to be routed to another server. - if (this.Connection.RoutingInfo == null - && (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) - && string.IsNullOrWhiteSpace(EnclaveType)) - { - throw SQL.EnclaveTypeNotReturned(); - } - - // Check if enclave attestation url was specified and the attestation protocol supports the enclave type. - SqlConnectionAttestationProtocol attestationProtocol = _connHandler.ConnectionOptions.AttestationProtocol; - if (this.Connection.RoutingInfo == null - && (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) - && (!string.IsNullOrWhiteSpace(EnclaveType)) - && (!IsValidAttestationProtocol(attestationProtocol, EnclaveType))) - { - throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + if (TceVersionSupported < TdsEnums.MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT) + { + // Check if enclave attestation url was specified and server does not support enclave computations and we aren't going to be routed to another server. + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol) + { + throw SQL.EnclaveComputationsNotSupported(); + } + else if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + { + throw SQL.AttestationURLNotSupported(); + } + else if (SqlConnectionAttestationProtocol.NotSpecified != _connHandler.ConnectionOptions.AttestationProtocol) + { + throw SQL.AttestationProtocolNotSupported(); + } + } + // Check if enclave attestation url was specified and server does not return an enclave type and we aren't going to be routed to another server. + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + { + if (string.IsNullOrWhiteSpace(EnclaveType)) + { + throw SQL.EnclaveTypeNotReturned(); + } + else + { + // Check if the attestation protocol is specified and supports the enclave type. + if (SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol && !IsValidAttestationProtocol(attestationProtocol, EnclaveType)) + { + throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + } + } + } } return true; @@ -4730,7 +4754,8 @@ internal void DrainData(TdsParserStateObject stateObj) TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); - try { + try + { tdsReliabilitySection.Start(); #else { @@ -4802,7 +4827,8 @@ internal void DrainData(TdsParserStateObject stateObj) } } #if DEBUG - finally { + finally + { tdsReliabilitySection.Stop(); } #endif //DEBUG @@ -9157,7 +9183,8 @@ internal byte[] GetDTCAddress(int timeout, TdsParserStateObject stateObj) dtcReader.GetBytes(0, 0, dtcAddr, 0, cb); } #if DEBUG - else { + else + { Debug.Fail("unexpected length (> Int32.MaxValue) returned from dtcReader.GetBytes"); // if we hit this case we'll just return a null address so that the user // will get a transcaction enlistment error in the upper layers @@ -10199,9 +10226,10 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo } } #if DEBUG - else { - Debug.Assert(writeParamTask == null, "Should not have a task when executing sync"); - } + else + { + Debug.Assert(writeParamTask == null, "Should not have a task when executing sync"); + } #endif } // parameter for loop @@ -10314,20 +10342,22 @@ private void TdsExecuteRPC_OnFailure(Exception exc, TdsParserStateObject stateOb try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); - RuntimeHelpers.PrepareConstrainedRegions(); - try { - tdsReliabilitySection.Start(); + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + tdsReliabilitySection.Start(); #else { #endif //DEBUG FailureCleanup(stateObj, exc); } #if DEBUG - finally { - tdsReliabilitySection.Stop(); - } + finally + { + tdsReliabilitySection.Stop(); + } #endif //DEBUG } catch (System.OutOfMemoryException) @@ -10362,7 +10392,8 @@ private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, T TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); - try { + try + { tdsReliabilitySection.Start(); #else { @@ -10370,7 +10401,8 @@ private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, T FailureCleanup(stateObj, tsk.Exception); } #if DEBUG - finally { + finally + { tdsReliabilitySection.Stop(); } #endif //DEBUG @@ -11408,7 +11440,8 @@ internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsPars #if DEBUG //In DEBUG mode, when SetAlwaysTaskOnWrite is true, we create a task. Allows us to verify async execution paths. - if (_asyncWrite && internalWriteTask == null && SqlBulkCopy.SetAlwaysTaskOnWrite == true) { + if (_asyncWrite && internalWriteTask == null && SqlBulkCopy.SetAlwaysTaskOnWrite == true) + { internalWriteTask = Task.FromResult(null); } #endif @@ -12055,7 +12088,8 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); - try { + try + { tdsReliabilitySection.Start(); #else { @@ -12076,7 +12110,8 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati } } #if DEBUG - finally { + finally + { tdsReliabilitySection.Stop(); } #endif //DEBUG diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index 2373ccc32f..146c442ddb 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -19,7 +19,7 @@ namespace System { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Strings { @@ -11673,6 +11673,15 @@ internal static string TCE_AttestationProtocolNotSpecifiedForGeneratingEnclavePa } } + /// + /// Looks up a localized string similar to You have specified the attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// + internal static string TCE_AttestationProtocolNotSupported { + get { + return ResourceManager.GetString("TCE_AttestationProtocolNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed to initialize connection. The attestation protocol '{0}' does not support the enclave type '{1}'.. /// @@ -11682,6 +11691,15 @@ internal static string TCE_AttestationProtocolNotSupportEnclaveType { } } + /// + /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// + internal static string TCE_AttestationURLNotSupported { + get { + return ResourceManager.GetString("TCE_AttestationURLNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} should be identical on all commands ({1}, {2}, {3}, {4}) when doing batch updates.. /// @@ -11962,7 +11980,7 @@ internal static string TCE_EmptyProviderName { } /// - /// Looks up a localized string similar to You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations.. + /// Looks up a localized string similar to You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations.. /// internal static string TCE_EnclaveComputationsNotSupported { get { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index 51ad3d8df1..572be5cd8b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -4336,8 +4336,14 @@ {0} instance in use does not support column encryption. + You have specified the enclave attestation URL and attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations. + + You have specified the enclave attestation URL in the connection string, but the SQL Server instance in use does not support enclave based computations. + + You have specified the attestation protocol in the connection string, but the SQL Server instance in use does not support enclave based computations. + You have specified the enclave attestation URL in the connection string, but the SQL Server instance did not return an enclave type. Please make sure the enclave type is correctly configured in your instance. From 001f03d14d943fdb9b2ad22444b9ae74763a62ae Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 10 Feb 2020 14:51:12 -0800 Subject: [PATCH 2/2] Add Test --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 1 + .../SQL/ExceptionTest/ExceptionTest.cs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) 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 c4eb3107af..68ce6b544b 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 @@ -3007,6 +3007,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) throw SQL.AttestationProtocolNotSupported(); } } + // Check if enclave attestation url was specified and server does not return an enclave type and we aren't going to be routed to another server. if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs index 31ed099772..45937d7f29 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs @@ -227,6 +227,23 @@ public static void IndependentConnectionExceptionTest() } } + [CheckConnStrSetupFact] + public static void EnclavesConnectionExceptionTest() + { + string connectionStringWithAttestationProtocol = DataTestUtility.TCPConnectionString + ";Attestation Protocol = HGS;"; + string connectionStringWithAttestionURL = DataTestUtility.TCPConnectionString + ";Enclave Attestation URL = https://dummyURL;"; + string connectionStringWithEnclave = connectionStringWithAttestionURL + ";Attestation Protocol = HGS;"; + + InvalidOperationException e1 = Assert.Throws(() => new SqlConnection(connectionStringWithAttestionURL).Open()); + Assert.Contains("You have specified the enclave attestation URL in the connection string", e1.Message); + + InvalidOperationException e2 = Assert.Throws(() => new SqlConnection(connectionStringWithAttestationProtocol).Open()); + Assert.Contains("You have specified the attestation protocol in the connection string", e2.Message); + + InvalidOperationException e3 = Assert.Throws(() => new SqlConnection(connectionStringWithEnclave).Open()); + Assert.Contains("You have specified the enclave attestation URL and attestation protocol in the connection string", e3.Message); + } + [CheckConnStrSetupFact] public static async Task UnobservedTaskExceptionTest() {