Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Net / System / Net / SecureProtocols / _NegoState.cs / 1 / _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; // // 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; // 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, bool isServer, NetworkCredential credential, string servicePrincipalName, 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) { // 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); } 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 e; } catch { // Roundtrip it through the SetException() Exception e = SetException(new Exception(SR.GetString(SR.net_nonClsCompliantException))); throw e; } 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; } } // // 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 (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); } catch { if (lazyResult.InternalPeekCompleted) { // This will throw on a worker thread. throw; } lazyResult.InvokeCallback(new Exception(SR.GetString(SR.net_nonClsCompliantException))); } } // // // 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); } catch { if (lazyResult.InternalPeekCompleted) { // This will throw on a worker thread. throw; } lazyResult.InvokeCallback(new Exception(SR.GetString(SR.net_nonClsCompliantException))); } } // // // 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); } } }
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SoapServerProtocol.cs
- WsatServiceCertificate.cs
- Point3DAnimation.cs
- Misc.cs
- CodeVariableDeclarationStatement.cs
- xdrvalidator.cs
- MenuCommand.cs
- MemberRelationshipService.cs
- ToolboxItemCollection.cs
- KeyNotFoundException.cs
- DocumentAutomationPeer.cs
- MembershipUser.cs
- Vector3DConverter.cs
- CollectionType.cs
- VoiceInfo.cs
- RawStylusActions.cs
- SQLSingleStorage.cs
- ObjectParameterCollection.cs
- XmlDataCollection.cs
- WindowsListViewItem.cs
- shaper.cs
- HwndKeyboardInputProvider.cs
- WaitForChangedResult.cs
- KerberosSecurityTokenProvider.cs
- JsonDataContract.cs
- HostingPreferredMapPath.cs
- MediaContext.cs
- ButtonColumn.cs
- CodeArgumentReferenceExpression.cs
- BaseValidator.cs
- SmiContext.cs
- State.cs
- DataTableMapping.cs
- KnownAssembliesSet.cs
- PathData.cs
- CodeDOMUtility.cs
- SafeMILHandle.cs
- SqlDelegatedTransaction.cs
- BuildDependencySet.cs
- EmptyImpersonationContext.cs
- UInt64Storage.cs
- ReflectionServiceProvider.cs
- SkinBuilder.cs
- TextEditorSelection.cs
- ProfileSection.cs
- DataQuery.cs
- ErasingStroke.cs
- DateTimeFormatInfo.cs
- WpfPayload.cs
- XsltCompileContext.cs
- RelationshipType.cs
- WebHeaderCollection.cs
- LogSwitch.cs
- NativeMethods.cs
- TextEncodedRawTextWriter.cs
- PropertyItem.cs
- ObjectItemCollectionAssemblyCacheEntry.cs
- MouseActionConverter.cs
- XmlChildNodes.cs
- FixedHighlight.cs
- JournalEntryListConverter.cs
- StructuralType.cs
- EmptyControlCollection.cs
- EventProvider.cs
- GroupBoxRenderer.cs
- HiddenField.cs
- RichTextBox.cs
- ScriptingAuthenticationServiceSection.cs
- _DynamicWinsockMethods.cs
- Attributes.cs
- ReadOnlyDictionary.cs
- TemplateField.cs
- FamilyCollection.cs
- TdsParserSafeHandles.cs
- DataServiceProviderWrapper.cs
- HtmlContainerControl.cs
- DnsElement.cs
- CoTaskMemHandle.cs
- XmlSchemaSimpleType.cs
- MenuItemCollection.cs
- MultiPropertyDescriptorGridEntry.cs
- StorageEntityTypeMapping.cs
- TextServicesHost.cs
- PersonalizationProviderHelper.cs
- IResourceProvider.cs
- ListMarkerLine.cs
- BrowserTree.cs
- OutputCacheModule.cs
- DrawingGroup.cs
- XmlSiteMapProvider.cs
- Models.cs
- XmlReflectionMember.cs
- XmlQueryStaticData.cs
- WsdlBuildProvider.cs
- ObjectAssociationEndMapping.cs
- InvalidEnumArgumentException.cs
- TreeBuilderXamlTranslator.cs
- EditorAttribute.cs
- ConfigurationManagerInternal.cs
- RelationshipManager.cs