//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1997.
//
//  File:       samhooks.cxx
//
//  Contents:   SAM Hooks for security packages
//
//  Classes:
//
//  Functions:
//
//  History:    3-10-97   RichardW   Created
//
//----------------------------------------------------------------------------


#include <lsapch.hxx>

#include <lmcons.h>
#include <ntsam.h>
#include <samrpc.h>
#include <samisrv.h>
#include <ntmsv1_0.h>
#include <pac.hxx>
#include "samhooks.hxx"




//+---------------------------------------------------------------------------
//
//  Function:   LsapMakeDomainRelativeSid
//
//  Synopsis:   Build a new SID based on a domain SID and a RID
//
//  Arguments:  [DomainId]   --
//              [RelativeId] --
//
//  History:    3-11-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
PSID
LsapMakeDomainRelativeSid(
    IN PSID DomainId,
    IN ULONG RelativeId
    )

{
    UCHAR DomainIdSubAuthorityCount;
    ULONG Size;
    PSID Sid;

    if ( !DomainId ) {

        return( NULL );
    }

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

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

    if ((Sid = LsapAllocateLsaHeap( 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 ) ) ) {
        LsapFreeLsaHeap( 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
LsapMakeDomainRelativeSid2(
    IN PSID DomainId,
    IN ULONG RelativeId
    )

{
    UCHAR DomainIdSubAuthorityCount;
    ULONG Size;
    PSID Sid;

    if ( !DomainId ) {

        return( NULL );
    }

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

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

    if ((Sid = LsapAllocatePrivateHeap( 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 ) ) ) {
        LsapFreePrivateHeap( 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;
}

//+-------------------------------------------------------------------------
//
//  Function:   LsapDuplicateSid
//
//  Synopsis:   Duplicates a SID
//
//  Effects:    allocates memory with LsaFunctions.AllocateLsaHeap
//
//  Arguments:  DestinationSid - Receives a copy of the SourceSid
//              SourceSid - SID to copy
//
//  Requires:
//
//  Returns:    STATUS_SUCCESS - the copy succeeded
//              STATUS_INSUFFICIENT_RESOURCES - the call to allocate memory
//                  failed
//
//  Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
LsapDuplicateSid(
    OUT PSID * DestinationSid,
    IN PSID SourceSid
    )
{
    ULONG SidSize;

    DsysAssert(RtlValidSid(SourceSid));

    SidSize = RtlLengthSid(SourceSid);

    *DestinationSid = (PSID) LsapAllocateLsaHeap( SidSize );

    if (*DestinationSid == NULL)
    {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    RtlCopyMemory(
        *DestinationSid,
        SourceSid,
        SidSize
        );

    return(STATUS_SUCCESS);
}

NTSTATUS
LsapDuplicateSid2(
    OUT PSID * DestinationSid,
    IN PSID SourceSid
    )
{
    ULONG SidSize;

    DsysAssert(RtlValidSid(SourceSid));

    SidSize = RtlLengthSid(SourceSid);

    *DestinationSid = (PSID) LsapAllocatePrivateHeap( SidSize );

    if (*DestinationSid == NULL)
    {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    RtlCopyMemory(
        *DestinationSid,
        SourceSid,
        SidSize
        );

    return(STATUS_SUCCESS);
}

//+---------------------------------------------------------------------------
//
//  Function:   LsapCaptureSamInfo
//
//  Synopsis:   Capture current SAM info for building a PAC
//
//  Arguments:  [DomainSid]   -- Returns domain SID
//              [DomainName]  -- returns domain name
//              [MachineName] -- returns current machine name
//
//  History:    3-14-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
LsapCaptureSamInfo(
    PSID *  DomainSid,
    PUNICODE_STRING DomainName,
    PUNICODE_STRING MachineName
    )
{
    NTSTATUS Status;
    PLSAPR_POLICY_INFORMATION PolicyInformation = NULL;
    UNICODE_STRING String;
    WCHAR LocalMachineName[ CNLEN + 1 ];
    DWORD Size ;
    PSID Sid ;

    Size = CNLEN + 1;

    if ( GetComputerName( LocalMachineName, &Size ) )
    {
        RtlInitUnicodeString( &String, LocalMachineName );

        Status = LsapDuplicateString( MachineName, &String );
    }
    else
    {
        MachineName->Buffer = (PWSTR) LsapAllocateLsaHeap( Size *
                                            sizeof(WCHAR) + 2 );

        if ( MachineName->Buffer )
        {
            MachineName->MaximumLength = (USHORT) (Size * sizeof(WCHAR) + 2);

            GetComputerName( MachineName->Buffer, &Size );

            MachineName->Length = (USHORT) (Size * sizeof( WCHAR ) );;

            Status = STATUS_SUCCESS ;
        }
        else
        {
            Status = STATUS_NO_MEMORY ;
        }
    }

    if ( !NT_SUCCESS( Status ) )
    {
        DebugLog(( DEB_ERROR, "Failed to get computer name, %x\n", Status ));
        goto Cleanup;
    }

    Status = LsarQueryInformationPolicy(
                LsapPolicyHandle,
                PolicyAccountDomainInformation,
                &PolicyInformation
                );

    if (!NT_SUCCESS(Status))
    {
        DebugLog((DEB_ERROR,"Failed to query information policy: 0x%x\n",Status));
        goto Cleanup;
    }

    Status = LsapDuplicateString(
                DomainName,
                (PUNICODE_STRING) &PolicyInformation->PolicyAccountDomainInfo.DomainName
                );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    Sid = (PSID) LocalAlloc(0, RtlLengthSid(
                        PolicyInformation->PolicyAccountDomainInfo.DomainSid)
                       );

    if ( Sid == NULL )
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }
    RtlCopyMemory(
        Sid,
        PolicyInformation->PolicyAccountDomainInfo.DomainSid,
        RtlLengthSid(PolicyInformation->PolicyAccountDomainInfo.DomainSid)
        );

    *DomainSid = Sid ;

Cleanup:
    if (PolicyInformation != NULL)
    {
        LsaIFree_LSAPR_POLICY_INFORMATION(
                PolicyPrimaryDomainInformation,
                PolicyInformation
                );
    }

    return( Status );

}


//+---------------------------------------------------------------------------
//
//  Function:   LsaOpenSamUser
//
//  Synopsis:   Opens a handle to the SAM user as specified by Name and NameType
//
//  Arguments:  [Name]       -- Name of user to find
//              [NameType]   -- SAM or AlternateId
//              [Prefix]     -- Prefix for AlternateId lookup
//              [AllowGuest] -- Open guest if user not found
//              [Reserved]   --
//              [UserHandle] -- Returned user handle
//
//  History:    3-14-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
NTAPI
LsaOpenSamUser(
    PSECURITY_STRING Name,
    SECPKG_NAME_TYPE NameType,
    PSECURITY_STRING Prefix,
    BOOLEAN AllowGuest,
    ULONG Reserved,
    PVOID * UserHandle
    )
{
    NTSTATUS Status = STATUS_NOT_IMPLEMENTED ;
    SECURITY_STRING CombinedName ;
    PSECURITY_STRING EffectiveName ;
    SAMPR_ULONG_ARRAY RelativeIdArray;
    SAMPR_ULONG_ARRAY UseArray;
    UNICODE_STRING TempString;

    RelativeIdArray.Element = NULL;
    UseArray.Element = NULL;



    if ( NameType == SecNameAlternateId )
    {
        if ( !Prefix )
        {
            return STATUS_INVALID_PARAMETER ;
        }

        CombinedName.MaximumLength = Name->Length + Prefix->Length +
                                        2 * sizeof( WCHAR );

        CombinedName.Length = CombinedName.MaximumLength - sizeof( WCHAR );

        CombinedName.Buffer = (PWSTR) LsapAllocateLsaHeap( CombinedName.MaximumLength );


        if ( CombinedName.Buffer )
        {
            CopyMemory( CombinedName.Buffer, Prefix->Buffer, Prefix->Length );

            CombinedName.Buffer[ Prefix->Length / sizeof( WCHAR ) ] = L':';

            CopyMemory( &CombinedName.Buffer[ Prefix->Length / sizeof( WCHAR ) + 1],
                        Name->Buffer,
                        Name->Length + sizeof( WCHAR ) );

            EffectiveName = &CombinedName ;

        }
        else
        {
            return SEC_E_INSUFFICIENT_MEMORY ;
        }

    }
    else
    {
        EffectiveName = Name ;
    }

    if ( NameType == SecNameSamCompatible )
    {
        Status = SamrLookupNamesInDomain(
                    LsapAccountDomainHandle,
                    1,
                    (PRPC_UNICODE_STRING) Name,
                    &RelativeIdArray,
                    &UseArray
                    );

        if (!NT_SUCCESS(Status))
        {
            DebugLog((DEB_ERROR,"Failed to lookup name %wZ in domain: 0x%x\n",
                Name, Status));

            goto CheckGuest ;
        }

        if (UseArray.Element[0] != SidTypeUser)
        {
            Status = STATUS_NO_SUCH_USER;

            goto CheckGuest ;
        }


        Status = SamrOpenUser(
                    LsapAccountDomainHandle,
                    USER_ALL_ACCESS,
                    RelativeIdArray.Element[0],
                    UserHandle
                    );

        SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );
        SamIFree_SAMPR_ULONG_ARRAY( &UseArray );

        RelativeIdArray.Element = NULL;
        UseArray.Element = NULL;


        if (!NT_SUCCESS(Status))
        {
            DebugLog((DEB_ERROR,"Failed to open user by relative ID: 0x%x\n",Status));

            goto CheckGuest ;
        }

    }
    else if ( NameType == SecNameAlternateId )
    {

        Status = SamIOpenUserByAlternateId(
                        LsapAccountDomainHandle,
                        USER_ALL_ACCESS,
                        EffectiveName,
                        UserHandle );

        if ( !NT_SUCCESS( Status ) )
        {
            DebugLog(( DEB_TRACE_SAM, "Failed to find user by alternate id, %x\n", Status ));

            goto CheckGuest ;
        }
    }
    else
    {
        Status = STATUS_NOT_IMPLEMENTED ;

        AllowGuest = FALSE ;
    }

    if ( RelativeIdArray.Element )
    {
        SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );

        RelativeIdArray.Element = NULL;
    }
    if ( UseArray.Element )
    {
        SamIFree_SAMPR_ULONG_ARRAY( &UseArray );

        UseArray.Element = NULL;
    }

    if ( EffectiveName == &CombinedName )
    {
        LsapFreeLsaHeap( EffectiveName->Buffer );
    }

    return Status ;

CheckGuest:

    if ( RelativeIdArray.Element )
    {
        SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );

        RelativeIdArray.Element = NULL;
    }
    if ( UseArray.Element )
    {
        SamIFree_SAMPR_ULONG_ARRAY( &UseArray );

        UseArray.Element = NULL;
    }

    if ( AllowGuest )
    {
        Status = SamrOpenUser(
                        LsapAccountDomainHandle,
                        USER_ALL_ACCESS,
                        DOMAIN_USER_RID_GUEST,
                        UserHandle );
    }

    if ( EffectiveName == &CombinedName )
    {
        LsapFreeLsaHeap( EffectiveName->Buffer );
    }


    return Status ;
}

//+---------------------------------------------------------------------------
//
//  Function:   LsaCloseSamUser
//
//  Synopsis:   Close a SAM user opened by LsaOpenSamUser
//
//  Arguments:  [UserHandle] --
//
//  History:    3-14-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
NTAPI
LsaCloseSamUser(
    PVOID UserHandle
    )
{
     return SamrCloseHandle( &((SAMPR_HANDLE) UserHandle) );
}


//+---------------------------------------------------------------------------
//
//  Function:   LsaGetUserCredentials
//
//  Synopsis:   Pull the creds for the user
//
//  Arguments:  [UserHandle]            --
//              [PrimaryCreds]          --
//              [PrimaryCredsSize]      --
//              [SupplementalCreds]     --
//              [SupplementalCredsSize] --
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    3-14-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
NTAPI
LsaGetUserCredentials(
    PVOID UserHandle,
    PVOID * PrimaryCreds,
    PULONG PrimaryCredsSize,
    PVOID * SupplementalCreds,
    PULONG SupplementalCredsSize
    )
{
    return SEC_E_UNSUPPORTED_FUNCTION ;
}


NTSTATUS
NTAPI
LsaGetUserAuthData(
    PVOID UserHandle,
    PUCHAR * UserAuthData,
    PULONG UserAuthDataSize
    )
{
    PSAMPR_USER_ALL_INFORMATION UserAll = NULL ;
    PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL ;
    NTSTATUS Status ;
    PPACTYPE pNewPac = NULL ;
    PSAMPR_GET_GROUPS_BUFFER GroupsBuffer = NULL ;
    PPACTYPE Pac ;
    UNICODE_STRING Domain ;
    UNICODE_STRING Machine ;
    PSID Sid ;

    Sid = NULL ;
    Machine.Buffer = NULL ;
    Domain.Buffer = NULL ;

    *UserAuthData = NULL ;

    Status = SamrQueryInformationUser(
                    (SAMPR_HANDLE) UserHandle,
                    UserAllInformation,
                    &UserAllInfo );

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

    UserAll = &UserAllInfo->All ;

    if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED )
    {
        Status = STATUS_ACCOUNT_DISABLED ;

        goto GetPac_Cleanup;
    }

    Status = SamrGetGroupsForUser(
                    (SAMPR_HANDLE) UserHandle,
                    &GroupsBuffer );

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

    Status = LsapCaptureSamInfo( &Sid, &Domain, &Machine );

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

    Status = PAC_Init( UserAll,
                       GroupsBuffer,
                       NULL,            // no extra groups
                       Sid,
                       &Domain,
                       &Machine,
                       0,               // no signature
                       0,               // no additional data
                       NULL,            // no additional data
                       &Pac );


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

    *UserAuthDataSize = PAC_GetSize( Pac );

    *UserAuthData = (PUCHAR) LsapAllocateLsaHeap( *UserAuthDataSize );

    if ( *UserAuthData )
    {
       PAC_Marshal( Pac, *UserAuthDataSize, *UserAuthData );
    }
    else
    {
        Status = SEC_E_INSUFFICIENT_MEMORY ;
    }

    MIDL_user_free( Pac );

GetPac_Cleanup:

    if ( UserAllInfo )
    {
        SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation );
    }

    if ( GroupsBuffer )
    {
        SamIFree_SAMPR_GET_GROUPS_BUFFER( GroupsBuffer );
    }
    if ( Sid )
    {
        LsapFreeLsaHeap( Sid );
    }

    if ( Domain.Buffer )
    {
        LsapFreeLsaHeap( Domain.Buffer );
    }

    if ( Machine.Buffer )
    {
        LsapFreeLsaHeap( Machine.Buffer );
    }

    return( Status );

}

//+-------------------------------------------------------------------------
//
//  Function:   LsapMakeTokenInformationV1
//
//  Synopsis:   This routine makes copies of all the pertinent
//              information from the UserInfo and generates a
//              LSA_TOKEN_INFORMATION_V1 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 from kerberos\client2\krbtoken.cxx, where it was
//              stolen from msv1_0\nlp.c:NlpMakeTokenInformationV1
//
//
//--------------------------------------------------------------------------
NTSTATUS
LsapMakeTokenInformationV1(
    IN  PNETLOGON_VALIDATION_SAM_INFO3 UserInfo,
    OUT PLSA_TOKEN_INFORMATION_V1 *TokenInformation
    )
{
    NTSTATUS Status;
    PLSA_TOKEN_INFORMATION_V1 V1;
    ULONG Size, i;



    //
    // Allocate the structure itself
    //

    Size = (ULONG)sizeof(LSA_TOKEN_INFORMATION_V1);
    V1 = (PLSA_TOKEN_INFORMATION_V1) LsapAllocateLsaHeap( Size );
    if ( V1 == NULL ) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(
        V1,
        Size
        );

    V1->User.User.Sid = NULL;
    V1->Groups = NULL;
    V1->PrimaryGroup.PrimaryGroup = NULL;
    OLD_TO_NEW_LARGE_INTEGER( UserInfo->KickOffTime, V1->ExpirationTime );


    //
    // Make a copy of the user SID (a required field)
    //

    V1->User.User.Attributes = 0;

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

    Size = ( (ULONG)sizeof(TOKEN_GROUPS)
       + (UserInfo->GroupCount * (ULONG)sizeof(SID_AND_ATTRIBUTES))
       - (ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES))
           );

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

    if (UserInfo->UserFlags & LOGON_EXTRA_SIDS) {
        Size += UserInfo->SidCount * (ULONG)sizeof(SID_AND_ATTRIBUTES);
    }

    //
    // If there are resource groups, add space for them
    //
    if (UserInfo->UserFlags & LOGON_RESOURCE_GROUPS) {
        Size += UserInfo->ResourceGroupCount * (ULONG)sizeof(SID_AND_ATTRIBUTES);

        if ((UserInfo->ResourceGroupCount != 0) &&
            ((UserInfo->ResourceGroupIds == NULL) ||
             (UserInfo->ResourceGroupDomainSid == NULL)))
        {
            Status = STATUS_INVALID_PARAMETER;
            goto Cleanup;
        }
    }



    V1->Groups = (PTOKEN_GROUPS) LsapAllocatePrivateHeap( Size );

    if ( V1->Groups == NULL ) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }

    RtlZeroMemory(
        V1->Groups,
        Size
        );

    V1->Groups->GroupCount = 0;

    //
    // Start copying SIDs into the structure
    //



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

    if ( UserInfo->UserId ) {
        V1->User.User.Sid =
                LsapMakeDomainRelativeSid( UserInfo->LogonDomainId,
                                          UserInfo->UserId );

        if( V1->User.User.Sid == NULL ) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto Cleanup;
        }

    }

    //
    // Make a copy of the primary group (a required field).
    //


    V1->PrimaryGroup.PrimaryGroup = LsapMakeDomainRelativeSid(
                                            UserInfo->LogonDomainId,
                                            UserInfo->PrimaryGroupId );

    if ( V1->PrimaryGroup.PrimaryGroup == NULL ) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }





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

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

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

        V1->Groups->Groups[V1->Groups->GroupCount].Sid = LsapMakeDomainRelativeSid2(
                                         UserInfo->LogonDomainId,
                                         UserInfo->GroupIds[i].RelativeId );

        if( V1->Groups->Groups[V1->Groups->GroupCount].Sid == NULL ) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto Cleanup;
        }

        V1->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 ( !V1->User.User.Sid ) {
            if ( UserInfo->SidCount <= index ) {

                Status = STATUS_INSUFFICIENT_LOGON_INFO;
                goto Cleanup;
            }
            Status = LsapDuplicateSid(
                        &V1->User.User.Sid,
                        UserInfo->ExtraSids[index].Sid
                        );

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

            index++;
        }

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

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

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

            Status = LsapDuplicateSid2(
                        &V1->Groups->Groups[V1->Groups->GroupCount].Sid,
                        UserInfo->ExtraSids[index].Sid
                        );
            if (!NT_SUCCESS(Status) ) {
                goto Cleanup;
            }


            V1->Groups->GroupCount++;
        }
    }

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

    if (UserInfo->UserFlags & LOGON_RESOURCE_GROUPS) {


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

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

            V1->Groups->Groups[V1->Groups->GroupCount].Sid = LsapMakeDomainRelativeSid2(
                                             UserInfo->ResourceGroupDomainSid,
                                             UserInfo->ResourceGroupIds[i].RelativeId );

            if( V1->Groups->Groups[V1->Groups->GroupCount].Sid == NULL ) {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto Cleanup;
            }

            V1->Groups->GroupCount++;
        }
    }

    if (!V1->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.
    //

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

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

    *TokenInformation = V1;
    return STATUS_SUCCESS;

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

Cleanup:
    if ( V1->User.User.Sid != NULL ) {
        LsapFreeLsaHeap( V1->User.User.Sid );
    }

    if ( V1->Groups != NULL ) {
        LsapFreeTokenGroups( V1->Groups );
    }

    if ( V1->PrimaryGroup.PrimaryGroup != NULL ) {
        LsapFreeLsaHeap( V1->PrimaryGroup.PrimaryGroup );
    }

    LsapFreeLsaHeap( V1 );

    return Status;

}

//+---------------------------------------------------------------------------
//
//  Function:   LsaFreeTokenInfo
//
//  Synopsis:   Frees a TokenInformation structure that was allocated by
//              the LSA
//
//  Arguments:  [TokenInfoType]    --
//              [TokenInformation] --
//
//  History:    3-14-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
NTAPI
LsaFreeTokenInfo(
    LSA_TOKEN_INFORMATION_TYPE TokenInfoType,
    PVOID TokenInformation
    )
{

    switch (TokenInfoType) {

        case LsaTokenInformationNull:

            LsapFreeTokenInformationNull( (PLSA_TOKEN_INFORMATION_NULL) TokenInformation );
            break;

        case LsaTokenInformationV1:

            LsapFreeTokenInformationV1( (PLSA_TOKEN_INFORMATION_V1) TokenInformation );
            break;

        case LsaTokenInformationV2:

            LsapFreeTokenInformationV2( (PLSA_TOKEN_INFORMATION_V2) TokenInformation );
            break;

    }
    return STATUS_SUCCESS ;
}


//+---------------------------------------------------------------------------
//
//  Function:   LsaConvertAuthDataToToken
//
//  Synopsis:   Convert an opaque PAC structure into a token.
//
//  Arguments:  [UserAuthData]         --
//              [UserAuthDataSize]     --
//              [TokenInformation]     --
//              [TokenInformationType] --
//
//  History:    3-14-97   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
NTAPI
LsaConvertAuthDataToToken(
    IN PVOID UserAuthData,
    IN ULONG UserAuthDataSize,
    IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
    IN PTOKEN_SOURCE TokenSource,
    IN SECURITY_LOGON_TYPE LogonType,
    IN PUNICODE_STRING AuthorityName,
    OUT PHANDLE TokenHandle,
    OUT PLUID LogonId,
    OUT PUNICODE_STRING AccountName,
    OUT PNTSTATUS SubStatus
    )
{
    NTSTATUS Status ;
    PPACTYPE Pac = NULL ;
    PPAC_INFO_BUFFER LogonInfo = NULL ;
    PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo = NULL ;
    PLSA_TOKEN_INFORMATION_V1 TokenInfo = NULL ;


    LogonId->HighPart = LogonId->LowPart = 0;
    *TokenHandle = NULL;
    RtlInitUnicodeString(
        AccountName,
        NULL
        );
    *SubStatus = STATUS_SUCCESS;

    Pac = (PPACTYPE) UserAuthData ;

    if ( PAC_UnMarshal( Pac, UserAuthDataSize ) == 0 )
    {
        DebugLog(( DEB_ERROR, "Failed to unmarshall pac\n" ));

        Status = STATUS_INVALID_PARAMETER ;

        goto CreateToken_Cleanup ;
    }

    LogonInfo = PAC_Find( Pac, PAC_LOGON_INFO, NULL );

    if ( !LogonInfo )
    {
        DebugLog(( DEB_ERROR, "Failed to find logon info in pac\n" ));

        Status = STATUS_INVALID_PARAMETER ;

        goto CreateToken_Cleanup ;
    }


    Status = PAC_UnmarshallValidationInfo(
                &ValidationInfo,
                LogonInfo->Data,
                LogonInfo->cbBufferSize
                );

    if (!NT_SUCCESS(Status))
    {
        DebugLog((DEB_ERROR,"Failed to unmarshall validation info: 0x%x\n",
            Status));

        goto CreateToken_Cleanup;
    }

    //
    // Now we need to build a LSA_TOKEN_INFORMATION_V1 from the validation
    // information
    //

    Status = LsapMakeTokenInformationV1(
                ValidationInfo,
                &TokenInfo
                );

    if (!NT_SUCCESS(Status))
    {
        DebugLog((DEB_ERROR,"Failed to make token informatin v1: 0x%x\n",
            Status));
        goto CreateToken_Cleanup;
    }

    //
    // Now, copy the user name.
    //

    Status = LsapDuplicateString( AccountName, &ValidationInfo->EffectiveName );

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

    //
    // Now create a logon session
    //

    Status = LsapCreateLogonSession( LogonId );
    if (!NT_SUCCESS(Status))
    {
        goto CreateToken_Cleanup;
    }

    //
    // Now create the token
    //

    Status = LsapCreateToken(
                LogonId,
                TokenSource,
                LogonType,
                ImpersonationLevel,
                LsaTokenInformationV1,
                TokenInfo,
                NULL,                   // no token groups
                AccountName,
                AuthorityName,
                NULL,
                &ValidationInfo->ProfilePath,
                TokenHandle,
                SubStatus
                );

    //
    // NULL out the TokenInfo pointer.  LsapCreateToken will
    // free the memory under all conditions
    //

    TokenInfo = NULL ;

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

    //
    // We don't need to free the token information because CreateToken does
    // that for us.
    //


    MIDL_user_free(ValidationInfo);
    return Status ;

CreateToken_Cleanup:

    if ( TokenInfo )
    {
        LsaFreeTokenInfo( LsaTokenInformationV1, TokenInfo );
    }
    if ((LogonId->LowPart != 0) || (LogonId->HighPart != 0))
    {
        LsapDeleteLogonSession(LogonId);
    }

    LsapFreeString(
        AccountName
        );

    if (ValidationInfo != NULL)
    {
        MIDL_user_free(ValidationInfo);

    }
    return Status ;
}

//+---------------------------------------------------------------------------
//
//  Function:   LsaGetAuthDataForUser
//
//  Synopsis:   Helper function - retrieves all auth data for a user
//              based on Name, NameType, and prefix
//
//  Arguments:  [Name]             -- Name to search for
//              [NameType]         -- Type of name supplied
//              [Prefix]           -- String prefix for name
//              [UserAuthData]     --
//              [UserAuthDataSize] --
//
//  History:    6-08-98   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
LsaGetAuthDataForUser(
    PSECURITY_STRING Name,
    SECPKG_NAME_TYPE NameType,
    PSECURITY_STRING Prefix OPTIONAL,
    PUCHAR * UserAuthData,
    PULONG UserAuthDataSize,
    PUNICODE_STRING UserFlatName OPTIONAL
    )
{
    NTSTATUS Status ;
    ULONG SamFlags ;
    PUNICODE_STRING AccountName ;
    UNICODE_STRING CombinedName = { 0 };
    SID_AND_ATTRIBUTES_LIST ReverseMembership ;
    PSAMPR_USER_ALL_INFORMATION UserAll = NULL ;
    PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL ;
    PPACTYPE pNewPac = NULL ;
    PPACTYPE Pac ;
    UNICODE_STRING Domain ;
    UNICODE_STRING Machine ;
    PSID Sid ;

    Sid = NULL ;
    Machine.Buffer = NULL ;
    Domain.Buffer = NULL ;

    ReverseMembership.Count = 0 ;

    *UserAuthData = NULL ;

    if ( UserFlatName )
    {
        ZeroMemory( UserFlatName, sizeof( UNICODE_STRING ) );
    }


    SamFlags = 0 ;
    switch ( NameType )
    {
        case SecNameSamCompatible:
            AccountName = Name ;
            break;

        case SecNameAlternateId:
            SamFlags |= SAM_OPEN_BY_ALTERNATE_ID ;
            if ( !Prefix )
            {
                return STATUS_INVALID_PARAMETER ;
            }

            CombinedName.MaximumLength = Name->Length + Prefix->Length +
                                            2 * sizeof( WCHAR );

            CombinedName.Length = CombinedName.MaximumLength - sizeof( WCHAR );

            CombinedName.Buffer = (PWSTR) LsapAllocateLsaHeap( CombinedName.MaximumLength );


            if ( CombinedName.Buffer )
            {
                CopyMemory( CombinedName.Buffer, Prefix->Buffer, Prefix->Length );

                CombinedName.Buffer[ Prefix->Length / sizeof( WCHAR ) ] = L':';

                CopyMemory( &CombinedName.Buffer[ Prefix->Length / sizeof( WCHAR ) + 1],
                            Name->Buffer,
                            Name->Length + sizeof( WCHAR ) );

                AccountName = &CombinedName ;

            }
            else
            {
                return SEC_E_INSUFFICIENT_MEMORY ;
            }

            break;

        case SecNameFlat:
            SamFlags |= SAM_OPEN_BY_UPN ;
            AccountName = Name ;
            break;

        default:
            return STATUS_INVALID_PARAMETER ;
    }

    Status = SamIGetUserLogonInformation(
                    LsapAccountDomainHandle,
                    SamFlags,
                    AccountName,
                    &UserAllInfo,
                    &ReverseMembership,
                    NULL );


    //
    // Free the combined name (if appropriate)
    //

    if ( CombinedName.Buffer )
    {
        LsapFreeLsaHeap( CombinedName.Buffer );
    }

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

    UserAll = &UserAllInfo->All ;

    if ( UserFlatName )
    {
        Status = LsapDuplicateString(
                        UserFlatName,
                        (PUNICODE_STRING) &UserAll->UserName );

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

    if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED )
    {
        Status = STATUS_ACCOUNT_DISABLED ;

        goto GetPac_Cleanup;
    }

    if ( UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED )
    {
        Status = STATUS_ACCOUNT_LOCKED_OUT ;

        goto GetPac_Cleanup ;
    }

    Status = LsapCaptureSamInfo( &Sid, &Domain, &Machine );

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

    Status = PAC_Init( UserAll,
                       NULL,
                       &ReverseMembership,            // no extra groups
                       Sid,
                       &Domain,
                       &Machine,
                       0,               // no signature
                       0,               // no additional data
                       NULL,            // no additional data
                       &Pac );


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

    *UserAuthDataSize = PAC_GetSize( Pac );

    *UserAuthData = (PUCHAR) LsapAllocateLsaHeap( *UserAuthDataSize );

    if ( *UserAuthData )
    {
       PAC_Marshal( Pac, *UserAuthDataSize, *UserAuthData );
    }
    else
    {
        Status = SEC_E_INSUFFICIENT_MEMORY ;
    }

    MIDL_user_free( Pac );

GetPac_Cleanup:

    if ( UserAllInfo )
    {
        SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation );
    }

    if ( ReverseMembership.Count )
    {
        SamIFreeSidAndAttributesList( &ReverseMembership );
    }

    if ( Sid )
    {
        LsapFreeLsaHeap( Sid );
    }

    if ( Domain.Buffer )
    {
        LsapFreeLsaHeap( Domain.Buffer );
    }

    if ( Machine.Buffer )
    {
        LsapFreeLsaHeap( Machine.Buffer );
    }

    return( Status );

}

NTSTATUS
NTAPI
LsaCrackSingleName(
    ULONG FormatOffered,
    BOOLEAN PerformAtGC,
    PUNICODE_STRING NameInput,
    PUNICODE_STRING Prefix OPTIONAL,
    ULONG RequestedFormat,
    PUNICODE_STRING CrackedName,
    PUNICODE_STRING DnsDomainName,
    PULONG SubStatus
    )
{
    NTSTATUS Status = STATUS_SUCCESS ;
    UNICODE_STRING DnsDomain ;
    UNICODE_STRING Name ;
    DWORD Ret ;
    DWORD DnsDomainLength ;
    DWORD NameLength ;
    UNICODE_STRING CombinedName = { 0 };
    PUNICODE_STRING AccountName ;

    if ( !SampUsingDsData() )
    {
        return SEC_E_UNSUPPORTED_FUNCTION ;
    }

    //
    // Cruft up the call to the DS
    //


    Name.Buffer = (PWSTR) LsapAllocateLsaHeap( MAX_PATH * sizeof(WCHAR) * 2 );
    DnsDomain.Buffer = (PWSTR) LsapAllocateLsaHeap( MAX_PATH * sizeof(WCHAR) );

    if ( Name.Buffer && DnsDomain.Buffer )
    {
        Name.MaximumLength = MAX_PATH * sizeof(WCHAR) * 2 ;
        Name.Length = 0 ;

        DnsDomain.MaximumLength = MAX_PATH * sizeof(WCHAR) ;
        DnsDomain.Length = 0 ;

        NameLength = MAX_PATH * 2 ;
        DnsDomainLength = MAX_PATH ;

        Name.Buffer[ 0 ] = L'\0';
        DnsDomain.Buffer[ 0 ] = L'\0';

        if ( Prefix )
        {
            CombinedName.MaximumLength = NameInput->Length + Prefix->Length +
                                            2 * sizeof( WCHAR );

            CombinedName.Length = CombinedName.MaximumLength - sizeof( WCHAR );

            CombinedName.Buffer = (PWSTR) LsapAllocatePrivateHeap( CombinedName.MaximumLength );


            if ( CombinedName.Buffer )
            {
                CopyMemory( CombinedName.Buffer, Prefix->Buffer, Prefix->Length );

                CombinedName.Buffer[ Prefix->Length / sizeof( WCHAR ) ] = L':';

                CopyMemory( &CombinedName.Buffer[ Prefix->Length / sizeof( WCHAR ) + 1],
                            NameInput->Buffer,
                            NameInput->Length + sizeof( WCHAR ) );

                AccountName = &CombinedName ;

            }
            else
            {
                AccountName = NULL ;
            }

        }
        else
        {
            AccountName = NameInput ;
        }

        if ( AccountName )
        {
            __try
            {
                Ret = CrackSingleName(
                            FormatOffered,
                            PerformAtGC ?
                                DS_NAME_FLAG_GCVERIFY : 0,
                            AccountName->Buffer,
                            RequestedFormat,
                            &DnsDomainLength,
                            DnsDomain.Buffer,
                            &NameLength,
                            Name.Buffer,
                            SubStatus );

                if ( Ret != 0 )
                {
                    Status = STATUS_UNSUCCESSFUL ;
                }
                else
                {
                    Status = STATUS_SUCCESS ;

                    RtlInitUnicodeString( &DnsDomain, DnsDomain.Buffer );
                    RtlInitUnicodeString( &Name, Name.Buffer );

                    *CrackedName = Name ;
                    *DnsDomainName = DnsDomain ;


                }


            }
            __except( EXCEPTION_EXECUTE_HANDLER )
            {
                Status = STATUS_UNSUCCESSFUL ;
            }

            if ( CombinedName.Buffer )
            {
                LsapFreePrivateHeap( CombinedName.Buffer );
            }

        }

    }
    else
    {
        Status = SEC_E_INSUFFICIENT_MEMORY ;

    }

    if ( !NT_SUCCESS( Status ) )
    {
        if ( Name.Buffer )
        {
            LsapFreeLsaHeap( Name.Buffer );
        }

        if ( DnsDomain.Buffer )
        {
            LsapFreeLsaHeap( DnsDomain.Buffer );
        }
    }

    return Status ;

}

NTSTATUS
LsapBuildPacSidList(
    IN  PNETLOGON_VALIDATION_SAM_INFO3 UserInfo,
    OUT PSAMPR_PSID_ARRAY Sids
    )
{
    ULONG Size = 0, i;
    NTSTATUS Status = STATUS_SUCCESS ;

    Sids->Count = 0;
    Sids->Sids = NULL;


    if (UserInfo->UserId != 0)
    {
        Size += sizeof( SAMPR_SID_INFORMATION );
    }

    Size += UserInfo->GroupCount * (ULONG)sizeof( SAMPR_SID_INFORMATION );


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

    if (UserInfo->UserFlags & LOGON_EXTRA_SIDS)
    {
        Size += UserInfo->SidCount * (ULONG)sizeof(SAMPR_SID_INFORMATION);
    }

    Sids->Sids = (PSAMPR_SID_INFORMATION) MIDL_user_allocate( Size );

    if ( Sids->Sids == NULL )
    {
        Status = STATUS_NO_MEMORY ;
        goto Cleanup;
    }

    RtlZeroMemory(
        Sids->Sids,
        Size
        );


    //
    // Start copying SIDs into the structure
    //

    i = 0;

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

    if ( UserInfo->UserId )
    {
        Sids->Sids[0].SidPointer = (PRPC_SID)
                LsapMakeDomainRelativeSid( UserInfo->LogonDomainId,
                                            UserInfo->UserId );

        if( Sids->Sids[0].SidPointer == NULL )
        {
            Status = STATUS_NO_MEMORY ;
            goto Cleanup;
        }
        Sids->Count++;
    }

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

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

        Sids->Sids[Sids->Count].SidPointer = (PRPC_SID)
                                    LsapMakeDomainRelativeSid(
                                         UserInfo->LogonDomainId,
                                         UserInfo->GroupIds[i].RelativeId );

        if( Sids->Sids[Sids->Count].SidPointer == NULL )
        {
            Status = STATUS_NO_MEMORY ;
            goto Cleanup;
        }

        Sids->Count++;
    }


    //
    // Add in the extra SIDs
    //

    //
    // No need to allocate these, but...
    //
    if (UserInfo->UserFlags & LOGON_EXTRA_SIDS)
    {

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

            Status = LsapDuplicateSid(
                                (PSID *) &Sids->Sids[Sids->Count].SidPointer,
                                UserInfo->ExtraSids[i].Sid );

            if ( !NT_SUCCESS( Status ) )
            {
                goto Cleanup ;
            }
            Sids->Count++;
        }
    }


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

Cleanup:
    if (!NT_SUCCESS( Status ))
    {
        if (Sids->Sids != NULL)
        {
            for (i = 0; i < Sids->Count ;i++ )
            {
                if (Sids->Sids[i].SidPointer != NULL)
                {
                    MIDL_user_free(Sids->Sids[i].SidPointer);
                }
            }
            MIDL_user_free(Sids->Sids);
            Sids->Sids = NULL;
            Sids->Count = 0;
        }
    }
    return Status ;

}



NTSTATUS
NTAPI
LsaExpandAuthDataForDomain(
    IN PUCHAR UserAuthData,
    IN ULONG UserAuthDataSize,
    IN PVOID Reserved,
    OUT PUCHAR * ExpandedAuthData,
    OUT PULONG ExpandedAuthDataSize
    )
{
    NTSTATUS Status = STATUS_SUCCESS ;
    PPACTYPE Pac = NULL ;
    PPAC_INFO_BUFFER LogonInfo = NULL ;
    PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo = NULL ;
    PLSA_TOKEN_INFORMATION_V1 TokenInfo = NULL ;
    SAMPR_PSID_ARRAY SidList = {0};
    PSAMPR_PSID_ARRAY ResourceGroups = NULL;
    PPACTYPE NewPac = NULL ;
    ULONG Index ;


    Pac = (PPACTYPE) UserAuthData ;

    if ( PAC_UnMarshal( Pac, UserAuthDataSize ) == 0 )
    {
        DebugLog(( DEB_ERROR, "Failed to unmarshall pac\n" ));

        Status = STATUS_INVALID_PARAMETER ;

        goto Expand_Cleanup ;
    }

    LogonInfo = PAC_Find( Pac, PAC_LOGON_INFO, NULL );

    if ( !LogonInfo )
    {
        DebugLog(( DEB_ERROR, "Failed to find logon info in pac\n" ));

        Status = STATUS_INVALID_PARAMETER ;

        goto Expand_Cleanup ;
    }


    Status = PAC_UnmarshallValidationInfo(
                &ValidationInfo,
                LogonInfo->Data,
                LogonInfo->cbBufferSize
                );

    if (!NT_SUCCESS(Status))
    {
        DebugLog((DEB_ERROR,"Failed to unmarshall validation info: 0x%x\n",
            Status));

        goto Expand_Cleanup;
    }

    Status = LsapBuildPacSidList(
                ValidationInfo,
                &SidList );

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


    //
    // Call SAM to get the sids
    //

    Status = SamIGetResourceGroupMembershipsTransitive(
                LsapAccountDomainHandle,
                &SidList,
                0,              // no flags
                &ResourceGroups
                );

    if (!NT_SUCCESS(Status))
    {
        DebugLog((DEB_ERROR,"Failed to get resource groups: 0x%x\n",Status));
        goto Expand_Cleanup;
    }

    //
    // Now build a new pac
    //

    Status = PAC_InitAndUpdateGroups(
                ValidationInfo,
                ResourceGroups,
                Pac,
                &NewPac
                );

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

    *ExpandedAuthDataSize = PAC_GetSize( NewPac );

    *ExpandedAuthData = (PUCHAR) LsapAllocateLsaHeap( *ExpandedAuthDataSize );

    if ( *ExpandedAuthData )
    {
       PAC_Marshal( NewPac, *ExpandedAuthDataSize, *ExpandedAuthData );
    }
    else
    {
        Status = STATUS_NO_MEMORY ;
    }

    MIDL_user_free( NewPac );


Expand_Cleanup:

    if ( ValidationInfo )
    {
        MIDL_user_free( ValidationInfo );
    }

    if (SidList.Sids != NULL)
    {
        for (Index = 0; Index < SidList.Count ;Index++ )
        {
            if (SidList.Sids[Index].SidPointer != NULL)
            {
                MIDL_user_free(SidList.Sids[Index].SidPointer);
            }
        }
        MIDL_user_free(SidList.Sids);
    }

    SamIFreeSidArray(
        ResourceGroups
        );

    return Status ;

}