| File: System\ServiceModel\Security\WindowsSspiNegotiation.cs | Web Access |
| Project: ..\..\..\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Buffers; using System.ComponentModel; using System.IdentityModel.Tokens; using System.Net; using System.Net.Security; using System.Security.Authentication.ExtendedProtection; using System.Security.Principal; namespace System.ServiceModel.Security { internal sealed class WindowsSspiNegotiation : ISspiNegotiation { private NegotiateAuthentication _outgoingNegotiateAuth; private NegotiateAuthentication _incomingNegotiateAuth; private bool _disposed; private bool _isCompleted; private bool _isServer; private string _servicePrincipalName; private TokenImpersonationLevel _impersonationLevel; private bool _doMutualAuth; private string _package; private NetworkCredential _credential; private object _syncObject = new object(); /// <summary> /// Client side constructor /// </summary> internal WindowsSspiNegotiation( string package, NetworkCredential credential, TokenImpersonationLevel impersonationLevel, string servicePrincipalName, bool doMutualAuth) { _isServer = false; _servicePrincipalName = servicePrincipalName; _impersonationLevel = impersonationLevel; _doMutualAuth = doMutualAuth; _package = package; _credential = credential ?? CredentialCache.DefaultNetworkCredentials; } private void EnsureInitialized(ChannelBinding channelBinding) { if (_outgoingNegotiateAuth == null) { var clientOptions = new NegotiateAuthenticationClientOptions { Package = _package, Credential = _credential, TargetName = _servicePrincipalName, RequiredProtectionLevel = ProtectionLevel.EncryptAndSign, RequireMutualAuthentication = _doMutualAuth, AllowedImpersonationLevel = _impersonationLevel, Binding = channelBinding, }; _outgoingNegotiateAuth = new NegotiateAuthentication(clientOptions); } } public DateTime ExpirationTimeUtc { get { ThrowIfDisposed(); // NegotiateAuthentication does not expose lifespan directly. // Return max value as in the .NET FX fallback. return SecurityUtils.MaxUtcDateTime; } } public bool IsCompleted { get { ThrowIfDisposed(); return _isCompleted; } } public bool IsMutualAuthFlag { get { ThrowIfDisposed(); return _outgoingNegotiateAuth.IsMutuallyAuthenticated; } } public bool IsValidContext { get { return _isCompleted && _outgoingNegotiateAuth != null && _outgoingNegotiateAuth.IsAuthenticated; } } public string KeyEncryptionAlgorithm { get { return SecurityAlgorithms.WindowsSspiKeyWrap; } } public string ProtocolName { get { ThrowIfDisposed(); return _outgoingNegotiateAuth.Package; } } public string ServicePrincipalName { get { ThrowIfDisposed(); return _servicePrincipalName; } } public string GetRemoteIdentityName() { if (!_isServer) { return _servicePrincipalName; } if (IsValidContext) { var remoteIdentity = _outgoingNegotiateAuth.RemoteIdentity; if (remoteIdentity != null) { return remoteIdentity.Name; } } return string.Empty; } public byte[] Decrypt(byte[] encryptedContent) { if (encryptedContent == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(encryptedContent)); } ThrowIfDisposed(); var auth = _incomingNegotiateAuth ?? _outgoingNegotiateAuth; var outputWriter = new ArrayBufferWriter<byte>(); NegotiateAuthenticationStatusCode statusCode = auth.Unwrap(encryptedContent, outputWriter, out _); if (statusCode != NegotiateAuthenticationStatusCode.Completed) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new Win32Exception(SRP.Format(SRP.InvalidSspiNegotiation))); } return outputWriter.WrittenSpan.ToArray(); } public byte[] Encrypt(byte[] input) { if (input == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(input)); } ThrowIfDisposed(); var auth = _outgoingNegotiateAuth; var outputWriter = new ArrayBufferWriter<byte>(); NegotiateAuthenticationStatusCode statusCode = auth.Wrap(input, outputWriter, true, out _); if (statusCode != NegotiateAuthenticationStatusCode.Completed) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new Win32Exception(SRP.Format(SRP.InvalidSspiNegotiation))); } return outputWriter.WrittenSpan.ToArray(); } public byte[] GetOutgoingBlob(byte[] incomingBlob, ChannelBinding channelBinding, ExtendedProtectionPolicy protectionPolicy) { ThrowIfDisposed(); EnsureInitialized(channelBinding); NegotiateAuthenticationStatusCode statusCode; ReadOnlySpan<byte> incomingSpan = incomingBlob != null ? incomingBlob : ReadOnlySpan<byte>.Empty; byte[] outgoingBlob = _outgoingNegotiateAuth.GetOutgoingBlob(incomingSpan, out statusCode); if (statusCode == NegotiateAuthenticationStatusCode.Completed) { _isCompleted = true; } else if (statusCode == NegotiateAuthenticationStatusCode.ContinueNeeded) { // continue the negotiation } else { _isCompleted = true; if (statusCode == NegotiateAuthenticationStatusCode.TargetUnknown || statusCode == NegotiateAuthenticationStatusCode.UnknownCredentials) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new Win32Exception(SRP.Format(SRP.IncorrectSpnOrUpnSpecified, _servicePrincipalName))); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new Win32Exception(SRP.Format(SRP.InvalidSspiNegotiation))); } } return outgoingBlob; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { lock (_syncObject) { if (!_disposed) { if (disposing) { _outgoingNegotiateAuth?.Dispose(); _incomingNegotiateAuth?.Dispose(); } _outgoingNegotiateAuth = null; _incomingNegotiateAuth = null; _servicePrincipalName = null; _disposed = true; } } } private void ThrowIfDisposed() { lock (_syncObject) { if (_disposed) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(null)); } } } } }