| // Copyright 2019 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "kerberos/account_manager.h" | |
| #include <limits> | |
| #include <utility> | |
| #include <base/base64.h> | |
| #include <base/check.h> | |
| #include <base/containers/contains.h> | |
| #include <base/files/file_util.h> | |
| #include <base/logging.h> | |
| #include <base/strings/string_util.h> | |
| #include <libpasswordprovider/password.h> | |
| #include <libpasswordprovider/password_provider.h> | |
| #include "kerberos/error_strings.h" | |
| #include "kerberos/kerberos_metrics.h" | |
| #include "kerberos/krb5_interface.h" | |
| #include "kerberos/krb5_jail_wrapper.h" | |
| namespace kerberos { | |
| namespace { | |
| constexpr int kInvalidIndex = -1; | |
| constexpr int kFileMode_rw = | |
| base::FILE_PERMISSION_READ_BY_USER | base::FILE_PERMISSION_WRITE_BY_USER; | |
| constexpr int kFileMode_rwxrwx = | |
| base::FILE_PERMISSION_USER_MASK | base::FILE_PERMISSION_GROUP_MASK; | |
| // Kerberos config files are stored as storage_dir/account_dir/this. | |
| constexpr char kKrb5ConfFilePart[] = "krb5.conf"; | |
| // Kerberos credential caches are stored as storage_dir/account_dir/this. | |
| constexpr char kKrb5CCFilePart[] = "krb5cc"; | |
| // Passwords are stored as storage_dir/account_dir/this. | |
| constexpr char kPasswordFilePart[] = "password"; | |
| // Account data is stored as storage_dir + this. | |
| constexpr char kAccountsFile[] = "accounts"; | |
| // Size limit for file (1 MB). | |
| constexpr size_t kFileSizeLimit = 1024 * 1024; | |
| // Returns the base64 encoded |principal_name|. This is used to create safe | |
| // filenames while at the same time allowing easy debugging. | |
| std::string GetSafeFilename(const std::string& principal_name) { | |
| return base::Base64Encode(principal_name); | |
| } | |
| // Reads the file at |path| into |data|. Returns |ERROR_LOCAL_IO| if the file | |
| // could not be read. | |
| [[nodiscard]] ErrorType LoadFile(const base::FilePath& path, | |
| std::string* data) { | |
| data->clear(); | |
| if (!base::ReadFileToStringWithMaxSize(path, data, kFileSizeLimit)) { | |
| PLOG(ERROR) << "Failed to read " << path.value(); | |
| data->clear(); | |
| return ERROR_LOCAL_IO; | |
| } | |
| return ERROR_NONE; | |
| } | |
| // Writes |data| to the file at |path|. Returns |ERROR_LOCAL_IO| if the file | |
| // could not be written. | |
| [[nodiscard]] ErrorType SaveFile(const base::FilePath& path, | |
| const std::string& data) { | |
| const int data_size = static_cast<int>(data.size()); | |
| if (base::WriteFile(path, data.data(), data_size) != data_size) { | |
| LOG(ERROR) << "Failed to write '" << path.value() << "'"; | |
| return ERROR_LOCAL_IO; | |
| } | |
| return ERROR_NONE; | |
| } | |
| // Sets file permissions for a given |path|. Returns ERROR_LOCAL_IO on error. | |
| [[nodiscard]] ErrorType SetFilePermissions(const base::FilePath& path, | |
| int mode) { | |
| if (!base::SetPosixFilePermissions(path, mode)) { | |
| LOG(ERROR) << "Failed to set permissions on '" << path.value() << "'"; | |
| return ERROR_LOCAL_IO; | |
| } | |
| return ERROR_NONE; | |
| } | |
| } // namespace | |
| AccountManager::AccountManager( | |
| base::FilePath storage_dir, | |
| KerberosFilesChangedCallback kerberos_files_changed, | |
| KerberosTicketExpiringCallback kerberos_ticket_expiring, | |
| std::unique_ptr<Krb5Interface> krb5, | |
| std::unique_ptr<password_provider::PasswordProviderInterface> | |
| password_provider, | |
| KerberosMetrics* metrics) | |
| : storage_dir_(std::move(storage_dir)), | |
| accounts_path_(storage_dir_.Append(kAccountsFile)), | |
| kerberos_files_changed_(std::move(kerberos_files_changed)), | |
| kerberos_ticket_expiring_(std::move(kerberos_ticket_expiring)), | |
| krb5_(std::move(krb5)), | |
| password_provider_(std::move(password_provider)), | |
| metrics_(metrics) { | |
| DCHECK(kerberos_files_changed_); | |
| DCHECK(kerberos_ticket_expiring_); | |
| } | |
| AccountManager::~AccountManager() = default; | |
| ErrorType AccountManager::SaveAccounts() const { | |
| // Copy |accounts_| into proto message. | |
| AccountDataList storage_accounts; | |
| for (const auto& account : accounts_) | |
| *storage_accounts.add_accounts() = account.data; | |
| // Store serialized proto message on disk. | |
| std::string accounts_blob; | |
| if (!storage_accounts.SerializeToString(&accounts_blob)) { | |
| LOG(ERROR) << "Failed to serialize accounts list to string"; | |
| return ERROR_LOCAL_IO; | |
| } | |
| ErrorType error = SaveFile(accounts_path_, accounts_blob); | |
| if (error != ERROR_NONE) | |
| return error; | |
| // Remove group and other read access. This prevents kerberosd-exec from | |
| // reading it (it's none of its business). | |
| return SetFilePermissions(accounts_path_, kFileMode_rw); | |
| } | |
| ErrorType AccountManager::LoadAccounts() { | |
| accounts_.clear(); | |
| // A missing file counts as a file with empty data. | |
| if (!base::PathExists(accounts_path_)) | |
| return ERROR_NONE; | |
| // Load serialized proto blob. | |
| std::string accounts_blob; | |
| ErrorType error = LoadFile(accounts_path_, &accounts_blob); | |
| if (error != ERROR_NONE) | |
| return error; | |
| // Parse blob into proto message. | |
| AccountDataList storage_accounts; | |
| if (!storage_accounts.ParseFromString(accounts_blob)) { | |
| LOG(ERROR) << "Failed to parse accounts list from string"; | |
| return ERROR_LOCAL_IO; | |
| } | |
| // Copy data into |accounts_|. | |
| accounts_.reserve(storage_accounts.accounts_size()); | |
| for (int n = 0; n < storage_accounts.accounts_size(); ++n) { | |
| accounts_.emplace_back(std::move(*storage_accounts.mutable_accounts(n)), | |
| this); | |
| } | |
| return ERROR_NONE; | |
| } | |
| ErrorType AccountManager::AddAccount(const std::string& principal_name, | |
| bool is_managed) { | |
| int index = GetAccountIndex(principal_name); | |
| if (index != kInvalidIndex) { | |
| // Policy should overwrite user-added accounts, but user-added accounts | |
| // should not overwrite policy accounts. | |
| if (!accounts_[index].data.is_managed() && is_managed) { | |
| DeleteAllFilesFor(principal_name); | |
| accounts_[index].data.set_is_managed(is_managed); | |
| SaveAccounts(); | |
| } | |
| return ERROR_DUPLICATE_PRINCIPAL_NAME; | |
| } | |
| // Create the account directory. | |
| const base::FilePath account_dir = GetAccountDir(principal_name); | |
| base::File::Error ferror; | |
| if (!base::CreateDirectoryAndGetError(account_dir, &ferror)) { | |
| LOG(ERROR) << "Failed to create directory '" << account_dir.value() | |
| << "': " << base::File::ErrorToString(ferror); | |
| return ERROR_LOCAL_IO; | |
| } | |
| // The account directory needs to be group accessible since kinit runs as | |
| // kerberosd-exec user and wants to write krbcc into that directory. | |
| ErrorType error = SetFilePermissions(account_dir, kFileMode_rwxrwx); | |
| if (error != ERROR_NONE) { | |
| base::DeletePathRecursively(account_dir); | |
| return error; | |
| } | |
| // Create account record. | |
| AccountData data; | |
| data.set_principal_name(principal_name); | |
| data.set_is_managed(is_managed); | |
| accounts_.emplace_back(std::move(data), this); | |
| SaveAccounts(); | |
| return ERROR_NONE; | |
| } | |
| ErrorType AccountManager::RemoveAccount(const std::string& principal_name) { | |
| int index = GetAccountIndex(principal_name); | |
| if (index == kInvalidIndex) | |
| return ERROR_UNKNOWN_PRINCIPAL_NAME; | |
| DeleteAllFilesFor(principal_name); | |
| accounts_.erase(accounts_.begin() + index); | |
| SaveAccounts(); | |
| return ERROR_NONE; | |
| } | |
| void AccountManager::DeleteAllFilesFor(const std::string& principal_name) { | |
| const bool krb5cc_existed = base::PathExists(GetKrb5CCPath(principal_name)); | |
| CHECK(base::DeletePathRecursively(GetAccountDir(principal_name))); | |
| if (krb5cc_existed) | |
| TriggerKerberosFilesChanged(principal_name); | |
| } | |
| ErrorType AccountManager::ClearAccounts( | |
| ClearMode mode, std::unordered_set<std::string> keep_list) { | |
| // Early out. | |
| if (accounts_.size() == 0) | |
| return ERROR_NONE; | |
| for (auto it = accounts_.begin(); it != accounts_.end(); /* empty */) { | |
| if (base::Contains(keep_list, it->data.principal_name())) { | |
| ++it; | |
| continue; | |
| } | |
| switch (DetermineWhatToRemove(mode, *it)) { | |
| case WhatToRemove::kNothing: | |
| ++it; | |
| continue; | |
| case WhatToRemove::kPassword: | |
| CHECK(base::DeleteFile(GetPasswordPath(it->data.principal_name()))); | |
| ++it; | |
| continue; | |
| case WhatToRemove::kAccount: | |
| DeleteAllFilesFor(it->data.principal_name()); | |
| it = accounts_.erase(it); | |
| continue; | |
| } | |
| } | |
| SaveAccounts(); | |
| return ERROR_NONE; | |
| } | |
| std::vector<Account> AccountManager::ListAccounts() const { | |
| std::vector<Account> accounts; | |
| for (const auto& it : accounts_) { | |
| Account account; | |
| account.set_principal_name(it.data.principal_name()); | |
| account.set_is_managed(it.data.is_managed()); | |
| account.set_password_was_remembered( | |
| base::PathExists(GetPasswordPath(it.data.principal_name()))); | |
| account.set_use_login_password(it.data.use_login_password()); | |
| // Do a best effort reporting results, don't bail on the first error. If | |
| // there's a broken account, the user is able to recover the situation | |
| // this way (reauthenticate or remove account and add back). | |
| // Check PathExists, so that no error is printed if the file doesn't exist. | |
| std::string krb5conf; | |
| const base::FilePath krb5conf_path = | |
| GetKrb5ConfPath(it.data.principal_name()); | |
| if (base::PathExists(krb5conf_path) && | |
| LoadFile(krb5conf_path, &krb5conf) == ERROR_NONE) { | |
| account.set_krb5conf(krb5conf); | |
| } | |
| // A missing krb5cc file just translates to an invalid ticket (lifetime 0). | |
| Krb5Interface::TgtStatus tgt_status; | |
| const base::FilePath krb5cc_path = GetKrb5CCPath(it.data.principal_name()); | |
| if (base::PathExists(krb5cc_path) && | |
| krb5_->GetTgtStatus(krb5cc_path, &tgt_status) == ERROR_NONE) { | |
| account.set_tgt_validity_seconds(tgt_status.validity_seconds); | |
| account.set_tgt_renewal_seconds(tgt_status.renewal_seconds); | |
| } | |
| accounts.push_back(std::move(account)); | |
| } | |
| return accounts; | |
| } | |
| ErrorType AccountManager::SetConfig(const std::string& principal_name, | |
| const std::string& krb5conf) { | |
| InternalAccount* account = GetMutableAccount(principal_name); | |
| if (!account) | |
| return ERROR_UNKNOWN_PRINCIPAL_NAME; | |
| // Saving the krb5 configuration is done even if the syntax is invalid. | |
| ErrorType error; | |
| error = SaveFile(GetKrb5ConfPath(principal_name), krb5conf); | |
| if (error != ERROR_NONE) | |
| return error; | |
| // Triggering the signal is only necessary if the credential cache exists. | |
| if (base::PathExists(GetKrb5CCPath(principal_name))) | |
| TriggerKerberosFilesChanged(principal_name); | |
| // Validate configuration to make sure it doesn't contain invalid options. | |
| ConfigErrorInfo error_info; | |
| error = krb5_->ValidateConfig(krb5conf, &error_info); | |
| // If the configuration is invalid we still want to keep it, but it needs to | |
| // be marked as invalid so that it can't be used for acquiring tickets. | |
| if (error != ERROR_NONE) | |
| account->data.set_is_config_valid(false); | |
| else | |
| account->data.set_is_config_valid(true); | |
| SaveAccounts(); | |
| return ERROR_NONE; | |
| } | |
| ErrorType AccountManager::ValidateConfig(const std::string& krb5conf, | |
| ConfigErrorInfo* error_info) const { | |
| return krb5_->ValidateConfig(krb5conf, error_info); | |
| } | |
| ErrorType AccountManager::AcquireTgt(const std::string& principal_name, | |
| std::string password, | |
| bool remember_password, | |
| bool use_login_password) { | |
| InternalAccount* account = GetMutableAccount(principal_name); | |
| if (!account) | |
| return ERROR_UNKNOWN_PRINCIPAL_NAME; | |
| if (!account->data.is_config_valid()) | |
| return ERROR_BAD_CONFIG; | |
| // Remember whether to use the login password. | |
| if (account->data.use_login_password() != use_login_password) { | |
| account->data.set_use_login_password(use_login_password); | |
| SaveAccounts(); | |
| } | |
| ErrorType error = use_login_password | |
| ? UpdatePasswordFromLogin(principal_name, &password) | |
| : UpdatePasswordFromSaved(principal_name, | |
| remember_password, &password); | |
| if (error != ERROR_NONE) | |
| return error; | |
| // Acquire a Kerberos ticket-granting-ticket. | |
| error = | |
| krb5_->AcquireTgt(principal_name, password, GetKrb5CCPath(principal_name), | |
| GetKrb5ConfPath(principal_name)); | |
| if (error == ERROR_NONE) { | |
| // Schedule task to automatically renew the ticket. If the ticket is invalid | |
| // for whatever reason, don't notify expiration immediately. This might lead | |
| // to an infinite loop when a password is stored and MaybeAutoAcquireTgt | |
| // tries to acquire a new TGT immediately. | |
| account->tgt_renewal_scheduler_->ScheduleRenewal( | |
| false /* notify_expiration */); | |
| // Assume the ticket changed if AcquireTgt() was successful. | |
| TriggerKerberosFilesChanged(principal_name); | |
| std::string krb5conf; | |
| ErrorType load_config_error = | |
| LoadFile(GetKrb5ConfPath(principal_name), &krb5conf); | |
| if (load_config_error == ERROR_NONE) { | |
| KerberosEncryptionTypes encryption_types; | |
| bool success = | |
| config_parser_.GetEncryptionTypes(krb5conf, &encryption_types); | |
| if (success) { | |
| metrics_->ReportKerberosEncryptionTypes(encryption_types); | |
| } | |
| } | |
| } | |
| // Trying to acquire a ticket qualifies this user as an active user, so report | |
| // stats. | |
| MaybeReportDailyUsageStats(); | |
| return error; | |
| } | |
| ErrorType AccountManager::GetKerberosFiles(const std::string& principal_name, | |
| KerberosFiles* files) const { | |
| // Trying to get Kerberos files qualifies this user as an active user, so | |
| // report stats. | |
| MaybeReportDailyUsageStats(); | |
| files->clear_krb5cc(); | |
| files->clear_krb5conf(); | |
| const InternalAccount* account = GetAccount(principal_name); | |
| if (!account) | |
| return ERROR_UNKNOWN_PRINCIPAL_NAME; | |
| // By convention, no credential cache means no error. | |
| const base::FilePath krb5cc_path = GetKrb5CCPath(principal_name); | |
| if (!base::PathExists(krb5cc_path)) | |
| return ERROR_NONE; | |
| std::string krb5cc; | |
| ErrorType error = LoadFile(krb5cc_path, &krb5cc); | |
| if (error != ERROR_NONE) | |
| return error; | |
| std::string krb5conf; | |
| error = LoadFile(GetKrb5ConfPath(principal_name), &krb5conf); | |
| if (error != ERROR_NONE) | |
| return error; | |
| files->mutable_krb5cc()->assign(krb5cc.begin(), krb5cc.end()); | |
| files->mutable_krb5conf()->assign(krb5conf.begin(), krb5conf.end()); | |
| return ERROR_NONE; | |
| } | |
| void AccountManager::StartObservingTickets() { | |
| for (const auto& account : accounts_) { | |
| const base::FilePath krb5cc_path = | |
| GetKrb5CCPath(account.data.principal_name()); | |
| // Might happen for managed accounts (e.g. misconfigured password). Chrome | |
| // only allows adding unmanaged accounts if a ticket can be acquired. | |
| if (!base::PathExists(krb5cc_path)) | |
| continue; | |
| // A ticket where GetTgtStatus fails is considered broken and hence invalid. | |
| Krb5Interface::TgtStatus tgt_status; | |
| if (krb5_->GetTgtStatus(krb5cc_path, &tgt_status) != ERROR_NONE || | |
| tgt_status.validity_seconds <= 0) { | |
| NotifyTgtExpiration(account.data.principal_name(), | |
| TgtRenewalScheduler::TgtExpiration::kExpired); | |
| continue; | |
| } | |
| // Ticket is valid. Schedule task to automatically renew it. | |
| account.tgt_renewal_scheduler_->ScheduleRenewal( | |
| true /* notify_expiration */); | |
| } | |
| } | |
| // static | |
| std::string AccountManager::GetSafeFilenameForTesting( | |
| const std::string& principal_name) { | |
| return GetSafeFilename(principal_name); | |
| } | |
| void AccountManager::WrapKrb5ForTesting() { | |
| krb5_ = std::make_unique<Krb5JailWrapper>(std::move(krb5_)); | |
| } | |
| void AccountManager::TriggerKerberosFilesChanged( | |
| const std::string& principal_name) const { | |
| kerberos_files_changed_.Run(principal_name); | |
| } | |
| void AccountManager::TriggerKerberosTicketExpiring( | |
| const std::string& principal_name) const { | |
| kerberos_ticket_expiring_.Run(principal_name); | |
| } | |
| ErrorType AccountManager::GetTgtStatus(const std::string& principal_name, | |
| Krb5Interface::TgtStatus* tgt_status) { | |
| return krb5_->GetTgtStatus(GetKrb5CCPath(principal_name), tgt_status); | |
| } | |
| ErrorType AccountManager::RenewTgt(const std::string& principal_name) { | |
| const InternalAccount* account = GetAccount(principal_name); | |
| if (!account) | |
| return ERROR_UNKNOWN_PRINCIPAL_NAME; | |
| if (!account->data.is_config_valid()) | |
| return ERROR_BAD_CONFIG; | |
| ErrorType error = | |
| krb5_->RenewTgt(principal_name, GetKrb5CCPath(principal_name), | |
| GetKrb5ConfPath(principal_name)); | |
| if (error != ERROR_NONE) { | |
| VLOG(1) << "RenewTgt failed with " << GetErrorString(error); | |
| // Renewal didn't work. See if we have a password stored and try to | |
| // auto-renew. | |
| MaybeAutoAcquireTgt(principal_name, &error); | |
| } | |
| last_renew_tgt_error_for_testing_ = error; | |
| return error; | |
| } | |
| void AccountManager::NotifyTgtExpiration( | |
| const std::string& principal_name, | |
| TgtRenewalScheduler::TgtExpiration expiration) { | |
| // First try to auto-acquire the TGT (usually works if password is stored). | |
| // Only if that isn't possible or doesn't work, trigger the signal. | |
| ErrorType error = ERROR_NONE; | |
| if (!MaybeAutoAcquireTgt(principal_name, &error) || error != ERROR_NONE) { | |
| TriggerKerberosTicketExpiring(principal_name); | |
| } | |
| } | |
| bool AccountManager::MaybeAutoAcquireTgt(const std::string& principal_name, | |
| ErrorType* error) { | |
| InternalAccount* account = GetMutableAccount(principal_name); | |
| DCHECK(account); | |
| // Check if |account| has access to the password. | |
| const bool use_login_password = account->data.use_login_password(); | |
| const bool password_was_remembered = | |
| base::PathExists(GetPasswordPath(principal_name)); | |
| if (!use_login_password && !password_was_remembered) | |
| return false; | |
| // Should not have remembered login password ourselves. | |
| DCHECK(!(use_login_password && password_was_remembered)); | |
| VLOG(1) << "Auto-acquiring new TGT using " | |
| << (use_login_password ? "login" : "remembered") << " password"; | |
| *error = AcquireTgt(principal_name, std::string() /* password */, | |
| password_was_remembered /* keep remembering */, | |
| use_login_password); | |
| if (*error != ERROR_NONE) | |
| VLOG(1) << "Auto-acquiring TGT failed with " << GetErrorString(*error); | |
| return true; | |
| } | |
| base::FilePath AccountManager::GetAccountDir( | |
| const std::string& principal_name) const { | |
| return storage_dir_.Append(GetSafeFilename(principal_name)); | |
| } | |
| base::FilePath AccountManager::GetKrb5ConfPath( | |
| const std::string& principal_name) const { | |
| return GetAccountDir(principal_name).Append(kKrb5ConfFilePart); | |
| } | |
| base::FilePath AccountManager::GetKrb5CCPath( | |
| const std::string& principal_name) const { | |
| return GetAccountDir(principal_name).Append(kKrb5CCFilePart); | |
| } | |
| base::FilePath AccountManager::GetPasswordPath( | |
| const std::string& principal_name) const { | |
| return GetAccountDir(principal_name).Append(kPasswordFilePart); | |
| } | |
| ErrorType AccountManager::UpdatePasswordFromLogin( | |
| const std::string& principal_name, std::string* password) { | |
| // Erase a previously remembered password. | |
| base::DeleteFile(GetPasswordPath(principal_name)); | |
| // Get login password from |password_provider_|. | |
| std::unique_ptr<password_provider::Password> login_password = | |
| password_provider_->GetPassword(); | |
| if (!login_password || login_password->size() == 0) { | |
| password->clear(); | |
| LOG(WARNING) << "Unable to retrieve login password"; | |
| } else { | |
| *password = std::string(login_password->GetRaw(), login_password->size()); | |
| } | |
| return ERROR_NONE; | |
| } | |
| ErrorType AccountManager::UpdatePasswordFromSaved( | |
| const std::string& principal_name, | |
| bool remember_password, | |
| std::string* password) { | |
| // Decision table what to do with the password: | |
| // pw empty / remember| false | true | |
| // -------------------+----------------------------+------------------------ | |
| // false | use given, erase file | use given, save to file | |
| // true | load from file, erase file | load from file | |
| // Remember password (even if authentication is going to fail below). | |
| const base::FilePath password_path = GetPasswordPath(principal_name); | |
| if (!password->empty() && remember_password) { | |
| ErrorType error = SaveFile(password_path, *password); | |
| if (error != ERROR_NONE) | |
| return error; | |
| // Remove group and other read access, just keep kerberosd rw. This prevents | |
| // kerberosd-exec from accessing the password. | |
| error = SetFilePermissions(password_path, kFileMode_rw); | |
| if (error != ERROR_NONE) { | |
| // Do a best effort removing the password. | |
| base::DeleteFile(password_path); | |
| return error; | |
| } | |
| } | |
| // Try to load a saved password if available and none is given. | |
| if (password->empty() && base::PathExists(password_path)) { | |
| ErrorType error = LoadFile(password_path, password); | |
| if (error != ERROR_NONE) | |
| return error; | |
| } | |
| // Erase a previously remembered password. | |
| if (!remember_password) | |
| base::DeleteFile(password_path); | |
| return ERROR_NONE; | |
| } | |
| void AccountManager::MaybeReportDailyUsageStats() const { | |
| // Did a day pass already? | |
| if (!metrics_->ShouldReportDailyUsageStats()) | |
| return; | |
| // Count different kinds of accounts. | |
| int total_count = static_cast<int>(accounts_.size()); | |
| int managed_count = 0; | |
| int unmanaged_count = 0; | |
| int remembered_password_count = 0; | |
| int use_login_password_count = 0; | |
| for (const auto& account : accounts_) { | |
| if (account.data.is_managed()) | |
| managed_count++; | |
| else | |
| unmanaged_count++; | |
| if (base::PathExists(GetPasswordPath(account.data.principal_name()))) | |
| remembered_password_count++; | |
| if (account.data.use_login_password()) | |
| use_login_password_count++; | |
| } | |
| // Report UMA stats. | |
| metrics_->ReportDailyUsageStats(total_count, managed_count, unmanaged_count, | |
| remembered_password_count, | |
| use_login_password_count); | |
| } | |
| AccountManager::InternalAccount::InternalAccount( | |
| AccountData&& _data, TgtRenewalScheduler::Delegate* delegate) | |
| : data(std::move(_data)), | |
| tgt_renewal_scheduler_(std::make_unique<TgtRenewalScheduler>( | |
| data.principal_name(), delegate)) {} | |
| int AccountManager::GetAccountIndex(const std::string& principal_name) const { | |
| for (size_t n = 0; n < accounts_.size(); ++n) { | |
| if (accounts_[n].data.principal_name() == principal_name) { | |
| CHECK(n <= std::numeric_limits<int>::max()); | |
| return static_cast<int>(n); | |
| } | |
| } | |
| return kInvalidIndex; | |
| } | |
| const AccountManager::InternalAccount* AccountManager::GetAccount( | |
| const std::string& principal_name) const { | |
| int index = GetAccountIndex(principal_name); | |
| return index != kInvalidIndex ? &accounts_[index] : nullptr; | |
| } | |
| AccountManager::InternalAccount* AccountManager::GetMutableAccount( | |
| const std::string& principal_name) { | |
| int index = GetAccountIndex(principal_name); | |
| return index != kInvalidIndex ? &accounts_[index] : nullptr; | |
| } | |
| AccountManager::WhatToRemove AccountManager::DetermineWhatToRemove( | |
| ClearMode mode, const InternalAccount& account) { | |
| switch (mode) { | |
| case CLEAR_ALL: | |
| return WhatToRemove::kAccount; | |
| case CLEAR_ONLY_MANAGED_ACCOUNTS: | |
| return account.data.is_managed() ? WhatToRemove::kAccount | |
| : WhatToRemove::kNothing; | |
| case CLEAR_ONLY_UNMANAGED_ACCOUNTS: | |
| return !account.data.is_managed() ? WhatToRemove::kAccount | |
| : WhatToRemove::kNothing; | |
| case CLEAR_ONLY_UNMANAGED_REMEMBERED_PASSWORDS: | |
| return !account.data.is_managed() ? WhatToRemove::kPassword | |
| : WhatToRemove::kNothing; | |
| } | |
| return WhatToRemove::kNothing; | |
| } | |
| } // namespace kerberos |