diff options
-rw-r--r-- | chrome/browser/net/ssl_config_service_manager_pref.cc | 79 | ||||
-rw-r--r-- | chrome/browser/net/ssl_config_service_manager_pref_unittest.cc | 112 | ||||
-rw-r--r-- | chrome/browser/prefs/browser_prefs.cc | 1 | ||||
-rw-r--r-- | chrome/browser/prefs/command_line_pref_store.cc | 17 | ||||
-rw-r--r-- | chrome/browser/prefs/command_line_pref_store.h | 3 | ||||
-rw-r--r-- | chrome/browser/prefs/command_line_pref_store_unittest.cc | 55 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 3 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 | ||||
-rw-r--r-- | chrome/common/pref_names.cc | 1 | ||||
-rw-r--r-- | chrome/common/pref_names.h | 1 |
11 files changed, 272 insertions, 2 deletions
diff --git a/chrome/browser/net/ssl_config_service_manager_pref.cc b/chrome/browser/net/ssl_config_service_manager_pref.cc index 5c3ff9b..1c18d62 100644 --- a/chrome/browser/net/ssl_config_service_manager_pref.cc +++ b/chrome/browser/net/ssl_config_service_manager_pref.cc @@ -1,16 +1,65 @@ // Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - -#include "base/message_loop.h" #include "chrome/browser/net/ssl_config_service_manager.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "chrome/browser/prefs/pref_change_registrar.h" #include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/pref_names.h" #include "content/browser/browser_thread.h" +#include "content/common/notification_details.h" +#include "content/common/notification_source.h" +#include "net/base/ssl_cipher_suite_names.h" #include "net/base/ssl_config_service.h" +namespace { + +// Converts a ListValue of StringValues into a vector of strings. Any Values +// which cannot be converted will be skipped. +std::vector<std::string> ListValueToStringVector(const ListValue* value) { + std::vector<std::string> results; + results.reserve(value->GetSize()); + std::string s; + for (ListValue::const_iterator it = value->begin(); it != value->end(); + ++it) { + if (!(*it)->GetAsString(&s)) + continue; + results.push_back(s); + } + return results; +} + +// Parses a vector of cipher suite strings, returning a sorted vector +// containing the underlying SSL/TLS cipher suites. Unrecognized/invalid +// cipher suites will be ignored. +std::vector<uint16> ParseCipherSuites( + const std::vector<std::string>& cipher_strings) { + std::vector<uint16> cipher_suites; + cipher_suites.reserve(cipher_strings.size()); + + for (std::vector<std::string>::const_iterator it = cipher_strings.begin(); + it != cipher_strings.end(); ++it) { + uint16 cipher_suite = 0; + if (!net::ParseSSLCipherString(*it, &cipher_suite)) { + LOG(ERROR) << "Ignoring unrecognized or unparsable cipher suite: " + << *it; + continue; + } + cipher_suites.push_back(cipher_suite); + } + std::sort(cipher_suites.begin(), cipher_suites.end()); + return cipher_suites; +} + +} // namespace + //////////////////////////////////////////////////////////////////////////////// // SSLConfigServicePref @@ -77,11 +126,20 @@ class SSLConfigServiceManagerPref // only be called from UI thread. void GetSSLConfigFromPrefs(net::SSLConfig* config); + // Processes changes to the disabled cipher suites preference, updating the + // cached list of parsed SSL/TLS cipher suites that are disabled. + void OnDisabledCipherSuitesChange(PrefService* prefs); + + PrefChangeRegistrar pref_change_registrar_; + // The prefs (should only be accessed from UI thread) BooleanPrefMember rev_checking_enabled_; BooleanPrefMember ssl3_enabled_; BooleanPrefMember tls1_enabled_; + // The cached list of disabled SSL cipher suites. + std::vector<uint16> disabled_cipher_suites_; + scoped_refptr<SSLConfigServicePref> ssl_config_service_; DISALLOW_COPY_AND_ASSIGN(SSLConfigServiceManagerPref); @@ -96,7 +154,10 @@ SSLConfigServiceManagerPref::SSLConfigServiceManagerPref( local_state, this); ssl3_enabled_.Init(prefs::kSSL3Enabled, local_state, this); tls1_enabled_.Init(prefs::kTLS1Enabled, local_state, this); + pref_change_registrar_.Init(local_state); + pref_change_registrar_.Add(prefs::kCipherSuiteBlacklist, this); + OnDisabledCipherSuitesChange(local_state); // Initialize from UI thread. This is okay as there shouldn't be anything on // the IO thread trying to access it yet. GetSSLConfigFromPrefs(&ssl_config_service_->cached_config_); @@ -111,6 +172,7 @@ void SSLConfigServiceManagerPref::RegisterPrefs(PrefService* prefs) { default_config.ssl3_enabled); prefs->RegisterBooleanPref(prefs::kTLS1Enabled, default_config.tls1_enabled); + prefs->RegisterListPref(prefs::kCipherSuiteBlacklist); } net::SSLConfigService* SSLConfigServiceManagerPref::Get() { @@ -122,6 +184,12 @@ void SSLConfigServiceManagerPref::Observe(int type, const NotificationDetails& details) { if (type == chrome::NOTIFICATION_PREF_CHANGED) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + std::string* pref_name_in = Details<std::string>(details).ptr(); + PrefService* prefs = Source<PrefService>(source).ptr(); + DCHECK(pref_name_in && prefs); + if (*pref_name_in == prefs::kCipherSuiteBlacklist) + OnDisabledCipherSuitesChange(prefs); + net::SSLConfig new_config; GetSSLConfigFromPrefs(&new_config); @@ -142,9 +210,16 @@ void SSLConfigServiceManagerPref::GetSSLConfigFromPrefs( config->rev_checking_enabled = rev_checking_enabled_.GetValue(); config->ssl3_enabled = ssl3_enabled_.GetValue(); config->tls1_enabled = tls1_enabled_.GetValue(); + config->disabled_cipher_suites = disabled_cipher_suites_; SSLConfigServicePref::SetSSLConfigFlags(config); } +void SSLConfigServiceManagerPref::OnDisabledCipherSuitesChange( + PrefService* prefs) { + const ListValue* value = prefs->GetList(prefs::kCipherSuiteBlacklist); + disabled_cipher_suites_ = ParseCipherSuites(ListValueToStringVector(value)); +} + //////////////////////////////////////////////////////////////////////////////// // SSLConfigServiceManager diff --git a/chrome/browser/net/ssl_config_service_manager_pref_unittest.cc b/chrome/browser/net/ssl_config_service_manager_pref_unittest.cc new file mode 100644 index 0000000..603947f --- /dev/null +++ b/chrome/browser/net/ssl_config_service_manager_pref_unittest.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/net/pref_proxy_config_service.h" + +#include "base/message_loop.h" +#include "base/values.h" +#include "chrome/browser/net/ssl_config_service_manager.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/testing_pref_service.h" +#include "content/browser/browser_thread.h" +#include "net/base/ssl_config_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::ListValue; +using base::Value; +using net::SSLConfig; +using net::SSLConfigService; + +class SSLConfigServiceManagerPrefTest : public testing::Test { + public: + SSLConfigServiceManagerPrefTest() {} + + virtual void SetUp() { + message_loop_.reset(new MessageLoop()); + ui_thread_.reset( + new BrowserThread(BrowserThread::UI, message_loop_.get())); + io_thread_.reset( + new BrowserThread(BrowserThread::IO, message_loop_.get())); + pref_service_.reset(new TestingPrefService()); + SSLConfigServiceManager::RegisterPrefs(pref_service_.get()); + } + + virtual void TearDown() { + pref_service_.reset(); + io_thread_.reset(); + ui_thread_.reset(); + message_loop_.reset(); + } + + protected: + scoped_ptr<MessageLoop> message_loop_; + scoped_ptr<BrowserThread> ui_thread_; + scoped_ptr<BrowserThread> io_thread_; + scoped_ptr<TestingPrefService> pref_service_; +}; + +// Test that cipher suites can be disabled. "Good" refers to the fact that +// every value is expected to be successfully parsed into a cipher suite. +TEST_F(SSLConfigServiceManagerPrefTest, GoodDisabledCipherSuites) { + scoped_ptr<SSLConfigServiceManager> config_manager( + SSLConfigServiceManager::CreateDefaultManager(pref_service_.get())); + ASSERT_TRUE(config_manager.get()); + scoped_refptr<SSLConfigService> config_service(config_manager->Get()); + ASSERT_TRUE(config_service.get()); + + SSLConfig old_config; + config_service->GetSSLConfig(&old_config); + EXPECT_TRUE(old_config.disabled_cipher_suites.empty()); + + ListValue* list_value = new ListValue(); + list_value->Append(Value::CreateStringValue("0x0004")); + list_value->Append(Value::CreateStringValue("0x0005")); + pref_service_->SetUserPref(prefs::kCipherSuiteBlacklist, list_value); + + // Pump the message loop to notify the SSLConfigServiceManagerPref that the + // preferences changed. + message_loop_->RunAllPending(); + + SSLConfig config; + config_service->GetSSLConfig(&config); + + EXPECT_NE(old_config.disabled_cipher_suites, config.disabled_cipher_suites); + ASSERT_EQ(2u, config.disabled_cipher_suites.size()); + EXPECT_EQ(0x0004, config.disabled_cipher_suites[0]); + EXPECT_EQ(0x0005, config.disabled_cipher_suites[1]); +} + +// Test that cipher suites can be disabled. "Bad" refers to the fact that +// there are one or more non-cipher suite strings in the preference. They +// should be ignored. +TEST_F(SSLConfigServiceManagerPrefTest, BadDisabledCipherSuites) { + scoped_ptr<SSLConfigServiceManager> config_manager( + SSLConfigServiceManager::CreateDefaultManager(pref_service_.get())); + ASSERT_TRUE(config_manager.get()); + scoped_refptr<SSLConfigService> config_service(config_manager->Get()); + ASSERT_TRUE(config_service.get()); + + SSLConfig old_config; + config_service->GetSSLConfig(&old_config); + EXPECT_TRUE(old_config.disabled_cipher_suites.empty()); + + ListValue* list_value = new ListValue(); + list_value->Append(Value::CreateStringValue("0x0004")); + list_value->Append(Value::CreateStringValue("TLS_NOT_WITH_A_CIPHER_SUITE")); + list_value->Append(Value::CreateStringValue("0x0005")); + list_value->Append(Value::CreateStringValue("0xBEEFY")); + pref_service_->SetUserPref(prefs::kCipherSuiteBlacklist, list_value); + + // Pump the message loop to notify the SSLConfigServiceManagerPref that the + // preferences changed. + message_loop_->RunAllPending(); + + SSLConfig config; + config_service->GetSSLConfig(&config); + + EXPECT_NE(old_config.disabled_cipher_suites, config.disabled_cipher_suites); + ASSERT_EQ(2u, config.disabled_cipher_suites.size()); + EXPECT_EQ(0x0004, config.disabled_cipher_suites[0]); + EXPECT_EQ(0x0005, config.disabled_cipher_suites[1]); +} diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index c163edd..4275cc6 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -28,6 +28,7 @@ #include "chrome/browser/metrics/metrics_log.h" #include "chrome/browser/metrics/metrics_service.h" #include "chrome/browser/net/net_pref_observer.h" +#include "chrome/browser/net/ssl_config_service_manager.h" #include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/pref_proxy_config_service.h" #include "chrome/browser/net/ssl_config_service_manager.h" diff --git a/chrome/browser/prefs/command_line_pref_store.cc b/chrome/browser/prefs/command_line_pref_store.cc index a475c45..021be74 100644 --- a/chrome/browser/prefs/command_line_pref_store.cc +++ b/chrome/browser/prefs/command_line_pref_store.cc @@ -5,6 +5,7 @@ #include "chrome/browser/prefs/command_line_pref_store.h" #include "base/logging.h" +#include "base/string_split.h" #include "base/values.h" #include "chrome/browser/prefs/proxy_config_dictionary.h" #include "chrome/common/chrome_switches.h" @@ -48,6 +49,7 @@ CommandLinePrefStore::CommandLinePrefStore(const CommandLine* command_line) ApplySimpleSwitches(); ApplyProxyMode(); ValidateProxySwitches(); + ApplySSLSwitches(); } CommandLinePrefStore::~CommandLinePrefStore() {} @@ -106,3 +108,18 @@ void CommandLinePrefStore::ApplyProxyMode() { bypass_list)); } } + +void CommandLinePrefStore::ApplySSLSwitches() { + if (command_line_->HasSwitch(switches::kCipherSuiteBlacklist)) { + std::string cipher_suites = + command_line_->GetSwitchValueASCII(switches::kCipherSuiteBlacklist); + std::vector<std::string> cipher_strings; + base::SplitString(cipher_suites, ',', &cipher_strings); + base::ListValue* list_value = new base::ListValue(); + for (std::vector<std::string>::const_iterator it = cipher_strings.begin(); + it != cipher_strings.end(); ++it) { + list_value->Append(base::Value::CreateStringValue(*it)); + } + SetValue(prefs::kCipherSuiteBlacklist, list_value); + } +} diff --git a/chrome/browser/prefs/command_line_pref_store.h b/chrome/browser/prefs/command_line_pref_store.h index 7bc3cc8..5555b18 100644 --- a/chrome/browser/prefs/command_line_pref_store.h +++ b/chrome/browser/prefs/command_line_pref_store.h @@ -46,6 +46,9 @@ class CommandLinePrefStore : public ValueMapPrefStore { // Determines the proxy mode preference from the given proxy switches. void ApplyProxyMode(); + // Apply the SSL/TLS preferences from the given switches. + void ApplySSLSwitches(); + // Weak reference. const CommandLine* command_line_; diff --git a/chrome/browser/prefs/command_line_pref_store_unittest.cc b/chrome/browser/prefs/command_line_pref_store_unittest.cc index aa4eeb6..ecb81d2 100644 --- a/chrome/browser/prefs/command_line_pref_store_unittest.cc +++ b/chrome/browser/prefs/command_line_pref_store_unittest.cc @@ -34,6 +34,23 @@ class TestCommandLinePrefStore : public CommandLinePrefStore { ASSERT_TRUE(dict.GetMode(&actual_mode)); EXPECT_EQ(expected_mode, actual_mode); } + + void VerifySSLCipherSuites(const char* const* ciphers, + size_t cipher_count) { + const Value* value = NULL; + ASSERT_EQ(PrefStore::READ_OK, + GetValue(prefs::kCipherSuiteBlacklist, &value)); + ASSERT_EQ(Value::TYPE_LIST, value->GetType()); + const ListValue* list_value = static_cast<const ListValue*>(value); + ASSERT_EQ(cipher_count, list_value->GetSize()); + + std::string cipher_string; + for (ListValue::const_iterator it = list_value->begin(); + it != list_value->end(); ++it, ++ciphers) { + ASSERT_TRUE((*it)->GetAsString(&cipher_string)); + EXPECT_EQ(*ciphers, cipher_string); + } + } }; const char unknown_bool[] = "unknown_switch"; @@ -159,3 +176,41 @@ TEST(CommandLinePrefStoreTest, ManualProxyModeInference) { new TestCommandLinePrefStore(&cl3); store3->VerifyProxyMode(ProxyPrefs::MODE_DIRECT); } + +TEST(CommandLinePrefStoreTest, DisableSSLCipherSuites) { + CommandLine cl1(CommandLine::NO_PROGRAM); + cl1.AppendSwitchASCII(switches::kCipherSuiteBlacklist, + "0x0004,0x0005"); + scoped_refptr<TestCommandLinePrefStore> store1 = + new TestCommandLinePrefStore(&cl1); + const char* const expected_ciphers1[] = { + "0x0004", + "0x0005", + }; + store1->VerifySSLCipherSuites(expected_ciphers1, + arraysize(expected_ciphers1)); + + CommandLine cl2(CommandLine::NO_PROGRAM); + cl2.AppendSwitchASCII(switches::kCipherSuiteBlacklist, + "0x0004, WHITESPACE_IGNORED TEST , 0x0005"); + scoped_refptr<TestCommandLinePrefStore> store2 = + new TestCommandLinePrefStore(&cl2); + const char* const expected_ciphers2[] = { + "0x0004", + "WHITESPACE_IGNORED TEST", + "0x0005", + }; + store2->VerifySSLCipherSuites(expected_ciphers2, + arraysize(expected_ciphers2)); + + CommandLine cl3(CommandLine::NO_PROGRAM); + cl3.AppendSwitchASCII(switches::kCipherSuiteBlacklist, + "0x0004;MOAR;0x0005"); + scoped_refptr<TestCommandLinePrefStore> store3 = + new TestCommandLinePrefStore(&cl3); + const char* const expected_ciphers3[] = { + "0x0004;MOAR;0x0005" + }; + store3->VerifySSLCipherSuites(expected_ciphers3, + arraysize(expected_ciphers3)); +} diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index a33673b..06814af 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1501,6 +1501,7 @@ 'browser/net/pref_proxy_config_service_unittest.cc', 'browser/net/quoted_printable_unittest.cc', 'browser/net/sqlite_persistent_cookie_store_unittest.cc', + 'browser/net/ssl_config_service_manager_pref_unittest.cc', 'browser/net/url_fixer_upper_unittest.cc', 'browser/net/url_info_unittest.cc', 'browser/notifications/desktop_notification_service_unittest.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 5eab5e3..1011697 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -112,6 +112,9 @@ const char kCheckForUpdateIntervalSec[] = "check-for-update-interval"; // this version cannot be loaded, Chrome will exit. const char kChromeVersion[] = "chrome-version"; +// Comma-separated list of SSL cipher suites to disable. +const char kCipherSuiteBlacklist[] = "cipher-suite-blacklist"; + // Used with kCloudPrintFile. Tells Chrome to delete the file when // finished displaying the print dialog. const char kCloudPrintDeleteFile[] = "cloud-print-delete-file"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 0088b0b..3fe3cfd 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -48,6 +48,7 @@ extern const char kBrowserAssertTest[]; extern const char kBrowserCrashTest[]; extern const char kCheckForUpdateIntervalSec[]; extern const char kChromeVersion[]; +extern const char kCipherSuiteBlacklist[]; extern const char kCloudPrintDeleteFile[]; extern const char kCloudPrintFile[]; extern const char kCloudPrintJobTitle[]; diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index eeb55ba..f11b4d8 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -749,6 +749,7 @@ const char kProfileInfoCache[] = "profile.info_cache"; const char kCertRevocationCheckingEnabled[] = "ssl.rev_checking.enabled"; const char kSSL3Enabled[] = "ssl.ssl3.enabled"; const char kTLS1Enabled[] = "ssl.tls1.enabled"; +const char kCipherSuiteBlacklist[] = "ssl.cipher_suites.blacklist"; // The metrics client GUID and session ID. const char kMetricsClientID[] = "user_experience_metrics.client_id"; diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 08e1318..f45de8a 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -255,6 +255,7 @@ extern const char kPasswordsUseLocalProfileId[]; extern const char kCertRevocationCheckingEnabled[]; extern const char kSSL3Enabled[]; extern const char kTLS1Enabled[]; +extern const char kCipherSuiteBlacklist[]; extern const char kMetricsClientID[]; extern const char kMetricsSessionID[]; |