[coop] Temporarily restore MonoThreadInfo when TLS destructor runs. Fixes #43099
[mono.git] / mcs / class / referencesource / System.Core / Microsoft / Win32 / SafeHandles / CapiSafeHandles.cs
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 {
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]
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)]
44         private static extern bool HeapFree(IntPtr hHeap, int dwFlags, IntPtr lpMem);
45
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")]
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)]
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)]
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
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
160         protected abstract bool ReleaseCapiChildHandle();
161
162 #if FEATURE_CORESYSTEM
163         [System.Security.SecurityCritical]
164 #endif
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 #if FEATURE_CORESYSTEM
190         [System.Security.SecurityCritical]
191 #endif
192         private SafeCapiHashHandle() {
193         }
194
195         /// <summary>
196         ///     NULL hash handle
197         /// </summary>
198         public static SafeCapiHashHandle InvalidHandle {
199             get {
200                 SafeCapiHashHandle handle = new SafeCapiHashHandle();
201                 handle.SetHandle(IntPtr.Zero);
202                 return handle;
203             }
204         }
205
206 #if FEATURE_CORESYSTEM
207         [System.Security.SecurityCritical]
208 #endif
209         [DllImport("advapi32")]
210 #if !FEATURE_CORESYSTEM
211         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
212 #endif
213         [SuppressUnmanagedCodeSecurity]
214         [return: MarshalAs(UnmanagedType.Bool)]
215         private static extern bool CryptDestroyHash(IntPtr hHash);
216
217 #if FEATURE_CORESYSTEM
218         [System.Security.SecurityCritical]
219 #endif
220         protected override bool ReleaseCapiChildHandle() {
221             return CryptDestroyHash(handle);
222         }
223     }
224
225     /// <summary>
226     ///     SafeHandle for CAPI keys (HCRYPTKEY)
227     /// </summary>
228 #if FEATURE_CORESYSTEM
229     [System.Security.SecurityCritical]
230 #else
231 #pragma warning disable 618    // Have not migrated to v4 transparency yet
232     [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)]
233 #pragma warning restore 618
234 #endif
235     internal sealed class SafeCapiKeyHandle : SafeCapiHandleBase {
236 #if FEATURE_CORESYSTEM
237         [System.Security.SecurityCritical]
238 #endif
239         private SafeCapiKeyHandle()  {
240         }
241
242         /// <summary>
243         ///     NULL key handle
244         /// </summary>
245         internal static SafeCapiKeyHandle InvalidHandle {
246             [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
247             get {
248                 SafeCapiKeyHandle handle = new SafeCapiKeyHandle();
249                 handle.SetHandle(IntPtr.Zero);
250                 return handle;
251             }
252         }
253
254         [DllImport("advapi32")]
255 #if FEATURE_CORESYSTEM
256         [System.Security.SecurityCritical]
257 #else
258         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
259 #endif
260         [SuppressUnmanagedCodeSecurity]
261         [return: MarshalAs(UnmanagedType.Bool)]
262         private static extern bool CryptDestroyKey(IntPtr hKey);
263
264         /// <summary>
265         ///     Make a copy of this key handle
266         /// </summary>
267         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] 
268 #if FEATURE_CORESYSTEM
269         [System.Security.SecurityCritical]
270 #endif
271         internal SafeCapiKeyHandle Duplicate() {
272             Contract.Requires(!IsInvalid && !IsClosed);
273             Contract.Ensures(Contract.Result<SafeCapiKeyHandle>() != null && !Contract.Result<SafeCapiKeyHandle>().IsInvalid && !Contract.Result<SafeCapiKeyHandle>().IsClosed);
274
275             SafeCapiKeyHandle duplicate = null;
276
277             RuntimeHelpers.PrepareConstrainedRegions();
278             try {
279                 if (!CapiNative.UnsafeNativeMethods.CryptDuplicateKey(this, IntPtr.Zero, 0, out duplicate)) {
280                     throw new CryptographicException(Marshal.GetLastWin32Error());
281                 }
282             }
283             finally {
284                 if (duplicate != null && !duplicate.IsInvalid && ParentCsp != IntPtr.Zero) {
285                     duplicate.ParentCsp = ParentCsp;
286                 }
287             }
288
289             return duplicate;
290         }
291
292 #if FEATURE_CORESYSTEM
293         [System.Security.SecurityCritical]
294 #endif
295         protected override bool ReleaseCapiChildHandle() {
296             return CryptDestroyKey(handle);
297         }
298     }
299
300     /// <summary>
301     ///     SafeHandle for crypto service providers (HCRYPTPROV)
302     /// </summary>
303 #if FEATURE_CORESYSTEM
304     [System.Security.SecurityCritical]
305 #else
306 #pragma warning disable 618    // Have not migrated to v4 transparency yet
307     [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)]
308 #pragma warning restore 618
309 #endif
310     internal sealed class SafeCspHandle : SafeHandleZeroOrMinusOneIsInvalid {
311         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
312 #if FEATURE_CORESYSTEM
313         [System.Security.SecurityCritical]
314 #endif
315         private SafeCspHandle() : base(true) {
316             return;
317         }
318
319         [DllImport("advapi32", SetLastError = true)]
320 #if !FEATURE_CORESYSTEM
321         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
322 #endif
323         [SuppressUnmanagedCodeSecurity]
324 #if FEATURE_CORESYSTEM
325         [System.Security.SecurityCritical]
326 #endif
327         [return: MarshalAs(UnmanagedType.Bool)]
328         private static extern bool CryptContextAddRef(SafeCspHandle hProv,
329                                                      IntPtr pdwReserved,
330                                                      int dwFlags);
331
332         [DllImport("advapi32")]
333 #if !FEATURE_CORESYSTEM
334         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
335 #endif
336         [SuppressUnmanagedCodeSecurity]
337 #if FEATURE_CORESYSTEM
338         [System.Security.SecurityCritical]
339 #endif
340         [return: MarshalAs(UnmanagedType.Bool)]
341         private static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags);
342
343         /// <summary>
344         ///     Create a second SafeCspHandle which refers to the same HCRYPTPROV
345         /// </summary>
346         [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] 
347 #if FEATURE_CORESYSTEM
348         [System.Security.SecurityCritical]
349 #endif
350         public SafeCspHandle Duplicate() {
351             Contract.Requires(!IsInvalid && !IsClosed);
352
353             // In the window between the call to CryptContextAddRef and when the raw handle value is assigned
354             // into this safe handle, there's a second reference to the original safe handle that the CLR does
355             // not know about, so we need to bump the reference count around this entire operation to ensure
356             // that we don't have the original handle closed underneath us.
357             bool acquired = false;
358             RuntimeHelpers.PrepareConstrainedRegions();
359             try {
360                 DangerousAddRef(ref acquired);
361                 IntPtr originalHandle = DangerousGetHandle();
362
363                 int error = (int)CapiNative.ErrorCode.Success;
364
365                 SafeCspHandle duplicate = new SafeCspHandle();
366
367                 // A successful call to CryptContextAddRef and an assignment of the handle value to the duplicate
368                 // SafeHandle need to happen atomically, so we contain them within a CER. 
369                 RuntimeHelpers.PrepareConstrainedRegions();
370                 try { }
371                 finally {
372                     if (!CryptContextAddRef(this, IntPtr.Zero, 0)) {
373                         error = Marshal.GetLastWin32Error();
374                     }
375                     else {
376                         duplicate.SetHandle(originalHandle);
377                     }
378                 }
379
380                 // If we could not call CryptContextAddRef succesfully, then throw the error here otherwise
381                 // we should be in a valid state at this point.
382                 if (error != (int)CapiNative.ErrorCode.Success) {
383                     duplicate.Dispose();
384                     throw new CryptographicException(error);
385                 }
386                 else {
387                     Debug.Assert(!duplicate.IsInvalid, "Failed to duplicate handle successfully");
388                 }
389
390                 return duplicate;
391             }
392             finally {
393                 if (acquired) {
394                     DangerousRelease();
395                 }
396             }
397         }
398
399 #if FEATURE_CORESYSTEM
400         [System.Security.SecurityCritical]
401 #endif
402         protected override bool ReleaseHandle() {
403             return CryptReleaseContext(handle, 0);
404         }
405     }
406 }