1 // Copyright (c) 2013 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/unpacked_installer.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/files/file_util.h"
10 #include "base/strings/string_util.h"
11 #include "base/threading/thread_restrictions.h"
12 #include "chrome/browser/extensions/extension_error_reporter.h"
13 #include "chrome/browser/extensions/extension_install_prompt.h"
14 #include "chrome/browser/extensions/extension_install_ui.h"
15 #include "chrome/browser/extensions/extension_management.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/permissions_updater.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/extensions/api/plugins/plugins_handler.h"
20 #include "components/crx_file/id_util.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "extensions/browser/extension_prefs.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/install_flag.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/extension_l10n_util.h"
27 #include "extensions/common/file_util.h"
28 #include "extensions/common/manifest.h"
29 #include "sync/api/string_ordinal.h"
30
31 using content::BrowserThread;
32 using extensions::Extension;
33
34 namespace {
35
36 const char kUnpackedExtensionsBlacklistedError[] =
37 "Loading of unpacked extensions is disabled by the administrator.";
38
39 // Manages an ExtensionInstallPrompt for a particular extension.
40 class SimpleExtensionLoadPrompt : public ExtensionInstallPrompt::Delegate {
41 public:
42 SimpleExtensionLoadPrompt(const Extension* extension,
43 Profile* profile,
44 const base::Closure& callback);
45 virtual ~SimpleExtensionLoadPrompt();
46
47 void ShowPrompt();
48
49 // ExtensionInstallUI::Delegate
50 virtual void InstallUIProceed() OVERRIDE;
51 virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
52
53 private:
54 scoped_ptr<ExtensionInstallPrompt> install_ui_;
55 scoped_refptr<const Extension> extension_;
56 base::Closure callback_;
57 };
58
SimpleExtensionLoadPrompt(const Extension * extension,Profile * profile,const base::Closure & callback)59 SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt(
60 const Extension* extension,
61 Profile* profile,
62 const base::Closure& callback)
63 : install_ui_(ExtensionInstallUI::CreateInstallPromptWithProfile(
64 profile)),
65 extension_(extension),
66 callback_(callback) {
67 }
68
~SimpleExtensionLoadPrompt()69 SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() {
70 }
71
ShowPrompt()72 void SimpleExtensionLoadPrompt::ShowPrompt() {
73 switch (ExtensionInstallPrompt::g_auto_confirm_for_tests) {
74 case ExtensionInstallPrompt::NONE:
75 install_ui_->ConfirmInstall(
76 this,
77 extension_.get(),
78 ExtensionInstallPrompt::GetDefaultShowDialogCallback());
79 break;
80 case ExtensionInstallPrompt::ACCEPT:
81 InstallUIProceed();
82 break;
83 case ExtensionInstallPrompt::CANCEL:
84 InstallUIAbort(false);
85 }
86 }
87
InstallUIProceed()88 void SimpleExtensionLoadPrompt::InstallUIProceed() {
89 callback_.Run();
90 delete this;
91 }
92
InstallUIAbort(bool user_initiated)93 void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated) {
94 delete this;
95 }
96
97 } // namespace
98
99 namespace extensions {
100
101 // static
Create(ExtensionService * extension_service)102 scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create(
103 ExtensionService* extension_service) {
104 DCHECK(extension_service);
105 return scoped_refptr<UnpackedInstaller>(
106 new UnpackedInstaller(extension_service));
107 }
108
UnpackedInstaller(ExtensionService * extension_service)109 UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service)
110 : service_weak_(extension_service->AsWeakPtr()),
111 prompt_for_plugins_(true),
112 require_modern_manifest_version_(true),
113 be_noisy_on_failure_(true),
114 install_checker_(extension_service->profile()) {
115 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
116 }
117
~UnpackedInstaller()118 UnpackedInstaller::~UnpackedInstaller() {
119 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
120 BrowserThread::CurrentlyOn(BrowserThread::FILE));
121 }
122
Load(const base::FilePath & path_in)123 void UnpackedInstaller::Load(const base::FilePath& path_in) {
124 DCHECK(extension_path_.empty());
125 extension_path_ = path_in;
126 BrowserThread::PostTask(
127 BrowserThread::FILE,
128 FROM_HERE,
129 base::Bind(&UnpackedInstaller::GetAbsolutePath, this));
130 }
131
LoadFromCommandLine(const base::FilePath & path_in,std::string * extension_id)132 bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath& path_in,
133 std::string* extension_id) {
134 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
135 DCHECK(extension_path_.empty());
136
137 if (!service_weak_.get())
138 return false;
139 // Load extensions from the command line synchronously to avoid a race
140 // between extension loading and loading an URL from the command line.
141 base::ThreadRestrictions::ScopedAllowIO allow_io;
142
143 extension_path_ = base::MakeAbsoluteFilePath(path_in);
144
145 if (!IsLoadingUnpackedAllowed()) {
146 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
147 return false;
148 }
149
150 std::string error;
151 install_checker_.set_extension(
152 file_util::LoadExtension(
153 extension_path_, Manifest::COMMAND_LINE, GetFlags(), &error).get());
154
155 if (!extension() ||
156 !extension_l10n_util::ValidateExtensionLocales(
157 extension_path_, extension()->manifest()->value(), &error)) {
158 ReportExtensionLoadError(error);
159 return false;
160 }
161
162 PermissionsUpdater(
163 service_weak_->profile(), PermissionsUpdater::INIT_FLAG_TRANSIENT)
164 .InitializePermissions(extension());
165 ShowInstallPrompt();
166
167 *extension_id = extension()->id();
168 return true;
169 }
170
ShowInstallPrompt()171 void UnpackedInstaller::ShowInstallPrompt() {
172 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
173 if (!service_weak_.get())
174 return;
175
176 const ExtensionSet& disabled_extensions =
177 ExtensionRegistry::Get(service_weak_->profile())->disabled_extensions();
178 if (service_weak_->show_extensions_prompts() && prompt_for_plugins_ &&
179 PluginInfo::HasPlugins(extension()) &&
180 !disabled_extensions.Contains(extension()->id())) {
181 SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt(
182 extension(),
183 install_checker_.profile(),
184 base::Bind(&UnpackedInstaller::StartInstallChecks, this));
185 prompt->ShowPrompt();
186 return;
187 }
188 StartInstallChecks();
189 }
190
StartInstallChecks()191 void UnpackedInstaller::StartInstallChecks() {
192 install_checker_.Start(
193 ExtensionInstallChecker::CHECK_REQUIREMENTS |
194 ExtensionInstallChecker::CHECK_MANAGEMENT_POLICY,
195 true /* fail fast */,
196 base::Bind(&UnpackedInstaller::OnInstallChecksComplete, this));
197 }
198
OnInstallChecksComplete(int failed_checks)199 void UnpackedInstaller::OnInstallChecksComplete(int failed_checks) {
200 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
201
202 if (!install_checker_.policy_error().empty()) {
203 ReportExtensionLoadError(install_checker_.policy_error());
204 return;
205 }
206
207 if (!install_checker_.requirement_errors().empty()) {
208 ReportExtensionLoadError(
209 JoinString(install_checker_.requirement_errors(), ' '));
210 return;
211 }
212
213 InstallExtension();
214 }
215
GetFlags()216 int UnpackedInstaller::GetFlags() {
217 std::string id = crx_file::id_util::GenerateIdForPath(extension_path_);
218 bool allow_file_access =
219 Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED);
220 ExtensionPrefs* prefs = ExtensionPrefs::Get(service_weak_->profile());
221 if (prefs->HasAllowFileAccessSetting(id))
222 allow_file_access = prefs->AllowFileAccess(id);
223
224 int result = Extension::FOLLOW_SYMLINKS_ANYWHERE;
225 if (allow_file_access)
226 result |= Extension::ALLOW_FILE_ACCESS;
227 if (require_modern_manifest_version_)
228 result |= Extension::REQUIRE_MODERN_MANIFEST_VERSION;
229
230 return result;
231 }
232
IsLoadingUnpackedAllowed() const233 bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
234 if (!service_weak_.get())
235 return true;
236 // If there is a "*" in the extension blacklist, then no extensions should be
237 // allowed at all (except explicitly whitelisted extensions).
238 return !ExtensionManagementFactory::GetForBrowserContext(
239 service_weak_->profile())->BlacklistedByDefault();
240 }
241
GetAbsolutePath()242 void UnpackedInstaller::GetAbsolutePath() {
243 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
244
245 extension_path_ = base::MakeAbsoluteFilePath(extension_path_);
246
247 std::string error;
248 if (!file_util::CheckForIllegalFilenames(extension_path_, &error)) {
249 BrowserThread::PostTask(
250 BrowserThread::UI,
251 FROM_HERE,
252 base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
253 return;
254 }
255 BrowserThread::PostTask(
256 BrowserThread::UI, FROM_HERE,
257 base::Bind(&UnpackedInstaller::CheckExtensionFileAccess, this));
258 }
259
CheckExtensionFileAccess()260 void UnpackedInstaller::CheckExtensionFileAccess() {
261 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
262 if (!service_weak_.get())
263 return;
264
265 if (!IsLoadingUnpackedAllowed()) {
266 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
267 return;
268 }
269
270 BrowserThread::PostTask(
271 BrowserThread::FILE,
272 FROM_HERE,
273 base::Bind(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags()));
274 }
275
LoadWithFileAccess(int flags)276 void UnpackedInstaller::LoadWithFileAccess(int flags) {
277 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
278
279 std::string error;
280 install_checker_.set_extension(
281 file_util::LoadExtension(
282 extension_path_, Manifest::UNPACKED, flags, &error).get());
283
284 if (!extension() ||
285 !extension_l10n_util::ValidateExtensionLocales(
286 extension_path_, extension()->manifest()->value(), &error)) {
287 BrowserThread::PostTask(
288 BrowserThread::UI,
289 FROM_HERE,
290 base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
291 return;
292 }
293
294 BrowserThread::PostTask(
295 BrowserThread::UI,
296 FROM_HERE,
297 base::Bind(&UnpackedInstaller::ShowInstallPrompt, this));
298 }
299
ReportExtensionLoadError(const std::string & error)300 void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) {
301 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
302
303 if (service_weak_.get()) {
304 ExtensionErrorReporter::GetInstance()->ReportLoadError(
305 extension_path_,
306 error,
307 service_weak_->profile(),
308 be_noisy_on_failure_);
309 }
310 }
311
InstallExtension()312 void UnpackedInstaller::InstallExtension() {
313 DCHECK_CURRENTLY_ON(BrowserThread::UI);
314
315 PermissionsUpdater perms_updater(service_weak_->profile());
316 perms_updater.InitializePermissions(extension());
317 perms_updater.GrantActivePermissions(extension());
318
319 service_weak_->OnExtensionInstalled(
320 extension(), syncer::StringOrdinal(), kInstallFlagInstallImmediately);
321 }
322
323 } // namespace extensions
324