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