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