|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name : w3metadata.cxx
Abstract: Code to read metadata and generate W3_METADATA objects Author: Bilal Alam (balam) 23-Jan-2000
Environment: Win32 - User Mode
Project: ULW3.DLL --*/
#include "precomp.hxx"
#include "redirect.hxx"
ALLOC_CACHE_HANDLER * W3_METADATA::sm_pachW3MetaData;
W3_METADATA::W3_METADATA( OBJECT_CACHE * pObjectCache ) : CACHE_ENTRY ( pObjectCache ), _dwAccessPerm ( MD_ACCESS_READ ), _dwSslAccessPerm ( 0 ), _cbIpAccessCheck ( 0 ), _fUseAnonSubAuth ( FALSE ), _dwLogonMethod ( LOGON32_LOGON_NETWORK_CLEARTEXT ), _dwVrLevel ( 0 ), _dwVrLen ( 0 ), _pRedirectBlob ( NULL ), _dwDirBrowseFlags ( MD_DIRBROW_LOADDEFAULT ), _dwAuthentication ( 0 ), _dwAuthPersistence ( 0 ), _strVrUserName ( _rgVrUserName, sizeof( _rgVrUserName ) ), _strVrPasswd ( _rgVrPasswd, sizeof( _rgVrPasswd ) ), _strUserName ( _rgUserName, sizeof( _rgUserName ) ), _strPasswd ( _rgPasswd, sizeof( _rgPasswd ) ), _strDomainName ( _rgDomainName, sizeof( _rgDomainName ) ), _strRealm ( _rgRealm, sizeof( _rgRealm ) ), _mstrAuthProviders (), _pctVrToken ( NULL ), _pctAnonymousToken ( NULL ), _fCreateProcessAsUser ( TRUE ), _fCreateProcessNewConsole ( FALSE ), _fDoStaticCompression ( HTTP_COMPRESSION::QueryDoStaticCompression() ), _fDoDynamicCompression ( HTTP_COMPRESSION::QueryDoDynamicCompression() ), _dwCGIScriptTimeout ( DEFAULT_SCRIPT_TIMEOUT ), _ScriptMap (), _pMimeMap ( NULL ), _fSSIExecDisabled ( FALSE ), _fDontLog ( FALSE ), _fFooterEnabled ( FALSE ), _dwExpireMode ( EXPIRE_MODE_NONE ), _fHaveNoCache ( FALSE ), _fHaveMaxAge ( FALSE ), _fDoReverseDNS ( FALSE ), _cbEntityReadAhead ( DEFAULT_ENTITY_READ_AHEAD ), _dwMaxRequestEntityAllowed ( DEFAULT_MAX_REQUEST_ENTITY_ALLOWED ), _fNoCache ( FALSE ), _dwAppIsolated ( 0 ), _dwAppOopRecoverLimit ( 0 ), _fKeepAliveEnabled ( TRUE ), _cGetAllRecords ( NULL ), _pGetAllBuffer ( NULL ), _fAppPoolMatches ( FALSE ), _dwRequireMapping ( MD_PASSPORT_TRY_MAPPING ), _fUNCUserInvalid ( FALSE ), _cbMatchingUrlA ( 0 ), _cbMatchingPathA ( 0 ) { //
// Hmmm, since most of these values aren't getting initialized, if
// somebody went and deleted all the metadata items from the tree,
// then bad things could happen. We should initialize with defaults
// things that might screw us
//
_dirmonConfig.hToken = NULL; _dirmonConfig.pszDirPath = NULL; }
W3_METADATA::~W3_METADATA() { if ( _pctVrToken != NULL ) { _pctVrToken->DereferenceCacheEntry(); _pctVrToken = NULL; } if ( _pctAnonymousToken != NULL ) { _pctAnonymousToken->DereferenceCacheEntry(); _pctAnonymousToken = NULL; }
if ( _pMimeMap != NULL ) { delete _pMimeMap; _pMimeMap = NULL; }
if ( _pRedirectBlob != NULL ) { delete _pRedirectBlob; _pRedirectBlob = NULL; } if ( _pGetAllBuffer != NULL ) { delete _pGetAllBuffer; _pGetAllBuffer = NULL; }
DBG_ASSERT(CheckSignature()); }
//static
HRESULT W3_METADATA::Initialize( VOID ) /*++
Routine Description:
Initialize metadata lookaside
Arguments:
None
Return Value:
HRESULT
--*/ { ALLOC_CACHE_CONFIGURATION acConfig; HRESULT hr;
//
// Initialize allocation lookaside
//
acConfig.nConcurrency = 1; acConfig.nThreshold = 100; acConfig.cbSize = sizeof( W3_METADATA );
DBG_ASSERT( sm_pachW3MetaData == NULL ); sm_pachW3MetaData = new ALLOC_CACHE_HANDLER( "W3_METADATA", &acConfig );
if ( sm_pachW3MetaData == NULL ) { hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
DBGPRINTF(( DBG_CONTEXT, "Error initializing sm_pachW3MetaData. hr = 0x%x\n", hr ));
return hr; } DATA_SET_CACHE::Initialize(); return NO_ERROR; }
//static
VOID W3_METADATA::Terminate( VOID ) { DATA_SET_CACHE::Terminate(); if ( sm_pachW3MetaData != NULL ) { delete sm_pachW3MetaData; sm_pachW3MetaData = NULL; } }
//
// Private constants.
//
#define DEFAULT_MD_RECORDS 40
#define DEFAULT_RECORD_SIZE 50
# define DEF_MD_REC_SIZE ((1 + DEFAULT_MD_RECORDS) * \
(sizeof(METADATA_GETALL_RECORD) + DEFAULT_RECORD_SIZE))
HRESULT W3_METADATA::ReadMetaData( const STRU & strMetabasePath, const STRU & strURL ) /*++
Routine Description:
Reads the metabase (directly) to get the metadata for the given URL
Arguments:
strMetabasePath - The preceding service/instance goo (like "LM/W3SVC/1/ROOT" ) strURL - URL in question Return Value:
HRESULT
--*/ { PMETADATA_GETALL_RECORD pMDRecord; DWORD dwNumMDRecords; DWORD dwNumWamRecords; BYTE tmpBuffer[ DEF_MD_REC_SIZE]; BYTE tmpWamBuffer[ DEF_MD_REC_SIZE]; BUFFER TempBuff( tmpBuffer, DEF_MD_REC_SIZE); BUFFER WamBuff( tmpWamBuffer, DEF_MD_REC_SIZE); DWORD i; DWORD dwDataSetNumber; DWORD dwWamDataSetNumber; WCHAR ch; LPWSTR pszInVr; LPWSTR pszMinInVr; LPWSTR pszURL; DWORD dwNeed; DWORD dwL; DWORD dwVRLen; BYTE tmpPrivateBuffer[ 20 ]; BUFFER PrivateBuffer( tmpPrivateBuffer, 20 ); DWORD dwPrivateBufferUsed; MB mb( g_pW3Server->QueryMDObject() ); MB * pmb = &mb; HRESULT hr = NO_ERROR; WCHAR * pszStart = NULL; DWORD cchLength = 0; STACK_STRU( strAppPoolId, 256 ); STACK_STRU( strMatchingUrl, 64 ); WCHAR * pszProcessAppPoolId; BOOL fRet;
//
// We lie about modifying the input path
//
pszURL = (LPWSTR)strURL.QueryStr(); DBG_ASSERT( pszURL != NULL );
DBG_ASSERT( TempBuff.QuerySize() >= (DEFAULT_MD_RECORDS * (sizeof(METADATA_GETALL_RECORD) + DEFAULT_RECORD_SIZE)) );
DBG_ASSERT( WamBuff.QuerySize() >= (DEFAULT_MD_RECORDS * (sizeof(METADATA_GETALL_RECORD) + DEFAULT_RECORD_SIZE)) );
//
// Read the metabase
//
if ( !pmb->Open( strMetabasePath.QueryStr() )) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; }
//
// Get the UT_FILE info
//
if ( !pmb->GetAll( pszURL, METADATA_INHERIT | METADATA_PARTIAL_PATH, IIS_MD_UT_FILE, &TempBuff, &dwNumMDRecords, &dwDataSetNumber )) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; }
//
// Get the UT_WAM info
//
if ( !pmb->GetAll( pszURL, METADATA_INHERIT | METADATA_PARTIAL_PATH, IIS_MD_UT_WAM, &WamBuff, &dwNumWamRecords, &dwWamDataSetNumber )) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; } //
// Aarrggh. MD_APP_APPPOOL_ID is UT_SERVER. We want to get this property
// so that we can validate that we don't execute AppPool A's stuff in a
// process running as AppPool B (A != B)
//
// Note that it is OK if this call fails. WAS by default uses
// DefaultAppPool as apppool
//
pmb->GetStr( pszURL, MD_APP_APPPOOL_ID, IIS_MD_UT_SERVER, &strAppPoolId, METADATA_INHERIT | METADATA_PARTIAL_PATH );
pszProcessAppPoolId = (WCHAR*) UlAtqGetContextProperty( NULL, ULATQ_PROPERTY_APP_POOL_ID ); DBG_ASSERT( pszProcessAppPoolId != NULL ); //
// For efficiency sake, we'll store whether the AppPools match. That way, we don't have
// to repeat the determination per request.
//
if ( strAppPoolId.IsEmpty() ) { _fAppPoolMatches = _wcsicmp( pszProcessAppPoolId, L"DefaultAppPool" ) == 0; } else { _fAppPoolMatches = _wcsicmp( pszProcessAppPoolId, strAppPoolId.QueryStr() ) == 0; }
//
// Both sets of data better have the same data set number
//
DBG_ASSERT( dwDataSetNumber == dwWamDataSetNumber );
//
// Set the data set number, so that this object is metadata cachable
//
_cacheKey.SetDataSetNumber( dwDataSetNumber ); //
// Grok the buffer containing all the records
//
pMDRecord = (PMETADATA_GETALL_RECORD)TempBuff.QueryPtr();
i = 0;
//
// Check from where we got VR_PATH
//
pszMinInVr = pszURL ; if ( *pszURL ) { for ( pszInVr = pszMinInVr + wcslen(pszMinInVr) ;; ) { ch = *pszInVr; *pszInVr = L'\0'; dwNeed = 0; if ( !pmb->GetString( pszURL, MD_VR_PATH, IIS_MD_UT_FILE, NULL, &dwNeed, 0 ) && GetLastError() == ERROR_INSUFFICIENT_BUFFER ) { *pszInVr = ch; // VR_PATH was defined at this level !
break; } *pszInVr = ch;
if ( ch ) { if ( pszInVr > pszMinInVr ) { pszInVr--; } else { //
// VR_PATH was defined above Instance vroot
// or not at all. If defined above, then the reference
// path is empty, so we can claim we found it.
// if not defined, then this will be catch later.
//
break; } }
// scan for previous delimiter
while ( *pszInVr != L'/' && *pszInVr != L'\\' ) { if ( pszInVr > pszMinInVr ) { pszInVr--; } else { //
// VR_PATH was defined above Instance vroot
// or not at all. If defined above, then the reference
// path is empty, so we can claim we found it.
// if not defined, then this will be catch later.
//
break; } } }
dwVRLen = DIFF(pszInVr - pszMinInVr); } else { dwVRLen = 0; pszInVr = pszMinInVr; }
// Close this now to minimize lock contention.
DBG_REQUIRE(pmb->Close());
for ( dwL = 0 ; pszMinInVr < pszInVr - 1 ; pszMinInVr++ ) { if ( *pszMinInVr == L'/' || *pszMinInVr == L'\\' ) { ++dwL; } } //
// Store away an ANSI copy of the matching URL
//
hr = strMatchingUrl.Copy( pszURL );
if ( FAILED( hr ) ) { goto Failure; }
DBG_ASSERT( strMatchingUrl.QueryCCH() >= dwVRLen );
strMatchingUrl.SetLen( dwVRLen );
hr = _strMatchingUrlA.CopyW( strMatchingUrl.QueryStr() );
if ( FAILED( hr ) ) { goto Failure; }
_cbMatchingUrlA = strlen( _strMatchingUrlA.QueryStr() );
//
// Now walk through the array of returned metadata objects and format
// each one into our predigested form.
//
_dwVrLevel = dwL; _dwVrLen = dwVRLen; dwPrivateBufferUsed = 0;
for ( ; i < dwNumMDRecords; i++, pMDRecord++ ) {
PVOID pDataPointer;
pDataPointer = (PVOID) ((PCHAR)TempBuff.QueryPtr() + pMDRecord->dwMDDataOffset);
DBG_ASSERT(pMDRecord->dwMDDataTag == 0);
switch ( pMDRecord->dwMDIdentifier ) {
case MD_ALLOW_KEEPALIVES: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
_fKeepAliveEnabled = *(DWORD *)pDataPointer; break;
case MD_FOOTER_DOCUMENT: if (pMDRecord->dwMDDataType != STRING_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
if (*(WCHAR *)pDataPointer != L'\0') { if (FAILED(hr = ReadCustomFooter((WCHAR *)pDataPointer))) { goto Failure; } }
break;
case MD_FOOTER_ENABLED: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
_fFooterEnabled = !!*((DWORD *) pDataPointer );
if ( _fFooterEnabled ) { //
// If we have footers for a static file, we cannot do static
// compression on it
//
_fDoStaticCompression = FALSE; } break;
case MD_HTTP_EXPIRES: if (pMDRecord->dwMDDataType != STRING_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
if (FAILED(hr = SetExpire((WCHAR *)pDataPointer))) { goto Failure; } break;
case MD_CC_NO_CACHE: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
if (*(BOOL *)pDataPointer) { _fHaveNoCache = TRUE; } break;
case MD_CC_MAX_AGE: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
_dwMaxAge = *(DWORD *)pDataPointer; _fHaveMaxAge = TRUE; break;
case MD_CC_OTHER: if (pMDRecord->dwMDDataType != STRING_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
if (FAILED(hr = _strCacheControlHeader.CopyWTruncate((WCHAR *)pDataPointer))) { goto Failure; } break;
case MD_HTTP_REDIRECT: { if (pMDRecord->dwMDDataType != STRING_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
STACK_STRU( strRealSource, MAX_PATH ); STACK_STRU( strDestination, MAX_PATH );
if (FAILED(hr = strDestination.Copy((WCHAR *)pDataPointer)) || FAILED(hr = GetTrueRedirectionSource( pszURL, strMetabasePath.QueryStr(), &strRealSource)) || FAILED(hr = SetRedirectionBlob(strRealSource, strDestination))) { goto Failure; }
break; }
case MD_DONT_LOG: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
_fDontLog = *(BOOL *)pDataPointer; break;
case MD_CREATE_PROCESS_AS_USER: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
_fCreateProcessAsUser = *(BOOL *)pDataPointer; break;
case MD_CREATE_PROC_NEW_CONSOLE: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
_fCreateProcessNewConsole = *(BOOL *)pDataPointer; break;
case MD_SCRIPT_TIMEOUT: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
_dwCGIScriptTimeout = *(DWORD *)pDataPointer; break;
case MD_MIME_MAP: if (pMDRecord->dwMDDataType != MULTISZ_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
if (*(WCHAR *)pDataPointer) { _pMimeMap = new MIME_MAP((WCHAR *)pDataPointer); if (_pMimeMap == NULL) { hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); goto Failure; } } break;
case MD_HC_DO_NAMESPACE_DYNAMIC_COMPRESSION: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
_fDoDynamicCompression = *(BOOL *)pDataPointer; break;
case MD_HC_DO_NAMESPACE_STATIC_COMPRESSION: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
_fDoStaticCompression = *(BOOL *)pDataPointer; break;
case MD_ANONYMOUS_USER_NAME: if ( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } if ( FAILED( hr = _strUserName.Copy( (WCHAR *)pDataPointer) ) ) { goto Failure; }
break;
case MD_ANONYMOUS_PWD: if ( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } if ( FAILED( hr = _strPasswd.Copy( (WCHAR *)pDataPointer) ) ) { goto Failure; } break;
case MD_ANONYMOUS_USE_SUBAUTH: if (pMDRecord->dwMDDataType != DWORD_METADATA) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); goto Failure; }
_fUseAnonSubAuth = *(BOOL *)pDataPointer; break; case MD_DEFAULT_LOGON_DOMAIN: if ( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } if ( FAILED( hr = _strDomainName.Copy( (WCHAR *)pDataPointer) ) ) { goto Failure; } break;
case MD_HTTP_PICS: case MD_HTTP_CUSTOM:
if ( pMDRecord->dwMDDataType != MULTISZ_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; }
//
// Copy all the specified headers into our header buffer
//
pszStart = (WCHAR*) pDataPointer; while ( *pszStart != L'\0' ) { cchLength = wcslen( pszStart ); hr = _strCustomHeaders.AppendW( pszStart ); if ( FAILED( hr ) ) { goto Failure; } hr = _strCustomHeaders.Append( "\r\n", 2 ); if ( FAILED( hr ) ) { goto Failure; } pszStart += ( cchLength + 1 ); } break;
case MD_LOGON_METHOD: if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } //
// The MD_LOGON_METHOD values in the metabase don't match
// the NT logon values, so we'll convert them
//
switch ( *((DWORD *) pDataPointer ) ) { case MD_LOGON_BATCH: _dwLogonMethod = LOGON32_LOGON_BATCH; break;
case MD_LOGON_INTERACTIVE: _dwLogonMethod = LOGON32_LOGON_INTERACTIVE; break; case MD_LOGON_NETWORK: _dwLogonMethod = LOGON32_LOGON_NETWORK; break; case MD_LOGON_NETWORK_CLEARTEXT: _dwLogonMethod = LOGON32_LOGON_NETWORK_CLEARTEXT; break;
default: break; }
break;
case MD_AUTHORIZATION: if( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } _dwAuthentication = *((DWORD *) pDataPointer ); break;
case MD_REALM: if( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; }
if( FAILED( hr = _strRealm.Copy( ( WCHAR* )pDataPointer ) ) ) { goto Failure; }
break;
case MD_NTAUTHENTICATION_PROVIDERS: if( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; }
hr = BuildProviderList( ( WCHAR* )pDataPointer ); if ( FAILED( hr ) ) { goto Failure; }
break;
case MD_AUTHORIZATION_PERSISTENCE: if( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } _dwAuthPersistence = *((DWORD *) pDataPointer ); break;
case MD_IP_SEC: if( pMDRecord->dwMDDataType != BINARY_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; }
if ( pMDRecord->dwMDDataLen ) { hr = SetIpAccessCheck( pDataPointer, pMDRecord->dwMDDataLen ); if ( FAILED( hr ) ) { goto Failure; } }
break;
case MD_ACCESS_PERM: if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } _dwAccessPerm = *((DWORD*) pDataPointer); break;
case MD_SSL_ACCESS_PERM: if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } _dwSslAccessPerm = *((DWORD*)pDataPointer) & MD_SSL_ACCESS_MASK; break;
case MD_VR_PATH: if ( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } if ( FAILED( hr = _strVrPath.Copy((WCHAR *)pDataPointer) ) ) { goto Failure; }
if ( FAILED( hr = _strMatchingPathA.CopyW((WCHAR *)pDataPointer) ) ) { goto Failure; }
_cbMatchingPathA = strlen( _strMatchingPathA.QueryStr() ); break;
case MD_APP_ROOT: if ( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } if ( FAILED( hr = _strAppMetaPath.Copy((WCHAR *)pDataPointer) ) ) { goto Failure; }
break;
case MD_VR_USERNAME: if ( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } if ( FAILED( hr = _strVrUserName.Copy( (WCHAR *)pDataPointer) ) ) { goto Failure; }
break;
case MD_VR_PASSWORD: if ( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } if ( FAILED( hr = _strVrPasswd.Copy( (WCHAR *)pDataPointer) ) ) { goto Failure; }
break;
case MD_REDIRECT_HEADERS: if ( pMDRecord->dwMDDataType != MULTISZ_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } //
// Copy all the specified headers into our header buffer
//
pszStart = (WCHAR*) pDataPointer; while ( *pszStart != L'\0' ) { cchLength = wcslen( pszStart ); hr = _strRedirectHeaders.AppendW( pszStart ); if ( FAILED( hr ) ) { goto Failure; } hr = _strRedirectHeaders.Append( "\r\n", 2 ); if ( FAILED( hr ) ) { goto Failure; } pszStart += ( cchLength + 1 ); } break; case MD_DIRECTORY_BROWSING: if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } _dwDirBrowseFlags = *((DWORD *) pDataPointer ); break; case MD_DEFAULT_LOAD_FILE: if ( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } hr = _strDefaultLoad.Copy( (WCHAR*) pDataPointer ); if ( FAILED( hr ) ) { goto Failure; } break; case MD_SCRIPT_MAPS: if ( pMDRecord->dwMDDataType != MULTISZ_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; }
hr = _ScriptMap.Initialize( (WCHAR *)pDataPointer );
if( FAILED(hr) ) { goto Failure; } break; case MD_CUSTOM_ERROR: if ( pMDRecord->dwMDDataType != MULTISZ_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } //
// An empty string means use hard-coded errors
//
if ( *((WCHAR*)pDataPointer) == L'\0' ) { break; } hr = _customErrorTable.BuildTable( (WCHAR*) pDataPointer ); if ( FAILED( hr ) ) { goto Failure; } break;
case MD_SSI_EXEC_DISABLED: if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } _fSSIExecDisabled = !!*( ( DWORD * ) pDataPointer ); break;
case MD_DO_REVERSE_DNS: if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } _fDoReverseDNS = !!*((DWORD*)pDataPointer); break; case MD_UPLOAD_READAHEAD_SIZE: if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } _cbEntityReadAhead = *(DWORD*)pDataPointer; break;
case MD_MAX_REQUEST_ENTITY_ALLOWED: if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; }
_dwMaxRequestEntityAllowed = *(DWORD*)pDataPointer; break;
case MD_VR_NO_CACHE: if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } _fNoCache = !!*((DWORD*)pDataPointer); break; case MD_PASSPORT_REQUIRE_AD_MAPPING: if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; } _dwRequireMapping = *((DWORD*)pDataPointer); break; default: // BUGBUGBUG
DBG_ASSERT(CheckSignature()); break; } }
//
// Walk through the WAM data
//
pMDRecord = (PMETADATA_GETALL_RECORD)WamBuff.QueryPtr();
i = 0;
for ( ; i < dwNumWamRecords; i++, pMDRecord++ ) {
PVOID pDataPointer;
pDataPointer = (PVOID) ((PCHAR)WamBuff.QueryPtr() + pMDRecord->dwMDDataOffset);
DBG_ASSERT(pMDRecord->dwMDDataTag == 0);
switch ( pMDRecord->dwMDIdentifier ) { case MD_APP_ISOLATED:
if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; }
_dwAppIsolated = *(DWORD*)pDataPointer;
break;
case MD_APP_WAM_CLSID:
if ( pMDRecord->dwMDDataType != STRING_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; }
hr = _strWamClsId.Copy( (WCHAR*)pDataPointer );
break;
case MD_APP_OOP_RECOVER_LIMIT:
if ( pMDRecord->dwMDDataType != DWORD_METADATA ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Failure; }
_dwAppOopRecoverLimit = *(DWORD*)pDataPointer;
break;
default: break; } }
//
// If no-cache, max-age or a dynamic expires directive is present, add
// it to Cache-Control header
//
if (FAILED(hr = SetCacheControlHeader())) { goto Failure; }
if ( _strVrPath.IsEmpty() ) { DBGPRINTF(( DBG_CONTEXT, "[ReadMetaData] Virtual Dir Path mapping not found\n" )); hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); goto Failure; }
//
// If this is an UNC share, logon using associated credentials
// keep a reference to this access token in the cache
//
if ( _strVrPath.QueryStr()[0] == L'\\' && _strVrPath.QueryStr()[1] == L'\\' ) { if ( _strVrUserName.QueryStr() != NULL && _strVrPasswd.QueryStr() != NULL && _strVrUserName.QueryStr()[0] ) { hr = CreateUNCVrToken( _strVrUserName.QueryStr(), _strVrPasswd.QueryStr() ); if( SUCCEEDED( hr ) ) { hr = EncryptMemoryPassword( &_strVrPasswd ); if( FAILED( hr ) ) { goto Failure; } } else { //
// Continue metadata creation, but remember the failure
//
hr = NO_ERROR; _fUNCUserInvalid = TRUE; } } } //
// Setup the dirmon configuration
//
if ( _pctVrToken != NULL ) { _dirmonConfig.hToken = _pctVrToken->QueryImpersonationToken(); } else { _dirmonConfig.hToken = NULL; } _dirmonConfig.pszDirPath = _strVrPath.QueryStr(); //
// Build a default provider list for SSPI auth if the
// NTAuthenticationProviders is not set in the metabase.
//
if( _mstrAuthProviders.IsEmpty() ) { hr = BuildDefaultProviderList(); if( FAILED( hr ) ) { goto Failure; } } //
// Get anonymous user token
//
hr = CreateAnonymousToken( _strUserName.QueryStr(), _strPasswd.QueryStr() ); if ( FAILED( hr ) ) { goto Failure; }
hr = EncryptMemoryPassword( &_strPasswd ); if( FAILED( hr ) ) { goto Failure; } DBG_ASSERT( CheckSignature() );
return S_OK;
Failure: return hr; }
HRESULT W3_METADATA::BuildPhysicalPath( STRU & strUrl, STRU * pstrPhysicalPath ) /*++
Routine Description:
From the current metadata, convert a URL to a physical path (using the MD_VR_ROOT property and inheritance level calculated on read)
Arguments:
strUrl - Virtual path pstrPhysicalPath - String filled with physical path of strURL Return Value:
BOOL
--*/ { LPWSTR pszInVr; DWORD dwL; WCHAR ch; HRESULT hr = S_OK;
DBG_ASSERT(CheckSignature());
//
// Build physical path from VR_PATH & portion of URI not used to define VR_PATH
//
//
// skip the URI components used to locate the virtual root
//
pszInVr = strUrl.QueryStr(); dwL = _dwVrLevel; while ( dwL-- ) { if ( *pszInVr ) { DBG_ASSERT( *pszInVr == L'/' || *pszInVr == L'\\' );
++pszInVr;
while ( (ch = *pszInVr) && ch != L'/' && ch !=L'\\' ) { pszInVr++; } } }
DBG_ASSERT( dwL == (DWORD)-1 );
if ( FAILED(hr = pstrPhysicalPath->Copy( _strVrPath ) ) ) { return hr; }
//
// Add a path delimiter char between virtual root mount point & significant part of URI
//
if ( pstrPhysicalPath->QueryCCH() ) { ch = pstrPhysicalPath->QueryStr()[ pstrPhysicalPath->QueryCCH() - 1 ]; if ( (ch == L'/' || ch == L'\\') && *pszInVr ) { ++pszInVr; } }
if ( FAILED(hr = pstrPhysicalPath->Append( pszInVr ) ) ) { return hr; }
//
// insure physical path last char uses standard directory delimiter
//
FlipSlashes( pstrPhysicalPath->QueryStr() );
return NO_ERROR; }
HRESULT W3_METADATA::BuildProviderList( IN WCHAR * pszProviders ) /*++
Description:
Builds a name array of SSPI Authentication providers
Arguments:
pszProviders - Comma separated list of providers
Returns:
HRESULT
--*/ { WCHAR * pszCursor; WCHAR * pszEnd; BOOL fFinished = FALSE; DBG_ASSERT(CheckSignature());
if ( pszProviders == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } //
// Parse comma delimited list of providers, removing white space
//
pszCursor = SkipWhite( pszProviders ); for( ; ; ) { pszEnd = wcschr( pszCursor, L',' ); if ( pszEnd == NULL ) { fFinished = TRUE; pszEnd = pszCursor + wcslen( pszCursor ); } while ( pszEnd > pszCursor ) { if ( !ISWHITEW( *pszEnd ) ) { break; } pszEnd--; } *pszEnd = L'\0'; if ( !_mstrAuthProviders.AppendW( pszCursor ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } if ( fFinished ) { break; } //
// Advance to next provider
//
pszCursor = SkipWhite( pszEnd + 1 ); }
return NO_ERROR; } HRESULT W3_METADATA::BuildDefaultProviderList( VOID ) /*++
Description:
Builds a default name array of SSPI Authentication providers if NTAuthenticationProviders is not set in metabase
Arguments:
NONE
Returns:
HRESULT
--*/ { DBG_ASSERT( CheckSignature() ); DBG_ASSERT( _mstrAuthProviders.IsEmpty() );
if ( !_mstrAuthProviders.Append( "Negotiate" ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } if ( !_mstrAuthProviders.Append( "NTLM" ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } return NO_ERROR; } BOOL W3_METADATA::CheckAuthProvider( IN const CHAR * pszPkgName ) /*++
Description:
Check if we support the SSP package of name pszPkgName
Arguments:
pszPkgName - Name of the package we check against
Returns:
TRUE if we support the package, FALSE if we don't.
--*/ { const CHAR * pszProvider; DBG_ASSERT( CheckSignature() ); if ( pszPkgName == NULL ) { DBG_ASSERT( FALSE ); return FALSE; }
pszProvider = _mstrAuthProviders.First(); while ( pszProvider != NULL ) { if ( _stricmp( pszPkgName, pszProvider ) == 0 ) { return TRUE; } pszProvider = _mstrAuthProviders.Next( pszProvider ); } return FALSE; } HRESULT W3_METADATA::CreateUNCVrToken( IN LPWSTR pszUserName, IN LPWSTR pszPasswd ) /*++
Description:
Logon the user account for the UNC virtual path
Arguments:
pszUserName - User name of the account in format domain\username pszPasswd - Passwd of the account
Returns:
HRESULT
--*/ { STACK_STRU( strUserName, UNLEN + 1 ); STACK_STRU( strDomainName, IIS_DNLEN + 1 ); // add 1 to strUserDomain for separator "\"
STACK_STRU( strUserDomain, UNLEN + IIS_DNLEN + 1 + 1); HRESULT hr; DWORD dwError; BOOL fPossibleUPNLogon = FALSE; hr = strUserDomain.Copy( pszUserName ); if ( FAILED( hr ) ) { return hr; } hr = W3_STATE_AUTHENTICATION::SplitUserDomain( strUserDomain, &strUserName, &strDomainName, NULL, &fPossibleUPNLogon ); if ( FAILED( hr ) ) { return hr; } DBG_ASSERT( g_pW3Server->QueryTokenCache() != NULL ); hr = g_pW3Server->QueryTokenCache()->GetCachedToken( strUserName.QueryStr(), strDomainName.QueryStr(), pszPasswd, QueryLogonMethod(), FALSE, fPossibleUPNLogon, NULL, &_pctVrToken, &dwError ); if ( FAILED( hr ) ) { return hr; } //
// If we didn't get a token, this is a fatal error. It means bad config
// so we will prop back an error which means Error 500
//
if ( _pctVrToken == NULL ) { DBG_ASSERT( dwError != ERROR_SUCCESS ); //
// Make the error ERROR_LOGON_FAILURE so that we can generate the appropriate
// custom error response
//
hr = HRESULT_FROM_WIN32( ERROR_LOGON_FAILURE ); return hr; } return NO_ERROR; }
HRESULT W3_METADATA::CreateAnonymousToken( IN LPWSTR pszUserName, IN LPWSTR pszPasswd ) /*++
Description:
Logon the user account for the UNC virtual path
Arguments:
pszUserName - User name of the account in format domain\username pszPasswd - Passwd of the account
Returns:
HRESULT
--*/ { STACK_STRU( strUserName, UNLEN ); STACK_STRU( strDomainName, IIS_DNLEN ); // add 1 to strUserDomain for separator "\"
STACK_STRU( strUserDomain, UNLEN + IIS_DNLEN + 1 ); HRESULT hr; DWORD dwLogonError; BOOL fPossibleUPNLogon = FALSE; BOOL fUseAnonSubAuth = FALSE; hr = strUserDomain.Copy( pszUserName ); if ( FAILED( hr ) ) { return hr; } hr = W3_STATE_AUTHENTICATION::SplitUserDomain( strUserDomain, &strUserName, &strDomainName, NULL, &fPossibleUPNLogon ); if ( FAILED( hr ) ) { return hr; }
DBG_ASSERT( g_pW3Server->QueryTokenCache() != NULL ); if( _fUseAnonSubAuth ) { if( !W3_STATE_AUTHENTICATION::sm_fSubAuthConfigured ) { if( W3_STATE_AUTHENTICATION::sm_lSubAuthAnonEvent == 0 ) { if( !InterlockedExchange( &W3_STATE_AUTHENTICATION::sm_lSubAuthAnonEvent, 1 ) ) { //
// The registry key for iissuba is not configured correctly on local machine
//
g_pW3Server->LogEvent( W3_EVENT_SUBAUTH_REGISTRY_CONFIGURATION_LOCAL, 0, NULL ); } } } else if( !W3_STATE_AUTHENTICATION::sm_fLocalSystem ) { if( W3_STATE_AUTHENTICATION::sm_lLocalSystemEvent == 0 ) { if( !InterlockedExchange( &W3_STATE_AUTHENTICATION::sm_lLocalSystemEvent, 1 ) ) { //
// The process token does not have SeTcbPrivilege
//
g_pW3Server->LogEvent( W3_EVENT_SUBAUTH_LOCAL_SYSTEM, 0, NULL ); } } } else { fUseAnonSubAuth = TRUE; } } hr = g_pW3Server->QueryTokenCache()->GetCachedToken( strUserName.QueryStr(), strDomainName.QueryStr(), pszPasswd, QueryLogonMethod(), fUseAnonSubAuth, fPossibleUPNLogon, NULL, &_pctAnonymousToken, &dwLogonError ); if ( FAILED( hr ) ) { return hr; }
return NO_ERROR; }
HRESULT W3_METADATA::GetAndRefAnonymousToken( TOKEN_CACHE_ENTRY ** ppCachedToken ) /*++
Description:
Get the anonymous token for the current request. Everybody that calls this function needs to call deref when it's done.
Arguments:
NONE
Returns:
HRESULT
--*/ { HRESULT hr = S_OK; BOOL fReadLocked = TRUE;
//
// If you want to modify the stack buffer length, make sure
// it's a multiple of 16 bytes( CRYPTPROTECTMEMORY_BLOCK_SIZE )
//
STACK_BUFFER( bufDecryptedPassword, PWLEN * sizeof( WCHAR ) );
if( ppCachedToken == NULL ) { return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *ppCachedToken = NULL; //
// Do a quick check for an existing token
//
if ( _pctAnonymousToken == NULL ) { return NO_ERROR; } //
// First see if we even have a token to return?
//
_TokenLock.ReadLock(); if ( _pctAnonymousToken != NULL ) { //
// Checkout the token. This insures we don't use a
// flushed token
//
if ( !_pctAnonymousToken->Checkout( NULL ) ) { //
// OK. Get rid of associated token and build a new one
//
_TokenLock.ConvertSharedToExclusive(); fReadLocked = FALSE; if ( _pctAnonymousToken != NULL ) { _pctAnonymousToken->DereferenceCacheEntry(); _pctAnonymousToken = NULL; } //
// Do the logon
//
hr = DecryptMemoryPassword( &_strPasswd, &bufDecryptedPassword ); if ( SUCCEEDED( hr ) ) { hr = CreateAnonymousToken( _strUserName.QueryStr(), ( WCHAR * )bufDecryptedPassword.QueryPtr() ); if ( SUCCEEDED( hr ) ) { if ( _pctAnonymousToken != NULL ) { _pctAnonymousToken->ReferenceCacheEntry(); } *ppCachedToken = _pctAnonymousToken; }
SecureZeroMemory( bufDecryptedPassword.QueryPtr(), bufDecryptedPassword.QuerySize() ); } } else { *ppCachedToken = _pctAnonymousToken; } } if ( fReadLocked ) { _TokenLock.ReadUnlock(); } else { _TokenLock.WriteUnlock(); }
return hr; }
HRESULT W3_METADATA::GetAndRefVrAccessToken( TOKEN_CACHE_ENTRY ** ppCachedToken ) /*++
Description:
Get the UNC token for the current request. Everybody that calls this function needs to call deref when it's done.
Arguments:
NONE
Returns:
HRESULT
--*/ { HRESULT hr = S_OK; BOOL fReadLocked = TRUE; STACK_BUFFER( bufDecryptedPassword, PWLEN * sizeof( WCHAR ) );
if( ppCachedToken == NULL ) { return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *ppCachedToken = NULL;
//
// Do a quick check for an existing token
//
if ( _pctVrToken == NULL ) { return NO_ERROR; } //
// First see if we even have a token to return?
//
_TokenLock.ReadLock(); if ( _pctVrToken != NULL ) { //
// Checkout the token. This insures we don't use a
// flushed token
//
if ( !_pctVrToken->Checkout( NULL ) ) { //
// OK. Get rid of associated token and build a new one
//
_TokenLock.ConvertSharedToExclusive(); fReadLocked = FALSE; if ( _pctVrToken != NULL ) { _pctVrToken->DereferenceCacheEntry(); _pctVrToken = NULL; } //
// Do the logon
//
if ( _strVrUserName.QueryStr() != NULL && _strVrPasswd.QueryStr() != NULL && _strVrUserName.QueryStr()[0] ) { hr = DecryptMemoryPassword( &_strVrPasswd, &bufDecryptedPassword ); if( SUCCEEDED( hr ) ) { hr = CreateUNCVrToken( _strVrUserName.QueryStr(), (WCHAR*) bufDecryptedPassword.QueryPtr() ); if( SUCCEEDED( hr ) ) { _dirmonConfig.hToken = _pctVrToken->QueryImpersonationToken(); _pctVrToken->ReferenceCacheEntry(); *ppCachedToken = _pctVrToken; } else { _fUNCUserInvalid = TRUE; hr = NO_ERROR; }
SecureZeroMemory( bufDecryptedPassword.QueryPtr(), bufDecryptedPassword.QuerySize() ); } } } else { *ppCachedToken = _pctVrToken; } }
if ( fReadLocked ) { _TokenLock.ReadUnlock(); } else { _TokenLock.WriteUnlock(); }
return hr; }
HRESULT W3_METADATA::SetIpAccessCheck( LPVOID pMDData, DWORD dwDataLen ) /*++
Description:
Store away the IP DNS list
Arguments:
pMDData - Beginning of binary blob to store dwDataLen - Length of binary data
Returns:
HRESULT
--*/ { if ( !_buffIpAccessCheck.Resize( dwDataLen ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } memcpy( _buffIpAccessCheck.QueryPtr(), pMDData, dwDataLen ); _cbIpAccessCheck = dwDataLen; return NO_ERROR; }
HRESULT W3_METADATA::GetMetadataProperty( W3_CONTEXT * pW3Context, DWORD dwPropertyId, PBYTE pbBuffer, DWORD cbBuffer, DWORD * pcbBufferRequired ) /*++
Routine Description:
Retrieve and serialize the requested metabase property.
Arguments:
pW3Context - W3 Context dwPropertyId - Property ID pbBuffer - Buffer (OPTIONAL) cbBuffer - Size of given buffer pcbBufferRequired - Set to size of buffer required
Returns:
HRESULT
--*/ { HRESULT hr; DWORD dwDataSetNumber; DWORD dwNumMDRecords; METADATA_GETALL_RECORD* pRecord; VOID * pDataPointer; DWORD cbNeeded = 0; METADATA_RECORD * pOutputRecord; if ( pcbBufferRequired == NULL || pW3Context == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } //
// First check whether we have already read the raw buffer. If not,
// read it in
//
if ( _pGetAllBuffer == NULL ) { BUFFER * pBuffer; MB mb( g_pW3Server->QueryMDObject() ); BOOL fRet; STACK_STRU( strUrl, 256 ); HANDLE hToken = NULL; pBuffer = new BUFFER; if ( pBuffer == NULL ) { return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY ); } hr = pW3Context->QueryRequest()->GetUrl( &strUrl ); if ( FAILED( hr ) ) { return hr; }
fRet = OpenThreadToken( GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hToken ); if ( fRet ) { DBG_ASSERT( hToken != NULL ); RevertToSelf(); } //
// Open the metabase at the site level
//
fRet = mb.Open( pW3Context->QuerySite()->QueryMBRoot()->QueryStr() ); if ( fRet ) { fRet = mb.GetAll( strUrl.QueryStr(), METADATA_INHERIT | METADATA_PARTIAL_PATH, IIS_MD_UT_FILE, pBuffer, &dwNumMDRecords, &dwDataSetNumber ); mb.Close(); } if ( hToken != NULL ) { SetThreadToken( NULL, hToken ); CloseHandle( hToken ); hToken = NULL; } if ( !fRet ) { return HRESULT_FROM_WIN32( GetLastError() ); } //
// Try to stuff our buffer into this cache entry
//
LockCacheEntry(); if ( _pGetAllBuffer == NULL ) { _pGetAllBuffer = pBuffer; _cGetAllRecords = dwNumMDRecords; } else { //
// We already have one in here. Kill ours.
//
delete pBuffer; pBuffer = NULL; } UnlockCacheEntry(); } DBG_ASSERT( _pGetAllBuffer != NULL ); //
// Lookup the property in question
//
for ( DWORD i = 0; i < _cGetAllRecords; i++ ) { pRecord = &(((METADATA_GETALL_RECORD*) _pGetAllBuffer->QueryPtr())[ i ]); pDataPointer = (PVOID) ((PCHAR) _pGetAllBuffer->QueryPtr() + pRecord->dwMDDataOffset ); DBG_ASSERT( pRecord != NULL ); DBG_ASSERT( pDataPointer != NULL );
if ( pRecord->dwMDIdentifier == dwPropertyId ) { //
// Found it!
//
switch( pRecord->dwMDDataType ) { case DWORD_METADATA: cbNeeded = sizeof( DWORD ); break; case STRING_METADATA: cbNeeded = ( wcslen( (WCHAR*) pDataPointer ) + 1 ) * sizeof( WCHAR ); break;
default: // CODEWORK: Handle the other data types
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } //
// Account for the METADATA_RECORD
//
cbNeeded += sizeof( METADATA_RECORD ); //
// Do the copy and other return values
//
DBG_ASSERT( cbNeeded != 0 ); *pcbBufferRequired = cbNeeded; if ( pbBuffer == NULL || cbBuffer < *pcbBufferRequired ) { return HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ); } pOutputRecord = (METADATA_RECORD*) pbBuffer; pOutputRecord->dwMDIdentifier = dwPropertyId; pOutputRecord->dwMDAttributes = pRecord->dwMDAttributes; pOutputRecord->dwMDUserType = pRecord->dwMDUserType; pOutputRecord->dwMDDataLen = cbNeeded - sizeof( METADATA_RECORD ); pOutputRecord->dwMDDataType = pRecord->dwMDDataType; pOutputRecord->pbMDData = pbBuffer + sizeof( METADATA_RECORD ); memcpy( pbBuffer + sizeof( METADATA_RECORD ), pDataPointer, pOutputRecord->dwMDDataLen );
return NO_ERROR; } } return HRESULT_FROM_WIN32( ERROR_INVALID_INDEX ); }
HRESULT W3_METADATA::ReadCustomFooter( WCHAR * pszFooter ) /*++
Routine Description:
Process a footer string, either reading the file or copying the string to the buffer.
Arguments:
pszFooter - The footer string, which may be a string or a file name. It looks like "STRING : some-string" or "FILE : file-name"
Returns:
HRESULT
--*/ { HRESULT hr; STACK_STRU( strFooter, MAX_PATH ); BOOL fFooterIsString = FALSE;
// First thing to do is to determine if this is a string or a file name.
// Skip preceding whitespace and then strcmp.
while (iswspace(*pszFooter)) { pszFooter++; }
if (!_wcsnicmp(pszFooter, L"STRING", 6)) { fFooterIsString = TRUE; pszFooter += 6; } else if (!_wcsnicmp(pszFooter, L"FILE", 4)) { fFooterIsString = FALSE; pszFooter += 4; } else { return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); }
// Now we look for 0 or more white space, followed by a colon, followed by
// more white space.
while (iswspace(*pszFooter)) { pszFooter++; }
if (*pszFooter != L':') { // No colon seperator, error.
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); }
pszFooter++;
//
// OK, now if this is a string we take everything after the colon to the
// end for the string. If this is a file name then we'll open and read the
// file.
//
if (fFooterIsString) { return _strFooterString.CopyW(pszFooter); } else { //
// For files, we'll skip any more white space before the name.
//
while (iswspace(*pszFooter)) { pszFooter++; } hr = _strFooterDocument.Copy( pszFooter ); if ( FAILED( hr ) ) { return hr; } }
return S_OK; }
HRESULT W3_METADATA::EncryptMemoryPassword( IN OUT STRU * strPassword ) /*++
Routine Description:
Encrypt the password within the current process
Arguments:
strPassword - The password to be encrypted. The encrypted password is in the buffer in the STRU, and is not null terminated. So QueryCB and QueryCCH don't mean anything here.
Returns:
HRESULT
--*/ { HRESULT hr;
hr = strPassword->Resize( ( ( strPassword->QueryCCH() + 1 ) * sizeof( WCHAR ) / CRYPTPROTECTMEMORY_BLOCK_SIZE + 1 ) * CRYPTPROTECTMEMORY_BLOCK_SIZE ); if( SUCCEEDED( hr ) ) { if( !CryptProtectMemory( strPassword->QueryBuffer()->QueryPtr(), strPassword->QueryBuffer()->QuerySize(), CRYPTPROTECTMEMORY_SAME_PROCESS ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); } } return hr; }
HRESULT W3_METADATA::DecryptMemoryPassword( IN STRU * strProtectedPassword, OUT BUFFER * bufDecryptedPassword ) /*++
Routine Description:
Decrypt the password with the current process
Arguments:
strProtectedPassword - The protected password to be decrypted. bufDecryptedPassword - Holds the decrypted password.
Returns:
HRESULT
--*/ { HRESULT hr = S_OK;
if( !bufDecryptedPassword->Resize( strProtectedPassword->QueryBuffer()->QuerySize() ) ) { return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY ); }
memcpy( bufDecryptedPassword->QueryPtr(), strProtectedPassword->QueryStr(), bufDecryptedPassword->QuerySize() );
if( !CryptUnprotectMemory( bufDecryptedPassword->QueryPtr(), bufDecryptedPassword->QuerySize(), CRYPTPROTECTMEMORY_SAME_PROCESS ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); }
return hr; }
HRESULT W3_METADATA::SetExpire(WCHAR *pszExpire) /*++
Routine Description:
Set the expire header to be used on all responses
Arguments:
pszExpire: the string containing the description. It could have the form empty: no expire "s, some-date" : expire on this date" "d, some-number" : expire after this many seconds
Returns:
HRESULT
--*/ { while (iswspace(*pszExpire)) { pszExpire++; }
LPWSTR pszParam; if ((pszParam = wcschr(pszExpire, L',')) == NULL) { if (*pszExpire == L'\0') { _dwExpireMode = EXPIRE_MODE_OFF; return S_OK; }
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); }
pszParam++; while (iswspace(*pszParam)) { pszParam++; }
HRESULT hr; switch (*pszExpire) { case L's': case L'S': if (FAILED(hr = _strExpireHeader.CopyWTruncate(pszParam))) { return hr; } _dwExpireMode = EXPIRE_MODE_STATIC; break;
case L'd': case L'D': LPWSTR endPtr; DWORD dwExpire;
if (pszParam[0] == L'0' && pszParam[1] == L'x') { dwExpire = wcstoul(pszParam + 2, &endPtr, 16); } else { dwExpire = wcstoul(pszParam, &endPtr, 10); }
if (!iswspace(*endPtr) && *endPtr != L'\0') { return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); }
if (dwExpire != ULONG_MAX) { if (dwExpire > MAX_GLOBAL_EXPIRE) { dwExpire = MAX_GLOBAL_EXPIRE; }
_dwExpireMode = EXPIRE_MODE_DYNAMIC; _dwExpireDelta = dwExpire; } break;
default: return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); }
return S_OK; }
HRESULT W3_METADATA::SetCacheControlHeader() /*++
Routine Description:
If no-cache, max-age or a dynamic expires directive is present, add it to Cache-Control header
Arguments:
None
Returns:
HRESULT
--*/ { switch (QueryExpireMode()) { case EXPIRE_MODE_NONE: _fHaveNoCache = FALSE; _fHaveMaxAge = FALSE; break;
case EXPIRE_MODE_DYNAMIC: // If we have a dynamic Expires header, create a max-age directive
if (_dwExpireDelta != 0) { _fHaveNoCache = FALSE; if (!_fHaveMaxAge) { _fHaveMaxAge = TRUE; _dwMaxAge = _dwExpireDelta; } } else { _fHaveNoCache = TRUE; _fHaveMaxAge = FALSE; } break;
default: break; }
BOOL fHaveCCHeader = !_strCacheControlHeader.IsEmpty(); HRESULT hr; if (_fHaveNoCache) { if (FAILED(hr = _strCacheControlHeader.Append( fHaveCCHeader ? ",no-cache" : "no-cache"))) { return hr; } } else if (_fHaveMaxAge) { CHAR pszMaxAgeBuffer[16]; _itoa(_dwMaxAge, pszMaxAgeBuffer, 10);
if (FAILED(hr = _strCacheControlHeader.Append( fHaveCCHeader ? ",max-age=" : "max-age=")) || FAILED(hr = _strCacheControlHeader.Append(pszMaxAgeBuffer))) { return hr; } }
return S_OK; }
HRESULT META_SCRIPT_MAP::Initialize( IN WCHAR * szScriptMapData ) /*++
Routine Description:
Initialize the collection of META_SCRIPT_MAP_ENTRIES from the metadata.
This routine will modify the multisz it works with (by replacing some ',' with '\0' ). Currently it modifies the in parameter, which is kindof icky. We could avoid this by copying the buffer.
Arguments: szScriptMapData - A multi-sz of script map entries.
Return Value:
Notes:
Script map is a multi-sz with each string being a comma separated list <extension>,<executable>,<flags>,<verb list>
<extension>: .xyz - Maximum of 128 characters * - Star script map - routes all requests though the executable
<executable> - Extension to invoke
<flags>: 1 - Allow run in script access directory ( MD_SCRIPTMAPFLAG_SCRIPT ) 4 - Check for pathinfo file ( MD_SCRIPTMAPFLAG_CHECK_PATH_INFO )
<verb list>: <verb>,<verb>,<verb> - Allowed verbs - If no verbs are listed, a value of "all verbs" is assumed.
--*/ { DBG_ASSERT( szScriptMapData );
HRESULT hr = NOERROR;
// Iterate over multisz
WCHAR * pszEntryIterator; // Current mapping
WCHAR * pszExtension; WCHAR * pszExecutable; WCHAR * pszFlags; DWORD Flags; WCHAR * pszVerbs; DWORD cchVerbs; WCHAR * pszDest;
//
// Iterate over each mapping
//
pszEntryIterator = szScriptMapData; while( *pszEntryIterator != L'\0' ) { //
// Get the extension
//
pszExtension = pszEntryIterator;
//
// Get the executable
//
pszEntryIterator = wcschr( pszEntryIterator, L',' ); if( pszEntryIterator == NULL ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); return hr; }
*pszEntryIterator++ = L'\0';
pszExecutable = pszEntryIterator;
if( pszExecutable == L'\0' ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); return hr; }
//
// Get the flags
//
pszEntryIterator = wcschr( pszEntryIterator, L',' ); if( pszEntryIterator == NULL ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); return hr; }
*pszEntryIterator++ = L'\0';
//
// We don't need pszFlags here, but we will need it if
// there is an empty verb list, to reset our iterator.
//
pszFlags = pszEntryIterator;
Flags = wcstoul( pszFlags, NULL, 10 );
//
// Get the verbs
//
pszEntryIterator = wcschr( pszEntryIterator, L',' ); if( pszEntryIterator != NULL ) { //
// There is a list of verbs
//
*pszEntryIterator++ = L'\0';
pszDest = pszVerbs = pszEntryIterator; //
// Format verb list as a multi-sz for each entry
//
cchVerbs = 1;
while( *pszEntryIterator != L'\0') { if( *pszEntryIterator == L',' || *pszEntryIterator == L' ' ) { while( *pszEntryIterator == L',' || *pszEntryIterator == L' ' ) { pszEntryIterator++; }
*pszDest++ = L'\0'; } else { *pszDest++ = *pszEntryIterator++; }
cchVerbs++; } } else { //
// Empty Verb List
//
//
// We've lost our iterator so we need to get it back.
// Point to the terminator.
//
pszEntryIterator = pszFlags + wcslen( pszFlags ); pszVerbs = pszEntryIterator; cchVerbs = 1; }
//
// Create and add the entry object to our list
//
META_SCRIPT_MAP_ENTRY * pNewEntry = new META_SCRIPT_MAP_ENTRY();
if( pNewEntry == NULL ) { hr = E_OUTOFMEMORY; return hr; }
hr = pNewEntry->Create( pszExtension, pszExecutable, Flags, pszVerbs, cchVerbs ); if( FAILED(hr) ) { delete pNewEntry; return hr; }
if (pNewEntry->QueryIsStarScriptMap()) { InsertTailList( &m_StarScriptMapListHead, &pNewEntry->m_ListEntry ); } else { InsertTailList( &m_ListHead, &pNewEntry->m_ListEntry ); } //
// Move to the next entry.
//
pszEntryIterator++; }
return hr; }
BOOL META_SCRIPT_MAP::FindEntry( IN const STRU & strExtension, OUT META_SCRIPT_MAP_ENTRY * * ppScriptMapEntry ) /*++
Routine Description:
Arguments: Return Value:
--*/ { *ppScriptMapEntry = NULL;
PLIST_ENTRY pEntry; META_SCRIPT_MAP_ENTRY * pScriptMapEntry = NULL;
for( pEntry = m_ListHead.Flink; pEntry != &m_ListHead; pEntry = pEntry->Flink ) { pScriptMapEntry = CONTAINING_RECORD( pEntry, META_SCRIPT_MAP_ENTRY, m_ListEntry );
if( strExtension.Equals( pScriptMapEntry->m_strExtension ) ) { *ppScriptMapEntry = pScriptMapEntry; return TRUE; } }
return FALSE; }
VOID META_SCRIPT_MAP::Terminate( VOID ) /*++
Routine Description:
Arguments: Return Value:
--*/ { META_SCRIPT_MAP_ENTRY * pScriptMapEntry;
while( !IsListEmpty( &m_StarScriptMapListHead ) ) { pScriptMapEntry = CONTAINING_RECORD( m_StarScriptMapListHead.Flink, META_SCRIPT_MAP_ENTRY, m_ListEntry );
RemoveEntryList( &pScriptMapEntry->m_ListEntry );
delete pScriptMapEntry; }
while( !IsListEmpty( &m_ListHead ) ) { pScriptMapEntry = CONTAINING_RECORD( m_ListHead.Flink, META_SCRIPT_MAP_ENTRY, m_ListEntry );
RemoveEntryList( &pScriptMapEntry->m_ListEntry );
delete pScriptMapEntry; } }
HRESULT META_SCRIPT_MAP_ENTRY::Create( IN const WCHAR * szExtension, IN const WCHAR * szExecutable, IN DWORD Flags, IN const WCHAR * szVerbs, IN DWORD cchVerbs ) /*++
Routine Description:
Arguments: Return Value:
--*/ { HRESULT hr = NOERROR; DWORD cchExecutable;
//
// Capture initialization parameters
//
m_Flags = Flags;
hr = m_strExtension.Copy( szExtension ); if( FAILED(hr) ) { return hr; }
//
// Lower-case to allow for case insensitive comparisons
//
_wcslwr( m_strExtension.QueryStr() ); if (szExtension[0] == L'*' && szExtension[1] == L'\0') { m_fIsStarScriptMapEntry = TRUE; }
//
// We treat the executable name as an ExpandSz, so expand it
//
WCHAR szExpand[MAX_PATH + 1]; if (!ExpandEnvironmentStrings(szExecutable, szExpand, sizeof szExpand/sizeof WCHAR)) { return HRESULT_FROM_WIN32(GetLastError()); }
if (FAILED(hr = m_strExecutable.Copy( szExpand ))) { return hr; } //
// If the executable is quoted, remove the quotes now
//
// Note that we can't just remove the first and last
// quote, as it's possible that the script map entry
// could look like this:
//
// "c:\program files\test\test.exe" "%s" "%s"
//
if ( m_strExecutable.QueryStr()[ 0 ] == L'\"' && wcschr( m_strExecutable.QueryStr() + 1, L'\"' ) ) { LPWSTR pCursor;
cchExecutable = m_strExecutable.QueryCCH();
pCursor = m_strExecutable.QueryStr();
while ( *(pCursor+1) != L'\"' ) { *pCursor = *(pCursor+1);
pCursor++; }
while ( *(pCursor+2) != L'\0' ) { *pCursor = *(pCursor+2);
pCursor++; }
*pCursor = L'\0';
m_strExecutable.SetLen( cchExecutable - 2 ); }
if (m_strExecutable.QueryCCH() > 4) { if (!_wcsicmp( m_strExecutable.QueryStr() + m_strExecutable.QueryCCH() - 4, L".dll")) { m_Gateway = GATEWAY_ISAPI; } else { m_Gateway = GATEWAY_CGI; } } else { return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); }
if (!m_Verbs.AppendW( szVerbs, cchVerbs )) { hr = HRESULT_FROM_WIN32(GetLastError()); return hr; }
return S_OK; }
HRESULT W3_METADATA_CACHE::Initialize( VOID ) /*++
Description:
Initialize metadata cache
Arguments:
None
Return:
HRESULT
--*/ { HRESULT hr; DWORD dwData; DWORD dwType; DWORD cbData = sizeof( DWORD ); DWORD csecTTL = DEFAULT_W3_METADATA_CACHE_TTL; HKEY hKey;
//
// What is the TTL for the URI cache
//
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Services\\w3svc\\Parameters", 0, KEY_READ, &hKey ) == ERROR_SUCCESS ) { DBG_ASSERT( hKey != NULL ); if ( RegQueryValueEx( hKey, L"MetadataCacheTTL", NULL, &dwType, (LPBYTE) &dwData, &cbData ) == ERROR_SUCCESS && dwType == REG_DWORD ) { csecTTL = dwData; }
RegCloseKey( hKey ); } //
// We'll use TTL for scavenge period, and expect two inactive periods to
// flush
//
hr = SetCacheConfiguration( csecTTL * 1000, csecTTL * 1000, CACHE_INVALIDATION_METADATA, NULL ); if ( FAILED( hr ) ) { return hr; }
return W3_METADATA::Initialize(); }
HRESULT W3_METADATA_CACHE::GetMetaData( W3_CONTEXT * pW3Context, STRU & strUrl, W3_METADATA ** ppMetaData ) /*++
Routine Description:
Retrieve a W3_METADATA, creating if necessary
Arguments:
pW3Context - W3 context strUrl - Url ppMetaData - Set to point to metadata on success Return Value:
HRESULT
--*/ { W3_METADATA_KEY metaKey; W3_METADATA * pMetaData; DWORD dwDataSetNumber; HRESULT hr; STACK_STRU( strFullPath, 128 ); DATA_SET_CACHE * pDataSetCache; MB mb( g_pW3Server->QueryMDObject() ); HANDLE hToken = NULL; if ( pW3Context == NULL || ppMetaData == NULL ) { DBG_ASSERT( FALSE ); hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); goto Failed; } *ppMetaData = NULL; //
// Setup a key to lookup by determining data set number
//
hr = GetFullMetadataPath( pW3Context, strUrl, &strFullPath ); if ( FAILED( hr ) ) { goto Failed; } //
// Get a data set cache
//
hr = pW3Context->QuerySite()->GetDataSetCache( &pDataSetCache ); if ( FAILED( hr ) ) { goto Failed; } DBG_ASSERT( pDataSetCache != NULL ); hr = pDataSetCache->GetDataSetNumber( strFullPath, &dwDataSetNumber );
pDataSetCache->DereferenceDataSetCache(); pDataSetCache = NULL;
if ( FAILED( hr ) ) { goto Failed; } metaKey.SetDataSetNumber( dwDataSetNumber ); //
// Look it up
//
hr = FindCacheEntry( &metaKey, (CACHE_ENTRY**) &pMetaData ); if ( SUCCEEDED( hr ) ) { DBG_ASSERT( pMetaData != NULL ); *ppMetaData = pMetaData; goto Succeeded; } //
// We need to create a metadata entry and add it
//
if ( OpenThreadToken( GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hToken ) ) { DBG_ASSERT( hToken != NULL ); DBG_REQUIRE( RevertToSelf() ); }
hr = CreateNewMetaData( pW3Context, strUrl, strFullPath, &pMetaData ); if ( FAILED( hr ) ) { goto Failed; } if ( hToken != NULL ) { DBG_REQUIRE( SetThreadToken( NULL, hToken ) ); DBG_REQUIRE( CloseHandle( hToken ) ); hToken = NULL; }
DBG_ASSERT( pMetaData != NULL ); //
// Add to the cache
//
AddCacheEntry( pMetaData ); *ppMetaData = pMetaData; Succeeded:
DBG_ASSERT( SUCCEEDED( hr ) );
return NO_ERROR;
Failed:
if ( hToken != NULL ) { DBG_REQUIRE( SetThreadToken( NULL, hToken ) ); DBG_REQUIRE( CloseHandle( hToken ) ); hToken = NULL; }
DBG_ASSERT( FAILED( hr ) );
return hr;
}
HRESULT W3_METADATA_CACHE::CreateNewMetaData( W3_CONTEXT * pW3Context, STRU & strUrl, STRU & strFullMetadataPath, W3_METADATA ** ppMetaData ) /*++
Routine Description:
Create a new W3_METADATA and initializes it
Arguments:
pW3Context - context strUrl - URL strFullMetadataPath - Full metabase path to open ppMetaData - Set to new metadata entry on success
Return Value:
HRESULT
--*/ { HRESULT hr; W3_METADATA * pMetaData = NULL; if ( pW3Context == NULL || ppMetaData == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *ppMetaData = NULL; //
// Create the metadata object
//
pMetaData = new W3_METADATA( this ); if ( pMetaData == NULL ) { return HRESULT_FROM_WIN32( GetLastError() ); } //
// Set full URL for flushing purposes
//
hr = pMetaData->QueryMetadataPath()->Copy( strFullMetadataPath ); if ( FAILED( hr ) ) { pMetaData->DereferenceCacheEntry(); return hr; } //
// Initialize it
//
hr = pMetaData->ReadMetaData( *(pW3Context->QuerySite()->QueryMBRoot()), strUrl ); if( FAILED(hr) ) { pMetaData->DereferenceCacheEntry(); return hr; }
*ppMetaData = pMetaData;
return NO_ERROR; }
HRESULT W3_METADATA_CACHE::GetFullMetadataPath( W3_CONTEXT * pW3Context, STRU & strUrl, STRU * pstrFullPath ) /*++
Routine Description:
Get the full metadata given the URL and site
Arguments:
pW3Context - Used to get the site strUrl - Url pstrFullPath - Filled with full path on success Return Value:
HRESULT
--*/ { HRESULT hr; WCHAR * pszSource; DWORD cchSource;
if ( pW3Context == NULL || pstrFullPath == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } //
// Build up full metabase path (including instance)
//
hr = pstrFullPath->Copy( *(pW3Context->QuerySite()->QueryMBRoot()) ); if ( FAILED( hr ) ) { return hr; } //
// Don't copy two slashes
//
if ( strUrl.QueryStr()[ 0 ] == L'/' ) { pszSource = strUrl.QueryStr() + 1; cchSource = strUrl.QueryCCH() - 1; } else { pszSource = strUrl.QueryStr(); cchSource = strUrl.QueryCCH(); } hr = pstrFullPath->Append( pszSource, cchSource ); if ( FAILED( hr ) ) { return hr; } return NO_ERROR; }
|