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, /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus, /*optional out*/ KeySizeStatus* keySizeStatus) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of VerifyCert\n")); PR_ASSERT(cert); PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV)); PR_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus); if (builtChain) { *builtChain = nullptr; } if (evOidPolicy) { *evOidPolicy = SEC_OID_UNKNOWN; } if (ocspStaplingStatus) { if (usage != certificateUsageSSLServer) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } *ocspStaplingStatus = OCSP_STAPLING_NEVER_CHECKED; } if (keySizeStatus) { if (usage != certificateUsageSSLServer) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } *keySizeStatus = KeySizeStatus::NeverChecked; } 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; } NSSCertDBTrustDomain::OCSPFetching ocspFetching = !mOCSPDownloadEnabled || (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP : !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail : NSSCertDBTrustDomain::FetchOCSPForDVHardFail; OcspGetConfig ocspGETConfig = mOCSPGETEnabled ? ocspGetEnabled : ocspGetDisabled; 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, pinningDisabled, MINIMUM_NON_ECC_BITS_WEAK, 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, mPinningMode, MINIMUM_NON_ECC_BITS, hostname, builtChain); rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time, KeyUsage::digitalSignature,// (EC)DHE KeyUsage::keyEncipherment, // RSA KeyUsage::keyAgreement, // (EC)DH KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse, ocspStaplingStatus); 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, mPinningMode, MINIMUM_NON_ECC_BITS, hostname, builtChain); rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time, KeyUsage::digitalSignature, // (EC)DHE KeyUsage::keyEncipherment, // RSA KeyUsage::keyAgreement, // (EC)DH KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, stapledOCSPResponse, ocspStaplingStatus); if (rv == Success) { if (keySizeStatus) { *keySizeStatus = KeySizeStatus::LargeMinimumSucceeded; } break; } // If that failed, try again with a smaller minimum key size. NSSCertDBTrustDomain trustDomainWeak(trustSSL, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, mPinningMode, MINIMUM_NON_ECC_BITS_WEAK, hostname, builtChain); rv = BuildCertChainForOneKeyUsage(trustDomainWeak, certDER, time, KeyUsage::digitalSignature, // (EC)DHE KeyUsage::keyEncipherment, // RSA KeyUsage::keyAgreement, // (EC)DH KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, stapledOCSPResponse, ocspStaplingStatus); if (keySizeStatus) { if (rv == Success) { *keySizeStatus = KeySizeStatus::CompatibilityRisk; } else { *keySizeStatus = KeySizeStatus::AlreadyBad; } } break; } case certificateUsageSSLCA: { NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, pinningDisabled, MINIMUM_NON_ECC_BITS_WEAK, 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, pinningDisabled, MINIMUM_NON_ECC_BITS_WEAK, nullptr, builtChain); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection, CertPolicyId::anyPolicy, stapledOCSPResponse); if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::nonRepudiation, 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, pinningDisabled, MINIMUM_NON_ECC_BITS_WEAK, 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, pinningDisabled, MINIMUM_NON_ECC_BITS_WEAK, 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, pinningDisabled, MINIMUM_NON_ECC_BITS_WEAK, 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, pinningDisabled, MINIMUM_NON_ECC_BITS_WEAK, 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, pinningDisabled, MINIMUM_NON_ECC_BITS_WEAK, 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 && usage == certificateUsageSSLServer) { 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 Result pinningResult = CertListContainsExpectedKeys(certList, hostname, time, mPinningMode); if (pinningResult != Success) { rv = pinningResult; } } PR_SetError(MapResultToPRErrorCode(rv), 0); return SECFailure; } return SECSuccess; }
SECStatus CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, Time time, void* pinArg, const char* hostname, /*out*/ ScopedCERTCertList& builtChain, /*optional*/ const Flags flags, /*optional*/ const SECItem* stapledOCSPResponseSECItem, /*optional out*/ SECOidTag* evOidPolicy, /*optional out*/ OCSPStaplingStatus* ocspStaplingStatus, /*optional out*/ KeySizeStatus* keySizeStatus, /*optional out*/ SHA1ModeResult* sha1ModeResult, /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo) { MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n")); PR_ASSERT(cert); PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV)); PR_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus); PR_ASSERT(usage == certificateUsageSSLServer || !sha1ModeResult); if (evOidPolicy) { *evOidPolicy = SEC_OID_UNKNOWN; } if (ocspStaplingStatus) { if (usage != certificateUsageSSLServer) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } *ocspStaplingStatus = OCSP_STAPLING_NEVER_CHECKED; } if (keySizeStatus) { if (usage != certificateUsageSSLServer) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } *keySizeStatus = KeySizeStatus::NeverChecked; } if (sha1ModeResult) { if (usage != certificateUsageSSLServer) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } *sha1ModeResult = SHA1ModeResult::NeverChecked; } 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; } // We configure the OCSP fetching modes separately for EV and non-EV // verifications. NSSCertDBTrustDomain::OCSPFetching defaultOCSPFetching = (mOCSPDownloadConfig == ocspOff) || (mOCSPDownloadConfig == ocspEVOnly) || (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP : !mOCSPStrict ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail : NSSCertDBTrustDomain::FetchOCSPForDVHardFail; OcspGetConfig ocspGETConfig = mOCSPGETEnabled ? ocspGetEnabled : ocspGetDisabled; 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, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, SHA1Mode::Allowed, builtChain, nullptr, nullptr); 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. // These configurations are in order of most restrictive to least // restrictive. This enables us to gather telemetry on the expected // results of setting the default policy to a particular configuration. SHA1Mode sha1ModeConfigurations[] = { SHA1Mode::Forbidden, SHA1Mode::Before2016, SHA1Mode::ImportedRoot, SHA1Mode::Allowed, }; SHA1ModeResult sha1ModeResults[] = { SHA1ModeResult::SucceededWithoutSHA1, SHA1ModeResult::SucceededWithSHA1Before2016, SHA1ModeResult::SucceededWithImportedRoot, SHA1ModeResult::SucceededWithSHA1, }; size_t sha1ModeConfigurationsCount = MOZ_ARRAY_LENGTH(sha1ModeConfigurations); static_assert(MOZ_ARRAY_LENGTH(sha1ModeConfigurations) == MOZ_ARRAY_LENGTH(sha1ModeResults), "digestAlgorithm array lengths differ"); rv = Result::ERROR_UNKNOWN_ERROR; #ifndef MOZ_NO_EV_CERTS // Try to validate for EV first. NSSCertDBTrustDomain::OCSPFetching evOCSPFetching = (mOCSPDownloadConfig == ocspOff) || (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV : NSSCertDBTrustDomain::FetchOCSPForEV; CertPolicyId evPolicy; SECOidTag evPolicyOidTag; SECStatus srv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag); for (size_t i = 0; i < sha1ModeConfigurationsCount && rv != Success && srv == SECSuccess; i++) { // Don't attempt verification if the SHA1 mode set by preferences // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on. // (To put it another way, only attempt verification if the SHA1 mode // option we're on is as restrictive or more restrictive than // mSHA1Mode.) This allows us to gather telemetry information while // still enforcing the mode set by preferences. if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[i])) { continue; } // Because of the try-strict and fallback approach, we have to clear any // previously noted telemetry information if (pinningTelemetryInfo) { pinningTelemetryInfo->Reset(); } NSSCertDBTrustDomain trustDomain(trustSSL, evOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, mPinningMode, MIN_RSA_BITS, ValidityCheckingMode::CheckForEV, sha1ModeConfigurations[i], builtChain, pinningTelemetryInfo, hostname); rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time, KeyUsage::digitalSignature,// (EC)DHE KeyUsage::keyEncipherment, // RSA KeyUsage::keyAgreement, // (EC)DH KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse, ocspStaplingStatus); // If we succeeded with the SHA1Mode of only allowing imported roots to // issue SHA1 certificates after 2015, if the chain we built doesn't // terminate with an imported root, we must reject it. (This only works // because we try SHA1 configurations in order of decreasing // strictness.) // Note that if there existed a certificate chain with a built-in root // that had SHA1 certificates issued before 2016, it would have already // been accepted. If such a chain had SHA1 certificates issued after // 2015, it will only be accepted in the SHA1Mode::Allowed case. if (rv == Success && sha1ModeConfigurations[i] == SHA1Mode::ImportedRoot) { bool isBuiltInRoot = false; rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot); if (rv != Success) { break; } if (isBuiltInRoot) { rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; } } if (rv == Success) { MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("cert is EV with status %i\n", sha1ModeResults[i])); if (evOidPolicy) { *evOidPolicy = evPolicyOidTag; } if (sha1ModeResult) { *sha1ModeResult = sha1ModeResults[i]; } } } if (rv == Success) { break; } #endif if (flags & FLAG_MUST_BE_EV) { rv = Result::ERROR_POLICY_VALIDATION_FAILED; break; } // Now try non-EV. unsigned int keySizeOptions[] = { MIN_RSA_BITS, MIN_RSA_BITS_WEAK }; KeySizeStatus keySizeStatuses[] = { KeySizeStatus::LargeMinimumSucceeded, KeySizeStatus::CompatibilityRisk }; static_assert(MOZ_ARRAY_LENGTH(keySizeOptions) == MOZ_ARRAY_LENGTH(keySizeStatuses), "keySize array lengths differ"); size_t keySizeOptionsCount = MOZ_ARRAY_LENGTH(keySizeStatuses); for (size_t i = 0; i < keySizeOptionsCount && rv != Success; i++) { for (size_t j = 0; j < sha1ModeConfigurationsCount && rv != Success; j++) { // Don't attempt verification if the SHA1 mode set by preferences // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on. // (To put it another way, only attempt verification if the SHA1 mode // option we're on is as restrictive or more restrictive than // mSHA1Mode.) This allows us to gather telemetry information while // still enforcing the mode set by preferences. if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[j])) { continue; } // invalidate any telemetry info relating to failed chains if (pinningTelemetryInfo) { pinningTelemetryInfo->Reset(); } NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, mPinningMode, keySizeOptions[i], ValidityCheckingMode::CheckingOff, sha1ModeConfigurations[j], builtChain, pinningTelemetryInfo, hostname); rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time, KeyUsage::digitalSignature,//(EC)DHE KeyUsage::keyEncipherment,//RSA KeyUsage::keyAgreement,//(EC)DH KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, stapledOCSPResponse, ocspStaplingStatus); // If we succeeded with the SHA1Mode of only allowing imported roots // to issue SHA1 certificates after 2015, if the chain we built // doesn't terminate with an imported root, we must reject it. (This // only works because we try SHA1 configurations in order of // decreasing strictness.) // Note that if there existed a certificate chain with a built-in root // that had SHA1 certificates issued before 2016, it would have // already been accepted. If such a chain had SHA1 certificates issued // after 2015, it will only be accepted in the SHA1Mode::Allowed case. if (rv == Success && sha1ModeConfigurations[j] == SHA1Mode::ImportedRoot) { bool isBuiltInRoot = false; rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot); if (rv != Success) { break; } if (isBuiltInRoot) { rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; } } if (rv == Success) { if (keySizeStatus) { *keySizeStatus = keySizeStatuses[i]; } if (sha1ModeResult) { *sha1ModeResult = sha1ModeResults[j]; } } } } if (rv == Success) { break; } if (keySizeStatus) { *keySizeStatus = KeySizeStatus::AlreadyBad; } // Only collect CERT_CHAIN_SHA1_POLICY_STATUS telemetry indicating a // failure when mSHA1Mode is the default. // NB: When we change the default, we have to change this. if (sha1ModeResult && mSHA1Mode == SHA1Mode::ImportedRoot) { *sha1ModeResult = SHA1ModeResult::Failed; } break; } case certificateUsageSSLCA: { NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, mSHA1Mode, builtChain, nullptr, nullptr); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, stapledOCSPResponse); break; } case certificateUsageEmailSigner: { NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, SHA1Mode::Allowed, builtChain, nullptr, nullptr); rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection, CertPolicyId::anyPolicy, stapledOCSPResponse); if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) { rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::nonRepudiation, 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, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, SHA1Mode::Allowed, builtChain, nullptr, nullptr); 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, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, SHA1Mode::Allowed, builtChain, nullptr, nullptr); 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, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, SHA1Mode::Allowed, builtChain, nullptr, nullptr); rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); if (rv == Result::ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain emailTrust(trustEmail, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, SHA1Mode::Allowed, builtChain, nullptr, nullptr); rv = BuildCertChain(emailTrust, certDER, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); if (rv == Result::ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning, defaultOCSPFetching, mOCSPCache, pinArg, ocspGETConfig, mCertShortLifetimeInDays, pinningDisabled, MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff, SHA1Mode::Allowed, builtChain, nullptr, nullptr); rv = BuildCertChain(objectSigningTrust, certDER, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); } } break; } default: rv = Result::FATAL_ERROR_INVALID_ARGS; } if (rv != Success) { PR_SetError(MapResultToPRErrorCode(rv), 0); return SECFailure; } return SECSuccess; }
SECStatus CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage, PRTime time, void* pinArg, const char* hostname, const Flags flags, /*optional*/ const SECItem* stapledOCSPResponse, /*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; } 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; SECStatus rv; 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, cert->derCert, 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; rv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag); if (rv == SECSuccess) { NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV : NSSCertDBTrustDomain::FetchOCSPForEV, mOCSPCache, pinArg, ocspGETConfig, &callbackContainer, builtChain); rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time, KeyUsage::digitalSignature,// (EC)DHE KeyUsage::keyEncipherment, // RSA KeyUsage::keyAgreement, // (EC)DH KeyPurposeId::id_kp_serverAuth, evPolicy, stapledOCSPResponse); if (rv == SECSuccess) { if (evOidPolicy) { *evOidPolicy = evPolicyOidTag; } break; } } #endif if (flags & FLAG_MUST_BE_EV) { PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0); rv = SECFailure; break; } // Now try non-EV. NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, &callbackContainer, builtChain); rv = BuildCertChainForOneKeyUsage(trustDomain, cert, 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, cert->derCert, 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, cert->derCert, 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, cert->derCert, time, EndEntityOrCA::MustBeEndEntity, KeyUsage::keyEncipherment, // RSA KeyPurposeId::id_kp_emailProtection, CertPolicyId::anyPolicy, stapledOCSPResponse); if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) { rv = BuildCertChain(trustDomain, cert->derCert, 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, cert->derCert, 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 implementation of window.crypto.importCertificates and in 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, cert->derCert, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(emailTrust, cert->derCert, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning, ocspFetching, mOCSPCache, pinArg, ocspGETConfig, nullptr, builtChain); rv = BuildCertChain(objectSigningTrust, cert->derCert, time, endEntityOrCA, keyUsage, eku, CertPolicyId::anyPolicy, stapledOCSPResponse); } } break; } default: PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } return rv; }