Home | History | Annotate | Download | only in component_updater
      1 // Copyright 2013 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 "chrome/browser/component_updater/component_patcher_operation.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/file_util.h"
     12 #include "base/files/memory_mapped_file.h"
     13 #include "base/json/json_file_value_serializer.h"
     14 #include "base/path_service.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "chrome/browser/component_updater/component_patcher.h"
     17 #include "chrome/browser/component_updater/component_updater_service.h"
     18 #include "chrome/common/chrome_utility_messages.h"
     19 #include "chrome/common/extensions/extension_constants.h"
     20 #include "content/public/browser/browser_thread.h"
     21 #include "content/public/browser/utility_process_host.h"
     22 #include "courgette/courgette.h"
     23 #include "courgette/third_party/bsdiff.h"
     24 #include "crypto/secure_hash.h"
     25 #include "crypto/sha2.h"
     26 #include "crypto/signature_verifier.h"
     27 #include "extensions/common/crx_file.h"
     28 #include "ipc/ipc_message_macros.h"
     29 
     30 using crypto::SecureHash;
     31 
     32 namespace component_updater {
     33 
     34 namespace {
     35 
     36 const char kInput[] = "input";
     37 const char kOp[] = "op";
     38 const char kOutput[] = "output";
     39 const char kPatch[] = "patch";
     40 const char kSha256[] = "sha256";
     41 
     42 // The integer offset disambiguates between overlapping error ranges.
     43 const int kCourgetteErrorOffset = 300;
     44 const int kBsdiffErrorOffset = 600;
     45 
     46 class CourgetteTraits : public DeltaUpdateOpPatchStrategy {
     47  public:
     48   virtual int GetErrorOffset() const OVERRIDE;
     49   virtual int GetSuccessCode() const OVERRIDE;
     50   virtual scoped_ptr<IPC::Message> GetPatchMessage(
     51       base::FilePath input_abs_path,
     52       base::FilePath patch_abs_path,
     53       base::FilePath output_abs_path) OVERRIDE;
     54   virtual int Patch(base::FilePath input_abs_path,
     55                     base::FilePath patch_abs_path,
     56                     base::FilePath output_abs_path) OVERRIDE;
     57 };
     58 
     59 int CourgetteTraits::GetErrorOffset() const {
     60   return kCourgetteErrorOffset;
     61 }
     62 
     63 int CourgetteTraits::GetSuccessCode() const {
     64   return courgette::C_OK;
     65 }
     66 
     67 scoped_ptr<IPC::Message> CourgetteTraits::GetPatchMessage(
     68     base::FilePath input_abs_path,
     69     base::FilePath patch_abs_path,
     70     base::FilePath output_abs_path) {
     71   return scoped_ptr<IPC::Message>(
     72       new ChromeUtilityMsg_PatchFileCourgette(input_abs_path,
     73                                               patch_abs_path,
     74                                               output_abs_path));
     75 }
     76 
     77 int CourgetteTraits::Patch(base::FilePath input_abs_path,
     78                            base::FilePath patch_abs_path,
     79                            base::FilePath output_abs_path) {
     80   return courgette::ApplyEnsemblePatch(input_abs_path.value().c_str(),
     81                                        patch_abs_path.value().c_str(),
     82                                        output_abs_path.value().c_str());
     83 }
     84 
     85 class BsdiffTraits : public DeltaUpdateOpPatchStrategy {
     86  public:
     87   virtual int GetErrorOffset() const OVERRIDE;
     88   virtual int GetSuccessCode() const OVERRIDE;
     89   virtual scoped_ptr<IPC::Message> GetPatchMessage(
     90       base::FilePath input_abs_path,
     91       base::FilePath patch_abs_path,
     92       base::FilePath output_abs_path) OVERRIDE;
     93   virtual int Patch(base::FilePath input_abs_path,
     94                     base::FilePath patch_abs_path,
     95                     base::FilePath output_abs_path) OVERRIDE;
     96 };
     97 
     98 int BsdiffTraits::GetErrorOffset() const {
     99   return kBsdiffErrorOffset;
    100 }
    101 
    102 int BsdiffTraits::GetSuccessCode() const {
    103   return courgette::OK;
    104 }
    105 
    106 scoped_ptr<IPC::Message> BsdiffTraits::GetPatchMessage(
    107     base::FilePath input_abs_path,
    108     base::FilePath patch_abs_path,
    109     base::FilePath output_abs_path) {
    110   return scoped_ptr<IPC::Message>(
    111       new ChromeUtilityMsg_PatchFileBsdiff(input_abs_path,
    112                                            patch_abs_path,
    113                                            output_abs_path));
    114 }
    115 
    116 int BsdiffTraits::Patch(base::FilePath input_abs_path,
    117                         base::FilePath patch_abs_path,
    118                         base::FilePath output_abs_path) {
    119   return courgette::ApplyBinaryPatch(input_abs_path,
    120                                      patch_abs_path,
    121                                      output_abs_path);
    122 }
    123 
    124 }  // namespace
    125 
    126 DeltaUpdateOpPatchStrategy::~DeltaUpdateOpPatchStrategy() {
    127 }
    128 
    129 DeltaUpdateOp* CreateDeltaUpdateOp(const std::string& operation) {
    130   if (operation == "copy") {
    131     return new DeltaUpdateOpCopy();
    132   } else if (operation == "create") {
    133     return new DeltaUpdateOpCreate();
    134   } else if (operation == "bsdiff") {
    135     scoped_ptr<DeltaUpdateOpPatchStrategy> strategy(new BsdiffTraits());
    136     return new DeltaUpdateOpPatch(strategy.Pass());
    137   } else if (operation == "courgette") {
    138     scoped_ptr<DeltaUpdateOpPatchStrategy> strategy(new CourgetteTraits());
    139     return new DeltaUpdateOpPatch(strategy.Pass());
    140   }
    141   return NULL;
    142 }
    143 
    144 DeltaUpdateOp* CreateDeltaUpdateOp(const base::DictionaryValue& command) {
    145   std::string operation;
    146   if (!command.GetString(kOp, &operation))
    147     return NULL;
    148   return CreateDeltaUpdateOp(operation);
    149 }
    150 
    151 DeltaUpdateOp::DeltaUpdateOp() : in_process_(false) {
    152 }
    153 
    154 DeltaUpdateOp::~DeltaUpdateOp() {
    155 }
    156 
    157 void DeltaUpdateOp::Run(const base::DictionaryValue* command_args,
    158                         const base::FilePath& input_dir,
    159                         const base::FilePath& unpack_dir,
    160                         ComponentInstaller* installer,
    161                         bool in_process,
    162                         const ComponentUnpacker::Callback& callback,
    163                         scoped_refptr<base::SequencedTaskRunner> task_runner) {
    164   callback_ = callback;
    165   in_process_ = in_process;
    166   task_runner_ = task_runner;
    167   std::string output_rel_path;
    168   if (!command_args->GetString(kOutput, &output_rel_path) ||
    169       !command_args->GetString(kSha256, &output_sha256_)) {
    170     DoneRunning(ComponentUnpacker::kDeltaBadCommands, 0);
    171     return;
    172   }
    173 
    174   output_abs_path_ =
    175       unpack_dir.Append(base::FilePath::FromUTF8Unsafe(output_rel_path));
    176   ComponentUnpacker::Error parse_result =
    177       DoParseArguments(command_args, input_dir, installer);
    178   if (parse_result != ComponentUnpacker::kNone) {
    179     DoneRunning(parse_result, 0);
    180     return;
    181   }
    182 
    183   const base::FilePath parent = output_abs_path_.DirName();
    184   if (!base::DirectoryExists(parent)) {
    185     if (!base::CreateDirectory(parent)) {
    186       DoneRunning(ComponentUnpacker::kIoError, 0);
    187       return;
    188     }
    189   }
    190 
    191   DoRun(base::Bind(&DeltaUpdateOp::DoneRunning,
    192                    scoped_refptr<DeltaUpdateOp>(this)));
    193 }
    194 
    195 void DeltaUpdateOp::DoneRunning(ComponentUnpacker::Error error,
    196                                 int extended_error) {
    197   if (error == ComponentUnpacker::kNone)
    198     error = CheckHash();
    199   task_runner_->PostTask(FROM_HERE,
    200                          base::Bind(callback_, error, extended_error));
    201   callback_.Reset();
    202 }
    203 
    204 // Uses the hash as a checksum to confirm that the file now residing in the
    205 // output directory probably has the contents it should.
    206 ComponentUnpacker::Error DeltaUpdateOp::CheckHash() {
    207   std::vector<uint8> expected_hash;
    208   if (!base::HexStringToBytes(output_sha256_, &expected_hash) ||
    209       expected_hash.size() != crypto::kSHA256Length)
    210     return ComponentUnpacker::kDeltaVerificationFailure;
    211 
    212   base::MemoryMappedFile output_file_mmapped;
    213   if (!output_file_mmapped.Initialize(output_abs_path_))
    214     return ComponentUnpacker::kDeltaVerificationFailure;
    215 
    216   uint8 actual_hash[crypto::kSHA256Length] = {0};
    217   const scoped_ptr<SecureHash> hasher(SecureHash::Create(SecureHash::SHA256));
    218   hasher->Update(output_file_mmapped.data(), output_file_mmapped.length());
    219   hasher->Finish(actual_hash, sizeof(actual_hash));
    220   if (memcmp(actual_hash, &expected_hash[0], sizeof(actual_hash)))
    221     return ComponentUnpacker::kDeltaVerificationFailure;
    222 
    223   return ComponentUnpacker::kNone;
    224 }
    225 
    226 bool DeltaUpdateOp::InProcess() {
    227   return in_process_;
    228 }
    229 
    230 scoped_refptr<base::SequencedTaskRunner> DeltaUpdateOp::GetTaskRunner() {
    231   return task_runner_;
    232 }
    233 
    234 DeltaUpdateOpCopy::DeltaUpdateOpCopy() {
    235 }
    236 
    237 DeltaUpdateOpCopy::~DeltaUpdateOpCopy() {
    238 }
    239 
    240 ComponentUnpacker::Error DeltaUpdateOpCopy::DoParseArguments(
    241     const base::DictionaryValue* command_args,
    242     const base::FilePath& input_dir,
    243     ComponentInstaller* installer) {
    244   std::string input_rel_path;
    245   if (!command_args->GetString(kInput, &input_rel_path))
    246     return ComponentUnpacker::kDeltaBadCommands;
    247 
    248   if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_))
    249     return ComponentUnpacker::kDeltaMissingExistingFile;
    250 
    251   return ComponentUnpacker::kNone;
    252 }
    253 
    254 void DeltaUpdateOpCopy::DoRun(const ComponentUnpacker::Callback& callback) {
    255   if (!base::CopyFile(input_abs_path_, output_abs_path_))
    256     callback.Run(ComponentUnpacker::kDeltaOperationFailure, 0);
    257   else
    258     callback.Run(ComponentUnpacker::kNone, 0);
    259 }
    260 
    261 DeltaUpdateOpCreate::DeltaUpdateOpCreate() {
    262 }
    263 
    264 DeltaUpdateOpCreate::~DeltaUpdateOpCreate() {
    265 }
    266 
    267 ComponentUnpacker::Error DeltaUpdateOpCreate::DoParseArguments(
    268     const base::DictionaryValue* command_args,
    269     const base::FilePath& input_dir,
    270     ComponentInstaller* installer) {
    271   std::string patch_rel_path;
    272   if (!command_args->GetString(kPatch, &patch_rel_path))
    273     return ComponentUnpacker::kDeltaBadCommands;
    274 
    275   patch_abs_path_ =
    276       input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path));
    277 
    278   return ComponentUnpacker::kNone;
    279 }
    280 
    281 void DeltaUpdateOpCreate::DoRun(const ComponentUnpacker::Callback& callback) {
    282   if (!base::Move(patch_abs_path_, output_abs_path_))
    283     callback.Run(ComponentUnpacker::kDeltaOperationFailure, 0);
    284   else
    285     callback.Run(ComponentUnpacker::kNone, 0);
    286 }
    287 
    288 DeltaUpdateOpPatchHost::DeltaUpdateOpPatchHost(
    289     scoped_refptr<DeltaUpdateOpPatch> patcher,
    290     scoped_refptr<base::SequencedTaskRunner> task_runner)
    291     : patcher_(patcher), task_runner_(task_runner) {
    292 }
    293 
    294 DeltaUpdateOpPatchHost::~DeltaUpdateOpPatchHost() {
    295 }
    296 
    297 void DeltaUpdateOpPatchHost::StartProcess(scoped_ptr<IPC::Message> message) {
    298   // The DeltaUpdateOpPatchHost is not responsible for deleting the
    299   // UtilityProcessHost object.
    300   content::UtilityProcessHost* host = content::UtilityProcessHost::Create(
    301       this, base::MessageLoopProxy::current().get());
    302   host->DisableSandbox();
    303   host->Send(message.release());
    304 }
    305 
    306 bool DeltaUpdateOpPatchHost::OnMessageReceived(const IPC::Message& message) {
    307   bool handled = true;
    308   IPC_BEGIN_MESSAGE_MAP(DeltaUpdateOpPatchHost, message)
    309     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_PatchFile_Succeeded,
    310                         OnPatchSucceeded)
    311     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_PatchFile_Failed,
    312                         OnPatchFailed)
    313     IPC_MESSAGE_UNHANDLED(handled = false)
    314   IPC_END_MESSAGE_MAP()
    315   return handled;
    316 }
    317 
    318 void DeltaUpdateOpPatchHost::OnPatchSucceeded() {
    319   task_runner_->PostTask(FROM_HERE,
    320                          base::Bind(&DeltaUpdateOpPatch::DonePatching,
    321                                     patcher_,
    322                                     ComponentUnpacker::kNone,
    323                                     0));
    324   task_runner_ = NULL;
    325   patcher_ = NULL;
    326 }
    327 
    328 void DeltaUpdateOpPatchHost::OnPatchFailed(int error_code) {
    329   task_runner_->PostTask(FROM_HERE,
    330                          base::Bind(&DeltaUpdateOpPatch::DonePatching,
    331                                     patcher_,
    332                                     ComponentUnpacker::kDeltaOperationFailure,
    333                                     error_code));
    334   task_runner_ = NULL;
    335   patcher_ = NULL;
    336 }
    337 
    338 void DeltaUpdateOpPatchHost::OnProcessCrashed(int exit_code) {
    339   task_runner_->PostTask(
    340       FROM_HERE,
    341       base::Bind(&DeltaUpdateOpPatch::DonePatching,
    342                  patcher_,
    343                  ComponentUnpacker::kDeltaPatchProcessFailure,
    344                  exit_code));
    345   task_runner_ = NULL;
    346   patcher_ = NULL;
    347 }
    348 
    349 DeltaUpdateOpPatch::DeltaUpdateOpPatch(
    350     scoped_ptr<DeltaUpdateOpPatchStrategy> strategy) {
    351   strategy_ = strategy.Pass();
    352 }
    353 
    354 DeltaUpdateOpPatch::~DeltaUpdateOpPatch() {
    355 }
    356 
    357 ComponentUnpacker::Error DeltaUpdateOpPatch::DoParseArguments(
    358     const base::DictionaryValue* command_args,
    359     const base::FilePath& input_dir,
    360     ComponentInstaller* installer) {
    361   std::string patch_rel_path;
    362   std::string input_rel_path;
    363   if (!command_args->GetString(kPatch, &patch_rel_path) ||
    364       !command_args->GetString(kInput, &input_rel_path))
    365     return ComponentUnpacker::kDeltaBadCommands;
    366 
    367   if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_))
    368     return ComponentUnpacker::kDeltaMissingExistingFile;
    369 
    370   patch_abs_path_ =
    371       input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path));
    372 
    373   return ComponentUnpacker::kNone;
    374 }
    375 
    376 void DeltaUpdateOpPatch::DoRun(const ComponentUnpacker::Callback& callback) {
    377   callback_ = callback;
    378   if (!InProcess()) {
    379     host_ = new DeltaUpdateOpPatchHost(scoped_refptr<DeltaUpdateOpPatch>(this),
    380                                        GetTaskRunner());
    381     content::BrowserThread::PostTask(
    382         content::BrowserThread::IO,
    383         FROM_HERE,
    384         base::Bind(&DeltaUpdateOpPatchHost::StartProcess,
    385                    host_,
    386                    base::Passed(strategy_->GetPatchMessage(input_abs_path_,
    387                                                            patch_abs_path_,
    388                                                            output_abs_path_))));
    389     return;
    390   }
    391   const int result = strategy_->Patch(input_abs_path_,
    392                                       patch_abs_path_,
    393                                       output_abs_path_);
    394   if (result == strategy_->GetSuccessCode())
    395     DonePatching(ComponentUnpacker::kNone, 0);
    396   else
    397     DonePatching(ComponentUnpacker::kDeltaOperationFailure, result);
    398 }
    399 
    400 void DeltaUpdateOpPatch::DonePatching(ComponentUnpacker::Error error,
    401                                       int error_code) {
    402   host_ = NULL;
    403   if (error != ComponentUnpacker::kNone) {
    404     error_code += strategy_->GetErrorOffset();
    405   }
    406   callback_.Run(error, error_code);
    407   // The callback is no longer needed - it is best to release it in case it
    408   // contains a reference to this object.
    409   callback_.Reset();
    410 }
    411 
    412 }  // namespace component_updater
    413