//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1991 - 1992
//
// File:        sphelp.c
//
// Contents:    Security Package Helper functions (see isecpkg.doc)
//
// Functions    LsapUnloadPackage
//              LsapAllocate
//              LsapFree
//              LsapClientAllocate
//              LsapCopyToClient
//              LsapCopyFromClient
//              LsapClientFree
//              LsapOpenClientProcess
//              LsapOpenClientThread
//              LsapDuplicateHandle
//              LsapSaveSupplementalCredentials
//              LsapGetWindow
//              LsapCreateThread
//              LsapMapClientBuffer
//
//
// Notes:       By defining TRACK_MEM, we will track all use of memory
//              allocated through the LsapAllocate.  DBG_MEM will track
//              memory leaks.
//
// History:     20 May 92   RichardW    Created
//
//------------------------------------------------------------------------

#include <lsapch.hxx>

extern "C"
{
#include "sesmgr.h"
#include "spdebug.h"
#include "klpcstub.h"

}


typedef struct _LSAP_THREAD_START {
    LPTHREAD_START_ROUTINE  lpStart;
    LPVOID                  lpParm;
    ULONG_PTR               dwPackageID;
} LSAP_THREAD_START, *PLSAP_THREAD_START;

extern LSA_CALL_INFO LsapDefaultCallInfo ;


ULONG_PTR   LsapUserModeLimit ;

//
// private heaps defines.
//

// HEAP_HEADER should always be size even multiple of heap allocation granularity
//
typedef struct {
    HANDLE      hHeap;
    SIZE_T      Magic;
} HEAP_HEADER_LSA, *PHEAP_HEADER_LSA;

#define HEAP_COUNT_MAX  32
#define HEAP_MAGIC_TAG  0x66677766

HANDLE gHeaps[ HEAP_COUNT_MAX ];
DWORD gcHeaps;



#if DBG
//
// Failure simulation parameters
//

ULONG TotalAllocations;
ULONG_PTR PackageToFail = SPMGR_ID;
SpmDbg_MemoryFailure MemFail;

#endif // DBG



void
CacheMachineInReg(GUID *);



#define MEM_MAGIC   0x0feedbed
#define MEM_FREED   0x0b00b00b
#define MEM_MASK    0x0FFFFFFF
#define MEM_NEVER   0x10000000

LSA_SECPKG_FUNCTION_TABLE LsapSecpkgFunctionTable = {
    LsapCreateLogonSession,
    LsapDeleteLogonSession,
    LsapAddCredential,
    LsapGetCredentials,
    LsapDeleteCredential,
    LsapAllocateLsaHeap,
    LsapFreeLsaHeap,
    LsapAllocateClientBuffer,
    LsapFreeClientBuffer,
    LsapCopyToClientBuffer,
    LsapCopyFromClientBuffer,
    LsapImpersonateClient,
    LsapUnloadPackage,
    LsapDuplicateHandle,
    NULL,                       // LsapSaveSupplementalCredentials,
    LsapCreateThread,
    LsapGetClientInfo,
    LsaIRegisterNotification,
    LsaICancelNotification,
    LsapMapClientBuffer,
    LsapCreateToken,
    LsapAuditLogon,
    LsaICallPackage,
    LsaIFreeReturnBuffer,
    LsapGetCallInfo,
    LsaICallPackageEx,
    LsaCreateSharedMemory,
    LsaAllocateSharedMemory,
    LsaFreeSharedMemory,
    LsaDeleteSharedMemory,
    LsaOpenSamUser,
    LsaGetUserCredentials,
    LsaGetUserAuthData,
    LsaCloseSamUser,
    LsaConvertAuthDataToToken,
    LsaClientCallback,
    LsapUpdateCredentials,
    LsaGetAuthDataForUser,
    LsaCrackSingleName,
    LsaIAuditAccountLogon,
    LsaICallPackagePassthrough,
    CrediRead,
    CrediReadDomainCredentials,
    CrediFreeCredentials,
    LsaProtectMemory,
    LsaUnprotectMemory,
    LsapOpenTokenByLogonId,
    LsaExpandAuthDataForDomain,
    LsapAllocatePrivateHeap,
    LsapFreePrivateHeap,
    LsapCreateTokenEx
    };



//+-------------------------------------------------------------------------
//
//  Function:   LsapOpenCaller
//
//  Synopsis:   Opens the calling process
//
//  Effects:
//
//  Arguments:  phProcess   -- receives handle to process
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
NTSTATUS
LsapOpenCaller(
    IN OUT PSession pSession
    )
{
    HANDLE hProcess;
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    CLIENT_ID ClientId;
    PVOID Ignored ;

    ClientId.UniqueThread = NULL;
    ClientId.UniqueProcess = (HANDLE) LongToHandle(pSession->dwProcessID);

    InitializeObjectAttributes(
        &ObjectAttributes,
        NULL,
        0,
        NULL,
        NULL
        );

    Status = NtOpenProcess(
                &hProcess,
                PROCESS_DUP_HANDLE |        // Duplicate Handles
                    PROCESS_QUERY_INFORMATION | // Get token
                    PROCESS_VM_OPERATION |      // Allocate
                    PROCESS_VM_READ |           // Read memory
                    PROCESS_VM_WRITE,           // Write memory
                &ObjectAttributes,
                &ClientId
                );
    if (!NT_SUCCESS(Status))
    {
        DebugLog((DEB_ERROR, "Did not open process %08x, error %08x\n",
            pSession->dwProcessID, Status));

        return( Status );
    }

    pSession->hProcess = hProcess;

    Status = NtQueryInformationProcess(
                    hProcess,
                    ProcessSessionInformation,
                    &pSession->SessionId,
                    sizeof( ULONG ),
                    NULL );

#if _WIN64
    Status = NtQueryInformationProcess(
                    hProcess,
                    ProcessWow64Information,
                    &Ignored,
                    sizeof( Ignored ),
                    NULL );

    if ( NT_SUCCESS( Status ) )
    {
        if ( Ignored != 0 )
        {
            pSession->fSession |= SESFLAG_WOW_PROCESS ;
        }
    }
#endif

    return( STATUS_SUCCESS );

}


//+-------------------------------------------------------------------------
//
//  Function:   CheckCaller
//
//  Synopsis:   Checks if calling process has been opened, opens if it
//              hasn't.
//
//  Effects:
//
//  Arguments:  pSession -  Current session
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------

NTSTATUS
CheckCaller(
    IN PSession pSession
    )
{
    NTSTATUS       Status;

    if (!pSession->hProcess)
    {
        Status = LsapOpenCaller(pSession);
        if (FAILED(Status))
        {
            DebugLog((DEB_ERROR, "Could not open client (%x)\n", Status));
            return(Status);
        }

    }
    return(STATUS_SUCCESS);

}




//+-------------------------------------------------------------------------
//
//  Function:   LsapUnloadPackage
//
//  Synopsis:   Unloads the calling package in case of catastrophic problems
//
//  Effects:    Unloads the DLL that generated this call.
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:      Calling thread is terminated through a special exception.
//
//--------------------------------------------------------------------------

NTSTATUS NTAPI
LsapUnloadPackage(
    VOID
    )
{
    ULONG_PTR PackageId;
    PSession    pSession = GetCurrentSession();

    PackageId = GetCurrentPackageId();


    //
    // If this is an autonomous thread, we should interrupt any other threads
    // that are currently executing in the DLL.  At this time, I do not know
    // what will happen if the virtual address space of a thread suddenly
    // becomes invalid, or even if that will happen.  We'll find out...
    //

    if (pSession->fSession & SESFLAG_AUTONOMOUS)
    {
        DebugLog((DEB_WARN, "Autonomous thread executed LsapUnloadPackage\n"));
    }

    pSession->fSession |= SESFLAG_UNLOADING;

    RaiseException((ULONG) SEC_E_BAD_PKGID, 0, 0, NULL);

    return(STATUS_SUCCESS);
}



BOOLEAN
LsapHeapInitialize(
    IN BOOLEAN Server
    )
{

    if( !Server )
    {
        NT_PRODUCT_TYPE ProductType;

        if ( RtlGetNtProductType( &ProductType ) &&
             (ProductType == NtProductServer || ProductType == NtProductLanManNt)
             )
        {
            Server = TRUE;
        }
    }

    if ( Server )
    {
        SYSTEM_INFO si;
        DWORD Heaps;
        DWORD i, cHeapsCreated;
        RTL_HEAP_PARAMETERS HeapParameters;

        GetSystemInfo( &si );

        if( si.dwNumberOfProcessors == 0 ) {
            Heaps = 1;
        } else if( si.dwNumberOfProcessors > HEAP_COUNT_MAX )
        {
            Heaps = HEAP_COUNT_MAX;
        } else {
            Heaps = si.dwNumberOfProcessors;
        }

        ZeroMemory( &HeapParameters, sizeof(HeapParameters) );
        HeapParameters.Length = sizeof(HeapParameters);
        HeapParameters.DeCommitTotalFreeThreshold = 8 * LsapPageSize ;

        cHeapsCreated = 0;

        for( i = 0 ; i < Heaps ; i++ )
        {
            gHeaps[ cHeapsCreated ] = RtlCreateHeap(
                                        HEAP_GROWABLE,
                                        NULL,
                                        0,
                                        0,
                                        NULL,
                                        &HeapParameters
                                        );

            if( gHeaps[ cHeapsCreated ] != NULL )
            {
                cHeapsCreated++;
            }
        }

        gcHeaps = cHeapsCreated;
    }


    if( gHeaps[ 0 ] == NULL )
    {
        gHeaps[ 0 ] = RtlProcessHeap();
        gcHeaps = 1;
    }




    return TRUE;
}




//+-------------------------------------------------------------------------
//
//  Function:   LsapAllocateLsaHeap
//
//  Synopsis:   Allocates memory on process heap
//
//  Effects:
//
//  Arguments:  cbMemory    -- Size of block needed
//
//  Requires:
//
//  Returns:    ptr to memory
//
//  Notes:      if DBGMEM or TRACK_MEM defined, extra work is done for
//              tracking purposes.
//
//              Can raise the exception STATUS_NO_MEMORY
//
//              Per object reuse rules of C2 and above, we zero any memory
//              allocated through this function
//
//--------------------------------------------------------------------------
PVOID NTAPI
LsapAllocateLsaHeap(
    IN ULONG cbMemory
    )
{

#if DBG
    if (MemFail.fSimulateFailure)
    {
        TotalAllocations++;
        if ((TotalAllocations > MemFail.FailureDelay) &&
            (TotalAllocations < MemFail.FailureLength + MemFail.FailureDelay))
        {
            if ((TotalAllocations % MemFail.FailureInterval) == 0)
            {
                if ((PackageToFail == SPMGR_ID) ||
                    (GetCurrentPackageId() == PackageToFail))
                {
                    DebugLog((DEB_TRACE,"LsapAllocateLsaHeap: Simulating failure\n"));
                    return(NULL);
                }
            }
        }
    }
#endif // DBG


    return(RtlAllocateHeap(
                RtlProcessHeap(),
                HEAP_ZERO_MEMORY,
                cbMemory
                ));
}

//+-------------------------------------------------------------------------
//
//  Function:   LsapFreeLsaHeap
//
//  Synopsis:   Frees memory allocated by LsapAllocateLsaHeap
//
//  Effects:
//
//  Arguments:  pvMemory
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
void NTAPI
LsapFreeLsaHeap(
    IN PVOID pvMemory
    )
{

    RtlFreeHeap(
                RtlProcessHeap(),
                0,
                pvMemory
                );

}


//+-------------------------------------------------------------------------
//
//  Function:   LsapAllocatePrivateHeap
//
//  Synopsis:   Allocates memory on private heap(s)
//
//  Effects:
//
//  Arguments:  cbMemory    -- Size of block needed
//
//  Requires:
//
//  Returns:    ptr to memory
//
//              Per object reuse rules of C2 and above, we zero any memory
//              allocated through this function
//
//--------------------------------------------------------------------------
PVOID
NTAPI
LsapAllocatePrivateHeap(
    IN SIZE_T cbMemory
    )
{
    HANDLE hHeap;

    PHEAP_HEADER_LSA memory;


    hHeap = LsapGetCurrentHeap();

    if( hHeap == NULL )
    {
        static ULONG heapindex;
        ULONG LocalHeapIndex;

        LocalHeapIndex = (ULONG)InterlockedIncrement( (PLONG)&heapindex );
        LocalHeapIndex %= gcHeaps;

        hHeap = gHeaps[ LocalHeapIndex ];

        LsapSetCurrentHeap( hHeap );
    }

    memory = (PHEAP_HEADER_LSA)RtlAllocateHeap(
                hHeap,
                HEAP_ZERO_MEMORY,
                cbMemory+sizeof(HEAP_HEADER_LSA)
                );

    if( memory != NULL )
    {

        memory->hHeap = hHeap;
        memory->Magic = (unsigned char*)HEAP_MAGIC_TAG-(unsigned char*)hHeap;

        return ( (unsigned char*)memory+sizeof(HEAP_HEADER_LSA) );
    }

    DebugLog((DEB_ERROR,"LsapAllocatePrivateHeap: %p failed allocate %lu bytes\n", hHeap, cbMemory));

    return NULL;
}

PVOID
NTAPI
LsapAllocatePrivateHeapNoZero(
    IN SIZE_T cbMemory
    )
{
    HANDLE hHeap;

    PHEAP_HEADER_LSA memory;


    hHeap = LsapGetCurrentHeap();

    if( hHeap == NULL )
    {
        static ULONG heapindex;
        ULONG LocalHeapIndex;

        LocalHeapIndex = (ULONG)InterlockedIncrement( (PLONG)&heapindex );
        LocalHeapIndex %= gcHeaps;

        hHeap = gHeaps[ LocalHeapIndex ];

        LsapSetCurrentHeap( hHeap );
    }

    memory = (PHEAP_HEADER_LSA)RtlAllocateHeap(
                hHeap,
                0,
                cbMemory+sizeof(HEAP_HEADER_LSA)
                );

    if( memory != NULL )
    {
        memory->hHeap = hHeap;
        memory->Magic = (unsigned char*)HEAP_MAGIC_TAG-(unsigned char*)hHeap;

        return ( (unsigned char*)memory+sizeof(HEAP_HEADER_LSA) );
    }

    DebugLog((DEB_ERROR,"LsapAllocatePrivateHeapNoZero: %p failed allocate %lu bytes\n", hHeap, cbMemory));

    return NULL;
}

//+-------------------------------------------------------------------------
//
//  Function:   LsapFreePrivateHeap
//
//  Synopsis:   Frees memory allocated by LsapAllocatePrivateHeap
//
//  Effects:
//
//  Arguments:  pvMemory
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
void
NTAPI
LsapFreePrivateHeap(
    IN PVOID pvMemory
    )
{
    PHEAP_HEADER_LSA HeapEntry;

    if( pvMemory == NULL )
    {
        return ;
    }

    HeapEntry = (PHEAP_HEADER_LSA)((unsigned char*)pvMemory-sizeof(HEAP_HEADER_LSA));

    if( HeapEntry->Magic + ((unsigned char*)HeapEntry->hHeap) != (unsigned char*)HEAP_MAGIC_TAG )
    {
        DebugLog((DEB_ERROR, "LsapFreePrivateHeap tried to free %p from wrong heap\n",
            pvMemory));

        DsysAssert( pvMemory == NULL );
        return;
    }

    RtlFreeHeap(
                HeapEntry->hHeap,
                0,
                HeapEntry
                );

}


//+-------------------------------------------------------------------------
//
//  Function:   LsapClientAllocate
//
//  Synopsis:   Allocates memory in client process
//
//  Effects:
//
//  Arguments:  cbMemory    -- Size of block to allocate
//
//  Requires:
//
//  Returns:    pointer to memory in client address space
//
//  Notes:      pointer is not valid in this process, only in client
//
//--------------------------------------------------------------------------
PVOID NTAPI
LsapClientAllocate(
    IN ULONG cbMemory
    )
{

    NTSTATUS        Status;
    PSession        pSession;
    void *          pClientMemory = NULL;
    SIZE_T          cbMem = cbMemory;
    PLSA_CALL_INFO  CallInfo ;

    CallInfo = LsapGetCurrentCall();

    pSession = GetCurrentSession() ;

    if ( pSession == NULL )
    {
        pSession = pDefaultSession ;
    }

    if ( CallInfo == NULL )
    {
        CallInfo = &LsapDefaultCallInfo ;
    }

    if (FAILED(CheckCaller(pSession)))
    {
        return(NULL);
    }

    //
    // If the INPROC flag is set, allocate out of the heap.  The copy functions
    // will also honor this.
    //

    if ( pSession->fSession & SESFLAG_INPROC )
    {
        pClientMemory = LsapAllocateLsaHeap( (ULONG) cbMem );

        return( pClientMemory );
    }

    if ( CallInfo->Flags & CALL_FLAG_KERNEL_POOL )
    {
        if ( ( CallInfo->Flags & CALL_FLAG_KMAP_USED ) != 0 )
        {

            pClientMemory = LsapAllocateFromKsecBuffer(
                                CallInfo->KMap,
                                (ULONG) cbMem );

            DebugLog((DEB_TRACE_HELPERS, "[%x] LsapClientAllocate(%d) = %p in KMap %p\n",
                        pSession->dwProcessID, cbMem,
                        pClientMemory, CallInfo->KMap ));
            
            return pClientMemory ;
        }

        
        
    }

    Status = NtAllocateVirtualMemory(pSession->hProcess,
                                    &pClientMemory,
                                    0,
                                    &cbMem,
                                    MEM_COMMIT,
                                    PAGE_READWRITE);
    if (!NT_SUCCESS(Status))
    {
        DebugLog((DEB_ERROR, "[%x] Could not allocate client memory (%x)\n",
                        pSession->dwProcessID, Status));

        pClientMemory = NULL;
    }


    DebugLog((DEB_TRACE_HELPERS, "[%x] LsapClientAllocate(%d) = %p\n",
                    pSession->dwProcessID, cbMemory, pClientMemory));

    if ( pClientMemory )
    {
        // Save pointer so that FreeContextBuffer will use
        // correct 'free' function.
        if(CallInfo->Allocs < MAX_BUFFERS_IN_CALL)
        {
            CallInfo->Buffers[ CallInfo->Allocs++ ] = pClientMemory ;
        }
    }

    return(pClientMemory);
}


//+-------------------------------------------------------------------------
//
//  Function:   LsapCopyToClient
//
//  Synopsis:   Copies data into client process
//
//  Effects:
//
//  Arguments:  pLocalMemory    -- pointer to data in this process
//              pClientMemory   -- pointer to destination in client process
//              cbMemory        -- how much to copy
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
LsapCopyToClient(
    IN PVOID pLocalMemory,
    OUT PVOID pClientMemory,
    IN ULONG cbMemory
    )
{
    NTSTATUS     Status;
    PSession    pSession;
    PLSA_CALL_INFO CallInfo;
    PKSEC_LSA_MEMORY_HEADER Header ;
    ULONG i ;
    ULONG_PTR Basis ;
    ULONG_PTR Limit ;
    BOOL Tried = FALSE ;

    if (cbMemory == 0)
    {
        return(STATUS_SUCCESS);
    }

    pSession = GetCurrentSession();

    if ( !pSession )
    {
        pSession = pDefaultSession ;
    }

    CallInfo = LsapGetCurrentCall();

    if ( ( pLocalMemory == NULL ) ||
         ( pClientMemory == NULL ) )
    {
        return STATUS_ACCESS_VIOLATION ;
    }

    if (FAILED(Status = CheckCaller(pSession)))
    {
        return(Status);
    }

    //
    // Cases for a direct copy: 
    //
    //  - The current session is the default session
    //  - This is an inproc call
    //  - We're using a KMap buffer
    //

    
    if (CallInfo &&
        CallInfo->Flags & CALL_FLAG_KERNEL_POOL )
    {

        Header = CallInfo->KMap ;

        if ( (ULONG_PTR) pClientMemory > LsapUserModeLimit )
        {
            //
            // Someone is trying to deal with a pool address directly.
            // we can handle this if it was copied into the KMap already
            //

            for ( i = 0 ; i < Header->MapCount ; i++ )
            {
                Limit  = (ULONG_PTR) Header->PoolMap[ i ].Pool + Header->PoolMap[ i ].Size ;

                if ( ((ULONG_PTR) pClientMemory >= (ULONG_PTR) Header->PoolMap[ i ].Pool ) &&
                    ( (ULONG_PTR) pClientMemory < Limit ) )
                {
                    //
                    // Found an overlap, this is promising.  Check the bounds:
                    //

                    Basis = (ULONG_PTR) pClientMemory - 
                                (ULONG_PTR) Header->PoolMap[ i ].Pool ;

                    if (  Basis + cbMemory <= Header->PoolMap[ i ].Size )
                    {
                        //
                        // Found it!
                        //
                        __try
                        {
                            RtlCopyMemory(
                                (PUCHAR) Header + 
                                        (Header->PoolMap[ i ].Offset +
                                         Basis),
                                pLocalMemory,
                                cbMemory );


                            Status = STATUS_SUCCESS ;
                        }
                        __except (EXCEPTION_EXECUTE_HANDLER)
                        {
                            Status = GetExceptionCode();
                        }

                        
                    }
                    Tried = TRUE ;
                    break;
                    
                }
                
            }

            if ( !Tried )
            {
                Status = STATUS_ACCESS_VIOLATION ;
                
            }
            
        }
        else if ( LsapIsBlockInKMap( CallInfo->KMap, pClientMemory ) )
        {
            __try
            {

                RtlCopyMemory( pClientMemory, pLocalMemory, cbMemory );

                Status = STATUS_SUCCESS ;
            }
            __except (EXCEPTION_EXECUTE_HANDLER)
            {
                Status = GetExceptionCode();
            }
            
        }
        else
        {

            Status = NtWriteVirtualMemory(pSession->hProcess,
                                        pClientMemory,
                                        pLocalMemory,
                                        cbMemory,
                                        NULL);
        }
        
    } else if ( (pSession->dwProcessID == pDefaultSession->dwProcessID) ||
         (pSession->fSession & SESFLAG_INPROC) ||
         (CallInfo->Flags & CALL_FLAG_KMAP_USED ) )
    {
        __try
        {

            RtlCopyMemory( pClientMemory, pLocalMemory, cbMemory );

            Status = STATUS_SUCCESS ;
        }
        __except ( EXCEPTION_EXECUTE_HANDLER )
        {
            Status = GetExceptionCode();
        }
    }
    else
    {

        Status = NtWriteVirtualMemory(  pSession->hProcess,
                                        pClientMemory,
                                        pLocalMemory,
                                        cbMemory,
                                        NULL);
    }

    DebugLog((DEB_TRACE_HELPERS, "[%x] LsapCopyToClient(%p, %p, %d) = %x\n",
                pSession->dwProcessID, pLocalMemory, pClientMemory, cbMemory,
                Status ));

    return(Status);
}

//+-------------------------------------------------------------------------
//
//  Function:   LsapCopyFromClient
//
//  Synopsis:   Copies memory from client to this process
//
//  Effects:
//
//  Arguments:  pClientMemory   -- Pointer to data in client space
//              pLocalMemory    -- Pointer to destination in this process
//              cbMemory        -- How much
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
LsapCopyFromClient(
    IN PVOID pClientMemory,
    OUT PVOID pLocalMemory,
    IN ULONG cbMemory
    )
{
    NTSTATUS       Status;
    PSession    pSession;
    PLSA_CALL_INFO CallInfo ;
    PKSEC_LSA_MEMORY_HEADER Header ;
    ULONG i ;
    ULONG_PTR Basis ;
    ULONG_PTR Limit ;
    BOOL Tried = FALSE ;



    if (cbMemory == 0)
    {
        return(STATUS_SUCCESS);
    }

    if ( ( pClientMemory == NULL ) ||
         ( pLocalMemory == NULL ) )
    {
        return STATUS_ACCESS_VIOLATION ;
    }


    pSession = GetCurrentSession();

    if ( pSession == NULL )
    {
        pSession = pDefaultSession ;
    }

    CallInfo = LsapGetCurrentCall();

    if (FAILED(Status = CheckCaller(pSession)))
    {
        return(Status);
    }

    if (CallInfo &&
        CallInfo->Flags & CALL_FLAG_KERNEL_POOL )
    {

        Header = CallInfo->KMap ;

        if ( (ULONG_PTR) pClientMemory > LsapUserModeLimit )
        {
            //
            // Someone is trying to deal with a pool address directly.
            // we can handle this if it was copied into the KMap already
            //

            for ( i = 0 ; i < Header->MapCount ; i++ )
            {
                Limit  = (ULONG_PTR) Header->PoolMap[ i ].Pool + Header->PoolMap[ i ].Size ;

                if ( ((ULONG_PTR) pClientMemory >= (ULONG_PTR) Header->PoolMap[ i ].Pool ) &&
                    ( (ULONG_PTR) pClientMemory < Limit ) )
                {
                    //
                    // Found an overlap, this is promising.  Check the bounds:
                    //

                    Basis = (ULONG_PTR) pClientMemory - 
                                (ULONG_PTR) Header->PoolMap[ i ].Pool ;

                    if (  Basis + cbMemory <= Header->PoolMap[ i ].Size )
                    {
                        //
                        // Found it!
                        //
                        __try
                        {
                            RtlCopyMemory(
                                pLocalMemory,
                                (PUCHAR) Header + 
                                        (Header->PoolMap[ i ].Offset +
                                         Basis),
                                cbMemory );


                            Status = STATUS_SUCCESS ;
                        }
                        __except (EXCEPTION_EXECUTE_HANDLER)
                        {
                            Status = GetExceptionCode();
                        }

                        
                    }
                    Tried = TRUE ;
                    break;
                    
                }
                
            }

            if ( !Tried )
            {
                Status = STATUS_ACCESS_VIOLATION ;
                
            }
            
        }
        else if ( LsapIsBlockInKMap( CallInfo->KMap, pClientMemory ) )
        {
            __try
            {

                RtlCopyMemory( pLocalMemory, pClientMemory, cbMemory );

                Status = STATUS_SUCCESS ;
            }
            __except (EXCEPTION_EXECUTE_HANDLER)
            {
                Status = GetExceptionCode();
            }
            
        }
        else
        {

            Status = NtReadVirtualMemory(pSession->hProcess,
                                        pClientMemory,
                                        pLocalMemory,
                                        cbMemory,
                                        NULL);
        }
        
    }
    else if ( (pSession->dwProcessID == pDefaultSession->dwProcessID) ||
         (pSession->fSession & SESFLAG_INPROC ) )
    {
        __try
        {

            RtlCopyMemory( pLocalMemory, pClientMemory, cbMemory );

            Status = STATUS_SUCCESS ;
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
            Status = GetExceptionCode();
        }
    }
    else
    {

        Status = NtReadVirtualMemory(pSession->hProcess,
                                    pClientMemory,
                                    pLocalMemory,
                                    cbMemory,
                                    NULL);
    }

    DebugLog((DEB_TRACE_HELPERS, "[%x] LsapCopyFromClient(%p, %p, %d) = %x\n",
                pSession->dwProcessID, pClientMemory, pLocalMemory, cbMemory,
                Status));


    return(Status);
}



//+-------------------------------------------------------------------------
//
//  Function:   LsapClientFree
//
//  Synopsis:   Frees memory allocated in client space
//
//  Effects:
//
//  Arguments:  pClientMemory   -- pointer to memory to free
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
LsapClientFree(
    IN PVOID pClientMemory
    )
{
    NTSTATUS       Status;
    SIZE_T         cbMem = 0;
    PSession    pSession;
    PLSA_CALL_INFO  CallInfo ;

    CallInfo = LsapGetCurrentCall();
    if ( CallInfo == NULL )
    {
        CallInfo = &LsapDefaultCallInfo ;
    }

    if (!pClientMemory)
    {
        return(STATUS_SUCCESS);
    }

    pSession = GetCurrentSession();

    if ( pSession == NULL )
    {
        pSession = pDefaultSession ;
    }

    if (FAILED(Status = CheckCaller(pSession)))
    {
        return(Status);
    }

    if ( pSession->fSession & SESFLAG_INPROC )
    {
        LsapFreeLsaHeap( pClientMemory );

        return( STATUS_SUCCESS );
    }

#if DBG
    if ( pSession->dwProcessID == pDefaultSession->dwProcessID )
    {
        DebugLog(( DEB_ERROR, "Freeing VM in LSA:  %x\n", pClientMemory ));
    }
#endif

    Status = NtFreeVirtualMemory(pSession->hProcess,
                                &pClientMemory,
                                &cbMem,
                                MEM_RELEASE);



    if ( pClientMemory )
    {
        ULONG i;

        // Remove this pointer from our list.
        for(i = 0; i < CallInfo->Allocs; i++)
        {
            if(CallInfo->Buffers[i] == pClientMemory)
            {
                if(i < CallInfo->Allocs - 1)
                {
                    memcpy(&CallInfo->Buffers[i],
                           &CallInfo->Buffers[i+1],
                           sizeof(PVOID) * (CallInfo->Allocs - i - 1));
                }
                CallInfo->Allocs--;
                break;
            }
        }
    }

    DebugLog((DEB_TRACE_HELPERS, "[%x] LsapClientFree(%x) == %x\n",
            pSession->dwProcessID, pClientMemory, Status));

    return(Status);

}




//+-------------------------------------------------------------------------
//
//  Function:   LsapDuplicateHandle
//
//  Synopsis:   Duplicates a handle to an NT object into the calling process
//
//  Effects:    A new handle is generated, referencing the object
//
//  Arguments:  hObject     -- handle to the object
//              hNewObject  -- New handle valid in calling process
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
LsapDuplicateHandle(
    IN HANDLE hObject,
    OUT PHANDLE phNewObject
    )

{
    NTSTATUS     Status;
    PSession    pSession;

    pSession = GetCurrentSession();

    if ( pSession == NULL )
    {
        pSession = pDefaultSession ;
    }

    if (Status = CheckCaller(pSession))
    {
        DebugLog((DEB_ERROR, "CheckCaller returned %d\n", Status));
        return(Status);
    }
    Status = NtDuplicateObject(  NtCurrentProcess(),
                                hObject,
                                pSession->hProcess,
                                phNewObject,
                                0,
                                0,
                                DUPLICATE_SAME_ACCESS);


    DebugLog((DEB_TRACE_HELPERS, "[%x] LsapDupHandle(%x, %x (@%x)) = %x\n",
            pSession->dwProcessID, hObject, *phNewObject, phNewObject, Status));

    return(Status);
}



//+---------------------------------------------------------------------------
//
//  Function:   LsapImpersonateClient
//
//  Synopsis:   Impersonate the client of the API call.
//
//  Arguments:  (none)
//
//  History:    6-05-95   RichardW   Created
//
//  Notes:      Threads should call RevertToSelf() when done.
//
//----------------------------------------------------------------------------
NTSTATUS NTAPI
LsapImpersonateClient(
    VOID
    )
{
    PSession            pSession;
    PLSA_CALL_INFO      CallInfo ;
    PSPM_LPC_MESSAGE    pApiMessage;
    NTSTATUS            Status;


    CallInfo = LsapGetCurrentCall() ;

    if ( CallInfo->InProcCall )
    {
        if ( CallInfo->InProcToken )
        {
            Status = NtSetInformationThread(
                        NtCurrentThread(),
                        ThreadImpersonationToken,
                        (PVOID) &CallInfo->InProcToken,
                        sizeof(HANDLE)
                        );
        }
        else
        {
            Status = RtlImpersonateSelf(SecurityImpersonation);
        }
    }
    else
    {
        pSession = GetCurrentSession() ;

        if ( !pSession )
        {
            pSession = pDefaultSession ;
        }

        Status = NtImpersonateClientOfPort(
                    pSession->hPort,
                    (PPORT_MESSAGE) CallInfo->Message);
    }

    return(Status);
}






//+-------------------------------------------------------------------------
//
//  Function:   LsapDuplicateString
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
NTSTATUS
LsapDuplicateString(
    OUT PUNICODE_STRING pDest,
    IN PUNICODE_STRING pSrc
    )

{
    pDest->Length = 0;
    if (pSrc == NULL)
    {
        pDest->Buffer = NULL;
        pDest->MaximumLength = 0;
        return(STATUS_SUCCESS);
    }

    pDest->Buffer = (LPWSTR) LsapAllocateLsaHeap(pSrc->Length + sizeof(WCHAR));
    if (pDest->Buffer)
    {
        pDest->MaximumLength = pSrc->Length + sizeof(WCHAR);
        RtlCopyMemory(
            pDest->Buffer,
            pSrc->Buffer,
            pSrc->Length
            );
        pDest->Buffer[pSrc->Length/sizeof(WCHAR)] = L'\0';
        pDest->Length = pSrc->Length;
        return(STATUS_SUCCESS);

    } else
    {
        pDest->MaximumLength = 0;
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

}

NTSTATUS
LsapDuplicateString2(
    OUT PUNICODE_STRING pDest,
    IN PUNICODE_STRING pSrc
    )
/*++

    Same as LsapDuplicateString(), but uses LsapPrivateHeap routines.

--*/
{
    pDest->Length = 0;
    if (pSrc == NULL)
    {
        pDest->Buffer = NULL;
        pDest->MaximumLength = 0;
        return(STATUS_SUCCESS);
    }

    pDest->Buffer = (LPWSTR) LsapAllocatePrivateHeap(pSrc->Length + sizeof(WCHAR));
    if (pDest->Buffer)
    {
        pDest->MaximumLength = pSrc->Length + sizeof(WCHAR);
        RtlCopyMemory(
            pDest->Buffer,
            pSrc->Buffer,
            pSrc->Length
            );
        pDest->Buffer[pSrc->Length/sizeof(WCHAR)] = L'\0';
        pDest->Length = pSrc->Length;
        return(STATUS_SUCCESS);

    } else
    {
        pDest->MaximumLength = 0;
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

}


//+-------------------------------------------------------------------------
//
//  Function:   LsapFreeString
//
//  Synopsis:   Frees a string allocated by LsapDuplicateString
//
//  Effects:
//
//  Arguments:  String - Optionally points to a UNICODE_STRING
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------


VOID
LsapFreeString(
    IN OPTIONAL PUNICODE_STRING String
    )
{
    if (ARGUMENT_PRESENT(String) && String->Buffer != NULL)
    {
        LsapFreeLsaHeap(String->Buffer);
        String->Buffer = NULL;
    }
}


//+-------------------------------------------------------------------------
//
//  Function:   LsapThreadBase
//
//  Synopsis:   Thread start routine
//
//  Effects:    Sets up all the TLS data for a thread, then executes
//              the "real" base function.
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------

void
LsapThreadBase(
    PLSAP_THREAD_START pThread)
{
    NTSTATUS Status;
    PSession        pSession;
    LSAP_THREAD_START tStart = *pThread;

    LsapFreePrivateHeap(pThread);

    SetCurrentSession( pDefaultSession );

    SpmpReferenceSession( pDefaultSession );

    // Initialize Session information:

    SetCurrentPackageId(tStart.dwPackageID);


    DebugLog((DEB_TRACE, "Thread start @%x\n", tStart.lpStart));

    // If this is a debug build, all threads are started in a protective
    // try-except block.  For retail, only threads started by packages
    // will be run this way.  Retail builds, we assume that the SPM is
    // debugged and running correctly, and threads started this way can
    // be trusted.

#if DBG == 0
    if (tStart.dwPackageID != SPMGR_ID)
#endif
    {
        __try
        {
            tStart.lpStart(tStart.lpParm);
        }
        __except (SP_EXCEPTION)
        {
            Status = GetExceptionCode();
            Status = SPException(Status, tStart.dwPackageID);
        }
    }
#if DBG == 0
    else
    {
        tStart.lpStart(tStart.lpParm);
    }
#endif

    pSession = GetCurrentSession();

    SpmpDereferenceSession( pSession );

    if ( pSession != pDefaultSession )
    {
        DebugLog(( DEB_ERROR, "Thread completing in session other than default!\n" ));
    }

    DebugLog((DEB_TRACE, "Thread exit\n" ));

}


//+-------------------------------------------------------------------------
//
//  Function:   LsapCreateThread
//
//  Synopsis:   Creates a thread with all the proper Tls stuff
//
//  Effects:
//
//  Arguments:  same as CreateThread
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
HANDLE NTAPI
LsapCreateThread(
    IN LPSECURITY_ATTRIBUTES lpSA,
    IN ULONG cbStack,
    IN LPTHREAD_START_ROUTINE lpStart,
    IN LPVOID lpvThreadParm,
    IN ULONG fCreate,
    OUT PULONG lpTID
    )
{
    PLSAP_THREAD_START pThread;
    HANDLE hThread;

    pThread = (PLSAP_THREAD_START) LsapAllocatePrivateHeap(sizeof(LSAP_THREAD_START));
    if (pThread == NULL)
    {
        DebugLog((DEB_ERROR, "LsapCreateThread, memory allocation failed.\n"));
        SetLastError(ERROR_OUTOFMEMORY);
        return(NULL);
    }

    pThread->lpStart = lpStart;
    pThread->lpParm = lpvThreadParm;
    pThread->dwPackageID = GetCurrentPackageId();

    hThread = CreateThread(
                        lpSA,
                        cbStack,
                        (LPTHREAD_START_ROUTINE) LsapThreadBase,
                        pThread,
                        fCreate,
                        lpTID
                        );

    if( hThread == NULL )
    {
        DebugLog((DEB_ERROR, "LsapCreateThread, failed thread creation (%lu)\n", GetLastError()));

        LsapFreePrivateHeap( pThread );
    }

    return hThread;
}


//+-------------------------------------------------------------------------
//
//  Function:   LsapGetClientInfo
//
//  Synopsis:
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
LsapGetClientInfo(
    OUT PSECPKG_CLIENT_INFO ClientInfo
    )
{
    PSession pSession = GetCurrentSession();
    PPORT_MESSAGE pMessage;
    HANDLE ClientToken;
    NTSTATUS Status;
    NTSTATUS ExtraStatus ;
    TOKEN_STATISTICS TokenStats;
    ULONG TokenStatsSize = sizeof(TOKEN_STATISTICS);
    PLSA_CALL_INFO CallInfo ;
    HANDLE Thread = NULL ;
    OBJECT_ATTRIBUTES NullAttributes = { 0 };
    KERNEL_USER_TIMES Times ;


    RtlZeroMemory(
        ClientInfo,
        sizeof(SECPKG_CLIENT_INFO)
        );

    if ( !pSession )
    {
        pSession = pDefaultSession ;
    }

    //
    // First, fill in the easy stuff from the session record. If we are
    // running with the LSA session then we want to ignore the LPC message
    // because it may be referring to somebody elses message (we may be
    // being called on behalf of someone doing authenticated RPC in response
    // to an LPC request)
    //

    CallInfo = LsapGetCurrentCall();

    if ( CallInfo )
    {
        ClientInfo->ProcessID  = CallInfo->CallInfo.ProcessId ;
        ClientInfo->ThreadID = CallInfo->CallInfo.ThreadId ;

        if (((pSession->fSession & SESFLAG_TCB_PRIV) != 0) ||
            ((pSession->fSession & SESFLAG_KERNEL) != 0))
        {
            ClientInfo->HasTcbPrivilege = TRUE;
        }
        else
        {
            ClientInfo->HasTcbPrivilege = FALSE;
        }

        if(CallInfo->CachedTokenInfo)
        {
            ClientInfo->LogonId = CallInfo->LogonId;
            ClientInfo->Restricted = CallInfo->Restricted;
            ClientInfo->Impersonating = CallInfo->Impersonating;
            ClientInfo->ImpersonationLevel = CallInfo->ImpersonationLevel;
        
            return STATUS_SUCCESS;
        }

        Status = LsapImpersonateClient();


        if ( !NT_SUCCESS( Status ) )
        {
            if ( Status == STATUS_BAD_IMPERSONATION_LEVEL )
            {
                Status = NtOpenThread(
                            &Thread,
                            THREAD_QUERY_INFORMATION,
                            &NullAttributes,
                            &CallInfo->Message->pmMessage.ClientId );
            }
            else if ( ( Status == STATUS_REPLY_MESSAGE_MISMATCH ) ||
                      ( Status == STATUS_INVALID_CID ) ||
                      ( Status == STATUS_PORT_DISCONNECTED ) )
            {
                //
                // This is a special status returned by the LPC layer to indicate
                // that the client thread has disappeared, or the process is 
                // terminating.  Set a flag to indicate this:
                //
                
                ClientInfo->ClientFlags |= SECPKG_CLIENT_THREAD_TERMINATED ;

                CallInfo->CallInfo.Attributes |= SECPKG_CALL_THREAD_TERM ;
                //
                // Check the process.  If the process has started to exit, set that
                // flag as well.
                //

                ExtraStatus = NtQueryInformationProcess(
                                pSession->hProcess,
                                ProcessTimes,
                                &Times,
                                sizeof( Times ),
                                NULL );

                if ( NT_SUCCESS( ExtraStatus ) )
                {
                    if ( Times.ExitTime.QuadPart != 0 )
                    {
                        ClientInfo->ClientFlags |= SECPKG_CLIENT_PROCESS_TERMINATED ;
                        CallInfo->CallInfo.Attributes |= SECPKG_CALL_PROCESS_TERM ;
                    }
                }

                DebugLog(( DEB_TRACE, "Client %x.%x has terminated\n",
                           ClientInfo->ProcessID, ClientInfo->ThreadID ));

                return STATUS_SUCCESS ;
            }

        }
        else
        {
            Thread = NtCurrentThread();
        }

        if (!NT_SUCCESS(Status))
        {
            DebugLog((DEB_WARN,"Failed to impersonate client: 0x%x\n",Status));
            return(Status);
        }

        Status = NtOpenThreadToken(
                    Thread,
                    TOKEN_QUERY,
                    TRUE,               // use LSA security context
                    &ClientToken
                    );
        if (NT_SUCCESS(Status))
        {
            ClientInfo->Restricted = ( IsTokenRestricted(ClientToken) != 0 );

            Status = NtQueryInformationToken(
                        ClientToken,
                        TokenStatistics,
                        (PVOID) &TokenStats,
                        TokenStatsSize,
                        &TokenStatsSize
                        );
            NtClose(ClientToken);
            if (NT_SUCCESS(Status))
            {
                ClientInfo->LogonId = TokenStats.AuthenticationId;
                ClientInfo->Impersonating = (TokenStats.TokenType == TokenPrimary) ? FALSE : TRUE;
                if( ClientInfo->Impersonating )
                {
                    ClientInfo->ImpersonationLevel = TokenStats.ImpersonationLevel;
                } else {
                    ClientInfo->ImpersonationLevel = SecurityImpersonation;
                }
            }
        }
        RevertToSelf();
        if ( Thread != NtCurrentThread() )
        {
            NtClose( Thread );
        }
        

        if(NT_SUCCESS(Status))
        {
            CallInfo->LogonId = ClientInfo->LogonId;
            CallInfo->Restricted = ClientInfo->Restricted;
            CallInfo->Impersonating = ClientInfo->Impersonating;
            CallInfo->ImpersonationLevel = ClientInfo->ImpersonationLevel;
            CallInfo->CachedTokenInfo = TRUE;
        }

        return(Status);

    }
    else
    {
        ClientInfo->ProcessID = GetCurrentProcessId();
        ClientInfo->ThreadID = GetCurrentThreadId();
        ClientInfo->HasTcbPrivilege = TRUE;
        ClientInfo->Impersonating = FALSE;
        ClientInfo->ImpersonationLevel = SecurityImpersonation;
        ClientInfo->LogonId = SystemLogonId;
        return(STATUS_SUCCESS);
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   LsapGetCallInfo
//
//  Synopsis:   Gets call information
//
//  Arguments:  [Info] --
//
//  History:    10-06-96   RichardW   Created
//
//  Notes:
//
//----------------------------------------------------------------------------
BOOLEAN
LsapGetCallInfo(
    PSECPKG_CALL_INFO   Info
    )
{
    PLSA_CALL_INFO CallInfo ;

    CallInfo = LsapGetCurrentCall() ;

    if ( CallInfo )
    {
        *Info = CallInfo->CallInfo ;
        if ( CallInfo->InProcCall )
        {
            Info->Attributes |= SECPKG_CALL_IN_PROC ;
        }

        return TRUE ;
    } else {
        Info->ProcessId = GetCurrentProcessId();
        Info->ThreadId = GetCurrentThreadId();
        Info->Attributes = SECPKG_CALL_IN_PROC |
                                SECPKG_CALL_IS_TCB ;
        Info->CallCount = 0;

        return TRUE;
    }

}

//+-------------------------------------------------------------------------
//
//  Function:   LsapMapClientBuffer
//
//  Synopsis:   Maps a client's SecBuffer into the caller's address space
//
//  Effects:    Clears the SECBUFFER_UNMAPPED field of the BufferType of
//              the return SecBuffer
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:      Doesn't modify pOutput until the end, so it is o.k. to pass
//              the same thing for pInput and pOutput.
//
//
//--------------------------------------------------------------------------
NTSTATUS
LsapMapClientBuffer(
    IN PSecBuffer pInput,
    OUT PSecBuffer pOutput
    )
{
    NTSTATUS Status = STATUS_SUCCESS ;
    SecBuffer Output ;
    Output = *pInput ;
    PLSA_CALL_INFO CallInfo = LsapGetCurrentCall();

    //
    // If the buffer is already mapped or it doesn't exist (is NULL) we
    // are done.
    //

    if (!(pInput->BufferType & SECBUFFER_UNMAPPED) ||
        !pInput->pvBuffer)
    {
        return( STATUS_SUCCESS );
    }
    else
    {
        Output.BufferType &= ~SECBUFFER_UNMAPPED;
    }

    if ( pInput->BufferType & SECBUFFER_KERNEL_MAP )
    {
        //
        // This one is already in process space
        //

        if ( ( CallInfo->CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE ) == 0 )
        {
            //
            // If this call did not come from kernel mode with
            // the kernel-pool flag set, then this is an attempted
            // hack on the LSA.  Reject it.
            //

            Status = STATUS_ACCESS_VIOLATION ;
            
        }
        else
        {
            //
            // The buffer is already in memory.  Mark the call as 
            // using kernel-pool memory, so we allocate correctly
            // on the return.
            //

            CallInfo->Flags |= CALL_FLAG_KERNEL_POOL ;
            DebugLog((DEB_TRACE_SPECIAL, "Kernel Pool Map at %p [%x,%x]\n", 
                        Output.pvBuffer, Output.BufferType, Output.cbBuffer ));
        }
            
        
    }
    else
    {
        Output.pvBuffer = LsapAllocateLsaHeap( Output.cbBuffer );

        if ( Output.pvBuffer != NULL )
        {
            Status = LsapCopyFromClient(
                pInput->pvBuffer,
                Output.pvBuffer,
                Output.cbBuffer );

            if ( !NT_SUCCESS( Status ) )
            {
                LsapFreeLsaHeap(Output.pvBuffer);
            }
            
        }
        else
        {

            Status = STATUS_NO_MEMORY ;
        }
        

    }

    if ( NT_SUCCESS( Status ) )
    {
        *pOutput = Output ;
        
    }

    return( Status );
}



//+-------------------------------------------------------------------------
//
//  Function:   LsaICallPackage
//
//  Synopsis:   Function to call another security package
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

extern "C"
NTSTATUS
LsaICallPackage(
    IN PUNICODE_STRING AuthenticationPackage,
    IN PVOID ProtocolSubmitBuffer,
    IN ULONG SubmitBufferLength,
    OUT PVOID *ProtocolReturnBuffer,
    OUT PULONG ReturnBufferLength,
    OUT PNTSTATUS ProtocolStatus
    )
{

    return LsaICallPackageEx( AuthenticationPackage,
                              ProtocolSubmitBuffer,   // client buffer base is same as local buffer
                              ProtocolSubmitBuffer,
                              SubmitBufferLength,
                              ProtocolReturnBuffer,
                              ReturnBufferLength,
                              ProtocolStatus );

}

//+-------------------------------------------------------------------------
//
//  Function:   LsaICallPackageEx
//
//  Synopsis:   Function to call another security package
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

extern "C"
NTSTATUS
LsaICallPackageEx(
    IN PUNICODE_STRING AuthenticationPackage,
    IN PVOID ClientBufferBase,
    IN PVOID ProtocolSubmitBuffer,
    IN ULONG SubmitBufferLength,
    OUT PVOID *ProtocolReturnBuffer,
    OUT PULONG ReturnBufferLength,
    OUT PNTSTATUS ProtocolStatus
    )
{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_SECURITY_PACKAGE Package;
    PSession OldSession;

    Package = SpmpLookupPackageAndRequest(
                AuthenticationPackage,
                SP_ORDINAL_CALLPACKAGE
                );

    if (Package == NULL)
    {
        DebugLog((DEB_WARN,"LsapCallPackage failed: package %wZ not found\n",
            AuthenticationPackage ));
        Status = STATUS_NO_SUCH_PACKAGE;
        goto Cleanup;
    }

    //
    // Set the session to be the LSA's so calls to allocate memory
    // will allocate in the correct process
    //

    OldSession = GetCurrentSession();
    SetCurrentSession( pDefaultSession );

    Status = Package->FunctionTable.CallPackage(
                NULL,
                ProtocolSubmitBuffer,
                ClientBufferBase,
                SubmitBufferLength,
                ProtocolReturnBuffer,
                ReturnBufferLength,
                ProtocolStatus
                );

    //
    // Restore our original session
    //

    SetCurrentSession( OldSession );


Cleanup:

    return(Status);

}

//+-------------------------------------------------------------------------
//
//  Function:   LsaICallPackagePassthrough
//
//  Synopsis:   Function to call another security package for pass-through request
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

extern "C"
NTSTATUS
LsaICallPackagePassthrough(
    IN PUNICODE_STRING AuthenticationPackage,
    IN PVOID ClientBufferBase,
    IN PVOID ProtocolSubmitBuffer,
    IN ULONG SubmitBufferLength,
    OUT PVOID *ProtocolReturnBuffer,
    OUT PULONG ReturnBufferLength,
    OUT PNTSTATUS ProtocolStatus
    )
{
    NTSTATUS Status = STATUS_SUCCESS;
    PLSAP_SECURITY_PACKAGE Package;
    PSession OldSession;

    Package = SpmpLookupPackageAndRequest(
                AuthenticationPackage,
                SP_ORDINAL_CALLPACKAGE
                );

    if (Package == NULL)
    {
        DebugLog((DEB_WARN,"LsapCallPackage failed: package %wZ not found\n",
            AuthenticationPackage ));
        Status = STATUS_NO_SUCH_PACKAGE;
        goto Cleanup;
    }

    //
    // Set the session to be the LSA's so calls to allocate memory
    // will allocate in the correct process
    //

    OldSession = GetCurrentSession();
    SetCurrentSession( pDefaultSession );

    Status = Package->FunctionTable.CallPackagePassthrough(
                NULL,
                ProtocolSubmitBuffer,
                ClientBufferBase,
                SubmitBufferLength,
                ProtocolReturnBuffer,
                ReturnBufferLength,
                ProtocolStatus
                );

    //
    // Restore our original session
    //

    SetCurrentSession( OldSession );


Cleanup:

    return(Status);

}

extern "C"
VOID
LsaIFreeReturnBuffer(
    IN PVOID Buffer
    )
/*++

Routine Description:

    Some of the LSA authentication services allocate memory buffers to
    hold returned information.  This service is used to free those buffers
    when no longer needed.

Arguments:

    Buffer - Supplies a pointer to the return buffer to be freed.

Return Status:

    STATUS_SUCCESS - Indicates the service completed successfully.

    Others - returned by NtFreeVirtualMemory().

--*/

{

    SIZE_T Length;

    Length = 0;

    DebugLog(( DEB_TRACE_HELPERS, "LsaIFreeReturnBuffer - freeing VM at %x\n", Buffer ));
    if (((ULONG_PTR) Buffer & 0xfff) != 0)
    {
        DbgPrint("Freeing non-page address: %p\n",Buffer);
        DbgBreakPoint();
    }

    NtFreeVirtualMemory(
        NtCurrentProcess(),
        &Buffer,
        &Length,
        MEM_RELEASE
        );

}

NTSTATUS
LsaClientCallback(
    PCHAR   Callback,
    ULONG_PTR Argument1,
    ULONG_PTR Argument2,
    PSecBuffer Input,
    PSecBuffer Output
    )
{
    PSession Session ;
    ULONG Type ;

    Session = GetCurrentSession();

    if ( !Session )
    {
        Session = pDefaultSession ;
    }
    if ( !Session->hPort &&
         ((Session->fSession & SESFLAG_DEFAULT) == 0) )
    {
        return SEC_E_INVALID_HANDLE ;
    }

    if ( (ULONG_PTR) Callback < 0x00010000 )
    {
        Type = SPM_CALLBACK_PACKAGE ;
    }
    else
    {
        Type = SPM_CALLBACK_EXPORT ;
    }

    return LsapClientCallback(  Session,
                                Type,
                                Callback,
                                (PVOID) Argument1,
                                (PVOID) Argument2,
                                Input,
                                Output );
}


#if 0
BOOL
LsapCaptureAuthData(
    PVOID   pvAuthData,
    BOOLEAN DesiredAnsi,
    PSEC_WINNT_AUTH_IDENTITY * AuthData
    )
{
    SEC_WINNT_AUTH_IDENTITY Auth ;
    PSEC_WINNT_AUTH_IDENTITY pAuth ;
    SECURITY_STATUS Status ;
    ULONG   TotalSize ;

    PWSTR   CurrentW ;
    PSTR    CurrentA ;
    PVOID   Current ;

    PWSTR   ConvertBufferW ;
    PSTR    ConvertBufferA ;
    PVOID   Convert;

    ULONG   Longest ;


    ZeroMemory( &Auth, sizeof( Auth ) );

    Status = LsapCopyFromClientBuffer(
                            NULL,
                            sizeof( SEC_WINNT_AUTH_IDENTITY ),
                            & Auth,
                            pvAuthData );

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

    Longest = Auth.UserLength ;
    Longest = max( Longest, Auth.DomainLength );
    Longest = max( Longest, Auth.PasswordLength );

    Longest = (Longest + 1) * sizeof(WCHAR);

    //
    // Always go with the extra, so we can handle DBCS
    //

    TotalSize = sizeof( SEC_WINNT_AUTH_IDENTITY ) +
                ( Auth.UserLength + 1 +
                  Auth.DomainLength + 1 +
                  Auth.PasswordLength + 1 ) * sizeof( WCHAR );


    pAuth = (PSEC_WINNT_AUTH_IDENTITY) LsapAllocateLsaHeap( TotalSize );

    if ( !pAuth )
    {
        return FALSE ;
    }

    ConvertBufferW = NULL ;
    ConvertBufferA = NULL ;
    Convert = NULL ;
    CurrentA = NULL ;
    CurrentW = NULL ;

    if ( Auth.Flags & SEC_WINNT_AUTH_IDENTITY_ANSI )
    {
        if ( !DesiredAnsi )
        {
            ConvertBufferA = (PSTR) LocalAlloc( LMEM_FIXED, Longest );
            Convert = ConvertBufferA ;
            CurrentW = (PWSTR) (pAuth + 1);
        }
        else
        {
            CurrentA = (PSTR) (pAuth + 1);
        }
    }
    else
    {
        if ( DesiredAnsi )
        {
            ConvertBufferW = (PWSTR) LocalAlloc( LMEM_FIXED, Longest );
            CurrentA = (PSTR) (pAuth + 1);
        }
        else
        {
            CurrentW = (PWSTR) pAuth + 1);
        }
    }

    pAuth->Flags = Auth.Flags ;

    CurrentW = (PWSTR) (pAuth + 1);
    CurrentA = (PSTR) (pAuth + 1);
    Current = CurrentW ;

    if ( Auth.User )
    {
        pAuth->User = Current ;

        Status = LsapCopyFromClientBuffer(
                            NULL,
                            (Auth.Flags & SEC_WINNT_AUTH_IDENTITY_ANSI ?
                                ( Auth.UserLength + 1 ) :
                                ( (Auth.UserLength + 1 ) * sizeof( WCHAR ) ) ),
                            (Convert ? Convert : Current ),
                            Auth.User );

        if ( Convert )
        {
            if ( ConvertBufferA )
            {

            }

        }
        else
        {
            pAuth->UserLength = Auth.UserLength ;
        }

        Status = LsaTable->CopyFromClientBuffer(
                            NULL,
                            (Auth.UserLength + 1) * sizeof(WCHAR) ,
                            pAuth->User,
                            Auth.User );

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

        Current += Auth.UserLength + 1;
    }

    if ( Auth.Domain )
    {
        pAuth->Domain = Current ;
        pAuth->DomainLength = Auth.DomainLength ;

        Status = LsaTable->CopyFromClientBuffer(
                            NULL,
                            (Auth.DomainLength + 1) * sizeof( WCHAR ),
                            pAuth->Domain,
                            Auth.Domain );

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

        Current += Auth.DomainLength + 1;

    }

    if ( Auth.Password )
    {
        pAuth->Password = Current ;
        pAuth->PasswordLength = Auth.PasswordLength ;

        Status = LsaTable->CopyFromClientBuffer(
                            NULL,
                            (Auth.PasswordLength + 1) * sizeof( WCHAR ),
                            pAuth->Password,
                            Auth.Password );

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

        Current += Auth.PasswordLength + 1;

    }

    *AuthData = pAuth ;

    return TRUE ;

Error_Cleanup:

    LocalFree( pAuth );

    return FALSE ;

}
#endif


//+-------------------------------------------------------------------------
//
//  Function:   LsapUpdateCredentials
//
//  Synopsis:   This function provides a mechanism for one package to notify
//              another package that the credentials for a logon session
//              have changed.
//
//  Effects:
//
//  Arguments:  PrimaryCredentials - Primary information about the user.
//                      All fields may be NULL but the LogonId
//              Credentials - Array of credentials for different packages
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

NTSTATUS
LsapUpdateCredentials(
    IN PSECPKG_PRIMARY_CRED PrimaryCredentials,
    IN OPTIONAL PSECPKG_SUPPLEMENTAL_CRED_ARRAY Credentials
    )
{
    return(LsapUpdateCredentialsWorker(
                (SECURITY_LOGON_TYPE) 0,              // no logon type
                NULL,           // no account name
                PrimaryCredentials,
                Credentials ));
}


//+-------------------------------------------------------------------------
//
//  Function:   LsapUpdateCredentialsWorker
//
//  Synopsis:   Worker function for updated credentials - calls all package
//              with specified credentials
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------


NTSTATUS
LsapUpdateCredentialsWorker(
    IN SECURITY_LOGON_TYPE LogonType,
    IN PUNICODE_STRING AccountName,
    IN PSECPKG_PRIMARY_CRED PrimaryCredentials,
    IN OPTIONAL PSECPKG_SUPPLEMENTAL_CRED_ARRAY Credentials
    )
{
    NTSTATUS Status;
    ULONG_PTR CurrentPackageId;
    PLSAP_SECURITY_PACKAGE SupplementalPackage;
    SupplementalPackage = SpmpIteratePackagesByRequest( NULL, SP_ORDINAL_ACCEPTCREDS );

    CurrentPackageId = GetCurrentPackageId();
    while (SupplementalPackage)
    {

        if (SupplementalPackage->dwPackageID != CurrentPackageId)
        {
            ULONG Index;
            PSECPKG_SUPPLEMENTAL_CRED SuppCreds;

            //
            // For all packages,  do the accept call so the
            // package can associate the credentials with
            // the logon session.
            //

            DebugLog((DEB_TRACE_WAPI, "Whacking package %ws with %x:%x = %wZ\n",
                        SupplementalPackage->Name.Buffer,
                        PrimaryCredentials->LogonId.HighPart, PrimaryCredentials->LogonId.LowPart,
                        AccountName));


            SetCurrentPackageId( SupplementalPackage->dwPackageID );

            //
            // Find any supplmental credentials
            //

            SuppCreds = NULL;
            if (Credentials != NULL)
            {
                for (Index = 0; Index < Credentials->CredentialCount ; Index++ ) {
                    if (RtlEqualUnicodeString(
                            &Credentials->Credentials[Index].PackageName,
                            &SupplementalPackage->Name,
                            TRUE))
                    {
                        SuppCreds = &Credentials->Credentials[Index];
                        break;
                    }

                }
            }

            __try
            {
                Status = SupplementalPackage->FunctionTable.AcceptCredentials(
                                LogonType,
                                AccountName,
                                PrimaryCredentials,
                                SuppCreds
                                );
            }
            __except (SP_EXCEPTION)
            {
                Status = GetExceptionCode();
                Status = SPException(Status, SupplementalPackage->dwPackageID);
            }


            // Note:  if an exception occurs, we don't fail the logon, we just
            // do the magic on the package that blew.  If the package blows,
            // the other packages may succeed, and so the user may not be able
            // to use that provider.

        }

        SupplementalPackage = SpmpIteratePackagesByRequest(
                                SupplementalPackage,
                                SP_ORDINAL_ACCEPTCREDS
                                );

    }
    return(STATUS_SUCCESS);
}