mirror of https://github.com/tongzx/nt5src
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.
770 lines
16 KiB
770 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1995-1996 Microsoft Corporation
|
|
|
|
Module Name :
|
|
setable.cxx
|
|
|
|
Abstract:
|
|
This module declares the SE_TABLE object which consists
|
|
of all the extensions for W3 service
|
|
|
|
Author:
|
|
|
|
Murali R. Krishnan ( MuraliK ) 18-July-1996
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Project:
|
|
|
|
W3 Services DLL
|
|
|
|
--*/
|
|
|
|
|
|
/************************************************************
|
|
* Include Headers
|
|
************************************************************/
|
|
|
|
# include <isapip.hxx>
|
|
|
|
# include "isapidll.hxx"
|
|
# include "setable.hxx"
|
|
|
|
# include <issched.hxx>
|
|
|
|
/************************************************************
|
|
* Global Data
|
|
************************************************************/
|
|
|
|
PSE_TABLE g_psextensions; // all extensions
|
|
static BOOL g_fExtInitialized = FALSE;
|
|
|
|
|
|
|
|
|
|
/**************************************************
|
|
* Member functions of SESD_LIST
|
|
**************************************************/
|
|
|
|
|
|
SESD_LIST::SESD_LIST(VOID)
|
|
: m_idWorkItem( 0)
|
|
{
|
|
INITIALIZE_CRITICAL_SECTION( &m_csLock);
|
|
InitializeListHead( &m_Head);
|
|
|
|
} // SESD_LIST::SESD_LIST()
|
|
|
|
|
|
SESD_LIST::~SESD_LIST(VOID)
|
|
{
|
|
// Should be all done
|
|
DBG_ASSERT( 0 == m_idWorkItem);
|
|
DBG_ASSERT( IsListEmpty( &m_Head));
|
|
|
|
DeleteCriticalSection( &m_csLock);
|
|
|
|
} // SESD_LIST::~SESD_LIST()
|
|
|
|
|
|
|
|
VOID
|
|
SESD_LIST::ScheduleExtensionForDeletion(
|
|
IN PHSE psExt
|
|
)
|
|
{
|
|
Lock();
|
|
|
|
// add to the list
|
|
InsertHeadList( &m_Head, &psExt->m_ListEntry);
|
|
|
|
if ( 0 == m_idWorkItem) {
|
|
// schedule the work item if not scheduled one already
|
|
m_idWorkItem = ScheduleWorkItem( SchedulerCallback, this, 0);
|
|
DBG_ASSERT( 0 != m_idWorkItem);
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return;
|
|
} // SESD_LIST::ScheduleExtensionForDeletion()
|
|
|
|
|
|
|
|
VOID
|
|
SESD_LIST::WaitTillFinished(VOID)
|
|
{
|
|
Lock();
|
|
|
|
// wait until nothing is scheduled
|
|
|
|
while ( 0 != m_idWorkItem) {
|
|
|
|
// let it drain itself while in the unlocked state
|
|
Unlock();
|
|
Sleep( 200 );
|
|
Lock();
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return;
|
|
} // SESD_LIST::WaitTillFinished()
|
|
|
|
|
|
|
|
VOID
|
|
WINAPI
|
|
SESD_LIST::SchedulerCallback(
|
|
void *pvContext
|
|
)
|
|
{
|
|
DBG_ASSERT( NULL != pvContext);
|
|
|
|
((SESD_LIST *)pvContext)->DeleteScheduledExtensions();
|
|
|
|
return;
|
|
} // SESD_LIST::SchedulerCallback()
|
|
|
|
|
|
VOID
|
|
SESD_LIST::DeleteScheduledExtensions(VOID)
|
|
{
|
|
PHSE psExt;
|
|
|
|
Lock();
|
|
|
|
// If it is scheduled there better be a reason why
|
|
DBG_ASSERT( 0 != m_idWorkItem);
|
|
|
|
while ( !IsListEmpty( &m_Head)) {
|
|
|
|
// extract extension from the list and delete it
|
|
|
|
psExt = CONTAINING_RECORD( m_Head.Flink,
|
|
HSE_BASE,
|
|
m_ListEntry );
|
|
|
|
RemoveEntryList( &psExt->m_ListEntry );
|
|
InitializeListHead( &psExt->m_ListEntry);
|
|
|
|
delete psExt;
|
|
|
|
} // while
|
|
|
|
|
|
// reset to 0 to indicate that we are done
|
|
m_idWorkItem = 0;
|
|
|
|
Unlock();
|
|
|
|
return;
|
|
} // SESD_LIST::DeleteScheduledExtensions()
|
|
|
|
|
|
|
|
/**************************************************
|
|
* Member functions of SE_TABLE
|
|
**************************************************/
|
|
|
|
|
|
SE_TABLE::SE_TABLE(VOID)
|
|
: m_cRefWams ( 0)
|
|
{
|
|
IF_DEBUG( INIT_CLEAN) {
|
|
DBGPRINTF(( DBG_CONTEXT, "Constructing SE_TABLE(%08x)\n", this));
|
|
}
|
|
|
|
INITIALIZE_CRITICAL_SECTION( &m_csLock);
|
|
InitializeListHead( &m_ExtensionHead);
|
|
|
|
} // SE_TABLE::SE_TABLE()
|
|
|
|
|
|
SE_TABLE::~SE_TABLE(VOID)
|
|
{
|
|
IF_DEBUG( INIT_CLEAN) {
|
|
DBGPRINTF(( DBG_CONTEXT, "Deleteing SE_TABLE(%08x) cWamRef=%d\n",
|
|
this, m_cRefWams));
|
|
}
|
|
|
|
// unload all the extensions if not already done
|
|
DBG_REQUIRE( UnloadExtensions());
|
|
|
|
// No WAM should be holding ref to the SE_TABLE
|
|
DBG_ASSERT( 0 == m_cRefWams);
|
|
|
|
DeleteCriticalSection( &m_csLock);
|
|
DBG_ASSERT( IsListEmpty( &m_ExtensionHead));
|
|
|
|
} // SE_TABLE::~SE_TABLE()
|
|
|
|
|
|
|
|
|
|
VOID
|
|
SE_TABLE::Print(VOID)
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
int i = 0;
|
|
|
|
// NYI: I need to write code to print out data here.
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" SE_TABLE(%08x) ListHead=%08x [%08x:%08x] cRefWams=%d\n",
|
|
this, &m_ExtensionHead,
|
|
m_ExtensionHead.Flink, m_ExtensionHead.Blink,
|
|
m_cRefWams
|
|
));
|
|
|
|
Lock();
|
|
|
|
for ( pEntry = m_ExtensionHead.Flink;
|
|
(pEntry != &m_ExtensionHead);
|
|
pEntry = pEntry->Flink, i++ )
|
|
{
|
|
HSE_BASE *
|
|
pExtension = CONTAINING_RECORD( pEntry, HSE_BASE, m_ListEntry );
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" Dll(%d): HSE_BASE = %08x [%08x:%08x]; Module=%s\n",
|
|
i, pExtension,
|
|
pExtension->m_ListEntry.Flink,
|
|
pExtension->m_ListEntry.Blink,
|
|
pExtension->QueryModuleName()));
|
|
} // for all entries in table
|
|
|
|
Unlock();
|
|
|
|
return;
|
|
} // SE_TABLE::Print()
|
|
|
|
|
|
BOOL
|
|
SE_TABLE::GetExtension(
|
|
IN const CHAR * pchModuleName,
|
|
IN HANDLE hImpersonation,
|
|
IN BOOL fCacheImpersonation,
|
|
IN BOOL fCache,
|
|
OUT PHSE * ppsExt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves an extension's DLL entry point
|
|
|
|
The appropriate user should be impersonated before calling this function
|
|
|
|
Arguments:
|
|
|
|
pchModuleName - Extension DLL module name
|
|
hImpersonation - Impersonation token of user making this call
|
|
fCacheImpersonation - if TRUE, the hImpersonation can be cached
|
|
fCache - TRUE if this item should be cached, FALSE otherwise
|
|
ppsExt - pointer to the Extensions handler object
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE on error
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
HSE_BASE * pExtension = NULL;
|
|
BOOL fRet = FALSE;
|
|
|
|
IF_DEBUG( BGI ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "[GetEntryPoint] Looking for module %s\n",
|
|
pchModuleName ));
|
|
|
|
}
|
|
|
|
//
|
|
// NYI: Optimize the locked section here - reduce time spent inside
|
|
// the locked block.
|
|
// Check cache to see if DLL is already loaded
|
|
//
|
|
|
|
DWORD cchModuleName = strlen( pchModuleName);
|
|
|
|
Lock();
|
|
|
|
for ( pEntry = m_ExtensionHead.Flink;
|
|
pEntry != &m_ExtensionHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pExtension = CONTAINING_RECORD( pEntry, HSE_BASE, m_ListEntry );
|
|
|
|
if ( pExtension->IsMatch( pchModuleName, cchModuleName)) {
|
|
//
|
|
// Already Loaded, return the extension object after access check
|
|
//
|
|
|
|
fRet = TRUE;
|
|
break;
|
|
} // a match
|
|
} // for all entries in table
|
|
|
|
IF_DEBUG( BGI ) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[GetEntryPoint] Lookup module %s => fRet =%d\n",
|
|
pchModuleName, fRet ));
|
|
}
|
|
|
|
if ( !fRet) {
|
|
|
|
//
|
|
// The module is not loaded. Load the module now.
|
|
// The module name refers to an ISAPI application.
|
|
// Try ISAPI app loader now...
|
|
pExtension = HSE_APPDLL::LoadModule( pchModuleName,
|
|
hImpersonation,
|
|
fCache);
|
|
|
|
fRet = (pExtension != NULL);
|
|
if (fRet) {
|
|
if ( !fCache) {
|
|
|
|
// Temporarily the ref count will drop to 0, but the code below
|
|
// will bump this up to 1.
|
|
|
|
pExtension->Dereference();
|
|
} else {
|
|
// add this extension to the list of cached extensions
|
|
InsertIntoListWithLock( pExtension);
|
|
}
|
|
} else {
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"LoadModule failed with %x\n",GetLastError()));
|
|
}
|
|
}
|
|
|
|
if ( fRet ) {
|
|
|
|
if ( pExtension->AccessCheck( hImpersonation, fCacheImpersonation)) {
|
|
|
|
DBG_ASSERT( pExtension != NULL);
|
|
|
|
if ( ppsExt != NULL) {
|
|
// ref the object before giving ptr away
|
|
pExtension->Reference();
|
|
*ppsExt = pExtension;
|
|
}
|
|
} else {
|
|
fRet = FALSE;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return fRet;
|
|
} // SE_TABLE::GetExtension()
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
SE_TABLE::RefreshAcl(
|
|
IN const CHAR * pchDLL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function reloads the ACL on an ISAPI Application .dll after that
|
|
.dll has already been loaded.
|
|
|
|
Arguments:
|
|
|
|
pchDLL - pointer to the ISAPI DLL for which ACL has to be refreshed.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
PHSE pExtension;
|
|
BOOL fRet = FALSE;
|
|
BOOL fFound = FALSE;
|
|
|
|
IF_DEBUG( BGI )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[RefreshISAPIAcl] Rereading ACL for %s\n",
|
|
pchDLL ));
|
|
|
|
}
|
|
|
|
//
|
|
// Check cache to see if the DLL is loaded
|
|
//
|
|
DWORD cchDll = strlen( pchDLL);
|
|
|
|
Lock();
|
|
|
|
for ( pEntry = m_ExtensionHead.Flink;
|
|
pEntry != &m_ExtensionHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pExtension = CONTAINING_RECORD( pEntry, HSE_BASE, m_ListEntry );
|
|
|
|
if ( pExtension->IsMatch( pchDLL, cchDll) )
|
|
{
|
|
//
|
|
// Force an access check on the next request with the new ACL
|
|
//
|
|
|
|
fRet = pExtension->LoadAcl();
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
if ( !fFound) { SetLastError( ERROR_FILE_NOT_FOUND ); }
|
|
|
|
return (fRet);
|
|
} // SE_TABLE::RefreshAcl()
|
|
|
|
|
|
|
|
BOOL
|
|
SE_TABLE::RefreshAcl(
|
|
IN DWORD dwId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function reloads the ACL on an ISAPI Application .dll after that
|
|
.dll has already been loaded.
|
|
|
|
Arguments:
|
|
|
|
pchDLL - pointer to the ISAPI DLL for which ACL has to be refreshed.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
PHSE pExtension;
|
|
BOOL fRet = FALSE;
|
|
BOOL fFound = FALSE;
|
|
|
|
IF_DEBUG( BGI )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[RefreshISAPIAcl] Rereading ACL for %d\n",
|
|
dwId ));
|
|
|
|
}
|
|
|
|
//
|
|
// Check cache to see if the DLL is loaded
|
|
//
|
|
|
|
Lock();
|
|
|
|
for ( pEntry = m_ExtensionHead.Flink;
|
|
pEntry != &m_ExtensionHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pExtension = CONTAINING_RECORD( pEntry, HSE_BASE, m_ListEntry );
|
|
|
|
if ( pExtension->GetDirMonitorId() == dwId )
|
|
{
|
|
//
|
|
// Force an access check on the next request with the new ACL
|
|
//
|
|
|
|
fRet = pExtension->LoadAcl();
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
if ( !fFound) { SetLastError( ERROR_FILE_NOT_FOUND ); }
|
|
|
|
return (fRet);
|
|
} // SE_TABLE::RefreshAcl()
|
|
|
|
|
|
|
|
BOOL
|
|
SE_TABLE::FlushAccessToken(
|
|
IN HANDLE hAccTok
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reset last successfull user it same as hAccTok
|
|
|
|
Arguments:
|
|
|
|
hAccTok - access token to remove from cache
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
HSE_APPDLL * pExtension;
|
|
BOOL fRet = FALSE;
|
|
BOOL fFound = FALSE;
|
|
|
|
IF_DEBUG( BGI )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[FlushAccessToken] removing handle %x from cache\n",
|
|
hAccTok ));
|
|
|
|
}
|
|
|
|
//
|
|
// Check cache for access token
|
|
//
|
|
|
|
Lock();
|
|
|
|
for ( pEntry = m_ExtensionHead.Flink;
|
|
pEntry != &m_ExtensionHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pExtension = CONTAINING_RECORD( pEntry, HSE_APPDLL, m_ListEntry );
|
|
|
|
if ( pExtension->QueryLastSuccessfulUser() == hAccTok )
|
|
{
|
|
//
|
|
// Force an access check on the next request for this token
|
|
//
|
|
|
|
pExtension->SetLastSuccessfulUser( NULL );
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return TRUE;
|
|
} // SE_TABLE::RefreshAcl()
|
|
|
|
|
|
|
|
BOOL
|
|
SE_TABLE::UnloadExtensions(VOID)
|
|
{
|
|
PHSE psExt;
|
|
|
|
DBG_ASSERT( m_cRefWams == 0);
|
|
|
|
Lock();
|
|
|
|
while ( !IsListEmpty( &m_ExtensionHead )) {
|
|
|
|
psExt = CONTAINING_RECORD( m_ExtensionHead.Flink,
|
|
HSE_BASE,
|
|
m_ListEntry );
|
|
|
|
//
|
|
// Move from the current list to the UnloadList
|
|
//
|
|
|
|
RemoveEntryList( &psExt->m_ListEntry );
|
|
InitializeListHead( &psExt->m_ListEntry); // Cleanup
|
|
|
|
IF_DEBUG( INIT_CLEAN) {
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Deref of HSE_APPDLL(%08x) = %s. Ref=%d\n",
|
|
psExt, psExt->QueryModuleName(), psExt->RefCount()));
|
|
}
|
|
|
|
//
|
|
// Cause an Unload of the ISAPI DLL
|
|
// Ugly cast should be cleaned up post-beta2
|
|
//
|
|
|
|
DBG_REQUIRE( ((HSE_APPDLL * )psExt)->Unload() == NO_ERROR);
|
|
|
|
// Decrement the life reference count of the ISAPI DLL structure
|
|
if ( !psExt->Dereference()) {
|
|
|
|
delete psExt;
|
|
}
|
|
} // while
|
|
|
|
Unlock();
|
|
|
|
// Drain the list of non-cached extensions scheduled for deletion
|
|
m_sesdExtensions.WaitTillFinished();
|
|
|
|
return (TRUE);
|
|
} // SE_TABLE::UnloadExtensions()
|
|
|
|
|
|
VOID
|
|
SE_TABLE::PrintRequestCounts(VOID)
|
|
/*++
|
|
Description:
|
|
Prints a summary of all the ISAPI Dlls loaded and the # requests
|
|
pending in each of them primarily for debugging purposes.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Return:
|
|
None
|
|
--*/
|
|
{
|
|
PHSE psExt;
|
|
PLIST_ENTRY pEntry;
|
|
|
|
Lock();
|
|
|
|
if( m_ExtensionHead.Flink != &m_ExtensionHead ) {
|
|
DBGPRINTF( (DBG_CONTEXT, "SE_TABLE::Printing refs for all loaded ISAPI\n") );
|
|
|
|
for ( pEntry = m_ExtensionHead.Flink;
|
|
pEntry != &m_ExtensionHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
|
|
psExt = CONTAINING_RECORD( pEntry, HSE_BASE, m_ListEntry );
|
|
|
|
RemoveEntryList( &psExt->m_ListEntry );
|
|
|
|
DBGPRINTF( (DBG_CONTEXT,
|
|
" \'%s\') has RefCount = %d\n",
|
|
psExt->QueryModuleName(),
|
|
psExt->RefCount()
|
|
) );
|
|
} // for
|
|
DBGPRINTF( (DBG_CONTEXT, "-------------------------------\n") );
|
|
} // if
|
|
|
|
Unlock();
|
|
|
|
return;
|
|
} // SE_TABLE::PrintRequestCounts()
|
|
|
|
|
|
|
|
VOID
|
|
SE_TABLE::ReleaseExtension( IN PHSE psExt)
|
|
{
|
|
// decrement ref count and remove from table if needed
|
|
if ( !psExt->Dereference()) {
|
|
|
|
if ( psExt->IsCached())
|
|
{
|
|
RemoveFromList( psExt);
|
|
delete psExt;
|
|
}
|
|
else {
|
|
|
|
// non cached ISAPIs should be deleted (TerminateExtension
|
|
// call should be called) from a different thread because
|
|
// this thread could be an ISAPI thread (in case of pended
|
|
// ISAPI this could be the DONE_WITH_SESSION thread)
|
|
|
|
m_sesdExtensions.ScheduleExtensionForDeletion( psExt);
|
|
}
|
|
}
|
|
|
|
return;
|
|
} // SE_TABLE::ReleaseExtension()
|
|
|
|
|
|
|
|
APIERR
|
|
InitializeHseExtensions(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the extension list cache
|
|
|
|
|
|
Return Value:
|
|
|
|
0 on success, win32 error on failure
|
|
|
|
--*/
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT, " InitExtensions() entered \n"));
|
|
|
|
DWORD err = NO_ERROR;
|
|
|
|
// UNDONE needs to move into wam.dll DllInit
|
|
if ( err == NO_ERROR ) {
|
|
|
|
// initialize other objects
|
|
g_psextensions = new SE_TABLE();
|
|
|
|
if ( (g_psextensions == NULL) ) {
|
|
|
|
err = (ERROR_NOT_ENOUGH_MEMORY);
|
|
if ( g_psextensions != NULL ) {
|
|
delete (g_psextensions);
|
|
g_psextensions = NULL;
|
|
}
|
|
|
|
} else {
|
|
g_fExtInitialized = TRUE;
|
|
}
|
|
}
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, " Leaving InitExtensions().\n" ));
|
|
|
|
return ( err);
|
|
|
|
} // InitializeHseExtensions()
|
|
|
|
|
|
|
|
VOID
|
|
CleanupHseExtensions(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Walks list and unloads each extension. No clients should be in an
|
|
extension at this point
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, " TerminateExtensions() called \n"
|
|
));
|
|
|
|
|
|
if ( !g_fExtInitialized )
|
|
return;
|
|
|
|
DBG_ASSERT( g_psextensions != NULL);
|
|
|
|
delete g_psextensions;
|
|
|
|
return;
|
|
} // CleanupHseExtensions()
|
|
|
|
|
|
/************************ End of File ***********************/
|