Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

724 lines
18 KiB

/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name :
dll_manager.cxx
Abstract:
IIS Plus ISAPI Handler. Dll management classes.
Author:
Taylor Weiss (TaylorW) 03-Feb-2000
Wade A. Hilmo (WadeH) 08-Mar-2001
Project:
w3isapi.dll
--*/
#include "precomp.hxx"
#include "dll_manager.h"
ISAPI_DLL_MANAGER * g_pDllManager = NULL;
PTRACE_LOG ISAPI_DLL::sm_pTraceLog;
ALLOC_CACHE_HANDLER * ISAPI_DLL::sm_pachIsapiDlls;
//
// Generic mapping for Application access check
//
GENERIC_MAPPING g_FileGenericMapping =
{
FILE_GENERIC_READ,
FILE_GENERIC_WRITE,
FILE_GENERIC_EXECUTE,
FILE_ALL_ACCESS
};
//static
HRESULT
ISAPI_DLL::Initialize(
VOID
)
{
ALLOC_CACHE_CONFIGURATION acConfig;
#if DBG
sm_pTraceLog = CreateRefTraceLog( 2000, 0 );
#endif
//
// Initialize a lookaside for this structure
//
acConfig.nConcurrency = 1;
acConfig.nThreshold = 100;
acConfig.cbSize = sizeof( ISAPI_DLL );
DBG_ASSERT( sm_pachIsapiDlls == NULL );
sm_pachIsapiDlls = new ALLOC_CACHE_HANDLER( "ISAPI_DLL",
&acConfig );
if ( sm_pachIsapiDlls == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
return NO_ERROR;
}
//static
VOID
ISAPI_DLL::Terminate(
VOID
)
{
if ( sm_pTraceLog != NULL )
{
DestroyRefTraceLog( sm_pTraceLog );
sm_pTraceLog = NULL;
}
if ( sm_pachIsapiDlls != NULL )
{
delete sm_pachIsapiDlls;
sm_pachIsapiDlls = NULL;
}
}
HRESULT
ISAPI_DLL::SetName(
const WCHAR * szModuleName,
HANDLE hImpersonation
)
{
HRESULT hr;
BOOL fImpersonateForUnc = FALSE;
DBG_ASSERT( CheckSignature() );
hr = m_strModuleName.Copy( szModuleName );
if ( FAILED( hr ) )
{
return hr;
}
//
// Store a copy of the anti-canon module name
//
hr = MakePathCanonicalizationProof(
const_cast<LPWSTR>( szModuleName ),
&m_strAntiCanonModuleName
);
if ( SUCCEEDED( hr ) )
{
//
// Verify that the file exists by getting its attributes.
//
// Note that in the UNC case, we have to do an impersonation
// before we can make this call.
//
if ( wcsncmp( szModuleName, L"\\\\", 2 ) == 0 )
{
fImpersonateForUnc = SetThreadToken( NULL, hImpersonation );
if ( !fImpersonateForUnc )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF((
DBG_CONTEXT,
"Attempt impersonate to get file attributes on %S failed "
"with error 0x%08x.\r\n",
szModuleName,
hr
));
return hr;
}
}
if ( GetFileAttributes( m_strAntiCanonModuleName.QueryStr() ) ==
0xffffffff )
{
IF_DEBUG( DLL_MANAGER )
{
DBGPRINTF((
DBG_CONTEXT,
"Attempt to get file attributes on %S failed "
"with error %d.\r\n",
szModuleName,
GetLastError()
));
}
//
// CODEWORK - This smoke and mirrors game with the last
// error is to preserve the old behavior that extensions
// that are not found return 500 errors to the client
// instead of 404.
//
if ( GetLastError() == ERROR_FILE_NOT_FOUND )
{
SetLastError( ERROR_MOD_NOT_FOUND );
}
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
if ( fImpersonateForUnc )
{
RevertToSelf();
}
return hr;
}
HRESULT
ISAPI_DLL::SetFastSid(
IN PSID pSid
)
{
DBG_ASSERT( pSid != NULL );
if ( GetLengthSid( pSid ) <= sizeof( m_abFastSid ) )
{
memcpy( m_abFastSid,
pSid,
GetLengthSid( pSid ) );
m_pFastSid = m_abFastSid;
}
return NO_ERROR;
}
HRESULT
ISAPI_DLL::Load(
IN HANDLE hImpersonation,
IN PSID pSid
)
{
HRESULT hr;
HSE_VERSION_INFO VersionInfo;
BOOL fImpersonatingForUnc = FALSE;
DBG_ASSERT( CheckSignature() );
//
// Check to see if the dll is already loaded.
// If so, just return NO_ERROR.
//
if ( m_fIsLoaded )
{
return NO_ERROR;
}
//
// So the dll is not loaded. Grab the lock and
// check again (another thread may have snuck in between
// the first test and now and already loaded it...
//
Lock();
if ( !m_fIsLoaded )
{
//
// Load the ACL for the dll file and do an access check
// before loading the dll itself. This is a slightly different
// approach than what earlier versions of IIS did. We are
// not going to be actually doing impersonation at the time
// we load the dll as earlier versions did. As a result,
// DllMain's DLL_PROCESS_ATTACH will run in the context of
// the worker process primary token instead of as the
// authenticated user.
//
// This new approach is the right way to do it, but it might
// introduce an incompatibility with any extensions that implement
// code in DllMain that depends on running as the authenticated
// user. It is very, very unlikely that we'll see this problem.
// If we do, the solution is to impersonate the token before
// calling LoadLibraryEx.
//
//
// Before calling LoadAcl, we should check to see if this
// dll lives on a UNC path. If it does, then we need to
// do the impersonation before attempting to load the ACL,
// since we can't assume that the inetinfo or w3wp process
// token can jump off the box.
//
if ( wcsncmp( m_strModuleName.QueryStr(), L"\\\\", 2 ) == 0 )
{
fImpersonatingForUnc = SetThreadToken( NULL, hImpersonation );
if ( !fImpersonatingForUnc )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"[ISAPI_DLL::Load] SetThreadToken failed for %S. Error %0x08x\n",
m_strModuleName.QueryStr(),
hr
));
goto Failed;
}
}
hr = LoadAcl( m_strAntiCanonModuleName );
if ( FAILED( hr ) )
{
goto Failed;
}
if ( !AccessCheck( hImpersonation,
pSid ) )
{
hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
goto Failed;
}
//
// Since we've just passed an AccessCheck, we can cache
// the fast SID now.
//
if ( pSid != NULL )
{
hr = SetFastSid( pSid );
if ( FAILED( hr ) )
{
goto Failed;
}
}
m_hModule = LoadLibraryEx( m_strAntiCanonModuleName.QueryStr(),
NULL,
LOAD_WITH_ALTERED_SEARCH_PATH
);
if ( m_hModule == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"[ISAPI_DLL::Load] LoadLibrary %S failed. Error %d\n",
m_strModuleName.QueryStr(),
GetLastError()
));
goto Failed;
}
//
// Lose the impersonation token before we call any more entry points...
//
if ( fImpersonatingForUnc )
{
RevertToSelf();
fImpersonatingForUnc = FALSE;
}
//
// Get the entry points
//
m_pfnGetExtensionVersion =
(PFN_GETEXTENSIONVERSION)GetProcAddress( m_hModule,
"GetExtensionVersion"
);
m_pfnTerminateExtension =
(PFN_TERMINATEEXTENSION)GetProcAddress( m_hModule,
"TerminateExtension"
);
m_pfnHttpExtensionProc =
(PFN_HTTPEXTENSIONPROC)GetProcAddress( m_hModule,
"HttpExtensionProc"
);
//
// HttpExtensionProc and GetExtensionVersion are required
//
if( !m_pfnGetExtensionVersion ||
!m_pfnHttpExtensionProc )
{
hr = HRESULT_FROM_WIN32( ERROR_PROC_NOT_FOUND );
//
// Make sure we dont call TerminateExtension on
// cleanup.
//
m_pfnTerminateExtension = NULL;
goto Failed;
}
if( !m_pfnGetExtensionVersion( &VersionInfo ) )
{
hr = GetLastError() == ERROR_SUCCESS ?
E_FAIL :
HRESULT_FROM_WIN32( GetLastError() );
goto Failed;
}
DBGPRINTF(( DBG_CONTEXT,
"ISAPI_DLL::Load() Loaded extension %S, "
" description \"%s\"\n",
m_strModuleName.QueryStr(),
VersionInfo.lpszExtensionDesc ));
m_fIsLoaded = TRUE;
}
Unlock();
return NO_ERROR;
Failed:
DBG_ASSERT( FAILED( hr ) );
//
// Clean up after the failed load attempt
//
if ( fImpersonatingForUnc )
{
RevertToSelf();
fImpersonatingForUnc = FALSE;
}
if ( m_pfnHttpExtensionProc )
{
m_pfnHttpExtensionProc = NULL;
}
if ( m_pfnGetExtensionVersion )
{
m_pfnGetExtensionVersion = NULL;
}
if ( m_pfnTerminateExtension )
{
m_pfnTerminateExtension = NULL;
}
if ( m_hModule )
{
FreeLibrary( m_hModule );
m_hModule = NULL;
}
Unlock();
return hr;
}
VOID
ISAPI_DLL::Unload( VOID )
{
if( m_pfnTerminateExtension )
{
m_pfnTerminateExtension( HSE_TERM_MUST_UNLOAD );
m_pfnTerminateExtension = NULL;
}
m_pfnGetExtensionVersion = NULL;
m_pfnHttpExtensionProc = NULL;
if( m_hModule )
{
FreeLibrary( m_hModule );
}
}
HRESULT
ISAPI_DLL::LoadAcl( STRU &strModuleName )
{
DWORD cbSecDesc = m_buffSD.QuerySize();
DWORD dwError;
DBG_ASSERT( CheckSignature() );
//
// Force an access check on the next request
//
while ( !GetFileSecurity( strModuleName.QueryStr(),
(OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION),
m_buffSD.QueryPtr(),
m_buffSD.QuerySize(),
&cbSecDesc ))
{
if ( ( dwError = GetLastError() ) != ERROR_INSUFFICIENT_BUFFER )
{
return HRESULT_FROM_WIN32( dwError );
}
if ( !m_buffSD.Resize( cbSecDesc ) )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
//
// Hopefully, we have sufficient buffer now, retry
//
}
return NOERROR;
}
BOOL
ISAPI_DLL::AccessCheck(
IN HANDLE hImpersonation,
IN PSID pSid
)
{
BOOL fRet = TRUE;
DWORD dwGrantedAccess = 0;
BYTE PrivSet[400];
DWORD cbPrivilegeSet = sizeof(PrivSet);
BOOL fAccessGranted;
DBG_ASSERT( CheckSignature() );
//
// First compare to the fast check SID if possible
//
if ( pSid != NULL && QueryFastSid() != NULL )
{
if ( EqualSid( pSid, QueryFastSid() ) )
{
return TRUE;
}
}
//
// Ok, just do the real access check
//
fRet = ( ::AccessCheck( QuerySecDesc(),
hImpersonation,
FILE_GENERIC_EXECUTE,
&g_FileGenericMapping,
(PRIVILEGE_SET *) &PrivSet,
&cbPrivilegeSet,
&dwGrantedAccess,
&fAccessGranted )
&& fAccessGranted);
return fRet;
}
HRESULT
ISAPI_DLL_MANAGER::GetIsapi(
IN const WCHAR * szModuleName,
OUT ISAPI_DLL ** ppIsapiDll,
IN HANDLE hImpersonation,
IN PSID pSid
)
{
HRESULT hr = NOERROR;
ISAPI_DLL * pIsapiDll = NULL;
LK_RETCODE lkrc;
IF_DEBUG( DLL_MANAGER )
{
DBGPRINTF((
DBG_CONTEXT,
"DllManager looking for %S.\r\n",
szModuleName
));
}
//
// Check for the ISAPI in the hash table. If we don't
// find it, then we'll need to create an entry in the
// hash for it.
//
lkrc = m_IsapiHash.FindKey( szModuleName, &pIsapiDll );
if ( lkrc == LK_SUCCESS )
{
IF_DEBUG( DLL_MANAGER )
{
DBGPRINTF((
DBG_CONTEXT,
"Found ISAPI_DLL %p (%S).\r\n",
pIsapiDll,
pIsapiDll->QueryModuleName()
));
}
}
else
{
//
// Create a new entry
//
IF_DEBUG( DLL_MANAGER )
{
DBGPRINTF((
DBG_CONTEXT,
"Creating new ISAPI_DLL object for %S.\r\n",
szModuleName
));
}
pIsapiDll = new ISAPI_DLL;
if ( !pIsapiDll )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Failed;
}
if ( FAILED( hr = pIsapiDll->SetName( szModuleName, hImpersonation ) ) )
{
pIsapiDll->DereferenceIsapiDll();
pIsapiDll = NULL;
goto Failed;
}
//
// Insert the new object into the hash table. If someone
// else beat us to it, then we'll just release our new one.
//
lkrc = m_IsapiHash.InsertRecord( pIsapiDll );
if ( lkrc == LK_SUCCESS )
{
IF_DEBUG( DLL_MANAGER )
{
DBGPRINTF((
DBG_CONTEXT,
"Added ISAPI_DLL %p to table for %S.\r\n",
pIsapiDll,
szModuleName
));
}
}
else
{
pIsapiDll->DereferenceIsapiDll();
pIsapiDll = NULL;
if ( lkrc == LK_KEY_EXISTS )
{
IF_DEBUG( DLL_MANAGER )
{
DBGPRINTF((
DBG_CONTEXT,
"InsertRecord for %S returned LK_KEY_EXISTS.\r\n",
szModuleName
));
}
//
// Ok, so let's get the existing one
//
lkrc = m_IsapiHash.FindKey( szModuleName, &pIsapiDll );
}
if ( lkrc != LK_SUCCESS )
{
hr = HRESULT_FROM_WIN32( lkrc );
goto Failed;
}
}
}
DBG_ASSERT( pIsapiDll );
//
// Call the Load function for the ISAPI_DLL. Note that the ISAPI_DLL
// function is smart enough to deal with locking on GetExtensionVersion
// and in only allowing it to happen one time.
//
hr = pIsapiDll->Load( hImpersonation,
pSid );
if ( FAILED( hr ) )
{
goto Failed;
}
//
// We've got the extension, but we still need to do
// an access check to make sure that the client
// is allowed to run it.
//
DBG_ASSERT( pIsapiDll );
if ( !pIsapiDll->AccessCheck( hImpersonation,
pSid ) )
{
hr = HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
goto Failed;
}
//
// We're about to return successfully. Set the out parameter now.
//
*ppIsapiDll = pIsapiDll;
DBG_ASSERT( SUCCEEDED( hr ) );
return hr;
Failed:
DBG_ASSERT( FAILED( hr ) );
IF_DEBUG( DLL_MANAGER )
{
DBGPRINTF((
DBG_CONTEXT,
"Error %d(0x%08x) occurred getting ISAPI_DLL object for %S.\r\n",
hr,
hr,
szModuleName
));
}
if ( pIsapiDll )
{
pIsapiDll->DereferenceIsapiDll();
pIsapiDll = NULL;
}
return hr;
}