/* * FUNCTION: PKIX_RevocationChecker_Check */ PKIX_Error * PKIX_RevocationChecker_Check( PKIX_PL_Cert *cert, PKIX_PL_Cert *issuer, PKIX_RevocationChecker *revChecker, PKIX_ProcessingParams *procParams, PKIX_Boolean chainVerificationState, PKIX_Boolean testingLeafCert, PKIX_RevocationStatus *pRevStatus, PKIX_UInt32 *pReasonCode, void **pNbioContext, void *plContext) { PKIX_RevocationStatus overallStatus = PKIX_RevStatus_NoInfo; PKIX_RevocationStatus methodStatus[PKIX_RevocationMethod_MAX]; PKIX_Boolean onlyUseRemoteMethods = PKIX_FALSE; PKIX_UInt32 revFlags = 0; PKIX_List *revList = NULL; PKIX_PL_Date *date = NULL; pkix_RevocationMethod *method = NULL; void *nbioContext; int tries; PKIX_ENTER(REVOCATIONCHECKER, "PKIX_RevocationChecker_Check"); PKIX_NULLCHECK_TWO(revChecker, procParams); nbioContext = *pNbioContext; *pNbioContext = NULL; if (testingLeafCert) { revList = revChecker->leafMethodList; revFlags = revChecker->leafMethodListFlags; } else { revList = revChecker->chainMethodList; revFlags = revChecker->chainMethodListFlags; } if (!revList) { /* Return NoInfo status */ goto cleanup; } PORT_Memset(methodStatus, PKIX_RevStatus_NoInfo, sizeof(PKIX_RevocationStatus) * PKIX_RevocationMethod_MAX); date = procParams->date; /* Need to have two loops if we testing all local info first: * first we are going to test all local(cached) info * second, all remote info(fetching) */ for (tries = 0;tries < 2;tries++) { int methodNum = 0; for (;methodNum < revList->length;methodNum++) { PKIX_UInt32 methodFlags = 0; PKIX_DECREF(method); PKIX_CHECK( PKIX_List_GetItem(revList, methodNum, (PKIX_PL_Object**)&method, plContext), PKIX_LISTGETITEMFAILED); methodFlags = method->flags; if (!(methodFlags & PKIX_REV_M_TEST_USING_THIS_METHOD)) { /* Will not check with this method. Skipping... */ continue; } if (!onlyUseRemoteMethods && methodStatus[methodNum] == PKIX_RevStatus_NoInfo) { PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; PKIX_CHECK_NO_GOTO( (*method->localRevChecker)(cert, issuer, date, method, procParams, methodFlags, chainVerificationState, &revStatus, pReasonCode, plContext), PKIX_REVCHECKERCHECKFAILED); methodStatus[methodNum] = revStatus; if (revStatus == PKIX_RevStatus_Revoked) { /* if error was generated use it as final error. */ overallStatus = PKIX_RevStatus_Revoked; goto cleanup; } if (pkixErrorResult) { /* Disregard errors. Only returned revStatus matters. */ PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult, plContext); pkixErrorResult = NULL; } } if ((!(revFlags & PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST) || onlyUseRemoteMethods) && chainVerificationState && methodStatus[methodNum] == PKIX_RevStatus_NoInfo) { if (!(methodFlags & PKIX_REV_M_FORBID_NETWORK_FETCHING)) { PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; PKIX_CHECK_NO_GOTO( (*method->externalRevChecker)(cert, issuer, date, method, procParams, methodFlags, &revStatus, pReasonCode, &nbioContext, plContext), PKIX_REVCHECKERCHECKFAILED); methodStatus[methodNum] = revStatus; if (revStatus == PKIX_RevStatus_Revoked) { /* if error was generated use it as final error. */ overallStatus = PKIX_RevStatus_Revoked; goto cleanup; } if (pkixErrorResult) { /* Disregard errors. Only returned revStatus matters. */ PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult, plContext); pkixErrorResult = NULL; } } else if (methodFlags & PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO) { /* Info is not in the local cache. Network fetching is not * allowed. If need to fail on missing fresh info for the * the method, then we should fail right here.*/ overallStatus = PKIX_RevStatus_Revoked; goto cleanup; } } /* If success and we should not check the next method, then * return a success. */ if (methodStatus[methodNum] == PKIX_RevStatus_Success && !(methodFlags & PKIX_REV_M_CONTINUE_TESTING_ON_FRESH_INFO)) { overallStatus = PKIX_RevStatus_Success; goto cleanup; } } /* inner loop */ if (!onlyUseRemoteMethods && revFlags & PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST && chainVerificationState) { onlyUseRemoteMethods = PKIX_TRUE; continue; } break; } /* outer loop */ if (overallStatus == PKIX_RevStatus_NoInfo && chainVerificationState) { /* The following check makes sence only for chain * validation step, sinse we do not fetch info while * in the process of finding trusted anchor. * For chain building step it is enough to know, that * the cert was not directly revoked by any of the * methods. */ /* Still have no info. But one of the method could * have returned success status(possible if CONTINUE * TESTING ON FRESH INFO flag was used). * If any of the methods have returned Success status, * the overallStatus should be success. */ int methodNum = 0; for (;methodNum < PKIX_RevocationMethod_MAX;methodNum++) { if (methodStatus[methodNum] == PKIX_RevStatus_Success) { overallStatus = PKIX_RevStatus_Success; goto cleanup; } } if (revFlags & PKIX_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE) { overallStatus = PKIX_RevStatus_Revoked; } } cleanup: *pRevStatus = overallStatus; PKIX_DECREF(method); PKIX_RETURN(REVOCATIONCHECKER); }
/* * The OCSPChecker is created in an idle state, and remains in this state until * either (a) the default Responder has been set and enabled, and a Check * request is received with no responder specified, or (b) a Check request is * received with a specified responder. A request message is constructed and * given to the HttpClient. When a response is received it is decoded and the * results provided to the caller. * * During the most recent enhancement of this function, it has been found that * it doesn't correctly implement non-blocking I/O. * * The nbioContext is used in two places, for "response-obtaining" and * for "response-verification". * * However, if this function gets called to resume, it always * repeats the "request creation" and "response fetching" steps! * As a result, the earlier operation is never resumed. */ PKIX_Error * pkix_OcspChecker_CheckExternal( PKIX_PL_Cert *cert, PKIX_PL_Cert *issuer, PKIX_PL_Date *date, pkix_RevocationMethod *checkerObject, PKIX_ProcessingParams *procParams, PKIX_UInt32 methodFlags, PKIX_RevocationStatus *pRevStatus, CERTCRLEntryReasonCode *pReasonCode, void **pNBIOContext, void *plContext) { SECErrorCodes resultCode = SEC_ERROR_REVOKED_CERTIFICATE_OCSP; PKIX_Boolean uriFound = PKIX_FALSE; PKIX_Boolean passed = PKIX_TRUE; pkix_OcspChecker *checker = NULL; PKIX_PL_OcspCertID *cid = NULL; PKIX_PL_OcspRequest *request = NULL; PKIX_PL_OcspResponse *response = NULL; PKIX_PL_Date *validity = NULL; PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; void *nbioContext = NULL; enum { stageGET, stagePOST } currentStage; PRBool retry = PR_FALSE; PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_CheckExternal"); PKIX_CHECK( pkix_CheckType((PKIX_PL_Object*)checkerObject, PKIX_OCSPCHECKER_TYPE, plContext), PKIX_OBJECTNOTOCSPCHECKER); checker = (pkix_OcspChecker *)checkerObject; PKIX_CHECK( PKIX_PL_OcspCertID_Create(cert, NULL, &cid, plContext), PKIX_OCSPCERTIDCREATEFAILED); /* create request */ PKIX_CHECK( pkix_pl_OcspRequest_Create(cert, cid, validity, NULL, methodFlags, &uriFound, &request, plContext), PKIX_OCSPREQUESTCREATEFAILED); if (uriFound == PKIX_FALSE) { /* no caching for certs lacking URI */ resultCode = 0; goto cleanup; } if (methodFlags & CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP) { /* Do not try HTTP GET, only HTTP POST */ currentStage = stagePOST; } else { /* Try HTTP GET first, falling back to POST */ currentStage = stageGET; } do { const char *method; passed = PKIX_TRUE; retry = PR_FALSE; if (currentStage == stageGET) { method = "GET"; } else { PORT_Assert(currentStage == stagePOST); method = "POST"; } /* send request and create a response object */ PKIX_CHECK_NO_GOTO( pkix_pl_OcspResponse_Create(request, method, NULL, checker->certVerifyFcn, &nbioContext, &response, plContext), PKIX_OCSPRESPONSECREATEFAILED); if (pkixErrorResult) { passed = PKIX_FALSE; } if (passed && nbioContext != 0) { *pNBIOContext = nbioContext; goto cleanup; } if (passed){ PKIX_CHECK_NO_GOTO( pkix_pl_OcspResponse_Decode(response, &passed, &resultCode, plContext), PKIX_OCSPRESPONSEDECODEFAILED); if (pkixErrorResult) { passed = PKIX_FALSE; } } if (passed){ PKIX_CHECK_NO_GOTO( pkix_pl_OcspResponse_GetStatus(response, &passed, &resultCode, plContext), PKIX_OCSPRESPONSEGETSTATUSRETURNEDANERROR); if (pkixErrorResult) { passed = PKIX_FALSE; } } if (passed){ PKIX_CHECK_NO_GOTO( pkix_pl_OcspResponse_VerifySignature(response, cert, procParams, &passed, &nbioContext, plContext), PKIX_OCSPRESPONSEVERIFYSIGNATUREFAILED); if (pkixErrorResult) { passed = PKIX_FALSE; } else { if (nbioContext != 0) { *pNBIOContext = nbioContext; goto cleanup; } } } if (!passed && currentStage == stagePOST) { /* We won't retry a POST failure, so it's final. * Because the following block with its call to * pkix_pl_OcspResponse_GetStatusForCert * will take care of caching good or bad state, * but we only execute that next block if there hasn't * been a failure yet, we must cache the POST * failure now. */ if (cid && cid->certID) { /* Caching MIGHT consume the cid. */ PKIX_Error *err; err = PKIX_PL_OcspCertID_RememberOCSPProcessingFailure( cid, plContext); if (err) { PKIX_PL_Object_DecRef((PKIX_PL_Object*)err, plContext); } } } if (passed){ PKIX_Boolean allowCachingOfFailures = (currentStage == stagePOST) ? PKIX_TRUE : PKIX_FALSE; PKIX_CHECK_NO_GOTO( pkix_pl_OcspResponse_GetStatusForCert(cid, response, allowCachingOfFailures, date, &passed, &resultCode, plContext), PKIX_OCSPRESPONSEGETSTATUSFORCERTFAILED); if (pkixErrorResult) { passed = PKIX_FALSE; } else if (passed == PKIX_FALSE) { revStatus = pkix_OcspChecker_MapResultCodeToRevStatus(resultCode); } else { revStatus = PKIX_RevStatus_Success; } } if (currentStage == stageGET && revStatus != PKIX_RevStatus_Success && revStatus != PKIX_RevStatus_Revoked) { /* we'll retry */ PKIX_DECREF(response); retry = PR_TRUE; currentStage = stagePOST; revStatus = PKIX_RevStatus_NoInfo; if (pkixErrorResult) { PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult, plContext); pkixErrorResult = NULL; } } } while (retry); cleanup: if (revStatus == PKIX_RevStatus_NoInfo && (uriFound || methodFlags & PKIX_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE) && methodFlags & PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO) { revStatus = PKIX_RevStatus_Revoked; } *pRevStatus = revStatus; /* ocsp carries only three statuses: good, bad, and unknown. * revStatus is used to pass them. reasonCode is always set * to be unknown. */ *pReasonCode = crlEntryReasonUnspecified; PKIX_DECREF(cid); PKIX_DECREF(request); PKIX_DECREF(response); PKIX_RETURN(OCSPCHECKER); }