1 // Copyright 2013 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 "extensions/common/permissions/permission_set.h" 6 7 #include <algorithm> 8 #include <iterator> 9 #include <string> 10 11 #include "base/strings/stringprintf.h" 12 #include "extensions/common/permissions/permissions_info.h" 13 #include "extensions/common/url_pattern.h" 14 #include "extensions/common/url_pattern_set.h" 15 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 16 #include "url/gurl.h" 17 18 namespace extensions { 19 20 namespace { 21 22 void AddPatternsAndRemovePaths(const URLPatternSet& set, URLPatternSet* out) { 23 DCHECK(out); 24 for (URLPatternSet::const_iterator i = set.begin(); i != set.end(); ++i) { 25 URLPattern p = *i; 26 p.SetPath("/*"); 27 out->AddPattern(p); 28 } 29 } 30 31 } // namespace 32 33 // 34 // PermissionSet 35 // 36 37 PermissionSet::PermissionSet() : should_warn_all_hosts_(UNINITIALIZED) {} 38 39 PermissionSet::PermissionSet( 40 const APIPermissionSet& apis, 41 const ManifestPermissionSet& manifest_permissions, 42 const URLPatternSet& explicit_hosts, 43 const URLPatternSet& scriptable_hosts) 44 : apis_(apis), 45 manifest_permissions_(manifest_permissions), 46 scriptable_hosts_(scriptable_hosts), 47 should_warn_all_hosts_(UNINITIALIZED) { 48 AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); 49 InitImplicitPermissions(); 50 InitEffectiveHosts(); 51 } 52 53 // static 54 PermissionSet* PermissionSet::CreateDifference( 55 const PermissionSet* set1, 56 const PermissionSet* set2) { 57 scoped_refptr<PermissionSet> empty = new PermissionSet(); 58 const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1; 59 const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2; 60 61 APIPermissionSet apis; 62 APIPermissionSet::Difference(set1_safe->apis(), set2_safe->apis(), &apis); 63 64 ManifestPermissionSet manifest_permissions; 65 ManifestPermissionSet::Difference(set1_safe->manifest_permissions(), 66 set2_safe->manifest_permissions(), 67 &manifest_permissions); 68 69 URLPatternSet explicit_hosts; 70 URLPatternSet::CreateDifference(set1_safe->explicit_hosts(), 71 set2_safe->explicit_hosts(), 72 &explicit_hosts); 73 74 URLPatternSet scriptable_hosts; 75 URLPatternSet::CreateDifference(set1_safe->scriptable_hosts(), 76 set2_safe->scriptable_hosts(), 77 &scriptable_hosts); 78 79 return new PermissionSet(apis, manifest_permissions, 80 explicit_hosts, scriptable_hosts); 81 } 82 83 // static 84 PermissionSet* PermissionSet::CreateIntersection( 85 const PermissionSet* set1, 86 const PermissionSet* set2) { 87 scoped_refptr<PermissionSet> empty = new PermissionSet(); 88 const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1; 89 const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2; 90 91 APIPermissionSet apis; 92 APIPermissionSet::Intersection(set1_safe->apis(), set2_safe->apis(), &apis); 93 94 ManifestPermissionSet manifest_permissions; 95 ManifestPermissionSet::Intersection(set1_safe->manifest_permissions(), 96 set2_safe->manifest_permissions(), 97 &manifest_permissions); 98 99 URLPatternSet explicit_hosts; 100 URLPatternSet::CreateIntersection(set1_safe->explicit_hosts(), 101 set2_safe->explicit_hosts(), 102 &explicit_hosts); 103 104 URLPatternSet scriptable_hosts; 105 URLPatternSet::CreateIntersection(set1_safe->scriptable_hosts(), 106 set2_safe->scriptable_hosts(), 107 &scriptable_hosts); 108 109 return new PermissionSet(apis, manifest_permissions, 110 explicit_hosts, scriptable_hosts); 111 } 112 113 // static 114 PermissionSet* PermissionSet::CreateUnion( 115 const PermissionSet* set1, 116 const PermissionSet* set2) { 117 scoped_refptr<PermissionSet> empty = new PermissionSet(); 118 const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1; 119 const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2; 120 121 APIPermissionSet apis; 122 APIPermissionSet::Union(set1_safe->apis(), set2_safe->apis(), &apis); 123 124 ManifestPermissionSet manifest_permissions; 125 ManifestPermissionSet::Union(set1_safe->manifest_permissions(), 126 set2_safe->manifest_permissions(), 127 &manifest_permissions); 128 129 URLPatternSet explicit_hosts; 130 URLPatternSet::CreateUnion(set1_safe->explicit_hosts(), 131 set2_safe->explicit_hosts(), 132 &explicit_hosts); 133 134 URLPatternSet scriptable_hosts; 135 URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(), 136 set2_safe->scriptable_hosts(), 137 &scriptable_hosts); 138 139 return new PermissionSet(apis, manifest_permissions, 140 explicit_hosts, scriptable_hosts); 141 } 142 143 bool PermissionSet::operator==( 144 const PermissionSet& rhs) const { 145 return apis_ == rhs.apis_ && 146 manifest_permissions_ == rhs.manifest_permissions_ && 147 scriptable_hosts_ == rhs.scriptable_hosts_ && 148 explicit_hosts_ == rhs.explicit_hosts_; 149 } 150 151 bool PermissionSet::Contains(const PermissionSet& set) const { 152 return apis_.Contains(set.apis()) && 153 manifest_permissions_.Contains(set.manifest_permissions()) && 154 explicit_hosts().Contains(set.explicit_hosts()) && 155 scriptable_hosts().Contains(set.scriptable_hosts()); 156 } 157 158 std::set<std::string> PermissionSet::GetAPIsAsStrings() const { 159 std::set<std::string> apis_str; 160 for (APIPermissionSet::const_iterator i = apis_.begin(); 161 i != apis_.end(); ++i) { 162 apis_str.insert(i->name()); 163 } 164 return apis_str; 165 } 166 167 bool PermissionSet::IsEmpty() const { 168 // Not default if any host permissions are present. 169 if (!(explicit_hosts().is_empty() && scriptable_hosts().is_empty())) 170 return false; 171 172 // Or if it has no api permissions. 173 return apis().empty() && manifest_permissions().empty(); 174 } 175 176 bool PermissionSet::HasAPIPermission( 177 APIPermission::ID id) const { 178 return apis().find(id) != apis().end(); 179 } 180 181 bool PermissionSet::HasAPIPermission(const std::string& permission_name) const { 182 const APIPermissionInfo* permission = 183 PermissionsInfo::GetInstance()->GetByName(permission_name); 184 // Ensure our PermissionsProvider is aware of this permission. 185 CHECK(permission) << permission_name; 186 return (permission && apis_.count(permission->id())); 187 } 188 189 bool PermissionSet::CheckAPIPermission(APIPermission::ID permission) const { 190 return CheckAPIPermissionWithParam(permission, NULL); 191 } 192 193 bool PermissionSet::CheckAPIPermissionWithParam( 194 APIPermission::ID permission, 195 const APIPermission::CheckParam* param) const { 196 APIPermissionSet::const_iterator iter = apis().find(permission); 197 if (iter == apis().end()) 198 return false; 199 return iter->Check(param); 200 } 201 202 bool PermissionSet::HasExplicitAccessToOrigin( 203 const GURL& origin) const { 204 return explicit_hosts().MatchesURL(origin); 205 } 206 207 bool PermissionSet::HasScriptableAccessToURL( 208 const GURL& origin) const { 209 // We only need to check our host list to verify access. The host list should 210 // already reflect any special rules (such as chrome://favicon, all hosts 211 // access, etc.). 212 return scriptable_hosts().MatchesURL(origin); 213 } 214 215 bool PermissionSet::HasEffectiveAccessToAllHosts() const { 216 // There are two ways this set can have effective access to all hosts: 217 // 1) it has an <all_urls> URL pattern. 218 // 2) it has a named permission with implied full URL access. 219 for (URLPatternSet::const_iterator host = effective_hosts().begin(); 220 host != effective_hosts().end(); ++host) { 221 if (host->match_all_urls() || 222 (host->match_subdomains() && host->host().empty())) 223 return true; 224 } 225 226 for (APIPermissionSet::const_iterator i = apis().begin(); 227 i != apis().end(); ++i) { 228 if (i->info()->implies_full_url_access()) 229 return true; 230 } 231 return false; 232 } 233 234 bool PermissionSet::ShouldWarnAllHosts() const { 235 if (should_warn_all_hosts_ == UNINITIALIZED) 236 InitShouldWarnAllHosts(); 237 return should_warn_all_hosts_ == WARN_ALL_HOSTS; 238 } 239 240 bool PermissionSet::HasEffectiveAccessToURL(const GURL& url) const { 241 return effective_hosts().MatchesURL(url); 242 } 243 244 bool PermissionSet::HasEffectiveFullAccess() const { 245 for (APIPermissionSet::const_iterator i = apis().begin(); 246 i != apis().end(); ++i) { 247 if (i->info()->implies_full_access()) 248 return true; 249 } 250 return false; 251 } 252 253 PermissionSet::~PermissionSet() {} 254 255 void PermissionSet::InitImplicitPermissions() { 256 // The downloads permission implies the internal version as well. 257 if (apis_.find(APIPermission::kDownloads) != apis_.end()) 258 apis_.insert(APIPermission::kDownloadsInternal); 259 260 // The fileBrowserHandler permission implies the internal version as well. 261 if (apis_.find(APIPermission::kFileBrowserHandler) != apis_.end()) 262 apis_.insert(APIPermission::kFileBrowserHandlerInternal); 263 } 264 265 void PermissionSet::InitEffectiveHosts() { 266 effective_hosts_.ClearPatterns(); 267 268 URLPatternSet::CreateUnion( 269 explicit_hosts(), scriptable_hosts(), &effective_hosts_); 270 } 271 272 void PermissionSet::InitShouldWarnAllHosts() const { 273 if (HasEffectiveAccessToAllHosts()) { 274 should_warn_all_hosts_ = WARN_ALL_HOSTS; 275 return; 276 } 277 278 for (URLPatternSet::const_iterator iter = effective_hosts_.begin(); 279 iter != effective_hosts_.end(); 280 ++iter) { 281 // If this doesn't even match subdomains, it can't possibly imply all hosts. 282 if (!iter->match_subdomains()) 283 continue; 284 285 // If iter->host() is a recognized TLD, this will be 0. We don't include 286 // private TLDs, so that, e.g., *.appspot.com does not imply all hosts. 287 size_t registry_length = 288 net::registry_controlled_domains::GetRegistryLength( 289 iter->host(), 290 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, 291 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); 292 // If there was more than just a TLD in the host (e.g., *.foobar.com), it 293 // doesn't imply all hosts. 294 if (registry_length > 0) 295 continue; 296 297 // At this point the host could either be just a TLD ("com") or some unknown 298 // TLD-like string ("notatld"). To disambiguate between them construct a 299 // fake URL, and check the registry. This returns 0 if the TLD is 300 // unrecognized, or the length of the recognized TLD. 301 registry_length = net::registry_controlled_domains::GetRegistryLength( 302 base::StringPrintf("foo.%s", iter->host().c_str()), 303 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, 304 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); 305 // If we recognized this TLD, then this is a pattern like *.com, and it 306 // should imply all hosts. 307 if (registry_length > 0) { 308 should_warn_all_hosts_ = WARN_ALL_HOSTS; 309 return; 310 } 311 } 312 313 should_warn_all_hosts_ = DONT_WARN_ALL_HOSTS; 314 } 315 316 } // namespace extensions 317