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.
 
 
 
 
 
 

2025 lines
48 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name :
passportprovider.cxx
Abstract:
Core passport authentication support
Author:
Bilal Alam (balam) 16-Mar-2001
Environment:
Win32 - User Mode
Project:
ULW3.DLL
--*/
#include "precomp.hxx"
#include "passportprovider.hxx"
#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \
const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
#define MAGIC_TWEENER_STRING L"msppchlg=1&mspplogin="
#define MAGIC_TWEENER_STRING_LEN ( sizeof( MAGIC_TWEENER_STRING ) / sizeof( WCHAR ) - 1 )
MIDL_DEFINE_GUID(IID,IID_IPassportManager3,0x1451151f,0x90a0,0x491b,0xb8,0xe1,0x81,0xa1,0x37,0x67,0xed,0x98);
MIDL_DEFINE_GUID(IID,IID_IPassportFactory,0x5602E147,0x27F6,0x11d3,0x94,0xDD,0x00,0xC0,0x4F,0x72,0xDC,0x08);
MIDL_DEFINE_GUID(CLSID,CLSID_PassportFactory,0x74EB2514,0xE239,0x11D2,0x95,0xE9,0x00,0xC0,0x4F,0x8E,0x7A,0x70);
IPassportFactory * PASSPORT_CONTEXT::sm_pPassportManagerFactory;
BSTR PASSPORT_CONTEXT::sm_bstrMemberIdHigh;
BSTR PASSPORT_CONTEXT::sm_bstrMemberIdLow;
BSTR PASSPORT_CONTEXT::sm_bstrReturnUrl;
BSTR PASSPORT_CONTEXT::sm_bstrTimeWindow;
BSTR PASSPORT_CONTEXT::sm_bstrForceSignIn;
BSTR PASSPORT_CONTEXT::sm_bstrCoBrandTemplate;
BSTR PASSPORT_CONTEXT::sm_bstrLanguageId;
BSTR PASSPORT_CONTEXT::sm_bstrSecureLevel;
//static
HRESULT
PASSPORT_CONTEXT::Initialize(
VOID
)
/*++
Routine Description:
Do global initialization for passport goo
Arguments:
None
Return Value:
HRESULT
--*/
{
HRESULT hr = NO_ERROR;
//
// Pre-allocate some BSTRs
//
sm_bstrMemberIdHigh = SysAllocString( L"MemberIdHigh" );
if ( sm_bstrMemberIdHigh == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
sm_bstrMemberIdLow = SysAllocString( L"MemberIdLow" );
if ( sm_bstrMemberIdLow == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
sm_bstrReturnUrl = SysAllocString( L"ReturnURL" );
if ( sm_bstrReturnUrl == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
sm_bstrTimeWindow = SysAllocString( L"TimeWindow" );
if ( sm_bstrTimeWindow == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
sm_bstrForceSignIn = SysAllocString( L"ForceSignIn" );
if ( sm_bstrForceSignIn == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
sm_bstrCoBrandTemplate = SysAllocString( L"CoBrandTemplate" );
if ( sm_bstrCoBrandTemplate == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
sm_bstrLanguageId = SysAllocString( L"LanguageId" );
if ( sm_bstrLanguageId == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
sm_bstrSecureLevel = SysAllocString( L"SecureLevel" );
if ( sm_bstrSecureLevel == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
//
// Try to initialize the passport manager factory. If we cannot, then
// we're done
//
hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
if( FAILED( hr ) )
{
goto Failure;
}
hr = CoCreateInstance( CLSID_PassportFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IPassportFactory,
(void**)&sm_pPassportManagerFactory );
CoUninitialize();
if ( FAILED( hr ) )
{
goto Failure;
}
DBG_ASSERT( sm_pPassportManagerFactory != NULL );
return NO_ERROR;
Failure:
if ( FAILED( hr ) )
{
if ( sm_pPassportManagerFactory != NULL )
{
sm_pPassportManagerFactory->Release();
sm_pPassportManagerFactory = NULL;
}
if ( sm_bstrMemberIdLow != NULL )
{
SysFreeString( sm_bstrMemberIdLow );
sm_bstrMemberIdLow = NULL;
}
if ( sm_bstrMemberIdHigh != NULL )
{
SysFreeString( sm_bstrMemberIdHigh );
sm_bstrMemberIdHigh = NULL;
}
if ( sm_bstrReturnUrl != NULL )
{
SysFreeString( sm_bstrReturnUrl );
sm_bstrReturnUrl = NULL;
}
if ( sm_bstrTimeWindow == NULL )
{
SysFreeString( sm_bstrTimeWindow );
sm_bstrTimeWindow = NULL;
}
if ( sm_bstrForceSignIn == NULL )
{
SysFreeString( sm_bstrForceSignIn );
sm_bstrForceSignIn = NULL;
}
if ( sm_bstrCoBrandTemplate == NULL )
{
SysFreeString( sm_bstrCoBrandTemplate );
sm_bstrCoBrandTemplate = NULL;
}
if ( sm_bstrLanguageId == NULL )
{
SysFreeString( sm_bstrLanguageId );
sm_bstrLanguageId = NULL;
}
if ( sm_bstrSecureLevel == NULL )
{
SysFreeString( sm_bstrSecureLevel );
sm_bstrSecureLevel = NULL;
}
}
return hr;
}
//static
VOID
PASSPORT_CONTEXT::Terminate(
VOID
)
/*++
Routine Description:
Cleanup global passport goo
Arguments:
None
Return Value:
None
--*/
{
if ( sm_pPassportManagerFactory != NULL )
{
sm_pPassportManagerFactory->Release();
sm_pPassportManagerFactory = NULL;
}
if ( sm_bstrMemberIdLow != NULL )
{
SysFreeString( sm_bstrMemberIdLow );
sm_bstrMemberIdLow = NULL;
}
if ( sm_bstrMemberIdHigh != NULL )
{
SysFreeString( sm_bstrMemberIdHigh );
sm_bstrMemberIdHigh = NULL;
}
if ( sm_bstrReturnUrl != NULL )
{
SysFreeString( sm_bstrReturnUrl );
sm_bstrReturnUrl = NULL;
}
if ( sm_bstrTimeWindow == NULL )
{
SysFreeString( sm_bstrTimeWindow );
sm_bstrTimeWindow = NULL;
}
if ( sm_bstrForceSignIn == NULL )
{
SysFreeString( sm_bstrForceSignIn );
sm_bstrForceSignIn = NULL;
}
if ( sm_bstrCoBrandTemplate == NULL )
{
SysFreeString( sm_bstrCoBrandTemplate );
sm_bstrCoBrandTemplate = NULL;
}
if ( sm_bstrLanguageId == NULL )
{
SysFreeString( sm_bstrLanguageId );
sm_bstrLanguageId = NULL;
}
if ( sm_bstrSecureLevel == NULL )
{
SysFreeString( sm_bstrSecureLevel );
sm_bstrSecureLevel = NULL;
}
}
BOOL
PASSPORT_CONTEXT::QueryUserError(
VOID
)
/*++
Routine Description:
Returns whether the user cancelled. That we have to do this work
really sucks
Arguments:
None
Return Value:
BOOL
--*/
{
LONG lError;
HRESULT hr;
if ( _pPassportManager == NULL )
{
return FALSE;
}
hr = _pPassportManager->get_Error( &lError );
if ( FAILED( hr ) )
{
return FALSE;
}
return lError != 0;
}
HRESULT
PASSPORT_CONTEXT::SetupDefaultRedirect(
W3_MAIN_CONTEXT * pMainContext,
BOOL * pfFoundRedirect
)
/*++
Routine Description:
Setup default redirect in case of client cancelling
Arguments:
pMainContext - main context
pfFoundRedirect - Set to TRUE if redirect URL found
Return Value:
HRESULT
--*/
{
HRESULT hr;
VARIANT vReturnUrl;
STACK_STRU( strRedirect, 512 );
VariantInit( &vReturnUrl );
if ( pMainContext == NULL ||
pfFoundRedirect == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfFoundRedirect = FALSE;
DBG_ASSERT( _pPassportManager != NULL );
//
// First get the default URL if any
//
hr = _pPassportManager->GetCurrentConfig( sm_bstrReturnUrl,
&vReturnUrl );
if ( FAILED( hr ) )
{
return NO_ERROR;
}
if ( vReturnUrl.vt != VT_BSTR ||
vReturnUrl.bstrVal[ 0 ] == L'\0' )
{
return NO_ERROR;
}
//
// Do the redirect
//
hr = strRedirect.Copy( vReturnUrl.bstrVal );
if ( FAILED( hr ) )
{
return hr;
}
hr = pMainContext->SetupHttpRedirect( strRedirect,
TRUE,
HttpStatusRedirect );
if ( FAILED( hr ) )
{
return hr;
}
*pfFoundRedirect = TRUE;
return NO_ERROR;
}
HRESULT
PASSPORT_CONTEXT::Create(
W3_FILTER_CONTEXT * pFilterContext
)
/*++
Routine Description:
Initialize a passport filter context
-the big thing being getting a passport manager for this request
Arguments:
pFilterContext - Filter context
Return Value:
HRESULT
--*/
{
IDispatch * pDispatch = NULL;
HRESULT hr;
DWORD cbBufferLength;
if ( sm_pPassportManagerFactory == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
}
//
// Do some COM/OLEAUT crap to get a passport manager
//
hr = sm_pPassportManagerFactory->CreatePassportManager( &pDispatch );
if ( FAILED( hr ) )
{
return hr;
}
DBG_ASSERT( pDispatch != NULL );
hr = pDispatch->QueryInterface( IID_IPassportManager3,
(VOID**) &_pPassportManager );
pDispatch->Release();
if ( FAILED( hr ) )
{
return hr;
}
DBG_ASSERT( _pPassportManager != NULL );
//
// Try a cookie of size 4096 since the samples all seem to use that size
//
if ( !_buffCookie.Resize( 4096 ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
cbBufferLength = _buffCookie.QuerySize();
//
// Pass filter context to passport manager so that it can inspect the
// request
//
DBG_ASSERT( _pPassportManager != NULL );
hr = _pPassportManager->OnStartPageFilter( (PBYTE) pFilterContext->QueryFC(),
&cbBufferLength,
(LPSTR)
_buffCookie.QueryPtr() );
if ( FAILED( hr ) )
{
return hr;
}
return NO_ERROR;
}
HRESULT
PASSPORT_CONTEXT::DoesApply(
HTTP_FILTER_CONTEXT * pfc,
BOOL * pfApplies,
STRA * pstrReturnCookie
)
/*++
Routine Description:
Check whether the given request has Passport stuff in it
Arguments:
pfc - Filter context
pfApplies - Set to TRUE if passport applies
pstrReturnCookie - Cookie to return in response
Return Value:
HRESULT
--*/
{
HRESULT hr;
VARIANT vTimeWindow;
VARIANT vForceLogin;
VARIANT vSecureLevel;
VARIANT_BOOL vb;
BUFFER bufReturnCookie;
if ( pfc == NULL ||
pfApplies == NULL ||
pstrReturnCookie == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfApplies = FALSE;
//
// Read parameters for IsAuthenticated(). If we can't find
// them then choose arbitrary (tm) defaults
//
VariantInit( &vTimeWindow );
hr = _pPassportManager->GetCurrentConfig( sm_bstrTimeWindow,
&vTimeWindow );
if ( FAILED( hr ) )
{
vTimeWindow.vt = VT_I4;
vTimeWindow.lVal = 10000;
}
VariantInit( &vForceLogin );
hr = _pPassportManager->GetCurrentConfig( sm_bstrForceSignIn,
&vForceLogin );
if ( FAILED( hr ) )
{
vForceLogin.vt = VT_BOOL;
vForceLogin.boolVal = VARIANT_FALSE;
}
VariantInit( &vSecureLevel );
hr = _pPassportManager->GetCurrentConfig( sm_bstrSecureLevel,
&vSecureLevel );
if ( FAILED( hr ) )
{
vSecureLevel.vt = VT_I4;
vSecureLevel.lVal = 10;
}
//
// Are we authenticated?
//
hr = _pPassportManager->IsAuthenticated( vTimeWindow,
vForceLogin,
vSecureLevel,
&vb );
if ( FAILED( hr ) )
{
return hr;
}
if ( vb == VARIANT_TRUE )
{
_fAuthenticated = TRUE;
}
*pfApplies = _fAuthenticated;
return pstrReturnCookie->Copy( (CHAR*) _buffCookie.QueryPtr() );
}
HRESULT
PASSPORT_CONTEXT::DoAuthenticate(
W3_MAIN_CONTEXT * pMainContext,
TOKEN_CACHE_ENTRY ** ppCachedToken,
STRU * pstrAuthUser,
STRU * pstrRemoteUser,
STRU & strDomainName
)
/*++
Routine Description:
Logon the passport user (i.e. get a token)
Arguments:
pMetaData - Metadata for this request
ppCachedToken - Filled with token cache entry represented mapped user
pstrAuthUser - Filled with AUTH_USER
pstrRemoteUser - Filled with PUID
strDomainName - Domain name
Return Value:
HRESULT
--*/
{
VARIANT vMemberId;
HRESULT hr;
WCHAR achLarge[ 64 ];
DWORD dwLogonError;
TOKEN_CACHE_ENTRY * pCachedToken = NULL;
LONG lLowPuid;
LONG lHighPuid;
BOOL fRet;
HANDLE hToken;
STACK_BUFFER( bufName, 512 );
DWORD cchName;
W3_METADATA * pMetaData;
if ( pMainContext == NULL ||
ppCachedToken == NULL ||
pstrAuthUser == NULL ||
pstrRemoteUser == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
DBG_ASSERT( pMetaData != NULL );
//
// Get the PUID -> this is the remote user name.
// Start with the high part
//
VariantInit( &vMemberId );
DBG_ASSERT( _pPassportManager != NULL );
hr = _pPassportManager->get_Profile( sm_bstrMemberIdHigh, &vMemberId );
if ( FAILED( hr ) )
{
goto Failure;
}
lHighPuid = V_I4( &vMemberId );
//
// Next the low part
//
hr = _pPassportManager->get_Profile( sm_bstrMemberIdLow, &vMemberId );
if ( FAILED( hr ) )
{
goto Failure;
}
lLowPuid = V_I4( &vMemberId );
//
// Now make a string out of the QuadPart
//
wsprintfW( achLarge,
L"%08X%08X",
lHighPuid,
lLowPuid );
//
// The REMOTE_USER server variable is always PUID@domain.
//
hr = pstrRemoteUser->Copy( achLarge );
if ( FAILED( hr ) )
{
goto Failure;
}
hr = pstrRemoteUser->Append( L"@" );
if ( FAILED( hr ) )
{
goto Failure;
}
hr = pstrRemoteUser->Append( strDomainName.QueryStr() );
if ( FAILED( hr ) )
{
goto Failure;
}
//
// Should we be doing mapping at all?
//
if ( pMetaData->QueryRequireMapping() == MD_PASSPORT_NO_MAPPING )
{
//
// No mapping. Just use anonymous.
//
hr = pMetaData->GetAndRefAnonymousToken( &pCachedToken );
if( FAILED( hr ) )
{
return hr;
}
if ( pCachedToken == NULL )
{
return HRESULT_FROM_WIN32( ERROR_LOGON_FAILURE );
}
pstrAuthUser->Reset();
*ppCachedToken = pCachedToken;
return NO_ERROR;
}
//
// If we got here then we must be doing mapping (trying it anyways)
//
//
// Get the cached token (in other words, call into LsaLogonUser() )
//
DBG_ASSERT( g_pW3Server->QueryTokenCache() != NULL );
hr = g_pW3Server->QueryTokenCache()->GetCachedToken(
pstrRemoteUser->QueryStr(),
L"",
L"",
( DWORD )IIS_LOGON_METHOD_PASSPORT,
FALSE,
FALSE,
pMainContext->QueryRequest()->
QueryRemoteSockAddress(),
&pCachedToken,
&dwLogonError );
if ( FAILED( hr ) )
{
goto Failure;
}
//
// Now, was mapping required or not??? (extra ??? for special effect)
//
if ( pCachedToken == NULL )
{
if ( pMetaData->QueryRequireMapping() == MD_PASSPORT_NEED_MAPPING )
{
//
// No mapping -> fail!
//
return HRESULT_FROM_WIN32( dwLogonError );
}
else
{
//
// We tried, we failed, we'll persevere!
//
hr = pMetaData->GetAndRefAnonymousToken( &pCachedToken );
if ( FAILED( hr ) )
{
return hr;
}
if ( pCachedToken == NULL )
{
return HRESULT_FROM_WIN32( ERROR_LOGON_FAILURE );
}
pstrAuthUser->Reset();
*ppCachedToken = pCachedToken;
return NO_ERROR;
}
}
//
// The AUTH_USER server variable is the account name if mapping worked, else empty string
//
//
// Get the token information by impersonating
// (we could just cache this info, but then again who is going
// to use this mapping feature anyways???)
//
hToken = pCachedToken->QueryImpersonationToken();
if ( hToken == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
fRet = ImpersonateLoggedOnUser( hToken );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
cchName = bufName.QuerySize() / sizeof( WCHAR );
fRet = GetUserNameExW( NameSamCompatible,
(WCHAR*) bufName.QueryPtr(),
&cchName );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
if ( hr == HRESULT_FROM_WIN32( ERROR_MORE_DATA ) )
{
DBG_ASSERT( cchName > bufName.QuerySize() / sizeof( WCHAR ) );
fRet = bufName.Resize( cchName * sizeof( WCHAR ) );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
else
{
fRet = GetUserNameExW( NameSamCompatible,
(WCHAR*) bufName.QueryPtr(),
&cchName );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
else
{
hr = NO_ERROR;
}
}
}
}
//
// Always revert
//
if ( !RevertToSelf() )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Failure;
}
//
// If we failed earlier, bail.
//
if ( FAILED( hr ) )
{
goto Failure;
}
hr = pstrAuthUser->Copy( (WCHAR*) bufName.QueryPtr() );
if ( FAILED( hr ) )
{
goto Failure;
}
if ( pCachedToken != NULL )
{
*ppCachedToken = pCachedToken;
}
return NO_ERROR;
Failure:
DBG_ASSERT( FAILED( hr ) );
if ( pCachedToken != NULL )
{
pCachedToken->DereferenceCacheEntry();
pCachedToken = NULL;
}
return hr;
}
HRESULT
PASSPORT_CONTEXT::OnChallenge(
STRU & strOriginalUrl
)
/*++
Routine Description:
Do a passport challenge
Arguments:
strOriginalUrl - Original URL
Return Value:
HRESULT
--*/
{
BSTR bstrOriginalUrl;
VARIANT vReturnURL;
VARIANT vTimeWindow;
VARIANT vForceLogin;
VARIANT vNoParam;
VARIANT vCoBrandTemplate;
VARIANT vSecureLevel;
HRESULT hr = NO_ERROR;
VariantInit( &vTimeWindow );
hr = _pPassportManager->GetCurrentConfig( sm_bstrTimeWindow,
&vTimeWindow );
if ( FAILED( hr ) )
{
vTimeWindow.vt = VT_I4;
vTimeWindow.lVal = 10000;
}
VariantInit( &vForceLogin );
hr = _pPassportManager->GetCurrentConfig( sm_bstrForceSignIn,
&vForceLogin );
if ( FAILED( hr ) )
{
vForceLogin.vt = VT_BOOL;
vForceLogin.boolVal = VARIANT_FALSE;
}
VariantInit( &vCoBrandTemplate );
hr = _pPassportManager->GetCurrentConfig( sm_bstrCoBrandTemplate,
&vCoBrandTemplate );
if ( FAILED( hr ) )
{
vCoBrandTemplate.vt = VT_ERROR;
vCoBrandTemplate.scode = DISP_E_PARAMNOTFOUND;
}
VariantInit( &vSecureLevel );
hr = _pPassportManager->GetCurrentConfig( sm_bstrSecureLevel,
&vSecureLevel );
if ( FAILED( hr ) )
{
vSecureLevel.vt = VT_I4;
vSecureLevel.lVal = 10;
}
//
// Make this a secure return URL if needed (lame)
//
if ( vSecureLevel.lVal > 0 )
{
if ( _wcsnicmp( strOriginalUrl.QueryStr(), L"https://", 8 ) != 0 )
{
STACK_STRU( strTemp, 256 );
//
// Must be a nonsecure url
//
DBG_ASSERT( _wcsnicmp( strOriginalUrl.QueryStr(), L"http://", 7 ) == 0 );
hr = strTemp.Copy( L"https://" );
if ( FAILED( hr ) )
{
return hr;
}
hr = strTemp.Append( strOriginalUrl.QueryStr() + 7 );
if ( FAILED( hr ) )
{
return hr;
}
bstrOriginalUrl = SysAllocString( strTemp.QueryStr() );
}
else
{
//
// Just use the original URL
//
bstrOriginalUrl = SysAllocString( strOriginalUrl.QueryStr() );
}
}
else
{
//
// Just use the original URL
//
bstrOriginalUrl = SysAllocString( strOriginalUrl.QueryStr() );
}
if ( bstrOriginalUrl == NULL )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
VariantInit( &vNoParam );
VariantInit( &vReturnURL );
vReturnURL.vt = VT_BSTR;
vReturnURL.bstrVal = bstrOriginalUrl;
vNoParam.vt = VT_ERROR;
vNoParam.scode = DISP_E_PARAMNOTFOUND;
hr = _pPassportManager->LoginUser( vReturnURL,
vTimeWindow,
vForceLogin,
vCoBrandTemplate,
vNoParam,
vNoParam,
vNoParam,
vSecureLevel,
vNoParam );
if ( FAILED( hr ) )
{
goto Finished;
}
Finished:
VariantClear( &vReturnURL );
return hr;
}
HRESULT
PASSPORT_AUTH_PROVIDER::Initialize(
DWORD dwInternalId
)
/*++
Routine Description:
Initialize passport authentication provider
Arguments:
None
Return Value:
HRESULT
--*/
{
SetInternalId( dwInternalId );
//
// We defer initialization of passport manager crap until we really
// need it. Why? Because loading their DLLs causes a process-wide
// perf hit with string compares. This hit is killing ASP perf
//
if( !INITIALIZE_CRITICAL_SECTION( &_csInitLock ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
_fInitialized = FALSE;
return NO_ERROR;
}
VOID
PASSPORT_AUTH_PROVIDER::Terminate(
VOID
)
/*++
Routine Description:
Terminate passport authentication provider
Arguments:
None
Return Value:
None
--*/
{
if ( _fInitialized )
{
PASSPORT_CONTEXT::Terminate();
_fInitialized = FALSE;
}
DeleteCriticalSection( &_csInitLock );
}
HRESULT
PASSPORT_AUTH_PROVIDER::DoesApply(
W3_MAIN_CONTEXT * pMainContext,
BOOL * pfApplies
)
/*++
Routine Description:
Check whether Passport applies to this request
Arguments:
pMainContext - Main context
pfApplies - Set to TRUE if one of the filters indicates the request applies
Return Value:
HRESULT
--*/
{
URL_CONTEXT * pUrlContext;
W3_METADATA * pMetaData;
PASSPORT_CONTEXT * pPassportContext;
W3_FILTER_CONTEXT * pFilterContext;
HRESULT hr = S_OK;
STACK_STRA( strReturnCookie, 256 );
BOOL fTweenerHandled = FALSE;
if ( pMainContext == NULL ||
pfApplies == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfApplies = FALSE;
//
// Before we call into filters, check whether custom auth is enabled.
// This is a departure from the other protocols, but is the practical
// thing to do, since we don't want to call into arbitrary code (or
// Passport Manager) on every request.
//
pUrlContext = pMainContext->QueryUrlContext();
DBG_ASSERT( pUrlContext != NULL );
pMetaData = pUrlContext->QueryMetaData();
DBG_ASSERT( pMetaData != NULL );
if ( !pMetaData->QueryAuthTypeSupported( MD_AUTH_PASSPORT ) )
{
return NO_ERROR;
}
//
// Ok. We need to do passport stuff. Initialize the passport stuff
// now if needed
//
if ( !_fInitialized )
{
EnterCriticalSection( &_csInitLock );
if ( !_fInitialized )
{
hr = PASSPORT_CONTEXT::Initialize();
if ( SUCCEEDED( hr ) )
{
_fInitialized = TRUE;
}
}
LeaveCriticalSection( &_csInitLock );
}
if ( !_fInitialized )
{
DBG_ASSERT( FAILED( hr ) );
return hr;
}
//
// Get a filter context since we'll need it now to ask passport manager
// whether the current request applies
//
pFilterContext = pMainContext->QueryFilterContext();
if ( pFilterContext == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
//
// Create a passport manager
//
pPassportContext = new (pMainContext) PASSPORT_CONTEXT;
if ( pPassportContext == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
hr = pPassportContext->Create( pFilterContext );
if ( FAILED( hr ) )
{
delete pPassportContext;
return hr;
}
pMainContext->SetContextState( pPassportContext );
//
// OK. Do some weird Tweener crap. Check the request for magic and if
// we see it, we avoid Passport Manager altogether
//
hr = DoTweenerSpecialCase( pMainContext,
&fTweenerHandled );
if ( FAILED( hr ) )
{
return hr;
}
if ( fTweenerHandled )
{
//
// We have done our thing. Just return success. We'll let the
// DoAuthenticate send the response
//
*pfApplies = TRUE;
pPassportContext->SetTweener( TRUE );
return NO_ERROR;
}
//
// Does this request look destined for passport?
//
hr = pPassportContext->DoesApply( pFilterContext->QueryFC(),
pfApplies,
&strReturnCookie );
if ( FAILED( hr ) )
{
return hr;
}
//
//
// If a cookie was set, add it to the response
//
if ( !strReturnCookie.IsEmpty() )
{
hr = pFilterContext->AddResponseHeaders( strReturnCookie.QueryStr() );
if ( FAILED( hr ) )
{
return hr;
}
}
return NO_ERROR;
}
HRESULT
PASSPORT_AUTH_PROVIDER::DoAuthenticate(
W3_MAIN_CONTEXT * pMainContext,
BOOL * pfFilterFinished
)
/*++
Routine Description:
Allows filter which applies to actually authenticate the request
Arguments:
pMainContext - Main context
pfFilterFinished - Set to TRUE if filter wants out
Return Value:
HRESULT
--*/
{
W3_METADATA * pMetaData;
URL_CONTEXT * pUrlContext;
TOKEN_CACHE_ENTRY * pToken = NULL;
PASSPORT_USER_CONTEXT * pUserContext;
HRESULT hr;
PASSPORT_CONTEXT * pPassportContext;
W3_RESPONSE * pResponse;
STACK_STRU( strAuthUser, 256 );
STACK_STRU( strRemoteUser, 256 );
STACK_STRU( strDomainName, 256 );
if ( pMainContext == NULL ||
pfFilterFinished == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfFilterFinished = FALSE;
//
// We must be initialized!
//
DBG_ASSERT( _fInitialized );
//
// We must be supported by metadata
//
pUrlContext = pMainContext->QueryUrlContext();
DBG_ASSERT( pUrlContext != NULL );
pMetaData = pUrlContext->QueryMetaData();
DBG_ASSERT( pMetaData != NULL );
DBG_ASSERT( pMetaData->QueryAuthTypeSupported( MD_AUTH_PASSPORT ) );
//
// Get the saved passport state, we better be able to find it!
//
pPassportContext = (PASSPORT_CONTEXT*) pMainContext->QueryContextState();
DBG_ASSERT( pPassportContext != NULL );
//
// Before we go any further, check whether we've already Tweenerized this
// request. If we have, the response is already setup. Just bail
//
if ( pPassportContext->QueryIsTweener() )
{
return NO_ERROR;
}
//
// Choose a domain for the logon. If the metabase domain is set, use it,
// otherwise choose the default domain name
//
if ( pMetaData->QueryDomainName() == NULL ||
pMetaData->QueryDomainName()[ 0 ] == L'\0' )
{
//
// If we're a member of a domain, use that domain name
//
if ( W3_STATE_AUTHENTICATION::QueryIsDomainMember() )
{
hr = strDomainName.Copy( W3_STATE_AUTHENTICATION::QueryMemberDomainName() );
}
else
{
hr = strDomainName.Copy( W3_STATE_AUTHENTICATION::QueryDefaultDomainName() );
}
if ( FAILED( hr ) )
{
return hr;
}
}
else
{
//
// Use the metabase domain name
//
hr = strDomainName.Copy( pMetaData->QueryDomainName() );
if ( FAILED( hr ) )
{
return hr;
}
}
//
// Lets authenticate
//
hr = pPassportContext->DoAuthenticate( pMainContext,
&pToken,
&strAuthUser,
&strRemoteUser,
strDomainName );
if ( FAILED( hr ) )
{
//
// Setup the 401 response
//
DBG_ASSERT( pToken == NULL );
pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
Http401BadLogon );
return NO_ERROR;
}
//
// Create a user context
//
pUserContext = new PASSPORT_USER_CONTEXT( this );
if ( pUserContext == NULL )
{
pToken->DereferenceCacheEntry();
pToken = NULL;
return HRESULT_FROM_WIN32( GetLastError() );
}
hr = pUserContext->Create( pToken,
strAuthUser,
strRemoteUser );
if ( FAILED( hr ) )
{
pToken->DereferenceCacheEntry();
pToken = NULL;
pUserContext->DereferenceUserContext();
pUserContext = NULL;
}
pMainContext->SetUserContext( pUserContext );
return NO_ERROR;
}
HRESULT
PASSPORT_AUTH_PROVIDER::EscapeAmpersands(
STRA & strUrl
)
/*++
Routine Description:
Sigh. A special function to escape ampersands so passport is happy
Arguments:
strUrl - String to escape
Return Value:
HRESULT
--*/
{
STACK_STRA( strTemp, 256 );
HRESULT hr;
CHAR * pszCursor;
CHAR * pszAmpersand;
pszCursor = strUrl.QueryStr();
pszAmpersand = strchr( pszCursor, '&' );
while ( pszAmpersand != NULL )
{
hr = strTemp.Append( pszCursor, DIFF( pszAmpersand - pszCursor ) );
if ( FAILED( hr ) )
{
return hr;
}
hr = strTemp.Append( "%26" );
if ( FAILED( hr ) )
{
return hr;
}
pszCursor = pszAmpersand + 1;
pszAmpersand = strchr( pszCursor, '&' );
}
hr = strTemp.Append( pszCursor );
if ( FAILED( hr ) )
{
return hr;
}
return strUrl.Copy( strTemp );
}
HRESULT
PASSPORT_AUTH_PROVIDER::OnAccessDenied(
W3_MAIN_CONTEXT * pMainContext
)
/*++
Routine Description:
If we are logged on, present an acecss denied page. Otherwise,
present the redirect
Arguments:
pMainContext - Main context
Return Value:
HRESULT
--*/
{
W3_METADATA * pMetaData;
URL_CONTEXT * pUrlContext;
HRESULT hr = S_OK;
PASSPORT_CONTEXT * pPassportContext;
STACK_STRU( strRedirect, 256 );
STACK_STRA( strReturnUrl, 256 );
STACK_STRU( strUnicodeReturnUrl, 256 );
W3_FILTER_CONTEXT * pFilterContext = NULL;
STACK_STRA( strRawUrl, 256 );
W3_RESPONSE * pResponse;
STACK_STRA( strAuthenticateHeader, 256 );
if ( pMainContext == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
//
// We must be supported by metadata
//
pUrlContext = pMainContext->QueryUrlContext();
DBG_ASSERT( pUrlContext != NULL );
pMetaData = pUrlContext->QueryMetaData();
DBG_ASSERT( pMetaData != NULL );
DBG_ASSERT( pMetaData->QueryAuthTypeSupported( MD_AUTH_PASSPORT ) );
//
// Sigh. Have to check whether a passport challenge is already
// setup. If so, just NOP
//
pResponse = pMainContext->QueryResponse();
DBG_ASSERT( pResponse != NULL );
hr = pResponse->GetHeader( "WWW-Authenticate", &strAuthenticateHeader );
if ( SUCCEEDED( hr ) )
{
if ( _strnicmp( strAuthenticateHeader.QueryStr(),
"Passport1.4 ",
12 ) == 0 )
{
//
// Challenge already there. Just NOP
//
return NO_ERROR;
}
}
//
// In some cases (too complicated to explain), we might actually get
// called on AccessDenied() before anything else. Therefore we do the
// same deferred init here
//
if ( !_fInitialized )
{
EnterCriticalSection( &_csInitLock );
if ( !_fInitialized )
{
hr = PASSPORT_CONTEXT::Initialize();
if ( SUCCEEDED( hr ) )
{
_fInitialized = TRUE;
}
}
LeaveCriticalSection( &_csInitLock );
}
if ( !_fInitialized )
{
DBG_ASSERT( FAILED( hr ) );
return hr;
}
//
// Get the saved passport state, we better be able to find it!
//
pPassportContext = (PASSPORT_CONTEXT*) pMainContext->QueryContextState( CONTEXT_STATE_AUTHENTICATION );
if ( pPassportContext == NULL )
{
pFilterContext = pMainContext->QueryFilterContext();
if ( pFilterContext == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
//
// Build a context now
//
pPassportContext = new (pMainContext) PASSPORT_CONTEXT;
if ( pPassportContext == NULL )
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
hr = pPassportContext->Create( pFilterContext );
if ( FAILED( hr ) )
{
delete pPassportContext;
return hr;
}
pMainContext->SetContextState( pPassportContext );
}
//
// If we have authenticated, then we'll want to send a local redirect
// to an access denied page. Otherwise, we'll send a 302 redirect
//
if ( !pPassportContext->QueryIsAuthenticated() )
{
//
// Ok. Before we ask PassportManager for the authentication URL
// to redirect to, first check whether the client just cancelled. If
// they did we will redirect to the default page.
//
if ( pPassportContext->QueryUserError() )
{
BOOL fDidRedirect = FALSE;
hr = pPassportContext->SetupDefaultRedirect( pMainContext, &fDidRedirect );
if ( FAILED( hr ) || fDidRedirect )
{
return hr;
}
//
// If we're here, then there was no DefaultRedirect configured in passport
//
// Just set the passport forbidden error
//
pMainContext->QueryResponse()->SetStatus( HttpStatusForbidden,
Http403PassportLoginFailure );
return NO_ERROR;
}
//
// We want a URL without the extra :80
//
hr = pMainContext->QueryRequest()->GetRawUrl( &strRawUrl );
if ( FAILED( hr ) )
{
return hr;
}
hr = pMainContext->QueryRequest()->BuildFullUrl( strRawUrl,
&strReturnUrl,
FALSE );
if ( FAILED( hr ) )
{
return hr;
}
hr = strReturnUrl.Escape();
if ( FAILED( hr ) )
{
return hr;
}
hr = EscapeAmpersands( strReturnUrl );
if ( FAILED( hr ) )
{
return hr;
}
hr = strUnicodeReturnUrl.CopyA( strReturnUrl.QueryStr() );
if ( FAILED( hr ) )
{
return hr;
}
hr = pPassportContext->OnChallenge( strUnicodeReturnUrl );
if ( FAILED( hr ) )
{
return hr;
}
}
return NO_ERROR;
}
HRESULT
PASSPORT_AUTH_PROVIDER::DoTweenerSpecialCase(
W3_MAIN_CONTEXT * pMainContext,
BOOL * pfTweenerHandled
)
/*++
Routine Description:
Handle Tweener crap
Arguments:
pMainContext - Main context
pfTweenerHandled - Set to TRUE if tweener crap was done
Return Value:
HRESULT
--*/
{
STACK_STRU( strQueryString, 512 );
STACK_STRA( strTweenerUrl, 512 );
HRESULT hr;
W3_RESPONSE * pResponse;
W3_REQUEST * pRequest;
STACK_STRA( strAuthenticateHeader, 512 );
CHAR * pszAuthenticateHeader = NULL;
CHAR * pszStupid;
CHAR * pszBizarre;
CHAR * pszAcceptAuth;
WCHAR * pszTweenerMagic;
STACK_STRA( strAcceptAuth, 32 );
BOOL fSendRedirect = TRUE;
if ( pMainContext == NULL ||
pfTweenerHandled == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
*pfTweenerHandled = FALSE;
pResponse = pMainContext->QueryResponse();
DBG_ASSERT( pResponse != NULL );
pRequest = pMainContext->QueryRequest();
DBG_ASSERT( pRequest != NULL );
//
// First check for a query string, if there isn't one, we're done
//
hr = pMainContext->QueryRequest()->GetQueryString( &strQueryString );
if ( FAILED( hr ) )
{
return hr;
}
if ( strQueryString.QueryStr()[ 0 ] == L'\0' )
{
//
// No query string. We're done.
//
return NO_ERROR;
}
//
// Check the query string for the magic "msppchlg=1&mspplogin=" string
//
pszTweenerMagic = wcsstr( strQueryString.QueryStr(), MAGIC_TWEENER_STRING );
if ( pszTweenerMagic == NULL )
{
return NO_ERROR;
}
//
// The next part of the string should be the URL
//
hr = strTweenerUrl.CopyWToUTF8Unescaped( pszTweenerMagic + MAGIC_TWEENER_STRING_LEN );
if ( FAILED( hr ) )
{
return hr;
}
strTweenerUrl.Unescape();
//
// Start generating the response
//
hr = pResponse->SetHeaderByReference( HttpHeaderContentType,
"text/html",
9 );
if ( FAILED( hr ) )
{
return hr;
}
hr = pResponse->SetHeader( HttpHeaderLocation,
strTweenerUrl.QueryStr(),
(USHORT)strTweenerUrl.QueryCCH() );
if ( FAILED( hr ) )
{
return hr;
}
//
// Calculate the authenticate header. This is the query string with
// in the tweener URL
//
pszAuthenticateHeader = strchr( strTweenerUrl.QueryStr(), '?' );
if ( pszAuthenticateHeader == NULL )
{
return NO_ERROR;
}
else
{
pszAuthenticateHeader++;
}
hr = strAuthenticateHeader.Copy( "Passport1.4 " );
if ( FAILED( hr ) )
{
return hr;
}
hr = strAuthenticateHeader.Append( pszAuthenticateHeader );
if ( FAILED( hr ) )
{
return hr;
}
pszBizarre = strchr( strAuthenticateHeader.QueryStr(), '&' );
while ( pszBizarre != NULL )
{
*pszBizarre = ',';
pszBizarre++;
pszBizarre = strchr( pszBizarre, '&' );
}
hr = pResponse->SetHeader( "WWW-Authenticate",
16,
strAuthenticateHeader.QueryStr(),
(USHORT)strAuthenticateHeader.QueryCCH() );
if ( FAILED( hr ) )
{
return hr;
}
//
// Finally, if this client supports it, return a 401, else a 302
//
pszAcceptAuth = pRequest->GetHeader( "Accept-Auth" );
if ( pszAcceptAuth != NULL )
{
hr = strAcceptAuth.Copy( pszAcceptAuth );
if ( FAILED( hr ) )
{
return hr;
}
_strupr( strAcceptAuth.QueryStr() );
if ( strstr( strAcceptAuth.QueryStr(), "PASSPORT1.4" ) != NULL )
{
fSendRedirect = FALSE;
}
}
if ( fSendRedirect )
{
pResponse->SetStatus( HttpStatusRedirect );
}
else
{
pResponse->SetStatus( HttpStatusUnauthorized,
Http401BadLogon );
}
*pfTweenerHandled = TRUE;
return NO_ERROR;
}
HRESULT
PASSPORT_USER_CONTEXT::Create(
TOKEN_CACHE_ENTRY * pToken,
STRU & strAuthUser,
STRU & strRemoteUser
)
/*++
Routine Description:
Create a user context based off token
Arguments:
pToken - Cached token
strAuthUser - AUTH_USER
strRemoteUser - REMOTE_USER
Return Value:
HRESULT
--*/
{
HRESULT hr;
if ( pToken == NULL )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
}
hr = _strAuthUser.Copy( strAuthUser );
if ( FAILED( hr ) )
{
return hr;
}
hr = _strRemoteUser.Copy( strRemoteUser );
if ( FAILED( hr ) )
{
return hr;
}
_pToken = pToken;
return NO_ERROR;
}
HANDLE
PASSPORT_USER_CONTEXT::QueryPrimaryToken(
VOID
)
/*++
Routine Description:
Get primary token for this user
Arguments:
None
Return Value:
Token handle
--*/
{
DBG_ASSERT( _pToken != NULL );
return _pToken->QueryPrimaryToken();
}
HANDLE
PASSPORT_USER_CONTEXT::QueryImpersonationToken(
VOID
)
/*++
Routine Description:
Get impersonation token for this user
Arguments:
None
Return Value:
Token handle
--*/
{
DBG_ASSERT( _pToken != NULL );
return _pToken->QueryImpersonationToken();
}