• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
FilterSingleOriginPermissions(const URLPatternSet & permissions,const URLPatternSet & bounds)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.
GetBoundedActivePermissions(const Extension * extension,const scoped_refptr<const PermissionSet> & active_permissions)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.
SegregateUrlPermissions(const URLPatternSet & url_patterns,bool withhold_permissions,URLPatternSet * granted,URLPatternSet * 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 
PermissionsUpdater(content::BrowserContext * browser_context)112 PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context)
113     : browser_context_(browser_context), init_flag_(INIT_FLAG_NONE) {
114 }
115 
PermissionsUpdater(content::BrowserContext * browser_context,InitFlag init_flag)116 PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context,
117                                        InitFlag init_flag)
118     : browser_context_(browser_context), init_flag_(init_flag) {
119 }
120 
~PermissionsUpdater()121 PermissionsUpdater::~PermissionsUpdater() {}
122 
AddPermissions(const Extension * extension,const PermissionSet * permissions)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 
RemovePermissions(const Extension * extension,const PermissionSet * permissions)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 
GrantActivePermissions(const Extension * extension)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 
InitializePermissions(const Extension * extension)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 
WithholdImpliedAllHosts(const Extension * extension)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 
GrantWithheldImpliedAllHosts(const Extension * extension)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 
SetPermissions(const Extension * extension,const scoped_refptr<const PermissionSet> & active,scoped_refptr<const PermissionSet> withheld)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 
DispatchEvent(const std::string & extension_id,const char * event_name,const PermissionSet * changed_permissions)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 
NotifyPermissionsUpdated(EventType event_type,const Extension * extension,const PermissionSet * changed)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