/* 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); }