Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Core / Microsoft / Win32 / SafeHandles / NCryptSafeHandles.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.Permissions;
14 using System.Diagnostics.Contracts;
15
16 namespace Microsoft.Win32.SafeHandles {
17
18     /// <summary>
19     ///     Base class for NCrypt handles which need to support being pseudo-duplicated. This class is not for
20     ///     external use (instead applications should consume the concrete subclasses of this class).
21     /// </summary>
22     /// <remarks>
23     ///     Since NCrypt handles do not have a native DuplicateHandle type call, we need to do manual 
24     ///     reference counting in managed code whenever we hand out an extra reference to one of these handles.
25     ///     This class wraps up the logic to correctly duplicate and free these handles to simluate a native
26     ///     duplication.
27     /// 
28     ///     Each open handle object can be thought of as being in one of three states:
29     ///        1. Owner     - created via the marshaler, traditional style safe handle. Notably, only one owner
30     ///                       handle exists for a given native handle.
31     ///        2. Duplicate - points at a handle in the Holder state. Releasing a handle in the duplicate state
32     ///                       results only in decrementing the reference count of the holder, not in a release
33     ///                       of the native handle.
34     ///        3. Holder    - holds onto a native handle and is referenced by handles in the duplicate state.
35     ///                       When all duplicate handles are closed, the holder handle releases the native
36     ///                       handle. A holder handle will never be finalized, since this results in a ----
37     ///                       between the finalizers of the duplicate handles and the holder handle. Instead,
38     ///                       it relies upon all of the duplicate handles to be finalized and decriment the
39     ///                       ref count to zero.  Instances of a holder handle should never be referenced by
40     ///                       anything but a duplicate handle.
41     /// </remarks>
42 #pragma warning disable 618    // Have not migrated to v4 transparency yet
43     [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)]
44 #pragma warning restore 618
45     [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
46     [SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
47     [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
48     public abstract class SafeNCryptHandle : SafeHandleZeroOrMinusOneIsInvalid {
49         private enum OwnershipState {
50             /// <summary>
51             ///     The safe handle owns the native handle outright. This must be value 0, as this is the
52             ///     state the marshaler will place the handle in when marshaling back a SafeHandle
53             /// </summary>
54             Owner = 0,
55
56             /// <summary>
57             ///     The safe handle does not own the native handle, but points to a Holder which does
58             /// </summary>
59             Duplicate,
60
61             /// <summary>
62             ///     The safe handle owns the native handle, but shares it with other Duplicate handles
63             /// </summary>
64             Holder
65         }
66
67         private OwnershipState m_ownershipState;
68
69         /// <summary>
70         ///     If the handle is a Duplicate, this points at the safe handle which actually owns the native handle.
71         /// </summary>
72         private SafeNCryptHandle m_holder;
73
74         protected SafeNCryptHandle() : base(true) {
75             return;
76         }
77
78         /// <summary>
79         ///     Wrapper for the m_holder field which ensures that we're in a consistent state
80         /// </summary>
81         private SafeNCryptHandle Holder {
82             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
83             get {
84                 Contract.Requires((m_ownershipState == OwnershipState.Duplicate && m_holder != null) ||
85                                   (m_ownershipState != OwnershipState.Duplicate && m_holder == null));
86                 Contract.Requires(m_holder == null || m_holder.m_ownershipState == OwnershipState.Holder);
87
88                 return m_holder;
89             }
90
91             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
92             set {
93                 Contract.Ensures(m_holder.m_ownershipState == OwnershipState.Holder);
94                 Contract.Ensures(m_ownershipState == OwnershipState.Duplicate);
95 #if DEBUG
96                 Contract.Ensures(IsValidOpenState);
97                 Contract.Assert(value.IsValidOpenState);
98 #endif
99                 Contract.Assert(m_ownershipState != OwnershipState.Duplicate);
100                 Contract.Assert(value.m_ownershipState == OwnershipState.Holder);
101                 
102                
103                 m_holder = value;
104                 m_ownershipState = OwnershipState.Duplicate;
105             }
106         }
107
108 #if DEBUG
109         /// <summary>
110         ///     Ensure the state of the handle is consistent for an open handle
111         /// </summary>
112         private bool IsValidOpenState {
113             [Pure]
114             get {
115                 switch (m_ownershipState) {
116                     // Owner handles do not have a holder
117                     case OwnershipState.Owner:
118                         return Holder == null && !IsInvalid && !IsClosed;
119
120                     // Duplicate handles have valid open holders with the same raw handle value
121                     case OwnershipState.Duplicate:
122                         bool acquiredHolder = false;
123
124                         RuntimeHelpers.PrepareConstrainedRegions();
125                         try {
126                             IntPtr holderRawHandle = IntPtr.Zero;
127
128                             if (Holder != null) {
129                                 Holder.DangerousAddRef(ref acquiredHolder);
130                                 holderRawHandle = Holder.DangerousGetHandle();
131                             }
132
133
134                             bool holderValid = Holder != null &&
135                                                !Holder.IsInvalid &&
136                                                !Holder.IsClosed &&
137                                                holderRawHandle != IntPtr.Zero &&
138                                                holderRawHandle == handle;
139
140                             return holderValid && !IsInvalid && !IsClosed;
141                         }
142                         finally {
143                             if (acquiredHolder) {
144                                 Holder.DangerousRelease();
145                             }
146                         }
147
148                     // Holder handles do not have a holder
149                     case OwnershipState.Holder:
150                         return Holder == null && !IsInvalid && !IsClosed;
151
152                     // Unknown ownership state
153                     default:
154                         return false;
155                 }
156             }
157         }
158 #endif
159
160         /// <summary>
161         ///     Duplicate a handle
162         /// </summary>
163         /// <remarks>
164         ///     #NCryptHandleDuplicationAlgorithm
165         /// 
166         ///     Duplicating a handle performs different operations depending upon the state of the handle:
167         /// 
168         ///     * Owner     - Allocate two new handles, a holder and a duplicate.
169         ///                 - Suppress finalization on the holder
170         ///                 - Transition into the duplicate state
171         ///                 - Use the new holder as the holder for both this handle and the duplicate
172         ///                 - Increment the reference count on the holder
173         /// 
174         ///     * Duplicate - Allocate a duplicate handle
175         ///                 - Increment the reference count of our holder
176         ///                 - Assign the duplicate's holder to be our holder
177         /// 
178         ///     * Holder    - Specifically disallowed. Holders should only ever be referenced by duplicates,
179         ///                   so duplication will occur on the duplicate rather than the holder handle.
180         /// </remarks>
181         internal T Duplicate<T>() where T : SafeNCryptHandle, new() {
182             // Spec#: Consider adding a model variable for ownership state?
183             Contract.Ensures(Contract.Result<T>() != null);
184             Contract.Ensures(m_ownershipState == OwnershipState.Duplicate);
185             Contract.Ensures(Contract.Result<T>().m_ownershipState == OwnershipState.Duplicate);
186 #if DEBUG
187             // Spec#: Consider a debug-only? model variable for IsValidOpenState?
188             Contract.Ensures(Contract.Result<T>().IsValidOpenState);
189             Contract.Ensures(IsValidOpenState);
190
191             Contract.Assert(IsValidOpenState);
192 #endif
193             Contract.Assert(m_ownershipState != OwnershipState.Holder);
194             Contract.Assert(typeof(T) == this.GetType());
195
196             if (m_ownershipState == OwnershipState.Owner) {
197                 return DuplicateOwnerHandle<T>();
198             }
199             else {
200                 // If we're not an owner handle, and we're being duplicated then we must be a duplicate handle.
201                 return DuplicateDuplicatedHandle<T>();
202             }
203         }
204
205         /// <summary>
206         ///     Duplicate a safe handle which is already duplicated.
207         /// 
208         ///     See code:Microsoft.Win32.SafeHandles.SafeNCryptHandle#NCryptHandleDuplicationAlgorithm for
209         ///     details about the algorithm.
210         /// </summary>
211         private T DuplicateDuplicatedHandle<T>() where T : SafeNCryptHandle, new() {
212             Contract.Ensures(m_ownershipState == OwnershipState.Duplicate);
213             Contract.Ensures(Contract.Result<T>() != null &&
214                              Contract.Result<T>().m_ownershipState == OwnershipState.Duplicate);
215 #if DEBUG
216             Contract.Ensures(IsValidOpenState);
217             Contract.Ensures(Contract.Result<T>().IsValidOpenState);
218
219             Contract.Assert(IsValidOpenState);
220 #endif
221             Contract.Assert(m_ownershipState == OwnershipState.Duplicate);
222             Contract.Assert(typeof(T) == this.GetType());
223
224             bool addedRef = false;
225             T duplicate = new T();
226
227             // We need to do this operation in a CER, since we need to make sure that if the AddRef occurs
228             // that the duplicated handle will always point back to the Holder, otherwise the Holder will leak
229             // since it will never have its ref count reduced to zero.
230             RuntimeHelpers.PrepareConstrainedRegions();
231             try { }
232             finally {
233                 Holder.DangerousAddRef(ref addedRef);
234                 duplicate.SetHandle(Holder.DangerousGetHandle());
235                 duplicate.Holder = Holder;              // Transitions to OwnershipState.Duplicate
236             }
237
238             return duplicate;
239         }
240
241         /// <summary>
242         ///     Duplicate a safe handle which is currently the exclusive owner of a native handle
243         /// 
244         ///     See code:Microsoft.Win32.SafeHandles.SafeNCryptHandle#NCryptHandleDuplicationAlgorithm for
245         ///     details about the algorithm.
246         /// </summary>
247         private T DuplicateOwnerHandle<T>() where T : SafeNCryptHandle, new() {
248             Contract.Ensures(m_ownershipState == OwnershipState.Duplicate);
249             Contract.Ensures(Contract.Result<T>() != null &&
250                              Contract.Result<T>().m_ownershipState == OwnershipState.Duplicate);
251 #if DEBUG
252             Contract.Ensures(IsValidOpenState);
253             Contract.Ensures(Contract.Result<T>().IsValidOpenState);
254
255             Contract.Assert(IsValidOpenState);
256 #endif
257             Contract.Assert(m_ownershipState == OwnershipState.Owner);
258             Contract.Assert(typeof(T) == this.GetType());
259
260             bool addRef = false;
261
262             T holder = new T();
263             T duplicate = new T();
264
265             // We need to do this operation in a CER in order to ensure that everybody's state stays consistent
266             // with the current view of the world.  If the state of the various handles gets out of sync, then
267             // we'll end up leaking since reference counts will not be set up properly.
268             RuntimeHelpers.PrepareConstrainedRegions();
269             try { }
270             finally {
271                 // Setup a holder safe handle to ref count the native handle
272                 holder.m_ownershipState = OwnershipState.Holder;
273                 holder.SetHandle(DangerousGetHandle());
274                 GC.SuppressFinalize(holder);
275
276                 // Transition into the duplicate state, referencing the holder. The initial reference count
277                 // on the holder will refer to the original handle so there is no need to AddRef here.
278                 Holder = holder;        // Transitions to OwnershipState.Duplicate
279
280                 // The duplicate handle will also reference the holder
281                 holder.DangerousAddRef(ref addRef);
282                 duplicate.SetHandle(holder.DangerousGetHandle());
283                 duplicate.Holder = holder;  // Transitions to OwnershipState.Duplicate
284             }
285
286             return duplicate;
287         }
288
289         /// <summary>
290         ///     Release the handle
291         /// </summary>
292         /// <remarks>
293         ///     Similar to duplication, releasing a handle performs different operations based upon the state
294         ///     of the handle.
295         /// 
296         ///     * Owner     - Simply call the release P/Invoke method
297         ///     * Duplicate - Decrement the reference count of the current holder
298         ///     * Holder    - Call the release P/Invoke. Note that ReleaseHandle on a holder implies a reference
299         ///                   count of zero.
300         /// </remarks>
301         protected override bool ReleaseHandle() {
302             if (m_ownershipState == OwnershipState.Duplicate) {
303                 Holder.DangerousRelease();
304                 return true;
305             }
306             else {
307                 return ReleaseNativeHandle();
308             }
309         }
310
311         /// <summary>
312         ///     Perform the actual release P/Invoke
313         /// </summary>
314         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
315         protected abstract bool ReleaseNativeHandle();
316     }
317
318     /// <summary>
319     ///     Safe handle representing an NCRYPT_KEY_HANDLE
320     /// </summary>
321 #pragma warning disable 618    // Have not migrated to v4 transparency yet
322     [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)]
323 #pragma warning restore 618
324     [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
325     [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
326     public sealed class SafeNCryptKeyHandle : SafeNCryptHandle {
327         [DllImport("ncrypt.dll")]
328         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
329         [SuppressUnmanagedCodeSecurity]
330         private static extern int NCryptFreeObject(IntPtr hObject);
331
332         internal SafeNCryptKeyHandle Duplicate() {
333             return Duplicate<SafeNCryptKeyHandle>();
334         }
335
336         protected override bool ReleaseNativeHandle() {
337             return NCryptFreeObject(handle) == 0;
338         }
339     }
340
341     /// <summary>
342     ///     Safe handle representing an NCRYPT_PROV_HANDLE
343     /// </summary>
344 #pragma warning disable 618    // Have not migrated to v4 transparency yet
345     [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)]
346 #pragma warning restore 618
347     [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
348     [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
349     public sealed class SafeNCryptProviderHandle : SafeNCryptHandle {
350         [DllImport("ncrypt.dll")]
351         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
352         [SuppressUnmanagedCodeSecurity]
353         private static extern int NCryptFreeObject(IntPtr hObject);
354
355         internal SafeNCryptProviderHandle Duplicate() {
356             return Duplicate<SafeNCryptProviderHandle>();
357         }
358
359         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
360         internal void SetHandleValue(IntPtr newHandleValue) {
361             Contract.Requires(newHandleValue != IntPtr.Zero);
362             Contract.Requires(!IsClosed);
363             Contract.Ensures(!IsInvalid);
364             Contract.Assert(handle == IntPtr.Zero);
365
366             SetHandle(newHandleValue);
367         }
368
369         protected override bool ReleaseNativeHandle() {
370             return NCryptFreeObject(handle) == 0;
371         }
372     }
373
374     /// <summary>
375     ///     Safe handle representing an NCRYPT_SECRET_HANDLE
376     /// </summary>
377 #pragma warning disable 618    // Have not migrated to v4 transparency yet
378     [System.Security.SecurityCritical(System.Security.SecurityCriticalScope.Everything)]
379 #pragma warning restore 618
380     [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
381     [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
382     public sealed class SafeNCryptSecretHandle : SafeNCryptHandle {
383         [DllImport("ncrypt.dll")]
384         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
385         [SuppressUnmanagedCodeSecurity]
386         private static extern int NCryptFreeObject(IntPtr hObject);
387
388         protected override bool ReleaseNativeHandle() {
389             return NCryptFreeObject(handle) == 0;
390         }
391     }
392 }