Chromium Code Reviews| Index: extensions/browser/sandboxed_unpacker.cc |
| diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc |
| index cd535a83dbed0c2e9397e188d8300973d417f084..cc40e45677711036170549c9ff481e7c0050c1c2 100644 |
| --- a/extensions/browser/sandboxed_unpacker.cc |
| +++ b/extensions/browser/sandboxed_unpacker.cc |
| @@ -18,6 +18,7 @@ |
| #include "base/numerics/safe_conversions.h" |
| #include "base/path_service.h" |
| #include "base/sequenced_task_runner.h" |
| +#include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/sequenced_worker_pool.h" |
| #include "components/crx_file/constants.h" |
| @@ -26,6 +27,8 @@ |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/utility_process_host.h" |
| #include "content/public/common/common_param_traits.h" |
| +#include "crypto/secure_hash.h" |
| +#include "crypto/sha2.h" |
| #include "crypto/signature_verifier.h" |
| #include "extensions/common/constants.h" |
| #include "extensions/common/extension.h" |
| @@ -35,6 +38,7 @@ |
| #include "extensions/common/file_util.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "extensions/common/manifest_handlers/icons_handler.h" |
| +#include "extensions/common/switches.h" |
| #include "grit/extensions_strings.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/base/l10n/l10n_util.h" |
| @@ -59,6 +63,10 @@ using crx_file::CrxFile; |
| #define UNPACK_RATE_HISTOGRAM(name, rate) \ |
| UMA_HISTOGRAM_CUSTOM_COUNTS(name, rate, 1, 100000, 100); |
| +// Record if the .crx hash sum is the same as in the updater manifest. |
| +#define CRX_HASH_CHECK_HISTOGRAM(name, success) \ |
|
Ilya Sherman
2015/02/03 21:40:29
Why do you need a wrapper macro?
|
| + UMA_HISTOGRAM_BOOLEAN(name, success) |
| + |
| namespace extensions { |
| namespace { |
| @@ -209,19 +217,25 @@ bool ReadMessageCatalogsFromFile(const base::FilePath& extension_path, |
| } // namespace |
| SandboxedUnpacker::SandboxedUnpacker( |
| - const base::FilePath& crx_path, |
| + const CRXFileInfo& file, |
| Manifest::Location location, |
| int creation_flags, |
| const base::FilePath& extensions_dir, |
| const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner, |
| SandboxedUnpackerClient* client) |
| - : crx_path_(crx_path), |
| + : crx_path_(file.path), |
| + package_hash_(file.hash), |
| + check_crx_hash_(false), |
| client_(client), |
| extensions_dir_(extensions_dir), |
| got_response_(false), |
| location_(location), |
| creation_flags_(creation_flags), |
| unpacker_io_task_runner_(unpacker_io_task_runner) { |
| + if (!package_hash_.empty()) { |
| + check_crx_hash_ = base::CommandLine::ForCurrentProcess()->HasSwitch( |
| + extensions::switches::kEnableCrxHashCheck); |
| + } |
| } |
| bool SandboxedUnpacker::CreateTempDirectory() { |
| @@ -402,9 +416,52 @@ void SandboxedUnpacker::OnUnpackExtensionFailed(const base::string16& error) { |
| l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, error)); |
| } |
| +static size_t ReadAndHash(void* ptr, |
| + size_t size, |
| + size_t nmemb, |
| + FILE* stream, |
| + scoped_ptr<crypto::SecureHash>& hash) { |
| + size_t len = fread(ptr, size, nmemb, stream); |
| + if (len > 0 && hash) { |
| + hash->Update(ptr, len * size); |
| + } |
| + return len; |
| +} |
| + |
| +bool SandboxedUnpacker::FinalizeHash(scoped_ptr<crypto::SecureHash>& hash) { |
| + if (hash) { |
| + uint8 output[crypto::kSHA256Length]; |
| + hash->Finish(output, sizeof(output)); |
| + if (base::StringToLowerASCII(base::HexEncode(output, sizeof(output))) != |
| + package_hash_) { |
| + // Package hash verification failed |
| + CRX_HASH_CHECK_HISTOGRAM("Extensions.SandboxUnpackHashCheck", false); |
| + if (check_crx_hash_) { |
| + std::string name = crx_path_.BaseName().AsUTF8Unsafe(); |
| + LOG(ERROR) << "Hash check failed for extension: " << name; |
| + ReportFailure(CRX_HASH_VERIFICATION_FAILED, |
| + l10n_util::GetStringFUTF16( |
| + IDS_EXTENSION_PACKAGE_ERROR_CODE, |
| + ASCIIToUTF16("CRX_HASH_VERIFICATION_FAILED"))); |
| + return false; |
| + } |
| + } else { |
| + 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
|
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| bool SandboxedUnpacker::ValidateSignature() { |
| base::ScopedFILE file(base::OpenFile(crx_path_, "rb")); |
| + scoped_ptr<crypto::SecureHash> hash; |
| + |
| + if (!package_hash_.empty()) { |
| + hash.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256)); |
| + } |
| + |
| if (!file.get()) { |
| // Could not open crx file for reading. |
| #if defined(OS_WIN) |
| @@ -437,7 +494,7 @@ bool SandboxedUnpacker::ValidateSignature() { |
| // code in the code base. So for now, this assumes that we're running |
| // on a little endian machine with 4 byte alignment. |
| CrxFile::Header header; |
| - size_t len = fread(&header, 1, sizeof(header), file.get()); |
| + size_t len = ReadAndHash(&header, 1, sizeof(header), file.get(), hash); |
| if (len < sizeof(header)) { |
| // Invalid crx header |
| ReportFailure(CRX_HEADER_INVALID, l10n_util::GetStringFUTF16( |
| @@ -492,7 +549,8 @@ bool SandboxedUnpacker::ValidateSignature() { |
| std::vector<uint8> key; |
| key.resize(header.key_size); |
| - len = fread(&key.front(), sizeof(uint8), header.key_size, file.get()); |
| + len = ReadAndHash(&key.front(), sizeof(uint8), header.key_size, file.get(), |
| + hash); |
| if (len < header.key_size) { |
| // Invalid public key |
| ReportFailure( |
| @@ -504,8 +562,8 @@ bool SandboxedUnpacker::ValidateSignature() { |
| std::vector<uint8> signature; |
| signature.resize(header.signature_size); |
| - len = fread(&signature.front(), sizeof(uint8), header.signature_size, |
| - file.get()); |
| + len = ReadAndHash(&signature.front(), sizeof(uint8), header.signature_size, |
| + file.get(), hash); |
| if (len < header.signature_size) { |
| // Invalid signature |
| ReportFailure( |
| @@ -530,7 +588,7 @@ bool SandboxedUnpacker::ValidateSignature() { |
| } |
| unsigned char buf[1 << 12]; |
| - while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0) |
| + while ((len = ReadAndHash(buf, 1, sizeof(buf), file.get(), hash)) > 0) |
| verifier.VerifyUpdate(buf, len); |
| if (!verifier.VerifyFinal()) { |
| @@ -542,6 +600,10 @@ bool SandboxedUnpacker::ValidateSignature() { |
| return false; |
| } |
| + if (!FinalizeHash(hash)) { |
| + return false; |
| + } |
| + |
| std::string public_key = |
| std::string(reinterpret_cast<char*>(&key.front()), key.size()); |
| base::Base64Encode(public_key, &public_key_); |