/* 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/. */
/*
 * nss_pkix_proxy.h
 *
 * PKIX - NSS proxy functions
 *
 * NOTE: All structures, functions, data types are parts of library private
 * api and are subjects to change in any following releases.
 *
 */
#include "prerror.h"
#include "prprf.h"

#include "nspr.h"
#include "pk11func.h"
#include "certdb.h"
#include "cert.h"
#include "secerr.h"
#include "nssb64.h"
#include "secasn1.h"
#include "secder.h"
#include "pkit.h"

#include "pkix_pl_common.h"

extern PRLogModuleInfo *pkixLog;

#ifdef PKIX_OBJECT_LEAK_TEST

extern PKIX_UInt32
pkix_pl_lifecycle_ObjectLeakCheck(int *);

extern SECStatus
pkix_pl_lifecycle_ObjectTableUpdate(int *objCountTable);

PRInt32 parallelFnInvocationCount;
#endif /* PKIX_OBJECT_LEAK_TEST */

static PRBool usePKIXValidationEngine = PR_FALSE;

/*
 * FUNCTION: CERT_SetUsePKIXForValidation
 * DESCRIPTION:
 *
 * Enables or disables use of libpkix for certificate validation
 *
 * PARAMETERS:
 *  "enable"
 *      PR_TRUE: enables use of libpkix for cert validation.
 *      PR_FALSE: disables.
 * THREAD SAFETY:
 *  NOT Thread Safe.
 * RETURNS:
 *  Returns SECSuccess if successfully enabled
 */
SECStatus
CERT_SetUsePKIXForValidation(PRBool enable)
{
    usePKIXValidationEngine = (enable > 0) ? PR_TRUE : PR_FALSE;
    return SECSuccess;
}

/*
 * FUNCTION: CERT_GetUsePKIXForValidation
 * DESCRIPTION:
 *
 * Checks if libpkix building function should be use for certificate
 * chain building.
 *
 * PARAMETERS:
 *  NONE
 * THREAD SAFETY:
 *  NOT Thread Safe
 * RETURNS:
 *  Returns PR_TRUE if libpkix should be used. PR_FALSE otherwise.
 */
PRBool
CERT_GetUsePKIXForValidation()
{
    return usePKIXValidationEngine;
}

#ifdef NOTDEF
/*
 * FUNCTION: cert_NssKeyUsagesToPkix
 * DESCRIPTION:
 *
 * Converts nss key usage bit field(PRUint32) to pkix key usage
 * bit field.
 *
 * PARAMETERS:
 *  "nssKeyUsage"
 *      Nss key usage bit field.
 *  "pkixKeyUsage"
 *      Pkix key usage big field.
 *  "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 Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
cert_NssKeyUsagesToPkix(
    PRUint32 nssKeyUsage,
    PKIX_UInt32 *pPkixKeyUsage,
    void *plContext)
{
    PKIX_UInt32 pkixKeyUsage = 0;

    PKIX_ENTER(CERTVFYPKIX, "cert_NssKeyUsagesToPkix");
    PKIX_NULLCHECK_ONE(pPkixKeyUsage);

    *pPkixKeyUsage = 0;

    if (nssKeyUsage & KU_DIGITAL_SIGNATURE) {
        pkixKeyUsage |= PKIX_DIGITAL_SIGNATURE;
    }

    if (nssKeyUsage & KU_NON_REPUDIATION) {
        pkixKeyUsage |= PKIX_NON_REPUDIATION;
    }

    if (nssKeyUsage & KU_KEY_ENCIPHERMENT) {
        pkixKeyUsage |= PKIX_KEY_ENCIPHERMENT;
    }

    if (nssKeyUsage & KU_DATA_ENCIPHERMENT) {
        pkixKeyUsage |= PKIX_DATA_ENCIPHERMENT;
    }

    if (nssKeyUsage & KU_KEY_AGREEMENT) {
        pkixKeyUsage |= PKIX_KEY_AGREEMENT;
    }

    if (nssKeyUsage & KU_KEY_CERT_SIGN) {
        pkixKeyUsage |= PKIX_KEY_CERT_SIGN;
    }

    if (nssKeyUsage & KU_CRL_SIGN) {
        pkixKeyUsage |= PKIX_CRL_SIGN;
    }

    if (nssKeyUsage & KU_ENCIPHER_ONLY) {
        pkixKeyUsage |= PKIX_ENCIPHER_ONLY;
    }

    /* Not supported. XXX we should support this once it is
     * fixed in NSS */
    /* pkixKeyUsage |= PKIX_DECIPHER_ONLY; */

    *pPkixKeyUsage = pkixKeyUsage;

    PKIX_RETURN(CERTVFYPKIX);
}

extern SECOidTag ekuOidStrings[];

enum {
    ekuIndexSSLServer = 0,
    ekuIndexSSLClient,
    ekuIndexCodeSigner,
    ekuIndexEmail,
    ekuIndexTimeStamp,
    ekuIndexStatusResponder,
    ekuIndexUnknown
} ekuIndex;

typedef struct {
    SECCertUsage certUsage;
    PRUint32 ekuStringIndex;
} SECCertUsageToEku;

const SECCertUsageToEku certUsageEkuStringMap[] = {
    { certUsageSSLClient, ekuIndexSSLClient },
    { certUsageSSLServer, ekuIndexSSLServer },
    { certUsageSSLCA, ekuIndexSSLServer },
    { certUsageEmailSigner, ekuIndexEmail },
    { certUsageEmailRecipient, ekuIndexEmail },
    { certUsageObjectSigner, ekuIndexCodeSigner },
    { certUsageUserCertImport, ekuIndexUnknown },
    { certUsageVerifyCA, ekuIndexUnknown },
    { certUsageProtectedObjectSigner, ekuIndexUnknown },
    { certUsageStatusResponder, ekuIndexStatusResponder },
    { certUsageAnyCA, ekuIndexUnknown },
};

/*
 * FUNCTION: cert_NssCertificateUsageToPkixKUAndEKU
 * DESCRIPTION:
 *
 * Converts nss CERTCertificateUsage bit field to pkix key and
 * extended key usages.
 *
 * PARAMETERS:
 *  "cert"
 *      Pointer to CERTCertificate structure of validating cert.
 *  "requiredCertUsages"
 *      Required usage that will be converted to pkix eku and ku.
 *  "requiredKeyUsage",
 *      Additional key usages impose to cert.
 *  "isCA",
 *      it true, convert usages for cert that is a CA cert.
 *  "ppkixEKUList"
 *      Returned address of a list of pkix extended key usages.
 *  "ppkixKU"
 *      Returned address of pkix required key usages bit field.
 *  "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 Cert Verify Error if the function fails in an unrecoverable way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
cert_NssCertificateUsageToPkixKUAndEKU(
    CERTCertificate *cert,
    SECCertUsage requiredCertUsage,
    PRUint32 requiredKeyUsages,
    PRBool isCA,
    PKIX_List **ppkixEKUList,
    PKIX_UInt32 *ppkixKU,
    void *plContext)
{
    PKIX_List *ekuOidsList = NULL;
    PKIX_PL_OID *ekuOid = NULL;
    int i = 0;
    int ekuIndex = ekuIndexUnknown;

    PKIX_ENTER(CERTVFYPKIX, "cert_NssCertificateUsageToPkixEku");
    PKIX_NULLCHECK_TWO(ppkixEKUList, ppkixKU);

    PKIX_CHECK(
        PKIX_List_Create(&ekuOidsList, plContext),
        PKIX_LISTCREATEFAILED);

    for (; i < PR_ARRAY_SIZE(certUsageEkuStringMap); i++) {
        const SECCertUsageToEku *usageToEkuElem =
            &certUsageEkuStringMap[i];
        if (usageToEkuElem->certUsage == requiredCertUsage) {
            ekuIndex = usageToEkuElem->ekuStringIndex;
            break;
        }
    }
    if (ekuIndex != ekuIndexUnknown) {
        PRUint32 reqKeyUsage = 0;
        PRUint32 reqCertType = 0;

        CERT_KeyUsageAndTypeForCertUsage(requiredCertUsage, isCA,
                                         &reqKeyUsage,
                                         &reqCertType);

        requiredKeyUsages |= reqKeyUsage;

        PKIX_CHECK(
            PKIX_PL_OID_Create(ekuOidStrings[ekuIndex], &ekuOid,
                               plContext),
            PKIX_OIDCREATEFAILED);

        PKIX_CHECK(
            PKIX_List_AppendItem(ekuOidsList, (PKIX_PL_Object *)ekuOid,
                                 plContext),
            PKIX_LISTAPPENDITEMFAILED);

        PKIX_DECREF(ekuOid);
    }

    PKIX_CHECK(
        cert_NssKeyUsagesToPkix(requiredKeyUsages, ppkixKU, plContext),
        PKIX_NSSCERTIFICATEUSAGETOPKIXKUANDEKUFAILED);

    *ppkixEKUList = ekuOidsList;
    ekuOidsList = NULL;

cleanup:

    PKIX_DECREF(ekuOid);
    PKIX_DECREF(ekuOidsList);

    PKIX_RETURN(CERTVFYPKIX);
}

#endif

/*
 * FUNCTION: cert_ProcessingParamsSetKeyAndCertUsage
 * DESCRIPTION:
 *
 * Converts cert usage to pkix KU type and sets
 * converted data into PKIX_ProcessingParams object. It also sets
 * proper cert usage into nsscontext object.
 *
 * PARAMETERS:
 *  "procParams"
 *      Pointer to PKIX_ProcessingParams used during validation.
 *  "requiredCertUsage"
 *      Required certificate usages the certificate and chain is built and
 *      validated for.
 *  "requiredKeyUsage"
 *      Request additional key usages the certificate should be validated for.
 *  "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 Cert Verify Error if the function fails in an unrecoverable way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
cert_ProcessingParamsSetKeyAndCertUsage(
    PKIX_ProcessingParams *procParams,
    SECCertUsage requiredCertUsage,
    PRUint32 requiredKeyUsages,
    void *plContext)
{
    PKIX_CertSelector *certSelector = NULL;
    PKIX_ComCertSelParams *certSelParams = NULL;
    PKIX_PL_NssContext *nssContext = (PKIX_PL_NssContext *)plContext;

    PKIX_ENTER(CERTVFYPKIX, "cert_ProcessingParamsSetKeyAndCertUsage");
    PKIX_NULLCHECK_TWO(procParams, nssContext);

    PKIX_CHECK(
        pkix_pl_NssContext_SetCertUsage(
            ((SECCertificateUsage)1) << requiredCertUsage, nssContext),
        PKIX_NSSCONTEXTSETCERTUSAGEFAILED);

    if (requiredKeyUsages) {
        PKIX_CHECK(
            PKIX_ProcessingParams_GetTargetCertConstraints(procParams,
                                                           &certSelector, plContext),
            PKIX_PROCESSINGPARAMSGETTARGETCERTCONSTRAINTSFAILED);

        PKIX_CHECK(
            PKIX_CertSelector_GetCommonCertSelectorParams(certSelector,
                                                          &certSelParams, plContext),
            PKIX_CERTSELECTORGETCOMMONCERTSELECTORPARAMSFAILED);

        PKIX_CHECK(
            PKIX_ComCertSelParams_SetKeyUsage(certSelParams, requiredKeyUsages,
                                              plContext),
            PKIX_COMCERTSELPARAMSSETKEYUSAGEFAILED);
    }
cleanup:
    PKIX_DECREF(certSelector);
    PKIX_DECREF(certSelParams);

    PKIX_RETURN(CERTVFYPKIX);
}

/*
 * Unused parameters:
 *
 *  CERTCertList *initialChain,
 *  CERTCertStores certStores,
 *  CERTCertRevCheckers certRevCheckers,
 *  CERTCertChainCheckers certChainCheckers,
 *  SECItem *initPolicies,
 *  PRBool policyQualifierRejected,
 *  PRBool anyPolicyInhibited,
 *  PRBool reqExplicitPolicy,
 *  PRBool policyMappingInhibited,
 *  PKIX_CertSelector certConstraints,
 */

/*
 * FUNCTION: cert_CreatePkixProcessingParams
 * DESCRIPTION:
 *
 * Creates and fills in PKIX_ProcessingParams structure to be used
 * for certificate chain building.
 *
 * PARAMETERS:
 *  "cert"
 *      Pointer to the CERTCertificate: the leaf certificate of a chain.
 *  "time"
 *      Validity time.
 *  "wincx"
 *      Nss db password token.
 *  "useArena"
 *      Flags to use arena for data allocation during chain building process.
 *  "pprocParams"
 *      Address to return created processing parameters.
 *  "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 Cert Verify Error if the function fails in an unrecoverable way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
cert_CreatePkixProcessingParams(
    CERTCertificate *cert,
    PRBool checkSig, /* not used yet. See bug 391476 */
    PRTime time,
    void *wincx,
    PRBool useArena,
    PRBool disableOCSPRemoteFetching,
    PKIX_ProcessingParams **pprocParams,
    void **pplContext)
{
    PKIX_List *anchors = NULL;
    PKIX_PL_Cert *targetCert = NULL;
    PKIX_PL_Date *date = NULL;
    PKIX_ProcessingParams *procParams = NULL;
    PKIX_CertSelector *certSelector = NULL;
    PKIX_ComCertSelParams *certSelParams = NULL;
    PKIX_CertStore *certStore = NULL;
    PKIX_List *certStores = NULL;
    PKIX_RevocationChecker *revChecker = NULL;
    PKIX_UInt32 methodFlags = 0;
    void *plContext = NULL;
    CERTStatusConfig *statusConfig = NULL;

    PKIX_ENTER(CERTVFYPKIX, "cert_CreatePkixProcessingParams");
    PKIX_NULLCHECK_TWO(cert, pprocParams);

    PKIX_CHECK(
        PKIX_PL_NssContext_Create(0, useArena, wincx, &plContext),
        PKIX_NSSCONTEXTCREATEFAILED);

    *pplContext = plContext;

#ifdef PKIX_NOTDEF
    /* Functions should be implemented in patch for 390532 */
    PKIX_CHECK(
        pkix_pl_NssContext_SetCertSignatureCheck(checkSig,
                                                 (PKIX_PL_NssContext *)plContext),
        PKIX_NSSCONTEXTSETCERTSIGNCHECKFAILED);

#endif /* PKIX_NOTDEF */

    PKIX_CHECK(
        PKIX_ProcessingParams_Create(&procParams, plContext),
        PKIX_PROCESSINGPARAMSCREATEFAILED);

    PKIX_CHECK(
        PKIX_ComCertSelParams_Create(&certSelParams, plContext),
        PKIX_COMCERTSELPARAMSCREATEFAILED);

    PKIX_CHECK(
        PKIX_PL_Cert_CreateFromCERTCertificate(cert, &targetCert, plContext),
        PKIX_CERTCREATEWITHNSSCERTFAILED);

    PKIX_CHECK(
        PKIX_ComCertSelParams_SetCertificate(certSelParams,
                                             targetCert, plContext),
        PKIX_COMCERTSELPARAMSSETCERTIFICATEFAILED);

    PKIX_CHECK(
        PKIX_CertSelector_Create(NULL, NULL, &certSelector, plContext),
        PKIX_COULDNOTCREATECERTSELECTOROBJECT);

    PKIX_CHECK(
        PKIX_CertSelector_SetCommonCertSelectorParams(certSelector,
                                                      certSelParams, plContext),
        PKIX_CERTSELECTORSETCOMMONCERTSELECTORPARAMSFAILED);

    PKIX_CHECK(
        PKIX_ProcessingParams_SetTargetCertConstraints(procParams,
                                                       certSelector, plContext),
        PKIX_PROCESSINGPARAMSSETTARGETCERTCONSTRAINTSFAILED);

    /* Turn off quialification of target cert since leaf cert is
     * already check for date validity, key usages and extended
     * key usages. */
    PKIX_CHECK(
        PKIX_ProcessingParams_SetQualifyTargetCert(procParams, PKIX_FALSE,
                                                   plContext),
        PKIX_PROCESSINGPARAMSSETQUALIFYTARGETCERTFLAGFAILED);

    PKIX_CHECK(
        PKIX_PL_Pk11CertStore_Create(&certStore, plContext),
        PKIX_PK11CERTSTORECREATEFAILED);

    PKIX_CHECK(
        PKIX_List_Create(&certStores, plContext),
        PKIX_UNABLETOCREATELIST);

    PKIX_CHECK(
        PKIX_List_AppendItem(certStores, (PKIX_PL_Object *)certStore,
                             plContext),
        PKIX_LISTAPPENDITEMFAILED);

    PKIX_CHECK(
        PKIX_ProcessingParams_SetCertStores(procParams, certStores,
                                            plContext),
        PKIX_PROCESSINGPARAMSADDCERTSTOREFAILED);

    PKIX_CHECK(
        PKIX_PL_Date_CreateFromPRTime(time, &date, plContext),
        PKIX_DATECREATEFROMPRTIMEFAILED);

    PKIX_CHECK(
        PKIX_ProcessingParams_SetDate(procParams, date, plContext),
        PKIX_PROCESSINGPARAMSSETDATEFAILED);

    PKIX_CHECK(
        PKIX_RevocationChecker_Create(
            PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST |
                PKIX_REV_MI_NO_OVERALL_INFO_REQUIREMENT,
            PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST |
                PKIX_REV_MI_NO_OVERALL_INFO_REQUIREMENT,
            &revChecker, plContext),
        PKIX_REVOCATIONCHECKERCREATEFAILED);

    PKIX_CHECK(
        PKIX_ProcessingParams_SetRevocationChecker(procParams, revChecker,
                                                   plContext),
        PKIX_PROCESSINGPARAMSSETREVOCATIONCHECKERFAILED);

    /* CRL method flags */
    methodFlags =
        PKIX_REV_M_TEST_USING_THIS_METHOD |
        PKIX_REV_M_FORBID_NETWORK_FETCHING |
        PKIX_REV_M_SKIP_TEST_ON_MISSING_SOURCE | /* 0 */
        PKIX_REV_M_IGNORE_MISSING_FRESH_INFO |   /* 0 */
        PKIX_REV_M_CONTINUE_TESTING_ON_FRESH_INFO;

    /* add CRL revocation method to check the leaf certificate */
    PKIX_CHECK(
        PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams,
                                                  PKIX_RevocationMethod_CRL, methodFlags,
                                                  0, NULL, PKIX_TRUE, plContext),
        PKIX_REVOCATIONCHECKERADDMETHODFAILED);

    /* add CRL revocation method for other certs in the chain. */
    PKIX_CHECK(
        PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams,
                                                  PKIX_RevocationMethod_CRL, methodFlags,
                                                  0, NULL, PKIX_FALSE, plContext),
        PKIX_REVOCATIONCHECKERADDMETHODFAILED);

    /* For compatibility with the old code, need to check that
     * statusConfig is set in the db handle and status checker
     * is defined befor allow ocsp status check on the leaf cert.*/
    statusConfig = CERT_GetStatusConfig(CERT_GetDefaultCertDB());
    if (statusConfig != NULL && statusConfig->statusChecker != NULL) {

        /* Enable OCSP revocation checking for the leaf cert. */
        /* OCSP method flags */
        methodFlags =
            PKIX_REV_M_TEST_USING_THIS_METHOD |
            PKIX_REV_M_ALLOW_NETWORK_FETCHING |        /* 0 */
            PKIX_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE | /* 0 */
            PKIX_REV_M_SKIP_TEST_ON_MISSING_SOURCE |   /* 0 */
            PKIX_REV_M_IGNORE_MISSING_FRESH_INFO |     /* 0 */
            PKIX_REV_M_CONTINUE_TESTING_ON_FRESH_INFO;

        /* Disabling ocsp fetching when checking the status
         * of ocsp response signer. Here and in the next if,
         * adjust flags for ocsp signer cert validation case. */
        if (disableOCSPRemoteFetching) {
            methodFlags |= PKIX_REV_M_FORBID_NETWORK_FETCHING;
        }

        if (ocsp_FetchingFailureIsVerificationFailure() &&
            !disableOCSPRemoteFetching) {
            methodFlags |=
                PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO;
        }

        /* add OCSP revocation method to check only the leaf certificate.*/
        PKIX_CHECK(
            PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams,
                                                      PKIX_RevocationMethod_OCSP, methodFlags,
                                                      1, NULL, PKIX_TRUE, plContext),
            PKIX_REVOCATIONCHECKERADDMETHODFAILED);
    }

    PKIX_CHECK(
        PKIX_ProcessingParams_SetAnyPolicyInhibited(procParams, PR_FALSE,
                                                    plContext),
        PKIX_PROCESSINGPARAMSSETANYPOLICYINHIBITED);

    PKIX_CHECK(
        PKIX_ProcessingParams_SetExplicitPolicyRequired(procParams, PR_FALSE,
                                                        plContext),
        PKIX_PROCESSINGPARAMSSETEXPLICITPOLICYREQUIRED);

    PKIX_CHECK(
        PKIX_ProcessingParams_SetPolicyMappingInhibited(procParams, PR_FALSE,
                                                        plContext),
        PKIX_PROCESSINGPARAMSSETPOLICYMAPPINGINHIBITED);

    *pprocParams = procParams;
    procParams = NULL;

cleanup:
    PKIX_DECREF(anchors);
    PKIX_DECREF(targetCert);
    PKIX_DECREF(date);
    PKIX_DECREF(certSelector);
    PKIX_DECREF(certSelParams);
    PKIX_DECREF(certStore);
    PKIX_DECREF(certStores);
    PKIX_DECREF(procParams);
    PKIX_DECREF(revChecker);

    PKIX_RETURN(CERTVFYPKIX);
}

/*
 * FUNCTION: cert_PkixToNssCertsChain
 * DESCRIPTION:
 *
 * Converts pkix cert list into nss cert list.
 *
 * PARAMETERS:
 *  "pkixCertChain"
 *      Pkix certificate list.
 *  "pvalidChain"
 *      An address of returned nss certificate list.
 *  "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 Cert Verify Error if the function fails in an unrecoverable way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
cert_PkixToNssCertsChain(
    PKIX_List *pkixCertChain,
    CERTCertList **pvalidChain,
    void *plContext)
{
    PLArenaPool *arena = NULL;
    CERTCertificate *nssCert = NULL;
    CERTCertList *validChain = NULL;
    PKIX_PL_Object *certItem = NULL;
    PKIX_UInt32 length = 0;
    PKIX_UInt32 i = 0;

    PKIX_ENTER(CERTVFYPKIX, "cert_PkixToNssCertsChain");
    PKIX_NULLCHECK_ONE(pvalidChain);

    if (pkixCertChain == NULL) {
        goto cleanup;
    }
    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        PKIX_ERROR(PKIX_OUTOFMEMORY);
    }
    validChain = (CERTCertList *)PORT_ArenaZAlloc(arena, sizeof(CERTCertList));
    if (validChain == NULL) {
        PKIX_ERROR(PKIX_PORTARENAALLOCFAILED);
    }
    PR_INIT_CLIST(&validChain->list);
    validChain->arena = arena;
    arena = NULL;

    PKIX_CHECK(
        PKIX_List_GetLength(pkixCertChain, &length, plContext),
        PKIX_LISTGETLENGTHFAILED);

    for (i = 0; i < length; i++) {
        CERTCertListNode *node = NULL;

        PKIX_CHECK(
            PKIX_List_GetItem(pkixCertChain, i, &certItem, plContext),
            PKIX_LISTGETITEMFAILED);

        PKIX_CHECK(
            PKIX_PL_Cert_GetCERTCertificate((PKIX_PL_Cert *)certItem, &nssCert,
                                            plContext),
            PKIX_CERTGETCERTCERTIFICATEFAILED);

        node =
            (CERTCertListNode *)PORT_ArenaZAlloc(validChain->arena,
                                                 sizeof(CERTCertListNode));
        if (node == NULL) {
            PKIX_ERROR(PKIX_PORTARENAALLOCFAILED);
        }

        PR_INSERT_BEFORE(&node->links, &validChain->list);

        node->cert = nssCert;
        nssCert = NULL;

        PKIX_DECREF(certItem);
    }

    *pvalidChain = validChain;

cleanup:
    if (PKIX_ERROR_RECEIVED) {
        if (validChain) {
            CERT_DestroyCertList(validChain);
        } else if (arena) {
            PORT_FreeArena(arena, PR_FALSE);
        }
        if (nssCert) {
            CERT_DestroyCertificate(nssCert);
        }
    }
    PKIX_DECREF(certItem);

    PKIX_RETURN(CERTVFYPKIX);
}

/*
 * FUNCTION: cert_BuildAndValidateChain
 * DESCRIPTION:
 *
 * The function builds and validates a cert chain based on certificate
 * selection criterias from procParams. This function call PKIX_BuildChain
 * to accomplish chain building. If PKIX_BuildChain returns with incomplete
 * IO, the function waits with PR_Poll until the blocking IO is finished and
 * return control back to PKIX_BuildChain.
 *
 * PARAMETERS:
 *  "procParams"
 *      Processing parameters to be used during chain building.
 *  "pResult"
 *      Returned build result.
 *  "pVerifyNode"
 *      Returned pointed to verify node structure: the tree-like structure
 *      that reports points of chain building failures.
 *  "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 Cert Verify Error if the function fails in an unrecoverable way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
cert_BuildAndValidateChain(
    PKIX_ProcessingParams *procParams,
    PKIX_BuildResult **pResult,
    PKIX_VerifyNode **pVerifyNode,
    void *plContext)
{
    PKIX_BuildResult *result = NULL;
    PKIX_VerifyNode *verifyNode = NULL;
    void *nbioContext = NULL;
    void *state = NULL;

    PKIX_ENTER(CERTVFYPKIX, "cert_BuildAndVerifyChain");
    PKIX_NULLCHECK_TWO(procParams, pResult);

    do {
        if (nbioContext && state) {
            /* PKIX-XXX: need to test functionality of NBIO handling in libPkix.
             * See bug 391180 */
            PRInt32 filesReady = 0;
            PRPollDesc *pollDesc = (PRPollDesc *)nbioContext;
            filesReady = PR_Poll(pollDesc, 1, PR_INTERVAL_NO_TIMEOUT);
            if (filesReady <= 0) {
                PKIX_ERROR(PKIX_PRPOLLRETBADFILENUM);
            }
        }

        PKIX_CHECK(
            PKIX_BuildChain(procParams, &nbioContext, &state,
                            &result, &verifyNode, plContext),
            PKIX_UNABLETOBUILDCHAIN);

    } while (nbioContext && state);

    *pResult = result;

cleanup:
    if (pVerifyNode) {
        *pVerifyNode = verifyNode;
    }

    PKIX_RETURN(CERTVFYPKIX);
}

/*
 * FUNCTION: cert_PkixErrorToNssCode
 * DESCRIPTION:
 *
 * Converts pkix error(PKIX_Error) structure to PR error codes.
 *
 * PKIX-XXX to be implemented. See 391183.
 *
 * PARAMETERS:
 *  "error"
 *      Pkix error that will be converted.
 *  "nssCode"
 *      Corresponding nss error code.
 *  "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 Cert Verify Error if the function fails in an unrecoverable way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
cert_PkixErrorToNssCode(
    PKIX_Error *error,
    SECErrorCodes *pNssErr,
    void *plContext)
{
    int errLevel = 0;
    PKIX_Int32 nssErr = 0;
    PKIX_Error *errPtr = error;

    PKIX_ENTER(CERTVFYPKIX, "cert_PkixErrorToNssCode");
    PKIX_NULLCHECK_TWO(error, pNssErr);

    /* Loop until we find at least one error with non-null
     * plErr code, that is going to be nss error code. */
    while (errPtr) {
        if (errPtr->plErr && !nssErr) {
            nssErr = errPtr->plErr;
            if (!pkixLog)
                break;
        }
        if (pkixLog) {
#ifdef PKIX_ERROR_DESCRIPTION
            PR_LOG(pkixLog, 2, ("Error at level %d: %s\n", errLevel,
                                PKIX_ErrorText[errPtr->errCode]));
#else
            PR_LOG(pkixLog, 2, ("Error at level %d: Error code %d\n", errLevel,
                                errPtr->errCode));
#endif /* PKIX_ERROR_DESCRIPTION */
        }
        errPtr = errPtr->cause;
        errLevel += 1;
    }
    PORT_Assert(nssErr);
    if (!nssErr) {
        *pNssErr = SEC_ERROR_LIBPKIX_INTERNAL;
    } else {
        *pNssErr = nssErr;
    }

    PKIX_RETURN(CERTVFYPKIX);
}

/*
 * FUNCTION: cert_GetLogFromVerifyNode
 * DESCRIPTION:
 *
 * Recursive function that converts verify node tree-like set of structures
 * to CERTVerifyLog.
 *
 * PARAMETERS:
 *  "log"
 *      Pointed to already allocated CERTVerifyLog structure.
 *  "node"
 *      A node of PKIX_VerifyNode tree.
 *  "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 Cert Verify Error if the function fails in an unrecoverable way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
cert_GetLogFromVerifyNode(
    CERTVerifyLog *log,
    PKIX_VerifyNode *node,
    void *plContext)
{
    PKIX_List *children = NULL;
    PKIX_VerifyNode *childNode = NULL;

    PKIX_ENTER(CERTVFYPKIX, "cert_GetLogFromVerifyNode");

    children = node->children;

    if (children == NULL) {
        PKIX_ERRORCODE errCode = PKIX_ANCHORDIDNOTCHAINTOCERT;
        if (node->error && node->error->errCode != errCode) {
            if (log != NULL) {
                SECErrorCodes nssErrorCode = 0;
                CERTCertificate *cert = NULL;

                cert = node->verifyCert->nssCert;

                PKIX_CHECK(
                    cert_PkixErrorToNssCode(node->error, &nssErrorCode,
                                            plContext),
                    PKIX_GETPKIXERRORCODEFAILED);

                cert_AddToVerifyLog(log, cert, nssErrorCode, node->depth, NULL);
            }
        }
        PKIX_RETURN(CERTVFYPKIX);
    } else {
        PRUint32 i = 0;
        PKIX_UInt32 length = 0;

        PKIX_CHECK(
            PKIX_List_GetLength(children, &length, plContext),
            PKIX_LISTGETLENGTHFAILED);

        for (i = 0; i < length; i++) {

            PKIX_CHECK(
                PKIX_List_GetItem(children, i, (PKIX_PL_Object **)&childNode,
                                  plContext),
                PKIX_LISTGETITEMFAILED);

            PKIX_CHECK(
                cert_GetLogFromVerifyNode(log, childNode, plContext),
                PKIX_ERRORINRECURSIVEEQUALSCALL);

            PKIX_DECREF(childNode);
        }
    }

cleanup:
    PKIX_DECREF(childNode);

    PKIX_RETURN(CERTVFYPKIX);
}

/*
 * FUNCTION: cert_GetBuildResults
 * DESCRIPTION:
 *
 * Converts pkix build results to nss results. This function is called
 * regardless of build result.
 *
 * If it called after chain was successfully constructed, then it will
 * convert:
 *   * pkix cert list that represent the chain to nss cert list
 *   * trusted root the chain was anchored to nss certificate.
 *
 * In case of failure it will convert:
 *   * pkix error to PR error code(will set it with PORT_SetError)
 *   * pkix validation log to nss CERTVerifyLog
 *
 * PARAMETERS:
 *  "buildResult"
 *      Build results returned by PKIX_BuildChain.
 *  "verifyNode"
 *      Tree-like structure of chain building/validation failures
 *      returned by PKIX_BuildChain. Ignored in case of success.
 *  "error"
 *      Final error returned by PKIX_BuildChain. Should be NULL in
 *      case of success.
 *  "log"
 *      Address of pre-allocated(if not NULL) CERTVerifyLog structure.
 *  "ptrustedRoot"
 *      Address of returned trusted root the chain was anchored to.
 *  "pvalidChain"
 *      Address of returned valid chain.
 *  "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 Cert Verify Error if the function fails in an unrecoverable way.
 *  Returns a Fatal Error if the function fails in an unrecoverable way.
 */
static PKIX_Error *
cert_GetBuildResults(
    PKIX_BuildResult *buildResult,
    PKIX_VerifyNode *verifyNode,
    PKIX_Error *error,
    CERTVerifyLog *log,
    CERTCertificate **ptrustedRoot,
    CERTCertList **pvalidChain,
    void *plContext)
{
    PKIX_ValidateResult *validResult = NULL;
    CERTCertList *validChain = NULL;
    CERTCertificate *trustedRoot = NULL;
    PKIX_TrustAnchor *trustAnchor = NULL;
    PKIX_PL_Cert *trustedCert = NULL;
    PKIX_List *pkixCertChain = NULL;

    PKIX_ENTER(CERTVFYPKIX, "cert_GetBuildResults");
    if (buildResult == NULL && error == NULL) {
        PKIX_ERROR(PKIX_NULLARGUMENT);
    }

    if (error) {
        SECErrorCodes nssErrorCode = 0;
        if (verifyNode) {
            PKIX_Error *tmpError =
                cert_GetLogFromVerifyNode(log, verifyNode, plContext);
            if (tmpError) {
                PKIX_PL_Object_DecRef((PKIX_PL_Object *)tmpError, plContext);
            }
        }
        cert_PkixErrorToNssCode(error, &nssErrorCode, plContext);
        PORT_SetError(nssErrorCode);
        goto cleanup;
    }

    if (pvalidChain) {
        PKIX_CHECK(
            PKIX_BuildResult_GetCertChain(buildResult, &pkixCertChain,
                                          plContext),
            PKIX_BUILDRESULTGETCERTCHAINFAILED);

        PKIX_CHECK(
            cert_PkixToNssCertsChain(pkixCertChain, &validChain, plContext),
            PKIX_CERTCHAINTONSSCHAINFAILED);
    }

    if (ptrustedRoot) {
        PKIX_CHECK(
            PKIX_BuildResult_GetValidateResult(buildResult, &validResult,
                                               plContext),
            PKIX_BUILDRESULTGETVALIDATERESULTFAILED);

        PKIX_CHECK(
            PKIX_ValidateResult_GetTrustAnchor(validResult, &trustAnchor,
                                               plContext),
            PKIX_VALIDATERESULTGETTRUSTANCHORFAILED);

        PKIX_CHECK(
            PKIX_TrustAnchor_GetTrustedCert(trustAnchor, &trustedCert,
                                            plContext),
            PKIX_TRUSTANCHORGETTRUSTEDCERTFAILED);

        PKIX_CHECK(
            PKIX_PL_Cert_GetCERTCertificate(trustedCert, &trustedRoot,
                                            plContext),
            PKIX_CERTGETCERTCERTIFICATEFAILED);
    }

    PORT_Assert(!PKIX_ERROR_RECEIVED);

    if (trustedRoot) {
        *ptrustedRoot = trustedRoot;
    }
    if (validChain) {
        *pvalidChain = validChain;
    }

cleanup:
    if (PKIX_ERROR_RECEIVED) {
        if (trustedRoot) {
            CERT_DestroyCertificate(trustedRoot);
        }
        if (validChain) {
            CERT_DestroyCertList(validChain);
        }
    }
    PKIX_DECREF(trustAnchor);
    PKIX_DECREF(trustedCert);
    PKIX_DECREF(pkixCertChain);
    PKIX_DECREF(validResult);
    PKIX_DECREF(error);
    PKIX_DECREF(verifyNode);
    PKIX_DECREF(buildResult);

    PKIX_RETURN(CERTVFYPKIX);
}

/*
 * FUNCTION: cert_VerifyCertChainPkix
 * DESCRIPTION:
 *
 * The main wrapper function that is called from CERT_VerifyCert and
 * CERT_VerifyCACertForUsage functions to validate cert with libpkix.
 *
 * PARAMETERS:
 *  "cert"
 *      Leaf certificate of a chain we want to build.
 *  "checkSig"
 *      Certificate signatures will not be verified if this
 *      flag is set to PR_FALSE.
 *  "requiredUsage"
 *      Required usage for certificate and chain.
 *  "time"
 *      Validity time.
 *  "wincx"
 *      Nss database password token.
 *  "log"
 *      Address of already allocated CERTVerifyLog structure. Not
 *      used if NULL;
 *  "pSigerror"
 *      Address of PRBool. If not NULL, returns true is cert chain
 *      was invalidated because of bad certificate signature.
 *  "pRevoked"
 *      Address of PRBool. If not NULL, returns true is cert chain
 *      was invalidated because a revoked certificate was found in
 *      the chain.
 * THREAD SAFETY:
 *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
 * RETURNS:
 *  SECFailure is chain building process has failed. SECSuccess otherwise.
 */
SECStatus
cert_VerifyCertChainPkix(
    CERTCertificate *cert,
    PRBool checkSig,
    SECCertUsage requiredUsage,
    PRTime time,
    void *wincx,
    CERTVerifyLog *log,
    PRBool *pSigerror,
    PRBool *pRevoked)
{
    PKIX_ProcessingParams *procParams = NULL;
    PKIX_BuildResult *result = NULL;
    PKIX_VerifyNode *verifyNode = NULL;
    PKIX_Error *error = NULL;

    SECStatus rv = SECFailure;
    void *plContext = NULL;

#ifdef PKIX_OBJECT_LEAK_TEST
    int leakedObjNum = 0;
    int memLeakLoopCount = 0;
    int objCountTable[PKIX_NUMTYPES];
    int fnInvLocalCount = 0;
    PKIX_Boolean savedUsePkixEngFlag = usePKIXValidationEngine;

    if (usePKIXValidationEngine) {
        /* current memory leak testing implementation does not allow
         * to run simultaneous tests one the same or a different threads.
         * Setting the variable to false, to make additional chain
         * validations be handled by old nss. */
        usePKIXValidationEngine = PR_FALSE;
    }
    testStartFnStackPosition = 2;
    fnStackNameArr[0] = "cert_VerifyCertChainPkix";
    fnStackInvCountArr[0] = 0;
    PKIX_Boolean abortOnLeak =
        (PR_GetEnvSecure("PKIX_OBJECT_LEAK_TEST_ABORT_ON_LEAK") == NULL) ? PKIX_FALSE
                                                                         : PKIX_TRUE;
    runningLeakTest = PKIX_TRUE;

    /* Prevent multi-threaded run of object leak test */
    fnInvLocalCount = PR_ATOMIC_INCREMENT(&parallelFnInvocationCount);
    PORT_Assert(fnInvLocalCount == 1);

    do {
        rv = SECFailure;
        plContext = NULL;
        procParams = NULL;
        result = NULL;
        verifyNode = NULL;
        error = NULL;
        errorGenerated = PKIX_FALSE;
        stackPosition = 0;

        if (leakedObjNum) {
            pkix_pl_lifecycle_ObjectTableUpdate(objCountTable);
        }
        memLeakLoopCount += 1;
#endif /* PKIX_OBJECT_LEAK_TEST */

        error =
            cert_CreatePkixProcessingParams(cert, checkSig, time, wincx,
                                            PR_FALSE /*use arena*/,
                                            requiredUsage == certUsageStatusResponder,
                                            &procParams, &plContext);
        if (error) {
            goto cleanup;
        }

        error =
            cert_ProcessingParamsSetKeyAndCertUsage(procParams, requiredUsage, 0,
                                                    plContext);
        if (error) {
            goto cleanup;
        }

        error =
            cert_BuildAndValidateChain(procParams, &result, &verifyNode, plContext);
        if (error) {
            goto cleanup;
        }

        if (pRevoked) {
            /* Currently always PR_FALSE. Will be fixed as a part of 394077 */
            *pRevoked = PR_FALSE;
        }
        if (pSigerror) {
            /* Currently always PR_FALSE. Will be fixed as a part of 394077 */
            *pSigerror = PR_FALSE;
        }
        rv = SECSuccess;

    cleanup:
        error = cert_GetBuildResults(result, verifyNode, error, log, NULL, NULL,
                                     plContext);
        if (error) {
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
        }
        if (procParams) {
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)procParams, plContext);
        }
        if (plContext) {
            PKIX_PL_NssContext_Destroy(plContext);
        }

#ifdef PKIX_OBJECT_LEAK_TEST
        leakedObjNum =
            pkix_pl_lifecycle_ObjectLeakCheck(leakedObjNum ? objCountTable : NULL);

        if (pkixLog && leakedObjNum) {
            PR_LOG(pkixLog, 1, ("The generated error caused an object leaks. Loop %d."
                                "Stack %s\n",
                                memLeakLoopCount, errorFnStackString));
        }
        PR_Free(errorFnStackString);
        errorFnStackString = NULL;
        if (abortOnLeak) {
            PORT_Assert(leakedObjNum == 0);
        }

    } while (errorGenerated);

    runningLeakTest = PKIX_FALSE;
    PR_ATOMIC_DECREMENT(&parallelFnInvocationCount);
    usePKIXValidationEngine = savedUsePkixEngFlag;
#endif /* PKIX_OBJECT_LEAK_TEST */

    return rv;
}

PKIX_CertSelector *
cert_GetTargetCertConstraints(CERTCertificate *target, void *plContext)
{
    PKIX_ComCertSelParams *certSelParams = NULL;
    PKIX_CertSelector *certSelector = NULL;
    PKIX_CertSelector *r = NULL;
    PKIX_PL_Cert *eeCert = NULL;
    PKIX_Error *error = NULL;

    error = PKIX_PL_Cert_CreateFromCERTCertificate(target, &eeCert, plContext);
    if (error != NULL)
        goto cleanup;

    error = PKIX_CertSelector_Create(NULL, NULL, &certSelector, plContext);
    if (error != NULL)
        goto cleanup;

    error = PKIX_ComCertSelParams_Create(&certSelParams, plContext);
    if (error != NULL)
        goto cleanup;

    error = PKIX_ComCertSelParams_SetCertificate(
        certSelParams, eeCert, plContext);
    if (error != NULL)
        goto cleanup;

    error = PKIX_CertSelector_SetCommonCertSelectorParams(certSelector, certSelParams, plContext);
    if (error != NULL)
        goto cleanup;

    error = PKIX_PL_Object_IncRef((PKIX_PL_Object *)certSelector, plContext);
    if (error == NULL)
        r = certSelector;

cleanup:
    if (certSelParams != NULL)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)certSelParams, plContext);

    if (eeCert != NULL)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)eeCert, plContext);

    if (certSelector != NULL)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)certSelector, plContext);

    if (error != NULL) {
        SECErrorCodes nssErr;

        cert_PkixErrorToNssCode(error, &nssErr, plContext);
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
        PORT_SetError(nssErr);
    }

    return r;
}

static PKIX_List *
cert_GetCertStores(void *plContext)
{
    PKIX_CertStore *certStore = NULL;
    PKIX_List *certStores = NULL;
    PKIX_List *r = NULL;
    PKIX_Error *error = NULL;

    error = PKIX_PL_Pk11CertStore_Create(&certStore, plContext);
    if (error != NULL)
        goto cleanup;

    error = PKIX_List_Create(&certStores, plContext);
    if (error != NULL)
        goto cleanup;

    error = PKIX_List_AppendItem(certStores,
                                 (PKIX_PL_Object *)certStore, plContext);
    if (error != NULL)
        goto cleanup;

    error = PKIX_PL_Object_IncRef((PKIX_PL_Object *)certStores, plContext);
    if (error == NULL)
        r = certStores;

cleanup:
    if (certStores != NULL)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)certStores, plContext);

    if (certStore != NULL)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)certStore, plContext);

    if (error != NULL) {
        SECErrorCodes nssErr;

        cert_PkixErrorToNssCode(error, &nssErr, plContext);
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
        PORT_SetError(nssErr);
    }

    return r;
}

struct fake_PKIX_PL_CertStruct {
    CERTCertificate *nssCert;
};

/* This needs to be part of the PKIX_PL_* */
/* This definitely needs to go away, and be replaced with
   a real accessor function in PKIX */
static CERTCertificate *
cert_NSSCertFromPKIXCert(const PKIX_PL_Cert *pkix_cert)
{
    struct fake_PKIX_PL_CertStruct *fcert = NULL;

    fcert = (struct fake_PKIX_PL_CertStruct *)pkix_cert;

    return CERT_DupCertificate(fcert->nssCert);
}

PKIX_List *
cert_PKIXMakeOIDList(const SECOidTag *oids, int oidCount, void *plContext)
{
    PKIX_List *r = NULL;
    PKIX_List *policyList = NULL;
    PKIX_PL_OID *policyOID = NULL;
    PKIX_Error *error = NULL;
    int i;

    error = PKIX_List_Create(&policyList, plContext);
    if (error != NULL) {
        goto cleanup;
    }

    for (i = 0; i < oidCount; i++) {
        error = PKIX_PL_OID_Create(oids[i], &policyOID, plContext);
        if (error) {
            goto cleanup;
        }
        error = PKIX_List_AppendItem(policyList,
                                     (PKIX_PL_Object *)policyOID, plContext);
        if (error != NULL) {
            goto cleanup;
        }
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyOID, plContext);
        policyOID = NULL;
    }

    error = PKIX_List_SetImmutable(policyList, plContext);
    if (error != NULL)
        goto cleanup;

    error = PKIX_PL_Object_IncRef((PKIX_PL_Object *)policyList, plContext);
    if (error == NULL)
        r = policyList;

cleanup:
    if (policyOID != NULL) {
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyOID, plContext);
    }
    if (policyList != NULL) {
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyList, plContext);
    }
    if (error != NULL) {
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
    }

    return r;
}

CERTValOutParam *
cert_pkix_FindOutputParam(CERTValOutParam *params, const CERTValParamOutType t)
{
    CERTValOutParam *i;
    if (params == NULL) {
        return NULL;
    }
    for (i = params; i->type != cert_po_end; i++) {
        if (i->type == t) {
            return i;
        }
    }
    return NULL;
}

static PKIX_Error *
setRevocationMethod(PKIX_RevocationChecker *revChecker,
                    PKIX_ProcessingParams *procParams,
                    const CERTRevocationTests *revTest,
                    CERTRevocationMethodIndex certRevMethod,
                    PKIX_RevocationMethodType pkixRevMethod,
                    PKIX_Boolean verifyResponderUsages,
                    PKIX_Boolean isLeafTest,
                    void *plContext)
{
    PKIX_UInt32 methodFlags = 0;
    PKIX_Error *error = NULL;
    PKIX_UInt32 priority = 0;

    if (revTest->number_of_defined_methods <= (PRUint32)certRevMethod) {
        return NULL;
    }
    if (revTest->preferred_methods) {
        unsigned int i = 0;
        for (; i < revTest->number_of_preferred_methods; i++) {
            if (revTest->preferred_methods[i] == certRevMethod)
                break;
        }
        priority = i;
    }
    methodFlags = revTest->cert_rev_flags_per_method[certRevMethod];
    if (verifyResponderUsages &&
        pkixRevMethod == PKIX_RevocationMethod_OCSP) {
        methodFlags |= PKIX_REV_M_FORBID_NETWORK_FETCHING;
    }
    error =
        PKIX_RevocationChecker_CreateAndAddMethod(revChecker, procParams,
                                                  pkixRevMethod, methodFlags,
                                                  priority, NULL,
                                                  isLeafTest, plContext);
    return error;
}

SECStatus
cert_pkixSetParam(PKIX_ProcessingParams *procParams,
                  const CERTValInParam *param, void *plContext)
{
    PKIX_Error *error = NULL;
    SECStatus r = SECSuccess;
    PKIX_PL_Date *date = NULL;
    PKIX_List *policyOIDList = NULL;
    PKIX_List *certListPkix = NULL;
    const CERTRevocationFlags *flags;
    SECErrorCodes errCode = SEC_ERROR_INVALID_ARGS;
    const CERTCertList *certList = NULL;
    CERTCertListNode *node;
    PKIX_PL_Cert *certPkix = NULL;
    PKIX_TrustAnchor *trustAnchor = NULL;
    PKIX_RevocationChecker *revChecker = NULL;
    PKIX_PL_NssContext *nssContext = (PKIX_PL_NssContext *)plContext;

    /* XXX we need a way to map generic PKIX error to generic NSS errors */

    switch (param->type) {

        case cert_pi_policyOID:

            /* needed? */
            error = PKIX_ProcessingParams_SetExplicitPolicyRequired(
                procParams, PKIX_TRUE, plContext);

            if (error != NULL) {
                break;
            }

            policyOIDList = cert_PKIXMakeOIDList(param->value.array.oids,
                                                 param->value.arraySize, plContext);
            if (policyOIDList == NULL) {
                r = SECFailure;
                PORT_SetError(SEC_ERROR_INVALID_ARGS);
                break;
            }

            error = PKIX_ProcessingParams_SetInitialPolicies(
                procParams, policyOIDList, plContext);
            break;

        case cert_pi_date:
            if (param->value.scalar.time == 0) {
                error = PKIX_PL_Date_Create_UTCTime(NULL, &date, plContext);
                if (error != NULL) {
                    errCode = SEC_ERROR_INVALID_TIME;
                    break;
                }
            } else {
                error = pkix_pl_Date_CreateFromPRTime(param->value.scalar.time,
                                                      &date, plContext);
                if (error != NULL) {
                    errCode = SEC_ERROR_INVALID_TIME;
                    break;
                }
            }

            error = PKIX_ProcessingParams_SetDate(procParams, date, plContext);
            if (error != NULL) {
                errCode = SEC_ERROR_INVALID_TIME;
            }
            break;

        case cert_pi_revocationFlags: {
            PKIX_UInt32 leafIMFlags = 0;
            PKIX_UInt32 chainIMFlags = 0;
            PKIX_Boolean validatingResponderCert = PKIX_FALSE;

            flags = param->value.pointer.revocation;
            if (!flags) {
                PORT_SetError(errCode);
                r = SECFailure;
                break;
            }

            leafIMFlags =
                flags->leafTests.cert_rev_method_independent_flags;
            chainIMFlags =
                flags->chainTests.cert_rev_method_independent_flags;

            error =
                PKIX_RevocationChecker_Create(leafIMFlags, chainIMFlags,
                                              &revChecker, plContext);
            if (error) {
                break;
            }

            error =
                PKIX_ProcessingParams_SetRevocationChecker(procParams,
                                                           revChecker, plContext);
            if (error) {
                break;
            }

            if (((PKIX_PL_NssContext *)plContext)->certificateUsage &
                certificateUsageStatusResponder) {
                validatingResponderCert = PKIX_TRUE;
            }

            error = setRevocationMethod(revChecker,
                                        procParams, &flags->leafTests,
                                        cert_revocation_method_crl,
                                        PKIX_RevocationMethod_CRL,
                                        validatingResponderCert,
                                        PKIX_TRUE, plContext);
            if (error) {
                break;
            }

            error = setRevocationMethod(revChecker,
                                        procParams, &flags->leafTests,
                                        cert_revocation_method_ocsp,
                                        PKIX_RevocationMethod_OCSP,
                                        validatingResponderCert,
                                        PKIX_TRUE, plContext);
            if (error) {
                break;
            }

            error = setRevocationMethod(revChecker,
                                        procParams, &flags->chainTests,
                                        cert_revocation_method_crl,
                                        PKIX_RevocationMethod_CRL,
                                        validatingResponderCert,
                                        PKIX_FALSE, plContext);
            if (error) {
                break;
            }

            error = setRevocationMethod(revChecker,
                                        procParams, &flags->chainTests,
                                        cert_revocation_method_ocsp,
                                        PKIX_RevocationMethod_OCSP,
                                        validatingResponderCert,
                                        PKIX_FALSE, plContext);
            if (error) {
                break;
            }

        } break;

        case cert_pi_trustAnchors:
            certList = param->value.pointer.chain;
            if (!certList) {
                PORT_SetError(errCode);
                r = SECFailure;
                break;
            }
            error = PKIX_List_Create(&certListPkix, plContext);
            if (error != NULL) {
                break;
            }
            for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
                 node = CERT_LIST_NEXT(node)) {
                error = PKIX_PL_Cert_CreateFromCERTCertificate(node->cert,
                                                               &certPkix, plContext);
                if (error) {
                    break;
                }
                error = PKIX_TrustAnchor_CreateWithCert(certPkix, &trustAnchor,
                                                        plContext);
                if (error) {
                    break;
                }
                error = PKIX_List_AppendItem(certListPkix,
                                             (PKIX_PL_Object *)trustAnchor, plContext);
                if (error) {
                    break;
                }
                PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchor, plContext);
                trustAnchor = NULL;
                PKIX_PL_Object_DecRef((PKIX_PL_Object *)certPkix, plContext);
                certPkix = NULL;
            }
            error =
                PKIX_ProcessingParams_SetTrustAnchors(procParams, certListPkix,
                                                      plContext);
            break;

        case cert_pi_useAIACertFetch:
            error =
                PKIX_ProcessingParams_SetUseAIAForCertFetching(procParams,
                                                               (PRBool)(param->value.scalar.b !=
                                                                        0),
                                                               plContext);
            break;

        case cert_pi_chainVerifyCallback: {
            const CERTChainVerifyCallback *chainVerifyCallback =
                param->value.pointer.chainVerifyCallback;
            if (!chainVerifyCallback || !chainVerifyCallback->isChainValid) {
                PORT_SetError(errCode);
                r = SECFailure;
                break;
            }

            nssContext->chainVerifyCallback = *chainVerifyCallback;
        } break;

        case cert_pi_useOnlyTrustAnchors:
            error =
                PKIX_ProcessingParams_SetUseOnlyTrustAnchors(procParams,
                                                             (PRBool)(param->value.scalar.b !=
                                                                      0),
                                                             plContext);
            break;

        default:
            PORT_SetError(errCode);
            r = SECFailure;
            break;
    }

    if (policyOIDList != NULL)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)policyOIDList, plContext);

    if (date != NULL)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)date, plContext);

    if (revChecker != NULL)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)revChecker, plContext);

    if (certListPkix)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)certListPkix, plContext);

    if (trustAnchor)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchor, plContext);

    if (certPkix)
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)certPkix, plContext);

    if (error != NULL) {
        PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
        PORT_SetError(errCode);
        r = SECFailure;
    }

    return r;
}

void
cert_pkixDestroyValOutParam(CERTValOutParam *params)
{
    CERTValOutParam *i;

    if (params == NULL) {
        return;
    }
    for (i = params; i->type != cert_po_end; i++) {
        switch (i->type) {
            case cert_po_trustAnchor:
                if (i->value.pointer.cert) {
                    CERT_DestroyCertificate(i->value.pointer.cert);
                    i->value.pointer.cert = NULL;
                }
                break;

            case cert_po_certList:
                if (i->value.pointer.chain) {
                    CERT_DestroyCertList(i->value.pointer.chain);
                    i->value.pointer.chain = NULL;
                }
                break;

            default:
                break;
        }
    }
}

static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_LeafFlags[2] = {
    /* crl */
    CERT_REV_M_TEST_USING_THIS_METHOD |
        CERT_REV_M_FORBID_NETWORK_FETCHING |
        CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
    /* ocsp */
    CERT_REV_M_TEST_USING_THIS_METHOD
};

static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_ChainFlags[2] = {
    /* crl */
    CERT_REV_M_TEST_USING_THIS_METHOD |
        CERT_REV_M_FORBID_NETWORK_FETCHING |
        CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
    /* ocsp */
    0
};

static CERTRevocationMethodIndex
    certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_Method_Preference = {
        cert_revocation_method_crl
    };

static const CERTRevocationFlags certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy = {
    { /* leafTests */
      2,
      certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_LeafFlags,
      1,
      &certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_Method_Preference,
      0 },
    { /* chainTests */
      2,
      certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy_ChainFlags,
      0,
      0,
      0 }
};

extern const CERTRevocationFlags *
CERT_GetClassicOCSPEnabledSoftFailurePolicy()
{
    return &certRev_NSS_3_11_Ocsp_Enabled_Soft_Policy;
}

static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_LeafFlags[2] = {
    /* crl */
    CERT_REV_M_TEST_USING_THIS_METHOD |
        CERT_REV_M_FORBID_NETWORK_FETCHING |
        CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
    /* ocsp */
    CERT_REV_M_TEST_USING_THIS_METHOD |
        CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO
};

static PRUint64 certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_ChainFlags[2] = {
    /* crl */
    CERT_REV_M_TEST_USING_THIS_METHOD |
        CERT_REV_M_FORBID_NETWORK_FETCHING |
        CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
    /* ocsp */
    0
};

static CERTRevocationMethodIndex
    certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_Method_Preference = {
        cert_revocation_method_crl
    };

static const CERTRevocationFlags certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy = {
    { /* leafTests */
      2,
      certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_LeafFlags,
      1,
      &certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_Method_Preference,
      0 },
    { /* chainTests */
      2,
      certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy_ChainFlags,
      0,
      0,
      0 }
};

extern const CERTRevocationFlags *
CERT_GetClassicOCSPEnabledHardFailurePolicy()
{
    return &certRev_NSS_3_11_Ocsp_Enabled_Hard_Policy;
}

static PRUint64 certRev_NSS_3_11_Ocsp_Disabled_Policy_LeafFlags[2] = {
    /* crl */
    CERT_REV_M_TEST_USING_THIS_METHOD |
        CERT_REV_M_FORBID_NETWORK_FETCHING |
        CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
    /* ocsp */
    0
};

static PRUint64 certRev_NSS_3_11_Ocsp_Disabled_Policy_ChainFlags[2] = {
    /* crl */
    CERT_REV_M_TEST_USING_THIS_METHOD |
        CERT_REV_M_FORBID_NETWORK_FETCHING |
        CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO,
    /* ocsp */
    0
};

static const CERTRevocationFlags certRev_NSS_3_11_Ocsp_Disabled_Policy = {
    { /* leafTests */
      2,
      certRev_NSS_3_11_Ocsp_Disabled_Policy_LeafFlags,
      0,
      0,
      0 },
    { /* chainTests */
      2,
      certRev_NSS_3_11_Ocsp_Disabled_Policy_ChainFlags,
      0,
      0,
      0 }
};

extern const CERTRevocationFlags *
CERT_GetClassicOCSPDisabledPolicy()
{
    return &certRev_NSS_3_11_Ocsp_Disabled_Policy;
}

static PRUint64 certRev_PKIX_Verify_Nist_Policy_LeafFlags[2] = {
    /* crl */
    CERT_REV_M_TEST_USING_THIS_METHOD |
        CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO |
        CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE,
    /* ocsp */
    0
};

static PRUint64 certRev_PKIX_Verify_Nist_Policy_ChainFlags[2] = {
    /* crl */
    CERT_REV_M_TEST_USING_THIS_METHOD |
        CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO |
        CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE,
    /* ocsp */
    0
};

static const CERTRevocationFlags certRev_PKIX_Verify_Nist_Policy = {
    { /* leafTests */
      2,
      certRev_PKIX_Verify_Nist_Policy_LeafFlags,
      0,
      0,
      0 },
    { /* chainTests */
      2,
      certRev_PKIX_Verify_Nist_Policy_ChainFlags,
      0,
      0,
      0 }
};

extern const CERTRevocationFlags *
CERT_GetPKIXVerifyNistRevocationPolicy()
{
    return &certRev_PKIX_Verify_Nist_Policy;
}

CERTRevocationFlags *
CERT_AllocCERTRevocationFlags(
    PRUint32 number_leaf_methods, PRUint32 number_leaf_pref_methods,
    PRUint32 number_chain_methods, PRUint32 number_chain_pref_methods)
{
    CERTRevocationFlags *flags;

    flags = PORT_New(CERTRevocationFlags);
    if (!flags)
        return (NULL);

    flags->leafTests.number_of_defined_methods = number_leaf_methods;
    flags->leafTests.cert_rev_flags_per_method =
        PORT_NewArray(PRUint64, number_leaf_methods);

    flags->leafTests.number_of_preferred_methods = number_leaf_pref_methods;
    flags->leafTests.preferred_methods =
        PORT_NewArray(CERTRevocationMethodIndex, number_leaf_pref_methods);

    flags->chainTests.number_of_defined_methods = number_chain_methods;
    flags->chainTests.cert_rev_flags_per_method =
        PORT_NewArray(PRUint64, number_chain_methods);

    flags->chainTests.number_of_preferred_methods = number_chain_pref_methods;
    flags->chainTests.preferred_methods =
        PORT_NewArray(CERTRevocationMethodIndex, number_chain_pref_methods);

    if (!flags->leafTests.cert_rev_flags_per_method ||
        !flags->leafTests.preferred_methods ||
        !flags->chainTests.cert_rev_flags_per_method ||
        !flags->chainTests.preferred_methods) {
        CERT_DestroyCERTRevocationFlags(flags);
        return (NULL);
    }

    return flags;
}

void
CERT_DestroyCERTRevocationFlags(CERTRevocationFlags *flags)
{
    if (!flags)
        return;

    if (flags->leafTests.cert_rev_flags_per_method)
        PORT_Free(flags->leafTests.cert_rev_flags_per_method);

    if (flags->leafTests.preferred_methods)
        PORT_Free(flags->leafTests.preferred_methods);

    if (flags->chainTests.cert_rev_flags_per_method)
        PORT_Free(flags->chainTests.cert_rev_flags_per_method);

    if (flags->chainTests.preferred_methods)
        PORT_Free(flags->chainTests.preferred_methods);

    PORT_Free(flags);
}

/*
 * CERT_PKIXVerifyCert
 *
 * Verify a Certificate using the PKIX library.
 *
 * Parameters:
 *  cert    - the target certificate to verify. Must be non-null
 *  params  - an array of type/value parameters which can be
 *            used to modify the behavior of the validation
 *            algorithm, or supply additional constraints.
 *
 *  outputTrustAnchor - the trust anchor which the certificate
 *                      chains to. The caller is responsible
 *                      for freeing this.
 *
 * Example Usage:
 *    CERTValParam args[3];
 *    args[0].type = cvpt_policyOID;
 *    args[0].value.si = oid;
 *    args[1].type = revCheckRequired;
 *    args[1].value.b = PR_TRUE;
 *    args[2].type = cvpt_end;
 *
 *    CERT_PKIXVerifyCert(cert, &output, args
 */
SECStatus
CERT_PKIXVerifyCert(
    CERTCertificate *cert,
    SECCertificateUsage usages,
    CERTValInParam *paramsIn,
    CERTValOutParam *paramsOut,
    void *wincx)
{
    SECStatus r = SECFailure;
    PKIX_Error *error = NULL;
    PKIX_ProcessingParams *procParams = NULL;
    PKIX_BuildResult *buildResult = NULL;
    void *nbioContext = NULL; /* for non-blocking IO */
    void *buildState = NULL;  /* for non-blocking IO */
    PKIX_CertSelector *certSelector = NULL;
    PKIX_List *certStores = NULL;
    PKIX_ValidateResult *valResult = NULL;
    PKIX_VerifyNode *verifyNode = NULL;
    PKIX_TrustAnchor *trustAnchor = NULL;
    PKIX_PL_Cert *trustAnchorCert = NULL;
    PKIX_List *builtCertList = NULL;
    CERTValOutParam *oparam = NULL;
    int i = 0;

    void *plContext = NULL;

#ifdef PKIX_OBJECT_LEAK_TEST
    int leakedObjNum = 0;
    int memLeakLoopCount = 0;
    int objCountTable[PKIX_NUMTYPES];
    int fnInvLocalCount = 0;
    PKIX_Boolean savedUsePkixEngFlag = usePKIXValidationEngine;

    if (usePKIXValidationEngine) {
        /* current memory leak testing implementation does not allow
         * to run simultaneous tests one the same or a different threads.
         * Setting the variable to false, to make additional chain
         * validations be handled by old nss. */
        usePKIXValidationEngine = PR_FALSE;
    }
    testStartFnStackPosition = 1;
    fnStackNameArr[0] = "CERT_PKIXVerifyCert";
    fnStackInvCountArr[0] = 0;
    PKIX_Boolean abortOnLeak =
        (PR_GetEnvSecure("PKIX_OBJECT_LEAK_TEST_ABORT_ON_LEAK") == NULL) ? PKIX_FALSE
                                                                         : PKIX_TRUE;
    runningLeakTest = PKIX_TRUE;

    /* Prevent multi-threaded run of object leak test */
    fnInvLocalCount = PR_ATOMIC_INCREMENT(&parallelFnInvocationCount);
    PORT_Assert(fnInvLocalCount == 1);

    do {
        r = SECFailure;
        error = NULL;
        procParams = NULL;
        buildResult = NULL;
        nbioContext = NULL; /* for non-blocking IO */
        buildState = NULL;  /* for non-blocking IO */
        certSelector = NULL;
        certStores = NULL;
        valResult = NULL;
        verifyNode = NULL;
        trustAnchor = NULL;
        trustAnchorCert = NULL;
        builtCertList = NULL;
        oparam = NULL;
        i = 0;
        errorGenerated = PKIX_FALSE;
        stackPosition = 0;

        if (leakedObjNum) {
            pkix_pl_lifecycle_ObjectTableUpdate(objCountTable);
        }
        memLeakLoopCount += 1;
#endif /* PKIX_OBJECT_LEAK_TEST */

        error = PKIX_PL_NssContext_Create(
            0, PR_FALSE /*use arena*/, wincx, &plContext);
        if (error != NULL) { /* need pkix->nss error map */
            PORT_SetError(SEC_ERROR_CERT_NOT_VALID);
            goto cleanup;
        }

        error = pkix_pl_NssContext_SetCertUsage(usages, plContext);
        if (error != NULL) {
            PORT_SetError(SEC_ERROR_INVALID_ARGS);
            goto cleanup;
        }

        error = PKIX_ProcessingParams_Create(&procParams, plContext);
        if (error != NULL) { /* need pkix->nss error map */
            PORT_SetError(SEC_ERROR_CERT_NOT_VALID);
            goto cleanup;
        }

        /* local cert store should be set into procParams before
         * filling in revocation settings. */
        certStores = cert_GetCertStores(plContext);
        if (certStores == NULL) {
            goto cleanup;
        }
        error = PKIX_ProcessingParams_SetCertStores(procParams, certStores, plContext);
        if (error != NULL) {
            goto cleanup;
        }

        /* now process the extensible input parameters structure */
        if (paramsIn != NULL) {
            i = 0;
            while (paramsIn[i].type != cert_pi_end) {
                if (paramsIn[i].type >= cert_pi_max) {
                    PORT_SetError(SEC_ERROR_INVALID_ARGS);
                    goto cleanup;
                }
                if (cert_pkixSetParam(procParams,
                                      &paramsIn[i], plContext) !=
                    SECSuccess) {
                    PORT_SetError(SEC_ERROR_INVALID_ARGS);
                    goto cleanup;
                }
                i++;
            }
        }

        certSelector = cert_GetTargetCertConstraints(cert, plContext);
        if (certSelector == NULL) {
            goto cleanup;
        }
        error = PKIX_ProcessingParams_SetTargetCertConstraints(procParams, certSelector, plContext);
        if (error != NULL) {
            goto cleanup;
        }

        error = PKIX_BuildChain(procParams, &nbioContext,
                                &buildState, &buildResult, &verifyNode,
                                plContext);
        if (error != NULL) {
            goto cleanup;
        }

        error = PKIX_BuildResult_GetValidateResult(buildResult, &valResult,
                                                   plContext);
        if (error != NULL) {
            goto cleanup;
        }

        error = PKIX_ValidateResult_GetTrustAnchor(valResult, &trustAnchor,
                                                   plContext);
        if (error != NULL) {
            goto cleanup;
        }

        if (trustAnchor != NULL) {
            error = PKIX_TrustAnchor_GetTrustedCert(trustAnchor, &trustAnchorCert,
                                                    plContext);
            if (error != NULL) {
                goto cleanup;
            }
        }

#ifdef PKIX_OBJECT_LEAK_TEST
        /* Can not continue if error was generated but not returned.
         * Jumping to cleanup. */
        if (errorGenerated)
            goto cleanup;
#endif /* PKIX_OBJECT_LEAK_TEST */

        oparam = cert_pkix_FindOutputParam(paramsOut, cert_po_trustAnchor);
        if (oparam != NULL) {
            if (trustAnchorCert != NULL) {
                oparam->value.pointer.cert =
                    cert_NSSCertFromPKIXCert(trustAnchorCert);
            } else {
                oparam->value.pointer.cert = NULL;
            }
        }

        error = PKIX_BuildResult_GetCertChain(buildResult, &builtCertList,
                                              plContext);
        if (error != NULL) {
            goto cleanup;
        }

        oparam = cert_pkix_FindOutputParam(paramsOut, cert_po_certList);
        if (oparam != NULL) {
            error = cert_PkixToNssCertsChain(builtCertList,
                                             &oparam->value.pointer.chain,
                                             plContext);
            if (error)
                goto cleanup;
        }

        r = SECSuccess;

    cleanup:
        if (verifyNode) {
            /* Return validation log only upon error. */
            oparam = cert_pkix_FindOutputParam(paramsOut, cert_po_errorLog);
#ifdef PKIX_OBJECT_LEAK_TEST
            if (!errorGenerated)
#endif /* PKIX_OBJECT_LEAK_TEST */
                if (r && oparam != NULL) {
                    PKIX_Error *tmpError =
                        cert_GetLogFromVerifyNode(oparam->value.pointer.log,
                                                  verifyNode, plContext);
                    if (tmpError) {
                        PKIX_PL_Object_DecRef((PKIX_PL_Object *)tmpError, plContext);
                    }
                }
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)verifyNode, plContext);
        }

        if (procParams != NULL)
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)procParams, plContext);

        if (trustAnchorCert != NULL)
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchorCert, plContext);

        if (trustAnchor != NULL)
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)trustAnchor, plContext);

        if (valResult != NULL)
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)valResult, plContext);

        if (buildResult != NULL)
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)buildResult, plContext);

        if (certStores != NULL)
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)certStores, plContext);

        if (certSelector != NULL)
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)certSelector, plContext);

        if (builtCertList != NULL)
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)builtCertList, plContext);

        if (error != NULL) {
            SECErrorCodes nssErrorCode = 0;

            cert_PkixErrorToNssCode(error, &nssErrorCode, plContext);
            cert_pkixDestroyValOutParam(paramsOut);
            PORT_SetError(nssErrorCode);
            PKIX_PL_Object_DecRef((PKIX_PL_Object *)error, plContext);
        }

        PKIX_PL_NssContext_Destroy(plContext);

#ifdef PKIX_OBJECT_LEAK_TEST
        leakedObjNum =
            pkix_pl_lifecycle_ObjectLeakCheck(leakedObjNum ? objCountTable : NULL);

        if (pkixLog && leakedObjNum) {
            PR_LOG(pkixLog, 1, ("The generated error caused an object leaks. Loop %d."
                                "Stack %s\n",
                                memLeakLoopCount, errorFnStackString));
        }
        PR_Free(errorFnStackString);
        errorFnStackString = NULL;
        if (abortOnLeak) {
            PORT_Assert(leakedObjNum == 0);
        }

    } while (errorGenerated);

    runningLeakTest = PKIX_FALSE;
    PR_ATOMIC_DECREMENT(&parallelFnInvocationCount);
    usePKIXValidationEngine = savedUsePkixEngFlag;
#endif /* PKIX_OBJECT_LEAK_TEST */

    return r;
}
