diff options
Diffstat (limited to 'chrome/browser/extensions/extensions_service.cc')
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 398 |
1 files changed, 50 insertions, 348 deletions
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 2acc256..1b993e6 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -6,23 +6,16 @@ #include "app/l10n_util.h" #include "base/command_line.h" -#include "base/crypto/signature_verifier.h" #include "base/file_util.h" -#include "base/gfx/png_encoder.h" -#include "base/scoped_handle.h" #include "base/scoped_temp_dir.h" #include "base/stl_util-inl.h" #include "base/string_util.h" -#include "base/third_party/nss/blapi.h" -#include "base/third_party/nss/sha256.h" -#include "base/thread.h" #include "base/values.h" #include "net/base/file_stream.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" -#include "chrome/browser/extensions/extension_creator.h" #include "chrome/browser/extensions/extension_browser_event_router.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_updater.h" @@ -31,22 +24,17 @@ #include "chrome/browser/extensions/theme_preview_infobar_delegate.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/utility_process_host.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_error_reporter.h" -#include "chrome/common/extensions/extension_unpacker.h" #include "chrome/common/json_value_serializer.h" #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" -#include "chrome/common/zip.h" #include "chrome/common/url_constants.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" -#include "net/base/base64.h" -#include "third_party/skia/include/core/SkBitmap.h" #if defined(OS_WIN) #include "app/win_util.h" @@ -59,8 +47,6 @@ // ExtensionsService. -const char ExtensionsService::kExtensionHeaderMagic[] = "Cr24"; - const char* ExtensionsService::kInstallDirectoryName = "Extensions"; const char* ExtensionsService::kCurrentVersionFileName = "Current Version"; @@ -69,27 +55,6 @@ const char* ExtensionsService::kGalleryDownloadURLPrefix = const char* ExtensionsService::kGalleryURLPrefix = "https://tools.google.com/chrome/"; -const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL"; - -namespace { - -// A temporary subdirectory where we unpack extensions. -const char* kUnpackExtensionDir = "TEMP_UNPACK"; - -// Unpacking errors -const char* kBadMagicNumberError = "Bad magic number"; -const char* kBadHeaderSizeError = "Excessively large key or signature"; -const char* kBadVersionNumberError = "Bad version number"; -const char* kInvalidExtensionHeaderError = "Invalid extension header"; -const char* kInvalidPublicKeyError = "Invalid public key"; -const char* kInvalidSignatureError = "Invalid signature"; -const char* kSignatureVerificationFailed = "Signature verification failed"; -const char* kSignatureVerificationInitFailed = - "Signature verification initialization failed. This is most likely " - "caused by a public key in the wrong format (should encode algorithm)."; - -} - // static bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url, const GURL& referrer_url) { @@ -101,131 +66,46 @@ bool ExtensionsService::IsDownloadFromGallery(const GURL& download_url, } } -// This class coordinates an extension unpack task which is run in a separate -// process. Results are sent back to this class, which we route to the -// ExtensionServiceBackend. +// This class hosts a SandboxedExtensionUnpacker task and routes the results +// back to ExtensionsService. The unpack process is started immediately on +// construction of this object. class ExtensionsServiceBackend::UnpackerClient - : public UtilityProcessHost::Client { + : public SandboxedExtensionUnpackerClient { public: UnpackerClient(ExtensionsServiceBackend* backend, const FilePath& extension_path, - const std::string& public_key, const std::string& expected_id, bool silent, bool from_gallery) : backend_(backend), extension_path_(extension_path), - public_key_(public_key), expected_id_(expected_id), got_response_(false), - silent_(silent), from_gallery_(from_gallery) { - } - - // Starts the unpack task. We call back to the backend when the task is done, - // or a problem occurs. - void Start() { - AddRef(); // balanced in OnUnpackExtensionReply() - - // TODO(mpcomplete): handle multiple installs - FilePath temp_dir = backend_->install_directory_.AppendASCII( - kUnpackExtensionDir); - if (!file_util::CreateDirectory(temp_dir)) { - backend_->ReportExtensionInstallError(extension_path_, - "Failed to create temporary directory."); - return; - } - - temp_extension_path_ = temp_dir.Append(extension_path_.BaseName()); - if (!file_util::CopyFile(extension_path_, temp_extension_path_)) { - backend_->ReportExtensionInstallError(extension_path_, - "Failed to copy extension file to temporary directory."); - return; - } - - if (backend_->resource_dispatcher_host_) { - ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE, - NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread, - backend_->resource_dispatcher_host_, - MessageLoop::current())); - } else { - // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're - // in a unit test and run the unpacker directly in-process. - ExtensionUnpacker unpacker(temp_extension_path_); - if (unpacker.Run()) { - OnUnpackExtensionSucceededImpl(*unpacker.parsed_manifest(), - unpacker.decoded_images()); - } else { - OnUnpackExtensionFailed(unpacker.error_message()); - } - } + expected_id_(expected_id), silent_(silent), from_gallery_(from_gallery) { + unpacker_ = new SandboxedExtensionUnpacker(extension_path, + backend->resource_dispatcher_host_, this); + unpacker_->Start(); } private: - // UtilityProcessHost::Client - virtual void OnProcessCrashed() { - // Don't report crashes if they happen after we got a response. - if (got_response_) - return; - - OnUnpackExtensionFailed("Chrome crashed while trying to install."); + // SandboxedExtensionUnpackerClient + virtual void OnUnpackSuccess(const FilePath& temp_dir, + const FilePath& extension_dir, + Extension* extension) { + backend_->OnExtensionUnpacked(extension_path_, extension_dir, extension, + expected_id_, silent_, from_gallery_); + file_util::Delete(temp_dir, true); + delete this; } - virtual void OnUnpackExtensionSucceeded(const DictionaryValue& manifest) { - ExtensionUnpacker::DecodedImages images; - if (!ExtensionUnpacker::ReadImagesFromFile(temp_extension_path_, - &images)) { - OnUnpackExtensionFailed("Couldn't read image data from disk."); - } else { - OnUnpackExtensionSucceededImpl(manifest, images); - } - } - - void OnUnpackExtensionSucceededImpl( - const DictionaryValue& manifest, - const ExtensionUnpacker::DecodedImages& images) { - // Add our public key into the parsed manifest. We want it to be saved so - // that we can later refer to it (eg for generating ids, validating - // signatures, etc). - // The const_cast is hacky, but seems like the right thing here, rather than - // making a full copy just to make this change. - const_cast<DictionaryValue*>(&manifest)->SetString( - extension_manifest_keys::kPublicKey, public_key_); - - // The extension was unpacked to the temp dir inside our unpacking dir. - FilePath extension_dir = temp_extension_path_.DirName().AppendASCII( - ExtensionsServiceBackend::kTempExtensionName); - backend_->OnExtensionUnpacked(extension_path_, extension_dir, - expected_id_, manifest, images, silent_, - from_gallery_); - Cleanup(); - } - - virtual void OnUnpackExtensionFailed(const std::string& error_message) { + virtual void OnUnpackFailure(const std::string& error_message) { backend_->ReportExtensionInstallError(extension_path_, error_message); - Cleanup(); - } - - // Cleans up our temp directory. - void Cleanup() { - if (got_response_) - return; - - got_response_ = true; - file_util::Delete(temp_extension_path_.DirName(), true); - Release(); // balanced in Run() + delete this; } - // Starts the utility process that unpacks our extension. - void StartProcessOnIOThread(ResourceDispatcherHost* rdh, - MessageLoop* file_loop) { - UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop); - host->StartExtensionUnpacker(temp_extension_path_); - } + scoped_refptr<SandboxedExtensionUnpacker> unpacker_; scoped_refptr<ExtensionsServiceBackend> backend_; // The path to the crx file that we're installing. FilePath extension_path_; - // The public key of the extension we're installing. - std::string public_key_; - // The path to the copy of the crx file in the temporary directory where we're // unpacking it. FilePath temp_extension_path_; @@ -233,10 +113,6 @@ class ExtensionsServiceBackend::UnpackerClient // The ID we expect this extension to have, if any. std::string expected_id_; - // True if we got a response from the utility process and have cleaned up - // already. - bool got_response_; - // True if the install should be done with no confirmation dialog. bool silent_; @@ -673,11 +549,6 @@ void ExtensionsServiceBackend::GarbageCollectExtensions( std::string extension_id = WideToASCII( extension_path.BaseName().ToWStringHack()); - // The utility process might be in the middle of unpacking an extension, so - // ignore the temp unpacking directory. - if (extension_id == kUnpackExtensionDir) - continue; - // If there is no Current Version file, just delete the directory and move // on. This can legitimately happen when an uninstall does not complete, for // example, when a plugin is in use at uninstall time. @@ -1038,130 +909,28 @@ void ExtensionsServiceBackend::UpdateExtension(const std::string& id, void ExtensionsServiceBackend::InstallOrUpdateExtension( const FilePath& extension_path, bool from_gallery, const std::string& expected_id, bool silent) { - std::string actual_public_key; - if (!ValidateSignature(extension_path, &actual_public_key)) - return; // Failures reported within ValidateSignature(). - - UnpackerClient* client = new UnpackerClient( - this, extension_path, actual_public_key, expected_id, silent, - from_gallery); - client->Start(); -} - -bool ExtensionsServiceBackend::ValidateSignature(const FilePath& extension_path, - std::string* key_out) { - ScopedStdioHandle file(file_util::OpenFile(extension_path, "rb")); - if (!file.get()) { - ReportExtensionInstallError(extension_path, "Could not open file."); - return NULL; - } - - // Read and verify the header. - ExtensionsService::ExtensionHeader header; - size_t len; - - // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it - // appears that we don't have any endian/alignment aware serialization - // code in the code base. So for now, this assumes that we're running - // on a little endian machine with 4 byte alignment. - len = fread(&header, 1, sizeof(ExtensionsService::ExtensionHeader), - file.get()); - if (len < sizeof(ExtensionsService::ExtensionHeader)) { - ReportExtensionInstallError(extension_path, kInvalidExtensionHeaderError); - return false; - } - if (strncmp(ExtensionsService::kExtensionHeaderMagic, header.magic, - sizeof(header.magic))) { - ReportExtensionInstallError(extension_path, kBadMagicNumberError); - return false; - } - if (header.version != ExtensionsService::kCurrentVersion) { - ReportExtensionInstallError(extension_path, kBadVersionNumberError); - return false; - } - if (header.key_size > ExtensionsService::kMaxPublicKeySize || - header.signature_size > ExtensionsService::kMaxSignatureSize) { - ReportExtensionInstallError(extension_path, kBadHeaderSizeError); - return false; - } - - std::vector<uint8> key; - key.resize(header.key_size); - len = fread(&key.front(), sizeof(uint8), header.key_size, file.get()); - if (len < header.key_size) { - ReportExtensionInstallError(extension_path, kInvalidPublicKeyError); - return false; - } - - std::vector<uint8> signature; - signature.resize(header.signature_size); - len = fread(&signature.front(), sizeof(uint8), header.signature_size, - file.get()); - if (len < header.signature_size) { - ReportExtensionInstallError(extension_path, kInvalidSignatureError); - return false; - } - - // Note: this structure is an ASN.1 which encodes the algorithm used - // with its parameters. This is defined in PKCS #1 v2.1 (RFC 3447). - // It is encoding: { OID sha1WithRSAEncryption PARAMETERS NULL } - // TODO(aa): This needs to be factored away someplace common. - const uint8 signature_algorithm[15] = { - 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00 - }; - - base::SignatureVerifier verifier; - if (!verifier.VerifyInit(signature_algorithm, - sizeof(signature_algorithm), - &signature.front(), - signature.size(), - &key.front(), - key.size())) { - ReportExtensionInstallError(extension_path, - kSignatureVerificationInitFailed); - return false; - } - - unsigned char buf[1 << 12]; - while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0) - verifier.VerifyUpdate(buf, len); - - if (!verifier.VerifyFinal()) { - ReportExtensionInstallError(extension_path, kSignatureVerificationFailed); - return false; - } - - net::Base64Encode(std::string(reinterpret_cast<char*>(&key.front()), - key.size()), key_out); - return true; + // NOTE: We don't need to keep a reference to this, it deletes itself when it + // is done. + new UnpackerClient(this, extension_path, expected_id, silent, from_gallery); } void ExtensionsServiceBackend::OnExtensionUnpacked( - const FilePath& extension_path, - const FilePath& temp_extension_dir, - const std::string& expected_id, - const DictionaryValue& manifest, - const std::vector< Tuple2<SkBitmap, FilePath> >& images, - bool silent, bool from_gallery) { - Extension extension; - std::string error; - if (!extension.InitFromValue(manifest, - true, // require ID - &error)) { - ReportExtensionInstallError(extension_path, "Invalid extension manifest."); - return; - } + const FilePath& crx_path, const FilePath& unpacked_path, + Extension* extension, const std::string expected_id, bool silent, + bool from_gallery) { + // Take ownership of the extension object. + scoped_ptr<Extension> extension_deleter(extension); Extension::Location location = Extension::INTERNAL; - LookupExternalExtension(extension.id(), NULL, &location); + LookupExternalExtension(extension->id(), NULL, &location); + extension->set_location(location); bool allow_install = false; if (extensions_enabled_) allow_install = true; // Always allow themes. - if (extension.IsTheme()) + if (extension->IsTheme()) allow_install = true; // Always allow externally installed extensions (partners use this). @@ -1169,8 +938,7 @@ void ExtensionsServiceBackend::OnExtensionUnpacked( allow_install = true; if (!allow_install) { - ReportExtensionInstallError(extension_path, - "Extensions are not enabled."); + ReportExtensionInstallError(crx_path, "Extensions are not enabled."); return; } @@ -1182,7 +950,7 @@ void ExtensionsServiceBackend::OnExtensionUnpacked( // - during tests (!frontend->show_extension_prompts()) // - autoupdate (silent). bool show_dialog = true; - if (extension.IsTheme()) + if (extension->IsTheme()) show_dialog = false; if (Extension::IsExternalLocation(location)) @@ -1215,7 +983,7 @@ void ExtensionsServiceBackend::OnExtensionUnpacked( NULL, CFSTR("Cancel"), NULL, &response); if (response == kCFUserNotificationAlternateResponse) { - ReportExtensionInstallError(extension_path, + ReportExtensionInstallError(crx_path, "User did not allow extension to be installed."); return; } @@ -1223,132 +991,66 @@ void ExtensionsServiceBackend::OnExtensionUnpacked( } // If an expected id was provided, make sure it matches. - if (!expected_id.empty() && expected_id != extension.id()) { + if (!expected_id.empty() && expected_id != extension->id()) { std::string error_msg = "ID in new extension manifest ("; - error_msg += extension.id(); + error_msg += extension->id(); error_msg += ") does not match expected ID ("; error_msg += expected_id; error_msg += ")"; - ReportExtensionInstallError(extension_path, error_msg); + ReportExtensionInstallError(crx_path, error_msg); return; } // <profile>/Extensions/<id> - FilePath dest_dir = install_directory_.AppendASCII(extension.id()); - std::string version = extension.VersionString(); + FilePath dest_dir = install_directory_.AppendASCII(extension->id()); + std::string version = extension->VersionString(); std::string current_version; Extension::InstallType install_type = - CompareToInstalledVersion(extension.id(), version, ¤t_version); + CompareToInstalledVersion(extension->id(), version, ¤t_version); // Do not allow downgrade. if (install_type == Extension::DOWNGRADE) { - ReportExtensionInstallError(extension_path, + ReportExtensionInstallError(crx_path, "Error: Attempt to downgrade extension from more recent version."); return; } if (install_type == Extension::REINSTALL) { - if (NeedsReinstall(extension.id(), current_version)) { + if (NeedsReinstall(extension->id(), current_version)) { // Treat corrupted existing installation as new install case. install_type = Extension::NEW_INSTALL; } else { // The client may use this as a signal (to switch themes, for instance). - ReportExtensionOverinstallAttempted(extension.id(), extension_path); - return; - } - } - - // Write our parsed manifest back to disk, to ensure it doesn't contain an - // exploitable bug that can be used to compromise the browser. - std::string manifest_json; - JSONStringValueSerializer serializer(&manifest_json); - serializer.set_pretty_print(true); - if (!serializer.Serialize(manifest)) { - ReportExtensionInstallError(extension_path, - "Error serializing manifest.json."); - return; - } - - FilePath manifest_path = - temp_extension_dir.AppendASCII(Extension::kManifestFilename); - if (!file_util::WriteFile(manifest_path, - manifest_json.data(), manifest_json.size())) { - ReportExtensionInstallError(extension_path, "Error saving manifest.json."); - return; - } - - // Delete any images that may be used by the browser. We're going to write - // out our own versions of the parsed images, and we want to make sure the - // originals are gone for good. - std::set<FilePath> image_paths = extension.GetBrowserImages(); - if (image_paths.size() != images.size()) { - ReportExtensionInstallError(extension_path, - "Decoded images don't match what's in the manifest."); - return; - } - - for (std::set<FilePath>::iterator it = image_paths.begin(); - it != image_paths.end(); ++it) { - if (!file_util::Delete(temp_extension_dir.Append(*it), false)) { - ReportExtensionInstallError(extension_path, - "Error removing old image file."); - return; - } - } - - // Write our parsed images back to disk as well. - for (size_t i = 0; i < images.size(); ++i) { - const SkBitmap& image = images[i].a; - FilePath path = temp_extension_dir.Append(images[i].b); - - std::vector<unsigned char> image_data; - // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even - // though they may originally be .jpg, etc. Figure something out. - // http://code.google.com/p/chromium/issues/detail?id=12459 - if (!PNGEncoder::EncodeBGRASkBitmap(image, false, &image_data)) { - ReportExtensionInstallError(extension_path, - "Error re-encoding theme image."); - return; - } - - // Note: we're overwriting existing files that the utility process wrote, - // so we can be sure the directory exists. - const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]); - if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) { - ReportExtensionInstallError(extension_path, "Error saving theme image."); + ReportExtensionOverinstallAttempted(extension->id(), crx_path); return; } } // <profile>/Extensions/<dir_name>/<version> FilePath version_dir = dest_dir.AppendASCII(version); + extension->set_path(version_dir); // If anything fails after this, we want to delete the extension dir. ScopedTempDir scoped_version_dir; scoped_version_dir.Set(version_dir); - if (!InstallDirSafely(temp_extension_dir, version_dir)) + if (!InstallDirSafely(unpacked_path, version_dir)) return; if (!SetCurrentVersion(dest_dir, version)) return; - Extension* loaded = LoadExtension(version_dir, - location, - true); // require id - CHECK(loaded); - frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod( - frontend_, &ExtensionsService::OnExtensionInstalled, extension_path, - loaded, install_type)); + frontend_, &ExtensionsService::OnExtensionInstalled, crx_path, + extension, install_type)); // Only one extension, but ReportExtensionsLoaded can handle multiple, // so we need to construct a list. - scoped_ptr<ExtensionList> extensions(new ExtensionList); - extensions->push_back(loaded); + ExtensionList* extensions = new ExtensionList; - // Hand off ownership of the loaded extensions to the frontend. - ReportExtensionsLoaded(extensions.release()); + // Hand off ownership of the extension to the frontend. + extensions->push_back(extension_deleter.release()); + ReportExtensionsLoaded(extensions); scoped_version_dir.Take(); } |