Bug 1107666: Fix OCSP stapling telemetry (SSL_OCSP_STAPLING), r=keeler

--HG--
extra : rebase_source : 926f091b2a361d7dce30bee918d6659259f1b3e4
This commit is contained in:
Brian Smith 2014-12-11 23:22:35 -08:00
parent 5cfa302167
commit 746ee1cc1d
5 changed files with 68 additions and 15 deletions

View File

@ -123,20 +123,25 @@ CertListContainsExpectedKeys(const CERTCertList* certList,
}
static Result
BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, Input certDER,
BuildCertChainForOneKeyUsage(NSSCertDBTrustDomain& trustDomain, Input certDER,
Time time, KeyUsage ku1, KeyUsage ku2,
KeyUsage ku3, KeyPurposeId eku,
const CertPolicyId& requiredPolicy,
const Input* stapledOCSPResponse)
const Input* stapledOCSPResponse,
/*optional out*/ CertVerifier::OCSPStaplingStatus*
ocspStaplingStatus)
{
trustDomain.ResetOCSPStaplingStatus();
Result rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity, ku1,
eku, requiredPolicy, stapledOCSPResponse);
if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
trustDomain.ResetOCSPStaplingStatus();
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity, ku2,
eku, requiredPolicy, stapledOCSPResponse);
if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
trustDomain.ResetOCSPStaplingStatus();
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity, ku3,
eku, requiredPolicy, stapledOCSPResponse);
@ -145,6 +150,9 @@ BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, Input certDER,
}
}
}
if (ocspStaplingStatus) {
*ocspStaplingStatus = trustDomain.GetOCSPStaplingStatus();
}
return rv;
}
@ -154,7 +162,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
const Flags flags,
/*optional*/ const SECItem* stapledOCSPResponseSECItem,
/*optional out*/ ScopedCERTCertList* builtChain,
/*optional out*/ SECOidTag* evOidPolicy)
/*optional out*/ SECOidTag* evOidPolicy,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus)
{
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of VerifyCert\n"));
@ -167,6 +176,13 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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 (!cert ||
(usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
@ -243,7 +259,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
KeyUsage::keyEncipherment, // RSA
KeyUsage::keyAgreement, // (EC)DH
KeyPurposeId::id_kp_serverAuth,
evPolicy, stapledOCSPResponse);
evPolicy, stapledOCSPResponse,
ocspStaplingStatus);
if (rv == Success) {
if (evOidPolicy) {
*evOidPolicy = evPolicyOidTag;
@ -268,7 +285,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
KeyUsage::keyAgreement, // (EC)DH
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
stapledOCSPResponse);
stapledOCSPResponse,
ocspStaplingStatus);
break;
}
@ -418,7 +436,8 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
bool saveIntermediatesInPermanentDatabase,
Flags flags,
/*optional out*/ ScopedCERTCertList* builtChain,
/*optional out*/ SECOidTag* evOidPolicy)
/*optional out*/ SECOidTag* evOidPolicy,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus)
{
PR_ASSERT(peerCert);
// XXX: PR_ASSERT(pinarg)
@ -442,7 +461,7 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
// if VerifyCert succeeded.
SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg,
hostname, flags, stapledOCSPResponse,
&builtChainTemp, evOidPolicy);
&builtChainTemp, evOidPolicy, ocspStaplingStatus);
if (rv != SECSuccess) {
return rv;
}

View File

@ -24,6 +24,15 @@ public:
// Don't perform fallback DV validation on EV validation failure.
static const Flags FLAG_MUST_BE_EV;
// These values correspond to the SSL_OCSP_STAPLING telemetry.
enum OCSPStaplingStatus {
OCSP_STAPLING_NEVER_CHECKED = 0,
OCSP_STAPLING_GOOD = 1,
OCSP_STAPLING_NONE = 2,
OCSP_STAPLING_EXPIRED = 3,
OCSP_STAPLING_INVALID = 4,
};
// *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV
// Only one usage per verification is supported.
SECStatus VerifyCert(CERTCertificate* cert,
@ -34,7 +43,8 @@ public:
Flags flags = 0,
/*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
/*optional out*/ ScopedCERTCertList* builtChain = nullptr,
/*optional out*/ SECOidTag* evOidPolicy = nullptr);
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr);
SECStatus VerifySSLServerCert(
CERTCertificate* peerCert,
@ -45,7 +55,8 @@ public:
bool saveIntermediatesInPermanentDatabase = false,
Flags flags = 0,
/*optional out*/ ScopedCERTCertList* builtChain = nullptr,
/*optional out*/ SECOidTag* evOidPolicy = nullptr);
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr);
enum PinningMode {
pinningDisabled = 0,

View File

@ -13,7 +13,6 @@
#include "NSSErrorsService.h"
#include "OCSPRequestor.h"
#include "certdb.h"
#include "mozilla/Telemetry.h"
#include "nss.h"
#include "pk11pub.h"
#include "pkix/pkix.h"
@ -68,6 +67,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
, mMinimumNonECCBits(forEV ? MINIMUM_NON_ECC_BITS_EV : MINIMUM_NON_ECC_BITS_DV)
, mHostname(hostname)
, mBuiltChain(builtChain)
, mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED)
{
}
@ -383,7 +383,7 @@ NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
ResponseWasStapled, expired);
if (stapledOCSPResponseResult == Success) {
// stapled OCSP response present and good
Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 1);
mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_GOOD;
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
("NSSCertDBTrustDomain: stapled OCSP response: good"));
return Success;
@ -391,19 +391,19 @@ NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
if (stapledOCSPResponseResult == Result::ERROR_OCSP_OLD_RESPONSE ||
expired) {
// stapled OCSP response present but expired
Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 3);
mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_EXPIRED;
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
("NSSCertDBTrustDomain: expired stapled OCSP response"));
} else {
// stapled OCSP response present but invalid for some reason
Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 4);
mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_INVALID;
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
("NSSCertDBTrustDomain: stapled OCSP response: failure"));
return stapledOCSPResponseResult;
}
} else {
// no stapled OCSP response
Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 2);
mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NONE;
PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
("NSSCertDBTrustDomain: no stapled OCSP response"));
}

View File

@ -50,6 +50,7 @@ public:
FetchOCSPForEV = 3,
LocalOnlyOCSPForEV = 4,
};
NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
OCSPCache& ocspCache, void* pinArg,
CertVerifier::ocsp_get_config ocspGETConfig,
@ -91,6 +92,15 @@ public:
virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
mozilla::pkix::Time time) MOZ_OVERRIDE;
CertVerifier::OCSPStaplingStatus GetOCSPStaplingStatus() const
{
return mOCSPStaplingStatus;
}
void ResetOCSPStaplingStatus()
{
mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
}
private:
enum EncodedResponseSource {
ResponseIsFromNetwork = 1,
@ -110,6 +120,7 @@ private:
const unsigned int mMinimumNonECCBits;
const char* mHostname; // non-owning - only used for pinning checks
ScopedCERTCertList* mBuiltChain; // non-owning
CertVerifier::OCSPStaplingStatus mOCSPStaplingStatus;
};
} } // namespace mozilla::psm

View File

@ -1085,11 +1085,22 @@ AuthCertificate(CertVerifier& certVerifier,
SECOidTag evOidPolicy;
ScopedCERTCertList certList;
CertVerifier::OCSPStaplingStatus ocspStaplingStatus =
CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse,
time, infoObject,
infoObject->GetHostNameRaw(),
saveIntermediates, 0, &certList,
&evOidPolicy);
&evOidPolicy, &ocspStaplingStatus);
PRErrorCode savedErrorCode;
if (rv != SECSuccess) {
savedErrorCode = PR_GetError();
}
if (ocspStaplingStatus != CertVerifier::OCSP_STAPLING_NEVER_CHECKED) {
Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, ocspStaplingStatus);
}
// We want to remember the CA certs in the temp db, so that the application can find the
// complete chain at any time it might need it.
@ -1142,6 +1153,7 @@ AuthCertificate(CertVerifier& certVerifier,
// infoObject so it can be used for error reporting. Note: infoObject
// indirectly takes ownership of peerCertChain.
infoObject->SetFailedCertChain(peerCertChain);
PR_SetError(savedErrorCode, 0);
}
return rv;