1380 lines
34 KiB
C++
1380 lines
34 KiB
C++
/*++
|
|
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
filtinit.cxx
|
|
|
|
Abstract:
|
|
|
|
This module contains the Microsoft HTTP server filter module for stuff that
|
|
has to do with filter initialization, loading and unloading.
|
|
|
|
Author:
|
|
|
|
John Ludeman (johnl) 06-Aug-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "w3p.hxx"
|
|
|
|
//
|
|
// If the request doesn't specify an entry point, default to using
|
|
// this
|
|
//
|
|
|
|
#define SF_DEFAULT_ENTRY "HttpFilterProc"
|
|
#define SF_VERSION_ENTRY "GetFilterVersion"
|
|
#define SF_TERM_ENTRY "TerminateFilter"
|
|
|
|
//
|
|
// Name of the value under the parameters key containing the list of
|
|
// filter dlls. This was the value for IIS 1.0 and 2.0. Global filters
|
|
// can still be specified under this key.
|
|
//
|
|
|
|
#define HTTP_FILTER_DLLS "Filter DLLs"
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
static BOOL g_fInitialized = FALSE;
|
|
LIST_ENTRY g_FilterHead; // List of filter DLLs
|
|
CRITICAL_SECTION g_csFilterDlls;
|
|
|
|
|
|
//
|
|
// Prototypes
|
|
//
|
|
|
|
BOOL
|
|
UpdateInstanceFilters(
|
|
IN const CHAR * pszNewDll,
|
|
IN const CHAR * pszOldDll,
|
|
IN W3_SERVER_INSTANCE * pInst
|
|
);
|
|
|
|
|
|
BOOL
|
|
AdjustInstanceFilterListFlags( IN PVOID pvContext1,
|
|
IN PVOID pvContext2,
|
|
IN IIS_SERVER_INSTANCE *pInstance );
|
|
|
|
|
|
FILTER_LIST *
|
|
InitializeFilters(
|
|
BOOL * pfAnySecureFilters,
|
|
W3_IIS_SERVICE * pSvc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads the global filter DLLs and their corresponding entry point. Note the
|
|
global filter list does not allow unloads.
|
|
|
|
g_fInitialized should only be set after all of the global filters are
|
|
loaded.
|
|
|
|
Arguments:
|
|
|
|
pfAnySecureFilters - Set to TRUE if there are any secure filters
|
|
pSvc - Pointer to service global. The global service pointer isn't
|
|
initialized yet so we pass it in for global filter initialization.
|
|
Only used for logging events.
|
|
|
|
Return Value:
|
|
|
|
Global filter list or NULL if an error occurred
|
|
|
|
--*/
|
|
{
|
|
CHAR szFilterKey[MAX_PATH+1];
|
|
FILTER_LIST * pfl;
|
|
DWORD cb;
|
|
CHAR szLoadOrder[1024];
|
|
CHAR szDllName[MAX_PATH+1];
|
|
CHAR * pchFilter;
|
|
CHAR * pchComma;
|
|
MB mb( (IMDCOM*) pSvc->QueryMDObject() );
|
|
DWORD fEnabled;
|
|
DWORD cFilters;
|
|
HKEY hkeyParam = NULL;
|
|
DWORD err = NO_ERROR;
|
|
BOOL fOpened;
|
|
|
|
INITIALIZE_CRITICAL_SECTION( &g_csFilterDlls );
|
|
InitializeListHead( &g_FilterHead );
|
|
|
|
strcpy( szFilterKey, IIS_MD_LOCAL_MACHINE_PATH "/" W3_SERVICE_NAME_A );
|
|
strcat( szFilterKey, IIS_MD_ISAPI_FILTERS );
|
|
DBG_ASSERT( strlen( szFilterKey ) + 1 < sizeof( szFilterKey ));
|
|
|
|
pfl = new FILTER_LIST();
|
|
|
|
if ( !pfl ) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Loop through filter keys, if we can't access the metabase, we assume
|
|
// success and continue
|
|
//
|
|
|
|
if ( !mb.Open( szFilterKey,
|
|
METADATA_PERMISSION_READ ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[InitializeFilterList] Cannot open path %s, error %lu\n",
|
|
szFilterKey, GetLastError() ));
|
|
return pfl;
|
|
}
|
|
fOpened = TRUE;
|
|
|
|
//
|
|
// Get the filter load order
|
|
//
|
|
|
|
cb = sizeof( szLoadOrder );
|
|
*szLoadOrder = '\0';
|
|
|
|
if ( !mb.GetString( "",
|
|
MD_FILTER_LOAD_ORDER,
|
|
IIS_MD_UT_SERVER,
|
|
szLoadOrder,
|
|
&cb,
|
|
0 ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[InitializeFilterList] Cannot load filter load order, error %lu\n",
|
|
szFilterKey, GetLastError() ));
|
|
delete pfl;
|
|
return NULL;
|
|
}
|
|
|
|
pchFilter = szLoadOrder;
|
|
|
|
while ( *pchFilter )
|
|
{
|
|
if ( !fOpened &&
|
|
!mb.Open( szFilterKey,
|
|
METADATA_PERMISSION_READ ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[InitializeFilterList] Cannot open path %s, error %lu\n",
|
|
szFilterKey, GetLastError() ));
|
|
break;
|
|
}
|
|
fOpened = TRUE;
|
|
|
|
pchComma = strchr( pchFilter, ',' );
|
|
|
|
if ( pchComma )
|
|
{
|
|
*pchComma = '\0';
|
|
}
|
|
|
|
while ( ISWHITEA( *pchFilter ))
|
|
{
|
|
pchFilter++;
|
|
}
|
|
|
|
fEnabled = TRUE;
|
|
mb.GetDword( pchFilter,
|
|
MD_FILTER_ENABLED,
|
|
IIS_MD_UT_SERVER,
|
|
&fEnabled );
|
|
|
|
if ( fEnabled )
|
|
{
|
|
cb = sizeof(szDllName);
|
|
if ( mb.GetString( pchFilter,
|
|
MD_FILTER_IMAGE_PATH,
|
|
IIS_MD_UT_SERVER,
|
|
szDllName,
|
|
&cb,
|
|
0 ))
|
|
{
|
|
mb.Close();
|
|
fOpened = FALSE;
|
|
|
|
if ( pfl->LoadFilter( &mb, szFilterKey, &fOpened, pchFilter, szDllName, TRUE, pSvc ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[InitializeFilterList] Loaded %s\n",
|
|
szDllName ));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pchComma )
|
|
{
|
|
pchFilter = pchComma + 1;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now load any filters that have been added in the registry for
|
|
// downlevel compatibility
|
|
//
|
|
|
|
err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
W3_PARAMETERS_KEY,
|
|
0,
|
|
KEY_READ,
|
|
&hkeyParam );
|
|
|
|
if( err == NO_ERROR )
|
|
{
|
|
TCHAR * psz;
|
|
TCHAR * pszFilterList = NULL;
|
|
|
|
if ( ReadRegString( hkeyParam,
|
|
&pszFilterList,
|
|
HTTP_FILTER_DLLS,
|
|
"" ))
|
|
{
|
|
RegCloseKey( hkeyParam );
|
|
|
|
psz = pszFilterList;
|
|
|
|
//
|
|
// Parse the comma separated list of dlls
|
|
//
|
|
|
|
INET_PARSER Parser( pszFilterList );
|
|
Parser.SetListMode( TRUE );
|
|
|
|
while ( *(psz = Parser.QueryToken()) )
|
|
{
|
|
if ( pfl->LoadFilter( NULL, NULL, &fOpened, psz, psz, TRUE, pSvc ))
|
|
{
|
|
const CHAR * apszSubString[1];
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[InitializeFilterList] Loaded %s\n",
|
|
psz ));
|
|
|
|
apszSubString[0] = psz;
|
|
|
|
//
|
|
// Log a warning here if we picked up a filter from the
|
|
// registry
|
|
//
|
|
|
|
pSvc->LogEvent( W3_MSG_LOADED_FILTER_FROM_REG,
|
|
1,
|
|
apszSubString,
|
|
NO_ERROR );
|
|
}
|
|
|
|
Parser.NextItem();
|
|
}
|
|
|
|
Parser.RestoreBuffer();
|
|
|
|
TCP_FREE( pszFilterList );
|
|
}
|
|
else
|
|
{
|
|
RegCloseKey( hkeyParam );
|
|
}
|
|
}
|
|
|
|
*pfAnySecureFilters = CheckForSecurityFilter( pfl );
|
|
|
|
g_fInitialized = TRUE;
|
|
|
|
return pfl;
|
|
}
|
|
|
|
|
|
VOID
|
|
TerminateFilters(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unloads any filter DLLs and their corresponding entry point
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
HTTP_FILTER_DLL * pFilterDll;
|
|
|
|
if ( g_fInitialized )
|
|
{
|
|
EnterCriticalSection( &g_csFilterDlls );
|
|
|
|
for ( pEntry = g_FilterHead.Flink;
|
|
pEntry != &g_FilterHead;
|
|
pEntry = g_FilterHead.Flink)
|
|
{
|
|
pFilterDll = CONTAINING_RECORD( pEntry, HTTP_FILTER_DLL, ListEntry );
|
|
|
|
RemoveEntryList( pEntry );
|
|
|
|
//
|
|
// This should delete the filter dll
|
|
//
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[TerminateFilters] Filter %s, ref count %d\n",
|
|
pFilterDll->QueryName(),
|
|
pFilterDll->QueryRef() ));
|
|
|
|
// DBG_ASSERT( pFilterDll->QueryRef() == 1 );
|
|
|
|
HTTP_FILTER_DLL::Dereference( pFilterDll );
|
|
}
|
|
|
|
LeaveCriticalSection( &g_csFilterDlls );
|
|
DeleteCriticalSection( &g_csFilterDlls );
|
|
}
|
|
}
|
|
|
|
HTTP_FILTER_DLL *
|
|
CheckoutFilterDll(
|
|
IN const CHAR * pszFilterDll
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks to see if an existing filter dll is already loaded and ups the ref
|
|
count if it is
|
|
|
|
NOTE: THE FILTER LIST LOCK MUST BE TAKEN PRIOR TO CALLING THIS FUNCTION
|
|
|
|
Arguments:
|
|
|
|
pszFilterDLL - Fully qualified path to filter dll to load
|
|
|
|
Return Value:
|
|
|
|
Pointer to the filter if it's already loaded, NULL if the filter isn't
|
|
loaded
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
HTTP_FILTER_DLL * pFilterDll;
|
|
|
|
for ( pEntry = g_FilterHead.Flink;
|
|
pEntry != &g_FilterHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pFilterDll = CONTAINING_RECORD( pEntry, HTTP_FILTER_DLL, ListEntry );
|
|
|
|
DBG_ASSERT( pFilterDll->CheckSignature() );
|
|
|
|
if ( !lstrcmpi( pFilterDll->QueryName(), pszFilterDll ))
|
|
{
|
|
pFilterDll->Reference();
|
|
return pFilterDll;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOL
|
|
HTTP_FILTER_DLL::LoadDll(
|
|
MB * pmb OPTIONAL,
|
|
const CHAR * pszKeyName,
|
|
LPBOOL pfOpened,
|
|
const CHAR * pszRelFilterPath,
|
|
const CHAR * pszFilterDll
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads the specified dll
|
|
|
|
Arguments:
|
|
|
|
pmb - Open metabase to /filters key
|
|
pszKeyName - metabase filters key name
|
|
pfOpened - updated with TRUE if pmb opened on return
|
|
pszFilterDll - Fully qualified name of filter to load
|
|
|
|
THE GLOBAL FILTER LIST LOCK MUST BE TAKEN PRIOR TO CALLING THIS METHOD
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
HTTP_FILTER_VERSION ver;
|
|
|
|
if ( !m_strName.Copy( pszFilterDll ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Load it and put it in the list, note the filter list lock
|
|
// is still taken
|
|
//
|
|
|
|
m_hmod = LoadLibraryEx( pszFilterDll,
|
|
NULL,
|
|
LOAD_WITH_ALTERED_SEARCH_PATH );
|
|
|
|
if ( g_fIsWindows95 &&
|
|
(m_hmod == NULL) &&
|
|
(GetLastError() == ERROR_FILE_NOT_FOUND) ) {
|
|
|
|
//
|
|
// According to vlads: the behaviour of flags used in loadlibraryex
|
|
// is different between chicago and NT. This caused a problem
|
|
// loading frontpage filters.
|
|
//
|
|
|
|
m_hmod = LoadLibrary( pszFilterDll);
|
|
}
|
|
|
|
if ( !m_hmod )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[LoadDll] LoadLibrary failed with error %d\n",
|
|
GetLastError()));
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Retrieve the entry point
|
|
//
|
|
|
|
m_pfnSFVer = (PFN_SF_VER_PROC) GetProcAddress( m_hmod,
|
|
SF_VERSION_ENTRY );
|
|
m_pfnSFProc = (PFN_SF_DLL_PROC) GetProcAddress( m_hmod,
|
|
SF_DEFAULT_ENTRY );
|
|
m_pfnSFTerm = (PFN_SF_TERM_PROC) GetProcAddress( m_hmod,
|
|
SF_TERM_ENTRY );
|
|
|
|
|
|
if ( !m_pfnSFProc || !m_pfnSFVer )
|
|
{
|
|
//
|
|
// Don't call the terminator if we didn't call the initializer
|
|
//
|
|
|
|
m_pfnSFTerm = NULL;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ver.dwServerFilterVersion = HTTP_FILTER_REVISION;
|
|
|
|
//
|
|
// Call the version entry point and get the filter capabilities
|
|
//
|
|
|
|
if ( !m_pfnSFVer( &ver ) )
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ( pmb &&
|
|
(*pfOpened ||
|
|
pmb->Open( pszKeyName, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE )) )
|
|
{
|
|
*pfOpened = TRUE;
|
|
|
|
if ( !pmb->SetString( pszRelFilterPath,
|
|
MD_FILTER_DESCRIPTION,
|
|
IIS_MD_UT_SERVER,
|
|
ver.lpszFilterDesc,
|
|
0 ) ||
|
|
!pmb->SetDword( pszRelFilterPath,
|
|
MD_FILTER_FLAGS,
|
|
IIS_MD_UT_SERVER,
|
|
ver.dwFlags,
|
|
0 ))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the client didn't specify any of the secure port notifications,
|
|
// supply them with the default of both
|
|
//
|
|
|
|
if ( !(ver.dwFlags & (SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT)))
|
|
{
|
|
ver.dwFlags |= (SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT);
|
|
}
|
|
|
|
m_dwVersion = ver.dwFilterVersion;
|
|
m_dwFlags = (ver.dwFlags & SF_NOTIFY_NONSECURE_PORT) ? ver.dwFlags : 0;
|
|
m_dwSecureFlags = (ver.dwFlags & SF_NOTIFY_SECURE_PORT) ? ver.dwFlags : 0;
|
|
|
|
//
|
|
// Put the new dll on the filter dll list
|
|
//
|
|
|
|
InsertHeadList( &g_FilterHead, &ListEntry );
|
|
|
|
return TRUE;
|
|
|
|
ErrorExit:
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
HTTP_FILTER_DLL::Unload(
|
|
const CHAR * pszDll
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds the existing DLL and sets it up to be unloaded
|
|
|
|
Arguments:
|
|
|
|
pszDLL - Fully qualified path to filter dll to load
|
|
|
|
Return Value:
|
|
|
|
TRUE if the filter DLL was found, FALSE if it wasn't found or it's already
|
|
marked for deletion
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY * pEntry;
|
|
HTTP_FILTER_DLL * pFilterDll;
|
|
|
|
EnterCriticalSection( &g_csFilterDlls );
|
|
|
|
for ( pEntry = g_FilterHead.Flink;
|
|
pEntry != &g_FilterHead;
|
|
pEntry = pEntry->Flink )
|
|
{
|
|
pFilterDll = CONTAINING_RECORD( pEntry, HTTP_FILTER_DLL, ListEntry );
|
|
|
|
DBG_ASSERT( pFilterDll->CheckSignature() );
|
|
|
|
if ( !lstrcmpi( pFilterDll->QueryName(), pszDll ))
|
|
{
|
|
RemoveEntryList( pEntry );
|
|
|
|
LeaveCriticalSection( &g_csFilterDlls );
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection( &g_csFilterDlls );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
FILTER_LIST::LoadFilter(
|
|
MB * pmb,
|
|
LPSTR pszKeyName,
|
|
LPBOOL pfOpened,
|
|
const CHAR * pszRelativeMBPath,
|
|
const CHAR * pszFilterDll,
|
|
BOOL fAllowRawRead,
|
|
W3_IIS_SERVICE * pSvc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads the specified filter into this filter list. Note the filter dll's
|
|
ref count starts at one for being on the g_FilterHead list. Thus if the
|
|
refcount drops to one, no filter list is using the filter dll and it can
|
|
be deleted.
|
|
|
|
Arguments:
|
|
|
|
pmb - metabase open at the filter root, opened for Read/Write
|
|
pszKeyName - Metabase open path for filters key
|
|
pfOpened - TRUE if pmb currently opened, otherwise FALSE
|
|
must be set before calling this function, which will
|
|
update it.
|
|
pszRelativeMBPath - Metabase path to this filter dll
|
|
pszFilterDll - Fully qualified name of filter to load
|
|
fAllowRawRead - Set to TRUE if raw read filters are allowed
|
|
pSvc - Optional service pointer for logging
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
HTTP_FILTER_VERSION ver;
|
|
HTTP_FILTER_DLL * pFilterDll;
|
|
DWORD i;
|
|
|
|
//
|
|
// pSvc will only be passed during InitializeFilters
|
|
//
|
|
|
|
if ( !pSvc )
|
|
{
|
|
pSvc = (W3_IIS_SERVICE *) g_pInetSvc;
|
|
}
|
|
|
|
//
|
|
// Make sure there's a free entry in the filter list array, and
|
|
// the secure/non-secure notification arrays (the latter two are used
|
|
// in conjunction with filters disabling themselves per request
|
|
//
|
|
|
|
if ( (m_cFilters+1) > (m_buffFilterArray.QuerySize() / sizeof(PVOID)))
|
|
{
|
|
if ( !m_buffFilterArray.Resize( (m_cFilters + 5) * sizeof(PVOID)) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !m_buffSecureArray.Resize( (m_cFilters + 5) * sizeof(DWORD)) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !m_buffNonSecureArray.Resize( (m_cFilters + 5) * sizeof(DWORD)) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Load the filter DLL
|
|
//
|
|
|
|
EnterCriticalSection( &g_csFilterDlls );
|
|
|
|
if ( !(pFilterDll = CheckoutFilterDll( pszFilterDll )))
|
|
{
|
|
pFilterDll = new HTTP_FILTER_DLL;
|
|
|
|
if ( !pFilterDll ||
|
|
!pFilterDll->LoadDll( pmb,
|
|
pszKeyName,
|
|
pfOpened,
|
|
pszRelativeMBPath,
|
|
pszFilterDll ) )
|
|
{
|
|
const CHAR * apszSubString[1];
|
|
DWORD err = GetLastError();
|
|
|
|
LeaveCriticalSection( &g_csFilterDlls );
|
|
delete pFilterDll;
|
|
|
|
apszSubString[0] = pszFilterDll;
|
|
|
|
if ( err )
|
|
{
|
|
//
|
|
// Log a warning here if the filter supplied an error code
|
|
//
|
|
|
|
pSvc->LogEvent( W3_EVENT_FILTER_DLL_LOAD_FAILED,
|
|
1,
|
|
apszSubString,
|
|
err );
|
|
}
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Cannot load filter dll (err = %d)\n",
|
|
err));
|
|
|
|
if ( pmb &&
|
|
(*pfOpened ||
|
|
pmb->Open( pszKeyName, METADATA_PERMISSION_READ |
|
|
METADATA_PERMISSION_WRITE )) )
|
|
{
|
|
*pfOpened = TRUE;
|
|
|
|
pmb->SetDword( pszRelativeMBPath,
|
|
MD_WIN32_ERROR,
|
|
IIS_MD_UT_SERVER,
|
|
err,
|
|
0 );
|
|
pmb->SetDword( pszRelativeMBPath,
|
|
MD_FILTER_STATE,
|
|
IIS_MD_UT_SERVER,
|
|
MD_FILTER_STATE_UNLOADED,
|
|
0 );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// This filter list now officially owns a reference to this filter dll
|
|
//
|
|
|
|
pFilterDll->Reference();
|
|
}
|
|
|
|
//
|
|
// Not a fatal error if the status doesn't get updated
|
|
//
|
|
|
|
DBG_ASSERT( pFilterDll != NULL );
|
|
|
|
if ( pmb &&
|
|
(*pfOpened ||
|
|
pmb->Open( pszKeyName, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE )) )
|
|
{
|
|
*pfOpened = TRUE;
|
|
|
|
if ( !pmb->SetDword( pszRelativeMBPath,
|
|
MD_WIN32_ERROR,
|
|
IIS_MD_UT_SERVER,
|
|
NO_ERROR,
|
|
0 ) ||
|
|
!pmb->SetDword( pszRelativeMBPath,
|
|
MD_FILTER_STATE,
|
|
IIS_MD_UT_SERVER,
|
|
MD_FILTER_STATE_LOADED,
|
|
0 ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error %d setting filter status\n",
|
|
GetLastError() ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Disallow any per-instance read raw filters
|
|
//
|
|
|
|
if ( !fAllowRawRead &&
|
|
(pFilterDll->QueryNotificationFlags() & SF_NOTIFY_READ_RAW_DATA) )
|
|
{
|
|
const CHAR * apszSubString[1];
|
|
|
|
apszSubString[0] = pszFilterDll;
|
|
|
|
pSvc->LogEvent( W3_MSG_READ_RAW_MUST_BE_GLOBAL,
|
|
1,
|
|
apszSubString,
|
|
0 );
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Refusing READ_RAW filter on server instance (%s)\n",
|
|
pszFilterDll));
|
|
|
|
if ( pmb &&
|
|
(*pfOpened ||
|
|
pmb->Open( pszKeyName, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE )) )
|
|
{
|
|
*pfOpened = TRUE;
|
|
|
|
if ( !pmb->SetDword( pszRelativeMBPath,
|
|
MD_WIN32_ERROR,
|
|
IIS_MD_UT_SERVER,
|
|
ERROR_INVALID_PARAMETER,
|
|
0 ) ||
|
|
!pmb->SetDword( pszRelativeMBPath,
|
|
MD_FILTER_STATE,
|
|
IIS_MD_UT_SERVER,
|
|
MD_FILTER_STATE_UNLOADED,
|
|
0 ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error %d setting filter status\n",
|
|
GetLastError() ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Undo the ref for being on this filter list
|
|
//
|
|
|
|
HTTP_FILTER_DLL::Dereference( pFilterDll );
|
|
|
|
//
|
|
// If nobody else is using this filter dll, then go ahead and force
|
|
// an unload. The filter dll can't be checked out with the filter
|
|
// critical section held so this is a safe operation.
|
|
//
|
|
|
|
if ( pFilterDll->QueryRef() == 1 )
|
|
{
|
|
RemoveEntryList( &pFilterDll->ListEntry );
|
|
HTTP_FILTER_DLL::Dereference( pFilterDll );
|
|
}
|
|
|
|
LeaveCriticalSection( &g_csFilterDlls );
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Find where pFilterDll goes in the list
|
|
//
|
|
|
|
for ( i = 0; i < m_cFilters; i++ )
|
|
{
|
|
if ( (QueryDll(i)->QueryNotificationFlags() & SF_NOTIFY_ORDER_MASK)
|
|
< (pFilterDll->QueryNotificationFlags() & SF_NOTIFY_ORDER_MASK) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// And insert it into the array
|
|
//
|
|
|
|
memmove( (PVOID *) m_buffFilterArray.QueryPtr() + i + 1,
|
|
(PVOID *) m_buffFilterArray.QueryPtr() + i,
|
|
(m_cFilters - i) * sizeof(PVOID) );
|
|
|
|
(((HTTP_FILTER_DLL * *) (m_buffFilterArray.QueryPtr())))[i] = pFilterDll;
|
|
|
|
//
|
|
// Add notification DWORDS to secure/non-secure arrays
|
|
//
|
|
|
|
memmove( (DWORD *) m_buffSecureArray.QueryPtr() + i + 1,
|
|
(DWORD *) m_buffSecureArray.QueryPtr() + i,
|
|
(m_cFilters - i) * sizeof(DWORD) );
|
|
|
|
((DWORD*) m_buffSecureArray.QueryPtr())[i] = pFilterDll->QuerySecureFlags();
|
|
|
|
memmove( (DWORD *) m_buffNonSecureArray.QueryPtr() + i + 1,
|
|
(DWORD *) m_buffNonSecureArray.QueryPtr() + i,
|
|
(m_cFilters - i) * sizeof(DWORD) );
|
|
|
|
((DWORD*) m_buffNonSecureArray.QueryPtr())[i] = pFilterDll->QueryNonsecureFlags();
|
|
|
|
m_cFilters++;
|
|
|
|
//
|
|
// Segregate the secure and non-secure port notifications
|
|
//
|
|
|
|
m_SecureNotifications |= pFilterDll->QuerySecureFlags();
|
|
m_NonSecureNotifications |= pFilterDll->QueryNonsecureFlags();
|
|
|
|
LeaveCriticalSection( &g_csFilterDlls );
|
|
return TRUE;
|
|
}
|
|
|
|
#if 0
|
|
BOOL
|
|
FILTER_LIST::Copy(
|
|
IN FILTER_LIST * pClone
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clones the passed filter list
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
|
|
DBG_ASSERT( pClone->CheckSignature() );
|
|
DBG_ASSERT( CheckSignature() );
|
|
|
|
//
|
|
// Walk the list to be cloned and load all of the dlls
|
|
//
|
|
|
|
EnterCriticalSection( &g_csFilterDlls );
|
|
|
|
for ( i = 0; i < pClone->QueryFilterCount(); i++ )
|
|
{
|
|
if ( !LoadFilter( pClone->QueryDll( i )->QueryName(),
|
|
FALSE ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[FILTER_LIST::Copy] Failed loading %s, error %d\n",
|
|
pClone->QueryDll( i )->QueryName(),
|
|
GetLastError() ));
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection( &g_csFilterDlls );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ReplaceFilterDll(
|
|
IN const CHAR * pszNewDll,
|
|
IN const CHAR * pszOldDll
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a new filter and an old filter, this function renames the old filter
|
|
dll to a temporary name, copies the new .dll into place then walks the
|
|
instance list and replaces all occurrences of the old .dll with the
|
|
new .dll.
|
|
|
|
Arguments:
|
|
|
|
pszNewDll - New filter dll
|
|
pszOldDll - Existing filter dll that should be replaced
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
CHAR achTmp[MAX_PATH + 1];
|
|
CHAR * pszTmp;
|
|
DWORD cVer = 1;
|
|
CHAR achVer[32];
|
|
const CHAR * apsz[3];
|
|
|
|
apsz[0] = pszOldDll;
|
|
apsz[1] = pszNewDll;
|
|
|
|
g_pInetSvc->LogEvent( W3_MSG_REP_FILTER,
|
|
2,
|
|
apsz,
|
|
0 );
|
|
|
|
//
|
|
// Find a back up name - this just appends a number onto the dll
|
|
//
|
|
|
|
strcpy( achTmp, pszOldDll );
|
|
pszTmp = achTmp + strlen( pszOldDll );
|
|
|
|
while ( TRUE )
|
|
{
|
|
_itoa( cVer, achVer, 10 );
|
|
strcpy( pszTmp, achVer );
|
|
|
|
if ( GetFileAttributes( achTmp ) != 0xffffffff ||
|
|
GetLastError() != ERROR_FILE_NOT_FOUND )
|
|
{
|
|
cVer++;
|
|
|
|
//
|
|
// If we've tried a reasonable number of backups and we couldn't
|
|
// find a candidate then get out
|
|
//
|
|
|
|
if ( cVer > 8192 )
|
|
{
|
|
SetLastError( ERROR_FILE_NOT_FOUND );
|
|
return FALSE;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Rename the old DLL to the new dll name
|
|
//
|
|
|
|
if ( !MoveFileEx( pszOldDll,
|
|
achTmp,
|
|
MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[ReplaceFilterDll] Move old file failed, %s -> %s, error %d\n",
|
|
pszOldDll,
|
|
achTmp,
|
|
GetLastError() ));
|
|
|
|
if ( GetLastError() == ERROR_FILE_EXISTS )
|
|
{
|
|
cVer++;
|
|
continue;
|
|
}
|
|
|
|
apsz[0] = pszOldDll;
|
|
apsz[1] = pszNewDll;
|
|
|
|
g_pInetSvc->LogEvent( W3_MSG_REP_FILTER,
|
|
2,
|
|
apsz,
|
|
GetLastError() );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Rename the new DLL name to the old dll
|
|
//
|
|
|
|
if ( !MoveFileEx( pszNewDll,
|
|
pszOldDll,
|
|
MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[ReplaceFilterDll] Move new file failed, %s -> %s, error %d\n",
|
|
pszNewDll,
|
|
pszOldDll,
|
|
GetLastError() ));
|
|
|
|
apsz[0] = pszNewDll;
|
|
apsz[1] = pszOldDll;
|
|
|
|
g_pInetSvc->LogEvent( W3_MSG_REP_FILTER_FAILED,
|
|
2,
|
|
apsz,
|
|
0 );
|
|
|
|
if ( !MoveFileEx( achTmp,
|
|
pszOldDll,
|
|
MOVEFILE_WRITE_THROUGH | MOVEFILE_COPY_ALLOWED ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[ReplaceFilterDll] Move tmp to old file failed, %s -> %s, error %d\n",
|
|
achTmp,
|
|
pszOldDll,
|
|
GetLastError() ));
|
|
|
|
apsz[0] = achTmp;
|
|
apsz[1] = pszOldDll;
|
|
|
|
g_pInetSvc->LogEvent( W3_MSG_REP_FILTER_FAILED,
|
|
2,
|
|
apsz,
|
|
0 );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Update all of the instance filters
|
|
//
|
|
|
|
if ( !g_pInetSvc->EnumServiceInstances(
|
|
(PVOID) pszNewDll,
|
|
(PVOID) pszOldDll,
|
|
(PFN_INSTANCE_ENUM) UpdateInstanceFilters ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
UpdateInstanceFilters(
|
|
IN const CHAR * pszNewDll,
|
|
IN const CHAR * pszOldDll,
|
|
IN W3_SERVER_INSTANCE * pInst
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a simple utility function that handles the instance callbacks
|
|
|
|
Arguments:
|
|
|
|
pszNewDll - New filter dll
|
|
pszOldDll - Existing filter dll that should be replaced
|
|
pInst - The instance the change is being applied to
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
#if 0
|
|
if ( !pInst->UpdateFilterList( pszNewDll,
|
|
pszOldDll ))
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"[UpdateInstanceFilters] Failed with error %d\n",
|
|
GetLastError() ));
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
FILTER_LIST::InsertGlobalFilters(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Transfers all of the global filters to the per-instance filter list
|
|
|
|
Note: This method assumes the server filter list is not dynamic
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure (call GetLastError)
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
HTTP_FILTER_DLL * pFilterDll;
|
|
BOOL fOpened;
|
|
|
|
for ( i = 0; i < GLOBAL_FILTER_LIST()->QueryFilterCount(); i++ )
|
|
{
|
|
//
|
|
// Ignore the return code, an event gets logged in LoadFilter()
|
|
// We allow raw read filters here as we're just duplicating the
|
|
// global filter list
|
|
//
|
|
|
|
LoadFilter( NULL, NULL, &fOpened, "", GLOBAL_FILTER_LIST()->QueryDll( i )->QueryName(), TRUE );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
CheckForSecurityFilter(
|
|
FILTER_LIST * pFilterList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks to see if there are any global filters that are encryption filters
|
|
|
|
Return Value:
|
|
|
|
TRUE if an encryption filter was found, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
HTTP_FILTER_DLL * pFilterDll;
|
|
BOOL fRet = FALSE;
|
|
|
|
for ( i = 0; i < pFilterList->QueryFilterCount(); i++ )
|
|
{
|
|
pFilterDll = pFilterList->QueryDll( i );
|
|
|
|
//
|
|
// port notification, assume he's an encryption filter
|
|
//
|
|
|
|
#define SECURE_FILTER (SF_NOTIFY_SECURE_PORT | \
|
|
SF_NOTIFY_READ_RAW_DATA | \
|
|
SF_NOTIFY_SEND_RAW_DATA | \
|
|
SF_NOTIFY_ORDER_HIGH)
|
|
|
|
if ( (pFilterDll->QueryNotificationFlags() & SECURE_FILTER) ==
|
|
SECURE_FILTER)
|
|
{
|
|
fRet = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
BOOL
|
|
AdjustFilterFlags(
|
|
PVOID pfnSFProc,
|
|
DWORD dwNewFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Private exported routine that allows a filter to dynamically adjust its
|
|
notification flags. This can only be done for global filters.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
W3_IIS_SERVICE * pSvc = (W3_IIS_SERVICE *) g_pInetSvc;
|
|
FILTER_LIST * pFilterList;
|
|
HTTP_FILTER_DLL * pFilterDll;
|
|
DWORD i;
|
|
BOOL fFoundSvcDLL = FALSE;
|
|
BOOL fFoundInstanceDLL = FALSE;
|
|
|
|
//
|
|
// Can't adjust priority, just notification flags
|
|
//
|
|
|
|
if ( dwNewFlags & SF_NOTIFY_ORDER_MASK )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
pFilterList = pSvc->QueryGlobalFilterList();
|
|
|
|
if ( !pFilterList )
|
|
{
|
|
//
|
|
// Shouldn't happen but you never know
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Try to find the appropriate DLL in the single global per-service filter list
|
|
//
|
|
for ( i = 0; i < pFilterList->QueryFilterCount(); i++ )
|
|
{
|
|
DBG_ASSERT( pFilterList->QueryDll(i)->CheckSignature() );
|
|
|
|
if ( pFilterList->QueryDll(i)->QueryEntryPoint() == pfnSFProc )
|
|
{
|
|
pFilterDll = pFilterList->QueryDll(i);
|
|
|
|
//
|
|
// We've found the matching filter dll, adjust the flags in the
|
|
// filter dll object then adjust the flags in the filter list
|
|
//
|
|
|
|
pFilterDll->SetNotificationFlags( dwNewFlags );
|
|
pFilterList->SetNotificationFlags( i, pFilterDll );
|
|
|
|
fFoundSvcDLL = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( fFoundSvcDLL )
|
|
{
|
|
//
|
|
// Try to update the flags in each of the per-instance filter lists
|
|
//
|
|
if ( pSvc )
|
|
{
|
|
if ( pSvc->EnumServiceInstances( (PVOID) &dwNewFlags,
|
|
(PVOID) pfnSFProc,
|
|
AdjustInstanceFilterListFlags ) )
|
|
{
|
|
fFoundInstanceDLL = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !( fFoundInstanceDLL && fFoundSvcDLL ) )
|
|
{
|
|
SetLastError( ERROR_FILE_NOT_FOUND );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL AdjustInstanceFilterListFlags( IN PVOID pdwNewFlags,
|
|
IN PVOID pfnSFProc,
|
|
IN IIS_SERVER_INSTANCE *pInstance )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callback function called when we need to adjust the notification flags
|
|
of a particular filter dll for the filter list associated with a server instance.
|
|
Note that this filter list consists of the -global- [ie service-wide] filter dlls.
|
|
|
|
Arguments :
|
|
pdwNewFlags - pointer to DWORD containing new notification flags
|
|
pfnSFProc - pointer to filter entry point function
|
|
pInstance - pointer to W3_SERVER_INSTANCE whose filter list is to be updated
|
|
|
|
Return Value:
|
|
|
|
TRUE if the DLL was found and the flags adjusted, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
HTTP_FILTER_DLL *pFilterDll = NULL;
|
|
W3_SERVER_INSTANCE *pW3Instance = (W3_SERVER_INSTANCE *) pInstance;
|
|
BOOL fFound = FALSE;
|
|
|
|
if ( !pInstance )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"NULL instance passed to AdjustInstanceFilterFlags !\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
pInstance->LockThisForRead();
|
|
FILTER_LIST *pFilterList = ((W3_SERVER_INSTANCE *) pInstance)->QueryFilterList();
|
|
pInstance->UnlockThis();
|
|
|
|
if ( !pFilterList )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Very odd - instance 0x%p has no filter list associated with it !\n",
|
|
pInstance));
|
|
return TRUE;
|
|
}
|
|
|
|
pInstance->LockThisForWrite();
|
|
|
|
//
|
|
// Try to find the appropriate DLL in the filter list for this instance
|
|
//
|
|
for ( DWORD i = 0; i < pFilterList->QueryFilterCount(); i++ )
|
|
{
|
|
DBG_ASSERT( pFilterList->QueryDll(i)->CheckSignature() );
|
|
|
|
if ( pFilterList->QueryDll(i)->QueryEntryPoint() == pfnSFProc )
|
|
{
|
|
pFilterDll = pFilterList->QueryDll(i);
|
|
|
|
//
|
|
// We've found the matching filter dll, adjust the flags in the
|
|
// filter dll object then adjust the flags in the filter list
|
|
//
|
|
|
|
pFilterDll->SetNotificationFlags( *((DWORD *) pdwNewFlags) );
|
|
pFilterList->SetNotificationFlags( i, pFilterDll );
|
|
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pInstance->UnlockThis();
|
|
|
|
return fFound;
|
|
}
|