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.h" 18 #include "chrome/common/extensions/extension_file_util.h" 19 #include "crypto/rsa_private_key.h" 20 #include "crypto/signature_creator.h" 21 #include "extensions/common/crx_file.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 = 1024; 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 (!file_util::ReadFileToString(private_key_path, 131 &private_key_contents)) { 132 error_message_ = 133 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_READ); 134 return NULL; 135 } 136 137 std::string private_key_bytes; 138 if (!Extension::ParsePEMKeyBytes(private_key_contents, 139 &private_key_bytes)) { 140 error_message_ = 141 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_INVALID); 142 return NULL; 143 } 144 145 return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo( 146 std::vector<uint8>(private_key_bytes.begin(), private_key_bytes.end())); 147 } 148 149 crypto::RSAPrivateKey* ExtensionCreator::GenerateKey(const base::FilePath& 150 output_private_key_path) { 151 scoped_ptr<crypto::RSAPrivateKey> key_pair( 152 crypto::RSAPrivateKey::Create(kRSAKeySize)); 153 if (!key_pair) { 154 error_message_ = 155 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_GENERATE); 156 return NULL; 157 } 158 159 std::vector<uint8> private_key_vector; 160 if (!key_pair->ExportPrivateKey(&private_key_vector)) { 161 error_message_ = 162 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_EXPORT); 163 return NULL; 164 } 165 std::string private_key_bytes( 166 reinterpret_cast<char*>(&private_key_vector.front()), 167 private_key_vector.size()); 168 169 std::string private_key; 170 if (!Extension::ProducePEM(private_key_bytes, &private_key)) { 171 error_message_ = 172 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT); 173 return NULL; 174 } 175 std::string pem_output; 176 if (!Extension::FormatPEMForFileOutput(private_key, &pem_output, 177 false)) { 178 error_message_ = 179 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT); 180 return NULL; 181 } 182 183 if (!output_private_key_path.empty()) { 184 if (-1 == file_util::WriteFile(output_private_key_path, 185 pem_output.c_str(), pem_output.size())) { 186 error_message_ = 187 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT); 188 return NULL; 189 } 190 } 191 192 return key_pair.release(); 193 } 194 195 bool ExtensionCreator::CreateZip(const base::FilePath& extension_dir, 196 const base::FilePath& temp_path, 197 base::FilePath* zip_path) { 198 *zip_path = temp_path.Append(FILE_PATH_LITERAL("extension.zip")); 199 200 scoped_refptr<ExtensionCreatorFilter> filter = new ExtensionCreatorFilter(); 201 const base::Callback<bool(const base::FilePath&)>& filter_cb = 202 base::Bind(&ExtensionCreatorFilter::ShouldPackageFile, filter.get()); 203 if (!zip::ZipWithFilterCallback(extension_dir, *zip_path, filter_cb)) { 204 error_message_ = 205 l10n_util::GetStringUTF8(IDS_EXTENSION_FAILED_DURING_PACKAGING); 206 return false; 207 } 208 209 return true; 210 } 211 212 bool ExtensionCreator::SignZip(const base::FilePath& zip_path, 213 crypto::RSAPrivateKey* private_key, 214 std::vector<uint8>* signature) { 215 scoped_ptr<crypto::SignatureCreator> signature_creator( 216 crypto::SignatureCreator::Create(private_key)); 217 ScopedStdioHandle zip_handle(file_util::OpenFile(zip_path, "rb")); 218 size_t buffer_size = 1 << 16; 219 scoped_ptr<uint8[]> buffer(new uint8[buffer_size]); 220 int bytes_read = -1; 221 while ((bytes_read = fread(buffer.get(), 1, buffer_size, 222 zip_handle.get())) > 0) { 223 if (!signature_creator->Update(buffer.get(), bytes_read)) { 224 error_message_ = 225 l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING); 226 return false; 227 } 228 } 229 zip_handle.Close(); 230 231 if (!signature_creator->Final(signature)) { 232 error_message_ = 233 l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING); 234 return false; 235 } 236 return true; 237 } 238 239 bool ExtensionCreator::WriteCRX(const base::FilePath& zip_path, 240 crypto::RSAPrivateKey* private_key, 241 const std::vector<uint8>& signature, 242 const base::FilePath& crx_path) { 243 if (base::PathExists(crx_path)) 244 base::DeleteFile(crx_path, false); 245 ScopedStdioHandle crx_handle(file_util::OpenFile(crx_path, "wb")); 246 if (!crx_handle.get()) { 247 error_message_ = l10n_util::GetStringUTF8(IDS_EXTENSION_SHARING_VIOLATION); 248 return false; 249 } 250 251 std::vector<uint8> public_key; 252 CHECK(private_key->ExportPublicKey(&public_key)); 253 254 CrxFile::Error error; 255 scoped_ptr<CrxFile> crx( 256 CrxFile::Create(public_key.size(), signature.size(), &error)); 257 if (!crx) { 258 LOG(ERROR) << "cannot create CrxFileHeader: " << error; 259 } 260 const CrxFile::Header header = crx->header(); 261 262 if (fwrite(&header, sizeof(header), 1, crx_handle.get()) != 1) { 263 PLOG(ERROR) << "fwrite failed to write header"; 264 } 265 if (fwrite(&public_key.front(), sizeof(uint8), public_key.size(), 266 crx_handle.get()) != public_key.size()) { 267 PLOG(ERROR) << "fwrite failed to write public_key.front"; 268 } 269 if (fwrite(&signature.front(), sizeof(uint8), signature.size(), 270 crx_handle.get()) != signature.size()) { 271 PLOG(ERROR) << "fwrite failed to write signature.front"; 272 } 273 274 size_t buffer_size = 1 << 16; 275 scoped_ptr<uint8[]> buffer(new uint8[buffer_size]); 276 size_t bytes_read = 0; 277 ScopedStdioHandle zip_handle(file_util::OpenFile(zip_path, "rb")); 278 while ((bytes_read = fread(buffer.get(), 1, buffer_size, 279 zip_handle.get())) > 0) { 280 if (fwrite(buffer.get(), sizeof(char), bytes_read, crx_handle.get()) != 281 bytes_read) { 282 PLOG(ERROR) << "fwrite failed to write buffer"; 283 } 284 } 285 286 return true; 287 } 288 289 bool ExtensionCreator::Run(const base::FilePath& extension_dir, 290 const base::FilePath& crx_path, 291 const base::FilePath& private_key_path, 292 const base::FilePath& output_private_key_path, 293 int run_flags) { 294 // Check input diretory and read manifest. 295 if (!InitializeInput(extension_dir, crx_path, private_key_path, 296 output_private_key_path, run_flags)) { 297 return false; 298 } 299 300 // Initialize Key Pair 301 scoped_ptr<crypto::RSAPrivateKey> key_pair; 302 if (!private_key_path.value().empty()) 303 key_pair.reset(ReadInputKey(private_key_path)); 304 else 305 key_pair.reset(GenerateKey(output_private_key_path)); 306 if (!key_pair) 307 return false; 308 309 // Perform some extra validation by loading the extension. 310 // TODO(aa): Can this go before creating the key pair? This would mean not 311 // passing ID into LoadExtension which seems OK. 312 if (!ValidateManifest(extension_dir, key_pair.get(), run_flags)) 313 return false; 314 315 base::ScopedTempDir temp_dir; 316 if (!temp_dir.CreateUniqueTempDir()) 317 return false; 318 319 // Zip up the extension. 320 base::FilePath zip_path; 321 std::vector<uint8> signature; 322 bool result = false; 323 if (CreateZip(extension_dir, temp_dir.path(), &zip_path) && 324 SignZip(zip_path, key_pair.get(), &signature) && 325 WriteCRX(zip_path, key_pair.get(), signature, crx_path)) { 326 result = true; 327 } 328 329 base::DeleteFile(zip_path, false); 330 return result; 331 } 332 333 } // namespace extensions 334