| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/password_manager/core/browser/login_database.h" | 5 #include "components/password_manager/core/browser/login_database.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <limits> | 10 #include <limits> |
| 11 #include <map> | 11 #include <map> |
| 12 #include <utility> | 12 #include <utility> |
| 13 | 13 |
| 14 #include "base/bind.h" | 14 #include "base/bind.h" |
| 15 #include "base/files/file_path.h" | 15 #include "base/files/file_path.h" |
| 16 #include "base/logging.h" | 16 #include "base/logging.h" |
| 17 #include "base/macros.h" | 17 #include "base/macros.h" |
| 18 #include "base/metrics/histogram_macros.h" | 18 #include "base/metrics/histogram_macros.h" |
| 19 #include "base/metrics/sparse_histogram.h" | 19 #include "base/metrics/sparse_histogram.h" |
| 20 #include "base/numerics/safe_conversions.h" |
| 20 #include "base/pickle.h" | 21 #include "base/pickle.h" |
| 21 #include "base/stl_util.h" | 22 #include "base/stl_util.h" |
| 22 #include "base/strings/string_util.h" | 23 #include "base/strings/string_util.h" |
| 23 #include "base/strings/stringprintf.h" | 24 #include "base/strings/stringprintf.h" |
| 24 #include "base/time/time.h" | 25 #include "base/time/time.h" |
| 25 #include "build/build_config.h" | 26 #include "build/build_config.h" |
| 26 #include "components/autofill/core/common/password_form.h" | 27 #include "components/autofill/core/common/password_form.h" |
| 27 #include "components/password_manager/core/browser/affiliation_utils.h" | 28 #include "components/password_manager/core/browser/affiliation_utils.h" |
| 28 #include "components/password_manager/core/browser/password_manager_client.h" | 29 #include "components/password_manager/core/browser/password_manager_client.h" |
| 29 #include "components/password_manager/core/browser/password_manager_metrics_util
.h" | 30 #include "components/password_manager/core/browser/password_manager_metrics_util
.h" |
| 31 #include "components/password_manager/core/browser/sql_table_builder.h" |
| 30 #include "google_apis/gaia/gaia_auth_util.h" | 32 #include "google_apis/gaia/gaia_auth_util.h" |
| 31 #include "google_apis/gaia/gaia_urls.h" | 33 #include "google_apis/gaia/gaia_urls.h" |
| 32 #include "sql/connection.h" | 34 #include "sql/connection.h" |
| 33 #include "sql/statement.h" | 35 #include "sql/statement.h" |
| 34 #include "sql/transaction.h" | 36 #include "sql/transaction.h" |
| 35 #include "url/origin.h" | 37 #include "url/origin.h" |
| 36 #include "url/url_constants.h" | 38 #include "url/url_constants.h" |
| 37 | 39 |
| 38 using autofill::PasswordForm; | 40 using autofill::PasswordForm; |
| 39 | 41 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 84 COLUMN_PASSWORD_TYPE, | 86 COLUMN_PASSWORD_TYPE, |
| 85 COLUMN_POSSIBLE_USERNAMES, | 87 COLUMN_POSSIBLE_USERNAMES, |
| 86 COLUMN_TIMES_USED, | 88 COLUMN_TIMES_USED, |
| 87 COLUMN_FORM_DATA, | 89 COLUMN_FORM_DATA, |
| 88 COLUMN_DATE_SYNCED, | 90 COLUMN_DATE_SYNCED, |
| 89 COLUMN_DISPLAY_NAME, | 91 COLUMN_DISPLAY_NAME, |
| 90 COLUMN_ICON_URL, | 92 COLUMN_ICON_URL, |
| 91 COLUMN_FEDERATION_URL, | 93 COLUMN_FEDERATION_URL, |
| 92 COLUMN_SKIP_ZERO_CLICK, | 94 COLUMN_SKIP_ZERO_CLICK, |
| 93 COLUMN_GENERATION_UPLOAD_STATUS, | 95 COLUMN_GENERATION_UPLOAD_STATUS, |
| 96 COLUMN_NUM // Keep this last. |
| 94 }; | 97 }; |
| 95 | 98 |
| 96 enum class HistogramSize { SMALL, LARGE }; | 99 enum class HistogramSize { SMALL, LARGE }; |
| 97 | 100 |
| 98 // An enum for UMA reporting. Add values to the end only. | 101 // An enum for UMA reporting. Add values to the end only. |
| 99 enum DatabaseInitError { | 102 enum DatabaseInitError { |
| 100 INIT_OK, | 103 INIT_OK, |
| 101 OPEN_FILE_ERROR, | 104 OPEN_FILE_ERROR, |
| 102 START_TRANSACTION_ERROR, | 105 START_TRANSACTION_ERROR, |
| 103 META_TABLE_INIT_ERROR, | 106 META_TABLE_INIT_ERROR, |
| (...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 328 LogNumberOfAccountsReusingPassword( | 331 LogNumberOfAccountsReusingPassword( |
| 329 source_realm_kind + ".OnAnyRealmWithDifferentHost", | 332 source_realm_kind + ".OnAnyRealmWithDifferentHost", |
| 330 num_accounts_for_different_domain[SCHEME_HTTP] + | 333 num_accounts_for_different_domain[SCHEME_HTTP] + |
| 331 num_accounts_for_different_domain[SCHEME_HTTPS], | 334 num_accounts_for_different_domain[SCHEME_HTTPS], |
| 332 HistogramSize::LARGE); | 335 HistogramSize::LARGE); |
| 333 } | 336 } |
| 334 } | 337 } |
| 335 } | 338 } |
| 336 } | 339 } |
| 337 | 340 |
| 338 // Creates a table named |table_name| using our current schema. | 341 // Teaches |builder| about the different DB schemes in different versions. |
| 339 bool CreateNewTable(sql::Connection* db, | 342 void InitializeBuilder(SQLTableBuilder* builder) { |
| 340 const char* table_name, | 343 // Versions 0 and 1, which are the same. |
| 341 const char* extra_columns) { | 344 builder->AddColumnToUniqueKey("origin_url", "VARCHAR NOT NULL"); |
| 342 std::string query = base::StringPrintf( | 345 builder->AddColumn("action_url", "VARCHAR"); |
| 343 "CREATE TABLE %s (" | 346 builder->AddColumnToUniqueKey("username_element", "VARCHAR"); |
| 344 "origin_url VARCHAR NOT NULL, " | 347 builder->AddColumnToUniqueKey("username_value", "VARCHAR"); |
| 345 "action_url VARCHAR, " | 348 builder->AddColumnToUniqueKey("password_element", "VARCHAR"); |
| 346 "username_element VARCHAR, " | 349 builder->AddColumn("password_value", "BLOB"); |
| 347 "username_value VARCHAR, " | 350 builder->AddColumn("submit_element", "VARCHAR"); |
| 348 "password_element VARCHAR, " | 351 builder->AddColumnToUniqueKey("signon_realm", "VARCHAR NOT NULL"); |
| 349 "password_value BLOB, " | 352 builder->AddColumn("ssl_valid", "INTEGER NOT NULL"); |
| 350 "submit_element VARCHAR, " | 353 builder->AddColumn("preferred", "INTEGER NOT NULL"); |
| 351 "signon_realm VARCHAR NOT NULL," | 354 builder->AddColumn("date_created", "INTEGER NOT NULL"); |
| 352 "ssl_valid INTEGER NOT NULL," | 355 builder->AddColumn("blacklisted_by_user", "INTEGER NOT NULL"); |
| 353 "preferred INTEGER NOT NULL," | 356 builder->AddColumn("scheme", "INTEGER NOT NULL"); |
| 354 "date_created INTEGER NOT NULL," | 357 builder->SealVersion(); |
| 355 "blacklisted_by_user INTEGER NOT NULL," | 358 unsigned version = builder->SealVersion(); |
| 356 "scheme INTEGER NOT NULL," | 359 DCHECK_EQ(1u, version); |
| 357 "password_type INTEGER," | 360 |
| 358 "possible_usernames BLOB," | 361 // Version 2. |
| 359 "times_used INTEGER," | 362 builder->AddColumn("password_type", "INTEGER"); |
| 360 "form_data BLOB," | 363 builder->AddColumn("possible_usernames", "BLOB"); |
| 361 "date_synced INTEGER," | 364 version = builder->SealVersion(); |
| 362 "display_name VARCHAR," | 365 DCHECK_EQ(2u, version); |
| 363 "icon_url VARCHAR," | 366 |
| 364 "federation_url VARCHAR," | 367 // Version 3. |
| 365 "skip_zero_click INTEGER," | 368 builder->AddColumn("times_used", "INTEGER"); |
| 366 "%s" | 369 version = builder->SealVersion(); |
| 367 "UNIQUE (origin_url, username_element, username_value, " | 370 DCHECK_EQ(3u, version); |
| 368 "password_element, signon_realm))", | 371 |
| 369 table_name, extra_columns); | 372 // Version 4. |
| 370 return db->Execute(query.c_str()); | 373 builder->AddColumn("form_data", "BLOB"); |
| 374 version = builder->SealVersion(); |
| 375 DCHECK_EQ(4u, version); |
| 376 |
| 377 // Version 5. |
| 378 builder->AddColumn("use_additional_auth", "INTEGER"); |
| 379 version = builder->SealVersion(); |
| 380 DCHECK_EQ(5u, version); |
| 381 |
| 382 // Version 6. |
| 383 builder->AddColumn("date_synced", "INTEGER"); |
| 384 version = builder->SealVersion(); |
| 385 DCHECK_EQ(6u, version); |
| 386 |
| 387 // Version 7. |
| 388 builder->AddColumn("display_name", "VARCHAR"); |
| 389 builder->AddColumn("avatar_url", "VARCHAR"); |
| 390 builder->AddColumn("federation_url", "VARCHAR"); |
| 391 builder->AddColumn("is_zero_click", "INTEGER"); |
| 392 version = builder->SealVersion(); |
| 393 DCHECK_EQ(7u, version); |
| 394 |
| 395 // Version 8. |
| 396 builder->SealVersion(); |
| 397 // Version 9. |
| 398 version = builder->SealVersion(); |
| 399 // Version 10. |
| 400 builder->DropColumn("use_additional_auth"); |
| 401 version = builder->SealVersion(); |
| 402 DCHECK_EQ(10u, version); |
| 403 |
| 404 // Version 11. |
| 405 builder->RenameColumn("is_zero_click", "skip_zero_click"); |
| 406 version = builder->SealVersion(); |
| 407 DCHECK_EQ(11u, version); |
| 408 |
| 409 // Version 12. |
| 410 builder->AddColumn("generation_upload_status", "INTEGER"); |
| 411 version = builder->SealVersion(); |
| 412 DCHECK_EQ(12u, version); |
| 413 |
| 414 // Version 13. |
| 415 builder->SealVersion(); |
| 416 // Version 14. |
| 417 builder->RenameColumn("avatar_url", "icon_url"); |
| 418 version = builder->SealVersion(); |
| 419 DCHECK_EQ(14u, version); |
| 420 |
| 421 // Version 15. |
| 422 builder->SealVersion(); |
| 423 // Version 16. |
| 424 builder->SealVersion(); |
| 425 // Version 17. |
| 426 version = builder->SealVersion(); |
| 427 DCHECK_EQ(17u, version); |
| 428 |
| 429 DCHECK_EQ(static_cast<size_t>(COLUMN_NUM), builder->NumberOfColumns()) |
| 430 << "Adjust LoginTableColumns if you change column definitions here."; |
| 371 } | 431 } |
| 372 | 432 |
| 373 bool CreateIndexOnSignonRealm(sql::Connection* db, const char* table_name) { | 433 // Call this after having called InitializeBuilder, to migrate the database from |
| 374 std::string query = base::StringPrintf( | 434 // the current version to kCurrentVersionNumber. |
| 375 "CREATE INDEX logins_signon ON %s (signon_realm)", table_name); | 435 bool MigrateLogins(unsigned current_version, |
| 376 return db->Execute(query.c_str()); | 436 SQLTableBuilder* builder, |
| 437 sql::Connection* db) { |
| 438 if (!builder->MigrateFrom(current_version, db)) |
| 439 return false; |
| 440 |
| 441 // Data changes, not covered by the schema migration above. |
| 442 if (current_version <= 8) { |
| 443 sql::Statement fix_time_format; |
| 444 fix_time_format.Assign(db->GetCachedStatement( |
| 445 SQL_FROM_HERE, |
| 446 "UPDATE logins SET date_created = (date_created * ?) + ?")); |
| 447 fix_time_format.BindInt64(0, base::Time::kMicrosecondsPerSecond); |
| 448 fix_time_format.BindInt64(1, base::Time::kTimeTToMicrosecondsOffset); |
| 449 if (!fix_time_format.Run()) |
| 450 return false; |
| 451 } |
| 452 |
| 453 if (current_version <= 16) { |
| 454 sql::Statement reset_zero_click; |
| 455 reset_zero_click.Assign(db->GetCachedStatement( |
| 456 SQL_FROM_HERE, "UPDATE logins SET skip_zero_click = 1")); |
| 457 if (!reset_zero_click.Run()) |
| 458 return false; |
| 459 } |
| 460 |
| 461 return true; |
| 462 } |
| 463 |
| 464 // Because of https://crbug.com/295851, some early version numbers might be |
| 465 // wrong. This function detects that and fixes the version. |
| 466 bool FixVersionIfNeeded(sql::Connection* db, int* current_version) { |
| 467 if (*current_version == 1) { |
| 468 int extra_columns = 0; |
| 469 if (db->DoesColumnExist("logins", "password_type")) |
| 470 ++extra_columns; |
| 471 if (db->DoesColumnExist("logins", "possible_usernames")) |
| 472 ++extra_columns; |
| 473 if (extra_columns == 2) { |
| 474 *current_version = 2; |
| 475 } else if (extra_columns == 1) { |
| 476 // If this is https://crbug.com/295851 then either both columns exist |
| 477 // or none. |
| 478 return false; |
| 479 } |
| 480 } |
| 481 if (*current_version == 2) { |
| 482 if (db->DoesColumnExist("logins", "times_used")) |
| 483 *current_version = 3; |
| 484 } |
| 485 if (*current_version == 3) { |
| 486 if (db->DoesColumnExist("logins", "form_data")) |
| 487 *current_version = 4; |
| 488 } |
| 489 return true; |
| 490 } |
| 491 |
| 492 // Generates the string "(?,?,...,?)" with |count| repetitions of "?". |
| 493 std::string GeneratePlaceholders(size_t count) { |
| 494 std::string result(2 * count + 1, ','); |
| 495 result.front() = '('; |
| 496 result.back() = ')'; |
| 497 for (size_t i = 1; i < 2 * count + 1; i += 2) { |
| 498 result[i] = '?'; |
| 499 } |
| 500 return result; |
| 377 } | 501 } |
| 378 | 502 |
| 379 } // namespace | 503 } // namespace |
| 380 | 504 |
| 381 LoginDatabase::LoginDatabase(const base::FilePath& db_path) | 505 LoginDatabase::LoginDatabase(const base::FilePath& db_path) |
| 382 : db_path_(db_path), clear_password_values_(false) { | 506 : db_path_(db_path), clear_password_values_(false) { |
| 383 } | 507 } |
| 384 | 508 |
| 385 LoginDatabase::~LoginDatabase() { | 509 LoginDatabase::~LoginDatabase() { |
| 386 } | 510 } |
| (...skipping 30 matching lines...) Expand all Loading... |
| 417 } | 541 } |
| 418 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { | 542 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { |
| 419 LogDatabaseInitError(INCOMPATIBLE_VERSION); | 543 LogDatabaseInitError(INCOMPATIBLE_VERSION); |
| 420 LOG(ERROR) << "Password store database is too new, kCurrentVersionNumber=" | 544 LOG(ERROR) << "Password store database is too new, kCurrentVersionNumber=" |
| 421 << kCurrentVersionNumber << ", GetCompatibleVersionNumber=" | 545 << kCurrentVersionNumber << ", GetCompatibleVersionNumber=" |
| 422 << meta_table_.GetCompatibleVersionNumber(); | 546 << meta_table_.GetCompatibleVersionNumber(); |
| 423 db_.Close(); | 547 db_.Close(); |
| 424 return false; | 548 return false; |
| 425 } | 549 } |
| 426 | 550 |
| 427 // Initialize the tables. | 551 SQLTableBuilder builder; |
| 428 if (!InitLoginsTable()) { | 552 InitializeBuilder(&builder); |
| 429 LogDatabaseInitError(INIT_LOGINS_ERROR); | 553 InitializeStatementStrings(builder); |
| 430 LOG(ERROR) << "Unable to initialize the logins table."; | 554 |
| 431 db_.Close(); | 555 if (!db_.DoesTableExist("logins")) { |
| 432 return false; | 556 if (!builder.CreateTable(&db_)) { |
| 557 VLOG(0) << "Failed to create the 'logins' table"; |
| 558 db_.Close(); |
| 559 return false; |
| 560 } |
| 433 } | 561 } |
| 562 |
| 434 stats_table_.Init(&db_); | 563 stats_table_.Init(&db_); |
| 435 | 564 |
| 565 int current_version = meta_table_.GetVersionNumber(); |
| 566 bool migration_success = FixVersionIfNeeded(&db_, ¤t_version); |
| 567 DCHECK_LE(current_version, kCurrentVersionNumber); |
| 568 |
| 436 // If the file on disk is an older database version, bring it up to date. | 569 // If the file on disk is an older database version, bring it up to date. |
| 437 if (meta_table_.GetVersionNumber() < kCurrentVersionNumber && | 570 if (migration_success && current_version < kCurrentVersionNumber) { |
| 438 !MigrateOldVersionsAsNeeded()) { | 571 migration_success = MigrateLogins( |
| 572 base::checked_cast<unsigned>(current_version), &builder, &db_); |
| 573 } |
| 574 if (migration_success && current_version <= 15) { |
| 575 migration_success = stats_table_.MigrateToVersion(16); |
| 576 } |
| 577 if (migration_success) { |
| 578 meta_table_.SetCompatibleVersionNumber(kCompatibleVersionNumber); |
| 579 meta_table_.SetVersionNumber(kCurrentVersionNumber); |
| 580 } else { |
| 439 LogDatabaseInitError(MIGRATION_ERROR); | 581 LogDatabaseInitError(MIGRATION_ERROR); |
| 440 UMA_HISTOGRAM_SPARSE_SLOWLY("PasswordManager.LoginDatabaseFailedVersion", | 582 UMA_HISTOGRAM_SPARSE_SLOWLY("PasswordManager.LoginDatabaseFailedVersion", |
| 441 meta_table_.GetVersionNumber()); | 583 meta_table_.GetVersionNumber()); |
| 442 LOG(ERROR) << "Unable to migrate database from " | 584 LOG(ERROR) << "Unable to migrate database from " |
| 443 << meta_table_.GetVersionNumber() << " to " | 585 << meta_table_.GetVersionNumber() << " to " |
| 444 << kCurrentVersionNumber; | 586 << kCurrentVersionNumber; |
| 445 db_.Close(); | 587 db_.Close(); |
| 446 return false; | 588 return false; |
| 447 } | 589 } |
| 448 | 590 |
| 449 if (!stats_table_.CreateTableIfNecessary()) { | 591 if (!stats_table_.CreateTableIfNecessary()) { |
| 450 LogDatabaseInitError(INIT_STATS_ERROR); | 592 LogDatabaseInitError(INIT_STATS_ERROR); |
| 451 LOG(ERROR) << "Unable to create the stats table."; | 593 LOG(ERROR) << "Unable to create the stats table."; |
| 452 db_.Close(); | 594 db_.Close(); |
| 453 return false; | 595 return false; |
| 454 } | 596 } |
| 455 | 597 |
| 456 if (!transaction.Commit()) { | 598 if (!transaction.Commit()) { |
| 457 LogDatabaseInitError(COMMIT_TRANSACTION_ERROR); | 599 LogDatabaseInitError(COMMIT_TRANSACTION_ERROR); |
| 458 LOG(ERROR) << "Unable to commit a transaction."; | 600 LOG(ERROR) << "Unable to commit a transaction."; |
| 459 db_.Close(); | 601 db_.Close(); |
| 460 return false; | 602 return false; |
| 461 } | 603 } |
| 462 | 604 |
| 463 LogDatabaseInitError(INIT_OK); | 605 LogDatabaseInitError(INIT_OK); |
| 464 return true; | 606 return true; |
| 465 } | 607 } |
| 466 | 608 |
| 467 bool LoginDatabase::MigrateOldVersionsAsNeeded() { | |
| 468 const int original_version = meta_table_.GetVersionNumber(); | |
| 469 switch (original_version) { | |
| 470 case 1: | |
| 471 // Column could exist because of https://crbug.com/295851 | |
| 472 if (!db_.DoesColumnExist("logins", "password_type") && | |
| 473 !db_.Execute("ALTER TABLE logins " | |
| 474 "ADD COLUMN password_type INTEGER")) { | |
| 475 return false; | |
| 476 } | |
| 477 if (!db_.DoesColumnExist("logins", "possible_usernames") && | |
| 478 !db_.Execute("ALTER TABLE logins " | |
| 479 "ADD COLUMN possible_usernames BLOB")) { | |
| 480 return false; | |
| 481 } | |
| 482 // Fall through. | |
| 483 case 2: | |
| 484 // Column could exist because of https://crbug.com/295851 | |
| 485 if (!db_.DoesColumnExist("logins", "times_used") && | |
| 486 !db_.Execute("ALTER TABLE logins ADD COLUMN times_used INTEGER")) { | |
| 487 return false; | |
| 488 } | |
| 489 // Fall through. | |
| 490 case 3: | |
| 491 // Column could exist because of https://crbug.com/295851 | |
| 492 if (!db_.DoesColumnExist("logins", "form_data") && | |
| 493 !db_.Execute("ALTER TABLE logins ADD COLUMN form_data BLOB")) { | |
| 494 return false; | |
| 495 } | |
| 496 // Fall through. | |
| 497 case 4: | |
| 498 if (!db_.Execute( | |
| 499 "ALTER TABLE logins ADD COLUMN use_additional_auth INTEGER")) { | |
| 500 return false; | |
| 501 } | |
| 502 // Fall through. | |
| 503 case 5: | |
| 504 if (!db_.Execute("ALTER TABLE logins ADD COLUMN date_synced INTEGER")) { | |
| 505 return false; | |
| 506 } | |
| 507 // Fall through. | |
| 508 case 6: | |
| 509 if (!db_.Execute("ALTER TABLE logins ADD COLUMN display_name VARCHAR") || | |
| 510 !db_.Execute("ALTER TABLE logins ADD COLUMN avatar_url VARCHAR") || | |
| 511 !db_.Execute("ALTER TABLE logins " | |
| 512 "ADD COLUMN federation_url VARCHAR") || | |
| 513 !db_.Execute("ALTER TABLE logins ADD COLUMN is_zero_click INTEGER")) { | |
| 514 return false; | |
| 515 } | |
| 516 // Fall through. | |
| 517 case 7: { | |
| 518 // Keep version 8 around even though no changes are made. See | |
| 519 // crbug.com/423716 for context. | |
| 520 // Fall through. | |
| 521 } | |
| 522 case 8: { | |
| 523 sql::Statement s; | |
| 524 s.Assign(db_.GetCachedStatement(SQL_FROM_HERE, | |
| 525 "UPDATE logins SET " | |
| 526 "date_created = " | |
| 527 "(date_created * ?) + ?")); | |
| 528 s.BindInt64(0, base::Time::kMicrosecondsPerSecond); | |
| 529 s.BindInt64(1, base::Time::kTimeTToMicrosecondsOffset); | |
| 530 if (!s.Run()) | |
| 531 return false; | |
| 532 // Fall through. | |
| 533 } | |
| 534 case 9: { | |
| 535 // Remove use_additional_auth column from database schema | |
| 536 // crbug.com/423716 for context. | |
| 537 std::string fields_to_copy = | |
| 538 "origin_url, action_url, username_element, username_value, " | |
| 539 "password_element, password_value, submit_element, " | |
| 540 "signon_realm, ssl_valid, preferred, date_created, " | |
| 541 "blacklisted_by_user, scheme, password_type, possible_usernames, " | |
| 542 "times_used, form_data, date_synced, display_name, avatar_url, " | |
| 543 "federation_url, is_zero_click"; | |
| 544 auto copy_data_query = | |
| 545 [&fields_to_copy](const std::string& from, const std::string& to) { | |
| 546 return "INSERT INTO " + to + " SELECT " + fields_to_copy + " FROM " + | |
| 547 from; | |
| 548 }; | |
| 549 | |
| 550 if (!db_.Execute(("CREATE TEMPORARY TABLE logins_data(" + fields_to_copy + | |
| 551 ")").c_str()) || | |
| 552 !db_.Execute(copy_data_query("logins", "logins_data").c_str()) || | |
| 553 !db_.Execute("DROP TABLE logins") || | |
| 554 !db_.Execute( | |
| 555 ("CREATE TABLE logins(" + fields_to_copy + ")").c_str()) || | |
| 556 !db_.Execute(copy_data_query("logins_data", "logins").c_str()) || | |
| 557 !db_.Execute("DROP TABLE logins_data") || | |
| 558 !CreateIndexOnSignonRealm(&db_, "logins")) { | |
| 559 return false; | |
| 560 } | |
| 561 // Fall through. | |
| 562 } | |
| 563 case 10: { | |
| 564 // Rename is_zero_click -> skip_zero_click. Note that previous versions | |
| 565 // may have incorrectly used a 6-column key (origin_url, username_element, | |
| 566 // username_value, password_element, signon_realm, submit_element). | |
| 567 // In that case, this step also restores the correct 5-column key; | |
| 568 // that is, the above without "submit_element". | |
| 569 const char copy_query[] = "INSERT OR REPLACE INTO logins_new SELECT " | |
| 570 "origin_url, action_url, username_element, username_value, " | |
| 571 "password_element, password_value, submit_element, signon_realm, " | |
| 572 "ssl_valid, preferred, date_created, blacklisted_by_user, scheme, " | |
| 573 "password_type, possible_usernames, times_used, form_data, " | |
| 574 "date_synced, display_name, avatar_url, federation_url, is_zero_click" | |
| 575 " FROM logins"; | |
| 576 if (!CreateNewTable(&db_, "logins_new", "") || | |
| 577 !db_.Execute(copy_query) || | |
| 578 !db_.Execute("DROP TABLE logins") || | |
| 579 !db_.Execute("ALTER TABLE logins_new RENAME TO logins") || | |
| 580 !CreateIndexOnSignonRealm(&db_, "logins")) { | |
| 581 return false; | |
| 582 } | |
| 583 // Fall through. | |
| 584 } | |
| 585 case 11: | |
| 586 if (!db_.Execute( | |
| 587 "ALTER TABLE logins ADD COLUMN " | |
| 588 "generation_upload_status INTEGER")) | |
| 589 return false; | |
| 590 // Fall through. | |
| 591 case 12: | |
| 592 // The stats table was added. Nothing to do really. | |
| 593 // Fall through. | |
| 594 case 13: { | |
| 595 // Rename avatar_url -> icon_url. Note that if the original version was | |
| 596 // at most 10, this renaming would have already happened in step 10, | |
| 597 // as |CreateNewTable| would create a table with the new column name. | |
| 598 if (original_version > 10) { | |
| 599 const char copy_query[] = "INSERT OR REPLACE INTO logins_new SELECT " | |
| 600 "origin_url, action_url, username_element, username_value, " | |
| 601 "password_element, password_value, submit_element, signon_realm, " | |
| 602 "ssl_valid, preferred, date_created, blacklisted_by_user, scheme, " | |
| 603 "password_type, possible_usernames, times_used, form_data, " | |
| 604 "date_synced, display_name, avatar_url, federation_url, " | |
| 605 "skip_zero_click, generation_upload_status FROM logins"; | |
| 606 if (!CreateNewTable( | |
| 607 &db_, "logins_new", "generation_upload_status INTEGER,") || | |
| 608 !db_.Execute(copy_query) || | |
| 609 !db_.Execute("DROP TABLE logins") || | |
| 610 !db_.Execute("ALTER TABLE logins_new RENAME TO logins") || | |
| 611 !CreateIndexOnSignonRealm(&db_, "logins")) { | |
| 612 return false; | |
| 613 } | |
| 614 } | |
| 615 // Fall through. | |
| 616 } | |
| 617 case 14: | |
| 618 // No change of schema. Version 15 was introduced to force all databases | |
| 619 // through an otherwise no-op migration process that will, however, now | |
| 620 // correctly set the 'compatible version number'. Previously, it was | |
| 621 // always being set to (and forever left at) version 1. | |
| 622 meta_table_.SetCompatibleVersionNumber(kCompatibleVersionNumber); | |
| 623 case 15: | |
| 624 // Recreate the statistics. | |
| 625 if (!stats_table_.MigrateToVersion(16)) | |
| 626 return false; | |
| 627 case 16: { | |
| 628 // No change in scheme: just disable auto sign-in by default in | |
| 629 // preparation to launch the credential management API. | |
| 630 if (!db_.Execute("UPDATE logins SET skip_zero_click = 1")) | |
| 631 return false; | |
| 632 // Fall through. | |
| 633 } | |
| 634 | |
| 635 // ------------------------------------------------------------------------- | |
| 636 // DO NOT FORGET to update |kCompatibleVersionNumber| if you add a migration | |
| 637 // step that is a breaking change. This is needed so that an older version | |
| 638 // of the browser can fail with a meaningful error when opening a newer | |
| 639 // database, as opposed to failing on the first database operation. | |
| 640 // ------------------------------------------------------------------------- | |
| 641 case kCurrentVersionNumber: | |
| 642 // Already up to date. | |
| 643 meta_table_.SetVersionNumber(kCurrentVersionNumber); | |
| 644 return true; | |
| 645 default: | |
| 646 NOTREACHED(); | |
| 647 return false; | |
| 648 } | |
| 649 } | |
| 650 | |
| 651 bool LoginDatabase::InitLoginsTable() { | |
| 652 if (!db_.DoesTableExist("logins")) { | |
| 653 if (!CreateNewTable(&db_, "logins", "generation_upload_status INTEGER,")) { | |
| 654 NOTREACHED(); | |
| 655 return false; | |
| 656 } | |
| 657 if (!CreateIndexOnSignonRealm(&db_, "logins")) { | |
| 658 NOTREACHED(); | |
| 659 return false; | |
| 660 } | |
| 661 } | |
| 662 return true; | |
| 663 } | |
| 664 | |
| 665 void LoginDatabase::ReportMetrics(const std::string& sync_username, | 609 void LoginDatabase::ReportMetrics(const std::string& sync_username, |
| 666 bool custom_passphrase_sync_enabled) { | 610 bool custom_passphrase_sync_enabled) { |
| 667 sql::Statement s(db_.GetCachedStatement( | 611 sql::Statement s(db_.GetCachedStatement( |
| 668 SQL_FROM_HERE, | 612 SQL_FROM_HERE, |
| 669 "SELECT signon_realm, password_type, blacklisted_by_user," | 613 "SELECT signon_realm, password_type, blacklisted_by_user," |
| 670 "COUNT(username_value) FROM logins GROUP BY " | 614 "COUNT(username_value) FROM logins GROUP BY " |
| 671 "signon_realm, password_type, blacklisted_by_user")); | 615 "signon_realm, password_type, blacklisted_by_user")); |
| 672 | 616 |
| 673 if (!s.is_valid()) | 617 if (!s.is_valid()) |
| 674 return; | 618 return; |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 849 PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) { | 793 PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) { |
| 850 PasswordStoreChangeList list; | 794 PasswordStoreChangeList list; |
| 851 if (!DoesMatchConstraints(form)) | 795 if (!DoesMatchConstraints(form)) |
| 852 return list; | 796 return list; |
| 853 std::string encrypted_password; | 797 std::string encrypted_password; |
| 854 if (EncryptedString( | 798 if (EncryptedString( |
| 855 clear_password_values_ ? base::string16() : form.password_value, | 799 clear_password_values_ ? base::string16() : form.password_value, |
| 856 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) | 800 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) |
| 857 return list; | 801 return list; |
| 858 | 802 |
| 859 // You *must* change LoginTableColumns if this query changes. | 803 DCHECK(!add_statement_.empty()); |
| 860 sql::Statement s(db_.GetCachedStatement( | 804 sql::Statement s( |
| 861 SQL_FROM_HERE, | 805 db_.GetCachedStatement(SQL_FROM_HERE, add_statement_.c_str())); |
| 862 "INSERT INTO logins " | |
| 863 "(origin_url, action_url, username_element, username_value, " | |
| 864 " password_element, password_value, submit_element, " | |
| 865 " signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 866 " scheme, password_type, possible_usernames, times_used, form_data, " | |
| 867 " date_synced, display_name, icon_url," | |
| 868 " federation_url, skip_zero_click, generation_upload_status) VALUES " | |
| 869 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); | |
| 870 BindAddStatement(form, encrypted_password, &s); | 806 BindAddStatement(form, encrypted_password, &s); |
| 871 db_.set_error_callback(base::Bind(&AddCallback)); | 807 db_.set_error_callback(base::Bind(&AddCallback)); |
| 872 const bool success = s.Run(); | 808 const bool success = s.Run(); |
| 873 db_.reset_error_callback(); | 809 db_.reset_error_callback(); |
| 874 if (success) { | 810 if (success) { |
| 875 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); | 811 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); |
| 876 return list; | 812 return list; |
| 877 } | 813 } |
| 878 // Repeat the same statement but with REPLACE semantic. | 814 // Repeat the same statement but with REPLACE semantic. |
| 879 s.Assign(db_.GetCachedStatement( | 815 DCHECK(!add_replace_statement_.empty()); |
| 880 SQL_FROM_HERE, | 816 s.Assign( |
| 881 "INSERT OR REPLACE INTO logins " | 817 db_.GetCachedStatement(SQL_FROM_HERE, add_replace_statement_.c_str())); |
| 882 "(origin_url, action_url, username_element, username_value, " | |
| 883 " password_element, password_value, submit_element, " | |
| 884 " signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 885 " scheme, password_type, possible_usernames, times_used, form_data, " | |
| 886 " date_synced, display_name, icon_url," | |
| 887 " federation_url, skip_zero_click, generation_upload_status) VALUES " | |
| 888 "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); | |
| 889 BindAddStatement(form, encrypted_password, &s); | 818 BindAddStatement(form, encrypted_password, &s); |
| 890 if (s.Run()) { | 819 if (s.Run()) { |
| 891 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); | 820 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); |
| 892 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); | 821 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form)); |
| 893 } | 822 } |
| 894 return list; | 823 return list; |
| 895 } | 824 } |
| 896 | 825 |
| 897 PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) { | 826 PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) { |
| 898 std::string encrypted_password; | 827 std::string encrypted_password; |
| 899 if (EncryptedString( | 828 if (EncryptedString( |
| 900 clear_password_values_ ? base::string16() : form.password_value, | 829 clear_password_values_ ? base::string16() : form.password_value, |
| 901 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) | 830 &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) |
| 902 return PasswordStoreChangeList(); | 831 return PasswordStoreChangeList(); |
| 903 | 832 |
| 904 #if defined(OS_IOS) | 833 #if defined(OS_IOS) |
| 905 DeleteEncryptedPassword(form); | 834 DeleteEncryptedPassword(form); |
| 906 #endif | 835 #endif |
| 907 // Replacement is necessary to deal with updating imported credentials. See | 836 // Replacement is necessary to deal with updating imported credentials. See |
| 908 // crbug.com/349138 for details. | 837 // crbug.com/349138 for details. |
| 909 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, | 838 DCHECK(!update_statement_.empty()); |
| 910 "UPDATE OR REPLACE logins SET " | 839 sql::Statement s( |
| 911 "action_url = ?, " | 840 db_.GetCachedStatement(SQL_FROM_HERE, update_statement_.c_str())); |
| 912 "password_value = ?, " | |
| 913 "ssl_valid = ?, " | |
| 914 "preferred = ?, " | |
| 915 "possible_usernames = ?, " | |
| 916 "times_used = ?, " | |
| 917 "submit_element = ?, " | |
| 918 "date_synced = ?, " | |
| 919 "date_created = ?, " | |
| 920 "blacklisted_by_user = ?, " | |
| 921 "scheme = ?, " | |
| 922 "password_type = ?, " | |
| 923 "display_name = ?, " | |
| 924 "icon_url = ?, " | |
| 925 "federation_url = ?, " | |
| 926 "skip_zero_click = ?, " | |
| 927 "generation_upload_status = ? " | |
| 928 "WHERE origin_url = ? AND " | |
| 929 "username_element = ? AND " | |
| 930 "username_value = ? AND " | |
| 931 "password_element = ? AND " | |
| 932 "signon_realm = ?")); | |
| 933 s.BindString(0, form.action.spec()); | 841 s.BindString(0, form.action.spec()); |
| 934 s.BindBlob(1, encrypted_password.data(), | 842 s.BindBlob(1, encrypted_password.data(), |
| 935 static_cast<int>(encrypted_password.length())); | 843 static_cast<int>(encrypted_password.length())); |
| 936 s.BindInt(2, form.ssl_valid); | 844 s.BindString16(2, form.submit_element); |
| 937 s.BindInt(3, form.preferred); | 845 s.BindInt(3, form.ssl_valid); |
| 846 s.BindInt(4, form.preferred); |
| 847 s.BindInt64(5, form.date_created.ToInternalValue()); |
| 848 s.BindInt(6, form.blacklisted_by_user); |
| 849 s.BindInt(7, form.scheme); |
| 850 s.BindInt(8, form.type); |
| 938 base::Pickle pickle = SerializeVector(form.other_possible_usernames); | 851 base::Pickle pickle = SerializeVector(form.other_possible_usernames); |
| 939 s.BindBlob(4, pickle.data(), pickle.size()); | 852 s.BindBlob(9, pickle.data(), pickle.size()); |
| 940 s.BindInt(5, form.times_used); | 853 s.BindInt(10, form.times_used); |
| 941 s.BindString16(6, form.submit_element); | 854 base::Pickle form_data_pickle; |
| 942 s.BindInt64(7, form.date_synced.ToInternalValue()); | 855 autofill::SerializeFormData(form.form_data, &form_data_pickle); |
| 943 s.BindInt64(8, form.date_created.ToInternalValue()); | 856 s.BindBlob(11, form_data_pickle.data(), form_data_pickle.size()); |
| 944 s.BindInt(9, form.blacklisted_by_user); | 857 s.BindInt64(12, form.date_synced.ToInternalValue()); |
| 945 s.BindInt(10, form.scheme); | 858 s.BindString16(13, form.display_name); |
| 946 s.BindInt(11, form.type); | 859 s.BindString(14, form.icon_url.spec()); |
| 947 s.BindString16(12, form.display_name); | |
| 948 s.BindString(13, form.icon_url.spec()); | |
| 949 // An empty Origin serializes as "null" which would be strange to store here. | 860 // An empty Origin serializes as "null" which would be strange to store here. |
| 950 s.BindString(14, form.federation_origin.unique() | 861 s.BindString(15, form.federation_origin.unique() |
| 951 ? std::string() | 862 ? std::string() |
| 952 : form.federation_origin.Serialize()); | 863 : form.federation_origin.Serialize()); |
| 953 s.BindInt(15, form.skip_zero_click); | 864 s.BindInt(16, form.skip_zero_click); |
| 954 s.BindInt(16, form.generation_upload_status); | 865 s.BindInt(17, form.generation_upload_status); |
| 955 | 866 |
| 956 // WHERE starts here. | 867 // WHERE starts here. |
| 957 s.BindString(17, form.origin.spec()); | 868 s.BindString(18, form.origin.spec()); |
| 958 s.BindString16(18, form.username_element); | 869 s.BindString16(19, form.username_element); |
| 959 s.BindString16(19, form.username_value); | 870 s.BindString16(20, form.username_value); |
| 960 s.BindString16(20, form.password_element); | 871 s.BindString16(21, form.password_element); |
| 961 s.BindString(21, form.signon_realm); | 872 s.BindString(22, form.signon_realm); |
| 962 | 873 |
| 963 if (!s.Run()) | 874 if (!s.Run()) |
| 964 return PasswordStoreChangeList(); | 875 return PasswordStoreChangeList(); |
| 965 | 876 |
| 966 PasswordStoreChangeList list; | 877 PasswordStoreChangeList list; |
| 967 if (db_.GetLastChangeCount()) | 878 if (db_.GetLastChangeCount()) |
| 968 list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form)); | 879 list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form)); |
| 969 | 880 |
| 970 return list; | 881 return list; |
| 971 } | 882 } |
| 972 | 883 |
| 973 bool LoginDatabase::RemoveLogin(const PasswordForm& form) { | 884 bool LoginDatabase::RemoveLogin(const PasswordForm& form) { |
| 974 if (form.is_public_suffix_match) { | 885 if (form.is_public_suffix_match) { |
| 975 // TODO(dvadym): Discuss whether we should allow to remove PSL matched | 886 // TODO(dvadym): Discuss whether we should allow to remove PSL matched |
| 976 // credentials. | 887 // credentials. |
| 977 return false; | 888 return false; |
| 978 } | 889 } |
| 979 #if defined(OS_IOS) | 890 #if defined(OS_IOS) |
| 980 DeleteEncryptedPassword(form); | 891 DeleteEncryptedPassword(form); |
| 981 #endif | 892 #endif |
| 982 // Remove a login by UNIQUE-constrained fields. | 893 // Remove a login by UNIQUE-constrained fields. |
| 983 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, | 894 DCHECK(!delete_statement_.empty()); |
| 984 "DELETE FROM logins WHERE " | 895 sql::Statement s( |
| 985 "origin_url = ? AND " | 896 db_.GetCachedStatement(SQL_FROM_HERE, delete_statement_.c_str())); |
| 986 "username_element = ? AND " | |
| 987 "username_value = ? AND " | |
| 988 "password_element = ? AND " | |
| 989 "submit_element = ? AND " | |
| 990 "signon_realm = ? ")); | |
| 991 s.BindString(0, form.origin.spec()); | 897 s.BindString(0, form.origin.spec()); |
| 992 s.BindString16(1, form.username_element); | 898 s.BindString16(1, form.username_element); |
| 993 s.BindString16(2, form.username_value); | 899 s.BindString16(2, form.username_value); |
| 994 s.BindString16(3, form.password_element); | 900 s.BindString16(3, form.password_element); |
| 995 s.BindString16(4, form.submit_element); | 901 s.BindString(4, form.signon_realm); |
| 996 s.BindString(5, form.signon_realm); | |
| 997 | 902 |
| 998 return s.Run() && db_.GetLastChangeCount() > 0; | 903 return s.Run() && db_.GetLastChangeCount() > 0; |
| 999 } | 904 } |
| 1000 | 905 |
| 1001 bool LoginDatabase::RemoveLoginsCreatedBetween(base::Time delete_begin, | 906 bool LoginDatabase::RemoveLoginsCreatedBetween(base::Time delete_begin, |
| 1002 base::Time delete_end) { | 907 base::Time delete_end) { |
| 1003 #if defined(OS_IOS) | 908 #if defined(OS_IOS) |
| 1004 ScopedVector<autofill::PasswordForm> forms; | 909 ScopedVector<autofill::PasswordForm> forms; |
| 1005 if (GetLoginsCreatedBetween(delete_begin, delete_end, &forms)) { | 910 if (GetLoginsCreatedBetween(delete_begin, delete_end, &forms)) { |
| 1006 for (size_t i = 0; i < forms.size(); i++) { | 911 for (size_t i = 0; i < forms.size(); i++) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1028 s.BindInt64(1, | 933 s.BindInt64(1, |
| 1029 delete_end.is_null() ? base::Time::Max().ToInternalValue() | 934 delete_end.is_null() ? base::Time::Max().ToInternalValue() |
| 1030 : delete_end.ToInternalValue()); | 935 : delete_end.ToInternalValue()); |
| 1031 | 936 |
| 1032 return s.Run(); | 937 return s.Run(); |
| 1033 } | 938 } |
| 1034 | 939 |
| 1035 bool LoginDatabase::GetAutoSignInLogins( | 940 bool LoginDatabase::GetAutoSignInLogins( |
| 1036 ScopedVector<autofill::PasswordForm>* forms) const { | 941 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1037 DCHECK(forms); | 942 DCHECK(forms); |
| 1038 sql::Statement s(db_.GetCachedStatement( | 943 DCHECK(!autosignin_statement_.empty()); |
| 1039 SQL_FROM_HERE, | 944 sql::Statement s( |
| 1040 "SELECT origin_url, action_url, username_element, username_value, " | 945 db_.GetCachedStatement(SQL_FROM_HERE, autosignin_statement_.c_str())); |
| 1041 "password_element, password_value, submit_element, signon_realm, " | |
| 1042 "ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 1043 "scheme, password_type, possible_usernames, times_used, form_data, " | |
| 1044 "date_synced, display_name, icon_url, " | |
| 1045 "federation_url, skip_zero_click, generation_upload_status FROM logins " | |
| 1046 "WHERE skip_zero_click = 0 ORDER BY origin_url")); | |
| 1047 | 946 |
| 1048 return StatementToForms(&s, nullptr, forms); | 947 return StatementToForms(&s, nullptr, forms); |
| 1049 } | 948 } |
| 1050 | 949 |
| 1051 bool LoginDatabase::DisableAutoSignInForOrigin(const GURL& origin) { | 950 bool LoginDatabase::DisableAutoSignInForOrigin(const GURL& origin) { |
| 1052 sql::Statement s(db_.GetCachedStatement( | 951 sql::Statement s(db_.GetCachedStatement( |
| 1053 SQL_FROM_HERE, | 952 SQL_FROM_HERE, |
| 1054 "UPDATE logins SET skip_zero_click = 1 WHERE origin_url = ?;")); | 953 "UPDATE logins SET skip_zero_click = 1 WHERE origin_url = ?;")); |
| 1055 s.BindString(0, origin.spec()); | 954 s.BindString(0, origin.spec()); |
| 1056 | 955 |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1128 form->generation_upload_status = | 1027 form->generation_upload_status = |
| 1129 static_cast<PasswordForm::GenerationUploadStatus>( | 1028 static_cast<PasswordForm::GenerationUploadStatus>( |
| 1130 generation_upload_status_int); | 1029 generation_upload_status_int); |
| 1131 return ENCRYPTION_RESULT_SUCCESS; | 1030 return ENCRYPTION_RESULT_SUCCESS; |
| 1132 } | 1031 } |
| 1133 | 1032 |
| 1134 bool LoginDatabase::GetLogins( | 1033 bool LoginDatabase::GetLogins( |
| 1135 const PasswordForm& form, | 1034 const PasswordForm& form, |
| 1136 ScopedVector<autofill::PasswordForm>* forms) const { | 1035 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1137 DCHECK(forms); | 1036 DCHECK(forms); |
| 1138 // You *must* change LoginTableColumns if this query changes. | |
| 1139 std::string sql_query = | |
| 1140 "SELECT origin_url, action_url, " | |
| 1141 "username_element, username_value, " | |
| 1142 "password_element, password_value, submit_element, " | |
| 1143 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 1144 "scheme, password_type, possible_usernames, times_used, form_data, " | |
| 1145 "date_synced, display_name, icon_url, " | |
| 1146 "federation_url, skip_zero_click, generation_upload_status " | |
| 1147 "FROM logins WHERE signon_realm == ? "; | |
| 1148 const GURL signon_realm(form.signon_realm); | 1037 const GURL signon_realm(form.signon_realm); |
| 1149 std::string registered_domain = GetRegistryControlledDomain(signon_realm); | 1038 std::string registered_domain = GetRegistryControlledDomain(signon_realm); |
| 1150 const bool should_PSL_matching_apply = | 1039 const bool should_PSL_matching_apply = |
| 1151 form.scheme == PasswordForm::SCHEME_HTML && | 1040 form.scheme == PasswordForm::SCHEME_HTML && |
| 1152 ShouldPSLDomainMatchingApply(registered_domain); | 1041 ShouldPSLDomainMatchingApply(registered_domain); |
| 1153 const bool should_federated_apply = form.scheme == PasswordForm::SCHEME_HTML; | 1042 const bool should_federated_apply = form.scheme == PasswordForm::SCHEME_HTML; |
| 1154 if (should_PSL_matching_apply) | 1043 DCHECK(!get_statement_.empty()); |
| 1155 sql_query += "OR signon_realm REGEXP ? "; | 1044 DCHECK(!get_statement_psl_.empty()); |
| 1156 if (should_federated_apply) | 1045 DCHECK(!get_statement_federated_.empty()); |
| 1157 sql_query += "OR (signon_realm LIKE ? AND password_type == 2) "; | 1046 DCHECK(!get_statement_psl_federated_.empty()); |
| 1047 const std::string* sql_query = &get_statement_; |
| 1048 if (should_PSL_matching_apply && should_federated_apply) |
| 1049 sql_query = &get_statement_psl_federated_; |
| 1050 else if (should_PSL_matching_apply) |
| 1051 sql_query = &get_statement_psl_; |
| 1052 else if (should_federated_apply) |
| 1053 sql_query = &get_statement_federated_; |
| 1158 | 1054 |
| 1159 // TODO(nyquist) Consider usage of GetCachedStatement when | 1055 // TODO(nyquist) Consider usage of GetCachedStatement when |
| 1160 // http://crbug.com/248608 is fixed. | 1056 // http://crbug.com/248608 is fixed. |
| 1161 sql::Statement s(db_.GetUniqueStatement(sql_query.c_str())); | 1057 sql::Statement s(db_.GetUniqueStatement(sql_query->c_str())); |
| 1162 s.BindString(0, form.signon_realm); | 1058 s.BindString(0, form.signon_realm); |
| 1163 int placeholder = 1; | 1059 int placeholder = 1; |
| 1164 | 1060 |
| 1165 // PSL matching only applies to HTML forms. | 1061 // PSL matching only applies to HTML forms. |
| 1166 if (should_PSL_matching_apply) { | 1062 if (should_PSL_matching_apply) { |
| 1167 // We are extending the original SQL query with one that includes more | 1063 // We are extending the original SQL query with one that includes more |
| 1168 // possible matches based on public suffix domain matching. Using a regexp | 1064 // possible matches based on public suffix domain matching. Using a regexp |
| 1169 // here is just an optimization to not have to parse all the stored entries | 1065 // here is just an optimization to not have to parse all the stored entries |
| 1170 // in the |logins| table. The result (scheme, domain and port) is verified | 1066 // in the |logins| table. The result (scheme, domain and port) is verified |
| 1171 // further down using GURL. See the functions SchemeMatches, | 1067 // further down using GURL. See the functions SchemeMatches, |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1203 return StatementToForms( | 1099 return StatementToForms( |
| 1204 &s, should_PSL_matching_apply || should_federated_apply ? &form : nullptr, | 1100 &s, should_PSL_matching_apply || should_federated_apply ? &form : nullptr, |
| 1205 forms); | 1101 forms); |
| 1206 } | 1102 } |
| 1207 | 1103 |
| 1208 bool LoginDatabase::GetLoginsCreatedBetween( | 1104 bool LoginDatabase::GetLoginsCreatedBetween( |
| 1209 const base::Time begin, | 1105 const base::Time begin, |
| 1210 const base::Time end, | 1106 const base::Time end, |
| 1211 ScopedVector<autofill::PasswordForm>* forms) const { | 1107 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1212 DCHECK(forms); | 1108 DCHECK(forms); |
| 1213 sql::Statement s(db_.GetCachedStatement( | 1109 DCHECK(!created_statement_.empty()); |
| 1214 SQL_FROM_HERE, | 1110 sql::Statement s( |
| 1215 "SELECT origin_url, action_url, " | 1111 db_.GetCachedStatement(SQL_FROM_HERE, created_statement_.c_str())); |
| 1216 "username_element, username_value, " | |
| 1217 "password_element, password_value, submit_element, " | |
| 1218 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 1219 "scheme, password_type, possible_usernames, times_used, form_data, " | |
| 1220 "date_synced, display_name, icon_url, " | |
| 1221 "federation_url, skip_zero_click, generation_upload_status FROM logins " | |
| 1222 "WHERE date_created >= ? AND date_created < ?" | |
| 1223 "ORDER BY origin_url")); | |
| 1224 s.BindInt64(0, begin.ToInternalValue()); | 1112 s.BindInt64(0, begin.ToInternalValue()); |
| 1225 s.BindInt64(1, end.is_null() ? std::numeric_limits<int64_t>::max() | 1113 s.BindInt64(1, end.is_null() ? std::numeric_limits<int64_t>::max() |
| 1226 : end.ToInternalValue()); | 1114 : end.ToInternalValue()); |
| 1227 | 1115 |
| 1228 return StatementToForms(&s, nullptr, forms); | 1116 return StatementToForms(&s, nullptr, forms); |
| 1229 } | 1117 } |
| 1230 | 1118 |
| 1231 bool LoginDatabase::GetLoginsSyncedBetween( | 1119 bool LoginDatabase::GetLoginsSyncedBetween( |
| 1232 const base::Time begin, | 1120 const base::Time begin, |
| 1233 const base::Time end, | 1121 const base::Time end, |
| 1234 ScopedVector<autofill::PasswordForm>* forms) const { | 1122 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1235 DCHECK(forms); | 1123 DCHECK(forms); |
| 1236 sql::Statement s(db_.GetCachedStatement( | 1124 DCHECK(!synced_statement_.empty()); |
| 1237 SQL_FROM_HERE, | 1125 sql::Statement s( |
| 1238 "SELECT origin_url, action_url, username_element, username_value, " | 1126 db_.GetCachedStatement(SQL_FROM_HERE, synced_statement_.c_str())); |
| 1239 "password_element, password_value, submit_element, signon_realm, " | |
| 1240 "ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 1241 "scheme, password_type, possible_usernames, times_used, form_data, " | |
| 1242 "date_synced, display_name, icon_url, " | |
| 1243 "federation_url, skip_zero_click, generation_upload_status FROM logins " | |
| 1244 "WHERE date_synced >= ? AND date_synced < ?" | |
| 1245 "ORDER BY origin_url")); | |
| 1246 s.BindInt64(0, begin.ToInternalValue()); | 1127 s.BindInt64(0, begin.ToInternalValue()); |
| 1247 s.BindInt64(1, | 1128 s.BindInt64(1, |
| 1248 end.is_null() ? base::Time::Max().ToInternalValue() | 1129 end.is_null() ? base::Time::Max().ToInternalValue() |
| 1249 : end.ToInternalValue()); | 1130 : end.ToInternalValue()); |
| 1250 | 1131 |
| 1251 return StatementToForms(&s, nullptr, forms); | 1132 return StatementToForms(&s, nullptr, forms); |
| 1252 } | 1133 } |
| 1253 | 1134 |
| 1254 bool LoginDatabase::GetAutofillableLogins( | 1135 bool LoginDatabase::GetAutofillableLogins( |
| 1255 ScopedVector<autofill::PasswordForm>* forms) const { | 1136 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1256 return GetAllLoginsWithBlacklistSetting(false, forms); | 1137 return GetAllLoginsWithBlacklistSetting(false, forms); |
| 1257 } | 1138 } |
| 1258 | 1139 |
| 1259 bool LoginDatabase::GetBlacklistLogins( | 1140 bool LoginDatabase::GetBlacklistLogins( |
| 1260 ScopedVector<autofill::PasswordForm>* forms) const { | 1141 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1261 return GetAllLoginsWithBlacklistSetting(true, forms); | 1142 return GetAllLoginsWithBlacklistSetting(true, forms); |
| 1262 } | 1143 } |
| 1263 | 1144 |
| 1264 bool LoginDatabase::GetAllLoginsWithBlacklistSetting( | 1145 bool LoginDatabase::GetAllLoginsWithBlacklistSetting( |
| 1265 bool blacklisted, | 1146 bool blacklisted, |
| 1266 ScopedVector<autofill::PasswordForm>* forms) const { | 1147 ScopedVector<autofill::PasswordForm>* forms) const { |
| 1267 DCHECK(forms); | 1148 DCHECK(forms); |
| 1268 // You *must* change LoginTableColumns if this query changes. | 1149 DCHECK(!blacklisted_statement_.empty()); |
| 1269 sql::Statement s(db_.GetCachedStatement( | 1150 sql::Statement s( |
| 1270 SQL_FROM_HERE, | 1151 db_.GetCachedStatement(SQL_FROM_HERE, blacklisted_statement_.c_str())); |
| 1271 "SELECT origin_url, action_url, " | |
| 1272 "username_element, username_value, " | |
| 1273 "password_element, password_value, submit_element, " | |
| 1274 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | |
| 1275 "scheme, password_type, possible_usernames, times_used, form_data, " | |
| 1276 "date_synced, display_name, icon_url, " | |
| 1277 "federation_url, skip_zero_click, generation_upload_status FROM logins " | |
| 1278 "WHERE blacklisted_by_user == ? ORDER BY origin_url")); | |
| 1279 s.BindInt(0, blacklisted ? 1 : 0); | 1152 s.BindInt(0, blacklisted ? 1 : 0); |
| 1280 | 1153 |
| 1281 return StatementToForms(&s, nullptr, forms); | 1154 return StatementToForms(&s, nullptr, forms); |
| 1282 } | 1155 } |
| 1283 | 1156 |
| 1284 bool LoginDatabase::DeleteAndRecreateDatabaseFile() { | 1157 bool LoginDatabase::DeleteAndRecreateDatabaseFile() { |
| 1285 DCHECK(db_.is_open()); | 1158 DCHECK(db_.is_open()); |
| 1286 meta_table_.Reset(); | 1159 meta_table_.Reset(); |
| 1287 db_.Close(); | 1160 db_.Close(); |
| 1288 sql::Connection::Delete(db_path_); | 1161 sql::Connection::Delete(db_path_); |
| 1289 return Init(); | 1162 return Init(); |
| 1290 } | 1163 } |
| 1291 | 1164 |
| 1292 std::string LoginDatabase::GetEncryptedPassword( | 1165 std::string LoginDatabase::GetEncryptedPassword( |
| 1293 const autofill::PasswordForm& form) const { | 1166 const autofill::PasswordForm& form) const { |
| 1167 DCHECK(!encrypted_statement_.empty()); |
| 1294 sql::Statement s( | 1168 sql::Statement s( |
| 1295 db_.GetCachedStatement(SQL_FROM_HERE, | 1169 db_.GetCachedStatement(SQL_FROM_HERE, encrypted_statement_.c_str())); |
| 1296 "SELECT password_value FROM logins WHERE " | |
| 1297 "origin_url = ? AND " | |
| 1298 "username_element = ? AND " | |
| 1299 "username_value = ? AND " | |
| 1300 "password_element = ? AND " | |
| 1301 "submit_element = ? AND " | |
| 1302 "signon_realm = ? ")); | |
| 1303 | 1170 |
| 1304 s.BindString(0, form.origin.spec()); | 1171 s.BindString(0, form.origin.spec()); |
| 1305 s.BindString16(1, form.username_element); | 1172 s.BindString16(1, form.username_element); |
| 1306 s.BindString16(2, form.username_value); | 1173 s.BindString16(2, form.username_value); |
| 1307 s.BindString16(3, form.password_element); | 1174 s.BindString16(3, form.password_element); |
| 1308 s.BindString16(4, form.submit_element); | 1175 s.BindString(4, form.signon_realm); |
| 1309 s.BindString(5, form.signon_realm); | |
| 1310 | 1176 |
| 1311 std::string encrypted_password; | 1177 std::string encrypted_password; |
| 1312 if (s.Step()) { | 1178 if (s.Step()) { |
| 1313 s.ColumnBlobAsString(0, &encrypted_password); | 1179 s.ColumnBlobAsString(0, &encrypted_password); |
| 1314 } | 1180 } |
| 1315 return encrypted_password; | 1181 return encrypted_password; |
| 1316 } | 1182 } |
| 1317 | 1183 |
| 1318 // static | 1184 // static |
| 1319 bool LoginDatabase::StatementToForms( | 1185 bool LoginDatabase::StatementToForms( |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1353 if (matched_form) { | 1219 if (matched_form) { |
| 1354 UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering", | 1220 UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering", |
| 1355 psl_domain_match_metric, PSL_DOMAIN_MATCH_COUNT); | 1221 psl_domain_match_metric, PSL_DOMAIN_MATCH_COUNT); |
| 1356 } | 1222 } |
| 1357 | 1223 |
| 1358 if (!statement->Succeeded()) | 1224 if (!statement->Succeeded()) |
| 1359 return false; | 1225 return false; |
| 1360 return true; | 1226 return true; |
| 1361 } | 1227 } |
| 1362 | 1228 |
| 1229 void LoginDatabase::InitializeStatementStrings(const SQLTableBuilder& builder) { |
| 1230 // This method may be called multiple times, if Chrome switches backends and |
| 1231 // LoginDatabase::DeleteAndRecreateDatabaseFile ends up being called. In those |
| 1232 // case do not recompute the SQL statements, because they would end up the |
| 1233 // same. |
| 1234 if (!add_statement_.empty()) |
| 1235 return; |
| 1236 |
| 1237 // Initialize the cached strings. |
| 1238 std::string all_column_names = builder.ListAllColumnNames(); |
| 1239 std::string right_amount_of_placeholders = |
| 1240 GeneratePlaceholders(builder.NumberOfColumns()); |
| 1241 std::string all_unique_key_column_names = builder.ListAllUniqueKeyNames(); |
| 1242 std::string all_nonunique_key_column_names = |
| 1243 builder.ListAllNonuniqueKeyNames(); |
| 1244 |
| 1245 add_statement_ = "INSERT INTO logins (" + all_column_names + ") VALUES " + |
| 1246 right_amount_of_placeholders; |
| 1247 DCHECK(add_replace_statement_.empty()); |
| 1248 add_replace_statement_ = "INSERT OR REPLACE INTO logins (" + |
| 1249 all_column_names + ") VALUES " + |
| 1250 right_amount_of_placeholders; |
| 1251 DCHECK(update_statement_.empty()); |
| 1252 update_statement_ = "UPDATE OR REPLACE logins SET " + |
| 1253 all_nonunique_key_column_names + " WHERE " + |
| 1254 all_unique_key_column_names; |
| 1255 DCHECK(delete_statement_.empty()); |
| 1256 delete_statement_ = "DELETE FROM logins WHERE " + all_unique_key_column_names; |
| 1257 DCHECK(autosignin_statement_.empty()); |
| 1258 autosignin_statement_ = "SELECT " + all_column_names + |
| 1259 " FROM logins " |
| 1260 "WHERE skip_zero_click = 0 ORDER BY origin_url"; |
| 1261 DCHECK(get_statement_.empty()); |
| 1262 get_statement_ = "SELECT " + all_column_names + |
| 1263 " FROM logins " |
| 1264 "WHERE signon_realm == ?"; |
| 1265 std::string psl_statement = "OR signon_realm REGEXP ? "; |
| 1266 std::string federated_statement = |
| 1267 "OR (signon_realm LIKE ? AND password_type == 2) "; |
| 1268 DCHECK(get_statement_psl_.empty()); |
| 1269 get_statement_psl_ = get_statement_ + psl_statement; |
| 1270 DCHECK(get_statement_federated_.empty()); |
| 1271 get_statement_federated_ = get_statement_ + federated_statement; |
| 1272 DCHECK(get_statement_psl_federated_.empty()); |
| 1273 get_statement_psl_federated_ = |
| 1274 get_statement_ + psl_statement + federated_statement; |
| 1275 DCHECK(created_statement_.empty()); |
| 1276 created_statement_ = |
| 1277 "SELECT " + all_column_names + |
| 1278 " FROM logins WHERE date_created >= ? AND date_created < " |
| 1279 "? ORDER BY origin_url"; |
| 1280 DCHECK(synced_statement_.empty()); |
| 1281 synced_statement_ = "SELECT " + all_column_names + |
| 1282 " FROM logins WHERE date_synced >= ? AND date_synced < " |
| 1283 "? ORDER BY origin_url"; |
| 1284 DCHECK(blacklisted_statement_.empty()); |
| 1285 blacklisted_statement_ = |
| 1286 "SELECT " + all_column_names + |
| 1287 " FROM logins WHERE blacklisted_by_user == ? ORDER BY origin_url"; |
| 1288 DCHECK(encrypted_statement_.empty()); |
| 1289 encrypted_statement_ = |
| 1290 "SELECT password_value FROM logins WHERE " + all_unique_key_column_names; |
| 1291 } |
| 1292 |
| 1363 } // namespace password_manager | 1293 } // namespace password_manager |
| OLD | NEW |