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

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: 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 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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698