| Index: extensions/browser/sandboxed_unpacker.cc
|
| diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
|
| index cd535a83dbed0c2e9397e188d8300973d417f084..5251bf07af80a3be8d872ed5833124cacba8f89e 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,7 @@
|
| #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/sha2.h"
|
| #include "crypto/signature_verifier.h"
|
| #include "extensions/common/constants.h"
|
| #include "extensions/common/extension.h"
|
| @@ -35,6 +37,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 +62,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) \
|
| + UMA_HISTOGRAM_BOOLEAN(name, success)
|
| +
|
| namespace extensions {
|
| namespace {
|
|
|
| @@ -209,19 +216,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 +415,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);
|
| + }
|
| + }
|
| +
|
| + 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 +493,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 +548,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 +561,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 +587,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 +599,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_);
|
|
|