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