e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1500 lines
60 KiB
C#
1500 lines
60 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/*++
|
|
Abstract:
|
|
The file contains SafeHandles implementations for SSPI.
|
|
These handle wrappers do guarantee that OS resources get cleaned up when the app domain dies.
|
|
|
|
All PInvoke declarations that do freeing the OS resources _must_ be in this file
|
|
All PInvoke declarations that do allocation the OS resources _must_ be in this file
|
|
|
|
|
|
Details:
|
|
|
|
The protection from leaking OF the OS resources is based on two technologies
|
|
1) SafeHandle class
|
|
2) Non interuptible regions using Constrained Execution Region (CER) technology
|
|
|
|
For simple cases SafeHandle class does all the job. The Prerequisites are:
|
|
- A resource is able to be represented by IntPtr type (32 bits on 32 bits platforms).
|
|
- There is a PInvoke availble that does the creation of the resource.
|
|
That PInvoke either returns the handle value or it writes the handle into out/ref parameter.
|
|
- The above PInvoke as part of the call does NOT free any OS resource.
|
|
|
|
For those "simple" cases we desinged SafeHandle-derived classes that provide
|
|
static methods to allocate a handle object.
|
|
Each such derived class provides a handle release method that is run as non-interrupted.
|
|
|
|
For more complicated cases we employ the support for non-interruptible methods (CERs).
|
|
Each CER is a tree of code rooted at a catch or finally clause for a specially marked exception
|
|
handler (preceded by the RuntimeHelpers.PrepareConstrainedRegions() marker) or the Dispose or
|
|
ReleaseHandle method of a SafeHandle derived class. The graph is automatically computed by the
|
|
runtime (typically at the jit time of the root method), but cannot follow virtual or interface
|
|
calls (these must be explicitly prepared via RuntimeHelpers.PrepareMethod once the definite target
|
|
method is known). Also, methods in the graph that must be included in the CER must be marked with
|
|
a reliability contract stating guarantees about the consistency of the system if an error occurs
|
|
while they are executing. Look for ReliabilityContract for examples (a full explanation of the
|
|
semantics of this contract is beyond the scope of this comment).
|
|
|
|
An example of the top-level of a CER:
|
|
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try
|
|
{
|
|
// Normal code
|
|
}
|
|
finally
|
|
{
|
|
// Guaranteed to get here even in low memory scenarios. Thread abort will not interrupt
|
|
// this clause and we won't fail because of a jit allocation of any method called (modulo
|
|
// restrictions on interface/virtual calls listed above and further restrictions listed
|
|
// below).
|
|
}
|
|
|
|
Another common pattern is an empty-try (where you really just want a region of code the runtime
|
|
won't interrupt you in):
|
|
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try {} finally
|
|
{
|
|
// Non-interruptible code here
|
|
}
|
|
|
|
This ugly syntax will be supplanted with compiler support at some point.
|
|
|
|
While within a CER region certain restrictions apply in order to avoid having the runtime inject
|
|
a potential fault point into your code (and of course you're are responsible for ensuring your
|
|
code doesn't inject any explicit fault points of its own unless you know how to tolerate them).
|
|
|
|
A quick and dirty guide to the possible causes of fault points in CER regions:
|
|
- Explicit allocations (though allocating a value type only implies allocation on the stack,
|
|
which may not present an issue).
|
|
- Boxing a value type (C# does this implicitly for you in many cases, so be careful).
|
|
- Use of Monitor.Enter or the lock keyword.
|
|
- Accessing a multi-dimensional array.
|
|
- Calling any method outside your control that doesn't make a guarantee (e.g. via a
|
|
ReliabilityAttribute) that it doesn't introduce failure points.
|
|
- Making P/Invoke calls with non-blittable parameters types. Blittable types are:
|
|
- SafeHandle when used as an [in] parameter
|
|
- NON BOXED base types that fit onto a machine word
|
|
- ref struct with blittable fields
|
|
- class type with blittable fields
|
|
- pinned Unicode strings using "fixed" statement
|
|
- pointers of any kind
|
|
- IntPtr type
|
|
- P/Invokes should not have any CharSet attribute on it's declaration.
|
|
Obvioulsy string types should not appear in the parameters.
|
|
- String type MUST not appear in a field of a marshaled ref struct or class in a P?Invoke
|
|
|
|
|
|
(taken from the NCL classes)
|
|
--*/
|
|
namespace System.IdentityModel
|
|
{
|
|
using System.Security;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.Versioning;
|
|
using System.Threading;
|
|
using System.Security.Permissions;
|
|
using System.ComponentModel;
|
|
using System.Text;
|
|
using System.ServiceModel.Diagnostics;
|
|
using Microsoft.Win32.SafeHandles;
|
|
using System.Runtime.ConstrainedExecution;
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
struct SSPIHandle
|
|
{
|
|
IntPtr HandleHi;
|
|
IntPtr HandleLo;
|
|
|
|
public bool IsZero
|
|
{
|
|
get { return HandleHi == IntPtr.Zero && HandleLo == IntPtr.Zero; }
|
|
}
|
|
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
internal void SetToInvalid()
|
|
{
|
|
HandleHi = IntPtr.Zero;
|
|
HandleLo = IntPtr.Zero;
|
|
}
|
|
|
|
//public static string ToString(ref SSPIHandle obj)
|
|
//{
|
|
// return obj.HandleHi.ToString("x") + ":" + obj.HandleLo.ToString("x");
|
|
//}
|
|
}
|
|
|
|
class SafeDeleteContext : SafeHandle
|
|
{
|
|
const string SECURITY = "security.Dll";
|
|
|
|
const string dummyStr = " ";
|
|
static readonly byte[] dummyBytes = new byte[] { 0 };
|
|
|
|
internal SSPIHandle _handle; //should be always used as by ref in PINvokes parameters
|
|
SafeFreeCredentials _EffectiveCredential;
|
|
|
|
protected SafeDeleteContext()
|
|
: base(IntPtr.Zero, true)
|
|
{
|
|
_handle = new SSPIHandle();
|
|
}
|
|
|
|
public override bool IsInvalid
|
|
{
|
|
get
|
|
{
|
|
return IsClosed || _handle.IsZero;
|
|
}
|
|
}
|
|
|
|
//This method should never be called for this type
|
|
//public new IntPtr DangerousGetHandle()
|
|
//{
|
|
// throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
|
|
//}
|
|
|
|
//public static string ToString(SafeDeleteContext obj)
|
|
//{
|
|
// { return obj == null ? "null" : SSPIHandle.ToString(ref obj._handle); }
|
|
//}
|
|
|
|
//-------------------------------------------------------------------
|
|
internal static unsafe int InitializeSecurityContext(
|
|
SafeFreeCredentials inCredentials,
|
|
ref SafeDeleteContext refContext,
|
|
string targetName,
|
|
SspiContextFlags inFlags,
|
|
Endianness endianness,
|
|
SecurityBuffer inSecBuffer,
|
|
SecurityBuffer[] inSecBuffers,
|
|
SecurityBuffer outSecBuffer,
|
|
ref SspiContextFlags outFlags)
|
|
{
|
|
if (inCredentials == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("inCredentials");
|
|
}
|
|
|
|
SecurityBufferDescriptor inSecurityBufferDescriptor = null;
|
|
if (inSecBuffer != null)
|
|
{
|
|
inSecurityBufferDescriptor = new SecurityBufferDescriptor(1);
|
|
}
|
|
else if (inSecBuffers != null)
|
|
{
|
|
inSecurityBufferDescriptor = new SecurityBufferDescriptor(inSecBuffers.Length);
|
|
}
|
|
SecurityBufferDescriptor outSecurityBufferDescriptor = new SecurityBufferDescriptor(1);
|
|
|
|
// actually this is returned in outFlags
|
|
bool isSspiAllocated = (inFlags & SspiContextFlags.AllocateMemory) != 0 ? true : false;
|
|
|
|
int errorCode = -1;
|
|
SSPIHandle contextHandle = new SSPIHandle();
|
|
if (refContext != null)
|
|
{
|
|
contextHandle = refContext._handle;
|
|
}
|
|
|
|
// these are pinned user byte arrays passed along with SecurityBuffers
|
|
GCHandle[] pinnedInBytes = null;
|
|
GCHandle pinnedOutBytes = new GCHandle();
|
|
|
|
// optional output buffer that may need to be freed
|
|
SafeFreeContextBuffer outFreeContextBuffer = null;
|
|
|
|
try
|
|
{
|
|
pinnedOutBytes = GCHandle.Alloc(outSecBuffer.token, GCHandleType.Pinned);
|
|
|
|
SecurityBufferStruct[] inUnmanagedBuffer = new SecurityBufferStruct[inSecurityBufferDescriptor == null ? 1 : inSecurityBufferDescriptor.Count];
|
|
fixed (void* inUnmanagedBufferPtr = inUnmanagedBuffer)
|
|
{
|
|
if (inSecurityBufferDescriptor != null)
|
|
{
|
|
// Fix Descriptor pointer that points to unmanaged SecurityBuffers
|
|
inSecurityBufferDescriptor.UnmanagedPointer = inUnmanagedBufferPtr;
|
|
pinnedInBytes = new GCHandle[inSecurityBufferDescriptor.Count];
|
|
SecurityBuffer securityBuffer;
|
|
for (int index = 0; index < inSecurityBufferDescriptor.Count; ++index)
|
|
{
|
|
securityBuffer = inSecBuffer != null ? inSecBuffer : inSecBuffers[index];
|
|
if (securityBuffer != null)
|
|
{
|
|
// Copy the SecurityBuffer content into unmanaged place holder
|
|
inUnmanagedBuffer[index].count = securityBuffer.size;
|
|
inUnmanagedBuffer[index].type = securityBuffer.type;
|
|
// use the unmanaged token if it's not null; otherwise use the managed buffer
|
|
if (securityBuffer.unmanagedToken != null)
|
|
{
|
|
inUnmanagedBuffer[index].token = securityBuffer.unmanagedToken.DangerousGetHandle();
|
|
}
|
|
else if (securityBuffer.token == null || securityBuffer.token.Length == 0)
|
|
{
|
|
inUnmanagedBuffer[index].token = IntPtr.Zero;
|
|
}
|
|
else
|
|
{
|
|
pinnedInBytes[index] = GCHandle.Alloc(securityBuffer.token, GCHandleType.Pinned);
|
|
inUnmanagedBuffer[index].token = Marshal.UnsafeAddrOfPinnedArrayElement(securityBuffer.token, securityBuffer.offset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SecurityBufferStruct[] outUnmanagedBuffer = new SecurityBufferStruct[1];
|
|
fixed (void* outUnmanagedBufferPtr = outUnmanagedBuffer)
|
|
{
|
|
// Fix Descriptor pointer that points to unmanaged SecurityBuffers
|
|
outSecurityBufferDescriptor.UnmanagedPointer = outUnmanagedBufferPtr;
|
|
outUnmanagedBuffer[0].count = outSecBuffer.size;
|
|
outUnmanagedBuffer[0].type = outSecBuffer.type;
|
|
if (outSecBuffer.token == null || outSecBuffer.token.Length == 0)
|
|
{
|
|
outUnmanagedBuffer[0].token = IntPtr.Zero;
|
|
}
|
|
else
|
|
{
|
|
outUnmanagedBuffer[0].token = Marshal.UnsafeAddrOfPinnedArrayElement(outSecBuffer.token, outSecBuffer.offset);
|
|
}
|
|
if (isSspiAllocated)
|
|
{
|
|
outFreeContextBuffer = SafeFreeContextBuffer.CreateEmptyHandle();
|
|
}
|
|
if (refContext == null || refContext.IsInvalid)
|
|
{
|
|
refContext = new SafeDeleteContext();
|
|
}
|
|
if (targetName == null || targetName.Length == 0)
|
|
{
|
|
targetName = dummyStr;
|
|
}
|
|
fixed (char* namePtr = targetName)
|
|
{
|
|
errorCode = MustRunInitializeSecurityContext(
|
|
inCredentials,
|
|
contextHandle.IsZero ? null : &contextHandle,
|
|
(byte*)(((object)targetName == (object)dummyStr) ? null : namePtr),
|
|
inFlags,
|
|
endianness,
|
|
inSecurityBufferDescriptor,
|
|
refContext,
|
|
outSecurityBufferDescriptor,
|
|
ref outFlags,
|
|
outFreeContextBuffer
|
|
);
|
|
}
|
|
|
|
// Get unmanaged buffer with index 0 as the only one passed into PInvoke
|
|
outSecBuffer.size = outUnmanagedBuffer[0].count;
|
|
outSecBuffer.type = outUnmanagedBuffer[0].type;
|
|
if (outSecBuffer.size > 0)
|
|
{
|
|
outSecBuffer.token = DiagnosticUtility.Utility.AllocateByteArray(outSecBuffer.size);
|
|
Marshal.Copy(outUnmanagedBuffer[0].token, outSecBuffer.token, 0, outSecBuffer.size);
|
|
}
|
|
else
|
|
{
|
|
outSecBuffer.token = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (pinnedInBytes != null)
|
|
{
|
|
for (int index = 0; index < pinnedInBytes.Length; index++)
|
|
{
|
|
if (pinnedInBytes[index].IsAllocated)
|
|
{
|
|
pinnedInBytes[index].Free();
|
|
}
|
|
}
|
|
}
|
|
if (pinnedOutBytes.IsAllocated)
|
|
{
|
|
pinnedOutBytes.Free();
|
|
}
|
|
if (outFreeContextBuffer != null)
|
|
{
|
|
outFreeContextBuffer.Close();
|
|
}
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
//
|
|
// After PINvoke call the method will fix the handleTemplate.handle with the returned value.
|
|
// The caller is responsible for creating a correct SafeFreeContextBuffer or null can be passed if no handle is returned.
|
|
//
|
|
// Since it has a CER, this method can't have any references to imports from DLLs that may not exist on the system.
|
|
//
|
|
static unsafe int MustRunInitializeSecurityContext(
|
|
SafeFreeCredentials inCredentials,
|
|
void* inContextPtr,
|
|
byte* targetName,
|
|
SspiContextFlags inFlags,
|
|
Endianness endianness,
|
|
SecurityBufferDescriptor inputBuffer,
|
|
SafeDeleteContext outContext,
|
|
SecurityBufferDescriptor outputBuffer,
|
|
ref SspiContextFlags attributes,
|
|
SafeFreeContextBuffer handleTemplate)
|
|
{
|
|
int errorCode = -1;
|
|
bool b1 = false;
|
|
bool b2 = false;
|
|
|
|
// Run the body of this method as a non-interruptible block.
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try
|
|
{
|
|
inCredentials.DangerousAddRef(ref b1);
|
|
outContext.DangerousAddRef(ref b2);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (System.Runtime.Fx.IsFatal(e))
|
|
throw;
|
|
|
|
if (b1)
|
|
{
|
|
inCredentials.DangerousRelease();
|
|
b1 = false;
|
|
}
|
|
|
|
if (b2)
|
|
{
|
|
outContext.DangerousRelease();
|
|
b2 = false;
|
|
}
|
|
|
|
if (!(e is ObjectDisposedException))
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
long timeStamp;
|
|
if (!b1)
|
|
{
|
|
// caller should retry
|
|
inCredentials = null;
|
|
}
|
|
else if (b1 && b2)
|
|
{
|
|
|
|
SSPIHandle credentialHandle = inCredentials._handle;
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // This API does not set Win32 Last Error.
|
|
errorCode = InitializeSecurityContextW(
|
|
ref credentialHandle,
|
|
inContextPtr,
|
|
targetName,
|
|
inFlags,
|
|
0,
|
|
endianness,
|
|
inputBuffer,
|
|
0,
|
|
ref outContext._handle,
|
|
outputBuffer,
|
|
ref attributes,
|
|
out timeStamp
|
|
);
|
|
//
|
|
// When a credential handle is first associated with the context we keep credential
|
|
// ref count bumped up to ensure ordered finalization.
|
|
// If the credential handle has been changed we de-ref the old one and associate the
|
|
// context with the new cred handle but only if the call was successful.
|
|
if (outContext._EffectiveCredential != inCredentials && (errorCode & 0x80000000) == 0)
|
|
{
|
|
// Disassociate the previous credential handle
|
|
if (outContext._EffectiveCredential != null)
|
|
outContext._EffectiveCredential.DangerousRelease();
|
|
outContext._EffectiveCredential = inCredentials;
|
|
}
|
|
else
|
|
{
|
|
inCredentials.DangerousRelease();
|
|
}
|
|
|
|
outContext.DangerousRelease();
|
|
|
|
// The idea is that SSPI has allocated a block and filled up outUnmanagedBuffer+8 slot with the pointer.
|
|
if (handleTemplate != null)
|
|
{
|
|
handleTemplate.Set(((SecurityBufferStruct*)outputBuffer.UnmanagedPointer)->token); //ATTN: on 64 BIT that is still +8 cause of 2* c++ unsigned long == 8 bytes
|
|
if (handleTemplate.IsInvalid)
|
|
{
|
|
handleTemplate.SetHandleAsInvalid();
|
|
}
|
|
}
|
|
|
|
}
|
|
if (inContextPtr == null && (errorCode & 0x80000000) != 0)
|
|
{
|
|
// an error on the first call, need to set the out handle to invalid value
|
|
outContext._handle.SetToInvalid();
|
|
}
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
internal static unsafe int AcceptSecurityContext(
|
|
SafeFreeCredentials inCredentials,
|
|
ref SafeDeleteContext refContext,
|
|
SspiContextFlags inFlags,
|
|
Endianness endianness,
|
|
SecurityBuffer inSecBuffer,
|
|
SecurityBuffer[] inSecBuffers,
|
|
SecurityBuffer outSecBuffer,
|
|
ref SspiContextFlags outFlags)
|
|
{
|
|
|
|
if (inCredentials == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("inCredentials");
|
|
}
|
|
|
|
SecurityBufferDescriptor inSecurityBufferDescriptor = null;
|
|
if (inSecBuffer != null)
|
|
{
|
|
inSecurityBufferDescriptor = new SecurityBufferDescriptor(1);
|
|
}
|
|
else if (inSecBuffers != null)
|
|
{
|
|
inSecurityBufferDescriptor = new SecurityBufferDescriptor(inSecBuffers.Length);
|
|
}
|
|
SecurityBufferDescriptor outSecurityBufferDescriptor = new SecurityBufferDescriptor(1);
|
|
|
|
// actually this is returned in outFlags
|
|
bool isSspiAllocated = (inFlags & SspiContextFlags.AllocateMemory) != 0 ? true : false;
|
|
|
|
int errorCode = -1;
|
|
SSPIHandle contextHandle = new SSPIHandle();
|
|
if (refContext != null)
|
|
{
|
|
contextHandle = refContext._handle;
|
|
}
|
|
|
|
// these are pinned user byte arrays passed along with SecurityBuffers
|
|
GCHandle[] pinnedInBytes = null;
|
|
GCHandle pinnedOutBytes = new GCHandle();
|
|
// optional output buffer that may need to be freed
|
|
SafeFreeContextBuffer outFreeContextBuffer = null;
|
|
|
|
try
|
|
{
|
|
pinnedOutBytes = GCHandle.Alloc(outSecBuffer.token, GCHandleType.Pinned);
|
|
|
|
SecurityBufferStruct[] inUnmanagedBuffer = new SecurityBufferStruct[inSecurityBufferDescriptor == null ? 1 : inSecurityBufferDescriptor.Count];
|
|
fixed (void* inUnmanagedBufferPtr = inUnmanagedBuffer)
|
|
{
|
|
if (inSecurityBufferDescriptor != null)
|
|
{
|
|
// Fix Descriptor pointer that points to unmanaged SecurityBuffers
|
|
inSecurityBufferDescriptor.UnmanagedPointer = inUnmanagedBufferPtr;
|
|
pinnedInBytes = new GCHandle[inSecurityBufferDescriptor.Count];
|
|
SecurityBuffer securityBuffer;
|
|
for (int index = 0; index < inSecurityBufferDescriptor.Count; ++index)
|
|
{
|
|
securityBuffer = inSecBuffer != null ? inSecBuffer : inSecBuffers[index];
|
|
if (securityBuffer != null)
|
|
{
|
|
// Copy the SecurityBuffer content into unmanaged place holder
|
|
inUnmanagedBuffer[index].count = securityBuffer.size;
|
|
inUnmanagedBuffer[index].type = securityBuffer.type;
|
|
// use the unmanaged token if it's not null; otherwise use the managed buffer
|
|
if (securityBuffer.unmanagedToken != null)
|
|
{
|
|
inUnmanagedBuffer[index].token = securityBuffer.unmanagedToken.DangerousGetHandle();
|
|
}
|
|
else if (securityBuffer.token == null || securityBuffer.token.Length == 0)
|
|
{
|
|
inUnmanagedBuffer[index].token = IntPtr.Zero;
|
|
}
|
|
else
|
|
{
|
|
pinnedInBytes[index] = GCHandle.Alloc(securityBuffer.token, GCHandleType.Pinned);
|
|
inUnmanagedBuffer[index].token = Marshal.UnsafeAddrOfPinnedArrayElement(securityBuffer.token, securityBuffer.offset);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SecurityBufferStruct[] outUnmanagedBuffer = new SecurityBufferStruct[1];
|
|
fixed (void* outUnmanagedBufferPtr = outUnmanagedBuffer)
|
|
{
|
|
// Fix Descriptor pointer that points to unmanaged SecurityBuffers
|
|
outSecurityBufferDescriptor.UnmanagedPointer = outUnmanagedBufferPtr;
|
|
// Copy the SecurityBuffer content into unmanaged place holder
|
|
outUnmanagedBuffer[0].count = outSecBuffer.size;
|
|
outUnmanagedBuffer[0].type = outSecBuffer.type;
|
|
if (outSecBuffer.token == null || outSecBuffer.token.Length == 0)
|
|
{
|
|
outUnmanagedBuffer[0].token = IntPtr.Zero;
|
|
}
|
|
else
|
|
{
|
|
outUnmanagedBuffer[0].token = Marshal.UnsafeAddrOfPinnedArrayElement(outSecBuffer.token, outSecBuffer.offset);
|
|
}
|
|
if (isSspiAllocated)
|
|
{
|
|
outFreeContextBuffer = SafeFreeContextBuffer.CreateEmptyHandle();
|
|
}
|
|
|
|
if (refContext == null || refContext.IsInvalid)
|
|
{
|
|
refContext = new SafeDeleteContext();
|
|
}
|
|
errorCode = MustRunAcceptSecurityContext(
|
|
inCredentials,
|
|
contextHandle.IsZero ? null : &contextHandle,
|
|
inSecurityBufferDescriptor,
|
|
inFlags,
|
|
endianness,
|
|
refContext,
|
|
outSecurityBufferDescriptor,
|
|
ref outFlags,
|
|
outFreeContextBuffer
|
|
);
|
|
|
|
// Get unmanaged buffer with index 0 as the only one passed into PInvoke
|
|
outSecBuffer.size = outUnmanagedBuffer[0].count;
|
|
outSecBuffer.type = outUnmanagedBuffer[0].type;
|
|
if (outSecBuffer.size > 0)
|
|
{
|
|
outSecBuffer.token = DiagnosticUtility.Utility.AllocateByteArray(outSecBuffer.size);
|
|
Marshal.Copy(outUnmanagedBuffer[0].token, outSecBuffer.token, 0, outSecBuffer.size);
|
|
}
|
|
else
|
|
{
|
|
outSecBuffer.token = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (pinnedInBytes != null)
|
|
{
|
|
for (int index = 0; index < pinnedInBytes.Length; index++)
|
|
{
|
|
if (pinnedInBytes[index].IsAllocated)
|
|
{
|
|
pinnedInBytes[index].Free();
|
|
}
|
|
}
|
|
}
|
|
if (pinnedOutBytes.IsAllocated)
|
|
{
|
|
pinnedOutBytes.Free();
|
|
}
|
|
if (outFreeContextBuffer != null)
|
|
{
|
|
outFreeContextBuffer.Close();
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
// After PINvoke call the method will fix the handleTemplate.handle with the returned value.
|
|
// The caller is responsible for creating a correct SafeFreeContextBuffer_XXX flavour or null can be passed if no handle is returned.
|
|
// This method is run as non-interruptible.
|
|
static unsafe int MustRunAcceptSecurityContext(
|
|
SafeFreeCredentials inCredentials,
|
|
void* inContextPtr,
|
|
SecurityBufferDescriptor inputBuffer,
|
|
SspiContextFlags inFlags,
|
|
Endianness endianness,
|
|
SafeDeleteContext outContext,
|
|
SecurityBufferDescriptor outputBuffer,
|
|
ref SspiContextFlags outFlags,
|
|
SafeFreeContextBuffer handleTemplate)
|
|
{
|
|
int errorCode = -1;
|
|
bool b1 = false;
|
|
bool b2 = false;
|
|
|
|
// Run the body of this method as a non-interruptible block.
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
|
|
try
|
|
{
|
|
inCredentials.DangerousAddRef(ref b1);
|
|
outContext.DangerousAddRef(ref b2);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (System.Runtime.Fx.IsFatal(e))
|
|
throw;
|
|
|
|
if (b1)
|
|
{
|
|
inCredentials.DangerousRelease();
|
|
b1 = false;
|
|
}
|
|
if (b2)
|
|
{
|
|
outContext.DangerousRelease();
|
|
b2 = false;
|
|
}
|
|
if (!(e is ObjectDisposedException))
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
long timeStamp;
|
|
if (!b1)
|
|
{
|
|
// caller should retry
|
|
inCredentials = null;
|
|
}
|
|
else if (b1 && b2)
|
|
{
|
|
|
|
SSPIHandle credentialHandle = inCredentials._handle;
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // This API does not set Win32 Last Error.
|
|
errorCode = AcceptSecurityContext(
|
|
ref credentialHandle,
|
|
inContextPtr,
|
|
inputBuffer,
|
|
inFlags,
|
|
endianness,
|
|
ref outContext._handle,
|
|
outputBuffer,
|
|
ref outFlags,
|
|
out timeStamp
|
|
);
|
|
|
|
//
|
|
// When a credential handle is first associated with the context we keep credential
|
|
// ref count bumped up to ensure ordered finalization.
|
|
// If the credential handle has been changed we de-ref the old one and associate the
|
|
// context with the new cred handle but only if the call was successful.
|
|
if (outContext._EffectiveCredential != inCredentials && (errorCode & 0x80000000) == 0)
|
|
{
|
|
// Disassociate the previous credential handle
|
|
if (outContext._EffectiveCredential != null)
|
|
outContext._EffectiveCredential.DangerousRelease();
|
|
outContext._EffectiveCredential = inCredentials;
|
|
}
|
|
else
|
|
{
|
|
inCredentials.DangerousRelease();
|
|
}
|
|
|
|
outContext.DangerousRelease();
|
|
|
|
// The idea is that SSPI has allocated a block and filled up outUnmanagedBuffer+8 slot with the pointer.
|
|
if (handleTemplate != null)
|
|
{
|
|
handleTemplate.Set(((SecurityBufferStruct*)outputBuffer.UnmanagedPointer)->token); //ATTN: on 64 BIT that is still +8 cause of 2* c++ unsigned long == 8 bytes
|
|
if (handleTemplate.IsInvalid)
|
|
{
|
|
handleTemplate.SetHandleAsInvalid();
|
|
}
|
|
}
|
|
if (inContextPtr == null && (errorCode & 0x80000000) != 0)
|
|
{
|
|
// an error on the first call, need to set the out handle to invalid value
|
|
outContext._handle.SetToInvalid();
|
|
}
|
|
}
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
public static int ImpersonateSecurityContext(SafeDeleteContext context)
|
|
{
|
|
int status = (int)SecurityStatus.InvalidHandle;
|
|
bool b = false;
|
|
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try
|
|
{
|
|
context.DangerousAddRef(ref b);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (System.Runtime.Fx.IsFatal(e))
|
|
throw;
|
|
|
|
if (b)
|
|
{
|
|
context.DangerousRelease();
|
|
b = false;
|
|
}
|
|
if (!(e is ObjectDisposedException))
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
|
|
if (b)
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. It returns a error code.
|
|
status = ImpersonateSecurityContext(ref context._handle);
|
|
context.DangerousRelease();
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
public static int EncryptMessage(SafeDeleteContext context, SecurityBufferDescriptor inputOutput, uint sequenceNumber)
|
|
{
|
|
int status = (int)SecurityStatus.InvalidHandle;
|
|
bool b = false;
|
|
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try
|
|
{
|
|
context.DangerousAddRef(ref b);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (System.Runtime.Fx.IsFatal(e))
|
|
throw;
|
|
|
|
if (b)
|
|
{
|
|
context.DangerousRelease();
|
|
b = false;
|
|
}
|
|
if (!(e is ObjectDisposedException))
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
if (b)
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. It returns a error code.
|
|
status = EncryptMessage(ref context._handle, 0, inputOutput, sequenceNumber);
|
|
context.DangerousRelease();
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
public unsafe static int DecryptMessage(SafeDeleteContext context, SecurityBufferDescriptor inputOutput, uint sequenceNumber)
|
|
{
|
|
int status = (int)SecurityStatus.InvalidHandle;
|
|
bool b = false;
|
|
uint qop = 0;
|
|
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try
|
|
{
|
|
context.DangerousAddRef(ref b);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (System.Runtime.Fx.IsFatal(e))
|
|
throw;
|
|
|
|
if (b)
|
|
{
|
|
context.DangerousRelease();
|
|
b = false;
|
|
}
|
|
if (!(e is ObjectDisposedException))
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
|
|
if (b)
|
|
{
|
|
#pragma warning suppress 56523 // we don't take any action on the win32 error message.
|
|
status = DecryptMessage(ref context._handle, inputOutput, sequenceNumber, &qop);
|
|
context.DangerousRelease();
|
|
}
|
|
}
|
|
|
|
const uint SECQOP_WRAP_NO_ENCRYPT = 0x80000001;
|
|
if (status == 0 && qop == SECQOP_WRAP_NO_ENCRYPT)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SspiPayloadNotEncrypted)));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
internal int GetSecurityContextToken(out SafeCloseHandle safeHandle)
|
|
{
|
|
int status = (int)SecurityStatus.InvalidHandle;
|
|
bool b = false;
|
|
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try
|
|
{
|
|
DangerousAddRef(ref b);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (System.Runtime.Fx.IsFatal(e))
|
|
throw;
|
|
|
|
if (b)
|
|
{
|
|
DangerousRelease();
|
|
b = false;
|
|
}
|
|
if (!(e is ObjectDisposedException))
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
if (b)
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. The API returns a error code.
|
|
status = QuerySecurityContextToken(ref _handle, out safeHandle);
|
|
DangerousRelease();
|
|
}
|
|
else
|
|
{
|
|
safeHandle = new SafeCloseHandle(IntPtr.Zero, false);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
protected override bool ReleaseHandle()
|
|
{
|
|
if (this._EffectiveCredential != null)
|
|
this._EffectiveCredential.DangerousRelease();
|
|
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. It returns a error code.
|
|
return DeleteSecurityContext(ref _handle) == 0;
|
|
}
|
|
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
extern static int QuerySecurityContextToken(ref SSPIHandle phContext, [Out] out SafeCloseHandle handle);
|
|
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
internal extern static unsafe int InitializeSecurityContextW(
|
|
ref SSPIHandle credentialHandle,
|
|
[In] void* inContextPtr,
|
|
[In] byte* targetName,
|
|
[In] SspiContextFlags inFlags,
|
|
[In] int reservedI,
|
|
[In] Endianness endianness,
|
|
[In] SecurityBufferDescriptor inputBuffer,
|
|
[In] int reservedII,
|
|
ref SSPIHandle outContextPtr,
|
|
[In, Out] SecurityBufferDescriptor outputBuffer,
|
|
[In, Out] ref SspiContextFlags attributes,
|
|
out long timestamp
|
|
);
|
|
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
internal extern static unsafe int AcceptSecurityContext(
|
|
ref SSPIHandle credentialHandle,
|
|
[In] void* inContextPtr,
|
|
[In] SecurityBufferDescriptor inputBuffer,
|
|
[In] SspiContextFlags inFlags,
|
|
[In] Endianness endianness,
|
|
ref SSPIHandle outContextPtr,
|
|
[In, Out] SecurityBufferDescriptor outputBuffer,
|
|
[In, Out] ref SspiContextFlags attributes,
|
|
out long timestamp
|
|
);
|
|
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[SuppressUnmanagedCodeSecurity]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
internal extern static int DeleteSecurityContext(
|
|
ref SSPIHandle handlePtr
|
|
);
|
|
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
internal extern static int ImpersonateSecurityContext(
|
|
ref SSPIHandle handlePtr
|
|
);
|
|
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
internal extern static int EncryptMessage(
|
|
ref SSPIHandle contextHandle,
|
|
[In] uint qualityOfProtection,
|
|
[In, Out] SecurityBufferDescriptor inputOutput,
|
|
[In] uint sequenceNumber
|
|
);
|
|
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
internal unsafe extern static int DecryptMessage(
|
|
ref SSPIHandle contextHandle,
|
|
[In, Out] SecurityBufferDescriptor inputOutput,
|
|
[In] uint sequenceNumber,
|
|
uint* qualityOfProtection
|
|
);
|
|
}
|
|
|
|
class SafeFreeCredentials : SafeHandle
|
|
{
|
|
const string SECURITY = "security.Dll";
|
|
internal SSPIHandle _handle; //should be always used as by ref in PINvokes parameters
|
|
|
|
protected SafeFreeCredentials()
|
|
: base(IntPtr.Zero, true)
|
|
{
|
|
_handle = new SSPIHandle();
|
|
}
|
|
|
|
//internal static string ToString(SafeFreeCredentials obj)
|
|
//{ return obj == null ? "null" : SSPIHandle.ToString(ref obj._handle); }
|
|
|
|
public override bool IsInvalid
|
|
{
|
|
get { return IsClosed || _handle.IsZero; }
|
|
}
|
|
|
|
//This method should never be called for this type
|
|
//public new IntPtr DangerousGetHandle()
|
|
//{
|
|
// throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException());
|
|
//}
|
|
|
|
public static unsafe int AcquireCredentialsHandle(
|
|
string package,
|
|
CredentialUse intent,
|
|
ref AuthIdentityEx authdata,
|
|
out SafeFreeCredentials outCredential)
|
|
{
|
|
int errorCode = -1;
|
|
long timeStamp;
|
|
|
|
outCredential = new SafeFreeCredentials();
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try { }
|
|
finally
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. It returns a error code.
|
|
errorCode = AcquireCredentialsHandleW(
|
|
null,
|
|
package,
|
|
(int)intent,
|
|
null,
|
|
ref authdata,
|
|
null,
|
|
null,
|
|
ref outCredential._handle,
|
|
out timeStamp
|
|
);
|
|
if (errorCode != 0)
|
|
{
|
|
outCredential.SetHandleAsInvalid();
|
|
}
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
public static unsafe int AcquireDefaultCredential(
|
|
string package,
|
|
CredentialUse intent,
|
|
ref AuthIdentityEx authIdentity,
|
|
out SafeFreeCredentials outCredential)
|
|
{
|
|
int errorCode = -1;
|
|
long timeStamp;
|
|
|
|
outCredential = new SafeFreeCredentials();
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try { }
|
|
finally
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. It returns a error code.
|
|
errorCode = AcquireCredentialsHandleW(
|
|
null,
|
|
package,
|
|
(int)intent,
|
|
null,
|
|
ref authIdentity, // IntPtr.Zero,
|
|
null,
|
|
null,
|
|
ref outCredential._handle,
|
|
out timeStamp
|
|
);
|
|
|
|
if (errorCode != 0)
|
|
{
|
|
outCredential.SetHandleAsInvalid();
|
|
}
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
public static unsafe int AcquireCredentialsHandle(
|
|
string package,
|
|
CredentialUse intent,
|
|
ref SecureCredential authdata,
|
|
out SafeFreeCredentials outCredential)
|
|
{
|
|
int errorCode = -1;
|
|
long timeStamp;
|
|
|
|
// If there is a certificate, wrap it into an array
|
|
IntPtr copiedPtr = authdata.certContextArray;
|
|
try
|
|
{
|
|
IntPtr certArrayPtr = new IntPtr(&copiedPtr);
|
|
if (copiedPtr != IntPtr.Zero)
|
|
{
|
|
authdata.certContextArray = certArrayPtr;
|
|
}
|
|
|
|
outCredential = new SafeFreeCredentials();
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try { }
|
|
finally
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. It returns a error code.
|
|
errorCode = AcquireCredentialsHandleW(
|
|
null,
|
|
package,
|
|
(int)intent,
|
|
null,
|
|
ref authdata,
|
|
null,
|
|
null,
|
|
ref outCredential._handle,
|
|
out timeStamp
|
|
);
|
|
if (errorCode != 0)
|
|
{
|
|
outCredential.SetHandleAsInvalid();
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
authdata.certContextArray = copiedPtr;
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
public static unsafe int AcquireCredentialsHandle(
|
|
string package,
|
|
CredentialUse intent,
|
|
ref IntPtr ppAuthIdentity,
|
|
out SafeFreeCredentials outCredential
|
|
)
|
|
{
|
|
int errorCode = -1;
|
|
long timeStamp;
|
|
outCredential = new SafeFreeCredentials();
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try { }
|
|
finally
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. It returns a error code.
|
|
errorCode = AcquireCredentialsHandleW(
|
|
null,
|
|
package,
|
|
(int)intent,
|
|
null,
|
|
ppAuthIdentity,
|
|
null,
|
|
null,
|
|
ref outCredential._handle,
|
|
out timeStamp
|
|
);
|
|
if (errorCode != 0)
|
|
{
|
|
outCredential.SetHandleAsInvalid();
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
protected override bool ReleaseHandle()
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. It returns a error code.
|
|
return FreeCredentialsHandle(ref _handle) == 0;
|
|
}
|
|
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[DllImport(SECURITY, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal extern static unsafe int AcquireCredentialsHandleW(
|
|
[In] string principal,
|
|
[In] string moduleName,
|
|
[In] int usage,
|
|
[In] void* logonID,
|
|
[In] ref AuthIdentityEx authdata,
|
|
[In] void* keyCallback,
|
|
[In] void* keyArgument,
|
|
ref SSPIHandle handlePtr,
|
|
[Out] out long timeStamp
|
|
);
|
|
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[DllImport(SECURITY, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal extern static unsafe int AcquireCredentialsHandleW(
|
|
[In] string principal,
|
|
[In] string moduleName,
|
|
[In] int usage,
|
|
[In] void* logonID,
|
|
[In] IntPtr zero,
|
|
[In] void* keyCallback,
|
|
[In] void* keyArgument,
|
|
ref SSPIHandle handlePtr,
|
|
[Out] out long timeStamp
|
|
);
|
|
|
|
[ResourceExposure(ResourceScope.None)]
|
|
[DllImport(SECURITY, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal extern static unsafe int AcquireCredentialsHandleW(
|
|
[In] string principal,
|
|
[In] string moduleName,
|
|
[In] int usage,
|
|
[In] void* logonID,
|
|
[In] ref SecureCredential authData,
|
|
[In] void* keyCallback,
|
|
[In] void* keyArgument,
|
|
ref SSPIHandle handlePtr,
|
|
[Out] out long timeStamp
|
|
);
|
|
|
|
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[SuppressUnmanagedCodeSecurity]
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
internal extern static int FreeCredentialsHandle(
|
|
ref SSPIHandle handlePtr
|
|
);
|
|
}
|
|
|
|
sealed class SafeFreeCertContext : SafeHandleZeroOrMinusOneIsInvalid
|
|
{
|
|
const string CRYPT32 = "crypt32.dll";
|
|
const string ADVAPI32 = "advapi32.dll";
|
|
|
|
internal SafeFreeCertContext() : base(true) { }
|
|
|
|
// This must be ONLY called from this file and form a MustRun method
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
internal unsafe void Set(IntPtr value)
|
|
{
|
|
this.handle = value;
|
|
}
|
|
|
|
const uint CRYPT_ACQUIRE_SILENT_FLAG = 0x00000040;
|
|
|
|
[DllImport(CRYPT32, ExactSpelling = true, SetLastError = true)]
|
|
[SuppressUnmanagedCodeSecurity]
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
extern static bool CertFreeCertificateContext(// Suppressing returned status check, it's always==TRUE,
|
|
[In] IntPtr certContext);
|
|
|
|
protected override bool ReleaseHandle()
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error.
|
|
return CertFreeCertificateContext(handle);
|
|
}
|
|
}
|
|
|
|
sealed class SafeFreeContextBuffer : SafeHandleZeroOrMinusOneIsInvalid
|
|
{
|
|
const string SECURITY = "security.dll";
|
|
|
|
SafeFreeContextBuffer() : base(true) { }
|
|
|
|
// This must be ONLY called from this file and form a MustRun method
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
internal unsafe void Set(IntPtr value)
|
|
{
|
|
this.handle = value;
|
|
}
|
|
|
|
internal static int EnumeratePackages(out int pkgnum, out SafeFreeContextBuffer pkgArray)
|
|
{
|
|
int res = -1;
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. The API returns a error code.
|
|
res = SafeFreeContextBuffer.EnumerateSecurityPackagesW(out pkgnum, out pkgArray);
|
|
|
|
if (res != 0)
|
|
{
|
|
Utility.CloseInvalidOutSafeHandle(pkgArray);
|
|
pkgArray = null;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
internal static SafeFreeContextBuffer CreateEmptyHandle()
|
|
{
|
|
return new SafeFreeContextBuffer();
|
|
}
|
|
|
|
//
|
|
// After PInvoke call the method will fix the refHandle.handle with the returned value.
|
|
// The caller is responsible for creating a correct SafeHandle template or null can be passed if no handle is returned.
|
|
//
|
|
// This method is run as non-interruptible.
|
|
//
|
|
public static unsafe int QueryContextAttributes(SafeDeleteContext phContext, ContextAttribute contextAttribute, byte* buffer, SafeHandle refHandle)
|
|
{
|
|
int status = (int)SecurityStatus.InvalidHandle;
|
|
bool b = false;
|
|
|
|
// We don't want to be interrupted by thread abort exceptions or unexpected out-of-memory errors failing to jit
|
|
// one of the following methods. So run within a CER non-interruptible block.
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
|
|
try
|
|
{
|
|
phContext.DangerousAddRef(ref b);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (System.Runtime.Fx.IsFatal(e))
|
|
throw;
|
|
|
|
if (b)
|
|
{
|
|
phContext.DangerousRelease();
|
|
b = false;
|
|
}
|
|
if (!(e is ObjectDisposedException))
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
if (b)
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. The API returns a error code.
|
|
status = SafeFreeContextBuffer.QueryContextAttributesW(ref phContext._handle, contextAttribute, buffer);
|
|
phContext.DangerousRelease();
|
|
}
|
|
if (status == 0 && refHandle != null)
|
|
{
|
|
if (refHandle is SafeFreeContextBuffer)
|
|
{
|
|
if (contextAttribute == ContextAttribute.SessionKey)
|
|
{
|
|
IntPtr keyPtr = Marshal.ReadIntPtr(new IntPtr(buffer), SecPkgContext_SessionKey.SessionkeyOffset);
|
|
((SafeFreeContextBuffer)refHandle).Set(keyPtr);
|
|
}
|
|
else
|
|
{
|
|
((SafeFreeContextBuffer)refHandle).Set(*(IntPtr*)buffer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
((SafeFreeCertContext)refHandle).Set(*(IntPtr*)buffer);
|
|
}
|
|
}
|
|
|
|
if (status != 0 && refHandle != null)
|
|
{
|
|
refHandle.SetHandleAsInvalid();
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
protected override bool ReleaseHandle()
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // The API does not set Win32 Last Error. The API returns a error code.
|
|
return FreeContextBuffer(handle) == 0;
|
|
}
|
|
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
internal extern static unsafe int QueryContextAttributesW(
|
|
ref SSPIHandle contextHandle,
|
|
[In] ContextAttribute attribute,
|
|
[In] void* buffer);
|
|
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
internal extern static int EnumerateSecurityPackagesW(
|
|
[Out] out int pkgnum,
|
|
[Out] out SafeFreeContextBuffer handle);
|
|
|
|
[DllImport(SECURITY, ExactSpelling = true, SetLastError = true)]
|
|
[SuppressUnmanagedCodeSecurity]
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
extern static int FreeContextBuffer(
|
|
[In] IntPtr contextBuffer);
|
|
}
|
|
|
|
sealed class SafeCloseHandle : SafeHandleZeroOrMinusOneIsInvalid
|
|
{
|
|
const string KERNEL32 = "kernel32.dll";
|
|
|
|
SafeCloseHandle() : base(true) { }
|
|
internal SafeCloseHandle(IntPtr handle, bool ownsHandle)
|
|
: base(ownsHandle)
|
|
{
|
|
DiagnosticUtility.DebugAssert(handle == IntPtr.Zero || !ownsHandle, "Unsafe to create a SafeHandle that owns a pre-existing handle before the SafeHandle was created.");
|
|
SetHandle(handle);
|
|
}
|
|
|
|
protected override bool ReleaseHandle()
|
|
{
|
|
// PreSharp Bug: Call 'Marshal.GetLastWin32Error' or 'Marshal.GetHRForLastWin32Error' before any other interop call.
|
|
#pragma warning suppress 56523 // We are not interested to throw an exception here. We can ignore the Last Error code.
|
|
return CloseHandle(handle);
|
|
}
|
|
|
|
[DllImport(KERNEL32, ExactSpelling = true, SetLastError = true)]
|
|
[SuppressUnmanagedCodeSecurity]
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
extern static bool CloseHandle(IntPtr handle);
|
|
}
|
|
|
|
#pragma warning disable 618 // have not moved to the v4 security model yet
|
|
[SecurityCritical(SecurityCriticalScope.Everything)]
|
|
#pragma warning restore 618
|
|
sealed class SafeHGlobalHandle : SafeHandleZeroOrMinusOneIsInvalid
|
|
{
|
|
SafeHGlobalHandle() : base(true) { }
|
|
|
|
// 0 is an Invalid Handle
|
|
SafeHGlobalHandle(IntPtr handle)
|
|
: base(true)
|
|
{
|
|
DiagnosticUtility.DebugAssert(handle == IntPtr.Zero, "SafeHGlobalHandle constructor can only be called with IntPtr.Zero.");
|
|
SetHandle(handle);
|
|
}
|
|
|
|
protected override bool ReleaseHandle()
|
|
{
|
|
Marshal.FreeHGlobal(handle);
|
|
return true;
|
|
}
|
|
|
|
public static SafeHGlobalHandle InvalidHandle
|
|
{
|
|
get { return new SafeHGlobalHandle(IntPtr.Zero); }
|
|
}
|
|
|
|
public static SafeHGlobalHandle AllocHGlobal(string s)
|
|
{
|
|
byte[] bytes = DiagnosticUtility.Utility.AllocateByteArray(checked((s.Length + 1) * 2));
|
|
Encoding.Unicode.GetBytes(s, 0, s.Length, bytes, 0);
|
|
return AllocHGlobal(bytes);
|
|
}
|
|
|
|
public static SafeHGlobalHandle AllocHGlobal(byte[] bytes)
|
|
{
|
|
SafeHGlobalHandle result = AllocHGlobal(bytes.Length);
|
|
Marshal.Copy(bytes, 0, result.DangerousGetHandle(), bytes.Length);
|
|
return result;
|
|
}
|
|
|
|
public static SafeHGlobalHandle AllocHGlobal(uint cb)
|
|
{
|
|
// The cast could overflow to minus.
|
|
// Unfortunately, Marshal.AllocHGlobal only takes int.
|
|
return AllocHGlobal((int)cb);
|
|
}
|
|
|
|
public static SafeHGlobalHandle AllocHGlobal(int cb)
|
|
{
|
|
if (cb < 0)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("cb", SR.GetString(SR.ValueMustBeNonNegative)));
|
|
}
|
|
|
|
SafeHGlobalHandle result = new SafeHGlobalHandle();
|
|
|
|
// CER
|
|
RuntimeHelpers.PrepareConstrainedRegions();
|
|
try { }
|
|
finally
|
|
{
|
|
IntPtr ptr = Marshal.AllocHGlobal(cb);
|
|
result.SetHandle(ptr);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
sealed class SafeLsaLogonProcessHandle : SafeHandleZeroOrMinusOneIsInvalid
|
|
{
|
|
SafeLsaLogonProcessHandle() : base(true) { }
|
|
|
|
// 0 is an Invalid Handle
|
|
internal SafeLsaLogonProcessHandle(IntPtr handle)
|
|
: base(true)
|
|
{
|
|
SetHandle(handle);
|
|
}
|
|
|
|
internal static SafeLsaLogonProcessHandle InvalidHandle
|
|
{
|
|
get { return new SafeLsaLogonProcessHandle(IntPtr.Zero); }
|
|
}
|
|
|
|
override protected bool ReleaseHandle()
|
|
{
|
|
// LsaDeregisterLogonProcess returns an NTSTATUS
|
|
return NativeMethods.LsaDeregisterLogonProcess(handle) >= 0;
|
|
}
|
|
}
|
|
|
|
sealed class SafeLsaReturnBufferHandle : SafeHandleZeroOrMinusOneIsInvalid
|
|
{
|
|
SafeLsaReturnBufferHandle() : base(true) { }
|
|
|
|
// 0 is an Invalid Handle
|
|
internal SafeLsaReturnBufferHandle(IntPtr handle)
|
|
: base(true)
|
|
{
|
|
SetHandle(handle);
|
|
}
|
|
|
|
internal static SafeLsaReturnBufferHandle InvalidHandle
|
|
{
|
|
get { return new SafeLsaReturnBufferHandle(IntPtr.Zero); }
|
|
}
|
|
|
|
override protected bool ReleaseHandle()
|
|
{
|
|
// LsaFreeReturnBuffer returns an NTSTATUS
|
|
return NativeMethods.LsaFreeReturnBuffer(handle) >= 0;
|
|
}
|
|
}
|
|
}
|