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/permissions_updater.h"
      6 
      7 #include "base/json/json_writer.h"
      8 #include "base/memory/ref_counted.h"
      9 #include "base/values.h"
     10 #include "chrome/browser/chrome_notification_types.h"
     11 #include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
     12 #include "chrome/browser/extensions/extension_util.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/common/extensions/api/permissions.h"
     15 #include "content/public/browser/notification_observer.h"
     16 #include "content/public/browser/notification_registrar.h"
     17 #include "content/public/browser/notification_service.h"
     18 #include "content/public/browser/render_process_host.h"
     19 #include "extensions/browser/event_router.h"
     20 #include "extensions/browser/extension_prefs.h"
     21 #include "extensions/common/extension.h"
     22 #include "extensions/common/extension_messages.h"
     23 #include "extensions/common/manifest_handlers/permissions_parser.h"
     24 #include "extensions/common/permissions/permission_set.h"
     25 #include "extensions/common/permissions/permissions_data.h"
     26 #include "extensions/common/url_pattern.h"
     27 #include "extensions/common/url_pattern_set.h"
     28 
     29 using content::RenderProcessHost;
     30 using extensions::permissions_api_helpers::PackPermissionSet;
     31 
     32 namespace extensions {
     33 
     34 namespace permissions = api::permissions;
     35 
     36 namespace {
     37 
     38 // Returns a set of single origin permissions from |permissions| that match
     39 // |bounds|. This is necessary for two reasons:
     40 //   a) single origin active permissions can get filtered out in
     41 //      GetBoundedActivePermissions because they are not recognized as a subset
     42 //      of all-host permissions
     43 //   b) active permissions that do not match any manifest permissions can
     44 //      exist if a manifest permission is dropped
     45 URLPatternSet FilterSingleOriginPermissions(const URLPatternSet& permissions,
     46                                             const URLPatternSet& bounds) {
     47   URLPatternSet single_origin_permissions;
     48   for (URLPatternSet::const_iterator iter = permissions.begin();
     49        iter != permissions.end();
     50        ++iter) {
     51     if (iter->MatchesSingleOrigin() &&
     52         bounds.MatchesURL(GURL(iter->GetAsString()))) {
     53       single_origin_permissions.AddPattern(*iter);
     54     }
     55   }
     56   return single_origin_permissions;
     57 }
     58 
     59 // Returns a PermissionSet that has the active permissions of the extension,
     60 // bounded to its current manifest.
     61 scoped_refptr<const PermissionSet> GetBoundedActivePermissions(
     62     const Extension* extension,
     63     const scoped_refptr<const PermissionSet>& active_permissions) {
     64   // If the extension has used the optional permissions API, it will have a
     65   // custom set of active permissions defined in the extension prefs. Here,
     66   // we update the extension's active permissions based on the prefs.
     67   if (!active_permissions.get())
     68     return extension->permissions_data()->active_permissions();
     69 
     70   scoped_refptr<const PermissionSet> required_permissions =
     71       PermissionsParser::GetRequiredPermissions(extension);
     72 
     73   // We restrict the active permissions to be within the bounds defined in the
     74   // extension's manifest.
     75   //  a) active permissions must be a subset of optional + default permissions
     76   //  b) active permissions must contains all default permissions
     77   scoped_refptr<PermissionSet> total_permissions = PermissionSet::CreateUnion(
     78       required_permissions.get(),
     79       PermissionsParser::GetOptionalPermissions(extension).get());
     80 
     81   // Make sure the active permissions contain no more than optional + default.
     82   scoped_refptr<PermissionSet> adjusted_active =
     83       PermissionSet::CreateIntersection(total_permissions.get(),
     84                                         active_permissions.get());
     85 
     86   // Make sure the active permissions contain the default permissions.
     87   adjusted_active = PermissionSet::CreateUnion(required_permissions.get(),
     88                                                adjusted_active.get());
     89 
     90   return adjusted_active;
     91 }
     92 
     93 // Divvy up the |url patterns| between those we grant and those we do not. If
     94 // |withhold_permissions| is false (because the requisite feature is not
     95 // enabled), no permissions are withheld.
     96 void SegregateUrlPermissions(const URLPatternSet& url_patterns,
     97                              bool withhold_permissions,
     98                              URLPatternSet* granted,
     99                              URLPatternSet* withheld) {
    100   for (URLPatternSet::const_iterator iter = url_patterns.begin();
    101        iter != url_patterns.end();
    102        ++iter) {
    103     if (withhold_permissions && iter->ImpliesAllHosts())
    104       withheld->AddPattern(*iter);
    105     else
    106       granted->AddPattern(*iter);
    107   }
    108 }
    109 
    110 }  // namespace
    111 
    112 PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context)
    113     : browser_context_(browser_context), init_flag_(INIT_FLAG_NONE) {
    114 }
    115 
    116 PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context,
    117                                        InitFlag init_flag)
    118     : browser_context_(browser_context), init_flag_(init_flag) {
    119 }
    120 
    121 PermissionsUpdater::~PermissionsUpdater() {}
    122 
    123 void PermissionsUpdater::AddPermissions(
    124     const Extension* extension, const PermissionSet* permissions) {
    125   scoped_refptr<const PermissionSet> existing(
    126       extension->permissions_data()->active_permissions());
    127   scoped_refptr<PermissionSet> total(
    128       PermissionSet::CreateUnion(existing.get(), permissions));
    129   scoped_refptr<PermissionSet> added(
    130       PermissionSet::CreateDifference(total.get(), existing.get()));
    131 
    132   SetPermissions(extension, total, NULL);
    133 
    134   // Update the granted permissions so we don't auto-disable the extension.
    135   GrantActivePermissions(extension);
    136 
    137   NotifyPermissionsUpdated(ADDED, extension, added.get());
    138 }
    139 
    140 void PermissionsUpdater::RemovePermissions(
    141     const Extension* extension, const PermissionSet* permissions) {
    142   scoped_refptr<const PermissionSet> existing(
    143       extension->permissions_data()->active_permissions());
    144   scoped_refptr<PermissionSet> total(
    145       PermissionSet::CreateDifference(existing.get(), permissions));
    146   scoped_refptr<PermissionSet> removed(
    147       PermissionSet::CreateDifference(existing.get(), total.get()));
    148 
    149   // We update the active permissions, and not the granted permissions, because
    150   // the extension, not the user, removed the permissions. This allows the
    151   // extension to add them again without prompting the user.
    152   SetPermissions(extension, total, NULL);
    153 
    154   NotifyPermissionsUpdated(REMOVED, extension, removed.get());
    155 }
    156 
    157 void PermissionsUpdater::GrantActivePermissions(const Extension* extension) {
    158   CHECK(extension);
    159 
    160   // We only maintain the granted permissions prefs for INTERNAL and LOAD
    161   // extensions.
    162   if (!Manifest::IsUnpackedLocation(extension->location()) &&
    163       extension->location() != Manifest::INTERNAL)
    164     return;
    165 
    166   ExtensionPrefs::Get(browser_context_)->AddGrantedPermissions(
    167       extension->id(),
    168       extension->permissions_data()->active_permissions().get());
    169 }
    170 
    171 void PermissionsUpdater::InitializePermissions(const Extension* extension) {
    172   scoped_refptr<const PermissionSet> active_permissions(NULL);
    173   scoped_refptr<const PermissionSet> bounded_active(NULL);
    174   // If |extension| is a transient dummy extension, we do not want to look for
    175   // it in preferences.
    176   if (init_flag_ & INIT_FLAG_TRANSIENT) {
    177     bounded_active = active_permissions =
    178         extension->permissions_data()->active_permissions();
    179   } else {
    180     active_permissions = ExtensionPrefs::Get(browser_context_)
    181                              ->GetActivePermissions(extension->id());
    182     bounded_active = GetBoundedActivePermissions(extension, active_permissions);
    183   }
    184 
    185   // Withhold permissions if the switch applies to this extension.
    186   // Non-transient extensions also must not have the preference to allow
    187   // scripting on all urls.
    188   bool should_withhold_permissions =
    189       util::ScriptsMayRequireActionForExtension(extension);
    190   if ((init_flag_ & INIT_FLAG_TRANSIENT) == 0) {
    191     should_withhold_permissions &=
    192         !util::AllowedScriptingOnAllUrls(extension->id(), browser_context_);
    193   }
    194 
    195   URLPatternSet granted_explicit_hosts;
    196   URLPatternSet withheld_explicit_hosts;
    197   SegregateUrlPermissions(bounded_active->explicit_hosts(),
    198                           should_withhold_permissions,
    199                           &granted_explicit_hosts,
    200                           &withheld_explicit_hosts);
    201 
    202   URLPatternSet granted_scriptable_hosts;
    203   URLPatternSet withheld_scriptable_hosts;
    204   SegregateUrlPermissions(bounded_active->scriptable_hosts(),
    205                           should_withhold_permissions,
    206                           &granted_scriptable_hosts,
    207                           &withheld_scriptable_hosts);
    208 
    209   // After withholding permissions, add back any origins to the active set that
    210   // may have been lost during the set operations that would have dropped them.
    211   // For example, the union of <all_urls> and "example.com" is <all_urls>, so
    212   // we may lose "example.com". However, "example.com" is important once
    213   // <all_urls> is stripped during withholding.
    214   if (active_permissions.get()) {
    215     granted_explicit_hosts.AddPatterns(
    216         FilterSingleOriginPermissions(active_permissions->explicit_hosts(),
    217                                       bounded_active->explicit_hosts()));
    218     granted_scriptable_hosts.AddPatterns(
    219         FilterSingleOriginPermissions(active_permissions->scriptable_hosts(),
    220                                       bounded_active->scriptable_hosts()));
    221   }
    222 
    223   bounded_active = new PermissionSet(bounded_active->apis(),
    224                                      bounded_active->manifest_permissions(),
    225                                      granted_explicit_hosts,
    226                                      granted_scriptable_hosts);
    227 
    228   scoped_refptr<const PermissionSet> withheld =
    229       new PermissionSet(APIPermissionSet(),
    230                         ManifestPermissionSet(),
    231                         withheld_explicit_hosts,
    232                         withheld_scriptable_hosts);
    233   SetPermissions(extension, bounded_active, withheld);
    234 }
    235 
    236 void PermissionsUpdater::WithholdImpliedAllHosts(const Extension* extension) {
    237   scoped_refptr<const PermissionSet> active =
    238       extension->permissions_data()->active_permissions();
    239   scoped_refptr<const PermissionSet> withheld =
    240       extension->permissions_data()->withheld_permissions();
    241 
    242   URLPatternSet withheld_scriptable = withheld->scriptable_hosts();
    243   URLPatternSet active_scriptable;
    244   SegregateUrlPermissions(active->scriptable_hosts(),
    245                           true,  // withhold permissions
    246                           &active_scriptable,
    247                           &withheld_scriptable);
    248 
    249   URLPatternSet withheld_explicit = withheld->explicit_hosts();
    250   URLPatternSet active_explicit;
    251   SegregateUrlPermissions(active->explicit_hosts(),
    252                           true,  // withhold permissions
    253                           &active_explicit,
    254                           &withheld_explicit);
    255 
    256   SetPermissions(extension,
    257                  new PermissionSet(active->apis(),
    258                                    active->manifest_permissions(),
    259                                    active_explicit,
    260                                    active_scriptable),
    261                   new PermissionSet(withheld->apis(),
    262                                     withheld->manifest_permissions(),
    263                                     withheld_explicit,
    264                                     withheld_scriptable));
    265   // TODO(rdevlin.cronin) We should notify the observers/renderer.
    266 }
    267 
    268 void PermissionsUpdater::GrantWithheldImpliedAllHosts(
    269     const Extension* extension) {
    270   scoped_refptr<const PermissionSet> active =
    271       extension->permissions_data()->active_permissions();
    272   scoped_refptr<const PermissionSet> withheld =
    273       extension->permissions_data()->withheld_permissions();
    274 
    275   // Move the all-hosts permission from withheld to active.
    276   // We can cheat a bit here since we know that the only host permission we
    277   // withhold is allhosts (or something similar enough to it), so we can just
    278   // grant all withheld host permissions.
    279   URLPatternSet explicit_hosts;
    280   URLPatternSet::CreateUnion(
    281       active->explicit_hosts(), withheld->explicit_hosts(), &explicit_hosts);
    282   URLPatternSet scriptable_hosts;
    283   URLPatternSet::CreateUnion(active->scriptable_hosts(),
    284                              withheld->scriptable_hosts(),
    285                              &scriptable_hosts);
    286 
    287   // Since we only withhold host permissions (so far), we know that withheld
    288   // permissions will be empty.
    289   SetPermissions(extension,
    290                  new PermissionSet(active->apis(),
    291                                    active->manifest_permissions(),
    292                                    explicit_hosts,
    293                                    scriptable_hosts),
    294                  new PermissionSet());
    295   // TODO(rdevlin.cronin) We should notify the observers/renderer.
    296 }
    297 
    298 void PermissionsUpdater::SetPermissions(
    299     const Extension* extension,
    300     const scoped_refptr<const PermissionSet>& active,
    301     scoped_refptr<const PermissionSet> withheld) {
    302   withheld = withheld.get() ? withheld
    303                  : extension->permissions_data()->withheld_permissions();
    304   extension->permissions_data()->SetPermissions(active, withheld);
    305   if ((init_flag_ & INIT_FLAG_TRANSIENT) == 0) {
    306     ExtensionPrefs::Get(browser_context_)
    307         ->SetActivePermissions(extension->id(), active.get());
    308   }
    309 }
    310 
    311 void PermissionsUpdater::DispatchEvent(
    312     const std::string& extension_id,
    313     const char* event_name,
    314     const PermissionSet* changed_permissions) {
    315   EventRouter* event_router = EventRouter::Get(browser_context_);
    316   if (!event_router)
    317     return;
    318 
    319   scoped_ptr<base::ListValue> value(new base::ListValue());
    320   scoped_ptr<api::permissions::Permissions> permissions =
    321       PackPermissionSet(changed_permissions);
    322   value->Append(permissions->ToValue().release());
    323   scoped_ptr<Event> event(new Event(event_name, value.Pass()));
    324   event->restrict_to_browser_context = browser_context_;
    325   event_router->DispatchEventToExtension(extension_id, event.Pass());
    326 }
    327 
    328 void PermissionsUpdater::NotifyPermissionsUpdated(
    329     EventType event_type,
    330     const Extension* extension,
    331     const PermissionSet* changed) {
    332   DCHECK((init_flag_ & INIT_FLAG_TRANSIENT) == 0);
    333   if (!changed || changed->IsEmpty())
    334     return;
    335 
    336   UpdatedExtensionPermissionsInfo::Reason reason;
    337   const char* event_name = NULL;
    338 
    339   if (event_type == REMOVED) {
    340     reason = UpdatedExtensionPermissionsInfo::REMOVED;
    341     event_name = permissions::OnRemoved::kEventName;
    342   } else {
    343     CHECK_EQ(ADDED, event_type);
    344     reason = UpdatedExtensionPermissionsInfo::ADDED;
    345     event_name = permissions::OnAdded::kEventName;
    346   }
    347 
    348   // Notify other APIs or interested parties.
    349   UpdatedExtensionPermissionsInfo info = UpdatedExtensionPermissionsInfo(
    350       extension, changed, reason);
    351   Profile* profile = Profile::FromBrowserContext(browser_context_);
    352   content::NotificationService::current()->Notify(
    353       extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
    354       content::Source<Profile>(profile),
    355       content::Details<UpdatedExtensionPermissionsInfo>(&info));
    356 
    357   ExtensionMsg_UpdatePermissions_Params params;
    358   params.extension_id = extension->id();
    359   params.active_permissions = ExtensionMsg_PermissionSetStruct(
    360       *extension->permissions_data()->active_permissions());
    361   params.withheld_permissions = ExtensionMsg_PermissionSetStruct(
    362       *extension->permissions_data()->withheld_permissions());
    363 
    364   // Send the new permissions to the renderers.
    365   for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
    366        !i.IsAtEnd(); i.Advance()) {
    367     RenderProcessHost* host = i.GetCurrentValue();
    368     if (profile->IsSameProfile(
    369             Profile::FromBrowserContext(host->GetBrowserContext()))) {
    370       host->Send(new ExtensionMsg_UpdatePermissions(params));
    371     }
    372   }
    373 
    374   // Trigger the onAdded and onRemoved events in the extension.
    375   DispatchEvent(extension->id(), event_name, changed);
    376 }
    377 
    378 }  // namespace extensions
    379