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/file_util.h" 11#include "base/json/json_file_value_serializer.h" 12#include "base/logging.h" 13#include "base/memory/scoped_handle.h" 14#include "base/strings/string_number_conversions.h" 15#include "base/strings/stringprintf.h" 16#include "base/values.h" 17#include "chrome/browser/component_updater/component_patcher.h" 18#include "chrome/browser/component_updater/component_updater_service.h" 19#include "chrome/common/extensions/extension_constants.h" 20#include "crypto/secure_hash.h" 21#include "crypto/signature_verifier.h" 22#include "extensions/common/crx_file.h" 23#include "third_party/zlib/google/zip.h" 24 25using crypto::SecureHash; 26 27namespace { 28 29// This class makes sure that the CRX digital signature is valid 30// and well formed. 31class CRXValidator { 32 public: 33 explicit CRXValidator(FILE* crx_file) : valid_(false), delta_(false) { 34 extensions::CrxFile::Header header; 35 size_t len = fread(&header, 1, sizeof(header), crx_file); 36 if (len < sizeof(header)) 37 return; 38 39 extensions::CrxFile::Error error; 40 scoped_ptr<extensions::CrxFile> crx( 41 extensions::CrxFile::Parse(header, &error)); 42 if (!crx.get()) 43 return; 44 delta_ = extensions::CrxFile::HeaderIsDelta(header); 45 46 std::vector<uint8> key(header.key_size); 47 len = fread(&key[0], sizeof(uint8), header.key_size, crx_file); 48 if (len < header.key_size) 49 return; 50 51 std::vector<uint8> signature(header.signature_size); 52 len = fread(&signature[0], sizeof(uint8), header.signature_size, crx_file); 53 if (len < header.signature_size) 54 return; 55 56 crypto::SignatureVerifier verifier; 57 if (!verifier.VerifyInit(extension_misc::kSignatureAlgorithm, 58 sizeof(extension_misc::kSignatureAlgorithm), 59 &signature[0], signature.size(), 60 &key[0], key.size())) { 61 // Signature verification initialization failed. This is most likely 62 // caused by a public key in the wrong format (should encode algorithm). 63 return; 64 } 65 66 const size_t kBufSize = 8 * 1024; 67 scoped_ptr<uint8[]> buf(new uint8[kBufSize]); 68 while ((len = fread(buf.get(), 1, kBufSize, crx_file)) > 0) 69 verifier.VerifyUpdate(buf.get(), len); 70 71 if (!verifier.VerifyFinal()) 72 return; 73 74 public_key_.swap(key); 75 valid_ = true; 76 } 77 78 bool valid() const { return valid_; } 79 80 bool delta() const { return delta_; } 81 82 const std::vector<uint8>& public_key() const { return public_key_; } 83 84 private: 85 bool valid_; 86 bool delta_; 87 std::vector<uint8> public_key_; 88}; 89 90} // namespace. 91 92// TODO(cpu): add a specific attribute check to a component json that the 93// extension unpacker will reject, so that a component cannot be installed 94// as an extension. 95scoped_ptr<base::DictionaryValue> ReadManifest( 96 const base::FilePath& unpack_path) { 97 base::FilePath manifest = 98 unpack_path.Append(FILE_PATH_LITERAL("manifest.json")); 99 if (!base::PathExists(manifest)) 100 return scoped_ptr<base::DictionaryValue>(); 101 JSONFileValueSerializer serializer(manifest); 102 std::string error; 103 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); 104 if (!root.get()) 105 return scoped_ptr<base::DictionaryValue>(); 106 if (!root->IsType(base::Value::TYPE_DICTIONARY)) 107 return scoped_ptr<base::DictionaryValue>(); 108 return scoped_ptr<base::DictionaryValue>( 109 static_cast<base::DictionaryValue*>(root.release())).Pass(); 110} 111 112ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash, 113 const base::FilePath& path, 114 const std::string& fingerprint, 115 ComponentPatcher* patcher, 116 ComponentInstaller* installer) 117 : error_(kNone), 118 extended_error_(0) { 119 if (pk_hash.empty() || path.empty()) { 120 error_ = kInvalidParams; 121 return; 122 } 123 // First, validate the CRX header and signature. As of today 124 // this is SHA1 with RSA 1024. 125 ScopedStdioHandle file(base::OpenFile(path, "rb")); 126 if (!file.get()) { 127 error_ = kInvalidFile; 128 return; 129 } 130 CRXValidator validator(file.get()); 131 if (!validator.valid()) { 132 error_ = kInvalidFile; 133 return; 134 } 135 file.Close(); 136 137 // File is valid and the digital signature matches. Now make sure 138 // the public key hash matches the expected hash. If they do we fully 139 // trust this CRX. 140 uint8 hash[32]; 141 scoped_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256)); 142 sha256->Update(&(validator.public_key()[0]), validator.public_key().size()); 143 sha256->Finish(hash, arraysize(hash)); 144 145 if (!std::equal(pk_hash.begin(), pk_hash.end(), hash)) { 146 error_ = kInvalidId; 147 return; 148 } 149 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), 150 &unpack_path_)) { 151 error_ = kUnzipPathError; 152 return; 153 } 154 if (validator.delta()) { // Package is a diff package. 155 // We want a different temp directory for the delta files; we'll put the 156 // patch output into unpack_path_. 157 base::FilePath unpack_diff_path; 158 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), 159 &unpack_diff_path)) { 160 error_ = kUnzipPathError; 161 return; 162 } 163 if (!zip::Unzip(path, unpack_diff_path)) { 164 error_ = kUnzipFailed; 165 return; 166 } 167 ComponentUnpacker::Error result = DifferentialUpdatePatch(unpack_diff_path, 168 unpack_path_, 169 patcher, 170 installer, 171 &extended_error_); 172 base::DeleteFile(unpack_diff_path, true); 173 unpack_diff_path.clear(); 174 error_ = result; 175 if (error_ != kNone) { 176 return; 177 } 178 } else { 179 // Package is a normal update/install; unzip it into unpack_path_ directly. 180 if (!zip::Unzip(path, unpack_path_)) { 181 error_ = kUnzipFailed; 182 return; 183 } 184 } 185 scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_)); 186 if (!manifest.get()) { 187 error_ = kBadManifest; 188 return; 189 } 190 // Write the fingerprint to disk. 191 if (static_cast<int>(fingerprint.size()) != 192 file_util::WriteFile( 193 unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")), 194 fingerprint.c_str(), 195 fingerprint.size())) { 196 error_ = kFingerprintWriteFailed; 197 return; 198 } 199 if (!installer->Install(*manifest, unpack_path_)) { 200 error_ = kInstallerError; 201 return; 202 } 203 // Installation successful. The directory is not our concern now. 204 unpack_path_.clear(); 205} 206 207ComponentUnpacker::~ComponentUnpacker() { 208 if (!unpack_path_.empty()) 209 base::DeleteFile(unpack_path_, true); 210} 211