Move NTAuthentication/NegotiateStreamPal back to the mothership (#87887)
authorFilip Navara <filip.navara@gmail.com>
Thu, 22 Jun 2023 07:26:45 +0000 (09:26 +0200)
committerGitHub <noreply@github.com>
Thu, 22 Jun 2023 07:26:45 +0000 (09:26 +0200)
14 files changed:
src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs [deleted file]
src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs [deleted file]
src/libraries/System.Net.Mail/tests/Unit/System.Net.Mail.Unit.Tests.csproj
src/libraries/System.Net.Security/src/System.Net.Security.csproj
src/libraries/System.Net.Security/src/System/Net/ContextFlagsAdapterPal.Unix.cs [moved from src/libraries/Common/src/System/Net/ContextFlagsAdapterPal.Unix.cs with 100% similarity]
src/libraries/System.Net.Security/src/System/Net/ContextFlagsAdapterPal.Windows.cs [moved from src/libraries/Common/src/System/Net/ContextFlagsAdapterPal.Windows.cs with 100% similarity]
src/libraries/System.Net.Security/src/System/Net/ContextFlagsPal.cs [moved from src/libraries/Common/src/System/Net/ContextFlagsPal.cs with 100% similarity]
src/libraries/System.Net.Security/src/System/Net/NTAuthentication.Common.cs [moved from src/libraries/Common/src/System/Net/NTAuthentication.Common.cs with 100% similarity]
src/libraries/System.Net.Security/src/System/Net/NTAuthentication.Managed.cs [moved from src/libraries/Common/src/System/Net/NTAuthentication.Managed.cs with 100% similarity]
src/libraries/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs
src/libraries/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Windows.cs
src/libraries/System.Net.Security/src/System/Net/SecurityStatusAdapterPal.Windows.cs [moved from src/libraries/Common/src/System/Net/SecurityStatusAdapterPal.Windows.cs with 100% similarity]
src/libraries/System.Net.Security/src/System/Net/SecurityStatusPal.cs [moved from src/libraries/Common/src/System/Net/SecurityStatusPal.cs with 100% similarity]
src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj

diff --git a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs
deleted file mode 100644 (file)
index 3dd3dac..0000000
+++ /dev/null
@@ -1,649 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.IO;
-using System.Buffers;
-using System.Buffers.Binary;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
-using System.Security;
-using System.Security.Authentication;
-using System.Security.Authentication.ExtendedProtection;
-using System.Security.Principal;
-using System.Text;
-using System.Threading;
-using Microsoft.Win32.SafeHandles;
-
-namespace System.Net.Security
-{
-    //
-    // The class maintains the state of the authentication process and the security context.
-    // It encapsulates security context and does the real work in authentication and
-    // user data encryption with NEGO SSPI package.
-    //
-    internal static partial class NegotiateStreamPal
-    {
-        // value should match the Windows sspicli NTE_FAIL value
-        // defined in winerror.h
-        private const int NTE_FAIL = unchecked((int)0x80090020);
-
-        internal static string QueryContextClientSpecifiedSpn(SafeDeleteContext _ /*securityContext*/)
-        {
-            throw new PlatformNotSupportedException(SR.net_nego_server_not_supported);
-        }
-
-        internal static string QueryContextAuthenticationPackage(SafeDeleteContext securityContext)
-        {
-            SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
-            return negoContext.IsNtlmUsed ? NegotiationInfoClass.NTLM : NegotiationInfoClass.Kerberos;
-        }
-
-        private static byte[] GssWrap(
-            SafeGssContextHandle context,
-            ref bool encrypt,
-            ReadOnlySpan<byte> buffer)
-        {
-            Interop.NetSecurityNative.GssBuffer encryptedBuffer = default;
-            try
-            {
-                Interop.NetSecurityNative.Status minorStatus;
-                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.WrapBuffer(out minorStatus, context, ref encrypt, buffer, ref encryptedBuffer);
-                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
-                {
-                    throw new Interop.NetSecurityNative.GssApiException(status, minorStatus);
-                }
-
-                return encryptedBuffer.ToByteArray();
-            }
-            finally
-            {
-                encryptedBuffer.Dispose();
-            }
-        }
-
-        private static int GssUnwrap(
-            SafeGssContextHandle context,
-            out bool encrypt,
-            Span<byte> buffer)
-        {
-            Interop.NetSecurityNative.GssBuffer decryptedBuffer = default(Interop.NetSecurityNative.GssBuffer);
-            try
-            {
-                Interop.NetSecurityNative.Status minorStatus;
-                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.UnwrapBuffer(out minorStatus, context, out encrypt, buffer, ref decryptedBuffer);
-                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
-                {
-                    throw new Interop.NetSecurityNative.GssApiException(status, minorStatus);
-                }
-
-                decryptedBuffer.Span.CopyTo(buffer);
-                return decryptedBuffer.Span.Length;
-            }
-            finally
-            {
-                decryptedBuffer.Dispose();
-            }
-        }
-
-        private static string GssGetUser(
-            ref SafeGssContextHandle? context)
-        {
-            Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
-
-            try
-            {
-                Interop.NetSecurityNative.Status status
-                    = Interop.NetSecurityNative.GetUser(out var minorStatus,
-                                                        context,
-                                                        ref token);
-
-                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
-                {
-                    throw new Interop.NetSecurityNative.GssApiException(status, minorStatus);
-                }
-
-                ReadOnlySpan<byte> tokenBytes = token.Span;
-                int length = tokenBytes.Length;
-                if (length > 0 && tokenBytes[length - 1] == '\0')
-                {
-                    // Some GSS-API providers (gss-ntlmssp) include the terminating null with strings, so skip that.
-                    tokenBytes = tokenBytes.Slice(0, length - 1);
-                }
-
-#if NETSTANDARD2_0
-                return Encoding.UTF8.GetString(tokenBytes.ToArray(), 0, tokenBytes.Length);
-#else
-                return Encoding.UTF8.GetString(tokenBytes);
-#endif
-            }
-            finally
-            {
-                token.Dispose();
-            }
-        }
-
-        private static SecurityStatusPal EstablishSecurityContext(
-          SafeFreeNegoCredentials credential,
-          ref SafeDeleteContext? context,
-          ChannelBinding? channelBinding,
-          string? targetName,
-          ContextFlagsPal inFlags,
-          ReadOnlySpan<byte> incomingBlob,
-          out byte[]? resultBuffer,
-          ref ContextFlagsPal outFlags)
-        {
-            Interop.NetSecurityNative.PackageType packageType = credential.PackageType;
-
-            resultBuffer = null;
-
-            if (context == null)
-            {
-                if (NetEventSource.Log.IsEnabled())
-                {
-                    string protocol = packageType switch {
-                        Interop.NetSecurityNative.PackageType.NTLM => "NTLM",
-                        Interop.NetSecurityNative.PackageType.Kerberos => "Kerberos",
-                        _ => "SPNEGO"
-                    };
-                    NetEventSource.Info(context, $"requested protocol = {protocol}, target = {targetName}");
-                }
-
-                context = new SafeDeleteNegoContext(credential, targetName!);
-            }
-
-            Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
-            Interop.NetSecurityNative.Status status;
-            Interop.NetSecurityNative.Status minorStatus;
-            SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
-            SafeGssContextHandle contextHandle = negoContext.GssContext;
-            try
-            {
-                Interop.NetSecurityNative.GssFlags inputFlags =
-                    ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
-                uint outputFlags;
-                bool isNtlmUsed;
-
-                if (channelBinding != null)
-                {
-                    // If a TLS channel binding token (cbt) is available then get the pointer
-                    // to the application specific data.
-                    int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
-                    Debug.Assert(appDataOffset < channelBinding.Size);
-                    IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
-                    int cbtAppDataSize = channelBinding.Size - appDataOffset;
-                    status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
-                                                                      credential.GssCredential,
-                                                                      ref contextHandle,
-                                                                      packageType,
-                                                                      cbtAppData,
-                                                                      cbtAppDataSize,
-                                                                      negoContext.TargetName,
-                                                                      (uint)inputFlags,
-                                                                      incomingBlob,
-                                                                      ref token,
-                                                                      out outputFlags,
-                                                                      out isNtlmUsed);
-                }
-                else
-                {
-                    status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
-                                                                      credential.GssCredential,
-                                                                      ref contextHandle,
-                                                                      packageType,
-                                                                      negoContext.TargetName,
-                                                                      (uint)inputFlags,
-                                                                      incomingBlob,
-                                                                      ref token,
-                                                                      out outputFlags,
-                                                                      out isNtlmUsed);
-                }
-
-                if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
-                    (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
-                {
-                    if (negoContext.GssContext.IsInvalid)
-                    {
-                        context.Dispose();
-                    }
-
-                    Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
-                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
-                    resultBuffer = Array.Empty<byte>();
-                    return new SecurityStatusPal(GetErrorCode(gex), gex);
-                }
-
-                resultBuffer = token.ToByteArray();
-
-                if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
-                {
-                    if (NetEventSource.Log.IsEnabled())
-                    {
-                        string protocol = packageType switch {
-                            Interop.NetSecurityNative.PackageType.NTLM => "NTLM",
-                            Interop.NetSecurityNative.PackageType.Kerberos => "Kerberos",
-                            _ => isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos"
-                        };
-                        NetEventSource.Info(context, $"actual protocol = {protocol}");
-                    }
-
-                    // Populate protocol used for authentication
-                    negoContext.SetAuthenticationPackage(isNtlmUsed);
-                }
-
-                Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
-                outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
-                    (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
-
-                SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
-                    SecurityStatusPalErrorCode.OK :
-                    SecurityStatusPalErrorCode.ContinueNeeded;
-                return new SecurityStatusPal(errorCode);
-            }
-            catch (Exception ex)
-            {
-                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
-                return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
-            }
-            finally
-            {
-                token.Dispose();
-
-                // Save the inner context handle for further calls to NetSecurity
-                //
-                // For the first call `negoContext.GssContext` is invalid and we expect the
-                // inital handle to be returned from InitSecContext. For any subsequent
-                // call the handle should stay the same or it can be destroyed by the native
-                // InitSecContext call.
-                Debug.Assert(
-                    negoContext.GssContext == contextHandle ||
-                    negoContext.GssContext.IsInvalid ||
-                    contextHandle.IsInvalid);
-                negoContext.SetGssContext(contextHandle);
-            }
-        }
-
-        internal static SecurityStatusPal InitializeSecurityContext(
-            ref SafeFreeCredentials credentialsHandle,
-            ref SafeDeleteContext? securityContext,
-            string? spn,
-            ContextFlagsPal requestedContextFlags,
-            ReadOnlySpan<byte> incomingBlob,
-            ChannelBinding? channelBinding,
-            ref byte[]? resultBlob,
-            out int resultBlobLength,
-            ref ContextFlagsPal contextFlags)
-        {
-            SafeFreeNegoCredentials negoCredentialsHandle = (SafeFreeNegoCredentials)credentialsHandle;
-
-            if (negoCredentialsHandle.IsDefault && string.IsNullOrEmpty(spn))
-            {
-                throw new PlatformNotSupportedException(SR.net_nego_not_supported_empty_target_with_defaultcreds);
-            }
-
-            SecurityStatusPal status = EstablishSecurityContext(
-                negoCredentialsHandle,
-                ref securityContext,
-                channelBinding,
-                spn,
-                requestedContextFlags,
-                incomingBlob,
-                out resultBlob,
-                ref contextFlags);
-            resultBlobLength = resultBlob?.Length ?? 0;
-
-            return status;
-        }
-
-#pragma warning disable IDE0060
-        internal static SecurityStatusPal AcceptSecurityContext(
-            SafeFreeCredentials? credentialsHandle,
-            ref SafeDeleteContext? securityContext,
-            ContextFlagsPal requestedContextFlags,
-            ReadOnlySpan<byte> incomingBlob,
-            ChannelBinding? channelBinding,
-            ref byte[] resultBlob,
-            out int resultBlobLength,
-            ref ContextFlagsPal contextFlags)
-        {
-            securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle!);
-
-            SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
-            SafeGssContextHandle contextHandle = negoContext.GssContext;
-            Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
-            try
-            {
-                Interop.NetSecurityNative.Status status;
-                Interop.NetSecurityNative.Status minorStatus;
-                status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
-                                                                    negoContext.AcceptorCredential,
-                                                                    ref contextHandle,
-                                                                    incomingBlob,
-                                                                    ref token,
-                                                                    out uint outputFlags,
-                                                                    out bool isNtlmUsed);
-
-                if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
-                    (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
-                {
-                    if (negoContext.GssContext.IsInvalid)
-                    {
-                        contextHandle.Dispose();
-                    }
-
-                    Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
-                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
-                    resultBlobLength = 0;
-                    return new SecurityStatusPal(GetErrorCode(gex), gex);
-                }
-
-                resultBlob = token.ToByteArray();
-
-                Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
-
-                contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
-                    (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
-                resultBlobLength = resultBlob.Length;
-
-                SecurityStatusPalErrorCode errorCode;
-                if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
-                {
-                    if (NetEventSource.Log.IsEnabled())
-                    {
-                        string protocol = isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos";
-                        NetEventSource.Info(securityContext, $"AcceptSecurityContext: actual protocol = {protocol}");
-                    }
-
-                    negoContext.SetAuthenticationPackage(isNtlmUsed);
-                    errorCode = SecurityStatusPalErrorCode.OK;
-                }
-                else
-                {
-                    errorCode = SecurityStatusPalErrorCode.ContinueNeeded;
-                }
-
-                return new SecurityStatusPal(errorCode);
-            }
-            catch (Exception ex)
-            {
-                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
-                resultBlobLength = 0;
-                return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
-            }
-            finally
-            {
-                token.Dispose();
-
-                // Save the inner context handle for further calls to NetSecurity
-                //
-                // For the first call `negoContext.GssContext` is invalid and we expect the
-                // inital handle to be returned from AcceptSecContext. For any subsequent
-                // call the handle should stay the same or it can be destroyed by the native
-                // AcceptSecContext call.
-                Debug.Assert(
-                    negoContext.GssContext == contextHandle ||
-                    negoContext.GssContext.IsInvalid ||
-                    contextHandle.IsInvalid);
-                negoContext.SetGssContext(contextHandle);
-            }
-        }
-#pragma warning restore IDE0060
-
-        // https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
-        private static SecurityStatusPalErrorCode GetErrorCode(Interop.NetSecurityNative.GssApiException exception)
-        {
-            switch (exception.MajorStatus)
-            {
-                case Interop.NetSecurityNative.Status.GSS_S_NO_CRED:
-                    return SecurityStatusPalErrorCode.UnknownCredentials;
-                case Interop.NetSecurityNative.Status.GSS_S_BAD_BINDINGS:
-                    return SecurityStatusPalErrorCode.BadBinding;
-                case Interop.NetSecurityNative.Status.GSS_S_CREDENTIALS_EXPIRED:
-                    return SecurityStatusPalErrorCode.CertExpired;
-                case Interop.NetSecurityNative.Status.GSS_S_DEFECTIVE_TOKEN:
-                    return SecurityStatusPalErrorCode.InvalidToken;
-                case Interop.NetSecurityNative.Status.GSS_S_DEFECTIVE_CREDENTIAL:
-                    return SecurityStatusPalErrorCode.IncompleteCredentials;
-                case Interop.NetSecurityNative.Status.GSS_S_BAD_SIG:
-                    return SecurityStatusPalErrorCode.MessageAltered;
-                case Interop.NetSecurityNative.Status.GSS_S_BAD_MECH:
-                    return SecurityStatusPalErrorCode.Unsupported;
-                case Interop.NetSecurityNative.Status.GSS_S_NO_CONTEXT:
-                default:
-                    return SecurityStatusPalErrorCode.InternalError;
-            }
-        }
-
-        private static string GetUser(
-            ref SafeDeleteContext securityContext)
-        {
-            SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
-            try
-            {
-                SafeGssContextHandle? contextHandle = negoContext.GssContext;
-                return GssGetUser(ref contextHandle);
-            }
-            catch (Exception ex)
-            {
-                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
-                throw;
-            }
-        }
-
-        internal static Win32Exception CreateExceptionFromError(SecurityStatusPal statusCode)
-        {
-            return new Win32Exception(NTE_FAIL, (statusCode.Exception != null) ? statusCode.Exception.Message : statusCode.ErrorCode.ToString());
-        }
-
-#pragma warning disable IDE0060
-        internal static int QueryMaxTokenSize(string package)
-        {
-            // This value is not used on Unix
-            return 0;
-        }
-#pragma warning restore IDE0060
-
-        internal static SafeFreeCredentials AcquireDefaultCredential(string package, bool isServer)
-        {
-            return AcquireCredentialsHandle(package, isServer, new NetworkCredential(string.Empty, string.Empty, string.Empty));
-        }
-
-        internal static SafeFreeCredentials AcquireCredentialsHandle(string package, bool isServer, NetworkCredential credential)
-        {
-            bool isEmptyCredential = string.IsNullOrWhiteSpace(credential.UserName) ||
-                                     string.IsNullOrWhiteSpace(credential.Password);
-            Interop.NetSecurityNative.PackageType packageType;
-
-            if (string.Equals(package, NegotiationInfoClass.Negotiate, StringComparison.OrdinalIgnoreCase))
-            {
-                packageType = Interop.NetSecurityNative.PackageType.Negotiate;
-            }
-            else if (string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase))
-            {
-                packageType = Interop.NetSecurityNative.PackageType.NTLM;
-                if (isEmptyCredential && !isServer)
-                {
-                    // NTLM authentication is not possible with default credentials which are no-op
-                    throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred);
-                }
-            }
-            else if (string.Equals(package, NegotiationInfoClass.Kerberos, StringComparison.OrdinalIgnoreCase))
-            {
-                packageType = Interop.NetSecurityNative.PackageType.Kerberos;
-            }
-            else
-            {
-                // Native shim currently supports only NTLM, Negotiate and Kerberos
-                throw new PlatformNotSupportedException(SR.net_securitypackagesupport);
-            }
-
-            try
-            {
-                return isEmptyCredential ?
-                    new SafeFreeNegoCredentials(packageType, string.Empty, string.Empty, string.Empty) :
-                    new SafeFreeNegoCredentials(packageType, credential.UserName, credential.Password, credential.Domain);
-            }
-            catch (Exception ex)
-            {
-                throw new Win32Exception(NTE_FAIL, ex.Message);
-            }
-        }
-
-#pragma warning disable IDE0060
-        internal static SecurityStatusPal CompleteAuthToken(
-            ref SafeDeleteContext? securityContext,
-            ReadOnlySpan<byte> incomingBlob)
-        {
-            return new SecurityStatusPal(SecurityStatusPalErrorCode.OK);
-        }
-#pragma warning restore IDE0060
-
-        internal static NegotiateAuthenticationStatusCode Unwrap(
-            SafeDeleteContext securityContext,
-            ReadOnlySpan<byte> input,
-            IBufferWriter<byte> outputWriter,
-            out bool isEncrypted)
-        {
-            SafeGssContextHandle gssContext = ((SafeDeleteNegoContext)securityContext).GssContext!;
-            Interop.NetSecurityNative.GssBuffer decryptedBuffer = default(Interop.NetSecurityNative.GssBuffer);
-            try
-            {
-                Interop.NetSecurityNative.Status minorStatus;
-                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.UnwrapBuffer(out minorStatus, gssContext, out isEncrypted, input, ref decryptedBuffer);
-                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
-                {
-                    return status switch
-                    {
-                        Interop.NetSecurityNative.Status.GSS_S_BAD_SIG => NegotiateAuthenticationStatusCode.MessageAltered,
-                        _ => NegotiateAuthenticationStatusCode.InvalidToken
-                    };
-                }
-
-                decryptedBuffer.Span.CopyTo(outputWriter.GetSpan(decryptedBuffer.Span.Length));
-                outputWriter.Advance(decryptedBuffer.Span.Length);
-                return NegotiateAuthenticationStatusCode.Completed;
-            }
-            finally
-            {
-                decryptedBuffer.Dispose();
-            }
-        }
-
-        internal static NegotiateAuthenticationStatusCode UnwrapInPlace(
-            SafeDeleteContext securityContext,
-            Span<byte> input,
-            out int unwrappedOffset,
-            out int unwrappedLength,
-            out bool isEncrypted)
-        {
-            SafeGssContextHandle gssContext = ((SafeDeleteNegoContext)securityContext).GssContext!;
-            Interop.NetSecurityNative.GssBuffer decryptedBuffer = default(Interop.NetSecurityNative.GssBuffer);
-            try
-            {
-                Interop.NetSecurityNative.Status minorStatus;
-                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.UnwrapBuffer(out minorStatus, gssContext, out isEncrypted, input, ref decryptedBuffer);
-                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
-                {
-                    unwrappedOffset = 0;
-                    unwrappedLength = 0;
-                    return status switch
-                    {
-                        Interop.NetSecurityNative.Status.GSS_S_BAD_SIG => NegotiateAuthenticationStatusCode.MessageAltered,
-                        _ => NegotiateAuthenticationStatusCode.InvalidToken
-                    };
-                }
-
-                decryptedBuffer.Span.CopyTo(input);
-                unwrappedOffset = 0;
-                unwrappedLength = decryptedBuffer.Span.Length;
-                return NegotiateAuthenticationStatusCode.Completed;
-            }
-            finally
-            {
-                decryptedBuffer.Dispose();
-            }
-        }
-
-        internal static NegotiateAuthenticationStatusCode Wrap(
-            SafeDeleteContext securityContext,
-            ReadOnlySpan<byte> input,
-            IBufferWriter<byte> outputWriter,
-            bool requestEncryption,
-            out bool isEncrypted)
-        {
-            SafeGssContextHandle gssContext = ((SafeDeleteNegoContext)securityContext).GssContext!;
-            Interop.NetSecurityNative.GssBuffer encryptedBuffer = default;
-            try
-            {
-                Interop.NetSecurityNative.Status minorStatus;
-                bool encrypt = requestEncryption;
-                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.WrapBuffer(
-                    out minorStatus,
-                    gssContext,
-                    ref encrypt,
-                    input,
-                    ref encryptedBuffer);
-                isEncrypted = encrypt;
-                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
-                {
-                    return NegotiateAuthenticationStatusCode.GenericFailure;
-                }
-
-                encryptedBuffer.Span.CopyTo(outputWriter.GetSpan(encryptedBuffer.Span.Length));
-                outputWriter.Advance(encryptedBuffer.Span.Length);
-                return NegotiateAuthenticationStatusCode.Completed;
-            }
-            finally
-            {
-                encryptedBuffer.Dispose();
-            }
-        }
-
-        internal static bool VerifyMIC(
-            SafeDeleteContext securityContext,
-            bool isConfidential,
-            ReadOnlySpan<byte> message,
-            ReadOnlySpan<byte> signature)
-        {
-            _ = isConfidential;
-            SafeGssContextHandle gssContext = ((SafeDeleteNegoContext)securityContext).GssContext!;
-            Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.VerifyMic(
-                out _,
-                gssContext,
-                message,
-                signature);
-            return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
-        }
-
-        internal static void GetMIC(
-            SafeDeleteContext securityContext,
-            bool isConfidential,
-            ReadOnlySpan<byte> message,
-            IBufferWriter<byte> signature)
-        {
-            _ = isConfidential;
-            SafeGssContextHandle gssContext = ((SafeDeleteNegoContext)securityContext).GssContext!;
-            Interop.NetSecurityNative.GssBuffer micBuffer = default;
-            try
-            {
-                Interop.NetSecurityNative.Status minorStatus;
-                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.GetMic(
-                    out minorStatus,
-                    gssContext,
-                    message,
-                    ref micBuffer);
-                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
-                {
-                    throw new Interop.NetSecurityNative.GssApiException(status, minorStatus);
-                }
-
-                signature.Write(micBuffer.Span);
-            }
-            finally
-            {
-                micBuffer.Dispose();
-            }
-        }
-    }
-}
diff --git a/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs b/src/libraries/Common/src/System/Net/Security/NegotiateStreamPal.Windows.cs
deleted file mode 100644 (file)
index 2b42c70..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Buffers.Binary;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
-using System.Runtime.InteropServices;
-using System.Security.Authentication.ExtendedProtection;
-
-namespace System.Net.Security
-{
-    //
-    // The class does the real work in authentication and
-    // user data encryption with NEGO SSPI package.
-    //
-    // This is part of the NegotiateStream PAL.
-    //
-    internal static partial class NegotiateStreamPal
-    {
-        internal static int QueryMaxTokenSize(string package)
-        {
-            return SSPIWrapper.GetVerifyPackageInfo(GlobalSSPI.SSPIAuth, package, true)!.MaxToken;
-        }
-
-        internal static SafeFreeCredentials AcquireDefaultCredential(string package, bool isServer)
-        {
-            return SSPIWrapper.AcquireDefaultCredential(
-                GlobalSSPI.SSPIAuth,
-                package,
-                (isServer ? Interop.SspiCli.CredentialUse.SECPKG_CRED_INBOUND : Interop.SspiCli.CredentialUse.SECPKG_CRED_OUTBOUND));
-        }
-
-        internal static SafeFreeCredentials AcquireCredentialsHandle(string package, bool isServer, NetworkCredential credential)
-        {
-            SafeSspiAuthDataHandle? authData = null;
-            try
-            {
-                Interop.SECURITY_STATUS result = Interop.SspiCli.SspiEncodeStringsAsAuthIdentity(
-                    credential.UserName, credential.Domain,
-                    credential.Password, out authData);
-
-                if (result != Interop.SECURITY_STATUS.OK)
-                {
-                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, SR.Format(SR.net_log_operation_failed_with_error, nameof(Interop.SspiCli.SspiEncodeStringsAsAuthIdentity), $"0x{(int)result:X}"));
-                    throw new Win32Exception((int)result);
-                }
-
-                return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPIAuth,
-                    package, (isServer ? Interop.SspiCli.CredentialUse.SECPKG_CRED_INBOUND : Interop.SspiCli.CredentialUse.SECPKG_CRED_OUTBOUND), ref authData);
-            }
-            finally
-            {
-                authData?.Dispose();
-            }
-        }
-
-        internal static string? QueryContextAssociatedName(SafeDeleteContext securityContext)
-        {
-            return SSPIWrapper.QueryStringContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NAMES);
-        }
-
-        internal static string? QueryContextClientSpecifiedSpn(SafeDeleteContext securityContext)
-        {
-            return SSPIWrapper.QueryStringContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CLIENT_SPECIFIED_TARGET);
-        }
-
-        internal static string? QueryContextAuthenticationPackage(SafeDeleteContext securityContext)
-        {
-            SecPkgContext_NegotiationInfoW ctx = default;
-            bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NEGOTIATION_INFO, typeof(SafeFreeContextBuffer), out SafeHandle? sspiHandle, ref ctx);
-            using (sspiHandle)
-            {
-                return success ? NegotiationInfoClass.GetAuthenticationPackageName(sspiHandle!, (int)ctx.NegotiationState) : null;
-            }
-        }
-
-        internal static SecurityStatusPal InitializeSecurityContext(
-            ref SafeFreeCredentials? credentialsHandle,
-            ref SafeDeleteContext? securityContext,
-            string? spn,
-            ContextFlagsPal requestedContextFlags,
-            ReadOnlySpan<byte> incomingBlob,
-            ChannelBinding? channelBinding,
-            ref byte[]? resultBlob,
-            out int resultBlobLength,
-            ref ContextFlagsPal contextFlags)
-        {
-
-            InputSecurityBuffers inputBuffers = default;
-            if (!incomingBlob.IsEmpty)
-            {
-                inputBuffers.SetNextBuffer(new InputSecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN));
-            }
-
-            if (channelBinding != null)
-            {
-                inputBuffers.SetNextBuffer(new InputSecurityBuffer(channelBinding));
-            }
-
-            var outSecurityBuffer = new SecurityBuffer(resultBlob, SecurityBufferType.SECBUFFER_TOKEN);
-
-            Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero;
-            // There is only one SafeDeleteContext type on Windows which is SafeDeleteSslContext so this cast is safe.
-            SafeDeleteSslContext? sslContext = (SafeDeleteSslContext?)securityContext;
-            Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.InitializeSecurityContext(
-                GlobalSSPI.SSPIAuth,
-                ref credentialsHandle,
-                ref sslContext,
-                spn,
-                ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(requestedContextFlags),
-                Interop.SspiCli.Endianness.SECURITY_NETWORK_DREP,
-                inputBuffers,
-                ref outSecurityBuffer,
-                ref outContextFlags);
-            securityContext = sslContext;
-            Debug.Assert(outSecurityBuffer.offset == 0);
-            resultBlob = outSecurityBuffer.token;
-            resultBlobLength = outSecurityBuffer.size;
-            contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(outContextFlags);
-            return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus);
-        }
-
-        internal static SecurityStatusPal CompleteAuthToken(
-            ref SafeDeleteContext? securityContext,
-            ReadOnlySpan<byte> incomingBlob)
-        {
-            // There is only one SafeDeleteContext type on Windows which is SafeDeleteSslContext so this cast is safe.
-            SafeDeleteSslContext? sslContext = (SafeDeleteSslContext?)securityContext;
-            var inSecurityBuffer = new InputSecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN);
-            Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.CompleteAuthToken(
-                GlobalSSPI.SSPIAuth,
-                ref sslContext,
-                in inSecurityBuffer);
-            securityContext = sslContext;
-            return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus);
-        }
-
-        internal static SecurityStatusPal AcceptSecurityContext(
-            SafeFreeCredentials? credentialsHandle,
-            ref SafeDeleteContext? securityContext,
-            ContextFlagsPal requestedContextFlags,
-            ReadOnlySpan<byte> incomingBlob,
-            ChannelBinding? channelBinding,
-            ref byte[]? resultBlob,
-            out int resultBlobLength,
-            ref ContextFlagsPal contextFlags)
-        {
-            InputSecurityBuffers inputBuffers = default;
-            if (!incomingBlob.IsEmpty)
-            {
-                inputBuffers.SetNextBuffer(new InputSecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN));
-            }
-
-            if (channelBinding != null)
-            {
-                inputBuffers.SetNextBuffer(new InputSecurityBuffer(channelBinding));
-            }
-
-            var outSecurityBuffer = new SecurityBuffer(resultBlob, SecurityBufferType.SECBUFFER_TOKEN);
-
-            Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero;
-            // There is only one SafeDeleteContext type on Windows which is SafeDeleteSslContext so this cast is safe.
-            SafeDeleteSslContext? sslContext = (SafeDeleteSslContext?)securityContext;
-            Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.AcceptSecurityContext(
-                GlobalSSPI.SSPIAuth,
-                credentialsHandle,
-                ref sslContext,
-                ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(requestedContextFlags),
-                Interop.SspiCli.Endianness.SECURITY_NETWORK_DREP,
-                inputBuffers,
-                ref outSecurityBuffer,
-                ref outContextFlags);
-
-            // SSPI Workaround
-            // If a client sends up a blob on the initial request, Negotiate returns SEC_E_INVALID_HANDLE
-            // when it should return SEC_E_INVALID_TOKEN.
-            if (winStatus == Interop.SECURITY_STATUS.InvalidHandle && securityContext == null && !incomingBlob.IsEmpty)
-            {
-                winStatus = Interop.SECURITY_STATUS.InvalidToken;
-            }
-
-            Debug.Assert(outSecurityBuffer.offset == 0);
-            resultBlob = outSecurityBuffer.token;
-            resultBlobLength = outSecurityBuffer.size;
-            securityContext = sslContext;
-            contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(outContextFlags);
-            return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus);
-        }
-
-        internal static Win32Exception CreateExceptionFromError(SecurityStatusPal statusCode)
-        {
-            return new Win32Exception((int)SecurityStatusAdapterPal.GetInteropFromSecurityStatusPal(statusCode));
-        }
-
-        internal static NegotiateAuthenticationStatusCode Unwrap(
-            SafeDeleteContext securityContext,
-            ReadOnlySpan<byte> input,
-            IBufferWriter<byte> outputWriter,
-            out bool wasEncrypted)
-        {
-            Span<byte> outputBuffer = outputWriter.GetSpan(input.Length).Slice(0, input.Length);
-            NegotiateAuthenticationStatusCode statusCode;
-
-            input.CopyTo(outputBuffer);
-            statusCode = UnwrapInPlace(securityContext, outputBuffer, out int unwrappedOffset, out int unwrappedLength, out wasEncrypted);
-
-            if (statusCode == NegotiateAuthenticationStatusCode.Completed)
-            {
-                if (unwrappedOffset > 0)
-                {
-                    outputBuffer.Slice(unwrappedOffset, unwrappedLength).CopyTo(outputBuffer);
-                }
-                outputWriter.Advance(unwrappedLength);
-            }
-
-            return statusCode;
-        }
-
-        internal static unsafe NegotiateAuthenticationStatusCode UnwrapInPlace(
-            SafeDeleteContext securityContext,
-            Span<byte> input,
-            out int unwrappedOffset,
-            out int unwrappedLength,
-            out bool wasEncrypted)
-        {
-            fixed (byte* inputPtr = input)
-            {
-                Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
-                Interop.SspiCli.SecBuffer* streamBuffer = &unmanagedBuffer[0];
-                Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[1];
-                streamBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM;
-                streamBuffer->pvBuffer = (IntPtr)inputPtr;
-                streamBuffer->cbBuffer = input.Length;
-                dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
-                dataBuffer->pvBuffer = IntPtr.Zero;
-                dataBuffer->cbBuffer = 0;
-
-                Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
-                {
-                    pBuffers = unmanagedBuffer
-                };
-
-                uint qop;
-                int errorCode = GlobalSSPI.SSPIAuth.DecryptMessage(securityContext, ref sdcInOut, out qop);
-                if (errorCode != 0)
-                {
-                    unwrappedOffset = 0;
-                    unwrappedLength = 0;
-                    wasEncrypted = false;
-                    return errorCode switch
-                    {
-                        (int)Interop.SECURITY_STATUS.MessageAltered => NegotiateAuthenticationStatusCode.MessageAltered,
-                        _ => NegotiateAuthenticationStatusCode.InvalidToken
-                    };
-                }
-
-                if (dataBuffer->BufferType != SecurityBufferType.SECBUFFER_DATA)
-                {
-                    throw new InternalException(dataBuffer->BufferType);
-                }
-
-                wasEncrypted = qop != Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT;
-
-                Debug.Assert((nint)dataBuffer->pvBuffer >= (nint)inputPtr);
-                Debug.Assert((nint)dataBuffer->pvBuffer + dataBuffer->cbBuffer <= (nint)inputPtr + input.Length);
-                unwrappedOffset = (int)((byte*)dataBuffer->pvBuffer - inputPtr);
-                unwrappedLength = dataBuffer->cbBuffer;
-                return NegotiateAuthenticationStatusCode.Completed;
-            }
-        }
-
-        internal static unsafe NegotiateAuthenticationStatusCode Wrap(
-            SafeDeleteContext securityContext,
-            ReadOnlySpan<byte> input,
-            IBufferWriter<byte> outputWriter,
-            bool requestEncryption,
-            out bool isEncrypted)
-        {
-            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 = input.Length + sizes.cbMaxSignature;
-            Span<byte> outputBuffer = outputWriter.GetSpan(resultSize);
-
-            // make a copy of user data for in-place encryption
-            input.CopyTo(outputBuffer.Slice(sizes.cbMaxSignature, input.Length));
-
-            isEncrypted = requestEncryption;
-
-            fixed (byte* outputPtr = outputBuffer)
-            {
-                // Prepare buffers TOKEN(signature), DATA and Padding.
-                Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
-                Interop.SspiCli.SecBuffer* tokenBuffer = &unmanagedBuffer[0];
-                Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[1];
-                tokenBuffer->BufferType = SecurityBufferType.SECBUFFER_TOKEN;
-                tokenBuffer->pvBuffer = (IntPtr)(outputPtr);
-                tokenBuffer->cbBuffer = sizes.cbMaxSignature;
-                dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
-                dataBuffer->pvBuffer = (IntPtr)(outputPtr + sizes.cbMaxSignature);
-                dataBuffer->cbBuffer = input.Length;
-
-                Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
-                {
-                    pBuffers = unmanagedBuffer
-                };
-
-                uint qop = requestEncryption ? 0 : Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT;
-                int errorCode = GlobalSSPI.SSPIAuth.EncryptMessage(securityContext, ref sdcInOut, qop);
-
-                if (errorCode != 0)
-                {
-                    return errorCode switch
-                    {
-                        (int)Interop.SECURITY_STATUS.ContextExpired => NegotiateAuthenticationStatusCode.ContextExpired,
-                        (int)Interop.SECURITY_STATUS.QopNotSupported => NegotiateAuthenticationStatusCode.QopNotSupported,
-                        _ => NegotiateAuthenticationStatusCode.GenericFailure,
-                    };
-                }
-
-                outputWriter.Advance(tokenBuffer->cbBuffer + dataBuffer->cbBuffer);
-                return NegotiateAuthenticationStatusCode.Completed;
-            }
-        }
-
-        internal static unsafe bool VerifyMIC(
-            SafeDeleteContext securityContext,
-            bool isConfidential,
-            ReadOnlySpan<byte> message,
-            ReadOnlySpan<byte> signature)
-        {
-            bool refAdded = false;
-
-            try
-            {
-                securityContext.DangerousAddRef(ref refAdded);
-
-                fixed (byte* messagePtr = message)
-                fixed (byte* signaturePtr = signature)
-                {
-                    Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
-                    Interop.SspiCli.SecBuffer* tokenBuffer = &unmanagedBuffer[0];
-                    Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[1];
-                    tokenBuffer->BufferType = SecurityBufferType.SECBUFFER_TOKEN;
-                    tokenBuffer->pvBuffer = (IntPtr)signaturePtr;
-                    tokenBuffer->cbBuffer = signature.Length;
-                    dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
-                    dataBuffer->pvBuffer = (IntPtr)messagePtr;
-                    dataBuffer->cbBuffer = message.Length;
-
-                    Interop.SspiCli.SecBufferDesc sdcIn = new Interop.SspiCli.SecBufferDesc(2)
-                    {
-                        pBuffers = unmanagedBuffer
-                    };
-
-                    uint qop;
-                    int errorCode = Interop.SspiCli.VerifySignature(ref securityContext._handle, in sdcIn, 0, &qop);
-
-                    if (errorCode != 0)
-                    {
-                        Exception e = new Win32Exception(errorCode);
-                        if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, e);
-                        throw new Win32Exception(errorCode);
-                    }
-
-                    if (isConfidential && qop == Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT)
-                    {
-                        Debug.Fail($"Expected qop = 0, returned value = {qop}");
-                        throw new InvalidOperationException(SR.net_auth_message_not_encrypted);
-                    }
-
-                    return true;
-                }
-            }
-            finally
-            {
-                if (refAdded)
-                {
-                    securityContext.DangerousRelease();
-                }
-            }
-        }
-
-        internal static unsafe void GetMIC(
-            SafeDeleteContext securityContext,
-            bool isConfidential,
-            ReadOnlySpan<byte> message,
-            IBufferWriter<byte> signature)
-        {
-            bool refAdded = false;
-
-            try
-            {
-                securityContext.DangerousAddRef(ref refAdded);
-
-                SecPkgContext_Sizes sizes = default;
-                bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes);
-                Debug.Assert(success);
-
-                Span<byte> signatureBuffer = signature.GetSpan(sizes.cbSecurityTrailer);
-
-                fixed (byte* messagePtr = message)
-                fixed (byte* signaturePtr = signatureBuffer)
-                {
-                    // Prepare buffers TOKEN(signature), DATA.
-                    Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
-                    Interop.SspiCli.SecBuffer* tokenBuffer = &unmanagedBuffer[0];
-                    Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[1];
-                    tokenBuffer->BufferType = SecurityBufferType.SECBUFFER_TOKEN;
-                    tokenBuffer->pvBuffer = (IntPtr)signaturePtr;
-                    tokenBuffer->cbBuffer = sizes.cbSecurityTrailer;
-                    dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
-                    dataBuffer->pvBuffer = (IntPtr)messagePtr;
-                    dataBuffer->cbBuffer = message.Length;
-
-                    Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
-                    {
-                        pBuffers = unmanagedBuffer
-                    };
-
-                    uint qop = isConfidential ? 0 : Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT;
-                    int errorCode = Interop.SspiCli.MakeSignature(ref securityContext._handle, qop, ref sdcInOut, 0);
-
-                    if (errorCode != 0)
-                    {
-                        Exception e = new Win32Exception(errorCode);
-                        if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, e);
-                        throw new Win32Exception(errorCode);
-                    }
-
-                    signature.Advance(signatureBuffer.Length);
-                }
-            }
-            finally
-            {
-                if (refAdded)
-                {
-                    securityContext.DangerousRelease();
-                }
-            }
-        }
-    }
-}
index 1f2f599..9ea5763 100644 (file)
              Link="Common\System\Collections\Generic\BidirectionalDictionary.cs" />
     <Compile Include="$(CommonPath)System\NotImplemented.cs"
              Link="Common\System\NotImplemented.cs" />
-    <Compile Include="$(CommonPath)System\Net\ContextFlagsPal.cs"
-             Link="Common\System\Net\ContextFlagsPal.cs" />
     <Compile Include="$(CommonPath)System\Net\NegotiationInfoClass.cs"
              Link="Common\System\Net\NegotiationInfoClass.cs" />
-    <Compile Include="$(CommonPath)System\Net\SecurityStatusPal.cs"
-             Link="Common\System\Net\SecurityStatusPal.cs" />
     <Compile Include="$(CommonPath)System\HexConverter.cs"
              Link="Common\System\HexConverter.cs" />
     <Compile Include="$(CommonPath)System\Obsoletions.cs" 
index bfedc0d..567656d 100644 (file)
@@ -48,6 +48,8 @@
     <Compile Include="System\Net\Security\TlsAlertType.cs" />
     <Compile Include="System\Net\Security\TlsFrameHelper.cs" />
     <!-- NegotiateStream -->
+    <Compile Include="System\Net\ContextFlagsPal.cs" />
+    <Compile Include="System\Net\SecurityStatusPal.cs" />
     <Compile Include="System\Net\NTAuthentication.cs" />
     <Compile Include="System\Net\StreamFramer.cs" />
     <Compile Include="System\Net\Security\NegotiateStream.cs" />
              Link="Common\System\Net\Security\SafeCredentialReference.cs" />
     <Compile Include="$(CommonPath)System\Net\Security\SSPIHandleCache.cs"
              Link="Common\System\Net\Security\SSPIHandleCache.cs" />
-    <Compile Include="$(CommonPath)System\Net\ContextFlagsPal.cs"
-             Link="Common\System\Net\ContextFlagsPal.cs" />
     <Compile Include="$(CommonPath)System\Net\NegotiationInfoClass.cs"
              Link="Common\System\Net\NegotiationInfoClass.cs" />
-    <Compile Include="$(CommonPath)System\Net\NTAuthentication.Common.cs"
-             Link="Common\System\Net\NTAuthentication.Common.cs"
+    <Compile Include="System\Net\NTAuthentication.Common.cs"
              Condition="'$(UseManagedNtlm)' != 'true'" />
-    <Compile Include="$(CommonPath)System\Net\SecurityStatusPal.cs"
-             Link="Common\System\Net\SecurityStatusPal.cs" />
     <Compile Include="$(CommonPath)System\HexConverter.cs"
              Link="Common\System\HexConverter.cs" />
     <Compile Include="$(CommonPath)System\Obsoletions.cs"
     <Compile Include="$(CommonPath)System\Net\Security\SecurityBufferType.Windows.cs"
              Link="Common\System\Net\Security\SecurityBufferType.Windows.cs" />
     <!-- NegotiateStream -->
-    <Compile Include="$(CommonPath)System\Net\SecurityStatusAdapterPal.Windows.cs"
-             Link="Common\System\Net\SecurityStatusAdapterPal.Windows.cs" />
-    <Compile Include="$(CommonPath)System\Net\ContextFlagsAdapterPal.Windows.cs"
-             Link="Common\System\Net\ContextFlagsAdapterPal.Windows.cs" />
-    <Compile Include="$(CommonPath)System\Net\Security\NegotiateStreamPal.Windows.cs"
-             Link="Common\System\Net\Security\NegotiateStreamPal.Windows.cs" />
+    <Compile Include="System\Net\SecurityStatusAdapterPal.Windows.cs" />
+    <Compile Include="System\Net\ContextFlagsAdapterPal.Windows.cs" />
     <Compile Include="$(CommonPath)System\Net\Security\SecurityContextTokenHandle.cs"
              Link="Common\System\Net\Security\SecurityContextTokenHandle.cs" />
     <!-- Interop -->
              Link="Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs" />
     <Compile Include="$(CommonPath)System\Net\Security\Unix\SafeFreeNegoCredentials.cs"
              Link="Common\System\Net\Security\Unix\SafeFreeNegoCredentials.cs" />
-    <Compile Include="$(CommonPath)System\Net\ContextFlagsAdapterPal.Unix.cs"
-             Link="Common\System\Net\ContextFlagsAdapterPal.Unix.cs" />
+    <Compile Include="System\Net\ContextFlagsAdapterPal.Unix.cs" />
     <Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs"
              Link="Common\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs" />
     <Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs"
              Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs" />
     <Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.IsNtlmInstalled.cs"
              Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.IsNtlmInstalled.cs" />
-    <Compile Include="$(CommonPath)System\Net\Security\NegotiateStreamPal.Unix.cs"
-             Link="Common\System\Net\Security\NegotiateStreamPal.Unix.cs" />
     <Compile Include="System\Net\Security\NegotiateStreamPal.Unix.cs" />
   </ItemGroup>
   <ItemGroup Condition="'$(UseManagedNtlm)' == 'true'">
     <Compile Include="System\Net\Security\NegotiateStreamPal.Managed.cs" />
-    <Compile Include="$(CommonPath)System\Net\NTAuthentication.Managed.cs"
-             Link="Common\System\Net\NTAuthentication.Managed.cs" />
+    <Compile Include="System\Net\NTAuthentication.Managed.cs" />
     <Compile Include="$(CommonPath)System\Net\Security\MD4.cs"
              Link="Common\System\Net\Security\MD4.cs" />
     <Compile Include="$(CommonPath)System\Net\Security\RC4.cs"
index b1288fc..421e1a4 100644 (file)
@@ -1,8 +1,20 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.IO;
+using System.Buffers;
+using System.Buffers.Binary;
 using System.ComponentModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Security.Authentication;
+using System.Security.Authentication.ExtendedProtection;
 using System.Security.Principal;
+using System.Text;
+using System.Threading;
+using Microsoft.Win32.SafeHandles;
 
 namespace System.Net.Security
 {
@@ -13,6 +25,627 @@ namespace System.Net.Security
     //
     internal static partial class NegotiateStreamPal
     {
+        // value should match the Windows sspicli NTE_FAIL value
+        // defined in winerror.h
+        private const int NTE_FAIL = unchecked((int)0x80090020);
+
+        internal static string QueryContextClientSpecifiedSpn(SafeDeleteContext _ /*securityContext*/)
+        {
+            throw new PlatformNotSupportedException(SR.net_nego_server_not_supported);
+        }
+
+        internal static string QueryContextAuthenticationPackage(SafeDeleteContext securityContext)
+        {
+            SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
+            return negoContext.IsNtlmUsed ? NegotiationInfoClass.NTLM : NegotiationInfoClass.Kerberos;
+        }
+
+        private static byte[] GssWrap(
+            SafeGssContextHandle context,
+            ref bool encrypt,
+            ReadOnlySpan<byte> buffer)
+        {
+            Interop.NetSecurityNative.GssBuffer encryptedBuffer = default;
+            try
+            {
+                Interop.NetSecurityNative.Status minorStatus;
+                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.WrapBuffer(out minorStatus, context, ref encrypt, buffer, ref encryptedBuffer);
+                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
+                {
+                    throw new Interop.NetSecurityNative.GssApiException(status, minorStatus);
+                }
+
+                return encryptedBuffer.ToByteArray();
+            }
+            finally
+            {
+                encryptedBuffer.Dispose();
+            }
+        }
+
+        private static int GssUnwrap(
+            SafeGssContextHandle context,
+            out bool encrypt,
+            Span<byte> buffer)
+        {
+            Interop.NetSecurityNative.GssBuffer decryptedBuffer = default(Interop.NetSecurityNative.GssBuffer);
+            try
+            {
+                Interop.NetSecurityNative.Status minorStatus;
+                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.UnwrapBuffer(out minorStatus, context, out encrypt, buffer, ref decryptedBuffer);
+                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
+                {
+                    throw new Interop.NetSecurityNative.GssApiException(status, minorStatus);
+                }
+
+                decryptedBuffer.Span.CopyTo(buffer);
+                return decryptedBuffer.Span.Length;
+            }
+            finally
+            {
+                decryptedBuffer.Dispose();
+            }
+        }
+
+        private static string GssGetUser(
+            ref SafeGssContextHandle? context)
+        {
+            Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
+
+            try
+            {
+                Interop.NetSecurityNative.Status status
+                    = Interop.NetSecurityNative.GetUser(out var minorStatus,
+                                                        context,
+                                                        ref token);
+
+                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
+                {
+                    throw new Interop.NetSecurityNative.GssApiException(status, minorStatus);
+                }
+
+                ReadOnlySpan<byte> tokenBytes = token.Span;
+                int length = tokenBytes.Length;
+                if (length > 0 && tokenBytes[length - 1] == '\0')
+                {
+                    // Some GSS-API providers (gss-ntlmssp) include the terminating null with strings, so skip that.
+                    tokenBytes = tokenBytes.Slice(0, length - 1);
+                }
+
+#if NETSTANDARD2_0
+                return Encoding.UTF8.GetString(tokenBytes.ToArray(), 0, tokenBytes.Length);
+#else
+                return Encoding.UTF8.GetString(tokenBytes);
+#endif
+            }
+            finally
+            {
+                token.Dispose();
+            }
+        }
+
+        private static SecurityStatusPal EstablishSecurityContext(
+          SafeFreeNegoCredentials credential,
+          ref SafeDeleteContext? context,
+          ChannelBinding? channelBinding,
+          string? targetName,
+          ContextFlagsPal inFlags,
+          ReadOnlySpan<byte> incomingBlob,
+          out byte[]? resultBuffer,
+          ref ContextFlagsPal outFlags)
+        {
+            Interop.NetSecurityNative.PackageType packageType = credential.PackageType;
+
+            resultBuffer = null;
+
+            if (context == null)
+            {
+                if (NetEventSource.Log.IsEnabled())
+                {
+                    string protocol = packageType switch {
+                        Interop.NetSecurityNative.PackageType.NTLM => "NTLM",
+                        Interop.NetSecurityNative.PackageType.Kerberos => "Kerberos",
+                        _ => "SPNEGO"
+                    };
+                    NetEventSource.Info(context, $"requested protocol = {protocol}, target = {targetName}");
+                }
+
+                context = new SafeDeleteNegoContext(credential, targetName!);
+            }
+
+            Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
+            Interop.NetSecurityNative.Status status;
+            Interop.NetSecurityNative.Status minorStatus;
+            SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
+            SafeGssContextHandle contextHandle = negoContext.GssContext;
+            try
+            {
+                Interop.NetSecurityNative.GssFlags inputFlags =
+                    ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
+                uint outputFlags;
+                bool isNtlmUsed;
+
+                if (channelBinding != null)
+                {
+                    // If a TLS channel binding token (cbt) is available then get the pointer
+                    // to the application specific data.
+                    int appDataOffset = Marshal.SizeOf<SecChannelBindings>();
+                    Debug.Assert(appDataOffset < channelBinding.Size);
+                    IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
+                    int cbtAppDataSize = channelBinding.Size - appDataOffset;
+                    status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
+                                                                      credential.GssCredential,
+                                                                      ref contextHandle,
+                                                                      packageType,
+                                                                      cbtAppData,
+                                                                      cbtAppDataSize,
+                                                                      negoContext.TargetName,
+                                                                      (uint)inputFlags,
+                                                                      incomingBlob,
+                                                                      ref token,
+                                                                      out outputFlags,
+                                                                      out isNtlmUsed);
+                }
+                else
+                {
+                    status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
+                                                                      credential.GssCredential,
+                                                                      ref contextHandle,
+                                                                      packageType,
+                                                                      negoContext.TargetName,
+                                                                      (uint)inputFlags,
+                                                                      incomingBlob,
+                                                                      ref token,
+                                                                      out outputFlags,
+                                                                      out isNtlmUsed);
+                }
+
+                if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
+                    (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
+                {
+                    if (negoContext.GssContext.IsInvalid)
+                    {
+                        context.Dispose();
+                    }
+
+                    Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
+                    resultBuffer = Array.Empty<byte>();
+                    return new SecurityStatusPal(GetErrorCode(gex), gex);
+                }
+
+                resultBuffer = token.ToByteArray();
+
+                if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
+                {
+                    if (NetEventSource.Log.IsEnabled())
+                    {
+                        string protocol = packageType switch {
+                            Interop.NetSecurityNative.PackageType.NTLM => "NTLM",
+                            Interop.NetSecurityNative.PackageType.Kerberos => "Kerberos",
+                            _ => isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos"
+                        };
+                        NetEventSource.Info(context, $"actual protocol = {protocol}");
+                    }
+
+                    // Populate protocol used for authentication
+                    negoContext.SetAuthenticationPackage(isNtlmUsed);
+                }
+
+                Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
+                outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
+                    (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
+
+                SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
+                    SecurityStatusPalErrorCode.OK :
+                    SecurityStatusPalErrorCode.ContinueNeeded;
+                return new SecurityStatusPal(errorCode);
+            }
+            catch (Exception ex)
+            {
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
+                return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
+            }
+            finally
+            {
+                token.Dispose();
+
+                // Save the inner context handle for further calls to NetSecurity
+                //
+                // For the first call `negoContext.GssContext` is invalid and we expect the
+                // inital handle to be returned from InitSecContext. For any subsequent
+                // call the handle should stay the same or it can be destroyed by the native
+                // InitSecContext call.
+                Debug.Assert(
+                    negoContext.GssContext == contextHandle ||
+                    negoContext.GssContext.IsInvalid ||
+                    contextHandle.IsInvalid);
+                negoContext.SetGssContext(contextHandle);
+            }
+        }
+
+        internal static SecurityStatusPal InitializeSecurityContext(
+            ref SafeFreeCredentials credentialsHandle,
+            ref SafeDeleteContext? securityContext,
+            string? spn,
+            ContextFlagsPal requestedContextFlags,
+            ReadOnlySpan<byte> incomingBlob,
+            ChannelBinding? channelBinding,
+            ref byte[]? resultBlob,
+            out int resultBlobLength,
+            ref ContextFlagsPal contextFlags)
+        {
+            SafeFreeNegoCredentials negoCredentialsHandle = (SafeFreeNegoCredentials)credentialsHandle;
+
+            if (negoCredentialsHandle.IsDefault && string.IsNullOrEmpty(spn))
+            {
+                throw new PlatformNotSupportedException(SR.net_nego_not_supported_empty_target_with_defaultcreds);
+            }
+
+            SecurityStatusPal status = EstablishSecurityContext(
+                negoCredentialsHandle,
+                ref securityContext,
+                channelBinding,
+                spn,
+                requestedContextFlags,
+                incomingBlob,
+                out resultBlob,
+                ref contextFlags);
+            resultBlobLength = resultBlob?.Length ?? 0;
+
+            return status;
+        }
+
+#pragma warning disable IDE0060
+        internal static SecurityStatusPal AcceptSecurityContext(
+            SafeFreeCredentials? credentialsHandle,
+            ref SafeDeleteContext? securityContext,
+            ContextFlagsPal requestedContextFlags,
+            ReadOnlySpan<byte> incomingBlob,
+            ChannelBinding? channelBinding,
+            ref byte[] resultBlob,
+            out int resultBlobLength,
+            ref ContextFlagsPal contextFlags)
+        {
+            securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle!);
+
+            SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
+            SafeGssContextHandle contextHandle = negoContext.GssContext;
+            Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
+            try
+            {
+                Interop.NetSecurityNative.Status status;
+                Interop.NetSecurityNative.Status minorStatus;
+                status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
+                                                                    negoContext.AcceptorCredential,
+                                                                    ref contextHandle,
+                                                                    incomingBlob,
+                                                                    ref token,
+                                                                    out uint outputFlags,
+                                                                    out bool isNtlmUsed);
+
+                if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
+                    (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
+                {
+                    if (negoContext.GssContext.IsInvalid)
+                    {
+                        contextHandle.Dispose();
+                    }
+
+                    Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
+                    resultBlobLength = 0;
+                    return new SecurityStatusPal(GetErrorCode(gex), gex);
+                }
+
+                resultBlob = token.ToByteArray();
+
+                Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
+
+                contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
+                    (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
+                resultBlobLength = resultBlob.Length;
+
+                SecurityStatusPalErrorCode errorCode;
+                if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
+                {
+                    if (NetEventSource.Log.IsEnabled())
+                    {
+                        string protocol = isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos";
+                        NetEventSource.Info(securityContext, $"AcceptSecurityContext: actual protocol = {protocol}");
+                    }
+
+                    negoContext.SetAuthenticationPackage(isNtlmUsed);
+                    errorCode = SecurityStatusPalErrorCode.OK;
+                }
+                else
+                {
+                    errorCode = SecurityStatusPalErrorCode.ContinueNeeded;
+                }
+
+                return new SecurityStatusPal(errorCode);
+            }
+            catch (Exception ex)
+            {
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
+                resultBlobLength = 0;
+                return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex);
+            }
+            finally
+            {
+                token.Dispose();
+
+                // Save the inner context handle for further calls to NetSecurity
+                //
+                // For the first call `negoContext.GssContext` is invalid and we expect the
+                // inital handle to be returned from AcceptSecContext. For any subsequent
+                // call the handle should stay the same or it can be destroyed by the native
+                // AcceptSecContext call.
+                Debug.Assert(
+                    negoContext.GssContext == contextHandle ||
+                    negoContext.GssContext.IsInvalid ||
+                    contextHandle.IsInvalid);
+                negoContext.SetGssContext(contextHandle);
+            }
+        }
+#pragma warning restore IDE0060
+
+        // https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
+        private static SecurityStatusPalErrorCode GetErrorCode(Interop.NetSecurityNative.GssApiException exception)
+        {
+            switch (exception.MajorStatus)
+            {
+                case Interop.NetSecurityNative.Status.GSS_S_NO_CRED:
+                    return SecurityStatusPalErrorCode.UnknownCredentials;
+                case Interop.NetSecurityNative.Status.GSS_S_BAD_BINDINGS:
+                    return SecurityStatusPalErrorCode.BadBinding;
+                case Interop.NetSecurityNative.Status.GSS_S_CREDENTIALS_EXPIRED:
+                    return SecurityStatusPalErrorCode.CertExpired;
+                case Interop.NetSecurityNative.Status.GSS_S_DEFECTIVE_TOKEN:
+                    return SecurityStatusPalErrorCode.InvalidToken;
+                case Interop.NetSecurityNative.Status.GSS_S_DEFECTIVE_CREDENTIAL:
+                    return SecurityStatusPalErrorCode.IncompleteCredentials;
+                case Interop.NetSecurityNative.Status.GSS_S_BAD_SIG:
+                    return SecurityStatusPalErrorCode.MessageAltered;
+                case Interop.NetSecurityNative.Status.GSS_S_BAD_MECH:
+                    return SecurityStatusPalErrorCode.Unsupported;
+                case Interop.NetSecurityNative.Status.GSS_S_NO_CONTEXT:
+                default:
+                    return SecurityStatusPalErrorCode.InternalError;
+            }
+        }
+
+        private static string GetUser(
+            ref SafeDeleteContext securityContext)
+        {
+            SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
+            try
+            {
+                SafeGssContextHandle? contextHandle = negoContext.GssContext;
+                return GssGetUser(ref contextHandle);
+            }
+            catch (Exception ex)
+            {
+                if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, ex);
+                throw;
+            }
+        }
+
+        internal static Win32Exception CreateExceptionFromError(SecurityStatusPal statusCode)
+        {
+            return new Win32Exception(NTE_FAIL, (statusCode.Exception != null) ? statusCode.Exception.Message : statusCode.ErrorCode.ToString());
+        }
+
+#pragma warning disable IDE0060
+        internal static int QueryMaxTokenSize(string package)
+        {
+            // This value is not used on Unix
+            return 0;
+        }
+#pragma warning restore IDE0060
+
+        internal static SafeFreeCredentials AcquireDefaultCredential(string package, bool isServer)
+        {
+            return AcquireCredentialsHandle(package, isServer, new NetworkCredential(string.Empty, string.Empty, string.Empty));
+        }
+
+        internal static SafeFreeCredentials AcquireCredentialsHandle(string package, bool isServer, NetworkCredential credential)
+        {
+            bool isEmptyCredential = string.IsNullOrWhiteSpace(credential.UserName) ||
+                                     string.IsNullOrWhiteSpace(credential.Password);
+            Interop.NetSecurityNative.PackageType packageType;
+
+            if (string.Equals(package, NegotiationInfoClass.Negotiate, StringComparison.OrdinalIgnoreCase))
+            {
+                packageType = Interop.NetSecurityNative.PackageType.Negotiate;
+            }
+            else if (string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase))
+            {
+                packageType = Interop.NetSecurityNative.PackageType.NTLM;
+                if (isEmptyCredential && !isServer)
+                {
+                    // NTLM authentication is not possible with default credentials which are no-op
+                    throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred);
+                }
+            }
+            else if (string.Equals(package, NegotiationInfoClass.Kerberos, StringComparison.OrdinalIgnoreCase))
+            {
+                packageType = Interop.NetSecurityNative.PackageType.Kerberos;
+            }
+            else
+            {
+                // Native shim currently supports only NTLM, Negotiate and Kerberos
+                throw new PlatformNotSupportedException(SR.net_securitypackagesupport);
+            }
+
+            try
+            {
+                return isEmptyCredential ?
+                    new SafeFreeNegoCredentials(packageType, string.Empty, string.Empty, string.Empty) :
+                    new SafeFreeNegoCredentials(packageType, credential.UserName, credential.Password, credential.Domain);
+            }
+            catch (Exception ex)
+            {
+                throw new Win32Exception(NTE_FAIL, ex.Message);
+            }
+        }
+
+#pragma warning disable IDE0060
+        internal static SecurityStatusPal CompleteAuthToken(
+            ref SafeDeleteContext? securityContext,
+            ReadOnlySpan<byte> incomingBlob)
+        {
+            return new SecurityStatusPal(SecurityStatusPalErrorCode.OK);
+        }
+#pragma warning restore IDE0060
+
+        internal static NegotiateAuthenticationStatusCode Unwrap(
+            SafeDeleteContext securityContext,
+            ReadOnlySpan<byte> input,
+            IBufferWriter<byte> outputWriter,
+            out bool isEncrypted)
+        {
+            SafeGssContextHandle gssContext = ((SafeDeleteNegoContext)securityContext).GssContext!;
+            Interop.NetSecurityNative.GssBuffer decryptedBuffer = default(Interop.NetSecurityNative.GssBuffer);
+            try
+            {
+                Interop.NetSecurityNative.Status minorStatus;
+                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.UnwrapBuffer(out minorStatus, gssContext, out isEncrypted, input, ref decryptedBuffer);
+                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
+                {
+                    return status switch
+                    {
+                        Interop.NetSecurityNative.Status.GSS_S_BAD_SIG => NegotiateAuthenticationStatusCode.MessageAltered,
+                        _ => NegotiateAuthenticationStatusCode.InvalidToken
+                    };
+                }
+
+                decryptedBuffer.Span.CopyTo(outputWriter.GetSpan(decryptedBuffer.Span.Length));
+                outputWriter.Advance(decryptedBuffer.Span.Length);
+                return NegotiateAuthenticationStatusCode.Completed;
+            }
+            finally
+            {
+                decryptedBuffer.Dispose();
+            }
+        }
+
+        internal static NegotiateAuthenticationStatusCode UnwrapInPlace(
+            SafeDeleteContext securityContext,
+            Span<byte> input,
+            out int unwrappedOffset,
+            out int unwrappedLength,
+            out bool isEncrypted)
+        {
+            SafeGssContextHandle gssContext = ((SafeDeleteNegoContext)securityContext).GssContext!;
+            Interop.NetSecurityNative.GssBuffer decryptedBuffer = default(Interop.NetSecurityNative.GssBuffer);
+            try
+            {
+                Interop.NetSecurityNative.Status minorStatus;
+                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.UnwrapBuffer(out minorStatus, gssContext, out isEncrypted, input, ref decryptedBuffer);
+                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
+                {
+                    unwrappedOffset = 0;
+                    unwrappedLength = 0;
+                    return status switch
+                    {
+                        Interop.NetSecurityNative.Status.GSS_S_BAD_SIG => NegotiateAuthenticationStatusCode.MessageAltered,
+                        _ => NegotiateAuthenticationStatusCode.InvalidToken
+                    };
+                }
+
+                decryptedBuffer.Span.CopyTo(input);
+                unwrappedOffset = 0;
+                unwrappedLength = decryptedBuffer.Span.Length;
+                return NegotiateAuthenticationStatusCode.Completed;
+            }
+            finally
+            {
+                decryptedBuffer.Dispose();
+            }
+        }
+
+        internal static NegotiateAuthenticationStatusCode Wrap(
+            SafeDeleteContext securityContext,
+            ReadOnlySpan<byte> input,
+            IBufferWriter<byte> outputWriter,
+            bool requestEncryption,
+            out bool isEncrypted)
+        {
+            SafeGssContextHandle gssContext = ((SafeDeleteNegoContext)securityContext).GssContext!;
+            Interop.NetSecurityNative.GssBuffer encryptedBuffer = default;
+            try
+            {
+                Interop.NetSecurityNative.Status minorStatus;
+                bool encrypt = requestEncryption;
+                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.WrapBuffer(
+                    out minorStatus,
+                    gssContext,
+                    ref encrypt,
+                    input,
+                    ref encryptedBuffer);
+                isEncrypted = encrypt;
+                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
+                {
+                    return NegotiateAuthenticationStatusCode.GenericFailure;
+                }
+
+                encryptedBuffer.Span.CopyTo(outputWriter.GetSpan(encryptedBuffer.Span.Length));
+                outputWriter.Advance(encryptedBuffer.Span.Length);
+                return NegotiateAuthenticationStatusCode.Completed;
+            }
+            finally
+            {
+                encryptedBuffer.Dispose();
+            }
+        }
+
+        internal static bool VerifyMIC(
+            SafeDeleteContext securityContext,
+            bool isConfidential,
+            ReadOnlySpan<byte> message,
+            ReadOnlySpan<byte> signature)
+        {
+            _ = isConfidential;
+            SafeGssContextHandle gssContext = ((SafeDeleteNegoContext)securityContext).GssContext!;
+            Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.VerifyMic(
+                out _,
+                gssContext,
+                message,
+                signature);
+            return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE;
+        }
+
+        internal static void GetMIC(
+            SafeDeleteContext securityContext,
+            bool isConfidential,
+            ReadOnlySpan<byte> message,
+            IBufferWriter<byte> signature)
+        {
+            _ = isConfidential;
+            SafeGssContextHandle gssContext = ((SafeDeleteNegoContext)securityContext).GssContext!;
+            Interop.NetSecurityNative.GssBuffer micBuffer = default;
+            try
+            {
+                Interop.NetSecurityNative.Status minorStatus;
+                Interop.NetSecurityNative.Status status = Interop.NetSecurityNative.GetMic(
+                    out minorStatus,
+                    gssContext,
+                    message,
+                    ref micBuffer);
+                if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
+                {
+                    throw new Interop.NetSecurityNative.GssApiException(status, minorStatus);
+                }
+
+                signature.Write(micBuffer.Span);
+            }
+            finally
+            {
+                micBuffer.Dispose();
+            }
+        }
+
         internal static IIdentity GetIdentity(NTAuthentication context)
         {
             string name = context.Spn!;
index 1fb9995..3d42cac 100644 (file)
@@ -1,12 +1,16 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Buffers;
+using System.Buffers.Binary;
 using System.ComponentModel;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
 using System.Runtime.InteropServices;
 using System.Security;
 using System.Security.Principal;
+using System.Security.Authentication.ExtendedProtection;
 
 namespace System.Net.Security
 {
@@ -18,6 +22,431 @@ namespace System.Net.Security
     //
     internal static partial class NegotiateStreamPal
     {
+        internal static int QueryMaxTokenSize(string package)
+        {
+            return SSPIWrapper.GetVerifyPackageInfo(GlobalSSPI.SSPIAuth, package, true)!.MaxToken;
+        }
+
+        internal static SafeFreeCredentials AcquireDefaultCredential(string package, bool isServer)
+        {
+            return SSPIWrapper.AcquireDefaultCredential(
+                GlobalSSPI.SSPIAuth,
+                package,
+                (isServer ? Interop.SspiCli.CredentialUse.SECPKG_CRED_INBOUND : Interop.SspiCli.CredentialUse.SECPKG_CRED_OUTBOUND));
+        }
+
+        internal static SafeFreeCredentials AcquireCredentialsHandle(string package, bool isServer, NetworkCredential credential)
+        {
+            SafeSspiAuthDataHandle? authData = null;
+            try
+            {
+                Interop.SECURITY_STATUS result = Interop.SspiCli.SspiEncodeStringsAsAuthIdentity(
+                    credential.UserName, credential.Domain,
+                    credential.Password, out authData);
+
+                if (result != Interop.SECURITY_STATUS.OK)
+                {
+                    if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, SR.Format(SR.net_log_operation_failed_with_error, nameof(Interop.SspiCli.SspiEncodeStringsAsAuthIdentity), $"0x{(int)result:X}"));
+                    throw new Win32Exception((int)result);
+                }
+
+                return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPIAuth,
+                    package, (isServer ? Interop.SspiCli.CredentialUse.SECPKG_CRED_INBOUND : Interop.SspiCli.CredentialUse.SECPKG_CRED_OUTBOUND), ref authData);
+            }
+            finally
+            {
+                authData?.Dispose();
+            }
+        }
+
+        internal static string? QueryContextAssociatedName(SafeDeleteContext securityContext)
+        {
+            return SSPIWrapper.QueryStringContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NAMES);
+        }
+
+        internal static string? QueryContextClientSpecifiedSpn(SafeDeleteContext securityContext)
+        {
+            return SSPIWrapper.QueryStringContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_CLIENT_SPECIFIED_TARGET);
+        }
+
+        internal static string? QueryContextAuthenticationPackage(SafeDeleteContext securityContext)
+        {
+            SecPkgContext_NegotiationInfoW ctx = default;
+            bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_NEGOTIATION_INFO, typeof(SafeFreeContextBuffer), out SafeHandle? sspiHandle, ref ctx);
+            using (sspiHandle)
+            {
+                return success ? NegotiationInfoClass.GetAuthenticationPackageName(sspiHandle!, (int)ctx.NegotiationState) : null;
+            }
+        }
+
+        internal static SecurityStatusPal InitializeSecurityContext(
+            ref SafeFreeCredentials? credentialsHandle,
+            ref SafeDeleteContext? securityContext,
+            string? spn,
+            ContextFlagsPal requestedContextFlags,
+            ReadOnlySpan<byte> incomingBlob,
+            ChannelBinding? channelBinding,
+            ref byte[]? resultBlob,
+            out int resultBlobLength,
+            ref ContextFlagsPal contextFlags)
+        {
+
+            InputSecurityBuffers inputBuffers = default;
+            if (!incomingBlob.IsEmpty)
+            {
+                inputBuffers.SetNextBuffer(new InputSecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN));
+            }
+
+            if (channelBinding != null)
+            {
+                inputBuffers.SetNextBuffer(new InputSecurityBuffer(channelBinding));
+            }
+
+            var outSecurityBuffer = new SecurityBuffer(resultBlob, SecurityBufferType.SECBUFFER_TOKEN);
+
+            Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero;
+            // There is only one SafeDeleteContext type on Windows which is SafeDeleteSslContext so this cast is safe.
+            SafeDeleteSslContext? sslContext = (SafeDeleteSslContext?)securityContext;
+            Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.InitializeSecurityContext(
+                GlobalSSPI.SSPIAuth,
+                ref credentialsHandle,
+                ref sslContext,
+                spn,
+                ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(requestedContextFlags),
+                Interop.SspiCli.Endianness.SECURITY_NETWORK_DREP,
+                inputBuffers,
+                ref outSecurityBuffer,
+                ref outContextFlags);
+            securityContext = sslContext;
+            Debug.Assert(outSecurityBuffer.offset == 0);
+            resultBlob = outSecurityBuffer.token;
+            resultBlobLength = outSecurityBuffer.size;
+            contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(outContextFlags);
+            return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus);
+        }
+
+        internal static SecurityStatusPal CompleteAuthToken(
+            ref SafeDeleteContext? securityContext,
+            ReadOnlySpan<byte> incomingBlob)
+        {
+            // There is only one SafeDeleteContext type on Windows which is SafeDeleteSslContext so this cast is safe.
+            SafeDeleteSslContext? sslContext = (SafeDeleteSslContext?)securityContext;
+            var inSecurityBuffer = new InputSecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN);
+            Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.CompleteAuthToken(
+                GlobalSSPI.SSPIAuth,
+                ref sslContext,
+                in inSecurityBuffer);
+            securityContext = sslContext;
+            return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus);
+        }
+
+        internal static SecurityStatusPal AcceptSecurityContext(
+            SafeFreeCredentials? credentialsHandle,
+            ref SafeDeleteContext? securityContext,
+            ContextFlagsPal requestedContextFlags,
+            ReadOnlySpan<byte> incomingBlob,
+            ChannelBinding? channelBinding,
+            ref byte[]? resultBlob,
+            out int resultBlobLength,
+            ref ContextFlagsPal contextFlags)
+        {
+            InputSecurityBuffers inputBuffers = default;
+            if (!incomingBlob.IsEmpty)
+            {
+                inputBuffers.SetNextBuffer(new InputSecurityBuffer(incomingBlob, SecurityBufferType.SECBUFFER_TOKEN));
+            }
+
+            if (channelBinding != null)
+            {
+                inputBuffers.SetNextBuffer(new InputSecurityBuffer(channelBinding));
+            }
+
+            var outSecurityBuffer = new SecurityBuffer(resultBlob, SecurityBufferType.SECBUFFER_TOKEN);
+
+            Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero;
+            // There is only one SafeDeleteContext type on Windows which is SafeDeleteSslContext so this cast is safe.
+            SafeDeleteSslContext? sslContext = (SafeDeleteSslContext?)securityContext;
+            Interop.SECURITY_STATUS winStatus = (Interop.SECURITY_STATUS)SSPIWrapper.AcceptSecurityContext(
+                GlobalSSPI.SSPIAuth,
+                credentialsHandle,
+                ref sslContext,
+                ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(requestedContextFlags),
+                Interop.SspiCli.Endianness.SECURITY_NETWORK_DREP,
+                inputBuffers,
+                ref outSecurityBuffer,
+                ref outContextFlags);
+
+            // SSPI Workaround
+            // If a client sends up a blob on the initial request, Negotiate returns SEC_E_INVALID_HANDLE
+            // when it should return SEC_E_INVALID_TOKEN.
+            if (winStatus == Interop.SECURITY_STATUS.InvalidHandle && securityContext == null && !incomingBlob.IsEmpty)
+            {
+                winStatus = Interop.SECURITY_STATUS.InvalidToken;
+            }
+
+            Debug.Assert(outSecurityBuffer.offset == 0);
+            resultBlob = outSecurityBuffer.token;
+            resultBlobLength = outSecurityBuffer.size;
+            securityContext = sslContext;
+            contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(outContextFlags);
+            return SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(winStatus);
+        }
+
+        internal static Win32Exception CreateExceptionFromError(SecurityStatusPal statusCode)
+        {
+            return new Win32Exception((int)SecurityStatusAdapterPal.GetInteropFromSecurityStatusPal(statusCode));
+        }
+
+        internal static NegotiateAuthenticationStatusCode Unwrap(
+            SafeDeleteContext securityContext,
+            ReadOnlySpan<byte> input,
+            IBufferWriter<byte> outputWriter,
+            out bool wasEncrypted)
+        {
+            Span<byte> outputBuffer = outputWriter.GetSpan(input.Length).Slice(0, input.Length);
+            NegotiateAuthenticationStatusCode statusCode;
+
+            input.CopyTo(outputBuffer);
+            statusCode = UnwrapInPlace(securityContext, outputBuffer, out int unwrappedOffset, out int unwrappedLength, out wasEncrypted);
+
+            if (statusCode == NegotiateAuthenticationStatusCode.Completed)
+            {
+                if (unwrappedOffset > 0)
+                {
+                    outputBuffer.Slice(unwrappedOffset, unwrappedLength).CopyTo(outputBuffer);
+                }
+                outputWriter.Advance(unwrappedLength);
+            }
+
+            return statusCode;
+        }
+
+        internal static unsafe NegotiateAuthenticationStatusCode UnwrapInPlace(
+            SafeDeleteContext securityContext,
+            Span<byte> input,
+            out int unwrappedOffset,
+            out int unwrappedLength,
+            out bool wasEncrypted)
+        {
+            fixed (byte* inputPtr = input)
+            {
+                Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
+                Interop.SspiCli.SecBuffer* streamBuffer = &unmanagedBuffer[0];
+                Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[1];
+                streamBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM;
+                streamBuffer->pvBuffer = (IntPtr)inputPtr;
+                streamBuffer->cbBuffer = input.Length;
+                dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
+                dataBuffer->pvBuffer = IntPtr.Zero;
+                dataBuffer->cbBuffer = 0;
+
+                Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
+                {
+                    pBuffers = unmanagedBuffer
+                };
+
+                uint qop;
+                int errorCode = GlobalSSPI.SSPIAuth.DecryptMessage(securityContext, ref sdcInOut, out qop);
+                if (errorCode != 0)
+                {
+                    unwrappedOffset = 0;
+                    unwrappedLength = 0;
+                    wasEncrypted = false;
+                    return errorCode switch
+                    {
+                        (int)Interop.SECURITY_STATUS.MessageAltered => NegotiateAuthenticationStatusCode.MessageAltered,
+                        _ => NegotiateAuthenticationStatusCode.InvalidToken
+                    };
+                }
+
+                if (dataBuffer->BufferType != SecurityBufferType.SECBUFFER_DATA)
+                {
+                    throw new InternalException(dataBuffer->BufferType);
+                }
+
+                wasEncrypted = qop != Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT;
+
+                Debug.Assert((nint)dataBuffer->pvBuffer >= (nint)inputPtr);
+                Debug.Assert((nint)dataBuffer->pvBuffer + dataBuffer->cbBuffer <= (nint)inputPtr + input.Length);
+                unwrappedOffset = (int)((byte*)dataBuffer->pvBuffer - inputPtr);
+                unwrappedLength = dataBuffer->cbBuffer;
+                return NegotiateAuthenticationStatusCode.Completed;
+            }
+        }
+
+        internal static unsafe NegotiateAuthenticationStatusCode Wrap(
+            SafeDeleteContext securityContext,
+            ReadOnlySpan<byte> input,
+            IBufferWriter<byte> outputWriter,
+            bool requestEncryption,
+            out bool isEncrypted)
+        {
+            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 = input.Length + sizes.cbMaxSignature;
+            Span<byte> outputBuffer = outputWriter.GetSpan(resultSize);
+
+            // make a copy of user data for in-place encryption
+            input.CopyTo(outputBuffer.Slice(sizes.cbMaxSignature, input.Length));
+
+            isEncrypted = requestEncryption;
+
+            fixed (byte* outputPtr = outputBuffer)
+            {
+                // Prepare buffers TOKEN(signature), DATA and Padding.
+                Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
+                Interop.SspiCli.SecBuffer* tokenBuffer = &unmanagedBuffer[0];
+                Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[1];
+                tokenBuffer->BufferType = SecurityBufferType.SECBUFFER_TOKEN;
+                tokenBuffer->pvBuffer = (IntPtr)(outputPtr);
+                tokenBuffer->cbBuffer = sizes.cbMaxSignature;
+                dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
+                dataBuffer->pvBuffer = (IntPtr)(outputPtr + sizes.cbMaxSignature);
+                dataBuffer->cbBuffer = input.Length;
+
+                Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
+                {
+                    pBuffers = unmanagedBuffer
+                };
+
+                uint qop = requestEncryption ? 0 : Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT;
+                int errorCode = GlobalSSPI.SSPIAuth.EncryptMessage(securityContext, ref sdcInOut, qop);
+
+                if (errorCode != 0)
+                {
+                    return errorCode switch
+                    {
+                        (int)Interop.SECURITY_STATUS.ContextExpired => NegotiateAuthenticationStatusCode.ContextExpired,
+                        (int)Interop.SECURITY_STATUS.QopNotSupported => NegotiateAuthenticationStatusCode.QopNotSupported,
+                        _ => NegotiateAuthenticationStatusCode.GenericFailure,
+                    };
+                }
+
+                outputWriter.Advance(tokenBuffer->cbBuffer + dataBuffer->cbBuffer);
+                return NegotiateAuthenticationStatusCode.Completed;
+            }
+        }
+
+        internal static unsafe bool VerifyMIC(
+            SafeDeleteContext securityContext,
+            bool isConfidential,
+            ReadOnlySpan<byte> message,
+            ReadOnlySpan<byte> signature)
+        {
+            bool refAdded = false;
+
+            try
+            {
+                securityContext.DangerousAddRef(ref refAdded);
+
+                fixed (byte* messagePtr = message)
+                fixed (byte* signaturePtr = signature)
+                {
+                    Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
+                    Interop.SspiCli.SecBuffer* tokenBuffer = &unmanagedBuffer[0];
+                    Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[1];
+                    tokenBuffer->BufferType = SecurityBufferType.SECBUFFER_TOKEN;
+                    tokenBuffer->pvBuffer = (IntPtr)signaturePtr;
+                    tokenBuffer->cbBuffer = signature.Length;
+                    dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
+                    dataBuffer->pvBuffer = (IntPtr)messagePtr;
+                    dataBuffer->cbBuffer = message.Length;
+
+                    Interop.SspiCli.SecBufferDesc sdcIn = new Interop.SspiCli.SecBufferDesc(2)
+                    {
+                        pBuffers = unmanagedBuffer
+                    };
+
+                    uint qop;
+                    int errorCode = Interop.SspiCli.VerifySignature(ref securityContext._handle, in sdcIn, 0, &qop);
+
+                    if (errorCode != 0)
+                    {
+                        Exception e = new Win32Exception(errorCode);
+                        if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, e);
+                        throw new Win32Exception(errorCode);
+                    }
+
+                    if (isConfidential && qop == Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT)
+                    {
+                        Debug.Fail($"Expected qop = 0, returned value = {qop}");
+                        throw new InvalidOperationException(SR.net_auth_message_not_encrypted);
+                    }
+
+                    return true;
+                }
+            }
+            finally
+            {
+                if (refAdded)
+                {
+                    securityContext.DangerousRelease();
+                }
+            }
+        }
+
+        internal static unsafe void GetMIC(
+            SafeDeleteContext securityContext,
+            bool isConfidential,
+            ReadOnlySpan<byte> message,
+            IBufferWriter<byte> signature)
+        {
+            bool refAdded = false;
+
+            try
+            {
+                securityContext.DangerousAddRef(ref refAdded);
+
+                SecPkgContext_Sizes sizes = default;
+                bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes);
+                Debug.Assert(success);
+
+                Span<byte> signatureBuffer = signature.GetSpan(sizes.cbSecurityTrailer);
+
+                fixed (byte* messagePtr = message)
+                fixed (byte* signaturePtr = signatureBuffer)
+                {
+                    // Prepare buffers TOKEN(signature), DATA.
+                    Interop.SspiCli.SecBuffer* unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
+                    Interop.SspiCli.SecBuffer* tokenBuffer = &unmanagedBuffer[0];
+                    Interop.SspiCli.SecBuffer* dataBuffer = &unmanagedBuffer[1];
+                    tokenBuffer->BufferType = SecurityBufferType.SECBUFFER_TOKEN;
+                    tokenBuffer->pvBuffer = (IntPtr)signaturePtr;
+                    tokenBuffer->cbBuffer = sizes.cbSecurityTrailer;
+                    dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
+                    dataBuffer->pvBuffer = (IntPtr)messagePtr;
+                    dataBuffer->cbBuffer = message.Length;
+
+                    Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
+                    {
+                        pBuffers = unmanagedBuffer
+                    };
+
+                    uint qop = isConfidential ? 0 : Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT;
+                    int errorCode = Interop.SspiCli.MakeSignature(ref securityContext._handle, qop, ref sdcInOut, 0);
+
+                    if (errorCode != 0)
+                    {
+                        Exception e = new Win32Exception(errorCode);
+                        if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, e);
+                        throw new Win32Exception(errorCode);
+                    }
+
+                    signature.Advance(signatureBuffer.Length);
+                }
+            }
+            finally
+            {
+                if (refAdded)
+                {
+                    securityContext.DangerousRelease();
+                }
+            }
+        }
+
         internal static IIdentity GetIdentity(NTAuthentication context)
         {
             IIdentity? result;
index 81dc725..53875c5 100644 (file)
 
   <!-- NT Authentication -->
   <ItemGroup Condition="'$(TargetPlatformIdentifier)' != 'browser'">
-    <Compile Include="$(CommonPath)System\Net\ContextFlagsPal.cs"
-             Link="Common\System\Net\ContextFlagsPal.cs" />
     <Compile Include="$(CommonPath)System\Net\DebugSafeHandleZeroOrMinusOneIsInvalid.cs"
              Link="Common\System\Net\DebugSafeHandleZeroOrMinusOneIsInvalid.cs" />
     <Compile Include="$(CommonPath)System\Net\DebugSafeHandle.cs"
              Link="Common\System\Net\ExceptionCheck.cs" />
     <Compile Include="$(CommonPath)System\Net\NegotiationInfoClass.cs"
              Link="Common\System\Net\NegotiationInfoClass.cs" />
-    <Compile Include="$(CommonPath)System\Net\NTAuthentication.Common.cs"
-             Link="Common\System\Net\NTAuthentication.Common.cs" />
-    <Compile Include="$(CommonPath)System\Net\SecurityStatusPal.cs"
-             Link="Common\System\Net\SecurityStatusPal.cs" />
     <Compile Include="$(CommonPath)System\Net\Security\SafeCredentialReference.cs"
              Link="Common\System\Net\Security\SafeCredentialReference.cs" />
     <Compile Include="$(CommonPath)System\Net\Security\SSPIHandleCache.cs"
   <ItemGroup Condition="'$(TargetPlatformIdentifier)' != 'windows' and '$(TargetPlatformIdentifier)' != 'browser'">
     <Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
              Link="Common\Interop\Unix\Interop.Libraries.cs" />
-    <Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs"
-             Link="Common\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs" />
-    <Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs"
-             Link="Common\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs" />
-    <Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs"
-             Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs" />
-    <Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.GssFlags.cs"
-             Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.GssFlags.cs" />
-    <Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.PackageType.cs"
-             Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.PackageType.cs" />
-    <Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.Status.cs"
-             Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.Status.cs" />
     <Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.IsNtlmInstalled.cs"
              Link="Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.IsNtlmInstalled.cs" />
-    <Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\GssSafeHandles.cs"
-             Link="Common\Microsoft\Win32\SafeHandles\GssSafeHandles.cs" />
-    <Compile Include="$(CommonPath)System\Net\ContextFlagsAdapterPal.Unix.cs"
-             Link="Common\System\Net\ContextFlagsAdapterPal.Unix.cs" />
-    <Compile Include="$(CommonPath)System\Net\Security\Unix\SecChannelBindings.cs"
-             Link="Common\System\Net\Security\Unix\SecChannelBindings.cs" />
-    <Compile Include="$(CommonPath)System\Net\Security\Unix\SafeDeleteContext.cs"
-             Link="Common\System\Net\Security\Unix\SafeDeleteContext.cs" />
-    <Compile Include="$(CommonPath)System\Net\Security\Unix\SafeDeleteNegoContext.cs"
-             Link="Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs" />
     <Compile Include="$(CommonPath)System\Net\Security\Unix\SafeFreeCredentials.cs"
              Link="Common\System\Net\Security\Unix\SafeFreeCredentials.cs" />
-    <Compile Include="$(CommonPath)System\Net\Security\Unix\SafeFreeNegoCredentials.cs"
-             Link="Common\System\Net\Security\Unix\SafeFreeNegoCredentials.cs" />
-    <Compile Include="$(CommonPath)System\Net\Security\NegotiateStreamPal.Unix.cs"
-             Link="Common\System\Net\Security\NegotiateStreamPal.Unix.cs" />
     <Compile Include="$(CommonTestPath)System\Net\Capability.Security.Unix.cs"
              Link="Common\System\Net\Capability.Security.Unix.cs" />
   </ItemGroup>
              Link="Common\System\Net\Security\SecurityContextTokenHandle.cs" />
     <Compile Include="$(CommonPath)System\Net\ContextAwareResult.Windows.cs"
              Link="Common\System\Net\ContextAwareResult.Windows.cs" />
-    <Compile Include="$(CommonPath)System\Net\SecurityStatusAdapterPal.Windows.cs"
-             Link="Common\System\Net\SecurityStatusAdapterPal.Windows.cs" />
-    <Compile Include="$(CommonPath)System\Net\ContextFlagsAdapterPal.Windows.cs"
-             Link="Common\System\Net\ContextFlagsAdapterPal.Windows.cs" />
-    <Compile Include="$(CommonPath)System\Net\Security\NegotiateStreamPal.Windows.cs"
-             Link="Common\System\Net\Security\NegotiateStreamPal.Windows.cs" />
     <Compile Include="$(CommonPath)System\Net\Security\NetEventSource.Security.Windows.cs"
              Link="Common\System\Net\Security\NetEventSource.Security.Windows.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CERT_CONTEXT.cs"