Roll src/third_party/WebKit 9f7fb92:f103b33 (svn 202621:202622)
[chromium-blink-merge.git] / components / update_client / component_unpacker.cc
blob9dfd555b4745dd7da53260513a8e5622d415a299
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"
7 #include <stdint.h>
8 #include <string>
9 #include <vector>
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)
44 : pk_hash_(pk_hash),
45 path_(path),
46 is_delta_(false),
47 fingerprint_(fingerprint),
48 installer_(installer),
49 oop_patcher_(oop_patcher),
50 error_(kNone),
51 extended_error_(0),
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
57 // as an extension.
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);
65 std::string error;
66 scoped_ptr<base::Value> root(deserializer.Deserialize(NULL, &error));
67 if (!root.get())
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) {
80 callback_ = callback;
81 if (!UnpackInternal())
82 Finish();
85 bool ComponentUnpacker::Verify() {
86 VLOG(1) << "Verifying component: " << path_.value();
87 if (pk_hash_.empty() || path_.empty()) {
88 error_ = kInvalidParams;
89 return false;
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;
101 return false;
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
107 // trust this CRX.
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();
115 error_ = kInvalidId;
116 return false;
118 VLOG(1) << "Verification successful: " << path_.value();
119 return true;
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(),
126 &destination)) {
127 VLOG(1) << "Unable to create temporary directory for unpacking.";
128 error_ = kUnzipPathError;
129 return false;
131 if (!zip::Unzip(path_, destination)) {
132 VLOG(1) << "Unzipping failed.";
133 error_ = kUnzipFailed;
134 return false;
136 VLOG(1) << "Unpacked successfully";
137 return true;
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(),
144 &unpack_path_)) {
145 error_ = kUnzipPathError;
146 return false;
148 patcher_ = new ComponentPatcher(unpack_diff_path_, unpack_path_, installer_,
149 oop_patcher_, task_runner_);
150 task_runner_->PostTask(
151 FROM_HERE,
152 base::Bind(&ComponentPatcher::Start, patcher_,
153 base::Bind(&ComponentUnpacker::EndPatching,
154 scoped_refptr<ComponentUnpacker>(this))));
155 } else {
156 task_runner_->PostTask(
157 FROM_HERE,
158 base::Bind(&ComponentUnpacker::EndPatching,
159 scoped_refptr<ComponentUnpacker>(this), kNone, 0));
161 return true;
164 void ComponentUnpacker::EndPatching(Error error, int extended_error) {
165 error_ = error;
166 extended_error_ = extended_error;
167 patcher_ = NULL;
168 if (error_ != kNone) {
169 Finish();
170 return;
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();
178 Install();
179 Finish();
182 void ComponentUnpacker::Install() {
183 // Write the fingerprint to disk.
184 if (static_cast<int>(fingerprint_.size()) !=
185 base::WriteFile(
186 unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
187 fingerprint_.c_str(), base::checked_cast<int>(fingerprint_.size()))) {
188 error_ = kFingerprintWriteFailed;
189 return;
191 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_));
192 if (!manifest.get()) {
193 error_ = kBadManifest;
194 return;
196 DCHECK(error_ == kNone);
197 if (!installer_->Install(*manifest, unpack_path_)) {
198 error_ = kInstallerError;
199 return;
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