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.
5 #include "chrome/browser/extensions/permissions_updater.h"
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/feature_switch.h"
24 #include "extensions/common/manifest_handlers/permissions_parser.h"
25 #include "extensions/common/permissions/permission_set.h"
26 #include "extensions/common/permissions/permissions_data.h"
27 #include "extensions/common/url_pattern.h"
28 #include "extensions/common/url_pattern_set.h"
30 using content::RenderProcessHost
;
31 using extensions::permissions_api_helpers::PackPermissionSet
;
33 namespace extensions
{
35 namespace permissions
= api::permissions
;
39 // Returns a set of single origin permissions from |permissions| that match
40 // |bounds|. This is necessary for two reasons:
41 // a) single origin active permissions can get filtered out in
42 // GetBoundedActivePermissions because they are not recognized as a subset
43 // of all-host permissions
44 // b) active permissions that do not match any manifest permissions can
45 // exist if a manifest permission is dropped
46 URLPatternSet
FilterSingleOriginPermissions(const URLPatternSet
& permissions
,
47 const URLPatternSet
& bounds
) {
48 URLPatternSet single_origin_permissions
;
49 for (URLPatternSet::const_iterator iter
= permissions
.begin();
50 iter
!= permissions
.end();
52 if (iter
->MatchesSingleOrigin() &&
53 bounds
.MatchesURL(GURL(iter
->GetAsString()))) {
54 single_origin_permissions
.AddPattern(*iter
);
57 return single_origin_permissions
;
60 // Returns a PermissionSet that has the active permissions of the extension,
61 // bounded to its current manifest.
62 scoped_refptr
<const PermissionSet
> GetBoundedActivePermissions(
63 const Extension
* extension
,
64 const scoped_refptr
<const PermissionSet
>& active_permissions
) {
65 // If the extension has used the optional permissions API, it will have a
66 // custom set of active permissions defined in the extension prefs. Here,
67 // we update the extension's active permissions based on the prefs.
68 if (!active_permissions
.get())
69 return extension
->permissions_data()->active_permissions();
71 scoped_refptr
<const PermissionSet
> required_permissions
=
72 PermissionsParser::GetRequiredPermissions(extension
);
74 // We restrict the active permissions to be within the bounds defined in the
75 // extension's manifest.
76 // a) active permissions must be a subset of optional + default permissions
77 // b) active permissions must contains all default permissions
78 scoped_refptr
<const PermissionSet
> total_permissions
=
79 PermissionSet::CreateUnion(
80 *required_permissions
,
81 *PermissionsParser::GetOptionalPermissions(extension
));
83 // Make sure the active permissions contain no more than optional + default.
84 scoped_refptr
<const PermissionSet
> adjusted_active
=
85 PermissionSet::CreateIntersection(*total_permissions
,
88 // Make sure the active permissions contain the default permissions.
90 PermissionSet::CreateUnion(*required_permissions
, *adjusted_active
);
92 return adjusted_active
;
95 // Divvy up the |url patterns| between those we grant and those we do not. If
96 // |withhold_permissions| is false (because the requisite feature is not
97 // enabled), no permissions are withheld.
98 void SegregateUrlPermissions(const URLPatternSet
& url_patterns
,
99 bool withhold_permissions
,
100 URLPatternSet
* granted
,
101 URLPatternSet
* withheld
) {
102 for (URLPatternSet::const_iterator iter
= url_patterns
.begin();
103 iter
!= url_patterns
.end();
105 if (withhold_permissions
&& iter
->ImpliesAllHosts())
106 withheld
->AddPattern(*iter
);
108 granted
->AddPattern(*iter
);
114 PermissionsUpdater::PermissionsUpdater(content::BrowserContext
* browser_context
)
115 : browser_context_(browser_context
), init_flag_(INIT_FLAG_NONE
) {
118 PermissionsUpdater::PermissionsUpdater(content::BrowserContext
* browser_context
,
120 : browser_context_(browser_context
), init_flag_(init_flag
) {
123 PermissionsUpdater::~PermissionsUpdater() {}
125 void PermissionsUpdater::AddPermissions(
126 const Extension
* extension
, const PermissionSet
* permissions
) {
127 scoped_refptr
<const PermissionSet
> active(
128 extension
->permissions_data()->active_permissions());
129 scoped_refptr
<const PermissionSet
> total(
130 PermissionSet::CreateUnion(*active
, *permissions
));
131 scoped_refptr
<const PermissionSet
> added(
132 PermissionSet::CreateDifference(*total
, *active
));
134 SetPermissions(extension
, total
, nullptr);
136 // Update the granted permissions so we don't auto-disable the extension.
137 GrantActivePermissions(extension
);
139 NotifyPermissionsUpdated(ADDED
, extension
, added
.get());
142 void PermissionsUpdater::RemovePermissions(const Extension
* extension
,
143 const PermissionSet
* to_remove
,
144 RemoveType remove_type
) {
145 // We should only be revoking revokable permissions.
146 CHECK(GetRevokablePermissions(extension
)->Contains(*to_remove
));
148 scoped_refptr
<const PermissionSet
> active(
149 extension
->permissions_data()->active_permissions());
150 scoped_refptr
<const PermissionSet
> remaining(
151 PermissionSet::CreateDifference(*active
, *to_remove
));
153 // Move any granted permissions that were in the withheld set back to the
154 // withheld set so they can be added back later.
155 // Any revoked permission that isn't from the optional permissions can only
156 // be a withheld permission.
157 scoped_refptr
<const PermissionSet
> removed_withheld
=
158 PermissionSet::CreateDifference(
159 *to_remove
, *PermissionsParser::GetOptionalPermissions(extension
));
160 scoped_refptr
<const PermissionSet
> withheld
= PermissionSet::CreateUnion(
162 *extension
->permissions_data()->withheld_permissions());
164 SetPermissions(extension
, remaining
, withheld
);
166 // We might not want to revoke the granted permissions because the extension,
167 // not the user, removed the permissions. This allows the extension to add
168 // them again without prompting the user.
169 if (remove_type
== REMOVE_HARD
) {
170 ExtensionPrefs::Get(browser_context_
)
171 ->RemoveGrantedPermissions(extension
->id(), to_remove
);
174 NotifyPermissionsUpdated(REMOVED
, extension
, to_remove
);
177 void PermissionsUpdater::RemovePermissionsUnsafe(
178 const Extension
* extension
,
179 const PermissionSet
* to_remove
) {
180 scoped_refptr
<const PermissionSet
> active(
181 extension
->permissions_data()->active_permissions());
182 scoped_refptr
<const PermissionSet
> total(
183 PermissionSet::CreateDifference(*active
, *to_remove
));
184 // |successfully_removed| might not equal |to_remove| if |to_remove| contains
185 // permissions the extension didn't have.
186 scoped_refptr
<const PermissionSet
> successfully_removed(
187 PermissionSet::CreateDifference(*active
, *total
));
189 SetPermissions(extension
, total
, nullptr);
190 NotifyPermissionsUpdated(REMOVED
, extension
, successfully_removed
.get());
193 scoped_refptr
<const PermissionSet
> PermissionsUpdater::GetRevokablePermissions(
194 const Extension
* extension
) const {
195 // Optional permissions are revokable.
196 scoped_refptr
<const PermissionSet
> revokable_permissions
=
197 PermissionsParser::GetOptionalPermissions(extension
);
198 scoped_refptr
<const PermissionSet
> active_permissions
=
199 extension
->permissions_data()->active_permissions();
200 // If click-to-script is enabled, then any hosts that are granted, but not
201 // listed explicitly as a required permission, are also revokable.
202 if (FeatureSwitch::scripts_require_action()->IsEnabled()) {
203 scoped_refptr
<const PermissionSet
> required_permissions
=
204 PermissionsParser::GetRequiredPermissions(extension
);
205 auto find_revokable_hosts
= [](const URLPatternSet
& active_hosts
,
206 const URLPatternSet
& required_hosts
) {
207 URLPatternSet revokable_hosts
;
208 for (const URLPattern
& pattern
: active_hosts
) {
209 if (std::find(required_hosts
.begin(), required_hosts
.end(), pattern
) ==
210 required_hosts
.end()) {
211 revokable_hosts
.AddPattern(pattern
);
214 return revokable_hosts
;
216 URLPatternSet revokable_explicit_hosts
=
217 find_revokable_hosts(active_permissions
->explicit_hosts(),
218 required_permissions
->explicit_hosts());
219 URLPatternSet revokable_scriptable_hosts
=
220 find_revokable_hosts(active_permissions
->scriptable_hosts(),
221 required_permissions
->scriptable_hosts());
222 scoped_refptr
<const PermissionSet
> revokable_host_permissions
=
223 new PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
224 revokable_explicit_hosts
, revokable_scriptable_hosts
);
225 revokable_permissions
= PermissionSet::CreateUnion(
226 *revokable_permissions
, *revokable_host_permissions
);
228 return scoped_refptr
<const PermissionSet
>(PermissionSet::CreateIntersection(
229 *active_permissions
, *revokable_permissions
));
232 void PermissionsUpdater::GrantActivePermissions(const Extension
* extension
) {
235 ExtensionPrefs::Get(browser_context_
)->AddGrantedPermissions(
237 extension
->permissions_data()->active_permissions().get());
240 void PermissionsUpdater::InitializePermissions(const Extension
* extension
) {
241 scoped_refptr
<const PermissionSet
> active_permissions(NULL
);
242 scoped_refptr
<const PermissionSet
> bounded_active(NULL
);
243 // If |extension| is a transient dummy extension, we do not want to look for
244 // it in preferences.
245 if (init_flag_
& INIT_FLAG_TRANSIENT
) {
246 bounded_active
= active_permissions
=
247 extension
->permissions_data()->active_permissions();
249 active_permissions
= ExtensionPrefs::Get(browser_context_
)
250 ->GetActivePermissions(extension
->id());
251 bounded_active
= GetBoundedActivePermissions(extension
, active_permissions
);
254 // Determine whether or not to withhold host permissions.
255 bool should_withhold_permissions
= false;
256 if (PermissionsData::ScriptsMayRequireActionForExtension(
257 extension
, bounded_active
.get())) {
258 should_withhold_permissions
=
259 init_flag_
& INIT_FLAG_TRANSIENT
?
260 !util::DefaultAllowedScriptingOnAllUrls() :
261 !util::AllowedScriptingOnAllUrls(extension
->id(), browser_context_
);
264 URLPatternSet granted_explicit_hosts
;
265 URLPatternSet withheld_explicit_hosts
;
266 SegregateUrlPermissions(bounded_active
->explicit_hosts(),
267 should_withhold_permissions
,
268 &granted_explicit_hosts
,
269 &withheld_explicit_hosts
);
271 URLPatternSet granted_scriptable_hosts
;
272 URLPatternSet withheld_scriptable_hosts
;
273 SegregateUrlPermissions(bounded_active
->scriptable_hosts(),
274 should_withhold_permissions
,
275 &granted_scriptable_hosts
,
276 &withheld_scriptable_hosts
);
278 // After withholding permissions, add back any origins to the active set that
279 // may have been lost during the set operations that would have dropped them.
280 // For example, the union of <all_urls> and "example.com" is <all_urls>, so
281 // we may lose "example.com". However, "example.com" is important once
282 // <all_urls> is stripped during withholding.
283 if (active_permissions
.get()) {
284 granted_explicit_hosts
.AddPatterns(
285 FilterSingleOriginPermissions(active_permissions
->explicit_hosts(),
286 bounded_active
->explicit_hosts()));
287 granted_scriptable_hosts
.AddPatterns(
288 FilterSingleOriginPermissions(active_permissions
->scriptable_hosts(),
289 bounded_active
->scriptable_hosts()));
292 bounded_active
= new PermissionSet(bounded_active
->apis(),
293 bounded_active
->manifest_permissions(),
294 granted_explicit_hosts
,
295 granted_scriptable_hosts
);
297 scoped_refptr
<const PermissionSet
> withheld
=
298 new PermissionSet(APIPermissionSet(),
299 ManifestPermissionSet(),
300 withheld_explicit_hosts
,
301 withheld_scriptable_hosts
);
302 SetPermissions(extension
, bounded_active
, withheld
);
305 void PermissionsUpdater::WithholdImpliedAllHosts(const Extension
* extension
) {
306 scoped_refptr
<const PermissionSet
> active
=
307 extension
->permissions_data()->active_permissions();
308 scoped_refptr
<const PermissionSet
> withheld
=
309 extension
->permissions_data()->withheld_permissions();
311 URLPatternSet withheld_scriptable
= withheld
->scriptable_hosts();
312 URLPatternSet active_scriptable
;
313 SegregateUrlPermissions(active
->scriptable_hosts(),
314 true, // withhold permissions
316 &withheld_scriptable
);
318 URLPatternSet withheld_explicit
= withheld
->explicit_hosts();
319 URLPatternSet active_explicit
;
320 SegregateUrlPermissions(active
->explicit_hosts(),
321 true, // withhold permissions
325 URLPatternSet delta_explicit
= URLPatternSet::CreateDifference(
326 active
->explicit_hosts(), active_explicit
);
327 URLPatternSet delta_scriptable
= URLPatternSet::CreateDifference(
328 active
->scriptable_hosts(), active_scriptable
);
330 SetPermissions(extension
,
331 new PermissionSet(active
->apis(),
332 active
->manifest_permissions(),
335 new PermissionSet(withheld
->apis(),
336 withheld
->manifest_permissions(),
338 withheld_scriptable
));
340 scoped_refptr
<const PermissionSet
> delta(new PermissionSet(
342 ManifestPermissionSet(),
345 NotifyPermissionsUpdated(REMOVED
, extension
, delta
.get());
348 void PermissionsUpdater::GrantWithheldImpliedAllHosts(
349 const Extension
* extension
) {
350 scoped_refptr
<const PermissionSet
> active
=
351 extension
->permissions_data()->active_permissions();
352 scoped_refptr
<const PermissionSet
> withheld
=
353 extension
->permissions_data()->withheld_permissions();
355 // Move the all-hosts permission from withheld to active.
356 // We can cheat a bit here since we know that the only host permission we
357 // withhold is allhosts (or something similar enough to it), so we can just
358 // grant all withheld host permissions.
359 URLPatternSet explicit_hosts
= URLPatternSet::CreateUnion(
360 active
->explicit_hosts(), withheld
->explicit_hosts());
361 URLPatternSet scriptable_hosts
= URLPatternSet::CreateUnion(
362 active
->scriptable_hosts(), withheld
->scriptable_hosts());
364 URLPatternSet delta_explicit
=
365 URLPatternSet::CreateDifference(explicit_hosts
, active
->explicit_hosts());
366 URLPatternSet delta_scriptable
= URLPatternSet::CreateDifference(
367 scriptable_hosts
, active
->scriptable_hosts());
369 // Since we only withhold host permissions (so far), we know that withheld
370 // permissions will be empty.
371 SetPermissions(extension
,
372 new PermissionSet(active
->apis(),
373 active
->manifest_permissions(),
376 new PermissionSet());
378 scoped_refptr
<const PermissionSet
> delta(new PermissionSet(
380 ManifestPermissionSet(),
383 NotifyPermissionsUpdated(ADDED
, extension
, delta
.get());
386 void PermissionsUpdater::SetPermissions(
387 const Extension
* extension
,
388 const scoped_refptr
<const PermissionSet
>& active
,
389 scoped_refptr
<const PermissionSet
> withheld
) {
390 withheld
= withheld
.get() ? withheld
391 : extension
->permissions_data()->withheld_permissions();
392 extension
->permissions_data()->SetPermissions(active
, withheld
);
393 if ((init_flag_
& INIT_FLAG_TRANSIENT
) == 0) {
394 ExtensionPrefs::Get(browser_context_
)
395 ->SetActivePermissions(extension
->id(), active
.get());
399 void PermissionsUpdater::DispatchEvent(
400 const std::string
& extension_id
,
401 events::HistogramValue histogram_value
,
402 const char* event_name
,
403 const PermissionSet
* changed_permissions
) {
404 EventRouter
* event_router
= EventRouter::Get(browser_context_
);
408 scoped_ptr
<base::ListValue
> value(new base::ListValue());
409 scoped_ptr
<api::permissions::Permissions
> permissions
=
410 PackPermissionSet(changed_permissions
);
411 value
->Append(permissions
->ToValue().release());
412 scoped_ptr
<Event
> event(new Event(histogram_value
, event_name
, value
.Pass()));
413 event
->restrict_to_browser_context
= browser_context_
;
414 event_router
->DispatchEventToExtension(extension_id
, event
.Pass());
417 void PermissionsUpdater::NotifyPermissionsUpdated(
418 EventType event_type
,
419 const Extension
* extension
,
420 const PermissionSet
* changed
) {
421 DCHECK((init_flag_
& INIT_FLAG_TRANSIENT
) == 0);
422 if (!changed
|| changed
->IsEmpty())
425 UpdatedExtensionPermissionsInfo::Reason reason
;
426 events::HistogramValue histogram_value
;
427 const char* event_name
= NULL
;
429 if (event_type
== REMOVED
) {
430 reason
= UpdatedExtensionPermissionsInfo::REMOVED
;
431 histogram_value
= events::PERMISSIONS_ON_REMOVED
;
432 event_name
= permissions::OnRemoved::kEventName
;
434 CHECK_EQ(ADDED
, event_type
);
435 reason
= UpdatedExtensionPermissionsInfo::ADDED
;
436 histogram_value
= events::PERMISSIONS_ON_ADDED
;
437 event_name
= permissions::OnAdded::kEventName
;
440 // Notify other APIs or interested parties.
441 UpdatedExtensionPermissionsInfo info
= UpdatedExtensionPermissionsInfo(
442 extension
, changed
, reason
);
443 Profile
* profile
= Profile::FromBrowserContext(browser_context_
);
444 content::NotificationService::current()->Notify(
445 extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED
,
446 content::Source
<Profile
>(profile
),
447 content::Details
<UpdatedExtensionPermissionsInfo
>(&info
));
449 ExtensionMsg_UpdatePermissions_Params params
;
450 params
.extension_id
= extension
->id();
451 params
.active_permissions
= ExtensionMsg_PermissionSetStruct(
452 *extension
->permissions_data()->active_permissions());
453 params
.withheld_permissions
= ExtensionMsg_PermissionSetStruct(
454 *extension
->permissions_data()->withheld_permissions());
456 // Send the new permissions to the renderers.
457 for (RenderProcessHost::iterator
i(RenderProcessHost::AllHostsIterator());
458 !i
.IsAtEnd(); i
.Advance()) {
459 RenderProcessHost
* host
= i
.GetCurrentValue();
460 if (profile
->IsSameProfile(
461 Profile::FromBrowserContext(host
->GetBrowserContext()))) {
462 host
->Send(new ExtensionMsg_UpdatePermissions(params
));
466 // Trigger the onAdded and onRemoved events in the extension.
467 DispatchEvent(extension
->id(), histogram_value
, event_name
, changed
);
470 } // namespace extensions