Esempio n. 1
0
        private void BeginWriteCallback(IAsyncResult transportResult)
        {
            if (!(transportResult.AsyncState is WorkerAsyncResult))
            {
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.AssertFormat("StreamFramer::BeginWriteCallback|The state expected to be WorkerAsyncResult, received:{0}.", transportResult.AsyncState.GetType().FullName);
                }

                Debug.Fail("StreamFramer::BeginWriteCallback|The state expected to be WorkerAsyncResult, received:" + transportResult.AsyncState.GetType().FullName + ".");
            }

            if (transportResult.CompletedSynchronously)
            {
                return;
            }

            var workerResult = (WorkerAsyncResult)transportResult.AsyncState;

            try
            {
                BeginWriteComplete(transportResult);
            }
            catch (Exception e)
            {
                if (e is OutOfMemoryException)
                {
                    throw;
                }

                workerResult.InvokeCallback(e);
            }
        }
Esempio n. 2
0
        internal SafeDeleteContext GetContext(out SecurityStatusPal status)
        {
            status = new SecurityStatusPal(SecurityStatusPalErrorCode.OK);
            if (!(IsCompleted && IsValidContext))
            {
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.AssertFormat("NTAuthentication#{0}::GetContextToken|Should be called only when completed with success, currently is not!", LoggingHash.HashString(this));
                }

                Debug.Fail("NTAuthentication#" + LoggingHash.HashString(this) + "::GetContextToken |Should be called only when completed with success, currently is not!");
            }

            if (!IsServer)
            {
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.AssertFormat("NTAuthentication#{0}::GetContextToken|The method must not be called by the client side!", LoggingHash.HashString(this));
                }

                Debug.Fail("NTAuthentication#" + LoggingHash.HashString(this) + "::GetContextToken |The method must not be called by the client side!");
            }

            if (!IsValidContext)
            {
                status = new SecurityStatusPal(SecurityStatusPalErrorCode.InvalidHandle);
                return(null);
            }

            return(_securityContext);
        }
Esempio n. 3
0
        private bool CheckCompletionBeforeNextRead(int bytes)
        {
            if (bytes == 0)
            {
                // 0 bytes was requested or EOF in the beginning of a frame, the caller should decide whether it's OK.
                if (_totalRead == 0)
                {
                    _request.CompleteRequest(0);
                    return(true);
                }

                // EOF in the middle of a frame.
                throw new IOException(SR.net_io_eof);
            }

            if (_totalRead + bytes > _request.Count)
            {
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.AssertFormat("FixedSizeReader::CheckCompletion()|State got out of range. Total:{0} Count:{1}", _totalRead + bytes, _request.Count);
                }
                Debug.Fail("FixedSizeReader::CheckCompletion()|State got out of range. Total:" + (_totalRead + bytes) + " Count:" + _request.Count);
            }

            if ((_totalRead += bytes) == _request.Count)
            {
                _request.CompleteRequest(_request.Count);
                return(true);
            }

            return(false);
        }
Esempio n. 4
0
        // This will return a client token when conducted authentication on server side.
        // This token can be used for impersonation. We use it to create a WindowsIdentity and hand it out to the server app.
        internal SecurityContextTokenHandle GetContextToken(out Interop.SecurityStatus status)
        {
            if ((IsCompleted && IsValidContext) && GlobalLog.IsEnabled)
            {
                GlobalLog.AssertFormat("NTAuthentication#{0}::GetContextToken|Should be called only when completed with success, currently is not!", LoggingHash.HashString(this));
            }

            if (IsServer && GlobalLog.IsEnabled)
            {
                GlobalLog.AssertFormat("NTAuthentication#{0}::GetContextToken|The method must not be called by the client side!", LoggingHash.HashString(this));
            }

            if (!IsValidContext)
            {
                throw new Win32Exception((int)Interop.SecurityStatus.InvalidHandle);
            }

            SecurityContextTokenHandle token = null;

            status = (Interop.SecurityStatus)SSPIWrapper.QuerySecurityContextToken(
                GlobalSSPI.SSPIAuth,
                _securityContext,
                out token);

            return(token);
        }
Esempio n. 5
0
        // Allows creating a pre-completed result with less interlockeds.  Beware!  Constructor calls the callback.
        // If a derived class ever uses this and overloads Cleanup, this may need to change.
        internal LazyAsyncResult(object myObject, object myState, AsyncCallback myCallBack, object result)
        {
            bool globalLogEnabled = GlobalLog.IsEnabled;

            if (result == DBNull.Value && globalLogEnabled)
            {
                GlobalLog.AssertFormat("LazyAsyncResult#{0}::.ctor()|Result can't be set to DBNull - it's a special internal value.", LoggingHash.HashString(this));
            }

            _asyncObject   = myObject;
            _asyncState    = myState;
            _asyncCallback = myCallBack;
            _result        = result;
            _intCompleted  = 1;

            if (_asyncCallback != null)
            {
                if (globalLogEnabled)
                {
                    GlobalLog.Print("LazyAsyncResult#" + LoggingHash.HashString(this) + "::Complete() invoking callback");
                }
                _asyncCallback(this);
            }
            else if (globalLogEnabled)
            {
                GlobalLog.Print("LazyAsyncResult#" + LoggingHash.HashString(this) + "::Complete() no callback to invoke");
            }

            if (globalLogEnabled)
            {
                GlobalLog.Print("LazyAsyncResult#" + LoggingHash.HashString(this) + "::.ctor() (pre-completed)");
            }
        }
Esempio n. 6
0
        // If ContextCopy or Identity will be used, the return value should be locked until FinishPostingAsyncOp() is called
        // or the operation has been aborted (e.g. by BeginXxx throwing).  Otherwise, this can be called with false to prevent the lock
        // object from being created.
        internal object StartPostingAsyncOp(bool lockCapture)
        {
            if (InternalPeekCompleted && GlobalLog.IsEnabled)
            {
                GlobalLog.AssertFormat("ContextAwareResult#{0}::StartPostingAsyncOp|Called on completed result.", LoggingHash.HashString(this));
            }

            DebugProtectState(true);

            _lock   = lockCapture ? new object() : null;
            _flags |= StateFlags.PostBlockStarted;
            return(_lock);
        }
Esempio n. 7
0
            /// <summary>
            /// <para>Creates new timers.  This method is thread-safe.</para>
            /// </summary>
            internal override Timer CreateTimer(Callback callback, object context)
            {
                TimerNode timer = new TimerNode(callback, context, Duration, _timers);

                // Add this on the tail.  (Actually, one before the tail - _timers is the sentinel tail.)
                bool needProd = false;

                lock (_timers)
                {
                    if (!(_timers.Prev.Next == _timers))
                    {
                        if (GlobalLog.IsEnabled)
                        {
                            GlobalLog.AssertFormat("TimerThread#{0}::CreateTimer()|Tail corruption.", Thread.CurrentThread.ManagedThreadId.ToString());
                        }

                        Debug.Fail(string.Format("TimerThread#{0}::CreateTimer()|Tail corruption.", Thread.CurrentThread.ManagedThreadId.ToString()));
                    }

                    // If this is the first timer in the list, we need to create a queue handle and prod the timer thread.
                    if (_timers.Next == _timers)
                    {
                        if (_thisHandle == IntPtr.Zero)
                        {
                            _thisHandle = (IntPtr)GCHandle.Alloc(this);
                        }
                        needProd = true;
                    }

                    timer.Next        = _timers;
                    timer.Prev        = _timers.Prev;
                    _timers.Prev.Next = timer;
                    _timers.Prev      = timer;
                }

                // If, after we add the new tail, there is a chance that the tail is the next
                // node to be processed, we need to wake up the timer thread.
                if (needProd)
                {
                    TimerThread.Prod();
                }

                return(timer);
            }
Esempio n. 8
0
        // IO COMPLETION CALLBACK
        //
        // This callback is responsible for getting the complete protocol frame.
        // 1. it reads the header.
        // 2. it determines the frame size.
        // 3. loops while not all frame received or an error.
        //
        private void ReadFrameComplete(IAsyncResult transportResult)
        {
            do
            {
                if (!(transportResult.AsyncState is WorkerAsyncResult))
                {
                    if (GlobalLog.IsEnabled)
                    {
                        GlobalLog.AssertFormat("StreamFramer::ReadFrameComplete|The state expected to be WorkerAsyncResult, received:{0}.", transportResult.GetType().FullName);
                    }

                    Debug.Fail("StreamFramer::ReadFrameComplete|The state expected to be WorkerAsyncResult, received:" + transportResult.GetType().FullName + ".");
                }

                WorkerAsyncResult workerResult = (WorkerAsyncResult)transportResult.AsyncState;

                int bytesRead = _transportAPM.EndRead(transportResult);
                workerResult.Offset += bytesRead;

                if (!(workerResult.Offset <= workerResult.End))
                {
                    if (GlobalLog.IsEnabled)
                    {
                        GlobalLog.AssertFormat("StreamFramer::ReadFrameCallback|WRONG: offset - end = {0}", workerResult.Offset - workerResult.End);
                    }

                    Debug.Fail("StreamFramer::ReadFrameCallback|WRONG: offset - end = " + (workerResult.Offset - workerResult.End));
                }

                if (bytesRead <= 0)
                {
                    // (by design) This indicates the stream has receives EOF
                    // If we are in the middle of a Frame - fail, otherwise - produce EOF
                    object result = null;
                    if (!workerResult.HeaderDone && workerResult.Offset == 0)
                    {
                        result = (object)-1;
                    }
                    else
                    {
                        result = new System.IO.IOException(SR.net_frame_read_io);
                    }

                    workerResult.InvokeCallback(result);
                    return;
                }

                if (workerResult.Offset >= workerResult.End)
                {
                    if (!workerResult.HeaderDone)
                    {
                        workerResult.HeaderDone = true;
                        // This indicates the header has been read succesfully
                        _curReadHeader.CopyFrom(workerResult.Buffer, 0, _readVerifier);
                        int payloadSize = _curReadHeader.PayloadSize;
                        if (payloadSize < 0)
                        {
                            // Let's call user callback and he call us back and we will throw
                            workerResult.InvokeCallback(new System.IO.IOException(SR.Format(SR.net_frame_read_size)));
                        }

                        if (payloadSize == 0)
                        {
                            // report emtpy frame (NOT eof!) to the caller, he might be interested in
                            workerResult.InvokeCallback(0);
                            return;
                        }

                        if (payloadSize > _curReadHeader.MaxMessageSize)
                        {
                            throw new InvalidOperationException(SR.Format(SR.net_frame_size,
                                                                          _curReadHeader.MaxMessageSize.ToString(NumberFormatInfo.InvariantInfo),
                                                                          payloadSize.ToString(NumberFormatInfo.InvariantInfo)));
                        }

                        // Start reading the remaining frame data (note header does not count).
                        byte[] frame = new byte[payloadSize];
                        // Save the ref of the data block
                        workerResult.Buffer = frame;
                        workerResult.End    = frame.Length;
                        workerResult.Offset = 0;

                        // Transport.BeginRead below will pickup those changes.
                    }
                    else
                    {
                        workerResult.HeaderDone = false; // Reset for optional object reuse.
                        workerResult.InvokeCallback(workerResult.End);
                        return;
                    }
                }

                // This means we need more data to complete the data block.
                transportResult = _transportAPM.BeginRead(workerResult.Buffer, workerResult.Offset, workerResult.End - workerResult.Offset,
                                                          _readFrameCallback, workerResult);
            } while (transportResult.CompletedSynchronously);
        }
Esempio n. 9
0
            /// <summary>
            /// <para>Fires the timer if it is still active and has expired.  Returns
            /// true if it can be deleted, or false if it is still timing.</para>
            /// </summary>
            internal bool Fire()
            {
                if (_timerState == TimerState.Sentinel)
                {
                    if (GlobalLog.IsEnabled)
                    {
                        GlobalLog.AssertFormat("TimerThread#{0}::Fire()|TimerQueue tried to Fire a Sentinel.", Thread.CurrentThread.ManagedThreadId.ToString());
                    }

                    Debug.Fail(string.Format("TimerThread#{0}::Fire()|TimerQueue tried to Fire a Sentinel.", Thread.CurrentThread.ManagedThreadId.ToString()));
                }

                if (_timerState != TimerState.Ready)
                {
                    return(true);
                }

                // Must get the current tick count within this method so it is guaranteed not to be before
                // StartTime, which is set in the constructor.
                int nowMilliseconds = Environment.TickCount;

                if (IsTickBetween(StartTime, Expiration, nowMilliseconds))
                {
                    if (GlobalLog.IsEnabled)
                    {
                        GlobalLog.Print("TimerThreadTimer#" + StartTime + "::Fire() Not firing (" + StartTime + " <= " + nowMilliseconds + " < " + Expiration + ")");
                    }
                    return(false);
                }

                bool needCallback = false;

                lock (_queueLock)
                {
                    if (_timerState == TimerState.Ready)
                    {
                        if (GlobalLog.IsEnabled)
                        {
                            GlobalLog.Print("TimerThreadTimer#" + StartTime + "::Fire() Firing (" + StartTime + " <= " + nowMilliseconds + " >= " + Expiration + ")");
                        }
                        _timerState = TimerState.Fired;

                        // Remove it from the list.
                        Next.Prev = Prev;
                        Prev.Next = Next;

                        Next         = null;
                        Prev         = null;
                        needCallback = _callback != null;
                    }
                }

                if (needCallback)
                {
                    try
                    {
                        Callback callback = _callback;
                        object   context  = _context;
                        _callback = null;
                        _context  = null;
                        callback(this, nowMilliseconds, context);
                    }
                    catch (Exception exception)
                    {
                        if (ExceptionCheck.IsFatal(exception))
                        {
                            throw;
                        }

                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.PrintError(NetEventSource.ComponentType.Web, "TimerThreadTimer#" + StartTime.ToString(NumberFormatInfo.InvariantInfo) + "::Fire() - exception in callback: " + exception);
                        }

                        if (GlobalLog.IsEnabled)
                        {
                            GlobalLog.Print("TimerThreadTimer#" + StartTime + "::Fire() exception in callback: " + exception);
                        }

                        // This thread is not allowed to go into user code, so we should never get an exception here.
                        // So, in debug, throw it up, killing the AppDomain.  In release, we'll just ignore it.
#if DEBUG
                        throw;
#endif
                    }
                }

                return(true);
            }
        // This must be called right before returning the result to the user.  It might call the callback itself,
        // to avoid flowing context.  Even if the operation completes before this call, the callback won't have been
        // called.
        //
        // Returns whether the operation completed sync or not.
        private bool CaptureOrComplete(ref ExecutionContext cachedContext, bool returnContext)
        {
            if ((_flags & StateFlags.PostBlockStarted) == 0)
            {
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.AssertFormat("ContextAwareResult#{0}::CaptureOrComplete|Called without calling StartPostingAsyncOp.", LoggingHash.HashString(this));
                }

                Debug.Fail("ContextAwareResult#" + LoggingHash.HashString(this) + "::CaptureOrComplete |Called without calling StartPostingAsyncOp.");
            }

            // See if we're going to need to capture the context.
            bool capturingContext = AsyncCallback != null || (_flags & StateFlags.CaptureContext) != 0;

            // Peek if we've already completed, but don't fix CompletedSynchronously yet
            // Capture the identity if requested, unless we're going to capture the context anyway, unless
            // capturing the context won't be sufficient.
            if ((_flags & StateFlags.CaptureIdentity) != 0 && !InternalPeekCompleted && (!capturingContext))
            {
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.Print("ContextAwareResult#" + LoggingHash.HashString(this) + "::CaptureOrComplete() starting identity capture");
                }

                SafeCaptureIdentity();
            }

            // No need to flow if there's no callback, unless it's been specifically requested.
            // Note that Capture() can return null, for example if SuppressFlow() is in effect.
            if (capturingContext && !InternalPeekCompleted)
            {
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.Print("ContextAwareResult#" + LoggingHash.HashString(this) + "::CaptureOrComplete() starting capture");
                }

                if (cachedContext == null)
                {
                    cachedContext = ExecutionContext.Capture();
                }

                if (cachedContext != null)
                {
                    if (!returnContext)
                    {
                        _context      = cachedContext;
                        cachedContext = null;
                    }
                    else
                    {
                        _context = cachedContext;
                    }
                }

                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.Print("ContextAwareResult#" + LoggingHash.HashString(this) + "::CaptureOrComplete() _Context:" + LoggingHash.HashString(_context));
                }
            }
            else
            {
                // Otherwise we have to have completed synchronously, or not needed the context.
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.Print("ContextAwareResult#" + LoggingHash.HashString(this) + "::CaptureOrComplete() skipping capture");
                }

                cachedContext = null;
                if (AsyncCallback != null && !CompletedSynchronously)
                {
                    if (GlobalLog.IsEnabled)
                    {
                        GlobalLog.AssertFormat("ContextAwareResult#{0}::CaptureOrComplete|Didn't capture context, but didn't complete synchronously!", LoggingHash.HashString(this));
                    }

                    Debug.Fail("ContextAwareResult#" + LoggingHash.HashString(this) + "::CaptureOrComplete |Didn't capture context, but didn't complete synchronously!");
                }
            }

            // Now we want to see for sure what to do.  We might have just captured the context for no reason.
            // This has to be the first time the state has been queried "for real" (apart from InvokeCallback)
            // to guarantee synchronization with Complete() (otherwise, Complete() could try to call the
            // callback without the context having been gotten).
            DebugProtectState(false);
            if (CompletedSynchronously)
            {
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.Print("ContextAwareResult#" + LoggingHash.HashString(this) + "::CaptureOrComplete() completing synchronously");
                }

                base.Complete(IntPtr.Zero);
                return(true);
            }

            return(false);
        }
Esempio n. 11
0
        private unsafe static int EncryptDecryptHelper(OP op, SSPIInterface secModule, SafeDeleteContext context, SecurityBuffer[] input, uint sequenceNumber)
        {
            Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(input.Length);
            var unmanagedBuffer = new Interop.SspiCli.SecBuffer[input.Length];

            fixed(Interop.SspiCli.SecBuffer *unmanagedBufferPtr = unmanagedBuffer)
            {
                sdcInOut.pBuffers = unmanagedBufferPtr;
                GCHandle[] pinnedBuffers = new GCHandle[input.Length];
                byte[][]   buffers       = new byte[input.Length][];
                try
                {
                    for (int i = 0; i < input.Length; i++)
                    {
                        SecurityBuffer iBuffer = input[i];
                        unmanagedBuffer[i].cbBuffer   = iBuffer.size;
                        unmanagedBuffer[i].BufferType = iBuffer.type;
                        if (iBuffer.token == null || iBuffer.token.Length == 0)
                        {
                            unmanagedBuffer[i].pvBuffer = IntPtr.Zero;
                        }
                        else
                        {
                            pinnedBuffers[i]            = GCHandle.Alloc(iBuffer.token, GCHandleType.Pinned);
                            unmanagedBuffer[i].pvBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(iBuffer.token, iBuffer.offset);
                            buffers[i] = iBuffer.token;
                        }
                    }

                    // The result is written in the input Buffer passed as type=BufferType.Data.
                    int errorCode;
                    switch (op)
                    {
                    case OP.Encrypt:
                        errorCode = secModule.EncryptMessage(context, sdcInOut, sequenceNumber);
                        break;

                    case OP.Decrypt:
                        errorCode = secModule.DecryptMessage(context, sdcInOut, sequenceNumber);
                        break;

                    case OP.MakeSignature:
                        errorCode = secModule.MakeSignature(context, sdcInOut, sequenceNumber);
                        break;

                    case OP.VerifySignature:
                        errorCode = secModule.VerifySignature(context, sdcInOut, sequenceNumber);
                        break;

                    default:
                        if (GlobalLog.IsEnabled)
                        {
                            GlobalLog.Assert("SSPIWrapper::EncryptDecryptHelper", "Unknown OP: " + op);
                        }

                        Debug.Fail("SSPIWrapper::EncryptDecryptHelper", "Unknown OP: " + op);
                        throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
                    }

                    // Marshalling back returned sizes / data.
                    for (int i = 0; i < input.Length; i++)
                    {
                        SecurityBuffer iBuffer = input[i];
                        iBuffer.size = unmanagedBuffer[i].cbBuffer;
                        iBuffer.type = unmanagedBuffer[i].BufferType;

                        if (iBuffer.size == 0)
                        {
                            iBuffer.offset = 0;
                            iBuffer.token  = null;
                        }
                        else
                        {
                            checked
                            {
                                // Find the buffer this is inside of.  Usually they all point inside buffer 0.
                                int j;
                                for (j = 0; j < input.Length; j++)
                                {
                                    if (buffers[j] == null)
                                    {
                                        continue;
                                    }

                                    byte *bufferAddress = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers[j], 0);
                                    if ((byte *)unmanagedBuffer[i].pvBuffer >= bufferAddress &&
                                        (byte *)unmanagedBuffer[i].pvBuffer + iBuffer.size <= bufferAddress + buffers[j].Length)
                                    {
                                        iBuffer.offset = (int)((byte *)unmanagedBuffer[i].pvBuffer - bufferAddress);
                                        iBuffer.token  = buffers[j];
                                        break;
                                    }
                                }

                                if (j >= input.Length)
                                {
                                    if (GlobalLog.IsEnabled)
                                    {
                                        GlobalLog.Assert("SSPIWrapper::EncryptDecryptHelper", "Output buffer out of range.");
                                    }

                                    Debug.Fail("SSPIWrapper::EncryptDecryptHelper", "Output buffer out of range.");
                                    iBuffer.size   = 0;
                                    iBuffer.offset = 0;
                                    iBuffer.token  = null;
                                }
                            }
                        }

                        // Backup validate the new sizes.
                        if (iBuffer.offset < 0 || iBuffer.offset > (iBuffer.token == null ? 0 : iBuffer.token.Length))
                        {
                            if (GlobalLog.IsEnabled)
                            {
                                GlobalLog.AssertFormat("SSPIWrapper::EncryptDecryptHelper|'offset' out of range.  [{0}]", iBuffer.offset);
                            }

                            Debug.Fail("SSPIWrapper::EncryptDecryptHelper|'offset' out of range.  [" + iBuffer.offset + "]");
                        }

                        if (iBuffer.size < 0 || iBuffer.size > (iBuffer.token == null ? 0 : iBuffer.token.Length - iBuffer.offset))
                        {
                            if (GlobalLog.IsEnabled)
                            {
                                GlobalLog.AssertFormat("SSPIWrapper::EncryptDecryptHelper|'size' out of range.  [{0}]", iBuffer.size);
                            }

                            Debug.Fail("SSPIWrapper::EncryptDecryptHelper|'size' out of range.  [" + iBuffer.size + "]");
                        }
                    }

                    if (errorCode != 0 && NetEventSource.Log.IsEnabled())
                    {
                        if (errorCode == Interop.SspiCli.SEC_I_RENEGOTIATE)
                        {
                            NetEventSource.PrintError(NetEventSource.ComponentType.Security, SR.Format(SR.event_OperationReturnedSomething, op, "SEC_I_RENEGOTIATE"));
                        }
                        else
                        {
                            NetEventSource.PrintError(NetEventSource.ComponentType.Security, SR.Format(SR.net_log_operation_failed_with_error, op, String.Format(CultureInfo.CurrentCulture, "0X{0:X}", errorCode)));
                        }
                    }

                    return(errorCode);
                }
                finally
                {
                    for (int i = 0; i < pinnedBuffers.Length; ++i)
                    {
                        if (pinnedBuffers[i].IsAllocated)
                        {
                            pinnedBuffers[i].Free();
                        }
                    }
                }
            }
        }
Esempio n. 12
0
        // Accepts an incoming binary security blob and returns an outgoing binary security blob.
        internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out Interop.SecurityStatus statusCode)
        {
            GlobalLog.Enter("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob", ((incomingBlob == null) ? "0" : incomingBlob.Length.ToString(NumberFormatInfo.InvariantInfo)) + " bytes");

            var list = new List <SecurityBuffer>(2);

            if (incomingBlob != null)
            {
                list.Add(new SecurityBuffer(incomingBlob, SecurityBufferType.Token));
            }

            if (_channelBinding != null)
            {
                list.Add(new SecurityBuffer(_channelBinding));
            }

            SecurityBuffer[] inSecurityBufferArray = null;
            if (list.Count > 0)
            {
                inSecurityBufferArray = list.ToArray();
            }

            var outSecurityBuffer = new SecurityBuffer(_tokenSize, SecurityBufferType.Token);

            bool firstTime = _securityContext == null;

            try
            {
                if (!_isServer)
                {
                    // client session
                    statusCode = (Interop.SecurityStatus)SSPIWrapper.InitializeSecurityContext(
                        GlobalSSPI.SSPIAuth,
                        _credentialsHandle,
                        ref _securityContext,
                        _spn,
                        _requestedContextFlags,
                        Interop.SspiCli.Endianness.Network,
                        inSecurityBufferArray,
                        outSecurityBuffer,
                        ref _contextFlags);

                    GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob() SSPIWrapper.InitializeSecurityContext() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");

                    if (statusCode == Interop.SecurityStatus.CompleteNeeded)
                    {
                        var inSecurityBuffers = new SecurityBuffer[1];
                        inSecurityBuffers[0] = outSecurityBuffer;

                        statusCode = (Interop.SecurityStatus)SSPIWrapper.CompleteAuthToken(
                            GlobalSSPI.SSPIAuth,
                            ref _securityContext,
                            inSecurityBuffers);

                        GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.CompleteAuthToken() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
                        outSecurityBuffer.token = null;
                    }
                }
                else
                {
                    // Server session.
                    statusCode = (Interop.SecurityStatus)SSPIWrapper.AcceptSecurityContext(
                        GlobalSSPI.SSPIAuth,
                        _credentialsHandle,
                        ref _securityContext,
                        _requestedContextFlags,
                        Interop.SspiCli.Endianness.Network,
                        inSecurityBufferArray,
                        outSecurityBuffer,
                        ref _contextFlags);

                    GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob() SSPIWrapper.AcceptSecurityContext() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
                }
            }
            finally
            {
                //
                // Assuming the ISC or ASC has referenced the credential on the first successful call,
                // we want to decrement the effective ref count by "disposing" it.
                // The real dispose will happen when the security context is closed.
                // Note if the first call was not successful the handle is physically destroyed here.
                //
                if (firstTime && _credentialsHandle != null)
                {
                    _credentialsHandle.Dispose();
                }
            }


            if (((int)statusCode & unchecked ((int)0x80000000)) != 0)
            {
                CloseContext();
                _isCompleted = true;
                if (throwOnError)
                {
                    var exception = new Win32Exception((int)statusCode);
                    GlobalLog.Leave("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob", "Win32Exception:" + exception);
                    throw exception;
                }

                GlobalLog.Leave("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob", "null statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
                return(null);
            }
            else if (firstTime && _credentialsHandle != null)
            {
                // Cache until it is pushed out by newly incoming handles.
                SSPIHandleCache.CacheCredential(_credentialsHandle);
            }

            // The return value from SSPI will tell us correctly if the
            // handshake is over or not: http://msdn.microsoft.com/library/psdk/secspi/sspiref_67p0.htm
            // we also have to consider the case in which SSPI formed a new context, in this case we're done as well.
            if (statusCode == Interop.SecurityStatus.OK)
            {
                // Success.
                if ((statusCode == Interop.SecurityStatus.OK) && GlobalLog.IsEnabled)
                {
                    GlobalLog.AssertFormat("NTAuthentication#{0}::GetOutgoingBlob()|statusCode:[0x{1:x8}] ({2}) m_SecurityContext#{3}::Handle:[{4}] [STATUS != OK]", LoggingHash.HashString(this), (int)statusCode, statusCode, LoggingHash.HashString(_securityContext), LoggingHash.ObjectToString(_securityContext));
                }

                _isCompleted = true;
            }
            else
            {
                // We need to continue.
                GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob() need continue statusCode:[0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + "] (" + statusCode.ToString() + ") m_SecurityContext#" + LoggingHash.HashString(_securityContext) + "::Handle:" + LoggingHash.ObjectToString(_securityContext) + "]");
            }

            GlobalLog.Leave("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob", "IsCompleted:" + IsCompleted.ToString());
            return(outSecurityBuffer.token);
        }