windows-nt/Source/XPSP1/NT/inetsrv/iis/iisrearc/iisplus/ulw3/authstate.cxx
2020-09-26 16:20:57 +08:00

1129 lines
28 KiB
C++

/*++
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;
}