• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/manifest_handlers/permissions_parser.h"
6 
7 #include "base/command_line.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "content/public/common/url_constants.h"
12 #include "extensions/common/error_utils.h"
13 #include "extensions/common/extension.h"
14 #include "extensions/common/extensions_client.h"
15 #include "extensions/common/features/feature.h"
16 #include "extensions/common/features/feature_provider.h"
17 #include "extensions/common/manifest.h"
18 #include "extensions/common/manifest_constants.h"
19 #include "extensions/common/manifest_handler.h"
20 #include "extensions/common/permissions/api_permission_set.h"
21 #include "extensions/common/permissions/permission_set.h"
22 #include "extensions/common/permissions/permissions_data.h"
23 #include "extensions/common/switches.h"
24 #include "extensions/common/url_pattern_set.h"
25 #include "url/url_constants.h"
26 
27 namespace extensions {
28 
29 namespace {
30 
31 namespace keys = manifest_keys;
32 namespace errors = manifest_errors;
33 
34 struct ManifestPermissions : public Extension::ManifestData {
35   ManifestPermissions(scoped_refptr<const PermissionSet> permissions);
36   virtual ~ManifestPermissions();
37 
38   scoped_refptr<const PermissionSet> permissions;
39 };
40 
ManifestPermissions(scoped_refptr<const PermissionSet> permissions)41 ManifestPermissions::ManifestPermissions(
42     scoped_refptr<const PermissionSet> permissions)
43     : permissions(permissions) {
44 }
45 
~ManifestPermissions()46 ManifestPermissions::~ManifestPermissions() {
47 }
48 
49 // Custom checks for the experimental permission that can't be expressed in
50 // _permission_features.json.
CanSpecifyExperimentalPermission(const Extension * extension)51 bool CanSpecifyExperimentalPermission(const Extension* extension) {
52   if (extension->location() == Manifest::COMPONENT)
53     return true;
54 
55   if (CommandLine::ForCurrentProcess()->HasSwitch(
56           switches::kEnableExperimentalExtensionApis)) {
57     return true;
58   }
59 
60   // We rely on the webstore to check access to experimental. This way we can
61   // whitelist extensions to have access to experimental in just the store, and
62   // not have to push a new version of the client.
63   if (extension->from_webstore())
64     return true;
65 
66   return false;
67 }
68 
69 // Checks whether the host |pattern| is allowed for the given |extension|,
70 // given API permissions |permissions|.
CanSpecifyHostPermission(const Extension * extension,const URLPattern & pattern,const APIPermissionSet & permissions)71 bool CanSpecifyHostPermission(const Extension* extension,
72                               const URLPattern& pattern,
73                               const APIPermissionSet& permissions) {
74   if (!pattern.match_all_urls() &&
75       pattern.MatchesScheme(content::kChromeUIScheme)) {
76     URLPatternSet chrome_scheme_hosts =
77         ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(extension,
78                                                                permissions);
79     if (chrome_scheme_hosts.ContainsPattern(pattern))
80       return true;
81 
82     // Component extensions can have access to all of chrome://*.
83     if (PermissionsData::CanExecuteScriptEverywhere(extension))
84       return true;
85 
86     if (CommandLine::ForCurrentProcess()->HasSwitch(
87             switches::kExtensionsOnChromeURLs)) {
88       return true;
89     }
90 
91     // TODO(aboxhall): return from_webstore() when webstore handles blocking
92     // extensions which request chrome:// urls
93     return false;
94   }
95 
96   // Otherwise, the valid schemes were handled by URLPattern.
97   return true;
98 }
99 
100 // Parses the host and api permissions from the specified permission |key|
101 // from |extension|'s manifest.
ParseHelper(Extension * extension,const char * key,APIPermissionSet * api_permissions,URLPatternSet * host_permissions,base::string16 * error)102 bool ParseHelper(Extension* extension,
103                  const char* key,
104                  APIPermissionSet* api_permissions,
105                  URLPatternSet* host_permissions,
106                  base::string16* error) {
107   if (!extension->manifest()->HasKey(key))
108     return true;
109 
110   const base::ListValue* permissions = NULL;
111   if (!extension->manifest()->GetList(key, &permissions)) {
112     *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions,
113                                                  std::string());
114     return false;
115   }
116 
117   // NOTE: We need to get the APIPermission before we check if features
118   // associated with them are available because the feature system does not
119   // know about aliases.
120 
121   std::vector<std::string> host_data;
122   if (!APIPermissionSet::ParseFromJSON(
123           permissions,
124           APIPermissionSet::kDisallowInternalPermissions,
125           api_permissions,
126           error,
127           &host_data)) {
128     return false;
129   }
130 
131   // Verify feature availability of permissions.
132   std::vector<APIPermission::ID> to_remove;
133   const FeatureProvider* permission_features =
134       FeatureProvider::GetPermissionFeatures();
135   for (APIPermissionSet::const_iterator iter = api_permissions->begin();
136        iter != api_permissions->end();
137        ++iter) {
138     Feature* feature = permission_features->GetFeature(iter->name());
139 
140     // The feature should exist since we just got an APIPermission for it. The
141     // two systems should be updated together whenever a permission is added.
142     DCHECK(feature) << "Could not find feature for " << iter->name();
143     // http://crbug.com/176381
144     if (!feature) {
145       to_remove.push_back(iter->id());
146       continue;
147     }
148 
149     Feature::Availability availability =
150         feature->IsAvailableToExtension(extension);
151     if (!availability.is_available()) {
152       // Don't fail, but warn the developer that the manifest contains
153       // unrecognized permissions. This may happen legitimately if the
154       // extensions requests platform- or channel-specific permissions.
155       extension->AddInstallWarning(
156           InstallWarning(availability.message(), feature->name()));
157       to_remove.push_back(iter->id());
158       continue;
159     }
160 
161     if (iter->id() == APIPermission::kExperimental) {
162       if (!CanSpecifyExperimentalPermission(extension)) {
163         *error = base::ASCIIToUTF16(errors::kExperimentalFlagRequired);
164         return false;
165       }
166     }
167   }
168 
169   api_permissions->AddImpliedPermissions();
170 
171   // Remove permissions that are not available to this extension.
172   for (std::vector<APIPermission::ID>::const_iterator iter = to_remove.begin();
173        iter != to_remove.end();
174        ++iter) {
175     api_permissions->erase(*iter);
176   }
177 
178   // Parse host pattern permissions.
179   const int kAllowedSchemes =
180       PermissionsData::CanExecuteScriptEverywhere(extension)
181           ? URLPattern::SCHEME_ALL
182           : Extension::kValidHostPermissionSchemes;
183 
184   for (std::vector<std::string>::const_iterator iter = host_data.begin();
185        iter != host_data.end();
186        ++iter) {
187     const std::string& permission_str = *iter;
188 
189     // Check if it's a host pattern permission.
190     URLPattern pattern = URLPattern(kAllowedSchemes);
191     URLPattern::ParseResult parse_result = pattern.Parse(permission_str);
192     if (parse_result == URLPattern::PARSE_SUCCESS) {
193       // The path component is not used for host permissions, so we force it
194       // to match all paths.
195       pattern.SetPath("/*");
196       int valid_schemes = pattern.valid_schemes();
197       if (pattern.MatchesScheme(url::kFileScheme) &&
198           !PermissionsData::CanExecuteScriptEverywhere(extension)) {
199         extension->set_wants_file_access(true);
200         if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS))
201           valid_schemes &= ~URLPattern::SCHEME_FILE;
202       }
203 
204       if (pattern.scheme() != content::kChromeUIScheme &&
205           !PermissionsData::CanExecuteScriptEverywhere(extension)) {
206         // Keep chrome:// in allowed schemes only if it's explicitly requested
207         // or CanExecuteScriptEverywhere is true. If the
208         // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission
209         // will fail, so don't check the flag here.
210         valid_schemes &= ~URLPattern::SCHEME_CHROMEUI;
211       }
212       pattern.SetValidSchemes(valid_schemes);
213 
214       if (!CanSpecifyHostPermission(extension, pattern, *api_permissions)) {
215         // TODO(aboxhall): make a warning (see pattern.match_all_urls() block
216         // below).
217         extension->AddInstallWarning(InstallWarning(
218             ErrorUtils::FormatErrorMessage(errors::kInvalidPermissionScheme,
219                                            permission_str),
220             key,
221             permission_str));
222         continue;
223       }
224 
225       host_permissions->AddPattern(pattern);
226       // We need to make sure all_urls matches chrome://favicon and (maybe)
227       // chrome://thumbnail, so add them back in to host_permissions separately.
228       if (pattern.match_all_urls()) {
229         host_permissions->AddPatterns(
230             ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(
231                 extension, *api_permissions));
232       }
233       continue;
234     }
235 
236     // It's probably an unknown API permission. Do not throw an error so
237     // extensions can retain backwards compatability (http://crbug.com/42742).
238     extension->AddInstallWarning(InstallWarning(
239         ErrorUtils::FormatErrorMessage(
240             manifest_errors::kPermissionUnknownOrMalformed, permission_str),
241         key,
242         permission_str));
243   }
244 
245   return true;
246 }
247 
248 }  // namespace
249 
250 struct PermissionsParser::InitialPermissions {
251   APIPermissionSet api_permissions;
252   ManifestPermissionSet manifest_permissions;
253   URLPatternSet host_permissions;
254   URLPatternSet scriptable_hosts;
255 };
256 
PermissionsParser()257 PermissionsParser::PermissionsParser() {
258 }
259 
~PermissionsParser()260 PermissionsParser::~PermissionsParser() {
261 }
262 
Parse(Extension * extension,base::string16 * error)263 bool PermissionsParser::Parse(Extension* extension, base::string16* error) {
264   initial_required_permissions_.reset(new InitialPermissions);
265   if (!ParseHelper(extension,
266                    keys::kPermissions,
267                    &initial_required_permissions_->api_permissions,
268                    &initial_required_permissions_->host_permissions,
269                    error)) {
270     return false;
271   }
272 
273   initial_optional_permissions_.reset(new InitialPermissions);
274   if (!ParseHelper(extension,
275                    keys::kOptionalPermissions,
276                    &initial_optional_permissions_->api_permissions,
277                    &initial_optional_permissions_->host_permissions,
278                    error)) {
279     return false;
280   }
281 
282   return true;
283 }
284 
Finalize(Extension * extension)285 void PermissionsParser::Finalize(Extension* extension) {
286   ManifestHandler::AddExtensionInitialRequiredPermissions(
287       extension, &initial_required_permissions_->manifest_permissions);
288 
289   scoped_refptr<const PermissionSet> required_permissions(
290       new PermissionSet(initial_required_permissions_->api_permissions,
291                         initial_required_permissions_->manifest_permissions,
292                         initial_required_permissions_->host_permissions,
293                         initial_required_permissions_->scriptable_hosts));
294   extension->SetManifestData(keys::kPermissions,
295                              new ManifestPermissions(required_permissions));
296 
297   scoped_refptr<const PermissionSet> optional_permissions(
298       new PermissionSet(initial_optional_permissions_->api_permissions,
299                         initial_optional_permissions_->manifest_permissions,
300                         initial_optional_permissions_->host_permissions,
301                         URLPatternSet()));
302   extension->SetManifestData(keys::kOptionalPermissions,
303                              new ManifestPermissions(optional_permissions));
304 }
305 
306 // static
AddAPIPermission(Extension * extension,APIPermission::ID permission)307 void PermissionsParser::AddAPIPermission(Extension* extension,
308                                          APIPermission::ID permission) {
309   DCHECK(extension->permissions_parser());
310   extension->permissions_parser()
311       ->initial_required_permissions_->api_permissions.insert(permission);
312 }
313 
314 // static
AddAPIPermission(Extension * extension,APIPermission * permission)315 void PermissionsParser::AddAPIPermission(Extension* extension,
316                                          APIPermission* permission) {
317   DCHECK(extension->permissions_parser());
318   extension->permissions_parser()
319       ->initial_required_permissions_->api_permissions.insert(permission);
320 }
321 
322 // static
HasAPIPermission(const Extension * extension,APIPermission::ID permission)323 bool PermissionsParser::HasAPIPermission(const Extension* extension,
324                                          APIPermission::ID permission) {
325   DCHECK(extension->permissions_parser());
326   return extension->permissions_parser()
327              ->initial_required_permissions_->api_permissions.count(
328                  permission) > 0;
329 }
330 
331 // static
SetScriptableHosts(Extension * extension,const URLPatternSet & scriptable_hosts)332 void PermissionsParser::SetScriptableHosts(
333     Extension* extension,
334     const URLPatternSet& scriptable_hosts) {
335   DCHECK(extension->permissions_parser());
336   extension->permissions_parser()
337       ->initial_required_permissions_->scriptable_hosts = scriptable_hosts;
338 }
339 
340 // static
GetRequiredPermissions(const Extension * extension)341 scoped_refptr<const PermissionSet> PermissionsParser::GetRequiredPermissions(
342     const Extension* extension) {
343   DCHECK(extension->GetManifestData(keys::kPermissions));
344   return static_cast<const ManifestPermissions*>(
345              extension->GetManifestData(keys::kPermissions))->permissions;
346 }
347 
348 // static
GetOptionalPermissions(const Extension * extension)349 scoped_refptr<const PermissionSet> PermissionsParser::GetOptionalPermissions(
350     const Extension* extension) {
351   DCHECK(extension->GetManifestData(keys::kOptionalPermissions));
352   return static_cast<const ManifestPermissions*>(
353              extension->GetManifestData(keys::kOptionalPermissions))
354       ->permissions;
355 }
356 
357 }  // namespace extensions
358