// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Security; using System.Security.Cryptography; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; namespace Microsoft.Win32.SafeHandles { /// /// SafeHandle for buffers returned by the Axl APIs /// #if !FEATURE_CORESYSTEM #pragma warning disable 618 // Have not migrated to v4 transparency yet [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] #pragma warning restore 618 #endif internal sealed class SafeAxlBufferHandle : SafeHandleZeroOrMinusOneIsInvalid { private SafeAxlBufferHandle() : base(true) { return; } [DllImport("kernel32")] #if !FEATURE_CORESYSTEM [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] #endif [SuppressUnmanagedCodeSecurity] private static extern IntPtr GetProcessHeap(); [DllImport("kernel32")] #if !FEATURE_CORESYSTEM [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] #endif [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool HeapFree(IntPtr hHeap, int dwFlags, IntPtr lpMem); protected override bool ReleaseHandle() { // _AxlFree is a wrapper around HeapFree on the process heap. Since it is not exported from mscorwks // we just call HeapFree directly. This needs to be updated if _AxlFree is ever changed. HeapFree(GetProcessHeap(), 0, handle); return true; } } /// /// SafeHandle base class for CAPI handles (such as HCRYPTKEY and HCRYPTHASH) which must keep their /// CSP alive as long as they stay alive as well. CAPI requires that all child handles belonging to a /// HCRYPTPROV must be destroyed up before the reference count to the HCRYPTPROV drops to zero. /// Since we cannot control the order of finalization between the two safe handles, SafeCapiHandleBase /// maintains a native refcount on its parent HCRYPTPROV to ensure that if the corresponding /// SafeCspKeyHandle is finalized first CAPI still keeps the provider alive. /// #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #else #pragma warning disable 618 // Have not migrated to v4 transparency yet [SecurityCritical(SecurityCriticalScope.Everything)] #pragma warning restore 618 #endif internal abstract class SafeCapiHandleBase : SafeHandleZeroOrMinusOneIsInvalid { private IntPtr m_csp; #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] internal SafeCapiHandleBase() : base(true) { } #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif [DllImport("advapi32", SetLastError = true)] #if !FEATURE_CORESYSTEM [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] #endif [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CryptContextAddRef(IntPtr hProv, IntPtr pdwReserved, int dwFlags); #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif [DllImport("advapi32")] #if !FEATURE_CORESYSTEM [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] #endif [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags); protected IntPtr ParentCsp { get { return m_csp; } #if !FEATURE_CORESYSTEM [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] #endif set { // We should not be resetting the parent CSP if it's already been set once - that will // lead to leaking the original handle. Debug.Assert(m_csp == IntPtr.Zero); int error = (int)CapiNative.ErrorCode.Success; // A successful call to CryptContextAddRef and an assignment of the handle value to our field // SafeHandle need to happen atomically, so we contain them within a CER. RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { if (CryptContextAddRef(value, IntPtr.Zero, 0)) { m_csp = value; } else { error = Marshal.GetLastWin32Error(); } } if (error != (int)CapiNative.ErrorCode.Success) { throw new CryptographicException(error); } } } #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif #if !FEATURE_CORESYSTEM [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] #endif internal void SetParentCsp(SafeCspHandle parentCsp) { bool addedRef = false; RuntimeHelpers.PrepareConstrainedRegions(); try { parentCsp.DangerousAddRef(ref addedRef); IntPtr rawParentHandle = parentCsp.DangerousGetHandle(); ParentCsp = rawParentHandle; } finally { if (addedRef) { parentCsp.DangerousRelease(); } } } #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif protected abstract bool ReleaseCapiChildHandle(); #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif protected override sealed bool ReleaseHandle() { // Order is important here - we must destroy the child handle before the parent CSP bool destroyedChild = ReleaseCapiChildHandle(); bool releasedCsp = true; if (m_csp != IntPtr.Zero) { releasedCsp = CryptReleaseContext(m_csp, 0); } return destroyedChild && releasedCsp; } } /// /// SafeHandle for CAPI hash algorithms (HCRYPTHASH) /// #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #else #pragma warning disable 618 // Have not migrated to v4 transparency yet [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] #pragma warning restore 618 #endif internal sealed class SafeCapiHashHandle : SafeCapiHandleBase { private static volatile SafeCapiHashHandle s_invalidHandle; #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif private SafeCapiHashHandle() { } /// /// NULL hash handle /// public static SafeCapiHashHandle InvalidHandle { get { if (s_invalidHandle == null) { // More than one of these might get created in parallel, but that's okay. // Saving one to the field saves on GC tracking, but by SuppressingFinalize on // any instance returned there's already less finalization pressure. SafeCapiHashHandle handle = new SafeCapiHashHandle(); handle.SetHandle(IntPtr.Zero); GC.SuppressFinalize(handle); s_invalidHandle = handle; } return s_invalidHandle; } } #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif [DllImport("advapi32")] #if !FEATURE_CORESYSTEM [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] #endif [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CryptDestroyHash(IntPtr hHash); #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif protected override bool ReleaseCapiChildHandle() { return CryptDestroyHash(handle); } } /// /// SafeHandle for CAPI keys (HCRYPTKEY) /// #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #else #pragma warning disable 618 // Have not migrated to v4 transparency yet [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] #pragma warning restore 618 #endif internal sealed class SafeCapiKeyHandle : SafeCapiHandleBase { private static volatile SafeCapiKeyHandle s_invalidHandle; #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif private SafeCapiKeyHandle() { } /// /// NULL key handle /// internal static SafeCapiKeyHandle InvalidHandle { [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] get { if (s_invalidHandle == null) { // More than one of these might get created in parallel, but that's okay. // Saving one to the field saves on GC tracking, but by SuppressingFinalize on // any instance returned there's already less finalization pressure. SafeCapiKeyHandle handle = new SafeCapiKeyHandle(); handle.SetHandle(IntPtr.Zero); GC.SuppressFinalize(handle); s_invalidHandle = handle; } return s_invalidHandle; } } [DllImport("advapi32")] #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #else [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] #endif [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CryptDestroyKey(IntPtr hKey); /// /// Make a copy of this key handle /// [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif internal SafeCapiKeyHandle Duplicate() { Contract.Requires(!IsInvalid && !IsClosed); Contract.Ensures(Contract.Result() != null && !Contract.Result().IsInvalid && !Contract.Result().IsClosed); SafeCapiKeyHandle duplicate = null; RuntimeHelpers.PrepareConstrainedRegions(); try { if (!CapiNative.UnsafeNativeMethods.CryptDuplicateKey(this, IntPtr.Zero, 0, out duplicate)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } } finally { if (duplicate != null && !duplicate.IsInvalid && ParentCsp != IntPtr.Zero) { duplicate.ParentCsp = ParentCsp; } } return duplicate; } #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif protected override bool ReleaseCapiChildHandle() { return CryptDestroyKey(handle); } } /// /// SafeHandle for crypto service providers (HCRYPTPROV) /// #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #else #pragma warning disable 618 // Have not migrated to v4 transparency yet [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)] #pragma warning restore 618 #endif internal sealed class SafeCspHandle : SafeHandleZeroOrMinusOneIsInvalid { [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif private SafeCspHandle() : base(true) { return; } [DllImport("advapi32", SetLastError = true)] #if !FEATURE_CORESYSTEM [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] #endif [SuppressUnmanagedCodeSecurity] #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CryptContextAddRef(SafeCspHandle hProv, IntPtr pdwReserved, int dwFlags); [DllImport("advapi32")] #if !FEATURE_CORESYSTEM [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] #endif [SuppressUnmanagedCodeSecurity] #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags); /// /// Create a second SafeCspHandle which refers to the same HCRYPTPROV /// [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif public SafeCspHandle Duplicate() { Contract.Requires(!IsInvalid && !IsClosed); // In the window between the call to CryptContextAddRef and when the raw handle value is assigned // into this safe handle, there's a second reference to the original safe handle that the CLR does // not know about, so we need to bump the reference count around this entire operation to ensure // that we don't have the original handle closed underneath us. bool acquired = false; RuntimeHelpers.PrepareConstrainedRegions(); try { DangerousAddRef(ref acquired); IntPtr originalHandle = DangerousGetHandle(); int error = (int)CapiNative.ErrorCode.Success; SafeCspHandle duplicate = new SafeCspHandle(); // A successful call to CryptContextAddRef and an assignment of the handle value to the duplicate // SafeHandle need to happen atomically, so we contain them within a CER. RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { if (!CryptContextAddRef(this, IntPtr.Zero, 0)) { error = Marshal.GetLastWin32Error(); } else { duplicate.SetHandle(originalHandle); } } // If we could not call CryptContextAddRef succesfully, then throw the error here otherwise // we should be in a valid state at this point. if (error != (int)CapiNative.ErrorCode.Success) { duplicate.Dispose(); throw new CryptographicException(error); } else { Debug.Assert(!duplicate.IsInvalid, "Failed to duplicate handle successfully"); } return duplicate; } finally { if (acquired) { DangerousRelease(); } } } #if FEATURE_CORESYSTEM [System.Security.SecurityCritical] #endif protected override bool ReleaseHandle() { return CryptReleaseContext(handle, 0); } } }