示例#1
0
        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();
                        }
                    }
                }
            }
        }
示例#2
0
        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;
        }
示例#3
0
        //-------------------------------------------------------------------
        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;
        }
示例#4
0
        public static unsafe SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, byte[] input, int offset, int size, int headerSize, int trailerSize, ref byte[] output, out int resultSize)
        {
            // Ensure that there is sufficient space for the message output.
            int bufferSizeNeeded;
            try
            {
                bufferSizeNeeded = checked(size + headerSize + trailerSize);
            }
            catch
            {
                NetEventSource.Fail(securityContext, "Arguments out of range");
                throw;
            }
            if (output == null || output.Length < bufferSizeNeeded)
            {
                output = new byte[bufferSizeNeeded];
            }

            // Copy the input into the output buffer to prepare for SCHANNEL's expectations
            Buffer.BlockCopy(input, offset, output, headerSize, size);

            const int NumSecBuffers = 4; // header + data + trailer + empty
            var unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers];
            var sdcInOut = new Interop.SspiCli.SecBufferDesc(NumSecBuffers);
            sdcInOut.pBuffers = unmanagedBuffer;
            fixed (byte* outputPtr = output)
            {
                Interop.SspiCli.SecBuffer* headerSecBuffer = &unmanagedBuffer[0];
                headerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_HEADER;
                headerSecBuffer->pvBuffer = (IntPtr)outputPtr;
                headerSecBuffer->cbBuffer = headerSize;

                Interop.SspiCli.SecBuffer* dataSecBuffer = &unmanagedBuffer[1];
                dataSecBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
                dataSecBuffer->pvBuffer = (IntPtr)(outputPtr + headerSize);
                dataSecBuffer->cbBuffer = size;

                Interop.SspiCli.SecBuffer* trailerSecBuffer = &unmanagedBuffer[2];
                trailerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_TRAILER;
                trailerSecBuffer->pvBuffer = (IntPtr)(outputPtr + headerSize + size);
                trailerSecBuffer->cbBuffer = trailerSize;

                Interop.SspiCli.SecBuffer* emptySecBuffer = &unmanagedBuffer[3];
                emptySecBuffer->BufferType = SecurityBufferType.SECBUFFER_EMPTY;
                emptySecBuffer->cbBuffer = 0;
                emptySecBuffer->pvBuffer = IntPtr.Zero;

                int errorCode = GlobalSSPI.SSPISecureChannel.EncryptMessage(securityContext, sdcInOut, 0);

                if (errorCode != 0)
                {
                    if (NetEventSource.IsEnabled) NetEventSource.Info(securityContext, $"Encrypt ERROR {errorCode:X}");
                    resultSize = 0;
                    return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode);
                }

                Debug.Assert(headerSecBuffer->cbBuffer >= 0 && dataSecBuffer->cbBuffer >= 0 && trailerSecBuffer->cbBuffer >= 0);
                Debug.Assert(checked(headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer) <= output.Length);

                resultSize = checked(headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer);
                return new SecurityStatusPal(SecurityStatusPalErrorCode.OK);
            }
        }
示例#5
0
        public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteContext securityContext, byte[] buffer, ref int offset, ref int count)
        {
            const int NumSecBuffers = 4; // data + empty + empty + empty
            var unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers];
            var sdcInOut = new Interop.SspiCli.SecBufferDesc(NumSecBuffers);
            sdcInOut.pBuffers = unmanagedBuffer;
            fixed (byte* bufferPtr = buffer)
            {
                Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[0];
                dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
                dataBuffer->pvBuffer = (IntPtr)bufferPtr + offset;
                dataBuffer->cbBuffer = count;

                for (int i = 1; i < NumSecBuffers; i++)
                {
                    Interop.SspiCli.SecBuffer* emptyBuffer = &unmanagedBuffer[i];
                    emptyBuffer->BufferType = SecurityBufferType.SECBUFFER_EMPTY;
                    emptyBuffer->pvBuffer = IntPtr.Zero;
                    emptyBuffer->cbBuffer = 0;
                }

                Interop.SECURITY_STATUS errorCode = (Interop.SECURITY_STATUS)GlobalSSPI.SSPISecureChannel.DecryptMessage(securityContext, sdcInOut, 0);

                // Decrypt may repopulate the sec buffers, likely with header + data + trailer + empty.
                // We need to find the data.
                count = 0;
                for (int i = 0; i < NumSecBuffers; i++)
                {
                    // Successfully decoded data and placed it at the following position in the buffer,
                    if ((errorCode == Interop.SECURITY_STATUS.OK && unmanagedBuffer[i].BufferType == SecurityBufferType.SECBUFFER_DATA)
                        // or we failed to decode the data, here is the encoded data.
                        || (errorCode != Interop.SECURITY_STATUS.OK && unmanagedBuffer[i].BufferType == SecurityBufferType.SECBUFFER_EXTRA))
                    {
                        offset = (int)((byte*)unmanagedBuffer[i].pvBuffer - bufferPtr);
                        count = unmanagedBuffer[i].cbBuffer;

                        Debug.Assert(offset >= 0 && count >= 0, $"Expected offset and count greater than 0, got {offset} and {count}");
                        Debug.Assert(checked(offset + count) <= buffer.Length, $"Expected offset+count <= buffer.Length, got {offset}+{count}>={buffer.Length}");

                        break;
                    }
                }

                return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(errorCode);
            }
        }
示例#6
0
        public static unsafe SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, ReadOnlyMemory <byte> input, int headerSize, int trailerSize, ref byte[] output, out int resultSize)
        {
            // Ensure that there is sufficient space for the message output.
            int bufferSizeNeeded;

            try
            {
                bufferSizeNeeded = checked (input.Length + headerSize + trailerSize);
            }
            catch
            {
                NetEventSource.Fail(securityContext, "Arguments out of range");
                throw;
            }
            if (output == null || output.Length < bufferSizeNeeded)
            {
                output = new byte[bufferSizeNeeded];
            }

            // Copy the input into the output buffer to prepare for SCHANNEL's expectations
            input.Span.CopyTo(new Span <byte>(output, headerSize, input.Length));

            const int NumSecBuffers   = 4; // header + data + trailer + empty
            var       unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers];
            var       sdcInOut        = new Interop.SspiCli.SecBufferDesc(NumSecBuffers);

            sdcInOut.pBuffers = unmanagedBuffer;
            fixed(byte *outputPtr = output)
            {
                Interop.SspiCli.SecBuffer *headerSecBuffer = &unmanagedBuffer[0];
                headerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_HEADER;
                headerSecBuffer->pvBuffer   = (IntPtr)outputPtr;
                headerSecBuffer->cbBuffer   = headerSize;

                Interop.SspiCli.SecBuffer *dataSecBuffer = &unmanagedBuffer[1];
                dataSecBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
                dataSecBuffer->pvBuffer   = (IntPtr)(outputPtr + headerSize);
                dataSecBuffer->cbBuffer   = input.Length;

                Interop.SspiCli.SecBuffer *trailerSecBuffer = &unmanagedBuffer[2];
                trailerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_TRAILER;
                trailerSecBuffer->pvBuffer   = (IntPtr)(outputPtr + headerSize + input.Length);
                trailerSecBuffer->cbBuffer   = trailerSize;

                Interop.SspiCli.SecBuffer *emptySecBuffer = &unmanagedBuffer[3];
                emptySecBuffer->BufferType = SecurityBufferType.SECBUFFER_EMPTY;
                emptySecBuffer->cbBuffer   = 0;
                emptySecBuffer->pvBuffer   = IntPtr.Zero;

                int errorCode = GlobalSSPI.SSPISecureChannel.EncryptMessage(securityContext, ref sdcInOut, 0);

                if (errorCode != 0)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(securityContext, $"Encrypt ERROR {errorCode:X}");
                    }
                    resultSize = 0;
                    return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode));
                }

                Debug.Assert(headerSecBuffer->cbBuffer >= 0 && dataSecBuffer->cbBuffer >= 0 && trailerSecBuffer->cbBuffer >= 0);
                Debug.Assert(checked (headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer) <= output.Length);

                resultSize = checked (headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer);
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.OK));
            }
        }
示例#7
0
 public int VerifySignature(SafeDeleteContext context, ref Interop.SspiCli.SecBufferDesc inputOutput, uint sequenceNumber)
 {
     throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
 }
示例#8
0
        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(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();
                        }
                    }
                }
            }
        }
示例#9
0
        private static unsafe int MustRunAcceptSecurityContext_SECURITY(
            ref SafeFreeCredentials inCredentials,
            bool isContextAbsent,
            Interop.SspiCli.SecBufferDesc *inputBuffer,
            Interop.SspiCli.ContextFlags inFlags,
            Interop.SspiCli.Endianness endianness,
            SafeDeleteContext outContext,
            ref Interop.SspiCli.SecBufferDesc outputBuffer,
            ref Interop.SspiCli.ContextFlags outFlags,
            SafeFreeContextBuffer?handleTemplate)
        {
            int errorCode = (int)Interop.SECURITY_STATUS.InvalidHandle;

            // Run the body of this method as a non-interruptible block.
            try
            {
                bool ignore = false;

                inCredentials.DangerousAddRef(ref ignore);
                outContext.DangerousAddRef(ref ignore);

                Interop.SspiCli.CredHandle credentialHandle = inCredentials._handle;
                long timeStamp;

                // Now that "outContext" (or "refContext" by the caller) references an actual handle (and cannot
                // be closed until it is released below), point "inContextPtr" to its embedded handle (or
                // null if the embedded handle has not yet been initialized).
                Interop.SspiCli.CredHandle contextHandle = outContext._handle;
                void *inContextPtr = contextHandle.IsZero ? null : &contextHandle;

                // The "isContextAbsent" supplied by the caller is generally correct but was computed without proper
                // synchronization. Rewrite the indicator now that the final "inContext" is known, update if necessary.
                isContextAbsent = (inContextPtr == null);

                errorCode = Interop.SspiCli.AcceptSecurityContext(
                    ref credentialHandle,
                    inContextPtr,
                    inputBuffer,
                    inFlags,
                    endianness,
                    ref outContext._handle,
                    ref outputBuffer,
                    ref outFlags,
                    out timeStamp);
            }
            finally
            {
                //
                // 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.
                    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)
            {
                //ATTN: on 64 BIT that is still +8 cause of 2* c++ unsigned long == 8 bytes.
                handleTemplate.Set(((Interop.SspiCli.SecBuffer *)outputBuffer.pBuffers)->pvBuffer);
                if (handleTemplate.IsInvalid)
                {
                    handleTemplate.SetHandleAsInvalid();
                }
            }

            if (isContextAbsent && (errorCode & 0x80000000) != 0)
            {
                // An error on the first call, need to set the out handle to invalid value.
                outContext._handle.SetToInvalid();
            }

            return(errorCode);
        }
示例#10
0
        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 (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(null, $"credential={inCredentials}, refContext={refContext}, inFlags={inFlags}");
            }

            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);
        }
示例#11
0
        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 (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(null, $"credential:{inCredentials}, crefContext:{refContext}, targetName:{targetName}, inFlags:{inFlags}, endianness:{endianness}");
            }

            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);
        }
示例#12
0
        public static unsafe SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, byte[] input, int offset, int size, int headerSize, int trailerSize, ref byte[] output, out int resultSize)
        {
            // Ensure that there is sufficient space for the message output.
            int bufferSizeNeeded;

            try
            {
                bufferSizeNeeded = checked (size + headerSize + trailerSize);
            }
            catch
            {
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.Assert("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt", "Arguments out of range.");
                }
                Debug.Fail("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt", "Arguments out of range.");
                throw;
            }
            if (output == null || output.Length < bufferSizeNeeded)
            {
                output = new byte[bufferSizeNeeded];
            }

            // Copy the input into the output buffer to prepare for SCHANNEL's expectations
            Buffer.BlockCopy(input, offset, output, headerSize, size);

            const int NumSecBuffers   = 4; // header + data + trailer + empty
            var       unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers];
            var       sdcInOut        = new Interop.SspiCli.SecBufferDesc(NumSecBuffers);

            sdcInOut.pBuffers = unmanagedBuffer;
            fixed(byte *outputPtr = output)
            {
                Interop.SspiCli.SecBuffer *headerSecBuffer = &unmanagedBuffer[0];
                headerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_HEADER;
                headerSecBuffer->pvBuffer   = (IntPtr)outputPtr;
                headerSecBuffer->cbBuffer   = headerSize;

                Interop.SspiCli.SecBuffer *dataSecBuffer = &unmanagedBuffer[1];
                dataSecBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
                dataSecBuffer->pvBuffer   = (IntPtr)(outputPtr + headerSize);
                dataSecBuffer->cbBuffer   = size;

                Interop.SspiCli.SecBuffer *trailerSecBuffer = &unmanagedBuffer[2];
                trailerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_TRAILER;
                trailerSecBuffer->pvBuffer   = (IntPtr)(outputPtr + headerSize + size);
                trailerSecBuffer->cbBuffer   = trailerSize;

                Interop.SspiCli.SecBuffer *emptySecBuffer = &unmanagedBuffer[3];
                emptySecBuffer->BufferType = SecurityBufferType.SECBUFFER_EMPTY;
                emptySecBuffer->cbBuffer   = 0;
                emptySecBuffer->pvBuffer   = IntPtr.Zero;

                int errorCode = GlobalSSPI.SSPISecureChannel.EncryptMessage(securityContext, sdcInOut, 0);

                if (errorCode != 0)
                {
                    if (GlobalLog.IsEnabled)
                    {
                        GlobalLog.Print("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt ERROR" + errorCode.ToString("x"));
                    }

                    resultSize = 0;
                    return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode));
                }

                Debug.Assert(headerSecBuffer->cbBuffer >= 0 && dataSecBuffer->cbBuffer >= 0 && trailerSecBuffer->cbBuffer >= 0);
                Debug.Assert(checked (headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer) <= output.Length);

                resultSize = checked (headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer);
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.OK));
            }
        }