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/component_updater/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/component_updater/component_patcher.h"
23 #include "components/component_updater/component_patcher_operation.h"
24 #include "components/component_updater/component_updater_service.h"
25 #include "components/crx_file/constants.h"
26 #include "components/crx_file/crx_file.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 component_updater
{
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(
49 crx_file::CrxFile::Parse(header
, &error
));
52 is_delta_
= crx_file::CrxFile::HeaderIsDelta(header
);
54 std::vector
<uint8_t> key(header
.key_size
);
55 len
= fread(&key
[0], sizeof(uint8_t), header
.key_size
, crx_file
);
56 if (len
< header
.key_size
)
59 std::vector
<uint8_t> signature(header
.signature_size
);
61 fread(&signature
[0], sizeof(uint8_t), header
.signature_size
, crx_file
);
62 if (len
< header
.signature_size
)
65 crypto::SignatureVerifier verifier
;
66 if (!verifier
.VerifyInit(crx_file::kSignatureAlgorithm
,
67 base::checked_cast
<int>(
68 sizeof(crx_file::kSignatureAlgorithm
)),
70 base::checked_cast
<int>(signature
.size()),
72 base::checked_cast
<int>(key
.size()))) {
73 // Signature verification initialization failed. This is most likely
74 // caused by a public key in the wrong format (should encode algorithm).
78 const size_t kBufSize
= 8 * 1024;
79 scoped_ptr
<uint8_t[]> buf(new uint8_t[kBufSize
]);
80 while ((len
= fread(buf
.get(), 1, kBufSize
, crx_file
)) > 0)
81 verifier
.VerifyUpdate(buf
.get(), base::checked_cast
<int>(len
));
83 if (!verifier
.VerifyFinal())
86 public_key_
.swap(key
);
90 bool valid() const { return valid_
; }
92 bool is_delta() const { return is_delta_
; }
94 const std::vector
<uint8_t>& public_key() const { return public_key_
; }
99 std::vector
<uint8_t> public_key_
;
104 ComponentUnpacker::ComponentUnpacker(
105 const std::vector
<uint8_t>& pk_hash
,
106 const base::FilePath
& path
,
107 const std::string
& fingerprint
,
108 ComponentInstaller
* installer
,
109 scoped_refptr
<OutOfProcessPatcher
> out_of_process_patcher
,
110 scoped_refptr
<base::SequencedTaskRunner
> task_runner
)
114 fingerprint_(fingerprint
),
115 installer_(installer
),
116 out_of_process_patcher_(out_of_process_patcher
),
119 task_runner_(task_runner
) {
122 // TODO(cpu): add a specific attribute check to a component json that the
123 // extension unpacker will reject, so that a component cannot be installed
125 scoped_ptr
<base::DictionaryValue
> ReadManifest(
126 const base::FilePath
& unpack_path
) {
127 base::FilePath manifest
=
128 unpack_path
.Append(FILE_PATH_LITERAL("manifest.json"));
129 if (!base::PathExists(manifest
))
130 return scoped_ptr
<base::DictionaryValue
>();
131 JSONFileValueSerializer
serializer(manifest
);
133 scoped_ptr
<base::Value
> root(serializer
.Deserialize(NULL
, &error
));
135 return scoped_ptr
<base::DictionaryValue
>();
136 if (!root
->IsType(base::Value::TYPE_DICTIONARY
))
137 return scoped_ptr
<base::DictionaryValue
>();
138 return scoped_ptr
<base::DictionaryValue
>(
139 static_cast<base::DictionaryValue
*>(root
.release())).Pass();
142 bool ComponentUnpacker::UnpackInternal() {
143 return Verify() && Unzip() && BeginPatching();
146 void ComponentUnpacker::Unpack(const Callback
& callback
) {
147 callback_
= callback
;
148 if (!UnpackInternal())
152 bool ComponentUnpacker::Verify() {
153 VLOG(1) << "Verifying component: " << path_
.value();
154 if (pk_hash_
.empty() || path_
.empty()) {
155 error_
= kInvalidParams
;
158 // First, validate the CRX header and signature. As of today
159 // this is SHA1 with RSA 1024.
160 base::ScopedFILE
file(base::OpenFile(path_
, "rb"));
162 error_
= kInvalidFile
;
165 CRXValidator
validator(file
.get());
167 if (!validator
.valid()) {
168 error_
= kInvalidFile
;
171 is_delta_
= validator
.is_delta();
173 // File is valid and the digital signature matches. Now make sure
174 // the public key hash matches the expected hash. If they do we fully
176 uint8_t hash
[32] = {};
177 scoped_ptr
<SecureHash
> sha256(SecureHash::Create(SecureHash::SHA256
));
178 sha256
->Update(&(validator
.public_key()[0]), validator
.public_key().size());
179 sha256
->Finish(hash
, arraysize(hash
));
181 if (!std::equal(pk_hash_
.begin(), pk_hash_
.end(), hash
)) {
182 VLOG(1) << "Hash mismatch: " << path_
.value();
186 VLOG(1) << "Verification successful: " << path_
.value();
190 bool ComponentUnpacker::Unzip() {
191 base::FilePath
& destination
= is_delta_
? unpack_diff_path_
: unpack_path_
;
192 VLOG(1) << "Unpacking in: " << destination
.value();
193 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
195 VLOG(1) << "Unable to create temporary directory for unpacking.";
196 error_
= kUnzipPathError
;
199 if (!zip::Unzip(path_
, destination
)) {
200 VLOG(1) << "Unzipping failed.";
201 error_
= kUnzipFailed
;
204 VLOG(1) << "Unpacked successfully";
208 bool ComponentUnpacker::BeginPatching() {
209 if (is_delta_
) { // Package is a diff package.
210 // Use a different temp directory for the patch output files.
211 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
213 error_
= kUnzipPathError
;
216 patcher_
= new ComponentPatcher(unpack_diff_path_
,
219 out_of_process_patcher_
,
221 task_runner_
->PostTask(
223 base::Bind(&ComponentPatcher::Start
,
225 base::Bind(&ComponentUnpacker::EndPatching
,
226 scoped_refptr
<ComponentUnpacker
>(this))));
228 task_runner_
->PostTask(FROM_HERE
,
229 base::Bind(&ComponentUnpacker::EndPatching
,
230 scoped_refptr
<ComponentUnpacker
>(this),
237 void ComponentUnpacker::EndPatching(Error error
, int extended_error
) {
239 extended_error_
= extended_error
;
241 if (error_
!= kNone
) {
245 // Optimization: clean up patch files early, in case disk space is too low to
246 // install otherwise.
247 if (!unpack_diff_path_
.empty()) {
248 base::DeleteFile(unpack_diff_path_
, true);
249 unpack_diff_path_
.clear();
255 void ComponentUnpacker::Install() {
256 // Write the fingerprint to disk.
257 if (static_cast<int>(fingerprint_
.size()) !=
259 unpack_path_
.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
260 fingerprint_
.c_str(),
261 base::checked_cast
<int>(fingerprint_
.size()))) {
262 error_
= kFingerprintWriteFailed
;
265 scoped_ptr
<base::DictionaryValue
> manifest(ReadManifest(unpack_path_
));
266 if (!manifest
.get()) {
267 error_
= kBadManifest
;
270 DCHECK(error_
== kNone
);
271 if (!installer_
->Install(*manifest
, unpack_path_
)) {
272 error_
= kInstallerError
;
277 void ComponentUnpacker::Finish() {
278 if (!unpack_diff_path_
.empty())
279 base::DeleteFile(unpack_diff_path_
, true);
280 if (!unpack_path_
.empty())
281 base::DeleteFile(unpack_path_
, true);
282 callback_
.Run(error_
, extended_error_
);
285 ComponentUnpacker::~ComponentUnpacker() {
288 } // namespace component_updater