blob: 59f2c46119f197975e4aeeed757e217c52f94f4b [file] [log] [blame]
Lutz Justenb7ef5802019-05-14 14:03:20 +02001// Copyright 2019 The Chromium OS 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 "kerberos/account_manager.h"
6
7#include <map>
8#include <memory>
9#include <utility>
10
11#include <base/bind.h>
Lutz Justene39cbd42019-05-14 14:52:24 +020012#include <base/files/file_path.h>
Lutz Justenb7ef5802019-05-14 14:03:20 +020013#include <base/files/file_util.h>
14#include <base/files/scoped_temp_dir.h>
Lutz Justend925d712019-06-27 17:34:00 +020015#include <base/memory/ref_counted.h>
16#include <base/test/test_mock_time_task_runner.h>
Lutz Justen2d857e12019-07-16 15:58:09 +020017#include <gmock/gmock.h>
Lutz Justenb7ef5802019-05-14 14:03:20 +020018#include <gtest/gtest.h>
Lutz Justen04afd272019-06-14 16:31:26 +020019#include <libpasswordprovider/fake_password_provider.h>
20#include <libpasswordprovider/password_provider_test_utils.h>
Lutz Justenb7ef5802019-05-14 14:03:20 +020021
Lutz Justene39cbd42019-05-14 14:52:24 +020022#include "kerberos/fake_krb5_interface.h"
Lutz Justen2d857e12019-07-16 15:58:09 +020023#include "kerberos/kerberos_metrics.h"
Lutz Justen33601802019-07-03 22:40:57 +020024#include "kerberos/krb5_jail_wrapper.h"
Lutz Justene39cbd42019-05-14 14:52:24 +020025
Lutz Justen2d857e12019-07-16 15:58:09 +020026using testing::_;
27using testing::Mock;
28using testing::NiceMock;
29using testing::Return;
30
Lutz Justend925d712019-06-27 17:34:00 +020031namespace kerberos {
Lutz Justenb7ef5802019-05-14 14:03:20 +020032namespace {
Lutz Justen6f582982019-05-15 10:24:22 +020033
Lutz Justenb7ef5802019-05-14 14:03:20 +020034constexpr char kUser[] = "user@REALM.COM";
Lutz Justen6f582982019-05-15 10:24:22 +020035constexpr char kUser2[] = "user2@REALM2.COM";
Lutz Justen781825e2019-07-13 07:49:44 +020036constexpr char kUser3[] = "user3@REALM3.COM";
Lutz Justen6f582982019-05-15 10:24:22 +020037constexpr char kPassword[] = "i<3k3R8e5Oz";
Lutz Justen04afd272019-06-14 16:31:26 +020038constexpr char kPassword2[] = "ih4zf00d";
Lutz Justen6f582982019-05-15 10:24:22 +020039constexpr char kKrb5Conf[] = R"(
40 [libdefaults]
Felipe Andrade66aaf6b2020-03-24 13:05:57 +010041 forwardable = true)";
42constexpr char kStrongKrb5Conf[] = R"(
43 [libdefaults]
44 forwardable = true
45 default_tkt_enctypes = aes256-cts-hmac-sha1-96
46 default_tgs_enctypes = aes256-cts-hmac-sha1-96
47 permitted_enctypes = aes256-cts-hmac-sha1-96)";
48constexpr char kLegacyKrb5Conf[] = R"(
49 [libdefaults]
50 forwardable = true
51 default_tkt_enctypes = arcfour-hmac
52 default_tgs_enctypes = arcfour-hmac
53 permitted_enctypes = arcfour-hmac)";
Felipe Andrade90cb84e2020-04-07 20:28:33 +020054constexpr char kInvalidKrb5Conf[] = R"(
55 [libdefaults]
56 stonkskew = 123)";
Lutz Justen6f582982019-05-15 10:24:22 +020057
Lutz Justend925d712019-06-27 17:34:00 +020058constexpr Krb5Interface::TgtStatus kValidTgt(3600, 3600);
59constexpr Krb5Interface::TgtStatus kExpiredTgt(0, 0);
60
Lutz Justen04afd272019-06-14 16:31:26 +020061// Convenience defines to make code more readable
62constexpr bool kManaged = true;
63constexpr bool kUnmanaged = false;
64
65constexpr bool kRememberPassword = true;
66constexpr bool kDontRememberPassword = false;
67
68constexpr bool kUseLoginPassword = true;
69constexpr bool kDontUseLoginPassword = false;
70
71constexpr char kEmptyPassword[] = "";
72
Lutz Justen2d857e12019-07-16 15:58:09 +020073class MockMetrics : public KerberosMetrics {
74 public:
75 explicit MockMetrics(const base::FilePath& storage_dir)
76 : KerberosMetrics(storage_dir) {}
Qijiang Fan6bc59e12020-11-11 02:51:06 +090077 MockMetrics(const MockMetrics&) = delete;
78 MockMetrics& operator=(const MockMetrics&) = delete;
79
Lutz Justen2d857e12019-07-16 15:58:09 +020080 ~MockMetrics() override = default;
81
Ben Chan60470012019-09-24 14:01:54 -070082 MOCK_METHOD(bool, ShouldReportDailyUsageStats, (), (override));
83 MOCK_METHOD(void,
84 ReportDailyUsageStats,
85 (int, int, int, int, int),
86 (override));
Felipe Andrade66aaf6b2020-03-24 13:05:57 +010087 MOCK_METHOD(void,
88 ReportKerberosEncryptionTypes,
89 (KerberosEncryptionTypes),
90 (override));
Lutz Justen2d857e12019-07-16 15:58:09 +020091};
92
Lutz Justen6f582982019-05-15 10:24:22 +020093} // namespace
Lutz Justenb7ef5802019-05-14 14:03:20 +020094
Lutz Justenb7ef5802019-05-14 14:03:20 +020095class AccountManagerTest : public ::testing::Test {
96 public:
Lutz Justen6f582982019-05-15 10:24:22 +020097 AccountManagerTest()
98 : kerberos_files_changed_(
99 base::BindRepeating(&AccountManagerTest::OnKerberosFilesChanged,
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200100 base::Unretained(this))),
101 kerberos_ticket_expiring_(
102 base::BindRepeating(&AccountManagerTest::OnKerberosTicketExpiring,
Lutz Justen6f582982019-05-15 10:24:22 +0200103 base::Unretained(this))) {}
Qijiang Fan6bc59e12020-11-11 02:51:06 +0900104 AccountManagerTest(const AccountManagerTest&) = delete;
105 AccountManagerTest& operator=(const AccountManagerTest&) = delete;
106
Lutz Justenb7ef5802019-05-14 14:03:20 +0200107 ~AccountManagerTest() override = default;
108
109 void SetUp() override {
110 ::testing::Test::SetUp();
111
112 // Create temp directory for files written during tests.
113 CHECK(storage_dir_.CreateUniqueTempDir());
Lutz Justen6f582982019-05-15 10:24:22 +0200114 accounts_path_ = storage_dir_.GetPath().Append("accounts");
Lutz Justen33601802019-07-03 22:40:57 +0200115 account_dir_ = storage_dir_.GetPath().Append(
Lutz Justenc5690502019-05-24 17:00:54 +0200116 AccountManager::GetSafeFilenameForTesting(kUser));
Lutz Justen33601802019-07-03 22:40:57 +0200117 krb5cc_path_ = account_dir_.Append("krb5cc");
118 krb5conf_path_ = account_dir_.Append("krb5.conf");
119 password_path_ = account_dir_.Append("password");
Lutz Justenb7ef5802019-05-14 14:03:20 +0200120
Lutz Justen6f582982019-05-15 10:24:22 +0200121 // Create the manager with a fake krb5 interface.
122 auto krb5 = std::make_unique<FakeKrb5Interface>();
Lutz Justen04afd272019-06-14 16:31:26 +0200123 auto password_provider =
124 std::make_unique<password_provider::FakePasswordProvider>();
Lutz Justen2d857e12019-07-16 15:58:09 +0200125 metrics_ = std::make_unique<NiceMock<MockMetrics>>(storage_dir_.GetPath());
Lutz Justen6f582982019-05-15 10:24:22 +0200126 krb5_ = krb5.get();
Lutz Justen04afd272019-06-14 16:31:26 +0200127 password_provider_ = password_provider.get();
Lutz Justenb7ef5802019-05-14 14:03:20 +0200128 manager_ = std::make_unique<AccountManager>(
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200129 storage_dir_.GetPath(), kerberos_files_changed_,
130 kerberos_ticket_expiring_, std::move(krb5),
Lutz Justen2d857e12019-07-16 15:58:09 +0200131 std::move(password_provider), metrics_.get());
Lutz Justenb7ef5802019-05-14 14:03:20 +0200132 }
133
Lutz Justen82d07ce2019-05-16 10:46:14 +0200134 void TearDown() override {
135 // Make sure the file stored on disk contains the same accounts as the
136 // manager instance. This catches cases where AccountManager forgets to save
137 // accounts on some change.
138 if (base::PathExists(accounts_path_)) {
Felipe Andrade89399f02019-11-11 15:52:14 +0100139 std::vector<Account> accounts = manager_->ListAccounts();
Lutz Justen82d07ce2019-05-16 10:46:14 +0200140
Lutz Justen04afd272019-06-14 16:31:26 +0200141 AccountManager other_manager(
142 storage_dir_.GetPath(), kerberos_files_changed_,
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200143 kerberos_ticket_expiring_, std::make_unique<FakeKrb5Interface>(),
Lutz Justen2d857e12019-07-16 15:58:09 +0200144 std::make_unique<password_provider::FakePasswordProvider>(),
145 metrics_.get());
Lutz Justen82d07ce2019-05-16 10:46:14 +0200146 other_manager.LoadAccounts();
Felipe Andrade89399f02019-11-11 15:52:14 +0100147 std::vector<Account> other_accounts = other_manager.ListAccounts();
Lutz Justen82d07ce2019-05-16 10:46:14 +0200148
149 ASSERT_NO_FATAL_FAILURE(ExpectAccountsEqual(accounts, other_accounts));
150 }
151
152 ::testing::Test::TearDown();
153 }
154
Lutz Justen04afd272019-06-14 16:31:26 +0200155 // Add account with default settings.
156 ErrorType AddAccount() { return manager_->AddAccount(kUser, kUnmanaged); }
157
158 // Sets some default Kerberos configuration.
Felipe Andrade66aaf6b2020-03-24 13:05:57 +0100159 ErrorType SetConfig() { return SetConfig(kKrb5Conf); }
160
161 // Sets Kerberos configuration.
162 ErrorType SetConfig(const std::string& config) {
163 return manager_->SetConfig(kUser, config);
164 }
Lutz Justen04afd272019-06-14 16:31:26 +0200165
166 // Acquire Kerberos ticket with default credentials and settings.
167 ErrorType AcquireTgt() {
168 return manager_->AcquireTgt(kUser, kPassword, kDontRememberPassword,
169 kDontUseLoginPassword);
170 }
171
172 void SaveLoginPassword(const char* password) {
173 auto password_ptr = password_provider::test::CreatePassword(password);
174 password_provider_->SavePassword(*password_ptr);
175 }
176
Lutz Justend925d712019-06-27 17:34:00 +0200177 // Fast forwards to the next scheduled task (assumed to be the renewal task)
178 // and verifies expectation that |krb5_->RenewTgt() was called|.
179 void RunScheduledRenewalTask() {
180 int initial_count = krb5_->renew_tgt_call_count();
181 EXPECT_EQ(1, task_runner_->GetPendingTaskCount());
182 task_runner_->FastForwardBy(task_runner_->NextPendingTaskDelay());
183 EXPECT_EQ(initial_count + 1, krb5_->renew_tgt_call_count());
184 }
185
Lutz Justenb7ef5802019-05-14 14:03:20 +0200186 protected:
187 void OnKerberosFilesChanged(const std::string& principal_name) {
188 kerberos_files_changed_count_[principal_name]++;
189 }
190
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200191 void OnKerberosTicketExpiring(const std::string& principal_name) {
192 kerberos_ticket_expiring_count_[principal_name]++;
193 }
194
Lutz Justen82d07ce2019-05-16 10:46:14 +0200195 void ExpectAccountsEqual(const std::vector<Account>& account_list_1,
196 const std::vector<Account>& account_list_2) {
197 ASSERT_EQ(account_list_1.size(), account_list_2.size());
198 for (size_t n = 0; n < account_list_1.size(); ++n) {
199 const Account& account1 = account_list_1[n];
200 const Account& account2 = account_list_2[n];
201
202 EXPECT_EQ(account1.principal_name(), account2.principal_name());
203 EXPECT_EQ(account1.is_managed(), account2.is_managed());
Lutz Justen04afd272019-06-14 16:31:26 +0200204 EXPECT_EQ(account1.use_login_password(), account2.use_login_password());
Lutz Justen82d07ce2019-05-16 10:46:14 +0200205 // TODO(https://crbug.com/952239): Check additional properties.
206 }
207 }
208
Lutz Justenb7ef5802019-05-14 14:03:20 +0200209 std::unique_ptr<AccountManager> manager_;
Lutz Justen6f582982019-05-15 10:24:22 +0200210
Lutz Justen04afd272019-06-14 16:31:26 +0200211 // Fake Kerberos interface used by |manager_|. Not owned.
Lutz Justen6f582982019-05-15 10:24:22 +0200212 FakeKrb5Interface* krb5_;
213
Lutz Justen04afd272019-06-14 16:31:26 +0200214 // Fake password provider to get the login password. Not owned.
215 password_provider::FakePasswordProvider* password_provider_;
216
Lutz Justen2d857e12019-07-16 15:58:09 +0200217 // Mock metrics for testing UMA stat recording.
218 std::unique_ptr<NiceMock<MockMetrics>> metrics_;
219
Lutz Justen6f582982019-05-15 10:24:22 +0200220 // Paths of files stored by |manager_|.
Lutz Justenb7ef5802019-05-14 14:03:20 +0200221 base::ScopedTempDir storage_dir_;
Lutz Justen6f582982019-05-15 10:24:22 +0200222 base::FilePath accounts_path_;
Lutz Justen33601802019-07-03 22:40:57 +0200223 base::FilePath account_dir_;
Lutz Justen6f582982019-05-15 10:24:22 +0200224 base::FilePath krb5conf_path_;
225 base::FilePath krb5cc_path_;
Lutz Justenc5690502019-05-24 17:00:54 +0200226 base::FilePath password_path_;
Lutz Justen6f582982019-05-15 10:24:22 +0200227
228 AccountManager::KerberosFilesChangedCallback kerberos_files_changed_;
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200229 AccountManager::KerberosTicketExpiringCallback kerberos_ticket_expiring_;
230
Lutz Justenb7ef5802019-05-14 14:03:20 +0200231 std::map<std::string, int> kerberos_files_changed_count_;
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200232 std::map<std::string, int> kerberos_ticket_expiring_count_;
Lutz Justenb7ef5802019-05-14 14:03:20 +0200233
Lutz Justend925d712019-06-27 17:34:00 +0200234 scoped_refptr<base::TestMockTimeTaskRunner> task_runner_{
235 new base::TestMockTimeTaskRunner()};
236 base::TestMockTimeTaskRunner::ScopedContext scoped_context_{task_runner_};
Lutz Justenb7ef5802019-05-14 14:03:20 +0200237};
238
Lutz Justen6f582982019-05-15 10:24:22 +0200239// Adding an account succeeds and serializes the file on disk.
Lutz Justenb7ef5802019-05-14 14:03:20 +0200240TEST_F(AccountManagerTest, AddAccountSuccess) {
Lutz Justen6f582982019-05-15 10:24:22 +0200241 EXPECT_FALSE(base::PathExists(accounts_path_));
Lutz Justen04afd272019-06-14 16:31:26 +0200242 EXPECT_EQ(ERROR_NONE, AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200243 EXPECT_TRUE(base::PathExists(accounts_path_));
Lutz Justenb7ef5802019-05-14 14:03:20 +0200244}
245
Lutz Justen6f582982019-05-15 10:24:22 +0200246// AddAccount() fails if the same account is added twice.
Lutz Justenb7ef5802019-05-14 14:03:20 +0200247TEST_F(AccountManagerTest, AddDuplicateAccountFail) {
Lutz Justen04afd272019-06-14 16:31:26 +0200248 ignore_result(AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200249
250 EXPECT_TRUE(base::DeleteFile(accounts_path_, false /* recursive */));
Lutz Justen04afd272019-06-14 16:31:26 +0200251 EXPECT_EQ(ERROR_DUPLICATE_PRINCIPAL_NAME, AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200252 EXPECT_FALSE(base::PathExists(accounts_path_));
Lutz Justenb7ef5802019-05-14 14:03:20 +0200253}
254
Lutz Justen6f582982019-05-15 10:24:22 +0200255// Adding a managed account overwrites an existing unmanaged account.
256TEST_F(AccountManagerTest, ManagedOverridesUnmanaged) {
Lutz Justen04afd272019-06-14 16:31:26 +0200257 ignore_result(manager_->AddAccount(kUser, kUnmanaged));
Lutz Justen6f582982019-05-15 10:24:22 +0200258
Lutz Justen04afd272019-06-14 16:31:26 +0200259 EXPECT_EQ(ERROR_NONE, AcquireTgt());
Lutz Justen82d07ce2019-05-16 10:46:14 +0200260 EXPECT_TRUE(base::PathExists(krb5cc_path_));
261
262 // Overwriting with a managed account should wipe existing files and make the
263 // account managed.
Lutz Justen6f582982019-05-15 10:24:22 +0200264 EXPECT_EQ(ERROR_DUPLICATE_PRINCIPAL_NAME,
Lutz Justen04afd272019-06-14 16:31:26 +0200265 manager_->AddAccount(kUser, kManaged));
Lutz Justen82d07ce2019-05-16 10:46:14 +0200266 EXPECT_FALSE(base::PathExists(krb5cc_path_));
267
Felipe Andrade89399f02019-11-11 15:52:14 +0100268 std::vector<Account> accounts = manager_->ListAccounts();
Lutz Justen6f582982019-05-15 10:24:22 +0200269 ASSERT_EQ(1u, accounts.size());
270 EXPECT_TRUE(accounts[0].is_managed());
271}
272
273// Adding an unmanaged account does not overwrite an existing managed account.
274TEST_F(AccountManagerTest, UnmanagedDoesNotOverrideManaged) {
Lutz Justen04afd272019-06-14 16:31:26 +0200275 ignore_result(manager_->AddAccount(kUser, kManaged));
Lutz Justen6f582982019-05-15 10:24:22 +0200276
277 EXPECT_EQ(ERROR_DUPLICATE_PRINCIPAL_NAME,
Lutz Justen04afd272019-06-14 16:31:26 +0200278 manager_->AddAccount(kUser, kUnmanaged));
Felipe Andrade89399f02019-11-11 15:52:14 +0100279 std::vector<Account> accounts = manager_->ListAccounts();
Lutz Justen6f582982019-05-15 10:24:22 +0200280 ASSERT_EQ(1u, accounts.size());
281 EXPECT_TRUE(accounts[0].is_managed());
282}
283
284// RemoveAccount() succeeds if the account exists and serializes the file on
285// disk.
Lutz Justenb7ef5802019-05-14 14:03:20 +0200286TEST_F(AccountManagerTest, RemoveAccountSuccess) {
Lutz Justen04afd272019-06-14 16:31:26 +0200287 ignore_result(AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200288
289 EXPECT_TRUE(base::DeleteFile(accounts_path_, false /* recursive */));
Lutz Justenb7ef5802019-05-14 14:03:20 +0200290 EXPECT_EQ(ERROR_NONE, manager_->RemoveAccount(kUser));
Lutz Justen6f582982019-05-15 10:24:22 +0200291 EXPECT_TRUE(base::PathExists(accounts_path_));
Lutz Justenb7ef5802019-05-14 14:03:20 +0200292}
293
Lutz Justen6f582982019-05-15 10:24:22 +0200294// RemoveAccount() fails if the account does not exist.
Lutz Justenb7ef5802019-05-14 14:03:20 +0200295TEST_F(AccountManagerTest, RemoveUnknownAccountFail) {
296 EXPECT_EQ(ERROR_UNKNOWN_PRINCIPAL_NAME, manager_->RemoveAccount(kUser));
Lutz Justen6f582982019-05-15 10:24:22 +0200297 EXPECT_FALSE(base::PathExists(accounts_path_));
298}
299
300// RemoveAccount() does not trigger KerberosFilesChanged if the credential cache
301// does not exists.
302TEST_F(AccountManagerTest, RemoveAccountTriggersKFCIfCCExists) {
Lutz Justen04afd272019-06-14 16:31:26 +0200303 ignore_result(AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200304
305 EXPECT_EQ(ERROR_NONE, manager_->RemoveAccount(kUser));
306 EXPECT_EQ(0, kerberos_files_changed_count_[kUser]);
307}
308
309// RemoveAccount() triggers KerberosFilesChanged if the credential cache exists.
310TEST_F(AccountManagerTest, RemoveAccountDoesNotTriggerKFCIfCCDoesNotExist) {
Lutz Justen04afd272019-06-14 16:31:26 +0200311 ignore_result(AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200312
Lutz Justen04afd272019-06-14 16:31:26 +0200313 EXPECT_EQ(ERROR_NONE, AcquireTgt());
Lutz Justen6f582982019-05-15 10:24:22 +0200314 EXPECT_EQ(1, kerberos_files_changed_count_[kUser]);
315 EXPECT_EQ(ERROR_NONE, manager_->RemoveAccount(kUser));
316 EXPECT_EQ(2, kerberos_files_changed_count_[kUser]);
Lutz Justenb7ef5802019-05-14 14:03:20 +0200317}
318
319// Repeatedly calling AddAccount() and RemoveAccount() succeeds.
320TEST_F(AccountManagerTest, RepeatedAddRemoveSuccess) {
Lutz Justen04afd272019-06-14 16:31:26 +0200321 ignore_result(AddAccount());
Lutz Justenb7ef5802019-05-14 14:03:20 +0200322 ignore_result(manager_->RemoveAccount(kUser));
Lutz Justen6f582982019-05-15 10:24:22 +0200323
Lutz Justen04afd272019-06-14 16:31:26 +0200324 EXPECT_EQ(ERROR_NONE, AddAccount());
Lutz Justenb7ef5802019-05-14 14:03:20 +0200325 EXPECT_EQ(ERROR_NONE, manager_->RemoveAccount(kUser));
326}
327
Lutz Justen2d420872019-06-25 14:17:21 +0200328// ClearAccounts(CLEAR_ALL) clears all accounts.
329TEST_F(AccountManagerTest, ClearAccountsSuccess) {
Lutz Justen04afd272019-06-14 16:31:26 +0200330 ignore_result(manager_->AddAccount(kUser, kUnmanaged));
331 ignore_result(manager_->AddAccount(kUser2, kManaged));
Lutz Justen82d07ce2019-05-16 10:46:14 +0200332
Lutz Justen781825e2019-07-13 07:49:44 +0200333 EXPECT_EQ(ERROR_NONE, manager_->ClearAccounts(CLEAR_ALL, {}));
Felipe Andrade89399f02019-11-11 15:52:14 +0100334 std::vector<Account> accounts = manager_->ListAccounts();
Lutz Justen82d07ce2019-05-16 10:46:14 +0200335 EXPECT_EQ(0u, accounts.size());
336}
337
Lutz Justen2d420872019-06-25 14:17:21 +0200338// ClearAccounts(CLEAR_ALL) wipes Kerberos configuration and credential cache.
339TEST_F(AccountManagerTest, ClearAccountsRemovesKerberosFiles) {
Lutz Justen04afd272019-06-14 16:31:26 +0200340 ignore_result(AddAccount());
Lutz Justen82d07ce2019-05-16 10:46:14 +0200341
Lutz Justen04afd272019-06-14 16:31:26 +0200342 EXPECT_EQ(ERROR_NONE, SetConfig());
343 EXPECT_EQ(ERROR_NONE, AcquireTgt());
Lutz Justen82d07ce2019-05-16 10:46:14 +0200344 EXPECT_TRUE(base::PathExists(krb5conf_path_));
345 EXPECT_TRUE(base::PathExists(krb5cc_path_));
Lutz Justen781825e2019-07-13 07:49:44 +0200346 EXPECT_EQ(ERROR_NONE, manager_->ClearAccounts(CLEAR_ALL, {}));
Lutz Justen82d07ce2019-05-16 10:46:14 +0200347 EXPECT_FALSE(base::PathExists(krb5conf_path_));
348 EXPECT_FALSE(base::PathExists(krb5cc_path_));
349}
350
Lutz Justen2d420872019-06-25 14:17:21 +0200351// ClearAccounts(CLEAR_ALL) triggers KerberosFilesChanged if the credential
352// cache exists.
353TEST_F(AccountManagerTest, ClearAccountsTriggersKFCIfCCExists) {
Lutz Justen04afd272019-06-14 16:31:26 +0200354 ignore_result(AddAccount());
Lutz Justen82d07ce2019-05-16 10:46:14 +0200355
Lutz Justen04afd272019-06-14 16:31:26 +0200356 EXPECT_EQ(ERROR_NONE, AcquireTgt());
Lutz Justen82d07ce2019-05-16 10:46:14 +0200357 EXPECT_EQ(1, kerberos_files_changed_count_[kUser]);
Lutz Justen781825e2019-07-13 07:49:44 +0200358 EXPECT_EQ(ERROR_NONE, manager_->ClearAccounts(CLEAR_ALL, {}));
Lutz Justen82d07ce2019-05-16 10:46:14 +0200359 EXPECT_EQ(2, kerberos_files_changed_count_[kUser]);
360}
361
Lutz Justen2d420872019-06-25 14:17:21 +0200362// ClearAccounts(CLEAR_ALL) does not trigger KerberosFilesChanged if the
363// credential cache does not exist.
364TEST_F(AccountManagerTest, ClearAccountsDoesNotTriggerKFCIfDoesNotCCExist) {
Lutz Justen04afd272019-06-14 16:31:26 +0200365 ignore_result(AddAccount());
Lutz Justen82d07ce2019-05-16 10:46:14 +0200366
Lutz Justen781825e2019-07-13 07:49:44 +0200367 EXPECT_EQ(ERROR_NONE, manager_->ClearAccounts(CLEAR_ALL, {}));
Lutz Justen82d07ce2019-05-16 10:46:14 +0200368 EXPECT_EQ(0, kerberos_files_changed_count_[kUser]);
369}
370
Lutz Justen2d420872019-06-25 14:17:21 +0200371// ClearAccounts(CLEAR_ONLY_UNMANAGED_ACCOUNTS) clears only unmanaged accounts.
372TEST_F(AccountManagerTest, ClearUnmanagedAccountsSuccess) {
373 ignore_result(manager_->AddAccount(kUser, kUnmanaged));
374 ignore_result(manager_->AddAccount(kUser2, kManaged));
375
Lutz Justen781825e2019-07-13 07:49:44 +0200376 EXPECT_EQ(ERROR_NONE,
377 manager_->ClearAccounts(CLEAR_ONLY_UNMANAGED_ACCOUNTS, {}));
Felipe Andrade89399f02019-11-11 15:52:14 +0100378 std::vector<Account> accounts = manager_->ListAccounts();
Lutz Justen2d420872019-06-25 14:17:21 +0200379 ASSERT_EQ(1u, accounts.size());
380 EXPECT_EQ(kUser2, accounts[0].principal_name());
381}
382
383// ClearAccounts(CLEAR_ONLY_UNMANAGED_REMEMBERED_PASSWORDS) clears only
384// passwords of unmanaged accounts.
385TEST_F(AccountManagerTest, ClearUnmanagedPasswordsSuccess) {
386 // kUser is unmanaged, kUser2 is managed.
387 ignore_result(manager_->AddAccount(kUser, kUnmanaged));
388 ignore_result(manager_->AddAccount(kUser2, kManaged));
389 ignore_result(manager_->AcquireTgt(kUser, kPassword, kRememberPassword,
390 kDontUseLoginPassword));
391 ignore_result(manager_->AcquireTgt(kUser2, kPassword, kRememberPassword,
392 kDontUseLoginPassword));
393
394 base::FilePath password_path_2 =
395 storage_dir_.GetPath()
396 .Append(AccountManager::GetSafeFilenameForTesting(kUser2))
397 .Append("password");
398 EXPECT_TRUE(base::PathExists(password_path_));
399 EXPECT_TRUE(base::PathExists(password_path_2));
400
Lutz Justen781825e2019-07-13 07:49:44 +0200401 EXPECT_EQ(ERROR_NONE, manager_->ClearAccounts(
402 CLEAR_ONLY_UNMANAGED_REMEMBERED_PASSWORDS, {}));
Felipe Andrade89399f02019-11-11 15:52:14 +0100403 std::vector<Account> accounts = manager_->ListAccounts();
Lutz Justen2d420872019-06-25 14:17:21 +0200404 ASSERT_EQ(2u, accounts.size());
405 EXPECT_FALSE(base::PathExists(password_path_));
406 EXPECT_TRUE(base::PathExists(password_path_2));
407}
408
Lutz Justen781825e2019-07-13 07:49:44 +0200409// ClearAccounts(CLEAR_ONLY_MANAGED_ACCOUNTS) clears only managed accounts that
410// are not on the keep list.
411TEST_F(AccountManagerTest, ClearManagedPasswordsWithKeepListSuccess) {
412 ignore_result(manager_->AddAccount(kUser, kManaged));
413 ignore_result(manager_->AddAccount(kUser2, kManaged));
414 ignore_result(manager_->AddAccount(kUser3, kUnmanaged));
415
416 // Keep the managed kUser-account.
417 EXPECT_EQ(ERROR_NONE,
418 manager_->ClearAccounts(CLEAR_ONLY_MANAGED_ACCOUNTS, {kUser}));
Felipe Andrade89399f02019-11-11 15:52:14 +0100419 std::vector<Account> accounts = manager_->ListAccounts();
Lutz Justen781825e2019-07-13 07:49:44 +0200420 ASSERT_EQ(2u, accounts.size());
421 EXPECT_EQ(kUser, accounts[0].principal_name());
422 EXPECT_EQ(kUser3, accounts[1].principal_name());
423}
424
Lutz Justen6f582982019-05-15 10:24:22 +0200425// SetConfig() succeeds and writes the config to |krb5conf_path_|.
426TEST_F(AccountManagerTest, SetConfigSuccess) {
Lutz Justen04afd272019-06-14 16:31:26 +0200427 ignore_result(AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200428
Lutz Justen04afd272019-06-14 16:31:26 +0200429 EXPECT_EQ(ERROR_NONE, SetConfig());
Lutz Justen6f582982019-05-15 10:24:22 +0200430 std::string krb5_conf;
431 EXPECT_TRUE(base::ReadFileToString(krb5conf_path_, &krb5_conf));
432 EXPECT_EQ(krb5_conf, kKrb5Conf);
433}
434
Lutz Justen90281402019-07-05 15:14:37 +0200435// SetConfig() calls ValidateConfig on the Kerberos interface.
436TEST_F(AccountManagerTest, SetConfigValidatesConfig) {
437 ignore_result(AddAccount());
438
439 krb5_->set_validate_config_error(ERROR_BAD_CONFIG);
440 EXPECT_EQ(ERROR_BAD_CONFIG, SetConfig());
441}
442
Lutz Justen6f582982019-05-15 10:24:22 +0200443// SetConfig() triggers KerberosFilesChanged if the credential cache exists.
444TEST_F(AccountManagerTest, SetConfigTriggersKFCIfCCExists) {
Lutz Justen04afd272019-06-14 16:31:26 +0200445 ignore_result(AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200446
Lutz Justen04afd272019-06-14 16:31:26 +0200447 EXPECT_EQ(ERROR_NONE, AcquireTgt());
Lutz Justen6f582982019-05-15 10:24:22 +0200448 EXPECT_EQ(1, kerberos_files_changed_count_[kUser]);
Lutz Justen04afd272019-06-14 16:31:26 +0200449 EXPECT_EQ(ERROR_NONE, SetConfig());
Lutz Justen6f582982019-05-15 10:24:22 +0200450 EXPECT_EQ(2, kerberos_files_changed_count_[kUser]);
451}
452
Lutz Justen82d07ce2019-05-16 10:46:14 +0200453// SetConfig() does not trigger KerberosFilesChanged if the credential cache
454// does not exist.
Lutz Justen6f582982019-05-15 10:24:22 +0200455TEST_F(AccountManagerTest, SetConfigDoesNotTriggerKFCIfDoesNotCCExist) {
Lutz Justen04afd272019-06-14 16:31:26 +0200456 ignore_result(AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200457
Lutz Justen04afd272019-06-14 16:31:26 +0200458 EXPECT_EQ(ERROR_NONE, SetConfig());
Lutz Justen6f582982019-05-15 10:24:22 +0200459 EXPECT_EQ(0, kerberos_files_changed_count_[kUser]);
460}
461
462// RemoveAccount() removes the config file.
463TEST_F(AccountManagerTest, RemoveAccountRemovesConfig) {
Lutz Justen04afd272019-06-14 16:31:26 +0200464 ignore_result(AddAccount());
465 ignore_result(SetConfig());
Lutz Justen6f582982019-05-15 10:24:22 +0200466
467 EXPECT_TRUE(base::PathExists(krb5conf_path_));
468 EXPECT_EQ(ERROR_NONE, manager_->RemoveAccount(kUser));
469 EXPECT_FALSE(base::PathExists(krb5conf_path_));
470}
471
Lutz Justen90281402019-07-05 15:14:37 +0200472// ValidateConfig() validates a good config successfully.
473TEST_F(AccountManagerTest, ValidateConfigSuccess) {
474 constexpr char kValidKrb5Conf[] = "";
475 ConfigErrorInfo error_info;
476 EXPECT_EQ(ERROR_NONE, manager_->ValidateConfig(kValidKrb5Conf, &error_info));
477 EXPECT_EQ(CONFIG_ERROR_NONE, error_info.code());
478}
479
480// ValidateConfig() returns the correct error for a bad config.
481TEST_F(AccountManagerTest, ValidateConfigFailure) {
482 ConfigErrorInfo expected_error_info;
483 expected_error_info.set_code(CONFIG_ERROR_SECTION_SYNTAX);
484 krb5_->set_config_error_info(expected_error_info);
485 krb5_->set_validate_config_error(ERROR_BAD_CONFIG);
486
487 constexpr char kBadKrb5Conf[] =
488 "[libdefaults]'); DROP TABLE KerberosTickets;--";
489 ConfigErrorInfo error_info;
490 EXPECT_EQ(ERROR_BAD_CONFIG,
491 manager_->ValidateConfig(kBadKrb5Conf, &error_info));
492 EXPECT_EQ(expected_error_info.SerializeAsString(),
493 error_info.SerializeAsString());
494}
495
Lutz Justen6f582982019-05-15 10:24:22 +0200496// AcquireTgt() succeeds and writes a credential cache file.
497TEST_F(AccountManagerTest, AcquireTgtSuccess) {
Lutz Justen04afd272019-06-14 16:31:26 +0200498 ignore_result(AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200499
Lutz Justen04afd272019-06-14 16:31:26 +0200500 EXPECT_EQ(ERROR_NONE, AcquireTgt());
Lutz Justen6f582982019-05-15 10:24:22 +0200501 EXPECT_TRUE(base::PathExists(krb5cc_path_));
502}
503
504// AcquireTgt() triggers KerberosFilesChanged on success.
505TEST_F(AccountManagerTest, AcquireTgtTriggersKFCOnSuccess) {
Lutz Justen04afd272019-06-14 16:31:26 +0200506 ignore_result(AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200507
508 EXPECT_EQ(0, kerberos_files_changed_count_[kUser]);
Lutz Justen04afd272019-06-14 16:31:26 +0200509 EXPECT_EQ(ERROR_NONE, AcquireTgt());
Lutz Justen6f582982019-05-15 10:24:22 +0200510 EXPECT_EQ(1, kerberos_files_changed_count_[kUser]);
511}
512
513// AcquireTgt() does not trigger KerberosFilesChanged on failure.
514TEST_F(AccountManagerTest, AcquireTgtDoesNotTriggerKFCOnFailure) {
Lutz Justen04afd272019-06-14 16:31:26 +0200515 ignore_result(AddAccount());
Lutz Justen6f582982019-05-15 10:24:22 +0200516
517 krb5_->set_acquire_tgt_error(ERROR_UNKNOWN);
Lutz Justen04afd272019-06-14 16:31:26 +0200518 EXPECT_EQ(ERROR_UNKNOWN, AcquireTgt());
Lutz Justen6f582982019-05-15 10:24:22 +0200519 EXPECT_EQ(0, kerberos_files_changed_count_[kUser]);
520}
521
Lutz Justenc5690502019-05-24 17:00:54 +0200522// AcquireTgt() saves password to disk if |remember_password| is true and
523// removes the file again if |remember_password| is false.
524TEST_F(AccountManagerTest, AcquireTgtRemembersPasswordsIfWanted) {
Lutz Justen04afd272019-06-14 16:31:26 +0200525 ignore_result(AddAccount());
Lutz Justenc5690502019-05-24 17:00:54 +0200526
527 EXPECT_FALSE(base::PathExists(password_path_));
Lutz Justen04afd272019-06-14 16:31:26 +0200528 EXPECT_EQ(ERROR_NONE,
529 manager_->AcquireTgt(kUser, kPassword, kRememberPassword,
530 kDontUseLoginPassword));
Lutz Justenc5690502019-05-24 17:00:54 +0200531 EXPECT_TRUE(base::PathExists(password_path_));
532
Lutz Justen04afd272019-06-14 16:31:26 +0200533 EXPECT_EQ(ERROR_NONE,
534 manager_->AcquireTgt(kUser, kPassword, kDontRememberPassword,
535 kDontUseLoginPassword));
Lutz Justenc5690502019-05-24 17:00:54 +0200536 EXPECT_FALSE(base::PathExists(password_path_));
537}
538
539// AcquireTgt() uses saved password if none is given, no matter if it should be
540// remembered again or not.
541TEST_F(AccountManagerTest, AcquireTgtLoadsRememberedPassword) {
Lutz Justen04afd272019-06-14 16:31:26 +0200542 ignore_result(AddAccount());
543 ignore_result(manager_->AcquireTgt(kUser, kPassword, kRememberPassword,
544 kDontUseLoginPassword));
Lutz Justenc5690502019-05-24 17:00:54 +0200545
546 // This should load stored password and keep it.
547 EXPECT_EQ(ERROR_NONE,
Lutz Justen04afd272019-06-14 16:31:26 +0200548 manager_->AcquireTgt(kUser, kEmptyPassword, kRememberPassword,
549 kDontUseLoginPassword));
Lutz Justenc5690502019-05-24 17:00:54 +0200550 EXPECT_TRUE(base::PathExists(password_path_));
551
552 // This should load stored password, but erase it afterwards.
553 EXPECT_EQ(ERROR_NONE,
Lutz Justen04afd272019-06-14 16:31:26 +0200554 manager_->AcquireTgt(kUser, kEmptyPassword, kDontRememberPassword,
555 kDontUseLoginPassword));
Lutz Justenc5690502019-05-24 17:00:54 +0200556 EXPECT_FALSE(base::PathExists(password_path_));
557
558 // Check that the fake krb5 interface returns an error for a missing password.
559 // This verifies that the above AcquireTgt() call actually loaded the
560 // password from disk.
561 EXPECT_EQ(ERROR_BAD_PASSWORD,
Lutz Justen04afd272019-06-14 16:31:26 +0200562 manager_->AcquireTgt(kUser, kEmptyPassword, kDontRememberPassword,
563 kDontUseLoginPassword));
564}
565
566// AcquireTgt() uses the login password if saved.
567TEST_F(AccountManagerTest, AcquireTgtUsesLoginPassword) {
568 ignore_result(AddAccount());
569
570 // Shouldn't explode if the login password not set yet.
571 EXPECT_EQ(ERROR_BAD_PASSWORD,
572 manager_->AcquireTgt(kUser, kEmptyPassword, kDontRememberPassword,
573 kUseLoginPassword));
574
575 SaveLoginPassword(kPassword);
576 krb5_->set_expected_password(kPassword);
577
578 // Uses the login password.
579 EXPECT_EQ(ERROR_NONE,
580 manager_->AcquireTgt(kUser, kEmptyPassword, kDontRememberPassword,
581 kUseLoginPassword));
582
583 // Check if auth fails without kUseLoginPassword.
584 EXPECT_EQ(ERROR_BAD_PASSWORD,
585 manager_->AcquireTgt(kUser, kEmptyPassword, kDontRememberPassword,
586 kDontUseLoginPassword));
587}
588
589// AcquireTgt() wipes a saved password if the login password is used.
590TEST_F(AccountManagerTest, AcquireTgtWipesStoredPasswordOnUsesLoginPassword) {
591 ignore_result(AddAccount());
592 ignore_result(manager_->AcquireTgt(kUser, kPassword, kRememberPassword,
593 kDontUseLoginPassword));
594 EXPECT_TRUE(base::PathExists(password_path_));
595
596 SaveLoginPassword(kPassword);
597
598 // Note: kRememberPassword gets ignored if kUseLoginPassword is passed.
599 EXPECT_EQ(ERROR_NONE,
600 manager_->AcquireTgt(kUser, kEmptyPassword, kRememberPassword,
601 kUseLoginPassword));
602 EXPECT_FALSE(base::PathExists(password_path_));
603}
604
605// AcquireTgt() ignores the passed password if the login password is used.
606TEST_F(AccountManagerTest, AcquireTgtIgnoresPassedPasswordOnUsesLoginPassword) {
607 ignore_result(AddAccount());
608
609 SaveLoginPassword(kPassword);
610 krb5_->set_expected_password(kPassword);
611
612 // Auth works despite passed kPassword2 != expected kPassword because the
613 // login kPassword is used.
614 EXPECT_EQ(ERROR_NONE,
615 manager_->AcquireTgt(kUser, kPassword2, kDontRememberPassword,
616 kUseLoginPassword));
Lutz Justenc5690502019-05-24 17:00:54 +0200617}
618
Felipe Andrade66aaf6b2020-03-24 13:05:57 +0100619// AcquireTgt() records all encryption types UMA stats on success.
620TEST_F(AccountManagerTest, AcquireTgtEnctypesMetricsAll) {
621 ignore_result(AddAccount());
622 ignore_result(SetConfig());
623
624 // The expected encryption type should be reported through |metric_|.
625 EXPECT_CALL(*metrics_,
626 ReportKerberosEncryptionTypes(KerberosEncryptionTypes::kAll));
627
628 EXPECT_EQ(ERROR_NONE, AcquireTgt());
629}
630
631// AcquireTgt() records strong encryption types UMA stats on success.
632TEST_F(AccountManagerTest, AcquireTgtEnctypesMetricsStrong) {
633 ignore_result(AddAccount());
634 ignore_result(SetConfig(kStrongKrb5Conf));
635
636 // The expected encryption type should be reported through |metric_|.
637 EXPECT_CALL(*metrics_,
638 ReportKerberosEncryptionTypes(KerberosEncryptionTypes::kStrong));
639
640 EXPECT_EQ(ERROR_NONE, AcquireTgt());
641}
642
643// AcquireTgt() records legacy encryption types UMA stats on success.
644TEST_F(AccountManagerTest, AcquireTgtEnctypesMetricsLegacy) {
645 ignore_result(AddAccount());
646 ignore_result(SetConfig(kLegacyKrb5Conf));
647
648 // The expected encryption type should be reported through |metric_|.
649 EXPECT_CALL(*metrics_,
650 ReportKerberosEncryptionTypes(KerberosEncryptionTypes::kLegacy));
651
652 EXPECT_EQ(ERROR_NONE, AcquireTgt());
653}
654
655// AcquireTgt() doesn't record encryption types UMA stats on failure.
656TEST_F(AccountManagerTest, AcquireTgtEnctypesMetricsFailure) {
657 ignore_result(AddAccount());
658 ignore_result(SetConfig());
659
660 // No encryption type should be reported through |metric_|.
661 EXPECT_CALL(*metrics_, ReportKerberosEncryptionTypes(_)).Times(0);
662
663 krb5_->set_acquire_tgt_error(ERROR_UNKNOWN);
664 EXPECT_EQ(ERROR_UNKNOWN, AcquireTgt());
665}
666
667// AcquireTgt() doesn't record encryption types UMA stats if no config is
668// available.
669TEST_F(AccountManagerTest, AcquireTgtEnctypesMetricsNoConfig) {
670 ignore_result(AddAccount());
671
672 // No encryption type should be reported through |metric_|.
673 EXPECT_CALL(*metrics_, ReportKerberosEncryptionTypes(_)).Times(0);
674
675 EXPECT_EQ(ERROR_NONE, AcquireTgt());
676}
677
Felipe Andrade90cb84e2020-04-07 20:28:33 +0200678// AcquireTgt() doesn't record encryption types UMA stats if config is invalid.
679TEST_F(AccountManagerTest, AcquireTgtEnctypesMetricsInvalidConfig) {
680 ignore_result(AddAccount());
681 ignore_result(SetConfig(kInvalidKrb5Conf));
682
683 // No encryption type should be reported through |metric_|.
684 EXPECT_CALL(*metrics_, ReportKerberosEncryptionTypes(_)).Times(0);
685
686 EXPECT_EQ(ERROR_NONE, AcquireTgt());
687}
688
Lutz Justen6f582982019-05-15 10:24:22 +0200689// RemoveAccount() removes the credential cache file.
690TEST_F(AccountManagerTest, RemoveAccountRemovesCC) {
Lutz Justen04afd272019-06-14 16:31:26 +0200691 ignore_result(AddAccount());
692 ignore_result(AcquireTgt());
Lutz Justen6f582982019-05-15 10:24:22 +0200693
694 EXPECT_TRUE(base::PathExists(krb5cc_path_));
695 EXPECT_EQ(ERROR_NONE, manager_->RemoveAccount(kUser));
696 EXPECT_FALSE(base::PathExists(krb5cc_path_));
697}
698
Lutz Justenc5690502019-05-24 17:00:54 +0200699// RemoveAccount() removes saved passwords.
700TEST_F(AccountManagerTest, RemoveAccountRemovesPassword) {
Lutz Justen04afd272019-06-14 16:31:26 +0200701 ignore_result(AddAccount());
702 ignore_result(manager_->AcquireTgt(kUser, kPassword, kRememberPassword,
703 kDontUseLoginPassword));
Lutz Justenc5690502019-05-24 17:00:54 +0200704
705 EXPECT_TRUE(base::PathExists(password_path_));
706 EXPECT_EQ(ERROR_NONE, manager_->RemoveAccount(kUser));
707 EXPECT_FALSE(base::PathExists(password_path_));
708}
709
Lutz Justen6f582982019-05-15 10:24:22 +0200710// ListAccounts() succeeds and contains the expected data.
711TEST_F(AccountManagerTest, ListAccountsSuccess) {
Lutz Justen04afd272019-06-14 16:31:26 +0200712 ignore_result(manager_->AddAccount(kUser, kManaged));
713 ignore_result(SetConfig());
714 ignore_result(manager_->AcquireTgt(kUser, kPassword, kRememberPassword,
715 kDontUseLoginPassword));
716 SaveLoginPassword(kPassword);
717 ignore_result(manager_->AddAccount(kUser2, kUnmanaged));
718 // Note: kRememberPassword should be ignored here, see below.
719 ignore_result(manager_->AcquireTgt(kUser2, kPassword, kRememberPassword,
720 kUseLoginPassword));
Lutz Justen6f582982019-05-15 10:24:22 +0200721 EXPECT_TRUE(base::PathExists(krb5cc_path_));
722
723 // Set a fake tgt status.
724 constexpr int kRenewalSeconds = 10;
725 constexpr int kValiditySeconds = 90;
Lutz Justend925d712019-06-27 17:34:00 +0200726 krb5_->set_tgt_status(
727 Krb5Interface::TgtStatus(kValiditySeconds, kRenewalSeconds));
Lutz Justen6f582982019-05-15 10:24:22 +0200728
729 // Verify that ListAccounts returns the expected account.
Felipe Andrade89399f02019-11-11 15:52:14 +0100730 std::vector<Account> accounts = manager_->ListAccounts();
Lutz Justen04afd272019-06-14 16:31:26 +0200731 ASSERT_EQ(2u, accounts.size());
732
Lutz Justen6f582982019-05-15 10:24:22 +0200733 EXPECT_EQ(kUser, accounts[0].principal_name());
734 EXPECT_EQ(kKrb5Conf, accounts[0].krb5conf());
735 EXPECT_EQ(kRenewalSeconds, accounts[0].tgt_renewal_seconds());
736 EXPECT_EQ(kValiditySeconds, accounts[0].tgt_validity_seconds());
737 EXPECT_TRUE(accounts[0].is_managed());
Lutz Justenc5690502019-05-24 17:00:54 +0200738 EXPECT_TRUE(accounts[0].password_was_remembered());
Lutz Justen04afd272019-06-14 16:31:26 +0200739
740 EXPECT_EQ(kUser2, accounts[1].principal_name());
741 EXPECT_FALSE(accounts[1].password_was_remembered());
742 EXPECT_TRUE(accounts[1].use_login_password());
Lutz Justen6f582982019-05-15 10:24:22 +0200743}
744
745// ListAccounts() ignores failures in GetTgtStatus() and loading the config.
746TEST_F(AccountManagerTest, ListAccountsIgnoresFailures) {
Lutz Justen04afd272019-06-14 16:31:26 +0200747 ignore_result(AddAccount());
748 ignore_result(SetConfig());
749 ignore_result(AcquireTgt());
Lutz Justen6f582982019-05-15 10:24:22 +0200750 EXPECT_TRUE(base::PathExists(krb5cc_path_));
751
752 // Make reading the config fail.
753 EXPECT_TRUE(base::SetPosixFilePermissions(krb5conf_path_, 0));
754
755 // Make GetTgtStatus() fail.
756 krb5_->set_get_tgt_status_error(ERROR_UNKNOWN);
757
758 // ListAccounts() should still work, despite the errors.
Felipe Andrade89399f02019-11-11 15:52:14 +0100759 std::vector<Account> accounts = manager_->ListAccounts();
Lutz Justen6f582982019-05-15 10:24:22 +0200760 ASSERT_EQ(1u, accounts.size());
761 EXPECT_EQ(kUser, accounts[0].principal_name());
762
763 // The config should not be set since we made reading the file fail.
764 EXPECT_FALSE(accounts[0].has_krb5conf());
765
766 // tgt_*_seconds should not be set since we made GetTgtStatus() fail.
767 EXPECT_FALSE(accounts[0].has_tgt_renewal_seconds());
768 EXPECT_FALSE(accounts[0].has_tgt_validity_seconds());
769}
770
771// GetKerberosFiles returns empty KerberosFiles if there is no credential cache,
772// even if there is a config.
Felipe Andrade66aaf6b2020-03-24 13:05:57 +0100773TEST_F(AccountManagerTest, GetKerberosFilesSucceedsWithoutCC) {
Lutz Justen04afd272019-06-14 16:31:26 +0200774 ignore_result(AddAccount());
775 ignore_result(SetConfig());
Lutz Justen6f582982019-05-15 10:24:22 +0200776
777 KerberosFiles files;
778 EXPECT_EQ(ERROR_NONE, manager_->GetKerberosFiles(kUser, &files));
779 EXPECT_FALSE(files.has_krb5cc());
780 EXPECT_FALSE(files.has_krb5conf());
781}
782
783// GetKerberosFiles returns the expected KerberosFiles if there is a credential
784// cache.
Felipe Andrade66aaf6b2020-03-24 13:05:57 +0100785TEST_F(AccountManagerTest, GetKerberosFilesSucceedsWithCC) {
Lutz Justen04afd272019-06-14 16:31:26 +0200786 ignore_result(AddAccount());
787 ignore_result(SetConfig());
788 ignore_result(AcquireTgt());
Lutz Justen6f582982019-05-15 10:24:22 +0200789
790 KerberosFiles files;
791 EXPECT_EQ(ERROR_NONE, manager_->GetKerberosFiles(kUser, &files));
792 EXPECT_FALSE(files.krb5cc().empty());
793 EXPECT_EQ(kKrb5Conf, files.krb5conf());
794}
795
796// Most methods return ERROR_UNKNOWN_PRINCIPAL if called with such a principal.
797TEST_F(AccountManagerTest, MethodsReturnUnknownPrincipal) {
798 KerberosFiles files;
799 EXPECT_EQ(ERROR_UNKNOWN_PRINCIPAL_NAME, manager_->RemoveAccount(kUser));
Lutz Justen04afd272019-06-14 16:31:26 +0200800 EXPECT_EQ(ERROR_UNKNOWN_PRINCIPAL_NAME, SetConfig());
801 EXPECT_EQ(ERROR_UNKNOWN_PRINCIPAL_NAME, AcquireTgt());
Lutz Justen6f582982019-05-15 10:24:22 +0200802 EXPECT_EQ(ERROR_UNKNOWN_PRINCIPAL_NAME,
803 manager_->GetKerberosFiles(kUser, &files));
804}
805
806// Accounts can be saved to disk and loaded from disk.
807TEST_F(AccountManagerTest, SerializationSuccess) {
Lutz Justen04afd272019-06-14 16:31:26 +0200808 ignore_result(manager_->AddAccount(kUser, kManaged));
809 ignore_result(manager_->AcquireTgt(kUser, kPassword, kDontRememberPassword,
810 kUseLoginPassword));
811
812 ignore_result(manager_->AddAccount(kUser2, kUnmanaged));
813 ignore_result(manager_->AcquireTgt(kUser2, kPassword, kDontRememberPassword,
814 kDontUseLoginPassword));
Lutz Justen6f582982019-05-15 10:24:22 +0200815
816 EXPECT_EQ(ERROR_NONE, manager_->SaveAccounts());
Lutz Justen04afd272019-06-14 16:31:26 +0200817 AccountManager other_manager(
818 storage_dir_.GetPath(), kerberos_files_changed_,
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200819 kerberos_ticket_expiring_, std::make_unique<FakeKrb5Interface>(),
Lutz Justen2d857e12019-07-16 15:58:09 +0200820 std::make_unique<password_provider::FakePasswordProvider>(),
821 metrics_.get());
Lutz Justen6f582982019-05-15 10:24:22 +0200822 other_manager.LoadAccounts();
Felipe Andrade89399f02019-11-11 15:52:14 +0100823 std::vector<Account> accounts = other_manager.ListAccounts();
Lutz Justen6f582982019-05-15 10:24:22 +0200824 ASSERT_EQ(2u, accounts.size());
825
826 EXPECT_EQ(kUser, accounts[0].principal_name());
827 EXPECT_EQ(kUser2, accounts[1].principal_name());
828
829 EXPECT_TRUE(accounts[0].is_managed());
830 EXPECT_FALSE(accounts[1].is_managed());
831
Lutz Justen04afd272019-06-14 16:31:26 +0200832 EXPECT_TRUE(accounts[0].use_login_password());
833 EXPECT_FALSE(accounts[1].use_login_password());
834
Lutz Justen6f582982019-05-15 10:24:22 +0200835 // TODO(https://crbug.com/952239): Check additional Account properties.
836}
Lutz Justenb7ef5802019-05-14 14:03:20 +0200837
Lutz Justend925d712019-06-27 17:34:00 +0200838// The StartObservingTickets() method triggers KerberosTicketExpiring for
839// expired signals and starts observing valid tickets.
840TEST_F(AccountManagerTest, StartObservingTickets) {
841 krb5_->set_tgt_status(kValidTgt);
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200842 ignore_result(AddAccount());
843 ignore_result(SetConfig());
844 ignore_result(AcquireTgt());
845 EXPECT_EQ(0, kerberos_ticket_expiring_count_[kUser]);
Lutz Justend925d712019-06-27 17:34:00 +0200846 task_runner_->ClearPendingTasks();
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200847
Lutz Justend925d712019-06-27 17:34:00 +0200848 // Fake an expired ticket. Check that KerberosTicketExpiring is triggered, but
849 // no renewal task is scheduled.
850 krb5_->set_tgt_status(kExpiredTgt);
851 manager_->StartObservingTickets();
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200852 EXPECT_EQ(1, kerberos_ticket_expiring_count_[kUser]);
Lutz Justend925d712019-06-27 17:34:00 +0200853 EXPECT_EQ(0, task_runner_->GetPendingTaskCount());
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200854
Lutz Justend925d712019-06-27 17:34:00 +0200855 // Fake a valid ticket. Check that KerberosTicketExpiring is NOT triggered,
856 // but a renewal task is scheduled.
857 krb5_->set_tgt_status(kValidTgt);
858 EXPECT_EQ(0, task_runner_->GetPendingTaskCount());
859 manager_->StartObservingTickets();
860 EXPECT_EQ(1, task_runner_->GetPendingTaskCount());
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200861 EXPECT_EQ(1, kerberos_ticket_expiring_count_[kUser]);
Lutz Justend925d712019-06-27 17:34:00 +0200862 EXPECT_EQ(0, krb5_->renew_tgt_call_count());
863 task_runner_->FastForwardBy(task_runner_->NextPendingTaskDelay());
864 EXPECT_EQ(1, krb5_->renew_tgt_call_count());
865}
866
867// When a TGT is acquired successfully, automatic renewal is scheduled.
868TEST_F(AccountManagerTest, AcquireTgtSchedulesRenewalOnSuccess) {
869 ignore_result(AddAccount());
870
871 krb5_->set_tgt_status(kValidTgt);
872 EXPECT_EQ(0, task_runner_->GetPendingTaskCount());
873 EXPECT_EQ(ERROR_NONE, AcquireTgt());
874 EXPECT_EQ(1, task_runner_->GetPendingTaskCount());
875}
876
877// When a TGT fails to be acquired, no automatic renewal is scheduled.
878TEST_F(AccountManagerTest, AcquireTgtDoesNotScheduleRenewalOnFailure) {
879 ignore_result(AddAccount());
880
881 krb5_->set_tgt_status(kValidTgt);
882 krb5_->set_acquire_tgt_error(ERROR_UNKNOWN);
883 EXPECT_EQ(0, task_runner_->GetPendingTaskCount());
884 EXPECT_EQ(ERROR_UNKNOWN, AcquireTgt());
885 EXPECT_EQ(0, task_runner_->GetPendingTaskCount());
886}
887
888// A scheduled TGT renewal task calls |krb5_->RenewTgt()|.
889TEST_F(AccountManagerTest, AutoRenewalCallsRenewTgt) {
890 krb5_->set_tgt_status(kValidTgt);
891 ignore_result(AddAccount());
892 ignore_result(AcquireTgt());
893 int initial_acquire_tgt_call_count = krb5_->acquire_tgt_call_count();
894
895 // Set some return value for the RenewTgt() call and fast forward to scheduled
896 // renewal task.
897 const ErrorType expected_error = ERROR_UNKNOWN;
898 krb5_->set_renew_tgt_error(expected_error);
899 RunScheduledRenewalTask();
900
901 EXPECT_EQ(initial_acquire_tgt_call_count, krb5_->acquire_tgt_call_count());
902 EXPECT_EQ(expected_error, manager_->last_renew_tgt_error_for_testing());
903}
904
905// A scheduled TGT renewal task calls |krb5_->AcquireTgt()| using the login
906// password if the call to |krb5_->RenewTgt()| fails and the login password was
907// used for the initial AcquireTgt() call.
908TEST_F(AccountManagerTest, AutoRenewalUsesLoginPasswordIfRenewalFails) {
909 krb5_->set_tgt_status(kValidTgt);
910 ignore_result(AddAccount());
911
912 // Acquire TGT with login password.
913 SaveLoginPassword(kPassword);
914 krb5_->set_expected_password(kPassword);
915 EXPECT_EQ(ERROR_NONE,
916 manager_->AcquireTgt(kUser, std::string(), kDontRememberPassword,
917 kUseLoginPassword));
918 int initial_acquire_tgt_call_count = krb5_->acquire_tgt_call_count();
919
920 krb5_->set_renew_tgt_error(ERROR_UNKNOWN);
921 RunScheduledRenewalTask();
922
923 // The scheduled renewal task should have called AcquireTgt() with the login
924 // password and succeeded.
925 EXPECT_EQ(initial_acquire_tgt_call_count + 1,
926 krb5_->acquire_tgt_call_count());
927 EXPECT_EQ(ERROR_NONE, manager_->last_renew_tgt_error_for_testing());
928}
929
930// A scheduled TGT renewal task calls |krb5_->AcquireTgt()| using the remembered
931// password if the call to |krb5_->RenewTgt()| fails and the password was
932// remembered for the initial AcquireTgt() call.
933TEST_F(AccountManagerTest, AutoRenewalUsesRememberedPasswordIfRenewalFails) {
934 krb5_->set_tgt_status(kValidTgt);
935 ignore_result(AddAccount());
936
937 // Acquire TGT and remember password.
938 krb5_->set_expected_password(kPassword);
939 EXPECT_EQ(ERROR_NONE,
940 manager_->AcquireTgt(kUser, kPassword, kRememberPassword,
941 kDontUseLoginPassword));
942 int initial_acquire_tgt_call_count = krb5_->acquire_tgt_call_count();
943
944 krb5_->set_renew_tgt_error(ERROR_UNKNOWN);
945 RunScheduledRenewalTask();
946
947 // The scheduled renewal task should have called AcquireTgt() with the
948 // remembered password and succeeded.
949 EXPECT_EQ(initial_acquire_tgt_call_count + 1,
950 krb5_->acquire_tgt_call_count());
951 EXPECT_EQ(ERROR_NONE, manager_->last_renew_tgt_error_for_testing());
952}
953
954// A scheduled TGT renewal task does not call |krb5_->AcquireTgt()| using the
955// remembered password if the call to |krb5_->RenewTgt()| succeeds and the
956// password was remembered for the initial AcquireTgt() call (similar for login
957// password, but we don't test that).
958TEST_F(AccountManagerTest, AutoRenewalDoesNotCallAcquireTgtIfRenewalSucceeds) {
959 krb5_->set_tgt_status(kValidTgt);
960 ignore_result(AddAccount());
961
962 // Acquire TGT and remember password.
963 krb5_->set_expected_password(kPassword);
964 EXPECT_EQ(ERROR_NONE,
965 manager_->AcquireTgt(kUser, kPassword, kRememberPassword,
966 kDontUseLoginPassword));
967 int initial_acquire_tgt_call_count = krb5_->acquire_tgt_call_count();
968
969 krb5_->set_renew_tgt_error(ERROR_NONE);
970 RunScheduledRenewalTask();
971
972 // The scheduled renewal task should NOT have called AcquireTgt() again since
973 // |krb5_->RenewTgt()|.
974 EXPECT_EQ(initial_acquire_tgt_call_count, krb5_->acquire_tgt_call_count());
975 EXPECT_EQ(ERROR_NONE, manager_->last_renew_tgt_error_for_testing());
Lutz Justen8eeb4a22019-06-19 17:00:32 +0200976}
977
Lutz Justen33601802019-07-03 22:40:57 +0200978// Verifies that all files written have the expected access permissions.
979// Unfortunately, file ownership can't be tested as the test won't run as
980// kerberosd user nor can it switch to it.
981TEST_F(AccountManagerTest, FilePermissions) {
982 constexpr int kFileMode_rw =
983 base::FILE_PERMISSION_READ_BY_USER | base::FILE_PERMISSION_WRITE_BY_USER;
984 constexpr int kFileMode_rw_r =
985 kFileMode_rw | base::FILE_PERMISSION_READ_BY_GROUP;
986 constexpr int kFileMode_rw_r__r =
987 kFileMode_rw_r | base::FILE_PERMISSION_READ_BY_OTHERS;
988 constexpr int kFileMode_rwxrwx =
989 base::FILE_PERMISSION_USER_MASK | base::FILE_PERMISSION_GROUP_MASK;
990
991 // Wrap the fake krb5 in a jail wrapper to get the file permissions of krb5cc
992 // right. Note that we can't use a Krb5JailWrapper for the whole test since
993 // that would break the counters in FakeKrb5Interface (they would be inc'ed in
994 // another process!).
995 manager_->WrapKrb5ForTesting();
996
997 // Can't set user in this test.
998 Krb5JailWrapper::DisableChangeUserForTesting(true);
999
1000 EXPECT_EQ(ERROR_NONE, AddAccount());
1001 EXPECT_EQ(ERROR_NONE, SetConfig());
1002 EXPECT_EQ(ERROR_NONE,
1003 manager_->AcquireTgt(kUser, kPassword, kRememberPassword,
1004 kDontUseLoginPassword));
1005
1006 int mode;
1007
1008 EXPECT_TRUE(GetPosixFilePermissions(accounts_path_, &mode));
1009 EXPECT_EQ(kFileMode_rw, mode);
1010
1011 EXPECT_TRUE(GetPosixFilePermissions(account_dir_, &mode));
1012 EXPECT_EQ(kFileMode_rwxrwx, mode);
1013
1014 EXPECT_TRUE(GetPosixFilePermissions(krb5cc_path_, &mode));
1015 EXPECT_EQ(kFileMode_rw_r, mode);
1016
1017 EXPECT_TRUE(GetPosixFilePermissions(krb5conf_path_, &mode));
1018 EXPECT_EQ(kFileMode_rw_r__r, mode);
1019
1020 EXPECT_TRUE(GetPosixFilePermissions(password_path_, &mode));
1021 EXPECT_EQ(kFileMode_rw, mode);
1022}
1023
Lutz Justen2d857e12019-07-16 15:58:09 +02001024// Tests that [Should]ReportDailyUsageStats is called as advertised.
1025TEST_F(AccountManagerTest, ReportDailyUsageStats) {
1026 // ShouldReportDailyUsageStats() should be called by GetKerberosFiles() even
1027 // if there is no account, and if that returns true, ReportDailyUsageStats()
1028 // should be called as well.
1029 EXPECT_CALL(*metrics_, ShouldReportDailyUsageStats()).WillOnce(Return(true));
1030 EXPECT_CALL(*metrics_, ReportDailyUsageStats(0, 0, 0, 0, 0));
1031 KerberosFiles files;
1032 EXPECT_EQ(ERROR_UNKNOWN_PRINCIPAL_NAME,
1033 manager_->GetKerberosFiles(kUser, &files));
1034 Mock::VerifyAndClearExpectations(metrics_.get());
1035
1036 AddAccount();
1037
1038 // ShouldReportDailyUsageStats() should be called by AcquireTgt(), but if that
1039 // returns false, ReportDailyUsageStats() should NOT be called.
1040 EXPECT_CALL(*metrics_, ShouldReportDailyUsageStats()).WillOnce(Return(false));
1041 EXPECT_CALL(*metrics_, ReportDailyUsageStats(_, _, _, _, _)).Times(0);
1042 AcquireTgt();
1043 Mock::VerifyAndClearExpectations(metrics_.get());
1044
1045 // ShouldReportDailyUsageStats() should be called by AcquireTgt(), and if that
1046 // returns true, ReportDailyUsageStats() should be called as well.
1047 EXPECT_CALL(*metrics_, ShouldReportDailyUsageStats()).WillOnce(Return(true));
1048 EXPECT_CALL(*metrics_, ReportDailyUsageStats(_, _, _, _, _));
1049 AcquireTgt();
1050 Mock::VerifyAndClearExpectations(metrics_.get());
1051}
1052
1053TEST_F(AccountManagerTest, AccountStats) {
1054 SaveLoginPassword(kPassword);
1055
1056 EXPECT_EQ(ERROR_NONE, manager_->AddAccount(kUser, kManaged));
1057 EXPECT_EQ(ERROR_NONE, manager_->AddAccount(kUser2, kManaged));
1058 EXPECT_EQ(ERROR_NONE, manager_->AddAccount(kUser3, kUnmanaged));
1059
1060 // Set metrics up so that the stats are reported on the last call.
1061 EXPECT_CALL(*metrics_, ShouldReportDailyUsageStats())
1062 .WillOnce(Return(false))
1063 .WillOnce(Return(false))
1064 .WillOnce(Return(true));
1065
1066 // 3 accounts total, 2 managed, 1 unmanaged, 1 remembering the password, 1
1067 // using the login password.
1068 EXPECT_CALL(*metrics_, ReportDailyUsageStats(3, 2, 1, 1, 1));
1069
1070 EXPECT_EQ(ERROR_NONE,
1071 manager_->AcquireTgt(kUser, kPassword, kDontRememberPassword,
1072 kDontUseLoginPassword));
1073 EXPECT_EQ(ERROR_NONE,
1074 manager_->AcquireTgt(kUser2, kPassword, kRememberPassword,
1075 kDontUseLoginPassword));
1076 EXPECT_EQ(ERROR_NONE,
1077 manager_->AcquireTgt(kUser3, kEmptyPassword, kDontRememberPassword,
1078 kUseLoginPassword));
1079}
1080
Lutz Justenb7ef5802019-05-14 14:03:20 +02001081} // namespace kerberos