/* * Create a DER-encoded OCSP request (for the certificate whose nickname * is "name") and dump it out. */ static SECStatus create_request (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert, PRBool add_service_locator, PRBool add_acceptable_responses) { CERTCertList *certs = NULL; CERTCertificate *myCert = NULL; CERTOCSPRequest *request = NULL; int64 now = PR_Now(); SECItem *encoding = NULL; SECStatus rv = SECFailure; if (handle == NULL || cert == NULL) return rv; myCert = CERT_DupCertificate(cert); if (myCert == NULL) goto loser; /* * We need to create a list of one. */ certs = CERT_NewCertList(); if (certs == NULL) goto loser; if (CERT_AddCertToListTail (certs, myCert) != SECSuccess) goto loser; /* * Now that cert is included in the list, we need to be careful * that we do not try to destroy it twice. This will prevent that. */ myCert = NULL; request = CERT_CreateOCSPRequest (certs, now, add_service_locator, NULL); if (request == NULL) goto loser; if (add_acceptable_responses) { rv = CERT_AddOCSPAcceptableResponses(request, SEC_OID_PKIX_OCSP_BASIC_RESPONSE); if (rv != SECSuccess) goto loser; } encoding = CERT_EncodeOCSPRequest (NULL, request, NULL); if (encoding == NULL) goto loser; MAKE_FILE_BINARY(out_file); if (fwrite (encoding->data, encoding->len, 1, out_file) != 1) goto loser; rv = SECSuccess; loser: if (encoding != NULL) SECITEM_FreeItem(encoding, PR_TRUE); if (request != NULL) CERT_DestroyOCSPRequest(request); if (certs != NULL) CERT_DestroyCertList (certs); if (myCert != NULL) CERT_DestroyCertificate(myCert); return rv; }
/* * Create a DER-encoded OCSP request (for the certificate whose nickname is * "cert_name"), then get and dump a corresponding response. The responder * location is either specified explicitly (as "responder_url") or found * via the AuthorityInfoAccess URL in the cert. */ static SECStatus dump_response (FILE *out_file, CERTCertDBHandle *handle, CERTCertificate *cert, const char *responder_url) { CERTCertList *certs = NULL; CERTCertificate *myCert = NULL; char *loc = NULL; int64 now = PR_Now(); SECItem *response = NULL; SECStatus rv = SECFailure; PRBool includeServiceLocator; if (handle == NULL || cert == NULL) return rv; myCert = CERT_DupCertificate(cert); if (myCert == NULL) goto loser; if (responder_url != NULL) { loc = (char *) responder_url; includeServiceLocator = PR_TRUE; } else { loc = CERT_GetOCSPAuthorityInfoAccessLocation (cert); if (loc == NULL) goto loser; includeServiceLocator = PR_FALSE; } /* * We need to create a list of one. */ certs = CERT_NewCertList(); if (certs == NULL) goto loser; if (CERT_AddCertToListTail (certs, myCert) != SECSuccess) goto loser; /* * Now that cert is included in the list, we need to be careful * that we do not try to destroy it twice. This will prevent that. */ myCert = NULL; response = CERT_GetEncodedOCSPResponse (NULL, certs, loc, now, includeServiceLocator, NULL, NULL, NULL); if (response == NULL) goto loser; MAKE_FILE_BINARY(out_file); if (fwrite (response->data, response->len, 1, out_file) != 1) goto loser; rv = SECSuccess; loser: if (response != NULL) SECITEM_FreeItem (response, PR_TRUE); if (certs != NULL) CERT_DestroyCertList (certs); if (myCert != NULL) CERT_DestroyCertificate(myCert); if (loc != NULL && loc != responder_url) PORT_Free (loc); return rv; }
SECStatus NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb, SECCertUsage certusage, PRBool keepcerts) { int certcount; CERTCertificate **certArray = NULL; CERTCertList *certList = NULL; CERTCertListNode *node; SECStatus rv; SECItem **rawArray; int i; PRTime now; if (!sigd) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } certcount = NSS_CMSArray_Count((void **)sigd->rawCerts); /* get the certs in the temp DB */ rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts, &certArray, PR_FALSE, PR_FALSE, NULL); if (rv != SECSuccess) { goto loser; } /* save the certs so they don't get destroyed */ for (i=0; i < certcount; i++) { CERTCertificate *cert = certArray[i]; if (cert) NSS_CMSSignedData_AddTempCertificate(sigd, cert); } if (!keepcerts) { goto done; } /* build a CertList for filtering */ certList = CERT_NewCertList(); if (certList == NULL) { rv = SECFailure; goto loser; } for (i=0; i < certcount; i++) { CERTCertificate *cert = certArray[i]; if (cert) cert = CERT_DupCertificate(cert); if (cert) CERT_AddCertToListTail(certList,cert); } /* filter out the certs we don't want */ rv = CERT_FilterCertListByUsage(certList,certusage, PR_FALSE); if (rv != SECSuccess) { goto loser; } /* go down the remaining list of certs and verify that they have * valid chains, then import them. */ now = PR_Now(); for (node = CERT_LIST_HEAD(certList) ; !CERT_LIST_END(node,certList); node= CERT_LIST_NEXT(node)) { CERTCertificateList *certChain; if (CERT_VerifyCert(certdb, node->cert, PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) { continue; } certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE); if (!certChain) { continue; } /* * CertChain returns an array of SECItems, import expects an array of * SECItem pointers. Create the SECItem Pointers from the array of * SECItems. */ rawArray = (SECItem **)PORT_Alloc(certChain->len*sizeof (SECItem *)); if (!rawArray) { CERT_DestroyCertificateList(certChain); continue; } for (i=0; i < certChain->len; i++) { rawArray[i] = &certChain->certs[i]; } (void )CERT_ImportCerts(certdb, certusage, certChain->len, rawArray, NULL, keepcerts, PR_FALSE, NULL); PORT_Free(rawArray); CERT_DestroyCertificateList(certChain); } rv = SECSuccess; /* XXX CRL handling */ done: if (sigd->signerInfos != NULL) { /* fill in all signerinfo's certs */ for (i = 0; sigd->signerInfos[i] != NULL; i++) (void)NSS_CMSSignerInfo_GetSigningCertificate( sigd->signerInfos[i], certdb); } loser: /* now free everything */ if (certArray) { CERT_DestroyCertArray(certArray,certcount); } if (certList) { CERT_DestroyCertList(certList); } return rv; }
SECStatus CertVerifier::VerifyCert(CERTCertificate * cert, const SECCertificateUsage usage, const PRTime time, nsIInterfaceRequestor * pinArg, const Flags flags, /*optional out*/ CERTCertList **validationChain, /*optional out*/ SECOidTag *evOidPolicy, /*optional out*/ CERTVerifyLog *verifyLog) { if (!cert) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (validationChain) { *validationChain = nullptr; } if (evOidPolicy) { *evOidPolicy = SEC_OID_UNKNOWN; } switch(usage){ case certificateUsageSSLClient: case certificateUsageSSLServer: case certificateUsageSSLCA: case certificateUsageEmailSigner: case certificateUsageEmailRecipient: case certificateUsageObjectSigner: case certificateUsageStatusResponder: break; default: NS_WARNING("Calling VerifyCert with invalid usage"); PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } ScopedCERTCertList trustAnchors; SECStatus rv; SECOidTag evPolicy = SEC_OID_UNKNOWN; #ifdef NSS_NO_LIBPKIX return ClassicVerifyCert(cert, usage, time, pinArg, validationChain, verifyLog); #else // Do EV checking only for sslserver usage if (usage == certificateUsageSSLServer) { SECStatus srv = getFirstEVPolicy(cert, evPolicy); if (srv == SECSuccess) { if (evPolicy != SEC_OID_UNKNOWN) { trustAnchors = getRootsForOid(evPolicy); } if (!trustAnchors) { return SECFailure; } // pkix ignores an empty trustanchors list and // decides then to use the whole set of trust in the DB // so we set the evPolicy to unkown in this case if (CERT_LIST_EMPTY(trustAnchors)) { evPolicy = SEC_OID_UNKNOWN; } } else { // Do not setup EV verification params evPolicy = SEC_OID_UNKNOWN; } } MOZ_ASSERT_IF(evPolicy != SEC_OID_UNKNOWN, trustAnchors); size_t i = 0; size_t validationChainLocation = 0; size_t validationTrustAnchorLocation = 0; CERTValOutParam cvout[4]; if (verifyLog) { cvout[i].type = cert_po_errorLog; cvout[i].value.pointer.log = verifyLog; ++i; } if (validationChain) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: setting up validation chain outparam.\n")); validationChainLocation = i; cvout[i].type = cert_po_certList; cvout[i].value.pointer.cert = nullptr; ++i; validationTrustAnchorLocation = i; cvout[i].type = cert_po_trustAnchor; cvout[i].value.pointer.chain = nullptr; ++i; } cvout[i].type = cert_po_end; CERTRevocationFlags rev; CERTRevocationMethodIndex revPreferredMethods[2]; rev.leafTests.preferred_methods = rev.chainTests.preferred_methods = revPreferredMethods; uint64_t revFlagsPerMethod[2]; rev.leafTests.cert_rev_flags_per_method = rev.chainTests.cert_rev_flags_per_method = revFlagsPerMethod; rev.leafTests.number_of_preferred_methods = rev.chainTests.number_of_preferred_methods = 1; rev.leafTests.number_of_defined_methods = rev.chainTests.number_of_defined_methods = cert_revocation_method_ocsp + 1; const bool localOnly = flags & FLAG_LOCAL_ONLY; CERTValInParam cvin[6]; // Parameters for both EV and DV validation cvin[0].type = cert_pi_useAIACertFetch; cvin[0].value.scalar.b = mMissingCertDownloadEnabled && !localOnly; cvin[1].type = cert_pi_revocationFlags; cvin[1].value.pointer.revocation = &rev; cvin[2].type = cert_pi_date; cvin[2].value.scalar.time = time; i = 3; const size_t evParamLocation = i; if (evPolicy != SEC_OID_UNKNOWN) { // EV setup! // XXX 859872 The current flags are not quite correct. (use // of ocsp flags for crl preferences). uint64_t ocspRevMethodFlags = CERT_REV_M_TEST_USING_THIS_METHOD | ((mOCSPDownloadEnabled && !localOnly) ? CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING) | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE | CERT_REV_M_IGNORE_MISSING_FRESH_INFO | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO | (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP); rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] = rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl] = CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD; rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] = rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] = ocspRevMethodFlags; rev.leafTests.cert_rev_method_independent_flags = rev.chainTests.cert_rev_method_independent_flags = // avoiding the network is good, let's try local first CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST // is overall revocation requirement strict or relaxed? | CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE ; rev.leafTests.preferred_methods[0] = rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp; cvin[i].type = cert_pi_policyOID; cvin[i].value.arraySize = 1; cvin[i].value.array.oids = &evPolicy; ++i; MOZ_ASSERT(trustAnchors); cvin[i].type = cert_pi_trustAnchors; cvin[i].value.pointer.chain = trustAnchors; ++i; cvin[i].type = cert_pi_end; rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg); if (rv == SECSuccess) { if (evOidPolicy) { *evOidPolicy = evPolicy; } PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: successful CERT_PKIXVerifyCert(ev) \n")); goto pkix_done; } PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: failed CERT_PKIXVerifyCert(ev)\n")); if (validationChain && *validationChain) { // There SHOULD not be a validation chain on failure, asserion here for // the debug builds AND a fallback for production builds MOZ_ASSERT(false, "certPKIXVerifyCert returned failure AND a validationChain"); CERT_DestroyCertList(*validationChain); *validationChain = nullptr; } if (verifyLog) { // Cleanup the log so that it is ready the the next validation CERTVerifyLogNode *i_node; for (i_node = verifyLog->head; i_node; i_node = i_node->next) { //destroy cert if any. if (i_node->cert) { CERT_DestroyCertificate(i_node->cert); } // No need to cleanup the actual nodes in the arena. } verifyLog->count = 0; verifyLog->head = nullptr; verifyLog->tail = nullptr; } } if (!nsNSSComponent::globalConstFlagUsePKIXVerification){ // XXX: we do not care about the localOnly flag (currently) as the // caller that wants localOnly should disable and reenable the fetching. return ClassicVerifyCert(cert, usage, time, pinArg, validationChain, verifyLog); } // The current flags check the chain the same way as the leafs rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] = rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl] = // implicit default source - makes no sense for CRLs CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE // let's not stop on fresh CRL. If OCSP is enabled, too, let's check it | CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO // no fresh CRL? well, let other flag decide whether to fail or not | CERT_REV_M_IGNORE_MISSING_FRESH_INFO // testing using local CRLs is always allowed | CERT_REV_M_TEST_USING_THIS_METHOD // no local crl and don't know where to get it from? ignore | CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE // crl download based on parameter | ((mCRLDownloadEnabled && !localOnly) ? CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING) ; rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] = rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] = // use OCSP CERT_REV_M_TEST_USING_THIS_METHOD // if app has a default OCSP responder configured, let's use it | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE // of course OCSP doesn't work without a source. let's accept such certs | CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE // if ocsp is required stop on lack of freshness | (mOCSPStrict ? CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO : CERT_REV_M_IGNORE_MISSING_FRESH_INFO) // ocsp success is sufficient | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO // ocsp enabled controls network fetching, too | ((mOCSPDownloadEnabled && !localOnly) ? CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING) | (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP); ; rev.leafTests.preferred_methods[0] = rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp; rev.leafTests.cert_rev_method_independent_flags = rev.chainTests.cert_rev_method_independent_flags = // avoiding the network is good, let's try local first CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST // is overall revocation requirement strict or relaxed? | (mRequireRevocationInfo ? CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE : CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT) ; // Skip EV parameters cvin[evParamLocation].type = cert_pi_end; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: calling CERT_PKIXVerifyCert(dv) \n")); rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg); pkix_done: if (validationChain) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: validation chain requested\n")); ScopedCERTCertificate trustAnchor(cvout[validationTrustAnchorLocation].value.pointer.cert); if (rv == SECSuccess) { if (! cvout[validationChainLocation].value.pointer.chain) { PR_SetError(PR_UNKNOWN_ERROR, 0); return SECFailure; } PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: I have a chain\n")); *validationChain = cvout[validationChainLocation].value.pointer.chain; if (trustAnchor) { // we should only add the issuer to the chain if it is not already // present. On CA cert checking, the issuer is the same cert, so in // that case we do not add the cert to the chain. if (!CERT_CompareCerts(trustAnchor, cert)) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: adding issuer to tail for display\n")); // note: rv is reused to catch errors on cert creation! ScopedCERTCertificate tempCert(CERT_DupCertificate(trustAnchor)); rv = CERT_AddCertToListTail(*validationChain, tempCert); if (rv == SECSuccess) { tempCert.forget(); // ownership traferred to validationChain } else { CERT_DestroyCertList(*validationChain); *validationChain = nullptr; } } } } else { // Validation was a fail, clean up if needed if (cvout[validationChainLocation].value.pointer.chain) { CERT_DestroyCertList(cvout[validationChainLocation].value.pointer.chain); } } } return rv; #endif }
nsresult VerifyCMSDetachedSignatureIncludingCertificate( const SECItem& buffer, const SECItem& detachedDigest, nsresult (*verifyCertificate)(CERTCertificate* cert, void* context, void* pinArg), void *verifyCertificateContext, void* pinArg) { // XXX: missing pinArg is tolerated. if (NS_WARN_IF(!buffer.data && buffer.len > 0) || NS_WARN_IF(!detachedDigest.data && detachedDigest.len > 0) || (!verifyCertificate) || NS_WARN_IF(!verifyCertificateContext)) { return NS_ERROR_INVALID_ARG; } ScopedNSSCMSMessage cmsMsg(NSS_CMSMessage_CreateFromDER(const_cast<SECItem*>(&buffer), nullptr, nullptr, nullptr, nullptr, nullptr, nullptr)); if (!cmsMsg) { return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } if (!NSS_CMSMessage_IsSigned(cmsMsg.get())) { return NS_ERROR_CMS_VERIFY_NOT_SIGNED; } NSSCMSContentInfo* cinfo = NSS_CMSMessage_ContentLevel(cmsMsg.get(), 0); if (!cinfo) { return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO; } // signedData is non-owning NSSCMSSignedData* signedData = reinterpret_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(cinfo)); if (!signedData) { return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO; } // Set digest value. if (NSS_CMSSignedData_SetDigestValue(signedData, SEC_OID_SHA1, const_cast<SECItem*>(&detachedDigest))) { return NS_ERROR_CMS_VERIFY_BAD_DIGEST; } // Parse the certificates into CERTCertificate objects held in memory so // verifyCertificate will be able to find them during path building. ScopedCERTCertList certs(CERT_NewCertList()); if (!certs) { return NS_ERROR_OUT_OF_MEMORY; } if (signedData->rawCerts) { for (size_t i = 0; signedData->rawCerts[i]; ++i) { ScopedCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), signedData->rawCerts[i], nullptr, false, true)); // Skip certificates that fail to parse if (cert) { if (CERT_AddCertToListTail(certs.get(), cert.get()) == SECSuccess) { cert.forget(); // ownership transfered } else { return NS_ERROR_OUT_OF_MEMORY; } } } } // Get the end-entity certificate. int numSigners = NSS_CMSSignedData_SignerInfoCount(signedData); if (NS_WARN_IF(numSigners != 1)) { return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } // signer is non-owning. NSSCMSSignerInfo* signer = NSS_CMSSignedData_GetSignerInfo(signedData, 0); if (NS_WARN_IF(!signer)) { return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } CERTCertificate* signerCert = NSS_CMSSignerInfo_GetSigningCertificate(signer, CERT_GetDefaultCertDB()); if (!signerCert) { return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } nsresult rv = verifyCertificate(signerCert, verifyCertificateContext, pinArg); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } // See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS. SECOidData* contentTypeOidData = SECOID_FindOID(&signedData->contentInfo.contentType); if (!contentTypeOidData) { return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } return MapSECStatus(NSS_CMSSignerInfo_Verify(signer, const_cast<SECItem*>(&detachedDigest), &contentTypeOidData->oid)); }
/* * [noscript] void importEmailCertificates(in charPtr data, in unsigned long length, * in nsIInterfaceRequestor ctx); */ NS_IMETHODIMP nsNSSCertificateDB::ImportEmailCertificate(PRUint8 * data, PRUint32 length, nsIInterfaceRequestor *ctx) { nsNSSShutDownPreventionLock locker; SECStatus srv = SECFailure; nsresult nsrv = NS_OK; CERTCertDBHandle *certdb; CERTCertificate **certArray = NULL; CERTCertList *certList = NULL; CERTCertListNode *node; PRTime now; SECCertUsage certusage; SECItem **rawArray; int numcerts; int i; PRArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) return NS_ERROR_OUT_OF_MEMORY; CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length); if (!certCollection) { PORT_FreeArena(arena, PR_FALSE); return NS_ERROR_FAILURE; } certdb = CERT_GetDefaultCertDB(); certusage = certUsageEmailRecipient; numcerts = certCollection->numcerts; rawArray = (SECItem **) PORT_Alloc(sizeof(SECItem *) * numcerts); if ( !rawArray ) { nsrv = NS_ERROR_FAILURE; goto loser; } for (i=0; i < numcerts; i++) { rawArray[i] = &certCollection->rawCerts[i]; } srv = CERT_ImportCerts(certdb, certusage, numcerts, rawArray, &certArray, PR_FALSE, PR_FALSE, NULL); PORT_Free(rawArray); rawArray = NULL; if (srv != SECSuccess) { nsrv = NS_ERROR_FAILURE; goto loser; } // build a CertList for filtering certList = CERT_NewCertList(); if (certList == NULL) { nsrv = NS_ERROR_FAILURE; goto loser; } for (i=0; i < numcerts; i++) { CERTCertificate *cert = certArray[i]; if (cert) cert = CERT_DupCertificate(cert); if (cert) CERT_AddCertToListTail(certList, cert); } /* go down the remaining list of certs and verify that they have * valid chains, then import them. */ now = PR_Now(); for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node,certList); node = CERT_LIST_NEXT(node)) { bool alert_and_skip = false; if (!node->cert) { continue; } if (CERT_VerifyCert(certdb, node->cert, PR_TRUE, certusage, now, ctx, NULL) != SECSuccess) { alert_and_skip = true; } CERTCertificateList *certChain = nsnull; CERTCertificateListCleaner chainCleaner(certChain); if (!alert_and_skip) { certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE); if (!certChain) { alert_and_skip = true; } } if (alert_and_skip) { nsCOMPtr<nsIX509Cert> certToShow = new nsNSSCertificate(node->cert); DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow); continue; } /* * CertChain returns an array of SECItems, import expects an array of * SECItem pointers. Create the SECItem Pointers from the array of * SECItems. */ rawArray = (SECItem **) PORT_Alloc(certChain->len * sizeof(SECItem *)); if (!rawArray) { continue; } for (i=0; i < certChain->len; i++) { rawArray[i] = &certChain->certs[i]; } CERT_ImportCerts(certdb, certusage, certChain->len, rawArray, NULL, PR_TRUE, PR_FALSE, NULL); CERT_SaveSMimeProfile(node->cert, NULL, NULL); PORT_Free(rawArray); } loser: if (certArray) { CERT_DestroyCertArray(certArray, numcerts); } if (certList) { CERT_DestroyCertList(certList); } if (arena) PORT_FreeArena(arena, PR_TRUE); return nsrv; }
nsresult nsNSSCertificateDB::handleCACertDownload(nsIArray *x509Certs, nsIInterfaceRequestor *ctx) { // First thing we have to do is figure out which certificate we're // gonna present to the user. The CA may have sent down a list of // certs which may or may not be a chained list of certs. Until // the day we can design some solid UI for the general case, we'll // code to the > 90% case. That case is where a CA sends down a // list that is a hierarchy whose root is either the first or // the last cert. What we're gonna do is compare the first // 2 entries, if the second was signed by the first, we assume // the root cert is the first cert and display it. Otherwise, // we compare the last 2 entries, if the second to last cert was // signed by the last cert, then we assume the last cert is the // root and display it. nsNSSShutDownPreventionLock locker; PRUint32 numCerts; x509Certs->GetLength(&numCerts); NS_ASSERTION(numCerts > 0, "Didn't get any certs to import."); if (numCerts == 0) return NS_OK; // Nothing to import, so nothing to do. nsCOMPtr<nsIX509Cert> certToShow; nsCOMPtr<nsISupports> isupports; PRUint32 selCertIndex; if (numCerts == 1) { // There's only one cert, so let's show it. selCertIndex = 0; certToShow = do_QueryElementAt(x509Certs, selCertIndex); } else { nsCOMPtr<nsIX509Cert> cert0; // first cert nsCOMPtr<nsIX509Cert> cert1; // second cert nsCOMPtr<nsIX509Cert> certn_2; // second to last cert nsCOMPtr<nsIX509Cert> certn_1; // last cert cert0 = do_QueryElementAt(x509Certs, 0); cert1 = do_QueryElementAt(x509Certs, 1); certn_2 = do_QueryElementAt(x509Certs, numCerts-2); certn_1 = do_QueryElementAt(x509Certs, numCerts-1); nsXPIDLString cert0SubjectName; nsXPIDLString cert1IssuerName; nsXPIDLString certn_2IssuerName; nsXPIDLString certn_1SubjectName; cert0->GetSubjectName(cert0SubjectName); cert1->GetIssuerName(cert1IssuerName); certn_2->GetIssuerName(certn_2IssuerName); certn_1->GetSubjectName(certn_1SubjectName); if (cert1IssuerName.Equals(cert0SubjectName)) { // In this case, the first cert in the list signed the second, // so the first cert is the root. Let's display it. selCertIndex = 0; certToShow = cert0; } else if (certn_2IssuerName.Equals(certn_1SubjectName)) { // In this case the last cert has signed the second to last cert. // The last cert is the root, so let's display it. selCertIndex = numCerts-1; certToShow = certn_1; } else { // It's not a chain, so let's just show the first one in the // downloaded list. selCertIndex = 0; certToShow = cert0; } } if (!certToShow) return NS_ERROR_FAILURE; nsCOMPtr<nsICertificateDialogs> dialogs; nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsICertificateDialogs), NS_CERTIFICATEDIALOGS_CONTRACTID); if (NS_FAILED(rv)) return rv; SECItem der; rv=certToShow->GetRawDER(&der.len, (PRUint8 **)&der.data); if (NS_FAILED(rv)) return rv; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Creating temp cert\n")); CERTCertificate *tmpCert; CERTCertDBHandle *certdb = CERT_GetDefaultCertDB(); tmpCert = CERT_FindCertByDERCert(certdb, &der); if (!tmpCert) { tmpCert = CERT_NewTempCertificate(certdb, &der, nsnull, PR_FALSE, PR_TRUE); } nsMemory::Free(der.data); der.data = nsnull; der.len = 0; if (!tmpCert) { NS_ERROR("Couldn't create cert from DER blob"); return NS_ERROR_FAILURE; } CERTCertificateCleaner tmpCertCleaner(tmpCert); if (!CERT_IsCACert(tmpCert, NULL)) { DisplayCertificateAlert(ctx, "NotACACert", certToShow); return NS_ERROR_FAILURE; } if (tmpCert->isperm) { DisplayCertificateAlert(ctx, "CaCertExists", certToShow); return NS_ERROR_FAILURE; } PRUint32 trustBits; PRBool allows; rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows); if (NS_FAILED(rv)) return rv; if (!allows) return NS_ERROR_NOT_AVAILABLE; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("trust is %d\n", trustBits)); nsXPIDLCString nickname; nickname.Adopt(CERT_MakeCANickname(tmpCert)); PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Created nick \"%s\"\n", nickname.get())); nsNSSCertTrust trust; trust.SetValidCA(); trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL), !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL), !!(trustBits & nsIX509CertDB::TRUSTED_OBJSIGN)); SECStatus srv = CERT_AddTempCertToPerm(tmpCert, const_cast<char*>(nickname.get()), trust.GetTrust()); if (srv != SECSuccess) return NS_ERROR_FAILURE; // Import additional delivered certificates that can be verified. // build a CertList for filtering CERTCertList *certList = CERT_NewCertList(); if (certList == NULL) { return NS_ERROR_FAILURE; } CERTCertListCleaner listCleaner(certList); // get all remaining certs into temp store for (PRUint32 i=0; i<numCerts; i++) { if (i == selCertIndex) { // we already processed that one continue; } certToShow = do_QueryElementAt(x509Certs, i); certToShow->GetRawDER(&der.len, (PRUint8 **)&der.data); CERTCertificate *tmpCert2 = CERT_NewTempCertificate(certdb, &der, nsnull, PR_FALSE, PR_TRUE); nsMemory::Free(der.data); der.data = nsnull; der.len = 0; if (!tmpCert2) { NS_ERROR("Couldn't create temp cert from DER blob"); continue; // Let's try to import the rest of 'em } CERT_AddCertToListTail(certList, tmpCert2); } return ImportValidCACertsInList(certList, ctx); }
SECStatus CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, Time time, void* pinArg, const char* hostname, const Flags flags, /*optional*/ const SECItem* stapledOCSPResponseSECItem, /*optional out*/ ScopedCERTCertList* builtChain, /*optional out*/ SECOidTag* evOidPolicy) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of VerifyCert\n")); PR_ASSERT(cert); PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV)); if (builtChain) { *builtChain = nullptr; } if (evOidPolicy) { *evOidPolicy = SEC_OID_UNKNOWN; } if (!cert || (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } Result rv; Input certDER; rv = certDER.Init(cert->derCert.data, cert->derCert.len); if (rv != Success) { PR_SetError(MapResultToPRErrorCode(rv), 0); return SECFailure; } ChainValidationCallbackState callbackState = { hostname, mPinningEnforcementLevel, usage, time }; CERTChainVerifyCallback callbackContainer; callbackContainer.isChainValid = chainValidationCallback; callbackContainer.isChainValidArg = &callbackState; NSSCertDBTrustDomain::OCSPFetching ocspFetching = !mOCSPDownloadEnabled || (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP : !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail : NSSCertDBTrustDomain::FetchOCSPForDVHardFail; ocsp_get_config ocspGETConfig = mOCSPGETEnabled ? ocsp_get_enabled : ocsp_get_disabled; Input stapledOCSPResponseInput; const Input* stapledOCSPResponse = nullptr; if (stapledOCSPResponseSECItem) { rv = stapledOCSPResponseInput.Init(stapledOCSPResponseSECItem->data, stapledOCSPResponseSECItem->len); if (rv != Success) { // The stapled OCSP response was too big. PR_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE, 0); return SECFailure; } stapledOCSPResponse = &stapledOCSPResponseInput; } switch (usage) { case certificateUsageSSLClient: { // XXX: We don't really have a trust bit for SSL client authentication so // just use trustEmail as it is the closest alternative. NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth, CertPolicyId::anyPolicy, stapledOCSPResponse); break; } case certificateUsageSSLServer: { // TODO: When verifying a certificate in an SSL handshake, we should // restrict the acceptable key usage based on the key exchange method // chosen by the server. #ifndef MOZ_NO_EV_CERTS // Try to validate for EV first. CertPolicyId evPolicy; SECOidTag evPolicyOidTag; SECStatus srv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag); if (srv == SECSuccess) { NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV : NSSCertDBTrustDomain::FetchOCSPForEV, mOCSPCache, pinArg, ocspGETConfig, &callbackContainer, builtChain); rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time, KeyUsage::digitalSignature,// (EC)DHE KeyUsage::keyEncipherment, // RSA KeyUsage::keyAgreement, // (EC)DH KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse); if (rv == Success) { if (evOidPolicy) { *evOidPolicy = evPolicyOidTag; } break; } } #endif if (flags & FLAG_MUST_BE_EV) { rv = Result::ERROR_POLICY_VALIDATION_FAILED; break; } // Now try non-EV. NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, &callbackContainer, builtChain); rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time, KeyUsage::digitalSignature, // (EC)DHE KeyUsage::keyEncipherment, // RSA KeyUsage::keyAgreement, // (EC)DH KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, stapledOCSPResponse); break; } case certificateUsageSSLCA: { NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, stapledOCSPResponse); break; } case certificateUsageEmailSigner: { NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection, CertPolicyId::anyPolicy, stapledOCSPResponse); break; } case certificateUsageEmailRecipient: { // TODO: The higher level S/MIME processing should pass in which key // usage it is trying to verify for, and base its algorithm choices // based on the result of the verification(s). NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::keyEncipherment, // RSA KeyPurposeId::id_kp_emailProtection, CertPolicyId::anyPolicy, stapledOCSPResponse); if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::keyAgreement, // ECDH/DH KeyPurposeId::id_kp_emailProtection, CertPolicyId::anyPolicy, stapledOCSPResponse); } break; } case certificateUsageObjectSigner: { NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::digitalSignature, KeyPurposeId::id_kp_codeSigning, CertPolicyId::anyPolicy, stapledOCSPResponse); break; } case certificateUsageVerifyCA: case certificateUsageStatusResponder: { // XXX This is a pretty useless way to verify a certificate. It is used // by the certificate viewer UI. Because we don't know what trust bit is // interesting, we just try them all. mozilla::pkix::EndEntityOrCA endEntityOrCA; mozilla::pkix::KeyUsage keyUsage; KeyPurposeId eku; if (usage == certificateUsageVerifyCA) { endEntityOrCA = EndEntityOrCA::MustBeCA; keyUsage = KeyUsage::keyCertSign; eku = KeyPurposeId::anyExtendedKeyUsage; } else { endEntityOrCA = EndEntityOrCA::MustBeEndEntity; keyUsage = KeyUsage::digitalSignature; eku = KeyPurposeId::id_kp_OCSPSigning; } NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); if (rv == Result::ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(emailTrust, certDER, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); if (rv == Result::ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(objectSigningTrust, certDER, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); } } break; } default: rv = Result::FATAL_ERROR_INVALID_ARGS; } if (rv != Success) { if (rv != Result::ERROR_KEY_PINNING_FAILURE) { ScopedCERTCertificate certCopy(CERT_DupCertificate(cert)); if (!certCopy) { return SECFailure; } ScopedCERTCertList certList(CERT_NewCertList()); if (!certList) { return SECFailure; } SECStatus srv = CERT_AddCertToListTail(certList.get(), certCopy.get()); if (srv != SECSuccess) { return SECFailure; } certCopy.forget(); // now owned by certList PRBool chainOK = false; srv = chainValidationCallback(&callbackState, certList, &chainOK); if (srv != SECSuccess) { return SECFailure; } if (!chainOK) { rv = Result::ERROR_KEY_PINNING_FAILURE; } } PR_SetError(MapResultToPRErrorCode(rv), 0); return SECFailure; } return SECSuccess; }