Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1527 lines
42 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows NT Security
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: ssctl.cpp
//
// Contents: Self Signed Certificate Trust List Subsystem used by the
// Certificate Chaining Infrastructure for building complex
// chains
//
// History: 11-Feb-98 kirtd Created
//
//----------------------------------------------------------------------------
#include <global.hxx>
#include <dbgdef.h>
//+-------------------------------------------------------------------------
// Attempt to get and allocate the CTL NextUpdate location Url array.
//--------------------------------------------------------------------------
BOOL
WINAPI
SSCtlGetNextUpdateUrl(
IN PCCTL_CONTEXT pCtl,
OUT PCRYPT_URL_ARRAY *ppUrlArray
)
{
BOOL fResult;
PCRYPT_URL_ARRAY pUrlArray = NULL;
DWORD cbUrlArray = 0;
LPVOID apv[2];
apv[0] = (LPVOID) pCtl;
apv[1] = (LPVOID)(UINT_PTR)(0); // Signer Index
if (!ChainGetObjectUrl(
URL_OID_CTL_NEXT_UPDATE,
apv,
0, // dwFlags
NULL, // pUrlArray
&cbUrlArray,
NULL, // pUrlInfo
NULL, // cbUrlInfo,
NULL // pvReserved
))
goto GetObjectUrlError;
pUrlArray = (PCRYPT_URL_ARRAY) new BYTE [cbUrlArray];
if (NULL == pUrlArray)
goto OutOfMemory;
if (!ChainGetObjectUrl(
URL_OID_CTL_NEXT_UPDATE,
apv,
0, // dwFlags
pUrlArray,
&cbUrlArray,
NULL, // pUrlInfo
NULL, // cbUrlInfo,
NULL // pvReserved
))
goto GetObjectUrlError;
if (0 == pUrlArray->cUrl)
goto NoNextUpdateUrls;
fResult = TRUE;
CommonReturn:
*ppUrlArray = pUrlArray;
return fResult;
ErrorReturn:
if (pUrlArray) {
delete (LPBYTE) pUrlArray;
pUrlArray = NULL;
}
fResult = FALSE;
goto CommonReturn;
TRACE_ERROR(GetObjectUrlError)
SET_ERROR(OutOfMemory, E_OUTOFMEMORY)
SET_ERROR(NoNextUpdateUrls, CRYPT_E_NOT_FOUND)
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObject::CSSCtlObject, public
//
// Synopsis: Constructor
//
//----------------------------------------------------------------------------
CSSCtlObject::CSSCtlObject (
IN PCCERTCHAINENGINE pChainEngine,
IN PCCTL_CONTEXT pCtlContext,
IN BOOL fAdditionalStore,
OUT BOOL& rfResult
)
{
DWORD cbData;
CRYPT_DATA_BLOB DataBlob;
rfResult = TRUE;
m_cRefs = 1;
m_pCtlContext = CertDuplicateCTLContext( pCtlContext );
m_fHasSignatureBeenVerified = FALSE;
m_fSignatureValid = FALSE;
m_hMessageStore = NULL;
m_hHashEntry = NULL;
m_pChainEngine = pChainEngine;
m_pNextUpdateUrlArray = NULL;
m_dwOfflineCnt = 0;
I_CryptZeroFileTime(&m_OfflineUpdateTime);
memset( &m_SignerInfo, 0, sizeof( m_SignerInfo ) );
cbData = CHAINHASHLEN;
rfResult = CertGetCTLContextProperty(
pCtlContext,
CERT_MD5_HASH_PROP_ID,
m_rgbCtlHash,
&cbData
);
if ( rfResult && CHAINHASHLEN != cbData)
{
rfResult = FALSE;
SetLastError( (DWORD) E_UNEXPECTED);
}
if (!fAdditionalStore)
{
if ( rfResult == TRUE )
{
DataBlob.cbData = CHAINHASHLEN;
DataBlob.pbData = m_rgbCtlHash;
rfResult = I_CryptCreateLruEntry(
pChainEngine->SSCtlObjectCache()->HashIndex(),
&DataBlob,
this,
&m_hHashEntry
);
}
if ( rfResult == TRUE )
{
m_hMessageStore = CertOpenStore(
CERT_STORE_PROV_MSG,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
NULL,
0,
pCtlContext->hCryptMsg
);
if ( m_hMessageStore == NULL )
{
rfResult = FALSE;
}
}
}
if ( rfResult == TRUE )
{
rfResult = SSCtlGetSignerInfo( pCtlContext, &m_SignerInfo );
}
if (!fAdditionalStore)
{
if ( rfResult == TRUE )
{
if (!I_CryptIsZeroFileTime(&m_pCtlContext->pCtlInfo->NextUpdate))
{
// Ignore any errors
SSCtlGetNextUpdateUrl(m_pCtlContext, &m_pNextUpdateUrlArray);
}
}
}
assert( m_pChainEngine != NULL );
assert( m_pCtlContext != NULL );
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObject::~CSSCtlObject, public
//
// Synopsis: Destructor
//
//----------------------------------------------------------------------------
CSSCtlObject::~CSSCtlObject ()
{
SSCtlFreeSignerInfo( &m_SignerInfo );
if ( m_hMessageStore != NULL )
{
CertCloseStore( m_hMessageStore, 0 );
}
if ( m_pNextUpdateUrlArray != NULL )
{
delete (LPBYTE) m_pNextUpdateUrlArray;
}
if ( m_hHashEntry != NULL )
{
I_CryptReleaseLruEntry( m_hHashEntry );
}
CertFreeCTLContext( m_pCtlContext );
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObject::GetSigner, public
//
// Synopsis: get the certificate object of the signer
//
//----------------------------------------------------------------------------
BOOL
CSSCtlObject::GetSigner (
IN PCCHAINPATHOBJECT pSubject,
IN PCCHAINCALLCONTEXT pCallContext,
IN HCERTSTORE hAdditionalStore,
OUT PCCHAINPATHOBJECT* ppSigner,
OUT BOOL* pfCtlSignatureValid
)
{
BOOL fResult;
PCCHAINPATHOBJECT pSigner = NULL;
BOOL fNewSigner = TRUE;
fResult = SSCtlGetSignerChainPathObject(
pSubject,
pCallContext,
&m_SignerInfo,
hAdditionalStore,
&pSigner,
&fNewSigner
);
if (fResult)
{
if ( !m_fHasSignatureBeenVerified || fNewSigner )
{
CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA CtrlPara;
memset(&CtrlPara, 0, sizeof(CtrlPara));
CtrlPara.cbSize = sizeof(CtrlPara);
// CtrlPara.hCryptProv =
// This needs to be updated when chain building
// supports CTLs with more than one signer.
CtrlPara.dwSignerIndex = 0;
CtrlPara.dwSignerType = CMSG_VERIFY_SIGNER_CERT;
CtrlPara.pvSigner = (void *) pSigner->CertObject()->CertContext();
m_fSignatureValid = CryptMsgControl(
m_pCtlContext->hCryptMsg,
0,
CMSG_CTRL_VERIFY_SIGNATURE_EX,
&CtrlPara
);
m_fHasSignatureBeenVerified = TRUE;
CertPerfIncrementChainVerifyCtlSignatureCount();
}
else
{
CertPerfIncrementChainBeenVerifiedCtlSignatureCount();
}
*ppSigner = pSigner;
}
*pfCtlSignatureValid = m_fSignatureValid;
return fResult;
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObject::GetTrustListInfo, public
//
// Synopsis: get the trust list information relative to a particular cert
// object
//
//----------------------------------------------------------------------------
BOOL
CSSCtlObject::GetTrustListInfo (
IN PCCERT_CONTEXT pCertContext,
OUT PCERT_TRUST_LIST_INFO* ppTrustListInfo
)
{
PCTL_ENTRY pCtlEntry;
PCERT_TRUST_LIST_INFO pTrustListInfo;
pCtlEntry = CertFindSubjectInCTL(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CTL_CERT_SUBJECT_TYPE,
(LPVOID)pCertContext,
m_pCtlContext,
0
);
if ( pCtlEntry == NULL )
{
SetLastError( (DWORD) CRYPT_E_NOT_FOUND );
return( FALSE );
}
pTrustListInfo = new CERT_TRUST_LIST_INFO;
if ( pTrustListInfo == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
return( FALSE );
}
pTrustListInfo->cbSize = sizeof( CERT_TRUST_LIST_INFO );
pTrustListInfo->pCtlEntry = pCtlEntry;
pTrustListInfo->pCtlContext = CertDuplicateCTLContext( m_pCtlContext );
*ppTrustListInfo = pTrustListInfo;
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObject::CalculateStatus, public
//
// Synopsis: calculate the status
//
//----------------------------------------------------------------------------
VOID
CSSCtlObject::CalculateStatus (
IN LPFILETIME pTime,
IN PCERT_USAGE_MATCH pRequestedUsage,
IN OUT PCERT_TRUST_STATUS pStatus
)
{
assert( m_fHasSignatureBeenVerified == TRUE );
SSCtlGetCtlTrustStatus(
m_pCtlContext,
m_fSignatureValid,
pTime,
pRequestedUsage,
pStatus
);
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObject::HasNextUpdateUrl, public
//
// Synopsis: returns TRUE if the Ctl has a NextUpdate time and location Url
//
//----------------------------------------------------------------------------
BOOL CSSCtlObject::HasNextUpdateUrl (
OUT LPFILETIME pUpdateTime
)
{
if ( m_pNextUpdateUrlArray != NULL )
{
assert(!I_CryptIsZeroFileTime(&m_pCtlContext->pCtlInfo->NextUpdate));
if (0 != m_dwOfflineCnt) {
assert(!I_CryptIsZeroFileTime(&m_OfflineUpdateTime));
*pUpdateTime = m_OfflineUpdateTime;
} else
*pUpdateTime = m_pCtlContext->pCtlInfo->NextUpdate;
return TRUE;
}
else
{
return FALSE;
}
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObject::SetOffline, public
//
// Synopsis: called when offline
//
//----------------------------------------------------------------------------
void CSSCtlObject::SetOffline (
IN LPFILETIME pCurrentTime,
OUT LPFILETIME pUpdateTime
)
{
m_dwOfflineCnt++;
I_CryptIncrementFileTimeBySeconds(
pCurrentTime,
ChainGetOfflineUrlDeltaSeconds(m_dwOfflineCnt),
&m_OfflineUpdateTime
);
*pUpdateTime = m_OfflineUpdateTime;
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObjectCache::CSSCtlObjectCache, public
//
// Synopsis: Constructor
//
//----------------------------------------------------------------------------
CSSCtlObjectCache::CSSCtlObjectCache (
OUT BOOL& rfResult
)
{
LRU_CACHE_CONFIG Config;
memset( &Config, 0, sizeof( Config ) );
Config.dwFlags = LRU_CACHE_NO_SERIALIZE | LRU_CACHE_NO_COPY_IDENTIFIER;
Config.pfnHash = CertObjectCacheHashMd5Identifier;
Config.cBuckets = DEFAULT_CERT_OBJECT_CACHE_BUCKETS;
Config.pfnOnRemoval = SSCtlOnRemovalFromCache;
m_hHashIndex = NULL;
rfResult = I_CryptCreateLruCache( &Config, &m_hHashIndex );
I_CryptZeroFileTime(&m_UpdateTime);
m_fFirstUpdate = FALSE;
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObjectCache::~CSSCtlObjectCache, public
//
// Synopsis: Destructor
//
//----------------------------------------------------------------------------
CSSCtlObjectCache::~CSSCtlObjectCache ()
{
I_CryptFreeLruCache( m_hHashIndex, 0, NULL );
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObjectCache::PopulateCache, public
//
// Synopsis: populate the cache
//
//----------------------------------------------------------------------------
BOOL
CSSCtlObjectCache::PopulateCache (
IN PCCERTCHAINENGINE pChainEngine
)
{
assert( pChainEngine->SSCtlObjectCache() == this );
return( SSCtlPopulateCacheFromCertStore( pChainEngine, NULL ) );
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObjectCache::AddObject, public
//
// Synopsis: add an object to the cache
//
//----------------------------------------------------------------------------
BOOL
CSSCtlObjectCache::AddObject (
IN PCSSCTLOBJECT pSSCtlObject,
IN BOOL fCheckForDuplicate
)
{
FILETIME UpdateTime;
if ( fCheckForDuplicate == TRUE )
{
PCSSCTLOBJECT pDuplicate;
pDuplicate = FindObjectByHash( pSSCtlObject->CtlHash() );
if ( pDuplicate != NULL )
{
pDuplicate->Release();
SetLastError( (DWORD) CRYPT_E_EXISTS );
return( FALSE );
}
}
pSSCtlObject->AddRef();
if (pSSCtlObject->HasNextUpdateUrl(&UpdateTime))
{
// Set earliest update time
if (I_CryptIsZeroFileTime(&m_UpdateTime) ||
0 > CompareFileTime(&UpdateTime, &m_UpdateTime))
{
m_UpdateTime = UpdateTime;
}
m_fFirstUpdate = TRUE;
}
I_CryptInsertLruEntry( pSSCtlObject->HashIndexEntry(), NULL );
if (pSSCtlObject->MessageStore() )
{
CertAddStoreToCollection(
pSSCtlObject->ChainEngine()->OtherStore(),
pSSCtlObject->MessageStore(),
0,
0
);
}
CertPerfIncrementChainCtlCacheCount();
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObjectCache::RemoveObject, public
//
// Synopsis: remove object from cache
//
//----------------------------------------------------------------------------
VOID
CSSCtlObjectCache::RemoveObject (
IN PCSSCTLOBJECT pSSCtlObject
)
{
I_CryptRemoveLruEntry( pSSCtlObject->HashIndexEntry(), 0, NULL );
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObjectCache::FindObjectByHash, public
//
// Synopsis: find object with given hash
//
//----------------------------------------------------------------------------
PCSSCTLOBJECT
CSSCtlObjectCache::FindObjectByHash (
IN BYTE rgbHash [ CHAINHASHLEN ]
)
{
HLRUENTRY hFound;
PCSSCTLOBJECT pFound = NULL;
CRYPT_HASH_BLOB HashBlob;
HashBlob.cbData = CHAINHASHLEN;
HashBlob.pbData = rgbHash;
hFound = I_CryptFindLruEntry( m_hHashIndex, &HashBlob );
if ( hFound != NULL )
{
pFound = (PCSSCTLOBJECT)I_CryptGetLruEntryData( hFound );
pFound->AddRef();
I_CryptReleaseLruEntry( hFound );
}
return( pFound );
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObjectCache::EnumObjects, public
//
// Synopsis: enumerate objects
//
//----------------------------------------------------------------------------
VOID
CSSCtlObjectCache::EnumObjects (
IN PFN_ENUM_SSCTLOBJECTS pfnEnum,
IN LPVOID pvParameter
)
{
SSCTL_ENUM_OBJECTS_DATA EnumData;
EnumData.pfnEnumObjects = pfnEnum;
EnumData.pvEnumParameter = pvParameter;
I_CryptWalkAllLruCacheEntries(
m_hHashIndex,
SSCtlEnumObjectsWalkFn,
&EnumData
);
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObjectCache::Resync, public
//
// Synopsis: resync the cache
//
//----------------------------------------------------------------------------
BOOL
CSSCtlObjectCache::Resync (IN PCCERTCHAINENGINE pChainEngine)
{
I_CryptFlushLruCache( m_hHashIndex, 0, NULL );
I_CryptZeroFileTime(&m_UpdateTime);
m_fFirstUpdate = FALSE;
return( PopulateCache( pChainEngine ) );
}
//+---------------------------------------------------------------------------
//
// Member: CSSCtlObjectCache::UpdateCache, public
//
// Synopsis: update the cache
//
// Leaves the engine's critical section to do the URL
// fetching. If the engine was touched by another thread,
// it fails with LastError set to ERROR_CAN_NOT_COMPLETE.
//
// If the CTL is updated, increments the engine's touch count
// and flushes issuer and end cert object caches.
//
// Assumption: Chain engine is locked once in the calling thread.
//
//----------------------------------------------------------------------------
BOOL
CSSCtlObjectCache::UpdateCache (
IN PCCERTCHAINENGINE pChainEngine,
IN PCCHAINCALLCONTEXT pCallContext
)
{
FILETIME CurrentTime;
SSCTL_UPDATE_CTL_OBJ_PARA Para;
assert( pChainEngine->SSCtlObjectCache() == this );
// Check if we have any CTLs needing to be updated
if (I_CryptIsZeroFileTime(&m_UpdateTime))
return TRUE;
pCallContext->CurrentTime(&CurrentTime);
if (0 < CompareFileTime(&m_UpdateTime, &CurrentTime))
return TRUE;
if (!m_fFirstUpdate && !pCallContext->IsOnline())
return TRUE;
memset(&Para, 0, sizeof(Para));
Para.pChainEngine = pChainEngine;
Para.pCallContext = pCallContext;
EnumObjects(SSCtlUpdateCtlObjectEnumFn, &Para);
if (pCallContext->IsTouchedEngine()) {
PSSCTL_UPDATE_CTL_OBJ_ENTRY pEntry;
pEntry = Para.pEntry;
while (pEntry) {
PSSCTL_UPDATE_CTL_OBJ_ENTRY pDeleteEntry;
pEntry->pSSCtlObjectAdd->Release();
pDeleteEntry = pEntry;
pEntry = pEntry->pNext;
delete pDeleteEntry;
}
return FALSE;
}
m_UpdateTime = Para.UpdateTime;
m_fFirstUpdate = FALSE;
if (Para.pEntry) {
HCERTSTORE hTrustStore;
PSSCTL_UPDATE_CTL_OBJ_ENTRY pEntry;
hTrustStore = pChainEngine->OpenTrustStore();
pChainEngine->CertObjectCache()->FlushObjects( pCallContext );
pCallContext->TouchEngine();
pEntry = Para.pEntry;
while (pEntry) {
PSSCTL_UPDATE_CTL_OBJ_ENTRY pDeleteEntry;
RemoveObject(pEntry->pSSCtlObjectRemove);
if (AddObject(pEntry->pSSCtlObjectAdd, TRUE)) {
if (hTrustStore) {
// Persist the newer CTL to the trust store
CertAddCTLContextToStore(
hTrustStore,
pEntry->pSSCtlObjectAdd->CtlContext(),
CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES,
NULL
);
}
}
pEntry->pSSCtlObjectAdd->Release();
pDeleteEntry = pEntry;
pEntry = pEntry->pNext;
delete pDeleteEntry;
}
if (hTrustStore)
CertCloseStore(hTrustStore, 0);
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlOnRemovalFromCache
//
// Synopsis: SS CTL removal notification used when the cache is destroyed
// or an object is explicitly removed. Note that this cache
// does not LRU remove objects
//
//----------------------------------------------------------------------------
VOID WINAPI
SSCtlOnRemovalFromCache (
IN LPVOID pv,
IN OPTIONAL LPVOID pvRemovalContext
)
{
PCSSCTLOBJECT pSSCtlObject = (PCSSCTLOBJECT) pv;
CertPerfDecrementChainCtlCacheCount();
assert( pvRemovalContext == NULL );
if (pSSCtlObject->MessageStore() )
{
CertRemoveStoreFromCollection(
pSSCtlObject->ChainEngine()->OtherStore(),
pSSCtlObject->MessageStore()
);
}
pSSCtlObject->Release();
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlGetSignerInfo
//
// Synopsis: get the signer info
//
//----------------------------------------------------------------------------
BOOL WINAPI
SSCtlGetSignerInfo (
IN PCCTL_CONTEXT pCtlContext,
OUT PSSCTL_SIGNER_INFO pSignerInfo
)
{
BOOL fResult;
PCERT_INFO pMessageSignerCertInfo = NULL;
DWORD cbData = 0;
fResult = CryptMsgGetParam(
pCtlContext->hCryptMsg,
CMSG_SIGNER_CERT_INFO_PARAM,
0,
NULL,
&cbData
);
if ( fResult == TRUE )
{
pMessageSignerCertInfo = (PCERT_INFO)new BYTE [ cbData ];
if ( pMessageSignerCertInfo != NULL )
{
fResult = CryptMsgGetParam(
pCtlContext->hCryptMsg,
CMSG_SIGNER_CERT_INFO_PARAM,
0,
pMessageSignerCertInfo,
&cbData
);
}
else
{
SetLastError( (DWORD) E_OUTOFMEMORY );
fResult = FALSE;
}
}
if ( fResult == TRUE )
{
pSignerInfo->pMessageSignerCertInfo = pMessageSignerCertInfo;
pSignerInfo->fSignerHashAvailable = FALSE;
}
else
{
delete pMessageSignerCertInfo;
}
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlFreeSignerInfo
//
// Synopsis: free the data in the signer info
//
//----------------------------------------------------------------------------
VOID WINAPI
SSCtlFreeSignerInfo (
IN PSSCTL_SIGNER_INFO pSignerInfo
)
{
delete (LPBYTE)pSignerInfo->pMessageSignerCertInfo;
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlGetSignerChainPathObject
//
// Synopsis: get the signer chain path object
//
//----------------------------------------------------------------------------
BOOL WINAPI
SSCtlGetSignerChainPathObject (
IN PCCHAINPATHOBJECT pSubject,
IN PCCHAINCALLCONTEXT pCallContext,
IN PSSCTL_SIGNER_INFO pSignerInfo,
IN HCERTSTORE hAdditionalStore,
OUT PCCHAINPATHOBJECT* ppSigner,
OUT BOOL *pfNewSigner
)
{
BOOL fResult = TRUE;
PCCERTCHAINENGINE pChainEngine = pSubject->CertObject()->ChainEngine();
PCCERTOBJECTCACHE pCertObjectCache = pChainEngine->CertObjectCache();
PCCERTOBJECT pCertObject = NULL;
PCCERT_CONTEXT pCertContext = NULL;
PCCHAINPATHOBJECT pSigner = NULL;
BOOL fAdditionalStoreUsed = FALSE;
BYTE rgbCertHash[ CHAINHASHLEN ];
*pfNewSigner = FALSE;
if ( pSignerInfo->fSignerHashAvailable == TRUE )
{
pCertObject = pCertObjectCache->FindIssuerObjectByHash(
pSignerInfo->rgbSignerCertHash );
}
if ( pCertObject == NULL )
{
if ( pSignerInfo->fSignerHashAvailable == TRUE )
{
pCertContext = SSCtlFindCertificateInStoreByHash(
pChainEngine->OtherStore(),
pSignerInfo->rgbSignerCertHash
);
if ( ( pCertContext == NULL ) && ( hAdditionalStore != NULL ) )
{
fAdditionalStoreUsed = TRUE;
pCertContext = SSCtlFindCertificateInStoreByHash(
hAdditionalStore,
pSignerInfo->rgbSignerCertHash
);
}
}
if ( pCertContext == NULL )
{
*pfNewSigner = TRUE;
fAdditionalStoreUsed = FALSE;
pCertContext = CertGetSubjectCertificateFromStore(
pChainEngine->OtherStore(),
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
pSignerInfo->pMessageSignerCertInfo
);
}
if ( ( pCertContext == NULL ) && ( hAdditionalStore != NULL ) )
{
fAdditionalStoreUsed = TRUE;
pCertContext = CertGetSubjectCertificateFromStore(
hAdditionalStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
pSignerInfo->pMessageSignerCertInfo
);
}
if ( pCertContext != NULL )
{
DWORD cbData = CHAINHASHLEN;
fResult = CertGetCertificateContextProperty(
pCertContext,
CERT_MD5_HASH_PROP_ID,
rgbCertHash,
&cbData
);
if ( fResult && CHAINHASHLEN != cbData)
{
fResult = FALSE;
SetLastError( (DWORD) E_UNEXPECTED);
}
if ( fResult == TRUE )
{
fResult = ChainCreateCertObject (
fAdditionalStoreUsed ?
CERT_EXTERNAL_ISSUER_OBJECT_TYPE :
CERT_CACHED_ISSUER_OBJECT_TYPE,
pCallContext,
pCertContext,
rgbCertHash,
&pCertObject
);
}
CertFreeCertificateContext( pCertContext );
}
else
{
fResult = FALSE;
SetLastError((DWORD) CRYPT_E_NOT_FOUND);
}
}
if ( fResult )
{
assert(pCertObject);
fResult = ChainCreatePathObject(
pCallContext,
pCertObject,
hAdditionalStore,
&pSigner
);
}
if ( fResult )
{
assert(pSigner);
if ( !pSignerInfo->fSignerHashAvailable || *pfNewSigner )
{
memcpy(
pSignerInfo->rgbSignerCertHash,
rgbCertHash,
CHAINHASHLEN
);
pSignerInfo->fSignerHashAvailable = TRUE;
}
}
if ( pCertObject != NULL )
{
pCertObject->Release();
}
*ppSigner = pSigner;
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlFindCertificateInStoreByHash
//
// Synopsis: find certificate in store by hash
//
//----------------------------------------------------------------------------
PCCERT_CONTEXT WINAPI
SSCtlFindCertificateInStoreByHash (
IN HCERTSTORE hStore,
IN BYTE rgbHash [ CHAINHASHLEN]
)
{
CRYPT_HASH_BLOB HashBlob;
HashBlob.cbData = CHAINHASHLEN;
HashBlob.pbData = rgbHash;
return( CertFindCertificateInStore(
hStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_MD5_HASH,
&HashBlob,
NULL
) );
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlGetCtlTrustStatus
//
// Synopsis: get the trust status for the CTL
//
//----------------------------------------------------------------------------
VOID WINAPI
SSCtlGetCtlTrustStatus (
IN PCCTL_CONTEXT pCtlContext,
IN BOOL fSignatureValid,
IN LPFILETIME pTime,
IN PCERT_USAGE_MATCH pRequestedUsage,
IN OUT PCERT_TRUST_STATUS pStatus
)
{
FILETIME NoTime;
CERT_TRUST_STATUS UsageStatus;
memset( &NoTime, 0, sizeof( NoTime ) );
if ( fSignatureValid == FALSE )
{
pStatus->dwErrorStatus |= CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID;
}
if ( ( CompareFileTime(
pTime,
&pCtlContext->pCtlInfo->ThisUpdate
) < 0 ) ||
( ( ( CompareFileTime(
&NoTime,
&pCtlContext->pCtlInfo->NextUpdate
) != 0 ) &&
( CompareFileTime(
pTime,
&pCtlContext->pCtlInfo->NextUpdate
) > 0 ) ) ) )
{
pStatus->dwErrorStatus |= CERT_TRUST_CTL_IS_NOT_TIME_VALID;
}
memset( &UsageStatus, 0, sizeof( UsageStatus ) );
ChainGetUsageStatus(
(PCERT_ENHKEY_USAGE)&pRequestedUsage->Usage,
(PCERT_ENHKEY_USAGE)&pCtlContext->pCtlInfo->SubjectUsage,
pRequestedUsage->dwType,
&UsageStatus
);
if ( UsageStatus.dwErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE )
{
pStatus->dwErrorStatus |= CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE;
}
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlPopulateCacheFromCertStore
//
// Synopsis: populate the SS CTL object cache from certificate store CTLs
//
//----------------------------------------------------------------------------
BOOL WINAPI
SSCtlPopulateCacheFromCertStore (
IN PCCERTCHAINENGINE pChainEngine,
IN OPTIONAL HCERTSTORE hStore
)
{
BOOL fResult;
BOOL fAdditionalStore = TRUE;
PCCTL_CONTEXT pCtlContext = NULL;
BYTE rgbCtlHash[ CHAINHASHLEN ];
PCSSCTLOBJECT pSSCtlObject;
PCSSCTLOBJECTCACHE pSSCtlObjectCache;
pSSCtlObjectCache = pChainEngine->SSCtlObjectCache();
if ( hStore == NULL )
{
hStore = pChainEngine->TrustStore();
fAdditionalStore = FALSE;
}
while ( ( pCtlContext = CertEnumCTLsInStore(
hStore,
pCtlContext
) ) != NULL )
{
DWORD cbData = CHAINHASHLEN;
fResult = CertGetCTLContextProperty(
pCtlContext,
CERT_MD5_HASH_PROP_ID,
rgbCtlHash,
&cbData
);
if ( fResult && CHAINHASHLEN != cbData)
{
fResult = FALSE;
SetLastError( (DWORD) E_UNEXPECTED);
}
if ( fResult == TRUE )
{
pSSCtlObject = pSSCtlObjectCache->FindObjectByHash( rgbCtlHash );
if ( pSSCtlObject == NULL )
{
fResult = SSCtlCreateCtlObject(
pChainEngine,
pCtlContext,
FALSE, // fAdditionalStore
&pSSCtlObject
);
}
else
{
pSSCtlObject->Release();
fResult = FALSE;
}
if ( fResult == TRUE )
{
fResult = pSSCtlObjectCache->AddObject( pSSCtlObject, FALSE );
// NOTE: Since fDuplicate == FALSE this should never fail
assert( fResult == TRUE );
pSSCtlObject->Release();
}
}
}
return( TRUE );
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlCreateCtlObject
//
// Synopsis: create an SS CTL Object
//
//----------------------------------------------------------------------------
BOOL WINAPI
SSCtlCreateCtlObject (
IN PCCERTCHAINENGINE pChainEngine,
IN PCCTL_CONTEXT pCtlContext,
IN BOOL fAdditionalStore,
OUT PCSSCTLOBJECT* ppSSCtlObject
)
{
BOOL fResult = TRUE;
PCSSCTLOBJECT pSSCtlObject;
pSSCtlObject = new CSSCtlObject(
pChainEngine, pCtlContext, fAdditionalStore, fResult );
if ( pSSCtlObject == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
fResult = FALSE;
}
else if ( fResult == TRUE )
{
*ppSSCtlObject = pSSCtlObject;
}
else
{
delete pSSCtlObject;
}
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlEnumObjectsWalkFn
//
// Synopsis: object enumerator walk function
//
//----------------------------------------------------------------------------
BOOL WINAPI
SSCtlEnumObjectsWalkFn (
IN LPVOID pvParameter,
IN HLRUENTRY hEntry
)
{
PSSCTL_ENUM_OBJECTS_DATA pEnumData = (PSSCTL_ENUM_OBJECTS_DATA)pvParameter;
return( ( *pEnumData->pfnEnumObjects )(
pEnumData->pvEnumParameter,
(PCSSCTLOBJECT)I_CryptGetLruEntryData( hEntry )
) );
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlCreateObjectCache
//
// Synopsis: create the SS CTL object cache
//
//----------------------------------------------------------------------------
BOOL WINAPI
SSCtlCreateObjectCache (
OUT PCSSCTLOBJECTCACHE* ppSSCtlObjectCache
)
{
BOOL fResult = TRUE;
PCSSCTLOBJECTCACHE pSSCtlObjectCache;
pSSCtlObjectCache = new CSSCtlObjectCache( fResult );
if ( pSSCtlObjectCache == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
fResult = FALSE;
}
else if ( fResult == TRUE )
{
*ppSSCtlObjectCache = pSSCtlObjectCache;
}
else
{
delete pSSCtlObjectCache;
}
return( fResult );
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlFreeObjectCache
//
// Synopsis: free the object cache
//
//----------------------------------------------------------------------------
VOID WINAPI
SSCtlFreeObjectCache (
IN PCSSCTLOBJECTCACHE pSSCtlObjectCache
)
{
delete pSSCtlObjectCache;
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlFreeTrustListInfo
//
// Synopsis: free the trust list info
//
//----------------------------------------------------------------------------
VOID WINAPI
SSCtlFreeTrustListInfo (
IN PCERT_TRUST_LIST_INFO pTrustListInfo
)
{
CertFreeCTLContext( pTrustListInfo->pCtlContext );
delete pTrustListInfo;
}
//+---------------------------------------------------------------------------
//
// Function: SSCtlAllocAndCopyTrustListInfo
//
// Synopsis: allocate and copy the trust list info
//
//----------------------------------------------------------------------------
BOOL WINAPI
SSCtlAllocAndCopyTrustListInfo (
IN PCERT_TRUST_LIST_INFO pTrustListInfo,
OUT PCERT_TRUST_LIST_INFO* ppTrustListInfo
)
{
PCERT_TRUST_LIST_INFO pCopyTrustListInfo;
pCopyTrustListInfo = new CERT_TRUST_LIST_INFO;
if ( pCopyTrustListInfo == NULL )
{
SetLastError( (DWORD) E_OUTOFMEMORY );
return( FALSE );
}
pCopyTrustListInfo->cbSize = sizeof( CERT_TRUST_LIST_INFO );
pCopyTrustListInfo->pCtlContext = CertDuplicateCTLContext(
pTrustListInfo->pCtlContext
);
pCopyTrustListInfo->pCtlEntry = pTrustListInfo->pCtlEntry;
*ppTrustListInfo = pCopyTrustListInfo;
return( TRUE );
}
//+-------------------------------------------------------------------------
// Retrieve a newer and time valid CTL at one of the NextUpdate Urls
//
// Leaves the engine's critical section to do the URL
// fetching. If the engine was touched by another thread,
// it fails with LastError set to ERROR_CAN_NOT_COMPLETE.
//
// Assumption: Chain engine is locked once in the calling thread.
//--------------------------------------------------------------------------
BOOL
WINAPI
SSCtlRetrieveCtlUrl(
IN PCCERTCHAINENGINE pChainEngine,
IN PCCHAINCALLCONTEXT pCallContext,
IN OUT PCRYPT_URL_ARRAY pNextUpdateUrlArray,
IN DWORD dwRetrievalFlags,
IN OUT PCCTL_CONTEXT *ppCtl,
IN OUT BOOL *pfNewerCtl,
IN OUT BOOL *pfTimeValid
)
{
BOOL fResult;
DWORD i;
// Loop through Urls and try to retrieve a newer and time valid CTL
for (i = 0; i < pNextUpdateUrlArray->cUrl; i++) {
PCCTL_CONTEXT pNewCtl = NULL;
LPWSTR pwszUrl = NULL;
DWORD cbUrl;
// Do URL fetching outside of the engine's critical section
// Need to make a copy of the Url string. pNextUpdateUrlArray
// can be modified by another thread outside of the critical section.
cbUrl = (wcslen(pNextUpdateUrlArray->rgwszUrl[i]) + 1) * sizeof(WCHAR);
pwszUrl = (LPWSTR) PkiNonzeroAlloc(cbUrl);
if (NULL == pwszUrl)
goto OutOfMemory;
memcpy(pwszUrl, pNextUpdateUrlArray->rgwszUrl[i], cbUrl);
pCallContext->ChainEngine()->UnlockEngine();
fResult = ChainRetrieveObjectByUrlW(
pwszUrl,
CONTEXT_OID_CTL,
dwRetrievalFlags |
CRYPT_LDAP_SCOPE_BASE_ONLY_RETRIEVAL |
CRYPT_STICKY_CACHE_RETRIEVAL,
pCallContext->ChainPara()->dwUrlRetrievalTimeout,
(LPVOID *) &pNewCtl,
NULL, // hAsyncRetrieve
NULL, // pCredentials
NULL, // pvVerify
NULL // pAuxInfo
);
pCallContext->ChainEngine()->LockEngine();
PkiFree(pwszUrl);
if (pCallContext->IsTouchedEngine()) {
if (pNewCtl)
CertFreeCTLContext(pNewCtl);
goto TouchedDuringUrlRetrieval;
}
if (fResult) {
PCCTL_CONTEXT pOldCtl;
assert(pNewCtl);
pOldCtl = *ppCtl;
if (0 < CompareFileTime(&pNewCtl->pCtlInfo->ThisUpdate,
&pOldCtl->pCtlInfo->ThisUpdate)) {
FILETIME CurrentTime;
// Move us to the head of the Url list
DWORD j;
LPWSTR pwszUrl = pNextUpdateUrlArray->rgwszUrl[i];
for (j = i; 0 < j; j--) {
pNextUpdateUrlArray->rgwszUrl[j] =
pNextUpdateUrlArray->rgwszUrl[j - 1];
}
pNextUpdateUrlArray->rgwszUrl[0] = pwszUrl;
*pfNewerCtl = TRUE;
CertFreeCTLContext(pOldCtl);
*ppCtl = pNewCtl;
pCallContext->CurrentTime(&CurrentTime);
if (I_CryptIsZeroFileTime(&pNewCtl->pCtlInfo->NextUpdate) ||
0 < CompareFileTime(&pNewCtl->pCtlInfo->NextUpdate,
&CurrentTime)) {
*pfTimeValid = TRUE;
break;
}
} else
CertFreeCTLContext(pNewCtl);
}
}
fResult = TRUE;
CommonReturn:
return fResult;
ErrorReturn:
fResult = FALSE;
goto CommonReturn;
SET_ERROR(TouchedDuringUrlRetrieval, ERROR_CAN_NOT_COMPLETE)
TRACE_ERROR(OutOfMemory)
}
//+-------------------------------------------------------------------------
// Update Ctl Object Enum Function
//
// Leaves the engine's critical section to do the URL
// fetching. If the engine was touched by another thread,
// it fails with LastError set to ERROR_CAN_NOT_COMPLETE.
//
// Assumption: Chain engine is locked once in the calling thread.
//--------------------------------------------------------------------------
BOOL
WINAPI
SSCtlUpdateCtlObjectEnumFn(
IN LPVOID pvPara,
IN PCSSCTLOBJECT pSSCtlObject
)
{
BOOL fTouchResult = TRUE;
PSSCTL_UPDATE_CTL_OBJ_PARA pPara = (PSSCTL_UPDATE_CTL_OBJ_PARA) pvPara;
FILETIME CurrentTime;
FILETIME UpdateTime;
PCCTL_CONTEXT pRetrieveCtl = NULL;
BOOL fTimeValid = FALSE;
BOOL fNewerCtl = FALSE;
PCRYPT_URL_ARRAY pNextUpdateUrlArray;
if (!pSSCtlObject->HasNextUpdateUrl(&UpdateTime))
return TRUE;
pPara->pCallContext->CurrentTime(&CurrentTime);
if (0 < CompareFileTime(&UpdateTime, &CurrentTime))
goto CommonReturn;
pRetrieveCtl = CertDuplicateCTLContext(pSSCtlObject->CtlContext());
pNextUpdateUrlArray = pSSCtlObject->NextUpdateUrlArray();
SSCtlRetrieveCtlUrl(
pPara->pChainEngine,
pPara->pCallContext,
pNextUpdateUrlArray,
CRYPT_CACHE_ONLY_RETRIEVAL,
&pRetrieveCtl,
&fNewerCtl,
&fTimeValid
);
if (pPara->pCallContext->IsTouchedEngine()) {
fTouchResult = FALSE;
goto TouchedDuringUrlRetrieval;
}
if (!fTimeValid && pPara->pCallContext->IsOnline()) {
SSCtlRetrieveCtlUrl(
pPara->pChainEngine,
pPara->pCallContext,
pNextUpdateUrlArray,
CRYPT_WIRE_ONLY_RETRIEVAL,
&pRetrieveCtl,
&fNewerCtl,
&fTimeValid
);
if (pPara->pCallContext->IsTouchedEngine()) {
fTouchResult = FALSE;
goto TouchedDuringUrlRetrieval;
}
if (!fNewerCtl)
pSSCtlObject->SetOffline(&CurrentTime, &UpdateTime);
}
if (fNewerCtl) {
PSSCTL_UPDATE_CTL_OBJ_ENTRY pEntry;
pSSCtlObject->SetOnline();
pEntry = new SSCTL_UPDATE_CTL_OBJ_ENTRY;
if (NULL == pEntry)
goto OutOfMemory;
if (!SSCtlCreateCtlObject(
pPara->pChainEngine,
pRetrieveCtl,
FALSE, // fAdditionalStore
&pEntry->pSSCtlObjectAdd
)) {
delete pEntry;
goto CreateCtlObjectError;
}
pEntry->pSSCtlObjectRemove = pSSCtlObject;
pEntry->pNext = pPara->pEntry;
pPara->pEntry = pEntry;
}
CommonReturn:
if (!fNewerCtl) {
if (I_CryptIsZeroFileTime(&pPara->UpdateTime) ||
0 > CompareFileTime(&UpdateTime, &pPara->UpdateTime))
pPara->UpdateTime = UpdateTime;
}
if (pRetrieveCtl)
CertFreeCTLContext(pRetrieveCtl);
return fTouchResult;
ErrorReturn:
fNewerCtl = FALSE;
goto CommonReturn;
SET_ERROR(OutOfMemory, E_OUTOFMEMORY)
TRACE_ERROR(CreateCtlObjectError)
SET_ERROR(TouchedDuringUrlRetrieval, ERROR_CAN_NOT_COMPLETE)
}