OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "extensions/browser/sandboxed_unpacker.h" | 5 #include "extensions/browser/sandboxed_unpacker.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 | 8 |
9 #include "base/base64.h" | 9 #include "base/base64.h" |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
12 #include "base/files/file_util.h" | 12 #include "base/files/file_util.h" |
13 #include "base/files/file_util_proxy.h" | 13 #include "base/files/file_util_proxy.h" |
14 #include "base/files/scoped_file.h" | 14 #include "base/files/scoped_file.h" |
15 #include "base/json/json_string_value_serializer.h" | 15 #include "base/json/json_string_value_serializer.h" |
16 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
17 #include "base/metrics/histogram.h" | 17 #include "base/metrics/histogram.h" |
18 #include "base/numerics/safe_conversions.h" | 18 #include "base/numerics/safe_conversions.h" |
19 #include "base/path_service.h" | 19 #include "base/path_service.h" |
20 #include "base/sequenced_task_runner.h" | 20 #include "base/sequenced_task_runner.h" |
21 #include "base/strings/string_number_conversions.h" | |
21 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
22 #include "base/threading/sequenced_worker_pool.h" | 23 #include "base/threading/sequenced_worker_pool.h" |
23 #include "components/crx_file/constants.h" | 24 #include "components/crx_file/constants.h" |
24 #include "components/crx_file/crx_file.h" | 25 #include "components/crx_file/crx_file.h" |
25 #include "components/crx_file/id_util.h" | 26 #include "components/crx_file/id_util.h" |
26 #include "content/public/browser/browser_thread.h" | 27 #include "content/public/browser/browser_thread.h" |
27 #include "content/public/browser/utility_process_host.h" | 28 #include "content/public/browser/utility_process_host.h" |
28 #include "content/public/common/common_param_traits.h" | 29 #include "content/public/common/common_param_traits.h" |
30 #include "crypto/secure_hash.h" | |
31 #include "crypto/sha2.h" | |
29 #include "crypto/signature_verifier.h" | 32 #include "crypto/signature_verifier.h" |
30 #include "extensions/common/constants.h" | 33 #include "extensions/common/constants.h" |
31 #include "extensions/common/extension.h" | 34 #include "extensions/common/extension.h" |
32 #include "extensions/common/extension_l10n_util.h" | 35 #include "extensions/common/extension_l10n_util.h" |
33 #include "extensions/common/extension_utility_messages.h" | 36 #include "extensions/common/extension_utility_messages.h" |
34 #include "extensions/common/extensions_client.h" | 37 #include "extensions/common/extensions_client.h" |
35 #include "extensions/common/file_util.h" | 38 #include "extensions/common/file_util.h" |
36 #include "extensions/common/manifest_constants.h" | 39 #include "extensions/common/manifest_constants.h" |
37 #include "extensions/common/manifest_handlers/icons_handler.h" | 40 #include "extensions/common/manifest_handlers/icons_handler.h" |
41 #include "extensions/common/switches.h" | |
38 #include "grit/extensions_strings.h" | 42 #include "grit/extensions_strings.h" |
39 #include "third_party/skia/include/core/SkBitmap.h" | 43 #include "third_party/skia/include/core/SkBitmap.h" |
40 #include "ui/base/l10n/l10n_util.h" | 44 #include "ui/base/l10n/l10n_util.h" |
41 #include "ui/gfx/codec/png_codec.h" | 45 #include "ui/gfx/codec/png_codec.h" |
42 | 46 |
43 using base::ASCIIToUTF16; | 47 using base::ASCIIToUTF16; |
44 using content::BrowserThread; | 48 using content::BrowserThread; |
45 using content::UtilityProcessHost; | 49 using content::UtilityProcessHost; |
46 using crx_file::CrxFile; | 50 using crx_file::CrxFile; |
47 | 51 |
48 // The following macro makes histograms that record the length of paths | 52 // The following macro makes histograms that record the length of paths |
49 // in this file much easier to read. | 53 // in this file much easier to read. |
50 // Windows has a short max path length. If the path length to a | 54 // 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 | 55 // 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 | 56 // 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 . | 57 // path to the temp unpack directory is. See crbug.com/69693 . |
54 #define PATH_LENGTH_HISTOGRAM(name, path) \ | 58 #define PATH_LENGTH_HISTOGRAM(name, path) \ |
55 UMA_HISTOGRAM_CUSTOM_COUNTS(name, path.value().length(), 0, 500, 100) | 59 UMA_HISTOGRAM_CUSTOM_COUNTS(name, path.value().length(), 0, 500, 100) |
56 | 60 |
57 // Record a rate (kB per second) at which extensions are unpacked. | 61 // Record a rate (kB per second) at which extensions are unpacked. |
58 // Range from 1kB/s to 100mB/s. | 62 // Range from 1kB/s to 100mB/s. |
59 #define UNPACK_RATE_HISTOGRAM(name, rate) \ | 63 #define UNPACK_RATE_HISTOGRAM(name, rate) \ |
60 UMA_HISTOGRAM_CUSTOM_COUNTS(name, rate, 1, 100000, 100); | 64 UMA_HISTOGRAM_CUSTOM_COUNTS(name, rate, 1, 100000, 100); |
61 | 65 |
66 // Record if the .crx hash sum is the same as in the updater manifest. | |
67 #define CRX_HASH_CHECK_HISTOGRAM(name, success) \ | |
Ilya Sherman
2015/02/03 21:40:29
Why do you need a wrapper macro?
| |
68 UMA_HISTOGRAM_BOOLEAN(name, success) | |
69 | |
62 namespace extensions { | 70 namespace extensions { |
63 namespace { | 71 namespace { |
64 | 72 |
65 void RecordSuccessfulUnpackTimeHistograms(const base::FilePath& crx_path, | 73 void RecordSuccessfulUnpackTimeHistograms(const base::FilePath& crx_path, |
66 const base::TimeDelta unpack_time) { | 74 const base::TimeDelta unpack_time) { |
67 const int64 kBytesPerKb = 1024; | 75 const int64 kBytesPerKb = 1024; |
68 const int64 kBytesPerMb = 1024 * 1024; | 76 const int64 kBytesPerMb = 1024 * 1024; |
69 | 77 |
70 UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackSuccessTime", unpack_time); | 78 UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackSuccessTime", unpack_time); |
71 | 79 |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
202 return false; | 210 return false; |
203 | 211 |
204 IPC::Message pickle(file_str.data(), file_str.size()); | 212 IPC::Message pickle(file_str.data(), file_str.size()); |
205 PickleIterator iter(pickle); | 213 PickleIterator iter(pickle); |
206 return IPC::ReadParam(&pickle, &iter, catalogs); | 214 return IPC::ReadParam(&pickle, &iter, catalogs); |
207 } | 215 } |
208 | 216 |
209 } // namespace | 217 } // namespace |
210 | 218 |
211 SandboxedUnpacker::SandboxedUnpacker( | 219 SandboxedUnpacker::SandboxedUnpacker( |
212 const base::FilePath& crx_path, | 220 const CRXFileInfo& file, |
213 Manifest::Location location, | 221 Manifest::Location location, |
214 int creation_flags, | 222 int creation_flags, |
215 const base::FilePath& extensions_dir, | 223 const base::FilePath& extensions_dir, |
216 const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner, | 224 const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner, |
217 SandboxedUnpackerClient* client) | 225 SandboxedUnpackerClient* client) |
218 : crx_path_(crx_path), | 226 : crx_path_(file.path), |
227 package_hash_(file.hash), | |
228 check_crx_hash_(false), | |
219 client_(client), | 229 client_(client), |
220 extensions_dir_(extensions_dir), | 230 extensions_dir_(extensions_dir), |
221 got_response_(false), | 231 got_response_(false), |
222 location_(location), | 232 location_(location), |
223 creation_flags_(creation_flags), | 233 creation_flags_(creation_flags), |
224 unpacker_io_task_runner_(unpacker_io_task_runner) { | 234 unpacker_io_task_runner_(unpacker_io_task_runner) { |
235 if (!package_hash_.empty()) { | |
236 check_crx_hash_ = base::CommandLine::ForCurrentProcess()->HasSwitch( | |
237 extensions::switches::kEnableCrxHashCheck); | |
238 } | |
225 } | 239 } |
226 | 240 |
227 bool SandboxedUnpacker::CreateTempDirectory() { | 241 bool SandboxedUnpacker::CreateTempDirectory() { |
228 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread()); | 242 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread()); |
229 | 243 |
230 base::FilePath temp_dir; | 244 base::FilePath temp_dir; |
231 if (!FindWritableTempLocation(extensions_dir_, &temp_dir)) { | 245 if (!FindWritableTempLocation(extensions_dir_, &temp_dir)) { |
232 ReportFailure(COULD_NOT_GET_TEMP_DIRECTORY, | 246 ReportFailure(COULD_NOT_GET_TEMP_DIRECTORY, |
233 l10n_util::GetStringFUTF16( | 247 l10n_util::GetStringFUTF16( |
234 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, | 248 IDS_EXTENSION_PACKAGE_INSTALL_ERROR, |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
395 } | 409 } |
396 | 410 |
397 void SandboxedUnpacker::OnUnpackExtensionFailed(const base::string16& error) { | 411 void SandboxedUnpacker::OnUnpackExtensionFailed(const base::string16& error) { |
398 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread()); | 412 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread()); |
399 got_response_ = true; | 413 got_response_ = true; |
400 ReportFailure( | 414 ReportFailure( |
401 UNPACKER_CLIENT_FAILED, | 415 UNPACKER_CLIENT_FAILED, |
402 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, error)); | 416 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, error)); |
403 } | 417 } |
404 | 418 |
419 static size_t ReadAndHash(void* ptr, | |
420 size_t size, | |
421 size_t nmemb, | |
422 FILE* stream, | |
423 scoped_ptr<crypto::SecureHash>& hash) { | |
424 size_t len = fread(ptr, size, nmemb, stream); | |
425 if (len > 0 && hash) { | |
426 hash->Update(ptr, len * size); | |
427 } | |
428 return len; | |
429 } | |
430 | |
431 bool SandboxedUnpacker::FinalizeHash(scoped_ptr<crypto::SecureHash>& hash) { | |
432 if (hash) { | |
433 uint8 output[crypto::kSHA256Length]; | |
434 hash->Finish(output, sizeof(output)); | |
435 if (base::StringToLowerASCII(base::HexEncode(output, sizeof(output))) != | |
436 package_hash_) { | |
437 // Package hash verification failed | |
438 CRX_HASH_CHECK_HISTOGRAM("Extensions.SandboxUnpackHashCheck", false); | |
439 if (check_crx_hash_) { | |
440 std::string name = crx_path_.BaseName().AsUTF8Unsafe(); | |
441 LOG(ERROR) << "Hash check failed for extension: " << name; | |
442 ReportFailure(CRX_HASH_VERIFICATION_FAILED, | |
443 l10n_util::GetStringFUTF16( | |
444 IDS_EXTENSION_PACKAGE_ERROR_CODE, | |
445 ASCIIToUTF16("CRX_HASH_VERIFICATION_FAILED"))); | |
446 return false; | |
447 } | |
448 } else { | |
449 CRX_HASH_CHECK_HISTOGRAM("Extensions.SandboxUnpackHashCheck", true); | |
Ilya Sherman
2015/02/03 21:40:29
I'd recommend having a single code path per histog
| |
450 } | |
451 } | |
452 | |
453 return true; | |
454 } | |
455 | |
405 bool SandboxedUnpacker::ValidateSignature() { | 456 bool SandboxedUnpacker::ValidateSignature() { |
406 base::ScopedFILE file(base::OpenFile(crx_path_, "rb")); | 457 base::ScopedFILE file(base::OpenFile(crx_path_, "rb")); |
407 | 458 |
459 scoped_ptr<crypto::SecureHash> hash; | |
460 | |
461 if (!package_hash_.empty()) { | |
462 hash.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); | |
463 } | |
464 | |
408 if (!file.get()) { | 465 if (!file.get()) { |
409 // Could not open crx file for reading. | 466 // Could not open crx file for reading. |
410 #if defined(OS_WIN) | 467 #if defined(OS_WIN) |
411 // On windows, get the error code. | 468 // On windows, get the error code. |
412 uint32 error_code = ::GetLastError(); | 469 uint32 error_code = ::GetLastError(); |
413 // TODO(skerner): Use this histogram to understand why so many | 470 // TODO(skerner): Use this histogram to understand why so many |
414 // windows users hit this error. crbug.com/69693 | 471 // windows users hit this error. crbug.com/69693 |
415 | 472 |
416 // Windows errors are unit32s, but all of likely errors are in | 473 // Windows errors are unit32s, but all of likely errors are in |
417 // [1, 1000]. See winerror.h for the meaning of specific values. | 474 // [1, 1000]. See winerror.h for the meaning of specific values. |
(...skipping 12 matching lines...) Expand all Loading... | |
430 ASCIIToUTF16("CRX_FILE_NOT_READABLE"))); | 487 ASCIIToUTF16("CRX_FILE_NOT_READABLE"))); |
431 return false; | 488 return false; |
432 } | 489 } |
433 | 490 |
434 // Read and verify the header. | 491 // Read and verify the header. |
435 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it | 492 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it |
436 // appears that we don't have any endian/alignment aware serialization | 493 // appears that we don't have any endian/alignment aware serialization |
437 // code in the code base. So for now, this assumes that we're running | 494 // code in the code base. So for now, this assumes that we're running |
438 // on a little endian machine with 4 byte alignment. | 495 // on a little endian machine with 4 byte alignment. |
439 CrxFile::Header header; | 496 CrxFile::Header header; |
440 size_t len = fread(&header, 1, sizeof(header), file.get()); | 497 size_t len = ReadAndHash(&header, 1, sizeof(header), file.get(), hash); |
441 if (len < sizeof(header)) { | 498 if (len < sizeof(header)) { |
442 // Invalid crx header | 499 // Invalid crx header |
443 ReportFailure(CRX_HEADER_INVALID, l10n_util::GetStringFUTF16( | 500 ReportFailure(CRX_HEADER_INVALID, l10n_util::GetStringFUTF16( |
444 IDS_EXTENSION_PACKAGE_ERROR_CODE, | 501 IDS_EXTENSION_PACKAGE_ERROR_CODE, |
445 ASCIIToUTF16("CRX_HEADER_INVALID"))); | 502 ASCIIToUTF16("CRX_HEADER_INVALID"))); |
446 return false; | 503 return false; |
447 } | 504 } |
448 | 505 |
449 CrxFile::Error error; | 506 CrxFile::Error error; |
450 scoped_ptr<CrxFile> crx(CrxFile::Parse(header, &error)); | 507 scoped_ptr<CrxFile> crx(CrxFile::Parse(header, &error)); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
485 l10n_util::GetStringFUTF16( | 542 l10n_util::GetStringFUTF16( |
486 IDS_EXTENSION_PACKAGE_ERROR_CODE, | 543 IDS_EXTENSION_PACKAGE_ERROR_CODE, |
487 ASCIIToUTF16("CRX_ZERO_SIGNATURE_LENGTH"))); | 544 ASCIIToUTF16("CRX_ZERO_SIGNATURE_LENGTH"))); |
488 break; | 545 break; |
489 } | 546 } |
490 return false; | 547 return false; |
491 } | 548 } |
492 | 549 |
493 std::vector<uint8> key; | 550 std::vector<uint8> key; |
494 key.resize(header.key_size); | 551 key.resize(header.key_size); |
495 len = fread(&key.front(), sizeof(uint8), header.key_size, file.get()); | 552 len = ReadAndHash(&key.front(), sizeof(uint8), header.key_size, file.get(), |
553 hash); | |
496 if (len < header.key_size) { | 554 if (len < header.key_size) { |
497 // Invalid public key | 555 // Invalid public key |
498 ReportFailure( | 556 ReportFailure( |
499 CRX_PUBLIC_KEY_INVALID, | 557 CRX_PUBLIC_KEY_INVALID, |
500 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_CODE, | 558 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_CODE, |
501 ASCIIToUTF16("CRX_PUBLIC_KEY_INVALID"))); | 559 ASCIIToUTF16("CRX_PUBLIC_KEY_INVALID"))); |
502 return false; | 560 return false; |
503 } | 561 } |
504 | 562 |
505 std::vector<uint8> signature; | 563 std::vector<uint8> signature; |
506 signature.resize(header.signature_size); | 564 signature.resize(header.signature_size); |
507 len = fread(&signature.front(), sizeof(uint8), header.signature_size, | 565 len = ReadAndHash(&signature.front(), sizeof(uint8), header.signature_size, |
508 file.get()); | 566 file.get(), hash); |
509 if (len < header.signature_size) { | 567 if (len < header.signature_size) { |
510 // Invalid signature | 568 // Invalid signature |
511 ReportFailure( | 569 ReportFailure( |
512 CRX_SIGNATURE_INVALID, | 570 CRX_SIGNATURE_INVALID, |
513 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_CODE, | 571 l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_CODE, |
514 ASCIIToUTF16("CRX_SIGNATURE_INVALID"))); | 572 ASCIIToUTF16("CRX_SIGNATURE_INVALID"))); |
515 return false; | 573 return false; |
516 } | 574 } |
517 | 575 |
518 crypto::SignatureVerifier verifier; | 576 crypto::SignatureVerifier verifier; |
519 if (!verifier.VerifyInit( | 577 if (!verifier.VerifyInit( |
520 crx_file::kSignatureAlgorithm, sizeof(crx_file::kSignatureAlgorithm), | 578 crx_file::kSignatureAlgorithm, sizeof(crx_file::kSignatureAlgorithm), |
521 &signature.front(), signature.size(), &key.front(), key.size())) { | 579 &signature.front(), signature.size(), &key.front(), key.size())) { |
522 // Signature verification initialization failed. This is most likely | 580 // Signature verification initialization failed. This is most likely |
523 // caused by a public key in the wrong format (should encode algorithm). | 581 // caused by a public key in the wrong format (should encode algorithm). |
524 ReportFailure( | 582 ReportFailure( |
525 CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED, | 583 CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED, |
526 l10n_util::GetStringFUTF16( | 584 l10n_util::GetStringFUTF16( |
527 IDS_EXTENSION_PACKAGE_ERROR_CODE, | 585 IDS_EXTENSION_PACKAGE_ERROR_CODE, |
528 ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED"))); | 586 ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED"))); |
529 return false; | 587 return false; |
530 } | 588 } |
531 | 589 |
532 unsigned char buf[1 << 12]; | 590 unsigned char buf[1 << 12]; |
533 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0) | 591 while ((len = ReadAndHash(buf, 1, sizeof(buf), file.get(), hash)) > 0) |
534 verifier.VerifyUpdate(buf, len); | 592 verifier.VerifyUpdate(buf, len); |
535 | 593 |
536 if (!verifier.VerifyFinal()) { | 594 if (!verifier.VerifyFinal()) { |
537 // Signature verification failed | 595 // Signature verification failed |
538 ReportFailure(CRX_SIGNATURE_VERIFICATION_FAILED, | 596 ReportFailure(CRX_SIGNATURE_VERIFICATION_FAILED, |
539 l10n_util::GetStringFUTF16( | 597 l10n_util::GetStringFUTF16( |
540 IDS_EXTENSION_PACKAGE_ERROR_CODE, | 598 IDS_EXTENSION_PACKAGE_ERROR_CODE, |
541 ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_FAILED"))); | 599 ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_FAILED"))); |
542 return false; | 600 return false; |
543 } | 601 } |
544 | 602 |
603 if (!FinalizeHash(hash)) { | |
604 return false; | |
605 } | |
606 | |
545 std::string public_key = | 607 std::string public_key = |
546 std::string(reinterpret_cast<char*>(&key.front()), key.size()); | 608 std::string(reinterpret_cast<char*>(&key.front()), key.size()); |
547 base::Base64Encode(public_key, &public_key_); | 609 base::Base64Encode(public_key, &public_key_); |
548 | 610 |
549 extension_id_ = crx_file::id_util::GenerateId(public_key); | 611 extension_id_ = crx_file::id_util::GenerateId(public_key); |
550 | 612 |
551 return true; | 613 return true; |
552 } | 614 } |
553 | 615 |
554 void SandboxedUnpacker::ReportFailure(FailureReason reason, | 616 void SandboxedUnpacker::ReportFailure(FailureReason reason, |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
783 | 845 |
784 void SandboxedUnpacker::Cleanup() { | 846 void SandboxedUnpacker::Cleanup() { |
785 DCHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread()); | 847 DCHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread()); |
786 if (!temp_dir_.Delete()) { | 848 if (!temp_dir_.Delete()) { |
787 LOG(WARNING) << "Can not delete temp directory at " | 849 LOG(WARNING) << "Can not delete temp directory at " |
788 << temp_dir_.path().value(); | 850 << temp_dir_.path().value(); |
789 } | 851 } |
790 } | 852 } |
791 | 853 |
792 } // namespace extensions | 854 } // namespace extensions |
OLD | NEW |