| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "extensions/browser/extension_function_dispatcher.h" | 5 #include "extensions/browser/extension_function_dispatcher.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/json/json_string_value_serializer.h" | 8 #include "base/json/json_string_value_serializer.h" |
| 9 #include "base/lazy_instance.h" | 9 #include "base/lazy_instance.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/memory/ref_counted.h" | 11 #include "base/memory/ref_counted.h" |
| 12 #include "base/metrics/sparse_histogram.h" | 12 #include "base/metrics/sparse_histogram.h" |
| 13 #include "base/process/process.h" | 13 #include "base/process/process.h" |
| 14 #include "base/values.h" | 14 #include "base/values.h" |
| 15 #include "build/build_config.h" | 15 #include "build/build_config.h" |
| 16 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
| 17 #include "content/public/browser/child_process_security_policy.h" |
| 17 #include "content/public/browser/render_frame_host.h" | 18 #include "content/public/browser/render_frame_host.h" |
| 18 #include "content/public/browser/render_process_host.h" | 19 #include "content/public/browser/render_process_host.h" |
| 19 #include "content/public/browser/render_view_host.h" | 20 #include "content/public/browser/render_view_host.h" |
| 20 #include "content/public/browser/user_metrics.h" | 21 #include "content/public/browser/user_metrics.h" |
| 21 #include "content/public/browser/web_contents.h" | 22 #include "content/public/browser/web_contents.h" |
| 22 #include "content/public/browser/web_contents_observer.h" | 23 #include "content/public/browser/web_contents_observer.h" |
| 23 #include "content/public/common/result_codes.h" | 24 #include "content/public/common/result_codes.h" |
| 24 #include "extensions/browser/api_activity_monitor.h" | 25 #include "extensions/browser/api_activity_monitor.h" |
| 25 #include "extensions/browser/extension_function_registry.h" | 26 #include "extensions/browser/extension_function_registry.h" |
| 26 #include "extensions/browser/extension_message_filter.h" | 27 #include "extensions/browser/extension_message_filter.h" |
| (...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 251 function->AsIOThreadExtensionFunction(); | 252 function->AsIOThreadExtensionFunction(); |
| 252 if (!function_io) { | 253 if (!function_io) { |
| 253 NOTREACHED(); | 254 NOTREACHED(); |
| 254 return; | 255 return; |
| 255 } | 256 } |
| 256 function_io->set_ipc_sender(ipc_sender, routing_id); | 257 function_io->set_ipc_sender(ipc_sender, routing_id); |
| 257 function_io->set_extension_info_map(extension_info_map); | 258 function_io->set_extension_info_map(extension_info_map); |
| 258 function->set_include_incognito( | 259 function->set_include_incognito( |
| 259 extension_info_map->IsIncognitoEnabled(extension->id())); | 260 extension_info_map->IsIncognitoEnabled(extension->id())); |
| 260 | 261 |
| 261 if (!CheckPermissions(function.get(), extension, params, callback)) | 262 if (!CheckPermissions(function.get(), params, callback)) |
| 262 return; | 263 return; |
| 263 | 264 |
| 264 QuotaService* quota = extension_info_map->GetQuotaService(); | 265 QuotaService* quota = extension_info_map->GetQuotaService(); |
| 265 std::string violation_error = quota->Assess(extension->id(), | 266 std::string violation_error = quota->Assess(extension->id(), |
| 266 function.get(), | 267 function.get(), |
| 267 ¶ms.arguments, | 268 ¶ms.arguments, |
| 268 base::TimeTicks::Now()); | 269 base::TimeTicks::Now()); |
| 269 if (violation_error.empty()) { | 270 if (violation_error.empty()) { |
| 270 scoped_ptr<base::ListValue> args(params.arguments.DeepCopy()); | 271 scoped_ptr<base::ListValue> args(params.arguments.DeepCopy()); |
| 271 NotifyApiFunctionCalled(extension->id(), | 272 NotifyApiFunctionCalled(extension->id(), |
| (...skipping 30 matching lines...) Expand all Loading... |
| 302 ui_thread_response_callback_wrappers_[render_view_host] = callback_wrapper; | 303 ui_thread_response_callback_wrappers_[render_view_host] = callback_wrapper; |
| 303 } else { | 304 } else { |
| 304 callback_wrapper = iter->second; | 305 callback_wrapper = iter->second; |
| 305 } | 306 } |
| 306 | 307 |
| 307 DispatchWithCallbackInternal( | 308 DispatchWithCallbackInternal( |
| 308 params, render_view_host, NULL, | 309 params, render_view_host, NULL, |
| 309 callback_wrapper->CreateCallback(params.request_id)); | 310 callback_wrapper->CreateCallback(params.request_id)); |
| 310 } | 311 } |
| 311 | 312 |
| 312 void ExtensionFunctionDispatcher::DispatchWithCallback( | |
| 313 const ExtensionHostMsg_Request_Params& params, | |
| 314 content::RenderFrameHost* render_frame_host, | |
| 315 const ExtensionFunction::ResponseCallback& callback) { | |
| 316 DispatchWithCallbackInternal(params, NULL, render_frame_host, callback); | |
| 317 } | |
| 318 | |
| 319 void ExtensionFunctionDispatcher::DispatchWithCallbackInternal( | 313 void ExtensionFunctionDispatcher::DispatchWithCallbackInternal( |
| 320 const ExtensionHostMsg_Request_Params& params, | 314 const ExtensionHostMsg_Request_Params& params, |
| 321 RenderViewHost* render_view_host, | 315 RenderViewHost* render_view_host, |
| 322 content::RenderFrameHost* render_frame_host, | 316 content::RenderFrameHost* render_frame_host, |
| 323 const ExtensionFunction::ResponseCallback& callback) { | 317 const ExtensionFunction::ResponseCallback& callback) { |
| 324 DCHECK(render_view_host || render_frame_host); | 318 DCHECK(render_view_host || render_frame_host); |
| 325 // TODO(yzshen): There is some shared logic between this method and | 319 // TODO(yzshen): There is some shared logic between this method and |
| 326 // DispatchOnIOThread(). It is nice to deduplicate. | 320 // DispatchOnIOThread(). It is nice to deduplicate. |
| 327 ProcessMap* process_map = ProcessMap::Get(browser_context_); | 321 ProcessMap* process_map = ProcessMap::Get(browser_context_); |
| 328 if (!process_map) | 322 if (!process_map) |
| 329 return; | 323 return; |
| 330 | 324 |
| 331 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_); | 325 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_); |
| 332 const Extension* extension = registry->enabled_extensions().GetByID( | 326 const Extension* extension = |
| 333 params.extension_id); | 327 registry->enabled_extensions().GetByID(params.extension_id); |
| 334 if (!extension) { | 328 if (!extension) { |
| 335 extension = | 329 extension = |
| 336 registry->enabled_extensions().GetHostedAppByURL(params.source_url); | 330 registry->enabled_extensions().GetHostedAppByURL(params.source_url); |
| 337 } | 331 } |
| 338 | 332 |
| 339 int process_id = render_view_host ? render_view_host->GetProcess()->GetID() : | 333 int process_id = render_view_host ? render_view_host->GetProcess()->GetID() : |
| 340 render_frame_host->GetProcess()->GetID(); | 334 render_frame_host->GetProcess()->GetID(); |
| 341 scoped_refptr<ExtensionFunction> function( | 335 scoped_refptr<ExtensionFunction> function( |
| 342 CreateExtensionFunction(params, | 336 CreateExtensionFunction(params, |
| 343 extension, | 337 extension, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 355 NOTREACHED(); | 349 NOTREACHED(); |
| 356 return; | 350 return; |
| 357 } | 351 } |
| 358 if (render_view_host) { | 352 if (render_view_host) { |
| 359 function_ui->SetRenderViewHost(render_view_host); | 353 function_ui->SetRenderViewHost(render_view_host); |
| 360 } else { | 354 } else { |
| 361 function_ui->SetRenderFrameHost(render_frame_host); | 355 function_ui->SetRenderFrameHost(render_frame_host); |
| 362 } | 356 } |
| 363 function_ui->set_dispatcher(AsWeakPtr()); | 357 function_ui->set_dispatcher(AsWeakPtr()); |
| 364 function_ui->set_browser_context(browser_context_); | 358 function_ui->set_browser_context(browser_context_); |
| 365 function->set_include_incognito( | 359 if (extension && |
| 366 ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito( | 360 ExtensionsBrowserClient::Get()->CanExtensionCrossIncognito( |
| 367 extension, browser_context_)); | 361 extension, browser_context_)) { |
| 362 function->set_include_incognito(true); |
| 363 } |
| 368 | 364 |
| 369 if (!CheckPermissions(function.get(), extension, params, callback)) | 365 if (!CheckPermissions(function.get(), params, callback)) |
| 370 return; | 366 return; |
| 371 | 367 |
| 372 ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_); | 368 if (extension) { |
| 373 QuotaService* quota = extension_system->quota_service(); | 369 ExtensionSystem* extension_system = ExtensionSystem::Get(browser_context_); |
| 374 std::string violation_error = quota->Assess(extension->id(), | 370 QuotaService* quota = extension_system->quota_service(); |
| 375 function.get(), | 371 std::string violation_error = quota->Assess(extension->id(), |
| 376 ¶ms.arguments, | 372 function.get(), |
| 377 base::TimeTicks::Now()); | 373 ¶ms.arguments, |
| 378 if (violation_error.empty()) { | 374 base::TimeTicks::Now()); |
| 379 scoped_ptr<base::ListValue> args(params.arguments.DeepCopy()); | |
| 380 | 375 |
| 381 NotifyApiFunctionCalled( | 376 if (violation_error.empty()) { |
| 382 extension->id(), params.name, args.Pass(), browser_context_); | 377 scoped_ptr<base::ListValue> args(params.arguments.DeepCopy()); |
| 383 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.FunctionCalls", | 378 |
| 384 function->histogram_value()); | 379 NotifyApiFunctionCalled( |
| 380 extension->id(), params.name, args.Pass(), browser_context_); |
| 381 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.FunctionCalls", |
| 382 function->histogram_value()); |
| 383 function->Run()->Execute(); |
| 384 } else { |
| 385 function->OnQuotaExceeded(violation_error); |
| 386 } |
| 387 |
| 388 // Note: do not access |this| after this point. We may have been deleted |
| 389 // if function->Run() ended up closing the tab that owns us. |
| 390 |
| 391 // Check if extension was uninstalled by management.uninstall. |
| 392 if (!registry->enabled_extensions().GetByID(params.extension_id)) |
| 393 return; |
| 394 |
| 395 // We only adjust the keepalive count for UIThreadExtensionFunction for |
| 396 // now, largely for simplicity's sake. This is OK because currently, only |
| 397 // the webRequest API uses IOThreadExtensionFunction, and that API is not |
| 398 // compatible with lazy background pages. |
| 399 extension_system->process_manager()->IncrementLazyKeepaliveCount(extension); |
| 400 } else { |
| 401 // Skip all of the UMA, quota, event page, activity logging stuff if there |
| 402 // isn't an extension. |
| 385 function->Run()->Execute(); | 403 function->Run()->Execute(); |
| 386 } else { | |
| 387 function->OnQuotaExceeded(violation_error); | |
| 388 } | 404 } |
| 389 | |
| 390 // Note: do not access |this| after this point. We may have been deleted | |
| 391 // if function->Run() ended up closing the tab that owns us. | |
| 392 | |
| 393 // Check if extension was uninstalled by management.uninstall. | |
| 394 if (!registry->enabled_extensions().GetByID(params.extension_id)) | |
| 395 return; | |
| 396 | |
| 397 // We only adjust the keepalive count for UIThreadExtensionFunction for | |
| 398 // now, largely for simplicity's sake. This is OK because currently, only | |
| 399 // the webRequest API uses IOThreadExtensionFunction, and that API is not | |
| 400 // compatible with lazy background pages. | |
| 401 extension_system->process_manager()->IncrementLazyKeepaliveCount(extension); | |
| 402 } | 405 } |
| 403 | 406 |
| 404 void ExtensionFunctionDispatcher::OnExtensionFunctionCompleted( | 407 void ExtensionFunctionDispatcher::OnExtensionFunctionCompleted( |
| 405 const Extension* extension) { | 408 const Extension* extension) { |
| 406 ExtensionSystem::Get(browser_context_)->process_manager()-> | 409 if (extension) { |
| 407 DecrementLazyKeepaliveCount(extension); | 410 ExtensionSystem::Get(browser_context_) |
| 411 ->process_manager() |
| 412 ->DecrementLazyKeepaliveCount(extension); |
| 413 } |
| 408 } | 414 } |
| 409 | 415 |
| 410 // static | 416 // static |
| 411 bool ExtensionFunctionDispatcher::CheckPermissions( | 417 bool ExtensionFunctionDispatcher::CheckPermissions( |
| 412 ExtensionFunction* function, | 418 ExtensionFunction* function, |
| 413 const Extension* extension, | |
| 414 const ExtensionHostMsg_Request_Params& params, | 419 const ExtensionHostMsg_Request_Params& params, |
| 415 const ExtensionFunction::ResponseCallback& callback) { | 420 const ExtensionFunction::ResponseCallback& callback) { |
| 416 if (!function->HasPermission()) { | 421 if (!function->HasPermission()) { |
| 417 LOG(ERROR) << "Extension " << extension->id() << " does not have " | 422 LOG(ERROR) << "Permission denied for " << params.name; |
| 418 << "permission to function: " << params.name; | |
| 419 SendAccessDenied(callback); | 423 SendAccessDenied(callback); |
| 420 return false; | 424 return false; |
| 421 } | 425 } |
| 422 return true; | 426 return true; |
| 423 } | 427 } |
| 424 | 428 |
| 425 namespace { | 429 namespace { |
| 426 | 430 |
| 427 // Only COMPONENT hosted apps may call extension APIs, and they are limited | 431 // Only COMPONENT hosted apps may call extension APIs, and they are limited |
| 428 // to just the permissions they explicitly request. They should not have access | 432 // to just the permissions they explicitly request. They should not have access |
| (...skipping 25 matching lines...) Expand all Loading... |
| 454 | 458 |
| 455 // static | 459 // static |
| 456 ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction( | 460 ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction( |
| 457 const ExtensionHostMsg_Request_Params& params, | 461 const ExtensionHostMsg_Request_Params& params, |
| 458 const Extension* extension, | 462 const Extension* extension, |
| 459 int requesting_process_id, | 463 int requesting_process_id, |
| 460 const ProcessMap& process_map, | 464 const ProcessMap& process_map, |
| 461 ExtensionAPI* api, | 465 ExtensionAPI* api, |
| 462 void* profile_id, | 466 void* profile_id, |
| 463 const ExtensionFunction::ResponseCallback& callback) { | 467 const ExtensionFunction::ResponseCallback& callback) { |
| 464 if (!extension) { | 468 const char* disallowed_reason = NULL; |
| 465 LOG(ERROR) << "Specified extension does not exist."; | 469 |
| 470 if (extension) { |
| 471 // Extension is calling this API. |
| 472 if (extension->is_hosted_app() && |
| 473 !AllowHostedAppAPICall(*extension, params.source_url, params.name)) { |
| 474 // Most hosted apps can't call APIs. |
| 475 disallowed_reason = "Hosted apps cannot call privileged APIs"; |
| 476 } else if (!process_map.Contains(extension->id(), requesting_process_id) && |
| 477 !api->IsAvailableInUntrustedContext(params.name, extension)) { |
| 478 // Privileged APIs can only be called from the process the extension |
| 479 // is running in. |
| 480 disallowed_reason = |
| 481 "Privileged APIs cannot be called from untrusted processes"; |
| 482 } |
| 483 } else if (content::ChildProcessSecurityPolicy::GetInstance() |
| 484 ->HasWebUIBindings(requesting_process_id)) { |
| 485 // WebUI is calling this API. |
| 486 if (!api->IsAvailableToWebUI(params.name)) { |
| 487 disallowed_reason = "WebUI can only call webui-enabled APIs"; |
| 488 } |
| 489 } else { |
| 490 // Web page is calling this API. However, the APIs that are available to |
| 491 // web pages (e.g. messaging) don't go through ExtensionFunctionDispatcher, |
| 492 // so this should be impossible. |
| 493 disallowed_reason = "Specified extension does not exist."; |
| 494 } |
| 495 |
| 496 if (disallowed_reason != NULL) { |
| 497 LOG(ERROR) << "Extension API call disallowed - name:" << params.name |
| 498 << ", pid:" << requesting_process_id |
| 499 << ", from URL: " << params.source_url.spec() |
| 500 << ", reason: " << disallowed_reason; |
| 466 SendAccessDenied(callback); | 501 SendAccessDenied(callback); |
| 467 return NULL; | 502 return NULL; |
| 468 } | 503 } |
| 469 | |
| 470 // Most hosted apps can't call APIs. | |
| 471 bool allowed = true; | |
| 472 if (extension->is_hosted_app()) | |
| 473 allowed = AllowHostedAppAPICall(*extension, params.source_url, params.name); | |
| 474 | |
| 475 // Privileged APIs can only be called from the process the extension | |
| 476 // is running in. | |
| 477 if (allowed && !api->IsAvailableInUntrustedContext(params.name, extension)) | |
| 478 allowed = process_map.Contains(extension->id(), requesting_process_id); | |
| 479 | |
| 480 if (!allowed) { | |
| 481 LOG(ERROR) << "Extension API call disallowed - name:" << params.name | |
| 482 << " pid:" << requesting_process_id | |
| 483 << " from URL " << params.source_url.spec(); | |
| 484 SendAccessDenied(callback); | |
| 485 return NULL; | |
| 486 } | |
| 487 | 504 |
| 488 ExtensionFunction* function = | 505 ExtensionFunction* function = |
| 489 ExtensionFunctionRegistry::GetInstance()->NewFunction(params.name); | 506 ExtensionFunctionRegistry::GetInstance()->NewFunction(params.name); |
| 490 if (!function) { | 507 if (!function) { |
| 491 LOG(ERROR) << "Unknown Extension API - " << params.name; | 508 LOG(ERROR) << "Unknown Extension API - " << params.name; |
| 492 SendAccessDenied(callback); | 509 SendAccessDenied(callback); |
| 493 return NULL; | 510 return NULL; |
| 494 } | 511 } |
| 495 | 512 |
| 496 function->SetArgs(¶ms.arguments); | 513 function->SetArgs(¶ms.arguments); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 508 | 525 |
| 509 // static | 526 // static |
| 510 void ExtensionFunctionDispatcher::SendAccessDenied( | 527 void ExtensionFunctionDispatcher::SendAccessDenied( |
| 511 const ExtensionFunction::ResponseCallback& callback) { | 528 const ExtensionFunction::ResponseCallback& callback) { |
| 512 base::ListValue empty_list; | 529 base::ListValue empty_list; |
| 513 callback.Run(ExtensionFunction::FAILED, empty_list, | 530 callback.Run(ExtensionFunction::FAILED, empty_list, |
| 514 "Access to extension API denied."); | 531 "Access to extension API denied."); |
| 515 } | 532 } |
| 516 | 533 |
| 517 } // namespace extensions | 534 } // namespace extensions |
| OLD | NEW |