|
|
/*++
Copyright (c) 2001 Microsoft Corporation
Module Name : iisctl.cxx
Abstract: IIS CTL (Certificate Trust List) handler This gets used only with SSL client certificates Author: Jaroslav Dunajsky
Environment: Win32 - User Mode
Project: Stream Filter Worker Process --*/
#include "precomp.hxx"
IIS_CTL_HASH * IIS_CTL::sm_pIisCtlHash;
IIS_CTL::IIS_CTL( IN CREDENTIAL_ID * pCredentialId ) : _pCredentialId( pCredentialId ), _pCtlContext( NULL ), _pCtlStore( NULL ), _cRefs( 1 ) { _dwSignature = IIS_CTL_SIGNATURE; }
IIS_CTL::~IIS_CTL() { if ( _pCtlContext != NULL ) { CertFreeCTLContext( _pCtlContext ); _pCtlContext = NULL; } if ( _pCtlStore != NULL ) { _pCtlStore->DereferenceStore(); _pCtlStore = NULL; }
if ( _pCredentialId != NULL ) { delete _pCredentialId; _pCredentialId = NULL; }
_dwSignature = IIS_CTL_SIGNATURE_FREE; }
//static
HRESULT IIS_CTL::Initialize( VOID ) /*++
Routine Description:
Initialize IIS CTL globals
Arguments:
None
Return Value:
HRESULT
--*/ { sm_pIisCtlHash = new IIS_CTL_HASH(); if ( sm_pIisCtlHash == NULL ) { return HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY ); } return NO_ERROR; }
//static
VOID IIS_CTL::Terminate( VOID ) /*++
Routine Description:
Cleanup IIS Ctl globals
Arguments:
None
Return Value:
None
--*/ { if ( sm_pIisCtlHash != NULL ) { delete sm_pIisCtlHash; sm_pIisCtlHash = NULL; } }
//static
HRESULT IIS_CTL::GetIisCtl( IN WCHAR * pszSslCtlIdentifier, IN WCHAR * pszSslCtlStoreName, OUT IIS_CTL ** ppIisCtl ) /*++
Routine Description:
Find a suitable Ctl for use with the site represented by given site SslConfig
Arguments:
pzsSslCtlIdentifier - identifies CTL pszSslCtlStoreName - store name where CTL is to be located (in the LOCAL_MACHINE context) If NULL, then default "CA" store is assumed ppIisCtl - pointer to IIS CTL
Return Value:
HRESULT
--*/ { IIS_CTL * pIisCtl = NULL; CREDENTIAL_ID * pCredentialId = NULL; HRESULT hr = NO_ERROR; LK_RETCODE lkrc; if ( ppIisCtl == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *ppIisCtl = NULL;
if ( pszSslCtlStoreName == NULL ) { //
// assume default store name
//
pszSslCtlStoreName = L"CA"; } //
// First build up a Credential ID to use in looking up in our
// server cert cache
//
pCredentialId = new CREDENTIAL_ID; if ( pCredentialId == NULL ) { return HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY ); } hr = IIS_CTL::BuildCredentialId( pszSslCtlIdentifier, pCredentialId ); if ( FAILED( hr ) ) { //
// Regardless of error, we are toast because we couldn't find
// a server cert
//
delete pCredentialId; return hr; } DBG_ASSERT( sm_pIisCtlHash != NULL ); lkrc = sm_pIisCtlHash->FindKey( pCredentialId, &pIisCtl ); if ( lkrc == LK_SUCCESS ) { //
// Server already contains a credential ID
//
delete pCredentialId; *ppIisCtl = pIisCtl; return NO_ERROR; } //
// Ok. It wasn't in our case, we need to do it there
//
// if IIS_CTL construction succeeds then IIS_CTL
// takes ownership of pCredentialId and is responsible for freeing it
//
pIisCtl = new IIS_CTL( pCredentialId ); if ( pIisCtl == NULL ) { hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
delete pCredentialId; return hr; } hr = pIisCtl->SetupIisCtl( pszSslCtlIdentifier, pszSslCtlStoreName ); if ( FAILED( hr ) ) { //
// Server certificate owns the reference to pCredentialId now
//
delete pIisCtl; return hr; } //
// Now try to add cert to hash.
//
lkrc = sm_pIisCtlHash->InsertRecord( pIisCtl );
//
// Ignore the error. If it didn't get added then we will naturally
// clean it up on the callers dereference
//
*ppIisCtl = pIisCtl; return NO_ERROR; }
//static
HRESULT IIS_CTL::BuildCredentialId( IN WCHAR * pszSslCtlIdentifier, OUT CREDENTIAL_ID * pCredentialId ) /*++
Routine Description:
Read the configured server cert and CTL hash. This forms the identifier for the credentials we need for this site
Arguments:
pszSslCtlIdentifier - CTL identifier pCredentialId - Filled with credential ID
Return Value:
HRESULT
--*/ { STACK_BUFFER( buff, 64 ); HRESULT hr = NO_ERROR;
if ( pCredentialId == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); }
if ( pszSslCtlIdentifier == NULL || pszSslCtlIdentifier[0] == '\0' ) { //
// No CTL
//
return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); } else { //
// Add to our credential ID
//
hr = pCredentialId->Append( (BYTE*) pszSslCtlIdentifier, (DWORD) wcslen( pszSslCtlIdentifier ) * sizeof(WCHAR) ); if ( FAILED( hr ) ) { return hr; } }
return NO_ERROR; }
//private
//static
HRESULT IIS_CTL::SetupIisCtl( IN WCHAR * pszSslCtlIdentifier, IN WCHAR * pszSslCtlStoreName ) /*++
Routine Description:
Build CTL context for the site based on SiteSslConfig Info Arguments:
pszSslCtlIdentifier - identifies CTL pszSslCtlStoreName - store name where CTL is to be located (in the LOCAL_MACHINE context)
Return Value:
HRESULT
--*/ { HRESULT hr = E_FAIL; PCCTL_CONTEXT pCtlContext = NULL; CERT_STORE * pCertStore = NULL; STACK_STRU( strStoreName, 256 );
if ( pszSslCtlIdentifier == NULL || pszSslCtlStoreName == NULL || pszSslCtlStoreName == L'\0' )
{ // CTLs not configured
_pCtlContext = NULL; return S_OK; }
//
// First get the desired store and store it away for later!
//
hr = strStoreName.Copy( pszSslCtlStoreName ); if ( FAILED( hr ) ) { goto Finished; } hr = CERT_STORE::OpenStore( strStoreName, &pCertStore ); if ( FAILED( hr ) ) { goto Finished; } DBG_ASSERT( pCertStore != NULL ); _pCtlStore = pCertStore;
CTL_FIND_USAGE_PARA CtlFindUsagePara; ZeroMemory( &CtlFindUsagePara, sizeof(CtlFindUsagePara) );
CtlFindUsagePara.cbSize = sizeof(CtlFindUsagePara); CtlFindUsagePara.ListIdentifier.cbData = ( (DWORD) wcslen( pszSslCtlIdentifier ) + 1 ) * sizeof(WCHAR); CtlFindUsagePara.ListIdentifier.pbData = (PBYTE) pszSslCtlIdentifier;
//
// Try to find CTL in specified store
//
pCtlContext = CertFindCTLInStore( _pCtlStore->QueryStore(), X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CTL_FIND_USAGE, (LPVOID) &CtlFindUsagePara, NULL );
if ( pCtlContext == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Finished; } _pCtlContext = pCtlContext; hr = S_OK; Finished: return hr; }
//static
LK_PREDICATE IIS_CTL::CertStorePredicate( IN IIS_CTL * pIisCtl, IN void * pvState ) /*++
Description:
DeleteIf() predicate used to find items which reference the CERT_STORE pointed to by pvState
Arguments:
pIisCtl - Server cert pvState - Points to CERT_STORE
Returns:
LK_PREDICATE - LKP_PERFORM indicates removing the current token from token cache
LKP_NO_ACTION indicates doing nothing.
--*/ { LK_PREDICATE lkpAction; CERT_STORE * pCtlStore;
DBG_ASSERT( pIisCtl != NULL ); pCtlStore = (CERT_STORE*) pvState; DBG_ASSERT( pCtlStore != NULL ); if ( pIisCtl->_pCtlStore == pCtlStore ) { //
// Before we delete the cert, flush any site which is referencing
// it
//
ENDPOINT_CONFIG::FlushByIisCtl( pIisCtl ); lkpAction = LKP_PERFORM; } else { lkpAction = LKP_NO_ACTION; }
return lkpAction; }
//static
HRESULT IIS_CTL::FlushByStore( IN CERT_STORE * pCertStore ) /*++
Routine Description:
Flush any server certs which reference the given store Arguments:
pCertStore - Cert store to check
Return Value:
HRESULT
--*/ { DBG_ASSERT( sm_pIisCtlHash != NULL ); sm_pIisCtlHash->DeleteIf( IIS_CTL::CertStorePredicate, pCertStore ); return NO_ERROR; }
HRESULT IIS_CTL::VerifyContainsCert( IN PCCERT_CONTEXT pChainTop, OUT BOOL * pfContainsCert ) /*++
Routine Description:
Verify is CTL contains given certificate
Arguments:
pChainTop - top certificate of the chain to be found in CTL pfContainsCert - TRUE if contains, FALSE if it doesn't (valuse valid only if function returns SUCCESS) Return Value:
HRESULT --*/
{ HRESULT hr = E_FAIL; const int SHA1_HASH_SIZE = 20; BYTE rgbChainTopHash[ SHA1_HASH_SIZE ]; DWORD cbSize = SHA1_HASH_SIZE;
if ( _pCtlContext == NULL ) { //
// pCTLContext could be NULL if there was failure in building
// CTL context in the SITE CONFIG setup
//
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); }
//
// get hash of the certificate to be verified
//
if ( !CertGetCertificateContextProperty( pChainTop, CERT_SHA1_HASH_PROP_ID, rgbChainTopHash, &cbSize ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF((DBG_CONTEXT, "Failed to get cert hash for CTL check: 0x%x\n", hr)); return hr; }
//
// Iterate through all the cert hashes in the CTL and compare hashes
//
for ( DWORD dwIndex = 0; dwIndex< _pCtlContext->pCtlInfo->cCTLEntry; dwIndex++ ) { CRYPT_DATA_BLOB CertInCTLHashBlob = _pCtlContext->pCtlInfo->rgCTLEntry[dwIndex].SubjectIdentifier; //
// CODEWORK: checking hash size is a bit simplistic way of
// verifying that SHA1 hash is used in CTL
//
if ( CertInCTLHashBlob.cbData != SHA1_HASH_SIZE || CertInCTLHashBlob.pbData == NULL ) { //
// hash in the CTL is no SHA1 because size is different
// or invalid CTL
//
DBG_ASSERT( FALSE ); return CRYPT_E_NOT_FOUND; } if ( memcmp( CertInCTLHashBlob.pbData, rgbChainTopHash, SHA1_HASH_SIZE ) == 0 ) { *pfContainsCert = TRUE; return S_OK; } } *pfContainsCert = FALSE; return S_OK; }
//static
VOID IIS_CTL::Cleanup( VOID ) /*++
Routine Description:
Cleanup must be called before Terminate Arguments:
none
Return Value:
VOID
--*/ { sm_pIisCtlHash->Clear(); }
|