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