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.
 
 
 
 
 
 

1406 lines
38 KiB

/*++
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"
#include "passportprovider.hxx"
#define IIS_SUBAUTH_NAME L"iissuba"
W3_STATE_AUTHENTICATION * W3_STATE_AUTHENTICATION::sm_pAuthState;
BOOL W3_STATE_AUTHENTICATION::sm_fSubAuthConfigured = FALSE;
BOOL W3_STATE_AUTHENTICATION::sm_fLocalSystem = FALSE;
LONG W3_STATE_AUTHENTICATION::sm_lSubAuthAnonEvent = 0;
LONG W3_STATE_AUTHENTICATION::sm_lSubAuthDigestEvent = 0;
LONG W3_STATE_AUTHENTICATION::sm_lLocalSystemEvent = 0;
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:
None
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 );
if( dwLength < sizeof( _achDefaultDomainName ) / sizeof( WCHAR ) )
{
wcsncpy( _achDefaultDomainName,
(LPCWSTR)pAcctDomainInfo->DomainName.Buffer,
dwLength );
_achDefaultDomainName[ dwLength ] = L'\0';
}
else
{
err = ERROR_INSUFFICIENT_BUFFER;
goto Cleanup;
}
//
// 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;
//
// Tres freakin lame. Gotta call into GetComputerNameEx() since I
// need to fully qualified name. If it fails, oh well, we don't
// provide a default domain name for Passport calls
//
dwLength = sizeof( _achMemberDomainName ) / sizeof( WCHAR );
GetComputerNameEx( ComputerNameDnsDomain,
_achMemberDomainName,
&dwLength );
}
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;
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;
}
//
// Don't care about the return value here since we want to add
// all possible authentication headers
//
hr = pProvider->OnAccessDenied( pMainContext );
}
return hr;
}
HRESULT
W3_STATE_AUTHENTICATION::GetSubAuthConfiguration(
VOID
)
/*++
Description:
Find out if sub authenticator is configured correctly for the
current process.
Arguments:
None.
Returns:
HRESULT
--*/
{
HRESULT hr = S_OK;
PPRIVILEGE_SET pPrivilegeSet = NULL;
HANDLE hProcessToken = NULL;
BOOL fPrivilegeEnabled = FALSE;
HKEY hKey = NULL;
LUID TcbPrivilegeValue;
WCHAR pszSubAuthName[ sizeof( IIS_SUBAUTH_NAME ) ];
DWORD dwType;
DWORD cbValue;
if ( !OpenProcessToken( GetCurrentProcess(),
TOKEN_ALL_ACCESS,
&hProcessToken ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Exit;
}
DBG_ASSERT( hProcessToken != NULL );
pPrivilegeSet = ( PPRIVILEGE_SET )LocalAlloc( LMEM_FIXED,
sizeof( PRIVILEGE_SET ) + sizeof( LUID_AND_ATTRIBUTES ) );
if( pPrivilegeSet == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Exit;
}
if ( !LookupPrivilegeValue( NULL,
L"SeTcbPrivilege",
&TcbPrivilegeValue ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Exit;
}
pPrivilegeSet->PrivilegeCount = 1;
pPrivilegeSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
pPrivilegeSet->Privilege[0].Luid = TcbPrivilegeValue;
pPrivilegeSet->Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
if( !PrivilegeCheck( hProcessToken,
pPrivilegeSet,
&fPrivilegeEnabled ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Exit;
}
if( fPrivilegeEnabled )
{
sm_fLocalSystem = TRUE;
}
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Control\\Lsa\\MSV1_0",
0,
KEY_READ,
&hKey ) == ERROR_SUCCESS )
{
DBG_ASSERT( hKey != NULL );
cbValue = sizeof pszSubAuthName;
if ( RegQueryValueEx( hKey,
L"Auth132",
NULL,
&dwType,
(LPBYTE) pszSubAuthName,
&cbValue ) != ERROR_SUCCESS ||
dwType != REG_SZ ||
_wcsicmp( pszSubAuthName, IIS_SUBAUTH_NAME ) )
{
}
else
{
sm_fSubAuthConfigured = TRUE;
}
RegCloseKey( hKey );
hKey = NULL;
}
Exit:
if( pPrivilegeSet != NULL )
{
LocalFree( pPrivilegeSet );
pPrivilegeSet = NULL;
}
return hr;
}
W3_STATE_AUTHENTICATION::W3_STATE_AUTHENTICATION()
{
_pAnonymousProvider = NULL;
_pCustomProvider = NULL;
_fHasAssociatedUserBefore = FALSE;
//
// Figure out the default domain name once
//
_hr = GetDefaultDomainName();
if ( FAILED( _hr ) )
{
return;
}
//
// Figure out if we can use the IIS subauthenticator
//
_hr = GetSubAuthConfiguration();
if( FAILED( _hr ) )
{
return;
}
//
// Initialize all the authentication providers
//
ZeroMemory( _rgAuthProviders, sizeof( _rgAuthProviders ) );
_hr = InitializeAuthenticationProviders();
if ( FAILED( _hr ) )
{
return;
}
//
// 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();
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++;
//
// Passport provider
//
DBG_ASSERT( cProviderCount < AUTH_PROVIDER_COUNT );
_rgAuthProviders[ cProviderCount ] = new PASSPORT_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;
BOOL fSupported = FALSE;
HRESULT hr = NO_ERROR;
BOOL fApplies = FALSE;
BOOL fFilterFinished = FALSE;
W3_MAIN_CONTEXT_STATE * pContextState = NULL;
UNREFERENCED_PARAMETER( cbCompletion );
UNREFERENCED_PARAMETER( dwCompletionStatus );
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!
//
pMetaData = pMainContext->QueryUrlContext()->QueryMetaData();
DBG_ASSERT( pMetaData != NULL );
//
// Optimization for path when only anonymous authentication is enabled
// (certmapping is checked as well)
// Issue jaroslad 01/08/23. This may conflict in the future with
// DoesApply of new providers.
//
if ( pMetaData->IsOnlyAnonymousAuthSupported() &&
pMainContext->QueryRequest()->GetHeader( HttpHeaderAuthorization ) == NULL )
{
//
// no authorization header and only anonymous enabled
//
pProvider = QueryAnonymousProvider();
DBG_ASSERT( pProvider != NULL );
}
else
{
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 );
}
//
// Clean up the security context if there is one
//
pProvider->SetConnectionAuthContext( pMainContext, 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)
//
if ( pMetaData->QueryAuthTypeSupported( pProvider->QueryAuthType() ) )
{
fSupported = TRUE;
}
else if( pProvider->QueryAuthType() == MD_AUTH_ANONYMOUS )
{
//
// Give the anonymous provider a shot at this request.
//
// We need to do this even if MD_AUTH_ANONYMOUS is not
// supported, so that authentication filters get a
// crack at it. It's up to the anonymous provider to
// fail if it's not supported, and no filter sets
// credentials.
//
pMainContext->SetCheckAnonAuthTypeSupported( TRUE );
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 )
{
//
// Clear any context state which was set
//
pContextState = pMainContext->QueryContextState();
if ( pContextState != NULL )
{
pContextState->Cleanup( pMainContext );
pMainContext->SetContextState( NULL );
}
pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
Http401Config );
pMainContext->SetErrorStatus( HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) );
pMainContext->SetFinishedResponse();
return CONTEXT_STATUS_CONTINUE;
}
//
// 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();
fFilterFinished = FALSE;
hr = pProvider->DoAuthenticate( pMainContext, &fFilterFinished );
if ( FAILED( hr ) )
{
DWORD dwError = WIN32_FROM_HRESULT( hr );
if( dwError == ERROR_PASSWORD_MUST_CHANGE ||
dwError == 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;
}
}
else if( SEC_E_NO_CREDENTIALS == hr )
{
pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
Http401Config );
pMainContext->SetErrorStatus( hr );
pMainContext->SetFinishedResponse();
return CONTEXT_STATUS_CONTINUE;
}
else
{
pMainContext->SetErrorStatus( hr );
if ( dwError == ERROR_ACCESS_DENIED )
{
pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
Http401Filter );
}
else if ( dwError == ERROR_FILE_NOT_FOUND ||
dwError == ERROR_PATH_NOT_FOUND )
{
pMainContext->QueryResponse()->SetStatus( HttpStatusNotFound );
}
else
{
pMainContext->QueryResponse()->SetStatus( HttpStatusServerError );
}
pMainContext->SetFinishedResponse();
pMainContext->SetDisconnect( TRUE );
return CONTEXT_STATUS_CONTINUE;
}
goto Finished;
}
if ( fFilterFinished )
{
pMainContext->SetDone();
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->QueryIsAuthNTLM()
&& !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
//
if ( !pMainContext->NotifyFilters( SF_NOTIFY_AUTH_COMPLETE,
&AuthInfo,
&fFinished ) )
{
DWORD dwError = GetLastError();
pMainContext->SetErrorStatus( HRESULT_FROM_WIN32( dwError ) );
if ( dwError == ERROR_ACCESS_DENIED )
{
pMainContext->QueryResponse()->SetStatus( HttpStatusUnauthorized,
Http401Filter );
}
else if ( dwError == ERROR_FILE_NOT_FOUND ||
dwError == ERROR_PATH_NOT_FOUND )
{
pMainContext->QueryResponse()->SetStatus( HttpStatusNotFound );
}
else
{
pMainContext->QueryResponse()->SetStatus( HttpStatusServerError );
}
pMainContext->SetFinishedResponse();
pMainContext->SetDisconnect( TRUE );
return CONTEXT_STATUS_CONTINUE;
}
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;
}
CONTEXT_STATUS
W3_STATE_AUTHENTICATION::OnCompletion(
W3_MAIN_CONTEXT * pMainContext,
DWORD cbCompletion,
DWORD dwCompletionStatus
)
/*++
Routine Description:
Complete the done state
Arguments:
pMainContext - W3_MAIN_CONTEXT representing an execution of the state machine
cbCompletion - Number of bytes on completion
dwCompletionStatus - Win32 Error on completion (if any)
Return Value:
CONTEXT_STATUS_CONTINUE - if we should continue in state machine
else stop executing the machine and free up the current thread
--*/
{
UNREFERENCED_PARAMETER( cbCompletion );
UNREFERENCED_PARAMETER( dwCompletionStatus );
DBG_ASSERT( pMainContext != NULL );
//
// During authentication state, only the routines for password notification
// could post asynchonous completion while they are doing child execution.
// The following assert is to make sure that the completion is posted by
// ExecuteExpiredURL routine.
//
DBG_ASSERT( pMainContext->QueryRequest()->GetHeader( "CFG-ENC-CAPS" )
!= NULL );
//
// Since the response has already been send asynchronously, we advance the
// state machine to CONTEXT_STATE_RESPONSE here.
//
pMainContext->SetFinishedResponse();
return CONTEXT_STATUS_CONTINUE;
}