172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/sandboxed_extension_unpacker.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <set> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/base64.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h" 11513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "base/file_util_proxy.h" 12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_handle.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h" 1472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/metrics/histogram.h" 1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/path_service.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/task.h" 173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h" // TODO(viettrungluu): delete me. 18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "crypto/signature_verifier.h" 1921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/extensions/extension_service.h" 2072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/common/chrome_paths.h" 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_switches.h" 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h" 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_constants.h" 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_file_util.h" 25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_l10n_util.h" 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_unpacker.h" 27dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h" 28dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/resource_dispatcher_host.h" 29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/json_value_serializer.h" 3021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "grit/generated_resources.h" 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkBitmap.h" 3272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h" 3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/codec/png_codec.h" 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// The following macro makes histograms that record the length of paths 36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// in this file much easier to read. 37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Windows has a short max path length. If the path length to a 38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// file being unpacked from a CRX exceeds the max length, we might 39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// fail to install. To see if this is happening, see how long the 40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// path to the temp unpack directory is. See crbug.com/69693 . 41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#define PATH_LENGTH_HISTOGRAM(name, path) \ 42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen UMA_HISTOGRAM_CUSTOM_COUNTS(name, path.value().length(), 0, 500, 100) 43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst char SandboxedExtensionUnpacker::kExtensionHeaderMagic[] = "Cr24"; 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochSandboxedExtensionUnpacker::SandboxedExtensionUnpacker( 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const FilePath& crx_path, 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ResourceDispatcherHost* rdh, 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SandboxedExtensionUnpackerClient* client) 5072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen : crx_path_(crx_path), 51731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick thread_identifier_(BrowserThread::ID_COUNT), 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch rdh_(rdh), client_(client), got_response_(false) { 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool SandboxedExtensionUnpacker::CreateTempDirectory() { 5672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_identifier_)); 5772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 5872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen FilePath user_data_temp_dir = extension_file_util::GetUserDataTempDir(); 5972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (user_data_temp_dir.empty()) { 6072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 6172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen COULD_NOT_GET_TEMP_DIRECTORY, 6272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 6372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 6472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("COULD_NOT_GET_TEMP_DIRECTORY"))); 6572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 6672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 6772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 6872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!temp_dir_.CreateUniqueTempDirUnderPath(user_data_temp_dir)) { 6972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 7072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen COULD_NOT_CREATE_TEMP_DIRECTORY, 7172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 7272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 7372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("COULD_NOT_CREATE_TEMP_DIRECTORY"))); 7472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 7572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 7672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 7772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return true; 7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 7972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid SandboxedExtensionUnpacker::Start() { 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We assume that we are started on the thread that the client wants us to do 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // file IO on. 83731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_identifier_)); 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackInitialCrxPathLength", 86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen crx_path_); 8772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!CreateTempDirectory()) 8872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return; // ReportFailure() already called. 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Initialize the path that will eventually contain the unpacked extension. 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch extension_root_ = temp_dir_.path().AppendASCII( 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch extension_filenames::kTempExtensionName); 93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackUnpackedCrxPathLength", 94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen extension_root_); 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Extract the public key and validate the package. 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!ValidateSignature()) 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; // ValidateSignature() already reported the error. 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Copy the crx file into our working directory. 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath temp_crx_path = temp_dir_.path().Append(crx_path_.BaseName()); 102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackTempCrxPathLength", 103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen temp_crx_path); 104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!file_util::CopyFile(crx_path_, temp_crx_path)) { 10621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Failed to copy extension file to temporary directory. 10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 10872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY, 10972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 11072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 11172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY"))); 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If we are supposed to use a subprocess, kick off the subprocess. 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(asargent) we shouldn't need to do this branch here - instead 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // UtilityProcessHost should handle it for us. (http://crbug.com/19192) 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool use_utility_process = rdh_ && 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (use_utility_process) { 1223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // The utility process will have access to the directory passed to 1233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // SandboxedExtensionUnpacker. That directory should not contain a 1243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // symlink or NTFS reparse point. When the path is used, following 1253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // the link/reparse point will cause file system access outside the 1263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // sandbox path, and the sandbox will deny the operation. 1273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick FilePath link_free_crx_path; 1283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!file_util::NormalizeFilePath(temp_crx_path, &link_free_crx_path)) { 1293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick LOG(ERROR) << "Could not get the normalized path of " 1303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick << temp_crx_path.value(); 13172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 13272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen COULD_NOT_GET_SANDBOX_FRIENDLY_PATH, 13372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringUTF8(IDS_EXTENSION_UNPACK_FAILED)); 1343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 1353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength", 137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen link_free_crx_path); 1383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 139731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask( 140731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::IO, FROM_HERE, 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewRunnableMethod( 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch this, 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &SandboxedExtensionUnpacker::StartProcessOnIOThread, 1443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick link_free_crx_path)); 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Otherwise, unpack the extension in this process. 1473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ExtensionUnpacker unpacker(temp_crx_path); 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (unpacker.Run() && unpacker.DumpImagesToFile() && 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch unpacker.DumpMessageCatalogsToFile()) { 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch OnUnpackExtensionSucceeded(*unpacker.parsed_manifest()); 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch OnUnpackExtensionFailed(unpacker.error_message()); 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 157513209b27ff55e2841eac0e4120199c23acce758Ben MurdochSandboxedExtensionUnpacker::~SandboxedExtensionUnpacker() { 158513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch base::FileUtilProxy::Delete( 159513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch BrowserThread::GetMessageLoopProxyForThread(thread_identifier_), 160513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch temp_dir_.Take(), 161513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch true, 162513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch NULL); 163513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 164731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid SandboxedExtensionUnpacker::StartProcessOnIOThread( 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const FilePath& temp_crx_path) { 167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen UtilityProcessHost* host = new UtilityProcessHost(this, thread_identifier_); 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host->StartExtensionUnpacker(temp_crx_path); 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded( 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const DictionaryValue& manifest) { 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Skip check for unittests. 174731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (thread_identifier_ != BrowserThread::ID_COUNT) 17572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CHECK(BrowserThread::CurrentlyOn(thread_identifier_)); 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch got_response_ = true; 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_ptr<DictionaryValue> final_manifest(RewriteManifestFile(manifest)); 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!final_manifest.get()) 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create an extension object that refers to the temporary location the 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // extension was unpacked to. We use this until the extension is finally 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // installed. For example, the install UI shows images from inside the 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // extension. 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Localize manifest now, so confirm UI gets correct extension name. 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string error; 189513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!extension_l10n_util::LocalizeExtension(extension_root_, 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch final_manifest.get(), 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &error)) { 19272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen COULD_NOT_LOCALIZE_EXTENSION, 19472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 19572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, 19672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16(error))); 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 200513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch extension_ = Extension::Create( 201dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen extension_root_, 202dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen Extension::INTERNAL, 203dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen *final_manifest, 204ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Extension::REQUIRE_KEY, 205dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen &error); 206513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 207513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!extension_.get()) { 20872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 20972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen INVALID_MANIFEST, 21072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen std::string("Manifest is invalid: ") + error); 211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!RewriteImageFiles()) 215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!RewriteCatalogFiles()) 218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ReportSuccess(); 221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid SandboxedExtensionUnpacker::OnUnpackExtensionFailed( 224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::string& error) { 22572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CHECK(BrowserThread::CurrentlyOn(thread_identifier_)); 226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch got_response_ = true; 22772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 22872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen UNPACKER_CLIENT_FAILED, 22972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 23072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, 23172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16(error))); 232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 23421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenvoid SandboxedExtensionUnpacker::OnProcessCrashed(int exit_code) { 235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't report crashes if they happen after we got a response. 236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (got_response_) 237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 23921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Utility process crashed while trying to install. 24072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 24172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL, 24272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 24372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 24472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL"))); 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool SandboxedExtensionUnpacker::ValidateSignature() { 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ScopedStdioHandle file(file_util::OpenFile(crx_path_, "rb")); 249ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!file.get()) { 251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Could not open crx file for reading. 252ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined (OS_WIN) 253ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // On windows, get the error code. 254ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen uint32 error_code = ::GetLastError(); 255ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(skerner): Use this histogram to understand why so many 256ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // windows users hit this error. crbug.com/69693 257ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Windows errors are unit32s, but all of likely errors are in 259ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // [1, 1000]. See winerror.h for the meaning of specific values. 260ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Clip errors outside the expected range to a single extra value. 261ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If there are errors in that extra bucket, we will know to expand 262ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // the range. 263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const uint32 kMaxErrorToSend = 1001; 264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen error_code = std::min(error_code, kMaxErrorToSend); 265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen UMA_HISTOGRAM_ENUMERATION("Extensions.ErrorCodeFromCrxOpen", 266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen error_code, kMaxErrorToSend); 267ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif 268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 26972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 27072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CRX_FILE_NOT_READABLE, 27172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 27272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 27372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_FILE_NOT_READABLE"))); 274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Read and verify the header. 278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExtensionHeader header; 279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t len; 280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // appears that we don't have any endian/alignment aware serialization 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // code in the code base. So for now, this assumes that we're running 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // on a little endian machine with 4 byte alignment. 285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch len = fread(&header, 1, sizeof(ExtensionHeader), 286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch file.get()); 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (len < sizeof(ExtensionHeader)) { 28821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Invalid crx header 28972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 29072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CRX_HEADER_INVALID, 29172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 29272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 29372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_HEADER_INVALID"))); 294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (strncmp(kExtensionHeaderMagic, header.magic, 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch sizeof(header.magic))) { 29821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Bad magic number 29972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 30072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CRX_MAGIC_NUMBER_INVALID, 30172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 30272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 30372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_MAGIC_NUMBER_INVALID"))); 304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (header.version != kCurrentVersion) { 30721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Bad version numer 30872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure(CRX_VERSION_NUMBER_INVALID, 30972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 31072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 31172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_VERSION_NUMBER_INVALID"))); 312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (header.key_size > kMaxPublicKeySize || 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch header.signature_size > kMaxSignatureSize) { 31621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Excessively large key or signature 31772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 31872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE, 31972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 32072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 32172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE"))); 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 3243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (header.key_size == 0) { 32521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Key length is zero 32672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 32772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CRX_ZERO_KEY_LENGTH, 32872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 32972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 33072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_ZERO_KEY_LENGTH"))); 3313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 3323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 3334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch if (header.signature_size == 0) { 33421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Signature length is zero 33572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 33672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CRX_ZERO_SIGNATURE_LENGTH, 33772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 33872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 33972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_ZERO_SIGNATURE_LENGTH"))); 3404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch return false; 3414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch } 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::vector<uint8> key; 344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch key.resize(header.key_size); 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch len = fread(&key.front(), sizeof(uint8), header.key_size, file.get()); 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (len < header.key_size) { 34721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Invalid public key 34872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 34972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CRX_PUBLIC_KEY_INVALID, 35072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 35172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 35272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_PUBLIC_KEY_INVALID"))); 353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::vector<uint8> signature; 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch signature.resize(header.signature_size); 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch len = fread(&signature.front(), sizeof(uint8), header.signature_size, 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch file.get()); 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (len < header.signature_size) { 36121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Invalid signature 36272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 36372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CRX_SIGNATURE_INVALID, 36472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 36572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 36672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_SIGNATURE_INVALID"))); 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 370ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen crypto::SignatureVerifier verifier; 3713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!verifier.VerifyInit(extension_misc::kSignatureAlgorithm, 3723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick sizeof(extension_misc::kSignatureAlgorithm), 373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &signature.front(), 374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch signature.size(), 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &key.front(), 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch key.size())) { 37721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Signature verification initialization failed. This is most likely 37821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // caused by a public key in the wrong format (should encode algorithm). 37972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 38072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED, 38172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 38272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 38372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED"))); 384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch unsigned char buf[1 << 12]; 388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0) 389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch verifier.VerifyUpdate(buf, len); 390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!verifier.VerifyFinal()) { 39221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Signature verification failed 39372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 39472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen CRX_SIGNATURE_VERIFICATION_FAILED, 39572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 39672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_ERROR_CODE, 39772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_FAILED"))); 398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch base::Base64Encode(std::string(reinterpret_cast<char*>(&key.front()), 402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch key.size()), &public_key_); 403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 40672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid SandboxedExtensionUnpacker::ReportFailure(FailureReason reason, 40772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const std::string& error) { 40872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackFailure", 1); 40972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen UMA_HISTOGRAM_ENUMERATION("Extensions.SandboxUnpackFailureReason", 41072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen reason, NUM_FAILURE_REASONS); 41172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch client_->OnUnpackFailure(error); 413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid SandboxedExtensionUnpacker::ReportSuccess() { 41672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1); 41772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Client takes ownership of temporary directory and extension. 419513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_, extension_); 420513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch extension_ = NULL; 421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 423c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochDictionaryValue* SandboxedExtensionUnpacker::RewriteManifestFile( 424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const DictionaryValue& manifest) { 425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Add the public key extracted earlier to the parsed manifest and overwrite 426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the original manifest. We do this to ensure the manifest doesn't contain an 427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // exploitable bug that could be used to compromise the browser. 42872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen scoped_ptr<DictionaryValue> final_manifest(manifest.DeepCopy()); 429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch final_manifest->SetString(extension_manifest_keys::kPublicKey, public_key_); 430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string manifest_json; 432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch JSONStringValueSerializer serializer(&manifest_json); 433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch serializer.set_pretty_print(true); 434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!serializer.Serialize(*final_manifest)) { 43521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Error serializing manifest.json. 43672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 43772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ERROR_SERIALIZING_MANIFEST_JSON, 43872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 43972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 44072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("ERROR_SERIALIZING_MANIFEST_JSON"))); 441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath manifest_path = 445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch extension_root_.Append(Extension::kManifestFilename); 446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!file_util::WriteFile(manifest_path, 447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch manifest_json.data(), manifest_json.size())) { 44821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Error saving manifest.json. 44972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 45072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ERROR_SAVING_MANIFEST_JSON, 45172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 45272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 45372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON"))); 454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return final_manifest.release(); 458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool SandboxedExtensionUnpacker::RewriteImageFiles() { 461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExtensionUnpacker::DecodedImages images; 462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!ExtensionUnpacker::ReadImagesFromFile(temp_dir_.path(), &images)) { 46321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Couldn't read image data from disk. 46472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 46572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen COULD_NOT_READ_IMAGE_DATA_FROM_DISK, 46672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 46772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 46872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("COULD_NOT_READ_IMAGE_DATA_FROM_DISK"))); 469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Delete any images that may be used by the browser. We're going to write 473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // out our own versions of the parsed images, and we want to make sure the 474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // originals are gone for good. 475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::set<FilePath> image_paths = extension_->GetBrowserImages(); 476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (image_paths.size() != images.size()) { 47721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Decoded images don't match what's in the manifest. 47872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 47972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST, 48072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 48172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 48272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST"))); 483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (std::set<FilePath>::iterator it = image_paths.begin(); 487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch it != image_paths.end(); ++it) { 488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath path = *it; 489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (path.IsAbsolute() || path.ReferencesParent()) { 49021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Invalid path for browser image. 49172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 49272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen INVALID_PATH_FOR_BROWSER_IMAGE, 49372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 49472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 49572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE"))); 496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!file_util::Delete(extension_root_.Append(path), false)) { 49921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Error removing old image file. 50072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 50172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ERROR_REMOVING_OLD_IMAGE_FILE, 50272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 50372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 50472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE"))); 505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Write our parsed images back to disk as well. 510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < images.size(); ++i) { 511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const SkBitmap& image = images[i].a; 512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath path_suffix = images[i].b; 513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) { 51421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Invalid path for bitmap image. 51572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 51672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen INVALID_PATH_FOR_BITMAP_IMAGE, 51772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 51872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 51972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE"))); 520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath path = extension_root_.Append(path_suffix); 523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::vector<unsigned char> image_data; 525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even 526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // though they may originally be .jpg, etc. Figure something out. 527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // http://code.google.com/p/chromium/issues/detail?id=12459 528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) { 52921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Error re-encoding theme image. 53072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 53172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ERROR_RE_ENCODING_THEME_IMAGE, 53272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 53372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 53472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE"))); 535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Note: we're overwriting existing files that the utility process wrote, 539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // so we can be sure the directory exists. 540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]); 541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) { 54221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Error saving theme image. 54372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 54472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ERROR_SAVING_THEME_IMAGE, 54572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 54672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 54772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE"))); 548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool SandboxedExtensionUnpacker::RewriteCatalogFiles() { 556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DictionaryValue catalogs; 557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!ExtensionUnpacker::ReadMessageCatalogsFromFile(temp_dir_.path(), 558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &catalogs)) { 55921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Could not read catalog data from disk. 56072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 56172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen COULD_NOT_READ_CATALOG_DATA_FROM_DISK, 56272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 56372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 56472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("COULD_NOT_READ_CATALOG_DATA_FROM_DISK"))); 565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Write our parsed catalogs back to disk. 569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (DictionaryValue::key_iterator key_it = catalogs.begin_keys(); 570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch key_it != catalogs.end_keys(); ++key_it) { 571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DictionaryValue* catalog; 572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!catalogs.GetDictionaryWithoutPathExpansion(*key_it, &catalog)) { 57321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Invalid catalog data. 57472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 57572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen INVALID_CATALOG_DATA, 57672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 57772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 57872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("INVALID_CATALOG_DATA"))); 579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // TODO(viettrungluu): Fix the |FilePath::FromWStringHack(UTF8ToWide())| 5833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // hack and remove the corresponding #include. 5843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick FilePath relative_path = FilePath::FromWStringHack(UTF8ToWide(*key_it)); 585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch relative_path = relative_path.Append(Extension::kMessagesFilename); 586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (relative_path.IsAbsolute() || relative_path.ReferencesParent()) { 58721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Invalid path for catalog. 58872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 58972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen INVALID_PATH_FOR_CATALOG, 59072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 59172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 59272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("INVALID_PATH_FOR_CATALOG"))); 593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath path = extension_root_.Append(relative_path); 596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string catalog_json; 598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch JSONStringValueSerializer serializer(&catalog_json); 599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch serializer.set_pretty_print(true); 600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!serializer.Serialize(*catalog)) { 60121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Error serializing catalog. 60272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 60372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ERROR_SERIALIZING_CATALOG, 60472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 60572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 60672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("ERROR_SERIALIZING_CATALOG"))); 607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Note: we're overwriting existing files that the utility process read, 611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // so we can be sure the directory exists. 612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!file_util::WriteFile(path, 613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch catalog_json.c_str(), 614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch catalog_json.size())) { 61521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Error saving catalog. 61672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ReportFailure( 61772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ERROR_SAVING_CATALOG, 61872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen l10n_util::GetStringFUTF8( 61972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen IDS_EXTENSION_PACKAGE_INSTALL_ERROR, 62072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ASCIIToUTF16("ERROR_SAVING_CATALOG"))); 621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 627