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 ¶ms.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 ¶ms.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(¶ms.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