michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: /* michael@0: * pkix_revocationchecker.c michael@0: * michael@0: * RevocationChecker Object Functions michael@0: * michael@0: */ michael@0: michael@0: #include "pkix_revocationchecker.h" michael@0: #include "pkix_tools.h" michael@0: michael@0: /* --Private-Functions-------------------------------------------- */ michael@0: michael@0: /* michael@0: * FUNCTION: pkix_RevocationChecker_Destroy michael@0: * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) michael@0: */ michael@0: static PKIX_Error * michael@0: pkix_RevocationChecker_Destroy( michael@0: PKIX_PL_Object *object, michael@0: void *plContext) michael@0: { michael@0: PKIX_RevocationChecker *checker = NULL; michael@0: michael@0: PKIX_ENTER(REVOCATIONCHECKER, "pkix_RevocationChecker_Destroy"); michael@0: PKIX_NULLCHECK_ONE(object); michael@0: michael@0: /* Check that this object is a revocation checker */ michael@0: PKIX_CHECK(pkix_CheckType michael@0: (object, PKIX_REVOCATIONCHECKER_TYPE, plContext), michael@0: PKIX_OBJECTNOTREVOCATIONCHECKER); michael@0: michael@0: checker = (PKIX_RevocationChecker *)object; michael@0: michael@0: PKIX_DECREF(checker->leafMethodList); michael@0: PKIX_DECREF(checker->chainMethodList); michael@0: michael@0: cleanup: michael@0: michael@0: PKIX_RETURN(REVOCATIONCHECKER); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_RevocationChecker_Duplicate michael@0: * (see comments for PKIX_PL_DuplicateCallback in pkix_pl_system.h) michael@0: */ michael@0: static PKIX_Error * michael@0: pkix_RevocationChecker_Duplicate( michael@0: PKIX_PL_Object *object, michael@0: PKIX_PL_Object **pNewObject, michael@0: void *plContext) michael@0: { michael@0: PKIX_RevocationChecker *checker = NULL; michael@0: PKIX_RevocationChecker *checkerDuplicate = NULL; michael@0: PKIX_List *dupLeafList = NULL; michael@0: PKIX_List *dupChainList = NULL; michael@0: michael@0: PKIX_ENTER(REVOCATIONCHECKER, "pkix_RevocationChecker_Duplicate"); michael@0: PKIX_NULLCHECK_TWO(object, pNewObject); michael@0: michael@0: PKIX_CHECK(pkix_CheckType michael@0: (object, PKIX_REVOCATIONCHECKER_TYPE, plContext), michael@0: PKIX_OBJECTNOTCERTCHAINCHECKER); michael@0: michael@0: checker = (PKIX_RevocationChecker *)object; michael@0: michael@0: if (checker->leafMethodList){ michael@0: PKIX_CHECK(PKIX_PL_Object_Duplicate michael@0: ((PKIX_PL_Object *)checker->leafMethodList, michael@0: (PKIX_PL_Object **)&dupLeafList, michael@0: plContext), michael@0: PKIX_OBJECTDUPLICATEFAILED); michael@0: } michael@0: if (checker->chainMethodList){ michael@0: PKIX_CHECK(PKIX_PL_Object_Duplicate michael@0: ((PKIX_PL_Object *)checker->chainMethodList, michael@0: (PKIX_PL_Object **)&dupChainList, michael@0: plContext), michael@0: PKIX_OBJECTDUPLICATEFAILED); michael@0: } michael@0: michael@0: PKIX_CHECK( michael@0: PKIX_RevocationChecker_Create(checker->leafMethodListFlags, michael@0: checker->chainMethodListFlags, michael@0: &checkerDuplicate, michael@0: plContext), michael@0: PKIX_REVOCATIONCHECKERCREATEFAILED); michael@0: michael@0: checkerDuplicate->leafMethodList = dupLeafList; michael@0: checkerDuplicate->chainMethodList = dupChainList; michael@0: dupLeafList = NULL; michael@0: dupChainList = NULL; michael@0: michael@0: *pNewObject = (PKIX_PL_Object *)checkerDuplicate; michael@0: michael@0: cleanup: michael@0: PKIX_DECREF(dupLeafList); michael@0: PKIX_DECREF(dupChainList); michael@0: michael@0: PKIX_RETURN(REVOCATIONCHECKER); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: pkix_RevocationChecker_RegisterSelf michael@0: * DESCRIPTION: michael@0: * Registers PKIX_REVOCATIONCHECKER_TYPE and its related functions with michael@0: * systemClasses[] michael@0: * THREAD SAFETY: michael@0: * Not Thread Safe - for performance and complexity reasons michael@0: * michael@0: * Since this function is only called by PKIX_PL_Initialize, which should michael@0: * only be called once, it is acceptable that this function is not michael@0: * thread-safe. michael@0: */ michael@0: PKIX_Error * michael@0: pkix_RevocationChecker_RegisterSelf(void *plContext) michael@0: { michael@0: extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; michael@0: pkix_ClassTable_Entry entry; michael@0: michael@0: PKIX_ENTER(REVOCATIONCHECKER, "pkix_RevocationChecker_RegisterSelf"); michael@0: michael@0: entry.description = "RevocationChecker"; michael@0: entry.objCounter = 0; michael@0: entry.typeObjectSize = sizeof(PKIX_RevocationChecker); michael@0: entry.destructor = pkix_RevocationChecker_Destroy; michael@0: entry.equalsFunction = NULL; michael@0: entry.hashcodeFunction = NULL; michael@0: entry.toStringFunction = NULL; michael@0: entry.comparator = NULL; michael@0: entry.duplicateFunction = pkix_RevocationChecker_Duplicate; michael@0: michael@0: systemClasses[PKIX_REVOCATIONCHECKER_TYPE] = entry; michael@0: michael@0: PKIX_RETURN(REVOCATIONCHECKER); michael@0: } michael@0: michael@0: /* Sort methods by theirs priorities */ michael@0: static PKIX_Error * michael@0: pkix_RevocationChecker_SortComparator( michael@0: PKIX_PL_Object *obj1, michael@0: PKIX_PL_Object *obj2, michael@0: PKIX_Int32 *pResult, michael@0: void *plContext) michael@0: { michael@0: pkix_RevocationMethod *method1 = NULL, *method2 = NULL; michael@0: michael@0: PKIX_ENTER(BUILD, "pkix_RevocationChecker_SortComparator"); michael@0: michael@0: method1 = (pkix_RevocationMethod *)obj1; michael@0: method2 = (pkix_RevocationMethod *)obj2; michael@0: michael@0: *pResult = (method1->priority > method2->priority); michael@0: michael@0: PKIX_RETURN(BUILD); michael@0: } michael@0: michael@0: michael@0: /* --Public-Functions--------------------------------------------- */ michael@0: michael@0: michael@0: /* michael@0: * FUNCTION: PKIX_RevocationChecker_Create (see comments in pkix_revchecker.h) michael@0: */ michael@0: PKIX_Error * michael@0: PKIX_RevocationChecker_Create( michael@0: PKIX_UInt32 leafMethodListFlags, michael@0: PKIX_UInt32 chainMethodListFlags, michael@0: PKIX_RevocationChecker **pChecker, michael@0: void *plContext) michael@0: { michael@0: PKIX_RevocationChecker *checker = NULL; michael@0: michael@0: PKIX_ENTER(REVOCATIONCHECKER, "PKIX_RevocationChecker_Create"); michael@0: PKIX_NULLCHECK_ONE(pChecker); michael@0: michael@0: PKIX_CHECK( michael@0: PKIX_PL_Object_Alloc(PKIX_REVOCATIONCHECKER_TYPE, michael@0: sizeof (PKIX_RevocationChecker), michael@0: (PKIX_PL_Object **)&checker, michael@0: plContext), michael@0: PKIX_COULDNOTCREATECERTCHAINCHECKEROBJECT); michael@0: michael@0: checker->leafMethodListFlags = leafMethodListFlags; michael@0: checker->chainMethodListFlags = chainMethodListFlags; michael@0: checker->leafMethodList = NULL; michael@0: checker->chainMethodList = NULL; michael@0: michael@0: *pChecker = checker; michael@0: checker = NULL; michael@0: michael@0: cleanup: michael@0: PKIX_DECREF(checker); michael@0: michael@0: PKIX_RETURN(REVOCATIONCHECKER); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: PKIX_RevocationChecker_CreateAndAddMethod michael@0: */ michael@0: PKIX_Error * michael@0: PKIX_RevocationChecker_CreateAndAddMethod( michael@0: PKIX_RevocationChecker *revChecker, michael@0: PKIX_ProcessingParams *params, michael@0: PKIX_RevocationMethodType methodType, michael@0: PKIX_UInt32 flags, michael@0: PKIX_UInt32 priority, michael@0: PKIX_PL_VerifyCallback verificationFn, michael@0: PKIX_Boolean isLeafMethod, michael@0: void *plContext) michael@0: { michael@0: PKIX_List **methodList = NULL; michael@0: PKIX_List *unsortedList = NULL; michael@0: PKIX_List *certStores = NULL; michael@0: pkix_RevocationMethod *method = NULL; michael@0: pkix_LocalRevocationCheckFn *localRevChecker = NULL; michael@0: pkix_ExternalRevocationCheckFn *externRevChecker = NULL; michael@0: PKIX_UInt32 miFlags; michael@0: michael@0: PKIX_ENTER(REVOCATIONCHECKER, "PKIX_RevocationChecker_CreateAndAddMethod"); michael@0: PKIX_NULLCHECK_ONE(revChecker); michael@0: michael@0: /* If the caller has said "Either one is sufficient, then don't let the michael@0: * absence of any one method's info lead to an overall failure. michael@0: */ michael@0: miFlags = isLeafMethod ? revChecker->leafMethodListFlags michael@0: : revChecker->chainMethodListFlags; michael@0: if (miFlags & PKIX_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE) michael@0: flags &= ~PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO; michael@0: michael@0: switch (methodType) { michael@0: case PKIX_RevocationMethod_CRL: michael@0: localRevChecker = pkix_CrlChecker_CheckLocal; michael@0: externRevChecker = pkix_CrlChecker_CheckExternal; michael@0: PKIX_CHECK( michael@0: PKIX_ProcessingParams_GetCertStores(params, &certStores, michael@0: plContext), michael@0: PKIX_PROCESSINGPARAMSGETCERTSTORESFAILED); michael@0: PKIX_CHECK( michael@0: pkix_CrlChecker_Create(methodType, flags, priority, michael@0: localRevChecker, externRevChecker, michael@0: certStores, verificationFn, michael@0: &method, michael@0: plContext), michael@0: PKIX_COULDNOTCREATECRLCHECKEROBJECT); michael@0: break; michael@0: case PKIX_RevocationMethod_OCSP: michael@0: localRevChecker = pkix_OcspChecker_CheckLocal; michael@0: externRevChecker = pkix_OcspChecker_CheckExternal; michael@0: PKIX_CHECK( michael@0: pkix_OcspChecker_Create(methodType, flags, priority, michael@0: localRevChecker, externRevChecker, michael@0: verificationFn, michael@0: &method, michael@0: plContext), michael@0: PKIX_COULDNOTCREATEOCSPCHECKEROBJECT); michael@0: break; michael@0: default: michael@0: PKIX_ERROR(PKIX_INVALIDREVOCATIONMETHOD); michael@0: } michael@0: michael@0: if (isLeafMethod) { michael@0: methodList = &revChecker->leafMethodList; michael@0: } else { michael@0: methodList = &revChecker->chainMethodList; michael@0: } michael@0: michael@0: if (*methodList == NULL) { michael@0: PKIX_CHECK( michael@0: PKIX_List_Create(methodList, plContext), michael@0: PKIX_LISTCREATEFAILED); michael@0: } michael@0: unsortedList = *methodList; michael@0: PKIX_CHECK( michael@0: PKIX_List_AppendItem(unsortedList, (PKIX_PL_Object*)method, plContext), michael@0: PKIX_LISTAPPENDITEMFAILED); michael@0: PKIX_CHECK( michael@0: pkix_List_BubbleSort(unsortedList, michael@0: pkix_RevocationChecker_SortComparator, michael@0: methodList, plContext), michael@0: PKIX_LISTBUBBLESORTFAILED); michael@0: michael@0: cleanup: michael@0: PKIX_DECREF(method); michael@0: PKIX_DECREF(unsortedList); michael@0: PKIX_DECREF(certStores); michael@0: michael@0: PKIX_RETURN(REVOCATIONCHECKER); michael@0: } michael@0: michael@0: /* michael@0: * FUNCTION: PKIX_RevocationChecker_Check michael@0: */ michael@0: PKIX_Error * michael@0: PKIX_RevocationChecker_Check( michael@0: PKIX_PL_Cert *cert, michael@0: PKIX_PL_Cert *issuer, michael@0: PKIX_RevocationChecker *revChecker, michael@0: PKIX_ProcessingParams *procParams, michael@0: PKIX_Boolean chainVerificationState, michael@0: PKIX_Boolean testingLeafCert, michael@0: PKIX_RevocationStatus *pRevStatus, michael@0: PKIX_UInt32 *pReasonCode, michael@0: void **pNbioContext, michael@0: void *plContext) michael@0: { michael@0: PKIX_RevocationStatus overallStatus = PKIX_RevStatus_NoInfo; michael@0: PKIX_RevocationStatus methodStatus[PKIX_RevocationMethod_MAX]; michael@0: PKIX_Boolean onlyUseRemoteMethods = PKIX_FALSE; michael@0: PKIX_UInt32 revFlags = 0; michael@0: PKIX_List *revList = NULL; michael@0: PKIX_PL_Date *date = NULL; michael@0: pkix_RevocationMethod *method = NULL; michael@0: void *nbioContext; michael@0: int tries; michael@0: michael@0: PKIX_ENTER(REVOCATIONCHECKER, "PKIX_RevocationChecker_Check"); michael@0: PKIX_NULLCHECK_TWO(revChecker, procParams); michael@0: michael@0: nbioContext = *pNbioContext; michael@0: *pNbioContext = NULL; michael@0: michael@0: if (testingLeafCert) { michael@0: revList = revChecker->leafMethodList; michael@0: revFlags = revChecker->leafMethodListFlags; michael@0: } else { michael@0: revList = revChecker->chainMethodList; michael@0: revFlags = revChecker->chainMethodListFlags; michael@0: } michael@0: if (!revList) { michael@0: /* Return NoInfo status */ michael@0: goto cleanup; michael@0: } michael@0: michael@0: PORT_Memset(methodStatus, PKIX_RevStatus_NoInfo, michael@0: sizeof(PKIX_RevocationStatus) * PKIX_RevocationMethod_MAX); michael@0: michael@0: date = procParams->date; michael@0: michael@0: /* Need to have two loops if we testing all local info first: michael@0: * first we are going to test all local(cached) info michael@0: * second, all remote info(fetching) */ michael@0: for (tries = 0;tries < 2;tries++) { michael@0: int methodNum = 0; michael@0: for (;methodNum < revList->length;methodNum++) { michael@0: PKIX_UInt32 methodFlags = 0; michael@0: michael@0: PKIX_DECREF(method); michael@0: PKIX_CHECK( michael@0: PKIX_List_GetItem(revList, methodNum, michael@0: (PKIX_PL_Object**)&method, plContext), michael@0: PKIX_LISTGETITEMFAILED); michael@0: methodFlags = method->flags; michael@0: if (!(methodFlags & PKIX_REV_M_TEST_USING_THIS_METHOD)) { michael@0: /* Will not check with this method. Skipping... */ michael@0: continue; michael@0: } michael@0: if (!onlyUseRemoteMethods && michael@0: methodStatus[methodNum] == PKIX_RevStatus_NoInfo) { michael@0: PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; michael@0: PKIX_CHECK_NO_GOTO( michael@0: (*method->localRevChecker)(cert, issuer, date, michael@0: method, procParams, michael@0: methodFlags, michael@0: chainVerificationState, michael@0: &revStatus, michael@0: pReasonCode, plContext), michael@0: PKIX_REVCHECKERCHECKFAILED); michael@0: methodStatus[methodNum] = revStatus; michael@0: if (revStatus == PKIX_RevStatus_Revoked) { michael@0: /* if error was generated use it as final error. */ michael@0: overallStatus = PKIX_RevStatus_Revoked; michael@0: goto cleanup; michael@0: } michael@0: if (pkixErrorResult) { michael@0: /* Disregard errors. Only returned revStatus matters. */ michael@0: PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult, michael@0: plContext); michael@0: pkixErrorResult = NULL; michael@0: } michael@0: } michael@0: if ((!(revFlags & PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST) || michael@0: onlyUseRemoteMethods) && michael@0: chainVerificationState && michael@0: methodStatus[methodNum] == PKIX_RevStatus_NoInfo) { michael@0: if (!(methodFlags & PKIX_REV_M_FORBID_NETWORK_FETCHING)) { michael@0: PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; michael@0: PKIX_CHECK_NO_GOTO( michael@0: (*method->externalRevChecker)(cert, issuer, date, michael@0: method, michael@0: procParams, methodFlags, michael@0: &revStatus, pReasonCode, michael@0: &nbioContext, plContext), michael@0: PKIX_REVCHECKERCHECKFAILED); michael@0: methodStatus[methodNum] = revStatus; michael@0: if (revStatus == PKIX_RevStatus_Revoked) { michael@0: /* if error was generated use it as final error. */ michael@0: overallStatus = PKIX_RevStatus_Revoked; michael@0: goto cleanup; michael@0: } michael@0: if (pkixErrorResult) { michael@0: /* Disregard errors. Only returned revStatus matters. */ michael@0: PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult, michael@0: plContext); michael@0: pkixErrorResult = NULL; michael@0: } michael@0: } else if (methodFlags & michael@0: PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO) { michael@0: /* Info is not in the local cache. Network fetching is not michael@0: * allowed. If need to fail on missing fresh info for the michael@0: * the method, then we should fail right here.*/ michael@0: overallStatus = PKIX_RevStatus_Revoked; michael@0: goto cleanup; michael@0: } michael@0: } michael@0: /* If success and we should not check the next method, then michael@0: * return a success. */ michael@0: if (methodStatus[methodNum] == PKIX_RevStatus_Success && michael@0: !(methodFlags & PKIX_REV_M_CONTINUE_TESTING_ON_FRESH_INFO)) { michael@0: overallStatus = PKIX_RevStatus_Success; michael@0: goto cleanup; michael@0: } michael@0: } /* inner loop */ michael@0: if (!onlyUseRemoteMethods && michael@0: revFlags & PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST && michael@0: chainVerificationState) { michael@0: onlyUseRemoteMethods = PKIX_TRUE; michael@0: continue; michael@0: } michael@0: break; michael@0: } /* outer loop */ michael@0: michael@0: if (overallStatus == PKIX_RevStatus_NoInfo && michael@0: chainVerificationState) { michael@0: /* The following check makes sence only for chain michael@0: * validation step, sinse we do not fetch info while michael@0: * in the process of finding trusted anchor. michael@0: * For chain building step it is enough to know, that michael@0: * the cert was not directly revoked by any of the michael@0: * methods. */ michael@0: michael@0: /* Still have no info. But one of the method could michael@0: * have returned success status(possible if CONTINUE michael@0: * TESTING ON FRESH INFO flag was used). michael@0: * If any of the methods have returned Success status, michael@0: * the overallStatus should be success. */ michael@0: int methodNum = 0; michael@0: for (;methodNum < PKIX_RevocationMethod_MAX;methodNum++) { michael@0: if (methodStatus[methodNum] == PKIX_RevStatus_Success) { michael@0: overallStatus = PKIX_RevStatus_Success; michael@0: goto cleanup; michael@0: } michael@0: } michael@0: if (revFlags & PKIX_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE) { michael@0: overallStatus = PKIX_RevStatus_Revoked; michael@0: } michael@0: } michael@0: michael@0: cleanup: michael@0: *pRevStatus = overallStatus; michael@0: PKIX_DECREF(method); michael@0: michael@0: PKIX_RETURN(REVOCATIONCHECKER); michael@0: } michael@0: