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