diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 70fe79041e..2a4a3b430b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -475,6 +475,9 @@ Microsoft\Data\SqlClient\TdsParameterSetter.cs + + Microsoft\Data\SqlClient\TdsParserStaticMethods.cs + Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs @@ -649,7 +652,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index b1e683badc..19696260a5 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -539,6 +539,9 @@ Microsoft\Data\SqlClient\TdsParameterSetter.cs + + Microsoft\Data\SqlClient\TdsParserStaticMethods.cs + Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs @@ -634,7 +637,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs similarity index 50% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs index 6f67764f9a..a530c4d697 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs @@ -4,11 +4,92 @@ using System; using Microsoft.Data.Common; +#if NETFRAMEWORK +using System.Globalization; +using System.Runtime.Versioning; +using System.Security.Permissions; +#endif namespace Microsoft.Data.SqlClient { internal sealed class TdsParserStaticMethods { +#if NETFRAMEWORK + private TdsParserStaticMethods() { /* prevent utility class from being insantiated*/ } + // + // Static methods + // + + // SxS: this method accesses registry to resolve the alias. + [ResourceExposure(ResourceScope.None)] + [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] + static internal void AliasRegistryLookup(ref string host, ref string protocol) + { + if (!ADP.IsEmpty(host)) + { + const string folder = "SOFTWARE\\Microsoft\\MSSQLServer\\Client\\ConnectTo"; + // Put a try...catch... around this so we don't abort ANY connection if we can't read the registry. + string aliasLookup = (string)ADP.LocalMachineRegistryValue(folder, host); + if (!ADP.IsEmpty(aliasLookup)) + { + /* Result will be in the form of: "DBNMPNTW,\\server\pipe\sql\query". or + Result will be in the form of: "DBNETLIB, via:\\server\pipe\sql\query". + + supported formats: + tcp - DBMSSOCN,[server|server\instance][,port] + np - DBNMPNTW,[\\server\pipe\sql\query | \\server\pipe\MSSQL$instance\sql\query] + where \sql\query is the pipename and can be replaced with any other pipe name + via - [DBMSGNET,server,port | DBNETLIB, via:server, port] + sm - DBMSLPCN,server + + unsupported formats: + rpc - DBMSRPCN,server,[parameters] where parameters could be "username,password" + bv - DBMSVINN,service@group@organization + appletalk - DBMSADSN,objectname@zone + spx - DBMSSPXN,[service | address,port,network] + */ + // We must parse into the two component pieces, then map the first protocol piece to the + // appropriate value. + int index = aliasLookup.IndexOf(','); + + // If we found the key, but there was no "," in the string, it is a bad Alias so return. + if (-1 != index) + { + string parsedProtocol = aliasLookup.Substring(0, index).ToLower(CultureInfo.InvariantCulture); + + // If index+1 >= length, Alias consisted of "FOO," which is a bad alias so return. + if (index + 1 < aliasLookup.Length) + { + string parsedAliasName = aliasLookup.Substring(index + 1); + + // Fix bug 298286 + if ("dbnetlib" == parsedProtocol) + { + index = parsedAliasName.IndexOf(':'); + if (-1 != index && index + 1 < parsedAliasName.Length) + { + parsedProtocol = parsedAliasName.Substring(0, index); + if (SqlConnectionString.ValidProtocol(parsedProtocol)) + { + protocol = parsedProtocol; + host = parsedAliasName.Substring(index + 1); + } + } + } + else + { + protocol = (string)SqlConnectionString.NetlibMapping()[parsedProtocol]; + if (null != protocol) + { + host = parsedAliasName; + } + } + } + } + } + } + } +#endif // Obfuscate password to be sent to SQL Server // Blurb from the TDS spec at https://msdn.microsoft.com/en-us/library/dd304523.aspx // "Before submitting a password from the client to the server, for every byte in the password buffer @@ -51,10 +132,18 @@ internal static byte[] ObfuscatePassword(byte[] password) return password; } +#if NETFRAMEWORK + [ResourceExposure(ResourceScope.None)] // SxS: we use this method for TDS login only + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] +#else private const int NoProcessId = -1; private static int s_currentProcessId = NoProcessId; +#endif // NETFRAMEWORK internal static int GetCurrentProcessIdForTdsLoginOnly() { +#if NETFRAMEWORK + return Common.SafeNativeMethods.GetCurrentProcessId(); +#else if (s_currentProcessId == NoProcessId) { // Pick up the process Id from the current process instead of randomly generating it. @@ -67,29 +156,85 @@ internal static int GetCurrentProcessIdForTdsLoginOnly() System.Threading.Volatile.Write(ref s_currentProcessId, processId); } return s_currentProcessId; +#endif // NETFRAMEWORK } - +#if NETFRAMEWORK + [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] + [ResourceExposure(ResourceScope.None)] // SxS: we use this method for TDS login only + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] +#endif internal static int GetCurrentThreadIdForTdsLoginOnly() { +#if NETFRAMEWORK +#pragma warning disable 618 + return AppDomain.GetCurrentThreadId(); // don't need this to be support fibres; +#pragma warning restore 618 +#else return Environment.CurrentManagedThreadId; +#endif // NETFRAMEWORK } - +#if NETFRAMEWORK + [ResourceExposure(ResourceScope.None)] // SxS: we use MAC address for TDS login only + [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] +#else private static byte[] s_nicAddress = null; +#endif // NETFRAMEWORK internal static byte[] GetNetworkPhysicalAddressForTdsLoginOnly() { +#if NETFRAMEWORK + // NIC address is stored in NetworkAddress key. However, if NetworkAddressLocal key + // has a value that is not zero, then we cannot use the NetworkAddress key and must + // instead generate a random one. I do not fully understand why, this is simply what + // the native providers do. As for generation, I use a random number generator, which + // means that different processes on the same machine will have different NIC address + // values on the server. It is not ideal, but native does not have the same value for + // different processes either. + + const string key = "NetworkAddress"; + const string localKey = "NetworkAddressLocal"; + const string folder = "SOFTWARE\\Description\\Microsoft\\Rpc\\UuidTemporaryData"; + + int result = 0; + byte[] nicAddress = null; + + object temp = ADP.LocalMachineRegistryValue(folder, localKey); + if (temp is int localKeyRegistryValue) + { + result = localKeyRegistryValue; + } + + if (result <= 0) + { + temp = ADP.LocalMachineRegistryValue(folder, key); + if (temp is byte[] keyRegistryValue) + { + nicAddress = keyRegistryValue; + } + } + + if (null == nicAddress) + { + nicAddress = new byte[TdsEnums.MAX_NIC_SIZE]; + Random random = new(); + random.NextBytes(nicAddress); + } + + return nicAddress; +#else // For ProjectK\CoreCLR we don't want to take a dependency on the registry to try to read a value // that isn't usually set, so we'll just use a random value each time instead if (null == s_nicAddress) { byte[] newNicAddress = new byte[TdsEnums.MAX_NIC_SIZE]; - Random random = new Random(); + Random random = new(); random.NextBytes(newNicAddress); System.Threading.Interlocked.CompareExchange(ref s_nicAddress, newNicAddress, null); } return s_nicAddress; +#endif // NETFRAMEWORK } // translates remaining time in stateObj (from user specified timeout) to timeout value for SNI