Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / extensions / browser / sandboxed_unpacker.cc
blob7b27e857508ada1a2eaa13630ba59245f5ecae61
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 const CRXFileInfo& file,
216 Manifest::Location location,
217 int creation_flags,
218 const base::FilePath& extensions_dir,
219 const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner,
220 SandboxedUnpackerClient* client)
221 : crx_path_(file.path),
222 package_hash_(base::ToLowerASCII(file.expected_hash)),
223 check_crx_hash_(false),
224 client_(client),
225 extensions_dir_(extensions_dir),
226 got_response_(false),
227 location_(location),
228 creation_flags_(creation_flags),
229 unpacker_io_task_runner_(unpacker_io_task_runner) {
230 if (!package_hash_.empty()) {
231 check_crx_hash_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
232 extensions::switches::kEnableCrxHashCheck);
236 bool SandboxedUnpacker::CreateTempDirectory() {
237 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
239 base::FilePath temp_dir;
240 if (!FindWritableTempLocation(extensions_dir_, &temp_dir)) {
241 ReportFailure(COULD_NOT_GET_TEMP_DIRECTORY,
242 l10n_util::GetStringFUTF16(
243 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
244 ASCIIToUTF16("COULD_NOT_GET_TEMP_DIRECTORY")));
245 return false;
248 if (!temp_dir_.CreateUniqueTempDirUnderPath(temp_dir)) {
249 ReportFailure(COULD_NOT_CREATE_TEMP_DIRECTORY,
250 l10n_util::GetStringFUTF16(
251 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
252 ASCIIToUTF16("COULD_NOT_CREATE_TEMP_DIRECTORY")));
253 return false;
256 return true;
259 void SandboxedUnpacker::Start() {
260 // We assume that we are started on the thread that the client wants us to do
261 // file IO on.
262 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
264 unpack_start_time_ = base::TimeTicks::Now();
266 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackInitialCrxPathLength",
267 crx_path_);
268 if (!CreateTempDirectory())
269 return; // ReportFailure() already called.
271 // Initialize the path that will eventually contain the unpacked extension.
272 extension_root_ = temp_dir_.path().AppendASCII(kTempExtensionName);
273 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackUnpackedCrxPathLength",
274 extension_root_);
276 // Extract the public key and validate the package.
277 if (!ValidateSignature())
278 return; // ValidateSignature() already reported the error.
280 // Copy the crx file into our working directory.
281 base::FilePath temp_crx_path = temp_dir_.path().Append(crx_path_.BaseName());
282 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackTempCrxPathLength",
283 temp_crx_path);
285 if (!base::CopyFile(crx_path_, temp_crx_path)) {
286 // Failed to copy extension file to temporary directory.
287 ReportFailure(
288 FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY,
289 l10n_util::GetStringFUTF16(
290 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
291 ASCIIToUTF16("FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY")));
292 return;
295 // The utility process will have access to the directory passed to
296 // SandboxedUnpacker. That directory should not contain a symlink or NTFS
297 // reparse point. When the path is used, following the link/reparse point
298 // will cause file system access outside the sandbox path, and the sandbox
299 // will deny the operation.
300 base::FilePath link_free_crx_path;
301 if (!base::NormalizeFilePath(temp_crx_path, &link_free_crx_path)) {
302 LOG(ERROR) << "Could not get the normalized path of "
303 << temp_crx_path.value();
304 ReportFailure(COULD_NOT_GET_SANDBOX_FRIENDLY_PATH,
305 l10n_util::GetStringUTF16(IDS_EXTENSION_UNPACK_FAILED));
306 return;
308 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength",
309 link_free_crx_path);
311 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
312 base::Bind(&SandboxedUnpacker::StartProcessOnIOThread,
313 this, link_free_crx_path));
316 SandboxedUnpacker::~SandboxedUnpacker() {
317 // To avoid blocking shutdown, don't delete temporary directory here if it
318 // hasn't been cleaned up or passed on to another owner yet.
319 temp_dir_.Take();
322 bool SandboxedUnpacker::OnMessageReceived(const IPC::Message& message) {
323 bool handled = true;
324 IPC_BEGIN_MESSAGE_MAP(SandboxedUnpacker, message)
325 IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Succeeded,
326 OnUnpackExtensionSucceeded)
327 IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Failed,
328 OnUnpackExtensionFailed)
329 IPC_MESSAGE_UNHANDLED(handled = false)
330 IPC_END_MESSAGE_MAP()
331 return handled;
334 void SandboxedUnpacker::OnProcessCrashed(int exit_code) {
335 // Don't report crashes if they happen after we got a response.
336 if (got_response_)
337 return;
339 // Utility process crashed while trying to install.
340 ReportFailure(
341 UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL,
342 l10n_util::GetStringFUTF16(
343 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
344 ASCIIToUTF16("UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL")) +
345 ASCIIToUTF16(". ") +
346 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_PROCESS_CRASHED));
349 void SandboxedUnpacker::StartProcessOnIOThread(
350 const base::FilePath& temp_crx_path) {
351 UtilityProcessHost* host =
352 UtilityProcessHost::Create(this, unpacker_io_task_runner_.get());
353 host->SetName(
354 l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_EXTENSION_UNPACKER_NAME));
355 // Grant the subprocess access to the entire subdir the extension file is
356 // in, so that it can unpack to that dir.
357 host->SetExposedDir(temp_crx_path.DirName());
358 host->Send(new ExtensionUtilityMsg_UnpackExtension(
359 temp_crx_path, extension_id_, location_, creation_flags_));
362 void SandboxedUnpacker::OnUnpackExtensionSucceeded(
363 const base::DictionaryValue& manifest) {
364 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
365 got_response_ = true;
367 scoped_ptr<base::DictionaryValue> final_manifest(
368 RewriteManifestFile(manifest));
369 if (!final_manifest)
370 return;
372 // Create an extension object that refers to the temporary location the
373 // extension was unpacked to. We use this until the extension is finally
374 // installed. For example, the install UI shows images from inside the
375 // extension.
377 // Localize manifest now, so confirm UI gets correct extension name.
379 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
380 // with base::string16
381 std::string utf8_error;
382 if (!extension_l10n_util::LocalizeExtension(
383 extension_root_, final_manifest.get(), &utf8_error)) {
384 ReportFailure(
385 COULD_NOT_LOCALIZE_EXTENSION,
386 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE,
387 base::UTF8ToUTF16(utf8_error)));
388 return;
391 extension_ =
392 Extension::Create(extension_root_, location_, *final_manifest,
393 Extension::REQUIRE_KEY | creation_flags_, &utf8_error);
395 if (!extension_.get()) {
396 ReportFailure(INVALID_MANIFEST,
397 ASCIIToUTF16("Manifest is invalid: " + utf8_error));
398 return;
401 SkBitmap install_icon;
402 if (!RewriteImageFiles(&install_icon))
403 return;
405 if (!RewriteCatalogFiles())
406 return;
408 ReportSuccess(manifest, install_icon);
411 void SandboxedUnpacker::OnUnpackExtensionFailed(const base::string16& error) {
412 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
413 got_response_ = true;
414 ReportFailure(
415 UNPACKER_CLIENT_FAILED,
416 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, error));
419 base::string16 SandboxedUnpacker::FailureReasonToString16(
420 FailureReason reason) {
421 switch (reason) {
422 case COULD_NOT_GET_TEMP_DIRECTORY:
423 return ASCIIToUTF16("COULD_NOT_GET_TEMP_DIRECTORY");
424 case COULD_NOT_CREATE_TEMP_DIRECTORY:
425 return ASCIIToUTF16("COULD_NOT_CREATE_TEMP_DIRECTORY");
426 case FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY:
427 return ASCIIToUTF16("FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY");
428 case COULD_NOT_GET_SANDBOX_FRIENDLY_PATH:
429 return ASCIIToUTF16("COULD_NOT_GET_SANDBOX_FRIENDLY_PATH");
430 case COULD_NOT_LOCALIZE_EXTENSION:
431 return ASCIIToUTF16("COULD_NOT_LOCALIZE_EXTENSION");
432 case INVALID_MANIFEST:
433 return ASCIIToUTF16("INVALID_MANIFEST");
434 case UNPACKER_CLIENT_FAILED:
435 return ASCIIToUTF16("UNPACKER_CLIENT_FAILED");
436 case UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL:
437 return ASCIIToUTF16("UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL");
439 case CRX_FILE_NOT_READABLE:
440 return ASCIIToUTF16("CRX_FILE_NOT_READABLE");
441 case CRX_HEADER_INVALID:
442 return ASCIIToUTF16("CRX_HEADER_INVALID");
443 case CRX_MAGIC_NUMBER_INVALID:
444 return ASCIIToUTF16("CRX_MAGIC_NUMBER_INVALID");
445 case CRX_VERSION_NUMBER_INVALID:
446 return ASCIIToUTF16("CRX_VERSION_NUMBER_INVALID");
447 case CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE:
448 return ASCIIToUTF16("CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE");
449 case CRX_ZERO_KEY_LENGTH:
450 return ASCIIToUTF16("CRX_ZERO_KEY_LENGTH");
451 case CRX_ZERO_SIGNATURE_LENGTH:
452 return ASCIIToUTF16("CRX_ZERO_SIGNATURE_LENGTH");
453 case CRX_PUBLIC_KEY_INVALID:
454 return ASCIIToUTF16("CRX_PUBLIC_KEY_INVALID");
455 case CRX_SIGNATURE_INVALID:
456 return ASCIIToUTF16("CRX_SIGNATURE_INVALID");
457 case CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED:
458 return ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED");
459 case CRX_SIGNATURE_VERIFICATION_FAILED:
460 return ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_FAILED");
462 case ERROR_SERIALIZING_MANIFEST_JSON:
463 return ASCIIToUTF16("ERROR_SERIALIZING_MANIFEST_JSON");
464 case ERROR_SAVING_MANIFEST_JSON:
465 return ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON");
467 case COULD_NOT_READ_IMAGE_DATA_FROM_DISK:
468 return ASCIIToUTF16("COULD_NOT_READ_IMAGE_DATA_FROM_DISK");
469 case DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST:
470 return ASCIIToUTF16("DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST");
471 case INVALID_PATH_FOR_BROWSER_IMAGE:
472 return ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE");
473 case ERROR_REMOVING_OLD_IMAGE_FILE:
474 return ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE");
475 case INVALID_PATH_FOR_BITMAP_IMAGE:
476 return ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE");
477 case ERROR_RE_ENCODING_THEME_IMAGE:
478 return ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE");
479 case ERROR_SAVING_THEME_IMAGE:
480 return ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE");
481 case ABORTED_DUE_TO_SHUTDOWN:
482 return ASCIIToUTF16("ABORTED_DUE_TO_SHUTDOWN");
484 case COULD_NOT_READ_CATALOG_DATA_FROM_DISK:
485 return ASCIIToUTF16("COULD_NOT_READ_CATALOG_DATA_FROM_DISK");
486 case INVALID_CATALOG_DATA:
487 return ASCIIToUTF16("INVALID_CATALOG_DATA");
488 case INVALID_PATH_FOR_CATALOG:
489 return ASCIIToUTF16("INVALID_PATH_FOR_CATALOG");
490 case ERROR_SERIALIZING_CATALOG:
491 return ASCIIToUTF16("ERROR_SERIALIZING_CATALOG");
492 case ERROR_SAVING_CATALOG:
493 return ASCIIToUTF16("ERROR_SAVING_CATALOG");
495 case CRX_HASH_VERIFICATION_FAILED:
496 return ASCIIToUTF16("CRX_HASH_VERIFICATION_FAILED");
498 case NUM_FAILURE_REASONS:
499 NOTREACHED();
500 return base::string16();
502 NOTREACHED();
503 return base::string16();
506 void SandboxedUnpacker::FailWithPackageError(FailureReason reason) {
507 ReportFailure(reason,
508 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_CODE,
509 FailureReasonToString16(reason)));
512 bool SandboxedUnpacker::ValidateSignature() {
513 CrxFile::ValidateError error = CrxFile::ValidateSignature(
514 crx_path_, check_crx_hash_ ? package_hash_ : std::string(), &public_key_,
515 &extension_id_, nullptr);
517 switch (error) {
518 case CrxFile::ValidateError::NONE: {
519 if (check_crx_hash_)
520 UMA_HISTOGRAM_BOOLEAN("Extensions.SandboxUnpackHashCheck", true);
521 return true;
524 case CrxFile::ValidateError::CRX_FILE_NOT_READABLE:
525 FailWithPackageError(CRX_FILE_NOT_READABLE);
526 break;
527 case CrxFile::ValidateError::CRX_HEADER_INVALID:
528 FailWithPackageError(CRX_HEADER_INVALID);
529 break;
530 case CrxFile::ValidateError::CRX_MAGIC_NUMBER_INVALID:
531 FailWithPackageError(CRX_MAGIC_NUMBER_INVALID);
532 break;
533 case CrxFile::ValidateError::CRX_VERSION_NUMBER_INVALID:
534 FailWithPackageError(CRX_VERSION_NUMBER_INVALID);
535 break;
536 case CrxFile::ValidateError::CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE:
537 FailWithPackageError(CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE);
538 break;
539 case CrxFile::ValidateError::CRX_ZERO_KEY_LENGTH:
540 FailWithPackageError(CRX_ZERO_KEY_LENGTH);
541 break;
542 case CrxFile::ValidateError::CRX_ZERO_SIGNATURE_LENGTH:
543 FailWithPackageError(CRX_ZERO_SIGNATURE_LENGTH);
544 break;
545 case CrxFile::ValidateError::CRX_PUBLIC_KEY_INVALID:
546 FailWithPackageError(CRX_PUBLIC_KEY_INVALID);
547 break;
548 case CrxFile::ValidateError::CRX_SIGNATURE_INVALID:
549 FailWithPackageError(CRX_SIGNATURE_INVALID);
550 break;
551 case CrxFile::ValidateError::
552 CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED:
553 FailWithPackageError(CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED);
554 break;
555 case CrxFile::ValidateError::CRX_SIGNATURE_VERIFICATION_FAILED:
556 FailWithPackageError(CRX_SIGNATURE_VERIFICATION_FAILED);
557 break;
558 case CrxFile::ValidateError::CRX_HASH_VERIFICATION_FAILED:
559 // We should never get this result unless we had specifically asked for
560 // verification of the crx file's hash.
561 CHECK(check_crx_hash_ && !package_hash_.empty());
562 UMA_HISTOGRAM_BOOLEAN("Extensions.SandboxUnpackHashCheck", false);
563 FailWithPackageError(CRX_HASH_VERIFICATION_FAILED);
564 break;
566 return false;
569 void SandboxedUnpacker::ReportFailure(FailureReason reason,
570 const base::string16& error) {
571 UMA_HISTOGRAM_ENUMERATION("Extensions.SandboxUnpackFailureReason", reason,
572 NUM_FAILURE_REASONS);
573 UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackFailureTime",
574 base::TimeTicks::Now() - unpack_start_time_);
575 Cleanup();
577 CrxInstallError error_info(reason == CRX_HASH_VERIFICATION_FAILED
578 ? CrxInstallError::ERROR_HASH_MISMATCH
579 : CrxInstallError::ERROR_OTHER,
580 error);
582 client_->OnUnpackFailure(error_info);
585 void SandboxedUnpacker::ReportSuccess(
586 const base::DictionaryValue& original_manifest,
587 const SkBitmap& install_icon) {
588 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1);
590 RecordSuccessfulUnpackTimeHistograms(
591 crx_path_, base::TimeTicks::Now() - unpack_start_time_);
593 // Client takes ownership of temporary directory and extension.
594 client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_,
595 &original_manifest, extension_.get(), install_icon);
596 extension_ = NULL;
599 base::DictionaryValue* SandboxedUnpacker::RewriteManifestFile(
600 const base::DictionaryValue& manifest) {
601 // Add the public key extracted earlier to the parsed manifest and overwrite
602 // the original manifest. We do this to ensure the manifest doesn't contain an
603 // exploitable bug that could be used to compromise the browser.
604 scoped_ptr<base::DictionaryValue> final_manifest(manifest.DeepCopy());
605 final_manifest->SetString(manifest_keys::kPublicKey, public_key_);
607 std::string manifest_json;
608 JSONStringValueSerializer serializer(&manifest_json);
609 serializer.set_pretty_print(true);
610 if (!serializer.Serialize(*final_manifest)) {
611 // Error serializing manifest.json.
612 ReportFailure(ERROR_SERIALIZING_MANIFEST_JSON,
613 l10n_util::GetStringFUTF16(
614 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
615 ASCIIToUTF16("ERROR_SERIALIZING_MANIFEST_JSON")));
616 return NULL;
619 base::FilePath manifest_path = extension_root_.Append(kManifestFilename);
620 int size = base::checked_cast<int>(manifest_json.size());
621 if (base::WriteFile(manifest_path, manifest_json.data(), size) != size) {
622 // Error saving manifest.json.
623 ReportFailure(
624 ERROR_SAVING_MANIFEST_JSON,
625 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
626 ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON")));
627 return NULL;
630 return final_manifest.release();
633 bool SandboxedUnpacker::RewriteImageFiles(SkBitmap* install_icon) {
634 DecodedImages images;
635 if (!ReadImagesFromFile(temp_dir_.path(), &images)) {
636 // Couldn't read image data from disk.
637 ReportFailure(COULD_NOT_READ_IMAGE_DATA_FROM_DISK,
638 l10n_util::GetStringFUTF16(
639 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
640 ASCIIToUTF16("COULD_NOT_READ_IMAGE_DATA_FROM_DISK")));
641 return false;
644 // Delete any images that may be used by the browser. We're going to write
645 // out our own versions of the parsed images, and we want to make sure the
646 // originals are gone for good.
647 std::set<base::FilePath> image_paths =
648 ExtensionsClient::Get()->GetBrowserImagePaths(extension_.get());
649 if (image_paths.size() != images.size()) {
650 // Decoded images don't match what's in the manifest.
651 ReportFailure(
652 DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST,
653 l10n_util::GetStringFUTF16(
654 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
655 ASCIIToUTF16("DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST")));
656 return false;
659 for (std::set<base::FilePath>::iterator it = image_paths.begin();
660 it != image_paths.end(); ++it) {
661 base::FilePath path = *it;
662 if (path.IsAbsolute() || path.ReferencesParent()) {
663 // Invalid path for browser image.
664 ReportFailure(INVALID_PATH_FOR_BROWSER_IMAGE,
665 l10n_util::GetStringFUTF16(
666 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
667 ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE")));
668 return false;
670 if (!base::DeleteFile(extension_root_.Append(path), false)) {
671 // Error removing old image file.
672 ReportFailure(ERROR_REMOVING_OLD_IMAGE_FILE,
673 l10n_util::GetStringFUTF16(
674 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
675 ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE")));
676 return false;
680 const std::string& install_icon_path =
681 IconsInfo::GetIcons(extension_.get())
682 .Get(extension_misc::EXTENSION_ICON_LARGE,
683 ExtensionIconSet::MATCH_BIGGER);
685 // Write our parsed images back to disk as well.
686 for (size_t i = 0; i < images.size(); ++i) {
687 if (BrowserThread::GetBlockingPool()->IsShutdownInProgress()) {
688 // Abort package installation if shutdown was initiated, crbug.com/235525
689 ReportFailure(
690 ABORTED_DUE_TO_SHUTDOWN,
691 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
692 ASCIIToUTF16("ABORTED_DUE_TO_SHUTDOWN")));
693 return false;
696 const SkBitmap& image = base::get<0>(images[i]);
697 base::FilePath path_suffix = base::get<1>(images[i]);
698 if (path_suffix.MaybeAsASCII() == install_icon_path)
699 *install_icon = image;
701 if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) {
702 // Invalid path for bitmap image.
703 ReportFailure(INVALID_PATH_FOR_BITMAP_IMAGE,
704 l10n_util::GetStringFUTF16(
705 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
706 ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE")));
707 return false;
709 base::FilePath path = extension_root_.Append(path_suffix);
711 std::vector<unsigned char> image_data;
712 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
713 // though they may originally be .jpg, etc. Figure something out.
714 // http://code.google.com/p/chromium/issues/detail?id=12459
715 if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) {
716 // Error re-encoding theme image.
717 ReportFailure(ERROR_RE_ENCODING_THEME_IMAGE,
718 l10n_util::GetStringFUTF16(
719 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
720 ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE")));
721 return false;
724 // Note: we're overwriting existing files that the utility process wrote,
725 // so we can be sure the directory exists.
726 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
727 int size = base::checked_cast<int>(image_data.size());
728 if (base::WriteFile(path, image_data_ptr, size) != size) {
729 // Error saving theme image.
730 ReportFailure(
731 ERROR_SAVING_THEME_IMAGE,
732 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
733 ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE")));
734 return false;
738 return true;
741 bool SandboxedUnpacker::RewriteCatalogFiles() {
742 base::DictionaryValue catalogs;
743 if (!ReadMessageCatalogsFromFile(temp_dir_.path(), &catalogs)) {
744 // Could not read catalog data from disk.
745 ReportFailure(COULD_NOT_READ_CATALOG_DATA_FROM_DISK,
746 l10n_util::GetStringFUTF16(
747 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
748 ASCIIToUTF16("COULD_NOT_READ_CATALOG_DATA_FROM_DISK")));
749 return false;
752 // Write our parsed catalogs back to disk.
753 for (base::DictionaryValue::Iterator it(catalogs); !it.IsAtEnd();
754 it.Advance()) {
755 const base::DictionaryValue* catalog = NULL;
756 if (!it.value().GetAsDictionary(&catalog)) {
757 // Invalid catalog data.
758 ReportFailure(
759 INVALID_CATALOG_DATA,
760 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
761 ASCIIToUTF16("INVALID_CATALOG_DATA")));
762 return false;
765 base::FilePath relative_path = base::FilePath::FromUTF8Unsafe(it.key());
766 relative_path = relative_path.Append(kMessagesFilename);
767 if (relative_path.IsAbsolute() || relative_path.ReferencesParent()) {
768 // Invalid path for catalog.
769 ReportFailure(
770 INVALID_PATH_FOR_CATALOG,
771 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
772 ASCIIToUTF16("INVALID_PATH_FOR_CATALOG")));
773 return false;
775 base::FilePath path = extension_root_.Append(relative_path);
777 std::string catalog_json;
778 JSONStringValueSerializer serializer(&catalog_json);
779 serializer.set_pretty_print(true);
780 if (!serializer.Serialize(*catalog)) {
781 // Error serializing catalog.
782 ReportFailure(ERROR_SERIALIZING_CATALOG,
783 l10n_util::GetStringFUTF16(
784 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
785 ASCIIToUTF16("ERROR_SERIALIZING_CATALOG")));
786 return false;
789 // Note: we're overwriting existing files that the utility process read,
790 // so we can be sure the directory exists.
791 int size = base::checked_cast<int>(catalog_json.size());
792 if (base::WriteFile(path, catalog_json.c_str(), size) != size) {
793 // Error saving catalog.
794 ReportFailure(
795 ERROR_SAVING_CATALOG,
796 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
797 ASCIIToUTF16("ERROR_SAVING_CATALOG")));
798 return false;
802 return true;
805 void SandboxedUnpacker::Cleanup() {
806 DCHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
807 if (!temp_dir_.Delete()) {
808 LOG(WARNING) << "Can not delete temp directory at "
809 << temp_dir_.path().value();
813 } // namespace extensions