/**********************************************************************/ /** Microsoft Windows NT **/ /** Copyright(c) Microsoft Corp., 1995 **/ /**********************************************************************/ /* extmap.cxx This module contains the extension mapping to CGI or BGI scripts. FILE HISTORY: Johnl 22-Sep-1995 Created */ #include "w3p.hxx" #include #include #include // // Name of the value under the parameters key containing the list of // script extension to BGI/CGI binaries. // #define HTTP_EXT_MAPS "Script Map" char Get[] = "GET"; #define GET_SIZE (sizeof("GET")) // // This is the maximum size for a script map extension // #define MAX_EXT_LEN 128 // // This list is the extension maps found in the registry - they always get // appended to the end of all extension mappings that are added to a particular // URI. // static BOOL fInitialized = FALSE; class EXT_MAP_ITEM { public: EXT_MAP_ITEM( const char * pszExtension, const char * pszImage, const DWORD dwFlags, const CHAR * pszExclusions, const DWORD dwExclusionLength) : _strExt ( pszExtension ), _strImage ( pszImage ), _GatewayType( GATEWAY_UNKNOWN ), _cchExt ( 0 ), _dwFlags ( dwFlags ), _fGetValid (0), _dwInclusionCount ( 0 ), _ppszInclusionTable ( NULL ), _pszInclusions ( NULL ) { DWORD cch; _fValid = _strExt.IsValid() && _strImage.IsValid() && ExpandImage(); if ( _fValid ) { const CHAR * pchtmp = pszImage; _cchExt = _strExt.QueryCCH(); // // Determine if this is a CGI or BGI gateway // while ( pchtmp = strchr( pchtmp + 1, '.' )) { if ( !_strnicmp( pchtmp, ".exe", 4 )) { _GatewayType = GATEWAY_CGI; } else if ( !_strnicmp( pchtmp, ".dll", 4 )) { _GatewayType = GATEWAY_BGI; } } if (!strcmp(pszExtension, "*")) { _fWildcard = TRUE; _dwFlags |= MD_SCRIPTMAPFLAG_WILDCARD; } else { _fWildcard = FALSE; _dwFlags &= ~MD_SCRIPTMAPFLAG_WILDCARD; } if (pszExclusions != NULL) { const CHAR *pszTemp; char *pszDest; _pszInclusions = new char[dwExclusionLength]; if (_pszInclusions == NULL) { _fValid = FALSE; return; } _dwInclusionCount = 1; pszTemp = pszExclusions; pszDest = _pszInclusions; while (*pszTemp != '\0') { CHAR ch; ch = *pszTemp; pszTemp++; if (ch != ',') { *pszDest = ch; } else { *pszDest = '\0'; _dwInclusionCount++; } pszDest++; } *pszDest = '\0'; _ppszInclusionTable = new char *[_dwInclusionCount]; if (_ppszInclusionTable == NULL) { _fValid = FALSE; return; } else { DWORD i; DWORD dwPos; pszTemp = _pszInclusions; i = 0; do { _ppszInclusionTable[i] = (CHAR *)pszTemp; if ( _fGetValid ) { dwPos = 0; } while (*pszTemp != '\0') { if (dwPos < GET_SIZE) { if (*pszTemp == Get[dwPos]) { dwPos++; if (dwPos == (GET_SIZE - 1)) { if (*(pszTemp+1) == '\0') { _fGetValid = TRUE; } else { dwPos = GET_SIZE; } } } else { dwPos = GET_SIZE; } } pszTemp++; } DBG_ASSERT(*pszTemp == '\0'); pszTemp++; i++; } while (i < _dwInclusionCount ); } } else { _dwInclusionCount = 0; _ppszInclusionTable = NULL; _pszInclusions = NULL; } } } ~EXT_MAP_ITEM( ) { delete _ppszInclusionTable; delete _pszInclusions; } GATEWAY_TYPE QueryGatewayType( VOID ) const { return _GatewayType; } const CHAR * QueryScript( VOID ) const { return _strImage.QueryStr(); } const CHAR * QueryExtension( VOID ) const { return _strExt.QueryStr(); } const BOOL AllowedOnReadDir() { return _dwFlags & MD_SCRIPTMAPFLAG_SCRIPT; } const DWORD QueryFlags() { return _dwFlags; } DWORD QueryCCHExt( VOID ) const { return _cchExt; } BOOL IsValid( VOID ) const { return _fValid; } BOOL IsWildCard( VOID ) const { return _fWildcard; } BOOL IsGetValid( VOID ) const { return _fGetValid; } BOOL ExpandImage( VOID ); BOOL CheckInclusions( const CHAR *pszVerb ); LIST_ENTRY _ListEntry; private: STR _strExt; STR _strImage; DWORD _cchExt; GATEWAY_TYPE _GatewayType; DWORD _fValid:1; DWORD _fWildcard:1; DWORD _fGetValid:1; DWORD _dwFlags; DWORD _dwInclusionCount; CHAR **_ppszInclusionTable; CHAR *_pszInclusions; }; BOOL EXT_MAP_ITEM::ExpandImage( VOID ) /*++ Routine Description: Expand any embedded environment variables in the image. Return Value: TRUE if successful, FALSE if not. --*/ { DWORD cbRet = 0; TCHAR achBuffer[ MAX_PATH + 1 ]; DWORD cbBufLen = sizeof( achBuffer ); cbRet = ExpandEnvironmentStringsA( _strImage.QueryStr(), achBuffer, cbBufLen ); if ( !cbRet || ( cbRet > cbBufLen ) ) { return FALSE; } else { return _strImage.Copy( achBuffer ); } } // // Private globals. // BOOL W3_METADATA::BuildExtMap( CHAR *pszExtMapList ) /*++ Routine Description: Builds the extension mapping into the metadata. The input string is a multi-sz of comma seperated ext, image name strings. Return Value: TRUE if successfull, FALSE if not. --*/ { EXT_MAP_ITEM * pExtMap; EXT_MAP_ITEM * pWCExtMapItem; LIST_ENTRY * pEntry; m_fAnyExtAllowedOnReadDir = FALSE; m_dwMaxExtLen = 0; do { CHAR *pszExt; CHAR *pszImage; CHAR *pszFlags; CHAR *pszExclusions; CHAR *pszTemp; CHAR *pszTemp2; CHAR *pszTemp3; DWORD dwExclusionSize; DWORD dwExtSize; pszExt = pszExtMapList; // Find the end of the extension, and temporarily NULL terminate it. pszImage = strchr(pszExt, ','); if (pszImage == NULL) { // Bad script map entry SetLastError(ERROR_INVALID_DATA); return FALSE; } pszTemp = pszImage++; *pszTemp = '\0'; pszFlags = strchr(pszImage, ','); if (pszFlags == NULL) { // Bad script map entry SetLastError(ERROR_INVALID_DATA); return FALSE; } pszTemp2 = pszFlags++; *pszTemp2 = '\0'; // // HOTFIX: make sure the extension is not too long to be copied to // our static buffer. // dwExtSize = strlen(pszExt); if (dwExtSize > MAX_EXT_LEN) { // Bad script map entry SetLastError(ERROR_INVALID_DATA); return FALSE; } else if (dwExtSize > m_dwMaxExtLen) { m_dwMaxExtLen = dwExtSize; } // // See if there's any excluded methods. If there are, break them out. // pszExclusions = strchr(pszFlags, ','); if (pszExclusions != NULL) { pszTemp3 = pszExclusions++; *pszTemp3 = '\0'; dwExclusionSize = strlen(pszExclusions) + 1; pszExtMapList = pszExclusions + dwExclusionSize; } else { pszExtMapList = pszFlags + strlen(pszFlags) + 1; dwExclusionSize = 0; } // // HOTFIX: Now convert extension to lower case so we can avoid // multi-byte string compares that cause lock contention. // IISstrlwr( (PUCHAR) pszExt ); // // Note we OR in the notransmit flag on *all* script mappings! // pExtMap = new EXT_MAP_ITEM( pszExt, pszImage, ((DWORD) atoi( pszFlags )), pszExclusions, dwExclusionSize); *pszTemp = ','; *pszTemp2 = ','; if (pszExclusions != NULL) { *pszTemp3 = '\0'; } if ( !pExtMap || !pExtMap->IsValid() ) { delete pExtMap; return FALSE; } if (!pExtMap->IsWildCard()) { InsertTailList( &m_ExtMapHead, &pExtMap->_ListEntry ); } else { pWCExtMapItem = (EXT_MAP_ITEM *)QueryWildcardMapping(); if (pWCExtMapItem != NULL) { delete pWCExtMapItem; } SetWildcardMapping( pExtMap ); } if ( pExtMap->QueryFlags() ) { m_fAnyExtAllowedOnReadDir = TRUE; } } while ( *pszExtMapList != '\0'); return TRUE; } // W3_METADATA::BuildExtMap VOID W3_METADATA::TerminateExtMap( VOID ) /*++ Routine Description: Cleans up the extension map list --*/ { LIST_ENTRY * pEntry; EXT_MAP_ITEM * pExtMap; while ( !IsListEmpty( &m_ExtMapHead )) { pExtMap = CONTAINING_RECORD( m_ExtMapHead.Flink, EXT_MAP_ITEM, _ListEntry ); RemoveEntryList( &pExtMap->_ListEntry ); delete pExtMap; } pExtMap = (EXT_MAP_ITEM *)QueryWildcardMapping(); if (pExtMap != NULL) { delete pExtMap; } } // W3_METADATA::TerminateExtMap BOOL EXT_MAP_ITEM::CheckInclusions( const CHAR *pszVerb ) /*++ Routine Description: Check the extension map list to see if the input verb is included. If it is, we return TRUE, otherwise we return FALSE. --*/ { DWORD i; // // Special case for EMPTY script map. Allow all verbs // if (0 == _dwInclusionCount) { return TRUE; } for (i = 0; i < _dwInclusionCount; i++) { if (!_stricmp(pszVerb, _ppszInclusionTable[i])) { return TRUE; } } return FALSE; } // EXT_MAP_ITEM::CheckInclusions BOOL W3_METADATA::LookupExtMap( IN const CHAR * pchExt, IN BOOL fNoWildcards, OUT STR * pstrGatewayImage, OUT GATEWAY_TYPE * pGatewayType, OUT DWORD * pcchExt, OUT BOOL * pfImageInURL, OUT BOOL * pfVerbExcluded, OUT DWORD * pdwFlags, IN const CHAR *pszVerb, IN enum HTTP_VERB Verb, IN OUT PVOID * ppvExtMapInfo ) /*++ Routine Description: Finds the admin specified mapping between a script extension and the associated CGI or BGI binary to run (or load). Arguments: pchExt - Pointer to possible extension to be mapped (i.e., '.pl') pstrGatewayImage - Receives the mapped binary image name pGatewayType - Specifies whether this is a BGI, CGI or MAP extension type pcchExt - Returns length of extension (including dot) pfImageInURL - Indicates an image was found encoded in the URL and not from a script extension mapping pdwFlags - Returns extension flags ppvExtMapInfo - Cached extension map info. If *ppvExtMapInfo is NULL on input, then set to the matched PEXT_MAP_ITEM. If not NULL on input, then it is used instead of doing lookup. --*/ { EXT_MAP_ITEM * pExtMapItem; DWORD cchTillEOS; BOOL fRet; LIST_ENTRY * pEntry; BOOL bFoundMatch = FALSE; BOOL fUseExtMapInfo = *ppvExtMapInfo != NULL; *pGatewayType = GATEWAY_UNKNOWN; *pfVerbExcluded = FALSE; // // Check for wildcard mapping first // if (!fNoWildcards) { pExtMapItem = (EXT_MAP_ITEM *)QueryWildcardMapping(); if (pExtMapItem != NULL) { if (Verb == HTV_GET && pExtMapItem->IsGetValid()) { bFoundMatch = TRUE; } else { // If verb is not included, don't return the * script map. // Instead, continue to look for a script map match. bFoundMatch = pExtMapItem->CheckInclusions( pszVerb ); } } } // // Look for the exact extension mapping if there's no wildcard // if (!bFoundMatch && pchExt != NULL) { if ( fUseExtMapInfo ) { // // If caller passed in a non-NULL pExtMapInfo, then use it // instead of doing a manual lookup // if ( *ppvExtMapInfo != EXTMAP_UNKNOWN_PTR ) { pExtMapItem = (EXT_MAP_ITEM*) *ppvExtMapInfo; bFoundMatch = TRUE; } } else { // // This buffer, rgchExtBuffer, holds a copy of a portion of a URL // which we are testing to see if it's a known extension. We copy // into this buffer so we can convert the extension to lower case // without disrupting the original. // CHAR rgchExtBuffer[MAX_EXT_LEN + 4]; DWORD dwLength; DBG_ASSERT( *pchExt == '.' ); // // HOTFIX: Now convert extension to lower case so we can avoid // multi-byte string compares that cause lock contention. // // Since we don't want to risk modifying the orignal URL, we // copy it into another buffer first. // cchTillEOS = strlen( pchExt ); dwLength = min(cchTillEOS, m_dwMaxExtLen + 1); memcpy(rgchExtBuffer, pchExt, dwLength); rgchExtBuffer[ dwLength ] = 0; IISstrlwr( (PUCHAR) rgchExtBuffer ); // // Look through the list of mappings // for ( pEntry = m_ExtMapHead.Flink; !bFoundMatch && pEntry != &m_ExtMapHead; pEntry = pEntry->Flink ) { pExtMapItem = CONTAINING_RECORD( pEntry, EXT_MAP_ITEM, _ListEntry ); if ( cchTillEOS >= pExtMapItem->QueryCCHExt() && (pchExt[pExtMapItem->QueryCCHExt()] == '/' || pchExt[pExtMapItem->QueryCCHExt()] == '\0' ) && !memcmp( rgchExtBuffer, pExtMapItem->QueryExtension(), pExtMapItem->QueryCCHExt()) ) { bFoundMatch = TRUE; *ppvExtMapInfo = pExtMapItem; } } } } if (bFoundMatch) { *pGatewayType = pExtMapItem->QueryGatewayType(); *pcchExt = pExtMapItem->QueryCCHExt(); *pfImageInURL = FALSE; *pdwFlags = pExtMapItem->QueryFlags(); // // Check that verb is included. If it isn't, we're still going to return // this item. Just indicate that the verb was excluded. // if (Verb != HTV_GET || !pExtMapItem->IsGetValid()) { *pfVerbExcluded = !pExtMapItem->CheckInclusions(pszVerb); } fRet = pstrGatewayImage->Copy( pExtMapItem->QueryScript() ); if ( !_stricmp( pExtMapItem->QueryScript(), "nogateway" ) ) { *pGatewayType = GATEWAY_NONE; } return fRet; } if ( !pchExt || fUseExtMapInfo ) { if ( !fUseExtMapInfo ) { *ppvExtMapInfo = EXTMAP_UNKNOWN_PTR; } return TRUE; } // // Either the image will be specified in the URL or not found, so // just indicate it's in the URL. Not found has precedence. // *pfImageInURL = TRUE; *pdwFlags = 0; // // Look for CGI or BGI scripts in the URL itself // if ( cchTillEOS >= 4 && (*(pchExt+4) == TEXT('/') || *(pchExt+4) == TEXT('\0')) ) { *pcchExt = 4; // // Don't confuse a menu map request with a gateway request // if ( !::_tcsnicmp( TEXT(".MAP"), pchExt, 4 )) { *pGatewayType = GATEWAY_MAP; return TRUE; } if ( !::_tcsnicmp( TEXT(".EXE"), pchExt, 4 ) || !::_tcsnicmp( TEXT(".CGI"), pchExt, 4 ) || !::_tcsnicmp( TEXT(".COM"), pchExt, 4 )) { *pGatewayType = GATEWAY_CGI; return TRUE; } else if (!::_tcsnicmp( TEXT(".DLL"), pchExt, 4 ) || !::_tcsnicmp( TEXT(".ISA"), pchExt, 4 ) ) { *pGatewayType = GATEWAY_BGI; return TRUE; } } *ppvExtMapInfo = EXTMAP_UNKNOWN_PTR; return TRUE; } // W3_METADATA::LookupExtMap APIERR ReadRegistryExtMap( VOID ) /*++ Routine Description: Builds the extension mapping from the registry Return Value: NO_ERROR if successful, win32 error code on failure --*/ { HKEY hkeyParam; DWORD dwDisposition; LIST_ENTRY pEntry; APIERR err; DWORD i = 0; DWORD dwRegType; MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() ); MULTISZ msz; BOOL fNeedToWrite = FALSE; if ( !fInitialized ) { fInitialized = TRUE; } // // Get the list // err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, W3_PARAMETERS_KEY "\\" HTTP_EXT_MAPS, 0, 0, 0, KEY_READ, NULL, &hkeyParam, &dwDisposition ); if( err != NO_ERROR ) { TCP_PRINT(( DBG_CONTEXT, "cannot open registry key, error %lu\n", err )); return NO_ERROR; } if ( !mb.Open( "/LM/W3SVC/", METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE ) || !mb.GetMultisz( "", MD_SCRIPT_MAPS, IIS_MD_UT_FILE, &msz )) { TCP_PRINT(( DBG_CONTEXT, "cannot get the script maps from the metabase, error %d\n", GetLastError() )); return NO_ERROR; } while ( TRUE ) { CHAR achExt[MAX_PATH+1]; CHAR achImage[MAX_PATH+1]; DWORD cchExt = sizeof( achExt ); DWORD cchImage = sizeof( achImage ); err = RegEnumValue( hkeyParam, i++, achExt, &cchExt, NULL, &dwRegType, (LPBYTE) achImage, &cchImage ); if ( err == ERROR_NO_MORE_ITEMS ) { err = NO_ERROR; break; } if ( dwRegType == REG_SZ ) { const CHAR * psz; BOOL fFound = FALSE; // // Look for this script map in the metabase, if not found, add it // for ( psz = msz.First(); psz != NULL; psz = msz.Next( psz ) ) { if ( !IISstrnicmp( (PUCHAR)achExt, (PUCHAR)psz, cchExt )) { fFound = TRUE; break; } } if ( !fFound ) { STR str; CHAR achFlags[32]; // // Note these scripts are added w/o the Script bit and with // the "never download" bit. In addition, we leave the // method exclusion list blank. // _itoa( 0, achFlags, 10 ); if ( !str.Append( achExt ) || !str.Append( "," ) || !str.Append( achImage ) || !str.Append( "," ) || !str.Append( achFlags ) || !msz.Append( str.QueryStr() )) { return err = GetLastError(); break; } TCP_PRINT(( DBG_CONTEXT, "Added \"%s\" from registry as script map\n", str.QueryStr() )); fNeedToWrite = TRUE; } } } if ( fNeedToWrite ) { if ( !mb.SetMultiSZ( "", MD_SCRIPT_MAPS, IIS_MD_UT_FILE, msz.QueryStr() )) { TCP_PRINT(( DBG_CONTEXT, "Failed to write MD_SCRIPT_MAPS back to metabase, error %d\n", GetLastError() )); } } RegCloseKey( hkeyParam ); return err; } VOID FreeRegistryExtMap( VOID ) { if ( !fInitialized ) { return; } fInitialized = FALSE; }