/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    smbadmin.c

Abstract:

    This module contains routines for processing the administrative SMBs:
    negotiate, session setup, tree connect, and logoff.

Author:

    David Treadwell (davidtr)    30-Oct-1989

Revision History:

--*/

#include "precomp.h"
#include "smbadmin.tmh"
#pragma hdrstop

#define ENCRYPT_TEXT_LENGTH 20

VOID
GetEncryptionKey (
    OUT CHAR EncryptionKey[MSV1_0_CHALLENGE_LENGTH]
    );

VOID SRVFASTCALL
BlockingSessionSetupAndX (
    IN OUT PWORK_CONTEXT WorkContext
    );

NTSTATUS
GetNtSecurityParameters(
    IN PWORK_CONTEXT WorkContext,
    OUT PCHAR *CasesensitivePassword,
    OUT PULONG CasesensitivePasswordLength,
    OUT PCHAR *CaseInsensitivePassword,
    OUT PULONG CaseInsensitivePasswordLength,
    OUT PUNICODE_STRING UserName,
    OUT PUNICODE_STRING DomainName,
    OUT PCHAR *RestOfDataBuffer,
    OUT PULONG RestOfDataLength );

VOID
BuildSessionSetupAndXResponse(
    IN PWORK_CONTEXT WorkContext,
    IN UCHAR NextCommand,
    IN USHORT Action,
    IN BOOLEAN IsUnicode);

NTSTATUS
GetExtendedSecurityParameters(
    IN PWORK_CONTEXT WorkContext,
    OUT PUCHAR *SecurityBuffer,
    OUT PULONG SecurityBufferLength,
    OUT PCHAR  *RestOfDataBuffer,
    OUT PULONG RestOfDataLength );

VOID
BuildExtendedSessionSetupAndXResponse(
    IN PWORK_CONTEXT WorkContext,
    IN ULONG SecurityBlobLength,
    IN NTSTATUS Status,
    IN UCHAR NextCommand,
    IN BOOLEAN IsUnicode);

VOID
InsertNativeOSAndType(
    IN BOOLEAN IsUnicode,
    OUT PCHAR Buffer,
    OUT PUSHORT ByteCount);

//
// EncryptionKeyCount is a monotonically increasing count of the number
// of times GetEncryptionKey has been called.  This number is added to
// the system time to ensure that we do not use the same seed twice in
// generating a random challenge.
//

STATIC
ULONG EncryptionKeyCount = 0;

ULONG SrvKsecValidErrors = 0;

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvSmbNegotiate )
#pragma alloc_text( PAGE, SrvSmbProcessExit )
#pragma alloc_text( PAGE, SrvSmbSessionSetupAndX )
#pragma alloc_text( PAGE, BlockingSessionSetupAndX )
#pragma alloc_text( PAGE, SrvSmbLogoffAndX )
#pragma alloc_text( PAGE, GetEncryptionKey )
#pragma alloc_text( PAGE, GetNtSecurityParameters )
#pragma alloc_text( PAGE, BuildSessionSetupAndXResponse )
#pragma alloc_text( PAGE, GetExtendedSecurityParameters )
#pragma alloc_text( PAGE, BuildExtendedSessionSetupAndXResponse )
#pragma alloc_text( PAGE, InsertNativeOSAndType )

#endif


SMB_PROCESSOR_RETURN_TYPE
SrvSmbNegotiate (
    SMB_PROCESSOR_PARAMETERS
    )

/*++

Routine Description:

    Processes a negotiate SMB.

Arguments:

    SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
        of the parameters to SMB processor routines.

Return Value:

    SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h

--*/

{
    PREQ_NEGOTIATE request;
    PRESP_NT_NEGOTIATE ntResponse;
    PRESP_NEGOTIATE response;
    PRESP_OLD_NEGOTIATE respOldNegotiate;
    PCONNECTION connection;
    PENDPOINT endpoint;
    PPAGED_CONNECTION pagedConnection;
    USHORT byteCount;
    USHORT flags2;
    PSMB_HEADER smbHeader;

    PSZ s, es;
    SMB_DIALECT bestDialect, serverDialect, firstDialect;
    USHORT consumerDialectChosen, consumerDialect;
    LARGE_INTEGER serverTime;
    SMB_DATE date;
    SMB_TIME time;
    ULONG capabilities;
    NTSTATUS   status    = STATUS_SUCCESS;
    SMB_STATUS SmbStatus = SmbStatusInProgress;

    PAGED_CODE( );
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_NEGOTIATE;
    SrvWmiStartContext(WorkContext);

    IF_SMB_DEBUG(ADMIN1) {
        SrvPrint2( "Negotiate request header at 0x%p, response header at 0x%p\n",
                    WorkContext->RequestHeader, WorkContext->ResponseHeader );
        SrvPrint2( "Negotiate request parameters at 0x%p, response parameters at 0x%p\n",
                    WorkContext->RequestParameters,
                    WorkContext->ResponseParameters );
    }

    //
    // Set up input and output buffers for parameters.
    //

    request = (PREQ_NEGOTIATE)WorkContext->RequestParameters;
    response = (PRESP_NEGOTIATE)WorkContext->ResponseParameters;
    ntResponse = (PRESP_NT_NEGOTIATE)WorkContext->ResponseParameters;
    smbHeader = WorkContext->RequestHeader;

    //
    // Make sure that this is the first negotiate command sent.
    // SrvStartListen() sets the dialect to illegal, so if it has changed
    // then a negotiate SMB has already been sent.
    //

    connection = WorkContext->Connection;
    pagedConnection = connection->PagedConnection;
    endpoint = connection->Endpoint;
    if ( connection->SmbDialect != SmbDialectIllegal ) {

        IF_DEBUG(SMB_ERRORS) {
            SrvPrint0( "SrvSmbNegotiate: Command already sent.\n" );
        }

        SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
        status    = STATUS_INVALID_SMB;
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // We don't know anything about the version number of this client yet.
    //
    pagedConnection->ClientBuildNumber = 0;

#if SRVNTVERCHK
    pagedConnection->ClientTooOld = FALSE;
#endif

    //
    // Find out which (if any) of the sent dialect strings matches the
    // dialect strings known by this server.  The ByteCount is verified
    // to be legitimate in SrvProcessSmb, so it is not possible to walk
    // off the end of the SMB here.
    //

    bestDialect = SmbDialectIllegal;
    consumerDialectChosen = (USHORT)0xFFFF;
    es = END_OF_REQUEST_SMB( WorkContext );

    if( endpoint->IsPrimaryName ) {
        firstDialect = FIRST_DIALECT;
    } else {
        firstDialect = FIRST_DIALECT_EMULATED;
    }

    for ( s = (PSZ)request->Buffer, consumerDialect = 0;
          s <= es && s < SmbGetUshort( &request->ByteCount ) + (PSZ)request->Buffer;
          consumerDialect++ ) {

        if ( *s++ != SMB_FORMAT_DIALECT ) {

            IF_DEBUG(SMB_ERRORS) {
                SrvPrint0( "SrvSmbNegotiate: Invalid dialect format code.\n" );
            }

            SrvLogInvalidSmb( WorkContext );

            SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
            status    = STATUS_INVALID_SMB;
            SmbStatus = SmbStatusSendResponse;
            goto Cleanup;
        }

        for ( serverDialect = firstDialect;
             serverDialect < bestDialect;
             serverDialect++ ) {

            if ( !strcmp( s, StrDialects[serverDialect] ) ) {
                IF_SMB_DEBUG(ADMIN2) {
                    SrvPrint2( "Matched: %s and %s\n",
                                StrDialects[serverDialect], s );
                }

                bestDialect = serverDialect;
                consumerDialectChosen = consumerDialect;
            }
        }

        //
        // Go to the next dialect string
        //
        for( ; *s && s < es; s++ )
            ;

        //
        // We are now at the end of the buffer, or are pointing to the NULL.
        // Advance the pointer.  If we are at the end of the buffer, the test in
        // the loop will terminate.
        //
        s++;
    }

    connection->SmbDialect = bestDialect;

    if( bestDialect <= SmbDialectNtLanMan ) {
        connection->IpxDropDuplicateCount = MIN_IPXDROPDUP;
    } else {
        connection->IpxDropDuplicateCount = MAX_IPXDROPDUP;
    }

    IF_SMB_DEBUG(ADMIN1) {
        SrvPrint2( "Choosing dialect #%ld, string = %s\n",
                    consumerDialectChosen, StrDialects[bestDialect] );
    }

    //
    //  Determine the current system time on the server.  We use this
    //  to determine the time zone of the server and to tell the client
    //  the current time of day on the server.
    //

    KeQuerySystemTime( &serverTime );

    //
    // If the consumer only knows the core protocol, return short (old)
    // form of the negotiate response.  Also, if no dialect is acceptable,
    // return 0xFFFF as the selected dialect.
    //

    if ( bestDialect == SmbDialectPcNet10 ||
         consumerDialectChosen == (USHORT)0xFFFF ) {

        respOldNegotiate = (PRESP_OLD_NEGOTIATE)response;
        respOldNegotiate->WordCount = 1;
        SmbPutUshort( &respOldNegotiate->DialectIndex, consumerDialectChosen );
        SmbPutUshort( &respOldNegotiate->ByteCount, 0 );
        WorkContext->ResponseParameters = NEXT_LOCATION(
                                            respOldNegotiate,
                                            RESP_OLD_NEGOTIATE,
                                            0
                                            );

    }

    else if ( bestDialect > SmbDialectNtLanMan ) {

        USHORT securityMode;

        //
        // Send the OS/2 LAN Man SMB response.
        //

        WorkContext->ResponseHeader->Flags =
            (UCHAR)(WorkContext->RequestHeader->Flags | SMB_FLAGS_LOCK_AND_READ_OK);

        response->WordCount = 13;
        SmbPutUshort( &response->DialectIndex, consumerDialectChosen );

        //
        // Indicate that we're user-level security and that we
        // want encrypted passwords.
        //

        securityMode = NEGOTIATE_USER_SECURITY | NEGOTIATE_ENCRYPT_PASSWORDS;

        SmbPutUshort(
            &response->SecurityMode,
            securityMode
            );

        //
        // Get an encryption key for this connection.
        //

        GetEncryptionKey( pagedConnection->EncryptionKey );

        SmbPutUshort( &response->EncryptionKeyLength, MSV1_0_CHALLENGE_LENGTH );
        SmbPutUshort( &response->Reserved, 0 );
        byteCount = MSV1_0_CHALLENGE_LENGTH;

        RtlCopyMemory(
            response->Buffer,
            pagedConnection->EncryptionKey,
            MSV1_0_CHALLENGE_LENGTH
            );

        if ( endpoint->IsConnectionless ) {

            ULONG adapterNumber;
            ULONG maxBufferSize;

            //
            // Our server max buffer size is the smaller of the
            // server receive buffer size and the ipx transport
            // indicated max packet size.
            //

            adapterNumber =
                WorkContext->ClientAddress->DatagramOptions.LocalTarget.NicId;

            maxBufferSize = GetIpxMaxBufferSize(
                                        endpoint,
                                        adapterNumber,
                                        SrvReceiveBufferLength
                                        );

            SmbPutUshort(
                &response->MaxBufferSize,
                (USHORT)maxBufferSize
                );

        } else {

            SmbPutUshort(
                &response->MaxBufferSize,
                (USHORT)SrvReceiveBufferLength
                );
        }

        SmbPutUshort( &response->MaxMpxCount, MIN(125, SrvMaxMpxCount) );   // Only send max of 125 to Win9x machines since they'll not connect if higher
        SmbPutUshort( &response->MaxNumberVcs, (USHORT)SrvMaxNumberVcs );
        SmbPutUlong( &response->SessionKey, 0 );

        //
        // If this is an MS-NET 1.03 client or before, then tell him that we
        // don't support raw writes.  MS-NET 1.03 does different things with
        // raw writes that are more trouble than they're worth, and since
        // raw is simply a performance issue, we don't support it.
        //

        if ( bestDialect >= SmbDialectMsNet103 ) {

            SmbPutUshort(
                &response->RawMode,
                (USHORT)(SrvEnableRawMode ?
                        NEGOTIATE_READ_RAW_SUPPORTED :
                        0)
                );

        } else {

            SmbPutUshort(
                &response->RawMode,
                (USHORT)(SrvEnableRawMode ?
                        NEGOTIATE_READ_RAW_SUPPORTED |
                        NEGOTIATE_WRITE_RAW_SUPPORTED :
                        0)
                );
        }

        SmbPutUlong( &response->SessionKey, 0 );

        SrvTimeToDosTime( &serverTime, &date, &time );

        SmbPutDate( &response->ServerDate, date );
        SmbPutTime( &response->ServerTime, time );

        //
        // Get time zone bias.  We compute this during session
        // setup  rather than once during server startup because
        // we might switch from daylight time to standard time
        // or vice versa during normal server operation.
        //

        SmbPutUshort( &response->ServerTimeZone,
                      SrvGetOs2TimeZone(&serverTime) );

        if ( bestDialect == SmbDialectLanMan21 ||
             bestDialect == SmbDialectDosLanMan21 ) {

            //
            // Append the domain to the SMB.
            //

            RtlCopyMemory(
                response->Buffer + byteCount,
                endpoint->OemDomainName.Buffer,
                endpoint->OemDomainName.Length + sizeof(CHAR)
                );

            byteCount += endpoint->OemDomainName.Length + sizeof(CHAR);

        }

        SmbPutUshort( &response->ByteCount, byteCount );
        WorkContext->ResponseParameters = NEXT_LOCATION(
                                              response,
                                              RESP_NEGOTIATE,
                                              byteCount
                                              );

    } else {

        //
        // NT or better protocol has been negotiated.
        //

        flags2 = SmbGetAlignedUshort( &smbHeader->Flags2 );

        //
        // We are going to attempt to validate this user with one of the listed
        // security packages at the end of the smb.  Currently the smb will
        // simply contain the output of EnumerateSecurityPackages.
        //

        if ( flags2 & SMB_FLAGS2_EXTENDED_SECURITY ) {

            if (!WorkContext->UsingExtraSmbBuffer) {
                status = SrvAllocateExtraSmbBuffer(WorkContext);
                if (!NT_SUCCESS(status)) {
                    SrvSetSmbError(WorkContext, status);
                    SmbStatus = SmbStatusSendResponse;
                    goto Cleanup;
                }

                RtlCopyMemory(
                    WorkContext->ResponseHeader,
                    WorkContext->RequestHeader,
                    sizeof( SMB_HEADER )
                    );
            }
            ntResponse = (PRESP_NT_NEGOTIATE)WorkContext->ResponseParameters;
            capabilities = CAP_EXTENDED_SECURITY;
        } else {
            capabilities = 0;
        }
#ifdef INCLUDE_SMB_PERSISTENT
        if ( bestDialect == SmbDialectNtLanMan2 ) {

            capabilities |= CAP_PERSISTENT_HANDLES;

            SrvPrint1( "SrvSmbNegotiate: persistent handles for conn 0x%x.\n",
                        WorkContext->Connection );
        }
#endif
        ntResponse->WordCount = 17;
        SmbPutUshort( &ntResponse->DialectIndex, consumerDialectChosen );

        // !!! This says that we don't want encrypted passwords.

        // If this is negotiating NtLanMan, but not UNICODE, we know its not a Win9x client
        // so it can handle MaxMpx larger than 125
        if( flags2 & SMB_FLAGS2_UNICODE )
        {
            SmbPutUshort( &ntResponse->MaxMpxCount, SrvMaxMpxCount );
        }
        else
        {
            // Again, for the Win9x problems we need to minimize the Mpx count.
            SmbPutUshort( &ntResponse->MaxMpxCount, MIN(125,SrvMaxMpxCount) );
        }
        SmbPutUshort( &ntResponse->MaxNumberVcs, (USHORT)SrvMaxNumberVcs );
        SmbPutUlong( &ntResponse->MaxRawSize, 64 * 1024 ); // !!!
        SmbPutUlong( &ntResponse->SessionKey, 0 );

        capabilities |= CAP_RAW_MODE            |
                       CAP_UNICODE              |
                       CAP_LARGE_FILES          |
                       CAP_NT_SMBS              |
                       CAP_NT_FIND              |
                       CAP_RPC_REMOTE_APIS      |
                       CAP_NT_STATUS            |
                       CAP_LEVEL_II_OPLOCKS     |
                       CAP_INFOLEVEL_PASSTHRU   |
                       CAP_LOCK_AND_READ;

        //
        // If we're supporting Dfs operations, let the client know about it.
        //
        if( SrvDfsFastIoDeviceControl ) {
            capabilities |= CAP_DFS;
        }

        if ( endpoint->IsConnectionless ) {

            ULONG adapterNumber;
            ULONG maxBufferSize;

            capabilities |= CAP_MPX_MODE;
            capabilities &= ~CAP_RAW_MODE;

            //
            // Our server max buffer size is the smaller of the
            // server receive buffer size and the ipx transport
            // indicated max packet size.
            //

            adapterNumber =
                WorkContext->ClientAddress->DatagramOptions.LocalTarget.NicId;

            maxBufferSize = GetIpxMaxBufferSize(
                                        endpoint,
                                        adapterNumber,
                                        SrvReceiveBufferLength
                                        );

            SmbPutUlong(
                &ntResponse->MaxBufferSize,
                maxBufferSize
                );

        } else {

            SmbPutUlong(
                &ntResponse->MaxBufferSize,
                SrvReceiveBufferLength
                );

            capabilities |= CAP_LARGE_READX;

            //
            // Unfortunately, NetBT is the only protocol that reliably supports
            //  transfers exceeding the negotiated buffer size.  So disable the
            //  other protocols for now (hopefully)
            //
            if( connection->ClientIPAddress ) {
                capabilities |= CAP_LARGE_WRITEX;

                if( SrvSupportsCompression ) {
                    capabilities |= CAP_COMPRESSED_DATA;
                }
            }
        }

        SmbPutUlong( &ntResponse->Capabilities, capabilities );

        //
        // Stick the servers system time and timezone in the negotiate
        // response.
        //

        SmbPutUlong( &ntResponse->SystemTimeLow, serverTime.LowPart );
        SmbPutUlong( &ntResponse->SystemTimeHigh, serverTime.HighPart );

        SmbPutUshort( &ntResponse->ServerTimeZone,
                      SrvGetOs2TimeZone(&serverTime) );

        //
        // Indicate that we're user-level security and that we
        // want encrypted passwords.
        //

        ntResponse->SecurityMode =
                NEGOTIATE_USER_SECURITY | NEGOTIATE_ENCRYPT_PASSWORDS;

        //
        // There is a bug in some W9x clients that preclude the use of security
        //  signatures.  We have produced a fix for vredir.vxd for this, but we
        //  can not tell whether or not we are working with one of these fixed
        //  clients.  The only way i can think of to tell the difference between
        //  a W9x client and a properly functioning NT client is to look to see
        //  if the client understands NT status codes.
        //
        if( SrvSmbSecuritySignaturesEnabled &&

            ( SrvEnableW9xSecuritySignatures == TRUE ||
              (flags2 & SMB_FLAGS2_NT_STATUS) ) ) {

            ntResponse->SecurityMode |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;

            if( SrvSmbSecuritySignaturesRequired ) {
                ntResponse->SecurityMode |= NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;
            }
        }

        //
        // Get an encryption key for this connection.
        //

        if ((capabilities & CAP_EXTENDED_SECURITY) == 0) {
            GetEncryptionKey( pagedConnection->EncryptionKey );

            RtlCopyMemory(
                ntResponse->Buffer,
                pagedConnection->EncryptionKey,
                MSV1_0_CHALLENGE_LENGTH
                );

            ASSERT ( MSV1_0_CHALLENGE_LENGTH <= 0xff ) ;

            ntResponse->EncryptionKeyLength = MSV1_0_CHALLENGE_LENGTH;

            byteCount = MSV1_0_CHALLENGE_LENGTH;

            {
                USHORT domainLength;
                PWCH buffer = (PWCHAR)( ntResponse->Buffer+byteCount );
                PWCH ptr;

                domainLength = endpoint->DomainName.Length +
                                      sizeof(UNICODE_NULL);
                ptr = endpoint->DomainName.Buffer;

                RtlCopyMemory(
                    buffer,
                    ptr,
                    domainLength
                    );

                byteCount += domainLength;

                //
                // Append the server name to the response.
                //
                if( SrvComputerName.Buffer ) {

                    buffer = (PWCHAR)((LPSTR)buffer + domainLength);

                    RtlCopyMemory( buffer,
                                   SrvComputerName.Buffer,
                                   SrvComputerName.Length
                                 );

                    SmbPutUshort( &buffer[ SrvComputerName.Length / 2 ], UNICODE_NULL );

                    byteCount += SrvComputerName.Length + sizeof( UNICODE_NULL );
                }

            }

            SmbPutUshort( &ntResponse->ByteCount, byteCount );

            WorkContext->ResponseParameters = NEXT_LOCATION(
                                                  ntResponse,
                                                  RESP_NT_NEGOTIATE,
                                                  byteCount
                                                  );

        } // if !(capabilities & CAP_EXTENDED_SECURITY)
        else {
            CtxtHandle negotiateHandle;
            USHORT bufferLength;
            PCHAR buffer;

            //
            // Reserved if extended security negotiated (MBZ!)
            //

            ntResponse->EncryptionKeyLength = 0;

            //
            // SrvGetExtensibleSecurityNegotiateBuffer will fill in the
            // securityblob field and return the length of that information.
            //

            RtlCopyMemory(&ntResponse->Buffer, &ServerGuid, sizeof(ServerGuid) );
            byteCount = sizeof(ServerGuid);

            buffer = ntResponse->Buffer + byteCount;

            status = SrvGetExtensibleSecurityNegotiateBuffer(
                                    &negotiateHandle,
                                    buffer,
                                    &bufferLength
                                    );


            if (!NT_SUCCESS(status)) {
                SrvSetSmbError(WorkContext, STATUS_ACCESS_DENIED);
                status    = STATUS_ACCESS_DENIED;
                SmbStatus = SmbStatusSendResponse;
                goto Cleanup;
            }

            //
            // Grab the session locks here...
            //

            ACQUIRE_LOCK( &connection->Lock );

            connection->NegotiateHandle = negotiateHandle;

            RELEASE_LOCK( &connection->Lock );

            byteCount += bufferLength;

            SmbPutUshort( &ntResponse->ByteCount, byteCount );

            WorkContext->ResponseParameters = NEXT_LOCATION(
                                                  ntResponse,
                                                  RESP_NT_NEGOTIATE,
                                                  byteCount
                                                  );
        }
    } // else (NT protocol has been negotiated).

    SmbStatus = SmbStatusSendResponse;

    IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbNegotiate complete.\n" );

Cleanup:
    SrvWmiEndContext(WorkContext);
    return SmbStatus;

} // SrvSmbNegotiate


SMB_PROCESSOR_RETURN_TYPE
SrvSmbProcessExit (
    SMB_PROCESSOR_PARAMETERS
    )

/*++

Routine Description:

    Processes a Process Exit SMB.

Arguments:

    SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
        of the parameters to SMB processor routines.

Return Value:

    SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h

--*/

{

    PREQ_PROCESS_EXIT request;
    PRESP_PROCESS_EXIT response;

    PSESSION session;
    USHORT pid;
    NTSTATUS status = STATUS_SUCCESS;

    PAGED_CODE( );
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_PROCESS_EXIT;
    SrvWmiStartContext(WorkContext);

    IF_SMB_DEBUG(ADMIN1) {
        SrvPrint2( "Process exit request header at 0x%p, response header at 0x%p\n",
                    WorkContext->RequestHeader,
                    WorkContext->ResponseHeader );
        SrvPrint2( "Process exit request parameters at 0x%p, response parameters at 0x%p\n",
                    WorkContext->RequestParameters,
                    WorkContext->ResponseParameters );
    }

    //
    // Set up parameters.
    //

    request = (PREQ_PROCESS_EXIT)(WorkContext->RequestParameters);
    response = (PRESP_PROCESS_EXIT)(WorkContext->ResponseParameters);

    //
    // If a session block has not already been assigned to the current
    // work context, verify the UID.  If verified, the address of the
    // session block corresponding to this user is stored in the
    // WorkContext block and the session block is referenced.
    //

    session = SrvVerifyUid(
                  WorkContext,
                  SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )
                  );

    if ( session == NULL ) {

        IF_DEBUG(SMB_ERRORS) {
            SrvPrint1( "SrvSmbProcessExit: Invalid UID: 0x%lx\n",
                SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ) );
        }

        SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID );
        status = STATUS_SMB_BAD_UID;
        goto Cleanup;
    }

    //
    // Close all files with the same PID as in the header for this request.
    //

    pid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid );

    IF_SMB_DEBUG(ADMIN1) SrvPrint1( "Closing files with PID = %lx\n", pid );

    SrvCloseRfcbsOnSessionOrPid( session, &pid );

    //
    // Close all searches with the same PID as in the header for this request.
    //

    IF_SMB_DEBUG(ADMIN1) SrvPrint1( "Closing searches with PID = %lx\n", pid );

    SrvCloseSearches(
            session->Connection,
            (PSEARCH_FILTER_ROUTINE)SrvSearchOnPid,
            (PVOID) pid,
            NULL
            );

    //
    // Close any cached directories for this client
    //
    SrvCloseCachedDirectoryEntries( session->Connection );

    //
    // Build the response SMB.
    //

    response->WordCount = 0;
    SmbPutUshort( &response->ByteCount, 0 );

    WorkContext->ResponseParameters = NEXT_LOCATION(
                                          response,
                                          RESP_PROCESS_EXIT,
                                          0
                                          );

Cleanup:
    SrvWmiEndContext(WorkContext);
    return SmbStatusSendResponse;

} // SrvSmbProcessExit


SMB_PROCESSOR_RETURN_TYPE
SrvSmbSessionSetupAndX(
    SMB_PROCESSOR_PARAMETERS
    )

/*++

Routine Description:

    Processes a session setup and X SMB.

Arguments:

    SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
        of the parameters to SMB processor routines.

Return Value:

    SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h

--*/

{
    PAGED_CODE();
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_SESSION_SETUP_AND_X;
    SrvWmiStartContext(WorkContext);

    //
    // This SMB must be processed in a blocking thread.
    //

    WorkContext->FspRestartRoutine = BlockingSessionSetupAndX;
    SrvQueueWorkToBlockingThread( WorkContext );
    SrvWmiEndContext(WorkContext);
    return SmbStatusInProgress;

} // SrvSmbSessionSetupAndX


VOID SRVFASTCALL
BlockingSessionSetupAndX(
    IN OUT PWORK_CONTEXT WorkContext
    )

/*++

Routine Description:

    Processes a session setup and X SMB.

Arguments:

    SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
        of the parameters to SMB processor routines.

Return Value:

    SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h

--*/

{
    PREQ_SESSION_SETUP_ANDX request;
    PREQ_NT_SESSION_SETUP_ANDX ntRequest;
    PREQ_NT_EXTENDED_SESSION_SETUP_ANDX ntExtendedRequest;
    PRESP_SESSION_SETUP_ANDX response;

    NTSTATUS SecStatus ;
    NTSTATUS status = STATUS_SUCCESS;
    SMB_STATUS SmbStatus = SmbStatusInProgress;
    PSESSION session;
    PCONNECTION connection;
    PENDPOINT endpoint;
    PPAGED_CONNECTION pagedConnection;
    PTABLE_ENTRY entry;
    LUID logonId;
    SHORT uidIndex;
    USHORT reqAndXOffset;
    UCHAR nextCommand;
    PCHAR smbInformation;
    ULONG smbInformationLength;
    ULONG returnBufferLength = 0;
#ifdef INCLUDE_SMB_PERSISTENT
    ULONG persistentSessionId = 0;
#endif
    UNICODE_STRING nameString;
    UNICODE_STRING domainString;
    USHORT action = 0;
    USHORT byteCount;
    BOOLEAN locksHeld;
    BOOLEAN isUnicode, isExtendedSecurity;
    BOOLEAN smbSecuritySignatureRequired = FALSE;
    BOOLEAN previousSecuritySignatureState;

    PAGED_CODE();
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_SESSION_SETUP_AND_X;
    SrvWmiStartContext(WorkContext);

    //
    // If the connection has closed (timed out), abort.
    //

    connection = WorkContext->Connection;

    if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {

        IF_DEBUG(ERRORS) {
            SrvPrint0( "SrvSmbSessionSetupAndX: Connection closing\n" );
        }

        SrvEndSmbProcessing( WorkContext, SmbStatusNoResponse );
        SmbStatus = SmbStatusNoResponse;
        goto Cleanup;

    }

    IF_SMB_DEBUG(ADMIN1) {
        SrvPrint2( "Session setup request header at 0x%p, response header at 0x%p\n",
                    WorkContext->RequestHeader, WorkContext->ResponseHeader );
        SrvPrint2( "Session setup request parameters at 0x%p, response parameters at 0x%p\n",
                    WorkContext->RequestParameters,
                    WorkContext->ResponseParameters );
    }

    //
    // Initialize local variables for error cleanup.
    //

    nameString.Buffer = NULL;
    domainString.Buffer = NULL;
    session = NULL;
    locksHeld = FALSE;
    isExtendedSecurity = FALSE;

    //
    // Set up parameters.
    //

    request = (PREQ_SESSION_SETUP_ANDX)(WorkContext->RequestParameters);
    ntRequest = (PREQ_NT_SESSION_SETUP_ANDX)(WorkContext->RequestParameters);
    ntExtendedRequest = (PREQ_NT_EXTENDED_SESSION_SETUP_ANDX)(WorkContext->RequestParameters);
    response = (PRESP_SESSION_SETUP_ANDX)(WorkContext->ResponseParameters);

    connection = WorkContext->Connection;
    pagedConnection = connection->PagedConnection;

    previousSecuritySignatureState = connection->SmbSecuritySignatureActive;

    //
    // First verify that the SMB format is correct.
    //

    if ( (connection->SmbDialect <= SmbDialectNtLanMan &&
         (!((request->WordCount == 13) ||
            ((request->WordCount == 12) &&
             ((ntExtendedRequest->Capabilities & CAP_EXTENDED_SECURITY) != 0))))) ||
         (connection->SmbDialect > SmbDialectNtLanMan &&
                                          request->WordCount != 10 )   ||
         (connection->SmbDialect == SmbDialectIllegal ) ) {

        //
        // The SMB word count is invalid.
        //

        IF_DEBUG(SMB_ERRORS) {

            if ( connection->SmbDialect == SmbDialectIllegal ) {

                SrvPrint1("BlockingSessionSetupAndX: Client %z is using an "
                "illegal dialect.\n", (PCSTRING)&connection->OemClientMachineNameString );
            }
        }
        status = STATUS_INVALID_SMB;
        goto error_exit1;
    }

    //
    // Convert the client name to unicode
    //

    if ( pagedConnection->ClientMachineNameString.Length == 0 ) {

        UNICODE_STRING clientMachineName;
        clientMachineName.Buffer = pagedConnection->ClientMachineName;
        clientMachineName.MaximumLength =
                        (USHORT)(COMPUTER_NAME_LENGTH+1)*sizeof(WCHAR);

        (VOID)RtlOemStringToUnicodeString(
                        &clientMachineName,
                        &connection->OemClientMachineNameString,
                        FALSE
                        );

        //
        // Add the double backslashes to the length
        //

        pagedConnection->ClientMachineNameString.Length =
                        (USHORT)(clientMachineName.Length + 2*sizeof(WCHAR));

    }

    //
    // If this is LanMan 2.1 or better, the session setup response may
    // be longer than the request.  Allocate an extra SMB buffer.  The
    // buffer is freed after we have finished sending the SMB response.
    //
    // !!! Try to be smarter before grabbing the extra buffer.
    //

    if ( connection->SmbDialect <= SmbDialectDosLanMan21 &&
                                    !WorkContext->UsingExtraSmbBuffer) {

        status = SrvAllocateExtraSmbBuffer( WorkContext );
        if ( !NT_SUCCESS(status) ) {
            goto error_exit;
        }

        response = (PRESP_SESSION_SETUP_ANDX)(WorkContext->ResponseParameters);

        RtlCopyMemory(
            WorkContext->ResponseHeader,
            WorkContext->RequestHeader,
            sizeof( SMB_HEADER )
            );
    }

    //
    // Get the client capabilities
    //

    if ( connection->SmbDialect <= SmbDialectNtLanMan ) {

        if (ntRequest->WordCount == 13) {

            connection->ClientCapabilities =
                SmbGetUlong( &ntRequest->Capabilities ) &
                                        ( CAP_UNICODE |
                                          CAP_LARGE_FILES |
                                          CAP_NT_SMBS |
                                          CAP_NT_FIND |
                                          CAP_NT_STATUS |
                                          CAP_DYNAMIC_REAUTH |
                                          CAP_EXTENDED_SECURITY |
#ifdef INCLUDE_SMB_PERSISTENT
                                          CAP_PERSISTENT_HANDLES |
#endif
                                          CAP_LEVEL_II_OPLOCKS );

        } else {

            connection->ClientCapabilities =
                SmbGetUlong( &ntExtendedRequest->Capabilities ) &
                                        ( CAP_UNICODE |
                                          CAP_LARGE_FILES |
                                          CAP_NT_SMBS |
                                          CAP_NT_FIND |
                                          CAP_NT_STATUS |
                                          CAP_DYNAMIC_REAUTH |
                                          CAP_EXTENDED_SECURITY |
#ifdef INCLUDE_SMB_PERSISTENT
                                          CAP_PERSISTENT_HANDLES |
#endif
                                          CAP_LEVEL_II_OPLOCKS );

        }

        if ( connection->ClientCapabilities & CAP_NT_SMBS ) {
            connection->ClientCapabilities |= CAP_NT_FIND;
        }

#ifdef INCLUDE_SMB_PERSISTENT
        if ( connection->SmbDialect <= SmbDialectNtLanMan2 ) {

            //
            //  do we need to restore a previous lost connection?
            //

            if (connection->ClientCapabilities & CAP_PERSISTENT_HANDLES) {

                while ( persistentSessionId == 0 ) {
                    persistentSessionId = InterlockedIncrement( &SrvGlobalPersistentSessionId );
                }
            }
        } else {

            connection->ClientCapabilities &= ~CAP_PERSISTENT_HANDLES;
        }
#endif
    }

    //
    // See if the client is requesting the use of SMB security signatures
    //
    if( SrvSmbSecuritySignaturesEnabled == TRUE &&
        connection->Endpoint->IsConnectionless == FALSE &&
        connection->SmbSecuritySignatureActive == FALSE &&
        ( SrvSmbSecuritySignaturesRequired == TRUE ||
        (WorkContext->RequestHeader->Flags2 & SMB_FLAGS2_SMB_SECURITY_SIGNATURE)) ) {

        smbSecuritySignatureRequired = TRUE;

    } else {

        smbSecuritySignatureRequired = FALSE;

    }

    //
    // Figure out what kind of security to use, use it to validate the
    // session setup request, and construct the session if request checks out.
    //

    isExtendedSecurity = CLIENT_CAPABLE_OF( EXTENDED_SECURITY, connection );

    if( isExtendedSecurity ) {
        USHORT flags2;

        flags2 = SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 );
        isExtendedSecurity = ((flags2 & SMB_FLAGS2_EXTENDED_SECURITY) != 0);
    }

    isUnicode = SMB_IS_UNICODE( WorkContext );

    if ((connection->SmbDialect <= SmbDialectNtLanMan) && isExtendedSecurity) {
        //
        // We are validating a client using extended security.  This meansthat
        //  there may be multiple round-trips necessary for the SessionSetup&X
        //  SMB.  Each request and response carries a "security blob", which is
        //  fed into the security system.  The security system may generate
        //  a new blob which is transmitted to the other end.  This exchange
        //  may require an arbitrary number of round trips.
        //

        PUCHAR securityBuffer;
        ULONG securityBufferLength;

        PRESP_NT_EXTENDED_SESSION_SETUP_ANDX ntExtendedResponse =
                (PRESP_NT_EXTENDED_SESSION_SETUP_ANDX)( WorkContext->ResponseParameters );

        //
        // No AndX is permitted with extended security logons
        //
        if( request->AndXCommand != SMB_COM_NO_ANDX_COMMAND ) {

            IF_DEBUG(SMB_ERRORS) {
                KdPrint(( "No follow-on command allowed for extended SS&X\n" ));
            }

            status = STATUS_INVALID_SMB;

        } else {

            //
            // Clean up old dead connections from this client
            //
            if( SmbGetUshort( &ntRequest->VcNumber ) == 0 ) {
                SrvCloseConnectionsFromClient( connection, FALSE );
            }

            status = GetExtendedSecurityParameters(
                        WorkContext,
                        &securityBuffer,
                        &securityBufferLength,
                        &smbInformation,
                        &smbInformationLength );
        }

        if (NT_SUCCESS(status)) {

            USHORT Uid = SmbGetAlignedUshort(&WorkContext->RequestHeader->Uid);

            //
            // Let's see if we have a session with this UID already around.
            //
            if( Uid ) {

                session = SrvVerifyUid ( WorkContext, Uid );

                if( session != NULL ) {
                    //
                    // This is an attempt to either refresh the UID, or we are
                    // in the middle of an extended security negotiation.
                    //

                    ACQUIRE_LOCK( &connection->Lock );

                    if( session->LogonSequenceInProgress == FALSE ) {
                        //
                        // We are just beginning to start the refresh
                        // of the UID.
                        //
                        session->LogonSequenceInProgress = TRUE;
                        session->IsAdmin = FALSE;
                        session->IsSessionExpired = TRUE;
                        status = SrvFreeSecurityContexts( session );

                        //
                        // Reduce the session count, as it will be incremented if authentication succeeds
                        //
                        ExInterlockedAddUlong(
                              &SrvStatistics.CurrentNumberOfSessions,
                              -1,
                              &GLOBAL_SPIN_LOCK(Statistics)
                              );
                    }

                    RELEASE_LOCK( &connection->Lock );

                } else {
                    //
                    // We don't know anything about the UID which
                    //  the client gave to us.
                    //
                    status = STATUS_SMB_BAD_UID;
                }

            } else {
                //
                // This is the first SS&X for this user id
                //
                SrvAllocateSession( &session, NULL, NULL );
                if( session == NULL ) {
                    status = STATUS_INSUFF_SERVER_RESOURCES;
                }
            }

            if( session != NULL ) {
                //
                // Validate the security buffer sent from the client.  Note that
                //  this may change the UserHandle value, so we need to own
                //  the connection lock.
                //
                ACQUIRE_LOCK( &connection->Lock );

                //
                // Try to authenticate this user.  If we get NT_SUCCESS(), then
                //  the user is fully authenticated. If we get
                //  STATUS_NOT_MORE_PROCESSING_REQUIRED, then things are going well,
                //  but we need to do some more exchanges with the client before
                //  authentication is complete. Anything else is an error
                //

                returnBufferLength = WorkContext->ResponseBuffer->BufferLength -
                                     PTR_DIFF(ntExtendedResponse->Buffer,
                                              WorkContext->ResponseBuffer->Buffer);

                status = SrvValidateSecurityBuffer(
                            WorkContext->Connection,
                            &session->UserHandle,
                            session,
                            securityBuffer,
                            securityBufferLength,
                            smbSecuritySignatureRequired,
                            ntExtendedResponse->Buffer,
                            &returnBufferLength,
                            &session->LogOffTime,
                            session->NtUserSessionKey,
                            &session->LogonId,
                            &session->GuestLogon
                            );

                SecStatus = KSecValidateBuffer(
                                ntExtendedResponse->Buffer,
                                returnBufferLength );

                if ( !NT_SUCCESS( SecStatus ) ) {
#if DBG
                    KdPrint(( "SRV: invalid buffer from KsecDD: %p,%lx\n",
                        ntExtendedResponse->Buffer, returnBufferLength ));
#endif
                    SrvKsecValidErrors++;
                }

                RELEASE_LOCK( &connection->Lock );

                if( NT_SUCCESS(status) ) {
                    //
                    // This client is now fully authenticated!
                    //
                    session->IsAdmin = SrvIsAdmin( session->UserHandle );
                    session->IsNullSession = SrvIsNullSession( session->UserHandle );
                    session->KickOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF;
                    session->EncryptedLogon = TRUE;
                    session->LogonSequenceInProgress = FALSE;
                    session->IsSessionExpired = FALSE;

                    if( session->IsNullSession ) {
                        session->LogOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF;
                    }

#ifdef INCLUDE_SMB_PERSISTENT
                    session->PersistentId = persistentSessionId;
                    session->PersistentFileOffset = 0;
                    session->PersistentState = PersistentStateFreed;
#endif

#if SRVNTVERCHK
                    //
                    // If we are restricting the domains of our clients, grab the
                    //  domain string of this client and compare against the list.
                    //  If the client is in the list, set the flag that disallows
                    //  access to disk shares.
                    //
                    if( SrvInvalidDomainNames != NULL ) {
                        if( domainString.Buffer == NULL ) {
                            SrvGetUserAndDomainName( session, NULL, &domainString );
                        }

                        ACQUIRE_LOCK_SHARED( &SrvConfigurationLock );
                        if( SrvInvalidDomainNames != NULL && domainString.Buffer != NULL ) {
                            int i;
                            for( i = 0; SrvInvalidDomainNames[i]; i++ ) {
                                if( _wcsicmp( SrvInvalidDomainNames[i],
                                              domainString.Buffer
                                            ) == 0 ) {

                                    session->ClientBadDomain = TRUE;
                                    break;
                                }
                            }
                        }
                        RELEASE_LOCK( &SrvConfigurationLock );
                    }
#endif
                } else {
                    if( status == STATUS_MORE_PROCESSING_REQUIRED ) {
                        session->LogonSequenceInProgress = TRUE;
                    }
                }
            }
        }

    } else {

        PCHAR caseInsensitivePassword;
        CLONG caseInsensitivePasswordLength;
        PCHAR caseSensitivePassword;
        CLONG caseSensitivePasswordLength;

        status = GetNtSecurityParameters(
                    WorkContext,
                    &caseSensitivePassword,
                    &caseSensitivePasswordLength,
                    &caseInsensitivePassword,
                    &caseInsensitivePasswordLength,
                    &nameString,
                    &domainString,
                    &smbInformation,
                    &smbInformationLength );

        if (NT_SUCCESS(status)) {

            SrvAllocateSession( &session, &nameString, &domainString );

            if( session != NULL ) {

                status = SrvValidateUser(
                                    &session->UserHandle,
                                    session,
                                    WorkContext->Connection,
                                    &nameString,
                                    caseInsensitivePassword,
                                    caseInsensitivePasswordLength,
                                    caseSensitivePassword,
                                    caseSensitivePasswordLength,
                                    smbSecuritySignatureRequired,
                                    &action
                                    );
            } else {
                status = STATUS_INSUFF_SERVER_RESOURCES;
            }
        }
    }

    //
    // Done with the name strings - they were captured into the session
    // structure if needed.
    //

    if (!isUnicode || isExtendedSecurity) {

        if (nameString.Buffer != NULL) {
            RtlFreeUnicodeString( &nameString );
            nameString.Buffer = NULL;
        }

        if (domainString.Buffer != NULL) {
            RtlFreeUnicodeString( &domainString );
            domainString.Buffer = NULL;
        }
    }

    //
    // If a bad name/password combination was sent, return an error.
    //
    if ( !NT_SUCCESS(status) && status != STATUS_MORE_PROCESSING_REQUIRED ) {

        IF_DEBUG(ERRORS) {
            SrvPrint0( "BlockingSessionSetupAndX: Bad user/password combination.\n" );
        }

        SrvStatistics.LogonErrors++;

        goto error_exit;

    }

    if( previousSecuritySignatureState == FALSE &&
        connection->SmbSecuritySignatureActive == TRUE ) {

        //
        // We have 'turned on' SMB security signatures.  Make sure that the
        //  signature for the Session Setup & X is correct
        //

        //
        // The client's index was 0
        //
        WorkContext->SmbSecuritySignatureIndex = 0;

        //
        // Our response index is 1
        //
        WorkContext->ResponseSmbSecuritySignatureIndex = 1;

        //
        // And the next request should be index 2
        //
        connection->SmbSecuritySignatureIndex = 2;
    }

    //
    // If we have a new session, fill in the remaining required information.  We
    //  may be operating on an already existing session if we are in the middle
    //  of a multi-round-trip extended security blob exchange, or if we are
    //  renewing a session.
    //
    if ( WorkContext->Session == NULL ) {

         if( connection->SmbDialect <= SmbDialectDosLanMan21 ) {

            ACQUIRE_LOCK( &connection->Lock );

            if ( connection->ClientOSType.Buffer == NULL ) {

                ULONG length;
                PWCH infoBuffer;

                //
                // If the SMB buffer is ANSI, adjust the size of the buffer we
                // are allocating to Unicode size.
                //

                if ( isUnicode ) {
                    smbInformation = ALIGN_SMB_WSTR(smbInformation);
                }

                length = isUnicode ? smbInformationLength : smbInformationLength * sizeof( WCHAR );
                infoBuffer = ALLOCATE_NONPAGED_POOL(
                                length,
                                BlockTypeDataBuffer );

                if ( infoBuffer == NULL ) {
                    RELEASE_LOCK( &connection->Lock );
                    status = STATUS_INSUFF_SERVER_RESOURCES;
                    goto error_exit;
                }

                connection->ClientOSType.Buffer = (PWCH)infoBuffer;
                connection->ClientOSType.MaximumLength = (USHORT)length;

                //
                // Copy the client OS type to the new buffer.
                //

                length = SrvGetString(
                             &connection->ClientOSType,
                             smbInformation,
                             END_OF_REQUEST_SMB( WorkContext ),
                             isUnicode
                             );

                if ( length == (USHORT)-1) {
                    connection->ClientOSType.Buffer = NULL;
                    RELEASE_LOCK( &connection->Lock );
                    DEALLOCATE_NONPAGED_POOL( infoBuffer );
                    status =  STATUS_INVALID_SMB;
                    goto error_exit;
                }

                smbInformation += length + sizeof( WCHAR );

                connection->ClientLanManType.Buffer = (PWCH)(
                                (PCHAR)connection->ClientOSType.Buffer +
                                connection->ClientOSType.Length +
                                sizeof( WCHAR ) );

                connection->ClientLanManType.MaximumLength =
                                    connection->ClientOSType.MaximumLength -
                                    connection->ClientOSType.Length -
                                    sizeof( WCHAR );

                //
                // Copy the client LAN Manager type to the new buffer.
                //

                length = SrvGetString(
                             &connection->ClientLanManType,
                             smbInformation,
                             END_OF_REQUEST_SMB( WorkContext ),
                             isUnicode
                             );

                if ( length == (USHORT)-1) {
                    connection->ClientOSType.Buffer = NULL;
                    RELEASE_LOCK( &connection->Lock );
                    DEALLOCATE_NONPAGED_POOL( infoBuffer );
                    status = STATUS_INVALID_SMB;
                    goto error_exit;
                }

                //
                // If we have an NT5 or later client, grab the build number from the
                //   OS version string.
                //
                if( isExtendedSecurity &&
                    connection->ClientOSType.Length &&
                    connection->PagedConnection->ClientBuildNumber == 0 ) {

                    PWCHAR pdigit = connection->ClientOSType.Buffer;
                    PWCHAR epdigit = pdigit + connection->ClientOSType.Length/sizeof(WCHAR);
                    ULONG clientBuildNumber = 0;

                    //
                    // Scan the ClientOSType string to find the last number, and
                    //  convert to a ULONG.  It should be the build number
                    //
                    while( 1 ) {
                        //
                        // Scan the string until we find a number.
                        //
                        for( ; pdigit < epdigit; pdigit++ ) {
                            if( *pdigit >= L'0' && *pdigit <= L'9' ) {
                                break;
                            }
                        }

                        //
                        // If we've hit the end of the string, we are done
                        //
                        if( pdigit == epdigit ) {
                            break;
                        }

                        clientBuildNumber = 0;

                        //
                        // Convert the number to a ULONG, assuming it is the build number
                        //
                        while( pdigit < epdigit && *pdigit >= L'0' && *pdigit <= '9' ) {
                            clientBuildNumber *= 10;
                            clientBuildNumber += (*pdigit++ - L'0');
                        }
                    }

                    connection->PagedConnection->ClientBuildNumber = clientBuildNumber;

#if SRVNTVERCHK
                    if( SrvMinNT5Client > 0 ) {

                        BOOLEAN allowThisClient = FALSE;
                        DWORD i;

                        //
                        // See if we should allow this client, because it is a well-known
                        // IP address.  This is to allow the build lab to more slowly upgrade
                        // than the rest of us.
                        //
                        if( connection->ClientIPAddress != 0 &&
                            connection->Endpoint->IsConnectionless == FALSE ) {

                            for( i = 0; SrvAllowIPAddress[i]; i++ ) {
                                if( SrvAllowIPAddress[i] == connection->ClientIPAddress ) {
                                    allowThisClient = TRUE;
                                    break;
                                }
                            }
                        }

                        if( allowThisClient == FALSE &&
                            connection->PagedConnection->ClientBuildNumber < SrvMinNT5Client ) {
                                connection->PagedConnection->ClientTooOld = TRUE;
                        }
                    }
#endif
                }
            }
            RELEASE_LOCK( &connection->Lock );
        }

        //
        // If using uppercase pathnames, indicate in the session block.  DOS
        // always uses uppercase paths.
        //

        if ( (WorkContext->RequestHeader->Flags &
                  SMB_FLAGS_CANONICALIZED_PATHS) != 0 ||
                                IS_DOS_DIALECT( connection->SmbDialect ) ) {
            session->UsingUppercasePaths = TRUE;
        } else {
            session->UsingUppercasePaths = FALSE;
        }

        //
        // Enter data from request SMB into the session block.  If MaxMpx is 1
        // disable oplocks on this connection.
        //

        endpoint = connection->Endpoint;
        if ( endpoint->IsConnectionless ) {

            ULONG adapterNumber;

            //
            // Our session max buffer size is the smaller of the
            // client buffer size and the ipx transport
            // indicated max packet size.
            //

            adapterNumber =
                WorkContext->ClientAddress->DatagramOptions.LocalTarget.NicId;

            session->MaxBufferSize =
                    (USHORT)GetIpxMaxBufferSize(
                                        endpoint,
                                        adapterNumber,
                                        (ULONG)SmbGetUshort(&request->MaxBufferSize)
                                        );

        } else {

            session->MaxBufferSize = SmbGetUshort( &request->MaxBufferSize );
        }

        //
        // Make sure the MaxBufferSize is correctly sized
        //
        session->MaxBufferSize &= ~03;

        if( session->MaxBufferSize < SrvMinClientBufferSize ) {
            //
            // Client asked for a buffer size that is too small!
            //
            IF_DEBUG(ERRORS) {
                KdPrint(( "BlockingSessionSetupAndX: Bad Client Buffer Size: %u\n",
                    session->MaxBufferSize ));
            }
            status = STATUS_INVALID_SMB;
            goto error_exit;
        }

        session->MaxMpxCount = SmbGetUshort( &request->MaxMpxCount );

        if ( session->MaxMpxCount < 2 ) {
            connection->OplocksAlwaysDisabled = TRUE;
        }
    }

    //
    // If we have completely authenticated the client, and the client thinks
    // that it is the first user on this connection, get rid of other
    // connections (may be due to rebooting of client).  Also get rid of other
    // sessions on this connection with the same user name--this handles a
    // DOS "weirdness" where it sends multiple session setups if a tree connect
    // fails.
    //
    // *** If VcNumber is non-zero, we do nothing special.  This is the
    //     case even though the SrvMaxVcNumber configurable variable
    //     should always be equal to one.  If a second VC is established
    //     between machines, a new session must also be established.
    //     This duplicates the LM 2.0 server's behavior.
    //

    if( isExtendedSecurity == FALSE &&
        NT_SUCCESS( status ) &&
        SmbGetUshort( &request->VcNumber ) == 0 ) {

        UNICODE_STRING userName;

        SrvCloseConnectionsFromClient( connection, FALSE );

        //
        // If a client is smart enough to use extended security, then it
        //  is presumably smart enough to know what it wants to do with
        //  its sessions.  So don't just blow off sessions from this client.
        //
        SrvGetUserAndDomainName( session, &userName, NULL );

        if( userName.Buffer ) {
            SrvCloseSessionsOnConnection( connection, &userName );
            SrvReleaseUserAndDomainName( session, &userName, NULL );
        }
    }

    if( WorkContext->Session == NULL ) {

        //
        // Making a new session visible is a multiple-step operation.  It
        // must be inserted in the global ordered tree connect list and the
        // containing connection's session table, and the connection must be
        // referenced.  We need to make these operations appear atomic, so
        // that the session cannot be accessed elsewhere before we're done
        // setting it up.  In order to do this, we hold all necessary locks
        // the entire time we're doing the operations.  The first operation
        // is protected by the global ordered list lock
        // (SrvOrderedListLock), while the other operations are protected by
        // the per-connection lock.  We take out the ordered list lock
        // first, then the connection lock.  This ordering is required by
        // lock levels (see lock.h).
        //

        ASSERT( SrvSessionList.Lock == &SrvOrderedListLock );
        ACQUIRE_LOCK( SrvSessionList.Lock );

        ACQUIRE_LOCK( &connection->Lock );

        locksHeld = TRUE;

        //
        // Ready to try to find a UID for the session.  Check to see if the
        // connection is being closed, and if so, terminate this operation.
        //

        if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {

            IF_DEBUG(ERRORS) {
                SrvPrint0( "BlockingSessionSetupAndX: Connection closing\n" );
            }

            status = STATUS_INVALID_PARAMETER;
            goto error_exit;

        }

        //
        // If this client speaks a dialect above LM 1.0, find a UID that can
        // be used for this session.  Otherwise, just use location 0 of the
        // table because those clients will not send a UID in SMBs and they
        // can have only one session.
        //

        if ( connection->SmbDialect < SmbDialectLanMan10 ) {
            NTSTATUS TableStatus;

            if ( pagedConnection->SessionTable.FirstFreeEntry == -1
                 &&
                 SrvGrowTable(
                     &pagedConnection->SessionTable,
                     SrvInitialSessionTableSize,
                     SrvMaxSessionTableSize,
                     &TableStatus ) == FALSE
               ) {

                //
                // No free entries in the user table.  Reject the request.
                //

                IF_DEBUG(ERRORS) {
                    SrvPrint0( "BlockingSessionSetupAndX: No more UIDs available.\n" );
                }

                if( TableStatus == STATUS_INSUFF_SERVER_RESOURCES )
                {
                    // The table size is being exceeded, log an error
                    SrvLogTableFullError( SRV_TABLE_SESSION );
                    status = STATUS_SMB_TOO_MANY_UIDS;
                }
                else
                {
                    // Memory allocation error, report it
                    status = TableStatus;
                }

                goto error_exit;

            }

            uidIndex = pagedConnection->SessionTable.FirstFreeEntry;

        } else {          // if ( dialect < SmbDialectLanMan10 )

            //
            // If this client already has a session at this server, abort.
            // The session should have been closed by the call to
            // SrvCloseSessionsOnConnection above.  (We could try to work
            // around the existence of the session by closing it, but that
            // would involve releasing the locks, closing the session, and
            // retrying.  This case shouldn't happen.)
            //

            if ( pagedConnection->SessionTable.Table[0].Owner != NULL ) {

                IF_DEBUG(ERRORS) {
                    SrvPrint0( "BlockingSessionSetupAndX: Core client already has session.\n" );
                }

                status = STATUS_SMB_TOO_MANY_UIDS;
                goto error_exit;
            }

            //
            // Use location 0 of the session table.
            //

            IF_SMB_DEBUG(ADMIN2) {
                SrvPrint0( "Client LM 1.0 or before--using location 0 of session table.\n" );
            }

            uidIndex = 0;

        }

        //
        // Remove the UID slot from the free list and set its owner and
        // sequence number.  Create a UID for the session.  Increment count
        // of sessions.
        //

        entry = &pagedConnection->SessionTable.Table[uidIndex];

        pagedConnection->SessionTable.FirstFreeEntry = entry->NextFreeEntry;
        DEBUG entry->NextFreeEntry = -2;
        if ( pagedConnection->SessionTable.LastFreeEntry == uidIndex ) {
            pagedConnection->SessionTable.LastFreeEntry = -1;
        }

        INCREMENT_UID_SEQUENCE( entry->SequenceNumber );
        if ( uidIndex == 0 && entry->SequenceNumber == 0 ) {
            INCREMENT_UID_SEQUENCE( entry->SequenceNumber );
        }
        session->Uid = MAKE_UID( uidIndex, entry->SequenceNumber );

        entry->Owner = session;

        pagedConnection->CurrentNumberOfSessions++;

        IF_SMB_DEBUG(ADMIN1) {
            SrvPrint2( "Found UID.  Index = 0x%lx, sequence = 0x%lx\n",
                        UID_INDEX( session->Uid ),
                        UID_SEQUENCE( session->Uid ) );
        }

        //
        // Insert the session on the global session list.
        //

        SrvInsertEntryOrderedList( &SrvSessionList, session );

        //
        // Reference the connection block to account for the new session.
        //

        SrvReferenceConnection( connection );
        session->Connection = connection;

        RELEASE_LOCK( &connection->Lock );
        RELEASE_LOCK( SrvSessionList.Lock );

        //
        // Session successfully created.  Insert the session in the global
        // list of active sessions.  Remember its address in the work
        // context block.
        //
        // *** Note that the reference count on the session block is
        //     initially set to 2, to allow for the active status on the
        //     block and the pointer that we're maintaining.  In other
        //     words, this is a referenced pointer, and the pointer must be
        //     dereferenced when processing of this SMB is complete.
        //

        WorkContext->Session = session;
    }

    //
    // Build response SMB, making sure to save request fields first in
    // case the response overwrites the request.  Save the
    // newly-assigned UID in both the request SMB and the response SMB
    // so that subsequent command processors and the client,
    // respectively, can see it.
    //

    nextCommand = request->AndXCommand;

    reqAndXOffset = SmbGetUshort( &request->AndXOffset );

    SmbPutAlignedUshort( &WorkContext->RequestHeader->Uid, session->Uid );
    SmbPutAlignedUshort( &WorkContext->ResponseHeader->Uid, session->Uid );

    if (isExtendedSecurity) {

        BuildExtendedSessionSetupAndXResponse(
            WorkContext,
            returnBufferLength,
            status,
            nextCommand,
            isUnicode);

    } else {

        BuildSessionSetupAndXResponse(
            WorkContext,
            nextCommand,
            action,
            isUnicode);

    }

    WorkContext->ResponseParameters = (PCHAR)WorkContext->ResponseHeader +
                                        SmbGetUshort( &response->AndXOffset );

    //
    // Test for legal followon command.
    //

    switch ( nextCommand ) {
    case SMB_COM_NO_ANDX_COMMAND:
        break;

    case SMB_COM_TREE_CONNECT_ANDX:
    case SMB_COM_OPEN:
    case SMB_COM_OPEN_ANDX:
    case SMB_COM_CREATE:
    case SMB_COM_CREATE_NEW:
    case SMB_COM_CREATE_DIRECTORY:
    case SMB_COM_DELETE:
    case SMB_COM_DELETE_DIRECTORY:
    case SMB_COM_FIND:
    case SMB_COM_FIND_UNIQUE:
    case SMB_COM_COPY:
    case SMB_COM_RENAME:
    case SMB_COM_NT_RENAME:
    case SMB_COM_CHECK_DIRECTORY:
    case SMB_COM_QUERY_INFORMATION:
    case SMB_COM_SET_INFORMATION:
    case SMB_COM_QUERY_INFORMATION_SRV:
    case SMB_COM_OPEN_PRINT_FILE:
    case SMB_COM_GET_PRINT_QUEUE:
    case SMB_COM_TRANSACTION:
        //
        // Make sure the AndX command is still within the received SMB
        //
        if( (PCHAR)WorkContext->RequestHeader + reqAndXOffset <=
            END_OF_REQUEST_SMB( WorkContext ) ) {
            break;
        }

        /* Falls Through */

    default:                            // Illegal followon command

        IF_DEBUG(SMB_ERRORS) {
            SrvPrint1( "BlockingSessionSetupAndX: Illegal followon command: "
                        "0x%lx\n", nextCommand );
        }

        status = STATUS_INVALID_SMB;
        goto error_exit1;
    }

    //
    // If there is an AndX command, set up to process it.  Otherwise,
    // indicate completion to the caller.
    //

    if ( nextCommand != SMB_COM_NO_ANDX_COMMAND ) {

        WorkContext->NextCommand = nextCommand;

        WorkContext->RequestParameters = (PCHAR)WorkContext->RequestHeader +
                                            reqAndXOffset;

        SrvProcessSmb( WorkContext );
        SmbStatus = SmbStatusNoResponse;
        goto Cleanup;

    }

    IF_DEBUG(TRACE2) SrvPrint0( "BlockingSessionSetupAndX complete.\n" );
    goto normal_exit;

error_exit:

    if ( locksHeld ) {
        RELEASE_LOCK( &connection->Lock );
        RELEASE_LOCK( SrvSessionList.Lock );
    }

    if ( session != NULL ) {
        if( WorkContext->Session ) {
            //
            // A re-validation of the session failed, or the extended exchange
            //  of security blobs failed.  Get rid of this user.
            //

            SrvCloseSession( session );

            SrvStatistics.SessionsLoggedOff++;

            //
            // Dereference the session, since it's no longer valid
            //
            SrvDereferenceSession( session );

            WorkContext->Session = NULL;

        } else {

            SrvFreeSession( session );
        }
    }

    if ( !isUnicode ) {
        if ( domainString.Buffer != NULL ) {
            RtlFreeUnicodeString( &domainString );
        }
        if ( nameString.Buffer != NULL ) {
            RtlFreeUnicodeString( &nameString );
        }
    }

error_exit1:

    SrvSetSmbError( WorkContext, status );

normal_exit:
    SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
    SmbStatus = SmbStatusSendResponse;

Cleanup:
    SrvWmiEndContext(WorkContext);
    return;

} // BlockingSessionSetupAndX


NTSTATUS
GetExtendedSecurityParameters(
    IN PWORK_CONTEXT WorkContext,
    OUT PUCHAR *SecurityBuffer,
    OUT PULONG SecurityBufferLength,
    OUT PCHAR  *RestOfDataBuffer,
    OUT PULONG RestOfDataLength)

/*++

Routine Description:

    Extracts the extensible security parameters from an extended session
    setup and X SMB.

Arguments:

    WorkContext - Context of the SMB

    SecurityBuffer - On return, points to the security buffer inside the
        extended session setup and X SMB

    SecurityBufferLength - On return, size in bytes of SecurityBuffer.

    RestOfDataBuffer - On return, points just past the security buffer

    ResetOfDataLength - On return, size in bytes of *RestOfDataBuffer

Return Value:

    STATUS_SUCCESS - This routine merely returns pointers within an SMB

--*/


{
    NTSTATUS status;
    PCONNECTION connection;
    PREQ_NT_EXTENDED_SESSION_SETUP_ANDX ntExtendedRequest;
    ULONG maxlength;

    connection = WorkContext->Connection;
    ASSERT( connection->SmbDialect <= SmbDialectNtLanMan );

    ntExtendedRequest = (PREQ_NT_EXTENDED_SESSION_SETUP_ANDX)
                            (WorkContext->RequestParameters);

    maxlength = (ULONG)(WorkContext->RequestBuffer->DataLength + sizeof( USHORT ) -
                        ((ULONG_PTR)ntExtendedRequest->Buffer -
                         (ULONG_PTR)WorkContext->RequestBuffer->Buffer));


    //
    // Get the extended security buffer
    //

    *SecurityBuffer = (PUCHAR) ntExtendedRequest->Buffer;
    *SecurityBufferLength = ntExtendedRequest->SecurityBlobLength;

    *RestOfDataBuffer = ntExtendedRequest->Buffer +
                            ntExtendedRequest->SecurityBlobLength;

    *RestOfDataLength = (USHORT)( (PUCHAR)ntExtendedRequest->Buffer +
                                  sizeof(USHORT) +
                                  SmbGetUshort( &ntExtendedRequest->ByteCount) -
                                  (*RestOfDataBuffer)
                                );

    if( *SecurityBufferLength > maxlength ||
        *RestOfDataLength > maxlength - *SecurityBufferLength ) {

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "GetExtendedSecurityParameters: Invalid security buffer\n" ));
        }

        return STATUS_INVALID_SMB;
    }

    return( STATUS_SUCCESS );
}


NTSTATUS
GetNtSecurityParameters(
    IN PWORK_CONTEXT WorkContext,
    OUT PCHAR *CaseSensitivePassword,
    OUT PULONG CaseSensitivePasswordLength,
    OUT PCHAR *CaseInsensitivePassword,
    OUT PULONG CaseInsensitivePasswordLength,
    OUT PUNICODE_STRING UserName,
    OUT PUNICODE_STRING DomainName,
    OUT PCHAR *RestOfDataBuffer,
    OUT PULONG RestOfDataLength)
{

    NTSTATUS status = STATUS_SUCCESS;
    PCONNECTION connection;
    PREQ_NT_SESSION_SETUP_ANDX ntRequest;
    PREQ_SESSION_SETUP_ANDX request;
    PSZ userName;
    USHORT nameLength;
    BOOLEAN isUnicode;

    connection = WorkContext->Connection;

    ntRequest = (PREQ_NT_SESSION_SETUP_ANDX)(WorkContext->RequestParameters);
    request = (PREQ_SESSION_SETUP_ANDX)(WorkContext->RequestParameters);

    //
    // Get the account name, and additional information from the SMB buffer.
    //

    if ( connection->SmbDialect <= SmbDialectNtLanMan) {

        //
        // The NT-NT SMB protocol passes both case sensitive (Unicode,
        // mixed case) and case insensitive (ANSI, uppercased) passwords.
        // Get pointers to them to pass to SrvValidateUser.
        //

        *CaseInsensitivePasswordLength =
            (CLONG)SmbGetUshort(&ntRequest->CaseInsensitivePasswordLength);
        *CaseInsensitivePassword = (PCHAR)(ntRequest->Buffer);
        *CaseSensitivePasswordLength =
            (CLONG)SmbGetUshort( &ntRequest->CaseSensitivePasswordLength );
        *CaseSensitivePassword =
           *CaseInsensitivePassword + *CaseInsensitivePasswordLength;
        userName = (PSZ)(*CaseSensitivePassword +
                                            *CaseSensitivePasswordLength);

    } else {

        //
        // Downlevel clients do not pass the case sensitive password;
        // just get the case insensitive password and use NULL as the
        // case sensitive password.  LSA will do the right thing with
        // it.
        //

        *CaseInsensitivePasswordLength =
            (CLONG)SmbGetUshort( &request->PasswordLength );
        *CaseInsensitivePassword = (PCHAR)request->Buffer;
        *CaseSensitivePasswordLength = 0;
        *CaseSensitivePassword = NULL;
        userName = (PSZ)(request->Buffer + *CaseInsensitivePasswordLength);
    }

    if( (*CaseInsensitivePassword) != NULL &&
        (*CaseInsensitivePassword) + (*CaseInsensitivePasswordLength) >
        END_OF_REQUEST_SMB( WorkContext ) ) {

        status = STATUS_INVALID_SMB;
        goto error_exit;
    }

    if( (*CaseSensitivePassword) != NULL &&
        (*CaseSensitivePassword) + (*CaseSensitivePasswordLength) >
        END_OF_REQUEST_SMB( WorkContext ) ) {

        status = STATUS_INVALID_SMB;
        goto error_exit;
    }

    isUnicode = SMB_IS_UNICODE( WorkContext );
    if ( isUnicode ) {
        userName = ALIGN_SMB_WSTR( userName );
    }

    nameLength = SrvGetStringLength(
                     userName,
                     END_OF_REQUEST_SMB( WorkContext ),
                     isUnicode,
                     FALSE      // don't include null terminator
                     );

    if ( nameLength == (USHORT)-1 ) {
        status = STATUS_INVALID_SMB;
        goto error_exit;
    }

    status = SrvMakeUnicodeString(
                 isUnicode,
                 UserName,
                 userName,
                 &nameLength );

    if ( !NT_SUCCESS( status ) ) {
        goto error_exit;
    }

    //
    // If client information strings exists, extract the information
    // from the SMB buffer.
    //

    if ( connection->SmbDialect <= SmbDialectDosLanMan21) {

        PCHAR smbInformation;
        USHORT length;
        PWCH infoBuffer;

        smbInformation = userName + nameLength +
                                    ( isUnicode ? sizeof( WCHAR ) : 1 );

        //
        // Now copy the strings to the allocated buffer.
        //

        if ( isUnicode ) {
            smbInformation = ALIGN_SMB_WSTR( smbInformation );
        }

        length = SrvGetStringLength(
                     smbInformation,
                     END_OF_REQUEST_SMB( WorkContext ),
                     isUnicode,
                     FALSE      // don't include null terminator
                     );

        if ( length == (USHORT)-1) {
            status = STATUS_INVALID_SMB;
            goto error_exit;
        }

        //
        // DOS clients send an empty domain name if they don't know
        // their domain name (e.g., during logon).  OS/2 clients send
        // a name of "?".  This confuses the LSA.  Convert such a name
        // to an empty name.
        //

        if ( isUnicode ) {
            if ( (length == sizeof(WCHAR)) &&
                 (*(PWCH)smbInformation == '?') ) {
                length = 0;
            }
        } else {
            if ( (length == 1) && (*smbInformation == '?') ) {
                length = 0;
            }
        }

        status = SrvMakeUnicodeString(
                     isUnicode,
                     DomainName,
                     smbInformation,
                     &length
                     );

        if ( !NT_SUCCESS( status ) ) {
            goto error_exit;
        }

        smbInformation += length + ( isUnicode ? sizeof(WCHAR) : 1 );

        *RestOfDataBuffer = smbInformation;

        if (connection->SmbDialect <= SmbDialectNtLanMan) {

            *RestOfDataLength = (USHORT) ( (PUCHAR)&ntRequest->ByteCount +
                                            sizeof(USHORT) +
                                            SmbGetUshort(&ntRequest->ByteCount) -
                                            smbInformation
                                         );
        } else {

            PREQ_SESSION_SETUP_ANDX request;

            request = (PREQ_SESSION_SETUP_ANDX)(WorkContext->RequestParameters);

            *RestOfDataLength = (USHORT) ( (PUCHAR)&request->ByteCount +
                                            sizeof(USHORT) +
                                            SmbGetUshort(&request->ByteCount) -
                                            smbInformation
                                         );

        }

    } else {

        DomainName->Length = 0;

        *RestOfDataBuffer = NULL;

        *RestOfDataLength = 0;

    }

error_exit:

    return( status );

}


VOID
BuildExtendedSessionSetupAndXResponse(
    IN PWORK_CONTEXT WorkContext,
    IN ULONG ReturnBufferLength,
    IN NTSTATUS Status,
    IN UCHAR NextCommand,
    IN BOOLEAN IsUnicode)
{
    PRESP_NT_EXTENDED_SESSION_SETUP_ANDX ntExtendedResponse;
    PCHAR buffer;
    USHORT byteCount;

    ntExtendedResponse = (PRESP_NT_EXTENDED_SESSION_SETUP_ANDX)
                            (WorkContext->ResponseParameters);

    ntExtendedResponse->WordCount = 4;
    ntExtendedResponse->AndXCommand = NextCommand;
    ntExtendedResponse->AndXReserved = 0;

    if( WorkContext->Session && WorkContext->Session->GuestLogon ) {
        SmbPutUshort( &ntExtendedResponse->Action, SMB_SETUP_GUEST );
    } else {
        SmbPutUshort( &ntExtendedResponse->Action, 0 );
    }

    SmbPutUshort( &ntExtendedResponse->SecurityBlobLength,(USHORT)ReturnBufferLength );

    buffer = ntExtendedResponse->Buffer + ReturnBufferLength;

    if (IsUnicode)
        buffer = ALIGN_SMB_WSTR( buffer );

    InsertNativeOSAndType( IsUnicode, buffer, &byteCount );

    byteCount += (USHORT)ReturnBufferLength;

    SmbPutUshort( &ntExtendedResponse->ByteCount, byteCount );

    SmbPutUshort( &ntExtendedResponse->AndXOffset, GET_ANDX_OFFSET(
                                             WorkContext->ResponseHeader,
                                             WorkContext->ResponseParameters,
                                             RESP_NT_EXTENDED_SESSION_SETUP_ANDX,
                                             byteCount
                                             ) );

    //
    // Make sure we return the error status here, as the client uses it to
    //  determine if extra round trips are necessary
    //
    SrvSetSmbError2 ( WorkContext, Status, TRUE );
}


VOID
BuildSessionSetupAndXResponse(
    IN PWORK_CONTEXT WorkContext,
    IN UCHAR NextCommand,
    IN USHORT Action,
    IN BOOLEAN IsUnicode)
{

    PRESP_SESSION_SETUP_ANDX response;
    PCONNECTION connection;
    PENDPOINT endpoint;
    PCHAR buffer;
    USHORT byteCount;

    response = (PRESP_SESSION_SETUP_ANDX) (WorkContext->ResponseParameters);

    connection = WorkContext->Connection;

    endpoint = connection->Endpoint;

    response->WordCount = 3;
    response->AndXCommand = NextCommand;
    response->AndXReserved = 0;

    if (connection->SmbDialect <= SmbDialectDosLanMan21) {

        buffer = response->Buffer;

        if (IsUnicode)
            buffer = ALIGN_SMB_WSTR( buffer );

        InsertNativeOSAndType( IsUnicode, buffer, &byteCount );

        buffer = buffer + byteCount;

        if (connection->SmbDialect <= SmbDialectNtLanMan) {

            USHORT stringLength;

            if ( IsUnicode ) {

                buffer = ALIGN_SMB_WSTR( buffer );

                stringLength = endpoint->DomainName.Length + sizeof(UNICODE_NULL);

                RtlCopyMemory(
                    buffer,
                    endpoint->DomainName.Buffer,
                    stringLength
                    );

                byteCount += (USHORT)stringLength;

            } else {

                stringLength = endpoint->OemDomainName.Length + sizeof(CHAR);

                RtlCopyMemory(
                    (PVOID) buffer,
                    endpoint->OemDomainName.Buffer,
                    stringLength
                    );

                byteCount += (USHORT)stringLength;

            }

        }

    } else {

        byteCount = 0;
    }

    SmbPutUshort( &response->ByteCount, byteCount );

    //
    // Normally, turning on bit 0 of Action indicates that the user was
    // logged on as GUEST.  However, NT does not have automatic guest
    // logon--a user ID and password are required for every single logon
    // (though the password may have null length).  Therefore, the
    // server need not concern itself with what kind of account the
    // client gets.
    //
    // Bit 1 tells the client that the user was logged on
    // using the lm session key instead of the user session key.
    //

    SmbPutUshort( &response->Action, Action );

    SmbPutUshort( &response->AndXOffset, GET_ANDX_OFFSET(
                                             WorkContext->ResponseHeader,
                                             WorkContext->ResponseParameters,
                                             RESP_SESSION_SETUP_ANDX,
                                             byteCount
                                             ) );


}


VOID
InsertNativeOSAndType(
    IN BOOLEAN IsUnicode,
    OUT PCHAR Buffer,
    OUT PUSHORT ByteCount)
{
    USHORT stringLength;

    if ( IsUnicode ) {

        stringLength = (USHORT)(SrvNativeOS.Length + sizeof(UNICODE_NULL));

        RtlCopyMemory(
            Buffer,
            SrvNativeOS.Buffer,
            stringLength
            );

        *ByteCount = stringLength;

        stringLength = SrvNativeLanMan.Length + sizeof(UNICODE_NULL);

        RtlCopyMemory(
            (PCHAR)Buffer + *ByteCount,
            SrvNativeLanMan.Buffer,
            stringLength
            );

        *ByteCount += (USHORT)stringLength;

    } else {

        stringLength = SrvOemNativeOS.Length + sizeof(CHAR);

        RtlCopyMemory(
            Buffer,
            SrvOemNativeOS.Buffer,
            stringLength
            );

        *ByteCount = stringLength;

        stringLength = SrvOemNativeLanMan.Length + sizeof(CHAR);

        RtlCopyMemory(
            (PCHAR)Buffer + *ByteCount,
            SrvOemNativeLanMan.Buffer,
            stringLength
            );

        *ByteCount += (USHORT)stringLength;

    }

}



SMB_PROCESSOR_RETURN_TYPE
SrvSmbLogoffAndX (
    SMB_PROCESSOR_PARAMETERS
    )

/*++

Routine Description:

    Processes a Logoff and X SMB.

Arguments:

    SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description
        of the parameters to SMB processor routines.

Return Value:

    SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h

--*/

{
    PREQ_LOGOFF_ANDX request;
    PRESP_LOGOFF_ANDX response;

    PSESSION session;
    USHORT reqAndXOffset;
    UCHAR nextCommand;

    NTSTATUS   status    = STATUS_SUCCESS;
    SMB_STATUS SmbStatus = SmbStatusInProgress;

    PAGED_CODE( );
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_LOGOFF_AND_X;
    SrvWmiStartContext(WorkContext);

    IF_SMB_DEBUG(ADMIN1) {
        SrvPrint2( "Logoff request header at 0x%p, response header at 0x%p\n",
                    WorkContext->RequestHeader, WorkContext->ResponseHeader );
        SrvPrint2( "Logoff request parameters at 0x%p, response parameters at 0x%p\n",
                    WorkContext->RequestParameters,
                    WorkContext->ResponseParameters );
    }

    //
    // Set up parameters.
    //

    request = (PREQ_LOGOFF_ANDX)(WorkContext->RequestParameters);
    response = (PRESP_LOGOFF_ANDX)(WorkContext->ResponseParameters);

    //
    // If a session block has not already been assigned to the current
    // work context, verify the UID.  If verified, the address of the
    // session block corresponding to this user is stored in the
    // WorkContext block and the session block is referenced.
    //

    session = SrvVerifyUid(
                  WorkContext,
                  SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )
                  );

    if ( session == NULL ) {

        IF_DEBUG(SMB_ERRORS) {
            SrvPrint1( "SrvSmbLogoffAndX: Invalid UID: 0x%lx\n",
                SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ) );
        }

        SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID );
        status    = STATUS_SMB_BAD_UID;
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // If we need to visit the license server, get over to a blocking
    // thread to ensure that we don't consume the nonblocking threads
    //
    if( WorkContext->UsingBlockingThread == 0 &&
        session->IsLSNotified == TRUE ) {
            //
            // Insert the work item at the tail of the blocking work queue
            //
            SrvInsertWorkQueueTail(
                &SrvBlockingWorkQueue,
                (PQUEUEABLE_BLOCK_HEADER)WorkContext
            );

            SmbStatus = SmbStatusInProgress;
            goto Cleanup;
    }

    //
    // Do the actual logoff.
    //

    SrvCloseSession( session );

    SrvStatistics.SessionsLoggedOff++;

    //
    // Dereference the session, since it's no longer valid, but we may
    // end up processing a chained command.  Clear the session pointer
    // in the work context block to indicate that we've done this.
    //

    SrvDereferenceSession( session );

    WorkContext->Session = NULL;

    //
    // Build the response SMB, making sure to save request fields first
    // in case the response overwrites the request.
    //

    reqAndXOffset = SmbGetUshort( &request->AndXOffset );
    nextCommand = request->AndXCommand;

    response->WordCount = 2;
    response->AndXCommand = request->AndXCommand;
    response->AndXReserved = 0;
    SmbPutUshort( &response->AndXOffset, GET_ANDX_OFFSET(
                                            WorkContext->ResponseHeader,
                                            WorkContext->ResponseParameters,
                                            RESP_LOGOFF_ANDX,
                                            0
                                            ) );
    SmbPutUshort( &response->ByteCount, 0 );

    WorkContext->ResponseParameters = (PCHAR)WorkContext->ResponseHeader +
                                        SmbGetUshort( &response->AndXOffset );

    //
    // Test for legal followon command.
    //

    switch ( nextCommand ) {

    case SMB_COM_NO_ANDX_COMMAND:
        break;

    case SMB_COM_SESSION_SETUP_ANDX:
        //
        // Make sure the AndX command is still within the received SMB
        //
        if( (PCHAR)WorkContext->RequestHeader + reqAndXOffset <=
            END_OF_REQUEST_SMB( WorkContext ) ) {
            break;
        }

        /* Falls Through */

    default:

        IF_DEBUG(SMB_ERRORS) {
            SrvPrint1( "SrvSmbLogoffAndX: Illegal followon command: 0x%lx\n",
                        nextCommand );
        }

        SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
        status    = STATUS_INVALID_SMB;
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // If there is an AndX command, set up to process it.  Otherwise,
    // indicate completion to the caller.
    //

    if ( nextCommand != SMB_COM_NO_ANDX_COMMAND ) {

        WorkContext->NextCommand = nextCommand;

        WorkContext->RequestParameters = (PCHAR)WorkContext->RequestHeader +
                                            reqAndXOffset;

        SmbStatus = SmbStatusMoreCommands;
        goto Cleanup;
    }
    SmbStatus = SmbStatusSendResponse;
    IF_DEBUG(TRACE2) SrvPrint0( "SrvSmbLogoffAndX complete.\n" );

Cleanup:
    SrvWmiEndContext(WorkContext);
    return SmbStatus;

} // SrvSmbLogoffAndX


STATIC
VOID
GetEncryptionKey (
    OUT CHAR EncryptionKey[MSV1_0_CHALLENGE_LENGTH]
    )

/*++

Routine Description:

    Creates an encryption key to use as a challenge for a logon.

    *** Although the MSV1_0 authentication package has a function that
        returns an encryption key, we do not use that function in order
        to avoid a trip through LPC and into LSA.

Arguments:

    EncryptionKey - a pointer to a buffer which receives the encryption
        key.

Return Value:

    NTSTATUS - result of operation.

--*/

{
    union {
        LARGE_INTEGER time;
        UCHAR bytes[8];
    } u;
    ULONG seed;
    ULONG challenge[2];
    ULONG result3;

    //
    // Create a pseudo-random 8-byte number by munging the system time
    // for use as a random number seed.
    //
    // Start by getting the system time.
    //

    ASSERT( MSV1_0_CHALLENGE_LENGTH == 2 * sizeof(ULONG) );

    KeQuerySystemTime( &u.time );

    //
    // To ensure that we don't use the same system time twice, add in the
    // count of the number of times this routine has been called.  Then
    // increment the counter.
    //
    // *** Since we don't use the low byte of the system time (it doesn't
    //     take on enough different values, because of the timer
    //     resolution), we increment the counter by 0x100.
    //
    // *** We don't interlock the counter because we don't really care
    //     if it's not 100% accurate.
    //

    u.time.LowPart += EncryptionKeyCount;

    EncryptionKeyCount += 0x100;

    //
    // Now use parts of the system time as a seed for the random
    // number generator.
    //
    // *** Because the middle two bytes of the low part of the system
    //     time change most rapidly, we use those in forming the seed.
    //

    seed = ((u.bytes[1] + 1) <<  0) |
           ((u.bytes[2] + 0) <<  8) |
           ((u.bytes[2] - 1) << 16) |
           ((u.bytes[1] + 0) << 24);

    //
    // Now get two random numbers.  RtlRandom does not return negative
    // numbers, so we pseudo-randomly negate them.
    //

    challenge[0] = RtlRandom( &seed );
    challenge[1] = RtlRandom( &seed );
    result3 = RtlRandom( &seed );

    if ( (result3 & 0x1) != 0 ) {
        challenge[0] |= 0x80000000;
    }
    if ( (result3 & 0x2) != 0 ) {
        challenge[1] |= 0x80000000;
    }

    //
    // Return the challenge.
    //

    RtlCopyMemory( EncryptionKey, challenge, MSV1_0_CHALLENGE_LENGTH );

} // GetEncryptionKey