//+------------------------------------------------------------------------- // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: msrevoke.cpp // // Contents: CRL Distribution Points version of CertDllVerifyRevocation. // // Restrictions: // - Only support CRYPT_ASN_ENCODING // - Only processes certificates having the // szOID_CRL_DIST_POINTS extension. // - For szOID_CRL_DIST_POINTS extension: only URL FullName, // no ReasonFlags or CRLIssuer. // - URLs: http:, file: // - CRL must be issued and signed by the issuer of the // certificate // - CRL must not have any critical extensions // // Functions: DllMain // DllRegisterServer // DllUnregisterServer // CertDllVerifyRevocation // // History: 10-Apr-97 philh created // 01-Oct-97 kirtd major simplification, use // CryptGetTimeValidObject // //-------------------------------------------------------------------------- #include "global.hxx" #include #define MSREVOKE_TIMEOUT 15000 //+------------------------------------------------------------------------- // Default stores searched to find an issuer of the subject certificate //-------------------------------------------------------------------------- static struct { LPCWSTR pwszStore; DWORD dwFlags; } rgDefaultIssuerStores[] = { L"CA", CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT", CERT_SYSTEM_STORE_CURRENT_USER, L"SPC", CERT_SYSTEM_STORE_LOCAL_MACHINE }; #define NUM_DEFAULT_ISSUER_STORES (sizeof(rgDefaultIssuerStores) / \ sizeof(rgDefaultIssuerStores[0])) //+------------------------------------------------------------------------- // Local functions called by MicrosoftCertDllVerifyRevocation //-------------------------------------------------------------------------- PCCERT_CONTEXT GetIssuerCert( IN DWORD cCert, IN PCCERT_CONTEXT rgpCert[], IN DWORD dwFlags, IN PCERT_REVOCATION_PARA pRevPara ); BOOL HasUnsupportedCrlCriticalExtension( IN PCCRL_CONTEXT pCrl ); // msrevoke specific flags that can be passed to GetTimeValidCrl #define MSREVOKE_DONT_CHECK_TIME_VALIDITY_FLAG 0x1 #define MSREVOKE_DELTA_CRL_FLAG 0x2 BOOL GetTimeValidCrl ( IN LPCSTR pszTimeValidOid, IN LPVOID pvTimeValidPara, IN PCCERT_CONTEXT pSubject, IN PCCERT_CONTEXT pIssuer, IN PCERT_REVOCATION_PARA pRevPara, IN PCERT_EXTENSION pCDPExt, IN DWORD dwRevFlags, IN DWORD dwMsrevokeFlags, IN FILETIME *pftEndUrlRetrieval, OUT PCCRL_CONTEXT *ppCrl, IN OUT BOOL *pfWireRetrieval ); BOOL GetBaseCrl ( IN PCCERT_CONTEXT pSubject, IN PCCERT_CONTEXT pIssuer, IN PCERT_REVOCATION_PARA pRevPara, IN PCERT_EXTENSION pCDPExt, IN DWORD dwRevFlags, IN FILETIME *pftEndUrlRetrieval, OUT PCCRL_CONTEXT *ppBaseCrl, OUT BOOL *pfBaseCrlTimeValid, OUT BOOL *pfBaseWireRetrieval ); BOOL GetDeltaCrl ( IN PCCERT_CONTEXT pSubject, IN PCCERT_CONTEXT pIssuer, IN PCERT_REVOCATION_PARA pRevPara, IN DWORD dwRevFlags, IN BOOL fBaseWireRetrieval, IN FILETIME *pftEndUrlRetrieval, IN OUT PCCRL_CONTEXT *ppBaseCrl, IN OUT BOOL *pfCrlTimeValid, OUT PCCRL_CONTEXT *ppDeltaCrl, OUT DWORD *pdwFreshnessTime ); DWORD GetCrlReason( IN PCRL_ENTRY pCrlEntry ); BOOL CrlIssuerIsCertIssuer ( IN PCCERT_CONTEXT pCert, IN PCERT_EXTENSION pCrlDistPointExt ); //+------------------------------------------------------------------------- // External functions called by CertDllVerifyRevocation //-------------------------------------------------------------------------- BOOL WINAPI NetscapeCertDllVerifyRevocation( IN DWORD dwEncodingType, IN DWORD dwRevType, IN DWORD cContext, IN PVOID rgpvContext[], IN DWORD dwFlags, IN PVOID pvReserved, IN OUT PCERT_REVOCATION_STATUS pRevStatus ); //+------------------------------------------------------------------------- // MicrosoftCertDllVerifyRevocation using CRL Distribution Points extension. //-------------------------------------------------------------------------- BOOL WINAPI MicrosoftCertDllVerifyRevocation( IN DWORD dwEncodingType, IN DWORD dwRevType, IN DWORD cContext, IN PVOID rgpvContext[], IN DWORD dwFlags, IN PCERT_REVOCATION_PARA pRevPara, IN OUT PCERT_REVOCATION_STATUS pRevStatus ) { BOOL fResult; DWORD dwIndex = 0; DWORD dwError = (DWORD) CRYPT_E_NO_REVOCATION_CHECK; DWORD dwReason = 0; PCCERT_CONTEXT pCert; // not allocated PCCERT_CONTEXT pIssuer = NULL; PCCRL_CONTEXT pBaseCrl = NULL; PCCRL_CONTEXT pDeltaCrl = NULL; PCRL_ENTRY pCrlEntry = NULL; // not allocated BOOL fDeltaCrlEntry = FALSE; BOOL fCrlTimeValid = FALSE; BOOL fBaseWireRetrieval = FALSE; PCERT_EXTENSION pCDPExt; BOOL fSaveCheckFreshnessTime; DWORD dwFreshnessTime; CERT_REVOCATION_PARA RevPara; FILETIME ftCurrent; // Following is only used for CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG FILETIME ftEndUrlRetrieval; // Ensure we have a structure containing all the possible parameters memset(&RevPara, 0, sizeof(RevPara)); if (pRevPara != NULL) memcpy(&RevPara, pRevPara, min(pRevPara->cbSize, sizeof(RevPara))); RevPara.cbSize = sizeof(RevPara); if (0 == RevPara.dwUrlRetrievalTimeout) RevPara.dwUrlRetrievalTimeout = MSREVOKE_TIMEOUT; if (NULL == RevPara.pftCurrentTime) { GetSystemTimeAsFileTime(&ftCurrent); RevPara.pftCurrentTime = &ftCurrent; } pRevPara = &RevPara; if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG) { FILETIME ftStartUrlRetrieval; GetSystemTimeAsFileTime(&ftStartUrlRetrieval); I_CryptIncrementFileTimeByMilliseconds( &ftStartUrlRetrieval, pRevPara->dwUrlRetrievalTimeout, &ftEndUrlRetrieval); } if (cContext == 0) goto NoContextError; if (GET_CERT_ENCODING_TYPE(dwEncodingType) != CRYPT_ASN_ENCODING) goto NoRevocationCheckForEncodingTypeError; if (dwRevType != CERT_CONTEXT_REVOCATION_TYPE) goto NoRevocationCheckForRevTypeError; pCert = (PCCERT_CONTEXT) rgpvContext[0]; // Check if we have a CRL dist point pCDPExt = CertFindExtension( szOID_CRL_DIST_POINTS, pCert->pCertInfo->cExtension, pCert->pCertInfo->rgExtension ); // On 04-05-01 changed back to W2K semantics. Continue to check // if expired certificates are on the CRL. // If we have a CDP and an expired certificate, // then, the CA no longer maintains CRL information for the // certificate. We must consider it as being revoked. // if (NULL != pCDPExt && // 0 < CompareFileTime(RevPara.pftCurrentTime, // &pCert->pCertInfo->NotAfter)) { // dwReason = CRL_REASON_CESSATION_OF_OPERATION; // goto ExpiredCertError; // } // Get the certificate's issuer if (NULL == (pIssuer = GetIssuerCert( cContext, (PCCERT_CONTEXT *) rgpvContext, dwFlags, &RevPara ))) goto NoIssuerError; // Get the Base CRL for the subject certificate. // // Remember and disable the freshness retrieval option. fSaveCheckFreshnessTime = RevPara.fCheckFreshnessTime; RevPara.fCheckFreshnessTime = FALSE; if (!GetBaseCrl( pCert, pIssuer, &RevPara, pCDPExt, dwFlags, &ftEndUrlRetrieval, &pBaseCrl, &fCrlTimeValid, &fBaseWireRetrieval )) goto GetBaseCrlError; RevPara.fCheckFreshnessTime = fSaveCheckFreshnessTime; // If either the base crl or subject cert has a freshest, delta CRL, // get it if (!GetDeltaCrl( pCert, pIssuer, &RevPara, dwFlags, fBaseWireRetrieval, &ftEndUrlRetrieval, &pBaseCrl, &fCrlTimeValid, &pDeltaCrl, &dwFreshnessTime )) goto GetDeltaCrlError; if (NULL == pDeltaCrl) { dwFreshnessTime = I_CryptSubtractFileTimes( RevPara.pftCurrentTime, &pBaseCrl->pCrlInfo->ThisUpdate); if (RevPara.fCheckFreshnessTime) { if (RevPara.dwFreshnessTime >= dwFreshnessTime) fCrlTimeValid = TRUE; else { // Attempt to get a base CRL with better "freshness" PCCRL_CONTEXT pNewCrl; if (GetBaseCrl( pCert, pIssuer, &RevPara, pCDPExt, dwFlags, &ftEndUrlRetrieval, &pNewCrl, &fCrlTimeValid, &fBaseWireRetrieval )) { CertFreeCRLContext(pBaseCrl); pBaseCrl = pNewCrl; dwFreshnessTime = I_CryptSubtractFileTimes( RevPara.pftCurrentTime, &pBaseCrl->pCrlInfo->ThisUpdate); } else fCrlTimeValid = FALSE; } } } else { if (!CertFindCertificateInCRL( pCert, pDeltaCrl, 0, // dwFlags NULL, // pvReserved &pCrlEntry )) goto CertFindCertificateInDeltaCRLError; } if (pCrlEntry) { // Delta CRL entry dwReason = GetCrlReason(pCrlEntry); if (CRL_REASON_REMOVE_FROM_CRL != dwReason) fDeltaCrlEntry = TRUE; else { if (!CertFindCertificateInCRL( pCert, pBaseCrl, 0, // dwFlags NULL, // pvReserved &pCrlEntry )) goto CertFindCertificateInBaseCRLError; if (pCrlEntry) { dwReason = GetCrlReason(pCrlEntry); if (CRL_REASON_CERTIFICATE_HOLD == dwReason) pCrlEntry = NULL; } if (NULL == pCrlEntry) dwReason = 0; } } else { if (!CertFindCertificateInCRL( pCert, pBaseCrl, 0, // dwFlags NULL, // pvReserved &pCrlEntry )) goto CertFindCertificateInBaseCRLError; if (pCrlEntry) dwReason = GetCrlReason(pCrlEntry); } dwError = 0; if ( ( pCrlEntry != NULL ) && ( ( RevPara.pftTimeToUse == NULL ) || ( CompareFileTime( RevPara.pftTimeToUse, &pCrlEntry->RevocationDate ) >= 0 ) ) ) { dwError = (DWORD) CRYPT_E_REVOKED; } else if (!fCrlTimeValid) { dwError = (DWORD) CRYPT_E_REVOCATION_OFFLINE; } if (pRevStatus->cbSize >= (offsetof(CERT_REVOCATION_STATUS, dwFreshnessTime) + sizeof(pRevStatus->dwFreshnessTime))) { pRevStatus->fHasFreshnessTime = TRUE; pRevStatus->dwFreshnessTime = dwFreshnessTime; } if (RevPara.pCrlInfo) { PCERT_REVOCATION_CRL_INFO pInfo = RevPara.pCrlInfo; if (pInfo->cbSize >= sizeof(*pInfo)) { if (pInfo->pBaseCrlContext) CertFreeCRLContext(pInfo->pBaseCrlContext); pInfo->pBaseCrlContext = CertDuplicateCRLContext(pBaseCrl); if (pInfo->pDeltaCrlContext) { CertFreeCRLContext(pInfo->pDeltaCrlContext); pInfo->pDeltaCrlContext = NULL; } if (pDeltaCrl) pInfo->pDeltaCrlContext = CertDuplicateCRLContext(pDeltaCrl); pInfo->fDeltaCrlEntry = fDeltaCrlEntry; pInfo->pCrlEntry = pCrlEntry; } } CommonReturn: if (0 == dwError) { // Successfully checked that the certificate wasn't revoked if (1 < cContext) { dwIndex = 1; dwError = (DWORD) CRYPT_E_NO_REVOCATION_CHECK; fResult = FALSE; } else fResult = TRUE; } else fResult = FALSE; if (pIssuer) CertFreeCertificateContext(pIssuer); if (pBaseCrl) CertFreeCRLContext(pBaseCrl); if (pDeltaCrl) CertFreeCRLContext(pDeltaCrl); pRevStatus->dwIndex = dwIndex; pRevStatus->dwError = dwError; pRevStatus->dwReason = dwReason; SetLastError(dwError); return fResult; ErrorReturn: dwError = GetLastError(); if (0 == dwError) dwError = (DWORD) E_UNEXPECTED; goto CommonReturn; SET_ERROR(NoContextError, E_INVALIDARG) SET_ERROR(NoRevocationCheckForEncodingTypeError, CRYPT_E_NO_REVOCATION_CHECK) SET_ERROR(NoRevocationCheckForRevTypeError, CRYPT_E_NO_REVOCATION_CHECK) // SET_ERROR(ExpiredCertError, CRYPT_E_REVOKED) TRACE_ERROR(NoIssuerError) TRACE_ERROR(GetBaseCrlError) TRACE_ERROR(GetDeltaCrlError) TRACE_ERROR(CertFindCertificateInDeltaCRLError) TRACE_ERROR(CertFindCertificateInBaseCRLError) } //+--------------------------------------------------------------------------- // // Function: HasUnsupportedCrlCriticalExtension // // Synopsis: checks if the CRL has an unsupported critical section // //---------------------------------------------------------------------------- LPCSTR rgpszSupportedCrlExtensionOID[] = { szOID_DELTA_CRL_INDICATOR, szOID_ISSUING_DIST_POINT, szOID_FRESHEST_CRL, szOID_CRL_NUMBER, szOID_AUTHORITY_KEY_IDENTIFIER2, NULL }; BOOL IsSupportedCrlExtension( PCERT_EXTENSION pExt ) { LPSTR pszExtOID = pExt->pszObjId; LPCSTR *ppSupOID; for (ppSupOID = rgpszSupportedCrlExtensionOID; NULL != *ppSupOID; ppSupOID++) { if (0 == strcmp(pszExtOID, *ppSupOID)) return TRUE; } return FALSE; } BOOL HasUnsupportedCrlCriticalExtension( IN PCCRL_CONTEXT pCrl ) { PCRL_INFO pCrlInfo = pCrl->pCrlInfo; DWORD cExt = pCrlInfo->cExtension; PCERT_EXTENSION pExt = pCrlInfo->rgExtension; for ( ; 0 < cExt; cExt--, pExt++) { if (pExt->fCritical && !IsSupportedCrlExtension(pExt)) return TRUE; } return FALSE; } //+--------------------------------------------------------------------------- // // Function: GetTimeValidCrl // // Synopsis: get a time valid base or delta CRL // //---------------------------------------------------------------------------- BOOL GetTimeValidCrl ( IN LPCSTR pszTimeValidOid, IN LPVOID pvTimeValidPara, IN PCCERT_CONTEXT pSubject, IN PCCERT_CONTEXT pIssuer, IN PCERT_REVOCATION_PARA pRevPara, IN PCERT_EXTENSION pCDPExt, IN DWORD dwRevFlags, IN DWORD dwMsrevokeFlags, IN FILETIME *pftEndUrlRetrieval, OUT PCCRL_CONTEXT *ppCrl, IN OUT BOOL *pfWireRetrieval ) { BOOL fResult = FALSE; DWORD dwFlags = 0; FILETIME ftFreshness; LPFILETIME pftValidFor; if (pRevPara->fCheckFreshnessTime) { I_CryptDecrementFileTimeBySeconds( pRevPara->pftCurrentTime, pRevPara->dwFreshnessTime, &ftFreshness); pftValidFor = &ftFreshness; dwFlags |= CRYPT_CHECK_FRESHNESS_TIME_VALIDITY; } else { pftValidFor = pRevPara->pftCurrentTime; } if ( dwMsrevokeFlags & MSREVOKE_DONT_CHECK_TIME_VALIDITY_FLAG ) { dwFlags |= CRYPT_DONT_CHECK_TIME_VALIDITY; } if ( pCDPExt != NULL ) { fResult = CryptGetTimeValidObject( pszTimeValidOid, pvTimeValidPara, pIssuer, pftValidFor, dwFlags | CRYPT_CACHE_ONLY_RETRIEVAL, 0, // dwTimeout (LPVOID *)ppCrl, NULL, // pCredentials NULL // pvReserved ); } if ( fResult == FALSE ) { DWORD dwSaveErr = 0; HCERTSTORE hCrlStore = pRevPara->hCrlStore; *ppCrl = NULL; if ( hCrlStore != NULL ) { PCCRL_CONTEXT pFindCrl = NULL; DWORD dwFindFlags; CRL_FIND_ISSUED_FOR_PARA FindPara; dwSaveErr = GetLastError(); dwFindFlags = CRL_FIND_ISSUED_BY_AKI_FLAG | CRL_FIND_ISSUED_BY_SIGNATURE_FLAG; if (dwMsrevokeFlags & MSREVOKE_DELTA_CRL_FLAG) dwFindFlags |= CRL_FIND_ISSUED_BY_DELTA_FLAG; else dwFindFlags |= CRL_FIND_ISSUED_BY_BASE_FLAG; FindPara.pSubjectCert = pSubject; FindPara.pIssuerCert = pIssuer; while ((pFindCrl = CertFindCRLInStore( hCrlStore, pIssuer->dwCertEncodingType, dwFindFlags, CRL_FIND_ISSUED_FOR, (const void *) &FindPara, pFindCrl ))) { if (!CertIsValidCRLForCertificate( pSubject, pFindCrl, 0, // dwFlags NULL // pvReserved )) continue; if ( !(dwMsrevokeFlags & MSREVOKE_DONT_CHECK_TIME_VALIDITY_FLAG )) { if (pRevPara->fCheckFreshnessTime) { if (CompareFileTime(pftValidFor, &pFindCrl->pCrlInfo->ThisUpdate) > 0) { continue; } } else { if ( CompareFileTime( pftValidFor, &pFindCrl->pCrlInfo->NextUpdate ) > 0 && !I_CryptIsZeroFileTime( &pFindCrl->pCrlInfo->NextUpdate) ) { continue; } } } if ( NULL == *ppCrl ) { *ppCrl = CertDuplicateCRLContext(pFindCrl); } else { PCCRL_CONTEXT pPrevCrl = *ppCrl; // See if this CRL is newer if ( CompareFileTime( &pFindCrl->pCrlInfo->ThisUpdate, &pPrevCrl->pCrlInfo->ThisUpdate ) > 0 ) { CertFreeCRLContext(pPrevCrl); *ppCrl = CertDuplicateCRLContext(pFindCrl); } } } } if ( *ppCrl != NULL ) { return( TRUE ); } else if ( pCDPExt == NULL ) { SetLastError( (DWORD) CRYPT_E_NO_REVOCATION_CHECK ); return( FALSE ); } if ( !( dwRevFlags & CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION ) ) { if (dwRevFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG) { pRevPara->dwUrlRetrievalTimeout = I_CryptRemainingMilliseconds(pftEndUrlRetrieval); if (0 == pRevPara->dwUrlRetrievalTimeout) pRevPara->dwUrlRetrievalTimeout = 1; dwFlags |= CRYPT_ACCUMULATIVE_TIMEOUT; } fResult = CryptGetTimeValidObject( pszTimeValidOid, pvTimeValidPara, pIssuer, pftValidFor, dwFlags | CRYPT_WIRE_ONLY_RETRIEVAL, pRevPara->dwUrlRetrievalTimeout, (LPVOID *)ppCrl, NULL, // pCredentials NULL // pvReserved ); *pfWireRetrieval = TRUE; } else if ( hCrlStore != NULL ) { SetLastError( dwSaveErr ); } assert( pCDPExt ); if (!fResult) { if ( CRYPT_E_NOT_FOUND == GetLastError() ) { SetLastError( (DWORD) CRYPT_E_NO_REVOCATION_CHECK ); } else { SetLastError( (DWORD) CRYPT_E_REVOCATION_OFFLINE ); } } } return( fResult ); } //+--------------------------------------------------------------------------- // // Function: GetBaseCrl // // Synopsis: get the base CRL associated with the subject certificate // //---------------------------------------------------------------------------- BOOL GetBaseCrl ( IN PCCERT_CONTEXT pSubject, IN PCCERT_CONTEXT pIssuer, IN PCERT_REVOCATION_PARA pRevPara, IN PCERT_EXTENSION pCDPExt, IN DWORD dwRevFlags, IN FILETIME *pftEndUrlRetrieval, OUT PCCRL_CONTEXT *ppBaseCrl, OUT BOOL *pfBaseCrlTimeValid, OUT BOOL *pfBaseWireRetrieval ) { BOOL fResult; PCCRL_CONTEXT pBaseCrl = NULL; *pfBaseWireRetrieval = FALSE; if (GetTimeValidCrl( TIME_VALID_OID_GET_CRL_FROM_CERT, (LPVOID) pSubject, pSubject, pIssuer, pRevPara, pCDPExt, dwRevFlags, 0, // dwMsrevokeFlags pftEndUrlRetrieval, &pBaseCrl, pfBaseWireRetrieval )) { *pfBaseCrlTimeValid = TRUE; } else { *pfBaseCrlTimeValid = FALSE; if (!GetTimeValidCrl( TIME_VALID_OID_GET_CRL_FROM_CERT, (LPVOID) pSubject, pSubject, pIssuer, pRevPara, pCDPExt, dwRevFlags, MSREVOKE_DONT_CHECK_TIME_VALIDITY_FLAG, pftEndUrlRetrieval, &pBaseCrl, pfBaseWireRetrieval )) { goto GetTimeInvalidCrlError; } } if (HasUnsupportedCrlCriticalExtension(pBaseCrl)) goto HasUnsupportedCriticalExtensionError; fResult = TRUE; CommonReturn: *ppBaseCrl = pBaseCrl; return fResult; ErrorReturn: if (pBaseCrl) { CertFreeCRLContext(pBaseCrl); pBaseCrl = NULL; } fResult = FALSE; goto CommonReturn; TRACE_ERROR(GetTimeInvalidCrlError) SET_ERROR(HasUnsupportedCriticalExtensionError, CRYPT_E_NO_REVOCATION_CHECK) } //+--------------------------------------------------------------------------- // // Function: GetDeltaCrl // // Synopsis: get the delta CRL associated with the subject certificate and // its base CRL // // For now, always return TRUE. If not able to find a delta CRL, set // *pfCrlTimeValid to FALSE. // //---------------------------------------------------------------------------- BOOL GetDeltaCrl ( IN PCCERT_CONTEXT pSubject, IN PCCERT_CONTEXT pIssuer, IN PCERT_REVOCATION_PARA pRevPara, IN DWORD dwRevFlags, IN BOOL fBaseWireRetrieval, IN FILETIME *pftEndUrlRetrieval, IN OUT PCCRL_CONTEXT *ppBaseCrl, IN OUT BOOL *pfCrlTimeValid, OUT PCCRL_CONTEXT *ppDeltaCrl, OUT DWORD *pdwFreshnessTime ) { PCERT_EXTENSION pFreshestExt; PCCRL_CONTEXT pBaseCrl = *ppBaseCrl; PCCRL_CONTEXT pDeltaCrl = NULL; LPCSTR pszTimeValidOid; LPVOID pvTimeValidPara; CERT_CRL_CONTEXT_PAIR CertCrlPair; BOOL fDeltaWireRetrieval; PCERT_EXTENSION pBaseCrlNumberExt; PCERT_EXTENSION pDeltaCrlIndicatorExt; int iBaseCrlNumber = 0; int iDeltaCrlIndicator = 0; DWORD cbInt; LPFILETIME pFreshnessThisUpdate; assert(pBaseCrl); // Check if the base CRL or the subject certificate has a freshest // ext if (pFreshestExt = CertFindExtension( szOID_FRESHEST_CRL, pBaseCrl->pCrlInfo->cExtension, pBaseCrl->pCrlInfo->rgExtension )) { pszTimeValidOid = TIME_VALID_OID_GET_FRESHEST_CRL_FROM_CRL; CertCrlPair.pCertContext = pSubject; CertCrlPair.pCrlContext = pBaseCrl; pvTimeValidPara = (LPVOID) &CertCrlPair; } else if (pFreshestExt = CertFindExtension( szOID_FRESHEST_CRL, pSubject->pCertInfo->cExtension, pSubject->pCertInfo->rgExtension )) { pszTimeValidOid = TIME_VALID_OID_GET_FRESHEST_CRL_FROM_CERT; pvTimeValidPara = (LPVOID) pSubject; } else { goto NoDeltaCrlReturn; } if (GetTimeValidCrl( pszTimeValidOid, pvTimeValidPara, pSubject, pIssuer, pRevPara, pFreshestExt, dwRevFlags, MSREVOKE_DELTA_CRL_FLAG, pftEndUrlRetrieval, &pDeltaCrl, &fDeltaWireRetrieval )) { *pfCrlTimeValid = TRUE; } else { *pfCrlTimeValid = FALSE; if (!GetTimeValidCrl( pszTimeValidOid, pvTimeValidPara, pSubject, pIssuer, pRevPara, pFreshestExt, dwRevFlags, MSREVOKE_DELTA_CRL_FLAG | MSREVOKE_DONT_CHECK_TIME_VALIDITY_FLAG, pftEndUrlRetrieval, &pDeltaCrl, &fDeltaWireRetrieval )) { goto GetTimeInvalidCrlError; } } if (HasUnsupportedCrlCriticalExtension(pDeltaCrl)) goto HasUnsupportedCriticalExtensionError; // Check that the base CRL number >= delta CRL indicator if (NULL == (pBaseCrlNumberExt = CertFindExtension( szOID_CRL_NUMBER, pBaseCrl->pCrlInfo->cExtension, pBaseCrl->pCrlInfo->rgExtension ))) goto MissingBaseCrlNumberError; if (NULL == (pDeltaCrlIndicatorExt = CertFindExtension( szOID_DELTA_CRL_INDICATOR, pDeltaCrl->pCrlInfo->cExtension, pDeltaCrl->pCrlInfo->rgExtension ))) goto MissingDeltaCrlIndicatorError; cbInt = sizeof(iBaseCrlNumber); if (!CryptDecodeObject( pBaseCrl->dwCertEncodingType, X509_INTEGER, pBaseCrlNumberExt->Value.pbData, pBaseCrlNumberExt->Value.cbData, 0, // dwFlags &iBaseCrlNumber, &cbInt )) goto DecodeBaseCrlNumberError; cbInt = sizeof(iDeltaCrlIndicator); if (!CryptDecodeObject( pDeltaCrl->dwCertEncodingType, X509_INTEGER, pDeltaCrlIndicatorExt->Value.pbData, pDeltaCrlIndicatorExt->Value.cbData, 0, // dwFlags &iDeltaCrlIndicator, &cbInt )) goto DecodeDeltaCrlIndicatorError; pFreshnessThisUpdate = &pDeltaCrl->pCrlInfo->ThisUpdate; if (iBaseCrlNumber < iDeltaCrlIndicator) { BOOL fValidBaseCrl = FALSE; if (!fBaseWireRetrieval && 0 == (dwRevFlags & CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION)) { // Attempt to get a more recent base CRL by hitting the wire PCCRL_CONTEXT pWireBaseCrl = NULL; CRL_IS_VALID_EXTRA_INFO CrlIsValidExtraInfo = { iDeltaCrlIndicator }; if (CryptGetTimeValidObject( TIME_VALID_OID_GET_CRL_FROM_CERT, (LPVOID)pSubject, pIssuer, NULL, // pftValidFor CRYPT_WIRE_ONLY_RETRIEVAL | CRYPT_DONT_CHECK_TIME_VALIDITY, pRevPara->dwUrlRetrievalTimeout, (LPVOID *) &pWireBaseCrl, NULL, // pCredentials &CrlIsValidExtraInfo )) { // Already checked that the new Base CRL number is valid CertFreeCRLContext(pBaseCrl); *ppBaseCrl = pBaseCrl = pWireBaseCrl; fValidBaseCrl = TRUE; } } if (!fValidBaseCrl) { *pfCrlTimeValid = FALSE; pFreshnessThisUpdate = &pBaseCrl->pCrlInfo->ThisUpdate; } } *pdwFreshnessTime = I_CryptSubtractFileTimes( pRevPara->pftCurrentTime, pFreshnessThisUpdate); if (pRevPara->fCheckFreshnessTime) { if (pRevPara->dwFreshnessTime >= *pdwFreshnessTime) *pfCrlTimeValid = TRUE; else *pfCrlTimeValid = FALSE; } NoDeltaCrlReturn: CommonReturn: *ppDeltaCrl = pDeltaCrl; return TRUE; ErrorReturn: if (pDeltaCrl) { CertFreeCRLContext(pDeltaCrl); pDeltaCrl = NULL; } *pfCrlTimeValid = FALSE; goto CommonReturn; TRACE_ERROR(GetTimeInvalidCrlError) SET_ERROR(HasUnsupportedCriticalExtensionError, CRYPT_E_NO_REVOCATION_CHECK) SET_ERROR(MissingBaseCrlNumberError, CRYPT_E_NO_REVOCATION_CHECK) SET_ERROR(MissingDeltaCrlIndicatorError, CRYPT_E_NO_REVOCATION_CHECK) TRACE_ERROR(DecodeBaseCrlNumberError) TRACE_ERROR(DecodeDeltaCrlIndicatorError) } //+------------------------------------------------------------------------- // If the CRL entry has a CRL Reason extension, the enumerated reason // code is returned. Otherwise, a reason code of 0 is returned. //-------------------------------------------------------------------------- DWORD GetCrlReason( IN PCRL_ENTRY pCrlEntry ) { DWORD dwReason = 0; PCERT_EXTENSION pExt; // Check if the certificate has a szOID_CRL_REASON_CODE extension if (pExt = CertFindExtension( szOID_CRL_REASON_CODE, pCrlEntry->cExtension, pCrlEntry->rgExtension )) { DWORD cbInfo = sizeof(dwReason); CryptDecodeObject( CRYPT_ASN_ENCODING, X509_CRL_REASON_CODE, pExt->Value.pbData, pExt->Value.cbData, 0, // dwFlags &dwReason, &cbInfo); } return dwReason; } //+========================================================================= // Get Issuer Certificate Functions //========================================================================== PCCERT_CONTEXT FindIssuerCertInStores( IN PCCERT_CONTEXT pSubjectCert, IN DWORD cStore, IN HCERTSTORE rgStore[] ) { PCCERT_CONTEXT pIssuerCert = NULL; DWORD i; for (i = 0; i < cStore; i++) { while (TRUE) { DWORD dwFlags = CERT_STORE_SIGNATURE_FLAG; pIssuerCert = CertGetIssuerCertificateFromStore( rgStore[i], pSubjectCert, pIssuerCert, &dwFlags); if (NULL == pIssuerCert) { if (CRYPT_E_SELF_SIGNED == GetLastError()) { if (0 == (dwFlags & CERT_STORE_SIGNATURE_FLAG)) pIssuerCert = CertDuplicateCertificateContext( pSubjectCert); return pIssuerCert; } break; } else if (0 == (dwFlags & CERT_STORE_SIGNATURE_FLAG)) return pIssuerCert; } } return NULL; } static PCCERT_CONTEXT FindIssuerCertInDefaultStores( IN PCCERT_CONTEXT pSubjectCert ) { PCCERT_CONTEXT pIssuerCert; HCERTSTORE hStore; DWORD i; for (i = 0; i < NUM_DEFAULT_ISSUER_STORES; i++) { if (hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, 0, // dwEncodingType 0, // hCryptProv rgDefaultIssuerStores[i].dwFlags | CERT_STORE_READONLY_FLAG, (const void *) rgDefaultIssuerStores[i].pwszStore )) { pIssuerCert = FindIssuerCertInStores(pSubjectCert, 1, &hStore); CertCloseStore(hStore, 0); if (pIssuerCert) return pIssuerCert; } } return NULL; } //+------------------------------------------------------------------------- // Get the issuer of the first certificate in the array // // Note, pRevPara is our copy and guaranteed to contain all the fields. //-------------------------------------------------------------------------- PCCERT_CONTEXT GetIssuerCert( IN DWORD cCert, IN PCCERT_CONTEXT rgpCert[], IN DWORD dwFlags, IN PCERT_REVOCATION_PARA pRevPara ) { PCCERT_CONTEXT pSubjectCert; PCCERT_CONTEXT pIssuerCert = NULL; assert(cCert >= 1); pSubjectCert = rgpCert[0]; if (cCert == 1) { pIssuerCert = pRevPara->pIssuerCert; if (NULL == pIssuerCert && CertCompareCertificateName( pSubjectCert->dwCertEncodingType, &pSubjectCert->pCertInfo->Subject, &pSubjectCert->pCertInfo->Issuer)) // Self issued pIssuerCert = pSubjectCert; } else if (dwFlags & CERT_VERIFY_REV_CHAIN_FLAG) pIssuerCert = rgpCert[1]; if (pIssuerCert) pIssuerCert = CertDuplicateCertificateContext(pIssuerCert); else { pIssuerCert = FindIssuerCertInStores( pSubjectCert, pRevPara->cCertStore, pRevPara->rgCertStore); if (NULL == pIssuerCert) pIssuerCert = FindIssuerCertInDefaultStores(pSubjectCert); } if (NULL == pIssuerCert) SetLastError((DWORD) CRYPT_E_REVOCATION_OFFLINE); return pIssuerCert; } //+--------------------------------------------------------------------------- // // Function: CrlIssuerIsCertIssuer // // Synopsis: is the issuer of the CRL the issuer of the cert // //---------------------------------------------------------------------------- BOOL CrlIssuerIsCertIssuer ( IN PCCERT_CONTEXT pCert, IN PCERT_EXTENSION pCrlDistPointExt ) { BOOL fResult = FALSE; DWORD cb = sizeof( PCRL_DIST_POINTS_INFO ); PCRL_DIST_POINTS_INFO pDistPointInfo; if ( pCrlDistPointExt == NULL ) { return( TRUE ); } if ( CryptDecodeObjectEx( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, szOID_CRL_DIST_POINTS, pCrlDistPointExt->Value.pbData, pCrlDistPointExt->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, (void *)&pDistPointInfo, &cb ) == TRUE ) { if ( pDistPointInfo->cDistPoint > 0 ) { if ( pDistPointInfo->rgDistPoint[ 0 ].CRLIssuer.cAltEntry == 0 ) { fResult = TRUE; } else if ( pDistPointInfo->rgDistPoint[ 0 ].CRLIssuer.rgAltEntry[ 0 ].dwAltNameChoice == CERT_ALT_NAME_DIRECTORY_NAME ) { if ( pDistPointInfo->rgDistPoint[ 0 ].CRLIssuer.rgAltEntry[ 0 ].DirectoryName.cbData == 0 ) { fResult = TRUE; } else if ( ( pDistPointInfo->rgDistPoint[ 0 ].CRLIssuer.rgAltEntry[ 0 ].DirectoryName.cbData == pCert->pCertInfo->Issuer.cbData ) && ( memcmp( pDistPointInfo->rgDistPoint[ 0 ].CRLIssuer.rgAltEntry[ 0 ].DirectoryName.pbData, pCert->pCertInfo->Issuer.pbData, pCert->pCertInfo->Issuer.cbData ) == 0 ) ) { fResult = TRUE; } } } else { fResult = TRUE; } LocalFree( pDistPointInfo ); } return( fResult ); } //+--------------------------------------------------------------------------- // // Function: CertDllVerifyRevocation // // Synopsis: Dispatches to msrevoke // //---------------------------------------------------------------------------- BOOL WINAPI CertDllVerifyRevocation( IN DWORD dwEncodingType, IN DWORD dwRevType, IN DWORD cContext, IN PVOID rgpvContext[], IN DWORD dwFlags, IN PCERT_REVOCATION_PARA pRevPara, IN OUT PCERT_REVOCATION_STATUS pRevStatus ) { return MicrosoftCertDllVerifyRevocation( dwEncodingType, dwRevType, cContext, rgpvContext, dwFlags, pRevPara, pRevStatus ); }