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> |
11 #include <utility> | 11 #include <utility> |
12 #include <vector> | 12 #include <vector> |
13 | 13 |
14 #include "base/callback.h" | 14 #include "base/callback.h" |
15 #include "base/logging.h" | 15 #include "base/logging.h" |
16 #include "base/mac/foundation_util.h" | 16 #include "base/mac/foundation_util.h" |
17 #include "base/mac/mac_logging.h" | 17 #include "base/mac/mac_logging.h" |
18 #include "base/message_loop/message_loop.h" | 18 #include "base/message_loop/message_loop.h" |
| 19 #include "base/metrics/histogram_macros.h" |
19 #include "base/stl_util.h" | 20 #include "base/stl_util.h" |
20 #include "base/strings/string_util.h" | 21 #include "base/strings/string_util.h" |
21 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
22 #include "chrome/browser/mac/security_wrappers.h" | 23 #include "chrome/browser/mac/security_wrappers.h" |
| 24 #include "components/os_crypt/os_crypt.h" |
23 #include "components/password_manager/core/browser/affiliation_utils.h" | 25 #include "components/password_manager/core/browser/affiliation_utils.h" |
24 #include "components/password_manager/core/browser/login_database.h" | 26 #include "components/password_manager/core/browser/login_database.h" |
25 #include "components/password_manager/core/browser/password_store_change.h" | 27 #include "components/password_manager/core/browser/password_store_change.h" |
26 #include "content/public/browser/browser_thread.h" | 28 #include "content/public/browser/browser_thread.h" |
27 #include "crypto/apple_keychain.h" | 29 #include "crypto/apple_keychain.h" |
28 | 30 |
29 using autofill::PasswordForm; | 31 using autofill::PasswordForm; |
30 using crypto::AppleKeychain; | 32 using crypto::AppleKeychain; |
31 using password_manager::PasswordStoreChange; | 33 using password_manager::PasswordStoreChange; |
32 using password_manager::PasswordStoreChangeList; | 34 using password_manager::PasswordStoreChangeList; |
(...skipping 395 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
428 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm | 430 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm |
429 // format. | 431 // format. |
430 form->signon_realm = form->origin.GetOrigin().spec(); | 432 form->signon_realm = form->origin.GetOrigin().spec(); |
431 if (form->scheme != PasswordForm::SCHEME_HTML) { | 433 if (form->scheme != PasswordForm::SCHEME_HTML) { |
432 form->signon_realm.append(security_domain); | 434 form->signon_realm.append(security_domain); |
433 } | 435 } |
434 } | 436 } |
435 return true; | 437 return true; |
436 } | 438 } |
437 | 439 |
| 440 bool HasChromeCreatorCode(const AppleKeychain& keychain, |
| 441 const SecKeychainItemRef& keychain_item) { |
| 442 SecKeychainAttributeInfo attr_info; |
| 443 UInt32 tags[] = {kSecCreatorItemAttr}; |
| 444 attr_info.count = arraysize(tags); |
| 445 attr_info.tag = tags; |
| 446 attr_info.format = nullptr; |
| 447 |
| 448 SecKeychainAttributeList* attr_list; |
| 449 UInt32 password_length; |
| 450 OSStatus result = keychain.ItemCopyAttributesAndData( |
| 451 keychain_item, &attr_info, nullptr, &attr_list, |
| 452 &password_length, nullptr); |
| 453 if (result != noErr) |
| 454 return false; |
| 455 OSType creator_code = 0; |
| 456 for (unsigned int i = 0; i < attr_list->count; i++) { |
| 457 SecKeychainAttribute attr = attr_list->attr[i]; |
| 458 if (!attr.data) { |
| 459 continue; |
| 460 } |
| 461 if (attr.tag == kSecCreatorItemAttr) { |
| 462 creator_code = *(static_cast<FourCharCode*>(attr.data)); |
| 463 break; |
| 464 } |
| 465 } |
| 466 keychain.ItemFreeAttributesAndData(attr_list, nullptr); |
| 467 return creator_code && creator_code == base::mac::CreatorCodeForApplication(); |
| 468 } |
| 469 |
438 bool FormsMatchForMerge(const PasswordForm& form_a, | 470 bool FormsMatchForMerge(const PasswordForm& form_a, |
439 const PasswordForm& form_b, | 471 const PasswordForm& form_b, |
440 FormMatchStrictness strictness) { | 472 FormMatchStrictness strictness) { |
441 // We never merge blacklist entries between our store and the Keychain, | 473 // We never merge blacklist entries between our store and the Keychain, |
442 // and federated logins should not be stored in the Keychain at all. | 474 // and federated logins should not be stored in the Keychain at all. |
443 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user || | 475 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user || |
444 !form_a.federation_url.is_empty() || !form_b.federation_url.is_empty()) { | 476 !form_a.federation_url.is_empty() || !form_b.federation_url.is_empty()) { |
445 return false; | 477 return false; |
446 } | 478 } |
447 bool equal_realm = form_a.signon_realm == form_b.signon_realm; | 479 bool equal_realm = form_a.signon_realm == form_b.signon_realm; |
(...skipping 467 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
915 #pragma mark - | 947 #pragma mark - |
916 | 948 |
917 PasswordStoreMac::PasswordStoreMac( | 949 PasswordStoreMac::PasswordStoreMac( |
918 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner, | 950 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner, |
919 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner, | 951 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner, |
920 scoped_ptr<AppleKeychain> keychain, | 952 scoped_ptr<AppleKeychain> keychain, |
921 password_manager::LoginDatabase* login_db) | 953 password_manager::LoginDatabase* login_db) |
922 : password_manager::PasswordStore(main_thread_runner, db_thread_runner), | 954 : password_manager::PasswordStore(main_thread_runner, db_thread_runner), |
923 keychain_(keychain.Pass()), | 955 keychain_(keychain.Pass()), |
924 login_metadata_db_(login_db) { | 956 login_metadata_db_(login_db) { |
925 DCHECK(keychain_.get()); | 957 DCHECK(keychain_); |
926 login_metadata_db_->set_clear_password_values(true); | 958 login_metadata_db_->set_clear_password_values(true); |
927 } | 959 } |
928 | 960 |
929 PasswordStoreMac::~PasswordStoreMac() {} | 961 PasswordStoreMac::~PasswordStoreMac() {} |
930 | 962 |
931 void PasswordStoreMac::InitWithTaskRunner( | 963 void PasswordStoreMac::InitWithTaskRunner( |
932 scoped_refptr<base::SingleThreadTaskRunner> background_task_runner) { | 964 scoped_refptr<base::SingleThreadTaskRunner> background_task_runner) { |
933 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 965 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
934 db_thread_runner_ = background_task_runner; | 966 db_thread_runner_ = background_task_runner; |
935 } | 967 } |
936 | 968 |
| 969 PasswordStoreMac::MigrationResult PasswordStoreMac::ImportFromKeychain() { |
| 970 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
| 971 if (!login_metadata_db_) |
| 972 return LOGIN_DB_UNAVAILABLE; |
| 973 |
| 974 ScopedVector<PasswordForm> database_forms; |
| 975 if (!login_metadata_db_->GetAutofillableLogins(&database_forms)) |
| 976 return LOGIN_DB_FAILURE; |
| 977 |
| 978 ScopedVector<PasswordForm> uninteresting_forms; |
| 979 internal_keychain_helpers::ExtractNonKeychainForms(&database_forms, |
| 980 &uninteresting_forms); |
| 981 // If there are no passwords to lookup in the Keychain then we're done. |
| 982 if (database_forms.empty()) |
| 983 return MIGRATION_OK; |
| 984 |
| 985 // Make sure that the encryption key is retrieved from the Keychain so the |
| 986 // encryption can be done. |
| 987 std::string ciphertext; |
| 988 if (!OSCrypt::EncryptString("test", &ciphertext)) |
| 989 return ENCRYPTOR_FAILURE; |
| 990 |
| 991 // Mute the Keychain. |
| 992 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed( |
| 993 false); |
| 994 |
| 995 // Retrieve the passwords. |
| 996 // Get all the password attributes first. |
| 997 std::vector<SecKeychainItemRef> keychain_items; |
| 998 std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs = |
| 999 internal_keychain_helpers:: |
| 1000 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items, |
| 1001 *keychain_); |
| 1002 |
| 1003 // Next, compare the attributes of the PasswordForms in |database_forms| |
| 1004 // against those in |item_form_pairs|, and extract password data for each |
| 1005 // matching PasswordForm using its corresponding SecKeychainItemRef. |
| 1006 size_t unmerged_forms_count = 0; |
| 1007 size_t chrome_owned_locked_forms_count = 0; |
| 1008 for (PasswordForm* form : database_forms) { |
| 1009 ScopedVector<autofill::PasswordForm> keychain_matches = |
| 1010 internal_keychain_helpers::ExtractPasswordsMergeableWithForm( |
| 1011 *keychain_, item_form_pairs, *form); |
| 1012 |
| 1013 const PasswordForm* best_match = |
| 1014 BestKeychainFormForForm(*form, keychain_matches.get()); |
| 1015 if (best_match) { |
| 1016 form->password_value = best_match->password_value; |
| 1017 } else { |
| 1018 unmerged_forms_count++; |
| 1019 // Check if any corresponding keychain items are created by Chrome. |
| 1020 for (const auto& item_form_pair : item_form_pairs) { |
| 1021 if (internal_keychain_helpers::FormIsValidAndMatchesOtherForm( |
| 1022 *form, *item_form_pair.second) && |
| 1023 internal_keychain_helpers::HasChromeCreatorCode( |
| 1024 *keychain_, *item_form_pair.first)) |
| 1025 chrome_owned_locked_forms_count++; |
| 1026 } |
| 1027 } |
| 1028 } |
| 1029 STLDeleteContainerPairSecondPointers(item_form_pairs.begin(), |
| 1030 item_form_pairs.end()); |
| 1031 for (SecKeychainItemRef item : keychain_items) |
| 1032 keychain_->Free(item); |
| 1033 |
| 1034 if (unmerged_forms_count) { |
| 1035 UMA_HISTOGRAM_COUNTS( |
| 1036 "PasswordManager.KeychainMigration.NumPasswordsOnFailure", |
| 1037 database_forms.size()); |
| 1038 UMA_HISTOGRAM_COUNTS("PasswordManager.KeychainMigration.NumFailedPasswords", |
| 1039 unmerged_forms_count); |
| 1040 UMA_HISTOGRAM_COUNTS( |
| 1041 "PasswordManager.KeychainMigration.NumChromeOwnedFailedPasswords", |
| 1042 chrome_owned_locked_forms_count); |
| 1043 return KEYCHAIN_BLOCKED; |
| 1044 } |
| 1045 // Now all the passwords are available. It's time to update LoginDatabase. |
| 1046 login_metadata_db_->set_clear_password_values(false); |
| 1047 for (PasswordForm* form : database_forms) |
| 1048 login_metadata_db_->UpdateLogin(*form); |
| 1049 return MIGRATION_OK; |
| 1050 } |
| 1051 |
937 bool PasswordStoreMac::Init( | 1052 bool PasswordStoreMac::Init( |
938 const syncer::SyncableService::StartSyncFlare& flare) { | 1053 const syncer::SyncableService::StartSyncFlare& flare) { |
939 // The class should be used inside PasswordStoreProxyMac only. | 1054 // The class should be used inside PasswordStoreProxyMac only. |
940 NOTREACHED(); | 1055 NOTREACHED(); |
941 return true; | 1056 return true; |
942 } | 1057 } |
943 | 1058 |
944 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username, | 1059 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username, |
945 bool custom_passphrase_sync_enabled) { | 1060 bool custom_passphrase_sync_enabled) { |
946 if (!login_metadata_db_) | 1061 if (!login_metadata_db_) |
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1205 ScopedVector<PasswordForm> forms_with_keychain_entry; | 1320 ScopedVector<PasswordForm> forms_with_keychain_entry; |
1206 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, | 1321 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, |
1207 &forms_with_keychain_entry); | 1322 &forms_with_keychain_entry); |
1208 | 1323 |
1209 // Clean up any orphaned database entries. | 1324 // Clean up any orphaned database entries. |
1210 RemoveDatabaseForms(&database_forms); | 1325 RemoveDatabaseForms(&database_forms); |
1211 | 1326 |
1212 // Move the orphaned DB forms to the output parameter. | 1327 // Move the orphaned DB forms to the output parameter. |
1213 AppendSecondToFirst(orphaned_forms, &database_forms); | 1328 AppendSecondToFirst(orphaned_forms, &database_forms); |
1214 } | 1329 } |
OLD | NEW |