Bug 1114816 - Implement TLS intolerance fallback whitelist. r=keeler
authorMasatoshi Kimura <VYV03354@nifty.ne.jp>
Thu, 29 Jan 2015 03:52:42 +0900
changeset 239754 6ebe8792139db9b734c97e6480ef4d51b4ea2d58
parent 239753 d975577991caa3a7f5aaff02f6ba5dbe16c66a7a
child 239755 b2f64e095d8e77bd3c88bd00d2854717d36c0974
push id506
push usermleibovic@mozilla.com
push dateThu, 29 Jan 2015 12:40:10 +0000
reviewerskeeler
bugs1114816
milestone38.0a1
Bug 1114816 - Implement TLS intolerance fallback whitelist. r=keeler
netwerk/base/security-prefs.js
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/src/nsNSSIOLayer.h
security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
--- a/netwerk/base/security-prefs.js
+++ b/netwerk/base/security-prefs.js
@@ -1,15 +1,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/. */
 
 pref("security.tls.version.min", 1);
 pref("security.tls.version.max", 3);
 pref("security.tls.version.fallback-limit", 3);
+# Do not add a site without filing a corresponding evangelism bug.
+# bug 1095507, www.kredodirect.com.ua
+# bug 1111354, web3.secureinternetbank.com
+# bug 1112110, cmypage.kuronekoyamato.co.jp
+# bug 1115883, www.timewarnercable.com and wayfarer.timewarnercable.com
+# bug 1126652, www.animate-onlineshop.jp
+# bug 1126654, www.gamers-onlineshop.jp
+pref("security.tls.insecure_fallback_hosts", "www.kredodirect.com.ua,web3.secureinternetbank.com,cmypage.kuronekoyamato.co.jp,www.timewarnercable.com,wayfarer.timewarnercable.com,www.animate-onlineshop.jp,www.gamers-onlineshop.jp");
 
 pref("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", false);
 pref("security.ssl.renego_unrestricted_hosts", "");
 pref("security.ssl.treat_unsafe_negotiation_as_broken", false);
 pref("security.ssl.require_safe_negotiation",  false);
 pref("security.ssl.warn_missing_rfc5746",  1);
 pref("security.ssl.enable_ocsp_stapling", true);
 pref("security.ssl.enable_false_start", true);
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -671,22 +671,20 @@ nsNSSSocketInfo::SetCertVerificationResu
 SharedSSLState&
 nsNSSSocketInfo::SharedState()
 {
   return mSharedState;
 }
 
 void nsSSLIOLayerHelpers::Cleanup()
 {
+  MutexAutoLock lock(mutex);
   mTLSIntoleranceInfo.Clear();
-
-  if (mRenegoUnrestrictedSites) {
-    delete mRenegoUnrestrictedSites;
-    mRenegoUnrestrictedSites = nullptr;
-  }
+  mRenegoUnrestrictedSites.Clear();
+  mInsecureFallbackSites.Clear();
 }
 
 static void
 nsHandleSSLError(nsNSSSocketInfo* socketInfo,
                  ::mozilla::psm::SSLErrorMessageType errtype,
                  PRErrorCode err)
 {
   if (!NS_IsMainThread()) {
@@ -858,25 +856,36 @@ nsSSLIOLayerHelpers::forgetIntolerance(c
 
     entry.AssertInvariant();
     mTLSIntoleranceInfo.Put(key, entry);
   }
 
   return tolerant;
 }
 
+bool
+nsSSLIOLayerHelpers::fallbackLimitReached(const nsACString& hostName,
+                                          uint16_t intolerant)
+{
+  MutexAutoLock lock(mutex);
+  if (mInsecureFallbackSites.Contains(hostName)) {
+    return intolerant <= SSL_LIBRARY_VERSION_TLS_1_0;
+  }
+  return intolerant <= mVersionFallbackLimit;
+}
+
 // returns true if we should retry the handshake
 bool
 nsSSLIOLayerHelpers::rememberIntolerantAtVersion(const nsACString& hostName,
                                                  int16_t port,
                                                  uint16_t minVersion,
                                                  uint16_t intolerant,
                                                  PRErrorCode intoleranceReason)
 {
-  if (intolerant <= minVersion || intolerant <= mVersionFallbackLimit) {
+  if (intolerant <= minVersion || fallbackLimitReached(hostName, intolerant)) {
     // We can't fall back any further. Assume that intolerance isn't the issue.
     uint32_t tolerant = forgetIntolerance(hostName, port);
     // If we know the server is tolerant at the version, we don't have to
     // gather the telemetry.
     if (intolerant <= tolerant) {
       return false;
     }
 
@@ -1439,18 +1448,17 @@ nsSSLIOLayerPoll(PRFileDesc* fd, int16_t
   // cert validation is complete.
   int16_t result = fd->lower->methods->poll(fd->lower, in_flags, out_flags);
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] poll SSL socket returned %d\n",
                                     (void*) fd, (int) result));
   return result;
 }
 
 nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
-  : mRenegoUnrestrictedSites(nullptr)
-  , mTreatUnsafeNegotiationAsBroken(false)
+  : mTreatUnsafeNegotiationAsBroken(false)
   , mWarnLevelMissingRFC5746(1)
   , mTLSIntoleranceInfo()
   , mFalseStartRequireNPN(true)
   , mVersionFallbackLimit(SSL_LIBRARY_VERSION_TLS_1_0)
   , mutex("nsSSLIOLayerHelpers.mutex")
 {
 }
 
@@ -1650,35 +1658,37 @@ NS_IMPL_ISUPPORTS(PrefObserver, nsIObser
 NS_IMETHODIMP
 PrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
                       const char16_t* someData)
 {
   if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
     NS_ConvertUTF16toUTF8 prefName(someData);
 
     if (prefName.EqualsLiteral("security.ssl.renego_unrestricted_hosts")) {
-      nsCString unrestricted_hosts;
-      Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
-      if (!unrestricted_hosts.IsEmpty()) {
-        mOwner->setRenegoUnrestrictedSites(unrestricted_hosts);
-      }
+      nsCString unrestrictedHosts;
+      Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestrictedHosts);
+      mOwner->setSiteList(mOwner->mRenegoUnrestrictedSites, unrestrictedHosts);
     } else if (prefName.EqualsLiteral("security.ssl.treat_unsafe_negotiation_as_broken")) {
       bool enabled;
       Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
       mOwner->setTreatUnsafeNegotiationAsBroken(enabled);
     } else if (prefName.EqualsLiteral("security.ssl.warn_missing_rfc5746")) {
       int32_t warnLevel = 1;
       Preferences::GetInt("security.ssl.warn_missing_rfc5746", &warnLevel);
       mOwner->setWarnLevelMissingRFC5746(warnLevel);
     } else if (prefName.EqualsLiteral("security.ssl.false_start.require-npn")) {
       mOwner->mFalseStartRequireNPN =
         Preferences::GetBool("security.ssl.false_start.require-npn",
                              FALSE_START_REQUIRE_NPN_DEFAULT);
     } else if (prefName.EqualsLiteral("security.tls.version.fallback-limit")) {
       mOwner->loadVersionFallbackLimit();
+    } else if (prefName.EqualsLiteral("security.tls.insecure_fallback_hosts")) {
+      nsCString insecureFallbackHosts;
+      Preferences::GetCString("security.tls.insecure_fallback_hosts", &insecureFallbackHosts);
+      mOwner->setInsecureFallbackSites(insecureFallbackHosts);
     }
   }
   return NS_OK;
 }
 
 static int32_t
 PlaintextRecv(PRFileDesc* fd, void* buf, int32_t amount, int flags,
               PRIntervalTime timeout)
@@ -1705,16 +1715,20 @@ nsSSLIOLayerHelpers::~nsSSLIOLayerHelper
     Preferences::RemoveObserver(mPrefObserver,
       "security.ssl.renego_unrestricted_hosts");
     Preferences::RemoveObserver(mPrefObserver,
         "security.ssl.treat_unsafe_negotiation_as_broken");
     Preferences::RemoveObserver(mPrefObserver,
         "security.ssl.warn_missing_rfc5746");
     Preferences::RemoveObserver(mPrefObserver,
         "security.ssl.false_start.require-npn");
+    Preferences::RemoveObserver(mPrefObserver,
+        "security.tls.version.fallback-limit");
+    Preferences::RemoveObserver(mPrefObserver,
+        "security.tls.insecure_fallback_hosts");
   }
 }
 
 nsresult
 nsSSLIOLayerHelpers::Init()
 {
   if (!nsSSLIOLayerInitialized) {
     nsSSLIOLayerInitialized = true;
@@ -1753,48 +1767,49 @@ nsSSLIOLayerHelpers::Init()
     nsSSLIOLayerMethods.read = nsSSLIOLayerRead;
     nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll;
 
     nsSSLPlaintextLayerIdentity = PR_GetUniqueIdentity("Plaintxext PSM layer");
     nsSSLPlaintextLayerMethods  = *PR_GetDefaultIOMethods();
     nsSSLPlaintextLayerMethods.recv = PlaintextRecv;
   }
 
-  mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>();
-
-  nsCString unrestricted_hosts;
-  Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
-  if (!unrestricted_hosts.IsEmpty()) {
-    setRenegoUnrestrictedSites(unrestricted_hosts);
-  }
+  nsCString unrestrictedHosts;
+  Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestrictedHosts);
+  setSiteList(mRenegoUnrestrictedSites, unrestrictedHosts);
 
   bool enabled = false;
   Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
   setTreatUnsafeNegotiationAsBroken(enabled);
 
   int32_t warnLevel = 1;
   Preferences::GetInt("security.ssl.warn_missing_rfc5746", &warnLevel);
   setWarnLevelMissingRFC5746(warnLevel);
 
   mFalseStartRequireNPN =
     Preferences::GetBool("security.ssl.false_start.require-npn",
                          FALSE_START_REQUIRE_NPN_DEFAULT);
   loadVersionFallbackLimit();
+  nsCString insecureFallbackHosts;
+  Preferences::GetCString("security.tls.insecure_fallback_hosts", &insecureFallbackHosts);
+  setInsecureFallbackSites(insecureFallbackHosts);
 
   mPrefObserver = new PrefObserver(this);
   Preferences::AddStrongObserver(mPrefObserver,
                                  "security.ssl.renego_unrestricted_hosts");
   Preferences::AddStrongObserver(mPrefObserver,
                                  "security.ssl.treat_unsafe_negotiation_as_broken");
   Preferences::AddStrongObserver(mPrefObserver,
                                  "security.ssl.warn_missing_rfc5746");
   Preferences::AddStrongObserver(mPrefObserver,
                                  "security.ssl.false_start.require-npn");
   Preferences::AddStrongObserver(mPrefObserver,
                                  "security.tls.version.fallback-limit");
+  Preferences::AddStrongObserver(mPrefObserver,
+                                 "security.tls.insecure_fallback_hosts");
   return NS_OK;
 }
 
 void
 nsSSLIOLayerHelpers::loadVersionFallbackLimit()
 {
   // see nsNSSComponent::setEnabledTLSVersions for pref handling rules
   uint32_t limit = Preferences::GetUint("security.tls.version.fallback-limit",
@@ -1805,49 +1820,49 @@ nsSSLIOLayerHelpers::loadVersionFallback
   nsNSSComponent::FillTLSVersionRange(filledInRange, limit, limit, defaults);
 
   mVersionFallbackLimit = filledInRange.max;
 }
 
 void
 nsSSLIOLayerHelpers::clearStoredData()
 {
-  mRenegoUnrestrictedSites->Clear();
+  MutexAutoLock lock(mutex);
+  mRenegoUnrestrictedSites.Clear();
+  mInsecureFallbackSites.Clear();
   mTLSIntoleranceInfo.Clear();
 }
 
 void
-nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(const nsCString& str)
+nsSSLIOLayerHelpers::setSiteList(nsTHashtable<nsCStringHashKey>& sites,
+                                 const nsCString& str)
 {
   MutexAutoLock lock(mutex);
 
-  if (mRenegoUnrestrictedSites) {
-    delete mRenegoUnrestrictedSites;
-    mRenegoUnrestrictedSites = nullptr;
+  sites.Clear();
+
+  if (str.IsEmpty()) {
+    return;
   }
 
-  mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>();
-  if (!mRenegoUnrestrictedSites)
-    return;
-
   nsCCharSeparatedTokenizer toker(str, ',');
 
   while (toker.hasMoreTokens()) {
     const nsCSubstring& host = toker.nextToken();
     if (!host.IsEmpty()) {
-      mRenegoUnrestrictedSites->PutEntry(host);
+      sites.PutEntry(host);
     }
   }
 }
 
 bool
 nsSSLIOLayerHelpers::isRenegoUnrestrictedSite(const nsCString& str)
 {
   MutexAutoLock lock(mutex);
-  return mRenegoUnrestrictedSites->Contains(str);
+  return mRenegoUnrestrictedSites.Contains(str);
 }
 
 void
 nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(bool broken)
 {
   MutexAutoLock lock(mutex);
   mTreatUnsafeNegotiationAsBroken = broken;
 }
--- a/security/manager/ssl/src/nsNSSIOLayer.h
+++ b/security/manager/ssl/src/nsNSSIOLayer.h
@@ -181,17 +181,17 @@ public:
   void Cleanup();
 
   static bool nsSSLIOLayerInitialized;
   static PRDescIdentity nsSSLIOLayerIdentity;
   static PRDescIdentity nsSSLPlaintextLayerIdentity;
   static PRIOMethods nsSSLIOLayerMethods;
   static PRIOMethods nsSSLPlaintextLayerMethods;
 
-  nsTHashtable<nsCStringHashKey>* mRenegoUnrestrictedSites;
+  nsTHashtable<nsCStringHashKey> mRenegoUnrestrictedSites;
   bool mTreatUnsafeNegotiationAsBroken;
   int32_t mWarnLevelMissingRFC5746;
 
   void setTreatUnsafeNegotiationAsBroken(bool broken);
   bool treatUnsafeNegotiationAsBroken();
   void setWarnLevelMissingRFC5746(int32_t level);
   int32_t getWarnLevelMissingRFC5746();
 
@@ -204,36 +204,46 @@ private:
     StrongCipherStatus strongCipherStatus;
 
     void AssertInvariant() const
     {
       MOZ_ASSERT(intolerant == 0 || tolerant < intolerant);
     }
   };
   nsDataHashtable<nsCStringHashKey, IntoleranceEntry> mTLSIntoleranceInfo;
+  // Sites that require insecure fallback to TLS 1.0, set by the pref
+  // security.tls.insecure_fallback_hosts, which is a comma-delimited
+  // list of domain names.
+  nsTHashtable<nsCStringHashKey> mInsecureFallbackSites;
 public:
   void rememberTolerantAtVersion(const nsACString& hostname, int16_t port,
                                  uint16_t tolerant);
+  bool fallbackLimitReached(const nsACString& hostname, uint16_t intolerant);
   bool rememberIntolerantAtVersion(const nsACString& hostname, int16_t port,
                                    uint16_t intolerant, uint16_t minVersion,
                                    PRErrorCode intoleranceReason);
   bool rememberStrongCiphersFailed(const nsACString& hostName, int16_t port,
                                    PRErrorCode intoleranceReason);
   // returns the known tolerant version
   // or 0 if there is no known tolerant version
   uint16_t forgetIntolerance(const nsACString& hostname, int16_t port);
   void adjustForTLSIntolerance(const nsACString& hostname, int16_t port,
                                /*in/out*/ SSLVersionRange& range,
                                /*out*/ StrongCipherStatus& strongCipherStatus);
   PRErrorCode getIntoleranceReason(const nsACString& hostname, int16_t port);
 
-  void setRenegoUnrestrictedSites(const nsCString& str);
+  void setSiteList(nsTHashtable<nsCStringHashKey>& sites,
+                   const nsCString& str);
   bool isRenegoUnrestrictedSite(const nsCString& str);
   void clearStoredData();
   void loadVersionFallbackLimit();
+  void setInsecureFallbackSites(const nsCString& str)
+  {
+    setSiteList(mInsecureFallbackSites, str);
+  }
 
   bool mFalseStartRequireNPN;
   bool mFalseStartRequireForwardSecrecy;
   uint16_t mVersionFallbackLimit;
 private:
   mozilla::Mutex mutex;
   nsCOMPtr<nsIObserver> mPrefObserver;
 };
--- a/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
+++ b/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
@@ -512,8 +512,87 @@ TEST_F(TLSIntoleranceTest, TLS_Dont_Forg
                               SSL_LIBRARY_VERSION_TLS_1_2 };
     StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
     helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
     ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
     ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
     ASSERT_EQ(StrongCiphersWorked, strongCipherStatus);
   }
 }
+
+TEST_F(TLSIntoleranceTest, TLS_Per_Site_Fallback_Limit)
+{
+  NS_NAMED_LITERAL_CSTRING(example_com, "example.com");
+  NS_NAMED_LITERAL_CSTRING(example_net, "example.net");
+  NS_NAMED_LITERAL_CSTRING(example_org, "example.org");
+
+  helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_0;
+
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+  helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_2;
+
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+  helpers.setInsecureFallbackSites(example_com);
+
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+  helpers.setInsecureFallbackSites(NS_LITERAL_CSTRING("example.com,example.net"));
+
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+  helpers.setInsecureFallbackSites(example_net);
+
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+  helpers.setInsecureFallbackSites(EmptyCString());
+
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+  ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+}