//+------------------------------------------------------------------------- // Microsoft Windows // // Copyright (C) Microsoft Corporation, 2001 - 2001 // // File: mscrlrev.cpp // // Contents: Check CRLs in CA store version of CertDllVerifyRevocation. // // Restrictions: // - Only support CRYPT_ASN_ENCODING // - CRL must already be in the CA system store // - 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: 15-Mar-01 philh created //-------------------------------------------------------------------------- #include "global.hxx" #include //+------------------------------------------------------------------------- // Default stores searched to find an issuer of the subject certificate //-------------------------------------------------------------------------- struct { LPCWSTR pwszStore; DWORD dwFlags; } rgDefaultIssuerStores[] = { L"CA", CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT", CERT_SYSTEM_STORE_CURRENT_USER }; #define NUM_DEFAULT_ISSUER_STORES (sizeof(rgDefaultIssuerStores) / \ sizeof(rgDefaultIssuerStores[0])) //+------------------------------------------------------------------------- // Dll initialization //-------------------------------------------------------------------------- BOOL WINAPI DllMain( HMODULE hInst, ULONG ulReason, LPVOID lpReserved) { switch (ulReason) { case DLL_PROCESS_ATTACH: break; case DLL_PROCESS_DETACH: case DLL_THREAD_DETACH: default: break; } return TRUE; } HRESULT HError() { DWORD dw = GetLastError(); HRESULT hr; if ( dw <= 0xFFFF ) hr = HRESULT_FROM_WIN32 ( dw ); else hr = dw; if ( ! FAILED ( hr ) ) { // somebody failed a call without properly setting an error condition hr = E_UNEXPECTED; } return hr; } //+------------------------------------------------------------------------- // DllRegisterServer //-------------------------------------------------------------------------- STDAPI DllRegisterServer(void) { if (!CryptRegisterDefaultOIDFunction( X509_ASN_ENCODING, CRYPT_OID_VERIFY_REVOCATION_FUNC, CRYPT_REGISTER_FIRST_INDEX, L"mscrlrev.dll" )) { if (ERROR_FILE_EXISTS != GetLastError()) return HError(); } return S_OK; } //+------------------------------------------------------------------------- // DllUnregisterServer //-------------------------------------------------------------------------- STDAPI DllUnregisterServer(void) { if (!CryptUnregisterDefaultOIDFunction( X509_ASN_ENCODING, CRYPT_OID_VERIFY_REVOCATION_FUNC, L"mscrlrev.dll" )) { if (ERROR_FILE_NOT_FOUND != GetLastError()) return HError(); } return S_OK; } //+------------------------------------------------------------------------- // Local functions called by CertDllVerifyRevocation //-------------------------------------------------------------------------- PCCERT_CONTEXT GetIssuerCert( IN DWORD cCert, IN PCCERT_CONTEXT rgpCert[], IN DWORD dwFlags, IN PCERT_REVOCATION_PARA pRevPara ); BOOL GetSubjectCrl ( IN PCCERT_CONTEXT pSubject, IN PCCERT_CONTEXT pIssuer, OUT PCCRL_CONTEXT* ppCrl ); PCRL_ENTRY FindCertInCrl( IN PCCERT_CONTEXT pCert, IN PCCRL_CONTEXT pCrl, IN PCERT_REVOCATION_PARA pRevPara ); DWORD GetCrlReason( IN PCRL_ENTRY pCrlEntry ); //+------------------------------------------------------------------------- // CertDllVerifyRevocation using pre-loaded CRLs in the CA store //-------------------------------------------------------------------------- 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 ) { DWORD dwError = (DWORD) CRYPT_E_NO_REVOCATION_CHECK; DWORD dwReason = 0; PCCERT_CONTEXT pCert; // not allocated PCCERT_CONTEXT pIssuer = NULL; PCCRL_CONTEXT pCrl = NULL; PCRL_ENTRY pCrlEntry; 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]; // Get the certificate's issuer if (NULL == (pIssuer = GetIssuerCert( cContext, (PCCERT_CONTEXT *) rgpvContext, dwFlags, pRevPara ))) goto NoIssuerError; if (!GetSubjectCrl( pCert, pIssuer, &pCrl )) goto NoCrl; // Check if revoked pCrlEntry = FindCertInCrl(pCert, pCrl, pRevPara); if (pCrlEntry) { dwError = (DWORD) CRYPT_E_REVOKED; dwReason = GetCrlReason(pCrlEntry); goto Revoked; } CommonReturn: if (pIssuer) CertFreeCertificateContext(pIssuer); if (pCrl) CertFreeCRLContext(pCrl); pRevStatus->dwIndex = 0; pRevStatus->dwError = dwError; pRevStatus->dwReason = dwReason; SetLastError(dwError); return FALSE; ErrorReturn: goto CommonReturn; TRACE_ERROR(NoContextError) TRACE_ERROR(NoRevocationCheckForEncodingTypeError) TRACE_ERROR(NoRevocationCheckForRevTypeError) TRACE_ERROR(NoIssuerError) TRACE_ERROR(NoCrl) TRACE_ERROR(Revoked) } //+------------------------------------------------------------------------- // 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) break; else if (0 == (dwFlags & CERT_STORE_SIGNATURE_FLAG)) return pIssuerCert; } } return NULL; } 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 //-------------------------------------------------------------------------- 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) { if (pRevPara && pRevPara->cbSize >= (offsetof(CERT_REVOCATION_PARA, pIssuerCert) + sizeof(pRevPara->pIssuerCert))) 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 { if (pRevPara && pRevPara->cbSize >= (offsetof(CERT_REVOCATION_PARA, rgCertStore) + sizeof(pRevPara->rgCertStore))) pIssuerCert = FindIssuerCertInStores( pSubjectCert, pRevPara->cCertStore, pRevPara->rgCertStore); if (NULL == pIssuerCert) pIssuerCert = FindIssuerCertInDefaultStores(pSubjectCert); } if (NULL == pIssuerCert) SetLastError(CRYPT_E_NO_REVOCATION_CHECK); return pIssuerCert; } //+------------------------------------------------------------------------- // Check that the CRL doesn't have any critical extensions //-------------------------------------------------------------------------- BOOL IsExtensionValidCrl( IN PCCRL_CONTEXT pCrl ) { DWORD cExt = pCrl->pCrlInfo->cExtension; PCERT_EXTENSION pExt = pCrl->pCrlInfo->rgExtension; for ( ; cExt > 0; cExt--, pExt++) { if (pExt->fCritical) return FALSE; } return TRUE; } //+--------------------------------------------------------------------------- // // Function: GetSubjectCrl // // Synopsis: get the CRL associated with the subject certificate // //---------------------------------------------------------------------------- BOOL GetSubjectCrl ( IN PCCERT_CONTEXT pSubject, IN PCCERT_CONTEXT pIssuer, OUT PCCRL_CONTEXT* ppCrl ) { BOOL fResult; HCERTSTORE hStore; PCCRL_CONTEXT pFindCrl = NULL; DWORD dwGetCrlFlags = CERT_STORE_SIGNATURE_FLAG; *ppCrl = NULL; hStore = CertOpenSystemStoreW( NULL, L"CA" ); if ( hStore != NULL ) { while ( ( pFindCrl = CertGetCRLFromStore( hStore, pIssuer, pFindCrl, &dwGetCrlFlags ) ) != NULL ) { if ( dwGetCrlFlags != 0 || !IsExtensionValidCrl( pFindCrl )) { dwGetCrlFlags = CERT_STORE_SIGNATURE_FLAG; continue; } *ppCrl = pFindCrl; break; } CertCloseStore( hStore, 0 ); if ( *ppCrl != NULL ) { return( TRUE ); } } return( FALSE ); } //+------------------------------------------------------------------------- // Find a certificate identified by its serial number in the CRL. //-------------------------------------------------------------------------- PCRL_ENTRY FindCertInCrl( IN PCCERT_CONTEXT pCert, IN PCCRL_CONTEXT pCrl, IN PCERT_REVOCATION_PARA pRevPara ) { DWORD cEntry = pCrl->pCrlInfo->cCRLEntry; PCRL_ENTRY pEntry = pCrl->pCrlInfo->rgCRLEntry; DWORD cbSerialNumber = pCert->pCertInfo->SerialNumber.cbData; BYTE *pbSerialNumber = pCert->pCertInfo->SerialNumber.pbData; if (0 == cbSerialNumber) return NULL; for ( ; 0 < cEntry; cEntry--, pEntry++) { if (cbSerialNumber == pEntry->SerialNumber.cbData && 0 == memcmp(pbSerialNumber, pEntry->SerialNumber.pbData, cbSerialNumber)) { if (pRevPara && pRevPara->cbSize >= (offsetof(CERT_REVOCATION_PARA, pftTimeToUse) + sizeof(pRevPara->pftTimeToUse)) && NULL != pRevPara->pftTimeToUse && 0 > CompareFileTime(pRevPara->pftTimeToUse, &pEntry->RevocationDate)) // It was used before being revoked return NULL; else return pEntry; } } return NULL; }