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