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/base64.h"
12 #include "base/bind.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_file.h"
16 #include "base/json/json_file_value_serializer.h"
17 #include "base/location.h"
18 #include "base/logging.h"
19 #include "base/numerics/safe_conversions.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/values.h"
23 #include "components/crx_file/constants.h"
24 #include "components/crx_file/crx_file.h"
25 #include "components/update_client/component_patcher.h"
26 #include "components/update_client/component_patcher_operation.h"
27 #include "components/update_client/update_client.h"
28 #include "crypto/secure_hash.h"
29 #include "crypto/sha2.h"
30 #include "third_party/zlib/google/zip.h"
32 using crypto::SecureHash
;
33 using crx_file::CrxFile
;
35 namespace update_client
{
37 ComponentUnpacker::ComponentUnpacker(
38 const std::vector
<uint8_t>& pk_hash
,
39 const base::FilePath
& path
,
40 const std::string
& fingerprint
,
41 const scoped_refptr
<CrxInstaller
>& installer
,
42 const scoped_refptr
<OutOfProcessPatcher
>& oop_patcher
,
43 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
)
47 fingerprint_(fingerprint
),
48 installer_(installer
),
49 oop_patcher_(oop_patcher
),
52 task_runner_(task_runner
) {
55 // TODO(cpu): add a specific attribute check to a component json that the
56 // extension unpacker will reject, so that a component cannot be installed
58 scoped_ptr
<base::DictionaryValue
> ReadManifest(
59 const base::FilePath
& unpack_path
) {
60 base::FilePath manifest
=
61 unpack_path
.Append(FILE_PATH_LITERAL("manifest.json"));
62 if (!base::PathExists(manifest
))
63 return scoped_ptr
<base::DictionaryValue
>();
64 JSONFileValueDeserializer
deserializer(manifest
);
66 scoped_ptr
<base::Value
> root(deserializer
.Deserialize(NULL
, &error
));
68 return scoped_ptr
<base::DictionaryValue
>();
69 if (!root
->IsType(base::Value::TYPE_DICTIONARY
))
70 return scoped_ptr
<base::DictionaryValue
>();
71 return scoped_ptr
<base::DictionaryValue
>(
72 static_cast<base::DictionaryValue
*>(root
.release())).Pass();
75 bool ComponentUnpacker::UnpackInternal() {
76 return Verify() && Unzip() && BeginPatching();
79 void ComponentUnpacker::Unpack(const Callback
& callback
) {
81 if (!UnpackInternal())
85 bool ComponentUnpacker::Verify() {
86 VLOG(1) << "Verifying component: " << path_
.value();
87 if (pk_hash_
.empty() || path_
.empty()) {
88 error_
= kInvalidParams
;
91 // First, validate the CRX header and signature. As of today
92 // this is SHA1 with RSA 1024.
93 std::string public_key_bytes
;
94 std::string public_key_base64
;
95 CrxFile::Header header
;
96 CrxFile::ValidateError error
= CrxFile::ValidateSignature(
97 path_
, std::string(), &public_key_base64
, nullptr, &header
);
98 if (error
!= CrxFile::ValidateError::NONE
||
99 !base::Base64Decode(public_key_base64
, &public_key_bytes
)) {
100 error_
= kInvalidFile
;
103 is_delta_
= CrxFile::HeaderIsDelta(header
);
105 // File is valid and the digital signature matches. Now make sure
106 // the public key hash matches the expected hash. If they do we fully
108 uint8_t hash
[crypto::kSHA256Length
] = {};
109 scoped_ptr
<SecureHash
> sha256(SecureHash::Create(SecureHash::SHA256
));
110 sha256
->Update(public_key_bytes
.data(), public_key_bytes
.size());
111 sha256
->Finish(hash
, arraysize(hash
));
113 if (!std::equal(pk_hash_
.begin(), pk_hash_
.end(), hash
)) {
114 VLOG(1) << "Hash mismatch: " << path_
.value();
118 VLOG(1) << "Verification successful: " << path_
.value();
122 bool ComponentUnpacker::Unzip() {
123 base::FilePath
& destination
= is_delta_
? unpack_diff_path_
: unpack_path_
;
124 VLOG(1) << "Unpacking in: " << destination
.value();
125 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
127 VLOG(1) << "Unable to create temporary directory for unpacking.";
128 error_
= kUnzipPathError
;
131 if (!zip::Unzip(path_
, destination
)) {
132 VLOG(1) << "Unzipping failed.";
133 error_
= kUnzipFailed
;
136 VLOG(1) << "Unpacked successfully";
140 bool ComponentUnpacker::BeginPatching() {
141 if (is_delta_
) { // Package is a diff package.
142 // Use a different temp directory for the patch output files.
143 if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
145 error_
= kUnzipPathError
;
148 patcher_
= new ComponentPatcher(unpack_diff_path_
, unpack_path_
, installer_
,
149 oop_patcher_
, task_runner_
);
150 task_runner_
->PostTask(
152 base::Bind(&ComponentPatcher::Start
, patcher_
,
153 base::Bind(&ComponentUnpacker::EndPatching
,
154 scoped_refptr
<ComponentUnpacker
>(this))));
156 task_runner_
->PostTask(
158 base::Bind(&ComponentUnpacker::EndPatching
,
159 scoped_refptr
<ComponentUnpacker
>(this), kNone
, 0));
164 void ComponentUnpacker::EndPatching(Error error
, int extended_error
) {
166 extended_error_
= extended_error
;
168 if (error_
!= kNone
) {
172 // Optimization: clean up patch files early, in case disk space is too low to
173 // install otherwise.
174 if (!unpack_diff_path_
.empty()) {
175 base::DeleteFile(unpack_diff_path_
, true);
176 unpack_diff_path_
.clear();
182 void ComponentUnpacker::Install() {
183 // Write the fingerprint to disk.
184 if (static_cast<int>(fingerprint_
.size()) !=
186 unpack_path_
.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
187 fingerprint_
.c_str(), base::checked_cast
<int>(fingerprint_
.size()))) {
188 error_
= kFingerprintWriteFailed
;
191 scoped_ptr
<base::DictionaryValue
> manifest(ReadManifest(unpack_path_
));
192 if (!manifest
.get()) {
193 error_
= kBadManifest
;
196 DCHECK(error_
== kNone
);
197 if (!installer_
->Install(*manifest
, unpack_path_
)) {
198 error_
= kInstallerError
;
203 void ComponentUnpacker::Finish() {
204 if (!unpack_diff_path_
.empty())
205 base::DeleteFile(unpack_diff_path_
, true);
206 if (!unpack_path_
.empty())
207 base::DeleteFile(unpack_path_
, true);
208 task_runner_
->PostTask(FROM_HERE
,
209 base::Bind(callback_
, error_
, extended_error_
));
212 ComponentUnpacker::~ComponentUnpacker() {
215 } // namespace update_client