internal unsafe static int AcceptSecurityContext(
ref SafeFreeCredentials inCredentials,
ref SafeDeleteContext refContext,
Interop.SspiCli.ContextFlags inFlags,
Interop.SspiCli.Endianness endianness,
SecurityBuffer inSecBuffer,
SecurityBuffer[] inSecBuffers,
SecurityBuffer outSecBuffer,
ref Interop.SspiCli.ContextFlags outFlags)
{
#if TRACE_VERBOSE
if (NetEventSource.IsEnabled)
{
NetEventSource.Enter(null, $"credential={inCredentials}, refContext={refContext}, inFlags={inFlags}");
if (inSecBuffers == null)
{
NetEventSource.Info(null, "inSecBuffers = (null)");
}
else
{
NetEventSource.Info(null, $"inSecBuffers[] = (inSecBuffers)");
}
}
#endif
if (outSecBuffer == null)
{
NetEventSource.Fail(null, "outSecBuffer != null");
}
if (inSecBuffer != null && inSecBuffers != null)
{
NetEventSource.Fail(null, "inSecBuffer == null || inSecBuffers == null");
}
if (inCredentials == null)
{
throw new ArgumentNullException(nameof(inCredentials));
}
Interop.SspiCli.SecBufferDesc inSecurityBufferDescriptor = default(Interop.SspiCli.SecBufferDesc);
bool haveInSecurityBufferDescriptor = false;
if (inSecBuffer != null)
{
inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1);
haveInSecurityBufferDescriptor = true;
}
else if (inSecBuffers != null)
{
inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Length);
haveInSecurityBufferDescriptor = true;
}
Interop.SspiCli.SecBufferDesc outSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1);
// Actually, this is returned in outFlags.
bool isSspiAllocated = (inFlags & Interop.SspiCli.ContextFlags.AllocateMemory) != 0 ? true : false;
int errorCode = -1;
Interop.SspiCli.CredHandle contextHandle = new Interop.SspiCli.CredHandle();
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);
var inUnmanagedBuffer = new Interop.SspiCli.SecBuffer[haveInSecurityBufferDescriptor ? inSecurityBufferDescriptor.cBuffers : 1];
fixed (void* inUnmanagedBufferPtr = inUnmanagedBuffer)
{
if (haveInSecurityBufferDescriptor)
{
// Fix Descriptor pointer that points to unmanaged SecurityBuffers.
inSecurityBufferDescriptor.pBuffers = inUnmanagedBufferPtr;
pinnedInBytes = new GCHandle[inSecurityBufferDescriptor.cBuffers];
SecurityBuffer securityBuffer;
for (int index = 0; index < inSecurityBufferDescriptor.cBuffers; ++index)
{
securityBuffer = inSecBuffer != null ? inSecBuffer : inSecBuffers[index];
if (securityBuffer != null)
{
// Copy the SecurityBuffer content into unmanaged place holder.
inUnmanagedBuffer[index].cbBuffer = securityBuffer.size;
inUnmanagedBuffer[index].BufferType = securityBuffer.type;
// Use the unmanaged token if it's not null; otherwise use the managed buffer.
if (securityBuffer.unmanagedToken != null)
{
inUnmanagedBuffer[index].pvBuffer = securityBuffer.unmanagedToken.DangerousGetHandle();
}
else if (securityBuffer.token == null || securityBuffer.token.Length == 0)
{
inUnmanagedBuffer[index].pvBuffer = IntPtr.Zero;
}
else
{
pinnedInBytes[index] = GCHandle.Alloc(securityBuffer.token, GCHandleType.Pinned);
inUnmanagedBuffer[index].pvBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(securityBuffer.token, securityBuffer.offset);
}
#if TRACE_VERBOSE
if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"SecBuffer: cbBuffer:{securityBuffer.size} BufferType:{securityBuffer.type}");
#endif
}
}
}
var outUnmanagedBuffer = new Interop.SspiCli.SecBuffer[1];
fixed (void* outUnmanagedBufferPtr = outUnmanagedBuffer)
{
// Fix Descriptor pointer that points to unmanaged SecurityBuffers.
outSecurityBufferDescriptor.pBuffers = outUnmanagedBufferPtr;
// Copy the SecurityBuffer content into unmanaged place holder.
outUnmanagedBuffer[0].cbBuffer = outSecBuffer.size;
outUnmanagedBuffer[0].BufferType = outSecBuffer.type;
if (outSecBuffer.token == null || outSecBuffer.token.Length == 0)
{
outUnmanagedBuffer[0].pvBuffer = IntPtr.Zero;
}
else
{
outUnmanagedBuffer[0].pvBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(outSecBuffer.token, outSecBuffer.offset);
}
if (isSspiAllocated)
{
outFreeContextBuffer = SafeFreeContextBuffer.CreateEmptyHandle();
}
if (refContext == null || refContext.IsInvalid)
{
refContext = new SafeDeleteContext_SECURITY();
}
errorCode = MustRunAcceptSecurityContext_SECURITY(
ref inCredentials,
contextHandle.IsZero ? null : &contextHandle,
haveInSecurityBufferDescriptor ? &inSecurityBufferDescriptor : null,
inFlags,
endianness,
refContext,
ref outSecurityBufferDescriptor,
ref outFlags,
outFreeContextBuffer);
if (NetEventSource.IsEnabled) NetEventSource.Info(null, "Marshaling OUT buffer");
// Get unmanaged buffer with index 0 as the only one passed into PInvoke.
outSecBuffer.size = outUnmanagedBuffer[0].cbBuffer;
outSecBuffer.type = outUnmanagedBuffer[0].BufferType;
if (outSecBuffer.size > 0)
{
outSecBuffer.token = new byte[outSecBuffer.size];
Marshal.Copy(outUnmanagedBuffer[0].pvBuffer, 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.Dispose();
}
}
if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"errorCode:0x{errorCode:x8}, refContext:{refContext}");
return errorCode;
}