windows-nt/Source/XPSP1/NT/ds/security/base/lsa/server/sht.cxx
2020-09-26 16:20:57 +08:00

973 lines
21 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1997.
//
// File: sht.cxx
//
// Contents: Small Handle table implementation
//
// Classes:
//
// Functions:
//
// History: 2-03-97 RichardW Created
//
//----------------------------------------------------------------------------
#include <lsapch.hxx>
#include "sht.hxx"
#if DBG
#define DBG_SHT 1
#else
#define DBG_SHT 0
#endif
#define SHT_ACTION_ADDREF 0
#define SHT_ACTION_DELREF 1
#define SHT_ACTION_FORCEDEL 2
#define SHT_ACTION_VALIDATE 3
#define SHT_ACTION_ADDHANDLE 4
#define SHT_ACTION_DELHANDLE 5
#define SHT_ACTION_MASK 0x0000FFFF
#define SHT_ACTION_LOCKED 0x00010000
#define SHTP_HANDLE_CHECKED 0x20000000
#define ShtLockTable( t ) \
if ( (((PSMALL_HANDLE_TABLE) t)->Flags & SHT_NO_SERIALIZE ) == 0 ) \
{ \
RtlEnterCriticalSection( &((PSMALL_HANDLE_TABLE)t)->Lock ); \
}
#define ShtUnlockTable( t ) \
if ( (((PSMALL_HANDLE_TABLE) t)->Flags & SHT_NO_SERIALIZE ) == 0 ) \
{ \
RtlLeaveCriticalSection( &((PSMALL_HANDLE_TABLE)t)->Lock ); \
}
HP_INITIALIZE_FN ShtInitialize ;
HP_CREATE_FN ShtCreate ;
HP_DELETE_FN ShtDelete ;
HP_ADD_HANDLE_FN ShtAddHandle ;
HP_DELETE_HANDLE_FN ShtDeleteHandle ;
HP_VALIDATE_HANDLE_FN ShtValidateHandle ;
HP_REF_HANDLE_FN ShtRefHandle ;
HP_DEREF_HANDLE_KEY_FN ShtDerefHandleKey ;
HP_GET_HANDLE_CONTEXT_FN ShtGetHandleContext ;
HP_RELEASE_CONTEXT_FN ShtReleaseContext ;
HANDLE_PACKAGE SmallHandlePackage = {
sizeof( SMALL_HANDLE_TABLE),
ShtInitialize,
ShtCreate,
ShtDelete,
ShtAddHandle,
ShtDeleteHandle,
ShtValidateHandle,
ShtRefHandle,
ShtDerefHandleKey,
ShtGetHandleContext,
ShtReleaseContext
};
//+---------------------------------------------------------------------------
//
// Function: ShtInitialize
//
// Synopsis: Initialize the small handle table package
//
// Arguments: (none)
//
// History: 3-04-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
ShtInitialize(
VOID
)
{
return TRUE ;
}
//+---------------------------------------------------------------------------
//
// Function: ShtCreate
//
// Synopsis: Create a small handle table
//
// Arguments: [Flags] -- Options
// [HandleTable] -- Space to fill
//
// History: 3-04-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
PVOID
ShtCreate(
IN ULONG Flags,
IN PVOID HandleTable OPTIONAL,
IN PHP_ENUM_CALLBACK_FN Callback OPTIONAL
)
{
PSMALL_HANDLE_TABLE Table ;
if ( HandleTable )
{
Table = (PSMALL_HANDLE_TABLE) HandleTable ;
}
else
{
Table = (PSMALL_HANDLE_TABLE) LsapAllocatePrivateHeap( sizeof( SMALL_HANDLE_TABLE ) );
}
if ( Table )
{
Table->Tag = SHT_TAG ;
Table->Count = 0 ;
Table->Flags = 0 ;
InitializeListHead( &Table->List );
//
// Turn on general flags:
//
Table->Flags = (Flags & HANDLE_PACKAGE_GENERAL_FLAGS);
if ( Flags & HANDLE_PACKAGE_NO_SERIALIZE )
{
Table->Flags |= SHT_NO_SERIALIZE ;
}
else
{
NTSTATUS Status = RtlInitializeCriticalSection( &Table->Lock );
if (!NT_SUCCESS(Status))
{
if ( !HandleTable )
{
LsapFreePrivateHeap( Table );
}
Table = NULL ;
}
}
if ( Table )
{
if ( Flags & HANDLE_PACKAGE_CALLBACK_ON_DELETE )
{
Table->DeleteCallback = Callback ;
}
if ( HandleTable )
{
Table->Flags |= SHT_NO_FREE ;
}
}
}
return Table ;
}
//+---------------------------------------------------------------------------
//
// Function: ShtDelete
//
// Synopsis: Deletes a handle table. Callback is called for every handle in
// the table.
//
// Arguments: [HandleTable] --
// [Callback] --
//
// History: 3-04-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
ShtDelete(
PVOID HandleTable,
PHP_ENUM_CALLBACK_FN Callback
)
{
PSMALL_HANDLE_TABLE Table ;
PSEC_HANDLE_ENTRY Entry ;
PLIST_ENTRY Scan ;
ULONG RefCount ;
Table = (PSMALL_HANDLE_TABLE) HandleTable ;
ShtLockTable( Table );
Table->Flags |= SHT_DELETE_PENDING ;
while ( !IsListEmpty( &Table->List ) )
{
Scan = RemoveHeadList( &Table->List );
Table->Count--;
Entry = (PSEC_HANDLE_ENTRY) Scan ;
Table->PendingHandle = Entry ;
Entry->Flags |= SEC_HANDLE_FLAG_DELETE_PENDING ;
RefCount = Entry->HandleCount ;
Entry->HandleCount = 1;
Entry->RefCount = 1 ;
if ( ( Callback ) &&
( ( Entry->Flags & SEC_HANDLE_FLAG_NO_CALLBACK ) == 0 ) )
{
Callback( &Entry->Handle, Entry->Context, RefCount );
}
LsapFreePrivateHeap( Entry );
}
DsysAssert( Table->Count == 0 );
if ( ( Table->Flags & SHT_NO_SERIALIZE ) == 0 )
{
RtlDeleteCriticalSection( &Table->Lock );
}
if ( (Table->Flags & SHT_NO_FREE) == 0 )
{
LsapFreePrivateHeap( Table );
}
return TRUE ;
}
#if DBG_SHT
//+---------------------------------------------------------------------------
//
// Function: ShtpValidateList
//
// Synopsis: Debug only - validates a handle table
//
// Arguments: [Table] --
//
// History: 3-04-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID
ShtpValidateList(
PSMALL_HANDLE_TABLE Table,
BOOL Locked
)
{
PLIST_ENTRY List ;
PSEC_HANDLE_ENTRY Entry ;
PSEC_HANDLE_ENTRY Back ;
ULONG Count ;
if ( !Locked )
{
ShtLockTable( Table );
}
List = Table->List.Flink ;
Count = 0 ;
while ( List && (List != &Table->List) )
{
Entry = (PSEC_HANDLE_ENTRY) List ;
if ( List->Blink != &Table->List )
{
Back = (PSEC_HANDLE_ENTRY) List->Blink ;
DsysAssertMsg( Back->Handle.dwUpper <= Entry->Handle.dwUpper, "Handle Table Corrupt (1)" );
}
List = List->Flink ;
Count++ ;
}
DsysAssertMsg( List, "Handle Table Corrupt (2)" );
DsysAssertMsg( Count == Table->Count, "Handle Table Corrupt (3)" );
if ( !Locked )
{
ShtUnlockTable( Table );
}
}
#endif
//+---------------------------------------------------------------------------
//
// Function: ShtpFindHandle
//
// Synopsis: General worker function for locating handles in the table
//
// Arguments: [Table] -- Table to search
// [Handle] -- Handle to find
// [Action] -- Action to take
// [Removed] -- Flag if it was removed or just deref'd
//
// History: 3-04-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
PSEC_HANDLE_ENTRY
ShtpFindHandle(
PSMALL_HANDLE_TABLE Table,
PSecHandle Handle,
ULONG Action,
PBOOL Removed OPTIONAL
)
{
PLIST_ENTRY Scan ;
PSEC_HANDLE_ENTRY Entry ;
BOOL Delete = FALSE ;
Entry = NULL ;
BOOL Locked ;
BOOL Checked ;
Locked = (Action & SHT_ACTION_LOCKED) == SHT_ACTION_LOCKED ;
Checked = (Action & SHTP_HANDLE_CHECKED) == SHTP_HANDLE_CHECKED ;
Action = Action & SHT_ACTION_MASK ;
#if DBG_SHT
ShtpValidateList( Table, Locked );
#endif
if ( !Locked )
{
ShtLockTable( Table );
}
if ( ( Table->Flags & SHT_DELETE_PENDING ) &&
( Table->PendingHandle ) )
{
if ( (Handle->dwUpper == Table->PendingHandle->Handle.dwUpper) &&
(Handle->dwLower == Table->PendingHandle->Handle.dwLower) )
{
Entry = Table->PendingHandle ;
DsysAssert( Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING );
goto FoundEntry ;
}
}
Scan = Table->List.Flink ;
while ( Scan != &Table->List )
{
Entry = (PSEC_HANDLE_ENTRY) Scan ;
if ( Entry->Handle.dwUpper == Handle->dwUpper )
{
if ( Entry->Handle.dwLower == Handle->dwLower )
{
break;
}
}
if ( Entry->Handle.dwUpper > Handle->dwUpper )
{
Entry = NULL ;
break;
}
Scan = Entry->List.Flink ;
Entry = NULL ;
}
if ( Entry &&
((Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING) != 0 ) )
{
DebugLog(( DEB_WARN, "Entry %p on list but marked delete pending\n", Entry ));
Entry = NULL ;
}
if ( Entry &&
( Entry->HandleCount == 0 ) &&
( !Checked ) )
{
DebugLog(( DEB_TRACE_HANDLES, "Entry %p has handle count 0, no ref for %p:%p \n",
Entry,
Entry->Handle.dwUpper,
Entry->Handle.dwLower ));
Entry = NULL ;
}
FoundEntry :
if ( Entry )
{
switch ( Action )
{
case SHT_ACTION_ADDHANDLE:
Entry->HandleIssuedCount++;
Entry->HandleCount++ ;
//
// Fall through to the ADDREF behavior:
//
case SHT_ACTION_ADDREF:
Entry->RefCount++;
break;
case SHT_ACTION_DELHANDLE:
if ( Entry->HandleCount )
{
Entry->HandleCount-- ;
}
else
{
break;
}
//
// Fall through to the DELREF behavior
//
case SHT_ACTION_DELREF:
case SHT_ACTION_FORCEDEL:
Entry->RefCount -- ;
DsysAssert( Entry->RefCount >= Entry->HandleCount );
if ( ( Entry->RefCount == 0 ) ||
( Action == SHT_ACTION_FORCEDEL ) )
{
if ( ( Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING ) == 0 )
{
RemoveEntryList( &Entry->List );
Table->Count-- ;
}
Delete = TRUE ;
}
break;
case SHT_ACTION_VALIDATE:
default:
break;
}
}
if ( !Locked )
{
ShtUnlockTable( Table );
}
if ( Removed )
{
*Removed = Delete ;
}
#if DBG_SHT
ShtpValidateList( Table, Locked );
#endif
return Entry ;
}
//+---------------------------------------------------------------------------
//
// Function: ShtpPopHandle
//
// Synopsis: Private function for the large package. Pops a handle out of
// the table for redistribution.
//
// Arguments: [Table] --
//
// History: 3-04-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
PSEC_HANDLE_ENTRY
ShtpPopHandle(
PSMALL_HANDLE_TABLE Table
)
{
PLIST_ENTRY List ;
ShtLockTable( Table );
if ( !IsListEmpty( &Table->List ) )
{
List = RemoveHeadList( &Table->List );
Table->Count-- ;
}
else
{
List = NULL ;
}
ShtUnlockTable( Table );
#if DBG_SHT
ShtpValidateList( Table, FALSE );
#endif
return ((PSEC_HANDLE_ENTRY) List );
}
//+---------------------------------------------------------------------------
//
// Function: ShtpInsertHandle
//
// Synopsis: Worker function for lht. Inserts an existing entry into a table
//
// Arguments: [Table] --
// [Entry] --
//
// History: 3-04-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID
ShtpInsertHandle(
PSMALL_HANDLE_TABLE Table,
PSEC_HANDLE_ENTRY Entry
)
{
PLIST_ENTRY Scan ;
PSEC_HANDLE_ENTRY Compare ;
ShtLockTable( Table );
Scan = Table->List.Flink ;
while ( Scan != &Table->List )
{
Compare = (PSEC_HANDLE_ENTRY) Scan ;
if ( Compare->Handle.dwUpper >= Entry->Handle.dwUpper )
{
break;
}
Scan = Compare->List.Flink ;
Compare = NULL ;
}
InsertTailList( Scan, &Entry->List );
Table->Count++;
ShtUnlockTable( Table );
#if DBG_SHT
ShtpValidateList( Table, FALSE );
#endif
}
//+---------------------------------------------------------------------------
//
// Function: ShtAddHandle
//
// Synopsis: Add a handle to the table.
//
// Arguments: [HandleTable] --
// [Handle] --
//
// History: 3-04-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
ShtAddHandle(
PVOID HandleTable,
PSecHandle Handle,
PVOID Context,
ULONG Flags
)
{
PSMALL_HANDLE_TABLE Table ;
PSEC_HANDLE_ENTRY Entry ;
PSEC_HANDLE_ENTRY Compare ;
PLIST_ENTRY Scan ;
Table = (PSMALL_HANDLE_TABLE) HandleTable ;
//
// Need to make whole add operation atomic
//
ShtLockTable( Table );
if ( ShtpFindHandle( Table,
Handle,
SHT_ACTION_ADDHANDLE | SHT_ACTION_LOCKED,
NULL ) )
{
ShtUnlockTable( Table );
if ( Table->Flags & HANDLE_PACKAGE_REQUIRE_UNIQUE )
{
return FALSE ;
}
return TRUE ;
}
Entry = (PSEC_HANDLE_ENTRY) LsapAllocatePrivateHeap(
sizeof( SEC_HANDLE_ENTRY ) );
if ( Entry )
{
Entry->RefCount = 1;
Entry->HandleCount = 1;
Entry->Handle = *Handle ;
Entry->Context = Context ;
Entry->HandleIssuedCount = 1;
Scan = Table->List.Flink ;
while ( Scan != &Table->List )
{
Compare = (PSEC_HANDLE_ENTRY) Scan ;
if ( Compare->Handle.dwUpper >= Handle->dwUpper )
{
break;
}
Scan = Compare->List.Flink ;
Compare = NULL ;
}
Entry->Flags = Flags ;
InsertTailList( Scan, &Entry->List );
Table->Count++;
ShtUnlockTable( Table );
#if DBG_SHT
ShtpValidateList( Table, FALSE );
#endif
return TRUE ;
}
ShtUnlockTable( Table );
return FALSE ;
}
//+---------------------------------------------------------------------------
//
// Function: ShtDeleteHandle
//
// Synopsis: Deletes a handle from the table
//
// Arguments: [HandleTable] --
// [Handle] --
// [Force] --
//
// History: 3-04-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
ShtDeleteHandle(
PVOID HandleTable,
PSecHandle Handle,
ULONG Options
)
{
PSEC_HANDLE_ENTRY Entry ;
BOOL Delete ;
Entry = ShtpFindHandle( (PSMALL_HANDLE_TABLE) HandleTable,
Handle,
(Options & DELHANDLE_FORCE) ?
SHT_ACTION_FORCEDEL : SHT_ACTION_DELHANDLE,
&Delete );
if ( Entry )
{
if ( Delete )
{
PSMALL_HANDLE_TABLE Table = (PSMALL_HANDLE_TABLE) HandleTable ;
if ( ( Table->DeleteCallback ) &&
( ( Options & DELHANDLE_NO_CALLBACK ) == 0 ) &&
( ( Entry->Flags & SEC_HANDLE_FLAG_NO_CALLBACK ) == 0 ) )
{
Table->DeleteCallback(
&Entry->Handle,
Entry->Context,
Entry->HandleIssuedCount // Entry->RefCount
);
}
if ( (Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING) == 0 )
{
LsapFreePrivateHeap( Entry );
}
}
return TRUE ;
}
return FALSE ;
}
//+---------------------------------------------------------------------------
//
// Function: ShtValidateHandle
//
// Synopsis: Validates a handle is listed in the table.
//
// Arguments: [HandleTable] --
// [Handle] --
//
// History: 3-04-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
ShtValidateHandle(
PVOID HandleTable,
PSecHandle Handle,
BOOL Deref
)
{
PSEC_HANDLE_ENTRY Entry ;
BOOL Delete = FALSE ;
PSMALL_HANDLE_TABLE Table = (PSMALL_HANDLE_TABLE) HandleTable ;
Entry = ShtpFindHandle( Table,
Handle,
(Deref ? SHT_ACTION_DELHANDLE : SHT_ACTION_VALIDATE),
&Delete );
if ( Entry )
{
if ( Delete )
{
if ( ( Table->DeleteCallback ) &&
( ( Entry->Flags & SEC_HANDLE_FLAG_NO_CALLBACK ) == 0 ) )
{
Table->DeleteCallback(
&Entry->Handle,
Entry->Context,
Entry->HandleIssuedCount // Entry->HandleCount
);
}
if ( ( Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING ) == 0 )
{
LsapFreePrivateHeap( Entry );
}
}
return TRUE ;
}
else
{
return FALSE ;
}
}
PVOID
ShtRefHandle(
PVOID HandleTable,
PSecHandle Handle
)
{
PSEC_HANDLE_ENTRY Entry ;
Entry = ShtpFindHandle( (PSMALL_HANDLE_TABLE) HandleTable,
Handle,
SHT_ACTION_ADDREF,
NULL );
return Entry ;
}
VOID
ShtDerefHandleKey(
PVOID HandleTable,
PVOID HandleKey
)
{
PSMALL_HANDLE_TABLE Table = (PSMALL_HANDLE_TABLE) HandleTable ;
PSEC_HANDLE_ENTRY Entry = (PSEC_HANDLE_ENTRY) HandleKey ;
BOOL Delete = FALSE ;
ShtLockTable( Table );
Entry->RefCount -- ;
DsysAssert( Entry->RefCount >= Entry->HandleCount );
if ( ( Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING ) == 0 )
{
if ( Entry->RefCount == 0 )
{
RemoveEntryList( &Entry->List );
Delete = TRUE ;
Table->Count-- ;
}
}
ShtUnlockTable( Table );
if ( Delete )
{
if ( ( Table->DeleteCallback ) &&
( ( Entry->Flags & SEC_HANDLE_FLAG_NO_CALLBACK ) == 0 ) )
{
Table->DeleteCallback( &Entry->Handle, Entry->Context, Entry->HandleCount );
}
if ( ( Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING ) == 0 )
{
LsapFreePrivateHeap( Entry );
}
}
}
//+---------------------------------------------------------------------------
//
// Function: ShtGetHandleContext
//
// Synopsis: Returns the context pointer associated with the handle
//
// Arguments: [HandleTable] --
// [Handle] --
//
// History: 8-17-98 RichardW Created
//
// Notes: Adds a reference so it can't be deleted while in use.
//
//----------------------------------------------------------------------------
PVOID
ShtGetHandleContext(
PVOID HandleTable,
PSecHandle Handle
)
{
PSEC_HANDLE_ENTRY Entry ;
Entry = ShtpFindHandle( (PSMALL_HANDLE_TABLE) HandleTable,
Handle,
SHT_ACTION_ADDREF,
NULL );
if ( Entry )
{
return Entry->Context ;
}
return NULL ;
}
//+---------------------------------------------------------------------------
//
// Function: ShtReleaseContext
//
// Synopsis: Deref's a handle entry
//
// Arguments: [HandleTable] --
// [Handle] --
//
// History: 8-17-98 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
ShtReleaseContext(
PVOID HandleTable,
PSecHandle Handle
)
{
PSEC_HANDLE_ENTRY Entry ;
BOOL Delete ;
Entry = ShtpFindHandle( (PSMALL_HANDLE_TABLE) HandleTable,
Handle,
SHT_ACTION_DELREF,
&Delete );
if ( Entry )
{
if ( Delete )
{
PSMALL_HANDLE_TABLE Table = (PSMALL_HANDLE_TABLE) HandleTable ;
if ( ( Table->DeleteCallback ) &&
( ( Entry->Flags & SEC_HANDLE_FLAG_NO_CALLBACK ) == 0 ) )
{
Table->DeleteCallback( &Entry->Handle, Entry->Context, Entry->RefCount );
}
if ( ( Entry->Flags & SEC_HANDLE_FLAG_DELETE_PENDING ) == 0 )
{
LsapFreePrivateHeap( Entry );
}
}
return TRUE ;
}
return FALSE ;
}