DbConnectionPoolIdentity.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DbConnectionPoolIdentity.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. //------------------------------------------------------------------------------
  7. namespace System.Data.ProviderBase {
  8. using System;
  9. using System.Collections;
  10. using System.Data.Common;
  11. using System.Diagnostics;
  12. using System.Globalization;
  13. using System.Runtime.CompilerServices;
  14. using System.Runtime.InteropServices;
  15. using System.Security;
  16. using System.Security.Permissions;
  17. using System.Security.Principal;
  18. using System.Threading;
  19. using System.Runtime.Versioning;
  20. [Serializable] // Serializable so SqlDependencyProcessDispatcher can marshall cross domain to SqlDependency.
  21. sealed internal class DbConnectionPoolIdentity {
  22. private const int E_NotImpersonationToken = unchecked((int)0x8007051D);
  23. private const int Win32_CheckTokenMembership = 1;
  24. private const int Win32_GetTokenInformation_1 = 2;
  25. private const int Win32_GetTokenInformation_2 = 3;
  26. private const int Win32_ConvertSidToStringSidW = 4;
  27. private const int Win32_CreateWellKnownSid = 5;
  28. static public readonly DbConnectionPoolIdentity NoIdentity = new DbConnectionPoolIdentity(String.Empty, false, true);
  29. static private readonly byte[] NetworkSid = (ADP.IsWindowsNT ? CreateWellKnownSid(WellKnownSidType.NetworkSid) : null);
  30. static private DbConnectionPoolIdentity _lastIdentity = null;
  31. private readonly string _sidString;
  32. private readonly bool _isRestricted;
  33. private readonly bool _isNetwork;
  34. private readonly int _hashCode;
  35. private DbConnectionPoolIdentity (string sidString, bool isRestricted, bool isNetwork) {
  36. _sidString = sidString;
  37. _isRestricted = isRestricted;
  38. _isNetwork = isNetwork;
  39. _hashCode = sidString == null ? 0 : sidString.GetHashCode();
  40. }
  41. internal bool IsRestricted {
  42. get { return _isRestricted; }
  43. }
  44. internal bool IsNetwork {
  45. get { return _isNetwork; }
  46. }
  47. static private byte[] CreateWellKnownSid(WellKnownSidType sidType) {
  48. // Passing an array as big as it can ever be is a small price to pay for
  49. // not having to P/Invoke twice (once to get the buffer, once to get the data)
  50. uint length = ( uint )SecurityIdentifier.MaxBinaryLength;
  51. byte[] resultSid = new byte[ length ];
  52. // NOTE - We copied this code from System.Security.Principal.Win32.CreateWellKnownSid...
  53. if ( 0 == UnsafeNativeMethods.CreateWellKnownSid(( int )sidType, null, resultSid, ref length )) {
  54. IntegratedSecurityError(Win32_CreateWellKnownSid);
  55. }
  56. return resultSid;
  57. }
  58. override public bool Equals(object value) {
  59. bool result = ((this == NoIdentity) || (this == value));
  60. if (!result && (null != value)) {
  61. DbConnectionPoolIdentity that = ((DbConnectionPoolIdentity) value);
  62. result = ((this._sidString == that._sidString) && (this._isRestricted == that._isRestricted) && (this._isNetwork == that._isNetwork));
  63. }
  64. return result;
  65. }
  66. [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.ControlPrincipal)]
  67. static internal WindowsIdentity GetCurrentWindowsIdentity() {
  68. return WindowsIdentity.GetCurrent();
  69. }
  70. [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.UnmanagedCode)]
  71. static private IntPtr GetWindowsIdentityToken(WindowsIdentity identity) {
  72. return identity.Token;
  73. }
  74. [ResourceExposure(ResourceScope.None)] // SxS: this method does not create named objects
  75. [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)]
  76. static internal DbConnectionPoolIdentity GetCurrent() {
  77. // DEVNOTE: GetTokenInfo and EqualSID do not work on 9x. WindowsIdentity does not
  78. // work either on 9x. In fact, after checking with native there is no way
  79. // to validate the user on 9x, so simply don't. It is a known issue in
  80. // native, and we will handle this the same way.
  81. if (!ADP.IsWindowsNT) {
  82. return NoIdentity;
  83. }
  84. WindowsIdentity identity = GetCurrentWindowsIdentity();
  85. IntPtr token = GetWindowsIdentityToken(identity); // Free'd by WindowsIdentity.
  86. uint bufferLength = 2048; // Suggested default given by Greg Fee.
  87. uint lengthNeeded = 0;
  88. IntPtr tokenStruct = IntPtr.Zero;
  89. IntPtr SID;
  90. IntPtr sidStringBuffer = IntPtr.Zero;
  91. bool isNetwork;
  92. // Win32NativeMethods.IsTokenRestricted will raise exception if the native call fails
  93. bool isRestricted = Win32NativeMethods.IsTokenRestrictedWrapper(token);
  94. DbConnectionPoolIdentity current = null;
  95. RuntimeHelpers.PrepareConstrainedRegions();
  96. try {
  97. if (!UnsafeNativeMethods.CheckTokenMembership(token, NetworkSid, out isNetwork)) {
  98. // will always fail with 0x8007051D if token is not an impersonation token
  99. IntegratedSecurityError(Win32_CheckTokenMembership);
  100. }
  101. RuntimeHelpers.PrepareConstrainedRegions();
  102. try { } finally {
  103. // allocating memory and assigning to tokenStruct must happen
  104. tokenStruct = SafeNativeMethods.LocalAlloc(DbBuffer.LMEM_FIXED, (IntPtr)bufferLength);
  105. }
  106. if (IntPtr.Zero == tokenStruct) {
  107. throw new OutOfMemoryException();
  108. }
  109. if (!UnsafeNativeMethods.GetTokenInformation(token, 1, tokenStruct, bufferLength, ref lengthNeeded)) {
  110. if (lengthNeeded > bufferLength) {
  111. bufferLength = lengthNeeded;
  112. RuntimeHelpers.PrepareConstrainedRegions();
  113. try { } finally {
  114. // freeing token struct and setting tokenstruct to null must happen together
  115. // allocating memory and assigning to tokenStruct must happen
  116. SafeNativeMethods.LocalFree(tokenStruct);
  117. tokenStruct = IntPtr.Zero; // protect against LocalAlloc throwing an exception
  118. tokenStruct = SafeNativeMethods.LocalAlloc(DbBuffer.LMEM_FIXED, (IntPtr)bufferLength);
  119. }
  120. if (IntPtr.Zero == tokenStruct) {
  121. throw new OutOfMemoryException();
  122. }
  123. if (!UnsafeNativeMethods.GetTokenInformation(token, 1, tokenStruct, bufferLength, ref lengthNeeded)) {
  124. IntegratedSecurityError(Win32_GetTokenInformation_1);
  125. }
  126. }
  127. else {
  128. IntegratedSecurityError(Win32_GetTokenInformation_2);
  129. }
  130. }
  131. identity.Dispose(); // Keep identity variable alive until after GetTokenInformation calls.
  132. SID = Marshal.ReadIntPtr(tokenStruct, 0);
  133. if (!UnsafeNativeMethods.ConvertSidToStringSidW(SID, out sidStringBuffer)) {
  134. IntegratedSecurityError(Win32_ConvertSidToStringSidW);
  135. }
  136. if (IntPtr.Zero == sidStringBuffer) {
  137. throw ADP.InternalError(ADP.InternalErrorCode.ConvertSidToStringSidWReturnedNull);
  138. }
  139. string sidString = Marshal.PtrToStringUni(sidStringBuffer);
  140. var lastIdentity = _lastIdentity;
  141. if ((lastIdentity != null) && (lastIdentity._sidString == sidString) && (lastIdentity._isRestricted == isRestricted) && (lastIdentity._isNetwork == isNetwork)) {
  142. current = lastIdentity;
  143. }
  144. else {
  145. current = new DbConnectionPoolIdentity(sidString, isRestricted, isNetwork);
  146. }
  147. }
  148. finally {
  149. // Marshal.FreeHGlobal does not have a ReliabilityContract
  150. if (IntPtr.Zero != tokenStruct) {
  151. SafeNativeMethods.LocalFree(tokenStruct);
  152. tokenStruct = IntPtr.Zero;
  153. }
  154. if (IntPtr.Zero != sidStringBuffer) {
  155. SafeNativeMethods.LocalFree(sidStringBuffer);
  156. sidStringBuffer = IntPtr.Zero;
  157. }
  158. }
  159. _lastIdentity = current;
  160. return current;
  161. }
  162. override public int GetHashCode() {
  163. return _hashCode;
  164. }
  165. static private void IntegratedSecurityError(int caller) {
  166. #if !FULL_AOT_RUNTIME
  167. // passing 1,2,3,4,5 instead of true/false so that with a debugger
  168. // we could determine more easily which Win32 method call failed
  169. int lastError = Marshal.GetHRForLastWin32Error();
  170. if ((Win32_CheckTokenMembership != caller) || (E_NotImpersonationToken != lastError)) {
  171. Marshal.ThrowExceptionForHR(lastError); // will only throw if (hresult < 0)
  172. }
  173. #endif
  174. }
  175. }
  176. }