|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: scavenge.c
//
// Contents: Home of the LSA scavenger thread
//
// Classes:
//
// Functions:
//
// History: 6-08-93 RichardW Created
//
//----------------------------------------------------------------------------
#include <lsapch.hxx>
#include "scavenge.hxx"
#define SCAV_INTERNAL_NO_TRACE 0x10000000
ULONG ScavNotifyCount;
LIST_ENTRY NotifyList ; LIST_ENTRY NotifyEvents ; LIST_ENTRY ScavList ;
RTL_CRITICAL_SECTION NotifyLock ; RTL_CRITICAL_SECTION ScavLock ;
HKEY LsaRegistryKey ; HANDLE LsaRegistryWatchEvent ;
#define SCAV_TABLE 8
PVOID DeadScavItems[ SCAV_TABLE ]; ULONG DeadScavIndex ;
#define SCAVENGER_WAIT_INTERVAL 60000L
//
// Internal flags:
//
#define SCAVFLAG_NOTIFY_EVENT 0x01000000
#define SCAVFLAG_ASYNC_TIMER_DELETE 0x00400000
#define NOTIFY_FLAG_SYNCHRONOUS 0x00000001
#define LockScavenger() RtlEnterCriticalSection( &ScavLock )
#define UnlockScavenger() RtlLeaveCriticalSection( &ScavLock )
#define LockNotify() RtlEnterCriticalSection( &NotifyLock )
#define UnlockNotify() RtlLeaveCriticalSection( &NotifyLock )
//
// Define locking macros for the scav list
//
#define LsapRefScavItem( Item ) \
{ \ RtlEnterCriticalSection( &ScavLock ); \ ((PLSAP_SCAVENGER_ITEM) Item)->RefCount++ ; \ RtlLeaveCriticalSection( &ScavLock ); \ }
#define LsapRefScavItemUnsafe( Item ) \
{ \ ((PLSAP_SCAVENGER_ITEM) Item)->RefCount++ ; \ }
DWORD WINAPI LsapScavengerThread( PVOID Ignored );
BOOLEAN LsapBreakEveryMinute = FALSE; BOOLEAN LsapDebuggerOk = FALSE ;
VOID FreeScavengerItem( IN PLSAP_SCAVENGER_ITEM Item ) { ASSERT( Item->List.Flink == NULL ); ASSERT( Item->PackageList.Flink == NULL );
LsapFreePrivateHeap( Item ); }
VOID LsapInternalBreak( VOID ) { if ( !LsapDebuggerOk ) { return; }
DbgBreakPoint(); }
ULONG NTAPI LsapScavengerBreak( PVOID Param ) { if (LsapBreakEveryMinute) { LsapInternalBreak(); }
HANDLE hToken; DWORD ReturnLength;
TOKEN_STATISTICS TokenStats;
NTSTATUS Status;
Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_QUERY, &hToken );
if (NT_SUCCESS( Status )) {
Status = NtQueryInformationToken ( hToken, TokenStatistics, &TokenStats, sizeof( TOKEN_STATISTICS ), &ReturnLength );
if (NT_SUCCESS( Status )) {
if (TokenStats.ExpirationTime.QuadPart == 0i64) {
LsapInternalBreak(); } }
NtClose( hToken ); }
return(0); }
//+---------------------------------------------------------------------------
//
// Function: LsapRegistryWatch
//
// Synopsis: Callback that handles registry changes in the LSA key
//
// Arguments: [Ignored]
//
// History: 05-10-00 RichardW
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD LsapRegistryWatch( PVOID Ignored ) { //
// refresh dynamically changable parameters.
//
LoadParameters( TRUE );
RegNotifyChangeKeyValue( LsaRegistryKey, TRUE, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, LsaRegistryWatchEvent, TRUE );
LsapEventNotify( NOTIFY_CLASS_REGISTRY_CHANGE, 0, 0, NULL );
return 0 ;
}
//+---------------------------------------------------------------------------
//
// Function: LsapDerefScavItem
//
// Synopsis: Dereference, optionally freeing a scavenger item
//
// Arguments: [Item] --
//
// History: 6-03-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID LsapDerefScavItem( PLSAP_SCAVENGER_ITEM Item ) { HANDLE TimerDeleteHandle ;
LockScavenger();
Item->RefCount-- ;
if ( Item->RefCount == 0 ) { DebugLog(( DEB_TRACE_SCAV, "Removing item %x\n", Item ));
if ( Item->List.Flink ) { RemoveEntryList( &Item->List );
Item->List.Flink = NULL ; }
if ( Item->PackageList.Flink ) { RemoveEntryList( &Item->PackageList );
Item->PackageList.Flink = NULL ; }
//
// Because the rtl thread pool is asynchronous to this one,
// we need to keep track of recently deceased scavenger items
// to prevent them from being used in an RTL thread. The add
// function will scan the table to remove any potential duplicates.
//
DeadScavItems[ DeadScavIndex ] = Item ; DeadScavIndex ++ ; DeadScavIndex &= (SCAV_TABLE - 1);
UnlockScavenger();
if ( Item->Type == NOTIFIER_TYPE_INTERVAL ) { //
// Kill the timerq handle:
//
if ( Item->TimerHandle != INVALID_HANDLE_VALUE && Item->TimerHandle != NULL ) { if ( (Item->Flags & SCAVFLAG_ASYNC_TIMER_DELETE) != 0 ) { TimerDeleteHandle = 0 ; } else { TimerDeleteHandle = INVALID_HANDLE_VALUE ; }
DeleteTimerQueueTimer( NULL, Item->TimerHandle, TimerDeleteHandle ); } } else if ( Item->Type == NOTIFIER_TYPE_HANDLE_WAIT ) { if ( Item->TimerHandle != INVALID_HANDLE_VALUE && Item->TimerHandle != NULL ) { UnregisterWaitEx( Item->TimerHandle, INVALID_HANDLE_VALUE ); } }
if ( ( Item->Type != NOTIFIER_TYPE_NOTIFY_EVENT ) && ( Item->Type != NOTIFIER_TYPE_IMMEDIATE ) ) { //
// Yield to let the other thread remove the item
//
Sleep( 100 ); }
Item->ScavCheck = SCAVMAGIC_FREE ;
FreeScavengerItem( Item ); } else { UnlockScavenger(); } }
//+---------------------------------------------------------------------------
//
// Function: LsapScavengerTrigger
//
// Synopsis: Actual Trigger
//
// Arguments: [Parameter] -- Item to call
//
// History: 5-24-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
ULONG LsapScavengerTrigger( PVOID Parameter ) { PLSAP_SCAVENGER_ITEM Item ; #ifdef LSAP_VERIFY_PACKAGE_ID
ULONG_PTR dwPackageID, dwCurId; #endif
if ( ShutdownBegun ) { return 0; }
SetCurrentSession( pDefaultSession );
Item = (PLSAP_SCAVENGER_ITEM) Parameter ;
DsysAssert( Item->ScavCheck == SCAVMAGIC_ACTIVE );
__try { #ifdef LSAP_VERIFY_PACKAGE_ID
dwPackageID = Item->PackageId; dwCurId = GetCurrentPackageId();
if ((dwCurId != SPMGR_ID) || (dwPackageID != SPMGR_ID)) #endif
{ SetCurrentPackageId( Item->PackageId ); }
(VOID) Item->Function( Item->Parameter );
#ifdef LSAP_VERIFY_PACKAGE_ID
if (dwPackageID != SPMGR_ID) #endif
{ SetCurrentPackageId( SPMGR_ID ); } } __except( SP_EXCEPTION ) { SPException( GetExceptionCode(), Item->PackageId ); }
LsapDerefScavItem( Item );
return 0 ; }
//+---------------------------------------------------------------------------
//
// Function: LsapTimerCallback
//
// Synopsis: Callback from thread pool for scavenger items
//
// Arguments: [Context] --
// [Timeout] --
//
// History: 7-01-98 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID LsapTimerCallback( PVOID Context, BOOLEAN Timeout ) { PLSAP_SCAVENGER_ITEM Item ; ULONG i ; BOOL OneShot ;
SetCurrentSession( pDefaultSession );
Item = (PLSAP_SCAVENGER_ITEM) Context ;
//
// We only scan for handle and timer events. Things executed
// by QueueUserWorkItem don't end up in this list, but that's
// okay, since those items go directly to LsapScavengerTrigger
//
LockScavenger();
for ( i = 0 ; i < SCAV_TABLE ; i++ ) { if ( DeadScavItems[ i ] == Item ) { break; } }
if ( i != SCAV_TABLE ) { //
// uh oh, a dead one that was still in the queue in this
// rtl worker thread. Ignore it.
//
UnlockScavenger();
return ; }
if ( Item->Flags & SCAVFLAG_ASYNC_TIMER_DELETE ) { //
// This is a bad condition. An item that should have
// been fired once has shown up again. Ignore it.
//
UnlockScavenger();
return; }
LsapRefScavItemUnsafe( Item );
OneShot = ( Item->Flags & NOTIFIER_FLAG_ONE_SHOT ) != 0 ;
if ( OneShot ) { //
// This flag has the side effect of preventing further
// callbacks. That lets us delete is asynchronously later.
//
Item->Flags |= SCAVFLAG_ASYNC_TIMER_DELETE ; }
UnlockScavenger();
if ( (Item->Flags & SCAV_INTERNAL_NO_TRACE ) == 0 ) { DebugLog(( DEB_TRACE_SCAV, "Triggering item %x, type %d\n", Item, Item->Type )); }
LsapScavengerTrigger( Item );
//
// If this is a one-shot item that's in the list, then it was
// a delayed or otherwise "real" one-shot. Deref it again to
// kill it.
//
if ( OneShot ) { LsapDerefScavItem( Item ); } }
//+---------------------------------------------------------------------------
//
// Function: LsapScavengerHandleNotify
//
// Synopsis: Called whenever a notification event goes off.
//
// Arguments: [Ignored] --
//
// History: 5-23-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD WINAPI LsapScavengerHandleNotify( PVOID Ignored ) { PLIST_ENTRY NotifyScan ; PLIST_ENTRY EventScan ; PLSAP_NOTIFY_EVENT Event ; PLSAP_SCAVENGER_ITEM Item ;
do { LockNotify();
if ( !IsListEmpty( &NotifyEvents ) ) { EventScan = RemoveHeadList( &NotifyEvents ); } else { EventScan = NULL ; }
UnlockNotify();
if ( EventScan ) { Event = CONTAINING_RECORD( EventScan, LSAP_NOTIFY_EVENT, List );
LockScavenger();
NotifyScan = NotifyList.Flink ;
while ( NotifyScan != &NotifyList ) { Item = CONTAINING_RECORD( NotifyScan, LSAP_SCAVENGER_ITEM, List );
if ( Item->Class == Event->Notify.EventClass ) { Event->Notify.PackageParameter = Item->Parameter ;
Item->Function( &Event->Notify ); }
NotifyScan = NotifyScan->Flink ; }
UnlockScavenger();
if ( Event->Flags & NOTIFY_FLAG_SYNCHRONOUS ) { SetEvent( Event->hSync ); }
LsapFreeLsaHeap( Event ); } } while ( EventScan );
return 0 ; }
//+---------------------------------------------------------------------------
//
// Function: LsaIRegisterNotification
//
// Synopsis: Registers a callback, to be called on either a handle signalled,
// or an time based interval, or special async events
//
// Arguments: [pFunction] -- Callback function
// [pvParameter] -- Parameter to pass
// [Type] -- Type of callback
// [Class] -- Event class
// [fItem] -- Flags
// [Interval] -- Interval to call
// [hEvent] -- Handle to wait on
//
// History: 6-03-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
PVOID NTAPI LsaIRegisterNotification( IN LPTHREAD_START_ROUTINE pFunction, IN PVOID pvParameter, IN ULONG Type, IN ULONG Class, IN ULONG fItem, IN ULONG Interval, IN HANDLE hEvent) { PLSAP_SCAVENGER_ITEM Item ; PLIST_ENTRY List = NULL ; BOOL Success ; DWORD DueTime ; ULONG i;
Item = (PLSAP_SCAVENGER_ITEM) LsapAllocatePrivateHeap( sizeof( LSAP_SCAVENGER_ITEM ) );
if ( !Item ) { return NULL ; }
Item->List.Flink = NULL ; Item->PackageList.Flink = NULL ;
Item->Type = Type ; Item->Function = pFunction ; Item->Parameter = pvParameter ; Item->RefCount = 1 ; Item->PackageId = GetCurrentPackageId(); Item->ScavCheck = SCAVMAGIC_ACTIVE; Item->Flags = fItem ; Item->TimerHandle = INVALID_HANDLE_VALUE;
//
// Okay, we have set up the item, more or less. Now, insert it
// into the list we have selected for it.
//
DebugLog(( DEB_TRACE_SCAV, "Created scavenger item %x, type %d\n", Item, Item->Type ));
switch ( Type ) { case NOTIFIER_TYPE_IMMEDIATE:
Item->Flags |= NOTIFIER_FLAG_ONE_SHOT ;
Success = QueueUserWorkItem( LsapScavengerTrigger, Item, FALSE );
//
// And that's all. the item may in fact be freed by now, since the
// worker thread could have completed. So, return success now,
//
return (Item);
break;
case NOTIFIER_TYPE_INTERVAL: case NOTIFIER_TYPE_HANDLE_WAIT:
LockScavenger();
InsertTailList( &ScavList, &Item->List );
//
// Make sure this pointer doesn't show up in the list of dead ones.
// this can happen due to heap reuse.
//
for ( i = 0 ; i < SCAV_TABLE ; i++ ) { if ( DeadScavItems[ i ] == Item ) { DeadScavItems[ i ] = NULL ; } }
UnlockScavenger();
if ( Type == NOTIFIER_TYPE_INTERVAL ) { if ( fItem & NOTIFIER_FLAG_SECONDS ) { Interval *= 60 ; }
Interval *= 1000 ;
Success = CreateTimerQueueTimer( &Item->TimerHandle, NULL, LsapTimerCallback, Item, Interval, (fItem & NOTIFIER_FLAG_ONE_SHOT ? 0 : Interval ), 0 ); } else { ASSERT( Type == NOTIFIER_TYPE_HANDLE_WAIT );
Item->TimerHandle = RegisterWaitForSingleObjectEx( hEvent, LsapTimerCallback, Item, INFINITE, (fItem & NOTIFIER_FLAG_NEW_THREAD ? 0 : WT_EXECUTEINWAITTHREAD ) );
Success = (Item->TimerHandle != NULL); }
if ( !Success ) { LsapDerefScavItem( Item );
return NULL ; }
break;
case NOTIFIER_TYPE_NOTIFY_EVENT:
Item->Class = Class ; Item->Flags |= SCAVFLAG_NOTIFY_EVENT ;
LockScavenger();
InsertTailList( &NotifyList, &Item->List );
UnlockScavenger();
Success = TRUE ;
break;
default:
Success = FALSE ;
FreeScavengerItem( Item );
break; }
return Item ; }
//+---------------------------------------------------------------------------
//
// Function: LsaICancelNotification
//
// Arguments: [pvScavHandle] --
//
// History: 5-26-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS NTAPI LsaICancelNotification( PVOID pvScavHandle ) { PLSAP_SCAVENGER_ITEM Item ;
Item = (PLSAP_SCAVENGER_ITEM) pvScavHandle ;
if ( Item->ScavCheck != SCAVMAGIC_ACTIVE ) { return STATUS_INVALID_PARAMETER ; }
LsapDerefScavItem( Item );
return STATUS_SUCCESS ; }
//+---------------------------------------------------------------------------
//
// Function: LsapEventNotify
//
// Synopsis: Notify waiters of a security package event
//
// Arguments: [Class] -- Event Class
// [Flags] -- Flags
// [EventSize] -- Size of event data
// [EventData] -- ptr to event data
//
// History: 5-26-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOLEAN NTAPI LsapEventNotify( ULONG Class, ULONG Flags, ULONG EventSize, PVOID EventData) { PLSAP_NOTIFY_EVENT Event ; HANDLE hEvent = NULL;
Event = (PLSAP_NOTIFY_EVENT) LsapAllocateLsaHeap( sizeof( LSAP_NOTIFY_EVENT ) + EventSize ); if (Event) { Event->Notify.EventClass = Class; Event->Notify.EventDataSize = EventSize; Event->Notify.EventData = (PUCHAR) ( Event + 1 );
RtlCopyMemory( Event->Notify.EventData, EventData, EventSize );
Event->Flags = Flags;
if (Flags & NOTIFY_FLAG_SYNCHRONOUS) { hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
Event->hSync = hEvent ;
if (!Event->hSync) { LsapFreeLsaHeap( Event ); return(FALSE); } } else { Event->hSync = NULL ; }
//
// Insert event into list
//
LockNotify();
InsertTailList( &NotifyEvents, &Event->List );
UnlockNotify();
DebugLog((DEB_TRACE_SCAV, "EventNotify( %d ) - Data at %x\n", Class, Event->Notify.EventData ));
//
// Wake up the scavenger thread
//
SetEvent( hStateChangeEvent );
//
// If told to wait, block until scav thread signals the event
//
if (Flags & NOTIFY_FLAG_SYNCHRONOUS) { WaitForSingleObjectEx( hEvent, INFINITE, FALSE );
CloseHandle( hEvent ); }
return( TRUE ); }
return( FALSE ); }
//+---------------------------------------------------------------------------
//
// Function: LsapInitializeScavenger
//
// Synopsis: Initialize Scavenger,
//
// Arguments: (none)
//
// History: 5-26-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL LsapInitializeScavenger( VOID ) { ULONG i ; PVOID hNotify ; HANDLE hThread ; DWORD tid ; DWORD Debugger ; DWORD dwSize ; DWORD dwType ; SYSTEM_KERNEL_DEBUGGER_INFORMATION KdInfo ;
//
// Initialize the lists
//
InitializeListHead( &NotifyList );
InitializeListHead( &NotifyEvents );
InitializeListHead( &ScavList );
RtlInitializeCriticalSection( &ScavLock );
RtlInitializeCriticalSection( &NotifyLock );
//
// Event set whenever there is a notification.
//
hStateChangeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
//
// Create basic entries
//
hNotify = LsaIRegisterNotification( LsapScavengerHandleNotify, 0, NOTIFIER_TYPE_HANDLE_WAIT, 0, NOTIFIER_FLAG_NEW_THREAD, 0, hStateChangeEvent );
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\Lsa"), 0, KEY_READ, &LsaRegistryKey ) == 0 ) { LsaRegistryWatchEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( LsaRegistryWatchEvent ) { hNotify = LsaIRegisterNotification( LsapRegistryWatch, NULL, NOTIFIER_TYPE_HANDLE_WAIT, 0, NOTIFIER_FLAG_NEW_THREAD, 0, LsaRegistryWatchEvent );
if ( hNotify ) { //
// Call it once to start the registry watch
//
LsapRegistryWatch( NULL ); } else { CloseHandle( LsaRegistryWatchEvent ); } } else { RegCloseKey( LsaRegistryKey ); } }
//
// If we are under a debugger, or a kernel debugger is attached, or the
// flag is in the registry, turn on the watch thread
//
Debugger = 0 ; dwSize = sizeof( Debugger );
RegQueryValueEx( LsaRegistryKey, TEXT("EnableDebugCheck"), 0, &dwType, (PBYTE) &Debugger, &dwSize );
NtQuerySystemInformation( SystemKernelDebuggerInformation, &KdInfo, sizeof( KdInfo ), NULL );
if ( (KdInfo.KernelDebuggerEnabled) || (NtCurrentPeb()->BeingDebugged) || (Debugger != 0 ) ) {
LsapDebuggerOk = TRUE ;
(void) LsaIRegisterNotification( LsapScavengerBreak, NULL, NOTIFIER_TYPE_INTERVAL, 0, // no class
SCAV_INTERNAL_NO_TRACE, // no flags
60, // every minute
NULL // no handle
); }
return TRUE ; }
|