Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_creator.cc
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 "crypto/rsa_private_key.h"
18 #include "crypto/signature_creator.h"
19 #include "extensions/common/crx_file.h"
20 #include "extensions/common/extension.h"
21 #include "extensions/common/file_util.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       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   ScopedStdioHandle zip_handle(base::OpenFile(zip_path, "rb"));
216   size_t buffer_size = 1 << 16;
217   scoped_ptr<uint8[]> buffer(new uint8[buffer_size]);
218   int bytes_read = -1;
219   while ((bytes_read = fread(buffer.get(), 1, buffer_size,
220        zip_handle.get())) > 0) {
221     if (!signature_creator->Update(buffer.get(), bytes_read)) {
222       error_message_ =
223           l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING);
224       return false;
225     }
226   }
227   zip_handle.Close();
228
229   if (!signature_creator->Final(signature)) {
230     error_message_ =
231         l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING);
232     return false;
233   }
234   return true;
235 }
236
237 bool ExtensionCreator::WriteCRX(const base::FilePath& zip_path,
238                                 crypto::RSAPrivateKey* private_key,
239                                 const std::vector<uint8>& signature,
240                                 const base::FilePath& crx_path) {
241   if (base::PathExists(crx_path))
242     base::DeleteFile(crx_path, false);
243   ScopedStdioHandle crx_handle(base::OpenFile(crx_path, "wb"));
244   if (!crx_handle.get()) {
245     error_message_ = l10n_util::GetStringUTF8(IDS_EXTENSION_SHARING_VIOLATION);
246     return false;
247   }
248
249   std::vector<uint8> public_key;
250   CHECK(private_key->ExportPublicKey(&public_key));
251
252   CrxFile::Error error;
253   scoped_ptr<CrxFile> crx(
254       CrxFile::Create(public_key.size(), signature.size(), &error));
255   if (!crx) {
256     LOG(ERROR) << "cannot create CrxFileHeader: " << error;
257   }
258   const CrxFile::Header header = crx->header();
259
260   if (fwrite(&header, sizeof(header), 1, crx_handle.get()) != 1) {
261     PLOG(ERROR) << "fwrite failed to write header";
262   }
263   if (fwrite(&public_key.front(), sizeof(uint8), public_key.size(),
264              crx_handle.get()) != public_key.size()) {
265     PLOG(ERROR) << "fwrite failed to write public_key.front";
266   }
267   if (fwrite(&signature.front(), sizeof(uint8), signature.size(),
268              crx_handle.get()) != signature.size()) {
269     PLOG(ERROR) << "fwrite failed to write signature.front";
270   }
271
272   size_t buffer_size = 1 << 16;
273   scoped_ptr<uint8[]> buffer(new uint8[buffer_size]);
274   size_t bytes_read = 0;
275   ScopedStdioHandle zip_handle(base::OpenFile(zip_path, "rb"));
276   while ((bytes_read = fread(buffer.get(), 1, buffer_size,
277                              zip_handle.get())) > 0) {
278     if (fwrite(buffer.get(), sizeof(char), bytes_read, crx_handle.get()) !=
279         bytes_read) {
280       PLOG(ERROR) << "fwrite failed to write buffer";
281     }
282   }
283
284   return true;
285 }
286
287 bool ExtensionCreator::Run(const base::FilePath& extension_dir,
288                            const base::FilePath& crx_path,
289                            const base::FilePath& private_key_path,
290                            const base::FilePath& output_private_key_path,
291                            int run_flags) {
292   // Check input diretory and read manifest.
293   if (!InitializeInput(extension_dir, crx_path, private_key_path,
294                        output_private_key_path, run_flags)) {
295     return false;
296   }
297
298   // Initialize Key Pair
299   scoped_ptr<crypto::RSAPrivateKey> key_pair;
300   if (!private_key_path.value().empty())
301     key_pair.reset(ReadInputKey(private_key_path));
302   else
303     key_pair.reset(GenerateKey(output_private_key_path));
304   if (!key_pair)
305     return false;
306
307   // Perform some extra validation by loading the extension.
308   // TODO(aa): Can this go before creating the key pair? This would mean not
309   // passing ID into LoadExtension which seems OK.
310   if (!ValidateManifest(extension_dir, key_pair.get(), run_flags))
311     return false;
312
313   base::ScopedTempDir temp_dir;
314   if (!temp_dir.CreateUniqueTempDir())
315     return false;
316
317   // Zip up the extension.
318   base::FilePath zip_path;
319   std::vector<uint8> signature;
320   bool result = false;
321   if (CreateZip(extension_dir, temp_dir.path(), &zip_path) &&
322       SignZip(zip_path, key_pair.get(), &signature) &&
323       WriteCRX(zip_path, key_pair.get(), signature, crx_path)) {
324     result = true;
325   }
326
327   base::DeleteFile(zip_path, false);
328   return result;
329 }
330
331 }  // namespace extensions