public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials?credentialsHandle, ref SafeDeleteSslContext?context, ReadOnlySpan <byte> inputBuffer, ref byte[]?outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { Interop.SspiCli.ContextFlags unusedAttributes = default; InputSecurityBuffers inputBuffers = default; inputBuffers.SetNextBuffer(new InputSecurityBuffer(inputBuffer, SecurityBufferType.SECBUFFER_TOKEN)); inputBuffers.SetNextBuffer(new InputSecurityBuffer(default, SecurityBufferType.SECBUFFER_EMPTY));
internal static SecurityStatusPal AcceptSecurityContext( SafeFreeCredentials?credentialsHandle, ref SafeDeleteContext?securityContext, ContextFlagsPal requestedContextFlags, ReadOnlySpan <byte> incomingBlob, ChannelBinding?channelBinding, ref byte[]?resultBlob, out int resultBlobLength, ref ContextFlagsPal contextFlags) { InputSecurityBuffers inputBuffers = default; if (!incomingBlob.IsEmpty) { inputBuffers.SetNextBuffer(new InputSecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN)); } if (channelBinding != null) { inputBuffers.SetNextBuffer(new InputSecurityBuffer(channelBinding)); } var outSecurityBuffer = new SecurityBuffer(resultBlob, SecurityBufferType.SECBUFFER_TOKEN); Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero; // There is only one SafeDeleteContext type on Windows which is SafeDeleteSslContext so this cast is safe. SafeDeleteSslContext?sslContext = (SafeDeleteSslContext?)securityContext; Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.AcceptSecurityContext( GlobalSSPI.SSPIAuth, credentialsHandle, ref sslContext, ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(requestedContextFlags), Interop.SspiCli.Endianness.SECURITY_NETWORK_DREP, inputBuffers, ref outSecurityBuffer, ref outContextFlags); // SSPI Workaround // If a client sends up a blob on the initial request, Negotiate returns SEC_E_INVALID_HANDLE // when it should return SEC_E_INVALID_TOKEN. if (winStatus == Interop.SECURITY_STATUS.InvalidHandle && securityContext == null && !incomingBlob.IsEmpty) { winStatus = Interop.SECURITY_STATUS.InvalidToken; } Debug.Assert(outSecurityBuffer.offset == 0); resultBlob = outSecurityBuffer.token; resultBlobLength = outSecurityBuffer.size; securityContext = sslContext; contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(outContextFlags); return(SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus)); }
internal static SecurityStatusPal InitializeSecurityContext( ref SafeFreeCredentials?credentialsHandle, ref SafeDeleteContext?securityContext, string?spn, ContextFlagsPal requestedContextFlags, ReadOnlySpan <byte> incomingBlob, ChannelBinding?channelBinding, ref byte[]?resultBlob, out int resultBlobLength, ref ContextFlagsPal contextFlags) { InputSecurityBuffers inputBuffers = default; if (!incomingBlob.IsEmpty) { inputBuffers.SetNextBuffer(new InputSecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN)); } if (channelBinding != null) { inputBuffers.SetNextBuffer(new InputSecurityBuffer(channelBinding)); } var outSecurityBuffer = new SecurityBuffer(resultBlob, SecurityBufferType.SECBUFFER_TOKEN); Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero; // There is only one SafeDeleteContext type on Windows which is SafeDeleteSslContext so this cast is safe. SafeDeleteSslContext?sslContext = (SafeDeleteSslContext?)securityContext; Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.InitializeSecurityContext( GlobalSSPI.SSPIAuth, ref credentialsHandle, ref sslContext, spn, ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(requestedContextFlags), Interop.SspiCli.Endianness.SECURITY_NETWORK_DREP, inputBuffers, ref outSecurityBuffer, ref outContextFlags); securityContext = sslContext; Debug.Assert(outSecurityBuffer.offset == 0); resultBlob = outSecurityBuffer.token; resultBlobLength = outSecurityBuffer.size; contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(outContextFlags); return(SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus)); }
internal static SecurityStatusPal AcceptSecurityContext( SafeFreeCredentials?credentialsHandle, ref SafeDeleteContext?securityContext, ContextFlagsPal requestedContextFlags, byte[]?incomingBlob, ChannelBinding?channelBinding, ref byte[]?resultBlob, ref ContextFlagsPal contextFlags) { InputSecurityBuffers inputBuffers = default; if (incomingBlob != null) { inputBuffers.SetNextBuffer(new InputSecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN)); } if (channelBinding != null) { inputBuffers.SetNextBuffer(new InputSecurityBuffer(channelBinding)); } var outSecurityBuffer = new SecurityBuffer(resultBlob, SecurityBufferType.SECBUFFER_TOKEN); Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero; // There is only one SafeDeleteContext type on Windows which is SafeDeleteSslContext so this cast is safe. SafeDeleteSslContext?sslContext = (SafeDeleteSslContext?)securityContext; Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.AcceptSecurityContext( GlobalSSPI.SSPIAuth, credentialsHandle, ref sslContext, ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(requestedContextFlags), Interop.SspiCli.Endianness.SECURITY_NETWORK_DREP, inputBuffers, ref outSecurityBuffer, ref outContextFlags); resultBlob = outSecurityBuffer.token; securityContext = sslContext; contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(outContextFlags); return(SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus)); }
internal static unsafe int AcceptSecurityContext( ref SafeFreeCredentials?inCredentials, ref SafeDeleteSslContext?refContext, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, InputSecurityBuffers inSecBuffers, ref SecurityBuffer outSecBuffer, ref Interop.SspiCli.ContextFlags outFlags) { #if TRACE_VERBOSE if (NetEventSource.IsEnabled) { NetEventSource.Enter(null, $"credential={inCredentials}, refContext={refContext}, inFlags={inFlags}"); NetEventSource.Info(null, $"inSecBuffers.Length = {inSecBuffers.Length}"); } #endif if (inCredentials == null) { throw new ArgumentNullException(nameof(inCredentials)); } Debug.Assert(inSecBuffers.Count <= 3); Interop.SspiCli.SecBufferDesc inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Count); Interop.SspiCli.SecBufferDesc outSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(count: 2); // Actually, this is returned in outFlags. bool isSspiAllocated = (inFlags & Interop.SspiCli.ContextFlags.AllocateMemory) != 0 ? true : false; int errorCode = -1; bool isContextAbsent = true; if (refContext != null) { isContextAbsent = refContext._handle.IsZero; } // Optional output buffer that may need to be freed. SafeFreeContextBuffer? outFreeContextBuffer = null; Span <Interop.SspiCli.SecBuffer> outUnmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2]; outUnmanagedBuffer[1].pvBuffer = IntPtr.Zero; try { // Allocate always maximum to allow better code optimization. Span <Interop.SspiCli.SecBuffer> inUnmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[3]; fixed(void *inUnmanagedBufferPtr = inUnmanagedBuffer) fixed(void *outUnmanagedBufferPtr = outUnmanagedBuffer) fixed(void *pinnedToken0 = inSecBuffers._item0.Token) fixed(void *pinnedToken1 = inSecBuffers._item1.Token) fixed(void *pinnedToken2 = inSecBuffers._item2.Token) { inSecurityBufferDescriptor.pBuffers = inUnmanagedBufferPtr; // Updated pvBuffer with pinned address. UnmanagedToken takes precedence. if (inSecBuffers.Count > 2) { inUnmanagedBuffer[2].BufferType = inSecBuffers._item2.Type; inUnmanagedBuffer[2].cbBuffer = inSecBuffers._item2.Token.Length; inUnmanagedBuffer[2].pvBuffer = inSecBuffers._item2.UnmanagedToken != null ? (IntPtr)inSecBuffers._item2.UnmanagedToken.DangerousGetHandle() : (IntPtr)pinnedToken2; } if (inSecBuffers.Count > 1) { inUnmanagedBuffer[1].BufferType = inSecBuffers._item1.Type; inUnmanagedBuffer[1].cbBuffer = inSecBuffers._item1.Token.Length; inUnmanagedBuffer[1].pvBuffer = inSecBuffers._item1.UnmanagedToken != null ? (IntPtr)inSecBuffers._item1.UnmanagedToken.DangerousGetHandle() : (IntPtr)pinnedToken1; } if (inSecBuffers.Count > 0) { inUnmanagedBuffer[0].BufferType = inSecBuffers._item0.Type; inUnmanagedBuffer[0].cbBuffer = inSecBuffers._item0.Token.Length; inUnmanagedBuffer[0].pvBuffer = inSecBuffers._item0.UnmanagedToken != null ? (IntPtr)inSecBuffers._item0.UnmanagedToken.DangerousGetHandle() : (IntPtr)pinnedToken0; } fixed(byte *pinnedOutBytes = outSecBuffer.token) { // 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; outUnmanagedBuffer[0].pvBuffer = outSecBuffer.token == null || outSecBuffer.token.Length == 0 ? IntPtr.Zero : (IntPtr)(pinnedOutBytes + outSecBuffer.offset); outUnmanagedBuffer[1].cbBuffer = 0; outUnmanagedBuffer[1].BufferType = SecurityBufferType.SECBUFFER_ALERT; if (isSspiAllocated) { outFreeContextBuffer = SafeFreeContextBuffer.CreateEmptyHandle(); } if (refContext == null || refContext.IsInvalid) { // Previous versions unconditionally built a new "refContext" here, but would pass // incorrect arguments to AcceptSecurityContext in cases where an "contextHandle" was // already present and non-zero. if (isContextAbsent) { refContext = new SafeDeleteSslContext(); } } errorCode = MustRunAcceptSecurityContext_SECURITY( ref inCredentials, isContextAbsent, &inSecurityBufferDescriptor, inFlags, endianness, refContext !, ref outSecurityBufferDescriptor, ref outFlags, outFreeContextBuffer); if (NetEventSource.IsEnabled) { NetEventSource.Info(null, "Marshaling OUT buffer"); } // No data written out but there is Alert if (outUnmanagedBuffer[0].cbBuffer == 0 && outUnmanagedBuffer[1].cbBuffer > 0) { outSecBuffer.size = outUnmanagedBuffer[1].cbBuffer; outSecBuffer.type = outUnmanagedBuffer[1].BufferType; outSecBuffer.token = new Span <byte>((byte *)outUnmanagedBuffer[1].pvBuffer, outUnmanagedBuffer[1].cbBuffer).ToArray(); } else { outSecBuffer.size = outUnmanagedBuffer[0].cbBuffer; outSecBuffer.type = outUnmanagedBuffer[0].BufferType; outSecBuffer.token = outUnmanagedBuffer[0].cbBuffer > 0 ? new Span <byte>((byte *)outUnmanagedBuffer[0].pvBuffer, outUnmanagedBuffer[0].cbBuffer).ToArray() : null; } } } } finally { outFreeContextBuffer?.Dispose(); if (outUnmanagedBuffer[1].pvBuffer != IntPtr.Zero) { Interop.SspiCli.FreeContextBuffer(outUnmanagedBuffer[1].pvBuffer); } } if (NetEventSource.IsEnabled) { NetEventSource.Exit(null, $"errorCode:0x{errorCode:x8}, refContext:{refContext}"); } return(errorCode); }
internal static unsafe int InitializeSecurityContext( ref SafeFreeCredentials?inCredentials, ref SafeDeleteSslContext?refContext, string?targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, InputSecurityBuffers inSecBuffers, ref SecurityBuffer outSecBuffer, ref Interop.SspiCli.ContextFlags outFlags) { #if TRACE_VERBOSE if (NetEventSource.IsEnabled) { NetEventSource.Enter(null, $"credential:{inCredentials}, crefContext:{refContext}, targetName:{targetName}, inFlags:{inFlags}, endianness:{endianness}"); NetEventSource.Info(null, $"inSecBuffers.Length = {inSecBuffers.Length}"); } #endif if (inCredentials == null) { throw new ArgumentNullException(nameof(inCredentials)); } Debug.Assert(inSecBuffers.Count <= 3); Interop.SspiCli.SecBufferDesc inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Count); 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; bool isContextAbsent = true; if (refContext != null) { isContextAbsent = refContext._handle.IsZero; } // Optional output buffer that may need to be freed. SafeFreeContextBuffer?outFreeContextBuffer = null; try { Span <Interop.SspiCli.SecBuffer> inUnmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[3]; fixed(void *inUnmanagedBufferPtr = inUnmanagedBuffer) fixed(void *pinnedToken0 = inSecBuffers._item0.Token) fixed(void *pinnedToken1 = inSecBuffers._item1.Token) fixed(void *pinnedToken2 = inSecBuffers._item2.Token) { // Fix Descriptor pointer that points to unmanaged SecurityBuffers. inSecurityBufferDescriptor.pBuffers = inUnmanagedBufferPtr; // Updated pvBuffer with pinned address. UnmanagedToken takes precedence. if (inSecBuffers.Count > 2) { inUnmanagedBuffer[2].BufferType = inSecBuffers._item2.Type; inUnmanagedBuffer[2].cbBuffer = inSecBuffers._item2.Token.Length; inUnmanagedBuffer[2].pvBuffer = inSecBuffers._item2.UnmanagedToken != null ? (IntPtr)inSecBuffers._item2.UnmanagedToken.DangerousGetHandle() : (IntPtr)pinnedToken2; } if (inSecBuffers.Count > 1) { inUnmanagedBuffer[1].BufferType = inSecBuffers._item1.Type; inUnmanagedBuffer[1].cbBuffer = inSecBuffers._item1.Token.Length; inUnmanagedBuffer[1].pvBuffer = inSecBuffers._item1.UnmanagedToken != null ? (IntPtr)inSecBuffers._item1.UnmanagedToken.DangerousGetHandle() : (IntPtr)pinnedToken1; } if (inSecBuffers.Count > 0) { inUnmanagedBuffer[0].BufferType = inSecBuffers._item0.Type; inUnmanagedBuffer[0].cbBuffer = inSecBuffers._item0.Token.Length; inUnmanagedBuffer[0].pvBuffer = inSecBuffers._item0.UnmanagedToken != null ? (IntPtr)inSecBuffers._item0.UnmanagedToken.DangerousGetHandle() : (IntPtr)pinnedToken0; } fixed(byte *pinnedOutBytes = outSecBuffer.token) { // Fix Descriptor pointer that points to unmanaged SecurityBuffers. Interop.SspiCli.SecBuffer outUnmanagedBuffer = default; outSecurityBufferDescriptor.pBuffers = &outUnmanagedBuffer; outUnmanagedBuffer.cbBuffer = outSecBuffer.size; outUnmanagedBuffer.BufferType = outSecBuffer.type; outUnmanagedBuffer.pvBuffer = outSecBuffer.token == null || outSecBuffer.token.Length == 0 ? IntPtr.Zero : (IntPtr)(pinnedOutBytes + outSecBuffer.offset); if (isSspiAllocated) { outFreeContextBuffer = SafeFreeContextBuffer.CreateEmptyHandle(); } if (refContext == null || refContext.IsInvalid) { // Previous versions unconditionally built a new "refContext" here, but would pass // incorrect arguments to InitializeSecurityContextW in cases where an "contextHandle" was // already present and non-zero. if (isContextAbsent) { refContext = new SafeDeleteSslContext(); } } if (targetName == null || targetName.Length == 0) { targetName = dummyStr; } string punyCode = s_idnMapping.GetAscii(targetName); fixed(char *namePtr = punyCode) { errorCode = MustRunInitializeSecurityContext( ref inCredentials, isContextAbsent, (byte *)(((object)targetName == (object)dummyStr) ? null : namePtr), inFlags, endianness, &inSecurityBufferDescriptor, refContext !, ref outSecurityBufferDescriptor, ref outFlags, outFreeContextBuffer); } if (NetEventSource.IsEnabled) { NetEventSource.Info(null, "Marshalling OUT buffer"); } // Get unmanaged buffer with index 0 as the only one passed into PInvoke. outSecBuffer.size = outUnmanagedBuffer.cbBuffer; outSecBuffer.type = outUnmanagedBuffer.BufferType; outSecBuffer.token = outSecBuffer.size > 0 ? new Span <byte>((byte *)outUnmanagedBuffer.pvBuffer, outUnmanagedBuffer.cbBuffer).ToArray() : null; } } } finally { outFreeContextBuffer?.Dispose(); } if (NetEventSource.IsEnabled) { NetEventSource.Exit(null, $"errorCode:0x{errorCode:x8}, refContext:{refContext}"); } return(errorCode); }