//+---------------------------------------------------------------------------
//
//  Microsoft Windows NT Security
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       catcache.cpp
//
//  Contents:   Implementation of Catalog Cache (see catcache.h for details)
//
//  History:    26-May-98    kirtd    Created
//
//----------------------------------------------------------------------------
#include <global.hxx>
//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::Initialize, public
//
//  Synopsis:   initialize the cache
//
//----------------------------------------------------------------------------
BOOL
CCatalogCache::Initialize ()
{
    LRU_CACHE_CONFIG Config;

    __try
    {
        InitializeCriticalSection( &m_Lock );
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return ( FALSE );
    }
    
    memset( &Config, 0, sizeof( Config ) );

    m_hCache = NULL;
    Config.dwFlags = LRU_CACHE_NO_SERIALIZE;
    Config.pfnFree = CatalogCacheFreeEntryData;
    Config.pfnHash = CatalogCacheHashIdentifier;
    Config.cBuckets = DEFAULT_CATALOG_CACHE_BUCKETS;
    Config.MaxEntries = DEFAULT_CATALOG_CACHE_MAX_ENTRIES;

    return( I_CryptCreateLruCache( &Config, &m_hCache ) );
}

//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::Uninitialize, public
//
//  Synopsis:   uninitialize the cache
//
//----------------------------------------------------------------------------
VOID
CCatalogCache::Uninitialize ()
{
    if ( m_hCache != NULL )
    {
        I_CryptFreeLruCache( m_hCache, 0, NULL );
    }

    DeleteCriticalSection( &m_Lock );
}

//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::IsCacheableWintrustCall, public
//
//  Synopsis:   is this a cacheable call
//
//----------------------------------------------------------------------------
BOOL
CCatalogCache::IsCacheableWintrustCall (WINTRUST_DATA* pWintrustData)
{
    if ( pWintrustData->dwUnionChoice != WTD_CHOICE_CATALOG )
    {
        return( FALSE );
    }

    if ( _ISINSTRUCT( WINTRUST_DATA, pWintrustData->cbStruct, hWVTStateData ) )
    {
        if ( ( pWintrustData->dwStateAction == WTD_STATEACTION_AUTO_CACHE ) ||
             ( pWintrustData->dwStateAction == WTD_STATEACTION_AUTO_CACHE_FLUSH ) )
        {
            return( TRUE );
        }
    }

    return( FALSE );
}

//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::AdjustWintrustDataToCachedState, public
//
//  Synopsis:   adjust the wintrust data structure
//
//----------------------------------------------------------------------------
VOID
CCatalogCache::AdjustWintrustDataToCachedState (
                     WINTRUST_DATA* pWintrustData,
                     PCATALOG_CACHED_STATE pCachedState,
                     BOOL fUndoAdjustment
                     )
{
    PCRYPT_PROVIDER_DATA pProvData;

    if ( fUndoAdjustment == FALSE )
    {
        pWintrustData->dwStateAction = WTD_STATEACTION_VERIFY;

        if ( pCachedState != NULL )
        {
            pWintrustData->hWVTStateData = pCachedState->hStateData;

            pProvData = WTHelperProvDataFromStateData( pCachedState->hStateData );
            pProvData->pWintrustData = pWintrustData;
        }
        else
        {
            pWintrustData->hWVTStateData = NULL;
        }
    }
    else
    {
        if ( pCachedState != NULL )
        {
            pProvData = WTHelperProvDataFromStateData( pCachedState->hStateData );
            pProvData->pWintrustData = NULL;
        }

        pWintrustData->dwStateAction = WTD_STATEACTION_AUTO_CACHE;
        pWintrustData->hWVTStateData = NULL;
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::CreateCachedStateFromWintrustData, public
//
//  Synopsis:   create cached state
//
//----------------------------------------------------------------------------
BOOL
CCatalogCache::CreateCachedStateFromWintrustData (
                     WINTRUST_DATA* pWintrustData,
                     PCATALOG_CACHED_STATE* ppCachedState
                     )
{
    BOOL                  fResult;
    PCATALOG_CACHED_STATE pCachedState;
    CRYPT_DATA_BLOB       Identifier;

    PCRYPT_PROVIDER_DATA  pProvData;

    if ( pWintrustData->hWVTStateData == NULL )
    {
        return( FALSE );
    }

    pProvData = WTHelperProvDataFromStateData( pWintrustData->hWVTStateData );

    if ( ( pProvData->padwTrustStepErrors[ TRUSTERROR_STEP_FINAL_INITPROV ] != ERROR_SUCCESS ) ||
         ( ( pProvData->padwTrustStepErrors[ TRUSTERROR_STEP_FINAL_OBJPROV ] != ERROR_SUCCESS ) &&
           ( pProvData->padwTrustStepErrors[ TRUSTERROR_STEP_FINAL_OBJPROV ] != TRUST_E_BAD_DIGEST ) ) ||
         ( pProvData->padwTrustStepErrors[ TRUSTERROR_STEP_FINAL_SIGPROV ] != ERROR_SUCCESS ) ||
         ( pProvData->hMsg == NULL ) )
    {
        return( FALSE );
    }

    assert( pProvData->hMsg != NULL );

    pCachedState = new CATALOG_CACHED_STATE;
    if ( pCachedState != NULL )
    {
        pCachedState->hStateData = pWintrustData->hWVTStateData;
        pCachedState->hEntry = NULL;

        Identifier.cbData = wcslen(
                               pWintrustData->pCatalog->pcwszCatalogFilePath
                               );

        Identifier.cbData *= sizeof( WCHAR );

        Identifier.pbData = (LPBYTE)pWintrustData->pCatalog->pcwszCatalogFilePath;

        fResult = I_CryptCreateLruEntry(
                         m_hCache,
                         &Identifier,
                         pCachedState,
                         &pCachedState->hEntry
                         );
    }
    else
    {
        SetLastError( E_OUTOFMEMORY );
        fResult = FALSE;
    }

    if ( fResult == TRUE )
    {
        *ppCachedState = pCachedState;
    }
    else
    {
        delete pCachedState;
    }

    return( fResult );
}

//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::ReleaseCachedState, public
//
//  Synopsis:   release the cached state
//
//----------------------------------------------------------------------------
VOID
CCatalogCache::ReleaseCachedState (PCATALOG_CACHED_STATE pCachedState)
{
    if ( pCachedState == NULL )
    {
        return;
    }

    I_CryptReleaseLruEntry( pCachedState->hEntry );
}

//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::AddCachedState, public
//
//  Synopsis:   add cached state
//
//----------------------------------------------------------------------------
VOID
CCatalogCache::AddCachedState (PCATALOG_CACHED_STATE pCachedState)
{
    I_CryptInsertLruEntry( pCachedState->hEntry, NULL );
}

//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::RemoveCachedState, public
//
//  Synopsis:   remove cached state
//
//----------------------------------------------------------------------------
VOID
CCatalogCache::RemoveCachedState (PCATALOG_CACHED_STATE pCachedState)
{
    I_CryptRemoveLruEntry( pCachedState->hEntry, 0, NULL );
}

//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::RemoveCachedState, public
//
//  Synopsis:   remove cached state
//
//----------------------------------------------------------------------------
VOID
CCatalogCache::RemoveCachedState (WINTRUST_DATA* pWintrustData)
{
    PCATALOG_CACHED_STATE pCachedState;

    pCachedState = FindCachedState( pWintrustData );

    if ( pCachedState != NULL )
    {
        RemoveCachedState( pCachedState );
        ReleaseCachedState( pCachedState );
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::FindCachedState, public
//
//  Synopsis:   find cached state, the state is addref'd via the entry
//
//----------------------------------------------------------------------------
PCATALOG_CACHED_STATE
CCatalogCache::FindCachedState (WINTRUST_DATA* pWintrustData)
{
    PCATALOG_CACHED_STATE pCachedState;
    CRYPT_DATA_BLOB       Identifier;
    HLRUENTRY             hEntry;

    Identifier.cbData = wcslen(
                           pWintrustData->pCatalog->pcwszCatalogFilePath
                           );

    Identifier.cbData *= sizeof( WCHAR );

    Identifier.pbData = (LPBYTE)pWintrustData->pCatalog->pcwszCatalogFilePath;

    pCachedState = (PCATALOG_CACHED_STATE)I_CryptFindLruEntryData(
                                                 m_hCache,
                                                 &Identifier,
                                                 &hEntry
                                                 );

    return( pCachedState );
}

//+---------------------------------------------------------------------------
//
//  Member:     CCatalogCache::FlushCache, public
//
//  Synopsis:   flush the cache
//
//----------------------------------------------------------------------------
VOID
CCatalogCache::FlushCache ()
{
    I_CryptFlushLruCache( m_hCache, 0, NULL );
}

//+---------------------------------------------------------------------------
//
//  Function:   CatalogCacheFreeEntryData
//
//  Synopsis:   free entry data
//
//----------------------------------------------------------------------------
VOID WINAPI
CatalogCacheFreeEntryData (LPVOID pvData)
{
    PCATALOG_CACHED_STATE pCachedState = (PCATALOG_CACHED_STATE)pvData;
    WINTRUST_DATA         WintrustData;
    GUID                  ActionGuid;

    memset( &ActionGuid, 0, sizeof( ActionGuid ) );

    memset( &WintrustData, 0, sizeof( WintrustData ) );
    WintrustData.cbStruct = sizeof( WintrustData );
    WintrustData.dwStateAction = WTD_STATEACTION_CLOSE;
    WintrustData.hWVTStateData = pCachedState->hStateData;

    WinVerifyTrust( NULL, &ActionGuid, &WintrustData );

    delete pCachedState;
}

//+---------------------------------------------------------------------------
//
//  Function:   CatalogCacheHashIdentifier
//
//  Synopsis:   hash the name
//
//----------------------------------------------------------------------------
DWORD WINAPI
CatalogCacheHashIdentifier (PCRYPT_DATA_BLOB pIdentifier)
{
    DWORD  dwHash = 0;
    DWORD  cb = pIdentifier->cbData;
    LPBYTE pb = pIdentifier->pbData;

    while ( cb-- )
    {
        if ( dwHash & 0x80000000 )
        {
            dwHash = ( dwHash << 1 ) | 1;
        }
        else
        {
            dwHash = dwHash << 1;
        }

        dwHash += *pb++;
    }

    return( dwHash );
}