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