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); }
private static unsafe int EncryptDecryptHelper(OP op, SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber) { Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(input.Length); var unmanagedBuffer = new Interop.SspiCli.SecBuffer[input.Length]; fixed(Interop.SspiCli.SecBuffer *unmanagedBufferPtr = unmanagedBuffer) { sdcInOut.pBuffers = unmanagedBufferPtr; GCHandle[] pinnedBuffers = new GCHandle[input.Length]; byte[][] buffers = new byte[input.Length][]; try { for (int i = 0; i < input.Length; i++) { SecurityBuffer iBuffer = input[i]; unmanagedBuffer[i].cbBuffer = iBuffer.size; unmanagedBuffer[i].BufferType = iBuffer.type; if (iBuffer.token == null || iBuffer.token.Length == 0) { unmanagedBuffer[i].pvBuffer = IntPtr.Zero; } else { pinnedBuffers[i] = GCHandle.Alloc(iBuffer.token, GCHandleType.Pinned); unmanagedBuffer[i].pvBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(iBuffer.token, iBuffer.offset); buffers[i] = iBuffer.token; } } // The result is written in the input Buffer passed as type=BufferType.Data. int errorCode; switch (op) { case OP.Encrypt: errorCode = secModule.EncryptMessage(context, ref sdcInOut, sequenceNumber); break; case OP.Decrypt: errorCode = secModule.DecryptMessage(context, ref sdcInOut, sequenceNumber); break; case OP.MakeSignature: errorCode = secModule.MakeSignature(context, ref sdcInOut, sequenceNumber); break; case OP.VerifySignature: errorCode = secModule.VerifySignature(context, ref sdcInOut, sequenceNumber); break; default: NetEventSource.Fail(null, $"Unknown OP: {op}"); throw NotImplemented.ByDesignWithMessage(Strings.net_MethodNotImplementedException); } // Marshalling back returned sizes / data. for (int i = 0; i < input.Length; i++) { SecurityBuffer iBuffer = input[i]; iBuffer.size = unmanagedBuffer[i].cbBuffer; iBuffer.type = unmanagedBuffer[i].BufferType; if (iBuffer.size == 0) { iBuffer.offset = 0; iBuffer.token = null; } else { checked { // Find the buffer this is inside of. Usually they all point inside buffer 0. int j; for (j = 0; j < input.Length; j++) { if (buffers[j] == null) { continue; } byte *bufferAddress = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers[j], 0); if ((byte *)unmanagedBuffer[i].pvBuffer >= bufferAddress && (byte *)unmanagedBuffer[i].pvBuffer + iBuffer.size <= bufferAddress + buffers[j].Length) { iBuffer.offset = (int)((byte *)unmanagedBuffer[i].pvBuffer - bufferAddress); iBuffer.token = buffers[j]; break; } } if (j >= input.Length) { NetEventSource.Fail(null, "Output buffer out of range."); iBuffer.size = 0; iBuffer.offset = 0; iBuffer.token = null; } } } // Backup validate the new sizes. if (iBuffer.offset < 0 || iBuffer.offset > (iBuffer.token == null ? 0 : iBuffer.token.Length)) { NetEventSource.Fail(null, $"'offset' out of range. [{iBuffer.offset}]"); } if (iBuffer.size < 0 || iBuffer.size > (iBuffer.token == null ? 0 : iBuffer.token.Length - iBuffer.offset)) { NetEventSource.Fail(null, $"'size' out of range. [{iBuffer.size}]"); } } if (NetEventSource.IsEnabled && errorCode != 0) { if (errorCode == Interop.SspiCli.SEC_I_RENEGOTIATE) { NetEventSource.Error(null, System.StringsHelper.Format(Strings.event_OperationReturnedSomething, op, "SEC_I_RENEGOTIATE")); } else { NetEventSource.Error(null, System.StringsHelper.Format(Strings.net_log_operation_failed_with_error, op, $"0x{0:X}")); } } return(errorCode); } finally { for (int i = 0; i < pinnedBuffers.Length; ++i) { if (pinnedBuffers[i].IsAllocated) { pinnedBuffers[i].Free(); } } } } }
//------------------------------------------------------------------- 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; }
private unsafe static int EncryptDecryptHelper(OP op, SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber) { Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(input.Length); var unmanagedBuffer = new Interop.SspiCli.SecBuffer[input.Length]; fixed (Interop.SspiCli.SecBuffer* unmanagedBufferPtr = unmanagedBuffer) { sdcInOut.pBuffers = unmanagedBufferPtr; GCHandle[] pinnedBuffers = new GCHandle[input.Length]; byte[][] buffers = new byte[input.Length][]; try { for (int i = 0; i < input.Length; i++) { SecurityBuffer iBuffer = input[i]; unmanagedBuffer[i].cbBuffer = iBuffer.size; unmanagedBuffer[i].BufferType = iBuffer.type; if (iBuffer.token == null || iBuffer.token.Length == 0) { unmanagedBuffer[i].pvBuffer = IntPtr.Zero; } else { pinnedBuffers[i] = GCHandle.Alloc(iBuffer.token, GCHandleType.Pinned); unmanagedBuffer[i].pvBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(iBuffer.token, iBuffer.offset); buffers[i] = iBuffer.token; } } // The result is written in the input Buffer passed as type=BufferType.Data. int errorCode; switch (op) { case OP.Encrypt: errorCode = secModule.EncryptMessage(context, sdcInOut, sequenceNumber); break; case OP.Decrypt: errorCode = secModule.DecryptMessage(context, sdcInOut, sequenceNumber); break; case OP.MakeSignature: errorCode = secModule.MakeSignature(context, sdcInOut, sequenceNumber); break; case OP.VerifySignature: errorCode = secModule.VerifySignature(context, sdcInOut, sequenceNumber); break; default: NetEventSource.Fail(null, $"Unknown OP: {op}"); throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException); } // Marshalling back returned sizes / data. for (int i = 0; i < input.Length; i++) { SecurityBuffer iBuffer = input[i]; iBuffer.size = unmanagedBuffer[i].cbBuffer; iBuffer.type = unmanagedBuffer[i].BufferType; if (iBuffer.size == 0) { iBuffer.offset = 0; iBuffer.token = null; } else { checked { // Find the buffer this is inside of. Usually they all point inside buffer 0. int j; for (j = 0; j < input.Length; j++) { if (buffers[j] == null) { continue; } byte* bufferAddress = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement(buffers[j], 0); if ((byte*)unmanagedBuffer[i].pvBuffer >= bufferAddress && (byte*)unmanagedBuffer[i].pvBuffer + iBuffer.size <= bufferAddress + buffers[j].Length) { iBuffer.offset = (int)((byte*)unmanagedBuffer[i].pvBuffer - bufferAddress); iBuffer.token = buffers[j]; break; } } if (j >= input.Length) { NetEventSource.Fail(null, "Output buffer out of range."); iBuffer.size = 0; iBuffer.offset = 0; iBuffer.token = null; } } } // Backup validate the new sizes. if (iBuffer.offset < 0 || iBuffer.offset > (iBuffer.token == null ? 0 : iBuffer.token.Length)) { NetEventSource.Fail(null, $"'offset' out of range. [{iBuffer.offset}]"); } if (iBuffer.size < 0 || iBuffer.size > (iBuffer.token == null ? 0 : iBuffer.token.Length - iBuffer.offset)) { NetEventSource.Fail(null, $"'size' out of range. [{iBuffer.size}]"); } } if (NetEventSource.IsEnabled && errorCode != 0) { if (errorCode == Interop.SspiCli.SEC_I_RENEGOTIATE) { NetEventSource.Error(null, SR.Format(SR.event_OperationReturnedSomething, op, "SEC_I_RENEGOTIATE")); } else { NetEventSource.Error(null, SR.Format(SR.net_log_operation_failed_with_error, op, $"0x{0:X}")); } } return errorCode; } finally { for (int i = 0; i < pinnedBuffers.Length; ++i) { if (pinnedBuffers[i].IsAllocated) { pinnedBuffers[i].Free(); } } } } }
internal unsafe static int ApplyControlToken( ref SafeDeleteContext refContext, SecurityBuffer[] inSecBuffers) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(null); NetEventSource.Info(null, $" refContext = {refContext}"); NetEventSource.Info(null, $" inSecBuffers[] = length:{inSecBuffers.Length}"); } if (inSecBuffers == null) { NetEventSource.Fail(null, "inSecBuffers == null"); } var inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Length); int errorCode = (int)Interop.SECURITY_STATUS.InvalidHandle; // These are pinned user byte arrays passed along with SecurityBuffers. GCHandle[] pinnedInBytes = null; var inUnmanagedBuffer = new Interop.SspiCli.SecBuffer[inSecurityBufferDescriptor.cBuffers]; fixed (void* inUnmanagedBufferPtr = inUnmanagedBuffer) { // 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 = inSecBuffers[index]; if (securityBuffer != null) { 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 } } // TODO: (#3114): Optimizations to remove the unnecesary allocation of a CredHandle, remove the AddRef // if refContext was previously null, refactor the code to unify CompleteAuthToken and ApplyControlToken. Interop.SspiCli.CredHandle contextHandle = new Interop.SspiCli.CredHandle(); if (refContext != null) { contextHandle = refContext._handle; } try { if (refContext == null || refContext.IsInvalid) { refContext = new SafeDeleteContext_SECURITY(); } try { bool ignore = false; refContext.DangerousAddRef(ref ignore); errorCode = Interop.SspiCli.ApplyControlToken(contextHandle.IsZero ? null : &contextHandle, ref inSecurityBufferDescriptor); } finally { refContext.DangerousRelease(); } } finally { if (pinnedInBytes != null) { for (int index = 0; index < pinnedInBytes.Length; index++) { if (pinnedInBytes[index].IsAllocated) { pinnedInBytes[index].Free(); } } } } } if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"unmanaged ApplyControlToken() errorCode:0x{errorCode:x8} refContext: {refContext}"); return errorCode; }