From 8ec5a72b49d44274becd9aea7a4db9eda39ad206 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 8 Feb 2019 15:09:50 -0500 Subject: [PATCH] Remove a lot of allocation from SslStream's handshake (dotnet/corefx#35091) * Remove a lot of allocation from SslStream's handshake This makes a sizeable dent in the allocation incurred as part of SslStream's handshake (AuthenticateAsClient/Server{Async}), though mainly on Windows; many of these changes also impact Unix, but on Unix there's also substantially more allocation, so the relevant percentage impact is less. Main changes: - X509Certificate2.RawData. This was being accessed by SslStream in order to store the byte[] away in case the RemoteCertificate property was accessed, in which case it would use it to instantiate an X509Certificate2. That, however, was resulting in lots of sizeable byte[]s being allocated. We can instead just instantiate an X509Certificate2 from the original, and while that entails a few additional small allocations, it's a sizeable reduction, and we'd eventually need the cert anyway if the property is accessed. - X509ChainPolicy. Every time Reset was called, it would allocate several new collections, but they often weren't used. Made them lazy. - X509 object finalization. A bunch of X509-related objects were being left for finalization. Those are now aggressively disposed. - ChainPal.GetChainStatusInformation: every call was P/Invoke'ing to get a string error message for a handful of prechosen error codes... we can just get the strings once upfront and avoid allocating them each time. - OidCollections. A bunch of these are created but not actually populated, and each time one is created it was allocating a new List. Changed to just have it be built around an array. - SecPkgContext classes. These have been converted to blittable structs, avoiding the associated allocations and marshaling costs, e.g. SecPkgContext_ApplicationProtocol would end up allocating a byte[] along with the instance itself. - PtrToStructure. When QueryContextAttributes was being called, in many cases PtrToStructure was being used. This has been cleaned up, to avoid the marshaling and just use spans / casting. - SecurityBuffer[] arrays. Lots of code was written in terms of SecurityBuffer[], which meant lots of operations allocating these. However, they were never larger than three items. We now just stack allocate spans for them. - Overloads taking both SecurityBuffer and SecurityBuffer[]. I believe because of the arrays, there were some cases where methods would take both a SecurityBuffer argument and a SecurityBuffer[] argument, with one and only one non-null. Now that we're just passing around allocation-free spans, this has been cleaned up. - GCHandles and GCHandle[]. Multiple methods were allocating arrays of GCHandles, and then one GCHandle.Alloc (not a managed allocation, but also not free) per SecurityBuffer. Now that we're known to be limited to at most three, we just used fixed (even if we don't actually have three), avoiding the need for the arrays and for the GCHandle.Alloc, which is more expensive than fixed. - SecBuffer[] arrays. Several places were allocating a SecBuffer[]; we can just stackalloc a struct. These are all limited by the number of SecurityBuffers passed around, which is inherently small (<= 3 in the current code). - SecureChannel.EnsurePrivateKey. It was always forcing a certificate's Thumbpring into existence, even though it was rarely needed. - SecureChannel.AcquireClientCredentials. It was always forcing a List into existence, even if it wasn't needed. - SslAuthenticationOptions. This internal type had several fields for delegates that were never used. Removed. - Span.ToArray. In several places utilize span to create an array from a pointer/length rather than allocating the array and Marshal.Copy'ing. This is mostly for readability. - Also cleaned up a few things, like using ?.Dispose across System.Net.Security. - Protocol version strings. On Unix we were getting the protocol version constant string from the native string, allocating a managed string for it, and then comparing it against the set of known constant strings. We don't need to do that. There's still more that can be done. On Windows, the two big remaining sources of allocation are for byte[]s created for data to send/receive in the handshake, and that will hopefully be addressed by work to combine SslStream/SslStreamInternal/SslState and to rewrite them to use async/await, and the allocation of the actual SecurityBuffers, which should be fairly straightforward to convert to structs (albeit with some care, as they're mutated as they're passed around). On Unix, there's a ton more allocation, in particular as part of X509Chain building, and that should be investigated separately as part of an effort to improve performance there. * Move SecurityBuffer to be Windows-only and make it a struct * Rename several files * Fix netstandard build of SqlClient Commit migrated from https://github.com/dotnet/corefx/commit/3c30357d51b96339021b4ee52f38445c31248bb2 --- .../Interop.OpenSsl.cs | 19 +- .../Interop.Ssl.cs | 7 +- .../Interop.SecPkgContext_ApplicationProtocol.cs | 10 +- .../SChannel/SecPkgContext_ConnectionInfo.cs | 28 +- .../src/Interop/Windows/SspiCli/GlobalSSPI.cs | 4 +- .../src/Interop/Windows/SspiCli/Interop.SSPI.cs | 12 +- .../Windows/SspiCli/NegotiationInfoClass.cs | 55 +- .../src/Interop/Windows/SspiCli/SSPIAuthType.cs | 24 +- .../src/Interop/Windows/SspiCli/SSPIInterface.cs | 13 +- .../Windows/SspiCli/SSPISecureChannelType.cs | 26 +- .../src/Interop/Windows/SspiCli/SSPIWrapper.cs | 298 +++++----- .../Interop/Windows/SspiCli/SecPkgContext_Sizes.cs | 26 +- .../Windows/SspiCli/SecPkgContext_StreamSizes.cs | 27 +- .../Interop/Windows/SspiCli/SecuritySafeHandles.cs | 635 +++++++-------------- .../Common/src/System/Net/LazyAsyncResult.cs | 5 +- .../src/System/Net/NTAuthentication.Common.cs | 49 +- .../Common/src/System/Net/NegotiationInfoClass.cs | 2 +- .../System/Net/Security/NegotiateStreamPal.Unix.cs | 55 +- .../Net/Security/NegotiateStreamPal.Windows.cs | 128 ++++- .../src/System/Net/Security/SSPIHandleCache.cs | 6 +- ...SecurityBuffer.cs => SecurityBuffer.Windows.cs} | 40 +- ...BufferType.cs => SecurityBufferType.Windows.cs} | 0 .../src/System.Data.SqlClient.csproj | 13 +- .../src/System/Data/SqlClient/SNI/SNIProxy.cs | 26 +- .../System.Net.Http/src/System.Net.Http.csproj | 12 +- .../src/System.Net.HttpListener.csproj | 11 +- .../System.Net.Mail/src/System.Net.Mail.csproj | 13 +- .../src/System.Net.Security.csproj | 12 +- .../System/Net/CertificateValidationPal.Unix.cs | 15 +- .../System/Net/CertificateValidationPal.Windows.cs | 41 +- .../src/System/Net/FixedSizeReader.cs | 3 +- .../System/Net/Security/InternalNegotiateStream.cs | 5 +- .../Net/Security/NegotiateStreamPal.Windows.cs | 32 +- .../src/System/Net/Security/SecureChannel.cs | 147 ++--- .../Net/Security/SslAuthenticationOptions.cs | 6 - .../System/Net/Security/SslConnectionInfo.Unix.cs | 69 ++- .../src/System/Net/Security/SslConnectionInfo.cs | 48 +- .../src/System/Net/Security/SslState.cs | 17 +- .../src/System/Net/Security/SslStream.cs | 25 +- .../src/System/Net/Security/SslStreamInternal.cs | 5 +- .../src/System/Net/Security/SslStreamPal.OSX.cs | 65 +-- .../src/System/Net/Security/SslStreamPal.Unix.cs | 59 +- .../System/Net/Security/SslStreamPal.Windows.cs | 163 +++--- .../System/Security/Cryptography/OidCollection.cs | 41 +- .../src/Internal/Cryptography/Pal.OSX/ChainPal.cs | 24 +- .../Cryptography/Pal.Windows/CertificatePal.cs | 4 +- .../ChainPal.GetChainStatusInformation.cs | 4 +- .../X509Certificates/X509Certificate.cs | 4 +- .../X509Certificates/X509Certificate2.cs | 6 + .../X509Certificates/X509ChainPolicy.cs | 34 +- 50 files changed, 972 insertions(+), 1401 deletions(-) rename src/libraries/Common/src/System/Net/Security/{SecurityBuffer.cs => SecurityBuffer.Windows.cs} (58%) rename src/libraries/Common/src/System/Net/Security/{SecurityBufferType.cs => SecurityBufferType.Windows.cs} (100%) diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index d184ae9..c3c5105 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -165,10 +165,27 @@ internal static partial class Interop certHandle.DangerousAddRef(ref hasCertReference); using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle())) { - using (X509Chain chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false)) + X509Chain chain = null; + try { + chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false); if (chain != null && !Ssl.AddExtraChainCertificates(context, chain)) + { throw CreateSslException(SR.net_ssl_use_cert_failed); + } + } + finally + { + if (chain != null) + { + int elementsCount = chain.ChainElements.Count; + for (int i = 0; i < elementsCount; i++) + { + chain.ChainElements[i].Certificate.Dispose(); + } + + chain.Dispose(); + } } } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs index c03d147..d1c07fa 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs @@ -47,7 +47,7 @@ internal static partial class Interop internal static extern void SslSetAcceptState(SafeSslHandle ssl); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslGetVersion")] - private static extern IntPtr SslGetVersion(SafeSslHandle ssl); + internal static extern IntPtr SslGetVersion(SafeSslHandle ssl); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslSetTlsExtHostName")] [return: MarshalAs(UnmanagedType.Bool)] @@ -70,11 +70,6 @@ internal static partial class Interop return result; } - internal static string GetProtocolVersion(SafeSslHandle ssl) - { - return Marshal.PtrToStringAnsi(SslGetVersion(ssl)); - } - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetSslConnectionInfo")] internal static extern bool GetSslConnectionInfo( SafeSslHandle ssl, diff --git a/src/libraries/Common/src/Interop/Windows/SChannel/Interop.SecPkgContext_ApplicationProtocol.cs b/src/libraries/Common/src/Interop/Windows/SChannel/Interop.SecPkgContext_ApplicationProtocol.cs index 447afb9..4f68e3d 100644 --- a/src/libraries/Common/src/Interop/Windows/SChannel/Interop.SecPkgContext_ApplicationProtocol.cs +++ b/src/libraries/Common/src/Interop/Windows/SChannel/Interop.SecPkgContext_ApplicationProtocol.cs @@ -22,20 +22,22 @@ internal static partial class Interop } [StructLayout(LayoutKind.Sequential)] - internal class SecPkgContext_ApplicationProtocol + internal unsafe struct SecPkgContext_ApplicationProtocol { private const int MaxProtocolIdSize = 0xFF; public ApplicationProtocolNegotiationStatus ProtoNegoStatus; public ApplicationProtocolNegotiationExt ProtoNegoExt; public byte ProtocolIdSize; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxProtocolIdSize)] - public byte[] ProtocolId; + public fixed byte ProtocolId[MaxProtocolIdSize]; public byte[] Protocol { get { - return new Span(ProtocolId, 0, ProtocolIdSize).ToArray(); + fixed (byte* pid = ProtocolId) + { + return new Span(pid, ProtocolIdSize).ToArray(); + } } } } diff --git a/src/libraries/Common/src/Interop/Windows/SChannel/SecPkgContext_ConnectionInfo.cs b/src/libraries/Common/src/Interop/Windows/SChannel/SecPkgContext_ConnectionInfo.cs index 57b6eb3..5e4b881 100644 --- a/src/libraries/Common/src/Interop/Windows/SChannel/SecPkgContext_ConnectionInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/SChannel/SecPkgContext_ConnectionInfo.cs @@ -2,15 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.Runtime.InteropServices; namespace System.Net { - // TODO (Issue #3114): Investigate if this can be safely converted to a struct. // From Schannel.h [StructLayout(LayoutKind.Sequential)] - internal class SecPkgContext_ConnectionInfo + internal struct SecPkgContext_ConnectionInfo { public readonly int Protocol; public readonly int DataCipherAlg; @@ -19,29 +17,5 @@ namespace System.Net public readonly int DataHashKeySize; public readonly int KeyExchangeAlg; public readonly int KeyExchKeySize; - - internal unsafe SecPkgContext_ConnectionInfo(byte[] nativeBuffer) - { - fixed (void* voidPtr = nativeBuffer) - { - try - { - // TODO (Issue #3114): replace with Marshal.PtrToStructure. - IntPtr unmanagedAddress = new IntPtr(voidPtr); - Protocol = Marshal.ReadInt32(unmanagedAddress); - DataCipherAlg = Marshal.ReadInt32(unmanagedAddress, 4); - DataKeySize = Marshal.ReadInt32(unmanagedAddress, 8); - DataHashAlg = Marshal.ReadInt32(unmanagedAddress, 12); - DataHashKeySize = Marshal.ReadInt32(unmanagedAddress, 16); - KeyExchangeAlg = Marshal.ReadInt32(unmanagedAddress, 20); - KeyExchKeySize = Marshal.ReadInt32(unmanagedAddress, 24); - } - catch (OverflowException) - { - NetEventSource.Fail(this, "Negative size"); - throw; - } - } - } } } diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/GlobalSSPI.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/GlobalSSPI.cs index 78571c5..4ae2a3e 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/GlobalSSPI.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/GlobalSSPI.cs @@ -6,7 +6,7 @@ namespace System.Net { internal static class GlobalSSPI { - internal static readonly SSPIInterface SSPIAuth = new SSPIAuthType(); - internal static readonly SSPIInterface SSPISecureChannel = new SSPISecureChannelType(); + internal static readonly SSPIAuthType SSPIAuth = new SSPIAuthType(); + internal static readonly SSPISecureChannelType SSPISecureChannel = new SSPISecureChannelType(); } } diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs index 34bed7c..dcf6399 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs @@ -161,18 +161,8 @@ internal static partial class Interop [StructLayout(LayoutKind.Sequential)] internal unsafe struct SecPkgContext_IssuerListInfoEx { - public SafeHandle aIssuers; + public IntPtr aIssuers; public uint cIssuers; - - public unsafe SecPkgContext_IssuerListInfoEx(SafeHandle handle, byte[] nativeBuffer) - { - aIssuers = handle; - fixed (byte* voidPtr = nativeBuffer) - { - // TODO (Issue #3114): Properly marshal the struct instead of assuming no padding. - cIssuers = *((uint*)(voidPtr + IntPtr.Size)); - } - } } [StructLayout(LayoutKind.Sequential)] diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/NegotiationInfoClass.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/NegotiationInfoClass.cs index ea6a7d6..ffe7759 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/NegotiationInfoClass.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/NegotiationInfoClass.cs @@ -8,51 +8,50 @@ namespace System.Net { // This class is used to determine if NTLM or // Kerberos are used in the context of a Negotiate handshake - internal partial class NegotiationInfoClass + internal static partial class NegotiationInfoClass { - internal string AuthenticationPackage; - - internal NegotiationInfoClass(SafeHandle safeHandle, int negotiationState) + internal static string GetAuthenticationPackageName(SafeHandle safeHandle, int negotiationState) { if (safeHandle.IsInvalid) { - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Invalid handle:{safeHandle}"); - return; + if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"Invalid handle:{safeHandle}"); + return null; } - IntPtr packageInfo = safeHandle.DangerousGetHandle(); - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"packageInfo:{packageInfo} negotiationState:{negotiationState:x}"); - - if (negotiationState == Interop.SspiCli.SECPKG_NEGOTIATION_COMPLETE - || negotiationState == Interop.SspiCli.SECPKG_NEGOTIATION_OPTIMISTIC) + bool gotRef = false; + try { - string name = null; + safeHandle.DangerousAddRef(ref gotRef); + IntPtr packageInfo = safeHandle.DangerousGetHandle(); + if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"packageInfo:{packageInfo} negotiationState:{negotiationState:x}"); - unsafe + if (negotiationState == Interop.SspiCli.SECPKG_NEGOTIATION_COMPLETE || + negotiationState == Interop.SspiCli.SECPKG_NEGOTIATION_OPTIMISTIC) { - IntPtr unmanagedString = ((SecurityPackageInfo *)packageInfo)->Name; - if (unmanagedString != IntPtr.Zero) + string name; + unsafe { - name = Marshal.PtrToStringUni(unmanagedString); + name = Marshal.PtrToStringUni(((SecurityPackageInfo*)packageInfo)->Name); } - } - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"packageInfo:{packageInfo} negotiationState:{negotiationState:x} name:{name}"); + if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"packageInfo:{packageInfo} negotiationState:{negotiationState:x} name:{name}"); - // An optimization for future string comparisons. - if (string.Equals(name, Kerberos, StringComparison.OrdinalIgnoreCase)) - { - AuthenticationPackage = Kerberos; + // An optimization for future string comparisons. + return + string.Equals(name, Kerberos, StringComparison.OrdinalIgnoreCase) ? Kerberos : + string.Equals(name, NTLM, StringComparison.OrdinalIgnoreCase) ? NTLM : + name; } - else if (string.Equals(name, NTLM, StringComparison.OrdinalIgnoreCase)) - { - AuthenticationPackage = NTLM; - } - else + } + finally + { + if (gotRef) { - AuthenticationPackage = name; + safeHandle.DangerousRelease(); } } + + return null; } } } diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIAuthType.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIAuthType.cs index c92dd85..0083e56 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIAuthType.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIAuthType.cs @@ -45,19 +45,14 @@ namespace System.Net return SafeFreeCredentials.AcquireCredentialsHandle(moduleName, usage, ref authdata, out outCredential); } - public int AcceptSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) + public int AcceptSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, ReadOnlySpan inputBuffers, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) { - return SafeDeleteContext.AcceptSecurityContext(ref credential, ref context, inFlags, endianness, null, inputBuffers, outputBuffer, ref outFlags); + return SafeDeleteContext.AcceptSecurityContext(ref credential, ref context, inFlags, endianness, inputBuffers, ref outputBuffer, ref outFlags); } - public int InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) + public int InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, ReadOnlySpan inputBuffers, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) { - return SafeDeleteContext.InitializeSecurityContext(ref credential, ref context, targetName, inFlags, endianness, inputBuffer, null, outputBuffer, ref outFlags); - } - - public int InitializeSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) - { - return SafeDeleteContext.InitializeSecurityContext(ref credential, ref context, targetName, inFlags, endianness, null, inputBuffers, outputBuffer, ref outFlags); + return SafeDeleteContext.InitializeSecurityContext(ref credential, ref context, targetName, inFlags, endianness, inputBuffers, ref outputBuffer, ref outFlags); } public int EncryptMessage(SafeDeleteContext context, ref Interop.SspiCli.SecBufferDesc inputOutput, uint sequenceNumber) @@ -135,11 +130,10 @@ namespace System.Net public int QueryContextChannelBinding(SafeDeleteContext context, Interop.SspiCli.ContextAttribute attribute, out SafeFreeContextBufferChannelBinding binding) { // Querying an auth SSP for a CBT is not supported. - binding = null; throw new NotSupportedException(); } - public unsafe int QueryContextAttributes(SafeDeleteContext context, Interop.SspiCli.ContextAttribute attribute, byte[] buffer, Type handleType, out SafeHandle refHandle) + public unsafe int QueryContextAttributes(SafeDeleteContext context, Interop.SspiCli.ContextAttribute attribute, Span buffer, Type handleType, out SafeHandle refHandle) { refHandle = null; if (handleType != null) @@ -169,15 +163,13 @@ namespace System.Net return GetSecurityContextToken(phContext, out phToken); } - public int CompleteAuthToken(ref SafeDeleteContext refContext, SecurityBuffer[] inputBuffers) + public int CompleteAuthToken(ref SafeDeleteContext refContext, in SecurityBuffer inputBuffer) { - return SafeDeleteContext.CompleteAuthToken(ref refContext, inputBuffers); + return SafeDeleteContext.CompleteAuthToken(ref refContext, in inputBuffer); } private static int GetSecurityContextToken(SafeDeleteContext phContext, out SecurityContextTokenHandle safeHandle) { - safeHandle = null; - try { bool ignore = false; @@ -190,7 +182,7 @@ namespace System.Net } } - public int ApplyControlToken(ref SafeDeleteContext refContext, SecurityBuffer[] inputBuffers) + public int ApplyControlToken(ref SafeDeleteContext refContext, in SecurityBuffer inputBuffers) { throw new NotSupportedException(); } diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIInterface.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIInterface.cs index 655dcec..7818332 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIInterface.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIInterface.cs @@ -13,20 +13,19 @@ namespace System.Net SecurityPackageInfoClass[] SecurityPackages { get; set; } int EnumerateSecurityPackages(out int pkgnum, out SafeFreeContextBuffer pkgArray); int AcquireCredentialsHandle(string moduleName, Interop.SspiCli.CredentialUse usage, ref SafeSspiAuthDataHandle authdata, out SafeFreeCredentials outCredential); - int AcquireDefaultCredential(string moduleName, Interop.SspiCli.CredentialUse usage, out SafeFreeCredentials outCredential); int AcquireCredentialsHandle(string moduleName, Interop.SspiCli.CredentialUse usage, ref Interop.SspiCli.SCHANNEL_CRED authdata, out SafeFreeCredentials outCredential); - int AcceptSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags); - int InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags); - int InitializeSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags); + int AcquireDefaultCredential(string moduleName, Interop.SspiCli.CredentialUse usage, out SafeFreeCredentials outCredential); + int AcceptSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, ReadOnlySpan inputBuffers, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags); + int InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, ReadOnlySpan inputBuffers, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags); int EncryptMessage(SafeDeleteContext context, ref Interop.SspiCli.SecBufferDesc inputOutput, uint sequenceNumber); int DecryptMessage(SafeDeleteContext context, ref Interop.SspiCli.SecBufferDesc inputOutput, uint sequenceNumber); int MakeSignature(SafeDeleteContext context, ref Interop.SspiCli.SecBufferDesc inputOutput, uint sequenceNumber); int VerifySignature(SafeDeleteContext context, ref Interop.SspiCli.SecBufferDesc inputOutput, uint sequenceNumber); int QueryContextChannelBinding(SafeDeleteContext phContext, Interop.SspiCli.ContextAttribute attribute, out SafeFreeContextBufferChannelBinding refHandle); - int QueryContextAttributes(SafeDeleteContext phContext, Interop.SspiCli.ContextAttribute attribute, byte[] buffer, Type handleType, out SafeHandle refHandle); + int QueryContextAttributes(SafeDeleteContext phContext, Interop.SspiCli.ContextAttribute attribute, Span buffer, Type handleType, out SafeHandle refHandle); int QuerySecurityContextToken(SafeDeleteContext phContext, out SecurityContextTokenHandle phToken); - int CompleteAuthToken(ref SafeDeleteContext refContext, SecurityBuffer[] inputBuffers); - int ApplyControlToken(ref SafeDeleteContext refContext, SecurityBuffer[] inputBuffers); + int CompleteAuthToken(ref SafeDeleteContext refContext, in SecurityBuffer inputBuffer); + int ApplyControlToken(ref SafeDeleteContext refContext, in SecurityBuffer inputBuffer); } } diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SSPISecureChannelType.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SSPISecureChannelType.cs index 84ba37b..58e7496 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SSPISecureChannelType.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SSPISecureChannelType.cs @@ -45,24 +45,14 @@ namespace System.Net return SafeFreeCredentials.AcquireCredentialsHandle(moduleName, usage, ref authdata, out outCredential); } - public int AcceptSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, SecurityBuffer inputBuffer, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) + public int AcceptSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, ReadOnlySpan inputBuffers, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) { - return SafeDeleteContext.AcceptSecurityContext(ref credential, ref context, inFlags, endianness, inputBuffer, null, outputBuffer, ref outFlags); + return SafeDeleteContext.AcceptSecurityContext(ref credential, ref context, inFlags, endianness, inputBuffers, ref outputBuffer, ref outFlags); } - public int AcceptSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) + public int InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, ReadOnlySpan inputBuffers, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) { - return SafeDeleteContext.AcceptSecurityContext(ref credential, ref context, inFlags, endianness, null, inputBuffers, outputBuffer, ref outFlags); - } - - public int InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) - { - return SafeDeleteContext.InitializeSecurityContext(ref credential, ref context, targetName, inFlags, endianness, inputBuffer, null, outputBuffer, ref outFlags); - } - - public int InitializeSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) - { - return SafeDeleteContext.InitializeSecurityContext(ref credential, ref context, targetName, inFlags, endianness, null, inputBuffers, outputBuffer, ref outFlags); + return SafeDeleteContext.InitializeSecurityContext(ref credential, ref context, targetName, inFlags, endianness, inputBuffers, ref outputBuffer, ref outFlags); } public int EncryptMessage(SafeDeleteContext context, ref Interop.SspiCli.SecBufferDesc inputOutput, uint sequenceNumber) @@ -113,7 +103,7 @@ namespace System.Net return SafeFreeContextBufferChannelBinding.QueryContextChannelBinding(phContext, attribute, &bindings, refHandle); } - public unsafe int QueryContextAttributes(SafeDeleteContext phContext, Interop.SspiCli.ContextAttribute attribute, byte[] buffer, Type handleType, out SafeHandle refHandle) + public unsafe int QueryContextAttributes(SafeDeleteContext phContext, Interop.SspiCli.ContextAttribute attribute, Span buffer, Type handleType, out SafeHandle refHandle) { refHandle = null; if (handleType != null) @@ -142,14 +132,14 @@ namespace System.Net throw new NotSupportedException(); } - public int CompleteAuthToken(ref SafeDeleteContext refContext, SecurityBuffer[] inputBuffers) + public int CompleteAuthToken(ref SafeDeleteContext refContext, in SecurityBuffer inputBuffer) { throw new NotSupportedException(); } - public int ApplyControlToken(ref SafeDeleteContext refContext, SecurityBuffer[] inputBuffers) + public int ApplyControlToken(ref SafeDeleteContext refContext, in SecurityBuffer inputBuffer) { - return SafeDeleteContext.ApplyControlToken(ref refContext, inputBuffers); + return SafeDeleteContext.ApplyControlToken(ref refContext, in inputBuffer); } } } diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIWrapper.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIWrapper.cs index 5ad4730..a09e749 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIWrapper.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIWrapper.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; +using System.Diagnostics; using System.Globalization; using System.Net.Security; using System.Runtime.InteropServices; @@ -45,10 +46,7 @@ namespace System.Net } finally { - if (arrayBaseHandle != null) - { - arrayBaseHandle.Dispose(); - } + arrayBaseHandle?.Dispose(); } } } @@ -142,51 +140,40 @@ namespace System.Net return outCredential; } - internal static int InitializeSecurityContext(SSPIInterface secModule, ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) + internal static int InitializeSecurityContext(SSPIInterface secModule, ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, ReadOnlySpan inputBuffers, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) { if (NetEventSource.IsEnabled) NetEventSource.Log.InitializeSecurityContext(credential, context, targetName, inFlags); - int errorCode = secModule.InitializeSecurityContext(ref credential, ref context, targetName, inFlags, datarep, inputBuffer, outputBuffer, ref outFlags); + int errorCode = secModule.InitializeSecurityContext(ref credential, ref context, targetName, inFlags, datarep, inputBuffers, ref outputBuffer, ref outFlags); - if (NetEventSource.IsEnabled) NetEventSource.Log.SecurityContextInputBuffer(nameof(InitializeSecurityContext), inputBuffer?.size ?? 0, outputBuffer.size, (Interop.SECURITY_STATUS)errorCode); + if (NetEventSource.IsEnabled) NetEventSource.Log.SecurityContextInputBuffers(nameof(InitializeSecurityContext), inputBuffers.Length, outputBuffer.size, (Interop.SECURITY_STATUS)errorCode); return errorCode; } - internal static int InitializeSecurityContext(SSPIInterface secModule, SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) - { - if (NetEventSource.IsEnabled) NetEventSource.Log.InitializeSecurityContext(credential, context, targetName, inFlags); - - int errorCode = secModule.InitializeSecurityContext(credential, ref context, targetName, inFlags, datarep, inputBuffers, outputBuffer, ref outFlags); - - if (NetEventSource.IsEnabled) NetEventSource.Log.SecurityContextInputBuffers(nameof(InitializeSecurityContext), inputBuffers?.Length ?? 0, outputBuffer.size, (Interop.SECURITY_STATUS)errorCode); - - return errorCode; - } - - internal static int AcceptSecurityContext(SSPIInterface secModule, SafeFreeCredentials credential, ref SafeDeleteContext context, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) + internal static int AcceptSecurityContext(SSPIInterface secModule, SafeFreeCredentials credential, ref SafeDeleteContext context, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness datarep, ReadOnlySpan inputBuffers, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags) { if (NetEventSource.IsEnabled) NetEventSource.Log.AcceptSecurityContext(credential, context, inFlags); - int errorCode = secModule.AcceptSecurityContext(credential, ref context, inputBuffers, inFlags, datarep, outputBuffer, ref outFlags); + int errorCode = secModule.AcceptSecurityContext(credential, ref context, inputBuffers, inFlags, datarep, ref outputBuffer, ref outFlags); - if (NetEventSource.IsEnabled) NetEventSource.Log.SecurityContextInputBuffers(nameof(AcceptSecurityContext), inputBuffers?.Length ?? 0, outputBuffer.size, (Interop.SECURITY_STATUS)errorCode); + if (NetEventSource.IsEnabled) NetEventSource.Log.SecurityContextInputBuffers(nameof(AcceptSecurityContext), inputBuffers.Length, outputBuffer.size, (Interop.SECURITY_STATUS)errorCode); return errorCode; } - internal static int CompleteAuthToken(SSPIInterface secModule, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers) + internal static int CompleteAuthToken(SSPIInterface secModule, ref SafeDeleteContext context, in SecurityBuffer inputBuffer) { - int errorCode = secModule.CompleteAuthToken(ref context, inputBuffers); + int errorCode = secModule.CompleteAuthToken(ref context, in inputBuffer); if (NetEventSource.IsEnabled) NetEventSource.Log.OperationReturnedSomething(nameof(CompleteAuthToken), (Interop.SECURITY_STATUS)errorCode); return errorCode; } - internal static int ApplyControlToken(SSPIInterface secModule, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers) + internal static int ApplyControlToken(SSPIInterface secModule, ref SafeDeleteContext context, in SecurityBuffer inputBuffer) { - int errorCode = secModule.ApplyControlToken(ref context, inputBuffers); + int errorCode = secModule.ApplyControlToken(ref context, in inputBuffer); if (NetEventSource.IsEnabled) NetEventSource.Log.OperationReturnedSomething(nameof(ApplyControlToken), (Interop.SECURITY_STATUS)errorCode); @@ -198,22 +185,22 @@ namespace System.Net return secModule.QuerySecurityContextToken(context, out token); } - public static int EncryptMessage(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber) + public static int EncryptMessage(SSPIInterface secModule, SafeDeleteContext context, Span input, uint sequenceNumber) { return EncryptDecryptHelper(OP.Encrypt, secModule, context, input, sequenceNumber); } - public static int DecryptMessage(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber) + public static int DecryptMessage(SSPIInterface secModule, SafeDeleteContext context, Span input, uint sequenceNumber) { return EncryptDecryptHelper(OP.Decrypt, secModule, context, input, sequenceNumber); } - internal static int MakeSignature(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber) + internal static int MakeSignature(SSPIInterface secModule, SafeDeleteContext context, Span input, uint sequenceNumber) { return EncryptDecryptHelper(OP.MakeSignature, secModule, context, input, sequenceNumber); } - public static int VerifySignature(SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber) + public static int VerifySignature(SSPIInterface secModule, SafeDeleteContext context, Span input, uint sequenceNumber) { return EncryptDecryptHelper(OP.VerifySignature, secModule, context, input, sequenceNumber); } @@ -226,21 +213,23 @@ namespace System.Net VerifySignature } - private static unsafe int EncryptDecryptHelper(OP op, SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber) + private static unsafe int EncryptDecryptHelper(OP op, SSPIInterface secModule, SafeDeleteContext context, Span input, uint sequenceNumber) { Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(input.Length); - var unmanagedBuffer = new Interop.SspiCli.SecBuffer[input.Length]; + Span unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[input.Length]; + unmanagedBuffer.Clear(); fixed (Interop.SspiCli.SecBuffer* unmanagedBufferPtr = unmanagedBuffer) { sdcInOut.pBuffers = unmanagedBufferPtr; - GCHandle[] pinnedBuffers = new GCHandle[input.Length]; + Span pinnedBuffers = stackalloc GCHandle[input.Length]; + pinnedBuffers.Clear(); byte[][] buffers = new byte[input.Length][]; try { for (int i = 0; i < input.Length; i++) { - SecurityBuffer iBuffer = input[i]; + ref readonly SecurityBuffer iBuffer = ref input[i]; unmanagedBuffer[i].cbBuffer = iBuffer.size; unmanagedBuffer[i].BufferType = iBuffer.type; if (iBuffer.token == null || iBuffer.token.Length == 0) @@ -283,7 +272,7 @@ namespace System.Net // Marshalling back returned sizes / data. for (int i = 0; i < input.Length; i++) { - SecurityBuffer iBuffer = input[i]; + ref SecurityBuffer iBuffer = ref input[i]; iBuffer.size = unmanagedBuffer[i].cbBuffer; iBuffer.type = unmanagedBuffer[i].BufferType; @@ -380,159 +369,148 @@ namespace System.Net return result; } - public static object QueryContextAttributes(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute) - { - int errorCode; - return QueryContextAttributes(secModule, securityContext, contextAttribute, out errorCode); - } - - public static object QueryContextAttributes(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute, out int errorCode) + public static bool QueryBlittableContextAttributes(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute, ref T attribute) where T : unmanaged { if (NetEventSource.IsEnabled) NetEventSource.Enter(null, contextAttribute); - int nativeBlockSize = IntPtr.Size; - Type handleType = null; - - switch (contextAttribute) + Span span = +#if netstandard + stackalloc T[1] { attribute }; +#else + MemoryMarshal.CreateSpan(ref attribute, 1); +#endif + int errorCode = secModule.QueryContextAttributes( + securityContext, contextAttribute, + MemoryMarshal.AsBytes(span), + null, + out SafeHandle sspiHandle); +#if netstandard + attribute = span[0]; +#endif + + using (sspiHandle) { - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES: - nativeBlockSize = SecPkgContext_Sizes.SizeOf; - break; - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_STREAM_SIZES: - nativeBlockSize = SecPkgContext_StreamSizes.SizeOf; - break; - - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NAMES: - handleType = typeof(SafeFreeContextBuffer); - break; - - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_PACKAGE_INFO: - handleType = typeof(SafeFreeContextBuffer); - break; - - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NEGOTIATION_INFO: - handleType = typeof(SafeFreeContextBuffer); - unsafe - { - nativeBlockSize = sizeof(SecPkgContext_NegotiationInfoW); - } - break; - - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CLIENT_SPECIFIED_TARGET: - handleType = typeof(SafeFreeContextBuffer); - break; + if (errorCode != 0) + { + if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"ERROR = {ErrorDescription(errorCode)}"); + return false; + } - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_REMOTE_CERT_CONTEXT: - handleType = typeof(SafeFreeCertContext); - break; + if (NetEventSource.IsEnabled) NetEventSource.Exit(null, attribute); + return true; + } + } - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_LOCAL_CERT_CONTEXT: - handleType = typeof(SafeFreeCertContext); - break; + public static bool QueryBlittableContextAttributes(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute, Type safeHandleType, out SafeHandle sspiHandle, ref T attribute) where T : unmanaged + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(null, contextAttribute); - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_ISSUER_LIST_EX: - nativeBlockSize = Marshal.SizeOf(); - handleType = typeof(SafeFreeContextBuffer); - break; + Span span = +#if netstandard + stackalloc T[1] { attribute }; +#else + MemoryMarshal.CreateSpan(ref attribute, 1); +#endif + int errorCode = secModule.QueryContextAttributes( + securityContext, contextAttribute, + MemoryMarshal.AsBytes(span), + safeHandleType, + out sspiHandle); +#if netstandard + attribute = span[0]; +#endif - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CONNECTION_INFO: - nativeBlockSize = Marshal.SizeOf(); - break; + if (errorCode != 0) + { + if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"ERROR = {ErrorDescription(errorCode)}"); + return false; + } - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_APPLICATION_PROTOCOL: - nativeBlockSize = Marshal.SizeOf(); - break; + if (NetEventSource.IsEnabled) NetEventSource.Exit(null, attribute); + return true; + } - default: - throw new ArgumentException(SR.Format(SR.net_invalid_enum, nameof(contextAttribute)), nameof(contextAttribute)); - } + public static string QueryStringContextAttributes(SSPIInterface secModule, SafeDeleteContext securityContext, Interop.SspiCli.ContextAttribute contextAttribute) + { + Debug.Assert( + contextAttribute == Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NAMES || + contextAttribute == Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CLIENT_SPECIFIED_TARGET); - SafeHandle sspiHandle = null; - object attribute = null; + if (NetEventSource.IsEnabled) NetEventSource.Enter(null, contextAttribute); - try + Span buffer = stackalloc IntPtr[1]; + int errorCode = secModule.QueryContextAttributes( + securityContext, + contextAttribute, + MemoryMarshal.AsBytes(buffer), + typeof(SafeFreeContextBuffer), + out SafeHandle sspiHandle); + using (sspiHandle) { - var nativeBuffer = new byte[nativeBlockSize]; - errorCode = secModule.QueryContextAttributes(securityContext, contextAttribute, nativeBuffer, handleType, out sspiHandle); if (errorCode != 0) { if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"ERROR = {ErrorDescription(errorCode)}"); return null; } - switch (contextAttribute) - { - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES: - attribute = new SecPkgContext_Sizes(nativeBuffer); - break; + string result = Marshal.PtrToStringUni(sspiHandle.DangerousGetHandle()); + if (NetEventSource.IsEnabled) NetEventSource.Exit(null, result); + return result; + } + } - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_STREAM_SIZES: - attribute = new SecPkgContext_StreamSizes(nativeBuffer); - break; + public static SafeFreeCertContext QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CONTEXT(SSPIInterface secModule, SafeDeleteContext securityContext) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(null); - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NAMES: - attribute = Marshal.PtrToStringUni(sspiHandle.DangerousGetHandle()); - break; + Span buffer = stackalloc IntPtr[1]; + int errorCode = secModule.QueryContextAttributes( + securityContext, + Interop.SspiCli.ContextAttribute.SECPKG_ATTR_REMOTE_CERT_CONTEXT, + MemoryMarshal.AsBytes(buffer), + typeof(SafeFreeCertContext), + out SafeHandle sspiHandle); - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_PACKAGE_INFO: - attribute = new SecurityPackageInfoClass(sspiHandle, 0); - break; + if (errorCode != 0) + { + sspiHandle?.Dispose(); + if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"ERROR = {ErrorDescription(errorCode)}"); + return null; + } - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NEGOTIATION_INFO: - unsafe - { - fixed (void* ptr = &nativeBuffer[0]) - { - attribute = new NegotiationInfoClass(sspiHandle, (int)((SecPkgContext_NegotiationInfoW*)ptr)->NegotiationState); - } - } - break; - - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CLIENT_SPECIFIED_TARGET: - attribute = Marshal.PtrToStringUni(sspiHandle.DangerousGetHandle()); - break; - - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_LOCAL_CERT_CONTEXT: - // Fall-through to RemoteCertificate is intentional. - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_REMOTE_CERT_CONTEXT: - attribute = sspiHandle; - sspiHandle = null; - break; - - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_ISSUER_LIST_EX: - attribute = new Interop.SspiCli.SecPkgContext_IssuerListInfoEx(sspiHandle, nativeBuffer); - sspiHandle = null; - break; - - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CONNECTION_INFO: - attribute = new SecPkgContext_ConnectionInfo(nativeBuffer); - break; - - case Interop.SspiCli.ContextAttribute.SECPKG_ATTR_APPLICATION_PROTOCOL: - unsafe - { - fixed (void *ptr = nativeBuffer) - { - attribute = Marshal.PtrToStructure(new IntPtr(ptr)); - } - } - break; + var result = (SafeFreeCertContext)sspiHandle; + if (NetEventSource.IsEnabled) NetEventSource.Exit(null, result); + return result; + } - default: - // Will return null. - break; - } - } - finally + public static bool QueryContextAttributes_SECPKG_ATTR_ISSUER_LIST_EX(SSPIInterface secModule, SafeDeleteContext securityContext, ref Interop.SspiCli.SecPkgContext_IssuerListInfoEx ctx, out SafeHandle sspiHandle) + { + if (NetEventSource.IsEnabled) NetEventSource.Enter(null); + + Span buffer = +#if netstandard + stackalloc Interop.SspiCli.SecPkgContext_IssuerListInfoEx[1] { ctx }; +#else + MemoryMarshal.CreateSpan(ref ctx, 1); +#endif + int errorCode = secModule.QueryContextAttributes( + securityContext, + Interop.SspiCli.ContextAttribute.SECPKG_ATTR_ISSUER_LIST_EX, + MemoryMarshal.AsBytes(buffer), + typeof(SafeFreeContextBuffer), + out sspiHandle); +#if netstandard + ctx = buffer[0]; +#endif + + if (errorCode != 0) { - if (sspiHandle != null) - { - sspiHandle.Dispose(); - } + if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"ERROR = {ErrorDescription(errorCode)}"); + return false; } - if (NetEventSource.IsEnabled) NetEventSource.Exit(null, attribute); - return attribute; + if (NetEventSource.IsEnabled) NetEventSource.Exit(null, ctx); + return true; } public static string ErrorDescription(int errorCode) diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SecPkgContext_Sizes.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SecPkgContext_Sizes.cs index 48cdd54..9dfaeb4 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SecPkgContext_Sizes.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SecPkgContext_Sizes.cs @@ -2,41 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.Runtime.InteropServices; namespace System.Net { // sspi.h [StructLayout(LayoutKind.Sequential)] - internal class SecPkgContext_Sizes + internal struct SecPkgContext_Sizes { public readonly int cbMaxToken; public readonly int cbMaxSignature; public readonly int cbBlockSize; public readonly int cbSecurityTrailer; - - internal unsafe SecPkgContext_Sizes(byte[] memory) - { - fixed (void* voidPtr = memory) - { - IntPtr unmanagedAddress = new IntPtr(voidPtr); - try - { - // TODO (Issue #3114): replace with Marshal.PtrToStructure. - cbMaxToken = (int)checked((uint)Marshal.ReadInt32(unmanagedAddress)); - cbMaxSignature = (int)checked((uint)Marshal.ReadInt32(unmanagedAddress, 4)); - cbBlockSize = (int)checked((uint)Marshal.ReadInt32(unmanagedAddress, 8)); - cbSecurityTrailer = (int)checked((uint)Marshal.ReadInt32(unmanagedAddress, 12)); - } - catch (OverflowException) - { - NetEventSource.Fail(this, "Negative size."); - throw; - } - } - } - - public static readonly int SizeOf = Marshal.SizeOf(); } } diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SecPkgContext_StreamSizes.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SecPkgContext_StreamSizes.cs index 519b607..cf315de 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SecPkgContext_StreamSizes.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SecPkgContext_StreamSizes.cs @@ -2,43 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.Runtime.InteropServices; namespace System.Net { // sspi.h [StructLayout(LayoutKind.Sequential)] - internal class SecPkgContext_StreamSizes + internal struct SecPkgContext_StreamSizes { public int cbHeader; public int cbTrailer; public int cbMaximumMessage; public int cBuffers; public int cbBlockSize; - - internal unsafe SecPkgContext_StreamSizes(byte[] memory) - { - fixed (void* voidPtr = memory) - { - var unmanagedAddress = new IntPtr(voidPtr); - try - { - // TODO (Issue #3114): replace with Marshal.PtrToStructure. - cbHeader = (int)checked((uint)Marshal.ReadInt32(unmanagedAddress)); - cbTrailer = (int)checked((uint)Marshal.ReadInt32(unmanagedAddress, 4)); - cbMaximumMessage = (int)checked((uint)Marshal.ReadInt32(unmanagedAddress, 8)); - cBuffers = (int)checked((uint)Marshal.ReadInt32(unmanagedAddress, 12)); - cbBlockSize = (int)checked((uint)Marshal.ReadInt32(unmanagedAddress, 16)); - } - catch (OverflowException) - { - NetEventSource.Fail(this, "Negative size."); - throw; - } - } - } - - public static readonly int SizeOf = Marshal.SizeOf(); } } diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs index c8359ff..ae8a526 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs @@ -2,12 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Win32.SafeHandles; - using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; using System.Security.Authentication.ExtendedProtection; +using Microsoft.Win32.SafeHandles; namespace System.Net.Security { @@ -56,9 +55,9 @@ namespace System.Net.Security res = Interop.SspiCli.EnumerateSecurityPackagesW(out pkgnum, out pkgArray_SECURITY); pkgArray = pkgArray_SECURITY; - if (res != 0 && pkgArray != null) + if (res != 0) { - pkgArray.SetHandleAsInvalid(); + pkgArray?.SetHandleAsInvalid(); } return res; @@ -103,9 +102,9 @@ namespace System.Net.Security } } - if (status != 0 && refHandle != null) + if (status != 0) { - refHandle.SetHandleAsInvalid(); + refHandle?.SetHandleAsInvalid(); } return status; @@ -361,11 +360,7 @@ namespace System.Net.Security protected override bool ReleaseHandle() { SafeFreeCredentials target = Target; - if (target != null) - { - target.DangerousRelease(); - } - + target?.DangerousRelease(); Target = null; return true; } @@ -392,7 +387,6 @@ namespace System.Net.Security { #endif private const string dummyStr = " "; - private static readonly byte[] s_dummyBytes = new byte[] { 0 }; private static readonly IdnMapping s_idnMapping = new IdnMapping(); protected SafeFreeCredentials _EffectiveCredential; @@ -404,53 +398,24 @@ namespace System.Net.Security string targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, - SecurityBuffer inSecBuffer, - SecurityBuffer[] inSecBuffers, - SecurityBuffer outSecBuffer, + ReadOnlySpan 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}"); - if (inSecBuffers == null) - { - NetEventSource.Info(null, $"inSecBuffers = (null)"); - } - else - { - NetEventSource.Info(null, $"inSecBuffers = {inSecBuffers}"); - } + NetEventSource.Info(null, $"inSecBuffers.Length = {inSecBuffers.Length}"); } #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 inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Length); Interop.SspiCli.SecBufferDesc outSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1); // Actually, this is returned in outFlags. @@ -464,141 +429,100 @@ namespace System.Net.Security isContextAbsent = refContext._handle.IsZero; } - // 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); - Interop.SspiCli.SecBuffer[] inUnmanagedBuffer = new Interop.SspiCli.SecBuffer[haveInSecurityBufferDescriptor ? inSecurityBufferDescriptor.cBuffers : 1]; + Span inUnmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[inSecurityBufferDescriptor.cBuffers]; + inUnmanagedBuffer.Clear(); + fixed (void* inUnmanagedBufferPtr = inUnmanagedBuffer) + fixed (void* pinnedToken0 = inSecBuffers.Length > 0 ? inSecBuffers[0].token : null) + fixed (void* pinnedToken1 = inSecBuffers.Length > 1 ? inSecBuffers[1].token : null) + fixed (void* pinnedToken2 = inSecBuffers.Length > 2 ? inSecBuffers[2].token : null) // pin all buffers, even if null or not used, to avoid needing to allocate GCHandles { - if (haveInSecurityBufferDescriptor) + Debug.Assert(inSecBuffers.Length <= 3); + + // Fix Descriptor pointer that points to unmanaged SecurityBuffers. + inSecurityBufferDescriptor.pBuffers = inUnmanagedBufferPtr; + for (int index = 0; index < inSecurityBufferDescriptor.cBuffers; ++index) { - // 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); - } + ref readonly SecurityBuffer securityBuffer = ref inSecBuffers[index]; + + // 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. + inUnmanagedBuffer[index].pvBuffer = + securityBuffer.unmanagedToken != null ? securityBuffer.unmanagedToken.DangerousGetHandle() : + securityBuffer.token == null || securityBuffer.token.Length == 0 ? IntPtr.Zero : + Marshal.UnsafeAddrOfPinnedArrayElement(securityBuffer.token, securityBuffer.offset); #if TRACE_VERBOSE - if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"SecBuffer: cbBuffer:{securityBuffer.size} BufferType:{securityBuffer.type}"); + if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"SecBuffer: cbBuffer:{securityBuffer.size} BufferType:{securityBuffer.type}"); #endif - } - } } - Interop.SspiCli.SecBuffer outUnmanagedBuffer = default; - - // Fix Descriptor pointer that points to unmanaged SecurityBuffers. - outSecurityBufferDescriptor.pBuffers = &outUnmanagedBuffer; - outUnmanagedBuffer.cbBuffer = outSecBuffer.size; - outUnmanagedBuffer.BufferType = outSecBuffer.type; - if (outSecBuffer.token == null || outSecBuffer.token.Length == 0) - { - outUnmanagedBuffer.pvBuffer = IntPtr.Zero; - } - else + fixed (byte* pinnedOutBytes = outSecBuffer.token) { - outUnmanagedBuffer.pvBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(outSecBuffer.token, outSecBuffer.offset); - } + // 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 (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 SafeDeleteContext_SECURITY(); - } + 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 SafeDeleteContext_SECURITY(); + } - if (targetName == null || targetName.Length == 0) - { - targetName = dummyStr; - } + 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, - haveInSecurityBufferDescriptor ? &inSecurityBufferDescriptor : null, - refContext, - ref outSecurityBufferDescriptor, - ref outFlags, - outFreeContextBuffer); - } + 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"); + 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; - if (outSecBuffer.size > 0) - { - outSecBuffer.token = new byte[outSecBuffer.size]; - Marshal.Copy(outUnmanagedBuffer.pvBuffer, outSecBuffer.token, 0, outSecBuffer.size); - } - else - { - outSecBuffer.token = null; + // 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*)outUnmanagedBuffer.pvBuffer, outUnmanagedBuffer.cbBuffer).ToArray() : + 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(); - } + outFreeContextBuffer?.Dispose(); } if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"errorCode:0x{errorCode:x8}, refContext:{refContext}"); @@ -667,11 +591,7 @@ namespace System.Net.Security if (outContext._EffectiveCredential != inCredentials && (errorCode & 0x80000000) == 0) { // Disassociate the previous credential handle - if (outContext._EffectiveCredential != null) - { - outContext._EffectiveCredential.DangerousRelease(); - } - + outContext._EffectiveCredential?.DangerousRelease(); outContext._EffectiveCredential = inCredentials; } else @@ -708,53 +628,24 @@ namespace System.Net.Security ref SafeDeleteContext refContext, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, - SecurityBuffer inSecBuffer, - SecurityBuffer[] inSecBuffers, - SecurityBuffer outSecBuffer, + ReadOnlySpan inSecBuffers, + ref 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)"); - } + NetEventSource.Info(null, $"inSecBuffers.Length = {inSecBuffers.Length}"); } #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 inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Length); Interop.SspiCli.SecBufferDesc outSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1); // Actually, this is returned in outFlags. @@ -768,71 +659,52 @@ namespace System.Net.Security isContextAbsent = refContext._handle.IsZero; } - // 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]; + Span inUnmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[inSecurityBufferDescriptor.cBuffers]; + inUnmanagedBuffer.Clear(); + fixed (void* inUnmanagedBufferPtr = inUnmanagedBuffer) + fixed (void* pinnedToken0 = inSecBuffers.Length > 0 ? inSecBuffers[0].token : null) + fixed (void* pinnedToken1 = inSecBuffers.Length > 1 ? inSecBuffers[1].token : null) + fixed (void* pinnedToken2 = inSecBuffers.Length > 2 ? inSecBuffers[2].token : null) // pin all buffers, even if null or not used, to avoid needing to allocate GCHandles { - if (haveInSecurityBufferDescriptor) + Debug.Assert(inSecBuffers.Length <= 3); + + // Fix Descriptor pointer that points to unmanaged SecurityBuffers. + inSecurityBufferDescriptor.pBuffers = inUnmanagedBufferPtr; + for (int index = 0; index < inSecurityBufferDescriptor.cBuffers; ++index) { - // 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); - } + ref readonly SecurityBuffer securityBuffer = ref inSecBuffers[index]; + + // 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. + inUnmanagedBuffer[index].pvBuffer = + securityBuffer.unmanagedToken != null ? securityBuffer.unmanagedToken.DangerousGetHandle() : + securityBuffer.token == null || securityBuffer.token.Length == 0 ? IntPtr.Zero : + Marshal.UnsafeAddrOfPinnedArrayElement(securityBuffer.token, securityBuffer.offset); #if TRACE_VERBOSE - if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"SecBuffer: cbBuffer:{securityBuffer.size} BufferType:{securityBuffer.type}"); + 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[0]) + 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; + Interop.SspiCli.SecBuffer outUnmanagedBuffer = default; + outSecurityBufferDescriptor.pBuffers = &outUnmanagedBuffer; - if (outSecBuffer.token == null || outSecBuffer.token.Length == 0) - { - outUnmanagedBuffer[0].pvBuffer = IntPtr.Zero; - } - else - { - outUnmanagedBuffer[0].pvBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(outSecBuffer.token, outSecBuffer.offset); - } + // Copy the SecurityBuffer content into unmanaged place holder. + 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) { @@ -851,7 +723,7 @@ namespace System.Net.Security errorCode = MustRunAcceptSecurityContext_SECURITY( ref inCredentials, isContextAbsent, - haveInSecurityBufferDescriptor ? &inSecurityBufferDescriptor : null, + &inSecurityBufferDescriptor, inFlags, endianness, refContext, @@ -862,42 +734,17 @@ namespace System.Net.Security 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; - } + outSecBuffer.size = outUnmanagedBuffer.cbBuffer; + outSecBuffer.type = outUnmanagedBuffer.BufferType; + outSecBuffer.token = outUnmanagedBuffer.cbBuffer > 0 ? + new Span((byte*)outUnmanagedBuffer.pvBuffer, outUnmanagedBuffer.cbBuffer).ToArray() : + 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(); - } + outFreeContextBuffer?.Dispose(); } if (NetEventSource.IsEnabled) NetEventSource.Exit(null, $"errorCode:0x{errorCode:x8}, refContext:{refContext}"); @@ -963,11 +810,7 @@ namespace System.Net.Security if (outContext._EffectiveCredential != inCredentials && (errorCode & 0x80000000) == 0) { // Disassociate the previous credential handle. - if (outContext._EffectiveCredential != null) - { - outContext._EffectiveCredential.DangerousRelease(); - } - + outContext._EffectiveCredential?.DangerousRelease(); outContext._EffectiveCredential = inCredentials; } else @@ -1000,101 +843,59 @@ namespace System.Net.Security internal static unsafe int CompleteAuthToken( ref SafeDeleteContext refContext, - SecurityBuffer[] inSecBuffers) + in SecurityBuffer inSecBuffer) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(null, "SafeDeleteContext::CompleteAuthToken"); NetEventSource.Info(null, $" refContext = {refContext}"); - NetEventSource.Info(null, $" inSecBuffers[] = {inSecBuffers}"); + NetEventSource.Info(null, $" inSecBuffer = {inSecBuffer}"); } - if (inSecBuffers == null) - { - NetEventSource.Fail(null, "inSecBuffers == null"); - } - - var inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(inSecBuffers.Length); + var inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1); 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) + Interop.SspiCli.SecBuffer inUnmanagedBuffer = default; + inSecurityBufferDescriptor.pBuffers = &inUnmanagedBuffer; + fixed (byte* pinnedToken = inSecBuffer.token) { - // 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); - } + inUnmanagedBuffer.cbBuffer = inSecBuffer.size; + inUnmanagedBuffer.BufferType = inSecBuffer.type; + + // Use the unmanaged token if it's not null; otherwise use the managed buffer. + inUnmanagedBuffer.pvBuffer = + inSecBuffer.unmanagedToken != null ? inSecBuffer.unmanagedToken.DangerousGetHandle() : + inSecBuffer.token == null || inSecBuffer.token.Length == 0 ? IntPtr.Zero : + (IntPtr)(pinnedToken + inSecBuffer.offset); #if TRACE_VERBOSE - if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"SecBuffer: cbBuffer:{securityBuffer.size} BufferType: {securityBuffer.type}"); + if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"SecBuffer: cbBuffer:{inSecBuffer.size} BufferType: {inSecBuffer.type}"); #endif - } - } - Interop.SspiCli.CredHandle contextHandle = new Interop.SspiCli.CredHandle(); - if (refContext != null) + Interop.SspiCli.CredHandle contextHandle = refContext != null ? refContext._handle : default; + if (refContext == null || refContext.IsInvalid) { - contextHandle = refContext._handle; - } - try - { - if (refContext == null || refContext.IsInvalid) + // Previous versions unconditionally built a new "refContext" here, but would pass + // incorrect arguments to CompleteAuthToken in cases where a nonzero "contextHandle" was + // already present. In these cases, allow the "refContext" to flow through unmodified + // (which will generate an ObjectDisposedException below). In all other cases, continue to + // build a new "refContext" in an attempt to maximize compat. + if (contextHandle.IsZero) { - // Previous versions unconditionally built a new "refContext" here, but would pass - // incorrect arguments to CompleteAuthToken in cases where a nonzero "contextHandle" was - // already present. In these cases, allow the "refContext" to flow through unmodified - // (which will generate an ObjectDisposedException below). In all other cases, continue to - // build a new "refContext" in an attempt to maximize compat. - if (contextHandle.IsZero) - refContext = new SafeDeleteContext_SECURITY(); + refContext = new SafeDeleteContext_SECURITY(); } + } - try - { - bool ignore = false; - refContext.DangerousAddRef(ref ignore); - errorCode = Interop.SspiCli.CompleteAuthToken(contextHandle.IsZero ? null : &contextHandle, ref inSecurityBufferDescriptor); - } - finally - { - refContext.DangerousRelease(); - } + bool gotRef = false; + try + { + refContext.DangerousAddRef(ref gotRef); + errorCode = Interop.SspiCli.CompleteAuthToken(contextHandle.IsZero ? null : &contextHandle, ref inSecurityBufferDescriptor); } finally { - if (pinnedInBytes != null) + if (gotRef) { - for (int index = 0; index < pinnedInBytes.Length; index++) - { - if (pinnedInBytes[index].IsAllocated) - { - pinnedInBytes[index].Free(); - } - } + refContext.DangerousRelease(); } } } @@ -1105,105 +906,61 @@ namespace System.Net.Security internal static unsafe int ApplyControlToken( ref SafeDeleteContext refContext, - SecurityBuffer[] inSecBuffers) + in SecurityBuffer inSecBuffer) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(null); NetEventSource.Info(null, $" refContext = {refContext}"); - NetEventSource.Info(null, $" inSecBuffers[] = length:{inSecBuffers.Length}"); + NetEventSource.Info(null, $" inSecBuffer = {inSecBuffer}"); } - 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. + fixed (byte* pinnedInSecBufferToken = inSecBuffer.token) { - // 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); - } + var inSecurityBufferDescriptor = new Interop.SspiCli.SecBufferDesc(1); + Interop.SspiCli.SecBuffer inUnmanagedBuffer = default; + inSecurityBufferDescriptor.pBuffers = &inUnmanagedBuffer; + inUnmanagedBuffer.cbBuffer = inSecBuffer.size; + inUnmanagedBuffer.BufferType = inSecBuffer.type; + + // Use the unmanaged token if it's not null; otherwise use the managed buffer. + inUnmanagedBuffer.pvBuffer = + inSecBuffer.unmanagedToken != null ? inSecBuffer.unmanagedToken.DangerousGetHandle() : + inSecBuffer.token == null || inSecBuffer.token.Length == 0 ? IntPtr.Zero : + (IntPtr)(pinnedInSecBufferToken + inSecBuffer.offset); #if TRACE_VERBOSE - if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"SecBuffer: cbBuffer:{securityBuffer.size} BufferType:{securityBuffer.type}"); + if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"SecBuffer: cbBuffer:{inSecBuffer.size} BufferType:{inSecBuffer.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; - } + Interop.SspiCli.CredHandle contextHandle = refContext != null ? refContext._handle : default; - try + if (refContext == null || refContext.IsInvalid) { - if (refContext == null || refContext.IsInvalid) + // Previous versions unconditionally built a new "refContext" here, but would pass + // incorrect arguments to ApplyControlToken in cases where a nonzero "contextHandle" was + // already present. In these cases, allow the "refContext" to flow through unmodified + // (which will generate an ObjectDisposedException below). In all other cases, continue to + // build a new "refContext" in an attempt to maximize compat. + if (contextHandle.IsZero) { - // Previous versions unconditionally built a new "refContext" here, but would pass - // incorrect arguments to ApplyControlToken in cases where a nonzero "contextHandle" was - // already present. In these cases, allow the "refContext" to flow through unmodified - // (which will generate an ObjectDisposedException below). In all other cases, continue to - // build a new "refContext" in an attempt to maximize compat. - if (contextHandle.IsZero) - refContext = new SafeDeleteContext_SECURITY(); + 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(); - } + bool gotRef = false; + try + { + refContext.DangerousAddRef(ref gotRef); + errorCode = Interop.SspiCli.ApplyControlToken(contextHandle.IsZero ? null : &contextHandle, ref inSecurityBufferDescriptor); } finally { - if (pinnedInBytes != null) + if (gotRef) { - for (int index = 0; index < pinnedInBytes.Length; index++) - { - if (pinnedInBytes[index].IsAllocated) - { - pinnedInBytes[index].Free(); - } - } + refContext.DangerousRelease(); } } } @@ -1219,11 +976,7 @@ namespace System.Net.Security protected override bool ReleaseHandle() { - if (this._EffectiveCredential != null) - { - this._EffectiveCredential.DangerousRelease(); - } - + this._EffectiveCredential?.DangerousRelease(); return Interop.SspiCli.DeleteSecurityContext(ref _handle) == 0; } } @@ -1282,9 +1035,9 @@ namespace System.Net.Security refHandle._size = (*buffer).BindingsLength; } - if (status != 0 && refHandle != null) + if (status != 0) { - refHandle.SetHandleAsInvalid(); + refHandle?.SetHandleAsInvalid(); } return status; diff --git a/src/libraries/Common/src/System/Net/LazyAsyncResult.cs b/src/libraries/Common/src/System/Net/LazyAsyncResult.cs index 60c2b19..794544c 100644 --- a/src/libraries/Common/src/System/Net/LazyAsyncResult.cs +++ b/src/libraries/Common/src/System/Net/LazyAsyncResult.cs @@ -174,10 +174,7 @@ namespace System.Net { // This should be very rare, but doing this will reduce the chance of deadlock. _event = null; - if (waitHandle != null) - { - waitHandle.Dispose(); - } + waitHandle?.Dispose(); throw; } diff --git a/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs b/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs index 84916e3..64eb87d 100644 --- a/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs +++ b/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs @@ -2,12 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Net.Security; +using System.Runtime.InteropServices; using System.Security.Authentication.ExtendedProtection; -using System.Threading; namespace System.Net { @@ -214,25 +212,7 @@ namespace System.Net { if (NetEventSource.IsEnabled) NetEventSource.Enter(this, incomingBlob); - SecurityBuffer[] inSecurityBufferArray = null; - if (incomingBlob != null && _channelBinding != null) - { - inSecurityBufferArray = new SecurityBuffer[2] - { - new SecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN), - new SecurityBuffer(_channelBinding) - }; - } - else if (incomingBlob != null) - { - inSecurityBufferArray = new SecurityBuffer[1] { new SecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN) }; - } - else if (_channelBinding != null) - { - inSecurityBufferArray = new SecurityBuffer[1] { new SecurityBuffer(_channelBinding) }; - } - - var outSecurityBuffer = new SecurityBuffer(_tokenSize, SecurityBufferType.SECBUFFER_TOKEN); + var result = new byte[_tokenSize]; bool firstTime = _securityContext == null; try @@ -241,26 +221,24 @@ namespace System.Net { // client session statusCode = NegotiateStreamPal.InitializeSecurityContext( - _credentialsHandle, + ref _credentialsHandle, ref _securityContext, _spn, _requestedContextFlags, - inSecurityBufferArray, - outSecurityBuffer, + incomingBlob, + _channelBinding, + ref result, ref _contextFlags); if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"SSPIWrapper.InitializeSecurityContext() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})"); if (statusCode.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded) { - var inSecurityBuffers = new SecurityBuffer[1]; - inSecurityBuffers[0] = outSecurityBuffer; - - statusCode = NegotiateStreamPal.CompleteAuthToken(ref _securityContext, inSecurityBuffers); + statusCode = NegotiateStreamPal.CompleteAuthToken(ref _securityContext, result); if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"SSPIWrapper.CompleteAuthToken() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})"); - outSecurityBuffer.token = null; + result = null; } } else @@ -270,8 +248,9 @@ namespace System.Net _credentialsHandle, ref _securityContext, _requestedContextFlags, - inSecurityBufferArray, - outSecurityBuffer, + incomingBlob, + _channelBinding, + ref result, ref _contextFlags); if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"SSPIWrapper.AcceptSecurityContext() returns statusCode:0x{((int)statusCode.ErrorCode):x8} ({statusCode})"); @@ -285,9 +264,9 @@ namespace System.Net // The real dispose will happen when the security context is closed. // Note if the first call was not successful the handle is physically destroyed here. // - if (firstTime && _credentialsHandle != null) + if (firstTime) { - _credentialsHandle.Dispose(); + _credentialsHandle?.Dispose(); } } @@ -329,7 +308,7 @@ namespace System.Net if (NetEventSource.IsEnabled) NetEventSource.Exit(this, $"IsCompleted: {IsCompleted}"); } - return outSecurityBuffer.token; + return result; } private string GetClientSpecifiedSpn() diff --git a/src/libraries/Common/src/System/Net/NegotiationInfoClass.cs b/src/libraries/Common/src/System/Net/NegotiationInfoClass.cs index 560098e..1e9fcaa 100644 --- a/src/libraries/Common/src/System/Net/NegotiationInfoClass.cs +++ b/src/libraries/Common/src/System/Net/NegotiationInfoClass.cs @@ -6,7 +6,7 @@ namespace System.Net { // This class is used to determine if NTLM or // Kerberos are used in the context of a Negotiate handshake - internal partial class NegotiationInfoClass + internal static partial class NegotiationInfoClass { internal const string NTLM = "NTLM"; internal const string Kerberos = "Kerberos"; diff --git a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 9e55c5f..6981f28 100644 --- a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -98,7 +98,7 @@ namespace System.Net.Security ref SafeGssContextHandle context, SafeGssCredHandle credential, bool isNtlm, - SecurityBuffer cbt, + ChannelBinding channelBinding, SafeGssNameHandle targetName, Interop.NetSecurityNative.GssFlags inFlags, byte[] buffer, @@ -110,12 +110,12 @@ namespace System.Net.Security // to the application specific data. IntPtr cbtAppData = IntPtr.Zero; int cbtAppDataSize = 0; - if (cbt != null) + if (channelBinding != null) { int appDataOffset = Marshal.SizeOf(); - Debug.Assert(appDataOffset < cbt.size); - cbtAppData = cbt.unmanagedToken.DangerousGetHandle() + appDataOffset; - cbtAppDataSize = cbt.size - appDataOffset; + Debug.Assert(appDataOffset < channelBinding.Size); + cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset; + cbtAppDataSize = channelBinding.Size - appDataOffset; } outputBuffer = null; @@ -167,11 +167,11 @@ namespace System.Net.Security private static SecurityStatusPal EstablishSecurityContext( SafeFreeNegoCredentials credential, ref SafeDeleteContext context, - SecurityBuffer cbt, + ChannelBinding channelBinding, string targetName, ContextFlagsPal inFlags, - SecurityBuffer inputBuffer, - SecurityBuffer outputBuffer, + byte[] incomingBlob, + ref byte[] resultBuffer, ref ContextFlagsPal outFlags) { bool isNtlmOnly = credential.IsNtlmOnly; @@ -193,17 +193,15 @@ namespace System.Net.Security ref contextHandle, credential.GssCredential, isNtlmOnly, - cbt, + channelBinding, negoContext.TargetName, inputFlags, - inputBuffer?.token, - out outputBuffer.token, + incomingBlob, + out resultBuffer, out outputFlags, out isNtlmUsed); - Debug.Assert(outputBuffer.token != null, "Unexpected null buffer returned by GssApi"); - outputBuffer.size = outputBuffer.token.Length; - outputBuffer.offset = 0; + Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi"); outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop((Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false); Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext); @@ -221,7 +219,7 @@ namespace System.Net.Security } SecurityStatusPalErrorCode errorCode = done ? - (negoContext.IsNtlmUsed && outputBuffer.size > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) : + (negoContext.IsNtlmUsed && resultBuffer.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) : SecurityStatusPalErrorCode.ContinueNeeded; return new SecurityStatusPal(errorCode); } @@ -233,12 +231,13 @@ namespace System.Net.Security } internal static SecurityStatusPal InitializeSecurityContext( - SafeFreeCredentials credentialsHandle, + ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext securityContext, string spn, ContextFlagsPal requestedContextFlags, - SecurityBuffer[] inSecurityBufferArray, - SecurityBuffer outSecurityBuffer, + byte[] incomingBlob, + ChannelBinding channelBinding, + ref byte[] resultBlob, ref ContextFlagsPal contextFlags) { SafeFreeNegoCredentials negoCredentialsHandle = (SafeFreeNegoCredentials)credentialsHandle; @@ -248,21 +247,14 @@ namespace System.Net.Security throw new PlatformNotSupportedException(SR.net_nego_not_supported_empty_target_with_defaultcreds); } - SecurityBuffer cbtBuffer = null; - if ((inSecurityBufferArray != null) && (inSecurityBufferArray.Length > 1)) - { - Debug.Assert(inSecurityBufferArray[1].type == SecurityBufferType.SECBUFFER_CHANNEL_BINDINGS); - cbtBuffer = inSecurityBufferArray[1]; - } - SecurityStatusPal status = EstablishSecurityContext( negoCredentialsHandle, ref securityContext, - cbtBuffer, + channelBinding, spn, requestedContextFlags, - ((inSecurityBufferArray != null && inSecurityBufferArray.Length != 0) ? inSecurityBufferArray[0] : null), - outSecurityBuffer, + incomingBlob, + ref resultBlob, ref contextFlags); // Confidentiality flag should not be set if not requested @@ -282,8 +274,9 @@ namespace System.Net.Security SafeFreeCredentials credentialsHandle, ref SafeDeleteContext securityContext, ContextFlagsPal requestedContextFlags, - SecurityBuffer[] inSecurityBufferArray, - SecurityBuffer outSecurityBuffer, + byte[] incomingBlob, + ChannelBinding channelBinding, + ref byte[] resultBlob, ref ContextFlagsPal contextFlags) { throw new PlatformNotSupportedException(SR.net_nego_server_not_supported); @@ -335,7 +328,7 @@ namespace System.Net.Security internal static SecurityStatusPal CompleteAuthToken( ref SafeDeleteContext securityContext, - SecurityBuffer[] inSecurityBufferArray) + byte[] incomingBlob) { return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); } diff --git a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs index d65dc85..88af1dd 100644 --- a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs +++ b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs @@ -2,8 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Globalization; using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Security.Authentication.ExtendedProtection; namespace System.Net.Security { @@ -48,57 +51,89 @@ namespace System.Net.Security } finally { - if (authData != null) - { - authData.Dispose(); - } + authData?.Dispose(); } } internal static string QueryContextClientSpecifiedSpn(SafeDeleteContext securityContext) { - return SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CLIENT_SPECIFIED_TARGET) as string; + return SSPIWrapper.QueryStringContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CLIENT_SPECIFIED_TARGET); } internal static string QueryContextAuthenticationPackage(SafeDeleteContext securityContext) { - var negotiationInfoClass = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NEGOTIATION_INFO) as NegotiationInfoClass; - return negotiationInfoClass?.AuthenticationPackage; + 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 SecurityStatusPal InitializeSecurityContext( - SafeFreeCredentials credentialsHandle, + ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext securityContext, string spn, ContextFlagsPal requestedContextFlags, - SecurityBuffer[] inSecurityBufferArray, - SecurityBuffer outSecurityBuffer, + byte[] incomingBlob, + ChannelBinding channelBinding, + ref byte[] resultBlob, ref ContextFlagsPal contextFlags) { +#if netstandard + Span inSecurityBufferSpan = new SecurityBuffer[2]; +#else + TwoSecurityBuffers twoSecurityBuffers = default; + Span inSecurityBufferSpan = MemoryMarshal.CreateSpan(ref twoSecurityBuffers._item0, 2); +#endif + + int inSecurityBufferSpanLength = 0; + if (incomingBlob != null && channelBinding != null) + { + inSecurityBufferSpan[0] = new SecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN); + inSecurityBufferSpan[1] = new SecurityBuffer(channelBinding); + inSecurityBufferSpanLength = 2; + } + else if (incomingBlob != null) + { + inSecurityBufferSpan[0] = new SecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN); + inSecurityBufferSpanLength = 1; + } + else if (channelBinding != null) + { + inSecurityBufferSpan[0] = new SecurityBuffer(channelBinding); + inSecurityBufferSpanLength = 1; + } + inSecurityBufferSpan = inSecurityBufferSpan.Slice(0, inSecurityBufferSpanLength); + + var outSecurityBuffer = new SecurityBuffer(resultBlob, SecurityBufferType.SECBUFFER_TOKEN); + Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero; Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.InitializeSecurityContext( GlobalSSPI.SSPIAuth, - credentialsHandle, + ref credentialsHandle, ref securityContext, spn, ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(requestedContextFlags), Interop.SspiCli.Endianness.SECURITY_NETWORK_DREP, - inSecurityBufferArray, - outSecurityBuffer, + inSecurityBufferSpan, + ref outSecurityBuffer, ref outContextFlags); + resultBlob = outSecurityBuffer.token; contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(outContextFlags); return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus); } internal static SecurityStatusPal CompleteAuthToken( ref SafeDeleteContext securityContext, - SecurityBuffer[] inSecurityBufferArray) + byte[] incomingBlob) { + var inSecurityBuffer = new SecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN); Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.CompleteAuthToken( GlobalSSPI.SSPIAuth, ref securityContext, - inSecurityBufferArray); + in inSecurityBuffer); return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus); } @@ -106,10 +141,39 @@ namespace System.Net.Security SafeFreeCredentials credentialsHandle, ref SafeDeleteContext securityContext, ContextFlagsPal requestedContextFlags, - SecurityBuffer[] inSecurityBufferArray, - SecurityBuffer outSecurityBuffer, + byte[] incomingBlob, + ChannelBinding channelBinding, + ref byte[] resultBlob, ref ContextFlagsPal contextFlags) { +#if netstandard + Span inSecurityBufferSpan = new SecurityBuffer[2]; +#else + TwoSecurityBuffers twoSecurityBuffers = default; + Span inSecurityBufferSpan = MemoryMarshal.CreateSpan(ref twoSecurityBuffers._item0, 2); +#endif + + int inSecurityBufferSpanLength = 0; + if (incomingBlob != null && channelBinding != null) + { + inSecurityBufferSpan[0] = new SecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN); + inSecurityBufferSpan[1] = new SecurityBuffer(channelBinding); + inSecurityBufferSpanLength = 2; + } + else if (incomingBlob != null) + { + inSecurityBufferSpan[0] = new SecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN); + inSecurityBufferSpanLength = 1; + } + else if (channelBinding != null) + { + inSecurityBufferSpan[0] = new SecurityBuffer(channelBinding); + inSecurityBufferSpanLength = 1; + } + inSecurityBufferSpan = inSecurityBufferSpan.Slice(0, inSecurityBufferSpanLength); + + var outSecurityBuffer = new SecurityBuffer(resultBlob, SecurityBufferType.SECBUFFER_TOKEN); + Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero; Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.AcceptSecurityContext( GlobalSSPI.SSPIAuth, @@ -117,10 +181,12 @@ namespace System.Net.Security ref securityContext, ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(requestedContextFlags), Interop.SspiCli.Endianness.SECURITY_NETWORK_DREP, - inSecurityBufferArray, - outSecurityBuffer, + inSecurityBufferSpan, + ref outSecurityBuffer, ref outContextFlags); + resultBlob = outSecurityBuffer.token; + contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(outContextFlags); return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus); } @@ -150,7 +216,12 @@ namespace System.Net.Security // setup security buffers for ssp call // one points at signed data // two will receive payload if signature is valid - SecurityBuffer[] securityBuffer = new SecurityBuffer[2]; +#if netstandard + Span securityBuffer = new SecurityBuffer[2]; +#else + TwoSecurityBuffers stackBuffer = default; + Span securityBuffer = MemoryMarshal.CreateSpan(ref stackBuffer._item0, 2); +#endif securityBuffer[0] = new SecurityBuffer(buffer, offset, count, SecurityBufferType.SECBUFFER_STREAM); securityBuffer[1] = new SecurityBuffer(0, SecurityBufferType.SECBUFFER_DATA); @@ -177,11 +248,9 @@ namespace System.Net.Security internal static int MakeSignature(SafeDeleteContext securityContext, byte[] buffer, int offset, int count, ref byte[] output) { - SecPkgContext_Sizes sizes = SSPIWrapper.QueryContextAttributes( - GlobalSSPI.SSPIAuth, - securityContext, - Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES - ) as SecPkgContext_Sizes; + 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; @@ -194,7 +263,12 @@ namespace System.Net.Security Buffer.BlockCopy(buffer, offset, output, sizes.cbMaxSignature, count); // setup security buffers for ssp call - SecurityBuffer[] securityBuffer = new SecurityBuffer[2]; +#if netstandard + Span securityBuffer = new SecurityBuffer[2]; +#else + TwoSecurityBuffers stackBuffer = default; + Span 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); diff --git a/src/libraries/Common/src/System/Net/Security/SSPIHandleCache.cs b/src/libraries/Common/src/System/Net/Security/SSPIHandleCache.cs index 69a4376..3b44ac0 100644 --- a/src/libraries/Common/src/System/Net/Security/SSPIHandleCache.cs +++ b/src/libraries/Common/src/System/Net/Security/SSPIHandleCache.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.Threading; namespace System.Net.Security @@ -34,10 +33,7 @@ namespace System.Net.Security newRef = Interlocked.Exchange(ref s_cacheSlots[index], newRef); } - if (newRef != null) - { - newRef.Dispose(); - } + newRef?.Dispose(); } catch (Exception e) { diff --git a/src/libraries/Common/src/System/Net/Security/SecurityBuffer.cs b/src/libraries/Common/src/System/Net/Security/SecurityBuffer.Windows.cs similarity index 58% rename from src/libraries/Common/src/System/Net/Security/SecurityBuffer.cs rename to src/libraries/Common/src/System/Net/Security/SecurityBuffer.Windows.cs index b507449..d076942 100644 --- a/src/libraries/Common/src/System/Net/Security/SecurityBuffer.cs +++ b/src/libraries/Common/src/System/Net/Security/SecurityBuffer.Windows.cs @@ -2,61 +2,89 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Authentication.ExtendedProtection; namespace System.Net.Security { - internal class SecurityBuffer + // Until we have stackalloc Span support, these two + // structs allow us to do the equivalent of stackalloc SecurityBuffer[2] + // and stackalloc SecurityBuffer[3], with code like: + // TwoSecurityBuffers tmp = default; + // Span buffers = MemoryMarshal.CreateSpan (data == null ? 0 : data.Length)) { - NetEventSource.Fail(this, $"'offset' out of range. [{offset}]"); + NetEventSource.Fail(typeof(SecurityBuffer), $"'offset' out of range. [{offset}]"); } if (size < 0 || size > (data == null ? 0 : data.Length - offset)) { - NetEventSource.Fail(this, $"'size' out of range. [{size}]"); + NetEventSource.Fail(typeof(SecurityBuffer), $"'size' out of range. [{size}]"); } this.offset = data == null || offset < 0 ? 0 : Math.Min(offset, data.Length); this.size = data == null || size < 0 ? 0 : Math.Min(size, data.Length - this.offset); this.type = tokentype; this.token = size == 0 ? null : data; + this.unmanagedToken = null; } public SecurityBuffer(byte[] data, SecurityBufferType tokentype) { + this.offset = 0; this.size = data == null ? 0 : data.Length; this.type = tokentype; this.token = size == 0 ? null : data; + this.unmanagedToken = null; } public SecurityBuffer(int size, SecurityBufferType tokentype) { if (size < 0) { - NetEventSource.Fail(this, $"'size' out of range. [{size}]"); + NetEventSource.Fail(typeof(SecurityBuffer), $"'size' out of range. [{size}]"); } + this.offset = 0; this.size = size; this.type = tokentype; this.token = size == 0 ? null : new byte[size]; + this.unmanagedToken = null; } public SecurityBuffer(ChannelBinding binding) { + this.offset = 0; this.size = (binding == null ? 0 : binding.Size); this.type = SecurityBufferType.SECBUFFER_CHANNEL_BINDINGS; + this.token = null; this.unmanagedToken = binding; } } diff --git a/src/libraries/Common/src/System/Net/Security/SecurityBufferType.cs b/src/libraries/Common/src/System/Net/Security/SecurityBufferType.Windows.cs similarity index 100% rename from src/libraries/Common/src/System/Net/Security/SecurityBufferType.cs rename to src/libraries/Common/src/System/Net/Security/SecurityBufferType.Windows.cs diff --git a/src/libraries/System.Data.SqlClient/src/System.Data.SqlClient.csproj b/src/libraries/System.Data.SqlClient/src/System.Data.SqlClient.csproj index 7070f91..edd0a65 100644 --- a/src/libraries/System.Data.SqlClient/src/System.Data.SqlClient.csproj +++ b/src/libraries/System.Data.SqlClient/src/System.Data.SqlClient.csproj @@ -9,6 +9,7 @@ SR.PlatformNotSupported_DataSqlClient 4.0.0.0 4.1.0.0 + $(DefineConstants);netstandard $(DefineConstants);netcoreapp $(DefineConstants);FEATURE_TCPKEEPALIVE net461-Windows_NT-Debug;net461-Windows_NT-Release;netcoreapp-Debug;netcoreapp-Release;netcoreapp-Unix-Debug;netcoreapp-Unix-Release;netcoreapp-Windows_NT-Debug;netcoreapp-Windows_NT-Release;netcoreapp2.1-Debug;netcoreapp2.1-Release;netcoreapp2.1-Unix-Debug;netcoreapp2.1-Unix-Release;netcoreapp2.1-Windows_NT-Debug;netcoreapp2.1-Windows_NT-Release;netfx-Windows_NT-Debug;netfx-Windows_NT-Release;netstandard-Debug;netstandard-Release;netstandard-Unix-Debug;netstandard-Unix-Release;netstandard-Windows_NT-Debug;netstandard-Windows_NT-Release;netstandard1.2-Debug;netstandard1.2-Release;netstandard1.3-Debug;netstandard1.3-Release;uap-Windows_NT-Debug;uap-Windows_NT-Release;uap10.0.16299-Windows_NT-Debug;uap10.0.16299-Windows_NT-Release @@ -329,6 +330,12 @@ + + Common\System\Net\Security\SecurityBufferType.Windows.cs + + + Common\System\Net\Security\SecurityBuffer.Windows.cs + Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs @@ -429,12 +436,6 @@ Common\System\Net\SecurityStatusPal.cs - - Common\System\Net\Security\SecurityBufferType.cs - - - Common\System\Net\Security\SecurityBuffer.cs - Common\System\Net\DebugSafeHandle.cs diff --git a/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNIProxy.cs b/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNIProxy.cs index 58fab56..b61f623 100644 --- a/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNIProxy.cs +++ b/src/libraries/System.Data.SqlClient/src/System/Data/SqlClient/SNI/SNIProxy.cs @@ -89,18 +89,8 @@ namespace System.Data.SqlClient.SNI credentialsHandle = NegotiateStreamPal.AcquireDefaultCredential(securityPackage, false); } - SecurityBuffer[] inSecurityBufferArray = null; - if (receivedBuff != null) - { - inSecurityBufferArray = new SecurityBuffer[] { new SecurityBuffer(receivedBuff, SecurityBufferType.SECBUFFER_TOKEN) }; - } - else - { - inSecurityBufferArray = Array.Empty(); - } - int tokenSize = NegotiateStreamPal.QueryMaxTokenSize(securityPackage); - SecurityBuffer outSecurityBuffer = new SecurityBuffer(tokenSize, SecurityBufferType.SECBUFFER_TOKEN); + byte[] resultToken = new byte[tokenSize]; ContextFlagsPal requestedContextFlags = ContextFlagsPal.Connection | ContextFlagsPal.Confidentiality @@ -110,23 +100,23 @@ namespace System.Data.SqlClient.SNI string serverSPN = System.Text.Encoding.UTF8.GetString(serverName); SecurityStatusPal statusCode = NegotiateStreamPal.InitializeSecurityContext( - credentialsHandle, + ref credentialsHandle, ref securityContext, serverSPN, requestedContextFlags, - inSecurityBufferArray, - outSecurityBuffer, + receivedBuff, + null, + ref resultToken, ref contextFlags); if (statusCode.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded || statusCode.ErrorCode == SecurityStatusPalErrorCode.CompAndContinue) { - inSecurityBufferArray = new SecurityBuffer[] { outSecurityBuffer }; - statusCode = NegotiateStreamPal.CompleteAuthToken(ref securityContext, inSecurityBufferArray); - outSecurityBuffer.token = null; + statusCode = NegotiateStreamPal.CompleteAuthToken(ref securityContext, resultToken); + resultToken = null; } - sendBuff = outSecurityBuffer.token; + sendBuff = resultToken; if (sendBuff == null) { sendBuff = Array.Empty(); diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index f6f786a..837c2c6 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -177,12 +177,6 @@ Common\System\Net\SecurityStatusPal.cs - - Common\System\Net\Security\SecurityBuffer.cs - - - Common\System\Net\Security\SecurityBufferType.cs - Common\System\Net\Security\SSPIHandleCache.cs @@ -264,6 +258,12 @@ Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs + + Common\System\Net\Security\SecurityBuffer.Windows.cs + + + Common\System\Net\Security\SecurityBufferType.Windows.cs + Common\System\Net\Security\SecurityContextTokenHandle.cs diff --git a/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj b/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj index 18528a4..3a6d03e 100644 --- a/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj +++ b/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj @@ -14,6 +14,7 @@ + @@ -238,11 +239,11 @@ Common\System\Net\SecurityStatusPal.cs - - Common\System\Net\Security\SecurityBuffer.cs + + Common\System\Net\Security\SecurityBuffer.Windows.cs - - Common\System\Net\Security\SecurityBufferType.cs + + Common\System\Net\Security\SecurityBufferType.Windows.cs Common\System\Net\Security\SSPIHandleCache.cs @@ -351,7 +352,7 @@ - + diff --git a/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj b/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj index 9938e6e..53c19af 100644 --- a/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj +++ b/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj @@ -139,12 +139,6 @@ Common\System\Net\SecurityStatusPal.cs - - Common\System\Net\Security\SecurityBuffer.cs - - - Common\System\Net\Security\SecurityBufferType.cs - Common\System\Net\Security\SSPIHandleCache.cs @@ -199,6 +193,12 @@ Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs + + Common\System\Net\Security\SecurityBuffer.Windows.cs + + + Common\System\Net\Security\SecurityBufferType.Windows.cs + Common\System\Net\Security\SecurityContextTokenHandle.cs @@ -292,6 +292,7 @@ + diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index fe99c62..ad5c116 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -84,12 +84,6 @@ Common\CoreLib\System\Threading\Tasks\TaskToApm.cs - - Common\System\Net\Security\SecurityBuffer.cs - - - Common\System\Net\Security\SecurityBufferType.cs - Common\System\Net\Security\SSPIHandleCache.cs @@ -113,6 +107,12 @@ + + Common\System\Net\Security\SecurityBuffer.Windows.cs + + + Common\System\Net\Security\SecurityBufferType.Windows.cs + Common\System\Net\SecurityStatusAdapterPal.Windows.cs diff --git a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs index e56873a..49f2823 100644 --- a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs @@ -91,15 +91,20 @@ namespace System.Net } } } + catch + { + result?.Dispose(); + throw; + } finally { - if (gotReference) - { - remoteContext.DangerousRelease(); - } - if (remoteContext != null) { + if (gotReference) + { + remoteContext.DangerousRelease(); + } + remoteContext.Dispose(); } } diff --git a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs index 3b79888..5ace8dc 100644 --- a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using Microsoft.Win32.SafeHandles; +using System.Diagnostics; using System.Net.Security; using System.Runtime.InteropServices; using System.Security.Cryptography; @@ -95,7 +96,7 @@ namespace System.Net SafeFreeCertContext remoteContext = null; try { - remoteContext = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPISecureChannel, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_REMOTE_CERT_CONTEXT) as SafeFreeCertContext; + remoteContext = SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CONTEXT(GlobalSSPI.SSPISecureChannel, securityContext); if (remoteContext != null && !remoteContext.IsInvalid) { result = new X509Certificate2(remoteContext.DangerousGetHandle()); @@ -124,41 +125,28 @@ namespace System.Net // internal static string[] GetRequestCertificateAuthorities(SafeDeleteContext securityContext) { - Interop.SspiCli.SecPkgContext_IssuerListInfoEx issuerList = - (Interop.SspiCli.SecPkgContext_IssuerListInfoEx)SSPIWrapper.QueryContextAttributes( - GlobalSSPI.SSPISecureChannel, - securityContext, - Interop.SspiCli.ContextAttribute.SECPKG_ATTR_ISSUER_LIST_EX); + Interop.SspiCli.SecPkgContext_IssuerListInfoEx issuerList = default; + bool success = SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_ISSUER_LIST_EX(GlobalSSPI.SSPISecureChannel, securityContext, ref issuerList, out SafeHandle sspiHandle); string[] issuers = Array.Empty(); - try { - if (issuerList.cIssuers > 0) + if (success && issuerList.cIssuers > 0) { unsafe { - uint count = issuerList.cIssuers; issuers = new string[issuerList.cIssuers]; - Interop.SspiCli.CERT_CHAIN_ELEMENT* pIL = (Interop.SspiCli.CERT_CHAIN_ELEMENT*)issuerList.aIssuers.DangerousGetHandle(); - for (int i = 0; i < count; ++i) + var elements = new Span((void*)sspiHandle.DangerousGetHandle(), issuers.Length); + for (int i = 0; i < elements.Length; ++i) { - Interop.SspiCli.CERT_CHAIN_ELEMENT* pIL2 = pIL + i; - if (pIL2->cbSize <= 0) + if (elements[i].cbSize <= 0) { - NetEventSource.Fail(securityContext, $"Interop.SspiCli._CERT_CHAIN_ELEMENT size is not positive: {pIL2->cbSize}"); + NetEventSource.Fail(securityContext, $"Interop.SspiCli._CERT_CHAIN_ELEMENT size is not positive: {elements[i].cbSize}"); } - if (pIL2->cbSize > 0) + if (elements[i].cbSize > 0) { - uint size = pIL2->cbSize; - byte* ptr = (byte*)(pIL2->pCertContext); - byte[] x = new byte[size]; - for (int j = 0; j < size; j++) - { - x[j] = *(ptr + j); - } - - X500DistinguishedName x500DistinguishedName = new X500DistinguishedName(x); + byte[] x = new Span((byte*)elements[i].pCertContext, checked((int)elements[i].cbSize)).ToArray(); + var x500DistinguishedName = new X500DistinguishedName(x); issuers[i] = x500DistinguishedName.Name; if (NetEventSource.IsEnabled) NetEventSource.Info(securityContext, "IssuerListEx[{issuers[i]}]"); } @@ -168,10 +156,7 @@ namespace System.Net } finally { - if (issuerList.aIssuers != null) - { - issuerList.aIssuers.Dispose(); - } + sspiHandle?.Dispose(); } return issuers; diff --git a/src/libraries/System.Net.Security/src/System/Net/FixedSizeReader.cs b/src/libraries/System.Net.Security/src/System/Net/FixedSizeReader.cs index 14ed8a7..5c0207d 100644 --- a/src/libraries/System.Net.Security/src/System/Net/FixedSizeReader.cs +++ b/src/libraries/System.Net.Security/src/System/Net/FixedSizeReader.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.IO; using System.Threading; +using System.Threading.Tasks; namespace System.Net { @@ -46,7 +47,7 @@ namespace System.Net /// Completes "request" with 0 if 0 bytes was requested or legitimate EOF received. /// Otherwise, reads as directed or completes "request" with an Exception. /// - public static async void ReadPacketAsync(Stream transport, AsyncProtocolRequest request) // "async Task" might result in additional, unnecessary allocation + public static async Task ReadPacketAsync(Stream transport, AsyncProtocolRequest request) { try { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs b/src/libraries/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs index 6e4d0f1..e3825d6 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -264,7 +263,7 @@ namespace System.Net.Security if (asyncRequest != null) { asyncRequest.SetNextRequest(_ReadHeader, 0, _ReadHeader.Length, s_readCallback); - FixedSizeReader.ReadPacketAsync(InnerStream, asyncRequest); + _ = FixedSizeReader.ReadPacketAsync(InnerStream, asyncRequest); if (!asyncRequest.MustCompleteSynchronously) { return 0; @@ -318,7 +317,7 @@ namespace System.Net.Security { asyncRequest.SetNextRequest(InternalBuffer, 0, readBytes, s_readCallback); - FixedSizeReader.ReadPacketAsync(InnerStream, asyncRequest); + _ = FixedSizeReader.ReadPacketAsync(InnerStream, asyncRequest); if (!asyncRequest.MustCompleteSynchronously) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Windows.cs index c581946..2b089c8 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Windows.cs @@ -2,15 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.ComponentModel; using System.Diagnostics; -using System.Globalization; -using System.IO; +using System.Runtime.InteropServices; using System.Security; using System.Security.Principal; -using System.Threading; -using System.ComponentModel; -using System.Security.Authentication; -using System.Security.Authentication.ExtendedProtection; namespace System.Net.Security { @@ -64,10 +60,7 @@ namespace System.Net.Security } finally { - if (token != null) - { - token.Dispose(); - } + token?.Dispose(); } } @@ -78,7 +71,7 @@ namespace System.Net.Security internal static string QueryContextAssociatedName(SafeDeleteContext securityContext) { - return SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NAMES) as string; + return SSPIWrapper.QueryStringContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NAMES); } internal static void ValidateImpersonationLevel(TokenImpersonationLevel impersonationLevel) @@ -101,11 +94,9 @@ namespace System.Net.Security ref byte[] output, uint sequenceNumber) { - SecPkgContext_Sizes sizes = SSPIWrapper.QueryContextAttributes( - GlobalSSPI.SSPIAuth, - securityContext, - Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES - ) as SecPkgContext_Sizes; + SecPkgContext_Sizes sizes = default; + bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes); + Debug.Assert(success); try { @@ -132,7 +123,8 @@ namespace System.Net.Security Buffer.BlockCopy(buffer, offset, output, 4 + sizes.cbSecurityTrailer, count); // Prepare buffers TOKEN(signature), DATA and Padding. - var securityBuffer = new SecurityBuffer[3]; + 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); @@ -216,7 +208,8 @@ namespace System.Net.Security // // Kerberos and up // - var securityBuffer = new SecurityBuffer[2]; + TwoSecurityBuffers buffers = default; + var securityBuffer = MemoryMarshal.CreateSpan(ref buffers._item0, 2); securityBuffer[0] = new SecurityBuffer(buffer, offset, count, SecurityBufferType.SECBUFFER_STREAM); securityBuffer[1] = new SecurityBuffer(0, SecurityBufferType.SECBUFFER_DATA); @@ -263,7 +256,8 @@ namespace System.Net.Security throw new ArgumentOutOfRangeException(nameof(count)); } - var securityBuffer = new SecurityBuffer[2]; + TwoSecurityBuffers buffers = default; + var securityBuffer = MemoryMarshal.CreateSpan(ref buffers._item0, 2); securityBuffer[0] = new SecurityBuffer(buffer, offset, ntlmSignatureLength, SecurityBufferType.SECBUFFER_TOKEN); securityBuffer[1] = new SecurityBuffer(buffer, offset + ntlmSignatureLength, count - ntlmSignatureLength, SecurityBufferType.SECBUFFER_DATA); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs index 9171d70..cf8d0e6 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Security; @@ -39,8 +38,8 @@ namespace System.Net.Security private SslAuthenticationOptions _sslAuthenticationOptions; private SslApplicationProtocol _negotiatedApplicationProtocol; - private readonly Oid _serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); - private readonly Oid _clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2"); + private static readonly Oid s_serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); + private static readonly Oid s_clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2"); internal SecureChannel(SslAuthenticationOptions sslAuthenticationOptions) { @@ -177,16 +176,8 @@ namespace System.Net.Security internal void Close() { - if (_securityContext != null) - { - _securityContext.Dispose(); - } - - if (_credentialsHandle != null) - { - _credentialsHandle.Dispose(); - } - + _securityContext?.Dispose(); + _credentialsHandle?.Dispose(); GC.SuppressFinalize(this); } @@ -206,13 +197,9 @@ namespace System.Net.Security try { - string certHash = null; - // Protecting from X509Certificate2 derived classes. X509Certificate2 certEx = MakeEx(certificate); - - certHash = certEx.Thumbprint; - + if (certEx != null) { if (certEx.HasPrivateKey) @@ -230,6 +217,7 @@ namespace System.Net.Security } X509Certificate2Collection collectionEx; + string certHash = certEx.Thumbprint; // ELSE Try the MY user and machine stores for private key check. // For server side mode MY machine store takes priority. @@ -348,7 +336,7 @@ namespace System.Net.Security // Acquire possible Client Certificate information and set it on the handle. X509Certificate clientCertificate = null; // This is a candidate that can come from the user callback or be guessed when targeting a session restart. - var filteredCerts = new List(); // This is an intermediate client certs collection that try to use if no selectedCert is available yet. + List filteredCerts = null; // This is an intermediate client certs collection that try to use if no selectedCert is available yet. string[] issuers = null; // This is a list of issuers sent by the server, only valid is we do know what the server cert is. bool sessionRestartAttempt = false; // If true and no cached creds we will use anonymous creds. @@ -373,10 +361,7 @@ namespace System.Net.Security } finally { - if (remoteCert != null) - { - remoteCert.Dispose(); - } + remoteCert?.Dispose(); } @@ -387,7 +372,7 @@ namespace System.Net.Security sessionRestartAttempt = true; } - filteredCerts.Add(clientCertificate); + EnsureInitialized(ref filteredCerts).Add(clientCertificate); if (NetEventSource.IsEnabled) NetEventSource.Log.CertificateFromDelegate(this); } @@ -415,7 +400,7 @@ namespace System.Net.Security sessionRestartAttempt = true; if (clientCertificate != null) { - filteredCerts.Add(clientCertificate); + EnsureInitialized(ref filteredCerts).Add(clientCertificate); } if (NetEventSource.IsEnabled) @@ -473,7 +458,8 @@ namespace System.Net.Security // if (chain.ChainElements.Count > 0) { - for (int ii = 0; ii < chain.ChainElements.Count; ++ii) + int elementsCount = chain.ChainElements.Count; + for (int ii = 0; ii < elementsCount; ++ii) { string issuer = chain.ChainElements[ii].Certificate.Issuer; found = Array.IndexOf(issuers, issuer) != -1; @@ -498,6 +484,12 @@ namespace System.Net.Security if (chain != null) { chain.Dispose(); + + int elementsCount = chain.ChainElements.Count; + for (int element = 0; element < elementsCount; element++) + { + chain.ChainElements[element].Certificate.Dispose(); + } } if (certificateEx != null && (object)certificateEx != (object)_sslAuthenticationOptions.ClientCertificates[i]) @@ -510,7 +502,7 @@ namespace System.Net.Security if (NetEventSource.IsEnabled) NetEventSource.Log.SelectedCert(_sslAuthenticationOptions.ClientCertificates[i], this); - filteredCerts.Add(_sslAuthenticationOptions.ClientCertificates[i]); + EnsureInitialized(ref filteredCerts).Add(_sslAuthenticationOptions.ClientCertificates[i]); } } @@ -521,13 +513,14 @@ namespace System.Net.Security if (NetEventSource.IsEnabled) { - NetEventSource.Log.CertsAfterFiltering(filteredCerts.Count, this); - if (filteredCerts.Count != 0) + if (filteredCerts != null && filteredCerts.Count != 0) { + NetEventSource.Log.CertsAfterFiltering(filteredCerts.Count, this); NetEventSource.Log.FindingMatchingCerts(this); } else { + NetEventSource.Log.CertsAfterFiltering(0, this); NetEventSource.Info(this, "No client certificate to choose from"); } } @@ -539,16 +532,19 @@ namespace System.Net.Security // SECURITY: Accessing X509 cert Credential is disabled for semitrust. // We no longer need to demand for unmanaged code permissions. // EnsurePrivateKey should do the right demand for us. - for (int i = 0; i < filteredCerts.Count; ++i) + if (filteredCerts != null) { - clientCertificate = filteredCerts[i]; - if ((selectedCert = EnsurePrivateKey(clientCertificate)) != null) + for (int i = 0; i < filteredCerts.Count; ++i) { - break; - } + clientCertificate = filteredCerts[i]; + if ((selectedCert = EnsurePrivateKey(clientCertificate)) != null) + { + break; + } - clientCertificate = null; - selectedCert = null; + clientCertificate = null; + selectedCert = null; + } } if ((object)clientCertificate != (object)selectedCert && !clientCertificate.Equals(selectedCert)) @@ -624,6 +620,7 @@ namespace System.Net.Security return cachedCred; } + private static List EnsureInitialized(ref List list) => list ?? (list = new List()); // // Acquire Server Side Certificate information and set it on the class. @@ -782,20 +779,8 @@ namespace System.Net.Security throw new ArgumentOutOfRangeException(nameof(count)); } - SecurityBuffer incomingSecurity = null; - SecurityBuffer[] incomingSecurityBuffers = null; - - if (input != null) - { - incomingSecurity = new SecurityBuffer(input, offset, count, SecurityBufferType.SECBUFFER_TOKEN); - } - - incomingSecurityBuffers = SslStreamPal.GetIncomingSecurityBuffers(_sslAuthenticationOptions, ref incomingSecurity); - - SecurityBuffer outgoingSecurity = new SecurityBuffer(null, SecurityBufferType.SECBUFFER_TOKEN); - - SecurityStatusPal status = default(SecurityStatusPal); - + byte[] result = Array.Empty(); + SecurityStatusPal status = default; bool cachedCreds = false; byte[] thumbPrint = null; @@ -820,32 +805,19 @@ namespace System.Net.Security status = SslStreamPal.AcceptSecurityContext( ref _credentialsHandle, ref _securityContext, - incomingSecurityBuffers, - outgoingSecurity, + input != null ? new ArraySegment(input, offset, count) : default, + ref result, _sslAuthenticationOptions); } else { - if (incomingSecurityBuffers == null) - { - status = SslStreamPal.InitializeSecurityContext( - ref _credentialsHandle, - ref _securityContext, - _sslAuthenticationOptions.TargetHost, - incomingSecurity, - outgoingSecurity, - _sslAuthenticationOptions); - } - else - { - status = SslStreamPal.InitializeSecurityContext( - _credentialsHandle, - ref _securityContext, - _sslAuthenticationOptions.TargetHost, - incomingSecurityBuffers, - outgoingSecurity, - _sslAuthenticationOptions); - } + status = SslStreamPal.InitializeSecurityContext( + ref _credentialsHandle, + ref _securityContext, + _sslAuthenticationOptions.TargetHost, + input != null ? new ArraySegment(input, offset, count) : default, + ref result, + _sslAuthenticationOptions); } } while (cachedCreds && _credentialsHandle == null); } @@ -859,10 +831,7 @@ namespace System.Net.Security // Assuming the ISC or ASC has referenced the credential, // we want to call dispose so to decrement the effective ref count. // - if (_credentialsHandle != null) - { - _credentialsHandle.Dispose(); - } + _credentialsHandle?.Dispose(); // // This call may bump up the credential reference count further. @@ -875,7 +844,7 @@ namespace System.Net.Security } } - output = outgoingSecurity.token; + output = result; if (_negotiatedApplicationProtocol == default) { // try to get ALPN info unless we already have it. (this function can be called multiple times) @@ -1000,9 +969,6 @@ namespace System.Net.Security --*/ //This method validates a remote certificate. - //SECURITY: The scenario is allowed in semitrust StorePermission is asserted for Chain.Build - // A user callback has unique signature so it is safe to call it under permission assert. - // internal bool VerifyRemoteCertificate(RemoteCertValidationCallback remoteCertValidationCallback, ref ProtocolToken alertToken) { if (NetEventSource.IsEnabled) @@ -1014,10 +980,10 @@ namespace System.Net.Security bool success = false; X509Chain chain = null; X509Certificate2 remoteCertificateEx = null; + X509Certificate2Collection remoteCertificateStore = null; try { - X509Certificate2Collection remoteCertificateStore; remoteCertificateEx = CertificateValidationPal.GetRemoteCertificate(_securityContext, out remoteCertificateStore); _isRemoteCertificateAvailable = remoteCertificateEx != null; @@ -1034,7 +1000,7 @@ namespace System.Net.Security chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; // Authenticate the remote party: (e.g. when operating in server mode, authenticate the client). - chain.ChainPolicy.ApplicationPolicy.Add(_sslAuthenticationOptions.IsServer ? _clientAuthOid : _serverAuthOid); + chain.ChainPolicy.ApplicationPolicy.Add(_sslAuthenticationOptions.IsServer ? s_clientAuthOid : s_serverAuthOid); if (remoteCertificateStore != null) { @@ -1082,15 +1048,28 @@ namespace System.Net.Security { // At least on Win2k server the chain is found to have dependencies on the original cert context. // So it should be closed first. + if (chain != null) { + int elementsCount = chain.ChainElements.Count; + for (int i = 0; i < elementsCount; i++) + { + chain.ChainElements[i].Certificate.Dispose(); + } + chain.Dispose(); } - if (remoteCertificateEx != null) + if (remoteCertificateStore != null) { - remoteCertificateEx.Dispose(); + int certCount = remoteCertificateStore.Count; + for (int i = 0; i < certCount; i++) + { + remoteCertificateStore[i].Dispose(); + } } + + remoteCertificateEx?.Dispose(); } if (NetEventSource.IsEnabled) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs index 8ed56d9..7b64a7f 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; @@ -22,14 +21,12 @@ namespace System.Net.Security EncryptionPolicy = sslClientAuthenticationOptions.EncryptionPolicy; IsServer = false; RemoteCertRequired = true; - RemoteCertificateValidationCallback = sslClientAuthenticationOptions.RemoteCertificateValidationCallback; TargetHost = sslClientAuthenticationOptions.TargetHost; // Client specific options. CertSelectionDelegate = localCallback; CertificateRevocationCheckMode = sslClientAuthenticationOptions.CertificateRevocationCheckMode; ClientCertificates = sslClientAuthenticationOptions.ClientCertificates; - LocalCertificateSelectionCallback = sslClientAuthenticationOptions.LocalCertificateSelectionCallback; } internal SslAuthenticationOptions(SslServerAuthenticationOptions sslServerAuthenticationOptions) @@ -46,7 +43,6 @@ namespace System.Net.Security { NetEventSource.Info(this, $"Server RemoteCertRequired: {RemoteCertRequired}."); } - RemoteCertificateValidationCallback = sslServerAuthenticationOptions.RemoteCertificateValidationCallback; TargetHost = string.Empty; // Server specific options. @@ -59,8 +55,6 @@ namespace System.Net.Security internal X509CertificateCollection ClientCertificates { get; set; } internal List ApplicationProtocols { get; } internal bool IsServer { get; set; } - internal RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; } - internal LocalCertificateSelectionCallback LocalCertificateSelectionCallback { get; set; } internal X509Certificate ServerCertificate { get; set; } internal SslProtocols EnabledSslProtocols { get; set; } internal X509RevocationMode CertificateRevocationCheckMode { get; set; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Unix.cs index 88c8ce9..196ebfb 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Unix.cs @@ -11,8 +11,7 @@ namespace System.Net.Security { public SslConnectionInfo(SafeSslHandle sslContext) { - string protocolVersion = Interop.Ssl.GetProtocolVersion(sslContext); - Protocol = (int)MapProtocolVersion(protocolVersion); + Protocol = (int)MapProtocolVersion(Interop.Ssl.SslGetVersion(sslContext)); int dataCipherAlg; int keyExchangeAlg; @@ -43,27 +42,61 @@ namespace System.Net.Security KeyExchKeySize = 0; } - private SslProtocols MapProtocolVersion(string protocolVersion) + private unsafe SslProtocols MapProtocolVersion(IntPtr protocolVersion) { - switch (protocolVersion) + // protocolVersion points to a static ASCII string that's one of: + // TLSv1 + // TLSv1.1 + // TLSv1.2 + // TLSv1.3 + // SSLv2 + // SSLv3 + // unknown + // Regardless, it's null terminated. + + byte* b = (byte*)protocolVersion; + if (b[0] == 'T') + { + if (b[1] == 'L' && + b[2] == 'S' && + b[3] == 'v' && + b[4] == '1') + { + if (b[5] == '\0') + { + return SslProtocols.Tls; + } + else if (b[5] == '.' && b[6] != '\0' && b[7] == '\0') + { + switch (b[6]) + { + case (byte)'1': return SslProtocols.Tls11; + case (byte)'2': return SslProtocols.Tls12; + case (byte)'3': return SslProtocols.Tls13; + } + } + } + } + else if (b[0] == 'S') { + if (b[1] == 'S' && + b[2] == 'L' && + b[3] == 'v') + { #pragma warning disable 0618 // Ssl2, Ssl3 are deprecated. - case "SSLv2": - return SslProtocols.Ssl2; - case "SSLv3": - return SslProtocols.Ssl3; + if (b[4] == '2' && b[5] == '\0') + { + return SslProtocols.Ssl2; + } + else if (b[4] == '3' && b[5] == '\0') + { + return SslProtocols.Ssl3; + } #pragma warning restore - case "TLSv1": - return SslProtocols.Tls; - case "TLSv1.1": - return SslProtocols.Tls11; - case "TLSv1.2": - return SslProtocols.Tls12; - case "TLSv1.3": - return SslProtocols.Tls13; - default: - return SslProtocols.None; + } } + + return SslProtocols.None; } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.cs index 512199d..1d28e0c 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.cs @@ -6,46 +6,12 @@ namespace System.Net.Security { internal partial class SslConnectionInfo { - public int Protocol - { - get; - private set; - } - - public int DataCipherAlg - { - get; - private set; - } - - public int DataKeySize - { - get; - private set; - } - - public int DataHashAlg - { - get; - private set; - } - - public int DataHashKeySize - { - get; - private set; - } - - public int KeyExchangeAlg - { - get; - private set; - } - - public int KeyExchKeySize - { - get; - private set; - } + public int Protocol { get; } + public int DataCipherAlg { get; private set; } + public int DataKeySize { get; private set; } + public int DataHashAlg { get; private set; } + public int DataHashKeySize { get; private set; } + public int KeyExchangeAlg { get; private set; } + public int KeyExchKeySize { get; private set; } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslState.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslState.cs index e9fd9a3..2c55e67 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslState.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslState.cs @@ -24,9 +24,8 @@ namespace System.Net.Security internal SslAuthenticationOptions _sslAuthenticationOptions; - private Stream _innerStream; - - private SslStreamInternal _secureStream; + private readonly Stream _innerStream; + private readonly SslStreamInternal _secureStream; private int _nestedAuth; private SecureChannel _context; @@ -75,6 +74,7 @@ namespace System.Net.Security internal SslState(Stream innerStream) { _innerStream = innerStream; + _secureStream = new SslStreamInternal(this); } /// Set as the _exception when the instance is disposed. @@ -398,11 +398,6 @@ namespace System.Net.Security get { CheckThrow(true); - if (_secureStream == null) - { - Interlocked.CompareExchange(ref _secureStream, new SslStreamInternal(this), null); - } - return _secureStream; } } @@ -855,7 +850,7 @@ namespace System.Net.Security else { asyncRequest.SetNextRequest(buffer, 0, SecureChannel.ReadHeaderSize, s_partialFrameCallback); - FixedSizeReader.ReadPacketAsync(_innerStream, asyncRequest); + _ = FixedSizeReader.ReadPacketAsync(_innerStream, asyncRequest); if (!asyncRequest.MustCompleteSynchronously) { return; @@ -903,7 +898,7 @@ namespace System.Net.Security else { asyncRequest.SetNextRequest(buffer, readBytes, restBytes, s_readFrameCallback); - FixedSizeReader.ReadPacketAsync(_innerStream, asyncRequest); + _ = FixedSizeReader.ReadPacketAsync(_innerStream, asyncRequest); if (!asyncRequest.MustCompleteSynchronously) { return; @@ -1611,7 +1606,7 @@ namespace System.Net.Security #if TRACE_VERBOSE if (bytes[1] != 3 && NetEventSource.IsEnabled) { - if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"WARNING: SslState::DetectFraming() SSL protocol is > 3, trying SSL3 framing in retail = {bytes[i]:x}"); + if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"WARNING: SslState::DetectFraming() SSL protocol is > 3, trying SSL3 framing in retail = {bytes[1]:x}"); } #endif diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs index 9e50c43..c3d90f9 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography.X509Certificates; @@ -41,7 +39,8 @@ namespace System.Net.Security public class SslStream : AuthenticatedStream { private SslState _sslState; - private object _remoteCertificateOrBytes; + private X509Certificate2 _remoteCertificate; + private bool _remoteCertificateExposed; internal RemoteCertificateValidationCallback _userCertificateValidationCallback; internal LocalCertificateSelectionCallback _userCertificateSelectionCallback; @@ -124,7 +123,7 @@ namespace System.Net.Security private bool UserCertValidationCallbackWrapper(string hostName, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { - _remoteCertificateOrBytes = certificate == null ? null : certificate.RawData; + _remoteCertificate = certificate == null ? null : new X509Certificate2(certificate); if (_userCertificateValidationCallback == null) { if (!_sslState.RemoteCertRequired) @@ -508,16 +507,8 @@ namespace System.Net.Security get { _sslState.CheckThrow(true); - - object chkCertificateOrBytes = _remoteCertificateOrBytes; - if (chkCertificateOrBytes != null && chkCertificateOrBytes.GetType() == typeof(byte[])) - { - return (X509Certificate)(_remoteCertificateOrBytes = new X509Certificate2((byte[])chkCertificateOrBytes)); - } - else - { - return chkCertificateOrBytes as X509Certificate; - } + _remoteCertificateExposed = true; + return _remoteCertificate; } } @@ -672,6 +663,12 @@ namespace System.Net.Security { try { + if (!_remoteCertificateExposed) + { + _remoteCertificate?.Dispose(); + _remoteCertificate = null; + _remoteCertificateExposed = false; + } _sslState.Close(); } finally diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs index f013775..ae39dfc 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs @@ -5,7 +5,6 @@ using System.Buffers; using System.Diagnostics; using System.IO; -using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; @@ -470,7 +469,7 @@ namespace System.Net.Security ValueTask t = adapter.ReadAsync(_internalBuffer, _internalBufferCount, _internalBuffer.Length - _internalBufferCount); if (!t.IsCompletedSuccessfully) { - return new ValueTask(InternalFillBufferAsync(adapter, t, minSize, initialCount)); + return InternalFillBufferAsync(adapter, t, minSize, initialCount); } int bytes = t.Result; if (bytes == 0) @@ -489,7 +488,7 @@ namespace System.Net.Security return new ValueTask(minSize); - async Task InternalFillBufferAsync(TReadAdapter adap, ValueTask task, int min, int initial) + async ValueTask InternalFillBufferAsync(TReadAdapter adap, ValueTask task, int min, int initial) { while (true) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs index b06b4d4..f14bf9b 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs @@ -35,61 +35,22 @@ namespace System.Net.Security public static SecurityStatusPal AcceptSecurityContext( ref SafeFreeCredentials credential, ref SafeDeleteContext context, - SecurityBuffer[] inputBuffers, - SecurityBuffer outputBuffer, + ArraySegment inputBuffer, + ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - if (inputBuffers != null) - { - Debug.Assert(inputBuffers.Length == 2); - Debug.Assert(inputBuffers[1].token == null); - - return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, sslAuthenticationOptions); - } - else - { - return HandshakeInternal(credential, ref context, inputBuffer: null, outputBuffer, sslAuthenticationOptions); - } + return HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal InitializeSecurityContext( ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, - SecurityBuffer inputBuffer, - SecurityBuffer outputBuffer, - SslAuthenticationOptions sslAuthenticationOptions) - { - return HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, sslAuthenticationOptions); - } - - public static SecurityStatusPal InitializeSecurityContext( - SafeFreeCredentials credential, - ref SafeDeleteContext context, - string targetName, - SecurityBuffer[] inputBuffers, - SecurityBuffer outputBuffer, + ArraySegment inputBuffer, + ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - Debug.Assert(inputBuffers.Length == 2); - Debug.Assert(inputBuffers[1].token == null); - return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, sslAuthenticationOptions); - } - - public static SecurityBuffer[] GetIncomingSecurityBuffers(SslAuthenticationOptions options, ref SecurityBuffer incomingSecurity) - { - SecurityBuffer[] incomingSecurityBuffers = null; - - if (incomingSecurity != null) - { - incomingSecurityBuffers = new SecurityBuffer[] - { - incomingSecurity, - new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY) - }; - } - - return incomingSecurityBuffers; + return HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SafeFreeCredentials AcquireCredentialsHandle( @@ -272,8 +233,8 @@ namespace System.Net.Security private static SecurityStatusPal HandshakeInternal( SafeFreeCredentials credential, ref SafeDeleteContext context, - SecurityBuffer inputBuffer, - SecurityBuffer outputBuffer, + ArraySegment inputBuffer, + ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { Debug.Assert(!credential.IsInvalid); @@ -299,9 +260,9 @@ namespace System.Net.Security } } - if (inputBuffer != null && inputBuffer.size > 0) + if (inputBuffer.Array != null && inputBuffer.Count > 0) { - sslContext.Write(inputBuffer.token, inputBuffer.offset, inputBuffer.size); + sslContext.Write(inputBuffer.Array, inputBuffer.Offset, inputBuffer.Count); } SafeSslHandle sslHandle = sslContext.SslContext; @@ -312,11 +273,7 @@ namespace System.Net.Security status = PerformHandshake(sslHandle); } - byte[] output = sslContext.ReadPendingWrites(); - outputBuffer.offset = 0; - outputBuffer.size = output?.Length ?? 0; - outputBuffer.token = output; - + outputBuffer = sslContext.ReadPendingWrites(); return status; } catch (Exception exc) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs index 5b5c322..c072103 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs @@ -28,49 +28,15 @@ namespace System.Net.Security } public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, - SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) + ArraySegment inputBuffer, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - if (inputBuffers != null) - { - Debug.Assert(inputBuffers.Length == 2); - Debug.Assert(inputBuffers[1].token == null); - - return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, sslAuthenticationOptions); - } - else - { - return HandshakeInternal(credential, ref context, inputBuffer: null, outputBuffer, sslAuthenticationOptions); - } - } - - public static SecurityStatusPal InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, - string targetName, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) - { - return HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, sslAuthenticationOptions); + return HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } - public static SecurityStatusPal InitializeSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) + public static SecurityStatusPal InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, + ArraySegment inputBuffer, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - Debug.Assert(inputBuffers.Length == 2); - Debug.Assert(inputBuffers[1].token == null); - - return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, sslAuthenticationOptions); - } - - public static SecurityBuffer[] GetIncomingSecurityBuffers(SslAuthenticationOptions options, ref SecurityBuffer incomingSecurity) - { - SecurityBuffer[] incomingSecurityBuffers = null; - - if (incomingSecurity != null) - { - incomingSecurityBuffers = new SecurityBuffer[] - { - incomingSecurity, - new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY) - }; - } - - return incomingSecurityBuffers; + return HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SafeFreeCredentials AcquireCredentialsHandle(X509Certificate certificate, @@ -133,8 +99,8 @@ namespace System.Net.Security return Interop.Ssl.ConvertAlpnProtocolListToByteArray(applicationProtocols); } - private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credential, ref SafeDeleteContext context, SecurityBuffer inputBuffer, - SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) + private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credential, ref SafeDeleteContext context, + ArraySegment inputBuffer, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { Debug.Assert(!credential.IsInvalid); @@ -149,13 +115,13 @@ namespace System.Net.Security int outputSize; bool done; - if (null == inputBuffer) + if (inputBuffer.Array == null) { done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, null, 0, 0, out output, out outputSize); } else { - done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, inputBuffer.token, inputBuffer.offset, inputBuffer.size, out output, out outputSize); + done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, inputBuffer.Array, inputBuffer.Offset, inputBuffer.Count, out output, out outputSize); } // When the handshake is done, and the context is server, check if the alpnHandle target was set to null during ALPN. @@ -168,9 +134,10 @@ namespace System.Net.Security return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, Interop.OpenSsl.CreateSslException(SR.net_alpn_failed)); } - outputBuffer.size = outputSize; - outputBuffer.offset = 0; - outputBuffer.token = outputSize > 0 ? output : null; + outputBuffer = + outputSize == 0 ? null : + outputSize == output.Length ? output : + new Span(output, 0, outputSize).ToArray(); return new SecurityStatusPal(done ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.ContinueNeeded); } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs index c35fcdb..ab9eb6a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs @@ -46,9 +46,18 @@ namespace System.Net.Security return Interop.Sec_Application_Protocols.ToByteArray(protocols); } - public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) + public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, ArraySegment input, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - Interop.SspiCli.ContextFlags unusedAttributes = default(Interop.SspiCli.ContextFlags); + Interop.SspiCli.ContextFlags unusedAttributes = default; + + ThreeSecurityBuffers threeSecurityBuffers = default; + SecurityBuffer? incomingSecurity = input.Array != null ? + new SecurityBuffer(input.Array, input.Offset, input.Count, SecurityBufferType.SECBUFFER_TOKEN) : + (SecurityBuffer?)null; + Span inputBuffers = MemoryMarshal.CreateSpan(ref threeSecurityBuffers._item0, 3); + GetIncomingSecurityBuffers(sslAuthenticationOptions, in incomingSecurity, ref inputBuffers); + + var resultBuffer = new SecurityBuffer(outputBuffer, SecurityBufferType.SECBUFFER_TOKEN); int errorCode = SSPIWrapper.AcceptSecurityContext( GlobalSSPI.SSPISecureChannel, @@ -57,56 +66,48 @@ namespace System.Net.Security ServerRequiredFlags | (sslAuthenticationOptions.RemoteCertRequired ? Interop.SspiCli.ContextFlags.MutualAuth : Interop.SspiCli.ContextFlags.Zero), Interop.SspiCli.Endianness.SECURITY_NATIVE_DREP, inputBuffers, - outputBuffer, + ref resultBuffer, ref unusedAttributes); + outputBuffer = resultBuffer.token; return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode); } - public static SecurityStatusPal InitializeSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, string targetName, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) + public static SecurityStatusPal InitializeSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, string targetName, ArraySegment input, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - Interop.SspiCli.ContextFlags unusedAttributes = default(Interop.SspiCli.ContextFlags); + Interop.SspiCli.ContextFlags unusedAttributes = default; - int errorCode = SSPIWrapper.InitializeSecurityContext( - GlobalSSPI.SSPISecureChannel, - ref credentialsHandle, - ref context, - targetName, - RequiredFlags | Interop.SspiCli.ContextFlags.InitManualCredValidation, - Interop.SspiCli.Endianness.SECURITY_NATIVE_DREP, - inputBuffer, - outputBuffer, - ref unusedAttributes); - - return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode); - } + ThreeSecurityBuffers threeSecurityBuffers = default; + SecurityBuffer? incomingSecurity = input.Array != null ? + new SecurityBuffer(input.Array, input.Offset, input.Count, SecurityBufferType.SECBUFFER_TOKEN) : + (SecurityBuffer?)null; + Span inputBuffers = MemoryMarshal.CreateSpan(ref threeSecurityBuffers._item0, 3); + GetIncomingSecurityBuffers(sslAuthenticationOptions, in incomingSecurity, ref inputBuffers); - public static SecurityStatusPal InitializeSecurityContext(SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, string targetName, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) - { - Interop.SspiCli.ContextFlags unusedAttributes = default(Interop.SspiCli.ContextFlags); + var resultBuffer = new SecurityBuffer(outputBuffer, SecurityBufferType.SECBUFFER_TOKEN); int errorCode = SSPIWrapper.InitializeSecurityContext( GlobalSSPI.SSPISecureChannel, - credentialsHandle, + ref credentialsHandle, ref context, targetName, RequiredFlags | Interop.SspiCli.ContextFlags.InitManualCredValidation, Interop.SspiCli.Endianness.SECURITY_NATIVE_DREP, inputBuffers, - outputBuffer, + ref resultBuffer, ref unusedAttributes); + outputBuffer = resultBuffer.token; return SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode); } - public static SecurityBuffer[] GetIncomingSecurityBuffers(SslAuthenticationOptions options, ref SecurityBuffer incomingSecurity) + private static void GetIncomingSecurityBuffers(SslAuthenticationOptions options, in SecurityBuffer? incomingSecurity, ref Span incomingSecurityBuffers) { - SecurityBuffer alpnBuffer = null; - SecurityBuffer[] incomingSecurityBuffers = null; + SecurityBuffer? alpnBuffer = null; if (options.ApplicationProtocols != null && options.ApplicationProtocols.Count != 0) { - byte[] alpnBytes = SslStreamPal.ConvertAlpnProtocolListToByteArray(options.ApplicationProtocols); + byte[] alpnBytes = ConvertAlpnProtocolListToByteArray(options.ApplicationProtocols); alpnBuffer = new SecurityBuffer(alpnBytes, 0, alpnBytes.Length, SecurityBufferType.SECBUFFER_APPLICATION_PROTOCOLS); } @@ -114,28 +115,29 @@ namespace System.Net.Security { if (alpnBuffer != null) { - incomingSecurityBuffers = new SecurityBuffer[] - { - incomingSecurity, - new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY), - alpnBuffer - }; + Debug.Assert(incomingSecurityBuffers.Length >= 3); + incomingSecurityBuffers[0] = incomingSecurity.GetValueOrDefault(); + incomingSecurityBuffers[1] = new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY); + incomingSecurityBuffers[2] = alpnBuffer.GetValueOrDefault(); + incomingSecurityBuffers = incomingSecurityBuffers.Slice(0, 3); } else { - incomingSecurityBuffers = new SecurityBuffer[] - { - incomingSecurity, - new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY) - }; + Debug.Assert(incomingSecurityBuffers.Length >= 2); + incomingSecurityBuffers[0] = incomingSecurity.GetValueOrDefault(); + incomingSecurityBuffers[1] = new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY); + incomingSecurityBuffers = incomingSecurityBuffers.Slice(0, 2); } } else if (alpnBuffer != null) { - incomingSecurity = alpnBuffer; + incomingSecurityBuffers[0] = alpnBuffer.GetValueOrDefault(); + incomingSecurityBuffers = incomingSecurityBuffers.Slice(0, 1); + } + else + { + incomingSecurityBuffers = default; } - - return incomingSecurityBuffers; } public static SafeFreeCredentials AcquireCredentialsHandle(X509Certificate certificate, SslProtocols protocols, EncryptionPolicy policy, bool isServer) @@ -179,20 +181,18 @@ namespace System.Net.Security internal static byte[] GetNegotiatedApplicationProtocol(SafeDeleteContext context) { - Interop.SecPkgContext_ApplicationProtocol alpnContext = SSPIWrapper.QueryContextAttributes( - GlobalSSPI.SSPISecureChannel, - context, - Interop.SspiCli.ContextAttribute.SECPKG_ATTR_APPLICATION_PROTOCOL) as Interop.SecPkgContext_ApplicationProtocol; + 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 (alpnContext == null || - alpnContext.ProtoNegoExt != Interop.ApplicationProtocolNegotiationExt.ALPN || - alpnContext.ProtoNegoStatus != Interop.ApplicationProtocolNegotiationStatus.Success) + if (success && + alpnContext.ProtoNegoExt == Interop.ApplicationProtocolNegotiationExt.ALPN && + alpnContext.ProtoNegoStatus == Interop.ApplicationProtocolNegotiationStatus.Success) { - return null; + return alpnContext.Protocol; } - return alpnContext.Protocol; + return null; } public static unsafe SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, ReadOnlyMemory input, int headerSize, int trailerSize, ref byte[] output, out int resultSize) @@ -309,48 +309,33 @@ namespace System.Net.Security public static SecurityStatusPal ApplyAlertToken(ref SafeFreeCredentials credentialsHandle, SafeDeleteContext securityContext, TlsAlertType alertType, TlsAlertMessage alertMessage) { - Interop.SChannel.SCHANNEL_ALERT_TOKEN alertToken; - alertToken.dwTokenType = Interop.SChannel.SCHANNEL_ALERT; - alertToken.dwAlertType = (uint)alertType; - alertToken.dwAlertNumber = (uint)alertMessage; - - var bufferDesc = new SecurityBuffer[1]; - - int alertTokenByteSize = Marshal.SizeOf(); - IntPtr p = Marshal.AllocHGlobal(alertTokenByteSize); - - try + var alertToken = new Interop.SChannel.SCHANNEL_ALERT_TOKEN { - var buffer = new byte[alertTokenByteSize]; - Marshal.StructureToPtr(alertToken, p, false); - Marshal.Copy(p, buffer, 0, alertTokenByteSize); + dwTokenType = Interop.SChannel.SCHANNEL_ALERT, + dwAlertType = (uint)alertType, + dwAlertNumber = (uint)alertMessage + }; + byte[] buffer = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref alertToken, 1)).ToArray(); + var securityBuffer = new SecurityBuffer(buffer, SecurityBufferType.SECBUFFER_TOKEN); - bufferDesc[0] = new SecurityBuffer(buffer, SecurityBufferType.SECBUFFER_TOKEN); - var errorCode = (Interop.SECURITY_STATUS)SSPIWrapper.ApplyControlToken( - GlobalSSPI.SSPISecureChannel, - ref securityContext, - bufferDesc); + var errorCode = (Interop.SECURITY_STATUS)SSPIWrapper.ApplyControlToken( + GlobalSSPI.SSPISecureChannel, + ref securityContext, + in securityBuffer); - return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(errorCode, attachException: true); - } - finally - { - Marshal.FreeHGlobal(p); - } + return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(errorCode, attachException: true); } + private static readonly byte[] s_schannelShutdownBytes = BitConverter.GetBytes(Interop.SChannel.SCHANNEL_SHUTDOWN); + public static SecurityStatusPal ApplyShutdownToken(ref SafeFreeCredentials credentialsHandle, SafeDeleteContext securityContext) { - int shutdownToken = Interop.SChannel.SCHANNEL_SHUTDOWN; - - var bufferDesc = new SecurityBuffer[1]; - var buffer = BitConverter.GetBytes(shutdownToken); + var securityBuffer = new SecurityBuffer(s_schannelShutdownBytes, SecurityBufferType.SECBUFFER_TOKEN); - bufferDesc[0] = new SecurityBuffer(buffer, SecurityBufferType.SECBUFFER_TOKEN); var errorCode = (Interop.SECURITY_STATUS)SSPIWrapper.ApplyControlToken( GlobalSSPI.SSPISecureChannel, ref securityContext, - bufferDesc); + in securityBuffer); return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(errorCode, attachException: true); } @@ -362,21 +347,17 @@ namespace System.Net.Security public static void QueryContextStreamSizes(SafeDeleteContext securityContext, out StreamSizes streamSizes) { - var interopStreamSizes = SSPIWrapper.QueryContextAttributes( - GlobalSSPI.SSPISecureChannel, - securityContext, - Interop.SspiCli.ContextAttribute.SECPKG_ATTR_STREAM_SIZES) as SecPkgContext_StreamSizes; - + SecPkgContext_StreamSizes interopStreamSizes = default; + bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPISecureChannel, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_STREAM_SIZES, ref interopStreamSizes); + Debug.Assert(success); streamSizes = new StreamSizes(interopStreamSizes); } public static void QueryContextConnectionInfo(SafeDeleteContext securityContext, out SslConnectionInfo connectionInfo) { - var interopConnectionInfo = SSPIWrapper.QueryContextAttributes( - GlobalSSPI.SSPISecureChannel, - securityContext, - Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CONNECTION_INFO) as SecPkgContext_ConnectionInfo; - + SecPkgContext_ConnectionInfo interopConnectionInfo = default; + bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPISecureChannel, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CONNECTION_INFO, ref interopConnectionInfo); + Debug.Assert(success); connectionInfo = new SslConnectionInfo(interopConnectionInfo); } diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/OidCollection.cs b/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/OidCollection.cs index 781a194..cc5b62c 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/OidCollection.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/OidCollection.cs @@ -3,26 +3,42 @@ // See the LICENSE file in the project root for more information. using System.Collections; -using System.Collections.Generic; using Internal.Cryptography; namespace System.Security.Cryptography { public sealed class OidCollection : ICollection { - public OidCollection() - { - _list = new List(); - } + private Oid[] _oids = Array.Empty(); + private int _count; + + public OidCollection() { } public int Add(Oid oid) { - int count = _list.Count; - _list.Add(oid); + int count = _count; + if (count == _oids.Length) + { + Array.Resize(ref _oids, count == 0 ? 4 : count * 2); + } + _oids[count] = oid; + _count = count + 1; return count; } - public Oid this[int index] => _list[index]; + public Oid this[int index] + { + get + { + if ((uint)index >= (uint)_count) + { + // For compat, throw an ArgumentOutOfRangeException instead of + // the IndexOutOfRangeException that comes from the array's indexer. + throw new ArgumentOutOfRangeException(nameof(index)); + } + return _oids[index]; + } + } // Indexer using an OID friendly name or value. public Oid this[string oid] @@ -35,8 +51,9 @@ namespace System.Security.Cryptography { oidValue = oid; } - foreach (Oid entry in _list) + for (int i = 0; i < _count; i++) { + Oid entry = _oids[i]; if (entry.Value == oidValue) return entry; } @@ -44,7 +61,7 @@ namespace System.Security.Cryptography } } - public int Count => _list.Count; + public int Count => _count; public OidEnumerator GetEnumerator() => new OidEnumerator(this); @@ -78,13 +95,11 @@ namespace System.Security.Cryptography if (index < 0 || index >= array.Length) throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index); - _list.CopyTo(array, index); + Array.Copy(_oids, 0, array, index, _count); } public bool IsSynchronized => false; public object SyncRoot => this; - - private readonly List _list; } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs index 4c429ff..dd8e9b6 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs @@ -413,9 +413,6 @@ namespace Internal.Cryptography.Pal private X509ChainElement BuildElement(X509Certificate2 cert, int dwStatus) { - const int errSecCertificateExpired = -67818; - const int errSecCertificateNotValidYet = -67819; - if (dwStatus == 0) { return new X509ChainElement(cert, Array.Empty(), ""); @@ -429,29 +426,30 @@ namespace Internal.Cryptography.Pal if ((mapping.ChainStatusFlag & flags) == mapping.ChainStatusFlag) { int osStatus; + string errorString; // Disambiguate the NotTimeValid code to get the right string. if (mapping.ChainStatusFlag == X509ChainStatusFlags.NotTimeValid) { - if (cert != null && cert.NotBefore > _verificationTime) - { - osStatus = errSecCertificateNotValidYet; - } - else - { - osStatus = errSecCertificateExpired; - } + const int errSecCertificateExpired = -67818; + const int errSecCertificateNotValidYet = -67819; + + osStatus = cert != null && cert.NotBefore > _verificationTime ? + errSecCertificateNotValidYet : + errSecCertificateExpired; + errorString = Interop.AppleCrypto.GetSecErrorString(osStatus); } else { osStatus = mapping.OSStatus; + errorString = mapping.ErrorString; } statuses.Add( new X509ChainStatus { Status = mapping.ChainStatusFlag, - StatusInformation = Interop.AppleCrypto.GetSecErrorString(osStatus), + StatusInformation = errorString }); } } @@ -492,11 +490,13 @@ namespace Internal.Cryptography.Pal internal readonly X509ChainStatusFlags ChainStatusFlag; internal readonly int OSStatus; + internal readonly string ErrorString; private X509ChainErrorMapping(X509ChainStatusFlags flag) { ChainStatusFlag = flag; OSStatus = Interop.AppleCrypto.GetOSStatusForChainStatus(flag); + ErrorString = Interop.AppleCrypto.GetSecErrorString(OSStatus); } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/CertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/CertificatePal.cs index 6b09a3d..803491b 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/CertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/CertificatePal.cs @@ -248,9 +248,7 @@ namespace Internal.Cryptography.Pal unsafe { CERT_CONTEXT* pCertContext = _certContext.CertContext; - int count = pCertContext->cbCertEncoded; - byte[] rawData = new byte[count]; - Marshal.Copy((IntPtr)(pCertContext->pbCertEncoded), rawData, 0, count); + byte[] rawData = new Span(pCertContext->pbCertEncoded, pCertContext->cbCertEncoded).ToArray(); GC.KeepAlive(this); return rawData; } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/ChainPal.GetChainStatusInformation.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/ChainPal.GetChainStatusInformation.cs index 2f9c40e..a3d363f 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/ChainPal.GetChainStatusInformation.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/ChainPal.GetChainStatusInformation.cs @@ -34,7 +34,7 @@ namespace Internal.Cryptography.Pal { Debug.Assert(index < chainStatus.Length); - chainStatus[index].StatusInformation = Interop.Kernel32.GetMessage(mapping.Win32ErrorCode); + chainStatus[index].StatusInformation = mapping.Message; chainStatus[index].Status = mapping.ChainStatusFlag; index++; dwStatus &= ~mapping.Win32Flag; @@ -65,12 +65,14 @@ namespace Internal.Cryptography.Pal public readonly CertTrustErrorStatus Win32Flag; public readonly int Win32ErrorCode; public readonly X509ChainStatusFlags ChainStatusFlag; + public readonly string Message; public X509ChainErrorMapping(CertTrustErrorStatus win32Flag, int win32ErrorCode, X509ChainStatusFlags chainStatusFlag) { Win32Flag = win32Flag; Win32ErrorCode = win32ErrorCode; ChainStatusFlag = chainStatusFlag; + Message = Interop.Kernel32.GetMessage(win32ErrorCode); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs index ad5f1cc..9741bc4 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate.cs @@ -37,9 +37,11 @@ namespace System.Security.Cryptography.X509Certificates _lazyNotAfter = DateTime.MinValue; ICertificatePalCore pal = Pal; - Pal = null; if (pal != null) + { + Pal = null; pal.Dispose(); + } } public X509Certificate() diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index b833924..614b5aa 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -623,6 +623,12 @@ namespace System.Security.Cryptography.X509Certificates // UrlRetrievalTimeout = new TimeSpan(0, 0, 0) bool verified = chain.Build(this, throwOnException: false); + + for (int i = 0; i < chain.ChainElements.Count; i++) + { + chain.ChainElements[i].Certificate.Dispose(); + } + return verified; } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509ChainPolicy.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509ChainPolicy.cs index 1005578..7bd0f34 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509ChainPolicy.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System/Security/Cryptography/X509Certificates/X509ChainPolicy.cs @@ -2,27 +2,29 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.IO; -using System.Text; -using System.Diagnostics; using System.Globalization; -using System.Runtime.InteropServices; - -using Internal.Cryptography; namespace System.Security.Cryptography.X509Certificates { public sealed class X509ChainPolicy { + private X509RevocationMode _revocationMode; + private X509RevocationFlag _revocationFlag; + private X509VerificationFlags _verificationFlags; + private OidCollection _applicationPolicy; + private OidCollection _certificatePolicy; + private X509Certificate2Collection _extraStore; + public X509ChainPolicy() { Reset(); } - public OidCollection ApplicationPolicy { get; private set; } + public OidCollection ApplicationPolicy => _applicationPolicy ?? (_applicationPolicy = new OidCollection()); + + public OidCollection CertificatePolicy => _certificatePolicy ?? (_certificatePolicy = new OidCollection()); - public OidCollection CertificatePolicy { get; private set; } + public X509Certificate2Collection ExtraStore => _extraStore ?? (_extraStore = new X509Certificate2Collection()); public X509RevocationMode RevocationMode { @@ -70,23 +72,17 @@ namespace System.Security.Cryptography.X509Certificates public TimeSpan UrlRetrievalTimeout { get; set; } - public X509Certificate2Collection ExtraStore { get; private set; } - public void Reset() { - ApplicationPolicy = new OidCollection(); - CertificatePolicy = new OidCollection(); + _applicationPolicy = null; + _certificatePolicy = null; + _extraStore = null; _revocationMode = X509RevocationMode.Online; _revocationFlag = X509RevocationFlag.ExcludeRoot; _verificationFlags = X509VerificationFlags.NoFlag; VerificationTime = DateTime.Now; - UrlRetrievalTimeout = new TimeSpan(0, 0, 0); // default timeout - ExtraStore = new X509Certificate2Collection(); + UrlRetrievalTimeout = TimeSpan.Zero; // default timeout } - - private X509RevocationMode _revocationMode; - private X509RevocationFlag _revocationFlag; - private X509VerificationFlags _verificationFlags; } } -- 2.7.4