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; | |
stuartmorgan
2015/06/26 21:26:08
attr_info
vasilii
2015/06/29 14:12:45
Done.
| |
443 UInt32 tags[] = {kSecCreatorItemAttr}; | |
444 attrInfo.count = arraysize(tags); | |
445 attrInfo.tag = tags; | |
446 attrInfo.format = NULL; | |
stuartmorgan
2015/06/26 21:26:08
nullptr
vasilii
2015/06/29 14:12:45
Done.
| |
447 | |
448 SecKeychainAttributeList* attrList; | |
stuartmorgan
2015/06/26 21:26:08
attr_list
vasilii
2015/06/29 14:12:44
Done.
| |
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 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); | |
970 if (!login_metadata_db_) | |
971 return LOGIN_DB_UNAVAILABLE; | |
972 ScopedVector<PasswordForm> database_forms; | |
973 if (!login_metadata_db_->GetAutofillableLogins(&database_forms)) | |
974 return LOGIN_DB_FAILURE; | |
975 ScopedVector<PasswordForm> uninteresting_forms; | |
Ryan Sleevi
2015/06/26 16:44:35
nits (feel free to ignore) - I found the transitio
vasilii
2015/06/29 14:12:45
Done.
| |
976 internal_keychain_helpers::ExtractNonKeychainForms(&database_forms, | |
977 &uninteresting_forms); | |
978 // If there are no passwords to lookup in the Keychain then we're done. | |
979 if (database_forms.empty()) | |
980 return MIGRATION_OK; | |
981 | |
982 // Make sure that the encryption key is retrieved from the Keychain so the | |
983 // encryption can be done. | |
984 DCHECK(!database_forms[0]->origin.is_empty()); | |
985 std::string ciphertext; | |
986 if (!OSCrypt::EncryptString(database_forms[0]->origin.spec(), &ciphertext)) | |
stuartmorgan
2015/06/26 21:26:07
Why are you pulling a random field out for this, i
vasilii
2015/06/29 14:12:44
Done. Binary size?
| |
987 return ENCRYPTOR_FAILURE; | |
988 // Mute the Keychain. | |
989 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed( | |
990 false); | |
991 | |
992 // Retrieve the passwords. | |
993 // Get all the password attributes first. | |
994 std::vector<SecKeychainItemRef> keychain_items; | |
995 std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs = | |
996 internal_keychain_helpers:: | |
997 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items, | |
stuartmorgan
2015/06/26 21:26:08
Indent 4 more; it's a continuation
vasilii
2015/06/29 14:12:44
Done.
| |
998 *keychain_); | |
999 | |
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; | |
Ryan Sleevi
2015/06/26 16:44:35
STYLE: (usually a SECURITY BUG, but here it just s
vasilii
2015/06/29 14:12:44
Done. Though implicit conversion to int in UMA_HIS
Ilya Sherman
2015/06/29 23:10:52
You could use a checked_cast (from safe_numerics.h
vasilii
2015/06/30 08:45:49
It contains CHECK(), and I don't want to have a cr
| |
1005 for (PasswordForm* form : database_forms) { | |
1006 ScopedVector<autofill::PasswordForm> keychain_matches = | |
1007 internal_keychain_helpers::ExtractPasswordsMergeableWithForm( | |
stuartmorgan
2015/06/26 21:26:08
This seems wasteful (it may do extra Keychain look
vasilii
2015/06/29 14:12:44
I think it should return exactly one form (which i
| |
1008 *keychain_, item_form_pairs, *form); | |
1009 | |
1010 const PasswordForm* best_match = | |
1011 BestKeychainFormForForm(*form, keychain_matches.get()); | |
1012 if (best_match) { | |
1013 form->password_value = best_match->password_value; | |
1014 } else { | |
1015 unmerged_forms_count++; | |
1016 // Check if the corresponding keychain items are created by Chrome | |
stuartmorgan
2015/06/26 21:26:07
s/the/any/ (since there may be none). Also, missin
vasilii
2015/06/29 14:12:45
Done.
| |
1017 for (const auto& item_form_pair : item_form_pairs) { | |
1018 if (internal_keychain_helpers::FormIsValidAndMatchesOtherForm( | |
1019 *form, *item_form_pair.second) && | |
1020 internal_keychain_helpers::HasChromeCreatorCode( | |
1021 *keychain_, *item_form_pair.first)) | |
1022 chrome_owned_locked_forms_count++; | |
1023 } | |
1024 } | |
1025 } | |
1026 STLDeleteContainerPairSecondPointers(item_form_pairs.begin(), | |
1027 item_form_pairs.end()); | |
1028 for (SecKeychainItemRef item : keychain_items) { | |
1029 keychain_->Free(item); | |
1030 } | |
Ryan Sleevi
2015/06/26 16:44:35
nit: Since it looks like the previous comment wasn
vasilii
2015/06/29 14:12:44
Done.
| |
1031 if (unmerged_forms_count) { | |
1032 UMA_HISTOGRAM_COUNTS( | |
1033 "PasswordManager.KeychainMigration.NumPasswordsOnFailure", | |
stuartmorgan
2015/06/26 21:26:08
Indent these lines 4, not 2.
vasilii
2015/06/29 14:12:44
Done.
| |
1034 database_forms.size()); | |
1035 UMA_HISTOGRAM_COUNTS("PasswordManager.KeychainMigration.NumFailedPasswords", | |
1036 unmerged_forms_count); | |
1037 UMA_HISTOGRAM_COUNTS( | |
1038 "PasswordManager.KeychainMigration.NumChromeOwnedFailedPasswords", | |
1039 chrome_owned_locked_forms_count); | |
1040 return KEYCHAIN_BLOCKED; | |
1041 } | |
1042 // Now all the passwords are available. It's time to update LoginDatabase. | |
1043 login_metadata_db_->set_clear_password_values(false); | |
1044 for (PasswordForm* form : database_forms) | |
1045 login_metadata_db_->UpdateLogin(*form); | |
1046 return MIGRATION_OK; | |
1047 } | |
1048 | |
937 bool PasswordStoreMac::Init( | 1049 bool PasswordStoreMac::Init( |
938 const syncer::SyncableService::StartSyncFlare& flare) { | 1050 const syncer::SyncableService::StartSyncFlare& flare) { |
939 // The class should be used inside PasswordStoreProxyMac only. | 1051 // The class should be used inside PasswordStoreProxyMac only. |
940 NOTREACHED(); | 1052 NOTREACHED(); |
941 return true; | 1053 return true; |
942 } | 1054 } |
943 | 1055 |
944 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username, | 1056 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username, |
945 bool custom_passphrase_sync_enabled) { | 1057 bool custom_passphrase_sync_enabled) { |
946 if (!login_metadata_db_) | 1058 if (!login_metadata_db_) |
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1205 ScopedVector<PasswordForm> forms_with_keychain_entry; | 1317 ScopedVector<PasswordForm> forms_with_keychain_entry; |
1206 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, | 1318 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms, |
1207 &forms_with_keychain_entry); | 1319 &forms_with_keychain_entry); |
1208 | 1320 |
1209 // Clean up any orphaned database entries. | 1321 // Clean up any orphaned database entries. |
1210 RemoveDatabaseForms(&database_forms); | 1322 RemoveDatabaseForms(&database_forms); |
1211 | 1323 |
1212 // Move the orphaned DB forms to the output parameter. | 1324 // Move the orphaned DB forms to the output parameter. |
1213 AppendSecondToFirst(orphaned_forms, &database_forms); | 1325 AppendSecondToFirst(orphaned_forms, &database_forms); |
1214 } | 1326 } |
OLD | NEW |