summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorcpu@chromium.org <cpu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-22 02:55:04 +0000
committercpu@chromium.org <cpu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-22 02:55:04 +0000
commit2e114e73ddef537fe075d84b1a580f459b50e8a4 (patch)
tree9ccea58b222d25a1b79023a6225620385399db14 /chrome/browser
parenta5a583a1e097d5e3d28cf756403c6e0a8bf657e0 (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/component_updater/component_unpacker.cc215
-rw-r--r--chrome/browser/component_updater/component_unpacker.h59
-rw-r--r--chrome/browser/component_updater/component_updater_service.h125
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_
+