- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / permissions / permissions_data.cc
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_constants.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   URLPatternSet host_permissions;
250   URLPatternSet scriptable_hosts;
251 };
252
253 PermissionsData::PermissionsData() {
254 }
255
256 PermissionsData::~PermissionsData() {
257 }
258
259 // static
260 void PermissionsData::SetPolicyDelegate(PolicyDelegate* delegate) {
261   g_policy_delegate = delegate;
262 }
263
264 // static
265 const PermissionSet* PermissionsData::GetOptionalPermissions(
266     const Extension* extension) {
267   return extension->permissions_data()->optional_permission_set_.get();
268 }
269
270 // static
271 const PermissionSet* PermissionsData::GetRequiredPermissions(
272     const Extension* extension) {
273   return extension->permissions_data()->required_permission_set_.get();
274 }
275
276 // static
277 const APIPermissionSet* PermissionsData::GetInitialAPIPermissions(
278     const Extension* extension) {
279   return &extension->permissions_data()->
280       initial_required_permissions_->api_permissions;
281 }
282
283 // static
284 APIPermissionSet* PermissionsData::GetInitialAPIPermissions(
285     Extension* extension) {
286   return &extension->permissions_data()->
287       initial_required_permissions_->api_permissions;
288 }
289
290 // static
291 void PermissionsData::SetInitialScriptableHosts(
292     Extension* extension, const URLPatternSet& scriptable_hosts) {
293   extension->permissions_data()->
294       initial_required_permissions_->scriptable_hosts = scriptable_hosts;
295 }
296
297 // static
298 void PermissionsData::SetActivePermissions(const Extension* extension,
299                                            const PermissionSet* permissions) {
300   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
301   extension->permissions_data()->active_permissions_ = permissions;
302 }
303
304 // static
305 scoped_refptr<const PermissionSet> PermissionsData::GetActivePermissions(
306     const Extension* extension) {
307   return extension->permissions_data()->active_permissions_;
308 }
309
310 // static
311 scoped_refptr<const PermissionSet> PermissionsData::GetTabSpecificPermissions(
312     const Extension* extension,
313     int tab_id) {
314   CHECK_GE(tab_id, 0);
315   TabPermissionsMap::const_iterator iter =
316       extension->permissions_data()->tab_specific_permissions_.find(tab_id);
317   return
318       (iter != extension->permissions_data()->tab_specific_permissions_.end())
319           ? iter->second
320           : NULL;
321 }
322
323 // static
324 void PermissionsData::UpdateTabSpecificPermissions(
325     const Extension* extension,
326     int tab_id,
327     scoped_refptr<const PermissionSet> permissions) {
328   CHECK_GE(tab_id, 0);
329   TabPermissionsMap* tab_permissions =
330       &extension->permissions_data()->tab_specific_permissions_;
331   if (tab_permissions->count(tab_id)) {
332     (*tab_permissions)[tab_id] = PermissionSet::CreateUnion(
333         (*tab_permissions)[tab_id].get(), permissions.get());
334   } else {
335     (*tab_permissions)[tab_id] = permissions;
336   }
337 }
338
339 // static
340 void PermissionsData::ClearTabSpecificPermissions(
341     const Extension* extension,
342     int tab_id) {
343   CHECK_GE(tab_id, 0);
344   extension->permissions_data()->tab_specific_permissions_.erase(tab_id);
345 }
346
347 // static
348 bool PermissionsData::HasAPIPermission(const Extension* extension,
349                                        APIPermission::ID permission) {
350   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
351   return GetActivePermissions(extension)->HasAPIPermission(permission);
352 }
353
354 // static
355 bool PermissionsData::HasAPIPermission(
356     const Extension* extension,
357     const std::string& permission_name) {
358   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
359   return GetActivePermissions(extension)->HasAPIPermission(permission_name);
360 }
361
362 // static
363 bool PermissionsData::HasAPIPermissionForTab(
364     const Extension* extension,
365     int tab_id,
366     APIPermission::ID permission) {
367   if (HasAPIPermission(extension, permission))
368     return true;
369
370   // Place autolock below the HasAPIPermission() check, since HasAPIPermission
371   // also acquires the lock.
372   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
373   scoped_refptr<const PermissionSet> tab_permissions =
374       GetTabSpecificPermissions(extension, tab_id);
375   return tab_permissions.get() && tab_permissions->HasAPIPermission(permission);
376 }
377
378 // static
379 bool PermissionsData::CheckAPIPermissionWithParam(
380     const Extension* extension,
381     APIPermission::ID permission,
382     const APIPermission::CheckParam* param) {
383   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
384   return GetActivePermissions(extension)->CheckAPIPermissionWithParam(
385       permission, param);
386 }
387
388 // static
389 const URLPatternSet& PermissionsData::GetEffectiveHostPermissions(
390     const Extension* extension) {
391   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
392   return GetActivePermissions(extension)->effective_hosts();
393 }
394
395 // static
396 bool PermissionsData::CanSilentlyIncreasePermissions(
397     const Extension* extension) {
398   return extension->location() != Manifest::INTERNAL;
399 }
400
401 // static
402 bool PermissionsData::ShouldSkipPermissionWarnings(const Extension* extension) {
403   return IsTrustedId(extension->id());
404 }
405
406 // static
407 bool PermissionsData::HasHostPermission(const Extension* extension,
408                                         const GURL& url) {
409   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
410   return GetActivePermissions(extension)->HasExplicitAccessToOrigin(url);
411 }
412
413 // static
414 bool PermissionsData::HasEffectiveAccessToAllHosts(const Extension* extension) {
415   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
416   return GetActivePermissions(extension)->HasEffectiveAccessToAllHosts();
417 }
418
419 // static
420 PermissionMessages PermissionsData::GetPermissionMessages(
421     const Extension* extension) {
422   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
423   if (ShouldSkipPermissionWarnings(extension)) {
424     return PermissionMessages();
425   } else {
426     return PermissionMessageProvider::Get()->GetPermissionMessages(
427         GetActivePermissions(extension), extension->GetType());
428   }
429 }
430
431 // static
432 std::vector<string16> PermissionsData::GetPermissionMessageStrings(
433     const Extension* extension) {
434   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
435   if (ShouldSkipPermissionWarnings(extension)) {
436     return std::vector<string16>();
437   } else {
438     return PermissionMessageProvider::Get()->GetWarningMessages(
439         GetActivePermissions(extension), extension->GetType());
440   }
441 }
442
443 // static
444 std::vector<string16> PermissionsData::GetPermissionMessageDetailsStrings(
445     const Extension* extension) {
446   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
447   if (ShouldSkipPermissionWarnings(extension)) {
448     return std::vector<string16>();
449   } else {
450     return PermissionMessageProvider::Get()->GetWarningMessagesDetails(
451         GetActivePermissions(extension), extension->GetType());
452   }
453 }
454
455 // static
456 bool PermissionsData::CanExecuteScriptOnPage(const Extension* extension,
457                                              const GURL& document_url,
458                                              const GURL& top_frame_url,
459                                              int tab_id,
460                                              const UserScript* script,
461                                              int process_id,
462                                              std::string* error) {
463   base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_);
464   // The gallery is special-cased as a restricted URL for scripting to prevent
465   // access to special JS bindings we expose to the gallery (and avoid things
466   // like extensions removing the "report abuse" link).
467   // TODO(erikkay): This seems like the wrong test.  Shouldn't we we testing
468   // against the store app extent?
469   GURL store_url(extension_urls::GetWebstoreLaunchURL());
470   const CommandLine* command_line = CommandLine::ForCurrentProcess();
471   bool can_execute_everywhere = CanExecuteScriptEverywhere(extension);
472
473   if (g_policy_delegate &&
474       !g_policy_delegate->CanExecuteScriptOnPage(
475            extension, document_url, top_frame_url, tab_id,
476            script, process_id, error))
477     return false;
478
479   if ((document_url.host() == store_url.host()) &&
480       !can_execute_everywhere &&
481       !command_line->HasSwitch(switches::kAllowScriptingGallery)) {
482     if (error)
483       *error = errors::kCannotScriptGallery;
484     return false;
485   }
486
487   if (!command_line->HasSwitch(switches::kExtensionsOnChromeURLs)) {
488     if (document_url.SchemeIs(chrome::kChromeUIScheme) &&
489         !can_execute_everywhere) {
490       if (error)
491         *error = errors::kCannotAccessChromeUrl;
492       return false;
493     }
494   }
495
496   if (top_frame_url.SchemeIs(extensions::kExtensionScheme) &&
497       top_frame_url.GetOrigin() !=
498           Extension::GetBaseURLFromExtensionId(extension->id()).GetOrigin() &&
499       !can_execute_everywhere) {
500     if (error)
501       *error = errors::kCannotAccessExtensionUrl;
502     return false;
503   }
504
505   // If a tab ID is specified, try the tab-specific permissions.
506   if (tab_id >= 0) {
507     scoped_refptr<const PermissionSet> tab_permissions =
508         GetTabSpecificPermissions(extension, tab_id);
509     if (tab_permissions.get() &&
510         tab_permissions->explicit_hosts().MatchesSecurityOrigin(document_url)) {
511       return true;
512     }
513   }
514
515   bool can_access = false;
516
517   if (script) {
518     // If a script is specified, use its matches.
519     can_access = script->MatchesURL(document_url);
520   } else {
521     // Otherwise, see if this extension has permission to execute script
522     // programmatically on pages.
523     can_access = GetActivePermissions(extension)->
524         HasExplicitAccessToOrigin(document_url);
525   }
526
527   if (!can_access && error) {
528     *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
529                                             document_url.spec());
530   }
531
532   return can_access;
533 }
534
535 // static
536 bool PermissionsData::CanExecuteScriptEverywhere(const Extension* extension) {
537   if (extension->location() == Manifest::COMPONENT)
538     return true;
539
540   const ExtensionsClient::ScriptingWhitelist& whitelist =
541       ExtensionsClient::Get()->GetScriptingWhitelist();
542
543   return std::find(whitelist.begin(), whitelist.end(), extension->id()) !=
544       whitelist.end();
545 }
546
547 // static
548 bool PermissionsData::CanCaptureVisiblePage(const Extension* extension,
549                                             const GURL& page_url,
550                                             int tab_id,
551                                             std::string* error) {
552   if (tab_id >= 0) {
553     scoped_refptr<const PermissionSet> tab_permissions =
554         GetTabSpecificPermissions(extension, tab_id);
555     if (tab_permissions.get() &&
556         tab_permissions->explicit_hosts().MatchesSecurityOrigin(page_url)) {
557       return true;
558     }
559   }
560
561   if (HasHostPermission(extension, page_url) ||
562       page_url.GetOrigin() == extension->url()) {
563     return true;
564   }
565
566   if (error) {
567     *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
568                                             page_url.spec());
569   }
570   return false;
571 }
572
573 bool PermissionsData::ParsePermissions(Extension* extension, string16* error) {
574   initial_required_permissions_.reset(new InitialPermissions);
575   if (!ParseHelper(extension,
576                    keys::kPermissions,
577                    &initial_required_permissions_->api_permissions,
578                    &initial_required_permissions_->host_permissions,
579                    error)) {
580     return false;
581   }
582
583   // TODO(jeremya/kalman) do this via the features system by exposing the
584   // app.window API to platform apps, with no dependency on any permissions.
585   // See http://crbug.com/120069.
586   if (extension->is_platform_app()) {
587     initial_required_permissions_->api_permissions.insert(
588         APIPermission::kAppCurrentWindowInternal);
589     initial_required_permissions_->api_permissions.insert(
590         APIPermission::kAppRuntime);
591     initial_required_permissions_->api_permissions.insert(
592         APIPermission::kAppWindow);
593   }
594
595   initial_optional_permissions_.reset(new InitialPermissions);
596   if (!ParseHelper(extension,
597                    keys::kOptionalPermissions,
598                    &initial_optional_permissions_->api_permissions,
599                    &initial_optional_permissions_->host_permissions,
600                    error)) {
601     return false;
602   }
603
604   return true;
605 }
606
607 void PermissionsData::FinalizePermissions(Extension* extension) {
608   active_permissions_ = new PermissionSet(
609       initial_required_permissions_->api_permissions,
610       initial_required_permissions_->host_permissions,
611       initial_required_permissions_->scriptable_hosts);
612
613   required_permission_set_ = new PermissionSet(
614       initial_required_permissions_->api_permissions,
615       initial_required_permissions_->host_permissions,
616       initial_required_permissions_->scriptable_hosts);
617
618   optional_permission_set_ = new PermissionSet(
619       initial_optional_permissions_->api_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