1 // Copyright 2014 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 "components/password_manager/core/browser/login_database.h"
6
7 #include <algorithm>
8 #include <limits>
9
10 #include "base/bind.h"
11 #include "base/files/file_path.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/pickle.h"
15 #include "base/strings/string_util.h"
16 #include "base/time/time.h"
17 #include "components/autofill/core/common/password_form.h"
18 #include "sql/connection.h"
19 #include "sql/statement.h"
20 #include "sql/transaction.h"
21
22 using autofill::PasswordForm;
23
24 namespace password_manager {
25
26 static const int kCurrentVersionNumber = 6;
27 static const int kCompatibleVersionNumber = 1;
28
SerializeVector(const std::vector<base::string16> & vec)29 Pickle SerializeVector(const std::vector<base::string16>& vec) {
30 Pickle p;
31 for (size_t i = 0; i < vec.size(); ++i) {
32 p.WriteString16(vec[i]);
33 }
34 return p;
35 }
36
DeserializeVector(const Pickle & p)37 std::vector<base::string16> DeserializeVector(const Pickle& p) {
38 std::vector<base::string16> ret;
39 base::string16 str;
40
41 PickleIterator iterator(p);
42 while (iterator.ReadString16(&str)) {
43 ret.push_back(str);
44 }
45 return ret;
46 }
47
48 namespace {
49
50 // Convenience enum for interacting with SQL queries that use all the columns.
51 enum LoginTableColumns {
52 COLUMN_ORIGIN_URL = 0,
53 COLUMN_ACTION_URL,
54 COLUMN_USERNAME_ELEMENT,
55 COLUMN_USERNAME_VALUE,
56 COLUMN_PASSWORD_ELEMENT,
57 COLUMN_PASSWORD_VALUE,
58 COLUMN_SUBMIT_ELEMENT,
59 COLUMN_SIGNON_REALM,
60 COLUMN_SSL_VALID,
61 COLUMN_PREFERRED,
62 COLUMN_DATE_CREATED,
63 COLUMN_BLACKLISTED_BY_USER,
64 COLUMN_SCHEME,
65 COLUMN_PASSWORD_TYPE,
66 COLUMN_POSSIBLE_USERNAMES,
67 COLUMN_TIMES_USED,
68 COLUMN_FORM_DATA,
69 COLUMN_USE_ADDITIONAL_AUTH,
70 COLUMN_DATE_SYNCED
71 };
72
BindAddStatement(const PasswordForm & form,const std::string & encrypted_password,sql::Statement * s)73 void BindAddStatement(const PasswordForm& form,
74 const std::string& encrypted_password,
75 sql::Statement* s) {
76 s->BindString(COLUMN_ORIGIN_URL, form.origin.spec());
77 s->BindString(COLUMN_ACTION_URL, form.action.spec());
78 s->BindString16(COLUMN_USERNAME_ELEMENT, form.username_element);
79 s->BindString16(COLUMN_USERNAME_VALUE, form.username_value);
80 s->BindString16(COLUMN_PASSWORD_ELEMENT, form.password_element);
81 s->BindBlob(COLUMN_PASSWORD_VALUE, encrypted_password.data(),
82 static_cast<int>(encrypted_password.length()));
83 s->BindString16(COLUMN_SUBMIT_ELEMENT, form.submit_element);
84 s->BindString(COLUMN_SIGNON_REALM, form.signon_realm);
85 s->BindInt(COLUMN_SSL_VALID, form.ssl_valid);
86 s->BindInt(COLUMN_PREFERRED, form.preferred);
87 s->BindInt64(COLUMN_DATE_CREATED, form.date_created.ToTimeT());
88 s->BindInt(COLUMN_BLACKLISTED_BY_USER, form.blacklisted_by_user);
89 s->BindInt(COLUMN_SCHEME, form.scheme);
90 s->BindInt(COLUMN_PASSWORD_TYPE, form.type);
91 Pickle usernames_pickle = SerializeVector(form.other_possible_usernames);
92 s->BindBlob(COLUMN_POSSIBLE_USERNAMES,
93 usernames_pickle.data(),
94 usernames_pickle.size());
95 s->BindInt(COLUMN_TIMES_USED, form.times_used);
96 Pickle form_data_pickle;
97 autofill::SerializeFormData(form.form_data, &form_data_pickle);
98 s->BindBlob(COLUMN_FORM_DATA,
99 form_data_pickle.data(),
100 form_data_pickle.size());
101 s->BindInt(COLUMN_USE_ADDITIONAL_AUTH, form.use_additional_authentication);
102 s->BindInt64(COLUMN_DATE_SYNCED, form.date_synced.ToInternalValue());
103 }
104
AddCallback(int err,sql::Statement *)105 void AddCallback(int err, sql::Statement* /*stmt*/) {
106 if (err == 19 /*SQLITE_CONSTRAINT*/)
107 DLOG(WARNING) << "LoginDatabase::AddLogin updated an existing form";
108 }
109
110 } // namespace
111
LoginDatabase()112 LoginDatabase::LoginDatabase() {
113 }
114
~LoginDatabase()115 LoginDatabase::~LoginDatabase() {
116 }
117
Init(const base::FilePath & db_path)118 bool LoginDatabase::Init(const base::FilePath& db_path) {
119 // Set pragmas for a small, private database (based on WebDatabase).
120 db_.set_page_size(2048);
121 db_.set_cache_size(32);
122 db_.set_exclusive_locking();
123 db_.set_restrict_to_user();
124
125 if (!db_.Open(db_path)) {
126 LOG(WARNING) << "Unable to open the password store database.";
127 return false;
128 }
129
130 sql::Transaction transaction(&db_);
131 transaction.Begin();
132
133 // Check the database version.
134 if (!meta_table_.Init(&db_, kCurrentVersionNumber,
135 kCompatibleVersionNumber)) {
136 db_.Close();
137 return false;
138 }
139 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
140 LOG(WARNING) << "Password store database is too new.";
141 db_.Close();
142 return false;
143 }
144
145 // Initialize the tables.
146 if (!InitLoginsTable()) {
147 LOG(WARNING) << "Unable to initialize the password store database.";
148 db_.Close();
149 return false;
150 }
151
152 // Save the path for DeleteDatabaseFile().
153 db_path_ = db_path;
154
155 // If the file on disk is an older database version, bring it up to date.
156 if (!MigrateOldVersionsAsNeeded()) {
157 LOG(WARNING) << "Unable to migrate database";
158 db_.Close();
159 return false;
160 }
161
162 if (!transaction.Commit()) {
163 db_.Close();
164 return false;
165 }
166
167 return true;
168 }
169
MigrateOldVersionsAsNeeded()170 bool LoginDatabase::MigrateOldVersionsAsNeeded() {
171 switch (meta_table_.GetVersionNumber()) {
172 case 1:
173 if (!db_.Execute("ALTER TABLE logins "
174 "ADD COLUMN password_type INTEGER") ||
175 !db_.Execute("ALTER TABLE logins "
176 "ADD COLUMN possible_usernames BLOB")) {
177 return false;
178 }
179 meta_table_.SetVersionNumber(2);
180 // Fall through.
181 case 2:
182 if (!db_.Execute("ALTER TABLE logins ADD COLUMN times_used INTEGER")) {
183 return false;
184 }
185 meta_table_.SetVersionNumber(3);
186 // Fall through.
187 case 3:
188 // We need to check if the column exists because of
189 // https://crbug.com/295851
190 if (!db_.DoesColumnExist("logins", "form_data") &&
191 !db_.Execute("ALTER TABLE logins ADD COLUMN form_data BLOB")) {
192 return false;
193 }
194 meta_table_.SetVersionNumber(4);
195 // Fall through.
196 case 4:
197 if (!db_.Execute(
198 "ALTER TABLE logins ADD COLUMN use_additional_auth INTEGER")) {
199 return false;
200 }
201 meta_table_.SetVersionNumber(5);
202 // Fall through.
203 case 5:
204 if (!db_.Execute(
205 "ALTER TABLE logins ADD COLUMN date_synced INTEGER")) {
206 return false;
207 }
208 meta_table_.SetVersionNumber(6);
209 // Fall through.
210 case kCurrentVersionNumber:
211 // Already up to date
212 return true;
213 default:
214 NOTREACHED();
215 return false;
216 }
217 }
218
InitLoginsTable()219 bool LoginDatabase::InitLoginsTable() {
220 if (!db_.DoesTableExist("logins")) {
221 if (!db_.Execute("CREATE TABLE logins ("
222 "origin_url VARCHAR NOT NULL, "
223 "action_url VARCHAR, "
224 "username_element VARCHAR, "
225 "username_value VARCHAR, "
226 "password_element VARCHAR, "
227 "password_value BLOB, "
228 "submit_element VARCHAR, "
229 "signon_realm VARCHAR NOT NULL,"
230 "ssl_valid INTEGER NOT NULL,"
231 "preferred INTEGER NOT NULL,"
232 "date_created INTEGER NOT NULL,"
233 "blacklisted_by_user INTEGER NOT NULL,"
234 "scheme INTEGER NOT NULL,"
235 "password_type INTEGER,"
236 "possible_usernames BLOB,"
237 "times_used INTEGER,"
238 "form_data BLOB,"
239 "use_additional_auth INTEGER,"
240 "date_synced INTEGER,"
241 "UNIQUE "
242 "(origin_url, username_element, "
243 "username_value, password_element, "
244 "submit_element, signon_realm))")) {
245 NOTREACHED();
246 return false;
247 }
248 if (!db_.Execute("CREATE INDEX logins_signon ON "
249 "logins (signon_realm)")) {
250 NOTREACHED();
251 return false;
252 }
253 }
254 return true;
255 }
256
ReportMetrics()257 void LoginDatabase::ReportMetrics() {
258 sql::Statement s(db_.GetCachedStatement(
259 SQL_FROM_HERE,
260 "SELECT signon_realm, blacklisted_by_user, COUNT(username_value) "
261 "FROM logins GROUP BY signon_realm, blacklisted_by_user"));
262
263 if (!s.is_valid())
264 return;
265
266 int total_accounts = 0;
267 int blacklisted_sites = 0;
268 while (s.Step()) {
269 int blacklisted = s.ColumnInt(1);
270 int accounts_per_site = s.ColumnInt(2);
271 if (blacklisted) {
272 ++blacklisted_sites;
273 } else {
274 total_accounts += accounts_per_site;
275 UMA_HISTOGRAM_CUSTOM_COUNTS("PasswordManager.AccountsPerSite",
276 accounts_per_site, 0, 32, 6);
277 }
278 }
279 UMA_HISTOGRAM_CUSTOM_COUNTS("PasswordManager.TotalAccounts",
280 total_accounts, 0, 32, 6);
281 UMA_HISTOGRAM_CUSTOM_COUNTS("PasswordManager.BlacklistedSites",
282 blacklisted_sites, 0, 32, 6);
283
284 sql::Statement usage_statement(db_.GetCachedStatement(
285 SQL_FROM_HERE,
286 "SELECT password_type, times_used FROM logins"));
287
288 if (!usage_statement.is_valid())
289 return;
290
291 while (usage_statement.Step()) {
292 PasswordForm::Type type = static_cast<PasswordForm::Type>(
293 usage_statement.ColumnInt(0));
294
295 if (type == PasswordForm::TYPE_GENERATED) {
296 UMA_HISTOGRAM_CUSTOM_COUNTS(
297 "PasswordManager.TimesGeneratedPasswordUsed",
298 usage_statement.ColumnInt(1), 0, 100, 10);
299 } else {
300 UMA_HISTOGRAM_CUSTOM_COUNTS(
301 "PasswordManager.TimesPasswordUsed",
302 usage_statement.ColumnInt(1), 0, 100, 10);
303 }
304 }
305 }
306
AddLogin(const PasswordForm & form)307 PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) {
308 PasswordStoreChangeList list;
309 std::string encrypted_password;
310 if (EncryptedString(form.password_value, &encrypted_password) !=
311 ENCRYPTION_RESULT_SUCCESS)
312 return list;
313
314 // You *must* change LoginTableColumns if this query changes.
315 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
316 "INSERT INTO logins "
317 "(origin_url, action_url, username_element, username_value, "
318 " password_element, password_value, submit_element, "
319 " signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
320 " scheme, password_type, possible_usernames, times_used, form_data, "
321 " use_additional_auth, date_synced) VALUES "
322 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
323 BindAddStatement(form, encrypted_password, &s);
324 db_.set_error_callback(base::Bind(&AddCallback));
325 const bool success = s.Run();
326 db_.reset_error_callback();
327 if (success) {
328 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
329 return list;
330 }
331 // Repeat the same statement but with REPLACE semantic.
332 s.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
333 "INSERT OR REPLACE INTO logins "
334 "(origin_url, action_url, username_element, username_value, "
335 " password_element, password_value, submit_element, "
336 " signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
337 " scheme, password_type, possible_usernames, times_used, form_data, "
338 " use_additional_auth, date_synced) VALUES "
339 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
340 BindAddStatement(form, encrypted_password, &s);
341 if (s.Run()) {
342 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
343 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
344 }
345 return list;
346 }
347
UpdateLogin(const PasswordForm & form)348 PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) {
349 std::string encrypted_password;
350 if (EncryptedString(form.password_value, &encrypted_password) !=
351 ENCRYPTION_RESULT_SUCCESS)
352 return PasswordStoreChangeList();
353
354 // Replacement is necessary to deal with updating imported credentials. See
355 // crbug.com/349138 for details.
356 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
357 "UPDATE OR REPLACE logins SET "
358 "action_url = ?, "
359 "password_value = ?, "
360 "ssl_valid = ?, "
361 "preferred = ?, "
362 "possible_usernames = ?, "
363 "times_used = ?, "
364 "submit_element = ?, "
365 "date_synced = ? "
366 "WHERE origin_url = ? AND "
367 "username_element = ? AND "
368 "username_value = ? AND "
369 "password_element = ? AND "
370 "signon_realm = ?"));
371 s.BindString(0, form.action.spec());
372 s.BindBlob(1, encrypted_password.data(),
373 static_cast<int>(encrypted_password.length()));
374 s.BindInt(2, form.ssl_valid);
375 s.BindInt(3, form.preferred);
376 Pickle pickle = SerializeVector(form.other_possible_usernames);
377 s.BindBlob(4, pickle.data(), pickle.size());
378 s.BindInt(5, form.times_used);
379 s.BindString16(6, form.submit_element);
380 s.BindInt64(7, form.date_synced.ToInternalValue());
381
382 s.BindString(8, form.origin.spec());
383 s.BindString16(9, form.username_element);
384 s.BindString16(10, form.username_value);
385 s.BindString16(11, form.password_element);
386 s.BindString(12, form.signon_realm);
387
388 if (!s.Run())
389 return PasswordStoreChangeList();
390
391 PasswordStoreChangeList list;
392 if (db_.GetLastChangeCount())
393 list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form));
394
395 return list;
396 }
397
RemoveLogin(const PasswordForm & form)398 bool LoginDatabase::RemoveLogin(const PasswordForm& form) {
399 // Remove a login by UNIQUE-constrained fields.
400 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
401 "DELETE FROM logins WHERE "
402 "origin_url = ? AND "
403 "username_element = ? AND "
404 "username_value = ? AND "
405 "password_element = ? AND "
406 "submit_element = ? AND "
407 "signon_realm = ? "));
408 s.BindString(0, form.origin.spec());
409 s.BindString16(1, form.username_element);
410 s.BindString16(2, form.username_value);
411 s.BindString16(3, form.password_element);
412 s.BindString16(4, form.submit_element);
413 s.BindString(5, form.signon_realm);
414
415 return s.Run();
416 }
417
RemoveLoginsCreatedBetween(base::Time delete_begin,base::Time delete_end)418 bool LoginDatabase::RemoveLoginsCreatedBetween(base::Time delete_begin,
419 base::Time delete_end) {
420 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
421 "DELETE FROM logins WHERE "
422 "date_created >= ? AND date_created < ?"));
423 s.BindInt64(0, delete_begin.ToTimeT());
424 s.BindInt64(1, delete_end.is_null() ? std::numeric_limits<int64>::max()
425 : delete_end.ToTimeT());
426
427 return s.Run();
428 }
429
RemoveLoginsSyncedBetween(base::Time delete_begin,base::Time delete_end)430 bool LoginDatabase::RemoveLoginsSyncedBetween(base::Time delete_begin,
431 base::Time delete_end) {
432 sql::Statement s(db_.GetCachedStatement(
433 SQL_FROM_HERE,
434 "DELETE FROM logins WHERE date_synced >= ? AND date_synced < ?"));
435 s.BindInt64(0, delete_begin.ToInternalValue());
436 s.BindInt64(1,
437 delete_end.is_null() ? base::Time::Max().ToInternalValue()
438 : delete_end.ToInternalValue());
439
440 return s.Run();
441 }
442
InitPasswordFormFromStatement(PasswordForm * form,sql::Statement & s) const443 LoginDatabase::EncryptionResult LoginDatabase::InitPasswordFormFromStatement(
444 PasswordForm* form,
445 sql::Statement& s) const {
446 std::string encrypted_password;
447 s.ColumnBlobAsString(COLUMN_PASSWORD_VALUE, &encrypted_password);
448 base::string16 decrypted_password;
449 EncryptionResult encryption_result =
450 DecryptedString(encrypted_password, &decrypted_password);
451 if (encryption_result != ENCRYPTION_RESULT_SUCCESS)
452 return encryption_result;
453
454 std::string tmp = s.ColumnString(COLUMN_ORIGIN_URL);
455 form->origin = GURL(tmp);
456 tmp = s.ColumnString(COLUMN_ACTION_URL);
457 form->action = GURL(tmp);
458 form->username_element = s.ColumnString16(COLUMN_USERNAME_ELEMENT);
459 form->username_value = s.ColumnString16(COLUMN_USERNAME_VALUE);
460 form->password_element = s.ColumnString16(COLUMN_PASSWORD_ELEMENT);
461 form->password_value = decrypted_password;
462 form->submit_element = s.ColumnString16(COLUMN_SUBMIT_ELEMENT);
463 tmp = s.ColumnString(COLUMN_SIGNON_REALM);
464 form->signon_realm = tmp;
465 form->ssl_valid = (s.ColumnInt(COLUMN_SSL_VALID) > 0);
466 form->preferred = (s.ColumnInt(COLUMN_PREFERRED) > 0);
467 form->date_created = base::Time::FromTimeT(
468 s.ColumnInt64(COLUMN_DATE_CREATED));
469 form->blacklisted_by_user = (s.ColumnInt(COLUMN_BLACKLISTED_BY_USER) > 0);
470 int scheme_int = s.ColumnInt(COLUMN_SCHEME);
471 DCHECK((scheme_int >= 0) && (scheme_int <= PasswordForm::SCHEME_OTHER));
472 form->scheme = static_cast<PasswordForm::Scheme>(scheme_int);
473 int type_int = s.ColumnInt(COLUMN_PASSWORD_TYPE);
474 DCHECK(type_int >= 0 && type_int <= PasswordForm::TYPE_GENERATED);
475 form->type = static_cast<PasswordForm::Type>(type_int);
476 if (s.ColumnByteLength(COLUMN_POSSIBLE_USERNAMES)) {
477 Pickle pickle(
478 static_cast<const char*>(s.ColumnBlob(COLUMN_POSSIBLE_USERNAMES)),
479 s.ColumnByteLength(COLUMN_POSSIBLE_USERNAMES));
480 form->other_possible_usernames = DeserializeVector(pickle);
481 }
482 form->times_used = s.ColumnInt(COLUMN_TIMES_USED);
483 if (s.ColumnByteLength(COLUMN_FORM_DATA)) {
484 Pickle form_data_pickle(
485 static_cast<const char*>(s.ColumnBlob(COLUMN_FORM_DATA)),
486 s.ColumnByteLength(COLUMN_FORM_DATA));
487 PickleIterator form_data_iter(form_data_pickle);
488 autofill::DeserializeFormData(&form_data_iter, &form->form_data);
489 }
490 form->use_additional_authentication =
491 (s.ColumnInt(COLUMN_USE_ADDITIONAL_AUTH) > 0);
492 form->date_synced = base::Time::FromInternalValue(
493 s.ColumnInt64(COLUMN_DATE_SYNCED));
494 return ENCRYPTION_RESULT_SUCCESS;
495 }
496
GetLogins(const PasswordForm & form,std::vector<PasswordForm * > * forms) const497 bool LoginDatabase::GetLogins(const PasswordForm& form,
498 std::vector<PasswordForm*>* forms) const {
499 DCHECK(forms);
500 // You *must* change LoginTableColumns if this query changes.
501 const std::string sql_query = "SELECT origin_url, action_url, "
502 "username_element, username_value, "
503 "password_element, password_value, submit_element, "
504 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
505 "scheme, password_type, possible_usernames, times_used, form_data, "
506 "use_additional_auth, date_synced FROM logins WHERE signon_realm == ? ";
507 sql::Statement s;
508 const GURL signon_realm(form.signon_realm);
509 std::string registered_domain =
510 PSLMatchingHelper::GetRegistryControlledDomain(signon_realm);
511 PSLMatchingHelper::PSLDomainMatchMetric psl_domain_match_metric =
512 PSLMatchingHelper::PSL_DOMAIN_MATCH_NONE;
513 // PSL matching only applies to HTML forms.
514 if (form.scheme == PasswordForm::SCHEME_HTML &&
515 psl_helper_.ShouldPSLDomainMatchingApply(registered_domain)) {
516 // We are extending the original SQL query with one that includes more
517 // possible matches based on public suffix domain matching. Using a regexp
518 // here is just an optimization to not have to parse all the stored entries
519 // in the |logins| table. The result (scheme, domain and port) is verified
520 // further down using GURL. See the functions SchemeMatches,
521 // RegistryControlledDomainMatches and PortMatches.
522 const std::string extended_sql_query =
523 sql_query + "OR signon_realm REGEXP ? ";
524 // TODO(nyquist) Re-enable usage of GetCachedStatement when
525 // http://crbug.com/248608 is fixed.
526 s.Assign(db_.GetUniqueStatement(extended_sql_query.c_str()));
527 // We need to escape . in the domain. Since the domain has already been
528 // sanitized using GURL, we do not need to escape any other characters.
529 base::ReplaceChars(registered_domain, ".", "\\.", ®istered_domain);
530 std::string scheme = signon_realm.scheme();
531 // We need to escape . in the scheme. Since the scheme has already been
532 // sanitized using GURL, we do not need to escape any other characters.
533 // The scheme soap.beep is an example with '.'.
534 base::ReplaceChars(scheme, ".", "\\.", &scheme);
535 const std::string port = signon_realm.port();
536 // For a signon realm such as http://foo.bar/, this regexp will match
537 // domains on the form http://foo.bar/, http://www.foo.bar/,
538 // http://www.mobile.foo.bar/. It will not match http://notfoo.bar/.
539 // The scheme and port has to be the same as the observed form.
540 std::string regexp = "^(" + scheme + ":\\/\\/)([\\w-]+\\.)*" +
541 registered_domain + "(:" + port + ")?\\/$";
542 s.BindString(0, form.signon_realm);
543 s.BindString(1, regexp);
544 } else {
545 psl_domain_match_metric = PSLMatchingHelper::PSL_DOMAIN_MATCH_DISABLED;
546 s.Assign(db_.GetCachedStatement(SQL_FROM_HERE, sql_query.c_str()));
547 s.BindString(0, form.signon_realm);
548 }
549
550 while (s.Step()) {
551 scoped_ptr<PasswordForm> new_form(new PasswordForm());
552 EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s);
553 if (result == ENCRYPTION_RESULT_SERVICE_FAILURE)
554 return false;
555 if (result == ENCRYPTION_RESULT_ITEM_FAILURE)
556 continue;
557 DCHECK(result == ENCRYPTION_RESULT_SUCCESS);
558 if (psl_helper_.IsMatchingEnabled()) {
559 if (!PSLMatchingHelper::IsPublicSuffixDomainMatch(new_form->signon_realm,
560 form.signon_realm)) {
561 // The database returned results that should not match. Skipping result.
562 continue;
563 }
564 if (form.signon_realm != new_form->signon_realm) {
565 // Ignore non-HTML matches.
566 if (new_form->scheme != PasswordForm::SCHEME_HTML)
567 continue;
568
569 psl_domain_match_metric = PSLMatchingHelper::PSL_DOMAIN_MATCH_FOUND;
570 // This is not a perfect match, so we need to create a new valid result.
571 // We do this by copying over origin, signon realm and action from the
572 // observed form and setting the original signon realm to what we found
573 // in the database. We use the fact that |original_signon_realm| is
574 // non-empty to communicate that this match was found using public
575 // suffix matching.
576 new_form->original_signon_realm = new_form->signon_realm;
577 new_form->origin = form.origin;
578 new_form->signon_realm = form.signon_realm;
579 new_form->action = form.action;
580 }
581 }
582 forms->push_back(new_form.release());
583 }
584 UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering",
585 psl_domain_match_metric,
586 PSLMatchingHelper::PSL_DOMAIN_MATCH_COUNT);
587 return s.Succeeded();
588 }
589
GetLoginsCreatedBetween(const base::Time begin,const base::Time end,std::vector<autofill::PasswordForm * > * forms) const590 bool LoginDatabase::GetLoginsCreatedBetween(
591 const base::Time begin,
592 const base::Time end,
593 std::vector<autofill::PasswordForm*>* forms) const {
594 DCHECK(forms);
595 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
596 "SELECT origin_url, action_url, "
597 "username_element, username_value, "
598 "password_element, password_value, submit_element, "
599 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
600 "scheme, password_type, possible_usernames, times_used, form_data, "
601 "use_additional_auth, date_synced FROM logins "
602 "WHERE date_created >= ? AND date_created < ?"
603 "ORDER BY origin_url"));
604 s.BindInt64(0, begin.ToTimeT());
605 s.BindInt64(1, end.is_null() ? std::numeric_limits<int64>::max()
606 : end.ToTimeT());
607
608 while (s.Step()) {
609 scoped_ptr<PasswordForm> new_form(new PasswordForm());
610 EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s);
611 if (result == ENCRYPTION_RESULT_SERVICE_FAILURE)
612 return false;
613 if (result == ENCRYPTION_RESULT_ITEM_FAILURE)
614 continue;
615 DCHECK(result == ENCRYPTION_RESULT_SUCCESS);
616 forms->push_back(new_form.release());
617 }
618 return s.Succeeded();
619 }
620
GetLoginsSyncedBetween(const base::Time begin,const base::Time end,std::vector<autofill::PasswordForm * > * forms) const621 bool LoginDatabase::GetLoginsSyncedBetween(
622 const base::Time begin,
623 const base::Time end,
624 std::vector<autofill::PasswordForm*>* forms) const {
625 DCHECK(forms);
626 sql::Statement s(db_.GetCachedStatement(
627 SQL_FROM_HERE,
628 "SELECT origin_url, action_url, username_element, username_value, "
629 "password_element, password_value, submit_element, signon_realm, "
630 "ssl_valid, preferred, date_created, blacklisted_by_user, "
631 "scheme, password_type, possible_usernames, times_used, form_data, "
632 "use_additional_auth, date_synced FROM logins "
633 "WHERE date_synced >= ? AND date_synced < ?"
634 "ORDER BY origin_url"));
635 s.BindInt64(0, begin.ToInternalValue());
636 s.BindInt64(1,
637 end.is_null() ? base::Time::Max().ToInternalValue()
638 : end.ToInternalValue());
639
640 while (s.Step()) {
641 scoped_ptr<PasswordForm> new_form(new PasswordForm());
642 EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s);
643 if (result == ENCRYPTION_RESULT_SERVICE_FAILURE)
644 return false;
645 if (result == ENCRYPTION_RESULT_ITEM_FAILURE)
646 continue;
647 DCHECK(result == ENCRYPTION_RESULT_SUCCESS);
648 forms->push_back(new_form.release());
649 }
650 return s.Succeeded();
651 }
652
GetAutofillableLogins(std::vector<PasswordForm * > * forms) const653 bool LoginDatabase::GetAutofillableLogins(
654 std::vector<PasswordForm*>* forms) const {
655 return GetAllLoginsWithBlacklistSetting(false, forms);
656 }
657
GetBlacklistLogins(std::vector<PasswordForm * > * forms) const658 bool LoginDatabase::GetBlacklistLogins(
659 std::vector<PasswordForm*>* forms) const {
660 return GetAllLoginsWithBlacklistSetting(true, forms);
661 }
662
GetAllLoginsWithBlacklistSetting(bool blacklisted,std::vector<PasswordForm * > * forms) const663 bool LoginDatabase::GetAllLoginsWithBlacklistSetting(
664 bool blacklisted, std::vector<PasswordForm*>* forms) const {
665 DCHECK(forms);
666 // You *must* change LoginTableColumns if this query changes.
667 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
668 "SELECT origin_url, action_url, "
669 "username_element, username_value, "
670 "password_element, password_value, submit_element, "
671 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, "
672 "scheme, password_type, possible_usernames, times_used, form_data, "
673 "use_additional_auth, date_synced FROM logins "
674 "WHERE blacklisted_by_user == ? ORDER BY origin_url"));
675 s.BindInt(0, blacklisted ? 1 : 0);
676
677 while (s.Step()) {
678 scoped_ptr<PasswordForm> new_form(new PasswordForm());
679 EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s);
680 if (result == ENCRYPTION_RESULT_SERVICE_FAILURE)
681 return false;
682 if (result == ENCRYPTION_RESULT_ITEM_FAILURE)
683 continue;
684 DCHECK(result == ENCRYPTION_RESULT_SUCCESS);
685 forms->push_back(new_form.release());
686 }
687 return s.Succeeded();
688 }
689
DeleteAndRecreateDatabaseFile()690 bool LoginDatabase::DeleteAndRecreateDatabaseFile() {
691 DCHECK(db_.is_open());
692 meta_table_.Reset();
693 db_.Close();
694 sql::Connection::Delete(db_path_);
695 return Init(db_path_);
696 }
697
698 } // namespace password_manager
699