diff options
author | mpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-15 22:23:43 +0000 |
---|---|---|
committer | mpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-15 22:23:43 +0000 |
commit | 1fca149ca717c64ae05edb534a61a909dc0a6d11 (patch) | |
tree | 6972a9efe49eba842a77cdeb98be8ac2caba8d27 | |
parent | 20a85780ae0ae9b8467b10146044fec8c1144e77 (diff) | |
download | chromium_src-1fca149ca717c64ae05edb534a61a909dc0a6d11.zip chromium_src-1fca149ca717c64ae05edb534a61a909dc0a6d11.tar.gz chromium_src-1fca149ca717c64ae05edb534a61a909dc0a6d11.tar.bz2 |
Introducing the Utility process, which handles the unpacking and verification
of extension packages.
This is a first pass. In the second pass, I will add support for transcoding
the manifest and any images in the browser process.
BUG=11680
Review URL: http://codereview.chromium.org/114027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16198 0039d316-1c4b-4281-b951-d872f2087c98
24 files changed, 1009 insertions, 98 deletions
diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc index be727ef..4a8d514 100644 --- a/chrome/app/chrome_dll_main.cc +++ b/chrome/app/chrome_dll_main.cc @@ -68,6 +68,7 @@ extern int BrowserMain(const MainFunctionParams&); extern int RendererMain(const MainFunctionParams&); extern int PluginMain(const MainFunctionParams&); extern int WorkerMain(const MainFunctionParams&); +extern int UtilityMain(const MainFunctionParams&); #if defined(OS_WIN) // TODO(erikkay): isn't this already defined somewhere? @@ -444,6 +445,8 @@ int ChromeMain(int argc, const char** argv) { rv = RendererMain(main_params); } else if (process_type == switches::kPluginProcess) { rv = PluginMain(main_params); + } else if (process_type == switches::kUtilityProcess) { + rv = UtilityMain(main_params); } else if (process_type == switches::kWorkerProcess) { #if defined(OS_WIN) rv = WorkerMain(main_params); diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index 07b0fb27c..bb869e6 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -2734,6 +2734,14 @@ RelativePath=".\toolbar_model.h" > </File> + <File + RelativePath=".\utility_process_host.cc" + > + </File> + <File + RelativePath=".\utility_process_host.h" + > + </File> </Files> <Globals> </Globals> diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index 05815a3..23c37fe 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -16,6 +16,7 @@ #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/extensions/extension.h" #include "chrome/browser/extensions/extension_browser_event_router.h" #include "chrome/browser/extensions/extension_error_reporter.h" @@ -23,6 +24,8 @@ #include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/plugin_service.h" #include "chrome/browser/profile.h" +#include "chrome/browser/utility_process_host.h" +#include "chrome/common/extensions/extension_unpacker.h" #include "chrome/common/json_value_serializer.h" #include "chrome/common/notification_service.h" #include "chrome/common/unzip.h" @@ -69,15 +72,117 @@ const wchar_t kRegistryExtensionVersion[] = L"version"; // source. const char kExternalInstallFile[] = "EXTERNAL_INSTALL"; +// A temporary subdirectory where we unpack extensions. +const char* kUnpackExtensionDir = "TEMP_UNPACK"; + // The version of the extension package that this code understands. const uint32 kExpectedVersion = 1; } +// This class coordinates an extension unpack task which is run in a separate +// process. Results are sent back to this class, which we route to the +// ExtensionServiceBackend. +class ExtensionsServiceBackend::UnpackerClient + : public UtilityProcessHost::Client { + public: + UnpackerClient(ExtensionsServiceBackend* backend, + const FilePath& extension_path, + const std::string& expected_id, + bool from_external) + : backend_(backend), extension_path_(extension_path), + expected_id_(expected_id), from_external_(from_external) { + } + + // Starts the unpack task. We call back to the backend when the task is done, + // or a problem occurs. + void Start() { + AddRef(); // balanced in OnUnpackExtensionReply() + + // TODO(mpcomplete): handle multiple installs + FilePath temp_dir = backend_->install_directory_.AppendASCII( + kUnpackExtensionDir); + if (!file_util::CreateDirectory(temp_dir)) { + backend_->ReportExtensionInstallError(extension_path_, + "Failed to create temporary directory."); + return; + } + + temp_extension_path_ = temp_dir.Append(extension_path_.BaseName()); + if (!file_util::CopyFile(extension_path_, temp_extension_path_)) { + backend_->ReportExtensionInstallError(extension_path_, + "Failed to copy extension file to temporary directory."); + return; + } + + if (backend_->resource_dispatcher_host_) { + ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE, + NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread, + backend_->resource_dispatcher_host_, + MessageLoop::current())); + } else { + // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're + // in a unit test and run the unpacker directly in-process. + ExtensionUnpacker unpacker(temp_extension_path_); + bool success = unpacker.Run(); + OnUnpackExtensionReply(success, unpacker.error_message()); + } + } + + private: + // UtilityProcessHost::Client + virtual void OnProcessCrashed() { + OnUnpackExtensionReply(false, "Chrome crashed while trying to install"); + } + + virtual void OnUnpackExtensionReply(bool success, + const std::string& error_message) { + if (success) { + // The extension was unpacked to the temp dir inside our unpacking dir. + FilePath extension_dir = temp_extension_path_.DirName().AppendASCII( + ExtensionsServiceBackend::kTempExtensionName); + backend_->OnExtensionUnpacked(extension_path_, extension_dir, + expected_id_, from_external_); + } else { + backend_->ReportExtensionInstallError(extension_path_, error_message); + } + Cleanup(); + Release(); // balanced in Run() + } + + // Cleans up our temp directory. + void Cleanup() { + file_util::Delete(temp_extension_path_.DirName(), true); + } + + // Starts the utility process that unpacks our extension. + void StartProcessOnIOThread(ResourceDispatcherHost* rdh, + MessageLoop* file_loop) { + UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop); + host->StartExtensionUnpacker(temp_extension_path_); + } + + scoped_refptr<ExtensionsServiceBackend> backend_; + + // The path to the crx file that we're installing. + FilePath extension_path_; + + // The path to the copy of the crx file in the temporary directory where we're + // unpacking it. + FilePath temp_extension_path_; + + // The ID we expect this extension to have, if any. + std::string expected_id_; + + // True if this is being installed from an external source. + bool from_external_; +}; + ExtensionsService::ExtensionsService(Profile* profile, UserScriptMaster* user_script_master) : message_loop_(MessageLoop::current()), install_directory_(profile->GetPath().AppendASCII(kInstallDirectoryName)), - backend_(new ExtensionsServiceBackend(install_directory_)), + backend_(new ExtensionsServiceBackend( + install_directory_, g_browser_process->resource_dispatcher_host())), user_script_master_(user_script_master) { } @@ -677,100 +782,80 @@ void ExtensionsServiceBackend::InstallExtension( frontend_ = frontend; alert_on_error_ = false; - bool was_update = false; - FilePath destination_path; - if (InstallOrUpdateExtension(extension_path, - std::string(), // no expected id - &destination_path, &was_update)) - ReportExtensionInstalled(destination_path.DirName(), was_update); + bool from_external = false; + InstallOrUpdateExtension(extension_path, std::string(), from_external); } -bool ExtensionsServiceBackend::InstallOrUpdateExtension( - const FilePath& source_file, const std::string& expected_id, - FilePath* version_dir, bool* was_update) { - if (was_update) - *was_update = false; +void ExtensionsServiceBackend::InstallOrUpdateExtension( + const FilePath& extension_path, const std::string& expected_id, + bool from_external) { + UnpackerClient* client = + new UnpackerClient(this, extension_path, expected_id, from_external); + client->Start(); +} - // Read and verify the extension. - scoped_ptr<DictionaryValue> manifest(ReadManifest(source_file)); +void ExtensionsServiceBackend::OnExtensionUnpacked( + const FilePath& extension_path, + const FilePath& temp_extension_dir, + const std::string expected_id, + bool from_external) { + // TODO(mpcomplete): the utility process should pass up a parsed manifest that + // we rewrite in the browser. + // Bug http://code.google.com/p/chromium/issues/detail?id=11680 + scoped_ptr<DictionaryValue> manifest(ReadManifest(extension_path)); if (!manifest.get()) { // ReadManifest has already reported the extension error. - return false; + return; } - DictionaryValue* dict = manifest.get(); + Extension extension; std::string error; - if (!extension.InitFromValue(*dict, + if (!extension.InitFromValue(*manifest, true, // require ID &error)) { - ReportExtensionInstallError(source_file, - "Invalid extension manifest."); - return false; - } - - // ID is required for installed extensions. - if (extension.id().empty()) { - ReportExtensionInstallError(source_file, "Required value 'id' is missing."); - return false; + ReportExtensionInstallError(extension_path, "Invalid extension manifest."); + return; } // If an expected id was provided, make sure it matches. - if (expected_id.length() && expected_id != extension.id()) { - ReportExtensionInstallError(source_file, + if (!expected_id.empty() && expected_id != extension.id()) { + ReportExtensionInstallError(extension_path, "ID in new extension manifest does not match expected ID."); - return false; + return; } // <profile>/Extensions/<id> FilePath dest_dir = install_directory_.AppendASCII(extension.id()); std::string version = extension.VersionString(); std::string current_version; + bool was_update = false; if (ReadCurrentVersion(dest_dir, ¤t_version)) { if (!CheckCurrentVersion(version, current_version, dest_dir)) - return false; - if (was_update) - *was_update = true; - } - - // <profile>/Extensions/INSTALL_TEMP - FilePath temp_dir = install_directory_.AppendASCII(kTempExtensionName); - - // Ensure we're starting with a clean slate. - if (file_util::PathExists(temp_dir)) { - if (!file_util::Delete(temp_dir, true)) { - ReportExtensionInstallError(source_file, - "Couldn't delete existing temporary directory."); - return false; - } - } - ScopedTempDir scoped_temp; - scoped_temp.Set(temp_dir); - if (!scoped_temp.IsValid()) { - ReportExtensionInstallError(source_file, - "Couldn't create temporary directory."); - return false; - } - - // <profile>/Extensions/INSTALL_TEMP/<version> - FilePath temp_version = temp_dir.AppendASCII(version); - file_util::CreateDirectory(temp_version); - if (!Unzip(source_file, temp_version, NULL)) { - ReportExtensionInstallError(source_file, "Couldn't unzip extension."); - return false; + return; + was_update = true; } // <profile>/Extensions/<dir_name>/<version> - *version_dir = dest_dir.AppendASCII(version); - if (!InstallDirSafely(temp_version, *version_dir)) - return false; + FilePath version_dir = dest_dir.AppendASCII(version); + if (!InstallDirSafely(temp_extension_dir, version_dir)) + return; if (!SetCurrentVersion(dest_dir, version)) { - if (!file_util::Delete(*version_dir, true)) + if (!file_util::Delete(version_dir, true)) LOG(WARNING) << "Can't remove " << dest_dir.value(); - return false; + return; } - return true; + // To mark that this extension was installed from an external source, create a + // zero-length file. At load time, this is used to indicate that the + // extension should be uninstalled. + // TODO(erikkay): move this into per-extension config storage when it appears. + if (from_external) { + FilePath marker = version_dir.AppendASCII(kExternalInstallFile); + file_util::WriteFile(marker, NULL, 0); + } + + ReportExtensionInstalled(dest_dir, was_update); } void ExtensionsServiceBackend::ReportExtensionInstallError( @@ -847,18 +932,9 @@ void ExtensionsServiceBackend::CheckForExternalUpdates( std::wstring extension_version; if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) { if (ShouldInstall(id, WideToASCII(extension_version))) { - FilePath version_dir; - if (InstallOrUpdateExtension(FilePath(extension_path), id, - &version_dir, NULL)) { - // To mark that this extension was installed from an external - // source, create a zero-length file. At load time, this is used - // to indicate that the extension should be uninstalled. - // TODO(erikkay): move this into per-extension config storage when - // it appears. - FilePath marker = version_dir.AppendASCII( - kExternalInstallFile); - file_util::WriteFile(marker, NULL, 0); - } + bool from_external = true; + InstallOrUpdateExtension(FilePath(extension_path), id, + from_external); } } else { // TODO(erikkay): find a way to get this into about:extensions diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h index 481cdd2..b601b73 100644 --- a/chrome/browser/extensions/extensions_service.h +++ b/chrome/browser/extensions/extensions_service.h @@ -19,6 +19,7 @@ class Extension; class ExtensionsServiceBackend; class GURL; class Profile; +class ResourceDispatcherHost; class SiteInstance; class UserScriptMaster; typedef std::vector<Extension*> ExtensionList; @@ -118,8 +119,10 @@ class ExtensionsService : public ExtensionsServiceFrontendInterface { class ExtensionsServiceBackend : public base::RefCountedThreadSafe<ExtensionsServiceBackend> { public: - explicit ExtensionsServiceBackend(const FilePath& install_directory) - : install_directory_(install_directory) {} + explicit ExtensionsServiceBackend(const FilePath& install_directory, + ResourceDispatcherHost* rdh) + : install_directory_(install_directory), + resource_dispatcher_host_(rdh) {} // Loads extensions from the install directory. The extensions are assumed to // be unpacked in directories that are direct children of the specified path. @@ -157,6 +160,9 @@ class ExtensionsServiceBackend void UninstallExtension(const std::string& extension_id); private: + class UnpackerClient; + friend class UnpackerClient; + // Load a single extension from |extension_path|, the top directory of // a specific extension where its manifest file lives. Extension* LoadExtension(const FilePath& extension_path, bool require_id); @@ -165,18 +171,23 @@ class ExtensionsServiceBackend // a versioned extension where its Current Version file lives. Extension* LoadExtensionCurrentVersion(const FilePath& extension_path); - // Install a crx file at |source_file|. If |expected_id| is not empty, it's - // verified against the extension's manifest before installation. If the - // extension is already installed, install the new version only if its version - // number is greater than the current installed version. On success, sets - // |version_dir| to the versioned directory the extension was installed to and - // |was_update| to whether the extension turned out to be an update to an - // already installed version. Both |version_dir| and |was_update| can be NULL - // if the caller doesn't care. - bool InstallOrUpdateExtension(const FilePath& source_file, + // Install a crx file at |extension_path|. If |expected_id| is not empty, it's + // verified against the extension's manifest before installation. If + // |from_external| is true, this extension install is from an external source, + // ie the Windows registry, and will be marked as such. If the extension is + // already installed, install the new version only if its version number is + // greater than the current installed version. + void InstallOrUpdateExtension(const FilePath& extension_path, const std::string& expected_id, - FilePath* version_dir, - bool* was_update); + bool from_external); + + // Finish installing an extension after it has been unpacked to + // |temp_extension_dir| by our utility process. If |expected_id| is not + // empty, it's verified against the extension's manifest before installation. + void OnExtensionUnpacked(const FilePath& extension_path, + const FilePath& temp_extension_dir, + const std::string expected_id, + bool from_external); // Notify the frontend that there was an error loading an extension. void ReportExtensionLoadError(const FilePath& extension_path, @@ -238,6 +249,9 @@ class ExtensionsServiceBackend // The top-level extensions directory being installed to. FilePath install_directory_; + // We only need a pointer to this to pass along to other interfaces. + ResourceDispatcherHost* resource_dispatcher_host_; + // Whether errors result in noisy alerts. bool alert_on_error_; diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc index bc1a3e3..ea01c25 100644 --- a/chrome/browser/extensions/extensions_service_unittest.cc +++ b/chrome/browser/extensions/extensions_service_unittest.cc @@ -152,7 +152,7 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectorySuccess) { extensions_path = extensions_path.AppendASCII("good"); scoped_refptr<ExtensionsServiceBackend> backend( - new ExtensionsServiceBackend(extensions_path)); + new ExtensionsServiceBackend(extensions_path, NULL)); scoped_refptr<ExtensionsServiceTestFrontend> frontend( new ExtensionsServiceTestFrontend); @@ -234,7 +234,7 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectoryFail) { extensions_path = extensions_path.AppendASCII("bad"); scoped_refptr<ExtensionsServiceBackend> backend( - new ExtensionsServiceBackend(extensions_path)); + new ExtensionsServiceBackend(extensions_path, NULL)); scoped_refptr<ExtensionsServiceTestFrontend> frontend( new ExtensionsServiceTestFrontend); @@ -273,7 +273,7 @@ TEST_F(ExtensionsServiceTest, InstallExtension) { file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("ext_test"), &install_dir); scoped_refptr<ExtensionsServiceBackend> backend( - new ExtensionsServiceBackend(install_dir)); + new ExtensionsServiceBackend(install_dir, NULL)); scoped_refptr<ExtensionsServiceTestFrontend> frontend( new ExtensionsServiceTestFrontend); @@ -313,7 +313,7 @@ TEST_F(ExtensionsServiceTest, UninstallExtension) { file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("ext_test"), &install_path); scoped_refptr<ExtensionsServiceBackend> backend( - new ExtensionsServiceBackend(install_path)); + new ExtensionsServiceBackend(install_path, NULL)); scoped_refptr<ExtensionsServiceTestFrontend> frontend( new ExtensionsServiceTestFrontend); @@ -356,7 +356,7 @@ TEST_F(ExtensionsServiceTest, ReinstallExtension) { file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("ext_test"), &install_dir); scoped_refptr<ExtensionsServiceBackend> backend( - new ExtensionsServiceBackend(install_dir)); + new ExtensionsServiceBackend(install_dir, NULL)); scoped_refptr<ExtensionsServiceTestFrontend> frontend( new ExtensionsServiceTestFrontend); @@ -409,7 +409,7 @@ TEST_F(ExtensionsServiceTest, LoadExtension) { extensions_path = extensions_path.AppendASCII("extensions"); scoped_refptr<ExtensionsServiceBackend> backend( - new ExtensionsServiceBackend(extensions_path)); + new ExtensionsServiceBackend(extensions_path, NULL)); scoped_refptr<ExtensionsServiceTestFrontend> frontend( new ExtensionsServiceTestFrontend); @@ -437,7 +437,7 @@ TEST_F(ExtensionsServiceTest, GenerateID) { extensions_path = extensions_path.AppendASCII("extensions"); scoped_refptr<ExtensionsServiceBackend> backend( - new ExtensionsServiceBackend(extensions_path)); + new ExtensionsServiceBackend(extensions_path, NULL)); scoped_refptr<ExtensionsServiceTestFrontend> frontend( new ExtensionsServiceTestFrontend); diff --git a/chrome/browser/sandbox_policy.cc b/chrome/browser/sandbox_policy.cc index 32c1f44..a50e01b 100644 --- a/chrome/browser/sandbox_policy.cc +++ b/chrome/browser/sandbox_policy.cc @@ -244,7 +244,6 @@ bool ApplyPolicyForUntrustedPlugin(sandbox::TargetPolicy* policy) { sandbox::TargetPolicy::FILES_ALLOW_ANY, policy)) return false; - if (!AddDirectoryAndChildren(base::DIR_APP_DATA, NULL, sandbox::TargetPolicy::FILES_ALLOW_READONLY, policy)) @@ -343,6 +342,11 @@ void AddPolicyForRenderer(HDESK desktop, sandbox::TargetPolicy* policy) { namespace sandbox { base::ProcessHandle StartProcess(CommandLine* cmd_line) { + return StartProcessWithAccess(cmd_line, FilePath()); +} + +base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line, + const FilePath& exposed_dir) { base::ProcessHandle process = 0; const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); ChildProcessInfo::ProcessType type; @@ -353,6 +357,8 @@ base::ProcessHandle StartProcess(CommandLine* cmd_line) { type = ChildProcessInfo::PLUGIN_PROCESS; } else if (type_str == switches::kWorkerProcess) { type = ChildProcessInfo::WORKER_PROCESS; + } else if (type_str == switches::kUtilityProcess) { + type = ChildProcessInfo::UTILITY_PROCESS; } else { NOTREACHED(); return 0; @@ -395,6 +401,21 @@ base::ProcessHandle StartProcess(CommandLine* cmd_line) { AddPolicyForRenderer(desktop, policy); } + if (!exposed_dir.empty()) { + result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, + sandbox::TargetPolicy::FILES_ALLOW_ANY, + exposed_dir.ToWStringHack().c_str()); + if (result != sandbox::SBOX_ALL_OK) + return 0; + + FilePath exposed_files = exposed_dir.AppendASCII("*"); + result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, + sandbox::TargetPolicy::FILES_ALLOW_ANY, + exposed_files.ToWStringHack().c_str()); + if (result != sandbox::SBOX_ALL_OK) + return 0; + } + if (!AddGenericPolicy(policy)) { NOTREACHED(); if (desktop) diff --git a/chrome/browser/sandbox_policy.h b/chrome/browser/sandbox_policy.h index cc9b538..664f279 100644 --- a/chrome/browser/sandbox_policy.h +++ b/chrome/browser/sandbox_policy.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_SANDBOX_POLICY_H_ #include "base/process.h" +#include "base/file_path.h" class CommandLine; @@ -14,6 +15,11 @@ namespace sandbox { // Starts a sandboxed process and returns a handle to it. base::ProcessHandle StartProcess(CommandLine* cmd_line); +// Starts a sandboxed process with the given directory unsandboxed +// and returns a handle to it. +base::ProcessHandle StartProcessWithAccess(CommandLine* cmd_line, + const FilePath& exposed_dir); + } // namespace sandbox; #endif // CHROME_BROWSER_SANDBOX_POLICY_H_ diff --git a/chrome/browser/utility_process_host.cc b/chrome/browser/utility_process_host.cc new file mode 100644 index 0000000..20b34d5 --- /dev/null +++ b/chrome/browser/utility_process_host.cc @@ -0,0 +1,87 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/utility_process_host.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/process_util.h" +#include "base/task.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/render_messages.h" + +#if defined(OS_WIN) +#include "chrome/browser/sandbox_policy.h" +#endif + +UtilityProcessHost::UtilityProcessHost(ResourceDispatcherHost* rdh, + Client* client, + MessageLoop* client_loop) + : ChildProcessHost(UTILITY_PROCESS, rdh), + client_(client), + client_loop_(client_loop) { +} + +UtilityProcessHost::~UtilityProcessHost() { +} + +bool UtilityProcessHost::StartExtensionUnpacker(const FilePath& extension) { + // Grant the subprocess access to the entire subdir the extension file is + // in, so that it can unpack to that dir. + if (!StartProcess(extension.DirName())) + return false; + + Send(new UtilityMsg_UnpackExtension(extension)); + return true; +} + +bool UtilityProcessHost::StartProcess(const FilePath& exposed_dir) { + if (!CreateChannel()) + return false; + + std::wstring exe_path; + if (!PathService::Get(base::FILE_EXE, &exe_path)) + return false; + + CommandLine cmd_line(exe_path); + cmd_line.AppendSwitchWithValue(switches::kProcessType, + switches::kUtilityProcess); + cmd_line.AppendSwitchWithValue(switches::kProcessChannelID, channel_id()); + + base::ProcessHandle process; +#if defined(OS_WIN) + process = sandbox::StartProcessWithAccess(&cmd_line, exposed_dir); +#else + // TODO(port): sandbox + base::LaunchApp(cmd_line, false, false, &process); +#endif + if (!process) + return false; + SetHandle(process); + + return true; +} + +void UtilityProcessHost::OnMessageReceived(const IPC::Message& message) { + client_loop_->PostTask(FROM_HERE, + NewRunnableMethod(client_.get(), &Client::OnMessageReceived, message)); +} + +void UtilityProcessHost::OnChannelError() { + bool child_exited; + bool did_crash = base::DidProcessCrash(&child_exited, handle()); + if (did_crash) { + client_loop_->PostTask(FROM_HERE, + NewRunnableMethod(client_.get(), &Client::OnProcessCrashed)); + } +} + +void UtilityProcessHost::Client::OnMessageReceived( + const IPC::Message& message) { + IPC_BEGIN_MESSAGE_MAP(UtilityProcessHost, message) + IPC_MESSAGE_HANDLER(UtilityHostMsg_UnpackExtension_Reply, + Client::OnUnpackExtensionReply) + IPC_END_MESSAGE_MAP_EX() +} diff --git a/chrome/browser/utility_process_host.h b/chrome/browser/utility_process_host.h new file mode 100644 index 0000000..8cb281e --- /dev/null +++ b/chrome/browser/utility_process_host.h @@ -0,0 +1,79 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UTILITY_PROCESS_HOST_H_ +#define CHROME_BROWSER_UTILITY_PROCESS_HOST_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "base/task.h" +#include "chrome/common/child_process_host.h" +#include "chrome/common/ipc_channel.h" + +class CommandLine; +class MessageLoop; + +// This class acts as the browser-side host to a utility child process. A +// utility process is a short-lived sandboxed process that is created to run +// a specific task. This class lives solely on the IO thread. +class UtilityProcessHost : public ChildProcessHost { + public: + // An interface to be implemented by consumers of the utility process to + // get results back. All functions are called on the thread passed along + // to UtilityProcessHost. + class Client : public base::RefCountedThreadSafe<Client> { + public: + Client() {} + virtual ~Client() {} + + // Called when the process has crashed. + virtual void OnProcessCrashed() {} + + // Called when the process sends a reply to an UnpackExtension message. + // If success if false, error_message contains a description of the problem. + virtual void OnUnpackExtensionReply(bool success, + const std::string& error_message) {} + private: + friend class UtilityProcessHost; + void OnMessageReceived(const IPC::Message& message); + + DISALLOW_COPY_AND_ASSIGN(Client); + }; + + UtilityProcessHost(ResourceDispatcherHost* rdh, Client* client, + MessageLoop* client_loop); + ~UtilityProcessHost(); + + // Start a process to unpack the extension at the given path. The process + // will be given access to the directory subtree that the extension file is + // in, so the caller is expected to have moved that file into a quarantined + // location first. + bool StartExtensionUnpacker(const FilePath& extension); + + private: + // Starts the process. Returns true iff it succeeded. + bool StartProcess(const FilePath& exposed_dir); + + // IPC messages: + void OnMessageReceived(const IPC::Message& message); + + // ChildProcessHost: + virtual void OnChannelError(); + virtual bool CanShutdown() { return true; } + virtual URLRequestContext* GetRequestContext( + uint32 request_id, + const ViewHostMsg_Resource_Request& request_data) { + return NULL; + } + + // A pointer to our client interface, who will be informed of progress. + scoped_refptr<Client> client_; + MessageLoop* client_loop_; + + DISALLOW_COPY_AND_ASSIGN(UtilityProcessHost); +}; + +#endif // CHROME_BROWSER_UTILITY_PROCESS_HOST_H_ diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index a8f6947..6f948c4 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -332,6 +332,8 @@ ], 'sources': [ # All .cc, .h, and .mm files under chrome/common except for tests. + 'common/extensions/extension_unpacker.cc', + 'common/extensions/extension_unpacker.h', 'common/extensions/url_pattern.cc', 'common/extensions/url_pattern.h', 'common/extensions/user_script.cc', @@ -1385,6 +1387,8 @@ 'browser/toolbar_model.h', 'browser/user_data_manager.cc', 'browser/user_data_manager.h', + 'browser/utility_process_host.cc', + 'browser/utility_process_host.h', 'browser/view_ids.h', 'browser/views/about_chrome_view.cc', 'browser/views/about_chrome_view.h', @@ -1924,6 +1928,38 @@ ], }, { + 'target_name': 'utility', + 'type': '<(library)', + 'dependencies': [ + '../base/base.gyp:base', + '../skia/skia.gyp:skia', + ], + 'sources': [ + 'tools/build/win/precompiled.cc', + 'tools/build/win/precompiled.h', + + 'utility/utility_main.cc', + 'utility/utility_thread.cc', + 'utility/utility_thread.h', + ], + 'include_dirs': [ + '..', + ], + 'configurations': { + 'Debug': { + 'msvs_precompiled_header': 'tools/build/win/precompiled.h', + 'msvs_precompiled_source': 'tools/build/win/precompiled.cc', + }, + }, + 'conditions': [ + ['OS=="linux"', { + 'dependencies': [ + '../build/linux/system.gyp:gtk', + ], + }], + ], + }, + { 'target_name': 'app', 'type': 'executable', 'mac_bundle': 1, @@ -1931,6 +1967,7 @@ 'common', 'browser', 'renderer', + 'utility', '../printing/printing.gyp:printing', '../webkit/webkit.gyp:inspector_resources', ], @@ -2675,6 +2712,7 @@ 'browser', 'common', 'renderer', + 'utility', 'chrome_resources', 'chrome_strings', 'test_support_unit', diff --git a/chrome/chrome.sln b/chrome/chrome.sln index c8b7250..e4a42a8 100644 --- a/chrome/chrome.sln +++ b/chrome/chrome.sln @@ -141,6 +141,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chrome_dll", "app\chrome_dl {326E9795-E760-410A-B69A-3F79DB3F5243} = {326E9795-E760-410A-B69A-3F79DB3F5243} {4631946D-7D5F-44BD-A5A8-504C0A7033BE} = {4631946D-7D5F-44BD-A5A8-504C0A7033BE} {49909552-0B0C-4C14-8CF6-DB8A2ADE0934} = {49909552-0B0C-4C14-8CF6-DB8A2ADE0934} + {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7} = {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7} {57823D8C-A317-4713-9125-2C91FDFD12D6} = {57823D8C-A317-4713-9125-2C91FDFD12D6} {5916D37D-8C97-424F-A904-74E52594C2D6} = {5916D37D-8C97-424F-A904-74E52594C2D6} {5BF908A7-68FB-4A4B-99E3-8C749F1FE4EA} = {5BF908A7-68FB-4A4B-99E3-8C749F1FE4EA} @@ -1552,6 +1553,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_shell_common", "..\web {CD9CA56E-4E94-444C-87D4-58CA1E6F300D} = {CD9CA56E-4E94-444C-87D4-58CA1E6F300D} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utility", "utility\utility.vcproj", "{4D2B38E6-65FF-4F97-B88A-E441DF54EBF7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Mixed Platforms = Debug|Mixed Platforms @@ -1920,6 +1923,14 @@ Global {4B6E199A-034A-49BD-AB93-458DD37E45B1}.Release|Mixed Platforms.Build.0 = Release|Win32 {4B6E199A-034A-49BD-AB93-458DD37E45B1}.Release|Win32.ActiveCfg = Release|Win32 {4B6E199A-034A-49BD-AB93-458DD37E45B1}.Release|Win32.Build.0 = Release|Win32 + {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7}.Debug|Win32.ActiveCfg = Debug|Win32 + {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7}.Debug|Win32.Build.0 = Debug|Win32 + {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7}.Release|Mixed Platforms.Build.0 = Release|Win32 + {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7}.Release|Win32.ActiveCfg = Release|Win32 + {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7}.Release|Win32.Build.0 = Release|Win32 {4EA8CE12-9C6F-45E5-9D08-720383FE3685}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 {4EA8CE12-9C6F-45E5-9D08-720383FE3685}.Debug|Mixed Platforms.Build.0 = Debug|Win32 {4EA8CE12-9C6F-45E5-9D08-720383FE3685}.Debug|Win32.ActiveCfg = Debug|Win32 @@ -2985,6 +2996,7 @@ Global {4631946D-7D5F-44BD-A5A8-504C0A7033BE} = {EF78C1F9-AA17-4CA5-B6CB-39B37A73A3DA} {4B60E8B8-416F-40B2-8A54-F75970A21992} = {2325D8C4-8EF5-42AC-8900-492225750DE4} {4B6E199A-034A-49BD-AB93-458DD37E45B1} = {1174D37F-6ABB-45DA-81B3-C631281273B7} + {4D2B38E6-65FF-4F97-B88A-E441DF54EBF7} = {97555540-8163-4D0F-BCAC-EFA0FFED3453} {4EA8CE12-9C6F-45E5-9D08-720383FE3685} = {846901FD-A619-4BD5-A303-38174730CDD6} {50B079C7-CD01-42D3-B8C4-9F8D9322E822} = {1174D37F-6ABB-45DA-81B3-C631281273B7} {57823D8C-A317-4713-9125-2C91FDFD12D6} = {97555540-8163-4D0F-BCAC-EFA0FFED3453} diff --git a/chrome/common/child_process_info.cc b/chrome/common/child_process_info.cc index bd4c070..15b0179 100644 --- a/chrome/common/child_process_info.cc +++ b/chrome/common/child_process_info.cc @@ -24,6 +24,8 @@ std::wstring ChildProcessInfo::GetTypeNameInEnglish( return L"Plug-in"; case WORKER_PROCESS: return L"Web Worker"; + case UTILITY_PROCESS: + return L"Utility"; case UNKNOWN_PROCESS: default: DCHECK(false) << "Unknown child process type!"; diff --git a/chrome/common/child_process_info.h b/chrome/common/child_process_info.h index 0b8386b..5bf9f44 100644 --- a/chrome/common/child_process_info.h +++ b/chrome/common/child_process_info.h @@ -17,6 +17,7 @@ class ChildProcessInfo { RENDER_PROCESS, PLUGIN_PROCESS, WORKER_PROCESS, + UTILITY_PROCESS, UNKNOWN_PROCESS, }; diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index d07c3e9..233259e 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -72,6 +72,9 @@ const wchar_t kPluginProcess[] = L"plugin"; // Causes the process to run as a worker subprocess. const wchar_t kWorkerProcess[] = L"worker"; +// Causes the process to run as a utility subprocess. +const wchar_t kUtilityProcess[] = L"utility"; + // Runs the renderer and plugins in the same process as the browser const wchar_t kSingleProcess[] = L"single-process"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index da3e0e4..f242ec9 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -29,6 +29,7 @@ extern const wchar_t kRendererProcess[]; extern const wchar_t kBrowserSubprocessPath[]; extern const wchar_t kPluginProcess[]; extern const wchar_t kWorkerProcess[]; +extern const wchar_t kUtilityProcess[]; extern const wchar_t kSingleProcess[]; extern const wchar_t kProcessPerTab[]; extern const wchar_t kProcessPerSite[]; diff --git a/chrome/common/common.vcproj b/chrome/common/common.vcproj index 5ca07ee..25b8b16 100644 --- a/chrome/common/common.vcproj +++ b/chrome/common/common.vcproj @@ -253,6 +253,14 @@ Name="extensions" > <File + RelativePath=".\extensions\extension_unpacker.cc" + > + </File> + <File + RelativePath=".\extensions\extension_unpacker.h" + > + </File> + <File RelativePath=".\extensions\url_pattern.cc" > </File> diff --git a/chrome/common/extensions/extension_unpacker.cc b/chrome/common/extensions/extension_unpacker.cc new file mode 100644 index 0000000..1849ec8 --- /dev/null +++ b/chrome/common/extensions/extension_unpacker.cc @@ -0,0 +1,212 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/extensions/extension_unpacker.h" + +#include "base/file_util.h" +#include "base/scoped_handle.h" +#include "base/scoped_temp_dir.h" +#include "base/string_util.h" +#include "base/third_party/nss/blapi.h" +#include "base/third_party/nss/sha256.h" +#include "base/thread.h" +#include "base/values.h" +#include "net/base/file_stream.h" +// TODO(mpcomplete): move to common +#include "chrome/browser/extensions/extension.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/unzip.h" +#include "chrome/common/url_constants.h" + +namespace { +const char kCurrentVersionFileName[] = "Current Version"; + +// The name of a temporary directory to install an extension into for +// validation before finalizing install. +const char kTempExtensionName[] = "TEMP_INSTALL"; + +// Chromium Extension magic number +const char kExtensionFileMagic[] = "Cr24"; + +struct ExtensionHeader { + char magic[sizeof(kExtensionFileMagic) - 1]; + uint32 version; + size_t header_size; + size_t manifest_size; +}; + +const size_t kZipHashBytes = 32; // SHA-256 +const size_t kZipHashHexBytes = kZipHashBytes * 2; // Hex string is 2x size. + +#if defined(OS_WIN) + +// Registry key where registry defined extension installers live. +const wchar_t kRegistryExtensions[] = L"Software\\Google\\Chrome\\Extensions"; + +// Registry value of of that key that defines the path to the .crx file. +const wchar_t kRegistryExtensionPath[] = L"path"; + +// Registry value of that key that defines the current version of the .crx file. +const wchar_t kRegistryExtensionVersion[] = L"version"; + +#endif + +// A marker file to indicate that an extension was installed from an external +// source. +const char kExternalInstallFile[] = "EXTERNAL_INSTALL"; + +// The version of the extension package that this code understands. +const uint32 kExpectedVersion = 1; +} + +// The extension file format is a header, followed by the manifest, followed +// by the zip file. The header is a magic number, a version, the size of the +// header, and the size of the manifest. These ints are 4 byte little endian. +DictionaryValue* ExtensionUnpacker::ReadManifest() { + ScopedStdioHandle file(file_util::OpenFile(extension_path_, "rb")); + if (!file.get()) { + SetError("no such extension file"); + return NULL; + } + + // Read and verify the header. + ExtensionHeader header; + size_t len; + + // TODO(erikkay): Yuck. I'm not a big fan of this kind of code, but it + // appears that we don't have any endian/alignment aware serialization + // code in the code base. So for now, this assumes that we're running + // on a little endian machine with 4 byte alignment. + len = fread(&header, 1, sizeof(ExtensionHeader), file.get()); + if (len < sizeof(ExtensionHeader)) { + SetError("invalid extension header"); + return NULL; + } + if (strncmp(kExtensionFileMagic, header.magic, sizeof(header.magic))) { + SetError("bad magic number"); + return NULL; + } + if (header.version != kExpectedVersion) { + SetError("bad version number"); + return NULL; + } + if (header.header_size > sizeof(ExtensionHeader)) + fseek(file.get(), header.header_size - sizeof(ExtensionHeader), SEEK_CUR); + + char buf[1 << 16]; + std::string manifest_str; + size_t read_size = std::min(sizeof(buf), header.manifest_size); + size_t remainder = header.manifest_size; + while ((len = fread(buf, 1, read_size, file.get())) > 0) { + manifest_str.append(buf, len); + if (len <= remainder) + break; + remainder -= len; + read_size = std::min(sizeof(buf), remainder); + } + + // Verify the JSON + JSONStringValueSerializer json(manifest_str); + std::string error; + scoped_ptr<Value> val(json.Deserialize(&error)); + if (!val.get()) { + SetError(error); + return NULL; + } + if (!val->IsType(Value::TYPE_DICTIONARY)) { + SetError("manifest isn't a JSON dictionary"); + return NULL; + } + DictionaryValue* manifest = static_cast<DictionaryValue*>(val.get()); + + std::string zip_hash; + if (!manifest->GetString(Extension::kZipHashKey, &zip_hash)) { + SetError("missing zip_hash key"); + return NULL; + } + if (zip_hash.size() != kZipHashHexBytes) { + SetError("invalid zip_hash key"); + return NULL; + } + + // Read the rest of the zip file and compute a hash to compare against + // what the manifest claims. Compute the hash incrementally since the + // zip file could be large. + const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf); + SHA256Context ctx; + SHA256_Begin(&ctx); + while ((len = fread(buf, 1, sizeof(buf), file.get())) > 0) + SHA256_Update(&ctx, ubuf, len); + uint8 hash[32]; + SHA256_End(&ctx, hash, NULL, sizeof(hash)); + + std::vector<uint8> zip_hash_bytes; + if (!HexStringToBytes(zip_hash, &zip_hash_bytes)) { + SetError("invalid zip_hash key"); + return NULL; + } + if (zip_hash_bytes.size() != kZipHashBytes) { + SetError("invalid zip_hash key"); + return NULL; + } + for (size_t i = 0; i < kZipHashBytes; ++i) { + if (zip_hash_bytes[i] != hash[i]) { + SetError("zip_hash key didn't match zip hash"); + return NULL; + } + } + + // TODO(erikkay): The manifest will also contain a signature of the hash + // (or perhaps the whole manifest) for authentication purposes. + + // The caller owns val (now cast to manifest). + val.release(); + return manifest; +} + +bool ExtensionUnpacker::Run() { + LOG(INFO) << "Installing extension " << extension_path_.value(); + + // Read and verify the extension. + scoped_ptr<DictionaryValue> manifest(ReadManifest()); + if (!manifest.get()) { + // ReadManifest has already reported the extension error. + return false; + } + Extension extension; + std::string error; + if (!extension.InitFromValue(*manifest, + true, // require ID + &error)) { + SetError("Invalid extension manifest."); + return false; + } + + // ID is required for installed extensions. + if (extension.id().empty()) { + SetError("Required value 'id' is missing."); + return false; + } + + // <profile>/Extensions/INSTALL_TEMP/<version> + std::string version = extension.VersionString(); + FilePath temp_install = + extension_path_.DirName().AppendASCII(kTempExtensionName); + if (!file_util::CreateDirectory(temp_install)) { + SetError("Couldn't create directory for unzipping."); + return false; + } + + if (!Unzip(extension_path_, temp_install, NULL)) { + SetError("Couldn't unzip extension."); + return false; + } + + return true; +} + +void ExtensionUnpacker::SetError(const std::string &error) { + error_message_ = error; +} diff --git a/chrome/common/extensions/extension_unpacker.h b/chrome/common/extensions/extension_unpacker.h new file mode 100644 index 0000000..6d5fbf3 --- /dev/null +++ b/chrome/common/extensions/extension_unpacker.h @@ -0,0 +1,45 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_EXTENSIONS_EXTENSION_UNPACKER_H_ +#define CHROME_COMMON_EXTENSIONS_EXTENSION_UNPACKER_H_ + +#include <string> + +#include "base/file_path.h" + +class DictionaryValue; + +// Implements IO for the ExtensionsService. +// TODO(aa): Extract an interface out of this for testing the frontend, once the +// frontend has significant logic to test. +class ExtensionUnpacker { + public: + explicit ExtensionUnpacker(const FilePath& extension_path) + : extension_path_(extension_path) {} + + // Install the extension file at |extension_path|. Returns true on success. + // Otherwise, error_message will contain a string explaining what went wrong. + bool Run(); + + const std::string& error_message() { return error_message_; } + + private: + // Read the manifest from the front of the extension file. + // Caller takes ownership of return value. + DictionaryValue* ReadManifest(); + + // Set the error message. + void SetError(const std::string& error); + + // The extension to unpack. + FilePath extension_path_; + + // The last error message that was set. Empty if there were no errors. + std::string error_message_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionUnpacker); +}; + +#endif // CHROME_COMMON_EXTENSIONS_EXTENSION_UNPACKER_H_ diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 5b12febb..4cf50d1 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -587,6 +587,15 @@ IPC_BEGIN_MESSAGES(View) // width. IPC_MESSAGE_ROUTED0(ViewMsg_EnableIntrinsicWidthChangedMode) + //--------------------------------------------------------------------------- + // Utility process messages: + // These are messages from the browser to the utility process. They're here + // because we ran out of spare message types. + + // Tell the utility process to unpack the given extension file in its + // directory and verify that it is valid. + IPC_MESSAGE_CONTROL1(UtilityMsg_UnpackExtension, + FilePath /* extension_filename */) IPC_END_MESSAGES(View) @@ -1372,4 +1381,14 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_ROUTED1(ViewHostMsg_ShowPopup, ViewHostMsg_ShowPopup_Params) + //--------------------------------------------------------------------------- + // Utility process host messages: + // These are messages from the utility process to the browser. They're here + // because we ran out of spare message types. + + // Reply when the utility process is done unpacking an extension. |success| + // argument is true if the extension unpacked and verified successfully. + IPC_MESSAGE_CONTROL2(UtilityHostMsg_UnpackExtension_Reply, + bool /* success */, + std::string /* error_message, if any */) IPC_END_MESSAGES(ViewHost) diff --git a/chrome/common/sandbox_init_wrapper.cc b/chrome/common/sandbox_init_wrapper.cc index 8d264ce..c296ff8 100644 --- a/chrome/common/sandbox_init_wrapper.cc +++ b/chrome/common/sandbox_init_wrapper.cc @@ -27,6 +27,7 @@ void SandboxInitWrapper::InitializeSandbox(const CommandLine& command_line, if (!command_line.HasSwitch(switches::kNoSandbox)) { if ((process_type == switches::kRendererProcess) || (process_type == switches::kWorkerProcess) || + (process_type == switches::kUtilityProcess) || (process_type == switches::kPluginProcess && command_line.HasSwitch(switches::kSafePlugins))) { #if defined(OS_WIN) diff --git a/chrome/utility/utility.vcproj b/chrome/utility/utility.vcproj new file mode 100644 index 0000000..0c77303 --- /dev/null +++ b/chrome/utility/utility.vcproj @@ -0,0 +1,155 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="utility" + ProjectGUID="{4D2B38E6-65FF-4F97-B88A-E441DF54EBF7}" + RootNamespace="utility" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="4" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;..\tools\build\win\precompiled.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="4" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;..\..\build\release.vsprops;$(SolutionDir)..\skia\using_skia.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <File + RelativePath="..\tools\build\win\precompiled.cc" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="1" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\tools\build\win\precompiled.h" + > + </File> + <File + RelativePath=".\utility_main.cc" + > + </File> + <File + RelativePath=".\utility_thread.cc" + > + </File> + <File + RelativePath=".\utility_thread.h" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/chrome/utility/utility_main.cc b/chrome/utility/utility_main.cc new file mode 100644 index 0000000..ad66043c --- /dev/null +++ b/chrome/utility/utility_main.cc @@ -0,0 +1,44 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/command_line.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/system_monitor.h" +#include "chrome/common/child_process.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/common/main_function_params.h" +#include "chrome/utility/utility_thread.h" + +#if defined(OS_WIN) +#include "chrome/common/sandbox_init_wrapper.h" +#include "sandbox/src/sandbox.h" +#endif + +// Mainline routine for running as the utility process. +int UtilityMain(const MainFunctionParams& parameters) { + // The main thread of the render process. + MessageLoopForIO main_message_loop; + std::wstring app_name = chrome::kBrowserAppName; + PlatformThread::SetName(WideToASCII(app_name + L"_UtilityMain").c_str()); + + // Initialize the SystemMonitor + base::SystemMonitor::Start(); + + ChildProcess utility_process(new UtilityThread()); +#if defined(OS_WIN) + sandbox::TargetServices* target_services = + parameters.sandbox_info_.TargetServices(); + if (!target_services) + return false; + + target_services->LowerToken(); +#endif + + MessageLoop::current()->Run(); + + return 0; +} diff --git a/chrome/utility/utility_thread.cc b/chrome/utility/utility_thread.cc new file mode 100644 index 0000000..269a86b --- /dev/null +++ b/chrome/utility/utility_thread.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/utility/utility_thread.h" + +#include "chrome/common/child_process.h" +#include "chrome/common/extensions/extension_unpacker.h" +#include "chrome/common/render_messages.h" + +UtilityThread::UtilityThread() : ChildThread(base::Thread::Options()) { +} + +UtilityThread::~UtilityThread() { +} + +void UtilityThread::Init() { + ChildThread::Init(); + ChildProcess::current()->AddRefProcess(); +} + +void UtilityThread::CleanUp() { + // Shutdown in reverse of the initialization order. + ChildThread::CleanUp(); +} + +void UtilityThread::OnControlMessageReceived(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(UtilityThread, msg) + IPC_MESSAGE_HANDLER(UtilityMsg_UnpackExtension, OnUnpackExtension) + IPC_END_MESSAGE_MAP() +} + +void UtilityThread::OnUnpackExtension(const FilePath& extension_path) { + ExtensionUnpacker unpacker(extension_path); + bool success = unpacker.Run(); + Send(new UtilityHostMsg_UnpackExtension_Reply(success, + unpacker.error_message())); + + ChildProcess::current()->ReleaseProcess(); +} diff --git a/chrome/utility/utility_thread.h b/chrome/utility/utility_thread.h new file mode 100644 index 0000000..9d918dc --- /dev/null +++ b/chrome/utility/utility_thread.h @@ -0,0 +1,36 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_UTILITY_UTILITY_THREAD_H_ +#define CHROME_UTILITY_UTILITY_THREAD_H_ + +#include "base/thread.h" +#include "chrome/common/child_thread.h" + +class GURL; + +// This class represents the background thread where the utility task runs. +class UtilityThread : public ChildThread { + public: + UtilityThread(); + ~UtilityThread(); + + // Returns the one utility thread. + static UtilityThread* current() { + return static_cast<UtilityThread*>(ChildThread::current()); + } + + private: + // IPC messages + virtual void OnControlMessageReceived(const IPC::Message& msg); + void OnUnpackExtension(const FilePath& extension_path); + + // Called by the thread base class + virtual void Init(); + virtual void CleanUp(); + + DISALLOW_COPY_AND_ASSIGN(UtilityThread); +}; + +#endif // CHROME_UTILITY_UTILITY_THREAD_H_ |