• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, ".", "\\.", &registered_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