/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    exsessup.c

Abstract:

    This module implements the routines for setting up a session using the
    the securitt negotiation mechanism ( post NT40 ).

Author:

    Balan Sethu Raman      [SethuR]      7-March-1995

Revision History:

Notes:

    The extended session setup is used in the new security negotiation scheme
    which involves multiple round trips to the server before the user can be
    successfully authenticated and a session established.

    In the modified scheme the negotiate returns a BLOB which is passed to the
    client side security package to initiate the session setup procedure. The
    BLOB returned by the server contains an encoding of the security packages
    supported by the server.

    The client side security package when presented with this BLOB chooses a
    security package and encodes the client credentials in the form of a
    BLOB which is shipped to the server using the EXTENDED_SESSION_SETUP_ANDX
    SMB.

    The server has one of three responses to an EXTENDED_SESSION_SETUP_ANDX
    SMB presented by the client.

        1) The server has enough information to establish the session.

        2) The server cannot proceed with the session setup because of an
           error in the information presented by the client or otherwise.

        3) The security package on the server needs an additional round trip
           before the session setup can be established. This is especially
           true of new security packages which support mutual authentication
           between the client and server.

    In the first two cases no further round trips are required. The action taken
    on the client side depends upon whether the server returned a BLOB. If the
    server returned a BLOB it must be presented to the client side security
    package to complete the session setup procedure.

    In the case of (3) the BLOB returned by the server must be presented to the
    client and the BLOB generated by the security package must be shipped back
    to the server.

    In the SMBCE_EXTENDED_SESSION_SETUP_EXCHANGE the following parameters support
    the protocol outlined above. A buffer with maximum server buffer size is allocated,
    locked and a MDL created as part of the exchange initialization. This buffer is 
    used to hold the server response BLOB. Notice that this avoids redundant copying 
    and handles all the known cases.

--*/

#include "precomp.h"
#pragma hdrstop

#include "exsessup.h"

#ifdef  ALLOC_PRAGMA
#pragma alloc_text(PAGE, SmbCeInitializeExtendedSessionSetupExchange)
#pragma alloc_text(PAGE, SmbCeDiscardExtendedSessionSetupExchange)
#pragma alloc_text(PAGE, SmbExtSecuritySessionSetupExchangeStart)
#pragma alloc_text(PAGE, SmbExtSecuritySessionSetupExchangeSendCompletionHandler)
#endif

// this string is used to test whether the server really supports security signature.
// if the server returns back the deferent string on SMB header security signature of the
// extended session setup response from the client sends out on the request, the server
// does support security signature.
CHAR InitialSecuritySignature[] = {"BSRSPYL "};

extern BOOLEAN MRxSmbSecuritySignaturesEnabled;

//
//  The Bug check file id for this module
//

#define BugCheckFileId  (RDBSS_BUG_CHECK_SMB_NETROOT)

//
//  The local debug trace level
//

#define Dbg (DEBUG_TRACE_DISPATCH)

//
// Forward declarations ...
//

NTSTATUS
SmbCeInitializeExtendedSessionSetupExchange(
    PSMB_EXCHANGE*  pExchangePtr,
    PMRX_V_NET_ROOT pVNetRoot)
/*++

Routine Description:

    This routine initializes an instance of a session setup exchange.

Arguments:

    pExchange - the exchange instance

    pVNetRoot - the MRX_V_NET_ROOT instance associated with the exchange.

Return Value:

    NTSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status;
    PSMB_EXTENDED_SESSION_SETUP_EXCHANGE pExtSessionSetupExchange;

    PAGED_CODE();

    ASSERT((pExchangePtr == NULL) ||
           ((*pExchangePtr)->Type == EXTENDED_SESSION_SETUP_EXCHANGE));

    Status = SmbCeInitializeExchange(
                 pExchangePtr,
                 NULL,
                 pVNetRoot,
                 EXTENDED_SESSION_SETUP_EXCHANGE,
                 &ExtendedSessionSetupExchangeDispatch);

    if (Status == STATUS_SUCCESS) {
        PSMBCEDB_SERVER_ENTRY pServerEntry;

        pServerEntry = SmbCeGetExchangeServerEntry(*pExchangePtr);

        pExtSessionSetupExchange = (PSMB_EXTENDED_SESSION_SETUP_EXCHANGE)
                                   (*pExchangePtr);


        pExtSessionSetupExchange->SmbCeFlags |= SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION;

        // Allocate the buffer to hold the server response.
        pExtSessionSetupExchange->BufferLength =
            pServerEntry->Server.MaximumBufferSize;

        pExtSessionSetupExchange->pActualBuffer = RxAllocatePoolWithTag(
                                         PagedPool,
              (pExtSessionSetupExchange->BufferLength + TRANSPORT_HEADER_SIZE),
                                         MRXSMB_KERBEROS_POOLTAG);

        pExtSessionSetupExchange->pServerResponseBlob = NULL;
        pExtSessionSetupExchange->ServerResponseBlobLength = 0;
        pExtSessionSetupExchange->Reparse = TRUE;

        if (pExtSessionSetupExchange->pActualBuffer != NULL) {
            (PCHAR) pExtSessionSetupExchange->pBuffer =
                (PCHAR) pExtSessionSetupExchange->pActualBuffer + TRANSPORT_HEADER_SIZE;

            RxAllocateHeaderMdl(
                pExtSessionSetupExchange->pBuffer,
                pExtSessionSetupExchange->BufferLength,
                pExtSessionSetupExchange->pBufferAsMdl
                );

            if (pExtSessionSetupExchange->pBufferAsMdl != NULL) {

                RxProbeAndLockHeaderPages(
                    pExtSessionSetupExchange->pBufferAsMdl,
                    KernelMode,
                    IoModifyAccess,
                    Status);
            } else {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
        } else {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }

        if (Status != STATUS_SUCCESS) {
            if (pExtSessionSetupExchange->pBufferAsMdl != NULL) {
                IoFreeMdl(pExtSessionSetupExchange->pBufferAsMdl);
            }

            if (pExtSessionSetupExchange->pActualBuffer != NULL) {
                RxFreePool(pExtSessionSetupExchange->pActualBuffer);
            }

            SmbCePrepareExchangeForReuse(*pExchangePtr);
        }
    }

    return Status;
}

VOID
SmbCeDiscardExtendedSessionSetupExchange(
    PSMB_EXTENDED_SESSION_SETUP_EXCHANGE pExtSessionSetupExchange)
/*++

Routine Description:

    This routine discards an instance of a session setup exchange.

Arguments:

    pExchange - the exchange instance

--*/
{
    PAGED_CODE();

    if (pExtSessionSetupExchange->pBufferAsMdl != NULL) {
        RxUnlockHeaderPages(pExtSessionSetupExchange->pBufferAsMdl);
        IoFreeMdl(pExtSessionSetupExchange->pBufferAsMdl);
    }

    if (pExtSessionSetupExchange->pActualBuffer != NULL) {
        RxFreePool(pExtSessionSetupExchange->pActualBuffer);
    }

    if (pExtSessionSetupExchange->pServerResponseBlob != NULL) {
        RxFreePool(pExtSessionSetupExchange->pServerResponseBlob);
    }

    // Normally discrading the exchange results in the session state being
    // updated. In order to avoid race conditions between those exchanges
    // which are awaiting this construction and the updating of the session
    // state it is done locally. COnsequently the exchange state needs to be
    // updated so the the discard routine does not attempt it again.

    pExtSessionSetupExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_SESSION_CONSTRUCTOR;

    SmbCeDiscardExchange(
        (PSMB_EXCHANGE)pExtSessionSetupExchange);
}


NTSTATUS
SmbExtSecuritySessionSetupExchangeStart(
    PSMB_EXCHANGE  pExchange)
/*++

Routine Description:

    This is the start routine for net root construction exchanges. This initiates the
    construction of the appropriate SMB's if required.

Arguments:

    pExchange - the exchange instance

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status;

    PSMB_EXTENDED_SESSION_SETUP_EXCHANGE pExtSessionSetupExchange;

    PSMB_HEADER                         pSmbHeader;
    PREQ_NT_EXTENDED_SESSION_SETUP_ANDX pSessionSetupRequest;
    PGENERIC_ANDX                       pGenericAndX;

    PSMBCEDB_SERVER_ENTRY  pServerEntry;
    PSMBCEDB_SESSION_ENTRY pSessionEntry;

    ULONG       SmbBufferUnconsumed;
    USHORT      Flags2 = 0;

    PAGED_CODE();

    pServerEntry  = SmbCeGetExchangeServerEntry(pExchange);
    pSessionEntry = SmbCeGetExchangeSessionEntry(pExchange);

    pExtSessionSetupExchange = (PSMB_EXTENDED_SESSION_SETUP_EXCHANGE)pExchange;

    if (!pExtSessionSetupExchange->FirstSessionSetup) {
        if (pSessionEntry->Header.State == SMBCEDB_ACTIVE) {
            return STATUS_SUCCESS;
        } else {
            return STATUS_USER_SESSION_DELETED;
        }
    }

    ASSERT((pExtSessionSetupExchange->Type == EXTENDED_SESSION_SETUP_EXCHANGE) &&
           (pExtSessionSetupExchange->pBuffer != NULL) &&
           (pExtSessionSetupExchange->pBufferAsMdl != NULL));

    SmbCeLog(("ExtSecSessSetup - %lx %lx\n",
              pExtSessionSetupExchange->pServerResponseBlob, pSessionEntry));
    SmbLog(LOG,
           SmbExtSecuritySessionSetupExchangeStart,
           LOGPTR(pExtSessionSetupExchange->pServerResponseBlob)
           LOGPTR(pSessionEntry));

    pSmbHeader = (PSMB_HEADER)(pExtSessionSetupExchange->pBuffer);

    // Fill in the buffer header
    pSessionSetupRequest = (PREQ_NT_EXTENDED_SESSION_SETUP_ANDX)(pSmbHeader + 1);
    pGenericAndX         = (PGENERIC_ANDX)pSessionSetupRequest;

    SmbBufferUnconsumed = pExtSessionSetupExchange->BufferLength - sizeof(SMB_HEADER);

    Flags2 |= (SMB_FLAGS2_UNICODE |
               SMB_FLAGS2_KNOWS_EAS |
               SMB_FLAGS2_KNOWS_LONG_NAMES |
               SMB_FLAGS2_NT_STATUS |
               SMB_FLAGS2_EXTENDED_SECURITY);

    *((PULONG)&pSmbHeader->Protocol) = SMB_HEADER_PROTOCOL;
    pSmbHeader->Flags      = (SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS);
    pSmbHeader->Flags2     = Flags2;
    pSmbHeader->Pid        = MRXSMB_PROCESS_ID;
    pSmbHeader->Uid        = pSessionEntry->Session.UserId;
    pSmbHeader->Tid        = 0;
    pSmbHeader->ErrorClass = 0;
    pSmbHeader->Reserved   = 0;
    pSmbHeader->Command    = SMB_COM_SESSION_SETUP_ANDX;
    SmbPutUshort(&pSmbHeader->Error,0);

    if (MRxSmbSecuritySignaturesEnabled) {
        pSmbHeader->Flags2 |= SMB_FLAGS2_SMB_SECURITY_SIGNATURE;
    }
    
    // Build the session setup and x.
    Status = SMBCE_SERVER_DIALECT_DISPATCH(
                 &pServerEntry->Server,
                 BuildSessionSetup,
                 (pExchange,
                  pGenericAndX,
                  &SmbBufferUnconsumed));

    if (Status == STATUS_SUCCESS) {
        // Update the buffer for the construction of the following SMB.
        SmbPutUshort(
            &pSessionSetupRequest->AndXOffset,
            (USHORT)(pExtSessionSetupExchange->BufferLength - SmbBufferUnconsumed));

        pSessionSetupRequest->AndXCommand  = SMB_COM_NO_ANDX_COMMAND;
        pSessionSetupRequest->AndXReserved = 0;

        if (pServerEntry->SecuritySignaturesEnabled &&
            !pServerEntry->SecuritySignaturesActive) {
            RtlCopyMemory(pSmbHeader->SecuritySignature,InitialSecuritySignature,SMB_SECURITY_SIGNATURE_LENGTH);
        }
    }

    if (Status == STATUS_SUCCESS) {
        Status = SmbCeTranceive(
                     pExchange,
                     (RXCE_SEND_PARTIAL | RXCE_SEND_SYNCHRONOUS),
                     pExtSessionSetupExchange->pBufferAsMdl,
                     (pExtSessionSetupExchange->BufferLength -
                     SmbBufferUnconsumed));

        RxDbgTrace( 0, Dbg, ("Net Root SmbCeTranceive returned %lx\n",Status));
    }


    return Status;
}

NTSTATUS
ParseExtSecuritySessionSetupResponse(
    IN PSMB_EXTENDED_SESSION_SETUP_EXCHANGE pExtSessionSetupExchange,
    IN ULONG        BytesIndicated,
    IN ULONG        BytesAvailable,
    IN  PSMB_HEADER pSmbHeader)
/*++

Routine Description:

    This is the routine used to parse the extended session setup response from
    the server.

Arguments:

    pExtSessionSetupExchange -- the exchange instance

    BytesIndicated  -- the number of bytes indicated

    BytesAvailable  -- the total number of bytes sent by the server

    pSmbHeader      -- the SMB header ( beginning of the response)

Return Value:

    NTSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status;
    ULONG    ResponseLength;
    PRESP_NT_EXTENDED_SESSION_SETUP_ANDX pSessionSetupResponse;

    PSMBCEDB_SESSION_ENTRY pSessionEntry;

    if (BytesIndicated < sizeof(SMB_HEADER) + 1) {
        // Abort the exchange. No further processing can be done.
        return STATUS_INVALID_NETWORK_RESPONSE;
    }

    pSessionEntry = SmbCeGetExchangeSessionEntry(pExtSessionSetupExchange);

    pSessionSetupResponse = (PRESP_NT_EXTENDED_SESSION_SETUP_ANDX)(pSmbHeader + 1);

    if ((pSessionSetupResponse->WordCount != 4) &&
        (pSessionSetupResponse->WordCount != 0)) {
        return STATUS_INVALID_NETWORK_RESPONSE;
    }

    // Parse the header and extract the status. The status has special significance
    // for further processing. If the server returns a BLOB and
    // STATUS_MORE_PROCESSING_REQUIRED additional round trips are required.

    pExtSessionSetupExchange->Status = GetSmbResponseNtStatus(pSmbHeader,(PSMB_EXCHANGE)pExtSessionSetupExchange);

    // Mask the errors returned by the security packagte on the server
    if (pExtSessionSetupExchange->Status == STATUS_INVALID_HANDLE) {
        pExtSessionSetupExchange->Status = STATUS_INVALID_NETWORK_RESPONSE;
    }

    //if no blob came back.....just get out
    if (pSessionSetupResponse->WordCount == 0) {
        pExtSessionSetupExchange->ServerResponseBlobLength = 0;

        return STATUS_SUCCESS;
    }

    // Squirrel away the UID on the first response. This UID needs to be used in
    // subsequent trips to complete the session establishment as it identifies
    // the session to the server.

    pSessionEntry->Session.UserId = pSmbHeader->Uid;

    if (FlagOn(SmbGetUshort(&pSessionSetupResponse->Action), SMB_SETUP_GUEST)) {
        pSessionEntry->Session.Flags |= SMBCE_SESSION_FLAGS_GUEST_SESSION;
    }

    RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseExtSecuritySessionSetupResponse BytesIndicated %ld\n",BytesIndicated));
    RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseExtSecuritySessionSetupResponse BytesAvailable %ld\n",BytesAvailable));

    // The bytes indicated should be atleast cover the SMB_HEADER and the
    // session setup response ( fixed portion )
    ResponseLength = sizeof(SMB_HEADER) +
                     FIELD_OFFSET(
                        RESP_NT_EXTENDED_SESSION_SETUP_ANDX,
                        Buffer);

    if (BytesIndicated > ResponseLength) {

        // Compute the extended session setup response length.
        pExtSessionSetupExchange->ResponseLength =
            ResponseLength +
            SmbGetUshort(
            &pSessionSetupResponse->ByteCount);

        RxDbgTrace(0,Dbg,("Kerberos session setup response length %ld\n",pExtSessionSetupExchange->ResponseLength));

        if (BytesIndicated < pExtSessionSetupExchange->ResponseLength) {
            // Set up the response for copying the data.
            if (pExtSessionSetupExchange->ResponseLength > pExtSessionSetupExchange->BufferLength) {
                Status = STATUS_BUFFER_OVERFLOW;
            } else {
                Status = pExtSessionSetupExchange->Reparse
                             ? STATUS_MORE_PROCESSING_REQUIRED
                             : STATUS_INVALID_NETWORK_RESPONSE;
            }

            pExtSessionSetupExchange->Status = Status;
        } else {
            // set up the offsets in the response.
            pExtSessionSetupExchange->ServerResponseBlobOffset =
                sizeof(SMB_HEADER) +
                FIELD_OFFSET(
                    RESP_NT_EXTENDED_SESSION_SETUP_ANDX,
                    Buffer);

            pExtSessionSetupExchange->ServerResponseBlobLength =
                pSessionSetupResponse->SecurityBlobLength;

            // Copy the response onto the buffer associated with the exchange.
            RtlCopyMemory(
                pExtSessionSetupExchange->pBuffer,
                pSmbHeader,
                pExtSessionSetupExchange->ResponseLength);

            Status = STATUS_SUCCESS;
        }
    } else {
        // Abort the exchange. No further processing can be done.
        Status = STATUS_INVALID_NETWORK_RESPONSE;
        pExtSessionSetupExchange->Status = Status;
    }
    
    return Status;
}

NTSTATUS
SmbExtSecuritySessionSetupExchangeReceive(
    IN struct _SMB_EXCHANGE *pExchange,    // The exchange instance
    IN ULONG  BytesIndicated,
    IN ULONG  BytesAvailable,
    OUT ULONG *pBytesTaken,
    IN  PSMB_HEADER pSmbHeader,
    OUT PMDL  *pDataBufferPointer,
    OUT PULONG pDataSize,
    IN ULONG  ReceiveFlags)
/*++

Routine Description:

    This is the recieve indication handling routine for net root construction exchanges

Arguments:

    pExchange - the exchange instance

    BytesIndicated - the number of bytes indicated

    Bytes Available - the number of bytes available

    pBytesTaken     - the number of bytes consumed

    pSmbHeader      - the byte buffer

    pDataBufferPointer - the buffer into which the remaining data is to be copied.

    pDataSize       - the buffer size.

Return Value:

    RXSTATUS - The return status for the operation

Notes:

    This routine is called at DPC level.

--*/
{
    NTSTATUS Status;

    PSMB_EXTENDED_SESSION_SETUP_EXCHANGE pExtSessionSetupExchange;
    PSMBCEDB_SERVER_ENTRY  pServerEntry = SmbCeGetExchangeServerEntry(pExchange);

    ULONG    SessionSetupResponseLength = 0;

    pExtSessionSetupExchange = (PSMB_EXTENDED_SESSION_SETUP_EXCHANGE)pExchange;

    if (pServerEntry->SecuritySignaturesEnabled &&
        !pServerEntry->SecuritySignaturesActive &&
        RtlCompareMemory(pSmbHeader->SecuritySignature,
                         InitialSecuritySignature,
                         SMB_SECURITY_SIGNATURE_LENGTH) != SMB_SECURITY_SIGNATURE_LENGTH) {
        pExtSessionSetupExchange->pResumptionContext->SecuritySignatureReturned = TRUE;
    }

    // Parse the response.
    Status = ParseExtSecuritySessionSetupResponse(
                 pExtSessionSetupExchange,
                 BytesIndicated,
                 BytesAvailable,
                 pSmbHeader);

    if (Status != STATUS_MORE_PROCESSING_REQUIRED) {
        *pBytesTaken = BytesAvailable;
        Status = STATUS_SUCCESS;
    } else {
        *pBytesTaken        = 0;
        *pDataBufferPointer = pExtSessionSetupExchange->pBufferAsMdl;
        *pDataSize          = pExtSessionSetupExchange->ResponseLength;
    }

    return Status;
}

NTSTATUS
SmbExtSecuritySessionSetupExchangeSendCompletionHandler(
    IN PSMB_EXCHANGE 	pExchange,    // The exchange instance
    IN PMDL       pXmitBuffer,
    IN NTSTATUS         SendCompletionStatus)
/*++

Routine Description:

    This is the send call back indication handling routine for net root construction exchanges

Arguments:

    pExchange - the exchange instance

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    return STATUS_SUCCESS;
}

NTSTATUS
SmbExtSecuritySessionSetupExchangeCopyDataHandler(
    IN PSMB_EXCHANGE 	pExchange,    // The exchange instance
    IN PMDL             pCopyDataBuffer,
    IN ULONG            DataSize)
/*++

Routine Description:

    This is the copy data handling routine for net root construction exchanges

Arguments:

    pExchange - the exchange instance

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status;

    PSMB_EXTENDED_SESSION_SETUP_EXCHANGE pExtSessionSetupExchange;
    PSMB_HEADER                      pSmbHeader;

    pExtSessionSetupExchange = (PSMB_EXTENDED_SESSION_SETUP_EXCHANGE)pExchange;

    pSmbHeader        = (PSMB_HEADER)(pExtSessionSetupExchange->pBuffer);

    Status = ParseExtSecuritySessionSetupResponse(
                 pExtSessionSetupExchange,
                 DataSize,
                 DataSize,
                 pSmbHeader);

    // At this time the parse routine cannot return STATUS_MORE_PROCESSING_REQUIRED
    // as the entire response has been consumed.

    if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
        DbgPrint("Mapping Incomplete Server Response to Invalid Response\n");
        SmbLogError(Status,
                    LOG,
                    SmbExtSecuritySessionSetupExchangeCopyDataHandler,
                    LOGPTR(pExtSessionSetupExchange));
        pExtSessionSetupExchange->Status = STATUS_INVALID_NETWORK_RESPONSE;
    }

    return STATUS_SUCCESS;
}

NTSTATUS
SmbExtSecuritySessionSetupExchangeFinalize(
    PSMB_EXCHANGE pExchange,
    BOOLEAN       *pPostFinalize)
/*++

Routine Description:

    This routine finalkzes the construct net root exchange. It resumes the RDBSS by invoking
    the call back and discards the exchange

Arguments:

    pExchange - the exchange instance

    CurrentIrql - the current interrupt request level

    pPostFinalize - a pointer to a BOOLEAN if the request should be posted

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status;

    PSMB_EXTENDED_SESSION_SETUP_EXCHANGE pExtSessionSetupExchange;
    PSMBCE_RESUMPTION_CONTEXT            pResumptionContext;

    pExtSessionSetupExchange = (PSMB_EXTENDED_SESSION_SETUP_EXCHANGE)pExchange;

    if (!pExtSessionSetupExchange->RequestPosted) {
        pExtSessionSetupExchange->RequestPosted = TRUE;
        *pPostFinalize = TRUE;
        return STATUS_SUCCESS;
    } else {
        // reset the flag since the exchange will be reused
        pExtSessionSetupExchange->RequestPosted = FALSE;
        *pPostFinalize = FALSE;
    }

    // Determine if further processing is required. If not finalize the
    // session entry.
    RxDbgTrace(0,Dbg,
              ("SmbExtSecuritySessionSetupExchangeFinalize: pESSExchange->Status = %lx\n",pExtSessionSetupExchange->Status));

    // If the server returned STATUS_MORE_PROCESSING_REQUIRED and a BLOB was present
    // another trip to the server is required and we start all over again.

    if ((Status = pExtSessionSetupExchange->Status) != STATUS_MORE_PROCESSING_REQUIRED) {

        // The server returned an error other than STATUS_MORE_PROCESSING_REQUIRED
        // The session establishment can be completed based on whether the server
        // returned a BLOB. If a BLOB was returned it needs to be passed to the
        // local security package. This will enable the local security package to
        // either complete the session establishment successfully or to extract
        // extended error information from the BLOB. This can in turn be used to
        // propogate more meaningful errors to the client.

        if (Status == STATUS_SUCCESS &&
            pExtSessionSetupExchange->ServerResponseBlobLength != 0) {
            PVOID pServerResponseBlob;

            // If we are not going back to the server an additional copy of the
            // server response BLOB is not required.

            pServerResponseBlob =
                ((PBYTE)pExtSessionSetupExchange->pBuffer +
                 pExtSessionSetupExchange->ServerResponseBlobOffset);

            Status = ValidateServerExtendedSessionSetupResponse(
                         pExtSessionSetupExchange,
                         pServerResponseBlob,
                         pExtSessionSetupExchange->ServerResponseBlobLength);
        }
    } else {
        // Make a copy of the server response blob so that the new session setup SMB
        // can be constructed

        if (pExtSessionSetupExchange->ServerResponseBlobLength != 0) {
            if (pExtSessionSetupExchange->pServerResponseBlob != NULL) {
                RxFreePool(pExtSessionSetupExchange->pServerResponseBlob);
            }

            pExtSessionSetupExchange->pServerResponseBlob =
                RxAllocatePoolWithTag(
                    PagedPool,
                    pExtSessionSetupExchange->ServerResponseBlobLength,
                    MRXSMB_KERBEROS_POOLTAG);

            if (pExtSessionSetupExchange->pServerResponseBlob != NULL) {
                RtlCopyMemory(
                    pExtSessionSetupExchange->pServerResponseBlob,
                    ((PBYTE)pExtSessionSetupExchange->pBuffer +
                     pExtSessionSetupExchange->ServerResponseBlobOffset),
                    pExtSessionSetupExchange->ServerResponseBlobLength);
            } else {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
        }
    }

    if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
        PMRX_V_NET_ROOT pVNetRoot;
        USHORT          SmbCeFlags;
        PRX_CONTEXT     RxContext;

        pVNetRoot = pExtSessionSetupExchange->SmbCeContext.pVNetRoot;
        RxContext = pExtSessionSetupExchange->RxContext;

        // This is required so that the session state is not effected as the exchange
        // is prepared for reuse. This will enable us to avoid redundant initialization
        // as well as to carry over the state from one trip to another easily.

        ClearFlag(
            pExtSessionSetupExchange->SmbCeFlags,
            SMBCE_EXCHANGE_SESSION_CONSTRUCTOR);

        SmbCePrepareExchangeForReuse((PSMB_EXCHANGE)pExtSessionSetupExchange);

        // Note: By invoking SmbCeInitializeExchange as opposed to
        // SmbCeInitializeExtendedSessionSetupExchange only the connection engine
        // portion of the exchange is initialized. This will enable us to carry
        // over the state ( the server response BLOB ) from one trip to another
        // easily.

        Status = SmbCeInitializeExchange(
                    (PSMB_EXCHANGE *)&pExtSessionSetupExchange,
                    NULL,
                    pVNetRoot,
                    EXTENDED_SESSION_SETUP_EXCHANGE,
                    &ExtendedSessionSetupExchangeDispatch);

        if (Status == STATUS_SUCCESS) {
            pExtSessionSetupExchange->SmbCeFlags |= SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION;
            pExtSessionSetupExchange->RxContext = RxContext;

            // Avoid duplicate counting during Exchange Initialization.
            SmbCeDecrementActiveExchangeCount();

            // Set the session echange state to SMBCE_EXCHANGE_SESSION_INITIALIZED
            // so that the connection engine does not queue up the request the normal
            // way. This will force the connection engine to initiate immediately.

            pExtSessionSetupExchange->SmbCeState = SMBCE_EXCHANGE_SESSION_INITIALIZED;

            Status = SmbCeInitiateExchange((PSMB_EXCHANGE)pExtSessionSetupExchange);
        }
    }

    
    if (Status != STATUS_PENDING) {
        PSMBCEDB_SERVER_ENTRY  pServerEntry;
        PSMBCEDB_SESSION_ENTRY pSessionEntry;

        SMBCEDB_OBJECT_STATE   SessionState;

        RxDbgTrace(0,Dbg,("Kerberos Exchange Session Final Status(%lx)\n",Status));

        pServerEntry  = SmbCeGetExchangeServerEntry(pExtSessionSetupExchange);
        pSessionEntry = SmbCeGetExchangeSessionEntry(pExtSessionSetupExchange);

        pResumptionContext = pExtSessionSetupExchange->pResumptionContext;

        // Tear down the exchange instance ...
        SmbCeDiscardExtendedSessionSetupExchange(pExtSessionSetupExchange);

        if (pResumptionContext != NULL) {
            pResumptionContext->Status = Status;
            SmbCeResume(pResumptionContext);
        }
    }

    return STATUS_SUCCESS;
}

SMB_EXCHANGE_DISPATCH_VECTOR
ExtendedSessionSetupExchangeDispatch =
                                   {
                                       SmbExtSecuritySessionSetupExchangeStart,
                                       SmbExtSecuritySessionSetupExchangeReceive,
                                       SmbExtSecuritySessionSetupExchangeCopyDataHandler,
                                       NULL,
                                       SmbExtSecuritySessionSetupExchangeFinalize,
                                       NULL
                                   };

