Index: chrome/browser/password_manager/password_store_mac.cc |
diff --git a/chrome/browser/password_manager/password_store_mac.cc b/chrome/browser/password_manager/password_store_mac.cc |
index 13ceae02d4991cf0dd38976bee75bc2e15dc9fb7..ca4253612c156a6e83ef81e5477be40fb1920d37 100644 |
--- a/chrome/browser/password_manager/password_store_mac.cc |
+++ b/chrome/browser/password_manager/password_store_mac.cc |
@@ -16,10 +16,12 @@ |
#include "base/mac/foundation_util.h" |
#include "base/mac/mac_logging.h" |
#include "base/message_loop/message_loop.h" |
+#include "base/metrics/histogram_macros.h" |
#include "base/stl_util.h" |
#include "base/strings/string_util.h" |
#include "base/strings/utf_string_conversions.h" |
#include "chrome/browser/mac/security_wrappers.h" |
+#include "components/os_crypt/os_crypt.h" |
#include "components/password_manager/core/browser/affiliation_utils.h" |
#include "components/password_manager/core/browser/login_database.h" |
#include "components/password_manager/core/browser/password_store_change.h" |
@@ -435,6 +437,36 @@ bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain, |
return true; |
} |
+bool HasChromeCreatorCode(const AppleKeychain& keychain, |
+ const SecKeychainItemRef& keychain_item) { |
+ SecKeychainAttributeInfo attr_info; |
+ UInt32 tags[] = {kSecCreatorItemAttr}; |
+ attr_info.count = arraysize(tags); |
+ attr_info.tag = tags; |
+ attr_info.format = nullptr; |
+ |
+ SecKeychainAttributeList* attr_list; |
+ UInt32 password_length; |
+ OSStatus result = keychain.ItemCopyAttributesAndData( |
+ keychain_item, &attr_info, nullptr, &attr_list, |
+ &password_length, nullptr); |
+ if (result != noErr) |
+ return false; |
+ OSType creator_code = 0; |
+ for (unsigned int i = 0; i < attr_list->count; i++) { |
+ SecKeychainAttribute attr = attr_list->attr[i]; |
+ if (!attr.data) { |
+ continue; |
+ } |
+ if (attr.tag == kSecCreatorItemAttr) { |
+ creator_code = *(static_cast<FourCharCode*>(attr.data)); |
+ break; |
+ } |
+ } |
+ keychain.ItemFreeAttributesAndData(attr_list, nullptr); |
+ return creator_code && creator_code == base::mac::CreatorCodeForApplication(); |
+} |
+ |
bool FormsMatchForMerge(const PasswordForm& form_a, |
const PasswordForm& form_b, |
FormMatchStrictness strictness) { |
@@ -922,7 +954,7 @@ PasswordStoreMac::PasswordStoreMac( |
: password_manager::PasswordStore(main_thread_runner, db_thread_runner), |
keychain_(keychain.Pass()), |
login_metadata_db_(login_db) { |
- DCHECK(keychain_.get()); |
+ DCHECK(keychain_); |
login_metadata_db_->set_clear_password_values(true); |
} |
@@ -934,6 +966,89 @@ void PasswordStoreMac::InitWithTaskRunner( |
db_thread_runner_ = background_task_runner; |
} |
+PasswordStoreMac::MigrationResult PasswordStoreMac::ImportFromKeychain() { |
+ DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); |
+ if (!login_metadata_db_) |
+ return LOGIN_DB_UNAVAILABLE; |
+ |
+ ScopedVector<PasswordForm> database_forms; |
+ if (!login_metadata_db_->GetAutofillableLogins(&database_forms)) |
+ return LOGIN_DB_FAILURE; |
+ |
+ ScopedVector<PasswordForm> uninteresting_forms; |
+ internal_keychain_helpers::ExtractNonKeychainForms(&database_forms, |
+ &uninteresting_forms); |
+ // If there are no passwords to lookup in the Keychain then we're done. |
+ if (database_forms.empty()) |
+ return MIGRATION_OK; |
+ |
+ // Make sure that the encryption key is retrieved from the Keychain so the |
+ // encryption can be done. |
+ std::string ciphertext; |
+ if (!OSCrypt::EncryptString("test", &ciphertext)) |
+ return ENCRYPTOR_FAILURE; |
+ |
+ // Mute the Keychain. |
+ chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed( |
+ false); |
+ |
+ // Retrieve the passwords. |
+ // Get all the password attributes first. |
+ std::vector<SecKeychainItemRef> keychain_items; |
+ std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs = |
+ internal_keychain_helpers:: |
+ ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items, |
+ *keychain_); |
+ |
+ // Next, compare the attributes of the PasswordForms in |database_forms| |
+ // against those in |item_form_pairs|, and extract password data for each |
+ // matching PasswordForm using its corresponding SecKeychainItemRef. |
+ size_t unmerged_forms_count = 0; |
+ size_t chrome_owned_locked_forms_count = 0; |
+ for (PasswordForm* form : database_forms) { |
+ ScopedVector<autofill::PasswordForm> keychain_matches = |
+ internal_keychain_helpers::ExtractPasswordsMergeableWithForm( |
+ *keychain_, item_form_pairs, *form); |
+ |
+ const PasswordForm* best_match = |
+ BestKeychainFormForForm(*form, keychain_matches.get()); |
+ if (best_match) { |
+ form->password_value = best_match->password_value; |
+ } else { |
+ unmerged_forms_count++; |
+ // Check if any corresponding keychain items are created by Chrome. |
+ for (const auto& item_form_pair : item_form_pairs) { |
+ if (internal_keychain_helpers::FormIsValidAndMatchesOtherForm( |
+ *form, *item_form_pair.second) && |
+ internal_keychain_helpers::HasChromeCreatorCode( |
+ *keychain_, *item_form_pair.first)) |
+ chrome_owned_locked_forms_count++; |
+ } |
+ } |
+ } |
+ STLDeleteContainerPairSecondPointers(item_form_pairs.begin(), |
+ item_form_pairs.end()); |
+ for (SecKeychainItemRef item : keychain_items) |
+ keychain_->Free(item); |
+ |
+ if (unmerged_forms_count) { |
+ UMA_HISTOGRAM_COUNTS( |
+ "PasswordManager.KeychainMigration.NumPasswordsOnFailure", |
+ database_forms.size()); |
+ UMA_HISTOGRAM_COUNTS("PasswordManager.KeychainMigration.NumFailedPasswords", |
+ unmerged_forms_count); |
+ UMA_HISTOGRAM_COUNTS( |
+ "PasswordManager.KeychainMigration.NumChromeOwnedInaccessiblePasswords", |
+ chrome_owned_locked_forms_count); |
+ return KEYCHAIN_BLOCKED; |
+ } |
+ // Now all the passwords are available. It's time to update LoginDatabase. |
+ login_metadata_db_->set_clear_password_values(false); |
+ for (PasswordForm* form : database_forms) |
+ login_metadata_db_->UpdateLogin(*form); |
+ return MIGRATION_OK; |
+} |
+ |
bool PasswordStoreMac::Init( |
const syncer::SyncableService::StartSyncFlare& flare) { |
// The class should be used inside PasswordStoreProxyMac only. |