/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    nlp.c

Abstract:

    This file is the contains private routines which support
    for the LAN Manager portions of the MSV1_0 authentication package.

Author:

    Cliff Van Dyke 29-Apr-1991

Revision History:
   Chandana Surlu         21-Jul-96      Stolen from \\kernel\razzle3\src\security\msv1_0\nlp.c

--*/

#include <global.h>

#include "msp.h"
#include "nlp.h"
#include "nlpcache.h"

#include "msvwow.h"

DWORD
NlpCopyDomainRelativeSid(
    OUT PSID TargetSid,
    IN PSID  DomainId,
    IN ULONG RelativeId
    );

VOID
NlpPutString(
    IN PUNICODE_STRING OutString,
    IN PUNICODE_STRING InString,
    IN PUCHAR *Where
    )

/*++

Routine Description:

    This routine copies the InString string to the memory pointed to by
    the Where parameter, and fixes the OutString string to point to that
    new copy.

Parameters:

    OutString - A pointer to a destination NT string

    InString - A pointer to an NT string to be copied

    Where - A pointer to space to put the actual string for the
        OutString.  The pointer is adjusted to point to the first byte
        following the copied string.

Return Values:

    None.

--*/

{
    ASSERT( OutString != NULL );
    ASSERT( InString != NULL );
    ASSERT( Where != NULL && *Where != NULL);
    ASSERT( *Where == ROUND_UP_POINTER( *Where, sizeof(WCHAR) ) );
#ifdef notdef
    KdPrint(("NlpPutString: %ld %Z\n", InString->Length, InString ));
    KdPrint(("  InString: %lx %lx OutString: %lx Where: %lx\n", InString,
        InString->Buffer, OutString, *Where ));
#endif

    if ( InString->Length > 0 ) {

        OutString->Buffer = (PWCH) *Where;
        OutString->MaximumLength = (USHORT)(InString->Length + sizeof(WCHAR));

        RtlCopyUnicodeString( OutString, InString );

        *Where += InString->Length;
//        *((WCHAR *)(*Where)) = L'\0';
        *(*Where) = '\0';
        *(*Where + 1) = '\0';
        *Where += 2;

    } else {
        RtlInitUnicodeString(OutString, NULL);
    }
#ifdef notdef
    KdPrint(("  OutString: %ld %lx\n",  OutString->Length, OutString->Buffer));
#endif

    return;
}


VOID
NlpInitClientBuffer(
    OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
    IN PLSA_CLIENT_REQUEST ClientRequest
    )

/*++

Routine Description:

    This routine initializes a ClientBufferDescriptor to known values.
    This routine must be called before any of the other routines that use
    the ClientBufferDescriptor.

Parameters:

    ClientBufferDesc - Descriptor of a buffer allocated in the client's
        address space.

    ClientRequest - Is a pointer to an opaque data structure
        representing the client's request.

Return Values:

    None.

--*/

{

    //
    // Fill in a pointer to the ClientRequest and zero the rest.
    //

    ClientBufferDesc->ClientRequest = ClientRequest;
    ClientBufferDesc->UserBuffer = NULL;
    ClientBufferDesc->MsvBuffer = NULL;
    ClientBufferDesc->StringOffset = 0;
    ClientBufferDesc->TotalSize = 0;

}


NTSTATUS
NlpAllocateClientBuffer(
    IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
    IN ULONG FixedSize,
    IN ULONG TotalSize
    )

/*++

Routine Description:

    This routine allocates a buffer in the clients address space.
    It also allocates a mirror buffer in MSV's address space.

    The data will be constructed in the MSV's address space then 'flushed'
    into the client's address space.

Parameters:

    ClientBufferDesc - Descriptor of a buffer allocated in the client's
        address space.

    FixedSize - The size in bytes of the fixed portion of the buffer.

    TotalSize - The size in bytes of the entire buffer.

Return Values:

    Status of the operation.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    //
    // Allocate the Mirror buffer.
    //

    ASSERT( ClientBufferDesc->MsvBuffer == NULL );
    ClientBufferDesc->MsvBuffer = I_NtLmAllocate( TotalSize );

    if ( ClientBufferDesc->MsvBuffer == NULL ) {
        return STATUS_NO_MEMORY;
    }


    //
    // Allocate the client's buffer
    //

    ASSERT( ClientBufferDesc->UserBuffer == NULL );
    if ((ClientBufferDesc->ClientRequest == (PLSA_CLIENT_REQUEST) (-1)))
    {
         ClientBufferDesc->UserBuffer = (*(Lsa.AllocateLsaHeap))(TotalSize);
    }
    else
    {
        Status = (*Lsa.AllocateClientBuffer)(
                    ClientBufferDesc->ClientRequest,
                    TotalSize,
                    (PVOID *)&ClientBufferDesc->UserBuffer );
    }

    if ((ClientBufferDesc->ClientRequest == (PLSA_CLIENT_REQUEST) (-1)))
    {
        if (ClientBufferDesc->UserBuffer == NULL)
        {
            NlpFreeClientBuffer( ClientBufferDesc );
            return STATUS_NO_MEMORY;
        }
    }
    else
    {
        if ( !NT_SUCCESS( Status ) ) {
            ClientBufferDesc->UserBuffer = NULL;
            NlpFreeClientBuffer( ClientBufferDesc );
            return Status;
        }
    }

    //
    // Return
    //

    ClientBufferDesc->StringOffset = FixedSize;
    ClientBufferDesc->TotalSize = TotalSize;

    return STATUS_SUCCESS;

}


NTSTATUS
NlpFlushClientBuffer(
    IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
    OUT PVOID* UserBuffer
    )

/*++

Routine Description:

    Copy the Mirror Buffer into the Client's address space.

Parameters:

    ClientBufferDesc - Descriptor of a buffer allocated in the client's
        address space.

    UserBuffer - If successful, returns a pointer to the user's buffer.
        (The caller is now resposible for deallocating the buffer.)

Return Values:

    Status of the operation.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    //
    // Copy the data to the client's address space.
    //

    if ((ClientBufferDesc->ClientRequest == (PLSA_CLIENT_REQUEST) (-1)))
    {
        RtlCopyMemory(
                 ClientBufferDesc->UserBuffer,
                 ClientBufferDesc->MsvBuffer,
                 ClientBufferDesc->TotalSize);
    }
    else
    {
        Status = (*Lsa.CopyToClientBuffer)(
                ClientBufferDesc->ClientRequest,
                ClientBufferDesc->TotalSize,
                ClientBufferDesc->UserBuffer,
                ClientBufferDesc->MsvBuffer );
    }


    if ( !NT_SUCCESS( Status ) ) {
        return Status;
    }

    //
    // Mark that we're no longer responsible for the client's buffer.
    //

    *UserBuffer = (PVOID) ClientBufferDesc->UserBuffer;
    ClientBufferDesc->UserBuffer = NULL;

    //
    // Free the mirror buffer
    //

    NlpFreeClientBuffer( ClientBufferDesc );


    return STATUS_SUCCESS;

}


VOID
NlpFreeClientBuffer(
    IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc
    )

/*++

Routine Description:

    Free any Mirror Buffer or Client buffer.

Parameters:

    ClientBufferDesc - Descriptor of a buffer allocated in the client's
        address space.

Return Values:

    None

--*/

{

    //
    // Free the mirror buffer.
    //

    if ( ClientBufferDesc->MsvBuffer != NULL ) {
        I_NtLmFree( ClientBufferDesc->MsvBuffer );
        ClientBufferDesc->MsvBuffer = NULL;
    }

    //
    // Free the Client's buffer
    //

    if ((ClientBufferDesc->ClientRequest == (PLSA_CLIENT_REQUEST) (-1)))
    {
        if ( ClientBufferDesc->UserBuffer != NULL ) {
            (*Lsa.FreeLsaHeap)(ClientBufferDesc->UserBuffer);
            ClientBufferDesc->UserBuffer = NULL;
        }
    }
    else
    {
        if ( ClientBufferDesc->UserBuffer != NULL ) {
            (VOID) (*Lsa.FreeClientBuffer)( ClientBufferDesc->ClientRequest,
                                            ClientBufferDesc->UserBuffer );
            ClientBufferDesc->UserBuffer = NULL;
        }
    }

}


VOID
NlpPutClientString(
    IN OUT PCLIENT_BUFFER_DESC ClientBufferDesc,
    IN PUNICODE_STRING OutString,
    IN PUNICODE_STRING InString
    )

/*++

Routine Description:

    This routine copies the InString string to the memory pointed to by
    ClientBufferDesc->StringOffset, and fixes the OutString string to point
    to that new copy.


Parameters:

    ClientBufferDesc - Descriptor of a buffer allocated in the client's
        address space.

    InString - A pointer to an NT string to be copied

    OutString - A pointer to a destination NT string.  This string structure
        is in the "Mirror" allocated buffer.

Return Status:

    STATUS_SUCCESS - Indicates the service completed successfully.

--*/

{

    //
    // Ensure our caller passed good data.
    //

    ASSERT( OutString != NULL );
    ASSERT( InString != NULL );
    ASSERT( COUNT_IS_ALIGNED( ClientBufferDesc->StringOffset, sizeof(WCHAR)) );
    ASSERT( (LPBYTE)OutString >= ClientBufferDesc->MsvBuffer );
    ASSERT( (LPBYTE)OutString <
            ClientBufferDesc->MsvBuffer + ClientBufferDesc->TotalSize - sizeof(UNICODE_STRING) );

    ASSERT( ClientBufferDesc->StringOffset + InString->Length + sizeof(WCHAR) <=
            ClientBufferDesc->TotalSize );

#ifdef notdef
    KdPrint(("NlpPutClientString: %ld %Z\n", InString->Length, InString ));
    KdPrint(("  Orig: UserBuffer: %lx Offset: 0x%lx TotalSize: 0x%lx\n",
                ClientBufferDesc->UserBuffer,
                ClientBufferDesc->StringOffset,
                ClientBufferDesc->TotalSize ));
#endif

    //
    // Build a string structure and copy the text to the Mirror buffer.
    //

    if ( InString->Length > 0 ) {

        //
        // Copy the string (Add a zero character)
        //

        RtlCopyMemory(
            ClientBufferDesc->MsvBuffer + ClientBufferDesc->StringOffset,
            InString->Buffer,
            InString->Length );

        // Do one byte at a time since some callers don't pass in an even
        // InString->Length
        *(ClientBufferDesc->MsvBuffer + ClientBufferDesc->StringOffset +
            InString->Length) = '\0';
        *(ClientBufferDesc->MsvBuffer + ClientBufferDesc->StringOffset +
            InString->Length+1) = '\0';

        //
        // Build the string structure to point to the data in the client's
        // address space.
        //

        OutString->Buffer = (PWSTR)(ClientBufferDesc->UserBuffer +
                            ClientBufferDesc->StringOffset);
        OutString->Length = InString->Length;
        OutString->MaximumLength = OutString->Length + sizeof(WCHAR);

        //
        // Adjust the offset to past the newly copied string.
        //

        ClientBufferDesc->StringOffset += OutString->MaximumLength;

    } else {
        RtlInitUnicodeString(OutString, NULL);
    }

#ifdef notdef
    KdPrint(("  New: Offset: 0x%lx StringStart: %lx\n",
                ClientBufferDesc->StringOffset,
                OutString->Buffer ));
#endif

    return;

}


VOID
NlpMakeRelativeString(
    IN PUCHAR BaseAddress,
    IN OUT PUNICODE_STRING String
    )

/*++

Routine Description:

    This routine converts the buffer address in the specified string to
    be a byte offset from BaseAddress.

Parameters:

    BaseAddress - A pointer to make the destination address relative to.

    String - A pointer to a NT string to make relative.

Return Values:

    None.

--*/

{
    ASSERT( BaseAddress != NULL );
    ASSERT( String != NULL );
    ASSERT( sizeof(ULONG_PTR) == sizeof(String->Buffer) );

    if ( String->Buffer != NULL ) {
        *((PULONG_PTR)(&String->Buffer)) =
            (ULONG_PTR)((PUCHAR)String->Buffer - (PUCHAR)BaseAddress);
    }

    return;
}


VOID
NlpRelativeToAbsolute(
    IN PVOID BaseAddress,
    IN OUT PULONG_PTR RelativeValue
    )

/*++

Routine Description:

    This routine converts the byte offset from BaseAddress to be an
    absolute address.

Parameters:

    BaseAddress - A pointer the destination address is relative to.

    RelativeValue - A pointer to a relative value to make absolute.

Return Values:

    None.

--*/

{
    ASSERT( BaseAddress != NULL );
    ASSERT( RelativeValue != NULL );

    if ( *((PUCHAR *)RelativeValue) != NULL ) {
        *RelativeValue = (ULONG_PTR)((PUCHAR)BaseAddress + (*RelativeValue));
    }

    return;
}


BOOLEAN
NlpFindActiveLogon(
    IN PLUID LogonId,
    OUT PACTIVE_LOGON **ActiveLogon
    )

/*++

Routine Description:

    This routine finds the specified Logon Id in the ActiveLogon table.
    It returns a boolean indicating whether the Logon Id exists in the
    ActiveLogon Table.  If so, this routine also returns a pointer to a
    pointer to the appropriate entry in the table.  If not, this routine
    returns a pointer to where such an entry would be inserted in the table.

    This routine must be called with the NlpActiveLogonLock locked.

Parameters:

    LogonId - The LogonId of the logon to find in the table.

    ActiveLogon - If the specified logon Id exists, returns a pointer to a
        pointer to the appropriate entry in the table.  Otherwise,
        returns a pointer to where such an entry would be inserted in the
        table.

Return Values:

    TRUE - The specified LogonId already exists in the table.

    FALSE - The specified LogonId does not exist in the table.

--*/

{
    PACTIVE_LOGON *Logon;

    //
    // Loop through the table looking for this particular LogonId.
    //

    for( Logon = &NlpActiveLogons; *Logon != NULL; Logon = &((*Logon)->Next) ) {
        if (RtlCompareMemory( &(*Logon)->LogonId, LogonId, sizeof(*LogonId))
                == sizeof(*LogonId) ) {

            *ActiveLogon = Logon;
            return TRUE;
        }
    }

    //
    // By returning a pointer to the NULL at the end of the list, we
    //  are forcing new entries to be placed at the end.  The list is
    //  thereby maintained in the order that the logon occurred.
    //  MsV1_0EnumerateUsers relies on this behavior.
    //

    *ActiveLogon = Logon;
    return FALSE;
}


ULONG
NlpCountActiveLogon(
    IN PUNICODE_STRING LogonDomainName,
    IN PUNICODE_STRING UserName
    )

/*++

Routine Description:

    This routine counts the number of time a particular user is logged on
    in the Active Logon Table.

Parameters:

    LogonDomainName - Domain in which this user account is defined.

    UserName - The user name to count the active logons for.

Return Values:

    The count of active logons for the specified user.

--*/

{
    PACTIVE_LOGON Logon;
    ULONG LogonCount = 0;


    //
    // Loop through the table looking for this particular LogonId.
    //

    NlpLockActiveLogonsRead();

    for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next ) {

        if(RtlEqualUnicodeString( UserName, &Logon->UserName, (BOOLEAN) TRUE) &&
           RtlEqualDomainName(LogonDomainName,&Logon->LogonDomainName )){
            LogonCount ++;
        }

    }

    NlpUnlockActiveLogons();

    return LogonCount;
}




NTSTATUS
NlpAllocateInteractiveProfile (
    IN PLSA_CLIENT_REQUEST ClientRequest,
    OUT PMSV1_0_INTERACTIVE_PROFILE *ProfileBuffer,
    OUT PULONG ProfileBufferSize,
    IN  PNETLOGON_VALIDATION_SAM_INFO4 NlpUser
    )

/*++

Routine Description:

    This allocates and fills in the clients interactive profile.

Arguments:

    ClientRequest - Is a pointer to an opaque data structure
        representing the client's request.

    ProfileBuffer - Is used to return the address of the profile
        buffer in the client process.  This routine is
        responsible for allocating and returning the profile buffer
        within the client process.  However, if the caller subsequently
        encounters an error which prevents a successful logon, then
        then it will take care of deallocating the buffer.  This
        buffer is allocated with the AllocateClientBuffer() service.

     ProfileBufferSize - Receives the Size (in bytes) of the
        returned profile buffer.

    NlpUser - Contains the validation information which is
        to be copied in the ProfileBuffer.

Return Value:

    STATUS_SUCCESS - Indicates the service completed successfully.

    STATUS_QUOTA_EXCEEDED -  This error indicates that the logon
        could not be completed because the client does not have
        sufficient quota to allocate the return buffer.

--*/

{
    NTSTATUS Status;
    CLIENT_BUFFER_DESC ClientBufferDesc;
    PMSV1_0_INTERACTIVE_PROFILE LocalProfileBuffer;

#if _WIN64

    if( ClientRequest != (PLSA_CLIENT_REQUEST)( -1 ) )
    {
        SECPKG_CALL_INFO  CallInfo;

        //
        // if the call originated outproc, need to check if wow64.
        //

        if(!LsaFunctions->GetCallInfo(&CallInfo))
        {
            Status = STATUS_INTERNAL_ERROR;
            goto Cleanup;
        }

        if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT)
        {
            return MsvAllocateInteractiveWOWProfile (
                                    ClientRequest,
                                    ProfileBuffer,
                                    ProfileBufferSize,
                                    NlpUser
                                    );
        }
    }
#endif  // _WIN64


    //
    // Alocate the profile buffer to return to the client
    //

    NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );

    *ProfileBuffer = NULL;

    *ProfileBufferSize = sizeof(MSV1_0_INTERACTIVE_PROFILE) +
        NlpUser->LogonScript.Length + sizeof(WCHAR) +
        NlpUser->HomeDirectory.Length + sizeof(WCHAR) +
        NlpUser->HomeDirectoryDrive.Length + sizeof(WCHAR) +
        NlpUser->FullName.Length + sizeof(WCHAR) +
        NlpUser->ProfilePath.Length + sizeof(WCHAR) +
        NlpUser->LogonServer.Length + sizeof(WCHAR);

    Status = NlpAllocateClientBuffer( &ClientBufferDesc,
                                      sizeof(MSV1_0_INTERACTIVE_PROFILE),
                                      *ProfileBufferSize );


    if ( !NT_SUCCESS( Status ) ) {
        goto Cleanup;
    }

    LocalProfileBuffer = (PMSV1_0_INTERACTIVE_PROFILE) ClientBufferDesc.MsvBuffer;

    //
    // Copy the scalar fields into the profile buffer.
    //

    LocalProfileBuffer->MessageType = MsV1_0InteractiveProfile;
    LocalProfileBuffer->LogonCount = NlpUser->LogonCount;
    LocalProfileBuffer->BadPasswordCount= NlpUser->BadPasswordCount;
    OLD_TO_NEW_LARGE_INTEGER( NlpUser->LogonTime,
                              LocalProfileBuffer->LogonTime );
    OLD_TO_NEW_LARGE_INTEGER( NlpUser->LogoffTime,
                              LocalProfileBuffer->LogoffTime );
    OLD_TO_NEW_LARGE_INTEGER( NlpUser->KickOffTime,
                              LocalProfileBuffer->KickOffTime );
    OLD_TO_NEW_LARGE_INTEGER( NlpUser->PasswordLastSet,
                              LocalProfileBuffer->PasswordLastSet );
    OLD_TO_NEW_LARGE_INTEGER( NlpUser->PasswordCanChange,
                              LocalProfileBuffer->PasswordCanChange );
    OLD_TO_NEW_LARGE_INTEGER( NlpUser->PasswordMustChange,
                              LocalProfileBuffer->PasswordMustChange );
    LocalProfileBuffer->UserFlags = NlpUser->UserFlags;

    //
    // Copy the Unicode strings into the profile buffer.
    //


    NlpPutClientString( &ClientBufferDesc,
                        &LocalProfileBuffer->LogonScript,
                        &NlpUser->LogonScript );

    NlpPutClientString( &ClientBufferDesc,
                        &LocalProfileBuffer->HomeDirectory,
                        &NlpUser->HomeDirectory );

    NlpPutClientString( &ClientBufferDesc,
                        &LocalProfileBuffer->HomeDirectoryDrive,
                        &NlpUser->HomeDirectoryDrive );

    NlpPutClientString( &ClientBufferDesc,
                        &LocalProfileBuffer->FullName,
                        &NlpUser->FullName );

    NlpPutClientString( &ClientBufferDesc,
                        &LocalProfileBuffer->ProfilePath,
                        &NlpUser->ProfilePath );

    NlpPutClientString( &ClientBufferDesc,
                        &LocalProfileBuffer->LogonServer,
                        &NlpUser->LogonServer );


    //
    // Flush the buffer to the client's address space.
    //

    Status = NlpFlushClientBuffer( &ClientBufferDesc,
                                   (PVOID *) ProfileBuffer );

Cleanup:

    //
    // If the copy wasn't successful,
    //  cleanup resources we would have returned to the caller.
    //

    if ( !NT_SUCCESS(Status) ) {
        NlpFreeClientBuffer( &ClientBufferDesc );
    }

    return Status;

}




NTSTATUS
NlpAllocateNetworkProfile (
    IN PLSA_CLIENT_REQUEST ClientRequest,
    OUT PMSV1_0_LM20_LOGON_PROFILE *ProfileBuffer,
    OUT PULONG ProfileBufferSize,
    IN  PNETLOGON_VALIDATION_SAM_INFO4 NlpUser,
    IN  ULONG ParameterControl
    )

/*++

Routine Description:

    This allocates and fills in the clients network profile.

Arguments:

    ClientRequest - Is a pointer to an opaque data structure
        representing the client's request.

    ProfileBuffer - Is used to return the address of the profile
        buffer in the client process.  This routine is
        responsible for allocating and returning the profile buffer
        within the client process.  However, if the caller subsequently
        encounters an error which prevents a successful logon, then
        then it will take care of deallocating the buffer.  This
        buffer is allocated with the AllocateClientBuffer() service.

     ProfileBufferSize - Receives the Size (in bytes) of the
        returned profile buffer.

    NlpUser - Contains the validation information which is
        to be copied in the ProfileBuffer.  Will be NULL to indicate a
        NULL session.

Return Value:

    STATUS_SUCCESS - Indicates the service completed successfully.

    STATUS_QUOTA_EXCEEDED -  This error indicates that the logon
        could not be completed because the client does not have
        sufficient quota to allocate the return buffer.

--*/

{
    NTSTATUS Status;
    NTSTATUS SubAuthStatus = STATUS_SUCCESS;

    CLIENT_BUFFER_DESC ClientBufferDesc;
    PMSV1_0_LM20_LOGON_PROFILE LocalProfile;

#if _WIN64

    if( ClientRequest != (PLSA_CLIENT_REQUEST)( -1 ) )
    {
        SECPKG_CALL_INFO  CallInfo;

        //
        // if the call originated outproc, need to check if wow64.
        //

        if(!LsaFunctions->GetCallInfo(&CallInfo))
        {
            Status = STATUS_INTERNAL_ERROR;
            goto Cleanup;
        }

        if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT)
        {
            return MsvAllocateNetworkWOWProfile (
                                    ClientRequest,
                                    ProfileBuffer,
                                    ProfileBufferSize,
                                    NlpUser,
                                    ParameterControl
                                    );
        }
    }
#endif  // _WIN64


    //
    // Alocate the profile buffer to return to the client
    //

    NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );

    *ProfileBuffer = NULL;
    *ProfileBufferSize = sizeof(MSV1_0_LM20_LOGON_PROFILE);

    if ( NlpUser != NULL ) {
        *ProfileBufferSize += NlpUser->LogonDomainName.Length + sizeof(WCHAR) +
                              NlpUser->LogonServer.Length + sizeof(WCHAR) +
                              NlpUser->HomeDirectoryDrive.Length + sizeof(WCHAR);
    }


    Status = NlpAllocateClientBuffer( &ClientBufferDesc,
                                      sizeof(MSV1_0_LM20_LOGON_PROFILE),
                                      *ProfileBufferSize );


    if ( !NT_SUCCESS( Status ) ) {
        goto Cleanup;
    }

    LocalProfile = (PMSV1_0_LM20_LOGON_PROFILE) ClientBufferDesc.MsvBuffer;
    LocalProfile->MessageType = MsV1_0Lm20LogonProfile;


    //
    // For a NULL session, return a constant profile buffer
    //

    if ( NlpUser == NULL ) {

        LocalProfile->KickOffTime.HighPart = 0x7FFFFFFF;
        LocalProfile->KickOffTime.LowPart = 0xFFFFFFFF;
        LocalProfile->LogoffTime.HighPart = 0x7FFFFFFF;
        LocalProfile->LogoffTime.LowPart = 0xFFFFFFFF;
        LocalProfile->UserFlags = 0;
        RtlZeroMemory( LocalProfile->UserSessionKey,
                       sizeof(LocalProfile->UserSessionKey));
        RtlZeroMemory( LocalProfile->LanmanSessionKey,
                       sizeof(LocalProfile->LanmanSessionKey));
        RtlInitUnicodeString( &LocalProfile->LogonDomainName, NULL );
        RtlInitUnicodeString( &LocalProfile->LogonServer, NULL );
        RtlInitUnicodeString( &LocalProfile->UserParameters, NULL );


    //
    // For non-null sessions,
    //  fill in the profile buffer.
    //

    } else {

        //
        // Copy the individual scalar fields into the profile buffer.
        //

        if ((ParameterControl & MSV1_0_RETURN_PASSWORD_EXPIRY) != 0) {
            OLD_TO_NEW_LARGE_INTEGER( NlpUser->PasswordMustChange,
                                      LocalProfile->LogoffTime);
        } else {
            OLD_TO_NEW_LARGE_INTEGER( NlpUser->LogoffTime,
                                      LocalProfile->LogoffTime);
        }
        OLD_TO_NEW_LARGE_INTEGER( NlpUser->KickOffTime,
                                  LocalProfile->KickOffTime);
        LocalProfile->UserFlags = NlpUser->UserFlags;

        RtlCopyMemory( LocalProfile->UserSessionKey,
                       &NlpUser->UserSessionKey,
                       sizeof(LocalProfile->UserSessionKey) );

        ASSERT( SAMINFO_LM_SESSION_KEY_SIZE ==
                sizeof(LocalProfile->LanmanSessionKey) );
        RtlCopyMemory(
            LocalProfile->LanmanSessionKey,
            &NlpUser->ExpansionRoom[SAMINFO_LM_SESSION_KEY],
            SAMINFO_LM_SESSION_KEY_SIZE );


        // We need to extract the true status sent back for subauth users,
        // but not by a sub auth package

        SubAuthStatus = NlpUser->ExpansionRoom[SAMINFO_SUBAUTH_STATUS];

        //
        // Copy the Unicode strings into the profile buffer.
        //

        NlpPutClientString( &ClientBufferDesc,
                            &LocalProfile->LogonDomainName,
                            &NlpUser->LogonDomainName );

        NlpPutClientString( &ClientBufferDesc,
                            &LocalProfile->LogonServer,
                            &NlpUser->LogonServer );

        //
        // Kludge: Pass back UserParameters in HomeDirectoryDrive since we
        // can't change the NETLOGON_VALIDATION_SAM_INFO structure between
        // releases NT 1.0 and NT 1.0A. HomeDirectoryDrive was NULL for release 1.0A
        // so we'll use that field.
        //

        NlpPutClientString( &ClientBufferDesc,
                            &LocalProfile->UserParameters,
                            &NlpUser->HomeDirectoryDrive );

    }

    //
    // Flush the buffer to the client's address space.
    //

    Status = NlpFlushClientBuffer( &ClientBufferDesc,
                                   ProfileBuffer );

Cleanup:

    //
    // If the copy wasn't successful,
    //  cleanup resources we would have returned to the caller.
    //

    if ( !NT_SUCCESS(Status) ) {
        NlpFreeClientBuffer( &ClientBufferDesc );
    }

    // Save the status for subauth logons

    if (NT_SUCCESS(Status) && !NT_SUCCESS(SubAuthStatus))
    {
        Status = SubAuthStatus;
    }

    return Status;

}


PSID
NlpMakeDomainRelativeSid(
    IN PSID DomainId,
    IN ULONG RelativeId
    )

/*++

Routine Description:

    Given a domain Id and a relative ID create the corresponding SID allocated
    from the LSA heap.

Arguments:

    DomainId - The template SID to use.

    RelativeId - The relative Id to append to the DomainId.

Return Value:

    Sid - Returns a pointer to a buffer allocated from the LsaHeap
            containing the resultant Sid.

--*/
{
    UCHAR DomainIdSubAuthorityCount;
    ULONG Size;
    PSID Sid;

    //
    // Allocate a Sid which has one more sub-authority than the domain ID.
    //

    DomainIdSubAuthorityCount = *(RtlSubAuthorityCountSid( DomainId ));
    Size = RtlLengthRequiredSid(DomainIdSubAuthorityCount+1);

    if ((Sid = (*Lsa.AllocateLsaHeap)( Size )) == NULL ) {
        return NULL;
    }

    //
    // Initialize the new SID to have the same inital value as the
    // domain ID.
    //

    if ( !NT_SUCCESS( RtlCopySid( Size, Sid, DomainId ) ) ) {
        (*Lsa.FreeLsaHeap)( Sid );
        return NULL;
    }

    //
    // Adjust the sub-authority count and
    //  add the relative Id unique to the newly allocated SID
    //

    (*(RtlSubAuthorityCountSid( Sid ))) ++;
    *RtlSubAuthoritySid( Sid, DomainIdSubAuthorityCount ) = RelativeId;


    return Sid;
}



PSID
NlpCopySid(
    IN  PSID * Sid
    )

/*++

Routine Description:

    Given a SID allocatees space for a new SID from the LSA heap and copies
    the original SID.

Arguments:

    Sid - The original SID.

Return Value:

    Sid - Returns a pointer to a buffer allocated from the LsaHeap
            containing the resultant Sid.

--*/
{
    PSID NewSid;
    ULONG Size;

    Size = RtlLengthSid( Sid );



    if ((NewSid = (*Lsa.AllocateLsaHeap)( Size )) == NULL ) {
        return NULL;
    }


    if ( !NT_SUCCESS( RtlCopySid( Size, NewSid, Sid ) ) ) {
        (*Lsa.FreeLsaHeap)( NewSid );
        return NULL;
    }


    return NewSid;
}

//+-------------------------------------------------------------------------
//
//  Function:   NlpMakeTokenInformationV2
//
//  Synopsis:   This routine makes copies of all the pertinent
//              information from the UserInfo and generates a
//              LSA_TOKEN_INFORMATION_V2 data structure.
//
//  Effects:
//
//  Arguments:
//
//    UserInfo - Contains the validation information which is
//        to be copied into the TokenInformation.
//
//    TokenInformation - Returns a pointer to a properly Version 1 token
//        information structures.  The structure and individual fields are
//        allocated properly as described in ntlsa.h.
//
//  Requires:
//
//  Returns:    STATUS_SUCCESS - Indicates the service completed successfully.
//
//              STATUS_INSUFFICIENT_RESOURCES -  This error indicates that
//                      the logon could not be completed because the client
//                      does not have sufficient quota to allocate the return
//                      buffer.
//
//  Notes:      stolen back from from kerberos\client2\krbtoken.cxx.c:KerbMakeTokenInformationV1
//
//
//--------------------------------------------------------------------------


NTSTATUS
NlpMakeTokenInformationV2(
    IN  PNETLOGON_VALIDATION_SAM_INFO4 ValidationInfo,
    OUT PLSA_TOKEN_INFORMATION_V2 *TokenInformation
    )
{
    PNETLOGON_VALIDATION_SAM_INFO3 UserInfo = (PNETLOGON_VALIDATION_SAM_INFO3) ValidationInfo;
    NTSTATUS Status;
    PLSA_TOKEN_INFORMATION_V2 V2 = NULL;
    ULONG Size, i;
    DWORD NumGroups = 0;
    PBYTE CurrentSid = NULL;
    ULONG SidLength = 0;

    //
    // Allocate the structure itself
    //

    Size = (ULONG)sizeof(LSA_TOKEN_INFORMATION_V2);

    //
    // Allocate an array to hold the groups
    //

    Size += sizeof(TOKEN_GROUPS);


    // Add room for groups passed as RIDS
    NumGroups = UserInfo->GroupCount;
    if(UserInfo->GroupCount)
    {
        Size += UserInfo->GroupCount * (RtlLengthSid(UserInfo->LogonDomainId) + sizeof(ULONG));
    }

    //
    // If there are extra SIDs, add space for them
    //

    if (UserInfo->UserFlags & LOGON_EXTRA_SIDS) {
        ULONG i = 0;
        NumGroups += UserInfo->SidCount;

        // Add room for the sid's themselves
        for(i=0; i < UserInfo->SidCount; i++)
        {
            Size += RtlLengthSid(UserInfo->ExtraSids[i].Sid);
        }
    }

    //
    // If there are resource groups, add space for them
    //
    if (UserInfo->UserFlags & LOGON_RESOURCE_GROUPS) {

        NumGroups += UserInfo->ResourceGroupCount;

        if ((UserInfo->ResourceGroupCount != 0) &&
            ((UserInfo->ResourceGroupIds == NULL) ||
             (UserInfo->ResourceGroupDomainSid == NULL)))
        {
            Status = STATUS_INVALID_PARAMETER;
            goto Cleanup;
        }
        // Allocate space for the sids
        if(UserInfo->ResourceGroupCount)
        {
            Size += UserInfo->ResourceGroupCount * (RtlLengthSid(UserInfo->ResourceGroupDomainSid) + sizeof(ULONG));
        }

    }


    if( UserInfo->UserId )
    {
        // Size of the user sid and the primary group sid.
        Size += 2*(RtlLengthSid(UserInfo->LogonDomainId) + sizeof(ULONG));
    }
    else
    {
        if ( UserInfo->SidCount <= 0 ) {

            Status = STATUS_INSUFFICIENT_LOGON_INFO;
            goto Cleanup;
        }

        // Size of the primary group sid.
        Size += (RtlLengthSid(UserInfo->LogonDomainId) + sizeof(ULONG));
    }


    Size += (NumGroups - ANYSIZE_ARRAY)*sizeof(SID_AND_ATTRIBUTES);


    V2 = (PLSA_TOKEN_INFORMATION_V2) (*Lsa.AllocateLsaHeap)( Size );
    if ( V2 == NULL ) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(
        V2,
        Size
        );

    V2->Groups = (PTOKEN_GROUPS)(V2+1);
    V2->Groups->GroupCount = 0;
    CurrentSid = (PBYTE)&V2->Groups->Groups[NumGroups];

    OLD_TO_NEW_LARGE_INTEGER( UserInfo->KickOffTime, V2->ExpirationTime );



    //
    // If the UserId is non-zero, then it contians the users RID.
    //

    if ( UserInfo->UserId ) {
        V2->User.User.Sid = (PSID)CurrentSid;
        CurrentSid += NlpCopyDomainRelativeSid((PSID)CurrentSid, UserInfo->LogonDomainId, UserInfo->UserId);
    }

    //
    // Make a copy of the primary group (a required field).
    //
    V2->PrimaryGroup.PrimaryGroup = (PSID)CurrentSid;
    CurrentSid += NlpCopyDomainRelativeSid((PSID)CurrentSid, UserInfo->LogonDomainId, UserInfo->PrimaryGroupId );




    //
    // Copy over all the groups passed as RIDs
    //

    for ( i=0; i < UserInfo->GroupCount; i++ ) {

        V2->Groups->Groups[V2->Groups->GroupCount].Attributes = UserInfo->GroupIds[i].Attributes;

        V2->Groups->Groups[V2->Groups->GroupCount].Sid = (PSID)CurrentSid;
        CurrentSid += NlpCopyDomainRelativeSid((PSID)CurrentSid, UserInfo->LogonDomainId, UserInfo->GroupIds[i].RelativeId);

        V2->Groups->GroupCount++;
    }


    //
    // Add in the extra SIDs
    //

    if (UserInfo->UserFlags & LOGON_EXTRA_SIDS) {

        ULONG index = 0;
        //
        // If the user SID wasn't passed as a RID, it is the first
        // SID.
        //

        if ( !V2->User.User.Sid ) {
            V2->User.User.Sid = (PSID)CurrentSid;
            SidLength = RtlLengthSid(UserInfo->ExtraSids[index].Sid);
            RtlCopySid(SidLength, (PSID)CurrentSid, UserInfo->ExtraSids[index].Sid);

            CurrentSid += SidLength;
            index++;
        }

        //
        // Copy over all additional SIDs as groups.
        //

        for ( ; index < UserInfo->SidCount; index++ ) {

            V2->Groups->Groups[V2->Groups->GroupCount].Attributes =
                UserInfo->ExtraSids[index].Attributes;

            V2->Groups->Groups[V2->Groups->GroupCount].Sid= (PSID)CurrentSid;
            SidLength = RtlLengthSid(UserInfo->ExtraSids[index].Sid);
            RtlCopySid(SidLength, (PSID)CurrentSid, UserInfo->ExtraSids[index].Sid);

            CurrentSid += SidLength;

            V2->Groups->GroupCount++;
        }
    }

    //
    // Check to see if any resouce groups exist
    //

    if (UserInfo->UserFlags & LOGON_RESOURCE_GROUPS) {


        for ( i=0; i < UserInfo->ResourceGroupCount; i++ ) {

            V2->Groups->Groups[V2->Groups->GroupCount].Attributes = UserInfo->ResourceGroupIds[i].Attributes;

            V2->Groups->Groups[V2->Groups->GroupCount].Sid= (PSID)CurrentSid;
            CurrentSid += NlpCopyDomainRelativeSid((PSID)CurrentSid, UserInfo->ResourceGroupDomainSid, UserInfo->ResourceGroupIds[i].RelativeId);

            V2->Groups->GroupCount++;
        }
    }

    ASSERT( ((PBYTE)V2 + Size) == CurrentSid );


    if (!V2->User.User.Sid) {

        Status = STATUS_INSUFFICIENT_LOGON_INFO;
        goto Cleanup;
    }

    //
    // There are no default privileges supplied.
    // We don't have an explicit owner SID.
    // There is no default DACL.
    //

    V2->Privileges = NULL;
    V2->Owner.Owner = NULL;
    V2->DefaultDacl.DefaultDacl = NULL;

    //
    // Return the Validation Information to the caller.
    //

    *TokenInformation = V2;
    return STATUS_SUCCESS;

    //
    // Deallocate any memory we've allocated
    //

Cleanup:

    (*Lsa.FreeLsaHeap)( V2 );

    return Status;

}



VOID
NlpPutOwfsInPrimaryCredential(
    IN PUNICODE_STRING CleartextPassword,
    OUT PMSV1_0_PRIMARY_CREDENTIAL Credential
    )
/*++

Routine Description:

    This routine puts the OWFs for the specified clear password into
    the passed in Credential structure.

Arguments:

    CleartextPassword - Is a string containing the user's cleartext password.
        The password may be up to 255 characters long and contain any
        UNICODE value.

    Credential - A pointer to the credential to update.

Return Value:

    STATUS_SUCCESS - Indicates the service completed successfully.

    STATUS_QUOTA_EXCEEDED -  This error indicates that the logon
        could not be completed because the client does not have
        sufficient quota to allocate the return buffer.


--*/

{
    NTSTATUS Status;


    //
    // Compute the Ansi version to the Cleartext password.
    //
    //  The Ansi version of the Cleartext password is at most 14 bytes long,
    //      exists in a trailing zero filled 15 byte buffer,
    //      is uppercased.
    //

    Credential->LmPasswordPresent = FALSE;

    if( CleartextPassword->Length <= (LM20_PWLEN*sizeof(WCHAR)) )
    {
        CHAR LmPassword[LM20_PWLEN+1];
        STRING AnsiCleartextPassword;

        AnsiCleartextPassword.Buffer = LmPassword;
        AnsiCleartextPassword.Length = sizeof(LmPassword);
        AnsiCleartextPassword.MaximumLength = AnsiCleartextPassword.Length;

        Status = RtlUpcaseUnicodeStringToOemString(
                                      &AnsiCleartextPassword,
                                      CleartextPassword,
                                      (BOOLEAN) FALSE );

        if( NT_SUCCESS( Status ) )
        {
            //
            // Save the OWF encrypted versions of the passwords.
            //

            Status = RtlCalculateLmOwfPassword( LmPassword,
                                                &Credential->LmOwfPassword );

            ASSERT( NT_SUCCESS(Status) );

            Credential->LmPasswordPresent = TRUE;
        }

        //
        // Don't leave passwords around in the pagefile
        //

        RtlZeroMemory( LmPassword, sizeof(LmPassword) );
    }





    Status = RtlCalculateNtOwfPassword( CleartextPassword,
                                        &Credential->NtOwfPassword );

    ASSERT( NT_SUCCESS(Status) );

    Credential->NtPasswordPresent = TRUE;


    Status = RtlCalculateShaOwfPassword( CleartextPassword,
                                         &Credential->ShaOwfPassword );

    ASSERT( NT_SUCCESS(Status) );

    Credential->ShaPasswordPresent = TRUE;


    return;
}



NTSTATUS
NlpMakePrimaryCredential(
    IN  PUNICODE_STRING LogonDomainName,
    IN  PUNICODE_STRING UserName,
    IN PUNICODE_STRING CleartextPassword,
    OUT PMSV1_0_PRIMARY_CREDENTIAL *CredentialBuffer,
    OUT PULONG CredentialSize
    )


/*++

Routine Description:

    This routine makes a primary credential for the given user nam and
    password.

Arguments:

    LogonDomainName - Is a string representing the domain in which the user's
        account is defined.

    UserName - Is a string representing the user's account name.  The
        name may be up to 255 characters long.  The name is treated case
        insensitive.

    CleartextPassword - Is a string containing the user's cleartext password.
        The password may be up to 255 characters long and contain any
        UNICODE value.

    CredentialBuffer - Returns a pointer to the specified credential allocated
        on the LsaHeap.  It is the callers responsibility to deallocate
        this credential.

    CredentialSize - the size of the allocated credential buffer (in bytes).

Return Value:

    STATUS_SUCCESS - Indicates the service completed successfully.

    STATUS_QUOTA_EXCEEDED -  This error indicates that the logon
        could not be completed because the client does not have
        sufficient quota to allocate the return buffer.


--*/

{
    PMSV1_0_PRIMARY_CREDENTIAL Credential;
    NTSTATUS Status;
    PUCHAR Where;
    ULONG PaddingLength;


    //
    // Build the credential
    //

    *CredentialSize = sizeof(MSV1_0_PRIMARY_CREDENTIAL) +
            LogonDomainName->Length + sizeof(WCHAR) +
            UserName->Length + sizeof(WCHAR);

    //
    // add padding for memory encryption interface.
    //

    PaddingLength = DESX_BLOCKLEN - (*CredentialSize % DESX_BLOCKLEN);
    if( PaddingLength == DESX_BLOCKLEN )
    {
        PaddingLength = 0;
    }

    *CredentialSize += PaddingLength;


    Credential = (*Lsa.AllocateLsaHeap)( *CredentialSize );

    if ( Credential == NULL ) {
        KdPrint(("MSV1_0: NlpMakePrimaryCredential: No memory %ld\n",
            *CredentialSize ));
        return STATUS_QUOTA_EXCEEDED;
    }


    //
    // Put the LogonDomainName into the Credential Buffer.
    //

    Where = (PUCHAR)(Credential + 1);

    NlpPutString( &Credential->LogonDomainName, LogonDomainName, &Where );


    //
    // Put the UserName into the Credential Buffer.
    //

    NlpPutString( &Credential->UserName, UserName, &Where );


    //
    // Put the OWF passwords into the newly allocated credential.
    //

    NlpPutOwfsInPrimaryCredential( CleartextPassword, Credential );


    //
    // Return the credential to the caller.
    //
    *CredentialBuffer = Credential;
    return STATUS_SUCCESS;
}


NTSTATUS
NlpMakePrimaryCredentialFromMsvCredential(
    IN  PUNICODE_STRING LogonDomainName,
    IN  PUNICODE_STRING UserName,
    IN  PMSV1_0_SUPPLEMENTAL_CREDENTIAL MsvCredential,
    OUT PMSV1_0_PRIMARY_CREDENTIAL *CredentialBuffer,
    OUT PULONG CredentialSize
    )


/*++

Routine Description:

    This routine makes a primary credential for the given user nam and
    password.

Arguments:

    LogonDomainName - Is a string representing the domain in which the user's
        account is defined.

    UserName - Is a string representing the user's account name.  The
        name may be up to 255 characters long.  The name is treated case
        insensitive.

    SupplementalCred - The credentials retrieved from the user's account on
        the domain controller.

    CredentialBuffer - Returns a pointer to the specified credential allocated
        on the LsaHeap.  It is the callers responsibility to deallocate
        this credential.

    CredentialSize - the size of the allocated credential buffer (in bytes).

Return Value:

    STATUS_SUCCESS - Indicates the service completed successfully.

    STATUS_QUOTA_EXCEEDED -  This error indicates that the logon
        could not be completed because the client does not have
        sufficient quota to allocate the return buffer.


--*/

{
    PMSV1_0_PRIMARY_CREDENTIAL Credential;
    NTSTATUS Status;
    PUCHAR Where;
    ULONG PaddingLength;


    //
    // Build the credential
    //

    *CredentialSize = sizeof(MSV1_0_PRIMARY_CREDENTIAL) +
            LogonDomainName->Length + sizeof(WCHAR) +
            UserName->Length + sizeof(WCHAR);

    //
    // add padding for memory encryption interface.
    //

    PaddingLength = DESX_BLOCKLEN - (*CredentialSize % DESX_BLOCKLEN);
    if( PaddingLength == DESX_BLOCKLEN )
    {
        PaddingLength = 0;
    }

    *CredentialSize += PaddingLength;


    Credential = (*Lsa.AllocateLsaHeap)( *CredentialSize );

    if ( Credential == NULL ) {
        KdPrint(("MSV1_0: NlpMakePrimaryCredential: No memory %ld\n",
            *CredentialSize ));
        return STATUS_QUOTA_EXCEEDED;
    }

    RtlZeroMemory(
        Credential,
        *CredentialSize
        );

    //
    // Put the LogonDomainName into the Credential Buffer.
    //

    Where = (PUCHAR)(Credential + 1);

    NlpPutString( &Credential->LogonDomainName, LogonDomainName, &Where );


    //
    // Put the UserName into the Credential Buffer.
    //

    NlpPutString( &Credential->UserName, UserName, &Where );



    //
    // Save the OWF encrypted versions of the passwords.
    //

    if (MsvCredential->Flags & MSV1_0_CRED_NT_PRESENT) {
        RtlCopyMemory(
            &Credential->NtOwfPassword,
            MsvCredential->NtPassword,
            MSV1_0_OWF_PASSWORD_LENGTH
            );
        Credential->NtPasswordPresent = TRUE;
    } else {
#if 0
        RtlCopyMemory(
            &Credential->NtOwfPassword,
            &NlpNullNtOwfPassword,
            MSV1_0_OWF_PASSWORD_LENGTH
            );
        Credential->NtPasswordPresent = TRUE;
#endif
        Credential->NtPasswordPresent = FALSE;
    }


    if (MsvCredential->Flags & MSV1_0_CRED_LM_PRESENT) {
        RtlCopyMemory(
            &Credential->LmOwfPassword,
            MsvCredential->LmPassword,
            MSV1_0_OWF_PASSWORD_LENGTH
            );
        Credential->LmPasswordPresent = TRUE;
    } else {
#if 0
        RtlCopyMemory(
            &Credential->LmOwfPassword,
            &NlpNullLmOwfPassword,
            MSV1_0_OWF_PASSWORD_LENGTH
            );
        Credential->LmPasswordPresent = TRUE;
#endif
        Credential->LmPasswordPresent = FALSE;

    }


    //
    // Return the credential to the caller.
    //
    *CredentialBuffer = Credential;
    return STATUS_SUCCESS;
}


NTSTATUS
NlpAddPrimaryCredential(
    IN PLUID LogonId,
    IN PMSV1_0_PRIMARY_CREDENTIAL Credential,
    IN ULONG CredentialSize
    )


/*++

Routine Description:

    This routine sets a primary credential for the given LogonId.

Arguments:

    LogonId - The LogonId of the LogonSession to set the Credentials
        for.

    Credential - Specifies a pointer to the credential.

Return Value:

    STATUS_SUCCESS - Indicates the service completed successfully.

    STATUS_QUOTA_EXCEEDED -  This error indicates that the logon
        could not be completed because the client does not have
        sufficient quota to allocate the return buffer.


--*/

{
    NTSTATUS Status;
    STRING CredentialString;
    STRING PrimaryKeyValue;

    //
    // Make all pointers in the credential relative.
    //

    NlpMakeRelativeString( (PUCHAR)Credential, &Credential->UserName );
    NlpMakeRelativeString( (PUCHAR)Credential, &Credential->LogonDomainName );

    //
    // Add the credential to the logon session.
    //

    RtlInitString( &PrimaryKeyValue, MSV1_0_PRIMARY_KEY );
    CredentialString.Buffer = (PCHAR) Credential;
    CredentialString.Length = (USHORT) CredentialSize;
    CredentialString.MaximumLength = CredentialString.Length;

    //
    // encrypt input credential.
    //

    (*Lsa.LsaProtectMemory)( CredentialString.Buffer, (ULONG)CredentialString.Length );

    Status = (*Lsa.AddCredential)(
                    LogonId,
                    MspAuthenticationPackageId,
                    &PrimaryKeyValue,
                    &CredentialString );

    if ( !NT_SUCCESS( Status ) ) {
        KdPrint(( "NlpAddPrimaryCredential: error from AddCredential %lX\n",
                  Status));
    }

    return Status;
}

NTSTATUS
NlpGetPrimaryCredentialByUserDomain(
    IN  PUNICODE_STRING LogonDomainName,
    IN  PUNICODE_STRING UserName,
    OUT PMSV1_0_PRIMARY_CREDENTIAL *CredentialBuffer,
    OUT PULONG CredentialSize OPTIONAL
    )
{
    PACTIVE_LOGON Logon;
    LUID LogonId;
    BOOLEAN Match = FALSE;

    //
    // Loop through the table looking for this particular LogonId.
    //

    NlpLockActiveLogonsRead();

    for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next ) {

        if(RtlEqualUnicodeString( UserName, &Logon->UserName, (BOOLEAN) TRUE) &&
           RtlEqualDomainName(LogonDomainName,&Logon->LogonDomainName ))
        {
            Match = TRUE;
            CopyMemory( &LogonId, &Logon->LogonId, sizeof(LogonId) );
            break;
        }

    }

    NlpUnlockActiveLogons();

    if( !Match )
        return STATUS_NO_SUCH_LOGON_SESSION;


    return NlpGetPrimaryCredential( &LogonId, CredentialBuffer, CredentialSize );

}


NTSTATUS
NlpGetPrimaryCredential(
    IN PLUID LogonId,
    OUT PMSV1_0_PRIMARY_CREDENTIAL *CredentialBuffer,
    OUT PULONG CredentialSize OPTIONAL
    )


/*++

Routine Description:

    This routine gets a primary credential for the given LogonId.

Arguments:

    LogonId - The LogonId of the LogonSession to retrieve the Credentials
        for.

    CredentialBuffer - Returns a pointer to the specified credential allocated
        on the LsaHeap.  It is the callers responsibility to deallocate
        this credential.

    CredentialSize - Optionally returns the size of the credential buffer.

Return Value:

    STATUS_SUCCESS - Indicates the service completed successfully.

    STATUS_QUOTA_EXCEEDED -  This error indicates that the logon
        could not be completed because the client does not have
        sufficient quota to allocate the return buffer.


--*/

{
    NTSTATUS Status;
    ULONG QueryContext = 0;
    ULONG PrimaryKeyLength;
    STRING PrimaryKeyValue;
    STRING CredentialString;
    PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;

    RtlInitString( &PrimaryKeyValue, MSV1_0_PRIMARY_KEY );

    Status = (*Lsa.GetCredentials)( LogonId,
                                    MspAuthenticationPackageId,
                                    &QueryContext,
                                    (BOOLEAN) FALSE,  // Just retrieve primary
                                    &PrimaryKeyValue,
                                    &PrimaryKeyLength,
                                    &CredentialString );

    if ( !NT_SUCCESS( Status ) ) {
        return Status;
    }

    //
    // Make all pointers in the credential absolute.
    //

    Credential = (PMSV1_0_PRIMARY_CREDENTIAL) CredentialString.Buffer;

    //
    // decrypt credential.
    //

    (*Lsa.LsaUnprotectMemory)( CredentialString.Buffer, (ULONG)CredentialString.Length );


    NlpRelativeToAbsolute( Credential,
                   (PULONG_PTR)&Credential->UserName.Buffer );
    NlpRelativeToAbsolute( Credential,
                   (PULONG_PTR)&Credential->LogonDomainName.Buffer );


    *CredentialBuffer = Credential;
    if ( CredentialSize != NULL ) {
        *CredentialSize = CredentialString.Length;
    }
    return STATUS_SUCCESS;
}


NTSTATUS
NlpDeletePrimaryCredential(
    IN PLUID LogonId
    )


/*++

Routine Description:

    This routine deletes the credential for the given LogonId.

Arguments:

    LogonId - The LogonId of the LogonSession to delete the Credentials for.

Return Value:

    STATUS_SUCCESS - Indicates the service completed successfully.

    STATUS_QUOTA_EXCEEDED -  This error indicates that the logon
        could not be completed because the client does not have
        sufficient quota to allocate the return buffer.


--*/

{
    NTSTATUS Status;
    STRING PrimaryKeyValue;

    RtlInitString( &PrimaryKeyValue, MSV1_0_PRIMARY_KEY );

    Status = (*Lsa.DeleteCredential)( LogonId,
                                    MspAuthenticationPackageId,
                                    &PrimaryKeyValue );

    return Status;

}


NTSTATUS
NlpChangePassword(
    IN PUNICODE_STRING DomainName,
    IN PUNICODE_STRING UserName,
    IN PUNICODE_STRING Password
    )

/*++

Routine Description:

    Change the password for the specified user in all currently stored
    credentials.

Arguments:

    DomainName - The Netbios name of the domain in which the account exists.

    UserName - The name of the account whose password is to be changed.

    Password - The new password.

Return Value:

    STATUS_SUCCESS - If the operation was successful.

--*/
{
    NTSTATUS Status = STATUS_NOT_FOUND;
    PACTIVE_LOGON Logon;

    MSV1_0_PRIMARY_CREDENTIAL TempCredential;

    LUID FastLogonIds[ 32 ];
    PLUID SlowLogonIds = NULL;
    ULONG AllocatedLogonIds;

    PLUID LogonIds;
    ULONG cLogonIds;
    UNICODE_STRING NetBiosLogonDomainName = {0};
    UNICODE_STRING DnsDomainName = {0};
    UNICODE_STRING* pNetBiosLogonDomainName = NULL;
    cLogonIds = 0;
    LogonIds = FastLogonIds;
    AllocatedLogonIds = sizeof(FastLogonIds) / sizeof(LUID);

    //
    // Compute the OWFs of the password.
    //

    NlpPutOwfsInPrimaryCredential( Password, &TempCredential );

    Status = LsaIGetNbAndDnsDomainNames(DomainName, &DnsDomainName, &NetBiosLogonDomainName);

    if (NT_SUCCESS(Status) && NetBiosLogonDomainName.Length != 0)
    {
        pNetBiosLogonDomainName = &NetBiosLogonDomainName;
    }
    else
    {
        pNetBiosLogonDomainName = DomainName;
    }

    //
    // Loop through the table looking for this particular UserName/DomainName.
    //

    NlpLockActiveLogonsRead();

    for (Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next ) {

        if (!RtlEqualUnicodeString( UserName, &Logon->UserName, (BOOLEAN) TRUE ))
        {
            continue;
        }

        if (!RtlEqualDomainName( pNetBiosLogonDomainName, &Logon->LogonDomainName ))
        {
            continue;
        }

        SspPrint((SSP_UPDATES, "NlpChangePassword matched LogonId=%lx.%lx\n",
                    Logon->LogonId.LowPart, Logon->LogonId.HighPart ));

        //
        // if we don't have space to store the new entry, allocate a new
        // buffer, copy the existing buffer, and keep going.
        //

        if( AllocatedLogonIds < (cLogonIds+1))
        {
            PLUID OldLogonIds = SlowLogonIds;

            AllocatedLogonIds *= 2;

            SlowLogonIds = I_NtLmAllocate( AllocatedLogonIds * sizeof(LUID) );
            if( SlowLogonIds == NULL )
            {
                break;
            }

            CopyMemory( SlowLogonIds, LogonIds, cLogonIds*sizeof(LUID) );

            LogonIds = SlowLogonIds;

            if( OldLogonIds != NULL )
            {
                I_NtLmFree( OldLogonIds );
            }
        }

        LogonIds[ cLogonIds ] = Logon->LogonId;

        cLogonIds++;
    }

    NlpUnlockActiveLogons();

    //
    // Pass the change back to the LSA. Note - this only changes it for the
    // last element in the list.
    //

    if (cLogonIds != 0)
    {
        SECPKG_PRIMARY_CRED PrimaryCredentials;
        ULONG Index;

        RtlZeroMemory(
            &PrimaryCredentials,
            sizeof(SECPKG_PRIMARY_CRED)
            );

        PrimaryCredentials.Password = *Password;
        PrimaryCredentials.Flags = PRIMARY_CRED_UPDATE | PRIMARY_CRED_CLEAR_PASSWORD;

        //
        // update each instance of the credential that matches.
        // Multiple logon session can legally reference the same creds,
        // eg: Terminal Services, RunAs, etc.
        //

        for ( Index = 0 ; Index < cLogonIds ; Index++ )
        {
            PrimaryCredentials.LogonId = LogonIds[ Index ];

            (VOID) LsaFunctions->UpdateCredentials(
                                &PrimaryCredentials,
                                NULL            // no supplemental credentials
                                );
        }

        Status = STATUS_SUCCESS;
    }
    else
    {
        Status = STATUS_NOT_FOUND;
    }

    //
    // Pass the new password on to the logon cache
    //

    NlpChangeCachePassword(
                DomainName,
                UserName,
                &TempCredential.LmOwfPassword,
                &TempCredential.NtOwfPassword
                );

    ZeroMemory( &TempCredential, sizeof(TempCredential) );

    if ( SlowLogonIds )
    {
        I_NtLmFree( SlowLogonIds );
    }

    if (NetBiosLogonDomainName.MaximumLength && NetBiosLogonDomainName.Buffer)
    {
        LsaIFreeHeap(NetBiosLogonDomainName.Buffer);
    }

    if (DnsDomainName.MaximumLength && DnsDomainName.Buffer)
    {
        LsaIFreeHeap(DnsDomainName.Buffer);
    }

    return Status;
}


NTSTATUS
NlpChangePasswordByLogonId(
    IN PLUID LogonId,
    IN PUNICODE_STRING Password
    )

/*++

Routine Description:

    Change the password for the specified user in all currently stored
    credentials.

Arguments:

    LogonId - Logon ID of user whose password changed.

    Password - New password.

Return Value:

    STATUS_SUCCESS - If the operation was successful.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    PACTIVE_LOGON Logon;
    ULONG LogonCount = 0;
    PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
    ULONG CredentialSize;

    MSV1_0_PRIMARY_CREDENTIAL TempCredential;

    //
    // Compute the OWFs of the password.
    //

    NlpPutOwfsInPrimaryCredential( Password, &TempCredential );

    //
    // Loop through the table looking for this particular UserName/DomainName.
    //

    // conservative: take the full write lock up front.
    NlpLockActiveLogonsWrite();

    for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next )
    {

        if(!RtlEqualLuid( LogonId, &Logon->LogonId) )
        {
            continue;
        }

        SspPrint((SSP_UPDATES, "NlpChangePasswordByLogonId LogonId=%lx.%lx\n",
                    LogonId->LowPart, LogonId->HighPart ));


        //
        // Get the current credential for this logonid.
        //

        Status = NlpGetPrimaryCredential( &Logon->LogonId,
                                          &Credential,
                                          &CredentialSize );

        if ( !NT_SUCCESS(Status) ) {
            break;
        }

        //
        // Delete it from the LSA.
        //

        Status = NlpDeletePrimaryCredential( &Logon->LogonId );

        if ( !NT_SUCCESS(Status) ) {
            (*Lsa.FreeLsaHeap)( Credential );
            break;
        }


        //
        // Change the passwords in it
        //

        Credential->LmOwfPassword = TempCredential.LmOwfPassword;
        Credential->NtOwfPassword = TempCredential.NtOwfPassword;
        Credential->ShaOwfPassword = TempCredential.ShaOwfPassword;
        Credential->LmPasswordPresent = TempCredential.LmPasswordPresent;
        Credential->NtPasswordPresent = TempCredential.NtPasswordPresent;
        Credential->ShaPasswordPresent = TempCredential.ShaPasswordPresent;

        //
        // Add it back to the LSA.
        //

        Status = NlpAddPrimaryCredential( &Logon->LogonId,
                                          Credential,
                                          CredentialSize );

        (*Lsa.FreeLsaHeap)( Credential );

        if ( !NT_SUCCESS(Status) ) {
            break;
        }

        //
        // Pass the new password on to the logon cache
        //

        NlpChangeCachePassword(
                    &Logon->LogonDomainName,
                    &Logon->UserName,
                    &TempCredential.LmOwfPassword,
                    &TempCredential.NtOwfPassword );

        break;
    }

    NlpUnlockActiveLogons();

    ZeroMemory( &TempCredential, sizeof(TempCredential) );

    return Status;
}


VOID
NlpGetAccountNames(
    IN  PNETLOGON_LOGON_IDENTITY_INFO LogonInfo,
    IN  PNETLOGON_VALIDATION_SAM_INFO4 NlpUser,
    OUT PUNICODE_STRING SamAccountName,
    OUT PUNICODE_STRING NetbiosDomainName,
    OUT PUNICODE_STRING DnsDomainName,
    OUT PUNICODE_STRING Upn
    )

/*++

Routine Description:

    Get the sundry account names from the LogonInfo and NlpUser

Arguments:

    LogonInfo   - pointer to NETLOGON_INTERACTIVE_INFO structure which contains
                  the domain name, user name and password for this user. These
                  are what the user typed to WinLogon

    NlpUser - pointer to NETLOGON_VALIDATION_SAM_INFO4 structure which
                  contains this user's specific interactive logon information

    SamAccountName - Returns the SamAccountName of the logged on user.
        The returned buffer is within the LogonInfo or NlpUser.

    NetbiosDomainName - Returns the NetbiosDomainName of the logged on user.
        The returned buffer is within the LogonInfo or NlpUser.

    DnsDomainName - Returns the DnsDomainName of the logged on user.
        The returned buffer is within the LogonInfo or NlpUser.
        The returned length will be zero if DnsDomainName is not known.

    UPN - Returns the UPN of the logged on user.
        The returned buffer is within the LogonInfo or NlpUser.
        The returned length will be zero if UPN is not known.

Return Value:

    None.

--*/
{

    //
    // Return the SamAccountName and Netbios Domain Name
    //
    *SamAccountName = NlpUser->EffectiveName;
    *NetbiosDomainName = NlpUser->LogonDomainName;

    //
    // Return the DNS domain name.
    //

    *DnsDomainName = NlpUser->DnsLogonDomainName;

    //
    // Determine the UPN of the account
    //
    // If the UPN was returned from the DC,
    //  use it.
    // else
    //  use the UPN the caller passed in
    //
    // The caller passed in a UPN if all of the following are true:
    //  There is no domain name.
    //  The passed in user name isn't the one returned from the DC.
    //  The passed in user name has an @ in it.
    //
    //

    *Upn = NlpUser->Upn;

    if ( Upn->Length == 0 ) {

        if ( LogonInfo->LogonDomainName.Length == 0 &&
             !RtlEqualUnicodeString( &LogonInfo->UserName, &NlpUser->EffectiveName, (BOOLEAN) TRUE ) ) {

             ULONG i;

             for ( i=0; i<LogonInfo->UserName.Length/sizeof(WCHAR); i++) {

                 if ( LogonInfo->UserName.Buffer[i] == L'@') {
                     *Upn = LogonInfo->UserName;
                     break;
                 }
             }

        }
    }
}


//+-------------------------------------------------------------------------
//
//  Function:   NlpCopyDomainRelativeSid
//
//  Synopsis:   Given a domain Id and a relative ID create the corresponding
//              SID at the location indicated by TargetSid
//
//  Effects:
//
//  Arguments:  TargetSid - target memory location
//              DomainId - The template SID to use.
//
//                  RelativeId - The relative Id to append to the DomainId.
//
//  Requires:
//
//  Returns:    Size - Size of the sid copied
//
//  Notes:
//
//
//--------------------------------------------------------------------------

DWORD
NlpCopyDomainRelativeSid(
    OUT PSID TargetSid,
    IN PSID  DomainId,
    IN ULONG RelativeId
    )
{
    UCHAR DomainIdSubAuthorityCount;
    ULONG Size;

    //
    // Allocate a Sid which has one more sub-authority than the domain ID.
    //

    DomainIdSubAuthorityCount = *(RtlSubAuthorityCountSid( DomainId ));
    Size = RtlLengthRequiredSid(DomainIdSubAuthorityCount+1);

    //
    // Initialize the new SID to have the same inital value as the
    // domain ID.
    //

    if ( !NT_SUCCESS( RtlCopySid( Size, TargetSid, DomainId ) ) ) {
        return 0;
    }

    //
    // Adjust the sub-authority count and
    //  add the relative Id unique to the newly allocated SID
    //

    (*(RtlSubAuthorityCountSid( TargetSid ))) ++;
    *RtlSubAuthoritySid( TargetSid, DomainIdSubAuthorityCount ) = RelativeId;


    return Size;
}


//
// temporary home for this function.
//


NTSTATUS
RtlCalculateShaOwfPassword(
    IN PSHA_PASSWORD ShaPassword,
    OUT PSHA_OWF_PASSWORD ShaOwfPassword
    )

/*++

Routine Description:

    Takes the passed ShaPassword and performs a one-way-function on it.
    Uses the FIPS approved SHA-1 function

Arguments:

    ShaPassword - The password to perform the one-way-function on.

    ShaOwfPassword - The hashed password is returned here

Return Values:

    STATUS_SUCCESS - The function was completed successfully. The hashed
                     password is in ShaOwfPassword.
--*/

{
    A_SHA_CTX   SHA_Context;

    A_SHAInit(&SHA_Context);
    A_SHAUpdate(&SHA_Context, (PCHAR)ShaPassword->Buffer, ShaPassword->Length);
    A_SHAFinal(&SHA_Context, (PCHAR)ShaOwfPassword);


    return(STATUS_SUCCESS);
}

