Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / extensions / sandboxed_unpacker.cc
blobf7625e70f468a9159249e1e6af1edb82f5891193
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 "chrome/browser/extensions/sandboxed_unpacker.h"
7 #include <set>
9 #include "base/base64.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/files/file_util_proxy.h"
14 #include "base/json/json_string_value_serializer.h"
15 #include "base/memory/scoped_handle.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/metrics/histogram.h"
18 #include "base/numerics/safe_conversions.h"
19 #include "base/path_service.h"
20 #include "base/sequenced_task_runner.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/threading/sequenced_worker_pool.h"
23 #include "chrome/browser/extensions/extension_service.h"
24 #include "chrome/common/chrome_paths.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/common/chrome_utility_messages.h"
27 #include "chrome/common/extensions/extension_file_util.h"
28 #include "chrome/common/extensions/extension_l10n_util.h"
29 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/utility_process_host.h"
32 #include "content/public/common/common_param_traits.h"
33 #include "crypto/signature_verifier.h"
34 #include "extensions/common/constants.h"
35 #include "extensions/common/crx_file.h"
36 #include "extensions/common/extension.h"
37 #include "extensions/common/id_util.h"
38 #include "extensions/common/manifest_constants.h"
39 #include "grit/generated_resources.h"
40 #include "third_party/skia/include/core/SkBitmap.h"
41 #include "ui/base/l10n/l10n_util.h"
42 #include "ui/gfx/codec/png_codec.h"
44 using base::ASCIIToUTF16;
45 using content::BrowserThread;
46 using content::UtilityProcessHost;
48 // The following macro makes histograms that record the length of paths
49 // in this file much easier to read.
50 // Windows has a short max path length. If the path length to a
51 // file being unpacked from a CRX exceeds the max length, we might
52 // fail to install. To see if this is happening, see how long the
53 // path to the temp unpack directory is. See crbug.com/69693 .
54 #define PATH_LENGTH_HISTOGRAM(name, path) \
55 UMA_HISTOGRAM_CUSTOM_COUNTS(name, path.value().length(), 0, 500, 100)
57 // Record a rate (kB per second) at which extensions are unpacked.
58 // Range from 1kB/s to 100mB/s.
59 #define UNPACK_RATE_HISTOGRAM(name, rate) \
60 UMA_HISTOGRAM_CUSTOM_COUNTS(name, rate, 1, 100000, 100);
62 namespace extensions {
63 namespace {
65 void RecordSuccessfulUnpackTimeHistograms(
66 const base::FilePath& crx_path, const base::TimeDelta unpack_time) {
68 const int64 kBytesPerKb = 1024;
69 const int64 kBytesPerMb = 1024 * 1024;
71 UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackSuccessTime", unpack_time);
73 // To get a sense of how CRX size impacts unpack time, record unpack
74 // time for several increments of CRX size.
75 int64 crx_file_size;
76 if (!base::GetFileSize(crx_path, &crx_file_size)) {
77 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccessCantGetCrxSize", 1);
78 return;
81 // Cast is safe as long as the number of bytes in the CRX is less than
82 // 2^31 * 2^10.
83 int crx_file_size_kb = static_cast<int>(crx_file_size / kBytesPerKb);
84 UMA_HISTOGRAM_COUNTS(
85 "Extensions.SandboxUnpackSuccessCrxSize", crx_file_size_kb);
87 // We have time in seconds and file size in bytes. We want the rate bytes are
88 // unpacked in kB/s.
89 double file_size_kb =
90 static_cast<double>(crx_file_size) / static_cast<double>(kBytesPerKb);
91 int unpack_rate_kb_per_s =
92 static_cast<int>(file_size_kb / unpack_time.InSecondsF());
93 UNPACK_RATE_HISTOGRAM("Extensions.SandboxUnpackRate", unpack_rate_kb_per_s);
95 if (crx_file_size < 50.0 * kBytesPerKb) {
96 UNPACK_RATE_HISTOGRAM(
97 "Extensions.SandboxUnpackRateUnder50kB", unpack_rate_kb_per_s);
99 } else if (crx_file_size < 1 * kBytesPerMb) {
100 UNPACK_RATE_HISTOGRAM(
101 "Extensions.SandboxUnpackRate50kBTo1mB", unpack_rate_kb_per_s);
103 } else if (crx_file_size < 2 * kBytesPerMb) {
104 UNPACK_RATE_HISTOGRAM(
105 "Extensions.SandboxUnpackRate1To2mB", unpack_rate_kb_per_s);
107 } else if (crx_file_size < 5 * kBytesPerMb) {
108 UNPACK_RATE_HISTOGRAM(
109 "Extensions.SandboxUnpackRate2To5mB", unpack_rate_kb_per_s);
111 } else if (crx_file_size < 10 * kBytesPerMb) {
112 UNPACK_RATE_HISTOGRAM(
113 "Extensions.SandboxUnpackRate5To10mB", unpack_rate_kb_per_s);
115 } else {
116 UNPACK_RATE_HISTOGRAM(
117 "Extensions.SandboxUnpackRateOver10mB", unpack_rate_kb_per_s);
121 // Work horse for FindWritableTempLocation. Creates a temp file in the folder
122 // and uses NormalizeFilePath to check if the path is junction free.
123 bool VerifyJunctionFreeLocation(base::FilePath* temp_dir) {
124 if (temp_dir->empty())
125 return false;
127 base::FilePath temp_file;
128 if (!base::CreateTemporaryFileInDir(*temp_dir, &temp_file)) {
129 LOG(ERROR) << temp_dir->value() << " is not writable";
130 return false;
132 // NormalizeFilePath requires a non-empty file, so write some data.
133 // If you change the exit points of this function please make sure all
134 // exit points delete this temp file!
135 if (file_util::WriteFile(temp_file, ".", 1) != 1)
136 return false;
138 base::FilePath normalized_temp_file;
139 bool normalized = base::NormalizeFilePath(temp_file, &normalized_temp_file);
140 if (!normalized) {
141 // If |temp_file| contains a link, the sandbox will block al file system
142 // operations, and the install will fail.
143 LOG(ERROR) << temp_dir->value() << " seem to be on remote drive.";
144 } else {
145 *temp_dir = normalized_temp_file.DirName();
147 // Clean up the temp file.
148 base::DeleteFile(temp_file, false);
150 return normalized;
153 // This function tries to find a location for unpacking the extension archive
154 // that is writable and does not lie on a shared drive so that the sandboxed
155 // unpacking process can write there. If no such location exists we can not
156 // proceed and should fail.
157 // The result will be written to |temp_dir|. The function will write to this
158 // parameter even if it returns false.
159 bool FindWritableTempLocation(const base::FilePath& extensions_dir,
160 base::FilePath* temp_dir) {
161 // On ChromeOS, we will only attempt to unpack extension in cryptohome (profile)
162 // directory to provide additional security/privacy and speed up the rest of
163 // the extension install process.
164 #if !defined(OS_CHROMEOS)
165 PathService::Get(base::DIR_TEMP, temp_dir);
166 if (VerifyJunctionFreeLocation(temp_dir))
167 return true;
168 #endif
170 *temp_dir = extension_file_util::GetInstallTempDir(extensions_dir);
171 if (VerifyJunctionFreeLocation(temp_dir))
172 return true;
173 // Neither paths is link free chances are good installation will fail.
174 LOG(ERROR) << "Both the %TEMP% folder and the profile seem to be on "
175 << "remote drives or read-only. Installation can not complete!";
176 return false;
179 // Read the decoded images back from the file we saved them to.
180 // |extension_path| is the path to the extension we unpacked that wrote the
181 // data. Returns true on success.
182 bool ReadImagesFromFile(const base::FilePath& extension_path,
183 DecodedImages* images) {
184 base::FilePath path =
185 extension_path.AppendASCII(kDecodedImagesFilename);
186 std::string file_str;
187 if (!base::ReadFileToString(path, &file_str))
188 return false;
190 IPC::Message pickle(file_str.data(), file_str.size());
191 PickleIterator iter(pickle);
192 return IPC::ReadParam(&pickle, &iter, images);
195 // Read the decoded message catalogs back from the file we saved them to.
196 // |extension_path| is the path to the extension we unpacked that wrote the
197 // data. Returns true on success.
198 bool ReadMessageCatalogsFromFile(const base::FilePath& extension_path,
199 base::DictionaryValue* catalogs) {
200 base::FilePath path = extension_path.AppendASCII(
201 kDecodedMessageCatalogsFilename);
202 std::string file_str;
203 if (!base::ReadFileToString(path, &file_str))
204 return false;
206 IPC::Message pickle(file_str.data(), file_str.size());
207 PickleIterator iter(pickle);
208 return IPC::ReadParam(&pickle, &iter, catalogs);
211 } // namespace
213 SandboxedUnpacker::SandboxedUnpacker(
214 const base::FilePath& crx_path,
215 Manifest::Location location,
216 int creation_flags,
217 const base::FilePath& extensions_dir,
218 base::SequencedTaskRunner* unpacker_io_task_runner,
219 SandboxedUnpackerClient* client)
220 : crx_path_(crx_path),
221 client_(client),
222 extensions_dir_(extensions_dir),
223 got_response_(false),
224 location_(location),
225 creation_flags_(creation_flags),
226 unpacker_io_task_runner_(unpacker_io_task_runner) {
229 bool SandboxedUnpacker::CreateTempDirectory() {
230 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
232 base::FilePath temp_dir;
233 if (!FindWritableTempLocation(extensions_dir_, &temp_dir)) {
234 ReportFailure(
235 COULD_NOT_GET_TEMP_DIRECTORY,
236 l10n_util::GetStringFUTF16(
237 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
238 ASCIIToUTF16("COULD_NOT_GET_TEMP_DIRECTORY")));
239 return false;
242 if (!temp_dir_.CreateUniqueTempDirUnderPath(temp_dir)) {
243 ReportFailure(
244 COULD_NOT_CREATE_TEMP_DIRECTORY,
245 l10n_util::GetStringFUTF16(
246 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
247 ASCIIToUTF16("COULD_NOT_CREATE_TEMP_DIRECTORY")));
248 return false;
251 return true;
254 void SandboxedUnpacker::Start() {
255 // We assume that we are started on the thread that the client wants us to do
256 // file IO on.
257 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
259 unpack_start_time_ = base::TimeTicks::Now();
261 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackInitialCrxPathLength",
262 crx_path_);
263 if (!CreateTempDirectory())
264 return; // ReportFailure() already called.
266 // Initialize the path that will eventually contain the unpacked extension.
267 extension_root_ = temp_dir_.path().AppendASCII(kTempExtensionName);
268 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackUnpackedCrxPathLength",
269 extension_root_);
271 // Extract the public key and validate the package.
272 if (!ValidateSignature())
273 return; // ValidateSignature() already reported the error.
275 // Copy the crx file into our working directory.
276 base::FilePath temp_crx_path = temp_dir_.path().Append(crx_path_.BaseName());
277 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackTempCrxPathLength",
278 temp_crx_path);
280 if (!base::CopyFile(crx_path_, temp_crx_path)) {
281 // Failed to copy extension file to temporary directory.
282 ReportFailure(
283 FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY,
284 l10n_util::GetStringFUTF16(
285 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
286 ASCIIToUTF16("FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY")));
287 return;
290 // The utility process will have access to the directory passed to
291 // SandboxedUnpacker. That directory should not contain a symlink or NTFS
292 // reparse point. When the path is used, following the link/reparse point
293 // will cause file system access outside the sandbox path, and the sandbox
294 // will deny the operation.
295 base::FilePath link_free_crx_path;
296 if (!base::NormalizeFilePath(temp_crx_path, &link_free_crx_path)) {
297 LOG(ERROR) << "Could not get the normalized path of "
298 << temp_crx_path.value();
299 ReportFailure(
300 COULD_NOT_GET_SANDBOX_FRIENDLY_PATH,
301 l10n_util::GetStringUTF16(IDS_EXTENSION_UNPACK_FAILED));
302 return;
304 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength",
305 link_free_crx_path);
307 BrowserThread::PostTask(
308 BrowserThread::IO, FROM_HERE,
309 base::Bind(
310 &SandboxedUnpacker::StartProcessOnIOThread,
311 this,
312 link_free_crx_path));
315 SandboxedUnpacker::~SandboxedUnpacker() {
318 bool SandboxedUnpacker::OnMessageReceived(const IPC::Message& message) {
319 bool handled = true;
320 IPC_BEGIN_MESSAGE_MAP(SandboxedUnpacker, message)
321 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_UnpackExtension_Succeeded,
322 OnUnpackExtensionSucceeded)
323 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_UnpackExtension_Failed,
324 OnUnpackExtensionFailed)
325 IPC_MESSAGE_UNHANDLED(handled = false)
326 IPC_END_MESSAGE_MAP()
327 return handled;
330 void SandboxedUnpacker::OnProcessCrashed(int exit_code) {
331 // Don't report crashes if they happen after we got a response.
332 if (got_response_)
333 return;
335 // Utility process crashed while trying to install.
336 ReportFailure(
337 UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL,
338 l10n_util::GetStringFUTF16(
339 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
340 ASCIIToUTF16("UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL")) +
341 ASCIIToUTF16(". ") +
342 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_PROCESS_CRASHED));
345 void SandboxedUnpacker::StartProcessOnIOThread(
346 const base::FilePath& temp_crx_path) {
347 UtilityProcessHost* host =
348 UtilityProcessHost::Create(this, unpacker_io_task_runner_.get());
349 // Grant the subprocess access to the entire subdir the extension file is
350 // in, so that it can unpack to that dir.
351 host->SetExposedDir(temp_crx_path.DirName());
352 host->Send(
353 new ChromeUtilityMsg_UnpackExtension(
354 temp_crx_path, extension_id_, location_, creation_flags_));
357 void SandboxedUnpacker::OnUnpackExtensionSucceeded(
358 const base::DictionaryValue& manifest) {
359 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
360 got_response_ = true;
362 scoped_ptr<base::DictionaryValue> final_manifest(
363 RewriteManifestFile(manifest));
364 if (!final_manifest)
365 return;
367 // Create an extension object that refers to the temporary location the
368 // extension was unpacked to. We use this until the extension is finally
369 // installed. For example, the install UI shows images from inside the
370 // extension.
372 // Localize manifest now, so confirm UI gets correct extension name.
374 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
375 // with base::string16
376 std::string utf8_error;
377 if (!extension_l10n_util::LocalizeExtension(extension_root_,
378 final_manifest.get(),
379 &utf8_error)) {
380 ReportFailure(
381 COULD_NOT_LOCALIZE_EXTENSION,
382 l10n_util::GetStringFUTF16(
383 IDS_EXTENSION_PACKAGE_ERROR_MESSAGE,
384 base::UTF8ToUTF16(utf8_error)));
385 return;
388 extension_ = Extension::Create(
389 extension_root_,
390 location_,
391 *final_manifest,
392 Extension::REQUIRE_KEY | creation_flags_,
393 &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(
417 IDS_EXTENSION_PACKAGE_ERROR_MESSAGE,
418 error));
421 bool SandboxedUnpacker::ValidateSignature() {
422 ScopedStdioHandle file(base::OpenFile(crx_path_, "rb"));
424 if (!file.get()) {
425 // Could not open crx file for reading.
426 #if defined (OS_WIN)
427 // On windows, get the error code.
428 uint32 error_code = ::GetLastError();
429 // TODO(skerner): Use this histogram to understand why so many
430 // windows users hit this error. crbug.com/69693
432 // Windows errors are unit32s, but all of likely errors are in
433 // [1, 1000]. See winerror.h for the meaning of specific values.
434 // Clip errors outside the expected range to a single extra value.
435 // If there are errors in that extra bucket, we will know to expand
436 // the range.
437 const uint32 kMaxErrorToSend = 1001;
438 error_code = std::min(error_code, kMaxErrorToSend);
439 UMA_HISTOGRAM_ENUMERATION("Extensions.ErrorCodeFromCrxOpen",
440 error_code, kMaxErrorToSend);
441 #endif
443 ReportFailure(
444 CRX_FILE_NOT_READABLE,
445 l10n_util::GetStringFUTF16(
446 IDS_EXTENSION_PACKAGE_ERROR_CODE,
447 ASCIIToUTF16("CRX_FILE_NOT_READABLE")));
448 return false;
451 // Read and verify the header.
452 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
453 // appears that we don't have any endian/alignment aware serialization
454 // code in the code base. So for now, this assumes that we're running
455 // on a little endian machine with 4 byte alignment.
456 CrxFile::Header header;
457 size_t len = fread(&header, 1, sizeof(header), file.get());
458 if (len < sizeof(header)) {
459 // Invalid crx header
460 ReportFailure(
461 CRX_HEADER_INVALID,
462 l10n_util::GetStringFUTF16(
463 IDS_EXTENSION_PACKAGE_ERROR_CODE,
464 ASCIIToUTF16("CRX_HEADER_INVALID")));
465 return false;
468 CrxFile::Error error;
469 scoped_ptr<CrxFile> crx(CrxFile::Parse(header, &error));
470 if (!crx) {
471 switch (error) {
472 case CrxFile::kWrongMagic:
473 ReportFailure(
474 CRX_MAGIC_NUMBER_INVALID,
475 l10n_util::GetStringFUTF16(
476 IDS_EXTENSION_PACKAGE_ERROR_CODE,
477 ASCIIToUTF16("CRX_MAGIC_NUMBER_INVALID")));
478 break;
479 case CrxFile::kInvalidVersion:
480 // Bad version numer
481 ReportFailure(
482 CRX_VERSION_NUMBER_INVALID,
483 l10n_util::GetStringFUTF16(
484 IDS_EXTENSION_PACKAGE_ERROR_CODE,
485 ASCIIToUTF16("CRX_VERSION_NUMBER_INVALID")));
486 break;
487 case CrxFile::kInvalidKeyTooLarge:
488 case CrxFile::kInvalidSignatureTooLarge:
489 // Excessively large key or signature
490 ReportFailure(
491 CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE,
492 l10n_util::GetStringFUTF16(
493 IDS_EXTENSION_PACKAGE_ERROR_CODE,
494 ASCIIToUTF16("CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE")));
495 break;
496 case CrxFile::kInvalidKeyTooSmall:
497 // Key length is zero
498 ReportFailure(
499 CRX_ZERO_KEY_LENGTH,
500 l10n_util::GetStringFUTF16(
501 IDS_EXTENSION_PACKAGE_ERROR_CODE,
502 ASCIIToUTF16("CRX_ZERO_KEY_LENGTH")));
503 break;
504 case CrxFile::kInvalidSignatureTooSmall:
505 // Signature length is zero
506 ReportFailure(
507 CRX_ZERO_SIGNATURE_LENGTH,
508 l10n_util::GetStringFUTF16(
509 IDS_EXTENSION_PACKAGE_ERROR_CODE,
510 ASCIIToUTF16("CRX_ZERO_SIGNATURE_LENGTH")));
511 break;
513 return false;
516 std::vector<uint8> key;
517 key.resize(header.key_size);
518 len = fread(&key.front(), sizeof(uint8), header.key_size, file.get());
519 if (len < header.key_size) {
520 // Invalid public key
521 ReportFailure(
522 CRX_PUBLIC_KEY_INVALID,
523 l10n_util::GetStringFUTF16(
524 IDS_EXTENSION_PACKAGE_ERROR_CODE,
525 ASCIIToUTF16("CRX_PUBLIC_KEY_INVALID")));
526 return false;
529 std::vector<uint8> signature;
530 signature.resize(header.signature_size);
531 len = fread(&signature.front(), sizeof(uint8), header.signature_size,
532 file.get());
533 if (len < header.signature_size) {
534 // Invalid signature
535 ReportFailure(
536 CRX_SIGNATURE_INVALID,
537 l10n_util::GetStringFUTF16(
538 IDS_EXTENSION_PACKAGE_ERROR_CODE,
539 ASCIIToUTF16("CRX_SIGNATURE_INVALID")));
540 return false;
543 crypto::SignatureVerifier verifier;
544 if (!verifier.VerifyInit(extension_misc::kSignatureAlgorithm,
545 sizeof(extension_misc::kSignatureAlgorithm),
546 &signature.front(),
547 signature.size(),
548 &key.front(),
549 key.size())) {
550 // Signature verification initialization failed. This is most likely
551 // caused by a public key in the wrong format (should encode algorithm).
552 ReportFailure(
553 CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED,
554 l10n_util::GetStringFUTF16(
555 IDS_EXTENSION_PACKAGE_ERROR_CODE,
556 ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED")));
557 return false;
560 unsigned char buf[1 << 12];
561 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0)
562 verifier.VerifyUpdate(buf, len);
564 if (!verifier.VerifyFinal()) {
565 // Signature verification failed
566 ReportFailure(
567 CRX_SIGNATURE_VERIFICATION_FAILED,
568 l10n_util::GetStringFUTF16(
569 IDS_EXTENSION_PACKAGE_ERROR_CODE,
570 ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_FAILED")));
571 return false;
574 std::string public_key =
575 std::string(reinterpret_cast<char*>(&key.front()), key.size());
576 base::Base64Encode(public_key, &public_key_);
578 extension_id_ = id_util::GenerateId(public_key);
580 return true;
583 void SandboxedUnpacker::ReportFailure(FailureReason reason,
584 const base::string16& error) {
585 UMA_HISTOGRAM_ENUMERATION("Extensions.SandboxUnpackFailureReason",
586 reason, NUM_FAILURE_REASONS);
587 UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackFailureTime",
588 base::TimeTicks::Now() - unpack_start_time_);
589 Cleanup();
590 client_->OnUnpackFailure(error);
593 void SandboxedUnpacker::ReportSuccess(
594 const base::DictionaryValue& original_manifest,
595 const SkBitmap& install_icon) {
596 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1);
598 RecordSuccessfulUnpackTimeHistograms(
599 crx_path_, base::TimeTicks::Now() - unpack_start_time_);
601 // Client takes ownership of temporary directory and extension.
602 client_->OnUnpackSuccess(
603 temp_dir_.Take(), extension_root_, &original_manifest, extension_.get(),
604 install_icon);
605 extension_ = NULL;
608 base::DictionaryValue* SandboxedUnpacker::RewriteManifestFile(
609 const base::DictionaryValue& manifest) {
610 // Add the public key extracted earlier to the parsed manifest and overwrite
611 // the original manifest. We do this to ensure the manifest doesn't contain an
612 // exploitable bug that could be used to compromise the browser.
613 scoped_ptr<base::DictionaryValue> final_manifest(manifest.DeepCopy());
614 final_manifest->SetString(manifest_keys::kPublicKey, public_key_);
616 std::string manifest_json;
617 JSONStringValueSerializer serializer(&manifest_json);
618 serializer.set_pretty_print(true);
619 if (!serializer.Serialize(*final_manifest)) {
620 // Error serializing manifest.json.
621 ReportFailure(
622 ERROR_SERIALIZING_MANIFEST_JSON,
623 l10n_util::GetStringFUTF16(
624 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
625 ASCIIToUTF16("ERROR_SERIALIZING_MANIFEST_JSON")));
626 return NULL;
629 base::FilePath manifest_path =
630 extension_root_.Append(kManifestFilename);
631 int size = base::checked_cast<int>(manifest_json.size());
632 if (file_util::WriteFile(manifest_path, manifest_json.data(), size) != size) {
633 // Error saving manifest.json.
634 ReportFailure(
635 ERROR_SAVING_MANIFEST_JSON,
636 l10n_util::GetStringFUTF16(
637 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
638 ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON")));
639 return NULL;
642 return final_manifest.release();
645 bool SandboxedUnpacker::RewriteImageFiles(SkBitmap* install_icon) {
646 DecodedImages images;
647 if (!ReadImagesFromFile(temp_dir_.path(), &images)) {
648 // Couldn't read image data from disk.
649 ReportFailure(
650 COULD_NOT_READ_IMAGE_DATA_FROM_DISK,
651 l10n_util::GetStringFUTF16(
652 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
653 ASCIIToUTF16("COULD_NOT_READ_IMAGE_DATA_FROM_DISK")));
654 return false;
657 // Delete any images that may be used by the browser. We're going to write
658 // out our own versions of the parsed images, and we want to make sure the
659 // originals are gone for good.
660 std::set<base::FilePath> image_paths =
661 extension_file_util::GetBrowserImagePaths(extension_.get());
662 if (image_paths.size() != images.size()) {
663 // Decoded images don't match what's in the manifest.
664 ReportFailure(
665 DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST,
666 l10n_util::GetStringFUTF16(
667 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
668 ASCIIToUTF16("DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST")));
669 return false;
672 for (std::set<base::FilePath>::iterator it = image_paths.begin();
673 it != image_paths.end(); ++it) {
674 base::FilePath path = *it;
675 if (path.IsAbsolute() || path.ReferencesParent()) {
676 // Invalid path for browser image.
677 ReportFailure(
678 INVALID_PATH_FOR_BROWSER_IMAGE,
679 l10n_util::GetStringFUTF16(
680 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
681 ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE")));
682 return false;
684 if (!base::DeleteFile(extension_root_.Append(path), false)) {
685 // Error removing old image file.
686 ReportFailure(
687 ERROR_REMOVING_OLD_IMAGE_FILE,
688 l10n_util::GetStringFUTF16(
689 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
690 ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE")));
691 return false;
695 std::string install_icon_path = IconsInfo::GetIcons(extension_).Get(
696 extension_misc::EXTENSION_ICON_LARGE,
697 ExtensionIconSet::MATCH_BIGGER);
699 // Write our parsed images back to disk as well.
700 for (size_t i = 0; i < images.size(); ++i) {
701 if (BrowserThread::GetBlockingPool()->IsShutdownInProgress()) {
702 // Abort package installation if shutdown was initiated, crbug.com/235525
703 ReportFailure(
704 ABORTED_DUE_TO_SHUTDOWN,
705 l10n_util::GetStringFUTF16(
706 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
707 ASCIIToUTF16("ABORTED_DUE_TO_SHUTDOWN")));
708 return false;
711 const SkBitmap& image = images[i].a;
712 base::FilePath path_suffix = images[i].b;
713 if (path_suffix.MaybeAsASCII() == install_icon_path)
714 *install_icon = image;
716 if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) {
717 // Invalid path for bitmap image.
718 ReportFailure(
719 INVALID_PATH_FOR_BITMAP_IMAGE,
720 l10n_util::GetStringFUTF16(
721 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
722 ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE")));
723 return false;
725 base::FilePath path = extension_root_.Append(path_suffix);
727 std::vector<unsigned char> image_data;
728 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
729 // though they may originally be .jpg, etc. Figure something out.
730 // http://code.google.com/p/chromium/issues/detail?id=12459
731 if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) {
732 // Error re-encoding theme image.
733 ReportFailure(
734 ERROR_RE_ENCODING_THEME_IMAGE,
735 l10n_util::GetStringFUTF16(
736 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
737 ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE")));
738 return false;
741 // Note: we're overwriting existing files that the utility process wrote,
742 // so we can be sure the directory exists.
743 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
744 int size = base::checked_cast<int>(image_data.size());
745 if (file_util::WriteFile(path, image_data_ptr, size) != size) {
746 // Error saving theme image.
747 ReportFailure(
748 ERROR_SAVING_THEME_IMAGE,
749 l10n_util::GetStringFUTF16(
750 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
751 ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE")));
752 return false;
756 return true;
759 bool SandboxedUnpacker::RewriteCatalogFiles() {
760 base::DictionaryValue catalogs;
761 if (!ReadMessageCatalogsFromFile(temp_dir_.path(), &catalogs)) {
762 // Could not read catalog data from disk.
763 ReportFailure(
764 COULD_NOT_READ_CATALOG_DATA_FROM_DISK,
765 l10n_util::GetStringFUTF16(
766 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
767 ASCIIToUTF16("COULD_NOT_READ_CATALOG_DATA_FROM_DISK")));
768 return false;
771 // Write our parsed catalogs back to disk.
772 for (base::DictionaryValue::Iterator it(catalogs);
773 !it.IsAtEnd(); it.Advance()) {
774 const base::DictionaryValue* catalog = NULL;
775 if (!it.value().GetAsDictionary(&catalog)) {
776 // Invalid catalog data.
777 ReportFailure(
778 INVALID_CATALOG_DATA,
779 l10n_util::GetStringFUTF16(
780 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
781 ASCIIToUTF16("INVALID_CATALOG_DATA")));
782 return false;
785 base::FilePath relative_path = base::FilePath::FromUTF8Unsafe(it.key());
786 relative_path = relative_path.Append(kMessagesFilename);
787 if (relative_path.IsAbsolute() || relative_path.ReferencesParent()) {
788 // Invalid path for catalog.
789 ReportFailure(
790 INVALID_PATH_FOR_CATALOG,
791 l10n_util::GetStringFUTF16(
792 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
793 ASCIIToUTF16("INVALID_PATH_FOR_CATALOG")));
794 return false;
796 base::FilePath path = extension_root_.Append(relative_path);
798 std::string catalog_json;
799 JSONStringValueSerializer serializer(&catalog_json);
800 serializer.set_pretty_print(true);
801 if (!serializer.Serialize(*catalog)) {
802 // Error serializing catalog.
803 ReportFailure(
804 ERROR_SERIALIZING_CATALOG,
805 l10n_util::GetStringFUTF16(
806 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
807 ASCIIToUTF16("ERROR_SERIALIZING_CATALOG")));
808 return false;
811 // Note: we're overwriting existing files that the utility process read,
812 // so we can be sure the directory exists.
813 int size = base::checked_cast<int>(catalog_json.size());
814 if (file_util::WriteFile(path, catalog_json.c_str(), size) != size) {
815 // Error saving catalog.
816 ReportFailure(
817 ERROR_SAVING_CATALOG,
818 l10n_util::GetStringFUTF16(
819 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
820 ASCIIToUTF16("ERROR_SAVING_CATALOG")));
821 return false;
825 return true;
828 void SandboxedUnpacker::Cleanup() {
829 DCHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
830 if (!temp_dir_.Delete()) {
831 LOG(WARNING) << "Can not delete temp directory at "
832 << temp_dir_.path().value();
836 } // namespace extensions