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 attrInfo; | |
443 UInt32 tags[] = {kSecCreatorItemAttr}; | |
444 attrInfo.count = arraysize(tags); | |
445 attrInfo.tag = tags; | |
446 attrInfo.format = NULL; | |
447 | |
448 SecKeychainAttributeList* attrList; | |
449 UInt32 password_length; | |
450 OSStatus result = keychain.ItemCopyAttributesAndData( | |
451 keychain_item, &attrInfo, nullptr, &attrList, &password_length, nullptr); | |
452 if (result != noErr) | |
453 return false; | |
454 OSType creator_code = 0; | |
455 for (unsigned int i = 0; i < attrList->count; i++) { | |
456 SecKeychainAttribute attr = attrList->attr[i]; | |
457 if (!attr.data) { | |
458 continue; | |
459 } | |
460 if (attr.tag == kSecCreatorItemAttr) { | |
461 creator_code = *(static_cast<FourCharCode*>(attr.data)); | |
462 break; | |
463 } | |
464 } | |
465 keychain.ItemFreeAttributesAndData(attrList, nullptr); | |
466 return creator_code && creator_code == base::mac::CreatorCodeForApplication(); | |
467 } | |
468 | |
438 bool FormsMatchForMerge(const PasswordForm& form_a, | 469 bool FormsMatchForMerge(const PasswordForm& form_a, |
439 const PasswordForm& form_b, | 470 const PasswordForm& form_b, |
440 FormMatchStrictness strictness) { | 471 FormMatchStrictness strictness) { |
441 // We never merge blacklist entries between our store and the Keychain, | 472 // We never merge blacklist entries between our store and the Keychain, |
442 // and federated logins should not be stored in the Keychain at all. | 473 // and federated logins should not be stored in the Keychain at all. |
443 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user || | 474 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user || |
444 !form_a.federation_url.is_empty() || !form_b.federation_url.is_empty()) { | 475 !form_a.federation_url.is_empty() || !form_b.federation_url.is_empty()) { |
445 return false; | 476 return false; |
446 } | 477 } |
447 bool equal_realm = form_a.signon_realm == form_b.signon_realm; | 478 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 - | 946 #pragma mark - |
916 | 947 |
917 PasswordStoreMac::PasswordStoreMac( | 948 PasswordStoreMac::PasswordStoreMac( |
918 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner, | 949 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner, |
919 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner, | 950 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner, |
920 scoped_ptr<AppleKeychain> keychain, | 951 scoped_ptr<AppleKeychain> keychain, |
921 password_manager::LoginDatabase* login_db) | 952 password_manager::LoginDatabase* login_db) |
922 : password_manager::PasswordStore(main_thread_runner, db_thread_runner), | 953 : password_manager::PasswordStore(main_thread_runner, db_thread_runner), |
923 keychain_(keychain.Pass()), | 954 keychain_(keychain.Pass()), |
924 login_metadata_db_(login_db) { | 955 login_metadata_db_(login_db) { |
925 DCHECK(keychain_.get()); | 956 DCHECK(keychain_); |
926 login_metadata_db_->set_clear_password_values(true); | 957 login_metadata_db_->set_clear_password_values(true); |
927 } | 958 } |
928 | 959 |
929 PasswordStoreMac::~PasswordStoreMac() {} | 960 PasswordStoreMac::~PasswordStoreMac() {} |
930 | 961 |
931 void PasswordStoreMac::InitWithTaskRunner( | 962 void PasswordStoreMac::InitWithTaskRunner( |
932 scoped_refptr<base::SingleThreadTaskRunner> background_task_runner) { | 963 scoped_refptr<base::SingleThreadTaskRunner> background_task_runner) { |
933 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 964 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
934 db_thread_runner_ = background_task_runner; | 965 db_thread_runner_ = background_task_runner; |
935 } | 966 } |
936 | 967 |
968 PasswordStoreMac::MigrationResult PasswordStoreMac::ImportFromKeychain() { | |
969 using namespace internal_keychain_helpers; | |
Ilya Sherman
2015/06/25 21:46:25
Hmm, why isn't this at the top of the file?
Ryan Sleevi
2015/06/26 10:46:37
It's not required to be:
http://google-styleguide.
vasilii
2015/06/26 12:18:47
Though it prohibits "using namespace foo;" so I r
vasilii
2015/06/26 12:18:47
I removed it.
| |
970 | |
971 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
972 if (!login_metadata_db_) | |
973 return LOGIN_DB_UNAVAILABLE; | |
974 ScopedVector<PasswordForm> database_forms; | |
975 if (!login_metadata_db_->GetAutofillableLogins(&database_forms)) | |
976 return LOGIN_DB_FAILURE; | |
977 ScopedVector<PasswordForm> uninteresting_forms; | |
978 internal_keychain_helpers::ExtractNonKeychainForms(&database_forms, | |
979 &uninteresting_forms); | |
980 // If there are no passwords to lookup in the Keychain then we're done. | |
981 if (database_forms.empty()) | |
982 return MIGRATION_OK; | |
983 | |
984 // Make sure that the encryption key is retrieved from the Keychain so the | |
985 // encryption can be done. | |
986 DCHECK(!database_forms[0]->origin.is_empty()); | |
987 std::string ciphertext; | |
988 if (!OSCrypt::EncryptString(database_forms[0]->origin.spec(), &ciphertext)) | |
989 return ENCRYPTOR_FAILURE; | |
990 // Mute the Keychain. | |
991 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed( | |
992 false); | |
993 | |
994 // Retrieve the passwords. | |
995 // Get all the password attributes first. | |
996 std::vector<SecKeychainItemRef> keychain_items; | |
997 std::vector<ItemFormPair> item_form_pairs = | |
998 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items, | |
999 *keychain_); | |
1000 // Next, compare the attributes of the PasswordForms in |database_forms| | |
1001 // against those in |item_form_pairs|, and extract password data for each | |
1002 // matching PasswordForm using its corresponding SecKeychainItemRef. | |
1003 int unmerged_forms_count = 0; | |
1004 int chrome_owned_locked_forms_count = 0; | |
1005 for (PasswordForm* form : database_forms) { | |
1006 ScopedVector<autofill::PasswordForm> keychain_matches = | |
1007 ExtractPasswordsMergeableWithForm(*keychain_, item_form_pairs, *form); | |
1008 | |
1009 const PasswordForm* best_match = | |
1010 BestKeychainFormForForm(*form, keychain_matches.get()); | |
1011 if (best_match) { | |
1012 form->password_value = best_match->password_value; | |
1013 } else { | |
1014 unmerged_forms_count++; | |
1015 // Check if the corresponding keychain items are created by Chrome | |
1016 for (const auto& item_form_pair : item_form_pairs) { | |
1017 if (FormIsValidAndMatchesOtherForm(*form, *item_form_pair.second) && | |
1018 HasChromeCreatorCode(*keychain_, *item_form_pair.first)) | |
1019 chrome_owned_locked_forms_count++; | |
1020 } | |
1021 } | |
1022 } | |
1023 STLDeleteContainerPairSecondPointers(item_form_pairs.begin(), | |
1024 item_form_pairs.end()); | |
1025 for (SecKeychainItemRef item : keychain_items) { | |
1026 keychain_->Free(item); | |
1027 } | |
Ryan Sleevi
2015/06/26 10:46:37
STYLE: You're inconsistent with the braces for sin
vasilii
2015/06/26 12:18:47
I'm consistent. It was a copy-paste :-)
| |
1028 if (unmerged_forms_count) { | |
1029 UMA_HISTOGRAM_COUNTS("PasswordManager.KeychainMigration.NumPasswords", | |
1030 database_forms.size()); | |
1031 UMA_HISTOGRAM_COUNTS("PasswordManager.KeychainMigration.NumLockedPasswords", | |
1032 unmerged_forms_count); | |
1033 UMA_HISTOGRAM_COUNTS( | |
1034 "PasswordManager.KeychainMigration.NumChromeOwnedLockedPasswords", | |
1035 unmerged_forms_count); | |
Ilya Sherman
2015/06/25 21:46:24
It looks like you emit "unmerged_forms_count" to b
vasilii
2015/06/26 12:18:47
Oh, that's a bug. It should be chrome_owned_locked
| |
1036 return KEYCHAIN_BLOCKED; | |
1037 } | |
1038 // Now all the passwords are available. It's time to update LoginDatabase. | |
1039 login_metadata_db_->set_clear_password_values(false); | |
1040 for (PasswordForm* form : database_forms) | |
1041 login_metadata_db_->UpdateLogin(*form); | |
1042 return MIGRATION_OK; | |
1043 } | |
1044 | |
937 bool PasswordStoreMac::Init( | 1045 bool PasswordStoreMac::Init( |
938 const syncer::SyncableService::StartSyncFlare& flare) { | 1046 const syncer::SyncableService::StartSyncFlare& flare) { |
939 // The class should be used inside PasswordStoreProxyMac only. | 1047 // The class should be used inside PasswordStoreProxyMac only. |
940 NOTREACHED(); | 1048 NOTREACHED(); |
941 return true; | 1049 return true; |
942 } | 1050 } |
943 | 1051 |
944 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username, | 1052 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username, |
945 bool custom_passphrase_sync_enabled) { | 1053 bool custom_passphrase_sync_enabled) { |
946 if (!login_metadata_db_) | 1054 if (!login_metadata_db_) |
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1205 ScopedVector<PasswordForm> forms_with_keychain_entry; | 1313 ScopedVector<PasswordForm> forms_with_keychain_entry; |
1206 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, | 1314 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, |
1207 &forms_with_keychain_entry); | 1315 &forms_with_keychain_entry); |
1208 | 1316 |
1209 // Clean up any orphaned database entries. | 1317 // Clean up any orphaned database entries. |
1210 RemoveDatabaseForms(&database_forms); | 1318 RemoveDatabaseForms(&database_forms); |
1211 | 1319 |
1212 // Move the orphaned DB forms to the output parameter. | 1320 // Move the orphaned DB forms to the output parameter. |
1213 AppendSecondToFirst(orphaned_forms, &database_forms); | 1321 AppendSecondToFirst(orphaned_forms, &database_forms); |
1214 } | 1322 } |
OLD | NEW |