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