diff options
Diffstat (limited to 'security/nss/lib/libpkix/pkix/top/pkix_build.c')
-rwxr-xr-x | security/nss/lib/libpkix/pkix/top/pkix_build.c | 3763 |
1 files changed, 3763 insertions, 0 deletions
diff --git a/security/nss/lib/libpkix/pkix/top/pkix_build.c b/security/nss/lib/libpkix/pkix/top/pkix_build.c new file mode 100755 index 000000000..0c87ba3f3 --- /dev/null +++ b/security/nss/lib/libpkix/pkix/top/pkix_build.c @@ -0,0 +1,3763 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * pkix_build.c + * + * Top level buildChain function + * + */ + +/* #define PKIX_BUILDDEBUG 1 */ +/* #define PKIX_FORWARDBUILDERSTATEDEBUG 1 */ + +#include "pkix_build.h" + +extern PRLogModuleInfo *pkixLog; + +/* + * List of critical extension OIDs associate with what build chain has + * checked. Those OIDs need to be removed from the unresolved critical + * extension OIDs list manually (instead of by checker automatically). + */ +static SECOidTag buildCheckedCritExtOIDs[] = { + PKIX_CERTKEYUSAGE_OID, + PKIX_CERTSUBJALTNAME_OID, + PKIX_BASICCONSTRAINTS_OID, + PKIX_NAMECONSTRAINTS_OID, + PKIX_EXTENDEDKEYUSAGE_OID, + PKIX_NSCERTTYPE_OID, + PKIX_UNKNOWN_OID +}; + +/* --Private-ForwardBuilderState-Functions---------------------------------- */ + +/* + * FUNCTION: pkix_ForwardBuilderState_Destroy + * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) + */ +static PKIX_Error * +pkix_ForwardBuilderState_Destroy( + PKIX_PL_Object *object, + void *plContext) +{ + PKIX_ForwardBuilderState *state = NULL; + + PKIX_ENTER(FORWARDBUILDERSTATE, "pkix_ForwardBuilderState_Destroy"); + PKIX_NULLCHECK_ONE(object); + + PKIX_CHECK(pkix_CheckType + (object, PKIX_FORWARDBUILDERSTATE_TYPE, plContext), + PKIX_OBJECTNOTFORWARDBUILDERSTATE); + + state = (PKIX_ForwardBuilderState *)object; + + state->status = BUILD_INITIAL; + state->traversedCACerts = 0; + state->certStoreIndex = 0; + state->numCerts = 0; + state->numAias = 0; + state->certIndex = 0; + state->aiaIndex = 0; + state->certCheckedIndex = 0; + state->checkerIndex = 0; + state->hintCertIndex = 0; + state->numFanout = 0; + state->numDepth = 0; + state->reasonCode = 0; + state->canBeCached = PKIX_FALSE; + state->useOnlyLocal = PKIX_FALSE; + state->revChecking = PKIX_FALSE; + state->usingHintCerts = PKIX_FALSE; + state->certLoopingDetected = PKIX_FALSE; + PKIX_DECREF(state->validityDate); + PKIX_DECREF(state->prevCert); + PKIX_DECREF(state->candidateCert); + PKIX_DECREF(state->traversedSubjNames); + PKIX_DECREF(state->trustChain); + PKIX_DECREF(state->aia); + PKIX_DECREF(state->candidateCerts); + PKIX_DECREF(state->reversedCertChain); + PKIX_DECREF(state->checkedCritExtOIDs); + PKIX_DECREF(state->checkerChain); + PKIX_DECREF(state->certSel); + PKIX_DECREF(state->verifyNode); + PKIX_DECREF(state->client); + + /* + * If we ever add a child link we have to be careful not to have loops + * in the Destroy process. But with one-way links we should be okay. + */ + if (state->parentState == NULL) { + state->buildConstants.numAnchors = 0; + state->buildConstants.numCertStores = 0; + state->buildConstants.numHintCerts = 0; + state->buildConstants.procParams = 0; + PKIX_DECREF(state->buildConstants.testDate); + PKIX_DECREF(state->buildConstants.timeLimit); + PKIX_DECREF(state->buildConstants.targetCert); + PKIX_DECREF(state->buildConstants.targetPubKey); + PKIX_DECREF(state->buildConstants.certStores); + PKIX_DECREF(state->buildConstants.anchors); + PKIX_DECREF(state->buildConstants.userCheckers); + PKIX_DECREF(state->buildConstants.hintCerts); + PKIX_DECREF(state->buildConstants.revChecker); + PKIX_DECREF(state->buildConstants.aiaMgr); + } else { + PKIX_DECREF(state->parentState); + } + +cleanup: + + PKIX_RETURN(FORWARDBUILDERSTATE); +} + +/* + * FUNCTION: pkix_ForwardBuilderState_Create + * + * DESCRIPTION: + * Allocate and initialize a ForwardBuilderState. + * + * PARAMETERS + * "traversedCACerts" + * Number of CA certificates traversed. + * "numFanout" + * Number of Certs that can be considered at this level (0 = no limit) + * "numDepth" + * Number of additional levels that can be searched (0 = no limit) + * "canBeCached" + * Boolean value indicating whether all certs on the chain can be cached. + * "validityDate" + * Address of Date at which build chain Certs' most restricted validity + * time is kept. May be NULL. + * "prevCert" + * Address of Cert just traversed. Must be non-NULL. + * "traversedSubjNames" + * Address of List of GeneralNames that have been traversed. + * Must be non-NULL. + * "trustChain" + * Address of List of certificates traversed. Must be non-NULL. + * "parentState" + * Address of previous ForwardBuilderState + * "pState" + * Address where ForwardBuilderState will be stored. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_ForwardBuilderState_Create( + PKIX_Int32 traversedCACerts, + PKIX_UInt32 numFanout, + PKIX_UInt32 numDepth, + PKIX_Boolean canBeCached, + PKIX_PL_Date *validityDate, + PKIX_PL_Cert *prevCert, + PKIX_List *traversedSubjNames, + PKIX_List *trustChain, + PKIX_ForwardBuilderState *parentState, + PKIX_ForwardBuilderState **pState, + void *plContext) +{ + PKIX_ForwardBuilderState *state = NULL; + + PKIX_ENTER(FORWARDBUILDERSTATE, "pkix_ForwardBuilderState_Create"); + PKIX_NULLCHECK_FOUR(prevCert, traversedSubjNames, pState, trustChain); + + PKIX_CHECK(PKIX_PL_Object_Alloc + (PKIX_FORWARDBUILDERSTATE_TYPE, + sizeof (PKIX_ForwardBuilderState), + (PKIX_PL_Object **)&state, + plContext), + PKIX_COULDNOTCREATEFORWARDBUILDERSTATEOBJECT); + + state->status = BUILD_INITIAL; + state->traversedCACerts = traversedCACerts; + state->certStoreIndex = 0; + state->numCerts = 0; + state->numAias = 0; + state->certIndex = 0; + state->aiaIndex = 0; + state->certCheckedIndex = 0; + state->checkerIndex = 0; + state->hintCertIndex = 0; + state->numFanout = numFanout; + state->numDepth = numDepth; + state->reasonCode = 0; + state->revChecking = numDepth; + state->canBeCached = canBeCached; + state->useOnlyLocal = PKIX_TRUE; + state->revChecking = PKIX_FALSE; + state->usingHintCerts = PKIX_FALSE; + state->certLoopingDetected = PKIX_FALSE; + + PKIX_INCREF(validityDate); + state->validityDate = validityDate; + + PKIX_INCREF(prevCert); + state->prevCert = prevCert; + + state->candidateCert = NULL; + + PKIX_INCREF(traversedSubjNames); + state->traversedSubjNames = traversedSubjNames; + + PKIX_INCREF(trustChain); + state->trustChain = trustChain; + + state->aia = NULL; + state->candidateCerts = NULL; + state->reversedCertChain = NULL; + state->checkedCritExtOIDs = NULL; + state->checkerChain = NULL; + state->certSel = NULL; + state->verifyNode = NULL; + state->client = NULL; + + PKIX_INCREF(parentState); + state->parentState = parentState; + + if (parentState != NULL) { + state->buildConstants.numAnchors = + parentState->buildConstants.numAnchors; + state->buildConstants.numCertStores = + parentState->buildConstants.numCertStores; + state->buildConstants.numHintCerts = + parentState->buildConstants.numHintCerts; + state->buildConstants.maxFanout = + parentState->buildConstants.maxFanout; + state->buildConstants.maxDepth = + parentState->buildConstants.maxDepth; + state->buildConstants.maxTime = + parentState->buildConstants.maxTime; + state->buildConstants.procParams = + parentState->buildConstants.procParams; + state->buildConstants.testDate = + parentState->buildConstants.testDate; + state->buildConstants.timeLimit = + parentState->buildConstants.timeLimit; + state->buildConstants.targetCert = + parentState->buildConstants.targetCert; + state->buildConstants.targetPubKey = + parentState->buildConstants.targetPubKey; + state->buildConstants.certStores = + parentState->buildConstants.certStores; + state->buildConstants.anchors = + parentState->buildConstants.anchors; + state->buildConstants.userCheckers = + parentState->buildConstants.userCheckers; + state->buildConstants.hintCerts = + parentState->buildConstants.hintCerts; + state->buildConstants.revChecker = + parentState->buildConstants.revChecker; + state->buildConstants.aiaMgr = + parentState->buildConstants.aiaMgr; + state->buildConstants.trustOnlyUserAnchors = + parentState->buildConstants.trustOnlyUserAnchors; + } + + *pState = state; + state = NULL; +cleanup: + + PKIX_DECREF(state); + + PKIX_RETURN(FORWARDBUILDERSTATE); +} + +/* + * FUNCTION: pkix_Build_GetResourceLimits + * + * DESCRIPTION: + * Retrieve Resource Limits from ProcessingParams and initialize them in + * BuildConstants. + * + * PARAMETERS + * "buildConstants" + * Address of a BuildConstants structure containing objects and values + * that remain constant throughout the building of a chain. Must be + * non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_Build_GetResourceLimits( + BuildConstants *buildConstants, + void *plContext) +{ + PKIX_ResourceLimits *resourceLimits = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_GetResourceLimits"); + PKIX_NULLCHECK_ONE(buildConstants); + + PKIX_CHECK(PKIX_ProcessingParams_GetResourceLimits + (buildConstants->procParams, &resourceLimits, plContext), + PKIX_PROCESSINGPARAMSGETRESOURCELIMITSFAILED); + + buildConstants->maxFanout = 0; + buildConstants->maxDepth = 0; + buildConstants->maxTime = 0; + + if (resourceLimits) { + + PKIX_CHECK(PKIX_ResourceLimits_GetMaxFanout + (resourceLimits, &buildConstants->maxFanout, plContext), + PKIX_RESOURCELIMITSGETMAXFANOUTFAILED); + + PKIX_CHECK(PKIX_ResourceLimits_GetMaxDepth + (resourceLimits, &buildConstants->maxDepth, plContext), + PKIX_RESOURCELIMITSGETMAXDEPTHFAILED); + + PKIX_CHECK(PKIX_ResourceLimits_GetMaxTime + (resourceLimits, &buildConstants->maxTime, plContext), + PKIX_RESOURCELIMITSGETMAXTIMEFAILED); + } + +cleanup: + + PKIX_DECREF(resourceLimits); + + PKIX_RETURN(BUILD); +} + +/* + * FUNCTION: pkix_ForwardBuilderState_ToString + * (see comments for PKIX_PL_ToStringCallback in pkix_pl_system.h) + */ +static PKIX_Error * +pkix_ForwardBuilderState_ToString + (PKIX_PL_Object *object, + PKIX_PL_String **pString, + void *plContext) +{ + PKIX_ForwardBuilderState *state = NULL; + PKIX_PL_String *formatString = NULL; + PKIX_PL_String *resultString = NULL; + PKIX_PL_String *buildStatusString = NULL; + PKIX_PL_String *validityDateString = NULL; + PKIX_PL_String *prevCertString = NULL; + PKIX_PL_String *candidateCertString = NULL; + PKIX_PL_String *traversedSubjNamesString = NULL; + PKIX_PL_String *trustChainString = NULL; + PKIX_PL_String *candidateCertsString = NULL; + PKIX_PL_String *certSelString = NULL; + PKIX_PL_String *verifyNodeString = NULL; + PKIX_PL_String *parentStateString = NULL; + char *asciiFormat = "\n" + "\t{buildStatus: \t%s\n" + "\ttraversedCACerts: \t%d\n" + "\tcertStoreIndex: \t%d\n" + "\tnumCerts: \t%d\n" + "\tnumAias: \t%d\n" + "\tcertIndex: \t%d\n" + "\taiaIndex: \t%d\n" + "\tnumFanout: \t%d\n" + "\tnumDepth: \t%d\n" + "\treasonCode: \t%d\n" + "\tcanBeCached: \t%d\n" + "\tuseOnlyLocal: \t%d\n" + "\trevChecking: \t%d\n" + "\tvalidityDate: \t%s\n" + "\tprevCert: \t%s\n" + "\tcandidateCert: \t%s\n" + "\ttraversedSubjNames: \t%s\n" + "\ttrustChain: \t%s\n" + "\tcandidateCerts: \t%s\n" + "\tcertSel: \t%s\n" + "\tverifyNode: \t%s\n" + "\tparentState: \t%s}\n"; + char *asciiStatus = NULL; + + PKIX_ENTER(FORWARDBUILDERSTATE, "pkix_ForwardBuilderState_ToString"); + PKIX_NULLCHECK_TWO(object, pString); + + PKIX_CHECK(pkix_CheckType + (object, PKIX_FORWARDBUILDERSTATE_TYPE, plContext), + PKIX_OBJECTNOTFORWARDBUILDERSTATE); + + state = (PKIX_ForwardBuilderState *)object; + + PKIX_CHECK(PKIX_PL_String_Create + (PKIX_ESCASCII, asciiFormat, 0, &formatString, plContext), + PKIX_STRINGCREATEFAILED); + + switch (state->status) { + case BUILD_SHORTCUTPENDING: asciiStatus = "BUILD_SHORTCUTPENDING"; + break; + case BUILD_INITIAL: asciiStatus = "BUILD_INITIAL"; + break; + case BUILD_TRYAIA: asciiStatus = "BUILD_TRYAIA"; + break; + case BUILD_AIAPENDING: asciiStatus = "BUILD_AIAPENDING"; + break; + case BUILD_COLLECTINGCERTS: asciiStatus = "BUILD_COLLECTINGCERTS"; + break; + case BUILD_GATHERPENDING: asciiStatus = "BUILD_GATHERPENDING"; + break; + case BUILD_CERTVALIDATING: asciiStatus = "BUILD_CERTVALIDATING"; + break; + case BUILD_ABANDONNODE: asciiStatus = "BUILD_ABANDONNODE"; + break; + case BUILD_DATEPREP: asciiStatus = "BUILD_DATEPREP"; + break; + case BUILD_CHECKTRUSTED: asciiStatus = "BUILD_CHECKTRUSTED"; + break; + case BUILD_CHECKTRUSTED2: asciiStatus = "BUILD_CHECKTRUSTED2"; + break; + case BUILD_ADDTOCHAIN: asciiStatus = "BUILD_ADDTOCHAIN"; + break; + case BUILD_VALCHAIN: asciiStatus = "BUILD_VALCHAIN"; + break; + case BUILD_VALCHAIN2: asciiStatus = "BUILD_VALCHAIN2"; + break; + case BUILD_EXTENDCHAIN: asciiStatus = "BUILD_EXTENDCHAIN"; + break; + case BUILD_GETNEXTCERT: asciiStatus = "BUILD_GETNEXTCERT"; + break; + default: asciiStatus = "INVALID STATUS"; + break; + } + + PKIX_CHECK(PKIX_PL_String_Create + (PKIX_ESCASCII, asciiStatus, 0, &buildStatusString, plContext), + PKIX_STRINGCREATEFAILED); + + PKIX_TOSTRING + (state->validityDate, &validityDateString, plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_TOSTRING + (state->prevCert, &prevCertString, plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_TOSTRING + (state->candidateCert, &candidateCertString, plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_TOSTRING + (state->traversedSubjNames, + &traversedSubjNamesString, + plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_TOSTRING + (state->trustChain, &trustChainString, plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_TOSTRING + (state->candidateCerts, &candidateCertsString, plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_TOSTRING + (state->certSel, &certSelString, plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_TOSTRING + (state->verifyNode, &verifyNodeString, plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_TOSTRING + (state->parentState, &parentStateString, plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_CHECK(PKIX_PL_Sprintf + (&resultString, + plContext, + formatString, + buildStatusString, + (PKIX_Int32)state->traversedCACerts, + (PKIX_UInt32)state->certStoreIndex, + (PKIX_UInt32)state->numCerts, + (PKIX_UInt32)state->numAias, + (PKIX_UInt32)state->certIndex, + (PKIX_UInt32)state->aiaIndex, + (PKIX_UInt32)state->numFanout, + (PKIX_UInt32)state->numDepth, + (PKIX_UInt32)state->reasonCode, + state->canBeCached, + state->useOnlyLocal, + state->revChecking, + validityDateString, + prevCertString, + candidateCertString, + traversedSubjNamesString, + trustChainString, + candidateCertsString, + certSelString, + verifyNodeString, + parentStateString), + PKIX_SPRINTFFAILED); + + *pString = resultString; + +cleanup: + PKIX_DECREF(formatString); + PKIX_DECREF(buildStatusString); + PKIX_DECREF(validityDateString); + PKIX_DECREF(prevCertString); + PKIX_DECREF(candidateCertString); + PKIX_DECREF(traversedSubjNamesString); + PKIX_DECREF(trustChainString); + PKIX_DECREF(candidateCertsString); + PKIX_DECREF(certSelString); + PKIX_DECREF(verifyNodeString); + PKIX_DECREF(parentStateString); + + PKIX_RETURN(FORWARDBUILDERSTATE); + +} + +/* + * FUNCTION: pkix_ForwardBuilderState_RegisterSelf + * + * DESCRIPTION: + * Registers PKIX_FORWARDBUILDERSTATE_TYPE and its related functions + * with systemClasses[] + * + * THREAD SAFETY: + * Not Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * + * Since this function is only called by PKIX_PL_Initialize, which should + * only be called once, it is acceptable that this function is not + * thread-safe. + */ +PKIX_Error * +pkix_ForwardBuilderState_RegisterSelf(void *plContext) +{ + + extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; + pkix_ClassTable_Entry entry; + + PKIX_ENTER(FORWARDBUILDERSTATE, + "pkix_ForwardBuilderState_RegisterSelf"); + + entry.description = "ForwardBuilderState"; + entry.objCounter = 0; + entry.typeObjectSize = sizeof(PKIX_ForwardBuilderState); + entry.destructor = pkix_ForwardBuilderState_Destroy; + entry.equalsFunction = NULL; + entry.hashcodeFunction = NULL; + entry.toStringFunction = pkix_ForwardBuilderState_ToString; + entry.comparator = NULL; + entry.duplicateFunction = NULL; + + systemClasses[PKIX_FORWARDBUILDERSTATE_TYPE] = entry; + + PKIX_RETURN(FORWARDBUILDERSTATE); +} + +#if PKIX_FORWARDBUILDERSTATEDEBUG +/* + * FUNCTION: pkix_ForwardBuilderState_DumpState + * + * DESCRIPTION: + * This function invokes the ToString function on the argument pointed to + * by "state". + * PARAMETERS: + * "state" + * The address of the ForwardBuilderState object. Must be non-NULL. + * + * THREAD SAFETY: + * Not Thread Safe (see Thread Safety Definitions in Programmer's Guide) + */ +PKIX_Error * +pkix_ForwardBuilderState_DumpState( + PKIX_ForwardBuilderState *state, + void *plContext) +{ + PKIX_PL_String *stateString = NULL; + char *stateAscii = NULL; + PKIX_UInt32 length; + + PKIX_ENTER(FORWARDBUILDERSTATE,"pkix_ForwardBuilderState_DumpState"); + PKIX_NULLCHECK_ONE(state); + + PKIX_CHECK(PKIX_PL_Object_InvalidateCache + ((PKIX_PL_Object *)state, plContext), + PKIX_OBJECTINVALIDATECACHEFAILED); + + PKIX_CHECK(PKIX_PL_Object_ToString + ((PKIX_PL_Object*)state, &stateString, plContext), + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_CHECK(PKIX_PL_String_GetEncoded + (stateString, + PKIX_ESCASCII, + (void **)&stateAscii, + &length, + plContext), + PKIX_STRINGGETENCODEDFAILED); + + PKIX_DEBUG_ARG("In Phase 1: state = %s\n", stateAscii); + + PKIX_FREE(stateAscii); + PKIX_DECREF(stateString); + +cleanup: + PKIX_RETURN(FORWARDBUILDERSTATE); +} +#endif + +/* + * FUNCTION: pkix_ForwardBuilderState_IsIOPending + * DESCRIPTION: + * + * This function determines whether the state of the ForwardBuilderState + * pointed to by "state" indicates I/O is in progress, and stores the Boolean + * result at "pPending". + * + * PARAMETERS: + * "state" + * The address of the ForwardBuilderState object. Must be non-NULL. + * "pPending" + * The address at which the result is stored. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a ForwardBuilderState Error if the function fails in a + * non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error* +pkix_ForwardBuilderState_IsIOPending( + PKIX_ForwardBuilderState *state, + PKIX_Boolean *pPending, + void *plContext) +{ + PKIX_ENTER(FORWARDBUILDERSTATE, "pkix_ForwardBuilderState_IsIOPending"); + PKIX_NULLCHECK_TWO(state, pPending); + + if ((state->status == BUILD_GATHERPENDING) || + (state->status == BUILD_CHECKTRUSTED2) || + (state->status == BUILD_VALCHAIN2) || + (state->status == BUILD_AIAPENDING)) { + *pPending = PKIX_TRUE; + } else { + *pPending = PKIX_FALSE; + } + + PKIX_RETURN(FORWARDBUILDERSTATE); +} + +/* --Private-BuildChain-Functions------------------------------------------- */ + +/* + * FUNCTION: pkix_Build_SortCertComparator + * DESCRIPTION: + * + * This Function takes two Certificates cast in "obj1" and "obj2", + * compares them to determine which is a more preferable certificate + * for chain building. This Function is suitable for use as a + * comparator callback for pkix_List_BubbleSort, setting "*pResult" to + * > 0 if "obj1" is less desirable than "obj2" and < 0 if "obj1" + * is more desirable than "obj2". + * + * PARAMETERS: + * "obj1" + * Address of the PKIX_PL_Object that is a cast of PKIX_PL_Cert. + * Must be non-NULL. + * "obj2" + * Address of the PKIX_PL_Object that is a cast of PKIX_PL_Cert. + * Must be non-NULL. + * "pResult" + * Address where the comparison result is returned. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_Build_SortCertComparator( + PKIX_PL_Object *obj1, + PKIX_PL_Object *obj2, + PKIX_Int32 *pResult, + void *plContext) +{ + PKIX_PL_Date *date1 = NULL; + PKIX_PL_Date *date2 = NULL; + PKIX_Int32 result = 0; + + PKIX_ENTER(BUILD, "pkix_Build_SortCertComparator"); + PKIX_NULLCHECK_THREE(obj1, obj2, pResult); + + /* + * For sorting candidate certificates, we use NotAfter date as the + * comparison key for now (can be expanded if desired in the future). + * + * In PKIX_BuildChain, the List of CertStores was reordered so that + * trusted CertStores are ahead of untrusted CertStores. That sort, or + * this one, could be taken out if it is determined that it doesn't help + * performance, or in some way hinders the solution of choosing desired + * candidates. + */ + + PKIX_CHECK(pkix_CheckType(obj1, PKIX_CERT_TYPE, plContext), + PKIX_OBJECTNOTCERT); + PKIX_CHECK(pkix_CheckType(obj2, PKIX_CERT_TYPE, plContext), + PKIX_OBJECTNOTCERT); + + PKIX_CHECK(PKIX_PL_Cert_GetValidityNotAfter + ((PKIX_PL_Cert *)obj1, &date1, plContext), + PKIX_CERTGETVALIDITYNOTAFTERFAILED); + + PKIX_CHECK(PKIX_PL_Cert_GetValidityNotAfter + ((PKIX_PL_Cert *)obj2, &date2, plContext), + PKIX_CERTGETVALIDITYNOTAFTERFAILED); + + PKIX_CHECK(PKIX_PL_Object_Compare + ((PKIX_PL_Object *)date1, + (PKIX_PL_Object *)date2, + &result, + plContext), + PKIX_OBJECTCOMPARATORFAILED); + + /* + * Invert the result, so that if date1 is greater than date2, + * obj1 is sorted before obj2. This is because pkix_List_BubbleSort + * sorts in ascending order. + */ + *pResult = -result; + +cleanup: + + PKIX_DECREF(date1); + PKIX_DECREF(date2); + + PKIX_RETURN(BUILD); +} + +/* This local error check macro */ +#define ERROR_CHECK(errCode) \ + if (pkixErrorResult) { \ + if (pkixLog) { \ + PR_LOG(pkixLog, PR_LOG_DEBUG, ("====> ERROR_CHECK code %s\n", #errCode)); \ + } \ + pkixTempErrorReceived = PKIX_TRUE; \ + pkixErrorClass = pkixErrorResult->errClass; \ + if (pkixErrorClass == PKIX_FATAL_ERROR) { \ + goto cleanup; \ + } \ + if (verifyNode) { \ + PKIX_DECREF(verifyNode->error); \ + PKIX_INCREF(pkixErrorResult); \ + verifyNode->error = pkixErrorResult; \ + } \ + pkixErrorCode = errCode; \ + goto cleanup; \ + } + +/* + * FUNCTION: pkix_Build_VerifyCertificate + * DESCRIPTION: + * + * Checks whether the previous Cert stored in the ForwardBuilderState pointed + * to by "state" successfully chains, including signature verification, to the + * candidate Cert also stored in "state", using the Boolean value in "trusted" + * to determine whether "candidateCert" is trusted. + * + * First it checks whether "candidateCert" has already been traversed by + * determining whether it is contained in the List of traversed Certs. It then + * checks the candidate Cert with user checkers, if any, in the List pointed to + * by "userCheckers". Finally, it runs the signature validation. + * + * If this Certificate fails verification, and state->verifyNode is non-NULL, + * this function sets the Error code into the verifyNode. + * + * PARAMETERS: + * "state" + * Address of ForwardBuilderState to be used. Must be non-NULL. + * "userCheckers" + * Address of a List of CertChainCheckers to be used, if present, to + * validate the candidateCert. + * "trusted" + * Boolean value of trust for the candidate Cert + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_Build_VerifyCertificate( + PKIX_ForwardBuilderState *state, + PKIX_List *userCheckers, + PKIX_Boolean *pTrusted, + PKIX_VerifyNode *verifyNode, + void *plContext) +{ + PKIX_UInt32 numUserCheckers = 0; + PKIX_UInt32 i = 0; + PKIX_Boolean loopFound = PKIX_FALSE; + PKIX_Boolean supportForwardChecking = PKIX_FALSE; + PKIX_Boolean trusted = PKIX_FALSE; + PKIX_PL_Cert *candidateCert = NULL; + PKIX_PL_PublicKey *candidatePubKey = NULL; + PKIX_CertChainChecker *userChecker = NULL; + PKIX_CertChainChecker_CheckCallback checkerCheck = NULL; + PKIX_PL_TrustAnchorMode trustAnchorMode = + PKIX_PL_TrustAnchorMode_Ignore; + void *nbioContext = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_VerifyCertificate"); + PKIX_NULLCHECK_TWO(state, pTrusted); + PKIX_NULLCHECK_THREE + (state->candidateCerts, state->prevCert, state->trustChain); + + PKIX_INCREF(state->candidateCert); + candidateCert = state->candidateCert; + + if (state->buildConstants.numAnchors) { + if (state->buildConstants.trustOnlyUserAnchors) { + trustAnchorMode = PKIX_PL_TrustAnchorMode_Exclusive; + } else { + trustAnchorMode = PKIX_PL_TrustAnchorMode_Additive; + } + } else { + trustAnchorMode = PKIX_PL_TrustAnchorMode_Ignore; + } + + PKIX_CHECK( + PKIX_PL_Cert_IsCertTrusted(candidateCert, trustAnchorMode, + &trusted, plContext), + PKIX_CERTISCERTTRUSTEDFAILED); + + *pTrusted = trusted; + + /* check for loops */ + PKIX_CHECK(pkix_List_Contains + (state->trustChain, + (PKIX_PL_Object *)candidateCert, + &loopFound, + plContext), + PKIX_LISTCONTAINSFAILED); + + if (loopFound) { + if (verifyNode != NULL) { + PKIX_Error *verifyError = NULL; + PKIX_ERROR_CREATE + (BUILD, + PKIX_LOOPDISCOVEREDDUPCERTSNOTALLOWED, + verifyError); + PKIX_DECREF(verifyNode->error); + verifyNode->error = verifyError; + } + /* Even if error logged, still need to abort + * if cert is not trusted. */ + if (!trusted) { + PKIX_ERROR(PKIX_LOOPDISCOVEREDDUPCERTSNOTALLOWED); + } + state->certLoopingDetected = PKIX_TRUE; + } + + if (userCheckers != NULL) { + + PKIX_CHECK(PKIX_List_GetLength + (userCheckers, &numUserCheckers, plContext), + PKIX_LISTGETLENGTHFAILED); + + for (i = 0; i < numUserCheckers; i++) { + + PKIX_CHECK(PKIX_List_GetItem + (userCheckers, + i, + (PKIX_PL_Object **) &userChecker, + plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK + (PKIX_CertChainChecker_IsForwardCheckingSupported + (userChecker, &supportForwardChecking, plContext), + PKIX_CERTCHAINCHECKERISFORWARDCHECKINGSUPPORTEDFAILED); + + if (supportForwardChecking == PKIX_TRUE) { + + PKIX_CHECK(PKIX_CertChainChecker_GetCheckCallback + (userChecker, &checkerCheck, plContext), + PKIX_CERTCHAINCHECKERGETCHECKCALLBACKFAILED); + + pkixErrorResult = + checkerCheck(userChecker, candidateCert, NULL, + &nbioContext, plContext); + + ERROR_CHECK(PKIX_USERCHECKERCHECKFAILED); + } + + PKIX_DECREF(userChecker); + } + } + + /* Check that public key of the trusted dsa cert has + * dsa parameters */ + if (trusted) { + PKIX_Boolean paramsNeeded = PKIX_FALSE; + PKIX_CHECK(PKIX_PL_Cert_GetSubjectPublicKey + (candidateCert, &candidatePubKey, plContext), + PKIX_CERTGETSUBJECTPUBLICKEYFAILED); + PKIX_CHECK(PKIX_PL_PublicKey_NeedsDSAParameters + (candidatePubKey, ¶msNeeded, plContext), + PKIX_PUBLICKEYNEEDSDSAPARAMETERSFAILED); + if (paramsNeeded) { + PKIX_ERROR(PKIX_MISSINGDSAPARAMETERS); + } + } + +cleanup: + PKIX_DECREF(candidateCert); + PKIX_DECREF(candidatePubKey); + PKIX_DECREF(userChecker); + + PKIX_RETURN(BUILD); +} + +/* + * FUNCTION: pkix_Build_ValidationCheckers + * DESCRIPTION: + * + * Creates a List of Objects to be used in determining whether the List of + * Certs pointed to by "certChain" successfully validates using the + * ForwardBuilderState pointed to by "state", and the TrustAnchor pointed to by + * "anchor". These objects are a reversed Cert Chain, consisting of the certs + * in "certChain" in reversed order, suitable for presenting to the + * CertChainCheckers; a List of critical extension OIDS that have already been + * processed in forward building; a List of CertChainCheckers to be called, and + * a List of RevocationCheckers to be called. These results are stored in + * fields of "state". + * + * PARAMETERS: + * "state" + * Address of ForwardBuilderState to be used. Must be non-NULL. + * "certChain" + * Address of List of Certs to be validated. Must be non-NULL. + * "anchor" + * Address of TrustAnchor to be used. Must be non-NULL. + * "addEkuChecker" + * Boolean flags that tells to add eku checker to the list + * of checkers. Only needs to be done for existing chain revalidation. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_Build_ValidationCheckers( + PKIX_ForwardBuilderState *state, + PKIX_List *certChain, + PKIX_TrustAnchor *anchor, + PKIX_Boolean chainRevalidationStage, + void *plContext) +{ + PKIX_List *checkers = NULL; + PKIX_List *initialPolicies = NULL; + PKIX_List *reversedCertChain = NULL; + PKIX_List *buildCheckedCritExtOIDsList = NULL; + PKIX_ProcessingParams *procParams = NULL; + PKIX_PL_Cert *trustedCert = NULL; + PKIX_PL_PublicKey *trustedPubKey = NULL; + PKIX_PL_CertNameConstraints *trustedNC = NULL; + PKIX_CertChainChecker *sigChecker = NULL; + PKIX_CertChainChecker *policyChecker = NULL; + PKIX_CertChainChecker *userChecker = NULL; + PKIX_CertChainChecker *nameConstraintsChecker = NULL; + PKIX_CertChainChecker *checker = NULL; + PKIX_CertSelector *certSelector = NULL; + PKIX_List *userCheckerExtOIDs = NULL; + PKIX_PL_OID *oid = NULL; + PKIX_Boolean supportForwardChecking = PKIX_FALSE; + PKIX_Boolean policyQualifiersRejected = PKIX_FALSE; + PKIX_Boolean initialPolicyMappingInhibit = PKIX_FALSE; + PKIX_Boolean initialAnyPolicyInhibit = PKIX_FALSE; + PKIX_Boolean initialExplicitPolicy = PKIX_FALSE; + PKIX_UInt32 numChainCerts; + PKIX_UInt32 numCertCheckers; + PKIX_UInt32 i; + + PKIX_ENTER(BUILD, "pkix_Build_ValidationCheckers"); + PKIX_NULLCHECK_THREE(state, certChain, anchor); + + PKIX_CHECK(PKIX_List_Create(&checkers, plContext), + PKIX_LISTCREATEFAILED); + + PKIX_CHECK(PKIX_List_ReverseList + (certChain, &reversedCertChain, plContext), + PKIX_LISTREVERSELISTFAILED); + + PKIX_CHECK(PKIX_List_GetLength + (reversedCertChain, &numChainCerts, plContext), + PKIX_LISTGETLENGTHFAILED); + + procParams = state->buildConstants.procParams; + + /* Do need to add a number of checker to revalidate + * a built chain. KU, EKU, CertType and Validity Date + * get checked by certificate selector during chain + * construction, but needed to be checked for chain from + * the cache.*/ + if (chainRevalidationStage) { + PKIX_CHECK(pkix_ExpirationChecker_Initialize + (state->buildConstants.testDate, &checker, plContext), + PKIX_EXPIRATIONCHECKERINITIALIZEFAILED); + PKIX_CHECK(PKIX_List_AppendItem + (checkers, (PKIX_PL_Object *)checker, plContext), + PKIX_LISTAPPENDITEMFAILED); + PKIX_DECREF(checker); + + PKIX_CHECK(PKIX_ProcessingParams_GetTargetCertConstraints + (procParams, &certSelector, plContext), + PKIX_PROCESSINGPARAMSGETTARGETCERTCONSTRAINTSFAILED); + + PKIX_CHECK(pkix_TargetCertChecker_Initialize + (certSelector, numChainCerts, &checker, plContext), + PKIX_EXPIRATIONCHECKERINITIALIZEFAILED); + PKIX_CHECK(PKIX_List_AppendItem + (checkers, (PKIX_PL_Object *)checker, plContext), + PKIX_LISTAPPENDITEMFAILED); + PKIX_DECREF(checker); + } + + PKIX_CHECK(PKIX_ProcessingParams_GetInitialPolicies + (procParams, &initialPolicies, plContext), + PKIX_PROCESSINGPARAMSGETINITIALPOLICIESFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_GetPolicyQualifiersRejected + (procParams, &policyQualifiersRejected, plContext), + PKIX_PROCESSINGPARAMSGETPOLICYQUALIFIERSREJECTEDFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_IsPolicyMappingInhibited + (procParams, &initialPolicyMappingInhibit, plContext), + PKIX_PROCESSINGPARAMSISPOLICYMAPPINGINHIBITEDFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_IsAnyPolicyInhibited + (procParams, &initialAnyPolicyInhibit, plContext), + PKIX_PROCESSINGPARAMSISANYPOLICYINHIBITEDFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_IsExplicitPolicyRequired + (procParams, &initialExplicitPolicy, plContext), + PKIX_PROCESSINGPARAMSISEXPLICITPOLICYREQUIREDFAILED); + + PKIX_CHECK(pkix_PolicyChecker_Initialize + (initialPolicies, + policyQualifiersRejected, + initialPolicyMappingInhibit, + initialExplicitPolicy, + initialAnyPolicyInhibit, + numChainCerts, + &policyChecker, + plContext), + PKIX_POLICYCHECKERINITIALIZEFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, (PKIX_PL_Object *)policyChecker, plContext), + PKIX_LISTAPPENDITEMFAILED); + + /* + * Create an OID list that contains critical extensions processed + * by BuildChain. These are specified in a static const array. + */ + PKIX_CHECK(PKIX_List_Create(&buildCheckedCritExtOIDsList, plContext), + PKIX_LISTCREATEFAILED); + + for (i = 0; buildCheckedCritExtOIDs[i] != PKIX_UNKNOWN_OID; i++) { + PKIX_CHECK(PKIX_PL_OID_Create + (buildCheckedCritExtOIDs[i], &oid, plContext), + PKIX_OIDCREATEFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (buildCheckedCritExtOIDsList, + (PKIX_PL_Object *) oid, + plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_DECREF(oid); + } + + if (state->buildConstants.userCheckers != NULL) { + + PKIX_CHECK(PKIX_List_GetLength + (state->buildConstants.userCheckers, + &numCertCheckers, + plContext), + PKIX_LISTGETLENGTHFAILED); + + for (i = 0; i < numCertCheckers; i++) { + + PKIX_CHECK(PKIX_List_GetItem + (state->buildConstants.userCheckers, + i, + (PKIX_PL_Object **) &userChecker, + plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK + (PKIX_CertChainChecker_IsForwardCheckingSupported + (userChecker, &supportForwardChecking, plContext), + PKIX_CERTCHAINCHECKERGETSUPPORTEDEXTENSIONSFAILED); + + /* + * If this userChecker supports forwardChecking then it + * should have been checked during build chain. Skip + * checking but need to add checker's extension OIDs + * to buildCheckedCritExtOIDsList. + */ + if (supportForwardChecking == PKIX_TRUE) { + + PKIX_CHECK + (PKIX_CertChainChecker_GetSupportedExtensions + (userChecker, &userCheckerExtOIDs, plContext), + PKIX_CERTCHAINCHECKERGETSUPPORTEDEXTENSIONSFAILED); + + if (userCheckerExtOIDs != NULL) { + PKIX_CHECK(pkix_List_AppendList + (buildCheckedCritExtOIDsList, + userCheckerExtOIDs, + plContext), + PKIX_LISTAPPENDLISTFAILED); + } + + } else { + PKIX_CHECK(PKIX_List_AppendItem + (checkers, + (PKIX_PL_Object *)userChecker, + plContext), + PKIX_LISTAPPENDITEMFAILED); + } + + PKIX_DECREF(userCheckerExtOIDs); + PKIX_DECREF(userChecker); + } + } + + /* Enabling post chain building signature check on the certs. */ + PKIX_CHECK(PKIX_TrustAnchor_GetTrustedCert + (anchor, &trustedCert, plContext), + PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED); + + PKIX_CHECK(PKIX_PL_Cert_GetSubjectPublicKey + (trustedCert, &trustedPubKey, plContext), + PKIX_CERTGETSUBJECTPUBLICKEYFAILED); + + PKIX_CHECK(pkix_SignatureChecker_Initialize + (trustedPubKey, + numChainCerts, + &sigChecker, + plContext), + PKIX_SIGNATURECHECKERINITIALIZEFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, + (PKIX_PL_Object *)sigChecker, + plContext), + PKIX_LISTAPPENDITEMFAILED); + + /* Enabling post chain building name constraints check on the certs. */ + PKIX_CHECK(PKIX_TrustAnchor_GetNameConstraints + (anchor, &trustedNC, plContext), + PKIX_TRUSTANCHORGETNAMECONSTRAINTSFAILED); + + PKIX_CHECK(pkix_NameConstraintsChecker_Initialize + (trustedNC, numChainCerts, &nameConstraintsChecker, + plContext), + PKIX_NAMECONSTRAINTSCHECKERINITIALIZEFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, + (PKIX_PL_Object *)nameConstraintsChecker, + plContext), + PKIX_LISTAPPENDITEMFAILED); + + + PKIX_DECREF(state->reversedCertChain); + PKIX_INCREF(reversedCertChain); + state->reversedCertChain = reversedCertChain; + PKIX_DECREF(state->checkedCritExtOIDs); + PKIX_INCREF(buildCheckedCritExtOIDsList); + state->checkedCritExtOIDs = buildCheckedCritExtOIDsList; + PKIX_DECREF(state->checkerChain); + state->checkerChain = checkers; + checkers = NULL; + state->certCheckedIndex = 0; + state->checkerIndex = 0; + state->revChecking = PKIX_FALSE; + + +cleanup: + + PKIX_DECREF(oid); + PKIX_DECREF(reversedCertChain); + PKIX_DECREF(buildCheckedCritExtOIDsList); + PKIX_DECREF(checker); + PKIX_DECREF(checkers); + PKIX_DECREF(initialPolicies); + PKIX_DECREF(trustedCert); + PKIX_DECREF(trustedPubKey); + PKIX_DECREF(certSelector); + PKIX_DECREF(sigChecker); + PKIX_DECREF(trustedNC); + PKIX_DECREF(nameConstraintsChecker); + PKIX_DECREF(policyChecker); + PKIX_DECREF(userChecker); + PKIX_DECREF(userCheckerExtOIDs); + + PKIX_RETURN(BUILD); +} + +/* + * FUNCTION: pkix_Build_ValidateEntireChain + * DESCRIPTION: + * + * Checks whether the current List of Certs successfully validates using the + * TrustAnchor pointed to by "anchor" and other parameters contained, as was + * the Cert List, in "state". + * + * If a checker using non-blocking I/O returns with a non-NULL non-blocking I/O + * context (NBIOContext), an indication that I/O is in progress and the + * checking has not been completed, this function stores that context at + * "pNBIOContext". Otherwise, it stores NULL at "pNBIOContext". + * + * If not awaiting I/O and if successful, a ValidateResult is created + * containing the Public Key of the target certificate (including DSA parameter + * inheritance, if any) and the PolicyNode representing the policy tree output + * by the validation algorithm. If not successful, an Error pointer is + * returned. + * + * PARAMETERS: + * "state" + * Address of ForwardBuilderState to be used. Must be non-NULL. + * "anchor" + * Address of TrustAnchor to be used. Must be non-NULL. + * "pNBIOContext" + * Address at which the NBIOContext is stored indicating whether the + * validation is complete. Must be non-NULL. + * "pValResult" + * Address at which the ValidateResult is stored. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_Build_ValidateEntireChain( + PKIX_ForwardBuilderState *state, + PKIX_TrustAnchor *anchor, + void **pNBIOContext, + PKIX_ValidateResult **pValResult, + PKIX_VerifyNode *verifyNode, + void *plContext) +{ + PKIX_UInt32 numChainCerts = 0; + PKIX_PL_PublicKey *subjPubKey = NULL; + PKIX_PolicyNode *policyTree = NULL; + PKIX_ValidateResult *valResult = NULL; + void *nbioContext = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_ValidateEntireChain"); + PKIX_NULLCHECK_FOUR(state, anchor, pNBIOContext, pValResult); + + *pNBIOContext = NULL; /* prepare for case of error exit */ + + PKIX_CHECK(PKIX_List_GetLength + (state->reversedCertChain, &numChainCerts, plContext), + PKIX_LISTGETLENGTHFAILED); + + pkixErrorResult = + pkix_CheckChain(state->reversedCertChain, numChainCerts, anchor, + state->checkerChain, + state->buildConstants.revChecker, + state->checkedCritExtOIDs, + state->buildConstants.procParams, + &state->certCheckedIndex, &state->checkerIndex, + &state->revChecking, &state->reasonCode, + &nbioContext, &subjPubKey, &policyTree, NULL, + plContext); + + if (nbioContext != NULL) { + *pNBIOContext = nbioContext; + goto cleanup; + } + + ERROR_CHECK(PKIX_CHECKCHAINFAILED); + + /* XXX Remove this assertion after 2014-12-31. See bug 946984. */ + PORT_Assert(state->reasonCode == 0); + + PKIX_CHECK(pkix_ValidateResult_Create + (subjPubKey, anchor, policyTree, &valResult, plContext), + PKIX_VALIDATERESULTCREATEFAILED); + + *pValResult = valResult; + valResult = NULL; + +cleanup: + PKIX_DECREF(subjPubKey); + PKIX_DECREF(policyTree); + PKIX_DECREF(valResult); + + PKIX_RETURN(BUILD); +} + +/* + * FUNCTION: pkix_Build_SortCandidateCerts + * DESCRIPTION: + * + * This function sorts a List of candidate Certs pointed to by "candidates" + * using an algorithm that places Certs most likely to produce a successful + * chain at the front of the list, storing the resulting sorted List at + * "pSortedCandidates". + * + * At present the only sort criterion is that trusted Certs go ahead of + * untrusted Certs. + * + * PARAMETERS: + * "candidates" + * Address of List of Candidate Certs to be sorted. Must be non-NULL. + * "pSortedCandidates" + * Address at which sorted List is stored. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_Build_SortCandidateCerts( + PKIX_List *candidates, + PKIX_List **pSortedCandidates, + void *plContext) +{ + PKIX_List *sortedList = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_SortCandidateCerts"); + PKIX_NULLCHECK_TWO(candidates, pSortedCandidates); + + /* + * Both bubble and quick sort algorithms are available. + * For a list of fewer than around 100 items, the bubble sort is more + * efficient. (This number was determined by experimenting with both + * algorithms on a Java List.) + * If the candidate list is very small, using the sort can drag down + * the performance a little bit. + */ + + PKIX_CHECK(pkix_List_BubbleSort + (candidates, + pkix_Build_SortCertComparator, + &sortedList, + plContext), + PKIX_LISTBUBBLESORTFAILED); + + *pSortedCandidates = sortedList; + +cleanup: + + PKIX_RETURN(BUILD); +} + +/* + * FUNCTION: pkix_Build_BuildSelectorAndParams + * DESCRIPTION: + * + * This function creates a CertSelector, initialized with an appropriate + * ComCertSelParams, using the variables provided in the ForwardBuilderState + * pointed to by "state". The CertSelector created is stored in the certsel + * element of "state". + * + * PARAMETERS: + * "state" + * Address of ForwardBuilderState to be used. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_Build_BuildSelectorAndParams( + PKIX_ForwardBuilderState *state, + void *plContext) +{ + PKIX_ComCertSelParams *certSelParams = NULL; + PKIX_CertSelector *certSel = NULL; + PKIX_PL_X500Name *currentIssuer = NULL; + PKIX_PL_ByteArray *authKeyId = NULL; + PKIX_PL_Date *testDate = NULL; + PKIX_CertSelector *callerCertSelector = NULL; + PKIX_ComCertSelParams *callerComCertSelParams = NULL; + PKIX_UInt32 reqKu = 0; + PKIX_List *reqEkuOids = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_BuildSelectorAndParams"); + PKIX_NULLCHECK_THREE(state, state->prevCert, state->traversedSubjNames); + + PKIX_CHECK(PKIX_PL_Cert_GetIssuer + (state->prevCert, ¤tIssuer, plContext), + PKIX_CERTGETISSUERFAILED); + + PKIX_CHECK(PKIX_PL_Cert_GetAuthorityKeyIdentifier + (state->prevCert, &authKeyId, plContext), + PKIX_CERTGETAUTHORITYKEYIDENTIFIERFAILED); + + PKIX_CHECK(PKIX_ComCertSelParams_Create(&certSelParams, plContext), + PKIX_COMCERTSELPARAMSCREATEFAILED); + + PKIX_CHECK(PKIX_ComCertSelParams_SetSubject + (certSelParams, currentIssuer, plContext), + PKIX_COMCERTSELPARAMSSETSUBJECTFAILED); + + if (authKeyId != NULL) { + PKIX_CHECK(PKIX_ComCertSelParams_SetSubjKeyIdentifier + (certSelParams, authKeyId, plContext), + PKIX_COMCERTSELPARAMSSETSUBJKEYIDENTIFIERFAILED); + } + + PKIX_INCREF(state->buildConstants.testDate); + testDate = state->buildConstants.testDate; + + PKIX_CHECK(PKIX_ComCertSelParams_SetCertificateValid + (certSelParams, testDate, plContext), + PKIX_COMCERTSELPARAMSSETCERTIFICATEVALIDFAILED); + + PKIX_CHECK(PKIX_ComCertSelParams_SetBasicConstraints + (certSelParams, state->traversedCACerts, plContext), + PKIX_COMCERTSELPARAMSSETBASICCONSTRAINTSFAILED); + + PKIX_CHECK(PKIX_ComCertSelParams_SetPathToNames + (certSelParams, state->traversedSubjNames, plContext), + PKIX_COMCERTSELPARAMSSETPATHTONAMESFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_GetTargetCertConstraints + (state->buildConstants.procParams, + &callerCertSelector, plContext), + PKIX_PROCESSINGPARAMSGETTARGETCERTCONSTRAINTSFAILED); + + if (callerCertSelector != NULL) { + + /* Get initial EKU OIDs from ComCertSelParams, if set */ + PKIX_CHECK(PKIX_CertSelector_GetCommonCertSelectorParams + (callerCertSelector, &callerComCertSelParams, plContext), + PKIX_CERTSELECTORGETCOMMONCERTSELECTORPARAMSFAILED); + + if (callerComCertSelParams != NULL) { + PKIX_CHECK(PKIX_ComCertSelParams_GetExtendedKeyUsage + (callerComCertSelParams, &reqEkuOids, plContext), + PKIX_COMCERTSELPARAMSGETEXTENDEDKEYUSAGEFAILED); + + PKIX_CHECK(PKIX_ComCertSelParams_GetKeyUsage + (callerComCertSelParams, &reqKu, plContext), + PKIX_COMCERTSELPARAMSGETEXTENDEDKEYUSAGEFAILED); + } + } + + PKIX_CHECK( + PKIX_ComCertSelParams_SetKeyUsage(certSelParams, reqKu, + plContext), + PKIX_COMCERTSELPARAMSSETKEYUSAGEFAILED); + + PKIX_CHECK( + PKIX_ComCertSelParams_SetExtendedKeyUsage(certSelParams, + reqEkuOids, + plContext), + PKIX_COMCERTSELPARAMSSETEXTKEYUSAGEFAILED); + + PKIX_CHECK(PKIX_CertSelector_Create + (NULL, NULL, &state->certSel, plContext), + PKIX_CERTSELECTORCREATEFAILED); + + PKIX_CHECK(PKIX_CertSelector_SetCommonCertSelectorParams + (state->certSel, certSelParams, plContext), + PKIX_CERTSELECTORSETCOMMONCERTSELECTORPARAMSFAILED); + + PKIX_CHECK(PKIX_List_Create(&state->candidateCerts, plContext), + PKIX_LISTCREATEFAILED); + + state->certStoreIndex = 0; + +cleanup: + PKIX_DECREF(certSelParams); + PKIX_DECREF(certSel); + PKIX_DECREF(currentIssuer); + PKIX_DECREF(authKeyId); + PKIX_DECREF(testDate); + PKIX_DECREF(reqEkuOids); + PKIX_DECREF(callerComCertSelParams); + PKIX_DECREF(callerCertSelector); + + PKIX_RETURN(BUILD); +} + +/* Match trust anchor to select params in order to find next cert. */ +static PKIX_Error* +pkix_Build_SelectCertsFromTrustAnchors( + PKIX_List *trustAnchorsList, + PKIX_ComCertSelParams *certSelParams, + PKIX_List **pMatchList, + void *plContext) +{ + unsigned int anchorIndex = 0; + PKIX_TrustAnchor *anchor = NULL; + PKIX_PL_Cert *trustedCert = NULL; + PKIX_List *matchList = NULL; + PKIX_CertSelector *certSel = NULL; + PKIX_CertSelector_MatchCallback selectorMatchCB = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_SelectCertsFromTrustAnchors"); + + PKIX_CHECK(PKIX_CertSelector_Create + (NULL, NULL, &certSel, plContext), + PKIX_CERTSELECTORCREATEFAILED); + PKIX_CHECK(PKIX_CertSelector_SetCommonCertSelectorParams + (certSel, certSelParams, plContext), + PKIX_CERTSELECTORSETCOMMONCERTSELECTORPARAMSFAILED); + PKIX_CHECK(PKIX_CertSelector_GetMatchCallback + (certSel, &selectorMatchCB, plContext), + PKIX_CERTSELECTORGETMATCHCALLBACKFAILED); + + for (anchorIndex = 0;anchorIndex < trustAnchorsList->length; anchorIndex++) { + PKIX_CHECK( + PKIX_List_GetItem(trustAnchorsList, + anchorIndex, + (PKIX_PL_Object **)&anchor, + plContext), + PKIX_LISTGETITEMFAILED); + PKIX_CHECK(PKIX_TrustAnchor_GetTrustedCert + (anchor, &trustedCert, plContext), + PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED); + pkixErrorResult = + (*selectorMatchCB)(certSel, trustedCert, plContext); + if (!pkixErrorResult) { + if (!matchList) { + PKIX_CHECK(PKIX_List_Create(&matchList, + plContext), + PKIX_LISTCREATEFAILED); + } + PKIX_CHECK( + PKIX_List_AppendItem(matchList, + (PKIX_PL_Object*)trustedCert, + plContext), + PKIX_LISTAPPENDITEMFAILED); + } else { + PKIX_DECREF(pkixErrorResult); + } + PKIX_DECREF(trustedCert); + PKIX_DECREF(anchor); + } + + *pMatchList = matchList; + matchList = NULL; + +cleanup: + PKIX_DECREF(matchList); + PKIX_DECREF(trustedCert); + PKIX_DECREF(anchor); + PKIX_DECREF(certSel); + + PKIX_RETURN(BUILD); +} + + +static PKIX_Error* +pkix_Build_RemoveDupUntrustedCerts( + PKIX_List *trustedCertList, + PKIX_List *certsFound, + void *plContext) +{ + PKIX_UInt32 trustIndex; + PKIX_PL_Cert *trustCert = NULL, *cert = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_RemoveDupUntrustedCerts"); + if (trustedCertList == NULL || certsFound == NULL) { + goto cleanup; + } + for (trustIndex = 0;trustIndex < trustedCertList->length; + trustIndex++) { + PKIX_UInt32 certIndex = 0; + PKIX_CHECK( + PKIX_List_GetItem(trustedCertList, + trustIndex, + (PKIX_PL_Object **)&trustCert, + plContext), + PKIX_LISTGETITEMFAILED); + + while (certIndex < certsFound->length) { + PKIX_Boolean result = PKIX_FALSE; + PKIX_DECREF(cert); + PKIX_CHECK( + PKIX_List_GetItem(certsFound, certIndex, + (PKIX_PL_Object **)&cert, + plContext), + PKIX_LISTGETITEMFAILED); + PKIX_CHECK( + PKIX_PL_Object_Equals((PKIX_PL_Object *)trustCert, + (PKIX_PL_Object *)cert, + &result, + plContext), + PKIX_OBJECTEQUALSFAILED); + if (!result) { + certIndex += 1; + continue; + } + PKIX_CHECK( + PKIX_List_DeleteItem(certsFound, certIndex, + plContext), + PKIX_LISTDELETEITEMFAILED); + } + PKIX_DECREF(trustCert); + } +cleanup: + PKIX_DECREF(cert); + PKIX_DECREF(trustCert); + + PKIX_RETURN(BUILD); +} + + +/* + * FUNCTION: pkix_Build_GatherCerts + * DESCRIPTION: + * + * This function traverses the CertStores in the List of CertStores contained + * in "state", using the certSelector and other parameters contained in + * "state", to obtain a List of all available Certs that satisfy the criteria. + * If a CertStore has a cache, "certSelParams" is used both to query the cache + * and, if an actual CertStore search occurred, to update the cache. (Behavior + * is undefined if "certSelParams" is different from the parameters that were + * used to initialize the certSelector in "state".) + * + * If a CertStore using non-blocking I/O returns with an indication that I/O is + * in progress and the checking has not been completed, this function stores + * platform-dependent information at "pNBIOContext". Otherwise it stores NULL + * at "pNBIOContext", and state is updated with the results of the search. + * + * PARAMETERS: + * "state" + * Address of ForwardBuilderState to be used. Must be non-NULL. + * "certSelParams" + * Address of ComCertSelParams which were used in creating the current + * CertSelector, and to be used in querying and updating any caches that + * may be associated with with the CertStores. + * "pNBIOContext" + * Address at which platform-dependent information is returned if request + * is suspended for non-blocking I/O. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +/* return NULL if wouldblock, empty list if none found, else list of found */ +static PKIX_Error * +pkix_Build_GatherCerts( + PKIX_ForwardBuilderState *state, + PKIX_ComCertSelParams *certSelParams, + void **pNBIOContext, + void *plContext) +{ + PKIX_Boolean certStoreIsCached = PKIX_FALSE; + PKIX_Boolean certStoreIsLocal = PKIX_FALSE; + PKIX_Boolean foundInCache = PKIX_FALSE; + PKIX_CertStore *certStore = NULL; + PKIX_CertStore_CertCallback getCerts = NULL; + PKIX_List *certsFound = NULL; + PKIX_List *trustedCertList = NULL; + void *nbioContext = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_GatherCerts"); + PKIX_NULLCHECK_THREE(state, certSelParams, pNBIOContext); + + nbioContext = *pNBIOContext; + *pNBIOContext = NULL; + + PKIX_DECREF(state->candidateCerts); + + while (state->certStoreIndex < state->buildConstants.numCertStores) { + + /* Get the current CertStore */ + PKIX_CHECK(PKIX_List_GetItem + (state->buildConstants.certStores, + state->certStoreIndex, + (PKIX_PL_Object **)&certStore, + plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK(PKIX_CertStore_GetLocalFlag + (certStore, &certStoreIsLocal, plContext), + PKIX_CERTSTOREGETLOCALFLAGFAILED); + + if (state->useOnlyLocal == certStoreIsLocal) { + /* If GATHERPENDING, we've already checked the cache */ + if (state->status == BUILD_GATHERPENDING) { + certStoreIsCached = PKIX_FALSE; + foundInCache = PKIX_FALSE; + } else { + PKIX_CHECK(PKIX_CertStore_GetCertStoreCacheFlag + (certStore, &certStoreIsCached, plContext), + PKIX_CERTSTOREGETCERTSTORECACHEFLAGFAILED); + + if (certStoreIsCached) { + /* + * Look for Certs in the cache, using the SubjectName as + * the key. Then the ComCertSelParams are used to filter + * for qualified certs. If none are found, then the + * certStores are queried. When we eventually add items + * to the cache, we will only add items that passed the + * ComCertSelParams filter, rather than all Certs which + * matched the SubjectName. + */ + + PKIX_CHECK(pkix_CacheCert_Lookup + (certStore, + certSelParams, + state->buildConstants.testDate, + &foundInCache, + &certsFound, + plContext), + PKIX_CACHECERTCHAINLOOKUPFAILED); + + } + } + + /* + * XXX need to verify if Cert is trusted, hence may not + * be worth it to have the Cert Cached or + * If it is trusted, don't cache, but once there is cached + * certs, we won't get certs from database any more. + * can use flag to force not getting certs from cache + */ + if (!foundInCache) { + + if (nbioContext == NULL) { + PKIX_CHECK(PKIX_CertStore_GetCertCallback + (certStore, &getCerts, plContext), + PKIX_CERTSTOREGETCERTCALLBACKFAILED); + + PKIX_CHECK(getCerts + (certStore, + state->certSel, + state->verifyNode, + &nbioContext, + &certsFound, + plContext), + PKIX_GETCERTSFAILED); + } else { + PKIX_CHECK(PKIX_CertStore_CertContinue + (certStore, + state->certSel, + state->verifyNode, + &nbioContext, + &certsFound, + plContext), + PKIX_CERTSTORECERTCONTINUEFAILED); + } + + if (certStoreIsCached && certsFound) { + + PKIX_CHECK(pkix_CacheCert_Add + (certStore, + certSelParams, + certsFound, + plContext), + PKIX_CACHECERTADDFAILED); + } + } + + /* + * getCerts returns an empty list for "NONE FOUND", + * a NULL list for "would block" + */ + if (certsFound == NULL) { + state->status = BUILD_GATHERPENDING; + *pNBIOContext = nbioContext; + goto cleanup; + } + } + + /* Are there any more certStores to query? */ + PKIX_DECREF(certStore); + ++(state->certStoreIndex); + } + + if (certsFound && certsFound->length > 1) { + PKIX_List *sorted = NULL; + + /* sort Certs to try to optimize search */ + PKIX_CHECK(pkix_Build_SortCandidateCerts + (certsFound, &sorted, plContext), + PKIX_BUILDSORTCANDIDATECERTSFAILED); + PKIX_DECREF(certsFound); + certsFound = sorted; + } + + PKIX_CHECK( + pkix_Build_SelectCertsFromTrustAnchors( + state->buildConstants.anchors, + certSelParams, &trustedCertList, + plContext), + PKIX_FAILTOSELECTCERTSFROMANCHORS); + PKIX_CHECK( + pkix_Build_RemoveDupUntrustedCerts(trustedCertList, + certsFound, + plContext), + PKIX_REMOVEDUPUNTRUSTEDCERTSFAILED); + + PKIX_CHECK( + pkix_List_MergeLists(trustedCertList, + certsFound, + &state->candidateCerts, + plContext), + PKIX_LISTMERGEFAILED); + + /* No, return the list we have gathered */ + PKIX_CHECK(PKIX_List_GetLength + (state->candidateCerts, &state->numCerts, plContext), + PKIX_LISTGETLENGTHFAILED); + + state->certIndex = 0; + +cleanup: + PKIX_DECREF(trustedCertList); + PKIX_DECREF(certStore); + PKIX_DECREF(certsFound); + + PKIX_RETURN(BUILD); +} + +/* + * FUNCTION: pkix_Build_UpdateDate + * DESCRIPTION: + * + * This function updates the validityDate contained in "state", for the current + * CertChain contained in "state", to include the validityDate of the + * candidateCert contained in "state". The validityDate of a chain is the + * earliest of all the notAfter dates contained in the respective Certificates. + * + * PARAMETERS: + * "state" + * Address of ForwardBuilderState to be used. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_Build_UpdateDate( + PKIX_ForwardBuilderState *state, + void *plContext) +{ + PKIX_Boolean canBeCached = PKIX_FALSE; + PKIX_Int32 comparison = 0; + PKIX_PL_Date *notAfter = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_UpdateDate"); + PKIX_NULLCHECK_ONE(state); + + PKIX_CHECK(PKIX_PL_Cert_GetCacheFlag + (state->candidateCert, &canBeCached, plContext), + PKIX_CERTGETCACHEFLAGFAILED); + + state->canBeCached = state->canBeCached && canBeCached; + if (state->canBeCached == PKIX_TRUE) { + + /* + * So far, all certs can be cached. Update cert + * chain validity time, which is the earliest of + * all certs' notAfter times. + */ + PKIX_CHECK(PKIX_PL_Cert_GetValidityNotAfter + (state->candidateCert, ¬After, plContext), + PKIX_CERTGETVALIDITYNOTAFTERFAILED); + + if (state->validityDate == NULL) { + state->validityDate = notAfter; + notAfter = NULL; + } else { + PKIX_CHECK(PKIX_PL_Object_Compare + ((PKIX_PL_Object *)state->validityDate, + (PKIX_PL_Object *)notAfter, + &comparison, + plContext), + PKIX_OBJECTCOMPARATORFAILED); + if (comparison > 0) { + PKIX_DECREF(state->validityDate); + state->validityDate = notAfter; + notAfter = NULL; + } + } + } + +cleanup: + + PKIX_DECREF(notAfter); + + PKIX_RETURN(BUILD); +} + +/* Prepare 'state' for the AIA round. */ +static void +pkix_PrepareForwardBuilderStateForAIA( + PKIX_ForwardBuilderState *state) +{ + PORT_Assert(state->useOnlyLocal == PKIX_TRUE); + state->useOnlyLocal = PKIX_FALSE; + state->certStoreIndex = 0; + state->numFanout = state->buildConstants.maxFanout; + state->status = BUILD_TRYAIA; +} + +extern SECStatus +isIssuerCertAllowedAtCertIssuanceTime(CERTCertificate *issuerCert, + CERTCertificate *referenceCert); + +/* + * FUNCTION: pkix_BuildForwardDepthFirstSearch + * DESCRIPTION: + * + * This function performs a depth first search in the "forward" direction (from + * the target Cert to the trust anchor). A non-NULL targetCert must be stored + * in the ForwardBuilderState before this function is called. It is not written + * recursively since execution may be suspended in in any of several places + * pending completion of non-blocking I/O. This iterative structure makes it + * much easier to resume where it left off. + * + * Since the nature of the search is recursive, the recursion is handled by + * chaining states. That is, each new step involves creating a new + * ForwardBuilderState linked to its predecessor. If a step turns out to be + * fruitless, the state of the predecessor is restored and the next alternative + * is tried. When a search is successful, values needed from the last state + * (canBeCached and validityDate) are copied to the state provided by the + * caller, so that the caller can retrieve those values. + * + * There are three return arguments, the NBIOContext, the ValidateResult and + * the ForwardBuilderState. If NBIOContext is non-NULL, it means the search is + * suspended until the results of a non-blocking IO become available. The + * caller may wait for the completion using platform-dependent methods and then + * call this function again, allowing it to resume the search. If NBIOContext + * is NULL and the ValidateResult is non-NULL, it means the search has + * concluded successfully. If the NBIOContext is NULL but the ValidateResult is + * NULL, it means the search was unsuccessful. + * + * This function performs several steps at each node in the constructed chain: + * + * 1) It retrieves Certs from the registered CertStores that match the + * criteria established by the ForwardBuilderState pointed to by "state", such + * as a subject name matching the issuer name of the previous Cert. If there + * are no matching Certs, the function returns to the previous, or "parent", + * state and tries to continue the chain building with another of the Certs + * obtained from the CertStores as possible issuers for that parent Cert. + * + * 2) For each candidate Cert returned by the CertStores, this function checks + * whether the Cert is valid. If it is trusted, this function checks whether + * this Cert might serve as a TrustAnchor for a complete chain. + * + * 3) It determines whether this Cert, in conjunction with any of the + * TrustAnchors, might complete a chain. A complete chain, from this or the + * preceding step, is checked to see whether it is valid as a complete + * chain, including the checks that cannot be done in the forward direction. + * + * 4) If this Cert chains successfully, but is not a complete chain, that is, + * we have not reached a trusted Cert, a new ForwardBuilderState is created + * with this Cert as the immediate predecessor, and we continue in step (1), + * attempting to get Certs from the CertStores with this Certs "issuer" as + * their subject. + * + * 5) If an entire chain validates successfully, then we are done. A + * ValidateResult is created containing the Public Key of the target + * certificate (including DSA parameter inheritance, if any) and the + * PolicyNode representing the policy tree output by the validation algorithm, + * and stored at pValResult, and the function exits returning NULL. + * + * 5) If the entire chain does not validate successfully, the algorithm + * discards the latest Cert and continues in step 2 with the next candidate + * Cert, backing up to a parent state when no more possibilities exist at a + * given level, and returning failure when we try to back up but discover we + * are at the top level. + * + * PARAMETERS: + * "pNBIOContext" + * Address at which platform-dependent information is returned if building + * is suspended for non-blocking I/O. Must be non-NULL. + * "pState" + * Address at which input ForwardBuilderState is found, and at which output + * ForwardBuilderState is stored. Must be non-NULL. + * "pValResult" + * Address at which the ValidateResult is stored. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_BuildForwardDepthFirstSearch( + void **pNBIOContext, + PKIX_ForwardBuilderState *state, + PKIX_ValidateResult **pValResult, + void *plContext) +{ + PKIX_Boolean outOfOptions = PKIX_FALSE; + PKIX_Boolean trusted = PKIX_FALSE; + PKIX_Boolean isSelfIssued = PKIX_FALSE; + PKIX_Boolean canBeCached = PKIX_FALSE; + PKIX_Boolean ioPending = PKIX_FALSE; + PKIX_PL_Date *validityDate = NULL; + PKIX_PL_Date *currTime = NULL; + PKIX_Int32 childTraversedCACerts = 0; + PKIX_UInt32 numSubjectNames = 0; + PKIX_UInt32 numChained = 0; + PKIX_Int32 cmpTimeResult = 0; + PKIX_UInt32 i = 0; + PKIX_UInt32 certsSoFar = 0; + PKIX_List *childTraversedSubjNames = NULL; + PKIX_List *subjectNames = NULL; + PKIX_List *unfilteredCerts = NULL; + PKIX_List *filteredCerts = NULL; + PKIX_PL_Object *subjectName = NULL; + PKIX_ValidateResult *valResult = NULL; + PKIX_ForwardBuilderState *childState = NULL; + PKIX_ForwardBuilderState *parentState = NULL; + PKIX_PL_Object *revCheckerState = NULL; + PKIX_ComCertSelParams *certSelParams = NULL; + PKIX_TrustAnchor *trustAnchor = NULL; + PKIX_PL_Cert *trustedCert = NULL; + PKIX_PL_Cert *targetCert = NULL; + PKIX_VerifyNode *verifyNode = NULL; + PKIX_Error *verifyError = NULL; + PKIX_Error *finalError = NULL; + void *nbio = NULL; + PKIX_UInt32 numIterations = 0; + + PKIX_ENTER(BUILD, "pkix_BuildForwardDepthFirstSearch"); + PKIX_NULLCHECK_THREE(pNBIOContext, state, pValResult); + + nbio = *pNBIOContext; + *pNBIOContext = NULL; + PKIX_INCREF(state->validityDate); + validityDate = state->validityDate; + canBeCached = state->canBeCached; + PKIX_DECREF(*pValResult); + targetCert = state->buildConstants.targetCert; + + /* + * We return if successful; if we fall off the end + * of this "while" clause our search has failed. + */ + while (outOfOptions == PKIX_FALSE) { + /* + * The maximum number of iterations works around a bug that + * causes this while loop to never exit when AIA and cross + * certificates are involved. See bug xxxxx. + */ + if (numIterations++ > 250) + PKIX_ERROR(PKIX_TIMECONSUMEDEXCEEDSRESOURCELIMITS); + + if (state->buildConstants.maxTime != 0) { + PKIX_DECREF(currTime); + PKIX_CHECK(PKIX_PL_Date_Create_UTCTime + (NULL, &currTime, plContext), + PKIX_DATECREATEUTCTIMEFAILED); + + PKIX_CHECK(PKIX_PL_Object_Compare + ((PKIX_PL_Object *)state->buildConstants.timeLimit, + (PKIX_PL_Object *)currTime, + &cmpTimeResult, + plContext), + PKIX_OBJECTCOMPARATORFAILED); + + if (cmpTimeResult < 0) { + if (state->verifyNode != NULL) { + PKIX_ERROR_CREATE + (BUILD, + PKIX_TIMECONSUMEDEXCEEDSRESOURCELIMITS, + verifyError); + PKIX_CHECK_FATAL(pkix_VerifyNode_SetError + (state->verifyNode, + verifyError, + plContext), + PKIX_VERIFYNODESETERRORFAILED); + PKIX_DECREF(finalError); + finalError = verifyError; + verifyError = NULL; + } + /* Even if we logged error, we still have to abort */ + PKIX_ERROR(PKIX_TIMECONSUMEDEXCEEDSRESOURCELIMITS); + } + } + + if (state->status == BUILD_INITIAL) { + + PKIX_CHECK(pkix_Build_BuildSelectorAndParams(state, plContext), + PKIX_BUILDBUILDSELECTORANDPARAMSFAILED); + + /* + * If the caller supplied a partial certChain (hintCerts) try + * the next one from that List before we go to the certStores. + */ + if (state->buildConstants.numHintCerts > 0) { + /* How many Certs does our trust chain have already? */ + PKIX_CHECK(PKIX_List_GetLength + (state->trustChain, &certsSoFar, plContext), + PKIX_LISTGETLENGTHFAILED); + + /* That includes the target Cert. Don't count it. */ + certsSoFar--; + + /* Are we still within range of the partial chain? */ + if (certsSoFar >= state->buildConstants.numHintCerts) { + state->status = BUILD_TRYAIA; + } else { + /* + * If we already have n certs, we want the n+1th + * (i.e., index = n) from the list of hints. + */ + PKIX_DECREF(state->candidateCert); + PKIX_CHECK(PKIX_List_GetItem + (state->buildConstants.hintCerts, + certsSoFar, + (PKIX_PL_Object **)&state->candidateCert, + plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (state->candidateCerts, + (PKIX_PL_Object *)state->candidateCert, + plContext), + PKIX_LISTAPPENDITEMFAILED); + + state->numCerts = 1; + state->usingHintCerts = PKIX_TRUE; + state->status = BUILD_CERTVALIDATING; + } + } else { + state->status = BUILD_TRYAIA; + } + + } + + if (state->status == BUILD_TRYAIA) { + if (state->useOnlyLocal == PKIX_TRUE) { + state->status = BUILD_COLLECTINGCERTS; + } else { + state->status = BUILD_AIAPENDING; + } + } + + if (state->status == BUILD_AIAPENDING && + state->buildConstants.aiaMgr) { + pkixErrorResult = PKIX_PL_AIAMgr_GetAIACerts + (state->buildConstants.aiaMgr, + state->prevCert, + &nbio, + &unfilteredCerts, + plContext); + + if (nbio != NULL) { + /* IO still pending, resume later */ + *pNBIOContext = nbio; + goto cleanup; + } + state->numCerts = 0; + if (pkixErrorResult) { + pkixErrorClass = pkixErrorResult->errClass; + if (pkixErrorClass == PKIX_FATAL_ERROR) { + goto fatal; + } + PKIX_DECREF(finalError); + finalError = pkixErrorResult; + pkixErrorResult = NULL; + if (state->verifyNode != NULL) { + /* state->verifyNode is the object that contains a list + * of verifyNodes. verifyNodes contains cert chain + * build failures that occurred on this level of chain + * building. Here, creating new verify node + * to log the failure and adding it to the list. */ + PKIX_CHECK_FATAL(pkix_VerifyNode_Create + (state->prevCert, + 0, NULL, + &verifyNode, + plContext), + PKIX_VERIFYNODECREATEFAILED); + PKIX_CHECK_FATAL(pkix_VerifyNode_SetError + (verifyNode, finalError, plContext), + PKIX_VERIFYNODESETERRORFAILED); + PKIX_CHECK_FATAL(pkix_VerifyNode_AddToTree + (state->verifyNode, + verifyNode, + plContext), + PKIX_VERIFYNODEADDTOTREEFAILED); + PKIX_DECREF(verifyNode); + } + } +#ifdef PKIX_BUILDDEBUG + /* Turn this on to trace the List of Certs, before CertSelect */ + { + PKIX_PL_String *unString; + char *unAscii; + PKIX_UInt32 length; + PKIX_TOSTRING + ((PKIX_PL_Object*)unfilteredCerts, + &unString, + plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_CHECK(PKIX_PL_String_GetEncoded + (unString, + PKIX_ESCASCII, + (void **)&unAscii, + &length, + plContext), + PKIX_STRINGGETENCODEDFAILED); + + PKIX_DEBUG_ARG + ("unfilteredCerts = %s\n", unAscii); + PKIX_DECREF(unString); + PKIX_FREE(unAscii); + } +#endif + + /* Note: Certs winnowed here don't get into VerifyTree. */ + if (unfilteredCerts) { + PKIX_CHECK(pkix_CertSelector_Select + (state->certSel, + unfilteredCerts, + &filteredCerts, + plContext), + PKIX_CERTSELECTORSELECTFAILED); + + PKIX_DECREF(unfilteredCerts); + + PKIX_CHECK(PKIX_List_GetLength + (filteredCerts, &(state->numCerts), plContext), + PKIX_LISTGETLENGTHFAILED); + +#ifdef PKIX_BUILDDEBUG + /* Turn this on to trace the List of Certs, after CertSelect */ + { + PKIX_PL_String *unString; + char *unAscii; + PKIX_UInt32 length; + PKIX_TOSTRING + ((PKIX_PL_Object*)filteredCerts, + &unString, + plContext, + PKIX_OBJECTTOSTRINGFAILED); + + PKIX_CHECK(PKIX_PL_String_GetEncoded + (unString, + PKIX_ESCASCII, + (void **)&unAscii, + &length, + plContext), + PKIX_STRINGGETENCODEDFAILED); + + PKIX_DEBUG_ARG("filteredCerts = %s\n", unAscii); + PKIX_DECREF(unString); + PKIX_FREE(unAscii); + } +#endif + + PKIX_DECREF(state->candidateCerts); + state->candidateCerts = filteredCerts; + state->certIndex = 0; + filteredCerts = NULL; + } + + /* Are there any Certs to try? */ + if (state->numCerts > 0) { + state->status = BUILD_CERTVALIDATING; + } else { + state->status = BUILD_COLLECTINGCERTS; + } + } + + PKIX_DECREF(certSelParams); + PKIX_CHECK(PKIX_CertSelector_GetCommonCertSelectorParams + (state->certSel, &certSelParams, plContext), + PKIX_CERTSELECTORGETCOMMONCERTSELECTORPARAMSFAILED); + + /* **** Querying the CertStores ***** */ + if ((state->status == BUILD_COLLECTINGCERTS) || + (state->status == BUILD_GATHERPENDING)) { + +#if PKIX_FORWARDBUILDERSTATEDEBUG + PKIX_CHECK(pkix_ForwardBuilderState_DumpState + (state, plContext), + PKIX_FORWARDBUILDERSTATEDUMPSTATEFAILED); +#endif + + PKIX_CHECK(pkix_Build_GatherCerts + (state, certSelParams, &nbio, plContext), + PKIX_BUILDGATHERCERTSFAILED); + + if (nbio != NULL) { + /* IO still pending, resume later */ + *pNBIOContext = nbio; + goto cleanup; + } + + /* Are there any Certs to try? */ + if (state->numCerts > 0) { + state->status = BUILD_CERTVALIDATING; + } else { + state->status = BUILD_ABANDONNODE; + } + } + + /* ****Phase 2 - Chain building***** */ + +#if PKIX_FORWARDBUILDERSTATEDEBUG + PKIX_CHECK(pkix_ForwardBuilderState_DumpState(state, plContext), + PKIX_FORWARDBUILDERSTATEDUMPSTATEFAILED); +#endif + + if (state->status == BUILD_CERTVALIDATING) { + PKIX_DECREF(state->candidateCert); + PKIX_CHECK(PKIX_List_GetItem + (state->candidateCerts, + state->certIndex, + (PKIX_PL_Object **)&(state->candidateCert), + plContext), + PKIX_LISTGETITEMFAILED); + + if (isIssuerCertAllowedAtCertIssuanceTime( + state->candidateCert->nssCert, targetCert->nssCert) + != SECSuccess) { + PKIX_ERROR(PKIX_CERTISBLACKLISTEDATISSUANCETIME); + } + + if ((state->verifyNode) != NULL) { + PKIX_CHECK_FATAL(pkix_VerifyNode_Create + (state->candidateCert, + 0, + NULL, + &verifyNode, + plContext), + PKIX_VERIFYNODECREATEFAILED); + } + + /* If failure, this function sets Error in verifyNode */ + verifyError = pkix_Build_VerifyCertificate + (state, + state->buildConstants.userCheckers, + &trusted, + verifyNode, + plContext); + + if (verifyError) { + pkixTempErrorReceived = PKIX_TRUE; + pkixErrorClass = verifyError->errClass; + if (pkixErrorClass == PKIX_FATAL_ERROR) { + pkixErrorResult = verifyError; + verifyError = NULL; + goto fatal; + } + } + + if (PKIX_ERROR_RECEIVED) { + if (state->verifyNode != NULL) { + PKIX_CHECK_FATAL(pkix_VerifyNode_SetError + (verifyNode, verifyError, plContext), + PKIX_VERIFYNODESETERRORFAILED); + PKIX_CHECK_FATAL(pkix_VerifyNode_AddToTree + (state->verifyNode, + verifyNode, + plContext), + PKIX_VERIFYNODEADDTOTREEFAILED); + PKIX_DECREF(verifyNode); + } + pkixTempErrorReceived = PKIX_FALSE; + PKIX_DECREF(finalError); + finalError = verifyError; + verifyError = NULL; + if (state->certLoopingDetected) { + PKIX_ERROR + (PKIX_LOOPDISCOVEREDDUPCERTSNOTALLOWED); + } + state->status = BUILD_GETNEXTCERT; + } else { + state->status = BUILD_DATEPREP; + } + } + + if (state->status == BUILD_DATEPREP) { + /* Keep track of whether this chain can be cached */ + PKIX_CHECK(pkix_Build_UpdateDate(state, plContext), + PKIX_BUILDUPDATEDATEFAILED); + + canBeCached = state->canBeCached; + PKIX_DECREF(validityDate); + PKIX_INCREF(state->validityDate); + validityDate = state->validityDate; + if (trusted == PKIX_TRUE) { + state->status = BUILD_CHECKTRUSTED; + } else { + state->status = BUILD_ADDTOCHAIN; + } + } + + if (state->status == BUILD_CHECKTRUSTED) { + + /* + * If this cert is trusted, try to validate the entire + * chain using this certificate as trust anchor. + */ + PKIX_CHECK(PKIX_TrustAnchor_CreateWithCert + (state->candidateCert, + &trustAnchor, + plContext), + PKIX_TRUSTANCHORCREATEWITHCERTFAILED); + + PKIX_CHECK(pkix_Build_ValidationCheckers + (state, + state->trustChain, + trustAnchor, + PKIX_FALSE, /* do not add eku checker + * since eku was already + * checked */ + plContext), + PKIX_BUILDVALIDATIONCHECKERSFAILED); + + state->status = BUILD_CHECKTRUSTED2; + } + + if (state->status == BUILD_CHECKTRUSTED2) { + verifyError = + pkix_Build_ValidateEntireChain(state, + trustAnchor, + &nbio, &valResult, + verifyNode, + plContext); + if (nbio != NULL) { + /* IO still pending, resume later */ + goto cleanup; + } else { + /* checking the error for fatal status */ + if (verifyError) { + pkixTempErrorReceived = PKIX_TRUE; + pkixErrorClass = verifyError->errClass; + if (pkixErrorClass == PKIX_FATAL_ERROR) { + pkixErrorResult = verifyError; + verifyError = NULL; + goto fatal; + } + } + if (state->verifyNode != NULL) { + PKIX_CHECK_FATAL(pkix_VerifyNode_AddToTree + (state->verifyNode, + verifyNode, + plContext), + PKIX_VERIFYNODEADDTOTREEFAILED); + PKIX_DECREF(verifyNode); + } + if (!PKIX_ERROR_RECEIVED) { + *pValResult = valResult; + valResult = NULL; + /* Change state so IsIOPending is FALSE */ + state->status = BUILD_CHECKTRUSTED; + goto cleanup; + } + PKIX_DECREF(finalError); + finalError = verifyError; + verifyError = NULL; + /* Reset temp error that was set by + * PKIX_CHECK_ONLY_FATAL and continue */ + pkixTempErrorReceived = PKIX_FALSE; + PKIX_DECREF(trustAnchor); + } + + /* + * If chain doesn't validate with a trusted Cert, + * adding more Certs to it can't help. + */ + if (state->certLoopingDetected) { + PKIX_DECREF(verifyError); + PKIX_ERROR_CREATE(BUILD, + PKIX_LOOPDISCOVEREDDUPCERTSNOTALLOWED, + verifyError); + PKIX_CHECK_FATAL( + pkix_VerifyNode_SetError(state->verifyNode, + verifyError, + plContext), + PKIX_VERIFYNODESETERRORFAILED); + PKIX_DECREF(verifyError); + } + state->status = BUILD_GETNEXTCERT; + } + + /* + * This Cert was not trusted. Add it to our chain, and + * continue building. If we don't reach a trust anchor, + * we'll take it off later and continue without it. + */ + if (state->status == BUILD_ADDTOCHAIN) { + PKIX_CHECK(PKIX_List_AppendItem + (state->trustChain, + (PKIX_PL_Object *)state->candidateCert, + plContext), + PKIX_LISTAPPENDITEMFAILED); + + state->status = BUILD_EXTENDCHAIN; + } + + if (state->status == BUILD_EXTENDCHAIN) { + + /* Check whether we are allowed to extend the chain */ + if ((state->buildConstants.maxDepth != 0) && + (state->numDepth <= 1)) { + + if (state->verifyNode != NULL) { + PKIX_ERROR_CREATE + (BUILD, + PKIX_DEPTHWOULDEXCEEDRESOURCELIMITS, + verifyError); + PKIX_CHECK_FATAL(pkix_VerifyNode_SetError + (verifyNode, verifyError, plContext), + PKIX_VERIFYNODESETERRORFAILED); + PKIX_CHECK_FATAL(pkix_VerifyNode_AddToTree + (state->verifyNode, verifyNode, plContext), + PKIX_VERIFYNODEADDTOTREEFAILED); + PKIX_DECREF(verifyNode); + PKIX_DECREF(finalError); + finalError = verifyError; + verifyError = NULL; + } + /* Even if error logged, still need to abort */ + PKIX_ERROR(PKIX_DEPTHWOULDEXCEEDRESOURCELIMITS); + } + + PKIX_CHECK(pkix_IsCertSelfIssued + (state->candidateCert, &isSelfIssued, plContext), + PKIX_ISCERTSELFISSUEDFAILED); + + PKIX_CHECK(PKIX_PL_Object_Duplicate + ((PKIX_PL_Object *)state->traversedSubjNames, + (PKIX_PL_Object **)&childTraversedSubjNames, + plContext), + PKIX_OBJECTDUPLICATEFAILED); + + if (isSelfIssued) { + childTraversedCACerts = state->traversedCACerts; + } else { + childTraversedCACerts = state->traversedCACerts + 1; + + PKIX_CHECK(PKIX_PL_Cert_GetAllSubjectNames + (state->candidateCert, + &subjectNames, + plContext), + PKIX_CERTGETALLSUBJECTNAMESFAILED); + + if (subjectNames) { + PKIX_CHECK(PKIX_List_GetLength + (subjectNames, + &numSubjectNames, + plContext), + PKIX_LISTGETLENGTHFAILED); + + } else { + numSubjectNames = 0; + } + + for (i = 0; i < numSubjectNames; i++) { + PKIX_CHECK(PKIX_List_GetItem + (subjectNames, + i, + &subjectName, + plContext), + PKIX_LISTGETITEMFAILED); + PKIX_NULLCHECK_ONE + (state->traversedSubjNames); + PKIX_CHECK(PKIX_List_AppendItem + (state->traversedSubjNames, + subjectName, + plContext), + PKIX_LISTAPPENDITEMFAILED); + PKIX_DECREF(subjectName); + } + PKIX_DECREF(subjectNames); + } + + PKIX_CHECK(pkix_ForwardBuilderState_Create + (childTraversedCACerts, + state->buildConstants.maxFanout, + state->numDepth - 1, + canBeCached, + validityDate, + state->candidateCert, + childTraversedSubjNames, + state->trustChain, + state, + &childState, + plContext), + PKIX_FORWARDBUILDSTATECREATEFAILED); + + PKIX_DECREF(childTraversedSubjNames); + PKIX_DECREF(certSelParams); + childState->verifyNode = verifyNode; + verifyNode = NULL; + PKIX_DECREF(state); + state = childState; /* state->status == BUILD_INITIAL */ + childState = NULL; + continue; /* with while (!outOfOptions) */ + } + + if (state->status == BUILD_GETNEXTCERT) { + pkixTempErrorReceived = PKIX_FALSE; + PKIX_DECREF(state->candidateCert); + + /* + * If we were using a Cert from the callier-supplied partial + * chain, delete it and go to the certStores. + */ + if (state->usingHintCerts == PKIX_TRUE) { + PKIX_DECREF(state->candidateCerts); + PKIX_CHECK(PKIX_List_Create + (&state->candidateCerts, plContext), + PKIX_LISTCREATEFAILED); + + state->numCerts = 0; + state->usingHintCerts = PKIX_FALSE; + state->status = BUILD_TRYAIA; + continue; + } else if (++(state->certIndex) < (state->numCerts)) { + if ((state->buildConstants.maxFanout != 0) && + (--(state->numFanout) == 0)) { + + if (state->verifyNode != NULL) { + PKIX_ERROR_CREATE + (BUILD, + PKIX_FANOUTEXCEEDSRESOURCELIMITS, + verifyError); + PKIX_CHECK_FATAL + (pkix_VerifyNode_SetError + (state->verifyNode, + verifyError, + plContext), + PKIX_VERIFYNODESETERRORFAILED); + PKIX_DECREF(finalError); + finalError = verifyError; + verifyError = NULL; + } + /* Even if error logged, still need to abort */ + PKIX_ERROR + (PKIX_FANOUTEXCEEDSRESOURCELIMITS); + } + state->status = BUILD_CERTVALIDATING; + continue; + } + } + + /* + * Adding the current cert to the chain didn't help. If our search + * has been restricted to local certStores, try opening up the + * search and see whether that helps. Otherwise, back up to the + * parent cert, and see if there are any more to try. + */ + if (state->useOnlyLocal == PKIX_TRUE) { + pkix_PrepareForwardBuilderStateForAIA(state); + } else do { + if (state->parentState == NULL) { + /* We are at the top level, and can't back up! */ + outOfOptions = PKIX_TRUE; + } else { + /* + * Try the next cert, if any, for this parent. + * Otherwise keep backing up until we reach a + * parent with more certs to try. + */ + PKIX_CHECK(PKIX_List_GetLength + (state->trustChain, &numChained, plContext), + PKIX_LISTGETLENGTHFAILED); + PKIX_CHECK(PKIX_List_DeleteItem + (state->trustChain, numChained - 1, plContext), + PKIX_LISTDELETEITEMFAILED); + + /* local and aia fetching returned no good certs. + * Creating a verify node in the parent that tells + * us this. */ + if (!state->verifyNode) { + PKIX_CHECK_FATAL( + pkix_VerifyNode_Create(state->prevCert, + 0, NULL, + &state->verifyNode, + plContext), + PKIX_VERIFYNODECREATEFAILED); + } + /* Updating the log with the error. */ + PKIX_DECREF(verifyError); + PKIX_ERROR_CREATE(BUILD, PKIX_SECERRORUNKNOWNISSUER, + verifyError); + PKIX_CHECK_FATAL( + pkix_VerifyNode_SetError(state->verifyNode, + verifyError, + plContext), + PKIX_VERIFYNODESETERRORFAILED); + PKIX_DECREF(verifyError); + + PKIX_INCREF(state->parentState); + parentState = state->parentState; + PKIX_DECREF(verifyNode); + verifyNode = state->verifyNode; + state->verifyNode = NULL; + PKIX_DECREF(state); + state = parentState; + parentState = NULL; + if (state->verifyNode != NULL && verifyNode) { + PKIX_CHECK_FATAL(pkix_VerifyNode_AddToTree + (state->verifyNode, + verifyNode, + plContext), + PKIX_VERIFYNODEADDTOTREEFAILED); + PKIX_DECREF(verifyNode); + } + PKIX_DECREF(validityDate); + PKIX_INCREF(state->validityDate); + validityDate = state->validityDate; + canBeCached = state->canBeCached; + + /* Are there any more Certs to try? */ + if (++(state->certIndex) < (state->numCerts)) { + state->status = BUILD_CERTVALIDATING; + PKIX_DECREF(state->candidateCert); + break; + } + if (state->useOnlyLocal == PKIX_TRUE) { + /* Clean up and go for AIA round. */ + pkix_PrepareForwardBuilderStateForAIA(state); + break; + } + } + PKIX_DECREF(state->candidateCert); + } while (outOfOptions == PKIX_FALSE); + + } /* while (outOfOptions == PKIX_FALSE) */ + +cleanup: + + if (pkixErrorClass == PKIX_FATAL_ERROR) { + goto fatal; + } + + /* verifyNode should be equal to NULL at this point. Assert it. + * Temporarelly use verifyError to store an error ref to which we + * have in pkixErrorResult. This is done to prevent error cloberring + * while using macros below. */ + PORT_Assert(verifyError == NULL); + verifyError = pkixErrorResult; + + /* + * We were called with an initialState that had no parent. If we are + * returning with an error or with a result, we must destroy any state + * that we created (any state with a parent). + */ + + PKIX_CHECK_FATAL(pkix_ForwardBuilderState_IsIOPending + (state, &ioPending, plContext), + PKIX_FORWARDBUILDERSTATEISIOPENDINGFAILED); + + if (ioPending == PKIX_FALSE) { + while (state->parentState) { + PKIX_INCREF(state->parentState); + parentState = state->parentState; + PKIX_DECREF(verifyNode); + verifyNode = state->verifyNode; + state->verifyNode = NULL; + PKIX_DECREF(state); + state = parentState; + parentState = NULL; + if (state->verifyNode != NULL && verifyNode) { + PKIX_CHECK_FATAL(pkix_VerifyNode_AddToTree + (state->verifyNode, + verifyNode, + plContext), + PKIX_VERIFYNODEADDTOTREEFAILED); + PKIX_DECREF(verifyNode); + } + } + state->canBeCached = canBeCached; + PKIX_DECREF(state->validityDate); + state->validityDate = validityDate; + validityDate = NULL; + } + if (!*pValResult && !verifyError) { + if (!finalError) { + PKIX_CHECK_FATAL( + pkix_VerifyNode_FindError(state->verifyNode, + &finalError, + plContext), + PKIX_VERIFYNODEFINDERRORFAILED); + } + if (finalError) { + pkixErrorResult = finalError; + pkixErrorCode = PKIX_BUILDFORWARDDEPTHFIRSTSEARCHFAILED; + finalError = NULL; + goto fatal; + } + pkixErrorCode = PKIX_SECERRORUNKNOWNISSUER; + pkixErrorReceived = PKIX_TRUE; + PKIX_ERROR_CREATE(BUILD, PKIX_SECERRORUNKNOWNISSUER, + verifyError); + PKIX_CHECK_FATAL( + pkix_VerifyNode_SetError(state->verifyNode, verifyError, + plContext), + PKIX_VERIFYNODESETERRORFAILED); + } else { + pkixErrorResult = verifyError; + verifyError = NULL; + } + +fatal: + if (state->parentState) { + /* parentState in "state" object should be NULL at this point. + * If itn't, that means that we got fatal error(we have jumped to + * "fatal" label) and we should destroy all state except the top one. */ + while (state->parentState) { + PKIX_Error *error = NULL; + PKIX_ForwardBuilderState *prntState = state->parentState; + /* Dumb: need to increment parentState to avoid destruction + * of "build constants"(they get destroyed when parentState is + * set to NULL. */ + PKIX_INCREF(prntState); + error = PKIX_PL_Object_DecRef((PKIX_PL_Object*)state, plContext); + if (error) { + PKIX_PL_Object_DecRef((PKIX_PL_Object*)error, plContext); + } + /* No need to decref the parent state. It was already done by + * pkix_ForwardBuilderState_Destroy function. */ + state = prntState; + } + } + PKIX_DECREF(parentState); + PKIX_DECREF(childState); + PKIX_DECREF(valResult); + PKIX_DECREF(verifyError); + PKIX_DECREF(finalError); + PKIX_DECREF(verifyNode); + PKIX_DECREF(childTraversedSubjNames); + PKIX_DECREF(certSelParams); + PKIX_DECREF(subjectNames); + PKIX_DECREF(subjectName); + PKIX_DECREF(trustAnchor); + PKIX_DECREF(validityDate); + PKIX_DECREF(revCheckerState); + PKIX_DECREF(currTime); + PKIX_DECREF(filteredCerts); + PKIX_DECREF(unfilteredCerts); + PKIX_DECREF(trustedCert); + + PKIX_RETURN(BUILD); +} + +/* + * FUNCTION: pkix_Build_CheckInCache + * DESCRIPTION: + * + * The function tries to locate a chain for a cert in the cert chain cache. + * If found, the chain goes through revocation chacking and returned back to + * caller. Chains that fail revocation check get removed from cache. + * + * PARAMETERS: + * "state" + * Address of ForwardBuilderState to be used. Must be non-NULL. + * "pBuildResult" + * Address at which the BuildResult is stored, after a successful build. + * Must be non-NULL. + * "pNBIOContext" + * Address at which the NBIOContext is stored indicating whether the + * validation is complete. Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error* +pkix_Build_CheckInCache( + PKIX_ForwardBuilderState *state, + PKIX_BuildResult **pBuildResult, + void **pNBIOContext, + void *plContext) +{ + PKIX_PL_Cert *targetCert = NULL; + PKIX_List *anchors = NULL; + PKIX_PL_Date *testDate = NULL; + PKIX_BuildResult *buildResult = NULL; + PKIX_ValidateResult *valResult = NULL; + PKIX_Error *buildError = NULL; + PKIX_TrustAnchor *matchingAnchor = NULL; + PKIX_PL_Cert *trustedCert = NULL; + PKIX_List *certList = NULL; + PKIX_Boolean cacheHit = PKIX_FALSE; + PKIX_Boolean trusted = PKIX_FALSE; + PKIX_Boolean stillValid = PKIX_FALSE; + void *nbioContext = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_CheckInCache"); + + nbioContext = *pNBIOContext; + *pNBIOContext = NULL; + + targetCert = state->buildConstants.targetCert; + anchors = state->buildConstants.anchors; + testDate = state->buildConstants.testDate; + + /* Check whether this cert verification has been cached. */ + PKIX_CHECK(pkix_CacheCertChain_Lookup + (targetCert, + anchors, + testDate, + &cacheHit, + &buildResult, + plContext), + PKIX_CACHECERTCHAINLOOKUPFAILED); + + if (!cacheHit) { + goto cleanup; + } + + /* + * We found something in cache. Verify that the anchor + * cert is still trusted, + */ + PKIX_CHECK(PKIX_BuildResult_GetValidateResult + (buildResult, &valResult, plContext), + PKIX_BUILDRESULTGETVALIDATERESULTFAILED); + + PKIX_CHECK(PKIX_ValidateResult_GetTrustAnchor + (valResult, &matchingAnchor, plContext), + PKIX_VALIDATERESULTGETTRUSTANCHORFAILED); + + PKIX_DECREF(valResult); + + PKIX_CHECK(PKIX_TrustAnchor_GetTrustedCert + (matchingAnchor, &trustedCert, plContext), + PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED); + + if (anchors && state->buildConstants.numAnchors) { + /* Check if it is one of the trust anchors */ + PKIX_CHECK( + pkix_List_Contains(anchors, + (PKIX_PL_Object *)matchingAnchor, + &trusted, + plContext), + PKIX_LISTCONTAINSFAILED); + } + + if ((!trusted && !state->buildConstants.trustOnlyUserAnchors) || + !state->buildConstants.numAnchors) { + /* If it is not one of the trust anchors and the trust anchors + * are supplemental, or if there are no trust anchors, then check + * if the cert is trusted directly. + */ + PKIX_CHECK( + PKIX_PL_Cert_IsCertTrusted(trustedCert, + PKIX_PL_TrustAnchorMode_Ignore, + &trusted, plContext), + PKIX_CERTISCERTTRUSTEDFAILED); + } + + if (!trusted) { + goto cleanup; + } + /* + * Since the key usage may vary for different + * applications, we need to verify the chain again. + * Reverification will be improved with a fix for 397805. + */ + PKIX_CHECK(PKIX_BuildResult_GetCertChain + (buildResult, &certList, plContext), + PKIX_BUILDRESULTGETCERTCHAINFAILED); + + PKIX_CHECK(pkix_Build_ValidationCheckers + (state, + certList, + matchingAnchor, + PKIX_TRUE, /* Chain revalidation stage. */ + plContext), + PKIX_BUILDVALIDATIONCHECKERSFAILED); + + PKIX_CHECK_ONLY_FATAL( + pkix_Build_ValidateEntireChain(state, matchingAnchor, + &nbioContext, &valResult, + state->verifyNode, plContext), + PKIX_BUILDVALIDATEENTIRECHAINFAILED); + + if (nbioContext != NULL) { + /* IO still pending, resume later */ + *pNBIOContext = nbioContext; + goto cleanup; + } + if (!PKIX_ERROR_RECEIVED) { + /* The result from cache is still valid. But we replace an old*/ + *pBuildResult = buildResult; + buildResult = NULL; + stillValid = PKIX_TRUE; + } + +cleanup: + + if (!nbioContext && cacheHit && !(trusted && stillValid)) { + /* The anchor of this chain is no longer trusted or + * chain cert(s) has been revoked. + * Invalidate this result in the cache */ + buildError = pkixErrorResult; + PKIX_CHECK_FATAL(pkix_CacheCertChain_Remove + (targetCert, + anchors, + plContext), + PKIX_CACHECERTCHAINREMOVEFAILED); + pkixErrorResult = buildError; + buildError = NULL; + } + +fatal: + PKIX_DECREF(buildResult); + PKIX_DECREF(valResult); + PKIX_DECREF(buildError); + PKIX_DECREF(certList); + PKIX_DECREF(matchingAnchor); + PKIX_DECREF(trustedCert); + + + PKIX_RETURN(BUILD); +} + +/* + * FUNCTION: pkix_Build_InitiateBuildChain + * DESCRIPTION: + * + * This function initiates the search for a BuildChain, using the parameters + * provided in "procParams" and, if continuing a search that was suspended + * for I/O, using the ForwardBuilderState pointed to by "pState". + * + * If a successful chain is built, this function stores the BuildResult at + * "pBuildResult". Alternatively, if an operation using non-blocking I/O + * is in progress and the operation has not been completed, this function + * stores the platform-dependent non-blocking I/O context (nbioContext) at + * "pNBIOContext", the FowardBuilderState at "pState", and NULL at + * "pBuildResult". Finally, if chain building was unsuccessful, this function + * stores NULL at both "pState" and at "pBuildResult". + * + * Note: This function is re-entered only for the case of non-blocking I/O + * in the "short-cut" attempt to build a chain using the target Certificate + * directly with one of the trustAnchors. For all other cases, resumption + * after non-blocking I/O is via pkix_Build_ResumeBuildChain. + * + * PARAMETERS: + * "procParams" + * Address of the ProcessingParams for the search. Must be non-NULL. + * "pNBIOContext" + * Address at which the NBIOContext is stored indicating whether the + * validation is complete. Must be non-NULL. + * "pState" + * Address at which the ForwardBuilderState is stored, if the chain + * building is suspended for waiting I/O; also, the address at which the + * ForwardBuilderState is provided for resumption of the chain building + * attempt. Must be non-NULL. + * "pBuildResult" + * Address at which the BuildResult is stored, after a successful build. + * Must be non-NULL. + * "pVerifyNode" + * Address at which a VerifyNode chain is returned, if non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_Build_InitiateBuildChain( + PKIX_ProcessingParams *procParams, + void **pNBIOContext, + PKIX_ForwardBuilderState **pState, + PKIX_BuildResult **pBuildResult, + PKIX_VerifyNode **pVerifyNode, + void *plContext) +{ + PKIX_UInt32 numAnchors = 0; + PKIX_UInt32 numCertStores = 0; + PKIX_UInt32 numHintCerts = 0; + PKIX_UInt32 i = 0; + PKIX_Boolean isDuplicate = PKIX_FALSE; + PKIX_PL_Cert *trustedCert = NULL; + PKIX_CertSelector *targetConstraints = NULL; + PKIX_ComCertSelParams *targetParams = NULL; + PKIX_List *anchors = NULL; + PKIX_List *targetSubjNames = NULL; + PKIX_PL_Cert *targetCert = NULL; + PKIX_PL_Object *firstHintCert = NULL; + PKIX_RevocationChecker *revChecker = NULL; + PKIX_List *certStores = NULL; + PKIX_CertStore *certStore = NULL; + PKIX_List *userCheckers = NULL; + PKIX_List *hintCerts = NULL; + PKIX_PL_Date *testDate = NULL; + PKIX_PL_PublicKey *targetPubKey = NULL; + void *nbioContext = NULL; + BuildConstants buildConstants; + + PKIX_List *tentativeChain = NULL; + PKIX_ValidateResult *valResult = NULL; + PKIX_BuildResult *buildResult = NULL; + PKIX_List *certList = NULL; + PKIX_ForwardBuilderState *state = NULL; + PKIX_CertStore_CheckTrustCallback trustCallback = NULL; + PKIX_CertSelector_MatchCallback selectorCallback = NULL; + PKIX_Boolean trusted = PKIX_FALSE; + PKIX_PL_AIAMgr *aiaMgr = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_InitiateBuildChain"); + PKIX_NULLCHECK_FOUR(procParams, pNBIOContext, pState, pBuildResult); + + nbioContext = *pNBIOContext; + *pNBIOContext = NULL; + + state = *pState; + *pState = NULL; /* no net change in reference count */ + + if (state == NULL) { + PKIX_CHECK(PKIX_ProcessingParams_GetDate + (procParams, &testDate, plContext), + PKIX_PROCESSINGPARAMSGETDATEFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_GetTrustAnchors + (procParams, &anchors, plContext), + PKIX_PROCESSINGPARAMSGETTRUSTANCHORSFAILED); + + PKIX_CHECK(PKIX_List_GetLength(anchors, &numAnchors, plContext), + PKIX_LISTGETLENGTHFAILED); + + /* retrieve stuff from targetCertConstraints */ + PKIX_CHECK(PKIX_ProcessingParams_GetTargetCertConstraints + (procParams, &targetConstraints, plContext), + PKIX_PROCESSINGPARAMSGETTARGETCERTCONSTRAINTSFAILED); + + PKIX_CHECK(PKIX_CertSelector_GetCommonCertSelectorParams + (targetConstraints, &targetParams, plContext), + PKIX_CERTSELECTORGETCOMMONCERTSELECTORPARAMSFAILED); + + PKIX_CHECK(PKIX_ComCertSelParams_GetCertificate + (targetParams, &targetCert, plContext), + PKIX_COMCERTSELPARAMSGETCERTIFICATEFAILED); + + PKIX_CHECK( + PKIX_ComCertSelParams_SetLeafCertFlag(targetParams, + PKIX_TRUE, plContext), + PKIX_COMCERTSELPARAMSSETLEAFCERTFLAGFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_GetHintCerts + (procParams, &hintCerts, plContext), + PKIX_PROCESSINGPARAMSGETHINTCERTSFAILED); + + if (hintCerts != NULL) { + PKIX_CHECK(PKIX_List_GetLength + (hintCerts, &numHintCerts, plContext), + PKIX_LISTGETLENGTHFAILED); + } + + /* + * Caller must provide either a target Cert + * (in ComCertSelParams->Certificate) or a partial Cert + * chain (in ProcParams->HintCerts). + */ + + if (targetCert == NULL) { + + /* Use first cert of hintCerts as the targetCert */ + if (numHintCerts == 0) { + PKIX_ERROR(PKIX_NOTARGETCERTSUPPLIED); + } + + PKIX_CHECK(PKIX_List_GetItem + (hintCerts, + 0, + (PKIX_PL_Object **)&targetCert, + plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK(PKIX_List_DeleteItem(hintCerts, 0, plContext), + PKIX_LISTGETITEMFAILED); + } else { + + /* + * If the first hintCert is the same as the targetCert, + * delete it from hintCerts. + */ + if (numHintCerts != 0) { + PKIX_CHECK(PKIX_List_GetItem + (hintCerts, 0, &firstHintCert, plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK(PKIX_PL_Object_Equals + ((PKIX_PL_Object *)targetCert, + firstHintCert, + &isDuplicate, + plContext), + PKIX_OBJECTEQUALSFAILED); + + if (isDuplicate) { + PKIX_CHECK(PKIX_List_DeleteItem + (hintCerts, 0, plContext), + PKIX_LISTGETITEMFAILED); + } + PKIX_DECREF(firstHintCert); + } + + } + + if (targetCert == NULL) { + PKIX_ERROR(PKIX_NOTARGETCERTSUPPLIED); + } + + PKIX_CHECK(PKIX_PL_Cert_IsLeafCertTrusted + (targetCert, + &trusted, + plContext), + PKIX_CERTISCERTTRUSTEDFAILED); + + PKIX_CHECK(PKIX_PL_Cert_GetAllSubjectNames + (targetCert, + &targetSubjNames, + plContext), + PKIX_CERTGETALLSUBJECTNAMESFAILED); + + PKIX_CHECK(PKIX_PL_Cert_GetSubjectPublicKey + (targetCert, &targetPubKey, plContext), + PKIX_CERTGETSUBJECTPUBLICKEYFAILED); + + PKIX_CHECK(PKIX_List_Create(&tentativeChain, plContext), + PKIX_LISTCREATEFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (tentativeChain, (PKIX_PL_Object *)targetCert, plContext), + PKIX_LISTAPPENDITEMFAILED); + + if (procParams->qualifyTargetCert) { + /* EE cert validation */ + /* Sync up the time on the target selector parameter struct. */ + PKIX_CHECK( + PKIX_ComCertSelParams_SetCertificateValid(targetParams, + testDate, + plContext), + PKIX_COMCERTSELPARAMSSETCERTIFICATEVALIDFAILED); + + PKIX_CHECK(PKIX_CertSelector_GetMatchCallback + (targetConstraints, &selectorCallback, plContext), + PKIX_CERTSELECTORGETMATCHCALLBACKFAILED); + + pkixErrorResult = + (*selectorCallback)(targetConstraints, targetCert, + plContext); + if (pkixErrorResult) { + pkixErrorClass = pkixErrorResult->errClass; + if (pkixErrorClass == PKIX_FATAL_ERROR) { + goto cleanup; + } + if (pVerifyNode != NULL) { + PKIX_Error *tempResult = + pkix_VerifyNode_Create(targetCert, 0, + pkixErrorResult, + pVerifyNode, + plContext); + if (tempResult) { + PKIX_DECREF(pkixErrorResult); + pkixErrorResult = tempResult; + pkixErrorCode = PKIX_VERIFYNODECREATEFAILED; + pkixErrorClass = PKIX_FATAL_ERROR; + goto cleanup; + } + } + pkixErrorCode = PKIX_CERTCHECKVALIDITYFAILED; + goto cleanup; + } + } + + /* If the EE cert is trusted, force success. We only want to do + * this if we aren't validating against a policy (like EV). */ + if (trusted && procParams->initialPolicies == NULL) { + if (pVerifyNode != NULL) { + PKIX_Error *tempResult = + pkix_VerifyNode_Create(targetCert, 0, NULL, + pVerifyNode, + plContext); + if (tempResult) { + pkixErrorResult = tempResult; + pkixErrorCode = PKIX_VERIFYNODECREATEFAILED; + pkixErrorClass = PKIX_FATAL_ERROR; + goto cleanup; + } + } + PKIX_CHECK(pkix_ValidateResult_Create + (targetPubKey, NULL /* anchor */, + NULL /* policyTree */, &valResult, plContext), + PKIX_VALIDATERESULTCREATEFAILED); + PKIX_CHECK( + pkix_BuildResult_Create(valResult, tentativeChain, + &buildResult, plContext), + PKIX_BUILDRESULTCREATEFAILED); + *pBuildResult = buildResult; + /* Note that *pState is NULL. The only side effect is that + * the cert chain won't be cached in PKIX_BuildChain, which + * is fine. */ + goto cleanup; + } + + PKIX_CHECK(PKIX_ProcessingParams_GetCertStores + (procParams, &certStores, plContext), + PKIX_PROCESSINGPARAMSGETCERTSTORESFAILED); + + PKIX_CHECK(PKIX_List_GetLength + (certStores, &numCertStores, plContext), + PKIX_LISTGETLENGTHFAILED); + + /* Reorder CertStores so trusted are at front of the List */ + if (numCertStores > 1) { + for (i = numCertStores - 1; i > 0; i--) { + PKIX_CHECK_ONLY_FATAL(PKIX_List_GetItem + (certStores, + i, + (PKIX_PL_Object **)&certStore, + plContext), + PKIX_LISTGETITEMFAILED); + PKIX_CHECK_ONLY_FATAL(PKIX_CertStore_GetTrustCallback + (certStore, &trustCallback, plContext), + PKIX_CERTSTOREGETTRUSTCALLBACKFAILED); + + if (trustCallback != NULL) { + /* Is a trusted Cert, move CertStore to front */ + PKIX_CHECK(PKIX_List_DeleteItem + (certStores, i, plContext), + PKIX_LISTDELETEITEMFAILED); + PKIX_CHECK(PKIX_List_InsertItem + (certStores, + 0, + (PKIX_PL_Object *)certStore, + plContext), + PKIX_LISTINSERTITEMFAILED); + + } + + PKIX_DECREF(certStore); + } + } + + PKIX_CHECK(PKIX_ProcessingParams_GetCertChainCheckers + (procParams, &userCheckers, plContext), + PKIX_PROCESSINGPARAMSGETCERTCHAINCHECKERSFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_GetRevocationChecker + (procParams, &revChecker, plContext), + PKIX_PROCESSINGPARAMSGETREVOCATIONCHECKERFAILED); + /* Do not initialize AIA manager if we are not going to fetch + * cert using aia url. */ + if (procParams->useAIAForCertFetching) { + PKIX_CHECK(PKIX_PL_AIAMgr_Create(&aiaMgr, plContext), + PKIX_AIAMGRCREATEFAILED); + } + + /* + * We initialize all the fields of buildConstants here, in one place, + * just to help keep track and ensure that we got everything. + */ + + buildConstants.numAnchors = numAnchors; + buildConstants.numCertStores = numCertStores; + buildConstants.numHintCerts = numHintCerts; + buildConstants.procParams = procParams; + buildConstants.testDate = testDate; + buildConstants.timeLimit = NULL; + buildConstants.targetCert = targetCert; + buildConstants.targetPubKey = targetPubKey; + buildConstants.certStores = certStores; + buildConstants.anchors = anchors; + buildConstants.userCheckers = userCheckers; + buildConstants.hintCerts = hintCerts; + buildConstants.revChecker = revChecker; + buildConstants.aiaMgr = aiaMgr; + buildConstants.trustOnlyUserAnchors = + procParams->useOnlyTrustAnchors; + + PKIX_CHECK(pkix_Build_GetResourceLimits(&buildConstants, plContext), + PKIX_BUILDGETRESOURCELIMITSFAILED); + + PKIX_CHECK(pkix_ForwardBuilderState_Create + (0, /* PKIX_UInt32 traversedCACerts */ + buildConstants.maxFanout, + buildConstants.maxDepth, + PKIX_TRUE, /* PKIX_Boolean canBeCached */ + NULL, /* PKIX_Date *validityDate */ + targetCert, /* PKIX_PL_Cert *prevCert */ + targetSubjNames, /* PKIX_List *traversedSubjNames */ + tentativeChain, /* PKIX_List *trustChain */ + NULL, /* PKIX_ForwardBuilderState *parent */ + &state, /* PKIX_ForwardBuilderState **pState */ + plContext), + PKIX_BUILDSTATECREATEFAILED); + + state->buildConstants.numAnchors = buildConstants.numAnchors; + state->buildConstants.numCertStores = buildConstants.numCertStores; + state->buildConstants.numHintCerts = buildConstants.numHintCerts; + state->buildConstants.maxFanout = buildConstants.maxFanout; + state->buildConstants.maxDepth = buildConstants.maxDepth; + state->buildConstants.maxTime = buildConstants.maxTime; + state->buildConstants.procParams = buildConstants.procParams; + PKIX_INCREF(buildConstants.testDate); + state->buildConstants.testDate = buildConstants.testDate; + state->buildConstants.timeLimit = buildConstants.timeLimit; + PKIX_INCREF(buildConstants.targetCert); + state->buildConstants.targetCert = buildConstants.targetCert; + PKIX_INCREF(buildConstants.targetPubKey); + state->buildConstants.targetPubKey = + buildConstants.targetPubKey; + PKIX_INCREF(buildConstants.certStores); + state->buildConstants.certStores = buildConstants.certStores; + PKIX_INCREF(buildConstants.anchors); + state->buildConstants.anchors = buildConstants.anchors; + PKIX_INCREF(buildConstants.userCheckers); + state->buildConstants.userCheckers = + buildConstants.userCheckers; + PKIX_INCREF(buildConstants.hintCerts); + state->buildConstants.hintCerts = buildConstants.hintCerts; + PKIX_INCREF(buildConstants.revChecker); + state->buildConstants.revChecker = buildConstants.revChecker; + state->buildConstants.aiaMgr = buildConstants.aiaMgr; + aiaMgr = NULL; + state->buildConstants.trustOnlyUserAnchors = + buildConstants.trustOnlyUserAnchors; + + if (buildConstants.maxTime != 0) { + PKIX_CHECK(PKIX_PL_Date_Create_CurrentOffBySeconds + (buildConstants.maxTime, + &state->buildConstants.timeLimit, + plContext), + PKIX_DATECREATECURRENTOFFBYSECONDSFAILED); + } + + if (pVerifyNode != NULL) { + PKIX_Error *tempResult = + pkix_VerifyNode_Create(targetCert, 0, NULL, + &(state->verifyNode), + plContext); + if (tempResult) { + pkixErrorResult = tempResult; + pkixErrorCode = PKIX_VERIFYNODECREATEFAILED; + pkixErrorClass = PKIX_FATAL_ERROR; + goto cleanup; + } + } + + PKIX_CHECK_ONLY_FATAL( + pkix_Build_CheckInCache(state, &buildResult, + &nbioContext, plContext), + PKIX_UNABLETOBUILDCHAIN); + if (nbioContext) { + *pNBIOContext = nbioContext; + *pState = state; + state = NULL; + goto cleanup; + } + if (buildResult) { + *pBuildResult = buildResult; + if (pVerifyNode != NULL) { + *pVerifyNode = state->verifyNode; + state->verifyNode = NULL; + } + goto cleanup; + } + } + + /* If we're resuming after non-blocking I/O we need to get SubjNames */ + if (targetSubjNames == NULL) { + PKIX_CHECK(PKIX_PL_Cert_GetAllSubjectNames + (state->buildConstants.targetCert, + &targetSubjNames, + plContext), + PKIX_CERTGETALLSUBJECTNAMESFAILED); + } + + state->status = BUILD_INITIAL; + + pkixErrorResult = + pkix_BuildForwardDepthFirstSearch(&nbioContext, state, + &valResult, plContext); + + /* non-null nbioContext means the build would block */ + if (pkixErrorResult == NULL && nbioContext != NULL) { + + *pNBIOContext = nbioContext; + *pBuildResult = NULL; + + /* no valResult means the build has failed */ + } else { + if (pVerifyNode != NULL) { + PKIX_INCREF(state->verifyNode); + *pVerifyNode = state->verifyNode; + } + + if (valResult == NULL || pkixErrorResult) + PKIX_ERROR(PKIX_UNABLETOBUILDCHAIN); + PKIX_CHECK( + pkix_BuildResult_Create(valResult, state->trustChain, + &buildResult, plContext), + PKIX_BUILDRESULTCREATEFAILED); + *pBuildResult = buildResult; + } + + *pState = state; + state = NULL; + +cleanup: + + PKIX_DECREF(targetConstraints); + PKIX_DECREF(targetParams); + PKIX_DECREF(anchors); + PKIX_DECREF(targetSubjNames); + PKIX_DECREF(targetCert); + PKIX_DECREF(revChecker); + PKIX_DECREF(certStores); + PKIX_DECREF(certStore); + PKIX_DECREF(userCheckers); + PKIX_DECREF(hintCerts); + PKIX_DECREF(firstHintCert); + PKIX_DECREF(testDate); + PKIX_DECREF(targetPubKey); + PKIX_DECREF(tentativeChain); + PKIX_DECREF(valResult); + PKIX_DECREF(certList); + PKIX_DECREF(trustedCert); + PKIX_DECREF(state); + PKIX_DECREF(aiaMgr); + + PKIX_RETURN(BUILD); +} + +/* + * FUNCTION: pkix_Build_ResumeBuildChain + * DESCRIPTION: + * + * This function continues the search for a BuildChain, using the parameters + * provided in "procParams" and the ForwardBuilderState pointed to by "state". + * + * If a successful chain is built, this function stores the BuildResult at + * "pBuildResult". Alternatively, if an operation using non-blocking I/O + * is in progress and the operation has not been completed, this function + * stores the FowardBuilderState at "pState" and NULL at "pBuildResult". + * Finally, if chain building was unsuccessful, this function stores NULL + * at both "pState" and at "pBuildResult". + * + * PARAMETERS: + * "pNBIOContext" + * Address at which the NBIOContext is stored indicating whether the + * validation is complete. Must be non-NULL. + * "pState" + * Address at which the ForwardBuilderState is provided for resumption of + * the chain building attempt; also, the address at which the + * ForwardBuilderStateis stored, if the chain building is suspended for + * waiting I/O. Must be non-NULL. + * "pBuildResult" + * Address at which the BuildResult is stored, after a successful build. + * Must be non-NULL. + * "plContext" + * Platform-specific context pointer. + * THREAD SAFETY: + * Thread Safe (see Thread Safety Definitions in Programmer's Guide) + * RETURNS: + * Returns NULL if the function succeeds. + * Returns a Build Error if the function fails in a non-fatal way + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +static PKIX_Error * +pkix_Build_ResumeBuildChain( + void **pNBIOContext, + PKIX_ForwardBuilderState *state, + PKIX_BuildResult **pBuildResult, + PKIX_VerifyNode **pVerifyNode, + void *plContext) +{ + PKIX_ValidateResult *valResult = NULL; + PKIX_BuildResult *buildResult = NULL; + void *nbioContext = NULL; + + PKIX_ENTER(BUILD, "pkix_Build_ResumeBuildChain"); + PKIX_NULLCHECK_TWO(state, pBuildResult); + + nbioContext = *pNBIOContext; + *pNBIOContext = NULL; + + pkixErrorResult = + pkix_BuildForwardDepthFirstSearch(&nbioContext, state, + &valResult, plContext); + + /* non-null nbioContext means the build would block */ + if (pkixErrorResult == NULL && nbioContext != NULL) { + + *pNBIOContext = nbioContext; + *pBuildResult = NULL; + + /* no valResult means the build has failed */ + } else { + if (pVerifyNode != NULL) { + PKIX_INCREF(state->verifyNode); + *pVerifyNode = state->verifyNode; + } + + if (valResult == NULL || pkixErrorResult) + PKIX_ERROR(PKIX_UNABLETOBUILDCHAIN); + + PKIX_CHECK( + pkix_BuildResult_Create(valResult, state->trustChain, + &buildResult, plContext), + PKIX_BUILDRESULTCREATEFAILED); + *pBuildResult = buildResult; + } + +cleanup: + + PKIX_DECREF(valResult); + + PKIX_RETURN(BUILD); +} + +/* --Public-Functions--------------------------------------------- */ + +/* + * FUNCTION: PKIX_BuildChain (see comments in pkix.h) + */ +PKIX_Error * +PKIX_BuildChain( + PKIX_ProcessingParams *procParams, + void **pNBIOContext, + void **pState, + PKIX_BuildResult **pBuildResult, + PKIX_VerifyNode **pVerifyNode, + void *plContext) +{ + PKIX_ForwardBuilderState *state = NULL; + PKIX_BuildResult *buildResult = NULL; + void *nbioContext = NULL; + + PKIX_ENTER(BUILD, "PKIX_BuildChain"); + PKIX_NULLCHECK_FOUR(procParams, pNBIOContext, pState, pBuildResult); + + nbioContext = *pNBIOContext; + *pNBIOContext = NULL; + + if (*pState == NULL) { + PKIX_CHECK(pkix_Build_InitiateBuildChain + (procParams, + &nbioContext, + &state, + &buildResult, + pVerifyNode, + plContext), + PKIX_BUILDINITIATEBUILDCHAINFAILED); + } else { + state = (PKIX_ForwardBuilderState *)(*pState); + *pState = NULL; /* no net change in reference count */ + if (state->status == BUILD_SHORTCUTPENDING) { + PKIX_CHECK(pkix_Build_InitiateBuildChain + (procParams, + &nbioContext, + &state, + &buildResult, + pVerifyNode, + plContext), + PKIX_BUILDINITIATEBUILDCHAINFAILED); + } else { + PKIX_CHECK(pkix_Build_ResumeBuildChain + (&nbioContext, + state, + &buildResult, + pVerifyNode, + plContext), + PKIX_BUILDINITIATEBUILDCHAINFAILED); + } + } + + /* non-null nbioContext means the build would block */ + if (nbioContext != NULL) { + + *pNBIOContext = nbioContext; + *pState = state; + state = NULL; + *pBuildResult = NULL; + + /* no buildResult means the build has failed */ + } else if (buildResult == NULL) { + PKIX_ERROR(PKIX_UNABLETOBUILDCHAIN); + } else { + /* + * If we made a successful chain by combining the target Cert + * with one of the Trust Anchors, we may have never created a + * validityDate. We treat this situation as + * canBeCached = PKIX_FALSE. + */ + if ((state != NULL) && + ((state->validityDate) != NULL) && + (state->canBeCached)) { + PKIX_CHECK(pkix_CacheCertChain_Add + (state->buildConstants.targetCert, + state->buildConstants.anchors, + state->validityDate, + buildResult, + plContext), + PKIX_CACHECERTCHAINADDFAILED); + } + + *pState = NULL; + *pBuildResult = buildResult; + buildResult = NULL; + } + +cleanup: + PKIX_DECREF(buildResult); + PKIX_DECREF(state); + + PKIX_RETURN(BUILD); +} |