| /* 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_pl_pk11certstore.c |
| * |
| * PKCS11CertStore Function Definitions |
| * |
| */ |
| |
| #include "pkix_pl_pk11certstore.h" |
| |
| /* |
| * PKIX_DEFAULT_MAX_RESPONSE_LENGTH (64 * 1024) is too small for downloading |
| * CRLs. We observed CRLs of sizes 338759 and 439035 in practice. So we |
| * need to use a higher max response length for CRLs. |
| */ |
| #define PKIX_DEFAULT_MAX_CRL_RESPONSE_LENGTH (512 * 1024) |
| |
| /* --Private-Pk11CertStore-Functions---------------------------------- */ |
| |
| /* |
| * FUNCTION: pkix_pl_Pk11CertStore_CheckTrust |
| * DESCRIPTION: |
| * This function checks the trust status of this "cert" that was retrieved |
| * from the CertStore "store" and returns its trust status at "pTrusted". |
| * |
| * PARAMETERS: |
| * "store" |
| * Address of the CertStore. Must be non-NULL. |
| * "cert" |
| * Address of the Cert. Must be non-NULL. |
| * "pTrusted" |
| * Address of PKIX_Boolean where the "cert" trust status is returned. |
| * Must be non-NULL. |
| * "plContext" |
| * Platform-specific context pointer |
| * THREAD SAFETY: |
| * Thread Safe (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds. |
| * Returns a CertStore 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_pl_Pk11CertStore_CheckTrust( |
| PKIX_CertStore *store, |
| PKIX_PL_Cert *cert, |
| PKIX_Boolean *pTrusted, |
| void *plContext) |
| { |
| SECStatus rv = SECFailure; |
| PKIX_Boolean trusted = PKIX_FALSE; |
| SECCertUsage certUsage = 0; |
| SECCertificateUsage certificateUsage; |
| unsigned int requiredFlags; |
| SECTrustType trustType; |
| CERTCertTrust trust; |
| |
| PKIX_ENTER(CERTSTORE, "pkix_pl_Pk11CertStore_CheckTrust"); |
| PKIX_NULLCHECK_THREE(store, cert, pTrusted); |
| PKIX_NULLCHECK_ONE(cert->nssCert); |
| |
| certificateUsage = ((PKIX_PL_NssContext*)plContext)->certificateUsage; |
| |
| /* ensure we obtained a single usage bit only */ |
| PORT_Assert(!(certificateUsage & (certificateUsage - 1))); |
| |
| /* convert SECertificateUsage (bit mask) to SECCertUsage (enum) */ |
| while (0 != (certificateUsage = certificateUsage >> 1)) { certUsage++; } |
| |
| rv = CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags, &trustType); |
| if (rv == SECSuccess) { |
| rv = CERT_GetCertTrust(cert->nssCert, &trust); |
| } |
| |
| if (rv == SECSuccess) { |
| unsigned int certFlags; |
| |
| if (certUsage != certUsageAnyCA && |
| certUsage != certUsageStatusResponder) { |
| CERTCertificate *nssCert = cert->nssCert; |
| |
| if (certUsage == certUsageVerifyCA) { |
| if (nssCert->nsCertType & NS_CERT_TYPE_EMAIL_CA) { |
| trustType = trustEmail; |
| } else if (nssCert->nsCertType & NS_CERT_TYPE_SSL_CA) { |
| trustType = trustSSL; |
| } else { |
| trustType = trustObjectSigning; |
| } |
| } |
| |
| certFlags = SEC_GET_TRUST_FLAGS((&trust), trustType); |
| if ((certFlags & requiredFlags) == requiredFlags) { |
| trusted = PKIX_TRUE; |
| } |
| } else { |
| for (trustType = trustSSL; trustType < trustTypeNone; |
| trustType++) { |
| certFlags = |
| SEC_GET_TRUST_FLAGS((&trust), trustType); |
| if ((certFlags & requiredFlags) == requiredFlags) { |
| trusted = PKIX_TRUE; |
| break; |
| } |
| } |
| } |
| } |
| |
| *pTrusted = trusted; |
| |
| PKIX_RETURN(CERTSTORE); |
| } |
| |
| /* |
| * FUNCTION: pkix_pl_Pk11CertStore_CertQuery |
| * DESCRIPTION: |
| * |
| * This function obtains from the database the Certs specified by the |
| * ComCertSelParams pointed to by "params" and stores the resulting |
| * List at "pSelected". If no matching Certs are found, a NULL pointer |
| * will be stored. |
| * |
| * This function uses a "smart" database query if the Subject has been set |
| * in ComCertSelParams. Otherwise, it uses a very inefficient call to |
| * retrieve all Certs in the database (and run them through the selector). |
| * |
| * PARAMETERS: |
| * "params" |
| * Address of the ComCertSelParams. Must be non-NULL. |
| * "pSelected" |
| * Address at which List 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 CertStore 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_pl_Pk11CertStore_CertQuery( |
| PKIX_ComCertSelParams *params, |
| PKIX_List **pSelected, |
| void *plContext) |
| { |
| PRBool validOnly = PR_FALSE; |
| PRTime prtime = 0; |
| PKIX_PL_X500Name *subjectName = NULL; |
| PKIX_PL_Date *certValid = NULL; |
| PKIX_List *certList = NULL; |
| PKIX_PL_Cert *cert = NULL; |
| CERTCertList *pk11CertList = NULL; |
| CERTCertListNode *node = NULL; |
| CERTCertificate *nssCert = NULL; |
| CERTCertDBHandle *dbHandle = NULL; |
| |
| PLArenaPool *arena = NULL; |
| SECItem *nameItem = NULL; |
| void *wincx = NULL; |
| |
| PKIX_ENTER(CERTSTORE, "pkix_pl_Pk11CertStore_CertQuery"); |
| PKIX_NULLCHECK_TWO(params, pSelected); |
| |
| /* avoid multiple calls to retrieve a constant */ |
| PKIX_PL_NSSCALLRV(CERTSTORE, dbHandle, CERT_GetDefaultCertDB, ()); |
| |
| /* |
| * Any of the ComCertSelParams may be obtained and used to constrain |
| * the database query, to allow the use of a "smart" query. See |
| * pkix_certsel.h for a list of the PKIX_ComCertSelParams_Get* |
| * calls available. No corresponding "smart" queries exist at present, |
| * except for CERT_CreateSubjectCertList based on Subject. When others |
| * are added, corresponding code should be added to |
| * pkix_pl_Pk11CertStore_CertQuery to use them when appropriate |
| * selector parameters have been set. |
| */ |
| |
| PKIX_CHECK(PKIX_ComCertSelParams_GetSubject |
| (params, &subjectName, plContext), |
| PKIX_COMCERTSELPARAMSGETSUBJECTFAILED); |
| |
| PKIX_CHECK(PKIX_ComCertSelParams_GetCertificateValid |
| (params, &certValid, plContext), |
| PKIX_COMCERTSELPARAMSGETCERTIFICATEVALIDFAILED); |
| |
| /* If caller specified a Date, convert it to PRTime */ |
| if (certValid) { |
| PKIX_CHECK(pkix_pl_Date_GetPRTime |
| (certValid, &prtime, plContext), |
| PKIX_DATEGETPRTIMEFAILED); |
| validOnly = PR_TRUE; |
| } |
| |
| /* |
| * If we have the subject name for the desired subject, |
| * ask the database for Certs with that subject. Otherwise |
| * ask the database for all Certs. |
| */ |
| if (subjectName) { |
| arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| if (arena) { |
| |
| PKIX_CHECK(pkix_pl_X500Name_GetDERName |
| (subjectName, arena, &nameItem, plContext), |
| PKIX_X500NAMEGETSECNAMEFAILED); |
| |
| if (nameItem) { |
| |
| PKIX_PL_NSSCALLRV |
| (CERTSTORE, |
| pk11CertList, |
| CERT_CreateSubjectCertList, |
| (NULL, dbHandle, nameItem, prtime, validOnly)); |
| } |
| PKIX_PL_NSSCALL |
| (CERTSTORE, PORT_FreeArena, (arena, PR_FALSE)); |
| arena = NULL; |
| } |
| |
| } else { |
| |
| PKIX_CHECK(pkix_pl_NssContext_GetWincx |
| ((PKIX_PL_NssContext *)plContext, &wincx), |
| PKIX_NSSCONTEXTGETWINCXFAILED); |
| |
| PKIX_PL_NSSCALLRV |
| (CERTSTORE, |
| pk11CertList, |
| PK11_ListCerts, |
| (PK11CertListAll, wincx)); |
| } |
| |
| if (pk11CertList) { |
| |
| PKIX_CHECK(PKIX_List_Create(&certList, plContext), |
| PKIX_LISTCREATEFAILED); |
| |
| for (node = CERT_LIST_HEAD(pk11CertList); |
| !(CERT_LIST_END(node, pk11CertList)); |
| node = CERT_LIST_NEXT(node)) { |
| |
| PKIX_PL_NSSCALLRV |
| (CERTSTORE, |
| nssCert, |
| CERT_DupCertificate, |
| (node->cert)); |
| |
| if (!nssCert) { |
| continue; /* just skip bad certs */ |
| } |
| |
| PKIX_CHECK_ONLY_FATAL(pkix_pl_Cert_CreateWithNSSCert |
| (nssCert, &cert, plContext), |
| PKIX_CERTCREATEWITHNSSCERTFAILED); |
| |
| if (PKIX_ERROR_RECEIVED) { |
| CERT_DestroyCertificate(nssCert); |
| nssCert = NULL; |
| continue; /* just skip bad certs */ |
| } |
| |
| PKIX_CHECK_ONLY_FATAL(PKIX_List_AppendItem |
| (certList, (PKIX_PL_Object *)cert, plContext), |
| PKIX_LISTAPPENDITEMFAILED); |
| |
| PKIX_DECREF(cert); |
| |
| } |
| |
| /* Don't throw away the list if one cert was bad! */ |
| pkixTempErrorReceived = PKIX_FALSE; |
| } |
| |
| *pSelected = certList; |
| certList = NULL; |
| |
| cleanup: |
| |
| if (pk11CertList) { |
| CERT_DestroyCertList(pk11CertList); |
| } |
| if (arena) { |
| PORT_FreeArena(arena, PR_FALSE); |
| } |
| |
| PKIX_DECREF(subjectName); |
| PKIX_DECREF(certValid); |
| PKIX_DECREF(cert); |
| PKIX_DECREF(certList); |
| |
| PKIX_RETURN(CERTSTORE); |
| } |
| |
| /* |
| * FUNCTION: pkix_pl_Pk11CertStore_ImportCrl |
| * DESCRIPTION: |
| * |
| * PARAMETERS: |
| * "params" |
| * Address of the ComCRLSelParams. Must be non-NULL. |
| * "pSelected" |
| * Address at which List 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 CertStore 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_pl_Pk11CertStore_ImportCrl( |
| PKIX_CertStore *store, |
| PKIX_PL_X500Name *issuerName, |
| PKIX_List *crlList, |
| void *plContext) |
| { |
| CERTCertDBHandle *certHandle = CERT_GetDefaultCertDB(); |
| PKIX_PL_CRL *crl = NULL; |
| SECItem *derCrl = NULL; |
| |
| PKIX_ENTER(CERTSTORE, "pkix_pl_Pk11CertStore_ImportCrl"); |
| PKIX_NULLCHECK_TWO(store, plContext); |
| |
| if (!crlList) { |
| goto cleanup; |
| } |
| while (crlList->length > 0) { |
| PKIX_CHECK( |
| PKIX_List_GetItem(crlList, 0, (PKIX_PL_Object**)&crl, |
| plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| /* Delete crl from the list to keep controll of the |
| * last reference. crl need to be destroyed right after |
| * it released the ownership of the crl der. */ |
| PKIX_CHECK( |
| PKIX_List_DeleteItem(crlList, 0, plContext), |
| PKIX_LISTDELETEITEMFAILED); |
| |
| /* acquire the crlder ownership */ |
| pkixErrorResult = |
| PKIX_PL_CRL_ReleaseDerCrl(crl, &derCrl, plContext); |
| PORT_Assert(!pkixErrorResult && derCrl); |
| if (pkixErrorResult || !derCrl) { |
| /* All pkix delivered crls should be able to |
| * release their ders. */ |
| PKIX_DECREF(pkixErrorResult); |
| PKIX_DECREF(crl); |
| continue; |
| } |
| cert_CacheCRLByGeneralName(certHandle, derCrl, |
| crl->derGenName); |
| /* Do not check the status. If it is a SECFailure, |
| * derCrl is already destroyed. */ |
| derCrl = NULL; |
| PKIX_DECREF(crl); |
| } |
| |
| cleanup: |
| PKIX_DECREF(crl); |
| |
| PKIX_RETURN(CERTSTORE); |
| } |
| |
| static PKIX_Error * |
| NameCacheHasFetchedCrlInfo(PKIX_PL_Cert *pkixCert, |
| PRTime time, |
| PKIX_Boolean *pHasFetchedCrlInCache, |
| void *plContext) |
| { |
| /* Returning true result in this case will mean, that case info |
| * is currect and should used as is. */ |
| NamedCRLCache* nameCrlCache = NULL; |
| PKIX_Boolean hasFetchedCrlInCache = PKIX_TRUE; |
| PKIX_List *dpList = NULL; |
| pkix_pl_CrlDp *dp = NULL; |
| PKIX_UInt32 dpIndex = 0; |
| SECStatus rv = SECSuccess; |
| PRTime reloadDelay = 0, badCrlInvalDelay = 0; |
| |
| PKIX_ENTER(CERTSTORE, "ChechCacheHasFetchedCrl"); |
| |
| reloadDelay = |
| ((PKIX_PL_NssContext*)plContext)->crlReloadDelay * |
| PR_USEC_PER_SEC; |
| badCrlInvalDelay = |
| ((PKIX_PL_NssContext*)plContext)->badDerCrlReloadDelay * |
| PR_USEC_PER_SEC; |
| if (!time) { |
| time = PR_Now(); |
| } |
| /* If we already download the crl and inserted into the cache, then |
| * there is no need to check for fetched crl. We have what we have. */ |
| PKIX_CHECK( |
| PKIX_PL_Cert_GetCrlDp(pkixCert, &dpList, plContext), |
| PKIX_CERTGETCRLDPFAILED); |
| if (dpList && dpList->length) { |
| hasFetchedCrlInCache = PKIX_FALSE; |
| rv = cert_AcquireNamedCRLCache(&nameCrlCache); |
| if (rv != SECSuccess) { |
| PKIX_DECREF(dpList); |
| } |
| } else { |
| /* If no dp then treat it as if we already have |
| * a fetched crl. */ |
| PKIX_DECREF(dpList); |
| } |
| for (;!hasFetchedCrlInCache && |
| dpList && dpIndex < dpList->length;dpIndex++) { |
| SECItem **derDpNames = NULL; |
| pkixErrorResult = |
| PKIX_List_GetItem(dpList, dpIndex, (PKIX_PL_Object **)&dp, |
| plContext); |
| if (pkixErrorResult) { |
| PKIX_DECREF(pkixErrorResult); |
| continue; |
| } |
| if (dp->nssdp->distPointType == generalName) { |
| /* dp can only be created from nssdp. */ |
| derDpNames = dp->nssdp->derFullName; |
| } |
| while (derDpNames && *derDpNames != NULL) { |
| NamedCRLCacheEntry* cacheEntry = NULL; |
| const SECItem *derDpName = *derDpNames++; |
| rv = cert_FindCRLByGeneralName(nameCrlCache, derDpName, |
| &cacheEntry); |
| if (rv == SECSuccess && cacheEntry) { |
| if ((cacheEntry->inCRLCache && |
| (cacheEntry->successfulInsertionTime + reloadDelay > time || |
| (cacheEntry->dupe && |
| cacheEntry->lastAttemptTime + reloadDelay > time))) || |
| (cacheEntry->badDER && |
| cacheEntry->lastAttemptTime + badCrlInvalDelay > time)) { |
| hasFetchedCrlInCache = PKIX_TRUE; |
| break; |
| } |
| } |
| } |
| PKIX_DECREF(dp); |
| } |
| cleanup: |
| *pHasFetchedCrlInCache = hasFetchedCrlInCache; |
| if (nameCrlCache) { |
| cert_ReleaseNamedCRLCache(nameCrlCache); |
| } |
| PKIX_DECREF(dpList); |
| |
| PKIX_RETURN(CERTSTORE); |
| } |
| |
| /* |
| * FUNCTION: pkix_pl_Pk11CertStore_CheckCrl |
| * DESCRIPTION: |
| * |
| * PARAMETERS: |
| * "params" |
| * Address of the ComCRLSelParams. Must be non-NULL. |
| * "pSelected" |
| * Address at which List 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 CertStore 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_pl_Pk11CertStore_CheckRevByCrl( |
| PKIX_CertStore *store, |
| PKIX_PL_Cert *pkixCert, |
| PKIX_PL_Cert *pkixIssuer, |
| PKIX_PL_Date *date, |
| PKIX_Boolean crlDownloadDone, |
| CERTCRLEntryReasonCode *pReasonCode, |
| PKIX_RevocationStatus *pStatus, |
| void *plContext) |
| { |
| PKIX_RevocationStatus pkixRevStatus = PKIX_RevStatus_NoInfo; |
| CERTRevocationStatus revStatus = certRevocationStatusUnknown; |
| PKIX_Boolean hasFetchedCrlInCache = PKIX_TRUE; |
| CERTCertificate *cert = NULL, *issuer = NULL; |
| SECStatus rv = SECSuccess; |
| void *wincx = NULL; |
| PRTime time = 0; |
| |
| PKIX_ENTER(CERTSTORE, "pkix_pl_Pk11CertStore_CheckRevByCrl"); |
| PKIX_NULLCHECK_FOUR(store, pkixCert, pkixIssuer, plContext); |
| |
| cert = pkixCert->nssCert; |
| issuer = pkixIssuer->nssCert; |
| if (date) { |
| PKIX_CHECK( |
| pkix_pl_Date_GetPRTime(date, &time, plContext), |
| PKIX_DATEGETPRTIMEFAILED); |
| } |
| PKIX_CHECK( |
| pkix_pl_NssContext_GetWincx((PKIX_PL_NssContext*)plContext, |
| &wincx), |
| PKIX_NSSCONTEXTGETWINCXFAILED); |
| /* No need to check any cDPs, since partitioned crls are not |
| * supported. If a ds does not point to partitioned crl, then |
| * the crl should be in issuer cache that is unrelated to any |
| * dp. Using NULL as a dp pointer to check it.*/ |
| rv = cert_CheckCertRevocationStatus(cert, issuer, NULL, |
| /* Will not validate the signature |
| * on the crl if time is not specified.*/ |
| time, wincx, &revStatus, pReasonCode); |
| if (rv == SECFailure) { |
| pkixRevStatus = PKIX_RevStatus_Revoked; |
| goto cleanup; |
| } |
| if (crlDownloadDone) { |
| if (revStatus == certRevocationStatusRevoked) { |
| pkixRevStatus = PKIX_RevStatus_Revoked; |
| } else if (revStatus == certRevocationStatusValid) { |
| pkixRevStatus = PKIX_RevStatus_Success; |
| } |
| } else { |
| pkixErrorResult = |
| NameCacheHasFetchedCrlInfo(pkixCert, time, &hasFetchedCrlInCache, |
| plContext); |
| if (pkixErrorResult) { |
| goto cleanup; |
| } |
| if (revStatus == certRevocationStatusRevoked && |
| (hasFetchedCrlInCache || |
| *pReasonCode != crlEntryReasoncertificatedHold)) { |
| pkixRevStatus = PKIX_RevStatus_Revoked; |
| } else if (revStatus == certRevocationStatusValid && |
| hasFetchedCrlInCache) { |
| pkixRevStatus = PKIX_RevStatus_Success; |
| } |
| } |
| cleanup: |
| *pStatus = pkixRevStatus; |
| |
| PKIX_RETURN(CERTSTORE); |
| } |
| |
| |
| /* |
| * FUNCTION: pkix_pl_Pk11CertStore_GetCert |
| * (see description of PKIX_CertStore_CertCallback in pkix_certstore.h) |
| */ |
| PKIX_Error * |
| pkix_pl_Pk11CertStore_GetCert( |
| PKIX_CertStore *store, |
| PKIX_CertSelector *selector, |
| PKIX_VerifyNode *parentVerifyNode, |
| void **pNBIOContext, |
| PKIX_List **pCertList, |
| void *plContext) |
| { |
| PKIX_UInt32 i = 0; |
| PKIX_UInt32 numFound = 0; |
| PKIX_PL_Cert *candidate = NULL; |
| PKIX_List *selected = NULL; |
| PKIX_List *filtered = NULL; |
| PKIX_CertSelector_MatchCallback selectorCallback = NULL; |
| PKIX_CertStore_CheckTrustCallback trustCallback = NULL; |
| PKIX_ComCertSelParams *params = NULL; |
| PKIX_Boolean cacheFlag = PKIX_FALSE; |
| PKIX_VerifyNode *verifyNode = NULL; |
| PKIX_Error *selectorError = NULL; |
| |
| PKIX_ENTER(CERTSTORE, "pkix_pl_Pk11CertStore_GetCert"); |
| PKIX_NULLCHECK_FOUR(store, selector, pNBIOContext, pCertList); |
| |
| *pNBIOContext = NULL; /* We don't use non-blocking I/O */ |
| |
| PKIX_CHECK(PKIX_CertSelector_GetMatchCallback |
| (selector, &selectorCallback, plContext), |
| PKIX_CERTSELECTORGETMATCHCALLBACKFAILED); |
| |
| PKIX_CHECK(PKIX_CertSelector_GetCommonCertSelectorParams |
| (selector, ¶ms, plContext), |
| PKIX_CERTSELECTORGETCOMCERTSELPARAMSFAILED); |
| |
| PKIX_CHECK(pkix_pl_Pk11CertStore_CertQuery |
| (params, &selected, plContext), |
| PKIX_PK11CERTSTORECERTQUERYFAILED); |
| |
| if (selected) { |
| PKIX_CHECK(PKIX_List_GetLength(selected, &numFound, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| } |
| |
| PKIX_CHECK(PKIX_CertStore_GetCertStoreCacheFlag |
| (store, &cacheFlag, plContext), |
| PKIX_CERTSTOREGETCERTSTORECACHEFLAGFAILED); |
| |
| PKIX_CHECK(PKIX_CertStore_GetTrustCallback |
| (store, &trustCallback, plContext), |
| PKIX_CERTSTOREGETTRUSTCALLBACKFAILED); |
| |
| PKIX_CHECK(PKIX_List_Create(&filtered, plContext), |
| PKIX_LISTCREATEFAILED); |
| |
| for (i = 0; i < numFound; i++) { |
| PKIX_CHECK_ONLY_FATAL(PKIX_List_GetItem |
| (selected, |
| i, |
| (PKIX_PL_Object **)&candidate, |
| plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| if (PKIX_ERROR_RECEIVED) { |
| continue; /* just skip bad certs */ |
| } |
| |
| selectorError = |
| selectorCallback(selector, candidate, plContext); |
| if (!selectorError) { |
| PKIX_CHECK(PKIX_PL_Cert_SetCacheFlag |
| (candidate, cacheFlag, plContext), |
| PKIX_CERTSETCACHEFLAGFAILED); |
| |
| if (trustCallback) { |
| PKIX_CHECK(PKIX_PL_Cert_SetTrustCertStore |
| (candidate, store, plContext), |
| PKIX_CERTSETTRUSTCERTSTOREFAILED); |
| } |
| |
| PKIX_CHECK_ONLY_FATAL(PKIX_List_AppendItem |
| (filtered, |
| (PKIX_PL_Object *)candidate, |
| plContext), |
| PKIX_LISTAPPENDITEMFAILED); |
| } else if (parentVerifyNode) { |
| PKIX_CHECK_FATAL( |
| pkix_VerifyNode_Create(candidate, 0, selectorError, |
| &verifyNode, plContext), |
| PKIX_VERIFYNODECREATEFAILED); |
| PKIX_CHECK_FATAL( |
| pkix_VerifyNode_AddToTree(parentVerifyNode, |
| verifyNode, |
| plContext), |
| PKIX_VERIFYNODEADDTOTREEFAILED); |
| PKIX_DECREF(verifyNode); |
| } |
| PKIX_DECREF(selectorError); |
| PKIX_DECREF(candidate); |
| } |
| |
| /* Don't throw away the list if one cert was bad! */ |
| pkixTempErrorReceived = PKIX_FALSE; |
| |
| *pCertList = filtered; |
| filtered = NULL; |
| |
| cleanup: |
| fatal: |
| PKIX_DECREF(filtered); |
| PKIX_DECREF(candidate); |
| PKIX_DECREF(selected); |
| PKIX_DECREF(params); |
| PKIX_DECREF(verifyNode); |
| PKIX_DECREF(selectorError); |
| |
| PKIX_RETURN(CERTSTORE); |
| } |
| |
| static PKIX_Error * |
| RemovePartitionedDpsFromList(PKIX_List *dpList, PKIX_PL_Date *date, |
| void *plContext) |
| { |
| NamedCRLCache* nameCrlCache = NULL; |
| pkix_pl_CrlDp *dp = NULL; |
| unsigned int dpIndex = 0; |
| PRTime time; |
| PRTime reloadDelay = 0, badCrlInvalDelay = 0; |
| SECStatus rv; |
| |
| PKIX_ENTER(CERTSTORE, "pkix_pl_Pk11CertStore_ListRemovePrtDp"); |
| |
| if (!dpList || !dpList->length) { |
| PKIX_RETURN(CERTSTORE); |
| } |
| reloadDelay = |
| ((PKIX_PL_NssContext*)plContext)->crlReloadDelay * |
| PR_USEC_PER_SEC; |
| badCrlInvalDelay = |
| ((PKIX_PL_NssContext*)plContext)->badDerCrlReloadDelay * |
| PR_USEC_PER_SEC; |
| PKIX_CHECK(pkix_pl_Date_GetPRTime(date, &time, plContext), |
| PKIX_DATEGETPRTIMEFAILED); |
| rv = cert_AcquireNamedCRLCache(&nameCrlCache); |
| if (rv == SECFailure) { |
| /* Baling out. Wont find out any thing useful. */ |
| PKIX_RETURN(CERTSTORE); |
| } |
| while (dpIndex < dpList->length) { |
| SECItem **derDpNames = NULL; |
| PKIX_Boolean removeDp = PKIX_FALSE; |
| |
| PKIX_CHECK( |
| PKIX_List_GetItem(dpList, dpIndex, (PKIX_PL_Object **)&dp, |
| plContext), |
| PKIX_LISTGETITEMFAILED); |
| if (!dp->isPartitionedByReasonCode) { |
| /* See if we know about this dp anything why we should |
| * not use it to download a crl. */ |
| if (dp->nssdp->distPointType == generalName) { |
| /* dp can only be created from nssdp. */ |
| derDpNames = dp->nssdp->derFullName; |
| } else { |
| removeDp = PKIX_TRUE; |
| } |
| while (derDpNames && *derDpNames != NULL) { |
| NamedCRLCacheEntry* cacheEntry = NULL; |
| const SECItem *derDpName = *derDpNames++; |
| /* Removing from the list all dps that we know about. */ |
| rv = cert_FindCRLByGeneralName(nameCrlCache, derDpName, |
| &cacheEntry); |
| if (rv && cacheEntry) { |
| if (cacheEntry->unsupported || |
| (cacheEntry->inCRLCache && |
| (cacheEntry->successfulInsertionTime + reloadDelay > time || |
| (cacheEntry->dupe && |
| cacheEntry->lastAttemptTime + reloadDelay > time))) || |
| (cacheEntry->badDER && |
| cacheEntry->lastAttemptTime + badCrlInvalDelay > time)) { |
| removeDp = PKIX_TRUE; |
| } |
| } |
| } |
| } else { |
| /* Remove dp that point to a partitioned crl . RFC 5280 |
| * recommends against crl partitioned by reason code. |
| * Will skip such crls */ |
| removeDp = PKIX_TRUE; |
| } |
| if (removeDp) { |
| PKIX_CHECK_ONLY_FATAL( |
| pkix_List_Remove(dpList,(PKIX_PL_Object*)dp, |
| plContext), |
| PKIX_LISTGETITEMFAILED); |
| } else { |
| dpIndex += 1; |
| } |
| PKIX_DECREF(dp); |
| } |
| |
| cleanup: |
| if (nameCrlCache) { |
| cert_ReleaseNamedCRLCache(nameCrlCache); |
| } |
| PKIX_DECREF(dp); |
| |
| PKIX_RETURN(CERTSTORE); |
| } |
| |
| /* |
| * FUNCTION: pkix_pl_Pk11CertStore_DownloadCrl |
| */ |
| static PKIX_Error * |
| DownloadCrl(pkix_pl_CrlDp *dp, PKIX_PL_CRL **crl, |
| const SEC_HttpClientFcnV1 *hcv1, void *plContext) |
| { |
| char *location = NULL; |
| char *hostname = NULL; |
| char *path = NULL; |
| PRUint16 port; |
| SEC_HTTP_SERVER_SESSION pServerSession = NULL; |
| SEC_HTTP_REQUEST_SESSION pRequestSession = NULL; |
| PRUint16 myHttpResponseCode; |
| const char *myHttpResponseData = NULL; |
| PRUint32 myHttpResponseDataLen; |
| SECItem *uri = NULL; |
| SECItem *derCrlCopy = NULL; |
| CERTSignedCrl *nssCrl = NULL; |
| CERTGeneralName *genName = NULL; |
| SECItem **derGenNames = NULL; |
| SECItem *derGenName = NULL; |
| |
| PKIX_ENTER(CERTSTORE, "pkix_pl_Pk11CertStore_DownloadCrl"); |
| |
| /* Do not support dps others than a one with GeneralName |
| * name type. */ |
| if (dp->distPointType != generalName || |
| !dp->nssdp->derFullName) { |
| PKIX_ERROR(PKIX_UNSUPPORTEDCRLDPTYPE); |
| } |
| genName = dp->name.fullName; |
| derGenNames = dp->nssdp->derFullName; |
| do { |
| derGenName = *derGenNames; |
| do { |
| if (!derGenName || |
| !genName->name.other.data) { |
| /* get to next name if no data. */ |
| break; |
| } |
| uri = &genName->name.other; |
| location = (char*)PR_Malloc(1 + uri->len); |
| if (!location) { |
| break; |
| } |
| PORT_Memcpy(location, uri->data, uri->len); |
| location[uri->len] = 0; |
| if (CERT_ParseURL(location, &hostname, |
| &port, &path) != SECSuccess) { |
| PORT_SetError(SEC_ERROR_BAD_CRL_DP_URL); |
| break; |
| } |
| |
| PORT_Assert(hostname != NULL); |
| PORT_Assert(path != NULL); |
| |
| if ((*hcv1->createSessionFcn)(hostname, port, |
| &pServerSession) != SECSuccess) { |
| PORT_SetError(SEC_ERROR_BAD_CRL_DP_URL); |
| break; |
| } |
| |
| if ((*hcv1->createFcn)(pServerSession, "http", path, "GET", |
| /* Users with slow connections might not get CRL revocation |
| checking for certs that use big CRLs because of the timeout |
| We absolutely need code that limits our retry attempts. |
| */ |
| PR_SecondsToInterval( |
| ((PKIX_PL_NssContext*)plContext)->timeoutSeconds), |
| &pRequestSession) != SECSuccess) { |
| break; |
| } |
| |
| myHttpResponseDataLen = |
| ((PKIX_PL_NssContext*)plContext)->maxResponseLength; |
| if (myHttpResponseDataLen < PKIX_DEFAULT_MAX_CRL_RESPONSE_LENGTH) |
| myHttpResponseDataLen = PKIX_DEFAULT_MAX_CRL_RESPONSE_LENGTH; |
| |
| /* We use a non-zero timeout, which means: |
| - the client will use blocking I/O |
| - TryFcn will not return WOULD_BLOCK nor a poll descriptor |
| - it's sufficient to call TryFcn once |
| */ |
| /* we don't want result objects larger than this: */ |
| if ((*hcv1->trySendAndReceiveFcn)( |
| pRequestSession, |
| NULL, |
| &myHttpResponseCode, |
| NULL, |
| NULL, |
| &myHttpResponseData, |
| &myHttpResponseDataLen) != SECSuccess) { |
| break; |
| } |
| |
| if (myHttpResponseCode != 200) { |
| break; |
| } |
| } while(0); |
| if (!myHttpResponseData) { |
| /* Going to the next one. */ |
| genName = CERT_GetNextGeneralName(genName); |
| derGenNames++; |
| } |
| /* Staing in the loop through all the names until |
| * we have a successful download. */ |
| } while (!myHttpResponseData && *derGenNames && |
| genName != dp->name.fullName); |
| /* Need this name to track the crl source location. */ |
| PORT_Assert(derGenName); |
| |
| if (!myHttpResponseData) { |
| /* Generating fake bad CRL to keep track of this dp */ |
| SECItem derCrl = {siBuffer, (void*)"BadCrl", 6 }; |
| |
| derCrlCopy = SECITEM_DupItem(&derCrl); |
| if (!derCrlCopy) { |
| PKIX_ERROR(PKIX_ALLOCERROR); |
| } |
| derGenName = *dp->nssdp->derFullName; |
| } else { |
| SECItem derCrl = { siBuffer, |
| (void*)myHttpResponseData, |
| myHttpResponseDataLen }; |
| derCrlCopy = SECITEM_DupItem(&derCrl); |
| if (!derCrlCopy) { |
| PKIX_ERROR(PKIX_ALLOCERROR); |
| } |
| /* crl will be based on derCrlCopy, but will not own the der. */ |
| nssCrl = |
| CERT_DecodeDERCrlWithFlags(NULL, derCrlCopy, SEC_CRL_TYPE, |
| CRL_DECODE_DONT_COPY_DER | |
| CRL_DECODE_SKIP_ENTRIES); |
| } |
| /* pkix crl owns the der. */ |
| PKIX_CHECK( |
| pkix_pl_CRL_CreateWithSignedCRL(nssCrl, derCrlCopy, |
| derGenName, |
| crl, plContext), |
| PKIX_CRLCREATEWITHSIGNEDCRLFAILED); |
| /* pkix crl now own both objects. */ |
| derCrlCopy = NULL; |
| nssCrl = NULL; |
| |
| cleanup: |
| if (derCrlCopy) |
| PORT_Free(derCrlCopy); |
| if (nssCrl) |
| SEC_DestroyCrl(nssCrl); |
| if (pRequestSession != NULL) |
| (*hcv1->freeFcn)(pRequestSession); |
| if (pServerSession != NULL) |
| (*hcv1->freeSessionFcn)(pServerSession); |
| if (path != NULL) |
| PORT_Free(path); |
| if (hostname != NULL) |
| PORT_Free(hostname); |
| if (location) { |
| PORT_Free(location); |
| } |
| |
| PKIX_RETURN(CERTSTORE); |
| } |
| |
| /* |
| * FUNCTION: pkix_pl_Pk11CertStore_GetCRL |
| * (see description of PKIX_CertStore_CRLCallback in pkix_certstore.h) |
| */ |
| static PKIX_Error * |
| pkix_pl_Pk11CertStore_GetCRL( |
| PKIX_CertStore *store, |
| PKIX_CRLSelector *selector, |
| void **pNBIOContext, |
| PKIX_List **pCrlList, |
| void *plContext) |
| { |
| PKIX_UInt32 dpIndex = 0; |
| PKIX_PL_CRL *crl = NULL; |
| PKIX_List *crlList = NULL; |
| PKIX_List *dpList = NULL; |
| pkix_pl_CrlDp *dp = NULL; |
| PKIX_PL_Date *date = NULL; |
| const SEC_HttpClientFcn *registeredHttpClient = NULL; |
| |
| PKIX_ENTER(CERTSTORE, "pkix_pl_Pk11CertStore_GetCRL"); |
| PKIX_NULLCHECK_THREE(store, pNBIOContext, pCrlList); |
| PKIX_NULLCHECK_TWO(selector, selector->params); |
| |
| registeredHttpClient = SEC_GetRegisteredHttpClient(); |
| if (!registeredHttpClient || registeredHttpClient->version != 1) { |
| goto cleanup; |
| } |
| dpList = selector->params->crldpList; |
| date = selector->params->date; |
| PKIX_CHECK( |
| RemovePartitionedDpsFromList(dpList, date, |
| plContext), |
| PKIX_FAILTOREMOVEDPFROMLIST); |
| for (;dpIndex < dpList->length;dpIndex++) { |
| PKIX_DECREF(dp); |
| pkixErrorResult = |
| PKIX_List_GetItem(dpList, dpIndex, |
| (PKIX_PL_Object **)&dp, |
| plContext); |
| if (pkixErrorResult) { |
| PKIX_DECREF(pkixErrorResult); |
| continue; |
| } |
| pkixErrorResult = |
| DownloadCrl(dp, &crl, |
| ®isteredHttpClient->fcnTable.ftable1, |
| plContext); |
| if (pkixErrorResult || !crl) { |
| /* continue to next dp in case of unsuccesfull |
| * download attempt. */ |
| PKIX_DECREF(pkixErrorResult); |
| continue; |
| } |
| if (!crlList) { |
| PKIX_CHECK(PKIX_List_Create(&crlList, plContext), |
| PKIX_LISTCREATEFAILED); |
| } |
| pkixErrorResult = |
| PKIX_List_AppendItem(crlList, (PKIX_PL_Object *)crl, |
| plContext); |
| if (pkixErrorResult) { |
| PKIX_DECREF(pkixErrorResult); |
| } |
| PKIX_DECREF(crl); |
| } |
| *pCrlList = crlList; |
| crlList = NULL; |
| |
| cleanup: |
| PKIX_DECREF(dp); |
| PKIX_DECREF(crl); |
| PKIX_DECREF(crlList); |
| |
| PKIX_RETURN(CERTSTORE); |
| } |
| |
| |
| /* --Public-Pk11CertStore-Functions----------------------------------- */ |
| |
| /* |
| * FUNCTION: PKIX_PL_Pk11CertStore_Create |
| * (see comments in pkix_samples_modules.h) |
| */ |
| PKIX_Error * |
| PKIX_PL_Pk11CertStore_Create( |
| PKIX_CertStore **pCertStore, |
| void *plContext) |
| { |
| PKIX_CertStore *certStore = NULL; |
| |
| PKIX_ENTER(CERTSTORE, "PKIX_PL_Pk11CertStore_Create"); |
| PKIX_NULLCHECK_ONE(pCertStore); |
| |
| PKIX_CHECK(PKIX_CertStore_Create |
| (pkix_pl_Pk11CertStore_GetCert, |
| pkix_pl_Pk11CertStore_GetCRL, |
| NULL, /* getCertContinue */ |
| NULL, /* getCrlContinue */ |
| pkix_pl_Pk11CertStore_CheckTrust, |
| pkix_pl_Pk11CertStore_ImportCrl, |
| pkix_pl_Pk11CertStore_CheckRevByCrl, |
| NULL, |
| PKIX_TRUE, /* cache flag */ |
| PKIX_TRUE, /* local - no network I/O */ |
| &certStore, |
| plContext), |
| PKIX_CERTSTORECREATEFAILED); |
| |
| *pCertStore = certStore; |
| |
| cleanup: |
| |
| PKIX_RETURN(CERTSTORE); |
| } |