Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(574)

Side by Side Diff: chrome/browser/password_manager/password_store_mac.cc

Issue 1207373002: Implement Mac Keychain migration algorithm. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: more comments Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698