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