// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/autofill/core/browser/autofill_ie_toolbar_import_win.h" #include #include #include #include #include #include "base/compiler_specific.h" #include "base/logging.h" #include "base/macros.h" #include "base/strings/string16.h" #include "base/win/registry.h" #include "components/autofill/core/browser/autofill_country.h" #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/crypto/rc4_decryptor.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_group.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/personal_data_manager_observer.h" #include "components/autofill/core/browser/phone_number.h" #include "components/autofill/core/browser/phone_number_i18n.h" #include "components/os_crypt/os_crypt.h" using base::win::RegKey; namespace autofill { // Forward declaration. This function is not in unnamed namespace as it // is referenced in the unittest. bool ImportCurrentUserProfiles(const std::string& app_locale, std::vector* profiles, std::vector* credit_cards); namespace { const wchar_t* const kProfileKey = L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Profiles"; const wchar_t* const kCreditCardKey = L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Credit Cards"; const wchar_t* const kPasswordHashValue = L"password_hash"; const wchar_t* const kSaltValue = L"salt"; // This string is stored along with saved addresses and credit cards in the // WebDB, and hence should not be modified, so that it remains consistent over // time. const char kIEToolbarImportOrigin[] = "Imported from Internet Explorer"; // This is RC4 decryption for Toolbar credit card data. This is necessary // because it is not standard, so Crypto API cannot be used. std::wstring DecryptCCNumber(const std::wstring& data) { const wchar_t* kEmptyKey = L"\x3605\xCEE5\xCE49\x44F7\xCF4E\xF6CC\x604B\xFCBE\xC70A\x08FD"; const size_t kMacLen = 10; if (data.length() <= kMacLen) return std::wstring(); RC4Decryptor rc4_algorithm(kEmptyKey); return rc4_algorithm.Run(data.substr(kMacLen)); } bool IsEmptySalt(std::wstring const& salt) { // Empty salt in IE Toolbar is \x1\x2...\x14 if (salt.length() != 20) return false; for (size_t i = 0; i < salt.length(); ++i) { if (salt[i] != i + 1) return false; } return true; } base::string16 ReadAndDecryptValue(const RegKey& key, const wchar_t* value_name) { DWORD data_type = REG_BINARY; DWORD data_size = 0; LONG result = key.ReadValue(value_name, NULL, &data_size, &data_type); if ((result != ERROR_SUCCESS) || !data_size || data_type != REG_BINARY) return base::string16(); std::string data; data.resize(data_size); result = key.ReadValue(value_name, &(data[0]), &data_size, &data_type); if (result == ERROR_SUCCESS) { std::string out_data; if (OSCrypt::DecryptString(data, &out_data)) { // The actual data is in UTF16 already. if (!(out_data.size() & 1) && (out_data.size() > 2) && !out_data[out_data.size() - 1] && !out_data[out_data.size() - 2]) { return base::string16( reinterpret_cast(out_data.c_str())); } } } return base::string16(); } struct { ServerFieldType field_type; const wchar_t *reg_value_name; } profile_reg_values[] = { {NAME_FIRST, L"name_first"}, {NAME_MIDDLE, L"name_middle"}, {NAME_LAST, L"name_last"}, {NAME_SUFFIX, L"name_suffix"}, {EMAIL_ADDRESS, L"email"}, {COMPANY_NAME, L"company_name"}, {PHONE_HOME_NUMBER, L"phone_home_number"}, {PHONE_HOME_CITY_CODE, L"phone_home_city_code"}, {PHONE_HOME_COUNTRY_CODE, L"phone_home_country_code"}, {ADDRESS_HOME_LINE1, L"address_home_line1"}, {ADDRESS_HOME_LINE2, L"address_home_line2"}, {ADDRESS_HOME_CITY, L"address_home_city"}, {ADDRESS_HOME_STATE, L"address_home_state"}, {ADDRESS_HOME_ZIP, L"address_home_zip"}, {ADDRESS_HOME_COUNTRY, L"address_home_country"}, {ADDRESS_BILLING_LINE1, L"address_billing_line1"}, {ADDRESS_BILLING_LINE2, L"address_billing_line2"}, {ADDRESS_BILLING_CITY, L"address_billing_city"}, {ADDRESS_BILLING_STATE, L"address_billing_state"}, {ADDRESS_BILLING_ZIP, L"address_billing_zip"}, {ADDRESS_BILLING_COUNTRY, L"address_billing_country"}, {CREDIT_CARD_NAME_FULL, L"credit_card_name_full"}, {CREDIT_CARD_NUMBER, L"credit_card_number"}, {CREDIT_CARD_EXP_MONTH, L"credit_card_exp_month"}, {CREDIT_CARD_EXP_4_DIGIT_YEAR, L"credit_card_exp_4_digit_year"}, {CREDIT_CARD_TYPE, L"credit_card_type"}, // We do not import verification code. }; typedef std::map RegToFieldMap; // Imports address or credit card data from the given registry |key| into the // given |form_group|, with the help of |reg_to_field|. When importing address // data, writes the phone data into |phone|; otherwise, |phone| should be null. // Returns true if any fields were set, false otherwise. bool ImportSingleFormGroup(const RegKey& key, const RegToFieldMap& reg_to_field, const std::string& app_locale, FormGroup* form_group, PhoneNumber::PhoneCombineHelper* phone) { if (!key.Valid()) return false; bool has_non_empty_fields = false; for (uint32_t i = 0; i < key.GetValueCount(); ++i) { std::wstring value_name; if (key.GetValueNameAt(i, &value_name) != ERROR_SUCCESS) continue; RegToFieldMap::const_iterator it = reg_to_field.find(value_name); if (it == reg_to_field.end()) continue; // This field is not imported. base::string16 field_value = ReadAndDecryptValue(key, value_name.c_str()); if (!field_value.empty()) { if (it->second == CREDIT_CARD_NUMBER) field_value = DecryptCCNumber(field_value); // Phone numbers are stored piece-by-piece, and then reconstructed from // the pieces. The rest of the fields are set "as is". if (!phone || !phone->SetInfo(AutofillType(it->second), field_value)) { has_non_empty_fields = true; form_group->SetInfo(AutofillType(it->second), field_value, app_locale); } } } return has_non_empty_fields; } // Imports address data from the given registry |key| into the given |profile|, // with the help of |reg_to_field|. Returns true if any fields were set, false // otherwise. bool ImportSingleProfile(const std::string& app_locale, const RegKey& key, const RegToFieldMap& reg_to_field, AutofillProfile* profile) { PhoneNumber::PhoneCombineHelper phone; bool has_non_empty_fields = ImportSingleFormGroup(key, reg_to_field, app_locale, profile, &phone); // Now re-construct the phones if needed. base::string16 constructed_number; if (phone.ParseNumber(*profile, app_locale, &constructed_number)) { has_non_empty_fields = true; profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, constructed_number); } return has_non_empty_fields; } // Imports profiles from the IE toolbar and stores them. Asynchronous // if PersonalDataManager has not been loaded yet. Deletes itself on completion. class AutofillImporter : public PersonalDataManagerObserver { public: explicit AutofillImporter(PersonalDataManager* personal_data_manager) : personal_data_manager_(personal_data_manager) { personal_data_manager_->AddObserver(this); } bool ImportProfiles() { if (!ImportCurrentUserProfiles(personal_data_manager_->app_locale(), &profiles_, &credit_cards_)) { delete this; return false; } if (personal_data_manager_->IsDataLoaded()) OnPersonalDataChanged(); return true; } // PersonalDataManagerObserver: void OnPersonalDataChanged() override { for (const AutofillProfile& it : profiles_) personal_data_manager_->AddProfile(it); for (const CreditCard& it : credit_cards_) personal_data_manager_->AddCreditCard(it); delete this; } private: ~AutofillImporter() override { personal_data_manager_->RemoveObserver(this); } PersonalDataManager* personal_data_manager_; std::vector profiles_; std::vector credit_cards_; }; } // namespace // Imports Autofill profiles and credit cards from IE Toolbar if present and not // password protected. Returns true if data is successfully retrieved. False if // there is no data, data is password protected or error occurred. bool ImportCurrentUserProfiles(const std::string& app_locale, std::vector* profiles, std::vector* credit_cards) { DCHECK(profiles); DCHECK(credit_cards); // Create a map of possible fields for a quick access. RegToFieldMap reg_to_field; for (const auto& profile_reg_value : profile_reg_values) { reg_to_field[std::wstring(profile_reg_value.reg_value_name)] = profile_reg_value.field_type; } base::win::RegistryKeyIterator iterator_profiles(HKEY_CURRENT_USER, kProfileKey); for (; iterator_profiles.Valid(); ++iterator_profiles) { std::wstring key_name(kProfileKey); key_name.append(L"\\"); key_name.append(iterator_profiles.Name()); RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ); AutofillProfile profile; profile.set_origin(kIEToolbarImportOrigin); if (ImportSingleProfile(app_locale, key, reg_to_field, &profile)) { // Combine phones into whole phone #. profiles->push_back(profile); } } base::string16 password_hash; base::string16 salt; RegKey cc_key(HKEY_CURRENT_USER, kCreditCardKey, KEY_READ); if (cc_key.Valid()) { password_hash = ReadAndDecryptValue(cc_key, kPasswordHashValue); salt = ReadAndDecryptValue(cc_key, kSaltValue); } // We import CC profiles only if they are not password protected. if (password_hash.empty() && IsEmptySalt(salt)) { base::win::RegistryKeyIterator iterator_cc(HKEY_CURRENT_USER, kCreditCardKey); for (; iterator_cc.Valid(); ++iterator_cc) { std::wstring key_name(kCreditCardKey); key_name.append(L"\\"); key_name.append(iterator_cc.Name()); RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ); CreditCard credit_card; credit_card.set_origin(kIEToolbarImportOrigin); if (ImportSingleFormGroup( key, reg_to_field, app_locale, &credit_card, NULL)) { base::string16 cc_number = credit_card.GetRawInfo(CREDIT_CARD_NUMBER); if (!cc_number.empty()) credit_cards->push_back(credit_card); } } } return (profiles->size() + credit_cards->size()) > 0; } bool ImportAutofillDataWin(PersonalDataManager* pdm) { // In incognito mode we do not have PDM - and we should not import anything. if (!pdm) return false; AutofillImporter* importer = new AutofillImporter(pdm); // importer will self delete. return importer->ImportProfiles(); } } // namespace autofill