3 // Copyright (c) Microsoft Corporation. All rights reserved.
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;
16 namespace Microsoft.Win32.SafeHandles {
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).
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
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.
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 {
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
57 /// The safe handle does not own the native handle, but points to a Holder which does
62 /// The safe handle owns the native handle, but shares it with other Duplicate handles
67 private OwnershipState m_ownershipState;
70 /// If the handle is a Duplicate, this points at the safe handle which actually owns the native handle.
72 private SafeNCryptHandle m_holder;
74 protected SafeNCryptHandle() : base(true) {
79 /// Wrapper for the m_holder field which ensures that we're in a consistent state
81 private SafeNCryptHandle Holder {
82 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
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);
91 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
93 Contract.Ensures(m_holder.m_ownershipState == OwnershipState.Holder);
94 Contract.Ensures(m_ownershipState == OwnershipState.Duplicate);
96 Contract.Ensures(IsValidOpenState);
97 Contract.Assert(value.IsValidOpenState);
99 Contract.Assert(m_ownershipState != OwnershipState.Duplicate);
100 Contract.Assert(value.m_ownershipState == OwnershipState.Holder);
104 m_ownershipState = OwnershipState.Duplicate;
110 /// Ensure the state of the handle is consistent for an open handle
112 private bool IsValidOpenState {
115 switch (m_ownershipState) {
116 // Owner handles do not have a holder
117 case OwnershipState.Owner:
118 return Holder == null && !IsInvalid && !IsClosed;
120 // Duplicate handles have valid open holders with the same raw handle value
121 case OwnershipState.Duplicate:
122 bool acquiredHolder = false;
124 RuntimeHelpers.PrepareConstrainedRegions();
126 IntPtr holderRawHandle = IntPtr.Zero;
128 if (Holder != null) {
129 Holder.DangerousAddRef(ref acquiredHolder);
130 holderRawHandle = Holder.DangerousGetHandle();
134 bool holderValid = Holder != null &&
137 holderRawHandle != IntPtr.Zero &&
138 holderRawHandle == handle;
140 return holderValid && !IsInvalid && !IsClosed;
143 if (acquiredHolder) {
144 Holder.DangerousRelease();
148 // Holder handles do not have a holder
149 case OwnershipState.Holder:
150 return Holder == null && !IsInvalid && !IsClosed;
152 // Unknown ownership state
161 /// Duplicate a handle
164 /// #NCryptHandleDuplicationAlgorithm
166 /// Duplicating a handle performs different operations depending upon the state of the handle:
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
174 /// * Duplicate - Allocate a duplicate handle
175 /// - Increment the reference count of our holder
176 /// - Assign the duplicate's holder to be our holder
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.
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);
187 // Spec#: Consider a debug-only? model variable for IsValidOpenState?
188 Contract.Ensures(Contract.Result<T>().IsValidOpenState);
189 Contract.Ensures(IsValidOpenState);
191 Contract.Assert(IsValidOpenState);
193 Contract.Assert(m_ownershipState != OwnershipState.Holder);
194 Contract.Assert(typeof(T) == this.GetType());
196 if (m_ownershipState == OwnershipState.Owner) {
197 return DuplicateOwnerHandle<T>();
200 // If we're not an owner handle, and we're being duplicated then we must be a duplicate handle.
201 return DuplicateDuplicatedHandle<T>();
206 /// Duplicate a safe handle which is already duplicated.
208 /// See code:Microsoft.Win32.SafeHandles.SafeNCryptHandle#NCryptHandleDuplicationAlgorithm for
209 /// details about the algorithm.
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);
216 Contract.Ensures(IsValidOpenState);
217 Contract.Ensures(Contract.Result<T>().IsValidOpenState);
219 Contract.Assert(IsValidOpenState);
221 Contract.Assert(m_ownershipState == OwnershipState.Duplicate);
222 Contract.Assert(typeof(T) == this.GetType());
224 bool addedRef = false;
225 T duplicate = new T();
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();
233 Holder.DangerousAddRef(ref addedRef);
234 duplicate.SetHandle(Holder.DangerousGetHandle());
235 duplicate.Holder = Holder; // Transitions to OwnershipState.Duplicate
242 /// Duplicate a safe handle which is currently the exclusive owner of a native handle
244 /// See code:Microsoft.Win32.SafeHandles.SafeNCryptHandle#NCryptHandleDuplicationAlgorithm for
245 /// details about the algorithm.
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);
252 Contract.Ensures(IsValidOpenState);
253 Contract.Ensures(Contract.Result<T>().IsValidOpenState);
255 Contract.Assert(IsValidOpenState);
257 Contract.Assert(m_ownershipState == OwnershipState.Owner);
258 Contract.Assert(typeof(T) == this.GetType());
263 T duplicate = new T();
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();
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);
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
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
290 /// Release the handle
293 /// Similar to duplication, releasing a handle performs different operations based upon the state
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
301 protected override bool ReleaseHandle() {
302 if (m_ownershipState == OwnershipState.Duplicate) {
303 Holder.DangerousRelease();
307 return ReleaseNativeHandle();
312 /// Perform the actual release P/Invoke
314 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
315 protected abstract bool ReleaseNativeHandle();
319 /// Safe handle representing an NCRYPT_KEY_HANDLE
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);
332 internal SafeNCryptKeyHandle Duplicate() {
333 return Duplicate<SafeNCryptKeyHandle>();
336 protected override bool ReleaseNativeHandle() {
337 return NCryptFreeObject(handle) == 0;
342 /// Safe handle representing an NCRYPT_PROV_HANDLE
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);
355 internal SafeNCryptProviderHandle Duplicate() {
356 return Duplicate<SafeNCryptProviderHandle>();
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);
366 SetHandle(newHandleValue);
369 protected override bool ReleaseNativeHandle() {
370 return NCryptFreeObject(handle) == 0;
375 /// Safe handle representing an NCRYPT_SECRET_HANDLE
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);
388 protected override bool ReleaseNativeHandle() {
389 return NCryptFreeObject(handle) == 0;