login_database.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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 22using autofill::PasswordForm; 23 24namespace password_manager { 25 26static const int kCurrentVersionNumber = 6; 27static const int kCompatibleVersionNumber = 1; 28 29Pickle 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 37std::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 48namespace { 49 50// Convenience enum for interacting with SQL queries that use all the columns. 51enum 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 73void 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 105void 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 112LoginDatabase::LoginDatabase() { 113} 114 115LoginDatabase::~LoginDatabase() { 116} 117 118bool 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 170bool 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 219bool 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 257void 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 307PasswordStoreChangeList 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 348PasswordStoreChangeList 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 398bool 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 418bool LoginDatabase::RemoveLoginsCreatedBetween(const base::Time delete_begin, 419 const 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 430bool 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 443LoginDatabase::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 497bool 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 590bool 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 621bool 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 653bool LoginDatabase::GetAutofillableLogins( 654 std::vector<PasswordForm*>* forms) const { 655 return GetAllLoginsWithBlacklistSetting(false, forms); 656} 657 658bool LoginDatabase::GetBlacklistLogins( 659 std::vector<PasswordForm*>* forms) const { 660 return GetAllLoginsWithBlacklistSetting(true, forms); 661} 662 663bool 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 690bool 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