/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    access.c

Abstract:

    This module contains routines for interfacing to the security
    system in NT.

--*/

#include "precomp.h"
#include "access.tmh"
#pragma hdrstop
#include <ntlmsp.h>

#define BugCheckFileId SRV_FILE_ACCESS

#if DBG
ULONG SrvLogonCount = 0;
ULONG SrvNullLogonCount = 0;
#endif


#define ROUND_UP_COUNT(Count,Pow2) \
        ( ((Count)+(Pow2)-1) & (~((Pow2)-1)) )

typedef struct _LOGON_INFO {
    PWCH WorkstationName;
    ULONG WorkstationNameLength;
    PWCH DomainName;
    ULONG DomainNameLength;
    PWCH UserName;
    ULONG UserNameLength;
    PCHAR CaseInsensitivePassword;
    ULONG CaseInsensitivePasswordLength;
    PCHAR CaseSensitivePassword;
    ULONG CaseSensitivePasswordLength;
    CHAR EncryptionKey[MSV1_0_CHALLENGE_LENGTH];
    LUID LogonId;
    CtxtHandle  Token;
    USHORT Uid;
    BOOLEAN     HaveHandle;
    LARGE_INTEGER KickOffTime;
    LARGE_INTEGER LogOffTime;
    USHORT Action;
    BOOLEAN GuestLogon;
    BOOLEAN EncryptedLogon;
    BOOLEAN NtSmbs;
    BOOLEAN IsNullSession;
    BOOLEAN IsAdmin;
    CHAR NtUserSessionKey[MSV1_0_USER_SESSION_KEY_LENGTH];
    CHAR LanManSessionKey[MSV1_0_LANMAN_SESSION_KEY_LENGTH];
} LOGON_INFO, *PLOGON_INFO;

NTSTATUS
DoUserLogon (
    IN PLOGON_INFO LogonInfo,
    IN BOOLEAN SecuritySignatureDesired,
    IN PCONNECTION Connection OPTIONAL,
    IN PSESSION Session
    );

NTSTATUS
AcquireExtensibleSecurityCredentials (
    VOID
    );

NTSTATUS
SrvGetLogonId(
    PCtxtHandle  Handle,
    PLUID LogonId
    );

ULONG SrvHaveCreds = 0;

//
// 24 hours short of never, in case any utc/local conversions are done
//
#define SRV_NEVER_TIME  (0x7FFFFFFFFFFFFFFFI64 - 0xC92A69C000I64)

#define HAVENTLM        1
#define HAVEEXTENDED    2

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvValidateUser )
#pragma alloc_text( PAGE, DoUserLogon )
#pragma alloc_text( PAGE, SrvIsAdmin )
#pragma alloc_text( PAGE, SrvFreeSecurityContexts )
#pragma alloc_text( PAGE, AcquireLMCredentials )
#pragma alloc_text( PAGE, AcquireExtensibleSecurityCredentials )
#pragma alloc_text( PAGE, SrvValidateSecurityBuffer )
#pragma alloc_text( PAGE, SrvGetUserAndDomainName )
#pragma alloc_text( PAGE, SrvReleaseUserAndDomainName )
#pragma alloc_text( PAGE, SrvGetExtensibleSecurityNegotiateBuffer )
#pragma alloc_text( PAGE, SrvGetLogonId )
#pragma alloc_text( PAGE, SrvInitializeSmbSecuritySignature )
#pragma alloc_text( PAGE, SrvAddSecurityCredentials )
#endif


NTSTATUS
SrvValidateUser (
    OUT CtxtHandle *Token,
    IN PSESSION Session OPTIONAL,
    IN PCONNECTION Connection OPTIONAL,
    IN PUNICODE_STRING UserName OPTIONAL,
    IN PCHAR CaseInsensitivePassword,
    IN CLONG CaseInsensitivePasswordLength,
    IN PCHAR CaseSensitivePassword OPTIONAL,
    IN CLONG CaseSensitivePasswordLength,
    IN BOOLEAN SmbSecuritySignatureIfPossible,
    OUT PUSHORT Action  OPTIONAL
    )

/*++

Routine Description:

    Validates a username/password combination by interfacing to the
    security subsystem.

Arguments:

    Session - A pointer to a session block so that this routine can
        insert a user token.

    Connection - A pointer to the connection this user is on.

    UserName - ASCIIZ string corresponding to the user name to validate.

    CaseInsensitivePassword - ASCII (not ASCIIZ) string containing
        password for the user.

    CaseInsensitivePasswordLength - Length of Password, in bytes.
        This includes the null terminator when the password is not
        encrypted.

    CaseSensitivePassword - a mixed case, Unicode version of the password.
        This is only supplied by NT clients; for downlevel clients,
        it will be NULL.

    CaseSensitivePasswordLength - the length of the case-sensitive password.

    Action - This is part of the sessionsetupandx response.

Return Value:

    NTSTATUS from the security system.

--*/

{
    NTSTATUS status;
    LOGON_INFO logonInfo;
    PPAGED_CONNECTION pagedConnection;
    UNICODE_STRING domainName;

    PAGED_CODE( );

    INVALIDATE_SECURITY_HANDLE( *Token );

    if( ARGUMENT_PRESENT( Session ) ) {
        INVALIDATE_SECURITY_HANDLE( Session->UserHandle );
    }

    RtlZeroMemory( &logonInfo, sizeof( logonInfo ) );

    //
    // Load input parameters for DoUserLogon into the LOGON_INFO struct.
    //
    // If this is the server's initialization attempt at creating a null
    // session, then the Connection and Session pointers will be NULL.
    //

    domainName.Buffer = NULL;
    domainName.Length = 0;

    if ( ARGUMENT_PRESENT(Connection) ) {

        pagedConnection = Connection->PagedConnection;

        logonInfo.WorkstationName =
                    pagedConnection->ClientMachineNameString.Buffer;
        logonInfo.WorkstationNameLength =
                    pagedConnection->ClientMachineNameString.Length;

        RtlCopyMemory(
            logonInfo.EncryptionKey,
            pagedConnection->EncryptionKey,
            MSV1_0_CHALLENGE_LENGTH
            );

        logonInfo.NtSmbs = CLIENT_CAPABLE_OF( NT_SMBS, Connection );

        ASSERT( ARGUMENT_PRESENT(Session) );

        SrvGetUserAndDomainName( Session, NULL, &domainName );

        logonInfo.DomainName = domainName.Buffer;
        logonInfo.DomainNameLength = domainName.Length;

    } else {

        ASSERT( !ARGUMENT_PRESENT(Session) );

        logonInfo.WorkstationName = StrNull;
        logonInfo.DomainName = StrNull;
    }

    if ( ARGUMENT_PRESENT(UserName) ) {
        logonInfo.UserName = UserName->Buffer;
        logonInfo.UserNameLength = UserName->Length;
    } else {
        logonInfo.UserName = StrNull;
    }

    logonInfo.CaseSensitivePassword = CaseSensitivePassword;
    logonInfo.CaseSensitivePasswordLength = CaseSensitivePasswordLength;

    logonInfo.CaseInsensitivePassword = CaseInsensitivePassword;
    logonInfo.CaseInsensitivePasswordLength = CaseInsensitivePasswordLength;

    INVALIDATE_SECURITY_HANDLE( logonInfo.Token );

    if ( ARGUMENT_PRESENT(Action) ) {
        logonInfo.Action = *Action;
    }

    if( ARGUMENT_PRESENT(Session) ) {
        logonInfo.Uid = Session->Uid;
    }

    //
    // Attempt the logon.
    //

    status = DoUserLogon( &logonInfo, SmbSecuritySignatureIfPossible, Connection, Session );

    if( logonInfo.HaveHandle ) {
        *Token = logonInfo.Token;
    }

    if( domainName.Buffer ) {
        SrvReleaseUserAndDomainName( Session, NULL, &domainName );
    }

    if ( NT_SUCCESS(status) ) {

        //
        // The logon succeeded.  Save output data.
        //

        if ( ARGUMENT_PRESENT(Session) ) {

            Session->LogonId = logonInfo.LogonId;

            Session->KickOffTime = logonInfo.KickOffTime;
            Session->LogOffTime = logonInfo.LogOffTime;

            Session->GuestLogon = logonInfo.GuestLogon;
            Session->EncryptedLogon = logonInfo.EncryptedLogon;
            Session->IsNullSession = logonInfo.IsNullSession;
            Session->IsAdmin = logonInfo.IsAdmin;

            if( logonInfo.HaveHandle ) {
                Session->UserHandle = logonInfo.Token;
            } else {
                INVALIDATE_SECURITY_HANDLE( Session->UserHandle );
            }

            RtlCopyMemory(
                Session->NtUserSessionKey,
                logonInfo.NtUserSessionKey,
                MSV1_0_USER_SESSION_KEY_LENGTH
                );
            RtlCopyMemory(
                Session->LanManSessionKey,
                logonInfo.LanManSessionKey,
                MSV1_0_LANMAN_SESSION_KEY_LENGTH
                );

            SET_BLOCK_STATE( Session, BlockStateActive );
        }

        if ( ARGUMENT_PRESENT(Action) ) {
            *Action = logonInfo.Action;
            if( logonInfo.GuestLogon ) {
                *Action |= SMB_SETUP_GUEST;
            }
        }
    }

    return status;

} // SrvValidateUser


NTSTATUS
DoUserLogon (
    IN PLOGON_INFO LogonInfo,
    IN BOOLEAN SecuritySignatureDesired,
    IN PCONNECTION Connection OPTIONAL,
    IN OPTIONAL PSESSION Session
    )

/*++

Routine Description:

    Validates a username/password combination by interfacing to the
    security subsystem.

Arguments:

    LogonInfo - Pointer to a block containing in/out information about
        the logon.

Return Value:

    NTSTATUS from the security system.

--*/

{
    NTSTATUS status, subStatus;
    ULONG actualUserInfoBufferLength;
    ULONG oldSessionCount;
    LUID LogonId;
    ULONG Catts = 0;
    LARGE_INTEGER Expiry;
    ULONG BufferOffset;
    SecBufferDesc InputToken;
    SecBuffer InputBuffers[2];
    SecBufferDesc OutputToken;
    SecBuffer OutputBuffer;
    PNTLM_AUTHENTICATE_MESSAGE NtlmInToken = NULL;
    PAUTHENTICATE_MESSAGE InToken = NULL;
    PNTLM_ACCEPT_RESPONSE OutToken = NULL;
    ULONG NtlmInTokenSize;
    ULONG InTokenSize;
    ULONG OutTokenSize;
    ULONG_PTR AllocateSize;

    ULONG profileBufferLength;

    PAGED_CODE( );

    LogonInfo->IsNullSession = FALSE;
    LogonInfo->IsAdmin = FALSE;

#if DBG
    SrvLogonCount++;
#endif

    //
    // If this is a null session request, use the cached null session
    // token, which was created during server startup ( if we got one! )
    //

    if ( (LogonInfo->UserNameLength == 0) &&
         (LogonInfo->CaseSensitivePasswordLength == 0) &&
         ( (LogonInfo->CaseInsensitivePasswordLength == 0) ||
           ( (LogonInfo->CaseInsensitivePasswordLength == 1) &&
             (*LogonInfo->CaseInsensitivePassword == '\0') ) ) ) {

        if( CONTEXT_NULL( SrvNullSessionToken ) ) {

            if( SrvFspActive ) {
                return STATUS_ACCESS_DENIED;
            }

        } else {

            LogonInfo->IsNullSession = TRUE;

#if DBG
            SrvNullLogonCount++;
#endif

            LogonInfo->HaveHandle = TRUE;
            LogonInfo->Token = SrvNullSessionToken;

            LogonInfo->KickOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF;
            LogonInfo->LogOffTime.QuadPart = 0x7FFFFFFFFFFFFFFF;

            LogonInfo->GuestLogon = FALSE;
            LogonInfo->EncryptedLogon = FALSE;

            return STATUS_SUCCESS;
        }
    }

    //
    // First make sure we have a credential handle
    //

    if ((SrvHaveCreds & HAVENTLM) == 0) {

        status = AcquireLMCredentials();

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

    //
    // Figure out how big a buffer we need.  We put all the messages
    // in one buffer for efficiency's sake.
    //

    NtlmInTokenSize = sizeof(NTLM_AUTHENTICATE_MESSAGE);
    NtlmInTokenSize = (NtlmInTokenSize + 3) & 0xfffffffc;

    InTokenSize = sizeof(AUTHENTICATE_MESSAGE) +
            LogonInfo->UserNameLength +
            LogonInfo->WorkstationNameLength +
            LogonInfo->DomainNameLength +
            LogonInfo->CaseInsensitivePasswordLength +
            ROUND_UP_COUNT(LogonInfo->CaseSensitivePasswordLength, sizeof(USHORT));


    InTokenSize = (InTokenSize + 3) & 0xfffffffc;

    OutTokenSize = sizeof(NTLM_ACCEPT_RESPONSE);
    OutTokenSize = (OutTokenSize + 3) & 0xfffffffc;

    //
    // Round this up to 8 byte boundary because the out token needs to be
    // quad word aligned for the LARGE_INTEGER.
    //

    AllocateSize = ((NtlmInTokenSize + InTokenSize + 7) & 0xfffffff8) + OutTokenSize;

    status = STATUS_SUCCESS ;

    InToken = ExAllocatePool( PagedPool, AllocateSize );

    if ( InToken == NULL )
    {
        status = STATUS_NO_MEMORY ;

    }

    if ( !NT_SUCCESS(status) ) {

        actualUserInfoBufferLength = (ULONG)AllocateSize;

        INTERNAL_ERROR(
            ERROR_LEVEL_EXPECTED,
            "SrvValidateUser: ExAllocatePool failed: %X\n.",
            status,
            NULL
            );

        SrvLogError(
            SrvDeviceObject,
            EVENT_SRV_NO_VIRTUAL_MEMORY,
            status,
            &actualUserInfoBufferLength,
            sizeof(ULONG),
            NULL,
            0
            );

        status = STATUS_INSUFF_SERVER_RESOURCES;
        goto error_exit;
    }

    //
    // Zero the input tokens
    //

    RtlZeroMemory(
        InToken,
        InTokenSize + NtlmInTokenSize
        );

    NtlmInToken = (PNTLM_AUTHENTICATE_MESSAGE) ((PUCHAR) InToken + InTokenSize);
    OutToken = (PNTLM_ACCEPT_RESPONSE) ((PUCHAR) (((ULONG_PTR) NtlmInToken + NtlmInTokenSize + 7) & ~7));

    //
    // First set up the NtlmInToken, since it is the easiest.
    //

    RtlCopyMemory(
        NtlmInToken->ChallengeToClient,
        LogonInfo->EncryptionKey,
        MSV1_0_CHALLENGE_LENGTH
        );

    NtlmInToken->ParameterControl = 0;


    //
    // Okay, now for the tought part - marshalling the AUTHENTICATE_MESSAGE
    //

    RtlCopyMemory(  InToken->Signature,
                    NTLMSSP_SIGNATURE,
                    sizeof(NTLMSSP_SIGNATURE));

    InToken->MessageType = NtLmAuthenticate;

    BufferOffset = sizeof(AUTHENTICATE_MESSAGE);

    //
    // LM password - case insensitive
    //

    InToken->LmChallengeResponse.Buffer = BufferOffset;
    InToken->LmChallengeResponse.Length =
        InToken->LmChallengeResponse.MaximumLength =
            (USHORT) LogonInfo->CaseInsensitivePasswordLength;

    RtlCopyMemory(  BufferOffset + (PCHAR) InToken,
                    LogonInfo->CaseInsensitivePassword,
                    LogonInfo->CaseInsensitivePasswordLength);

    BufferOffset += ROUND_UP_COUNT(LogonInfo->CaseInsensitivePasswordLength, sizeof(USHORT));

    //
    // NT password - case sensitive
    //

    InToken->NtChallengeResponse.Buffer = BufferOffset;
    InToken->NtChallengeResponse.Length =
        InToken->NtChallengeResponse.MaximumLength =
            (USHORT) LogonInfo->CaseSensitivePasswordLength;

    RtlCopyMemory(  BufferOffset + (PCHAR) InToken,
                    LogonInfo->CaseSensitivePassword,
                    LogonInfo->CaseSensitivePasswordLength);

    BufferOffset += LogonInfo->CaseSensitivePasswordLength;

    //
    // Domain Name
    //

    InToken->DomainName.Buffer = BufferOffset;
    InToken->DomainName.Length =
        InToken->DomainName.MaximumLength =
            (USHORT) LogonInfo->DomainNameLength;

    RtlCopyMemory(  BufferOffset + (PCHAR) InToken,
                    LogonInfo->DomainName,
                    LogonInfo->DomainNameLength);

    BufferOffset += LogonInfo->DomainNameLength;

    //
    // Workstation Name
    //

    InToken->Workstation.Buffer = BufferOffset;
    InToken->Workstation.Length =
        InToken->Workstation.MaximumLength =
            (USHORT) LogonInfo->WorkstationNameLength;

    RtlCopyMemory(  BufferOffset + (PCHAR) InToken,
                    LogonInfo->WorkstationName,
                    LogonInfo->WorkstationNameLength);

    BufferOffset += LogonInfo->WorkstationNameLength;


    //
    // User Name
    //

    InToken->UserName.Buffer = BufferOffset;
    InToken->UserName.Length =
        InToken->UserName.MaximumLength =
            (USHORT) LogonInfo->UserNameLength;

    RtlCopyMemory(  BufferOffset + (PCHAR) InToken,
                    LogonInfo->UserName,
                    LogonInfo->UserNameLength);

    BufferOffset += LogonInfo->UserNameLength;



    //
    // Setup all the buffers properly
    //

    InputToken.pBuffers = InputBuffers;
    InputToken.cBuffers = 2;
    InputToken.ulVersion = 0;
    InputBuffers[0].pvBuffer = InToken;
    InputBuffers[0].cbBuffer = InTokenSize;
    InputBuffers[0].BufferType = SECBUFFER_TOKEN;
    InputBuffers[1].pvBuffer = NtlmInToken;
    InputBuffers[1].cbBuffer = NtlmInTokenSize;
    InputBuffers[1].BufferType = SECBUFFER_TOKEN;

    OutputToken.pBuffers = &OutputBuffer;
    OutputToken.cBuffers = 1;
    OutputToken.ulVersion = 0;
    OutputBuffer.pvBuffer = OutToken;
    OutputBuffer.cbBuffer = OutTokenSize;
    OutputBuffer.BufferType = SECBUFFER_TOKEN;

    SrvStatistics.SessionLogonAttempts++;

    status = AcceptSecurityContext(
                &SrvLmLsaHandle,
                NULL,
                &InputToken,
                ASC_REQ_ALLOW_NON_USER_LOGONS | ASC_REQ_ALLOW_NULL_SESSION,
                SECURITY_NATIVE_DREP,
                &LogonInfo->Token,
                &OutputToken,
                &Catts,
                (PTimeStamp) &Expiry
                );

    status = MapSecurityError( status );

    if ( !NT_SUCCESS(status) ) {

        INVALIDATE_SECURITY_HANDLE( LogonInfo->Token );

        INTERNAL_ERROR(
            ERROR_LEVEL_EXPECTED,
            "SrvValidateUser: LsaLogonUser failed: %X",
            status,
            NULL
            );

        ExFreePool( InToken );


        goto error_exit;
    }

    LogonInfo->KickOffTime = OutToken->KickoffTime;
    // Sspi will return time in LocalTime, convert to SystemTime
    ExLocalTimeToSystemTime( &Expiry, &LogonInfo->LogOffTime );
    //LogonInfo->LogOffTime = Expiry;
    LogonInfo->GuestLogon = (BOOLEAN)(OutToken->UserFlags & LOGON_GUEST);
    LogonInfo->EncryptedLogon = (BOOLEAN)!(OutToken->UserFlags & LOGON_NOENCRYPTION);
    LogonInfo->LogonId = OutToken->LogonId;
    LogonInfo->HaveHandle = TRUE;

    if ( (OutToken->UserFlags & LOGON_USED_LM_PASSWORD) &&
        LogonInfo->NtSmbs ) {

        ASSERT( MSV1_0_USER_SESSION_KEY_LENGTH >=
                MSV1_0_LANMAN_SESSION_KEY_LENGTH );

        RtlZeroMemory(
            LogonInfo->NtUserSessionKey,
            MSV1_0_USER_SESSION_KEY_LENGTH
            );

        RtlCopyMemory(
            LogonInfo->NtUserSessionKey,
            OutToken->LanmanSessionKey,
            MSV1_0_LANMAN_SESSION_KEY_LENGTH
            );

        //
        // Turn on bit 1 to tell the client that we are using
        // the lm session key instead of the user session key.
        //

        LogonInfo->Action |= SMB_SETUP_USE_LANMAN_KEY;

    } else {

        RtlCopyMemory(
            LogonInfo->NtUserSessionKey,
            OutToken->UserSessionKey,
            MSV1_0_USER_SESSION_KEY_LENGTH
            );

    }

    //
    // If we have a session and we didn't do a guest logon, start up
    //   security signatures if requested
    //

    if ( ARGUMENT_PRESENT( Connection ) &&
        SecuritySignatureDesired &&
        LogonInfo->GuestLogon == FALSE &&
        ( SrvSmbSecuritySignaturesRequired ||
          SrvEnableW9xSecuritySignatures   ||
          CLIENT_CAPABLE_OF(NT_STATUS, Connection) )
        )
        {

        SrvInitializeSmbSecuritySignature(
                    Connection,
                    LogonInfo->NtUserSessionKey,
                    ((OutToken->UserFlags & LOGON_USED_LM_PASSWORD) != 0) ?
                        LogonInfo->CaseInsensitivePassword :
                            LogonInfo->CaseSensitivePassword,
                    ((OutToken->UserFlags & LOGON_USED_LM_PASSWORD) != 0) ?
                        LogonInfo->CaseInsensitivePasswordLength :
                            LogonInfo->CaseSensitivePasswordLength
                    );
    }

    RtlCopyMemory(
        LogonInfo->LanManSessionKey,
        OutToken->LanmanSessionKey,
        MSV1_0_LANMAN_SESSION_KEY_LENGTH
        );

    ExFreePool( InToken );

    //
    // Note whether or not this user is an administrator
    //

    LogonInfo->IsAdmin = SrvIsAdmin( LogonInfo->Token );

    //
    // One last check:  Is our session count being exceeded?
    //   We will let the session be exceeded by 1 iff the client
    //   is an administrator.
    //

    if( LogonInfo->IsNullSession == FALSE ) {

        oldSessionCount = ExInterlockedAddUlong(
                          &SrvStatistics.CurrentNumberOfSessions,
                          1,
                          &GLOBAL_SPIN_LOCK(Statistics)
                          );

        SrvInhibitIdlePowerDown();

        if ( ARGUMENT_PRESENT(Session) && (!Session->IsSessionExpired && oldSessionCount >= SrvMaxUsers) ) {
            if( oldSessionCount != SrvMaxUsers || !LogonInfo->IsAdmin ) {

                ExInterlockedAddUlong(
                    &SrvStatistics.CurrentNumberOfSessions,
                    (ULONG)-1,
                    &GLOBAL_SPIN_LOCK(Statistics)
                    );

                DeleteSecurityContext( &LogonInfo->Token );
                INVALIDATE_SECURITY_HANDLE( LogonInfo->Token );

                status = STATUS_REQUEST_NOT_ACCEPTED;
                SrvAllowIdlePowerDown();
                goto error_exit;
            }
        }
    }

    return STATUS_SUCCESS;

error_exit:

    return status;

} // DoUserLogon

BOOLEAN
SrvIsAdmin(
    CtxtHandle  Handle
)
/*++

Routine Description:

    Returns TRUE if the user represented by Handle is an
      administrator

Arguments:

    Handle - Represents the user we're interested in

Return Value:

    TRUE if the user is an administrator.  FALSE otherwise.

--*/
{
    NTSTATUS                 status;
    SECURITY_SUBJECT_CONTEXT SubjectContext;
    ACCESS_MASK              GrantedAccess;
    GENERIC_MAPPING          Mapping = {   FILE_GENERIC_READ,
                                           FILE_GENERIC_WRITE,
                                           FILE_GENERIC_EXECUTE,
                                           FILE_ALL_ACCESS
                                       };
    HANDLE                   NullHandle = NULL;
    BOOLEAN                  retval  = FALSE;

    PAGED_CODE();

    //
    // Impersonate the client
    //
    status = ImpersonateSecurityContext( &Handle );

    if( !NT_SUCCESS( status ) )
        return FALSE;

    SeCaptureSubjectContext( &SubjectContext );

    retval = SeAccessCheck( &SrvAdminSecurityDescriptor,
                            &SubjectContext,
                            FALSE,
                            FILE_GENERIC_READ,
                            0,
                            NULL,
                            &Mapping,
                            UserMode,
                            &GrantedAccess,
                            &status );

    SeReleaseSubjectContext( &SubjectContext );

    //
    // Revert back to our original identity
    //

    REVERT( );
    return retval;
}

BOOLEAN
SrvIsNullSession(
    CtxtHandle  Handle
)
/*++

Routine Description:

    Returns TRUE if the user represented by Handle is an
      anonymous logon

Arguments:

    Handle - Represents the user we're interested in

Return Value:

    TRUE if the user is an anonymous logon.  FALSE otherwise.

--*/
{
    NTSTATUS                 status;
    SECURITY_SUBJECT_CONTEXT SubjectContext;
    ACCESS_MASK              GrantedAccess;
    GENERIC_MAPPING          Mapping = {   FILE_GENERIC_READ,
                                           FILE_GENERIC_WRITE,
                                           FILE_GENERIC_EXECUTE,
                                           FILE_ALL_ACCESS
                                       };
    HANDLE                   NullHandle = NULL;
    BOOLEAN                  retval  = FALSE;

    PAGED_CODE();

    //
    // Impersonate the client
    //
    status = ImpersonateSecurityContext( &Handle );

    if( !NT_SUCCESS( status ) )
        return FALSE;

    SeCaptureSubjectContext( &SubjectContext );

    retval = SeAccessCheck( &SrvNullSessionSecurityDescriptor,
                            &SubjectContext,
                            FALSE,
                            FILE_GENERIC_READ,
                            0,
                            NULL,
                            &Mapping,
                            UserMode,
                            &GrantedAccess,
                            &status );

    SeReleaseSubjectContext( &SubjectContext );

    //
    // Revert back to our original identity
    //

    REVERT( );
    return retval;
}

NTSTATUS
SrvGetLogonId(
    PCtxtHandle  Handle,
    PLUID LogonId
)
/*++

Routine Description:

    Returns the Logon Id for the requested context.

Arguments:

    Handle - Represents the user we're interested in

Return Value:

    Error codes from ImpersonateSecurityContext and SeQueryAuthenticationId.

--*/
{
    NTSTATUS                 Status;
    SECURITY_SUBJECT_CONTEXT SubjectContext;

    PAGED_CODE();

    //
    // Impersonate the client
    //
    Status = ImpersonateSecurityContext( Handle );

    if( !NT_SUCCESS( Status ) )
        return MapSecurityError(Status);

    SeCaptureSubjectContext( &SubjectContext );

    SeLockSubjectContext( &SubjectContext );

    Status = SeQueryAuthenticationIdToken(
                SubjectContext.ClientToken,
                LogonId
                );

    SeUnlockSubjectContext( &SubjectContext );
    SeReleaseSubjectContext( &SubjectContext );

    REVERT( );

    return(Status);
}


NTSTATUS
SrvValidateSecurityBuffer(
    IN PCONNECTION Connection,
    IN OUT PCtxtHandle Handle,
    IN PSESSION Session,
    IN PCHAR Buffer,
    IN ULONG  BufferLength,
    IN BOOLEAN SecuritySignaturesRequired,
    OUT PCHAR ReturnBuffer,
    IN OUT PULONG ReturnBufferLength,
    OUT PLARGE_INTEGER Expiry,
    OUT PCHAR NtUserSessionKey,
    OUT PLUID LogonId,
    OUT PBOOLEAN IsGuest
    )

/*++

Routine Description:

    Validates a Security Buffer sent from the client

Arguments:

    Handle - On successful return, contains the security context handle
        associated with the user login.

    Session - Points to the session structure for this user

    Buffer - The Buffer to validate

    BufferLength - The length in bytes of Buffer

    SecuritySignaturesRequired - Are we required to generate a security
        signature for the SMBs?

    ReturnBuffer - On return, contains a security buffer to return to the
        client.

    ReturnBufferLength - On return, size in bytes of ReturnBuffer.  On entry,
            the largest buffer we can return.

    Expiry - The time after which this security buffer is no longer valid.

    NtUserSessionKey - If STATUS_SUCCESS, the session key is returned here. This
        must point to a buffer at least MSV1_0_USER_SESSION_KEY_LENGTH big.

    LogonId - If successful, receives the logon id for this context.

    IsGuest - If successful, TRUE if the client has been validated as a guest

Return Value:

    NTSTATUS from the security system.  If STATUS_SUCCESS is returned, the user
        has been completely authenticated.

Notes:

    BUGBUG

    AcceptSecurityContext() needs to return the KickOffTime (ie, the logon
    hours restriction) so that the server can enfore it. The contact person
    is MikeSw for this.

--*/

{
    NTSTATUS Status;
    ULONG Catts;
    PUCHAR AllocateMemory = NULL;
    ULONG maxReturnBuffer = *ReturnBufferLength;
    ULONG_PTR AllocateLength = MAX(BufferLength, maxReturnBuffer );
    BOOLEAN virtualMemoryAllocated = FALSE;
    SecBufferDesc InputToken;
    SecBuffer InputBuffer;
    SecBufferDesc OutputToken;
    SecBuffer OutputBuffer;
    SecPkgContext_NamesW SecNames;
    SecPkgContext_SessionKey SecKeys;
    ULONG oldSessionCount;
    TimeStamp LocalExpiry = {0};

    *ReturnBufferLength = 0;
    *IsGuest = FALSE;

    if ( (SrvHaveCreds & HAVEEXTENDED) == 0 ) {
        return STATUS_ACCESS_DENIED;
    }

    RtlZeroMemory( &SecKeys, sizeof( SecKeys ) );
    RtlZeroMemory( &SecNames, sizeof( SecNames ) );

    InputToken.pBuffers = &InputBuffer;
    InputToken.cBuffers = 1;
    InputToken.ulVersion = 0;
    InputBuffer.pvBuffer = Buffer;
    InputBuffer.cbBuffer = BufferLength;
    InputBuffer.BufferType = SECBUFFER_TOKEN;

    OutputToken.pBuffers = &OutputBuffer;
    OutputToken.cBuffers = 1;
    OutputToken.ulVersion = 0;
    OutputBuffer.pvBuffer = ReturnBuffer ;
    OutputBuffer.cbBuffer = maxReturnBuffer ;
    OutputBuffer.BufferType = SECBUFFER_TOKEN;

    SrvStatistics.SessionLogonAttempts++;
    Catts = 0;

    Status = AcceptSecurityContext(
                    &SrvExtensibleSecurityHandle,
                    IS_VALID_SECURITY_HANDLE( *Handle ) ? Handle : NULL,
                    &InputToken,
                    ASC_REQ_EXTENDED_ERROR | ASC_REQ_ALLOW_NULL_SESSION |
                            ASC_REQ_DELEGATE | ASC_REQ_FRAGMENT_TO_FIT,
                    SECURITY_NATIVE_DREP,
                    Handle,
                    &OutputToken,
                    &Catts,
                    &LocalExpiry);

    Status = MapSecurityError( Status );

    //
    // If there is a return buffer to be sent back, copy it into the caller's
    // buffers now.
    //
    if ( NT_SUCCESS(Status) || (Catts & ASC_RET_EXTENDED_ERROR) ) {

        if( Status == STATUS_SUCCESS ) {
            NTSTATUS qcaStatus;
            SecPkgContext_UserFlags userFlags;

            // Sspi will return time in LocalTime, convert to UTC
            // Enable dynamic reauthentication if possible or required
            if( SrvEnforceLogoffTimes || CLIENT_CAPABLE_OF( DYNAMIC_REAUTH, Connection ) )
            {
                ExLocalTimeToSystemTime (&LocalExpiry, Expiry);
            }
            else
            {
                Expiry->QuadPart = SRV_NEVER_TIME ;
            }


            //
            // The user has been completely authenticated.  See if the session
            // count is being exceeded.  We'll allow it only if the new client
            // is an administrator.
            //

            oldSessionCount = ExInterlockedAddUlong(
                              &SrvStatistics.CurrentNumberOfSessions,
                              1,
                              &GLOBAL_SPIN_LOCK(Statistics)
                              );

            SrvInhibitIdlePowerDown();

            if ( !Session->IsSessionExpired && oldSessionCount >= SrvMaxUsers ) {
                if( oldSessionCount != SrvMaxUsers ||
                        !SrvIsAdmin( *Handle ) ) {

                    ExInterlockedAddUlong(
                        &SrvStatistics.CurrentNumberOfSessions,
                        (ULONG)-1,
                        &GLOBAL_SPIN_LOCK(Statistics)
                        );

                    DeleteSecurityContext( Handle );

                    INVALIDATE_SECURITY_HANDLE( *Handle );

                    Status = STATUS_REQUEST_NOT_ACCEPTED;
                    SrvAllowIdlePowerDown();
                    goto exit;
                }
            }

            //
            // Figure out if we validated the client as GUEST
            //
            qcaStatus = QueryContextAttributes(
                            Handle,
                            SECPKG_ATTR_USER_FLAGS,
                            &userFlags);


            if( NT_SUCCESS( MapSecurityError( qcaStatus ) ) ) {

                if( userFlags.UserFlags & LOGON_GUEST ) {
                    *IsGuest = TRUE;
                }

            } else {
                SrvLogServiceFailure( SRV_SVC_SECURITY_PKG_PROBLEM, qcaStatus );
            }

            //
            // Get the Logon Id for this context
            //
            Status = SrvGetLogonId( Handle, LogonId );

            //
            // Capture the session key for this context
            //
            RtlZeroMemory( (PVOID) NtUserSessionKey, MSV1_0_USER_SESSION_KEY_LENGTH );

            qcaStatus = QueryContextAttributes(
                            Handle,
                            SECPKG_ATTR_SESSION_KEY,
                            &SecKeys);

            if( NT_SUCCESS( MapSecurityError( qcaStatus ) ) ) {

                RtlCopyMemory(
                    (PVOID) NtUserSessionKey,
                    SecKeys.SessionKey,
                    MIN(MSV1_0_USER_SESSION_KEY_LENGTH, SecKeys.SessionKeyLength)
                    );

                //
                // Start the security signatures, if required.  We do not do security signatures
                //   if we have a null session or a guest logon.
                //
                if( NT_SUCCESS( Status ) &&
                    SecuritySignaturesRequired &&
                    *IsGuest == FALSE &&
                    Connection->SmbSecuritySignatureActive == FALSE &&
                    !SrvIsNullSession( *Handle ) ) {

                    //
                    // Start the sequence number generation
                    //
                    SrvInitializeSmbSecuritySignature(
                                    Connection,
                                    NULL,
                                    SecKeys.SessionKey,
                                    SecKeys.SessionKeyLength
                                    );
                }

                FreeContextBuffer( SecKeys.SessionKey );

            } else {

                SrvLogServiceFailure( SRV_SVC_SECURITY_PKG_PROBLEM, qcaStatus );
            }

            if( !NT_SUCCESS( Status ) ) {
                DeleteSecurityContext( Handle );
                INVALIDATE_SECURITY_HANDLE( *Handle );
            }
        }

        ASSERT( OutputBuffer.cbBuffer <= maxReturnBuffer );

        //
        // If it fits, and a buffer was returned, send it to the client.  If it doesn't fit,
        //  then log the problem.
        //
        if( OutputBuffer.cbBuffer <= maxReturnBuffer ) {
            if( OutputBuffer.cbBuffer != 0 ) {
                *ReturnBufferLength = OutputBuffer.cbBuffer;
            }
        } else {
            SrvLogServiceFailure( SRV_SVC_SECURITY_PKG_PROBLEM, OutputBuffer.cbBuffer );
        }
    }

exit:

#if DBG

    //
    // RDR or SRV is sending in a corrupt security blob to LSA -- need to
    // find out what the source is.
    //

    if( NT_SUCCESS(Status) )
    {
        if( (OutputBuffer.pvBuffer != NULL) &&
            (OutputBuffer.cbBuffer >= sizeof(DWORD))
            )
        {
            PUCHAR pValidate = (PUCHAR) OutputBuffer.pvBuffer ;

            ASSERT( ( pValidate[0] != 0 ) ||
                    ( pValidate[1] != 0 ) ||
                    ( pValidate[2] != 0 ) ||
                    ( pValidate[3] != 0 ) );
        }
    }
#endif


    if( NT_SUCCESS( Status ) && Status != STATUS_SUCCESS ) {
        Status = STATUS_MORE_PROCESSING_REQUIRED;
    }

    return Status;

} // SrvValidateSecurityBuffer

NTSTATUS
SrvGetUserAndDomainName (
    IN PSESSION Session,
    OUT PUNICODE_STRING UserName OPTIONAL,
    OUT PUNICODE_STRING DomainName OPTIONAL
    )
/*++

Routine Description

    Return the user and domain names associated with the Session

Arguments:
    IN PSESSION Session : The session

Return Value:
    IN OUT PUNICODE_STRING UserName
    IN OUT PUNICODE_STRING DomainName

Note:
    The caller must call SrvReleaseUserAndDomainName() when finished

--*/
{
    SecPkgContext_NamesW SecNames;
    NTSTATUS status;
    UNICODE_STRING fullName, tmpUserName, tmpDomainName;
    USHORT i, fullNameLength;

    PAGED_CODE();

    if( !IS_VALID_SECURITY_HANDLE( Session->UserHandle ) ) {

        if( ARGUMENT_PRESENT( UserName ) ) {
            *UserName = Session->NtUserName;
        }
        if( ARGUMENT_PRESENT( DomainName ) ) {
            *DomainName = Session->NtUserDomain;
        }

        return STATUS_SUCCESS;
    }

    if( ARGUMENT_PRESENT( UserName ) ) {
        UserName->Buffer = NULL;
        UserName->Length = 0;
    }

    if( ARGUMENT_PRESENT( DomainName ) ) {
        DomainName->Buffer = NULL;
        DomainName->Length = 0;
    }

    //
    // If it's the NULL session, then there are no names to be returned!
    //
    if( Session->IsNullSession == TRUE ) {
        return STATUS_SUCCESS;
    }

    SecNames.sUserName = NULL;

    status = QueryContextAttributesW(
                    &Session->UserHandle,
                    SECPKG_ATTR_NAMES,
                    &SecNames
            );

    status = MapSecurityError( status );

    if (!NT_SUCCESS(status)) {
        if( Session->LogonSequenceInProgress == FALSE ) {
            //
            // If the client is in the middle of an extended logon sequence,
            //   then failures of this type are expected and we don't want
            //   to clutter the event log with them
            //
            SrvLogServiceFailure( SRV_SVC_LSA_LOOKUP_PACKAGE, status );
        }
        return status;
    }

    //
    // See if we have a NULL user names.  This shouldn't happen, but
    //  might if a security package is incomplete or something
    //
    if( SecNames.sUserName == NULL || *SecNames.sUserName == L'\0' ) {

        if( SecNames.sUserName != NULL ) {
            FreeContextBuffer( SecNames.sUserName );
        }
        return STATUS_SUCCESS;
    }

    //
    // The return SecNames.sUserName should be in domainname\username format.
    //  We need to split it apart.
    //
    RtlInitUnicodeString( &fullName, SecNames.sUserName );

    fullNameLength = fullName.Length / sizeof(WCHAR);

    tmpDomainName.Buffer = fullName.Buffer;

    for (i = 0; i < fullNameLength && tmpDomainName.Buffer[i] != L'\\'; i++) {
         NOTHING;
    }

    if( tmpDomainName.Buffer[i] != L'\\' ) {
        FreeContextBuffer( SecNames.sUserName );
        return STATUS_INVALID_ACCOUNT_NAME;
    }

    tmpDomainName.Length = i * sizeof(WCHAR);
    tmpDomainName.MaximumLength = tmpDomainName.Length;

    tmpUserName.Buffer = &tmpDomainName.Buffer[i + 1];
    tmpUserName.Length = fullName.Length - tmpDomainName.Length - sizeof(WCHAR);
    tmpUserName.MaximumLength = tmpUserName.Length;

    if( ARGUMENT_PRESENT( UserName ) ) {
        status = RtlUpcaseUnicodeString( UserName, &tmpUserName, TRUE);
        if( !NT_SUCCESS( status ) ) {
            SrvLogServiceFailure( SRV_SVC_LSA_LOOKUP_PACKAGE, status );
            FreeContextBuffer( SecNames.sUserName );
            return status;
        }
    }

    if( ARGUMENT_PRESENT( DomainName ) ) {
        status = RtlUpcaseUnicodeString( DomainName, &tmpDomainName, TRUE );
        if( !NT_SUCCESS( status ) ) {
            SrvLogServiceFailure( SRV_SVC_LSA_LOOKUP_PACKAGE, status );
            FreeContextBuffer( SecNames.sUserName );
            if( UserName != NULL ) {
                RtlFreeUnicodeString( UserName );
            }
            return status;
        }
    }

    FreeContextBuffer( SecNames.sUserName );

    return STATUS_SUCCESS;
}

VOID
SrvReleaseUserAndDomainName(
    IN PSESSION Session,
    IN OUT PUNICODE_STRING UserName OPTIONAL,
    IN OUT PUNICODE_STRING DomainName OPTIONAL
    )
/*++

Routine Description

    This is the complement of SrvGetUserAndDomainName.  It frees the memory
        if necessary.

--*/

{
    PAGED_CODE();

    if( ARGUMENT_PRESENT( UserName ) &&
        UserName->Buffer != NULL &&
        UserName->Buffer != Session->NtUserName.Buffer ) {

        RtlFreeUnicodeString( UserName );
    }

    if( ARGUMENT_PRESENT( DomainName ) &&
        DomainName->Buffer != NULL &&
        DomainName->Buffer != Session->NtUserDomain.Buffer ) {

        RtlFreeUnicodeString( DomainName );

    }
}


NTSTATUS
SrvFreeSecurityContexts (
    IN PSESSION Session
    )

/*++

Routine Description:

    Releases any context obtained for security purposes

Arguments:

    IN PSESSION Session : The session

Return Value:

    NTSTATUS

--*/

{
    SECURITY_STATUS status;

    if( IS_VALID_SECURITY_HANDLE( Session->UserHandle ) ) {

        if ( !CONTEXT_EQUAL( Session->UserHandle, SrvNullSessionToken ) ) {

            status = DeleteSecurityContext( &Session->UserHandle );
            INVALIDATE_SECURITY_HANDLE( Session->UserHandle );

            if( NT_SUCCESS( status ) &&
                !Session->LogonSequenceInProgress ) {

                ExInterlockedAddUlong(
                    &SrvStatistics.CurrentNumberOfSessions,
                    (ULONG)-1,
                    &GLOBAL_SPIN_LOCK(Statistics)
                    );

                SrvAllowIdlePowerDown();
            }

        }
    }

    return STATUS_SUCCESS;

} // SrvFreeSecurityContexts


NTSTATUS
AcquireLMCredentials (
    VOID
    )
{
    UNICODE_STRING Ntlm;
    NTSTATUS status;
    TimeStamp Expiry;

    RtlInitUnicodeString( &Ntlm, L"NTLM" );

    //
    // We pass in 1 for the GetKeyArg to indicate that this is
    // downlevel NTLM, to distinguish it from NT5 NTLM.
    //

    status = AcquireCredentialsHandle(
                NULL,                   // Default principal
                (PSECURITY_STRING) &Ntlm,
                SECPKG_CRED_INBOUND,    // Need to define this
                NULL,                   // No LUID
                NULL,                   // No AuthData
                NULL,                   // No GetKeyFn
                NTLMSP_NTLM_CREDENTIAL, // GetKeyArg
                &SrvLmLsaHandle,
                &Expiry
                );

    if ( !NT_SUCCESS(status) ) {
        status = MapSecurityError(status);
        return status;
    }
    SrvHaveCreds |= HAVENTLM;

    return status;

} // AcquireLMCredentials

#ifndef EXTENSIBLESSP_NAME
#define EXTENSIBLESSP_NAME NEGOSSP_NAME_W
#endif


NTSTATUS
AcquireExtensibleSecurityCredentials (
    VOID
    )

/*++

Routine Description:

    Acquires the handle to the security negotiate package.

Arguments:

    none.

Return Value:

    NTSTATUS

--*/

{
    UNICODE_STRING NegotiateName;
    TimeStamp Expiry;
    NTSTATUS status ;


    RtlInitUnicodeString( &NegotiateName, EXTENSIBLESSP_NAME );

    status = AcquireCredentialsHandle(
                NULL,                   // Default principal
                (PSECURITY_STRING) &NegotiateName,
                SECPKG_CRED_INBOUND,    // Need to define this
                NULL,                   // No LUID
                NULL,                   // No AuthData
                NULL,                   // No GetKeyFn
                NULL,                   // No GetKeyArg
                &SrvExtensibleSecurityHandle,
                &Expiry
                );


    if ( !NT_SUCCESS(status) ) {
        status = MapSecurityError(status);
        return status;
    }
    SrvHaveCreds |= HAVEEXTENDED;

    return status;

} // AcquireExtensibleSecurityCredentials

VOID
SrvAddSecurityCredentials(
    IN PANSI_STRING ComputerNameA,
    IN PUNICODE_STRING DomainName,
    IN DWORD PasswordLength,
    IN PBYTE Password
)
/*++

Routine Description:

    In order for mutual authentication to work, the security subsystem needs to know
    all the names the server is using, as well as any passwords needed to decrypt
    the security information associated with the server name.  This routine informs
    the security subsystem.

Arguments:

    ComputerName, DomainName - these are the names the clients will be using to access this system

    PasswordLength, Password - this is the secret the security system needs to know to decode the
        passed security information

--*/
{
    NTSTATUS status;
    UNICODE_STRING ComputerName;
    PUSHORT p;
    PVOID VirtualMem ;
    SIZE_T Size ;
    PSEC_WINNT_AUTH_IDENTITY Auth ;
    PUCHAR Where ;
    UNICODE_STRING NegotiateName;
    TimeStamp Expiry;

    PAGED_CODE();

    status = RtlAnsiStringToUnicodeString( &ComputerName, ComputerNameA, TRUE );

    if( !NT_SUCCESS( status ) ) {
        IF_DEBUG( ERRORS ) {
            KdPrint(( "SRV: SrvAddSecurityCredentials, status %X at %d\n", status, __LINE__ ));
        }
        return;
    }

    if ((SrvHaveCreds & HAVEEXTENDED) == 0) {
        if (status = AcquireExtensibleSecurityCredentials()) {
            return ;
        }
    }

    //
    // Trim off any trailing blanks
    //
    for( p = &ComputerName.Buffer[ (ComputerName.Length / sizeof( WCHAR )) - 1 ];
         p > ComputerName.Buffer;
         p-- ) {

        if( *p != L' ' )
            break;
    }

    ComputerName.Length = (USHORT)((p - ComputerName.Buffer + 1) * sizeof( WCHAR ));

    if( ComputerName.Length ) {
        //
        // Tell the security subsystem about this name.
        //
        RtlInitUnicodeString( &NegotiateName, EXTENSIBLESSP_NAME );

        Size = ComputerName.Length + sizeof( WCHAR ) +
               DomainName->Length + sizeof( WCHAR ) +
               PasswordLength +
               sizeof( SEC_WINNT_AUTH_IDENTITY ) ;


        VirtualMem = NULL ;

        status = NtAllocateVirtualMemory(
                    NtCurrentProcess(),
                    &VirtualMem,
                    0,
                    &Size,
                    MEM_COMMIT,
                    PAGE_READWRITE );

        if ( NT_SUCCESS( status ) )
        {
            Auth = (PSEC_WINNT_AUTH_IDENTITY) VirtualMem ;

            Where = (PUCHAR) (Auth + 1);

            Auth->User = (PWSTR) Where ;

            Auth->UserLength = ComputerName.Length / sizeof( WCHAR );

            RtlCopyMemory(
                Where,
                ComputerName.Buffer,
                ComputerName.Length );

            Where += ComputerName.Length ;

            Auth->Domain = (PWSTR) Where ;

            Auth->DomainLength = DomainName->Length / sizeof( WCHAR );

            RtlCopyMemory(
                Where,
                DomainName->Buffer,
                DomainName->Length );

            Where += DomainName->Length ;

            Auth->Password = (PWSTR) Where ;

            Auth->PasswordLength = PasswordLength / sizeof( WCHAR );

            RtlCopyMemory(
                Where,
                Password,
                PasswordLength );

            Auth->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE ;


            status = AddCredentials(
                        &SrvExtensibleSecurityHandle,                   // Default principal
                        NULL,
                        (PSECURITY_STRING) &NegotiateName,
                        SECPKG_CRED_INBOUND,    // Need to define this
                        Auth,                   // Auth data
                        NULL,                   // No GetKeyFn
                        NULL,                   // No GetKeyArg
                        &Expiry );

            NtFreeVirtualMemory(
                NtCurrentProcess(),
                &VirtualMem,
                &Size,
                MEM_RELEASE );

        }

    }

    //
    // Free up our memory
    //
    RtlFreeUnicodeString( &ComputerName );
}

NTSTATUS
SrvGetExtensibleSecurityNegotiateBuffer(
    OUT PCtxtHandle Token,
    OUT PCHAR Buffer,
    OUT USHORT *BufferLength
    )

{

    NTSTATUS Status;
    ULONG Attributes;
    TimeStamp Expiry;
    SecBufferDesc OutputToken;
    SecBuffer OutputBuffer;

    if ((SrvHaveCreds & HAVEEXTENDED) == 0) {
        if (Status = AcquireExtensibleSecurityCredentials()) {
            *BufferLength = 0;
            return(Status);
        }
    }


    OutputToken.pBuffers = &OutputBuffer;
    OutputToken.cBuffers = 1;
    OutputToken.ulVersion = 0;
    OutputBuffer.pvBuffer = 0;
    OutputBuffer.cbBuffer = 0;
    OutputBuffer.BufferType = SECBUFFER_TOKEN;

    Status = AcceptSecurityContext (
                   &SrvExtensibleSecurityHandle,
                   NULL,
                   NULL,
                   ASC_REQ_INTEGRITY | ASC_REQ_CONFIDENTIALITY |
                        ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_ALLOW_NULL_SESSION |
                        ASC_REQ_DELEGATE,
                   SECURITY_NATIVE_DREP,
                   Token,
                   &OutputToken,
                   &Attributes,
                   &Expiry);

     if (!NT_SUCCESS(Status)) {
        *BufferLength = 0;
        return(Status);
     }

     if (OutputBuffer.cbBuffer >=
            (SrvReceiveBufferSize - sizeof(PRESP_NT_NEGOTIATE))) {

         Status = STATUS_INVALID_BUFFER_SIZE;

         SrvLogServiceFailure( SRV_SVC_LSA_CALL_AUTH_PACKAGE, Status);

         *BufferLength = 0;

     } else {

         RtlCopyMemory(Buffer, OutputBuffer.pvBuffer, OutputBuffer.cbBuffer);

         *BufferLength = (USHORT) OutputBuffer.cbBuffer;

     }

#if DBG

    //
    // RDR or SRV is sending in a corrupt security blob to LSA -- need to
    // find out what the source is.
    //

    if( NT_SUCCESS(Status) )
    {
        if( (OutputBuffer.pvBuffer != NULL) &&
            (OutputBuffer.cbBuffer >= sizeof(DWORD))
            )
        {
            PDWORD pdwValidate = (DWORD*)OutputBuffer.pvBuffer;
            ASSERT( *pdwValidate != 0 );
        }
    }
#endif


     FreeContextBuffer(OutputBuffer.pvBuffer);

     return( Status );

}

VOID SRVFASTCALL
SrvInitializeSmbSecuritySignature(
    IN OUT PCONNECTION Connection,
    IN PUCHAR SessionKey OPTIONAL,
    IN PUCHAR ChallengeResponse,
    IN ULONG ChallengeResponseLength
    )
/*++

Routine Description:

    Initializes the security signature generator for a session by calling MD5Update
    on the session key, challenge response

Arguments:

    SessionKey - Either the LM or NT session key, depending on which
        password was used for authentication, must be at least 16 bytes
    ChallengeResponse - The challenge response used for authentication, must
        be at least 24 bytes

--*/
{
    RtlZeroMemory( &Connection->Md5Context, sizeof( Connection->Md5Context ) );

    MD5Init( &Connection->Md5Context );

    if( ARGUMENT_PRESENT( SessionKey ) ) {
        MD5Update( &Connection->Md5Context, SessionKey, USER_SESSION_KEY_LENGTH );
    }

    MD5Update( &Connection->Md5Context, ChallengeResponse, ChallengeResponseLength );

    Connection->SmbSecuritySignatureIndex = 0;
    Connection->SmbSecuritySignatureActive = TRUE;

    //
    // We don't know how to do RAW and security signatures
    //
    Connection->EnableRawIo = FALSE;

    IF_DEBUG( SECSIG ) {
        KdPrint(( "SRV: SMB sigs enabled for %wZ, conn %p, build %d\n",
                &Connection->PagedConnection->ClientMachineNameString,
                Connection, Connection->PagedConnection->ClientBuildNumber ));
    }
}

VOID SRVFASTCALL
SrvAddSmbSecuritySignature(
    IN OUT PWORK_CONTEXT WorkContext,
    IN PMDL Mdl,
    IN ULONG SendLength
    )
/*++

Routine Description:

    Generates the next security signature

Arguments:

    WorkContext - the context to sign

Return Value:

    none.

--*/
{
    MD5_CTX Context;
    PSMB_HEADER Smb = MmGetSystemAddressForMdl( Mdl );

    IF_DEBUG( SECSIG ) {
        KdPrint(( "SRV: resp sig: cmd %x, index %u, len %d\n",
                Smb->Command, WorkContext->ResponseSmbSecuritySignatureIndex, SendLength ));
    }

#if DBG
    //
    // Put the index number right after the signature.  This allows us to figure out on the client
    //  side if we have a signature mismatch.
    //
    SmbPutUshort( &Smb->SecuritySignature[SMB_SECURITY_SIGNATURE_LENGTH],
        (USHORT)WorkContext->ResponseSmbSecuritySignatureIndex );

#endif

    //
    // Put the next index number into the SMB
    //
    SmbPutUlong( Smb->SecuritySignature, WorkContext->ResponseSmbSecuritySignatureIndex );
    RtlZeroMemory(  Smb->SecuritySignature + sizeof(ULONG),
                    SMB_SECURITY_SIGNATURE_LENGTH-sizeof(ULONG)
                 );

    //
    // Start out with our initial context
    //
    RtlCopyMemory( &Context, &WorkContext->Connection->Md5Context, sizeof( Context ) );

    //
    // Compute the signature for the SMB we're about to send
    //
    do {
        PCHAR SystemAddressForBuffer;

        ULONG len = MIN( SendLength, MmGetMdlByteCount( Mdl ) );

        SystemAddressForBuffer = MmGetSystemAddressForMdlSafe(Mdl,NormalPoolPriority);

        if (SystemAddressForBuffer == NULL) {
            // return without updating the security signature field. This will
            // in turn cause the client to reject the packet and tear down the
            // connection
            return;
        }

        MD5Update( &Context, SystemAddressForBuffer, len );

        SendLength -= len;

    } while( SendLength && (Mdl = Mdl->Next) != NULL );

    MD5Final( &Context );

    //
    // Put the signature into the SMB
    //
    RtlCopyMemory(
        Smb->SecuritySignature,
        Context.digest,
        SMB_SECURITY_SIGNATURE_LENGTH
        );
}

//
// Print the mismatched signature information to the debugger
//
VOID
SrvDumpSignatureError(
    IN PWORK_CONTEXT WorkContext,
    IN PUCHAR ExpectedSignature,
    IN PUCHAR ActualSignature,
    IN ULONG Length,
    IN ULONG ExpectedIndexNumber

    )
{
#if DBG
    DWORD i;
    PMDL Mdl = WorkContext->RequestBuffer->Mdl;
    ULONG requestLength = MIN( WorkContext->RequestBuffer->DataLength, 64 );
    PSMB_HEADER Smb = MmGetSystemAddressForMdl( Mdl );

    if( Smb->Command == SMB_COM_ECHO ) {
        return;
    }

    //
    // Security Signature Mismatch!
    //
    IF_DEBUG( ERRORS ) {
        KdPrint(( "SRV: Invalid security signature in request smb (cmd %X)", Smb->Command ));

        if( WorkContext->Connection && WorkContext->Connection->PagedConnection ) {
            KdPrint(( " from %wZ" ,
                        &WorkContext->Connection->PagedConnection->ClientMachineNameString ));
        }
    }
    IF_DEBUG( SECSIG ) {
        KdPrint(( "\n\tExpected: " ));
        for( i = 0; i < SMB_SECURITY_SIGNATURE_LENGTH; i++ ) {
            KdPrint(( "%X ", ExpectedSignature[i] & 0xff ));
        }
        KdPrint(( "\n\tReceived: " ));
        for( i = 0; i < SMB_SECURITY_SIGNATURE_LENGTH; i++ ) {
            KdPrint(( "%X ", ActualSignature[i] & 0xff ));
        }
        KdPrint(( "\n\tLength %u, Expected Index Number %u\n", Length, ExpectedIndexNumber ));

        //
        // Dump out some of the errant SMB
        //
        i = 1;
        do {
            ULONG len = MIN( requestLength, Mdl->ByteCount );
            PBYTE p = MmGetSystemAddressForMdl( Mdl );
            PBYTE ep = (PBYTE)MmGetSystemAddressForMdl( Mdl ) + len;

            for( ; p < ep; p++, i++ ) {
                KdPrint(("%2.2x ", (*p) & 0xff ));
                if( !(i%32) ) {
                    KdPrint(( "\n" ));
                }
            }

            requestLength -= len;

        } while( requestLength != 0 && (Mdl = Mdl->Next) != NULL );

        KdPrint(( "\n" ));
    }

    IF_DEBUG( SECSIG ) {
        DbgPrint( "WorkContext: %p\n", WorkContext );
        DbgBreakPoint();
    }

#endif
}

BOOLEAN SRVFASTCALL
SrvCheckSmbSecuritySignature(
    IN OUT PWORK_CONTEXT WorkContext
    )
{
    MD5_CTX Context;
    PMDL Mdl = WorkContext->RequestBuffer->Mdl;
    ULONG requestLength = WorkContext->RequestBuffer->DataLength;
    CHAR SavedSignature[ SMB_SECURITY_SIGNATURE_LENGTH ];
    PSMB_HEADER Smb = MmGetSystemAddressForMdl( Mdl );
    ULONG len;

    //
    // Initialize the Context
    //
    RtlCopyMemory( &Context, &WorkContext->Connection->Md5Context, sizeof( Context ) );

    //
    // Save the signature that's presently in the SMB
    //
    RtlCopyMemory( SavedSignature, Smb->SecuritySignature, sizeof( SavedSignature ));

    //
    // Put the correct (expected) signature index into the buffer
    //
    SmbPutUlong( Smb->SecuritySignature, WorkContext->SmbSecuritySignatureIndex );
    RtlZeroMemory(  Smb->SecuritySignature + sizeof(ULONG),
                    SMB_SECURITY_SIGNATURE_LENGTH-sizeof(ULONG)
                 );

    //
    // Compute what the signature should be
    //
    do {

        len = MIN( requestLength, Mdl->ByteCount );

        MD5Update( &Context, MmGetSystemAddressForMdl( Mdl ), len );

        requestLength -= len;

    } while( requestLength != 0 && (Mdl = Mdl->Next) != NULL );

    MD5Final( &Context );

    //
    // Put the signature back
    //
    RtlCopyMemory( Smb->SecuritySignature, SavedSignature, sizeof( Smb->SecuritySignature ));

    //
    // Now compare them!
    //
    if( RtlCompareMemory( Context.digest, SavedSignature, sizeof( SavedSignature ) ) !=
        sizeof( SavedSignature ) ) {

        SrvDumpSignatureError(  WorkContext,
                                Context.digest,
                                SavedSignature,
                                WorkContext->RequestBuffer->DataLength,
                                WorkContext->SmbSecuritySignatureIndex
                              );
        return FALSE;

    }

    return TRUE;
}