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
27namespace extensions {
28
29namespace {
30
31namespace keys = manifest_keys;
32namespace errors = manifest_errors;
33
34struct ManifestPermissions : public Extension::ManifestData {
35  ManifestPermissions(scoped_refptr<const PermissionSet> permissions);
36  virtual ~ManifestPermissions();
37
38  scoped_refptr<const PermissionSet> permissions;
39};
40
41ManifestPermissions::ManifestPermissions(
42    scoped_refptr<const PermissionSet> permissions)
43    : permissions(permissions) {
44}
45
46ManifestPermissions::~ManifestPermissions() {
47}
48
49// Custom checks for the experimental permission that can't be expressed in
50// _permission_features.json.
51bool 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|.
71bool 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.
102bool 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
250struct PermissionsParser::InitialPermissions {
251  APIPermissionSet api_permissions;
252  ManifestPermissionSet manifest_permissions;
253  URLPatternSet host_permissions;
254  URLPatternSet scriptable_hosts;
255};
256
257PermissionsParser::PermissionsParser() {
258}
259
260PermissionsParser::~PermissionsParser() {
261}
262
263bool 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
285void 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
307void 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
315void 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
323bool 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
332void 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
341scoped_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
349scoped_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