Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / extensions / browser / sandboxed_unpacker.cc
blob965de488c5fdf47a18e1c9dc0005563254883d34
1 // Copyright (c) 2012 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.
5 #include "extensions/browser/sandboxed_unpacker.h"
7 #include <set>
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/files/file_util.h"
12 #include "base/json/json_string_value_serializer.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/path_service.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/sequenced_worker_pool.h"
21 #include "components/crx_file/crx_file.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/utility_process_host.h"
24 #include "content/public/common/common_param_traits.h"
25 #include "extensions/common/constants.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/extension_l10n_util.h"
28 #include "extensions/common/extension_utility_messages.h"
29 #include "extensions/common/extensions_client.h"
30 #include "extensions/common/file_util.h"
31 #include "extensions/common/manifest_constants.h"
32 #include "extensions/common/manifest_handlers/icons_handler.h"
33 #include "extensions/common/switches.h"
34 #include "grit/extensions_strings.h"
35 #include "third_party/skia/include/core/SkBitmap.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/gfx/codec/png_codec.h"
39 using base::ASCIIToUTF16;
40 using content::BrowserThread;
41 using content::UtilityProcessHost;
42 using crx_file::CrxFile;
44 // The following macro makes histograms that record the length of paths
45 // in this file much easier to read.
46 // Windows has a short max path length. If the path length to a
47 // file being unpacked from a CRX exceeds the max length, we might
48 // fail to install. To see if this is happening, see how long the
49 // path to the temp unpack directory is. See crbug.com/69693 .
50 #define PATH_LENGTH_HISTOGRAM(name, path) \
51 UMA_HISTOGRAM_CUSTOM_COUNTS(name, path.value().length(), 0, 500, 100)
53 // Record a rate (kB per second) at which extensions are unpacked.
54 // Range from 1kB/s to 100mB/s.
55 #define UNPACK_RATE_HISTOGRAM(name, rate) \
56 UMA_HISTOGRAM_CUSTOM_COUNTS(name, rate, 1, 100000, 100);
58 namespace extensions {
59 namespace {
61 void RecordSuccessfulUnpackTimeHistograms(const base::FilePath& crx_path,
62 const base::TimeDelta unpack_time) {
63 const int64 kBytesPerKb = 1024;
64 const int64 kBytesPerMb = 1024 * 1024;
66 UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackSuccessTime", unpack_time);
68 // To get a sense of how CRX size impacts unpack time, record unpack
69 // time for several increments of CRX size.
70 int64 crx_file_size;
71 if (!base::GetFileSize(crx_path, &crx_file_size)) {
72 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccessCantGetCrxSize", 1);
73 return;
76 // Cast is safe as long as the number of bytes in the CRX is less than
77 // 2^31 * 2^10.
78 int crx_file_size_kb = static_cast<int>(crx_file_size / kBytesPerKb);
79 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccessCrxSize",
80 crx_file_size_kb);
82 // We have time in seconds and file size in bytes. We want the rate bytes are
83 // unpacked in kB/s.
84 double file_size_kb =
85 static_cast<double>(crx_file_size) / static_cast<double>(kBytesPerKb);
86 int unpack_rate_kb_per_s =
87 static_cast<int>(file_size_kb / unpack_time.InSecondsF());
88 UNPACK_RATE_HISTOGRAM("Extensions.SandboxUnpackRate", unpack_rate_kb_per_s);
90 if (crx_file_size < 50.0 * kBytesPerKb) {
91 UNPACK_RATE_HISTOGRAM("Extensions.SandboxUnpackRateUnder50kB",
92 unpack_rate_kb_per_s);
94 } else if (crx_file_size < 1 * kBytesPerMb) {
95 UNPACK_RATE_HISTOGRAM("Extensions.SandboxUnpackRate50kBTo1mB",
96 unpack_rate_kb_per_s);
98 } else if (crx_file_size < 2 * kBytesPerMb) {
99 UNPACK_RATE_HISTOGRAM("Extensions.SandboxUnpackRate1To2mB",
100 unpack_rate_kb_per_s);
102 } else if (crx_file_size < 5 * kBytesPerMb) {
103 UNPACK_RATE_HISTOGRAM("Extensions.SandboxUnpackRate2To5mB",
104 unpack_rate_kb_per_s);
106 } else if (crx_file_size < 10 * kBytesPerMb) {
107 UNPACK_RATE_HISTOGRAM("Extensions.SandboxUnpackRate5To10mB",
108 unpack_rate_kb_per_s);
110 } else {
111 UNPACK_RATE_HISTOGRAM("Extensions.SandboxUnpackRateOver10mB",
112 unpack_rate_kb_per_s);
116 // Work horse for FindWritableTempLocation. Creates a temp file in the folder
117 // and uses NormalizeFilePath to check if the path is junction free.
118 bool VerifyJunctionFreeLocation(base::FilePath* temp_dir) {
119 if (temp_dir->empty())
120 return false;
122 base::FilePath temp_file;
123 if (!base::CreateTemporaryFileInDir(*temp_dir, &temp_file)) {
124 LOG(ERROR) << temp_dir->value() << " is not writable";
125 return false;
127 // NormalizeFilePath requires a non-empty file, so write some data.
128 // If you change the exit points of this function please make sure all
129 // exit points delete this temp file!
130 if (base::WriteFile(temp_file, ".", 1) != 1)
131 return false;
133 base::FilePath normalized_temp_file;
134 bool normalized = base::NormalizeFilePath(temp_file, &normalized_temp_file);
135 if (!normalized) {
136 // If |temp_file| contains a link, the sandbox will block al file system
137 // operations, and the install will fail.
138 LOG(ERROR) << temp_dir->value() << " seem to be on remote drive.";
139 } else {
140 *temp_dir = normalized_temp_file.DirName();
142 // Clean up the temp file.
143 base::DeleteFile(temp_file, false);
145 return normalized;
148 // This function tries to find a location for unpacking the extension archive
149 // that is writable and does not lie on a shared drive so that the sandboxed
150 // unpacking process can write there. If no such location exists we can not
151 // proceed and should fail.
152 // The result will be written to |temp_dir|. The function will write to this
153 // parameter even if it returns false.
154 bool FindWritableTempLocation(const base::FilePath& extensions_dir,
155 base::FilePath* temp_dir) {
156 // On ChromeOS, we will only attempt to unpack extension in cryptohome (profile)
157 // directory to provide additional security/privacy and speed up the rest of
158 // the extension install process.
159 #if !defined(OS_CHROMEOS)
160 PathService::Get(base::DIR_TEMP, temp_dir);
161 if (VerifyJunctionFreeLocation(temp_dir))
162 return true;
163 #endif
165 *temp_dir = file_util::GetInstallTempDir(extensions_dir);
166 if (VerifyJunctionFreeLocation(temp_dir))
167 return true;
168 // Neither paths is link free chances are good installation will fail.
169 LOG(ERROR) << "Both the %TEMP% folder and the profile seem to be on "
170 << "remote drives or read-only. Installation can not complete!";
171 return false;
174 // Read the decoded images back from the file we saved them to.
175 // |extension_path| is the path to the extension we unpacked that wrote the
176 // data. Returns true on success.
177 bool ReadImagesFromFile(const base::FilePath& extension_path,
178 DecodedImages* images) {
179 base::FilePath path = extension_path.AppendASCII(kDecodedImagesFilename);
180 std::string file_str;
181 if (!base::ReadFileToString(path, &file_str))
182 return false;
184 IPC::Message pickle(file_str.data(), file_str.size());
185 base::PickleIterator iter(pickle);
186 return IPC::ReadParam(&pickle, &iter, images);
189 // Read the decoded message catalogs back from the file we saved them to.
190 // |extension_path| is the path to the extension we unpacked that wrote the
191 // data. Returns true on success.
192 bool ReadMessageCatalogsFromFile(const base::FilePath& extension_path,
193 base::DictionaryValue* catalogs) {
194 base::FilePath path =
195 extension_path.AppendASCII(kDecodedMessageCatalogsFilename);
196 std::string file_str;
197 if (!base::ReadFileToString(path, &file_str))
198 return false;
200 IPC::Message pickle(file_str.data(), file_str.size());
201 base::PickleIterator iter(pickle);
202 return IPC::ReadParam(&pickle, &iter, catalogs);
205 } // namespace
207 SandboxedUnpackerClient::SandboxedUnpackerClient()
208 : RefCountedDeleteOnMessageLoop<SandboxedUnpackerClient>(
209 content::BrowserThread::GetMessageLoopProxyForThread(
210 content::BrowserThread::UI)) {
211 DCHECK_CURRENTLY_ON(BrowserThread::UI);
214 SandboxedUnpacker::SandboxedUnpacker(
215 Manifest::Location location,
216 int creation_flags,
217 const base::FilePath& extensions_dir,
218 const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner,
219 SandboxedUnpackerClient* client)
220 : client_(client),
221 extensions_dir_(extensions_dir),
222 got_response_(false),
223 location_(location),
224 creation_flags_(creation_flags),
225 unpacker_io_task_runner_(unpacker_io_task_runner),
226 utility_wrapper_(new UtilityHostWrapper) {}
228 bool SandboxedUnpacker::CreateTempDirectory() {
229 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
231 base::FilePath temp_dir;
232 if (!FindWritableTempLocation(extensions_dir_, &temp_dir)) {
233 ReportFailure(COULD_NOT_GET_TEMP_DIRECTORY,
234 l10n_util::GetStringFUTF16(
235 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
236 ASCIIToUTF16("COULD_NOT_GET_TEMP_DIRECTORY")));
237 return false;
240 if (!temp_dir_.CreateUniqueTempDirUnderPath(temp_dir)) {
241 ReportFailure(COULD_NOT_CREATE_TEMP_DIRECTORY,
242 l10n_util::GetStringFUTF16(
243 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
244 ASCIIToUTF16("COULD_NOT_CREATE_TEMP_DIRECTORY")));
245 return false;
248 return true;
251 void SandboxedUnpacker::StartWithCrx(const CRXFileInfo& crx_info) {
252 // We assume that we are started on the thread that the client wants us to do
253 // file IO on.
254 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
256 crx_unpack_start_time_ = base::TimeTicks::Now();
257 std::string expected_hash;
258 if (!crx_info.expected_hash.empty() &&
259 base::CommandLine::ForCurrentProcess()->HasSwitch(
260 extensions::switches::kEnableCrxHashCheck)) {
261 expected_hash = base::ToLowerASCII(crx_info.expected_hash);
264 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackInitialCrxPathLength",
265 crx_info.path);
266 if (!CreateTempDirectory())
267 return; // ReportFailure() already called.
269 // Initialize the path that will eventually contain the unpacked extension.
270 extension_root_ = temp_dir_.path().AppendASCII(kTempExtensionName);
271 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackUnpackedCrxPathLength",
272 extension_root_);
274 // Extract the public key and validate the package.
275 if (!ValidateSignature(crx_info.path, expected_hash))
276 return; // ValidateSignature() already reported the error.
278 // Copy the crx file into our working directory.
279 base::FilePath temp_crx_path =
280 temp_dir_.path().Append(crx_info.path.BaseName());
281 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackTempCrxPathLength",
282 temp_crx_path);
284 if (!base::CopyFile(crx_info.path, temp_crx_path)) {
285 // Failed to copy extension file to temporary directory.
286 ReportFailure(
287 FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY,
288 l10n_util::GetStringFUTF16(
289 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
290 ASCIIToUTF16("FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY")));
291 return;
294 // The utility process will have access to the directory passed to
295 // SandboxedUnpacker. That directory should not contain a symlink or NTFS
296 // reparse point. When the path is used, following the link/reparse point
297 // will cause file system access outside the sandbox path, and the sandbox
298 // will deny the operation.
299 base::FilePath link_free_crx_path;
300 if (!base::NormalizeFilePath(temp_crx_path, &link_free_crx_path)) {
301 LOG(ERROR) << "Could not get the normalized path of "
302 << temp_crx_path.value();
303 ReportFailure(COULD_NOT_GET_SANDBOX_FRIENDLY_PATH,
304 l10n_util::GetStringUTF16(IDS_EXTENSION_UNPACK_FAILED));
305 return;
307 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength",
308 link_free_crx_path);
310 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
311 base::Bind(&SandboxedUnpacker::StartUnzipOnIOThread,
312 this, link_free_crx_path));
315 void SandboxedUnpacker::StartWithDirectory(const std::string& extension_id,
316 const std::string& public_key,
317 const base::FilePath& directory) {
318 extension_id_ = extension_id;
319 public_key_ = public_key;
320 if (!CreateTempDirectory())
321 return; // ReportFailure() already called.
323 extension_root_ = temp_dir_.path().AppendASCII(kTempExtensionName);
325 if (!base::Move(directory, extension_root_)) {
326 LOG(ERROR) << "Could not move " << directory.value() << " to "
327 << extension_root_.value();
328 ReportFailure(
329 DIRECTORY_MOVE_FAILED,
330 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
331 ASCIIToUTF16("DIRECTORY_MOVE_FAILED")));
332 return;
335 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
336 base::Bind(&SandboxedUnpacker::StartUnpackOnIOThread,
337 this, extension_root_));
340 SandboxedUnpacker::~SandboxedUnpacker() {
341 // To avoid blocking shutdown, don't delete temporary directory here if it
342 // hasn't been cleaned up or passed on to another owner yet.
343 temp_dir_.Take();
346 bool SandboxedUnpacker::OnMessageReceived(const IPC::Message& message) {
347 bool handled = true;
348 IPC_BEGIN_MESSAGE_MAP(SandboxedUnpacker, message)
349 IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Succeeded,
350 OnUnzipToDirSucceeded)
351 IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Failed,
352 OnUnzipToDirFailed)
353 IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Succeeded,
354 OnUnpackExtensionSucceeded)
355 IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Failed,
356 OnUnpackExtensionFailed)
357 IPC_MESSAGE_UNHANDLED(handled = false)
358 IPC_END_MESSAGE_MAP()
359 return handled;
362 void SandboxedUnpacker::OnProcessCrashed(int exit_code) {
363 // Don't report crashes if they happen after we got a response.
364 if (got_response_)
365 return;
367 // Utility process crashed while trying to install.
368 ReportFailure(
369 UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL,
370 l10n_util::GetStringFUTF16(
371 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
372 ASCIIToUTF16("UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL")) +
373 ASCIIToUTF16(". ") +
374 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_PROCESS_CRASHED));
377 void SandboxedUnpacker::StartUnzipOnIOThread(const base::FilePath& crx_path) {
378 if (!utility_wrapper_->StartIfNeeded(temp_dir_.path(), this,
379 unpacker_io_task_runner_)) {
380 ReportFailure(
381 COULD_NOT_START_UTILITY_PROCESS,
382 l10n_util::GetStringFUTF16(
383 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
384 FailureReasonToString16(COULD_NOT_START_UTILITY_PROCESS)));
385 return;
387 DCHECK(crx_path.DirName() == temp_dir_.path());
388 base::FilePath unzipped_dir =
389 crx_path.DirName().AppendASCII(kTempExtensionName);
390 utility_wrapper_->host()->Send(
391 new ExtensionUtilityMsg_UnzipToDir(crx_path, unzipped_dir));
394 void SandboxedUnpacker::StartUnpackOnIOThread(
395 const base::FilePath& directory_path) {
396 if (!utility_wrapper_->StartIfNeeded(temp_dir_.path(), this,
397 unpacker_io_task_runner_)) {
398 ReportFailure(
399 COULD_NOT_START_UTILITY_PROCESS,
400 l10n_util::GetStringFUTF16(
401 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
402 FailureReasonToString16(COULD_NOT_START_UTILITY_PROCESS)));
403 return;
405 DCHECK(directory_path.DirName() == temp_dir_.path());
406 utility_wrapper_->host()->Send(new ExtensionUtilityMsg_UnpackExtension(
407 directory_path, extension_id_, location_, creation_flags_));
410 void SandboxedUnpacker::OnUnzipToDirSucceeded(const base::FilePath& directory) {
411 BrowserThread::PostTask(
412 BrowserThread::IO, FROM_HERE,
413 base::Bind(&SandboxedUnpacker::StartUnpackOnIOThread, this, directory));
416 void SandboxedUnpacker::OnUnzipToDirFailed(const std::string& error) {
417 got_response_ = true;
418 utility_wrapper_ = nullptr;
419 ReportFailure(UNZIP_FAILED,
420 l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR));
423 void SandboxedUnpacker::OnUnpackExtensionSucceeded(
424 const base::DictionaryValue& manifest) {
425 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
426 got_response_ = true;
427 utility_wrapper_ = nullptr;
429 scoped_ptr<base::DictionaryValue> final_manifest(
430 RewriteManifestFile(manifest));
431 if (!final_manifest)
432 return;
434 // Create an extension object that refers to the temporary location the
435 // extension was unpacked to. We use this until the extension is finally
436 // installed. For example, the install UI shows images from inside the
437 // extension.
439 // Localize manifest now, so confirm UI gets correct extension name.
441 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
442 // with base::string16
443 std::string utf8_error;
444 if (!extension_l10n_util::LocalizeExtension(
445 extension_root_, final_manifest.get(), &utf8_error)) {
446 ReportFailure(
447 COULD_NOT_LOCALIZE_EXTENSION,
448 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE,
449 base::UTF8ToUTF16(utf8_error)));
450 return;
453 extension_ =
454 Extension::Create(extension_root_, location_, *final_manifest,
455 Extension::REQUIRE_KEY | creation_flags_, &utf8_error);
457 if (!extension_.get()) {
458 ReportFailure(INVALID_MANIFEST,
459 ASCIIToUTF16("Manifest is invalid: " + utf8_error));
460 return;
463 SkBitmap install_icon;
464 if (!RewriteImageFiles(&install_icon))
465 return;
467 if (!RewriteCatalogFiles())
468 return;
470 ReportSuccess(manifest, install_icon);
473 void SandboxedUnpacker::OnUnpackExtensionFailed(const base::string16& error) {
474 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
475 got_response_ = true;
476 utility_wrapper_ = nullptr;
477 ReportFailure(
478 UNPACKER_CLIENT_FAILED,
479 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, error));
482 base::string16 SandboxedUnpacker::FailureReasonToString16(
483 FailureReason reason) {
484 switch (reason) {
485 case COULD_NOT_GET_TEMP_DIRECTORY:
486 return ASCIIToUTF16("COULD_NOT_GET_TEMP_DIRECTORY");
487 case COULD_NOT_CREATE_TEMP_DIRECTORY:
488 return ASCIIToUTF16("COULD_NOT_CREATE_TEMP_DIRECTORY");
489 case FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY:
490 return ASCIIToUTF16("FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY");
491 case COULD_NOT_GET_SANDBOX_FRIENDLY_PATH:
492 return ASCIIToUTF16("COULD_NOT_GET_SANDBOX_FRIENDLY_PATH");
493 case COULD_NOT_LOCALIZE_EXTENSION:
494 return ASCIIToUTF16("COULD_NOT_LOCALIZE_EXTENSION");
495 case INVALID_MANIFEST:
496 return ASCIIToUTF16("INVALID_MANIFEST");
497 case UNPACKER_CLIENT_FAILED:
498 return ASCIIToUTF16("UNPACKER_CLIENT_FAILED");
499 case UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL:
500 return ASCIIToUTF16("UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL");
502 case CRX_FILE_NOT_READABLE:
503 return ASCIIToUTF16("CRX_FILE_NOT_READABLE");
504 case CRX_HEADER_INVALID:
505 return ASCIIToUTF16("CRX_HEADER_INVALID");
506 case CRX_MAGIC_NUMBER_INVALID:
507 return ASCIIToUTF16("CRX_MAGIC_NUMBER_INVALID");
508 case CRX_VERSION_NUMBER_INVALID:
509 return ASCIIToUTF16("CRX_VERSION_NUMBER_INVALID");
510 case CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE:
511 return ASCIIToUTF16("CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE");
512 case CRX_ZERO_KEY_LENGTH:
513 return ASCIIToUTF16("CRX_ZERO_KEY_LENGTH");
514 case CRX_ZERO_SIGNATURE_LENGTH:
515 return ASCIIToUTF16("CRX_ZERO_SIGNATURE_LENGTH");
516 case CRX_PUBLIC_KEY_INVALID:
517 return ASCIIToUTF16("CRX_PUBLIC_KEY_INVALID");
518 case CRX_SIGNATURE_INVALID:
519 return ASCIIToUTF16("CRX_SIGNATURE_INVALID");
520 case CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED:
521 return ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED");
522 case CRX_SIGNATURE_VERIFICATION_FAILED:
523 return ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_FAILED");
525 case ERROR_SERIALIZING_MANIFEST_JSON:
526 return ASCIIToUTF16("ERROR_SERIALIZING_MANIFEST_JSON");
527 case ERROR_SAVING_MANIFEST_JSON:
528 return ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON");
530 case COULD_NOT_READ_IMAGE_DATA_FROM_DISK:
531 return ASCIIToUTF16("COULD_NOT_READ_IMAGE_DATA_FROM_DISK");
532 case DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST:
533 return ASCIIToUTF16("DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST");
534 case INVALID_PATH_FOR_BROWSER_IMAGE:
535 return ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE");
536 case ERROR_REMOVING_OLD_IMAGE_FILE:
537 return ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE");
538 case INVALID_PATH_FOR_BITMAP_IMAGE:
539 return ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE");
540 case ERROR_RE_ENCODING_THEME_IMAGE:
541 return ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE");
542 case ERROR_SAVING_THEME_IMAGE:
543 return ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE");
544 case ABORTED_DUE_TO_SHUTDOWN:
545 return ASCIIToUTF16("ABORTED_DUE_TO_SHUTDOWN");
547 case COULD_NOT_READ_CATALOG_DATA_FROM_DISK:
548 return ASCIIToUTF16("COULD_NOT_READ_CATALOG_DATA_FROM_DISK");
549 case INVALID_CATALOG_DATA:
550 return ASCIIToUTF16("INVALID_CATALOG_DATA");
551 case INVALID_PATH_FOR_CATALOG:
552 return ASCIIToUTF16("INVALID_PATH_FOR_CATALOG");
553 case ERROR_SERIALIZING_CATALOG:
554 return ASCIIToUTF16("ERROR_SERIALIZING_CATALOG");
555 case ERROR_SAVING_CATALOG:
556 return ASCIIToUTF16("ERROR_SAVING_CATALOG");
558 case CRX_HASH_VERIFICATION_FAILED:
559 return ASCIIToUTF16("CRX_HASH_VERIFICATION_FAILED");
561 case UNZIP_FAILED:
562 return ASCIIToUTF16("UNZIP_FAILED");
563 case DIRECTORY_MOVE_FAILED:
564 return ASCIIToUTF16("DIRECTORY_MOVE_FAILED");
565 case COULD_NOT_START_UTILITY_PROCESS:
566 return ASCIIToUTF16("COULD_NOT_START_UTILITY_PROCESS");
568 case NUM_FAILURE_REASONS:
569 NOTREACHED();
570 return base::string16();
572 NOTREACHED();
573 return base::string16();
576 void SandboxedUnpacker::FailWithPackageError(FailureReason reason) {
577 ReportFailure(reason,
578 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_CODE,
579 FailureReasonToString16(reason)));
582 bool SandboxedUnpacker::ValidateSignature(const base::FilePath& crx_path,
583 const std::string& expected_hash) {
584 CrxFile::ValidateError error = CrxFile::ValidateSignature(
585 crx_path, expected_hash, &public_key_, &extension_id_, nullptr);
587 switch (error) {
588 case CrxFile::ValidateError::NONE: {
589 if (!expected_hash.empty())
590 UMA_HISTOGRAM_BOOLEAN("Extensions.SandboxUnpackHashCheck", true);
591 return true;
594 case CrxFile::ValidateError::CRX_FILE_NOT_READABLE:
595 FailWithPackageError(CRX_FILE_NOT_READABLE);
596 break;
597 case CrxFile::ValidateError::CRX_HEADER_INVALID:
598 FailWithPackageError(CRX_HEADER_INVALID);
599 break;
600 case CrxFile::ValidateError::CRX_MAGIC_NUMBER_INVALID:
601 FailWithPackageError(CRX_MAGIC_NUMBER_INVALID);
602 break;
603 case CrxFile::ValidateError::CRX_VERSION_NUMBER_INVALID:
604 FailWithPackageError(CRX_VERSION_NUMBER_INVALID);
605 break;
606 case CrxFile::ValidateError::CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE:
607 FailWithPackageError(CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE);
608 break;
609 case CrxFile::ValidateError::CRX_ZERO_KEY_LENGTH:
610 FailWithPackageError(CRX_ZERO_KEY_LENGTH);
611 break;
612 case CrxFile::ValidateError::CRX_ZERO_SIGNATURE_LENGTH:
613 FailWithPackageError(CRX_ZERO_SIGNATURE_LENGTH);
614 break;
615 case CrxFile::ValidateError::CRX_PUBLIC_KEY_INVALID:
616 FailWithPackageError(CRX_PUBLIC_KEY_INVALID);
617 break;
618 case CrxFile::ValidateError::CRX_SIGNATURE_INVALID:
619 FailWithPackageError(CRX_SIGNATURE_INVALID);
620 break;
621 case CrxFile::ValidateError::
622 CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED:
623 FailWithPackageError(CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED);
624 break;
625 case CrxFile::ValidateError::CRX_SIGNATURE_VERIFICATION_FAILED:
626 FailWithPackageError(CRX_SIGNATURE_VERIFICATION_FAILED);
627 break;
628 case CrxFile::ValidateError::CRX_HASH_VERIFICATION_FAILED:
629 // We should never get this result unless we had specifically asked for
630 // verification of the crx file's hash.
631 CHECK(!expected_hash.empty());
632 UMA_HISTOGRAM_BOOLEAN("Extensions.SandboxUnpackHashCheck", false);
633 FailWithPackageError(CRX_HASH_VERIFICATION_FAILED);
634 break;
636 return false;
639 void SandboxedUnpacker::ReportFailure(FailureReason reason,
640 const base::string16& error) {
641 utility_wrapper_ = nullptr;
642 UMA_HISTOGRAM_ENUMERATION("Extensions.SandboxUnpackFailureReason", reason,
643 NUM_FAILURE_REASONS);
644 if (!crx_unpack_start_time_.is_null())
645 UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackFailureTime",
646 base::TimeTicks::Now() - crx_unpack_start_time_);
647 Cleanup();
649 CrxInstallError error_info(reason == CRX_HASH_VERIFICATION_FAILED
650 ? CrxInstallError::ERROR_HASH_MISMATCH
651 : CrxInstallError::ERROR_OTHER,
652 error);
654 client_->OnUnpackFailure(error_info);
657 void SandboxedUnpacker::ReportSuccess(
658 const base::DictionaryValue& original_manifest,
659 const SkBitmap& install_icon) {
660 utility_wrapper_ = nullptr;
661 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1);
663 if (!crx_unpack_start_time_.is_null())
664 RecordSuccessfulUnpackTimeHistograms(
665 crx_path_for_histograms_,
666 base::TimeTicks::Now() - crx_unpack_start_time_);
667 DCHECK(!temp_dir_.path().empty());
669 // Client takes ownership of temporary directory and extension.
670 client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_,
671 &original_manifest, extension_.get(), install_icon);
672 extension_ = NULL;
675 base::DictionaryValue* SandboxedUnpacker::RewriteManifestFile(
676 const base::DictionaryValue& manifest) {
677 // Add the public key extracted earlier to the parsed manifest and overwrite
678 // the original manifest. We do this to ensure the manifest doesn't contain an
679 // exploitable bug that could be used to compromise the browser.
680 DCHECK(!public_key_.empty());
681 scoped_ptr<base::DictionaryValue> final_manifest(manifest.DeepCopy());
682 final_manifest->SetString(manifest_keys::kPublicKey, public_key_);
684 std::string manifest_json;
685 JSONStringValueSerializer serializer(&manifest_json);
686 serializer.set_pretty_print(true);
687 if (!serializer.Serialize(*final_manifest)) {
688 // Error serializing manifest.json.
689 ReportFailure(ERROR_SERIALIZING_MANIFEST_JSON,
690 l10n_util::GetStringFUTF16(
691 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
692 ASCIIToUTF16("ERROR_SERIALIZING_MANIFEST_JSON")));
693 return NULL;
696 base::FilePath manifest_path = extension_root_.Append(kManifestFilename);
697 int size = base::checked_cast<int>(manifest_json.size());
698 if (base::WriteFile(manifest_path, manifest_json.data(), size) != size) {
699 // Error saving manifest.json.
700 ReportFailure(
701 ERROR_SAVING_MANIFEST_JSON,
702 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
703 ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON")));
704 return NULL;
707 return final_manifest.release();
710 bool SandboxedUnpacker::RewriteImageFiles(SkBitmap* install_icon) {
711 DCHECK(!temp_dir_.path().empty());
712 DecodedImages images;
713 if (!ReadImagesFromFile(temp_dir_.path(), &images)) {
714 // Couldn't read image data from disk.
715 ReportFailure(COULD_NOT_READ_IMAGE_DATA_FROM_DISK,
716 l10n_util::GetStringFUTF16(
717 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
718 ASCIIToUTF16("COULD_NOT_READ_IMAGE_DATA_FROM_DISK")));
719 return false;
722 // Delete any images that may be used by the browser. We're going to write
723 // out our own versions of the parsed images, and we want to make sure the
724 // originals are gone for good.
725 std::set<base::FilePath> image_paths =
726 ExtensionsClient::Get()->GetBrowserImagePaths(extension_.get());
727 if (image_paths.size() != images.size()) {
728 // Decoded images don't match what's in the manifest.
729 ReportFailure(
730 DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST,
731 l10n_util::GetStringFUTF16(
732 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
733 ASCIIToUTF16("DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST")));
734 return false;
737 for (std::set<base::FilePath>::iterator it = image_paths.begin();
738 it != image_paths.end(); ++it) {
739 base::FilePath path = *it;
740 if (path.IsAbsolute() || path.ReferencesParent()) {
741 // Invalid path for browser image.
742 ReportFailure(INVALID_PATH_FOR_BROWSER_IMAGE,
743 l10n_util::GetStringFUTF16(
744 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
745 ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE")));
746 return false;
748 if (!base::DeleteFile(extension_root_.Append(path), false)) {
749 // Error removing old image file.
750 ReportFailure(ERROR_REMOVING_OLD_IMAGE_FILE,
751 l10n_util::GetStringFUTF16(
752 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
753 ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE")));
754 return false;
758 const std::string& install_icon_path =
759 IconsInfo::GetIcons(extension_.get())
760 .Get(extension_misc::EXTENSION_ICON_LARGE,
761 ExtensionIconSet::MATCH_BIGGER);
763 // Write our parsed images back to disk as well.
764 for (size_t i = 0; i < images.size(); ++i) {
765 if (BrowserThread::GetBlockingPool()->IsShutdownInProgress()) {
766 // Abort package installation if shutdown was initiated, crbug.com/235525
767 ReportFailure(
768 ABORTED_DUE_TO_SHUTDOWN,
769 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
770 ASCIIToUTF16("ABORTED_DUE_TO_SHUTDOWN")));
771 return false;
774 const SkBitmap& image = base::get<0>(images[i]);
775 base::FilePath path_suffix = base::get<1>(images[i]);
776 if (path_suffix.MaybeAsASCII() == install_icon_path)
777 *install_icon = image;
779 if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) {
780 // Invalid path for bitmap image.
781 ReportFailure(INVALID_PATH_FOR_BITMAP_IMAGE,
782 l10n_util::GetStringFUTF16(
783 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
784 ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE")));
785 return false;
787 base::FilePath path = extension_root_.Append(path_suffix);
789 std::vector<unsigned char> image_data;
790 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
791 // though they may originally be .jpg, etc. Figure something out.
792 // http://code.google.com/p/chromium/issues/detail?id=12459
793 if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) {
794 // Error re-encoding theme image.
795 ReportFailure(ERROR_RE_ENCODING_THEME_IMAGE,
796 l10n_util::GetStringFUTF16(
797 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
798 ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE")));
799 return false;
802 // Note: we're overwriting existing files that the utility process wrote,
803 // so we can be sure the directory exists.
804 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
805 int size = base::checked_cast<int>(image_data.size());
806 if (base::WriteFile(path, image_data_ptr, size) != size) {
807 // Error saving theme image.
808 ReportFailure(
809 ERROR_SAVING_THEME_IMAGE,
810 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
811 ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE")));
812 return false;
816 return true;
819 bool SandboxedUnpacker::RewriteCatalogFiles() {
820 base::DictionaryValue catalogs;
821 if (!ReadMessageCatalogsFromFile(temp_dir_.path(), &catalogs)) {
822 // Could not read catalog data from disk.
823 ReportFailure(COULD_NOT_READ_CATALOG_DATA_FROM_DISK,
824 l10n_util::GetStringFUTF16(
825 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
826 ASCIIToUTF16("COULD_NOT_READ_CATALOG_DATA_FROM_DISK")));
827 return false;
830 // Write our parsed catalogs back to disk.
831 for (base::DictionaryValue::Iterator it(catalogs); !it.IsAtEnd();
832 it.Advance()) {
833 const base::DictionaryValue* catalog = NULL;
834 if (!it.value().GetAsDictionary(&catalog)) {
835 // Invalid catalog data.
836 ReportFailure(
837 INVALID_CATALOG_DATA,
838 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
839 ASCIIToUTF16("INVALID_CATALOG_DATA")));
840 return false;
843 base::FilePath relative_path = base::FilePath::FromUTF8Unsafe(it.key());
844 relative_path = relative_path.Append(kMessagesFilename);
845 if (relative_path.IsAbsolute() || relative_path.ReferencesParent()) {
846 // Invalid path for catalog.
847 ReportFailure(
848 INVALID_PATH_FOR_CATALOG,
849 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
850 ASCIIToUTF16("INVALID_PATH_FOR_CATALOG")));
851 return false;
853 base::FilePath path = extension_root_.Append(relative_path);
855 std::string catalog_json;
856 JSONStringValueSerializer serializer(&catalog_json);
857 serializer.set_pretty_print(true);
858 if (!serializer.Serialize(*catalog)) {
859 // Error serializing catalog.
860 ReportFailure(ERROR_SERIALIZING_CATALOG,
861 l10n_util::GetStringFUTF16(
862 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
863 ASCIIToUTF16("ERROR_SERIALIZING_CATALOG")));
864 return false;
867 // Note: we're overwriting existing files that the utility process read,
868 // so we can be sure the directory exists.
869 int size = base::checked_cast<int>(catalog_json.size());
870 if (base::WriteFile(path, catalog_json.c_str(), size) != size) {
871 // Error saving catalog.
872 ReportFailure(
873 ERROR_SAVING_CATALOG,
874 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
875 ASCIIToUTF16("ERROR_SAVING_CATALOG")));
876 return false;
880 return true;
883 void SandboxedUnpacker::Cleanup() {
884 DCHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
885 if (!temp_dir_.Delete()) {
886 LOG(WARNING) << "Can not delete temp directory at "
887 << temp_dir_.path().value();
891 SandboxedUnpacker::UtilityHostWrapper::UtilityHostWrapper() {}
893 bool SandboxedUnpacker::UtilityHostWrapper::StartIfNeeded(
894 const base::FilePath& exposed_dir,
895 const scoped_refptr<UtilityProcessHostClient>& client,
896 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner) {
897 DCHECK_CURRENTLY_ON(BrowserThread::IO);
898 if (!utility_host_) {
899 utility_host_ =
900 UtilityProcessHost::Create(client, client_task_runner)->AsWeakPtr();
901 utility_host_->SetName(
902 l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_EXTENSION_UNPACKER_NAME));
904 // Grant the subprocess access to our temp dir so it can write out files.
905 DCHECK(!exposed_dir.empty());
906 utility_host_->SetExposedDir(exposed_dir);
907 if (!utility_host_->StartBatchMode()) {
908 utility_host_.reset();
909 return false;
912 return true;
915 content::UtilityProcessHost* SandboxedUnpacker::UtilityHostWrapper::host()
916 const {
917 DCHECK_CURRENTLY_ON(BrowserThread::IO);
918 return utility_host_.get();
921 SandboxedUnpacker::UtilityHostWrapper::~UtilityHostWrapper() {
922 DCHECK_CURRENTLY_ON(BrowserThread::IO);
923 if (utility_host_) {
924 utility_host_->EndBatchMode();
925 utility_host_.reset();
929 } // namespace extensions