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 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
80 bool delta() const { return delta_; } | 83 bool delta() const { return 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 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 delta_(false), | |
107 fingerprint_(fingerprint), | |
108 patcher_(patcher), | |
109 installer_(installer), | |
110 error_(component_updater::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 void ComponentUnpacker::Unpack( |
113 const base::FilePath& path, | 137 const base::Callback<void(component_updater::Error, int)>& callback) { |
114 const std::string& fingerprint, | 138 callback_ = callback; |
115 ComponentPatcher* patcher, | 139 if (!Unzip()) { |
116 ComponentInstaller* installer) | 140 Finish(); |
117 : error_(kNone), | |
118 extended_error_(0) { | |
119 if (pk_hash.empty() || path.empty()) { | |
120 error_ = kInvalidParams; | |
121 return; | 141 return; |
122 } | 142 } |
143 if (!BeginPatching()) | |
144 Finish(); | |
145 // Else, DonePatching will be called asynchronously. | |
Sorin Jianu
2013/11/21 19:48:37
Consider rewriting so the comment does not look li
waffles
2013/11/26 00:46:55
Done.
| |
146 } | |
147 | |
148 bool ComponentUnpacker::Unzip() { | |
149 if (pk_hash_.empty() || path_.empty()) { | |
150 error_ = component_updater::kInvalidParams; | |
Sorin Jianu
2013/11/21 19:48:37
do we want to handle pk_hash_.empty() here or let
waffles
2013/11/26 00:46:55
I preferred this, since pk_hash_ is learned from o
| |
151 return false; | |
152 } | |
123 // First, validate the CRX header and signature. As of today | 153 // First, validate the CRX header and signature. As of today |
124 // this is SHA1 with RSA 1024. | 154 // this is SHA1 with RSA 1024. |
125 ScopedStdioHandle file(file_util::OpenFile(path, "rb")); | 155 ScopedStdioHandle file(file_util::OpenFile(path_, "rb")); |
126 if (!file.get()) { | 156 if (!file.get()) { |
127 error_ = kInvalidFile; | 157 error_ = component_updater::kInvalidFile; |
128 return; | 158 return false; |
129 } | 159 } |
130 CRXValidator validator(file.get()); | 160 CRXValidator validator(file.get()); |
131 if (!validator.valid()) { | 161 if (!validator.valid()) { |
132 error_ = kInvalidFile; | 162 error_ = component_updater::kInvalidFile; |
133 return; | 163 return false; |
134 } | 164 } |
135 file.Close(); | 165 delta_ = validator.delta(); |
136 | 166 |
137 // File is valid and the digital signature matches. Now make sure | 167 // 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 | 168 // the public key hash matches the expected hash. If they do we fully |
139 // trust this CRX. | 169 // trust this CRX. |
140 uint8 hash[32]; | 170 uint8 hash[32]; |
Sorin Jianu
2013/11/21 19:48:37
not initialized.
waffles
2013/11/26 00:46:55
Done.
| |
141 scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); | 171 scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); |
142 sha256->Update(&(validator.public_key()[0]), validator.public_key().size()); | 172 sha256->Update(&(validator.public_key()[0]), validator.public_key().size()); |
143 sha256->Finish(hash, arraysize(hash)); | 173 sha256->Finish(hash, arraysize(hash)); |
144 | 174 |
145 if (!std::equal(pk_hash.begin(), pk_hash.end(), hash)) { | 175 if (!std::equal(pk_hash_.begin(), pk_hash_.end(), hash)) { |
146 error_ = kInvalidId; | 176 error_ = component_updater::kInvalidId; |
177 return false; | |
178 } | |
179 base::FilePath* destination; | |
Sorin Jianu
2013/11/21 19:48:37
not initialized.
waffles
2013/11/26 00:46:55
Done.
| |
180 if (delta_) | |
Sorin Jianu
2013/11/21 19:48:37
can use ?:
waffles
2013/11/26 00:46:55
Done.
| |
181 destination = &unpack_diff_path_; | |
182 else | |
183 destination = &unpack_path_; | |
Sorin Jianu
2013/11/21 19:48:37
Better use a reference and pass the address of tha
waffles
2013/11/26 00:46:55
Done.
| |
184 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""), | |
185 destination)) { | |
186 error_ = component_updater::kUnzipPathError; | |
187 return false; | |
188 } | |
189 file.Close(); // TODO(waffles): Is it safe to close the file? It could change. | |
Sorin Jianu
2013/11/21 19:48:37
TODO is not clear
waffles
2013/11/26 00:46:55
Removed it. It's not clear that there can be a sol
| |
190 if (!zip::Unzip(path_, *destination)) | |
191 error_ = component_updater::kUnzipFailed; | |
192 return true; | |
193 } | |
194 | |
195 | |
196 bool ComponentUnpacker::BeginPatching() { | |
197 if (delta_) { // Package is a diff package. | |
198 // We want a different temp directory to put the patch output files into. | |
199 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""), | |
200 &unpack_path_)) { | |
201 error_ = component_updater::kUnzipPathError; | |
202 return false; | |
203 } | |
204 task_runner_->PostTask( | |
205 FROM_HERE, base::Bind(&DifferentialUpdatePatch, | |
206 unpack_diff_path_, | |
207 unpack_path_, | |
208 patcher_, | |
209 installer_, | |
210 base::Bind(&ComponentUnpacker::DonePatching, | |
211 GetWeakPtr()))); | |
212 } else { | |
213 task_runner_->PostTask( | |
214 FROM_HERE, base::Bind(&ComponentUnpacker::DonePatching, | |
215 GetWeakPtr(), | |
216 component_updater::kNone, | |
217 0)); | |
218 } | |
219 return true; | |
220 } | |
221 | |
222 void ComponentUnpacker::DonePatching(component_updater::Error error, | |
223 int extended_error) { | |
224 error_ = error; | |
225 extended_error_ = extended_error; | |
226 if (error_ != component_updater::kNone) { | |
227 Finish(); | |
147 return; | 228 return; |
148 } | 229 } |
149 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""), | 230 // Optimization: clean up patch files early, in case we're too low on disk to |
150 &unpack_path_)) { | 231 // install otherwise. |
151 error_ = kUnzipPathError; | 232 if (!unpack_diff_path_.empty()) { |
233 base::DeleteFile(unpack_diff_path_, true); | |
234 unpack_diff_path_.clear(); | |
235 } | |
236 Install(); | |
237 Finish(); | |
238 } | |
239 | |
240 void ComponentUnpacker::Install() { | |
241 // Write the fingerprint to disk. | |
242 if (static_cast<int>(fingerprint_.size()) != | |
243 file_util::WriteFile( | |
244 unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")), | |
245 fingerprint_.c_str(), | |
246 fingerprint_.size())) { | |
247 error_ = component_updater::kFingerprintWriteFailed; | |
152 return; | 248 return; |
153 } | 249 } |
154 if (validator.delta()) { // Package is a diff package. | 250 if (error_ == component_updater::kNone) { |
155 // We want a different temp directory for the delta files; we'll put the | 251 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); |
156 // patch output into unpack_path_. | 252 if (!manifest.get()) { |
157 base::FilePath unpack_diff_path; | 253 error_ = component_updater::kBadManifest; |
158 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL(""), | |
159 &unpack_diff_path)) { | |
160 error_ = kUnzipPathError; | |
161 return; | 254 return; |
162 } | 255 } |
163 if (!zip::Unzip(path, unpack_diff_path)) { | 256 if (!installer_->Install(*manifest, unpack_path_)) { |
164 error_ = kUnzipFailed; | 257 error_ = component_updater::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; | 258 return; |
183 } | 259 } |
184 } | 260 } |
185 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); | 261 } |
186 if (!manifest.get()) { | 262 |
187 error_ = kBadManifest; | 263 void ComponentUnpacker::Finish() { |
188 return; | 264 if (!unpack_diff_path_.empty()) |
189 } | 265 base::DeleteFile(unpack_diff_path_, true); |
190 // Write the fingerprint to disk. | 266 if (!unpack_path_.empty()) |
191 if (static_cast<int>(fingerprint.size()) != | 267 base::DeleteFile(unpack_path_, true); |
192 file_util::WriteFile( | 268 callback_.Run(error_, extended_error_); |
193 unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")), | 269 } |
194 fingerprint.c_str(), | 270 |
195 fingerprint.size())) { | 271 base::WeakPtr<ComponentUnpacker> ComponentUnpacker::GetWeakPtr() { |
196 error_ = kFingerprintWriteFailed; | 272 return ptr_factory_.GetWeakPtr(); |
Sorin Jianu
2013/11/21 19:48:37
why do we call this a ptr_factory_ ?
waffles
2013/11/26 00:46:55
Because it is a base::WeakPtrFactory<ComponentUnpa
| |
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 } | 273 } |
206 | 274 |
207 ComponentUnpacker::~ComponentUnpacker() { | 275 ComponentUnpacker::~ComponentUnpacker() { |
208 if (!unpack_path_.empty()) | |
209 base::DeleteFile(unpack_path_, true); | |
210 } | 276 } |
277 | |
278 } // namespace component_updater | |
OLD | NEW |