Bug 1075991 - Remember version intolerance reason code, r=keeler
authorMartin Thomson <martin.thomson@gmail.com>
Fri, 03 Oct 2014 11:01:24 -0700
changeset 233308 b1a9b31ab26e28c99b9583a31e5ea5c97f16aa68
parent 233307 3bea361c1f4014e09fdcee60db8b585b104acab9
child 233309 59ef591ec31895fff8d6ca429832e93e94462356
push id611
push userraliiev@mozilla.com
push dateMon, 05 Jan 2015 23:23:16 +0000
treeherdermozilla-release@345cd3b9c445 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1075991
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1075991 - Remember version intolerance reason code, r=keeler
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/src/nsNSSIOLayer.h
security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -788,45 +788,49 @@ nsSSLIOLayerHelpers::rememberTolerantAtV
   MutexAutoLock lock(mutex);
 
   IntoleranceEntry entry;
   if (mTLSIntoleranceInfo.Get(key, &entry)) {
     entry.AssertInvariant();
     entry.tolerant = std::max(entry.tolerant, tolerant);
     if (entry.intolerant != 0 && entry.intolerant <= entry.tolerant) {
       entry.intolerant = entry.tolerant + 1;
+      entry.intoleranceReason = 0; // lose the reason
     }
   } else {
     entry.tolerant = tolerant;
     entry.intolerant = 0;
+    entry.intoleranceReason = 0;
   }
 
   entry.AssertInvariant();
 
   mTLSIntoleranceInfo.Put(key, entry);
 }
 
 // returns true if we should retry the handshake
 bool
 nsSSLIOLayerHelpers::rememberIntolerantAtVersion(const nsACString& hostName,
                                                  int16_t port,
                                                  uint16_t minVersion,
-                                                 uint16_t intolerant)
+                                                 uint16_t intolerant,
+                                                 PRErrorCode intoleranceReason)
 {
   nsCString key;
   getSiteKey(hostName, port, key);
 
   MutexAutoLock lock(mutex);
 
   if (intolerant <= minVersion) {
     // We can't fall back any further. Assume that intolerance isn't the issue.
     IntoleranceEntry entry;
     if (mTLSIntoleranceInfo.Get(key, &entry)) {
       entry.AssertInvariant();
       entry.intolerant = 0;
+      entry.intoleranceReason = 0;
       entry.AssertInvariant();
       mTLSIntoleranceInfo.Put(key, entry);
     }
 
     return false;
   }
 
   IntoleranceEntry entry;
@@ -840,16 +844,17 @@ nsSSLIOLayerHelpers::rememberIntolerantA
       // We already know that the server is intolerant at a lower version.
       return true;
     }
   } else {
     entry.tolerant = 0;
   }
 
   entry.intolerant = intolerant;
+  entry.intoleranceReason = intoleranceReason;
   entry.AssertInvariant();
   mTLSIntoleranceInfo.Put(key, entry);
 
   return true;
 }
 
 void
 nsSSLIOLayerHelpers::adjustForTLSIntolerance(const nsACString& hostName,
@@ -874,16 +879,36 @@ nsSSLIOLayerHelpers::adjustForTLSIntoler
     // We've tried connecting at a higher range but failed, so try at the
     // version we haven't tried yet, unless we have reached the minimum.
     if (range.min < entry.intolerant) {
       range.max = entry.intolerant - 1;
     }
   }
 }
 
+PRErrorCode
+nsSSLIOLayerHelpers::getIntoleranceReason(const nsACString& hostName,
+                                          int16_t port)
+{
+  IntoleranceEntry entry;
+
+  {
+    nsCString key;
+    getSiteKey(hostName, port, key);
+
+    MutexAutoLock lock(mutex);
+    if (!mTLSIntoleranceInfo.Get(key, &entry)) {
+      return 0;
+    }
+  }
+
+  entry.AssertInvariant();
+  return entry.intoleranceReason;
+}
+
 bool nsSSLIOLayerHelpers::nsSSLIOLayerInitialized = false;
 PRDescIdentity nsSSLIOLayerHelpers::nsSSLIOLayerIdentity;
 PRDescIdentity nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity;
 PRIOMethods nsSSLIOLayerHelpers::nsSSLIOLayerMethods;
 PRIOMethods nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods;
 
 static PRStatus
 nsSSLIOLayerClose(PRFileDesc* fd)
@@ -1110,17 +1135,17 @@ retryDueToTLSIntolerance(PRErrorCode err
 
   // The difference between _PRE and _POST represents how often we avoided
   // TLS intolerance fallback due to remembered tolerance.
   Telemetry::Accumulate(pre, reason);
 
   if (!socketInfo->SharedState().IOLayerHelpers()
                  .rememberIntolerantAtVersion(socketInfo->GetHostName(),
                                               socketInfo->GetPort(),
-                                              range.min, range.max)) {
+                                              range.min, range.max, err)) {
     return false;
   }
 
   Telemetry::Accumulate(post, reason);
 
   return true;
 }
 
--- a/security/manager/ssl/src/nsNSSIOLayer.h
+++ b/security/manager/ssl/src/nsNSSIOLayer.h
@@ -179,30 +179,33 @@ public:
   void setWarnLevelMissingRFC5746(int32_t level);
   int32_t getWarnLevelMissingRFC5746();
 
 private:
   struct IntoleranceEntry
   {
     uint16_t tolerant;
     uint16_t intolerant;
+    PRErrorCode intoleranceReason;
 
     void AssertInvariant() const
     {
       MOZ_ASSERT(intolerant == 0 || tolerant < intolerant);
     }
   };
   nsDataHashtable<nsCStringHashKey, IntoleranceEntry> mTLSIntoleranceInfo;
 public:
   void rememberTolerantAtVersion(const nsACString& hostname, int16_t port,
                                  uint16_t tolerant);
   bool rememberIntolerantAtVersion(const nsACString& hostname, int16_t port,
-                                   uint16_t intolerant, uint16_t minVersion);
+                                   uint16_t intolerant, uint16_t minVersion,
+                                   PRErrorCode intoleranceReason);
   void adjustForTLSIntolerance(const nsACString& hostname, int16_t port,
                                /*in/out*/ SSLVersionRange& range);
+  PRErrorCode getIntoleranceReason(const nsACString& hostname, int16_t port);
 
   void setRenegoUnrestrictedSites(const nsCString& str);
   bool isRenegoUnrestrictedSite(const nsCString& str);
   void clearStoredData();
 
   bool mFalseStartRequireNPN;
   bool mFalseStartRequireForwardSecrecy;
 private:
--- a/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
+++ b/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "nsNSSIOLayer.h"
 #include "sslproto.h"
+#include "sslerr.h"
 
 #include "gtest/gtest.h"
 
 NS_NAMED_LITERAL_CSTRING(HOST, "example.org");
 const int16_t PORT = 443;
 
 class TLSIntoleranceTest : public ::testing::Test
 {
@@ -24,52 +25,52 @@ TEST_F(TLSIntoleranceTest, Test_1_2_thro
   {
     SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
                               SSL_LIBRARY_VERSION_TLS_1_2 };
     helpers.adjustForTLSIntolerance(HOST, PORT, range);
     ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
     ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
 
     ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
-                                                    range.min, range.max));
+                                                    range.min, range.max, 0));
   }
 
   {
     SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
                               SSL_LIBRARY_VERSION_TLS_1_2 };
     helpers.adjustForTLSIntolerance(HOST, PORT, range);
     ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
     ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
 
     ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
-                                                    range.min, range.max));
+                                                    range.min, range.max, 0));
   }
 
   {
     SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
                               SSL_LIBRARY_VERSION_TLS_1_2 };
     helpers.adjustForTLSIntolerance(HOST, PORT, range);
     ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
     ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.max);
 
     ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
-                                                    range.min, range.max));
+                                                    range.min, range.max, 0));
   }
 
   {
     SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
                               SSL_LIBRARY_VERSION_TLS_1_2 };
 
     helpers.adjustForTLSIntolerance(HOST, PORT, range);
     ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
     ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.max);
 
     // false because we reached the floor set by range.min
     ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
-                                                     range.min, range.max));
+                                                     range.min, range.max, 0));
   }
 
   {
     SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
                               SSL_LIBRARY_VERSION_TLS_1_2 };
     helpers.adjustForTLSIntolerance(HOST, PORT, range);
     ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
     // When rememberIntolerantAtVersion returns false, it also resets the
@@ -77,69 +78,108 @@ TEST_F(TLSIntoleranceTest, Test_1_2_thro
     ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
   }
 }
 
 TEST_F(TLSIntoleranceTest, Test_Tolerant_Overrides_Intolerant_1)
 {
   ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
                                                   SSL_LIBRARY_VERSION_3_0,
-                                                  SSL_LIBRARY_VERSION_TLS_1_0));
+                                                  SSL_LIBRARY_VERSION_TLS_1_0,
+                                                  0));
   helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0);
   SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
                             SSL_LIBRARY_VERSION_TLS_1_2 };
   helpers.adjustForTLSIntolerance(HOST, PORT, range);
   ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
   ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.max);
 }
 
 TEST_F(TLSIntoleranceTest, Test_Tolerant_Overrides_Intolerant_2)
 {
   ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
                                                   SSL_LIBRARY_VERSION_3_0,
-                                                  SSL_LIBRARY_VERSION_TLS_1_0));
+                                                  SSL_LIBRARY_VERSION_TLS_1_0,
+                                                  0));
   helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1);
   SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
                             SSL_LIBRARY_VERSION_TLS_1_2 };
   helpers.adjustForTLSIntolerance(HOST, PORT, range);
   ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
   ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
 }
 
 TEST_F(TLSIntoleranceTest, Test_Intolerant_Does_Not_Override_Tolerant)
 {
   // No adjustment made when there is no entry for the site.
   helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_0);
   // false because we reached the floor set by rememberTolerantAtVersion.
   ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
                                                    SSL_LIBRARY_VERSION_3_0,
-                                                   SSL_LIBRARY_VERSION_TLS_1_0));
+                                                   SSL_LIBRARY_VERSION_TLS_1_0,
+                                                   0));
   SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
                             SSL_LIBRARY_VERSION_TLS_1_2 };
   helpers.adjustForTLSIntolerance(HOST, PORT, range);
   ASSERT_EQ(SSL_LIBRARY_VERSION_3_0, range.min);
   ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
 }
 
 TEST_F(TLSIntoleranceTest, Test_Port_Is_Relevant)
 {
   helpers.rememberTolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_2);
   ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, 1,
                                                    SSL_LIBRARY_VERSION_3_0,
-                                                   SSL_LIBRARY_VERSION_TLS_1_2));
+                                                   SSL_LIBRARY_VERSION_TLS_1_2,
+                                                   0));
   ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, 2,
                                                   SSL_LIBRARY_VERSION_3_0,
-                                                  SSL_LIBRARY_VERSION_TLS_1_2));
+                                                  SSL_LIBRARY_VERSION_TLS_1_2,
+                                                  0));
 
   {
     SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
                               SSL_LIBRARY_VERSION_TLS_1_2 };
     helpers.adjustForTLSIntolerance(HOST, 1, range);
     ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
   }
 
   {
     SSLVersionRange range = { SSL_LIBRARY_VERSION_3_0,
                               SSL_LIBRARY_VERSION_TLS_1_2 };
     helpers.adjustForTLSIntolerance(HOST, 2, range);
     ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
   }
 }
+
+TEST_F(TLSIntoleranceTest, Test_Intolerance_Reason_Initial)
+{
+  ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 1));
+
+  helpers.rememberTolerantAtVersion(HOST, 2, SSL_LIBRARY_VERSION_TLS_1_2);
+  ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 2));
+}
+
+TEST_F(TLSIntoleranceTest, Test_Intolerance_Reason_Stored)
+{
+  helpers.rememberIntolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_3_0,
+                                      SSL_LIBRARY_VERSION_TLS_1_2,
+                                      SSL_ERROR_BAD_SERVER);
+  ASSERT_EQ(SSL_ERROR_BAD_SERVER, helpers.getIntoleranceReason(HOST, 1));
+
+  helpers.rememberIntolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_3_0,
+                                      SSL_LIBRARY_VERSION_TLS_1_1,
+                                      SSL_ERROR_BAD_MAC_READ);
+  ASSERT_EQ(SSL_ERROR_BAD_MAC_READ, helpers.getIntoleranceReason(HOST, 1));
+}
+
+TEST_F(TLSIntoleranceTest, Test_Intolerance_Reason_Cleared)
+{
+  ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 1));
+
+  helpers.rememberIntolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_3_0,
+                                      SSL_LIBRARY_VERSION_TLS_1_2,
+                                      SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+  ASSERT_EQ(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT, helpers.getIntoleranceReason(HOST, 1));
+
+  helpers.rememberTolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_2);
+  ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 1));
+}