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_temp_dir.h" 14 #include "base/memory/scoped_handle.h" 15 #include "base/strings/string_util.h" 16 #include "chrome/browser/extensions/extension_creator_filter.h" 17 #include "chrome/common/extensions/extension_file_util.h" 18 #include "crypto/rsa_private_key.h" 19 #include "crypto/signature_creator.h" 20 #include "extensions/common/crx_file.h" 21 #include "extensions/common/extension.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 extension_file_util::LoadExtension( 113 extension_dir, 114 extension_id, 115 Manifest::INTERNAL, 116 create_flags, 117 &error_message_)); 118 return !!extension.get(); 119 } 120 121 crypto::RSAPrivateKey* ExtensionCreator::ReadInputKey(const base::FilePath& 122 private_key_path) { 123 if (!base::PathExists(private_key_path)) { 124 error_message_ = 125 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_NO_EXISTS); 126 return NULL; 127 } 128 129 std::string private_key_contents; 130 if (!base::ReadFileToString(private_key_path, &private_key_contents)) { 131 error_message_ = 132 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_READ); 133 return NULL; 134 } 135 136 std::string private_key_bytes; 137 if (!Extension::ParsePEMKeyBytes(private_key_contents, 138 &private_key_bytes)) { 139 error_message_ = 140 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_INVALID); 141 return NULL; 142 } 143 144 return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo( 145 std::vector<uint8>(private_key_bytes.begin(), private_key_bytes.end())); 146 } 147 148 crypto::RSAPrivateKey* ExtensionCreator::GenerateKey(const base::FilePath& 149 output_private_key_path) { 150 scoped_ptr<crypto::RSAPrivateKey> key_pair( 151 crypto::RSAPrivateKey::Create(kRSAKeySize)); 152 if (!key_pair) { 153 error_message_ = 154 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_GENERATE); 155 return NULL; 156 } 157 158 std::vector<uint8> private_key_vector; 159 if (!key_pair->ExportPrivateKey(&private_key_vector)) { 160 error_message_ = 161 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_EXPORT); 162 return NULL; 163 } 164 std::string private_key_bytes( 165 reinterpret_cast<char*>(&private_key_vector.front()), 166 private_key_vector.size()); 167 168 std::string private_key; 169 if (!Extension::ProducePEM(private_key_bytes, &private_key)) { 170 error_message_ = 171 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT); 172 return NULL; 173 } 174 std::string pem_output; 175 if (!Extension::FormatPEMForFileOutput(private_key, &pem_output, 176 false)) { 177 error_message_ = 178 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT); 179 return NULL; 180 } 181 182 if (!output_private_key_path.empty()) { 183 if (-1 == file_util::WriteFile(output_private_key_path, 184 pem_output.c_str(), pem_output.size())) { 185 error_message_ = 186 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT); 187 return NULL; 188 } 189 } 190 191 return key_pair.release(); 192 } 193 194 bool ExtensionCreator::CreateZip(const base::FilePath& extension_dir, 195 const base::FilePath& temp_path, 196 base::FilePath* zip_path) { 197 *zip_path = temp_path.Append(FILE_PATH_LITERAL("extension.zip")); 198 199 scoped_refptr<ExtensionCreatorFilter> filter = new ExtensionCreatorFilter(); 200 const base::Callback<bool(const base::FilePath&)>& filter_cb = 201 base::Bind(&ExtensionCreatorFilter::ShouldPackageFile, filter.get()); 202 if (!zip::ZipWithFilterCallback(extension_dir, *zip_path, filter_cb)) { 203 error_message_ = 204 l10n_util::GetStringUTF8(IDS_EXTENSION_FAILED_DURING_PACKAGING); 205 return false; 206 } 207 208 return true; 209 } 210 211 bool ExtensionCreator::SignZip(const base::FilePath& zip_path, 212 crypto::RSAPrivateKey* private_key, 213 std::vector<uint8>* signature) { 214 scoped_ptr<crypto::SignatureCreator> signature_creator( 215 crypto::SignatureCreator::Create(private_key)); 216 ScopedStdioHandle 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.Close(); 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 ScopedStdioHandle 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 CrxFile::Error error; 254 scoped_ptr<CrxFile> crx( 255 CrxFile::Create(public_key.size(), signature.size(), &error)); 256 if (!crx) { 257 LOG(ERROR) << "cannot create CrxFileHeader: " << error; 258 } 259 const 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 ScopedStdioHandle 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