Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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/browser/extensions/sandboxed_extension_unpacker.h"
      6 
      7 #include <set>
      8 
      9 #include "base/base64.h"
     10 #include "base/file_util.h"
     11 #include "base/file_util_proxy.h"
     12 #include "base/memory/scoped_handle.h"
     13 #include "base/message_loop.h"
     14 #include "base/metrics/histogram.h"
     15 #include "base/path_service.h"
     16 #include "base/task.h"
     17 #include "base/utf_string_conversions.h"  // TODO(viettrungluu): delete me.
     18 #include "crypto/signature_verifier.h"
     19 #include "chrome/browser/extensions/extension_service.h"
     20 #include "chrome/common/chrome_paths.h"
     21 #include "chrome/common/chrome_switches.h"
     22 #include "chrome/common/extensions/extension.h"
     23 #include "chrome/common/extensions/extension_constants.h"
     24 #include "chrome/common/extensions/extension_file_util.h"
     25 #include "chrome/common/extensions/extension_l10n_util.h"
     26 #include "chrome/common/extensions/extension_unpacker.h"
     27 #include "content/browser/browser_thread.h"
     28 #include "content/browser/renderer_host/resource_dispatcher_host.h"
     29 #include "content/common/json_value_serializer.h"
     30 #include "grit/generated_resources.h"
     31 #include "third_party/skia/include/core/SkBitmap.h"
     32 #include "ui/base/l10n/l10n_util.h"
     33 #include "ui/gfx/codec/png_codec.h"
     34 
     35 // The following macro makes histograms that record the length of paths
     36 // in this file much easier to read.
     37 // Windows has a short max path length. If the path length to a
     38 // file being unpacked from a CRX exceeds the max length, we might
     39 // fail to install. To see if this is happening, see how long the
     40 // path to the temp unpack directory is. See crbug.com/69693 .
     41 #define PATH_LENGTH_HISTOGRAM(name, path) \
     42     UMA_HISTOGRAM_CUSTOM_COUNTS(name, path.value().length(), 0, 500, 100)
     43 
     44 const char SandboxedExtensionUnpacker::kExtensionHeaderMagic[] = "Cr24";
     45 
     46 SandboxedExtensionUnpacker::SandboxedExtensionUnpacker(
     47     const FilePath& crx_path,
     48     ResourceDispatcherHost* rdh,
     49     SandboxedExtensionUnpackerClient* client)
     50     : crx_path_(crx_path),
     51       thread_identifier_(BrowserThread::ID_COUNT),
     52       rdh_(rdh), client_(client), got_response_(false) {
     53 }
     54 
     55 bool SandboxedExtensionUnpacker::CreateTempDirectory() {
     56   CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_identifier_));
     57 
     58   FilePath user_data_temp_dir = extension_file_util::GetUserDataTempDir();
     59   if (user_data_temp_dir.empty()) {
     60     ReportFailure(
     61         COULD_NOT_GET_TEMP_DIRECTORY,
     62         l10n_util::GetStringFUTF8(
     63             IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
     64             ASCIIToUTF16("COULD_NOT_GET_TEMP_DIRECTORY")));
     65     return false;
     66   }
     67 
     68   if (!temp_dir_.CreateUniqueTempDirUnderPath(user_data_temp_dir)) {
     69     ReportFailure(
     70         COULD_NOT_CREATE_TEMP_DIRECTORY,
     71         l10n_util::GetStringFUTF8(
     72             IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
     73             ASCIIToUTF16("COULD_NOT_CREATE_TEMP_DIRECTORY")));
     74     return false;
     75   }
     76 
     77   return true;
     78 }
     79 
     80 void SandboxedExtensionUnpacker::Start() {
     81   // We assume that we are started on the thread that the client wants us to do
     82   // file IO on.
     83   CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_identifier_));
     84 
     85   PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackInitialCrxPathLength",
     86                         crx_path_);
     87   if (!CreateTempDirectory())
     88     return;  // ReportFailure() already called.
     89 
     90   // Initialize the path that will eventually contain the unpacked extension.
     91   extension_root_ = temp_dir_.path().AppendASCII(
     92       extension_filenames::kTempExtensionName);
     93   PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackUnpackedCrxPathLength",
     94                         extension_root_);
     95 
     96   // Extract the public key and validate the package.
     97   if (!ValidateSignature())
     98     return;  // ValidateSignature() already reported the error.
     99 
    100   // Copy the crx file into our working directory.
    101   FilePath temp_crx_path = temp_dir_.path().Append(crx_path_.BaseName());
    102   PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackTempCrxPathLength",
    103                         temp_crx_path);
    104 
    105   if (!file_util::CopyFile(crx_path_, temp_crx_path)) {
    106     // Failed to copy extension file to temporary directory.
    107     ReportFailure(
    108         FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY,
    109         l10n_util::GetStringFUTF8(
    110             IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    111             ASCIIToUTF16("FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY")));
    112     return;
    113   }
    114 
    115   // If we are supposed to use a subprocess, kick off the subprocess.
    116   //
    117   // TODO(asargent) we shouldn't need to do this branch here - instead
    118   // UtilityProcessHost should handle it for us. (http://crbug.com/19192)
    119   bool use_utility_process = rdh_ &&
    120       !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
    121   if (use_utility_process) {
    122     // The utility process will have access to the directory passed to
    123     // SandboxedExtensionUnpacker.  That directory should not contain a
    124     // symlink or NTFS reparse point.  When the path is used, following
    125     // the link/reparse point will cause file system access outside the
    126     // sandbox path, and the sandbox will deny the operation.
    127     FilePath link_free_crx_path;
    128     if (!file_util::NormalizeFilePath(temp_crx_path, &link_free_crx_path)) {
    129       LOG(ERROR) << "Could not get the normalized path of "
    130                  << temp_crx_path.value();
    131       ReportFailure(
    132           COULD_NOT_GET_SANDBOX_FRIENDLY_PATH,
    133           l10n_util::GetStringUTF8(IDS_EXTENSION_UNPACK_FAILED));
    134       return;
    135     }
    136     PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength",
    137                           link_free_crx_path);
    138 
    139     BrowserThread::PostTask(
    140         BrowserThread::IO, FROM_HERE,
    141         NewRunnableMethod(
    142             this,
    143             &SandboxedExtensionUnpacker::StartProcessOnIOThread,
    144             link_free_crx_path));
    145   } else {
    146     // Otherwise, unpack the extension in this process.
    147     ExtensionUnpacker unpacker(temp_crx_path);
    148     if (unpacker.Run() && unpacker.DumpImagesToFile() &&
    149         unpacker.DumpMessageCatalogsToFile()) {
    150       OnUnpackExtensionSucceeded(*unpacker.parsed_manifest());
    151     } else {
    152       OnUnpackExtensionFailed(unpacker.error_message());
    153     }
    154   }
    155 }
    156 
    157 SandboxedExtensionUnpacker::~SandboxedExtensionUnpacker() {
    158   base::FileUtilProxy::Delete(
    159       BrowserThread::GetMessageLoopProxyForThread(thread_identifier_),
    160       temp_dir_.Take(),
    161       true,
    162       NULL);
    163 }
    164 
    165 void SandboxedExtensionUnpacker::StartProcessOnIOThread(
    166     const FilePath& temp_crx_path) {
    167   UtilityProcessHost* host = new UtilityProcessHost(this, thread_identifier_);
    168   host->StartExtensionUnpacker(temp_crx_path);
    169 }
    170 
    171 void SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded(
    172     const DictionaryValue& manifest) {
    173   // Skip check for unittests.
    174   if (thread_identifier_ != BrowserThread::ID_COUNT)
    175     CHECK(BrowserThread::CurrentlyOn(thread_identifier_));
    176   got_response_ = true;
    177 
    178   scoped_ptr<DictionaryValue> final_manifest(RewriteManifestFile(manifest));
    179   if (!final_manifest.get())
    180     return;
    181 
    182   // Create an extension object that refers to the temporary location the
    183   // extension was unpacked to. We use this until the extension is finally
    184   // installed. For example, the install UI shows images from inside the
    185   // extension.
    186 
    187   // Localize manifest now, so confirm UI gets correct extension name.
    188   std::string error;
    189   if (!extension_l10n_util::LocalizeExtension(extension_root_,
    190                                               final_manifest.get(),
    191                                               &error)) {
    192     ReportFailure(
    193         COULD_NOT_LOCALIZE_EXTENSION,
    194         l10n_util::GetStringFUTF8(
    195             IDS_EXTENSION_PACKAGE_ERROR_MESSAGE,
    196             ASCIIToUTF16(error)));
    197     return;
    198   }
    199 
    200   extension_ = Extension::Create(
    201       extension_root_,
    202       Extension::INTERNAL,
    203       *final_manifest,
    204       Extension::REQUIRE_KEY,
    205       &error);
    206 
    207   if (!extension_.get()) {
    208     ReportFailure(
    209         INVALID_MANIFEST,
    210         std::string("Manifest is invalid: ") + error);
    211     return;
    212   }
    213 
    214   if (!RewriteImageFiles())
    215     return;
    216 
    217   if (!RewriteCatalogFiles())
    218     return;
    219 
    220   ReportSuccess();
    221 }
    222 
    223 void SandboxedExtensionUnpacker::OnUnpackExtensionFailed(
    224     const std::string& error) {
    225   CHECK(BrowserThread::CurrentlyOn(thread_identifier_));
    226   got_response_ = true;
    227   ReportFailure(
    228       UNPACKER_CLIENT_FAILED,
    229       l10n_util::GetStringFUTF8(
    230            IDS_EXTENSION_PACKAGE_ERROR_MESSAGE,
    231            ASCIIToUTF16(error)));
    232 }
    233 
    234 void SandboxedExtensionUnpacker::OnProcessCrashed(int exit_code) {
    235   // Don't report crashes if they happen after we got a response.
    236   if (got_response_)
    237     return;
    238 
    239   // Utility process crashed while trying to install.
    240   ReportFailure(
    241      UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL,
    242      l10n_util::GetStringFUTF8(
    243          IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    244          ASCIIToUTF16("UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL")));
    245 }
    246 
    247 bool SandboxedExtensionUnpacker::ValidateSignature() {
    248   ScopedStdioHandle file(file_util::OpenFile(crx_path_, "rb"));
    249 
    250   if (!file.get()) {
    251     // Could not open crx file for reading.
    252 #if defined (OS_WIN)
    253     // On windows, get the error code.
    254     uint32 error_code = ::GetLastError();
    255     // TODO(skerner): Use this histogram to understand why so many
    256     // windows users hit this error.  crbug.com/69693
    257 
    258     // Windows errors are unit32s, but all of likely errors are in
    259     // [1, 1000].  See winerror.h for the meaning of specific values.
    260     // Clip errors outside the expected range to a single extra value.
    261     // If there are errors in that extra bucket, we will know to expand
    262     // the range.
    263     const uint32 kMaxErrorToSend = 1001;
    264     error_code = std::min(error_code, kMaxErrorToSend);
    265     UMA_HISTOGRAM_ENUMERATION("Extensions.ErrorCodeFromCrxOpen",
    266                               error_code, kMaxErrorToSend);
    267 #endif
    268 
    269     ReportFailure(
    270         CRX_FILE_NOT_READABLE,
    271         l10n_util::GetStringFUTF8(
    272             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    273             ASCIIToUTF16("CRX_FILE_NOT_READABLE")));
    274     return false;
    275   }
    276 
    277   // Read and verify the header.
    278   ExtensionHeader header;
    279   size_t len;
    280 
    281   // TODO(erikkay): Yuck.  I'm not a big fan of this kind of code, but it
    282   // appears that we don't have any endian/alignment aware serialization
    283   // code in the code base.  So for now, this assumes that we're running
    284   // on a little endian machine with 4 byte alignment.
    285   len = fread(&header, 1, sizeof(ExtensionHeader),
    286       file.get());
    287   if (len < sizeof(ExtensionHeader)) {
    288     // Invalid crx header
    289     ReportFailure(
    290         CRX_HEADER_INVALID,
    291         l10n_util::GetStringFUTF8(
    292             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    293             ASCIIToUTF16("CRX_HEADER_INVALID")));
    294     return false;
    295   }
    296   if (strncmp(kExtensionHeaderMagic, header.magic,
    297       sizeof(header.magic))) {
    298     // Bad magic number
    299     ReportFailure(
    300         CRX_MAGIC_NUMBER_INVALID,
    301         l10n_util::GetStringFUTF8(
    302             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    303             ASCIIToUTF16("CRX_MAGIC_NUMBER_INVALID")));
    304     return false;
    305   }
    306   if (header.version != kCurrentVersion) {
    307     // Bad version numer
    308     ReportFailure(CRX_VERSION_NUMBER_INVALID,
    309         l10n_util::GetStringFUTF8(
    310             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    311             ASCIIToUTF16("CRX_VERSION_NUMBER_INVALID")));
    312     return false;
    313   }
    314   if (header.key_size > kMaxPublicKeySize ||
    315       header.signature_size > kMaxSignatureSize) {
    316     // Excessively large key or signature
    317     ReportFailure(
    318         CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE,
    319         l10n_util::GetStringFUTF8(
    320             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    321             ASCIIToUTF16("CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE")));
    322     return false;
    323   }
    324   if (header.key_size == 0) {
    325     // Key length is zero
    326     ReportFailure(
    327         CRX_ZERO_KEY_LENGTH,
    328         l10n_util::GetStringFUTF8(
    329             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    330             ASCIIToUTF16("CRX_ZERO_KEY_LENGTH")));
    331     return false;
    332   }
    333   if (header.signature_size == 0) {
    334     // Signature length is zero
    335     ReportFailure(
    336         CRX_ZERO_SIGNATURE_LENGTH,
    337         l10n_util::GetStringFUTF8(
    338             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    339             ASCIIToUTF16("CRX_ZERO_SIGNATURE_LENGTH")));
    340     return false;
    341   }
    342 
    343   std::vector<uint8> key;
    344   key.resize(header.key_size);
    345   len = fread(&key.front(), sizeof(uint8), header.key_size, file.get());
    346   if (len < header.key_size) {
    347     // Invalid public key
    348     ReportFailure(
    349         CRX_PUBLIC_KEY_INVALID,
    350         l10n_util::GetStringFUTF8(
    351             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    352             ASCIIToUTF16("CRX_PUBLIC_KEY_INVALID")));
    353     return false;
    354   }
    355 
    356   std::vector<uint8> signature;
    357   signature.resize(header.signature_size);
    358   len = fread(&signature.front(), sizeof(uint8), header.signature_size,
    359       file.get());
    360   if (len < header.signature_size) {
    361     // Invalid signature
    362     ReportFailure(
    363         CRX_SIGNATURE_INVALID,
    364         l10n_util::GetStringFUTF8(
    365             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    366             ASCIIToUTF16("CRX_SIGNATURE_INVALID")));
    367     return false;
    368   }
    369 
    370   crypto::SignatureVerifier verifier;
    371   if (!verifier.VerifyInit(extension_misc::kSignatureAlgorithm,
    372                            sizeof(extension_misc::kSignatureAlgorithm),
    373                            &signature.front(),
    374                            signature.size(),
    375                            &key.front(),
    376                            key.size())) {
    377     // Signature verification initialization failed. This is most likely
    378     // caused by a public key in the wrong format (should encode algorithm).
    379     ReportFailure(
    380         CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED,
    381         l10n_util::GetStringFUTF8(
    382             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    383             ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED")));
    384     return false;
    385   }
    386 
    387   unsigned char buf[1 << 12];
    388   while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0)
    389     verifier.VerifyUpdate(buf, len);
    390 
    391   if (!verifier.VerifyFinal()) {
    392     // Signature verification failed
    393     ReportFailure(
    394         CRX_SIGNATURE_VERIFICATION_FAILED,
    395         l10n_util::GetStringFUTF8(
    396             IDS_EXTENSION_PACKAGE_ERROR_CODE,
    397             ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_FAILED")));
    398     return false;
    399   }
    400 
    401   base::Base64Encode(std::string(reinterpret_cast<char*>(&key.front()),
    402       key.size()), &public_key_);
    403   return true;
    404 }
    405 
    406 void SandboxedExtensionUnpacker::ReportFailure(FailureReason reason,
    407                                                const std::string& error) {
    408   UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackFailure", 1);
    409   UMA_HISTOGRAM_ENUMERATION("Extensions.SandboxUnpackFailureReason",
    410                             reason, NUM_FAILURE_REASONS);
    411 
    412   client_->OnUnpackFailure(error);
    413 }
    414 
    415 void SandboxedExtensionUnpacker::ReportSuccess() {
    416   UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1);
    417 
    418   // Client takes ownership of temporary directory and extension.
    419   client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_, extension_);
    420   extension_ = NULL;
    421 }
    422 
    423 DictionaryValue* SandboxedExtensionUnpacker::RewriteManifestFile(
    424     const DictionaryValue& manifest) {
    425   // Add the public key extracted earlier to the parsed manifest and overwrite
    426   // the original manifest. We do this to ensure the manifest doesn't contain an
    427   // exploitable bug that could be used to compromise the browser.
    428   scoped_ptr<DictionaryValue> final_manifest(manifest.DeepCopy());
    429   final_manifest->SetString(extension_manifest_keys::kPublicKey, public_key_);
    430 
    431   std::string manifest_json;
    432   JSONStringValueSerializer serializer(&manifest_json);
    433   serializer.set_pretty_print(true);
    434   if (!serializer.Serialize(*final_manifest)) {
    435     // Error serializing manifest.json.
    436     ReportFailure(
    437         ERROR_SERIALIZING_MANIFEST_JSON,
    438         l10n_util::GetStringFUTF8(
    439             IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    440             ASCIIToUTF16("ERROR_SERIALIZING_MANIFEST_JSON")));
    441     return NULL;
    442   }
    443 
    444   FilePath manifest_path =
    445       extension_root_.Append(Extension::kManifestFilename);
    446   if (!file_util::WriteFile(manifest_path,
    447                             manifest_json.data(), manifest_json.size())) {
    448     // Error saving manifest.json.
    449     ReportFailure(
    450         ERROR_SAVING_MANIFEST_JSON,
    451         l10n_util::GetStringFUTF8(
    452             IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    453             ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON")));
    454     return NULL;
    455   }
    456 
    457   return final_manifest.release();
    458 }
    459 
    460 bool SandboxedExtensionUnpacker::RewriteImageFiles() {
    461   ExtensionUnpacker::DecodedImages images;
    462   if (!ExtensionUnpacker::ReadImagesFromFile(temp_dir_.path(), &images)) {
    463     // Couldn't read image data from disk.
    464     ReportFailure(
    465         COULD_NOT_READ_IMAGE_DATA_FROM_DISK,
    466         l10n_util::GetStringFUTF8(
    467             IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    468             ASCIIToUTF16("COULD_NOT_READ_IMAGE_DATA_FROM_DISK")));
    469     return false;
    470   }
    471 
    472   // Delete any images that may be used by the browser.  We're going to write
    473   // out our own versions of the parsed images, and we want to make sure the
    474   // originals are gone for good.
    475   std::set<FilePath> image_paths = extension_->GetBrowserImages();
    476   if (image_paths.size() != images.size()) {
    477     // Decoded images don't match what's in the manifest.
    478     ReportFailure(
    479         DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST,
    480         l10n_util::GetStringFUTF8(
    481             IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    482             ASCIIToUTF16("DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST")));
    483     return false;
    484   }
    485 
    486   for (std::set<FilePath>::iterator it = image_paths.begin();
    487        it != image_paths.end(); ++it) {
    488     FilePath path = *it;
    489     if (path.IsAbsolute() || path.ReferencesParent()) {
    490       // Invalid path for browser image.
    491       ReportFailure(
    492           INVALID_PATH_FOR_BROWSER_IMAGE,
    493           l10n_util::GetStringFUTF8(
    494               IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    495               ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE")));
    496       return false;
    497     }
    498     if (!file_util::Delete(extension_root_.Append(path), false)) {
    499       // Error removing old image file.
    500       ReportFailure(
    501           ERROR_REMOVING_OLD_IMAGE_FILE,
    502           l10n_util::GetStringFUTF8(
    503               IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    504               ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE")));
    505       return false;
    506     }
    507   }
    508 
    509   // Write our parsed images back to disk as well.
    510   for (size_t i = 0; i < images.size(); ++i) {
    511     const SkBitmap& image = images[i].a;
    512     FilePath path_suffix = images[i].b;
    513     if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) {
    514       // Invalid path for bitmap image.
    515       ReportFailure(
    516           INVALID_PATH_FOR_BITMAP_IMAGE,
    517           l10n_util::GetStringFUTF8(
    518               IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    519               ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE")));
    520       return false;
    521     }
    522     FilePath path = extension_root_.Append(path_suffix);
    523 
    524     std::vector<unsigned char> image_data;
    525     // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
    526     // though they may originally be .jpg, etc.  Figure something out.
    527     // http://code.google.com/p/chromium/issues/detail?id=12459
    528     if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) {
    529       // Error re-encoding theme image.
    530       ReportFailure(
    531           ERROR_RE_ENCODING_THEME_IMAGE,
    532           l10n_util::GetStringFUTF8(
    533               IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    534               ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE")));
    535       return false;
    536     }
    537 
    538     // Note: we're overwriting existing files that the utility process wrote,
    539     // so we can be sure the directory exists.
    540     const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
    541     if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) {
    542       // Error saving theme image.
    543       ReportFailure(
    544           ERROR_SAVING_THEME_IMAGE,
    545           l10n_util::GetStringFUTF8(
    546               IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    547               ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE")));
    548       return false;
    549     }
    550   }
    551 
    552   return true;
    553 }
    554 
    555 bool SandboxedExtensionUnpacker::RewriteCatalogFiles() {
    556   DictionaryValue catalogs;
    557   if (!ExtensionUnpacker::ReadMessageCatalogsFromFile(temp_dir_.path(),
    558                                                       &catalogs)) {
    559     // Could not read catalog data from disk.
    560     ReportFailure(
    561         COULD_NOT_READ_CATALOG_DATA_FROM_DISK,
    562         l10n_util::GetStringFUTF8(
    563             IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    564             ASCIIToUTF16("COULD_NOT_READ_CATALOG_DATA_FROM_DISK")));
    565     return false;
    566   }
    567 
    568   // Write our parsed catalogs back to disk.
    569   for (DictionaryValue::key_iterator key_it = catalogs.begin_keys();
    570        key_it != catalogs.end_keys(); ++key_it) {
    571     DictionaryValue* catalog;
    572     if (!catalogs.GetDictionaryWithoutPathExpansion(*key_it, &catalog)) {
    573       // Invalid catalog data.
    574       ReportFailure(
    575           INVALID_CATALOG_DATA,
    576           l10n_util::GetStringFUTF8(
    577               IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    578               ASCIIToUTF16("INVALID_CATALOG_DATA")));
    579       return false;
    580     }
    581 
    582     // TODO(viettrungluu): Fix the |FilePath::FromWStringHack(UTF8ToWide())|
    583     // hack and remove the corresponding #include.
    584     FilePath relative_path = FilePath::FromWStringHack(UTF8ToWide(*key_it));
    585     relative_path = relative_path.Append(Extension::kMessagesFilename);
    586     if (relative_path.IsAbsolute() || relative_path.ReferencesParent()) {
    587       // Invalid path for catalog.
    588       ReportFailure(
    589           INVALID_PATH_FOR_CATALOG,
    590           l10n_util::GetStringFUTF8(
    591               IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    592               ASCIIToUTF16("INVALID_PATH_FOR_CATALOG")));
    593       return false;
    594     }
    595     FilePath path = extension_root_.Append(relative_path);
    596 
    597     std::string catalog_json;
    598     JSONStringValueSerializer serializer(&catalog_json);
    599     serializer.set_pretty_print(true);
    600     if (!serializer.Serialize(*catalog)) {
    601       // Error serializing catalog.
    602       ReportFailure(
    603           ERROR_SERIALIZING_CATALOG,
    604           l10n_util::GetStringFUTF8(
    605               IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    606               ASCIIToUTF16("ERROR_SERIALIZING_CATALOG")));
    607       return false;
    608     }
    609 
    610     // Note: we're overwriting existing files that the utility process read,
    611     // so we can be sure the directory exists.
    612     if (!file_util::WriteFile(path,
    613                               catalog_json.c_str(),
    614                               catalog_json.size())) {
    615       // Error saving catalog.
    616       ReportFailure(
    617           ERROR_SAVING_CATALOG,
    618           l10n_util::GetStringFUTF8(
    619               IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
    620               ASCIIToUTF16("ERROR_SAVING_CATALOG")));
    621       return false;
    622     }
    623   }
    624 
    625   return true;
    626 }
    627