OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/browser/password_manager/password_store_mac.h" | 5 #include "chrome/browser/password_manager/password_store_mac.h" |
6 #include "chrome/browser/password_manager/password_store_mac_internal.h" | 6 #include "chrome/browser/password_manager/password_store_mac_internal.h" |
7 | 7 |
8 #include <CoreServices/CoreServices.h> | 8 #include <CoreServices/CoreServices.h> |
9 #include <set> | 9 #include <set> |
10 #include <string> | 10 #include <string> |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 mover(scoped_ptr<autofill::PasswordForm>(form_ptr)); | 231 mover(scoped_ptr<autofill::PasswordForm>(form_ptr)); |
232 } | 232 } |
233 // We moved the ownership of every form out of |forms|. For performance | 233 // We moved the ownership of every form out of |forms|. For performance |
234 // reasons, we can just weak_clear it, instead of nullptr-ing the respective | 234 // reasons, we can just weak_clear it, instead of nullptr-ing the respective |
235 // elements and letting the vector's destructor to go through the list once | 235 // elements and letting the vector's destructor to go through the list once |
236 // more. This was tested on a benchmark, and seemed to make a difference on | 236 // more. This was tested on a benchmark, and seemed to make a difference on |
237 // Mac. | 237 // Mac. |
238 forms->weak_clear(); | 238 forms->weak_clear(); |
239 } | 239 } |
240 | 240 |
241 // True if the form has no password to be stored in Keychain. | |
242 bool IsLoginDatabaseOnlyForm(const autofill::PasswordForm& form) { | |
243 return form.blacklisted_by_user || !form.federation_url.is_empty() || | |
244 form.scheme == autofill::PasswordForm::SCHEME_USERNAME_ONLY; | |
245 } | |
246 | |
241 } // namespace | 247 } // namespace |
242 | 248 |
243 #pragma mark - | 249 #pragma mark - |
244 | 250 |
245 // TODO(stuartmorgan): Convert most of this to private helpers in | 251 // TODO(stuartmorgan): Convert most of this to private helpers in |
246 // MacKeychainPasswordFormAdapter once it has sufficient higher-level public | 252 // MacKeychainPasswordFormAdapter once it has sufficient higher-level public |
247 // methods to provide test coverage. | 253 // methods to provide test coverage. |
248 namespace internal_keychain_helpers { | 254 namespace internal_keychain_helpers { |
249 | 255 |
250 // Returns a URL built from the given components. To create a URL without a | 256 // Returns a URL built from the given components. To create a URL without a |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
463 break; | 469 break; |
464 } | 470 } |
465 } | 471 } |
466 keychain.ItemFreeAttributesAndData(attr_list, nullptr); | 472 keychain.ItemFreeAttributesAndData(attr_list, nullptr); |
467 return creator_code && creator_code == base::mac::CreatorCodeForApplication(); | 473 return creator_code && creator_code == base::mac::CreatorCodeForApplication(); |
468 } | 474 } |
469 | 475 |
470 bool FormsMatchForMerge(const PasswordForm& form_a, | 476 bool FormsMatchForMerge(const PasswordForm& form_a, |
471 const PasswordForm& form_b, | 477 const PasswordForm& form_b, |
472 FormMatchStrictness strictness) { | 478 FormMatchStrictness strictness) { |
473 // We never merge blacklist entries between our store and the Keychain, | 479 if (IsLoginDatabaseOnlyForm(form_a) || IsLoginDatabaseOnlyForm(form_b)) |
474 // and federated logins should not be stored in the Keychain at all. | |
475 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user || | |
476 !form_a.federation_url.is_empty() || !form_b.federation_url.is_empty()) { | |
477 return false; | 480 return false; |
478 } | 481 |
479 bool equal_realm = form_a.signon_realm == form_b.signon_realm; | 482 bool equal_realm = form_a.signon_realm == form_b.signon_realm; |
480 if (strictness == FUZZY_FORM_MATCH) { | 483 if (strictness == FUZZY_FORM_MATCH) { |
481 equal_realm |= form_a.is_public_suffix_match; | 484 equal_realm |= form_a.is_public_suffix_match; |
482 } | 485 } |
483 return form_a.scheme == form_b.scheme && equal_realm && | 486 return form_a.scheme == form_b.scheme && equal_realm && |
484 form_a.username_value == form_b.username_value; | 487 form_a.username_value == form_b.username_value; |
485 } | 488 } |
486 | 489 |
487 // Moves entries from |forms| that represent either blacklisted or federated | 490 // Moves entries from |forms| that represent either blacklisted or federated |
488 // logins into |extracted|. These two types are stored only in the LoginDatabase | 491 // logins into |extracted|. These two types are stored only in the LoginDatabase |
489 // and do not have corresponding Keychain entries. | 492 // and do not have corresponding Keychain entries. |
490 void ExtractNonKeychainForms(ScopedVector<autofill::PasswordForm>* forms, | 493 void ExtractNonKeychainForms(ScopedVector<autofill::PasswordForm>* forms, |
491 ScopedVector<autofill::PasswordForm>* extracted) { | 494 ScopedVector<autofill::PasswordForm>* extracted) { |
492 extracted->reserve(extracted->size() + forms->size()); | 495 extracted->reserve(extracted->size() + forms->size()); |
493 ScopedVector<autofill::PasswordForm> remaining; | 496 ScopedVector<autofill::PasswordForm> remaining; |
494 MoveAllFormsOut( | 497 MoveAllFormsOut( |
495 forms, [&remaining, extracted](scoped_ptr<autofill::PasswordForm> form) { | 498 forms, [&remaining, extracted](scoped_ptr<autofill::PasswordForm> form) { |
496 if (form->blacklisted_by_user || !form->federation_url.is_empty()) | 499 if (IsLoginDatabaseOnlyForm(*form)) |
497 extracted->push_back(form.Pass()); | 500 extracted->push_back(form.Pass()); |
498 else | 501 else |
499 remaining.push_back(form.Pass()); | 502 remaining.push_back(form.Pass()); |
500 }); | 503 }); |
501 forms->swap(remaining); | 504 forms->swap(remaining); |
502 } | 505 } |
503 | 506 |
504 // Takes |keychain_forms| and |database_forms| and moves the following 2 types | 507 // Takes |keychain_forms| and |database_forms| and moves the following 2 types |
505 // of forms to |merged_forms|: | 508 // of forms to |merged_forms|: |
506 // (1) |database_forms| that by principle never have a corresponding Keychain | 509 // (1) |database_forms| that by principle never have a corresponding Keychain |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
714 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form); | 717 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form); |
715 if (keychain_item) { | 718 if (keychain_item) { |
716 keychain_->Free(keychain_item); | 719 keychain_->Free(keychain_item); |
717 return true; | 720 return true; |
718 } | 721 } |
719 return false; | 722 return false; |
720 } | 723 } |
721 | 724 |
722 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm( | 725 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm( |
723 const PasswordForm& query_form) { | 726 const PasswordForm& query_form) { |
724 if (!query_form.federation_url.is_empty()) | 727 if (IsLoginDatabaseOnlyForm(query_form)) |
725 return false; | 728 return false; |
726 std::string username = base::UTF16ToUTF8(query_form.username_value); | 729 std::string username = base::UTF16ToUTF8(query_form.username_value); |
727 std::vector<SecKeychainItemRef> matches = | 730 std::vector<SecKeychainItemRef> matches = |
728 MatchingKeychainItems(query_form.signon_realm, query_form.scheme, | 731 MatchingKeychainItems(query_form.signon_realm, query_form.scheme, |
729 NULL, username.c_str()); | 732 NULL, username.c_str()); |
730 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin(); | 733 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin(); |
731 i != matches.end(); ++i) { | 734 i != matches.end(); ++i) { |
732 keychain_->Free(*i); | 735 keychain_->Free(*i); |
733 } | 736 } |
734 | 737 |
(...skipping 26 matching lines...) Expand all Loading... | |
761 } | 764 } |
762 | 765 |
763 ScopedVector<autofill::PasswordForm> | 766 ScopedVector<autofill::PasswordForm> |
764 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() { | 767 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() { |
765 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems(); | 768 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems(); |
766 return ConvertKeychainItemsToForms(&items); | 769 return ConvertKeychainItemsToForms(&items); |
767 } | 770 } |
768 | 771 |
769 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) { | 772 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) { |
770 // We should never be trying to store a blacklist in the keychain. | 773 // We should never be trying to store a blacklist in the keychain. |
771 DCHECK(!form.blacklisted_by_user); | 774 DCHECK(!IsLoginDatabaseOnlyForm(form)); |
772 | 775 |
773 std::string server; | 776 std::string server; |
774 std::string security_domain; | 777 std::string security_domain; |
775 UInt32 port; | 778 UInt32 port; |
776 bool is_secure; | 779 bool is_secure; |
777 if (!internal_keychain_helpers::ExtractSignonRealmComponents( | 780 if (!internal_keychain_helpers::ExtractSignonRealmComponents( |
778 form.signon_realm, &server, &port, &is_secure, &security_domain)) { | 781 form.signon_realm, &server, &port, &is_secure, &security_domain)) { |
779 return false; | 782 return false; |
780 } | 783 } |
781 std::string path; | 784 std::string path; |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
841 } | 844 } |
842 items->clear(); | 845 items->clear(); |
843 return forms.Pass(); | 846 return forms.Pass(); |
844 } | 847 } |
845 | 848 |
846 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm( | 849 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm( |
847 const PasswordForm& form) { | 850 const PasswordForm& form) { |
848 // We don't store blacklist entries in the keychain, so the answer to "what | 851 // We don't store blacklist entries in the keychain, so the answer to "what |
849 // Keychain item goes with this form" is always "nothing" for blacklists. | 852 // Keychain item goes with this form" is always "nothing" for blacklists. |
850 // Same goes for federated logins. | 853 // Same goes for federated logins. |
851 if (form.blacklisted_by_user || !form.federation_url.is_empty()) { | 854 if (IsLoginDatabaseOnlyForm(form)) |
852 return NULL; | 855 return NULL; |
853 } | |
854 | 856 |
855 std::string path; | 857 std::string path; |
856 // Path doesn't make sense for Android app credentials. | 858 // Path doesn't make sense for Android app credentials. |
857 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm)) | 859 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm)) |
858 path = form.origin.path(); | 860 path = form.origin.path(); |
859 std::string username = base::UTF16ToUTF8(form.username_value); | 861 std::string username = base::UTF16ToUTF8(form.username_value); |
860 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems( | 862 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems( |
861 form.signon_realm, form.scheme, path.c_str(), username.c_str()); | 863 form.signon_realm, form.scheme, path.c_str(), username.c_str()); |
862 | 864 |
863 if (matches.empty()) { | 865 if (matches.empty()) { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
909 } | 911 } |
910 | 912 |
911 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|. | 913 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|. |
912 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme( | 914 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme( |
913 PasswordForm::Scheme scheme) { | 915 PasswordForm::Scheme scheme) { |
914 switch (scheme) { | 916 switch (scheme) { |
915 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm; | 917 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm; |
916 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic; | 918 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic; |
917 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest; | 919 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest; |
918 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault; | 920 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault; |
921 case PasswordForm::SCHEME_USERNAME_ONLY: | |
922 NOTREACHED(); | |
923 break; | |
919 } | 924 } |
920 NOTREACHED(); | 925 NOTREACHED(); |
921 return kSecAuthenticationTypeDefault; | 926 return kSecAuthenticationTypeDefault; |
922 } | 927 } |
923 | 928 |
924 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword( | 929 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword( |
925 const SecKeychainItemRef& keychain_item, const std::string& password) { | 930 const SecKeychainItemRef& keychain_item, const std::string& password) { |
926 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL, | 931 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL, |
927 password.size(), | 932 password.size(), |
928 password.c_str()); | 933 password.c_str()); |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1175 } | 1180 } |
1176 | 1181 |
1177 // Let's gather all signon realms we want to match with keychain entries. | 1182 // Let's gather all signon realms we want to match with keychain entries. |
1178 std::set<std::string> realm_set; | 1183 std::set<std::string> realm_set; |
1179 realm_set.insert(form.signon_realm); | 1184 realm_set.insert(form.signon_realm); |
1180 for (const autofill::PasswordForm* db_form : database_forms) { | 1185 for (const autofill::PasswordForm* db_form : database_forms) { |
1181 // TODO(vabr): We should not be getting different schemes here. | 1186 // TODO(vabr): We should not be getting different schemes here. |
1182 // http://crbug.com/340112 | 1187 // http://crbug.com/340112 |
1183 if (form.scheme != db_form->scheme) | 1188 if (form.scheme != db_form->scheme) |
1184 continue; // Forms with different schemes never match. | 1189 continue; // Forms with different schemes never match. |
1185 if (db_form->is_public_suffix_match) | 1190 if (db_form->is_public_suffix_match) |
engedy
2015/10/13 12:11:33
We need to take |is_affiliation_based_match| into
vasilii
2015/10/13 18:05:26
That's unrelated to the current CL. Please follow
| |
1186 realm_set.insert(db_form->signon_realm); | 1191 realm_set.insert(db_form->signon_realm); |
1187 } | 1192 } |
1188 ScopedVector<autofill::PasswordForm> keychain_forms; | 1193 ScopedVector<autofill::PasswordForm> keychain_forms; |
1189 for (std::set<std::string>::const_iterator realm = realm_set.begin(); | 1194 for (std::set<std::string>::const_iterator realm = realm_set.begin(); |
1190 realm != realm_set.end(); ++realm) { | 1195 realm != realm_set.end(); ++realm) { |
1191 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); | 1196 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get()); |
1192 ScopedVector<autofill::PasswordForm> temp_keychain_forms = | 1197 ScopedVector<autofill::PasswordForm> temp_keychain_forms = |
1193 keychain_adapter.PasswordsFillingForm(*realm, form.scheme); | 1198 keychain_adapter.PasswordsFillingForm(*realm, form.scheme); |
1194 AppendSecondToFirst(&keychain_forms, &temp_keychain_forms); | 1199 AppendSecondToFirst(&keychain_forms, &temp_keychain_forms); |
1195 } | 1200 } |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1254 | 1259 |
1255 scoped_ptr<password_manager::InteractionsStats> | 1260 scoped_ptr<password_manager::InteractionsStats> |
1256 PasswordStoreMac::GetSiteStatsImpl(const GURL& origin_domain) { | 1261 PasswordStoreMac::GetSiteStatsImpl(const GURL& origin_domain) { |
1257 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | 1262 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
1258 return login_metadata_db_ | 1263 return login_metadata_db_ |
1259 ? login_metadata_db_->stats_table().GetRow(origin_domain) | 1264 ? login_metadata_db_->stats_table().GetRow(origin_domain) |
1260 : scoped_ptr<password_manager::InteractionsStats>(); | 1265 : scoped_ptr<password_manager::InteractionsStats>(); |
1261 } | 1266 } |
1262 | 1267 |
1263 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { | 1268 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { |
1264 if (form.blacklisted_by_user || !form.federation_url.is_empty()) | 1269 if (IsLoginDatabaseOnlyForm(form)) |
1265 return true; | 1270 return true; |
1266 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get()); | 1271 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get()); |
1267 return keychainAdapter.AddPassword(form); | 1272 return keychainAdapter.AddPassword(form); |
1268 } | 1273 } |
1269 | 1274 |
1270 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm( | 1275 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm( |
1271 const autofill::PasswordForm& form) { | 1276 const autofill::PasswordForm& form) { |
1272 DCHECK(login_metadata_db_); | 1277 DCHECK(login_metadata_db_); |
1273 bool has_match = false; | 1278 bool has_match = false; |
1274 ScopedVector<autofill::PasswordForm> database_forms; | 1279 ScopedVector<autofill::PasswordForm> database_forms; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1323 ScopedVector<PasswordForm> forms_with_keychain_entry; | 1328 ScopedVector<PasswordForm> forms_with_keychain_entry; |
1324 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, | 1329 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, |
1325 &forms_with_keychain_entry); | 1330 &forms_with_keychain_entry); |
1326 | 1331 |
1327 // Clean up any orphaned database entries. | 1332 // Clean up any orphaned database entries. |
1328 RemoveDatabaseForms(&database_forms); | 1333 RemoveDatabaseForms(&database_forms); |
1329 | 1334 |
1330 // Move the orphaned DB forms to the output parameter. | 1335 // Move the orphaned DB forms to the output parameter. |
1331 AppendSecondToFirst(orphaned_forms, &database_forms); | 1336 AppendSecondToFirst(orphaned_forms, &database_forms); |
1332 } | 1337 } |
OLD | NEW |