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