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