| 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 325 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 |