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