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

Give system key store providers precedence over instance-level providers #1101

Merged
merged 9 commits into from
Jun 8, 2021
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1026,7 +1026,7 @@ GO
<summary>
Registers the column encryption key store providers. This function should only be called once in an app. This does shallow copying of the dictionary so that the app cannot alter the custom provider list once it has been set.

The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered. No providers should be registered on the connection or command instances if one of the built-in column master key store providers is needed.
The built-in column master key store providers that are available for the Windows Certificate Store, CNG Store and CSP are pre-registered.
</summary>
<remarks>
<format type="text/markdown"><![CDATA[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,11 @@ private SqlConnection(SqlConnection connection)
CacheConnectionStringProperties();
}

internal static bool TryGetSystemColumnEncryptionKeyStoreProvider(string keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider)
{
return s_systemColumnEncryptionKeyStoreProviders.TryGetValue(keyStoreName, out provider);
}

/// <summary>
/// This function walks through both system and custom column encryption key store providers and returns an object if found.
johnnypham marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
Expand All @@ -240,17 +245,12 @@ internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out Sq
return _customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider);
}

// Search in the sytem provider list.
if (s_systemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider))
{
return true;
}

lock (s_globalCustomColumnEncryptionKeyProvidersLock)
{
// If custom provider is not set, then return false
if (s_globalCustomColumnEncryptionKeyStoreProviders is null)
{
columnKeyStoreProvider = null;
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ private static void ValidateCustomProviders(IDictionary<string, SqlColumnEncrypt
}
}

internal static bool TryGetSystemColumnEncryptionKeyStoreProvider(string keyStoreName, out SqlColumnEncryptionKeyStoreProvider provider)
{
return s_systemColumnEncryptionKeyStoreProviders.TryGetValue(keyStoreName, out provider);
}

/// <summary>
/// This function walks through both system and custom column encryption key store providers and returns an object if found.
/// </summary>
Expand All @@ -228,17 +233,12 @@ internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out Sq
return _customColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider);
}

// Search in the system provider list.
if (s_systemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider))
{
return true;
}

lock (s_globalCustomColumnEncryptionKeyProvidersLock)
{
// If custom provider is not set, then return false
if (s_globalCustomColumnEncryptionKeyStoreProviders is null)
{
columnKeyStoreProvider = null;
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Data.Common;

namespace Microsoft.Data.SqlClient
{
Expand Down Expand Up @@ -274,7 +275,7 @@ internal static void DecryptSymmetricKey(SqlTceCipherInfoEntry sqlTceCipherInfoE
{
try
{
sqlClientSymmetricKey = InstanceLevelProvidersAreRegistered(connection, command) ?
sqlClientSymmetricKey = ShouldUseInstanceLevelProviderFlow(keyInfo.keyStoreName, connection, command) ?
GetKeyFromLocalProviders(keyInfo, connection, command) :
globalCekCache.GetKey(keyInfo, connection, command);
encryptionkeyInfoChosen = keyInfo;
Expand Down Expand Up @@ -367,7 +368,7 @@ internal static void VerifyColumnMasterKeySignature(string keyStoreName, string
GetListOfProviderNamesThatWereSearched(connection, command));
}

if (InstanceLevelProvidersAreRegistered(connection, command))
if (ShouldUseInstanceLevelProviderFlow(keyStoreName,connection, command))
{
isValidSignature = provider.VerifyColumnMasterKeyMetadata(keyPath, isEnclaveEnabled, CMKSignature);
}
Expand Down Expand Up @@ -399,6 +400,15 @@ internal static void VerifyColumnMasterKeySignature(string keyStoreName, string
}
}

// Instance-level providers will be used if at least one is registered on a connection or command and
// the required provider is not a system provider. System providers are pre-registered globally and
// must use the global provider flow
private static bool ShouldUseInstanceLevelProviderFlow(string keyStoreName, SqlConnection connection, SqlCommand command)
{
return InstanceLevelProvidersAreRegistered(connection, command) &&
!keyStoreName.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix);
}

private static bool InstanceLevelProvidersAreRegistered(SqlConnection connection, SqlCommand command) =>
connection.HasColumnEncryptionKeyStoreProvidersRegistered ||
(command is not null && command.HasColumnEncryptionKeyStoreProvidersRegistered);
Expand All @@ -423,6 +433,11 @@ internal static bool TryGetColumnEncryptionKeyStoreProvider(string keyStoreName,
{
Debug.Assert(!string.IsNullOrWhiteSpace(keyStoreName), "Provider name is invalid");

if (SqlConnection.TryGetSystemColumnEncryptionKeyStoreProvider(keyStoreName, out provider))
{
return true;
}

// command may be null because some callers do not have a command object, eg SqlBulkCopy
if (command is not null && command.HasColumnEncryptionKeyStoreProvidersRegistered)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2242,6 +2242,31 @@ public void TestCommandCustomKeyStoreProviderDuringAeQuery(string connectionStri
}
}

// On Windows, "_fixture" will be type SQLSetupStrategyCertStoreProvider
// On non-Windows, "_fixture" will be type SQLSetupStrategyAzureKeyVault
// Test will pass on both but only SQLSetupStrategyCertStoreProvider is a system provider
[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))]
[ClassData(typeof(AEConnectionStringProvider))]
public void TestSystemProvidersHavePrecedenceOverInstanceLevelProviders(string connectionString)
johnnypham marked this conversation as resolved.
Show resolved Hide resolved
{
Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new()
{
{
SqlColumnEncryptionAzureKeyVaultProvider.ProviderName,
new SqlColumnEncryptionAzureKeyVaultProvider(new SqlClientCustomTokenCredential())
}
};

using SqlConnection connection = new(connectionString);
connection.Open();
using SqlCommand command = new(
$"SELECT * FROM [{_fixture.CustomKeyStoreProviderTestTable.Name}] WHERE FirstName = @firstName",
connection, null, SqlCommandColumnEncryptionSetting.Enabled);
command.Parameters.AddWithValue("firstName", "abc");
command.RegisterColumnEncryptionKeyStoreProvidersOnCommand(customKeyStoreProviders);
command.ExecuteReader();
}

[ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE), nameof(DataTestUtility.EnclaveEnabled))]
[ClassData(typeof(AEConnectionStringProvider))]
public void TestRetryWhenAEParameterMetadataCacheIsStale(string connectionString)
Expand Down