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