diff options
author | cpu@chromium.org <cpu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-22 02:55:04 +0000 |
---|---|---|
committer | cpu@chromium.org <cpu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-22 02:55:04 +0000 |
commit | 2e114e73ddef537fe075d84b1a580f459b50e8a4 (patch) | |
tree | 9ccea58b222d25a1b79023a6225620385399db14 /chrome/browser | |
parent | a5a583a1e097d5e3d28cf756403c6e0a8bf657e0 (diff) | |
download | chromium_src-2e114e73ddef537fe075d84b1a580f459b50e8a4.zip chromium_src-2e114e73ddef537fe075d84b1a580f459b50e8a4.tar.gz chromium_src-2e114e73ddef537fe075d84b1a580f459b50e8a4.tar.bz2 |
Component updater second installment
introducing the main external objects:
-ComponentInstaller
-ComponentUpdateService
The ComponentUnpacker is an implementation detail but it
is fairly simple and we might as well deal with it.
Tests are comming with the next CL.
TEST=none
BUG=61602
Review URL: http://codereview.chromium.org/7458011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93530 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
3 files changed, 399 insertions, 0 deletions
diff --git a/chrome/browser/component_updater/component_unpacker.cc b/chrome/browser/component_updater/component_unpacker.cc new file mode 100644 index 0000000..0af658b --- /dev/null +++ b/chrome/browser/component_updater/component_unpacker.cc @@ -0,0 +1,215 @@ +// Copyright (c) 2011 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/component_updater/component_unpacker.h" + +#include <string> +#include <vector> + +#include "base/file_util.h" +#include "base/memory/scoped_handle.h" +#include "base/string_number_conversions.h" +#include "base/stringprintf.h" +#include "chrome/browser/extensions/sandboxed_extension_unpacker.h" +#include "chrome/browser/component_updater/component_updater_service.h" +#include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/zip.h" +#include "content/common/json_value_serializer.h" +#include "crypto/secure_hash.h" +#include "crypto/signature_verifier.h" + +using crypto::SecureHash; + +// TODO(cpu): remove this ctor and dtor from here once other pieces land. +CrxComponent::CrxComponent() {} +CrxComponent::~CrxComponent() {} + +namespace { +// This class makes sure that the CRX digital signature is valid +// and well formed. +class CRXValidator { + public: + enum Result { + kValid, + kInvalidHeader, + kWrongMagic, + kInvalidVersion, + kInvalidKey, + kInvalidSignature, + kWrongKeyFormat, + kSignatureMismatch, + kLast + }; + + explicit CRXValidator(FILE* crx_file) : result_(kLast) { + SXU::ExtensionHeader header; + size_t len = fread(&header, 1, sizeof(header), crx_file); + if (len < sizeof(header)) { + result_ = kInvalidHeader; + return; + } + if (strncmp(SXU::kExtensionHeaderMagic, header.magic, + sizeof(header.magic))) { + result_ = kWrongMagic; + return; + } + if (header.version != SXU::kCurrentVersion) { + result_ = kInvalidVersion; + return; + } + if ((header.key_size > SXU::kMaxPublicKeySize) || + (header.key_size == 0)) { + result_ = kInvalidKey; + return; + } + if ((header.signature_size > SXU::kMaxSignatureSize) || + (header.signature_size == 0)) { + result_ = kInvalidSignature; + return; + } + + std::vector<uint8> key(header.key_size); + len = fread(&key[0], sizeof(uint8), header.key_size, crx_file); + if (len < header.key_size) { + result_ = kInvalidKey; + return; + } + + std::vector<uint8> signature(header.signature_size); + len = fread(&signature[0], sizeof(uint8), header.signature_size, crx_file); + if (len < header.signature_size) { + result_ = kInvalidSignature; + return; + } + + crypto::SignatureVerifier verifier; + if (!verifier.VerifyInit(extension_misc::kSignatureAlgorithm, + sizeof(extension_misc::kSignatureAlgorithm), + &signature[0], signature.size(), + &key[0], key.size())) { + // Signature verification initialization failed. This is most likely + // caused by a public key in the wrong format (should encode algorithm). + result_ = kWrongKeyFormat; + return; + } + + const size_t kBufSize = 8 * 1024; + scoped_ptr<uint8> buf(new uint8[kBufSize]); + while ((len = fread(buf.get(), 1, kBufSize, crx_file)) > 0) + verifier.VerifyUpdate(buf.get(), len); + + if (!verifier.VerifyFinal()) { + result_ = kSignatureMismatch; + return; + } + + public_key_.swap(key); + result_ = kValid; + } + + Result result() const { return result_; } + + const std::vector<uint8>& public_key() const { return public_key_; } + + private: + // TODO(cpu): Move the kExtensionHeaderMagic constants to a better header + // right now it feels we are reaching too deep into the extension code. + typedef SandboxedExtensionUnpacker SXU; + Result result_; + std::vector<uint8> public_key_; +}; + +// Deserialize the CRX manifest. The top level must be a dictionary. +// TODO(cpu): add a specific attribute check to a component json that the +// extension unpacker will reject, so that a component cannot be installed +// as an extension. +base::DictionaryValue* ReadManifest(const FilePath& unpack_path) { + FilePath manifest = unpack_path.Append(FILE_PATH_LITERAL("manifest.json")); + if (!file_util::PathExists(manifest)) + return NULL; + JSONFileValueSerializer serializer(manifest); + std::string error; + scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); + if (!root.get()) + return NULL; + if (!root->IsType(base::Value::TYPE_DICTIONARY)) + return NULL; + return static_cast<base::DictionaryValue*>(root.release()); +} + +} // namespace. + +ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash, + const FilePath& path, + ComponentInstaller* installer) + : error_(kNone) { + if (pk_hash.empty() || path.empty()) { + error_ = kInvalidParams; + return; + } + // First, validate the CRX header and signature. As of today + // this is SHA1 with RSA 1024. + ScopedStdioHandle file(file_util::OpenFile(path, "rb")); + if (!file.get()) { + error_ = kInvalidFile; + return; + } + CRXValidator validator(file.get()); + if (validator.result() != CRXValidator::kValid) { + error_ = kInvalidFile; + return; + } + file.Close(); + + // File is vaild and the digital signature matches. Now make sure + // the public key hash matches the expected hash. If they do we fully + // trust this CRX. + uint8 hash[32]; + scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); + sha256->Update(&(validator.public_key()[0]), validator.public_key().size()); + sha256->Finish(hash, arraysize(hash)); + + if (!std::equal(pk_hash.begin(), pk_hash.end(), hash)) { + error_ = kInvalidId; + return; + } + // We want the temporary directory to be unique and yet predictable, so + // we can easily find the package in a end user machine. + std::string dir(StringPrintf("CRX_%s", base::HexEncode(hash, 6).c_str())); + unpack_path_ = path.DirName().AppendASCII(dir.c_str()); + if (file_util::DirectoryExists(unpack_path_)) { + if (!file_util::Delete(unpack_path_, true)) { + unpack_path_.clear(); + error_ = kUzipPathError; + return; + } + } + if (!file_util::CreateDirectory(unpack_path_)) { + unpack_path_.clear(); + error_ = kUzipPathError; + return; + } + if (!Unzip(path, unpack_path_)) { + error_ = kUnzipFailed; + return; + } + scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); + if (!manifest.get()) { + error_ = kBadManifest; + return; + } + if (!installer->Install(manifest.release(), unpack_path_)) { + error_ = kInstallerError; + return; + } + // Installation succesful. The directory is not our concern now. + unpack_path_.clear(); +} + +ComponentUnpacker::~ComponentUnpacker() { + if (!unpack_path_.empty()) { + file_util::Delete(unpack_path_, true); + } +} + diff --git a/chrome/browser/component_updater/component_unpacker.h b/chrome/browser/component_updater/component_unpacker.h new file mode 100644 index 0000000..60fc385 --- /dev/null +++ b/chrome/browser/component_updater/component_unpacker.h @@ -0,0 +1,59 @@ +// Copyright (c) 2011 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_COMPONENT_UPDATER_COMPONENT_UNPACKER_H_ +#define CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UNPACKER_H_ +#pragma once + +#include <vector> + +#include "base/file_path.h" + +class ComponentInstaller; + +// In charge of unpacking the component CRX package and verifying that it is +// well formed and the cryptographic signature is correct. If there is no +// error the component specific installer will be invoked to proceed with +// the component installation or update. +// +// This class should be used only by the component updater. It is inspired +// and overlaps with code in the extension's SandboxedExtensionUnpacker. +// The main differences are: +// - The public key hash is full SHA256. +// - Does not use a sandboxed unpacker. A valid component is fully trusted. +// - The manifest can have different attributes and resources are not +// transcoded. +class ComponentUnpacker { + public: + // Possible error conditions. + enum Error { + kNone, + kInvalidParams, + kInvalidFile, + kUzipPathError, + kUnzipFailed, + kNoManifest, + kBadManifest, + kBadExtension, + kInvalidId, + kInstallerError, + }; + // Unpacks, verifies and calls the installer. |pk_hash| is the expected + // public key SHA256 hash. |path| is the current location of the CRX. + ComponentUnpacker(const std::vector<uint8>& pk_hash, + const FilePath& path, + ComponentInstaller* installer); + + // If something went wrong during unpacking or installer invocation, the + // destructor will delete the unpacked CRX files. + ~ComponentUnpacker(); + + Error error() const { return error_; } + + private: + FilePath unpack_path_; + Error error_; +}; + +#endif // CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UNPACKER_H_ diff --git a/chrome/browser/component_updater/component_updater_service.h b/chrome/browser/component_updater/component_updater_service.h new file mode 100644 index 0000000..9e55a33 --- /dev/null +++ b/chrome/browser/component_updater/component_updater_service.h @@ -0,0 +1,125 @@ +// Copyright (c) 2011 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_COMPONENT_UPDATER_COMPONENT_UPDATER_SERVICE_H_ +#define CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UPDATER_SERVICE_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/version.h" +#include "googleurl/src/gurl.h" + +class FilePath; + +namespace net { +class URLRequestContextGetter; +} + +namespace base { +class DictionaryValue; +} + +// Component specific installers must derive from this class and implement +// OnUpdateError() and Install(). A valid instance of this class must be +// given to ComponentUpdateService::RegisterComponent(). +class ComponentInstaller { + public : + virtual ~ComponentInstaller() {} + + // Called by the component updater on the UI thread when there was a + // problem unpacking or verifying the component. |error| is a non-zero + // value which is only meaninful to the component updater. + virtual void OnUpdateError(int error) = 0; + + // Called by the component updater when a component has been unpacked + // and is ready to be installed. |manifest| contains the CRX manifest + // json dictionary and |unpack_path| contains the temporary directory + // with all the unpacked CRX files. + virtual bool Install(base::DictionaryValue* manifest, + const FilePath& unpack_path) = 0; +}; + +// Describes a particular component that can be installed or updated. This +// structure is required to register a component with the component updater. +// Only |name| is optional. |pk_hash| is the SHA256 hash of the component's +// public key. If the component is to be installed then version should be +// "0" or "0.0", else it should be the current version. +struct CrxComponent { + std::vector<uint8> pk_hash; + ComponentInstaller* installer; + Version version; + std::string name; + CrxComponent(); + ~CrxComponent(); +}; + +// The component update service is in charge of installing or upgrading +// select parts of chrome. Each part is called a component and managed by +// instances of CrxComponent registered using RegisterComponent(). On the +// server, each component is packaged as a CRX which is the same format used +// to package extensions. To the update service each component is identified +// by its public key hash (CrxComponent::pk_hash). If there is an update +// available and its version is bigger than (CrxComponent::version), it will +// be downloaded, verified and unpacked. Then component-specific installer +// ComponentInstaller::Install (of CrxComponent::installer) will be called. +// +// During the normal operation of the component updater some specific +// notifications are fired, like COMPONENT_UPDATER_STARTED and +// COMPONENT_UPDATE_FOUND. See notification_type.h for more details. +// +// All methods are safe to call ONLY from chrome's UI thread. +class ComponentUpdateService { + public: + enum Status { + kOk, + kReplaced, + kError + }; + // Controls the component updater behavior. + class Configurator { + public: + virtual ~Configurator() {} + // Delay in seconds from calling Start() to the first update check. + virtual int InitialDelay() = 0; + // Delay in seconds to every subsequent update check. 0 means don't check. + virtual int NextCheckDelay() = 0; + // Delay in seconds from each task step. Used to smooth out CPU/IO usage. + virtual int StepDelay() = 0; + // The url that is going to be used update checks over Omaha protocol. + virtual GURL UpdateUrl() = 0; + // How big each update request can be. Don't go above 2000. + virtual size_t UrlSizeLimit() = 0; + // The source of contexts for all the url requests. + virtual net::URLRequestContextGetter* RequestContext() = 0; + // True means that all ops are peformed in this process. + virtual bool InProcess() = 0; + }; + + // Start doing update checks and installing new versions of registered + // components after Configurator::InitialDelay() seconds. + virtual Status Start() = 0; + + // Stop doing update checks. In-flight requests and pending installations + // will not be cancelled. + virtual Status Stop() = 0; + + // Add component to be checked for updates. You can call this method + // before calling Start(). + virtual Status RegisterComponent(const CrxComponent& component) = 0; + + protected: + virtual ~ComponentUpdateService() {} +}; + +// Creates the component updater. Pass NULL in |config| to use the default +// configuration. Only the first caller can specify a configuration; on +// subsequent calls it will be ignored and the current instance of the +// component updater will be used. +ComponentUpdateService* ComponentUpdateServiceFactory( + ComponentUpdateService::Configurator* config); + +#endif // CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UPDATER_SERVICE_H_ + |