summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-15 22:23:43 +0000
committermpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-15 22:23:43 +0000
commit1fca149ca717c64ae05edb534a61a909dc0a6d11 (patch)
tree6972a9efe49eba842a77cdeb98be8ac2caba8d27
parent20a85780ae0ae9b8467b10146044fec8c1144e77 (diff)
downloadchromium_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
-rw-r--r--chrome/app/chrome_dll_main.cc3
-rw-r--r--chrome/browser/browser.vcproj8
-rw-r--r--chrome/browser/extensions/extensions_service.cc230
-rw-r--r--chrome/browser/extensions/extensions_service.h40
-rw-r--r--chrome/browser/extensions/extensions_service_unittest.cc14
-rw-r--r--chrome/browser/sandbox_policy.cc23
-rw-r--r--chrome/browser/sandbox_policy.h6
-rw-r--r--chrome/browser/utility_process_host.cc87
-rw-r--r--chrome/browser/utility_process_host.h79
-rw-r--r--chrome/chrome.gyp38
-rw-r--r--chrome/chrome.sln12
-rw-r--r--chrome/common/child_process_info.cc2
-rw-r--r--chrome/common/child_process_info.h1
-rw-r--r--chrome/common/chrome_switches.cc3
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/common.vcproj8
-rw-r--r--chrome/common/extensions/extension_unpacker.cc212
-rw-r--r--chrome/common/extensions/extension_unpacker.h45
-rw-r--r--chrome/common/render_messages_internal.h19
-rw-r--r--chrome/common/sandbox_init_wrapper.cc1
-rw-r--r--chrome/utility/utility.vcproj155
-rw-r--r--chrome/utility/utility_main.cc44
-rw-r--r--chrome/utility/utility_thread.cc40
-rw-r--r--chrome/utility/utility_thread.h36
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, &current_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_