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