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

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

Issue 864093002: Move sandboxed_unpacker.{h,cc} from chrome/ to extensions/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix compile errors Created 5 years, 11 months 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/sandboxed_unpacker.h"
6
7 #include <set>
8
9 #include "base/base64.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/files/file_util_proxy.h"
14 #include "base/files/scoped_file.h"
15 #include "base/json/json_string_value_serializer.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/metrics/histogram.h"
18 #include "base/numerics/safe_conversions.h"
19 #include "base/path_service.h"
20 #include "base/sequenced_task_runner.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/threading/sequenced_worker_pool.h"
23 #include "chrome/browser/extensions/extension_service.h"
24 #include "chrome/common/chrome_paths.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/common/chrome_utility_messages.h"
27 #include "chrome/common/extensions/chrome_utility_extensions_messages.h"
28 #include "chrome/grit/generated_resources.h"
29 #include "components/crx_file/constants.h"
30 #include "components/crx_file/crx_file.h"
31 #include "components/crx_file/id_util.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/utility_process_host.h"
34 #include "content/public/common/common_param_traits.h"
35 #include "crypto/signature_verifier.h"
36 #include "extensions/common/constants.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/extension_l10n_util.h"
39 #include "extensions/common/extension_utility_messages.h"
40 #include "extensions/common/extensions_client.h"
41 #include "extensions/common/file_util.h"
42 #include "extensions/common/manifest_constants.h"
43 #include "extensions/common/manifest_handlers/icons_handler.h"
44 #include "third_party/skia/include/core/SkBitmap.h"
45 #include "ui/base/l10n/l10n_util.h"
46 #include "ui/gfx/codec/png_codec.h"
47
48 using base::ASCIIToUTF16;
49 using content::BrowserThread;
50 using content::UtilityProcessHost;
51 using crx_file::CrxFile;
52
53 // The following macro makes histograms that record the length of paths
54 // in this file much easier to read.
55 // Windows has a short max path length. If the path length to a
56 // file being unpacked from a CRX exceeds the max length, we might
57 // fail to install. To see if this is happening, see how long the
58 // path to the temp unpack directory is. See crbug.com/69693 .
59 #define PATH_LENGTH_HISTOGRAM(name, path) \
60 UMA_HISTOGRAM_CUSTOM_COUNTS(name, path.value().length(), 0, 500, 100)
61
62 // Record a rate (kB per second) at which extensions are unpacked.
63 // Range from 1kB/s to 100mB/s.
64 #define UNPACK_RATE_HISTOGRAM(name, rate) \
65 UMA_HISTOGRAM_CUSTOM_COUNTS(name, rate, 1, 100000, 100);
66
67 namespace extensions {
68 namespace {
69
70 void RecordSuccessfulUnpackTimeHistograms(
71 const base::FilePath& crx_path, const base::TimeDelta unpack_time) {
72
73 const int64 kBytesPerKb = 1024;
74 const int64 kBytesPerMb = 1024 * 1024;
75
76 UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackSuccessTime", unpack_time);
77
78 // To get a sense of how CRX size impacts unpack time, record unpack
79 // time for several increments of CRX size.
80 int64 crx_file_size;
81 if (!base::GetFileSize(crx_path, &crx_file_size)) {
82 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccessCantGetCrxSize", 1);
83 return;
84 }
85
86 // Cast is safe as long as the number of bytes in the CRX is less than
87 // 2^31 * 2^10.
88 int crx_file_size_kb = static_cast<int>(crx_file_size / kBytesPerKb);
89 UMA_HISTOGRAM_COUNTS(
90 "Extensions.SandboxUnpackSuccessCrxSize", crx_file_size_kb);
91
92 // We have time in seconds and file size in bytes. We want the rate bytes are
93 // unpacked in kB/s.
94 double file_size_kb =
95 static_cast<double>(crx_file_size) / static_cast<double>(kBytesPerKb);
96 int unpack_rate_kb_per_s =
97 static_cast<int>(file_size_kb / unpack_time.InSecondsF());
98 UNPACK_RATE_HISTOGRAM("Extensions.SandboxUnpackRate", unpack_rate_kb_per_s);
99
100 if (crx_file_size < 50.0 * kBytesPerKb) {
101 UNPACK_RATE_HISTOGRAM(
102 "Extensions.SandboxUnpackRateUnder50kB", unpack_rate_kb_per_s);
103
104 } else if (crx_file_size < 1 * kBytesPerMb) {
105 UNPACK_RATE_HISTOGRAM(
106 "Extensions.SandboxUnpackRate50kBTo1mB", unpack_rate_kb_per_s);
107
108 } else if (crx_file_size < 2 * kBytesPerMb) {
109 UNPACK_RATE_HISTOGRAM(
110 "Extensions.SandboxUnpackRate1To2mB", unpack_rate_kb_per_s);
111
112 } else if (crx_file_size < 5 * kBytesPerMb) {
113 UNPACK_RATE_HISTOGRAM(
114 "Extensions.SandboxUnpackRate2To5mB", unpack_rate_kb_per_s);
115
116 } else if (crx_file_size < 10 * kBytesPerMb) {
117 UNPACK_RATE_HISTOGRAM(
118 "Extensions.SandboxUnpackRate5To10mB", unpack_rate_kb_per_s);
119
120 } else {
121 UNPACK_RATE_HISTOGRAM(
122 "Extensions.SandboxUnpackRateOver10mB", unpack_rate_kb_per_s);
123 }
124 }
125
126 // Work horse for FindWritableTempLocation. Creates a temp file in the folder
127 // and uses NormalizeFilePath to check if the path is junction free.
128 bool VerifyJunctionFreeLocation(base::FilePath* temp_dir) {
129 if (temp_dir->empty())
130 return false;
131
132 base::FilePath temp_file;
133 if (!base::CreateTemporaryFileInDir(*temp_dir, &temp_file)) {
134 LOG(ERROR) << temp_dir->value() << " is not writable";
135 return false;
136 }
137 // NormalizeFilePath requires a non-empty file, so write some data.
138 // If you change the exit points of this function please make sure all
139 // exit points delete this temp file!
140 if (base::WriteFile(temp_file, ".", 1) != 1)
141 return false;
142
143 base::FilePath normalized_temp_file;
144 bool normalized = base::NormalizeFilePath(temp_file, &normalized_temp_file);
145 if (!normalized) {
146 // If |temp_file| contains a link, the sandbox will block al file system
147 // operations, and the install will fail.
148 LOG(ERROR) << temp_dir->value() << " seem to be on remote drive.";
149 } else {
150 *temp_dir = normalized_temp_file.DirName();
151 }
152 // Clean up the temp file.
153 base::DeleteFile(temp_file, false);
154
155 return normalized;
156 }
157
158 // This function tries to find a location for unpacking the extension archive
159 // that is writable and does not lie on a shared drive so that the sandboxed
160 // unpacking process can write there. If no such location exists we can not
161 // proceed and should fail.
162 // The result will be written to |temp_dir|. The function will write to this
163 // parameter even if it returns false.
164 bool FindWritableTempLocation(const base::FilePath& extensions_dir,
165 base::FilePath* temp_dir) {
166 // On ChromeOS, we will only attempt to unpack extension in cryptohome (profile)
167 // directory to provide additional security/privacy and speed up the rest of
168 // the extension install process.
169 #if !defined(OS_CHROMEOS)
170 PathService::Get(base::DIR_TEMP, temp_dir);
171 if (VerifyJunctionFreeLocation(temp_dir))
172 return true;
173 #endif
174
175 *temp_dir = file_util::GetInstallTempDir(extensions_dir);
176 if (VerifyJunctionFreeLocation(temp_dir))
177 return true;
178 // Neither paths is link free chances are good installation will fail.
179 LOG(ERROR) << "Both the %TEMP% folder and the profile seem to be on "
180 << "remote drives or read-only. Installation can not complete!";
181 return false;
182 }
183
184 // Read the decoded images back from the file we saved them to.
185 // |extension_path| is the path to the extension we unpacked that wrote the
186 // data. Returns true on success.
187 bool ReadImagesFromFile(const base::FilePath& extension_path,
188 DecodedImages* images) {
189 base::FilePath path =
190 extension_path.AppendASCII(kDecodedImagesFilename);
191 std::string file_str;
192 if (!base::ReadFileToString(path, &file_str))
193 return false;
194
195 IPC::Message pickle(file_str.data(), file_str.size());
196 PickleIterator iter(pickle);
197 return IPC::ReadParam(&pickle, &iter, images);
198 }
199
200 // Read the decoded message catalogs back from the file we saved them to.
201 // |extension_path| is the path to the extension we unpacked that wrote the
202 // data. Returns true on success.
203 bool ReadMessageCatalogsFromFile(const base::FilePath& extension_path,
204 base::DictionaryValue* catalogs) {
205 base::FilePath path = extension_path.AppendASCII(
206 kDecodedMessageCatalogsFilename);
207 std::string file_str;
208 if (!base::ReadFileToString(path, &file_str))
209 return false;
210
211 IPC::Message pickle(file_str.data(), file_str.size());
212 PickleIterator iter(pickle);
213 return IPC::ReadParam(&pickle, &iter, catalogs);
214 }
215
216 } // namespace
217
218 SandboxedUnpacker::SandboxedUnpacker(
219 const base::FilePath& crx_path,
220 Manifest::Location location,
221 int creation_flags,
222 const base::FilePath& extensions_dir,
223 const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner,
224 SandboxedUnpackerClient* client)
225 : crx_path_(crx_path),
226 client_(client),
227 extensions_dir_(extensions_dir),
228 got_response_(false),
229 location_(location),
230 creation_flags_(creation_flags),
231 unpacker_io_task_runner_(unpacker_io_task_runner) {
232 }
233
234 bool SandboxedUnpacker::CreateTempDirectory() {
235 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
236
237 base::FilePath temp_dir;
238 if (!FindWritableTempLocation(extensions_dir_, &temp_dir)) {
239 ReportFailure(
240 COULD_NOT_GET_TEMP_DIRECTORY,
241 l10n_util::GetStringFUTF16(
242 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
243 ASCIIToUTF16("COULD_NOT_GET_TEMP_DIRECTORY")));
244 return false;
245 }
246
247 if (!temp_dir_.CreateUniqueTempDirUnderPath(temp_dir)) {
248 ReportFailure(
249 COULD_NOT_CREATE_TEMP_DIRECTORY,
250 l10n_util::GetStringFUTF16(
251 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
252 ASCIIToUTF16("COULD_NOT_CREATE_TEMP_DIRECTORY")));
253 return false;
254 }
255
256 return true;
257 }
258
259 void SandboxedUnpacker::Start() {
260 // We assume that we are started on the thread that the client wants us to do
261 // file IO on.
262 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
263
264 unpack_start_time_ = base::TimeTicks::Now();
265
266 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackInitialCrxPathLength",
267 crx_path_);
268 if (!CreateTempDirectory())
269 return; // ReportFailure() already called.
270
271 // Initialize the path that will eventually contain the unpacked extension.
272 extension_root_ = temp_dir_.path().AppendASCII(kTempExtensionName);
273 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackUnpackedCrxPathLength",
274 extension_root_);
275
276 // Extract the public key and validate the package.
277 if (!ValidateSignature())
278 return; // ValidateSignature() already reported the error.
279
280 // Copy the crx file into our working directory.
281 base::FilePath temp_crx_path = temp_dir_.path().Append(crx_path_.BaseName());
282 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackTempCrxPathLength",
283 temp_crx_path);
284
285 if (!base::CopyFile(crx_path_, temp_crx_path)) {
286 // Failed to copy extension file to temporary directory.
287 ReportFailure(
288 FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY,
289 l10n_util::GetStringFUTF16(
290 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
291 ASCIIToUTF16("FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY")));
292 return;
293 }
294
295 // The utility process will have access to the directory passed to
296 // SandboxedUnpacker. That directory should not contain a symlink or NTFS
297 // reparse point. When the path is used, following the link/reparse point
298 // will cause file system access outside the sandbox path, and the sandbox
299 // will deny the operation.
300 base::FilePath link_free_crx_path;
301 if (!base::NormalizeFilePath(temp_crx_path, &link_free_crx_path)) {
302 LOG(ERROR) << "Could not get the normalized path of "
303 << temp_crx_path.value();
304 ReportFailure(
305 COULD_NOT_GET_SANDBOX_FRIENDLY_PATH,
306 l10n_util::GetStringUTF16(IDS_EXTENSION_UNPACK_FAILED));
307 return;
308 }
309 PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackLinkFreeCrxPathLength",
310 link_free_crx_path);
311
312 BrowserThread::PostTask(
313 BrowserThread::IO, FROM_HERE,
314 base::Bind(
315 &SandboxedUnpacker::StartProcessOnIOThread,
316 this,
317 link_free_crx_path));
318 }
319
320 SandboxedUnpacker::~SandboxedUnpacker() {
321 }
322
323 bool SandboxedUnpacker::OnMessageReceived(const IPC::Message& message) {
324 bool handled = true;
325 IPC_BEGIN_MESSAGE_MAP(SandboxedUnpacker, message)
326 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_UnpackExtension_Succeeded,
327 OnUnpackExtensionSucceeded)
328 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_UnpackExtension_Failed,
329 OnUnpackExtensionFailed)
330 IPC_MESSAGE_UNHANDLED(handled = false)
331 IPC_END_MESSAGE_MAP()
332 return handled;
333 }
334
335 void SandboxedUnpacker::OnProcessCrashed(int exit_code) {
336 // Don't report crashes if they happen after we got a response.
337 if (got_response_)
338 return;
339
340 // Utility process crashed while trying to install.
341 ReportFailure(
342 UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL,
343 l10n_util::GetStringFUTF16(
344 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
345 ASCIIToUTF16("UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL")) +
346 ASCIIToUTF16(". ") +
347 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_PROCESS_CRASHED));
348 }
349
350 void SandboxedUnpacker::StartProcessOnIOThread(
351 const base::FilePath& temp_crx_path) {
352 UtilityProcessHost* host =
353 UtilityProcessHost::Create(this, unpacker_io_task_runner_.get());
354 // Grant the subprocess access to the entire subdir the extension file is
355 // in, so that it can unpack to that dir.
356 host->SetExposedDir(temp_crx_path.DirName());
357 host->Send(
358 new ChromeUtilityMsg_UnpackExtension(
359 temp_crx_path, extension_id_, location_, creation_flags_));
360 }
361
362 void SandboxedUnpacker::OnUnpackExtensionSucceeded(
363 const base::DictionaryValue& manifest) {
364 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
365 got_response_ = true;
366
367 scoped_ptr<base::DictionaryValue> final_manifest(
368 RewriteManifestFile(manifest));
369 if (!final_manifest)
370 return;
371
372 // Create an extension object that refers to the temporary location the
373 // extension was unpacked to. We use this until the extension is finally
374 // installed. For example, the install UI shows images from inside the
375 // extension.
376
377 // Localize manifest now, so confirm UI gets correct extension name.
378
379 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
380 // with base::string16
381 std::string utf8_error;
382 if (!extension_l10n_util::LocalizeExtension(extension_root_,
383 final_manifest.get(),
384 &utf8_error)) {
385 ReportFailure(
386 COULD_NOT_LOCALIZE_EXTENSION,
387 l10n_util::GetStringFUTF16(
388 IDS_EXTENSION_PACKAGE_ERROR_MESSAGE,
389 base::UTF8ToUTF16(utf8_error)));
390 return;
391 }
392
393 extension_ = Extension::Create(
394 extension_root_,
395 location_,
396 *final_manifest,
397 Extension::REQUIRE_KEY | creation_flags_,
398 &utf8_error);
399
400 if (!extension_.get()) {
401 ReportFailure(INVALID_MANIFEST,
402 ASCIIToUTF16("Manifest is invalid: " + utf8_error));
403 return;
404 }
405
406 SkBitmap install_icon;
407 if (!RewriteImageFiles(&install_icon))
408 return;
409
410 if (!RewriteCatalogFiles())
411 return;
412
413 ReportSuccess(manifest, install_icon);
414 }
415
416 void SandboxedUnpacker::OnUnpackExtensionFailed(const base::string16& error) {
417 CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
418 got_response_ = true;
419 ReportFailure(
420 UNPACKER_CLIENT_FAILED,
421 l10n_util::GetStringFUTF16(
422 IDS_EXTENSION_PACKAGE_ERROR_MESSAGE,
423 error));
424 }
425
426 bool SandboxedUnpacker::ValidateSignature() {
427 base::ScopedFILE file(base::OpenFile(crx_path_, "rb"));
428
429 if (!file.get()) {
430 // Could not open crx file for reading.
431 #if defined (OS_WIN)
432 // On windows, get the error code.
433 uint32 error_code = ::GetLastError();
434 // TODO(skerner): Use this histogram to understand why so many
435 // windows users hit this error. crbug.com/69693
436
437 // Windows errors are unit32s, but all of likely errors are in
438 // [1, 1000]. See winerror.h for the meaning of specific values.
439 // Clip errors outside the expected range to a single extra value.
440 // If there are errors in that extra bucket, we will know to expand
441 // the range.
442 const uint32 kMaxErrorToSend = 1001;
443 error_code = std::min(error_code, kMaxErrorToSend);
444 UMA_HISTOGRAM_ENUMERATION("Extensions.ErrorCodeFromCrxOpen",
445 error_code, kMaxErrorToSend);
446 #endif
447
448 ReportFailure(
449 CRX_FILE_NOT_READABLE,
450 l10n_util::GetStringFUTF16(
451 IDS_EXTENSION_PACKAGE_ERROR_CODE,
452 ASCIIToUTF16("CRX_FILE_NOT_READABLE")));
453 return false;
454 }
455
456 // Read and verify the header.
457 // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it
458 // appears that we don't have any endian/alignment aware serialization
459 // code in the code base. So for now, this assumes that we're running
460 // on a little endian machine with 4 byte alignment.
461 CrxFile::Header header;
462 size_t len = fread(&header, 1, sizeof(header), file.get());
463 if (len < sizeof(header)) {
464 // Invalid crx header
465 ReportFailure(
466 CRX_HEADER_INVALID,
467 l10n_util::GetStringFUTF16(
468 IDS_EXTENSION_PACKAGE_ERROR_CODE,
469 ASCIIToUTF16("CRX_HEADER_INVALID")));
470 return false;
471 }
472
473 CrxFile::Error error;
474 scoped_ptr<CrxFile> crx(CrxFile::Parse(header, &error));
475 if (!crx) {
476 switch (error) {
477 case CrxFile::kWrongMagic:
478 ReportFailure(
479 CRX_MAGIC_NUMBER_INVALID,
480 l10n_util::GetStringFUTF16(
481 IDS_EXTENSION_PACKAGE_ERROR_CODE,
482 ASCIIToUTF16("CRX_MAGIC_NUMBER_INVALID")));
483 break;
484 case CrxFile::kInvalidVersion:
485 // Bad version numer
486 ReportFailure(
487 CRX_VERSION_NUMBER_INVALID,
488 l10n_util::GetStringFUTF16(
489 IDS_EXTENSION_PACKAGE_ERROR_CODE,
490 ASCIIToUTF16("CRX_VERSION_NUMBER_INVALID")));
491 break;
492 case CrxFile::kInvalidKeyTooLarge:
493 case CrxFile::kInvalidSignatureTooLarge:
494 // Excessively large key or signature
495 ReportFailure(
496 CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE,
497 l10n_util::GetStringFUTF16(
498 IDS_EXTENSION_PACKAGE_ERROR_CODE,
499 ASCIIToUTF16("CRX_EXCESSIVELY_LARGE_KEY_OR_SIGNATURE")));
500 break;
501 case CrxFile::kInvalidKeyTooSmall:
502 // Key length is zero
503 ReportFailure(
504 CRX_ZERO_KEY_LENGTH,
505 l10n_util::GetStringFUTF16(
506 IDS_EXTENSION_PACKAGE_ERROR_CODE,
507 ASCIIToUTF16("CRX_ZERO_KEY_LENGTH")));
508 break;
509 case CrxFile::kInvalidSignatureTooSmall:
510 // Signature length is zero
511 ReportFailure(
512 CRX_ZERO_SIGNATURE_LENGTH,
513 l10n_util::GetStringFUTF16(
514 IDS_EXTENSION_PACKAGE_ERROR_CODE,
515 ASCIIToUTF16("CRX_ZERO_SIGNATURE_LENGTH")));
516 break;
517 }
518 return false;
519 }
520
521 std::vector<uint8> key;
522 key.resize(header.key_size);
523 len = fread(&key.front(), sizeof(uint8), header.key_size, file.get());
524 if (len < header.key_size) {
525 // Invalid public key
526 ReportFailure(
527 CRX_PUBLIC_KEY_INVALID,
528 l10n_util::GetStringFUTF16(
529 IDS_EXTENSION_PACKAGE_ERROR_CODE,
530 ASCIIToUTF16("CRX_PUBLIC_KEY_INVALID")));
531 return false;
532 }
533
534 std::vector<uint8> signature;
535 signature.resize(header.signature_size);
536 len = fread(&signature.front(), sizeof(uint8), header.signature_size,
537 file.get());
538 if (len < header.signature_size) {
539 // Invalid signature
540 ReportFailure(
541 CRX_SIGNATURE_INVALID,
542 l10n_util::GetStringFUTF16(
543 IDS_EXTENSION_PACKAGE_ERROR_CODE,
544 ASCIIToUTF16("CRX_SIGNATURE_INVALID")));
545 return false;
546 }
547
548 crypto::SignatureVerifier verifier;
549 if (!verifier.VerifyInit(crx_file::kSignatureAlgorithm,
550 sizeof(crx_file::kSignatureAlgorithm),
551 &signature.front(),
552 signature.size(),
553 &key.front(),
554 key.size())) {
555 // Signature verification initialization failed. This is most likely
556 // caused by a public key in the wrong format (should encode algorithm).
557 ReportFailure(
558 CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED,
559 l10n_util::GetStringFUTF16(
560 IDS_EXTENSION_PACKAGE_ERROR_CODE,
561 ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_INITIALIZATION_FAILED")));
562 return false;
563 }
564
565 unsigned char buf[1 << 12];
566 while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0)
567 verifier.VerifyUpdate(buf, len);
568
569 if (!verifier.VerifyFinal()) {
570 // Signature verification failed
571 ReportFailure(
572 CRX_SIGNATURE_VERIFICATION_FAILED,
573 l10n_util::GetStringFUTF16(
574 IDS_EXTENSION_PACKAGE_ERROR_CODE,
575 ASCIIToUTF16("CRX_SIGNATURE_VERIFICATION_FAILED")));
576 return false;
577 }
578
579 std::string public_key =
580 std::string(reinterpret_cast<char*>(&key.front()), key.size());
581 base::Base64Encode(public_key, &public_key_);
582
583 extension_id_ = crx_file::id_util::GenerateId(public_key);
584
585 return true;
586 }
587
588 void SandboxedUnpacker::ReportFailure(FailureReason reason,
589 const base::string16& error) {
590 UMA_HISTOGRAM_ENUMERATION("Extensions.SandboxUnpackFailureReason",
591 reason, NUM_FAILURE_REASONS);
592 UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackFailureTime",
593 base::TimeTicks::Now() - unpack_start_time_);
594 Cleanup();
595 client_->OnUnpackFailure(error);
596 }
597
598 void SandboxedUnpacker::ReportSuccess(
599 const base::DictionaryValue& original_manifest,
600 const SkBitmap& install_icon) {
601 UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1);
602
603 RecordSuccessfulUnpackTimeHistograms(
604 crx_path_, base::TimeTicks::Now() - unpack_start_time_);
605
606 // Client takes ownership of temporary directory and extension.
607 client_->OnUnpackSuccess(
608 temp_dir_.Take(), extension_root_, &original_manifest, extension_.get(),
609 install_icon);
610 extension_ = NULL;
611 }
612
613 base::DictionaryValue* SandboxedUnpacker::RewriteManifestFile(
614 const base::DictionaryValue& manifest) {
615 // Add the public key extracted earlier to the parsed manifest and overwrite
616 // the original manifest. We do this to ensure the manifest doesn't contain an
617 // exploitable bug that could be used to compromise the browser.
618 scoped_ptr<base::DictionaryValue> final_manifest(manifest.DeepCopy());
619 final_manifest->SetString(manifest_keys::kPublicKey, public_key_);
620
621 std::string manifest_json;
622 JSONStringValueSerializer serializer(&manifest_json);
623 serializer.set_pretty_print(true);
624 if (!serializer.Serialize(*final_manifest)) {
625 // Error serializing manifest.json.
626 ReportFailure(
627 ERROR_SERIALIZING_MANIFEST_JSON,
628 l10n_util::GetStringFUTF16(
629 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
630 ASCIIToUTF16("ERROR_SERIALIZING_MANIFEST_JSON")));
631 return NULL;
632 }
633
634 base::FilePath manifest_path =
635 extension_root_.Append(kManifestFilename);
636 int size = base::checked_cast<int>(manifest_json.size());
637 if (base::WriteFile(manifest_path, manifest_json.data(), size) != size) {
638 // Error saving manifest.json.
639 ReportFailure(
640 ERROR_SAVING_MANIFEST_JSON,
641 l10n_util::GetStringFUTF16(
642 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
643 ASCIIToUTF16("ERROR_SAVING_MANIFEST_JSON")));
644 return NULL;
645 }
646
647 return final_manifest.release();
648 }
649
650 bool SandboxedUnpacker::RewriteImageFiles(SkBitmap* install_icon) {
651 DecodedImages images;
652 if (!ReadImagesFromFile(temp_dir_.path(), &images)) {
653 // Couldn't read image data from disk.
654 ReportFailure(
655 COULD_NOT_READ_IMAGE_DATA_FROM_DISK,
656 l10n_util::GetStringFUTF16(
657 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
658 ASCIIToUTF16("COULD_NOT_READ_IMAGE_DATA_FROM_DISK")));
659 return false;
660 }
661
662 // Delete any images that may be used by the browser. We're going to write
663 // out our own versions of the parsed images, and we want to make sure the
664 // originals are gone for good.
665 std::set<base::FilePath> image_paths =
666 ExtensionsClient::Get()->GetBrowserImagePaths(extension_.get());
667 if (image_paths.size() != images.size()) {
668 // Decoded images don't match what's in the manifest.
669 ReportFailure(
670 DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST,
671 l10n_util::GetStringFUTF16(
672 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
673 ASCIIToUTF16("DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST")));
674 return false;
675 }
676
677 for (std::set<base::FilePath>::iterator it = image_paths.begin();
678 it != image_paths.end(); ++it) {
679 base::FilePath path = *it;
680 if (path.IsAbsolute() || path.ReferencesParent()) {
681 // Invalid path for browser image.
682 ReportFailure(
683 INVALID_PATH_FOR_BROWSER_IMAGE,
684 l10n_util::GetStringFUTF16(
685 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
686 ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE")));
687 return false;
688 }
689 if (!base::DeleteFile(extension_root_.Append(path), false)) {
690 // Error removing old image file.
691 ReportFailure(
692 ERROR_REMOVING_OLD_IMAGE_FILE,
693 l10n_util::GetStringFUTF16(
694 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
695 ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE")));
696 return false;
697 }
698 }
699
700 const std::string& install_icon_path =
701 IconsInfo::GetIcons(extension_.get()).Get(
702 extension_misc::EXTENSION_ICON_LARGE, ExtensionIconSet::MATCH_BIGGER);
703
704 // Write our parsed images back to disk as well.
705 for (size_t i = 0; i < images.size(); ++i) {
706 if (BrowserThread::GetBlockingPool()->IsShutdownInProgress()) {
707 // Abort package installation if shutdown was initiated, crbug.com/235525
708 ReportFailure(
709 ABORTED_DUE_TO_SHUTDOWN,
710 l10n_util::GetStringFUTF16(
711 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
712 ASCIIToUTF16("ABORTED_DUE_TO_SHUTDOWN")));
713 return false;
714 }
715
716 const SkBitmap& image = get<0>(images[i]);
717 base::FilePath path_suffix = get<1>(images[i]);
718 if (path_suffix.MaybeAsASCII() == install_icon_path)
719 *install_icon = image;
720
721 if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) {
722 // Invalid path for bitmap image.
723 ReportFailure(
724 INVALID_PATH_FOR_BITMAP_IMAGE,
725 l10n_util::GetStringFUTF16(
726 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
727 ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE")));
728 return false;
729 }
730 base::FilePath path = extension_root_.Append(path_suffix);
731
732 std::vector<unsigned char> image_data;
733 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
734 // though they may originally be .jpg, etc. Figure something out.
735 // http://code.google.com/p/chromium/issues/detail?id=12459
736 if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) {
737 // Error re-encoding theme image.
738 ReportFailure(
739 ERROR_RE_ENCODING_THEME_IMAGE,
740 l10n_util::GetStringFUTF16(
741 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
742 ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE")));
743 return false;
744 }
745
746 // Note: we're overwriting existing files that the utility process wrote,
747 // so we can be sure the directory exists.
748 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
749 int size = base::checked_cast<int>(image_data.size());
750 if (base::WriteFile(path, image_data_ptr, size) != size) {
751 // Error saving theme image.
752 ReportFailure(
753 ERROR_SAVING_THEME_IMAGE,
754 l10n_util::GetStringFUTF16(
755 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
756 ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE")));
757 return false;
758 }
759 }
760
761 return true;
762 }
763
764 bool SandboxedUnpacker::RewriteCatalogFiles() {
765 base::DictionaryValue catalogs;
766 if (!ReadMessageCatalogsFromFile(temp_dir_.path(), &catalogs)) {
767 // Could not read catalog data from disk.
768 ReportFailure(
769 COULD_NOT_READ_CATALOG_DATA_FROM_DISK,
770 l10n_util::GetStringFUTF16(
771 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
772 ASCIIToUTF16("COULD_NOT_READ_CATALOG_DATA_FROM_DISK")));
773 return false;
774 }
775
776 // Write our parsed catalogs back to disk.
777 for (base::DictionaryValue::Iterator it(catalogs);
778 !it.IsAtEnd(); it.Advance()) {
779 const base::DictionaryValue* catalog = NULL;
780 if (!it.value().GetAsDictionary(&catalog)) {
781 // Invalid catalog data.
782 ReportFailure(
783 INVALID_CATALOG_DATA,
784 l10n_util::GetStringFUTF16(
785 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
786 ASCIIToUTF16("INVALID_CATALOG_DATA")));
787 return false;
788 }
789
790 base::FilePath relative_path = base::FilePath::FromUTF8Unsafe(it.key());
791 relative_path = relative_path.Append(kMessagesFilename);
792 if (relative_path.IsAbsolute() || relative_path.ReferencesParent()) {
793 // Invalid path for catalog.
794 ReportFailure(
795 INVALID_PATH_FOR_CATALOG,
796 l10n_util::GetStringFUTF16(
797 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
798 ASCIIToUTF16("INVALID_PATH_FOR_CATALOG")));
799 return false;
800 }
801 base::FilePath path = extension_root_.Append(relative_path);
802
803 std::string catalog_json;
804 JSONStringValueSerializer serializer(&catalog_json);
805 serializer.set_pretty_print(true);
806 if (!serializer.Serialize(*catalog)) {
807 // Error serializing catalog.
808 ReportFailure(
809 ERROR_SERIALIZING_CATALOG,
810 l10n_util::GetStringFUTF16(
811 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
812 ASCIIToUTF16("ERROR_SERIALIZING_CATALOG")));
813 return false;
814 }
815
816 // Note: we're overwriting existing files that the utility process read,
817 // so we can be sure the directory exists.
818 int size = base::checked_cast<int>(catalog_json.size());
819 if (base::WriteFile(path, catalog_json.c_str(), size) != size) {
820 // Error saving catalog.
821 ReportFailure(
822 ERROR_SAVING_CATALOG,
823 l10n_util::GetStringFUTF16(
824 IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
825 ASCIIToUTF16("ERROR_SAVING_CATALOG")));
826 return false;
827 }
828 }
829
830 return true;
831 }
832
833 void SandboxedUnpacker::Cleanup() {
834 DCHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
835 if (!temp_dir_.Delete()) {
836 LOG(WARNING) << "Can not delete temp directory at "
837 << temp_dir_.path().value();
838 }
839 }
840
841 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/browser/extensions/sandboxed_unpacker.h ('k') | chrome/browser/extensions/sandboxed_unpacker_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698