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