author | Brian Smith <brian@briansmith.org> |
Sun, 16 Feb 2014 17:35:40 -0800 | |
changeset 169459 | 302def56019a278411ed9d71e3de7126d1729811 |
parent 169458 | 853227869c6b0c60dc2ac4f1808ca4ff4fe4ce8b |
child 169460 | b8fd613d94d560daeab160b6e8efbbb33668a971 |
push id | 26251 |
push user | cbook@mozilla.com |
push date | Wed, 19 Feb 2014 12:59:09 +0000 |
treeherder | mozilla-central@a6f7ad1e6090 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | keeler, cviecco |
bugs | 915931 |
milestone | 30.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/security/apps/AppSignatureVerification.cpp +++ b/security/apps/AppSignatureVerification.cpp @@ -600,17 +600,17 @@ VerifySignature(AppTrustedRoot trustedRo // Verify certificate. AppTrustDomain trustDomain(nullptr); // TODO: null pinArg if (trustDomain.SetTrustedRoot(trustedRoot) != SECSuccess) { return MapSECStatus(SECFailure); } if (BuildCertChain(trustDomain, signerCert, PR_Now(), MustBeEndEntity, KU_DIGITAL_SIGNATURE, SEC_OID_EXT_KEY_USAGE_CODE_SIGN, - builtChain) != SECSuccess) { + nullptr, builtChain) != SECSuccess) { return MapSECStatus(SECFailure); } // 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;
--- a/security/apps/AppTrustDomain.cpp +++ b/security/apps/AppTrustDomain.cpp @@ -173,9 +173,21 @@ AppTrustDomain::GetCertTrust(EndEntityOr SECStatus AppTrustDomain::VerifySignedData(const CERTSignedData* signedData, const CERTCertificate* cert) { return ::insanity::pkix::VerifySignedData(signedData, cert, mPinArg); } +SECStatus +AppTrustDomain::CheckRevocation(EndEntityOrCA, + const CERTCertificate*, + /*const*/ CERTCertificate*, + PRTime time, + /*optional*/ const SECItem*) +{ + // We don't currently do revocation checking. If we need to distrust an Apps + // certificate, we will use the active distrust mechanism. + return SECSuccess; +} + } }
--- a/security/apps/AppTrustDomain.h +++ b/security/apps/AppTrustDomain.h @@ -24,17 +24,21 @@ public: const CERTCertificate* candidateCert, /*out*/ TrustLevel* trustLevel) MOZ_OVERRIDE; SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName, PRTime time, /*out*/ insanity::pkix::ScopedCERTCertList& results) MOZ_OVERRIDE; SECStatus VerifySignedData(const CERTSignedData* signedData, const CERTCertificate* cert) MOZ_OVERRIDE; - + SECStatus CheckRevocation(insanity::pkix::EndEntityOrCA endEntityOrCA, + const CERTCertificate* cert, + /*const*/ CERTCertificate* issuerCertToDup, + PRTime time, + /*optional*/ const SECItem* stapledOCSPresponse); private: void* mPinArg; // non-owning! insanity::pkix::ScopedCERTCertificate mTrustedRoot; }; } } // namespace mozilla::psm #endif // mozilla_psm_AppsTrustDomain_h
--- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -18,17 +18,17 @@ #include "sslerr.h" // ScopedXXX in this file are insanity::pkix::ScopedXXX, not // mozilla::ScopedXXX. using namespace insanity::pkix; using namespace mozilla::psm; #ifdef PR_LOGGING -static PRLogModuleInfo* gCertVerifierLog = nullptr; +PRLogModuleInfo* gCertVerifierLog = nullptr; #endif namespace mozilla { namespace psm { const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1; const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2; CertVerifier::CertVerifier(implementation_config ic, @@ -189,47 +189,48 @@ destroyCertListThatShouldNotExist(CERTCe } } #endif static SECStatus BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert, PRTime time, KeyUsages ku1, KeyUsages ku2, KeyUsages ku3, SECOidTag eku, + const SECItem* stapledOCSPResponse, ScopedCERTCertList& builtChain) { PR_ASSERT(ku1); PR_ASSERT(ku2); SECStatus rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity, - ku1, eku, builtChain); + ku1, eku, stapledOCSPResponse, builtChain); if (rv != SECSuccess && ku2 && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) { rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity, - ku2, eku, builtChain); + ku2, eku, stapledOCSPResponse, builtChain); if (rv != SECSuccess && ku3 && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) { rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity, - ku3, eku, builtChain); + ku3, eku, stapledOCSPResponse, builtChain); if (rv != SECSuccess) { PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0); } } } return rv; } SECStatus CertVerifier::InsanityVerifyCert( CERTCertificate* cert, - /*optional*/ const SECItem* /*TODO: stapledOCSPResponse*/, const SECCertificateUsage usage, const PRTime time, void* pinArg, const Flags flags, + /*optional*/ const SECItem* stapledOCSPResponse, /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of InsanityVerifyCert\n")); SECStatus rv; // TODO(bug 970750): anyExtendedKeyUsage // TODO: encipherOnly/decipherOnly // S/MIME Key Usage: http://tools.ietf.org/html/rfc3850#section-4.4.2 @@ -243,78 +244,78 @@ CertVerifier::InsanityVerifyCert( 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, mOCSPDownloadEnabled, mOCSPStrict, pinArg); rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity, KU_DIGITAL_SIGNATURE, SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH, - builtChain); + stapledOCSPResponse, builtChain); 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. NSSCertDBTrustDomain trustDomain(trustSSL, mOCSPDownloadEnabled, mOCSPStrict, pinArg); rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time, KU_DIGITAL_SIGNATURE, // ECDHE/DHE KU_KEY_ENCIPHERMENT, // RSA KU_KEY_AGREEMENT, // ECDH/DH SEC_OID_EXT_KEY_USAGE_SERVER_AUTH, - builtChain); + stapledOCSPResponse, builtChain); break; } case certificateUsageSSLCA: { NSSCertDBTrustDomain trustDomain(trustSSL, mOCSPDownloadEnabled, mOCSPStrict, pinArg); rv = BuildCertChain(trustDomain, cert, time, MustBeCA, KU_KEY_CERT_SIGN, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH, - builtChain); + stapledOCSPResponse, builtChain); break; } case certificateUsageEmailSigner: { NSSCertDBTrustDomain trustDomain(trustEmail, mOCSPDownloadEnabled, mOCSPStrict, pinArg); rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity, KU_DIGITAL_SIGNATURE, SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT, - builtChain); + stapledOCSPResponse, builtChain); 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, mOCSPDownloadEnabled, mOCSPStrict, pinArg); rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time, KU_KEY_ENCIPHERMENT, // RSA KU_KEY_AGREEMENT, // ECDH/DH 0, SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT, - builtChain); + stapledOCSPResponse, builtChain); break; } case certificateUsageObjectSigner: { NSSCertDBTrustDomain trustDomain(trustObjectSigning, mOCSPDownloadEnabled, mOCSPStrict, pinArg); rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity, KU_DIGITAL_SIGNATURE, SEC_OID_EXT_KEY_USAGE_CODE_SIGN, - builtChain); + stapledOCSPResponse, builtChain); 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 @@ -330,28 +331,28 @@ CertVerifier::InsanityVerifyCert( endEntityOrCA = MustBeEndEntity; keyUsage = KU_DIGITAL_SIGNATURE; eku = SEC_OID_OCSP_RESPONDER; } NSSCertDBTrustDomain sslTrust(trustSSL, mOCSPDownloadEnabled, mOCSPStrict, pinArg); rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA, - keyUsage, eku, builtChain); + keyUsage, eku, stapledOCSPResponse, builtChain); if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain emailTrust(trustEmail, mOCSPDownloadEnabled, mOCSPStrict, pinArg); rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage, - eku, builtChain); + eku, stapledOCSPResponse, builtChain); if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) { NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning, mOCSPDownloadEnabled, mOCSPStrict, pinArg); rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA, - keyUsage, eku, builtChain); + keyUsage, eku, stapledOCSPResponse, builtChain); } } break; } default: PR_SetError(SEC_ERROR_INVALID_ARGS, 0); @@ -595,18 +596,18 @@ CertVerifier::VerifyCert(CERTCertificate PR_SetError(SEC_ERROR_INVALID_ARGS, 0); #else PR_SetError(PR_INVALID_STATE_ERROR, 0); #endif return SECFailure; } if (mImplementation == insanity) { - return InsanityVerifyCert(cert, stapledOCSPResponse, usage, time, - pinArg, flags, validationChain); + return InsanityVerifyCert(cert, usage, time, pinArg, flags, + stapledOCSPResponse, validationChain); } if (mImplementation == classic) { // 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); }
--- a/security/certverifier/CertVerifier.h +++ b/security/certverifier/CertVerifier.h @@ -73,20 +73,20 @@ public: const bool mCRLDownloadEnabled; #endif const bool mOCSPDownloadEnabled; const bool mOCSPStrict; const bool mOCSPGETEnabled; private: SECStatus InsanityVerifyCert(CERTCertificate* cert, - /*optional*/ const SECItem* stapledOCSPResponse, const SECCertificateUsage usage, const PRTime time, void* pinArg, const Flags flags, + /*optional*/ const SECItem* stapledOCSPResponse, /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain); }; void InitCertVerifierLog(); } } // namespace mozilla::psm #endif // mozilla_psm__CertVerifier_h
--- a/security/certverifier/NSSCertDBTrustDomain.cpp +++ b/security/certverifier/NSSCertDBTrustDomain.cpp @@ -33,22 +33,22 @@ namespace { inline void PORT_Free_string(char* str) { PORT_Free(str); } typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule; } // unnamed namespace NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType, - bool /*ocspDownloadEnabled*/, - bool /*ocspStrict*/, + bool ocspDownloadEnabled, + bool ocspStrict, void* pinArg) : mCertDBTrustType(certDBTrustType) -// , mOCSPDownloadEnabled(ocspDownloadEnabled) -// , mOCSPStrict(ocspStrict) + , mOCSPDownloadEnabled(ocspDownloadEnabled) + , mOCSPStrict(ocspStrict) , mPinArg(pinArg) { } SECStatus NSSCertDBTrustDomain::FindPotentialIssuers( const SECItem* encodedIssuerName, PRTime time, /*out*/ insanity::pkix::ScopedCERTCertList& results) @@ -119,16 +119,128 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn SECStatus NSSCertDBTrustDomain::VerifySignedData(const CERTSignedData* signedData, const CERTCertificate* cert) { return ::insanity::pkix::VerifySignedData(signedData, cert, mPinArg); } +SECStatus +NSSCertDBTrustDomain::CheckRevocation( + insanity::pkix::EndEntityOrCA endEntityOrCA, + const CERTCertificate* cert, + /*const*/ CERTCertificate* issuerCert, + PRTime time, + /*optional*/ const SECItem* stapledOCSPResponse) +{ + // Actively distrusted certificates will have already been blocked by + // GetCertTrust. + + // TODO: need to verify that IsRevoked isn't called for trust anchors AND + // that that fact is documented in insanity. + + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, + ("NSSCertDBTrustDomain: Top of CheckRevocation\n")); + + PORT_Assert(cert); + PORT_Assert(issuerCert); + if (!cert || !issuerCert) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + // If we have a stapled OCSP response then the verification of that response + // determines the result unless the OCSP response is expired. We make an + // exception for expired responses because some servers, nginx in particular, + // are known to serve expired responses due to bugs. + if (stapledOCSPResponse) { + PR_ASSERT(endEntityOrCA == MustBeEndEntity); + SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time, + stapledOCSPResponse); + if (rv == SECSuccess) { + return rv; + } + if (PR_GetError() != SEC_ERROR_OCSP_OLD_RESPONSE) { + return rv; + } + } + + // TODO(bug 921885): We need to change this when we add EV support. + + // TODO: when !mOCSPDownloadEnabled, we still need to handle the fallback for + // expired responses. But, if/when we disable OCSP fetching by default, it + // would be ambiguous whether !mOCSPDownloadEnabled means "I want the default" + // or "I really never want you to ever fetch OCSP." + if (mOCSPDownloadEnabled) { + // We don't do OCSP fetching for intermediates. + if (endEntityOrCA == MustBeCA) { + PR_ASSERT(!stapledOCSPResponse); + return SECSuccess; + } + + ScopedPtr<char, PORT_Free_string> + url(CERT_GetOCSPAuthorityInfoAccessLocation(cert)); + + // Nothing to do if we don't have an OCSP responder URI for the cert; just + // assume it is good. Note that this is the confusing, but intended, + // interpretation of "strict" revocation checking in the face of a + // certificate that lacks an OCSP responder URI. + if (!url) { + if (stapledOCSPResponse) { + PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0); + return SECFailure; + } + return SECSuccess; + } + + ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + if (!arena) { + return SECFailure; + } + + const SECItem* request + = CreateEncodedOCSPRequest(arena.get(), cert, issuerCert); + if (!request) { + return SECFailure; + } + + const SECItem* response(CERT_PostOCSPRequest(arena.get(), url.get(), + request)); + if (!response) { + if (mOCSPStrict) { + return SECFailure; + } + // Soft fail -> success :( + } else { + SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time, + response); + if (rv == SECSuccess) { + return SECSuccess; + } + PRErrorCode error = PR_GetError(); + switch (error) { + case SEC_ERROR_OCSP_UNKNOWN_CERT: + case SEC_ERROR_REVOKED_CERTIFICATE: + return SECFailure; + default: + if (mOCSPStrict) { + return SECFailure; + } + break; // Soft fail -> success :( + } + } + } + + PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, + ("NSSCertDBTrustDomain: end of CheckRevocation")); + + return SECSuccess; +} + namespace { static char* nss_addEscape(const char* string, char quote) { char* newString = 0; int escapes = 0, size = 0; const char* src;
--- a/security/certverifier/NSSCertDBTrustDomain.h +++ b/security/certverifier/NSSCertDBTrustDomain.h @@ -58,18 +58,24 @@ public: virtual SECStatus GetCertTrust(insanity::pkix::EndEntityOrCA endEntityOrCA, const CERTCertificate* candidateCert, /*out*/ TrustLevel* trustLevel); virtual SECStatus VerifySignedData(const CERTSignedData* signedData, const CERTCertificate* cert); + virtual SECStatus CheckRevocation(insanity::pkix::EndEntityOrCA endEntityOrCA, + const CERTCertificate* cert, + /*const*/ CERTCertificate* issuerCert, + PRTime time, + /*optional*/ const SECItem* stapledOCSPResponse); + private: const SECTrustType mCertDBTrustType; -// const bool mOCSPDownloadEnabled; -// const bool mOCSPStrict; + const bool mOCSPDownloadEnabled; + const bool mOCSPStrict; void* mPinArg; // non-owning! }; } } // namespace mozilla::psm #endif // mozilla_psm__NSSCertDBTrustDomain_h