private static byte[]? GetNegotiatedApplicationProtocol(SafeDeleteContext context) { Interop.SecPkgContext_ApplicationProtocol alpnContext = default; bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPISecureChannel, context, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_APPLICATION_PROTOCOL, ref alpnContext); // Check if the context returned is alpn data, with successful negotiation. if (success && alpnContext.ProtoNegoExt == Interop.ApplicationProtocolNegotiationExt.ALPN && alpnContext.ProtoNegoStatus == Interop.ApplicationProtocolNegotiationStatus.Success) { if (alpnContext.Protocol.SequenceEqual(s_http1)) { return(s_http1); } else if (alpnContext.Protocol.SequenceEqual(s_http2)) { return(s_http2); } else if (alpnContext.Protocol.SequenceEqual(s_http3)) { return(s_http3); } return(alpnContext.Protocol.ToArray()); } return(null); }
public void UpdateSslConnectionInfo(SafeDeleteContext securityContext) { SecPkgContext_ConnectionInfo interopConnectionInfo = default; bool success = SSPIWrapper.QueryBlittableContextAttributes( GlobalSSPI.SSPISecureChannel, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CONNECTION_INFO, ref interopConnectionInfo); Debug.Assert(success); TlsCipherSuite cipherSuite = default; SecPkgContext_CipherInfo cipherInfo = default; success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPISecureChannel, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CIPHER_INFO, ref cipherInfo); if (success) { cipherSuite = (TlsCipherSuite)cipherInfo.dwCipherSuite; } Protocol = interopConnectionInfo.Protocol; DataCipherAlg = interopConnectionInfo.DataCipherAlg; DataKeySize = interopConnectionInfo.DataKeySize; DataHashAlg = interopConnectionInfo.DataHashAlg; DataHashKeySize = interopConnectionInfo.DataHashKeySize; KeyExchangeAlg = interopConnectionInfo.KeyExchangeAlg; KeyExchKeySize = interopConnectionInfo.KeyExchKeySize; TlsCipherSuite = cipherSuite; ApplicationProtocol = GetNegotiatedApplicationProtocol(securityContext); }
internal static string?QueryContextAuthenticationPackage(SafeDeleteContext securityContext) { SecPkgContext_NegotiationInfoW ctx = default; bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NEGOTIATION_INFO, typeof(SafeFreeContextBuffer), out SafeHandle? sspiHandle, ref ctx); using (sspiHandle) { return(success ? NegotiationInfoClass.GetAuthenticationPackageName(sspiHandle !, (int)ctx.NegotiationState) : null); } }
internal static unsafe int Wrap(SafeDeleteContext securityContext, ReadOnlySpan <byte> buffer, [NotNull] ref byte[]?output, bool isConfidential) { SecPkgContext_Sizes sizes = default; bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes); Debug.Assert(success); // alloc new output buffer if not supplied or too small int resultSize = buffer.Length + sizes.cbMaxSignature; if (output == null || output.Length < resultSize) { output = new byte[resultSize]; } // make a copy of user data for in-place encryption buffer.CopyTo(output.AsSpan(sizes.cbMaxSignature, buffer.Length)); fixed(byte *outputPtr = output) { // Prepare buffers TOKEN(signature), DATA and Padding. Interop.SspiCli.SecBuffer *unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2]; Interop.SspiCli.SecBuffer *tokenBuffer = &unmanagedBuffer[0]; Interop.SspiCli.SecBuffer *dataBuffer = &unmanagedBuffer[1]; tokenBuffer->BufferType = SecurityBufferType.SECBUFFER_TOKEN; tokenBuffer->pvBuffer = (IntPtr)(outputPtr); tokenBuffer->cbBuffer = sizes.cbMaxSignature; dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA; dataBuffer->pvBuffer = (IntPtr)(outputPtr + sizes.cbMaxSignature); dataBuffer->cbBuffer = buffer.Length; Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2) { pBuffers = unmanagedBuffer }; uint qop = isConfidential ? 0 : Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT; int errorCode = GlobalSSPI.SSPIAuth.EncryptMessage(securityContext, ref sdcInOut, qop); if (errorCode != 0) { Exception e = new Win32Exception(errorCode); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(null, e); } throw new Win32Exception(errorCode); } // return signed size return(tokenBuffer->cbBuffer + dataBuffer->cbBuffer); } }
internal static int MakeSignature(SafeDeleteContext securityContext, byte[] buffer, int offset, int count, [AllowNull] ref byte[] output) { SecPkgContext_Sizes sizes = default; bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes); Debug.Assert(success); // alloc new output buffer if not supplied or too small int resultSize = count + sizes.cbMaxSignature; if (output == null || output.Length < resultSize) { output = new byte[resultSize]; } // make a copy of user data for in-place encryption Buffer.BlockCopy(buffer, offset, output, sizes.cbMaxSignature, count); // setup security buffers for ssp call #if NETSTANDARD2_0 Span <SecurityBuffer> securityBuffer = new SecurityBuffer[2]; #else TwoSecurityBuffers stackBuffer = default; Span <SecurityBuffer> securityBuffer = MemoryMarshal.CreateSpan(ref stackBuffer._item0, 2); #endif securityBuffer[0] = new SecurityBuffer(output, 0, sizes.cbMaxSignature, SecurityBufferType.SECBUFFER_TOKEN); securityBuffer[1] = new SecurityBuffer(output, sizes.cbMaxSignature, count, SecurityBufferType.SECBUFFER_DATA); // call SSP Function int errorCode = SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, 0); // throw if error if (errorCode != 0) { NetEventSource.Info($"MakeSignature threw error: {errorCode.ToString("x", NumberFormatInfo.InvariantInfo)}"); throw new Win32Exception(errorCode); } // return signed size return(securityBuffer[0].size + securityBuffer[1].size); }
internal static int Encrypt( SafeDeleteContext securityContext, byte[] buffer, int offset, int count, bool isConfidential, bool isNtlm, ref byte[] output, uint sequenceNumber) { SecPkgContext_Sizes sizes = default; bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes); Debug.Assert(success); try { int maxCount = checked (int.MaxValue - 4 - sizes.cbBlockSize - sizes.cbSecurityTrailer); if (count > maxCount || count < 0) { throw new ArgumentOutOfRangeException(nameof(count), SR.Format(SR.net_io_out_range, maxCount)); } } catch (Exception e) when(!ExceptionCheck.IsFatal(e)) { NetEventSource.Fail(null, "Arguments out of range."); throw; } int resultSize = count + sizes.cbSecurityTrailer + sizes.cbBlockSize; if (output == null || output.Length < resultSize + 4) { output = new byte[resultSize + 4]; } // Make a copy of user data for in-place encryption. Buffer.BlockCopy(buffer, offset, output, 4 + sizes.cbSecurityTrailer, count); // Prepare buffers TOKEN(signature), DATA and Padding. ThreeSecurityBuffers buffers = default; var securityBuffer = MemoryMarshal.CreateSpan(ref buffers._item0, 3); securityBuffer[0] = new SecurityBuffer(output, 4, sizes.cbSecurityTrailer, SecurityBufferType.SECBUFFER_TOKEN); securityBuffer[1] = new SecurityBuffer(output, 4 + sizes.cbSecurityTrailer, count, SecurityBufferType.SECBUFFER_DATA); securityBuffer[2] = new SecurityBuffer(output, 4 + sizes.cbSecurityTrailer + count, sizes.cbBlockSize, SecurityBufferType.SECBUFFER_PADDING); int errorCode; if (isConfidential) { errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber); } else { if (isNtlm) { securityBuffer[1].type |= SecurityBufferType.SECBUFFER_READONLY; } errorCode = SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, 0); } if (errorCode != 0) { Exception e = new Win32Exception(errorCode); if (NetEventSource.IsEnabled) { NetEventSource.Error(null, e); } throw e; } // Compacting the result. resultSize = securityBuffer[0].size; bool forceCopy = false; if (resultSize != sizes.cbSecurityTrailer) { forceCopy = true; Buffer.BlockCopy(output, securityBuffer[1].offset, output, 4 + resultSize, securityBuffer[1].size); } resultSize += securityBuffer[1].size; if (securityBuffer[2].size != 0 && (forceCopy || resultSize != (count + sizes.cbSecurityTrailer))) { Buffer.BlockCopy(output, securityBuffer[2].offset, output, 4 + resultSize, securityBuffer[2].size); } resultSize += securityBuffer[2].size; unchecked { output[0] = (byte)((resultSize) & 0xFF); output[1] = (byte)(((resultSize) >> 8) & 0xFF); output[2] = (byte)(((resultSize) >> 16) & 0xFF); output[3] = (byte)(((resultSize) >> 24) & 0xFF); } return(resultSize + 4); }
internal static unsafe int Encrypt( SafeDeleteContext securityContext, ReadOnlySpan <byte> buffer, bool isConfidential, bool isNtlm, [NotNull] ref byte[]?output) { SecPkgContext_Sizes sizes = default; bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes); Debug.Assert(success); int maxCount = checked (int.MaxValue - 4 - sizes.cbBlockSize - sizes.cbSecurityTrailer); if (buffer.Length > maxCount) { throw new ArgumentOutOfRangeException(nameof(buffer.Length), SR.Format(SR.net_io_out_range, maxCount)); } int resultSize = buffer.Length + sizes.cbSecurityTrailer + sizes.cbBlockSize; if (output == null || output.Length < resultSize + 4) { output = new byte[resultSize + 4]; } // Make a copy of user data for in-place encryption. buffer.CopyTo(output.AsSpan(4 + sizes.cbSecurityTrailer)); fixed(byte *outputPtr = output) { // Prepare buffers TOKEN(signature), DATA and Padding. Interop.SspiCli.SecBuffer *unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[3]; Interop.SspiCli.SecBuffer *tokenBuffer = &unmanagedBuffer[0]; Interop.SspiCli.SecBuffer *dataBuffer = &unmanagedBuffer[1]; Interop.SspiCli.SecBuffer *paddingBuffer = &unmanagedBuffer[2]; tokenBuffer->BufferType = SecurityBufferType.SECBUFFER_TOKEN; tokenBuffer->pvBuffer = (IntPtr)(outputPtr + 4); tokenBuffer->cbBuffer = sizes.cbSecurityTrailer; dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA; dataBuffer->pvBuffer = (IntPtr)(outputPtr + 4 + sizes.cbSecurityTrailer); dataBuffer->cbBuffer = buffer.Length; paddingBuffer->BufferType = SecurityBufferType.SECBUFFER_PADDING; paddingBuffer->pvBuffer = (IntPtr)(outputPtr + 4 + sizes.cbSecurityTrailer + buffer.Length); paddingBuffer->cbBuffer = sizes.cbBlockSize; Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(3) { pBuffers = unmanagedBuffer }; if (isNtlm && !isConfidential) { dataBuffer->BufferType |= SecurityBufferType.SECBUFFER_READONLY; } uint qop = isConfidential ? 0 : Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT; int errorCode = GlobalSSPI.SSPIAuth.EncryptMessage(securityContext, ref sdcInOut, qop); if (errorCode != 0) { Exception e = new Win32Exception(errorCode); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(null, e); } throw new Win32Exception(errorCode); } // Compacting the result. resultSize = tokenBuffer->cbBuffer; bool forceCopy = false; if (resultSize != sizes.cbSecurityTrailer) { forceCopy = true; output.AsSpan(4 + sizes.cbSecurityTrailer, dataBuffer->cbBuffer).CopyTo(output.AsSpan(4 + resultSize, dataBuffer->cbBuffer)); } resultSize += dataBuffer->cbBuffer; if (paddingBuffer->cbBuffer != 0 && (forceCopy || resultSize != (buffer.Length + sizes.cbSecurityTrailer))) { output.AsSpan(4 + sizes.cbSecurityTrailer + buffer.Length, paddingBuffer->cbBuffer).CopyTo(output.AsSpan(4 + resultSize, paddingBuffer->cbBuffer)); } resultSize += paddingBuffer->cbBuffer; BinaryPrimitives.WriteInt32LittleEndian(output, resultSize); return(resultSize + 4); } }