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 component_updater { | 30 namespace component_updater { |
28 | 31 |
29 namespace { | 32 namespace { |
30 | 33 |
31 // This class makes sure that the CRX digital signature is valid | 34 // This class makes sure that the CRX digital signature is valid |
32 // and well formed. | 35 // and well formed. |
33 class CRXValidator { | 36 class CRXValidator { |
34 public: | 37 public: |
35 explicit CRXValidator(FILE* crx_file) : valid_(false), delta_(false) { | 38 explicit CRXValidator(FILE* crx_file) : valid_(false), is_delta_(false) { |
36 extensions::CrxFile::Header header; | 39 extensions::CrxFile::Header header; |
37 size_t len = fread(&header, 1, sizeof(header), crx_file); | 40 size_t len = fread(&header, 1, sizeof(header), crx_file); |
38 if (len < sizeof(header)) | 41 if (len < sizeof(header)) |
39 return; | 42 return; |
40 | 43 |
41 extensions::CrxFile::Error error; | 44 extensions::CrxFile::Error error; |
42 scoped_ptr<extensions::CrxFile> crx( | 45 scoped_ptr<extensions::CrxFile> crx( |
43 extensions::CrxFile::Parse(header, &error)); | 46 extensions::CrxFile::Parse(header, &error)); |
44 if (!crx.get()) | 47 if (!crx.get()) |
45 return; | 48 return; |
46 delta_ = extensions::CrxFile::HeaderIsDelta(header); | 49 is_delta_ = extensions::CrxFile::HeaderIsDelta(header); |
47 | 50 |
48 std::vector<uint8> key(header.key_size); | 51 std::vector<uint8> key(header.key_size); |
49 len = fread(&key[0], sizeof(uint8), header.key_size, crx_file); | 52 len = fread(&key[0], sizeof(uint8), header.key_size, crx_file); |
50 if (len < header.key_size) | 53 if (len < header.key_size) |
51 return; | 54 return; |
52 | 55 |
53 std::vector<uint8> signature(header.signature_size); | 56 std::vector<uint8> signature(header.signature_size); |
54 len = fread(&signature[0], sizeof(uint8), header.signature_size, crx_file); | 57 len = fread(&signature[0], sizeof(uint8), header.signature_size, crx_file); |
55 if (len < header.signature_size) | 58 if (len < header.signature_size) |
56 return; | 59 return; |
(...skipping 15 matching lines...) Expand all Loading... | |
72 | 75 |
73 if (!verifier.VerifyFinal()) | 76 if (!verifier.VerifyFinal()) |
74 return; | 77 return; |
75 | 78 |
76 public_key_.swap(key); | 79 public_key_.swap(key); |
77 valid_ = true; | 80 valid_ = true; |
78 } | 81 } |
79 | 82 |
80 bool valid() const { return valid_; } | 83 bool valid() const { return valid_; } |
81 | 84 |
82 bool delta() const { return delta_; } | 85 bool is_delta() const { return is_delta_; } |
83 | 86 |
84 const std::vector<uint8>& public_key() const { return public_key_; } | 87 const std::vector<uint8>& public_key() const { return public_key_; } |
85 | 88 |
86 private: | 89 private: |
87 bool valid_; | 90 bool valid_; |
88 bool delta_; | 91 bool is_delta_; |
89 std::vector<uint8> public_key_; | 92 std::vector<uint8> public_key_; |
90 }; | 93 }; |
91 | 94 |
92 } // namespace. | 95 } // namespace |
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 } | |
93 | 115 |
94 // 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 |
95 // extension unpacker will reject, so that a component cannot be installed | 117 // extension unpacker will reject, so that a component cannot be installed |
96 // as an extension. | 118 // as an extension. |
97 scoped_ptr<base::DictionaryValue> ReadManifest( | 119 scoped_ptr<base::DictionaryValue> ReadManifest( |
98 const base::FilePath& unpack_path) { | 120 const base::FilePath& unpack_path) { |
99 base::FilePath manifest = | 121 base::FilePath manifest = |
100 unpack_path.Append(FILE_PATH_LITERAL("manifest.json")); | 122 unpack_path.Append(FILE_PATH_LITERAL("manifest.json")); |
101 if (!base::PathExists(manifest)) | 123 if (!base::PathExists(manifest)) |
102 return scoped_ptr<base::DictionaryValue>(); | 124 return scoped_ptr<base::DictionaryValue>(); |
103 JSONFileValueSerializer serializer(manifest); | 125 JSONFileValueSerializer serializer(manifest); |
104 std::string error; | 126 std::string error; |
105 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); | 127 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); |
106 if (!root.get()) | 128 if (!root.get()) |
107 return scoped_ptr<base::DictionaryValue>(); | 129 return scoped_ptr<base::DictionaryValue>(); |
108 if (!root->IsType(base::Value::TYPE_DICTIONARY)) | 130 if (!root->IsType(base::Value::TYPE_DICTIONARY)) |
109 return scoped_ptr<base::DictionaryValue>(); | 131 return scoped_ptr<base::DictionaryValue>(); |
110 return scoped_ptr<base::DictionaryValue>( | 132 return scoped_ptr<base::DictionaryValue>( |
111 static_cast<base::DictionaryValue*>(root.release())).Pass(); | 133 static_cast<base::DictionaryValue*>(root.release())).Pass(); |
112 } | 134 } |
113 | 135 |
114 ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash, | 136 // At the end of this execution, either Finish() will be called or |
115 const base::FilePath& path, | 137 // DonePatching() will be called asynchronously. |
116 const std::string& fingerprint, | 138 void ComponentUnpacker::Unpack( |
Sorin Jianu
2014/01/25 02:30:21
We could have a function like:
bool UnpackInterna
waffles
2014/01/28 21:06:31
Done.
| |
117 ComponentPatcher* patcher, | 139 const base::Callback<void(Error, int)>& callback) { |
118 ComponentInstaller* installer) | 140 callback_ = callback; |
119 : error_(kNone), | 141 if (!Verify()) { |
Sorin Jianu
2014/01/25 02:30:21
I knew it Verify would be the first step.
waffles
2014/01/28 21:06:31
Acknowledged.
| |
120 extended_error_(0) { | 142 Finish(); |
121 if (pk_hash.empty() || path.empty()) { | 143 return; |
144 } | |
145 if (!Unzip()) { | |
146 Finish(); | |
147 return; | |
148 } | |
149 if (!BeginPatching()) | |
150 Finish(); | |
151 } | |
152 | |
153 bool ComponentUnpacker::Verify() { | |
154 if (pk_hash_.empty() || path_.empty()) { | |
122 error_ = kInvalidParams; | 155 error_ = kInvalidParams; |
123 return; | 156 return false; |
124 } | 157 } |
125 // First, validate the CRX header and signature. As of today | 158 // First, validate the CRX header and signature. As of today |
126 // this is SHA1 with RSA 1024. | 159 // this is SHA1 with RSA 1024. |
127 ScopedStdioHandle file(base::OpenFile(path, "rb")); | 160 ScopedStdioHandle file(base::OpenFile(path_, "rb")); |
128 if (!file.get()) { | 161 if (!file.get()) { |
129 error_ = kInvalidFile; | 162 error_ = kInvalidFile; |
130 return; | 163 return false; |
131 } | 164 } |
132 CRXValidator validator(file.get()); | 165 CRXValidator validator(file.get()); |
166 file.Close(); | |
133 if (!validator.valid()) { | 167 if (!validator.valid()) { |
134 error_ = kInvalidFile; | 168 error_ = kInvalidFile; |
135 return; | 169 return false; |
136 } | 170 } |
137 file.Close(); | 171 is_delta_ = validator.is_delta(); |
138 | 172 |
139 // File is valid and the digital signature matches. Now make sure | 173 // File is valid and the digital signature matches. Now make sure |
140 // the public key hash matches the expected hash. If they do we fully | 174 // the public key hash matches the expected hash. If they do we fully |
141 // trust this CRX. | 175 // trust this CRX. |
142 uint8 hash[32]; | 176 uint8 hash[32] = {}; |
143 scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); | 177 scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); |
144 sha256->Update(&(validator.public_key()[0]), validator.public_key().size()); | 178 sha256->Update(&(validator.public_key()[0]), validator.public_key().size()); |
145 sha256->Finish(hash, arraysize(hash)); | 179 sha256->Finish(hash, arraysize(hash)); |
146 | 180 |
147 if (!std::equal(pk_hash.begin(), pk_hash.end(), hash)) { | 181 if (!std::equal(pk_hash_.begin(), pk_hash_.end(), hash)) { |
148 error_ = kInvalidId; | 182 error_ = kInvalidId; |
183 return false; | |
184 } | |
185 return true; | |
186 } | |
187 | |
188 bool ComponentUnpacker::Unzip() { | |
189 base::FilePath& destination = is_delta_ ? unpack_diff_path_ : unpack_path_; | |
190 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), | |
191 &destination)) { | |
192 error_ = kUnzipPathError; | |
193 return false; | |
194 } | |
195 if (!zip::Unzip(path_, destination)) | |
196 error_ = kUnzipFailed; | |
Sorin Jianu
2014/01/25 02:30:21
is the code missing a return false here?
waffles
2014/01/28 21:06:31
Yes, thanks. Done.
| |
197 return true; | |
198 } | |
199 | |
200 | |
201 bool ComponentUnpacker::BeginPatching() { | |
202 if (is_delta_) { // Package is a diff package. | |
203 // We want a different temp directory to put the patch output files into. | |
Sorin Jianu
2014/01/25 02:30:21
I am in the definitely pet peeve territory here bu
waffles
2014/01/28 21:06:31
Done. I agree, this one slipped in.
| |
204 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), | |
205 &unpack_path_)) { | |
206 error_ = kUnzipPathError; | |
207 return false; | |
208 } | |
209 task_runner_->PostTask( | |
210 FROM_HERE, base::Bind(&DifferentialUpdatePatch, | |
Sorin Jianu
2014/01/25 02:30:21
Can this function create the unpack_path and retur
waffles
2014/01/28 21:06:31
Let's discuss. I like Unpacker being responsible f
Sorin Jianu
2014/01/31 00:32:16
I'll leave it up to you to structure the code. In
| |
211 unpack_diff_path_, | |
212 unpack_path_, | |
213 patcher_, | |
214 installer_, | |
215 base::Bind(&ComponentUnpacker::DonePatching, | |
216 GetWeakPtr()))); | |
217 } else { | |
218 task_runner_->PostTask( | |
219 FROM_HERE, base::Bind(&ComponentUnpacker::DonePatching, | |
220 GetWeakPtr(), | |
221 kNone, | |
222 0)); | |
223 } | |
224 return true; | |
225 } | |
226 | |
227 void ComponentUnpacker::DonePatching(Error error, int extended_error) { | |
228 error_ = error; | |
229 extended_error_ = extended_error; | |
230 if (error_ != kNone) { | |
231 Finish(); | |
149 return; | 232 return; |
150 } | 233 } |
151 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), | 234 // Optimization: clean up patch files early, in case we're too low on disk to |
152 &unpack_path_)) { | 235 // install otherwise. |
153 error_ = kUnzipPathError; | 236 if (!unpack_diff_path_.empty()) { |
Sorin Jianu
2014/01/25 02:30:21
Arguably, we can have DifferentialUpdatePatch dele
waffles
2014/01/28 21:06:31
We could do that; I like having the ComponentUnpac
| |
237 base::DeleteFile(unpack_diff_path_, true); | |
238 unpack_diff_path_.clear(); | |
239 } | |
240 Install(); | |
241 Finish(); | |
242 } | |
243 | |
244 void ComponentUnpacker::Install() { | |
245 // Write the fingerprint to disk. | |
Sorin Jianu
2014/01/25 02:30:21
Is the semantics of the Install that it copies/ins
waffles
2014/01/28 21:06:31
Personally I consider manifest.fingerprint a piece
Sorin Jianu
2014/01/31 00:32:16
I have no preference, I am just offering an idea.
| |
246 if (static_cast<int>(fingerprint_.size()) != | |
247 file_util::WriteFile( | |
248 unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")), | |
249 fingerprint_.c_str(), | |
250 fingerprint_.size())) { | |
251 error_ = kFingerprintWriteFailed; | |
154 return; | 252 return; |
155 } | 253 } |
156 if (validator.delta()) { // Package is a diff package. | 254 if (error_ == kNone) { |
Sorin Jianu
2014/01/25 02:30:21
in which case error_ != kNone would be true at thi
waffles
2014/01/28 21:06:31
No cases, as far as I can tell. I've removed it.
Sorin Jianu
2014/01/31 00:32:16
Seems a good place to assert that errors were hand
| |
157 // We want a different temp directory for the delta files; we'll put the | 255 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); |
158 // patch output into unpack_path_. | 256 if (!manifest.get()) { |
159 base::FilePath unpack_diff_path; | 257 error_ = kBadManifest; |
160 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), | |
161 &unpack_diff_path)) { | |
162 error_ = kUnzipPathError; | |
163 return; | 258 return; |
164 } | 259 } |
165 if (!zip::Unzip(path, unpack_diff_path)) { | 260 if (!installer_->Install(*manifest, unpack_path_)) { |
166 error_ = kUnzipFailed; | 261 error_ = kInstallerError; |
167 return; | |
168 } | |
169 ComponentUnpacker::Error result = DifferentialUpdatePatch(unpack_diff_path, | |
170 unpack_path_, | |
171 patcher, | |
172 installer, | |
173 &extended_error_); | |
174 base::DeleteFile(unpack_diff_path, true); | |
175 unpack_diff_path.clear(); | |
176 error_ = result; | |
177 if (error_ != kNone) { | |
178 return; | |
179 } | |
180 } else { | |
181 // Package is a normal update/install; unzip it into unpack_path_ directly. | |
182 if (!zip::Unzip(path, unpack_path_)) { | |
183 error_ = kUnzipFailed; | |
184 return; | 262 return; |
185 } | 263 } |
186 } | 264 } |
187 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); | 265 } |
188 if (!manifest.get()) { | 266 |
189 error_ = kBadManifest; | 267 void ComponentUnpacker::Finish() { |
190 return; | 268 if (!unpack_diff_path_.empty()) |
191 } | 269 base::DeleteFile(unpack_diff_path_, true); |
192 // Write the fingerprint to disk. | 270 if (!unpack_path_.empty()) |
193 if (static_cast<int>(fingerprint.size()) != | 271 base::DeleteFile(unpack_path_, true); |
194 file_util::WriteFile( | 272 callback_.Run(error_, extended_error_); |
195 unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")), | 273 } |
196 fingerprint.c_str(), | 274 |
197 fingerprint.size())) { | 275 base::WeakPtr<ComponentUnpacker> ComponentUnpacker::GetWeakPtr() { |
198 error_ = kFingerprintWriteFailed; | 276 return ptr_factory_.GetWeakPtr(); |
199 return; | |
200 } | |
201 if (!installer->Install(*manifest, unpack_path_)) { | |
202 error_ = kInstallerError; | |
203 return; | |
204 } | |
205 // Installation successful. The directory is not our concern now. | |
206 unpack_path_.clear(); | |
207 } | 277 } |
208 | 278 |
209 ComponentUnpacker::~ComponentUnpacker() { | 279 ComponentUnpacker::~ComponentUnpacker() { |
210 if (!unpack_path_.empty()) | |
211 base::DeleteFile(unpack_path_, true); | |
212 } | 280 } |
213 | 281 |
214 } // namespace component_updater | 282 } // namespace component_updater |
215 | |
OLD | NEW |