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