+++ /dev/null
-// 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();
- }
- }
- }
-}
+++ /dev/null
-// 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();
- }
- }
- }
- }
-}
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"
<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"
// 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
{
//
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!;
// 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
{
//
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;
<!-- 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"