bug 1019198 - fail handshake if given an expired OCSP response and fetching a new one fails r=briansmith
authorDavid Keeler <dkeeler@mozilla.com>
Fri, 06 Jun 2014 09:20:50 -0700
changeset 206458 3697556d43f79c2eb9dfdfe283e96c4ca065608f
parent 206457 bebb2a8b8f83b8379eb85b1a2db31f28d5c99825
child 206459 d4fc8141e2db56af7651e959c06dfe730058bc85
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbriansmith
bugs1019198
milestone32.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
bug 1019198 - fail handshake if given an expired OCSP response and fetching a new one fails r=briansmith
security/certverifier/NSSCertDBTrustDomain.cpp
security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -386,16 +386,23 @@ NSSCertDBTrustDomain::CheckRevocation(
     }
     if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) {
       PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
              ("NSSCertDBTrustDomain: returning SECFailure from cached "
               "response after OCSP request failure"));
       PR_SetError(cachedResponseErrorCode, 0);
       return SECFailure;
     }
+    if (stapledOCSPResponse) {
+      PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+             ("NSSCertDBTrustDomain: returning SECFailure from expired "
+              "stapled response after OCSP request failure"));
+      PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
+      return SECFailure;
+    }
 
     PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
            ("NSSCertDBTrustDomain: returning SECSuccess after "
             "OCSP request failure"));
     return SECSuccess; // Soft fail -> success :(
   }
 
   SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert, time,
@@ -409,20 +416,28 @@ NSSCertDBTrustDomain::CheckRevocation(
   }
 
   PRErrorCode error = PR_GetError();
   if (error == SEC_ERROR_OCSP_UNKNOWN_CERT ||
       error == SEC_ERROR_REVOKED_CERTIFICATE) {
     return rv;
   }
 
+  if (stapledOCSPResponse) {
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("NSSCertDBTrustDomain: returning SECFailure from expired stapled "
+            "response after OCSP request verification failure"));
+    PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
+    return SECFailure;
+  }
+
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
          ("NSSCertDBTrustDomain: end of CheckRevocation"));
 
-  return SECSuccess;
+  return SECSuccess; // Soft fail -> success :(
 }
 
 SECStatus
 NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
   const CERTCertificate* cert, CERTCertificate* issuerCert, PRTime time,
   uint16_t maxLifetimeInDays, const SECItem* encodedResponse,
   EncodedResponseSource responseSource)
 {
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
@@ -43,19 +43,24 @@ let oldValidityPeriodOCSPResponseGood = 
 // Fresh signature, certificate is revoked.
 let ocspResponseRevoked = ocspResponses[3];
 // Fresh signature, certificate is unknown.
 let ocspResponseUnknown = ocspResponses[4];
 
 function run_test() {
   let ocspResponder = new HttpServer();
   ocspResponder.registerPrefixHandler("/", function(request, response) {
-    response.setStatusLine(request.httpVersion, 200, "OK");
-    response.setHeader("Content-Type", "application/ocsp-response");
-    response.write(gCurrentOCSPResponse);
+    if (gCurrentOCSPResponse) {
+      response.setStatusLine(request.httpVersion, 200, "OK");
+      response.setHeader("Content-Type", "application/ocsp-response");
+      response.write(gCurrentOCSPResponse);
+    } else {
+      response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+      response.write("Internal Server Error");
+    }
     gOCSPRequestCount++;
   });
   ocspResponder.start(8080);
   add_tls_server_setup("OCSPStaplingServer");
   add_tests_in_mode(true);
   add_tests_in_mode(false);
   add_test(function () { ocspResponder.stop(run_next_test); });
   add_test(check_ocsp_stapling_telemetry);
@@ -78,33 +83,62 @@ function add_tests_in_mode(useMozillaPKI
   // staples an expired OCSP response. The certificate has not expired.
   // For ocsp-stapling-expired-fresh-ca.example.com, the OCSP stapling
   // server staples an OCSP response with a recent signature but with an
   // out-of-date validity period. The certificate has not expired.
   add_ocsp_test("ocsp-stapling-expired.example.com", Cr.NS_OK,
                 ocspResponseGood);
   add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com", Cr.NS_OK,
                 ocspResponseGood);
-  add_ocsp_test("ocsp-stapling-expired.example.com", Cr.NS_OK,
+  // With mozilla::pkix, if we can't fetch a more recent response when
+  // given an expired stapled response, we terminate the connection.
+  add_ocsp_test("ocsp-stapling-expired.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
                 expiredOCSPResponseGood);
-  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com", Cr.NS_OK,
+  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
                 expiredOCSPResponseGood);
-  add_ocsp_test("ocsp-stapling-expired.example.com", Cr.NS_OK,
+  add_ocsp_test("ocsp-stapling-expired.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
+                oldValidityPeriodOCSPResponseGood);
+  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
                 oldValidityPeriodOCSPResponseGood);
-  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com", Cr.NS_OK,
-                oldValidityPeriodOCSPResponseGood);
+  add_ocsp_test("ocsp-stapling-expired.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
+                null);
+  add_ocsp_test("ocsp-stapling-expired.example.com",
+                useMozillaPKIX
+                  ? getXPCOMStatusFromNSS(SEC_ERROR_OCSP_OLD_RESPONSE)
+                  : Cr.NS_OK,
+                null);
+  // Of course, if the newer response indicates Revoked or Unknown,
+  // that status must be returned.
   add_ocsp_test("ocsp-stapling-expired.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_REVOKED_CERTIFICATE),
                 ocspResponseRevoked);
   add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_REVOKED_CERTIFICATE),
                 ocspResponseRevoked);
   add_ocsp_test("ocsp-stapling-expired.example.com",
                 getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNKNOWN_CERT),
                 ocspResponseUnknown);
+  add_ocsp_test("ocsp-stapling-expired-fresh-ca.example.com",
+                getXPCOMStatusFromNSS(SEC_ERROR_OCSP_UNKNOWN_CERT),
+                ocspResponseUnknown);
 
   if (useMozillaPKIX) {
     // These tests are verifying that an valid but very old response
     // is rejected as a valid stapled response, requiring a fetch
     // from the ocsp responder.
     add_ocsp_test("ocsp-stapling-ancient-valid.example.com", Cr.NS_OK,
                   ocspResponseGood);
     add_ocsp_test("ocsp-stapling-ancient-valid.example.com",
@@ -119,16 +153,13 @@ function add_tests_in_mode(useMozillaPKI
 function check_ocsp_stapling_telemetry() {
   let histogram = Cc["@mozilla.org/base/telemetry;1"]
                     .getService(Ci.nsITelemetry)
                     .getHistogramById("SSL_OCSP_STAPLING")
                     .snapshot();
   do_check_eq(histogram.counts[0], 2 * 0); // histogram bucket 0 is unused
   do_check_eq(histogram.counts[1], 2 * 0); // 0 connections with a good response
   do_check_eq(histogram.counts[2], 2 * 0); // 0 connections with no stapled resp.
-  do_check_eq(histogram.counts[3], 2 * 9 + 3); // 9 connections with an expired response
-                                               // 3 connection with a response
-                                               // considered expired due to being
-                                               // old but having an overly-long
-                                               // validity period
+  do_check_eq(histogram.counts[3], 2 * 12 + 3); // 12 connections with an expired response
+                                                // +3 more mozilla::pkix-only expired responses
   do_check_eq(histogram.counts[4], 2 * 0); // 0 connections with bad responses
   run_next_test();
 }