Home | History | Annotate | Download | only in manifest_handlers
      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 
     41 ManifestPermissions::ManifestPermissions(
     42     scoped_refptr<const PermissionSet> permissions)
     43     : permissions(permissions) {
     44 }
     45 
     46 ManifestPermissions::~ManifestPermissions() {
     47 }
     48 
     49 // Custom checks for the experimental permission that can't be expressed in
     50 // _permission_features.json.
     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|.
     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.
    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 
    257 PermissionsParser::PermissionsParser() {
    258 }
    259 
    260 PermissionsParser::~PermissionsParser() {
    261 }
    262 
    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 
    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
    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
    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
    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
    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
    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
    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