Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(728)

Side by Side Diff: chrome/common/extensions/permissions/permissions_data.cc

Issue 15239002: Move Extension and PermissionsData to extensions/common. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 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 "chrome/common/extensions/permissions/permissions_data.h"
6
7 #include "base/command_line.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/strings/string16.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/common/extensions/extension.h"
15 #include "chrome/common/extensions/extension_constants.h"
16 #include "content/public/common/url_constants.h"
17 #include "extensions/common/constants.h"
18 #include "extensions/common/error_utils.h"
19 #include "extensions/common/extensions_client.h"
20 #include "extensions/common/features/feature.h"
21 #include "extensions/common/features/feature_provider.h"
22 #include "extensions/common/manifest.h"
23 #include "extensions/common/manifest_constants.h"
24 #include "extensions/common/manifest_handler.h"
25 #include "extensions/common/permissions/api_permission_set.h"
26 #include "extensions/common/permissions/permission_message_provider.h"
27 #include "extensions/common/permissions/permission_set.h"
28 #include "extensions/common/permissions/permissions_info.h"
29 #include "extensions/common/switches.h"
30 #include "extensions/common/url_pattern_set.h"
31 #include "extensions/common/user_script.h"
32 #include "url/gurl.h"
33
34 namespace extensions {
35
36 namespace keys = manifest_keys;
37 namespace errors = manifest_errors;
38
39 namespace {
40
41 PermissionsData::PolicyDelegate* g_policy_delegate = NULL;
42
43 // Custom checks for the experimental permission that can't be expressed in
44 // _permission_features.json.
45 bool CanSpecifyExperimentalPermission(const Extension* extension) {
46 if (extension->location() == Manifest::COMPONENT)
47 return true;
48
49 if (CommandLine::ForCurrentProcess()->HasSwitch(
50 switches::kEnableExperimentalExtensionApis)) {
51 return true;
52 }
53
54 // We rely on the webstore to check access to experimental. This way we can
55 // whitelist extensions to have access to experimental in just the store, and
56 // not have to push a new version of the client.
57 if (extension->from_webstore())
58 return true;
59
60 return false;
61 }
62
63 // Checks whether the host |pattern| is allowed for the given |extension|,
64 // given API permissions |permissions|.
65 bool CanSpecifyHostPermission(const Extension* extension,
66 const URLPattern& pattern,
67 const APIPermissionSet& permissions) {
68 if (!pattern.match_all_urls() &&
69 pattern.MatchesScheme(chrome::kChromeUIScheme)) {
70 URLPatternSet chrome_scheme_hosts = ExtensionsClient::Get()->
71 GetPermittedChromeSchemeHosts(extension, permissions);
72 if (chrome_scheme_hosts.ContainsPattern(pattern))
73 return true;
74
75 // Component extensions can have access to all of chrome://*.
76 if (PermissionsData::CanExecuteScriptEverywhere(extension))
77 return true;
78
79 if (CommandLine::ForCurrentProcess()->HasSwitch(
80 switches::kExtensionsOnChromeURLs)) {
81 return true;
82 }
83
84 // TODO(aboxhall): return from_webstore() when webstore handles blocking
85 // extensions which request chrome:// urls
86 return false;
87 }
88
89 // Otherwise, the valid schemes were handled by URLPattern.
90 return true;
91 }
92
93 // Parses the host and api permissions from the specified permission |key|
94 // from |extension|'s manifest.
95 bool ParseHelper(Extension* extension,
96 const char* key,
97 APIPermissionSet* api_permissions,
98 URLPatternSet* host_permissions,
99 string16* error) {
100 if (!extension->manifest()->HasKey(key))
101 return true;
102
103 const base::ListValue* permissions = NULL;
104 if (!extension->manifest()->GetList(key, &permissions)) {
105 *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions,
106 std::string());
107 return false;
108 }
109
110 // NOTE: We need to get the APIPermission before we check if features
111 // associated with them are available because the feature system does not
112 // know about aliases.
113
114 std::vector<std::string> host_data;
115 if (!APIPermissionSet::ParseFromJSON(
116 permissions, APIPermissionSet::kDisallowInternalPermissions,
117 api_permissions, error, &host_data)) {
118 return false;
119 }
120
121 // Verify feature availability of permissions.
122 std::vector<APIPermission::ID> to_remove;
123 FeatureProvider* permission_features =
124 FeatureProvider::GetPermissionFeatures();
125 for (APIPermissionSet::const_iterator iter = api_permissions->begin();
126 iter != api_permissions->end(); ++iter) {
127 Feature* feature = permission_features->GetFeature(iter->name());
128
129 // The feature should exist since we just got an APIPermission for it. The
130 // two systems should be updated together whenever a permission is added.
131 DCHECK(feature);
132 // http://crbug.com/176381
133 if (!feature) {
134 to_remove.push_back(iter->id());
135 continue;
136 }
137
138 Feature::Availability availability = feature->IsAvailableToManifest(
139 extension->id(),
140 extension->GetType(),
141 Feature::ConvertLocation(extension->location()),
142 extension->manifest_version());
143
144 if (!availability.is_available()) {
145 // Don't fail, but warn the developer that the manifest contains
146 // unrecognized permissions. This may happen legitimately if the
147 // extensions requests platform- or channel-specific permissions.
148 extension->AddInstallWarning(InstallWarning(availability.message(),
149 feature->name()));
150 to_remove.push_back(iter->id());
151 continue;
152 }
153
154 if (iter->id() == APIPermission::kExperimental) {
155 if (!CanSpecifyExperimentalPermission(extension)) {
156 *error = ASCIIToUTF16(errors::kExperimentalFlagRequired);
157 return false;
158 }
159 }
160 }
161
162 api_permissions->AddImpliedPermissions();
163
164 // Remove permissions that are not available to this extension.
165 for (std::vector<APIPermission::ID>::const_iterator iter = to_remove.begin();
166 iter != to_remove.end(); ++iter) {
167 api_permissions->erase(*iter);
168 }
169
170 // Parse host pattern permissions.
171 const int kAllowedSchemes =
172 PermissionsData::CanExecuteScriptEverywhere(extension) ?
173 URLPattern::SCHEME_ALL : Extension::kValidHostPermissionSchemes;
174
175 for (std::vector<std::string>::const_iterator iter = host_data.begin();
176 iter != host_data.end(); ++iter) {
177 const std::string& permission_str = *iter;
178
179 // Check if it's a host pattern permission.
180 URLPattern pattern = URLPattern(kAllowedSchemes);
181 URLPattern::ParseResult parse_result = pattern.Parse(permission_str);
182 if (parse_result == URLPattern::PARSE_SUCCESS) {
183 // The path component is not used for host permissions, so we force it
184 // to match all paths.
185 pattern.SetPath("/*");
186 int valid_schemes = pattern.valid_schemes();
187 if (pattern.MatchesScheme(chrome::kFileScheme) &&
188 !PermissionsData::CanExecuteScriptEverywhere(extension)) {
189 extension->set_wants_file_access(true);
190 if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS))
191 valid_schemes &= ~URLPattern::SCHEME_FILE;
192 }
193
194 if (pattern.scheme() != chrome::kChromeUIScheme &&
195 !PermissionsData::CanExecuteScriptEverywhere(extension)) {
196 // Keep chrome:// in allowed schemes only if it's explicitly requested
197 // or CanExecuteScriptEverywhere is true. If the
198 // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission
199 // will fail, so don't check the flag here.
200 valid_schemes &= ~URLPattern::SCHEME_CHROMEUI;
201 }
202 pattern.SetValidSchemes(valid_schemes);
203
204 if (!CanSpecifyHostPermission(extension, pattern, *api_permissions)) {
205 // TODO(aboxhall): make a warning (see pattern.match_all_urls() block
206 // below).
207 extension->AddInstallWarning(InstallWarning(
208 ErrorUtils::FormatErrorMessage(
209 errors::kInvalidPermissionScheme, permission_str),
210 key,
211 permission_str));
212 continue;
213 }
214
215 host_permissions->AddPattern(pattern);
216 // We need to make sure all_urls matches chrome://favicon and (maybe)
217 // chrome://thumbnail, so add them back in to host_permissions separately.
218 if (pattern.match_all_urls()) {
219 host_permissions->AddPatterns(
220 ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(
221 extension, *api_permissions));
222 }
223 continue;
224 }
225
226 // It's probably an unknown API permission. Do not throw an error so
227 // extensions can retain backwards compatability (http://crbug.com/42742).
228 extension->AddInstallWarning(InstallWarning(
229 ErrorUtils::FormatErrorMessage(
230 manifest_errors::kPermissionUnknownOrMalformed,
231 permission_str),
232 key,
233 permission_str));
234 }
235
236 return true;
237 }
238
239 // Returns true if this extension id is from a trusted provider.
240 bool IsTrustedId(const std::string& extension_id) {
241 // See http://b/4946060 for more details.
242 return extension_id == std::string("nckgahadagoaajjgafhacjanaoiihapd");
243 }
244
245 } // namespace
246
247 struct PermissionsData::InitialPermissions {
248 APIPermissionSet api_permissions;
249 ManifestPermissionSet manifest_permissions;
250 URLPatternSet host_permissions;
251 URLPatternSet scriptable_hosts;
252 };
253
254 PermissionsData::PermissionsData() {
255 }
256
257 PermissionsData::~PermissionsData() {
258 }
259
260 // static
261 void PermissionsData::SetPolicyDelegate(PolicyDelegate* delegate) {
262 g_policy_delegate = delegate;
263 }
264
265 // static
266 const PermissionSet* PermissionsData::GetOptionalPermissions(
267 const Extension* extension) {
268 return extension->permissions_data()->optional_permission_set_.get();
269 }
270
271 // static
272 const PermissionSet* PermissionsData::GetRequiredPermissions(
273 const Extension* extension) {
274 return extension->permissions_data()->required_permission_set_.get();
275 }
276
277 // static
278 const APIPermissionSet* PermissionsData::GetInitialAPIPermissions(
279 const Extension* extension) {
280 return &extension->permissions_data()->
281 initial_required_permissions_->api_permissions;
282 }
283
284 // static
285 APIPermissionSet* PermissionsData::GetInitialAPIPermissions(
286 Extension* extension) {
287 return &extension->permissions_data()->
288 initial_required_permissions_->api_permissions;
289 }
290
291 // static
292 void PermissionsData::SetInitialScriptableHosts(
293 Extension* extension, const URLPatternSet& scriptable_hosts) {
294 extension->permissions_data()->
295 initial_required_permissions_->scriptable_hosts = scriptable_hosts;
296 }
297
298 // static
299 void PermissionsData::SetActivePermissions(const Extension* extension,
300 const PermissionSet* permissions) {
301 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
302 extension->permissions_data()->active_permissions_ = permissions;
303 }
304
305 // static
306 scoped_refptr<const PermissionSet> PermissionsData::GetActivePermissions(
307 const Extension* extension) {
308 return extension->permissions_data()->active_permissions_;
309 }
310
311 // static
312 scoped_refptr<const PermissionSet> PermissionsData::GetTabSpecificPermissions(
313 const Extension* extension,
314 int tab_id) {
315 CHECK_GE(tab_id, 0);
316 TabPermissionsMap::const_iterator iter =
317 extension->permissions_data()->tab_specific_permissions_.find(tab_id);
318 return
319 (iter != extension->permissions_data()->tab_specific_permissions_.end())
320 ? iter->second
321 : NULL;
322 }
323
324 // static
325 void PermissionsData::UpdateTabSpecificPermissions(
326 const Extension* extension,
327 int tab_id,
328 scoped_refptr<const PermissionSet> permissions) {
329 CHECK_GE(tab_id, 0);
330 TabPermissionsMap* tab_permissions =
331 &extension->permissions_data()->tab_specific_permissions_;
332 if (tab_permissions->count(tab_id)) {
333 (*tab_permissions)[tab_id] = PermissionSet::CreateUnion(
334 (*tab_permissions)[tab_id].get(), permissions.get());
335 } else {
336 (*tab_permissions)[tab_id] = permissions;
337 }
338 }
339
340 // static
341 void PermissionsData::ClearTabSpecificPermissions(
342 const Extension* extension,
343 int tab_id) {
344 CHECK_GE(tab_id, 0);
345 extension->permissions_data()->tab_specific_permissions_.erase(tab_id);
346 }
347
348 // static
349 bool PermissionsData::HasAPIPermission(const Extension* extension,
350 APIPermission::ID permission) {
351 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
352 return GetActivePermissions(extension)->HasAPIPermission(permission);
353 }
354
355 // static
356 bool PermissionsData::HasAPIPermission(
357 const Extension* extension,
358 const std::string& permission_name) {
359 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
360 return GetActivePermissions(extension)->HasAPIPermission(permission_name);
361 }
362
363 // static
364 bool PermissionsData::HasAPIPermissionForTab(
365 const Extension* extension,
366 int tab_id,
367 APIPermission::ID permission) {
368 if (HasAPIPermission(extension, permission))
369 return true;
370
371 // Place autolock below the HasAPIPermission() check, since HasAPIPermission
372 // also acquires the lock.
373 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
374 scoped_refptr<const PermissionSet> tab_permissions =
375 GetTabSpecificPermissions(extension, tab_id);
376 return tab_permissions.get() && tab_permissions->HasAPIPermission(permission);
377 }
378
379 // static
380 bool PermissionsData::CheckAPIPermissionWithParam(
381 const Extension* extension,
382 APIPermission::ID permission,
383 const APIPermission::CheckParam* param) {
384 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
385 return GetActivePermissions(extension)->CheckAPIPermissionWithParam(
386 permission, param);
387 }
388
389 // static
390 const URLPatternSet& PermissionsData::GetEffectiveHostPermissions(
391 const Extension* extension) {
392 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
393 return GetActivePermissions(extension)->effective_hosts();
394 }
395
396 // static
397 bool PermissionsData::CanSilentlyIncreasePermissions(
398 const Extension* extension) {
399 return extension->location() != Manifest::INTERNAL;
400 }
401
402 // static
403 bool PermissionsData::ShouldSkipPermissionWarnings(const Extension* extension) {
404 return IsTrustedId(extension->id());
405 }
406
407 // static
408 bool PermissionsData::HasHostPermission(const Extension* extension,
409 const GURL& url) {
410 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
411 return GetActivePermissions(extension)->HasExplicitAccessToOrigin(url);
412 }
413
414 // static
415 bool PermissionsData::HasEffectiveAccessToAllHosts(const Extension* extension) {
416 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
417 return GetActivePermissions(extension)->HasEffectiveAccessToAllHosts();
418 }
419
420 // static
421 PermissionMessages PermissionsData::GetPermissionMessages(
422 const Extension* extension) {
423 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
424 if (ShouldSkipPermissionWarnings(extension)) {
425 return PermissionMessages();
426 } else {
427 return PermissionMessageProvider::Get()->GetPermissionMessages(
428 GetActivePermissions(extension), extension->GetType());
429 }
430 }
431
432 // static
433 std::vector<string16> PermissionsData::GetPermissionMessageStrings(
434 const Extension* extension) {
435 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
436 if (ShouldSkipPermissionWarnings(extension)) {
437 return std::vector<string16>();
438 } else {
439 return PermissionMessageProvider::Get()->GetWarningMessages(
440 GetActivePermissions(extension), extension->GetType());
441 }
442 }
443
444 // static
445 std::vector<string16> PermissionsData::GetPermissionMessageDetailsStrings(
446 const Extension* extension) {
447 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
448 if (ShouldSkipPermissionWarnings(extension)) {
449 return std::vector<string16>();
450 } else {
451 return PermissionMessageProvider::Get()->GetWarningMessagesDetails(
452 GetActivePermissions(extension), extension->GetType());
453 }
454 }
455
456 // static
457 bool PermissionsData::CanExecuteScriptOnPage(const Extension* extension,
458 const GURL& document_url,
459 const GURL& top_frame_url,
460 int tab_id,
461 const UserScript* script,
462 int process_id,
463 std::string* error) {
464 base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
465 const CommandLine* command_line = CommandLine::ForCurrentProcess();
466 bool can_execute_everywhere = CanExecuteScriptEverywhere(extension);
467
468 if (g_policy_delegate &&
469 !g_policy_delegate->CanExecuteScriptOnPage(
470 extension, document_url, top_frame_url, tab_id,
471 script, process_id, error))
472 return false;
473
474 if (!can_execute_everywhere &&
475 !ExtensionsClient::Get()->IsScriptableURL(document_url, error)) {
476 return false;
477 }
478
479 if (!command_line->HasSwitch(switches::kExtensionsOnChromeURLs)) {
480 if (document_url.SchemeIs(chrome::kChromeUIScheme) &&
481 !can_execute_everywhere) {
482 if (error)
483 *error = errors::kCannotAccessChromeUrl;
484 return false;
485 }
486 }
487
488 if (top_frame_url.SchemeIs(extensions::kExtensionScheme) &&
489 top_frame_url.GetOrigin() !=
490 Extension::GetBaseURLFromExtensionId(extension->id()).GetOrigin() &&
491 !can_execute_everywhere) {
492 if (error)
493 *error = errors::kCannotAccessExtensionUrl;
494 return false;
495 }
496
497 // If a tab ID is specified, try the tab-specific permissions.
498 if (tab_id >= 0) {
499 scoped_refptr<const PermissionSet> tab_permissions =
500 GetTabSpecificPermissions(extension, tab_id);
501 if (tab_permissions.get() &&
502 tab_permissions->explicit_hosts().MatchesSecurityOrigin(document_url)) {
503 return true;
504 }
505 }
506
507 bool can_access = false;
508
509 if (script) {
510 // If a script is specified, use its matches.
511 can_access = script->MatchesURL(document_url);
512 } else {
513 // Otherwise, see if this extension has permission to execute script
514 // programmatically on pages.
515 can_access = GetActivePermissions(extension)->
516 HasExplicitAccessToOrigin(document_url);
517 }
518
519 if (!can_access && error) {
520 *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
521 document_url.spec());
522 }
523
524 return can_access;
525 }
526
527 // static
528 bool PermissionsData::CanExecuteScriptEverywhere(const Extension* extension) {
529 if (extension->location() == Manifest::COMPONENT)
530 return true;
531
532 const ExtensionsClient::ScriptingWhitelist& whitelist =
533 ExtensionsClient::Get()->GetScriptingWhitelist();
534
535 return std::find(whitelist.begin(), whitelist.end(), extension->id()) !=
536 whitelist.end();
537 }
538
539 // static
540 bool PermissionsData::CanCaptureVisiblePage(const Extension* extension,
541 const GURL& page_url,
542 int tab_id,
543 std::string* error) {
544 if (tab_id >= 0) {
545 scoped_refptr<const PermissionSet> tab_permissions =
546 GetTabSpecificPermissions(extension, tab_id);
547 if (tab_permissions.get() &&
548 tab_permissions->explicit_hosts().MatchesSecurityOrigin(page_url)) {
549 return true;
550 }
551 }
552
553 if (HasHostPermission(extension, page_url) ||
554 page_url.GetOrigin() == extension->url()) {
555 return true;
556 }
557
558 if (error) {
559 *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
560 page_url.spec());
561 }
562 return false;
563 }
564
565 bool PermissionsData::ParsePermissions(Extension* extension, string16* error) {
566 initial_required_permissions_.reset(new InitialPermissions);
567 if (!ParseHelper(extension,
568 keys::kPermissions,
569 &initial_required_permissions_->api_permissions,
570 &initial_required_permissions_->host_permissions,
571 error)) {
572 return false;
573 }
574
575 // TODO(jeremya/kalman) do this via the features system by exposing the
576 // app.window API to platform apps, with no dependency on any permissions.
577 // See http://crbug.com/120069.
578 if (extension->is_platform_app()) {
579 initial_required_permissions_->api_permissions.insert(
580 APIPermission::kAppCurrentWindowInternal);
581 initial_required_permissions_->api_permissions.insert(
582 APIPermission::kAppRuntime);
583 initial_required_permissions_->api_permissions.insert(
584 APIPermission::kAppWindow);
585 }
586
587 initial_optional_permissions_.reset(new InitialPermissions);
588 if (!ParseHelper(extension,
589 keys::kOptionalPermissions,
590 &initial_optional_permissions_->api_permissions,
591 &initial_optional_permissions_->host_permissions,
592 error)) {
593 return false;
594 }
595
596 return true;
597 }
598
599 void PermissionsData::InitializeManifestPermissions(Extension* extension) {
600 ManifestHandler::AddExtensionInitialRequiredPermissions(
601 extension, &initial_required_permissions_->manifest_permissions);
602 }
603
604 void PermissionsData::FinalizePermissions(Extension* extension) {
605 active_permissions_ = new PermissionSet(
606 initial_required_permissions_->api_permissions,
607 initial_required_permissions_->manifest_permissions,
608 initial_required_permissions_->host_permissions,
609 initial_required_permissions_->scriptable_hosts);
610
611 required_permission_set_ = new PermissionSet(
612 initial_required_permissions_->api_permissions,
613 initial_required_permissions_->manifest_permissions,
614 initial_required_permissions_->host_permissions,
615 initial_required_permissions_->scriptable_hosts);
616
617 optional_permission_set_ = new PermissionSet(
618 initial_optional_permissions_->api_permissions,
619 initial_optional_permissions_->manifest_permissions,
620 initial_optional_permissions_->host_permissions,
621 URLPatternSet());
622
623 initial_required_permissions_.reset();
624 initial_optional_permissions_.reset();
625 }
626
627 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698