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