password_store_win.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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
3// found in the LICENSE file.
4
5#include "chrome/browser/password_manager/password_store_win.h"
6
7#include <map>
8
9#include "base/bind.h"
10#include "base/logging.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/strings/string_util.h"
13#include "base/strings/utf_string_conversions.h"
14#include "chrome/browser/password_manager/password_manager.h"
15#include "chrome/browser/webdata/web_data_service.h"
16#include "components/webdata/encryptor/ie7_password_win.h"
17#include "content/public/browser/browser_thread.h"
18
19using autofill::PasswordForm;
20using content::BrowserThread;
21
22// Handles requests to WebDataService.
23class PasswordStoreWin::DBHandler : public WebDataServiceConsumer {
24 public:
25  DBHandler(WebDataService* web_data_service,
26            PasswordStoreWin* password_store)
27      : web_data_service_(web_data_service),
28        password_store_(password_store) {
29  }
30
31  ~DBHandler();
32
33  // Requests the IE7 login for |form|. This is async. |callback_runner| will be
34  // run when complete.
35  void GetIE7Login(
36      const PasswordForm& form,
37      const PasswordStoreWin::ConsumerCallbackRunner& callback_runner);
38
39 private:
40  struct RequestInfo {
41    RequestInfo() {}
42
43    RequestInfo(PasswordForm* request_form,
44                const PasswordStoreWin::ConsumerCallbackRunner& runner)
45        : form(request_form),
46          callback_runner(runner) {}
47
48    PasswordForm* form;
49    PasswordStoreWin::ConsumerCallbackRunner callback_runner;
50  };
51
52  // Holds info associated with in-flight GetIE7Login requests.
53  typedef std::map<WebDataService::Handle, RequestInfo> PendingRequestMap;
54
55  // Gets logins from IE7 if no others are found. Also copies them into
56  // Chrome's WebDatabase so we don't need to look next time.
57  std::vector<autofill::PasswordForm*> GetIE7Results(
58      const WDTypedResult* result,
59      const PasswordForm& form);
60
61  // WebDataServiceConsumer implementation.
62  virtual void OnWebDataServiceRequestDone(
63      WebDataService::Handle handle,
64      const WDTypedResult* result) OVERRIDE;
65
66  scoped_refptr<WebDataService> web_data_service_;
67
68  // This creates a cycle between us and PasswordStore. The cycle is broken
69  // from PasswordStoreWin::Shutdown, which deletes us.
70  scoped_refptr<PasswordStoreWin> password_store_;
71
72  PendingRequestMap pending_requests_;
73
74  DISALLOW_COPY_AND_ASSIGN(DBHandler);
75};
76
77PasswordStoreWin::DBHandler::~DBHandler() {
78  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
79  for (PendingRequestMap::const_iterator i = pending_requests_.begin();
80       i != pending_requests_.end();
81       ++i) {
82    web_data_service_->CancelRequest(i->first);
83    delete i->second.form;
84  }
85}
86
87void PasswordStoreWin::DBHandler::GetIE7Login(
88    const PasswordForm& form,
89    const PasswordStoreWin::ConsumerCallbackRunner& callback_runner) {
90  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
91  IE7PasswordInfo info;
92  info.url_hash =
93      ie7_password::GetUrlHash(base::UTF8ToWide(form.origin.spec()));
94  WebDataService::Handle handle = web_data_service_->GetIE7Login(info, this);
95  pending_requests_[handle] =
96      RequestInfo(new PasswordForm(form), callback_runner);
97}
98
99std::vector<PasswordForm*> PasswordStoreWin::DBHandler::GetIE7Results(
100    const WDTypedResult *result,
101    const PasswordForm& form) {
102  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
103  std::vector<PasswordForm*> matching_forms;
104
105  const WDResult<IE7PasswordInfo>* r =
106      static_cast<const WDResult<IE7PasswordInfo>*>(result);
107  IE7PasswordInfo info = r->GetValue();
108
109  if (!info.encrypted_data.empty()) {
110    // We got a result.
111    // Delete the entry. If it's good we will add it to the real saved password
112    // table.
113    web_data_service_->RemoveIE7Login(info);
114    std::vector<ie7_password::DecryptedCredentials> credentials;
115    std::wstring url = base::ASCIIToWide(form.origin.spec());
116    if (ie7_password::DecryptPasswords(url,
117                                       info.encrypted_data,
118                                       &credentials)) {
119      for (size_t i = 0; i < credentials.size(); ++i) {
120        PasswordForm* autofill = new PasswordForm();
121        autofill->username_value = credentials[i].username;
122        autofill->password_value = credentials[i].password;
123        autofill->signon_realm = form.signon_realm;
124        autofill->origin = form.origin;
125        autofill->preferred = true;
126        autofill->ssl_valid = form.origin.SchemeIsSecure();
127        autofill->date_created = info.date_created;
128
129        matching_forms.push_back(autofill);
130        // Add this PasswordForm to the saved password table. We're on the DB
131        // thread already, so we use AddLoginImpl.
132        password_store_->AddLoginImpl(*autofill);
133      }
134    }
135  }
136  return matching_forms;
137}
138
139void PasswordStoreWin::DBHandler::OnWebDataServiceRequestDone(
140    WebDataService::Handle handle,
141    const WDTypedResult* result) {
142  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
143
144  PendingRequestMap::iterator i = pending_requests_.find(handle);
145  DCHECK(i != pending_requests_.end());
146
147  scoped_ptr<PasswordForm> form(i->second.form);
148  PasswordStoreWin::ConsumerCallbackRunner callback_runner(
149      i->second.callback_runner);
150  pending_requests_.erase(i);
151
152  if (!result) {
153    // The WDS returns NULL if it is shutting down. Run callback with empty
154    // result.
155    callback_runner.Run(std::vector<autofill::PasswordForm*>());
156    return;
157  }
158
159  DCHECK_EQ(PASSWORD_IE7_RESULT, result->GetType());
160  std::vector<autofill::PasswordForm*> matched_forms =
161      GetIE7Results(result, *form);
162
163  callback_runner.Run(matched_forms);
164}
165
166PasswordStoreWin::PasswordStoreWin(
167    scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
168    scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
169    LoginDatabase* login_database,
170    WebDataService* web_data_service)
171    : PasswordStoreDefault(main_thread_runner,
172                           db_thread_runner,
173                           login_database) {
174  db_handler_.reset(new DBHandler(web_data_service, this));
175}
176
177PasswordStoreWin::~PasswordStoreWin() {
178}
179
180void PasswordStoreWin::ShutdownOnDBThread() {
181  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
182  db_handler_.reset();
183}
184
185void PasswordStoreWin::Shutdown() {
186  BrowserThread::PostTask(
187      BrowserThread::DB, FROM_HERE,
188      base::Bind(&PasswordStoreWin::ShutdownOnDBThread, this));
189  PasswordStoreDefault::Shutdown();
190}
191
192void PasswordStoreWin::GetIE7LoginIfNecessary(
193    const PasswordForm& form,
194    const ConsumerCallbackRunner& callback_runner,
195    const std::vector<autofill::PasswordForm*>& matched_forms) {
196  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
197  if (matched_forms.empty() && db_handler_.get()) {
198    db_handler_->GetIE7Login(form, callback_runner);
199  } else {
200    // No need to get IE7 login.
201    callback_runner.Run(matched_forms);
202  }
203}
204
205void PasswordStoreWin::GetLoginsImpl(
206    const PasswordForm& form,
207    AuthorizationPromptPolicy prompt_policy,
208    const ConsumerCallbackRunner& callback_runner) {
209  ConsumerCallbackRunner get_ie7_login =
210      base::Bind(&PasswordStoreWin::GetIE7LoginIfNecessary,
211                 this, form, callback_runner);
212  PasswordStoreDefault::GetLoginsImpl(form, prompt_policy, get_ie7_login);
213}
214