|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 2001.
//
// File: mapcache.c
//
// Contents: Routines to manage a cache that holds issuer names we've
// recently processed via many-to-one certificate mapping.
// This is a negative cache, so only issuers that have failed
// mapping are stored in the cache.
//
// The purpose of this cache is to avoid attempting to map
// the same issuers over and over again. This is especially
// important now that the many-to-one mapper walks up the
// certificate chain, attempting to map each CA as it goes.
//
// This code is active on DC machines only.
//
// Functions:
//
// History: 04-12-2001 jbanes Created
//
//----------------------------------------------------------------------------
#include "spbase.h"
#include <mapper.h>
ISSUER_CACHE IssuerCache;
SP_STATUS SPInitIssuerCache(void) { DWORD i; NTSTATUS Status;
memset(&IssuerCache, 0, sizeof(IssuerCache)); IssuerCache.dwLifespan = ISSUER_CACHE_LIFESPAN; IssuerCache.dwCacheSize = ISSUER_CACHE_SIZE; IssuerCache.dwMaximumEntries = ISSUER_CACHE_SIZE;
InitializeListHead(&IssuerCache.EntryList);
__try { RtlInitializeResource(&IssuerCache.Lock); } __except(EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } IssuerCache.LockInitialized = TRUE;
IssuerCache.Cache = (PLIST_ENTRY)SPExternalAlloc(IssuerCache.dwCacheSize * sizeof(LIST_ENTRY)); if(IssuerCache.Cache == NULL) { Status = SP_LOG_RESULT(STATUS_NO_MEMORY); goto cleanup; }
for(i = 0; i < IssuerCache.dwCacheSize; i++) { InitializeListHead(&IssuerCache.Cache[i]); }
Status = STATUS_SUCCESS;
cleanup:
if(!NT_SUCCESS(Status)) { SPShutdownIssuerCache(); }
return Status; }
void SPShutdownIssuerCache(void) { ISSUER_CACHE_ENTRY *pItem; PLIST_ENTRY pList;
if(IssuerCache.LockInitialized) { RtlAcquireResourceExclusive(&IssuerCache.Lock, TRUE); }
if(IssuerCache.Cache != NULL) { pList = IssuerCache.EntryList.Flink;
while(pList != &IssuerCache.EntryList) { pItem = CONTAINING_RECORD(pList, ISSUER_CACHE_ENTRY, EntryList.Flink); pList = pList->Flink;
SPDeleteIssuerEntry(pItem); }
SPExternalFree(IssuerCache.Cache); }
if(IssuerCache.LockInitialized) { RtlDeleteResource(&IssuerCache.Lock); IssuerCache.LockInitialized = FALSE; } }
void SPPurgeIssuerCache(void) { ISSUER_CACHE_ENTRY *pItem; PLIST_ENTRY pList;
if(!IssuerCache.LockInitialized) { return; }
if(IssuerCache.dwUsedEntries == 0) { return; }
RtlAcquireResourceExclusive(&IssuerCache.Lock, TRUE);
pList = IssuerCache.EntryList.Flink;
while(pList != &IssuerCache.EntryList) { pItem = CONTAINING_RECORD(pList, ISSUER_CACHE_ENTRY, EntryList.Flink); pList = pList->Flink;
RemoveEntryList(&pItem->IndexEntryList); RemoveEntryList(&pItem->EntryList); IssuerCache.dwUsedEntries--;
SPDeleteIssuerEntry(pItem); }
RtlReleaseResource(&IssuerCache.Lock); }
void SPDeleteIssuerEntry( ISSUER_CACHE_ENTRY *pItem) { if(pItem == NULL) { return; }
if(pItem->pbIssuer) { LogDistinguishedName(DEB_TRACE, "Delete from cache: %s\n", pItem->pbIssuer, pItem->cbIssuer); SPExternalFree(pItem->pbIssuer); }
SPExternalFree(pItem); }
DWORD ComputeIssuerCacheIndex( PBYTE pbIssuer, DWORD cbIssuer) { ULONG Index = 0; ULONG i;
if(pbIssuer == NULL) { Index = 0; } else { for(i = 0; i < cbIssuer; i++) { Index += (pbIssuer[i] ^ 0x55); }
Index %= IssuerCache.dwCacheSize; }
return Index; }
BOOL SPFindIssuerInCache( PBYTE pbIssuer, DWORD cbIssuer) { DWORD Index; DWORD timeNow; ISSUER_CACHE_ENTRY *pItem; PLIST_ENTRY pList; BOOL fFound = FALSE;
if(pbIssuer == NULL || cbIssuer == 0) { return FALSE; }
if(!IssuerCache.LockInitialized) { return FALSE; }
//
// Compute the cache index.
//
Index = ComputeIssuerCacheIndex(pbIssuer, cbIssuer);
Index %= IssuerCache.dwCacheSize;
//
// Search through the cache entries at the computed index.
//
timeNow = GetTickCount();
RtlAcquireResourceShared(&IssuerCache.Lock, TRUE);
pList = IssuerCache.Cache[Index].Flink;
while(pList != &IssuerCache.Cache[Index]) { pItem = CONTAINING_RECORD(pList, ISSUER_CACHE_ENTRY, IndexEntryList.Flink); pList = pList->Flink ;
// Has this item expired?
if(HasTimeElapsed(pItem->CreationTime, timeNow, IssuerCache.dwLifespan)) { continue; }
// Does the issuer name match?
if(cbIssuer != pItem->cbIssuer) { continue; } if(memcmp(pbIssuer, pItem->pbIssuer, cbIssuer) != 0) { continue; }
// Found item in cache!!
fFound = TRUE; break; }
RtlReleaseResource(&IssuerCache.Lock);
return fFound; }
void SPExpireIssuerCacheElements(void) { ISSUER_CACHE_ENTRY *pItem; PLIST_ENTRY pList; BOOL fDeleteEntry; DWORD timeNow;
if(!IssuerCache.LockInitialized) { return; }
if(IssuerCache.dwUsedEntries == 0) { return; }
timeNow = GetTickCount();
RtlAcquireResourceExclusive(&IssuerCache.Lock, TRUE);
pList = IssuerCache.EntryList.Flink;
while(pList != &IssuerCache.EntryList) { pItem = CONTAINING_RECORD(pList, ISSUER_CACHE_ENTRY, EntryList.Flink); pList = pList->Flink;
fDeleteEntry = FALSE;
// Mark all expired cache entries as non-resumable.
if(HasTimeElapsed(pItem->CreationTime, timeNow, IssuerCache.dwLifespan)) { fDeleteEntry = TRUE; }
// If the cache has gotten too large, then expire elements early. The
// cache elements are sorted by creation time, so the oldest
// entries will be expired first.
if(IssuerCache.dwUsedEntries > IssuerCache.dwMaximumEntries) { fDeleteEntry = TRUE; }
// Remove this entry from the cache.
if(fDeleteEntry) { RemoveEntryList(&pItem->IndexEntryList); RemoveEntryList(&pItem->EntryList); IssuerCache.dwUsedEntries--;
SPDeleteIssuerEntry(pItem); } }
RtlReleaseResource(&IssuerCache.Lock); }
void SPAddIssuerToCache( PBYTE pbIssuer, DWORD cbIssuer) { DWORD Index; DWORD timeNow; ISSUER_CACHE_ENTRY *pItem = NULL;
if(pbIssuer == NULL || cbIssuer == 0) { return; }
if(!IssuerCache.LockInitialized) { return; }
//
// Determine if the issuer is already in the cache. This isn't particularly
// thread-safe, so it's possible that the same issuer might sneak into
// the cache multiple times, but that's harmless.
//
if(SPFindIssuerInCache(pbIssuer, cbIssuer)) { return; }
//
// Compute the cache index.
//
Index = ComputeIssuerCacheIndex(pbIssuer, cbIssuer);
Index %= IssuerCache.dwCacheSize;
timeNow = GetTickCount();
//
// Allocate a new cache entry.
//
pItem = SPExternalAlloc(sizeof(ISSUER_CACHE_ENTRY)); if(pItem == NULL) { SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); return; }
//
// Fill in the cache internal fields.
//
pItem->pbIssuer = SPExternalAlloc(cbIssuer); if(pItem->pbIssuer == NULL) { SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY); SPExternalFree(pItem); return; }
pItem->cbIssuer = cbIssuer; memcpy(pItem->pbIssuer, pbIssuer, cbIssuer);
pItem->CreationTime = timeNow;
//
// Add the new entry to the cache.
//
LogDistinguishedName(DEB_TRACE, "Add to cache: %s\n", pbIssuer, cbIssuer);
RtlAcquireResourceExclusive(&IssuerCache.Lock, TRUE);
InsertTailList(&IssuerCache.Cache[Index], &pItem->IndexEntryList); InsertTailList(&IssuerCache.EntryList, &pItem->EntryList); IssuerCache.dwUsedEntries++;
RtlReleaseResource(&IssuerCache.Lock); }
|