1 // Copyright 2017 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 "extensions/browser/api/declarative_net_request/rules_monitor_service.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/files/file_util.h"
12 #include "base/lazy_instance.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/task/post_task.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "content/public/browser/browser_task_traits.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "extensions/browser/api/declarative_net_request/composite_matcher.h"
20 #include "extensions/browser/api/declarative_net_request/file_sequence_helper.h"
21 #include "extensions/browser/api/declarative_net_request/ruleset_manager.h"
22 #include "extensions/browser/api/declarative_net_request/ruleset_matcher.h"
23 #include "extensions/browser/api/declarative_net_request/ruleset_source.h"
24 #include "extensions/browser/api/web_request/permission_helper.h"
25 #include "extensions/browser/api/web_request/web_request_api.h"
26 #include "extensions/browser/extension_file_task_runner.h"
27 #include "extensions/browser/extension_prefs.h"
28 #include "extensions/browser/extension_prefs_factory.h"
29 #include "extensions/browser/extension_registry_factory.h"
30 #include "extensions/browser/warning_service.h"
31 #include "extensions/browser/warning_service_factory.h"
32 #include "extensions/browser/warning_set.h"
33 #include "extensions/common/api/declarative_net_request.h"
34 #include "extensions/common/api/declarative_net_request/constants.h"
35 #include "extensions/common/api/declarative_net_request/dnr_manifest_data.h"
36 #include "extensions/common/api/declarative_net_request/utils.h"
37 #include "extensions/common/extension_id.h"
38 
39 namespace extensions {
40 namespace declarative_net_request {
41 
42 namespace {
43 
44 namespace dnr_api = api::declarative_net_request;
45 
46 static base::LazyInstance<
47     BrowserContextKeyedAPIFactory<RulesMonitorService>>::Leaky g_factory =
48     LAZY_INSTANCE_INITIALIZER;
49 
50 }  // namespace
51 
52 // Helper to bridge tasks to FileSequenceHelper. Lives on the UI thread.
53 class RulesMonitorService::FileSequenceBridge {
54  public:
FileSequenceBridge()55   FileSequenceBridge()
56       : file_task_runner_(GetExtensionFileTaskRunner()),
57         file_sequence_helper_(std::make_unique<FileSequenceHelper>()) {}
58 
~FileSequenceBridge()59   ~FileSequenceBridge() {
60     file_task_runner_->DeleteSoon(FROM_HERE, std::move(file_sequence_helper_));
61   }
62 
LoadRulesets(LoadRequestData load_data,FileSequenceHelper::LoadRulesetsUICallback ui_callback) const63   void LoadRulesets(
64       LoadRequestData load_data,
65       FileSequenceHelper::LoadRulesetsUICallback ui_callback) const {
66     // base::Unretained is safe here because we trigger the destruction of
67     // |file_sequence_helper_| on |file_task_runner_| from our destructor. Hence
68     // it is guaranteed to be alive when |load_ruleset_task| is run.
69     base::OnceClosure load_ruleset_task =
70         base::BindOnce(&FileSequenceHelper::LoadRulesets,
71                        base::Unretained(file_sequence_helper_.get()),
72                        std::move(load_data), std::move(ui_callback));
73     file_task_runner_->PostTask(FROM_HERE, std::move(load_ruleset_task));
74   }
75 
UpdateDynamicRules(LoadRequestData load_data,std::vector<int> rule_ids_to_remove,std::vector<dnr_api::Rule> rules_to_add,FileSequenceHelper::UpdateDynamicRulesUICallback ui_callback) const76   void UpdateDynamicRules(
77       LoadRequestData load_data,
78       std::vector<int> rule_ids_to_remove,
79       std::vector<dnr_api::Rule> rules_to_add,
80       FileSequenceHelper::UpdateDynamicRulesUICallback ui_callback) const {
81     // base::Unretained is safe here because we trigger the destruction of
82     // |file_sequence_state_| on |file_task_runner_| from our destructor. Hence
83     // it is guaranteed to be alive when |update_dynamic_rules_task| is run.
84     base::OnceClosure update_dynamic_rules_task =
85         base::BindOnce(&FileSequenceHelper::UpdateDynamicRules,
86                        base::Unretained(file_sequence_helper_.get()),
87                        std::move(load_data), std::move(rule_ids_to_remove),
88                        std::move(rules_to_add), std::move(ui_callback));
89     file_task_runner_->PostTask(FROM_HERE,
90                                 std::move(update_dynamic_rules_task));
91   }
92 
93  private:
94   scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
95 
96   // Created on the UI thread. Accessed and destroyed on |file_task_runner_|.
97   // Maintains state needed on |file_task_runner_|.
98   std::unique_ptr<FileSequenceHelper> file_sequence_helper_;
99 
100   DISALLOW_COPY_AND_ASSIGN(FileSequenceBridge);
101 };
102 
103 // static
104 BrowserContextKeyedAPIFactory<RulesMonitorService>*
GetFactoryInstance()105 RulesMonitorService::GetFactoryInstance() {
106   return g_factory.Pointer();
107 }
108 
109 // static
Get(content::BrowserContext * browser_context)110 RulesMonitorService* RulesMonitorService::Get(
111     content::BrowserContext* browser_context) {
112   return BrowserContextKeyedAPIFactory<RulesMonitorService>::Get(
113       browser_context);
114 }
115 
HasAnyRegisteredRulesets() const116 bool RulesMonitorService::HasAnyRegisteredRulesets() const {
117   return !extensions_with_rulesets_.empty();
118 }
119 
HasRegisteredRuleset(const ExtensionId & extension_id) const120 bool RulesMonitorService::HasRegisteredRuleset(
121     const ExtensionId& extension_id) const {
122   return extensions_with_rulesets_.find(extension_id) !=
123          extensions_with_rulesets_.end();
124 }
125 
UpdateDynamicRules(const Extension & extension,std::vector<int> rule_ids_to_remove,std::vector<api::declarative_net_request::Rule> rules_to_add,DynamicRuleUpdateUICallback callback)126 void RulesMonitorService::UpdateDynamicRules(
127     const Extension& extension,
128     std::vector<int> rule_ids_to_remove,
129     std::vector<api::declarative_net_request::Rule> rules_to_add,
130     DynamicRuleUpdateUICallback callback) {
131   DCHECK(HasRegisteredRuleset(extension.id()));
132 
133   LoadRequestData data(extension.id());
134 
135   // We are updating the indexed ruleset. Don't set the expected checksum since
136   // it'll change.
137   data.rulesets.emplace_back(RulesetSource::CreateDynamic(context_, extension));
138 
139   auto update_rules_callback =
140       base::BindOnce(&RulesMonitorService::OnDynamicRulesUpdated,
141                      weak_factory_.GetWeakPtr(), std::move(callback));
142   file_sequence_bridge_->UpdateDynamicRules(
143       std::move(data), std::move(rule_ids_to_remove), std::move(rules_to_add),
144       std::move(update_rules_callback));
145 }
146 
RulesMonitorService(content::BrowserContext * browser_context)147 RulesMonitorService::RulesMonitorService(
148     content::BrowserContext* browser_context)
149     : file_sequence_bridge_(std::make_unique<FileSequenceBridge>()),
150       prefs_(ExtensionPrefs::Get(browser_context)),
151       extension_registry_(ExtensionRegistry::Get(browser_context)),
152       warning_service_(WarningService::Get(browser_context)),
153       context_(browser_context),
154       ruleset_manager_(browser_context),
155       action_tracker_(browser_context) {
156   registry_observer_.Add(extension_registry_);
157 }
158 
159 RulesMonitorService::~RulesMonitorService() = default;
160 
161 /* Description of thread hops for various scenarios:
162 
163    On ruleset load success:
164       - UI -> File -> UI.
165       - The File sequence might reindex the ruleset while parsing JSON OOP.
166 
167    On ruleset load failure:
168       - UI -> File -> UI.
169       - The File sequence might reindex the ruleset while parsing JSON OOP.
170 
171    On ruleset unload:
172       - UI.
173 
174    On dynamic rules update.
175       - UI -> File -> UI -> IPC to extension
176 */
177 
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * extension)178 void RulesMonitorService::OnExtensionLoaded(
179     content::BrowserContext* browser_context,
180     const Extension* extension) {
181   DCHECK_EQ(context_, browser_context);
182 
183   if (!declarative_net_request::DNRManifestData::HasRuleset(*extension))
184     return;
185 
186   DCHECK(IsAPIAvailable());
187 
188   LoadRequestData load_data(extension->id());
189   int expected_ruleset_checksum;
190 
191   // Static ruleset.
192   {
193     std::vector<RulesetSource> static_rulesets =
194         RulesetSource::CreateStatic(*extension);
195 
196     // TODO(crbug.com/754526): Load all static rulesets for the extension.
197     RulesetInfo static_ruleset(std::move(static_rulesets[0]));
198     bool has_checksum = prefs_->GetDNRStaticRulesetChecksum(
199         extension->id(), static_ruleset.source().id(),
200         &expected_ruleset_checksum);
201 
202     if (!has_checksum) {
203       // This might happen on prefs corruption.
204       warning_service_->AddWarnings(
205           {Warning::CreateRulesetFailedToLoadWarning(load_data.extension_id)});
206     } else {
207       static_ruleset.set_expected_checksum(expected_ruleset_checksum);
208       load_data.rulesets.push_back(std::move(static_ruleset));
209     }
210   }
211 
212   // Dynamic ruleset
213   if (prefs_->GetDNRDynamicRulesetChecksum(extension->id(),
214                                            &expected_ruleset_checksum)) {
215     RulesetInfo dynamic_ruleset(
216         RulesetSource::CreateDynamic(browser_context, *extension));
217     dynamic_ruleset.set_expected_checksum(expected_ruleset_checksum);
218     load_data.rulesets.push_back(std::move(dynamic_ruleset));
219   }
220 
221   if (load_data.rulesets.empty())
222     return;
223 
224   auto load_ruleset_callback = base::BindOnce(
225       &RulesMonitorService::OnRulesetLoaded, weak_factory_.GetWeakPtr());
226   file_sequence_bridge_->LoadRulesets(std::move(load_data),
227                                       std::move(load_ruleset_callback));
228 }
229 
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionReason reason)230 void RulesMonitorService::OnExtensionUnloaded(
231     content::BrowserContext* browser_context,
232     const Extension* extension,
233     UnloadedExtensionReason reason) {
234   DCHECK_EQ(context_, browser_context);
235 
236   // Return early if the extension does not have an active indexed ruleset.
237   if (!extensions_with_rulesets_.erase(extension->id()))
238     return;
239 
240   DCHECK(IsAPIAvailable());
241 
242   UnloadRuleset(extension->id());
243 }
244 
OnExtensionUninstalled(content::BrowserContext * browser_context,const Extension * extension,UninstallReason reason)245 void RulesMonitorService::OnExtensionUninstalled(
246     content::BrowserContext* browser_context,
247     const Extension* extension,
248     UninstallReason reason) {
249   DCHECK_EQ(context_, browser_context);
250 
251   // Skip if the extension will be reinstalled soon.
252   if (reason == UNINSTALL_REASON_REINSTALL)
253     return;
254 
255   // Skip if the extension doesn't have a dynamic ruleset.
256   int dynamic_checksum;
257   if (!prefs_->GetDNRDynamicRulesetChecksum(extension->id(),
258                                             &dynamic_checksum)) {
259     return;
260   }
261 
262   // Cleanup the dynamic rules directory for the extension.
263   // TODO(karandeepb): It's possible that this task fails, e.g. during shutdown.
264   // Make this more robust.
265   RulesetSource source =
266       RulesetSource::CreateDynamic(browser_context, *extension);
267   DCHECK_EQ(source.json_path().DirName(), source.indexed_path().DirName());
268   GetExtensionFileTaskRunner()->PostTask(
269       FROM_HERE,
270       base::BindOnce(base::IgnoreResult(&base::DeleteFile),
271                      source.json_path().DirName(), false /* recursive */));
272 }
273 
OnRulesetLoaded(LoadRequestData load_data)274 void RulesMonitorService::OnRulesetLoaded(LoadRequestData load_data) {
275   // Currently we only support a single static and an optional dynamic ruleset
276   // per extension.
277   DCHECK(load_data.rulesets.size() == 1u || load_data.rulesets.size() == 2u);
278   RulesetInfo& static_ruleset = load_data.rulesets[0];
279   DCHECK_GE(static_ruleset.source().id(), kMinValidStaticRulesetID)
280       << static_ruleset.source().id();
281 
282   RulesetInfo* dynamic_ruleset =
283       load_data.rulesets.size() == 2 ? &load_data.rulesets[1] : nullptr;
284   DCHECK(!dynamic_ruleset ||
285          dynamic_ruleset->source().id() == kDynamicRulesetID)
286       << dynamic_ruleset->source().id();
287 
288   // Update the ruleset checksums if needed.
289   if (static_ruleset.new_checksum()) {
290     prefs_->SetDNRStaticRulesetChecksum(load_data.extension_id,
291                                         static_ruleset.source().id(),
292                                         *(static_ruleset.new_checksum()));
293   }
294 
295   if (dynamic_ruleset && dynamic_ruleset->new_checksum()) {
296     prefs_->SetDNRDynamicRulesetChecksum(load_data.extension_id,
297                                          *(dynamic_ruleset->new_checksum()));
298   }
299 
300   // It's possible that the extension has been disabled since the initial load
301   // ruleset request. If it's disabled, do nothing.
302   if (!extension_registry_->enabled_extensions().Contains(
303           load_data.extension_id))
304     return;
305 
306   CompositeMatcher::MatcherList matchers;
307   if (static_ruleset.did_load_successfully()) {
308     matchers.push_back(static_ruleset.TakeMatcher());
309   }
310   if (dynamic_ruleset && dynamic_ruleset->did_load_successfully()) {
311     matchers.push_back(dynamic_ruleset->TakeMatcher());
312   }
313 
314   // A ruleset failed to load. Notify the user.
315   if (matchers.size() < load_data.rulesets.size()) {
316     warning_service_->AddWarnings(
317         {Warning::CreateRulesetFailedToLoadWarning(load_data.extension_id)});
318   }
319 
320   if (matchers.empty())
321     return;
322 
323   extensions_with_rulesets_.insert(load_data.extension_id);
324   LoadRuleset(load_data.extension_id,
325               std::make_unique<CompositeMatcher>(std::move(matchers)));
326 }
327 
OnDynamicRulesUpdated(DynamicRuleUpdateUICallback callback,LoadRequestData load_data,base::Optional<std::string> error)328 void RulesMonitorService::OnDynamicRulesUpdated(
329     DynamicRuleUpdateUICallback callback,
330     LoadRequestData load_data,
331     base::Optional<std::string> error) {
332   DCHECK_EQ(1u, load_data.rulesets.size());
333 
334   RulesetInfo& dynamic_ruleset = load_data.rulesets[0];
335   DCHECK_EQ(dynamic_ruleset.did_load_successfully(), !error.has_value());
336 
337   // Update the ruleset checksums if needed. Note it's possible that
338   // new_checksum() is valid while did_load_successfully() returns false below.
339   // This should be rare but can happen when updating the rulesets succeeds but
340   // we fail to create a RulesetMatcher from the indexed ruleset file (e.g. due
341   // to a file read error). We still update the prefs checksum to ensure the
342   // next ruleset load succeeds.
343   if (dynamic_ruleset.new_checksum()) {
344     prefs_->SetDNRDynamicRulesetChecksum(load_data.extension_id,
345                                          *dynamic_ruleset.new_checksum());
346   }
347 
348   // Respond to the extension.
349   std::move(callback).Run(std::move(error));
350 
351   if (!dynamic_ruleset.did_load_successfully())
352     return;
353 
354   DCHECK(dynamic_ruleset.new_checksum());
355 
356   // It's possible that the extension has been disabled since the initial update
357   // rule request. If it's disabled, do nothing.
358   if (!extension_registry_->enabled_extensions().Contains(
359           load_data.extension_id)) {
360     return;
361   }
362 
363   // Update the dynamic ruleset.
364   UpdateRuleset(load_data.extension_id, dynamic_ruleset.TakeMatcher());
365 }
366 
UnloadRuleset(const ExtensionId & extension_id)367 void RulesMonitorService::UnloadRuleset(const ExtensionId& extension_id) {
368   bool had_extra_headers_matcher = ruleset_manager_.HasAnyExtraHeadersMatcher();
369   ruleset_manager_.RemoveRuleset(extension_id);
370   action_tracker_.ClearExtensionData(extension_id);
371 
372   if (had_extra_headers_matcher &&
373       !ruleset_manager_.HasAnyExtraHeadersMatcher()) {
374     ExtensionWebRequestEventRouter::GetInstance()
375         ->DecrementExtraHeadersListenerCount(context_);
376   }
377 }
378 
LoadRuleset(const ExtensionId & extension_id,std::unique_ptr<CompositeMatcher> matcher)379 void RulesMonitorService::LoadRuleset(
380     const ExtensionId& extension_id,
381     std::unique_ptr<CompositeMatcher> matcher) {
382   bool increment_extra_headers =
383       !ruleset_manager_.HasAnyExtraHeadersMatcher() &&
384       matcher->HasAnyExtraHeadersMatcher();
385   ruleset_manager_.AddRuleset(extension_id, std::move(matcher));
386 
387   if (increment_extra_headers) {
388     ExtensionWebRequestEventRouter::GetInstance()
389         ->IncrementExtraHeadersListenerCount(context_);
390   }
391 }
392 
UpdateRuleset(const ExtensionId & extension_id,std::unique_ptr<RulesetMatcher> ruleset_matcher)393 void RulesMonitorService::UpdateRuleset(
394     const ExtensionId& extension_id,
395     std::unique_ptr<RulesetMatcher> ruleset_matcher) {
396   bool had_extra_headers_matcher = ruleset_manager_.HasAnyExtraHeadersMatcher();
397 
398   CompositeMatcher* matcher =
399       ruleset_manager_.GetMatcherForExtension(extension_id);
400   DCHECK(matcher);
401   matcher->AddOrUpdateRuleset(std::move(ruleset_matcher));
402 
403   bool has_extra_headers_matcher = ruleset_manager_.HasAnyExtraHeadersMatcher();
404   if (had_extra_headers_matcher == has_extra_headers_matcher)
405     return;
406   if (has_extra_headers_matcher) {
407     ExtensionWebRequestEventRouter::GetInstance()
408         ->IncrementExtraHeadersListenerCount(context_);
409   } else {
410     ExtensionWebRequestEventRouter::GetInstance()
411         ->DecrementExtraHeadersListenerCount(context_);
412   }
413 }
414 
415 }  // namespace declarative_net_request
416 
417 template <>
418 void BrowserContextKeyedAPIFactory<
419     declarative_net_request::RulesMonitorService>::
DeclareFactoryDependencies()420     DeclareFactoryDependencies() {
421   DependsOn(ExtensionRegistryFactory::GetInstance());
422   DependsOn(ExtensionPrefsFactory::GetInstance());
423   DependsOn(WarningServiceFactory::GetInstance());
424   DependsOn(PermissionHelper::GetFactoryInstance());
425 }
426 
427 }  // namespace extensions
428