|
|
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
w3site.cxx
Abstract:
W3_SITE object holds state for each site
Author:
Anil Ruia (AnilR) 18-Jan-2000
Revision History:
--*/
#include "precomp.hxx"
//
// No advance notification
//
#define DEFAULT_W3_ADV_NOT_PWD_EXP_IN_DAYS 14
//
// OWA change flags
//
#define DEFAULT_W3_AUTH_CHANGE_FLAGS 6
//
// In seconds
//
#define DEFAULT_W3_ADV_CACHE_TTL ( 10 * 60 )
//static
CRITICAL_SECTION W3_SITE::sm_csIISCertMapLock;
W3_SITE::W3_SITE( DWORD SiteId ) : m_SiteId ( SiteId ), m_cRefs ( 1 ), m_pInstanceFilterList ( NULL ), m_pLogging ( NULL ), m_fAllowPathInfoForScriptMappings ( FALSE ), m_fUseDSMapper ( FALSE ), m_dwAuthChangeFlags ( 0 ), m_dwAdvNotPwdExpInDays ( DEFAULT_W3_ADV_NOT_PWD_EXP_IN_DAYS ), m_dwAdvCacheTTL ( DEFAULT_W3_ADV_CACHE_TTL ), m_fSSLSupported ( FALSE ), m_pIISCertMap ( NULL ), m_fAlreadyAttemptedToLoadIISCertMap ( FALSE ), m_pDataSetCache ( NULL ), m_Signature ( W3_SITE_SIGNATURE ) { ZeroMemory(&m_PerfCounters, sizeof m_PerfCounters); }
W3_SITE::~W3_SITE() { if (m_pInstanceFilterList != NULL) { m_pInstanceFilterList->Dereference(); m_pInstanceFilterList = NULL; } if (m_pLogging != NULL) { m_pLogging->Release(); m_pLogging = NULL; }
if ( m_pIISCertMap != NULL ) { m_pIISCertMap->DereferenceCertMapping(); m_pIISCertMap = NULL; } if ( m_pDataSetCache != NULL ) { m_pDataSetCache->DereferenceDataSetCache(); m_pDataSetCache = NULL; } m_Signature = W3_SITE_SIGNATURE_FREE; }
HRESULT W3_SITE::Initialize(LOGGING *pLogging, FILTER_LIST *pFilterList) /*++
Routine Description:
Initialize W3_SITE. Should be called after constructor
Arguments: None Return Value:
HRESULT
--*/ { HRESULT hr = NO_ERROR; WCHAR idToStr[MAX_SITEID_LENGTH + 6]; CHAR idToStrA[MAX_SITEID_LENGTH + 6];
//
// Setup Site Name like "W3SVC1"
//
sprintf(idToStrA, "W3SVC%u", m_SiteId); if (FAILED(hr = m_SiteName.Copy(idToStrA))) { goto Failure; }
//
// Setup site path (like "/LM/W3SVC/1")
//
hr = m_SiteMBPath.Copy(g_pW3Server->QueryMDPath()); if ( FAILED( hr ) ) { goto Failure; }
_itow(m_SiteId, idToStr, 10);
hr = m_SiteMBPath.Append( idToStr ); if ( FAILED( hr ) ) { goto Failure; }
//
// Setup site root (like "/LM/W3SVC/1/ROOT/")
//
hr = m_SiteMBRoot.Copy( m_SiteMBPath ); if ( FAILED( hr ) ) { goto Failure; }
hr = m_SiteMBRoot.Append( L"/Root/" ); if ( FAILED( hr ) ) { goto Failure; }
//
// Read the per-site properties from the metabase
//
if (FAILED(hr = ReadPrivateProperties())) { goto Failure; }
//
// Initialize instance filters
//
if (pFilterList) { pFilterList->Reference(); m_pInstanceFilterList = pFilterList; } else { m_pInstanceFilterList = new FILTER_LIST(); if (m_pInstanceFilterList == NULL) { hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); goto Failure; }
hr = m_pInstanceFilterList->InsertGlobalFilters(); if (FAILED(hr)) { goto Failure; }
hr = m_pInstanceFilterList->LoadFilters( m_SiteMBPath.QueryStr(), FALSE, TRUE ); if (FAILED(hr)) { goto Failure; } } //
// Initialize logging
//
if (pLogging) { pLogging->AddRef(); m_pLogging = pLogging; } else { m_pLogging = new LOGGING; if (m_pLogging == NULL) { hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); goto Failure; }
if (FAILED(hr = m_pLogging->ActivateLogging(m_SiteName.QueryStr(), m_SiteMBPath.QueryStr(), g_pW3Server->QueryMDObject(), g_pW3Server->QueryDoCentralBinaryLogging()))) { LPCWSTR apsz[1]; apsz[0] = idToStr; g_pW3Server->LogEvent(W3_EVENT_LOGGING_MODULE_FAILED_TO_LOAD, 1, apsz);
goto Failure; } } return S_OK; Failure: return hr; }
HRESULT W3_SITE::ReadPrivateProperties() /*++
Routine description: Read the site specific properties from the metabase
Arguments: none
Return Value: HRESULT --*/ { MB mb( g_pW3Server->QueryMDObject() ); MULTISZ mszSecureBindings;
//
// Read per-site properties from the metabase
//
if ( !mb.Open(m_SiteMBPath.QueryStr()) ) { return HRESULT_FROM_WIN32(GetLastError()); }
if ( !mb.GetDword(L"", MD_ALLOW_PATH_INFO_FOR_SCRIPT_MAPPINGS, IIS_MD_UT_FILE, (DWORD *)&m_fAllowPathInfoForScriptMappings, 0) ) { m_fAllowPathInfoForScriptMappings = FALSE; }
mb.GetStr( L"", MD_AUTH_CHANGE_URL, IIS_MD_UT_SERVER, &m_strAuthChangeUrl );
mb.GetStr( L"", MD_AUTH_EXPIRED_URL, IIS_MD_UT_SERVER, &m_strAuthExpiredUrl );
mb.GetStr( L"", MD_AUTH_NOTIFY_PWD_EXP_URL, IIS_MD_UT_SERVER, &m_strAdvNotPwdExpUrl );
mb.GetStr( L"", MD_AUTH_EXPIRED_UNSECUREURL, IIS_MD_UT_SERVER, &m_strAuthExpiredUnsecureUrl );
mb.GetStr( L"", MD_AUTH_NOTIFY_PWD_EXP_UNSECUREURL, IIS_MD_UT_SERVER, &m_strAdvNotPwdExpUnsecureUrl );
if ( !mb.GetDword( L"", MD_ADV_NOTIFY_PWD_EXP_IN_DAYS, IIS_MD_UT_SERVER, &m_dwAdvNotPwdExpInDays ) ) { m_dwAdvNotPwdExpInDays = DEFAULT_W3_ADV_NOT_PWD_EXP_IN_DAYS; }
if ( !mb.GetDword( L"", MD_AUTH_CHANGE_FLAGS, IIS_MD_UT_SERVER, &m_dwAuthChangeFlags ) ) { m_dwAuthChangeFlags = DEFAULT_W3_AUTH_CHANGE_FLAGS; }
if ( !mb.GetDword( L"", MD_ADV_CACHE_TTL, IIS_MD_UT_SERVER, &m_dwAdvCacheTTL ) ) { m_dwAdvCacheTTL = DEFAULT_W3_ADV_CACHE_TTL; }
//
// Read the secure bindings.
//
if ( mb.GetMultisz( L"", MD_SECURE_BINDINGS, IIS_MD_UT_SERVER, &mszSecureBindings ) ) { if( !mszSecureBindings.IsEmpty() ) { m_fSSLSupported = TRUE; } }
DBG_REQUIRE( mb.Close() );
//
// Read global properties from the metabase that affect site config
//
if ( !mb.Open(g_pW3Server->QueryMDPath()) ) { return HRESULT_FROM_WIN32(GetLastError()); }
if ( !mb.GetDword( L"", MD_SSL_USE_DS_MAPPER, IIS_MD_UT_SERVER, (DWORD*) &m_fUseDSMapper, 0 )) { m_fUseDSMapper = FALSE; }
DBG_REQUIRE( mb.Close() );
return S_OK; }
HRESULT W3_SITE::HandleMetabaseChange( const MD_CHANGE_OBJECT &ChangeObject, IN W3_SITE_LIST *pTempSiteList ) /*++
Routine Description:
Handle metabase changes. The change may be to either the /LM/W3SVC/ node or they may be changes to this site or one of its children.
This routine needs to perform cache flushes and reget site metadata if necessary.
Arguments: ChangeObject Return Value:
See W3_SERVER_INSTANCE::MDChangeNotify and IIS_SERVER_INSTANCE::MDChangeNotify for implemenation details
--*/ { DBGPRINTF(( DBG_CONTEXT, "W3_SITE Notified - Path(%S) Type(%d) NumIds(%08x)\n", ChangeObject.pszMDPath, ChangeObject.dwMDChangeType, ChangeObject.dwMDNumDataIDs ));
if (ChangeObject.dwMDChangeType == MD_CHANGE_TYPE_SET_DATA) { BOOL fDontCare = TRUE;
//
// Find out if we care about these properties
// We definitely don't care about ServerState or Win32Error
//
for (DWORD i = 0; i < ChangeObject.dwMDNumDataIDs; i++) { DWORD PropertyID = ChangeObject.pdwMDDataIDs[i];
if (PropertyID != MD_SERVER_STATE && PropertyID != MD_WIN32_ERROR && PropertyID != MD_SERVER_AUTOSTART) { fDontCare = FALSE; break; } }
if (fDontCare) { return S_OK; } }
//
// Let the cache manager invalidate the various cache entries dependent
// on metadata
//
W3CacheDoMetadataInvalidation( ChangeObject.pszMDPath, (DWORD)wcslen( ChangeObject.pszMDPath ) );
//
// Get rid of the data set cache if we have one
//
RemoveDataSetCache();
//
// Handle any site level change
// That means any changes at /LM/w3svc/ or /LM/w3svc/n/ or
// /LM/w3svc/n/Filters/
//
// IIS 1to1 Client certificate mappings are stored
// under /Cert11/Mappings/ on the site level. Any change in mappings
// will cause recreation of W3_SITE instance
// This is not optimal but changes in 1to1 Cert mappings are not frequent
// and feature itself is not used by many customers
// so there should be no problems with this approach.
//
if ((_wcsicmp(ChangeObject.pszMDPath, W3_SERVER_MB_PATH) == 0) || ((_wcsnicmp(ChangeObject.pszMDPath, m_SiteMBPath.QueryStr(), m_SiteMBPath.QueryCCH()) == 0) && ((wcscmp(ChangeObject.pszMDPath + m_SiteMBPath.QueryCCH(), L"/") == 0) || (_wcsicmp(ChangeObject.pszMDPath + m_SiteMBPath.QueryCCH(), L"/Filters/") == 0) || (_wcsnicmp(ChangeObject.pszMDPath + m_SiteMBPath.QueryCCH(), L"/Cert11/Mappings/", wcslen(L"/Cert11/Mappings/") ) == 0) ))) { //
// If the site (or its root application) has been deleted, remove it
// unless we are in the iterator in which case it will anyway be
// removed
//
if (ChangeObject.dwMDChangeType == MD_CHANGE_TYPE_DELETE_OBJECT) { if (pTempSiteList == NULL) { g_pW3Server->RemoveSite(this); }
return S_OK; }
//
// Now handle any property changes.
//
BOOL fLoggingHasChanged = FALSE; BOOL fFiltersHaveChanged = FALSE;
//
// Find out if we would need to handle any logging or filter changes
//
for (DWORD i = 0; i < ChangeObject.dwMDNumDataIDs; i++) { DWORD PropertyID = ChangeObject.pdwMDDataIDs[i];
if (((PropertyID >= IIS_MD_LOG_BASE) && (PropertyID <= IIS_MD_LOG_LAST)) || ((PropertyID >= IIS_MD_LOGCUSTOM_BASE) && (PropertyID <= IIS_MD_LOGCUSTOM_LAST))) { fLoggingHasChanged = TRUE; } else if (PropertyID == MD_FILTER_LOAD_ORDER) { fFiltersHaveChanged = TRUE; } }
//
// Create a new site
//
W3_SITE *site = new W3_SITE(m_SiteId); if (site == NULL) { return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); }
//
// Copy over the cache context, and also logging and filter info
// if applicable
//
HRESULT hr; if (FAILED(hr = site->Initialize( fLoggingHasChanged ? NULL : m_pLogging, fFiltersHaveChanged ? NULL : m_pInstanceFilterList))) { site->DereferenceSite(); return hr; }
//
// Depending on whether we are in the iterator, either replace the
// site in the site list or add it to the temp list which will replace
// the real list later
//
if (pTempSiteList == NULL) { g_pW3Server->AddSite(site, true); } else { pTempSiteList->InsertRecord(site); }
// Release the extra reference
site->DereferenceSite(); }
return S_OK; }
VOID W3_SITE::RemoveDataSetCache( VOID ) /*++
Routine Description:
Remove data set cache
Arguments:
None Return Value:
None
--*/ { m_DataSetCacheLock.WriteLock(); if ( m_pDataSetCache != NULL ) { m_pDataSetCache->DereferenceDataSetCache(); m_pDataSetCache = NULL; } m_DataSetCacheLock.WriteUnlock(); }
HRESULT W3_SITE::GetDataSetCache( DATA_SET_CACHE ** ppDataSetCache ) /*++
Routine Description:
Retrieve a data set cache for this site
Arguments:
ppDataSetCache - Set to point to data set cache on success Return Value:
HRESULT
--*/ { DATA_SET_CACHE * pDataSetCache; HRESULT hr; HANDLE hToken = NULL; if ( ppDataSetCache == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *ppDataSetCache = NULL; m_DataSetCacheLock.ReadLock(); if ( m_pDataSetCache != NULL ) { m_pDataSetCache->ReferenceDataSetCache(); *ppDataSetCache = m_pDataSetCache; m_DataSetCacheLock.ReadUnlock(); } else { m_DataSetCacheLock.ReadUnlock();
if ( OpenThreadToken( GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hToken ) ) { DBG_ASSERT( hToken != NULL ); DBG_REQUIRE( RevertToSelf() ); }
// Create a data set cache and then try to add it
//
pDataSetCache = new DATA_SET_CACHE; if ( pDataSetCache == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Finished; } hr = pDataSetCache->Create( *QueryMBRoot() ); if ( FAILED( hr ) ) { pDataSetCache->DereferenceDataSetCache(); goto Finished; } if ( hToken != NULL ) { DBG_REQUIRE( SetThreadToken( NULL, hToken ) ); DBG_REQUIRE( CloseHandle( hToken ) ); hToken = NULL; }
//
//
// Try to add it
//
m_DataSetCacheLock.WriteLock(); if ( m_pDataSetCache != NULL ) { //
// Someone else added it. No problem -> we'll just kill the one
// we just created
//
pDataSetCache->DereferenceDataSetCache(); } else { m_pDataSetCache = pDataSetCache; } m_pDataSetCache->ReferenceDataSetCache(); *ppDataSetCache = m_pDataSetCache; m_DataSetCacheLock.WriteUnlock(); } DBG_ASSERT( *ppDataSetCache != NULL );
return S_OK;
Finished: if ( hToken != NULL ) { DBG_REQUIRE( SetThreadToken( NULL, hToken ) ); DBG_REQUIRE( CloseHandle( hToken ) ); hToken = NULL; }
return hr; }
HRESULT W3_SITE::GetIISCertificateMapping( IIS_CERTIFICATE_MAPPING ** ppIISCertificateMapping ) /*++
Routine Description:
Arguments: ppIISCertificateMapping - returns found iis cert mapping object
m_pIISCertMap is created on demand. It will not be created when W3_SITE is created, only when first request that requires certificate mappings Return Value:
HRESULT
--*/
{ HRESULT hr = E_FAIL; DBG_ASSERT( ppIISCertificateMapping != NULL ); if ( m_fAlreadyAttemptedToLoadIISCertMap ) { *ppIISCertificateMapping = m_pIISCertMap; hr = S_OK; goto Finished; } else { IIS_CERTIFICATE_MAPPING * pCertMap = NULL;
//
// This lock only applies when certmapping was not yet read
// Global lock will only apply for reading certmapper for site
// on the very first attempt to fetch file that enables
// IIS certmapping
//
GlobalLockIISCertMap();
//
// try again to prevent loading of mapping multiple times
//
if ( m_fAlreadyAttemptedToLoadIISCertMap ) { hr = S_OK; } else { //
// build IIS Certificate mapping structure and
// add update W3_SITE structure
//
if ( m_fUseDSMapper ) { m_pIISCertMap = NULL; hr = S_OK; } else { hr = IIS_CERTIFICATE_MAPPING::GetCertificateMapping( m_SiteId, &pCertMap ); if ( SUCCEEDED( hr ) ) { m_pIISCertMap = pCertMap; } } //
// always set m_fAlreadyAttemptedToLoadIISCertMap to TRUE (regardless of error)
// that would prevent reading mappings for each request in the case of failure
// it is valid for m_pIISCertMap to be NULL (regardless of m_fAlreadyAttemptedToLoadIISCertMap)
//
InterlockedExchange( reinterpret_cast<LONG *>(&m_fAlreadyAttemptedToLoadIISCertMap), TRUE ); } GlobalUnlockIISCertMap(); if ( FAILED( hr ) ) { //
// Write event that certificate mappings couldn't be read
// This will be written only once per W3_SITE instance
// so it should not cause any event log overflow troubles
// (unless to many site parameters change notifications
// cause W3_SITE instance to be recreated over and
// over which is unlikely)
//
LPCWSTR apsz[1]; WCHAR achSiteId[ 33 ]; // _ultow uses up to 33 characters
apsz[0] =_ultow( m_SiteId, achSiteId, 10 );
g_pW3Server->LogEvent( W3_EVENT_CERTIFICATE_MAPPING_COULD_NOT_BE_LOADED, 1, apsz, (DWORD) hr ); // we are not converting to WIN32 error
// not to lose HRESULT error
goto Finished; } } *ppIISCertificateMapping = m_pIISCertMap; hr = S_OK; Finished: return hr; }
|