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(); |
Charlie Reis
2014/07/22 21:10:24
Minor nit: Maybe this would be easier to read if w
not at google - send to devlin
2014/07/22 23:42:33
Done.
| |
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 |