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/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