autofill_ie_toolbar_import_win.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/autofill/autofill_ie_toolbar_import_win.h"
6
7#include "base/basictypes.h"
8#include "base/string16.h"
9#include "base/win/registry.h"
10#include "chrome/browser/autofill/autofill_profile.h"
11#include "chrome/browser/autofill/credit_card.h"
12#include "chrome/browser/autofill/crypto/rc4_decryptor.h"
13#include "chrome/browser/autofill/field_types.h"
14#include "chrome/browser/autofill/personal_data_manager.h"
15#include "chrome/browser/sync/util/data_encryption.h"
16
17using base::win::RegKey;
18
19// Forward declaration. This function is not in unnamed namespace as it
20// is referenced in the unittest.
21bool ImportCurrentUserProfiles(std::vector<AutoFillProfile>* profiles,
22                               std::vector<CreditCard>* credit_cards);
23namespace {
24
25const wchar_t* const kProfileKey =
26    L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Profiles";
27const wchar_t* const kCreditCardKey =
28    L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Credit Cards";
29const wchar_t* const kPasswordHashValue = L"password_hash";
30const wchar_t* const kSaltValue = L"salt";
31
32// This is RC4 decryption for Toolbar credit card data. This is necessary
33// because it is not standard, so Crypto api cannot be used.
34std::wstring DecryptCCNumber(const std::wstring& data) {
35  const wchar_t* kEmptyKey =
36    L"\x3605\xCEE5\xCE49\x44F7\xCF4E\xF6CC\x604B\xFCBE\xC70A\x08FD";
37  const size_t kMacLen = 10;
38
39  if (data.length() <= kMacLen)
40    return std::wstring();
41
42  RC4Decryptor rc4_algorithm(kEmptyKey);
43  return rc4_algorithm.Run(data.substr(kMacLen));
44}
45
46bool IsEmptySalt(std::wstring const& salt) {
47  // Empty salt in IE Toolbar is \x1\x2...\x14
48  if (salt.length() != 20)
49    return false;
50  for (size_t i = 0; i < salt.length(); ++i) {
51    if (salt[i] != i + 1)
52      return false;
53  }
54  return true;
55}
56
57string16 ReadAndDecryptValue(RegKey* key, const wchar_t* value_name) {
58  DWORD data_type = REG_BINARY;
59  DWORD data_size = 0;
60  LONG result = key->ReadValue(value_name, NULL, &data_size, &data_type);
61  if ((result != ERROR_SUCCESS) || !data_size || data_type != REG_BINARY)
62    return string16();
63  std::vector<uint8> data;
64  data.resize(data_size);
65  result = key->ReadValue(value_name, &(data[0]), &data_size, &data_type);
66  if (result == ERROR_SUCCESS) {
67    std::string out_data;
68    if (DecryptData(data, &out_data)) {
69      // The actual data is in UTF16 already.
70      if (!(out_data.size() & 1) && (out_data.size() > 2) &&
71          !out_data[out_data.size() - 1] && !out_data[out_data.size() - 2]) {
72        return string16(
73            reinterpret_cast<const wchar_t *>(out_data.c_str()));
74      }
75    }
76  }
77  return string16();
78}
79
80struct {
81  AutoFillFieldType field_type;
82  const wchar_t *reg_value_name;
83} profile_reg_values[] = {
84  { NAME_FIRST,                    L"name_first" },
85  { NAME_MIDDLE,                   L"name_middle" },
86  { NAME_LAST,                     L"name_last" },
87  { NAME_SUFFIX,                   L"name_suffix" },
88  { EMAIL_ADDRESS,                 L"email" },
89  { COMPANY_NAME,                  L"company_name" },
90  { PHONE_HOME_NUMBER,             L"phone_home_number" },
91  { PHONE_HOME_CITY_CODE,          L"phone_home_city_code" },
92  { PHONE_HOME_COUNTRY_CODE,       L"phone_home_country_code" },
93  { PHONE_FAX_NUMBER,              L"phone_fax_number" },
94  { PHONE_FAX_CITY_CODE,           L"phone_fax_city_code" },
95  { PHONE_FAX_COUNTRY_CODE,        L"phone_fax_country_code" },
96  { ADDRESS_HOME_LINE1,            L"address_home_line1" },
97  { ADDRESS_HOME_LINE2,            L"address_home_line2" },
98  { ADDRESS_HOME_CITY,             L"address_home_city" },
99  { ADDRESS_HOME_STATE,            L"address_home_state" },
100  { ADDRESS_HOME_ZIP,              L"address_home_zip" },
101  { ADDRESS_HOME_COUNTRY,          L"address_home_country" },
102  { ADDRESS_BILLING_LINE1,         L"address_billing_line1" },
103  { ADDRESS_BILLING_LINE2,         L"address_billing_line2" },
104  { ADDRESS_BILLING_CITY,          L"address_billing_city" },
105  { ADDRESS_BILLING_STATE,         L"address_billing_state" },
106  { ADDRESS_BILLING_ZIP,           L"address_billing_zip" },
107  { ADDRESS_BILLING_COUNTRY,       L"address_billing_country" },
108  { CREDIT_CARD_NAME,              L"credit_card_name" },
109  { CREDIT_CARD_NUMBER,            L"credit_card_number" },
110  { CREDIT_CARD_EXP_MONTH,         L"credit_card_exp_month" },
111  { CREDIT_CARD_EXP_4_DIGIT_YEAR,  L"credit_card_exp_4_digit_year" },
112  { CREDIT_CARD_TYPE,              L"credit_card_type" },
113  // We do not import verification code.
114};
115
116typedef std::map<std::wstring, AutoFillFieldType> RegToFieldMap;
117
118bool ImportSingleProfile(FormGroup* profile,
119                         RegKey* key,
120                         const RegToFieldMap& reg_to_field ) {
121  DCHECK(profile != NULL);
122  if (!key->Valid())
123    return false;
124
125  bool has_non_empty_fields = false;
126
127  for (uint32 value_index = 0; value_index < key->ValueCount(); ++value_index) {
128    std::wstring value_name;
129    if (key->ReadName(value_index, &value_name) != ERROR_SUCCESS)
130      continue;
131    RegToFieldMap::const_iterator it = reg_to_field.find(value_name);
132    if (it == reg_to_field.end())
133      continue;  // This field is not imported.
134    string16 field_value = ReadAndDecryptValue(key, value_name.c_str());
135    if (!field_value.empty()) {
136      has_non_empty_fields = true;
137      if (it->second == CREDIT_CARD_NUMBER) {
138        field_value = DecryptCCNumber(field_value);
139      }
140      profile->SetInfo(AutoFillType(it->second), field_value);
141    }
142  }
143  return has_non_empty_fields;
144}
145
146// Imports profiles from the IE toolbar and stores them. Asynchronous
147// if PersonalDataManager has not been loaded yet. Deletes itself on completion.
148class AutoFillImporter : public PersonalDataManager::Observer {
149 public:
150  explicit AutoFillImporter(PersonalDataManager* personal_data_manager)
151    : personal_data_manager_(personal_data_manager) {
152      personal_data_manager_->SetObserver(this);
153  }
154
155  bool ImportProfiles() {
156    if (!ImportCurrentUserProfiles(&profiles_, &credit_cards_)) {
157      delete this;
158      return false;
159    }
160    if (personal_data_manager_->IsDataLoaded())
161      OnPersonalDataLoaded();
162    return true;
163  }
164
165  // PersonalDataManager::Observer methods:
166  virtual void OnPersonalDataLoaded() {
167    if (!profiles_.empty())
168      personal_data_manager_->SetProfiles(&profiles_);
169    if (!credit_cards_.empty())
170      personal_data_manager_->SetCreditCards(&credit_cards_);
171    delete this;
172  }
173
174 private:
175  ~AutoFillImporter() {
176    personal_data_manager_->RemoveObserver(this);
177  }
178
179  PersonalDataManager* personal_data_manager_;
180  std::vector<AutoFillProfile> profiles_;
181  std::vector<CreditCard> credit_cards_;
182};
183
184}  // namespace
185
186// Imports AutoFill profiles and credit cards from IE Toolbar if present and not
187// password protected. Returns true if data is successfully retrieved. False if
188// there is no data, data is password protected or error occurred.
189bool ImportCurrentUserProfiles(std::vector<AutoFillProfile>* profiles,
190                               std::vector<CreditCard>* credit_cards) {
191  DCHECK(profiles);
192  DCHECK(credit_cards);
193
194  // Create a map of possible fields for a quick access.
195  RegToFieldMap reg_to_field;
196  for (size_t i = 0; i < arraysize(profile_reg_values); ++i) {
197    reg_to_field[std::wstring(profile_reg_values[i].reg_value_name)] =
198        profile_reg_values[i].field_type;
199  }
200
201  base::win::RegistryKeyIterator iterator_profiles(HKEY_CURRENT_USER,
202                                                   kProfileKey);
203  for (; iterator_profiles.Valid(); ++iterator_profiles) {
204    std::wstring key_name(kProfileKey);
205    key_name.append(L"\\");
206    key_name.append(iterator_profiles.Name());
207    RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ);
208    AutoFillProfile profile;
209    if (ImportSingleProfile(&profile, &key, reg_to_field)) {
210      // Combine phones into whole phone #.
211      string16 phone;
212      phone = profile.GetFieldText(AutoFillType(PHONE_HOME_COUNTRY_CODE));
213      phone.append(profile.GetFieldText(AutoFillType(PHONE_HOME_CITY_CODE)));
214      phone.append(profile.GetFieldText(AutoFillType(PHONE_HOME_NUMBER)));
215      profile.SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER), phone);
216      phone = profile.GetFieldText(AutoFillType(PHONE_FAX_COUNTRY_CODE));
217      phone.append(profile.GetFieldText(AutoFillType(PHONE_FAX_CITY_CODE)));
218      phone.append(profile.GetFieldText(AutoFillType(PHONE_FAX_NUMBER)));
219      profile.SetInfo(AutoFillType(PHONE_FAX_WHOLE_NUMBER), phone);
220      profiles->push_back(profile);
221    }
222  }
223  string16 password_hash;
224  string16 salt;
225  RegKey cc_key(HKEY_CURRENT_USER, kCreditCardKey, KEY_READ);
226  if (cc_key.Valid()) {
227    password_hash = ReadAndDecryptValue(&cc_key, kPasswordHashValue);
228    salt = ReadAndDecryptValue(&cc_key, kSaltValue);
229  }
230
231  // We import CC profiles only if they are not password protected.
232  if (password_hash.empty() && IsEmptySalt(salt)) {
233    base::win::RegistryKeyIterator iterator_cc(HKEY_CURRENT_USER,
234                                               kCreditCardKey);
235    for (; iterator_cc.Valid(); ++iterator_cc) {
236      std::wstring key_name(kCreditCardKey);
237      key_name.append(L"\\");
238      key_name.append(iterator_cc.Name());
239      RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ);
240      CreditCard credit_card;
241      if (ImportSingleProfile(&credit_card, &key, reg_to_field)) {
242        string16 cc_number = credit_card.GetFieldText(
243            AutoFillType(CREDIT_CARD_NUMBER));
244        if (!cc_number.empty())
245          credit_cards->push_back(credit_card);
246      }
247    }
248  }
249  return (profiles->size() + credit_cards->size()) > 0;
250}
251
252bool ImportAutofillDataWin(PersonalDataManager* pdm) {
253  // In incognito mode we do not have PDM - and we should not import anything.
254  if (!pdm)
255    return false;
256  AutoFillImporter *importer = new AutoFillImporter(pdm);
257  // importer will self delete.
258  return importer->ImportProfiles();
259}
260
261