| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h" |
| |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/containers/contains.h" |
| #include "base/files/file_enumerator.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/notreached.h" |
| #include "base/path_service.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h" |
| #include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h" |
| #include "third_party/abseil-cpp/absl/types/variant.h" |
| |
| namespace ash { |
| |
| using ::user_data_auth::CryptohomeErrorCode; |
| |
| namespace { |
| |
| // Specialized structs for each auth factor with factor-specific metadata. |
| // Secrets are stored the same way they are sent to cryptohome (i.e. salted and |
| // hashed), but only if secret checking has been enabled via |
| // `TestApi::set_enabled_auth_check`. |
| // `FakeAuthFactor` is the union/absl::variant of the factor-specific auth |
| // factor structs. |
| |
| struct PasswordFactor { |
| // This will be `absl::nullopt` if auth checking hasn't been activated. |
| absl::optional<std::string> password; |
| }; |
| |
| struct PinFactor { |
| // This will be `absl::nullopt` if auth checking hasn't been activated. |
| absl::optional<std::string> pin = absl::nullopt; |
| bool locked = false; |
| }; |
| |
| struct RecoveryFactor {}; |
| struct SmartCardFactor { |
| std::string public_key_spki_der; |
| }; |
| |
| struct KioskFactor {}; |
| |
| using FakeAuthFactor = absl::variant<PasswordFactor, |
| PinFactor, |
| RecoveryFactor, |
| KioskFactor, |
| SmartCardFactor>; |
| |
| // Strings concatenated with the account id to obtain a user's profile |
| // directory name. The prefix "u-" below corresponds to |
| // `chrome::kProfileDirPrefix` (which can not be easily included here) and |
| // "-hash" is as in `GetStubSanitizedUsername`. |
| const std::string kUserDataDirNamePrefix = "u-"; |
| const std::string kUserDataDirNameSuffix = "-hash"; |
| |
| // Label of the recovery auth factor. |
| const std::string kCryptohomeRecoveryKeyLabel = "recovery"; |
| // Label of the kiosk auth factor. |
| const std::string kCryptohomePublicMountLabel = "publicmount"; |
| // Label of the GAIA password key |
| const std::string kCryptohomeGaiaKeyLabel = "gaia"; |
| |
| } // namespace |
| |
| struct FakeUserDataAuthClient::UserCryptohomeState { |
| // Maps labels to auth factors. |
| base::flat_map<std::string, FakeAuthFactor> auth_factors; |
| |
| // A flag describing how we pretend that the user's home directory is |
| // encrypted. |
| HomeEncryptionMethod home_encryption_method = |
| HomeEncryptionMethod::kDirCrypto; |
| |
| // A flag describing how we pretend that the user's home directory migration |
| // was not completed correctly. |
| bool incomplete_migration = false; |
| }; |
| |
| namespace { |
| |
| // Interval to update the progress of MigrateToDircrypto in milliseconds. |
| constexpr int kDircryptoMigrationUpdateIntervalMs = 200; |
| // The number of updates the MigrateToDircrypto will send before it completes. |
| constexpr uint64_t kDircryptoMigrationMaxProgress = 15; |
| |
| // Timeout after which an authenticated session is destroyed by the real |
| // cryptohome service. |
| constexpr int kSessionTimeoutSeconds = 5 * 60; |
| |
| // Template for auth session ID. |
| constexpr char kAuthSessionIdTemplate[] = "AuthSession-%d"; |
| |
| // Guest username constant that mirrors the one in real cryptohome |
| constexpr char kGuestUserName[] = "$guest"; |
| |
| // Used to track the global fake instance. This global fake instance is created |
| // in the first call to FakeUserDataAuth::Get(). During browser startup in |
| // browser tests and cros-linux, the global instance pointer in |
| // userdataauth_client.cc is set to the address of this global fake instance. |
| // During shutdown, the fake instance is deleted in the same way as the normal |
| // UserDataAuth instance would be deleted. We do this to stay as faithful as |
| // possible to the real implementation. |
| // However, browser tests can access and configure the fake instance via the |
| // TestApi or CryptohomeMixin even before the browser starts, for example in |
| // the constructor of a browser test fixture. |
| FakeUserDataAuthClient* g_instance = nullptr; |
| |
| // `OverloadedFunctor` and `FunctorWithReturnType` are used to implement |
| // `Overload`, which constructs a visitor appropriate for use with |
| // `absl::visit` from lambdas for each case. |
| |
| // A functor combining the `operator()` definitions of a list of functors into |
| // a single functor with overloaded `operator()`. |
| template <class... Functors> |
| struct OverloadedFunctor : Functors... { |
| using Functors::operator()...; |
| }; |
| |
| // Used to fix the return type of a functor with overloaded `operator()`. |
| // This is useful in case the `operator()` overloads have different return |
| // types, but all return types are convertible into the intended fixed |
| // `ReturnType`. |
| template <class ReturnType, class Functor> |
| struct FunctorWithReturnType { |
| template <class Arg> |
| ReturnType operator()(Arg&& arg) { |
| return functor(std::forward<Arg>(arg)); |
| } |
| |
| Functor functor; |
| }; |
| |
| // `Overload` constructs a visitor appropriate for use with `absl::visit` from |
| // a number of lambdas for each case. The return type of each provided lambda |
| // must be convertible to `ReturnType`, and the `operator()` of the combined |
| // visitor will always return `ReturnType`. |
| template <class ReturnType, class... Functors> |
| FunctorWithReturnType<ReturnType, OverloadedFunctor<Functors...>> Overload( |
| Functors... functors) { |
| return {{std::move(functors)...}}; |
| } |
| |
| absl::optional<cryptohome::KeyData> FakeAuthFactorToKeyData( |
| std::string label, |
| const FakeAuthFactor& factor) { |
| return absl::visit( |
| Overload<absl::optional<cryptohome::KeyData>>( |
| [&](const PasswordFactor& password) { |
| cryptohome::KeyData data; |
| data.set_type(cryptohome::KeyData::KEY_TYPE_PASSWORD); |
| data.set_label(std::move(label)); |
| return data; |
| }, |
| [&](const PinFactor& pin) { |
| cryptohome::KeyData data; |
| data.set_type(cryptohome::KeyData::KEY_TYPE_PASSWORD); |
| data.set_label(std::move(label)); |
| data.mutable_policy()->set_low_entropy_credential(true); |
| data.mutable_policy()->set_auth_locked(pin.locked); |
| return data; |
| }, |
| [&](const RecoveryFactor&) { return absl::nullopt; }, |
| [&](const SmartCardFactor& smart_card) { |
| cryptohome::KeyData data; |
| data.set_type(cryptohome::KeyData::KEY_TYPE_CHALLENGE_RESPONSE); |
| data.set_label(std::move(label)); |
| data.add_challenge_response_key()->set_public_key_spki_der( |
| smart_card.public_key_spki_der); |
| // TODO (b/241259026): populate algorithms. |
| return data; |
| }, |
| [&](const KioskFactor& kiosk) { |
| cryptohome::KeyData data; |
| data.set_type(cryptohome::KeyData::KEY_TYPE_KIOSK); |
| data.set_label(std::move(label)); |
| return data; |
| }), |
| factor); |
| } |
| |
| absl::optional<user_data_auth::AuthFactor> FakeAuthFactorToAuthFactor( |
| std::string label, |
| const FakeAuthFactor& factor) { |
| return absl::visit( |
| Overload<absl::optional<user_data_auth::AuthFactor>>( |
| [&](const PasswordFactor& password) { |
| user_data_auth::AuthFactor result; |
| result.set_label(std::move(label)); |
| result.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| result.mutable_password_metadata(); |
| return result; |
| }, |
| [&](const PinFactor& pin) { |
| user_data_auth::AuthFactor result; |
| result.set_label(std::move(label)); |
| result.set_type(user_data_auth::AUTH_FACTOR_TYPE_PIN); |
| result.mutable_pin_metadata()->set_auth_locked(pin.locked); |
| return result; |
| }, |
| [&](const RecoveryFactor&) { |
| user_data_auth::AuthFactor result; |
| result.set_label(std::move(label)); |
| result.set_type( |
| user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY); |
| result.mutable_cryptohome_recovery_metadata(); |
| return result; |
| }, |
| [&](const KioskFactor& kiosk) { |
| user_data_auth::AuthFactor result; |
| result.set_label(std::move(label)); |
| result.set_type(user_data_auth::AUTH_FACTOR_TYPE_KIOSK); |
| result.mutable_kiosk_metadata(); |
| return result; |
| }, |
| [&](const SmartCardFactor& smart_card) { |
| user_data_auth::AuthFactor result; |
| result.set_label(std::move(label)); |
| result.set_type(user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD); |
| result.mutable_smart_card_metadata()->set_public_key_spki_der( |
| smart_card.public_key_spki_der); |
| return result; |
| }), |
| factor); |
| } |
| |
| // Turns a cryptohome::Key into a pair of label and FakeAuthFactor. |
| std::pair<std::string, FakeAuthFactor> KeyToFakeAuthFactor( |
| const cryptohome::Key& key, |
| bool save_secret) { |
| const cryptohome::KeyData& data = key.data(); |
| const std::string& label = data.label(); |
| CHECK_NE(label, "") << "Key label must not be empty string"; |
| absl::optional<std::string> secret = absl::nullopt; |
| if (save_secret && key.has_secret()) { |
| secret = key.secret(); |
| } |
| |
| switch (data.type()) { |
| case cryptohome::KeyData::KEY_TYPE_FINGERPRINT: |
| LOG(FATAL) << "Unsupported key type: " << data.type(); |
| __builtin_unreachable(); |
| case cryptohome::KeyData::KEY_TYPE_CHALLENGE_RESPONSE: |
| return {label, |
| SmartCardFactor{ |
| .public_key_spki_der = |
| data.challenge_response_key(0).public_key_spki_der()}}; |
| case cryptohome::KeyData::KEY_TYPE_PASSWORD: |
| if (data.has_policy() && data.policy().low_entropy_credential()) { |
| return {label, PinFactor{.pin = secret, .locked = false}}; |
| } |
| return {label, PasswordFactor{.password = secret}}; |
| case cryptohome::KeyData::KEY_TYPE_KIOSK: |
| return {label, KioskFactor{}}; |
| } |
| } |
| |
| // Turns AuthFactor+AuthInput into a pair of label and FakeAuthFactor. |
| std::pair<std::string, FakeAuthFactor> AuthFactorWithInputToFakeAuthFactor( |
| const user_data_auth::AuthFactor& factor, |
| const user_data_auth::AuthInput& input, |
| bool save_secret) { |
| const std::string& label = factor.label(); |
| CHECK_NE(label, "") << "Key label must not be empty string"; |
| |
| absl::optional<std::string> secret = absl::nullopt; |
| if (save_secret) { |
| if (factor.type() == user_data_auth::AUTH_FACTOR_TYPE_PASSWORD) { |
| secret = input.password_input().secret(); |
| } else if (factor.type() == user_data_auth::AUTH_FACTOR_TYPE_PIN) { |
| secret = input.pin_input().secret(); |
| } |
| } |
| |
| switch (factor.type()) { |
| case user_data_auth::AUTH_FACTOR_TYPE_UNSPECIFIED: |
| LOG(FATAL) << "Chrome should never send Unspecified auth factor."; |
| __builtin_unreachable(); |
| case user_data_auth::AUTH_FACTOR_TYPE_PIN: |
| return {label, PinFactor{.pin = secret, .locked = false}}; |
| case user_data_auth::AUTH_FACTOR_TYPE_PASSWORD: |
| return {label, PasswordFactor{.password = secret}}; |
| case user_data_auth::AUTH_FACTOR_TYPE_KIOSK: |
| return {label, KioskFactor{}}; |
| case user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY: |
| return {label, RecoveryFactor{}}; |
| case user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD: { |
| std::string key = factor.smart_card_metadata().public_key_spki_der(); |
| return {label, SmartCardFactor{.public_key_spki_der = key}}; |
| } |
| default: |
| NOTREACHED(); |
| __builtin_unreachable(); |
| } |
| } |
| |
| bool CheckCredentialsViaAuthFactor(const FakeAuthFactor& factor, |
| const std::string& secret) { |
| return absl::visit( |
| Overload<bool>( |
| [&](const PasswordFactor& password) { |
| return password.password == secret; |
| }, |
| [&](const PinFactor& pin) { return pin.pin == secret; }, |
| [&](const RecoveryFactor& recovery) { |
| LOG(FATAL) << "Checking recovery key is not allowed"; |
| return false; |
| }, |
| [&](const KioskFactor& kiosk) { |
| // Kiosk key secrets are derived from app ids and don't leave |
| // cryptohome, so there's nothing to check. |
| return true; |
| }, |
| [&](const SmartCardFactor& smart_card) { |
| LOG(FATAL) << "Checking smart card key is not implemented yet"; |
| return false; |
| }), |
| factor); |
| } |
| |
| template <class FakeFactorType> |
| bool ContainsFakeFactor( |
| const base::flat_map<std::string, FakeAuthFactor>& factors) { |
| const auto it = |
| base::ranges::find_if(factors, [](const auto label_factor_pair) { |
| const FakeAuthFactor& fake_factor = label_factor_pair.second; |
| return absl::get_if<FakeFactorType>(&fake_factor) != nullptr; |
| }); |
| return it != std::end(factors); |
| } |
| |
| bool AuthInputMatchesFakeFactorType( |
| const ::user_data_auth::AuthInput& auth_input, |
| const FakeAuthFactor& fake_factor) { |
| return absl::visit( |
| Overload<bool>( |
| [&](const PasswordFactor& password) { |
| return auth_input.has_password_input(); |
| }, |
| [&](const PinFactor& pin) { return auth_input.has_pin_input(); }, |
| [&](const RecoveryFactor& recovery) { |
| return auth_input.has_cryptohome_recovery_input(); |
| }, |
| [&](const KioskFactor& kiosk) { |
| return auth_input.has_kiosk_input(); |
| }, |
| [&](const SmartCardFactor& smart_card) { |
| return auth_input.has_smart_card_input(); |
| }), |
| fake_factor); |
| } |
| |
| // Helper that automatically sends a reply struct to a supplied callback when |
| // it goes out of scope. Basically a specialized `absl::Cleanup` or |
| // `std::scope_exit`. |
| template <typename ReplyType> |
| class ReplyOnReturn { |
| public: |
| explicit ReplyOnReturn(ReplyType* reply, |
| chromeos::DBusMethodCallback<ReplyType> callback) |
| : reply_(reply), callback_(std::move(callback)) {} |
| ReplyOnReturn(const ReplyOnReturn<ReplyType>&) = delete; |
| |
| ~ReplyOnReturn() { |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback_), *reply_)); |
| } |
| |
| ReplyOnReturn<ReplyType>& operator=(const ReplyOnReturn<ReplyType>&) = delete; |
| |
| private: |
| raw_ptr<ReplyType> reply_; |
| chromeos::DBusMethodCallback<ReplyType> callback_; |
| }; |
| |
| } // namespace |
| |
| // =============== `AuthSessionData` ===================== |
| FakeUserDataAuthClient::AuthSessionData::AuthSessionData() = default; |
| FakeUserDataAuthClient::AuthSessionData::AuthSessionData( |
| const AuthSessionData& other) = default; |
| FakeUserDataAuthClient::AuthSessionData& |
| FakeUserDataAuthClient::AuthSessionData::operator=(const AuthSessionData&) = |
| default; |
| FakeUserDataAuthClient::AuthSessionData::~AuthSessionData() = default; |
| |
| // static |
| FakeUserDataAuthClient::TestApi* FakeUserDataAuthClient::TestApi::Get() { |
| static TestApi instance; |
| return &instance; |
| } |
| |
| // static |
| void FakeUserDataAuthClient::TestApi::OverrideGlobalInstance( |
| std::unique_ptr<FakeUserDataAuthClient> client) { |
| CHECK(!g_instance); |
| g_instance = client.release(); |
| } |
| |
| void FakeUserDataAuthClient::TestApi::SetServiceIsAvailable(bool is_available) { |
| FakeUserDataAuthClient::Get()->service_is_available_ = is_available; |
| if (!is_available) { |
| return; |
| } |
| FakeUserDataAuthClient::Get() |
| ->RunPendingWaitForServiceToBeAvailableCallbacks(); |
| } |
| |
| void FakeUserDataAuthClient::TestApi::ReportServiceIsNotAvailable() { |
| DCHECK(!FakeUserDataAuthClient::Get()->service_is_available_); |
| FakeUserDataAuthClient::Get()->service_reported_not_available_ = true; |
| FakeUserDataAuthClient::Get() |
| ->RunPendingWaitForServiceToBeAvailableCallbacks(); |
| } |
| |
| void FakeUserDataAuthClient::TestApi::SetHomeEncryptionMethod( |
| const cryptohome::AccountIdentifier& cryptohome_id, |
| HomeEncryptionMethod method) { |
| auto user_it = FakeUserDataAuthClient::Get()->users_.find(cryptohome_id); |
| if (user_it == std::end(FakeUserDataAuthClient::Get()->users_)) { |
| LOG(ERROR) << "User does not exist: " << cryptohome_id.account_id(); |
| // TODO(crbug.com/1334538): Some existing tests rely on us creating the |
| // user here, but new tests shouldn't. Eventually this should crash. |
| user_it = FakeUserDataAuthClient::Get() |
| ->users_.insert({cryptohome_id, UserCryptohomeState()}) |
| .first; |
| } |
| DCHECK(user_it != std::end(FakeUserDataAuthClient::Get()->users_)); |
| UserCryptohomeState& user_state = user_it->second; |
| user_state.home_encryption_method = method; |
| } |
| |
| void FakeUserDataAuthClient::TestApi::SetEncryptionMigrationIncomplete( |
| const cryptohome::AccountIdentifier& cryptohome_id, |
| bool incomplete) { |
| auto user_it = FakeUserDataAuthClient::Get()->users_.find(cryptohome_id); |
| if (user_it == std::end(FakeUserDataAuthClient::Get()->users_)) { |
| LOG(ERROR) << "User does not exist: " << cryptohome_id.account_id(); |
| // TODO(crbug.com/1334538): Some existing tests rely on us creating the |
| // user here, but new tests shouldn't. Eventually this should crash. |
| user_it = FakeUserDataAuthClient::Get() |
| ->users_.insert({cryptohome_id, UserCryptohomeState()}) |
| .first; |
| } |
| DCHECK(user_it != std::end(FakeUserDataAuthClient::Get()->users_)); |
| UserCryptohomeState& user_state = user_it->second; |
| user_state.incomplete_migration = incomplete; |
| } |
| |
| void FakeUserDataAuthClient::TestApi::SetPinLocked( |
| const cryptohome::AccountIdentifier& account_id, |
| const std::string& label, |
| bool locked) { |
| auto user_it = FakeUserDataAuthClient::Get()->users_.find(account_id); |
| CHECK(user_it != FakeUserDataAuthClient::Get()->users_.end()) |
| << "User does not exist: " << account_id.account_id(); |
| UserCryptohomeState& user_state = user_it->second; |
| |
| auto factor_it = user_state.auth_factors.find(label); |
| CHECK(factor_it != user_state.auth_factors.end()) |
| << "Factor does not exist: " << label; |
| FakeAuthFactor& factor = factor_it->second; |
| |
| PinFactor* pin_factor = absl::get_if<PinFactor>(&factor); |
| CHECK(pin_factor) << "Factor is not PIN: " << label; |
| |
| pin_factor->locked = locked; |
| } |
| |
| void FakeUserDataAuthClient::TestApi::AddExistingUser( |
| const cryptohome::AccountIdentifier& account_id) { |
| const auto [user_it, was_inserted] = |
| FakeUserDataAuthClient::Get()->users_.insert( |
| {std::move(account_id), UserCryptohomeState()}); |
| if (!was_inserted) { |
| LOG(WARNING) << "User already exists: " << user_it->first.account_id(); |
| return; |
| } |
| |
| const absl::optional<base::FilePath> profile_dir = |
| FakeUserDataAuthClient::Get()->GetUserProfileDir(user_it->first); |
| if (!profile_dir) { |
| LOG(WARNING) << "User data directory has not been set, will not create " |
| "user profile directory"; |
| return; |
| } |
| |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| CHECK(base::CreateDirectory(*profile_dir)); |
| } |
| |
| absl::optional<base::FilePath> |
| FakeUserDataAuthClient::TestApi::GetUserProfileDir( |
| const cryptohome::AccountIdentifier& account_id) const { |
| return FakeUserDataAuthClient::Get()->GetUserProfileDir(account_id); |
| } |
| |
| void FakeUserDataAuthClient::TestApi::AddKey( |
| const cryptohome::AccountIdentifier& account_id, |
| const cryptohome::Key& key) { |
| UserCryptohomeState& user_state = GetUserState(account_id); |
| |
| const auto [factor_it, was_inserted] = |
| user_state.auth_factors.insert(KeyToFakeAuthFactor( |
| key, FakeUserDataAuthClient::Get()->enable_auth_check_)); |
| CHECK(was_inserted) << "Factor already exists"; |
| } |
| |
| void FakeUserDataAuthClient::TestApi::AddRecoveryFactor( |
| const cryptohome::AccountIdentifier& account_id) { |
| UserCryptohomeState& user_state = GetUserState(account_id); |
| |
| FakeAuthFactor factor{RecoveryFactor()}; |
| const auto [factor_it, was_inserted] = user_state.auth_factors.insert( |
| {kCryptohomeRecoveryKeyLabel, std::move(factor)}); |
| CHECK(was_inserted) << "Factor already exists"; |
| } |
| |
| bool FakeUserDataAuthClient::TestApi::HasRecoveryFactor( |
| const cryptohome::AccountIdentifier& account_id) { |
| const UserCryptohomeState& user_state = GetUserState(account_id); |
| return ContainsFakeFactor<RecoveryFactor>(user_state.auth_factors); |
| } |
| |
| bool FakeUserDataAuthClient::TestApi::HasPinFactor( |
| const cryptohome::AccountIdentifier& account_id) { |
| const UserCryptohomeState& user_state = GetUserState(account_id); |
| return ContainsFakeFactor<PinFactor>(user_state.auth_factors); |
| } |
| |
| std::string FakeUserDataAuthClient::TestApi::AddSession( |
| const cryptohome::AccountIdentifier& account_id, |
| bool authenticated) { |
| CHECK(FakeUserDataAuthClient::Get()->users_.contains(account_id)); |
| |
| std::string auth_session_id = base::StringPrintf( |
| kAuthSessionIdTemplate, |
| FakeUserDataAuthClient::Get()->next_auth_session_id_++); |
| |
| CHECK_EQ(FakeUserDataAuthClient::Get()->auth_sessions_.count(auth_session_id), |
| 0u); |
| AuthSessionData& session = |
| FakeUserDataAuthClient::Get()->auth_sessions_[auth_session_id]; |
| |
| session.id = auth_session_id; |
| session.ephemeral = false; |
| session.account = account_id; |
| session.authenticated = authenticated; |
| |
| return auth_session_id; |
| } |
| |
| void FakeUserDataAuthClient::TestApi::DestroySessions() { |
| g_instance->auth_sessions_.clear(); |
| } |
| |
| FakeUserDataAuthClient::UserCryptohomeState& |
| FakeUserDataAuthClient::TestApi::GetUserState( |
| const cryptohome::AccountIdentifier& account_id) { |
| const auto user_it = FakeUserDataAuthClient::Get()->users_.find(account_id); |
| CHECK(user_it != std::end(FakeUserDataAuthClient::Get()->users_)) |
| << "User doesn't exist"; |
| return user_it->second; |
| } |
| |
| void FakeUserDataAuthClient::TestApi::SendLegacyFPAuthSignal( |
| user_data_auth::FingerprintScanResult result) { |
| for (auto& observer : g_instance->fingerprint_observers_) { |
| observer.OnFingerprintScan(result); |
| } |
| } |
| |
| FakeUserDataAuthClient::FakeUserDataAuthClient() = default; |
| |
| FakeUserDataAuthClient::~FakeUserDataAuthClient() { |
| if (this == g_instance) { |
| // If we're deleting the global instance, clear the pointer to it. |
| g_instance = nullptr; |
| } |
| } |
| |
| // static |
| FakeUserDataAuthClient* FakeUserDataAuthClient::Get() { |
| if (!g_instance) { |
| g_instance = new FakeUserDataAuthClient(); |
| } |
| return g_instance; |
| } |
| |
| void FakeUserDataAuthClient::AddObserver(Observer* observer) { |
| observer_list_.AddObserver(observer); |
| } |
| |
| void FakeUserDataAuthClient::RemoveObserver(Observer* observer) { |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| void FakeUserDataAuthClient::AddFingerprintAuthObserver( |
| FingerprintAuthObserver* observer) { |
| fingerprint_observers_.AddObserver(observer); |
| } |
| |
| void FakeUserDataAuthClient::RemoveFingerprintAuthObserver( |
| FingerprintAuthObserver* observer) { |
| fingerprint_observers_.RemoveObserver(observer); |
| } |
| |
| void FakeUserDataAuthClient::IsMounted( |
| const ::user_data_auth::IsMountedRequest& request, |
| IsMountedCallback callback) { |
| ::user_data_auth::IsMountedReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| reply.set_is_mounted(true); |
| } |
| |
| void FakeUserDataAuthClient::Unmount( |
| const ::user_data_auth::UnmountRequest& request, |
| UnmountCallback callback) { |
| ::user_data_auth::UnmountReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| } |
| |
| void FakeUserDataAuthClient::Remove( |
| const ::user_data_auth::RemoveRequest& request, |
| RemoveCallback callback) { |
| ::user_data_auth::RemoveReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| cryptohome::AccountIdentifier account_id; |
| if (request.has_identifier()) { |
| account_id = request.identifier(); |
| } else { |
| auto auth_session = auth_sessions_.find(request.auth_session_id()); |
| CHECK(auth_session != std::end(auth_sessions_)) << "Invalid auth session"; |
| account_id = auth_session->second.account; |
| } |
| |
| const auto user_it = users_.find(account_id); |
| if (user_it == users_.end()) { |
| reply.set_error(::user_data_auth::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND); |
| return; |
| } |
| |
| const absl::optional<base::FilePath> profile_dir = |
| GetUserProfileDir(account_id); |
| if (profile_dir) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| CHECK(base::DeletePathRecursively(*profile_dir)); |
| } else { |
| LOG(WARNING) << "User data directory has not been set, will not delete " |
| "user profile directory"; |
| } |
| |
| users_.erase(user_it); |
| if (!request.auth_session_id().empty()) { |
| // Removing the user also invalidates the AuthSession. |
| auth_sessions_.erase(request.auth_session_id()); |
| } |
| } |
| |
| void FakeUserDataAuthClient::GetKeyData( |
| const ::user_data_auth::GetKeyDataRequest& request, |
| GetKeyDataCallback callback) { |
| ::user_data_auth::GetKeyDataReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| // Check if user exists. |
| const auto user_it = users_.find(request.account_id()); |
| if (user_it == std::end(users_)) { |
| LOG(ERROR) << "User does not exist: " << request.account_id().account_id(); |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND); |
| return; |
| } |
| const UserCryptohomeState& user_state = user_it->second; |
| |
| const std::string& requested_label = |
| request.authorization_request().key().data().label(); |
| |
| // Create range [factors_begin, factors_end) of factors matching |
| // `requested_label`: If the `requested_label` is empty, then every factor |
| // matches. Otherwise the factor with that precise label matches. If no such |
| // factor exists, the range is empty. |
| auto factors_begin = std::begin(user_state.auth_factors); |
| auto factors_end = std::end(user_state.auth_factors); |
| if (!requested_label.empty()) { |
| factors_begin = user_state.auth_factors.find(requested_label); |
| if (factors_begin != factors_end) { |
| factors_end = std::next(factors_begin); |
| } |
| } |
| |
| // Fill `reply.key_data()` with the factors we found. |
| for (auto factors_it = factors_begin; factors_it != factors_end; |
| ++factors_it) { |
| const std::string& label = factors_it->first; |
| const FakeAuthFactor& factor = factors_it->second; |
| |
| absl::optional<cryptohome::KeyData> key_data = |
| FakeAuthFactorToKeyData(label, factor); |
| if (key_data.has_value()) { |
| reply.mutable_key_data()->Add(std::move(*key_data)); |
| } else { |
| LOG(WARNING) << "Ignoring auth factor incompatible with legacy API: " |
| << label; |
| } |
| } |
| |
| if (reply.key_data().empty()) { |
| // This happens if no or only unsupported factors matched the request. |
| LOG(ERROR) << "No legacy key exists for label " << requested_label; |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_ERROR_KEY_NOT_FOUND); |
| } |
| } |
| |
| void FakeUserDataAuthClient::CheckKey( |
| const ::user_data_auth::CheckKeyRequest& request, |
| CheckKeyCallback callback) { |
| ::user_data_auth::CheckKeyReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| last_unlock_webauthn_secret_ = request.unlock_webauthn_secret(); |
| |
| const cryptohome::Key& key = request.authorization_request().key(); |
| switch (AuthenticateViaAuthFactors( |
| request.account_id(), /*factor_label=*/key.data().label(), |
| /*secret=*/key.secret(), /*wildcard_allowed=*/true)) { |
| case AuthResult::kAuthSuccess: |
| // Empty reply denotes a successful check. |
| break; |
| case AuthResult::kUserNotFound: |
| reply.set_error(::user_data_auth::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND); |
| break; |
| case AuthResult::kFactorNotFound: |
| reply.set_error(::user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND); |
| break; |
| case AuthResult::kAuthFailed: |
| reply.set_error( |
| ::user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED); |
| break; |
| } |
| } |
| |
| void FakeUserDataAuthClient::AddKey( |
| const ::user_data_auth::AddKeyRequest& request, |
| AddKeyCallback callback) { |
| ::user_data_auth::AddKeyReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| const cryptohome::AccountIdentifier& account_id = request.account_id(); |
| const bool clobber_if_exists = request.clobber_if_exists(); |
| const cryptohome::Key& new_key = request.key(); |
| |
| auto user_it = users_.find(account_id); |
| if (user_it == std::end(users_)) { |
| // TODO(crbug.com/1334538): Cryptohome would not create a new user here, |
| // but many tests rely on it. New tests shouldn't rely on this behavior. |
| LOG(ERROR) << "Need to create new user: " << account_id.account_id(); |
| user_it = users_.insert(user_it, {account_id, UserCryptohomeState()}); |
| } |
| DCHECK(user_it != std::end(users_)); |
| UserCryptohomeState& user_state = user_it->second; |
| |
| auto [new_label, new_factor] = |
| KeyToFakeAuthFactor(new_key, enable_auth_check_); |
| CHECK(clobber_if_exists || !user_state.auth_factors.contains(new_label)) |
| << "Key exists, will not clobber: " << new_label; |
| user_state.auth_factors[std::move(new_label)] = std::move(new_factor); |
| } |
| void FakeUserDataAuthClient::RemoveKey( |
| const ::user_data_auth::RemoveKeyRequest& request, |
| RemoveKeyCallback callback) { |
| ::user_data_auth::RemoveKeyReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| const auto user_it = users_.find(request.account_id()); |
| if (user_it == std::end(users_)) { |
| // TODO(crbug.com/1334538): Cryptohome would report an error here, but many |
| // tests do not set up users before calling RemoveKey. That's why we don't |
| // report an error here. New tests shouldn't rely on this behavior. |
| LOG(ERROR) << "User does not exist: " << request.account_id().account_id(); |
| return; |
| } |
| UserCryptohomeState& user_state = user_it->second; |
| |
| const std::string& label = request.key().data().label(); |
| if (label.empty()) { |
| // An empty request label matches all keys, so remove all. |
| LOG(WARNING) << "RemoveKey for empty label removes all keys"; |
| user_state.auth_factors.clear(); |
| } else { |
| user_state.auth_factors.erase(label); |
| } |
| } |
| void FakeUserDataAuthClient::StartFingerprintAuthSession( |
| const ::user_data_auth::StartFingerprintAuthSessionRequest& request, |
| StartFingerprintAuthSessionCallback callback) { |
| ::user_data_auth::StartFingerprintAuthSessionReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| } |
| void FakeUserDataAuthClient::EndFingerprintAuthSession( |
| const ::user_data_auth::EndFingerprintAuthSessionRequest& request, |
| EndFingerprintAuthSessionCallback callback) { |
| ::user_data_auth::EndFingerprintAuthSessionReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| } |
| void FakeUserDataAuthClient::StartMigrateToDircrypto( |
| const ::user_data_auth::StartMigrateToDircryptoRequest& request, |
| StartMigrateToDircryptoCallback callback) { |
| last_migrate_to_dircrypto_request_ = request; |
| ::user_data_auth::StartMigrateToDircryptoReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| dircrypto_migration_progress_ = 0; |
| |
| if (run_default_dircrypto_migration_) { |
| dircrypto_migration_progress_timer_.Start( |
| FROM_HERE, base::Milliseconds(kDircryptoMigrationUpdateIntervalMs), |
| this, &FakeUserDataAuthClient::OnDircryptoMigrationProgressUpdated); |
| } |
| } |
| void FakeUserDataAuthClient::NeedsDircryptoMigration( |
| const ::user_data_auth::NeedsDircryptoMigrationRequest& request, |
| NeedsDircryptoMigrationCallback callback) { |
| ::user_data_auth::NeedsDircryptoMigrationReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| const cryptohome::AccountIdentifier& account_id = request.account_id(); |
| |
| const auto user_it = users_.find(account_id); |
| if (user_it == std::end(users_)) { |
| // TODO(crbug.com/1334538): New tests shouldn't rely on this behavior and |
| // instead set up the user first. |
| LOG(ERROR) << "User does not exist: " << account_id.account_id(); |
| reply.set_needs_dircrypto_migration(false); |
| return; |
| } |
| DCHECK(user_it != users_.end()); |
| const UserCryptohomeState& user_state = user_it->second; |
| |
| const bool is_ecryptfs = |
| user_state.home_encryption_method == HomeEncryptionMethod::kEcryptfs; |
| reply.set_needs_dircrypto_migration(is_ecryptfs); |
| } |
| void FakeUserDataAuthClient::GetSupportedKeyPolicies( |
| const ::user_data_auth::GetSupportedKeyPoliciesRequest& request, |
| GetSupportedKeyPoliciesCallback callback) { |
| ::user_data_auth::GetSupportedKeyPoliciesReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| reply.set_low_entropy_credentials_supported( |
| supports_low_entropy_credentials_); |
| } |
| void FakeUserDataAuthClient::GetAccountDiskUsage( |
| const ::user_data_auth::GetAccountDiskUsageRequest& request, |
| GetAccountDiskUsageCallback callback) { |
| ::user_data_auth::GetAccountDiskUsageReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| // Sets 100 MB as a fake usage. |
| reply.set_size(100 * 1024 * 1024); |
| } |
| |
| void FakeUserDataAuthClient::StartAuthSession( |
| const ::user_data_auth::StartAuthSessionRequest& request, |
| StartAuthSessionCallback callback) { |
| ::user_data_auth::StartAuthSessionReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| if (auto error = TakeOperationError(Operation::kStartAuthSession); |
| error != CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { |
| reply.set_error(error); |
| return; |
| } |
| |
| std::string auth_session_id = |
| base::StringPrintf(kAuthSessionIdTemplate, next_auth_session_id_++); |
| |
| DCHECK_EQ(auth_sessions_.count(auth_session_id), 0u); |
| AuthSessionData& session = auth_sessions_[auth_session_id]; |
| session.id = auth_session_id; |
| session.ephemeral = |
| (request.flags() & ::user_data_auth::AUTH_SESSION_FLAGS_EPHEMERAL_USER) != |
| 0; |
| session.account = request.account_id(); |
| session.requested_auth_session_intent = request.intent(); |
| |
| reply.set_auth_session_id(auth_session_id); |
| |
| const auto user_it = users_.find(request.account_id()); |
| const bool user_exists = user_it != std::end(users_); |
| reply.set_user_exists(user_exists); |
| |
| const std::string& account_id = request.account_id().account_id(); |
| // See device_local_account.h |
| const bool is_kiosk = |
| base::EndsWith(account_id, "kiosk-apps.device-local.localhost"); |
| |
| if (user_exists) { |
| UserCryptohomeState& user_state = user_it->second; |
| |
| // TODO(b/239422391): Some tests expect that kiosk or gaia keys exist |
| // for existing users, but don't set those keys up. Until those tests are |
| // fixed, we explicitly add keys here. |
| if (is_kiosk) { |
| if (!user_state.auth_factors.contains(kCryptohomePublicMountLabel)) { |
| LOG(ERROR) << "Listing kiosk key even though it was not set up"; |
| |
| FakeAuthFactor factor{KioskFactor()}; |
| user_state.auth_factors.insert( |
| {kCryptohomeRecoveryKeyLabel, std::move(factor)}); |
| }; |
| } else { |
| if (!user_state.auth_factors.contains(kCryptohomeGaiaKeyLabel)) { |
| LOG(ERROR) << "Listing GAIA password key even though it was not set up"; |
| FakeAuthFactor factor{PasswordFactor()}; |
| user_state.auth_factors.insert( |
| {kCryptohomeGaiaKeyLabel, std::move(factor)}); |
| }; |
| } |
| |
| for (const auto& [label, factor] : user_state.auth_factors) { |
| absl::optional<cryptohome::KeyData> key_data = |
| FakeAuthFactorToKeyData(label, factor); |
| if (key_data) { |
| reply.mutable_key_label_data()->insert({label, std::move(*key_data)}); |
| } else { |
| LOG(WARNING) << "Ignoring auth factor incompatible with legacy API: " |
| << label; |
| } |
| absl::optional<user_data_auth::AuthFactor> auth_factor = |
| FakeAuthFactorToAuthFactor(label, factor); |
| if (key_data) { |
| *reply.add_auth_factors() = *auth_factor; |
| } else { |
| LOG(WARNING) |
| << "Ignoring auth factor incompatible with AuthFactor API: " |
| << label; |
| } |
| } |
| } |
| |
| // TODO(crbug.com/1334538): Some tests expect that kiosk or gaia keys exist |
| // for existing users, but don't set those keys up. Until those tests are |
| // fixed, we explicitly add keys here. |
| if (user_exists) { |
| if (is_kiosk) { |
| std::string kiosk_label = kCryptohomePublicMountLabel; |
| cryptohome::KeyData kiosk_key; |
| kiosk_key.set_label(kiosk_label); |
| kiosk_key.set_type(cryptohome::KeyData::KEY_TYPE_KIOSK); |
| const auto [_, was_inserted] = reply.mutable_key_label_data()->insert( |
| {std::move(kiosk_label), std::move(kiosk_key)}); |
| LOG_IF(ERROR, was_inserted) |
| << "Listing kiosk key even though it was not set up"; |
| } else { |
| std::string gaia_label = kCryptohomeGaiaKeyLabel; |
| cryptohome::KeyData gaia_key; |
| gaia_key.set_label(gaia_label); |
| gaia_key.set_type(cryptohome::KeyData::KEY_TYPE_PASSWORD); |
| const auto [_, was_inserted] = reply.mutable_key_label_data()->insert( |
| {std::move(gaia_label), std::move(gaia_key)}); |
| LOG_IF(ERROR, was_inserted) |
| << "Listing gaia key even though it was not set up"; |
| } |
| } |
| } |
| |
| void FakeUserDataAuthClient::ListAuthFactors( |
| const ::user_data_auth::ListAuthFactorsRequest& request, |
| ListAuthFactorsCallback callback) { |
| ::user_data_auth::ListAuthFactorsReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| if (auto error = TakeOperationError(Operation::kListAuthFactors); |
| error != CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { |
| reply.set_error(error); |
| return; |
| } |
| |
| const auto user_it = users_.find(request.account_id()); |
| const bool user_exists = user_it != std::end(users_); |
| if (!user_exists) { |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND); |
| return; |
| } |
| |
| const UserCryptohomeState& user_state = user_it->second; |
| for (const auto& [label, factor] : user_state.auth_factors) { |
| absl::optional<user_data_auth::AuthFactor> auth_factor = |
| FakeAuthFactorToAuthFactor(label, factor); |
| if (auth_factor) { |
| *reply.add_configured_auth_factors() = *auth_factor; |
| // Hack as cryptohome sends information via list of available intents. |
| auto* factor_with_status = |
| reply.add_configured_auth_factors_with_status(); |
| *factor_with_status->mutable_auth_factor() = *auth_factor; |
| factor_with_status->add_available_for_intents( |
| user_data_auth::AUTH_INTENT_DECRYPT); |
| factor_with_status->add_available_for_intents( |
| user_data_auth::AUTH_INTENT_VERIFY_ONLY); |
| factor_with_status->add_available_for_intents( |
| user_data_auth::AUTH_INTENT_WEBAUTHN); |
| if (absl::holds_alternative<PinFactor>(factor)) { |
| if (absl::get<PinFactor>(factor).locked) { |
| factor_with_status->clear_available_for_intents(); |
| } |
| } |
| } else { |
| LOG(WARNING) << "Ignoring auth factor incompatible with AuthFactor API: " |
| << label; |
| } |
| } |
| |
| const std::string& account_id = request.account_id().account_id(); |
| // See device_local_account.h |
| const bool is_kiosk = |
| base::EndsWith(account_id, "kiosk-apps.device-local.localhost"); |
| |
| if (is_kiosk) { |
| reply.add_supported_auth_factors(user_data_auth::AUTH_FACTOR_TYPE_KIOSK); |
| } else { |
| reply.add_supported_auth_factors(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD); |
| if (supports_low_entropy_credentials_) { |
| reply.add_supported_auth_factors(user_data_auth::AUTH_FACTOR_TYPE_PIN); |
| } |
| reply.add_supported_auth_factors( |
| user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY); |
| } |
| } |
| |
| void FakeUserDataAuthClient::PrepareGuestVault( |
| const ::user_data_auth::PrepareGuestVaultRequest& request, |
| PrepareGuestVaultCallback callback) { |
| ::user_data_auth::PrepareGuestVaultReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| if (auto error = TakeOperationError(Operation::kPrepareGuestVault); |
| error != CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { |
| reply.set_error(error); |
| return; |
| } |
| |
| prepare_guest_request_count_++; |
| |
| cryptohome::AccountIdentifier account; |
| account.set_account_id(kGuestUserName); |
| reply.set_sanitized_username(GetStubSanitizedUsername(account)); |
| } |
| |
| void FakeUserDataAuthClient::PrepareEphemeralVault( |
| const ::user_data_auth::PrepareEphemeralVaultRequest& request, |
| PrepareEphemeralVaultCallback callback) { |
| ::user_data_auth::PrepareEphemeralVaultReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| if (auto error = TakeOperationError(Operation::kPrepareEphemeralVault); |
| error != CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { |
| reply.set_error(error); |
| return; |
| } |
| |
| const auto session_it = auth_sessions_.find(request.auth_session_id()); |
| if (session_it == auth_sessions_.end()) { |
| LOG(ERROR) << "AuthSession not found"; |
| reply.set_sanitized_username(std::string()); |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN); |
| return; |
| } |
| AuthSessionData& auth_session = session_it->second; |
| if (!auth_session.ephemeral) { |
| LOG(ERROR) << "Non-ephemeral AuthSession used with PrepareEphemeralVault"; |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| return; |
| } |
| cryptohome::AccountIdentifier account = auth_session.account; |
| // Ephemeral mount does not require session to be authenticated; |
| // It authenticates session instead. |
| if (auth_session.authenticated) { |
| LOG(ERROR) << "AuthSession is authenticated"; |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| return; |
| } |
| auth_session.authenticated = true; |
| |
| const auto [_, was_inserted] = |
| users_.insert({auth_session.account, UserCryptohomeState()}); |
| |
| if (!was_inserted) { |
| LOG(ERROR) << "User already exists: " << auth_session.account.account_id(); |
| reply.set_error( |
| CryptohomeErrorCode::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY); |
| return; |
| } |
| |
| reply.set_sanitized_username(GetStubSanitizedUsername(account)); |
| } |
| |
| void FakeUserDataAuthClient::CreatePersistentUser( |
| const ::user_data_auth::CreatePersistentUserRequest& request, |
| CreatePersistentUserCallback callback) { |
| ::user_data_auth::CreatePersistentUserReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| if (auto error = TakeOperationError(Operation::kCreatePersistentUser); |
| error != CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { |
| reply.set_error(error); |
| return; |
| } |
| |
| const auto session_it = auth_sessions_.find(request.auth_session_id()); |
| if (session_it == auth_sessions_.end()) { |
| LOG(ERROR) << "AuthSession not found"; |
| reply.set_sanitized_username(std::string()); |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN); |
| return; |
| } |
| AuthSessionData& auth_session = session_it->second; |
| |
| if (auth_session.ephemeral) { |
| LOG(ERROR) << "Ephemeral AuthSession used with CreatePersistentUser"; |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| return; |
| } |
| |
| const auto [_, was_inserted] = |
| users_.insert({auth_session.account, UserCryptohomeState()}); |
| |
| if (!was_inserted) { |
| LOG(ERROR) << "User already exists: " << auth_session.account.account_id(); |
| reply.set_error( |
| CryptohomeErrorCode::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY); |
| return; |
| } |
| |
| auth_session.authenticated = true; |
| } |
| |
| void FakeUserDataAuthClient::PreparePersistentVault( |
| const ::user_data_auth::PreparePersistentVaultRequest& request, |
| PreparePersistentVaultCallback callback) { |
| ::user_data_auth::PreparePersistentVaultReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| if (auto error = TakeOperationError(Operation::kPreparePersistentVault); |
| error != CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { |
| reply.set_error(error); |
| return; |
| } |
| |
| auto error = CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET; |
| auto* authenticated_auth_session = |
| GetAuthenticatedAuthSession(request.auth_session_id(), &error); |
| |
| if (authenticated_auth_session == nullptr) { |
| reply.set_error(error); |
| return; |
| } |
| |
| if (authenticated_auth_session->ephemeral) { |
| LOG(ERROR) << "Ephemeral AuthSession used with PreparePersistentVault"; |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| return; |
| } |
| |
| const auto user_it = users_.find(authenticated_auth_session->account); |
| if (user_it == std::end(users_)) { |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND); |
| return; |
| } |
| |
| if (request.block_ecryptfs() && user_it->second.home_encryption_method == |
| HomeEncryptionMethod::kEcryptfs) { |
| if (user_it->second.incomplete_migration) { |
| LOG(ERROR) << "Encryption migration required, incomplete migration"; |
| reply.set_error(CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_MOUNT_PREVIOUS_MIGRATION_INCOMPLETE); |
| } else { |
| LOG(ERROR) << "Encryption migration required, full migration"; |
| reply.set_error( |
| CryptohomeErrorCode::CRYPTOHOME_ERROR_MOUNT_OLD_ENCRYPTION); |
| } |
| return; |
| } |
| |
| reply.set_sanitized_username( |
| GetStubSanitizedUsername(authenticated_auth_session->account)); |
| } |
| |
| void FakeUserDataAuthClient::PrepareVaultForMigration( |
| const ::user_data_auth::PrepareVaultForMigrationRequest& request, |
| PrepareVaultForMigrationCallback callback) { |
| ::user_data_auth::PrepareVaultForMigrationReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| if (auto error = TakeOperationError(Operation::kPrepareVaultForMigration); |
| error != CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { |
| reply.set_error(error); |
| return; |
| } |
| |
| auto error = CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET; |
| auto* authenticated_auth_session = |
| GetAuthenticatedAuthSession(request.auth_session_id(), &error); |
| |
| if (authenticated_auth_session == nullptr) { |
| reply.set_error(error); |
| return; |
| } |
| |
| if (!users_.contains(authenticated_auth_session->account)) { |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND); |
| return; |
| } |
| } |
| |
| void FakeUserDataAuthClient::InvalidateAuthSession( |
| const ::user_data_auth::InvalidateAuthSessionRequest& request, |
| InvalidateAuthSessionCallback callback) { |
| ::user_data_auth::InvalidateAuthSessionReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| auto auth_session = auth_sessions_.find(request.auth_session_id()); |
| if (auth_session == auth_sessions_.end()) { |
| LOG(ERROR) << "AuthSession not found"; |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN); |
| return; |
| } |
| |
| auth_sessions_.erase(auth_session); |
| } |
| |
| void FakeUserDataAuthClient::ExtendAuthSession( |
| const ::user_data_auth::ExtendAuthSessionRequest& request, |
| ExtendAuthSessionCallback callback) { |
| ::user_data_auth::ExtendAuthSessionReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| auto error = CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET; |
| GetAuthenticatedAuthSession(request.auth_session_id(), &error); |
| reply.set_error(error); |
| } |
| |
| void FakeUserDataAuthClient::AddAuthFactor( |
| const ::user_data_auth::AddAuthFactorRequest& request, |
| AddAuthFactorCallback callback) { |
| ::user_data_auth::AddAuthFactorReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| last_add_auth_factor_request_ = request; |
| |
| if (auto error = TakeOperationError(Operation::kAddAuthFactor); |
| error != CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { |
| reply.set_error(error); |
| return; |
| } |
| |
| auto error = CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET; |
| auto* session = |
| GetAuthenticatedAuthSession(request.auth_session_id(), &error); |
| if (session == nullptr) { |
| reply.set_error(error); |
| return; |
| } |
| |
| auto user_it = users_.find(session->account); |
| CHECK(user_it != std::end(users_)) |
| << "User associated with session does not exist"; |
| UserCryptohomeState& user_state = user_it->second; |
| |
| auto [new_label, new_factor] = AuthFactorWithInputToFakeAuthFactor( |
| request.auth_factor(), request.auth_input(), enable_auth_check_); |
| CHECK(!user_state.auth_factors.contains(new_label)) |
| << "Key exists, will not clobber: " << new_label; |
| user_state.auth_factors[std::move(new_label)] = std::move(new_factor); |
| } |
| |
| void FakeUserDataAuthClient::AuthenticateAuthFactor( |
| const ::user_data_auth::AuthenticateAuthFactorRequest& request, |
| AuthenticateAuthFactorCallback callback) { |
| ::user_data_auth::AuthenticateAuthFactorReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| if (auto error = TakeOperationError(Operation::kAuthenticateAuthFactor); |
| error != CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { |
| reply.set_error(error); |
| return; |
| } |
| |
| last_unlock_webauthn_secret_ = false; |
| |
| const auto session_it = auth_sessions_.find(request.auth_session_id()); |
| if (session_it == auth_sessions_.end()) { |
| LOG(ERROR) << "AuthSession not found"; |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN); |
| return; |
| } |
| auto& session = session_it->second; |
| |
| CHECK(!session.authenticated) << "Session is already authenticated"; |
| |
| const auto user_it = users_.find(session.account); |
| DCHECK(user_it != std::end(users_)); |
| const UserCryptohomeState& user_state = user_it->second; |
| |
| const std::string& label = request.auth_factor_label(); |
| const auto factor_it = user_state.auth_factors.find(label); |
| if (factor_it == user_state.auth_factors.end()) { |
| LOG(ERROR) << "Factor not found: " << label; |
| reply.set_error(::user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND); |
| return; |
| } |
| const FakeAuthFactor& factor = factor_it->second; |
| |
| const ::user_data_auth::AuthInput& auth_input = request.auth_input(); |
| |
| if (!AuthInputMatchesFakeFactorType(auth_input, factor)) { |
| LOG(ERROR) << "Auth input does not match factor type"; |
| reply.set_error(::user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND); |
| return; |
| } |
| |
| // Factor-specific verification logic. Will set the `error` field in the |
| // reply if a check didn't pass. |
| absl::visit( |
| Overload<void>( |
| [&](const PasswordFactor& password_factor) { |
| const auto& password_input = auth_input.password_input(); |
| |
| if (enable_auth_check_ && |
| password_input.secret() != password_factor.password) { |
| reply.set_error( |
| ::user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED); |
| return; |
| } |
| }, |
| [&](const PinFactor& pin_factor) { |
| const auto& pin_input = auth_input.pin_input(); |
| |
| if (enable_auth_check_ && pin_input.secret() != pin_factor.pin) { |
| reply.set_error( |
| ::user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED); |
| return; |
| } |
| }, |
| [&](const RecoveryFactor& recovery) { |
| const auto& recovery_input = auth_input.cryptohome_recovery_input(); |
| |
| if (recovery_input.mediator_pub_key().empty()) { |
| LOG(ERROR) << "Missing mediate pub key"; |
| reply.set_error( |
| ::user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED); |
| return; |
| } |
| if (recovery_input.epoch_response().empty()) { |
| LOG(ERROR) << "Missing epoch response"; |
| reply.set_error( |
| ::user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED); |
| return; |
| } |
| if (recovery_input.recovery_response().empty()) { |
| LOG(ERROR) << "Missing recovery response"; |
| reply.set_error( |
| ::user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED); |
| return; |
| } |
| }, |
| [&](const KioskFactor& kiosk) {}, |
| [&](const SmartCardFactor& smart_card) { |
| LOG(ERROR) << "Checking smart card key is not implemented yet"; |
| }), |
| factor); |
| |
| if (reply.error() != CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET) { |
| return; |
| } |
| |
| session.authenticated = true; |
| session.authorized_auth_session_intent.Put( |
| session.requested_auth_session_intent); |
| if (session.requested_auth_session_intent == |
| user_data_auth::AUTH_INTENT_DECRYPT) { |
| reply.set_authenticated(true); |
| } |
| reply.add_authorized_for(session.requested_auth_session_intent); |
| reply.set_seconds_left(kSessionTimeoutSeconds); |
| } |
| |
| void FakeUserDataAuthClient::UpdateAuthFactor( |
| const ::user_data_auth::UpdateAuthFactorRequest& request, |
| UpdateAuthFactorCallback callback) { |
| ::user_data_auth::UpdateAuthFactorReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| auto error = CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET; |
| auto* session = |
| GetAuthenticatedAuthSession(request.auth_session_id(), &error); |
| reply.set_error(error); |
| if (session == nullptr) { |
| return; |
| } |
| |
| auto user_it = users_.find(session->account); |
| DCHECK(user_it != std::end(users_)); |
| UserCryptohomeState& user_state = user_it->second; |
| |
| // Update the fake auth factor according to the new secret. |
| auto [new_label, new_factor] = AuthFactorWithInputToFakeAuthFactor( |
| request.auth_factor(), request.auth_input(), enable_auth_check_); |
| CHECK_EQ(new_label, request.auth_factor_label()); |
| CHECK(user_state.auth_factors.contains(new_label)) |
| << "Key does not exist: " << new_label; |
| user_state.auth_factors[std::move(new_label)] = std::move(new_factor); |
| } |
| |
| void FakeUserDataAuthClient::RemoveAuthFactor( |
| const ::user_data_auth::RemoveAuthFactorRequest& request, |
| RemoveAuthFactorCallback callback) { |
| ::user_data_auth::RemoveAuthFactorReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| auto error = CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET; |
| auto* session = |
| GetAuthenticatedAuthSession(request.auth_session_id(), &error); |
| reply.set_error(error); |
| if (session == nullptr) { |
| return; |
| } |
| auto user_it = users_.find(session->account); |
| DCHECK(user_it != std::end(users_)); |
| UserCryptohomeState& user_state = user_it->second; |
| |
| const std::string& label = request.auth_factor_label(); |
| DCHECK(!label.empty()); |
| bool erased = user_state.auth_factors.erase(label) > 0; |
| |
| if (!erased) { |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_ERROR_KEY_NOT_FOUND); |
| } |
| } |
| |
| void FakeUserDataAuthClient::GetAuthFactorExtendedInfo( |
| const ::user_data_auth::GetAuthFactorExtendedInfoRequest& request, |
| GetAuthFactorExtendedInfoCallback callback) { |
| ::user_data_auth::GetAuthFactorExtendedInfoReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| } |
| |
| void FakeUserDataAuthClient::GetRecoveryRequest( |
| const ::user_data_auth::GetRecoveryRequestRequest& request, |
| GetRecoveryRequestCallback callback) { |
| ::user_data_auth::GetRecoveryRequestReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| } |
| |
| void FakeUserDataAuthClient::GetAuthSessionStatus( |
| const ::user_data_auth::GetAuthSessionStatusRequest& request, |
| GetAuthSessionStatusCallback callback) { |
| ::user_data_auth::GetAuthSessionStatusReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| const std::string auth_session_id = request.auth_session_id(); |
| auto auth_session = auth_sessions_.find(auth_session_id); |
| |
| // Check if the token refers to a valid AuthSession. |
| if (auth_session == auth_sessions_.end()) { |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN); |
| return; |
| } |
| if (!auth_session->second.authenticated) { |
| reply.set_status( |
| ::user_data_auth::AUTH_SESSION_STATUS_FURTHER_FACTOR_REQUIRED); |
| return; |
| } |
| reply.set_status(::user_data_auth::AUTH_SESSION_STATUS_AUTHENTICATED); |
| // Use 5 minutes timeout - as if auth session has just started. |
| reply.set_time_left(5 * 60); |
| } |
| |
| void FakeUserDataAuthClient::PrepareAuthFactor( |
| const ::user_data_auth::PrepareAuthFactorRequest& request, |
| PrepareAuthFactorCallback callback) { |
| ::user_data_auth::PrepareAuthFactorReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| const std::string auth_session_id = request.auth_session_id(); |
| auto auth_session = auth_sessions_.find(auth_session_id); |
| // Check if the token refers to a valid AuthSession. |
| if (auth_session == auth_sessions_.end()) { |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN); |
| return; |
| } |
| |
| CHECK_EQ(request.auth_factor_type(), |
| user_data_auth::AUTH_FACTOR_TYPE_LEGACY_FINGERPRINT) |
| << "Only Legacy FP is supported in FakeUDAC"; |
| CHECK(!fingerprint_observers_.empty()) |
| << "Add relevant observer before calling PrepareAuthFactor"; |
| |
| CHECK(!auth_session->second.is_listening_for_fingerprint_events) |
| << "Duplicate call to PrepareAuthFactor"; |
| auth_session->second.is_listening_for_fingerprint_events = true; |
| } |
| |
| void FakeUserDataAuthClient::TerminateAuthFactor( |
| const ::user_data_auth::TerminateAuthFactorRequest& request, |
| TerminateAuthFactorCallback callback) { |
| ::user_data_auth::TerminateAuthFactorReply reply; |
| ReplyOnReturn auto_reply(&reply, std::move(callback)); |
| |
| const std::string auth_session_id = request.auth_session_id(); |
| auto auth_session = auth_sessions_.find(auth_session_id); |
| // Check if the token refers to a valid AuthSession. |
| if (auth_session == auth_sessions_.end()) { |
| reply.set_error(CryptohomeErrorCode::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN); |
| return; |
| } |
| |
| CHECK_EQ(request.auth_factor_type(), |
| user_data_auth::AUTH_FACTOR_TYPE_LEGACY_FINGERPRINT) |
| << "Only Legacy FP is supported in FakeUDAC"; |
| |
| CHECK(auth_session->second.is_listening_for_fingerprint_events) |
| << "Call to TerminateAuthFactor without prior PrepareAuthFactor"; |
| auth_session->second.is_listening_for_fingerprint_events = false; |
| } |
| |
| void FakeUserDataAuthClient::WaitForServiceToBeAvailable( |
| chromeos::WaitForServiceToBeAvailableCallback callback) { |
| if (service_is_available_ || service_reported_not_available_) { |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), service_is_available_)); |
| } else { |
| pending_wait_for_service_to_be_available_callbacks_.push_back( |
| std::move(callback)); |
| } |
| } |
| |
| void FakeUserDataAuthClient::RunPendingWaitForServiceToBeAvailableCallbacks() { |
| std::vector<chromeos::WaitForServiceToBeAvailableCallback> callbacks; |
| callbacks.swap(pending_wait_for_service_to_be_available_callbacks_); |
| for (auto& callback : callbacks) { |
| std::move(callback).Run(false); |
| } |
| } |
| |
| FakeUserDataAuthClient::AuthResult |
| FakeUserDataAuthClient::AuthenticateViaAuthFactors( |
| const cryptohome::AccountIdentifier& account_id, |
| const std::string& factor_label, |
| const std::string& secret, |
| bool wildcard_allowed, |
| std::string* matched_factor_label) const { |
| if (!enable_auth_check_) { |
| return AuthResult::kAuthSuccess; |
| } |
| |
| const auto user_it = users_.find(account_id); |
| if (user_it == std::end(users_)) { |
| return AuthResult::kUserNotFound; |
| } |
| const UserCryptohomeState& user_state = user_it->second; |
| |
| if (wildcard_allowed && factor_label.empty()) { |
| // Do a wildcard match (it's only used for legacy APIs): try the secret |
| // against every credential. |
| for (const auto& [candidate_label, candidate_factor] : |
| user_state.auth_factors) { |
| if (CheckCredentialsViaAuthFactor(candidate_factor, secret)) { |
| if (matched_factor_label) { |
| *matched_factor_label = candidate_label; |
| } |
| return AuthResult::kAuthSuccess; |
| } |
| } |
| // It's not well-defined which error is returned on a failed wildcard |
| // authentication, but we follow what the real cryptohome does (at least in |
| // CheckKey). |
| return AuthResult::kAuthFailed; |
| } |
| |
| const auto factor_it = user_state.auth_factors.find(factor_label); |
| if (factor_it == std::end(user_state.auth_factors)) { |
| return AuthResult::kFactorNotFound; |
| } |
| const auto& [label, factor] = *factor_it; |
| if (!CheckCredentialsViaAuthFactor(factor, secret)) { |
| return AuthResult::kAuthFailed; |
| } |
| if (matched_factor_label) { |
| *matched_factor_label = label; |
| } |
| return AuthResult::kAuthSuccess; |
| } |
| |
| void FakeUserDataAuthClient::SetNextOperationError( |
| FakeUserDataAuthClient::Operation operation, |
| CryptohomeErrorCode error) { |
| operation_errors_[operation] = error; |
| } |
| |
| CryptohomeErrorCode FakeUserDataAuthClient::TakeOperationError( |
| Operation operation) { |
| const auto op_error = operation_errors_.find(operation); |
| if (op_error == std::end(operation_errors_)) { |
| return CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET; |
| } |
| CryptohomeErrorCode result = op_error->second; |
| operation_errors_.erase(op_error); |
| return result; |
| } |
| |
| void FakeUserDataAuthClient::OnDircryptoMigrationProgressUpdated() { |
| dircrypto_migration_progress_++; |
| |
| if (dircrypto_migration_progress_ >= kDircryptoMigrationMaxProgress) { |
| NotifyDircryptoMigrationProgress( |
| ::user_data_auth::DircryptoMigrationStatus::DIRCRYPTO_MIGRATION_SUCCESS, |
| dircrypto_migration_progress_, kDircryptoMigrationMaxProgress); |
| const auto user_it = |
| users_.find(last_migrate_to_dircrypto_request_.account_id()); |
| DCHECK(user_it != std::end(users_)) |
| << "User for dircrypto migration does not exist"; |
| |
| UserCryptohomeState& user_state = user_it->second; |
| user_state.home_encryption_method = HomeEncryptionMethod::kDirCrypto; |
| dircrypto_migration_progress_timer_.Stop(); |
| return; |
| } |
| NotifyDircryptoMigrationProgress(::user_data_auth::DircryptoMigrationStatus:: |
| DIRCRYPTO_MIGRATION_IN_PROGRESS, |
| dircrypto_migration_progress_, |
| kDircryptoMigrationMaxProgress); |
| } |
| |
| void FakeUserDataAuthClient::NotifyLowDiskSpace(uint64_t disk_free_bytes) { |
| ::user_data_auth::LowDiskSpace status; |
| status.set_disk_free_bytes(disk_free_bytes); |
| for (auto& observer : observer_list_) { |
| observer.LowDiskSpace(status); |
| } |
| } |
| |
| void FakeUserDataAuthClient::NotifyDircryptoMigrationProgress( |
| ::user_data_auth::DircryptoMigrationStatus status, |
| uint64_t current, |
| uint64_t total) { |
| ::user_data_auth::DircryptoMigrationProgress progress; |
| progress.set_status(status); |
| progress.set_current_bytes(current); |
| progress.set_total_bytes(total); |
| for (auto& observer : observer_list_) { |
| observer.DircryptoMigrationProgress(progress); |
| } |
| } |
| |
| absl::optional<base::FilePath> FakeUserDataAuthClient::GetUserProfileDir( |
| const cryptohome::AccountIdentifier& account_id) const { |
| if (!user_data_dir_.has_value()) { |
| return absl::nullopt; |
| } |
| |
| std::string user_dir_base_name = |
| kUserDataDirNamePrefix + account_id.account_id() + kUserDataDirNameSuffix; |
| const base::FilePath profile_dir = |
| user_data_dir_->Append(std::move(user_dir_base_name)); |
| return profile_dir; |
| } |
| |
| const FakeUserDataAuthClient::AuthSessionData* |
| FakeUserDataAuthClient::GetAuthenticatedAuthSession( |
| const std::string& auth_session_id, |
| CryptohomeErrorCode* error) const { |
| auto auth_session = auth_sessions_.find(auth_session_id); |
| |
| // Check if the token refers to a valid AuthSession. |
| if (auth_session == auth_sessions_.end()) { |
| LOG(ERROR) << "AuthSession not found"; |
| *error = CryptohomeErrorCode::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN; |
| return nullptr; |
| } |
| |
| // Check if the AuthSession is properly authenticated. |
| if (!auth_session->second.authenticated) { |
| LOG(ERROR) << "AuthSession is not authenticated"; |
| *error = CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT; |
| return nullptr; |
| } |
| |
| return &auth_session->second; |
| } |
| |
| void FakeUserDataAuthClient::SetUserDataDir(base::FilePath path) { |
| CHECK(!user_data_dir_.has_value()); |
| user_data_dir_ = std::move(path); |
| |
| std::string pattern = kUserDataDirNamePrefix + "*" + kUserDataDirNameSuffix; |
| base::FileEnumerator e(*user_data_dir_, /*recursive=*/false, |
| base::FileEnumerator::DIRECTORIES, std::move(pattern)); |
| for (base::FilePath name = e.Next(); !name.empty(); name = e.Next()) { |
| const base::FilePath base_name = name.BaseName(); |
| DCHECK(base::StartsWith(base_name.value(), kUserDataDirNamePrefix)); |
| DCHECK(base::EndsWith(base_name.value(), kUserDataDirNameSuffix)); |
| |
| // Remove kUserDataDirNamePrefix from front and kUserDataDirNameSuffix from |
| // end to obtain account id. |
| std::string account_id_str( |
| base_name.value().begin() + kUserDataDirNamePrefix.size(), |
| base_name.value().end() - kUserDataDirNameSuffix.size()); |
| |
| cryptohome::AccountIdentifier account_id; |
| account_id.set_account_id(std::move(account_id_str)); |
| |
| // This does intentionally not override existing entries. |
| users_.insert({std::move(account_id), UserCryptohomeState()}); |
| } |
| } |
| |
| } // namespace ash |