nsresult nsNSSCertificate::hasValidEVOidTag(SECOidTag &resultOidTag, PRBool &validEV) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; nsresult nrv; nsCOMPtr<nsINSSComponent> nssComponent = do_GetService(PSM_COMPONENT_CONTRACTID, &nrv); if (NS_FAILED(nrv)) return nrv; nssComponent->EnsureIdentityInfoLoaded(); validEV = PR_FALSE; resultOidTag = SEC_OID_UNKNOWN; PRBool isOCSPEnabled = PR_FALSE; nsCOMPtr<nsIX509CertDB> certdb; certdb = do_GetService(NS_X509CERTDB_CONTRACTID); if (certdb) certdb->GetIsOcspOn(&isOCSPEnabled); // No OCSP, no EV if (!isOCSPEnabled) return NS_OK; SECOidTag oid_tag; SECStatus rv = getFirstEVPolicy(mCert, oid_tag); if (rv != SECSuccess) return NS_OK; if (oid_tag == SEC_OID_UNKNOWN) // not in our list of OIDs accepted for EV return NS_OK; CERTCertList *rootList = getRootsForOid(oid_tag); CERTCertListCleaner rootListCleaner(); CERTRevocationMethodIndex preferedRevMethods[1] = { cert_revocation_method_ocsp }; PRUint64 revMethodFlags = CERT_REV_M_TEST_USING_THIS_METHOD | CERT_REV_M_ALLOW_NETWORK_FETCHING | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; PRUint64 revMethodIndependentFlags = CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST | CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; PRUint64 methodFlags[2]; methodFlags[cert_revocation_method_crl] = revMethodFlags; methodFlags[cert_revocation_method_ocsp] = revMethodFlags; CERTRevocationFlags rev; rev.leafTests.number_of_defined_methods = cert_revocation_method_ocsp +1; rev.leafTests.cert_rev_flags_per_method = methodFlags; rev.leafTests.number_of_preferred_methods = 1; rev.leafTests.preferred_methods = preferedRevMethods; rev.leafTests.cert_rev_method_independent_flags = revMethodIndependentFlags; rev.chainTests.number_of_defined_methods = cert_revocation_method_ocsp +1; rev.chainTests.cert_rev_flags_per_method = methodFlags; rev.chainTests.number_of_preferred_methods = 1; rev.chainTests.preferred_methods = preferedRevMethods; rev.chainTests.cert_rev_method_independent_flags = revMethodIndependentFlags; CERTValInParam cvin[3]; cvin[0].type = cert_pi_policyOID; cvin[0].value.arraySize = 1; cvin[0].value.array.oids = &oid_tag; cvin[1].type = cert_pi_revocationFlags; cvin[1].value.pointer.revocation = &rev; cvin[2].type = cert_pi_trustAnchors; cvin[2].value.pointer.chain = rootList; cvin[3].type = cert_pi_end; CERTValOutParam cvout[2]; cvout[0].type = cert_po_trustAnchor; cvout[0].value.pointer.cert = nsnull; cvout[1].type = cert_po_end; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("calling CERT_PKIXVerifyCert nss cert %p\n", mCert)); rv = CERT_PKIXVerifyCert(mCert, certificateUsageSSLServer, cvin, cvout, nsnull); if (rv != SECSuccess) return NS_OK; CERTCertificate *issuerCert = cvout[0].value.pointer.cert; CERTCertificateCleaner issuerCleaner(issuerCert); #ifdef PR_LOGGING if (PR_LOG_TEST(gPIPNSSLog, PR_LOG_DEBUG)) { nsNSSCertificate ic(issuerCert); nsAutoString fingerprint; ic.GetSha1Fingerprint(fingerprint); NS_LossyConvertUTF16toASCII fpa(fingerprint); PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CERT_PKIXVerifyCert returned success, issuer: %s, SHA1: %s\n", issuerCert->subjectName, fpa.get())); } #endif validEV = isApprovedForEV(oid_tag, issuerCert); if (validEV) resultOidTag = oid_tag; return NS_OK; }
static int vfy_chain_pkix(CERTCertificate **chain, int chain_len, CERTCertificate **end_out, bool *rev_opts) { CERTCertificate *end_cert = NULL; CERTCertList *trustcl = get_all_root_certs(); if (trustcl == NULL) { DBG(DBG_X509, DBG_log("X509: no trust anchor available for verification")); return VERIFY_RET_SKIP; } int i; for (i = 0; i < chain_len; i++) { if (!CERT_IsCACert(chain[i], NULL)) { end_cert = chain[i]; break; } } if (end_cert == NULL) { libreswan_log("X509: no EE-cert in chain!"); return VERIFY_RET_FAIL; } CERTVerifyLog *cur_log = NULL; CERTVerifyLog vfy_log; CERTVerifyLog vfy_log2; new_vfy_log(&vfy_log); new_vfy_log(&vfy_log2); CERTRevocationFlags rev; zero(&rev); /* ??? are there pointer fields? */ PRUint64 revFlagsLeaf[2] = { 0, 0 }; PRUint64 revFlagsChain[2] = { 0, 0 }; set_rev_per_meth(&rev, revFlagsLeaf, revFlagsChain); set_rev_params(&rev, rev_opts[RO_CRL_S], rev_opts[RO_OCSP], rev_opts[RO_OCSP_S]); int in_idx = 0; CERTValInParam cvin[7]; CERTValOutParam cvout[3]; zero(&cvin); /* ??? are there pointer fields? */ zero(&cvout); /* ??? are there pointer fields? */ cvin[in_idx].type = cert_pi_revocationFlags; cvin[in_idx++].value.pointer.revocation = &rev; cvin[in_idx].type = cert_pi_useAIACertFetch; cvin[in_idx++].value.scalar.b = rev_opts[RO_OCSP]; cvin[in_idx].type = cert_pi_trustAnchors; cvin[in_idx++].value.pointer.chain = trustcl; cvin[in_idx].type = cert_pi_useOnlyTrustAnchors; cvin[in_idx++].value.scalar.b = PR_TRUE; cvin[in_idx].type = cert_pi_end; cvout[0].type = cert_po_errorLog; cvout[0].value.pointer.log = cur_log = &vfy_log; cvout[1].type = cert_po_certList; cvout[1].value.pointer.chain = NULL; cvout[2].type = cert_po_end; /* kludge alert!! * verification may be performed twice: once with the * 'client' usage and once with 'server', which is an NSS * detail and not related to IKE. In the absence of a real * IKE profile being available for NSS, this covers more * KU/EKU combinations */ int fin; SECCertificateUsage usage; for (usage = certificateUsageSSLClient; ; usage = certificateUsageSSLServer) { SECStatus rv = CERT_PKIXVerifyCert(end_cert, usage, cvin, cvout, NULL); if (rv != SECSuccess || cur_log->count > 0) { if (cur_log->count > 0 && cur_log->head != NULL) { if (usage == certificateUsageSSLClient && RETRYABLE_TYPE(cur_log->head->error)) { /* try again, after some adjustments */ DBG(DBG_X509, DBG_log("retrying verification with the NSS serverAuth profile")); /* ??? since we are about to overwrite cvout[1], * should we be doing: * if (cvout[1].value.pointer.chain != NULL) * CERT_DestroyCertList(cvout[1].value.pointer.chain); */ cvout[0].value.pointer.log = cur_log = &vfy_log2; cvout[1].value.pointer.chain = NULL; continue; } else { fin = nss_err_to_revfail(cur_log->head); } } else { /* * An rv != SECSuccess without CERTVerifyLog results should not * happen, but catch it anyway */ libreswan_log("X509: unspecified NSS verification failure"); fin = VERIFY_RET_FAIL; } } else { DBG(DBG_X509, DBG_log("certificate is valid")); *end_out = end_cert; fin = VERIFY_RET_OK; } break; } CERT_DestroyCertList(trustcl); PORT_FreeArena(vfy_log.arena, PR_FALSE); PORT_FreeArena(vfy_log2.arena, PR_FALSE); if (cvout[1].value.pointer.chain != NULL) { CERT_DestroyCertList(cvout[1].value.pointer.chain); } return fin; }
int main(int argc, char *argv[], char *envp[]) { char * certDir = NULL; char * progName = NULL; char * oidStr = NULL; CERTCertificate * cert; CERTCertificate * firstCert = NULL; CERTCertificate * issuerCert = NULL; CERTCertDBHandle * defaultDB = NULL; PRBool isAscii = PR_FALSE; PRBool trusted = PR_FALSE; SECStatus secStatus; SECCertificateUsage certUsage = certificateUsageSSLServer; PLOptState * optstate; PRTime time = 0; PLOptStatus status; int usePkix = 0; int rv = 1; int usage; CERTVerifyLog log; CERTCertList *builtChain = NULL; PRBool certFetching = PR_FALSE; int revDataIndex = 0; PRBool ocsp_fetchingFailureIsAFailure = PR_TRUE; PRBool useDefaultRevFlags = PR_TRUE; int vfyCounts = 1; PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); progName = PL_strdup(argv[0]); optstate = PL_CreateOptState(argc, argv, "ab:c:d:efg:h:i:m:o:prs:tu:vw:W:"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch(optstate->option) { case 0 : /* positional parameter */ goto breakout; case 'a' : isAscii = PR_TRUE; break; case 'b' : secStatus = DER_AsciiToTime(&time, optstate->value); if (secStatus != SECSuccess) Usage(progName); break; case 'd' : certDir = PL_strdup(optstate->value); break; case 'e' : ocsp_fetchingFailureIsAFailure = PR_FALSE; break; case 'f' : certFetching = PR_TRUE; break; case 'g' : if (revMethodsData[revDataIndex].testTypeStr || revMethodsData[revDataIndex].methodTypeStr) { revDataIndex += 1; if (revDataIndex == REV_METHOD_INDEX_MAX) { fprintf(stderr, "Invalid revocation configuration" "specified.\n"); secStatus = SECFailure; break; } } useDefaultRevFlags = PR_FALSE; revMethodsData[revDataIndex]. testTypeStr = PL_strdup(optstate->value); break; case 'h' : revMethodsData[revDataIndex]. testFlagsStr = PL_strdup(optstate->value);break; case 'i' : vfyCounts = PORT_Atoi(optstate->value); break; break; case 'm' : if (revMethodsData[revDataIndex].methodTypeStr) { revDataIndex += 1; if (revDataIndex == REV_METHOD_INDEX_MAX) { fprintf(stderr, "Invalid revocation configuration" "specified.\n"); secStatus = SECFailure; break; } } useDefaultRevFlags = PR_FALSE; revMethodsData[revDataIndex]. methodTypeStr = PL_strdup(optstate->value); break; case 'o' : oidStr = PL_strdup(optstate->value); break; case 'p' : usePkix += 1; break; case 'r' : isAscii = PR_FALSE; break; case 's' : revMethodsData[revDataIndex]. methodFlagsStr = PL_strdup(optstate->value); break; case 't' : trusted = PR_TRUE; break; case 'u' : usage = PORT_Atoi(optstate->value); if (usage < 0 || usage > 62) Usage(progName); certUsage = ((SECCertificateUsage)1) << usage; if (certUsage > certificateUsageHighest) Usage(progName); break; case 'w': pwdata.source = PW_PLAINTEXT; pwdata.data = PORT_Strdup(optstate->value); break; case 'W': pwdata.source = PW_FROMFILE; pwdata.data = PORT_Strdup(optstate->value); break; case 'v' : verbose++; break; default : Usage(progName); break; } } breakout: if (status != PL_OPT_OK) Usage(progName); if (usePkix < 2) { if (oidStr) { fprintf(stderr, "Policy oid(-o) can be used only with" " CERT_PKIXVerifyChain(-pp) function.\n"); Usage(progName); } if (trusted) { fprintf(stderr, "Cert trust flag can be used only with" " CERT_PKIXVerifyChain(-pp) function.\n"); Usage(progName); } } if (!useDefaultRevFlags && parseRevMethodsAndFlags()) { fprintf(stderr, "Invalid revocation configuration specified.\n"); goto punt; } /* Set our password function callback. */ PK11_SetPasswordFunc(SECU_GetModulePassword); /* Initialize the NSS libraries. */ if (certDir) { secStatus = NSS_Init(certDir); } else { secStatus = NSS_NoDB_Init(NULL); /* load the builtins */ SECMOD_AddNewModule("Builtins", DLL_PREFIX"nssckbi."DLL_SUFFIX, 0, 0); } if (secStatus != SECSuccess) { exitErr("NSS_Init"); } SECU_RegisterDynamicOids(); if (isOCSPEnabled()) { CERT_EnableOCSPChecking(CERT_GetDefaultCertDB()); CERT_DisableOCSPDefaultResponder(CERT_GetDefaultCertDB()); if (!ocsp_fetchingFailureIsAFailure) { CERT_SetOCSPFailureMode(ocspMode_FailureIsNotAVerificationFailure); } } while (status == PL_OPT_OK) { switch(optstate->option) { default : Usage(progName); break; case 'a' : isAscii = PR_TRUE; break; case 'r' : isAscii = PR_FALSE; break; case 't' : trusted = PR_TRUE; break; case 0 : /* positional parameter */ if (usePkix < 2 && trusted) { fprintf(stderr, "Cert trust flag can be used only with" " CERT_PKIXVerifyChain(-pp) function.\n"); Usage(progName); } cert = getCert(optstate->value, isAscii, progName); if (!cert) goto punt; rememberCert(cert, trusted); if (!firstCert) firstCert = cert; trusted = PR_FALSE; } status = PL_GetNextOpt(optstate); } PL_DestroyOptState(optstate); if (status == PL_OPT_BAD || !firstCert) Usage(progName); /* Initialize log structure */ log.arena = PORT_NewArena(512); log.head = log.tail = NULL; log.count = 0; do { if (usePkix < 2) { /* NOW, verify the cert chain. */ if (usePkix) { /* Use old API with libpkix validation lib */ CERT_SetUsePKIXForValidation(PR_TRUE); } if (!time) time = PR_Now(); defaultDB = CERT_GetDefaultCertDB(); secStatus = CERT_VerifyCertificate(defaultDB, firstCert, PR_TRUE /* check sig */, certUsage, time, &pwdata, /* wincx */ &log, /* error log */ NULL);/* returned usages */ } else do { static CERTValOutParam cvout[4]; static CERTValInParam cvin[6]; SECOidTag oidTag; int inParamIndex = 0; static PRUint64 revFlagsLeaf[2]; static PRUint64 revFlagsChain[2]; static CERTRevocationFlags rev; if (oidStr) { PRArenaPool *arena; SECOidData od; memset(&od, 0, sizeof od); od.offset = SEC_OID_UNKNOWN; od.desc = "User Defined Policy OID"; od.mechanism = CKM_INVALID_MECHANISM; od.supportedExtension = INVALID_CERT_EXTENSION; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if ( !arena ) { fprintf(stderr, "out of memory"); goto punt; } secStatus = SEC_StringToOID(arena, &od.oid, oidStr, 0); if (secStatus != SECSuccess) { PORT_FreeArena(arena, PR_FALSE); fprintf(stderr, "Can not encode oid: %s(%s)\n", oidStr, SECU_Strerror(PORT_GetError())); break; } oidTag = SECOID_AddEntry(&od); PORT_FreeArena(arena, PR_FALSE); if (oidTag == SEC_OID_UNKNOWN) { fprintf(stderr, "Can not add new oid to the dynamic " "table: %s\n", oidStr); secStatus = SECFailure; break; } cvin[inParamIndex].type = cert_pi_policyOID; cvin[inParamIndex].value.arraySize = 1; cvin[inParamIndex].value.array.oids = &oidTag; inParamIndex++; } if (trustedCertList) { cvin[inParamIndex].type = cert_pi_trustAnchors; cvin[inParamIndex].value.pointer.chain = trustedCertList; inParamIndex++; } cvin[inParamIndex].type = cert_pi_useAIACertFetch; cvin[inParamIndex].value.scalar.b = certFetching; inParamIndex++; rev.leafTests.cert_rev_flags_per_method = revFlagsLeaf; rev.chainTests.cert_rev_flags_per_method = revFlagsChain; secStatus = configureRevocationParams(&rev); if (secStatus) { fprintf(stderr, "Can not config revocation parameters "); break; } cvin[inParamIndex].type = cert_pi_revocationFlags; cvin[inParamIndex].value.pointer.revocation = &rev; inParamIndex++; if (time) { cvin[inParamIndex].type = cert_pi_date; cvin[inParamIndex].value.scalar.time = time; inParamIndex++; } cvin[inParamIndex].type = cert_pi_end; cvout[0].type = cert_po_trustAnchor; cvout[0].value.pointer.cert = NULL; cvout[1].type = cert_po_certList; cvout[1].value.pointer.chain = NULL; /* setting pointer to CERTVerifyLog. Initialized structure * will be used CERT_PKIXVerifyCert */ cvout[2].type = cert_po_errorLog; cvout[2].value.pointer.log = &log; cvout[3].type = cert_po_end; secStatus = CERT_PKIXVerifyCert(firstCert, certUsage, cvin, cvout, &pwdata); if (secStatus != SECSuccess) { break; } issuerCert = cvout[0].value.pointer.cert; builtChain = cvout[1].value.pointer.chain; } while (0); /* Display validation results */ if (secStatus != SECSuccess || log.count > 0) { CERTVerifyLogNode *node = NULL; PRIntn err = PR_GetError(); fprintf(stderr, "Chain is bad, %d = %s\n", err, SECU_Strerror(err)); SECU_displayVerifyLog(stderr, &log, verbose); /* Have cert refs in the log only in case of failure. * Destroy them. */ for (node = log.head; node; node = node->next) { if (node->cert) CERT_DestroyCertificate(node->cert); } rv = 1; } else { fprintf(stderr, "Chain is good!\n"); if (issuerCert) { if (verbose > 1) { rv = SEC_PrintCertificateAndTrust(issuerCert, "Root Certificate", NULL); if (rv != SECSuccess) { SECU_PrintError(progName, "problem printing certificate"); } } else if (verbose > 0) { SECU_PrintName(stdout, &issuerCert->subject, "Root " "Certificate Subject:", 0); } CERT_DestroyCertificate(issuerCert); } if (builtChain) { CERTCertListNode *node; int count = 0; char buff[256]; if (verbose) { for(node = CERT_LIST_HEAD(builtChain); !CERT_LIST_END(node, builtChain); node = CERT_LIST_NEXT(node), count++ ) { sprintf(buff, "Certificate %d Subject", count + 1); SECU_PrintName(stdout, &node->cert->subject, buff, 0); } } CERT_DestroyCertList(builtChain); } rv = 0; } } while (--vfyCounts > 0); /* Need to destroy CERTVerifyLog arena at the end */ PORT_FreeArena(log.arena, PR_FALSE); punt: forgetCerts(); if (NSS_Shutdown() != SECSuccess) { SECU_PrintError(progName, "NSS_Shutdown"); rv = 1; } PORT_Free(progName); PORT_Free(certDir); PORT_Free(oidStr); freeRevocationMethodData(); if (pwdata.data) { PORT_Free(pwdata.data); } PR_Cleanup(); return rv; }
nsresult nsUsageArrayHelper::GetUsagesArray(const char *suffix, bool localOnly, uint32_t outArraySize, uint32_t *_verified, uint32_t *_count, PRUnichar **outUsages) { nsNSSShutDownPreventionLock locker; if (NS_FAILED(m_rv)) return m_rv; if (outArraySize < max_returned_out_array_size) return NS_ERROR_FAILURE; nsCOMPtr<nsINSSComponent> nssComponent; if (!nsNSSComponent::globalConstFlagUsePKIXVerification && localOnly) { nsresult rv; nssComponent = do_GetService(kNSSComponentCID, &rv); if (NS_FAILED(rv)) return rv; if (nssComponent) { nssComponent->SkipOcsp(); } } uint32_t &count = *_count; count = 0; SECCertificateUsage usages = 0; int err = 0; if (!nsNSSComponent::globalConstFlagUsePKIXVerification) { // CERT_VerifyCertificateNow returns SECFailure unless the certificate is // valid for all the given usages. Hoewver, we are only looking for the list // of usages for which the cert *is* valid. (void) CERT_VerifyCertificateNow(defaultcertdb, mCert, true, certificateUsageSSLClient | certificateUsageSSLServer | certificateUsageSSLServerWithStepUp | certificateUsageEmailSigner | certificateUsageEmailRecipient | certificateUsageObjectSigner | certificateUsageSSLCA | certificateUsageStatusResponder, NULL, &usages); err = PR_GetError(); } else { nsresult nsrv; nsCOMPtr<nsINSSComponent> inss = do_GetService(kNSSComponentCID, &nsrv); if (!inss) return nsrv; nsRefPtr<nsCERTValInParamWrapper> survivingParams; if (localOnly) nsrv = inss->GetDefaultCERTValInParamLocalOnly(survivingParams); else nsrv = inss->GetDefaultCERTValInParam(survivingParams); if (NS_FAILED(nsrv)) return nsrv; CERTValOutParam cvout[2]; cvout[0].type = cert_po_usages; cvout[0].value.scalar.usages = 0; cvout[1].type = cert_po_end; CERT_PKIXVerifyCert(mCert, certificateUsageCheckAllUsages, survivingParams->GetRawPointerForNSS(), cvout, NULL); err = PR_GetError(); usages = cvout[0].value.scalar.usages; } // The following list of checks must be < max_returned_out_array_size check(suffix, usages & certificateUsageSSLClient, count, outUsages); check(suffix, usages & certificateUsageSSLServer, count, outUsages); check(suffix, usages & certificateUsageSSLServerWithStepUp, count, outUsages); check(suffix, usages & certificateUsageEmailSigner, count, outUsages); check(suffix, usages & certificateUsageEmailRecipient, count, outUsages); check(suffix, usages & certificateUsageObjectSigner, count, outUsages); #if 0 check(suffix, usages & certificateUsageProtectedObjectSigner, count, outUsages); check(suffix, usages & certificateUsageUserCertImport, count, outUsages); #endif check(suffix, usages & certificateUsageSSLCA, count, outUsages); #if 0 check(suffix, usages & certificateUsageVerifyCA, count, outUsages); #endif check(suffix, usages & certificateUsageStatusResponder, count, outUsages); #if 0 check(suffix, usages & certificateUsageAnyCA, count, outUsages); #endif if (!nsNSSComponent::globalConstFlagUsePKIXVerification && localOnly && nssComponent) { nssComponent->SkipOcspOff(); } if (count == 0) { verifyFailed(_verified, err); } else { *_verified = nsNSSCertificate::VERIFIED_OK; } return NS_OK; }
NS_IMETHODIMP nsNSSCertificate::VerifyForUsage(uint32_t usage, uint32_t *verificationResult) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(verificationResult); nsresult nsrv; nsCOMPtr<nsINSSComponent> inss = do_GetService(kNSSComponentCID, &nsrv); if (!inss) return nsrv; nsRefPtr<nsCERTValInParamWrapper> survivingParams; nsrv = inss->GetDefaultCERTValInParam(survivingParams); if (NS_FAILED(nsrv)) return nsrv; SECCertificateUsage nss_usage; switch (usage) { case CERT_USAGE_SSLClient: nss_usage = certificateUsageSSLClient; break; case CERT_USAGE_SSLServer: nss_usage = certificateUsageSSLServer; break; case CERT_USAGE_SSLServerWithStepUp: nss_usage = certificateUsageSSLServerWithStepUp; break; case CERT_USAGE_SSLCA: nss_usage = certificateUsageSSLCA; break; case CERT_USAGE_EmailSigner: nss_usage = certificateUsageEmailSigner; break; case CERT_USAGE_EmailRecipient: nss_usage = certificateUsageEmailRecipient; break; case CERT_USAGE_ObjectSigner: nss_usage = certificateUsageObjectSigner; break; case CERT_USAGE_UserCertImport: nss_usage = certificateUsageUserCertImport; break; case CERT_USAGE_VerifyCA: nss_usage = certificateUsageVerifyCA; break; case CERT_USAGE_ProtectedObjectSigner: nss_usage = certificateUsageProtectedObjectSigner; break; case CERT_USAGE_StatusResponder: nss_usage = certificateUsageStatusResponder; break; case CERT_USAGE_AnyCA: nss_usage = certificateUsageAnyCA; break; default: return NS_ERROR_FAILURE; } SECStatus verify_result; if (!nsNSSComponent::globalConstFlagUsePKIXVerification) { CERTCertDBHandle *defaultcertdb = CERT_GetDefaultCertDB(); verify_result = CERT_VerifyCertificateNow(defaultcertdb, mCert, true, nss_usage, NULL, NULL); } else { CERTValOutParam cvout[1]; cvout[0].type = cert_po_end; verify_result = CERT_PKIXVerifyCert(mCert, nss_usage, survivingParams->GetRawPointerForNSS(), cvout, NULL); } if (verify_result == SECSuccess) { *verificationResult = VERIFIED_OK; } else { int err = PR_GetError(); // this list was cloned from verifyFailed switch (err) { case SEC_ERROR_INADEQUATE_KEY_USAGE: case SEC_ERROR_INADEQUATE_CERT_TYPE: *verificationResult = USAGE_NOT_ALLOWED; break; case SEC_ERROR_REVOKED_CERTIFICATE: *verificationResult = CERT_REVOKED; break; case SEC_ERROR_EXPIRED_CERTIFICATE: *verificationResult = CERT_EXPIRED; break; case SEC_ERROR_UNTRUSTED_CERT: *verificationResult = CERT_NOT_TRUSTED; break; case SEC_ERROR_UNTRUSTED_ISSUER: *verificationResult = ISSUER_NOT_TRUSTED; break; case SEC_ERROR_UNKNOWN_ISSUER: *verificationResult = ISSUER_UNKNOWN; break; case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: *verificationResult = SIGNATURE_ALGORITHM_DISABLED; break; case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: *verificationResult = INVALID_CA; break; case SEC_ERROR_CERT_USAGES_INVALID: default: *verificationResult = NOT_VERIFIED_UNKNOWN; break; } } return NS_OK; }
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 }
int main(int argc, char *argv[]) { int opt; long fin = 0; int use_pkix = 0; SECStatus rv; char pbuf[1024]; PRBool crlcheck = PR_FALSE; PRBool ocspcheck = PR_FALSE; PRBool strict = PR_FALSE; CERTCertDBHandle *handle = NULL; CERTCertificate **certout = NULL; CERTVerifyLog vfy_log; CERTVerifyLog vfy_log2; CERTVerifyLog *cur_log; CERTValOutParam *pkixout = NULL; SECItem c1; SECItem c2; SECItem *certs[2]; certs[0] = &c1; certs[1] = &c2; int numcerts = 0; while ((opt = getopt(argc, argv, "u:d:e:pn:s:coSr")) != -1) { switch(opt) { /* usage type */ case 'u': set_usage(optarg); break; case 'd': db_dir = optarg; break; case 's': sub_file = optarg; break; case 'c': crlcheck = PR_TRUE; break; case 'o': ocspcheck = PR_TRUE; break; case 'S': strict = PR_TRUE; break; case 'e': end_file = optarg; break; case 'p': use_pkix = 1; break; case 'n': rightca_nick = optarg; break; case 'r': retry_verify = PR_TRUE; break; default: print_usage(); break; } } if (db_dir == NULL) db_dir = "testfiles/"; if (end_file == NULL) end_file = "testfiles/end.pem"; get_file(certs[numcerts++], end_file); if (sub_file != NULL) { get_file(certs[numcerts++], sub_file); } snprintf(pbuf, sizeof(pbuf), "sql:%s", db_dir); if (NSS_Initialize(pbuf, "", "", "secmod.db", 0x1) != SECSuccess) { printf("NSS_Initialize failed %d\n", PORT_GetError()); exit(-1); } if ((handle = CERT_GetDefaultCertDB()) == NULL) { printf("NULL handle\n"); exit(-1); } if (ocspcheck) { CERT_EnableOCSPChecking(handle); CERT_DisableOCSPDefaultResponder(handle); if (strict) CERT_SetOCSPFailureMode(ocspMode_FailureIsNotAVerificationFailure); } rv = CERT_ImportCerts(handle, 0, numcerts, certs, &certout, PR_FALSE, PR_FALSE, NULL); if (rv != SECSuccess) { printf("CERT_ImportCerts failed %d\n", PORT_GetError()); exit(-1); } vfy_log.count = 0; vfy_log.head = NULL; vfy_log.tail = NULL; vfy_log.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); vfy_log2.count = 0; vfy_log2.head = NULL; vfy_log2.tail = NULL; vfy_log2.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (use_pkix) { int in_idx = 0; CERTValInParam cvin[7]; CERTValOutParam cvout[3]; CERTCertList *trustcl = NULL; CERTRevocationFlags rev; PRUint64 revFlagsLeaf[2] = { 0, 0 }; PRUint64 revFlagsChain[2] = { 0, 0 }; zero(&cvin); /* ??? is this reasonable? */ zero(&cvout); /* ??? is this reasonable? */ zero(&rev); /* ??? is this reasonable? */ if (rightca_nick == NULL) rightca_nick = "root"; if ((trustcl = get_trust_certlist(handle, rightca_nick)) == NULL) { printf("Couldn't find trust anchor\n"); exit(-1); } cvin[in_idx].type = cert_pi_useAIACertFetch; cvin[in_idx++].value.scalar.b = PR_TRUE; cvin[in_idx].type = cert_pi_revocationFlags; cvin[in_idx++].value.pointer.revocation = &rev; cvin[in_idx].type = cert_pi_trustAnchors; cvin[in_idx++].value.pointer.chain = trustcl; cvin[in_idx].type = cert_pi_useOnlyTrustAnchors; cvin[in_idx++].value.scalar.b = PR_TRUE; set_rev_per_meth(&rev, revFlagsLeaf, revFlagsChain); set_rev_params(&rev, crlcheck, ocspcheck, strict); cvin[in_idx].type = cert_pi_end; cvout[0].type = cert_po_errorLog; cvout[0].value.pointer.log = &vfy_log; cur_log = &vfy_log; cvout[1].type = cert_po_certList; cvout[1].value.pointer.chain = NULL; cvout[2].type = cert_po_end; pkixout = &cvout[0]; pkixredo: rv = CERT_PKIXVerifyCert(*certout, pkixusage, cvin, cvout, NULL); //CERT_DestroyCertList(trustcl); } else { cur_log = &vfy_log; vfyredo: rv = CERT_VerifyCert(handle, *certout, PR_TRUE, usage, PR_Now(), NULL, cur_log); } if (rv != SECSuccess || cur_log->count > 0) { if (cur_log->count > 0 && cur_log->head != NULL) { fin = err_stat(cur_log->head); } else { fin = PORT_GetError(); } if (fin == SEC_ERROR_INADEQUATE_KEY_USAGE) { printf("SEC_ERROR_INADEQUATE_KEY_USAGE : Certificate key usage inadequate for attempted operation.\n" ); } else if (fin == SEC_ERROR_INADEQUATE_CERT_TYPE) { printf("SEC_ERROR_INADEQUATE_CERT_TYPE : Certificate type not approved for application.\n" ); } else { printf("OTHER : %ld", fin); } } if ((fin == SEC_ERROR_INADEQUATE_CERT_TYPE || fin == SEC_ERROR_INADEQUATE_KEY_USAGE) && retry_verify && !retried) { printf("Retrying verification\n"); fin = 0; retried = PR_TRUE; if (use_pkix) { pkixout[0].value.pointer.log = &vfy_log2; cur_log = &vfy_log2; pkixout[1].value.pointer.chain = NULL; if (pkixusage == certificateUsageSSLClient) { pkixusage = certificateUsageSSLServer; } else { pkixusage = certificateUsageSSLClient; } goto pkixredo; } else { if (usage == certUsageSSLClient) { usage = certUsageSSLServer; } else { usage = certUsageSSLClient; } goto vfyredo; } } PORT_FreeArena(vfy_log.arena, PR_FALSE); PORT_FreeArena(vfy_log2.arena, PR_FALSE); NSS_Shutdown(); exit(fin == 0 ? 0 : 1); }