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 |