//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1995 - 1999 // // File: fndchain.cpp // // Contents: Find Certificate Chain in Store API // // Functions: CertFindChainInStore // IFC_IsEndCertValidForUsage // // History: 28-Feb-98 philh created //-------------------------------------------------------------------------- #include "global.hxx" #include BOOL IFC_IsEndCertValidForUsages( IN PCCERT_CONTEXT pCert, IN PCERT_ENHKEY_USAGE pUsage, IN BOOL fOrUsage ) { BOOL fResult; int cNumOIDs; LPSTR *ppOIDs = NULL; DWORD cbOIDs; if (0 == pUsage->cUsageIdentifier) goto SuccessReturn; cbOIDs = 0; if (!CertGetValidUsages( 1, // cCerts &pCert, &cNumOIDs, NULL, // rghOIDs &cbOIDs )) goto CertGetValidUsagesError; if (-1 == cNumOIDs) // Cert doesn't have any EKU goto SuccessReturn; else if (0 == cNumOIDs) // Intersection of usages in properties and extensions is NONE goto NoMatch; assert(cbOIDs); if (NULL == (ppOIDs = (LPSTR *) PkiNonzeroAlloc(cbOIDs))) goto OutOfMemory; if (!CertGetValidUsages( 1, // cCerts &pCert, &cNumOIDs, ppOIDs, &cbOIDs )) goto CertGetValidUsagesError; if (0 >= cNumOIDs) // We had a change from the first call goto NoMatch; { DWORD cId1 = pUsage->cUsageIdentifier; LPSTR *ppszId1 = pUsage->rgpszUsageIdentifier; for ( ; cId1 > 0; cId1--, ppszId1++) { DWORD cId2 = cNumOIDs; LPSTR *ppszId2 = ppOIDs; for ( ; cId2 > 0; cId2--, ppszId2++) { if (0 == strcmp(*ppszId1, *ppszId2)) { if (fOrUsage) goto SuccessReturn; else break; } } if (!fOrUsage && 0 == cId2) goto NoMatch; } if (fOrUsage) // For the "OR" option we're here without any match goto NoMatch; // else // For the "AND" option we have matched all the specified // identifiers } SuccessReturn: fResult = TRUE; CommonReturn: PkiFree(ppOIDs); return fResult; NoMatch: ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(CertGetValidUsagesError) TRACE_ERROR(OutOfMemory) } BOOL IFC_IsEndCertValidForUsage( IN PCCERT_CONTEXT pCert, IN LPCSTR pszUsageIdentifier ) { CERT_ENHKEY_USAGE Usage = { 1, (LPSTR *) &pszUsageIdentifier}; return IFC_IsEndCertValidForUsages( pCert, &Usage, TRUE // fOrUsage ); } BOOL CompareChainIssuerNameBlobs( IN DWORD dwCertEncodingType, IN DWORD dwFindFlags, IN PCERT_CHAIN_FIND_BY_ISSUER_PARA pPara, IN OUT PCCERT_CHAIN_CONTEXT *ppChainContext ) { DWORD i; DWORD cIssuer = pPara->cIssuer; PCERT_NAME_BLOB pIssuer = pPara->rgIssuer; PCCERT_CHAIN_CONTEXT pChainContext = *ppChainContext; if (0 == cIssuer) return TRUE; for (i = 0; i < pChainContext->cChain; i++) { DWORD j; PCERT_SIMPLE_CHAIN pChain; if (0 < i && 0 == (dwFindFlags & CERT_CHAIN_FIND_BY_ISSUER_COMPLEX_CHAIN_FLAG)) break; pChain = pChainContext->rgpChain[i]; for (j = 0; j < pChain->cElement; j++) { DWORD k; PCCERT_CONTEXT pCert = pChain->rgpElement[j]->pCertContext; PCERT_NAME_BLOB pChainIssuer = &pCert->pCertInfo->Issuer; for (k = 0; k < cIssuer; k++) { if (CertCompareCertificateName( dwCertEncodingType, pChainIssuer, &pIssuer[k] )) { if (STRUCT_CBSIZE(CERT_CHAIN_FIND_BY_ISSUER_PARA, pdwIssuerElementIndex) <= pPara->cbSize) { if (pPara->pdwIssuerChainIndex) *pPara->pdwIssuerChainIndex = i; if (pPara->pdwIssuerElementIndex) *pPara->pdwIssuerElementIndex = j + 1; } return TRUE; } } } } // See if we have a match in any of the lower quality chains for (i = 0; i < pChainContext->cLowerQualityChainContext; i++) { PCCERT_CHAIN_CONTEXT pLowerQualityChainContext = pChainContext->rgpLowerQualityChainContext[i]; if (pLowerQualityChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_SIGNATURE_VALID) // Lower quality chains must at least have valid signatures continue; CertDuplicateCertificateChain(pLowerQualityChainContext); if (CompareChainIssuerNameBlobs( dwCertEncodingType, dwFindFlags, pPara, &pLowerQualityChainContext )) { // Replace the input chain context with the lower quality // chain context CertFreeCertificateChain(pChainContext); *ppChainContext = pLowerQualityChainContext; return TRUE; } else { assert(pLowerQualityChainContext == pChainContext->rgpLowerQualityChainContext[i]); CertFreeCertificateChain(pLowerQualityChainContext); } } return FALSE; } static DWORD GetChainKeyIdentifierPropId( IN PCCERT_CONTEXT pCert, IN DWORD dwKeySpec ) { DWORD dwPropId; PCRYPT_KEY_PROV_INFO pKeyProvInfo = NULL; DWORD cbKeyProvInfo; BYTE rgbKeyId[MAX_HASH_LEN]; DWORD cbKeyId; CRYPT_HASH_BLOB KeyIdentifier; cbKeyId = sizeof(rgbKeyId); if (!CertGetCertificateContextProperty( pCert, CERT_KEY_IDENTIFIER_PROP_ID, rgbKeyId, &cbKeyId )) return 0; KeyIdentifier.pbData = rgbKeyId; KeyIdentifier.cbData = cbKeyId; if (!CryptGetKeyIdentifierProperty( &KeyIdentifier, CERT_KEY_PROV_INFO_PROP_ID, CRYPT_KEYID_ALLOC_FLAG, NULL, // pwszComputerName NULL, // pvReserved (void *) &pKeyProvInfo, &cbKeyProvInfo )) { // Try again, searching LocalMachine if (!CryptGetKeyIdentifierProperty( &KeyIdentifier, CERT_KEY_PROV_INFO_PROP_ID, CRYPT_KEYID_ALLOC_FLAG | CRYPT_KEYID_MACHINE_FLAG, NULL, // pwszComputerName NULL, // pvReserved (void *) &pKeyProvInfo, &cbKeyProvInfo )) return 0; } if (dwKeySpec && dwKeySpec != pKeyProvInfo->dwKeySpec) dwPropId = 0; else dwPropId = CERT_KEY_PROV_INFO_PROP_ID; PkiDefaultCryptFree(pKeyProvInfo); return dwPropId; } DWORD GetChainPrivateKeyPropId( IN PCCERT_CONTEXT pCert, IN DWORD dwKeySpec ) { DWORD dwPropId; CERT_KEY_CONTEXT KeyContext; PCRYPT_KEY_PROV_INFO pKeyProvInfo = NULL; DWORD cbProp; cbProp = sizeof(KeyContext); if (CertGetCertificateContextProperty( pCert, CERT_KEY_CONTEXT_PROP_ID, &KeyContext, &cbProp )) { assert(sizeof(KeyContext) <= cbProp); if (dwKeySpec && dwKeySpec != KeyContext.dwKeySpec) return 0; else return CERT_KEY_CONTEXT_PROP_ID; } if (!CertGetCertificateContextProperty( pCert, CERT_KEY_PROV_INFO_PROP_ID, NULL, // pvData &cbProp )) return 0; if (NULL == (pKeyProvInfo = (PCRYPT_KEY_PROV_INFO) PkiNonzeroAlloc( cbProp))) goto OutOfMemory; if (!CertGetCertificateContextProperty( pCert, CERT_KEY_PROV_INFO_PROP_ID, pKeyProvInfo, &cbProp )) goto CertGetCertificateContextPropertyError; if (dwKeySpec && dwKeySpec != pKeyProvInfo->dwKeySpec) goto NoMatch; dwPropId = CERT_KEY_PROV_INFO_PROP_ID; CommonReturn: PkiFree(pKeyProvInfo); return dwPropId; NoMatch: ErrorReturn: dwPropId = 0; goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(CertGetCertificateContextPropertyError) } BOOL FindChainByIssuer( IN DWORD dwCertEncodingType, IN DWORD dwFindFlags, IN PCERT_CHAIN_FIND_BY_ISSUER_PARA pPara, IN PCCERT_CONTEXT pCert, OUT PCCERT_CHAIN_CONTEXT *ppChainContext ) { BOOL fResult = TRUE; PCCERT_CHAIN_CONTEXT pChainContext = NULL; DWORD dwPrivateKeyPropId; CERT_CHAIN_PARA ChainPara; DWORD dwCreateChainFlags; if (NULL == pPara || offsetof(CERT_CHAIN_FIND_BY_ISSUER_PARA, pvFindArg) > pPara->cbSize) { fResult = FALSE; goto InvalidArg; } if (dwFindFlags & CERT_CHAIN_FIND_BY_ISSUER_NO_KEY_FLAG) dwPrivateKeyPropId = CERT_KEY_CONTEXT_PROP_ID; else if (0 == (dwPrivateKeyPropId = GetChainPrivateKeyPropId( pCert, pPara->dwKeySpec ))) { if (0 == (dwPrivateKeyPropId = GetChainKeyIdentifierPropId( pCert, pPara->dwKeySpec ))) goto NoMatch; } if (pPara->pszUsageIdentifier) { if (!IFC_IsEndCertValidForUsage( pCert, pPara->pszUsageIdentifier )) goto NoMatch; } if (pPara->pfnFindCallback) { if (!pPara->pfnFindCallback( pCert, pPara->pvFindArg )) goto NoMatch; } memset(&ChainPara, 0, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); if (pPara->pszUsageIdentifier) { ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; ChainPara.RequestedUsage.Usage.cUsageIdentifier = 1; ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = (LPSTR *) &pPara->pszUsageIdentifier; } dwCreateChainFlags = 0; if (0 != pPara->cIssuer) { // For cross certs, might need to look at the lower quality chains dwCreateChainFlags |= CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS; } if (dwFindFlags & CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_URL_FLAG) dwCreateChainFlags |= CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL; if (dwFindFlags & CERT_CHAIN_FIND_BY_ISSUER_LOCAL_MACHINE_FLAG) dwCreateChainFlags |= CERT_CHAIN_USE_LOCAL_MACHINE_STORE; if (!CertGetCertificateChain( NULL, // hChainEngine pCert, NULL, // pTime dwFindFlags & CERT_CHAIN_FIND_BY_ISSUER_CACHE_ONLY_FLAG ? 0 : pCert->hCertStore, &ChainPara, dwCreateChainFlags, NULL, // pvReserved &pChainContext )) goto CertGetCertificateChainError; if (!CompareChainIssuerNameBlobs( dwCertEncodingType, dwFindFlags, pPara, &pChainContext )) goto NoMatch; if (dwFindFlags & CERT_CHAIN_FIND_BY_ISSUER_COMPARE_KEY_FLAG) { if (CERT_KEY_CONTEXT_PROP_ID != dwPrivateKeyPropId) { DWORD dwAcquireFlags = pPara->dwAcquirePrivateKeyFlags | CRYPT_ACQUIRE_COMPARE_KEY_FLAG; HCRYPTPROV hProv; BOOL fCallerFreeProv; if (!CryptAcquireCertificatePrivateKey( pCert, dwAcquireFlags, NULL, // pvReserved &hProv, NULL, // pdwKeySpec &fCallerFreeProv )) goto CryptAcquireCertificatePrivateKeyError; if (fCallerFreeProv) CryptReleaseContext(hProv, 0); } } CommonReturn: *ppChainContext = pChainContext; return fResult; NoMatch: ErrorReturn: if (pChainContext) { CertFreeCertificateChain(pChainContext); pChainContext = NULL; } goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(CertGetCertificateChainError) TRACE_ERROR(CryptAcquireCertificatePrivateKeyError) } PCCERT_CHAIN_CONTEXT WINAPI CertFindChainInStore( IN HCERTSTORE hCertStore, IN DWORD dwCertEncodingType, IN DWORD dwFindFlags, IN DWORD dwFindType, IN const void *pvFindPara, IN PCCERT_CHAIN_CONTEXT pPrevChainContext ) { PCCERT_CONTEXT pCert = NULL; PCCERT_CHAIN_CONTEXT pChainContext = NULL; if (0 == dwCertEncodingType) dwCertEncodingType = X509_ASN_ENCODING; if (pPrevChainContext) { if (pPrevChainContext->cChain) { PCERT_SIMPLE_CHAIN pChain = pPrevChainContext->rgpChain[0]; if (pChain->cElement) pCert = CertDuplicateCertificateContext( pChain->rgpElement[0]->pCertContext); } CertFreeCertificateChain(pPrevChainContext); } while (pCert = CertEnumCertificatesInStore(hCertStore, pCert)) { switch (dwFindType) { case CERT_CHAIN_FIND_BY_ISSUER: if (!FindChainByIssuer( dwCertEncodingType, dwFindFlags, (PCERT_CHAIN_FIND_BY_ISSUER_PARA) pvFindPara, pCert, &pChainContext )) goto FindChainByIssuerError; if (pChainContext) goto CommonReturn; break; default: goto InvalidArg; } } SetLastError((DWORD) CRYPT_E_NOT_FOUND); CommonReturn: if (pCert) CertFreeCertificateContext(pCert); return pChainContext; ErrorReturn: goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(FindChainByIssuerError) }