blob: 24e9b482c88c96be2a04a1cee3306cee8630c419 [file] [log] [blame]
[email protected]de0fdca22014-08-19 05:26:091// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]2e114e732011-07-22 02:55:042// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
sorin52ac0882015-01-24 01:15:005#include "components/update_client/component_unpacker.h"
[email protected]2e114e732011-07-22 02:55:046
sorin5cb1f5492014-09-23 04:07:447#include <stdint.h>
[email protected]2e114e732011-07-22 02:55:048#include <string>
9#include <vector>
10
asargent710893f2015-07-27 21:22:3211#include "base/base64.h"
[email protected]f5d27e32014-01-31 06:48:5312#include "base/bind.h"
[email protected]f5d27e32014-01-31 06:48:5313#include "base/files/file_path.h"
thestig819adcc82014-09-10 22:24:5314#include "base/files/file_util.h"
[email protected]0910bae2014-06-10 17:53:2115#include "base/files/scoped_file.h"
[email protected]ffbec692012-02-26 20:26:4216#include "base/json/json_file_value_serializer.h"
[email protected]9e1685a2014-02-13 15:08:3417#include "base/location.h"
[email protected]871bdf12013-10-26 10:52:0618#include "base/logging.h"
avi5dd91f82015-12-25 22:30:4619#include "base/macros.h"
[email protected]de0fdca22014-08-19 05:26:0920#include "base/numerics/safe_conversions.h"
[email protected]3ea1b182013-02-08 22:38:4121#include "base/strings/string_number_conversions.h"
[email protected]e7463412013-06-10 22:53:4622#include "base/strings/stringprintf.h"
[email protected]871bdf12013-10-26 10:52:0623#include "base/values.h"
[email protected]8c83fe02014-08-15 19:07:3524#include "components/crx_file/crx_file.h"
sorin52ac0882015-01-24 01:15:0025#include "components/update_client/component_patcher.h"
26#include "components/update_client/component_patcher_operation.h"
27#include "components/update_client/update_client.h"
sorin7b8650522016-11-02 18:23:4128#include "components/update_client/update_client_errors.h"
[email protected]2e114e732011-07-22 02:55:0429#include "crypto/secure_hash.h"
asargent710893f2015-07-27 21:22:3230#include "crypto/sha2.h"
[email protected]4170d3a2013-05-03 23:02:5731#include "third_party/zlib/google/zip.h"
[email protected]2e114e732011-07-22 02:55:0432
33using crypto::SecureHash;
asargent710893f2015-07-27 21:22:3234using crx_file::CrxFile;
[email protected]2e114e732011-07-22 02:55:0435
sorin52ac0882015-01-24 01:15:0036namespace update_client {
[email protected]055981f2014-01-17 20:22:3237
[email protected]2e114e732011-07-22 02:55:0438// TODO(cpu): add a specific attribute check to a component json that the
39// extension unpacker will reject, so that a component cannot be installed
40// as an extension.
dchengd0fc6aa92016-04-22 18:03:1241std::unique_ptr<base::DictionaryValue> ReadManifest(
[email protected]871bdf12013-10-26 10:52:0642 const base::FilePath& unpack_path) {
[email protected]650b2d52013-02-10 03:41:4543 base::FilePath manifest =
44 unpack_path.Append(FILE_PATH_LITERAL("manifest.json"));
[email protected]7567484142013-07-11 17:36:0745 if (!base::PathExists(manifest))
dchengd0fc6aa92016-04-22 18:03:1246 return std::unique_ptr<base::DictionaryValue>();
prashhir54a994502015-03-05 09:30:5747 JSONFileValueDeserializer deserializer(manifest);
[email protected]2e114e732011-07-22 02:55:0448 std::string error;
dchengd0fc6aa92016-04-22 18:03:1249 std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, &error);
[email protected]2e114e732011-07-22 02:55:0450 if (!root.get())
dchengd0fc6aa92016-04-22 18:03:1251 return std::unique_ptr<base::DictionaryValue>();
[email protected]2e114e732011-07-22 02:55:0452 if (!root->IsType(base::Value::TYPE_DICTIONARY))
dchengd0fc6aa92016-04-22 18:03:1253 return std::unique_ptr<base::DictionaryValue>();
54 return std::unique_ptr<base::DictionaryValue>(
dcheng51ace48a2015-12-26 22:45:1755 static_cast<base::DictionaryValue*>(root.release()));
[email protected]2e114e732011-07-22 02:55:0456}
57
sorincbb7c092016-10-27 01:39:2058ComponentUnpacker::Result::Result() {}
59
60ComponentUnpacker::ComponentUnpacker(
61 const std::vector<uint8_t>& pk_hash,
62 const base::FilePath& path,
63 const scoped_refptr<CrxInstaller>& installer,
64 const scoped_refptr<OutOfProcessPatcher>& oop_patcher,
65 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
66 : pk_hash_(pk_hash),
67 path_(path),
68 is_delta_(false),
69 installer_(installer),
70 oop_patcher_(oop_patcher),
sorin7b8650522016-11-02 18:23:4171 error_(UnpackerError::kNone),
sorincbb7c092016-10-27 01:39:2072 extended_error_(0),
73 task_runner_(task_runner) {}
74
75ComponentUnpacker::~ComponentUnpacker() {}
76
[email protected]f5d27e32014-01-31 06:48:5377bool ComponentUnpacker::UnpackInternal() {
78 return Verify() && Unzip() && BeginPatching();
79}
80
[email protected]94a481b2014-03-28 19:41:5581void ComponentUnpacker::Unpack(const Callback& callback) {
[email protected]f5d27e32014-01-31 06:48:5382 callback_ = callback;
83 if (!UnpackInternal())
sorincbb7c092016-10-27 01:39:2084 EndUnpacking();
[email protected]f5d27e32014-01-31 06:48:5385}
86
87bool ComponentUnpacker::Verify() {
[email protected]fb53e652014-04-30 11:27:1988 VLOG(1) << "Verifying component: " << path_.value();
[email protected]f5d27e32014-01-31 06:48:5389 if (pk_hash_.empty() || path_.empty()) {
sorin7b8650522016-11-02 18:23:4190 error_ = UnpackerError::kInvalidParams;
[email protected]f5d27e32014-01-31 06:48:5391 return false;
[email protected]2e114e732011-07-22 02:55:0492 }
93 // First, validate the CRX header and signature. As of today
94 // this is SHA1 with RSA 1024.
asargent710893f2015-07-27 21:22:3295 std::string public_key_bytes;
96 std::string public_key_base64;
97 CrxFile::Header header;
98 CrxFile::ValidateError error = CrxFile::ValidateSignature(
99 path_, std::string(), &public_key_base64, nullptr, &header);
100 if (error != CrxFile::ValidateError::NONE ||
101 !base::Base64Decode(public_key_base64, &public_key_bytes)) {
sorin7b8650522016-11-02 18:23:41102 error_ = UnpackerError::kInvalidFile;
[email protected]f5d27e32014-01-31 06:48:53103 return false;
[email protected]2e114e732011-07-22 02:55:04104 }
asargent710893f2015-07-27 21:22:32105 is_delta_ = CrxFile::HeaderIsDelta(header);
[email protected]2e114e732011-07-22 02:55:04106
[email protected]f1050432012-02-15 01:35:46107 // File is valid and the digital signature matches. Now make sure
[email protected]2e114e732011-07-22 02:55:04108 // the public key hash matches the expected hash. If they do we fully
109 // trust this CRX.
asargent710893f2015-07-27 21:22:32110 uint8_t hash[crypto::kSHA256Length] = {};
dchengd0fc6aa92016-04-22 18:03:12111 std::unique_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256));
asargent710893f2015-07-27 21:22:32112 sha256->Update(public_key_bytes.data(), public_key_bytes.size());
[email protected]2e114e732011-07-22 02:55:04113 sha256->Finish(hash, arraysize(hash));
114
[email protected]f5d27e32014-01-31 06:48:53115 if (!std::equal(pk_hash_.begin(), pk_hash_.end(), hash)) {
[email protected]fb53e652014-04-30 11:27:19116 VLOG(1) << "Hash mismatch: " << path_.value();
sorin7b8650522016-11-02 18:23:41117 error_ = UnpackerError::kInvalidId;
[email protected]f5d27e32014-01-31 06:48:53118 return false;
[email protected]2e114e732011-07-22 02:55:04119 }
[email protected]fb53e652014-04-30 11:27:19120 VLOG(1) << "Verification successful: " << path_.value();
[email protected]f5d27e32014-01-31 06:48:53121 return true;
122}
123
124bool ComponentUnpacker::Unzip() {
sorincbb7c092016-10-27 01:39:20125 // Mind the reference to non-const type, passed as an argument below.
[email protected]f5d27e32014-01-31 06:48:53126 base::FilePath& destination = is_delta_ ? unpack_diff_path_ : unpack_path_;
[email protected]03d9afc02013-12-03 17:55:52127 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
[email protected]f5d27e32014-01-31 06:48:53128 &destination)) {
[email protected]fb53e652014-04-30 11:27:19129 VLOG(1) << "Unable to create temporary directory for unpacking.";
sorin7b8650522016-11-02 18:23:41130 error_ = UnpackerError::kUnzipPathError;
[email protected]f5d27e32014-01-31 06:48:53131 return false;
132 }
sorincbb7c092016-10-27 01:39:20133 VLOG(1) << "Unpacking in: " << destination.value();
[email protected]f5d27e32014-01-31 06:48:53134 if (!zip::Unzip(path_, destination)) {
[email protected]fb53e652014-04-30 11:27:19135 VLOG(1) << "Unzipping failed.";
sorin7b8650522016-11-02 18:23:41136 error_ = UnpackerError::kUnzipFailed;
[email protected]f5d27e32014-01-31 06:48:53137 return false;
138 }
[email protected]fb53e652014-04-30 11:27:19139 VLOG(1) << "Unpacked successfully";
[email protected]f5d27e32014-01-31 06:48:53140 return true;
141}
142
[email protected]f5d27e32014-01-31 06:48:53143bool ComponentUnpacker::BeginPatching() {
144 if (is_delta_) { // Package is a diff package.
145 // Use a different temp directory for the patch output files.
146 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
147 &unpack_path_)) {
sorin7b8650522016-11-02 18:23:41148 error_ = UnpackerError::kUnzipPathError;
[email protected]f5d27e32014-01-31 06:48:53149 return false;
150 }
sorin52ac0882015-01-24 01:15:00151 patcher_ = new ComponentPatcher(unpack_diff_path_, unpack_path_, installer_,
152 oop_patcher_, task_runner_);
[email protected]f5d27e32014-01-31 06:48:53153 task_runner_->PostTask(
[email protected]94a481b2014-03-28 19:41:55154 FROM_HERE,
sorin52ac0882015-01-24 01:15:00155 base::Bind(&ComponentPatcher::Start, patcher_,
[email protected]94a481b2014-03-28 19:41:55156 base::Bind(&ComponentUnpacker::EndPatching,
157 scoped_refptr<ComponentUnpacker>(this))));
[email protected]f5d27e32014-01-31 06:48:53158 } else {
sorin7b8650522016-11-02 18:23:41159 task_runner_->PostTask(FROM_HERE,
160 base::Bind(&ComponentUnpacker::EndPatching,
161 scoped_refptr<ComponentUnpacker>(this),
162 UnpackerError::kNone, 0));
[email protected]f5d27e32014-01-31 06:48:53163 }
164 return true;
165}
166
sorin7b8650522016-11-02 18:23:41167void ComponentUnpacker::EndPatching(UnpackerError error, int extended_error) {
[email protected]f5d27e32014-01-31 06:48:53168 error_ = error;
169 extended_error_ = extended_error;
[email protected]94a481b2014-03-28 19:41:55170 patcher_ = NULL;
sorincbb7c092016-10-27 01:39:20171
172 EndUnpacking();
[email protected]f5d27e32014-01-31 06:48:53173}
174
sorincbb7c092016-10-27 01:39:20175void ComponentUnpacker::EndUnpacking() {
[email protected]f5d27e32014-01-31 06:48:53176 if (!unpack_diff_path_.empty())
177 base::DeleteFile(unpack_diff_path_, true);
sorin7b8650522016-11-02 18:23:41178 if (error_ != UnpackerError::kNone && !unpack_path_.empty())
[email protected]f5d27e32014-01-31 06:48:53179 base::DeleteFile(unpack_path_, true);
[email protected]f5d27e32014-01-31 06:48:53180
sorincbb7c092016-10-27 01:39:20181 Result result;
182 result.error = error_;
183 result.extended_error = extended_error_;
sorin7b8650522016-11-02 18:23:41184 if (error_ == UnpackerError::kNone)
sorincbb7c092016-10-27 01:39:20185 result.unpack_path = unpack_path_;
186
187 task_runner_->PostTask(FROM_HERE, base::Bind(callback_, result));
[email protected]2e114e732011-07-22 02:55:04188}
[email protected]055981f2014-01-17 20:22:32189
sorin52ac0882015-01-24 01:15:00190} // namespace update_client