windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/w3/server/extmap.cxx

939 lines
23 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/**********************************************************************/
/** 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 <rpc.h>
#include <rpcndr.h>
#include <mbstring.h>
//
// 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;
}