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