Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1190 lines
34 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
cmsecache.c
Abstract:
This module implements the security cache.
Author:
Dragos C. Sambotin (dragoss) 09-Sep-1999
--*/
#include "cmp.h"
#define SECURITY_CACHE_GROW_INCREMENTS 0x10
#ifdef HIVE_SECURITY_STATS
ULONG
CmpCheckForSecurityDuplicates(
IN OUT PCMHIVE CmHive
);
#endif
BOOLEAN
CmpFindMatchingDescriptorCell(
IN PCMHIVE CmHive,
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN ULONG Type,
OUT PHCELL_INDEX MatchingCell,
OUT OPTIONAL PCM_KEY_SECURITY_CACHE *CachedSecurityPointer
);
PCM_KEY_SECURITY_CACHE
CmpFindReusableCellFromCache(IN PCMHIVE CmHive,
IN HCELL_INDEX SecurityCell,
IN ULONG PreviousCount
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,CmpSecConvKey)
#pragma alloc_text(PAGE,CmpInitSecurityCache)
#pragma alloc_text(PAGE,CmpDestroySecurityCache)
#pragma alloc_text(PAGE,CmpRebuildSecurityCache)
#pragma alloc_text(PAGE,CmpAddSecurityCellToCache)
#pragma alloc_text(PAGE,CmpFindSecurityCellCacheIndex)
#pragma alloc_text(PAGE,CmpAdjustSecurityCacheSize)
#pragma alloc_text(PAGE,CmpRemoveFromSecurityCache)
#pragma alloc_text(PAGE,CmpFindMatchingDescriptorCell)
#pragma alloc_text(PAGE,CmpAssignSecurityToKcb)
#pragma alloc_text(PAGE,CmpFindReusableCellFromCache)
#ifdef HIVE_SECURITY_STATS
#pragma alloc_text(PAGE,CmpCheckForSecurityDuplicates)
#endif
#pragma alloc_text(PAGE,CmpBuildSecurityCellMappingArray)
#endif
ULONG
CmpSecConvKey(
IN ULONG DescriptorLength,
IN PULONG Descriptor
)
/*++
Routine Description:
Computes the ConvKey for the given security descriptor.
The algorithm is stollen from the NTFS security hash.
(it was proven to be efficient there; why shouldn't do the same ?)
For speed in the hash, we consider the security descriptor as an array
of ULONGs. The fragment at the end that is ignored should not affect
the collision nature of this hash.
Arguments:
DescriptorLength - length (in bytes) of the sd
Descriptor - actual sd to cache
Return Value:
ConvKey
Note:
We may want to convert this to a macro
--*/
{
ULONG Count;
ULONG Hash = 0;
PAGED_CODE();
Count = DescriptorLength / 4;
while (Count--) {
Hash = ((Hash << 3) | (Hash >> (32-3))) + *Descriptor++;
}
return Hash;
}
VOID
CmpInitSecurityCache(
IN OUT PCMHIVE CmHive
)
{
ULONG i;
PAGED_CODE();
CmHive->SecurityCache = NULL;
CmHive->SecurityCacheSize = 0;
CmHive->SecurityCount = 0;
CmHive->SecurityHitHint = -1; // no hint
for( i=0;i<CmpSecHashTableSize;i++) {
InitializeListHead(&(CmHive->SecurityHash[i]));
}
}
NTSTATUS
CmpAddSecurityCellToCache (
IN OUT PCMHIVE CmHive,
IN HCELL_INDEX SecurityCell,
IN BOOLEAN BuildUp,
IN PCM_KEY_SECURITY_CACHE SecurityCached
)
/*++
Routine Description:
This routine adds the specified security cell to the cache of the
specified hive. It takes care of cache allocation (grow) as well.
At build up time, cache size grows with a PAGE_SIZE, to avoid memory
fragmentation. After the table is builded, it's size is adjusted (most
of the hives never add new security cells). Then, at run-time, the size
grows with 16 entries at a time (same reason)
The cache is ordered by the cell's index, so we can do a binary search on
cells retrieval.
Arguments:
CmHive - the hive to which the security cell belongs
SecurityCell - the security cell to be added to the cache
BuildUp - specifies that this is build up time
SecurityCached - if not NULL it means we have it already allocated (this happens when rebuilding the cache).
Return Value:
NTSTATUS - STATUS_SUCCESS if the operation is successful and an
appropriate error status otherwise (i.e STATUS_INSUFFICIENT_RESOURCES).
Note:
If the security cell is already IN the cache; this function will return TRUE.
--*/
{
ULONG Index;
PCM_KEY_SECURITY Security;
PAGED_CODE();
if( CmpFindSecurityCellCacheIndex (CmHive,SecurityCell,&Index) == TRUE ) {
//
// cell already exist in the cache; return;
//
return STATUS_SUCCESS;
}
//
// if this fails, we're doomed !
//
ASSERT( (PAGE_SIZE % sizeof(CM_KEY_SECURITY_CACHE_ENTRY)) == 0 );
//
// check if the cache can accomodate a new cell
//
if( CmHive->SecurityCount == CmHive->SecurityCacheSize ) {
//
// We're at the limit with the cache; we need to extend it by a page
//
// OBS: this takes care of the first allocation too, as SecurityCount
// and SecurityCacheSize are both initialized with 0
//
PCM_KEY_SECURITY_CACHE_ENTRY Temp;
// store the actual buffer
Temp = CmHive->SecurityCache;
//
// compute the new size and allocate a new buffer
//
if( BuildUp == TRUE ) {
//
// We are building up the cache; grow the table in page increments
//
ASSERT( ((CmHive->SecurityCacheSize * sizeof(CM_KEY_SECURITY_CACHE_ENTRY)) % PAGE_SIZE) == 0 );
CmHive->SecurityCacheSize += (PAGE_SIZE / sizeof(CM_KEY_SECURITY_CACHE_ENTRY));
} else {
//
// normal case (running time); a new security cell is added; grow the
// table with a fixed number of increments (to avoid fragmentation, in
// case of an Office install :-) )
//
CmHive->SecurityCacheSize += SECURITY_CACHE_GROW_INCREMENTS;
}
CmRetryExAllocatePoolWithTag(PagedPool, CmHive->SecurityCacheSize * sizeof(CM_KEY_SECURITY_CACHE_ENTRY),
CM_SECCACHE_TAG|PROTECTED_POOL,CmHive->SecurityCache);
if( CmHive->SecurityCache == NULL ) {
//
// bad luck; bail out
//
CmHive->SecurityCache = Temp;
CmHive->SecurityCacheSize = CmHive->SecurityCount;
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// copy existing data in the new location and free the old buffer
//
RtlCopyMemory(CmHive->SecurityCache,Temp,CmHive->SecurityCount*sizeof(CM_KEY_SECURITY_CACHE_ENTRY));
if( Temp != NULL ) {
ExFreePoolWithTag(Temp, CM_SECCACHE_TAG|PROTECTED_POOL );
} else {
ASSERT( CmHive->SecurityCount == 0 );
}
}
//
// try first to get the security cell from the hive; if this fails, there is no point to go on
//
Security = (PCM_KEY_SECURITY)HvGetCell(&(CmHive->Hive),SecurityCell);
if( Security == NULL ){
//
// we failed to map the view containing this cell; bail out
//
return STATUS_INSUFFICIENT_RESOURCES;
}
if( !SecurityCached ) {
ULONG Size;
//
// compute the size for the cached security structure
//
Size = FIELD_OFFSET(CM_KEY_SECURITY_CACHE,Descriptor) + Security->DescriptorLength;
//
// think forward: allocate and initialize a copy for the security cell, in order to store it in the cache
//
CmRetryExAllocatePoolWithTag(PagedPool,Size,CM_SECCACHE_TAG|PROTECTED_POOL,SecurityCached);
if(SecurityCached == NULL) {
//
// bad luck; bail out
//
HvReleaseCell(&(CmHive->Hive),SecurityCell);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
//
// from now on, nothing can go wrong !
//
RtlCopyMemory(&(SecurityCached->Descriptor),&(Security->Descriptor),Security->DescriptorLength);
SecurityCached->Cell = SecurityCell;
SecurityCached->DescriptorLength = Security->DescriptorLength;
//
// now add this to the hash table
//
SecurityCached->ConvKey = CmpSecConvKey(Security->DescriptorLength,(PULONG)(&(Security->Descriptor)));
// add it to the end of the list with this conv key
InsertTailList( &(CmHive->SecurityHash[SecurityCached->ConvKey % CmpSecHashTableSize]),
&(SecurityCached->List)
);
HvReleaseCell(&(CmHive->Hive),SecurityCell);
//
// At this point we are sure we have space for at least one more entry
// Move data to make room for the new entry
//
if( Index < CmHive->SecurityCount ) {
//
// RtlMoveMemory will take care of the overlapping problem
//
RtlMoveMemory( ((PUCHAR)CmHive->SecurityCache) + (Index+1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // destination
((PUCHAR)CmHive->SecurityCache) + Index*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // source
(CmHive->SecurityCount - Index)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY) // size
);
}
//
// setup the new entry
//
CmHive->SecurityCache[Index].Cell = SecurityCell;
CmHive->SecurityCache[Index].CachedSecurity = SecurityCached;
// update the count
CmHive->SecurityCount++;
return STATUS_SUCCESS;
}
BOOLEAN
CmpFindSecurityCellCacheIndex (
IN PCMHIVE CmHive,
IN HCELL_INDEX SecurityCell,
OUT PULONG Index
)
/*++
Routine Description:
Search (binary) for the specified cellindex in the security cache.
Returns the index of the cache entry where the cell is cached or
it should be added
Arguments:
CmHive - the hive to which the security cell belongs
SecurityCell - the security cell to search for
Index - out param to pass the index at which the cell is (or it should be)
Return Value:
TRUE - the cell was found (at *Index)
FALSE - the cell is not in the cache (it should be added at *Index)
--*/
{
ULONG High;
ULONG Low;
ULONG Current;
USHORT State = 0; // state of the operation: 0 - normal binary search
// 1 - last low
// 2 - last high
LONG Result;
LONG Tmp1,Tmp2;
PAGED_CODE();
if( CmHive->SecurityCount == 0 ) {
//
// there is no cell in the security cache
//
*Index = 0;
return FALSE;
}
// sanity asserts
ASSERT( CmHive->SecurityCount <= CmHive->SecurityCacheSize );
ASSERT( CmHive->SecurityCache != NULL );
High = CmHive->SecurityCount - 1;
Low = 0;
if( (CmHive->SecurityHitHint >= 0) && ( (ULONG)CmHive->SecurityHitHint <= High) ) {
//
// try the last search
//
Current = CmHive->SecurityHitHint;
} else {
Current = High/2;
}
// sign adjustment
Tmp1 = SecurityCell & ~HCELL_TYPE_MASK;
if( SecurityCell & HCELL_TYPE_MASK ) {
Tmp1 = -Tmp1;
}
while( TRUE ) {
Tmp2 = CmHive->SecurityCache[Current].Cell & ~HCELL_TYPE_MASK;
// sign adjustment
if( CmHive->SecurityCache[Current].Cell & HCELL_TYPE_MASK ) {
Tmp2 = -Tmp2;
}
Result = Tmp1 - Tmp2;
if (Result == 0) {
//
// Success, return data to caller and exit
//
*Index = Current;
//
// we have a hit! update the count and exit
//
CmHive->SecurityHitHint = Current;
return TRUE;
}
//
// compute the next index to try
//
switch(State) {
case 0:
//
// normal binary search state
//
if( Result < 0 ) {
High = Current;
} else {
Low = Current;
}
if ((High - Low) <= 1) {
//
// advance to the new state
//
Current = Low;
State = 1;
} else {
Current = Low + ( (High-Low) / 2 );
}
break;
case 1:
//
// last low state
//
// this should be true
ASSERT( Current == Low );
if (Result < 0) {
//
// does not exist, under
//
*Index = Current;
return FALSE;
} else if( Low == High ) {
//
// low and high are identical; but current is bigger than them; insert after
//
*Index = Current + 1;
return FALSE;
} else {
//
// advance to the new state; i.e. look at high
//
State = 2;
Current = High;
}
break;
case 2:
//
// last high state; if we got here, High = Low +1 and Current == High
//
ASSERT( Current == High);
ASSERT( High == (Low + 1) );
if( Result < 0 ) {
//
// under High, but above Low; we should insert it here
//
*Index = Current;
return FALSE;
} else {
//
// above High;
//
*Index = Current + 1;
return FALSE;
}
break;
default:
ASSERT( FALSE );
break;
}
}
//
// we shouldn't get here !!!
//
ASSERT( FALSE );
return FALSE;
}
BOOLEAN
CmpAdjustSecurityCacheSize (
IN PCMHIVE CmHive
)
/*++
Routine Description:
Adjust the scusrity cache size for the specified hive. This function
should be called after all the security cells for the hive were cached,
in order to give back extra memory used in the process.
Arguments:
CmHive - the hive to which the security cell belongs
Return Value:
TRUE - success
FALSE - failure - the size remains the same
--*/
{
PCM_KEY_SECURITY_CACHE_ENTRY Buffer;
PAGED_CODE();
if( CmHive->SecurityCount < CmHive->SecurityCacheSize ) {
//
// cache size is bigger than what we need; there is a good chance
// nobody will ever add new security cells to this hive, so go on
// and free the extra space
//
//
// allocate a new buffer with the exact size we need
//
CmRetryExAllocatePoolWithTag(PagedPool, CmHive->SecurityCount * sizeof(CM_KEY_SECURITY_CACHE_ENTRY),
CM_SECCACHE_TAG|PROTECTED_POOL,Buffer);
if( Buffer == NULL ) {
//
// the system is low on resources; leave the cache as it is
//
return FALSE;
}
//
// copy significant data inot the new buffer
//
RtlCopyMemory(Buffer,CmHive->SecurityCache,CmHive->SecurityCount*sizeof(CM_KEY_SECURITY_CACHE_ENTRY));
//
// free the old buffer and update cache members
//
ExFreePoolWithTag(CmHive->SecurityCache, CM_SECCACHE_TAG|PROTECTED_POOL );
CmHive->SecurityCache = Buffer;
CmHive->SecurityCacheSize = CmHive->SecurityCount;
}
return TRUE;
}
VOID
CmpRemoveFromSecurityCache (
IN OUT PCMHIVE CmHive,
IN HCELL_INDEX SecurityCell
)
/*++
Routine Description:
Removes the specified security cell from the security cache.
(only if present !)
For performance (and memory fragmentation) reasons, it does not
change (shrink) the cache size.
Arguments:
CmHive - the hive to which the security cell belongs
SecurityCell - the security cell to be removed from the cache
Return Value:
<none>
--*/
{
ULONG Index;
PAGED_CODE();
if( CmpFindSecurityCellCacheIndex (CmHive,SecurityCell,&Index) == FALSE ) {
//
// cell is not in the cache
//
return;
}
ASSERT( CmHive->SecurityCache[Index].Cell == SecurityCell );
ASSERT( CmHive->SecurityCache[Index].CachedSecurity->Cell == SecurityCell );
//
// remove the cached structure from the hash
//
CmpRemoveEntryList(&(CmHive->SecurityCache[Index].CachedSecurity->List));
//
// free up the cached security cell;
//
ExFreePoolWithTag(CmHive->SecurityCache[Index].CachedSecurity, CM_SECCACHE_TAG|PROTECTED_POOL );
//
// move memory to reflect the new size, and update the cache count
//
RtlMoveMemory( ((PUCHAR)CmHive->SecurityCache) + Index*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // destination
((PUCHAR)CmHive->SecurityCache) + (Index+1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // source
(CmHive->SecurityCount - Index - 1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY) // size
);
CmHive->SecurityCount--;
}
VOID
CmpDestroySecurityCache (
IN OUT PCMHIVE CmHive
)
/*++
Routine Description:
Frees up all the cached security cells and the cache itself
Arguments:
CmHive - the hive to which the security cell belongs
Return Value:
<none>
--*/
{
ULONG i;
PAGED_CODE();
for( i=0;i<CmHive->SecurityCount;i++) {
CmpRemoveEntryList(&(CmHive->SecurityCache[i].CachedSecurity->List));
ExFreePoolWithTag(CmHive->SecurityCache[i].CachedSecurity, CM_SECCACHE_TAG|PROTECTED_POOL );
}
if( CmHive->SecurityCount != 0 ) {
ASSERT( CmHive->SecurityCache != NULL );
ExFreePoolWithTag(CmHive->SecurityCache, CM_SECCACHE_TAG|PROTECTED_POOL );
}
CmHive->SecurityCache = NULL;
CmHive->SecurityCacheSize = CmHive->SecurityCount = 0;
}
PCM_KEY_SECURITY_CACHE
CmpFindReusableCellFromCache(IN PCMHIVE CmHive,
IN HCELL_INDEX SecurityCell,
IN ULONG PreviousCount)
/*++
Routine Description:
Attempts to find the smallest cell which can accomodate the current security cell.
Then moves it at the end and return a pointer to it. Shifts the array towards the end
as we are going to extend the cache
Security doesn't change too often, so chances are that we will be able to reuse the cells 90%
of the time.
If one cannot be found, the last cell in the array is freed. A new one will be allocated inside
CmpAddSecurityCellToCache
Arguments:
CmHive - the hive to which the security cell belongs
SecurityCell - the security cell
PreviousCount - the end of the array
Return Value:
the cached cell or NULL
--*/
{
ULONG Size;
PCM_KEY_SECURITY Security;
PCM_KEY_SECURITY_CACHE SecurityCached;
ULONG i;
ULONG SmallestSize = 0;
ULONG SmallestIndex = 0;
PAGED_CODE();
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
//
// try first to get the security cell from the hive; if this fails, there is no point to go on
//
Security = (PCM_KEY_SECURITY)HvGetCell(&(CmHive->Hive),SecurityCell);
if( Security == NULL ){
//
// we failed to map the view containing this cell; bail out
//
goto ErrorExit;
}
//
// compute the size for the cached security structure
//
Size = Security->DescriptorLength;
HvReleaseCell(&(CmHive->Hive),SecurityCell);
//
// Now that we know the desired size, start iterating the array to find a suitable entry.
//
for(i = CmHive->SecurityCount; i < PreviousCount; i++) {
SecurityCached = CmHive->SecurityCache[i].CachedSecurity;
if( SecurityCached->DescriptorLength == Size ) {
Found:
//
// we have found one matching the exact size; move it at the end
// shift to the end one entry as we are going to extend the cache
//
// this factored down translates to:
//
RtlMoveMemory( ((PUCHAR)CmHive->SecurityCache) + (CmHive->SecurityCount+1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // destination
((PUCHAR)CmHive->SecurityCache) + CmHive->SecurityCount*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // source
(i - CmHive->SecurityCount)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY) // size
);
return SecurityCached;
}
//
// if not; record the smallest suitable entry
//
if( SecurityCached->DescriptorLength > Size ) {
if( (SmallestSize == 0) ||
(SmallestSize > SecurityCached->DescriptorLength)
) {
//
// first one or this one is smaller
//
SmallestSize = SecurityCached->DescriptorLength;
SmallestIndex = i;
}
}
}
if( SmallestSize != 0 ) {
SecurityCached = CmHive->SecurityCache[SmallestIndex].CachedSecurity;
ASSERT( SecurityCached->DescriptorLength == SmallestSize );
ASSERT( Size < SmallestSize );
i = SmallestIndex;
goto Found;
}
ErrorExit:
ExFreePoolWithTag(CmHive->SecurityCache[PreviousCount - 1].CachedSecurity, CM_SECCACHE_TAG|PROTECTED_POOL );
//
// shift to the end one entry as we are going to extend the cache
//
RtlMoveMemory( ((PUCHAR)CmHive->SecurityCache) + (CmHive->SecurityCount+1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // destination
((PUCHAR)CmHive->SecurityCache) + CmHive->SecurityCount*sizeof(CM_KEY_SECURITY_CACHE_ENTRY), // source
(PreviousCount - CmHive->SecurityCount - 1)*sizeof(CM_KEY_SECURITY_CACHE_ENTRY) // size
);
return NULL;
}
BOOLEAN
CmpRebuildSecurityCache(
IN OUT PCMHIVE CmHive
)
/*++
Routine Description:
Rebuilds the security cache by reiterating all security cells
and adding them to the cache; this routine is intended for hive
refresh operations
Arguments:
CmHive - the hive to which the security cell belongs
Return Value:
TRUE or FALSE
--*/
{
PCM_KEY_NODE RootNode;
PCM_KEY_SECURITY SecurityCell;
HCELL_INDEX ListAnchor;
HCELL_INDEX NextCell;
HCELL_INDEX LastCell = 0;
PHHIVE Hive;
PRELEASE_CELL_ROUTINE ReleaseCellRoutine;
BOOLEAN Result = TRUE;
ULONG PreviousCount;
PCM_KEY_SECURITY_CACHE SecCache;
PAGED_CODE();
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
//
// avoid extra work
//
Hive = &(CmHive->Hive);
ReleaseCellRoutine = Hive->ReleaseCellRoutine;
Hive->ReleaseCellRoutine = NULL;
//
// be smart and reuse the cache; For each cell, we'll iterate through the cache
// find an entry big enough to accomodate the current cell, move it at the end
// and then reuse it.
//
// first, reinitialize the hash table.
//
for( PreviousCount=0;PreviousCount<CmpSecHashTableSize;PreviousCount++) {
InitializeListHead(&(CmHive->SecurityHash[PreviousCount]));
}
//
// this we use to keep track of how many valid cells were in the cache previously
//
PreviousCount = CmHive->SecurityCount;
//
// start building an pseudo-empty one.
//
CmHive->SecurityCount = 0;
if (!HvIsCellAllocated(Hive,Hive->BaseBlock->RootCell)) {
//
// root cell HCELL_INDEX is bogus
//
Result = FALSE;
goto JustReturn;
}
RootNode = (PCM_KEY_NODE) HvGetCell(Hive, Hive->BaseBlock->RootCell);
if( RootNode == NULL ) {
//
// we couldn't map a view for the bin containing this cell
//
Result = FALSE;
goto JustReturn;
}
ListAnchor = NextCell = RootNode->Security;
do {
if (!HvIsCellAllocated(Hive, NextCell)) {
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SEC,"CM: CmpRebuildSecurityCache\n"));
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SEC," NextCell: %08lx is invalid HCELL_INDEX\n",NextCell));
Result = FALSE;
goto JustReturn;
}
SecurityCell = (PCM_KEY_SECURITY) HvGetCell(Hive, NextCell);
if( SecurityCell == NULL ) {
//
// we couldn't map a view for the bin containing this cell
//
Result = FALSE;
goto JustReturn;
}
if (NextCell != ListAnchor) {
//
// Check to make sure that our Blink points to where we just
// came from.
//
if (SecurityCell->Blink != LastCell) {
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SEC," Invalid Blink (%ld) on security cell %ld\n",SecurityCell->Blink, NextCell));
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SEC," should point to %ld\n", LastCell));
Result = FALSE;
goto JustReturn;
}
}
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_SEC,"CmpValidSD: SD shared by %d nodes\n",SecurityCell->ReferenceCount));
if (!SeValidSecurityDescriptor(SecurityCell->DescriptorLength, &SecurityCell->Descriptor)) {
#if DBG
CmpDumpSecurityDescriptor(&SecurityCell->Descriptor,"INVALID DESCRIPTOR");
#endif
Result = FALSE;
goto JustReturn;
}
SecCache = CmpFindReusableCellFromCache(CmHive,NextCell,PreviousCount);
if( !NT_SUCCESS(CmpAddSecurityCellToCache ( CmHive,NextCell,TRUE,SecCache) ) ) {
Result = FALSE;
goto JustReturn;
}
LastCell = NextCell;
NextCell = SecurityCell->Flink;
} while ( NextCell != ListAnchor );
JustReturn:
Hive->ReleaseCellRoutine = ReleaseCellRoutine;
return Result;
}
BOOLEAN
CmpFindMatchingDescriptorCell(
IN PCMHIVE CmHive,
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN ULONG Type,
OUT PHCELL_INDEX MatchingCell,
OUT OPTIONAL PCM_KEY_SECURITY_CACHE *CachedSecurityPointer
)
/*++
Routine Description:
This routine attempts to find a security descriptor in the hive that
is identical to the one passed in. If it finds one, it returns its
cell index.
Obsolete:
Currently, this routine checks the security descriptors of the parent
and siblings of the node to find a match.
New:
It looks for the sd in the security cache for this hive. This will
eliminate duplicates and make the search process faster.
Arguments:
CmHive - Supplies a pointer to the hive control structure for the node.
Needed to get access to the cache
SecurityDescriptor - Supplies the cooked security descriptor which
should be searched for.
Type - Indicates whether the Security Descriptor that matches must
be in Stable or Volatile store
MatchingCell - Returns the cell index of a security cell whose
security descriptor is identical to SecurityDescriptor.
Valid only if TRUE is returned.
CachedSecurityPointer - pointer to the cached security (for update reasons)
Return Value:
TRUE - Matching security descriptor found. MatchingCell returns the
cell index of the matching security descriptor.
FALSE - No matching security descriptor found. MatchingCell is invalid.
--*/
{
ULONG DescriptorLength;
ULONG ConvKey;
PLIST_ENTRY ListAnchor;
PLIST_ENTRY Current;
PCM_KEY_SECURITY_CACHE CachedSecurity;
PAGED_CODE();
DescriptorLength = RtlLengthSecurityDescriptor(SecurityDescriptor);
//
// calculate the conv key
//
ConvKey = CmpSecConvKey(DescriptorLength,(PULONG)SecurityDescriptor);
ListAnchor = &(CmHive->SecurityHash[ConvKey % CmpSecHashTableSize]);
if( IsListEmpty(ListAnchor) == TRUE ) {
return FALSE;
}
//
// iterate through the list of colisions for this convkey
// start with teh first element in list
//
Current = (PLIST_ENTRY)(ListAnchor->Flink);
while( Current != ListAnchor ){
//
// get the current cached security
//
CachedSecurity = CONTAINING_RECORD(Current,
CM_KEY_SECURITY_CACHE,
List);
//
// see if it matches with the given descriptor;
//
if( (CachedSecurity->ConvKey == ConvKey) && // same convkey
(Type == HvGetCellType(CachedSecurity->Cell)) && // same cell type
(DescriptorLength == CachedSecurity->DescriptorLength) && // same length
(RtlEqualMemory(SecurityDescriptor, // and, finally, bit-wise identical
&(CachedSecurity->Descriptor),
DescriptorLength))
) {
//
// we have found a match
//
*MatchingCell = CachedSecurity->Cell;
if (ARGUMENT_PRESENT(CachedSecurityPointer)) {
*CachedSecurityPointer = CachedSecurity;
}
return TRUE;
}
//
// advance to the next element
//
Current = (PLIST_ENTRY)(Current->Flink);
}
// sorry, no match
return FALSE;
}
VOID
CmpAssignSecurityToKcb(
IN PCM_KEY_CONTROL_BLOCK Kcb,
IN HCELL_INDEX SecurityCell
)
/*++
Routine Description:
Establishes the connection between the KCB and the cached security
descriptor.
As most of the time this is called after the security cell has been
linked to the Key Node, and because the binary search starts with
the last cell looked up, we will not hit a performance impact here.
Arguments:
Kcb - the KCb to which this security cell needs to be attached
SecurityCell - Security cell for the kcb
Return Value:
NONE; bugchecks on error
--*/
{
ULONG Index;
PCMHIVE CmHive;
PAGED_CODE();
if( SecurityCell == HCELL_NIL ) {
Kcb->CachedSecurity = NULL;
return;
}
CmHive = (PCMHIVE)(Kcb->KeyHive);
//
// get the security descriptor from cache
//
if( CmpFindSecurityCellCacheIndex (CmHive,SecurityCell,&Index) == FALSE ) {
Kcb->CachedSecurity = NULL;
//
// we are doomed !!!
//
CM_BUGCHECK( REGISTRY_ERROR,BAD_SECURITY_CACHE,1,Kcb,SecurityCell);
}
//
// success; link the cached security to this KCB
//
Kcb->CachedSecurity = CmHive->SecurityCache[Index].CachedSecurity;
}
#ifdef HIVE_SECURITY_STATS
ULONG
CmpCheckForSecurityDuplicates(
IN OUT PCMHIVE CmHive
)
/*++
Routine Description:
Iterates through the security cache for the specified hive and detects
if there are any security descriptors which are duplicated
Arguments:
CmHive - the hive in question
Return Value:
number of duplicates (it should be 0)
--*/
{
ULONG i,j,Duplicates = 0;
PCM_KEY_SECURITY_CACHE CachedSecurity1,CachedSecurity2;
HCELL_INDEX Cell1,Cell2;
PAGED_CODE();
for( i=0;i<CmHive->SecurityCount - 1;i++) {
CachedSecurity1 = CmHive->SecurityCache[i].CachedSecurity;
Cell1 = CmHive->SecurityCache[i].Cell;
ASSERT( Cell1 == CachedSecurity1->Cell );
for( j=i+1;j<CmHive->SecurityCount;j++) {
CachedSecurity2 = CmHive->SecurityCache[j].CachedSecurity;
Cell2 = CmHive->SecurityCache[j].Cell;
ASSERT( Cell2 == CachedSecurity2->Cell );
if ((CachedSecurity1->DescriptorLength == CachedSecurity2->DescriptorLength) &&
(HvGetCellType(Cell1) == HvGetCellType(Cell2)) &&
(RtlEqualMemory(&(CachedSecurity1->Descriptor),
&(CachedSecurity2->Descriptor),
CachedSecurity1->DescriptorLength))) {
ASSERT( CachedSecurity1->ConvKey == CachedSecurity2->ConvKey );
//
// we've found a duplicate cell;
//
#ifndef _CM_LDR_
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"Duplicate security cell found in Hive %p Cell1=%8lx Cell2 = %8lx\n",(&(CmHive->Hive)),Cell1,Cell2);
#endif //_CM_LDR_
Duplicates++;
break;
}
}
}
return Duplicates;
}
#endif
BOOLEAN
CmpBuildSecurityCellMappingArray(
IN PCMHIVE CmHive
)
/*++
Routine Description:
Iterates through the security cache for the specified hive and
build the array of mappings.
Arguments:
CmHive - the hive in question
Return Value:
TRUE/FALSE
--*/
{
ULONG i;
PAGED_CODE();
ASSERT( CmHive->CellRemapArray == NULL );
CmHive->CellRemapArray = ExAllocatePool(PagedPool,sizeof(CM_CELL_REMAP_BLOCK)*CmHive->SecurityCount);
if( CmHive->CellRemapArray == NULL ) {
return FALSE;
}
for( i=0;i<CmHive->SecurityCount;i++) {
CmHive->CellRemapArray[i].OldCell = CmHive->SecurityCache[i].Cell;
if( HvGetCellType(CmHive->SecurityCache[i].Cell) == (ULONG)Volatile ) {
//
// we preserve volatile cells
//
CmHive->CellRemapArray[i].NewCell = CmHive->SecurityCache[i].Cell;
} else {
CmHive->CellRemapArray[i].NewCell = HCELL_NIL;
}
}
return TRUE;
}