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