1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/base_paths.h"
6 #include "base/bind.h"
7 #include "base/callback.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/path_service.h"
11 #include "base/run_loop.h"
12 #include "base/scoped_native_library.h"
13 #include "base/test/bind.h"
14 #include "base/test/scoped_feature_list.h"
15 #include "base/test/test_reg_util_win.h"
16 #include "base/win/registry.h"
17 #include "base/win/windows_version.h"
18 #include "chrome/browser/win/conflicts/module_blacklist_cache_updater.h"
19 #include "chrome/browser/win/conflicts/module_blacklist_cache_util.h"
20 #include "chrome/browser/win/conflicts/module_database.h"
21 #include "chrome/browser/win/conflicts/proto/module_list.pb.h"
22 #include "chrome/browser/win/conflicts/third_party_conflicts_manager.h"
23 #include "chrome/chrome_elf/third_party_dlls/packed_list_format.h"
24 #include "chrome/common/chrome_features.h"
25 #include "chrome/install_static/install_util.h"
26 #include "chrome/test/base/in_process_browser_test.h"
27 #include "components/services/quarantine/public/cpp/quarantine_features_win.h"
28 #include "content/public/test/browser_test.h"
29
30 namespace {
31
32 // This classes watches the third-party blocking key to detect when the path to
33 // the freshly created cache is written into the registry.
34 class ThirdPartyRegistryKeyObserver {
35 public:
ThirdPartyRegistryKeyObserver()36 ThirdPartyRegistryKeyObserver()
37 : registry_key_(HKEY_CURRENT_USER,
38 GetRegistryKeyPath().c_str(),
39 KEY_CREATE_SUB_KEY | KEY_READ | KEY_NOTIFY) {}
40
StartWatching()41 bool StartWatching() {
42 return registry_key_.StartWatching(base::Bind(
43 &ThirdPartyRegistryKeyObserver::OnChange, base::Unretained(this)));
44 }
45
WaitForCachePathWritten()46 void WaitForCachePathWritten() {
47 if (path_written_)
48 return;
49
50 base::RunLoop run_loop;
51 run_loop_quit_closure_ = run_loop.QuitClosure();
52 run_loop.Run();
53 }
54
OnChange()55 void OnChange() {
56 if (!registry_key_.HasValue(third_party_dlls::kBlFilePathRegValue))
57 return;
58
59 path_written_ = true;
60
61 if (run_loop_quit_closure_)
62 std::move(run_loop_quit_closure_).Run();
63 }
64
65 private:
GetRegistryKeyPath()66 base::string16 GetRegistryKeyPath() {
67 return install_static::GetRegistryPath().append(
68 third_party_dlls::kThirdPartyRegKeyName);
69 }
70
71 base::win::RegKey registry_key_;
72
73 // Remembers if the path of the cache was written in the registry in case the
74 // callback is invoked before WaitForCachePathWritten() was called.
75 bool path_written_ = false;
76
77 base::Closure run_loop_quit_closure_;
78
79 DISALLOW_COPY_AND_ASSIGN(ThirdPartyRegistryKeyObserver);
80 };
81
82 // Creates an empty serialized ModuleList proto in the module list component
83 // directory and returns its path.
CreateModuleList(base::FilePath * module_list_path)84 void CreateModuleList(base::FilePath* module_list_path) {
85 chrome::conflicts::ModuleList module_list;
86 // Include an empty blacklist and whitelist.
87 module_list.mutable_blacklist();
88 module_list.mutable_whitelist();
89
90 std::string contents;
91 ASSERT_TRUE(module_list.SerializeToString(&contents));
92
93 // Put the module list beside the module blacklist cache.
94 *module_list_path = ModuleBlacklistCacheUpdater::GetModuleBlacklistCachePath()
95 .DirName()
96 .Append(FILE_PATH_LITERAL("ModuleList.bin"));
97
98 base::ScopedAllowBlockingForTesting scoped_allow_blocking;
99 ASSERT_TRUE(base::CreateDirectory(module_list_path->DirName()));
100 ASSERT_EQ(static_cast<int>(contents.size()),
101 base::WriteFile(*module_list_path, contents.data(),
102 static_cast<int>(contents.size())));
103 }
104
105 class ThirdPartyBlockingBrowserTest : public InProcessBrowserTest {
106 protected:
107 ThirdPartyBlockingBrowserTest() = default;
108 ~ThirdPartyBlockingBrowserTest() override = default;
109
110 // InProcessBrowserTest:
SetUp()111 void SetUp() override {
112 scoped_feature_list_.InitWithFeatures({features::kThirdPartyModulesBlocking,
113 quarantine::kOutOfProcessQuarantine},
114 {});
115
116 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
117 ASSERT_NO_FATAL_FAILURE(
118 registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
119
120 InProcessBrowserTest::SetUp();
121 }
122
123 // Creates a copy of a test DLL into a temp directory that will act as the
124 // third-party module and return its path. It can't be located in the output
125 // directory because modules in the same directory as chrome.exe are
126 // whitelisted in non-official builds.
CreateThirdPartyModule(base::FilePath * third_party_module_path)127 void CreateThirdPartyModule(base::FilePath* third_party_module_path) {
128 base::FilePath test_dll_path;
129 ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &test_dll_path));
130 test_dll_path =
131 test_dll_path.Append(FILE_PATH_LITERAL("conflicts_dll.dll"));
132 *third_party_module_path = scoped_temp_dir_.GetPath().Append(
133 FILE_PATH_LITERAL("third_party_module.dll"));
134 base::ScopedAllowBlockingForTesting scoped_allow_blocking;
135 ASSERT_TRUE(base::CopyFile(test_dll_path, *third_party_module_path));
136 }
137
138 // Enables the ThirdPartyModulesBlocking feature.
139 base::test::ScopedFeatureList scoped_feature_list_;
140
141 registry_util::RegistryOverrideManager registry_override_manager_;
142
143 // Temp directory where the third-party module is located.
144 base::ScopedTempDir scoped_temp_dir_;
145
146 DISALLOW_COPY_AND_ASSIGN(ThirdPartyBlockingBrowserTest);
147 };
148
149 } // namespace
150
151 // This is an integration test for the blocking of third-party modules.
152 //
153 // This test makes sure that all the different classes interact together
154 // correctly to produce a valid module blacklist cache and to write its path in
155 // the registry.
156 //
157 // Note: This doesn't test that the modules are actually blocked on the next
158 // browser launch.
IN_PROC_BROWSER_TEST_F(ThirdPartyBlockingBrowserTest,CreateModuleBlacklistCache)159 IN_PROC_BROWSER_TEST_F(ThirdPartyBlockingBrowserTest,
160 CreateModuleBlacklistCache) {
161 if (base::win::GetVersion() < base::win::Version::WIN8)
162 return;
163
164 // Create the observer early so the change is guaranteed to be observed.
165 ThirdPartyRegistryKeyObserver third_party_registry_key_observer;
166 ASSERT_TRUE(third_party_registry_key_observer.StartWatching());
167
168 base::RunLoop run_loop;
169 ModuleDatabase::GetTaskRunner()->PostTask(
170 FROM_HERE,
171 base::BindLambdaForTesting([quit_closure = run_loop.QuitClosure()]() {
172 ModuleDatabase* module_database = ModuleDatabase::GetInstance();
173
174 // Speed up the test.
175 module_database->IncreaseInspectionPriority();
176
177 base::FilePath module_list_path;
178 ASSERT_NO_FATAL_FAILURE(CreateModuleList(&module_list_path));
179 ASSERT_FALSE(module_list_path.empty());
180
181 // Simulate the download of the module list component.
182 module_database->third_party_conflicts_manager()->LoadModuleList(
183 module_list_path);
184
185 quit_closure.Run();
186 }));
187 run_loop.Run();
188
189 // Injects the third-party DLL into the process.
190 base::FilePath third_party_module_path;
191 ASSERT_NO_FATAL_FAILURE(CreateThirdPartyModule(&third_party_module_path));
192 ASSERT_FALSE(third_party_module_path.empty());
193
194 base::ScopedAllowBlockingForTesting scoped_allow_blocking;
195 base::ScopedNativeLibrary dll(third_party_module_path);
196 ASSERT_TRUE(dll.is_valid());
197
198 // Now the module blacklist cache will eventually be created and its path
199 // written in the registry.
200 third_party_registry_key_observer.WaitForCachePathWritten();
201
202 base::FilePath module_blacklist_cache_path =
203 ModuleBlacklistCacheUpdater::GetModuleBlacklistCachePath();
204 ASSERT_FALSE(module_blacklist_cache_path.empty());
205 ASSERT_TRUE(base::PathExists(module_blacklist_cache_path));
206
207 // Now check that the third-party DLL was added to the module blacklist cache.
208 third_party_dlls::PackedListMetadata metadata;
209 std::vector<third_party_dlls::PackedListModule> blacklisted_modules;
210 base::MD5Digest md5_digest;
211 ASSERT_EQ(ReadResult::kSuccess,
212 ReadModuleBlacklistCache(module_blacklist_cache_path, &metadata,
213 &blacklisted_modules, &md5_digest));
214
215 EXPECT_GE(blacklisted_modules.size(), 1u);
216 }
217