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/bind.h" |
(...skipping 13 matching lines...) Expand all Loading... |
24 #include "extensions/common/constants.h" | 24 #include "extensions/common/constants.h" |
25 #include "extensions/common/crx_file.h" | 25 #include "extensions/common/crx_file.h" |
26 #include "third_party/zlib/google/zip.h" | 26 #include "third_party/zlib/google/zip.h" |
27 | 27 |
28 using crypto::SecureHash; | 28 using crypto::SecureHash; |
29 | 29 |
30 namespace component_updater { | 30 namespace component_updater { |
31 | 31 |
32 namespace { | 32 namespace { |
33 | 33 |
| 34 namespace serialize { |
| 35 |
| 36 const char kKeyFingerprint[] = "fingerprint"; |
| 37 const char kKeyPKHash[] = "public_key_hash"; |
| 38 const char kKeyInProcess[] = "in_process"; |
| 39 |
| 40 const base::FilePath::CharType kCrxFileName[] = |
| 41 FILE_PATH_LITERAL("saved_installer.crx"); |
| 42 const base::FilePath::CharType kInstallDataFileName[] = |
| 43 FILE_PATH_LITERAL("metadata.json"); |
| 44 |
| 45 } // namespace serialize |
| 46 |
34 // This class makes sure that the CRX digital signature is valid | 47 // This class makes sure that the CRX digital signature is valid |
35 // and well formed. | 48 // and well formed. |
36 class CRXValidator { | 49 class CRXValidator { |
37 public: | 50 public: |
38 explicit CRXValidator(FILE* crx_file) : valid_(false), is_delta_(false) { | 51 explicit CRXValidator(FILE* crx_file) : valid_(false), is_delta_(false) { |
39 extensions::CrxFile::Header header; | 52 extensions::CrxFile::Header header; |
40 size_t len = fread(&header, 1, sizeof(header), crx_file); | 53 size_t len = fread(&header, 1, sizeof(header), crx_file); |
41 if (len < sizeof(header)) | 54 if (len < sizeof(header)) |
42 return; | 55 return; |
43 | 56 |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
117 // TODO(cpu): add a specific attribute check to a component json that the | 130 // TODO(cpu): add a specific attribute check to a component json that the |
118 // extension unpacker will reject, so that a component cannot be installed | 131 // extension unpacker will reject, so that a component cannot be installed |
119 // as an extension. | 132 // as an extension. |
120 scoped_ptr<base::DictionaryValue> ReadManifest( | 133 scoped_ptr<base::DictionaryValue> ReadManifest( |
121 const base::FilePath& unpack_path) { | 134 const base::FilePath& unpack_path) { |
122 base::FilePath manifest = | 135 base::FilePath manifest = |
123 unpack_path.Append(FILE_PATH_LITERAL("manifest.json")); | 136 unpack_path.Append(FILE_PATH_LITERAL("manifest.json")); |
124 if (!base::PathExists(manifest)) | 137 if (!base::PathExists(manifest)) |
125 return scoped_ptr<base::DictionaryValue>(); | 138 return scoped_ptr<base::DictionaryValue>(); |
126 JSONFileValueSerializer serializer(manifest); | 139 JSONFileValueSerializer serializer(manifest); |
127 std::string error; | 140 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, NULL)); |
128 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); | |
129 if (!root.get()) | 141 if (!root.get()) |
130 return scoped_ptr<base::DictionaryValue>(); | 142 return scoped_ptr<base::DictionaryValue>(); |
131 if (!root->IsType(base::Value::TYPE_DICTIONARY)) | 143 if (!root->IsType(base::Value::TYPE_DICTIONARY)) |
132 return scoped_ptr<base::DictionaryValue>(); | 144 return scoped_ptr<base::DictionaryValue>(); |
133 return scoped_ptr<base::DictionaryValue>( | 145 return scoped_ptr<base::DictionaryValue>( |
134 static_cast<base::DictionaryValue*>(root.release())).Pass(); | 146 static_cast<base::DictionaryValue*>(root.release())).Pass(); |
135 } | 147 } |
136 | 148 |
137 bool ComponentUnpacker::UnpackInternal() { | 149 bool ComponentUnpacker::UnpackInternal() { |
138 return Verify() && Unzip() && BeginPatching(); | 150 return Verify() && Unzip() && BeginPatching(); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
175 | 187 |
176 if (!std::equal(pk_hash_.begin(), pk_hash_.end(), hash)) { | 188 if (!std::equal(pk_hash_.begin(), pk_hash_.end(), hash)) { |
177 VLOG(1) << "Hash mismatch: " << path_.value(); | 189 VLOG(1) << "Hash mismatch: " << path_.value(); |
178 error_ = kInvalidId; | 190 error_ = kInvalidId; |
179 return false; | 191 return false; |
180 } | 192 } |
181 VLOG(1) << "Verification successful: " << path_.value(); | 193 VLOG(1) << "Verification successful: " << path_.value(); |
182 return true; | 194 return true; |
183 } | 195 } |
184 | 196 |
| 197 bool ComponentUnpacker::CreateUnpackDirectory(base::FilePath* new_dir) { |
| 198 if (unpack_base_dir_.empty()) { |
| 199 return base::CreateNewTempDirectory(base::FilePath::StringType(), new_dir); |
| 200 } |
| 201 |
| 202 return base::CreateTemporaryDirInDir( |
| 203 unpack_base_dir_, base::FilePath::StringType(), new_dir); |
| 204 } |
| 205 |
185 bool ComponentUnpacker::Unzip() { | 206 bool ComponentUnpacker::Unzip() { |
186 base::FilePath& destination = is_delta_ ? unpack_diff_path_ : unpack_path_; | 207 base::FilePath& destination = is_delta_ ? unpack_diff_path_ : unpack_path_; |
187 VLOG(1) << "Unpacking in: " << destination.value(); | 208 VLOG(1) << "Unpacking in: " << destination.value(); |
188 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), | 209 if (!CreateUnpackDirectory(&destination)) { |
189 &destination)) { | |
190 VLOG(1) << "Unable to create temporary directory for unpacking."; | 210 VLOG(1) << "Unable to create temporary directory for unpacking."; |
191 error_ = kUnzipPathError; | 211 error_ = kUnzipPathError; |
192 return false; | 212 return false; |
193 } | 213 } |
194 if (!zip::Unzip(path_, destination)) { | 214 if (!zip::Unzip(path_, destination)) { |
195 VLOG(1) << "Unzipping failed."; | 215 VLOG(1) << "Unzipping failed."; |
196 error_ = kUnzipFailed; | 216 error_ = kUnzipFailed; |
197 return false; | 217 return false; |
198 } | 218 } |
199 VLOG(1) << "Unpacked successfully"; | 219 VLOG(1) << "Unpacked successfully"; |
200 return true; | 220 return true; |
201 } | 221 } |
202 | 222 |
203 bool ComponentUnpacker::BeginPatching() { | 223 bool ComponentUnpacker::BeginPatching() { |
204 if (is_delta_) { // Package is a diff package. | 224 if (is_delta_) { // Package is a diff package. |
205 // Use a different temp directory for the patch output files. | 225 // Use a different temp directory for the patch output files. |
206 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), | 226 if (!CreateUnpackDirectory(&unpack_path_)) { |
207 &unpack_path_)) { | |
208 error_ = kUnzipPathError; | 227 error_ = kUnzipPathError; |
209 return false; | 228 return false; |
210 } | 229 } |
211 patcher_ = new ComponentPatcher(unpack_diff_path_, | 230 patcher_ = new ComponentPatcher(unpack_diff_path_, |
212 unpack_path_, | 231 unpack_path_, |
213 installer_, | 232 installer_, |
214 in_process_, | 233 in_process_, |
215 task_runner_); | 234 task_runner_); |
216 task_runner_->PostTask( | 235 task_runner_->PostTask( |
217 FROM_HERE, | 236 FROM_HERE, |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 error_ = kFingerprintWriteFailed; | 276 error_ = kFingerprintWriteFailed; |
258 return; | 277 return; |
259 } | 278 } |
260 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); | 279 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); |
261 if (!manifest.get()) { | 280 if (!manifest.get()) { |
262 error_ = kBadManifest; | 281 error_ = kBadManifest; |
263 return; | 282 return; |
264 } | 283 } |
265 DCHECK(error_ == kNone); | 284 DCHECK(error_ == kNone); |
266 if (!installer_->Install(*manifest, unpack_path_)) { | 285 if (!installer_->Install(*manifest, unpack_path_)) { |
| 286 // Saves the installer files and tries to re-install which is not managed |
| 287 // by the component updater service. |
| 288 if (SaveInstallSource(installer_->GetBackupPath())) { |
| 289 installer_->InstallExternally(); |
| 290 } |
| 291 |
| 292 // But keep current install error code since we cannot get the external |
| 293 // install result. |
267 error_ = kInstallerError; | 294 error_ = kInstallerError; |
268 return; | 295 return; |
269 } | 296 } |
270 } | 297 } |
271 | 298 |
272 void ComponentUnpacker::Finish() { | 299 void ComponentUnpacker::Finish() { |
273 if (!unpack_diff_path_.empty()) | 300 if (!unpack_diff_path_.empty()) |
274 base::DeleteFile(unpack_diff_path_, true); | 301 base::DeleteFile(unpack_diff_path_, true); |
275 if (!unpack_path_.empty()) | 302 if (!unpack_path_.empty()) |
276 base::DeleteFile(unpack_path_, true); | 303 base::DeleteFile(unpack_path_, true); |
277 callback_.Run(error_, extended_error_); | 304 callback_.Run(error_, extended_error_); |
278 } | 305 } |
279 | 306 |
280 ComponentUnpacker::~ComponentUnpacker() { | 307 ComponentUnpacker::~ComponentUnpacker() { |
281 } | 308 } |
282 | 309 |
| 310 bool ComponentUnpacker::SaveInstallSource( |
| 311 const base::FilePath& backup_path) const { |
| 312 if (backup_path.empty()) { |
| 313 return false; |
| 314 } |
| 315 |
| 316 if (!base::CreateDirectory(backup_path)) { |
| 317 return false; |
| 318 } |
| 319 |
| 320 const base::FilePath crx_path = backup_path.Append(serialize::kCrxFileName); |
| 321 if (!base::CopyFile(path_, crx_path)) { |
| 322 return false; |
| 323 } |
| 324 |
| 325 const base::FilePath install_data_file_path = |
| 326 backup_path.Append(serialize::kInstallDataFileName); |
| 327 JSONFileValueSerializer serializer(install_data_file_path); |
| 328 |
| 329 scoped_ptr<base::DictionaryValue> root(new base::DictionaryValue); |
| 330 root->SetString(serialize::kKeyFingerprint, fingerprint_); |
| 331 root->SetBoolean(serialize::kKeyInProcess, in_process_); |
| 332 scoped_ptr<base::ListValue> pk_hash_list(new base::ListValue); |
| 333 for (size_t i = 0; i < pk_hash_.size(); ++i) { |
| 334 pk_hash_list->Append(base::Value::CreateIntegerValue( |
| 335 static_cast<int>(pk_hash_[i]))); |
| 336 } |
| 337 root->Set(serialize::kKeyPKHash, pk_hash_list.release()); |
| 338 if (!serializer.Serialize(*root)) { |
| 339 return false; |
| 340 } |
| 341 |
| 342 return true; |
| 343 } |
| 344 |
| 345 void ComponentUnpacker::set_unpack_base_dir(const base::FilePath& path) { |
| 346 unpack_base_dir_ = path; |
| 347 } |
| 348 |
| 349 scoped_refptr<ComponentUnpacker> ComponentUnpacker::CreateFromBackup( |
| 350 const base::FilePath& backup_path, |
| 351 const std::vector<uint8>& pk_hash, |
| 352 ComponentInstaller* installer, |
| 353 scoped_refptr<base::SequencedTaskRunner> task_runner) { |
| 354 const base::FilePath crx_path = backup_path.Append(serialize::kCrxFileName); |
| 355 if (!base::PathExists(crx_path) || base::DirectoryExists(crx_path)) { |
| 356 return scoped_refptr<ComponentUnpacker>(); |
| 357 } |
| 358 |
| 359 std::string fingerprint; |
| 360 bool in_process = false; |
| 361 |
| 362 const base::FilePath install_data_file_path = |
| 363 backup_path.Append(serialize::kInstallDataFileName); |
| 364 if (!base::PathExists(install_data_file_path)) { |
| 365 return scoped_refptr<ComponentUnpacker>(); |
| 366 } |
| 367 |
| 368 JSONFileValueSerializer serialize(install_data_file_path); |
| 369 scoped_ptr<base::Value> root(serialize.Deserialize(NULL, NULL)); |
| 370 if (!root.get() || !root->IsType(base::Value::TYPE_DICTIONARY)) { |
| 371 return scoped_refptr<ComponentUnpacker>(); |
| 372 } |
| 373 |
| 374 scoped_ptr<base::DictionaryValue> metadata( |
| 375 static_cast<base::DictionaryValue*>(root.release())); |
| 376 |
| 377 base::ListValue* pk_hash_list = NULL; |
| 378 if (!metadata->GetStringASCII(serialize::kKeyFingerprint, &fingerprint) || |
| 379 !metadata->GetBoolean(serialize::kKeyInProcess, &in_process) || |
| 380 !metadata->GetList(serialize::kKeyPKHash, &pk_hash_list) || |
| 381 pk_hash_list == NULL || |
| 382 pk_hash_list->GetSize() != pk_hash.size()) { |
| 383 return scoped_refptr<ComponentUnpacker>(); |
| 384 } |
| 385 |
| 386 for (size_t i = 0; i < pk_hash.size(); ++i) { |
| 387 int value = 0; |
| 388 if (!pk_hash_list->GetInteger(i, &value) || |
| 389 pk_hash[i] != static_cast<uint8>(value)) { |
| 390 return scoped_refptr<ComponentUnpacker>(); |
| 391 } |
| 392 } |
| 393 |
| 394 return make_scoped_refptr(new ComponentUnpacker( |
| 395 pk_hash, crx_path, fingerprint, installer, in_process, task_runner)); |
| 396 } |
| 397 |
283 } // namespace component_updater | 398 } // namespace component_updater |
OLD | NEW |