Home | History | Annotate | Download | only in extensions
      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