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"
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
{
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.
71 if (!base::GetFileSize(crx_path
, &crx_file_size
)) {
72 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccessCantGetCrxSize", 1);
76 // Cast is safe as long as the number of bytes in the CRX is less than
78 int crx_file_size_kb
= static_cast<int>(crx_file_size
/ kBytesPerKb
);
79 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccessCrxSize",
82 // We have time in seconds and file size in bytes. We want the rate bytes are
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
);
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())
122 base::FilePath temp_file
;
123 if (!base::CreateTemporaryFileInDir(*temp_dir
, &temp_file
)) {
124 LOG(ERROR
) << temp_dir
->value() << " is not writable";
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)
133 base::FilePath normalized_temp_file
;
134 bool normalized
= base::NormalizeFilePath(temp_file
, &normalized_temp_file
);
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.";
140 *temp_dir
= normalized_temp_file
.DirName();
142 // Clean up the temp file.
143 base::DeleteFile(temp_file
, false);
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
))
165 *temp_dir
= file_util::GetInstallTempDir(extensions_dir
);
166 if (VerifyJunctionFreeLocation(temp_dir
))
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!";
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
))
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
))
200 IPC::Message
pickle(file_str
.data(), file_str
.size());
201 base::PickleIterator
iter(pickle
);
202 return IPC::ReadParam(&pickle
, &iter
, catalogs
);
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
,
217 const base::FilePath
& extensions_dir
,
218 const scoped_refptr
<base::SequencedTaskRunner
>& unpacker_io_task_runner
,
219 SandboxedUnpackerClient
* client
)
221 extensions_dir_(extensions_dir
),
222 got_response_(false),
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")));
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")));
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
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",
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",
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",
284 if (!base::CopyFile(crx_info
.path
, temp_crx_path
)) {
285 // Failed to copy extension file to temporary directory.
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")));
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
));
307 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength",
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();
329 DIRECTORY_MOVE_FAILED
,
330 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR
,
331 ASCIIToUTF16("DIRECTORY_MOVE_FAILED")));
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.
346 bool SandboxedUnpacker::OnMessageReceived(const IPC::Message
& message
) {
348 IPC_BEGIN_MESSAGE_MAP(SandboxedUnpacker
, message
)
349 IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Succeeded
,
350 OnUnzipToDirSucceeded
)
351 IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Failed
,
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()
362 void SandboxedUnpacker::OnProcessCrashed(int exit_code
) {
363 // Don't report crashes if they happen after we got a response.
367 // Utility process crashed while trying to install.
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")) +
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_
)) {
381 COULD_NOT_START_UTILITY_PROCESS
,
382 l10n_util::GetStringFUTF16(
383 IDS_EXTENSION_PACKAGE_INSTALL_ERROR
,
384 FailureReasonToString16(COULD_NOT_START_UTILITY_PROCESS
)));
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_
)) {
399 COULD_NOT_START_UTILITY_PROCESS
,
400 l10n_util::GetStringFUTF16(
401 IDS_EXTENSION_PACKAGE_INSTALL_ERROR
,
402 FailureReasonToString16(COULD_NOT_START_UTILITY_PROCESS
)));
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
));
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
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
)) {
447 COULD_NOT_LOCALIZE_EXTENSION
,
448 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE
,
449 base::UTF8ToUTF16(utf8_error
)));
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
));
463 SkBitmap install_icon
;
464 if (!RewriteImageFiles(&install_icon
))
467 if (!RewriteCatalogFiles())
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;
478 UNPACKER_CLIENT_FAILED
,
479 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE
, error
));
482 base::string16
SandboxedUnpacker::FailureReasonToString16(
483 FailureReason 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");
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
:
570 return base::string16();
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);
588 case CrxFile::ValidateError::NONE
: {
589 if (!expected_hash
.empty())
590 UMA_HISTOGRAM_BOOLEAN("Extensions.SandboxUnpackHashCheck", true);
594 case CrxFile::ValidateError::CRX_FILE_NOT_READABLE
:
595 FailWithPackageError(CRX_FILE_NOT_READABLE
);
597 case CrxFile::ValidateError::CRX_HEADER_INVALID
:
598 FailWithPackageError(CRX_HEADER_INVALID
);
600 case CrxFile::ValidateError::CRX_MAGIC_NUMBER_INVALID
:
601 FailWithPackageError(CRX_MAGIC_NUMBER_INVALID
);
603 case CrxFile::ValidateError::CRX_VERSION_NUMBER_INVALID
:
604 FailWithPackageError(CRX_VERSION_NUMBER_INVALID
);
606 case CrxFile::ValidateError::CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE
:
607 FailWithPackageError(CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE
);
609 case CrxFile::ValidateError::CRX_ZERO_KEY_LENGTH
:
610 FailWithPackageError(CRX_ZERO_KEY_LENGTH
);
612 case CrxFile::ValidateError::CRX_ZERO_SIGNATURE_LENGTH
:
613 FailWithPackageError(CRX_ZERO_SIGNATURE_LENGTH
);
615 case CrxFile::ValidateError::CRX_PUBLIC_KEY_INVALID
:
616 FailWithPackageError(CRX_PUBLIC_KEY_INVALID
);
618 case CrxFile::ValidateError::CRX_SIGNATURE_INVALID
:
619 FailWithPackageError(CRX_SIGNATURE_INVALID
);
621 case CrxFile::ValidateError::
622 CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED
:
623 FailWithPackageError(CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED
);
625 case CrxFile::ValidateError::CRX_SIGNATURE_VERIFICATION_FAILED
:
626 FailWithPackageError(CRX_SIGNATURE_VERIFICATION_FAILED
);
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
);
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_
);
649 CrxInstallError
error_info(reason
== CRX_HASH_VERIFICATION_FAILED
650 ? CrxInstallError::ERROR_HASH_MISMATCH
651 : CrxInstallError::ERROR_OTHER
,
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
);
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")));
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.
701 ERROR_SAVING_MANIFEST_JSON
,
702 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR
,
703 ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON")));
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")));
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.
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")));
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")));
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")));
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
768 ABORTED_DUE_TO_SHUTDOWN
,
769 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR
,
770 ASCIIToUTF16("ABORTED_DUE_TO_SHUTDOWN")));
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")));
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")));
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.
809 ERROR_SAVING_THEME_IMAGE
,
810 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR
,
811 ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE")));
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")));
830 // Write our parsed catalogs back to disk.
831 for (base::DictionaryValue::Iterator
it(catalogs
); !it
.IsAtEnd();
833 const base::DictionaryValue
* catalog
= NULL
;
834 if (!it
.value().GetAsDictionary(&catalog
)) {
835 // Invalid catalog data.
837 INVALID_CATALOG_DATA
,
838 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR
,
839 ASCIIToUTF16("INVALID_CATALOG_DATA")));
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.
848 INVALID_PATH_FOR_CATALOG
,
849 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR
,
850 ASCIIToUTF16("INVALID_PATH_FOR_CATALOG")));
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")));
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.
873 ERROR_SAVING_CATALOG
,
874 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR
,
875 ASCIIToUTF16("ERROR_SAVING_CATALOG")));
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_
) {
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();
915 content::UtilityProcessHost
* SandboxedUnpacker::UtilityHostWrapper::host()
917 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
918 return utility_host_
.get();
921 SandboxedUnpacker::UtilityHostWrapper::~UtilityHostWrapper() {
922 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
924 utility_host_
->EndBatchMode();
925 utility_host_
.reset();
929 } // namespace extensions