1 // Copyright (c) 2013 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 "chrome/common/extensions/extension_file_util.h" 6 7 #include <map> 8 #include <vector> 9 10 #include "base/file_util.h" 11 #include "base/files/file_enumerator.h" 12 #include "base/files/file_path.h" 13 #include "base/files/scoped_temp_dir.h" 14 #include "base/json/json_file_value_serializer.h" 15 #include "base/logging.h" 16 #include "base/metrics/histogram.h" 17 #include "base/path_service.h" 18 #include "base/strings/stringprintf.h" 19 #include "base/strings/utf_string_conversions.h" 20 #include "base/threading/thread_restrictions.h" 21 #include "chrome/common/chrome_paths.h" 22 #include "chrome/common/extensions/api/extension_action/action_info.h" 23 #include "chrome/common/extensions/extension.h" 24 #include "chrome/common/extensions/extension_icon_set.h" 25 #include "chrome/common/extensions/extension_l10n_util.h" 26 #include "chrome/common/extensions/extension_manifest_constants.h" 27 #include "chrome/common/extensions/extension_messages.h" 28 #include "chrome/common/extensions/manifest.h" 29 #include "chrome/common/extensions/manifest_handler.h" 30 #include "chrome/common/extensions/manifest_handlers/icons_handler.h" 31 #include "chrome/common/extensions/manifest_handlers/theme_handler.h" 32 #include "chrome/common/extensions/message_bundle.h" 33 #include "extensions/common/constants.h" 34 #include "extensions/common/extension_resource.h" 35 #include "extensions/common/install_warning.h" 36 #include "grit/generated_resources.h" 37 #include "net/base/escape.h" 38 #include "net/base/file_stream.h" 39 #include "ui/base/l10n/l10n_util.h" 40 41 using extensions::Extension; 42 using extensions::ExtensionResource; 43 using extensions::Manifest; 44 45 namespace errors = extension_manifest_errors; 46 47 namespace { 48 49 const base::FilePath::CharType kTempDirectoryName[] = FILE_PATH_LITERAL("Temp"); 50 51 // Add the image paths contained in the |icon_set| to |image_paths|. 52 void AddPathsFromIconSet(const ExtensionIconSet& icon_set, 53 std::set<base::FilePath>* image_paths) { 54 // TODO(viettrungluu): These |FilePath::FromUTF8Unsafe()| indicate that we're 55 // doing something wrong. 56 for (ExtensionIconSet::IconMap::const_iterator iter = icon_set.map().begin(); 57 iter != icon_set.map().end(); ++iter) { 58 image_paths->insert(base::FilePath::FromUTF8Unsafe(iter->second)); 59 } 60 } 61 62 } // namespace 63 64 namespace extension_file_util { 65 66 base::FilePath InstallExtension(const base::FilePath& unpacked_source_dir, 67 const std::string& id, 68 const std::string& version, 69 const base::FilePath& extensions_dir) { 70 base::FilePath extension_dir = extensions_dir.AppendASCII(id); 71 base::FilePath version_dir; 72 73 // Create the extension directory if it doesn't exist already. 74 if (!base::PathExists(extension_dir)) { 75 if (!file_util::CreateDirectory(extension_dir)) 76 return base::FilePath(); 77 } 78 79 // Get a temp directory on the same file system as the profile. 80 base::FilePath install_temp_dir = GetInstallTempDir(extensions_dir); 81 base::ScopedTempDir extension_temp_dir; 82 if (install_temp_dir.empty() || 83 !extension_temp_dir.CreateUniqueTempDirUnderPath(install_temp_dir)) { 84 LOG(ERROR) << "Creating of temp dir under in the profile failed."; 85 return base::FilePath(); 86 } 87 base::FilePath crx_temp_source = 88 extension_temp_dir.path().Append(unpacked_source_dir.BaseName()); 89 if (!base::Move(unpacked_source_dir, crx_temp_source)) { 90 LOG(ERROR) << "Moving extension from : " << unpacked_source_dir.value() 91 << " to : " << crx_temp_source.value() << " failed."; 92 return base::FilePath(); 93 } 94 95 // Try to find a free directory. There can be legitimate conflicts in the case 96 // of overinstallation of the same version. 97 const int kMaxAttempts = 100; 98 for (int i = 0; i < kMaxAttempts; ++i) { 99 base::FilePath candidate = extension_dir.AppendASCII( 100 base::StringPrintf("%s_%u", version.c_str(), i)); 101 if (!base::PathExists(candidate)) { 102 version_dir = candidate; 103 break; 104 } 105 } 106 107 if (version_dir.empty()) { 108 LOG(ERROR) << "Could not find a home for extension " << id << " with " 109 << "version " << version << "."; 110 return base::FilePath(); 111 } 112 113 if (!base::Move(crx_temp_source, version_dir)) { 114 LOG(ERROR) << "Installing extension from : " << crx_temp_source.value() 115 << " into : " << version_dir.value() << " failed."; 116 return base::FilePath(); 117 } 118 119 return version_dir; 120 } 121 122 void UninstallExtension(const base::FilePath& extensions_dir, 123 const std::string& id) { 124 // We don't care about the return value. If this fails (and it can, due to 125 // plugins that aren't unloaded yet), it will get cleaned up by 126 // ExtensionService::GarbageCollectExtensions. 127 base::DeleteFile(extensions_dir.AppendASCII(id), true); // recursive. 128 } 129 130 scoped_refptr<Extension> LoadExtension(const base::FilePath& extension_path, 131 Manifest::Location location, 132 int flags, 133 std::string* error) { 134 return LoadExtension(extension_path, std::string(), location, flags, error); 135 } 136 137 scoped_refptr<Extension> LoadExtension(const base::FilePath& extension_path, 138 const std::string& extension_id, 139 Manifest::Location location, 140 int flags, 141 std::string* error) { 142 scoped_ptr<base::DictionaryValue> manifest( 143 LoadManifest(extension_path, error)); 144 if (!manifest.get()) 145 return NULL; 146 if (!extension_l10n_util::LocalizeExtension(extension_path, manifest.get(), 147 error)) { 148 return NULL; 149 } 150 151 scoped_refptr<Extension> extension(Extension::Create(extension_path, 152 location, 153 *manifest, 154 flags, 155 extension_id, 156 error)); 157 if (!extension.get()) 158 return NULL; 159 160 std::vector<extensions::InstallWarning> warnings; 161 if (!ValidateExtension(extension.get(), error, &warnings)) 162 return NULL; 163 extension->AddInstallWarnings(warnings); 164 165 return extension; 166 } 167 168 base::DictionaryValue* LoadManifest(const base::FilePath& extension_path, 169 std::string* error) { 170 base::FilePath manifest_path = 171 extension_path.Append(extensions::kManifestFilename); 172 if (!base::PathExists(manifest_path)) { 173 *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); 174 return NULL; 175 } 176 177 JSONFileValueSerializer serializer(manifest_path); 178 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, error)); 179 if (!root.get()) { 180 if (error->empty()) { 181 // If |error| is empty, than the file could not be read. 182 // It would be cleaner to have the JSON reader give a specific error 183 // in this case, but other code tests for a file error with 184 // error->empty(). For now, be consistent. 185 *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_UNREADABLE); 186 } else { 187 *error = base::StringPrintf("%s %s", 188 errors::kManifestParseError, 189 error->c_str()); 190 } 191 return NULL; 192 } 193 194 if (!root->IsType(base::Value::TYPE_DICTIONARY)) { 195 *error = l10n_util::GetStringUTF8(IDS_EXTENSION_MANIFEST_INVALID); 196 return NULL; 197 } 198 199 return static_cast<base::DictionaryValue*>(root.release()); 200 } 201 202 std::vector<base::FilePath> FindPrivateKeyFiles( 203 const base::FilePath& extension_dir) { 204 std::vector<base::FilePath> result; 205 // Pattern matching only works at the root level, so filter manually. 206 base::FileEnumerator traversal(extension_dir, /*recursive=*/true, 207 base::FileEnumerator::FILES); 208 for (base::FilePath current = traversal.Next(); !current.empty(); 209 current = traversal.Next()) { 210 if (!current.MatchesExtension(extensions::kExtensionKeyFileExtension)) 211 continue; 212 213 std::string key_contents; 214 if (!file_util::ReadFileToString(current, &key_contents)) { 215 // If we can't read the file, assume it's not a private key. 216 continue; 217 } 218 std::string key_bytes; 219 if (!Extension::ParsePEMKeyBytes(key_contents, &key_bytes)) { 220 // If we can't parse the key, assume it's ok too. 221 continue; 222 } 223 224 result.push_back(current); 225 } 226 return result; 227 } 228 229 bool ValidateFilePath(const base::FilePath& path) { 230 int64 size = 0; 231 if (!base::PathExists(path) || 232 !file_util::GetFileSize(path, &size) || 233 size == 0) { 234 return false; 235 } 236 237 return true; 238 } 239 240 bool ValidateExtensionIconSet(const ExtensionIconSet& icon_set, 241 const Extension* extension, 242 int error_message_id, 243 std::string* error) { 244 for (ExtensionIconSet::IconMap::const_iterator iter = icon_set.map().begin(); 245 iter != icon_set.map().end(); 246 ++iter) { 247 const base::FilePath path = 248 extension->GetResource(iter->second).GetFilePath(); 249 if (!ValidateFilePath(path)) { 250 *error = l10n_util::GetStringFUTF8(error_message_id, 251 UTF8ToUTF16(iter->second)); 252 return false; 253 } 254 } 255 return true; 256 } 257 258 bool ValidateExtension(const Extension* extension, 259 std::string* error, 260 std::vector<extensions::InstallWarning>* warnings) { 261 // Ask registered manifest handlers to validate their paths. 262 if (!extensions::ManifestHandler::ValidateExtension( 263 extension, error, warnings)) 264 return false; 265 266 // Check children of extension root to see if any of them start with _ and is 267 // not on the reserved list. 268 if (!CheckForIllegalFilenames(extension->path(), error)) { 269 return false; 270 } 271 272 // Check that extensions don't include private key files. 273 std::vector<base::FilePath> private_keys = 274 FindPrivateKeyFiles(extension->path()); 275 if (extension->creation_flags() & Extension::ERROR_ON_PRIVATE_KEY) { 276 if (!private_keys.empty()) { 277 // Only print one of the private keys because l10n_util doesn't have a way 278 // to translate a list of strings. 279 *error = l10n_util::GetStringFUTF8( 280 IDS_EXTENSION_CONTAINS_PRIVATE_KEY, 281 private_keys.front().LossyDisplayName()); 282 return false; 283 } 284 } else { 285 for (size_t i = 0; i < private_keys.size(); ++i) { 286 warnings->push_back(extensions::InstallWarning( 287 extensions::InstallWarning::FORMAT_TEXT, 288 l10n_util::GetStringFUTF8( 289 IDS_EXTENSION_CONTAINS_PRIVATE_KEY, 290 private_keys[i].LossyDisplayName()))); 291 } 292 // Only warn; don't block loading the extension. 293 } 294 return true; 295 } 296 297 std::set<base::FilePath> GetBrowserImagePaths(const Extension* extension) { 298 std::set<base::FilePath> image_paths; 299 300 AddPathsFromIconSet(extensions::IconsInfo::GetIcons(extension), &image_paths); 301 302 // Theme images 303 const base::DictionaryValue* theme_images = 304 extensions::ThemeInfo::GetImages(extension); 305 if (theme_images) { 306 for (base::DictionaryValue::Iterator it(*theme_images); !it.IsAtEnd(); 307 it.Advance()) { 308 base::FilePath::StringType path; 309 if (it.value().GetAsString(&path)) 310 image_paths.insert(base::FilePath(path)); 311 } 312 } 313 314 const extensions::ActionInfo* page_action = 315 extensions::ActionInfo::GetPageActionInfo(extension); 316 if (page_action && !page_action->default_icon.empty()) 317 AddPathsFromIconSet(page_action->default_icon, &image_paths); 318 319 const extensions::ActionInfo* browser_action = 320 extensions::ActionInfo::GetBrowserActionInfo(extension); 321 if (browser_action && !browser_action->default_icon.empty()) 322 AddPathsFromIconSet(browser_action->default_icon, &image_paths); 323 324 return image_paths; 325 } 326 327 void GarbageCollectExtensions( 328 const base::FilePath& install_directory, 329 const std::multimap<std::string, base::FilePath>& extension_paths) { 330 // Nothing to clean up if it doesn't exist. 331 if (!base::DirectoryExists(install_directory)) 332 return; 333 334 DVLOG(1) << "Garbage collecting extensions..."; 335 base::FileEnumerator enumerator(install_directory, 336 false, // Not recursive. 337 base::FileEnumerator::DIRECTORIES); 338 base::FilePath extension_path; 339 for (extension_path = enumerator.Next(); !extension_path.value().empty(); 340 extension_path = enumerator.Next()) { 341 std::string extension_id; 342 343 base::FilePath basename = extension_path.BaseName(); 344 // Clean up temporary files left if Chrome crashed or quit in the middle 345 // of an extension install. 346 if (basename.value() == kTempDirectoryName) { 347 base::DeleteFile(extension_path, true); // Recursive 348 continue; 349 } 350 351 // Parse directory name as a potential extension ID. 352 if (IsStringASCII(basename.value())) { 353 extension_id = UTF16ToASCII(basename.LossyDisplayName()); 354 if (!Extension::IdIsValid(extension_id)) 355 extension_id.clear(); 356 } 357 358 // Delete directories that aren't valid IDs. 359 if (extension_id.empty()) { 360 DLOG(WARNING) << "Invalid extension ID encountered in extensions " 361 "directory: " << basename.value(); 362 DVLOG(1) << "Deleting invalid extension directory " 363 << extension_path.value() << "."; 364 base::DeleteFile(extension_path, true); // Recursive. 365 continue; 366 } 367 368 typedef std::multimap<std::string, base::FilePath>::const_iterator Iter; 369 std::pair<Iter, Iter> iter_pair = extension_paths.equal_range(extension_id); 370 371 // If there is no entry in the prefs file, just delete the directory and 372 // move on. This can legitimately happen when an uninstall does not 373 // complete, for example, when a plugin is in use at uninstall time. 374 if (iter_pair.first == iter_pair.second) { 375 DVLOG(1) << "Deleting unreferenced install for directory " 376 << extension_path.LossyDisplayName() << "."; 377 base::DeleteFile(extension_path, true); // Recursive. 378 continue; 379 } 380 381 // Clean up old version directories. 382 base::FileEnumerator versions_enumerator( 383 extension_path, 384 false, // Not recursive. 385 base::FileEnumerator::DIRECTORIES); 386 for (base::FilePath version_dir = versions_enumerator.Next(); 387 !version_dir.value().empty(); 388 version_dir = versions_enumerator.Next()) { 389 bool knownVersion = false; 390 for (Iter it = iter_pair.first; it != iter_pair.second; ++it) 391 if (version_dir.BaseName() == it->second.BaseName()) { 392 knownVersion = true; 393 break; 394 } 395 if (!knownVersion) { 396 DVLOG(1) << "Deleting old version for directory " 397 << version_dir.LossyDisplayName() << "."; 398 base::DeleteFile(version_dir, true); // Recursive. 399 } 400 } 401 } 402 } 403 404 extensions::MessageBundle* LoadMessageBundle( 405 const base::FilePath& extension_path, 406 const std::string& default_locale, 407 std::string* error) { 408 error->clear(); 409 // Load locale information if available. 410 base::FilePath locale_path = extension_path.Append(extensions::kLocaleFolder); 411 if (!base::PathExists(locale_path)) 412 return NULL; 413 414 std::set<std::string> locales; 415 if (!extension_l10n_util::GetValidLocales(locale_path, &locales, error)) 416 return NULL; 417 418 if (default_locale.empty() || 419 locales.find(default_locale) == locales.end()) { 420 *error = l10n_util::GetStringUTF8( 421 IDS_EXTENSION_LOCALES_NO_DEFAULT_LOCALE_SPECIFIED); 422 return NULL; 423 } 424 425 extensions::MessageBundle* message_bundle = 426 extension_l10n_util::LoadMessageCatalogs( 427 locale_path, 428 default_locale, 429 extension_l10n_util::CurrentLocaleOrDefault(), 430 locales, 431 error); 432 433 return message_bundle; 434 } 435 436 SubstitutionMap* LoadMessageBundleSubstitutionMap( 437 const base::FilePath& extension_path, 438 const std::string& extension_id, 439 const std::string& default_locale) { 440 SubstitutionMap* returnValue = new SubstitutionMap(); 441 if (!default_locale.empty()) { 442 // Touch disk only if extension is localized. 443 std::string error; 444 scoped_ptr<extensions::MessageBundle> bundle( 445 LoadMessageBundle(extension_path, default_locale, &error)); 446 447 if (bundle.get()) 448 *returnValue = *bundle->dictionary(); 449 } 450 451 // Add @@extension_id reserved message here, so it's available to 452 // non-localized extensions too. 453 returnValue->insert( 454 std::make_pair(extensions::MessageBundle::kExtensionIdKey, extension_id)); 455 456 return returnValue; 457 } 458 459 bool CheckForIllegalFilenames(const base::FilePath& extension_path, 460 std::string* error) { 461 // Reserved underscore names. 462 static const base::FilePath::CharType* reserved_names[] = { 463 extensions::kLocaleFolder, 464 extensions::kPlatformSpecificFolder, 465 FILE_PATH_LITERAL("__MACOSX"), 466 }; 467 CR_DEFINE_STATIC_LOCAL( 468 std::set<base::FilePath::StringType>, reserved_underscore_names, 469 (reserved_names, reserved_names + arraysize(reserved_names))); 470 471 // Enumerate all files and directories in the extension root. 472 // There is a problem when using pattern "_*" with FileEnumerator, so we have 473 // to cheat with find_first_of and match all. 474 const int kFilesAndDirectories = 475 base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES; 476 base::FileEnumerator all_files(extension_path, false, kFilesAndDirectories); 477 478 base::FilePath file; 479 while (!(file = all_files.Next()).empty()) { 480 base::FilePath::StringType filename = file.BaseName().value(); 481 // Skip all that don't start with "_". 482 if (filename.find_first_of(FILE_PATH_LITERAL("_")) != 0) continue; 483 if (reserved_underscore_names.find(filename) == 484 reserved_underscore_names.end()) { 485 *error = base::StringPrintf( 486 "Cannot load extension with file or directory name %s. " 487 "Filenames starting with \"_\" are reserved for use by the system.", 488 filename.c_str()); 489 return false; 490 } 491 } 492 493 return true; 494 } 495 496 base::FilePath ExtensionURLToRelativeFilePath(const GURL& url) { 497 std::string url_path = url.path(); 498 if (url_path.empty() || url_path[0] != '/') 499 return base::FilePath(); 500 501 // Drop the leading slashes and convert %-encoded UTF8 to regular UTF8. 502 std::string file_path = net::UnescapeURLComponent(url_path, 503 net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS); 504 size_t skip = file_path.find_first_not_of("/\\"); 505 if (skip != file_path.npos) 506 file_path = file_path.substr(skip); 507 508 base::FilePath path = base::FilePath::FromUTF8Unsafe(file_path); 509 510 // It's still possible for someone to construct an annoying URL whose path 511 // would still wind up not being considered relative at this point. 512 // For example: chrome-extension://id/c:////foo.html 513 if (path.IsAbsolute()) 514 return base::FilePath(); 515 516 return path; 517 } 518 519 base::FilePath ExtensionResourceURLToFilePath(const GURL& url, 520 const base::FilePath& root) { 521 std::string host = net::UnescapeURLComponent(url.host(), 522 net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS); 523 if (host.empty()) 524 return base::FilePath(); 525 526 base::FilePath relative_path = ExtensionURLToRelativeFilePath(url); 527 if (relative_path.empty()) 528 return base::FilePath(); 529 530 base::FilePath path = root.AppendASCII(host).Append(relative_path); 531 if (!base::PathExists(path)) 532 return base::FilePath(); 533 path = base::MakeAbsoluteFilePath(path); 534 if (path.empty() || !root.IsParent(path)) 535 return base::FilePath(); 536 return path; 537 } 538 539 base::FilePath GetInstallTempDir(const base::FilePath& extensions_dir) { 540 // We do file IO in this function, but only when the current profile's 541 // Temp directory has never been used before, or in a rare error case. 542 // Developers are not likely to see these situations often, so do an 543 // explicit thread check. 544 base::ThreadRestrictions::AssertIOAllowed(); 545 546 // Create the temp directory as a sub-directory of the Extensions directory. 547 // This guarantees it is on the same file system as the extension's eventual 548 // install target. 549 base::FilePath temp_path = extensions_dir.Append(kTempDirectoryName); 550 if (base::PathExists(temp_path)) { 551 if (!base::DirectoryExists(temp_path)) { 552 DLOG(WARNING) << "Not a directory: " << temp_path.value(); 553 return base::FilePath(); 554 } 555 if (!base::PathIsWritable(temp_path)) { 556 DLOG(WARNING) << "Can't write to path: " << temp_path.value(); 557 return base::FilePath(); 558 } 559 // This is a directory we can write to. 560 return temp_path; 561 } 562 563 // Directory doesn't exist, so create it. 564 if (!file_util::CreateDirectory(temp_path)) { 565 DLOG(WARNING) << "Couldn't create directory: " << temp_path.value(); 566 return base::FilePath(); 567 } 568 return temp_path; 569 } 570 571 void DeleteFile(const base::FilePath& path, bool recursive) { 572 base::DeleteFile(path, recursive); 573 } 574 575 } // namespace extension_file_util 576