blob: 2be854caaab1cb0ebc667adbd8ef0534e2d16b9b [file] [log] [blame]
[email protected]098fa7a2013-03-08 22:11:171// Copyright (c) 2013 The Chromium Authors. All rights reserved.
[email protected]d8c8f25f2011-11-02 18:18:012// 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/unpacked_installer.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
thestig18dfb7a52014-08-26 10:44:049#include "base/files/file_util.h"
Karan Bhatia328e31a2017-09-18 21:46:1610#include "base/json/json_file_value_serializer.h"
dgozmand741d25d2017-03-23 00:35:3411#include "base/memory/ptr_util.h"
michaelpga8ea0372017-04-06 20:41:3512#include "base/strings/string16.h"
[email protected]46acbf12013-06-10 18:43:4213#include "base/strings/string_util.h"
michaelpga8ea0372017-04-06 20:41:3514#include "base/strings/utf_string_conversions.h"
Istiaque Ahmed81ff01e2017-08-09 16:00:5915#include "base/task_scheduler/post_task.h"
[email protected]bebe1d02012-08-02 20:17:0916#include "base/threading/thread_restrictions.h"
binjin1569c9b2014-09-05 13:33:1817#include "chrome/browser/extensions/extension_management.h"
[email protected]d8c8f25f2011-11-02 18:18:0118#include "chrome/browser/extensions/extension_service.h"
Devlin Cronin9722a722017-12-16 03:35:1019#include "chrome/browser/extensions/load_error_reporter.h"
[email protected]0d904312012-01-25 23:00:1620#include "chrome/browser/extensions/permissions_updater.h"
[email protected]284ffac2014-02-12 01:08:5821#include "chrome/browser/profiles/profile.h"
[email protected]fdd28372014-08-21 02:27:2622#include "components/crx_file/id_util.h"
skym71603842016-10-10 18:17:3123#include "components/sync/model/string_ordinal.h"
[email protected]79a60642012-10-20 21:03:1824#include "content/public/browser/browser_thread.h"
Karan Bhatia328e31a2017-09-18 21:46:1625#include "extensions/browser/api/declarative_net_request/utils.h"
Istiaque Ahmed81ff01e2017-08-09 16:00:5926#include "extensions/browser/extension_file_task_runner.h"
[email protected]489db0842014-01-22 18:20:0327#include "extensions/browser/extension_prefs.h"
[email protected]4a1d9c0d2014-06-13 12:50:1128#include "extensions/browser/install_flag.h"
michaelpg6a4874f2017-04-13 20:41:3329#include "extensions/browser/policy_check.h"
30#include "extensions/browser/preload_check_group.h"
31#include "extensions/browser/requirements_checker.h"
Karan Bhatia328e31a2017-09-18 21:46:1632#include "extensions/common/api/declarative_net_request/dnr_manifest_data.h"
Karan Bhatiafe12281a2017-09-16 02:38:1833#include "extensions/common/constants.h"
[email protected]e4452d32013-11-15 23:07:4134#include "extensions/common/extension.h"
[email protected]6668e5d2014-04-08 23:32:5235#include "extensions/common/extension_l10n_util.h"
[email protected]85df9d12014-04-15 17:02:1436#include "extensions/common/file_util.h"
[email protected]d42c11152013-08-22 19:36:3237#include "extensions/common/manifest.h"
Karan Bhatia71f6a622017-10-02 19:39:1038#include "extensions/common/manifest_constants.h"
elijahtaylore343b542014-10-03 19:48:1939#include "extensions/common/manifest_handlers/shared_module_info.h"
rdevlin.cronine2d0fd02015-09-24 22:35:4940#include "extensions/common/permissions/permissions_data.h"
[email protected]d8c8f25f2011-11-02 18:18:0141
[email protected]545ab7a2011-11-03 14:54:2142using content::BrowserThread;
[email protected]1c321ee2012-05-21 03:02:3443using extensions::Extension;
elijahtaylore343b542014-10-03 19:48:1944using extensions::SharedModuleInfo;
[email protected]545ab7a2011-11-03 14:54:2145
Karan Bhatiafe12281a2017-09-16 02:38:1846namespace extensions {
47
[email protected]d8c8f25f2011-11-02 18:18:0148namespace {
49
[email protected]8e7b2cf42012-04-18 14:26:5850const char kUnpackedExtensionsBlacklistedError[] =
51 "Loading of unpacked extensions is disabled by the administrator.";
52
elijahtaylorc93f75472014-10-15 18:42:2553const char kImportMinVersionNewer[] =
54 "'import' version requested is newer than what is installed.";
55const char kImportMissing[] = "'import' extension is not installed.";
56const char kImportNotSharedModule[] = "'import' is not a shared module.";
57
Karan Bhatiafe12281a2017-09-16 02:38:1858// Deletes files reserved for use by the Extension system in the kMetadataFolder
59// and the kMetadataFolder itself if it is empty.
60void MaybeCleanupMetadataFolder(const base::FilePath& extension_path) {
61 const std::vector<base::FilePath> reserved_filepaths =
62 file_util::GetReservedMetadataFilePaths(extension_path);
63 for (const auto& file : reserved_filepaths)
64 base::DeleteFile(file, false /*recursive*/);
[email protected]d8c8f25f2011-11-02 18:18:0165
Karan Bhatiafe12281a2017-09-16 02:38:1866 const base::FilePath& metadata_dir = extension_path.Append(kMetadataFolder);
67 if (base::IsDirectoryEmpty(metadata_dir))
68 base::DeleteFile(metadata_dir, true /*recursive*/);
69}
70
71} // namespace
Alexander Alekseev63c02142017-09-14 19:19:1972
[email protected]d8c8f25f2011-11-02 18:18:0173// static
74scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create(
75 ExtensionService* extension_service) {
[email protected]3a7fbc242013-12-19 17:43:3776 DCHECK(extension_service);
[email protected]d8c8f25f2011-11-02 18:18:0177 return scoped_refptr<UnpackedInstaller>(
78 new UnpackedInstaller(extension_service));
79}
80
81UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service)
82 : service_weak_(extension_service->AsWeakPtr()),
dgozmand741d25d2017-03-23 00:35:3483 profile_(extension_service->profile()),
[email protected]9d02fa12013-02-19 05:12:5784 require_modern_manifest_version_(true),
dgozmand741d25d2017-03-23 00:35:3485 be_noisy_on_failure_(true) {
yoz416ae9f2015-02-24 19:40:4186 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]d8c8f25f2011-11-02 18:18:0187}
88
89UnpackedInstaller::~UnpackedInstaller() {
[email protected]d8c8f25f2011-11-02 18:18:0190}
91
[email protected]650b2d52013-02-10 03:41:4592void UnpackedInstaller::Load(const base::FilePath& path_in) {
[email protected]98270432012-09-11 20:51:2493 DCHECK(extension_path_.empty());
[email protected]d8c8f25f2011-11-02 18:18:0194 extension_path_ = path_in;
Istiaque Ahmed81ff01e2017-08-09 16:00:5995 GetExtensionFileTaskRunner()->PostTask(
96 FROM_HERE, base::BindOnce(&UnpackedInstaller::GetAbsolutePath, this));
[email protected]d8c8f25f2011-11-02 18:18:0197}
98
[email protected]2a69b942013-05-31 09:37:5399bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath& path_in,
proberge80a37f32016-08-04 19:44:55100 std::string* extension_id,
101 bool only_allow_apps) {
yoz416ae9f2015-02-24 19:40:41102 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]98270432012-09-11 20:51:24103 DCHECK(extension_path_.empty());
104
[email protected]e8dad9b2013-06-04 04:43:45105 if (!service_weak_.get())
[email protected]2a69b942013-05-31 09:37:53106 return false;
[email protected]d8c8f25f2011-11-02 18:18:01107 // Load extensions from the command line synchronously to avoid a race
108 // between extension loading and loading an URL from the command line.
109 base::ThreadRestrictions::ScopedAllowIO allow_io;
110
[email protected]15476932013-04-12 05:17:15111 extension_path_ = base::MakeAbsoluteFilePath(path_in);
[email protected]d8c8f25f2011-11-02 18:18:01112
[email protected]8e7b2cf42012-04-18 14:26:58113 if (!IsLoadingUnpackedAllowed()) {
114 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
[email protected]2a69b942013-05-31 09:37:53115 return false;
[email protected]8e7b2cf42012-04-18 14:26:58116 }
117
[email protected]d8c8f25f2011-11-02 18:18:01118 std::string error;
Karan Bhatiafe12281a2017-09-16 02:38:18119 if (!LoadExtension(Manifest::COMMAND_LINE, GetFlags(), &error)) {
probergeac2a4b62016-08-29 16:16:09120 ReportExtensionLoadError(error);
121 return false;
122 }
123
proberge80a37f32016-08-04 19:44:55124 if (only_allow_apps && !extension()->is_platform_app()) {
125#if defined(GOOGLE_CHROME_BUILD)
126 // Avoid crashing for users with hijacked shortcuts.
127 return true;
128#else
129 // Defined here to avoid unused variable errors in official builds.
130 const char extension_instead_of_app_error[] =
131 "App loading flags cannot be used to load extensions. Please use "
132 "--load-extension instead.";
133 ReportExtensionLoadError(extension_instead_of_app_error);
134 return false;
135#endif
136 }
137
rdevlin.cronine2d0fd02015-09-24 22:35:49138 extension()->permissions_data()->BindToCurrentThread();
rdevlin.croninf173cbc42014-09-24 18:03:32139 PermissionsUpdater(
140 service_weak_->profile(), PermissionsUpdater::INIT_FLAG_TRANSIENT)
141 .InitializePermissions(extension());
Karan Bhatiaf70d840b2017-11-22 19:21:09142 StartInstallChecks();
[email protected]2a69b942013-05-31 09:37:53143
[email protected]253fc2bb2014-07-10 04:21:18144 *extension_id = extension()->id();
[email protected]2a69b942013-05-31 09:37:53145 return true;
[email protected]98270432012-09-11 20:51:24146}
147
[email protected]253fc2bb2014-07-10 04:21:18148void UnpackedInstaller::StartInstallChecks() {
Karan Bhatiaf70d840b2017-11-22 19:21:09149 DCHECK_CURRENTLY_ON(BrowserThread::UI);
150 ExtensionService* service = service_weak_.get();
151 if (!service)
152 return;
153
elijahtaylorc93f75472014-10-15 18:42:25154 // TODO(crbug.com/421128): Enable these checks all the time. The reason
155 // they are disabled for extensions loaded from the command-line is that
156 // installing unpacked extensions is asynchronous, but there can be
157 // dependencies between the extensions loaded by the command line.
158 if (extension()->manifest()->location() != Manifest::COMMAND_LINE) {
Karan Bhatiaf70d840b2017-11-22 19:21:09159 if (service->browser_terminating())
elijahtaylorc93f75472014-10-15 18:42:25160 return;
161
162 // TODO(crbug.com/420147): Move this code to a utility class to avoid
163 // duplication of SharedModuleService::CheckImports code.
164 if (SharedModuleInfo::ImportsModules(extension())) {
165 const std::vector<SharedModuleInfo::ImportInfo>& imports =
166 SharedModuleInfo::GetImports(extension());
167 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
168 for (i = imports.begin(); i != imports.end(); ++i) {
pwnallcbd73192016-08-22 18:59:17169 base::Version version_required(i->minimum_version);
elijahtaylorc93f75472014-10-15 18:42:25170 const Extension* imported_module =
171 service->GetExtensionById(i->extension_id, true);
172 if (!imported_module) {
173 ReportExtensionLoadError(kImportMissing);
174 return;
175 } else if (imported_module &&
176 !SharedModuleInfo::IsSharedModule(imported_module)) {
177 ReportExtensionLoadError(kImportNotSharedModule);
178 return;
179 } else if (imported_module && (version_required.IsValid() &&
180 imported_module->version()->CompareTo(
181 version_required) < 0)) {
182 ReportExtensionLoadError(kImportMinVersionNewer);
183 return;
184 }
185 }
186 }
187 }
188
michaelpg6a4874f2017-04-13 20:41:33189 policy_check_ = base::MakeUnique<PolicyCheck>(profile_, extension_);
190 requirements_check_ = base::MakeUnique<RequirementsChecker>(extension_);
191
192 check_group_ = base::MakeUnique<PreloadCheckGroup>();
193 check_group_->set_stop_on_first_error(true);
194
195 check_group_->AddCheck(policy_check_.get());
196 check_group_->AddCheck(requirements_check_.get());
197 check_group_->Start(
198 base::BindOnce(&UnpackedInstaller::OnInstallChecksComplete, this));
[email protected]98270432012-09-11 20:51:24199}
200
Istiaque Ahmed400c83a2017-10-11 02:39:35201void UnpackedInstaller::OnInstallChecksComplete(
202 const PreloadCheck::Errors& errors) {
yoz416ae9f2015-02-24 19:40:41203 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]98270432012-09-11 20:51:24204
michaelpg6a4874f2017-04-13 20:41:33205 if (errors.empty()) {
206 InstallExtension();
[email protected]253fc2bb2014-07-10 04:21:18207 return;
208 }
209
michaelpg6a4874f2017-04-13 20:41:33210 base::string16 error_message;
211 if (errors.count(PreloadCheck::DISALLOWED_BY_POLICY))
212 error_message = policy_check_->GetErrorMessage();
213 else
214 error_message = requirements_check_->GetErrorMessage();
215
216 DCHECK(!error_message.empty());
217 ReportExtensionLoadError(base::UTF16ToUTF8(error_message));
[email protected]d8c8f25f2011-11-02 18:18:01218}
219
[email protected]b7462f32012-09-02 15:18:12220int UnpackedInstaller::GetFlags() {
[email protected]fdd28372014-08-21 02:27:26221 std::string id = crx_file::id_util::GenerateIdForPath(extension_path_);
[email protected]b7462f32012-09-02 15:18:12222 bool allow_file_access =
[email protected]12075d12013-02-27 05:38:05223 Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED);
[email protected]7c82539c2014-02-19 06:09:17224 ExtensionPrefs* prefs = ExtensionPrefs::Get(service_weak_->profile());
[email protected]849749d2013-05-06 17:30:45225 if (prefs->HasAllowFileAccessSetting(id))
226 allow_file_access = prefs->AllowFileAccess(id);
[email protected]b7462f32012-09-02 15:18:12227
228 int result = Extension::FOLLOW_SYMLINKS_ANYWHERE;
229 if (allow_file_access)
230 result |= Extension::ALLOW_FILE_ACCESS;
231 if (require_modern_manifest_version_)
232 result |= Extension::REQUIRE_MODERN_MANIFEST_VERSION;
233
234 return result;
235}
236
Karan Bhatiafe12281a2017-09-16 02:38:18237bool UnpackedInstaller::LoadExtension(Manifest::Location location,
238 int flags,
239 std::string* error) {
Francois Doray66bdfd82017-10-20 13:50:37240 base::AssertBlockingAllowed();
Karan Bhatiafe12281a2017-09-16 02:38:18241
242 // Clean up the kMetadataFolder if necessary. This prevents spurious
243 // warnings/errors and ensures we don't treat a user provided file as one by
244 // the Extension system.
245 MaybeCleanupMetadataFolder(extension_path_);
246
247 // Treat presence of illegal filenames as a hard error for unpacked
248 // extensions. Don't do so for command line extensions since this breaks
249 // Chrome OS autotests (crbug.com/764787).
250 if (location == Manifest::UNPACKED &&
251 !file_util::CheckForIllegalFilenames(extension_path_, error)) {
252 return false;
253 }
254
255 extension_ =
256 file_util::LoadExtension(extension_path_, location, flags, error);
257
258 return extension() &&
259 extension_l10n_util::ValidateExtensionLocales(
Karan Bhatia328e31a2017-09-18 21:46:16260 extension_path_, extension()->manifest()->value(), error) &&
261 IndexAndPersistRulesIfNeeded(error);
262}
263
264bool UnpackedInstaller::IndexAndPersistRulesIfNeeded(std::string* error) {
265 DCHECK(extension());
Francois Doray66bdfd82017-10-20 13:50:37266 base::AssertBlockingAllowed();
Karan Bhatia328e31a2017-09-18 21:46:16267
268 const ExtensionResource* resource =
269 declarative_net_request::DNRManifestData::GetRulesetResource(extension());
270 // The extension did not provide a ruleset.
271 if (!resource)
272 return true;
273
274 // TODO(crbug.com/761107): Change this so that we don't need to parse JSON
275 // in the browser process.
276 JSONFileValueDeserializer deserializer(resource->GetFilePath());
Karan Bhatia71f6a622017-10-02 19:39:10277 std::unique_ptr<base::Value> root = deserializer.Deserialize(nullptr, error);
278 if (!root)
Karan Bhatia328e31a2017-09-18 21:46:16279 return false;
280
Karan Bhatia71f6a622017-10-02 19:39:10281 if (!root->is_list()) {
282 *error = manifest_errors::kDeclarativeNetRequestListNotPassed;
283 return false;
284 }
285
Karan Bhatia328e31a2017-09-18 21:46:16286 std::vector<InstallWarning> warnings;
Karandeep Bhatiaa8930652017-10-11 17:41:12287 int ruleset_checksum;
288 if (!declarative_net_request::IndexAndPersistRules(
289 *base::ListValue::From(std::move(root)), *extension(), error,
290 &warnings, &ruleset_checksum)) {
291 return false;
292 }
293
294 dnr_ruleset_checksum_ = ruleset_checksum;
Karan Bhatia328e31a2017-09-18 21:46:16295 extension_->AddInstallWarnings(warnings);
Karandeep Bhatiaa8930652017-10-11 17:41:12296 return true;
Karan Bhatiafe12281a2017-09-16 02:38:18297}
298
[email protected]8e7b2cf42012-04-18 14:26:58299bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
[email protected]e8dad9b2013-06-04 04:43:45300 if (!service_weak_.get())
[email protected]8e7b2cf42012-04-18 14:26:58301 return true;
302 // If there is a "*" in the extension blacklist, then no extensions should be
303 // allowed at all (except explicitly whitelisted extensions).
binjin1569c9b2014-09-05 13:33:18304 return !ExtensionManagementFactory::GetForBrowserContext(
305 service_weak_->profile())->BlacklistedByDefault();
[email protected]8e7b2cf42012-04-18 14:26:58306}
307
[email protected]d8c8f25f2011-11-02 18:18:01308void UnpackedInstaller::GetAbsolutePath() {
Francois Doray66bdfd82017-10-20 13:50:37309 base::AssertBlockingAllowed();
[email protected]d8c8f25f2011-11-02 18:18:01310
[email protected]15476932013-04-12 05:17:15311 extension_path_ = base::MakeAbsoluteFilePath(extension_path_);
[email protected]d8c8f25f2011-11-02 18:18:01312
[email protected]849749d2013-05-06 17:30:45313 BrowserThread::PostTask(
314 BrowserThread::UI, FROM_HERE,
tzik8d880ee2017-04-20 19:46:24315 base::BindOnce(&UnpackedInstaller::CheckExtensionFileAccess, this));
[email protected]d8c8f25f2011-11-02 18:18:01316}
317
318void UnpackedInstaller::CheckExtensionFileAccess() {
yoz416ae9f2015-02-24 19:40:41319 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]e8dad9b2013-06-04 04:43:45320 if (!service_weak_.get())
[email protected]8e7b2cf42012-04-18 14:26:58321 return;
322
323 if (!IsLoadingUnpackedAllowed()) {
324 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
325 return;
326 }
327
Istiaque Ahmed81ff01e2017-08-09 16:00:59328 GetExtensionFileTaskRunner()->PostTask(
329 FROM_HERE,
tzik8d880ee2017-04-20 19:46:24330 base::BindOnce(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags()));
[email protected]d8c8f25f2011-11-02 18:18:01331}
332
[email protected]b7462f32012-09-02 15:18:12333void UnpackedInstaller::LoadWithFileAccess(int flags) {
Francois Doray66bdfd82017-10-20 13:50:37334 base::AssertBlockingAllowed();
[email protected]b7462f32012-09-02 15:18:12335
[email protected]d8c8f25f2011-11-02 18:18:01336 std::string error;
Karan Bhatiafe12281a2017-09-16 02:38:18337 if (!LoadExtension(Manifest::UNPACKED, flags, &error)) {
[email protected]849749d2013-05-06 17:30:45338 BrowserThread::PostTask(
tzik8d880ee2017-04-20 19:46:24339 BrowserThread::UI, FROM_HERE,
340 base::BindOnce(&UnpackedInstaller::ReportExtensionLoadError, this,
341 error));
[email protected]d8c8f25f2011-11-02 18:18:01342 return;
343 }
344
[email protected]849749d2013-05-06 17:30:45345 BrowserThread::PostTask(
tzik8d880ee2017-04-20 19:46:24346 BrowserThread::UI, FROM_HERE,
Karan Bhatiaf70d840b2017-11-22 19:21:09347 base::BindOnce(&UnpackedInstaller::StartInstallChecks, this));
[email protected]d8c8f25f2011-11-02 18:18:01348}
349
350void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) {
yoz416ae9f2015-02-24 19:40:41351 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]500c7bb2014-04-26 22:44:33352
[email protected]1f0722442014-05-01 17:26:02353 if (service_weak_.get()) {
Devlin Cronin9722a722017-12-16 03:35:10354 LoadErrorReporter::GetInstance()->ReportLoadError(
355 extension_path_, error, service_weak_->profile(), be_noisy_on_failure_);
[email protected]1f0722442014-05-01 17:26:02356 }
rdevlin.croninf6ea63a2015-03-04 17:51:04357
358 if (!callback_.is_null()) {
359 callback_.Run(nullptr, extension_path_, error);
360 callback_.Reset();
361 }
[email protected]d8c8f25f2011-11-02 18:18:01362}
363
[email protected]253fc2bb2014-07-10 04:21:18364void UnpackedInstaller::InstallExtension() {
[email protected]54ee8192014-03-29 17:37:24365 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]0d904312012-01-25 23:00:16366
asargent1e46c512016-09-16 00:46:09367 if (!service_weak_.get()) {
368 callback_.Reset();
369 return;
370 }
371
[email protected]0d904312012-01-25 23:00:16372 PermissionsUpdater perms_updater(service_weak_->profile());
gpdavis.chromium48f08c42014-09-18 22:09:34373 perms_updater.InitializePermissions(extension());
[email protected]253fc2bb2014-07-10 04:21:18374 perms_updater.GrantActivePermissions(extension());
[email protected]9d02fa12013-02-19 05:12:57375
Karandeep Bhatiaa8930652017-10-11 17:41:12376 service_weak_->OnExtensionInstalled(extension(), syncer::StringOrdinal(),
377 kInstallFlagInstallImmediately,
378 dnr_ruleset_checksum_);
rdevlin.croninf6ea63a2015-03-04 17:51:04379
380 if (!callback_.is_null()) {
381 callback_.Run(extension(), extension_path_, std::string());
382 callback_.Reset();
383 }
[email protected]d8c8f25f2011-11-02 18:18:01384}
385
386} // namespace extensions