Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(404)

Side by Side Diff: chrome/browser/extensions/sandboxed_extension_unpacker.cc

Issue 390019: Parse messages.json in ExtensionUnpacker (like we do for manifest).... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 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/extensions/sandboxed_extension_unpacker.h" 5 #include "chrome/browser/extensions/sandboxed_extension_unpacker.h"
6 6
7 #include <set> 7 #include <set>
8 8
9 #include "app/gfx/codec/png_codec.h" 9 #include "app/gfx/codec/png_codec.h"
10 #include "base/crypto/signature_verifier.h" 10 #include "base/crypto/signature_verifier.h"
11 #include "base/file_util.h" 11 #include "base/file_util.h"
12 #include "base/message_loop.h" 12 #include "base/message_loop.h"
13 #include "base/scoped_handle.h" 13 #include "base/scoped_handle.h"
14 #include "base/task.h" 14 #include "base/task.h"
15 #include "chrome/browser/chrome_thread.h" 15 #include "chrome/browser/chrome_thread.h"
16 #include "chrome/browser/extensions/extensions_service.h" 16 #include "chrome/browser/extensions/extensions_service.h"
17 #include "chrome/browser/renderer_host/resource_dispatcher_host.h" 17 #include "chrome/browser/renderer_host/resource_dispatcher_host.h"
18 #include "chrome/common/chrome_switches.h" 18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/common/extensions/extension.h" 19 #include "chrome/common/extensions/extension.h"
20 #include "chrome/common/extensions/extension_constants.h" 20 #include "chrome/common/extensions/extension_constants.h"
21 #include "chrome/common/extensions/extension_unpacker.h" 21 #include "chrome/common/extensions/extension_unpacker.h"
22 #include "chrome/common/json_value_serializer.h" 22 #include "chrome/common/json_value_serializer.h"
23 #include "net/base/base64.h" 23 #include "net/base/base64.h"
24
25 #include "third_party/skia/include/core/SkBitmap.h" 24 #include "third_party/skia/include/core/SkBitmap.h"
26 25
27 const char SandboxedExtensionUnpacker::kExtensionHeaderMagic[] = "Cr24"; 26 const char SandboxedExtensionUnpacker::kExtensionHeaderMagic[] = "Cr24";
28 27
29 SandboxedExtensionUnpacker::SandboxedExtensionUnpacker( 28 SandboxedExtensionUnpacker::SandboxedExtensionUnpacker(
30 const FilePath& crx_path, ResourceDispatcherHost* rdh, 29 const FilePath& crx_path, ResourceDispatcherHost* rdh,
31 SandboxedExtensionUnpackerClient* client) 30 SandboxedExtensionUnpackerClient* client)
32 : crx_path_(crx_path), thread_identifier_(ChromeThread::ID_COUNT), 31 : crx_path_(crx_path), thread_identifier_(ChromeThread::ID_COUNT),
33 rdh_(rdh), client_(client), got_response_(false) { 32 rdh_(rdh), client_(client), got_response_(false) {
34 } 33 }
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
76 ChromeThread::PostTask( 75 ChromeThread::PostTask(
77 ChromeThread::IO, FROM_HERE, 76 ChromeThread::IO, FROM_HERE,
78 NewRunnableMethod( 77 NewRunnableMethod(
79 this, 78 this,
80 &SandboxedExtensionUnpacker::StartProcessOnIOThread, 79 &SandboxedExtensionUnpacker::StartProcessOnIOThread,
81 temp_crx_path)); 80 temp_crx_path));
82 } else { 81 } else {
83 // Otherwise, unpack the extension in this process. 82 // Otherwise, unpack the extension in this process.
84 ExtensionUnpacker unpacker(temp_crx_path); 83 ExtensionUnpacker unpacker(temp_crx_path);
85 if (unpacker.Run() && unpacker.DumpImagesToFile()) 84 if (unpacker.Run() && unpacker.DumpImagesToFile())
86 OnUnpackExtensionSucceeded(*unpacker.parsed_manifest()); 85 OnUnpackExtensionSucceeded(*unpacker.parsed_manifest(),
86 *unpacker.parsed_catalogs());
87 else 87 else
88 OnUnpackExtensionFailed(unpacker.error_message()); 88 OnUnpackExtensionFailed(unpacker.error_message());
89 } 89 }
90 } 90 }
91 91
92 void SandboxedExtensionUnpacker::StartProcessOnIOThread( 92 void SandboxedExtensionUnpacker::StartProcessOnIOThread(
93 const FilePath& temp_crx_path) { 93 const FilePath& temp_crx_path) {
94 UtilityProcessHost* host = new UtilityProcessHost( 94 UtilityProcessHost* host = new UtilityProcessHost(
95 rdh_, this, thread_identifier_); 95 rdh_, this, thread_identifier_);
96 host->StartExtensionUnpacker(temp_crx_path); 96 host->StartExtensionUnpacker(temp_crx_path);
97 } 97 }
98 98
99 void SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded( 99 void SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded(
100 const DictionaryValue& manifest) { 100 const DictionaryValue& manifest,
101 DCHECK(ChromeThread::CurrentlyOn(thread_identifier_)); 101 const DictionaryValue& catalogs) {
102 // Skip check for unittests.
103 if (thread_identifier_ != ChromeThread::ID_COUNT)
104 DCHECK(ChromeThread::CurrentlyOn(thread_identifier_));
102 got_response_ = true; 105 got_response_ = true;
103 106
104 ExtensionUnpacker::DecodedImages images; 107 scoped_ptr<DictionaryValue> final_manifest(RewriteManifestFile(manifest));
105 if (!ExtensionUnpacker::ReadImagesFromFile(temp_dir_.path(), &images)) { 108 if (!final_manifest.get())
106 ReportFailure("Couldn't read image data from disk.");
107 return; 109 return;
108 }
109
110 // Add the public key extracted earlier to the parsed manifest and overwrite
111 // the original manifest. We do this to ensure the manifest doesn't contain an
112 // exploitable bug that could be used to compromise the browser.
113 scoped_ptr<DictionaryValue> final_manifest(
114 static_cast<DictionaryValue*>(manifest.DeepCopy()));
115 final_manifest->SetString(extension_manifest_keys::kPublicKey, public_key_);
116
117 std::string manifest_json;
118 JSONStringValueSerializer serializer(&manifest_json);
119 serializer.set_pretty_print(true);
120 if (!serializer.Serialize(*final_manifest)) {
121 ReportFailure("Error serializing manifest.json.");
122 return;
123 }
124
125 FilePath manifest_path =
126 extension_root_.AppendASCII(Extension::kManifestFilename);
127 if (!file_util::WriteFile(manifest_path,
128 manifest_json.data(), manifest_json.size())) {
129 ReportFailure("Error saving manifest.json.");
130 return;
131 }
132 110
133 // Create an extension object that refers to the temporary location the 111 // Create an extension object that refers to the temporary location the
134 // extension was unpacked to. We use this until the extension is finally 112 // extension was unpacked to. We use this until the extension is finally
135 // installed. For example, the install UI shows images from inside the 113 // installed. For example, the install UI shows images from inside the
136 // extension. 114 // extension.
137 extension_.reset(new Extension(extension_root_)); 115 extension_.reset(new Extension(extension_root_));
138 116
139 std::string manifest_error; 117 std::string manifest_error;
140 if (!extension_->InitFromValue(*final_manifest, true, // require id 118 if (!extension_->InitFromValue(*final_manifest, true, // require id
141 &manifest_error)) { 119 &manifest_error)) {
142 ReportFailure(std::string("Manifest is invalid: ") + 120 ReportFailure(std::string("Manifest is invalid: ") +
143 manifest_error); 121 manifest_error);
144 return; 122 return;
145 } 123 }
146 124
147 // Delete any images that may be used by the browser. We're going to write 125 if (!RewriteImageFiles())
148 // out our own versions of the parsed images, and we want to make sure the
149 // originals are gone for good.
150 std::set<FilePath> image_paths = extension_->GetBrowserImages();
151 if (image_paths.size() != images.size()) {
152 ReportFailure("Decoded images don't match what's in the manifest.");
153 return; 126 return;
154 }
155 127
156 for (std::set<FilePath>::iterator it = image_paths.begin(); 128 if (!RewriteCatalogFiles(catalogs))
157 it != image_paths.end(); ++it) { 129 return;
158 FilePath path = *it;
159 if (path.IsAbsolute() || path.ReferencesParent()) {
160 ReportFailure("Invalid path for browser image.");
161 return;
162 }
163 if (!file_util::Delete(extension_root_.Append(path), false)) {
164 ReportFailure("Error removing old image file.");
165 return;
166 }
167 }
168
169 // Write our parsed images back to disk as well.
170 for (size_t i = 0; i < images.size(); ++i) {
171 const SkBitmap& image = images[i].a;
172 FilePath path_suffix = images[i].b;
173 if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) {
174 ReportFailure("Invalid path for bitmap image.");
175 return;
176 }
177 FilePath path = extension_root_.Append(path_suffix);
178
179 std::vector<unsigned char> image_data;
180 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
181 // though they may originally be .jpg, etc. Figure something out.
182 // http://code.google.com/p/chromium/issues/detail?id=12459
183 if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) {
184 ReportFailure("Error re-encoding theme image.");
185 return;
186 }
187
188 // Note: we're overwriting existing files that the utility process wrote,
189 // so we can be sure the directory exists.
190 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
191 if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) {
192 ReportFailure("Error saving theme image.");
193 return;
194 }
195 }
196 130
197 ReportSuccess(); 131 ReportSuccess();
198 } 132 }
199 133
200 void SandboxedExtensionUnpacker::OnUnpackExtensionFailed( 134 void SandboxedExtensionUnpacker::OnUnpackExtensionFailed(
201 const std::string& error) { 135 const std::string& error) {
202 DCHECK(ChromeThread::CurrentlyOn(thread_identifier_)); 136 DCHECK(ChromeThread::CurrentlyOn(thread_identifier_));
203 got_response_ = true; 137 got_response_ = true;
204 ReportFailure(error); 138 ReportFailure(error);
205 } 139 }
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
303 237
304 void SandboxedExtensionUnpacker::ReportFailure(const std::string& error) { 238 void SandboxedExtensionUnpacker::ReportFailure(const std::string& error) {
305 client_->OnUnpackFailure(error); 239 client_->OnUnpackFailure(error);
306 } 240 }
307 241
308 void SandboxedExtensionUnpacker::ReportSuccess() { 242 void SandboxedExtensionUnpacker::ReportSuccess() {
309 // Client takes ownership of temporary directory and extension. 243 // Client takes ownership of temporary directory and extension.
310 client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_, 244 client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_,
311 extension_.release()); 245 extension_.release());
312 } 246 }
247
248 DictionaryValue* SandboxedExtensionUnpacker::RewriteManifestFile(
249 const DictionaryValue& manifest) {
250 // Add the public key extracted earlier to the parsed manifest and overwrite
251 // the original manifest. We do this to ensure the manifest doesn't contain an
252 // exploitable bug that could be used to compromise the browser.
253 scoped_ptr<DictionaryValue> final_manifest(
254 static_cast<DictionaryValue*>(manifest.DeepCopy()));
255 final_manifest->SetString(extension_manifest_keys::kPublicKey, public_key_);
256
257 std::string manifest_json;
258 JSONStringValueSerializer serializer(&manifest_json);
259 serializer.set_pretty_print(true);
260 if (!serializer.Serialize(*final_manifest)) {
261 ReportFailure("Error serializing manifest.json.");
262 return NULL;
263 }
264
265 FilePath manifest_path =
266 extension_root_.AppendASCII(Extension::kManifestFilename);
267 if (!file_util::WriteFile(manifest_path,
268 manifest_json.data(), manifest_json.size())) {
269 ReportFailure("Error saving manifest.json.");
270 return NULL;
271 }
272
273 return final_manifest.release();
274 }
275
276 bool SandboxedExtensionUnpacker::RewriteImageFiles() {
277 ExtensionUnpacker::DecodedImages images;
278 if (!ExtensionUnpacker::ReadImagesFromFile(temp_dir_.path(), &images)) {
279 ReportFailure("Couldn't read image data from disk.");
280 return false;
281 }
282
283 // Delete any images that may be used by the browser. We're going to write
284 // out our own versions of the parsed images, and we want to make sure the
285 // originals are gone for good.
286 std::set<FilePath> image_paths = extension_->GetBrowserImages();
287 if (image_paths.size() != images.size()) {
288 ReportFailure("Decoded images don't match what's in the manifest.");
289 return false;
290 }
291
292 for (std::set<FilePath>::iterator it = image_paths.begin();
293 it != image_paths.end(); ++it) {
294 FilePath path = *it;
295 if (path.IsAbsolute() || path.ReferencesParent()) {
296 ReportFailure("Invalid path for browser image.");
297 return false;
298 }
299 if (!file_util::Delete(extension_root_.Append(path), false)) {
300 ReportFailure("Error removing old image file.");
301 return false;
302 }
303 }
304
305 // Write our parsed images back to disk as well.
306 for (size_t i = 0; i < images.size(); ++i) {
307 const SkBitmap& image = images[i].a;
308 FilePath path_suffix = images[i].b;
309 if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) {
310 ReportFailure("Invalid path for bitmap image.");
311 return false;
312 }
313 FilePath path = extension_root_.Append(path_suffix);
314
315 std::vector<unsigned char> image_data;
316 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
317 // though they may originally be .jpg, etc. Figure something out.
318 // http://code.google.com/p/chromium/issues/detail?id=12459
319 if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) {
320 ReportFailure("Error re-encoding theme image.");
321 return false;
322 }
323
324 // Note: we're overwriting existing files that the utility process wrote,
325 // so we can be sure the directory exists.
326 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
327 if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) {
328 ReportFailure("Error saving theme image.");
329 return false;
330 }
331 }
332
333 return true;
334 }
335
336 bool SandboxedExtensionUnpacker::RewriteCatalogFiles(
337 const DictionaryValue& catalogs) {
338 // Write our parsed catalogs back to disk.
339 DictionaryValue::key_iterator key_it = catalogs.begin_keys();
340 for (; key_it != catalogs.end_keys(); ++key_it) {
341 DictionaryValue* catalog;
342 if (!catalogs.GetDictionary(*key_it, &catalog)) {
343 ReportFailure("Invalid catalog data.");
344 return false;
345 }
346
347 FilePath relative_path = FilePath::FromWStringHack(*key_it);
348 relative_path = relative_path.AppendASCII(Extension::kMessagesFilename);
349 if (relative_path.IsAbsolute() || relative_path.ReferencesParent()) {
350 ReportFailure("Invalid path for catalog.");
351 return false;
352 }
353 FilePath path = extension_root_.Append(relative_path);
354
355 std::string catalog_json;
356 JSONStringValueSerializer serializer(&catalog_json);
357 serializer.set_pretty_print(true);
358 if (!serializer.Serialize(*catalog)) {
359 ReportFailure("Error serializing catalog.");
360 return false;
361 }
362
363 // Note: we're overwriting existing files that the utility process read,
364 // so we can be sure the directory exists.
365 if (!file_util::WriteFile(path,
366 catalog_json.c_str(),
367 catalog_json.size())) {
368 ReportFailure("Error saving catalog.");
369 return false;
370 }
371 }
372
373 return true;
374 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698