Home | History | Annotate | Download | only in extensions
      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                                               &params.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                                               &params.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(&params.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