password_store_x.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1// Copyright (c) 2010 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 <map> 8#include <vector> 9 10#include "base/logging.h" 11#include "base/stl_util-inl.h" 12#include "chrome/browser/password_manager/password_store_change.h" 13#include "chrome/common/notification_service.h" 14#include "content/browser/browser_thread.h" 15 16using std::vector; 17using webkit_glue::PasswordForm; 18 19PasswordStoreX::PasswordStoreX(LoginDatabase* login_db, 20 Profile* profile, 21 WebDataService* web_data_service, 22 NativeBackend* backend) 23 : PasswordStoreDefault(login_db, profile, web_data_service), 24 backend_(backend), migration_checked_(!backend), allow_fallback_(false) { 25} 26 27PasswordStoreX::~PasswordStoreX() { 28} 29 30void PasswordStoreX::AddLoginImpl(const PasswordForm& form) { 31 CheckMigration(); 32 if (use_native_backend() && backend_->AddLogin(form)) { 33 PasswordStoreChangeList changes; 34 changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); 35 NotificationService::current()->Notify( 36 NotificationType::LOGINS_CHANGED, 37 Source<PasswordStore>(this), 38 Details<PasswordStoreChangeList>(&changes)); 39 allow_fallback_ = false; 40 } else if (allow_default_store()) { 41 PasswordStoreDefault::AddLoginImpl(form); 42 } 43} 44 45void PasswordStoreX::UpdateLoginImpl(const PasswordForm& form) { 46 CheckMigration(); 47 if (use_native_backend() && backend_->UpdateLogin(form)) { 48 PasswordStoreChangeList changes; 49 changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form)); 50 NotificationService::current()->Notify( 51 NotificationType::LOGINS_CHANGED, 52 Source<PasswordStore>(this), 53 Details<PasswordStoreChangeList>(&changes)); 54 allow_fallback_ = false; 55 } else if (allow_default_store()) { 56 PasswordStoreDefault::UpdateLoginImpl(form); 57 } 58} 59 60void PasswordStoreX::RemoveLoginImpl(const PasswordForm& form) { 61 CheckMigration(); 62 if (use_native_backend() && backend_->RemoveLogin(form)) { 63 PasswordStoreChangeList changes; 64 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); 65 NotificationService::current()->Notify( 66 NotificationType::LOGINS_CHANGED, 67 Source<PasswordStore>(this), 68 Details<PasswordStoreChangeList>(&changes)); 69 allow_fallback_ = false; 70 } else if (allow_default_store()) { 71 PasswordStoreDefault::RemoveLoginImpl(form); 72 } 73} 74 75void PasswordStoreX::RemoveLoginsCreatedBetweenImpl( 76 const base::Time& delete_begin, 77 const base::Time& delete_end) { 78 CheckMigration(); 79 vector<PasswordForm*> forms; 80 if (use_native_backend() && 81 backend_->GetLoginsCreatedBetween(delete_begin, delete_end, &forms) && 82 backend_->RemoveLoginsCreatedBetween(delete_begin, delete_end)) { 83 PasswordStoreChangeList changes; 84 for (vector<PasswordForm*>::const_iterator it = forms.begin(); 85 it != forms.end(); ++it) { 86 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, 87 **it)); 88 } 89 NotificationService::current()->Notify( 90 NotificationType::LOGINS_CHANGED, 91 Source<PasswordStore>(this), 92 Details<PasswordStoreChangeList>(&changes)); 93 allow_fallback_ = false; 94 } else if (allow_default_store()) { 95 PasswordStoreDefault::RemoveLoginsCreatedBetweenImpl(delete_begin, 96 delete_end); 97 } 98 STLDeleteElements(&forms); 99} 100 101void PasswordStoreX::GetLoginsImpl(GetLoginsRequest* request, 102 const PasswordForm& form) { 103 CheckMigration(); 104 vector<PasswordForm*> forms; 105 if (use_native_backend() && backend_->GetLogins(form, &forms)) { 106 NotifyConsumer(request, forms); 107 allow_fallback_ = false; 108 } else if (allow_default_store()) { 109 PasswordStoreDefault::GetLoginsImpl(request, form); 110 } else { 111 // The consumer will be left hanging unless we reply. 112 NotifyConsumer(request, forms); 113 } 114} 115 116void PasswordStoreX::GetAutofillableLoginsImpl(GetLoginsRequest* request) { 117 CheckMigration(); 118 vector<PasswordForm*> forms; 119 if (use_native_backend() && backend_->GetAutofillableLogins(&forms)) { 120 NotifyConsumer(request, forms); 121 allow_fallback_ = false; 122 } else if (allow_default_store()) { 123 PasswordStoreDefault::GetAutofillableLoginsImpl(request); 124 } else { 125 // The consumer will be left hanging unless we reply. 126 NotifyConsumer(request, forms); 127 } 128} 129 130void PasswordStoreX::GetBlacklistLoginsImpl(GetLoginsRequest* request) { 131 CheckMigration(); 132 vector<PasswordForm*> forms; 133 if (use_native_backend() && backend_->GetBlacklistLogins(&forms)) { 134 NotifyConsumer(request, forms); 135 allow_fallback_ = false; 136 } else if (allow_default_store()) { 137 PasswordStoreDefault::GetBlacklistLoginsImpl(request); 138 } else { 139 // The consumer will be left hanging unless we reply. 140 NotifyConsumer(request, forms); 141 } 142} 143 144bool PasswordStoreX::FillAutofillableLogins(vector<PasswordForm*>* forms) { 145 CheckMigration(); 146 if (use_native_backend() && backend_->GetAutofillableLogins(forms)) { 147 allow_fallback_ = false; 148 return true; 149 } 150 if (allow_default_store()) 151 return PasswordStoreDefault::FillAutofillableLogins(forms); 152 return false; 153} 154 155bool PasswordStoreX::FillBlacklistLogins(vector<PasswordForm*>* forms) { 156 CheckMigration(); 157 if (use_native_backend() && backend_->GetBlacklistLogins(forms)) { 158 allow_fallback_ = false; 159 return true; 160 } 161 if (allow_default_store()) 162 return PasswordStoreDefault::FillBlacklistLogins(forms); 163 return false; 164} 165 166void PasswordStoreX::CheckMigration() { 167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 168 if (migration_checked_ || !backend_.get()) 169 return; 170 migration_checked_ = true; 171 ssize_t migrated = MigrateLogins(); 172 if (migrated > 0) { 173 VLOG(1) << "Migrated " << migrated << " passwords to native store."; 174 } else if (migrated == 0) { 175 // As long as we are able to migrate some passwords, we know the native 176 // store is working. But if there is nothing to migrate, the "migration" 177 // can succeed even when the native store would fail. In this case we 178 // allow a later fallback to the default store. Once any later operation 179 // succeeds on the native store, we will no longer allow it. 180 allow_fallback_ = true; 181 } else { 182 LOG(WARNING) << "Native password store migration failed! " << 183 "Falling back on default (unencrypted) store."; 184 backend_.reset(NULL); 185 } 186} 187 188bool PasswordStoreX::allow_default_store() { 189 if (allow_fallback_) { 190 LOG(WARNING) << "Native password store failed! " << 191 "Falling back on default (unencrypted) store."; 192 backend_.reset(NULL); 193 // Don't warn again. We'll use the default store because backend_ is NULL. 194 allow_fallback_ = false; 195 } 196 return !backend_.get(); 197} 198 199ssize_t PasswordStoreX::MigrateLogins() { 200 DCHECK(backend_.get()); 201 vector<PasswordForm*> forms; 202 bool ok = PasswordStoreDefault::FillAutofillableLogins(&forms) && 203 PasswordStoreDefault::FillBlacklistLogins(&forms); 204 if (ok) { 205 // We add all the passwords (and blacklist entries) to the native backend 206 // before attempting to remove any from the login database, to make sure we 207 // don't somehow end up with some of the passwords in one store and some in 208 // another. We'll always have at least one intact store this way. 209 for (size_t i = 0; i < forms.size(); ++i) { 210 if (!backend_->AddLogin(*forms[i])) { 211 ok = false; 212 break; 213 } 214 } 215 if (ok) { 216 for (size_t i = 0; i < forms.size(); ++i) { 217 // If even one of these calls to RemoveLoginImpl() succeeds, then we 218 // should prefer the native backend to the now-incomplete login 219 // database. Thus we want to return a success status even in the case 220 // where some fail. The only real problem with this is that we might 221 // leave passwords in the login database and never come back to clean 222 // them out if any of these calls do fail. 223 PasswordStoreDefault::RemoveLoginImpl(*forms[i]); 224 } 225 // Finally, delete the database file itself. We remove the passwords from 226 // it before deleting the file just in case there is some problem deleting 227 // the file (e.g. directory is not writable, but file is), which would 228 // otherwise cause passwords to re-migrate next (or maybe every) time. 229 DeleteAndRecreateDatabaseFile(); 230 } 231 } 232 ssize_t result = ok ? forms.size() : -1; 233 STLDeleteElements(&forms); 234 return result; 235} 236