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.
 
 
 
 
 
 

606 lines
13 KiB

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