OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/password_manager/password_store_mac.h" | 5 #include "chrome/browser/password_manager/password_store_mac.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/files/scoped_temp_dir.h" | 8 #include "base/files/scoped_temp_dir.h" |
9 #include "base/scoped_observer.h" | 9 #include "base/scoped_observer.h" |
10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
(...skipping 1155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1166 } | 1166 } |
1167 | 1167 |
1168 #pragma mark - | 1168 #pragma mark - |
1169 | 1169 |
1170 class PasswordStoreMacTest : public testing::Test { | 1170 class PasswordStoreMacTest : public testing::Test { |
1171 public: | 1171 public: |
1172 PasswordStoreMacTest() : ui_thread_(BrowserThread::UI, &message_loop_) {} | 1172 PasswordStoreMacTest() : ui_thread_(BrowserThread::UI, &message_loop_) {} |
1173 | 1173 |
1174 void SetUp() override { | 1174 void SetUp() override { |
1175 ASSERT_TRUE(db_dir_.CreateUniqueTempDir()); | 1175 ASSERT_TRUE(db_dir_.CreateUniqueTempDir()); |
| 1176 histogram_tester_.reset(new base::HistogramTester); |
1176 | 1177 |
1177 // Ensure that LoginDatabase will use the mock keychain if it needs to | 1178 // Ensure that LoginDatabase will use the mock keychain if it needs to |
1178 // encrypt/decrypt a password. | 1179 // encrypt/decrypt a password. |
1179 OSCrypt::UseMockKeychain(true); | 1180 OSCrypt::UseMockKeychain(true); |
1180 login_db_.reset( | 1181 login_db_.reset( |
1181 new password_manager::LoginDatabase(test_login_db_file_path())); | 1182 new password_manager::LoginDatabase(test_login_db_file_path())); |
1182 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread")); | 1183 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread")); |
1183 ASSERT_TRUE(thread_->Start()); | 1184 ASSERT_TRUE(thread_->Start()); |
1184 ASSERT_TRUE(thread_->task_runner()->PostTask( | 1185 ASSERT_TRUE(thread_->task_runner()->PostTask( |
1185 FROM_HERE, base::Bind(&PasswordStoreMacTest::InitLoginDatabase, | 1186 FROM_HERE, base::Bind(&PasswordStoreMacTest::InitLoginDatabase, |
1186 base::Unretained(login_db_.get())))); | 1187 base::Unretained(login_db_.get())))); |
1187 CreateAndInitPasswordStore(login_db_.get()); | 1188 CreateAndInitPasswordStore(login_db_.get()); |
1188 // Make sure deferred initialization is performed before some tests start | 1189 // Make sure deferred initialization is performed before some tests start |
1189 // accessing the |login_db| directly. | 1190 // accessing the |login_db| directly. |
1190 FinishAsyncProcessing(); | 1191 FinishAsyncProcessing(); |
1191 } | 1192 } |
1192 | 1193 |
1193 void TearDown() override { | 1194 void TearDown() override { |
1194 ClosePasswordStore(); | 1195 ClosePasswordStore(); |
1195 thread_.reset(); | 1196 thread_.reset(); |
1196 login_db_.reset(); | 1197 login_db_.reset(); |
| 1198 crypto::MockAppleKeychain::set_locked(false); |
1197 // Whatever a test did, PasswordStoreMac stores only empty password values | 1199 // Whatever a test did, PasswordStoreMac stores only empty password values |
1198 // in LoginDatabase. The empty valus do not require encryption and therefore | 1200 // in LoginDatabase. The empty valus do not require encryption and therefore |
1199 // OSCrypt shouldn't call the Keychain. The histogram doesn't cover the | 1201 // OSCrypt shouldn't call the Keychain. The histogram doesn't cover the |
1200 // internet passwords. | 1202 // internet passwords. |
1201 EXPECT_FALSE(histogram_tester_.GetHistogramSamplesSinceCreation( | 1203 if (histogram_tester_) { |
1202 "OSX.Keychain.Access")); | 1204 scoped_ptr<base::HistogramSamples> samples = |
| 1205 histogram_tester_->GetHistogramSamplesSinceCreation( |
| 1206 "OSX.Keychain.Access"); |
| 1207 EXPECT_TRUE(!samples || samples->TotalCount() == 0); |
| 1208 } |
1203 } | 1209 } |
1204 | 1210 |
1205 static void InitLoginDatabase(password_manager::LoginDatabase* login_db) { | 1211 static void InitLoginDatabase(password_manager::LoginDatabase* login_db) { |
1206 ASSERT_TRUE(login_db->Init()); | 1212 ASSERT_TRUE(login_db->Init()); |
1207 } | 1213 } |
1208 | 1214 |
1209 void CreateAndInitPasswordStore(password_manager::LoginDatabase* login_db) { | 1215 void CreateAndInitPasswordStore(password_manager::LoginDatabase* login_db) { |
1210 store_ = new PasswordStoreMac( | 1216 store_ = new PasswordStoreMac( |
1211 base::ThreadTaskRunnerHandle::Get(), nullptr, | 1217 base::ThreadTaskRunnerHandle::Get(), nullptr, |
1212 make_scoped_ptr<AppleKeychain>(new MockAppleKeychain), login_db); | 1218 make_scoped_ptr<AppleKeychain>(new MockAppleKeychain), login_db); |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1297 | 1303 |
1298 protected: | 1304 protected: |
1299 base::MessageLoopForUI message_loop_; | 1305 base::MessageLoopForUI message_loop_; |
1300 content::TestBrowserThread ui_thread_; | 1306 content::TestBrowserThread ui_thread_; |
1301 // Thread that the synchronous methods are run on. | 1307 // Thread that the synchronous methods are run on. |
1302 scoped_ptr<base::Thread> thread_; | 1308 scoped_ptr<base::Thread> thread_; |
1303 | 1309 |
1304 base::ScopedTempDir db_dir_; | 1310 base::ScopedTempDir db_dir_; |
1305 scoped_ptr<password_manager::LoginDatabase> login_db_; | 1311 scoped_ptr<password_manager::LoginDatabase> login_db_; |
1306 scoped_refptr<PasswordStoreMac> store_; | 1312 scoped_refptr<PasswordStoreMac> store_; |
1307 base::HistogramTester histogram_tester_; | 1313 scoped_ptr<base::HistogramTester> histogram_tester_; |
1308 }; | 1314 }; |
1309 | 1315 |
1310 TEST_F(PasswordStoreMacTest, TestStoreUpdate) { | 1316 TEST_F(PasswordStoreMacTest, TestStoreUpdate) { |
1311 // Insert a password into both the database and the keychain. | 1317 // Insert a password into both the database and the keychain. |
1312 // This is done manually, rather than through store_->AddLogin, because the | 1318 // This is done manually, rather than through store_->AddLogin, because the |
1313 // Mock Keychain isn't smart enough to be able to support update generically, | 1319 // Mock Keychain isn't smart enough to be able to support update generically, |
1314 // so some.domain.com triggers special handling to test it that make inserting | 1320 // so some.domain.com triggers special handling to test it that make inserting |
1315 // fail. | 1321 // fail. |
1316 PasswordFormData joint_data = { | 1322 PasswordFormData joint_data = { |
1317 PasswordForm::SCHEME_HTML, "http://some.domain.com/", | 1323 PasswordForm::SCHEME_HTML, "http://some.domain.com/", |
(...skipping 433 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1751 // Verify that federated credentials can be stored, retrieved and deleted. | 1757 // Verify that federated credentials can be stored, retrieved and deleted. |
1752 TEST_F(PasswordStoreMacTest, StoringAndRetrievingFederatedCredentials) { | 1758 TEST_F(PasswordStoreMacTest, StoringAndRetrievingFederatedCredentials) { |
1753 PasswordForm form; | 1759 PasswordForm form; |
1754 form.signon_realm = "android://7x7IDboo8u9YKraUsbmVkuf1@net.rateflix.app/"; | 1760 form.signon_realm = "android://7x7IDboo8u9YKraUsbmVkuf1@net.rateflix.app/"; |
1755 form.federation_url = GURL(password_manager::kTestingFederationUrlSpec); | 1761 form.federation_url = GURL(password_manager::kTestingFederationUrlSpec); |
1756 form.username_value = base::UTF8ToUTF16("randomusername"); | 1762 form.username_value = base::UTF8ToUTF16("randomusername"); |
1757 form.password_value = base::UTF8ToUTF16(""); // No password. | 1763 form.password_value = base::UTF8ToUTF16(""); // No password. |
1758 | 1764 |
1759 VerifyCredentialLifecycle(form); | 1765 VerifyCredentialLifecycle(form); |
1760 } | 1766 } |
| 1767 |
| 1768 void CheckMigrationResult(PasswordStoreMac::MigrationResult expected_result, |
| 1769 PasswordStoreMac::MigrationResult result) { |
| 1770 EXPECT_EQ(expected_result, result); |
| 1771 QuitUIMessageLoop(); |
| 1772 } |
| 1773 |
| 1774 // Import the passwords from the Keychain to LoginDatabase. |
| 1775 TEST_F(PasswordStoreMacTest, ImportFromKeychain) { |
| 1776 PasswordForm form1; |
| 1777 form1.origin = GURL("http://accounts.google.com/LoginAuth"); |
| 1778 form1.signon_realm = "http://accounts.google.com/"; |
| 1779 form1.username_value = ASCIIToUTF16("my_username"); |
| 1780 form1.password_value = ASCIIToUTF16("my_password"); |
| 1781 |
| 1782 PasswordForm form2; |
| 1783 form2.origin = GURL("http://facebook.com/Login"); |
| 1784 form2.signon_realm = "http://facebook.com/"; |
| 1785 form2.username_value = ASCIIToUTF16("my_username"); |
| 1786 form2.password_value = ASCIIToUTF16("my_password"); |
| 1787 |
| 1788 PasswordForm blacklisted_form; |
| 1789 blacklisted_form.origin = GURL("http://badsite.com/Login"); |
| 1790 blacklisted_form.signon_realm = "http://badsite.com/"; |
| 1791 blacklisted_form.blacklisted_by_user = true; |
| 1792 |
| 1793 store()->AddLogin(form1); |
| 1794 store()->AddLogin(form2); |
| 1795 store()->AddLogin(blacklisted_form); |
| 1796 FinishAsyncProcessing(); |
| 1797 |
| 1798 ASSERT_TRUE(base::PostTaskAndReplyWithResult( |
| 1799 thread_->task_runner().get(), FROM_HERE, |
| 1800 base::Bind(&PasswordStoreMac::ImportFromKeychain, store()), |
| 1801 base::Bind(&CheckMigrationResult, PasswordStoreMac::MIGRATION_OK))); |
| 1802 FinishAsyncProcessing(); |
| 1803 |
| 1804 // The password should be stored in the database by now. |
| 1805 ScopedVector<PasswordForm> matching_items; |
| 1806 EXPECT_TRUE(login_db()->GetLogins(form1, &matching_items)); |
| 1807 ASSERT_EQ(1u, matching_items.size()); |
| 1808 EXPECT_EQ(form1, *matching_items[0]); |
| 1809 |
| 1810 EXPECT_TRUE(login_db()->GetLogins(form2, &matching_items)); |
| 1811 ASSERT_EQ(1u, matching_items.size()); |
| 1812 EXPECT_EQ(form2, *matching_items[0]); |
| 1813 |
| 1814 EXPECT_TRUE(login_db()->GetLogins(blacklisted_form, &matching_items)); |
| 1815 ASSERT_EQ(1u, matching_items.size()); |
| 1816 EXPECT_EQ(blacklisted_form, *matching_items[0]); |
| 1817 |
| 1818 // The passwords are encrypted using a key from the Keychain. |
| 1819 EXPECT_TRUE(histogram_tester_->GetHistogramSamplesSinceCreation( |
| 1820 "OSX.Keychain.Access")->TotalCount()); |
| 1821 histogram_tester_.reset(); |
| 1822 } |
| 1823 |
| 1824 // Import a federated credential while the Keychain is locked. |
| 1825 TEST_F(PasswordStoreMacTest, ImportFederatedFromLockedKeychain) { |
| 1826 crypto::MockAppleKeychain::set_locked(true); |
| 1827 PasswordForm form1; |
| 1828 form1.origin = GURL("http://example.com/Login"); |
| 1829 form1.signon_realm = "http://example.com/"; |
| 1830 form1.username_value = ASCIIToUTF16("my_username"); |
| 1831 form1.federation_url = GURL("https://accounts.google.com/"); |
| 1832 |
| 1833 store()->AddLogin(form1); |
| 1834 FinishAsyncProcessing(); |
| 1835 ASSERT_TRUE(base::PostTaskAndReplyWithResult( |
| 1836 thread_->task_runner().get(), FROM_HERE, |
| 1837 base::Bind(&PasswordStoreMac::ImportFromKeychain, store()), |
| 1838 base::Bind(&CheckMigrationResult, PasswordStoreMac::MIGRATION_OK))); |
| 1839 FinishAsyncProcessing(); |
| 1840 |
| 1841 ScopedVector<PasswordForm> matching_items; |
| 1842 EXPECT_TRUE(login_db()->GetLogins(form1, &matching_items)); |
| 1843 ASSERT_EQ(1u, matching_items.size()); |
| 1844 EXPECT_EQ(form1, *matching_items[0]); |
| 1845 } |
| 1846 |
| 1847 // Try to import while the Keychain is locked but the encryption key had been |
| 1848 // read earlier. |
| 1849 TEST_F(PasswordStoreMacTest, ImportFromLockedKeychainError) { |
| 1850 PasswordForm form1; |
| 1851 form1.origin = GURL("http://accounts.google.com/LoginAuth"); |
| 1852 form1.signon_realm = "http://accounts.google.com/"; |
| 1853 form1.username_value = ASCIIToUTF16("my_username"); |
| 1854 form1.password_value = ASCIIToUTF16("my_password"); |
| 1855 |
| 1856 store()->AddLogin(form1); |
| 1857 FinishAsyncProcessing(); |
| 1858 crypto::MockAppleKeychain::set_locked(true); |
| 1859 ASSERT_TRUE(base::PostTaskAndReplyWithResult( |
| 1860 thread_->task_runner().get(), FROM_HERE, |
| 1861 base::Bind(&PasswordStoreMac::ImportFromKeychain, store()), |
| 1862 base::Bind(&CheckMigrationResult, PasswordStoreMac::KEYCHAIN_BLOCKED))); |
| 1863 FinishAsyncProcessing(); |
| 1864 |
| 1865 ScopedVector<PasswordForm> matching_items; |
| 1866 EXPECT_TRUE(login_db()->GetLogins(form1, &matching_items)); |
| 1867 ASSERT_EQ(1u, matching_items.size()); |
| 1868 EXPECT_EQ(base::string16(), matching_items[0]->password_value); |
| 1869 |
| 1870 histogram_tester_->ExpectUniqueSample( |
| 1871 "PasswordManager.KeychainMigration.NumPasswords", 1, 1); |
| 1872 histogram_tester_->ExpectUniqueSample( |
| 1873 "PasswordManager.KeychainMigration.NumLockedPasswords", 1, 1); |
| 1874 histogram_tester_->ExpectUniqueSample( |
| 1875 "PasswordManager.KeychainMigration.NumChromeOwnedLockedPasswords", 1, 1); |
| 1876 // Don't test the encryption key access. |
| 1877 histogram_tester_.reset(); |
| 1878 } |
OLD | NEW |