File: Microsoft\Win32\SafeHandles\CapiSafeHandles.cs
Project: ndp\fx\src\Core\System.Core.csproj (System.Core)
// ==++==
// 
//   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 {
    /// <summary>
    ///     SafeHandle for buffers returned by the Axl APIs
    /// </summary>
#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;
        }
    }
 
    /// <summary>
    ///     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.
    /// </summary>
#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;
        }
    }
 
    /// <summary>
    ///     SafeHandle for CAPI hash algorithms (HCRYPTHASH)
    /// </summary>
#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() {
        }
 
        /// <summary>
        ///     NULL hash handle
        /// </summary>
        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);
        }
    }
 
    /// <summary>
    ///     SafeHandle for CAPI keys (HCRYPTKEY)
    /// </summary>
#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()  {
        }
 
        /// <summary>
        ///     NULL key handle
        /// </summary>
        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);
 
        /// <summary>
        ///     Make a copy of this key handle
        /// </summary>
        [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] 
#if FEATURE_CORESYSTEM
        [System.Security.SecurityCritical]
#endif
        internal SafeCapiKeyHandle Duplicate() {
            Contract.Requires(!IsInvalid && !IsClosed);
            Contract.Ensures(Contract.Result<SafeCapiKeyHandle>() != null && !Contract.Result<SafeCapiKeyHandle>().IsInvalid && !Contract.Result<SafeCapiKeyHandle>().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);
        }
    }
 
    /// <summary>
    ///     SafeHandle for crypto service providers (HCRYPTPROV)
    /// </summary>
#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);
 
        /// <summary>
        ///     Create a second SafeCspHandle which refers to the same HCRYPTPROV
        /// </summary>
        [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);
        }
    }
}