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