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 <windows.h>
6#include <wincrypt.h>
7#include <string>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/files/scoped_temp_dir.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/message_loop/message_loop.h"
15#include "base/prefs/pref_service.h"
16#include "base/stl_util.h"
17#include "base/synchronization/waitable_event.h"
18#include "base/time/time.h"
19#include "chrome/browser/password_manager/password_store_win.h"
20#include "chrome/test/base/testing_profile.h"
21#include "components/os_crypt/ie7_password_win.h"
22#include "components/password_manager/core/browser/password_form_data.h"
23#include "components/password_manager/core/browser/password_store_consumer.h"
24#include "components/password_manager/core/browser/webdata/logins_table.h"
25#include "components/password_manager/core/browser/webdata/password_web_data_service_win.h"
26#include "components/password_manager/core/common/password_manager_pref_names.h"
27#include "components/webdata/common/web_database_service.h"
28#include "content/public/test/test_browser_thread.h"
29#include "testing/gmock/include/gmock/gmock.h"
30#include "testing/gtest/include/gtest/gtest.h"
31
32using autofill::PasswordForm;
33using base::WaitableEvent;
34using content::BrowserThread;
35using password_manager::LoginDatabase;
36using password_manager::ContainsAllPasswordForms;
37using password_manager::PasswordFormData;
38using password_manager::PasswordStore;
39using password_manager::PasswordStoreConsumer;
40using testing::_;
41using testing::DoAll;
42using testing::WithArg;
43
44namespace {
45
46class MockPasswordStoreConsumer : public PasswordStoreConsumer {
47 public:
48  MOCK_METHOD1(OnGetPasswordStoreResults,
49               void(const std::vector<autofill::PasswordForm*>&));
50};
51
52class MockWebDataServiceConsumer : public WebDataServiceConsumer {
53public:
54  MOCK_METHOD2(OnWebDataServiceRequestDone,
55               void(PasswordWebDataService::Handle, const WDTypedResult*));
56};
57
58}  // anonymous namespace
59
60typedef std::vector<PasswordForm*> VectorOfForms;
61
62class PasswordStoreWinTest : public testing::Test {
63 protected:
64  PasswordStoreWinTest()
65      : ui_thread_(BrowserThread::UI, &message_loop_),
66        db_thread_(BrowserThread::DB) {
67  }
68
69  bool CreateIE7PasswordInfo(const std::wstring& url, const base::Time& created,
70                             IE7PasswordInfo* info) {
71    // Copied from chrome/browser/importer/importer_unittest.cc
72    // The username is "abcdefgh" and the password "abcdefghijkl".
73    unsigned char data[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x2c\x00\x00\x00"
74                           "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00"
75                           "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00"
76                           "\x00\x00\x00\x00\x4e\xfa\x67\x76\x22\x94\xc8\x01"
77                           "\x08\x00\x00\x00\x12\x00\x00\x00\x4e\xfa\x67\x76"
78                           "\x22\x94\xc8\x01\x0c\x00\x00\x00\x61\x00\x62\x00"
79                           "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00"
80                           "\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00"
81                           "\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00"
82                           "\x6c\x00\x00\x00";
83    DATA_BLOB input = {0};
84    DATA_BLOB url_key = {0};
85    DATA_BLOB output = {0};
86
87    input.pbData = data;
88    input.cbData = sizeof(data);
89
90    url_key.pbData = reinterpret_cast<unsigned char*>(
91        const_cast<wchar_t*>(url.data()));
92    url_key.cbData = static_cast<DWORD>((url.size() + 1) *
93                                        sizeof(std::wstring::value_type));
94
95    if (!CryptProtectData(&input, NULL, &url_key, NULL, NULL,
96                          CRYPTPROTECT_UI_FORBIDDEN, &output))
97      return false;
98
99    std::vector<unsigned char> encrypted_data;
100    encrypted_data.resize(output.cbData);
101    memcpy(&encrypted_data.front(), output.pbData, output.cbData);
102
103    LocalFree(output.pbData);
104
105    info->url_hash = ie7_password::GetUrlHash(url);
106    info->encrypted_data = encrypted_data;
107    info->date_created = created;
108
109    return true;
110  }
111
112  virtual void SetUp() {
113    ASSERT_TRUE(db_thread_.Start());
114    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
115
116    profile_.reset(new TestingProfile());
117
118    login_db_.reset(new LoginDatabase());
119    ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append(
120        FILE_PATH_LITERAL("login_test"))));
121    base::FilePath path = temp_dir_.path().AppendASCII("web_data_test");
122    wdbs_ = new WebDatabaseService(path,
123        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
124        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB));
125    // Need to add at least one table so the database gets created.
126    wdbs_->AddTable(scoped_ptr<WebDatabaseTable>(new LoginsTable()));
127    wdbs_->LoadDatabase();
128    wds_ = new PasswordWebDataService(
129        wdbs_,
130        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
131        WebDataServiceBase::ProfileErrorCallback());
132    wds_->Init();
133  }
134
135  virtual void TearDown() {
136    if (store_)
137      store_->Shutdown();
138    wds_->ShutdownOnUIThread();
139    wdbs_->ShutdownDatabase();
140    wds_ = NULL;
141    wdbs_ = NULL;
142    base::WaitableEvent done(false, false);
143    BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
144        base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
145    done.Wait();
146    base::MessageLoop::current()->PostTask(FROM_HERE,
147                                           base::MessageLoop::QuitClosure());
148    base::MessageLoop::current()->Run();
149    db_thread_.Stop();
150  }
151
152  PasswordStoreWin* CreatePasswordStore() {
153    return new PasswordStoreWin(
154        base::MessageLoopProxy::current(),
155        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
156        login_db_.release(),
157        wds_.get());
158  }
159
160  base::MessageLoopForUI message_loop_;
161  content::TestBrowserThread ui_thread_;
162  // PasswordStore, WDS schedule work on this thread.
163  content::TestBrowserThread db_thread_;
164
165  base::ScopedTempDir temp_dir_;
166  scoped_ptr<TestingProfile> profile_;
167  scoped_ptr<LoginDatabase> login_db_;
168  scoped_refptr<PasswordWebDataService> wds_;
169  scoped_refptr<WebDatabaseService> wdbs_;
170  scoped_refptr<PasswordStore> store_;
171};
172
173ACTION(STLDeleteElements0) {
174  STLDeleteContainerPointers(arg0.begin(), arg0.end());
175}
176
177ACTION(QuitUIMessageLoop) {
178  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
179  base::MessageLoop::current()->Quit();
180}
181
182MATCHER(EmptyWDResult, "") {
183  return static_cast<const WDResult<std::vector<PasswordForm*> >*>(
184      arg)->GetValue().empty();
185}
186
187// Hangs flakily, http://crbug.com/71385.
188TEST_F(PasswordStoreWinTest, DISABLED_ConvertIE7Login) {
189  IE7PasswordInfo password_info;
190  ASSERT_TRUE(CreateIE7PasswordInfo(L"http://example.com/origin",
191                                    base::Time::FromDoubleT(1),
192                                    &password_info));
193  // Verify the URL hash
194  ASSERT_EQ(L"39471418FF5453FEEB3731E382DEB5D53E14FAF9B5",
195            password_info.url_hash);
196
197  // This IE7 password will be retrieved by the GetLogins call.
198  wds_->AddIE7Login(password_info);
199
200  // The WDS schedules tasks to run on the DB thread so we schedule yet another
201  // task to notify us that it's safe to carry on with the test.
202  WaitableEvent done(false, false);
203  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
204      base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
205  done.Wait();
206
207  store_ = CreatePasswordStore();
208  EXPECT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare(), ""));
209
210  MockPasswordStoreConsumer consumer;
211
212  // Make sure we quit the MessageLoop even if the test fails.
213  ON_CALL(consumer, OnGetPasswordStoreResults(_))
214      .WillByDefault(QuitUIMessageLoop());
215
216  PasswordFormData form_data = {
217    PasswordForm::SCHEME_HTML,
218    "http://example.com/",
219    "http://example.com/origin",
220    "http://example.com/action",
221    L"submit_element",
222    L"username_element",
223    L"password_element",
224    L"",
225    L"",
226    true, false, 1,
227  };
228  scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
229
230  // The returned form will not have 'action' or '*_element' fields set. This
231  // is because credentials imported from IE don't have this information.
232  PasswordFormData expected_form_data = {
233    PasswordForm::SCHEME_HTML,
234    "http://example.com/",
235    "http://example.com/origin",
236    "",
237    L"",
238    L"",
239    L"",
240    L"abcdefgh",
241    L"abcdefghijkl",
242    true, false, 1,
243  };
244  std::vector<PasswordForm*> forms;
245  forms.push_back(CreatePasswordFormFromData(expected_form_data));
246
247  // The IE7 password should be returned.
248  EXPECT_CALL(consumer,
249              OnGetPasswordStoreResults(ContainsAllPasswordForms(forms)))
250      .WillOnce(QuitUIMessageLoop());
251
252  store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &consumer);
253  base::MessageLoop::current()->Run();
254
255  STLDeleteElements(&forms);
256}
257
258// Crashy.  http://crbug.com/86558
259TEST_F(PasswordStoreWinTest, DISABLED_OutstandingWDSQueries) {
260  store_ = CreatePasswordStore();
261  EXPECT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare(), ""));
262
263  PasswordFormData form_data = {
264    PasswordForm::SCHEME_HTML,
265    "http://example.com/",
266    "http://example.com/origin",
267    "http://example.com/action",
268    L"submit_element",
269    L"username_element",
270    L"password_element",
271    L"",
272    L"",
273    true, false, 1,
274  };
275  scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
276
277  MockPasswordStoreConsumer consumer;
278  store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &consumer);
279
280  // Release the PSW and the WDS before the query can return.
281  store_->Shutdown();
282  store_ = NULL;
283  wds_ = NULL;
284
285  base::MessageLoop::current()->RunUntilIdle();
286}
287
288// Hangs flakily, see http://crbug.com/43836.
289TEST_F(PasswordStoreWinTest, DISABLED_MultipleWDSQueriesOnDifferentThreads) {
290  IE7PasswordInfo password_info;
291  ASSERT_TRUE(CreateIE7PasswordInfo(L"http://example.com/origin",
292                                    base::Time::FromDoubleT(1),
293                                    &password_info));
294  wds_->AddIE7Login(password_info);
295
296  // The WDS schedules tasks to run on the DB thread so we schedule yet another
297  // task to notify us that it's safe to carry on with the test.
298  WaitableEvent done(false, false);
299  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
300      base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
301  done.Wait();
302
303  store_ = CreatePasswordStore();
304  EXPECT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare(), ""));
305
306  MockPasswordStoreConsumer password_consumer;
307  // Make sure we quit the MessageLoop even if the test fails.
308  ON_CALL(password_consumer, OnGetPasswordStoreResults(_))
309      .WillByDefault(QuitUIMessageLoop());
310
311  PasswordFormData form_data = {
312    PasswordForm::SCHEME_HTML,
313    "http://example.com/",
314    "http://example.com/origin",
315    "http://example.com/action",
316    L"submit_element",
317    L"username_element",
318    L"password_element",
319    L"",
320    L"",
321    true, false, 1,
322  };
323  scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
324
325  PasswordFormData expected_form_data = {
326    PasswordForm::SCHEME_HTML,
327    "http://example.com/",
328    "http://example.com/origin",
329    "http://example.com/action",
330    L"submit_element",
331    L"username_element",
332    L"password_element",
333    L"abcdefgh",
334    L"abcdefghijkl",
335    true, false, 1,
336  };
337  std::vector<PasswordForm*> forms;
338  forms.push_back(CreatePasswordFormFromData(expected_form_data));
339
340  // The IE7 password should be returned.
341  EXPECT_CALL(password_consumer,
342              OnGetPasswordStoreResults(ContainsAllPasswordForms(forms)))
343      .WillOnce(QuitUIMessageLoop());
344
345  store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &password_consumer);
346
347  MockWebDataServiceConsumer wds_consumer;
348
349  EXPECT_CALL(wds_consumer,
350              OnWebDataServiceRequestDone(_, _))
351      .WillOnce(QuitUIMessageLoop());
352
353  wds_->GetIE7Login(password_info, &wds_consumer);
354
355  // Run the MessageLoop twice: once for the GetIE7Login that PasswordStoreWin
356  // schedules on the DB thread and once for the one we just scheduled on the UI
357  // thread.
358  base::MessageLoop::current()->Run();
359  base::MessageLoop::current()->Run();
360
361  STLDeleteElements(&forms);
362}
363
364TEST_F(PasswordStoreWinTest, EmptyLogins) {
365  store_ = CreatePasswordStore();
366  store_->Init(syncer::SyncableService::StartSyncFlare(), "");
367
368  PasswordFormData form_data = {
369    PasswordForm::SCHEME_HTML,
370    "http://example.com/",
371    "http://example.com/origin",
372    "http://example.com/action",
373    L"submit_element",
374    L"username_element",
375    L"password_element",
376    L"",
377    L"",
378    true, false, 1,
379  };
380  scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
381
382  MockPasswordStoreConsumer consumer;
383
384  // Make sure we quit the MessageLoop even if the test fails.
385  ON_CALL(consumer, OnGetPasswordStoreResults(_))
386      .WillByDefault(QuitUIMessageLoop());
387
388  VectorOfForms expect_none;
389  // expect that we get no results;
390  EXPECT_CALL(consumer,
391              OnGetPasswordStoreResults(ContainsAllPasswordForms(expect_none)))
392      .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
393
394  store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &consumer);
395  base::MessageLoop::current()->Run();
396}
397
398TEST_F(PasswordStoreWinTest, EmptyBlacklistLogins) {
399  store_ = CreatePasswordStore();
400  store_->Init(syncer::SyncableService::StartSyncFlare(), "");
401
402  MockPasswordStoreConsumer consumer;
403
404  // Make sure we quit the MessageLoop even if the test fails.
405  ON_CALL(consumer, OnGetPasswordStoreResults(_))
406      .WillByDefault(QuitUIMessageLoop());
407
408  VectorOfForms expect_none;
409  // expect that we get no results;
410  EXPECT_CALL(
411      consumer,
412      OnGetPasswordStoreResults(ContainsAllPasswordForms(expect_none)))
413      .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
414
415  store_->GetBlacklistLogins(&consumer);
416  base::MessageLoop::current()->Run();
417}
418
419TEST_F(PasswordStoreWinTest, EmptyAutofillableLogins) {
420  store_ = CreatePasswordStore();
421  store_->Init(syncer::SyncableService::StartSyncFlare(), "");
422
423  MockPasswordStoreConsumer consumer;
424
425  // Make sure we quit the MessageLoop even if the test fails.
426  ON_CALL(consumer, OnGetPasswordStoreResults(_))
427      .WillByDefault(QuitUIMessageLoop());
428
429  VectorOfForms expect_none;
430  // expect that we get no results;
431  EXPECT_CALL(
432      consumer,
433      OnGetPasswordStoreResults(ContainsAllPasswordForms(expect_none)))
434      .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
435
436  store_->GetAutofillableLogins(&consumer);
437  base::MessageLoop::current()->Run();
438}
439