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