_NegoState.cs source code in C# .NET

Source code for the .NET framework in C#

                        

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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK