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