extension_function_dispatcher.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
1// Copyright 2014 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/extension_function_dispatcher.h"
6
7#include "base/bind.h"
8#include "base/json/json_string_value_serializer.h"
9#include "base/lazy_instance.h"
10#include "base/logging.h"
11#include "base/memory/ref_counted.h"
12#include "base/process/process.h"
13#include "base/values.h"
14#include "build/build_config.h"
15#include "chrome/browser/renderer_host/chrome_render_message_filter.h"
16#include "content/public/browser/browser_thread.h"
17#include "content/public/browser/render_frame_host.h"
18#include "content/public/browser/render_process_host.h"
19#include "content/public/browser/render_view_host.h"
20#include "content/public/browser/user_metrics.h"
21#include "content/public/browser/web_contents.h"
22#include "content/public/browser/web_contents_observer.h"
23#include "content/public/common/result_codes.h"
24#include "extensions/browser/api_activity_monitor.h"
25#include "extensions/browser/extension_function_registry.h"
26#include "extensions/browser/extension_registry.h"
27#include "extensions/browser/extension_system.h"
28#include "extensions/browser/extensions_browser_client.h"
29#include "extensions/browser/process_manager.h"
30#include "extensions/browser/process_map.h"
31#include "extensions/browser/quota_service.h"
32#include "extensions/common/extension_api.h"
33#include "extensions/common/extension_messages.h"
34#include "extensions/common/extension_set.h"
35#include "ipc/ipc_message.h"
36#include "ipc/ipc_message_macros.h"
37
38using extensions::Extension;
39using extensions::ExtensionAPI;
40using extensions::ExtensionsBrowserClient;
41using extensions::ExtensionSystem;
42using extensions::Feature;
43using content::BrowserThread;
44using content::RenderViewHost;
45
46namespace {
47
48// Notifies the ApiActivityMonitor that an extension API function has been
49// called. May be called from any thread.
50void NotifyApiFunctionCalled(const std::string& extension_id,
51                             const std::string& api_name,
52                             scoped_ptr<base::ListValue> args,
53                             content::BrowserContext* browser_context) {
54  // The ApiActivityMonitor can only be accessed from the main (UI) thread. If
55  // we're running on the wrong thread, re-dispatch from the main thread.
56  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
57    BrowserThread::PostTask(BrowserThread::UI,
58                            FROM_HERE,
59                            base::Bind(&NotifyApiFunctionCalled,
60                                       extension_id,
61                                       api_name,
62                                       base::Passed(&args),
63                                       browser_context));
64    return;
65  }
66  // The BrowserContext may become invalid after the task above is posted.
67  if (!ExtensionsBrowserClient::Get()->IsValidContext(browser_context))
68    return;
69
70  extensions::ApiActivityMonitor* monitor =
71      ExtensionsBrowserClient::Get()->GetApiActivityMonitor(browser_context);
72  if (monitor)
73    monitor->OnApiFunctionCalled(extension_id, api_name, args.Pass());
74}
75
76// Separate copy of ExtensionAPI used for IO thread extension functions. We need
77// this because ExtensionAPI has mutable data. It should be possible to remove
78// this once all the extension APIs are updated to the feature system.
79struct Static {
80  Static()
81      : api(extensions::ExtensionAPI::CreateWithDefaultConfiguration()) {
82  }
83  scoped_ptr<extensions::ExtensionAPI> api;
84};
85base::LazyInstance<Static> g_global_io_data = LAZY_INSTANCE_INITIALIZER;
86
87// Kills the specified process because it sends us a malformed message.
88void KillBadMessageSender(base::ProcessHandle process) {
89  NOTREACHED();
90  content::RecordAction(base::UserMetricsAction("BadMessageTerminate_EFD"));
91  if (process)
92    base::KillProcess(process, content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
93}
94
95void CommonResponseCallback(IPC::Sender* ipc_sender,
96                            int routing_id,
97                            base::ProcessHandle peer_process,
98                            int request_id,
99                            ExtensionFunction::ResponseType type,
100                            const base::ListValue& results,
101                            const std::string& error) {
102  DCHECK(ipc_sender);
103
104  if (type == ExtensionFunction::BAD_MESSAGE) {
105    // The renderer has done validation before sending extension api requests.
106    // Therefore, we should never receive a request that is invalid in a way
107    // that JSON validation in the renderer should have caught. It could be an
108    // attacker trying to exploit the browser, so we crash the renderer instead.
109    LOG(ERROR) <<
110        "Terminating renderer because of malformed extension message.";
111    if (content::RenderProcessHost::run_renderer_in_process()) {
112      // In single process mode it is better if we don't suicide but just crash.
113      CHECK(false);
114    } else {
115      KillBadMessageSender(peer_process);
116    }
117
118    return;
119  }
120
121  ipc_sender->Send(new ExtensionMsg_Response(
122      routing_id, request_id, type == ExtensionFunction::SUCCEEDED, results,
123      error));
124}
125
126void IOThreadResponseCallback(
127    const base::WeakPtr<ChromeRenderMessageFilter>& ipc_sender,
128    int routing_id,
129    int request_id,
130    ExtensionFunction::ResponseType type,
131    const base::ListValue& results,
132    const std::string& error) {
133  if (!ipc_sender.get())
134    return;
135
136  CommonResponseCallback(ipc_sender.get(),
137                         routing_id,
138                         ipc_sender->PeerHandle(),
139                         request_id,
140                         type,
141                         results,
142                         error);
143}
144
145}  // namespace
146
147class ExtensionFunctionDispatcher::UIThreadResponseCallbackWrapper
148    : public content::WebContentsObserver {
149 public:
150  UIThreadResponseCallbackWrapper(
151      const base::WeakPtr<ExtensionFunctionDispatcher>& dispatcher,
152      RenderViewHost* render_view_host)
153      : content::WebContentsObserver(
154            content::WebContents::FromRenderViewHost(render_view_host)),
155        dispatcher_(dispatcher),
156        render_view_host_(render_view_host),
157        weak_ptr_factory_(this) {
158  }
159
160  virtual ~UIThreadResponseCallbackWrapper() {
161  }
162
163  // content::WebContentsObserver overrides.
164  virtual void RenderViewDeleted(
165      RenderViewHost* render_view_host) OVERRIDE {
166    DCHECK_CURRENTLY_ON(BrowserThread::UI);
167    if (render_view_host != render_view_host_)
168      return;
169
170    if (dispatcher_.get()) {
171      dispatcher_->ui_thread_response_callback_wrappers_
172          .erase(render_view_host);
173    }
174
175    delete this;
176  }
177
178  ExtensionFunction::ResponseCallback CreateCallback(int request_id) {
179    return base::Bind(
180        &UIThreadResponseCallbackWrapper::OnExtensionFunctionCompleted,
181        weak_ptr_factory_.GetWeakPtr(),
182        request_id);
183  }
184
185 private:
186  void OnExtensionFunctionCompleted(int request_id,
187                                    ExtensionFunction::ResponseType type,
188                                    const base::ListValue& results,
189                                    const std::string& error) {
190    CommonResponseCallback(
191        render_view_host_, render_view_host_->GetRoutingID(),
192        render_view_host_->GetProcess()->GetHandle(), request_id, type,
193        results, error);
194  }
195
196  base::WeakPtr<ExtensionFunctionDispatcher> dispatcher_;
197  content::RenderViewHost* render_view_host_;
198  base::WeakPtrFactory<UIThreadResponseCallbackWrapper> weak_ptr_factory_;
199
200  DISALLOW_COPY_AND_ASSIGN(UIThreadResponseCallbackWrapper);
201};
202
203extensions::WindowController*
204ExtensionFunctionDispatcher::Delegate::GetExtensionWindowController()
205    const {
206  return NULL;
207}
208
209content::WebContents*
210ExtensionFunctionDispatcher::Delegate::GetAssociatedWebContents() const {
211  return NULL;
212}
213
214content::WebContents*
215ExtensionFunctionDispatcher::Delegate::GetVisibleWebContents() const {
216  return GetAssociatedWebContents();
217}
218
219void ExtensionFunctionDispatcher::GetAllFunctionNames(
220    std::vector<std::string>* names) {
221  ExtensionFunctionRegistry::GetInstance()->GetAllNames(names);
222}
223
224bool ExtensionFunctionDispatcher::OverrideFunction(
225    const std::string& name, ExtensionFunctionFactory factory) {
226  return ExtensionFunctionRegistry::GetInstance()->OverrideFunction(name,
227                                                                    factory);
228}
229
230// static
231void ExtensionFunctionDispatcher::DispatchOnIOThread(
232    extensions::InfoMap* extension_info_map,
233    void* browser_context,
234    int render_process_id,
235    base::WeakPtr<ChromeRenderMessageFilter> ipc_sender,
236    int routing_id,
237    const ExtensionHostMsg_Request_Params& params) {
238  const Extension* extension =
239      extension_info_map->extensions().GetByID(params.extension_id);
240
241  ExtensionFunction::ResponseCallback callback(
242      base::Bind(&IOThreadResponseCallback, ipc_sender, routing_id,
243                 params.request_id));
244
245  scoped_refptr<ExtensionFunction> function(
246      CreateExtensionFunction(params, extension, render_process_id,
247                              extension_info_map->process_map(),
248                              g_global_io_data.Get().api.get(),
249                              browser_context, callback));
250  if (!function.get())
251    return;
252
253  IOThreadExtensionFunction* function_io =
254      function->AsIOThreadExtensionFunction();
255  if (!function_io) {
256    NOTREACHED();
257    return;
258  }
259  function_io->set_ipc_sender(ipc_sender, routing_id);
260  function_io->set_extension_info_map(extension_info_map);
261  function->set_include_incognito(
262      extension_info_map->IsIncognitoEnabled(extension->id()));
263
264  if (!CheckPermissions(function.get(), extension, params, callback))
265    return;
266
267  extensions::QuotaService* quota = extension_info_map->GetQuotaService();
268  std::string violation_error = quota->Assess(extension->id(),
269                                              function.get(),
270                                              &params.arguments,
271                                              base::TimeTicks::Now());
272  if (violation_error.empty()) {
273    scoped_ptr<base::ListValue> args(params.arguments.DeepCopy());
274    NotifyApiFunctionCalled(
275        extension->id(),
276        params.name,
277        args.Pass(),
278        static_cast<content::BrowserContext*>(browser_context));
279    function->Run();
280  } else {
281    function->OnQuotaExceeded(violation_error);
282  }
283}
284
285ExtensionFunctionDispatcher::ExtensionFunctionDispatcher(
286    content::BrowserContext* browser_context,
287    Delegate* delegate)
288    : browser_context_(browser_context),
289      delegate_(delegate) {
290}
291
292ExtensionFunctionDispatcher::~ExtensionFunctionDispatcher() {
293}
294
295void ExtensionFunctionDispatcher::Dispatch(
296    const ExtensionHostMsg_Request_Params& params,
297    RenderViewHost* render_view_host) {
298  UIThreadResponseCallbackWrapperMap::const_iterator
299      iter = ui_thread_response_callback_wrappers_.find(render_view_host);
300  UIThreadResponseCallbackWrapper* callback_wrapper = NULL;
301  if (iter == ui_thread_response_callback_wrappers_.end()) {
302    callback_wrapper = new UIThreadResponseCallbackWrapper(AsWeakPtr(),
303                                                           render_view_host);
304    ui_thread_response_callback_wrappers_[render_view_host] = callback_wrapper;
305  } else {
306    callback_wrapper = iter->second;
307  }
308
309  DispatchWithCallbackInternal(
310      params, render_view_host, NULL,
311      callback_wrapper->CreateCallback(params.request_id));
312}
313
314void ExtensionFunctionDispatcher::DispatchWithCallback(
315    const ExtensionHostMsg_Request_Params& params,
316    content::RenderFrameHost* render_frame_host,
317    const ExtensionFunction::ResponseCallback& callback) {
318  DispatchWithCallbackInternal(params, NULL, render_frame_host, callback);
319}
320
321void ExtensionFunctionDispatcher::DispatchWithCallbackInternal(
322    const ExtensionHostMsg_Request_Params& params,
323    RenderViewHost* render_view_host,
324    content::RenderFrameHost* render_frame_host,
325    const ExtensionFunction::ResponseCallback& callback) {
326  DCHECK(render_view_host || render_frame_host);
327  // TODO(yzshen): There is some shared logic between this method and
328  // DispatchOnIOThread(). It is nice to deduplicate.
329  extensions::ProcessMap* process_map =
330      extensions::ProcessMap::Get(browser_context_);
331  if (!process_map)
332    return;
333
334  extensions::ExtensionRegistry* registry =
335      extensions::ExtensionRegistry::Get(browser_context_);
336  const Extension* extension = registry->enabled_extensions().GetByID(
337      params.extension_id);
338  if (!extension) {
339    extension =
340        registry->enabled_extensions().GetHostedAppByURL(params.source_url);
341  }
342
343  int process_id = render_view_host ? render_view_host->GetProcess()->GetID() :
344                                      render_frame_host->GetProcess()->GetID();
345  scoped_refptr<ExtensionFunction> function(
346      CreateExtensionFunction(params,
347                              extension,
348                              process_id,
349                              *process_map,
350                              extensions::ExtensionAPI::GetSharedInstance(),
351                              browser_context_,
352                              callback));
353  if (!function.get())
354    return;
355
356  UIThreadExtensionFunction* function_ui =
357      function->AsUIThreadExtensionFunction();
358  if (!function_ui) {
359    NOTREACHED();
360    return;
361  }
362  if (render_view_host) {
363    function_ui->SetRenderViewHost(render_view_host);
364  } else {
365    function_ui->SetRenderFrameHost(render_frame_host);
366  }
367  function_ui->set_dispatcher(AsWeakPtr());
368  function_ui->set_browser_context(browser_context_);
369  function->set_include_incognito(
370      ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito(
371          extension, browser_context_));
372
373  if (!CheckPermissions(function.get(), extension, params, callback))
374    return;
375
376  ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_);
377  extensions::QuotaService* quota = extension_system->quota_service();
378  std::string violation_error = quota->Assess(extension->id(),
379                                              function.get(),
380                                              &params.arguments,
381                                              base::TimeTicks::Now());
382  if (violation_error.empty()) {
383    scoped_ptr<base::ListValue> args(params.arguments.DeepCopy());
384
385    // See crbug.com/39178.
386    ExtensionsBrowserClient::Get()->PermitExternalProtocolHandler();
387    NotifyApiFunctionCalled(
388        extension->id(), params.name, args.Pass(), browser_context_);
389    function->Run();
390  } else {
391    function->OnQuotaExceeded(violation_error);
392  }
393
394  // Note: do not access |this| after this point. We may have been deleted
395  // if function->Run() ended up closing the tab that owns us.
396
397  // Check if extension was uninstalled by management.uninstall.
398  if (!registry->enabled_extensions().GetByID(params.extension_id))
399    return;
400
401  // We only adjust the keepalive count for UIThreadExtensionFunction for
402  // now, largely for simplicity's sake. This is OK because currently, only
403  // the webRequest API uses IOThreadExtensionFunction, and that API is not
404  // compatible with lazy background pages.
405  extension_system->process_manager()->IncrementLazyKeepaliveCount(extension);
406}
407
408void ExtensionFunctionDispatcher::OnExtensionFunctionCompleted(
409    const Extension* extension) {
410  ExtensionSystem::Get(browser_context_)->process_manager()->
411      DecrementLazyKeepaliveCount(extension);
412}
413
414// static
415bool ExtensionFunctionDispatcher::CheckPermissions(
416    ExtensionFunction* function,
417    const Extension* extension,
418    const ExtensionHostMsg_Request_Params& params,
419    const ExtensionFunction::ResponseCallback& callback) {
420  if (!function->HasPermission()) {
421    LOG(ERROR) << "Extension " << extension->id() << " does not have "
422               << "permission to function: " << params.name;
423    SendAccessDenied(callback);
424    return false;
425  }
426  return true;
427}
428
429namespace {
430
431// Only COMPONENT hosted apps may call extension APIs, and they are limited
432// to just the permissions they explicitly request. They should not have access
433// to extension APIs like eg chrome.runtime, chrome.windows, etc. that normally
434// are available without permission.
435// TODO(mpcomplete): move this to ExtensionFunction::HasPermission (or remove
436// it altogether).
437bool AllowHostedAppAPICall(const Extension& extension,
438                           const GURL& source_url,
439                           const std::string& function_name) {
440  if (extension.location() != extensions::Manifest::COMPONENT)
441    return false;
442
443  if (!extension.web_extent().MatchesURL(source_url))
444    return false;
445
446  // Note: Not BLESSED_WEB_PAGE_CONTEXT here because these component hosted app
447  // entities have traditionally been treated as blessed extensions, for better
448  // or worse.
449  Feature::Availability availability =
450      ExtensionAPI::GetSharedInstance()->IsAvailable(
451          function_name, &extension, Feature::BLESSED_EXTENSION_CONTEXT,
452          source_url);
453  return availability.is_available();
454}
455
456}  // namespace
457
458
459// static
460ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction(
461    const ExtensionHostMsg_Request_Params& params,
462    const Extension* extension,
463    int requesting_process_id,
464    const extensions::ProcessMap& process_map,
465    extensions::ExtensionAPI* api,
466    void* profile,
467    const ExtensionFunction::ResponseCallback& callback) {
468  if (!extension) {
469    LOG(ERROR) << "Specified extension does not exist.";
470    SendAccessDenied(callback);
471    return NULL;
472  }
473
474  // Most hosted apps can't call APIs.
475  bool allowed = true;
476  if (extension->is_hosted_app())
477    allowed = AllowHostedAppAPICall(*extension, params.source_url, params.name);
478
479  // Privileged APIs can only be called from the process the extension
480  // is running in.
481  if (allowed && api->IsPrivileged(params.name))
482    allowed = process_map.Contains(extension->id(), requesting_process_id);
483
484  if (!allowed) {
485    LOG(ERROR) << "Extension API call disallowed - name:" << params.name
486               << " pid:" << requesting_process_id
487               << " from URL " << params.source_url.spec();
488    SendAccessDenied(callback);
489    return NULL;
490  }
491
492  ExtensionFunction* function =
493      ExtensionFunctionRegistry::GetInstance()->NewFunction(params.name);
494  if (!function) {
495    LOG(ERROR) << "Unknown Extension API - " << params.name;
496    SendAccessDenied(callback);
497    return NULL;
498  }
499
500  function->SetArgs(&params.arguments);
501  function->set_source_url(params.source_url);
502  function->set_request_id(params.request_id);
503  function->set_has_callback(params.has_callback);
504  function->set_user_gesture(params.user_gesture);
505  function->set_extension(extension);
506  function->set_profile_id(profile);
507  function->set_response_callback(callback);
508  function->set_source_tab_id(params.source_tab_id);
509
510  return function;
511}
512
513// static
514void ExtensionFunctionDispatcher::SendAccessDenied(
515    const ExtensionFunction::ResponseCallback& callback) {
516  base::ListValue empty_list;
517  callback.Run(ExtensionFunction::FAILED, empty_list,
518               "Access to extension API denied.");
519}
520