1 // Copyright (c) 2011 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 "content/browser/browser_thread.h" 14 #include "content/common/notification_service.h" 15 16 using std::vector; 17 using webkit_glue::PasswordForm; 18 19 PasswordStoreX::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 27 PasswordStoreX::~PasswordStoreX() { 28 } 29 30 void 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 45 void 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 60 void 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 75 void 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 101 void PasswordStoreX::GetLoginsImpl(GetLoginsRequest* request, 102 const PasswordForm& form) { 103 CheckMigration(); 104 if (use_native_backend() && backend_->GetLogins(form, &request->value)) { 105 ForwardLoginsResult(request); 106 allow_fallback_ = false; 107 } else if (allow_default_store()) { 108 PasswordStoreDefault::GetLoginsImpl(request, form); 109 } else { 110 // The consumer will be left hanging unless we reply. 111 ForwardLoginsResult(request); 112 } 113 } 114 115 void PasswordStoreX::GetAutofillableLoginsImpl(GetLoginsRequest* request) { 116 CheckMigration(); 117 if (use_native_backend() && 118 backend_->GetAutofillableLogins(&request->value)) { 119 ForwardLoginsResult(request); 120 allow_fallback_ = false; 121 } else if (allow_default_store()) { 122 PasswordStoreDefault::GetAutofillableLoginsImpl(request); 123 } else { 124 // The consumer will be left hanging unless we reply. 125 ForwardLoginsResult(request); 126 } 127 } 128 129 void PasswordStoreX::GetBlacklistLoginsImpl(GetLoginsRequest* request) { 130 CheckMigration(); 131 if (use_native_backend() && 132 backend_->GetBlacklistLogins(&request->value)) { 133 ForwardLoginsResult(request); 134 allow_fallback_ = false; 135 } else if (allow_default_store()) { 136 PasswordStoreDefault::GetBlacklistLoginsImpl(request); 137 } else { 138 // The consumer will be left hanging unless we reply. 139 ForwardLoginsResult(request); 140 } 141 } 142 143 bool PasswordStoreX::FillAutofillableLogins(vector<PasswordForm*>* forms) { 144 CheckMigration(); 145 if (use_native_backend() && backend_->GetAutofillableLogins(forms)) { 146 allow_fallback_ = false; 147 return true; 148 } 149 if (allow_default_store()) 150 return PasswordStoreDefault::FillAutofillableLogins(forms); 151 return false; 152 } 153 154 bool PasswordStoreX::FillBlacklistLogins(vector<PasswordForm*>* forms) { 155 CheckMigration(); 156 if (use_native_backend() && backend_->GetBlacklistLogins(forms)) { 157 allow_fallback_ = false; 158 return true; 159 } 160 if (allow_default_store()) 161 return PasswordStoreDefault::FillBlacklistLogins(forms); 162 return false; 163 } 164 165 void PasswordStoreX::CheckMigration() { 166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 167 if (migration_checked_ || !backend_.get()) 168 return; 169 migration_checked_ = true; 170 ssize_t migrated = MigrateLogins(); 171 if (migrated > 0) { 172 VLOG(1) << "Migrated " << migrated << " passwords to native store."; 173 } else if (migrated == 0) { 174 // As long as we are able to migrate some passwords, we know the native 175 // store is working. But if there is nothing to migrate, the "migration" 176 // can succeed even when the native store would fail. In this case we 177 // allow a later fallback to the default store. Once any later operation 178 // succeeds on the native store, we will no longer allow it. 179 allow_fallback_ = true; 180 } else { 181 LOG(WARNING) << "Native password store migration failed! " << 182 "Falling back on default (unencrypted) store."; 183 backend_.reset(NULL); 184 } 185 } 186 187 bool PasswordStoreX::allow_default_store() { 188 if (allow_fallback_) { 189 LOG(WARNING) << "Native password store failed! " << 190 "Falling back on default (unencrypted) store."; 191 backend_.reset(NULL); 192 // Don't warn again. We'll use the default store because backend_ is NULL. 193 allow_fallback_ = false; 194 } 195 return !backend_.get(); 196 } 197 198 ssize_t PasswordStoreX::MigrateLogins() { 199 DCHECK(backend_.get()); 200 vector<PasswordForm*> forms; 201 bool ok = PasswordStoreDefault::FillAutofillableLogins(&forms) && 202 PasswordStoreDefault::FillBlacklistLogins(&forms); 203 if (ok) { 204 // We add all the passwords (and blacklist entries) to the native backend 205 // before attempting to remove any from the login database, to make sure we 206 // don't somehow end up with some of the passwords in one store and some in 207 // another. We'll always have at least one intact store this way. 208 for (size_t i = 0; i < forms.size(); ++i) { 209 if (!backend_->AddLogin(*forms[i])) { 210 ok = false; 211 break; 212 } 213 } 214 if (forms.empty()) { 215 // If there's nothing to migrate, then we try to insert a dummy login form 216 // just to force the native store to unlock if it was locked. We delete it 217 // right away if we are successful. If the first operation we try to do is 218 // a read, then in some cases this is just an error rather than an action 219 // that causes the native store to prompt the user to unlock. 220 // TODO(mdm): this means we no longer need the allow_fallback mechanism. 221 // Remove it once this preemptive unlock by write is baked for a while. 222 PasswordForm dummy; 223 dummy.origin = GURL("http://www.example.com/force-keyring-unlock"); 224 dummy.signon_realm = "www.example.com"; 225 if (backend_->AddLogin(dummy)) 226 backend_->RemoveLogin(dummy); 227 else 228 ok = false; 229 } 230 if (ok) { 231 for (size_t i = 0; i < forms.size(); ++i) { 232 // If even one of these calls to RemoveLoginImpl() succeeds, then we 233 // should prefer the native backend to the now-incomplete login 234 // database. Thus we want to return a success status even in the case 235 // where some fail. The only real problem with this is that we might 236 // leave passwords in the login database and never come back to clean 237 // them out if any of these calls do fail. 238 PasswordStoreDefault::RemoveLoginImpl(*forms[i]); 239 } 240 // Finally, delete the database file itself. We remove the passwords from 241 // it before deleting the file just in case there is some problem deleting 242 // the file (e.g. directory is not writable, but file is), which would 243 // otherwise cause passwords to re-migrate next (or maybe every) time. 244 DeleteAndRecreateDatabaseFile(); 245 } 246 } 247 ssize_t result = ok ? forms.size() : -1; 248 STLDeleteElements(&forms); 249 return result; 250 } 251