1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/update_client/component_unpacker.h"
11 #include "base/bind.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_file.h"
15 #include "base/json/json_file_value_serializer.h"
16 #include "base/location.h"
17 #include "base/logging.h"
18 #include "base/numerics/safe_conversions.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/values.h"
22 #include "components/crx_file/constants.h"
23 #include "components/crx_file/crx_file.h"
24 #include "components/update_client/component_patcher.h"
25 #include "components/update_client/component_patcher_operation.h"
26 #include "components/update_client/update_client.h"
27 #include "crypto/secure_hash.h"
28 #include "crypto/signature_verifier.h"
29 #include "third_party/zlib/google/zip.h"
31 using crypto::SecureHash
;
33 namespace update_client
{
37 // This class makes sure that the CRX digital signature is valid
41 explicit CRXValidator(FILE* crx_file
) : valid_(false), is_delta_(false) {
42 crx_file::CrxFile::Header header
;
43 size_t len
= fread(&header
, 1, sizeof(header
), crx_file
);
44 if (len
< sizeof(header
))
47 crx_file::CrxFile::Error error
;
48 scoped_ptr
<crx_file::CrxFile
> crx(crx_file::CrxFile::Parse(header
, &error
));
51 is_delta_
= crx_file::CrxFile::HeaderIsDelta(header
);
53 std::vector
<uint8_t> key(header
.key_size
);
54 len
= fread(&key
[0], sizeof(uint8_t), header
.key_size
, crx_file
);
55 if (len
< header
.key_size
)
58 std::vector
<uint8_t> signature(header
.signature_size
);
60 fread(&signature
[0], sizeof(uint8_t), header
.signature_size
, crx_file
);
61 if (len
< header
.signature_size
)
64 crypto::SignatureVerifier verifier
;
65 if (!verifier
.VerifyInit(
66 crx_file::kSignatureAlgorithm
,
67 base::checked_cast
<int>(sizeof(crx_file::kSignatureAlgorithm
)),
68 &signature
[0], base::checked_cast
<int>(signature
.size()), &key
[0],
69 base::checked_cast
<int>(key
.size()))) {
70 // Signature verification initialization failed. This is most likely
71 // caused by a public key in the wrong format (should encode algorithm).
75 const size_t kBufSize
= 8 * 1024;
76 scoped_ptr
<uint8_t[]> buf(new uint8_t[kBufSize
]);
77 while ((len
= fread(buf
.get(), 1, kBufSize
, crx_file
)) > 0)
78 verifier
.VerifyUpdate(buf
.get(), base::checked_cast
<int>(len
));
80 if (!verifier
.VerifyFinal())
83 public_key_
.swap(key
);
87 bool valid() const { return valid_
; }
89 bool is_delta() const { return is_delta_
; }
91 const std::vector
<uint8_t>& public_key() const { return public_key_
; }
96 std::vector
<uint8_t> public_key_
;
101 ComponentUnpacker::ComponentUnpacker(
102 const std::vector
<uint8_t>& pk_hash
,
103 const base::FilePath
& path
,
104 const std::string
& fingerprint
,
105 const scoped_refptr
<CrxInstaller
>& installer
,
106 const scoped_refptr
<OutOfProcessPatcher
>& oop_patcher
,
107 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
)
111 fingerprint_(fingerprint
),
112 installer_(installer
),
113 oop_patcher_(oop_patcher
),
116 task_runner_(task_runner
) {
119 // TODO(cpu): add a specific attribute check to a component json that the
120 // extension unpacker will reject, so that a component cannot be installed
122 scoped_ptr
<base::DictionaryValue
> ReadManifest(
123 const base::FilePath
& unpack_path
) {
124 base::FilePath manifest
=
125 unpack_path
.Append(FILE_PATH_LITERAL("manifest.json"));
126 if (!base::PathExists(manifest
))
127 return scoped_ptr
<base::DictionaryValue
>();
128 JSONFileValueDeserializer
deserializer(manifest
);
130 scoped_ptr
<base::Value
> root(deserializer
.Deserialize(NULL
, &error
));
132 return scoped_ptr
<base::DictionaryValue
>();
133 if (!root
->IsType(base::Value::TYPE_DICTIONARY
))
134 return scoped_ptr
<base::DictionaryValue
>();
135 return scoped_ptr
<base::DictionaryValue
>(
136 static_cast<base::DictionaryValue
*>(root
.release())).Pass();
139 bool ComponentUnpacker::UnpackInternal() {
140 return Verify() && Unzip() && BeginPatching();
143 void ComponentUnpacker::Unpack(const Callback
& callback
) {
144 callback_
= callback
;
145 if (!UnpackInternal())
149 bool ComponentUnpacker::Verify() {
150 VLOG(1) << "Verifying component: " << path_
.value();
151 if (pk_hash_
.empty() || path_
.empty()) {
152 error_
= kInvalidParams
;
155 // First, validate the CRX header and signature. As of today
156 // this is SHA1 with RSA 1024.
157 base::ScopedFILE
file(base::OpenFile(path_
, "rb"));
159 error_
= kInvalidFile
;
162 CRXValidator
validator(file
.get());
164 if (!validator
.valid()) {
165 error_
= kInvalidFile
;
168 is_delta_
= validator
.is_delta();
170 // File is valid and the digital signature matches. Now make sure
171 // the public key hash matches the expected hash. If they do we fully
173 uint8_t hash
[32] = {};
174 scoped_ptr
<SecureHash
> sha256(SecureHash::Create(SecureHash::SHA256
));
175 sha256
->Update(&(validator
.public_key()[0]), validator
.public_key().size());
176 sha256
->Finish(hash
, arraysize(hash
));
178 if (!std::equal(pk_hash_
.begin(), pk_hash_
.end(), hash
)) {
179 VLOG(1) << "Hash mismatch: " << path_
.value();
183 VLOG(1) << "Verification successful: " << path_
.value();
187 bool ComponentUnpacker::Unzip() {
188 base::FilePath
& destination
= is_delta_
? unpack_diff_path_
: unpack_path_
;
189 VLOG(1) << "Unpacking in: " << destination
.value();
190 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
192 VLOG(1) << "Unable to create temporary directory for unpacking.";
193 error_
= kUnzipPathError
;
196 if (!zip::Unzip(path_
, destination
)) {
197 VLOG(1) << "Unzipping failed.";
198 error_
= kUnzipFailed
;
201 VLOG(1) << "Unpacked successfully";
205 bool ComponentUnpacker::BeginPatching() {
206 if (is_delta_
) { // Package is a diff package.
207 // Use a different temp directory for the patch output files.
208 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
210 error_
= kUnzipPathError
;
213 patcher_
= new ComponentPatcher(unpack_diff_path_
, unpack_path_
, installer_
,
214 oop_patcher_
, task_runner_
);
215 task_runner_
->PostTask(
217 base::Bind(&ComponentPatcher::Start
, patcher_
,
218 base::Bind(&ComponentUnpacker::EndPatching
,
219 scoped_refptr
<ComponentUnpacker
>(this))));
221 task_runner_
->PostTask(
223 base::Bind(&ComponentUnpacker::EndPatching
,
224 scoped_refptr
<ComponentUnpacker
>(this), kNone
, 0));
229 void ComponentUnpacker::EndPatching(Error error
, int extended_error
) {
231 extended_error_
= extended_error
;
233 if (error_
!= kNone
) {
237 // Optimization: clean up patch files early, in case disk space is too low to
238 // install otherwise.
239 if (!unpack_diff_path_
.empty()) {
240 base::DeleteFile(unpack_diff_path_
, true);
241 unpack_diff_path_
.clear();
247 void ComponentUnpacker::Install() {
248 // Write the fingerprint to disk.
249 if (static_cast<int>(fingerprint_
.size()) !=
251 unpack_path_
.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
252 fingerprint_
.c_str(), base::checked_cast
<int>(fingerprint_
.size()))) {
253 error_
= kFingerprintWriteFailed
;
256 scoped_ptr
<base::DictionaryValue
> manifest(ReadManifest(unpack_path_
));
257 if (!manifest
.get()) {
258 error_
= kBadManifest
;
261 DCHECK(error_
== kNone
);
262 if (!installer_
->Install(*manifest
, unpack_path_
)) {
263 error_
= kInstallerError
;
268 void ComponentUnpacker::Finish() {
269 if (!unpack_diff_path_
.empty())
270 base::DeleteFile(unpack_diff_path_
, true);
271 if (!unpack_path_
.empty())
272 base::DeleteFile(unpack_path_
, true);
273 task_runner_
->PostTask(FROM_HERE
,
274 base::Bind(callback_
, error_
, extended_error_
));
277 ComponentUnpacker::~ComponentUnpacker() {
280 } // namespace update_client