/*++ Copyright (c) 1999 Microsoft Corporation Module Name : certmapprovider.cxx Abstract: IIS Certificate Mapper provider Author: Bilal Alam (balam) 10-Jan-2000 Environment: Win32 - User Mode Project: ULW3.DLL --*/ #include "precomp.hxx" #include "iiscertmapprovider.hxx" HRESULT IISCERTMAP_AUTH_PROVIDER::DoesApply( W3_MAIN_CONTEXT * pMainContext, BOOL * pfApplies ) /*++ Routine Description: Does certificate map authentication apply? Arguments: pMainContext - Main context pfApplies - Set to TRUE if cert map auth applies Return Value: HRESULT --*/ { CERTIFICATE_CONTEXT * pCertificateContext; URL_CONTEXT * pUrlContext = NULL; W3_METADATA * pMetaData = NULL; BOOL fApplies = FALSE; W3_SITE * pSite = NULL; IIS_CERTIFICATE_MAPPING * pIISCertificateMapping = NULL; TOKEN_CACHE_ENTRY * pCachedIISMappedToken = NULL; BOOL fClientCertDeniedByMapper = FALSE; if ( pMainContext == NULL || pfApplies == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } // // If cert mapping is not allowed for this vroot, then ignore client // cert token and let other authentication mechanisms do their thing // pUrlContext = pMainContext->QueryUrlContext(); DBG_ASSERT( pUrlContext != NULL ); pMetaData = pUrlContext->QueryMetaData(); DBG_ASSERT( pMetaData != NULL ); if ( pMetaData->QuerySslAccessPerms() & MD_ACCESS_MAP_CERT ) { pCertificateContext = pMainContext->QueryCertificateContext(); if ( pCertificateContext == NULL ) { fApplies = FALSE; goto Finished; } pSite = pMainContext->QuerySite(); DBG_ASSERT( pSite != NULL ); if ( ! pSite->QueryUseDSMapper() ) { // // IIS mapper enabled // HRESULT hr = E_FAIL; PBYTE pbClientCertBlob = NULL; DWORD cbClientCertBlob = 0; // // No need to call DereferenceCertMapping after QueryIISCertificateMapping // IISCertificateMapping is referenced by W3_SITE and we hold reference // to W3_SITE already // hr = pSite->GetIISCertificateMapping( &pIISCertificateMapping ); if ( FAILED( hr ) || ( pIISCertificateMapping == NULL ) ) { // // If we couldn't read the mapping because not found, thats OK. // // CODEWORK: we may need smarted error handling (ignoring error // and assuming that mapping was not found is not very good idea // fApplies = FALSE; goto Finished; } // // retrieve client certificate // pCertificateContext->QueryEncodedCertificate( reinterpret_cast(&pbClientCertBlob), &cbClientCertBlob ); if( pbClientCertBlob == NULL || cbClientCertBlob == 0 ) { fApplies = FALSE; goto Finished; } DBG_ASSERT( pIISCertificateMapping != NULL ); hr = pIISCertificateMapping->DoMapCredential( pMainContext, pbClientCertBlob, cbClientCertBlob, &pCachedIISMappedToken, &fClientCertDeniedByMapper ); if ( FAILED( hr ) ) { // // IISCERTMAP applies only when there was successful mapping // Otherwise it will yield other auth providers // if ( hr == SEC_E_UNKNOWN_CREDENTIALS ) { // // DoMapCredential didn't find any mathing mapping // or user/pwd in the mapping was invalid // hr = S_OK; } fApplies = FALSE; goto Finished; } DBG_ASSERT ( fClientCertDeniedByMapper || pCachedIISMappedToken!= NULL ); if( ( pCachedIISMappedToken != NULL && pCachedIISMappedToken->QueryImpersonationToken() != NULL ) || fClientCertDeniedByMapper ) { IISCERTMAP_CONTEXT_STATE * pContextState = NULL; // // Use IISCERTMAP_CONTEXT_STATE to communicate information // from DoesApply() to DoAuthenticate() // We don't want to be calling mapper twice // pContextState = new (pMainContext) IISCERTMAP_CONTEXT_STATE( pCachedIISMappedToken, fClientCertDeniedByMapper ); if ( pContextState == NULL ) { if ( pCachedIISMappedToken != NULL ) { pCachedIISMappedToken->DereferenceCacheEntry(); pCachedIISMappedToken = NULL; } hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY ); goto Finished; } // // pContextState is taking ownership of pCachedIISMappedToken // pMainContext->SetContextState( pContextState ); fApplies = TRUE; } } } Finished: *pfApplies = fApplies; if ( pCachedIISMappedToken != NULL ) { // // if creating CERTMAP_CONTEXT_STATE succeeded it will hold it's own reference // to cached token // pCachedIISMappedToken->DereferenceCacheEntry(); pCachedIISMappedToken = NULL; } return NO_ERROR; } HRESULT IISCERTMAP_AUTH_PROVIDER::DoAuthenticate( W3_MAIN_CONTEXT * pMainContext, BOOL * pfFilterFinished ) /*++ Routine Description: Create a user context representing a cert mapped token Arguments: pMainContext - Main context pfFilterFinished - Set to TRUE if filter wants out Return Value: HRESULT --*/ { IISCERTMAP_USER_CONTEXT * pUserContext = NULL; HRESULT hr = NO_ERROR; W3_SITE * pSite = NULL; IISCERTMAP_CONTEXT_STATE * pContextState = NULL; TOKEN_CACHE_ENTRY * CachedToken = NULL; if ( pMainContext == NULL || pfFilterFinished == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *pfFilterFinished = FALSE; pSite = pMainContext->QuerySite(); DBG_ASSERT( pSite != NULL ); // IIS mapper DBG_ASSERT ( !pSite->QueryUseDSMapper() ); pContextState = (IISCERTMAP_CONTEXT_STATE *) pMainContext->QueryContextState(); DBG_ASSERT( pContextState != NULL ); if ( pContextState->QueryClientCertDeniedByIISCertMap() ) { // // Report denied by IIS mapper error // pMainContext->QueryResponse()->SetStatus( HttpStatusForbidden, Http403MapperDenyAccess); pMainContext->SetErrorStatus( S_OK ); return S_OK; } CachedToken = pContextState->QueryCachedIISCertMapToken(); DBG_ASSERT( CachedToken != NULL ); // // Create the user context for this request // pUserContext = new IISCERTMAP_USER_CONTEXT( this ); if ( pUserContext == NULL ) { CachedToken->DereferenceCacheEntry(); return HRESULT_FROM_WIN32( GetLastError() ); } hr = pUserContext->Create( CachedToken ); if ( FAILED( hr ) ) { pUserContext->DereferenceUserContext(); pUserContext = NULL; return hr; } pMainContext->SetUserContext( pUserContext ); return NO_ERROR; } HRESULT IISCERTMAP_AUTH_PROVIDER::OnAccessDenied( W3_MAIN_CONTEXT * /*pMainContext*/ ) /*++ Routine Description: NOP since we have nothing to do on access denied Arguments: pMainContext - Main context (not used) Return Value: HRESULT --*/ { // // No headers to add // return NO_ERROR; } HRESULT IISCERTMAP_USER_CONTEXT::Create( TOKEN_CACHE_ENTRY * pCachedToken ) /*++ Routine Description: Create a certificate mapped user context Arguments: pCachedToken - cached token Note: function takes ownership of pCachedToken. It will dereference it even in the case of failure Return Value: HRESULT --*/ { HRESULT hr = E_FAIL; if ( pCachedToken == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } // // First the easy stuff // pCachedToken->ReferenceCacheEntry(); _pCachedToken = pCachedToken; SetCachedToken( TRUE ); // // Now get the user name // if ( !SetThreadToken( NULL, _pCachedToken->QueryImpersonationToken() ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Failed; } // cchUserName will be set to size of the buffer in chars (including terminating one) DWORD cchUserName = sizeof( _achUserName ) / sizeof( WCHAR ); if ( !GetUserNameEx( NameSamCompatible, _achUserName, &cchUserName ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); RevertToSelf(); goto Failed; } RevertToSelf(); return NO_ERROR; Failed: if ( _pCachedToken != NULL ) { _pCachedToken->DereferenceCacheEntry(); _pCachedToken = NULL; } return hr; }