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
27namespace {
28  const int kRSAKeySize = 2048;
29};
30
31namespace extensions {
32
33ExtensionCreator::ExtensionCreator() : error_type_(kOtherError) {
34}
35
36bool 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
88bool 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
120crypto::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
147crypto::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
193bool 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
210bool 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
238bool 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
288bool 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