diff options
-rw-r--r-- | chrome/browser/browser_resources.grd | 1 | ||||
-rw-r--r-- | chrome/browser/chrome_browser_main_win.cc | 14 | ||||
-rw-r--r-- | chrome/browser/chrome_browser_main_win.h | 1 | ||||
-rw-r--r-- | chrome/browser/install_module_verifier_unittest_win.cc | 156 | ||||
-rw-r--r-- | chrome/browser/install_module_verifier_win.cc | 219 | ||||
-rw-r--r-- | chrome/browser/install_module_verifier_win.h | 48 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests_unit.gypi | 1 |
8 files changed, 442 insertions, 0 deletions
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 43eaafe..346fdb3 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd @@ -396,6 +396,7 @@ <include name="IDR_AUTOMATIC_PROFILE_RESET_RULES_DRY" file="internal\resources\profile_reset\automatic_profile_reset_rules_dry.dat" type="BINDATA" /> <include name="IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED" file="internal\resources\profile_reset\automatic_profile_reset_hash_seed.dat" type="BINDATA" /> <include name="IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED_DRY" file="internal\resources\profile_reset\automatic_profile_reset_hash_seed_dry.dat" type="BINDATA" /> + <include name="IDR_ADDITIONAL_MODULES_LIST" file="internal\resources\additional_modules_list.txt" type="BINDATA" /> </if> <if expr="pp_ifdef('chromeos')"> <include name="IDR_SOUND_STARTUP_WAV" file="resources\chromeos\sounds\startup.wav" type="BINDATA" /> diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc index 16a6452..7be7e23 100644 --- a/chrome/browser/chrome_browser_main_win.cc +++ b/chrome/browser/chrome_browser_main_win.cc @@ -22,6 +22,7 @@ #include "base/win/windows_version.h" #include "base/win/wrapped_window_proc.h" #include "chrome/browser/browser_util_win.h" +#include "chrome/browser/install_module_verifier_win.h" #include "chrome/browser/profiles/profile_info_cache.h" #include "chrome/browser/profiles/profile_shortcut_manager.h" #include "chrome/browser/shell_integration.h" @@ -38,6 +39,7 @@ #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/l10n_string_util.h" #include "chrome/installer/util/shell_util.h" +#include "content/public/browser/browser_thread.h" #include "content/public/common/main_function_params.h" #include "grit/app_locale_settings.h" #include "grit/chromium_strings.h" @@ -224,6 +226,18 @@ void ChromeBrowserMainPartsWin::ShowMissingLocaleMessageBox() { MB_OK | MB_ICONERROR | MB_TOPMOST); } +void ChromeBrowserMainPartsWin::PostBrowserStart() { + ChromeBrowserMainParts::PostBrowserStart(); + + // Set up a task to verify installed modules in the current process. Use a + // delay to reduce the impact on startup time. + content::BrowserThread::GetMessageLoopProxyForThread( + content::BrowserThread::UI)->PostDelayedTask( + FROM_HERE, + base::Bind(&BeginModuleVerification), + base::TimeDelta::FromSeconds(45)); +} + // static void ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment( const CommandLine& parsed_command_line) { diff --git a/chrome/browser/chrome_browser_main_win.h b/chrome/browser/chrome_browser_main_win.h index f3e7c78..170f92b 100644 --- a/chrome/browser/chrome_browser_main_win.h +++ b/chrome/browser/chrome_browser_main_win.h @@ -30,6 +30,7 @@ class ChromeBrowserMainPartsWin : public ChromeBrowserMainParts { // ChromeBrowserMainParts overrides. virtual void ShowMissingLocaleMessageBox() OVERRIDE; + virtual void PostBrowserStart() OVERRIDE; // Prepares the localized strings that are going to be displayed to // the user if the browser process dies. These strings are stored in the diff --git a/chrome/browser/install_module_verifier_unittest_win.cc b/chrome/browser/install_module_verifier_unittest_win.cc new file mode 100644 index 0000000..0e1f501 --- /dev/null +++ b/chrome/browser/install_module_verifier_unittest_win.cc @@ -0,0 +1,156 @@ +// Copyright 2013 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/install_module_verifier_win.h" + +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include "base/md5.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chrome/browser/enumerate_modules_model_win.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void AddModule(base::ListValue* list, + ModuleEnumerator::ModuleType type, + const std::string& name) { + scoped_ptr<base::DictionaryValue> data(new DictionaryValue()); + data->SetInteger("type", type); + data->SetString("name", name); + list->Append(data.release()); +} + +} // namespace + +TEST(InstallModuleVerifierTest, ExtractLoadedModuleNameDigests) { + std::set<std::string> loaded_module_name_digests; + scoped_ptr<base::ListValue> list(new ListValue()); + ExtractLoadedModuleNameDigests(*list, &loaded_module_name_digests); + ASSERT_TRUE(loaded_module_name_digests.empty()); + // WinSock modules are ignored. + AddModule(list.get(), + ModuleEnumerator::SHELL_EXTENSION, "winsock_module.dll"); + // Shell Extension modules are ignored. + AddModule( + list.get(), + ModuleEnumerator::WINSOCK_MODULE_REGISTRATION, "shell_extension.dll"); + AddModule(list.get(), ModuleEnumerator::LOADED_MODULE, "fancy_pants.dll"); + AddModule(list.get(), ModuleEnumerator::LOADED_MODULE, "chrome.dll"); + ExtractLoadedModuleNameDigests(*list, &loaded_module_name_digests); + ASSERT_EQ(2, loaded_module_name_digests.size()); + ASSERT_NE(loaded_module_name_digests.end(), + loaded_module_name_digests.find(base::MD5String("chrome.dll"))); + ASSERT_NE(loaded_module_name_digests.end(), + loaded_module_name_digests.find( + base::MD5String("fancy_pants.dll"))); +} + +TEST(InstallModuleVerifierTest, VerifyModules) { + std::set<std::string> loaded_module_name_digests; + std::set<size_t> reported_module_ids; + + VerifyModules(loaded_module_name_digests, + AdditionalModules(), + &reported_module_ids); + ASSERT_TRUE(reported_module_ids.empty()); + + // Expected, Loaded modules are reported. + loaded_module_name_digests.insert(base::MD5String("chrome.dll")); + // Unexpected modules are ignored. + loaded_module_name_digests.insert(base::MD5String("fancy_pants.dll")); + VerifyModules(loaded_module_name_digests, + AdditionalModules(), + &reported_module_ids); + ASSERT_EQ(1, reported_module_ids.size()); + // chrome.dll + ASSERT_NE(reported_module_ids.end(), reported_module_ids.find(1u)); + reported_module_ids.clear(); + + // AdditionalModules can be used to detect other modules. + AdditionalModules additional_modules; + std::string fancy_pants_md5_digest(base::MD5String("fancy_pants.dll")); + additional_modules.push_back( + std::make_pair(123u, base::StringPiece(fancy_pants_md5_digest))); + VerifyModules(loaded_module_name_digests, + additional_modules, + &reported_module_ids); + ASSERT_EQ(2, reported_module_ids.size()); + // chrome.dll + ASSERT_NE(reported_module_ids.end(), reported_module_ids.find(1u)); + // fancy_pants.dll + ASSERT_NE(reported_module_ids.end(), reported_module_ids.find(123u)); + reported_module_ids.clear(); + + // TODO(erikwright): Verify case-insensitive. +} + +TEST(InstallModuleVerifierTest, ParseAdditionalModules) { + std::vector<std::pair<size_t, base::StringPiece> > additional_modules; + + ParseAdditionalModules(base::StringPiece(), &additional_modules); + ASSERT_EQ(0u, additional_modules.size()); + + // Invalid input. + ParseAdditionalModules("hello", &additional_modules); + ASSERT_EQ(0u, additional_modules.size()); + ParseAdditionalModules("hello world", &additional_modules); + ASSERT_EQ(0u, additional_modules.size()); + ParseAdditionalModules("hello world\nfoo bar", &additional_modules); + ASSERT_EQ(0u, additional_modules.size()); + ParseAdditionalModules("123 world\nfoo bar", &additional_modules); + ASSERT_EQ(0u, additional_modules.size()); + + // A single valid line followed by an invalid line. + ParseAdditionalModules("hello 0123456789abcdef0123456789abcdef\nfoo bar", + &additional_modules); + ASSERT_EQ(0u, additional_modules.size()); + ParseAdditionalModules("123 0123456789abcdef0123456789abcdef\nfoo bar", + &additional_modules); + ASSERT_EQ(1u, additional_modules.size()); + ASSERT_EQ(123u, additional_modules[0].first); + ASSERT_EQ("0123456789abcdef0123456789abcdef", additional_modules[0].second); + additional_modules.clear(); + + // The same, but with \r\n. + ParseAdditionalModules("123 0123456789abcdef0123456789abcdef\r\nfoo bar", + &additional_modules); + ASSERT_EQ(1u, additional_modules.size()); + ASSERT_EQ(123u, additional_modules[0].first); + ASSERT_EQ("0123456789abcdef0123456789abcdef", additional_modules[0].second); + additional_modules.clear(); + + // Several valid and invalid lines, with varying line terminations etc. + ParseAdditionalModules("123 0123456789abcdef0123456789abcdef\r\n" + "456 DEADBEEFDEADBEEF\r" + "789 DEADBEEFDEADBEEFDEADBEEFDEADBEEF\n" + "\n" + "\n" + "321 BAADCAFEBAADCAFEBAADCAFEBAADCAFE\n", + &additional_modules); + ASSERT_EQ(3u, additional_modules.size()); + ASSERT_EQ(123u, additional_modules[0].first); + ASSERT_EQ("0123456789abcdef0123456789abcdef", additional_modules[0].second); + ASSERT_EQ(789u, additional_modules[1].first); + ASSERT_EQ("DEADBEEFDEADBEEFDEADBEEFDEADBEEF", additional_modules[1].second); + ASSERT_EQ(321u, additional_modules[2].first); + ASSERT_EQ("BAADCAFEBAADCAFEBAADCAFEBAADCAFE", additional_modules[2].second); + additional_modules.clear(); + + // Leading empty lines, no termination on final line. + ParseAdditionalModules("\n" + "123 0123456789abcdef0123456789abcdef\r\n" + "321 BAADCAFEBAADCAFEBAADCAFEBAADCAFE", + &additional_modules); + ASSERT_EQ(2u, additional_modules.size()); + ASSERT_EQ(123u, additional_modules[0].first); + ASSERT_EQ("0123456789abcdef0123456789abcdef", additional_modules[0].second); + ASSERT_EQ(321u, additional_modules[1].first); + ASSERT_EQ("BAADCAFEBAADCAFEBAADCAFEBAADCAFE", additional_modules[1].second); + additional_modules.clear(); +} diff --git a/chrome/browser/install_module_verifier_win.cc b/chrome/browser/install_module_verifier_win.cc new file mode 100644 index 0000000..bca42a5 --- /dev/null +++ b/chrome/browser/install_module_verifier_win.cc @@ -0,0 +1,219 @@ +// Copyright 2013 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/install_module_verifier_win.h" + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/md5.h" +#include "base/memory/scoped_ptr.h" +#include "base/metrics/sparse_histogram.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_tokenizer.h" +#include "base/strings/string_util.h" +#include "base/values.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/enumerate_modules_model_win.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_source.h" +#include "grit/browser_resources.h" +#include "ui/base/resource/resource_bundle.h" + +namespace { + +struct { size_t id; const char* module_name_digest; } + kExpectedInstallModules[] = { + {1u, "c8cc47613e155f2129f480c6ced84549"}, // chrome.dll + {2u, "49b78a23b0d8d5d8fb60d4e472b22764"}, // chrome_child.dll + }; + +// Helper to extract canonical loaded module names from the EnumerateModulesWin +// output and then verify the results. +void VerifyEnumeratedModules(const base::ListValue& module_list) { + std::set<std::string> module_name_digests; + ExtractLoadedModuleNameDigests(module_list, &module_name_digests); + + AdditionalModules additional_modules; + ParseAdditionalModules( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_ADDITIONAL_MODULES_LIST), + &additional_modules); + + std::set<size_t> installed_module_ids; + VerifyModules(module_name_digests, additional_modules, &installed_module_ids); + for (std::set<size_t>::iterator it = installed_module_ids.begin(); + it != installed_module_ids.end(); + ++it) { + UMA_HISTOGRAM_SPARSE_SLOWLY("InstallVerifier.ModuleMatch", *it); + } +} + +// Waits for NOTIFICATION_MODULE_LIST_ENUMERATED, which indicates that +// EnumerateModulesWin has completed its work. Retrieves the enumerated module +// list and processes it. +class InstallModuleVerifier : public content::NotificationObserver { + public: + // Creates an instance that will wait for module enumeration to complete, + // process the results, and then delete itself. + static void WaitForModuleList() { + // Will delete itself when scan results are available. + new InstallModuleVerifier(); + } + + private: + InstallModuleVerifier() { + notification_registrar_.Add(this, + chrome::NOTIFICATION_MODULE_LIST_ENUMERATED, + content::NotificationService::AllSources()); + } + + ~InstallModuleVerifier() {} + + // content::NotificationObserver implementation. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE { + DCHECK_EQ(type, chrome::NOTIFICATION_MODULE_LIST_ENUMERATED); + EnumerateModulesModel* model = + content::Source<EnumerateModulesModel>(source).ptr(); + scoped_ptr<base::ListValue> module_list(model->GetModuleList()); + + if (module_list.get()) + VerifyEnumeratedModules(*module_list); + + delete this; + } + + content::NotificationRegistrar notification_registrar_; + + DISALLOW_COPY_AND_ASSIGN(InstallModuleVerifier); +}; + +} // namespace + +void BeginModuleVerification() { + scoped_ptr<base::ListValue> module_list( + EnumerateModulesModel::GetInstance()->GetModuleList()); + if (module_list.get()) { + VerifyEnumeratedModules(*module_list); + } else { + InstallModuleVerifier::WaitForModuleList(); + EnumerateModulesModel::GetInstance()->ScanNow(); + } +} + +void ExtractLoadedModuleNameDigests( + const base::ListValue& module_list, + std::set<std::string>* module_name_digests) { + DCHECK(module_name_digests); + + // EnumerateModulesModel produces a list of dictionaries. + // Each dictionary corresponds to a module and exposes a number of properties. + // We care only about 'type' and 'name'. + for (size_t i = 0; i < module_list.GetSize(); ++i) { + const base::DictionaryValue* module_dictionary = NULL; + if (!module_list.GetDictionary(i, &module_dictionary)) + continue; + ModuleEnumerator::ModuleType module_type = + ModuleEnumerator::LOADED_MODULE; + if (!module_dictionary->GetInteger( + "type", reinterpret_cast<int*>(&module_type)) || + module_type != ModuleEnumerator::LOADED_MODULE) { + continue; + } + std::string module_name; + if (!module_dictionary->GetString("name", &module_name)) + continue; + StringToLowerASCII(&module_name); + module_name_digests->insert(base::MD5String(module_name)); + } +} + +void VerifyModules( + const std::set<std::string>& module_name_digests, + const AdditionalModules& additional_modules, + std::set<size_t>* installed_module_ids) { + DCHECK(installed_module_ids); + + for (size_t i = 0; i < arraysize(kExpectedInstallModules); ++i) { + if (module_name_digests.find( + kExpectedInstallModules[i].module_name_digest) != + module_name_digests.end()) { + installed_module_ids->insert(kExpectedInstallModules[i].id); + } + } + + for (AdditionalModules::const_iterator it = additional_modules.begin(); + it != additional_modules.end(); ++it) { + std::string additional_module = it->second.as_string(); + StringToLowerASCII(&additional_module); + + if (module_name_digests.find(additional_module) + != module_name_digests.end()) { + installed_module_ids->insert(it->first); + } + } +} + +namespace { + +// Parses a line consisting of a positive decimal number and a 32-digit +// hexadecimal number, separated by a space. Inserts the output, if valid, into +// |additional_modules|. Unexpected leading or trailing characters will cause +// the line to be ignored, as will invalid decimal/hexadecimal characters. +void ParseAdditionalModuleLine( + const base::StringPiece& line, + AdditionalModules* additional_modules) { + DCHECK(additional_modules); + + base::CStringTokenizer line_tokenizer(line.begin(), line.end(), " "); + + if (!line_tokenizer.GetNext()) + return; // Empty string. + base::StringPiece id_piece(line_tokenizer.token_piece()); + + if (!line_tokenizer.GetNext()) + return; // No delimiter (' '). + base::StringPiece digest_piece(line_tokenizer.token_piece()); + + if (line_tokenizer.GetNext()) + return; // Unexpected trailing characters. + + unsigned id = 0; + if (!StringToUint(id_piece, &id)) + return; // First token was not decimal. + + if (digest_piece.length() != 32) + return; // Second token is not the right length. + + for (base::StringPiece::const_iterator it = digest_piece.begin(); + it != digest_piece.end(); ++it) { + if (!IsHexDigit(*it)) + return; // Second token has invalid characters. + } + + // This is a valid line. + additional_modules->push_back(std::make_pair(id, digest_piece)); +} + +} // namespace + +void ParseAdditionalModules( + const base::StringPiece& raw_data, + AdditionalModules* additional_modules) { + DCHECK(additional_modules); + + base::CStringTokenizer file_tokenizer(raw_data.begin(), + raw_data.end(), + "\r\n"); + while (file_tokenizer.GetNext()) { + ParseAdditionalModuleLine(base::StringPiece(file_tokenizer.token_piece()), + additional_modules); + } +} diff --git a/chrome/browser/install_module_verifier_win.h b/chrome/browser/install_module_verifier_win.h new file mode 100644 index 0000000..ab0683d --- /dev/null +++ b/chrome/browser/install_module_verifier_win.h @@ -0,0 +1,48 @@ +// Copyright 2013 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. + +#ifndef CHROME_BROWSER_INSTALL_MODULE_VERIFIER_WIN_H_ +#define CHROME_BROWSER_INSTALL_MODULE_VERIFIER_WIN_H_ + +#include <set> +#include <string> +#include <utility> +#include <vector> + +#include "base/strings/string_piece.h" + +namespace base { +class ListValue; +} // namespace base + +typedef std::vector<std::pair<size_t, base::StringPiece> > AdditionalModules; + +// Starts a background process to verify installed modules. Results are reported +// via UMA. +void BeginModuleVerification(); + +// Converts a list of modules descriptions extracted from EnumerateModulesModel +// into a list of loaded module name digests. +void ExtractLoadedModuleNameDigests( + const base::ListValue& module_list, + std::set<std::string>* loaded_module_name_digests); + +// Verifies a list of installed module name digests and inserts installed module +// IDs into the supplied set. +void VerifyModules(const std::set<std::string>& module_name_digests, + const AdditionalModules& additional_modules, + std::set<size_t>* installed_module_ids); + +// Parses a list of additional modules to verify. The data format is a series of +// lines. Each line starts with a decimal ID, then a module name digest, +// separated by a space. Lines are terminated by \r and/or \n. The result is a +// vector of pairs where the first value is an ID and the second value is the +// corresponding module name digest. The StringPieces use the same underlying +// storage as |raw_data|. +// +// Invalid lines are ignored. +void ParseAdditionalModules(const base::StringPiece& raw_data, + AdditionalModules* additional_modules); + +#endif // CHROME_BROWSER_INSTALL_MODULE_VERIFIER_WIN_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index cf2aa24..bb4c335 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -904,6 +904,8 @@ 'browser/infobars/insecure_content_infobar_delegate.h', 'browser/infobars/simple_alert_infobar_delegate.cc', 'browser/infobars/simple_alert_infobar_delegate.h', + 'browser/install_module_verifier_win.cc', + 'browser/install_module_verifier_win.h', 'browser/internal_auth.cc', 'browser/internal_auth.h', 'browser/intranet_redirect_detector.cc', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 0044fd4..9742249 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -954,6 +954,7 @@ 'browser/importer/firefox_profile_lock_unittest.cc', 'browser/importer/profile_writer_unittest.cc', 'browser/internal_auth_unittest.cc', + 'browser/install_module_verifier_unittest_win.cc', 'browser/invalidation/invalidation_service_android_unittest.cc', 'browser/invalidation/invalidation_service_test_template.cc', 'browser/invalidation/invalidation_service_test_template.h', |