| 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 <algorithm> | 7 #include <algorithm> |
| 8 #include <limits> | 8 #include <limits> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 653 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 664 SQL_FROM_HERE, | 664 SQL_FROM_HERE, |
| 665 "DELETE FROM logins WHERE date_synced >= ? AND date_synced < ?")); | 665 "DELETE FROM logins WHERE date_synced >= ? AND date_synced < ?")); |
| 666 s.BindInt64(0, delete_begin.ToInternalValue()); | 666 s.BindInt64(0, delete_begin.ToInternalValue()); |
| 667 s.BindInt64(1, | 667 s.BindInt64(1, |
| 668 delete_end.is_null() ? base::Time::Max().ToInternalValue() | 668 delete_end.is_null() ? base::Time::Max().ToInternalValue() |
| 669 : delete_end.ToInternalValue()); | 669 : delete_end.ToInternalValue()); |
| 670 | 670 |
| 671 return s.Run(); | 671 return s.Run(); |
| 672 } | 672 } |
| 673 | 673 |
| 674 // static |
| 674 LoginDatabase::EncryptionResult LoginDatabase::InitPasswordFormFromStatement( | 675 LoginDatabase::EncryptionResult LoginDatabase::InitPasswordFormFromStatement( |
| 675 PasswordForm* form, | 676 PasswordForm* form, |
| 676 sql::Statement& s) const { | 677 sql::Statement& s) { |
| 677 std::string encrypted_password; | 678 std::string encrypted_password; |
| 678 s.ColumnBlobAsString(COLUMN_PASSWORD_VALUE, &encrypted_password); | 679 s.ColumnBlobAsString(COLUMN_PASSWORD_VALUE, &encrypted_password); |
| 679 base::string16 decrypted_password; | 680 base::string16 decrypted_password; |
| 680 EncryptionResult encryption_result = | 681 EncryptionResult encryption_result = |
| 681 DecryptedString(encrypted_password, &decrypted_password); | 682 DecryptedString(encrypted_password, &decrypted_password); |
| 682 if (encryption_result != ENCRYPTION_RESULT_SUCCESS) | 683 if (encryption_result != ENCRYPTION_RESULT_SUCCESS) |
| 683 return encryption_result; | 684 return encryption_result; |
| 684 | 685 |
| 685 std::string tmp = s.ColumnString(COLUMN_ORIGIN_URL); | 686 std::string tmp = s.ColumnString(COLUMN_ORIGIN_URL); |
| 686 form->origin = GURL(tmp); | 687 form->origin = GURL(tmp); |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 731 form->generation_upload_status = | 732 form->generation_upload_status = |
| 732 static_cast<PasswordForm::GenerationUploadStatus>( | 733 static_cast<PasswordForm::GenerationUploadStatus>( |
| 733 generation_upload_status_int); | 734 generation_upload_status_int); |
| 734 return ENCRYPTION_RESULT_SUCCESS; | 735 return ENCRYPTION_RESULT_SUCCESS; |
| 735 } | 736 } |
| 736 | 737 |
| 737 bool LoginDatabase::GetLogins( | 738 bool LoginDatabase::GetLogins( |
| 738 const PasswordForm& form, | 739 const PasswordForm& form, |
| 739 ScopedVector<autofill::PasswordForm>* forms) const { | 740 ScopedVector<autofill::PasswordForm>* forms) const { |
| 740 DCHECK(forms); | 741 DCHECK(forms); |
| 742 forms->clear(); |
| 741 // You *must* change LoginTableColumns if this query changes. | 743 // You *must* change LoginTableColumns if this query changes. |
| 742 const std::string sql_query = | 744 const std::string sql_query = |
| 743 "SELECT origin_url, action_url, " | 745 "SELECT origin_url, action_url, " |
| 744 "username_element, username_value, " | 746 "username_element, username_value, " |
| 745 "password_element, password_value, submit_element, " | 747 "password_element, password_value, submit_element, " |
| 746 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | 748 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " |
| 747 "scheme, password_type, possible_usernames, times_used, form_data, " | 749 "scheme, password_type, possible_usernames, times_used, form_data, " |
| 748 "date_synced, display_name, avatar_url, " | 750 "date_synced, display_name, avatar_url, " |
| 749 "federation_url, skip_zero_click, generation_upload_status " | 751 "federation_url, skip_zero_click, generation_upload_status " |
| 750 "FROM logins WHERE signon_realm == ? "; | 752 "FROM logins WHERE signon_realm == ? "; |
| 751 sql::Statement s; | 753 sql::Statement s; |
| 752 const GURL signon_realm(form.signon_realm); | 754 const GURL signon_realm(form.signon_realm); |
| 753 std::string registered_domain = GetRegistryControlledDomain(signon_realm); | 755 std::string registered_domain = GetRegistryControlledDomain(signon_realm); |
| 754 PSLDomainMatchMetric psl_domain_match_metric = PSL_DOMAIN_MATCH_NONE; | |
| 755 const bool should_PSL_matching_apply = | 756 const bool should_PSL_matching_apply = |
| 756 form.scheme == PasswordForm::SCHEME_HTML && | 757 form.scheme == PasswordForm::SCHEME_HTML && |
| 757 ShouldPSLDomainMatchingApply(registered_domain); | 758 ShouldPSLDomainMatchingApply(registered_domain); |
| 758 // PSL matching only applies to HTML forms. | 759 // PSL matching only applies to HTML forms. |
| 759 if (should_PSL_matching_apply) { | 760 if (should_PSL_matching_apply) { |
| 760 // We are extending the original SQL query with one that includes more | 761 // We are extending the original SQL query with one that includes more |
| 761 // possible matches based on public suffix domain matching. Using a regexp | 762 // possible matches based on public suffix domain matching. Using a regexp |
| 762 // here is just an optimization to not have to parse all the stored entries | 763 // here is just an optimization to not have to parse all the stored entries |
| 763 // in the |logins| table. The result (scheme, domain and port) is verified | 764 // in the |logins| table. The result (scheme, domain and port) is verified |
| 764 // further down using GURL. See the functions SchemeMatches, | 765 // further down using GURL. See the functions SchemeMatches, |
| (...skipping 14 matching lines...) Expand all Loading... |
| 779 const std::string port = signon_realm.port(); | 780 const std::string port = signon_realm.port(); |
| 780 // For a signon realm such as http://foo.bar/, this regexp will match | 781 // For a signon realm such as http://foo.bar/, this regexp will match |
| 781 // domains on the form http://foo.bar/, http://www.foo.bar/, | 782 // domains on the form http://foo.bar/, http://www.foo.bar/, |
| 782 // http://www.mobile.foo.bar/. It will not match http://notfoo.bar/. | 783 // http://www.mobile.foo.bar/. It will not match http://notfoo.bar/. |
| 783 // The scheme and port has to be the same as the observed form. | 784 // The scheme and port has to be the same as the observed form. |
| 784 std::string regexp = "^(" + scheme + ":\\/\\/)([\\w-]+\\.)*" + | 785 std::string regexp = "^(" + scheme + ":\\/\\/)([\\w-]+\\.)*" + |
| 785 registered_domain + "(:" + port + ")?\\/$"; | 786 registered_domain + "(:" + port + ")?\\/$"; |
| 786 s.BindString(0, form.signon_realm); | 787 s.BindString(0, form.signon_realm); |
| 787 s.BindString(1, regexp); | 788 s.BindString(1, regexp); |
| 788 } else { | 789 } else { |
| 789 psl_domain_match_metric = PSL_DOMAIN_MATCH_NOT_USED; | 790 UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering", |
| 791 PSL_DOMAIN_MATCH_NOT_USED, |
| 792 PSL_DOMAIN_MATCH_COUNT); |
| 790 s.Assign(db_.GetCachedStatement(SQL_FROM_HERE, sql_query.c_str())); | 793 s.Assign(db_.GetCachedStatement(SQL_FROM_HERE, sql_query.c_str())); |
| 791 s.BindString(0, form.signon_realm); | 794 s.BindString(0, form.signon_realm); |
| 792 } | 795 } |
| 793 | 796 |
| 794 while (s.Step()) { | 797 return StatementToForms(&s, should_PSL_matching_apply ? &form : nullptr, |
| 795 scoped_ptr<PasswordForm> new_form(new PasswordForm()); | 798 forms); |
| 796 EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s); | |
| 797 if (result == ENCRYPTION_RESULT_SERVICE_FAILURE) | |
| 798 return false; | |
| 799 if (result == ENCRYPTION_RESULT_ITEM_FAILURE) | |
| 800 continue; | |
| 801 DCHECK(result == ENCRYPTION_RESULT_SUCCESS); | |
| 802 if (should_PSL_matching_apply) { | |
| 803 if (!IsPublicSuffixDomainMatch(new_form->signon_realm, | |
| 804 form.signon_realm)) { | |
| 805 // The database returned results that should not match. Skipping result. | |
| 806 continue; | |
| 807 } | |
| 808 if (form.signon_realm != new_form->signon_realm) { | |
| 809 // Ignore non-HTML matches. | |
| 810 if (new_form->scheme != PasswordForm::SCHEME_HTML) | |
| 811 continue; | |
| 812 | |
| 813 psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND; | |
| 814 // This is not a perfect match, so we need to create a new valid result. | |
| 815 // We do this by copying over origin, signon realm and action from the | |
| 816 // observed form and setting the original signon realm to what we found | |
| 817 // in the database. We use the fact that |original_signon_realm| is | |
| 818 // non-empty to communicate that this match was found using public | |
| 819 // suffix matching. | |
| 820 new_form->original_signon_realm = new_form->signon_realm; | |
| 821 new_form->origin = form.origin; | |
| 822 new_form->signon_realm = form.signon_realm; | |
| 823 new_form->action = form.action; | |
| 824 } | |
| 825 } | |
| 826 forms->push_back(new_form.release()); | |
| 827 } | |
| 828 UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering", | |
| 829 psl_domain_match_metric, | |
| 830 PSL_DOMAIN_MATCH_COUNT); | |
| 831 return s.Succeeded(); | |
| 832 } | 799 } |
| 833 | 800 |
| 834 bool LoginDatabase::GetLoginsCreatedBetween( | 801 bool LoginDatabase::GetLoginsCreatedBetween( |
| 835 const base::Time begin, | 802 const base::Time begin, |
| 836 const base::Time end, | 803 const base::Time end, |
| 837 ScopedVector<autofill::PasswordForm>* forms) const { | 804 ScopedVector<autofill::PasswordForm>* forms) const { |
| 838 DCHECK(forms); | 805 DCHECK(forms); |
| 806 forms->clear(); |
| 839 sql::Statement s(db_.GetCachedStatement( | 807 sql::Statement s(db_.GetCachedStatement( |
| 840 SQL_FROM_HERE, | 808 SQL_FROM_HERE, |
| 841 "SELECT origin_url, action_url, " | 809 "SELECT origin_url, action_url, " |
| 842 "username_element, username_value, " | 810 "username_element, username_value, " |
| 843 "password_element, password_value, submit_element, " | 811 "password_element, password_value, submit_element, " |
| 844 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | 812 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " |
| 845 "scheme, password_type, possible_usernames, times_used, form_data, " | 813 "scheme, password_type, possible_usernames, times_used, form_data, " |
| 846 "date_synced, display_name, avatar_url, " | 814 "date_synced, display_name, avatar_url, " |
| 847 "federation_url, skip_zero_click, generation_upload_status FROM logins " | 815 "federation_url, skip_zero_click, generation_upload_status FROM logins " |
| 848 "WHERE date_created >= ? AND date_created < ?" | 816 "WHERE date_created >= ? AND date_created < ?" |
| 849 "ORDER BY origin_url")); | 817 "ORDER BY origin_url")); |
| 850 s.BindInt64(0, begin.ToInternalValue()); | 818 s.BindInt64(0, begin.ToInternalValue()); |
| 851 s.BindInt64(1, end.is_null() ? std::numeric_limits<int64>::max() | 819 s.BindInt64(1, end.is_null() ? std::numeric_limits<int64>::max() |
| 852 : end.ToInternalValue()); | 820 : end.ToInternalValue()); |
| 853 | 821 |
| 854 while (s.Step()) { | 822 return StatementToForms(&s, nullptr, forms); |
| 855 scoped_ptr<PasswordForm> new_form(new PasswordForm()); | |
| 856 EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s); | |
| 857 if (result == ENCRYPTION_RESULT_SERVICE_FAILURE) | |
| 858 return false; | |
| 859 if (result == ENCRYPTION_RESULT_ITEM_FAILURE) | |
| 860 continue; | |
| 861 DCHECK(result == ENCRYPTION_RESULT_SUCCESS); | |
| 862 forms->push_back(new_form.release()); | |
| 863 } | |
| 864 return s.Succeeded(); | |
| 865 } | 823 } |
| 866 | 824 |
| 867 bool LoginDatabase::GetLoginsSyncedBetween( | 825 bool LoginDatabase::GetLoginsSyncedBetween( |
| 868 const base::Time begin, | 826 const base::Time begin, |
| 869 const base::Time end, | 827 const base::Time end, |
| 870 ScopedVector<autofill::PasswordForm>* forms) const { | 828 ScopedVector<autofill::PasswordForm>* forms) const { |
| 871 DCHECK(forms); | 829 DCHECK(forms); |
| 830 forms->clear(); |
| 872 sql::Statement s(db_.GetCachedStatement( | 831 sql::Statement s(db_.GetCachedStatement( |
| 873 SQL_FROM_HERE, | 832 SQL_FROM_HERE, |
| 874 "SELECT origin_url, action_url, username_element, username_value, " | 833 "SELECT origin_url, action_url, username_element, username_value, " |
| 875 "password_element, password_value, submit_element, signon_realm, " | 834 "password_element, password_value, submit_element, signon_realm, " |
| 876 "ssl_valid, preferred, date_created, blacklisted_by_user, " | 835 "ssl_valid, preferred, date_created, blacklisted_by_user, " |
| 877 "scheme, password_type, possible_usernames, times_used, form_data, " | 836 "scheme, password_type, possible_usernames, times_used, form_data, " |
| 878 "date_synced, display_name, avatar_url, " | 837 "date_synced, display_name, avatar_url, " |
| 879 "federation_url, skip_zero_click, generation_upload_status FROM logins " | 838 "federation_url, skip_zero_click, generation_upload_status FROM logins " |
| 880 "WHERE date_synced >= ? AND date_synced < ?" | 839 "WHERE date_synced >= ? AND date_synced < ?" |
| 881 "ORDER BY origin_url")); | 840 "ORDER BY origin_url")); |
| 882 s.BindInt64(0, begin.ToInternalValue()); | 841 s.BindInt64(0, begin.ToInternalValue()); |
| 883 s.BindInt64(1, | 842 s.BindInt64(1, |
| 884 end.is_null() ? base::Time::Max().ToInternalValue() | 843 end.is_null() ? base::Time::Max().ToInternalValue() |
| 885 : end.ToInternalValue()); | 844 : end.ToInternalValue()); |
| 886 | 845 |
| 887 while (s.Step()) { | 846 return StatementToForms(&s, nullptr, forms); |
| 888 scoped_ptr<PasswordForm> new_form(new PasswordForm()); | |
| 889 EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s); | |
| 890 if (result == ENCRYPTION_RESULT_SERVICE_FAILURE) | |
| 891 return false; | |
| 892 if (result == ENCRYPTION_RESULT_ITEM_FAILURE) | |
| 893 continue; | |
| 894 DCHECK(result == ENCRYPTION_RESULT_SUCCESS); | |
| 895 forms->push_back(new_form.release()); | |
| 896 } | |
| 897 return s.Succeeded(); | |
| 898 } | 847 } |
| 899 | 848 |
| 900 bool LoginDatabase::GetAutofillableLogins( | 849 bool LoginDatabase::GetAutofillableLogins( |
| 901 ScopedVector<autofill::PasswordForm>* forms) const { | 850 ScopedVector<autofill::PasswordForm>* forms) const { |
| 902 return GetAllLoginsWithBlacklistSetting(false, forms); | 851 return GetAllLoginsWithBlacklistSetting(false, forms); |
| 903 } | 852 } |
| 904 | 853 |
| 905 bool LoginDatabase::GetBlacklistLogins( | 854 bool LoginDatabase::GetBlacklistLogins( |
| 906 ScopedVector<autofill::PasswordForm>* forms) const { | 855 ScopedVector<autofill::PasswordForm>* forms) const { |
| 907 return GetAllLoginsWithBlacklistSetting(true, forms); | 856 return GetAllLoginsWithBlacklistSetting(true, forms); |
| 908 } | 857 } |
| 909 | 858 |
| 910 bool LoginDatabase::GetAllLoginsWithBlacklistSetting( | 859 bool LoginDatabase::GetAllLoginsWithBlacklistSetting( |
| 911 bool blacklisted, | 860 bool blacklisted, |
| 912 ScopedVector<autofill::PasswordForm>* forms) const { | 861 ScopedVector<autofill::PasswordForm>* forms) const { |
| 913 DCHECK(forms); | 862 DCHECK(forms); |
| 863 forms->clear(); |
| 914 // You *must* change LoginTableColumns if this query changes. | 864 // You *must* change LoginTableColumns if this query changes. |
| 915 sql::Statement s(db_.GetCachedStatement( | 865 sql::Statement s(db_.GetCachedStatement( |
| 916 SQL_FROM_HERE, | 866 SQL_FROM_HERE, |
| 917 "SELECT origin_url, action_url, " | 867 "SELECT origin_url, action_url, " |
| 918 "username_element, username_value, " | 868 "username_element, username_value, " |
| 919 "password_element, password_value, submit_element, " | 869 "password_element, password_value, submit_element, " |
| 920 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " | 870 "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " |
| 921 "scheme, password_type, possible_usernames, times_used, form_data, " | 871 "scheme, password_type, possible_usernames, times_used, form_data, " |
| 922 "date_synced, display_name, avatar_url, " | 872 "date_synced, display_name, avatar_url, " |
| 923 "federation_url, skip_zero_click, generation_upload_status FROM logins " | 873 "federation_url, skip_zero_click, generation_upload_status FROM logins " |
| 924 "WHERE blacklisted_by_user == ? ORDER BY origin_url")); | 874 "WHERE blacklisted_by_user == ? ORDER BY origin_url")); |
| 925 s.BindInt(0, blacklisted ? 1 : 0); | 875 s.BindInt(0, blacklisted ? 1 : 0); |
| 926 | 876 |
| 927 while (s.Step()) { | 877 return StatementToForms(&s, nullptr, forms); |
| 928 scoped_ptr<PasswordForm> new_form(new PasswordForm()); | |
| 929 EncryptionResult result = InitPasswordFormFromStatement(new_form.get(), s); | |
| 930 if (result == ENCRYPTION_RESULT_SERVICE_FAILURE) | |
| 931 return false; | |
| 932 if (result == ENCRYPTION_RESULT_ITEM_FAILURE) | |
| 933 continue; | |
| 934 DCHECK(result == ENCRYPTION_RESULT_SUCCESS); | |
| 935 forms->push_back(new_form.release()); | |
| 936 } | |
| 937 return s.Succeeded(); | |
| 938 } | 878 } |
| 939 | 879 |
| 940 bool LoginDatabase::DeleteAndRecreateDatabaseFile() { | 880 bool LoginDatabase::DeleteAndRecreateDatabaseFile() { |
| 941 DCHECK(db_.is_open()); | 881 DCHECK(db_.is_open()); |
| 942 meta_table_.Reset(); | 882 meta_table_.Reset(); |
| 943 db_.Close(); | 883 db_.Close(); |
| 944 sql::Connection::Delete(db_path_); | 884 sql::Connection::Delete(db_path_); |
| 945 return Init(); | 885 return Init(); |
| 946 } | 886 } |
| 947 | 887 |
| 888 // static |
| 889 bool LoginDatabase::StatementToForms( |
| 890 sql::Statement* statement, |
| 891 const autofill::PasswordForm* psl_match, |
| 892 ScopedVector<autofill::PasswordForm>* forms) { |
| 893 PSLDomainMatchMetric psl_domain_match_metric = PSL_DOMAIN_MATCH_NONE; |
| 894 forms->clear(); |
| 895 |
| 896 // Swap |collected_forms| into |*forms| first on success, to avoid returning |
| 897 // partial results on error. |
| 898 ScopedVector<autofill::PasswordForm> collected_forms; |
| 899 |
| 900 while (statement->Step()) { |
| 901 scoped_ptr<PasswordForm> new_form(new PasswordForm()); |
| 902 EncryptionResult result = |
| 903 InitPasswordFormFromStatement(new_form.get(), *statement); |
| 904 if (result == ENCRYPTION_RESULT_SERVICE_FAILURE) |
| 905 return false; |
| 906 if (result == ENCRYPTION_RESULT_ITEM_FAILURE) |
| 907 continue; |
| 908 DCHECK(result == ENCRYPTION_RESULT_SUCCESS); |
| 909 if (psl_match && psl_match->signon_realm != new_form->signon_realm) { |
| 910 if (new_form->scheme != PasswordForm::SCHEME_HTML) |
| 911 continue; // Ignore non-HTML matches. |
| 912 |
| 913 if (!IsPublicSuffixDomainMatch(new_form->signon_realm, |
| 914 psl_match->signon_realm)) { |
| 915 continue; |
| 916 } |
| 917 |
| 918 psl_domain_match_metric = PSL_DOMAIN_MATCH_FOUND; |
| 919 // This is not a perfect match, so we need to create a new valid result. |
| 920 // We do this by copying over origin, signon realm and action from the |
| 921 // observed form and setting the original signon realm to what we found |
| 922 // in the database. We use the fact that |original_signon_realm| is |
| 923 // non-empty to communicate that this match was found using public |
| 924 // suffix matching. |
| 925 new_form->original_signon_realm = new_form->signon_realm; |
| 926 new_form->origin = psl_match->origin; |
| 927 new_form->signon_realm = psl_match->signon_realm; |
| 928 new_form->action = psl_match->action; |
| 929 } |
| 930 collected_forms.push_back(new_form.Pass()); |
| 931 } |
| 932 |
| 933 if (psl_match) { |
| 934 UMA_HISTOGRAM_ENUMERATION("PasswordManager.PslDomainMatchTriggering", |
| 935 psl_domain_match_metric, PSL_DOMAIN_MATCH_COUNT); |
| 936 } |
| 937 |
| 938 if (!statement->Succeeded()) |
| 939 return false; |
| 940 forms->swap(collected_forms); |
| 941 return true; |
| 942 } |
| 943 |
| 948 } // namespace password_manager | 944 } // namespace password_manager |
| OLD | NEW |