Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Net / System / Net / SecureProtocols / _NegoState.cs / 1305376 / _NegoState.cs
/*++ Copyright (c) Microsoft Corporation Module Name: _NegoState.cs Abstract: The internal class is used by Negotiate Client&Server and by (internal) NegoStream. It encapsulates security context and does the real work in authentication and user data encryption with NEGO SSPI package. Author: Alexei Vopilov 12-Aug-2003 Revision History: 12-Aug-2003 New design that has obsoleted Authenticator class --*/ namespace System.Net.Security { using System; using System.Net; using System.IO; using System.Security; using System.Security.Principal; using System.Security.Permissions; using System.Threading; using System.ComponentModel; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; // // The class maintains the state of the authentication process and the security context. // On the success it returns the remote side identites created as the result // of authentication. // internal class NegoState { private const int ERROR_TRUST_FAILURE = 1790; //used to serialize protectionLevel or impersonationLevel mismatch error to the remote side private static readonly byte[] _EmptyMessage = new byte[0]; private static readonly AsyncCallback _ReadCallback = new AsyncCallback(ReadCallback); private static readonly AsyncCallback _WriteCallback = new AsyncCallback(WriteCallback); private Stream _InnerStream; private bool _LeaveStreamOpen; private Exception _Exception; private StreamFramer _Framer; private NTAuthentication _Context; private int _NestedAuth; internal const int c_MaxReadFrameSize = 64*1024; internal const int c_MaxWriteDataSize = 63*1024; //we give 1k for the framing and trailer that is laways less as per SSPI. private bool _CanRetryAuthentication; private ProtectionLevel _ExpectedProtectionLevel; private TokenImpersonationLevel _ExpectedImpersonationLevel; private uint _WriteSequenceNumber; private uint _ReadSequenceNumber; private ExtendedProtectionPolicy _ExtendedProtectionPolicy; // SSPI does not send a server ack on successfull auth. // So, this is a state variable used to gracefully handle auth confirmation private bool _RemoteOk = false; // // // internal NegoState(Stream innerStream, bool leaveStreamOpen) { if (innerStream==null) { throw new ArgumentNullException("stream"); } _InnerStream = innerStream; _LeaveStreamOpen = leaveStreamOpen; } // internal static string DefaultPackage { get { return ComNetOS.IsWin9x? NegotiationInfoClass.NTLM: NegotiationInfoClass.Negotiate; } } // // // internal void ValidateCreateContext(string package, NetworkCredential credential, string servicePrincipalName, ExtendedProtectionPolicy policy, ProtectionLevel protectionLevel, TokenImpersonationLevel impersonationLevel) { if (policy != null) { if (!AuthenticationManager.OSSupportsExtendedProtection) { if (policy.PolicyEnforcement == PolicyEnforcement.Always) { throw new PlatformNotSupportedException(SR.GetString(SR.security_ExtendedProtection_NoOSSupport)); } } else { // One of these must be set if EP is turned on if (policy.CustomChannelBinding == null && policy.CustomServiceNames == null) throw new ArgumentException(SR.GetString(SR.net_auth_must_specify_extended_protection_scheme), "policy"); } _ExtendedProtectionPolicy = policy; } else { _ExtendedProtectionPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never); } ValidateCreateContext(package, true, credential, servicePrincipalName, _ExtendedProtectionPolicy.CustomChannelBinding, protectionLevel, impersonationLevel); } // internal void ValidateCreateContext( string package, bool isServer, NetworkCredential credential, string servicePrincipalName, ChannelBinding channelBinding, ProtectionLevel protectionLevel, TokenImpersonationLevel impersonationLevel ) { if (_Exception != null && !_CanRetryAuthentication) { throw _Exception; } if (_Context != null && _Context.IsValidContext) { throw new InvalidOperationException(SR.GetString(SR.net_auth_reauth)); } if (credential == null) { throw new ArgumentNullException("credential"); } if (servicePrincipalName == null) { throw new ArgumentNullException("servicePrincipalName"); } if (ComNetOS.IsWin9x && protectionLevel != ProtectionLevel.None) { throw new NotSupportedException(SR.GetString(SR.net_auth_no_protection_on_win9x)); } if (impersonationLevel != TokenImpersonationLevel.Identification && impersonationLevel != TokenImpersonationLevel.Impersonation && impersonationLevel != TokenImpersonationLevel.Delegation) { throw new ArgumentOutOfRangeException("impersonationLevel", impersonationLevel.ToString(), SR.GetString(SR.net_auth_supported_impl_levels)); } if (_Context != null && IsServer != isServer) { throw new InvalidOperationException(SR.GetString(SR.net_auth_client_server)); } _Exception = null; _RemoteOk = false; _Framer = new StreamFramer(_InnerStream); _Framer.WriteHeader.MessageId = FrameHeader.HandshakeId; _ExpectedProtectionLevel = protectionLevel; _ExpectedImpersonationLevel = isServer? impersonationLevel: TokenImpersonationLevel.None; _WriteSequenceNumber = 0; _ReadSequenceNumber = 0; ContextFlags flags = ContextFlags.Connection; // A workaround for the client when talking to Win9x on the server side if (protectionLevel == ProtectionLevel.None && !isServer) { package = NegotiationInfoClass.NTLM; } else if (protectionLevel == ProtectionLevel.EncryptAndSign) { flags |= ContextFlags.Confidentiality; } else if (protectionLevel == ProtectionLevel.Sign) { // Assuming user expects NT4 SP4 and above flags |= ContextFlags.ReplayDetect | ContextFlags.SequenceDetect | ContextFlags.InitIntegrity; } if (isServer) { if (_ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported) { flags |= ContextFlags.AllowMissingBindings; } if (_ExtendedProtectionPolicy.PolicyEnforcement != PolicyEnforcement.Never && _ExtendedProtectionPolicy.ProtectionScenario == ProtectionScenario.TrustedProxy) { flags |= ContextFlags.ProxyBindings; } } else { // According to lzhu server side should not request any of these flags if (protectionLevel != ProtectionLevel.None) {flags |= ContextFlags.MutualAuth;} if (impersonationLevel == TokenImpersonationLevel.Identification) {flags |= ContextFlags.InitIdentify;} if (impersonationLevel == TokenImpersonationLevel.Delegation) {flags |= ContextFlags.Delegate;} } _CanRetryAuthentication = false; // // Security: We used to rely on NetworkCredential class to demand permission // Switched over to explicit ControlPrincipalPermission demand (except for DefaultCredential case) // The mitigated attack is brute-force pasword guessing through SSPI. if (!(credential is SystemNetworkCredential)) ExceptionHelper.ControlPrincipalPermission.Demand(); try { // _Context = new NTAuthentication(isServer, package, credential, servicePrincipalName, flags, channelBinding); } catch (Win32Exception e) { throw new AuthenticationException(SR.GetString(SR.net_auth_SSPI), e); } } // // private Exception SetException(Exception e) { if (_Exception == null || !(_Exception is ObjectDisposedException)) { _Exception = e; } if (_Exception != null && _Context != null) { _Context.CloseContext(); } return _Exception; } // // General informational properties // // // internal bool IsAuthenticated { get { return _Context != null && HandshakeComplete && _Exception == null && _RemoteOk; } } // // // internal bool IsMutuallyAuthenticated { get { if (!IsAuthenticated) return false; // On Win9x it is alwasy false if (ComNetOS.IsWin9x) return false; // Suppressing for NTLM since SSPI does not return correct value int the context flags. if (_Context.IsNTLM) return false; return _Context.IsMutualAuthFlag; } } // internal bool IsEncrypted { get { return IsAuthenticated && _Context.IsConfidentialityFlag; } } // internal bool IsSigned { get { return IsAuthenticated && (_Context.IsIntegrityFlag || _Context.IsConfidentialityFlag); } } // internal bool IsServer { get { return _Context != null && _Context.IsServer; } } // // NEGO specific informational properties // internal bool CanGetSecureStream { get { return (_Context.IsConfidentialityFlag || _Context.IsIntegrityFlag); } } // // // internal TokenImpersonationLevel AllowedImpersonation { get { CheckThrow(true); return PrivateImpersonationLevel; } } // private TokenImpersonationLevel PrivateImpersonationLevel { get { // according to lzhu we should suppress dlegate flag in NTLM case return (_Context.IsDelegationFlag && _Context.ProtocolName != NegotiationInfoClass.NTLM) ? TokenImpersonationLevel.Delegation :_Context.IsIdentifyFlag? TokenImpersonationLevel.Identification :(ComNetOS.IsWin9x && _Context.IsServer) ? TokenImpersonationLevel.Identification //Win9x workaround. :TokenImpersonationLevel.Impersonation; } } // // // private bool HandshakeComplete { get { return _Context.IsCompleted && _Context.IsValidContext; } } // // Note that method will demand PrincipalControlPermission // which essentially means demanding full trust // internal IIdentity GetIdentity() { CheckThrow(true); IIdentity result = null; string name = _Context.IsServer? _Context.AssociatedName: _Context.Spn; string protocol = "NTLM"; if (!ComNetOS.IsWin9x) { protocol = _Context.ProtocolName; } if (_Context.IsServer && !ComNetOS.IsWin9x) { SafeCloseHandle token = null; try { token = _Context.GetContextToken(); string authtype = _Context.ProtocolName; result = new WindowsIdentity(token.DangerousGetHandle(), authtype, WindowsAccountType.Normal, true); return result; } catch (SecurityException) { //ignore and construct generic Identity if failed due to security problem } finally { if (token != null) { token.Close(); } } } // on the client we don't have access to the remote side identity. result = new GenericIdentity(name, protocol); return result; } // // Methods // // // internal void CheckThrow(bool authSucessCheck) { if (_Exception != null) { throw _Exception; } if (authSucessCheck && !IsAuthenticated) { throw new InvalidOperationException(SR.GetString(SR.net_auth_noauth)); } } // // This is to not depend on GC&SafeHandle class if the context is not needed anymore. // internal void Close() { // Mark this instance as disposed _Exception = new ObjectDisposedException("NegotiateStream"); if (_Context != null) { _Context.CloseContext(); } } // // // internal void ProcessAuthentication(LazyAsyncResult lazyResult) { CheckThrow(false); if (Interlocked.Exchange(ref _NestedAuth, 1) == 1) { throw new InvalidOperationException(SR.GetString(SR.net_io_invalidnestedcall, lazyResult==null?"BeginAuthenticate":"Authenticate", "authenticate")); } try { if (_Context.IsServer) { // Listen for a client blob StartReceiveBlob(lazyResult); } else { // we start with the first blob StartSendBlob(null, lazyResult); } } catch (Exception e) { // Roundtrip it through the SetException() e = SetException(e); throw; } finally { if (lazyResult == null || _Exception != null) { _NestedAuth = 0; } } } // // // internal void EndProcessAuthentication(IAsyncResult result) { if (result == null) { throw new ArgumentNullException("asyncResult"); } LazyAsyncResult lazyResult = result as LazyAsyncResult; if (lazyResult == null) { throw new ArgumentException(SR.GetString(SR.net_io_async_result, result.GetType().FullName), "asyncResult"); } if (Interlocked.Exchange(ref _NestedAuth, 0) == 0) { throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndAuthenticate")); } // No "artificial" timeouts implemented so far, InnerStream controls that. lazyResult.InternalWaitForCompletion(); Exception e = lazyResult.Result as Exception; if (e != null) { // Roundtrip it through the SetException() e = SetException(e); throw e; } } private bool CheckSpn() { if (_Context.IsKerberos) { return true; } if (_ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Never || _ExtendedProtectionPolicy.CustomServiceNames == null) { return true; } if (!AuthenticationManager.OSSupportsExtendedProtection) { GlobalLog.Assert(_ExtendedProtectionPolicy.PolicyEnforcement != PolicyEnforcement.Always, "User managed to set PolicyEnforcement.Always when the OS does not support extended protection!"); return true; } string clientSpn = _Context.ClientSpecifiedSpn; if (String.IsNullOrEmpty(clientSpn)) { if (_ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported) { return true; } } else { foreach (string serviceName in _ExtendedProtectionPolicy.CustomServiceNames) { if (String.Compare(clientSpn, serviceName, StringComparison.OrdinalIgnoreCase) == 0) { return true; } } } return false; } // // Client side starts here, but server also loops through this method // private void StartSendBlob(byte[] message, LazyAsyncResult lazyResult) { Win32Exception win32exception = null; if (message != _EmptyMessage) { message = GetOutgoingBlob(message, ref win32exception); } if (win32exception != null) { // signal remote side on a failed attempt StartSendAuthResetSignal(lazyResult, message, win32exception); return; } if (HandshakeComplete) { if (_Context.IsServer && !CheckSpn()) { Exception exception = new AuthenticationException(SR.GetString(SR.net_auth_bad_client_creds_or_target_mismatch)); int statusCode = ERROR_TRUST_FAILURE; message = new byte[8]; //sizeof(long) for (int i = message.Length - 1; i >= 0; --i) { message[i] = (byte)(statusCode & 0xFF); statusCode = (int)((uint)statusCode >> 8); } StartSendAuthResetSignal(lazyResult, message, exception); return; } if (PrivateImpersonationLevel < _ExpectedImpersonationLevel) { Exception exception = new AuthenticationException(SR.GetString(SR.net_auth_context_expectation, _ExpectedImpersonationLevel.ToString(), PrivateImpersonationLevel.ToString())); int statusCode = ERROR_TRUST_FAILURE; message = new byte[8]; //sizeof(long) for (int i = message.Length-1; i >= 0; --i) { message[i] = (byte)(statusCode & 0xFF); statusCode = (int) ((uint) statusCode >> 8); } StartSendAuthResetSignal(lazyResult, message, exception); return; } ProtectionLevel result = _Context.IsConfidentialityFlag? ProtectionLevel.EncryptAndSign: _Context.IsIntegrityFlag? ProtectionLevel.Sign: ProtectionLevel.None; if (result < _ExpectedProtectionLevel) { Exception exception = new AuthenticationException(SR.GetString(SR.net_auth_context_expectation, result.ToString(), _ExpectedProtectionLevel.ToString())); int statusCode = ERROR_TRUST_FAILURE; message = new byte[8]; //sizeof(long) for (int i = message.Length-1; i >= 0; --i) { message[i] = (byte)(statusCode & 0xFF); statusCode = (int) ((uint) statusCode >> 8); } StartSendAuthResetSignal(lazyResult, message, exception); return; } // Signal remote party that we are done _Framer.WriteHeader.MessageId = FrameHeader.HandshakeDoneId; if (_Context.IsServer) { // Server may complete now because client SSPI would not complain at this point. _RemoteOk = true; // However the client will wait for server to send this ACK //Force signalling server OK to the client if (message == null) { message = _EmptyMessage; } } } else if (message == null || message == _EmptyMessage) { throw new InternalException(); } if (message != null) { //even if we are comleted, there could be a blob for sending. if (lazyResult == null) { _Framer.WriteMessage(message); } else { IAsyncResult ar = _Framer.BeginWriteMessage(message, _WriteCallback, lazyResult); if (!ar.CompletedSynchronously) { return; } _Framer.EndWriteMessage(ar); } } CheckCompletionBeforeNextReceive(lazyResult); } // // This will check and logically complete the auth handshake // private void CheckCompletionBeforeNextReceive(LazyAsyncResult lazyResult) { if (HandshakeComplete && _RemoteOk) { //we are done with success if (lazyResult != null) { lazyResult.InvokeCallback(); } return; } StartReceiveBlob(lazyResult); } // // Server side starts here, but client also loops through this method // private void StartReceiveBlob(LazyAsyncResult lazyResult) { byte[] message; if (lazyResult == null) { message = _Framer.ReadMessage(); } else { IAsyncResult ar = _Framer.BeginReadMessage(_ReadCallback, lazyResult); if (!ar.CompletedSynchronously) { return; } message = _Framer.EndReadMessage(ar); } ProcessReceivedBlob(message, lazyResult); } // // // private void ProcessReceivedBlob(byte[] message, LazyAsyncResult lazyResult) { // This is an EOF otherwise we would get at least *empty* message but not a null one. if (message == null) { throw new AuthenticationException(SR.GetString(SR.net_auth_eof), null); } //Process Header information if (_Framer.ReadHeader.MessageId == FrameHeader.HandshakeErrId) { Win32Exception e = null; if (message.Length >= 8) // sizeof(long) { // Try to recover remote win32 Exception long error = 0; for (int i = 0; i < 8; ++i) error = (error<<8) + message[i]; e = new Win32Exception((int)error); } if (e != null) { if (e.NativeErrorCode == (int)SecurityStatus.LogonDenied) throw new InvalidCredentialException(SR.GetString(SR.net_auth_bad_client_creds), e); if (e.NativeErrorCode == ERROR_TRUST_FAILURE) throw new AuthenticationException(SR.GetString(SR.net_auth_context_expectation_remote), e); } throw new AuthenticationException(SR.GetString(SR.net_auth_alert), e); } if (_Framer.ReadHeader.MessageId == FrameHeader.HandshakeDoneId) { _RemoteOk = true; } else if (_Framer.ReadHeader.MessageId != FrameHeader.HandshakeId) { throw new AuthenticationException(SR.GetString(SR.net_io_header_id, "MessageId", _Framer.ReadHeader.MessageId, FrameHeader.HandshakeId), null); } CheckCompletionBeforeNextSend(message, lazyResult); } // // This will check and logically complete the auth handshake // private void CheckCompletionBeforeNextSend(byte[] message, LazyAsyncResult lazyResult) { //If we are done don't go into send if (HandshakeComplete) { if (!_RemoteOk) { throw new AuthenticationException(SR.GetString(SR.net_io_header_id, "MessageId", _Framer.ReadHeader.MessageId, FrameHeader.HandshakeDoneId), null); } if (lazyResult != null) { lazyResult.InvokeCallback(); } return; } // Not yet done, get a new blob and send it if any StartSendBlob(message, lazyResult); } // // This is to reset auth state on remote side. // If this write succeeds we will allow auth retrying. // private void StartSendAuthResetSignal(LazyAsyncResult lazyResult, byte[] message, Exception exception) { _Framer.WriteHeader.MessageId = FrameHeader.HandshakeErrId; Win32Exception win32exception = exception as Win32Exception; if (win32exception != null && win32exception.NativeErrorCode == (int)SecurityStatus.LogonDenied) if (IsServer) exception = new InvalidCredentialException(SR.GetString(SR.net_auth_bad_client_creds), exception); else exception = new InvalidCredentialException(SR.GetString(SR.net_auth_bad_client_creds_or_target_mismatch), exception); if (!(exception is AuthenticationException)) exception = new AuthenticationException(SR.GetString(SR.net_auth_SSPI), exception); if (lazyResult == null) { _Framer.WriteMessage(message); } else { lazyResult.Result = exception; IAsyncResult ar = _Framer.BeginWriteMessage(message, _WriteCallback, lazyResult); if(!ar.CompletedSynchronously) { return; } _Framer.EndWriteMessage(ar); } _CanRetryAuthentication = true; throw exception; } // // // private static void WriteCallback(IAsyncResult transportResult) { GlobalLog.Assert(transportResult.AsyncState is LazyAsyncResult, "WriteCallback|State type is wrong, expected LazyAsyncResult."); if (transportResult.CompletedSynchronously) { return; } LazyAsyncResult lazyResult = (LazyAsyncResult) transportResult.AsyncState; // Async completion try { NegoState authState = (NegoState)lazyResult.AsyncObject; authState._Framer.EndWriteMessage(transportResult); //special case for an error notification if (lazyResult.Result is Exception) { authState._CanRetryAuthentication = true; throw (Exception)lazyResult.Result; } authState.CheckCompletionBeforeNextReceive(lazyResult); } catch (Exception e) { if (lazyResult.InternalPeekCompleted) { // This will throw on a worker thread. throw; } lazyResult.InvokeCallback(e); } } // // // private static void ReadCallback(IAsyncResult transportResult) { GlobalLog.Assert(transportResult.AsyncState is LazyAsyncResult, "ReadCallback|State type is wrong, expected LazyAsyncResult."); if (transportResult.CompletedSynchronously) { return; } LazyAsyncResult lazyResult = (LazyAsyncResult) transportResult.AsyncState; // Async completion try { NegoState authState = (NegoState)lazyResult.AsyncObject; byte[] message = authState._Framer.EndReadMessage(transportResult); authState.ProcessReceivedBlob(message, lazyResult); } catch (Exception e) { if (lazyResult.InternalPeekCompleted) { // This will throw on a worker thread. throw; } lazyResult.InvokeCallback(e); } } // // // private unsafe byte[] GetOutgoingBlob(byte[] incomingBlob, ref Win32Exception e) { SecurityStatus statusCode; byte[] message = _Context.GetOutgoingBlob(incomingBlob, false, out statusCode); if (((int) statusCode & unchecked((int) 0x80000000)) != 0) { e = new System.ComponentModel.Win32Exception((int) statusCode); message = new byte[8]; //sizeof(long) for (int i = message.Length-1; i >= 0; --i) { message[i] = (byte) ((uint) statusCode & 0xFF); statusCode = (SecurityStatus) ((uint) statusCode >> 8); } } if (message != null && message.Length == 0) { message = _EmptyMessage; } return message; } // // // internal int EncryptData(byte[] buffer, int offset, int count, ref byte[] outBuffer) { CheckThrow(true); // // Well, this is to play by the rules but in reality SSPI seems to ignore this sequence number. // Means we could simply pass 0 // ++_WriteSequenceNumber; return _Context.Encrypt(buffer, offset, count, ref outBuffer, _WriteSequenceNumber); } // // // internal int DecryptData(byte[] buffer, int offset, int count, out int newOffset) { CheckThrow(true); // // Well, this is to play by the rules but in reality SSPI seems to ignore this sequence number. // Means we could simply pass 0 // ++_ReadSequenceNumber; return _Context.Decrypt(buffer, offset, count, out newOffset, _ReadSequenceNumber); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. /*++ Copyright (c) Microsoft Corporation Module Name: _NegoState.cs Abstract: The internal class is used by Negotiate Client&Server and by (internal) NegoStream. It encapsulates security context and does the real work in authentication and user data encryption with NEGO SSPI package. Author: Alexei Vopilov 12-Aug-2003 Revision History: 12-Aug-2003 New design that has obsoleted Authenticator class --*/ namespace System.Net.Security { using System; using System.Net; using System.IO; using System.Security; using System.Security.Principal; using System.Security.Permissions; using System.Threading; using System.ComponentModel; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; // // The class maintains the state of the authentication process and the security context. // On the success it returns the remote side identites created as the result // of authentication. // internal class NegoState { private const int ERROR_TRUST_FAILURE = 1790; //used to serialize protectionLevel or impersonationLevel mismatch error to the remote side private static readonly byte[] _EmptyMessage = new byte[0]; private static readonly AsyncCallback _ReadCallback = new AsyncCallback(ReadCallback); private static readonly AsyncCallback _WriteCallback = new AsyncCallback(WriteCallback); private Stream _InnerStream; private bool _LeaveStreamOpen; private Exception _Exception; private StreamFramer _Framer; private NTAuthentication _Context; private int _NestedAuth; internal const int c_MaxReadFrameSize = 64*1024; internal const int c_MaxWriteDataSize = 63*1024; //we give 1k for the framing and trailer that is laways less as per SSPI. private bool _CanRetryAuthentication; private ProtectionLevel _ExpectedProtectionLevel; private TokenImpersonationLevel _ExpectedImpersonationLevel; private uint _WriteSequenceNumber; private uint _ReadSequenceNumber; private ExtendedProtectionPolicy _ExtendedProtectionPolicy; // SSPI does not send a server ack on successfull auth. // So, this is a state variable used to gracefully handle auth confirmation private bool _RemoteOk = false; // // // internal NegoState(Stream innerStream, bool leaveStreamOpen) { if (innerStream==null) { throw new ArgumentNullException("stream"); } _InnerStream = innerStream; _LeaveStreamOpen = leaveStreamOpen; } // internal static string DefaultPackage { get { return ComNetOS.IsWin9x? NegotiationInfoClass.NTLM: NegotiationInfoClass.Negotiate; } } // // // internal void ValidateCreateContext(string package, NetworkCredential credential, string servicePrincipalName, ExtendedProtectionPolicy policy, ProtectionLevel protectionLevel, TokenImpersonationLevel impersonationLevel) { if (policy != null) { if (!AuthenticationManager.OSSupportsExtendedProtection) { if (policy.PolicyEnforcement == PolicyEnforcement.Always) { throw new PlatformNotSupportedException(SR.GetString(SR.security_ExtendedProtection_NoOSSupport)); } } else { // One of these must be set if EP is turned on if (policy.CustomChannelBinding == null && policy.CustomServiceNames == null) throw new ArgumentException(SR.GetString(SR.net_auth_must_specify_extended_protection_scheme), "policy"); } _ExtendedProtectionPolicy = policy; } else { _ExtendedProtectionPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never); } ValidateCreateContext(package, true, credential, servicePrincipalName, _ExtendedProtectionPolicy.CustomChannelBinding, protectionLevel, impersonationLevel); } // internal void ValidateCreateContext( string package, bool isServer, NetworkCredential credential, string servicePrincipalName, ChannelBinding channelBinding, ProtectionLevel protectionLevel, TokenImpersonationLevel impersonationLevel ) { if (_Exception != null && !_CanRetryAuthentication) { throw _Exception; } if (_Context != null && _Context.IsValidContext) { throw new InvalidOperationException(SR.GetString(SR.net_auth_reauth)); } if (credential == null) { throw new ArgumentNullException("credential"); } if (servicePrincipalName == null) { throw new ArgumentNullException("servicePrincipalName"); } if (ComNetOS.IsWin9x && protectionLevel != ProtectionLevel.None) { throw new NotSupportedException(SR.GetString(SR.net_auth_no_protection_on_win9x)); } if (impersonationLevel != TokenImpersonationLevel.Identification && impersonationLevel != TokenImpersonationLevel.Impersonation && impersonationLevel != TokenImpersonationLevel.Delegation) { throw new ArgumentOutOfRangeException("impersonationLevel", impersonationLevel.ToString(), SR.GetString(SR.net_auth_supported_impl_levels)); } if (_Context != null && IsServer != isServer) { throw new InvalidOperationException(SR.GetString(SR.net_auth_client_server)); } _Exception = null; _RemoteOk = false; _Framer = new StreamFramer(_InnerStream); _Framer.WriteHeader.MessageId = FrameHeader.HandshakeId; _ExpectedProtectionLevel = protectionLevel; _ExpectedImpersonationLevel = isServer? impersonationLevel: TokenImpersonationLevel.None; _WriteSequenceNumber = 0; _ReadSequenceNumber = 0; ContextFlags flags = ContextFlags.Connection; // A workaround for the client when talking to Win9x on the server side if (protectionLevel == ProtectionLevel.None && !isServer) { package = NegotiationInfoClass.NTLM; } else if (protectionLevel == ProtectionLevel.EncryptAndSign) { flags |= ContextFlags.Confidentiality; } else if (protectionLevel == ProtectionLevel.Sign) { // Assuming user expects NT4 SP4 and above flags |= ContextFlags.ReplayDetect | ContextFlags.SequenceDetect | ContextFlags.InitIntegrity; } if (isServer) { if (_ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported) { flags |= ContextFlags.AllowMissingBindings; } if (_ExtendedProtectionPolicy.PolicyEnforcement != PolicyEnforcement.Never && _ExtendedProtectionPolicy.ProtectionScenario == ProtectionScenario.TrustedProxy) { flags |= ContextFlags.ProxyBindings; } } else { // According to lzhu server side should not request any of these flags if (protectionLevel != ProtectionLevel.None) {flags |= ContextFlags.MutualAuth;} if (impersonationLevel == TokenImpersonationLevel.Identification) {flags |= ContextFlags.InitIdentify;} if (impersonationLevel == TokenImpersonationLevel.Delegation) {flags |= ContextFlags.Delegate;} } _CanRetryAuthentication = false; // // Security: We used to rely on NetworkCredential class to demand permission // Switched over to explicit ControlPrincipalPermission demand (except for DefaultCredential case) // The mitigated attack is brute-force pasword guessing through SSPI. if (!(credential is SystemNetworkCredential)) ExceptionHelper.ControlPrincipalPermission.Demand(); try { // _Context = new NTAuthentication(isServer, package, credential, servicePrincipalName, flags, channelBinding); } catch (Win32Exception e) { throw new AuthenticationException(SR.GetString(SR.net_auth_SSPI), e); } } // // private Exception SetException(Exception e) { if (_Exception == null || !(_Exception is ObjectDisposedException)) { _Exception = e; } if (_Exception != null && _Context != null) { _Context.CloseContext(); } return _Exception; } // // General informational properties // // // internal bool IsAuthenticated { get { return _Context != null && HandshakeComplete && _Exception == null && _RemoteOk; } } // // // internal bool IsMutuallyAuthenticated { get { if (!IsAuthenticated) return false; // On Win9x it is alwasy false if (ComNetOS.IsWin9x) return false; // Suppressing for NTLM since SSPI does not return correct value int the context flags. if (_Context.IsNTLM) return false; return _Context.IsMutualAuthFlag; } } // internal bool IsEncrypted { get { return IsAuthenticated && _Context.IsConfidentialityFlag; } } // internal bool IsSigned { get { return IsAuthenticated && (_Context.IsIntegrityFlag || _Context.IsConfidentialityFlag); } } // internal bool IsServer { get { return _Context != null && _Context.IsServer; } } // // NEGO specific informational properties // internal bool CanGetSecureStream { get { return (_Context.IsConfidentialityFlag || _Context.IsIntegrityFlag); } } // // // internal TokenImpersonationLevel AllowedImpersonation { get { CheckThrow(true); return PrivateImpersonationLevel; } } // private TokenImpersonationLevel PrivateImpersonationLevel { get { // according to lzhu we should suppress dlegate flag in NTLM case return (_Context.IsDelegationFlag && _Context.ProtocolName != NegotiationInfoClass.NTLM) ? TokenImpersonationLevel.Delegation :_Context.IsIdentifyFlag? TokenImpersonationLevel.Identification :(ComNetOS.IsWin9x && _Context.IsServer) ? TokenImpersonationLevel.Identification //Win9x workaround. :TokenImpersonationLevel.Impersonation; } } // // // private bool HandshakeComplete { get { return _Context.IsCompleted && _Context.IsValidContext; } } // // Note that method will demand PrincipalControlPermission // which essentially means demanding full trust // internal IIdentity GetIdentity() { CheckThrow(true); IIdentity result = null; string name = _Context.IsServer? _Context.AssociatedName: _Context.Spn; string protocol = "NTLM"; if (!ComNetOS.IsWin9x) { protocol = _Context.ProtocolName; } if (_Context.IsServer && !ComNetOS.IsWin9x) { SafeCloseHandle token = null; try { token = _Context.GetContextToken(); string authtype = _Context.ProtocolName; result = new WindowsIdentity(token.DangerousGetHandle(), authtype, WindowsAccountType.Normal, true); return result; } catch (SecurityException) { //ignore and construct generic Identity if failed due to security problem } finally { if (token != null) { token.Close(); } } } // on the client we don't have access to the remote side identity. result = new GenericIdentity(name, protocol); return result; } // // Methods // // // internal void CheckThrow(bool authSucessCheck) { if (_Exception != null) { throw _Exception; } if (authSucessCheck && !IsAuthenticated) { throw new InvalidOperationException(SR.GetString(SR.net_auth_noauth)); } } // // This is to not depend on GC&SafeHandle class if the context is not needed anymore. // internal void Close() { // Mark this instance as disposed _Exception = new ObjectDisposedException("NegotiateStream"); if (_Context != null) { _Context.CloseContext(); } } // // // internal void ProcessAuthentication(LazyAsyncResult lazyResult) { CheckThrow(false); if (Interlocked.Exchange(ref _NestedAuth, 1) == 1) { throw new InvalidOperationException(SR.GetString(SR.net_io_invalidnestedcall, lazyResult==null?"BeginAuthenticate":"Authenticate", "authenticate")); } try { if (_Context.IsServer) { // Listen for a client blob StartReceiveBlob(lazyResult); } else { // we start with the first blob StartSendBlob(null, lazyResult); } } catch (Exception e) { // Roundtrip it through the SetException() e = SetException(e); throw; } finally { if (lazyResult == null || _Exception != null) { _NestedAuth = 0; } } } // // // internal void EndProcessAuthentication(IAsyncResult result) { if (result == null) { throw new ArgumentNullException("asyncResult"); } LazyAsyncResult lazyResult = result as LazyAsyncResult; if (lazyResult == null) { throw new ArgumentException(SR.GetString(SR.net_io_async_result, result.GetType().FullName), "asyncResult"); } if (Interlocked.Exchange(ref _NestedAuth, 0) == 0) { throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndAuthenticate")); } // No "artificial" timeouts implemented so far, InnerStream controls that. lazyResult.InternalWaitForCompletion(); Exception e = lazyResult.Result as Exception; if (e != null) { // Roundtrip it through the SetException() e = SetException(e); throw e; } } private bool CheckSpn() { if (_Context.IsKerberos) { return true; } if (_ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Never || _ExtendedProtectionPolicy.CustomServiceNames == null) { return true; } if (!AuthenticationManager.OSSupportsExtendedProtection) { GlobalLog.Assert(_ExtendedProtectionPolicy.PolicyEnforcement != PolicyEnforcement.Always, "User managed to set PolicyEnforcement.Always when the OS does not support extended protection!"); return true; } string clientSpn = _Context.ClientSpecifiedSpn; if (String.IsNullOrEmpty(clientSpn)) { if (_ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported) { return true; } } else { foreach (string serviceName in _ExtendedProtectionPolicy.CustomServiceNames) { if (String.Compare(clientSpn, serviceName, StringComparison.OrdinalIgnoreCase) == 0) { return true; } } } return false; } // // Client side starts here, but server also loops through this method // private void StartSendBlob(byte[] message, LazyAsyncResult lazyResult) { Win32Exception win32exception = null; if (message != _EmptyMessage) { message = GetOutgoingBlob(message, ref win32exception); } if (win32exception != null) { // signal remote side on a failed attempt StartSendAuthResetSignal(lazyResult, message, win32exception); return; } if (HandshakeComplete) { if (_Context.IsServer && !CheckSpn()) { Exception exception = new AuthenticationException(SR.GetString(SR.net_auth_bad_client_creds_or_target_mismatch)); int statusCode = ERROR_TRUST_FAILURE; message = new byte[8]; //sizeof(long) for (int i = message.Length - 1; i >= 0; --i) { message[i] = (byte)(statusCode & 0xFF); statusCode = (int)((uint)statusCode >> 8); } StartSendAuthResetSignal(lazyResult, message, exception); return; } if (PrivateImpersonationLevel < _ExpectedImpersonationLevel) { Exception exception = new AuthenticationException(SR.GetString(SR.net_auth_context_expectation, _ExpectedImpersonationLevel.ToString(), PrivateImpersonationLevel.ToString())); int statusCode = ERROR_TRUST_FAILURE; message = new byte[8]; //sizeof(long) for (int i = message.Length-1; i >= 0; --i) { message[i] = (byte)(statusCode & 0xFF); statusCode = (int) ((uint) statusCode >> 8); } StartSendAuthResetSignal(lazyResult, message, exception); return; } ProtectionLevel result = _Context.IsConfidentialityFlag? ProtectionLevel.EncryptAndSign: _Context.IsIntegrityFlag? ProtectionLevel.Sign: ProtectionLevel.None; if (result < _ExpectedProtectionLevel) { Exception exception = new AuthenticationException(SR.GetString(SR.net_auth_context_expectation, result.ToString(), _ExpectedProtectionLevel.ToString())); int statusCode = ERROR_TRUST_FAILURE; message = new byte[8]; //sizeof(long) for (int i = message.Length-1; i >= 0; --i) { message[i] = (byte)(statusCode & 0xFF); statusCode = (int) ((uint) statusCode >> 8); } StartSendAuthResetSignal(lazyResult, message, exception); return; } // Signal remote party that we are done _Framer.WriteHeader.MessageId = FrameHeader.HandshakeDoneId; if (_Context.IsServer) { // Server may complete now because client SSPI would not complain at this point. _RemoteOk = true; // However the client will wait for server to send this ACK //Force signalling server OK to the client if (message == null) { message = _EmptyMessage; } } } else if (message == null || message == _EmptyMessage) { throw new InternalException(); } if (message != null) { //even if we are comleted, there could be a blob for sending. if (lazyResult == null) { _Framer.WriteMessage(message); } else { IAsyncResult ar = _Framer.BeginWriteMessage(message, _WriteCallback, lazyResult); if (!ar.CompletedSynchronously) { return; } _Framer.EndWriteMessage(ar); } } CheckCompletionBeforeNextReceive(lazyResult); } // // This will check and logically complete the auth handshake // private void CheckCompletionBeforeNextReceive(LazyAsyncResult lazyResult) { if (HandshakeComplete && _RemoteOk) { //we are done with success if (lazyResult != null) { lazyResult.InvokeCallback(); } return; } StartReceiveBlob(lazyResult); } // // Server side starts here, but client also loops through this method // private void StartReceiveBlob(LazyAsyncResult lazyResult) { byte[] message; if (lazyResult == null) { message = _Framer.ReadMessage(); } else { IAsyncResult ar = _Framer.BeginReadMessage(_ReadCallback, lazyResult); if (!ar.CompletedSynchronously) { return; } message = _Framer.EndReadMessage(ar); } ProcessReceivedBlob(message, lazyResult); } // // // private void ProcessReceivedBlob(byte[] message, LazyAsyncResult lazyResult) { // This is an EOF otherwise we would get at least *empty* message but not a null one. if (message == null) { throw new AuthenticationException(SR.GetString(SR.net_auth_eof), null); } //Process Header information if (_Framer.ReadHeader.MessageId == FrameHeader.HandshakeErrId) { Win32Exception e = null; if (message.Length >= 8) // sizeof(long) { // Try to recover remote win32 Exception long error = 0; for (int i = 0; i < 8; ++i) error = (error<<8) + message[i]; e = new Win32Exception((int)error); } if (e != null) { if (e.NativeErrorCode == (int)SecurityStatus.LogonDenied) throw new InvalidCredentialException(SR.GetString(SR.net_auth_bad_client_creds), e); if (e.NativeErrorCode == ERROR_TRUST_FAILURE) throw new AuthenticationException(SR.GetString(SR.net_auth_context_expectation_remote), e); } throw new AuthenticationException(SR.GetString(SR.net_auth_alert), e); } if (_Framer.ReadHeader.MessageId == FrameHeader.HandshakeDoneId) { _RemoteOk = true; } else if (_Framer.ReadHeader.MessageId != FrameHeader.HandshakeId) { throw new AuthenticationException(SR.GetString(SR.net_io_header_id, "MessageId", _Framer.ReadHeader.MessageId, FrameHeader.HandshakeId), null); } CheckCompletionBeforeNextSend(message, lazyResult); } // // This will check and logically complete the auth handshake // private void CheckCompletionBeforeNextSend(byte[] message, LazyAsyncResult lazyResult) { //If we are done don't go into send if (HandshakeComplete) { if (!_RemoteOk) { throw new AuthenticationException(SR.GetString(SR.net_io_header_id, "MessageId", _Framer.ReadHeader.MessageId, FrameHeader.HandshakeDoneId), null); } if (lazyResult != null) { lazyResult.InvokeCallback(); } return; } // Not yet done, get a new blob and send it if any StartSendBlob(message, lazyResult); } // // This is to reset auth state on remote side. // If this write succeeds we will allow auth retrying. // private void StartSendAuthResetSignal(LazyAsyncResult lazyResult, byte[] message, Exception exception) { _Framer.WriteHeader.MessageId = FrameHeader.HandshakeErrId; Win32Exception win32exception = exception as Win32Exception; if (win32exception != null && win32exception.NativeErrorCode == (int)SecurityStatus.LogonDenied) if (IsServer) exception = new InvalidCredentialException(SR.GetString(SR.net_auth_bad_client_creds), exception); else exception = new InvalidCredentialException(SR.GetString(SR.net_auth_bad_client_creds_or_target_mismatch), exception); if (!(exception is AuthenticationException)) exception = new AuthenticationException(SR.GetString(SR.net_auth_SSPI), exception); if (lazyResult == null) { _Framer.WriteMessage(message); } else { lazyResult.Result = exception; IAsyncResult ar = _Framer.BeginWriteMessage(message, _WriteCallback, lazyResult); if(!ar.CompletedSynchronously) { return; } _Framer.EndWriteMessage(ar); } _CanRetryAuthentication = true; throw exception; } // // // private static void WriteCallback(IAsyncResult transportResult) { GlobalLog.Assert(transportResult.AsyncState is LazyAsyncResult, "WriteCallback|State type is wrong, expected LazyAsyncResult."); if (transportResult.CompletedSynchronously) { return; } LazyAsyncResult lazyResult = (LazyAsyncResult) transportResult.AsyncState; // Async completion try { NegoState authState = (NegoState)lazyResult.AsyncObject; authState._Framer.EndWriteMessage(transportResult); //special case for an error notification if (lazyResult.Result is Exception) { authState._CanRetryAuthentication = true; throw (Exception)lazyResult.Result; } authState.CheckCompletionBeforeNextReceive(lazyResult); } catch (Exception e) { if (lazyResult.InternalPeekCompleted) { // This will throw on a worker thread. throw; } lazyResult.InvokeCallback(e); } } // // // private static void ReadCallback(IAsyncResult transportResult) { GlobalLog.Assert(transportResult.AsyncState is LazyAsyncResult, "ReadCallback|State type is wrong, expected LazyAsyncResult."); if (transportResult.CompletedSynchronously) { return; } LazyAsyncResult lazyResult = (LazyAsyncResult) transportResult.AsyncState; // Async completion try { NegoState authState = (NegoState)lazyResult.AsyncObject; byte[] message = authState._Framer.EndReadMessage(transportResult); authState.ProcessReceivedBlob(message, lazyResult); } catch (Exception e) { if (lazyResult.InternalPeekCompleted) { // This will throw on a worker thread. throw; } lazyResult.InvokeCallback(e); } } // // // private unsafe byte[] GetOutgoingBlob(byte[] incomingBlob, ref Win32Exception e) { SecurityStatus statusCode; byte[] message = _Context.GetOutgoingBlob(incomingBlob, false, out statusCode); if (((int) statusCode & unchecked((int) 0x80000000)) != 0) { e = new System.ComponentModel.Win32Exception((int) statusCode); message = new byte[8]; //sizeof(long) for (int i = message.Length-1; i >= 0; --i) { message[i] = (byte) ((uint) statusCode & 0xFF); statusCode = (SecurityStatus) ((uint) statusCode >> 8); } } if (message != null && message.Length == 0) { message = _EmptyMessage; } return message; } // // // internal int EncryptData(byte[] buffer, int offset, int count, ref byte[] outBuffer) { CheckThrow(true); // // Well, this is to play by the rules but in reality SSPI seems to ignore this sequence number. // Means we could simply pass 0 // ++_WriteSequenceNumber; return _Context.Encrypt(buffer, offset, count, ref outBuffer, _WriteSequenceNumber); } // // // internal int DecryptData(byte[] buffer, int offset, int count, out int newOffset) { CheckThrow(true); // // Well, this is to play by the rules but in reality SSPI seems to ignore this sequence number. // Means we could simply pass 0 // ++_ReadSequenceNumber; return _Context.Decrypt(buffer, offset, count, out newOffset, _ReadSequenceNumber); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- GenericTextProperties.cs
- XPathException.cs
- XamlTypeMapper.cs
- LifetimeServices.cs
- MaskedTextBoxDesigner.cs
- SmiContextFactory.cs
- LinearQuaternionKeyFrame.cs
- BaseDataBoundControl.cs
- TextTreeTextBlock.cs
- EventMappingSettings.cs
- LayoutEngine.cs
- JavaScriptObjectDeserializer.cs
- CompilerTypeWithParams.cs
- WebPartVerb.cs
- SortedDictionary.cs
- CustomAssemblyResolver.cs
- TypedReference.cs
- BuildProvidersCompiler.cs
- DataRelationPropertyDescriptor.cs
- ServiceModelSectionGroup.cs
- ProcessThreadCollection.cs
- ModelPerspective.cs
- SqlReferenceCollection.cs
- FactoryGenerator.cs
- Directory.cs
- DocumentDesigner.cs
- WeakReference.cs
- DataTableTypeConverter.cs
- WebPartAddingEventArgs.cs
- EventProxy.cs
- BinaryNode.cs
- Rfc2898DeriveBytes.cs
- OutKeywords.cs
- DefaultMergeHelper.cs
- PersonalizationStateInfoCollection.cs
- ADRoleFactoryConfiguration.cs
- ScrollProperties.cs
- CssClassPropertyAttribute.cs
- XDRSchema.cs
- MatrixCamera.cs
- __Error.cs
- InternalRelationshipCollection.cs
- PerfCounters.cs
- PolyBezierSegmentFigureLogic.cs
- GeneralTransform3DCollection.cs
- SequenceFullException.cs
- OleDbMetaDataFactory.cs
- HttpRuntime.cs
- KeyboardInputProviderAcquireFocusEventArgs.cs
- TileBrush.cs
- ConsumerConnectionPointCollection.cs
- DocumentAutomationPeer.cs
- NetDataContractSerializer.cs
- MarkupWriter.cs
- TextTrailingWordEllipsis.cs
- ImmutableObjectAttribute.cs
- PropertyStore.cs
- FormViewInsertedEventArgs.cs
- RepeaterItem.cs
- MemberDomainMap.cs
- SendKeys.cs
- ValueType.cs
- GridViewUpdatedEventArgs.cs
- TypeBuilder.cs
- CompiledQuery.cs
- ProxyFragment.cs
- UnconditionalPolicy.cs
- RuntimeEnvironment.cs
- SqlMethodAttribute.cs
- CodeActivityContext.cs
- TraceHandlerErrorFormatter.cs
- ProgressBarAutomationPeer.cs
- MSG.cs
- _RegBlobWebProxyDataBuilder.cs
- Solver.cs
- BuildManager.cs
- CommandSet.cs
- DbParameterHelper.cs
- DataConnectionHelper.cs
- Executor.cs
- DefaultExpression.cs
- ReadOnlyDictionary.cs
- IOException.cs
- CalendarDay.cs
- MemberAssignment.cs
- Size.cs
- Crc32.cs
- DynamicEndpointElement.cs
- InputLangChangeEvent.cs
- XmlSchemaValidationException.cs
- ClientFormsAuthenticationMembershipProvider.cs
- TimeSpanSecondsConverter.cs
- EncryptedKey.cs
- WebPartChrome.cs
- AncestorChangedEventArgs.cs
- PersonalizationStateInfoCollection.cs
- ProfileBuildProvider.cs
- XmlReflectionImporter.cs
- Solver.cs
- HorizontalAlignConverter.cs