Chromium Code Reviews| 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/logging.h" | 14 #include "base/logging.h" |
| 13 #include "base/memory/scoped_handle.h" | 15 #include "base/memory/scoped_handle.h" |
| 14 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
| 15 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
| 16 #include "base/values.h" | 18 #include "base/values.h" |
| 17 #include "chrome/browser/component_updater/component_patcher.h" | 19 #include "chrome/browser/component_updater/component_patcher.h" |
| 18 #include "chrome/browser/component_updater/component_updater_service.h" | 20 #include "chrome/browser/component_updater/component_updater_service.h" |
| 19 #include "chrome/common/extensions/extension_constants.h" | 21 #include "chrome/common/extensions/extension_constants.h" |
| 22 #include "content/public/browser/browser_thread.h" | |
| 20 #include "crypto/secure_hash.h" | 23 #include "crypto/secure_hash.h" |
| 21 #include "crypto/signature_verifier.h" | 24 #include "crypto/signature_verifier.h" |
| 22 #include "extensions/common/crx_file.h" | 25 #include "extensions/common/crx_file.h" |
| 23 #include "third_party/zlib/google/zip.h" | 26 #include "third_party/zlib/google/zip.h" |
| 24 | 27 |
| 25 using crypto::SecureHash; | 28 using crypto::SecureHash; |
| 26 | 29 |
| 27 namespace { | 30 namespace { |
| 28 | 31 |
| 29 // This class makes sure that the CRX digital signature is valid | 32 // This class makes sure that the CRX digital signature is valid |
| 30 // and well formed. | 33 // and well formed. |
| 31 class CRXValidator { | 34 class CRXValidator { |
| 32 public: | 35 public: |
| 33 explicit CRXValidator(FILE* crx_file) : valid_(false), delta_(false) { | 36 explicit CRXValidator(FILE* crx_file) : valid_(false), is_delta_(false) { |
| 34 extensions::CrxFile::Header header; | 37 extensions::CrxFile::Header header; |
| 35 size_t len = fread(&header, 1, sizeof(header), crx_file); | 38 size_t len = fread(&header, 1, sizeof(header), crx_file); |
| 36 if (len < sizeof(header)) | 39 if (len < sizeof(header)) |
| 37 return; | 40 return; |
| 38 | 41 |
| 39 extensions::CrxFile::Error error; | 42 extensions::CrxFile::Error error; |
| 40 scoped_ptr<extensions::CrxFile> crx( | 43 scoped_ptr<extensions::CrxFile> crx( |
| 41 extensions::CrxFile::Parse(header, &error)); | 44 extensions::CrxFile::Parse(header, &error)); |
| 42 if (!crx.get()) | 45 if (!crx.get()) |
| 43 return; | 46 return; |
| 44 delta_ = extensions::CrxFile::HeaderIsDelta(header); | 47 is_delta_ = extensions::CrxFile::HeaderIsDelta(header); |
| 45 | 48 |
| 46 std::vector<uint8> key(header.key_size); | 49 std::vector<uint8> key(header.key_size); |
| 47 len = fread(&key[0], sizeof(uint8), header.key_size, crx_file); | 50 len = fread(&key[0], sizeof(uint8), header.key_size, crx_file); |
| 48 if (len < header.key_size) | 51 if (len < header.key_size) |
| 49 return; | 52 return; |
| 50 | 53 |
| 51 std::vector<uint8> signature(header.signature_size); | 54 std::vector<uint8> signature(header.signature_size); |
| 52 len = fread(&signature[0], sizeof(uint8), header.signature_size, crx_file); | 55 len = fread(&signature[0], sizeof(uint8), header.signature_size, crx_file); |
| 53 if (len < header.signature_size) | 56 if (len < header.signature_size) |
| 54 return; | 57 return; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 70 | 73 |
| 71 if (!verifier.VerifyFinal()) | 74 if (!verifier.VerifyFinal()) |
| 72 return; | 75 return; |
| 73 | 76 |
| 74 public_key_.swap(key); | 77 public_key_.swap(key); |
| 75 valid_ = true; | 78 valid_ = true; |
| 76 } | 79 } |
| 77 | 80 |
| 78 bool valid() const { return valid_; } | 81 bool valid() const { return valid_; } |
| 79 | 82 |
| 80 bool delta() const { return delta_; } | 83 bool is_delta() const { return is_delta_; } |
| 81 | 84 |
| 82 const std::vector<uint8>& public_key() const { return public_key_; } | 85 const std::vector<uint8>& public_key() const { return public_key_; } |
| 83 | 86 |
| 84 private: | 87 private: |
| 85 bool valid_; | 88 bool valid_; |
| 86 bool delta_; | 89 bool is_delta_; |
| 87 std::vector<uint8> public_key_; | 90 std::vector<uint8> public_key_; |
| 88 }; | 91 }; |
| 89 | 92 |
| 90 } // namespace. | 93 } // namespace |
| 94 | |
| 95 namespace component_updater { | |
| 96 | |
| 97 ComponentUnpacker::ComponentUnpacker( | |
| 98 const std::vector<uint8>& pk_hash, | |
| 99 const base::FilePath& path, | |
| 100 const std::string& fingerprint, | |
| 101 ComponentPatcher* patcher, | |
| 102 ComponentInstaller* installer, | |
| 103 scoped_refptr<base::SequencedTaskRunner> task_runner) | |
| 104 : pk_hash_(pk_hash), | |
| 105 path_(path), | |
| 106 is_delta_(false), | |
| 107 fingerprint_(fingerprint), | |
| 108 patcher_(patcher), | |
| 109 installer_(installer), | |
| 110 error_(kNone), | |
| 111 extended_error_(0), | |
| 112 ptr_factory_(this), | |
| 113 task_runner_(task_runner) { | |
| 114 } | |
| 91 | 115 |
| 92 // TODO(cpu): add a specific attribute check to a component json that the | 116 // TODO(cpu): add a specific attribute check to a component json that the |
| 93 // extension unpacker will reject, so that a component cannot be installed | 117 // extension unpacker will reject, so that a component cannot be installed |
| 94 // as an extension. | 118 // as an extension. |
| 95 scoped_ptr<base::DictionaryValue> ReadManifest( | 119 scoped_ptr<base::DictionaryValue> ReadManifest( |
| 96 const base::FilePath& unpack_path) { | 120 const base::FilePath& unpack_path) { |
| 97 base::FilePath manifest = | 121 base::FilePath manifest = |
| 98 unpack_path.Append(FILE_PATH_LITERAL("manifest.json")); | 122 unpack_path.Append(FILE_PATH_LITERAL("manifest.json")); |
| 99 if (!base::PathExists(manifest)) | 123 if (!base::PathExists(manifest)) |
| 100 return scoped_ptr<base::DictionaryValue>(); | 124 return scoped_ptr<base::DictionaryValue>(); |
| 101 JSONFileValueSerializer serializer(manifest); | 125 JSONFileValueSerializer serializer(manifest); |
| 102 std::string error; | 126 std::string error; |
| 103 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); | 127 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); |
| 104 if (!root.get()) | 128 if (!root.get()) |
| 105 return scoped_ptr<base::DictionaryValue>(); | 129 return scoped_ptr<base::DictionaryValue>(); |
| 106 if (!root->IsType(base::Value::TYPE_DICTIONARY)) | 130 if (!root->IsType(base::Value::TYPE_DICTIONARY)) |
| 107 return scoped_ptr<base::DictionaryValue>(); | 131 return scoped_ptr<base::DictionaryValue>(); |
| 108 return scoped_ptr<base::DictionaryValue>( | 132 return scoped_ptr<base::DictionaryValue>( |
| 109 static_cast<base::DictionaryValue*>(root.release())).Pass(); | 133 static_cast<base::DictionaryValue*>(root.release())).Pass(); |
| 110 } | 134 } |
| 111 | 135 |
| 112 ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash, | 136 // At the end of this execution, either Finish() will be called or |
| 113 const base::FilePath& path, | 137 // DonePatching() will be called asynchronously. |
| 114 const std::string& fingerprint, | 138 void ComponentUnpacker::Unpack( |
| 115 ComponentPatcher* patcher, | 139 const base::Callback<void(Error, int)>& callback) { |
| 116 ComponentInstaller* installer) | 140 callback_ = callback; |
| 117 : error_(kNone), | 141 if (!Unzip()) { |
| 118 extended_error_(0) { | 142 Finish(); |
| 119 if (pk_hash.empty() || path.empty()) { | 143 return; |
| 144 } | |
| 145 if (!BeginPatching()) | |
| 146 Finish(); | |
| 147 } | |
| 148 | |
| 149 bool ComponentUnpacker::Unzip() { | |
| 150 if (pk_hash_.empty() || path_.empty()) { | |
| 120 error_ = kInvalidParams; | 151 error_ = kInvalidParams; |
| 121 return; | 152 return false; |
| 122 } | 153 } |
| 123 // First, validate the CRX header and signature. As of today | 154 // First, validate the CRX header and signature. As of today |
| 124 // this is SHA1 with RSA 1024. | 155 // this is SHA1 with RSA 1024. |
| 125 ScopedStdioHandle file(file_util::OpenFile(path, "rb")); | 156 ScopedStdioHandle file(file_util::OpenFile(path_, "rb")); |
| 126 if (!file.get()) { | 157 if (!file.get()) { |
| 127 error_ = kInvalidFile; | 158 error_ = kInvalidFile; |
| 128 return; | 159 return false; |
| 129 } | 160 } |
| 130 CRXValidator validator(file.get()); | 161 CRXValidator validator(file.get()); |
| 131 if (!validator.valid()) { | 162 if (!validator.valid()) { |
| 132 error_ = kInvalidFile; | 163 error_ = kInvalidFile; |
| 133 return; | 164 return false; |
| 134 } | 165 } |
| 135 file.Close(); | 166 is_delta_ = validator.is_delta(); |
| 136 | 167 |
| 137 // File is valid and the digital signature matches. Now make sure | 168 // File is valid and the digital signature matches. Now make sure |
| 138 // the public key hash matches the expected hash. If they do we fully | 169 // the public key hash matches the expected hash. If they do we fully |
| 139 // trust this CRX. | 170 // trust this CRX. |
| 140 uint8 hash[32]; | 171 uint8 hash[32] = {}; |
| 141 scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); | 172 scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); |
| 142 sha256->Update(&(validator.public_key()[0]), validator.public_key().size()); | 173 sha256->Update(&(validator.public_key()[0]), validator.public_key().size()); |
| 143 sha256->Finish(hash, arraysize(hash)); | 174 sha256->Finish(hash, arraysize(hash)); |
| 144 | 175 |
| 145 if (!std::equal(pk_hash.begin(), pk_hash.end(), hash)) { | 176 if (!std::equal(pk_hash_.begin(), pk_hash_.end(), hash)) { |
| 146 error_ = kInvalidId; | 177 error_ = kInvalidId; |
| 178 return false; | |
| 179 } | |
| 180 base::FilePath& destination = is_delta_ ? unpack_diff_path_ : unpack_path_; | |
| 181 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""), | |
| 182 &destination)) { | |
| 183 error_ = kUnzipPathError; | |
| 184 return false; | |
| 185 } | |
| 186 file.Close(); | |
|
Sorin Jianu
2013/11/27 00:34:01
Is |file| used after we have validated it? If not,
waffles
2013/11/27 01:26:43
Done.
| |
| 187 if (!zip::Unzip(path_, destination)) | |
| 188 error_ = kUnzipFailed; | |
| 189 return true; | |
| 190 } | |
| 191 | |
| 192 | |
| 193 bool ComponentUnpacker::BeginPatching() { | |
| 194 if (is_delta_) { // Package is a diff package. | |
|
Sorin Jianu
2013/11/27 00:34:01
comment is not needed.
waffles
2013/11/27 01:26:43
Done.
| |
| 195 // We want a different temp directory to put the patch output files into. | |
| 196 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""), | |
| 197 &unpack_path_)) { | |
| 198 error_ = kUnzipPathError; | |
| 199 return false; | |
| 200 } | |
| 201 task_runner_->PostTask( | |
| 202 FROM_HERE, base::Bind(&DifferentialUpdatePatch, | |
| 203 unpack_diff_path_, | |
| 204 unpack_path_, | |
| 205 patcher_, | |
| 206 installer_, | |
| 207 base::Bind(&ComponentUnpacker::DonePatching, | |
| 208 GetWeakPtr()))); | |
| 209 } else { | |
| 210 task_runner_->PostTask( | |
| 211 FROM_HERE, base::Bind(&ComponentUnpacker::DonePatching, | |
| 212 GetWeakPtr(), | |
| 213 kNone, | |
| 214 0)); | |
| 215 } | |
| 216 return true; | |
| 217 } | |
| 218 | |
| 219 void ComponentUnpacker::DonePatching(Error error, int extended_error) { | |
| 220 error_ = error; | |
| 221 extended_error_ = extended_error; | |
| 222 if (error_ != kNone) { | |
| 223 Finish(); | |
| 147 return; | 224 return; |
| 148 } | 225 } |
| 149 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""), | 226 // Optimization: clean up patch files early, in case we're too low on disk to |
| 150 &unpack_path_)) { | 227 // install otherwise. |
| 151 error_ = kUnzipPathError; | 228 if (!unpack_diff_path_.empty()) { |
| 229 base::DeleteFile(unpack_diff_path_, true); | |
| 230 unpack_diff_path_.clear(); | |
| 231 } | |
| 232 Install(); | |
| 233 Finish(); | |
| 234 } | |
| 235 | |
| 236 void ComponentUnpacker::Install() { | |
| 237 // Write the fingerprint to disk. | |
| 238 if (static_cast<int>(fingerprint_.size()) != | |
| 239 file_util::WriteFile( | |
| 240 unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")), | |
| 241 fingerprint_.c_str(), | |
| 242 fingerprint_.size())) { | |
| 243 error_ = kFingerprintWriteFailed; | |
| 152 return; | 244 return; |
| 153 } | 245 } |
| 154 if (validator.delta()) { // Package is a diff package. | 246 if (error_ == kNone) { |
| 155 // We want a different temp directory for the delta files; we'll put the | 247 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); |
| 156 // patch output into unpack_path_. | 248 if (!manifest.get()) { |
| 157 base::FilePath unpack_diff_path; | 249 error_ = kBadManifest; |
| 158 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""), | |
| 159 &unpack_diff_path)) { | |
| 160 error_ = kUnzipPathError; | |
| 161 return; | 250 return; |
| 162 } | 251 } |
| 163 if (!zip::Unzip(path, unpack_diff_path)) { | 252 if (!installer_->Install(*manifest, unpack_path_)) { |
| 164 error_ = kUnzipFailed; | 253 error_ = kInstallerError; |
| 165 return; | |
| 166 } | |
| 167 ComponentUnpacker::Error result = DifferentialUpdatePatch(unpack_diff_path, | |
| 168 unpack_path_, | |
| 169 patcher, | |
| 170 installer, | |
| 171 &extended_error_); | |
| 172 base::DeleteFile(unpack_diff_path, true); | |
| 173 unpack_diff_path.clear(); | |
| 174 error_ = result; | |
| 175 if (error_ != kNone) { | |
| 176 return; | |
| 177 } | |
| 178 } else { | |
| 179 // Package is a normal update/install; unzip it into unpack_path_ directly. | |
| 180 if (!zip::Unzip(path, unpack_path_)) { | |
| 181 error_ = kUnzipFailed; | |
| 182 return; | 254 return; |
| 183 } | 255 } |
| 184 } | 256 } |
| 185 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); | 257 } |
| 186 if (!manifest.get()) { | 258 |
| 187 error_ = kBadManifest; | 259 void ComponentUnpacker::Finish() { |
| 188 return; | 260 if (!unpack_diff_path_.empty()) |
| 189 } | 261 base::DeleteFile(unpack_diff_path_, true); |
| 190 // Write the fingerprint to disk. | 262 if (!unpack_path_.empty()) |
| 191 if (static_cast<int>(fingerprint.size()) != | 263 base::DeleteFile(unpack_path_, true); |
| 192 file_util::WriteFile( | 264 callback_.Run(error_, extended_error_); |
| 193 unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")), | 265 } |
| 194 fingerprint.c_str(), | 266 |
| 195 fingerprint.size())) { | 267 base::WeakPtr<ComponentUnpacker> ComponentUnpacker::GetWeakPtr() { |
| 196 error_ = kFingerprintWriteFailed; | 268 return ptr_factory_.GetWeakPtr(); |
| 197 return; | |
| 198 } | |
| 199 if (!installer->Install(*manifest, unpack_path_)) { | |
| 200 error_ = kInstallerError; | |
| 201 return; | |
| 202 } | |
| 203 // Installation successful. The directory is not our concern now. | |
| 204 unpack_path_.clear(); | |
| 205 } | 269 } |
| 206 | 270 |
| 207 ComponentUnpacker::~ComponentUnpacker() { | 271 ComponentUnpacker::~ComponentUnpacker() { |
| 208 if (!unpack_path_.empty()) | |
| 209 base::DeleteFile(unpack_path_, true); | |
| 210 } | 272 } |
| 273 | |
| 274 } // namespace component_updater | |
| OLD | NEW |