| Index: chrome/browser/component_updater/component_unpacker.cc
|
| diff --git a/chrome/browser/component_updater/component_unpacker.cc b/chrome/browser/component_updater/component_unpacker.cc
|
| index dc9bddbaa5e2b3dc01a913679fb118b29778a9b3..796b056f1232e983a9f24b99204655712937e290 100644
|
| --- a/chrome/browser/component_updater/component_unpacker.cc
|
| +++ b/chrome/browser/component_updater/component_unpacker.cc
|
| @@ -7,7 +7,9 @@
|
| #include <string>
|
| #include <vector>
|
|
|
| +#include "base/bind.h"
|
| #include "base/file_util.h"
|
| +#include "base/files/file_path.h"
|
| #include "base/json/json_file_value_serializer.h"
|
| #include "base/logging.h"
|
| #include "base/memory/scoped_handle.h"
|
| @@ -17,6 +19,7 @@
|
| #include "chrome/browser/component_updater/component_patcher.h"
|
| #include "chrome/browser/component_updater/component_updater_service.h"
|
| #include "chrome/common/extensions/extension_constants.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| #include "crypto/secure_hash.h"
|
| #include "crypto/signature_verifier.h"
|
| #include "extensions/common/crx_file.h"
|
| @@ -32,7 +35,7 @@ namespace {
|
| // and well formed.
|
| class CRXValidator {
|
| public:
|
| - explicit CRXValidator(FILE* crx_file) : valid_(false), delta_(false) {
|
| + explicit CRXValidator(FILE* crx_file) : valid_(false), is_delta_(false) {
|
| extensions::CrxFile::Header header;
|
| size_t len = fread(&header, 1, sizeof(header), crx_file);
|
| if (len < sizeof(header))
|
| @@ -43,7 +46,7 @@ class CRXValidator {
|
| extensions::CrxFile::Parse(header, &error));
|
| if (!crx.get())
|
| return;
|
| - delta_ = extensions::CrxFile::HeaderIsDelta(header);
|
| + is_delta_ = extensions::CrxFile::HeaderIsDelta(header);
|
|
|
| std::vector<uint8> key(header.key_size);
|
| len = fread(&key[0], sizeof(uint8), header.key_size, crx_file);
|
| @@ -79,17 +82,36 @@ class CRXValidator {
|
|
|
| bool valid() const { return valid_; }
|
|
|
| - bool delta() const { return delta_; }
|
| + bool is_delta() const { return is_delta_; }
|
|
|
| const std::vector<uint8>& public_key() const { return public_key_; }
|
|
|
| private:
|
| bool valid_;
|
| - bool delta_;
|
| + bool is_delta_;
|
| std::vector<uint8> public_key_;
|
| };
|
|
|
| -} // namespace.
|
| +} // namespace
|
| +
|
| +ComponentUnpacker::ComponentUnpacker(
|
| + const std::vector<uint8>& pk_hash,
|
| + const base::FilePath& path,
|
| + const std::string& fingerprint,
|
| + ComponentPatcher* patcher,
|
| + ComponentInstaller* installer,
|
| + scoped_refptr<base::SequencedTaskRunner> task_runner)
|
| + : pk_hash_(pk_hash),
|
| + path_(path),
|
| + is_delta_(false),
|
| + fingerprint_(fingerprint),
|
| + patcher_(patcher),
|
| + installer_(installer),
|
| + error_(kNone),
|
| + extended_error_(0),
|
| + ptr_factory_(this),
|
| + task_runner_(task_runner) {
|
| +}
|
|
|
| // TODO(cpu): add a specific attribute check to a component json that the
|
| // extension unpacker will reject, so that a component cannot be installed
|
| @@ -111,105 +133,145 @@ scoped_ptr<base::DictionaryValue> ReadManifest(
|
| static_cast<base::DictionaryValue*>(root.release())).Pass();
|
| }
|
|
|
| -ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash,
|
| - const base::FilePath& path,
|
| - const std::string& fingerprint,
|
| - ComponentPatcher* patcher,
|
| - ComponentInstaller* installer)
|
| - : error_(kNone),
|
| - extended_error_(0) {
|
| - if (pk_hash.empty() || path.empty()) {
|
| +bool ComponentUnpacker::UnpackInternal() {
|
| + return Verify() && Unzip() && BeginPatching();
|
| +}
|
| +
|
| +void ComponentUnpacker::Unpack(
|
| + const base::Callback<void(Error, int)>& callback) {
|
| + callback_ = callback;
|
| + if (!UnpackInternal())
|
| + Finish();
|
| +}
|
| +
|
| +bool ComponentUnpacker::Verify() {
|
| + if (pk_hash_.empty() || path_.empty()) {
|
| error_ = kInvalidParams;
|
| - return;
|
| + return false;
|
| }
|
| // First, validate the CRX header and signature. As of today
|
| // this is SHA1 with RSA 1024.
|
| - ScopedStdioHandle file(base::OpenFile(path, "rb"));
|
| + ScopedStdioHandle file(base::OpenFile(path_, "rb"));
|
| if (!file.get()) {
|
| error_ = kInvalidFile;
|
| - return;
|
| + return false;
|
| }
|
| CRXValidator validator(file.get());
|
| + file.Close();
|
| if (!validator.valid()) {
|
| error_ = kInvalidFile;
|
| - return;
|
| + return false;
|
| }
|
| - file.Close();
|
| + is_delta_ = validator.is_delta();
|
|
|
| // File is valid and the digital signature matches. Now make sure
|
| // the public key hash matches the expected hash. If they do we fully
|
| // trust this CRX.
|
| - uint8 hash[32];
|
| + uint8 hash[32] = {};
|
| scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256));
|
| sha256->Update(&(validator.public_key()[0]), validator.public_key().size());
|
| sha256->Finish(hash, arraysize(hash));
|
|
|
| - if (!std::equal(pk_hash.begin(), pk_hash.end(), hash)) {
|
| + if (!std::equal(pk_hash_.begin(), pk_hash_.end(), hash)) {
|
| error_ = kInvalidId;
|
| - return;
|
| + return false;
|
| }
|
| + return true;
|
| +}
|
| +
|
| +bool ComponentUnpacker::Unzip() {
|
| + base::FilePath& destination = is_delta_ ? unpack_diff_path_ : unpack_path_;
|
| if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
|
| - &unpack_path_)) {
|
| + &destination)) {
|
| error_ = kUnzipPathError;
|
| - return;
|
| + return false;
|
| }
|
| - if (validator.delta()) { // Package is a diff package.
|
| - // We want a different temp directory for the delta files; we'll put the
|
| - // patch output into unpack_path_.
|
| - base::FilePath unpack_diff_path;
|
| + if (!zip::Unzip(path_, destination)) {
|
| + error_ = kUnzipFailed;
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +bool ComponentUnpacker::BeginPatching() {
|
| + if (is_delta_) { // Package is a diff package.
|
| + // Use a different temp directory for the patch output files.
|
| if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
|
| - &unpack_diff_path)) {
|
| + &unpack_path_)) {
|
| error_ = kUnzipPathError;
|
| - return;
|
| - }
|
| - if (!zip::Unzip(path, unpack_diff_path)) {
|
| - error_ = kUnzipFailed;
|
| - return;
|
| - }
|
| - ComponentUnpacker::Error result = DifferentialUpdatePatch(unpack_diff_path,
|
| - unpack_path_,
|
| - patcher,
|
| - installer,
|
| - &extended_error_);
|
| - base::DeleteFile(unpack_diff_path, true);
|
| - unpack_diff_path.clear();
|
| - error_ = result;
|
| - if (error_ != kNone) {
|
| - return;
|
| + return false;
|
| }
|
| + task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&DifferentialUpdatePatch,
|
| + unpack_diff_path_,
|
| + unpack_path_,
|
| + patcher_,
|
| + installer_,
|
| + base::Bind(&ComponentUnpacker::EndPatching,
|
| + GetWeakPtr())));
|
| } else {
|
| - // Package is a normal update/install; unzip it into unpack_path_ directly.
|
| - if (!zip::Unzip(path, unpack_path_)) {
|
| - error_ = kUnzipFailed;
|
| - return;
|
| - }
|
| + task_runner_->PostTask(
|
| + FROM_HERE, base::Bind(&ComponentUnpacker::EndPatching,
|
| + GetWeakPtr(),
|
| + kNone,
|
| + 0));
|
| }
|
| - scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_));
|
| - if (!manifest.get()) {
|
| - error_ = kBadManifest;
|
| + return true;
|
| +}
|
| +
|
| +void ComponentUnpacker::EndPatching(Error error, int extended_error) {
|
| + error_ = error;
|
| + extended_error_ = extended_error;
|
| + if (error_ != kNone) {
|
| + Finish();
|
| return;
|
| }
|
| + // Optimization: clean up patch files early, in case disk space is too low to
|
| + // install otherwise.
|
| + if (!unpack_diff_path_.empty()) {
|
| + base::DeleteFile(unpack_diff_path_, true);
|
| + unpack_diff_path_.clear();
|
| + }
|
| + Install();
|
| + Finish();
|
| +}
|
| +
|
| +void ComponentUnpacker::Install() {
|
| // Write the fingerprint to disk.
|
| - if (static_cast<int>(fingerprint.size()) !=
|
| + if (static_cast<int>(fingerprint_.size()) !=
|
| file_util::WriteFile(
|
| unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
|
| - fingerprint.c_str(),
|
| - fingerprint.size())) {
|
| + fingerprint_.c_str(),
|
| + fingerprint_.size())) {
|
| error_ = kFingerprintWriteFailed;
|
| return;
|
| }
|
| - if (!installer->Install(*manifest, unpack_path_)) {
|
| + scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_));
|
| + if (!manifest.get()) {
|
| + error_ = kBadManifest;
|
| + return;
|
| + }
|
| + DCHECK(error_ == kNone);
|
| + if (!installer_->Install(*manifest, unpack_path_)) {
|
| error_ = kInstallerError;
|
| return;
|
| }
|
| - // Installation successful. The directory is not our concern now.
|
| - unpack_path_.clear();
|
| }
|
|
|
| -ComponentUnpacker::~ComponentUnpacker() {
|
| +void ComponentUnpacker::Finish() {
|
| + if (!unpack_diff_path_.empty())
|
| + base::DeleteFile(unpack_diff_path_, true);
|
| if (!unpack_path_.empty())
|
| base::DeleteFile(unpack_path_, true);
|
| + callback_.Run(error_, extended_error_);
|
| }
|
|
|
| -} // namespace component_updater
|
| +base::WeakPtr<ComponentUnpacker> ComponentUnpacker::GetWeakPtr() {
|
| + return ptr_factory_.GetWeakPtr();
|
| +}
|
|
|
| +ComponentUnpacker::~ComponentUnpacker() {
|
| +}
|
| +
|
| +} // namespace component_updater
|
|
|