Home | History | Annotate | Download | only in component_updater
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/component_updater/component_unpacker.h"
      6 
      7 #include <stdint.h>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/bind.h"
     12 #include "base/files/file_path.h"
     13 #include "base/files/file_util.h"
     14 #include "base/files/scoped_file.h"
     15 #include "base/json/json_file_value_serializer.h"
     16 #include "base/location.h"
     17 #include "base/logging.h"
     18 #include "base/numerics/safe_conversions.h"
     19 #include "base/strings/string_number_conversions.h"
     20 #include "base/strings/stringprintf.h"
     21 #include "base/values.h"
     22 #include "components/component_updater/component_patcher.h"
     23 #include "components/component_updater/component_patcher_operation.h"
     24 #include "components/component_updater/component_updater_service.h"
     25 #include "components/crx_file/constants.h"
     26 #include "components/crx_file/crx_file.h"
     27 #include "crypto/secure_hash.h"
     28 #include "crypto/signature_verifier.h"
     29 #include "third_party/zlib/google/zip.h"
     30 
     31 using crypto::SecureHash;
     32 
     33 namespace component_updater {
     34 
     35 namespace {
     36 
     37 // This class makes sure that the CRX digital signature is valid
     38 // and well formed.
     39 class CRXValidator {
     40  public:
     41   explicit CRXValidator(FILE* crx_file) : valid_(false), is_delta_(false) {
     42     crx_file::CrxFile::Header header;
     43     size_t len = fread(&header, 1, sizeof(header), crx_file);
     44     if (len < sizeof(header))
     45       return;
     46 
     47     crx_file::CrxFile::Error error;
     48     scoped_ptr<crx_file::CrxFile> crx(
     49         crx_file::CrxFile::Parse(header, &error));
     50     if (!crx.get())
     51       return;
     52     is_delta_ = crx_file::CrxFile::HeaderIsDelta(header);
     53 
     54     std::vector<uint8_t> key(header.key_size);
     55     len = fread(&key[0], sizeof(uint8_t), header.key_size, crx_file);
     56     if (len < header.key_size)
     57       return;
     58 
     59     std::vector<uint8_t> signature(header.signature_size);
     60     len =
     61         fread(&signature[0], sizeof(uint8_t), header.signature_size, crx_file);
     62     if (len < header.signature_size)
     63       return;
     64 
     65     crypto::SignatureVerifier verifier;
     66     if (!verifier.VerifyInit(crx_file::kSignatureAlgorithm,
     67                              base::checked_cast<int>(
     68                                  sizeof(crx_file::kSignatureAlgorithm)),
     69                              &signature[0],
     70                              base::checked_cast<int>(signature.size()),
     71                              &key[0],
     72                              base::checked_cast<int>(key.size()))) {
     73       // Signature verification initialization failed. This is most likely
     74       // caused by a public key in the wrong format (should encode algorithm).
     75       return;
     76     }
     77 
     78     const size_t kBufSize = 8 * 1024;
     79     scoped_ptr<uint8_t[]> buf(new uint8_t[kBufSize]);
     80     while ((len = fread(buf.get(), 1, kBufSize, crx_file)) > 0)
     81       verifier.VerifyUpdate(buf.get(), base::checked_cast<int>(len));
     82 
     83     if (!verifier.VerifyFinal())
     84       return;
     85 
     86     public_key_.swap(key);
     87     valid_ = true;
     88   }
     89 
     90   bool valid() const { return valid_; }
     91 
     92   bool is_delta() const { return is_delta_; }
     93 
     94   const std::vector<uint8_t>& public_key() const { return public_key_; }
     95 
     96  private:
     97   bool valid_;
     98   bool is_delta_;
     99   std::vector<uint8_t> public_key_;
    100 };
    101 
    102 }  // namespace
    103 
    104 ComponentUnpacker::ComponentUnpacker(
    105     const std::vector<uint8_t>& pk_hash,
    106     const base::FilePath& path,
    107     const std::string& fingerprint,
    108     ComponentInstaller* installer,
    109     scoped_refptr<OutOfProcessPatcher> out_of_process_patcher,
    110     scoped_refptr<base::SequencedTaskRunner> task_runner)
    111     : pk_hash_(pk_hash),
    112       path_(path),
    113       is_delta_(false),
    114       fingerprint_(fingerprint),
    115       installer_(installer),
    116       out_of_process_patcher_(out_of_process_patcher),
    117       error_(kNone),
    118       extended_error_(0),
    119       task_runner_(task_runner) {
    120 }
    121 
    122 // TODO(cpu): add a specific attribute check to a component json that the
    123 // extension unpacker will reject, so that a component cannot be installed
    124 // as an extension.
    125 scoped_ptr<base::DictionaryValue> ReadManifest(
    126     const base::FilePath& unpack_path) {
    127   base::FilePath manifest =
    128       unpack_path.Append(FILE_PATH_LITERAL("manifest.json"));
    129   if (!base::PathExists(manifest))
    130     return scoped_ptr<base::DictionaryValue>();
    131   JSONFileValueSerializer serializer(manifest);
    132   std::string error;
    133   scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error));
    134   if (!root.get())
    135     return scoped_ptr<base::DictionaryValue>();
    136   if (!root->IsType(base::Value::TYPE_DICTIONARY))
    137     return scoped_ptr<base::DictionaryValue>();
    138   return scoped_ptr<base::DictionaryValue>(
    139              static_cast<base::DictionaryValue*>(root.release())).Pass();
    140 }
    141 
    142 bool ComponentUnpacker::UnpackInternal() {
    143   return Verify() && Unzip() && BeginPatching();
    144 }
    145 
    146 void ComponentUnpacker::Unpack(const Callback& callback) {
    147   callback_ = callback;
    148   if (!UnpackInternal())
    149     Finish();
    150 }
    151 
    152 bool ComponentUnpacker::Verify() {
    153   VLOG(1) << "Verifying component: " << path_.value();
    154   if (pk_hash_.empty() || path_.empty()) {
    155     error_ = kInvalidParams;
    156     return false;
    157   }
    158   // First, validate the CRX header and signature. As of today
    159   // this is SHA1 with RSA 1024.
    160   base::ScopedFILE file(base::OpenFile(path_, "rb"));
    161   if (!file.get()) {
    162     error_ = kInvalidFile;
    163     return false;
    164   }
    165   CRXValidator validator(file.get());
    166   file.reset();
    167   if (!validator.valid()) {
    168     error_ = kInvalidFile;
    169     return false;
    170   }
    171   is_delta_ = validator.is_delta();
    172 
    173   // File is valid and the digital signature matches. Now make sure
    174   // the public key hash matches the expected hash. If they do we fully
    175   // trust this CRX.
    176   uint8_t hash[32] = {};
    177   scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256));
    178   sha256->Update(&(validator.public_key()[0]), validator.public_key().size());
    179   sha256->Finish(hash, arraysize(hash));
    180 
    181   if (!std::equal(pk_hash_.begin(), pk_hash_.end(), hash)) {
    182     VLOG(1) << "Hash mismatch: " << path_.value();
    183     error_ = kInvalidId;
    184     return false;
    185   }
    186   VLOG(1) << "Verification successful: " << path_.value();
    187   return true;
    188 }
    189 
    190 bool ComponentUnpacker::Unzip() {
    191   base::FilePath& destination = is_delta_ ? unpack_diff_path_ : unpack_path_;
    192   VLOG(1) << "Unpacking in: " << destination.value();
    193   if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
    194                                     &destination)) {
    195     VLOG(1) << "Unable to create temporary directory for unpacking.";
    196     error_ = kUnzipPathError;
    197     return false;
    198   }
    199   if (!zip::Unzip(path_, destination)) {
    200     VLOG(1) << "Unzipping failed.";
    201     error_ = kUnzipFailed;
    202     return false;
    203   }
    204   VLOG(1) << "Unpacked successfully";
    205   return true;
    206 }
    207 
    208 bool ComponentUnpacker::BeginPatching() {
    209   if (is_delta_) {  // Package is a diff package.
    210     // Use a different temp directory for the patch output files.
    211     if (!base::CreateNewTempDirectory(base::FilePath::StringType(),
    212                                       &unpack_path_)) {
    213       error_ = kUnzipPathError;
    214       return false;
    215     }
    216     patcher_ = new ComponentPatcher(unpack_diff_path_,
    217                                     unpack_path_,
    218                                     installer_,
    219                                     out_of_process_patcher_,
    220                                     task_runner_);
    221     task_runner_->PostTask(
    222         FROM_HERE,
    223         base::Bind(&ComponentPatcher::Start,
    224                    patcher_,
    225                    base::Bind(&ComponentUnpacker::EndPatching,
    226                               scoped_refptr<ComponentUnpacker>(this))));
    227   } else {
    228     task_runner_->PostTask(FROM_HERE,
    229                            base::Bind(&ComponentUnpacker::EndPatching,
    230                                       scoped_refptr<ComponentUnpacker>(this),
    231                                       kNone,
    232                                       0));
    233   }
    234   return true;
    235 }
    236 
    237 void ComponentUnpacker::EndPatching(Error error, int extended_error) {
    238   error_ = error;
    239   extended_error_ = extended_error;
    240   patcher_ = NULL;
    241   if (error_ != kNone) {
    242     Finish();
    243     return;
    244   }
    245   // Optimization: clean up patch files early, in case disk space is too low to
    246   // install otherwise.
    247   if (!unpack_diff_path_.empty()) {
    248     base::DeleteFile(unpack_diff_path_, true);
    249     unpack_diff_path_.clear();
    250   }
    251   Install();
    252   Finish();
    253 }
    254 
    255 void ComponentUnpacker::Install() {
    256   // Write the fingerprint to disk.
    257   if (static_cast<int>(fingerprint_.size()) !=
    258       base::WriteFile(
    259           unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
    260           fingerprint_.c_str(),
    261           base::checked_cast<int>(fingerprint_.size()))) {
    262     error_ = kFingerprintWriteFailed;
    263     return;
    264   }
    265   scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_));
    266   if (!manifest.get()) {
    267     error_ = kBadManifest;
    268     return;
    269   }
    270   DCHECK(error_ == kNone);
    271   if (!installer_->Install(*manifest, unpack_path_)) {
    272     error_ = kInstallerError;
    273     return;
    274   }
    275 }
    276 
    277 void ComponentUnpacker::Finish() {
    278   if (!unpack_diff_path_.empty())
    279     base::DeleteFile(unpack_diff_path_, true);
    280   if (!unpack_path_.empty())
    281     base::DeleteFile(unpack_path_, true);
    282   callback_.Run(error_, extended_error_);
    283 }
    284 
    285 ComponentUnpacker::~ComponentUnpacker() {
    286 }
    287 
    288 }  // namespace component_updater
    289