Make default apps cache multiprofile friendly
[chromium-blink-merge.git] / chrome / browser / extensions / extension_creator.cc
blob61d4a4ab8c2f2443adce89aaff700d638d145a77
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.
5 #include "chrome/browser/extensions/extension_creator.h"
7 #include <string>
8 #include <vector>
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"
27 namespace {
28 const int kRSAKeySize = 2048;
31 namespace extensions {
33 ExtensionCreator::ExtensionCreator() : error_type_(kOtherError) {
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;
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;
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;
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;
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;
82 return false;
85 return true;
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;
98 std::string public_key;
99 public_key.insert(public_key.begin(),
100 public_key_bytes.begin(), public_key_bytes.end());
102 std::string extension_id = id_util::GenerateId(public_key);
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;
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();
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;
129 std::string private_key_contents;
130 if (!base::ReadFileToString(private_key_path, &private_key_contents)) {
131 error_message_ =
132 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_READ);
133 return NULL;
136 std::string private_key_bytes;
137 if (!Extension::ParsePEMKeyBytes(private_key_contents,
138 &private_key_bytes)) {
139 error_message_ =
140 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_INVALID);
141 return NULL;
144 return crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(
145 std::vector<uint8>(private_key_bytes.begin(), private_key_bytes.end()));
148 crypto::RSAPrivateKey* ExtensionCreator::GenerateKey(const base::FilePath&
149 output_private_key_path) {
150 scoped_ptr<crypto::RSAPrivateKey> key_pair(
151 crypto::RSAPrivateKey::Create(kRSAKeySize));
152 if (!key_pair) {
153 error_message_ =
154 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_GENERATE);
155 return NULL;
158 std::vector<uint8> private_key_vector;
159 if (!key_pair->ExportPrivateKey(&private_key_vector)) {
160 error_message_ =
161 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_EXPORT);
162 return NULL;
164 std::string private_key_bytes(
165 reinterpret_cast<char*>(&private_key_vector.front()),
166 private_key_vector.size());
168 std::string private_key;
169 if (!Extension::ProducePEM(private_key_bytes, &private_key)) {
170 error_message_ =
171 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT);
172 return NULL;
174 std::string pem_output;
175 if (!Extension::FormatPEMForFileOutput(private_key, &pem_output,
176 false)) {
177 error_message_ =
178 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT);
179 return NULL;
182 if (!output_private_key_path.empty()) {
183 if (-1 == file_util::WriteFile(output_private_key_path,
184 pem_output.c_str(), pem_output.size())) {
185 error_message_ =
186 l10n_util::GetStringUTF8(IDS_EXTENSION_PRIVATE_KEY_FAILED_TO_OUTPUT);
187 return NULL;
191 return key_pair.release();
194 bool ExtensionCreator::CreateZip(const base::FilePath& extension_dir,
195 const base::FilePath& temp_path,
196 base::FilePath* zip_path) {
197 *zip_path = temp_path.Append(FILE_PATH_LITERAL("extension.zip"));
199 scoped_refptr<ExtensionCreatorFilter> filter = new ExtensionCreatorFilter();
200 const base::Callback<bool(const base::FilePath&)>& filter_cb =
201 base::Bind(&ExtensionCreatorFilter::ShouldPackageFile, filter.get());
202 if (!zip::ZipWithFilterCallback(extension_dir, *zip_path, filter_cb)) {
203 error_message_ =
204 l10n_util::GetStringUTF8(IDS_EXTENSION_FAILED_DURING_PACKAGING);
205 return false;
208 return true;
211 bool ExtensionCreator::SignZip(const base::FilePath& zip_path,
212 crypto::RSAPrivateKey* private_key,
213 std::vector<uint8>* signature) {
214 scoped_ptr<crypto::SignatureCreator> signature_creator(
215 crypto::SignatureCreator::Create(private_key));
216 ScopedStdioHandle zip_handle(file_util::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;
228 zip_handle.Close();
230 if (!signature_creator->Final(signature)) {
231 error_message_ =
232 l10n_util::GetStringUTF8(IDS_EXTENSION_ERROR_WHILE_SIGNING);
233 return false;
235 return true;
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 ScopedStdioHandle crx_handle(file_util::OpenFile(crx_path, "wb"));
245 if (!crx_handle.get()) {
246 error_message_ = l10n_util::GetStringUTF8(IDS_EXTENSION_SHARING_VIOLATION);
247 return false;
250 std::vector<uint8> public_key;
251 CHECK(private_key->ExportPublicKey(&public_key));
253 CrxFile::Error error;
254 scoped_ptr<CrxFile> crx(
255 CrxFile::Create(public_key.size(), signature.size(), &error));
256 if (!crx) {
257 LOG(ERROR) << "cannot create CrxFileHeader: " << error;
259 const CrxFile::Header header = crx->header();
261 if (fwrite(&header, sizeof(header), 1, crx_handle.get()) != 1) {
262 PLOG(ERROR) << "fwrite failed to write header";
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";
268 if (fwrite(&signature.front(), sizeof(uint8), signature.size(),
269 crx_handle.get()) != signature.size()) {
270 PLOG(ERROR) << "fwrite failed to write signature.front";
273 size_t buffer_size = 1 << 16;
274 scoped_ptr<uint8[]> buffer(new uint8[buffer_size]);
275 size_t bytes_read = 0;
276 ScopedStdioHandle zip_handle(file_util::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";
285 return true;
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;
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;
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;
314 base::ScopedTempDir temp_dir;
315 if (!temp_dir.CreateUniqueTempDir())
316 return false;
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;
328 base::DeleteFile(zip_path, false);
329 return result;
332 } // namespace extensions