password_store_x.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 113namespace { 114struct LoginLessThan { 115 bool operator()(const PasswordForm* a, const PasswordForm* b) { 116 return a->origin < b->origin; 117 } 118}; 119} // anonymous namespace 120 121void PasswordStoreX::SortLoginsByOrigin(NativeBackend::PasswordFormList* list) { 122 // In login_database.cc, the query has ORDER BY origin_url. Simulate that. 123 std::sort(list->begin(), list->end(), LoginLessThan()); 124} 125 126void PasswordStoreX::GetLoginsImpl( 127 const autofill::PasswordForm& form, 128 AuthorizationPromptPolicy prompt_policy, 129 const ConsumerCallbackRunner& callback_runner) { 130 CheckMigration(); 131 std::vector<autofill::PasswordForm*> matched_forms; 132 if (use_native_backend() && backend_->GetLogins(form, &matched_forms)) { 133 SortLoginsByOrigin(&matched_forms); 134 // The native backend may succeed and return no data even while locked, if 135 // the query did not match anything stored. So we continue to allow fallback 136 // until we perform a write operation, or until a read returns actual data. 137 if (matched_forms.size() > 0) 138 allow_fallback_ = false; 139 } else if (allow_default_store()) { 140 DCHECK(matched_forms.empty()); 141 PasswordStoreDefault::GetLoginsImpl(form, prompt_policy, callback_runner); 142 return; 143 } 144 // The consumer will be left hanging unless we reply. 145 callback_runner.Run(matched_forms); 146} 147 148void PasswordStoreX::GetAutofillableLoginsImpl(GetLoginsRequest* request) { 149 CheckMigration(); 150 if (use_native_backend() && 151 backend_->GetAutofillableLogins(request->result())) { 152 SortLoginsByOrigin(request->result()); 153 // See GetLoginsImpl() for why we disallow fallback conditionally here. 154 if (request->result()->size() > 0) 155 allow_fallback_ = false; 156 } else if (allow_default_store()) { 157 PasswordStoreDefault::GetAutofillableLoginsImpl(request); 158 return; 159 } 160 // The consumer will be left hanging unless we reply. 161 ForwardLoginsResult(request); 162} 163 164void PasswordStoreX::GetBlacklistLoginsImpl(GetLoginsRequest* request) { 165 CheckMigration(); 166 if (use_native_backend() && 167 backend_->GetBlacklistLogins(request->result())) { 168 SortLoginsByOrigin(request->result()); 169 // See GetLoginsImpl() for why we disallow fallback conditionally here. 170 if (request->result()->size() > 0) 171 allow_fallback_ = false; 172 } else if (allow_default_store()) { 173 PasswordStoreDefault::GetBlacklistLoginsImpl(request); 174 return; 175 } 176 // The consumer will be left hanging unless we reply. 177 ForwardLoginsResult(request); 178} 179 180bool PasswordStoreX::FillAutofillableLogins(vector<PasswordForm*>* forms) { 181 CheckMigration(); 182 if (use_native_backend() && backend_->GetAutofillableLogins(forms)) { 183 // See GetLoginsImpl() for why we disallow fallback conditionally here. 184 if (forms->size() > 0) 185 allow_fallback_ = false; 186 return true; 187 } 188 if (allow_default_store()) 189 return PasswordStoreDefault::FillAutofillableLogins(forms); 190 return false; 191} 192 193bool PasswordStoreX::FillBlacklistLogins(vector<PasswordForm*>* forms) { 194 CheckMigration(); 195 if (use_native_backend() && backend_->GetBlacklistLogins(forms)) { 196 // See GetLoginsImpl() for why we disallow fallback conditionally here. 197 if (forms->size() > 0) 198 allow_fallback_ = false; 199 return true; 200 } 201 if (allow_default_store()) 202 return PasswordStoreDefault::FillBlacklistLogins(forms); 203 return false; 204} 205 206void PasswordStoreX::CheckMigration() { 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 208 if (migration_checked_ || !backend_.get()) 209 return; 210 migration_checked_ = true; 211 ssize_t migrated = MigrateLogins(); 212 if (migrated > 0) { 213 VLOG(1) << "Migrated " << migrated << " passwords to native store."; 214 } else if (migrated == 0) { 215 // As long as we are able to migrate some passwords, we know the native 216 // store is working. But if there is nothing to migrate, the "migration" 217 // can succeed even when the native store would fail. In this case we 218 // allow a later fallback to the default store. Once any later operation 219 // succeeds on the native store, we will no longer allow fallback. 220 allow_fallback_ = true; 221 } else { 222 LOG(WARNING) << "Native password store migration failed! " << 223 "Falling back on default (unencrypted) store."; 224 backend_.reset(NULL); 225 } 226} 227 228bool PasswordStoreX::allow_default_store() { 229 if (allow_fallback_) { 230 LOG(WARNING) << "Native password store failed! " << 231 "Falling back on default (unencrypted) store."; 232 backend_.reset(NULL); 233 // Don't warn again. We'll use the default store because backend_ is NULL. 234 allow_fallback_ = false; 235 } 236 return !backend_.get(); 237} 238 239ssize_t PasswordStoreX::MigrateLogins() { 240 DCHECK(backend_.get()); 241 vector<PasswordForm*> forms; 242 bool ok = PasswordStoreDefault::FillAutofillableLogins(&forms) && 243 PasswordStoreDefault::FillBlacklistLogins(&forms); 244 if (ok) { 245 // We add all the passwords (and blacklist entries) to the native backend 246 // before attempting to remove any from the login database, to make sure we 247 // don't somehow end up with some of the passwords in one store and some in 248 // another. We'll always have at least one intact store this way. 249 for (size_t i = 0; i < forms.size(); ++i) { 250 PasswordStoreChangeList changes; 251 if (!AddLoginToBackend(backend_, *forms[i], &changes)) { 252 ok = false; 253 break; 254 } 255 } 256 if (ok) { 257 for (size_t i = 0; i < forms.size(); ++i) { 258 // If even one of these calls to RemoveLoginImpl() succeeds, then we 259 // should prefer the native backend to the now-incomplete login 260 // database. Thus we want to return a success status even in the case 261 // where some fail. The only real problem with this is that we might 262 // leave passwords in the login database and never come back to clean 263 // them out if any of these calls do fail. 264 PasswordStoreDefault::RemoveLoginImpl(*forms[i]); 265 } 266 // Finally, delete the database file itself. We remove the passwords from 267 // it before deleting the file just in case there is some problem deleting 268 // the file (e.g. directory is not writable, but file is), which would 269 // otherwise cause passwords to re-migrate next (or maybe every) time. 270 DeleteAndRecreateDatabaseFile(); 271 } 272 } 273 ssize_t result = ok ? forms.size() : -1; 274 STLDeleteElements(&forms); 275 return result; 276} 277