|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows NT Security
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: chain.cpp
//
// Contents: Certificate Chaining Infrastructure
//
// History: 15-Jan-98 kirtd Created
//
//----------------------------------------------------------------------------
#include <global.hxx>
#include <dbgdef.h>
//+===========================================================================
// CCertObject methods
//============================================================================
//+---------------------------------------------------------------------------
//
// Member: CCertObject::CCertObject, public
//
// Synopsis: Constructor
//
// Leaves the engine's critical section to create an object of
// dwObjectType = CERT_END_OBJECT_TYPE. For a self-signed root
// may also leave the critical section to retrieve and validate
// the AuthRoot Auto Update CTL and add such a root to the
// AuthRoot store.
//
// Assumption: Chain engine is locked once in the calling thread.
//
//----------------------------------------------------------------------------
CCertObject::CCertObject ( IN DWORD dwObjectType, IN PCCHAINCALLCONTEXT pCallContext, IN PCCERT_CONTEXT pCertContext, IN BYTE rgbCertHash[CHAINHASHLEN], OUT BOOL& rfResult ) { BOOL fLocked = TRUE; CRYPT_DATA_BLOB DataBlob; DWORD cbData;
if (CERT_END_OBJECT_TYPE == dwObjectType) { pCallContext->ChainEngine()->UnlockEngine(); fLocked = FALSE; }
m_dwObjectType = dwObjectType; m_cRefs = 1;
// NOTE: The chain engine is NOT addref'd
m_pChainEngine = pCallContext->ChainEngine();
m_dwIssuerMatchFlags = 0; m_dwCachedMatchFlags = 0; m_dwIssuerStatusFlags = 0; m_dwInfoFlags = 0; m_pCtlCacheHead = NULL; m_pCertContext = CertDuplicateCertificateContext( pCertContext ); memset(&m_PoliciesInfo, 0, sizeof(m_PoliciesInfo)); m_pBasicConstraintsInfo = NULL; m_pKeyUsage = NULL; m_pIssuerNameConstraintsInfo = NULL; m_fAvailableSubjectNameConstraintsInfo = FALSE; memset(&m_SubjectNameConstraintsInfo, 0, sizeof(m_SubjectNameConstraintsInfo)); m_pAuthKeyIdentifier = NULL; // m_ObjectIdentifier;
memcpy(m_rgbCertHash, rgbCertHash, CHAINHASHLEN); m_cbKeyIdentifier = 0; m_pbKeyIdentifier = NULL; // m_rgbPublicKeyHash[ CHAINHASHLEN ];
// m_rgbIssuerPublicKeyHash[ CHAINHASHLEN ];
// m_rgbIssuerExactMatchHash[ CHAINHASHLEN ];
// m_rgbIssuerNameMatchHash[ CHAINHASHLEN ];
m_hHashEntry = NULL; m_hIdentifierEntry = NULL; m_hSubjectNameEntry = NULL; m_hKeyIdEntry = NULL; m_hPublicKeyHashEntry = NULL;
m_hEndHashEntry = NULL;
if (!CertGetCertificateContextProperty( pCertContext, CERT_KEY_IDENTIFIER_PROP_ID, NULL, &m_cbKeyIdentifier )) goto GetKeyIdentifierPropertyError; m_pbKeyIdentifier = new BYTE [ m_cbKeyIdentifier ]; if (NULL == m_pbKeyIdentifier) goto OutOfMemory; if (!CertGetCertificateContextProperty( pCertContext, CERT_KEY_IDENTIFIER_PROP_ID, m_pbKeyIdentifier, &m_cbKeyIdentifier )) goto GetKeyIdentifierPropertyError;
cbData = CHAINHASHLEN; if (!CertGetCertificateContextProperty( pCertContext, CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID, m_rgbPublicKeyHash, &cbData ) || CHAINHASHLEN != cbData) goto GetSubjectPublicKeyHashPropertyError;
cbData = CHAINHASHLEN; if (CertGetCertificateContextProperty( pCertContext, CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID, m_rgbIssuerPublicKeyHash, &cbData ) && CHAINHASHLEN == cbData) m_dwIssuerStatusFlags |= CERT_ISSUER_PUBKEY_FLAG;
ChainGetPoliciesInfo(pCertContext, &m_PoliciesInfo);
if (!ChainGetBasicConstraintsInfo(pCertContext, &m_pBasicConstraintsInfo)) m_dwInfoFlags |= CHAIN_INVALID_BASIC_CONSTRAINTS_INFO_FLAG;
if (!ChainGetKeyUsage(pCertContext, &m_pKeyUsage)) m_dwInfoFlags |= CHAIN_INVALID_KEY_USAGE_FLAG;
if (!ChainGetIssuerNameConstraintsInfo(pCertContext, &m_pIssuerNameConstraintsInfo)) m_dwInfoFlags |= CHAIN_INVALID_ISSUER_NAME_CONSTRAINTS_INFO_FLAG;
if (CERT_CACHED_ISSUER_OBJECT_TYPE == dwObjectType) { DataBlob.cbData = CHAINHASHLEN; DataBlob.pbData = m_rgbCertHash; if (!I_CryptCreateLruEntry( m_pChainEngine->CertObjectCache()->HashIndex(), &DataBlob, this, &m_hHashEntry )) goto CreateHashLruEntryError;
// Need to double check this, only needed for issuer caching ???
ChainCreateCertificateObjectIdentifier( &pCertContext->pCertInfo->Issuer, &pCertContext->pCertInfo->SerialNumber, m_ObjectIdentifier );
DataBlob.cbData = sizeof( CERT_OBJECT_IDENTIFIER ); DataBlob.pbData = m_ObjectIdentifier; if (!I_CryptCreateLruEntry( m_pChainEngine->CertObjectCache()->IdentifierIndex(), &DataBlob, this, &m_hIdentifierEntry )) goto CreateIdentifierLruEntryError;
DataBlob.cbData = pCertContext->pCertInfo->Subject.cbData; DataBlob.pbData = pCertContext->pCertInfo->Subject.pbData; if (!I_CryptCreateLruEntry( m_pChainEngine->CertObjectCache()->SubjectNameIndex(), &DataBlob, this, &m_hSubjectNameEntry )) goto CreateSubjectNameLruEntryError;
DataBlob.cbData = m_cbKeyIdentifier; DataBlob.pbData = m_pbKeyIdentifier; if (!I_CryptCreateLruEntry( m_pChainEngine->CertObjectCache()->KeyIdIndex(), &DataBlob, this, &m_hKeyIdEntry )) goto CreateKeyIdLruEntryError;
DataBlob.cbData = CHAINHASHLEN; DataBlob.pbData = m_rgbPublicKeyHash; if (!I_CryptCreateLruEntry( m_pChainEngine->CertObjectCache()->PublicKeyHashIndex(), &DataBlob, this, &m_hPublicKeyHashEntry )) goto CreatePublicKeyHashLruEntryError; }
ChainGetIssuerMatchInfo( pCertContext, &m_dwIssuerMatchFlags, &m_pAuthKeyIdentifier );
ChainGetSelfSignedStatus(pCallContext, this, &m_dwIssuerStatusFlags);
if (m_dwIssuerStatusFlags & CERT_ISSUER_SELF_SIGNED_FLAG) { //
// NOTE: This means that only self-signed roots are supported
//
if (!fLocked) { pCallContext->ChainEngine()->LockEngine(); fLocked = TRUE; }
ChainGetRootStoreStatus( m_pChainEngine->RootStore(), m_pChainEngine->RealRootStore(), rgbCertHash, &m_dwIssuerStatusFlags );
if (!(m_dwIssuerStatusFlags & CERT_ISSUER_TRUSTED_ROOT_FLAG)) { if (!ChainGetAuthRootAutoUpdateStatus( pCallContext, this, &m_dwIssuerStatusFlags )) goto AuthRootAutoUpdateError; }
if (!(m_dwIssuerStatusFlags & CERT_ISSUER_TRUSTED_ROOT_FLAG)) { // Get all cached CTLs we are a member of
CERT_OBJECT_CTL_CACHE_ENUM_DATA EnumData;
memset(&EnumData, 0, sizeof(EnumData)); EnumData.fResult = TRUE; EnumData.pCertObject = this;
m_pChainEngine->SSCtlObjectCache()->EnumObjects( ChainFillCertObjectCtlCacheEnumFn, &EnumData );
if (!EnumData.fResult) { SetLastError(EnumData.dwLastError); goto FillCertObjectCtlCacheError; } } }
rfResult = TRUE;
CommonReturn: if (!fLocked) pCallContext->ChainEngine()->LockEngine(); return;
ErrorReturn: rfResult = FALSE; goto CommonReturn;
TRACE_ERROR(GetKeyIdentifierPropertyError) SET_ERROR(OutOfMemory, E_OUTOFMEMORY) TRACE_ERROR(GetSubjectPublicKeyHashPropertyError) TRACE_ERROR(CreateHashLruEntryError) TRACE_ERROR(CreateIdentifierLruEntryError) TRACE_ERROR(CreateSubjectNameLruEntryError) TRACE_ERROR(CreateKeyIdLruEntryError) TRACE_ERROR(CreatePublicKeyHashLruEntryError) TRACE_ERROR(AuthRootAutoUpdateError) TRACE_ERROR(FillCertObjectCtlCacheError) }
//+---------------------------------------------------------------------------
//
// Member: CCertObject::~CCertObject, public
//
// Synopsis: Destructor
//
//----------------------------------------------------------------------------
CCertObject::~CCertObject () { if ( m_hKeyIdEntry != NULL ) { I_CryptReleaseLruEntry( m_hKeyIdEntry ); }
if ( m_hSubjectNameEntry != NULL ) { I_CryptReleaseLruEntry( m_hSubjectNameEntry ); }
if ( m_hIdentifierEntry != NULL ) { I_CryptReleaseLruEntry( m_hIdentifierEntry ); }
if ( m_hPublicKeyHashEntry != NULL ) { I_CryptReleaseLruEntry( m_hPublicKeyHashEntry ); }
if ( m_hHashEntry != NULL ) { I_CryptReleaseLruEntry( m_hHashEntry ); }
if ( m_hEndHashEntry != NULL ) { I_CryptReleaseLruEntry( m_hEndHashEntry ); }
ChainFreeCertObjectCtlCache(m_pCtlCacheHead);
delete m_pbKeyIdentifier; ChainFreeAuthorityKeyIdentifier( m_pAuthKeyIdentifier ); ChainFreePoliciesInfo( &m_PoliciesInfo ); ChainFreeBasicConstraintsInfo( m_pBasicConstraintsInfo ); ChainFreeKeyUsage( m_pKeyUsage ); ChainFreeIssuerNameConstraintsInfo( m_pIssuerNameConstraintsInfo ); ChainFreeSubjectNameConstraintsInfo( &m_SubjectNameConstraintsInfo ); CertFreeCertificateContext( m_pCertContext ); }
//+---------------------------------------------------------------------------
//
// Member: CCertObject::CacheEndObject, public
//
// Synopsis: Convert a CERT_END_OBJECT_TYPE to a CERT_CACHED_END_OBJECT_TYPE.
//
//----------------------------------------------------------------------------
BOOL CCertObject::CacheEndObject( IN PCCHAINCALLCONTEXT pCallContext ) { BOOL fResult; CRYPT_DATA_BLOB DataBlob;
assert(CERT_END_OBJECT_TYPE == m_dwObjectType);
DataBlob.cbData = CHAINHASHLEN; DataBlob.pbData = m_rgbCertHash; fResult = I_CryptCreateLruEntry( m_pChainEngine->CertObjectCache()->EndHashIndex(), &DataBlob, this, &m_hEndHashEntry );
if (fResult) m_dwObjectType = CERT_CACHED_END_OBJECT_TYPE;
return fResult; }
//+---------------------------------------------------------------------------
//
// Member: CCertObject::SubjectNameConstraintsInfo, public
//
// Synopsis: return the subject name constraints info
//
// allocation and getting of info is deferred until the
// first name constraint check is done.
//
// Assumption: chain engine isn't locked upon entry.
//
//----------------------------------------------------------------------------
PCHAIN_SUBJECT_NAME_CONSTRAINTS_INFO CCertObject::SubjectNameConstraintsInfo () { if (!m_fAvailableSubjectNameConstraintsInfo) { CHAIN_SUBJECT_NAME_CONSTRAINTS_INFO Info;
memset(&Info, 0, sizeof(Info));
ChainGetSubjectNameConstraintsInfo(m_pCertContext, &Info);
// Must do the update while holding the engine's critical section
m_pChainEngine->LockEngine();
if (m_fAvailableSubjectNameConstraintsInfo) // Another thread already did the update
ChainFreeSubjectNameConstraintsInfo(&Info); else { memcpy(&m_SubjectNameConstraintsInfo, &Info, sizeof(m_SubjectNameConstraintsInfo));
// Must be set last!!!
m_fAvailableSubjectNameConstraintsInfo = TRUE; } m_pChainEngine->UnlockEngine(); }
return &m_SubjectNameConstraintsInfo; }
//+---------------------------------------------------------------------------
//
// Member: CCertObject::GetIssuerExactMatchHash, public
//
// Synopsis: if the cert has an Authority Key Info extension with
// the optional issuer and serial number, returns the count and
// pointer to the MD5 hash of the issuer name and serial number.
// Otherwise, pMatchHash->cbData is set to 0.
//
// MD5 hash calculation is deferred until the first call.
//
// Assumption: Chain engine is locked once in the calling thread.
//
//----------------------------------------------------------------------------
VOID CCertObject::GetIssuerExactMatchHash( OUT PCRYPT_DATA_BLOB pMatchHash ) { if (!(m_dwIssuerStatusFlags & CERT_ISSUER_EXACT_MATCH_HASH_FLAG)) { PCERT_AUTHORITY_KEY_ID_INFO pAKI = m_pAuthKeyIdentifier;
if (pAKI && 0 != pAKI->CertIssuer.cbData && 0 != pAKI->CertSerialNumber.cbData) { ChainCreateCertificateObjectIdentifier( &pAKI->CertIssuer, &pAKI->CertSerialNumber, m_rgbIssuerExactMatchHash ); m_dwIssuerStatusFlags |= CERT_ISSUER_EXACT_MATCH_HASH_FLAG; } else { pMatchHash->cbData = 0; pMatchHash->pbData = NULL; return; } } // else
// We have already calculated the MD5 hash
pMatchHash->cbData = CHAINHASHLEN; pMatchHash->pbData = m_rgbIssuerExactMatchHash; }
//+---------------------------------------------------------------------------
//
// Member: CCertObject::GetIssuerKeyMatchHash, public
//
// Synopsis: if the cert has an Authority Key Info extension with
// the optional key id, returns the key id.
// Otherwise, pMatchHash->cbData is set to 0.
//
//----------------------------------------------------------------------------
VOID CCertObject::GetIssuerKeyMatchHash( OUT PCRYPT_DATA_BLOB pMatchHash ) { PCERT_AUTHORITY_KEY_ID_INFO pAKI = m_pAuthKeyIdentifier;
if (pAKI) *pMatchHash = pAKI->KeyId; else { pMatchHash->cbData = 0; pMatchHash->pbData = NULL; } }
//+---------------------------------------------------------------------------
//
// Member: CCertObject::GetIssuerNameMatchHash, public
//
// Synopsis: if the cert has an issuer name, returns the count and
// pointer to the MD5 hash of the issuer name.
// Otherwise, pMatchHash->cbData is set to 0.
//
// MD5 hash calculation is deferred until the first call.
//
// Assumption: Chain engine is locked once in the calling thread.
//
//----------------------------------------------------------------------------
VOID CCertObject::GetIssuerNameMatchHash( OUT PCRYPT_DATA_BLOB pMatchHash ) { if (!(m_dwIssuerStatusFlags & CERT_ISSUER_NAME_MATCH_HASH_FLAG)) { PCERT_INFO pCertInfo = m_pCertContext->pCertInfo;
if (0 != pCertInfo->Issuer.cbData) { MD5_CTX md5ctx;
MD5Init( &md5ctx ); MD5Update( &md5ctx, pCertInfo->Issuer.pbData, pCertInfo->Issuer.cbData ); MD5Final( &md5ctx );
assert(CHAINHASHLEN == MD5DIGESTLEN); memcpy(m_rgbIssuerNameMatchHash, md5ctx.digest, CHAINHASHLEN);
m_dwIssuerStatusFlags |= CERT_ISSUER_NAME_MATCH_HASH_FLAG; } else { pMatchHash->cbData = 0; pMatchHash->pbData = NULL; return; } }
pMatchHash->cbData = CHAINHASHLEN; pMatchHash->pbData = m_rgbIssuerNameMatchHash; }
//+===========================================================================
// CChainPathObject methods
//============================================================================
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::CChainPathObject, public
//
// Synopsis: Constructor
//
// Once successfully added to the call context cache, rfAddedToCreationCache
// is set. This object will be deleted when CChainCallContext gets destroyed.
//
// Since this object is per call, no AddRef'ing is required.
//
//----------------------------------------------------------------------------
CChainPathObject::CChainPathObject ( IN PCCHAINCALLCONTEXT pCallContext, IN BOOL fCyclic, IN LPVOID pvObject, // fCyclic : pPathObject ? pCertObject
IN OPTIONAL HCERTSTORE hAdditionalStore, OUT BOOL& rfResult, OUT BOOL& rfAddedToCreationCache ) { PCCERTOBJECT pCertObject; PCCHAINPATHOBJECT pPathObject; DWORD dwIssuerStatusFlags;
rfAddedToCreationCache = FALSE;
if (fCyclic) { pPathObject = (PCCHAINPATHOBJECT) pvObject; pCertObject = pPathObject->CertObject(); } else { pPathObject = NULL; pCertObject = (PCCERTOBJECT) pvObject; }
m_pCertObject = pCertObject; pCertObject->AddRef(); memset( &m_TrustStatus, 0, sizeof( m_TrustStatus ) ); m_dwPass1Quality = 0; m_dwChainIndex = 0; m_dwElementIndex = 0; m_pDownIssuerElement = NULL; m_pDownPathObject = NULL; m_pUpIssuerElement = NULL; m_fHasAdditionalStatus = FALSE; memset( &m_AdditionalStatus, 0, sizeof( m_AdditionalStatus ) ); m_fHasRevocationInfo = FALSE; memset( &m_RevocationInfo, 0, sizeof( m_RevocationInfo ) ); memset( &m_RevocationCrlInfo, 0, sizeof( m_RevocationCrlInfo ) ); m_pIssuerList = NULL; m_pwszExtendedErrorInfo = NULL; m_fCompleted = FALSE;
if (!ChainCreateIssuerList( this, &m_pIssuerList )) goto CreateIssuerListError;
if (!pCallContext->AddPathObjectToCreationCache( this )) goto AddPathObjectToCreationCacheError; rfAddedToCreationCache = TRUE;
if (fCyclic) { m_TrustStatus = pPathObject->m_TrustStatus; m_TrustStatus.dwInfoStatus |= ChainGetMatchInfoStatusForNoIssuer( pCertObject->IssuerMatchFlags()); m_TrustStatus.dwErrorStatus |= CERT_TRUST_IS_CYCLIC; goto SuccessReturn; }
dwIssuerStatusFlags = pCertObject->IssuerStatusFlags(); if (dwIssuerStatusFlags & CERT_ISSUER_SELF_SIGNED_FLAG) { m_TrustStatus.dwInfoStatus |= CERT_TRUST_IS_SELF_SIGNED; ChainGetMatchInfoStatus(pCertObject, pCertObject, &m_TrustStatus.dwInfoStatus); m_dwPass1Quality |= CERT_QUALITY_COMPLETE_CHAIN | CERT_QUALITY_NOT_CYCLIC;
if (dwIssuerStatusFlags & CERT_ISSUER_VALID_SIGNATURE_FLAG) { m_dwPass1Quality |= CERT_QUALITY_SIGNATURE_VALID; } else { m_TrustStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_SIGNATURE_VALID; m_TrustStatus.dwInfoStatus &= ~CERT_TRUST_HAS_PREFERRED_ISSUER; }
if (dwIssuerStatusFlags & CERT_ISSUER_TRUSTED_ROOT_FLAG) { m_dwPass1Quality |= CERT_QUALITY_HAS_TRUSTED_ROOT;
// Check if we have a time valid root. This is an extra
// check necessary to determine if we will need to do
// AuthRoot Auto Update.
FILETIME RequestedTime; PCERT_INFO pCertInfo = pCertObject->CertContext()->pCertInfo;
pCallContext->RequestedTime(&RequestedTime); if ((0 == (pCallContext->CallFlags() & CERT_CHAIN_TIMESTAMP_TIME)) && 0 == CertVerifyTimeValidity(&RequestedTime, pCertInfo)) { m_dwPass1Quality |= CERT_QUALITY_HAS_TIME_VALID_TRUSTED_ROOT; } else { // Use current time for timestamping or try again using the
// current time. This is necessary for cross certificate
// chains.
FILETIME CurrentTime;
pCallContext->CurrentTime(&CurrentTime); if (0 == CertVerifyTimeValidity(&CurrentTime, pCertInfo)) { m_dwPass1Quality |= CERT_QUALITY_HAS_TIME_VALID_TRUSTED_ROOT; } } } else { m_TrustStatus.dwErrorStatus |= CERT_TRUST_IS_UNTRUSTED_ROOT;
if (!FindAndAddCtlIssuersFromCache(pCallContext, hAdditionalStore)) goto FindAndCtlIssuersFromCacheError;
if (hAdditionalStore) { if (!FindAndAddCtlIssuersFromAdditionalStore( pCallContext, hAdditionalStore )) goto FindAndCtlIssuersFromAdditionalStoreError; }
if (!(dwIssuerStatusFlags & CERT_ISSUER_VALID_SIGNATURE_FLAG)) m_dwPass1Quality &= ~CERT_QUALITY_SIGNATURE_VALID; } } else { if (!FindAndAddIssuers ( pCallContext, hAdditionalStore, NULL // hIssuerUrlStore
)) goto FindAndAddIssuersError;
dwIssuerStatusFlags = pCertObject->IssuerStatusFlags(); if (m_pIssuerList->IsEmpty() || (!(dwIssuerStatusFlags & CERT_ISSUER_URL_FLAG) && (!(dwIssuerStatusFlags & CERT_ISSUER_VALID_SIGNATURE_FLAG) || !(m_dwPass1Quality & CERT_QUALITY_SIGNATURE_VALID)))) { DWORD i;
// Try the following 2 URL cases:
// 0 - AIA cache
// 1 - AIA wire
// Continue through the cases until finding a "good" issuer.
for (i = 0; i <= 1; i++) { HCERTSTORE hIssuerUrlStore = NULL; DWORD dwRetrievalFlags;
if (0 == i) dwRetrievalFlags = CRYPT_CACHE_ONLY_RETRIEVAL; else { if (!pCallContext->IsOnline()) break; dwRetrievalFlags = CRYPT_WIRE_ONLY_RETRIEVAL; }
// The following leaves the engine's critical section to do
// URL fetching. If the engine was touched by another
// thread, it fails with LastError set to
// ERROR_CAN_NOT_COMPLETE.
if (!pCallContext->ChainEngine()->GetIssuerUrlStore( pCallContext, pCertObject->CertContext(), dwRetrievalFlags, &hIssuerUrlStore )) goto GetIssuerUrlStoreError;
if (hIssuerUrlStore) { BOOL fResult;
fResult = FindAndAddIssuers ( pCallContext, hAdditionalStore, hIssuerUrlStore ); CertCloseStore(hIssuerUrlStore, 0);
if (!fResult) goto FindAndAddIssuersFromUrlStoreError;
dwIssuerStatusFlags = pCertObject->IssuerStatusFlags(); if (!m_pIssuerList->IsEmpty() && (dwIssuerStatusFlags & CERT_ISSUER_VALID_SIGNATURE_FLAG)) { assert(dwIssuerStatusFlags & CERT_ISSUER_PUBKEY_FLAG);
// Try to find all issuers having the same public key.
if (!FindAndAddIssuersByMatchType( CERT_PUBKEY_ISSUER_MATCH_TYPE, pCallContext, hAdditionalStore, NULL // hIssuerUrlStore
)) goto FindIssuersByPubKeyError;
if (m_dwPass1Quality & CERT_QUALITY_SIGNATURE_VALID) break; }
} }
pCertObject->OrIssuerStatusFlags(CERT_ISSUER_URL_FLAG); }
// Check if we have a time valid, signature valid, trusted root
if ((CERT_QUALITY_HAS_TIME_VALID_TRUSTED_ROOT | CERT_QUALITY_SIGNATURE_VALID) != (m_dwPass1Quality & (CERT_QUALITY_HAS_TIME_VALID_TRUSTED_ROOT | CERT_QUALITY_SIGNATURE_VALID)) && pCallContext->IsOnline()) { HCERTSTORE hIssuerUrlStore = NULL;
// The following leaves the engine's critical section to do
// URL fetching. If the engine was touched by another
// thread, it fails with LastError set to
// ERROR_CAN_NOT_COMPLETE.
// Note, we only hit the wire to fetch AuthRoots stored
// on Microsoft's web server
if (!GetAuthRootAutoUpdateUrlStore( pCallContext, &hIssuerUrlStore )) goto GetAuthRootAutoUpdateUrlStoreError;
if (hIssuerUrlStore) { BOOL fResult;
fResult = FindAndAddIssuers ( pCallContext, hAdditionalStore, hIssuerUrlStore ); CertCloseStore(hIssuerUrlStore, 0);
if (!fResult) goto FindAndAddIssuersFromUrlStoreError; } }
if (m_pIssuerList->IsEmpty()) { m_TrustStatus.dwInfoStatus |= ChainGetMatchInfoStatusForNoIssuer( pCertObject->IssuerMatchFlags());
assert(0 == (m_dwPass1Quality & (CERT_QUALITY_HAS_TRUSTED_ROOT | CERT_QUALITY_COMPLETE_CHAIN)));
// Unable to verify our signature, default to being valid.
// Also, we can't be cyclic.
m_dwPass1Quality |= CERT_QUALITY_SIGNATURE_VALID | CERT_QUALITY_NOT_CYCLIC; } }
SuccessReturn: rfResult = TRUE; CommonReturn: m_fCompleted = TRUE; return; ErrorReturn: rfResult = FALSE; goto CommonReturn;
TRACE_ERROR(CreateIssuerListError) TRACE_ERROR(AddPathObjectToCreationCacheError) TRACE_ERROR(FindAndCtlIssuersFromCacheError) TRACE_ERROR(FindAndCtlIssuersFromAdditionalStoreError) TRACE_ERROR(FindAndAddIssuersError) TRACE_ERROR(GetIssuerUrlStoreError) TRACE_ERROR(GetAuthRootAutoUpdateUrlStoreError) TRACE_ERROR(FindAndAddIssuersFromUrlStoreError) TRACE_ERROR(FindIssuersByPubKeyError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::~CChainPathObject, public
//
// Synopsis: Destructor
//
//----------------------------------------------------------------------------
CChainPathObject::~CChainPathObject () { if (m_pCertObject) m_pCertObject->Release();
if (m_fHasRevocationInfo) { if (m_RevocationCrlInfo.pBaseCrlContext) CertFreeCRLContext(m_RevocationCrlInfo.pBaseCrlContext); if (m_RevocationCrlInfo.pDeltaCrlContext) CertFreeCRLContext(m_RevocationCrlInfo.pDeltaCrlContext); }
if (m_pIssuerList) ChainFreeIssuerList( m_pIssuerList ); if (m_pwszExtendedErrorInfo) PkiFree(m_pwszExtendedErrorInfo); }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::FindAndAddIssuers, public
//
// Synopsis: find and add issuers for all matching types
//
//----------------------------------------------------------------------------
BOOL CChainPathObject::FindAndAddIssuers ( IN PCCHAINCALLCONTEXT pCallContext, IN OPTIONAL HCERTSTORE hAdditionalStore, IN OPTIONAL HCERTSTORE hIssuerUrlStore ) { BOOL fResult; PCCERTOBJECT pCertObject = m_pCertObject; DWORD dwIssuerMatchFlags; DWORD i;
static const rgdwMatchType[] = { CERT_EXACT_ISSUER_MATCH_TYPE, CERT_KEYID_ISSUER_MATCH_TYPE, CERT_NAME_ISSUER_MATCH_TYPE }; #define FIND_MATCH_TYPE_CNT (sizeof(rgdwMatchType) / sizeof(rgdwMatchType[0]))
if (pCertObject->IssuerStatusFlags() & CERT_ISSUER_PUBKEY_FLAG) { // We know the issuer's public key. First, attempt to find all issuers
// having that public key.
if (!FindAndAddIssuersByMatchType( CERT_PUBKEY_ISSUER_MATCH_TYPE, pCallContext, hAdditionalStore, hIssuerUrlStore )) goto FindIssuersByPubKeyError;
if (!m_pIssuerList->IsEmpty() && (pCertObject->IssuerStatusFlags() & CERT_ISSUER_VALID_SIGNATURE_FLAG)) goto SuccessReturn; }
dwIssuerMatchFlags = pCertObject->IssuerMatchFlags();
for (i = 0; i < FIND_MATCH_TYPE_CNT; i++) { if (dwIssuerMatchFlags & CERT_MATCH_TYPE_TO_FLAG(rgdwMatchType[i])) { DWORD dwIssuerStatusFlags;
if (!FindAndAddIssuersByMatchType( rgdwMatchType[i], pCallContext, hAdditionalStore, hIssuerUrlStore )) goto FindIssuersByMatchTypeError;
dwIssuerStatusFlags = pCertObject->IssuerStatusFlags(); if (!m_pIssuerList->IsEmpty() && (dwIssuerStatusFlags & CERT_ISSUER_VALID_SIGNATURE_FLAG)) { assert(dwIssuerStatusFlags & CERT_ISSUER_PUBKEY_FLAG);
// We can now find all issuers having the same public key.
if (!FindAndAddIssuersByMatchType( CERT_PUBKEY_ISSUER_MATCH_TYPE, pCallContext, hAdditionalStore, hIssuerUrlStore )) goto FindIssuersByPubKeyError;
break; } } }
SuccessReturn: fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(FindIssuersByPubKeyError) TRACE_ERROR(FindIssuersByMatchTypeError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::FindAndAddIssuersByMatchType, public
//
// Synopsis: find and add issuers for the specified match type
//
//----------------------------------------------------------------------------
BOOL CChainPathObject::FindAndAddIssuersByMatchType( IN DWORD dwMatchType, IN PCCHAINCALLCONTEXT pCallContext, IN OPTIONAL HCERTSTORE hAdditionalStore, IN OPTIONAL HCERTSTORE hIssuerUrlStore ) { BOOL fResult; PCCERTOBJECT pCertObject = m_pCertObject;
if (NULL == hIssuerUrlStore) { DWORD dwIssuerStatusFlags; DWORD dwCachedMatchFlags;
// Note, we need to get the cached match flags before finding
// in the cache. Due to recursive, doing a find further up the
// chain may result in another issuer being inserted at the beginning
// of the cache bucket list. Pretty remote, but possible.
dwCachedMatchFlags = pCertObject->CachedMatchFlags();
if (!FindAndAddIssuersFromCacheByMatchType( dwMatchType, pCallContext, hAdditionalStore )) goto FindIssuersFromCacheError;
dwIssuerStatusFlags = pCertObject->IssuerStatusFlags(); if (CERT_PUBKEY_ISSUER_MATCH_TYPE != dwMatchType && !m_pIssuerList->IsEmpty() && (dwIssuerStatusFlags & CERT_ISSUER_VALID_SIGNATURE_FLAG)) { assert(dwIssuerStatusFlags & CERT_ISSUER_PUBKEY_FLAG);
// We will be called again using the PUBKEY match
goto SuccessReturn; }
if (!(dwCachedMatchFlags & CERT_MATCH_TYPE_TO_FLAG(dwMatchType))) { if (!FindAndAddIssuersFromStoreByMatchType( dwMatchType, pCallContext, FALSE, // fExternalStore
hAdditionalStore, NULL // hIssuerUrlStore
)) goto FindIssuersFromEngineStoreError;
dwIssuerStatusFlags = pCertObject->IssuerStatusFlags(); if (CERT_PUBKEY_ISSUER_MATCH_TYPE != dwMatchType && !m_pIssuerList->IsEmpty() && (dwIssuerStatusFlags & CERT_ISSUER_VALID_SIGNATURE_FLAG)) { assert(dwIssuerStatusFlags & CERT_ISSUER_PUBKEY_FLAG);
// We will be called again using the PUBKEY match
goto SuccessReturn; } } }
if (NULL != hAdditionalStore || NULL != hIssuerUrlStore) { if (!FindAndAddIssuersFromStoreByMatchType( dwMatchType, pCallContext, TRUE, // fExternalStore
hAdditionalStore, hIssuerUrlStore )) goto FindIssuersFromAdditionalOrUrlStoreError; }
SuccessReturn: fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(FindIssuersFromCacheError) TRACE_ERROR(FindIssuersFromEngineStoreError) TRACE_ERROR(FindIssuersFromAdditionalOrUrlStoreError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::FindAndAddIssuersFromCacheByMatchType, public
//
// Synopsis: find and add cached issuers for the specified match type
//
//----------------------------------------------------------------------------
BOOL CChainPathObject::FindAndAddIssuersFromCacheByMatchType( IN DWORD dwMatchType, IN PCCHAINCALLCONTEXT pCallContext, IN OPTIONAL HCERTSTORE hAdditionalStore ) { BOOL fResult; PCCERTOBJECT pCertObject = m_pCertObject; PCCERTCHAINENGINE pChainEngine = pCertObject->ChainEngine(); PCCERTOBJECTCACHE pCertObjectCache = pChainEngine->CertObjectCache(); PCCERTOBJECT pIssuer = NULL;
HLRUCACHE hCache; HLRUENTRY hEntry; PCRYPT_DATA_BLOB pIdentifier; CRYPT_DATA_BLOB DataBlob;
PCERT_AUTHORITY_KEY_ID_INFO pAuthKeyIdentifier;
switch (dwMatchType) { case CERT_EXACT_ISSUER_MATCH_TYPE: hCache = pCertObjectCache->IdentifierIndex(); pCertObject->GetIssuerExactMatchHash(&DataBlob); pIdentifier = &DataBlob; break; case CERT_KEYID_ISSUER_MATCH_TYPE: hCache = pCertObjectCache->KeyIdIndex(); pAuthKeyIdentifier = pCertObject->AuthorityKeyIdentifier(); pIdentifier = &pAuthKeyIdentifier->KeyId; break; case CERT_NAME_ISSUER_MATCH_TYPE: hCache = pCertObjectCache->SubjectNameIndex(); pIdentifier = &pCertObject->CertContext()->pCertInfo->Issuer; break; case CERT_PUBKEY_ISSUER_MATCH_TYPE: hCache = pCertObjectCache->PublicKeyHashIndex(); DataBlob.cbData = CHAINHASHLEN; DataBlob.pbData = pCertObject->IssuerPublicKeyHash(); pIdentifier = &DataBlob; break; default: goto InvalidMatchType; }
pIssuer = pCertObjectCache->FindIssuerObject(hCache, pIdentifier); while (pIssuer) { DWORD dwIssuerStatusFlags;
if (!m_pIssuerList->AddIssuer( pCallContext, hAdditionalStore, pIssuer )) goto AddIssuerError;
dwIssuerStatusFlags = pCertObject->IssuerStatusFlags(); if (CERT_PUBKEY_ISSUER_MATCH_TYPE != dwMatchType && (dwIssuerStatusFlags & CERT_ISSUER_VALID_SIGNATURE_FLAG)) { assert(dwIssuerStatusFlags & CERT_ISSUER_PUBKEY_FLAG);
// We will be called again using the PUBKEY match
goto SuccessReturn; }
switch (dwMatchType) { case CERT_EXACT_ISSUER_MATCH_TYPE: hEntry = pIssuer->IdentifierIndexEntry(); break; case CERT_KEYID_ISSUER_MATCH_TYPE: hEntry = pIssuer->KeyIdIndexEntry(); break; case CERT_NAME_ISSUER_MATCH_TYPE: hEntry = pIssuer->SubjectNameIndexEntry(); break; case CERT_PUBKEY_ISSUER_MATCH_TYPE: hEntry = pIssuer->PublicKeyHashIndexEntry(); break; default: goto InvalidMatchType; }
pIssuer = pCertObjectCache->NextMatchingIssuerObject(hEntry, pIssuer); }
SuccessReturn: fResult = TRUE; CommonReturn: if (pIssuer) { DWORD dwErr = GetLastError();
pIssuer->Release();
SetLastError(dwErr); } return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidMatchType, E_UNEXPECTED) TRACE_ERROR(AddIssuerError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::FindAndAddIssuersFromStoreByMatchType, public
//
// Synopsis: find and add issuers from either the engine's or an
// external store for the specified match type
//
//----------------------------------------------------------------------------
BOOL CChainPathObject::FindAndAddIssuersFromStoreByMatchType( IN DWORD dwMatchType, IN PCCHAINCALLCONTEXT pCallContext, IN BOOL fExternalStore, IN OPTIONAL HCERTSTORE hAdditionalStore, IN OPTIONAL HCERTSTORE hIssuerUrlStore ) { BOOL fResult; PCCERTOBJECT pCertObject = m_pCertObject; PCCERTCHAINENGINE pChainEngine = pCertObject->ChainEngine();
HCERTSTORE hAdditionalStoreToUse = NULL; HCERTSTORE hStore = NULL; PCCERT_CONTEXT pCertContext = NULL; DWORD dwFindType; const void *pvFindPara; CRYPT_DATA_BLOB DataBlob; CERT_INFO CertInfo; PCERT_AUTHORITY_KEY_ID_INFO pAuthKeyIdentifier;
if (fExternalStore) { if (hIssuerUrlStore) { hStore = CertDuplicateStore(hIssuerUrlStore); if (hAdditionalStore) { hAdditionalStoreToUse = CertOpenStore( CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL ); if (NULL == hAdditionalStoreToUse) goto OpenCollectionStoreError;
if (!CertAddStoreToCollection(hAdditionalStoreToUse, hIssuerUrlStore, 0, 0)) goto AddToCollectionStoreError; if (!CertAddStoreToCollection(hAdditionalStoreToUse, hAdditionalStore, 0, 0)) goto AddToCollectionStoreError; } else hAdditionalStoreToUse = CertDuplicateStore(hIssuerUrlStore);
} else { assert(hAdditionalStore); hStore = CertDuplicateStore(hAdditionalStore); hAdditionalStoreToUse = CertDuplicateStore(hAdditionalStore); } } else { hStore = CertDuplicateStore(pChainEngine->OtherStore()); if (hAdditionalStore) hAdditionalStoreToUse = CertDuplicateStore(hAdditionalStore); }
switch (dwMatchType) { case CERT_EXACT_ISSUER_MATCH_TYPE: dwFindType = CERT_FIND_SUBJECT_CERT; pAuthKeyIdentifier = pCertObject->AuthorityKeyIdentifier(); CertInfo.Issuer = pAuthKeyIdentifier->CertIssuer; CertInfo.SerialNumber = pAuthKeyIdentifier->CertSerialNumber; pvFindPara = (const void *) &CertInfo; break; case CERT_KEYID_ISSUER_MATCH_TYPE: dwFindType = CERT_FIND_KEY_IDENTIFIER; pAuthKeyIdentifier = pCertObject->AuthorityKeyIdentifier(); pvFindPara = (const void *) &pAuthKeyIdentifier->KeyId; break; case CERT_NAME_ISSUER_MATCH_TYPE: dwFindType = CERT_FIND_SUBJECT_NAME; pvFindPara = (const void *) &pCertObject->CertContext()->pCertInfo->Issuer; break; case CERT_PUBKEY_ISSUER_MATCH_TYPE: dwFindType = CERT_FIND_PUBKEY_MD5_HASH; DataBlob.cbData = CHAINHASHLEN; DataBlob.pbData = pCertObject->IssuerPublicKeyHash(); pvFindPara = (const void *) &DataBlob; break; default: goto InvalidMatchType; }
while (pCertContext = CertFindCertificateInStore( hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, // dwFindFlags
dwFindType, pvFindPara, pCertContext )) { DWORD dwIssuerStatusFlags; PCCERTOBJECT pIssuer = NULL;
if (!ChainCreateCertObject ( fExternalStore ? CERT_EXTERNAL_ISSUER_OBJECT_TYPE : CERT_CACHED_ISSUER_OBJECT_TYPE, pCallContext, pCertContext, NULL, // rgbCertHash
&pIssuer )) goto CreateIssuerObjectError;
fResult = m_pIssuerList->AddIssuer( pCallContext, hAdditionalStoreToUse, pIssuer ); pIssuer->Release(); if (!fResult) goto AddIssuerError;
dwIssuerStatusFlags = pCertObject->IssuerStatusFlags(); if (CERT_PUBKEY_ISSUER_MATCH_TYPE != dwMatchType && (dwIssuerStatusFlags & CERT_ISSUER_VALID_SIGNATURE_FLAG)) { assert(dwIssuerStatusFlags & CERT_ISSUER_PUBKEY_FLAG);
// We will be called again using the PUBKEY match
goto SuccessReturn; } }
if (CRYPT_E_NOT_FOUND != GetLastError()) goto FindCertificateInStoreError;
if (!fExternalStore) // All matching issuers from the engine's store should be in
// the cache now.
pCertObject->OrCachedMatchFlags(CERT_MATCH_TYPE_TO_FLAG(dwMatchType));
SuccessReturn: fResult = TRUE; CommonReturn: if (pCertContext) CertFreeCertificateContext(pCertContext);
if (hAdditionalStoreToUse) CertCloseStore(hAdditionalStoreToUse, 0); if (hStore) CertCloseStore(hStore, 0);
return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(OpenCollectionStoreError) TRACE_ERROR(AddToCollectionStoreError) SET_ERROR(InvalidMatchType, E_UNEXPECTED) TRACE_ERROR(CreateIssuerObjectError) TRACE_ERROR(AddIssuerError) TRACE_ERROR(FindCertificateInStoreError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::FindAndAddCtlIssuersFromCache, public
//
// Synopsis: find and add matching CTL issuers from the cache
//
//----------------------------------------------------------------------------
BOOL CChainPathObject::FindAndAddCtlIssuersFromCache ( IN PCCHAINCALLCONTEXT pCallContext, IN OPTIONAL HCERTSTORE hAdditionalStore ) { PCERT_OBJECT_CTL_CACHE_ENTRY pEntry;
assert(m_pCertObject->IssuerStatusFlags() & CERT_ISSUER_SELF_SIGNED_FLAG); assert(!(m_pCertObject->IssuerStatusFlags() & CERT_ISSUER_TRUSTED_ROOT_FLAG));
pEntry = NULL; while (pEntry = m_pCertObject->NextCtlCacheEntry(pEntry)) { PCERT_TRUST_LIST_INFO pTrustListInfo = NULL;
if (!SSCtlAllocAndCopyTrustListInfo( pEntry->pTrustListInfo, &pTrustListInfo )) return FALSE;
if (!m_pIssuerList->AddCtlIssuer( pCallContext, hAdditionalStore, pEntry->pSSCtlObject, pTrustListInfo )) { SSCtlFreeTrustListInfo(pTrustListInfo); return FALSE; } }
return TRUE; }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::FindAndAddCtlIssuersFromAdditionalStore, public
//
// Synopsis: find and add matching Ctl issuers from an additional store
//
//----------------------------------------------------------------------------
BOOL CChainPathObject::FindAndAddCtlIssuersFromAdditionalStore ( IN PCCHAINCALLCONTEXT pCallContext, IN HCERTSTORE hAdditionalStore ) { BOOL fResult; PCCTL_CONTEXT pCtlContext = NULL; PCSSCTLOBJECT pSSCtlObject = NULL;
assert(hAdditionalStore);
while (pCtlContext = CertEnumCTLsInStore(hAdditionalStore, pCtlContext)) { PCERT_TRUST_LIST_INFO pTrustListInfo = NULL;
pSSCtlObject = NULL;
if (!SSCtlCreateCtlObject( m_pCertObject->ChainEngine(), pCtlContext, TRUE, // fAdditionalStore
&pSSCtlObject )) // Should look at the different errors
continue; if (!pSSCtlObject->GetTrustListInfo( m_pCertObject->CertContext(), &pTrustListInfo )) { DWORD dwErr = GetLastError(); if (CRYPT_E_NOT_FOUND != dwErr) goto GetTrustListInfoError; else { pSSCtlObject->Release(); continue; } }
if (!m_pIssuerList->AddCtlIssuer( pCallContext, hAdditionalStore, pSSCtlObject, pTrustListInfo )) { SSCtlFreeTrustListInfo(pTrustListInfo); goto AddCtlIssuerError; }
pSSCtlObject->Release(); }
fResult = TRUE;
CommonReturn: return fResult; ErrorReturn: if (pCtlContext) CertFreeCTLContext(pCtlContext); if (pSSCtlObject) pSSCtlObject->Release();
fResult = FALSE; goto CommonReturn;
TRACE_ERROR(GetTrustListInfoError) TRACE_ERROR(AddCtlIssuerError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::NextPath, public
//
// Synopsis: Get the next top path object for this end path object.
//
//----------------------------------------------------------------------------
PCCHAINPATHOBJECT CChainPathObject::NextPath ( IN PCCHAINCALLCONTEXT pCallContext, IN OPTIONAL PCCHAINPATHOBJECT pPrevTopPathObject ) { PCCHAINPATHOBJECT pTopPathObject; PCERT_ISSUER_ELEMENT pSubjectIssuerElement; PCCHAINPATHOBJECT pSubjectPathObject; DWORD dwFlags = pCallContext->CallFlags();
if (NULL == pPrevTopPathObject) { pSubjectIssuerElement = NULL; pSubjectPathObject = NULL; } else { // Find the next issuer for the issuer's subject certificate.
// We iterate downward toward the end certificate
while (TRUE) { pSubjectIssuerElement = pPrevTopPathObject->m_pDownIssuerElement; pSubjectPathObject = pPrevTopPathObject->m_pDownPathObject;
// Set to NULL so it can be reused. Used to determine if
// cyclic.
pPrevTopPathObject->m_pDownPathObject = NULL; pPrevTopPathObject->m_fHasAdditionalStatus = FALSE;
if (NULL == pSubjectPathObject) { // We have reached the end certificate without having a
// next path
SetLastError((DWORD) CRYPT_E_NOT_FOUND); goto NoPath; }
assert(pSubjectIssuerElement); if (pSubjectIssuerElement->pCyclicSaveIssuer) { // Restore the issuer replaced by the cyclic issuer
pSubjectIssuerElement->pIssuer = pSubjectIssuerElement->pCyclicSaveIssuer; pSubjectIssuerElement->pCyclicSaveIssuer = NULL; }
// Move on to the next issuer for the subject. Skip low
// quality issuers
while (pSubjectIssuerElement = pSubjectPathObject->m_pIssuerList->NextElement( pSubjectIssuerElement)) { if ((dwFlags & CERT_CHAIN_DISABLE_PASS1_QUALITY_FILTERING) || pSubjectIssuerElement->dwPass1Quality >= pSubjectPathObject->m_dwPass1Quality) break; }
if (pSubjectIssuerElement) // The subject has another issuer
break;
// Note, a untrusted self signed root without CTLs is equal and
// possibly higher quality than having untrusted CTLs
if ((pSubjectPathObject->m_TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) && (dwFlags & CERT_CHAIN_DISABLE_PASS1_QUALITY_FILTERING) && !(pSubjectPathObject->m_dwPass1Quality & CERT_QUALITY_HAS_TRUSTED_ROOT)) { pTopPathObject = pSubjectPathObject; pTopPathObject->m_pUpIssuerElement = NULL; goto SelfSignedRootInsteadOfCtlPathReturn; }
// Find the next issuer for my subject
pPrevTopPathObject = pSubjectPathObject; } }
// Iterate upward until the TopPathObject's issuer list is empty or
// we have detected a cyclic PathObject
while (TRUE) { if (NULL == pSubjectIssuerElement) { // End (bottom) certificate
pTopPathObject = this; pTopPathObject->m_dwChainIndex = 0; pTopPathObject->m_dwElementIndex = 0; } else { pTopPathObject = pSubjectIssuerElement->pIssuer; // Determine if cyclic.
if (pTopPathObject->m_pDownPathObject || pTopPathObject == this) { // The returned Cyclic path won't have any issuers
if (!ChainCreateCyclicPathObject( pCallContext, pTopPathObject, &pTopPathObject )) goto CreateCyclicPathObjectError; pSubjectIssuerElement->pCyclicSaveIssuer = pSubjectIssuerElement->pIssuer; pSubjectIssuerElement->pIssuer = pTopPathObject; }
if (pSubjectPathObject->m_TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) { pTopPathObject->m_dwChainIndex = pSubjectPathObject->m_dwChainIndex + 1; pTopPathObject->m_dwElementIndex = 0; } else { pTopPathObject->m_dwChainIndex = pSubjectPathObject->m_dwChainIndex; pTopPathObject->m_dwElementIndex = pSubjectPathObject->m_dwElementIndex + 1; }
pSubjectPathObject->m_pUpIssuerElement = pSubjectIssuerElement;
}
pTopPathObject->m_pDownIssuerElement = pSubjectIssuerElement; pTopPathObject->m_pDownPathObject = pSubjectPathObject;
pSubjectPathObject = pTopPathObject;
// Find the first issuer having sufficient quality
pSubjectIssuerElement = NULL; while (pSubjectIssuerElement = pSubjectPathObject->m_pIssuerList->NextElement( pSubjectIssuerElement)) { if ((dwFlags & CERT_CHAIN_DISABLE_PASS1_QUALITY_FILTERING) || pSubjectIssuerElement->dwPass1Quality >= pSubjectPathObject->m_dwPass1Quality) { // For a CTL, check that we have an issuer
if (NULL != pSubjectIssuerElement->pIssuer) break; else { assert(pSubjectIssuerElement->fCtlIssuer); } } }
if (NULL == pSubjectIssuerElement) { pTopPathObject->m_pUpIssuerElement = NULL; break; }
}
SelfSignedRootInsteadOfCtlPathReturn: CommonReturn: return pTopPathObject;
NoPath: ErrorReturn: pTopPathObject = NULL; goto CommonReturn; TRACE_ERROR(CreateCyclicPathObjectError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::CalculateAdditionalStatus, public
//
// Synopsis: calculate additional status bits based on time, usage,
// revocation, ...
//
//----------------------------------------------------------------------------
VOID CChainPathObject::CalculateAdditionalStatus ( IN PCCHAINCALLCONTEXT pCallContext, IN HCERTSTORE hAllStore ) { PCERT_INFO pCertInfo = m_pCertObject->CertContext()->pCertInfo; FILETIME RequestedTime; FILETIME CurrentTime;
assert(!m_fHasAdditionalStatus); memset(&m_AdditionalStatus, 0, sizeof(m_AdditionalStatus)); if (m_pwszExtendedErrorInfo) { PkiFree(m_pwszExtendedErrorInfo); m_pwszExtendedErrorInfo = NULL; }
pCallContext->RequestedTime(&RequestedTime); pCallContext->CurrentTime(&CurrentTime);
if (0 == m_dwChainIndex) { // First simple chain
if (0 == m_dwElementIndex) { // End cert
if (pCallContext->CallFlags() & CERT_CHAIN_TIMESTAMP_TIME) { // For time stamping, the end certificate needs to be valid
// for both the time stamped and current times.
if (0 != CertVerifyTimeValidity(&RequestedTime, pCertInfo) || 0 != CertVerifyTimeValidity(&CurrentTime, pCertInfo)) m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_TIME_VALID; } else { // End certificate needs to be valid for the requested time
if (0 != CertVerifyTimeValidity(&RequestedTime, pCertInfo)) m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_TIME_VALID; } } else { // CA or root
if (pCallContext->CallFlags() & CERT_CHAIN_TIMESTAMP_TIME) { // For time stamping, the CA or root needs to be valid using
// current time
if (0 != CertVerifyTimeValidity(&CurrentTime, pCertInfo)) m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_TIME_VALID; } else { // The CA or root needs to be valid using either the requested
// or current time. Allowing current time is necessary for
// cross certificate chains.
if (!(0 == CertVerifyTimeValidity(&RequestedTime, pCertInfo) || 0 == CertVerifyTimeValidity(&CurrentTime, pCertInfo))) m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_TIME_VALID; } } } else { // CTL signer chains. Must be valid using current time.
if (0 != CertVerifyTimeValidity(&CurrentTime, pCertInfo)) m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_TIME_VALID; } if (m_pDownIssuerElement) { PCERT_USAGE_MATCH pUsageToUse; CERT_USAGE_MATCH CtlUsage; LPSTR pszUsage = szOID_KP_CTL_USAGE_SIGNING;
// Update subject's issuer status
assert (m_pDownIssuerElement->pIssuer = this);
if (0 != m_pDownPathObject->m_dwChainIndex) { // CTL path object
memset(&CtlUsage, 0, sizeof(CtlUsage));
CtlUsage.dwType = USAGE_MATCH_TYPE_AND; CtlUsage.Usage.cUsageIdentifier = 1; CtlUsage.Usage.rgpszUsageIdentifier = &pszUsage;
pUsageToUse = &CtlUsage; } else pUsageToUse = &pCallContext->ChainPara()->RequestedUsage;
if (m_pDownIssuerElement->fCtlIssuer) { FILETIME CurrentTime;
memset(&m_pDownIssuerElement->SubjectStatus, 0, sizeof(m_pDownIssuerElement->SubjectStatus)); pCallContext->CurrentTime(&CurrentTime); m_pDownIssuerElement->pCtlIssuerData->pSSCtlObject-> CalculateStatus( &CurrentTime, pUsageToUse, &m_pDownIssuerElement->SubjectStatus ); } else { CalculatePolicyConstraintsStatus(); CalculateBasicConstraintsStatus(); CalculateKeyUsageStatus(); CalculateNameConstraintsStatus(pUsageToUse); } }
if (pCallContext->CallFlags() & CERT_CHAIN_REVOCATION_CHECK_ALL) { // For CTL signer chains, always use current time
CalculateRevocationStatus( pCallContext, hAllStore, 0 == m_dwChainIndex ? &RequestedTime : &CurrentTime ); }
m_fHasAdditionalStatus = TRUE; }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::CalculatePolicyConstraintsStatus, public
//
// Synopsis: calculate policy constraints additional status for this
// issuer
//
//----------------------------------------------------------------------------
VOID CChainPathObject::CalculatePolicyConstraintsStatus () { PCHAIN_POLICIES_INFO pPoliciesInfo; DWORD i;
assert (0 != m_dwElementIndex);
pPoliciesInfo = m_pCertObject->PoliciesInfo(); for (i = 0; i < CHAIN_ISS_OR_APP_COUNT; i++ ) { PCERT_POLICY_CONSTRAINTS_INFO pConstraints = pPoliciesInfo->rgIssOrAppInfo[i].pConstraints;
DWORD dwRequireSkipCerts; DWORD dwInhibitSkipCerts; PCCHAINPATHOBJECT pPathObject;
if (NULL == pConstraints) continue;
dwRequireSkipCerts = pConstraints->dwRequireExplicitPolicySkipCerts; dwInhibitSkipCerts = pConstraints->dwInhibitPolicyMappingSkipCerts; for (pPathObject = m_pDownPathObject; NULL != pPathObject && pPathObject->m_dwChainIndex == m_dwChainIndex; pPathObject = pPathObject->m_pDownPathObject) { PCHAIN_POLICIES_INFO pSubjectPoliciesInfo;
pSubjectPoliciesInfo = pPathObject->m_pCertObject->PoliciesInfo();
if (pConstraints->fRequireExplicitPolicy) { if (0 < dwRequireSkipCerts) dwRequireSkipCerts--; else { if (NULL == pSubjectPoliciesInfo->rgIssOrAppInfo[i].pPolicy) { m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_INVALID_POLICY_CONSTRAINTS; goto RequireExplicitPolicyError; } } }
if (pConstraints->fInhibitPolicyMapping) { if (0 < dwInhibitSkipCerts) dwInhibitSkipCerts--; else { if (pSubjectPoliciesInfo->rgIssOrAppInfo[i].pMappings) { m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_INVALID_POLICY_CONSTRAINTS; goto InhibitPolicyMappingError; } } } } }
CommonReturn: return;
ErrorReturn: goto CommonReturn; TRACE_ERROR(RequireExplicitPolicyError) TRACE_ERROR(InhibitPolicyMappingError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::CalculateBasicConstraintsStatus, public
//
// Synopsis: calculate basic constraints additional status for this
// issuer
//
//----------------------------------------------------------------------------
VOID CChainPathObject::CalculateBasicConstraintsStatus () { PCERT_BASIC_CONSTRAINTS2_INFO pInfo;
assert (0 != m_dwElementIndex);
if (m_pCertObject->InfoFlags() & CHAIN_INVALID_BASIC_CONSTRAINTS_INFO_FLAG) { m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_BASIC_CONSTRAINTS; }
pInfo = m_pCertObject->BasicConstraintsInfo(); if (NULL == pInfo) return;
if (!pInfo->fCA || (pInfo->fPathLenConstraint && m_dwElementIndex > pInfo->dwPathLenConstraint + 1)) { m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_INVALID_BASIC_CONSTRAINTS; goto BasicConstraintsError; }
CommonReturn: return;
ErrorReturn: goto CommonReturn; TRACE_ERROR(BasicConstraintsError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::CalculateKeyUsageStatus, public
//
// Synopsis: calculate key usage additional status for this
// issuer
//
//----------------------------------------------------------------------------
VOID CChainPathObject::CalculateKeyUsageStatus () { PCRYPT_BIT_BLOB pKeyUsage;
assert (0 != m_dwElementIndex);
if (m_pCertObject->InfoFlags() & CHAIN_INVALID_KEY_USAGE_FLAG) { m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_IS_NOT_VALID_FOR_USAGE; }
pKeyUsage = m_pCertObject->KeyUsage(); if (NULL == pKeyUsage) return;
if (1 > pKeyUsage->cbData || 0 == (pKeyUsage->pbData[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) { m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_VALID_FOR_USAGE; goto KeyUsageError; }
CommonReturn: return;
ErrorReturn: goto CommonReturn; TRACE_ERROR(KeyUsageError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::CalculateNameConstraintsStatus, public
//
// Synopsis: calculate name constraints additional status for this
// issuer
//
//----------------------------------------------------------------------------
VOID CChainPathObject::CalculateNameConstraintsStatus ( IN PCERT_USAGE_MATCH pUsageToUse ) { PCERT_NAME_CONSTRAINTS_INFO pIssuerInfo; PCHAIN_SUBJECT_NAME_CONSTRAINTS_INFO pSubjectInfo; PCERT_BASIC_CONSTRAINTS2_INFO pSubjectBasicInfo; PCCHAINPATHOBJECT pSubjectObject; DWORD dwErrorStatus = 0;
assert (0 != m_dwElementIndex);
if (m_pCertObject->InfoFlags() & CHAIN_INVALID_ISSUER_NAME_CONSTRAINTS_INFO_FLAG) { m_AdditionalStatus.dwErrorStatus |= CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS;
ChainFormatAndAppendExtendedErrorInfo( &m_pwszExtendedErrorInfo, IDS_INVALID_ISSUER_NAME_CONSTRAINT_EXT ); } pIssuerInfo = m_pCertObject->IssuerNameConstraintsInfo(); if (NULL == pIssuerInfo) // No NameConstraint check
return;
// We only verify the name constraints on the end cert
for (pSubjectObject = m_pDownPathObject; NULL != pSubjectObject && 0 != pSubjectObject->m_dwElementIndex; pSubjectObject = pSubjectObject->m_pDownPathObject) ;
assert(pSubjectObject); assert(pSubjectObject->m_dwChainIndex == m_dwChainIndex); if (NULL == pSubjectObject) return;
pSubjectBasicInfo = pSubjectObject->m_pCertObject->BasicConstraintsInfo(); if (pSubjectBasicInfo && pSubjectBasicInfo->fCA) // End cert is a CA.
return;
pSubjectInfo = pSubjectObject->m_pCertObject->SubjectNameConstraintsInfo();
if (pSubjectInfo->fInvalid) { dwErrorStatus |= CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS;
ChainFormatAndAppendExtendedErrorInfo( &m_pwszExtendedErrorInfo, IDS_INVALID_SUBJECT_NAME_CONSTRAINT_INFO );
goto InvalidNameConstraints; }
if (pSubjectInfo->pAltNameInfo) { // Loop through all the AltName entries. There needs to be a
// name constraint for each entry.
DWORD cEntry; PCERT_ALT_NAME_ENTRY pEntry; cEntry = pSubjectInfo->pAltNameInfo->cAltEntry; pEntry = pSubjectInfo->pAltNameInfo->rgAltEntry; for ( ; 0 < cEntry; cEntry--, pEntry++) { BOOL fSupported;
// Check if a NameConstraint for this entry choice is supported
fSupported = FALSE; switch (pEntry->dwAltNameChoice) { case CERT_ALT_NAME_OTHER_NAME: // Only support the UPN OID
if (0 == strcmp(pEntry->pOtherName->pszObjId, szOID_NT_PRINCIPAL_NAME)) fSupported = TRUE; break; case CERT_ALT_NAME_RFC822_NAME: case CERT_ALT_NAME_DNS_NAME: case CERT_ALT_NAME_URL: case CERT_ALT_NAME_DIRECTORY_NAME: fSupported = TRUE; break; case CERT_ALT_NAME_IP_ADDRESS: // Only support 4 or 16 byte IP addresses
if (4 == pEntry->IPAddress.cbData || 16 == pEntry->IPAddress.cbData) fSupported = TRUE; break; case CERT_ALT_NAME_X400_ADDRESS: case CERT_ALT_NAME_EDI_PARTY_NAME: case CERT_ALT_NAME_REGISTERED_ID: default: // Not supported
break; }
if (!fSupported) { dwErrorStatus |= CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
ChainFormatAndAppendNameConstraintsAltNameEntryFixup( &m_pwszExtendedErrorInfo, pEntry, IDS_NOT_SUPPORTED_ENTRY_NAME_CONSTRAINT ); } else dwErrorStatus |= ChainCalculateNameConstraintsErrorStatusForAltNameEntry( pEntry, pIssuerInfo, &m_pwszExtendedErrorInfo); } }
if (pSubjectInfo->pUnicodeNameInfo) { // Check as a DIRECTORY_NAME AltNameEntry choice. The DIRECTORY_NAME
// fixup expects the DirectoryName.pbData to be the decoded and
// fixup'ed UnicodeNameInfo.
CERT_ALT_NAME_ENTRY Entry;
Entry.dwAltNameChoice = CERT_ALT_NAME_DIRECTORY_NAME; Entry.DirectoryName.pbData = (BYTE *) pSubjectInfo->pUnicodeNameInfo; dwErrorStatus |= ChainCalculateNameConstraintsErrorStatusForAltNameEntry( &Entry, pIssuerInfo, &m_pwszExtendedErrorInfo); }
if (pSubjectInfo->pEmailAttr) { // The SubjectAltName doesn't have an email choice. However, there is an
// email attribute in the Subject UnicodeNameInfo.
//
// Check as a CERT_ALT_NAME_RFC822_NAME AltNameEntry choice. The
// RFC822 fixup uses the DirectoryName.pbData and DirectoryName.cbData
// to contain the pointer to and length of the unicode string.
CERT_ALT_NAME_ENTRY Entry; Entry.dwAltNameChoice = CERT_ALT_NAME_RFC822_NAME; Entry.DirectoryName = pSubjectInfo->pEmailAttr->Value; dwErrorStatus |= ChainCalculateNameConstraintsErrorStatusForAltNameEntry( &Entry, pIssuerInfo, &m_pwszExtendedErrorInfo); }
if (!pSubjectInfo->fHasDnsAltNameEntry && NULL != pSubjectInfo->pUnicodeNameInfo && ChainIsOIDInUsage(szOID_PKIX_KP_SERVER_AUTH, &pUsageToUse->Usage)) { // The SubjectAltName doesn't have a DNS choice and we are building
// a ServerAuth chain.
// Need to check all the CN components in the UnicodeNameInfo.
DWORD cRDN; PCERT_RDN pRDN;
cRDN = pSubjectInfo->pUnicodeNameInfo->cRDN; pRDN = pSubjectInfo->pUnicodeNameInfo->rgRDN; for ( ; cRDN > 0; cRDN--, pRDN++) { DWORD cAttr = pRDN->cRDNAttr; PCERT_RDN_ATTR pAttr = pRDN->rgRDNAttr; for ( ; cAttr > 0; cAttr--, pAttr++) { if (!IS_CERT_RDN_CHAR_STRING(pAttr->dwValueType)) continue; if (0 == strcmp(pAttr->pszObjId, szOID_COMMON_NAME)) { //
// Check as a CERT_ALT_NAME_DNS_NAME AltNameEntry choice.
// The DNS fixup uses the DirectoryName.pbData and
// DirectoryName.cbData to contain the pointer to and
// length of the unicode string.
CERT_ALT_NAME_ENTRY Entry; Entry.dwAltNameChoice = CERT_ALT_NAME_DNS_NAME; Entry.DirectoryName = pAttr->Value; dwErrorStatus |= ChainCalculateNameConstraintsErrorStatusForAltNameEntry( &Entry, pIssuerInfo, &m_pwszExtendedErrorInfo); } } } }
CommonReturn: if (0 == dwErrorStatus) m_AdditionalStatus.dwInfoStatus |= CERT_TRUST_HAS_VALID_NAME_CONSTRAINTS; else m_AdditionalStatus.dwErrorStatus |= dwErrorStatus; return;
ErrorReturn: goto CommonReturn; TRACE_ERROR(InvalidNameConstraints) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::CalculateRevocationStatus, public
//
// Synopsis: calculate additional status bits based on revocation
//
//----------------------------------------------------------------------------
VOID CChainPathObject::CalculateRevocationStatus ( IN PCCHAINCALLCONTEXT pCallContext, IN HCERTSTORE hCrlStore, IN LPFILETIME pTime ) { CERT_REVOCATION_PARA RevPara; CERT_REVOCATION_STATUS RevStatus; DWORD dwRevFlags; DWORD dwFlags = pCallContext->CallFlags(); PCERT_CHAIN_PARA pChainPara = pCallContext->ChainPara(); FILETIME CurrentTime;
assert(dwFlags & CERT_CHAIN_REVOCATION_CHECK_ALL);
memset( &RevPara, 0, sizeof( RevPara ) ); RevPara.cbSize = sizeof( RevPara ); RevPara.hCrlStore = hCrlStore; RevPara.pftTimeToUse = pTime; RevPara.dwUrlRetrievalTimeout = pCallContext->RevocationUrlRetrievalTimeout(); RevPara.fCheckFreshnessTime = pChainPara->fCheckRevocationFreshnessTime; RevPara.dwFreshnessTime = pChainPara->dwRevocationFreshnessTime; pCallContext->CurrentTime(&CurrentTime); RevPara.pftCurrentTime = &CurrentTime;
memset( &RevStatus, 0, sizeof( RevStatus ) ); RevStatus.cbSize = sizeof( RevStatus );
dwRevFlags = 0; if (dwFlags & CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY) dwRevFlags |= CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION; if (dwFlags & CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT) dwRevFlags |= CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG;
if (!m_fHasRevocationInfo) { BOOL fHasRevocationInfo = FALSE;
if (m_TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) { BOOL fDoRevocation = FALSE;
if (dwFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT) { ; } else if (dwFlags & CERT_CHAIN_REVOCATION_CHECK_END_CERT) { if (0 == m_dwChainIndex && 0 == m_dwElementIndex) fDoRevocation = TRUE; } else { assert(dwFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN); fDoRevocation = TRUE; }
if (fDoRevocation) { PCCERT_CONTEXT pSubjectCert = m_pCertObject->CertContext(); RevPara.pIssuerCert = m_pCertObject->CertContext(); RevPara.pCrlInfo = &m_RevocationCrlInfo; m_RevocationCrlInfo.cbSize = sizeof(m_RevocationCrlInfo);
RevStatus.dwError = (DWORD) CRYPT_E_REVOCATION_OFFLINE; CertVerifyRevocation( X509_ASN_ENCODING, CERT_CONTEXT_REVOCATION_TYPE, 1, (LPVOID *) &pSubjectCert, dwRevFlags, &RevPara, &RevStatus ); fHasRevocationInfo = TRUE; } } else if (NULL == m_pUpIssuerElement) { if (dwFlags & CERT_CHAIN_REVOCATION_CHECK_END_CERT) { if (0 == m_dwChainIndex && 0 == m_dwElementIndex) fHasRevocationInfo = TRUE; } else { fHasRevocationInfo = TRUE; }
if (fHasRevocationInfo) { RevStatus.dwError = (DWORD) CRYPT_E_REVOCATION_OFFLINE; } }
if (fHasRevocationInfo) { ChainUpdateRevocationInfo(&RevStatus, &m_RevocationInfo, &m_TrustStatus); m_fHasRevocationInfo = TRUE;
memset( &RevStatus, 0, sizeof( RevStatus ) ); RevStatus.cbSize = sizeof( RevStatus ); } }
if (m_pDownIssuerElement && !m_pDownIssuerElement->fCtlIssuer && !m_pDownIssuerElement->fHasRevocationInfo) { BOOL fDoRevocation = FALSE;
if (dwFlags & CERT_CHAIN_REVOCATION_CHECK_END_CERT) { if (0 == m_dwChainIndex && 1 == m_dwElementIndex) fDoRevocation = TRUE; } else { fDoRevocation = TRUE; }
if (fDoRevocation) { PCCERT_CONTEXT pSubjectCert = m_pDownPathObject->m_pCertObject->CertContext(); RevPara.pIssuerCert = m_pCertObject->CertContext(); RevPara.pCrlInfo = &m_pDownIssuerElement->RevocationCrlInfo; m_pDownIssuerElement->RevocationCrlInfo.cbSize = sizeof(m_pDownIssuerElement->RevocationCrlInfo);
RevStatus.dwError = (DWORD) CRYPT_E_REVOCATION_OFFLINE; CertVerifyRevocation( X509_ASN_ENCODING, CERT_CONTEXT_REVOCATION_TYPE, 1, (LPVOID *) &pSubjectCert, dwRevFlags, &RevPara, &RevStatus );
ChainUpdateRevocationInfo(&RevStatus, &m_pDownIssuerElement->RevocationInfo, &m_pDownIssuerElement->SubjectStatus); m_pDownIssuerElement->fHasRevocationInfo = TRUE; } } }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::CreateChainContextFromPath, public
//
// Synopsis: Create the chain context for chain path ending in the
// specified top path object. Also calculates the chain's
// quality value.
//
//----------------------------------------------------------------------------
PINTERNAL_CERT_CHAIN_CONTEXT CChainPathObject::CreateChainContextFromPath ( IN PCCHAINCALLCONTEXT pCallContext, IN PCCHAINPATHOBJECT pTopPathObject ) { // Single PkiZeroAlloc for all of the following:
PINTERNAL_CERT_CHAIN_CONTEXT pContext = NULL; PCERT_SIMPLE_CHAIN *ppChain; PCERT_SIMPLE_CHAIN pChain; PCERT_CHAIN_ELEMENT *ppElement; PCERT_CHAIN_ELEMENT pElement; DWORD cChain; DWORD cTotalElement; DWORD cbTotal; PCCHAINPATHOBJECT pPathObject; DWORD dwQuality; DWORD dwChainErrorStatus; DWORD dwChainInfoStatus; PCERT_ENHKEY_USAGE pAppUsage;
BOOL fHasContextRevocationFreshnessTime;
// Restricted usage info that gets propogated downward
CHAIN_RESTRICTED_USAGE_INFO RestrictedUsageInfo;
memset(&RestrictedUsageInfo, 0, sizeof(RestrictedUsageInfo));
cChain = pTopPathObject->m_dwChainIndex + 1;
if (1 == cChain) { cTotalElement = pTopPathObject->m_dwElementIndex + 1; } else { cTotalElement = 0; for (pPathObject = pTopPathObject; NULL != pPathObject; pPathObject = pPathObject->m_pDownPathObject) cTotalElement++; }
cbTotal = sizeof(INTERNAL_CERT_CHAIN_CONTEXT) + sizeof(PCERT_SIMPLE_CHAIN) * cChain + sizeof(CERT_SIMPLE_CHAIN) * cChain + sizeof(PCERT_CHAIN_ELEMENT) * cTotalElement + sizeof(CERT_CHAIN_ELEMENT) * cTotalElement;
pContext = (PINTERNAL_CERT_CHAIN_CONTEXT) PkiZeroAlloc(cbTotal); if (NULL == pContext) goto OutOfMemory; ppChain = (PCERT_SIMPLE_CHAIN *) &pContext[1]; pChain = (PCERT_SIMPLE_CHAIN) &ppChain[cChain]; ppElement = (PCERT_CHAIN_ELEMENT *) &pChain[cChain]; pElement = (PCERT_CHAIN_ELEMENT) &ppElement[cTotalElement];
pContext->cRefs = 1; pContext->ChainContext.cbSize = sizeof(CERT_CHAIN_CONTEXT); pContext->ChainContext.cChain = cChain; pContext->ChainContext.rgpChain = ppChain;
if (1 < cChain ) pContext->ChainContext.TrustStatus.dwInfoStatus |= CERT_TRUST_IS_COMPLEX_CHAIN;
// Default to having preferred issuers
pContext->ChainContext.TrustStatus.dwInfoStatus |= CERT_TRUST_HAS_PREFERRED_ISSUER;
// Default to having revocation freshness time
fHasContextRevocationFreshnessTime = TRUE;
// Work our way from the top downward
pPathObject = pTopPathObject; ppChain += cChain - 1; pChain += cChain - 1; ppElement += cTotalElement - 1; pElement += cTotalElement - 1;
if (!(pTopPathObject->m_TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED)) pChain->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_PARTIAL_CHAIN;
for ( ; 0 < cChain; cChain--, ppChain--, pChain--) { BOOL fHasChainRevocationFreshnessTime; DWORD cElement;
*ppChain = pChain; pChain->cbSize = sizeof(CERT_SIMPLE_CHAIN);
// Default to having preferred issuers
pChain->TrustStatus.dwInfoStatus |= CERT_TRUST_HAS_PREFERRED_ISSUER;
// Default to having revocation freshness time
fHasChainRevocationFreshnessTime = TRUE;
cElement = pPathObject->m_dwElementIndex + 1; pChain->cElement = cElement; pChain->rgpElement = ppElement - (cElement - 1); for ( ; 0 < cElement; cElement--, cTotalElement--, ppElement--, pElement--, pPathObject = pPathObject->m_pDownPathObject) { assert(pPathObject); *ppElement = pElement; pElement->cbSize = sizeof(CERT_CHAIN_ELEMENT);
if (!pPathObject->UpdateChainContextUsageForPathObject ( pCallContext, pChain, pElement, &RestrictedUsageInfo )) goto UpdateChainContextUsageForPathObjectError;
// This must be last. It updates the chain's TrustStatus
// from the element's TrustStatus.
if (!pPathObject->UpdateChainContextFromPathObject ( pCallContext, pChain, pElement )) goto UpdateChainContextFromPathObjectError;
// Remember the largest revocation freshness time for the
// simple chain and the chain context.
if (pElement->pRevocationInfo && fHasChainRevocationFreshnessTime) { PCERT_REVOCATION_INFO pRevInfo = pElement->pRevocationInfo;
if (pRevInfo->fHasFreshnessTime) { if (pRevInfo->dwFreshnessTime > pChain->dwRevocationFreshnessTime) pChain->dwRevocationFreshnessTime = pRevInfo->dwFreshnessTime; pChain->fHasRevocationFreshnessTime = TRUE;
if (fHasContextRevocationFreshnessTime) { if (pRevInfo->dwFreshnessTime > pContext->ChainContext.dwRevocationFreshnessTime) pContext->ChainContext.dwRevocationFreshnessTime = pRevInfo->dwFreshnessTime; pContext->ChainContext.fHasRevocationFreshnessTime = TRUE; } } else if (CRYPT_E_NO_REVOCATION_CHECK != pRevInfo->dwRevocationResult) { fHasChainRevocationFreshnessTime = FALSE; pChain->fHasRevocationFreshnessTime = FALSE;
fHasContextRevocationFreshnessTime = FALSE; pContext->ChainContext.fHasRevocationFreshnessTime = FALSE; } }
CertPerfIncrementChainElementCount();
}
ChainUpdateSummaryStatusByTrustStatus( &pContext->ChainContext.TrustStatus, &pChain->TrustStatus);
ChainFreeAndClearRestrictedUsageInfo(&RestrictedUsageInfo); }
assert(0 == cTotalElement);
// Calculate chain quality value
dwQuality = 0; dwChainErrorStatus = pContext->ChainContext.TrustStatus.dwErrorStatus; dwChainInfoStatus = pContext->ChainContext.TrustStatus.dwInfoStatus;
if (!(dwChainErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID) && !(dwChainErrorStatus & CERT_TRUST_CTL_IS_NOT_TIME_VALID)) dwQuality |= CERT_QUALITY_TIME_VALID;
if (!(dwChainErrorStatus & CERT_TRUST_IS_NOT_VALID_FOR_USAGE) && !(dwChainErrorStatus & CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE)) dwQuality |= CERT_QUALITY_MEETS_USAGE_CRITERIA;
pAppUsage = pContext->ChainContext.rgpChain[0]->rgpElement[0]->pApplicationUsage; if (NULL == pAppUsage || 0 != pAppUsage->cUsageIdentifier) dwQuality |= CERT_QUALITY_HAS_APPLICATION_USAGE;
if (!(dwChainErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT)) dwQuality |= CERT_QUALITY_HAS_TRUSTED_ROOT;
if (!(dwChainErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) && !(dwChainErrorStatus & CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID)) dwQuality |= CERT_QUALITY_SIGNATURE_VALID;
if (!(dwChainErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)) dwQuality |= CERT_QUALITY_COMPLETE_CHAIN;
if (!(dwChainErrorStatus & CERT_TRUST_IS_REVOKED)) dwQuality |= CERT_QUALITY_NOT_REVOKED;
if (!(dwChainErrorStatus & CERT_TRUST_IS_OFFLINE_REVOCATION) && !(dwChainErrorStatus & CERT_TRUST_IS_REVOKED)) dwQuality |= CERT_QUALITY_ONLINE_REVOCATION;
if (!(dwChainErrorStatus & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) && !(dwChainErrorStatus & CERT_TRUST_IS_REVOKED)) dwQuality |= CERT_QUALITY_CHECK_REVOCATION;
if (!(dwChainInfoStatus & CERT_TRUST_IS_COMPLEX_CHAIN)) dwQuality |= CERT_QUALITY_SIMPLE_CHAIN;
if (dwChainInfoStatus & CERT_TRUST_HAS_PREFERRED_ISSUER) dwQuality |= CERT_QUALITY_PREFERRED_ISSUER;
if (dwChainInfoStatus & CERT_TRUST_HAS_ISSUANCE_CHAIN_POLICY) dwQuality |= CERT_QUALITY_HAS_ISSUANCE_CHAIN_POLICY; if (!(dwChainErrorStatus & (CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY | CERT_TRUST_INVALID_POLICY_CONSTRAINTS))) dwQuality |= CERT_QUALITY_POLICY_CONSTRAINTS_VALID; if (!(dwChainErrorStatus & CERT_TRUST_INVALID_BASIC_CONSTRAINTS)) dwQuality |= CERT_QUALITY_BASIC_CONSTRAINTS_VALID;
if (dwChainInfoStatus & CERT_TRUST_HAS_VALID_NAME_CONSTRAINTS) dwQuality |= CERT_QUALITY_HAS_NAME_CONSTRAINTS; if (!(dwChainErrorStatus & (CERT_TRUST_INVALID_NAME_CONSTRAINTS | CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT | CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT))) dwQuality |= CERT_QUALITY_NAME_CONSTRAINTS_VALID; if (!(dwChainErrorStatus & (CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT | CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT))) dwQuality |= CERT_QUALITY_NAME_CONSTRAINTS_MET;
pContext->dwQuality = dwQuality;
CertPerfIncrementChainCount();
CommonReturn: return pContext;
ErrorReturn: if (pContext) { ChainReleaseInternalChainContext(pContext); pContext = NULL; }
ChainFreeAndClearRestrictedUsageInfo(&RestrictedUsageInfo); goto CommonReturn;
SET_ERROR(OutOfMemory, E_OUTOFMEMORY) TRACE_ERROR(UpdateChainContextUsageForPathObjectError) TRACE_ERROR(UpdateChainContextFromPathObjectError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::UpdateChainContextUsageForPathObject, public
//
// Synopsis: update the chain context usage information for this
// path object.
//
//----------------------------------------------------------------------------
BOOL CChainPathObject::UpdateChainContextUsageForPathObject ( IN PCCHAINCALLCONTEXT pCallContext, IN OUT PCERT_SIMPLE_CHAIN pChain, IN OUT PCERT_CHAIN_ELEMENT pElement, IN OUT PCHAIN_RESTRICTED_USAGE_INFO pRestrictedUsageInfo ) { BOOL fResult; PCHAIN_POLICIES_INFO pPoliciesInfo = m_pCertObject->PoliciesInfo(); CERT_USAGE_MATCH CtlUsage; PCERT_USAGE_MATCH pUsageToUse; LPSTR pszUsage = szOID_KP_CTL_USAGE_SIGNING; PCERT_ENHKEY_USAGE pIssUsage; PCERT_ENHKEY_USAGE pAppUsage; PCERT_ENHKEY_USAGE pPropUsage; DWORD dwIssFlags; DWORD dwAppFlags;
static const CERT_ENHKEY_USAGE NoUsage = { 0, NULL };
// Update the usage to use for the second and subsequent chains
if (0 != m_dwChainIndex) { // CTL path object
memset(&CtlUsage, 0, sizeof(CtlUsage));
CtlUsage.dwType = USAGE_MATCH_TYPE_AND; CtlUsage.Usage.cUsageIdentifier = 1; CtlUsage.Usage.rgpszUsageIdentifier = &pszUsage;
pUsageToUse = &CtlUsage; } else { pUsageToUse = &pCallContext->ChainPara()->RequestedUsage; }
dwIssFlags = pPoliciesInfo->rgIssOrAppInfo[CHAIN_ISS_INDEX].dwFlags; dwAppFlags = pPoliciesInfo->rgIssOrAppInfo[CHAIN_APP_INDEX].dwFlags;
// Update TrustStatus to reflect any policy decoding errors
if ((dwIssFlags & CHAIN_INVALID_POLICY_FLAG) || (dwAppFlags & CHAIN_INVALID_POLICY_FLAG)) pElement->TrustStatus.dwErrorStatus |= CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_POLICY_CONSTRAINTS;
// Issuance :: restricted and mapped usage
pIssUsage = pPoliciesInfo->rgIssOrAppInfo[CHAIN_ISS_INDEX].pUsage; if (NULL == pIssUsage) { // NULL => Any Usage
// Only allow any usage for self signed roots or certs having
// the CertPolicies extension. Otherwise, treat as having no usage.
if (!(m_TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED) && NULL == pPoliciesInfo->rgIssOrAppInfo[CHAIN_ISS_INDEX].pPolicy) pIssUsage = (PCERT_ENHKEY_USAGE) &NoUsage; }
if (!ChainCalculateRestrictedUsage ( pIssUsage, pPoliciesInfo->rgIssOrAppInfo[CHAIN_ISS_INDEX].pMappings, &pRestrictedUsageInfo->pIssuanceRestrictedUsage, &pRestrictedUsageInfo->pIssuanceMappedUsage, &pRestrictedUsageInfo->rgdwIssuanceMappedIndex )) goto CalculateIssuanceRestrictedUsageError;
if (!ChainAllocAndCopyUsage( pRestrictedUsageInfo->pIssuanceRestrictedUsage, &pElement->pIssuanceUsage )) goto AllocAndCopyUsageError;
if (0 != m_dwElementIndex) { PCERT_POLICY_CONSTRAINTS_INFO pConstraints = pPoliciesInfo->rgIssOrAppInfo[CHAIN_ISS_INDEX].pConstraints;
if (pConstraints && pConstraints->fRequireExplicitPolicy && m_dwElementIndex > pConstraints->dwRequireExplicitPolicySkipCerts) pRestrictedUsageInfo->fRequireIssuancePolicy = TRUE; } else { // For the end cert, update the require issuance chain policy
// TrustStatus. Also, check the requested issuance policy.
if (pRestrictedUsageInfo->fRequireIssuancePolicy) { if (pRestrictedUsageInfo->pIssuanceRestrictedUsage && 0 == pRestrictedUsageInfo->pIssuanceRestrictedUsage->cUsageIdentifier) { // Must have either ANY_POLICY or some policy OIDs
pChain->TrustStatus.dwErrorStatus |= CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY; } else if (pPoliciesInfo->rgIssOrAppInfo[CHAIN_ISS_INDEX].pPolicy) { pChain->TrustStatus.dwInfoStatus |= CERT_TRUST_HAS_ISSUANCE_CHAIN_POLICY; } }
pIssUsage = pElement->pIssuanceUsage; if (pIssUsage) { PCERT_USAGE_MATCH pRequestedIssuancePolicy = &pCallContext->ChainPara()->RequestedIssuancePolicy;
ChainGetUsageStatus( &pRequestedIssuancePolicy->Usage, pIssUsage, pRequestedIssuancePolicy->dwType, &pElement->TrustStatus ); } }
if (USAGE_MATCH_TYPE_OR == pUsageToUse->dwType && 1 < pUsageToUse->Usage.cUsageIdentifier) { // For "OR" match type request, we can't use restricted property usage
pPropUsage = pPoliciesInfo->pPropertyUsage;
// For "OR" match type request, we only use restricted application
// usage upon seeing policy mappings.
if (pRestrictedUsageInfo->pApplicationMappedUsage || pPoliciesInfo->rgIssOrAppInfo[CHAIN_APP_INDEX].pMappings) { if (!ChainCalculateRestrictedUsage ( pPoliciesInfo->rgIssOrAppInfo[CHAIN_APP_INDEX].pUsage, pPoliciesInfo->rgIssOrAppInfo[CHAIN_APP_INDEX].pMappings, &pRestrictedUsageInfo->pApplicationRestrictedUsage, &pRestrictedUsageInfo->pApplicationMappedUsage, &pRestrictedUsageInfo->rgdwApplicationMappedIndex )) goto CalculateApplicationRestrictedUsageError; pAppUsage = pRestrictedUsageInfo->pApplicationRestrictedUsage; } else pAppUsage = pPoliciesInfo->rgIssOrAppInfo[CHAIN_APP_INDEX].pUsage; } else { // Restricted property and application usage
PCERT_ENHKEY_USAGE pPropMappedUsage = NULL; LPDWORD pdwPropMappedIndex = NULL;
fResult = ChainCalculateRestrictedUsage ( pPoliciesInfo->pPropertyUsage, NULL, // pMappings
&pRestrictedUsageInfo->pPropertyRestrictedUsage, &pPropMappedUsage, &pdwPropMappedIndex ); assert(NULL == pPropMappedUsage && NULL == pdwPropMappedIndex); if (!fResult) goto CalculatePropertyRestrictedUsageError; pPropUsage = pRestrictedUsageInfo->pPropertyRestrictedUsage;
if (!ChainCalculateRestrictedUsage ( pPoliciesInfo->rgIssOrAppInfo[CHAIN_APP_INDEX].pUsage, pPoliciesInfo->rgIssOrAppInfo[CHAIN_APP_INDEX].pMappings, &pRestrictedUsageInfo->pApplicationRestrictedUsage, &pRestrictedUsageInfo->pApplicationMappedUsage, &pRestrictedUsageInfo->rgdwApplicationMappedIndex )) goto CalculateApplicationRestrictedUsageError; pAppUsage = pRestrictedUsageInfo->pApplicationRestrictedUsage; }
// The element's application usage includes the intersection with
// the property usage
if (NULL == pAppUsage) { if (!ChainAllocAndCopyUsage( pPropUsage, &pElement->pApplicationUsage )) goto AllocAndCopyUsageError; } else { if (!ChainAllocAndCopyUsage( pAppUsage, &pElement->pApplicationUsage )) goto AllocAndCopyUsageError; if (pPropUsage) // Remove OIDs not also in the property usage
ChainIntersectUsages(pPropUsage, pElement->pApplicationUsage); }
// Check the requested usage
pAppUsage = pElement->pApplicationUsage; if (pAppUsage) ChainGetUsageStatus( &pUsageToUse->Usage, pAppUsage, pUsageToUse->dwType, &pElement->TrustStatus );
fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(CalculateIssuanceRestrictedUsageError) TRACE_ERROR(AllocAndCopyUsageError) TRACE_ERROR(CalculateApplicationRestrictedUsageError) TRACE_ERROR(CalculatePropertyRestrictedUsageError) }
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::UpdateChainContextFromPathObject, public
//
// Synopsis: update the chain context using information from this
// path object.
//
//----------------------------------------------------------------------------
BOOL CChainPathObject::UpdateChainContextFromPathObject ( IN PCCHAINCALLCONTEXT pCallContext, IN OUT PCERT_SIMPLE_CHAIN pChain, IN OUT PCERT_CHAIN_ELEMENT pElement ) { BOOL fResult; PCERT_REVOCATION_INFO pRevocationInfo = NULL; PCERT_REVOCATION_CRL_INFO pRevocationCrlInfo = NULL;
ChainOrInStatusBits(&pElement->TrustStatus, &m_TrustStatus); assert(m_fHasAdditionalStatus); ChainOrInStatusBits(&pElement->TrustStatus, &m_AdditionalStatus);
if (m_pUpIssuerElement) { if (m_pUpIssuerElement->fCtlIssuer) { ChainOrInStatusBits(&pChain->TrustStatus, &m_pUpIssuerElement->SubjectStatus);
assert(pElement->TrustStatus.dwErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT);
pElement->TrustStatus.dwErrorStatus &= ~CERT_TRUST_IS_UNTRUSTED_ROOT;
if (!SSCtlAllocAndCopyTrustListInfo( m_pUpIssuerElement->pCtlIssuerData->pTrustListInfo, &pChain->pTrustListInfo )) goto AllocAndCopyTrustListInfoError; } else { ChainOrInStatusBits(&pElement->TrustStatus, &m_pUpIssuerElement->SubjectStatus); } }
pRevocationInfo = NULL; if (m_fHasRevocationInfo) { pRevocationInfo = &m_RevocationInfo; pRevocationCrlInfo = &m_RevocationCrlInfo; } else if (m_pUpIssuerElement && m_pUpIssuerElement->fHasRevocationInfo) { pRevocationInfo = &m_pUpIssuerElement->RevocationInfo; pRevocationCrlInfo = &m_pUpIssuerElement->RevocationCrlInfo; }
if (pRevocationInfo) { pElement->pRevocationInfo = new CERT_REVOCATION_INFO; if (NULL == pElement->pRevocationInfo) goto OutOfMemory;
memset(pElement->pRevocationInfo, 0, sizeof(CERT_REVOCATION_INFO)); pElement->pRevocationInfo->cbSize = sizeof(CERT_REVOCATION_INFO); pElement->pRevocationInfo->dwRevocationResult = pRevocationInfo->dwRevocationResult; pElement->pRevocationInfo->fHasFreshnessTime = pRevocationInfo->fHasFreshnessTime; pElement->pRevocationInfo->dwFreshnessTime = pRevocationInfo->dwFreshnessTime;
if (NULL != pRevocationCrlInfo->pBaseCrlContext) { PCERT_REVOCATION_CRL_INFO pCrlInfo;
pCrlInfo = new CERT_REVOCATION_CRL_INFO; if (NULL == pCrlInfo) goto OutOfMemory;
pElement->pRevocationInfo->pCrlInfo = pCrlInfo; memcpy(pCrlInfo, pRevocationCrlInfo, sizeof(*pCrlInfo)); assert(pCrlInfo->cbSize = sizeof(*pCrlInfo));
pCrlInfo->pBaseCrlContext = CertDuplicateCRLContext( pRevocationCrlInfo->pBaseCrlContext); if (NULL != pRevocationCrlInfo->pDeltaCrlContext) pCrlInfo->pDeltaCrlContext = CertDuplicateCRLContext( pRevocationCrlInfo->pDeltaCrlContext); } }
if (m_pwszExtendedErrorInfo) { DWORD cbExtendedErrorInfo; LPWSTR pwszExtendedErrorInfo;
cbExtendedErrorInfo = (wcslen(m_pwszExtendedErrorInfo) + 1) * sizeof(WCHAR); if (NULL == (pwszExtendedErrorInfo = (LPWSTR) PkiNonzeroAlloc( cbExtendedErrorInfo))) goto OutOfMemory; memcpy(pwszExtendedErrorInfo, m_pwszExtendedErrorInfo, cbExtendedErrorInfo); pElement->pwszExtendedErrorInfo = pwszExtendedErrorInfo; }
pElement->pCertContext = CertDuplicateCertificateContext( m_pCertObject->CertContext());
ChainUpdateSummaryStatusByTrustStatus(&pChain->TrustStatus, &pElement->TrustStatus);
fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(AllocAndCopyTrustListInfoError) SET_ERROR(OutOfMemory, E_OUTOFMEMORY) }
//+===========================================================================
// CCertIssuerList methods
//============================================================================
//+---------------------------------------------------------------------------
//
// Member: CCertIssuerList::CCertIssuerList, public
//
// Synopsis: Constructor
//
//----------------------------------------------------------------------------
CCertIssuerList::CCertIssuerList (IN PCCHAINPATHOBJECT pSubject) { m_pSubject = pSubject; m_pHead = NULL; }
//+---------------------------------------------------------------------------
//
// Member: CCertIssuerList::~CCertIssuerList, public
//
// Synopsis: Destructor
//
//----------------------------------------------------------------------------
CCertIssuerList::~CCertIssuerList () { PCERT_ISSUER_ELEMENT pElement;
while ( ( pElement = NextElement( NULL ) ) != NULL ) { RemoveElement( pElement ); DeleteElement( pElement ); } }
//+---------------------------------------------------------------------------
//
// Member: CCertIssuerList::AddIssuer, public
//
// Synopsis: add an issuer to the list
//
//----------------------------------------------------------------------------
BOOL CCertIssuerList::AddIssuer( IN PCCHAINCALLCONTEXT pCallContext, IN OPTIONAL HCERTSTORE hAdditionalStore, IN PCCERTOBJECT pIssuer ) { BOOL fResult; PCCHAINPATHOBJECT pIssuerPathObject = NULL; PCERT_ISSUER_ELEMENT pElement = NULL;
if (CheckForDuplicateElement(pIssuer->CertHash(), FALSE)) return TRUE; // Don't add ourself as an issuer.
if (0 == memcmp(m_pSubject->CertObject()->CertHash(), pIssuer->CertHash(), CHAINHASHLEN)) return TRUE;
// Mainly for certs generated by tstore2.exe that mostly contain
// the same public key, need to add an additional filter to
// discard certs that only match via the public key, ie no
// AKI, name or basic constraints match.
if (!ChainIsValidPubKeyMatchForIssuer(pIssuer, m_pSubject->CertObject())) return TRUE;
if (!ChainCreatePathObject( pCallContext, pIssuer, hAdditionalStore, &pIssuerPathObject )) return FALSE;
fResult = CreateElement( pCallContext, FALSE, // fCtlIssuer
pIssuerPathObject, hAdditionalStore, NULL, // pSSCtlObject
NULL, // pTrustListInfo
&pElement );
if (!fResult) { return( FALSE ); }
AddElement( pElement );
return( TRUE ); }
//+---------------------------------------------------------------------------
//
// Member: CCertIssuerList::AddCtlIssuer, public
//
// Synopsis: add an issuer to the list
//
//----------------------------------------------------------------------------
BOOL CCertIssuerList::AddCtlIssuer( IN PCCHAINCALLCONTEXT pCallContext, IN OPTIONAL HCERTSTORE hAdditionalStore, IN PCSSCTLOBJECT pSSCtlObject, IN PCERT_TRUST_LIST_INFO pTrustListInfo ) { PCERT_ISSUER_ELEMENT pElement = NULL;
if (CheckForDuplicateElement(pSSCtlObject->CtlHash(), TRUE)) return TRUE;
if (!CreateElement( pCallContext, TRUE, // fCtlIssuer
NULL, // pIssuerPathObject
hAdditionalStore, pSSCtlObject, pTrustListInfo, &pElement )) return FALSE;
AddElement( pElement );
return( TRUE ); }
//+---------------------------------------------------------------------------
//
// Member: CCertIssuerList::CreateElement, public
//
// Synopsis: create an element
//
//----------------------------------------------------------------------------
BOOL CCertIssuerList::CreateElement( IN PCCHAINCALLCONTEXT pCallContext, IN BOOL fCtlIssuer, IN OPTIONAL PCCHAINPATHOBJECT pIssuer, IN OPTIONAL HCERTSTORE hAdditionalStore, IN OPTIONAL PCSSCTLOBJECT pSSCtlObject, IN OPTIONAL PCERT_TRUST_LIST_INFO pTrustListInfo, // allocated by caller
OUT PCERT_ISSUER_ELEMENT* ppElement ) { BOOL fResult; BOOL fCtlSignatureValid = FALSE; PCERT_ISSUER_ELEMENT pElement;
pElement = new CERT_ISSUER_ELEMENT; if (NULL == pElement) goto OutOfMemory;
memset( pElement, 0, sizeof( CERT_ISSUER_ELEMENT ) );
pElement->fCtlIssuer = fCtlIssuer;
if (!fCtlIssuer) { pElement->pIssuer = pIssuer;
// The following may leave the engine's critical section to verify the
// signature. If the engine was touched by another thread, it fails with
// LastError set to ERROR_CAN_NOT_COMPLETE.
if (!ChainGetSubjectStatus( pCallContext, pIssuer, m_pSubject, &pElement->SubjectStatus )) goto GetSubjectStatusError; } else { pElement->pCtlIssuerData = new CTL_ISSUER_DATA; if (NULL == pElement->pCtlIssuerData) goto OutOfMemory;
memset( pElement->pCtlIssuerData, 0, sizeof( CTL_ISSUER_DATA ) );
pSSCtlObject->AddRef(); pElement->pCtlIssuerData->pSSCtlObject = pSSCtlObject; pElement->pCtlIssuerData->pTrustListInfo = pTrustListInfo;
// The following may leave the engine's critical section to verify a
// signature or do URL retrieval. If the engine was touched by
// another thread, it fails with LastError set to
// ERROR_CAN_NOT_COMPLETE.
if (!pSSCtlObject->GetSigner( m_pSubject, pCallContext, hAdditionalStore, &pElement->pIssuer, &fCtlSignatureValid )) { if (GetLastError() != CRYPT_E_NOT_FOUND) goto GetSignerError; } }
if (pElement->pIssuer) { // If the Issuer hasn't completed yet, then, we are cyclic.
if (!pElement->pIssuer->IsCompleted()) pElement->dwPass1Quality = 0; else { pElement->dwPass1Quality = pElement->pIssuer->Pass1Quality();
if (!fCtlIssuer) { if (pElement->SubjectStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) { pElement->dwPass1Quality &= ~CERT_QUALITY_SIGNATURE_VALID; } } else if (!fCtlSignatureValid) { pElement->dwPass1Quality &= ~CERT_QUALITY_SIGNATURE_VALID; } } } else { assert(fCtlIssuer); pElement->dwPass1Quality = 0; }
// Remember highest quality issuer
if (pElement->dwPass1Quality > m_pSubject->Pass1Quality()) m_pSubject->SetPass1Quality(pElement->dwPass1Quality); fResult = TRUE;
CommonReturn: *ppElement = pElement; return fResult;
ErrorReturn: if (pElement) { DeleteElement(pElement); pElement = NULL; }
fResult = FALSE; goto CommonReturn;
SET_ERROR(OutOfMemory, E_OUTOFMEMORY) TRACE_ERROR(GetSubjectStatusError) TRACE_ERROR(GetSignerError) }
//+---------------------------------------------------------------------------
//
// Member: CCertIssuerList::DeleteElement, public
//
// Synopsis: delete an element
//
//----------------------------------------------------------------------------
VOID CCertIssuerList::DeleteElement (IN PCERT_ISSUER_ELEMENT pElement) { if ( pElement->pCtlIssuerData ) { ChainFreeCtlIssuerData( pElement->pCtlIssuerData ); }
if (pElement->fHasRevocationInfo) { if (pElement->RevocationCrlInfo.pBaseCrlContext) CertFreeCRLContext(pElement->RevocationCrlInfo.pBaseCrlContext); if (pElement->RevocationCrlInfo.pDeltaCrlContext) CertFreeCRLContext(pElement->RevocationCrlInfo.pDeltaCrlContext); }
delete pElement; }
//+---------------------------------------------------------------------------
//
// Member: CCertIssuerList::CheckForDuplicateElement, public
//
// Synopsis: check for a duplicate element
//
//----------------------------------------------------------------------------
BOOL CCertIssuerList::CheckForDuplicateElement ( IN BYTE rgbHash[ CHAINHASHLEN ], IN BOOL fCtlIssuer ) { PCERT_ISSUER_ELEMENT pElement = NULL;
while ( ( pElement = NextElement( pElement ) ) != NULL ) { if ( pElement->fCtlIssuer == fCtlIssuer ) { if ( fCtlIssuer == FALSE ) { if ( memcmp( rgbHash, pElement->pIssuer->CertObject()->CertHash(), CHAINHASHLEN ) == 0 ) { return( TRUE ); } } else { if ( memcmp( rgbHash, pElement->pCtlIssuerData->pSSCtlObject->CtlHash(), CHAINHASHLEN ) == 0 ) { return( TRUE ); } } } }
return( FALSE ); }
//+===========================================================================
// CCertObjectCache methods
//============================================================================
//+---------------------------------------------------------------------------
//
// Member: CCertObjectCache::CCertObjectCache, public
//
// Synopsis: Constructor
//
//----------------------------------------------------------------------------
CCertObjectCache::CCertObjectCache ( IN DWORD MaxIndexEntries, OUT BOOL& rfResult ) { LRU_CACHE_CONFIG Config;
memset( &Config, 0, sizeof( Config ) );
Config.dwFlags = LRU_CACHE_NO_SERIALIZE | LRU_CACHE_NO_COPY_IDENTIFIER; Config.cBuckets = DEFAULT_CERT_OBJECT_CACHE_BUCKETS;
m_hHashIndex = NULL; m_hIdentifierIndex = NULL; m_hKeyIdIndex = NULL; m_hSubjectNameIndex = NULL; m_hPublicKeyHashIndex = NULL; m_hEndHashIndex = NULL;
Config.pfnHash = CertObjectCacheHashNameIdentifier;
rfResult = I_CryptCreateLruCache( &Config, &m_hSubjectNameIndex );
Config.pfnHash = CertObjectCacheHashMd5Identifier;
if ( rfResult == TRUE ) { rfResult = I_CryptCreateLruCache( &Config, &m_hIdentifierIndex ); }
if ( rfResult == TRUE ) { rfResult = I_CryptCreateLruCache( &Config, &m_hKeyIdIndex ); }
if ( rfResult == TRUE ) { rfResult = I_CryptCreateLruCache( &Config, &m_hPublicKeyHashIndex ); }
Config.pfnOnRemoval = CertObjectCacheOnRemovalFromPrimaryIndex;
if ( rfResult == TRUE ) { rfResult = I_CryptCreateLruCache( &Config, &m_hHashIndex ); }
Config.MaxEntries = MaxIndexEntries; Config.pfnOnRemoval = CertObjectCacheOnRemovalFromEndHashIndex;
if ( rfResult == TRUE ) { rfResult = I_CryptCreateLruCache( &Config, &m_hEndHashIndex ); } }
//+---------------------------------------------------------------------------
//
// Member: CCertObjectCache::~CCertObjectCache, public
//
// Synopsis: Destructor
//
//----------------------------------------------------------------------------
CCertObjectCache::~CCertObjectCache () { I_CryptFreeLruCache( m_hHashIndex, 0, NULL );
I_CryptFreeLruCache( m_hSubjectNameIndex, LRU_SUPPRESS_REMOVAL_NOTIFICATION, NULL );
I_CryptFreeLruCache( m_hIdentifierIndex, LRU_SUPPRESS_REMOVAL_NOTIFICATION, NULL );
I_CryptFreeLruCache( m_hKeyIdIndex, LRU_SUPPRESS_REMOVAL_NOTIFICATION, NULL );
I_CryptFreeLruCache( m_hPublicKeyHashIndex, LRU_SUPPRESS_REMOVAL_NOTIFICATION, NULL );
I_CryptFreeLruCache( m_hEndHashIndex, 0, NULL ); }
//+---------------------------------------------------------------------------
//
// Member: CCertObjectCache::AddIssuerObject, public
//
// Synopsis: add an issuer object to the cache
// Increments engine's touch count
//
//----------------------------------------------------------------------------
VOID CCertObjectCache::AddIssuerObject ( IN PCCHAINCALLCONTEXT pCallContext, IN PCCERTOBJECT pCertObject ) { assert(CERT_CACHED_ISSUER_OBJECT_TYPE == pCertObject->ObjectType()); pCertObject->AddRef();
I_CryptInsertLruEntry( pCertObject->HashIndexEntry(), pCallContext ); I_CryptInsertLruEntry( pCertObject->IdentifierIndexEntry(), pCallContext ); I_CryptInsertLruEntry( pCertObject->SubjectNameIndexEntry(), pCallContext ); I_CryptInsertLruEntry( pCertObject->KeyIdIndexEntry(), pCallContext ); I_CryptInsertLruEntry( pCertObject->PublicKeyHashIndexEntry(), pCallContext );
pCallContext->TouchEngine();
CertPerfIncrementChainCertCacheCount(); }
//+---------------------------------------------------------------------------
//
// Member: CCertObjectCache::AddEndObject, public
//
// Synopsis: add an end object to the cache
//
//----------------------------------------------------------------------------
VOID CCertObjectCache::AddEndObject ( IN PCCHAINCALLCONTEXT pCallContext, IN PCCERTOBJECT pCertObject ) { PCCERTOBJECT pDuplicate;
if (CERT_END_OBJECT_TYPE != pCertObject->ObjectType()) return;
pDuplicate = FindEndObjectByHash(pCertObject->CertHash()); if (pDuplicate) { pDuplicate->Release(); return; }
if (pCertObject->CacheEndObject(pCallContext)) { pCertObject->AddRef();
I_CryptInsertLruEntry( pCertObject->EndHashIndexEntry(), pCallContext );
CertPerfIncrementChainCertCacheCount();
CertPerfIncrementChainCacheEndCertCount(); } }
//+---------------------------------------------------------------------------
//
// Member: CCertObjectCache::FindIssuerObject, public
//
// Synopsis: find object
//
// Note, also called by FindEndObjectByHash
//
//----------------------------------------------------------------------------
PCCERTOBJECT CCertObjectCache::FindIssuerObject ( IN HLRUCACHE hIndex, IN PCRYPT_DATA_BLOB pIdentifier ) { HLRUENTRY hFound; PCCERTOBJECT pFound = NULL;
hFound = I_CryptFindLruEntry( hIndex, pIdentifier ); if ( hFound != NULL ) { pFound = (PCCERTOBJECT)I_CryptGetLruEntryData( hFound ); pFound->AddRef();
I_CryptReleaseLruEntry( hFound ); }
return( pFound ); }
//+---------------------------------------------------------------------------
//
// Member: CCertObjectCache::FindIssuerObjectByHash, public
//
// Synopsis: find object by hash
//
//----------------------------------------------------------------------------
PCCERTOBJECT CCertObjectCache::FindIssuerObjectByHash ( IN BYTE rgbCertHash[ CHAINHASHLEN ] ) { CRYPT_DATA_BLOB DataBlob;
DataBlob.cbData = CHAINHASHLEN; DataBlob.pbData = rgbCertHash; return( FindIssuerObject( m_hHashIndex, &DataBlob ) ); }
//+---------------------------------------------------------------------------
//
// Member: CCertObjectCache::FindEndObjectByHash, public
//
// Synopsis: find object by hash
//
//----------------------------------------------------------------------------
PCCERTOBJECT CCertObjectCache::FindEndObjectByHash ( IN BYTE rgbCertHash[ CHAINHASHLEN ] ) { CRYPT_DATA_BLOB DataBlob;
DataBlob.cbData = CHAINHASHLEN; DataBlob.pbData = rgbCertHash; return( FindIssuerObject( m_hEndHashIndex, &DataBlob ) ); }
//+---------------------------------------------------------------------------
//
// Member: CCertObjectCache::NextMatchingIssuerObject, public
//
// Synopsis: next matching issuer object
//
//----------------------------------------------------------------------------
PCCERTOBJECT CCertObjectCache::NextMatchingIssuerObject ( IN HLRUENTRY hObjectEntry, IN PCCERTOBJECT pCertObject ) { HLRUENTRY hFound; PCCERTOBJECT pFound = NULL;
I_CryptAddRefLruEntry( hObjectEntry );
hFound = I_CryptEnumMatchingLruEntries( hObjectEntry ); if ( hFound != NULL ) { pFound = (PCCERTOBJECT)I_CryptGetLruEntryData( hFound ); pFound->AddRef();
I_CryptReleaseLruEntry( hFound ); }
pCertObject->Release();
return( pFound ); }
//+===========================================================================
// CCertChainEngine methods
//============================================================================
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::CCertChainEngine, public
//
// Synopsis: Constructor
//
//----------------------------------------------------------------------------
CCertChainEngine::CCertChainEngine ( IN PCERT_CHAIN_ENGINE_CONFIG pConfig, IN BOOL fDefaultEngine, OUT BOOL& rfResult ) { HCERTSTORE hWorld = NULL; DWORD dwStoreFlags = CERT_SYSTEM_STORE_CURRENT_USER;
assert( pConfig->cbSize == sizeof( CERT_CHAIN_ENGINE_CONFIG ) );
rfResult = TRUE;
m_cRefs = 1; m_hRootStore = NULL; m_hRealRootStore = NULL; m_hTrustStore = NULL; m_hOtherStore = NULL; m_hCAStore = NULL; m_hEngineStore = NULL; m_hEngineStoreChangeEvent = NULL; m_pCertObjectCache = NULL; m_pSSCtlObjectCache = NULL; m_dwFlags = pConfig->dwFlags; if (0 == pConfig->dwUrlRetrievalTimeout) { m_dwUrlRetrievalTimeout = DEFAULT_ENGINE_URL_RETRIEVAL_TIMEOUT; m_fDefaultUrlRetrievalTimeout = TRUE; } else { m_dwUrlRetrievalTimeout = pConfig->dwUrlRetrievalTimeout; m_fDefaultUrlRetrievalTimeout = FALSE; } m_dwTouchEngineCount = 0;
m_pCrossCertDPEntry = NULL; m_pCrossCertDPLink = NULL; m_hCrossCertStore = NULL; m_dwCrossCertDPResyncIndex = 0; m_pAuthRootAutoUpdateInfo = NULL;
if ( !Pki_InitializeCriticalSection( &m_Lock )) { rfResult = FALSE; return; }
if ( pConfig->dwFlags & CERT_CHAIN_USE_LOCAL_MACHINE_STORE ) { dwStoreFlags = CERT_SYSTEM_STORE_LOCAL_MACHINE; }
if ( pConfig->dwFlags & CERT_CHAIN_ENABLE_SHARE_STORE ) { dwStoreFlags |= CERT_STORE_SHARE_STORE_FLAG; }
dwStoreFlags |= CERT_STORE_SHARE_CONTEXT_FLAG;
m_hRealRootStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, dwStoreFlags | CERT_STORE_MAXIMUM_ALLOWED_FLAG, L"root" );
if ( m_hRealRootStore == NULL ) { rfResult = FALSE; return; }
m_hCAStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, dwStoreFlags | CERT_STORE_MAXIMUM_ALLOWED_FLAG, L"ca" );
if ( pConfig->hRestrictedRoot != NULL ) { if ( ChainIsProperRestrictedRoot( m_hRealRootStore, pConfig->hRestrictedRoot ) == TRUE ) { m_hRootStore = CertDuplicateStore( pConfig->hRestrictedRoot );
// Having restricted roots implicitly disables the auto
// updating of roots
m_dwFlags |= CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE; } } else { m_hRootStore = CertDuplicateStore( m_hRealRootStore ); }
if ( m_hRootStore == NULL ) { rfResult = FALSE; return; }
if ( ( pConfig->hRestrictedTrust == NULL ) || ( pConfig->hRestrictedOther == NULL ) ) { rfResult = ChainCreateWorldStore( m_hRootStore, m_hCAStore, pConfig->cAdditionalStore, pConfig->rghAdditionalStore, dwStoreFlags, &hWorld );
if ( rfResult == FALSE ) { return; } }
if ( pConfig->hRestrictedTrust != NULL ) { m_hTrustStore = CertDuplicateStore( pConfig->hRestrictedTrust ); } else { m_hTrustStore = CertDuplicateStore( hWorld ); }
m_hOtherStore = CertOpenStore( CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL );
if ( m_hOtherStore != NULL ) { if ( pConfig->hRestrictedOther != NULL ) { rfResult = CertAddStoreToCollection( m_hOtherStore, pConfig->hRestrictedOther, 0, 0 );
if ( rfResult == TRUE ) { rfResult = CertAddStoreToCollection( m_hOtherStore, m_hRootStore, 0, 0 ); } } else { rfResult = CertAddStoreToCollection( m_hOtherStore, hWorld, 0, 0 );
if ( ( rfResult == TRUE ) && ( pConfig->hRestrictedTrust != NULL ) ) { rfResult = CertAddStoreToCollection( m_hOtherStore, pConfig->hRestrictedTrust, 0, 0 ); } } } else { rfResult = FALSE; }
if ( hWorld != NULL ) { CertCloseStore( hWorld, 0 ); }
if ( rfResult == TRUE ) { rfResult = ChainCreateEngineStore( m_hRootStore, m_hTrustStore, m_hOtherStore, fDefaultEngine, pConfig->dwFlags, &m_hEngineStore, &m_hEngineStoreChangeEvent ); }
if ( rfResult == TRUE ) { rfResult = ChainCreateCertificateObjectCache( pConfig->MaximumCachedCertificates, &m_pCertObjectCache ); }
if ( rfResult == TRUE ) { rfResult = SSCtlCreateObjectCache( &m_pSSCtlObjectCache ); }
if ( rfResult == TRUE ) { rfResult = m_pSSCtlObjectCache->PopulateCache( this ); }
assert( m_hRootStore != NULL );
// Beginning of cross certificate stuff
if ( rfResult == FALSE ) { return; }
m_hCrossCertStore = CertOpenStore( CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL );
if ( m_hCrossCertStore == NULL ) { rfResult = FALSE; return; }
rfResult = GetCrossCertDistPointsForStore( m_hEngineStore, &m_pCrossCertDPLink ); if ( rfResult == FALSE ) { return; }
rfResult = CertAddStoreToCollection( m_hOtherStore, m_hCrossCertStore, 0, 0 );
// End of cross certificate stuff
CertPerfIncrementChainEngineCurrentCount(); CertPerfIncrementChainEngineTotalCount(); }
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::~CCertChainEngine, public
//
// Synopsis: Destructor
//
//----------------------------------------------------------------------------
CCertChainEngine::~CCertChainEngine () { CertPerfDecrementChainEngineCurrentCount();
// Beginning of cross certificate stuff
FreeCrossCertDistPoints( &m_pCrossCertDPLink );
assert( NULL == m_pCrossCertDPLink ); assert( NULL == m_pCrossCertDPEntry );
if ( m_hCrossCertStore != NULL ) { CertCloseStore( m_hCrossCertStore, 0 ); }
// End of cross certificate stuff
FreeAuthRootAutoUpdateInfo(m_pAuthRootAutoUpdateInfo);
ChainFreeCertificateObjectCache( m_pCertObjectCache ); SSCtlFreeObjectCache( m_pSSCtlObjectCache );
if ( m_hRootStore != NULL ) { CertCloseStore( m_hRootStore, 0 ); }
if ( m_hRealRootStore != NULL ) { CertCloseStore( m_hRealRootStore, 0 ); }
if ( m_hTrustStore != NULL ) { CertCloseStore( m_hTrustStore, 0 ); }
if ( m_hOtherStore != NULL ) { CertCloseStore( m_hOtherStore, 0 ); }
if ( m_hCAStore != NULL ) { CertCloseStore( m_hCAStore, 0 ); }
if ( m_hEngineStore != NULL ) { if ( m_hEngineStoreChangeEvent != NULL ) { CertControlStore( m_hEngineStore, 0, // dwFlags
CERT_STORE_CTRL_CANCEL_NOTIFY, &m_hEngineStoreChangeEvent ); }
CertCloseStore( m_hEngineStore, 0 ); }
if ( m_hEngineStoreChangeEvent != NULL ) { CloseHandle( m_hEngineStoreChangeEvent ); }
DeleteCriticalSection( &m_Lock ); }
// "CrossCA"
const BYTE rgbEncodedCrossCAUnicodeString[] = { 0x1E, 0x0E, 0x00, 0x43, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x73, 0x00, 0x43, 0x00, 0x41 };
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::GetChainContext, public
//
// Synopsis: get a certificate chain context
//
// NOTE: This method acquires the engine lock
//
//----------------------------------------------------------------------------
BOOL CCertChainEngine::GetChainContext ( IN PCCERT_CONTEXT pCertContext, IN LPFILETIME pTime, IN OPTIONAL HCERTSTORE hAdditionalStore, IN OPTIONAL PCERT_CHAIN_PARA pChainPara, IN DWORD dwFlags, IN LPVOID pvReserved, OUT PCCERT_CHAIN_CONTEXT* ppChainContext ) { BOOL fResult; DWORD dwLastError = 0; PCCHAINCALLCONTEXT pCallContext = NULL; PCCERT_CHAIN_CONTEXT pChainContext = NULL;
if (!CallContextCreateCallObject( this, pTime, pChainPara, dwFlags, &pCallContext )) goto CallContextCreateCallObjectError;
if (!CreateChainContextFromPathGraph( pCallContext, pCertContext, hAdditionalStore, &pChainContext )) goto CreateChainContextFromPathGraphError;
if ((pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) && pCallContext->IsOnline()) { // For a revoked CA, try to retrieve a newer CA cert via the subject's
// AIA extension.
//
// Note, will only try for the first revoked CA cert in the first
// simple chain.
HCERTSTORE hNewerIssuerUrlStore = NULL; PCERT_SIMPLE_CHAIN pChain = pChainContext->rgpChain[0]; DWORD cEle = pChain->cElement; PCERT_CHAIN_ELEMENT *ppEle = pChain->rgpElement; DWORD i;
for (i = 1; i < cEle; i++) { PCERT_CHAIN_ELEMENT pIssuerEle = ppEle[i];
if (pIssuerEle->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED) { // First Revoked CA
PCCERT_CONTEXT pIssuerCert = pIssuerEle->pCertContext; PCERT_EXTENSION pExt;
// Ignore CrossCA's. If the CA cert has a Certificate
// Template Name extension we will check if its set to
// "CrossCA". Note, this is only a hint. Its not a
// requirement to have this extension for a cross cert.
pExt = CertFindExtension( szOID_ENROLL_CERTTYPE_EXTENSION, pIssuerCert->pCertInfo->cExtension, pIssuerCert->pCertInfo->rgExtension ); if (pExt && pExt->Value.cbData == sizeof(rgbEncodedCrossCAUnicodeString) && 0 == memcmp(pExt->Value.pbData, rgbEncodedCrossCAUnicodeString, sizeof(rgbEncodedCrossCAUnicodeString))) break; hNewerIssuerUrlStore = GetNewerIssuerUrlStore( pCallContext, ppEle[i - 1]->pCertContext, // Subject
pIssuerCert );
break; } }
if (hNewerIssuerUrlStore) { // Rebuild the chain using the newer AIA retrieved Issuer cert
HCERTSTORE hNewerAdditionalStore = NULL;
if (hAdditionalStore) { hNewerAdditionalStore = CertOpenStore( CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL ); if (hNewerAdditionalStore) { if (!CertAddStoreToCollection(hNewerAdditionalStore, hNewerIssuerUrlStore, 0, 0) || !CertAddStoreToCollection(hNewerAdditionalStore, hAdditionalStore, 0, 0)) {
CertCloseStore(hNewerAdditionalStore, 0); hNewerAdditionalStore = NULL; } } } else hNewerAdditionalStore = CertDuplicateStore(hNewerIssuerUrlStore);
if (hNewerAdditionalStore) { PCCERT_CHAIN_CONTEXT pNewerChainContext = NULL;
LockEngine();
pCallContext->FlushObjectsInCreationCache( );
UnlockEngine();
if (CreateChainContextFromPathGraph( pCallContext, pCertContext, hNewerAdditionalStore, &pNewerChainContext )) { assert(pNewerChainContext); CertFreeCertificateChain(pChainContext); pChainContext = pNewerChainContext; }
CertCloseStore(hNewerAdditionalStore, 0); }
CertCloseStore(hNewerIssuerUrlStore, 0); } }
fResult = TRUE;
CommonReturn: if (pCallContext) { LockEngine();
CallContextFreeCallObject(pCallContext);
UnlockEngine(); }
if (0 != dwLastError) SetLastError(dwLastError);
*ppChainContext = pChainContext; return fResult;
ErrorReturn: dwLastError = GetLastError();
assert(NULL == pChainContext); fResult = FALSE; goto CommonReturn;
TRACE_ERROR(CallContextCreateCallObjectError) TRACE_ERROR(CreateChainContextFromPathGraphError) }
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::CreateChainContextFromPathGraph, public
//
// Synopsis: builds a chain path graph and returns quality ordered
// chain contexts
//
// NOTE: This method acquires the engine lock
//
//----------------------------------------------------------------------------
BOOL CCertChainEngine::CreateChainContextFromPathGraph ( IN PCCHAINCALLCONTEXT pCallContext, IN PCCERT_CONTEXT pCertContext, IN OPTIONAL HCERTSTORE hAdditionalStore, OUT PCCERT_CHAIN_CONTEXT* ppChainContext ) { BOOL fResult; DWORD dwLastError = 0; BOOL fLocked = FALSE; BYTE rgbCertHash[CHAINHASHLEN]; DWORD cbCertHash; PCCERTOBJECT pEndCertObject = NULL; PCCHAINPATHOBJECT pEndPathObject = NULL; PCCHAINPATHOBJECT pTopPathObject = NULL; HCERTSTORE hAdditionalStoreToUse = NULL; HCERTSTORE hAllStore = NULL; PINTERNAL_CERT_CHAIN_CONTEXT pNewChainContext = NULL; // don't release
PINTERNAL_CERT_CHAIN_CONTEXT pChainContext = NULL; DWORD cChainContext = 0; DWORD dwFlags = pCallContext->CallFlags();
cbCertHash = CHAINHASHLEN; if (!CertGetCertificateContextProperty( pCertContext, CERT_MD5_HASH_PROP_ID, rgbCertHash, &cbCertHash ) || CHAINHASHLEN != cbCertHash) goto GetCertHashError;
if (hAdditionalStore) { if (!ChainCreateCollectionIncludingCtlCertificates( hAdditionalStore, &hAdditionalStoreToUse )) goto CreateAdditionalStoreCollectionError;
hAllStore = CertOpenStore( CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL ); if (NULL == hAllStore) goto OpenAllCollectionError; if (!CertAddStoreToCollection(hAllStore, OtherStore(), 0, 0 )) goto AddToAllCollectionError; if (!CertAddStoreToCollection(hAllStore, hAdditionalStoreToUse, 0, 0 )) goto AddToAllCollectionError; } else hAllStore = CertDuplicateStore(OtherStore());
LockEngine(); fLocked = TRUE;
// We're in this loop to handle the case where we leave the engine's
// critical section and another thread has entered the engine's
// critical section and done a resync or added a cached issuer cert object.
while (TRUE) { if (!Resync(pCallContext, FALSE)) goto ResyncError;
pCallContext->ResetTouchEngine();
assert(NULL == pEndCertObject); pEndCertObject = m_pCertObjectCache->FindIssuerObjectByHash( rgbCertHash);
fResult = TRUE; if (NULL == pEndCertObject) { pEndCertObject = m_pCertObjectCache->FindEndObjectByHash( rgbCertHash);
if (NULL == pEndCertObject) { fResult = ChainCreateCertObject( CERT_END_OBJECT_TYPE, pCallContext, pCertContext, rgbCertHash, &pEndCertObject ); } else { CertPerfIncrementChainEndCertInCacheCount(); } }
if (pCallContext->IsTouchedEngine()) { // The chain engine was touched at some point when we left
// the engine's lock to create the end cert object
if (pEndCertObject) { pEndCertObject->Release(); pEndCertObject = NULL; }
continue; }
if (!fResult) goto CreateCertObjectError; assert(pEndCertObject);
// This will create the entire path graph
fResult = ChainCreatePathObject( pCallContext, pEndCertObject, hAdditionalStoreToUse, &pEndPathObject );
if (pCallContext->IsTouchedEngine()) { // The chain engine was touched at some point when we left
// the engine's lock to verify a signature or do URL fetching.
pEndCertObject->Release(); pEndCertObject = NULL; pEndPathObject = NULL; pCallContext->FlushObjectsInCreationCache( ); } else break; }
if (!fResult) goto CreatePathObjectError;
if (pCallContext->CallOrEngineFlags() & CERT_CHAIN_CACHE_END_CERT) m_pCertObjectCache->AddEndObject(pCallContext, pEndCertObject);
// Create the ChainContext without holding the engine lock
UnlockEngine(); fLocked = FALSE;
// Loop through all the certificate paths:
// - Calculate additional status
// - Create chain context and its quality value
// - Determine highest quality chain
// - Optionally, maintain a linked list of the lower quality chains
while (pTopPathObject = pEndPathObject->NextPath( pCallContext, pTopPathObject )) { PCCHAINPATHOBJECT pPathObject;
// Loop downward to calculate additional status
for (pPathObject = pTopPathObject; pPathObject && !pPathObject->HasAdditionalStatus(); pPathObject = pPathObject->DownPathObject()) { pPathObject->CalculateAdditionalStatus( pCallContext, hAllStore ); }
// Also calculates the chain's quality value
pNewChainContext = pEndPathObject->CreateChainContextFromPath( pCallContext, pTopPathObject ); if (NULL == pNewChainContext) goto CreateChainContextFromPathError;
// Fixup end cert
ChainUpdateEndEntityCertContext(pNewChainContext, pCertContext);
// Add logic to call either the chain engine's or the caller's
// callback function here to provide additional chain context
// quality
if (NULL == pChainContext) { pChainContext = pNewChainContext; cChainContext = 1; } else { BOOL fNewHigherQuality = FALSE;
if (pNewChainContext->dwQuality > pChainContext->dwQuality) fNewHigherQuality = TRUE; else if (pNewChainContext->dwQuality == pChainContext->dwQuality) { BOOL fDupPublicKey = FALSE;
PCERT_SIMPLE_CHAIN pChain = pChainContext->ChainContext.rgpChain[0]; PCERT_SIMPLE_CHAIN pNewChain = pNewChainContext->ChainContext.rgpChain[0]; DWORD cElement = pChain->cElement; DWORD cNewElement = pNewChain->cElement;
if (cElement != cNewElement) { // Check if the longer chain has any duplicate public
// keys. This could happen if we have 2 sets of cross
// certificates
PCERT_SIMPLE_CHAIN pLongChain; DWORD cLongElement; DWORD i;
if (cElement > cNewElement) { pLongChain = pChain; cLongElement = cElement; } else { pLongChain = pNewChain; cLongElement = cNewElement; }
// Start with the CA and compare all keys up to and
// including the root
for (i = 1; i + 1 < cLongElement; i++) { DWORD j; DWORD cbHash; BYTE rgbHash0[ CHAINHASHLEN ];
cbHash = CHAINHASHLEN; if (!CertGetCertificateContextProperty( pLongChain->rgpElement[i]->pCertContext, CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID, rgbHash0, &cbHash ) || CHAINHASHLEN != cbHash) break;
for (j = i + 1; j < cLongElement; j++) { BYTE rgbHash1[ CHAINHASHLEN ];
cbHash = CHAINHASHLEN; if (!CertGetCertificateContextProperty( pLongChain->rgpElement[j]->pCertContext, CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID, rgbHash1, &cbHash ) || CHAINHASHLEN != cbHash) break;
if (0 == memcmp(rgbHash0, rgbHash1, CHAINHASHLEN)) { fDupPublicKey = TRUE; break; } }
if (fDupPublicKey) break; } }
if (fDupPublicKey) { if (cElement > cNewElement) fNewHigherQuality = TRUE; } else { DWORD i; DWORD cMinElement;
// Chains having certs with later NotAfter/NotBefore dates
// starting with the first CA cert are considered higher
// quality when dwQuality is the same. Will only compare
// the first simple chain.
cMinElement = min(cElement, cNewElement);
for (i = 1; i < cMinElement; i++) { LONG lCmp;
PCERT_INFO pCertInfo = pChain->rgpElement[i]->pCertContext->pCertInfo; PCERT_INFO pNewCertInfo = pNewChain->rgpElement[i]->pCertContext->pCertInfo; lCmp = CompareFileTime(&pNewCertInfo->NotAfter, &pCertInfo->NotAfter); if (0 < lCmp) { fNewHigherQuality = TRUE; break; } else if (0 > lCmp) { break; } else { // Same NotAfter. Check NotBefore.
lCmp = CompareFileTime(&pNewCertInfo->NotBefore, &pCertInfo->NotBefore); if (0 < lCmp) { fNewHigherQuality = TRUE; break; } else if (0 > lCmp) break; // else
// Same
} } } } // else
// fNewHigherQuality = FALSE;
if (fNewHigherQuality) { if (dwFlags & CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS) { pNewChainContext->pNext = pChainContext; pChainContext = pNewChainContext; cChainContext++; } else { ChainReleaseInternalChainContext(pChainContext); pChainContext = pNewChainContext; } } else { if (dwFlags & CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS) { PINTERNAL_CERT_CHAIN_CONTEXT p;
// Insert according to quality
for (p = pChainContext; p->pNext && p->pNext->dwQuality >= pNewChainContext->dwQuality; p = p->pNext) { ; }
pNewChainContext->pNext = p->pNext; p->pNext = pNewChainContext;
cChainContext++; } else { ChainReleaseInternalChainContext(pNewChainContext); } } } }
if (GetLastError() != CRYPT_E_NOT_FOUND) goto NextPathError;
assert(pChainContext && cChainContext);
if (cChainContext > 1) { PINTERNAL_CERT_CHAIN_CONTEXT p; PCCERT_CHAIN_CONTEXT *ppLower;
// Create array of lower quality chain contexts
ppLower = new PCCERT_CHAIN_CONTEXT [ cChainContext - 1]; if (NULL == ppLower) goto OutOfMemory;
pChainContext->ChainContext.cLowerQualityChainContext = cChainContext - 1; pChainContext->ChainContext.rgpLowerQualityChainContext = ppLower;
for (p = pChainContext->pNext; p; p = p->pNext, ppLower++) { assert(cChainContext > 1); cChainContext--;
*ppLower = (PCCERT_CHAIN_CONTEXT) p; }
}
assert(1 == cChainContext);
fResult = TRUE;
CommonReturn: if (!fLocked) LockEngine();
if (pEndCertObject) pEndCertObject->Release();
if (hAllStore) CertCloseStore(hAllStore, 0); if (hAdditionalStoreToUse) CertCloseStore(hAdditionalStoreToUse, 0);
*ppChainContext = (PCCERT_CHAIN_CONTEXT) pChainContext;
UnlockEngine();
if (0 != dwLastError) SetLastError(dwLastError); return fResult;
ErrorReturn: dwLastError = GetLastError();
if (pChainContext) { PINTERNAL_CERT_CHAIN_CONTEXT p;
while (p = pChainContext->pNext) { pChainContext->pNext = p->pNext; ChainReleaseInternalChainContext(p); }
ChainReleaseInternalChainContext(pChainContext); pChainContext = NULL; }
fResult = FALSE; goto CommonReturn;
TRACE_ERROR(GetCertHashError) TRACE_ERROR(CreateAdditionalStoreCollectionError) TRACE_ERROR(OpenAllCollectionError) TRACE_ERROR(AddToAllCollectionError) TRACE_ERROR(ResyncError) TRACE_ERROR(CreateCertObjectError) TRACE_ERROR(CreatePathObjectError) TRACE_ERROR(CreateChainContextFromPathError) TRACE_ERROR(NextPathError) SET_ERROR(OutOfMemory, E_OUTOFMEMORY) }
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::GetIssuerUrlStore, public
//
// Synopsis: if the certificate has an Authority Info Access extension,
// return a store containing the issuing certificates
//
// 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 CCertChainEngine::GetIssuerUrlStore( IN PCCHAINCALLCONTEXT pCallContext, IN PCCERT_CONTEXT pSubjectCertContext, IN DWORD dwRetrievalFlags, OUT HCERTSTORE *phIssuerUrlStore ) { BOOL fTouchedResult = TRUE; BOOL fResult; DWORD cbUrlArray; PCRYPT_URL_ARRAY pUrlArray = NULL; DWORD cCount; DWORD dwCacheResultFlag;
*phIssuerUrlStore = NULL;
dwRetrievalFlags |= CRYPT_RETRIEVE_MULTIPLE_OBJECTS | CRYPT_LDAP_SCOPE_BASE_ONLY_RETRIEVAL | CRYPT_OFFLINE_CHECK_RETRIEVAL;
fResult = ChainGetObjectUrl( URL_OID_CERTIFICATE_ISSUER, (LPVOID) pSubjectCertContext, CRYPT_GET_URL_FROM_EXTENSION, NULL, &cbUrlArray, NULL, NULL, NULL );
if ( fResult == TRUE ) { pUrlArray = (PCRYPT_URL_ARRAY)new BYTE [ cbUrlArray ]; if ( pUrlArray == NULL ) { SetLastError( (DWORD) E_OUTOFMEMORY ); return( FALSE ); }
fResult = ChainGetObjectUrl( URL_OID_CERTIFICATE_ISSUER, (LPVOID) pSubjectCertContext, CRYPT_GET_URL_FROM_EXTENSION, pUrlArray, &cbUrlArray, NULL, NULL, NULL ); }
if ( fResult == TRUE ) { BOOL fLocked = FALSE;
//
// We are about to go on the wire to retrieve the issuer certificate.
// At this time we will release the chain engine lock so others can
// go about there business while we wait for the protocols to do the
// fetching.
//
UnlockEngine();
for ( cCount = 0; cCount < pUrlArray->cUrl; cCount++ ) { if ( !( dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL ) && ( ChainIsFileOrLdapUrl( pUrlArray->rgwszUrl[ cCount ] ) == TRUE ) ) { dwCacheResultFlag = CRYPT_DONT_CACHE_RESULT; } else { dwCacheResultFlag = 0; }
fResult = ChainRetrieveObjectByUrlW( pUrlArray->rgwszUrl[ cCount ], CONTEXT_OID_CERTIFICATE, dwRetrievalFlags | dwCacheResultFlag, pCallContext->ChainPara()->dwUrlRetrievalTimeout, (LPVOID *)phIssuerUrlStore, NULL, NULL, NULL, NULL );
if ( fResult == TRUE ) { CertPerfIncrementChainUrlIssuerCount(); if (dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL) CertPerfIncrementChainCacheOnlyUrlIssuerCount();
//
// Retake the engine lock. Also check if the engine was
// touched during our absence.
//
LockEngine(); if (pCallContext->IsTouchedEngine()) { fTouchedResult = FALSE; SetLastError( (DWORD) ERROR_CAN_NOT_COMPLETE ); }
fLocked = TRUE;
ChainCopyToCAStore( this, *phIssuerUrlStore );
if (!fTouchedResult) { CertCloseStore(*phIssuerUrlStore, 0); *phIssuerUrlStore = NULL; }
break; } }
//
// Retake the engine lock if necessary
//
if ( fLocked == FALSE ) { LockEngine(); if (pCallContext->IsTouchedEngine()) { fTouchedResult = FALSE; SetLastError( (DWORD) ERROR_CAN_NOT_COMPLETE ); } } }
delete (LPBYTE)pUrlArray;
// NOTE: Need to somehow log that we tried to retrieve the issuer but
// it was inaccessible
return( fTouchedResult ); }
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::GetNewerIssuerUrlStore, public
//
// Synopsis: if the subject certificate has an Authority Info Access
// extension, attempts an online URL retrieval of the
// issuer certificate(s). If any of the URL retrieved
// certs are different from the input Issuer cert,
// returns a store containing the issuing certificates.
// Otherwise, returns NULL store.
//
// Assumption: Chain engine isn't locked in the calling thread. Also,
// only called if online.
//
//----------------------------------------------------------------------------
HCERTSTORE CCertChainEngine::GetNewerIssuerUrlStore( IN PCCHAINCALLCONTEXT pCallContext, IN PCCERT_CONTEXT pSubjectCertContext, IN PCCERT_CONTEXT pIssuerCertContext ) { HCERTSTORE hNewIssuerUrlStore = NULL;
LockEngine();
while (TRUE) { pCallContext->ResetTouchEngine();
GetIssuerUrlStore( pCallContext, pSubjectCertContext, CRYPT_WIRE_ONLY_RETRIEVAL, &hNewIssuerUrlStore ); if (!pCallContext->IsTouchedEngine()) break;
assert(NULL == hNewIssuerUrlStore); }
UnlockEngine();
if (hNewIssuerUrlStore) { // Discard if it doesn't contain more than just the input
// pIssuerCertContext
PCCERT_CONTEXT pCert;
pCert = NULL; while (pCert = CertEnumCertificatesInStore(hNewIssuerUrlStore, pCert)) { if (!CertCompareCertificate( pCert->dwCertEncodingType, pCert->pCertInfo, pIssuerCertContext->pCertInfo )) { CertFreeCertificateContext(pCert); return hNewIssuerUrlStore; } }
CertCloseStore(hNewIssuerUrlStore, 0); }
return NULL; }
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::Resync, public
//
// Synopsis: resync the store if necessary
//
// 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.
//
// A resync increments the engine's touch count.
//
// Assumption: Chain engine is locked once in the calling thread.
//
//----------------------------------------------------------------------------
BOOL CCertChainEngine::Resync (IN PCCHAINCALLCONTEXT pCallContext, BOOL fForce) { BOOL fResync = FALSE; BOOL fResult = TRUE;
if ( fForce == FALSE ) { if ( WaitForSingleObject( m_hEngineStoreChangeEvent, 0 ) == WAIT_OBJECT_0 ) { fResync = TRUE; } } else { fResync = TRUE; }
if ( fResync ) { CertControlStore( m_hEngineStore, CERT_STORE_CTRL_INHIBIT_DUPLICATE_HANDLE_FLAG, CERT_STORE_CTRL_RESYNC, &m_hEngineStoreChangeEvent );
m_pCertObjectCache->FlushObjects( pCallContext );
fResult = m_pSSCtlObjectCache->Resync( this );
assert( fResult == TRUE );
assert( m_hCrossCertStore );
// Remove CrossCert collection from engine's list. Don't want to
// also search it for cross cert distribution points
CertRemoveStoreFromCollection( m_hOtherStore, m_hCrossCertStore );
fResult = GetCrossCertDistPointsForStore( m_hEngineStore, &m_pCrossCertDPLink );
CertAddStoreToCollection( m_hOtherStore, m_hCrossCertStore, 0, 0 );
pCallContext->TouchEngine();
CertPerfIncrementChainEngineResyncCount(); }
if ( fResult ) { while (TRUE ) { pCallContext->ResetTouchEngine();
// The following 2 updates leave 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 and IsTouchedEngine() is TRUE.
UpdateCrossCerts(pCallContext); if (pCallContext->IsTouchedEngine()) continue;
m_pSSCtlObjectCache->UpdateCache(this, pCallContext); if (!pCallContext->IsTouchedEngine()) break; } }
return( TRUE ); }
//+===========================================================================
// CCertObject helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Function: ChainCreateCertObject
//
// Synopsis: create a cert object, note since it is a ref-counted
// object, freeing occurs by doing a pCertObject->Release
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCreateCertObject ( IN DWORD dwObjectType, IN PCCHAINCALLCONTEXT pCallContext, IN PCCERT_CONTEXT pCertContext, IN OPTIONAL LPBYTE pbCertHash, OUT PCCERTOBJECT *ppCertObject ) { BOOL fResult = TRUE; PCCERTOBJECT pCertObject; BYTE rgbHash[CHAINHASHLEN];
if (NULL == pbCertHash) { DWORD cbHash = CHAINHASHLEN;
if (!CertGetCertificateContextProperty( pCertContext, CERT_MD5_HASH_PROP_ID, rgbHash, &cbHash ) || CHAINHASHLEN != cbHash) { *ppCertObject = NULL; return FALSE; } pbCertHash = rgbHash; }
if (CERT_CACHED_ISSUER_OBJECT_TYPE == dwObjectType) { pCertObject = pCallContext->ChainEngine()->CertObjectCache()->FindIssuerObjectByHash( pbCertHash);
if (NULL != pCertObject) { *ppCertObject = pCertObject; return TRUE; } } else { PCCHAINPATHOBJECT pPathObject;
pPathObject = pCallContext->FindPathObjectInCreationCache( pbCertHash); if (NULL != pPathObject) { pCertObject = pPathObject->CertObject(); pCertObject->AddRef(); *ppCertObject = pCertObject;
return TRUE; } }
pCertObject = new CCertObject( dwObjectType, pCallContext, pCertContext, pbCertHash, fResult );
if (NULL != pCertObject) { if (!fResult) { pCertObject->Release(); pCertObject = NULL; } else if (CERT_CACHED_ISSUER_OBJECT_TYPE == dwObjectType) { // Following add increments the engine's touch count
pCallContext->ChainEngine()->CertObjectCache()->AddIssuerObject( pCallContext, pCertObject ); } } else { fResult = FALSE;
}
*ppCertObject = pCertObject; return fResult; }
//+---------------------------------------------------------------------------
//
// Function: ChainFillCertObjectCtlCacheEnumFn
//
// Synopsis: CSSCtlObjectCache::EnumObjects callback used to create
// the linked list of CTL cache entries.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainFillCertObjectCtlCacheEnumFn( IN LPVOID pvParameter, IN PCSSCTLOBJECT pSSCtlObject ) { PCERT_OBJECT_CTL_CACHE_ENUM_DATA pEnumData = (PCERT_OBJECT_CTL_CACHE_ENUM_DATA) pvParameter; PCERT_TRUST_LIST_INFO pTrustListInfo = NULL; PCERT_OBJECT_CTL_CACHE_ENTRY pEntry = NULL;
if (!pEnumData->fResult) return FALSE;
if (!pSSCtlObject->GetTrustListInfo( pEnumData->pCertObject->CertContext(), &pTrustListInfo )) { DWORD dwErr = GetLastError(); if (CRYPT_E_NOT_FOUND == dwErr) return TRUE; else { pEnumData->fResult = FALSE; pEnumData->dwLastError = dwErr; return FALSE; } }
pEntry = new CERT_OBJECT_CTL_CACHE_ENTRY; if (NULL == pEntry) { SSCtlFreeTrustListInfo(pTrustListInfo);
pEnumData->fResult = FALSE; pEnumData->dwLastError = (DWORD) E_OUTOFMEMORY; return FALSE; }
pSSCtlObject->AddRef(); pEntry->pSSCtlObject = pSSCtlObject; pEntry->pTrustListInfo = pTrustListInfo; pEnumData->pCertObject->InsertCtlCacheEntry(pEntry); return TRUE; }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeCertObjectCtlCache
//
// Synopsis: free the linked list of CTL cache entries.
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeCertObjectCtlCache( IN PCERT_OBJECT_CTL_CACHE_ENTRY pCtlCacheHead ) { PCERT_OBJECT_CTL_CACHE_ENTRY pCtlCache;
while (pCtlCache = pCtlCacheHead) { pCtlCacheHead = pCtlCacheHead->pNext;
if (pCtlCache->pTrustListInfo) SSCtlFreeTrustListInfo(pCtlCache->pTrustListInfo);
if (pCtlCache->pSSCtlObject) pCtlCache->pSSCtlObject->Release();
delete pCtlCache; } }
//+---------------------------------------------------------------------------
//
// Function: ChainAllocAndDecodeObject
//
// Synopsis: allocate and decodes the ASN.1 encoded data structure.
//
// NULL is returned for a decoding or allocation error.
// PkiFree must be called to free the allocated data structure.
//
//----------------------------------------------------------------------------
LPVOID WINAPI ChainAllocAndDecodeObject( IN LPCSTR lpszStructType, IN const BYTE *pbEncoded, IN DWORD cbEncoded ) { DWORD cbStructInfo; void *pvStructInfo;
if (!CryptDecodeObjectEx( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, lpszStructType, pbEncoded, cbEncoded, CRYPT_DECODE_SHARE_OID_STRING_FLAG | CRYPT_DECODE_NOCOPY_FLAG | CRYPT_DECODE_ALLOC_FLAG, &PkiDecodePara, (void *) &pvStructInfo, &cbStructInfo )) goto DecodeError;
CommonReturn: return pvStructInfo; ErrorReturn: pvStructInfo = NULL; goto CommonReturn; TRACE_ERROR(DecodeError) }
//+---------------------------------------------------------------------------
//
// Function: ChainGetIssuerMatchInfo
//
// Synopsis: return match bits specifying the types of issuer matching
// that can be done for this certificate and if available return
// the decoded authority key identifier extension
//
//----------------------------------------------------------------------------
VOID WINAPI ChainGetIssuerMatchInfo ( IN PCCERT_CONTEXT pCertContext, OUT DWORD *pdwIssuerMatchFlags, OUT PCERT_AUTHORITY_KEY_ID_INFO* ppAuthKeyIdentifier ) { PCERT_EXTENSION pExt; LPVOID pv = NULL; BOOL fV1AuthKeyIdInfo = TRUE; PCERT_AUTHORITY_KEY_ID_INFO pAuthKeyIdentifier = NULL; DWORD dwIssuerMatchFlags = 0;
pExt = CertFindExtension( szOID_AUTHORITY_KEY_IDENTIFIER, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension );
if ( pExt == NULL ) { fV1AuthKeyIdInfo = FALSE;
pExt = CertFindExtension( szOID_AUTHORITY_KEY_IDENTIFIER2, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension ); }
if ( pExt != NULL ) {
pv = ChainAllocAndDecodeObject( pExt->pszObjId, pExt->Value.pbData, pExt->Value.cbData ); }
if ( pv ) { if ( fV1AuthKeyIdInfo == FALSE ) { // NOTENOTE: Yes, this is a bit backwards but, right now but the
// V1 structure is a bit easier to deal with and we
// only support the V1 version of the V2 structure
// anyway
ChainConvertAuthKeyIdentifierFromV2ToV1( (PCERT_AUTHORITY_KEY_ID2_INFO)pv, &pAuthKeyIdentifier );
} else { pAuthKeyIdentifier = (PCERT_AUTHORITY_KEY_ID_INFO)pv; pv = NULL; }
if ( pAuthKeyIdentifier != NULL ) { if ( ( pAuthKeyIdentifier->CertIssuer.cbData != 0 ) && ( pAuthKeyIdentifier->CertSerialNumber.cbData != 0 ) ) { dwIssuerMatchFlags |= CERT_EXACT_ISSUER_MATCH_FLAG; }
if ( pAuthKeyIdentifier->KeyId.cbData != 0 ) { dwIssuerMatchFlags |= CERT_KEYID_ISSUER_MATCH_FLAG; }
if (0 == dwIssuerMatchFlags) { delete (LPBYTE) pAuthKeyIdentifier; pAuthKeyIdentifier = NULL; }
} }
dwIssuerMatchFlags |= CERT_NAME_ISSUER_MATCH_FLAG;
if (pv) PkiFree(pv);
*pdwIssuerMatchFlags = dwIssuerMatchFlags; *ppAuthKeyIdentifier = pAuthKeyIdentifier; }
//+---------------------------------------------------------------------------
//
// Function: ChainConvertAuthKeyIdentifierFromV2ToV1
//
// Synopsis: convert authority key identifier from V2 to V1
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainConvertAuthKeyIdentifierFromV2ToV1 ( IN PCERT_AUTHORITY_KEY_ID2_INFO pAuthKeyIdentifier2, OUT PCERT_AUTHORITY_KEY_ID_INFO* ppAuthKeyIdentifier ) { DWORD cb; PCERT_AUTHORITY_KEY_ID_INFO pAuthKeyIdentifier; BOOL fExactMatchAvailable = FALSE;
if ( ( pAuthKeyIdentifier2->AuthorityCertIssuer.cAltEntry == 1 ) && ( pAuthKeyIdentifier2->AuthorityCertIssuer.rgAltEntry[0].dwAltNameChoice == CERT_ALT_NAME_DIRECTORY_NAME ) ) { fExactMatchAvailable = TRUE; }
cb = sizeof( CERT_AUTHORITY_KEY_ID_INFO ); cb += pAuthKeyIdentifier2->KeyId.cbData;
if ( fExactMatchAvailable == TRUE ) { cb += pAuthKeyIdentifier2->AuthorityCertIssuer.rgAltEntry[0].DirectoryName.cbData; cb += pAuthKeyIdentifier2->AuthorityCertSerialNumber.cbData; }
pAuthKeyIdentifier = (PCERT_AUTHORITY_KEY_ID_INFO)PkiZeroAlloc(cb); if ( pAuthKeyIdentifier == NULL ) { return( FALSE ); }
pAuthKeyIdentifier->KeyId.cbData = pAuthKeyIdentifier2->KeyId.cbData; pAuthKeyIdentifier->KeyId.pbData = (LPBYTE)pAuthKeyIdentifier + sizeof( CERT_AUTHORITY_KEY_ID_INFO );
memcpy( pAuthKeyIdentifier->KeyId.pbData, pAuthKeyIdentifier2->KeyId.pbData, pAuthKeyIdentifier->KeyId.cbData );
if ( fExactMatchAvailable == TRUE ) { pAuthKeyIdentifier->CertIssuer.cbData = pAuthKeyIdentifier2->AuthorityCertIssuer.rgAltEntry[0].DirectoryName.cbData; pAuthKeyIdentifier->CertIssuer.pbData = pAuthKeyIdentifier->KeyId.pbData + pAuthKeyIdentifier->KeyId.cbData;
memcpy( pAuthKeyIdentifier->CertIssuer.pbData, pAuthKeyIdentifier2->AuthorityCertIssuer.rgAltEntry[0].DirectoryName.pbData, pAuthKeyIdentifier->CertIssuer.cbData );
pAuthKeyIdentifier->CertSerialNumber.cbData = pAuthKeyIdentifier2->AuthorityCertSerialNumber.cbData; pAuthKeyIdentifier->CertSerialNumber.pbData = pAuthKeyIdentifier->CertIssuer.pbData + pAuthKeyIdentifier->CertIssuer.cbData;
memcpy( pAuthKeyIdentifier->CertSerialNumber.pbData, pAuthKeyIdentifier2->AuthorityCertSerialNumber.pbData, pAuthKeyIdentifier->CertSerialNumber.cbData ); }
*ppAuthKeyIdentifier = pAuthKeyIdentifier;
return( TRUE ); }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeAuthorityKeyIdentifier
//
// Synopsis: free the authority key identifier
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeAuthorityKeyIdentifier ( IN PCERT_AUTHORITY_KEY_ID_INFO pAuthKeyIdInfo ) { PkiFree(pAuthKeyIdInfo); }
//+---------------------------------------------------------------------------
//
// Function: ChainProcessSpecialOrDuplicateOIDsInUsage
//
// Synopsis: process and removes special or duplicate OIDs from the usage
//
// For szOID_ANY_CERT_POLICY, frees the usage
//
//----------------------------------------------------------------------------
VOID WINAPI ChainProcessSpecialOrDuplicateOIDsInUsage ( IN OUT PCERT_ENHKEY_USAGE *ppUsage, IN OUT DWORD *pdwFlags ) { PCERT_ENHKEY_USAGE pUsage = *ppUsage; DWORD dwFlags = *pdwFlags; LPSTR *ppszOID; DWORD cOID; DWORD i;
cOID = pUsage->cUsageIdentifier; ppszOID = pUsage->rgpszUsageIdentifier;
i = 0; while (i < cOID) { BOOL fSpecialOrDuplicate = TRUE; LPSTR pszOID = ppszOID[i];
if (0 == strcmp(pszOID, szOID_ANY_CERT_POLICY)) dwFlags |= CHAIN_ANY_POLICY_FLAG; else { // Check for duplicate OID
DWORD j;
fSpecialOrDuplicate = FALSE; for (j = 0; j < i; j++) { if (0 == strcmp(ppszOID[j], ppszOID[i])) { fSpecialOrDuplicate = TRUE; break; } } }
if (fSpecialOrDuplicate) { // Remove the special or duplicate OID string and move the remaining
// strings up one.
DWORD j;
for (j = i; j + 1 < cOID; j++) ppszOID[j] = ppszOID[j + 1];
cOID--; pUsage->cUsageIdentifier = cOID; } else i++; }
if (dwFlags & CHAIN_ANY_POLICY_FLAG) { PkiFree(pUsage); *ppUsage = NULL; } *pdwFlags = dwFlags; }
//+---------------------------------------------------------------------------
//
// Function: ChainConvertPoliciesToUsage
//
// Synopsis: extract the usage OIDs from the cert policies
//
//----------------------------------------------------------------------------
VOID WINAPI ChainConvertPoliciesToUsage ( IN PCERT_POLICIES_INFO pPolicy, IN OUT DWORD *pdwFlags, OUT PCERT_ENHKEY_USAGE *ppUsage ) { PCERT_ENHKEY_USAGE pUsage; LPSTR *ppszOID; DWORD cOID; DWORD i;
cOID = pPolicy->cPolicyInfo;
pUsage = (PCERT_ENHKEY_USAGE) PkiNonzeroAlloc( sizeof(CERT_ENHKEY_USAGE) + sizeof(LPSTR) * cOID);
if (NULL == pUsage) { *pdwFlags |= CHAIN_INVALID_POLICY_FLAG; *ppUsage = NULL; return; }
ppszOID = (LPSTR *) &pUsage[1];
pUsage->cUsageIdentifier = cOID; pUsage->rgpszUsageIdentifier = ppszOID;
for (i = 0; i < cOID; i++) ppszOID[i] = pPolicy->rgPolicyInfo[i].pszPolicyIdentifier;
*ppUsage = pUsage;
ChainProcessSpecialOrDuplicateOIDsInUsage(ppUsage, pdwFlags); }
//+---------------------------------------------------------------------------
//
// Function: ChainRemoveDuplicatePolicyMappings
//
// Synopsis: remove any duplicate mappings
//
//----------------------------------------------------------------------------
VOID WINAPI ChainRemoveDuplicatePolicyMappings ( IN OUT PCERT_POLICY_MAPPINGS_INFO pInfo ) { DWORD cMap = pInfo->cPolicyMapping; PCERT_POLICY_MAPPING pMap = pInfo->rgPolicyMapping; DWORD i;
i = 0; while (i < cMap) { DWORD j;
for (j = 0; j < i; j++) { if (0 == strcmp(pMap[i].pszSubjectDomainPolicy, pMap[j].pszSubjectDomainPolicy)) break; }
if (j < i) { // Duplicate
//
// Remove the duplicate mapping and move the remaining
// mappings up one.
for (j = i; j + 1 < cMap; j++) pMap[j] = pMap[j + 1];
cMap--; pInfo->cPolicyMapping = cMap; } else i++; }
}
//+---------------------------------------------------------------------------
//
// Function: ChainGetPoliciesInfo
//
// Synopsis: allocate and return the policies and usage info
//
//----------------------------------------------------------------------------
VOID WINAPI ChainGetPoliciesInfo ( IN PCCERT_CONTEXT pCertContext, IN OUT PCHAIN_POLICIES_INFO pPoliciesInfo ) { DWORD cExt = pCertContext->pCertInfo->cExtension; PCERT_EXTENSION rgExt = pCertContext->pCertInfo->rgExtension; DWORD i; DWORD cbData;
for (i = 0; i < CHAIN_ISS_OR_APP_COUNT; i++ ) { PCHAIN_ISS_OR_APP_INFO pInfo = &pPoliciesInfo->rgIssOrAppInfo[i]; PCERT_EXTENSION pExt;
pExt = CertFindExtension( CHAIN_ISS_INDEX == i ? szOID_CERT_POLICIES : szOID_APPLICATION_CERT_POLICIES, cExt, rgExt); if (pExt) { pInfo->pPolicy = (PCERT_POLICIES_INFO) ChainAllocAndDecodeObject( X509_CERT_POLICIES, pExt->Value.pbData, pExt->Value.cbData );
if (NULL == pInfo->pPolicy) pInfo->dwFlags |= CHAIN_INVALID_POLICY_FLAG; else ChainConvertPoliciesToUsage(pInfo->pPolicy, &pInfo->dwFlags, &pInfo->pUsage); } else if (CHAIN_APP_INDEX == i) { pExt = CertFindExtension(szOID_ENHANCED_KEY_USAGE, cExt, rgExt); if (pExt) { pInfo->pUsage = (PCERT_ENHKEY_USAGE) ChainAllocAndDecodeObject( X509_ENHANCED_KEY_USAGE, pExt->Value.pbData, pExt->Value.cbData );
if (NULL == pInfo->pUsage) pInfo->dwFlags |= CHAIN_INVALID_POLICY_FLAG; else ChainProcessSpecialOrDuplicateOIDsInUsage( &pInfo->pUsage, &pInfo->dwFlags); } }
pExt = CertFindExtension( CHAIN_ISS_INDEX == i ? szOID_POLICY_MAPPINGS : szOID_APPLICATION_POLICY_MAPPINGS, cExt, rgExt); if (pExt) { pInfo->pMappings = (PCERT_POLICY_MAPPINGS_INFO) ChainAllocAndDecodeObject( X509_POLICY_MAPPINGS, pExt->Value.pbData, pExt->Value.cbData );
if (NULL == pInfo->pMappings) pInfo->dwFlags |= CHAIN_INVALID_POLICY_FLAG; else ChainRemoveDuplicatePolicyMappings(pInfo->pMappings); }
pExt = CertFindExtension( CHAIN_ISS_INDEX == i ? szOID_POLICY_CONSTRAINTS : szOID_APPLICATION_POLICY_CONSTRAINTS, cExt, rgExt); if (pExt) { pInfo->pConstraints = (PCERT_POLICY_CONSTRAINTS_INFO) ChainAllocAndDecodeObject( X509_POLICY_CONSTRAINTS, pExt->Value.pbData, pExt->Value.cbData );
if (NULL == pInfo->pConstraints) pInfo->dwFlags |= CHAIN_INVALID_POLICY_FLAG; } }
cbData = 0; if (CertGetCertificateContextProperty( pCertContext, CERT_ENHKEY_USAGE_PROP_ID, NULL, // pbData
&cbData ) && 0 != cbData) { BYTE *pbData;
pbData = (BYTE *) PkiNonzeroAlloc(cbData); if (pbData) { if (CertGetCertificateContextProperty( pCertContext, CERT_ENHKEY_USAGE_PROP_ID, pbData, &cbData )) pPoliciesInfo->pPropertyUsage = (PCERT_ENHKEY_USAGE) ChainAllocAndDecodeObject( X509_ENHANCED_KEY_USAGE, pbData, cbData );
PkiFree(pbData); }
if (NULL == pPoliciesInfo->pPropertyUsage) pPoliciesInfo->rgIssOrAppInfo[CHAIN_APP_INDEX].dwFlags |= CHAIN_INVALID_POLICY_FLAG; } }
//+---------------------------------------------------------------------------
//
// Function: ChainFreePoliciesInfo
//
// Synopsis: free the policies and usage info
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreePoliciesInfo ( IN OUT PCHAIN_POLICIES_INFO pPoliciesInfo ) { DWORD i;
for (i = 0; i < CHAIN_ISS_OR_APP_COUNT; i++ ) { PCHAIN_ISS_OR_APP_INFO pInfo = &pPoliciesInfo->rgIssOrAppInfo[i];
PkiFree(pInfo->pPolicy); PkiFree(pInfo->pUsage); PkiFree(pInfo->pMappings); PkiFree(pInfo->pConstraints); }
PkiFree(pPoliciesInfo->pPropertyUsage); }
//+---------------------------------------------------------------------------
//
// Function: ChainGetBasicConstraintsInfo
//
// Synopsis: alloc and return the basic constraints info.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainGetBasicConstraintsInfo ( IN PCCERT_CONTEXT pCertContext, IN OUT PCERT_BASIC_CONSTRAINTS2_INFO *ppInfo ) { BOOL fResult; PCERT_EXTENSION pExt; PCERT_BASIC_CONSTRAINTS2_INFO pInfo = NULL; PCERT_BASIC_CONSTRAINTS_INFO pLegacyInfo = NULL;
pExt = CertFindExtension( szOID_BASIC_CONSTRAINTS2, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension );
if (pExt) { pInfo = (PCERT_BASIC_CONSTRAINTS2_INFO) ChainAllocAndDecodeObject( X509_BASIC_CONSTRAINTS2, pExt->Value.pbData, pExt->Value.cbData ); if (NULL == pInfo) goto DecodeError; } else { // Try to find the legacy extension
pExt = CertFindExtension( szOID_BASIC_CONSTRAINTS, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension );
if (pExt) { pLegacyInfo = (PCERT_BASIC_CONSTRAINTS_INFO) ChainAllocAndDecodeObject( X509_BASIC_CONSTRAINTS, pExt->Value.pbData, pExt->Value.cbData ); if (NULL == pLegacyInfo) goto DecodeError;
// Convert to new format
pInfo = (PCERT_BASIC_CONSTRAINTS2_INFO) PkiZeroAlloc( sizeof(CERT_BASIC_CONSTRAINTS2_INFO)); if (NULL == pInfo) goto OutOfMemory;
if (pLegacyInfo->SubjectType.cbData > 0 && (pLegacyInfo->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG)) { pInfo->fCA = TRUE; pInfo->fPathLenConstraint = pLegacyInfo->fPathLenConstraint; pInfo->dwPathLenConstraint = pLegacyInfo->dwPathLenConstraint; } } }
fResult = TRUE; CommonReturn: if (pLegacyInfo) PkiFree(pLegacyInfo); *ppInfo = pInfo; return fResult;
ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(DecodeError) TRACE_ERROR(OutOfMemory) }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeBasicConstraintsInfo
//
// Synopsis: free the basic constraints info
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeBasicConstraintsInfo ( IN OUT PCERT_BASIC_CONSTRAINTS2_INFO pInfo ) { PkiFree(pInfo); }
//+---------------------------------------------------------------------------
//
// Function: ChainGetKeyUsage
//
// Synopsis: alloc and return the key usage.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainGetKeyUsage ( IN PCCERT_CONTEXT pCertContext, IN OUT PCRYPT_BIT_BLOB *ppKeyUsage ) { BOOL fResult; PCERT_EXTENSION pExt; PCRYPT_BIT_BLOB pKeyUsage = NULL;
pExt = CertFindExtension( szOID_KEY_USAGE, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension );
if (pExt) { pKeyUsage = (PCRYPT_BIT_BLOB) ChainAllocAndDecodeObject( X509_KEY_USAGE, pExt->Value.pbData, pExt->Value.cbData ); if (NULL == pKeyUsage) goto DecodeError; }
fResult = TRUE; CommonReturn: *ppKeyUsage = pKeyUsage; return fResult;
ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(DecodeError) }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeKeyUsage
//
// Synopsis: free the key usage
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeKeyUsage ( IN OUT PCRYPT_BIT_BLOB pKeyUsage ) { PkiFree(pKeyUsage); }
//+---------------------------------------------------------------------------
//
// Function: ChainGetSelfSignedStatus
//
// Synopsis: return status bits specifying if the certificate is self signed
// and if so, if it is signature valid
//
//----------------------------------------------------------------------------
VOID WINAPI ChainGetSelfSignedStatus ( IN PCCHAINCALLCONTEXT pCallContext, IN PCCERTOBJECT pCertObject, IN OUT DWORD *pdwIssuerStatusFlags ) { DWORD dwInfoStatus = 0;
// If the certificate has an AKI, then, ignore name matching
if (ChainGetMatchInfoStatus(pCertObject, pCertObject, &dwInfoStatus) && (CERT_TRUST_HAS_NAME_MATCH_ISSUER != dwInfoStatus)) { *pdwIssuerStatusFlags |= CERT_ISSUER_SELF_SIGNED_FLAG;
if (CryptVerifyCertificateSignatureEx( NULL, // hCryptProv
X509_ASN_ENCODING, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *) pCertObject->CertContext(), CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *) pCertObject->CertContext(), 0, // dwFlags
NULL // pvReserved
)) *pdwIssuerStatusFlags |= CERT_ISSUER_VALID_SIGNATURE_FLAG;
CertPerfIncrementChainVerifyCertSignatureCount(); } }
//+---------------------------------------------------------------------------
//
// Function: ChainGetRootStoreStatus
//
// Synopsis: determine if the certificate with the given hash is in the
// root store
//
// Assumption: Chain engine is locked once in the calling thread.
//----------------------------------------------------------------------------
VOID WINAPI ChainGetRootStoreStatus ( IN HCERTSTORE hRoot, IN HCERTSTORE hRealRoot, IN BYTE rgbCertHash[ CHAINHASHLEN ], IN OUT DWORD *pdwIssuerStatusFlags ) { CRYPT_HASH_BLOB HashBlob; PCCERT_CONTEXT pCertContext;
HashBlob.cbData = CHAINHASHLEN; HashBlob.pbData = rgbCertHash; pCertContext = CertFindCertificateInStore( hRoot, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_MD5_HASH, (LPVOID) &HashBlob, NULL );
if ( pCertContext ) { CertFreeCertificateContext( pCertContext );
if ( hRoot == hRealRoot ) { *pdwIssuerStatusFlags |= CERT_ISSUER_TRUSTED_ROOT_FLAG; return; }
pCertContext = CertFindCertificateInStore( hRealRoot, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_MD5_HASH, (LPVOID) &HashBlob, NULL );
if ( pCertContext ) { CertFreeCertificateContext( pCertContext ); *pdwIssuerStatusFlags |= CERT_ISSUER_TRUSTED_ROOT_FLAG; } } }
//+===========================================================================
// CCertObjectCache helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Function: ChainCreateCertificateObjectCache
//
// Synopsis: create certificate object cache object
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCreateCertificateObjectCache ( IN DWORD MaxIndexEntries, OUT PCCERTOBJECTCACHE* ppCertObjectCache ) { BOOL fResult = FALSE; PCCERTOBJECTCACHE pCertObjectCache = NULL;
pCertObjectCache = new CCertObjectCache( MaxIndexEntries, fResult ); if ( pCertObjectCache != NULL ) { if ( fResult == TRUE ) { *ppCertObjectCache = pCertObjectCache; } else { delete pCertObjectCache; } } else { SetLastError( (DWORD) E_OUTOFMEMORY ); }
return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeCertificateObjectCache
//
// Synopsis: free the certificate object cache object
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeCertificateObjectCache ( IN PCCERTOBJECTCACHE pCertObjectCache ) { delete pCertObjectCache; }
//+---------------------------------------------------------------------------
//
// Function: CertObjectCacheOnRemovalFromPrimaryIndex
//
// Synopsis: removes the cert object from all other indexes and also
// removes the reference on the cert object.
//
//----------------------------------------------------------------------------
VOID WINAPI CertObjectCacheOnRemovalFromPrimaryIndex ( IN LPVOID pv, IN OPTIONAL LPVOID pvRemovalContext ) { PCCERTOBJECT pCertObject = (PCCERTOBJECT) pv;
I_CryptRemoveLruEntry( pCertObject->IdentifierIndexEntry(), LRU_SUPPRESS_REMOVAL_NOTIFICATION, NULL );
I_CryptRemoveLruEntry( pCertObject->SubjectNameIndexEntry(), LRU_SUPPRESS_REMOVAL_NOTIFICATION, NULL );
I_CryptRemoveLruEntry( pCertObject->KeyIdIndexEntry(), LRU_SUPPRESS_REMOVAL_NOTIFICATION, NULL );
I_CryptRemoveLruEntry( pCertObject->PublicKeyHashIndexEntry(), LRU_SUPPRESS_REMOVAL_NOTIFICATION, NULL );
pCertObject->Release();
CertPerfDecrementChainCertCacheCount(); }
//+---------------------------------------------------------------------------
//
// Function: CertObjectCacheOnRemovalFromEndHashIndex
//
// Synopsis: removes the reference on the end cert object.
//
//----------------------------------------------------------------------------
VOID WINAPI CertObjectCacheOnRemovalFromEndHashIndex ( IN LPVOID pv, IN LPVOID pvRemovalContext ) { PCCERTOBJECT pCertObject = (PCCERTOBJECT) pv;
pCertObject->Release();
CertPerfDecrementChainCertCacheCount(); }
//+---------------------------------------------------------------------------
//
// Function: CertObjectCacheHashMd5Identifier
//
// Synopsis: DWORD hash an MD5 identifier. This is done by taking the
// first four bytes of the MD5 hash since there is enough
// randomness already
//
//----------------------------------------------------------------------------
DWORD WINAPI CertObjectCacheHashMd5Identifier ( IN PCRYPT_DATA_BLOB pIdentifier ) { if ( sizeof(DWORD) > pIdentifier->cbData ) { return 0; } else { return( *( (DWORD UNALIGNED *)pIdentifier->pbData ) ); } }
//+---------------------------------------------------------------------------
//
// Function: CertObjectCacheHashNameIdentifier
//
// Synopsis: DWORD hash a subject or issuer name.
//
//----------------------------------------------------------------------------
DWORD WINAPI CertObjectCacheHashNameIdentifier ( IN PCRYPT_DATA_BLOB pIdentifier ) { DWORD dwHash = 0; DWORD cb = pIdentifier->cbData; LPBYTE pb = pIdentifier->pbData;
while ( cb-- ) { if ( dwHash & 0x80000000 ) { dwHash = ( dwHash << 1 ) | 1; } else { dwHash = dwHash << 1; }
dwHash += *pb++; }
return( dwHash ); }
//+---------------------------------------------------------------------------
//
// Function: ChainCreateCertificateObjectIdentifier
//
// Synopsis: create an object identifier given the issuer name and serial
// number. This is done using an MD5 hash over the content
//
//----------------------------------------------------------------------------
VOID WINAPI ChainCreateCertificateObjectIdentifier ( IN PCERT_NAME_BLOB pIssuer, IN PCRYPT_INTEGER_BLOB pSerialNumber, OUT CERT_OBJECT_IDENTIFIER ObjectIdentifier ) { MD5_CTX md5ctx;
MD5Init( &md5ctx );
MD5Update( &md5ctx, pIssuer->pbData, pIssuer->cbData ); MD5Update( &md5ctx, pSerialNumber->pbData, pSerialNumber->cbData );
MD5Final( &md5ctx );
assert(CHAINHASHLEN == MD5DIGESTLEN);
memcpy( ObjectIdentifier, md5ctx.digest, CHAINHASHLEN ); }
//+===========================================================================
// CChainPathObject helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Function: ChainCreatePathObject
//
// Synopsis: create a path object, note since it is a ref-counted
// object, freeing occurs by doing a pCertObject->Release
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCreatePathObject ( IN PCCHAINCALLCONTEXT pCallContext, IN PCCERTOBJECT pCertObject, IN OPTIONAL HCERTSTORE hAdditionalStore, OUT PCCHAINPATHOBJECT *ppPathObject ) { BOOL fResult = TRUE; BOOL fAddedToCreationCache = TRUE; PCCHAINPATHOBJECT pPathObject = NULL;
pPathObject = pCallContext->FindPathObjectInCreationCache( pCertObject->CertHash() ); if ( pPathObject != NULL ) { *ppPathObject = pPathObject; return( TRUE ); }
pPathObject = new CChainPathObject( pCallContext, FALSE, // fCyclic
(LPVOID) pCertObject, hAdditionalStore, fResult, fAddedToCreationCache );
if ( pPathObject != NULL ) { if (!fResult) { if (!fAddedToCreationCache) { delete pPathObject; } pPathObject = NULL; } } else { fResult = FALSE; }
*ppPathObject = pPathObject; return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: ChainCreateCyclicPathObject
//
// Synopsis: create a path object, note since it is a ref-counted
// object, freeing occurs by doing a pCertObject->Release
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCreateCyclicPathObject ( IN PCCHAINCALLCONTEXT pCallContext, IN PCCHAINPATHOBJECT pPathObject, OUT PCCHAINPATHOBJECT *ppCyclicPathObject ) { BOOL fResult = TRUE; BOOL fAddedToCreationCache = TRUE; PCCHAINPATHOBJECT pCyclicPathObject = NULL;
pCyclicPathObject = new CChainPathObject( pCallContext, TRUE, // fCyclic
(LPVOID) pPathObject, NULL, // hAdditionalStore
fResult, fAddedToCreationCache );
if ( pCyclicPathObject != NULL ) { if (!fResult) { if (!fAddedToCreationCache) { delete pCyclicPathObject; } pCyclicPathObject = NULL; } } else { fResult = FALSE; }
*ppCyclicPathObject = pCyclicPathObject; return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: ChainAllocAndCopyOID
//
// Synopsis: allocate and copy OID
//
//----------------------------------------------------------------------------
LPSTR WINAPI ChainAllocAndCopyOID ( IN LPSTR pszSrcOID ) { DWORD cchOID; LPSTR pszDstOID;
cchOID = strlen(pszSrcOID) + 1; pszDstOID = (LPSTR) PkiNonzeroAlloc(cchOID); if (NULL == pszDstOID) return NULL;
memcpy(pszDstOID, pszSrcOID, cchOID); return pszDstOID; }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeOID
//
// Synopsis: free allocated OID
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeOID ( IN OUT LPSTR pszOID ) { PkiFree(pszOID); }
//+---------------------------------------------------------------------------
//
// Function: ChainAllocAndCopyUsage
//
// Synopsis: allocates and copies usage OIDs.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainAllocAndCopyUsage ( IN PCERT_ENHKEY_USAGE pSrcUsage, OUT PCERT_ENHKEY_USAGE *ppDstUsage ) { BOOL fResult; PCERT_ENHKEY_USAGE pDstUsage = NULL; DWORD cOID; LPSTR *ppszDstOID; DWORD i;
if (NULL == pSrcUsage) goto SuccessReturn;
cOID = pSrcUsage->cUsageIdentifier;
pDstUsage = (PCERT_ENHKEY_USAGE) PkiZeroAlloc( sizeof(CERT_ENHKEY_USAGE) + sizeof(LPSTR) * cOID); if (NULL == pDstUsage) goto OutOfMemory;
ppszDstOID = (LPSTR *) &pDstUsage[1];
pDstUsage->cUsageIdentifier = cOID; pDstUsage->rgpszUsageIdentifier = ppszDstOID;
for (i = 0; i < cOID; i++) { ppszDstOID[i] = ChainAllocAndCopyOID(pSrcUsage->rgpszUsageIdentifier[i]); if (NULL == ppszDstOID[i]) goto OutOfMemory; }
SuccessReturn: fResult = TRUE; CommonReturn: *ppDstUsage = pDstUsage; return fResult;
ErrorReturn: if (pDstUsage) { ChainFreeUsage(pDstUsage); pDstUsage = NULL; } fResult = FALSE; goto CommonReturn;
SET_ERROR(OutOfMemory, E_OUTOFMEMORY) }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeUsage
//
// Synopsis: frees usage OIDs allocated by ChainAllocAndCopyUsage
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeUsage ( IN OUT PCERT_ENHKEY_USAGE pUsage ) { if (pUsage) { DWORD cOID = pUsage->cUsageIdentifier; LPSTR *ppszOID = pUsage->rgpszUsageIdentifier; DWORD i;
for (i = 0; i < cOID; i++) ChainFreeOID(ppszOID[i]);
PkiFree(pUsage); } }
//+---------------------------------------------------------------------------
//
// Function: ChainIsOIDInUsage
//
// Synopsis: returns TRUE if the OID is in the usage
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainIsOIDInUsage ( IN LPSTR pszOID, IN PCERT_ENHKEY_USAGE pUsage ) { DWORD cOID; DWORD i;
assert(pUsage);
cOID = pUsage->cUsageIdentifier; for (i = 0; i < cOID; i++){ if (0 == strcmp(pszOID, pUsage->rgpszUsageIdentifier[i])) return TRUE; }
return FALSE; }
//+---------------------------------------------------------------------------
//
// Function: ChainIntersectUsages
//
// Synopsis: returns the intersection of the two usages
//
//----------------------------------------------------------------------------
VOID WINAPI ChainIntersectUsages ( IN PCERT_ENHKEY_USAGE pCertUsage, IN OUT PCERT_ENHKEY_USAGE pRestrictedUsage ) { LPSTR *ppszOID; DWORD cOID; DWORD i; cOID = pRestrictedUsage->cUsageIdentifier; ppszOID = pRestrictedUsage->rgpszUsageIdentifier; i = 0; while (i < cOID) { if (ChainIsOIDInUsage(ppszOID[i], pCertUsage)) i++; else { // Remove the OID string and move the remaining
// strings up one.
DWORD j;
ChainFreeOID(ppszOID[i]);
for (j = i; j + 1 < cOID; j++) ppszOID[j] = ppszOID[j + 1];
cOID--; pRestrictedUsage->cUsageIdentifier = cOID; } } }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeAndClearRestrictedUsageInfo
//
// Synopsis: frees allocated restricted usage info
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeAndClearRestrictedUsageInfo( IN OUT PCHAIN_RESTRICTED_USAGE_INFO pInfo ) { ChainFreeUsage(pInfo->pIssuanceRestrictedUsage); ChainFreeUsage(pInfo->pIssuanceMappedUsage); PkiFree(pInfo->rgdwIssuanceMappedIndex); // fRequireIssuancePolicy
ChainFreeUsage(pInfo->pApplicationRestrictedUsage); ChainFreeUsage(pInfo->pApplicationMappedUsage); PkiFree(pInfo->rgdwApplicationMappedIndex);
ChainFreeUsage(pInfo->pPropertyRestrictedUsage);
memset(pInfo, 0, sizeof(*pInfo)); } //+---------------------------------------------------------------------------
//
// Function: ChainCalculateRestrictedUsage
//
// Synopsis: update the restricted and mapped usage using the cert's
// usage and optional policy mappings
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCalculateRestrictedUsage ( IN PCERT_ENHKEY_USAGE pCertUsage, IN OPTIONAL PCERT_POLICY_MAPPINGS_INFO pMappings, IN OUT PCERT_ENHKEY_USAGE *ppRestrictedUsage, IN OUT PCERT_ENHKEY_USAGE *ppMappedUsage, IN OUT LPDWORD *ppdwMappedIndex ) { BOOL fResult; PCERT_ENHKEY_USAGE pNewMappedUsage = NULL; LPDWORD pdwNewMappedIndex = NULL;
if (pCertUsage) { if (NULL == *ppRestrictedUsage) { // Top most, first certificate with a usage restriction
assert(NULL == *ppMappedUsage); assert(NULL == *ppdwMappedIndex);
if (!ChainAllocAndCopyUsage(pCertUsage, ppRestrictedUsage)) goto AllocAndCopyUsageError; } else { PCERT_ENHKEY_USAGE pRestrictedUsage = *ppRestrictedUsage; PCERT_ENHKEY_USAGE pMappedUsage = *ppMappedUsage; if (NULL == pMappedUsage) { // Take the intersection of the restricted and cert's
// usage
ChainIntersectUsages(pCertUsage, pRestrictedUsage);
} else { // Take the intersection of the mapped and cert's
// usage. If removed from the mapped usage,
// we might also need to remove from the restricted usage.
LPDWORD pdwMappedIndex = *ppdwMappedIndex; LPSTR *ppszOID; DWORD cOID; DWORD i;
assert(pdwMappedIndex);
cOID = pMappedUsage->cUsageIdentifier; ppszOID = pMappedUsage->rgpszUsageIdentifier; i = 0; while (i < cOID) { if (ChainIsOIDInUsage(ppszOID[i], pCertUsage)) i++; else { // If no other mappings to the restricted OID, then,
// remove the restricted OID.
DWORD j; BOOL fRemoveRestricted;
if ((0 == i || pdwMappedIndex[i - 1] != pdwMappedIndex[i]) && (i + 1 == cOID || pdwMappedIndex[i] != pdwMappedIndex[i + 1])) { // Remove the restricted OID we are mapped to.
LPSTR *ppszRestrictedOID = pRestrictedUsage->rgpszUsageIdentifier; DWORD cRestrictedOID = pRestrictedUsage->cUsageIdentifier;
fRemoveRestricted = TRUE;
j = pdwMappedIndex[i]; assert(j < cRestrictedOID);
if (j < cRestrictedOID) ChainFreeOID(ppszRestrictedOID[j]);
for ( ; j + 1 < cRestrictedOID; j++) ppszRestrictedOID[j] = ppszRestrictedOID[j + 1];
cRestrictedOID--; pRestrictedUsage->cUsageIdentifier = cRestrictedOID; } else fRemoveRestricted = FALSE;
// Remove the OID string and mapped index. Move the
// remaining strings and indices up one.
ChainFreeOID(ppszOID[i]);
for (j = i; j + 1 < cOID; j++) { ppszOID[j] = ppszOID[j + 1]; pdwMappedIndex[j] = pdwMappedIndex[j + 1]; if (fRemoveRestricted) { assert(0 < pdwMappedIndex[j]); pdwMappedIndex[j] -= 1; } }
cOID--; pMappedUsage->cUsageIdentifier = cOID; } } } } } // else
// No restrictions added by certificate
if (pMappings) { PCERT_ENHKEY_USAGE pRestrictedUsage = *ppRestrictedUsage; PCERT_ENHKEY_USAGE pMappedUsage = *ppMappedUsage;
if (NULL == pRestrictedUsage || 0 == pRestrictedUsage->cUsageIdentifier) { // Nothing to be mapped.
assert(NULL == pMappedUsage || 0 == pMappedUsage->cUsageIdentifier); } else { LPDWORD pdwMappedIndex; PCERT_ENHKEY_USAGE pSrcUsage; LPSTR *ppszSrcOID; DWORD cSrcOID; DWORD iSrc;
DWORD cMap; PCERT_POLICY_MAPPING pMap;
DWORD cNewOID; LPSTR *ppszNewOID;
if (pMappedUsage) { // Subsequent mapping
assert(0 < pMappedUsage->cUsageIdentifier); pSrcUsage = pMappedUsage; pdwMappedIndex = *ppdwMappedIndex; assert(pdwMappedIndex); } else { // First mapping
pSrcUsage = pRestrictedUsage; pdwMappedIndex = NULL; }
cSrcOID = pSrcUsage->cUsageIdentifier; ppszSrcOID = pSrcUsage->rgpszUsageIdentifier;
cMap = pMappings->cPolicyMapping; pMap = pMappings->rgPolicyMapping;
// Note, all duplicates have been remove from usage and
// mappings
cNewOID = cSrcOID + cMap;
pNewMappedUsage = (PCERT_ENHKEY_USAGE) PkiZeroAlloc( sizeof(CERT_ENHKEY_USAGE) + sizeof(LPSTR) * cNewOID); if (NULL == pNewMappedUsage) goto OutOfMemory;
ppszNewOID = (LPSTR *) &pNewMappedUsage[1]; pNewMappedUsage->cUsageIdentifier = cNewOID; pNewMappedUsage->rgpszUsageIdentifier = ppszNewOID;
pdwNewMappedIndex = (LPDWORD) PkiZeroAlloc( sizeof(DWORD) * cNewOID); if (NULL == pdwNewMappedIndex) goto OutOfMemory;
cNewOID = 0; for (iSrc = 0; iSrc < cSrcOID; iSrc++) { DWORD iMap; BOOL fMapped = FALSE;
for (iMap = 0; iMap < cMap; iMap++) { if (0 == strcmp(ppszSrcOID[iSrc], pMap[iMap].pszIssuerDomainPolicy)) { assert(cNewOID < pNewMappedUsage->cUsageIdentifier);
ppszNewOID[cNewOID] = ChainAllocAndCopyOID( pMap[iMap].pszSubjectDomainPolicy); if (NULL == ppszNewOID[cNewOID]) goto OutOfMemory;
if (pdwMappedIndex) pdwNewMappedIndex[cNewOID] = pdwMappedIndex[iSrc]; else pdwNewMappedIndex[cNewOID] = iSrc; cNewOID++; fMapped = TRUE; } }
if (!fMapped) { assert(cNewOID < pNewMappedUsage->cUsageIdentifier);
ppszNewOID[cNewOID] = ChainAllocAndCopyOID(ppszSrcOID[iSrc]); if (NULL == ppszNewOID[cNewOID]) goto OutOfMemory; if (pdwMappedIndex) pdwNewMappedIndex[cNewOID] = pdwMappedIndex[iSrc]; else pdwNewMappedIndex[cNewOID] = iSrc;
cNewOID++;
} }
assert(cNewOID >= cSrcOID); pNewMappedUsage->cUsageIdentifier = cNewOID;
if (pMappedUsage) { ChainFreeUsage(pMappedUsage); PkiFree(pdwMappedIndex); }
*ppMappedUsage = pNewMappedUsage; *ppdwMappedIndex = pdwNewMappedIndex;
} }
fResult = TRUE;
CommonReturn: return fResult; ErrorReturn: ChainFreeUsage(pNewMappedUsage); PkiFree(pdwNewMappedIndex); fResult = FALSE; goto CommonReturn;
TRACE_ERROR(AllocAndCopyUsageError) TRACE_ERROR(OutOfMemory) }
//+---------------------------------------------------------------------------
//
// Function: ChainGetUsageStatus
//
// Synopsis: get the usage status
//
//----------------------------------------------------------------------------
VOID WINAPI ChainGetUsageStatus ( IN PCERT_ENHKEY_USAGE pRequestedUsage, IN PCERT_ENHKEY_USAGE pAvailableUsage, IN DWORD dwMatchType, IN OUT PCERT_TRUST_STATUS pStatus ) { DWORD cRequested; DWORD cAvailable; DWORD cFound; BOOL fFound;
if ( pAvailableUsage == NULL ) { return; }
if ( ( pRequestedUsage->cUsageIdentifier > pAvailableUsage->cUsageIdentifier ) && ( dwMatchType == USAGE_MATCH_TYPE_AND ) ) { pStatus->dwErrorStatus |= CERT_TRUST_IS_NOT_VALID_FOR_USAGE; return; }
for ( cRequested = 0, cFound = 0; cRequested < pRequestedUsage->cUsageIdentifier; cRequested++ ) { for ( cAvailable = 0, fFound = FALSE; ( cAvailable < pAvailableUsage->cUsageIdentifier ) && ( fFound == FALSE ); cAvailable++ ) { // NOTE: Optimize compares of OIDs. Perhaps with a different
// encoding
if ( strcmp( pRequestedUsage->rgpszUsageIdentifier[ cRequested ], pAvailableUsage->rgpszUsageIdentifier[ cAvailable ] ) == 0 ) { fFound = TRUE; } }
if ( fFound == TRUE ) { cFound += 1; } }
if ( ( dwMatchType == USAGE_MATCH_TYPE_AND ) && ( cFound != pRequestedUsage->cUsageIdentifier ) ) { pStatus->dwErrorStatus |= CERT_TRUST_IS_NOT_VALID_FOR_USAGE; } else if ( ( dwMatchType == USAGE_MATCH_TYPE_OR ) && ( cFound == 0 ) ) { pStatus->dwErrorStatus |= CERT_TRUST_IS_NOT_VALID_FOR_USAGE; } }
//+---------------------------------------------------------------------------
//
// Function: ChainOrInStatusBits
//
// Synopsis: bit or in the status bits from the source into the destination
//
//----------------------------------------------------------------------------
VOID WINAPI ChainOrInStatusBits ( IN PCERT_TRUST_STATUS pDestStatus, IN PCERT_TRUST_STATUS pSourceStatus ) { pDestStatus->dwErrorStatus |= pSourceStatus->dwErrorStatus; pDestStatus->dwInfoStatus |= pSourceStatus->dwInfoStatus; }
//+---------------------------------------------------------------------------
//
// Function: ChainGetMatchInfoStatus
//
// Synopsis: return the info status used to match the issuer
//
// For a match returns TRUE, where dwInfoStatus can be
// one of the following:
// - CERT_TRUST_HAS_EXACT_MATCH_ISSUER |
// CERT_TRUST_HAS_PREFERRED_ISSUER
// - CERT_TRUST_HAS_KEY_MATCH_ISSUER |
// CERT_TRUST_HAS_PREFERRED_ISSUER
// - CERT_TRUST_HAS_KEY_MATCH_ISSUER (nonmatching AKI exact match)
// - CERT_TRUST_HAS_NAME_MATCH_ISSUER |
// CERT_TRUST_HAS_PREFERRED_ISSUER
// - CERT_TRUST_HAS_NAME_MATCH_ISSUER (nonmatching AKI)
//
// For no match returns FALSE with dwInfoStatus set to the
// following:
// - CERT_TRUST_HAS_KEY_MATCH_ISSUER
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainGetMatchInfoStatus ( IN PCCERTOBJECT pIssuerObject, IN PCCERTOBJECT pSubjectObject, IN OUT DWORD *pdwInfoStatus ) { BOOL fResult = FALSE; DWORD dwInfoStatus = 0; DWORD dwPreferredStatus = CERT_TRUST_HAS_PREFERRED_ISSUER;
PCERT_INFO pSubjectInfo = pSubjectObject->CertContext()->pCertInfo; PCERT_AUTHORITY_KEY_ID_INFO pAKI = pSubjectObject->AuthorityKeyIdentifier(); PCERT_INFO pIssuerInfo = pIssuerObject->CertContext()->pCertInfo;
if (pAKI) { if ( ( pAKI->CertIssuer.cbData != 0 ) && ( pAKI->CertSerialNumber.cbData != 0 ) ) { DWORD cbAuthIssuerName; LPBYTE pbAuthIssuerName; DWORD cbAuthSerialNumber; LPBYTE pbAuthSerialNumber;
cbAuthIssuerName = pAKI->CertIssuer.cbData; pbAuthIssuerName = pAKI->CertIssuer.pbData; cbAuthSerialNumber = pAKI->CertSerialNumber.cbData; pbAuthSerialNumber = pAKI->CertSerialNumber.pbData;
if ( ( cbAuthIssuerName == pIssuerInfo->Issuer.cbData ) && ( memcmp( pbAuthIssuerName, pIssuerInfo->Issuer.pbData, cbAuthIssuerName ) == 0 ) && ( cbAuthSerialNumber == pIssuerInfo->SerialNumber.cbData ) && ( memcmp( pbAuthSerialNumber, pIssuerInfo->SerialNumber.pbData, cbAuthSerialNumber ) == 0 ) ) { dwInfoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER | CERT_TRUST_HAS_PREFERRED_ISSUER; goto SuccessReturn; } else { // Doesn't have preferred match
dwPreferredStatus = 0; } }
if ( pAKI->KeyId.cbData != 0 ) { DWORD cbAuthKeyIdentifier; LPBYTE pbAuthKeyIdentifier; DWORD cbIssuerKeyIdentifier; LPBYTE pbIssuerKeyIdentifier;
cbAuthKeyIdentifier = pAKI->KeyId.cbData; pbAuthKeyIdentifier = pAKI->KeyId.pbData; cbIssuerKeyIdentifier = pIssuerObject->KeyIdentifierSize(); pbIssuerKeyIdentifier = pIssuerObject->KeyIdentifier();
if ( ( cbAuthKeyIdentifier == cbIssuerKeyIdentifier ) && ( memcmp( pbAuthKeyIdentifier, pbIssuerKeyIdentifier, cbAuthKeyIdentifier ) == 0 ) ) { dwInfoStatus = dwPreferredStatus | CERT_TRUST_HAS_KEY_MATCH_ISSUER; goto SuccessReturn; } else { // Doesn't have preferred match
dwPreferredStatus = 0; } } }
if ( ( pSubjectInfo->Issuer.cbData == pIssuerInfo->Subject.cbData ) && ( pSubjectInfo->Issuer.cbData != 0) && ( memcmp( pSubjectInfo->Issuer.pbData, pIssuerInfo->Subject.pbData, pIssuerInfo->Subject.cbData ) == 0 ) ) { dwInfoStatus = dwPreferredStatus | CERT_TRUST_HAS_NAME_MATCH_ISSUER; goto SuccessReturn; }
// Default to nonPreferred public key match
dwInfoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER; goto ErrorReturn;
SuccessReturn: fResult = TRUE; CommonReturn: *pdwInfoStatus |= dwInfoStatus; return fResult;
ErrorReturn: fResult = FALSE; goto CommonReturn; }
//+---------------------------------------------------------------------------
//
// Function: ChainGetMatchInfoStatusForNoIssuer
//
// Synopsis: return the info status when unable to find our issuer
//
//----------------------------------------------------------------------------
DWORD WINAPI ChainGetMatchInfoStatusForNoIssuer ( IN DWORD dwIssuerMatchFlags ) { if (dwIssuerMatchFlags & CERT_EXACT_ISSUER_MATCH_FLAG) return CERT_TRUST_HAS_EXACT_MATCH_ISSUER; else if (dwIssuerMatchFlags & CERT_KEYID_ISSUER_MATCH_TYPE) return CERT_TRUST_HAS_KEY_MATCH_ISSUER; else return CERT_TRUST_HAS_NAME_MATCH_ISSUER; }
//+---------------------------------------------------------------------------
//
// Function: ChainIsValidPubKeyMatchForIssuer
//
// Synopsis: returns TRUE if the issuer matches more than just the
// public key match criteria
//
// This logic is mainly here to handle tstore2.exe and regress.bat
// which has end, CA and root certificates using the same
// public key.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainIsValidPubKeyMatchForIssuer ( IN PCCERTOBJECT pIssuer, IN PCCERTOBJECT pSubject ) { BOOL fResult = TRUE; BOOL fCheckMatchInfo; PCERT_BASIC_CONSTRAINTS2_INFO pIssuerBasicConstraints;
fCheckMatchInfo = FALSE;
// Check if the issuer has a basic constraints extension. If it does
// and it isn't a CA, then, we will need to do an additional issuer match.
pIssuerBasicConstraints = pIssuer->BasicConstraintsInfo(); if (pIssuerBasicConstraints && !pIssuerBasicConstraints->fCA) fCheckMatchInfo = TRUE; else { // Check if the issuer has the same public key as the subject. If it
// does, then, will need to do an additional issuer match.
BYTE *pbIssuerPublicKeyHash; BYTE *pbSubjectPublicKeyHash;
pbIssuerPublicKeyHash = pIssuer->PublicKeyHash(); pbSubjectPublicKeyHash = pSubject->PublicKeyHash(); if (0 == memcmp(pbIssuerPublicKeyHash, pbSubjectPublicKeyHash, CHAINHASHLEN)) fCheckMatchInfo = TRUE; }
if (fCheckMatchInfo) { // Check that the issuer matches the subject's AKI or subject's
// issuer name.
DWORD dwInfoStatus = 0;
// Following returns FALSE if only has the public key match
fResult = ChainGetMatchInfoStatus(pIssuer, pSubject, &dwInfoStatus); }
return fResult; }
//+---------------------------------------------------------------------------
//
// Function: ChainGetSubjectStatus
//
// Synopsis: get the subject status bits by checking the time nesting and
// signature validity
//
// For CERT_END_OBJECT_TYPE or CERT_EXTERNAL_ISSUER_OBJECT_TYPE
// CCertObject types, leaves the engine's critical section to
// verify the signature. 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 ChainGetSubjectStatus ( IN PCCHAINCALLCONTEXT pCallContext, IN PCCHAINPATHOBJECT pIssuerPathObject, IN PCCHAINPATHOBJECT pSubjectPathObject, IN OUT PCERT_TRUST_STATUS pStatus ) { BOOL fResult;
PCCERTOBJECT pIssuerObject = pIssuerPathObject->CertObject(); PCCERTOBJECT pSubjectObject = pSubjectPathObject->CertObject(); PCCERT_CONTEXT pIssuerContext = pIssuerObject->CertContext(); PCCERT_CONTEXT pSubjectContext = pSubjectObject->CertContext();
DWORD dwIssuerStatusFlags;
ChainGetMatchInfoStatus( pIssuerObject, pSubjectObject, &pStatus->dwInfoStatus );
dwIssuerStatusFlags = pSubjectObject->IssuerStatusFlags(); if (!(dwIssuerStatusFlags & CERT_ISSUER_VALID_SIGNATURE_FLAG)) { DWORD dwObjectType;
dwObjectType = pSubjectObject->ObjectType(); if (CERT_END_OBJECT_TYPE == dwObjectType || CERT_EXTERNAL_ISSUER_OBJECT_TYPE == dwObjectType) pCallContext->ChainEngine()->UnlockEngine();
fResult = CryptVerifyCertificateSignatureEx( NULL, // hCryptProv
X509_ASN_ENCODING, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *) pSubjectContext, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *) pIssuerContext, 0, // dwFlags
NULL // pvReserved
);
if (CERT_END_OBJECT_TYPE == dwObjectType || CERT_EXTERNAL_ISSUER_OBJECT_TYPE == dwObjectType) { pCallContext->ChainEngine()->LockEngine(); if (pCallContext->IsTouchedEngine()) goto TouchedDuringSignatureVerification; }
if (!fResult) { pStatus->dwErrorStatus |= CERT_TRUST_IS_NOT_SIGNATURE_VALID; pStatus->dwInfoStatus &= ~CERT_TRUST_HAS_PREFERRED_ISSUER; } else { if (dwIssuerStatusFlags & CERT_ISSUER_PUBKEY_FLAG) { // Verify the issuer's public key hash
if (0 != memcmp(pSubjectObject->IssuerPublicKeyHash(), pIssuerObject->PublicKeyHash(), CHAINHASHLEN)) dwIssuerStatusFlags &= ~CERT_ISSUER_PUBKEY_FLAG; }
if (!(dwIssuerStatusFlags & CERT_ISSUER_PUBKEY_FLAG)) { CRYPT_DATA_BLOB DataBlob;
memcpy(pSubjectObject->IssuerPublicKeyHash(), pIssuerObject->PublicKeyHash(), CHAINHASHLEN); DataBlob.pbData = pSubjectObject->IssuerPublicKeyHash(), DataBlob.cbData = CHAINHASHLEN; CertSetCertificateContextProperty( pSubjectContext, CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID, CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, &DataBlob ); }
pSubjectObject->OrIssuerStatusFlags( CERT_ISSUER_PUBKEY_FLAG | CERT_ISSUER_VALID_SIGNATURE_FLAG ); }
CertPerfIncrementChainVerifyCertSignatureCount(); } else {
// also need to check public key parameters
assert(dwIssuerStatusFlags & CERT_ISSUER_PUBKEY_FLAG); if (0 != memcmp(pSubjectObject->IssuerPublicKeyHash(), pIssuerObject->PublicKeyHash(), CHAINHASHLEN)) { pStatus->dwErrorStatus |= CERT_TRUST_IS_NOT_SIGNATURE_VALID; pStatus->dwInfoStatus &= ~CERT_TRUST_HAS_PREFERRED_ISSUER; }
CertPerfIncrementChainCompareIssuerPublicKeyCount(); }
fResult = TRUE; CommonReturn: return fResult;
ErrorReturn: fResult = FALSE; goto CommonReturn;
SET_ERROR(TouchedDuringSignatureVerification, ERROR_CAN_NOT_COMPLETE) }
//+---------------------------------------------------------------------------
//
// Function: ChainUpdateSummaryStatusByTrustStatus
//
// Synopsis: update the summary status bits given new trust status bits
//
//----------------------------------------------------------------------------
VOID WINAPI ChainUpdateSummaryStatusByTrustStatus( IN OUT PCERT_TRUST_STATUS pSummaryStatus, IN PCERT_TRUST_STATUS pTrustStatus ) { pSummaryStatus->dwErrorStatus |= pTrustStatus->dwErrorStatus; pSummaryStatus->dwInfoStatus |= pTrustStatus->dwInfoStatus & ~(CERT_TRUST_CERTIFICATE_ONLY_INFO_STATUS | CERT_TRUST_HAS_PREFERRED_ISSUER); if (!(pTrustStatus->dwInfoStatus & CERT_TRUST_HAS_PREFERRED_ISSUER)) pSummaryStatus->dwInfoStatus &= ~CERT_TRUST_HAS_PREFERRED_ISSUER;
if (pSummaryStatus->dwErrorStatus & CERT_TRUST_ANY_NAME_CONSTRAINT_ERROR_STATUS) pSummaryStatus->dwInfoStatus &= ~CERT_TRUST_HAS_VALID_NAME_CONSTRAINTS; }
//+===========================================================================
// Format and append extended error information helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Function: ChainAllocAndEncodeObject
//
// Synopsis: allocate and ASN.1 encodes the data structure.
//
// PkiFree must be called to free the encoded bytes
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainAllocAndEncodeObject( IN LPCSTR lpszStructType, IN const void *pvStructInfo, OUT BYTE **ppbEncoded, OUT DWORD *pcbEncoded ) { return CryptEncodeObjectEx( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, lpszStructType, pvStructInfo, CRYPT_ENCODE_ALLOC_FLAG, &PkiEncodePara, (void *) ppbEncoded, pcbEncoded ); }
//+---------------------------------------------------------------------------
//
// Function: ChainAppendExtendedErrorInfo
//
// Synopsis: PkiReallocate and append an already localization formatted
// line of extended error information
//
//----------------------------------------------------------------------------
VOID WINAPI ChainAppendExtendedErrorInfo( IN OUT LPWSTR *ppwszExtErrorInfo, IN LPWSTR pwszAppend, IN DWORD cchAppend // Includes NULL terminator
) { LPWSTR pwszExtErrorInfo = *ppwszExtErrorInfo; DWORD cchExtErrorInfo;
if (pwszExtErrorInfo) cchExtErrorInfo = wcslen(pwszExtErrorInfo); else cchExtErrorInfo = 0;
assert(0 < cchAppend);
if (pwszExtErrorInfo = (LPWSTR) PkiRealloc(pwszExtErrorInfo, (cchExtErrorInfo + cchAppend) * sizeof(WCHAR))) { memcpy(&pwszExtErrorInfo[cchExtErrorInfo], pwszAppend, cchAppend * sizeof(WCHAR)); *ppwszExtErrorInfo = pwszExtErrorInfo; } }
//+---------------------------------------------------------------------------
//
// Function: ChainFormatAndAppendExtendedErrorInfo
//
// Synopsis: localization format a line of extended error information
// and append via the above ChainAppendExtendedErrorInfo
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFormatAndAppendExtendedErrorInfo( IN OUT LPWSTR *ppwszExtErrorInfo, IN UINT nFormatID, ... ) { DWORD cchMsg = 0; LPWSTR pwszMsg = NULL; WCHAR wszFormat[256]; wszFormat[0] = '\0'; va_list argList;
// get format string from resources
if(0 == LoadStringU(g_hChainInst, nFormatID, wszFormat, sizeof(wszFormat)/sizeof(wszFormat[0]))) return;
__try {
// format message into requested buffer
va_start(argList, nFormatID); cchMsg = FormatMessageU( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, wszFormat, 0, // dwMessageId
0, // dwLanguageId
(LPWSTR) &pwszMsg, 0, // minimum size to allocate
&argList);
va_end(argList);
// Must at least have the L'\n' terminator
if (1 < cchMsg && pwszMsg) ChainAppendExtendedErrorInfo( ppwszExtErrorInfo, pwszMsg, cchMsg + 1 );
} __except(EXCEPTION_EXECUTE_HANDLER) { }
if (pwszMsg) LocalFree(pwszMsg); }
//+===========================================================================
// Name Constraint helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Function: ChainIsWhiteSpace
//
// Synopsis: returns TRUE for a white space character
//
//----------------------------------------------------------------------------
static inline BOOL ChainIsWhiteSpace(WCHAR wc) { return wc == L' ' || (wc >= 0x09 && wc <= 0x0d); }
//+---------------------------------------------------------------------------
//
// Function: ChainRemoveLeadingAndTrailingWhiteSpace
//
// Synopsis: advances the pointer past any leading white space. Removes
// any trailing white space by inserting the L'\0' and updating
// the character count.
//
//----------------------------------------------------------------------------
VOID WINAPI ChainRemoveLeadingAndTrailingWhiteSpace( IN LPWSTR pwszIn, OUT LPWSTR *ppwszOut, OUT DWORD *pcchOut ) { LPWSTR pwszOut; DWORD cchOut; WCHAR wc;
// Remove leading white space
for (pwszOut = pwszIn ; L'\0' != (wc = *pwszOut); pwszOut++) { if (!ChainIsWhiteSpace(wc)) break; }
for (cchOut = wcslen(pwszOut); 0 < cchOut; cchOut--) { if (!ChainIsWhiteSpace(pwszOut[cchOut - 1])) break; }
pwszOut[cchOut] = L'\0'; *ppwszOut = pwszOut; *pcchOut = cchOut; }
#define NO_LOCALE MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
//+---------------------------------------------------------------------------
//
// Function: ChainIsRightStringInString
//
// Synopsis: returns TRUE for a case insensitive match of the
// "Right" string with the right most characters of the
// string.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainIsRightStringInString( IN LPCWSTR pwszRight, IN DWORD cchRight, IN LPCWSTR pwszString, IN DWORD cchString ) { if (0 == cchRight) return TRUE; if (cchRight > cchString) return FALSE;
if (CSTR_EQUAL == CompareStringU( NO_LOCALE, NORM_IGNORECASE, pwszRight, cchRight, pwszString + (cchString - cchRight), cchRight )) return TRUE; else return FALSE; }
//+---------------------------------------------------------------------------
//
// Function: ChainFixupNameConstraintsUPN
//
// Synopsis: fixup the CERT_ALT_NAME_OTHER_NAME AltName entry choice
// for szOID_NT_PRINCIPAL_NAME by allocating and converting
// to a PCERT_NAME_VALUE containing the unicode string
// with leading and trailing white space removed.
//
// The pOtherName->Value.pbData is updated to point to the
// PCERT_NAME_VALUE instead of the original ASN.1 encoded
// bytes.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainFixupNameConstraintsUPN( IN OUT PCRYPT_OBJID_BLOB pUPN ) { BOOL fResult; PCERT_NAME_VALUE pNameValue; LPWSTR pwsz; DWORD cch;
pNameValue = (PCERT_NAME_VALUE) ChainAllocAndDecodeObject( X509_UNICODE_ANY_STRING, pUPN->pbData, pUPN->cbData ); if (NULL == pNameValue) goto DecodeError;
if (!IS_CERT_RDN_CHAR_STRING(pNameValue->dwValueType)) { PkiFree(pNameValue); goto InvalidUPNStringType; }
ChainRemoveLeadingAndTrailingWhiteSpace( (LPWSTR) pNameValue->Value.pbData, &pwsz, &cch );
pNameValue->Value.pbData = (BYTE *) pwsz; pNameValue->Value.cbData = cch * sizeof(WCHAR);
pUPN->pbData = (BYTE *) pNameValue;
fResult = TRUE; CommonReturn: return fResult;
ErrorReturn: pUPN->pbData = NULL; fResult = FALSE; goto CommonReturn;
TRACE_ERROR(DecodeError) SET_ERROR(InvalidUPNStringType, CRYPT_E_BAD_ENCODE) }
//+---------------------------------------------------------------------------
//
// Function: ChainAllocDecodeAndFixupNameConstraintsDirectoryName
//
// Synopsis: fixup the CERT_ALT_NAME_DIRECTORY_NAME AltName entry choice
// or the encoded certificate Subject name by allocating and
// converting to a unicode PCERT_NAME_INFO where
// leading and trailing white space has been removed from
// all the attributes.
//
// The DirectoryName.pbData is updated to point to the
// PCERT_NAME_INFO instead of the original ASN.1 encoded
// bytes.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainAllocDecodeAndFixupNameConstraintsDirectoryName( IN PCERT_NAME_BLOB pDirName, OUT PCERT_NAME_INFO *ppNameInfo ) { BOOL fResult; PCERT_NAME_INFO pNameInfo = NULL; DWORD cRDN; PCERT_RDN pRDN;
if (0 == pDirName->cbData) goto SuccessReturn;
pNameInfo = (PCERT_NAME_INFO) ChainAllocAndDecodeObject( X509_UNICODE_NAME, pDirName->pbData, pDirName->cbData ); if (NULL == pNameInfo) goto DecodeError;
if (0 == pNameInfo->cRDN) { PkiFree(pNameInfo); pNameInfo = NULL; goto SuccessReturn; }
// Iterate through all the attributes and remove leading and trailing
// white space.
cRDN = pNameInfo->cRDN; pRDN = pNameInfo->rgRDN; for ( ; cRDN > 0; cRDN--, pRDN++) { DWORD cAttr = pRDN->cRDNAttr; PCERT_RDN_ATTR pAttr = pRDN->rgRDNAttr; for ( ; cAttr > 0; cAttr--, pAttr++) { LPWSTR pwsz; DWORD cch;
if (!IS_CERT_RDN_CHAR_STRING(pAttr->dwValueType)) continue;
ChainRemoveLeadingAndTrailingWhiteSpace( (LPWSTR) pAttr->Value.pbData, &pwsz, &cch );
pAttr->Value.pbData = (BYTE *) pwsz; pAttr->Value.cbData = cch * sizeof(WCHAR); } }
SuccessReturn: fResult = TRUE;
CommonReturn: *ppNameInfo = pNameInfo; return fResult;
ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(DecodeError) }
//+---------------------------------------------------------------------------
//
// Function: ChainFixupNameConstraintsAltNameEntry
//
// Synopsis: fixup the AltName entry choices as follows:
// CERT_ALT_NAME_OTHER_NAME
// For szOID_NT_PRINCIPAL_NAME, pOtherName->Value.pbData
// is updated to point to the allocated
// PCERT_NAME_VALUE containing the decoded unicode string.
//
// CERT_ALT_NAME_RFC822_NAME
// CERT_ALT_NAME_DNS_NAME
// CERT_ALT_NAME_URL
// Uses DirectoryName.pbData and DirectoryName.cbData
// to contain the pointer to and length of the unicode
// string.
//
// For the subject URL, the DirectoryName.pbData's
// unicode string is the allocated host name.
//
// CERT_ALT_NAME_DIRECTORY_NAME:
// DirectoryName.pbData is updated to point to the
// allocated and decoded unicode PCERT_NAME_INFO.
//
// For the above choices, leading and trailing white space
// has been removed. cbData is number of bytes and not number
// of characters, ie, cbData = cch * sizeof(WCHAR)
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainFixupNameConstraintsAltNameEntry( IN BOOL fSubjectConstraint, IN OUT PCERT_ALT_NAME_ENTRY pEntry ) { BOOL fResult = TRUE;
LPWSTR pwsz = NULL; DWORD cch = 0;
switch (pEntry->dwAltNameChoice) { case CERT_ALT_NAME_OTHER_NAME: if (0 == strcmp(pEntry->pOtherName->pszObjId, szOID_NT_PRINCIPAL_NAME)) fResult = ChainFixupNameConstraintsUPN( &pEntry->pOtherName->Value); break; case CERT_ALT_NAME_RFC822_NAME: case CERT_ALT_NAME_DNS_NAME: ChainRemoveLeadingAndTrailingWhiteSpace( pEntry->pwszRfc822Name, &pwsz, &cch ); // Use the directory name's BLOB choice to contain both
// the pointer to and length of the string
pEntry->DirectoryName.pbData = (BYTE *) pwsz; pEntry->DirectoryName.cbData = cch * sizeof(WCHAR); break; case CERT_ALT_NAME_URL: if (fSubjectConstraint) { WCHAR rgwszHostName[MAX_PATH + 1]; LPWSTR pwszHostName;
rgwszHostName[0] = L'\0'; fResult = ChainGetHostNameFromUrl( pEntry->pwszURL, MAX_PATH, rgwszHostName); if (fResult) { ChainRemoveLeadingAndTrailingWhiteSpace( rgwszHostName, &pwszHostName, &cch ); pwsz = (LPWSTR) PkiNonzeroAlloc((cch + 1) * sizeof(WCHAR)); if (NULL == pwsz) fResult = FALSE; else memcpy(pwsz, pwszHostName, (cch + 1) * sizeof(WCHAR)); }
if (!fResult) { pwsz = NULL; cch = 0; } } else { ChainRemoveLeadingAndTrailingWhiteSpace( pEntry->pwszURL, &pwsz, &cch ); }
// Use the directory name's BLOB choice to contain both
// the pointer to and length of the string
pEntry->DirectoryName.pbData = (BYTE *) pwsz; pEntry->DirectoryName.cbData = cch * sizeof(WCHAR); break; case CERT_ALT_NAME_DIRECTORY_NAME: { PCERT_NAME_INFO pNameInfo = NULL; fResult = ChainAllocDecodeAndFixupNameConstraintsDirectoryName( &pEntry->DirectoryName, &pNameInfo);
// Update the directory name's BLOB to contain the pointer
// to the decoded name info
pEntry->DirectoryName.pbData = (BYTE *) pNameInfo; } break; case CERT_ALT_NAME_X400_ADDRESS: case CERT_ALT_NAME_EDI_PARTY_NAME: case CERT_ALT_NAME_IP_ADDRESS: case CERT_ALT_NAME_REGISTERED_ID: default: break; }
return fResult; }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeNameConstraintsAltNameEntryFixup
//
// Synopsis: free memory allocated by the above
// ChainFixupNameConstraintsAltNameEntry
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeNameConstraintsAltNameEntryFixup( IN BOOL fSubjectConstraint, IN OUT PCERT_ALT_NAME_ENTRY pEntry ) { switch (pEntry->dwAltNameChoice) { case CERT_ALT_NAME_OTHER_NAME: if (0 == strcmp(pEntry->pOtherName->pszObjId, szOID_NT_PRINCIPAL_NAME)) // pbData :: PCERT_NAME_VALUE
PkiFree(pEntry->pOtherName->Value.pbData); break; case CERT_ALT_NAME_RFC822_NAME: case CERT_ALT_NAME_DNS_NAME: break; case CERT_ALT_NAME_URL: if (fSubjectConstraint) // pbData :: LPWSTR
PkiFree(pEntry->DirectoryName.pbData); break; case CERT_ALT_NAME_DIRECTORY_NAME: // pbData :: PCERT_NAME_INFO
PkiFree(pEntry->DirectoryName.pbData); break; case CERT_ALT_NAME_X400_ADDRESS: case CERT_ALT_NAME_EDI_PARTY_NAME: case CERT_ALT_NAME_IP_ADDRESS: case CERT_ALT_NAME_REGISTERED_ID: default: break; } }
//+---------------------------------------------------------------------------
//
// Function: ChainFormatNameConstraintsAltNameEntryFixup
//
// Synopsis: localization format and allocate a previously fixed up
// AltName entry.
//
// The returned string must be freed via PkiFree().
//
//----------------------------------------------------------------------------
LPWSTR WINAPI ChainFormatNameConstraintsAltNameEntryFixup( IN PCERT_ALT_NAME_ENTRY pEntry ) { DWORD dwExceptionCode; LPWSTR pwszFormat = NULL; DWORD cbFormat = 0; CERT_ALT_NAME_ENTRY AltEntry; const CERT_ALT_NAME_INFO AltNameInfo = { 1, &AltEntry }; CERT_OTHER_NAME OtherName;
BYTE *pbEncoded = NULL; DWORD cbEncoded; BYTE *pbEncoded2 = NULL; DWORD cbEncoded2;
__try {
AltEntry = *pEntry;
// Restore fixed up entries so we can re-encode
switch (AltEntry.dwAltNameChoice) { case CERT_ALT_NAME_OTHER_NAME: if (0 == strcmp(pEntry->pOtherName->pszObjId, szOID_NT_PRINCIPAL_NAME)) { // Restore from the following fixup:
// pEntry->pOtherName->Value.pbData :: PCERT_NAME_VALUE
if (NULL == pEntry->pOtherName->Value.pbData) goto InvalidUPN; if (!ChainAllocAndEncodeObject( X509_UNICODE_ANY_STRING, (PCERT_NAME_VALUE) pEntry->pOtherName->Value.pbData, &pbEncoded2, &cbEncoded2 )) goto EncodedUPNError; OtherName.pszObjId = pEntry->pOtherName->pszObjId; OtherName.Value.pbData = pbEncoded2; OtherName.Value.cbData = cbEncoded2;
AltEntry.pOtherName = &OtherName; } break; case CERT_ALT_NAME_RFC822_NAME: case CERT_ALT_NAME_DNS_NAME: case CERT_ALT_NAME_URL: // Restore from the following fixup:
// pEntry->DirectoryName.pbData = (BYTE *) pwsz;
// pEntry->DirectoryName.cbData = cch * sizeof(WCHAR);
if (NULL == pEntry->DirectoryName.pbData || 0 == pEntry->DirectoryName.cbData) AltEntry.pwszRfc822Name = L"???"; else AltEntry.pwszRfc822Name = (LPWSTR) pEntry->DirectoryName.pbData; break; case CERT_ALT_NAME_DIRECTORY_NAME: // Restore from the following fixup:
// pEntry->DirectoryName.pbData :: PCERT_NAME_INFO
if (NULL == pEntry->DirectoryName.pbData) goto InvalidDirName; if (!ChainAllocAndEncodeObject( X509_UNICODE_NAME, (PCERT_NAME_INFO) pEntry->DirectoryName.pbData, &pbEncoded2, &cbEncoded2 )) goto EncodeDirNameError;
AltEntry.DirectoryName.pbData = pbEncoded2; AltEntry.DirectoryName.cbData = cbEncoded2; break; case CERT_ALT_NAME_X400_ADDRESS: case CERT_ALT_NAME_EDI_PARTY_NAME: case CERT_ALT_NAME_IP_ADDRESS: case CERT_ALT_NAME_REGISTERED_ID: default: break; }
if (!ChainAllocAndEncodeObject( X509_ALTERNATE_NAME, &AltNameInfo, &pbEncoded, &cbEncoded )) goto EncodeAltNameError;
if (!CryptFormatObject( X509_ASN_ENCODING, 0, // dwFormatType
0, // dwFormatStrType
NULL, // pFormatStruct
X509_ALTERNATE_NAME, pbEncoded, cbEncoded, NULL, // pwszFormat
&cbFormat )) goto FormatAltNameError;
if (NULL == (pwszFormat = (LPWSTR) PkiZeroAlloc( cbFormat + sizeof(WCHAR)))) goto OutOfMemory;
if (!CryptFormatObject( X509_ASN_ENCODING, 0, // dwFormatType
0, // dwFormatStrType
NULL, // pFormatStruct
X509_ALTERNATE_NAME, pbEncoded, cbEncoded, pwszFormat, &cbFormat )) goto FormatAltNameError;
} __except(EXCEPTION_EXECUTE_HANDLER) { dwExceptionCode = GetExceptionCode(); goto ExceptionError; }
CommonReturn: PkiFree(pbEncoded); PkiFree(pbEncoded2);
return pwszFormat;
ErrorReturn: if (pwszFormat) { PkiFree(pwszFormat); pwszFormat = NULL; } goto CommonReturn;
SET_ERROR(InvalidUPN, ERROR_INVALID_DATA) TRACE_ERROR(EncodedUPNError) TRACE_ERROR(InvalidDirName) TRACE_ERROR(EncodeDirNameError) TRACE_ERROR(EncodeAltNameError) TRACE_ERROR(FormatAltNameError) TRACE_ERROR(OutOfMemory) SET_ERROR_VAR(ExceptionError, dwExceptionCode) }
//+---------------------------------------------------------------------------
//
// Function: ChainFormatAndAppendNameConstraintsAltNameEntryFixup
//
// Synopsis: localization format a previously fixed up
// AltName entry and append to the extended error information.
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFormatAndAppendNameConstraintsAltNameEntryFixup( IN OUT LPWSTR *ppwszExtErrorInfo, IN PCERT_ALT_NAME_ENTRY pEntry, IN UINT nFormatID, IN OPTIONAL DWORD dwSubtreeIndex // 0 => no subtree parameter
) { LPWSTR pwszAllocFormatEntry = NULL; LPWSTR pwszFormatEntry;
pwszAllocFormatEntry = ChainFormatNameConstraintsAltNameEntryFixup(pEntry); if (pwszAllocFormatEntry) pwszFormatEntry = pwszAllocFormatEntry; else pwszFormatEntry = L"???";
if (0 == dwSubtreeIndex) ChainFormatAndAppendExtendedErrorInfo( ppwszExtErrorInfo, nFormatID, pwszFormatEntry ); else ChainFormatAndAppendExtendedErrorInfo( ppwszExtErrorInfo, nFormatID, dwSubtreeIndex, pwszFormatEntry );
PkiFree(pwszAllocFormatEntry); }
//+---------------------------------------------------------------------------
//
// Function: ChainGetIssuerNameConstraintsInfo
//
// Synopsis: alloc and return the issuer name constraints info.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainGetIssuerNameConstraintsInfo ( IN PCCERT_CONTEXT pCertContext, IN OUT PCERT_NAME_CONSTRAINTS_INFO *ppInfo ) { BOOL fResult; PCERT_EXTENSION pExt; PCERT_NAME_CONSTRAINTS_INFO pInfo = NULL; PCERT_GENERAL_SUBTREE pSubtree; DWORD cSubtree;
pExt = CertFindExtension( szOID_NAME_CONSTRAINTS, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension ); if (NULL == pExt) goto SuccessReturn;
pInfo = (PCERT_NAME_CONSTRAINTS_INFO) ChainAllocAndDecodeObject( X509_NAME_CONSTRAINTS, pExt->Value.pbData, pExt->Value.cbData ); if (NULL == pInfo) goto DecodeError;
// Fixup all the AltName entries
// Note, even for an error we need to fixup all the entries.
// ChainFreeIssuerNameConstraintsInfo iterates through all the entries.
fResult = TRUE;
cSubtree = pInfo->cPermittedSubtree; pSubtree = pInfo->rgPermittedSubtree; for ( ; 0 < cSubtree; cSubtree--, pSubtree++) { if (!ChainFixupNameConstraintsAltNameEntry(FALSE, &pSubtree->Base)) fResult = FALSE; }
cSubtree = pInfo->cExcludedSubtree; pSubtree = pInfo->rgExcludedSubtree; for ( ; 0 < cSubtree; cSubtree--, pSubtree++) { if (!ChainFixupNameConstraintsAltNameEntry(FALSE, &pSubtree->Base)) fResult = FALSE; }
if (!fResult) goto FixupAltNameEntryError;
SuccessReturn: fResult = TRUE; CommonReturn: *ppInfo = pInfo; return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn;
TRACE_ERROR(DecodeError) TRACE_ERROR(FixupAltNameEntryError) }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeIssuerNameConstraintsInfo
//
// Synopsis: free the issuer name constraints info
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeIssuerNameConstraintsInfo ( IN OUT PCERT_NAME_CONSTRAINTS_INFO pInfo ) { PCERT_GENERAL_SUBTREE pSubtree; DWORD cSubtree;
if (NULL == pInfo) return;
cSubtree = pInfo->cPermittedSubtree; pSubtree = pInfo->rgPermittedSubtree; for ( ; 0 < cSubtree; cSubtree--, pSubtree++) ChainFreeNameConstraintsAltNameEntryFixup(FALSE, &pSubtree->Base);
cSubtree = pInfo->cExcludedSubtree; pSubtree = pInfo->rgExcludedSubtree; for ( ; 0 < cSubtree; cSubtree--, pSubtree++) ChainFreeNameConstraintsAltNameEntryFixup(FALSE, &pSubtree->Base);
PkiFree(pInfo); }
//+---------------------------------------------------------------------------
//
// Function: ChainGetSubjectNameConstraintsInfo
//
// Synopsis: alloc and return the subject name constraints info.
//
//----------------------------------------------------------------------------
VOID WINAPI ChainGetSubjectNameConstraintsInfo ( IN PCCERT_CONTEXT pCertContext, IN OUT PCHAIN_SUBJECT_NAME_CONSTRAINTS_INFO pSubjectInfo ) { PCERT_EXTENSION pExt; BOOL fHasEmailAltNameEntry = FALSE;
pExt = CertFindExtension( szOID_SUBJECT_ALT_NAME2, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension );
if (NULL == pExt) { pExt = CertFindExtension( szOID_SUBJECT_ALT_NAME, pCertContext->pCertInfo->cExtension, pCertContext->pCertInfo->rgExtension ); }
if (pExt) { PCERT_ALT_NAME_INFO pAltNameInfo;
pAltNameInfo = (PCERT_ALT_NAME_INFO) ChainAllocAndDecodeObject( X509_ALTERNATE_NAME, pExt->Value.pbData, pExt->Value.cbData ); if (NULL == pAltNameInfo) pSubjectInfo->fInvalid = TRUE; else { DWORD cEntry; PCERT_ALT_NAME_ENTRY pEntry; pSubjectInfo->pAltNameInfo = pAltNameInfo;
// Fixup all the AltName entries
// Note, even for an error we need to fixup all the entries.
// ChainFreeSubjectNameConstraintsInfo iterates through all
// the entries.
cEntry = pAltNameInfo->cAltEntry; pEntry = pAltNameInfo->rgAltEntry; for ( ; 0 < cEntry; cEntry--, pEntry++) { if (CERT_ALT_NAME_RFC822_NAME == pEntry->dwAltNameChoice) fHasEmailAltNameEntry = TRUE; else if (CERT_ALT_NAME_DNS_NAME == pEntry->dwAltNameChoice) pSubjectInfo->fHasDnsAltNameEntry = TRUE;
if (!ChainFixupNameConstraintsAltNameEntry(TRUE, pEntry)) pSubjectInfo->fInvalid = TRUE; } } }
if (!ChainAllocDecodeAndFixupNameConstraintsDirectoryName( &pCertContext->pCertInfo->Subject, &pSubjectInfo->pUnicodeNameInfo )) pSubjectInfo->fInvalid = TRUE;
if (!fHasEmailAltNameEntry && pSubjectInfo->pUnicodeNameInfo) { DWORD cRDN; PCERT_RDN pRDN;
cRDN = pSubjectInfo->pUnicodeNameInfo->cRDN; pRDN = pSubjectInfo->pUnicodeNameInfo->rgRDN; for ( ; cRDN > 0; cRDN--, pRDN++) { DWORD cAttr = pRDN->cRDNAttr; PCERT_RDN_ATTR pAttr = pRDN->rgRDNAttr; for ( ; cAttr > 0; cAttr--, pAttr++) { if (!IS_CERT_RDN_CHAR_STRING(pAttr->dwValueType)) continue;
if (0 == strcmp(pAttr->pszObjId, szOID_RSA_emailAddr)) { pSubjectInfo->pEmailAttr = pAttr; break; }
} if (cAttr > 0) break; } } }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeSubjectNameConstraintsInfo
//
// Synopsis: free the subject name constraints info
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeSubjectNameConstraintsInfo ( IN OUT PCHAIN_SUBJECT_NAME_CONSTRAINTS_INFO pSubjectInfo ) { PCERT_ALT_NAME_INFO pAltNameInfo;
pAltNameInfo = pSubjectInfo->pAltNameInfo; if (pAltNameInfo) { DWORD cEntry; PCERT_ALT_NAME_ENTRY pEntry; cEntry = pAltNameInfo->cAltEntry; pEntry = pAltNameInfo->rgAltEntry; for ( ; 0 < cEntry; cEntry--, pEntry++) ChainFreeNameConstraintsAltNameEntryFixup(TRUE, pEntry);
PkiFree(pAltNameInfo); }
PkiFree(pSubjectInfo->pUnicodeNameInfo); }
//+---------------------------------------------------------------------------
//
// Function: ChainCompareNameConstraintsDirectoryName
//
// Synopsis: returns TRUE if all the subtree RDN attributes match
// the RDN attributes at the beginning of the subject
// directory name. A case insensitive match
// is performed on each RDN attribute that is a string type.
// A binary compare is performed on nonstring attribute types.
//
// The OIDs of the RDN attributes must match.
//
// Note, a NULL subtree or a subtree with no RDNs matches
// any subject directory name. Also, an empty subtree
// RDN attribute matches any subject attribute.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCompareNameConstraintsDirectoryName( IN PCERT_NAME_INFO pSubjectInfo, IN PCERT_NAME_INFO pSubtreeInfo ) { DWORD cSubjectRDN; PCERT_RDN pSubjectRDN; DWORD cSubtreeRDN; PCERT_RDN pSubtreeRDN;
if (NULL == pSubtreeInfo || 0 == pSubtreeInfo->cRDN) // Match any subject
return TRUE; if (NULL == pSubjectInfo) return FALSE;
cSubjectRDN = pSubjectInfo->cRDN; cSubtreeRDN = pSubtreeInfo->cRDN; if (cSubtreeRDN > cSubjectRDN) return FALSE;
pSubjectRDN = pSubjectInfo->rgRDN; pSubtreeRDN = pSubtreeInfo->rgRDN; for ( ; cSubtreeRDN > 0; cSubtreeRDN--, pSubtreeRDN++, pSubjectRDN++) { DWORD cSubjectAttr = pSubjectRDN->cRDNAttr; PCERT_RDN_ATTR pSubjectAttr = pSubjectRDN->rgRDNAttr; DWORD cSubtreeAttr = pSubtreeRDN->cRDNAttr; PCERT_RDN_ATTR pSubtreeAttr = pSubtreeRDN->rgRDNAttr;
if (1 < cSubtreeRDN) { if (cSubtreeAttr != cSubjectAttr) return FALSE; } else { if (cSubtreeAttr > cSubjectAttr) return FALSE; }
for ( ; cSubtreeAttr > 0; cSubtreeAttr--, pSubtreeAttr++, pSubjectAttr++) { if (0 != strcmp(pSubtreeAttr->pszObjId, pSubjectAttr->pszObjId)) return FALSE;
if (IS_CERT_RDN_CHAR_STRING(pSubtreeAttr->dwValueType) != IS_CERT_RDN_CHAR_STRING(pSubjectAttr->dwValueType)) return FALSE;
if (IS_CERT_RDN_CHAR_STRING(pSubtreeAttr->dwValueType)) { DWORD cchSubtree = pSubtreeAttr->Value.cbData / sizeof(WCHAR);
if (0 == cchSubtree) { // Match any attribute
; } else if (cchSubtree != pSubjectAttr->Value.cbData / sizeof(WCHAR)) { // For X.509, must match entire attribute
return FALSE; } else if (!ChainIsRightStringInString( (LPCWSTR) pSubtreeAttr->Value.pbData, cchSubtree, (LPCWSTR) pSubjectAttr->Value.pbData, cchSubtree )) { return FALSE; } } else { if (pSubtreeAttr->Value.cbData != pSubjectAttr->Value.cbData) return FALSE; if (0 != memcmp(pSubtreeAttr->Value.pbData, pSubjectAttr->Value.pbData, pSubtreeAttr->Value.cbData )) return FALSE; } } }
return TRUE; }
//+---------------------------------------------------------------------------
//
// Function: ChainCompareNameConstraintsIPAddress
//
// Synopsis: returns TRUE if the subject IP address is within the IP
// range specified by subtree IP address and mask.
//
// The subtree IP contains the octet bytes for both the
// IP address and its mask.
//
// For IPv4, there are 4 address bytes followed by 4 mask bytes.
// See RFC 2459 for more details.
//
// Here's my interpretation:
//
// For a match: SubtreeIPAddr == (SubjectIPAddr & SubtreeIPMask)
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCompareNameConstraintsIPAddress( IN PCRYPT_DATA_BLOB pSubjectIPAddress, IN PCRYPT_DATA_BLOB pSubtreeIPAddress ) { BYTE *pbSubject = pSubjectIPAddress->pbData; DWORD cbSubject = pSubjectIPAddress->cbData; BYTE *pbSubtree = pSubtreeIPAddress->pbData; DWORD cbSubtree = pSubtreeIPAddress->cbData; BYTE *pbSubtreeMask = pbSubtree + cbSubject;
DWORD i;
if (0 == cbSubtree) // Match any IP address
return TRUE;
// Only compare if the number of subtree bytes is twice the length of
// the subject. Second half contains the mask.
if (cbSubtree != 2 * cbSubject) return FALSE;
for (i = 0; i < cbSubject; i++) { if (pbSubtree[i] != (pbSubject[i] & pbSubtreeMask[i])) return FALSE; }
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Function: ChainCompareNameConstraintsUPN
//
// Synopsis: returns TRUE if the subtree UPN string matches the right most
// characters of the subject's UPN doing a case insensitive
// match.
//
// Note, the Value.pbData points to the decoded PCERT_NAME_VALUE.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCompareNameConstraintsUPN( IN PCRYPT_OBJID_BLOB pSubjectValue, IN PCRYPT_OBJID_BLOB pSubtreeValue ) { // The UPN's Value.pbData is used to point to the decoded
// PCERT_NAME_VALUE
BOOL fCompare; PCERT_NAME_VALUE pSubjectNameValue; PCERT_NAME_VALUE pSubtreeNameValue;
pSubjectNameValue = (PCERT_NAME_VALUE) pSubjectValue->pbData; pSubtreeNameValue = (PCERT_NAME_VALUE) pSubtreeValue->pbData;
if (pSubjectNameValue && pSubtreeNameValue) fCompare = ChainIsRightStringInString( (LPCWSTR) pSubtreeNameValue->Value.pbData, pSubtreeNameValue->Value.cbData / sizeof(WCHAR), (LPCWSTR) pSubjectNameValue->Value.pbData, pSubjectNameValue->Value.cbData / sizeof(WCHAR) ); else fCompare = FALSE;
return fCompare; }
//+---------------------------------------------------------------------------
//
// Function: ChainCalculateNameConstraintsSubtreeErrorStatusForAltNameEntry
//
// Synopsis: calculates the name constraints error status by seeing if
// the subject AltName entry matches any subtree AltName entry.
//
//----------------------------------------------------------------------------
DWORD WINAPI ChainCalculateNameConstraintsSubtreeErrorStatusForAltNameEntry( IN PCERT_ALT_NAME_ENTRY pSubjectEntry, IN BOOL fExcludedSubtree, IN DWORD cSubtree, IN PCERT_GENERAL_SUBTREE pSubtree, IN OUT LPWSTR *ppwszExtErrorInfo ) { DWORD dwErrorStatus = 0; BOOL fHasSubtreeEntry = FALSE; DWORD dwAltNameChoice = pSubjectEntry->dwAltNameChoice; DWORD i;
for (i = 0; i < cSubtree; i++, pSubtree++) { PCERT_ALT_NAME_ENTRY pSubtreeEntry; BOOL fCompare;
if (0 != pSubtree->dwMinimum || pSubtree->fMaximum) { dwErrorStatus |= CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
ChainFormatAndAppendExtendedErrorInfo( ppwszExtErrorInfo, fExcludedSubtree ? IDS_NOT_SUPPORTED_EXCLUDED_NAME_CONSTRAINT : IDS_NOT_SUPPORTED_PERMITTED_NAME_CONSTRAINT, i + 1 ); continue; }
pSubtreeEntry = &pSubtree->Base; if (dwAltNameChoice != pSubtreeEntry->dwAltNameChoice) continue;
fCompare = FALSE; switch (dwAltNameChoice) { case CERT_ALT_NAME_OTHER_NAME: // Only support the UPN OID
if (0 != strcmp(pSubtreeEntry->pOtherName->pszObjId, szOID_NT_PRINCIPAL_NAME)) { dwErrorStatus |= CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
ChainFormatAndAppendExtendedErrorInfo( ppwszExtErrorInfo, fExcludedSubtree ? IDS_NOT_SUPPORTED_EXCLUDED_NAME_CONSTRAINT : IDS_NOT_SUPPORTED_PERMITTED_NAME_CONSTRAINT, i + 1 ); } else { assert(0 == strcmp(pSubjectEntry->pOtherName->pszObjId, szOID_NT_PRINCIPAL_NAME)); fHasSubtreeEntry = TRUE; fCompare = ChainCompareNameConstraintsUPN( &pSubjectEntry->pOtherName->Value, &pSubtreeEntry->pOtherName->Value ); } break; case CERT_ALT_NAME_RFC822_NAME: case CERT_ALT_NAME_DNS_NAME: case CERT_ALT_NAME_URL: fHasSubtreeEntry = TRUE; // The directory name's BLOB choice is used to contain both
// the pointer to and length of the string
fCompare = ChainIsRightStringInString( (LPCWSTR) pSubtreeEntry->DirectoryName.pbData, pSubtreeEntry->DirectoryName.cbData / sizeof(WCHAR), (LPCWSTR) pSubjectEntry->DirectoryName.pbData, pSubjectEntry->DirectoryName.cbData / sizeof(WCHAR) ); break; case CERT_ALT_NAME_DIRECTORY_NAME: fHasSubtreeEntry = TRUE; fCompare = ChainCompareNameConstraintsDirectoryName( (PCERT_NAME_INFO) pSubjectEntry->DirectoryName.pbData, (PCERT_NAME_INFO) pSubtreeEntry->DirectoryName.pbData ); break; case CERT_ALT_NAME_IP_ADDRESS: fHasSubtreeEntry = TRUE; fCompare = ChainCompareNameConstraintsIPAddress( &pSubjectEntry->IPAddress, &pSubtreeEntry->IPAddress); break; case CERT_ALT_NAME_X400_ADDRESS: case CERT_ALT_NAME_EDI_PARTY_NAME: case CERT_ALT_NAME_REGISTERED_ID: default: assert(0); break; }
if (fCompare) { if (fExcludedSubtree) { dwErrorStatus |= CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
ChainFormatAndAppendNameConstraintsAltNameEntryFixup( ppwszExtErrorInfo, pSubjectEntry, IDS_EXCLUDED_ENTRY_NAME_CONSTRAINT, i + 1 ); } return dwErrorStatus; } }
if (!fExcludedSubtree) { if (fHasSubtreeEntry) { dwErrorStatus |= CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
ChainFormatAndAppendNameConstraintsAltNameEntryFixup( ppwszExtErrorInfo, pSubjectEntry, IDS_NOT_PERMITTED_ENTRY_NAME_CONSTRAINT ); } else { dwErrorStatus |= CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT;
ChainFormatAndAppendNameConstraintsAltNameEntryFixup( ppwszExtErrorInfo, pSubjectEntry, IDS_NOT_DEFINED_ENTRY_NAME_CONSTRAINT ); } }
return dwErrorStatus; }
//+---------------------------------------------------------------------------
//
// Function: ChainCalculateNameConstraintsErrorStatusForAltNameEntry
//
// Synopsis: calculates the name constraints error status by seeing if
// the subject AltName entry matches either an excluded
// or permitted subtree AltName entry.
//
//----------------------------------------------------------------------------
DWORD WINAPI ChainCalculateNameConstraintsErrorStatusForAltNameEntry( IN PCERT_ALT_NAME_ENTRY pSubjectEntry, IN PCERT_NAME_CONSTRAINTS_INFO pNameConstraintsInfo, IN OUT LPWSTR *ppwszExtErrorInfo ) { DWORD dwErrorStatus;
dwErrorStatus = ChainCalculateNameConstraintsSubtreeErrorStatusForAltNameEntry( pSubjectEntry, TRUE, // fExcludedSubtree
pNameConstraintsInfo->cExcludedSubtree, pNameConstraintsInfo->rgExcludedSubtree, ppwszExtErrorInfo );
if (!(dwErrorStatus & CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT)) dwErrorStatus = ChainCalculateNameConstraintsSubtreeErrorStatusForAltNameEntry( pSubjectEntry, FALSE, // fExcludedSubtree
pNameConstraintsInfo->cPermittedSubtree, pNameConstraintsInfo->rgPermittedSubtree, ppwszExtErrorInfo );
return dwErrorStatus; }
//+===========================================================================
// CCertIssuerList helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Function: ChainCreateIssuerList
//
// Synopsis: create the issuer list object for the given subject
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCreateIssuerList ( IN PCCHAINPATHOBJECT pSubject, OUT PCCERTISSUERLIST* ppIssuerList ) { PCCERTISSUERLIST pIssuerList;
pIssuerList = new CCertIssuerList( pSubject ); if ( pIssuerList == NULL ) { SetLastError( (DWORD) E_OUTOFMEMORY ); return( FALSE ); }
*ppIssuerList = pIssuerList; return( TRUE ); }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeIssuerList
//
// Synopsis: free the issuer list object
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeIssuerList ( IN PCCERTISSUERLIST pIssuerList ) { delete pIssuerList; }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeCtlIssuerData
//
// Synopsis: free CTL issuer data
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeCtlIssuerData ( IN PCTL_ISSUER_DATA pCtlIssuerData ) { if ( pCtlIssuerData->pTrustListInfo != NULL ) { SSCtlFreeTrustListInfo( pCtlIssuerData->pTrustListInfo ); }
if ( pCtlIssuerData->pSSCtlObject != NULL ) { pCtlIssuerData->pSSCtlObject->Release(); }
delete pCtlIssuerData; }
//+===========================================================================
// INTERNAL_CERT_CHAIN_CONTEXT helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Function: ChainAddRefInternalChainContext
//
// Synopsis: addref the internal chain context
//
//----------------------------------------------------------------------------
VOID WINAPI ChainAddRefInternalChainContext ( IN PINTERNAL_CERT_CHAIN_CONTEXT pChainContext ) { InterlockedIncrement( &pChainContext->cRefs ); }
//+---------------------------------------------------------------------------
//
// Function: ChainReleaseInternalChainContext
//
// Synopsis: release the internal chain context
//
//----------------------------------------------------------------------------
VOID WINAPI ChainReleaseInternalChainContext ( IN PINTERNAL_CERT_CHAIN_CONTEXT pChainContext ) { if ( InterlockedDecrement( &pChainContext->cRefs ) == 0 ) { ChainFreeInternalChainContext( pChainContext ); } }
//+---------------------------------------------------------------------------
//
// Function: ChainFreeInternalChainContext
//
// Synopsis: free the internal chain context
//
//----------------------------------------------------------------------------
VOID WINAPI ChainFreeInternalChainContext ( IN PINTERNAL_CERT_CHAIN_CONTEXT pContext ) {
PCERT_SIMPLE_CHAIN *ppChain; DWORD cChain;
PINTERNAL_CERT_CHAIN_CONTEXT *ppLowerContext;
if (NULL == pContext) return;
cChain = pContext->ChainContext.cChain; ppChain = pContext->ChainContext.rgpChain; for ( ; 0 < cChain; cChain--, ppChain++) { PCERT_SIMPLE_CHAIN pChain; DWORD cElement; PCERT_CHAIN_ELEMENT *ppElement;
pChain = *ppChain; if (NULL == pChain) continue;
if (pChain->pTrustListInfo) SSCtlFreeTrustListInfo(pChain->pTrustListInfo);
cElement = pChain->cElement; ppElement = pChain->rgpElement; for ( ; 0 < cElement; cElement--, ppElement++) { PCERT_CHAIN_ELEMENT pElement;
pElement = *ppElement; if (NULL == pElement) continue;
if (pElement->pRevocationInfo) { PCERT_REVOCATION_CRL_INFO pCrlInfo = pElement->pRevocationInfo->pCrlInfo;
if (pCrlInfo) { if (pCrlInfo->pBaseCrlContext) CertFreeCRLContext(pCrlInfo->pBaseCrlContext); if (pCrlInfo->pDeltaCrlContext) CertFreeCRLContext(pCrlInfo->pDeltaCrlContext);
delete pCrlInfo; }
delete pElement->pRevocationInfo; }
if (pElement->pCertContext) CertFreeCertificateContext(pElement->pCertContext);
ChainFreeUsage(pElement->pIssuanceUsage); ChainFreeUsage(pElement->pApplicationUsage);
if (pElement->pwszExtendedErrorInfo) PkiFree((LPWSTR) pElement->pwszExtendedErrorInfo); }
}
ppLowerContext = (PINTERNAL_CERT_CHAIN_CONTEXT*) pContext->ChainContext.rgpLowerQualityChainContext; if (ppLowerContext) { DWORD cLowerContext; DWORD i;
cLowerContext = pContext->ChainContext.cLowerQualityChainContext; for (i = 0; i < cLowerContext; i++) ChainReleaseInternalChainContext(ppLowerContext[i]);
delete ppLowerContext; }
PkiFree(pContext); }
//+---------------------------------------------------------------------------
//
// Function: ChainUpdateEndEntityCertContext
//
// Synopsis: update the end entity cert context in the chain context
//
//----------------------------------------------------------------------------
VOID ChainUpdateEndEntityCertContext( IN OUT PINTERNAL_CERT_CHAIN_CONTEXT pChainContext, IN OUT PCCERT_CONTEXT pEndCertContext ) { PCCERT_CONTEXT pCertContext = pChainContext->ChainContext.rgpChain[0]->rgpElement[0]->pCertContext; if (pCertContext == pEndCertContext) return; pChainContext->ChainContext.rgpChain[0]->rgpElement[0]->pCertContext = pEndCertContext;
{ DWORD cbData; DWORD cbEndData;
// If the chain context's end context has the public key parameter
// property and the end context passed in to CertGetCertificateChain
// doesn't, then copy the public key parameter property.
if (CertGetCertificateContextProperty( pCertContext, CERT_PUBKEY_ALG_PARA_PROP_ID, NULL, // pvData
&cbData) && 0 < cbData && !CertGetCertificateContextProperty( pEndCertContext, CERT_PUBKEY_ALG_PARA_PROP_ID, NULL, // pvData
&cbEndData)) { BYTE *pbData;
__try { pbData = (BYTE *) _alloca(cbData); } __except(EXCEPTION_EXECUTE_HANDLER) { pbData = NULL; } if (pbData) { if (CertGetCertificateContextProperty( pCertContext, CERT_PUBKEY_ALG_PARA_PROP_ID, pbData, &cbData)) { CRYPT_DATA_BLOB Para; Para.pbData = pbData; Para.cbData = cbData; CertSetCertificateContextProperty( pEndCertContext, CERT_PUBKEY_ALG_PARA_PROP_ID, CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, &Para ); } } } }
CertDuplicateCertificateContext(pEndCertContext); CertFreeCertificateContext(pCertContext); }
//+===========================================================================
// CERT_REVOCATION_INFO helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Function: ChainUpdateRevocationInfo
//
// Synopsis: update the revocation information on the element
//
//----------------------------------------------------------------------------
VOID WINAPI ChainUpdateRevocationInfo ( IN PCERT_REVOCATION_STATUS pRevStatus, IN OUT PCERT_REVOCATION_INFO pRevocationInfo, IN OUT PCERT_TRUST_STATUS pTrustStatus ) { CertPerfIncrementChainRevocationCount();
if (ERROR_SUCCESS == pRevStatus->dwError) { ; } else if (CRYPT_E_REVOKED == pRevStatus->dwError) { pTrustStatus->dwErrorStatus |= CERT_TRUST_IS_REVOKED; CertPerfIncrementChainRevokedCount(); } else { pTrustStatus->dwErrorStatus |= CERT_TRUST_REVOCATION_STATUS_UNKNOWN; if (CRYPT_E_NO_REVOCATION_CHECK == pRevStatus->dwError) { CertPerfIncrementChainNoRevocationCheckCount(); } else { pTrustStatus->dwErrorStatus |= CERT_TRUST_IS_OFFLINE_REVOCATION; CertPerfIncrementChainRevocationOfflineCount(); } }
pRevocationInfo->cbSize = sizeof(CERT_REVOCATION_INFO); pRevocationInfo->dwRevocationResult = pRevStatus->dwError; pRevocationInfo->fHasFreshnessTime = pRevStatus->fHasFreshnessTime; pRevocationInfo->dwFreshnessTime = pRevStatus->dwFreshnessTime; }
//+===========================================================================
// CCertChainEngine helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Function: ChainCreateWorldStore
//
// Synopsis: create the world store
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCreateWorldStore ( IN HCERTSTORE hRoot, IN HCERTSTORE hCA, IN DWORD cAdditionalStore, IN HCERTSTORE* rghAdditionalStore, IN DWORD dwStoreFlags, OUT HCERTSTORE* phWorld ) { BOOL fResult; HCERTSTORE hWorld; HCERTSTORE hStore; DWORD cCount;
hWorld = CertOpenStore( CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL );
if ( hWorld == NULL ) { return( FALSE ); }
fResult = CertAddStoreToCollection( hWorld, hRoot, 0, 0 );
for ( cCount = 0; ( cCount < cAdditionalStore ) && ( fResult == TRUE ); cCount++ ) { fResult = CertAddStoreToCollection( hWorld, rghAdditionalStore[ cCount ], 0, 0 ); }
dwStoreFlags |= CERT_STORE_MAXIMUM_ALLOWED_FLAG | CERT_STORE_SHARE_CONTEXT_FLAG;
if ( fResult == TRUE ) { hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, dwStoreFlags, L"trust" );
if ( hStore != NULL ) { fResult = CertAddStoreToCollection( hWorld, hStore, 0, 0 ); CertCloseStore( hStore, 0 ); } else { fResult = FALSE; } }
if ( fResult == TRUE ) { if ( hCA != NULL ) { fResult = CertAddStoreToCollection( hWorld, hCA, 0, 0 ); } else { fResult = FALSE; } }
if ( fResult == TRUE ) { hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, dwStoreFlags, L"my" );
if ( hStore != NULL ) { fResult = CertAddStoreToCollection( hWorld, hStore, 0, 0 ); CertCloseStore( hStore, 0 ); } else { fResult = FALSE; } }
if ( fResult == TRUE ) { *phWorld = hWorld; } else { CertCloseStore( hWorld, 0 ); }
return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: ChainCreateEngineStore
//
// Synopsis: create the engine store and the change event handle
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCreateEngineStore ( IN HCERTSTORE hRootStore, IN HCERTSTORE hTrustStore, IN HCERTSTORE hOtherStore, IN BOOL fDefaultEngine, IN DWORD dwFlags, OUT HCERTSTORE* phEngineStore, OUT HANDLE* phEngineStoreChangeEvent ) { BOOL fResult = TRUE; HCERTSTORE hEngineStore; HANDLE hEvent;
hEngineStore = CertOpenStore( CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL );
hEvent = CreateEventA( NULL, FALSE, FALSE, NULL );
if ( ( hEngineStore == NULL ) || ( hEvent == NULL ) ) { fResult = FALSE; }
if ( fResult == TRUE ) { fResult = CertAddStoreToCollection( hEngineStore, hRootStore, 0, 0 ); }
if ( fResult == TRUE ) { fResult = CertAddStoreToCollection( hEngineStore, hTrustStore, 0, 0 ); }
if ( fResult == TRUE ) { fResult = CertAddStoreToCollection( hEngineStore, hOtherStore, 0, 0 ); }
if ( ( fResult == TRUE ) && ( dwFlags & CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE ) ) { // Someday support a let me know about errors flag
CertControlStore( hEngineStore, CERT_STORE_CTRL_INHIBIT_DUPLICATE_HANDLE_FLAG, CERT_STORE_CTRL_NOTIFY_CHANGE, &hEvent ); }
if ( fResult == TRUE ) { *phEngineStore = hEngineStore; *phEngineStoreChangeEvent = hEvent; } else { if ( hEngineStore != NULL ) { CertCloseStore( hEngineStore, 0 ); }
if ( hEvent != NULL ) { CloseHandle( hEvent ); } }
return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: ChainIsProperRestrictedRoot
//
// Synopsis: check to see if this restricted root store is a proper subset
// of the real root store
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainIsProperRestrictedRoot ( IN HCERTSTORE hRealRoot, IN HCERTSTORE hRestrictedRoot ) { PCCERT_CONTEXT pCertContext = NULL; PCCERT_CONTEXT pFound = NULL; DWORD cbData = CHAINHASHLEN; BYTE CertificateHash[ CHAINHASHLEN ]; CRYPT_HASH_BLOB HashBlob;
HashBlob.cbData = cbData; HashBlob.pbData = CertificateHash;
while ( ( pCertContext = CertEnumCertificatesInStore( hRestrictedRoot, pCertContext ) ) != NULL ) { if ( CertGetCertificateContextProperty( pCertContext, CERT_MD5_HASH_PROP_ID, CertificateHash, &cbData ) == TRUE ) { pFound = CertFindCertificateInStore( hRealRoot, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_MD5_HASH, &HashBlob, NULL );
if ( pFound == NULL ) { CertFreeCertificateContext( pCertContext ); return( FALSE ); } else { CertFreeCertificateContext( pFound ); } } }
return( TRUE ); }
//+---------------------------------------------------------------------------
//
// Function: ChainCreateCollectionIncludingCtlCertificates
//
// Synopsis: create a collection which includes the source store hStore and
// any CTL certificates from it
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCreateCollectionIncludingCtlCertificates ( IN HCERTSTORE hStore, OUT HCERTSTORE* phCollection ) { BOOL fResult = FALSE; HCERTSTORE hCollection; PCCTL_CONTEXT pCtlContext = NULL; HCERTSTORE hCtlStore;
hCollection = CertOpenStore( CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL );
if ( hCollection == NULL ) { return( FALSE ); }
fResult = CertAddStoreToCollection( hCollection, hStore, 0, 0 );
while ( ( fResult == TRUE ) && ( ( pCtlContext = CertEnumCTLsInStore( hStore, pCtlContext ) ) != NULL ) ) { hCtlStore = CertOpenStore( CERT_STORE_PROV_MSG, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, 0, pCtlContext->hCryptMsg );
if ( hCtlStore != NULL ) { fResult = CertAddStoreToCollection( hCollection, hCtlStore, 0, 0 );
CertCloseStore( hCtlStore, 0 ); } }
if ( fResult == TRUE ) { *phCollection = hCollection; } else { CertCloseStore( hCollection, 0 ); }
return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: ChainCopyToCAStore
//
// Synopsis: copies the hStore to the m_hCAStore of the engine
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainCopyToCAStore ( PCCERTCHAINENGINE pChainEngine, HCERTSTORE hStore ) { PCCERT_CONTEXT pCertContext = NULL;
if ( pChainEngine->CAStore() == NULL ) { SetLastError( ERROR_INVALID_PARAMETER ); return( FALSE ); }
while ( ( pCertContext = CertEnumCertificatesInStore( hStore, pCertContext ) ) != NULL ) { // Don't add self signed certificates to the CA store
if (!CertCompareCertificateName( pCertContext->dwCertEncodingType, &pCertContext->pCertInfo->Subject, &pCertContext->pCertInfo->Issuer )) { CertAddCertificateContextToStore( pChainEngine->CAStore(), pCertContext, CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, NULL ); } }
return( TRUE ); }
//+===========================================================================
// URL helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Function: ChainGetObjectUrl
//
// Synopsis: thunk to CryptGetObjectUrl in cryptnet.dll
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainGetObjectUrl ( IN LPCSTR pszUrlOid, IN LPVOID pvPara, IN DWORD dwFlags, OUT OPTIONAL PCRYPT_URL_ARRAY pUrlArray, IN OUT DWORD* pcbUrlArray, OUT OPTIONAL PCRYPT_URL_INFO pUrlInfo, IN OUT OPTIONAL DWORD* pcbUrlInfo, IN OPTIONAL LPVOID pvReserved ) { BOOL fResult = FALSE; HMODULE hModule; PFN_GETOBJECTURL pfn = NULL;
hModule = ChainGetCryptnetModule();
if ( hModule != NULL ) { pfn = (PFN_GETOBJECTURL)GetProcAddress( hModule, "CryptGetObjectUrl" ); }
if ( pfn != NULL ) { fResult = ( *pfn )( pszUrlOid, pvPara, dwFlags, pUrlArray, pcbUrlArray, pUrlInfo, pcbUrlInfo, pvReserved ); }
return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: ChainRetrieveObjectByUrlW
//
// Synopsis: thunk to CryptRetrieveObjectByUrlW in cryptnet.dll
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainRetrieveObjectByUrlW ( IN LPCWSTR pszUrl, IN LPCSTR pszObjectOid, IN DWORD dwRetrievalFlags, IN DWORD dwTimeout, OUT LPVOID* ppvObject, IN HCRYPTASYNC hAsyncRetrieve, IN PCRYPT_CREDENTIALS pCredentials, IN LPVOID pvVerify, IN OPTIONAL PCRYPT_RETRIEVE_AUX_INFO pAuxInfo ) { BOOL fResult = FALSE; HMODULE hModule; PFN_RETRIEVEOBJECTBYURLW pfn = NULL;
hModule = ChainGetCryptnetModule();
if ( hModule != NULL ) { pfn = (PFN_RETRIEVEOBJECTBYURLW)GetProcAddress( hModule, "CryptRetrieveObjectByUrlW" ); }
if ( pfn != NULL ) { fResult = ( *pfn )( pszUrl, pszObjectOid, dwRetrievalFlags, dwTimeout, ppvObject, hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo ); }
return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: ChainIsConnected
//
// Synopsis: thunk to I_CryptNetIsConnected in cryptnet.dll
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainIsConnected() { BOOL fResult = FALSE; HMODULE hModule; PFN_I_CRYPTNET_IS_CONNECTED pfn = NULL;
hModule = ChainGetCryptnetModule();
if ( hModule != NULL ) { pfn = (PFN_I_CRYPTNET_IS_CONNECTED)GetProcAddress( hModule, "I_CryptNetIsConnected" ); }
if ( pfn != NULL ) { fResult = ( *pfn )(); }
return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: ChainGetHostNameFromUrl
//
// Synopsis: thunk to I_CryptNetGetHostNameFromUrl in cryptnet.dll
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainGetHostNameFromUrl ( IN LPWSTR pwszUrl, IN DWORD cchHostName, OUT LPWSTR pwszHostName ) { BOOL fResult = FALSE; HMODULE hModule; PFN_I_CRYPTNET_GET_HOST_NAME_FROM_URL pfn = NULL;
hModule = ChainGetCryptnetModule();
if ( hModule != NULL ) { pfn = (PFN_I_CRYPTNET_GET_HOST_NAME_FROM_URL)GetProcAddress( hModule, "I_CryptNetGetHostNameFromUrl" ); }
if ( pfn != NULL ) { fResult = ( *pfn )( pwszUrl, cchHostName, pwszHostName ); }
return( fResult ); }
//+---------------------------------------------------------------------------
//
// Function: ChainIsFileOrLdapUrl
//
// Synopsis: check if the URL given is a file or ldap one
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainIsFileOrLdapUrl ( IN LPCWSTR pwszUrl ) { LPWSTR pwsz;
pwsz = wcschr( pwszUrl, L':' ); if ( pwsz != NULL ) { if ( ( _wcsnicmp( pwszUrl, L"file", 4 ) == 0 ) || ( _wcsnicmp( pwszUrl, L"ldap", 4 ) == 0 ) ) { return( TRUE ); } else { return( FALSE ); } }
return( TRUE ); }
//+---------------------------------------------------------------------------
//
// Function: ChainGetOfflineUrlDeltaSeconds
//
// Synopsis: given the number of unsuccessful attempts to retrieve the
// Url, returns the number of seconds to wait before the
// next attempt.
//
//----------------------------------------------------------------------------
const DWORD rgdwChainOfflineUrlDeltaSeconds[] = { 15, // 15 seconds
15, // 15 seconds
60, // 1 minute
60 * 5, // 5 minutes
60 * 10, // 10 minutes
60 * 30, // 30 minutes
};
#define CHAIN_OFFLINE_URL_DELTA_SECONDS_CNT \
(sizeof(rgdwChainOfflineUrlDeltaSeconds) / \ sizeof(rgdwChainOfflineUrlDeltaSeconds[0]))
DWORD WINAPI ChainGetOfflineUrlDeltaSeconds ( IN DWORD dwOfflineCnt ) { if (0 == dwOfflineCnt) return 0;
if (CHAIN_OFFLINE_URL_DELTA_SECONDS_CNT < dwOfflineCnt) dwOfflineCnt = CHAIN_OFFLINE_URL_DELTA_SECONDS_CNT;
return rgdwChainOfflineUrlDeltaSeconds[dwOfflineCnt - 1]; }
//+===========================================================================
// AuthRoot Auto Update methods and helper functions
//============================================================================
//+---------------------------------------------------------------------------
//
// Member: CChainPathObject::GetAuthRootAutoUpdateUrlStore, public
//
// Synopsis: attempts to get a time valid AuthRoot Auto Update CTL.
// Checks if there is CTL entry matching the subject
// certificate's AKI exact match, key identifier or name
// match. For a match URL retrieves the certificate and
// returns a store containing the retrieved certificates
//
// 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.
//
// Only returns FALSE, if the engine was touched when
// leaving the critical section.
//
// The caller has already checked that we are online.
//
//----------------------------------------------------------------------------
// CN=Root Agency
const BYTE rgbRootAgencyIssuerName[] = { 0x30, 0x16, // SEQUENCE
0x31, 0x14, // SET
0x30, 0x12, // SEQUENCE
0x06, 0x03, 0x55, 0x04, 0x03, // OID
// PRINTABLE STRING
0x13, 0x0b, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x41, 0x67, 0x65, 0x6e, 0x63, 0x79 };
// CN=Root SGC Authority
const BYTE rgbRootSGCAuthorityIssuerName[] = { 0x30, 0x1d, // SEQUENCE
0x31, 0x1b, // SET
0x30, 0x19, // SEQUENCE
0x06, 0x03, 0x55, 0x04, 0x03, // OID
// PRINTABLE STRING
0x13, 0x12, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x47, 0x43, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79 };
const CRYPT_DATA_BLOB rgSkipPartialIssuer[] = { sizeof(rgbRootAgencyIssuerName), (BYTE *) rgbRootAgencyIssuerName, sizeof(rgbRootSGCAuthorityIssuerName), (BYTE *) rgbRootSGCAuthorityIssuerName }; #define SKIP_PARTIAL_ISSUER_CNT (sizeof(rgSkipPartialIssuer)/ \
sizeof(rgSkipPartialIssuer[0]))
BOOL CChainPathObject::GetAuthRootAutoUpdateUrlStore( IN PCCHAINCALLCONTEXT pCallContext, OUT HCERTSTORE *phIssuerUrlStore ) { BOOL fTouchedResult = TRUE; PCCERTCHAINENGINE pChainEngine = pCallContext->ChainEngine(); PCERT_INFO pCertInfo = m_pCertObject->CertContext()->pCertInfo; PCCTL_CONTEXT pCtl = NULL; HCERTSTORE hIssuerUrlStore = NULL;
CRYPT_DATA_BLOB rgAuthRootMatchHash[AUTH_ROOT_MATCH_CNT]; DWORD cEntry = 0; PCTL_ENTRY *rgpEntry = NULL; PCCERT_CONTEXT pCert; DWORD cCert; DWORD i;
*phIssuerUrlStore = NULL;
// Loop and skip known issuers such as, "Root Agency". Don't want all
// clients in the world hiting the wire when building these chains
for (i = 0; i < SKIP_PARTIAL_ISSUER_CNT; i++) { if (pCertInfo->Issuer.cbData == rgSkipPartialIssuer[i].cbData && 0 == memcmp(pCertInfo->Issuer.pbData, rgSkipPartialIssuer[i].pbData, rgSkipPartialIssuer[i].cbData)) return TRUE; } fTouchedResult = pChainEngine->GetAuthRootAutoUpdateCtl( pCallContext, &pCtl );
if (!fTouchedResult || NULL == pCtl) {
#if 0
// This logs too many test failures
if (fTouchedResult) { PAUTH_ROOT_AUTO_UPDATE_INFO pInfo = pChainEngine->AuthRootAutoUpdateInfo();
if (NULL == pInfo || !(pInfo->dwFlags & CERT_AUTH_ROOT_AUTO_UPDATE_DISABLE_PARTIAL_CHAIN_LOGGING_FLAG)) IPR_LogCertInformation( MSG_PARTIAL_CHAIN_INFORMATIONAL, m_pCertObject->CertContext(), TRUE // fFormatIssuerName
); } #endif
return fTouchedResult; }
// We have a valid AuthRoot Auto Update CTL.
// See if we can find any matching AuthRoots
memset(rgAuthRootMatchHash, 0, sizeof(rgAuthRootMatchHash));
m_pCertObject->GetIssuerKeyMatchHash( &rgAuthRootMatchHash[AUTH_ROOT_KEY_MATCH_IDX]); m_pCertObject->GetIssuerNameMatchHash( &rgAuthRootMatchHash[AUTH_ROOT_NAME_MATCH_IDX]);
pChainEngine->FindAuthRootAutoUpdateMatchingCtlEntries( rgAuthRootMatchHash, &pCtl, &cEntry, &rgpEntry );
if (0 == cEntry) {
#if 0
// This logs too many test failures
PAUTH_ROOT_AUTO_UPDATE_INFO pInfo = pChainEngine->AuthRootAutoUpdateInfo();
if (NULL == pInfo || !(pInfo->dwFlags & CERT_AUTH_ROOT_AUTO_UPDATE_DISABLE_PARTIAL_CHAIN_LOGGING_FLAG)) IPR_LogCertInformation( MSG_PARTIAL_CHAIN_INFORMATIONAL, m_pCertObject->CertContext(), TRUE // fFormatIssuerName
); #endif
goto NoAutoUpdateCtlEntry; }
hIssuerUrlStore = CertOpenStore( CERT_STORE_PROV_MEMORY, 0, // dwEncodingType
NULL, // hCryptProv
0, // dwFlags
NULL // pvPara
); if (NULL == hIssuerUrlStore) goto OpenMemoryStoreError;
for (i = 0; i < cEntry; i++) { PCTL_ENTRY pEntry = rgpEntry[i];
// If already in our store, no need to hit the wire and retrieve.
if (pCert = CertFindCertificateInStore( pChainEngine->OtherStore(), X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SHA1_HASH, (LPVOID) &pEntry->SubjectIdentifier, NULL )) { CertFreeCertificateContext(pCert); continue; }
fTouchedResult = pChainEngine->GetAuthRootAutoUpdateCert( pCallContext, pEntry, hIssuerUrlStore );
if (!fTouchedResult) goto TouchedDuringUrlRetrievalOfAuthRoots; }
pCert = NULL; cCert = 0; while (pCert = CertEnumCertificatesInStore(hIssuerUrlStore, pCert)) cCert++;
if (0 == cCert) goto NoAuthRootAutoUpdateCerts;
if (1 < cCert) { // If more than one root in the list, explicitly add them all here.
// While building the chain using the returned AuthRoots we might
// leave the critical section and restart. After restarting may
// have a trusted root and won't redo this URL retrieval.
pChainEngine->UnlockEngine();
pCert = NULL; while (pCert = CertEnumCertificatesInStore(hIssuerUrlStore, pCert)) IPR_AddCertInAuthRootAutoUpdateCtl(pCert, pCtl);
pChainEngine->LockEngine(); if (pCallContext->IsTouchedEngine()) { fTouchedResult = FALSE; goto TouchedDuringAddOfAuthRoots; } }
*phIssuerUrlStore = hIssuerUrlStore; CommonReturn: if (rgpEntry) PkiFree(rgpEntry); if (pCtl) CertFreeCTLContext(pCtl);
return fTouchedResult; ErrorReturn: if (hIssuerUrlStore) CertCloseStore(hIssuerUrlStore, 0); goto CommonReturn;
TRACE_ERROR(NoAutoUpdateCtlEntry) TRACE_ERROR(OpenMemoryStoreError) TRACE_ERROR(TouchedDuringUrlRetrievalOfAuthRoots) TRACE_ERROR(NoAuthRootAutoUpdateCerts) TRACE_ERROR(TouchedDuringAddOfAuthRoots) }
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::RetrieveAuthRootAutoUpdateObjectByUrlW, public
//
// Synopsis: URL retrieves an AuthRoot Auto Update object. For wire
// retrieval, logs the event.
//
// 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.
//
// If the object was successfully retrieved,
// *ppvObject != NULL. Otherwise, *ppvObject = NULL.
//
// Only returns FALSE, if the engine was touched when
// leaving the critical section. *ppvObject may be != NULL
// when touched.
//
//----------------------------------------------------------------------------
BOOL CCertChainEngine::RetrieveAuthRootAutoUpdateObjectByUrlW( IN PCCHAINCALLCONTEXT pCallContext, IN DWORD dwSuccessEventID, IN DWORD dwFailEventID, IN LPCWSTR pwszUrl, IN LPCSTR pszObjectOid, IN DWORD dwRetrievalFlags, IN DWORD dwTimeout, // 0 => use default
OUT LPVOID* ppvObject, IN OPTIONAL PCRYPT_RETRIEVE_AUX_INFO pAuxInfo ) { BOOL fTouchedResult = TRUE; BOOL fResult;
*ppvObject = NULL; if (0 == dwTimeout) dwTimeout = pCallContext->ChainPara()->dwUrlRetrievalTimeout;
//
// We are about to go on the wire to retrieve the object.
// At this time we will release the chain engine lock so others can
// go about there business while we wait for the protocols to do the
// fetching.
//
UnlockEngine();
// Note, the windows update server doesn't require authentication.
// wininet sometimes calls us within a critical section. NO_AUTH
// normally will fix this deadlock.
//
// On 09-May-01 the above was fixed by wininet.
// Removed setting CRYPT_NO_AUTH_RETRIEVAL.
//
// Authentication may be required by a proxy.
fResult = ChainRetrieveObjectByUrlW( pwszUrl, pszObjectOid, dwRetrievalFlags, dwTimeout, ppvObject, NULL, // hAsyncRetrieve
NULL, // pCredentials
NULL, // pvVerify
pAuxInfo );
if (dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL) { // Only log wire retrievals
if (fResult) { LPCWSTR rgpwszStrings[1] = { pwszUrl };
IPR_LogCrypt32Event( EVENTLOG_INFORMATION_TYPE, dwSuccessEventID, 1, // wNumStrings
rgpwszStrings ); } else IPR_LogCrypt32Error( dwFailEventID, pwszUrl, GetLastError() ); }
LockEngine();
if (pCallContext->IsTouchedEngine()) { fTouchedResult = FALSE; goto TouchedDuringAuthRootObjectUrlRetrieval; }
if (fResult) assert(*ppvObject); else assert(NULL == *ppvObject);
CommonReturn: return fTouchedResult; ErrorReturn: goto CommonReturn;
SET_ERROR(TouchedDuringAuthRootObjectUrlRetrieval, ERROR_CAN_NOT_COMPLETE) }
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::GetAuthRootAutoUpdateCtl, public
//
// Synopsis: if auto update hasn't been disabled,
// returns the AuthRoot Auto Update CTL. Hits the wire
// if necessary to get a "fresh" CTL.
//
// Note, 2 URL fetches. One for the SequenceNumber file. The
// other for the CTL cab file. The SequenceNumber file
// is small and bounded in size. If it matches the SequenceNumber
// in an already retrieved CTL, then, no need to hit the
// wire to retrive the larger CTL file. This optimization will
// reduce the number of bytes needing to be fetched across the
// wire. The CTL won't be updated that often.
//
// 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.
//
// If auto update has been disabled, returns TRUE and
// *ppCtl = NULL.
//
// Only returns FALSE, if the engine was touched when
// leaving the critical section.
//
// The returned pCtl is AddRef'ed.
//
//----------------------------------------------------------------------------
BOOL CCertChainEngine::GetAuthRootAutoUpdateCtl( IN PCCHAINCALLCONTEXT pCallContext, OUT PCCTL_CONTEXT *ppCtl ) { BOOL fTouchedResult = TRUE; FILETIME CurrentTime; PAUTH_ROOT_AUTO_UPDATE_INFO pInfo; PCRYPT_BLOB_ARRAY pcbaSeq = NULL; PCRYPT_BLOB_ARRAY pcbaCab = NULL; PCCTL_CONTEXT pNewCtl = NULL; CRYPT_RETRIEVE_AUX_INFO RetrieveAuxInfo; DWORD i;
*ppCtl = NULL;
if ((pCallContext->CallOrEngineFlags() & CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE) || IPR_IsAuthRootAutoUpdateDisabled()) return TRUE;
if (NULL == (pInfo = m_pAuthRootAutoUpdateInfo)) { if (NULL == (pInfo = CreateAuthRootAutoUpdateInfo())) return TRUE; m_pAuthRootAutoUpdateInfo = pInfo; }
pCallContext->CurrentTime(&CurrentTime);
memset(&RetrieveAuxInfo, 0, sizeof(RetrieveAuxInfo)); RetrieveAuxInfo.cbSize = sizeof(RetrieveAuxInfo);
// First try the cache. If unable to retrieve the seq file or
// find a time valid CTL cab in the cache, hit the wire.
for (i = 0; i <= 1; i++) { BOOL fResult; DWORD dwRetrievalFlags; DWORD dwCtlTimeout = 0; PCRYPT_INTEGER_BLOB pSequenceNumber; FILETIME NewLastSyncTime; FILETIME CtlLastSyncTime; PCTL_INFO pNewCtlInfo;
if (pInfo->pCtl && 0 < CompareFileTime(&pInfo->NextSyncTime, &CurrentTime)) // We already have a time valid CTL
break;
if (0 == i) dwRetrievalFlags = CRYPT_CACHE_ONLY_RETRIEVAL; else { if (!pCallContext->IsOnline()) break; dwRetrievalFlags = CRYPT_WIRE_ONLY_RETRIEVAL; }
// First try to fetch the CTL's sequence number file
RetrieveAuxInfo.pLastSyncTime = &NewLastSyncTime; fTouchedResult = RetrieveAuthRootAutoUpdateObjectByUrlW( pCallContext, MSG_ROOT_SEQUENCE_NUMBER_AUTO_UPDATE_URL_RETRIEVAL_INFORMATIONAL, MSG_ROOT_SEQUENCE_NUMBER_AUTO_UPDATE_URL_RETRIEVAL_ERROR, pInfo->pwszSeqUrl, NULL, // pszObjectOid,
dwRetrievalFlags | CRYPT_OFFLINE_CHECK_RETRIEVAL | CRYPT_STICKY_CACHE_RETRIEVAL, 0, // dwTimeout (use default)
(LPVOID*) &pcbaSeq, &RetrieveAuxInfo ); if (!fTouchedResult) goto TouchedDuringAuthRootSeqUrlRetrieval;
pSequenceNumber = NULL; if (NULL == pcbaSeq) { // SequenceNumber retrieval failed
if (0 != i) // For wire retrieval failure, don't try to fetch the CTL
continue; } else if (0 > CompareFileTime(&NewLastSyncTime, &pInfo->LastSyncTime)) { // An older sync time
CryptMemFree(pcbaSeq); pcbaSeq = NULL; } else { // Extract the Sequence Number from the retrieved blob.
// Convert the ascii hex characters to binary. Overwrite
// the ascii hex with the converted bytes.
// Convert binary to little endian.
DWORD cchSeq; BOOL fUpperNibble = TRUE; DWORD cb = 0; DWORD j;
pSequenceNumber = pcbaSeq->rgBlob; if (0 == pcbaSeq->cBlob) cchSeq = 0; else cchSeq = pSequenceNumber->cbData;
for (j = 0; j < cchSeq; j++) { char ch = (char) pSequenceNumber->pbData[j]; BYTE b;
// only convert ascii hex characters 0..9, a..f, A..F
// silently ignore all others
if (ch >= '0' && ch <= '9') b = (BYTE) (ch - '0'); else if (ch >= 'a' && ch <= 'f') b = (BYTE) (10 + ch - 'a'); else if (ch >= 'A' && ch <= 'F') b = (BYTE) (10 + ch - 'A'); else continue; if (fUpperNibble) { pSequenceNumber->pbData[cb] = b << 4; fUpperNibble = FALSE; } else { pSequenceNumber->pbData[cb] |= b; cb++; fUpperNibble = TRUE; } }
if (0 == cb) { // Empty sequence number.
CryptMemFree(pcbaSeq); pcbaSeq = NULL; } else { pSequenceNumber->cbData = cb;
PkiAsn1ReverseBytes(pSequenceNumber->pbData, pSequenceNumber->cbData);
// Check if we already have a CTL corresponding to this
// fetched SequenceNumber
if (pInfo->pCtl) { PCTL_INFO pCtlInfo = pInfo->pCtl->pCtlInfo;
if (pCtlInfo->SequenceNumber.cbData == pSequenceNumber->cbData && 0 == memcmp(pCtlInfo->SequenceNumber.pbData, pSequenceNumber->pbData, pSequenceNumber->cbData)) { // Same CTL
pInfo->LastSyncTime = NewLastSyncTime; I_CryptIncrementFileTimeBySeconds( &pInfo->LastSyncTime, pInfo->dwSyncDeltaTime, &pInfo->NextSyncTime );
CryptMemFree(pcbaSeq); pcbaSeq = NULL; continue; } }
// The SequenceNumber consists of the FILETIME followed by
// an optional byte containing a hint for the CTL URL
// retrieval timeout (in seconds). If we are using the
// default retrieval timeout, use the hint if it exceeds
// the default timeout.
if (sizeof(FILETIME) < cb && pCallContext->HasDefaultUrlRetrievalTimeout()) { dwCtlTimeout = ((DWORD) pSequenceNumber->pbData[sizeof(FILETIME)]) * 1000; if (dwCtlTimeout < pCallContext->ChainPara()->dwUrlRetrievalTimeout) dwCtlTimeout = pCallContext->ChainPara()->dwUrlRetrievalTimeout; } } }
// After retrieving the sequence number file, now
// try to fetch the cab containing the CTL
RetrieveAuxInfo.pLastSyncTime = &CtlLastSyncTime; fTouchedResult = RetrieveAuthRootAutoUpdateObjectByUrlW( pCallContext, MSG_ROOT_LIST_AUTO_UPDATE_URL_RETRIEVAL_INFORMATIONAL, MSG_ROOT_LIST_AUTO_UPDATE_URL_RETRIEVAL_ERROR, pInfo->pwszCabUrl, NULL, // pszObjectOid,
dwRetrievalFlags | CRYPT_OFFLINE_CHECK_RETRIEVAL | CRYPT_STICKY_CACHE_RETRIEVAL, dwCtlTimeout, (LPVOID*) &pcbaCab, &RetrieveAuxInfo ); if (!fTouchedResult) goto TouchedDuringAuthRootCabUrlRetrieval;
if (NULL == pcbaCab) { // Cab Retrieval failed
if (pcbaSeq) { CryptMemFree(pcbaSeq); pcbaSeq = NULL; } continue; }
// Leave the engine to extract the CTL from the cab
UnlockEngine();
pNewCtl = ExtractAuthRootAutoUpdateCtlFromCab(pcbaCab); if (NULL == pNewCtl) IPR_LogCrypt32Error( MSG_ROOT_LIST_AUTO_UPDATE_EXTRACT_ERROR, pInfo->pwszCabUrl, GetLastError() );
CryptMemFree(pcbaCab); pcbaCab = NULL;
LockEngine();
if (pCallContext->IsTouchedEngine()) { fTouchedResult = FALSE; goto TouchedDuringExtractAuthRootCtl; }
if (NULL == pNewCtl) { // Ctl Extraction failed
if (pcbaSeq) { CryptMemFree(pcbaSeq); pcbaSeq = NULL; } continue; }
// If the SequenceNumber is the same as the one in the retrieved
// Ctl, then, use the lastest sync of the 2 URL fetches. Otherwise,
// use the Ctl sync time
pNewCtlInfo = pNewCtl->pCtlInfo; if (NULL == pcbaSeq || pNewCtlInfo->SequenceNumber.cbData != pSequenceNumber->cbData || 0 != memcmp(pNewCtlInfo->SequenceNumber.pbData, pSequenceNumber->pbData, pSequenceNumber->cbData) || 0 < CompareFileTime(&CtlLastSyncTime, &NewLastSyncTime)) NewLastSyncTime = CtlLastSyncTime;
// We are done with the SequenceNumber info
if (pcbaSeq) { CryptMemFree(pcbaSeq); pcbaSeq = NULL; }
if (0 >= CompareFileTime(&NewLastSyncTime, &pInfo->LastSyncTime)) { // Not a newer sync
CertFreeCTLContext(pNewCtl); pNewCtl = NULL; continue; } if (pInfo->pCtl && pInfo->pCtl->cbCtlEncoded == pNewCtl->cbCtlEncoded && 0 == memcmp(pInfo->pCtl->pbCtlEncoded, pNewCtl->pbCtlEncoded, pNewCtl->cbCtlEncoded)) { // Same CTL
pInfo->LastSyncTime = NewLastSyncTime; I_CryptIncrementFileTimeBySeconds( &pInfo->LastSyncTime, pInfo->dwSyncDeltaTime, &pInfo->NextSyncTime );
CertFreeCTLContext(pNewCtl); pNewCtl = NULL; continue; }
// Leave the engine to verify the CTL
UnlockEngine(); fResult = IRL_VerifyAuthRootAutoUpdateCtl(pNewCtl); if (!fResult) IPR_LogCrypt32Error( MSG_ROOT_LIST_AUTO_UPDATE_EXTRACT_ERROR, pInfo->pwszCabUrl, GetLastError() ); LockEngine();
if (fResult && 0 < CompareFileTime(&NewLastSyncTime, &pInfo->LastSyncTime)) { // Valid CTL that is newer
pInfo->LastSyncTime = NewLastSyncTime; I_CryptIncrementFileTimeBySeconds( &pInfo->LastSyncTime, pInfo->dwSyncDeltaTime, &pInfo->NextSyncTime );
FreeAuthRootAutoUpdateMatchCaches(pInfo->rghMatchCache); if (pInfo->pCtl) CertFreeCTLContext(pInfo->pCtl); pInfo->pCtl = pNewCtl; pNewCtl = NULL; }
if (pCallContext->IsTouchedEngine()) { fTouchedResult = FALSE; goto TouchedDuringVerifyAuthRootCtl; } }
if (pInfo->pCtl) *ppCtl = CertDuplicateCTLContext(pInfo->pCtl);
CommonReturn: if (pcbaSeq) CryptMemFree(pcbaSeq); if (pcbaCab) CryptMemFree(pcbaCab); if (pNewCtl) CertFreeCTLContext(pNewCtl); return fTouchedResult; ErrorReturn: goto CommonReturn;
TRACE_ERROR(TouchedDuringAuthRootSeqUrlRetrieval) TRACE_ERROR(TouchedDuringAuthRootCabUrlRetrieval) SET_ERROR(TouchedDuringExtractAuthRootCtl, ERROR_CAN_NOT_COMPLETE) SET_ERROR(TouchedDuringVerifyAuthRootCtl, ERROR_CAN_NOT_COMPLETE) }
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::FindAuthRootAutoUpdateMatchingCtlEntries, public
//
// Synopsis: If the CTL hash match cache doesn't exist its created.
// Iterates through the key and name hash cache entries.
// Returns matching entries. Removes duplicates.
//
// Assumption: Chain engine is locked once in the calling thread.
//
// The returned prgpCtlEntry must be PkiFree()'ed.
//
// Note, if the engine's pCtl is different then the passed in
// pCtl, the passed in pCtl is free'ed and updated with the
// engine's.
//
//----------------------------------------------------------------------------
VOID CCertChainEngine::FindAuthRootAutoUpdateMatchingCtlEntries( IN CRYPT_DATA_BLOB rgMatchHash[AUTH_ROOT_MATCH_CNT], IN OUT PCCTL_CONTEXT *ppCtl, OUT DWORD *pcCtlEntry, OUT PCTL_ENTRY **prgpCtlEntry ) { PAUTH_ROOT_AUTO_UPDATE_INFO pInfo; PCCTL_CONTEXT pCtl; DWORD cCtlEntry = 0; PCTL_ENTRY *rgpCtlEntry = NULL; DWORD i;
pInfo = m_pAuthRootAutoUpdateInfo; if (NULL == pInfo || NULL == pInfo->pCtl) goto InvalidCtl;
pCtl = *ppCtl; if (pCtl != pInfo->pCtl) { assert(pCtl); CertFreeCTLContext(pCtl); *ppCtl = pCtl = pInfo->pCtl; CertDuplicateCTLContext(pCtl); }
if (!CreateAuthRootAutoUpdateMatchCaches( pCtl, pInfo->rghMatchCache )) goto CreateMatchCachesError;
assert(pInfo->rghMatchCache[0]); assert(pInfo->rghMatchCache[AUTH_ROOT_MATCH_CNT - 1]);
// Loop through the exact, key and name match hashes and try to find an
// entry in the corresponding CTL match cache
for (i = 0; i < AUTH_ROOT_MATCH_CNT; i++) { HLRUENTRY hEntry;
if (0 == rgMatchHash[i].cbData) continue;
hEntry = I_CryptFindLruEntry(pInfo->rghMatchCache[i], &rgMatchHash[i]); while (NULL != hEntry) { PCTL_ENTRY pCtlEntry; PCTL_ENTRY *rgpNewCtlEntry; DWORD j;
pCtlEntry = (PCTL_ENTRY) I_CryptGetLruEntryData(hEntry); hEntry = I_CryptEnumMatchingLruEntries(hEntry);
assert(pCtlEntry); if (NULL == pCtlEntry) continue;
// Check if we already have this Ctl Entry
for (j = 0; j < cCtlEntry; j++) { if (pCtlEntry == rgpCtlEntry[j]) break; }
if (j < cCtlEntry) continue; if (NULL == (rgpNewCtlEntry = (PCTL_ENTRY *) PkiRealloc( rgpCtlEntry, (cCtlEntry + 1) * sizeof(PCTL_ENTRY)))) continue;
rgpCtlEntry = rgpNewCtlEntry; rgpCtlEntry[cCtlEntry++] = pCtlEntry; } }
CommonReturn: *pcCtlEntry = cCtlEntry; *prgpCtlEntry = rgpCtlEntry; return; ErrorReturn: goto CommonReturn;
TRACE_ERROR(InvalidCtl) TRACE_ERROR(CreateMatchCachesError) }
//+---------------------------------------------------------------------------
//
// Member: CCertChainEngine::GetAuthRootAutoUpdateCert, public
//
// Synopsis: URL retrieval of the AuthRoot from the Microsoft web
// server.
//
// 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.
//
// Only returns FALSE, if the engine was touched when
// leaving the critical section.
//
//----------------------------------------------------------------------------
BOOL CCertChainEngine::GetAuthRootAutoUpdateCert( IN PCCHAINCALLCONTEXT pCallContext, IN PCTL_ENTRY pCtlEntry, IN OUT HCERTSTORE hStore ) { BOOL fTouchedResult = TRUE; LPWSTR pwszCertUrl = NULL; HCERTSTORE hUrlStore = NULL;
assert(m_pAuthRootAutoUpdateInfo);
if (SHA1_HASH_LEN != pCtlEntry->SubjectIdentifier.cbData) goto InvalidCtlEntryError;
if (NULL == (pwszCertUrl = FormatAuthRootAutoUpdateCertUrl( pCtlEntry->SubjectIdentifier.pbData, m_pAuthRootAutoUpdateInfo ))) goto FormatCertUrlError;
fTouchedResult = RetrieveAuthRootAutoUpdateObjectByUrlW( pCallContext, MSG_ROOT_CERT_AUTO_UPDATE_URL_RETRIEVAL_INFORMATIONAL, MSG_ROOT_CERT_AUTO_UPDATE_URL_RETRIEVAL_ERROR, pwszCertUrl, CONTEXT_OID_CERTIFICATE, CRYPT_RETRIEVE_MULTIPLE_OBJECTS | CRYPT_LDAP_SCOPE_BASE_ONLY_RETRIEVAL | CRYPT_OFFLINE_CHECK_RETRIEVAL | CRYPT_WIRE_ONLY_RETRIEVAL | CRYPT_DONT_CACHE_RESULT, 0, // dwTimeout (use default)
(LPVOID *) &hUrlStore, NULL // pAuxInfo
); if (!fTouchedResult) goto TouchedDuringAuthRootCertUrlRetrieval;
if (hUrlStore) I_CertUpdateStore(hStore, hUrlStore, 0, NULL);
CommonReturn: PkiFree(pwszCertUrl); if (hUrlStore) CertCloseStore(hUrlStore, 0); return fTouchedResult; ErrorReturn: goto CommonReturn; SET_ERROR(InvalidCtlEntryError, ERROR_INVALID_DATA) TRACE_ERROR(FormatCertUrlError) TRACE_ERROR(TouchedDuringAuthRootCertUrlRetrieval) }
//+---------------------------------------------------------------------------
//
// Function: CreateAuthRootAutoUpdateInfo
//
// Synopsis: creates and initializes the AuthRoot Auto Update info
//
//----------------------------------------------------------------------------
PAUTH_ROOT_AUTO_UPDATE_INFO WINAPI CreateAuthRootAutoUpdateInfo() { HKEY hKey = NULL; PAUTH_ROOT_AUTO_UPDATE_INFO pInfo = NULL; DWORD cchDir; DWORD cchUrl;
if (NULL == (pInfo = (PAUTH_ROOT_AUTO_UPDATE_INFO) PkiZeroAlloc( sizeof(AUTH_ROOT_AUTO_UPDATE_INFO)))) goto OutOfMemory;
if (ERROR_SUCCESS != RegOpenKeyExU( HKEY_LOCAL_MACHINE, CERT_AUTH_ROOT_AUTO_UPDATE_LOCAL_MACHINE_REGPATH, 0, // dwReserved
KEY_READ, &hKey )) hKey = NULL;
if (hKey) { // Attempt to get values from registry
ILS_ReadDWORDValueFromRegistry( hKey, CERT_AUTH_ROOT_AUTO_UPDATE_SYNC_DELTA_TIME_VALUE_NAME, &pInfo->dwSyncDeltaTime );
ILS_ReadDWORDValueFromRegistry( hKey, CERT_AUTH_ROOT_AUTO_UPDATE_FLAGS_VALUE_NAME, &pInfo->dwFlags );
pInfo->pwszRootDirUrl = ILS_ReadSZValueFromRegistry( hKey, CERT_AUTH_ROOT_AUTO_UPDATE_ROOT_DIR_URL_VALUE_NAME ); if (pInfo->pwszRootDirUrl && L'\0' == *pInfo->pwszRootDirUrl) { PkiFree(pInfo->pwszRootDirUrl); pInfo->pwszRootDirUrl = NULL; } }
// If not defined in registry, use our defaults
if (0 == pInfo->dwSyncDeltaTime) pInfo->dwSyncDeltaTime = AUTH_ROOT_AUTO_UPDATE_SYNC_DELTA_TIME;
if (NULL == pInfo->pwszRootDirUrl) { if (NULL == (pInfo->pwszRootDirUrl = ILS_AllocAndCopyString( AUTH_ROOT_AUTO_UPDATE_ROOT_DIR_URL))) goto OutOfMemory; }
// Construct the CTL and Seq Urls
cchDir = wcslen(pInfo->pwszRootDirUrl);
cchUrl = cchDir + 1 + wcslen(CERT_AUTH_ROOT_CAB_FILENAME) + 1; if (NULL == (pInfo->pwszCabUrl = (LPWSTR) PkiNonzeroAlloc( sizeof(WCHAR) * cchUrl))) goto OutOfMemory; wcscpy(pInfo->pwszCabUrl, pInfo->pwszRootDirUrl); pInfo->pwszCabUrl[cchDir] = L'/'; wcscpy(pInfo->pwszCabUrl + cchDir + 1, CERT_AUTH_ROOT_CAB_FILENAME);
cchUrl = cchDir + 1 + wcslen(CERT_AUTH_ROOT_SEQ_FILENAME) + 1; if (NULL == (pInfo->pwszSeqUrl = (LPWSTR) PkiNonzeroAlloc( sizeof(WCHAR) * cchUrl))) goto OutOfMemory; wcscpy(pInfo->pwszSeqUrl, pInfo->pwszRootDirUrl); pInfo->pwszSeqUrl[cchDir] = L'/'; wcscpy(pInfo->pwszSeqUrl + cchDir + 1, CERT_AUTH_ROOT_SEQ_FILENAME);
CommonReturn: ILS_CloseRegistryKey(hKey); return pInfo;
ErrorReturn: FreeAuthRootAutoUpdateInfo(pInfo); pInfo = NULL; goto CommonReturn;
TRACE_ERROR(OutOfMemory) }
//+---------------------------------------------------------------------------
//
// Function: FreeAuthRootAutoUpdateInfo
//
// Synopsis: frees the AuthRoot Auto Update info
//
//----------------------------------------------------------------------------
VOID WINAPI FreeAuthRootAutoUpdateInfo( IN OUT PAUTH_ROOT_AUTO_UPDATE_INFO pInfo ) { if (NULL == pInfo) return;
PkiFree(pInfo->pwszRootDirUrl); PkiFree(pInfo->pwszCabUrl); PkiFree(pInfo->pwszSeqUrl);
FreeAuthRootAutoUpdateMatchCaches(pInfo->rghMatchCache);
if (pInfo->pCtl) CertFreeCTLContext(pInfo->pCtl);
PkiFree(pInfo); }
const LPCSTR rgpszAuthRootMatchOID[AUTH_ROOT_MATCH_CNT] = { szOID_CERT_KEY_IDENTIFIER_PROP_ID, szOID_CERT_SUBJECT_NAME_MD5_HASH_PROP_ID };
//+---------------------------------------------------------------------------
//
// Function: CreateAuthRootAutoUpdateMatchCaches
//
// Synopsis: if not already created, iterates through the CTL entries
// and creates key and name match caches entries from
// the associated entry hash attribute values.
//
//----------------------------------------------------------------------------
BOOL WINAPI CreateAuthRootAutoUpdateMatchCaches( IN PCCTL_CONTEXT pCtl, IN OUT HLRUCACHE rghMatchCache[AUTH_ROOT_MATCH_CNT] ) { BOOL fResult; LRU_CACHE_CONFIG Config; DWORD i; DWORD cEntry; PCTL_ENTRY pEntry;
if (NULL != rghMatchCache[0]) // Already created.
return TRUE;
memset( &Config, 0, sizeof( Config ) ); Config.dwFlags = LRU_CACHE_NO_SERIALIZE | LRU_CACHE_NO_COPY_IDENTIFIER; Config.pfnHash = CertObjectCacheHashMd5Identifier; Config.cBuckets = AUTH_ROOT_MATCH_CACHE_BUCKETS;
for (i = 0; i < AUTH_ROOT_MATCH_CNT; i++) { if (!I_CryptCreateLruCache(&Config, &rghMatchCache[i])) goto CreateLruCacheError; }
// Loop through the CTL entries and add the exact, key and name match
// hash cache entries
cEntry = pCtl->pCtlInfo->cCTLEntry; pEntry = pCtl->pCtlInfo->rgCTLEntry; for ( ; cEntry > 0; cEntry--, pEntry++) { DWORD cAttr; PCRYPT_ATTRIBUTE pAttr;
cAttr = pEntry->cAttribute; pAttr = pEntry->rgAttribute;
// Skip a remove entry
if (CertFindAttribute( szOID_REMOVE_CERTIFICATE, cAttr, pAttr )) continue;
for ( ; cAttr > 0; cAttr--, pAttr++) { for (i = 0; i < AUTH_ROOT_MATCH_CNT; i++) { if (0 == strcmp(rgpszAuthRootMatchOID[i], pAttr->pszObjId)) break; }
if (i < AUTH_ROOT_MATCH_CNT) { PCRYPT_ATTR_BLOB pValue; DWORD cbHash; const BYTE *pbHash; CRYPT_DATA_BLOB DataBlob; HLRUENTRY hEntry = NULL;
// Check that we have a single valued attribute encoded as an
// OCTET STRING
if (1 != pAttr->cValue) continue;
pValue = pAttr->rgValue; if (2 > pValue->cbData || ASN1UTIL_TAG_OCTETSTRING != pValue->pbData[0]) continue;
// Extract the hash bytes from the encoded OCTET STRING
if (0 >= Asn1UtilExtractContent( pValue->pbData, pValue->cbData, &cbHash, &pbHash ) || CMSG_INDEFINITE_LENGTH == cbHash || 0 == cbHash) continue;
DataBlob.cbData = cbHash; DataBlob.pbData = (BYTE *) pbHash; if (!I_CryptCreateLruEntry( rghMatchCache[i], &DataBlob, pEntry, &hEntry )) goto CreateLruEntryError; I_CryptInsertLruEntry(hEntry, NULL); I_CryptReleaseLruEntry(hEntry); } } }
fResult = TRUE; CommonReturn: return fResult;
ErrorReturn: FreeAuthRootAutoUpdateMatchCaches(rghMatchCache); fResult = FALSE; goto CommonReturn;
TRACE_ERROR(CreateLruCacheError) TRACE_ERROR(CreateLruEntryError) }
//+---------------------------------------------------------------------------
//
// Function: FreeAuthRootAutoUpdateMatchCaches
//
// Synopsis: frees the AuthRoot Auto Match Caches
//
//----------------------------------------------------------------------------
VOID WINAPI FreeAuthRootAutoUpdateMatchCaches( IN OUT HLRUCACHE rghMatchCache[AUTH_ROOT_MATCH_CNT] ) { DWORD i;
for (i = 0; i < AUTH_ROOT_MATCH_CNT; i++) { if (NULL != rghMatchCache[i]) { I_CryptFreeLruCache( rghMatchCache[i], LRU_SUPPRESS_REMOVAL_NOTIFICATION, NULL ); rghMatchCache[i] = NULL; } } }
//+---------------------------------------------------------------------------
//
// Function: FormatAuthRootAutoUpdateCertUrl
//
// Synopsis: allocates and formats the URL to retrieve the auth root cert
//
// returns "RootDir" "/" "AsciiHexHash" ".cer"
// for example,
// "http://www.xyz.com/roots/216B2A29E62A00CE820146D8244141B92511B279.cer"
//
//----------------------------------------------------------------------------
LPWSTR WINAPI FormatAuthRootAutoUpdateCertUrl( IN BYTE rgbSha1Hash[SHA1_HASH_LEN], IN PAUTH_ROOT_AUTO_UPDATE_INFO pInfo ) { LPWSTR pwszUrl; DWORD cchDir; DWORD cchUrl;
assert(pInfo->pwszRootDirUrl);
cchDir = wcslen(pInfo->pwszRootDirUrl);
cchUrl = cchDir + 1 + SHA1_HASH_NAME_LEN + wcslen(CERT_AUTH_ROOT_CERT_EXT) + 1;
if (NULL == (pwszUrl = (LPWSTR) PkiNonzeroAlloc(sizeof(WCHAR) * cchUrl))) return NULL;
wcscpy(pwszUrl, pInfo->pwszRootDirUrl); pwszUrl[cchDir] = L'/'; ILS_BytesToWStr(SHA1_HASH_LEN, rgbSha1Hash, pwszUrl + cchDir + 1); wcscpy(pwszUrl + cchDir + 1 + SHA1_HASH_NAME_LEN, CERT_AUTH_ROOT_CERT_EXT); return pwszUrl; }
// Known invalid roots
BYTE AuthRootInvalidList[][SHA1_HASH_LEN] = { // verisign "timestamp" - '97
{ 0xD4, 0x73, 0x5D, 0x8A, 0x9A, 0xE5, 0xBC, 0x4B, 0x0A, 0x0D, 0xC2, 0x70, 0xD6, 0xA6, 0x25, 0x38, 0xA5, 0x87, 0xD3, 0x2F },
// Root Agency (test root)
{ 0xFE, 0xE4, 0x49, 0xEE, 0x0E, 0x39, 0x65, 0xA5, 0x24, 0x6F, 0x00, 0x0E, 0x87, 0xFD, 0xE2, 0xA0, 0x65, 0xFD, 0x89, 0xD4 }, };
#define AUTH_ROOT_INVALID_LIST_CNT (sizeof(AuthRootInvalidList) / \
sizeof(AuthRootInvalidList[0]))
//+---------------------------------------------------------------------------
//
// Function: ChainGetAuthRootAutoUpdateStatus
//
// Synopsis: return status bits specifying if the root is
// trusted via the AuthRoot Auto Update CTL.
//
// Leaves the engine's critical section to URL retrieve and
// validate the CTL. Also leaves critical section to
// add the cert to the AuthRoot store via crypt32 service.
// 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.
//
// Only returns FALSE, if the engine was touched when
// leaving the critical section.
//
//----------------------------------------------------------------------------
BOOL WINAPI ChainGetAuthRootAutoUpdateStatus ( IN PCCHAINCALLCONTEXT pCallContext, IN PCCERTOBJECT pCertObject, IN OUT DWORD *pdwIssuerStatusFlags ) { BOOL fTouchedResult = TRUE; BOOL fResult; PCCERTCHAINENGINE pChainEngine = pCallContext->ChainEngine(); PCCERT_CONTEXT pCert = pCertObject->CertContext(); PCCTL_CONTEXT pCtl = NULL; PCTL_ENTRY pCtlEntry; PCERT_BASIC_CONSTRAINTS2_INFO pBasicConstraintsInfo;
DWORD i; DWORD cbData; BYTE rgbSha1Hash[SHA1_HASH_LEN];
// Check if the root has an end entity basic constraint. These can't
// be used for roots.
pBasicConstraintsInfo = pCertObject->BasicConstraintsInfo(); if (pBasicConstraintsInfo && !pBasicConstraintsInfo->fCA) return TRUE;
// Check if a known invalid root, such as, expired timestamp
// root or the "Root Agency" test root. Don't want all clients in the
// world hiting the wire for these guys.
cbData = SHA1_HASH_LEN; if (!CertGetCertificateContextProperty( pCert, CERT_SHA1_HASH_PROP_ID, rgbSha1Hash, &cbData ) || SHA1_HASH_LEN != cbData) goto GetSha1HashPropertyError;
for (i = 0; i < AUTH_ROOT_INVALID_LIST_CNT; i++) { if (0 == memcmp(AuthRootInvalidList[i], rgbSha1Hash, SHA1_HASH_LEN)) return TRUE; }
// Check if this certificate has an associated private key. Such
// certificates are generated by EFS.
cbData = 0; if (CertGetCertificateContextProperty( pCert, CERT_KEY_PROV_INFO_PROP_ID, NULL, // pbData
&cbData) && 0 < cbData) return TRUE;
fTouchedResult = pChainEngine->GetAuthRootAutoUpdateCtl( pCallContext, &pCtl );
if (!fTouchedResult || NULL == pCtl) {
#if 0
// This logs too many test failures
if (fTouchedResult) { PAUTH_ROOT_AUTO_UPDATE_INFO pInfo = pChainEngine->AuthRootAutoUpdateInfo();
if (NULL == pInfo || !(pInfo->dwFlags & CERT_AUTH_ROOT_AUTO_UPDATE_DISABLE_UNTRUSTED_ROOT_LOGGING_FLAG)) IPR_LogCertInformation( MSG_UNTRUSTED_ROOT_INFORMATIONAL, pCert, FALSE // fFormatIssuerName
); } #endif
return fTouchedResult; }
if (NULL == (pCtlEntry = CertFindSubjectInCTL( pCert->dwCertEncodingType, CTL_CERT_SUBJECT_TYPE, (void *) pCert, pCtl, 0 // dwFlags
))) {
#if 0
// This logs too many test failures
PAUTH_ROOT_AUTO_UPDATE_INFO pInfo = pChainEngine->AuthRootAutoUpdateInfo();
if (NULL == pInfo || !(pInfo->dwFlags & CERT_AUTH_ROOT_AUTO_UPDATE_DISABLE_UNTRUSTED_ROOT_LOGGING_FLAG)) IPR_LogCertInformation( MSG_UNTRUSTED_ROOT_INFORMATIONAL, pCert, FALSE // fFormatIssuerName
); #endif
goto CommonReturn; }
// Check if a remove entry
if (CertFindAttribute( szOID_REMOVE_CERTIFICATE, pCtlEntry->cAttribute, pCtlEntry->rgAttribute )) goto CommonReturn;
pChainEngine->UnlockEngine(); fResult = IPR_AddCertInAuthRootAutoUpdateCtl(pCert, pCtl); pChainEngine->LockEngine(); if (pCallContext->IsTouchedEngine()) { fTouchedResult = FALSE; goto TouchedDuringAddAuthRootInCtl; }
if (fResult && CertSetCertificateContextPropertiesFromCTLEntry( pCert, pCtlEntry, CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG )) *pdwIssuerStatusFlags |= CERT_ISSUER_TRUSTED_ROOT_FLAG;
CommonReturn: if (pCtl) CertFreeCTLContext(pCtl);
return fTouchedResult; ErrorReturn: goto CommonReturn;
TRACE_ERROR(GetSha1HashPropertyError) SET_ERROR(TouchedDuringAddAuthRootInCtl, ERROR_CAN_NOT_COMPLETE) }
|