Home | History | Annotate | Download | only in password_manager
      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/browser/webdata/logins_table.h"
     21 #include "chrome/browser/webdata/web_data_service.h"
     22 #include "chrome/test/base/testing_profile.h"
     23 #include "components/os_crypt/ie7_password_win.h"
     24 #include "components/password_manager/core/browser/password_form_data.h"
     25 #include "components/password_manager/core/browser/password_store_consumer.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 
     32 using autofill::PasswordForm;
     33 using base::WaitableEvent;
     34 using content::BrowserThread;
     35 using password_manager::LoginDatabase;
     36 using password_manager::ContainsAllPasswordForms;
     37 using password_manager::PasswordFormData;
     38 using password_manager::PasswordStore;
     39 using password_manager::PasswordStoreConsumer;
     40 using testing::_;
     41 using testing::DoAll;
     42 using testing::WithArg;
     43 
     44 namespace {
     45 
     46 class MockPasswordStoreConsumer : public PasswordStoreConsumer {
     47  public:
     48   MOCK_METHOD1(OnGetPasswordStoreResults,
     49                void(const std::vector<autofill::PasswordForm*>&));
     50 };
     51 
     52 class MockWebDataServiceConsumer : public WebDataServiceConsumer {
     53 public:
     54   MOCK_METHOD2(OnWebDataServiceRequestDone,
     55                void(WebDataService::Handle, const WDTypedResult*));
     56 };
     57 
     58 }  // anonymous namespace
     59 
     60 typedef std::vector<PasswordForm*> VectorOfForms;
     61 
     62 class 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 WebDataService(wdbs_,
    129                               WebDataServiceBase::ProfileErrorCallback());
    130     wds_->Init();
    131   }
    132 
    133   virtual void TearDown() {
    134     if (store_)
    135       store_->Shutdown();
    136     wds_->ShutdownOnUIThread();
    137     wdbs_->ShutdownDatabase();
    138     wds_ = NULL;
    139     wdbs_ = NULL;
    140     base::WaitableEvent done(false, false);
    141     BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
    142         base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
    143     done.Wait();
    144     base::MessageLoop::current()->PostTask(FROM_HERE,
    145                                            base::MessageLoop::QuitClosure());
    146     base::MessageLoop::current()->Run();
    147     db_thread_.Stop();
    148   }
    149 
    150   PasswordStoreWin* CreatePasswordStore() {
    151     return new PasswordStoreWin(
    152         base::MessageLoopProxy::current(),
    153         BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
    154         login_db_.release(),
    155         wds_.get());
    156   }
    157 
    158   base::MessageLoopForUI message_loop_;
    159   content::TestBrowserThread ui_thread_;
    160   // PasswordStore, WDS schedule work on this thread.
    161   content::TestBrowserThread db_thread_;
    162 
    163   base::ScopedTempDir temp_dir_;
    164   scoped_ptr<TestingProfile> profile_;
    165   scoped_ptr<LoginDatabase> login_db_;
    166   scoped_refptr<WebDataService> wds_;
    167   scoped_refptr<WebDatabaseService> wdbs_;
    168   scoped_refptr<PasswordStore> store_;
    169 };
    170 
    171 ACTION(STLDeleteElements0) {
    172   STLDeleteContainerPointers(arg0.begin(), arg0.end());
    173 }
    174 
    175 ACTION(QuitUIMessageLoop) {
    176   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    177   base::MessageLoop::current()->Quit();
    178 }
    179 
    180 MATCHER(EmptyWDResult, "") {
    181   return static_cast<const WDResult<std::vector<PasswordForm*> >*>(
    182       arg)->GetValue().empty();
    183 }
    184 
    185 // Hangs flakily, http://crbug.com/71385.
    186 TEST_F(PasswordStoreWinTest, DISABLED_ConvertIE7Login) {
    187   IE7PasswordInfo password_info;
    188   ASSERT_TRUE(CreateIE7PasswordInfo(L"http://example.com/origin",
    189                                     base::Time::FromDoubleT(1),
    190                                     &password_info));
    191   // Verify the URL hash
    192   ASSERT_EQ(L"39471418FF5453FEEB3731E382DEB5D53E14FAF9B5",
    193             password_info.url_hash);
    194 
    195   // This IE7 password will be retrieved by the GetLogins call.
    196   wds_->AddIE7Login(password_info);
    197 
    198   // The WDS schedules tasks to run on the DB thread so we schedule yet another
    199   // task to notify us that it's safe to carry on with the test.
    200   WaitableEvent done(false, false);
    201   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
    202       base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
    203   done.Wait();
    204 
    205   store_ = CreatePasswordStore();
    206   EXPECT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare()));
    207 
    208   MockPasswordStoreConsumer consumer;
    209 
    210   // Make sure we quit the MessageLoop even if the test fails.
    211   ON_CALL(consumer, OnGetPasswordStoreResults(_))
    212       .WillByDefault(QuitUIMessageLoop());
    213 
    214   PasswordFormData form_data = {
    215     PasswordForm::SCHEME_HTML,
    216     "http://example.com/",
    217     "http://example.com/origin",
    218     "http://example.com/action",
    219     L"submit_element",
    220     L"username_element",
    221     L"password_element",
    222     L"",
    223     L"",
    224     true, false, 1,
    225   };
    226   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
    227 
    228   // The returned form will not have 'action' or '*_element' fields set. This
    229   // is because credentials imported from IE don't have this information.
    230   PasswordFormData expected_form_data = {
    231     PasswordForm::SCHEME_HTML,
    232     "http://example.com/",
    233     "http://example.com/origin",
    234     "",
    235     L"",
    236     L"",
    237     L"",
    238     L"abcdefgh",
    239     L"abcdefghijkl",
    240     true, false, 1,
    241   };
    242   std::vector<PasswordForm*> forms;
    243   forms.push_back(CreatePasswordFormFromData(expected_form_data));
    244 
    245   // The IE7 password should be returned.
    246   EXPECT_CALL(consumer,
    247               OnGetPasswordStoreResults(ContainsAllPasswordForms(forms)))
    248       .WillOnce(QuitUIMessageLoop());
    249 
    250   store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &consumer);
    251   base::MessageLoop::current()->Run();
    252 
    253   STLDeleteElements(&forms);
    254 }
    255 
    256 // Crashy.  http://crbug.com/86558
    257 TEST_F(PasswordStoreWinTest, DISABLED_OutstandingWDSQueries) {
    258   store_ = CreatePasswordStore();
    259   EXPECT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare()));
    260 
    261   PasswordFormData form_data = {
    262     PasswordForm::SCHEME_HTML,
    263     "http://example.com/",
    264     "http://example.com/origin",
    265     "http://example.com/action",
    266     L"submit_element",
    267     L"username_element",
    268     L"password_element",
    269     L"",
    270     L"",
    271     true, false, 1,
    272   };
    273   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
    274 
    275   MockPasswordStoreConsumer consumer;
    276   store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &consumer);
    277 
    278   // Release the PSW and the WDS before the query can return.
    279   store_->Shutdown();
    280   store_ = NULL;
    281   wds_ = NULL;
    282 
    283   base::MessageLoop::current()->RunUntilIdle();
    284 }
    285 
    286 // Hangs flakily, see http://crbug.com/43836.
    287 TEST_F(PasswordStoreWinTest, DISABLED_MultipleWDSQueriesOnDifferentThreads) {
    288   IE7PasswordInfo password_info;
    289   ASSERT_TRUE(CreateIE7PasswordInfo(L"http://example.com/origin",
    290                                     base::Time::FromDoubleT(1),
    291                                     &password_info));
    292   wds_->AddIE7Login(password_info);
    293 
    294   // The WDS schedules tasks to run on the DB thread so we schedule yet another
    295   // task to notify us that it's safe to carry on with the test.
    296   WaitableEvent done(false, false);
    297   BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
    298       base::Bind(&WaitableEvent::Signal, base::Unretained(&done)));
    299   done.Wait();
    300 
    301   store_ = CreatePasswordStore();
    302   EXPECT_TRUE(store_->Init(syncer::SyncableService::StartSyncFlare()));
    303 
    304   MockPasswordStoreConsumer password_consumer;
    305   // Make sure we quit the MessageLoop even if the test fails.
    306   ON_CALL(password_consumer, OnGetPasswordStoreResults(_))
    307       .WillByDefault(QuitUIMessageLoop());
    308 
    309   PasswordFormData form_data = {
    310     PasswordForm::SCHEME_HTML,
    311     "http://example.com/",
    312     "http://example.com/origin",
    313     "http://example.com/action",
    314     L"submit_element",
    315     L"username_element",
    316     L"password_element",
    317     L"",
    318     L"",
    319     true, false, 1,
    320   };
    321   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
    322 
    323   PasswordFormData expected_form_data = {
    324     PasswordForm::SCHEME_HTML,
    325     "http://example.com/",
    326     "http://example.com/origin",
    327     "http://example.com/action",
    328     L"submit_element",
    329     L"username_element",
    330     L"password_element",
    331     L"abcdefgh",
    332     L"abcdefghijkl",
    333     true, false, 1,
    334   };
    335   std::vector<PasswordForm*> forms;
    336   forms.push_back(CreatePasswordFormFromData(expected_form_data));
    337 
    338   // The IE7 password should be returned.
    339   EXPECT_CALL(password_consumer,
    340               OnGetPasswordStoreResults(ContainsAllPasswordForms(forms)))
    341       .WillOnce(QuitUIMessageLoop());
    342 
    343   store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &password_consumer);
    344 
    345   MockWebDataServiceConsumer wds_consumer;
    346 
    347   EXPECT_CALL(wds_consumer,
    348               OnWebDataServiceRequestDone(_, _))
    349       .WillOnce(QuitUIMessageLoop());
    350 
    351   wds_->GetIE7Login(password_info, &wds_consumer);
    352 
    353   // Run the MessageLoop twice: once for the GetIE7Login that PasswordStoreWin
    354   // schedules on the DB thread and once for the one we just scheduled on the UI
    355   // thread.
    356   base::MessageLoop::current()->Run();
    357   base::MessageLoop::current()->Run();
    358 
    359   STLDeleteElements(&forms);
    360 }
    361 
    362 TEST_F(PasswordStoreWinTest, EmptyLogins) {
    363   store_ = CreatePasswordStore();
    364   store_->Init(syncer::SyncableService::StartSyncFlare());
    365 
    366   PasswordFormData form_data = {
    367     PasswordForm::SCHEME_HTML,
    368     "http://example.com/",
    369     "http://example.com/origin",
    370     "http://example.com/action",
    371     L"submit_element",
    372     L"username_element",
    373     L"password_element",
    374     L"",
    375     L"",
    376     true, false, 1,
    377   };
    378   scoped_ptr<PasswordForm> form(CreatePasswordFormFromData(form_data));
    379 
    380   MockPasswordStoreConsumer consumer;
    381 
    382   // Make sure we quit the MessageLoop even if the test fails.
    383   ON_CALL(consumer, OnGetPasswordStoreResults(_))
    384       .WillByDefault(QuitUIMessageLoop());
    385 
    386   VectorOfForms expect_none;
    387   // expect that we get no results;
    388   EXPECT_CALL(consumer,
    389               OnGetPasswordStoreResults(ContainsAllPasswordForms(expect_none)))
    390       .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
    391 
    392   store_->GetLogins(*form, PasswordStore::DISALLOW_PROMPT, &consumer);
    393   base::MessageLoop::current()->Run();
    394 }
    395 
    396 TEST_F(PasswordStoreWinTest, EmptyBlacklistLogins) {
    397   store_ = CreatePasswordStore();
    398   store_->Init(syncer::SyncableService::StartSyncFlare());
    399 
    400   MockPasswordStoreConsumer consumer;
    401 
    402   // Make sure we quit the MessageLoop even if the test fails.
    403   ON_CALL(consumer, OnGetPasswordStoreResults(_))
    404       .WillByDefault(QuitUIMessageLoop());
    405 
    406   VectorOfForms expect_none;
    407   // expect that we get no results;
    408   EXPECT_CALL(
    409       consumer,
    410       OnGetPasswordStoreResults(ContainsAllPasswordForms(expect_none)))
    411       .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
    412 
    413   store_->GetBlacklistLogins(&consumer);
    414   base::MessageLoop::current()->Run();
    415 }
    416 
    417 TEST_F(PasswordStoreWinTest, EmptyAutofillableLogins) {
    418   store_ = CreatePasswordStore();
    419   store_->Init(syncer::SyncableService::StartSyncFlare());
    420 
    421   MockPasswordStoreConsumer consumer;
    422 
    423   // Make sure we quit the MessageLoop even if the test fails.
    424   ON_CALL(consumer, OnGetPasswordStoreResults(_))
    425       .WillByDefault(QuitUIMessageLoop());
    426 
    427   VectorOfForms expect_none;
    428   // expect that we get no results;
    429   EXPECT_CALL(
    430       consumer,
    431       OnGetPasswordStoreResults(ContainsAllPasswordForms(expect_none)))
    432       .WillOnce(DoAll(WithArg<0>(STLDeleteElements0()), QuitUIMessageLoop()));
    433 
    434   store_->GetAutofillableLogins(&consumer);
    435   base::MessageLoop::current()->Run();
    436 }
    437