Bug 1215796 - Remove the static fallback whitelist. r=keeler

This commit is contained in:
Masatoshi Kimura 2015-10-22 21:37:40 +09:00
parent cb40608ac4
commit 50b87b30dd
6 changed files with 2 additions and 1460 deletions

View File

@ -6,7 +6,6 @@ pref("security.tls.version.min", 1);
pref("security.tls.version.max", 3);
pref("security.tls.version.fallback-limit", 3);
pref("security.tls.insecure_fallback_hosts", "");
pref("security.tls.insecure_fallback_hosts.use_static_list", false);
pref("security.tls.unrestricted_rc4_fallback", false);
pref("security.ssl.treat_unsafe_negotiation_as_broken", false);

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,6 @@
#include "pkix/pkixtypes.h"
#include "nsNSSComponent.h"
#include "mozilla/BinarySearch.h"
#include "mozilla/Casting.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/UniquePtr.h"
@ -47,8 +46,6 @@
#include <algorithm>
#include "IntolerantFallbackList.inc"
using namespace mozilla;
using namespace mozilla::psm;
@ -734,7 +731,8 @@ bool
nsSSLIOLayerHelpers::fallbackLimitReached(const nsACString& hostName,
uint16_t intolerant)
{
if (isInsecureFallbackSite(hostName)) {
MutexAutoLock lock(mutex);
if (mInsecureFallbackSites.Contains(hostName)) {
return intolerant <= SSL_LIBRARY_VERSION_TLS_1_0;
}
return intolerant <= mVersionFallbackLimit;
@ -1325,7 +1323,6 @@ nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
: mTreatUnsafeNegotiationAsBroken(false)
, mTLSIntoleranceInfo()
, mFalseStartRequireNPN(false)
, mUseStaticFallbackList(true)
, mUnrestrictedRC4Fallback(false)
, mVersionFallbackLimit(SSL_LIBRARY_VERSION_TLS_1_0)
, mutex("nsSSLIOLayerHelpers.mutex")
@ -1548,9 +1545,6 @@ PrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
if (mOwner->isPublic()) {
mOwner->initInsecureFallbackSites();
}
} else if (prefName.EqualsLiteral("security.tls.insecure_fallback_hosts.use_static_list")) {
mOwner->mUseStaticFallbackList =
Preferences::GetBool("security.tls.insecure_fallback_hosts.use_static_list", true);
} else if (prefName.EqualsLiteral("security.tls.unrestricted_rc4_fallback")) {
mOwner->mUnrestrictedRC4Fallback =
Preferences::GetBool("security.tls.unrestricted_rc4_fallback", false);
@ -1649,8 +1643,6 @@ nsSSLIOLayerHelpers::Init()
FALSE_START_REQUIRE_NPN_DEFAULT);
loadVersionFallbackLimit();
initInsecureFallbackSites();
mUseStaticFallbackList =
Preferences::GetBool("security.tls.insecure_fallback_hosts.use_static_list", true);
mUnrestrictedRC4Fallback =
Preferences::GetBool("security.tls.unrestricted_rc4_fallback", false);
@ -1807,52 +1799,6 @@ nsSSLIOLayerHelpers::removeInsecureFallbackSite(const nsACString& hostname,
}
}
struct FallbackListComparator
{
explicit FallbackListComparator(const char* aTarget)
: mTarget(aTarget)
{}
int operator()(const char* aVal) const {
return strcmp(mTarget, aVal);
}
private:
const char* mTarget;
};
static const char* const kFallbackWildcardList[] =
{
".eur.xerox.com", // bug 1187215
".kuronekoyamato.co.jp", // bug 1128366
".wildcard.test",
};
bool
nsSSLIOLayerHelpers::isInsecureFallbackSite(const nsACString& hostname)
{
size_t match;
if (mUseStaticFallbackList) {
const char* host = PromiseFlatCString(hostname).get();
if (BinarySearchIf(kIntolerantFallbackList, 0,
ArrayLength(kIntolerantFallbackList),
FallbackListComparator(host), &match)) {
return true;
}
for (size_t i = 0; i < ArrayLength(kFallbackWildcardList); ++i) {
size_t hostLen = hostname.Length();
const char* target = kFallbackWildcardList[i];
size_t targetLen = strlen(target);
if (hostLen > targetLen &&
!memcmp(host + hostLen - targetLen, target, targetLen)) {
return true;
}
}
}
MutexAutoLock lock(mutex);
return mInsecureFallbackSites.Contains(hostname);
}
void
nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(bool broken)
{

View File

@ -230,13 +230,8 @@ public:
bool isPublic() const;
void addInsecureFallbackSite(const nsCString& hostname, bool temporary);
void removeInsecureFallbackSite(const nsACString& hostname, uint16_t port);
bool isInsecureFallbackSite(const nsACString& hostname);
bool mFalseStartRequireNPN;
// Use the static list of sites that require insecure fallback
// to TLS 1.0 if true, set by the pref
// security.tls.insecure_fallback_hosts.use_static_list.
bool mUseStaticFallbackList;
bool mUnrestrictedRC4Fallback;
uint16_t mVersionFallbackLimit;
private:

View File

@ -566,67 +566,3 @@ TEST_F(TLSIntoleranceTest, TLS_Per_Site_Fallback_Limit)
ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
}
TEST_F(TLSIntoleranceTest, TLS_Static_Fallback_List)
{
NS_NAMED_LITERAL_CSTRING(fallback_test, "fallback.test");
NS_NAMED_LITERAL_CSTRING(no_fallback_test, "no.fallback.test");
NS_NAMED_LITERAL_CSTRING(wildcard_test, "wildcard.test");
NS_NAMED_LITERAL_CSTRING(a_wildcard_test, "a.wildcard.test");
NS_NAMED_LITERAL_CSTRING(long_example_wildcard_test, "long.example.wildcard.test");
helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_0;
helpers.mUseStaticFallbackList = false;
ASSERT_FALSE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_FALSE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_FALSE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_FALSE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_FALSE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_FALSE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_FALSE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_FALSE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_FALSE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_FALSE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0));
helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_2;
ASSERT_TRUE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_TRUE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_TRUE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_TRUE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_TRUE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_TRUE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0));
helpers.mUseStaticFallbackList = true;
ASSERT_FALSE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_FALSE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(fallback_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(no_fallback_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_FALSE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_FALSE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(a_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0));
ASSERT_FALSE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_2));
ASSERT_FALSE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_1));
ASSERT_TRUE(helpers.fallbackLimitReached(long_example_wildcard_test, SSL_LIBRARY_VERSION_TLS_1_0));
}

View File

@ -1,264 +0,0 @@
/* 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/. */
// How to run this file:
// 1. [obtain firefox source code]
// 2. [build/obtain firefox binaries]
// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \
// [path to]/genIntolerantFallbackList.js \
// [absolute path to]/IntolerantFallbackList.inc
var { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components;
const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
const SEC_ERROR_UNKNOWN_ISSUER = 0x805a1ff3;
const OUTPUT = "IntolerantFallbackList.inc";
const ERROR_OUTPUT = "IntolerantFallbackList.errors";
const MAX_CONCURRENT_REQUESTS = 5;
const MAX_RETRIES = 3;
const REQUEST_TIMEOUT = 30 * 1000;
const TEST_DOMAINS = {
"fallback.test": true,
};
const FILE_HEADER = `/* 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/. */
///////////////////////////////////////////////////////////////////////////////
// This is an automatically generated file. If you're not
// nsNSSIOLayer.cpp, you shouldn't be #including it.
///////////////////////////////////////////////////////////////////////////////
static const char* const kIntolerantFallbackList[] =
{
`;
const FILE_FOOTER = "};\n";
var errorTable = {};
var errorWithoutFallbacks = {};
try {
if (arguments.length != 1) {
throw "Usage: genIntolerantFallbackList.js " +
"<absolute path to IntolerantFallbackList.inc> ";
}
// initialize the error message table
for (let name in Cr) {
errorTable[Cr[name]] = name;
}
// disable the current fallback list so it won't interfere with requests we make
Services.prefs.setBoolPref("security.tls.insecure_fallback_hosts.use_static_list", false);
// get the current preload list
let gIntolerantFallbackList = readCurrentList(arguments[0]);
// get the fallback status of each host without whitelist
let fallbackStatuses = [];
getFallbackStatuses(gIntolerantFallbackList, fallbackStatuses);
// reenable the current fallback list
Services.prefs.clearUserPref("security.tls.insecure_fallback_hosts.use_static_list");
// get the fallback status of each host with whitelist
for (let entry of fallbackStatuses) {
entry.errorWithoutFallback = entry.error;
delete entry.error;
entry.retries = MAX_RETRIES;
}
gIntolerantFallbackList = fallbackStatuses;
fallbackStatuses = [];
getFallbackStatuses(gIntolerantFallbackList, fallbackStatuses);
// sort the hosts alphabetically
fallbackStatuses.sort(function(a, b) {
return (a.name > b.name ? 1 : (a.name < b.name ? -1 : 0));
});
// write the results to a file (this is where we filter out hosts that we
// either could connect to without list, or couldn't connect to with list)
output(fallbackStatuses);
} catch (e) {
dump([e, e.stack].join("\n"));
throw e;
}
function readCurrentList(filename) {
let currentHosts = [];
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(filename);
let fis = Cc["@mozilla.org/network/file-input-stream;1"]
.createInstance(Ci.nsILineInputStream);
fis.init(file, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF);
let line = {};
let entryRegex = / "([^"]*)",(?: \/\/ (.*))?/;
while (fis.readLine(line)) {
let match = entryRegex.exec(line.value);
if (match) {
currentHosts.push({
name: match[1],
comment: match[2],
retries: MAX_RETRIES}
);
}
}
return currentHosts;
}
function getFallbackStatuses(inHosts, outStatuses) {
const expectedOutputLength = inHosts.length;
let tmpOutput = [];
function handleOneHost() {
let host = inHosts.shift();
dump("spinning off request to '" + host.name + "' (remaining retries: " +
host.retries + ")\n");
getFallbackStatus(host, tmpOutput);
}
for (let i = 0; i < MAX_CONCURRENT_REQUESTS && inHosts.length > 0; i++) {
handleOneHost();
}
while (outStatuses.length != expectedOutputLength) {
waitForAResponse(tmpOutput);
let response = tmpOutput.shift();
dump("request to '" + response.name + "' finished: " +
errorToString(response.error) + "\n");
outStatuses.push(response);
if (inHosts.length > 0) {
handleOneHost();
}
}
}
function getFallbackStatus(host, resultList) {
if (TEST_DOMAINS[host.name]) {
host.error = Cr.NS_OK;
host.retries--;
resultList.push(host);
return;
}
let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
let inResultList = false;
let uri = "https://" + host.name + "/";
req.open("GET", uri, true);
req.timeout = REQUEST_TIMEOUT;
req.channel.notificationCallbacks = new RedirectAndAuthStopper();
req.onreadystatechange = function(event) {
if (!inResultList && req.readyState == 4) {
inResultList = true;
// If the url is changed, a redirect happened that means
// a successfull TLS handshake. Treat it as success rather than
// using a status from the redirect target.
host.error = uri == req.responseURL ? req.channel.status : Cr.NS_OK;
host.retries--;
resultList.push(host);
}
};
try {
req.send();
}
catch (e) {
dump("ERROR: exception making request to " + host.name + ": " + e + "\n");
}
}
// RedirectAndAuthStopper prevents redirects and HTTP authentication
function RedirectAndAuthStopper() {};
RedirectAndAuthStopper.prototype = {
// nsIChannelEventSink
asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
throw Cr.NS_ERROR_ENTITY_CHANGED;
},
// nsIAuthPrompt2
promptAuth: function(channel, level, authInfo) {
return false;
},
asyncPromptAuth: function(channel, callback, context, level, authInfo) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
getInterface: function(iid) {
return this.QueryInterface(iid);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink,
Ci.nsIAuthPrompt2])
};
// Since all events are processed on the main thread, and since event
// handlers are not preemptible, there shouldn't be any concurrency issues.
function waitForAResponse(outputList) {
// From <https://developer.mozilla.org/en/XPConnect/xpcshell/HOWTO>
let threadManager = Cc["@mozilla.org/thread-manager;1"]
.getService(Ci.nsIThreadManager);
let mainThread = threadManager.currentThread;
while (outputList.length == 0) {
mainThread.processNextEvent(true);
}
}
function errorToString(error) {
return error != undefined ? errorTable[error] || ("0x" + error.toString(16)) : error;
}
function formatComment(comment) {
return comment ? " // " + comment : "";
}
function success(error) {
return error == Cr.NS_OK || error == SEC_ERROR_UNKNOWN_ISSUER;
}
function output(sortedStatuses) {
let file = FileUtils.getFile("CurWorkD", [OUTPUT]);
let errorFile = FileUtils.getFile("CurWorkD", [ERROR_OUTPUT]);
let fos = FileUtils.openSafeFileOutputStream(file);
let eos = FileUtils.openSafeFileOutputStream(errorFile);
writeTo(FILE_HEADER, fos);
for (let status of sortedStatuses) {
if (!TEST_DOMAINS[status.name] &&
success(status.errorWithoutFallback)) {
dump("INFO: " + status.name + " does NOT require fallback\n");
writeTo(status.name + ": worked (" +
errorToString(status.errorWithoutFallback) + " / " +
errorToString(status.error) + ")" +
formatComment(status.comment) + "\n", eos);
} else {
if (!success(status.error)) {
dump("INFO: " + status.name + " is dead?\n");
writeTo(status.name + ": failed (" +
errorToString(status.errorWithoutFallback) + " / " +
errorToString(status.error) + ")" +
formatComment(status.comment) + "\n", eos);
}
dump("INFO: " + status.name + " ON the fallback list\n");
writeTo(" \"" + status.name + "\"," +
formatComment(status.comment) + "\n", fos);
}
}
writeTo(FILE_FOOTER, fos);
FileUtils.closeSafeFileOutputStream(fos);
FileUtils.closeSafeFileOutputStream(eos);
}
function writeTo(string, fos) {
fos.write(string, string.length);
}