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/extensions/extension_creator.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/bind.h" 11 #include "base/callback.h" 12 #include "base/files/file_util.h" 13 #include "base/files/scoped_file.h" 14 #include "base/files/scoped_temp_dir.h" 15 #include "base/strings/string_util.h" 16 #include "chrome/browser/extensions/extension_creator_filter.h" 17 #include "chrome/grit/generated_resources.h" 18 #include "components/crx_file/crx_file.h" 19 #include "components/crx_file/id_util.h" 20 #include "crypto/rsa_private_key.h" 21 #include "crypto/signature_creator.h" 22 #include "extensions/common/extension.h" 23 #include "extensions/common/file_util.h" 24 #include "third_party/zlib/google/zip.h" 25 #include "ui/base/l10n/l10n_util.h" 26 27 namespace { 28 const int kRSAKeySize = 2048; 29 }; 30 31 namespace extensions { 32 33 ExtensionCreator::ExtensionCreator() : error_type_(kOtherError) { 34 } 35 36 bool ExtensionCreator::InitializeInput( 37 const base::FilePath& extension_dir, 38 const base::FilePath& crx_path, 39 const base::FilePath& private_key_path, 40 const base::FilePath& private_key_output_path, 41 int run_flags) { 42 // Validate input |extension_dir|. 43 if (extension_dir.value().empty() || 44 !base::DirectoryExists(extension_dir)) { 45 error_message_ = 46 l10n_util::GetStringUTF8(IDS_EXTENSION_DIRECTORY_NO_EXISTS); 47 return false; 48 } 49 50 base::FilePath absolute_extension_dir = 51 base::MakeAbsoluteFilePath(extension_dir); 52 if (absolute_extension_dir.empty()) { 53 error_message_ = 54 l10n_util::GetStringUTF8(IDS_EXTENSION_CANT_GET_ABSOLUTE_PATH); 55 return false; 56 } 57 58 // Validate input |private_key| (if provided). 59 if (!private_key_path.value().empty() && 60 !base::PathExists(private_key_path)) { 61 error_message_ = 62 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_INVALID_PATH); 63 return false; 64 } 65 66 // If an |output_private_key| path is given, make sure it doesn't over-write 67 // an existing private key. 68 if (private_key_path.value().empty() && 69 !private_key_output_path.value().empty() && 70 base::PathExists(private_key_output_path)) { 71 error_message_ = 72 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_EXISTS); 73 return false; 74 } 75 76 // Check whether crx file already exists. Should be last check, as this is 77 // a warning only. 78 if (!(run_flags & kOverwriteCRX) && base::PathExists(crx_path)) { 79 error_message_ = l10n_util::GetStringUTF8(IDS_EXTENSION_CRX_EXISTS); 80 error_type_ = kCRXExists; 81 82 return false; 83 } 84 85 return true; 86 } 87 88 bool ExtensionCreator::ValidateManifest(const base::FilePath& extension_dir, 89 crypto::RSAPrivateKey* key_pair, 90 int run_flags) { 91 std::vector<uint8> public_key_bytes; 92 if (!key_pair->ExportPublicKey(&public_key_bytes)) { 93 error_message_ = 94 l10n_util::GetStringUTF8(IDS_EXTENSION_PUBLIC_KEY_FAILED_TO_EXPORT); 95 return false; 96 } 97 98 std::string public_key; 99 public_key.insert(public_key.begin(), 100 public_key_bytes.begin(), public_key_bytes.end()); 101 102 std::string extension_id = crx_file::id_util::GenerateId(public_key); 103 104 // Load the extension once. We don't really need it, but this does a lot of 105 // useful validation of the structure. 106 int create_flags = 107 Extension::FOLLOW_SYMLINKS_ANYWHERE | Extension::ERROR_ON_PRIVATE_KEY; 108 if (run_flags & kRequireModernManifestVersion) 109 create_flags |= Extension::REQUIRE_MODERN_MANIFEST_VERSION; 110 111 scoped_refptr<Extension> extension( 112 file_util::LoadExtension(extension_dir, 113 extension_id, 114 Manifest::INTERNAL, 115 create_flags, 116 &error_message_)); 117 return !!extension.get(); 118 } 119 120 crypto::RSAPrivateKey* ExtensionCreator::ReadInputKey(const base::FilePath& 121 private_key_path) { 122 if (!base::PathExists(private_key_path)) { 123 error_message_ = 124 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_NO_EXISTS); 125 return NULL; 126 } 127 128 std::string private_key_contents; 129 if (!base::ReadFileToString(private_key_path, &private_key_contents)) { 130 error_message_ = 131 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_READ); 132 return NULL; 133 } 134 135 std::string private_key_bytes; 136 if (!Extension::ParsePEMKeyBytes(private_key_contents, 137 &private_key_bytes)) { 138 error_message_ = 139 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_INVALID); 140 return NULL; 141 } 142 143 return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo( 144 std::vector<uint8>(private_key_bytes.begin(), private_key_bytes.end())); 145 } 146 147 crypto::RSAPrivateKey* ExtensionCreator::GenerateKey(const base::FilePath& 148 output_private_key_path) { 149 scoped_ptr<crypto::RSAPrivateKey> key_pair( 150 crypto::RSAPrivateKey::Create(kRSAKeySize)); 151 if (!key_pair) { 152 error_message_ = 153 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_GENERATE); 154 return NULL; 155 } 156 157 std::vector<uint8> private_key_vector; 158 if (!key_pair->ExportPrivateKey(&private_key_vector)) { 159 error_message_ = 160 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_EXPORT); 161 return NULL; 162 } 163 std::string private_key_bytes( 164 reinterpret_cast<char*>(&private_key_vector.front()), 165 private_key_vector.size()); 166 167 std::string private_key; 168 if (!Extension::ProducePEM(private_key_bytes, &private_key)) { 169 error_message_ = 170 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT); 171 return NULL; 172 } 173 std::string pem_output; 174 if (!Extension::FormatPEMForFileOutput(private_key, &pem_output, 175 false)) { 176 error_message_ = 177 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT); 178 return NULL; 179 } 180 181 if (!output_private_key_path.empty()) { 182 if (-1 == base::WriteFile(output_private_key_path, 183 pem_output.c_str(), pem_output.size())) { 184 error_message_ = 185 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT); 186 return NULL; 187 } 188 } 189 190 return key_pair.release(); 191 } 192 193 bool ExtensionCreator::CreateZip(const base::FilePath& extension_dir, 194 const base::FilePath& temp_path, 195 base::FilePath* zip_path) { 196 *zip_path = temp_path.Append(FILE_PATH_LITERAL("extension.zip")); 197 198 scoped_refptr<ExtensionCreatorFilter> filter = new ExtensionCreatorFilter(); 199 const base::Callback<bool(const base::FilePath&)>& filter_cb = 200 base::Bind(&ExtensionCreatorFilter::ShouldPackageFile, filter.get()); 201 if (!zip::ZipWithFilterCallback(extension_dir, *zip_path, filter_cb)) { 202 error_message_ = 203 l10n_util::GetStringUTF8(IDS_EXTENSION_FAILED_DURING_PACKAGING); 204 return false; 205 } 206 207 return true; 208 } 209 210 bool ExtensionCreator::SignZip(const base::FilePath& zip_path, 211 crypto::RSAPrivateKey* private_key, 212 std::vector<uint8>* signature) { 213 scoped_ptr<crypto::SignatureCreator> signature_creator( 214 crypto::SignatureCreator::Create(private_key, 215 crypto::SignatureCreator::SHA1)); 216 base::ScopedFILE zip_handle(base::OpenFile(zip_path, "rb")); 217 size_t buffer_size = 1 << 16; 218 scoped_ptr<uint8[]> buffer(new uint8[buffer_size]); 219 int bytes_read = -1; 220 while ((bytes_read = fread(buffer.get(), 1, buffer_size, 221 zip_handle.get())) > 0) { 222 if (!signature_creator->Update(buffer.get(), bytes_read)) { 223 error_message_ = 224 l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING); 225 return false; 226 } 227 } 228 zip_handle.reset(); 229 230 if (!signature_creator->Final(signature)) { 231 error_message_ = 232 l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING); 233 return false; 234 } 235 return true; 236 } 237 238 bool ExtensionCreator::WriteCRX(const base::FilePath& zip_path, 239 crypto::RSAPrivateKey* private_key, 240 const std::vector<uint8>& signature, 241 const base::FilePath& crx_path) { 242 if (base::PathExists(crx_path)) 243 base::DeleteFile(crx_path, false); 244 base::ScopedFILE crx_handle(base::OpenFile(crx_path, "wb")); 245 if (!crx_handle.get()) { 246 error_message_ = l10n_util::GetStringUTF8(IDS_EXTENSION_SHARING_VIOLATION); 247 return false; 248 } 249 250 std::vector<uint8> public_key; 251 CHECK(private_key->ExportPublicKey(&public_key)); 252 253 crx_file::CrxFile::Error error; 254 scoped_ptr<crx_file::CrxFile> crx( 255 crx_file::CrxFile::Create(public_key.size(), signature.size(), &error)); 256 if (!crx) { 257 LOG(ERROR) << "cannot create CrxFileHeader: " << error; 258 } 259 const crx_file::CrxFile::Header header = crx->header(); 260 261 if (fwrite(&header, sizeof(header), 1, crx_handle.get()) != 1) { 262 PLOG(ERROR) << "fwrite failed to write header"; 263 } 264 if (fwrite(&public_key.front(), sizeof(uint8), public_key.size(), 265 crx_handle.get()) != public_key.size()) { 266 PLOG(ERROR) << "fwrite failed to write public_key.front"; 267 } 268 if (fwrite(&signature.front(), sizeof(uint8), signature.size(), 269 crx_handle.get()) != signature.size()) { 270 PLOG(ERROR) << "fwrite failed to write signature.front"; 271 } 272 273 size_t buffer_size = 1 << 16; 274 scoped_ptr<uint8[]> buffer(new uint8[buffer_size]); 275 size_t bytes_read = 0; 276 base::ScopedFILE zip_handle(base::OpenFile(zip_path, "rb")); 277 while ((bytes_read = fread(buffer.get(), 1, buffer_size, 278 zip_handle.get())) > 0) { 279 if (fwrite(buffer.get(), sizeof(char), bytes_read, crx_handle.get()) != 280 bytes_read) { 281 PLOG(ERROR) << "fwrite failed to write buffer"; 282 } 283 } 284 285 return true; 286 } 287 288 bool ExtensionCreator::Run(const base::FilePath& extension_dir, 289 const base::FilePath& crx_path, 290 const base::FilePath& private_key_path, 291 const base::FilePath& output_private_key_path, 292 int run_flags) { 293 // Check input diretory and read manifest. 294 if (!InitializeInput(extension_dir, crx_path, private_key_path, 295 output_private_key_path, run_flags)) { 296 return false; 297 } 298 299 // Initialize Key Pair 300 scoped_ptr<crypto::RSAPrivateKey> key_pair; 301 if (!private_key_path.value().empty()) 302 key_pair.reset(ReadInputKey(private_key_path)); 303 else 304 key_pair.reset(GenerateKey(output_private_key_path)); 305 if (!key_pair) 306 return false; 307 308 // Perform some extra validation by loading the extension. 309 // TODO(aa): Can this go before creating the key pair? This would mean not 310 // passing ID into LoadExtension which seems OK. 311 if (!ValidateManifest(extension_dir, key_pair.get(), run_flags)) 312 return false; 313 314 base::ScopedTempDir temp_dir; 315 if (!temp_dir.CreateUniqueTempDir()) 316 return false; 317 318 // Zip up the extension. 319 base::FilePath zip_path; 320 std::vector<uint8> signature; 321 bool result = false; 322 if (CreateZip(extension_dir, temp_dir.path(), &zip_path) && 323 SignZip(zip_path, key_pair.get(), &signature) && 324 WriteCRX(zip_path, key_pair.get(), signature, crx_path)) { 325 result = true; 326 } 327 328 base::DeleteFile(zip_path, false); 329 return result; 330 } 331 332 } // namespace extensions 333