普通文本  |  358行  |  12.87 KB

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/common/manifest_handlers/permissions_parser.h"

#include "base/command_line.h"
#include "base/memory/ref_counted.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "content/public/common/url_constants.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension.h"
#include "extensions/common/extensions_client.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/features/feature_provider.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handler.h"
#include "extensions/common/permissions/api_permission_set.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/switches.h"
#include "extensions/common/url_pattern_set.h"
#include "url/url_constants.h"

namespace extensions {

namespace {

namespace keys = manifest_keys;
namespace errors = manifest_errors;

struct ManifestPermissions : public Extension::ManifestData {
  ManifestPermissions(scoped_refptr<const PermissionSet> permissions);
  virtual ~ManifestPermissions();

  scoped_refptr<const PermissionSet> permissions;
};

ManifestPermissions::ManifestPermissions(
    scoped_refptr<const PermissionSet> permissions)
    : permissions(permissions) {
}

ManifestPermissions::~ManifestPermissions() {
}

// Custom checks for the experimental permission that can't be expressed in
// _permission_features.json.
bool CanSpecifyExperimentalPermission(const Extension* extension) {
  if (extension->location() == Manifest::COMPONENT)
    return true;

  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableExperimentalExtensionApis)) {
    return true;
  }

  // We rely on the webstore to check access to experimental. This way we can
  // whitelist extensions to have access to experimental in just the store, and
  // not have to push a new version of the client.
  if (extension->from_webstore())
    return true;

  return false;
}

// Checks whether the host |pattern| is allowed for the given |extension|,
// given API permissions |permissions|.
bool CanSpecifyHostPermission(const Extension* extension,
                              const URLPattern& pattern,
                              const APIPermissionSet& permissions) {
  if (!pattern.match_all_urls() &&
      pattern.MatchesScheme(content::kChromeUIScheme)) {
    URLPatternSet chrome_scheme_hosts =
        ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(extension,
                                                               permissions);
    if (chrome_scheme_hosts.ContainsPattern(pattern))
      return true;

    // Component extensions can have access to all of chrome://*.
    if (PermissionsData::CanExecuteScriptEverywhere(extension))
      return true;

    if (CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kExtensionsOnChromeURLs)) {
      return true;
    }

    // TODO(aboxhall): return from_webstore() when webstore handles blocking
    // extensions which request chrome:// urls
    return false;
  }

  // Otherwise, the valid schemes were handled by URLPattern.
  return true;
}

// Parses the host and api permissions from the specified permission |key|
// from |extension|'s manifest.
bool ParseHelper(Extension* extension,
                 const char* key,
                 APIPermissionSet* api_permissions,
                 URLPatternSet* host_permissions,
                 base::string16* error) {
  if (!extension->manifest()->HasKey(key))
    return true;

  const base::ListValue* permissions = NULL;
  if (!extension->manifest()->GetList(key, &permissions)) {
    *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions,
                                                 std::string());
    return false;
  }

  // NOTE: We need to get the APIPermission before we check if features
  // associated with them are available because the feature system does not
  // know about aliases.

  std::vector<std::string> host_data;
  if (!APIPermissionSet::ParseFromJSON(
          permissions,
          APIPermissionSet::kDisallowInternalPermissions,
          api_permissions,
          error,
          &host_data)) {
    return false;
  }

  // Verify feature availability of permissions.
  std::vector<APIPermission::ID> to_remove;
  const FeatureProvider* permission_features =
      FeatureProvider::GetPermissionFeatures();
  for (APIPermissionSet::const_iterator iter = api_permissions->begin();
       iter != api_permissions->end();
       ++iter) {
    Feature* feature = permission_features->GetFeature(iter->name());

    // The feature should exist since we just got an APIPermission for it. The
    // two systems should be updated together whenever a permission is added.
    DCHECK(feature) << "Could not find feature for " << iter->name();
    // http://crbug.com/176381
    if (!feature) {
      to_remove.push_back(iter->id());
      continue;
    }

    Feature::Availability availability =
        feature->IsAvailableToExtension(extension);
    if (!availability.is_available()) {
      // Don't fail, but warn the developer that the manifest contains
      // unrecognized permissions. This may happen legitimately if the
      // extensions requests platform- or channel-specific permissions.
      extension->AddInstallWarning(
          InstallWarning(availability.message(), feature->name()));
      to_remove.push_back(iter->id());
      continue;
    }

    if (iter->id() == APIPermission::kExperimental) {
      if (!CanSpecifyExperimentalPermission(extension)) {
        *error = base::ASCIIToUTF16(errors::kExperimentalFlagRequired);
        return false;
      }
    }
  }

  api_permissions->AddImpliedPermissions();

  // Remove permissions that are not available to this extension.
  for (std::vector<APIPermission::ID>::const_iterator iter = to_remove.begin();
       iter != to_remove.end();
       ++iter) {
    api_permissions->erase(*iter);
  }

  // Parse host pattern permissions.
  const int kAllowedSchemes =
      PermissionsData::CanExecuteScriptEverywhere(extension)
          ? URLPattern::SCHEME_ALL
          : Extension::kValidHostPermissionSchemes;

  for (std::vector<std::string>::const_iterator iter = host_data.begin();
       iter != host_data.end();
       ++iter) {
    const std::string& permission_str = *iter;

    // Check if it's a host pattern permission.
    URLPattern pattern = URLPattern(kAllowedSchemes);
    URLPattern::ParseResult parse_result = pattern.Parse(permission_str);
    if (parse_result == URLPattern::PARSE_SUCCESS) {
      // The path component is not used for host permissions, so we force it
      // to match all paths.
      pattern.SetPath("/*");
      int valid_schemes = pattern.valid_schemes();
      if (pattern.MatchesScheme(url::kFileScheme) &&
          !PermissionsData::CanExecuteScriptEverywhere(extension)) {
        extension->set_wants_file_access(true);
        if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS))
          valid_schemes &= ~URLPattern::SCHEME_FILE;
      }

      if (pattern.scheme() != content::kChromeUIScheme &&
          !PermissionsData::CanExecuteScriptEverywhere(extension)) {
        // Keep chrome:// in allowed schemes only if it's explicitly requested
        // or CanExecuteScriptEverywhere is true. If the
        // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission
        // will fail, so don't check the flag here.
        valid_schemes &= ~URLPattern::SCHEME_CHROMEUI;
      }
      pattern.SetValidSchemes(valid_schemes);

      if (!CanSpecifyHostPermission(extension, pattern, *api_permissions)) {
        // TODO(aboxhall): make a warning (see pattern.match_all_urls() block
        // below).
        extension->AddInstallWarning(InstallWarning(
            ErrorUtils::FormatErrorMessage(errors::kInvalidPermissionScheme,
                                           permission_str),
            key,
            permission_str));
        continue;
      }

      host_permissions->AddPattern(pattern);
      // We need to make sure all_urls matches chrome://favicon and (maybe)
      // chrome://thumbnail, so add them back in to host_permissions separately.
      if (pattern.match_all_urls()) {
        host_permissions->AddPatterns(
            ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(
                extension, *api_permissions));
      }
      continue;
    }

    // It's probably an unknown API permission. Do not throw an error so
    // extensions can retain backwards compatability (http://crbug.com/42742).
    extension->AddInstallWarning(InstallWarning(
        ErrorUtils::FormatErrorMessage(
            manifest_errors::kPermissionUnknownOrMalformed, permission_str),
        key,
        permission_str));
  }

  return true;
}

}  // namespace

struct PermissionsParser::InitialPermissions {
  APIPermissionSet api_permissions;
  ManifestPermissionSet manifest_permissions;
  URLPatternSet host_permissions;
  URLPatternSet scriptable_hosts;
};

PermissionsParser::PermissionsParser() {
}

PermissionsParser::~PermissionsParser() {
}

bool PermissionsParser::Parse(Extension* extension, base::string16* error) {
  initial_required_permissions_.reset(new InitialPermissions);
  if (!ParseHelper(extension,
                   keys::kPermissions,
                   &initial_required_permissions_->api_permissions,
                   &initial_required_permissions_->host_permissions,
                   error)) {
    return false;
  }

  initial_optional_permissions_.reset(new InitialPermissions);
  if (!ParseHelper(extension,
                   keys::kOptionalPermissions,
                   &initial_optional_permissions_->api_permissions,
                   &initial_optional_permissions_->host_permissions,
                   error)) {
    return false;
  }

  return true;
}

void PermissionsParser::Finalize(Extension* extension) {
  ManifestHandler::AddExtensionInitialRequiredPermissions(
      extension, &initial_required_permissions_->manifest_permissions);

  scoped_refptr<const PermissionSet> required_permissions(
      new PermissionSet(initial_required_permissions_->api_permissions,
                        initial_required_permissions_->manifest_permissions,
                        initial_required_permissions_->host_permissions,
                        initial_required_permissions_->scriptable_hosts));
  extension->SetManifestData(keys::kPermissions,
                             new ManifestPermissions(required_permissions));

  scoped_refptr<const PermissionSet> optional_permissions(
      new PermissionSet(initial_optional_permissions_->api_permissions,
                        initial_optional_permissions_->manifest_permissions,
                        initial_optional_permissions_->host_permissions,
                        URLPatternSet()));
  extension->SetManifestData(keys::kOptionalPermissions,
                             new ManifestPermissions(optional_permissions));
}

// static
void PermissionsParser::AddAPIPermission(Extension* extension,
                                         APIPermission::ID permission) {
  DCHECK(extension->permissions_parser());
  extension->permissions_parser()
      ->initial_required_permissions_->api_permissions.insert(permission);
}

// static
void PermissionsParser::AddAPIPermission(Extension* extension,
                                         APIPermission* permission) {
  DCHECK(extension->permissions_parser());
  extension->permissions_parser()
      ->initial_required_permissions_->api_permissions.insert(permission);
}

// static
bool PermissionsParser::HasAPIPermission(const Extension* extension,
                                         APIPermission::ID permission) {
  DCHECK(extension->permissions_parser());
  return extension->permissions_parser()
             ->initial_required_permissions_->api_permissions.count(
                 permission) > 0;
}

// static
void PermissionsParser::SetScriptableHosts(
    Extension* extension,
    const URLPatternSet& scriptable_hosts) {
  DCHECK(extension->permissions_parser());
  extension->permissions_parser()
      ->initial_required_permissions_->scriptable_hosts = scriptable_hosts;
}

// static
scoped_refptr<const PermissionSet> PermissionsParser::GetRequiredPermissions(
    const Extension* extension) {
  DCHECK(extension->GetManifestData(keys::kPermissions));
  return static_cast<const ManifestPermissions*>(
             extension->GetManifestData(keys::kPermissions))->permissions;
}

// static
scoped_refptr<const PermissionSet> PermissionsParser::GetOptionalPermissions(
    const Extension* extension) {
  DCHECK(extension->GetManifestData(keys::kOptionalPermissions));
  return static_cast<const ManifestPermissions*>(
             extension->GetManifestData(keys::kOptionalPermissions))
      ->permissions;
}

}  // namespace extensions