password_store_factory.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/password_manager/password_store_factory.h"
6
7#include "base/command_line.h"
8#include "base/environment.h"
9#include "base/prefs/pref_service.h"
10#include "chrome/browser/password_manager/login_database.h"
11#include "chrome/browser/password_manager/password_store.h"
12#include "chrome/browser/password_manager/password_store_default.h"
13#include "chrome/browser/profiles/profile_dependency_manager.h"
14#include "chrome/browser/webdata/web_data_service.h"
15#include "chrome/browser/webdata/web_data_service_factory.h"
16#include "chrome/common/chrome_constants.h"
17#include "chrome/common/chrome_switches.h"
18#include "chrome/common/pref_names.h"
19#include "components/user_prefs/pref_registry_syncable.h"
20
21#if defined(OS_WIN)
22#include "chrome/browser/password_manager/password_store_win.h"
23#elif defined(OS_MACOSX)
24#include "chrome/browser/password_manager/password_store_mac.h"
25#include "crypto/apple_keychain.h"
26#include "crypto/mock_apple_keychain.h"
27#elif defined(OS_CHROMEOS) || defined(OS_ANDROID)
28// Don't do anything. We're going to use the default store.
29#elif defined(OS_POSIX)
30#include "base/nix/xdg_util.h"
31#if defined(USE_GNOME_KEYRING)
32#include "chrome/browser/password_manager/native_backend_gnome_x.h"
33#endif
34#include "chrome/browser/password_manager/native_backend_kwallet_x.h"
35#include "chrome/browser/password_manager/password_store_x.h"
36#endif
37
38#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) && \
39    defined(OS_POSIX)
40namespace {
41
42const LocalProfileId kInvalidLocalProfileId =
43    static_cast<LocalProfileId>(0);
44
45}  // namespace
46#endif
47
48scoped_refptr<PasswordStore> PasswordStoreFactory::GetForProfile(
49    Profile* profile,
50    Profile::ServiceAccessType sat) {
51  if (sat == Profile::IMPLICIT_ACCESS && profile->IsOffTheRecord()) {
52    NOTREACHED() << "This profile is OffTheRecord";
53    return NULL;
54  }
55
56  return static_cast<PasswordStore*>(
57      GetInstance()->GetServiceForProfile(profile, true).get());
58}
59
60// static
61PasswordStoreFactory* PasswordStoreFactory::GetInstance() {
62  return Singleton<PasswordStoreFactory>::get();
63}
64
65PasswordStoreFactory::PasswordStoreFactory()
66    : RefcountedProfileKeyedServiceFactory(
67        "PasswordStore",
68        ProfileDependencyManager::GetInstance()) {
69  DependsOn(WebDataServiceFactory::GetInstance());
70}
71
72PasswordStoreFactory::~PasswordStoreFactory() {}
73
74#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) && \
75    defined(OS_POSIX)
76LocalProfileId PasswordStoreFactory::GetLocalProfileId(
77    PrefService* prefs) const {
78  LocalProfileId id = prefs->GetInteger(prefs::kLocalProfileId);
79  if (id == kInvalidLocalProfileId) {
80    // Note that there are many more users than this. Thus, by design, this is
81    // not a unique id. However, it is large enough that it is very unlikely
82    // that it would be repeated twice on a single machine. It is still possible
83    // for that to occur though, so the potential results of it actually
84    // happening should be considered when using this value.
85    static const LocalProfileId kLocalProfileIdMask =
86        static_cast<LocalProfileId>((1 << 24) - 1);
87    do {
88      id = rand() & kLocalProfileIdMask;
89      // TODO(mdm): scan other profiles to make sure they are not using this id?
90    } while (id == kInvalidLocalProfileId);
91    prefs->SetInteger(prefs::kLocalProfileId, id);
92  }
93  return id;
94}
95#endif
96
97scoped_refptr<RefcountedProfileKeyedService>
98PasswordStoreFactory::BuildServiceInstanceFor(Profile* profile) const {
99  scoped_refptr<PasswordStore> ps;
100  base::FilePath login_db_file_path = profile->GetPath();
101  login_db_file_path = login_db_file_path.Append(chrome::kLoginDataFileName);
102  LoginDatabase* login_db = new LoginDatabase();
103  {
104    // TODO(paivanof@gmail.com): execution of login_db->Init() should go
105    // to DB thread. http://crbug.com/138903
106    base::ThreadRestrictions::ScopedAllowIO allow_io;
107    if (!login_db->Init(login_db_file_path)) {
108      LOG(ERROR) << "Could not initialize login database.";
109      delete login_db;
110      return NULL;
111    }
112  }
113#if defined(OS_WIN)
114  ps = new PasswordStoreWin(
115      login_db, profile,
116      WebDataService::FromBrowserContext(profile));
117#elif defined(OS_MACOSX)
118  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseMockKeychain)) {
119    ps = new PasswordStoreMac(new crypto::MockAppleKeychain(), login_db);
120  } else {
121    ps = new PasswordStoreMac(new crypto::AppleKeychain(), login_db);
122  }
123#elif defined(OS_CHROMEOS) || defined(OS_ANDROID)
124  // For now, we use PasswordStoreDefault. We might want to make a native
125  // backend for PasswordStoreX (see below) in the future though.
126  ps = new PasswordStoreDefault(login_db, profile);
127#elif defined(OS_POSIX)
128  // On POSIX systems, we try to use the "native" password management system of
129  // the desktop environment currently running, allowing GNOME Keyring in XFCE.
130  // (In all cases we fall back on the basic store in case of failure.)
131  base::nix::DesktopEnvironment desktop_env;
132  std::string store_type =
133      CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
134          switches::kPasswordStore);
135  if (store_type == "kwallet") {
136    desktop_env = base::nix::DESKTOP_ENVIRONMENT_KDE4;
137  } else if (store_type == "gnome") {
138    desktop_env = base::nix::DESKTOP_ENVIRONMENT_GNOME;
139  } else if (store_type == "basic") {
140    desktop_env = base::nix::DESKTOP_ENVIRONMENT_OTHER;
141  } else {
142    // Detect the store to use automatically.
143    scoped_ptr<base::Environment> env(base::Environment::Create());
144    desktop_env = base::nix::GetDesktopEnvironment(env.get());
145    const char* name = base::nix::GetDesktopEnvironmentName(desktop_env);
146    VLOG(1) << "Password storage detected desktop environment: "
147            << (name ? name : "(unknown)");
148  }
149
150  PrefService* prefs = profile->GetPrefs();
151  LocalProfileId id = GetLocalProfileId(prefs);
152
153  scoped_ptr<PasswordStoreX::NativeBackend> backend;
154  if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE4) {
155    // KDE3 didn't use DBus, which our KWallet store uses.
156    VLOG(1) << "Trying KWallet for password storage.";
157    backend.reset(new NativeBackendKWallet(id, prefs));
158    if (backend->Init())
159      VLOG(1) << "Using KWallet for password storage.";
160    else
161      backend.reset();
162  } else if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_GNOME ||
163             desktop_env == base::nix::DESKTOP_ENVIRONMENT_UNITY ||
164             desktop_env == base::nix::DESKTOP_ENVIRONMENT_XFCE) {
165#if defined(USE_GNOME_KEYRING)
166    VLOG(1) << "Trying GNOME keyring for password storage.";
167    backend.reset(new NativeBackendGnome(id, prefs));
168    if (backend->Init())
169      VLOG(1) << "Using GNOME keyring for password storage.";
170    else
171      backend.reset();
172#endif  // defined(USE_GNOME_KEYRING)
173  }
174
175  if (!backend.get()) {
176    LOG(WARNING) << "Using basic (unencrypted) store for password storage. "
177        "See http://code.google.com/p/chromium/wiki/LinuxPasswordStorage for "
178        "more information about password storage options.";
179  }
180
181  ps = new PasswordStoreX(login_db, profile, backend.release());
182#else
183  NOTIMPLEMENTED();
184#endif
185  if (!ps)
186    delete login_db;
187
188  if (!ps || !ps->Init()) {
189    NOTREACHED() << "Could not initialize password manager.";
190    return NULL;
191  }
192
193  return ps;
194}
195
196void PasswordStoreFactory::RegisterUserPrefs(PrefRegistrySyncable* registry) {
197#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) \
198  && defined(OS_POSIX)
199  registry->RegisterIntegerPref(prefs::kLocalProfileId,
200                                kInvalidLocalProfileId,
201                                PrefRegistrySyncable::UNSYNCABLE_PREF);
202
203  // Notice that the preprocessor conditions above are exactly those that will
204  // result in using PasswordStoreX in CreatePasswordStore() below.
205  PasswordStoreX::RegisterUserPrefs(registry);
206#endif
207}
208
209bool PasswordStoreFactory::ServiceRedirectedInIncognito() const {
210  return true;
211}
212
213bool PasswordStoreFactory::ServiceIsNULLWhileTesting() const {
214  return true;
215}
216