summaryrefslogtreecommitdiffstats
path: root/extensions/browser
diff options
context:
space:
mode:
authorasargent <asargent@chromium.org>2015-08-28 15:44:39 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-28 22:45:13 +0000
commitc4fdad2c3113c76f68b61b5cdf3c27eb97b868c5 (patch)
treec2990060f9f5902c4b57787ddc2f2f06fa1bbcdc /extensions/browser
parenta2a22b892203e521d5355f613147504278b098cc (diff)
downloadchromium_src-c4fdad2c3113c76f68b61b5cdf3c27eb97b868c5.zip
chromium_src-c4fdad2c3113c76f68b61b5cdf3c27eb97b868c5.tar.gz
chromium_src-c4fdad2c3113c76f68b61b5cdf3c27eb97b868c5.tar.bz2
Let the SandboxedUnpacker start with either crx files or directories
Traditionally installing always had to start with a .crx file, which was first unzipped followed by having some files sanitized. But to support differential updates, we'll no longer be starting with a .crx file, but rather with an unpacked directory that was assembled for us by the differential update code applying patches to copies of files from the currently installed version. So we need a way to do the sanitizing starting from the equivalent of an unzipped directory. BUG=490418 Review URL: https://codereview.chromium.org/1256263007 Cr-Commit-Position: refs/heads/master@{#346255}
Diffstat (limited to 'extensions/browser')
-rw-r--r--extensions/browser/DEPS1
-rw-r--r--extensions/browser/sandboxed_unpacker.cc202
-rw-r--r--extensions/browser/sandboxed_unpacker.h102
-rw-r--r--extensions/browser/sandboxed_unpacker_unittest.cc58
4 files changed, 285 insertions, 78 deletions
diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS
index b20ccf8..abf85d9 100644
--- a/extensions/browser/DEPS
+++ b/extensions/browser/DEPS
@@ -24,6 +24,7 @@ include_rules = [
"+third_party/leveldatabase",
"+third_party/re2",
"+third_party/WebKit/public/web",
+ "+third_party/zlib/google",
]
specific_include_rules = {
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
index 7b27e85..965de48 100644
--- a/extensions/browser/sandboxed_unpacker.cc
+++ b/extensions/browser/sandboxed_unpacker.cc
@@ -212,26 +212,18 @@ SandboxedUnpackerClient::SandboxedUnpackerClient()
}
SandboxedUnpacker::SandboxedUnpacker(
- const CRXFileInfo& file,
Manifest::Location location,
int creation_flags,
const base::FilePath& extensions_dir,
const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner,
SandboxedUnpackerClient* client)
- : crx_path_(file.path),
- package_hash_(base::ToLowerASCII(file.expected_hash)),
- check_crx_hash_(false),
- client_(client),
+ : client_(client),
extensions_dir_(extensions_dir),
got_response_(false),
location_(location),
creation_flags_(creation_flags),
- unpacker_io_task_runner_(unpacker_io_task_runner) {
- if (!package_hash_.empty()) {
- check_crx_hash_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
- extensions::switches::kEnableCrxHashCheck);
- }
-}
+ unpacker_io_task_runner_(unpacker_io_task_runner),
+ utility_wrapper_(new UtilityHostWrapper) {}
bool SandboxedUnpacker::CreateTempDirectory() {
CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
@@ -256,15 +248,21 @@ bool SandboxedUnpacker::CreateTempDirectory() {
return true;
}
-void SandboxedUnpacker::Start() {
+void SandboxedUnpacker::StartWithCrx(const CRXFileInfo& crx_info) {
// We assume that we are started on the thread that the client wants us to do
// file IO on.
CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
- unpack_start_time_ = base::TimeTicks::Now();
+ crx_unpack_start_time_ = base::TimeTicks::Now();
+ std::string expected_hash;
+ if (!crx_info.expected_hash.empty() &&
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ extensions::switches::kEnableCrxHashCheck)) {
+ expected_hash = base::ToLowerASCII(crx_info.expected_hash);
+ }
PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackInitialCrxPathLength",
- crx_path_);
+ crx_info.path);
if (!CreateTempDirectory())
return; // ReportFailure() already called.
@@ -274,15 +272,16 @@ void SandboxedUnpacker::Start() {
extension_root_);
// Extract the public key and validate the package.
- if (!ValidateSignature())
+ if (!ValidateSignature(crx_info.path, expected_hash))
return; // ValidateSignature() already reported the error.
// Copy the crx file into our working directory.
- base::FilePath temp_crx_path = temp_dir_.path().Append(crx_path_.BaseName());
+ base::FilePath temp_crx_path =
+ temp_dir_.path().Append(crx_info.path.BaseName());
PATH_LENGTH_HISTOGRAM("Extensions.SandboxUnpackTempCrxPathLength",
temp_crx_path);
- if (!base::CopyFile(crx_path_, temp_crx_path)) {
+ if (!base::CopyFile(crx_info.path, temp_crx_path)) {
// Failed to copy extension file to temporary directory.
ReportFailure(
FAILED_TO_COPY_EXTENSION_FILE_TO_TEMP_DIRECTORY,
@@ -309,10 +308,35 @@ void SandboxedUnpacker::Start() {
link_free_crx_path);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
- base::Bind(&SandboxedUnpacker::StartProcessOnIOThread,
+ base::Bind(&SandboxedUnpacker::StartUnzipOnIOThread,
this, link_free_crx_path));
}
+void SandboxedUnpacker::StartWithDirectory(const std::string& extension_id,
+ const std::string& public_key,
+ const base::FilePath& directory) {
+ extension_id_ = extension_id;
+ public_key_ = public_key;
+ if (!CreateTempDirectory())
+ return; // ReportFailure() already called.
+
+ extension_root_ = temp_dir_.path().AppendASCII(kTempExtensionName);
+
+ if (!base::Move(directory, extension_root_)) {
+ LOG(ERROR) << "Could not move " << directory.value() << " to "
+ << extension_root_.value();
+ ReportFailure(
+ DIRECTORY_MOVE_FAILED,
+ l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
+ ASCIIToUTF16("DIRECTORY_MOVE_FAILED")));
+ return;
+ }
+
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ base::Bind(&SandboxedUnpacker::StartUnpackOnIOThread,
+ this, extension_root_));
+}
+
SandboxedUnpacker::~SandboxedUnpacker() {
// To avoid blocking shutdown, don't delete temporary directory here if it
// hasn't been cleaned up or passed on to another owner yet.
@@ -322,11 +346,15 @@ SandboxedUnpacker::~SandboxedUnpacker() {
bool SandboxedUnpacker::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(SandboxedUnpacker, message)
- IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Succeeded,
- OnUnpackExtensionSucceeded)
- IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Failed,
- OnUnpackExtensionFailed)
- IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Succeeded,
+ OnUnzipToDirSucceeded)
+ IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnzipToDir_Failed,
+ OnUnzipToDirFailed)
+ IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Succeeded,
+ OnUnpackExtensionSucceeded)
+ IPC_MESSAGE_HANDLER(ExtensionUtilityHostMsg_UnpackExtension_Failed,
+ OnUnpackExtensionFailed)
+ IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
@@ -346,23 +374,57 @@ void SandboxedUnpacker::OnProcessCrashed(int exit_code) {
l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_PROCESS_CRASHED));
}
-void SandboxedUnpacker::StartProcessOnIOThread(
- const base::FilePath& temp_crx_path) {
- UtilityProcessHost* host =
- UtilityProcessHost::Create(this, unpacker_io_task_runner_.get());
- host->SetName(
- l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_EXTENSION_UNPACKER_NAME));
- // Grant the subprocess access to the entire subdir the extension file is
- // in, so that it can unpack to that dir.
- host->SetExposedDir(temp_crx_path.DirName());
- host->Send(new ExtensionUtilityMsg_UnpackExtension(
- temp_crx_path, extension_id_, location_, creation_flags_));
+void SandboxedUnpacker::StartUnzipOnIOThread(const base::FilePath& crx_path) {
+ if (!utility_wrapper_->StartIfNeeded(temp_dir_.path(), this,
+ unpacker_io_task_runner_)) {
+ ReportFailure(
+ COULD_NOT_START_UTILITY_PROCESS,
+ l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
+ FailureReasonToString16(COULD_NOT_START_UTILITY_PROCESS)));
+ return;
+ }
+ DCHECK(crx_path.DirName() == temp_dir_.path());
+ base::FilePath unzipped_dir =
+ crx_path.DirName().AppendASCII(kTempExtensionName);
+ utility_wrapper_->host()->Send(
+ new ExtensionUtilityMsg_UnzipToDir(crx_path, unzipped_dir));
+}
+
+void SandboxedUnpacker::StartUnpackOnIOThread(
+ const base::FilePath& directory_path) {
+ if (!utility_wrapper_->StartIfNeeded(temp_dir_.path(), this,
+ unpacker_io_task_runner_)) {
+ ReportFailure(
+ COULD_NOT_START_UTILITY_PROCESS,
+ l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
+ FailureReasonToString16(COULD_NOT_START_UTILITY_PROCESS)));
+ return;
+ }
+ DCHECK(directory_path.DirName() == temp_dir_.path());
+ utility_wrapper_->host()->Send(new ExtensionUtilityMsg_UnpackExtension(
+ directory_path, extension_id_, location_, creation_flags_));
+}
+
+void SandboxedUnpacker::OnUnzipToDirSucceeded(const base::FilePath& directory) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&SandboxedUnpacker::StartUnpackOnIOThread, this, directory));
+}
+
+void SandboxedUnpacker::OnUnzipToDirFailed(const std::string& error) {
+ got_response_ = true;
+ utility_wrapper_ = nullptr;
+ ReportFailure(UNZIP_FAILED,
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PACKAGE_UNZIP_ERROR));
}
void SandboxedUnpacker::OnUnpackExtensionSucceeded(
const base::DictionaryValue& manifest) {
CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
got_response_ = true;
+ utility_wrapper_ = nullptr;
scoped_ptr<base::DictionaryValue> final_manifest(
RewriteManifestFile(manifest));
@@ -411,6 +473,7 @@ void SandboxedUnpacker::OnUnpackExtensionSucceeded(
void SandboxedUnpacker::OnUnpackExtensionFailed(const base::string16& error) {
CHECK(unpacker_io_task_runner_->RunsTasksOnCurrentThread());
got_response_ = true;
+ utility_wrapper_ = nullptr;
ReportFailure(
UNPACKER_CLIENT_FAILED,
l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_ERROR_MESSAGE, error));
@@ -495,6 +558,13 @@ base::string16 SandboxedUnpacker::FailureReasonToString16(
case CRX_HASH_VERIFICATION_FAILED:
return ASCIIToUTF16("CRX_HASH_VERIFICATION_FAILED");
+ case UNZIP_FAILED:
+ return ASCIIToUTF16("UNZIP_FAILED");
+ case DIRECTORY_MOVE_FAILED:
+ return ASCIIToUTF16("DIRECTORY_MOVE_FAILED");
+ case COULD_NOT_START_UTILITY_PROCESS:
+ return ASCIIToUTF16("COULD_NOT_START_UTILITY_PROCESS");
+
case NUM_FAILURE_REASONS:
NOTREACHED();
return base::string16();
@@ -509,14 +579,14 @@ void SandboxedUnpacker::FailWithPackageError(FailureReason reason) {
FailureReasonToString16(reason)));
}
-bool SandboxedUnpacker::ValidateSignature() {
+bool SandboxedUnpacker::ValidateSignature(const base::FilePath& crx_path,
+ const std::string& expected_hash) {
CrxFile::ValidateError error = CrxFile::ValidateSignature(
- crx_path_, check_crx_hash_ ? package_hash_ : std::string(), &public_key_,
- &extension_id_, nullptr);
+ crx_path, expected_hash, &public_key_, &extension_id_, nullptr);
switch (error) {
case CrxFile::ValidateError::NONE: {
- if (check_crx_hash_)
+ if (!expected_hash.empty())
UMA_HISTOGRAM_BOOLEAN("Extensions.SandboxUnpackHashCheck", true);
return true;
}
@@ -558,7 +628,7 @@ bool SandboxedUnpacker::ValidateSignature() {
case CrxFile::ValidateError::CRX_HASH_VERIFICATION_FAILED:
// We should never get this result unless we had specifically asked for
// verification of the crx file's hash.
- CHECK(check_crx_hash_ && !package_hash_.empty());
+ CHECK(!expected_hash.empty());
UMA_HISTOGRAM_BOOLEAN("Extensions.SandboxUnpackHashCheck", false);
FailWithPackageError(CRX_HASH_VERIFICATION_FAILED);
break;
@@ -568,10 +638,12 @@ bool SandboxedUnpacker::ValidateSignature() {
void SandboxedUnpacker::ReportFailure(FailureReason reason,
const base::string16& error) {
+ utility_wrapper_ = nullptr;
UMA_HISTOGRAM_ENUMERATION("Extensions.SandboxUnpackFailureReason", reason,
NUM_FAILURE_REASONS);
- UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackFailureTime",
- base::TimeTicks::Now() - unpack_start_time_);
+ if (!crx_unpack_start_time_.is_null())
+ UMA_HISTOGRAM_TIMES("Extensions.SandboxUnpackFailureTime",
+ base::TimeTicks::Now() - crx_unpack_start_time_);
Cleanup();
CrxInstallError error_info(reason == CRX_HASH_VERIFICATION_FAILED
@@ -585,10 +657,14 @@ void SandboxedUnpacker::ReportFailure(FailureReason reason,
void SandboxedUnpacker::ReportSuccess(
const base::DictionaryValue& original_manifest,
const SkBitmap& install_icon) {
+ utility_wrapper_ = nullptr;
UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1);
- RecordSuccessfulUnpackTimeHistograms(
- crx_path_, base::TimeTicks::Now() - unpack_start_time_);
+ if (!crx_unpack_start_time_.is_null())
+ RecordSuccessfulUnpackTimeHistograms(
+ crx_path_for_histograms_,
+ base::TimeTicks::Now() - crx_unpack_start_time_);
+ DCHECK(!temp_dir_.path().empty());
// Client takes ownership of temporary directory and extension.
client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_,
@@ -601,6 +677,7 @@ base::DictionaryValue* SandboxedUnpacker::RewriteManifestFile(
// Add the public key extracted earlier to the parsed manifest and overwrite
// the original manifest. We do this to ensure the manifest doesn't contain an
// exploitable bug that could be used to compromise the browser.
+ DCHECK(!public_key_.empty());
scoped_ptr<base::DictionaryValue> final_manifest(manifest.DeepCopy());
final_manifest->SetString(manifest_keys::kPublicKey, public_key_);
@@ -631,6 +708,7 @@ base::DictionaryValue* SandboxedUnpacker::RewriteManifestFile(
}
bool SandboxedUnpacker::RewriteImageFiles(SkBitmap* install_icon) {
+ DCHECK(!temp_dir_.path().empty());
DecodedImages images;
if (!ReadImagesFromFile(temp_dir_.path(), &images)) {
// Couldn't read image data from disk.
@@ -810,4 +888,42 @@ void SandboxedUnpacker::Cleanup() {
}
}
+SandboxedUnpacker::UtilityHostWrapper::UtilityHostWrapper() {}
+
+bool SandboxedUnpacker::UtilityHostWrapper::StartIfNeeded(
+ const base::FilePath& exposed_dir,
+ const scoped_refptr<UtilityProcessHostClient>& client,
+ const scoped_refptr<base::SequencedTaskRunner>& client_task_runner) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (!utility_host_) {
+ utility_host_ =
+ UtilityProcessHost::Create(client, client_task_runner)->AsWeakPtr();
+ utility_host_->SetName(
+ l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_EXTENSION_UNPACKER_NAME));
+
+ // Grant the subprocess access to our temp dir so it can write out files.
+ DCHECK(!exposed_dir.empty());
+ utility_host_->SetExposedDir(exposed_dir);
+ if (!utility_host_->StartBatchMode()) {
+ utility_host_.reset();
+ return false;
+ }
+ }
+ return true;
+}
+
+content::UtilityProcessHost* SandboxedUnpacker::UtilityHostWrapper::host()
+ const {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ return utility_host_.get();
+}
+
+SandboxedUnpacker::UtilityHostWrapper::~UtilityHostWrapper() {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (utility_host_) {
+ utility_host_->EndBatchMode();
+ utility_host_.reset();
+ }
+}
+
} // namespace extensions
diff --git a/extensions/browser/sandboxed_unpacker.h b/extensions/browser/sandboxed_unpacker.h
index 17156df..826d939 100644
--- a/extensions/browser/sandboxed_unpacker.h
+++ b/extensions/browser/sandboxed_unpacker.h
@@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_EXTENSIONS_SANDBOXED_UNPACKER_H_
-#define CHROME_BROWSER_EXTENSIONS_SANDBOXED_UNPACKER_H_
+#ifndef EXTENSIONS_BROWSER_SANDBOXED_UNPACKER_H_
+#define EXTENSIONS_BROWSER_SANDBOXED_UNPACKER_H_
#include <string>
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted_delete_on_message_loop.h"
+#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/utility_process_host_client.h"
@@ -24,6 +25,10 @@ class DictionaryValue;
class SequencedTaskRunner;
}
+namespace content {
+class UtilityProcessHost;
+}
+
namespace crypto {
class SecureHash;
}
@@ -64,10 +69,11 @@ class SandboxedUnpackerClient
virtual ~SandboxedUnpackerClient() {}
};
-// SandboxedUnpacker unpacks extensions from the CRX format into a
-// directory. This is done in a sandboxed subprocess to protect the browser
-// process from parsing complex formats like JPEG or JSON from untrusted
-// sources.
+// SandboxedUnpacker does work to optionally unpack and then validate/sanitize
+// an extension, either starting from a crx file or an already unzipped
+// directory (eg from differential update). This is done in a sandboxed
+// subprocess to protect the browser process from parsing complex formats like
+// JPEG or JSON from untrusted sources.
//
// Unpacking an extension using this class makes minor changes to its source,
// such as transcoding all images to PNG, parsing all message catalogs
@@ -87,19 +93,25 @@ class SandboxedUnpackerClient
// NOTE: This class should only be used on the file thread.
class SandboxedUnpacker : public content::UtilityProcessHostClient {
public:
- // Unpacks the extension in |crx_path| into a temporary directory and calls
- // |client| with the result. If |run_out_of_process| is provided, unpacking
- // is done in a sandboxed subprocess. Otherwise, it is done in-process.
+ // Creates a SanboxedUnpacker that will do work to unpack an extension,
+ // passing the |location| and |creation_flags| to Extension::Create. The
+ // |extensions_dir| parameter should specify the directory under which we'll
+ // create a subdirectory to write the unpacked extension contents.
SandboxedUnpacker(
- const CRXFileInfo& file,
Manifest::Location location,
int creation_flags,
const base::FilePath& extensions_dir,
const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner,
SandboxedUnpackerClient* client);
- // Start unpacking the extension. The client is called with the results.
- void Start();
+ // Start processing the extension, either from a CRX file or already unzipped
+ // in a directory. The client is called with the results. The directory form
+ // requires the id and base64-encoded public key (for insertion into the
+ // 'key' field of the manifest.json file).
+ void StartWithCrx(const CRXFileInfo& crx_info);
+ void StartWithDirectory(const std::string& extension_id,
+ const std::string& public_key_base64,
+ const base::FilePath& directory);
private:
class ProcessHostClient;
@@ -163,6 +175,10 @@ class SandboxedUnpacker : public content::UtilityProcessHostClient {
// SandboxedUnpacker::ValidateSignature()
CRX_HASH_VERIFICATION_FAILED,
+ UNZIP_FAILED,
+ DIRECTORY_MOVE_FAILED,
+ COULD_NOT_START_UTILITY_PROCESS,
+
NUM_FAILURE_REASONS
};
@@ -181,16 +197,19 @@ class SandboxedUnpacker : public content::UtilityProcessHostClient {
// Validates the signature of the extension and extract the key to
// |public_key_|. Returns true if the signature validates, false otherwise.
- bool ValidateSignature();
+ bool ValidateSignature(const base::FilePath& crx_path,
+ const std::string& expected_hash);
- // Starts the utility process that unpacks our extension.
- void StartProcessOnIOThread(const base::FilePath& temp_crx_path);
+ void StartUnzipOnIOThread(const base::FilePath& crx_path);
+ void StartUnpackOnIOThread(const base::FilePath& directory_path);
// UtilityProcessHostClient
bool OnMessageReceived(const IPC::Message& message) override;
void OnProcessCrashed(int exit_code) override;
// IPC message handlers.
+ void OnUnzipToDirSucceeded(const base::FilePath& directory);
+ void OnUnzipToDirFailed(const std::string& error);
void OnUnpackExtensionSucceeded(const base::DictionaryValue& manifest);
void OnUnpackExtensionFailed(const base::string16& error_message);
@@ -211,14 +230,41 @@ class SandboxedUnpacker : public content::UtilityProcessHostClient {
// Cleans up temp directory artifacts.
void Cleanup();
- // The path to the CRX to unpack.
- base::FilePath crx_path_;
-
- // The package SHA256 hash sum that was reported from the Web Store.
- std::string package_hash_;
+ // This is a helper class to make it easier to keep track of the lifecycle of
+ // a UtilityProcessHost, including automatic begin and end of batch mode.
+ class UtilityHostWrapper : public base::RefCountedThreadSafe<
+ UtilityHostWrapper,
+ content::BrowserThread::DeleteOnIOThread> {
+ public:
+ UtilityHostWrapper();
+
+ // Start up the utility process if it is not already started, putting it
+ // into batch mode and giving it access to |exposed_dir|. This should only
+ // be called on the IO thread. Returns false if there was an error starting
+ // the utility process or putting it into batch mode.
+ bool StartIfNeeded(
+ const base::FilePath& exposed_dir,
+ const scoped_refptr<UtilityProcessHostClient>& client,
+ const scoped_refptr<base::SequencedTaskRunner>& client_task_runner);
+
+ // This should only be called on the IO thread.
+ content::UtilityProcessHost* host() const;
+
+ private:
+ friend struct content::BrowserThread::DeleteOnThread<
+ content::BrowserThread::IO>;
+ friend class base::DeleteHelper<UtilityHostWrapper>;
+ ~UtilityHostWrapper();
+
+ // Should only be used on the IO thread.
+ base::WeakPtr<content::UtilityProcessHost> utility_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(UtilityHostWrapper);
+ };
- // Whether we need to check the .crx hash sum.
- bool check_crx_hash_;
+ // If we unpacked a crx file, we hold on to the path for use in various
+ // histograms.
+ base::FilePath crx_path_for_histograms_;
// Our client.
scoped_refptr<SandboxedUnpackerClient> client_;
@@ -245,8 +291,9 @@ class SandboxedUnpacker : public content::UtilityProcessHostClient {
// header.
std::string extension_id_;
- // Time at which unpacking started. Used to compute the time unpacking takes.
- base::TimeTicks unpack_start_time_;
+ // If we unpacked a .crx file, the time at which unpacking started. Used to
+ // compute the time unpacking takes.
+ base::TimeTicks crx_unpack_start_time_;
// Location to use for the unpacked extension.
Manifest::Location location_;
@@ -257,8 +304,13 @@ class SandboxedUnpacker : public content::UtilityProcessHostClient {
// Sequenced task runner where file I/O operations will be performed at.
scoped_refptr<base::SequencedTaskRunner> unpacker_io_task_runner_;
+
+ // Used for sending tasks to the utility process.
+ scoped_refptr<UtilityHostWrapper> utility_wrapper_;
+
+ DISALLOW_COPY_AND_ASSIGN(SandboxedUnpacker);
};
} // namespace extensions
-#endif // CHROME_BROWSER_EXTENSIONS_SANDBOXED_UNPACKER_H_
+#endif // EXTENSIONS_BROWSER_SANDBOXED_UNPACKER_H_
diff --git a/extensions/browser/sandboxed_unpacker_unittest.cc b/extensions/browser/sandboxed_unpacker_unittest.cc
index 02fceaf..a66b2fa 100644
--- a/extensions/browser/sandboxed_unpacker_unittest.cc
+++ b/extensions/browser/sandboxed_unpacker_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/base64.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
@@ -11,6 +12,7 @@
#include "base/strings/string_util.h"
#include "base/thread_task_runner_handle.h"
#include "base/values.h"
+#include "components/crx_file/id_util.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/extensions_test.h"
@@ -21,6 +23,7 @@
#include "extensions/common/switches.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/zlib/google/zip.h"
namespace extensions {
@@ -69,6 +72,10 @@ class SandboxedUnpackerTest : public ExtensionsTest {
new content::InProcessUtilityThreadHelper);
// It will delete itself.
client_ = new MockSandboxedUnpackerClient;
+
+ sandboxed_unpacker_ = new SandboxedUnpacker(
+ Manifest::INTERNAL, Extension::NO_FLAGS, extensions_dir_.path(),
+ base::ThreadTaskRunnerHandle::Get(), client_);
}
void TearDown() override {
@@ -79,21 +86,38 @@ class SandboxedUnpackerTest : public ExtensionsTest {
ExtensionsTest::TearDown();
}
+ base::FilePath GetCrxFullPath(const std::string& crx_name) {
+ base::FilePath full_path;
+ EXPECT_TRUE(PathService::Get(extensions::DIR_TEST_DATA, &full_path));
+ full_path = full_path.AppendASCII("unpacker").AppendASCII(crx_name);
+ EXPECT_TRUE(base::PathExists(full_path)) << full_path.value();
+ return full_path;
+ }
+
void SetupUnpacker(const std::string& crx_name,
const std::string& package_hash) {
- base::FilePath original_path;
- ASSERT_TRUE(PathService::Get(extensions::DIR_TEST_DATA, &original_path));
- original_path = original_path.AppendASCII("unpacker").AppendASCII(crx_name);
- ASSERT_TRUE(base::PathExists(original_path)) << original_path.value();
+ base::FilePath crx_path = GetCrxFullPath(crx_name);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &SandboxedUnpacker::StartWithCrx, sandboxed_unpacker_,
+ extensions::CRXFileInfo(std::string(), crx_path, package_hash)));
+ client_->WaitForUnpack();
+ }
- sandboxed_unpacker_ = new SandboxedUnpacker(
- extensions::CRXFileInfo(std::string(), original_path, package_hash),
- Manifest::INTERNAL, Extension::NO_FLAGS, extensions_dir_.path(),
- base::ThreadTaskRunnerHandle::Get(), client_);
+ void SetupUnpackerWithDirectory(const std::string& crx_name) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath crx_path = GetCrxFullPath(crx_name);
+ ASSERT_TRUE(zip::Unzip(crx_path, temp_dir.path()));
+ std::string fake_id = crx_file::id_util::GenerateId(crx_name);
+ std::string fake_public_key;
+ base::Base64Encode(std::string(2048, 'k'), &fake_public_key);
base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::Bind(&SandboxedUnpacker::Start, sandboxed_unpacker_.get()));
+ FROM_HERE, base::Bind(&SandboxedUnpacker::StartWithDirectory,
+ sandboxed_unpacker_.get(), fake_id,
+ fake_public_key, temp_dir.Take()));
client_->WaitForUnpack();
}
@@ -119,6 +143,13 @@ TEST_F(SandboxedUnpackerTest, NoCatalogsSuccess) {
EXPECT_FALSE(base::PathExists(install_path));
}
+TEST_F(SandboxedUnpackerTest, FromDirNoCatalogsSuccess) {
+ SetupUnpackerWithDirectory("no_l10n.crx");
+ // Check that there is no _locales folder.
+ base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
+ EXPECT_FALSE(base::PathExists(install_path));
+}
+
TEST_F(SandboxedUnpackerTest, WithCatalogsSuccess) {
SetupUnpacker("good_l10n.crx", "");
// Check that there is _locales folder.
@@ -126,6 +157,13 @@ TEST_F(SandboxedUnpackerTest, WithCatalogsSuccess) {
EXPECT_TRUE(base::PathExists(install_path));
}
+TEST_F(SandboxedUnpackerTest, FromDirWithCatalogsSuccess) {
+ SetupUnpackerWithDirectory("good_l10n.crx");
+ // Check that there is _locales folder.
+ base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
+ EXPECT_TRUE(base::PathExists(install_path));
+}
+
TEST_F(SandboxedUnpackerTest, FailHashCheck) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
extensions::switches::kEnableCrxHashCheck);