Bug 1114816 - Implement TLS intolerance fallback whitelist. r=keeler

This commit is contained in:
Masatoshi Kimura 2015-01-29 03:52:42 +09:00
parent 984b596986
commit 62c0790149
4 changed files with 144 additions and 32 deletions

View File

@ -5,6 +5,14 @@
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", "");

View File

@ -676,12 +676,10 @@ nsNSSSocketInfo::SharedState()
void nsSSLIOLayerHelpers::Cleanup()
{
MutexAutoLock lock(mutex);
mTLSIntoleranceInfo.Clear();
if (mRenegoUnrestrictedSites) {
delete mRenegoUnrestrictedSites;
mRenegoUnrestrictedSites = nullptr;
}
mRenegoUnrestrictedSites.Clear();
mInsecureFallbackSites.Clear();
}
static void
@ -863,6 +861,17 @@ nsSSLIOLayerHelpers::forgetIntolerance(const nsACString& hostName,
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,
@ -871,7 +880,7 @@ nsSSLIOLayerHelpers::rememberIntolerantAtVersion(const nsACString& hostName,
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
@ -1444,8 +1453,7 @@ nsSSLIOLayerPoll(PRFileDesc* fd, int16_t in_flags, int16_t* out_flags)
}
nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
: mRenegoUnrestrictedSites(nullptr)
, mTreatUnsafeNegotiationAsBroken(false)
: mTreatUnsafeNegotiationAsBroken(false)
, mWarnLevelMissingRFC5746(1)
, mTLSIntoleranceInfo()
, mFalseStartRequireNPN(true)
@ -1655,11 +1663,9 @@ PrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
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);
@ -1674,6 +1680,10 @@ PrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
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;
@ -1710,6 +1720,10 @@ nsSSLIOLayerHelpers::~nsSSLIOLayerHelpers()
"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");
}
}
@ -1758,13 +1772,9 @@ nsSSLIOLayerHelpers::Init()
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);
@ -1778,6 +1788,9 @@ nsSSLIOLayerHelpers::Init()
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,
@ -1790,6 +1803,8 @@ nsSSLIOLayerHelpers::Init()
"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;
}
@ -1810,30 +1825,30 @@ nsSSLIOLayerHelpers::loadVersionFallbackLimit()
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();
mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>();
if (!mRenegoUnrestrictedSites)
if (str.IsEmpty()) {
return;
}
nsCCharSeparatedTokenizer toker(str, ',');
while (toker.hasMoreTokens()) {
const nsCSubstring& host = toker.nextToken();
if (!host.IsEmpty()) {
mRenegoUnrestrictedSites->PutEntry(host);
sites.PutEntry(host);
}
}
}
@ -1842,7 +1857,7 @@ bool
nsSSLIOLayerHelpers::isRenegoUnrestrictedSite(const nsCString& str)
{
MutexAutoLock lock(mutex);
return mRenegoUnrestrictedSites->Contains(str);
return mRenegoUnrestrictedSites.Contains(str);
}
void

View File

@ -186,7 +186,7 @@ public:
static PRIOMethods nsSSLIOLayerMethods;
static PRIOMethods nsSSLPlaintextLayerMethods;
nsTHashtable<nsCStringHashKey>* mRenegoUnrestrictedSites;
nsTHashtable<nsCStringHashKey> mRenegoUnrestrictedSites;
bool mTreatUnsafeNegotiationAsBroken;
int32_t mWarnLevelMissingRFC5746;
@ -209,9 +209,14 @@ private:
}
};
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);
@ -225,10 +230,15 @@ public:
/*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;

View File

@ -517,3 +517,82 @@ TEST_F(TLSIntoleranceTest, TLS_Dont_Forget_Tolerance)
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));
}