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