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
AddPatternsAndRemovePaths(const URLPatternSet & set,URLPatternSet * out)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
PermissionSet()37 PermissionSet::PermissionSet() : should_warn_all_hosts_(UNINITIALIZED) {}
38
PermissionSet(const APIPermissionSet & apis,const ManifestPermissionSet & manifest_permissions,const URLPatternSet & explicit_hosts,const URLPatternSet & scriptable_hosts)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
CreateDifference(const PermissionSet * set1,const PermissionSet * set2)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
CreateIntersection(const PermissionSet * set1,const PermissionSet * set2)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
CreateUnion(const PermissionSet * set1,const PermissionSet * set2)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
operator ==(const PermissionSet & rhs) const143 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
Contains(const PermissionSet & set) const151 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
GetAPIsAsStrings() const158 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
IsEmpty() const167 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
HasAPIPermission(APIPermission::ID id) const176 bool PermissionSet::HasAPIPermission(
177 APIPermission::ID id) const {
178 return apis().find(id) != apis().end();
179 }
180
HasAPIPermission(const std::string & permission_name) const181 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
CheckAPIPermission(APIPermission::ID permission) const189 bool PermissionSet::CheckAPIPermission(APIPermission::ID permission) const {
190 return CheckAPIPermissionWithParam(permission, NULL);
191 }
192
CheckAPIPermissionWithParam(APIPermission::ID permission,const APIPermission::CheckParam * param) const193 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
HasExplicitAccessToOrigin(const GURL & origin) const202 bool PermissionSet::HasExplicitAccessToOrigin(
203 const GURL& origin) const {
204 return explicit_hosts().MatchesURL(origin);
205 }
206
HasScriptableAccessToURL(const GURL & origin) const207 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
HasEffectiveAccessToAllHosts() const215 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
ShouldWarnAllHosts() const234 bool PermissionSet::ShouldWarnAllHosts() const {
235 if (should_warn_all_hosts_ == UNINITIALIZED)
236 InitShouldWarnAllHosts();
237 return should_warn_all_hosts_ == WARN_ALL_HOSTS;
238 }
239
HasEffectiveAccessToURL(const GURL & url) const240 bool PermissionSet::HasEffectiveAccessToURL(const GURL& url) const {
241 return effective_hosts().MatchesURL(url);
242 }
243
HasEffectiveFullAccess() const244 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
~PermissionSet()253 PermissionSet::~PermissionSet() {}
254
InitImplicitPermissions()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
InitEffectiveHosts()265 void PermissionSet::InitEffectiveHosts() {
266 effective_hosts_.ClearPatterns();
267
268 URLPatternSet::CreateUnion(
269 explicit_hosts(), scriptable_hosts(), &effective_hosts_);
270 }
271
InitShouldWarnAllHosts() const272 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