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