OLD | NEW |
---|---|
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 Loading... | |
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 } | |
OLD | NEW |