--- 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/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));
+}