reland bug 967975 - certificate error override telemetry r=briansmith, a=sledru
authorDavid Keeler <dkeeler@mozilla.com>
Thu, 13 Feb 2014 14:53:29 -0800
changeset 183050 b81c398392535ca6221537fc4a6839d8b52fc342
parent 183049 5f62171194438532cdb2fc09626353de7c7a3786
child 183051 eb09596e4e037054f646a6757d53e468e04d2ca8
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbriansmith, sledru
bugs967975
milestone29.0a2
reland bug 967975 - certificate error override telemetry r=briansmith, a=sledru
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
@@ -286,16 +286,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");
@@ -352,16 +373,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,
@@ -947,16 +984,17 @@ SSLServerCertVerificationJob::Run()
                                    mStapledOCSPResponse, mProviderFlags,
                                    mTime);
     if (rv == SECSuccess) {
       uint32_t interval = (uint32_t) ((TimeStamp::Now() - mJobStartTime).ToMilliseconds());
       RefPtr<SSLServerCertVerificationResult> restart(
         new SSLServerCertVerificationResult(mInfoObject, 0,
                                             successTelemetry, 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();
@@ -1098,16 +1136,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(*certVerifier, socketInfo, serverCert,
                                  stapledOCSPResponse, providerFlags, now);
   if (rv == SECSuccess) {
+    Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1);
     return SECSuccess;
   }
 
   PRErrorCode error = PR_GetError();
   if (error != 0) {
     RefPtr<CertErrorRunnable> runnable(
         CreateCertErrorRunnable(*certVerifier, error, socketInfo, serverCert,
                                 stapledOCSPResponse,
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -14,33 +14,42 @@ 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_EXTENSION_NOT_FOUND                     = SEC_ERROR_BASE +  35;
+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;
 
 // Supported Certificate Usages
 const certificateUsageSSLClient              = 0x0001;
 const certificateUsageSSLServer              = 0x0002;
 const certificateUsageSSLCA                  = 0x0008;
 const certificateUsageEmailSigner            = 0x0010;
 const certificateUsageEmailRecipient         = 0x0020;
 const certificateUsageObjectSigner           = 0x0040;
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..3f9edf8a364a8e714f01b76e9badbcd1020bf073
GIT binary patch
literal 65536
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
z761SM000000000000000000000000000000000000000000000000000000000000
K0002Mr~d=8S+Sb{
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
@@ -0,0 +1,74 @@
+/* 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 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 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 "TLSServer.h"
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+struct BadCertHost
+{
+  const char *mHostName;
+  const char *mCertName;
+};
+
+const BadCertHost sBadCertHosts[] =
+{
+  { "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 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);
+  }
+
+  ScopedCERTCertificate cert;
+  SSLKEAType certKEA;
+  if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, host->mCertName,
+                                                    &cert, &certKEA)) {
+    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]);
+    return 1;
+  }
+
+  return StartServer(argv[1], DoSNISocketConfig, nullptr);
+}
--- 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..89bf4683440a823cc4be244b9496ccd84777e984
GIT binary patch
literal 527
zc$_n6V&XPvVk}v}%*4pV#K>sC%f_kI=F#?@mywZ`mBB#BP{4qXjX9KsS(rT}wYWsV
z+0j5woY&CAz|hFt$k5Ql)FMip*94hsAY~|FAO_LH57y(Fm!g}RSCUy$Y0x+y*#<^d
z2Ij_I27|^<rp88wn|~AIk4?NF>wJzY$jW@ic~(82IR|Zn1HKo|imZCK-B)S#!}_K3
z?U*;0Bu@ER{wu6m{%FGDDbp6*VAH7*KWkod@b50^s(bPUXZaqkj5Bmvw;<SFRNno>
zJ=^TLjD<{5mm&%$^WUBLZgtl$>ysVPOakpcHG2+ioi?i{a6@<F7LWH#%!~|-i!%+<
z4U~bdmlb4@F_3EF%*jtq%*n_vE@|S`(o3yK%q_@C)l1ILH85i1&}IV$5<4R!i<*Io
zff9^wz}P01QBqQ1rLPayY-M3!VW4lo12R~ck?}tZlK}%-STh5I;M|J4ud-QcwwiAI
zqnVukKH^DtuG3SUbwBHGB`m8}dO7*U+xCn#FScB)ZA{&BGP%gT{(`#Y2Gy{ay@n4q
zy<dOw_ZrEV#?>ua8-$`vg^%VGx>{sUoyMOUmNoIaeMiF%fsX==JPe*E@05Ru{TKK4
Y!x}D@9V*v+nHIGBe|*2`<GqPR03eLLQUCw|
--- 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..e7189981af7e9cc01ef7553085af08ea9674890c
GIT binary patch
literal 36864
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
HF!kR6s|SnZ
index 4161f2bd76f0b97c2f9079641b96adc62bacc4d0..2ef37e4bb12320e20e3ac394e804dbd44c151d4b
GIT binary patch
literal 452
zc$_n6Vmx5b#HhJ|nTe5!iIrhqt`(;NFB_*;n@8JsUPeY%Rt5tJLoovpHs(+kW?^3c
zl8n?Mg_6|b5(Q^R137VCLlXl-BXc7|LlaYrC~;mBWG)_^jq{OhVPs`sZtP_+XzXNa
zY-BjL#V5z|?&cYW(~q1Luenybhu3=H$=W-!XUt2>b=<gA-#2CM4&Mb_@+`Bo+HR~#
zKPA1~w%YFQA{&-H)jKX<-?5M@q207?(u94v%4J)U4&J)NnR(A_|FYiC4nbL;BdU}q
zcid(F_OQUX=h^=5JF@XNKWufnmV4vx)onTplq^kxI=Gpb85tNC16^()1a!NsFeBrC
z77hb8AjQPUh!!}^K=-mNn0o8A@y^6$5^K-Zm;{6}-(+aq%I7pINi)U$#qKNGx3=$W
z+M)CGh0H_STPG(oiaSoozyIohRCQ=gO7=PNnw;*d3l?6~2-W#?usWIV%fo`VkA&Ek
yraj-a%y#}dmZVBEX8SEVv+U;Rd2Bo%u&g<GsrDpuCMIRJr*27$S1VX9SO)<9sHcMf
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..413712f2419faa86dd8a06df64e54a8c9262ba3a
GIT binary patch
literal 440
zc$_n6V%%cT#3;LfnTe5!iIrhqt`&y?FB_*;n@8JsUPeY%Rt5thLjeOmHs(+kW?}Y_
z)Z!8aXGa4$ab80c14AQoBSS+IQ;R5ZUK3<4Ry~dLkxgb~WngaXWiV*$WNK_=c;~ue
zqu2N2x-as+h|JWrd46o?qvOK)4Tj-KE*l>&R}Nd1x{j|(PCS0b_s&YA=IW%~XHO*b
z`5k=y_4@CsC!sTItQGGZ>RRS^#pT=l533n3zmszfp3uHo@!?rJW)0P!!Y|*p+CRAM
zuDWqEUrBp^{{!9FZ&|^-$EpO47d)EF+*Wv=iJ6gsaj}?zh=CB$)w05jjQ?3U4A_7a
z6C)#9fG`8y+wny8PSLD}bB|kA^%vh=`|VfpnyeplxwOQU%uH@JtlzXhef{_46WZcs
z_Rn8ezrEIPZ@B#%kC<bz#Wqs+d#nRGnjSCNYcRV<f7gSjSuyFy?xgcKi78C>_|DhV
sAAHaD*W2B_rp{sy_KKKCRj&#+X$s!8P~=q3ceP(HZ}M&FzV0~#09?naP5=M^
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -56,12 +56,16 @@ fail-if = os == "android"
 # 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_getchain.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"
 [test_intermediate_basic_usage_constraints.js]
 # Bug 676972: test hangs consistently on Android
 skip-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
@@ -5678,16 +5678,22 @@
     "description": "Status of OCSP stapling on this handshake (1=present, good; 2=none; 3=present, expired; 4=present, other error)"
   },
   "SSL_OCSP_MAY_FETCH": {
     "expires_in_version": "never",
     "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": {
+    "expires_in_version": "never",
+    "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)"
+  },
   "TELEMETRY_TEST_EXPIRED": {
     "expires_in_version": "4.0a1",
     "kind": "flag",
     "description": "a testing histogram; not meant to be touched"
   },
   "CERT_OCSP_ENABLED": {
     "expires_in_version": "never",
     "kind": "boolean",
--- 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 \