// 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.
#ifndef CHROMEOS_ASH_COMPONENTS_DBUS_USERDATAAUTH_FAKE_USERDATAAUTH_CLIENT_H_
#define CHROMEOS_ASH_COMPONENTS_DBUS_USERDATAAUTH_FAKE_USERDATAAUTH_CLIENT_H_
#include <optional>
#include <string>
#include <utility>
#include "base/component_export.h"
#include "base/containers/enum_set.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/cryptohome/error_types.h"
#include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
#include "chromeos/ash/components/dbus/cryptohome/account_identifier_operators.h"
#include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h"
#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
#include "third_party/protobuf/src/google/protobuf/message_lite.h"
namespace ash {
class COMPONENT_EXPORT(USERDATAAUTH_CLIENT) FakeUserDataAuthClient
: public UserDataAuthClient {
private:
struct UserCryptohomeState;
public:
enum class Operation {
kStartAuthSession,
kAuthenticateAuthFactor,
kPrepareGuestVault,
kPrepareEphemeralVault,
kCreatePersistentUser,
kPreparePersistentVault,
kPrepareVaultForMigration,
kAddAuthFactor,
kUpdateAuthFactor,
kUpdateAuthFactorMetadata,
kReplaceAuthFactor,
kListAuthFactors,
kStartMigrateToDircrypto,
kRemove,
kGetRecoverableKeyStores,
};
// The method by which a user's home directory can be encrypted.
enum class HomeEncryptionMethod {
kDirCrypto,
kEcryptfs,
kDmCrypt,
};
// The TestAPI of FakeUserDataAuth. Prefer to use `ash::CryptohomeMixin`,
// which exposes all the methods here and some additional ones.
class COMPONENT_EXPORT(USERDATAAUTH_CLIENT) TestApi {
public:
// Legacy method for tests that do not use `CryptohomeMixin`.
static TestApi* Get();
// Override the global fake instance for browser tests. Must be called
// before browser startup, for example in the constructor of the fixture,
// and before the global instance is configured in any way using this
// TestApi or FakeUserDataAuth::Get().
static void OverrideGlobalInstance(std::unique_ptr<FakeUserDataAuthClient>);
// Sets whether dircrypto migration update should be run automatically.
// If set to false, the client will not send any dircrypto migration
// progress updates on its own - a test that sets this will have to call
// NotifyDircryptoMigrationProgress() for the progress to update.
void set_run_default_dircrypto_migration(bool value) {
FakeUserDataAuthClient::Get()->run_default_dircrypto_migration_ = value;
}
// If set, next call to GetSupportedKeyPolicies() will tell caller that low
// entropy credentials are supported.
void set_supports_low_entropy_credentials(bool supports) {
FakeUserDataAuthClient::Get()->supports_low_entropy_credentials_ =
supports;
}
// If enable_auth_check is true, then authentication requests actually check
// the key.
void set_enable_auth_check(bool enable_auth_check) {
FakeUserDataAuthClient::Get()->enable_auth_check_ = enable_auth_check;
}
// Sets whether ARC disk quota is supported or not.
void set_arc_quota_supported(bool supported) {
FakeUserDataAuthClient::Get()->arc_quota_supported_ = supported;
}
// Changes the behavior of WaitForServiceToBeAvailable(). This method runs
// pending callbacks if is_available is true.
void SetServiceIsAvailable(bool is_available);
// Runs pending availability callbacks reporting that the service is
// unavailable. Expects service not to be available when called.
void ReportServiceIsNotAvailable();
// Marks |cryptohome_id| as using ecryptfs (|use_ecryptfs|=true) or
// dircrypto
// (|use_ecryptfs|=false).
void SetHomeEncryptionMethod(
const cryptohome::AccountIdentifier& cryptohome_id,
HomeEncryptionMethod method);
// Marks |cryptohome_id| as failed previous migration attempt.
void SetEncryptionMigrationIncomplete(
const cryptohome::AccountIdentifier& cryptohome_id,
bool incomplete);
// Marks a PIN key as locked or unlocked. The key is identified by the
// |account_id| of the user it belongs to and its |label|. The key must
// exist prior to this call, and it must be a PIN key.
void SetPinLocked(const cryptohome::AccountIdentifier& account_id,
const std::string& label,
bool locked);
// Marks a user as existing and creates the user's home directory. No auth
// factors are added.
void AddExistingUser(const cryptohome::AccountIdentifier& account_id);
// Returns the user's home directory, or an empty optional if the user data
// directory is not initialized or the user doesn't exist.
std::optional<base::FilePath> GetUserProfileDir(
const cryptohome::AccountIdentifier& account_id) const;
// Creates user directories once UserDataDir is available.
void CreatePostponedDirectories();
// Adds the given key as a fake auth factor to the user (the user must
// already exist).
void AddAuthFactor(const cryptohome::AccountIdentifier& account_id,
const user_data_auth::AuthFactor& factor,
const user_data_auth::AuthInput& input);
void AddRecoveryFactor(const cryptohome::AccountIdentifier& account_id);
bool HasRecoveryFactor(const cryptohome::AccountIdentifier& account_id);
bool HasPinFactor(const cryptohome::AccountIdentifier& account_id);
// Returns {authsession_id, broadcast_id} pair.
std::pair<std::string, std::string> AddSession(
const cryptohome::AccountIdentifier& account_id,
bool authenticated);
// Checks that there is one active auth session and returns whether session
// is ephemeral.
bool IsCurrentSessionEphemeral();
void DestroySessions();
void SendLegacyFPAuthSignal(user_data_auth::FingerprintScanResult result);
// Sets the CryptohomeError value to return during next operation.
void SetNextOperationError(Operation operation,
::cryptohome::ErrorWrapper error);
bool IsAuthenticated(const cryptohome::AccountIdentifier& account_id);
private:
FakeUserDataAuthClient::UserCryptohomeState& GetUserState(
const cryptohome::AccountIdentifier& account_id);
};
// Represents the ongoing AuthSessions.
struct AuthSessionData {
explicit AuthSessionData();
AuthSessionData(const AuthSessionData& other);
AuthSessionData& operator=(const AuthSessionData&);
~AuthSessionData();
// AuthSession id.
std::string id;
std::string broadcast_id;
// Whether the is_ephemeral_user flag was set on creation.
bool ephemeral = false;
// Account associated with the session.
cryptohome::AccountIdentifier account;
// True if session is authenticated.
bool authenticated = false;
// The requested AuthIntent.
user_data_auth::AuthIntent requested_auth_session_intent =
user_data_auth::AUTH_INTENT_DECRYPT;
using AuthProtoIntents =
base::EnumSet<user_data_auth::AuthIntent,
user_data_auth::AuthIntent::AUTH_INTENT_UNSPECIFIED,
user_data_auth::AuthIntent::AUTH_INTENT_WEBAUTHN>;
// List of Authorized AuthIntents.
AuthProtoIntents authorized_auth_session_intent;
// Indication that session is set to listen for FP events.
bool is_listening_for_fingerprint_events = false;
base::Time lifetime;
};
FakeUserDataAuthClient();
~FakeUserDataAuthClient() override;
// Not copyable or movable.
FakeUserDataAuthClient(const FakeUserDataAuthClient&) = delete;
FakeUserDataAuthClient& operator=(const FakeUserDataAuthClient&) = delete;
// Checks that a FakeUserDataAuthClient instance was initialized and returns
// it.
static FakeUserDataAuthClient* Get();
// UserDataAuthClient override:
void AddObserver(Observer* observer) override;
void RemoveObserver(Observer* observer) override;
void AddFingerprintAuthObserver(FingerprintAuthObserver* observer) override;
void RemoveFingerprintAuthObserver(
FingerprintAuthObserver* observer) override;
void AddPrepareAuthFactorProgressObserver(
PrepareAuthFactorProgressObserver* observer) override;
void RemovePrepareAuthFactorProgressObserver(
PrepareAuthFactorProgressObserver* observer) override;
void AddAuthFactorStatusUpdateObserver(
AuthFactorStatusUpdateObserver* observer) override;
void RemoveAuthFactorStatusUpdateObserver(
AuthFactorStatusUpdateObserver* observer) override;
void WaitForServiceToBeAvailable(
chromeos::WaitForServiceToBeAvailableCallback callback) override;
void IsMounted(const ::user_data_auth::IsMountedRequest& request,
IsMountedCallback callback) override;
void GetVaultProperties(
const ::user_data_auth::GetVaultPropertiesRequest& request,
GetVaultPropertiesCallback callback) override;
void Unmount(const ::user_data_auth::UnmountRequest& request,
UnmountCallback callback) override;
void Remove(const ::user_data_auth::RemoveRequest& request,
RemoveCallback callback) override;
void StartMigrateToDircrypto(
const ::user_data_auth::StartMigrateToDircryptoRequest& request,
StartMigrateToDircryptoCallback callback) override;
void NeedsDircryptoMigration(
const ::user_data_auth::NeedsDircryptoMigrationRequest& request,
NeedsDircryptoMigrationCallback callback) override;
void GetSupportedKeyPolicies(
const ::user_data_auth::GetSupportedKeyPoliciesRequest& request,
GetSupportedKeyPoliciesCallback callback) override;
void GetAccountDiskUsage(
const ::user_data_auth::GetAccountDiskUsageRequest& request,
GetAccountDiskUsageCallback callback) override;
void StartAuthSession(
const ::user_data_auth::StartAuthSessionRequest& request,
StartAuthSessionCallback callback) override;
void PrepareGuestVault(
const ::user_data_auth::PrepareGuestVaultRequest& request,
PrepareGuestVaultCallback callback) override;
void PrepareEphemeralVault(
const ::user_data_auth::PrepareEphemeralVaultRequest& request,
PrepareEphemeralVaultCallback callback) override;
void CreatePersistentUser(
const ::user_data_auth::CreatePersistentUserRequest& request,
CreatePersistentUserCallback callback) override;
void PreparePersistentVault(
const ::user_data_auth::PreparePersistentVaultRequest& request,
PreparePersistentVaultCallback callback) override;
void PrepareVaultForMigration(
const ::user_data_auth::PrepareVaultForMigrationRequest& request,
PrepareVaultForMigrationCallback callback) override;
void InvalidateAuthSession(
const ::user_data_auth::InvalidateAuthSessionRequest& request,
InvalidateAuthSessionCallback callback) override;
void ExtendAuthSession(
const ::user_data_auth::ExtendAuthSessionRequest& request,
ExtendAuthSessionCallback callback) override;
void AddAuthFactor(const ::user_data_auth::AddAuthFactorRequest& request,
AddAuthFactorCallback callback) override;
void AuthenticateAuthFactor(
const ::user_data_auth::AuthenticateAuthFactorRequest& request,
AuthenticateAuthFactorCallback callback) override;
void UpdateAuthFactor(
const ::user_data_auth::UpdateAuthFactorRequest& request,
UpdateAuthFactorCallback callback) override;
void UpdateAuthFactorMetadata(
const ::user_data_auth::UpdateAuthFactorMetadataRequest& request,
UpdateAuthFactorMetadataCallback callback) override;
void ReplaceAuthFactor(
const ::user_data_auth::ReplaceAuthFactorRequest& request,
ReplaceAuthFactorCallback callback) override;
void RemoveAuthFactor(
const ::user_data_auth::RemoveAuthFactorRequest& request,
RemoveAuthFactorCallback callback) override;
void ListAuthFactors(const ::user_data_auth::ListAuthFactorsRequest& request,
ListAuthFactorsCallback callback) override;
void GetAuthFactorExtendedInfo(
const ::user_data_auth::GetAuthFactorExtendedInfoRequest& request,
GetAuthFactorExtendedInfoCallback callback) override;
void GetAuthSessionStatus(
const ::user_data_auth::GetAuthSessionStatusRequest& request,
GetAuthSessionStatusCallback callback) override;
void PrepareAuthFactor(
const ::user_data_auth::PrepareAuthFactorRequest& request,
PrepareAuthFactorCallback callback) override;
void TerminateAuthFactor(
const ::user_data_auth::TerminateAuthFactorRequest& request,
TerminateAuthFactorCallback callback) override;
void GetArcDiskFeatures(
const ::user_data_auth::GetArcDiskFeaturesRequest& request,
GetArcDiskFeaturesCallback callback) override;
void GetRecoverableKeyStores(
const ::user_data_auth::GetRecoverableKeyStoresRequest& request,
GetRecoverableKeyStoresCallback) override;
void SetUserDataStorageWriteEnabled(
const ::user_data_auth::SetUserDataStorageWriteEnabledRequest& request,
SetUserDataStorageWriteEnabledCallback callback) override;
int get_prepare_guest_request_count() const {
return prepare_guest_request_count_;
}
// Per-operation API:
template <Operation>
struct ProtobufTypes;
// Template magic to have mapping from operation to
// associated types for protobufs.
#define FUDAC_OPERATION_TYPES(OPERATION, REQUEST) \
template <> \
struct ProtobufTypes<Operation::OPERATION> { \
using RequestType = ::user_data_auth::REQUEST; \
}
FUDAC_OPERATION_TYPES(kStartAuthSession, StartAuthSessionRequest);
FUDAC_OPERATION_TYPES(kAuthenticateAuthFactor, AuthenticateAuthFactorRequest);
FUDAC_OPERATION_TYPES(kPrepareGuestVault, PrepareGuestVaultRequest);
FUDAC_OPERATION_TYPES(kPrepareEphemeralVault, PrepareEphemeralVaultRequest);
FUDAC_OPERATION_TYPES(kCreatePersistentUser, CreatePersistentUserRequest);
FUDAC_OPERATION_TYPES(kPreparePersistentVault, PreparePersistentVaultRequest);
FUDAC_OPERATION_TYPES(kPrepareVaultForMigration,
PrepareVaultForMigrationRequest);
FUDAC_OPERATION_TYPES(kAddAuthFactor, AddAuthFactorRequest);
FUDAC_OPERATION_TYPES(kUpdateAuthFactor, UpdateAuthFactorRequest);
FUDAC_OPERATION_TYPES(kUpdateAuthFactorMetadata,
UpdateAuthFactorMetadataRequest);
FUDAC_OPERATION_TYPES(kReplaceAuthFactor, ReplaceAuthFactorRequest);
FUDAC_OPERATION_TYPES(kListAuthFactors, ListAuthFactorsRequest);
FUDAC_OPERATION_TYPES(kStartMigrateToDircrypto,
StartMigrateToDircryptoRequest);
FUDAC_OPERATION_TYPES(kRemove, RemoveRequest);
FUDAC_OPERATION_TYPES(kGetRecoverableKeyStores,
GetRecoverableKeyStoresRequest);
#undef FUDAC_OPERATION_TYPES
// Sets the CryptohomeError value to return during next operation.
void SetNextOperationError(Operation operation,
::cryptohome::ErrorWrapper error);
// Checks if operation was called.
template <Operation Op>
bool WasCalled() {
const auto op_request = operation_requests_.find(Op);
return op_request != std::end(operation_requests_);
}
// Provides request protobuf passed to last call of operation.
// Would crash if operation was not called before, use `WasCalled()` to
// check.
template <Operation Op>
const typename ProtobufTypes<Op>::RequestType& GetLastRequest() {
const auto op_request = operation_requests_.find(Op);
CHECK(op_request != std::end(operation_requests_));
return *static_cast<const typename ProtobufTypes<Op>::RequestType*>(
op_request->second.get());
}
// See also `RememberRequest` in private section.
// Calls DircryptoMigrationProgress() on Observer instances.
void NotifyDircryptoMigrationProgress(
::user_data_auth::DircryptoMigrationStatus status,
uint64_t current,
uint64_t total);
// Calls LowDiskSpace() on Observer instances.
void NotifyLowDiskSpace(uint64_t disk_free_bytes);
// Reads synchronously from disk, so must only be called in a scope that
// allows blocking IO.
void SetUserDataDir(base::FilePath path);
private:
enum class AuthResult {
kAuthSuccess,
kUserNotFound,
kFactorNotFound,
kAuthFailed,
};
// Utility method to remember request passed to operation in a
// type-safe way.
template <Operation Op>
void RememberRequest(const typename ProtobufTypes<Op>::RequestType& request) {
operation_requests_[Op] =
std::make_unique<typename ProtobufTypes<Op>::RequestType>(request);
}
// Helper that returns the protobuf reply.
template <typename ReplyType>
void ReturnProtobufMethodCallback(
const ReplyType& reply,
chromeos::DBusMethodCallback<ReplyType> callback);
// This method is used to implement StartMigrateToDircrypto with simulated
// progress updates.
void OnDircryptoMigrationProgressUpdated();
// Returns a path to home directory for account.
std::optional<base::FilePath> GetUserProfileDir(
const cryptohome::AccountIdentifier& account_id) const;
// The method takes serialized auth session id and returns an authenticated
// auth session associated with the id. If the session is missing or not
// authenticated, an error code is assigned to |*error| and |nullptr| is
// returned.
const AuthSessionData* GetAuthenticatedAuthSession(
const std::string& auth_session_id,
::cryptohome::ErrorWrapper* error) const;
void RunPendingWaitForServiceToBeAvailableCallbacks();
// Checks the given credentials against the fake factors configured for the
// given user. If `wildcard_allowed` is true and `factor_label` is empty,
// every configured factor is attempted; `matched_factor_label` can be passed
// in order to know the found factor's label.
AuthResult AuthenticateViaAuthFactors(
const cryptohome::AccountIdentifier& account_id,
const std::string& factor_label,
const std::string& secret,
bool wildcard_allowed,
std::string* matched_factor_label = nullptr) const;
// Checks if there is a per-operation error defined, and uses it.
::cryptohome::ErrorWrapper TakeOperationError(Operation operation);
int prepare_guest_request_count_ = 0;
// The error that would be triggered once operation is called.
base::flat_map<Operation, ::cryptohome::ErrorWrapper> operation_errors_;
// Remembered requests.
base::flat_map<Operation, std::unique_ptr<::google::protobuf::MessageLite>>
operation_requests_;
// The collection of users we know about.
base::flat_map<cryptohome::AccountIdentifier, UserCryptohomeState> users_;
// Timer for triggering the dircrypto migration progress signal.
base::RepeatingTimer dircrypto_migration_progress_timer_;
// The current dircrypto migration progress indicator, used when we trigger
// the migration progress signal.
uint64_t dircrypto_migration_progress_ = 0;
// The auth sessions on file.
base::flat_map<std::string, AuthSessionData> auth_sessions_;
// Next available auth session id.
int next_auth_session_id_ = 0;
// The list of callbacks passed to WaitForServiceToBeAvailable when the
// service wasn't available.
std::vector<chromeos::WaitForServiceToBeAvailableCallback>
pending_wait_for_service_to_be_available_callbacks_;
// The list of usernames of users with mounted user dirs.
std::set<std::string> mounted_user_dirs_;
// Other stuff/miscellaneous:
// Base directory of user directories.
std::optional<base::FilePath> user_data_dir_;
// List of observers.
base::ObserverList<Observer> observer_list_;
// List of legacy fingerprint event observers.
base::ObserverList<FingerprintAuthObserver> fingerprint_observers_;
// List of PrepareAuthFactorProgress event observers.
base::ObserverList<PrepareAuthFactorProgressObserver> progress_observers_;
// List of observers for dbus signal AuthFactorStatusUpdate.
base::ObserverList<AuthFactorStatusUpdateObserver>
auth_factor_status_observer_list_;
// Do we run the dircrypto migration, as in, emit signals, when
// StartMigrateToDircrypto() is called?
bool run_default_dircrypto_migration_ = true;
// If low entropy credentials are supported for the key. This is the value
// that GetSupportedKeyPolicies() returns.
bool supports_low_entropy_credentials_ = false;
// If true, authentication requests actually check the key.
bool enable_auth_check_ = false;
// If set, we tell callers that service is available.
bool service_is_available_ = true;
// Whether ARC disk quota is supported or not.
bool arc_quota_supported_ = true;
// If set, WaitForServiceToBeAvailable will run the callback, even if
// service is not available (instead of adding the callback to pending
// callback list).
bool service_reported_not_available_ = false;
};
} // namespace ash
#endif // CHROMEOS_ASH_COMPONENTS_DBUS_USERDATAAUTH_FAKE_USERDATAAUTH_CLIENT_H_