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.
 
 
 
 
 
 

900 lines
24 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
urlauth.cxx
Abstract:
Implements an ISAPI * scriptmap which handles authorization by calling
into the NT AuthZ framework.
Author:
Bilal Alam (balam) Nov 26, 2001
--*/
#include "precomp.hxx"
DECLARE_DEBUG_PRINTS_OBJECT();
DECLARE_DEBUG_VARIABLE();
// local function declarations
HRESULT
GetTokenForExecution(
EXTENSION_CONTROL_BLOCK *pecb,
HANDLE * phToken
);
// global data
ADMIN_MANAGER_CACHE * g_pAdminManagerCache;
VOID
WINAPI
UrlAuthCompletion(
EXTENSION_CONTROL_BLOCK * pecb,
PVOID pvContext,
DWORD,
DWORD
)
/*++
Routine Description:
Routine to be called whenever ISAPI async stuff completes
Arguments:
pecb - EXTENSION_CONTROL_BLOCK *
pContext - Unused
cbIo - Unused
dwError - Unused
Return Value:
None
--*/
{
if ( pvContext )
{
DBG_REQUIRE( CloseHandle( pvContext ) );
pvContext = NULL;
}
pecb->ServerSupportFunction( pecb->ConnID,
HSE_REQ_DONE_WITH_SESSION,
NULL,
NULL,
NULL );
}
DWORD
WINAPI
HttpExtensionProc(
EXTENSION_CONTROL_BLOCK * pecb
)
/*++
Routine Description:
Main entry point for ISAPI
Arguments:
pecb - EXTENSION_CONTROL_BLOCK *
Return Value:
HSE_STATUS_*
--*/
{
BOOL fRet;
STACK_BUFFER( buff, 512 );
DWORD cbBuffer;
BOOL fAccessGranted = FALSE;
BOOL fFatalError = FALSE;
WCHAR * pszAdminStore;
ADMIN_MANAGER * pAdminManager = NULL;
AZ_APPLICATION * pApplication = NULL;
WCHAR * pszScopeName;
HRESULT hr = NO_ERROR;
METADATA_RECORD * pRecord;
HANDLE hImpersonationToken = NULL;
HSE_CUSTOM_ERROR_INFO CustomErrorInfo = { "500 Server Error", 0, TRUE };
//
// First determine whether URL authorization is even enabled for this
// URL
//
cbBuffer = buff.QuerySize();
fRet = pecb->ServerSupportFunction( pecb,
HSE_REQ_GET_METADATA_PROPERTY,
buff.QueryPtr(),
&cbBuffer,
(LPDWORD) MD_ENABLE_URL_AUTHORIZATION );
if ( !fRet )
{
if ( GetLastError() == ERROR_INVALID_INDEX )
{
// the common case is to fail with ERROR_INVALID_INDEX == access granted by no present metadata
// if the above call failed with someting like OUTOFMEMORY or INVALIDPARAMETER do not allow access.
fAccessGranted = TRUE;
}
else
{
fFatalError = TRUE;
hr = HRESULT_FROM_WIN32( GetLastError() );
}
goto AccessCheckComplete;
}
//
// Check the property now. If invalid type or if 0, then we're done
//
pRecord = (METADATA_RECORD*) buff.QueryPtr();
if ( pRecord->dwMDDataType != DWORD_METADATA ||
*((DWORD*)pRecord->pbMDData) == 0 )
{
fAccessGranted = TRUE;
goto AccessCheckComplete;
}
//
// If we're still a go, then retrieve the store name from the metabase.
// This is our key into the AdminManager cache we maintain
//
cbBuffer = buff.QuerySize();
fRet = pecb->ServerSupportFunction( pecb,
HSE_REQ_GET_METADATA_PROPERTY,
buff.QueryPtr(),
&cbBuffer,
(LPDWORD) MD_URL_AUTHORIZATION_STORE_NAME );
if ( !fRet )
{
//
// The only acceptable error is insufficient buffer. Otherwise, we
// don't have a store name and we're sunk
//
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
{
CustomErrorInfo.pszStatus = "500 Server Error";
CustomErrorInfo.uHttpSubError = 17;
CustomErrorInfo.fAsync = TRUE;
goto AccessCheckComplete;
}
DBG_ASSERT( cbBuffer > buff.QuerySize() );
if ( !buff.Resize( cbBuffer ) )
{
fFatalError = TRUE;
hr = HRESULT_FROM_WIN32( GetLastError() );
goto AccessCheckComplete;
}
cbBuffer = buff.QuerySize();
fRet = pecb->ServerSupportFunction( pecb,
HSE_REQ_GET_METADATA_PROPERTY,
buff.QueryPtr(),
&cbBuffer,
(LPDWORD) MD_URL_AUTHORIZATION_STORE_NAME );
if ( !fRet )
{
DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER );
fFatalError = TRUE;
hr = HRESULT_FROM_WIN32( GetLastError() );
goto AccessCheckComplete;
}
}
//
// Store name better be a string
//
pRecord = (METADATA_RECORD*) buff.QueryPtr();
if ( pRecord->dwMDDataType != STRING_METADATA )
{
fFatalError = TRUE;
hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
goto AccessCheckComplete;
}
//
// We should have an AdminManager store name now. Get an ADMIN_MANAGER
// from the cache (cache will create it if not already in cache)
//
pszAdminStore = (WCHAR*) pRecord->pbMDData;
DBG_ASSERT( g_pAdminManagerCache != NULL );
hr = g_pAdminManagerCache->GetAdminManager( pszAdminStore,
&pAdminManager );
if ( FAILED( hr ) )
{
//
// That sucks. We couldn't find an admin manager
//
CustomErrorInfo.pszStatus = "500 Server Error";
CustomErrorInfo.uHttpSubError = 18;
CustomErrorInfo.fAsync = TRUE;
goto AccessCheckComplete;
}
DBG_ASSERT( pAdminManager != NULL );
//
// Get the application from the AdminManager. That should be trivial
// since we always have the same application name.
//
hr = pAdminManager->GetApplication( &pApplication );
if ( FAILED( hr ) )
{
fFatalError = TRUE;
goto AccessCheckComplete;
}
DBG_ASSERT( pApplication != NULL );
//
// Get the scope name for this request
//
cbBuffer = buff.QuerySize();
fRet = pecb->ServerSupportFunction( pecb,
HSE_REQ_GET_METADATA_PROPERTY,
buff.QueryPtr(),
&cbBuffer,
(LPDWORD) MD_URL_AUTHORIZATION_SCOPE_NAME );
if ( !fRet )
{
//
// It is acceptable to have no scope name. Then we use the NULL scope
//
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
{
DBG_ASSERT( cbBuffer > buff.QuerySize() );
if ( !buff.Resize( cbBuffer ) )
{
fFatalError = TRUE;
hr = HRESULT_FROM_WIN32( GetLastError() );
goto AccessCheckComplete;
}
cbBuffer = buff.QuerySize();
fRet = pecb->ServerSupportFunction( pecb,
HSE_REQ_GET_METADATA_PROPERTY,
buff.QueryPtr(),
&cbBuffer,
(LPDWORD) MD_URL_AUTHORIZATION_SCOPE_NAME );
if ( !fRet )
{
DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER );
fFatalError = TRUE;
hr = HRESULT_FROM_WIN32( GetLastError() );
goto AccessCheckComplete;
}
}
}
if ( fRet )
{
pRecord = (METADATA_RECORD*) buff.QueryPtr();
if ( pRecord->dwMDDataType != STRING_METADATA )
{
fFatalError = TRUE;
hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
goto AccessCheckComplete;
}
//
// We have a scope name. It better be a string
//
pszScopeName = (WCHAR*) pRecord->pbMDData;
}
else
{
pszScopeName = NULL;
}
//
// Do the access check
//
hr = pApplication->DoAccessCheck( pecb,
pszScopeName,
&fAccessGranted );
if ( FAILED( hr ) )
{
fFatalError = TRUE;
goto AccessCheckComplete;
}
//
// Determine what impersonation token should be used for execution of real URL
//
if ( fAccessGranted )
{
hr = GetTokenForExecution( pecb, &hImpersonationToken );
if ( FAILED ( hr ) )
{
DBG_ASSERT( !hImpersonationToken );
fAccessGranted = FALSE;
fFatalError = TRUE;
goto AccessCheckComplete;
}
}
else
{
//
// Didn't have access. Thats a 401.7
//
CustomErrorInfo.pszStatus = "401 Unauthorized";
CustomErrorInfo.uHttpSubError = 7;
CustomErrorInfo.fAsync = TRUE;
}
AccessCheckComplete:
// not OK to have both access granted and a fatal error.
DBG_ASSERT ( !( fAccessGranted && fFatalError ) );
//
// Do some cleanup
//
if ( pAdminManager != NULL )
{
pAdminManager->DereferenceCacheEntry();
pAdminManager = NULL;
}
//
// Time to do something (TM).
//
if ( fAccessGranted )
{
HSE_EXEC_UNICODE_URL_INFO ExecUrlInfo;
HSE_EXEC_UNICODE_URL_USER_INFO ExecUrlUserInfo;
//
// Everything is ok. Just execute the original URL
//
fRet = pecb->ServerSupportFunction( pecb->ConnID,
HSE_REQ_IO_COMPLETION,
UrlAuthCompletion,
NULL,
(LPDWORD) hImpersonationToken );
if ( !fRet )
{
DBG_ASSERT( FALSE );
if ( hImpersonationToken )
{
DBG_REQUIRE( CloseHandle( hImpersonationToken ) );
hImpersonationToken = NULL;
}
return HSE_STATUS_ERROR;
}
ZeroMemory( &ExecUrlInfo, sizeof( ExecUrlInfo ) );
ExecUrlInfo.dwExecUrlFlags = HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR;
if ( hImpersonationToken )
{
ExecUrlInfo.pUserInfo = &ExecUrlUserInfo;
ZeroMemory( &ExecUrlUserInfo, sizeof( ExecUrlUserInfo ) );
cbBuffer = buff.QuerySize();
fRet = pecb->GetServerVariable( pecb->ConnID,
"UNICODE_REMOTE_USER",
buff.QueryPtr(),
&cbBuffer );
if ( !fRet )
{
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
{
if ( hImpersonationToken )
{
DBG_REQUIRE( CloseHandle( hImpersonationToken ) );
hImpersonationToken = NULL;
}
return HSE_STATUS_ERROR;
}
DBG_ASSERT( cbBuffer > buff.QuerySize() );
fRet = buff.Resize( cbBuffer );
if ( !fRet )
{
if ( hImpersonationToken )
{
DBG_REQUIRE( CloseHandle( hImpersonationToken ) );
hImpersonationToken = NULL;
}
return HSE_STATUS_ERROR;
}
cbBuffer = buff.QuerySize();
fRet = pecb->GetServerVariable( pecb->ConnID,
"UNICODE_REMOTE_USER",
buff.QueryPtr(),
&cbBuffer );
if ( !fRet )
{
if ( hImpersonationToken )
{
DBG_REQUIRE( CloseHandle( hImpersonationToken ) );
hImpersonationToken = NULL;
}
return HSE_STATUS_ERROR;
}
}
ExecUrlUserInfo.pszCustomUserName = (WCHAR*) buff.QueryPtr();
ExecUrlUserInfo.pszCustomAuthType = "URLAUTH";
ExecUrlUserInfo.hImpersonationToken = hImpersonationToken;
}
fRet = pecb->ServerSupportFunction( pecb->ConnID,
HSE_REQ_EXEC_UNICODE_URL,
&ExecUrlInfo,
NULL,
NULL );
if ( !fRet && hImpersonationToken )
{
DBG_REQUIRE( CloseHandle( hImpersonationToken ) );
}
hImpersonationToken = NULL;
return fRet ? HSE_STATUS_PENDING : HSE_STATUS_ERROR;
}
else if ( !fFatalError )
{
//
// This just means access wasn't granted. Send back an error
//
fRet = pecb->ServerSupportFunction( pecb->ConnID,
HSE_REQ_IO_COMPLETION,
UrlAuthCompletion,
NULL,
NULL );
if ( !fRet )
{
DBG_ASSERT( FALSE );
return HSE_STATUS_ERROR;
}
fRet = pecb->ServerSupportFunction( pecb->ConnID,
HSE_REQ_SEND_CUSTOM_ERROR,
&CustomErrorInfo,
NULL,
NULL );
if ( !fRet )
{
pecb->ServerSupportFunction( pecb->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER,
"500 Server Error",
NULL,
NULL );
return HSE_STATUS_ERROR;
}
else
{
return HSE_STATUS_PENDING;
}
}
else
{
//
// Fatal error. Just set the last error and bail.
//
SetLastError( WIN32_FROM_HRESULT( hr ) );
return HSE_STATUS_ERROR;
}
}
BOOL
WINAPI
GetExtensionVersion(
HSE_VERSION_INFO * pver
)
/*++
Routine Description:
Initialization routine for ISAPI
Arguments:
pver - Version information
Return Value:
TRUE if successful, else FALSE
--*/
{
HRESULT hr;
//
// Do ISAPI interface init crap
//
pver->dwExtensionVersion = MAKELONG( 0, 1 );
strcpy( pver->lpszExtensionDesc,
"URL Authorization ISAPI" );
//
// Create an admin manager cache
//
g_pAdminManagerCache = new ADMIN_MANAGER_CACHE;
if ( g_pAdminManagerCache == NULL )
{
return FALSE;
}
//
// Initialize ADMIN_MANAGER globals
//
hr = ADMIN_MANAGER::Initialize();
if ( FAILED( hr ) )
{
delete g_pAdminManagerCache;
g_pAdminManagerCache = NULL;
return FALSE;
}
//
// Initialize AZ_APPLICATION globals
//
hr = AZ_APPLICATION::Initialize();
if ( FAILED( hr ) )
{
ADMIN_MANAGER::Terminate();
delete g_pAdminManagerCache;
g_pAdminManagerCache = NULL;
return FALSE;
}
return TRUE;
}
BOOL
WINAPI
TerminateExtension(
DWORD
)
/*++
Routine Description:
Cleanup ISAPI extension
Arguments:
None
Return Value:
TRUE if successful, else FALSE
--*/
{
AZ_APPLICATION::Terminate();
ADMIN_MANAGER::Terminate();
delete g_pAdminManagerCache;
g_pAdminManagerCache = NULL;
return TRUE;
}
BOOL
WINAPI
DllMain(
HINSTANCE hDll,
DWORD dwReason,
LPVOID
)
/*++
Routine Description:
DLL Entry Routine
Arguments:
Return Value:
TRUE if successful, else FALSE
--*/
{
switch ( dwReason )
{
case DLL_PROCESS_ATTACH:
CREATE_DEBUG_PRINT_OBJECT( "urlauth" );
DisableThreadLibraryCalls( hDll );
break;
case DLL_PROCESS_DETACH:
DELETE_DEBUG_PRINT_OBJECT();
break;
default:
break;
}
return TRUE;
}
HRESULT
GetTokenForExecution(
EXTENSION_CONTROL_BLOCK *pecb,
HANDLE * phToken
)
/*++
Routine Description:
From metadata, determine the impersonation token to return
Arguments:
pecb - current ecb for request
phToken - where to store the token on success
Return Value:
HRESULT
--*/
{
HRESULT hr = S_OK;
BOOL fRet = FALSE;
METADATA_RECORD *pRecord = NULL;
STACK_BUFFER( buff, 512 );
DWORD cbBuffer;
DWORD dwValue= 0;
HANDLE hToken = NULL;
HANDLE hToken2 = NULL;
DBG_ASSERT(pecb);
DBG_ASSERT(phToken);
*phToken = NULL;
cbBuffer = buff.QuerySize();
fRet = pecb->ServerSupportFunction( pecb,
HSE_REQ_GET_METADATA_PROPERTY,
buff.QueryPtr(),
&cbBuffer,
(LPDWORD) MD_URL_AUTHORIZATION_IMPERSONATION_LEVEL );
if ( !fRet &&
GetLastError() != ERROR_INVALID_INDEX )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto exit;
}
else if ( fRet )
{
//
// Check the property now. If invalid type then we're done
//
pRecord = (METADATA_RECORD*) buff.QueryPtr();
if ( pRecord->dwMDDataType != DWORD_METADATA )
{
hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
goto exit;
}
// valid type of data - validity of data value done in switch below
dwValue = *(DWORD*)pRecord->pbMDData;
}
else
{
DBG_ASSERT( !fRet && GetLastError() == ERROR_INVALID_INDEX );
// no metadata present, therefore use the default of zero
dwValue = 0;
}
switch(dwValue)
{
case 0:
// use the current authenticated user
fRet = OpenThreadToken( GetCurrentThread(),
TOKEN_ALL_ACCESS,
TRUE,
&hToken );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto exit;
}
break;
case 1:
// use the process token to impersonate
// first get the current impersonation token
fRet = OpenThreadToken( GetCurrentThread(),
TOKEN_IMPERSONATE,
TRUE,
&hToken2 );
if ( fRet )
{
DBG_ASSERT( hToken2 != NULL );
RevertToSelf();
}
// get the current process token - it's a primary token
fRet = OpenProcessToken( GetCurrentProcess(),
TOKEN_ALL_ACCESS,
&hToken );
if ( hToken2 != NULL )
{
if ( !SetThreadToken( NULL, hToken2 ) )
{
DBG_ASSERT( FALSE );
}
DBG_REQUIRE( CloseHandle( hToken2 ) );
hToken2 = NULL;
}
// checking the return value from open the process token here
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto exit;
}
// create an impersonation token from the primary process token
fRet = DuplicateHandle ( GetCurrentProcess(),
hToken,
GetCurrentProcess(),
&hToken2,
0,
FALSE,
DUPLICATE_SAME_ACCESS );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto exit;
}
DBG_REQUIRE( CloseHandle( hToken ) );
hToken = hToken2;
hToken2 = NULL;
break;
case 2:
// get the anonymous token for impersonation
cbBuffer = buff.QuerySize();
fRet = pecb->GetServerVariable( pecb->ConnID,
"UNICODE_SCRIPT_NAME",
(CHAR*) buff.QueryPtr(),
&cbBuffer );
if ( !fRet )
{
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto exit;
}
DBG_ASSERT( cbBuffer > buff.QuerySize() );
fRet = buff.Resize( cbBuffer );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto exit;
}
cbBuffer = buff.QuerySize();
fRet = pecb->GetServerVariable( pecb->ConnID,
"UNICODE_SCRIPT_NAME",
(CHAR*) buff.QueryPtr(),
&cbBuffer );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto exit;
}
}
fRet = pecb->ServerSupportFunction( pecb->ConnID,
HSE_REQ_GET_UNICODE_ANONYMOUS_TOKEN,
buff.QueryPtr(),
(LPDWORD)&hToken,
NULL );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto exit;
}
break;
default:
// If not 0, 1, or 2 then not valid metadata
hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
goto exit;
break;
}
*phToken = hToken;
hToken = NULL;
hr = S_OK;
exit:
if ( hToken )
{
DBG_REQUIRE( CloseHandle( hToken ) );
hToken = NULL;
}
return hr;
};