Bug 1406856 - Re-plumb nsISSLStatus.idl to carry with it the whole nsIX509CertList r?keeler draft
authorMark Goodwin <mgoodwin@mozilla.com>
Thu, 26 Oct 2017 17:52:11 +0100
changeset 688163 00903bfc27fe61de233e81cf259ccbfe324b6d3c
parent 684703 d49501f258b105c5e2dcd0a59896ec1ceabf726b
child 737797 2085ef94b1e067323d4f2853f44f2c665bce4907
push id86671
push usermgoodwin@mozilla.com
push dateSat, 28 Oct 2017 11:02:56 +0000
reviewerskeeler
bugs1406856
milestone58.0a1
Bug 1406856 - Re-plumb nsISSLStatus.idl to carry with it the whole nsIX509CertList r?keeler MozReview-Commit-ID: 2YDmCzqdm26
security/manager/ssl/SSLServerCertVerification.cpp
security/manager/ssl/TransportSecurityInfo.cpp
security/manager/ssl/nsISSLStatus.idl
security/manager/ssl/nsNSSCallbacks.cpp
security/manager/ssl/nsSSLStatus.cpp
security/manager/ssl/nsSSLStatus.h
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/test_cert_chains.js
security/manager/ssl/tests/unit/test_ssl_status.js
security/manager/ssl/tests/unit/xpcshell.ini
--- a/security/manager/ssl/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/SSLServerCertVerification.cpp
@@ -1463,37 +1463,37 @@ AuthCertificate(CertVerifier& certVerifi
     // a client cert. Let's provide a minimal SSLStatus
     // to the caller that contains at least the cert and its status.
     RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
     if (!status) {
       status = new nsSSLStatus();
       infoObject->SetSSLStatus(status);
     }
 
-    if (!status->HasServerCert()) {
-      EVStatus evStatus;
-      if (evOidPolicy == SEC_OID_UNKNOWN) {
-        evStatus = EVStatus::NotEV;
-      } else {
-        evStatus = EVStatus::EV;
-      }
+    EVStatus evStatus;
+    if (evOidPolicy == SEC_OID_UNKNOWN) {
+      evStatus = EVStatus::NotEV;
+    } else {
+      evStatus = EVStatus::EV;
+    }
 
-      RefPtr<nsNSSCertificate> nsc = nsNSSCertificate::Create(cert.get());
-      status->SetServerCert(nsc, evStatus);
-      MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-              ("AuthCertificate setting NEW cert %p", nsc.get()));
-    }
+    RefPtr<nsNSSCertificate> nsc = nsNSSCertificate::Create(cert.get());
+    status->SetServerCert(nsc, evStatus);
+
+    status->SetSucceededCertChain(Move(certList));
+    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+        ("AuthCertificate setting NEW cert %p", nsc.get()));
 
     status->SetCertificateTransparencyInfo(certificateTransparencyInfo);
   }
 
   if (rv != Success) {
     // Certificate validation failed; store the peer certificate chain on
     // infoObject so it can be used for error reporting.
-    infoObject->SetFailedCertChain(Move(peerCertChain));
+    infoObject->SetFailedCertChain(Move(certList));
     PR_SetError(MapResultToPRErrorCode(rv), 0);
   }
 
   return rv == Success ? SECSuccess : SECFailure;
 }
 
 /*static*/ SECStatus
 SSLServerCertVerificationJob::Dispatch(
--- a/security/manager/ssl/TransportSecurityInfo.cpp
+++ b/security/manager/ssl/TransportSecurityInfo.cpp
@@ -1007,16 +1007,17 @@ TransportSecurityInfo::SetStatusErrorBit
 {
   MutexAutoLock lock(mMutex);
 
   if (!mSSLStatus) {
     mSSLStatus = new nsSSLStatus();
   }
 
   mSSLStatus->SetServerCert(cert, EVStatus::NotEV);
+  mSSLStatus->SetFailedCertChain(mFailedCertChain);
 
   mSSLStatus->mHaveCertErrorBits = true;
   mSSLStatus->mIsDomainMismatch =
     collected_errors & nsICertOverrideService::ERROR_MISMATCH;
   mSSLStatus->mIsNotValidAtThisTime =
     collected_errors & nsICertOverrideService::ERROR_TIME;
   mSSLStatus->mIsUntrusted =
     collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
--- a/security/manager/ssl/nsISSLStatus.idl
+++ b/security/manager/ssl/nsISSLStatus.idl
@@ -2,20 +2,23 @@
  *
  * 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/. */
 
 #include "nsISupports.idl"
 
 interface nsIX509Cert;
+interface nsIX509CertList;
 
 [scriptable, uuid(fa9ba95b-ca3b-498a-b889-7c79cf28fee8)]
 interface nsISSLStatus : nsISupports {
   readonly attribute nsIX509Cert serverCert;
+  readonly attribute nsIX509CertList failedCertChain;
+  readonly attribute nsIX509CertList succeededCertChain;
 
   [must_use]
   readonly attribute ACString cipherName;
   [must_use]
   readonly attribute unsigned long keyLength;
   [must_use]
   readonly attribute unsigned long secretKeyLength;
   [must_use]
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -1216,26 +1216,26 @@ DetermineEVAndCTStatusAndSetNewCert(RefP
               mozilla::psm::CertVerifier::FLAG_MUST_BE_EV;
   if (!infoObject->SharedState().IsOCSPStaplingEnabled() ||
       !infoObject->SharedState().IsOCSPMustStapleEnabled()) {
     flags |= CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
   }
 
   SECOidTag evOidPolicy;
   CertificateTransparencyInfo certificateTransparencyInfo;
-  UniqueCERTCertList unusedBuiltChain;
+  UniqueCERTCertList builtChain;
   const bool saveIntermediates = false;
   mozilla::pkix::Result rv = certVerifier->VerifySSLServerCert(
     cert,
     stapledOCSPResponse,
     sctsFromTLSExtension,
     mozilla::pkix::Now(),
     infoObject,
     infoObject->GetHostName(),
-    unusedBuiltChain,
+    builtChain,
     saveIntermediates,
     flags,
     infoObject->GetOriginAttributes(),
     &evOidPolicy,
     nullptr, // OCSP stapling telemetry
     nullptr, // key size telemetry
     nullptr, // SHA-1 telemetry
     nullptr, // pinning telemetry
@@ -1250,16 +1250,17 @@ DetermineEVAndCTStatusAndSetNewCert(RefP
   } else {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("HandshakeCallback using NEW cert %p (is not EV)", nssc.get()));
     sslStatus->SetServerCert(nssc, EVStatus::NotEV);
   }
 
   if (rv == Success) {
     sslStatus->SetCertificateTransparencyInfo(certificateTransparencyInfo);
+    sslStatus->SetSucceededCertChain(Move(builtChain));
   }
 }
 
 void HandshakeCallback(PRFileDesc* fd, void* client_data) {
   nsNSSShutDownPreventionLock locker;
   SECStatus rv;
 
   nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
--- a/security/manager/ssl/nsSSLStatus.cpp
+++ b/security/manager/ssl/nsSSLStatus.cpp
@@ -6,18 +6,25 @@
 
 #include "CTVerifyResult.h"
 #include "mozilla/Casting.h"
 #include "nsSSLStatus.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIObjectInputStream.h"
 #include "nsNSSCertificate.h"
+#include "nsNSSShutDown.h"
 #include "ssl.h"
 
+
+void
+nsSSLStatus::virtualDestroyNSSReference()
+{
+}
+
 NS_IMETHODIMP
 nsSSLStatus::GetServerCert(nsIX509Cert** aServerCert)
 {
   NS_ENSURE_ARG_POINTER(aServerCert);
 
   nsCOMPtr<nsIX509Cert> cert = mServerCert;
   cert.forget(aServerCert);
   return NS_OK;
@@ -220,24 +227,41 @@ nsSSLStatus::Read(nsIObjectInputStream* 
   if (streamFormatVersion >= 2) {
     rv = aStream->ReadCString(mKeaGroup);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = aStream->ReadCString(mSignatureSchemeName);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  // Added in version 3 (see bug 1406856).
+  if (streamFormatVersion >= 3) {
+    nsCOMPtr<nsISupports> succeededCertChainSupports;
+    rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(succeededCertChainSupports));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    mSucceededCertChain = do_QueryInterface(succeededCertChainSupports);
+
+    nsCOMPtr<nsISupports> failedCertChainSupports;
+    rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(failedCertChainSupports));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    mFailedCertChain = do_QueryInterface(failedCertChainSupports);
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSSLStatus::Write(nsIObjectOutputStream* aStream)
 {
   // The current version of the binary stream format.
-  const uint8_t STREAM_FORMAT_VERSION = 2;
+  const uint8_t STREAM_FORMAT_VERSION = 3;
 
   nsresult rv = aStream->WriteCompoundObject(mServerCert,
                                              NS_GET_IID(nsIX509Cert),
                                              true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aStream->Write16(mCipherSuite);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -270,16 +294,33 @@ nsSSLStatus::Write(nsIObjectOutputStream
 
   // Added in version 2.
   rv = aStream->WriteStringZ(mKeaGroup.get());
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aStream->WriteStringZ(mSignatureSchemeName.get());
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Added in version 3.
+  rv = NS_WriteOptionalCompoundObject(aStream,
+                                      mSucceededCertChain,
+                                      NS_GET_IID(nsIX509CertList),
+                                      true);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = NS_WriteOptionalCompoundObject(aStream,
+                                      mFailedCertChain,
+                                      NS_GET_IID(nsIX509CertList),
+                                      true);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSSLStatus::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
 {
   *aCount = 0;
   *aArray = nullptr;
@@ -349,28 +390,75 @@ nsSSLStatus::nsSSLStatus()
 , mHaveCertErrorBits(false)
 {
 }
 
 NS_IMPL_ISUPPORTS(nsSSLStatus, nsISSLStatus, nsISerializable, nsIClassInfo)
 
 nsSSLStatus::~nsSSLStatus()
 {
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return;
+  }
+  shutdown(ShutdownCalledFrom::Object);
 }
 
 void
 nsSSLStatus::SetServerCert(nsNSSCertificate* aServerCert, EVStatus aEVStatus)
 {
   MOZ_ASSERT(aServerCert);
 
   mServerCert = aServerCert;
   mIsEV = (aEVStatus == EVStatus::EV);
   mHasIsEVStatus = true;
 }
 
+nsresult
+nsSSLStatus::SetSucceededCertChain(UniqueCERTCertList aCertList)
+{
+  nsNSSShutDownPreventionLock lock;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // nsNSSCertList takes ownership of certList
+  mSucceededCertChain = new nsNSSCertList(Move(aCertList), lock);
+
+  return NS_OK;
+}
+
+void
+nsSSLStatus::SetFailedCertChain(nsIX509CertList* aX509CertList)
+{
+  mFailedCertChain = aX509CertList;
+}
+
+NS_IMETHODIMP
+nsSSLStatus::GetSucceededCertChain(nsIX509CertList** _result)
+{
+  NS_ENSURE_ARG_POINTER(_result);
+
+  nsCOMPtr<nsIX509CertList> tmpList = mSucceededCertChain;
+  tmpList.forget(_result);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSSLStatus::GetFailedCertChain(nsIX509CertList** _result)
+{
+  NS_ENSURE_ARG_POINTER(_result);
+
+  nsCOMPtr<nsIX509CertList> tmpList = mFailedCertChain;
+  tmpList.forget(_result);
+
+  return NS_OK;
+}
+
 void
 nsSSLStatus::SetCertificateTransparencyInfo(
   const mozilla::psm::CertificateTransparencyInfo& info)
 {
   using mozilla::ct::CTPolicyCompliance;
 
   mCertificateTransparencyStatus =
     nsISSLStatus::CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE;
--- a/security/manager/ssl/nsSSLStatus.h
+++ b/security/manager/ssl/nsSSLStatus.h
@@ -7,50 +7,59 @@
 #ifndef _NSSSLSTATUS_H
 #define _NSSSLSTATUS_H
 
 #include "CertVerifier.h" // For CertificateTransparencyInfo
 #include "nsISSLStatus.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsIX509Cert.h"
+#include "nsIX509CertList.h"
 #include "nsISerializable.h"
 #include "nsIClassInfo.h"
+#include "nsNSSCertificate.h"
+#include "ScopedNSSTypes.h"
 
 class nsNSSCertificate;
 
 enum class EVStatus {
   NotEV = 0,
   EV = 1,
 };
 
 class nsSSLStatus final
   : public nsISSLStatus
   , public nsISerializable
   , public nsIClassInfo
+  , public nsNSSShutDownObject
 {
 protected:
   virtual ~nsSSLStatus();
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISSLSTATUS
   NS_DECL_NSISERIALIZABLE
   NS_DECL_NSICLASSINFO
 
   nsSSLStatus();
 
   void SetServerCert(nsNSSCertificate* aServerCert, EVStatus aEVStatus);
 
+  nsresult SetSucceededCertChain(mozilla::UniqueCERTCertList certList);
+  void SetFailedCertChain(nsIX509CertList* x509CertList);
+
   bool HasServerCert() {
     return mServerCert != nullptr;
   }
 
   void SetCertificateTransparencyInfo(
     const mozilla::psm::CertificateTransparencyInfo& info);
 
+  virtual void virtualDestroyNSSReference() override;
+
   /* public for initilization in this file */
   uint16_t mCipherSuite;
   uint16_t mProtocolVersion;
   uint16_t mCertificateTransparencyStatus;
   nsCString mKeaGroup;
   nsCString mSignatureSchemeName;
 
   bool mIsDomainMismatch;
@@ -62,15 +71,17 @@ public:
   bool mHaveCipherSuiteAndProtocol;
 
   /* mHaveCertErrrorBits is relied on to determine whether or not a SPDY
      connection is eligible for joining in nsNSSSocketInfo::JoinConnection() */
   bool mHaveCertErrorBits;
 
 private:
   nsCOMPtr<nsIX509Cert> mServerCert;
+  nsCOMPtr<nsIX509CertList> mSucceededCertChain;
+  nsCOMPtr<nsIX509CertList> mFailedCertChain;
 };
 
 #define NS_SSLSTATUS_CID \
 { 0xe2f14826, 0x9e70, 0x4647, \
   { 0xb2, 0x3f, 0x10, 0x10, 0xf5, 0x12, 0x46, 0x28 } }
 
 #endif
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -116,16 +116,26 @@ const NO_FLAGS = 0;
 // with no newlines or BEGIN/END headers. This is a helper function to convert
 // PEM to the format that nsIX509CertDB requires.
 function pemToBase64(pem) {
   return pem.replace(/-----BEGIN CERTIFICATE-----/, "")
             .replace(/-----END CERTIFICATE-----/, "")
             .replace(/[\r\n]/g, "");
 }
 
+function build_cert_chain(certNames) {
+  let certList = Cc["@mozilla.org/security/x509certlist;1"]
+                   .createInstance(Ci.nsIX509CertList);
+  certNames.forEach(function(certName) {
+    let cert = constructCertFromFile("bad_certs/" + certName + ".pem");
+    certList.addCert(cert);
+  });
+  return certList;
+}
+
 function readFile(file) {
   let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
                   .createInstance(Ci.nsIFileInputStream);
   fstream.init(file, -1, 0, 0);
   let data = NetUtil.readInputStreamToString(fstream, fstream.available());
   fstream.close();
   return data;
 }
@@ -697,39 +707,48 @@ function add_cert_override(aHost, aExpec
               "Actual error message should match expected error regexp");
   }
   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);
+
   Assert.equal(bits, aExpectedBits,
                "Actual and expected override bits should match");
   let cert = sslstatus.serverCert;
   let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
                               .getService(Ci.nsICertOverrideService);
   certOverrideService.rememberValidityOverride(aHost, 8443, cert, aExpectedBits,
                                                true);
 }
 
 // Given a host, expected error bits (see nsICertOverrideService.idl), an
 // expected error code, and optionally a regular expression that the resulting
 // error message must match, tests that an initial connection to the host fails
 // with the expected errors and that adding an override results in a subsequent
 // connection succeeding.
 function add_cert_override_test(aHost, aExpectedBits, aExpectedError,
-                                aExpectedErrorRegexp = undefined) {
+                                aExpectedErrorRegexp = undefined,
+                                aExpectedSSLStatus = undefined) {
   add_connection_test(aHost, aExpectedError, null,
                       add_cert_override.bind(this, aHost, aExpectedBits,
                                              aExpectedErrorRegexp));
   add_connection_test(aHost, PRErrorCodeSuccess, null, aSecurityInfo => {
     Assert.ok(aSecurityInfo.securityState &
               Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
               "Cert override flag should be set on the security state");
+    if (aExpectedSSLStatus) {
+      let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+                                  .SSLStatus;
+      if (aExpectedSSLStatus.failedCertChain) {
+        ok(aExpectedSSLStatus.failedCertChain.equals(sslstatus.failedCertChain));
+      }
+    }
   });
 }
 
 // Helper function for add_prevented_cert_override_test. This is much like
 // add_cert_override except it may not be the case that the connection has an
 // SSLStatus set on it. In this case, the error was not overridable anyway, so
 // we consider it a success.
 function attempt_adding_cert_override(aHost, aExpectedBits, aSecurityInfo) {
--- a/security/manager/ssl/tests/unit/test_cert_chains.js
+++ b/security/manager/ssl/tests/unit/test_cert_chains.js
@@ -1,25 +1,15 @@
 // -*- 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";
 
-function build_cert_chain(certNames) {
-  let certList = Cc["@mozilla.org/security/x509certlist;1"]
-                   .createInstance(Ci.nsIX509CertList);
-  certNames.forEach(function(certName) {
-    let cert = constructCertFromFile("bad_certs/" + certName + ".pem");
-    certList.addCert(cert);
-  });
-  return certList;
-}
-
 function test_cert_equals() {
   let certA = constructCertFromFile("bad_certs/default-ee.pem");
   let certB = constructCertFromFile("bad_certs/default-ee.pem");
   let certC = constructCertFromFile("bad_certs/expired-ee.pem");
 
   ok(certA != certB,
      "Cert objects constructed from the same file should not be equal" +
      " according to the equality operators");
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_ssl_status.js
@@ -0,0 +1,55 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 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";
+
+do_get_profile();
+
+function run_test() {
+  Services.prefs.setIntPref("security.OCSP.enabled", 1);
+  add_tls_server_setup("BadCertServer", "bad_certs");
+
+  let fakeOCSPResponder = new HttpServer();
+  fakeOCSPResponder.registerPrefixHandler("/", function (request, response) {
+    response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
+  });
+  fakeOCSPResponder.start(8888);
+
+  // Test successful connection (failedCertChain should be null,
+  // succeededCertChain should be set as expected)
+  add_connection_test(
+    "good.include-subdomains.pinning.example.com", PRErrorCodeSuccess, null,
+    function withSecurityInfo(aSSLStatus) {
+      let sslstatus = aSSLStatus.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+      equal(sslstatus.failedCertChain, null,
+            "failedCertChain for a successful connection should be null");
+      ok(sslstatus.succeededCertChain.equals(build_cert_chain(["default-ee", "test-ca"])),
+            "succeededCertChain for a successful connection should be as expected");
+    }
+  );
+
+  // Test failed connection (failedCertChain should be set as expected,
+  // succeededCertChain should be null)
+  add_connection_test(
+    "expired.example.com", SEC_ERROR_EXPIRED_CERTIFICATE, null,
+    function withSecurityInfo(aSSLStatus) {
+      let sslstatus = aSSLStatus.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
+      equal(sslstatus.succeededCertChain, null,
+            "succeededCertChain for a failed connection should be null");
+      ok(sslstatus.failedCertChain.equals(build_cert_chain(["expired-ee", "test-ca"])),
+            "failedCertChain for a failed connection should be as expected");
+    }
+  );
+
+  // Ensure the correct failed cert chain is set on cert override
+  let overrideStatus = {
+    failedCertChain: build_cert_chain(["expired-ee", "test-ca"])
+  };
+  add_cert_override_test("expired.example.com",
+                         Ci.nsICertOverrideService.ERROR_TIME,
+                         SEC_ERROR_EXPIRED_CERTIFICATE, undefined,
+                         overrideStatus);
+
+  run_next_test();
+}
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -146,16 +146,17 @@ requesttimeoutfactor = 2
 [test_sdr_preexisting_with_password.js]
 # Not relevant to Android. See the comment in the test.
 skip-if = toolkit == 'android'
 [test_session_resumption.js]
 run-sequentially = hardcoded ports
 [test_signed_apps.js]
 [test_signed_dir.js]
 tags = addons psm
+[test_ssl_status.js]
 [test_sss_enumerate.js]
 [test_sss_eviction.js]
 [test_sss_originAttributes.js]
 [test_sss_readstate.js]
 [test_sss_readstate_child.js]
 support-files = sss_readstate_child_worker.js
 # bug 1124289 - run_test_in_child violates the sandbox on android
 skip-if = toolkit == 'android'