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

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: Created 5 years, 6 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 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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698