diff options
author | asargent <asargent@chromium.org> | 2015-08-28 15:44:39 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-28 22:45:13 +0000 |
commit | c4fdad2c3113c76f68b61b5cdf3c27eb97b868c5 (patch) | |
tree | c2990060f9f5902c4b57787ddc2f2f06fa1bbcdc /extensions/browser/sandboxed_unpacker.cc | |
parent | a2a22b892203e521d5355f613147504278b098cc (diff) | |
download | chromium_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/sandboxed_unpacker.cc')
-rw-r--r-- | extensions/browser/sandboxed_unpacker.cc | 202 |
1 files changed, 159 insertions, 43 deletions
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 |