bug 967975 - certificate error override telemetry r=briansmith a=lsblakk
authorDavid Keeler <dkeeler@mozilla.com>
Thu, 13 Feb 2014 14:53:29 -0800
changeset 176365 2bbbf1068a45bd321080207285a3bccd147d6471
parent 176364 8025e17b77d5b6e39ff5778f8b187bfc239646a9
child 176366 3fe65d101f2f3cb22823689d6db96ba4e5d17c58
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbriansmith, lsblakk
bugs967975
milestone28.0
bug 967975 - certificate error override telemetry r=briansmith a=lsblakk
security/manager/ssl/src/SSLServerCertVerification.cpp
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/test_cert_overrides.js
security/manager/ssl/tests/unit/tlsserver/cert8.db
security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
security/manager/ssl/tests/unit/tlsserver/default-ee.der
security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
security/manager/ssl/tests/unit/tlsserver/key3.db
security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
security/manager/ssl/tests/unit/tlsserver/secmod.db
security/manager/ssl/tests/unit/tlsserver/test-ca.der
security/manager/ssl/tests/unit/xpcshell.ini
testing/mochitest/Makefile.in
toolkit/components/telemetry/Histograms.json
toolkit/mozapps/installer/packager.mk
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -289,16 +289,37 @@ private:
   const PRErrorCode mDefaultErrorCodeToReport;
   const uint32_t mCollectedErrors;
   const PRErrorCode mErrorCodeTrust;
   const PRErrorCode mErrorCodeMismatch;
   const PRErrorCode mErrorCodeExpired;
   const uint32_t mProviderFlags;
 };
 
+// A probe value of 1 means "no error".
+uint32_t
+MapCertErrorToProbeValue(PRErrorCode errorCode)
+{
+  switch (errorCode)
+  {
+    case SEC_ERROR_UNKNOWN_ISSUER:                     return  2;
+    case SEC_ERROR_CA_CERT_INVALID:                    return  3;
+    case SEC_ERROR_UNTRUSTED_ISSUER:                   return  4;
+    case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:         return  5;
+    case SEC_ERROR_UNTRUSTED_CERT:                     return  6;
+    case SEC_ERROR_INADEQUATE_KEY_USAGE:               return  7;
+    case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:  return  8;
+    case SSL_ERROR_BAD_CERT_DOMAIN:                    return  9;
+    case SEC_ERROR_EXPIRED_CERTIFICATE:                return 10;
+  }
+  NS_WARNING("Unknown certificate error code. Does MapCertErrorToProbeValue "
+             "handle everything in PRErrorCodeToOverrideType?");
+  return 0;
+}
+
 SSLServerCertVerificationResult *
 CertErrorRunnable::CheckCertOverrides()
 {
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p][%p] top of CheckCertOverrides\n",
                                     mFdForLogging, this));
 
   if (!NS_IsMainThread()) {
     NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
@@ -355,16 +376,32 @@ CertErrorRunnable::CheckCertOverrides()
       if (NS_SUCCEEDED(nsrv) && haveOverride) 
       {
        // remove the errors that are already overriden
         remaining_display_errors &= ~overrideBits;
       }
     }
 
     if (!remaining_display_errors) {
+      // This can double- or triple-count one certificate with multiple
+      // different types of errors. Since this is telemetry and we just
+      // want a ballpark answer, we don't care.
+      if (mErrorCodeTrust != 0) {
+        uint32_t probeValue = MapCertErrorToProbeValue(mErrorCodeTrust);
+        Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, probeValue);
+      }
+      if (mErrorCodeMismatch != 0) {
+        uint32_t probeValue = MapCertErrorToProbeValue(mErrorCodeMismatch);
+        Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, probeValue);
+      }
+      if (mErrorCodeExpired != 0) {
+        uint32_t probeValue = MapCertErrorToProbeValue(mErrorCodeExpired);
+        Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, probeValue);
+      }
+
       // all errors are covered by override rules, so let's accept the cert
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
              ("[%p][%p] All errors covered by override rules\n",
              mFdForLogging, this));
       return new SSLServerCertVerificationResult(mInfoObject, 0);
     }
   } else {
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
@@ -1138,16 +1175,17 @@ SSLServerCertVerificationJob::Run()
         telemetryID = Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_CLASSIC;
 #ifndef NSS_NO_LIBPKIX
       }
 #endif
       RefPtr<SSLServerCertVerificationResult> restart(
         new SSLServerCertVerificationResult(mInfoObject, 0,
                                             telemetryID, interval));
       restart->Dispatch();
+      Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1);
       return NS_OK;
     }
 
     // Note: the interval is not calculated once as PR_GetError MUST be called
     // before any other  function call
     error = PR_GetError();
     {
       TimeStamp now = TimeStamp::Now();
@@ -1290,16 +1328,17 @@ AuthCertificateHook(void *arg, PRFileDes
   // We can't do certificate verification on a background thread, because the
   // thread doing the network I/O may not interrupt its network I/O on receipt
   // of our SSLServerCertVerificationResult event, and/or it might not even be
   // a non-blocking socket.
 
   SECStatus rv = AuthCertificate(socketInfo, serverCert, stapledOCSPResponse,
                                  providerFlags);
   if (rv == SECSuccess) {
+    Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1);
     return SECSuccess;
   }
 
   PRErrorCode error = PR_GetError();
   if (error != 0) {
     RefPtr<CertErrorRunnable> runnable(CreateCertErrorRunnable(
                     error, socketInfo, serverCert,
                     static_cast<const void *>(fd), providerFlags, PR_Now()));
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -14,31 +14,41 @@ let { HttpServer } = Cu.import("resource
 let { ctypes } = Cu.import("resource://gre/modules/ctypes.jsm");
 
 let gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 
 const isDebugBuild = Cc["@mozilla.org/xpcom/debug;1"]
                        .getService(Ci.nsIDebug2).isDebugBuild;
 
 const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
+const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
 
 // Sort in numerical order
+const SEC_ERROR_EXPIRED_CERTIFICATE                     = SEC_ERROR_BASE +  11;
 const SEC_ERROR_REVOKED_CERTIFICATE                     = SEC_ERROR_BASE +  12;
+const SEC_ERROR_UNKNOWN_ISSUER                          = SEC_ERROR_BASE +  13;
 const SEC_ERROR_BAD_DATABASE                            = SEC_ERROR_BASE +  18;
 const SEC_ERROR_UNTRUSTED_ISSUER                        = SEC_ERROR_BASE +  20;
+const SEC_ERROR_UNTRUSTED_CERT                          = SEC_ERROR_BASE +  21;
+const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE              = SEC_ERROR_BASE +  30;
+const SEC_ERROR_CA_CERT_INVALID                         = SEC_ERROR_BASE +  36;
+const SEC_ERROR_INADEQUATE_KEY_USAGE                    = SEC_ERROR_BASE +  90;
 const SEC_ERROR_OCSP_MALFORMED_REQUEST                  = SEC_ERROR_BASE + 120;
 const SEC_ERROR_OCSP_SERVER_ERROR                       = SEC_ERROR_BASE + 121;
 const SEC_ERROR_OCSP_TRY_SERVER_LATER                   = SEC_ERROR_BASE + 122;
 const SEC_ERROR_OCSP_REQUEST_NEEDS_SIG                  = SEC_ERROR_BASE + 123;
 const SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST               = SEC_ERROR_BASE + 124;
 const SEC_ERROR_OCSP_UNKNOWN_CERT                       = SEC_ERROR_BASE + 126;
 const SEC_ERROR_OCSP_MALFORMED_RESPONSE                 = SEC_ERROR_BASE + 129;
 const SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE              = SEC_ERROR_BASE + 130;
 const SEC_ERROR_OCSP_OLD_RESPONSE                       = SEC_ERROR_BASE + 132;
 const SEC_ERROR_OCSP_INVALID_SIGNING_CERT               = SEC_ERROR_BASE + 144;
+const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED       = SEC_ERROR_BASE + 176;
+
+const SSL_ERROR_BAD_CERT_DOMAIN                         = SSL_ERROR_BASE +  12;
 
 // Certificate Usages
 const certificateUsageSSLClient              = 0x0001;
 const certificateUsageSSLServer              = 0x0002;
 const certificateUsageSSLServerWithStepUp    = 0x0004;
 const certificateUsageSSLCA                  = 0x0008;
 const certificateUsageEmailSigner            = 0x0010;
 const certificateUsageEmailRecipient         = 0x0020;
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_overrides.js
@@ -0,0 +1,103 @@
+// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+"use strict";
+
+// Tests the certificate overrides we allow.
+// add_cert_override_test will queue a test that does the following:
+// 1. Attempt to connect to the given host. This should fail with the
+//    given error and override bits.
+// 2. Add an override for that host/port/certificate/override bits.
+// 3. Connect again. This should succeed.
+
+function add_cert_override(aHost, aExpectedBits, aSecurityInfo) {
+  let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+                               .SSLStatus;
+  let bits =
+    (sslstatus.isUntrusted ? Ci.nsICertOverrideService.ERROR_UNTRUSTED : 0) |
+    (sslstatus.isDomainMismatch ? Ci.nsICertOverrideService.ERROR_MISMATCH : 0) |
+    (sslstatus.isNotValidAtThisTime ? Ci.nsICertOverrideService.ERROR_TIME : 0);
+  do_check_eq(bits, aExpectedBits);
+  let cert = sslstatus.serverCert;
+  let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
+                              .getService(Ci.nsICertOverrideService);
+  certOverrideService.rememberValidityOverride(aHost, 8443, cert, aExpectedBits,
+                                               true);
+}
+
+function add_cert_override_test(aHost, aExpectedBits, aExpectedError) {
+  add_connection_test(aHost, aExpectedError, null,
+                      add_cert_override.bind(this, aHost, aExpectedBits));
+  add_connection_test(aHost, Cr.NS_OK);
+}
+
+function check_telemetry() {
+  let histogram = Cc["@mozilla.org/base/telemetry;1"]
+                    .getService(Ci.nsITelemetry)
+                    .getHistogramById("SSL_CERT_ERROR_OVERRIDES")
+                    .snapshot();
+  do_check_eq(histogram.counts[ 0], 0);
+  do_check_eq(histogram.counts[ 1], 1);
+  do_check_eq(histogram.counts[ 2], 1);
+  do_check_eq(histogram.counts[ 3], 1);
+  do_check_eq(histogram.counts[ 4], 1);
+  do_check_eq(histogram.counts[ 5], 1);
+  do_check_eq(histogram.counts[ 6], 1);
+  do_check_eq(histogram.counts[ 7], 1);
+  do_check_eq(histogram.counts[ 8], 1);
+  do_check_eq(histogram.counts[ 9], 1);
+  do_check_eq(histogram.counts[10], 1);
+  run_next_test();
+}
+
+function run_test() {
+  do_get_profile();
+  add_tls_server_setup("BadCertServer");
+  add_cert_override_test("expired.example.com",
+                         Ci.nsICertOverrideService.ERROR_TIME,
+                         getXPCOMStatusFromNSS(SEC_ERROR_EXPIRED_CERTIFICATE));
+  add_cert_override_test("selfsigned.example.com",
+                         Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                         getXPCOMStatusFromNSS(SEC_ERROR_CA_CERT_INVALID));
+  add_cert_override_test("unknownissuer.example.com",
+                         Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                         getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER));
+  add_cert_override_test("expiredissuer.example.com",
+                         Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                         getXPCOMStatusFromNSS(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE));
+  add_cert_override_test("md5signature.example.com",
+                         Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                         getXPCOMStatusFromNSS(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED));
+  add_cert_override_test("inadequatekeyusage.example.com",
+                         Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                         getXPCOMStatusFromNSS(SEC_ERROR_INADEQUATE_KEY_USAGE));
+  add_cert_override_test("mismatch.example.com",
+                         Ci.nsICertOverrideService.ERROR_MISMATCH,
+                         getXPCOMStatusFromNSS(SSL_ERROR_BAD_CERT_DOMAIN));
+  // Before we specifically distrust this certificate, it should be trusted.
+  add_connection_test("untrusted.example.com", Cr.NS_OK);
+  add_test(function() {
+    let certdb = Cc["@mozilla.org/security/x509certdb;1"]
+                    .getService(Ci.nsIX509CertDB);
+    // A trust argument of "pu" means the cert is actively distrusted.
+    addCertFromFile(certdb, "tlsserver/default-ee.der", "pu,,");
+    clearSessionCache();
+    run_next_test();
+  });
+  add_cert_override_test("untrusted.example.com",
+                         Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                         getXPCOMStatusFromNSS(SEC_ERROR_UNTRUSTED_CERT));
+  add_test(function() {
+    let certdb = Cc["@mozilla.org/security/x509certdb;1"]
+                    .getService(Ci.nsIX509CertDB);
+    // A trust argument of "pu" means the cert is actively distrusted.
+    addCertFromFile(certdb, "tlsserver/other-test-ca.der", "pu,,");
+    run_next_test();
+  });
+  add_cert_override_test("untrustedissuer.example.com",
+                         Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                         getXPCOMStatusFromNSS(SEC_ERROR_UNTRUSTED_ISSUER));
+  add_test(check_telemetry);
+  run_next_test();
+}
index ac0939405ba15d6f36089591a35b411aab673c5d..95fa54f725bb28cd3add0789062a5359c143a101
GIT binary patch
literal 65536
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
z00000000000000000000000000000000000000000000000000000000000000000
H!0i73W<c_g
copy from security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
copy to security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
@@ -1,105 +1,69 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// This is a standalone server that delivers various stapled OCSP responses.
+// This is a standalone server that uses various bad certificates.
 // The client is expected to connect, initiate an SSL handshake (with SNI
-// to indicate which "server" to connect to), and verify the OCSP response.
+// to indicate which "server" to connect to), and verify the certificate.
 // If all is good, the client then sends one encrypted byte and receives that
 // same byte back.
 // This server also has the ability to "call back" another process waiting on
 // it. That is, when the server is all set up and ready to receive connections,
 // it will connect to a specified port and issue a simple HTTP request.
 
 #include <stdio.h>
 
-#include "OCSPCommon.h"
 #include "TLSServer.h"
 
 using namespace mozilla;
 using namespace mozilla::test;
 
-const OCSPHost sOCSPHosts[] =
+struct BadCertHost
+{
+  const char *mHostName;
+  const char *mCertName;
+};
+
+const BadCertHost sBadCertHosts[] =
 {
-  { "ocsp-stapling-good.example.com", ORTGood, nullptr },
-  { "ocsp-stapling-revoked.example.com", ORTRevoked, nullptr },
-  { "ocsp-stapling-unknown.example.com", ORTUnknown, nullptr },
-  { "ocsp-stapling-good-other.example.com", ORTGoodOtherCert, "ocspOtherEndEntity" },
-  { "ocsp-stapling-good-other-ca.example.com", ORTGoodOtherCA, "otherCA" },
-  { "ocsp-stapling-expired.example.com", ORTExpired, nullptr },
-  { "ocsp-stapling-expired-fresh-ca.example.com", ORTExpiredFreshCA, nullptr },
-  { "ocsp-stapling-none.example.com", ORTNone, nullptr },
-  { "ocsp-stapling-empty.example.com", ORTEmpty, nullptr },
-  { "ocsp-stapling-malformed.example.com", ORTMalformed, nullptr },
-  { "ocsp-stapling-srverr.example.com", ORTSrverr, nullptr },
-  { "ocsp-stapling-trylater.example.com", ORTTryLater, nullptr },
-  { "ocsp-stapling-needssig.example.com", ORTNeedsSig, nullptr },
-  { "ocsp-stapling-unauthorized.example.com", ORTUnauthorized, nullptr },
-  { "ocsp-stapling-with-intermediate.example.com", ORTGood, "ocspEEWithIntermediate" },
-  { nullptr, ORTNull, nullptr }
+  { "expired.example.com", "expired" },
+  { "selfsigned.example.com", "selfsigned" },
+  { "unknownissuer.example.com", "unknownissuer" },
+  { "mismatch.example.com", "mismatch" },
+  { "expiredissuer.example.com", "expiredissuer" },
+  { "md5signature.example.com", "md5signature" },
+  { "untrusted.example.com", "localhostAndExampleCom" },
+  { "inadequatekeyusage.example.com", "inadequatekeyusage" },
+  { "untrustedissuer.example.com", "untrustedissuer" },
+  { nullptr, nullptr }
 };
 
 int32_t
 DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr,
                   uint32_t aSrvNameArrSize, void *aArg)
 {
-  const OCSPHost *host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize,
-                                       sOCSPHosts);
+  const BadCertHost *host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize,
+                                          sBadCertHosts);
   if (!host) {
     return SSL_SNI_SEND_ALERT;
   }
 
   if (gDebugLevel >= DEBUG_VERBOSE) {
     fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
   }
 
-  const char *certNickname;
-  if (strcmp(host->mHostName,
-             "ocsp-stapling-with-intermediate.example.com") == 0) {
-    certNickname = host->mAdditionalCertName;
-  } else {
-    certNickname = DEFAULT_CERT_NICKNAME;
-  }
-
   ScopedCERTCertificate cert;
   SSLKEAType certKEA;
-  if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, certNickname,
+  if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, host->mCertName,
                                                     &cert, &certKEA)) {
     return SSL_SNI_SEND_ALERT;
   }
 
-  // If the OCSP response type is "none", don't staple a response.
-  if (host->mORT == ORTNone) {
-    return 0;
-  }
-
-  PLArenaPool *arena = PORT_NewArena(1024);
-  if (!arena) {
-    PrintPRError("PORT_NewArena failed");
-    return SSL_SNI_SEND_ALERT;
-  }
-
-  // response is contained by the arena - freeing the arena will free it
-  SECItemArray *response = GetOCSPResponseForType(host->mORT, cert, arena,
-                                                  host->mAdditionalCertName);
-  if (!response) {
-    PORT_FreeArena(arena, PR_FALSE);
-    return SSL_SNI_SEND_ALERT;
-  }
-
-  // SSL_SetStapledOCSPResponses makes a deep copy of response
-  SECStatus st = SSL_SetStapledOCSPResponses(aFd, response, certKEA);
-  PORT_FreeArena(arena, PR_FALSE);
-  if (st != SECSuccess) {
-    PrintPRError("SSL_SetStapledOCSPResponses failed");
-    return SSL_SNI_SEND_ALERT;
-  }
-
   return 0;
 }
 
 int
 main(int argc, char *argv[])
 {
   if (argc != 2) {
     fprintf(stderr, "usage: %s <NSS DB directory>\n", argv[0]);
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
@@ -2,15 +2,16 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 FAIL_ON_WARNINGS = True
 
 SIMPLE_PROGRAMS = [
+    'BadCertServer',
     'GenerateOCSPResponse',
     'OCSPStaplingServer',
 ]
 
 SOURCES += [
     '%s.cpp' % s for s in SIMPLE_PROGRAMS
 ]
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d6ce471b8362ed4733bbc38e3765242a4c6eeef8
GIT binary patch
literal 527
zc$_n6V&XPvVk}v}%*4pV#K>sC%f_kI=F#?@mywZ`mBB#BP{4qXjX9KsS(rT}wYWsV
z+0j5woY&CAz|hdx(9+P{)HF(**94hsAY~|FAO_LH57y(Fm!g}RSCUy$Y0x+y*#<^d
z2Ij_I27|^<rp88w?QJJYU8X&b*}o$;imStQxrs!;uIF+`rG3KB@3mOyH@R-<EXCBy
zM=O5_Y*?ND@y&tgUaq!1to30@4jRw6*=$#KZTEfQZvMUMe&Xg!=hp>UikqHUF|VpM
zZgxw-y*U=lVK;i__3ZBvUGBvgd|P_yMYAveKb+=x|GRGvhuzP1zcxoEW=00a#hC`_
z2FgI!%L=l{7)Uj7=Hw?Q=49j-mo)Ke>7`aA<`(3n>Lusr8W^#0XtMzWiJg&=Ma@9P
zKncb-U~Ci1C@Cqh($|M;wz4p=Fwi&P0U0dJ$oQXy$$$YZteJs9z<Kbu!pUjIvwURN
z^1rb-{JFq!(d@rQeG}g-<dUDJsp#1ixT5&qa|f1{H##R(OBwF{tPnQmgoM-7buXTL
zGF1Dy#&mwENcQccd!DTDI{aqFij{(81?u{LgZB4cp5e9SU(4Rg*R3|H;&ln!O`D$n
Zjy7I5H`+BY>a1^-;(YJY&09TQ0|2jDypsR`
--- a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
+++ b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
@@ -74,54 +74,69 @@ function make_CA {
 
 SERIALNO=1
 
 function make_INT {
   INT_RESPONSES="y\n0\ny\n2\n7\nhttp://localhost:8080/\n\nn\nn\n"
   NICKNAME="${1}"
   SUBJECT="${2}"
   CA="${3}"
+  EXTRA_ARGS="${4}"
 
   echo -e "$INT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -S \
                                                     -n $NICKNAME \
                                                     -s "$SUBJECT" \
                                                     -c $CA \
                                                     -t ",," \
                                                     -m $SERIALNO \
                                                     --extAIA \
-                                                    $COMMON_ARGS
+                                                    $COMMON_ARGS \
+                                                    $EXTRA_ARGS
   SERIALNO=$(($SERIALNO + 1))
 }
 
 function make_EE {
   CERT_RESPONSES="n\n\ny\n2\n7\nhttp://localhost:8080/\n\nn\nn\n"
   NICKNAME="${1}"
   SUBJECT="${2}"
   CA="${3}"
   SUBJECT_ALT_NAME="${4}"
-  if [ -n "$SUBJECT_ALT_NAME" ]; then
-    SUBJECT_ALT_NAME="-8 $SUBJECT_ALT_NAME"
-  fi
+  EXTRA_ARGS="${5}"
 
   echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -S \
                                                      -n $NICKNAME \
                                                      -s "$SUBJECT" \
-                                                     $SUBJECT_ALT_NAME \
+                                                     -8 $SUBJECT_ALT_NAME \
                                                      -c $CA \
                                                      -t ",," \
                                                      -m $SERIALNO \
                                                      --extAIA \
-                                                     $COMMON_ARGS
+                                                     $COMMON_ARGS \
+                                                     $EXTRA_ARGS
   SERIALNO=$(($SERIALNO + 1))
 }
 
 make_CA testCA 'CN=Test CA' test-ca.der
 make_CA otherCA 'CN=Other test CA' other-test-ca.der
 make_EE localhostAndExampleCom 'CN=Test End-entity' testCA "localhost,*.example.com"
+$RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -L -n localhostAndExampleCom -r > $OUTPUT_DIR/default-ee.der
 # A cert that is like localhostAndExampleCom, but with a different serial number for
 # testing the "OCSP response is from the right issuer, but it is for the wrong cert"
 # case.
 make_EE ocspOtherEndEntity 'CN=Other Cert' testCA "localhost,*.example.com"
 
 make_INT testINT 'CN=Test Intermediate' testCA
 make_EE ocspEEWithIntermediate 'CN=Test End-entity with Intermediate' testINT "localhost,*.example.com"
+make_EE expired 'CN=Expired Test End-entity' testCA "expired.example.com" "-w -400"
+make_EE mismatch 'CN=Mismatch Test End-entity' testCA "doesntmatch.example.com"
+make_EE selfsigned 'CN=Self-signed Test End-entity' testCA "selfsigned.example.com" "-x"
+# If the certificate 'CN=Test Intermediate' isn't loaded into memory,
+# this certificate will have an unknown issuer.
+make_INT deletedINT 'CN=Test Intermediate to delete' testCA
+make_EE unknownissuer 'CN=Test End-entity from unknown issuer' deletedINT "unknownissuer.example.com"
+$RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -D -n deletedINT
+make_INT expiredINT 'CN=Expired Test Intermediate' testCA "-w -400"
+make_EE expiredissuer 'CN=Test End-entity with expired issuer' expiredINT "expiredissuer.example.com"
+NSS_ALLOW_WEAK_SIGNATURE_ALG=1 make_EE md5signature 'CN=Test End-entity with MD5 signature' testCA "md5signature.example.com" "-Z MD5"
+make_EE inadequatekeyusage 'CN=Test End-entity with inadequate key usage' testCA "inadequatekeyusage.example.com" "--keyUsage crlSigning"
+make_EE untrustedissuer 'CN=Test End-entity with untrusted issuer' otherCA "untrustedissuer.example.com"
 
 cleanup
index 2d4698c8d1bedeec2aecfc114b31b546d0f9004f..567ae9ac82b20526d21706bbf5f10a2a364e3a0c
GIT binary patch
literal 32768
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
index 4161f2bd76f0b97c2f9079641b96adc62bacc4d0..83b01deddfc049ef363795d0ba0af242aa5a7685
GIT binary patch
literal 452
zc$_n6Vmx5b#HhJ|nTe5!iIriV=fzqBUN%mxHjlRNyo`*jtPBPchGGUHY|No7%)-3>
zB^jwj3MHw<B?``t26E!Oh9(AvhQ@}LhUTWGQR2KN$Xq-+8|Ne2!pO?N+}O)t(Ade;
z*vN3C`ES7L*Y`yxz3iH{S@-ir?epeueAoSrI+=UDIqR*#ZbPn4Tb<|&PI}3kHlHt=
zJU3a@)2!(E%g1MTf7a>o_M3Z3@}c<k2YdauIIQ5@Gwt!;;4|%pmy{ZUIV>f=hh_h|
zknwHh``Y(jr(1ZO#cu5o_pOr^ar?8%S0>xicb_urH*Y3pMh3>kK$jZ`0o^Vu%*gnk
zg~NaiNHH-oq6H2!(7n>V?$c*C&->l9ea>zf&bjYv=6%||UM$tvGVJKVhn0&jILzOo
zCx2v{>P4P86VwY{nrS~f|MJKGlNMUX3+F4@UV9_>ra!$Z_1%A0D_Jw~lRtPjc=w0w
y5B$)&T_$JKfw-ra`{Xp{-93Nkwc?|*6D<nea`Y;6JWn5C)ShMDsJ2swa{~ZI&aIyS
index 3dced7b9ece589b22ede0c537c179c08ba79fd55..a5f2f603a72865b8a22beb933932e44338599699
GIT binary patch
literal 16384
zc%1FpK}y3w6oBE^R%#bg-MAAOaFLy%2gs^!q$q`Yf!It-XquFniP(k5aPJjdckKnd
zf?h*vYy!1VTw0O;gU5R_6W)YbKWQF^B2pLGdlfk<N;wdzNg2|rih7kBtGqfYKYC0b
z-lSBo;@7omySNnq00000000000D!-vF7Jnn_H%pD-u0gV00000uw7GBZGU6-yH`Xc
z)$F>QTn`57GIiRdVWRrI?m(S&I_fHm@<bQsFT*Sy#p5V8foJtFH?f=h(=ZLk+W6Kc
zzSGuOKTl)lyTn>;?zHjLP#f1bdKBLWo~I^yzFrhvrdhP=R^;>O)>ES-9NR$UvnX_W
zb(L2wx*L7Uz25h3<yIW6>ziz%(+h2fCZ4%COTSLaIsB)_GAQ!tws-pJ89tT(00000
H;J^C>R^wEH
index 36cfcddcfb5a3363e425fa7a24cbaf5ce3622521..61e984b620572ec673ccf6b610c9e58687090e8a
GIT binary patch
literal 440
zc$_n6V%%cT#3;LfnTe5!iIriV=fzqBUN%mxHjlRNyo`*jtPBQ1h5`nBY|No7%);y;
zsl_D<&W;9h;=G0?28M>lhL(orrlwKiye7z8ta=*fBb&^~%D~*%%V5yh$<)}$u#@Mo
z$FJo#3f-fQC7r&lmlgAQx>wnhikxz*{+A^R9-AM>xL?qdcx~+&qL~`Cd~V-?85^#r
zL|<Ly;gG-hq<hZ4_s@d$&n)nDE4Z=ZRFchA>2~RfyNbF~JsUq9iVf^I6}L9obccuE
z>64rjf2Mu-xsWCJW%Hk+gjAnJ^4hm04=ZkFVrFDuTr6fFVju)`wX85B<9`+o12!PV
z#K?#iAk0Ab#%yo6-EOoz@__E-i-#_}PV}&Q*w^9srJ2F@v}S_uN&7|K(t<0h+h!MC
z6<yg8Il0^3KKg{pWx2bPf_I(c{9~0Yw`R(@Z(AMCMQOhK5@{Flu2|Nhc+HvUoWD}{
rwDu;}^-0BF-<*4{w$!HX+~k{k1E)Um-+w&MW2Wz)E<Me<nffLGbRVZ<
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -46,8 +46,12 @@ run-sequentially = hardcoded ports
 fail-if = os == "android"
 [test_sts_ipv4_ipv6.js]
 [test_cert_signatures.js]
 # Bug 676972: test fails consistently on Android
 fail-if = os == "android"
 [test_ev_certs.js]
 # Bug 676972: test fails consistently on Android
 fail-if = os == "android"
+[test_cert_overrides.js]
+run-sequentially = hardcoded ports
+# Bug 676972: test fails consistently on Android
+fail-if = os == "android"
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -106,16 +106,17 @@ libs::
 
 # Binaries and scripts that don't get packaged with the build,
 # but that we need for the test harness
 TEST_HARNESS_BINS := \
   xpcshell$(BIN_SUFFIX) \
   ssltunnel$(BIN_SUFFIX) \
   certutil$(BIN_SUFFIX) \
   pk12util$(BIN_SUFFIX) \
+  BadCertServer$(BIN_SUFFIX) \
   OCSPStaplingServer$(BIN_SUFFIX) \
   GenerateOCSPResponse$(BIN_SUFFIX) \
   fix_stack_using_bpsyms.py \
   $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
 TEST_HARNESS_BINS += \
   crashinject$(BIN_SUFFIX) \
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4654,16 +4654,21 @@
     "n_values": 8,
     "description": "Status of OCSP stapling on this handshake (1=present, good; 2=none; 3=present, expired; 4=present, other error)"
   },
   "SSL_OCSP_MAY_FETCH": {
     "kind": "enumerated",
     "n_values": 8,
     "description": "For non-stapling cases, is OCSP fetching a possibility? (0=yes, 1=no because missing/invalid OCSP URI, 2=no because fetching disabled, 3=no because both)"
   },
+  "SSL_CERT_ERROR_OVERRIDES": {
+    "kind": "enumerated",
+    "n_values": 24,
+    "description": "Was a certificate error overridden on this handshake? What was it? (0=unknown error (indicating bug), 1=no, >1=a specific error)"
+  },
   "CERT_OCSP_ENABLED": {
     "kind": "boolean",
     "description": "Is OCSP fetching enabled? (pref security.OCSP.enabled)"
   },
   "CERT_OCSP_REQUIRED": {
     "kind": "boolean",
     "description": "Is OCSP required when the cert has an OCSP URI? (pref security.OCSP.require)"
   }
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -616,16 +616,17 @@ NO_PKG_FILES += \
 	nm2tsv* \
 	nsinstall* \
 	res/samples \
 	res/throbber \
 	shlibsign* \
 	ssltunnel* \
 	certutil* \
 	pk12util* \
+	BadCertServer* \
 	OCSPStaplingServer* \
 	GenerateOCSPResponse* \
 	winEmbed.exe \
 	chrome/chrome.rdf \
 	chrome/app-chrome.manifest \
 	chrome/overlayinfo \
 	components/compreg.dat \
 	components/xpti.dat \