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