password_store_x.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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_x.h" 6 7#include <algorithm> 8#include <map> 9#include <vector> 10 11#include "base/bind.h" 12#include "base/logging.h" 13#include "base/prefs/pref_service.h" 14#include "base/stl_util.h" 15#include "chrome/browser/chrome_notification_types.h" 16#include "chrome/browser/password_manager/password_store_change.h" 17#include "chrome/common/pref_names.h" 18#include "components/user_prefs/pref_registry_syncable.h" 19#include "content/public/browser/browser_thread.h" 20#include "content/public/browser/notification_service.h" 21 22using autofill::PasswordForm; 23using content::BrowserThread; 24using std::vector; 25 26PasswordStoreX::PasswordStoreX(LoginDatabase* login_db, 27 Profile* profile, 28 NativeBackend* backend) 29 : PasswordStoreDefault(login_db, profile), 30 backend_(backend), migration_checked_(!backend), allow_fallback_(false) { 31} 32 33PasswordStoreX::~PasswordStoreX() {} 34 35void PasswordStoreX::AddLoginImpl(const PasswordForm& form) { 36 CheckMigration(); 37 if (use_native_backend() && backend_->AddLogin(form)) { 38 PasswordStoreChangeList changes; 39 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); 40 content::NotificationService::current()->Notify( 41 chrome::NOTIFICATION_LOGINS_CHANGED, 42 content::Source<PasswordStore>(this), 43 content::Details<PasswordStoreChangeList>(&changes)); 44 allow_fallback_ = false; 45 } else if (allow_default_store()) { 46 PasswordStoreDefault::AddLoginImpl(form); 47 } 48} 49 50void PasswordStoreX::UpdateLoginImpl(const PasswordForm& form) { 51 CheckMigration(); 52 if (use_native_backend() && backend_->UpdateLogin(form)) { 53 PasswordStoreChangeList changes; 54 changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form)); 55 content::NotificationService::current()->Notify( 56 chrome::NOTIFICATION_LOGINS_CHANGED, 57 content::Source<PasswordStore>(this), 58 content::Details<PasswordStoreChangeList>(&changes)); 59 allow_fallback_ = false; 60 } else if (allow_default_store()) { 61 PasswordStoreDefault::UpdateLoginImpl(form); 62 } 63} 64 65void PasswordStoreX::RemoveLoginImpl(const PasswordForm& form) { 66 CheckMigration(); 67 if (use_native_backend() && backend_->RemoveLogin(form)) { 68 PasswordStoreChangeList changes; 69 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); 70 content::NotificationService::current()->Notify( 71 chrome::NOTIFICATION_LOGINS_CHANGED, 72 content::Source<PasswordStore>(this), 73 content::Details<PasswordStoreChangeList>(&changes)); 74 allow_fallback_ = false; 75 } else if (allow_default_store()) { 76 PasswordStoreDefault::RemoveLoginImpl(form); 77 } 78} 79 80void PasswordStoreX::RemoveLoginsCreatedBetweenImpl( 81 const base::Time& delete_begin, 82 const base::Time& delete_end) { 83 CheckMigration(); 84 vector<PasswordForm*> forms; 85 if (use_native_backend() && 86 backend_->GetLoginsCreatedBetween(delete_begin, delete_end, &forms) && 87 backend_->RemoveLoginsCreatedBetween(delete_begin, delete_end)) { 88 PasswordStoreChangeList changes; 89 for (vector<PasswordForm*>::const_iterator it = forms.begin(); 90 it != forms.end(); ++it) { 91 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, 92 **it)); 93 } 94 content::NotificationService::current()->Notify( 95 chrome::NOTIFICATION_LOGINS_CHANGED, 96 content::Source<PasswordStore>(this), 97 content::Details<PasswordStoreChangeList>(&changes)); 98 allow_fallback_ = false; 99 } else if (allow_default_store()) { 100 PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(delete_begin, 101 delete_end); 102 } 103 STLDeleteElements(&forms); 104} 105 106namespace { 107struct LoginLessThan { 108 bool operator()(const PasswordForm* a, const PasswordForm* b) { 109 return a->origin < b->origin; 110 } 111}; 112} // anonymous namespace 113 114void PasswordStoreX::SortLoginsByOrigin(NativeBackend::PasswordFormList* list) { 115 // In login_database.cc, the query has ORDER BY origin_url. Simulate that. 116 std::sort(list->begin(), list->end(), LoginLessThan()); 117} 118 119void PasswordStoreX::GetLoginsImpl( 120 const autofill::PasswordForm& form, 121 const ConsumerCallbackRunner& callback_runner) { 122 CheckMigration(); 123 std::vector<autofill::PasswordForm*> matched_forms; 124 if (use_native_backend() && backend_->GetLogins(form, &matched_forms)) { 125 SortLoginsByOrigin(&matched_forms); 126 callback_runner.Run(matched_forms); 127 // The native backend may succeed and return no data even while locked, if 128 // the query did not match anything stored. So we continue to allow fallback 129 // until we perform a write operation, or until a read returns actual data. 130 if (matched_forms.size() > 0) 131 allow_fallback_ = false; 132 } else if (allow_default_store()) { 133 DCHECK(matched_forms.empty()); 134 PasswordStoreDefault::GetLoginsImpl(form, callback_runner); 135 } else { 136 // The consumer will be left hanging unless we reply. 137 callback_runner.Run(matched_forms); 138 } 139} 140 141void PasswordStoreX::GetAutofillableLoginsImpl(GetLoginsRequest* request) { 142 CheckMigration(); 143 if (use_native_backend() && 144 backend_->GetAutofillableLogins(&request->value)) { 145 SortLoginsByOrigin(&request->value); 146 ForwardLoginsResult(request); 147 // See GetLoginsImpl() for why we disallow fallback conditionally here. 148 if (request->value.size() > 0) 149 allow_fallback_ = false; 150 } else if (allow_default_store()) { 151 PasswordStoreDefault::GetAutofillableLoginsImpl(request); 152 } else { 153 // The consumer will be left hanging unless we reply. 154 ForwardLoginsResult(request); 155 } 156} 157 158void PasswordStoreX::GetBlacklistLoginsImpl(GetLoginsRequest* request) { 159 CheckMigration(); 160 if (use_native_backend() && 161 backend_->GetBlacklistLogins(&request->value)) { 162 SortLoginsByOrigin(&request->value); 163 ForwardLoginsResult(request); 164 // See GetLoginsImpl() for why we disallow fallback conditionally here. 165 if (request->value.size() > 0) 166 allow_fallback_ = false; 167 } else if (allow_default_store()) { 168 PasswordStoreDefault::GetBlacklistLoginsImpl(request); 169 } else { 170 // The consumer will be left hanging unless we reply. 171 ForwardLoginsResult(request); 172 } 173} 174 175bool PasswordStoreX::FillAutofillableLogins(vector<PasswordForm*>* forms) { 176 CheckMigration(); 177 if (use_native_backend() && backend_->GetAutofillableLogins(forms)) { 178 // See GetLoginsImpl() for why we disallow fallback conditionally here. 179 if (forms->size() > 0) 180 allow_fallback_ = false; 181 return true; 182 } 183 if (allow_default_store()) 184 return PasswordStoreDefault::FillAutofillableLogins(forms); 185 return false; 186} 187 188bool PasswordStoreX::FillBlacklistLogins(vector<PasswordForm*>* forms) { 189 CheckMigration(); 190 if (use_native_backend() && backend_->GetBlacklistLogins(forms)) { 191 // See GetLoginsImpl() for why we disallow fallback conditionally here. 192 if (forms->size() > 0) 193 allow_fallback_ = false; 194 return true; 195 } 196 if (allow_default_store()) 197 return PasswordStoreDefault::FillBlacklistLogins(forms); 198 return false; 199} 200 201void PasswordStoreX::CheckMigration() { 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 203 if (migration_checked_ || !backend_.get()) 204 return; 205 migration_checked_ = true; 206 ssize_t migrated = MigrateLogins(); 207 if (migrated > 0) { 208 VLOG(1) << "Migrated " << migrated << " passwords to native store."; 209 } else if (migrated == 0) { 210 // As long as we are able to migrate some passwords, we know the native 211 // store is working. But if there is nothing to migrate, the "migration" 212 // can succeed even when the native store would fail. In this case we 213 // allow a later fallback to the default store. Once any later operation 214 // succeeds on the native store, we will no longer allow fallback. 215 allow_fallback_ = true; 216 } else { 217 LOG(WARNING) << "Native password store migration failed! " << 218 "Falling back on default (unencrypted) store."; 219 backend_.reset(NULL); 220 } 221} 222 223bool PasswordStoreX::allow_default_store() { 224 if (allow_fallback_) { 225 LOG(WARNING) << "Native password store failed! " << 226 "Falling back on default (unencrypted) store."; 227 backend_.reset(NULL); 228 // Don't warn again. We'll use the default store because backend_ is NULL. 229 allow_fallback_ = false; 230 } 231 return !backend_.get(); 232} 233 234ssize_t PasswordStoreX::MigrateLogins() { 235 DCHECK(backend_.get()); 236 vector<PasswordForm*> forms; 237 bool ok = PasswordStoreDefault::FillAutofillableLogins(&forms) && 238 PasswordStoreDefault::FillBlacklistLogins(&forms); 239 if (ok) { 240 // We add all the passwords (and blacklist entries) to the native backend 241 // before attempting to remove any from the login database, to make sure we 242 // don't somehow end up with some of the passwords in one store and some in 243 // another. We'll always have at least one intact store this way. 244 for (size_t i = 0; i < forms.size(); ++i) { 245 if (!backend_->AddLogin(*forms[i])) { 246 ok = false; 247 break; 248 } 249 } 250 if (ok) { 251 for (size_t i = 0; i < forms.size(); ++i) { 252 // If even one of these calls to RemoveLoginImpl() succeeds, then we 253 // should prefer the native backend to the now-incomplete login 254 // database. Thus we want to return a success status even in the case 255 // where some fail. The only real problem with this is that we might 256 // leave passwords in the login database and never come back to clean 257 // them out if any of these calls do fail. 258 PasswordStoreDefault::RemoveLoginImpl(*forms[i]); 259 } 260 // Finally, delete the database file itself. We remove the passwords from 261 // it before deleting the file just in case there is some problem deleting 262 // the file (e.g. directory is not writable, but file is), which would 263 // otherwise cause passwords to re-migrate next (or maybe every) time. 264 DeleteAndRecreateDatabaseFile(); 265 } 266 } 267 ssize_t result = ok ? forms.size() : -1; 268 STLDeleteElements(&forms); 269 return result; 270} 271 272#if !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) 273// static 274void PasswordStoreX::RegisterProfilePrefs( 275 user_prefs::PrefRegistrySyncable* registry) { 276 // Normally we should be on the UI thread here, but in tests we might not. 277 registry->RegisterBooleanPref( 278 prefs::kPasswordsUseLocalProfileId, 279 // default: passwords don't use local ids 280 false, 281 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 282} 283 284// static 285bool PasswordStoreX::PasswordsUseLocalProfileId(PrefService* prefs) { 286 // Normally we should be on the UI thread here, but in tests we might not. 287 return prefs->GetBoolean(prefs::kPasswordsUseLocalProfileId); 288} 289 290namespace { 291// This function is a hack to do something not entirely thread safe: the pref 292// service comes from the UI thread, but it's not ref counted. We keep a pointer 293// to it on the DB thread, and need to invoke a method on the UI thread. This 294// function does that for us without requiring ref counting the pref service. 295// TODO(mdm): Fix this if it becomes a problem. Given that this function will 296// be called once ever per profile, it probably will not cause a problem... 297void UISetPasswordsUseLocalProfileId(PrefService* prefs) { 298 prefs->SetBoolean(prefs::kPasswordsUseLocalProfileId, true); 299} 300} // anonymous namespace 301 302// static 303void PasswordStoreX::SetPasswordsUseLocalProfileId(PrefService* prefs) { 304 // This method should work on any thread, but we expect the DB thread. 305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 306 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 307 base::Bind(&UISetPasswordsUseLocalProfileId, prefs)); 308} 309#endif // !defined(OS_MACOSX) && !defined(OS_CHROMEOS) && defined(OS_POSIX) 310