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/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 "crypto/rsa_private_key.h" 18 #include "crypto/signature_creator.h" 19 #include "extensions/common/crx_file.h" 20 #include "extensions/common/extension.h" 21 #include "extensions/common/file_util.h" 22 #include "extensions/common/id_util.h" 23 #include "grit/generated_resources.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 = 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 base::ScopedFILE zip_handle(base::OpenFile(zip_path, "rb")); 216 size_t buffer_size = 1 << 16; 217 scoped_ptr<uint8[]> buffer(new uint8[buffer_size]); 218 int bytes_read = -1; 219 while ((bytes_read = fread(buffer.get(), 1, buffer_size, 220 zip_handle.get())) > 0) { 221 if (!signature_creator->Update(buffer.get(), bytes_read)) { 222 error_message_ = 223 l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING); 224 return false; 225 } 226 } 227 zip_handle.reset(); 228 229 if (!signature_creator->Final(signature)) { 230 error_message_ = 231 l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING); 232 return false; 233 } 234 return true; 235 } 236 237 bool ExtensionCreator::WriteCRX(const base::FilePath& zip_path, 238 crypto::RSAPrivateKey* private_key, 239 const std::vector<uint8>& signature, 240 const base::FilePath& crx_path) { 241 if (base::PathExists(crx_path)) 242 base::DeleteFile(crx_path, false); 243 base::ScopedFILE crx_handle(base::OpenFile(crx_path, "wb")); 244 if (!crx_handle.get()) { 245 error_message_ = l10n_util::GetStringUTF8(IDS_EXTENSION_SHARING_VIOLATION); 246 return false; 247 } 248 249 std::vector<uint8> public_key; 250 CHECK(private_key->ExportPublicKey(&public_key)); 251 252 CrxFile::Error error; 253 scoped_ptr<CrxFile> crx( 254 CrxFile::Create(public_key.size(), signature.size(), &error)); 255 if (!crx) { 256 LOG(ERROR) << "cannot create CrxFileHeader: " << error; 257 } 258 const CrxFile::Header header = crx->header(); 259 260 if (fwrite(&header, sizeof(header), 1, crx_handle.get()) != 1) { 261 PLOG(ERROR) << "fwrite failed to write header"; 262 } 263 if (fwrite(&public_key.front(), sizeof(uint8), public_key.size(), 264 crx_handle.get()) != public_key.size()) { 265 PLOG(ERROR) << "fwrite failed to write public_key.front"; 266 } 267 if (fwrite(&signature.front(), sizeof(uint8), signature.size(), 268 crx_handle.get()) != signature.size()) { 269 PLOG(ERROR) << "fwrite failed to write signature.front"; 270 } 271 272 size_t buffer_size = 1 << 16; 273 scoped_ptr<uint8[]> buffer(new uint8[buffer_size]); 274 size_t bytes_read = 0; 275 base::ScopedFILE zip_handle(base::OpenFile(zip_path, "rb")); 276 while ((bytes_read = fread(buffer.get(), 1, buffer_size, 277 zip_handle.get())) > 0) { 278 if (fwrite(buffer.get(), sizeof(char), bytes_read, crx_handle.get()) != 279 bytes_read) { 280 PLOG(ERROR) << "fwrite failed to write buffer"; 281 } 282 } 283 284 return true; 285 } 286 287 bool ExtensionCreator::Run(const base::FilePath& extension_dir, 288 const base::FilePath& crx_path, 289 const base::FilePath& private_key_path, 290 const base::FilePath& output_private_key_path, 291 int run_flags) { 292 // Check input diretory and read manifest. 293 if (!InitializeInput(extension_dir, crx_path, private_key_path, 294 output_private_key_path, run_flags)) { 295 return false; 296 } 297 298 // Initialize Key Pair 299 scoped_ptr<crypto::RSAPrivateKey> key_pair; 300 if (!private_key_path.value().empty()) 301 key_pair.reset(ReadInputKey(private_key_path)); 302 else 303 key_pair.reset(GenerateKey(output_private_key_path)); 304 if (!key_pair) 305 return false; 306 307 // Perform some extra validation by loading the extension. 308 // TODO(aa): Can this go before creating the key pair? This would mean not 309 // passing ID into LoadExtension which seems OK. 310 if (!ValidateManifest(extension_dir, key_pair.get(), run_flags)) 311 return false; 312 313 base::ScopedTempDir temp_dir; 314 if (!temp_dir.CreateUniqueTempDir()) 315 return false; 316 317 // Zip up the extension. 318 base::FilePath zip_path; 319 std::vector<uint8> signature; 320 bool result = false; 321 if (CreateZip(extension_dir, temp_dir.path(), &zip_path) && 322 SignZip(zip_path, key_pair.get(), &signature) && 323 WriteCRX(zip_path, key_pair.get(), signature, crx_path)) { 324 result = true; 325 } 326 327 base::DeleteFile(zip_path, false); 328 return result; 329 } 330 331 } // namespace extensions 332