Chromium Code Reviews| 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 IsDBOnlyForm(const autofill::PasswordForm& form) { | |
|
engedy
2015/10/08 17:31:00
nit: IsLoginDatabaseOnlyForm
vasilii
2015/10/12 15:12:48
Done.
| |
| 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 // We never merge blacklist entries between our store and the Keychain, |
|
vabr (Chromium)
2015/10/09 07:53:09
nit: This comment is slightly out of date. And giv
vasilii
2015/10/12 15:12:48
Done.
| |
| 474 // and federated logins should not be stored in the Keychain at all. | 480 // and federated logins should not be stored in the Keychain at all. |
| 475 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user || | 481 if (IsDBOnlyForm(form_a) || IsDBOnlyForm(form_b)) |
| 476 !form_a.federation_url.is_empty() || !form_b.federation_url.is_empty()) { | |
| 477 return false; | 482 return false; |
| 478 } | 483 |
| 479 bool equal_realm = form_a.signon_realm == form_b.signon_realm; | 484 bool equal_realm = form_a.signon_realm == form_b.signon_realm; |
| 480 if (strictness == FUZZY_FORM_MATCH) { | 485 if (strictness == FUZZY_FORM_MATCH) { |
| 481 equal_realm |= (!form_a.original_signon_realm.empty()) && | 486 equal_realm |= (!form_a.original_signon_realm.empty()) && |
| 482 form_a.original_signon_realm == form_b.signon_realm; | 487 form_a.original_signon_realm == form_b.signon_realm; |
| 483 } | 488 } |
| 484 return form_a.scheme == form_b.scheme && equal_realm && | 489 return form_a.scheme == form_b.scheme && equal_realm && |
| 485 form_a.username_value == form_b.username_value; | 490 form_a.username_value == form_b.username_value; |
| 486 } | 491 } |
| 487 | 492 |
| 488 // Moves entries from |forms| that represent either blacklisted or federated | 493 // Moves entries from |forms| that represent either blacklisted or federated |
| 489 // logins into |extracted|. These two types are stored only in the LoginDatabase | 494 // logins into |extracted|. These two types are stored only in the LoginDatabase |
| 490 // and do not have corresponding Keychain entries. | 495 // and do not have corresponding Keychain entries. |
| 491 void ExtractNonKeychainForms(ScopedVector<autofill::PasswordForm>* forms, | 496 void ExtractNonKeychainForms(ScopedVector<autofill::PasswordForm>* forms, |
| 492 ScopedVector<autofill::PasswordForm>* extracted) { | 497 ScopedVector<autofill::PasswordForm>* extracted) { |
| 493 extracted->reserve(extracted->size() + forms->size()); | 498 extracted->reserve(extracted->size() + forms->size()); |
| 494 ScopedVector<autofill::PasswordForm> remaining; | 499 ScopedVector<autofill::PasswordForm> remaining; |
| 495 MoveAllFormsOut( | 500 MoveAllFormsOut( |
| 496 forms, [&remaining, extracted](scoped_ptr<autofill::PasswordForm> form) { | 501 forms, [&remaining, extracted](scoped_ptr<autofill::PasswordForm> form) { |
| 497 if (form->blacklisted_by_user || !form->federation_url.is_empty()) | 502 if (IsDBOnlyForm(*form)) |
| 498 extracted->push_back(form.Pass()); | 503 extracted->push_back(form.Pass()); |
| 499 else | 504 else |
| 500 remaining.push_back(form.Pass()); | 505 remaining.push_back(form.Pass()); |
| 501 }); | 506 }); |
| 502 forms->swap(remaining); | 507 forms->swap(remaining); |
| 503 } | 508 } |
| 504 | 509 |
| 505 // Takes |keychain_forms| and |database_forms| and moves the following 2 types | 510 // Takes |keychain_forms| and |database_forms| and moves the following 2 types |
| 506 // of forms to |merged_forms|: | 511 // of forms to |merged_forms|: |
| 507 // (1) |database_forms| that by principle never have a corresponding Keychain | 512 // (1) |database_forms| that by principle never have a corresponding Keychain |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 715 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form); | 720 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form); |
| 716 if (keychain_item) { | 721 if (keychain_item) { |
| 717 keychain_->Free(keychain_item); | 722 keychain_->Free(keychain_item); |
| 718 return true; | 723 return true; |
| 719 } | 724 } |
| 720 return false; | 725 return false; |
| 721 } | 726 } |
| 722 | 727 |
| 723 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm( | 728 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm( |
| 724 const PasswordForm& query_form) { | 729 const PasswordForm& query_form) { |
| 725 if (!query_form.federation_url.is_empty()) | 730 if (IsDBOnlyForm(query_form)) |
| 726 return false; | 731 return false; |
| 727 std::string username = base::UTF16ToUTF8(query_form.username_value); | 732 std::string username = base::UTF16ToUTF8(query_form.username_value); |
| 728 std::vector<SecKeychainItemRef> matches = | 733 std::vector<SecKeychainItemRef> matches = |
| 729 MatchingKeychainItems(query_form.signon_realm, query_form.scheme, | 734 MatchingKeychainItems(query_form.signon_realm, query_form.scheme, |
| 730 NULL, username.c_str()); | 735 NULL, username.c_str()); |
| 731 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin(); | 736 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin(); |
| 732 i != matches.end(); ++i) { | 737 i != matches.end(); ++i) { |
| 733 keychain_->Free(*i); | 738 keychain_->Free(*i); |
| 734 } | 739 } |
| 735 | 740 |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 762 } | 767 } |
| 763 | 768 |
| 764 ScopedVector<autofill::PasswordForm> | 769 ScopedVector<autofill::PasswordForm> |
| 765 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() { | 770 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() { |
| 766 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems(); | 771 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems(); |
| 767 return ConvertKeychainItemsToForms(&items); | 772 return ConvertKeychainItemsToForms(&items); |
| 768 } | 773 } |
| 769 | 774 |
| 770 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) { | 775 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) { |
| 771 // We should never be trying to store a blacklist in the keychain. | 776 // We should never be trying to store a blacklist in the keychain. |
| 772 DCHECK(!form.blacklisted_by_user); | 777 DCHECK(!IsDBOnlyForm(form)); |
| 773 | 778 |
| 774 std::string server; | 779 std::string server; |
| 775 std::string security_domain; | 780 std::string security_domain; |
| 776 UInt32 port; | 781 UInt32 port; |
| 777 bool is_secure; | 782 bool is_secure; |
| 778 if (!internal_keychain_helpers::ExtractSignonRealmComponents( | 783 if (!internal_keychain_helpers::ExtractSignonRealmComponents( |
| 779 form.signon_realm, &server, &port, &is_secure, &security_domain)) { | 784 form.signon_realm, &server, &port, &is_secure, &security_domain)) { |
| 780 return false; | 785 return false; |
| 781 } | 786 } |
| 782 std::string path; | 787 std::string path; |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 842 } | 847 } |
| 843 items->clear(); | 848 items->clear(); |
| 844 return forms.Pass(); | 849 return forms.Pass(); |
| 845 } | 850 } |
| 846 | 851 |
| 847 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm( | 852 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm( |
| 848 const PasswordForm& form) { | 853 const PasswordForm& form) { |
| 849 // We don't store blacklist entries in the keychain, so the answer to "what | 854 // We don't store blacklist entries in the keychain, so the answer to "what |
| 850 // Keychain item goes with this form" is always "nothing" for blacklists. | 855 // Keychain item goes with this form" is always "nothing" for blacklists. |
| 851 // Same goes for federated logins. | 856 // Same goes for federated logins. |
| 852 if (form.blacklisted_by_user || !form.federation_url.is_empty()) { | 857 if (IsDBOnlyForm(form)) |
| 853 return NULL; | 858 return NULL; |
| 854 } | |
| 855 | 859 |
| 856 std::string path; | 860 std::string path; |
| 857 // Path doesn't make sense for Android app credentials. | 861 // Path doesn't make sense for Android app credentials. |
| 858 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm)) | 862 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm)) |
| 859 path = form.origin.path(); | 863 path = form.origin.path(); |
| 860 std::string username = base::UTF16ToUTF8(form.username_value); | 864 std::string username = base::UTF16ToUTF8(form.username_value); |
| 861 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems( | 865 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems( |
| 862 form.signon_realm, form.scheme, path.c_str(), username.c_str()); | 866 form.signon_realm, form.scheme, path.c_str(), username.c_str()); |
| 863 | 867 |
| 864 if (matches.empty()) { | 868 if (matches.empty()) { |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 910 } | 914 } |
| 911 | 915 |
| 912 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|. | 916 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|. |
| 913 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme( | 917 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme( |
| 914 PasswordForm::Scheme scheme) { | 918 PasswordForm::Scheme scheme) { |
| 915 switch (scheme) { | 919 switch (scheme) { |
| 916 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm; | 920 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm; |
| 917 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic; | 921 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic; |
| 918 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest; | 922 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest; |
| 919 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault; | 923 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault; |
| 924 case PasswordForm::SCHEME_USERNAME_ONLY: | |
| 925 NOTREACHED(); | |
| 926 break; | |
| 920 } | 927 } |
| 921 NOTREACHED(); | 928 NOTREACHED(); |
| 922 return kSecAuthenticationTypeDefault; | 929 return kSecAuthenticationTypeDefault; |
| 923 } | 930 } |
| 924 | 931 |
| 925 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword( | 932 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword( |
| 926 const SecKeychainItemRef& keychain_item, const std::string& password) { | 933 const SecKeychainItemRef& keychain_item, const std::string& password) { |
| 927 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL, | 934 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL, |
| 928 password.size(), | 935 password.size(), |
| 929 password.c_str()); | 936 password.c_str()); |
| (...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1256 | 1263 |
| 1257 scoped_ptr<password_manager::InteractionsStats> | 1264 scoped_ptr<password_manager::InteractionsStats> |
| 1258 PasswordStoreMac::GetSiteStatsImpl(const GURL& origin_domain) { | 1265 PasswordStoreMac::GetSiteStatsImpl(const GURL& origin_domain) { |
| 1259 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | 1266 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
| 1260 return login_metadata_db_ | 1267 return login_metadata_db_ |
| 1261 ? login_metadata_db_->stats_table().GetRow(origin_domain) | 1268 ? login_metadata_db_->stats_table().GetRow(origin_domain) |
| 1262 : scoped_ptr<password_manager::InteractionsStats>(); | 1269 : scoped_ptr<password_manager::InteractionsStats>(); |
| 1263 } | 1270 } |
| 1264 | 1271 |
| 1265 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { | 1272 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) { |
| 1266 if (form.blacklisted_by_user || !form.federation_url.is_empty()) | 1273 if (IsDBOnlyForm(form)) |
| 1267 return true; | 1274 return true; |
| 1268 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get()); | 1275 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get()); |
| 1269 return keychainAdapter.AddPassword(form); | 1276 return keychainAdapter.AddPassword(form); |
| 1270 } | 1277 } |
| 1271 | 1278 |
| 1272 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm( | 1279 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm( |
| 1273 const autofill::PasswordForm& form) { | 1280 const autofill::PasswordForm& form) { |
| 1274 DCHECK(login_metadata_db_); | 1281 DCHECK(login_metadata_db_); |
| 1275 bool has_match = false; | 1282 bool has_match = false; |
| 1276 ScopedVector<autofill::PasswordForm> database_forms; | 1283 ScopedVector<autofill::PasswordForm> database_forms; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1325 ScopedVector<PasswordForm> forms_with_keychain_entry; | 1332 ScopedVector<PasswordForm> forms_with_keychain_entry; |
| 1326 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, | 1333 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, |
| 1327 &forms_with_keychain_entry); | 1334 &forms_with_keychain_entry); |
| 1328 | 1335 |
| 1329 // Clean up any orphaned database entries. | 1336 // Clean up any orphaned database entries. |
| 1330 RemoveDatabaseForms(&database_forms); | 1337 RemoveDatabaseForms(&database_forms); |
| 1331 | 1338 |
| 1332 // Move the orphaned DB forms to the output parameter. | 1339 // Move the orphaned DB forms to the output parameter. |
| 1333 AppendSecondToFirst(orphaned_forms, &database_forms); | 1340 AppendSecondToFirst(orphaned_forms, &database_forms); |
| 1334 } | 1341 } |
| OLD | NEW |