Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / password_manager / password_store_factory.cc
blob81f2a0fd460dedae137f6618166a5b00f5f4c457
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/password_manager/password_store_factory.h"
7 #include "base/command_line.h"
8 #include "base/environment.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "chrome/browser/profiles/incognito_helpers.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/sync/glue/sync_start_util.h"
16 #include "chrome/browser/sync/profile_sync_service.h"
17 #include "chrome/browser/sync/profile_sync_service_factory.h"
18 #include "chrome/browser/web_data_service_factory.h"
19 #include "chrome/common/chrome_constants.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "components/keyed_service/content/browser_context_dependency_manager.h"
22 #include "components/os_crypt/os_crypt_switches.h"
23 #include "components/password_manager/core/browser/affiliated_match_helper.h"
24 #include "components/password_manager/core/browser/affiliation_service.h"
25 #include "components/password_manager/core/browser/affiliation_utils.h"
26 #include "components/password_manager/core/browser/login_database.h"
27 #include "components/password_manager/core/browser/password_store.h"
28 #include "components/password_manager/core/browser/password_store_default.h"
29 #include "components/password_manager/core/common/password_manager_pref_names.h"
30 #include "components/pref_registry/pref_registry_syncable.h"
31 #include "content/public/browser/browser_thread.h"
33 #if defined(OS_WIN)
34 #include "chrome/browser/password_manager/password_manager_util_win.h"
35 #include "chrome/browser/password_manager/password_store_win.h"
36 #include "components/password_manager/core/browser/webdata/password_web_data_service_win.h"
37 #elif defined(OS_MACOSX)
38 #include "chrome/browser/password_manager/password_store_proxy_mac.h"
39 #include "crypto/apple_keychain.h"
40 #include "crypto/mock_apple_keychain.h"
41 #elif defined(OS_CHROMEOS) || defined(OS_ANDROID)
42 // Don't do anything. We're going to use the default store.
43 #elif defined(USE_X11)
44 #include "base/nix/xdg_util.h"
45 #if defined(USE_GNOME_KEYRING)
46 #include "chrome/browser/password_manager/native_backend_gnome_x.h"
47 #endif
48 #if defined(USE_LIBSECRET)
49 #include "base/metrics/field_trial.h"
50 #include "chrome/browser/password_manager/native_backend_libsecret.h"
51 #endif
52 #include "chrome/browser/password_manager/native_backend_kwallet_x.h"
53 #include "chrome/browser/password_manager/password_store_x.h"
54 #endif
56 using password_manager::PasswordStore;
58 namespace {
60 #if !defined(OS_CHROMEOS) && defined(USE_X11)
61 const LocalProfileId kInvalidLocalProfileId =
62 static_cast<LocalProfileId>(0);
63 #endif
65 #if defined(USE_LIBSECRET)
66 const char kLibsecretFieldTrialName[] = "Libsecret";
67 const char kLibsecretFieldTrialDisabledGroupName[] = "Disabled";
68 #endif
70 base::FilePath GetAffiliationDatabasePath(Profile* profile) {
71 DCHECK(profile);
72 return profile->GetPath().Append(chrome::kAffiliationDatabaseFileName);
75 bool ShouldAffiliationBasedMatchingBeActive(Profile* profile) {
76 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
77 if (!password_manager::IsAffiliationBasedMatchingEnabled(*command_line))
78 return false;
80 DCHECK(profile);
81 ProfileSyncService* profile_sync_service =
82 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
83 return profile_sync_service &&
84 profile_sync_service->CanSyncStart() &&
85 profile_sync_service->IsSyncActive() &&
86 profile_sync_service->GetPreferredDataTypes().Has(syncer::PASSWORDS) &&
87 !profile_sync_service->IsUsingSecondaryPassphrase();
90 bool ShouldPropagatingPasswordChangesToWebCredentialsBeEnabled() {
91 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
92 return password_manager::IsPropagatingPasswordChangesToWebCredentialsEnabled(
93 *command_line);
96 void ActivateAffiliationBasedMatching(PasswordStore* password_store,
97 Profile* profile) {
98 DCHECK(password_store);
99 DCHECK(profile);
101 // The PasswordStore is so far the only consumer of the AffiliationService,
102 // therefore the service is owned by the AffiliatedMatchHelper, which in
103 // turn is owned by the PasswordStore.
104 // TODO(engedy): Double-check which request context we want.
105 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner(
106 content::BrowserThread::GetMessageLoopProxyForThread(
107 content::BrowserThread::DB));
108 scoped_ptr<password_manager::AffiliationService> affiliation_service(
109 new password_manager::AffiliationService(db_thread_runner));
110 affiliation_service->Initialize(profile->GetRequestContext(),
111 GetAffiliationDatabasePath(profile));
112 scoped_ptr<password_manager::AffiliatedMatchHelper> affiliated_match_helper(
113 new password_manager::AffiliatedMatchHelper(password_store,
114 affiliation_service.Pass()));
115 affiliated_match_helper->Initialize();
116 password_store->SetAffiliatedMatchHelper(affiliated_match_helper.Pass());
117 password_store->enable_propagating_password_changes_to_web_credentials(
118 ShouldPropagatingPasswordChangesToWebCredentialsBeEnabled());
121 } // namespace
124 PasswordStoreService::PasswordStoreService(
125 scoped_refptr<PasswordStore> password_store)
126 : password_store_(password_store) {}
128 PasswordStoreService::~PasswordStoreService() {}
130 scoped_refptr<PasswordStore> PasswordStoreService::GetPasswordStore() {
131 return password_store_;
134 void PasswordStoreService::Shutdown() {
135 if (password_store_.get())
136 password_store_->Shutdown();
139 // static
140 scoped_refptr<PasswordStore> PasswordStoreFactory::GetForProfile(
141 Profile* profile,
142 ServiceAccessType sat) {
143 if (sat == ServiceAccessType::IMPLICIT_ACCESS && profile->IsOffTheRecord()) {
144 NOTREACHED() << "This profile is OffTheRecord";
145 return nullptr;
148 PasswordStoreFactory* factory = GetInstance();
149 PasswordStoreService* service = static_cast<PasswordStoreService*>(
150 factory->GetServiceForBrowserContext(profile, true));
151 if (!service)
152 return nullptr;
153 return service->GetPasswordStore();
156 // static
157 PasswordStoreFactory* PasswordStoreFactory::GetInstance() {
158 return Singleton<PasswordStoreFactory>::get();
161 // static
162 void PasswordStoreFactory::OnPasswordsSyncedStatePotentiallyChanged(
163 Profile* profile) {
164 scoped_refptr<PasswordStore> password_store =
165 GetForProfile(profile, ServiceAccessType::EXPLICIT_ACCESS);
166 if (!password_store)
167 return;
169 if (ShouldAffiliationBasedMatchingBeActive(profile) &&
170 !password_store->affiliated_match_helper()) {
171 ActivateAffiliationBasedMatching(password_store.get(), profile);
172 } else if (!ShouldAffiliationBasedMatchingBeActive(profile) &&
173 password_store->affiliated_match_helper()) {
174 password_store->SetAffiliatedMatchHelper(
175 make_scoped_ptr<password_manager::AffiliatedMatchHelper>(nullptr));
179 // static
180 void PasswordStoreFactory::TrimOrDeleteAffiliationCache(Profile* profile) {
181 scoped_refptr<PasswordStore> password_store =
182 GetForProfile(profile, ServiceAccessType::EXPLICIT_ACCESS);
183 if (password_store && password_store->affiliated_match_helper()) {
184 password_store->TrimAffiliationCache();
185 } else {
186 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner(
187 content::BrowserThread::GetMessageLoopProxyForThread(
188 content::BrowserThread::DB));
189 password_manager::AffiliationService::DeleteCache(
190 GetAffiliationDatabasePath(profile), db_thread_runner.get());
194 PasswordStoreFactory::PasswordStoreFactory()
195 : BrowserContextKeyedServiceFactory(
196 "PasswordStore",
197 BrowserContextDependencyManager::GetInstance()) {
198 DependsOn(WebDataServiceFactory::GetInstance());
201 PasswordStoreFactory::~PasswordStoreFactory() {}
203 #if !defined(OS_CHROMEOS) && defined(USE_X11)
204 LocalProfileId PasswordStoreFactory::GetLocalProfileId(
205 PrefService* prefs) const {
206 LocalProfileId id =
207 prefs->GetInteger(password_manager::prefs::kLocalProfileId);
208 if (id == kInvalidLocalProfileId) {
209 // Note that there are many more users than this. Thus, by design, this is
210 // not a unique id. However, it is large enough that it is very unlikely
211 // that it would be repeated twice on a single machine. It is still possible
212 // for that to occur though, so the potential results of it actually
213 // happening should be considered when using this value.
214 static const LocalProfileId kLocalProfileIdMask =
215 static_cast<LocalProfileId>((1 << 24) - 1);
216 do {
217 id = rand() & kLocalProfileIdMask;
218 // TODO(mdm): scan other profiles to make sure they are not using this id?
219 } while (id == kInvalidLocalProfileId);
220 prefs->SetInteger(password_manager::prefs::kLocalProfileId, id);
222 return id;
224 #endif
226 KeyedService* PasswordStoreFactory::BuildServiceInstanceFor(
227 content::BrowserContext* context) const {
228 #if defined(OS_WIN)
229 password_manager_util_win::DelayReportOsPassword();
230 #endif
231 Profile* profile = static_cast<Profile*>(context);
233 // Given that LoginDatabase::Init() takes ~100ms on average; it will be called
234 // by PasswordStore::Init() on the background thread to avoid UI jank.
235 base::FilePath login_db_file_path = profile->GetPath();
236 login_db_file_path = login_db_file_path.Append(chrome::kLoginDataFileName);
237 scoped_ptr<password_manager::LoginDatabase> login_db(
238 new password_manager::LoginDatabase(login_db_file_path));
240 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner(
241 base::ThreadTaskRunnerHandle::Get());
242 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner(
243 content::BrowserThread::GetMessageLoopProxyForThread(
244 content::BrowserThread::DB));
246 scoped_refptr<PasswordStore> ps;
247 #if defined(OS_WIN)
248 ps = new PasswordStoreWin(main_thread_runner, db_thread_runner,
249 login_db.Pass(),
250 WebDataServiceFactory::GetPasswordWebDataForProfile(
251 profile, ServiceAccessType::EXPLICIT_ACCESS));
252 #elif defined(OS_MACOSX)
253 scoped_ptr<crypto::AppleKeychain> keychain(
254 base::CommandLine::ForCurrentProcess()->HasSwitch(
255 os_crypt::switches::kUseMockKeychain)
256 ? new crypto::MockAppleKeychain()
257 : new crypto::AppleKeychain());
258 ps = new PasswordStoreProxyMac(main_thread_runner, keychain.Pass(),
259 login_db.Pass(), profile->GetPrefs());
260 #elif defined(OS_CHROMEOS) || defined(OS_ANDROID)
261 // For now, we use PasswordStoreDefault. We might want to make a native
262 // backend for PasswordStoreX (see below) in the future though.
263 ps = new password_manager::PasswordStoreDefault(
264 main_thread_runner, db_thread_runner, login_db.Pass());
265 #elif defined(USE_X11)
266 // On POSIX systems, we try to use the "native" password management system of
267 // the desktop environment currently running, allowing GNOME Keyring in XFCE.
268 // (In all cases we fall back on the basic store in case of failure.)
269 base::nix::DesktopEnvironment desktop_env = GetDesktopEnvironment();
270 base::nix::DesktopEnvironment used_desktop_env;
271 std::string store_type =
272 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
273 switches::kPasswordStore);
274 LinuxBackendUsed used_backend = PLAINTEXT;
275 if (store_type == "kwallet") {
276 used_desktop_env = base::nix::DESKTOP_ENVIRONMENT_KDE4;
277 } else if (store_type == "gnome") {
278 used_desktop_env = base::nix::DESKTOP_ENVIRONMENT_GNOME;
279 } else if (store_type == "basic") {
280 used_desktop_env = base::nix::DESKTOP_ENVIRONMENT_OTHER;
281 } else {
282 // Detect the store to use automatically.
283 used_desktop_env = desktop_env;
284 const char* name = base::nix::GetDesktopEnvironmentName(desktop_env);
285 VLOG(1) << "Password storage detected desktop environment: "
286 << (name ? name : "(unknown)");
289 PrefService* prefs = profile->GetPrefs();
290 LocalProfileId id = GetLocalProfileId(prefs);
292 scoped_ptr<PasswordStoreX::NativeBackend> backend;
293 if (used_desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE4) {
294 // KDE3 didn't use DBus, which our KWallet store uses.
295 VLOG(1) << "Trying KWallet for password storage.";
296 backend.reset(new NativeBackendKWallet(id));
297 if (backend->Init()) {
298 VLOG(1) << "Using KWallet for password storage.";
299 used_backend = KWALLET;
300 } else {
301 backend.reset();
303 } else if (used_desktop_env == base::nix::DESKTOP_ENVIRONMENT_GNOME ||
304 used_desktop_env == base::nix::DESKTOP_ENVIRONMENT_UNITY ||
305 used_desktop_env == base::nix::DESKTOP_ENVIRONMENT_XFCE) {
306 #if defined(USE_LIBSECRET)
307 if (base::FieldTrialList::FindFullName(kLibsecretFieldTrialName) !=
308 kLibsecretFieldTrialDisabledGroupName) {
309 VLOG(1) << "Trying libsecret for password storage.";
310 backend.reset(new NativeBackendLibsecret(id));
311 if (backend->Init()) {
312 VLOG(1) << "Using libsecret keyring for password storage.";
313 used_backend = LIBSECRET;
314 } else {
315 backend.reset();
318 #endif // defined(USE_LIBSECRET)
319 if (!backend.get()) {
320 #if defined(USE_GNOME_KEYRING)
321 VLOG(1) << "Trying GNOME keyring for password storage.";
322 backend.reset(new NativeBackendGnome(id));
323 if (backend->Init()) {
324 VLOG(1) << "Using GNOME keyring for password storage.";
325 used_backend = GNOME_KEYRING;
326 } else {
327 backend.reset();
329 #endif // defined(USE_GNOME_KEYRING)
333 if (!backend.get()) {
334 LOG(WARNING) << "Using basic (unencrypted) store for password storage. "
335 "See http://code.google.com/p/chromium/wiki/LinuxPasswordStorage for "
336 "more information about password storage options.";
339 ps = new PasswordStoreX(main_thread_runner, db_thread_runner, login_db.Pass(),
340 backend.release());
341 RecordBackendStatistics(desktop_env, store_type, used_backend);
342 #elif defined(USE_OZONE)
343 ps = new password_manager::PasswordStoreDefault(
344 main_thread_runner, db_thread_runner, login_db.Pass());
345 #else
346 NOTIMPLEMENTED();
347 #endif
348 if (!ps.get() ||
349 !ps->Init(
350 sync_start_util::GetFlareForSyncableService(profile->GetPath()))) {
351 NOTREACHED() << "Could not initialize password manager.";
352 return nullptr;
355 return new PasswordStoreService(ps);
358 void PasswordStoreFactory::RegisterProfilePrefs(
359 user_prefs::PrefRegistrySyncable* registry) {
360 #if !defined(OS_CHROMEOS) && defined(USE_X11)
361 // Notice that the preprocessor conditions above are exactly those that will
362 // result in using PasswordStoreX in BuildServiceInstanceFor().
363 registry->RegisterIntegerPref(password_manager::prefs::kLocalProfileId,
364 kInvalidLocalProfileId);
365 #endif
368 content::BrowserContext* PasswordStoreFactory::GetBrowserContextToUse(
369 content::BrowserContext* context) const {
370 return chrome::GetBrowserContextRedirectedInIncognito(context);
373 bool PasswordStoreFactory::ServiceIsNULLWhileTesting() const {
374 return true;
377 #if defined(USE_X11)
378 base::nix::DesktopEnvironment PasswordStoreFactory::GetDesktopEnvironment() {
379 scoped_ptr<base::Environment> env(base::Environment::Create());
380 return base::nix::GetDesktopEnvironment(env.get());
383 void PasswordStoreFactory::RecordBackendStatistics(
384 base::nix::DesktopEnvironment desktop_env,
385 const std::string& command_line_flag,
386 LinuxBackendUsed used_backend) {
387 LinuxBackendUsage usage = OTHER_PLAINTEXT;
388 if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE4) {
389 if (command_line_flag == "kwallet") {
390 usage = used_backend == KWALLET ? KDE_KWALLETFLAG_KWALLET
391 : KDE_KWALLETFLAG_PLAINTEXT;
392 } else if (command_line_flag == "gnome") {
393 usage = used_backend == PLAINTEXT
394 ? KDE_GNOMEFLAG_PLAINTEXT
395 : (used_backend == GNOME_KEYRING ? KDE_GNOMEFLAG_KEYRING
396 : KDE_GNOMEFLAG_LIBSECRET);
397 } else if (command_line_flag == "basic") {
398 usage = KDE_BASICFLAG_PLAINTEXT;
399 } else {
400 usage =
401 used_backend == KWALLET ? KDE_NOFLAG_KWALLET : KDE_NOFLAG_PLAINTEXT;
403 } else if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_GNOME ||
404 desktop_env == base::nix::DESKTOP_ENVIRONMENT_UNITY ||
405 desktop_env == base::nix::DESKTOP_ENVIRONMENT_XFCE) {
406 if (command_line_flag == "kwallet") {
407 usage = used_backend == KWALLET ? GNOME_KWALLETFLAG_KWALLET
408 : GNOME_KWALLETFLAG_PLAINTEXT;
409 } else if (command_line_flag == "gnome") {
410 usage = used_backend == PLAINTEXT
411 ? GNOME_GNOMEFLAG_PLAINTEXT
412 : (used_backend == GNOME_KEYRING ? GNOME_GNOMEFLAG_KEYRING
413 : GNOME_GNOMEFLAG_LIBSECRET);
414 } else if (command_line_flag == "basic") {
415 usage = GNOME_BASICFLAG_PLAINTEXT;
416 } else {
417 usage = used_backend == PLAINTEXT
418 ? GNOME_NOFLAG_PLAINTEXT
419 : (used_backend == GNOME_KEYRING ? GNOME_NOFLAG_KEYRING
420 : GNOME_NOFLAG_LIBSECRET);
422 } else {
423 // It is neither Gnome nor KDE environment.
424 switch (used_backend) {
425 case PLAINTEXT:
426 usage = OTHER_PLAINTEXT;
427 break;
428 case KWALLET:
429 usage = OTHER_KWALLET;
430 break;
431 case GNOME_KEYRING:
432 usage = OTHER_KEYRING;
433 break;
434 case LIBSECRET:
435 usage = OTHER_LIBSECRET;
436 break;
439 UMA_HISTOGRAM_ENUMERATION("PasswordManager.LinuxBackendStatistics", usage,
440 MAX_BACKEND_USAGE_VALUE);
442 #endif