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
PasswordStoreX(LoginDatabase * login_db,Profile * profile,WebDataService * web_data_service,NativeBackend * backend)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
~PasswordStoreX()27 PasswordStoreX::~PasswordStoreX() {
28 }
29
AddLoginImpl(const PasswordForm & form)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
UpdateLoginImpl(const PasswordForm & form)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
RemoveLoginImpl(const PasswordForm & form)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
RemoveLoginsCreatedBetweenImpl(const base::Time & delete_begin,const base::Time & delete_end)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
GetLoginsImpl(GetLoginsRequest * request,const PasswordForm & form)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
GetAutofillableLoginsImpl(GetLoginsRequest * request)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
GetBlacklistLoginsImpl(GetLoginsRequest * request)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
FillAutofillableLogins(vector<PasswordForm * > * forms)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
FillBlacklistLogins(vector<PasswordForm * > * forms)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
CheckMigration()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
allow_default_store()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
MigrateLogins()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