Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: chrome/browser/component_updater/component_unpacker.cc

Issue 25883006: Support asynchronous patching operations in the component updater. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@tests
Patch Set: New LKGR, Windows fixes. Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/component_updater/component_unpacker.h" 5 #include "chrome/browser/component_updater/component_unpacker.h"
6 6
7 #include <string> 7 #include <string>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/bind.h"
10 #include "base/file_util.h" 11 #include "base/file_util.h"
12 #include "base/files/file_path.h"
11 #include "base/json/json_file_value_serializer.h" 13 #include "base/json/json_file_value_serializer.h"
12 #include "base/memory/scoped_handle.h" 14 #include "base/memory/scoped_handle.h"
13 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/stringprintf.h" 16 #include "base/strings/stringprintf.h"
15 #include "chrome/browser/component_updater/component_patcher.h" 17 #include "chrome/browser/component_updater/component_patcher.h"
16 #include "chrome/browser/component_updater/component_updater_service.h" 18 #include "chrome/browser/component_updater/component_updater_service.h"
17 #include "chrome/common/extensions/extension_constants.h" 19 #include "chrome/common/extensions/extension_constants.h"
20 #include "content/public/browser/browser_thread.h"
18 #include "crypto/secure_hash.h" 21 #include "crypto/secure_hash.h"
19 #include "crypto/signature_verifier.h" 22 #include "crypto/signature_verifier.h"
20 #include "extensions/common/crx_file.h" 23 #include "extensions/common/crx_file.h"
21 #include "third_party/zlib/google/zip.h" 24 #include "third_party/zlib/google/zip.h"
22 25
23 using crypto::SecureHash; 26 using crypto::SecureHash;
24 27
25 namespace { 28 namespace {
26 29
27 // This class makes sure that the CRX digital signature is valid 30 // This class makes sure that the CRX digital signature is valid
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
97 JSONFileValueSerializer serializer(manifest); 100 JSONFileValueSerializer serializer(manifest);
98 std::string error; 101 std::string error;
99 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); 102 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error));
100 if (!root.get()) 103 if (!root.get())
101 return NULL; 104 return NULL;
102 if (!root->IsType(base::Value::TYPE_DICTIONARY)) 105 if (!root->IsType(base::Value::TYPE_DICTIONARY))
103 return NULL; 106 return NULL;
104 return static_cast<base::DictionaryValue*>(root.release()); 107 return static_cast<base::DictionaryValue*>(root.release());
105 } 108 }
106 109
107 } // namespace. 110 // In charge of unpacking the component CRX package and verifying that it is
111 // well formed and the cryptographic signature is correct. If there is no
112 // error the component specific installer will be invoked to proceed with
113 // the component installation or update.
114 //
115 // This class should be used only by the component updater. It is inspired
116 // and overlaps with code in the extension's SandboxedUnpacker.
117 // The main differences are:
118 // - The public key hash is full SHA256.
119 // - Does not use a sandboxed unpacker. A valid component is fully trusted.
120 // - The manifest can have different attributes and resources are not
121 // transcoded.
cpu_(ooo_6.6-7.5) 2013/10/25 23:59:26 can we get a bit of an ascii flow chart here? to m
waffles 2013/10/28 22:13:23 Done.
122 class ComponentUnpacker {
123 public:
124 // Constructs an unpacker for a specific component unpacking operation.
125 // |pk_hash| is the expected
126 // public key SHA256 hash. |path| is the current location of
127 // the CRX. When done, runs |callback| with the error and extra error code.
128 ComponentUnpacker(const std::vector<uint8>& pk_hash,
129 const base::FilePath& path,
130 const std::string& fingerprint,
131 ComponentPatcher* patcher,
132 ComponentInstaller* installer);
108 133
109 ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash, 134 virtual ~ComponentUnpacker();
110 const base::FilePath& path, 135
111 const std::string& fingerprint, 136 // This helper posts a task to call |callback| with |error| and
112 ComponentPatcher* patcher, 137 // |extended_error|. It is a method in order to take advantage of
113 ComponentInstaller* installer) 138 // base::Bind()'s ownership features. The task is posted to the FILE thread.
114 : error_(kNone), 139 void CallbackHelper(
115 extended_error_(0) { 140 const base::Callback<void(component_updater::Error, int)>& callback,
116 if (pk_hash.empty() || path.empty()) { 141 component_updater::Error error,
117 error_ = kInvalidParams; 142 int extended_error);
143
144 // Begin the actual unpacking of the files. May invoke a patcher if the
145 // package is a differential update.
146 void Start(
147 const base::Callback<void(component_updater::Error, int)>& callback);
148
149 private:
150 // The first step of unpacking is to unzip.
151 void Unzip();
152
153 // The second step is to optionally patch files - this is a no-op for
154 // full (non-differential) updates. This step is asynchronous.
155 void BeginPatching();
156
157 // When patching is complete, DonePatching is called before moving on to step
158 // three.
159 void DonePatching(component_updater::Error error, int extended_error);
160
161 // The third step is to install the component.
162 void Install();
163
164 // The final step is to do clean-up for things that can't be tidied as we go.
165 // If there is an error at any step, the remaining steps are skipped and
166 // and Finish is called.
167 // Finish is responsible for calling the callback provided in Start().
168 void Finish();
169
170 // Returns a weak pointer to this object.
171 base::WeakPtr<ComponentUnpacker> GetWeakPtr();
172
173 void UnzipHelper();
174
175 void BeginPatchingHelper();
176
177 void InstallHelper();
178
179 std::vector<uint8> pk_hash_;
180 base::FilePath path_;
181 base::FilePath unpack_path_;
182 base::FilePath unpack_diff_path_;
183 bool delta_;
184 std::string fingerprint_;
185 ComponentPatcher* patcher_;
186 ComponentInstaller* installer_;
187 base::Callback<void(component_updater::Error, int)> callback_;
188 component_updater::Error error_;
189 int extended_error_;
190 base::WeakPtrFactory<ComponentUnpacker> ptr_factory_;
191 };
192
193 ComponentUnpacker::ComponentUnpacker(
194 const std::vector<uint8>& pk_hash,
195 const base::FilePath& path,
196 const std::string& fingerprint,
197 ComponentPatcher* patcher,
198 ComponentInstaller* installer)
199 : pk_hash_(pk_hash),
200 path_(path),
201 delta_(false),
202 fingerprint_(fingerprint),
203 patcher_(patcher),
204 installer_(installer),
205 error_(component_updater::kNone),
206 extended_error_(0),
207 ptr_factory_(this) {
208 }
209
210 void ComponentUnpacker::CallbackHelper(
211 const base::Callback<void(component_updater::Error, int)>& callback,
212 component_updater::Error error,
213 int extended_error) {
214 content::BrowserThread::PostTask(
215 content::BrowserThread::FILE,
216 FROM_HERE,
217 base::Bind(callback, error, extended_error));
218 }
219
220 void ComponentUnpacker::Start(
221 const base::Callback<void(component_updater::Error, int)>& callback) {
222 callback_ = callback;
223 Unzip();
224 }
225
226 void ComponentUnpacker::Unzip() {
227 UnzipHelper();
228 if (error_ != component_updater::kNone)
229 Finish();
230 else
231 BeginPatching();
232 }
233
234 void ComponentUnpacker::BeginPatching() {
235 BeginPatchingHelper();
236 if (error_ != component_updater::kNone)
237 Finish();
238 // Else, if the helper succeeds, DonePatching will be called asynchronously.
239 }
240
241 void ComponentUnpacker::DonePatching(component_updater::Error error,
242 int extended_error) {
243 error_ = error;
244 extended_error_ = extended_error;
245 if (delta_) {
246 base::DeleteFile(unpack_path_, true);
247 unpack_path_.clear();
248 }
249 if (error_ != component_updater::kNone)
250 Finish();
251 else
252 Install();
253 }
254
255 void ComponentUnpacker::Install() {
256 InstallHelper();
257 Finish();
258 }
259
260 void ComponentUnpacker::Finish() {
261 if (unpack_diff_path_.empty())
262 base::DeleteFile(unpack_diff_path_, true);
cpu_(ooo_6.6-7.5) 2013/10/25 23:59:26 don't get the logic of lines 261-262, deleting wit
waffles 2013/10/28 22:13:23 Bug; thanks.
263 if (unpack_path_.empty())
264 base::DeleteFile(unpack_path_, true);
cpu_(ooo_6.6-7.5) 2013/10/25 23:59:26 same
waffles 2013/10/28 22:13:23 Done.
265 callback_.Run(error_, extended_error_);
266 }
267
268 void ComponentUnpacker::UnzipHelper() {
269 if (pk_hash_.empty() || path_.empty()) {
270 error_ = component_updater::kInvalidParams;
118 return; 271 return;
119 } 272 }
120 // First, validate the CRX header and signature. As of today 273 // First, validate the CRX header and signature. As of today
121 // this is SHA1 with RSA 1024. 274 // this is SHA1 with RSA 1024.
122 ScopedStdioHandle file(file_util::OpenFile(path, "rb")); 275 ScopedStdioHandle file(file_util::OpenFile(path_, "rb"));
123 if (!file.get()) { 276 if (!file.get()) {
124 error_ = kInvalidFile; 277 error_ = component_updater::kInvalidFile;
125 return; 278 return;
126 } 279 }
127 CRXValidator validator(file.get()); 280 CRXValidator validator(file.get());
128 if (!validator.valid()) { 281 if (!validator.valid()) {
129 error_ = kInvalidFile; 282 error_ = component_updater::kInvalidFile;
130 return; 283 return;
131 } 284 }
132 file.Close(); 285 delta_ = validator.delta();
133 286
134 // File is valid and the digital signature matches. Now make sure 287 // File is valid and the digital signature matches. Now make sure
135 // the public key hash matches the expected hash. If they do we fully 288 // the public key hash matches the expected hash. If they do we fully
136 // trust this CRX. 289 // trust this CRX.
137 uint8 hash[32]; 290 uint8 hash[32];
138 scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); 291 scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256));
139 sha256->Update(&(validator.public_key()[0]), validator.public_key().size()); 292 sha256->Update(&(validator.public_key()[0]), validator.public_key().size());
140 sha256->Finish(hash, arraysize(hash)); 293 sha256->Finish(hash, arraysize(hash));
141 294
142 if (!std::equal(pk_hash.begin(), pk_hash.end(), hash)) { 295 if (!std::equal(pk_hash_.begin(), pk_hash_.end(), hash)) {
143 error_ = kInvalidId; 296 error_ = component_updater::kInvalidId;
144 return; 297 return;
145 } 298 }
146 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""), 299 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""),
147 &unpack_path_)) { 300 &unpack_path_)) {
148 error_ = kUnzipPathError; 301 error_ = component_updater::kUnzipPathError;
149 return; 302 return;
150 } 303 }
151 if (validator.delta()) { // Package is a diff package. 304 file.Close(); // TODO(waffles): Is it safe to close the file? It could change.
152 // We want a different temp directory for the delta files; we'll put the 305 if (!zip::Unzip(path_, unpack_path_))
153 // patch output into unpack_path_. 306 error_ = component_updater::kUnzipFailed;
154 base::FilePath unpack_diff_path; 307 }
308
309
310 void ComponentUnpacker::BeginPatchingHelper() {
311 if (delta_) { // Package is a diff package.
312 // We want a different temp directory to put the patch output files into.
155 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""), 313 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""),
156 &unpack_diff_path)) { 314 &unpack_diff_path_)) {
157 error_ = kUnzipPathError; 315 error_ = component_updater::kUnzipPathError;
158 return; 316 return;
159 } 317 }
cpu_(ooo_6.6-7.5) 2013/10/25 23:59:26 we are in the file thread, aren't we?
waffles 2013/10/28 22:13:23 This is something I'm struggling with in this code
160 if (!zip::Unzip(path, unpack_diff_path)) { 318 content::BrowserThread::PostTask(
161 error_ = kUnzipFailed; 319 content::BrowserThread::FILE,
320 FROM_HERE,
321 base::Bind(&DifferentialUpdatePatch,
322 unpack_path_,
323 unpack_diff_path_,
324 patcher_,
325 installer_,
326 base::Bind(&ComponentUnpacker::DonePatching,
327 GetWeakPtr())));
328 } else {
329 // Post a task instead of doing a direct call; otherwise we can call
330 // Finish() twice.
331 content::BrowserThread::PostTask(
332 content::BrowserThread::FILE,
333 FROM_HERE,
334 base::Bind(&ComponentUnpacker::DonePatching,
335 GetWeakPtr(),
336 component_updater::kNone,
337 0));
338 }
339 }
340
341 void ComponentUnpacker::InstallHelper() {
342 base::FilePath inst_path;
343 if (delta_)
344 inst_path = unpack_diff_path_;
345 else
346 inst_path = unpack_path_;
347 // Write the fingerprint to disk.
348 if (static_cast<int>(fingerprint_.size()) !=
349 file_util::WriteFile(
350 inst_path.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
351 fingerprint_.c_str(),
352 fingerprint_.size())) {
353 error_ = component_updater::kFingerprintWriteFailed;
354 return;
355 }
356 if (error_ == component_updater::kNone) {
357 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(inst_path));
358 if (!manifest.get()) {
359 error_ = component_updater::kBadManifest;
162 return; 360 return;
163 } 361 }
164 ComponentUnpacker::Error result = DifferentialUpdatePatch(unpack_diff_path, 362 if (!installer_->Install(*manifest, inst_path)) {
165 unpack_path_, 363 error_ = component_updater::kInstallerError;
166 patcher,
167 installer,
168 &extended_error_);
169 base::DeleteFile(unpack_diff_path, true);
170 unpack_diff_path.clear();
171 error_ = result;
172 if (error_ != kNone) {
173 return;
174 }
175 } else {
176 // Package is a normal update/install; unzip it into unpack_path_ directly.
177 if (!zip::Unzip(path, unpack_path_)) {
178 error_ = kUnzipFailed;
179 return; 364 return;
180 } 365 }
181 } 366 }
182 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); 367 }
183 if (!manifest.get()) { 368
184 error_ = kBadManifest; 369 base::WeakPtr<ComponentUnpacker> ComponentUnpacker::GetWeakPtr() {
185 return; 370 return ptr_factory_.GetWeakPtr();
186 }
187 // Write the fingerprint to disk.
188 if (static_cast<int>(fingerprint.size()) !=
189 file_util::WriteFile(
190 unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
191 fingerprint.c_str(),
192 fingerprint.size())) {
193 error_ = kFingerprintWriteFailed;
194 return;
195 }
196 if (!installer->Install(*manifest, unpack_path_)) {
197 error_ = kInstallerError;
198 return;
199 }
200 // Installation successful. The directory is not our concern now.
201 unpack_path_.clear();
202 } 371 }
203 372
204 ComponentUnpacker::~ComponentUnpacker() { 373 ComponentUnpacker::~ComponentUnpacker() {
205 if (!unpack_path_.empty())
206 base::DeleteFile(unpack_path_, true);
207 } 374 }
375
376 } // namespace.
377
378 void component_updater::Unpack(
379 const std::vector<uint8>& pk_hash,
380 const base::FilePath& path,
381 const std::string& fingerprint,
382 ComponentPatcher* patcher,
383 ComponentInstaller* installer,
384 const base::Callback<void(component_updater::Error, int)>& callback) {
385 // A callback wrapper will own the unpacker object.
386 ComponentUnpacker* unpacker = new ComponentUnpacker(
387 pk_hash, path, fingerprint, patcher, installer);
388 unpacker->Start(base::Bind(&ComponentUnpacker::CallbackHelper,
389 base::Owned(unpacker),
390 callback));
391 }
OLDNEW
« no previous file with comments | « chrome/browser/component_updater/component_unpacker.h ('k') | chrome/browser/component_updater/component_updater_service.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698