diff options
Diffstat (limited to 'chrome/common')
-rw-r--r-- | chrome/common/child_process_info.cc | 2 | ||||
-rw-r--r-- | chrome/common/child_process_info.h | 1 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 3 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 | ||||
-rw-r--r-- | chrome/common/common.vcproj | 8 | ||||
-rw-r--r-- | chrome/common/extensions/extension_unpacker.cc | 212 | ||||
-rw-r--r-- | chrome/common/extensions/extension_unpacker.h | 45 | ||||
-rw-r--r-- | chrome/common/render_messages_internal.h | 19 | ||||
-rw-r--r-- | chrome/common/sandbox_init_wrapper.cc | 1 |
9 files changed, 292 insertions, 0 deletions
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) |