Home | History | Annotate | Download | only in password_manager
      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