1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 7 using System; 8 using System.Diagnostics; 9 using System.Runtime.CompilerServices; 10 using System.Runtime.ConstrainedExecution; 11 using System.Runtime.InteropServices; 12 using System.Security; 13 using System.Security.Cryptography; 14 using System.Diagnostics.CodeAnalysis; 15 using System.Diagnostics.Contracts; 16 17 namespace Microsoft.Win32.SafeHandles { 18 /// <summary> 19 /// SafeHandle for buffers returned by the Axl APIs 20 /// </summary> 21 #if !FEATURE_CORESYSTEM 22 #pragma warning disable 618 // Have not migrated to v4 transparency yet 23 [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] 24 #pragma warning restore 618 25 #endif 26 internal sealed class SafeAxlBufferHandle : SafeHandleZeroOrMinusOneIsInvalid { SafeAxlBufferHandle()27 private SafeAxlBufferHandle() : base(true) { 28 return; 29 } 30 31 [DllImport("kernel32")] 32 #if !FEATURE_CORESYSTEM 33 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 34 #endif 35 [SuppressUnmanagedCodeSecurity] GetProcessHeap()36 private static extern IntPtr GetProcessHeap(); 37 38 [DllImport("kernel32")] 39 #if !FEATURE_CORESYSTEM 40 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 41 #endif 42 [SuppressUnmanagedCodeSecurity] 43 [return: MarshalAs(UnmanagedType.Bool)] HeapFree(IntPtr hHeap, int dwFlags, IntPtr lpMem)44 private static extern bool HeapFree(IntPtr hHeap, int dwFlags, IntPtr lpMem); 45 ReleaseHandle()46 protected override bool ReleaseHandle() { 47 // _AxlFree is a wrapper around HeapFree on the process heap. Since it is not exported from mscorwks 48 // we just call HeapFree directly. This needs to be updated if _AxlFree is ever changed. 49 HeapFree(GetProcessHeap(), 0, handle); 50 return true; 51 } 52 } 53 54 /// <summary> 55 /// SafeHandle base class for CAPI handles (such as HCRYPTKEY and HCRYPTHASH) which must keep their 56 /// CSP alive as long as they stay alive as well. CAPI requires that all child handles belonging to a 57 /// HCRYPTPROV must be destroyed up before the reference count to the HCRYPTPROV drops to zero. 58 /// Since we cannot control the order of finalization between the two safe handles, SafeCapiHandleBase 59 /// maintains a native refcount on its parent HCRYPTPROV to ensure that if the corresponding 60 /// SafeCspKeyHandle is finalized first CAPI still keeps the provider alive. 61 /// </summary> 62 #if FEATURE_CORESYSTEM 63 [System.Security.SecurityCritical] 64 #else 65 #pragma warning disable 618 // Have not migrated to v4 transparency yet 66 [SecurityCritical(SecurityCriticalScope.Everything)] 67 #pragma warning restore 618 68 #endif 69 internal abstract class SafeCapiHandleBase : SafeHandleZeroOrMinusOneIsInvalid { 70 private IntPtr m_csp; 71 72 #if FEATURE_CORESYSTEM 73 [System.Security.SecurityCritical] 74 #endif 75 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] SafeCapiHandleBase()76 internal SafeCapiHandleBase() : base(true) { 77 } 78 79 #if FEATURE_CORESYSTEM 80 [System.Security.SecurityCritical] 81 #endif 82 [DllImport("advapi32", SetLastError = true)] 83 #if !FEATURE_CORESYSTEM 84 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 85 #endif 86 [SuppressUnmanagedCodeSecurity] 87 [return: MarshalAs(UnmanagedType.Bool)] CryptContextAddRef(IntPtr hProv, IntPtr pdwReserved, int dwFlags)88 private static extern bool CryptContextAddRef(IntPtr hProv, 89 IntPtr pdwReserved, 90 int dwFlags); 91 92 #if FEATURE_CORESYSTEM 93 [System.Security.SecurityCritical] 94 #endif 95 [DllImport("advapi32")] 96 #if !FEATURE_CORESYSTEM 97 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 98 #endif 99 [SuppressUnmanagedCodeSecurity] 100 [return: MarshalAs(UnmanagedType.Bool)] CryptReleaseContext(IntPtr hProv, int dwFlags)101 private static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags); 102 103 104 protected IntPtr ParentCsp { 105 get { return m_csp; } 106 107 #if !FEATURE_CORESYSTEM 108 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 109 #endif 110 set { 111 // We should not be resetting the parent CSP if it's already been set once - that will 112 // lead to leaking the original handle. 113 Debug.Assert(m_csp == IntPtr.Zero); 114 115 int error = (int)CapiNative.ErrorCode.Success; 116 117 // A successful call to CryptContextAddRef and an assignment of the handle value to our field 118 // SafeHandle need to happen atomically, so we contain them within a CER. 119 RuntimeHelpers.PrepareConstrainedRegions(); 120 try { } 121 finally { 122 if (CryptContextAddRef(value, IntPtr.Zero, 0)) { 123 m_csp = value; 124 } 125 else { 126 error = Marshal.GetLastWin32Error(); 127 } 128 } 129 130 if (error != (int)CapiNative.ErrorCode.Success) { 131 throw new CryptographicException(error); 132 } 133 } 134 } 135 136 #if FEATURE_CORESYSTEM 137 [System.Security.SecurityCritical] 138 #endif 139 #if !FEATURE_CORESYSTEM 140 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 141 #endif SetParentCsp(SafeCspHandle parentCsp)142 internal void SetParentCsp(SafeCspHandle parentCsp) { 143 bool addedRef = false; 144 RuntimeHelpers.PrepareConstrainedRegions(); 145 try { 146 parentCsp.DangerousAddRef(ref addedRef); 147 IntPtr rawParentHandle = parentCsp.DangerousGetHandle(); 148 ParentCsp = rawParentHandle; 149 } 150 finally { 151 if (addedRef) { 152 parentCsp.DangerousRelease(); 153 } 154 } 155 } 156 157 #if FEATURE_CORESYSTEM 158 [System.Security.SecurityCritical] 159 #endif ReleaseCapiChildHandle()160 protected abstract bool ReleaseCapiChildHandle(); 161 162 #if FEATURE_CORESYSTEM 163 [System.Security.SecurityCritical] 164 #endif ReleaseHandle()165 protected override sealed bool ReleaseHandle() { 166 // Order is important here - we must destroy the child handle before the parent CSP 167 bool destroyedChild = ReleaseCapiChildHandle(); 168 bool releasedCsp = true; 169 170 if (m_csp != IntPtr.Zero) { 171 releasedCsp = CryptReleaseContext(m_csp, 0); 172 } 173 174 return destroyedChild && releasedCsp; 175 } 176 } 177 178 /// <summary> 179 /// SafeHandle for CAPI hash algorithms (HCRYPTHASH) 180 /// </summary> 181 #if FEATURE_CORESYSTEM 182 [System.Security.SecurityCritical] 183 #else 184 #pragma warning disable 618 // Have not migrated to v4 transparency yet 185 [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] 186 #pragma warning restore 618 187 #endif 188 internal sealed class SafeCapiHashHandle : SafeCapiHandleBase { 189 private static volatile SafeCapiHashHandle s_invalidHandle; 190 191 #if FEATURE_CORESYSTEM 192 [System.Security.SecurityCritical] 193 #endif SafeCapiHashHandle()194 private SafeCapiHashHandle() { 195 } 196 197 /// <summary> 198 /// NULL hash handle 199 /// </summary> 200 public static SafeCapiHashHandle InvalidHandle { 201 get { 202 if (s_invalidHandle == null) { 203 // More than one of these might get created in parallel, but that's okay. 204 // Saving one to the field saves on GC tracking, but by SuppressingFinalize on 205 // any instance returned there's already less finalization pressure. 206 SafeCapiHashHandle handle = new SafeCapiHashHandle(); 207 handle.SetHandle(IntPtr.Zero); 208 GC.SuppressFinalize(handle); 209 s_invalidHandle = handle; 210 } 211 212 return s_invalidHandle; 213 } 214 } 215 216 #if FEATURE_CORESYSTEM 217 [System.Security.SecurityCritical] 218 #endif 219 [DllImport("advapi32")] 220 #if !FEATURE_CORESYSTEM 221 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 222 #endif 223 [SuppressUnmanagedCodeSecurity] 224 [return: MarshalAs(UnmanagedType.Bool)] CryptDestroyHash(IntPtr hHash)225 private static extern bool CryptDestroyHash(IntPtr hHash); 226 227 #if FEATURE_CORESYSTEM 228 [System.Security.SecurityCritical] 229 #endif ReleaseCapiChildHandle()230 protected override bool ReleaseCapiChildHandle() { 231 return CryptDestroyHash(handle); 232 } 233 } 234 235 /// <summary> 236 /// SafeHandle for CAPI keys (HCRYPTKEY) 237 /// </summary> 238 #if FEATURE_CORESYSTEM 239 [System.Security.SecurityCritical] 240 #else 241 #pragma warning disable 618 // Have not migrated to v4 transparency yet 242 [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] 243 #pragma warning restore 618 244 #endif 245 internal sealed class SafeCapiKeyHandle : SafeCapiHandleBase { 246 private static volatile SafeCapiKeyHandle s_invalidHandle; 247 248 #if FEATURE_CORESYSTEM 249 [System.Security.SecurityCritical] 250 #endif SafeCapiKeyHandle()251 private SafeCapiKeyHandle() { 252 } 253 254 /// <summary> 255 /// NULL key handle 256 /// </summary> 257 internal static SafeCapiKeyHandle InvalidHandle { 258 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] 259 get { 260 if (s_invalidHandle == null) { 261 // More than one of these might get created in parallel, but that's okay. 262 // Saving one to the field saves on GC tracking, but by SuppressingFinalize on 263 // any instance returned there's already less finalization pressure. 264 SafeCapiKeyHandle handle = new SafeCapiKeyHandle(); 265 handle.SetHandle(IntPtr.Zero); 266 GC.SuppressFinalize(handle); 267 s_invalidHandle = handle; 268 } 269 270 return s_invalidHandle; 271 } 272 } 273 274 [DllImport("advapi32")] 275 #if FEATURE_CORESYSTEM 276 [System.Security.SecurityCritical] 277 #else 278 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 279 #endif 280 [SuppressUnmanagedCodeSecurity] 281 [return: MarshalAs(UnmanagedType.Bool)] CryptDestroyKey(IntPtr hKey)282 private static extern bool CryptDestroyKey(IntPtr hKey); 283 284 /// <summary> 285 /// Make a copy of this key handle 286 /// </summary> 287 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] 288 #if FEATURE_CORESYSTEM 289 [System.Security.SecurityCritical] 290 #endif Duplicate()291 internal SafeCapiKeyHandle Duplicate() { 292 Contract.Requires(!IsInvalid && !IsClosed); 293 Contract.Ensures(Contract.Result<SafeCapiKeyHandle>() != null && !Contract.Result<SafeCapiKeyHandle>().IsInvalid && !Contract.Result<SafeCapiKeyHandle>().IsClosed); 294 295 SafeCapiKeyHandle duplicate = null; 296 297 RuntimeHelpers.PrepareConstrainedRegions(); 298 try { 299 if (!CapiNative.UnsafeNativeMethods.CryptDuplicateKey(this, IntPtr.Zero, 0, out duplicate)) { 300 throw new CryptographicException(Marshal.GetLastWin32Error()); 301 } 302 } 303 finally { 304 if (duplicate != null && !duplicate.IsInvalid && ParentCsp != IntPtr.Zero) { 305 duplicate.ParentCsp = ParentCsp; 306 } 307 } 308 309 return duplicate; 310 } 311 312 #if FEATURE_CORESYSTEM 313 [System.Security.SecurityCritical] 314 #endif ReleaseCapiChildHandle()315 protected override bool ReleaseCapiChildHandle() { 316 return CryptDestroyKey(handle); 317 } 318 } 319 320 /// <summary> 321 /// SafeHandle for crypto service providers (HCRYPTPROV) 322 /// </summary> 323 #if FEATURE_CORESYSTEM 324 [System.Security.SecurityCritical] 325 #else 326 #pragma warning disable 618 // Have not migrated to v4 transparency yet 327 [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] 328 #pragma warning restore 618 329 #endif 330 internal sealed class SafeCspHandle : SafeHandleZeroOrMinusOneIsInvalid { 331 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] 332 #if FEATURE_CORESYSTEM 333 [System.Security.SecurityCritical] 334 #endif SafeCspHandle()335 private SafeCspHandle() : base(true) { 336 return; 337 } 338 339 [DllImport("advapi32", SetLastError = true)] 340 #if !FEATURE_CORESYSTEM 341 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 342 #endif 343 [SuppressUnmanagedCodeSecurity] 344 #if FEATURE_CORESYSTEM 345 [System.Security.SecurityCritical] 346 #endif 347 [return: MarshalAs(UnmanagedType.Bool)] CryptContextAddRef(SafeCspHandle hProv, IntPtr pdwReserved, int dwFlags)348 private static extern bool CryptContextAddRef(SafeCspHandle hProv, 349 IntPtr pdwReserved, 350 int dwFlags); 351 352 [DllImport("advapi32")] 353 #if !FEATURE_CORESYSTEM 354 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 355 #endif 356 [SuppressUnmanagedCodeSecurity] 357 #if FEATURE_CORESYSTEM 358 [System.Security.SecurityCritical] 359 #endif 360 [return: MarshalAs(UnmanagedType.Bool)] CryptReleaseContext(IntPtr hProv, int dwFlags)361 private static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags); 362 363 /// <summary> 364 /// Create a second SafeCspHandle which refers to the same HCRYPTPROV 365 /// </summary> 366 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] 367 #if FEATURE_CORESYSTEM 368 [System.Security.SecurityCritical] 369 #endif Duplicate()370 public SafeCspHandle Duplicate() { 371 Contract.Requires(!IsInvalid && !IsClosed); 372 373 // In the window between the call to CryptContextAddRef and when the raw handle value is assigned 374 // into this safe handle, there's a second reference to the original safe handle that the CLR does 375 // not know about, so we need to bump the reference count around this entire operation to ensure 376 // that we don't have the original handle closed underneath us. 377 bool acquired = false; 378 RuntimeHelpers.PrepareConstrainedRegions(); 379 try { 380 DangerousAddRef(ref acquired); 381 IntPtr originalHandle = DangerousGetHandle(); 382 383 int error = (int)CapiNative.ErrorCode.Success; 384 385 SafeCspHandle duplicate = new SafeCspHandle(); 386 387 // A successful call to CryptContextAddRef and an assignment of the handle value to the duplicate 388 // SafeHandle need to happen atomically, so we contain them within a CER. 389 RuntimeHelpers.PrepareConstrainedRegions(); 390 try { } 391 finally { 392 if (!CryptContextAddRef(this, IntPtr.Zero, 0)) { 393 error = Marshal.GetLastWin32Error(); 394 } 395 else { 396 duplicate.SetHandle(originalHandle); 397 } 398 } 399 400 // If we could not call CryptContextAddRef succesfully, then throw the error here otherwise 401 // we should be in a valid state at this point. 402 if (error != (int)CapiNative.ErrorCode.Success) { 403 duplicate.Dispose(); 404 throw new CryptographicException(error); 405 } 406 else { 407 Debug.Assert(!duplicate.IsInvalid, "Failed to duplicate handle successfully"); 408 } 409 410 return duplicate; 411 } 412 finally { 413 if (acquired) { 414 DangerousRelease(); 415 } 416 } 417 } 418 419 #if FEATURE_CORESYSTEM 420 [System.Security.SecurityCritical] 421 #endif ReleaseHandle()422 protected override bool ReleaseHandle() { 423 return CryptReleaseContext(handle, 0); 424 } 425 } 426 } 427