|
|
/*++
Copyright (c) 2000 Microsoft Corporation Module Name: authstate.cxx
Abstract: Authenticate state implementation (and authentication utilities)
Author: Ming Lu ( MingLu ) 2-Feb-2000
Environment: Win32 User Mode
Revision History:
--*/
#include "precomp.hxx"
#include "sspiprovider.hxx"
#include "digestprovider.hxx"
#include "iisdigestprovider.hxx"
#include "basicprovider.hxx"
#include "anonymousprovider.hxx"
#include "certmapprovider.hxx"
#include "iiscertmapprovider.hxx"
#include "customprovider.hxx"
W3_STATE_AUTHENTICATION * W3_STATE_AUTHENTICATION::sm_pAuthState; LUID W3_STATE_AUTHENTICATION::sm_BackupPrivilegeTcbValue; PTOKEN_PRIVILEGES W3_STATE_AUTHENTICATION::sm_pTokenPrivilege = NULL; PTRACE_LOG W3_USER_CONTEXT::sm_pTraceLog; PTRACE_LOG CONNECTION_AUTH_CONTEXT::sm_pTraceLog;
HRESULT W3_STATE_AUTHENTICATION::GetDefaultDomainName( VOID ) /*++
Description: Fills in the member variable with the name of the default domain to use for logon validation
Arguments:
szDefaultDomainName - Buffer to hold the default domain name
Returns: HRESULT
--*/ { OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS NtStatus; DWORD dwLength; DWORD err = 0; LSA_HANDLE LsaPolicyHandle = NULL; PPOLICY_ACCOUNT_DOMAIN_INFO pAcctDomainInfo = NULL; PPOLICY_PRIMARY_DOMAIN_INFO pPrimaryDomainInfo = NULL; HRESULT hr = S_OK;
//
// Open a handle to the local machine's LSA policy object.
//
InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL );
NtStatus = LsaOpenPolicy( NULL, &ObjectAttributes, POLICY_EXECUTE, &LsaPolicyHandle );
if( !NT_SUCCESS( NtStatus ) ) { DBGPRINTF(( DBG_CONTEXT, "cannot open lsa policy, error %08lX\n", NtStatus ));
err = LsaNtStatusToWinError( NtStatus );
//
// Failure LsaOpenPolicy() does not guarantee that
// LsaPolicyHandle was not touched.
//
LsaPolicyHandle = NULL;
goto Cleanup; }
//
// Query the account domain information from the policy object.
//
NtStatus = LsaQueryInformationPolicy( LsaPolicyHandle, PolicyAccountDomainInformation, (PVOID *)&pAcctDomainInfo );
if( !NT_SUCCESS( NtStatus ) ) {
DBGPRINTF(( DBG_CONTEXT, "cannot query lsa policy info, error %08lX\n", NtStatus ));
err = LsaNtStatusToWinError( NtStatus ); goto Cleanup; }
DBG_ASSERT( pAcctDomainInfo != NULL ); dwLength = pAcctDomainInfo->DomainName.Length / sizeof( WCHAR );
wcsncpy( _achDefaultDomainName, (LPCWSTR)pAcctDomainInfo->DomainName.Buffer, sizeof( _achDefaultDomainName ) / sizeof( WCHAR ) );
_achDefaultDomainName[ dwLength ] = L'\0';
//
// Query the primary domain information from the policy object.
//
NtStatus = LsaQueryInformationPolicy( LsaPolicyHandle, PolicyPrimaryDomainInformation, (PVOID *)&pPrimaryDomainInfo );
if( !NT_SUCCESS( NtStatus ) ) {
DBGPRINTF(( DBG_CONTEXT, "cannot query lsa policy info, error %08lX\n", NtStatus ));
err = LsaNtStatusToWinError( NtStatus ); goto Cleanup; }
DBG_ASSERT( pPrimaryDomainInfo != NULL ); if( pPrimaryDomainInfo->Sid ) { //
// We are a domain member
//
_fIsDomainMember = TRUE; } else { _fIsDomainMember = FALSE; }
//
// Success!
//
DBG_ASSERT( err == 0 );
Cleanup:
if( pAcctDomainInfo != NULL ) { LsaFreeMemory( (PVOID)pAcctDomainInfo ); pAcctDomainInfo = NULL; }
if( pPrimaryDomainInfo != NULL ) { LsaFreeMemory( (PVOID)pPrimaryDomainInfo ); pPrimaryDomainInfo = NULL; }
if( LsaPolicyHandle != NULL ) { LsaClose( LsaPolicyHandle ); }
if ( err ) { hr = HRESULT_FROM_WIN32( err ); }
return hr; };
//static
HRESULT W3_STATE_AUTHENTICATION::SplitUserDomain( STRU & strUserDomain, STRU * pstrUserName, STRU * pstrDomainName, WCHAR * pszDefaultDomain, BOOL * pfPossibleUPNLogon ) /*++
Description: Split the input user name into user/domain.
Arguments:
strUserDomain - Combined domain\username (not altered) pstrUserName - Filled with user name only pstrDomainName - Filled with domain name (either embedded in *pstrUserName,or from metabase/computer domain name) pszDefaultDomain - Default domain specified in metabase pfPossibleUPNLogon - TRUE if we may need to do UNP logon, otherwise FALSE
Returns: HRESULT
--*/ { WCHAR * pszUserName; W3_METADATA * pMetaData = NULL; WCHAR * pszDomain; DWORD cbDomain; HRESULT hr; if ( pstrUserName == NULL || pstrDomainName == NULL || pfPossibleUPNLogon == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } pszUserName = wcspbrk( strUserDomain.QueryStr(), L"/\\" ); if ( pszUserName == NULL ) { //
// No domain in the user name. First try the metabase domain
// name
//
pszDomain = pszDefaultDomain; if ( pszDomain == NULL || *pszDomain == L'\0' ) { //
// No metabase domain, use default domain name
//
pszDomain = QueryDefaultDomainName(); DBG_ASSERT( pszDomain != NULL ); } pszUserName = strUserDomain.QueryStr(); hr = pstrDomainName->Copy( pszDomain ); if ( FAILED( hr ) ) { return hr; }
*pfPossibleUPNLogon = TRUE; } else { cbDomain = DIFF( pszUserName - strUserDomain.QueryStr() ); if( cbDomain == 0 ) { hr = pstrDomainName->Copy( L"." ); } else { hr = pstrDomainName->Copy( strUserDomain.QueryStr(), cbDomain ); }
if ( FAILED( hr ) ) { return hr; }
pszUserName = pszUserName + 1;
*pfPossibleUPNLogon = FALSE; } hr = pstrUserName->Copy( pszUserName ); if ( FAILED( hr ) ) { return hr; } return NO_ERROR; }
HRESULT W3_STATE_AUTHENTICATION::OnAccessDenied( W3_MAIN_CONTEXT * pMainContext ) /*++
Description: Called when a resource is access denied. This routines will call all authentication providers so that they may add authentication headers, etc.
Arguments:
pMainContext - main context
Returns: HRESULT
--*/ { AUTH_PROVIDER * pProvider; DWORD cProviderCount = 0; W3_METADATA * pMetaData; HRESULT hr = NO_ERROR; if ( pMainContext == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( GetLastError() ); } pMetaData = pMainContext->QueryUrlContext()->QueryMetaData(); DBG_ASSERT( pMetaData != NULL );
//
// Loop thru all authentication providers
//
for ( cProviderCount = 0; ; cProviderCount++ ) { pProvider = _rgAuthProviders[ cProviderCount ]; if ( pProvider == NULL ) { break; } //
// Only call OnAccessDenied() if the authentication provider is
// supported for the given metadata of the denied request
//
if ( !pMetaData->QueryAuthTypeSupported( pProvider->QueryAuthType() ) ) { continue; } hr = pProvider->OnAccessDenied( pMainContext ); if ( FAILED( hr ) ) { break; } } return hr; }
VOID W3_STATE_AUTHENTICATION::GetSSPTokenPrivilege( VOID ) /*++
Description: Prepare an appropriate token privilege used to adjust the SSP impersonation token privilege in order to work around the problem introduced by using FILE_FLAG_BACKUP_SEMANTICS in CreateFileW call in W3_FILE_INFO::OpenFile.
Arguments:
None.
Returns: None.
--*/ { sm_pTokenPrivilege = ( PTOKEN_PRIVILEGES )LocalAlloc( LMEM_FIXED, sizeof( TOKEN_PRIVILEGES ) + sizeof( LUID_AND_ATTRIBUTES )); if ( sm_pTokenPrivilege != NULL ) { if ( !LookupPrivilegeValue( NULL, L"SeBackupPrivilege", &sm_BackupPrivilegeTcbValue ) ) { sm_pTokenPrivilege->PrivilegeCount = 0; } else { //
// Set attributes to disable SeBackupPrivilege for SSP
// impersonation token
//
sm_pTokenPrivilege->PrivilegeCount = 1;
sm_pTokenPrivilege->Privileges[0].Luid = sm_BackupPrivilegeTcbValue; sm_pTokenPrivilege->Privileges[0].Attributes = 0; } } } W3_STATE_AUTHENTICATION::W3_STATE_AUTHENTICATION() { _pAnonymousProvider = NULL; _pCustomProvider = NULL; _fHasAssociatedUserBefore = FALSE; //
// Initialize token privilege for SSP impersionation token
//
GetSSPTokenPrivilege(); //
// Figure out the default domain name once
//
_hr = GetDefaultDomainName(); if ( FAILED( _hr ) ) { return; } //
// Initialize all the authentication providers
//
ZeroMemory( _rgAuthProviders, sizeof( _rgAuthProviders ) ); _hr = InitializeAuthenticationProviders(); if ( FAILED( _hr ) ) { return; } _cbContextSize = sizeof( SSPI_CONTEXT_STATE ) + sizeof( ANONYMOUS_USER_CONTEXT );
//
// Initialize reverse DNS service
//
if (!InitRDns()) { _hr = HRESULT_FROM_WIN32(GetLastError());
DBGPRINTF(( DBG_CONTEXT, "Error initializing RDns service. hr = 0x%x\n", _hr )); TerminateAuthenticationProviders(); return; }
//
// Initialize the W3_USER_CONTEXT reftrace log
//
#if DBG
W3_USER_CONTEXT::sm_pTraceLog = CreateRefTraceLog( 2000, 0 ); #else
W3_USER_CONTEXT::sm_pTraceLog = NULL; #endif
//
// Store a pointer to the singleton (no C++ goo used in creating
// this singleton)
//
if ( sm_pAuthState != NULL ) { DBG_ASSERT( sm_pAuthState != NULL ); _hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } else { sm_pAuthState = this; } }
W3_STATE_AUTHENTICATION::~W3_STATE_AUTHENTICATION() { if ( W3_USER_CONTEXT::sm_pTraceLog != NULL ) { DestroyRefTraceLog( W3_USER_CONTEXT::sm_pTraceLog ); W3_USER_CONTEXT::sm_pTraceLog = NULL; }
TerminateRDns(); TerminateAuthenticationProviders(); if (sm_pTokenPrivilege != NULL) { LocalFree(sm_pTokenPrivilege); sm_pTokenPrivilege = NULL; }
sm_pAuthState = NULL; }
HRESULT W3_STATE_AUTHENTICATION::InitializeAuthenticationProviders( VOID ) /*++
Routine Description:
Initialize all authentication providers
Arguments:
None Return Value:
HRESULT
--*/ { HRESULT hr = NO_ERROR; DWORD cProviderCount = 0;
//
// Initialize trace for connection contexts
//
hr = CONNECTION_AUTH_CONTEXT::Initialize(); if ( FAILED( hr ) ) { goto Failure; }
//
// Certificate map provider. This must be the first !!!!!!
//
DBG_ASSERT( cProviderCount < AUTH_PROVIDER_COUNT ); _rgAuthProviders[ cProviderCount ] = new CERTMAP_AUTH_PROVIDER; if ( _rgAuthProviders[ cProviderCount ] == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; } hr = _rgAuthProviders[ cProviderCount ]->Initialize( cProviderCount ); if ( FAILED( hr ) ) { delete _rgAuthProviders[ cProviderCount ]; _rgAuthProviders[ cProviderCount ] = NULL; goto Failure; } cProviderCount++;
DBG_ASSERT( cProviderCount < AUTH_PROVIDER_COUNT ); _rgAuthProviders[ cProviderCount ] = new IISCERTMAP_AUTH_PROVIDER; if ( _rgAuthProviders[ cProviderCount ] == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; } hr = _rgAuthProviders[ cProviderCount ]->Initialize( cProviderCount ); if ( FAILED( hr ) ) { delete _rgAuthProviders[ cProviderCount ]; _rgAuthProviders[ cProviderCount ] = NULL; goto Failure; } cProviderCount++;
//
// SSPI provider
//
DBG_ASSERT( cProviderCount < AUTH_PROVIDER_COUNT ); _rgAuthProviders[ cProviderCount ] = new SSPI_AUTH_PROVIDER( MD_AUTH_NT ); if ( _rgAuthProviders[ cProviderCount ] == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; } hr = _rgAuthProviders[ cProviderCount ]->Initialize( cProviderCount ); if ( FAILED( hr ) ) { delete _rgAuthProviders[ cProviderCount ]; _rgAuthProviders[ cProviderCount ] = NULL; goto Failure; } cProviderCount++;
//
// Digest provider
//
DBG_ASSERT( cProviderCount < AUTH_PROVIDER_COUNT ); _rgAuthProviders[ cProviderCount ] = new DIGEST_AUTH_PROVIDER( MD_AUTH_MD5 ); if ( _rgAuthProviders[ cProviderCount ] == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; } hr = _rgAuthProviders[ cProviderCount ]->Initialize( cProviderCount ); if ( FAILED( hr ) ) { delete _rgAuthProviders[ cProviderCount ]; _rgAuthProviders[ cProviderCount ] = NULL; goto Failure; } cProviderCount++;
//
// IIS Digest provider (for backward compatibility)
//
DBG_ASSERT( cProviderCount < AUTH_PROVIDER_COUNT ); _rgAuthProviders[ cProviderCount ] = new IIS_DIGEST_AUTH_PROVIDER(); if ( _rgAuthProviders[ cProviderCount ] == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; } hr = _rgAuthProviders[ cProviderCount ]->Initialize( cProviderCount ); if ( FAILED( hr ) ) { delete _rgAuthProviders[ cProviderCount ]; _rgAuthProviders[ cProviderCount ] = NULL; goto Failure; } cProviderCount++; //
// Basic provider
//
DBG_ASSERT( cProviderCount < AUTH_PROVIDER_COUNT ); _rgAuthProviders[ cProviderCount ] = new BASIC_AUTH_PROVIDER; if ( _rgAuthProviders[ cProviderCount ] == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; } hr = _rgAuthProviders[ cProviderCount ]->Initialize( cProviderCount ); if ( FAILED( hr ) ) { delete _rgAuthProviders[ cProviderCount ]; _rgAuthProviders[ cProviderCount ] = NULL; goto Failure; } cProviderCount++; //
// Anonymous provider.
//
// Note: This one should always be the last one
//
DBG_ASSERT( cProviderCount < AUTH_PROVIDER_COUNT ); _rgAuthProviders[ cProviderCount ] = new ANONYMOUS_AUTH_PROVIDER; if ( _rgAuthProviders[ cProviderCount ] == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; } hr = _rgAuthProviders[ cProviderCount ]->Initialize( cProviderCount ); if ( FAILED( hr ) ) { delete _rgAuthProviders[ cProviderCount ]; _rgAuthProviders[ cProviderCount ] = NULL; goto Failure; } _pAnonymousProvider = _rgAuthProviders[ cProviderCount ]; cProviderCount++;
//
// Custom provider. Not really a provider in the sense that it does not
// participate in authenticating a request. Instead, it is just used
// as a stub provider for custom authentication done with
// HSE_REQ_EXEC_URL
//
_pCustomProvider = new CUSTOM_AUTH_PROVIDER; if ( _pCustomProvider == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failure; }
return NO_ERROR; Failure: for ( DWORD i = 0; i < AUTH_PROVIDER_COUNT; i++ ) { if ( _rgAuthProviders[ i ] != NULL ) { _rgAuthProviders[ i ]->Terminate(); delete _rgAuthProviders[ i ]; _rgAuthProviders[ i ] = NULL; } } CONNECTION_AUTH_CONTEXT::Terminate(); return hr; }
VOID W3_STATE_AUTHENTICATION::TerminateAuthenticationProviders( VOID ) /*++
Routine Description:
Terminate all authentication providers
Arguments:
None Return Value:
None
--*/ { for ( DWORD i = 0; i < AUTH_PROVIDER_COUNT; i++ ) { if ( _rgAuthProviders[ i ] != NULL ) { _rgAuthProviders[ i ]->Terminate(); delete _rgAuthProviders[ i ]; _rgAuthProviders[ i ] = NULL; } } if ( _pCustomProvider != NULL ) { delete _pCustomProvider; _pCustomProvider = NULL; } CONNECTION_AUTH_CONTEXT::Terminate(); }
CONTEXT_STATUS W3_STATE_AUTHENTICATION::DoWork( W3_MAIN_CONTEXT * pMainContext, DWORD cbCompletion, DWORD dwCompletionStatus ) /*++
Routine Description:
Handle authentication for this request
Arguments:
pMainContext - W3_MAIN_CONTEXT representing execution of state machine cbCompletion - Number of bytes in an async completion dwCompletionStatus - Error status of a completion Return Value:
CONTEXT_STATUS_CONTINUE - if we should continue in state machine else stop executing the machine and free up the current thread
--*/ { DWORD cProviderCount = 0; AUTH_PROVIDER * pProvider = NULL; W3_METADATA * pMetaData = NULL; W3_USER_CONTEXT * pUserContext = NULL; URL_CONTEXT * pUrlContext = NULL; BOOL fSupported = FALSE; HRESULT hr = NO_ERROR; BOOL fApplies = FALSE;
DBG_ASSERT( pMainContext != NULL );
//
// If we already have a user context, then we must have had an
// AUTH_COMPLETE notification which caused the state machine to back up
// and resume from URLINFO state. In that case, just bail
//
if ( pMainContext->QueryUserContext() != NULL ) { DBG_ASSERT( pMainContext->IsNotificationNeeded( SF_NOTIFY_AUTH_COMPLETE ) );
return CONTEXT_STATUS_CONTINUE; } //
// First, find the authentication provider which applies. We
// should always find a matching provider (since anonymous
// provider) should always match!
//
for ( ; ; ) { pProvider = _rgAuthProviders[ cProviderCount ]; if ( pProvider == NULL ) { break; } DBG_ASSERT( pProvider != NULL );
hr = pProvider->DoesApply( pMainContext, &fApplies ); if ( FAILED( hr ) ) { goto Finished; } if ( fApplies ) { //
// Cool. We have a match!
//
break; } cProviderCount++;
}
//
// If only the anonymous provider matched, then check whether we
// have credentials associated with the connection (since IE won't
// send Authorization: header for subsequent SSPI authenticated
// requests on a connection)
//
if ( pProvider->QueryAuthType() == MD_AUTH_ANONYMOUS ) { //
// Another slimy optimization. If we haven't associated a user
// with the connection, then we don't have to bother looking up
// connection
//
if ( _fHasAssociatedUserBefore ) { pUserContext = pMainContext->QueryConnectionUserContext(); if ( pUserContext != NULL ) { pProvider = pUserContext->QueryProvider(); DBG_ASSERT( pProvider != NULL ); } } } else { //
// If a provider applies, then ignore/remove any
// cached user associated with the request
//
pUserContext = pMainContext->QueryConnectionUserContext(); if ( pUserContext != NULL ) { pMainContext->SetConnectionUserContext( NULL ); pUserContext->DereferenceUserContext(); pUserContext = NULL; } }
//
// Is the given provider supported (by metadata)
//
pMetaData = pMainContext->QueryUrlContext()->QueryMetaData(); DBG_ASSERT( pMetaData != NULL ); if ( pMetaData->QueryAuthTypeSupported( pProvider->QueryAuthType() ) ) { fSupported = TRUE; } else { //
// If anonymous authentication is supported, then we can
// still let it thru
//
if ( pMetaData->QueryAuthTypeSupported( MD_AUTH_ANONYMOUS ) ) { pProvider = QueryAnonymousProvider(); DBG_ASSERT( pProvider != NULL );
//
// Anonymous provider applies, remove the previous cached
// user associated with the request
//
if ( pUserContext != NULL ) { pMainContext->SetConnectionUserContext( NULL ); pUserContext->DereferenceUserContext(); pUserContext = NULL; }
fSupported = TRUE; } }
//
// Not supported, you're outta here!
//
if ( !fSupported ) { pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized, Http401Config ); pMainContext->SetFinishedResponse();
hr = pMainContext->OnAccessDenied(); goto Finished; }
//
// Now we can authenticate
//
if ( pUserContext != NULL ) { //
// We already have a context associated with connection. Use it!
//
pUserContext->ReferenceUserContext(); pMainContext->SetUserContext( pUserContext ); } else { DBG_ASSERT( pProvider != NULL );
// perf ctr
pMainContext->QuerySite()->IncLogonAttempts();
hr = pProvider->DoAuthenticate( pMainContext ); if ( FAILED( hr ) ) { if( WIN32_FROM_HRESULT( hr ) == ERROR_PASSWORD_MUST_CHANGE || WIN32_FROM_HRESULT( hr ) == ERROR_PASSWORD_EXPIRED ) { hr = pMainContext->PasswdChangeExecute(); if( S_OK == hr ) { return CONTEXT_STATUS_PENDING; } else if( S_FALSE == hr ) { //
// S_FALSE means password change disabled
//
pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized, Http401BadLogon ); pMainContext->SetErrorStatus( hr ); pMainContext->SetFinishedResponse();
return CONTEXT_STATUS_CONTINUE; } }
goto Finished; } }
//
// Do we have a valid user now
//
pUserContext = pMainContext->QueryUserContext();
if ( pUserContext != NULL ) { if ( pUserContext->QueryAuthType() != MD_AUTH_ANONYMOUS ) { hr = pMainContext->PasswdExpireNotify(); if( FAILED( hr ) ) { //
// Internal error
//
goto Finished; } else if( hr == S_OK ) { //
// We've successfully handled password expire
// notification
//
return CONTEXT_STATUS_PENDING; }
//
// Advanced password expire notification is disabled,
// we should allow the user to get access, fall through
//
} //
// Should we cache the user on the connection? Do so, only if
//
DBG_ASSERT( pMetaData != NULL ); if ( pMetaData->QueryAuthPersistence() != MD_AUTH_SINGLEREQUEST && pUserContext->QueryProvider()->QueryAuthType() == MD_AUTH_NT && !pMainContext->QueryRequest()->IsProxyRequest() && pUserContext != pMainContext->QueryConnectionUserContext() ) { pUserContext->ReferenceUserContext(); pMainContext->SetConnectionUserContext( pUserContext ); _fHasAssociatedUserBefore = TRUE; } } else { //
// If we don't have a user, then we must not allow handle request
// state to happen!
//
pMainContext->SetFinishedResponse(); } //
// OK. If we got to here and we have a user context, then authentication
// is complete! So lets notify AUTH_COMPLETE filters
//
if ( pUserContext != NULL ) { if ( pMainContext->IsNotificationNeeded( SF_NOTIFY_AUTH_COMPLETE ) ) { HTTP_FILTER_AUTH_COMPLETE_INFO AuthInfo; STACK_STRU( strOriginal, MAX_PATH ); STACK_STRU( strNewUrl, MAX_PATH ); BOOL fFinished = FALSE; //
// Store away the original URL
//
hr = pMainContext->QueryRequest()->GetUrl( &strOriginal ); if ( FAILED( hr ) ) { goto Finished; }
//
// Call the filter
//
pMainContext->NotifyFilters( SF_NOTIFY_AUTH_COMPLETE, &AuthInfo, &fFinished ); if ( fFinished ) { pMainContext->SetDone(); return CONTEXT_STATUS_CONTINUE; } //
// If the URL has changed, we'll need to backup the state machine
//
hr = pMainContext->QueryRequest()->GetUrl( &strNewUrl ); if ( FAILED( hr ) ) { goto Finished; } if ( wcscmp( strNewUrl.QueryStr(), strOriginal.QueryStr() ) != 0 ) { //
// URL is different!
//
pMainContext->BackupStateMachine(); } else { //
// URL is the same. Do nothing and continue
//
} } }
Finished: if ( FAILED( hr ) ) { pMainContext->QueryResponse()-> SetStatus( HttpStatusServerError ); pMainContext->SetFinishedResponse(); pMainContext->SetErrorStatus( hr ); } return CONTEXT_STATUS_CONTINUE; }
|