diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/libpkix/pkix/top/pkix_validate.c | 1451 |
1 files changed, 1451 insertions, 0 deletions
diff --git a/security/nss/lib/libpkix/pkix/top/pkix_validate.c b/security/nss/lib/libpkix/pkix/top/pkix_validate.c new file mode 100644 index 0000000000..1e5dec795a --- /dev/null +++ b/security/nss/lib/libpkix/pkix/top/pkix_validate.c @@ -0,0 +1,1451 @@ +/* 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_validate.c + * + * Top level validateChain function + * + */ + +#include "pkix_validate.h" +#include "pkix_pl_common.h" + +/* --Private-Functions-------------------------------------------- */ + +/* + * FUNCTION: pkix_AddToVerifyLog + * DESCRIPTION: + * + * This function returns immediately if the address for the VerifyNode tree + * pointed to by "pVerifyTree" is NULL. Otherwise it creates a new VerifyNode + * from the Cert pointed to by "cert" and the Error pointed to by "error", + * and inserts it at the depth in the VerifyNode tree determined by "depth". A + * depth of zero means that this function creates the root node of a new tree. + * + * Note: this function does not include the means of choosing among branches + * of a tree. It is intended for non-branching trees, that is, where each + * parent node has only a single child node. + * + * PARAMETERS: + * "cert" + * The address of the Cert to be included in the new VerifyNode. Must be + * non-NULL. + * "depth" + * The UInt32 value of the depth. + * "error" + * The address of the Error to be included in the new VerifyNode. + * "pVerifyTree" + * The address of the VerifyNode tree into which the created VerifyNode + * is to be inserted. The node is not created if VerifyTree is 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 Validate 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_AddToVerifyLog( + PKIX_PL_Cert *cert, + PKIX_UInt32 depth, + PKIX_Error *error, + PKIX_VerifyNode **pVerifyTree, + void *plContext) +{ + + PKIX_VerifyNode *verifyNode = NULL; + + PKIX_ENTER(VALIDATE, "pkix_AddToVerifyLog"); + PKIX_NULLCHECK_ONE(cert); + + if (pVerifyTree) { /* nothing to do if no address given for log */ + + PKIX_CHECK(pkix_VerifyNode_Create + (cert, depth, error, &verifyNode, plContext), + PKIX_VERIFYNODECREATEFAILED); + + if (depth == 0) { + /* We just created the root node */ + *pVerifyTree = verifyNode; + } else { + PKIX_CHECK(pkix_VerifyNode_AddToChain + (*pVerifyTree, verifyNode, plContext), + PKIX_VERIFYNODEADDTOCHAINFAILED); + } + } + +cleanup: + + PKIX_RETURN(VALIDATE); + +} + +/* + * FUNCTION: pkix_CheckCert + * DESCRIPTION: + * + * Checks whether the Cert pointed to by "cert" successfully validates + * using the List of CertChainCheckers pointed to by "checkers". If the + * certificate does not validate, an Error pointer is returned. + * + * This function should be called initially with the UInt32 pointed to by + * "pCheckerIndex" containing zero, and the pointer at "pNBIOContext" + * containing NULL. If a checker does non-blocking I/O, this function will + * return with the index of that checker stored at "pCheckerIndex" and a + * platform-dependent non-blocking I/O context stored at "pNBIOContext". + * A subsequent call to this function with those values intact will allow the + * checking to resume where it left off. This should be repeated until the + * function returns with NULL stored at "pNBIOContext". + * + * PARAMETERS: + * "cert" + * Address of Cert to validate. Must be non-NULL. + * "checkers" + * List of CertChainCheckers which must each validate the certificate. + * Must be non-NULL. + * "checkedExtOIDs" + * List of PKIX_PL_OID that has been processed. If called from building + * chain, it is the list of critical extension OIDs that has been + * processed prior to validation. May be NULL. + * "pCheckerIndex" + * Address at which is stored the the index, within the List "checkers", + * of a checker whose processing was interrupted by non-blocking I/O. + * Must be non-NULL. + * "pNBIOContext" + * Address at which is stored platform-specific non-blocking I/O context. + * 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 Validate 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_CheckCert( + PKIX_PL_Cert *cert, + PKIX_List *checkers, + PKIX_List *checkedExtOIDsList, + PKIX_UInt32 *pCheckerIndex, + void **pNBIOContext, + void *plContext) +{ + PKIX_CertChainChecker_CheckCallback checkerCheck = NULL; + PKIX_CertChainChecker *checker = NULL; + PKIX_List *unresCritExtOIDs = NULL; + PKIX_UInt32 numCheckers; + PKIX_UInt32 numUnresCritExtOIDs = 0; + PKIX_UInt32 checkerIndex = 0; + void *nbioContext = NULL; + + PKIX_ENTER(VALIDATE, "pkix_CheckCert"); + PKIX_NULLCHECK_FOUR(cert, checkers, pCheckerIndex, pNBIOContext); + + nbioContext = *pNBIOContext; + *pNBIOContext = NULL; /* prepare for case of error exit */ + + PKIX_CHECK(PKIX_PL_Cert_GetCriticalExtensionOIDs + (cert, &unresCritExtOIDs, plContext), + PKIX_CERTGETCRITICALEXTENSIONOIDSFAILED); + + PKIX_CHECK(PKIX_List_GetLength(checkers, &numCheckers, plContext), + PKIX_LISTGETLENGTHFAILED); + + for (checkerIndex = *pCheckerIndex; + checkerIndex < numCheckers; + checkerIndex++) { + + PKIX_CHECK(PKIX_List_GetItem + (checkers, + checkerIndex, + (PKIX_PL_Object **)&checker, + plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK(PKIX_CertChainChecker_GetCheckCallback + (checker, &checkerCheck, plContext), + PKIX_CERTCHAINCHECKERGETCHECKCALLBACKFAILED); + + PKIX_CHECK(checkerCheck(checker, cert, unresCritExtOIDs, + &nbioContext, plContext), + PKIX_CERTCHAINCHECKERCHECKFAILED); + + if (nbioContext != NULL) { + *pCheckerIndex = checkerIndex; + *pNBIOContext = nbioContext; + goto cleanup; + } + + PKIX_DECREF(checker); + } + + if (unresCritExtOIDs){ + +#ifdef PKIX_VALIDATEDEBUG + { + PKIX_PL_String *oidString = NULL; + PKIX_UInt32 length; + char *oidAscii = NULL; + PKIX_TOSTRING(unresCritExtOIDs, &oidString, plContext, + PKIX_LISTTOSTRINGFAILED); + PKIX_CHECK(PKIX_PL_String_GetEncoded + (oidString, + PKIX_ESCASCII, + (void **) &oidAscii, + &length, + plContext), + PKIX_STRINGGETENCODEDFAILED); + PKIX_VALIDATE_DEBUG_ARG + ("unrecognized critical extension OIDs:" + " %s\n", oidAscii); + PKIX_DECREF(oidString); + PKIX_PL_Free(oidAscii, plContext); + } +#endif + + if (checkedExtOIDsList != NULL) { + /* Take out OID's that had been processed, if any */ + PKIX_CHECK(pkix_List_RemoveItems + (unresCritExtOIDs, + checkedExtOIDsList, + plContext), + PKIX_LISTREMOVEITEMSFAILED); + } + + PKIX_CHECK(PKIX_List_GetLength + (unresCritExtOIDs, &numUnresCritExtOIDs, plContext), + PKIX_LISTGETLENGTHFAILED); + + if (numUnresCritExtOIDs != 0){ + PKIX_ERROR(PKIX_UNRECOGNIZEDCRITICALEXTENSION); + } + + } + +cleanup: + + PKIX_DECREF(checker); + PKIX_DECREF(unresCritExtOIDs); + + PKIX_RETURN(VALIDATE); + +} + +/* + * FUNCTION: pkix_InitializeCheckers + * DESCRIPTION: + * + * Creates several checkers and initializes them with values derived from the + * TrustAnchor pointed to by "anchor", the ProcessingParams pointed to by + * "procParams", and the number of Certs in the Chain, represented by + * "numCerts". The List of checkers is stored at "pCheckers". + * + * PARAMETERS: + * "anchor" + * Address of TrustAnchor used to initialize the SignatureChecker and + * NameChainingChecker. Must be non-NULL. + * "procParams" + * Address of ProcessingParams used to initialize the ExpirationChecker + * and TargetCertChecker. Must be non-NULL. + * "numCerts" + * Number of certificates in the CertChain. + * "pCheckers" + * Address where object pointer 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 Validate 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_InitializeCheckers( + PKIX_TrustAnchor *anchor, + PKIX_ProcessingParams *procParams, + PKIX_UInt32 numCerts, + PKIX_List **pCheckers, + void *plContext) +{ + PKIX_CertChainChecker *targetCertChecker = NULL; + PKIX_CertChainChecker *expirationChecker = NULL; + PKIX_CertChainChecker *nameChainingChecker = NULL; + PKIX_CertChainChecker *nameConstraintsChecker = NULL; + PKIX_CertChainChecker *basicConstraintsChecker = NULL; + PKIX_CertChainChecker *policyChecker = NULL; + PKIX_CertChainChecker *sigChecker = NULL; + PKIX_CertChainChecker *defaultCrlChecker = NULL; + PKIX_CertChainChecker *userChecker = NULL; + PKIX_PL_X500Name *trustedCAName = NULL; + PKIX_PL_PublicKey *trustedPubKey = NULL; + PKIX_List *checkers = NULL; + PKIX_PL_Date *testDate = NULL; + PKIX_CertSelector *certSelector = NULL; + PKIX_PL_Cert *trustedCert = NULL; + PKIX_PL_CertNameConstraints *trustedNC = NULL; + PKIX_List *initialPolicies = NULL; + PKIX_Boolean policyQualifiersRejected = PKIX_FALSE; + PKIX_Boolean initialPolicyMappingInhibit = PKIX_FALSE; + PKIX_Boolean initialAnyPolicyInhibit = PKIX_FALSE; + PKIX_Boolean initialExplicitPolicy = PKIX_FALSE; + PKIX_List *userCheckersList = NULL; + PKIX_List *certStores = NULL; + PKIX_UInt32 numCertCheckers = 0; + PKIX_UInt32 i; + + PKIX_ENTER(VALIDATE, "pkix_InitializeCheckers"); + PKIX_NULLCHECK_THREE(anchor, procParams, pCheckers); + PKIX_CHECK(PKIX_List_Create(&checkers, plContext), + PKIX_LISTCREATEFAILED); + + /* + * The TrustAnchor may have been created using CreateWithCert + * (in which case GetCAPublicKey and GetCAName will return NULL) + * or may have been created using CreateWithNameKeyPair (in which + * case GetTrustedCert will return NULL. So we call GetTrustedCert + * and populate trustedPubKey and trustedCAName accordingly. + */ + + PKIX_CHECK(PKIX_TrustAnchor_GetTrustedCert + (anchor, &trustedCert, plContext), + PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED); + + if (trustedCert){ + PKIX_CHECK(PKIX_PL_Cert_GetSubjectPublicKey + (trustedCert, &trustedPubKey, plContext), + PKIX_CERTGETSUBJECTPUBLICKEYFAILED); + + PKIX_CHECK(PKIX_PL_Cert_GetSubject + (trustedCert, &trustedCAName, plContext), + PKIX_CERTGETSUBJECTFAILED); + } else { + PKIX_CHECK(PKIX_TrustAnchor_GetCAPublicKey + (anchor, &trustedPubKey, plContext), + PKIX_TRUSTANCHORGETCAPUBLICKEYFAILED); + + PKIX_CHECK(PKIX_TrustAnchor_GetCAName + (anchor, &trustedCAName, plContext), + PKIX_TRUSTANCHORGETCANAMEFAILED); + } + + PKIX_NULLCHECK_TWO(trustedPubKey, trustedCAName); + + PKIX_CHECK(PKIX_TrustAnchor_GetNameConstraints + (anchor, &trustedNC, plContext), + PKIX_TRUSTANCHORGETNAMECONSTRAINTSFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_GetTargetCertConstraints + (procParams, &certSelector, plContext), + PKIX_PROCESSINGPARAMSGETTARGETCERTCONSTRAINTSFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_GetDate + (procParams, &testDate, plContext), + PKIX_PROCESSINGPARAMSGETDATEFAILED); + + 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_ProcessingParams_GetCertStores + (procParams, &certStores, plContext), + PKIX_PROCESSINGPARAMSGETCERTSTORESFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_GetCertChainCheckers + (procParams, &userCheckersList, plContext), + PKIX_PROCESSINGPARAMSGETCERTCHAINCHECKERSFAILED); + + /* now, initialize all the checkers */ + PKIX_CHECK(pkix_TargetCertChecker_Initialize + (certSelector, numCerts, &targetCertChecker, plContext), + PKIX_TARGETCERTCHECKERINITIALIZEFAILED); + + PKIX_CHECK(pkix_ExpirationChecker_Initialize + (testDate, &expirationChecker, plContext), + PKIX_EXPIRATIONCHECKERINITIALIZEFAILED); + + PKIX_CHECK(pkix_NameChainingChecker_Initialize + (trustedCAName, &nameChainingChecker, plContext), + PKIX_NAMECHAININGCHECKERINITIALIZEFAILED); + + PKIX_CHECK(pkix_NameConstraintsChecker_Initialize + (trustedNC, numCerts, &nameConstraintsChecker, plContext), + PKIX_NAMECONSTRAINTSCHECKERINITIALIZEFAILED); + + PKIX_CHECK(pkix_BasicConstraintsChecker_Initialize + (numCerts, &basicConstraintsChecker, plContext), + PKIX_BASICCONSTRAINTSCHECKERINITIALIZEFAILED); + + PKIX_CHECK(pkix_PolicyChecker_Initialize + (initialPolicies, + policyQualifiersRejected, + initialPolicyMappingInhibit, + initialExplicitPolicy, + initialAnyPolicyInhibit, + numCerts, + &policyChecker, + plContext), + PKIX_POLICYCHECKERINITIALIZEFAILED); + + PKIX_CHECK(pkix_SignatureChecker_Initialize + (trustedPubKey, numCerts, &sigChecker, plContext), + PKIX_SIGNATURECHECKERINITIALIZEFAILED); + + if (userCheckersList != NULL) { + + PKIX_CHECK(PKIX_List_GetLength + (userCheckersList, &numCertCheckers, plContext), + PKIX_LISTGETLENGTHFAILED); + + for (i = 0; i < numCertCheckers; i++) { + + PKIX_CHECK(PKIX_List_GetItem + (userCheckersList, + i, + (PKIX_PL_Object **) &userChecker, + plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, + (PKIX_PL_Object *)userChecker, + plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_DECREF(userChecker); + } + } + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, (PKIX_PL_Object *)targetCertChecker, plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, (PKIX_PL_Object *)expirationChecker, plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, (PKIX_PL_Object *)nameChainingChecker, plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, (PKIX_PL_Object *)nameConstraintsChecker, plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, (PKIX_PL_Object *)basicConstraintsChecker, plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, (PKIX_PL_Object *)policyChecker, plContext), + PKIX_LISTAPPENDITEMFAILED); + + PKIX_CHECK(PKIX_List_AppendItem + (checkers, (PKIX_PL_Object *)sigChecker, plContext), + PKIX_LISTAPPENDITEMFAILED); + + *pCheckers = checkers; + +cleanup: + + if (PKIX_ERROR_RECEIVED){ + PKIX_DECREF(checkers); + } + + PKIX_DECREF(certSelector); + PKIX_DECREF(testDate); + PKIX_DECREF(initialPolicies); + PKIX_DECREF(targetCertChecker); + PKIX_DECREF(expirationChecker); + PKIX_DECREF(nameChainingChecker); + PKIX_DECREF(nameConstraintsChecker); + PKIX_DECREF(basicConstraintsChecker); + PKIX_DECREF(policyChecker); + PKIX_DECREF(sigChecker); + PKIX_DECREF(trustedCAName); + PKIX_DECREF(trustedPubKey); + PKIX_DECREF(trustedNC); + PKIX_DECREF(trustedCert); + PKIX_DECREF(defaultCrlChecker); + PKIX_DECREF(userCheckersList); + PKIX_DECREF(certStores); + PKIX_DECREF(userChecker); + + PKIX_RETURN(VALIDATE); +} + +/* + * FUNCTION: pkix_RetrieveOutputs + * DESCRIPTION: + * + * This function queries the respective states of the List of checkers in + * "checkers" to to obtain the final public key from the SignatureChecker + * and the policy tree from the PolicyChecker, storing those values at + * "pFinalSubjPubKey" and "pPolicyTree", respectively. + * + * PARAMETERS: + * "checkers" + * Address of List of checkers to be queried. Must be non-NULL. + * "pFinalSubjPubKey" + * Address where final public key will be stored. Must be non-NULL. + * "pPolicyTree" + * Address where policy tree 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 Validate 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_RetrieveOutputs( + PKIX_List *checkers, + PKIX_PL_PublicKey **pFinalSubjPubKey, + PKIX_PolicyNode **pPolicyTree, + void *plContext) +{ + PKIX_PL_PublicKey *finalSubjPubKey = NULL; + PKIX_PolicyNode *validPolicyTree = NULL; + PKIX_CertChainChecker *checker = NULL; + PKIX_PL_Object *state = NULL; + PKIX_UInt32 numCheckers = 0; + PKIX_UInt32 type; + PKIX_Int32 j; + + PKIX_ENTER(VALIDATE, "pkix_RetrieveOutputs"); + + PKIX_NULLCHECK_TWO(checkers, pPolicyTree); + + /* + * To optimize the search, we guess that the sigChecker is + * last in the tree and is preceded by the policyChecker. We + * search toward the front of the chain. Remember that List + * items are indexed 0..(numItems - 1). + */ + + PKIX_CHECK(PKIX_List_GetLength(checkers, &numCheckers, plContext), + PKIX_LISTGETLENGTHFAILED); + + for (j = numCheckers - 1; j >= 0; j--){ + PKIX_CHECK(PKIX_List_GetItem + (checkers, j, (PKIX_PL_Object **)&checker, plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK(PKIX_CertChainChecker_GetCertChainCheckerState + (checker, &state, plContext), + PKIX_CERTCHAINCHECKERGETCERTCHAINCHECKERSTATEFAILED); + + /* user defined checker may have no state */ + if (state != NULL) { + + PKIX_CHECK(PKIX_PL_Object_GetType(state, &type, plContext), + PKIX_OBJECTGETTYPEFAILED); + + if (type == PKIX_SIGNATURECHECKERSTATE_TYPE){ + /* final pubKey will include any inherited DSA params */ + finalSubjPubKey = + ((pkix_SignatureCheckerState *)state)-> + prevPublicKey; + PKIX_INCREF(finalSubjPubKey); + *pFinalSubjPubKey = finalSubjPubKey; + } + + if (type == PKIX_CERTPOLICYCHECKERSTATE_TYPE) { + validPolicyTree = + ((PKIX_PolicyCheckerState *)state)->validPolicyTree; + break; + } + } + + PKIX_DECREF(checker); + PKIX_DECREF(state); + } + + PKIX_INCREF(validPolicyTree); + *pPolicyTree = validPolicyTree; + +cleanup: + + PKIX_DECREF(checker); + PKIX_DECREF(state); + + PKIX_RETURN(VALIDATE); + +} + +/* + * FUNCTION: pkix_CheckChain + * DESCRIPTION: + * + * Checks whether the List of Certs pointed to by "certs", containing + * "numCerts" entries, successfully validates using each CertChainChecker in + * the List pointed to by "checkers" and has not been revoked, according to any + * of the Revocation Checkers in the List pointed to by "revChecker". Checkers + * are expected to remove from "removeCheckedExtOIDs" and extensions that they + * process. Indices to the certChain and the checkerChain are obtained and + * returned in "pCertCheckedIndex" and "pCheckerIndex", respectively. These + * should be set to zero prior to the initial call, but may be changed (and + * must be supplied on subsequent calls) if processing is suspended for non- + * blocking I/O. Each time a Cert passes from being validated by one of the + * CertChainCheckers to being checked by a Revocation Checker, the Boolean + * stored at "pRevChecking" is changed from FALSE to TRUE. If the Cert is + * rejected by a Revocation Checker, its reason code is returned at + * "pReasonCode. If the List of Certs successfully validates, the public key i + * the final certificate is obtained and stored at "pFinalSubjPubKey" and the + * validPolicyTree, which could be NULL, is stored at pPolicyTree. If the List + * of Certs fails to validate, an Error pointer is returned. + * + * If "pVerifyTree" is non-NULL, a chain of VerifyNodes is created which + * tracks the results of the validation. That is, either each node in the + * chain has a NULL Error component, or the last node contains an Error + * which indicates why the validation failed. + * + * The number of Certs in the List, represented by "numCerts", is used to + * determine which Cert is the final Cert. + * + * PARAMETERS: + * "certs" + * Address of List of Certs to validate. Must be non-NULL. + * "numCerts" + * Number of certificates in the List of certificates. + * "checkers" + * List of CertChainCheckers which must each validate the List of + * certificates. Must be non-NULL. + * "revChecker" + * List of RevocationCheckers which must each not reject the List of + * certificates. May be empty, but must be non-NULL. + * "removeCheckedExtOIDs" + * List of PKIX_PL_OID that has been processed. If called from building + * chain, it is the list of critical extension OIDs that has been + * processed prior to validation. Extension OIDs that may be processed by + * user defined checker processes are also in the list. May be NULL. + * "procParams" + * Address of ProcessingParams used to initialize various checkers. Must + * be non-NULL. + * "pCertCheckedIndex" + * Address where Int32 index to the Cert chain is obtained and + * returned. Must be non-NULL. + * "pCheckerIndex" + * Address where Int32 index to the CheckerChain is obtained and + * returned. Must be non-NULL. + * "pRevChecking" + * Address where Boolean is obtained and returned, indicating, if FALSE, + * that CertChainCheckers are being called; or, if TRUE, that RevChecker + * are being called. Must be non-NULL. + * "pReasonCode" + * Address where UInt32 results of revocation checking are stored. Must be + * non-NULL. + * "pNBIOContext" + * Address where platform-dependent context is stored if checking is + * suspended for non-blocking I/O. Must be non-NULL. + * "pFinalSubjPubKey" + * Address where the final public key will be stored. Must be non-NULL. + * "pPolicyTree" + * Address where the final validPolicyTree is stored. Must be non-NULL. + * "pVerifyTree" + * Address where a VerifyTree is stored, 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 Validate Error if the function fails in a non-fatal way. + * Returns a Fatal Error if the function fails in an unrecoverable way. + */ +PKIX_Error * +pkix_CheckChain( + PKIX_List *certs, + PKIX_UInt32 numCerts, + PKIX_TrustAnchor *anchor, + PKIX_List *checkers, + PKIX_RevocationChecker *revChecker, + PKIX_List *removeCheckedExtOIDs, + PKIX_ProcessingParams *procParams, + PKIX_UInt32 *pCertCheckedIndex, + PKIX_UInt32 *pCheckerIndex, + PKIX_Boolean *pRevChecking, + PKIX_UInt32 *pReasonCode, + void **pNBIOContext, + PKIX_PL_PublicKey **pFinalSubjPubKey, + PKIX_PolicyNode **pPolicyTree, + PKIX_VerifyNode **pVerifyTree, + void *plContext) +{ + PKIX_UInt32 j = 0; + PKIX_Boolean revChecking = PKIX_FALSE; + PKIX_Error *checkCertError = NULL; + void *nbioContext = NULL; + PKIX_PL_Cert *cert = NULL; + PKIX_PL_Cert *issuer = NULL; + PKIX_PL_NssContext *nssContext = NULL; + CERTCertList *certList = NULL; + const CERTChainVerifyCallback *chainVerifyCallback = NULL; + CERTCertificate *nssCert = NULL; + + PKIX_ENTER(VALIDATE, "pkix_CheckChain"); + PKIX_NULLCHECK_FOUR(certs, checkers, revChecker, pCertCheckedIndex); + PKIX_NULLCHECK_FOUR(pCheckerIndex, pRevChecking, pReasonCode, anchor); + PKIX_NULLCHECK_THREE(pNBIOContext, pFinalSubjPubKey, pPolicyTree); + + nbioContext = *pNBIOContext; + *pNBIOContext = NULL; + revChecking = *pRevChecking; + nssContext = (PKIX_PL_NssContext *)plContext; + chainVerifyCallback = &nssContext->chainVerifyCallback; + + if (chainVerifyCallback->isChainValid != NULL) { + PRBool chainOK = PR_FALSE; /*assume failure*/ + SECStatus rv; + + certList = CERT_NewCertList(); + if (certList == NULL) { + PKIX_ERROR_ALLOC_ERROR(); + } + + /* Add the trust anchor to the list */ + PKIX_CHECK(PKIX_TrustAnchor_GetTrustedCert + (anchor, &cert, plContext), + PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED); + + PKIX_CHECK( + PKIX_PL_Cert_GetCERTCertificate(cert, &nssCert, plContext), + PKIX_CERTGETCERTCERTIFICATEFAILED); + + rv = CERT_AddCertToListHead(certList, nssCert); + if (rv != SECSuccess) { + PKIX_ERROR_ALLOC_ERROR(); + } + /* the certList takes ownership of nssCert on success */ + nssCert = NULL; + PKIX_DECREF(cert); + + /* Add the rest of the chain to the list */ + for (j = *pCertCheckedIndex; j < numCerts; j++) { + PKIX_CHECK(PKIX_List_GetItem( + certs, j, (PKIX_PL_Object **)&cert, plContext), + PKIX_LISTGETITEMFAILED); + + PKIX_CHECK( + PKIX_PL_Cert_GetCERTCertificate(cert, &nssCert, plContext), + PKIX_CERTGETCERTCERTIFICATEFAILED); + + rv = CERT_AddCertToListHead(certList, nssCert); + if (rv != SECSuccess) { + PKIX_ERROR_ALLOC_ERROR(); + } + /* the certList takes ownership of nssCert on success */ + nssCert = NULL; + PKIX_DECREF(cert); + } + + rv = (*chainVerifyCallback->isChainValid) + (chainVerifyCallback->isChainValidArg, certList, &chainOK); + if (rv != SECSuccess) { + PKIX_ERROR_FATAL(PKIX_CHAINVERIFYCALLBACKFAILED); + } + + if (!chainOK) { + PKIX_ERROR(PKIX_CHAINVERIFYCALLBACKFAILED); + } + + } + + PKIX_CHECK(PKIX_TrustAnchor_GetTrustedCert + (anchor, &cert, plContext), + PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED); + + for (j = *pCertCheckedIndex; j < numCerts; j++) { + + PORT_Assert(cert); + PKIX_DECREF(issuer); + issuer = cert; + cert = NULL; + + PKIX_CHECK(PKIX_List_GetItem( + certs, j, (PKIX_PL_Object **)&cert, plContext), + PKIX_LISTGETITEMFAILED); + + /* check if cert pointer is valid */ + PORT_Assert(cert); + if (cert == NULL) { + continue; + } + + if (revChecking == PKIX_FALSE) { + + PKIX_CHECK(pkix_CheckCert + (cert, + checkers, + removeCheckedExtOIDs, + pCheckerIndex, + &nbioContext, + plContext), + PKIX_CHECKCERTFAILED); + + if (nbioContext != NULL) { + *pCertCheckedIndex = j; + *pRevChecking = revChecking; + *pNBIOContext = nbioContext; + goto cleanup; + } + + revChecking = PKIX_TRUE; + *pCheckerIndex = 0; + } + + if (revChecking == PKIX_TRUE) { + PKIX_RevocationStatus revStatus; + pkixErrorResult = + PKIX_RevocationChecker_Check( + cert, issuer, revChecker, + procParams, PKIX_TRUE, + (j == numCerts - 1) ? PKIX_TRUE : PKIX_FALSE, + &revStatus, pReasonCode, + &nbioContext, plContext); + if (nbioContext != NULL) { + *pCertCheckedIndex = j; + *pRevChecking = revChecking; + *pNBIOContext = nbioContext; + goto cleanup; + } + if (revStatus == PKIX_RevStatus_Revoked || + pkixErrorResult) { + if (!pkixErrorResult) { + /* if pkixErrorResult is returned then + * use it as it has a detailed revocation + * error code. Otherwise create a new error */ + PKIX_ERROR_CREATE(VALIDATE, + PKIX_CERTIFICATEREVOKED, + pkixErrorResult); + } + goto cleanup; + } + revChecking = PKIX_FALSE; + *pCheckerIndex = 0; + } + + PKIX_CHECK(pkix_AddToVerifyLog + (cert, j, NULL, pVerifyTree, plContext), + PKIX_ADDTOVERIFYLOGFAILED); + } + + PKIX_CHECK(pkix_RetrieveOutputs + (checkers, pFinalSubjPubKey, pPolicyTree, plContext), + PKIX_RETRIEVEOUTPUTSFAILED); + + *pNBIOContext = NULL; + +cleanup: + if (PKIX_ERROR_RECEIVED && cert) { + checkCertError = pkixErrorResult; + + PKIX_CHECK_FATAL( + pkix_AddToVerifyLog(cert, j, checkCertError, pVerifyTree, + plContext), + PKIX_ADDTOVERIFYLOGFAILED); + pkixErrorResult = checkCertError; + pkixErrorCode = pkixErrorResult->errCode; + checkCertError = NULL; + } + +fatal: + if (nssCert) { + CERT_DestroyCertificate(nssCert); + } + + if (certList) { + CERT_DestroyCertList(certList); + } + + PKIX_DECREF(checkCertError); + PKIX_DECREF(cert); + PKIX_DECREF(issuer); + + PKIX_RETURN(VALIDATE); +} + +/* + * FUNCTION: pkix_ExtractParameters + * DESCRIPTION: + * + * Extracts several parameters from the ValidateParams object pointed to by + * "valParams" and stores the CertChain at "pChain", the List of Certs at + * "pCerts", the number of Certs in the chain at "pNumCerts", the + * ProcessingParams object at "pProcParams", the List of TrustAnchors at + * "pAnchors", and the number of TrustAnchors at "pNumAnchors". + * + * PARAMETERS: + * "valParams" + * Address of ValidateParams from which the parameters are extracted. + * Must be non-NULL. + * "pCerts" + * Address where object pointer for List of Certs will be stored. + * Must be non-NULL. + * "pNumCerts" + * Address where number of Certs will be stored. Must be non-NULL. + * "pProcParams" + * Address where object pointer for ProcessingParams will be stored. + * Must be non-NULL. + * "pAnchors" + * Address where object pointer for List of Anchors will be stored. + * Must be non-NULL. + * "pNumAnchors" + * Address where number of Anchors 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 Validate 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_ExtractParameters( + PKIX_ValidateParams *valParams, + PKIX_List **pCerts, + PKIX_UInt32 *pNumCerts, + PKIX_ProcessingParams **pProcParams, + PKIX_List **pAnchors, + PKIX_UInt32 *pNumAnchors, + void *plContext) +{ + PKIX_ENTER(VALIDATE, "pkix_ExtractParameters"); + PKIX_NULLCHECK_THREE(valParams, pCerts, pNumCerts); + PKIX_NULLCHECK_THREE(pProcParams, pAnchors, pNumAnchors); + + /* extract relevant parameters from chain */ + PKIX_CHECK(PKIX_ValidateParams_GetCertChain + (valParams, pCerts, plContext), + PKIX_VALIDATEPARAMSGETCERTCHAINFAILED); + + PKIX_CHECK(PKIX_List_GetLength(*pCerts, pNumCerts, plContext), + PKIX_LISTGETLENGTHFAILED); + + /* extract relevant parameters from procParams */ + PKIX_CHECK(PKIX_ValidateParams_GetProcessingParams + (valParams, pProcParams, plContext), + PKIX_VALIDATEPARAMSGETPROCESSINGPARAMSFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_GetTrustAnchors + (*pProcParams, pAnchors, plContext), + PKIX_PROCESSINGPARAMSGETTRUSTANCHORSFAILED); + + PKIX_CHECK(PKIX_List_GetLength(*pAnchors, pNumAnchors, plContext), + PKIX_LISTGETLENGTHFAILED); + +cleanup: + + PKIX_RETURN(VALIDATE); +} + +/* --Public-Functions--------------------------------------------- */ + +/* + * FUNCTION: PKIX_ValidateChain (see comments in pkix.h) + */ +PKIX_Error * +PKIX_ValidateChain( + PKIX_ValidateParams *valParams, + PKIX_ValidateResult **pResult, + PKIX_VerifyNode **pVerifyTree, + void *plContext) +{ + PKIX_Error *chainFailed = NULL; + + PKIX_ProcessingParams *procParams = NULL; + PKIX_CertChainChecker *userChecker = NULL; + PKIX_RevocationChecker *revChecker = NULL; + PKIX_List *certs = NULL; + PKIX_List *checkers = NULL; + PKIX_List *anchors = NULL; + PKIX_List *userCheckers = NULL; + PKIX_List *userCheckerExtOIDs = NULL; + PKIX_List *validateCheckedCritExtOIDsList = NULL; + PKIX_TrustAnchor *anchor = NULL; + PKIX_ValidateResult *valResult = NULL; + PKIX_PL_PublicKey *finalPubKey = NULL; + PKIX_PolicyNode *validPolicyTree = NULL; + PKIX_Boolean supportForwarding = PKIX_FALSE; + PKIX_Boolean revChecking = PKIX_FALSE; + PKIX_UInt32 i, numCerts, numAnchors; + PKIX_UInt32 numUserCheckers = 0; + PKIX_UInt32 certCheckedIndex = 0; + PKIX_UInt32 checkerIndex = 0; + PKIX_UInt32 reasonCode = 0; + void *nbioContext = NULL; + + PKIX_ENTER(VALIDATE, "PKIX_ValidateChain"); + PKIX_NULLCHECK_TWO(valParams, pResult); + + /* extract various parameters from valParams */ + PKIX_CHECK(pkix_ExtractParameters + (valParams, + &certs, + &numCerts, + &procParams, + &anchors, + &numAnchors, + plContext), + PKIX_EXTRACTPARAMETERSFAILED); + + /* + * setup an extension OID list that user had defined for his checker + * processing. User checker is not responsible for taking out OIDs + * from unresolved critical extension list as the libpkix checker + * is doing. Here we add those user checkers' OIDs to the removal + * list to be taken out by CheckChain + */ + PKIX_CHECK(PKIX_ProcessingParams_GetCertChainCheckers + (procParams, &userCheckers, plContext), + PKIX_PROCESSINGPARAMSGETCERTCHAINCHECKERSFAILED); + + if (userCheckers != NULL) { + + PKIX_CHECK(PKIX_List_Create + (&validateCheckedCritExtOIDsList, + plContext), + PKIX_LISTCREATEFAILED); + + 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, &supportForwarding, plContext), + PKIX_CERTCHAINCHECKERISFORWARDCHECKINGSUPPORTEDFAILED); + + if (supportForwarding == PKIX_FALSE) { + + PKIX_CHECK + (PKIX_CertChainChecker_GetSupportedExtensions + (userChecker, &userCheckerExtOIDs, plContext), + PKIX_CERTCHAINCHECKERGETSUPPORTEDEXTENSIONSFAILED); + + if (userCheckerExtOIDs != NULL) { + PKIX_CHECK(pkix_List_AppendList + (validateCheckedCritExtOIDsList, + userCheckerExtOIDs, + plContext), + PKIX_LISTAPPENDLISTFAILED); + } + } + + PKIX_DECREF(userCheckerExtOIDs); + PKIX_DECREF(userChecker); + } + } + + PKIX_CHECK(PKIX_ProcessingParams_GetRevocationChecker + (procParams, &revChecker, plContext), + PKIX_PROCESSINGPARAMSGETREVOCATIONCHECKERFAILED); + + /* try to validate the chain with each anchor */ + for (i = 0; i < numAnchors; i++){ + + /* get trust anchor */ + PKIX_CHECK(PKIX_List_GetItem + (anchors, i, (PKIX_PL_Object **)&anchor, plContext), + PKIX_LISTGETITEMFAILED); + + /* initialize checkers using information from trust anchor */ + PKIX_CHECK(pkix_InitializeCheckers + (anchor, procParams, numCerts, &checkers, plContext), + PKIX_INITIALIZECHECKERSFAILED); + + /* + * Validate the chain using this trust anchor and these + * checkers. (WARNING: checkers that use non-blocking I/O + * are not currently supported.) + */ + certCheckedIndex = 0; + checkerIndex = 0; + revChecking = PKIX_FALSE; + chainFailed = pkix_CheckChain + (certs, + numCerts, + anchor, + checkers, + revChecker, + validateCheckedCritExtOIDsList, + procParams, + &certCheckedIndex, + &checkerIndex, + &revChecking, + &reasonCode, + &nbioContext, + &finalPubKey, + &validPolicyTree, + pVerifyTree, + plContext); + + if (chainFailed) { + + /* cert chain failed to validate */ + + PKIX_DECREF(chainFailed); + PKIX_DECREF(anchor); + PKIX_DECREF(checkers); + PKIX_DECREF(validPolicyTree); + + /* if last anchor, we fail; else, we try next anchor */ + if (i == (numAnchors - 1)) { /* last anchor */ + PKIX_ERROR(PKIX_VALIDATECHAINFAILED); + } + + } else { + + /* XXX Remove this assertion after 2014-12-31. + * See bug 946984. */ + PORT_Assert(reasonCode == 0); + + /* cert chain successfully validated! */ + PKIX_CHECK(pkix_ValidateResult_Create + (finalPubKey, + anchor, + validPolicyTree, + &valResult, + plContext), + PKIX_VALIDATERESULTCREATEFAILED); + + *pResult = valResult; + + /* no need to try any more anchors in the loop */ + goto cleanup; + } + } + +cleanup: + + PKIX_DECREF(finalPubKey); + PKIX_DECREF(certs); + PKIX_DECREF(anchors); + PKIX_DECREF(anchor); + PKIX_DECREF(checkers); + PKIX_DECREF(revChecker); + PKIX_DECREF(validPolicyTree); + PKIX_DECREF(chainFailed); + PKIX_DECREF(procParams); + PKIX_DECREF(userCheckers); + PKIX_DECREF(validateCheckedCritExtOIDsList); + + PKIX_RETURN(VALIDATE); +} + +/* + * FUNCTION: pkix_Validate_BuildUserOIDs + * DESCRIPTION: + * + * This function creates a List of the OIDs that are processed by the user + * checkers in the List pointed to by "userCheckers", storing the resulting + * List at "pUserCritOIDs". If the List of userCheckers is NULL, the output + * List will be NULL. Otherwise the output List will be non-NULL, but may be + * empty. + * + * PARAMETERS: + * "userCheckers" + * The address of the List of userCheckers. + * "pUserCritOIDs" + * The address at which the 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 VALIDATE 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_Validate_BuildUserOIDs( + PKIX_List *userCheckers, + PKIX_List **pUserCritOIDs, + void *plContext) +{ + PKIX_UInt32 numUserCheckers = 0; + PKIX_UInt32 i = 0; + PKIX_List *userCritOIDs = NULL; + PKIX_List *userCheckerExtOIDs = NULL; + PKIX_Boolean supportForwarding = PKIX_FALSE; + PKIX_CertChainChecker *userChecker = NULL; + + PKIX_ENTER(VALIDATE, "pkix_Validate_BuildUserOIDs"); + PKIX_NULLCHECK_ONE(pUserCritOIDs); + + if (userCheckers != NULL) { + PKIX_CHECK(PKIX_List_Create(&userCritOIDs, plContext), + PKIX_LISTCREATEFAILED); + + 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, &supportForwarding, plContext), + PKIX_CERTCHAINCHECKERISFORWARDCHECKINGSUPPORTEDFAILED); + + if (supportForwarding == PKIX_FALSE) { + + PKIX_CHECK(PKIX_CertChainChecker_GetSupportedExtensions + (userChecker, &userCheckerExtOIDs, plContext), + PKIX_CERTCHAINCHECKERGETSUPPORTEDEXTENSIONSFAILED); + + if (userCheckerExtOIDs != NULL) { + PKIX_CHECK(pkix_List_AppendList + (userCritOIDs, userCheckerExtOIDs, plContext), + PKIX_LISTAPPENDLISTFAILED); + } + } + + PKIX_DECREF(userCheckerExtOIDs); + PKIX_DECREF(userChecker); + } + } + + *pUserCritOIDs = userCritOIDs; + +cleanup: + + if (PKIX_ERROR_RECEIVED){ + PKIX_DECREF(userCritOIDs); + } + + PKIX_DECREF(userCheckerExtOIDs); + PKIX_DECREF(userChecker); + + PKIX_RETURN(VALIDATE); +} + +/* + * FUNCTION: PKIX_ValidateChain_nb (see comments in pkix.h) + */ +PKIX_Error * +PKIX_ValidateChain_NB( + PKIX_ValidateParams *valParams, + PKIX_UInt32 *pCertIndex, + PKIX_UInt32 *pAnchorIndex, + PKIX_UInt32 *pCheckerIndex, + PKIX_Boolean *pRevChecking, + PKIX_List **pCheckers, + void **pNBIOContext, + PKIX_ValidateResult **pResult, + PKIX_VerifyNode **pVerifyTree, + void *plContext) +{ + PKIX_UInt32 numCerts = 0; + PKIX_UInt32 numAnchors = 0; + PKIX_UInt32 i = 0; + PKIX_UInt32 certIndex = 0; + PKIX_UInt32 anchorIndex = 0; + PKIX_UInt32 checkerIndex = 0; + PKIX_UInt32 reasonCode = 0; + PKIX_Boolean revChecking = PKIX_FALSE; + PKIX_List *certs = NULL; + PKIX_List *anchors = NULL; + PKIX_List *checkers = NULL; + PKIX_List *userCheckers = NULL; + PKIX_List *validateCheckedCritExtOIDsList = NULL; + PKIX_TrustAnchor *anchor = NULL; + PKIX_ValidateResult *valResult = NULL; + PKIX_PL_PublicKey *finalPubKey = NULL; + PKIX_PolicyNode *validPolicyTree = NULL; + PKIX_ProcessingParams *procParams = NULL; + PKIX_RevocationChecker *revChecker = NULL; + PKIX_Error *chainFailed = NULL; + void *nbioContext = NULL; + + PKIX_ENTER(VALIDATE, "PKIX_ValidateChain_NB"); + PKIX_NULLCHECK_FOUR + (valParams, pCertIndex, pAnchorIndex, pCheckerIndex); + PKIX_NULLCHECK_FOUR(pRevChecking, pCheckers, pNBIOContext, pResult); + + nbioContext = *pNBIOContext; + *pNBIOContext = NULL; + + /* extract various parameters from valParams */ + PKIX_CHECK(pkix_ExtractParameters + (valParams, + &certs, + &numCerts, + &procParams, + &anchors, + &numAnchors, + plContext), + PKIX_EXTRACTPARAMETERSFAILED); + + /* + * Create a List of the OIDs that will be processed by the user + * checkers. User checkers are not responsible for removing OIDs from + * the List of unresolved critical extensions, as libpkix checkers are. + * So we add those user checkers' OIDs to the removal list to be taken + * out by CheckChain. + */ + PKIX_CHECK(PKIX_ProcessingParams_GetCertChainCheckers + (procParams, &userCheckers, plContext), + PKIX_PROCESSINGPARAMSGETCERTCHAINCHECKERSFAILED); + + PKIX_CHECK(pkix_Validate_BuildUserOIDs + (userCheckers, &validateCheckedCritExtOIDsList, plContext), + PKIX_VALIDATEBUILDUSEROIDSFAILED); + + PKIX_CHECK(PKIX_ProcessingParams_GetRevocationChecker + (procParams, &revChecker, plContext), + PKIX_PROCESSINGPARAMSGETREVOCATIONCHECKERFAILED); + + /* Are we resuming after a WOULDBLOCK return, or starting anew ? */ + if (nbioContext != NULL) { + /* Resuming */ + certIndex = *pCertIndex; + anchorIndex = *pAnchorIndex; + checkerIndex = *pCheckerIndex; + revChecking = *pRevChecking; + checkers = *pCheckers; + *pCheckers = NULL; + } + + /* try to validate the chain with each anchor */ + for (i = anchorIndex; i < numAnchors; i++) { + + /* get trust anchor */ + PKIX_CHECK(PKIX_List_GetItem + (anchors, i, (PKIX_PL_Object **)&anchor, plContext), + PKIX_LISTGETITEMFAILED); + + /* initialize checkers using information from trust anchor */ + if (nbioContext == NULL) { + PKIX_CHECK(pkix_InitializeCheckers + (anchor, + procParams, + numCerts, + &checkers, + plContext), + PKIX_INITIALIZECHECKERSFAILED); + } + + /* + * Validate the chain using this trust anchor and these + * checkers. + */ + chainFailed = pkix_CheckChain + (certs, + numCerts, + anchor, + checkers, + revChecker, + validateCheckedCritExtOIDsList, + procParams, + &certIndex, + &checkerIndex, + &revChecking, + &reasonCode, + &nbioContext, + &finalPubKey, + &validPolicyTree, + pVerifyTree, + plContext); + + if (nbioContext != NULL) { + *pCertIndex = certIndex; + *pAnchorIndex = anchorIndex; + *pCheckerIndex = checkerIndex; + *pRevChecking = revChecking; + PKIX_INCREF(checkers); + *pCheckers = checkers; + *pNBIOContext = nbioContext; + goto cleanup; + } + + if (chainFailed) { + + /* cert chain failed to validate */ + + PKIX_DECREF(chainFailed); + PKIX_DECREF(anchor); + PKIX_DECREF(checkers); + PKIX_DECREF(validPolicyTree); + + /* if last anchor, we fail; else, we try next anchor */ + if (i == (numAnchors - 1)) { /* last anchor */ + PKIX_ERROR(PKIX_VALIDATECHAINFAILED); + } + + } else { + + /* XXX Remove this assertion after 2014-12-31. + * See bug 946984. */ + PORT_Assert(reasonCode == 0); + + /* cert chain successfully validated! */ + PKIX_CHECK(pkix_ValidateResult_Create + (finalPubKey, + anchor, + validPolicyTree, + &valResult, + plContext), + PKIX_VALIDATERESULTCREATEFAILED); + + *pResult = valResult; + + /* no need to try any more anchors in the loop */ + goto cleanup; + } + } + +cleanup: + + PKIX_DECREF(finalPubKey); + PKIX_DECREF(certs); + PKIX_DECREF(anchors); + PKIX_DECREF(anchor); + PKIX_DECREF(checkers); + PKIX_DECREF(revChecker); + PKIX_DECREF(validPolicyTree); + PKIX_DECREF(chainFailed); + PKIX_DECREF(procParams); + PKIX_DECREF(userCheckers); + PKIX_DECREF(validateCheckedCritExtOIDsList); + + PKIX_RETURN(VALIDATE); +} |