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"
10 #include "base/bind.h"
11 #include "base/file_util.h"
12 #include "base/files/file_path.h"
13 #include "base/files/scoped_file.h"
14 #include "base/json/json_file_value_serializer.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/values.h"
21 #include "components/component_updater/component_patcher.h"
22 #include "components/component_updater/component_patcher_operation.h"
23 #include "components/component_updater/component_updater_service.h"
24 #include "components/crx_file/constants.h"
25 #include "components/crx_file/crx_file.h"
26 #include "crypto/secure_hash.h"
27 #include "crypto/signature_verifier.h"
28 #include "third_party/zlib/google/zip.h"
30 using crypto::SecureHash
;
32 namespace component_updater
{
36 // This class makes sure that the CRX digital signature is valid
40 explicit CRXValidator(FILE* crx_file
) : valid_(false), is_delta_(false) {
41 crx_file::CrxFile::Header header
;
42 size_t len
= fread(&header
, 1, sizeof(header
), crx_file
);
43 if (len
< sizeof(header
))
46 crx_file::CrxFile::Error error
;
47 scoped_ptr
<crx_file::CrxFile
> crx(
48 crx_file::CrxFile::Parse(header
, &error
));
51 is_delta_
= crx_file::CrxFile::HeaderIsDelta(header
);
53 std::vector
<uint8
> key(header
.key_size
);
54 len
= fread(&key
[0], sizeof(uint8
), header
.key_size
, crx_file
);
55 if (len
< header
.key_size
)
58 std::vector
<uint8
> signature(header
.signature_size
);
59 len
= fread(&signature
[0], sizeof(uint8
), header
.signature_size
, crx_file
);
60 if (len
< header
.signature_size
)
63 crypto::SignatureVerifier verifier
;
64 if (!verifier
.VerifyInit(crx_file::kSignatureAlgorithm
,
65 base::checked_cast
<int>(
66 sizeof(crx_file::kSignatureAlgorithm
)),
68 base::checked_cast
<int>(signature
.size()),
70 base::checked_cast
<int>(key
.size()))) {
71 // Signature verification initialization failed. This is most likely
72 // caused by a public key in the wrong format (should encode algorithm).
76 const size_t kBufSize
= 8 * 1024;
77 scoped_ptr
<uint8
[]> buf(new uint8
[kBufSize
]);
78 while ((len
= fread(buf
.get(), 1, kBufSize
, crx_file
)) > 0)
79 verifier
.VerifyUpdate(buf
.get(), base::checked_cast
<int>(len
));
81 if (!verifier
.VerifyFinal())
84 public_key_
.swap(key
);
88 bool valid() const { return valid_
; }
90 bool is_delta() const { return is_delta_
; }
92 const std::vector
<uint8
>& public_key() const { return public_key_
; }
97 std::vector
<uint8
> public_key_
;
102 ComponentUnpacker::ComponentUnpacker(
103 const std::vector
<uint8
>& pk_hash
,
104 const base::FilePath
& path
,
105 const std::string
& fingerprint
,
106 ComponentInstaller
* installer
,
107 scoped_refptr
<OutOfProcessPatcher
> out_of_process_patcher
,
108 scoped_refptr
<base::SequencedTaskRunner
> task_runner
)
112 fingerprint_(fingerprint
),
113 installer_(installer
),
114 out_of_process_patcher_(out_of_process_patcher
),
117 task_runner_(task_runner
) {
120 // TODO(cpu): add a specific attribute check to a component json that the
121 // extension unpacker will reject, so that a component cannot be installed
123 scoped_ptr
<base::DictionaryValue
> ReadManifest(
124 const base::FilePath
& unpack_path
) {
125 base::FilePath manifest
=
126 unpack_path
.Append(FILE_PATH_LITERAL("manifest.json"));
127 if (!base::PathExists(manifest
))
128 return scoped_ptr
<base::DictionaryValue
>();
129 JSONFileValueSerializer
serializer(manifest
);
131 scoped_ptr
<base::Value
> root(serializer
.Deserialize(NULL
, &error
));
133 return scoped_ptr
<base::DictionaryValue
>();
134 if (!root
->IsType(base::Value::TYPE_DICTIONARY
))
135 return scoped_ptr
<base::DictionaryValue
>();
136 return scoped_ptr
<base::DictionaryValue
>(
137 static_cast<base::DictionaryValue
*>(root
.release())).Pass();
140 bool ComponentUnpacker::UnpackInternal() {
141 return Verify() && Unzip() && BeginPatching();
144 void ComponentUnpacker::Unpack(const Callback
& callback
) {
145 callback_
= callback
;
146 if (!UnpackInternal())
150 bool ComponentUnpacker::Verify() {
151 VLOG(1) << "Verifying component: " << path_
.value();
152 if (pk_hash_
.empty() || path_
.empty()) {
153 error_
= kInvalidParams
;
156 // First, validate the CRX header and signature. As of today
157 // this is SHA1 with RSA 1024.
158 base::ScopedFILE
file(base::OpenFile(path_
, "rb"));
160 error_
= kInvalidFile
;
163 CRXValidator
validator(file
.get());
165 if (!validator
.valid()) {
166 error_
= kInvalidFile
;
169 is_delta_
= validator
.is_delta();
171 // File is valid and the digital signature matches. Now make sure
172 // the public key hash matches the expected hash. If they do we fully
175 scoped_ptr
<SecureHash
> sha256(SecureHash::Create(SecureHash::SHA256
));
176 sha256
->Update(&(validator
.public_key()[0]), validator
.public_key().size());
177 sha256
->Finish(hash
, arraysize(hash
));
179 if (!std::equal(pk_hash_
.begin(), pk_hash_
.end(), hash
)) {
180 VLOG(1) << "Hash mismatch: " << path_
.value();
184 VLOG(1) << "Verification successful: " << path_
.value();
188 bool ComponentUnpacker::Unzip() {
189 base::FilePath
& destination
= is_delta_
? unpack_diff_path_
: unpack_path_
;
190 VLOG(1) << "Unpacking in: " << destination
.value();
191 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
193 VLOG(1) << "Unable to create temporary directory for unpacking.";
194 error_
= kUnzipPathError
;
197 if (!zip::Unzip(path_
, destination
)) {
198 VLOG(1) << "Unzipping failed.";
199 error_
= kUnzipFailed
;
202 VLOG(1) << "Unpacked successfully";
206 bool ComponentUnpacker::BeginPatching() {
207 if (is_delta_
) { // Package is a diff package.
208 // Use a different temp directory for the patch output files.
209 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
211 error_
= kUnzipPathError
;
214 patcher_
= new ComponentPatcher(unpack_diff_path_
,
217 out_of_process_patcher_
,
219 task_runner_
->PostTask(
221 base::Bind(&ComponentPatcher::Start
,
223 base::Bind(&ComponentUnpacker::EndPatching
,
224 scoped_refptr
<ComponentUnpacker
>(this))));
226 task_runner_
->PostTask(FROM_HERE
,
227 base::Bind(&ComponentUnpacker::EndPatching
,
228 scoped_refptr
<ComponentUnpacker
>(this),
235 void ComponentUnpacker::EndPatching(Error error
, int extended_error
) {
237 extended_error_
= extended_error
;
239 if (error_
!= kNone
) {
243 // Optimization: clean up patch files early, in case disk space is too low to
244 // install otherwise.
245 if (!unpack_diff_path_
.empty()) {
246 base::DeleteFile(unpack_diff_path_
, true);
247 unpack_diff_path_
.clear();
253 void ComponentUnpacker::Install() {
254 // Write the fingerprint to disk.
255 if (static_cast<int>(fingerprint_
.size()) !=
257 unpack_path_
.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
258 fingerprint_
.c_str(),
259 base::checked_cast
<int>(fingerprint_
.size()))) {
260 error_
= kFingerprintWriteFailed
;
263 scoped_ptr
<base::DictionaryValue
> manifest(ReadManifest(unpack_path_
));
264 if (!manifest
.get()) {
265 error_
= kBadManifest
;
268 DCHECK(error_
== kNone
);
269 if (!installer_
->Install(*manifest
, unpack_path_
)) {
270 error_
= kInstallerError
;
275 void ComponentUnpacker::Finish() {
276 if (!unpack_diff_path_
.empty())
277 base::DeleteFile(unpack_diff_path_
, true);
278 if (!unpack_path_
.empty())
279 base::DeleteFile(unpack_path_
, true);
280 callback_
.Run(error_
, extended_error_
);
283 ComponentUnpacker::~ComponentUnpacker() {
286 } // namespace component_updater