| // Copyright 2020 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cryptohome/auth_session.h" |
| |
| #include <algorithm> |
| #include <limits> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| #include <base/check.h> |
| #include <base/check_op.h> |
| #include <base/containers/flat_set.h> |
| #include <base/containers/span.h> |
| #include <base/logging.h> |
| #include <base/memory/ptr_util.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/time/time.h> |
| #include <brillo/cryptohome.h> |
| #include <brillo/secure_blob.h> |
| #include <cryptohome/proto_bindings/UserDataAuth.pb.h> |
| #include <libhwsec-foundation/crypto/aes.h> |
| #include <libhwsec-foundation/crypto/hmac.h> |
| #include <libhwsec-foundation/crypto/secure_blob_util.h> |
| |
| #include "base/functional/callback_forward.h" |
| #include "base/functional/callback_helpers.h" |
| #include "cryptohome/auth_blocks/auth_block.h" |
| #include "cryptohome/auth_blocks/auth_block_type.h" |
| #include "cryptohome/auth_blocks/auth_block_utility.h" |
| #include "cryptohome/auth_blocks/auth_block_utils.h" |
| #include "cryptohome/auth_factor/auth_factor.h" |
| #include "cryptohome/auth_factor/auth_factor_label_arity.h" |
| #include "cryptohome/auth_factor/auth_factor_manager.h" |
| #include "cryptohome/auth_factor/auth_factor_metadata.h" |
| #include "cryptohome/auth_factor/auth_factor_prepare_purpose.h" |
| #include "cryptohome/auth_factor/auth_factor_storage_type.h" |
| #include "cryptohome/auth_factor/auth_factor_type.h" |
| #include "cryptohome/auth_factor/protobuf.h" |
| #include "cryptohome/auth_factor/types/interface.h" |
| #include "cryptohome/auth_factor/types/pin.h" |
| #include "cryptohome/auth_factor/with_driver.h" |
| #include "cryptohome/auth_factor_vault_keyset_converter.h" |
| #include "cryptohome/auth_input_utils.h" |
| #include "cryptohome/auth_session_protobuf.h" |
| #include "cryptohome/credential_verifier.h" |
| #include "cryptohome/cryptohome_metrics.h" |
| #include "cryptohome/cryptorecovery/recovery_crypto_util.h" |
| #include "cryptohome/error/converter.h" |
| #include "cryptohome/error/cryptohome_crypto_error.h" |
| #include "cryptohome/error/cryptohome_error.h" |
| #include "cryptohome/error/location_utils.h" |
| #include "cryptohome/error/reap.h" |
| #include "cryptohome/error/reporting.h" |
| #include "cryptohome/error/utilities.h" |
| #include "cryptohome/features.h" |
| #include "cryptohome/filesystem_layout.h" |
| #include "cryptohome/flatbuffer_schemas/auth_block_state.h" |
| #include "cryptohome/flatbuffer_schemas/auth_factor.h" |
| #include "cryptohome/keyset_management.h" |
| #include "cryptohome/platform.h" |
| #include "cryptohome/signature_sealing/structures_proto.h" |
| #include "cryptohome/storage/file_system_keyset.h" |
| #include "cryptohome/user_secret_stash/migrator.h" |
| #include "cryptohome/user_secret_stash/storage.h" |
| #include "cryptohome/user_secret_stash/user_secret_stash.h" |
| #include "cryptohome/user_session/user_session_map.h" |
| #include "cryptohome/username.h" |
| #include "cryptohome/vault_keyset.h" |
| |
| namespace cryptohome { |
| namespace { |
| |
| using brillo::cryptohome::home::SanitizeUserName; |
| using cryptohome::error::CryptohomeCryptoError; |
| using cryptohome::error::CryptohomeError; |
| using cryptohome::error::CryptohomeMountError; |
| using cryptohome::error::ErrorActionSet; |
| using cryptohome::error::PossibleAction; |
| using cryptohome::error::PrimaryAction; |
| using cryptohome::error::PrimaryActionIs; |
| using cryptohome::error::ReportCryptohomeOk; |
| using hwsec_foundation::CreateSecureRandomBlob; |
| using hwsec_foundation::HmacSha256; |
| using hwsec_foundation::kAesBlockSize; |
| using hwsec_foundation::status::MakeStatus; |
| using hwsec_foundation::status::OkStatus; |
| using hwsec_foundation::status::StatusChain; |
| using user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_EPHEMERAL_USER; |
| |
| // Size of the values used serialization of UnguessableToken. |
| constexpr int kSizeOfSerializedValueInToken = sizeof(uint64_t); |
| // Number of uint64 used serialization of UnguessableToken. |
| constexpr int kNumberOfSerializedValuesInToken = 2; |
| // Offset where the high value is used in Serialized string. |
| constexpr int kHighTokenOffset = 0; |
| // Offset where the low value is used in Serialized string. |
| constexpr int kLowTokenOffset = kSizeOfSerializedValueInToken; |
| // Upper limit of the Size of user specified name. |
| constexpr int kUserSpecifiedNameSizeLimit = 256; |
| // Message to use when generating a secret for hibernate. |
| constexpr char kHibernateSecretHmacMessage[] = "AuthTimeHibernateSecret"; |
| // This is the frequency with which a signal is sent for a locked out user, |
| // unless the lockout time is less than this. |
| const base::TimeDelta kAuthFactorStatusUpdateDelay = base::Seconds(30); |
| // This is the post auth action that means no action needs to be taken. |
| AuthSession::PostAuthAction kNoPostAction{ |
| .action_type = AuthSession::PostAuthActionType::kNone, |
| .repeat_request = std::nullopt, |
| }; |
| |
| // Check if a given type of AuthFactor supports Vault Keysets. |
| constexpr bool IsFactorTypeSupportedByVk(AuthFactorType auth_factor_type) { |
| return auth_factor_type == AuthFactorType::kPassword || |
| auth_factor_type == AuthFactorType::kPin || |
| auth_factor_type == AuthFactorType::kSmartCard || |
| auth_factor_type == AuthFactorType::kKiosk; |
| } |
| |
| constexpr std::string_view IntentToDebugString(AuthIntent intent) { |
| switch (intent) { |
| case AuthIntent::kDecrypt: |
| return "decrypt"; |
| case AuthIntent::kVerifyOnly: |
| return "verify-only"; |
| case AuthIntent::kWebAuthn: |
| return "webauthn"; |
| } |
| } |
| |
| std::string IntentSetToDebugString(const base::flat_set<AuthIntent>& intents) { |
| std::vector<std::string_view> strings; |
| strings.reserve(intents.size()); |
| for (auto intent : intents) { |
| strings.push_back(IntentToDebugString(intent)); |
| } |
| return base::JoinString(strings, ","); |
| } |
| |
| cryptorecovery::RequestMetadata RequestMetadataFromProto( |
| const user_data_auth::GetRecoveryRequestRequest& request) { |
| cryptorecovery::RequestMetadata result; |
| |
| result.requestor_user_id = request.requestor_user_id(); |
| switch (request.requestor_user_id_type()) { |
| case user_data_auth::GetRecoveryRequestRequest_UserType_GAIA_ID: |
| result.requestor_user_id_type = cryptorecovery::UserType::kGaiaId; |
| break; |
| case user_data_auth::GetRecoveryRequestRequest_UserType_UNKNOWN: |
| default: |
| result.requestor_user_id_type = cryptorecovery::UserType::kUnknown; |
| break; |
| } |
| |
| result.auth_claim = cryptorecovery::AuthClaim{ |
| .gaia_access_token = request.gaia_access_token(), |
| .gaia_reauth_proof_token = request.gaia_reauth_proof_token(), |
| }; |
| |
| return result; |
| } |
| |
| // Generates a PIN reset secret from the |reset_seed| of the passed password |
| // VaultKeyset and updates the AuthInput |reset_seed|, |reset_salt| and |
| // |reset_secret| values. |
| CryptohomeStatusOr<AuthInput> UpdateAuthInputWithResetParamsFromPasswordVk( |
| const AuthInput& auth_input, const VaultKeyset& vault_keyset) { |
| if (!vault_keyset.HasWrappedResetSeed()) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocUpdateAuthInputNoWrappedSeedInVaultKeyset), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| if (vault_keyset.GetResetSeed().empty()) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocUpdateAuthInputResetSeedEmptyInVaultKeyset), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| AuthInput out_auth_input = auth_input; |
| out_auth_input.reset_seed = vault_keyset.GetResetSeed(); |
| out_auth_input.reset_salt = CreateSecureRandomBlob(kAesBlockSize); |
| out_auth_input.reset_secret = HmacSha256(out_auth_input.reset_salt.value(), |
| out_auth_input.reset_seed.value()); |
| LOG(INFO) << "Reset seed, to generate the reset_secret for the PIN factor, " |
| "is obtained from password VaultKeyset with label: " |
| << vault_keyset.GetLabel(); |
| return out_auth_input; |
| } |
| |
| // Utility function to force-remove a keyset file for |obfuscated_username| |
| // identified by |label|. |
| CryptohomeStatus RemoveKeysetByLabel( |
| KeysetManagement& keyset_management, |
| const ObfuscatedUsername& obfuscated_username, |
| const std::string& label) { |
| std::unique_ptr<VaultKeyset> remove_vk = |
| keyset_management.GetVaultKeyset(obfuscated_username, label); |
| if (!remove_vk.get()) { |
| LOG(WARNING) << "RemoveKeysetByLabel: key to remove not found."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionVKNotFoundInRemoveKeysetByLabel), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND); |
| } |
| |
| CryptohomeStatus status = keyset_management.ForceRemoveKeyset( |
| obfuscated_username, remove_vk->GetLegacyIndex()); |
| if (!status.ok()) { |
| LOG(ERROR) << "RemoveKeysetByLabel: failed to remove keyset file."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionRemoveFailedInRemoveKeysetByLabel), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE) |
| .Wrap(std::move(status)); |
| } |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| // Removes the backup VaultKeyset with the given label. Returns success if |
| // there's no keyset found. |
| CryptohomeStatus CleanUpBackupKeyset( |
| KeysetManagement& keyset_management, |
| const ObfuscatedUsername& obfuscated_username, |
| const std::string& label) { |
| std::unique_ptr<VaultKeyset> remove_vk = |
| keyset_management.GetVaultKeyset(obfuscated_username, label); |
| if (!remove_vk.get() || !remove_vk->IsForBackup()) { |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| CryptohomeStatus status = keyset_management.RemoveKeysetFile(*remove_vk); |
| if (!status.ok()) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionRemoveFailedInCleanUpBackupKeyset), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE) |
| .Wrap(std::move(status)); |
| } |
| LOG(INFO) << "Removed backup keyset with label: " << label; |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| // Calculates and returns the reset secret for the PIN VaultKeyset with |label| |
| // if it exists and has |reset_salt|, returns nullopt otherwise. |
| std::optional<brillo::SecureBlob> GetResetSecretFromVaultKeyset( |
| const brillo::SecureBlob& reset_seed, |
| const ObfuscatedUsername& obfuscated_username, |
| const std::string& label, |
| const KeysetManagement& keyset_management) { |
| std::unique_ptr<VaultKeyset> vk = |
| keyset_management.GetVaultKeyset(obfuscated_username, label); |
| if (vk == nullptr) { |
| LOG(WARNING) << "Pin VK for the reset could not be retrieved for " << label |
| << "."; |
| return std::nullopt; |
| } |
| brillo::SecureBlob reset_salt = vk->GetResetSalt(); |
| if (reset_salt.empty()) { |
| LOG(WARNING) << "Reset salt is empty in VK with label: " << label; |
| return std::nullopt; |
| } |
| std::optional<brillo::SecureBlob> reset_secret = |
| HmacSha256(reset_salt, reset_seed); |
| LOG(INFO) << "Reset secret for " << label << " is captured from VaultKeyset"; |
| return reset_secret; |
| } |
| |
| // Removes the backup VaultKeysets. |
| CryptohomeStatus CleanUpAllBackupKeysets( |
| KeysetManagement& keyset_management, |
| const ObfuscatedUsername& obfuscated_username, |
| const AuthFactorMap& auth_factor_map) { |
| for (auto item : auth_factor_map) { |
| CryptohomeStatus status = CleanUpBackupKeyset( |
| keyset_management, obfuscated_username, item.auth_factor().label()); |
| if (!status.ok()) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionRemoveFailedInCleanUpAllBackupKeysets), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE) |
| .Wrap(std::move(status)); |
| } |
| } |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| void ReportRecreateAuthFactorError(CryptohomeStatus status, |
| AuthFactorType auth_factor_type) { |
| const std::array<std::string, 2> error_bucket_paths{ |
| kCryptohomeErrorRecreateAuthFactorErrorBucket, |
| AuthFactorTypeToCamelCaseString(auth_factor_type)}; |
| ReapAndReportError(std::move(status), error_bucket_paths); |
| } |
| |
| void ReportRecreateAuthFactorOk(AuthFactorType auth_factor_type) { |
| const std::array<std::string, 2> error_bucket_paths{ |
| kCryptohomeErrorRecreateAuthFactorErrorBucket, |
| AuthFactorTypeToCamelCaseString(auth_factor_type)}; |
| ReportCryptohomeOk(error_bucket_paths); |
| } |
| |
| template <typename... Args> |
| void ReportAndExecuteCallback( |
| base::OnceCallback<void(Args..., CryptohomeStatus)> callback, |
| AuthFactorType auth_factor_type, |
| const std::string& bucket_name, |
| Args... args, |
| CryptohomeStatus status) { |
| const std::array<std::string, 2> error_bucket_paths{ |
| bucket_name, AuthFactorTypeToCamelCaseString(auth_factor_type)}; |
| ReportOperationStatus(status, error_bucket_paths); |
| std::move(callback).Run(std::move(args)..., std::move(status)); |
| } |
| |
| template <typename... Args> |
| base::OnceCallback<void(Args..., CryptohomeStatus)> |
| WrapCallbackWithMetricsReporting( |
| base::OnceCallback<void(Args..., CryptohomeStatus)> callback, |
| AuthFactorType auth_factor_type, |
| std::string bucket_name) { |
| return base::BindOnce(&ReportAndExecuteCallback<Args...>, std::move(callback), |
| auth_factor_type, std::move(bucket_name)); |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<AuthSession> AuthSession::Create(Username account_id, |
| unsigned int flags, |
| AuthIntent intent, |
| BackingApis backing_apis) { |
| ObfuscatedUsername obfuscated_username = SanitizeUserName(account_id); |
| |
| // Try to determine if a user exists in two ways: they have a persistent |
| // homedir, or they have an active mount. The latter can happen if the user is |
| // ephemeral, in which case there will be no persistent directory but the user |
| // still "exists" so long as they remain active. |
| bool persistent_user_exists = |
| backing_apis.platform->DirectoryExists(UserPath(obfuscated_username)); |
| UserSession* user_session = backing_apis.user_session_map->Find(account_id); |
| bool user_is_active = user_session && user_session->IsActive(); |
| bool user_exists = persistent_user_exists || user_is_active; |
| |
| // If we have an existing persistent user, load all of their auth factors. |
| AuthFactorMap auth_factor_map; |
| if (persistent_user_exists) { |
| AuthFactorVaultKeysetConverter converter(backing_apis.keyset_management); |
| auth_factor_map = backing_apis.auth_factor_manager->LoadAllAuthFactors( |
| obfuscated_username, converter); |
| |
| // If only uss factors exists, then we should remove all the backups. |
| if (!auth_factor_map.HasFactorWithStorage( |
| AuthFactorStorageType::kVaultKeyset)) { |
| CryptohomeStatus cleanup_status = |
| CleanUpAllBackupKeysets(*backing_apis.keyset_management, |
| obfuscated_username, auth_factor_map); |
| if (!cleanup_status.ok()) { |
| LOG(WARNING) << "Cleaning up backup keysets failed."; |
| // Error can be ignored. |
| } |
| } |
| } |
| // Assumption here is that keyset_management_ will outlive this AuthSession. |
| AuthSession::Params params = { |
| .username = std::move(account_id), |
| .is_ephemeral_user = flags & AUTH_SESSION_FLAGS_EPHEMERAL_USER, |
| .intent = intent, |
| .auth_factor_status_update_timer = |
| std::make_unique<base::WallClockTimer>(), |
| .user_exists = user_exists, |
| .auth_factor_map = std::move(auth_factor_map)}; |
| return std::make_unique<AuthSession>(std::move(params), backing_apis); |
| } |
| |
| AuthSession::AuthSession(Params params, BackingApis backing_apis) |
| : username_(std::move(*params.username)), |
| obfuscated_username_(SanitizeUserName(username_)), |
| is_ephemeral_user_(*params.is_ephemeral_user), |
| auth_intent_(*params.intent), |
| auth_factor_status_update_timer_( |
| std::move(params.auth_factor_status_update_timer)), |
| auth_session_creation_time_(base::TimeTicks::Now()), |
| crypto_(backing_apis.crypto), |
| platform_(backing_apis.platform), |
| user_session_map_(backing_apis.user_session_map), |
| verifier_forwarder_(username_, user_session_map_), |
| keyset_management_(backing_apis.keyset_management), |
| auth_block_utility_(backing_apis.auth_block_utility), |
| auth_factor_driver_manager_(backing_apis.auth_factor_driver_manager), |
| auth_factor_manager_(backing_apis.auth_factor_manager), |
| user_secret_stash_storage_(backing_apis.user_secret_stash_storage), |
| user_metadata_reader_(backing_apis.user_metadata_reader), |
| features_(backing_apis.features), |
| converter_(keyset_management_), |
| token_(platform_->CreateUnguessableToken()), |
| serialized_token_(GetSerializedStringFromToken(token_).value_or("")), |
| public_token_(platform_->CreateUnguessableToken()), |
| serialized_public_token_( |
| GetSerializedStringFromToken(public_token_).value_or("")), |
| user_exists_(*params.user_exists), |
| auth_factor_map_(std::move(params.auth_factor_map)) { |
| CHECK(!serialized_token_.empty()); |
| CHECK(auth_factor_status_update_timer_); |
| CHECK(crypto_); |
| CHECK(platform_); |
| CHECK(user_session_map_); |
| CHECK(keyset_management_); |
| CHECK(auth_block_utility_); |
| CHECK(auth_factor_manager_); |
| CHECK(user_secret_stash_storage_); |
| CHECK(features_); |
| auth_factor_map_.ReportAuthFactorBackingStoreMetrics(); |
| RecordAuthSessionStart(); |
| } |
| |
| AuthSession::~AuthSession() { |
| std::string append_string = is_ephemeral_user_ ? ".Ephemeral" : ".Persistent"; |
| ReportTimerDuration(kAuthSessionTotalLifetimeTimer, |
| auth_session_creation_time_, append_string); |
| ReportTimerDuration(kAuthSessionAuthenticatedLifetimeTimer, |
| authenticated_time_, append_string); |
| } |
| |
| void AuthSession::RecordAuthSessionStart() const { |
| std::vector<std::string> factors; |
| factors.reserve(auth_factor_map_.size()); |
| for (AuthFactorMap::ValueView item : auth_factor_map_) { |
| factors.push_back(base::StringPrintf( |
| "%s(type %d %s)", item.auth_factor().label().c_str(), |
| static_cast<int>(item.auth_factor().type()), |
| AuthFactorStorageTypeToDebugString(item.storage_type()))); |
| } |
| LOG(INFO) << "AuthSession: started with is_ephemeral_user=" |
| << is_ephemeral_user_ |
| << " intent=" << IntentToDebugString(auth_intent_) |
| << " user_exists=" << user_exists_ |
| << " factors=" << base::JoinString(factors, ",") << "."; |
| } |
| |
| void AuthSession::SetAuthorizedForIntents( |
| base::span<const AuthIntent> new_authorized_intents) { |
| if (new_authorized_intents.empty()) { |
| NOTREACHED() << "Empty intent set cannot be authorized"; |
| return; |
| } |
| authorized_intents_.insert(new_authorized_intents.begin(), |
| new_authorized_intents.end()); |
| if (authorized_intents_.contains(AuthIntent::kDecrypt)) { |
| // Record time of authentication for metric keeping. |
| authenticated_time_ = base::TimeTicks::Now(); |
| } |
| LOG(INFO) << "AuthSession: authorized for " |
| << IntentSetToDebugString(authorized_intents_) << "."; |
| |
| // Trigger all of the on-auth callbacks. |
| std::vector<base::OnceClosure> callbacks; |
| std::swap(callbacks, on_auth_); |
| for (base::OnceClosure& callback : callbacks) { |
| std::move(callback).Run(); |
| } |
| } |
| |
| void AuthSession::SetAuthorizedForFullAuthIntents( |
| AuthFactorType auth_factor_type) { |
| // Determine what intents are allowed for this factor type under full auth. |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor_type); |
| std::vector<AuthIntent> authorized_for; |
| for (AuthIntent intent : kAuthorizedIntentsForFullAuth) { |
| if (factor_driver.IsFullAuthSupported(intent)) { |
| authorized_for.push_back(intent); |
| } |
| } |
| // Authorize the session for the subset of intents we found. |
| SetAuthorizedForIntents(authorized_for); |
| } |
| |
| void AuthSession::SendAuthFactorStatusUpdateSignal() { |
| // If the auth factor status update callback is not set (testing purposes), |
| // then we won't need to send a signal. |
| if (!auth_factor_status_update_callback_) { |
| LOG(ERROR) << "Auth factor status update callback has not been set."; |
| return; |
| } |
| for (AuthFactorMap::ValueView item : auth_factor_map_) { |
| const AuthFactor& auth_factor = item.auth_factor(); |
| AuthFactorDriver& driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor.type()); |
| // Skip this entire process for factors which don't support delays. |
| if (!driver.IsDelaySupported()) { |
| continue; |
| } |
| |
| auto auth_factor_proto = |
| driver.ConvertToProto(auth_factor.label(), auth_factor.metadata()); |
| if (!auth_factor_proto) { |
| continue; |
| } |
| user_data_auth::AuthFactorWithStatus auth_factor_with_status; |
| *auth_factor_with_status.mutable_auth_factor() = |
| std::move(*auth_factor_proto); |
| auto supported_intents = GetSupportedIntents( |
| obfuscated_username_, auth_factor, *auth_factor_driver_manager_); |
| for (const auto& auth_intent : supported_intents) { |
| auth_factor_with_status.add_available_for_intents( |
| AuthIntentToProto(auth_intent)); |
| } |
| |
| auto delay = driver.GetFactorDelay(obfuscated_username_, auth_factor); |
| if (delay.ok()) { |
| auth_factor_with_status.mutable_status_info()->set_time_available_in( |
| delay->is_max() ? std::numeric_limits<uint64_t>::max() |
| : delay->InMilliseconds()); |
| auth_factor_status_update_callback_.Run(auth_factor_with_status, |
| serialized_public_token_); |
| if (delay->is_zero()) { |
| continue; |
| } |
| base::TimeDelta next_signal_delay = |
| std::min(*delay, kAuthFactorStatusUpdateDelay); |
| auth_factor_status_update_timer_->Start( |
| FROM_HERE, base::Time::Now() + next_signal_delay, |
| base::BindOnce(&AuthSession::SendAuthFactorStatusUpdateSignal, |
| base::Unretained(this))); |
| } |
| } |
| } |
| |
| CryptohomeStatus AuthSession::OnUserCreated() { |
| // Since this function is called for a new user, it is safe to put the |
| // AuthSession in an authenticated state. |
| SetAuthorizedForIntents(kAuthorizedIntentsForFullAuth); |
| user_exists_ = true; |
| |
| if (!is_ephemeral_user_) { |
| // Creating file_system_keyset to the prepareVault call next. |
| if (!file_system_keyset_.has_value()) { |
| file_system_keyset_ = FileSystemKeyset::CreateRandom(); |
| } |
| if (IsUserSecretStashExperimentEnabled(platform_)) { |
| // Check invariants. |
| CHECK(!user_secret_stash_); |
| CHECK(!user_secret_stash_main_key_.has_value()); |
| CHECK(file_system_keyset_.has_value()); |
| // The USS experiment is on, hence create the USS for the newly created |
| // non-ephemeral user. Keep the USS in memory: it will be persisted after |
| // the first auth factor gets added. |
| CryptohomeStatusOr<std::unique_ptr<UserSecretStash>> uss_status = |
| UserSecretStash::CreateRandom(file_system_keyset_.value()); |
| if (!uss_status.ok()) { |
| LOG(ERROR) << "User secret stash creation failed"; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionCreateUSSFailedInOnUserCreated), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState, |
| PossibleAction::kReboot}), |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_MOUNT_FATAL); |
| } |
| user_secret_stash_ = std::move(uss_status).value(); |
| user_secret_stash_main_key_ = UserSecretStash::CreateRandomMainKey(); |
| } |
| } |
| |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| void AuthSession::CreateAndPersistVaultKeyset( |
| const KeyData& key_data, |
| AuthInput auth_input, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| StatusCallback on_done, |
| CryptohomeStatus callback_error, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::unique_ptr<AuthBlockState> auth_state) { |
| // callback_error, key_blobs and auth_state are returned by |
| // AuthBlock::CreateCallback. |
| if (!callback_error.ok() || key_blobs == nullptr || auth_state == nullptr) { |
| if (callback_error.ok()) { |
| callback_error = MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNullParamInCallbackInAddKeyset), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO, |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_NOT_IMPLEMENTED); |
| } |
| LOG(ERROR) << "KeyBlobs derivation failed before adding keyset."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionCreateFailedInAddKeyset), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED) |
| .Wrap(std::move(callback_error))); |
| return; |
| } |
| |
| CryptohomeStatus status = |
| AddVaultKeyset(key_data.label(), key_data, |
| !auth_factor_map_.HasFactorWithStorage( |
| AuthFactorStorageType::kVaultKeyset), |
| VaultKeysetIntent{.backup = false}, std::move(key_blobs), |
| std::move(auth_state)); |
| |
| if (!status.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionAddVaultKeysetFailedinAddAuthFactor), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED) |
| .Wrap(std::move(status))); |
| return; |
| } |
| |
| std::unique_ptr<AuthFactor> added_auth_factor = |
| converter_.VaultKeysetToAuthFactor(obfuscated_username_, |
| key_data.label()); |
| // Initialize auth_factor_type with kPassword for CredentailVerifier. |
| AuthFactorType auth_factor_type = AuthFactorType::kPassword; |
| if (added_auth_factor) { |
| auth_factor_type = added_auth_factor->type(); |
| auth_factor_map_.Add(std::move(added_auth_factor), |
| AuthFactorStorageType::kVaultKeyset); |
| } else { |
| LOG(WARNING) << "Failed to convert added keyset to AuthFactor."; |
| } |
| |
| AddCredentialVerifier(auth_factor_type, key_data.label(), auth_input); |
| |
| // Report timer for how long AuthSession operation takes. |
| ReportTimerDuration(auth_session_performance_timer.get()); |
| std::move(on_done).Run(OkStatus<CryptohomeError>()); |
| } |
| |
| CryptohomeStatus AuthSession::AddVaultKeyset( |
| const std::string& key_label, |
| const KeyData& key_data, |
| bool is_initial_keyset, |
| VaultKeysetIntent vk_backup_intent, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::unique_ptr<AuthBlockState> auth_state) { |
| CHECK(key_blobs); |
| CHECK(auth_state); |
| if (is_initial_keyset) { |
| if (!file_system_keyset_.has_value()) { |
| LOG(ERROR) << "AddInitialKeyset: file_system_keyset is invalid."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNoFSKeyInAddKeyset), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState, |
| PossibleAction::kReboot}), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED); |
| } |
| // TODO(b/229825202): Migrate KeysetManagement and wrap the returned error. |
| CryptohomeStatusOr<std::unique_ptr<VaultKeyset>> vk_status = |
| keyset_management_->AddInitialKeyset( |
| vk_backup_intent, obfuscated_username_, key_data, |
| /*challenge_credentials_keyset_info*/ std::nullopt, |
| file_system_keyset_.value(), std::move(*key_blobs.get()), |
| std::move(auth_state)); |
| if (!vk_status.ok()) { |
| vault_keyset_ = nullptr; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionAddInitialFailedInAddKeyset), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState, |
| PossibleAction::kReboot}), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED); |
| } |
| LOG(INFO) << "AuthSession: added initial keyset " << key_data.label() |
| << "."; |
| vault_keyset_ = std::move(vk_status).value(); |
| } else { |
| if (!vault_keyset_) { |
| // This shouldn't normally happen, but is possible if, e.g., the backup VK |
| // is corrupted and the authentication completed via USS. |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNoVkInAddKeyset), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED); |
| } |
| CryptohomeStatus status = keyset_management_->AddKeyset( |
| vk_backup_intent, obfuscated_username_, key_label, key_data, |
| *vault_keyset_.get(), std::move(*key_blobs.get()), |
| std::move(auth_state), true /*clobber*/); |
| if (!status.ok()) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionAddFailedInAddKeyset)) |
| .Wrap(std::move(status)); |
| } |
| LOG(INFO) << "AuthSession: added additional keyset " << key_label << "."; |
| } |
| |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| void AuthSession::MigrateToUssDuringUpdateVaultKeyset( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthFactorMetadata& auth_factor_metadata, |
| const KeyData& key_data, |
| const AuthInput& auth_input, |
| StatusCallback on_done, |
| CryptohomeStatus callback_error, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::unique_ptr<AuthBlockState> auth_block_state) { |
| // Update can happen only during an authenticated AuthSession. |
| CHECK(file_system_keyset_.has_value()); |
| |
| if (!callback_error.ok() || key_blobs == nullptr || |
| auth_block_state == nullptr) { |
| if (callback_error.ok()) { |
| callback_error = MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNullParamInCallbackInUpdateKeyset), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO, |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_NOT_IMPLEMENTED); |
| } |
| LOG(ERROR) << "KeyBlobs derivation failed before updating keyset."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionCreateFailedInUpdateKeyset)) |
| .Wrap(std::move(callback_error))); |
| return; |
| } |
| |
| // Add the new secret to the AuthSession's credential verifier. On successful |
| // completion of the UpdateAuthFactor this will be passed to UserSession's |
| // credential verifier to cache the secret for future lightweight |
| // verifications. |
| AddCredentialVerifier(auth_factor_type, auth_factor_label, auth_input); |
| |
| if (IsUserSecretStashExperimentEnabled(platform_)) { |
| UssMigrator migrator(username_); |
| // FilesystemKeyset is the same for all VaultKeysets hence the session's |
| // |file_system_keyset_| is what we need for the migrator. |
| migrator.MigrateVaultKeysetToUss( |
| *user_secret_stash_storage_, auth_factor_label, |
| file_system_keyset_.value(), |
| base::BindOnce(&AuthSession::OnMigrationUssCreatedForUpdate, |
| weak_factory_.GetWeakPtr(), auth_factor_type, |
| auth_factor_label, auth_factor_metadata, auth_input, |
| std::move(on_done), std::move(callback_error), |
| std::move(key_blobs), std::move(auth_block_state))); |
| // Since migration removes the keyset file, we don't update the keyset file. |
| return; |
| } |
| |
| CryptohomeStatus status = keyset_management_->UpdateKeysetWithKeyBlobs( |
| VaultKeysetIntent{.backup = false}, obfuscated_username_, key_data, |
| *vault_keyset_.get(), std::move(*key_blobs.get()), |
| std::move(auth_block_state)); |
| if (!status.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionUpdateWithBlobFailedInUpdateKeyset)) |
| .Wrap(std::move(status))); |
| } |
| std::move(on_done).Run(OkStatus<CryptohomeError>()); |
| } |
| |
| void AuthSession::AuthenticateViaVaultKeysetAndMigrateToUss( |
| AuthFactorType request_auth_factor_type, |
| const std::string& key_label, |
| const AuthInput& auth_input, |
| const AuthFactorMetadata& metadata, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| StatusCallback on_done) { |
| // Identify the key via `key_label` instead of `key_data_.label()`, as the |
| // latter can be empty for legacy keysets. |
| std::unique_ptr<VaultKeyset> vault_keyset = |
| keyset_management_->GetVaultKeyset(obfuscated_username_, key_label); |
| if (!vault_keyset) { |
| LOG(ERROR) |
| << "No vault keyset is found on disk for label " << key_label |
| << ". Cannot obtain AuthBlockState without vault keyset metadata."; |
| std::move(on_done).Run(MakeStatus<error::CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionVaultKeysetMissingInAuthViaVaultKey), |
| ErrorActionSet({error::PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)); |
| return; |
| } |
| AuthBlockState auth_state; |
| if (!GetAuthBlockState(*vault_keyset, auth_state)) { |
| LOG(ERROR) << "Error in obtaining AuthBlock state for key derivation."; |
| std::move(on_done).Run(MakeStatus<error::CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionBlockStateMissingInAuthViaVaultKey), |
| ErrorActionSet({error::PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)); |
| return; |
| } |
| |
| // Determine the auth block type to use. |
| std::optional<AuthBlockType> auth_block_type = |
| auth_block_utility_->GetAuthBlockTypeFromState(auth_state); |
| if (!auth_block_type) { |
| LOG(ERROR) << "Failed to determine auth block type from auth block state"; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionInvalidBlockTypeInAuthViaVaultKey), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)); |
| return; |
| } |
| |
| // Parameterize the AuthSession performance timer by AuthBlockType |
| auth_session_performance_timer->auth_block_type = *auth_block_type; |
| |
| // Derive KeyBlobs from the existing VaultKeyset, using GetValidKeyset |
| // as a callback that loads |vault_keyset_| and resaves if needed. |
| AuthBlock::DeriveCallback derive_callback = base::BindOnce( |
| &AuthSession::LoadVaultKeysetAndFsKeys, weak_factory_.GetWeakPtr(), |
| request_auth_factor_type, auth_input, *auth_block_type, metadata, |
| std::move(auth_session_performance_timer), std::move(on_done)); |
| |
| auth_block_utility_->DeriveKeyBlobsWithAuthBlock( |
| *auth_block_type, auth_input, auth_state, std::move(derive_callback)); |
| } |
| |
| void AuthSession::LoadVaultKeysetAndFsKeys( |
| AuthFactorType request_auth_factor_type, |
| const AuthInput& auth_input, |
| AuthBlockType auth_block_type, |
| const AuthFactorMetadata& metadata, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| StatusCallback on_done, |
| CryptohomeStatus status, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::optional<AuthBlock::SuggestedAction> suggested_action) { |
| if (!status.ok() || !key_blobs) { |
| // For LE credentials, if deriving the key blobs failed due to too many |
| // attempts, set auth_locked=true in the corresponding keyset. Then save it |
| // for future callers who can Load it w/o Decrypt'ing to check that flag. |
| // When the pin is entered wrong and AuthBlock fails to derive the KeyBlobs |
| // it doesn't make it into the VaultKeyset::Decrypt(); so auth_lock should |
| // be set here. |
| if (!status.ok() && |
| PrimaryActionIs(status, error::PrimaryAction::kLeLockedOut)) { |
| // Get the corresponding encrypted vault keyset for the user and the label |
| // to set the auth_locked. |
| std::unique_ptr<VaultKeyset> vk = keyset_management_->GetVaultKeyset( |
| obfuscated_username_, key_data_.label()); |
| if (vk != nullptr) { |
| LOG(INFO) << "PIN is locked out due to too many wrong attempts."; |
| vk->SetAuthLocked(true); |
| vk->Save(vk->GetSourceFile()); |
| } |
| } |
| if (status.ok()) { |
| // Maps to the default value of MountError which is |
| // MOUNT_ERROR_KEY_FAILURE |
| status = MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionNullParamInCallbackInLoadVaultKeyset), |
| ErrorActionSet({error::PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO, |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_NOT_IMPLEMENTED); |
| } |
| LOG(ERROR) << "Failed to load VaultKeyset since authentication has failed"; |
| std::move(on_done).Run( |
| MakeStatus<error::CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionDeriveFailedInLoadVaultKeyset)) |
| .Wrap(std::move(status))); |
| return; |
| } |
| |
| CHECK(status.ok()); |
| |
| MountStatusOr<std::unique_ptr<VaultKeyset>> vk_status = |
| keyset_management_->GetValidKeyset( |
| obfuscated_username_, std::move(*key_blobs.get()), key_data_.label()); |
| if (!vk_status.ok()) { |
| vault_keyset_ = nullptr; |
| LOG(ERROR) << "Failed to load VaultKeyset and file system keyset."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeMountError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionGetValidKeysetFailedInLoadVaultKeyset)) |
| .Wrap(std::move(vk_status).err_status())); |
| return; |
| } |
| vault_keyset_ = std::move(vk_status).value(); |
| |
| // Authentication is successfully completed. Reset LE Credential counter if |
| // the current AutFactor is not an LECredential. |
| if (!vault_keyset_->IsLECredential()) { |
| ResetLECredentials(); |
| } |
| |
| // If there is a change in the AuthBlock type during resave operation it'll be |
| // updated. |
| AuthBlockType auth_block_type_for_resaved_vk = |
| ResaveVaultKeysetIfNeeded(auth_input.user_input, auth_block_type); |
| file_system_keyset_ = FileSystemKeyset(*vault_keyset_); |
| |
| CryptohomeStatus prepare_status = OkStatus<error::CryptohomeError>(); |
| if (auth_intent_ == AuthIntent::kWebAuthn) { |
| // Even if we failed to prepare WebAuthn secret, file system keyset |
| // is already populated and we should proceed to set AuthSession as |
| // authenticated. Just return the error status at last. |
| prepare_status = PrepareWebAuthnSecret(); |
| if (!prepare_status.ok()) { |
| LOG(ERROR) << "Failed to prepare WebAuthn secret: " << prepare_status; |
| } |
| } |
| |
| // Flip the status on the successful authentication. |
| SetAuthorizedForFullAuthIntents(request_auth_factor_type); |
| |
| // Set the credential verifier for this credential. |
| AddCredentialVerifier(request_auth_factor_type, vault_keyset_->GetLabel(), |
| auth_input); |
| |
| ReportTimerDuration(auth_session_performance_timer.get()); |
| |
| if (authorized_intents_.contains(AuthIntent::kDecrypt) && |
| IsUserSecretStashExperimentEnabled(platform_)) { |
| UssMigrator migrator(username_); |
| |
| migrator.MigrateVaultKeysetToUss( |
| *user_secret_stash_storage_, vault_keyset_->GetLabel(), |
| file_system_keyset_.value(), |
| base::BindOnce( |
| &AuthSession::OnMigrationUssCreated, weak_factory_.GetWeakPtr(), |
| auth_block_type_for_resaved_vk, request_auth_factor_type, metadata, |
| auth_input, std::move(prepare_status), std::move(on_done))); |
| return; |
| } |
| |
| std::move(on_done).Run(std::move(prepare_status)); |
| } |
| |
| void AuthSession::OnMigrationUssCreatedForUpdate( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthFactorMetadata& auth_factor_metadata, |
| const AuthInput& auth_input, |
| StatusCallback on_done, |
| CryptohomeStatus callback_error, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::unique_ptr<AuthBlockState> auth_block_state, |
| std::unique_ptr<UserSecretStash> user_secret_stash, |
| brillo::SecureBlob uss_main_key) { |
| if (!user_secret_stash || uss_main_key.empty()) { |
| LOG(ERROR) << "Uss migration during UpdateVaultKeyset failed for " |
| "VaultKeyset with label: " |
| << auth_factor_label; |
| // We don't report VK to USS migration status here because it is expected |
| // that the actual migration will have already reported a more precise error |
| // directly. |
| std::move(on_done).Run(OkStatus<CryptohomeError>()); |
| return; |
| } |
| |
| user_secret_stash_ = std::move(user_secret_stash); |
| user_secret_stash_main_key_ = std::move(uss_main_key); |
| |
| auto migration_performance_timer = |
| std::make_unique<AuthSessionPerformanceTimer>(kUSSMigrationTimer); |
| |
| // Migrating a VaultKeyset to UserSecretStash during UpdateAuthFactor is |
| // adding a new KeyBlock to UserSecretStash. |
| PersistAuthFactorToUserSecretStashOnMigration( |
| auth_factor_type, auth_factor_label, auth_factor_metadata, auth_input, |
| std::move(migration_performance_timer), std::move(on_done), |
| OkStatus<CryptohomeError>(), std::move(callback_error), |
| std::move(key_blobs), std::move(auth_block_state)); |
| } |
| |
| void AuthSession::OnMigrationUssCreated( |
| AuthBlockType auth_block_type, |
| AuthFactorType auth_factor_type, |
| const AuthFactorMetadata& auth_factor_metadata, |
| const AuthInput& auth_input, |
| CryptohomeStatus pre_migration_status, |
| StatusCallback on_done, |
| std::unique_ptr<UserSecretStash> user_secret_stash, |
| brillo::SecureBlob uss_main_key) { |
| if (!user_secret_stash || uss_main_key.empty()) { |
| LOG(ERROR) << "Uss migration failed for VaultKeyset with label: " |
| << key_data_.label(); |
| // We don't report VK to USS migration status here because it is expected |
| // that the actual migration will have already reported a more precise error |
| // directly. |
| std::move(on_done).Run(std::move(pre_migration_status)); |
| return; |
| } |
| |
| user_secret_stash_ = std::move(user_secret_stash); |
| user_secret_stash_main_key_ = std::move(uss_main_key); |
| |
| auto migration_performance_timer = |
| std::make_unique<AuthSessionPerformanceTimer>(kUSSMigrationTimer); |
| |
| // During the USS migration of a password credential reset_secret is driven |
| // and put into the newly created USS file. This reset_secret is used for |
| // unmigrated PIN credential if needed. |
| // |
| // During the USS migration of a PIN credential reset_secret is added together |
| // with the created KeyBlobs, which already includes the reset secret of the |
| // migrated PIN. Hence don't abort the password migration if the |
| // |reset_secret| can't be added during the password migration. |
| if (MigrateResetSecretToUss()) { |
| LOG(INFO) << "Reset secret is migrated to UserSecretStash before the " |
| "migration of the PIN VaultKeyset."; |
| } |
| |
| CryptohomeStatusOr<AuthInput> migration_auth_input_status = |
| CreateAuthInputForMigration(auth_input, auth_factor_type); |
| if (!migration_auth_input_status.ok()) { |
| LOG(ERROR) << "Failed to create migration AuthInput: " |
| << migration_auth_input_status.status(); |
| ReapAndReportError(std::move(migration_auth_input_status).status(), |
| kCryptohomeErrorUssMigrationErrorBucket); |
| ReportVkToUssMigrationStatus(VkToUssMigrationStatus::kFailedInput); |
| std::move(on_done).Run(std::move(pre_migration_status)); |
| return; |
| } |
| |
| // If |vault_keyset_| has an empty label legacy label from GetLabel() is |
| // passed for the USS wrapped block. |
| auto create_callback = base::BindOnce( |
| &AuthSession::PersistAuthFactorToUserSecretStashOnMigration, |
| weak_factory_.GetWeakPtr(), auth_factor_type, vault_keyset_->GetLabel(), |
| auth_factor_metadata, migration_auth_input_status.value(), |
| std::move(migration_performance_timer), std::move(on_done), |
| std::move(pre_migration_status)); |
| |
| auth_block_utility_->CreateKeyBlobsWithAuthBlock( |
| auth_block_type, migration_auth_input_status.value(), |
| std::move(create_callback)); |
| } |
| |
| const FileSystemKeyset& AuthSession::file_system_keyset() const { |
| CHECK(file_system_keyset_.has_value()); |
| return file_system_keyset_.value(); |
| } |
| |
| bool AuthSession::PersistResetSecretToUss() { |
| CHECK(user_secret_stash_main_key_.has_value()); |
| |
| // Update UserSecretStash in memory with reset_secret from not-migrated |
| // keyset. If reset_secret isn't updated, no need to persist. |
| if (!MigrateResetSecretToUss()) { |
| return false; |
| } |
| |
| // Encrypt the updated USS. |
| CryptohomeStatusOr<brillo::Blob> encrypted_uss_container = |
| user_secret_stash_->GetEncryptedContainer( |
| user_secret_stash_main_key_.value()); |
| if (!encrypted_uss_container.ok()) { |
| LOG(ERROR) << "Failed to encrypt user secret stash after updating " |
| "reset_secret."; |
| return false; |
| } |
| |
| // Persist the USS. |
| if (!user_secret_stash_storage_ |
| ->Persist(encrypted_uss_container.value(), obfuscated_username_) |
| .ok()) { |
| LOG(ERROR) << "Failed to persist user secret stash after " |
| "updating reset_secret."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AuthSession::MigrateResetSecretToUss() { |
| CHECK(user_secret_stash_); |
| if (!vault_keyset_->HasWrappedResetSeed()) { |
| // Authenticated VaultKeyset doesn't include a reset seed if it is not a |
| // password VaultKeyset"; |
| return false; |
| } |
| |
| bool updated = false; |
| for (AuthFactorMap::ValueView stored_auth_factor : auth_factor_map_) { |
| // Look for only pinweaver and VaultKeyset backed AuthFactors. |
| if (stored_auth_factor.storage_type() != |
| AuthFactorStorageType::kVaultKeyset) { |
| continue; |
| } |
| const AuthFactor& auth_factor = stored_auth_factor.auth_factor(); |
| if (auth_factor.type() != AuthFactorType::kPin) { |
| continue; |
| } |
| |
| std::optional<brillo::SecureBlob> reset_secret = |
| GetResetSecretFromVaultKeyset(vault_keyset_->GetResetSeed(), |
| obfuscated_username_, auth_factor.label(), |
| *keyset_management_); |
| if (!reset_secret.has_value()) { |
| LOG(WARNING) |
| << "Failed to obtain reset secret to migrate to USS for the factor: " |
| << auth_factor.label(); |
| continue; |
| } |
| |
| if (!(user_secret_stash_->GetResetSecretForLabel(auth_factor.label()) |
| .has_value()) && |
| user_secret_stash_->SetResetSecretForLabel(auth_factor.label(), |
| reset_secret.value())) { |
| updated = true; |
| } |
| } |
| return updated; |
| } |
| |
| void AuthSession::AuthenticateAuthFactor( |
| const AuthenticateAuthFactorRequest& request, |
| AuthenticateAuthFactorCallback callback) { |
| const std::vector<std::string>& auth_factor_labels = |
| request.auth_factor_labels; |
| const user_data_auth::AuthInput& auth_input_proto = request.auth_input_proto; |
| std::string label_text = auth_factor_labels.empty() |
| ? "(unlabelled)" |
| : base::JoinString(auth_factor_labels, ","); |
| LOG(INFO) << "AuthSession: " << IntentToDebugString(auth_intent_) |
| << " authentication attempt via " << label_text; |
| // Determine the factor type from the request. |
| std::optional<AuthFactorType> request_auth_factor_type = |
| DetermineFactorTypeFromAuthInput(auth_input_proto); |
| if (!request_auth_factor_type.has_value()) { |
| LOG(ERROR) << "Unexpected AuthInput type."; |
| std::move(callback).Run( |
| kNoPostAction, |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNoAuthFactorTypeInAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| auto callback_with_metrics = |
| WrapCallbackWithMetricsReporting<const PostAuthAction&>( |
| std::move(callback), *request_auth_factor_type, |
| kCryptohomeErrorAuthenticateAuthFactorErrorBucket); |
| |
| // Currently only lightweight auth might specify a non-null post-auth action, |
| // so use the callback pre-bound with null post-auth action in all other |
| // places to keep code simple. |
| auto [on_done_temp, on_done_with_action] = |
| base::SplitOnceCallback(std::move(callback_with_metrics)); |
| StatusCallback on_done = |
| base::BindOnce(std::move(on_done_temp), kNoPostAction); |
| |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(*request_auth_factor_type); |
| AuthFactorLabelArity label_arity = factor_driver.GetAuthFactorLabelArity(); |
| switch (label_arity) { |
| case AuthFactorLabelArity::kNone: { |
| if (auth_factor_labels.size() > 0) { |
| LOG(ERROR) << "Unexpected labels for request auth factor type:" |
| << AuthFactorTypeToString(*request_auth_factor_type); |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionMismatchedZeroLabelSizeAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| const CredentialVerifier* verifier = nullptr; |
| // Search for a verifier from the User Session, if available. |
| const UserSession* user_session = user_session_map_->Find(username_); |
| if (user_session && user_session->VerifyUser(obfuscated_username_)) { |
| verifier = |
| user_session->FindCredentialVerifier(*request_auth_factor_type); |
| } |
| // A CredentialVerifier must exist if there is no label and the verifier |
| // will be used for authentication. |
| if (!verifier || !factor_driver.IsLightAuthSupported(auth_intent_) || |
| request.flags.force_full_auth == ForceFullAuthFlag::kForce) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionVerifierNotValidInAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION)); |
| return; |
| } |
| CryptohomeStatusOr<AuthInput> auth_input = |
| CreateAuthInputForAuthentication(auth_input_proto, |
| verifier->auth_factor_metadata()); |
| if (!auth_input.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionAuthInputParseFailedInAuthAuthFactor)) |
| .Wrap(std::move(auth_input).err_status())); |
| return; |
| } |
| auto verify_callback = base::BindOnce( |
| &AuthSession::CompleteVerifyOnlyAuthentication, |
| weak_factory_.GetWeakPtr(), std::move(on_done_with_action), request, |
| *request_auth_factor_type); |
| verifier->Verify(std::move(*auth_input), std::move(verify_callback)); |
| return; |
| } |
| case AuthFactorLabelArity::kSingle: { |
| if (auth_factor_labels.size() != 1) { |
| LOG(ERROR) << "Unexpected zero or multiple labels for request auth " |
| "factor type:" |
| << AuthFactorTypeToString(*request_auth_factor_type); |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionMismatchedSingleLabelSizeAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| // Construct a CredentialVerifier and verify as authentication if the auth |
| // intent allows it. |
| const CredentialVerifier* verifier = nullptr; |
| // Search for a verifier from the User Session, if available. |
| const UserSession* user_session = user_session_map_->Find(username_); |
| if (user_session && user_session->VerifyUser(obfuscated_username_)) { |
| verifier = user_session->FindCredentialVerifier(auth_factor_labels[0]); |
| } |
| |
| // Attempt lightweight authentication via a credential verifier if |
| // suitable. |
| if (verifier && factor_driver.IsLightAuthSupported(auth_intent_) && |
| request.flags.force_full_auth != ForceFullAuthFlag::kForce) { |
| CryptohomeStatusOr<AuthInput> auth_input = |
| CreateAuthInputForAuthentication(auth_input_proto, |
| verifier->auth_factor_metadata()); |
| if (!auth_input.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionAuthInputParseFailed2InAuthAuthFactor)) |
| .Wrap(std::move(auth_input).err_status())); |
| return; |
| } |
| auto verify_callback = base::BindOnce( |
| &AuthSession::CompleteVerifyOnlyAuthentication, |
| weak_factory_.GetWeakPtr(), std::move(on_done_with_action), request, |
| *request_auth_factor_type); |
| verifier->Verify(std::move(*auth_input), std::move(verify_callback)); |
| return; |
| } |
| |
| // If we get here, we need to use full authentication. Make sure that it |
| // is supported for this type of auth factor and intent. |
| if (!factor_driver.IsFullAuthSupported(auth_intent_)) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionSingleLabelFullAuthNotSupportedAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| // Load the auth factor and it should exist for authentication. |
| std::optional<AuthFactorMap::ValueView> stored_auth_factor = |
| auth_factor_map_.Find(auth_factor_labels[0]); |
| if (!stored_auth_factor) { |
| // This could happen for 2 reasons, either the user doesn't exist or the |
| // auth factor is not available for this user. |
| if (!user_exists_) { |
| // Attempting to authenticate a user that doesn't exist. |
| LOG(ERROR) << "Attempting to authenticate user that doesn't exist: " |
| << username_; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionUserNotFoundInAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND)); |
| return; |
| } |
| LOG(ERROR) << "Authentication factor not found: " |
| << auth_factor_labels[0]; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionFactorNotFoundInAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_KEY_NOT_FOUND)); |
| return; |
| } |
| |
| AuthFactorMetadata metadata = |
| stored_auth_factor->auth_factor().metadata(); |
| // Ensure that if an auth factor is found, the requested type matches what |
| // we have on disk for the user. |
| if (*request_auth_factor_type != |
| stored_auth_factor->auth_factor().type()) { |
| // We have to special case kiosk keysets, because for old vault keyset |
| // factors the underlying data may not be marked as a kiosk and so it |
| // will show up as a password auth factor instead. In that case we treat |
| // the request as authoritative, and instead fix up the metadata. |
| if (stored_auth_factor->storage_type() == |
| AuthFactorStorageType::kVaultKeyset && |
| request_auth_factor_type == AuthFactorType::kKiosk) { |
| metadata.metadata = auth_factor::KioskMetadata(); |
| } else { |
| LOG(ERROR) |
| << "Unexpected mismatch in type from label and auth_input."; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionMismatchedAuthTypes), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| } |
| |
| CryptohomeStatusOr<AuthInput> auth_input = |
| CreateAuthInputForAuthentication(auth_input_proto, metadata); |
| if (!auth_input.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionAuthInputParseFailed3InAuthAuthFactor)) |
| .Wrap(std::move(auth_input).err_status())); |
| return; |
| } |
| AuthenticateViaSingleFactor(*request_auth_factor_type, |
| stored_auth_factor->auth_factor().label(), |
| std::move(*auth_input), metadata, |
| *stored_auth_factor, std::move(on_done)); |
| return; |
| } |
| case AuthFactorLabelArity::kMultiple: { |
| if (auth_factor_labels.size() == 0) { |
| LOG(ERROR) << "Unexpected zero label for request auth factor type:" |
| << AuthFactorTypeToString(*request_auth_factor_type); |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionMismatchedMultipLabelSizeAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| // If we get here, we need to use full authentication. Make sure that it |
| // is supported for this type of auth factor and intent. |
| if (!factor_driver.IsFullAuthSupported(auth_intent_)) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionMultiLabelFullAuthNotSupportedAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| std::vector<AuthFactor> auth_factors; |
| // All the auth factors iterated here should have the same auth block |
| // type. |
| std::optional<AuthBlockType> auth_block_type; |
| for (const std::string& label : auth_factor_labels) { |
| // Load the auth factor and it should exist for authentication. |
| std::optional<AuthFactorMap::ValueView> stored_auth_factor = |
| auth_factor_map_.Find(label); |
| if (!stored_auth_factor) { |
| // This could happen for 2 reasons, either the user doesn't exist or |
| // the auth factor is not available for this user. |
| if (!user_exists_) { |
| // Attempting to authenticate a user that doesn't exist. |
| LOG(ERROR) << "Attempting to authenticate user that doesn't exist: " |
| << username_; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionUserNotFoundInMultiLabelAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND)); |
| return; |
| } |
| LOG(ERROR) << "Authentication factor not found: " << label; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionFactorNotFoundInMultiLabelAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_KEY_NOT_FOUND)); |
| return; |
| } |
| |
| // Ensure that if an auth factor is found, the requested type matches |
| // what we have on disk for the user. |
| if (*request_auth_factor_type != |
| stored_auth_factor->auth_factor().type()) { |
| LOG(ERROR) |
| << "Unexpected mismatch in type from label and auth_input."; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionMultiLabelMismatchedAuthTypes), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| std::optional<AuthBlockType> cur_auth_block_type = |
| auth_block_utility_->GetAuthBlockTypeFromState( |
| stored_auth_factor->auth_factor().auth_block_state()); |
| if (!cur_auth_block_type.has_value()) { |
| LOG(ERROR) << "Failed to determine auth block type."; |
| std::move(on_done).Run(MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionInvalidBlockTypeInAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO)); |
| return; |
| } |
| if (auth_block_type.has_value()) { |
| if (cur_auth_block_type.value() != auth_block_type.value()) { |
| LOG(ERROR) << "Unexpected mismatch in auth block types in auth " |
| "factor candidates."; |
| std::move(on_done).Run(MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionMismatchedBlockTypesInAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO)); |
| return; |
| } |
| } else { |
| auth_block_type = cur_auth_block_type.value(); |
| } |
| |
| // Perform the storage type check here because we want to directly call |
| // AuthenticateViaUserSecretStash later on. |
| if (stored_auth_factor->storage_type() != |
| AuthFactorStorageType::kUserSecretStash) { |
| LOG(ERROR) << "Multiple label arity auth factors are only supported " |
| "with USS storage type."; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionMultiLabelInvalidStorageType), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| auth_factors.push_back(stored_auth_factor->auth_factor()); |
| } |
| |
| // auth_block_type is guaranteed to be non-null because we've checked |
| // auth_factor_labels's length above, and auth_block_type must be set in |
| // the first iteration of the loop. |
| CHECK(auth_block_type.has_value()); |
| |
| CryptohomeStatusOr<AuthInput> auth_input = |
| CreateAuthInputForSelectFactor(*request_auth_factor_type); |
| if (!auth_input.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionAuthInputParseFailed4InAuthAuthFactor)) |
| .Wrap(std::move(auth_input).err_status())); |
| return; |
| } |
| |
| // Record current time for timing for how long AuthenticateAuthFactor will |
| // take. |
| auto auth_session_performance_timer = |
| std::make_unique<AuthSessionPerformanceTimer>( |
| kAuthSessionAuthenticateAuthFactorUSSTimer); |
| auth_block_utility_->SelectAuthFactorWithAuthBlock( |
| auth_block_type.value(), auth_input.value(), std::move(auth_factors), |
| base::BindOnce(&AuthSession::AuthenticateViaSelectedAuthFactor, |
| weak_factory_.GetWeakPtr(), std::move(on_done), |
| std::move(auth_session_performance_timer))); |
| return; |
| } |
| } |
| } |
| |
| void AuthSession::RemoveAuthFactor( |
| const user_data_auth::RemoveAuthFactorRequest& request, |
| StatusCallback on_done) { |
| if (!authorized_intents_.contains(AuthIntent::kDecrypt)) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionUnauthedInRemoveAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION)); |
| return; |
| } |
| |
| auto remove_timer_start = base::TimeTicks::Now(); |
| const auto& auth_factor_label = request.auth_factor_label(); |
| |
| std::optional<AuthFactorMap::ValueView> stored_auth_factor = |
| auth_factor_map_.Find(auth_factor_label); |
| if (!stored_auth_factor) { |
| LOG(ERROR) << "AuthSession: Key to remove not found: " << auth_factor_label; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionFactorNotFoundInRemoveAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_KEY_NOT_FOUND)); |
| return; |
| } |
| |
| on_done = WrapCallbackWithMetricsReporting( |
| std::move(on_done), stored_auth_factor->auth_factor().type(), |
| kCryptohomeErrorRemoveAuthFactorErrorBucket); |
| |
| if (auth_factor_map_.size() == 1) { |
| LOG(ERROR) << "AuthSession: Cannot remove the last auth factor."; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionLastFactorInRemoveAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_REMOVE_CREDENTIALS_FAILED)); |
| return; |
| } |
| |
| // Authenticated |vault_keyset_| of the current session (backup VaultKeyset or |
| // regular VaultKeyset) cannot be removed. |
| if (vault_keyset_ && auth_factor_label == vault_keyset_->GetLabel()) { |
| LOG(ERROR) << "AuthSession: Cannot remove the authenticated VaultKeyset."; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionRemoveSameVKInRemoveAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_REMOVE_CREDENTIALS_FAILED)); |
| return; |
| } |
| |
| bool remove_using_uss = |
| user_secret_stash_ && stored_auth_factor->storage_type() == |
| AuthFactorStorageType::kUserSecretStash; |
| |
| if (remove_using_uss) { |
| RemoveAuthFactorViaUserSecretStash( |
| auth_factor_label, stored_auth_factor->auth_factor(), |
| base::BindOnce(&AuthSession::ClearAuthFactorInMemoryObjects, |
| base::Unretained(this), auth_factor_label, |
| *stored_auth_factor, remove_timer_start, |
| std::move(on_done))); |
| return; |
| } |
| |
| // Remove the VaultKeyset with the given label if it exists from disk |
| // regardless of its purpose, i.e backup, regular or migrated. Error is |
| // ignored if remove_using_uss was true as the keyset that matters is now |
| // deleted. |
| CryptohomeStatus remove_status = RemoveKeysetByLabel( |
| *keyset_management_, obfuscated_username_, auth_factor_label); |
| if (!remove_using_uss && !remove_status.ok() && |
| stored_auth_factor->auth_factor().type() != |
| AuthFactorType::kCryptohomeRecovery) { |
| LOG(ERROR) << "AuthSession: Failed to remove VaultKeyset."; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionRemoveVKFailedInRemoveAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_REMOVE_CREDENTIALS_FAILED)); |
| return; |
| } |
| |
| // Remove the AuthFactor from the map. |
| auth_factor_map_.Remove(auth_factor_label); |
| verifier_forwarder_.RemoveVerifier(auth_factor_label); |
| |
| // Report time taken for a successful remove. |
| ReportTimerDuration(kAuthSessionRemoveAuthFactorVKTimer, remove_timer_start, |
| "" /*append_string*/); |
| std::move(on_done).Run(OkStatus<CryptohomeError>()); |
| } |
| |
| void AuthSession::PrepareUserForRemoval() { |
| // All auth factors of the user are being removed when we remove the user, so |
| // we should PrepareForRemoval() all auth factors. |
| for (AuthFactorMap::ValueView stored_auth_factor : auth_factor_map_) { |
| const AuthFactor& auth_factor = stored_auth_factor.auth_factor(); |
| auto log_status = [](const AuthFactor& auth_factor, |
| CryptohomeStatus remove_status) { |
| if (!remove_status.ok()) { |
| LOG(WARNING) << "Failed to prepare auth factor " << auth_factor.label() |
| << " for removal: " << remove_status; |
| } |
| }; |
| auth_block_utility_->PrepareAuthBlockForRemoval( |
| obfuscated_username_, auth_factor.auth_block_state(), |
| base::BindOnce(log_status, auth_factor)); |
| } |
| |
| // Remove rate-limiter here, as it won't be removed by any auth factor's |
| // removal. |
| RemoveRateLimiters(); |
| } |
| |
| void AuthSession::RemoveRateLimiters() { |
| // Currently fingerprint is the only auth factor type using rate |
| // limiter, so the field name isn't generic. We'll make it generic to any |
| // auth factor types in the future. |
| CryptohomeStatusOr<UserMetadata> user_metadata = |
| user_metadata_reader_->Load(obfuscated_username_); |
| if (!user_metadata.ok()) { |
| LOG(WARNING) << "Failed to load the user metadata."; |
| return; |
| } |
| if (!user_metadata->fingerprint_rate_limiter_id.has_value()) { |
| return; |
| } |
| if (!crypto_->RemoveLECredential( |
| user_metadata->fingerprint_rate_limiter_id.value())) { |
| LOG(WARNING) << "Failed to remove rate-limiter leaf."; |
| } |
| } |
| |
| void AuthSession::ClearAuthFactorInMemoryObjects( |
| const std::string& auth_factor_label, |
| const AuthFactorMap::ValueView& stored_auth_factor, |
| const base::TimeTicks& remove_timer_start, |
| StatusCallback on_done, |
| CryptohomeStatus status) { |
| if (!status.ok()) { |
| LOG(ERROR) << "AuthSession: Failed to remove auth factor."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionRemoveAuthFactorViaUserSecretStashFailed), |
| user_data_auth::CRYPTOHOME_REMOVE_CREDENTIALS_FAILED) |
| .Wrap(std::move(status))); |
| return; |
| } |
| |
| // Attempt to remove the keyset with the given label regardless if it |
| // exists. Error is logged and ignored. |
| CryptohomeStatus remove_status = RemoveKeysetByLabel( |
| *keyset_management_, obfuscated_username_, auth_factor_label); |
| if (!remove_status.ok() && stored_auth_factor.auth_factor().type() != |
| AuthFactorType::kCryptohomeRecovery) { |
| LOG(INFO) << "AuthSession: Failed to remove VaultKeyset in USS auth " |
| "factor removal."; |
| } |
| |
| // Remove the AuthFactor from the map. |
| auth_factor_map_.Remove(auth_factor_label); |
| verifier_forwarder_.RemoveVerifier(auth_factor_label); |
| ReportTimerDuration(kAuthSessionRemoveAuthFactorUSSTimer, remove_timer_start, |
| "" /*append_string*/); |
| std::move(on_done).Run(OkStatus<CryptohomeError>()); |
| } |
| |
| void AuthSession::RemoveAuthFactorViaUserSecretStash( |
| const std::string& auth_factor_label, |
| const AuthFactor& auth_factor, |
| StatusCallback on_done) { |
| // Preconditions. |
| CHECK(user_secret_stash_); |
| CHECK(user_secret_stash_main_key_.has_value()); |
| |
| auth_factor_manager_->RemoveAuthFactor( |
| obfuscated_username_, auth_factor, auth_block_utility_, |
| base::BindOnce(&AuthSession::ResaveUssWithFactorRemoved, |
| base::Unretained(this), auth_factor_label, auth_factor, |
| std::move(on_done))); |
| } |
| |
| void AuthSession::ResaveUssWithFactorRemoved( |
| const std::string& auth_factor_label, |
| const AuthFactor& auth_factor, |
| StatusCallback on_done, |
| CryptohomeStatus status) { |
| if (!status.ok()) { |
| LOG(ERROR) << "AuthSession: Failed to remove auth factor."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionRemoveFactorFailedInRemoveAuthFactor), |
| user_data_auth::CRYPTOHOME_REMOVE_CREDENTIALS_FAILED) |
| .Wrap(std::move(status))); |
| return; |
| } |
| |
| status = RemoveAuthFactorFromUssInMemory(auth_factor_label); |
| if (!status.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionRemoveFromUssFailedInRemoveAuthFactor), |
| user_data_auth::CRYPTOHOME_REMOVE_CREDENTIALS_FAILED) |
| .Wrap(std::move(status))); |
| return; |
| } |
| |
| CryptohomeStatusOr<brillo::Blob> encrypted_uss_container = |
| user_secret_stash_->GetEncryptedContainer( |
| user_secret_stash_main_key_.value()); |
| if (!encrypted_uss_container.ok()) { |
| LOG(ERROR) << "AuthSession: Failed to encrypt user secret stash after auth " |
| "factor removal."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionEncryptFailedInRemoveAuthFactor), |
| user_data_auth::CRYPTOHOME_REMOVE_CREDENTIALS_FAILED) |
| .Wrap(std::move(encrypted_uss_container).err_status())); |
| return; |
| } |
| |
| status = user_secret_stash_storage_->Persist(encrypted_uss_container.value(), |
| obfuscated_username_); |
| if (!status.ok()) { |
| LOG(ERROR) << "AuthSession: Failed to persist user secret stash after auth " |
| "factor removal."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionPersistUSSFailedInRemoveAuthFactor), |
| user_data_auth::CRYPTOHOME_REMOVE_CREDENTIALS_FAILED) |
| .Wrap(std::move(status))); |
| } |
| |
| std::move(on_done).Run(OkStatus<CryptohomeError>()); |
| } |
| |
| CryptohomeStatus AuthSession::RemoveAuthFactorFromUssInMemory( |
| const std::string& auth_factor_label) { |
| if (!user_secret_stash_->RemoveWrappedMainKey( |
| /*wrapping_id=*/auth_factor_label)) { |
| LOG(ERROR) |
| << "AuthSession: Failed to remove auth factor from user secret stash."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionRemoveMainKeyFailedInRemoveSecretFromUss), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_REMOVE_CREDENTIALS_FAILED); |
| } |
| |
| // Note: we may or may not have a reset secret for this auth factor - |
| // therefore we don't check the return value. |
| user_secret_stash_->RemoveResetSecretForLabel(auth_factor_label); |
| |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| void AuthSession::UpdateAuthFactor( |
| const user_data_auth::UpdateAuthFactorRequest& request, |
| StatusCallback on_done) { |
| if (!authorized_intents_.contains(AuthIntent::kDecrypt)) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionUnauthedInUpdateAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION)); |
| return; |
| } |
| |
| if (request.auth_factor_label().empty()) { |
| LOG(ERROR) << "AuthSession: Old auth factor label is empty."; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNoOldLabelInUpdateAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| std::optional<AuthFactorMap::ValueView> stored_auth_factor = |
| auth_factor_map_.Find(request.auth_factor_label()); |
| if (!stored_auth_factor) { |
| LOG(ERROR) << "AuthSession: Key to update not found: " |
| << request.auth_factor_label(); |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionFactorNotFoundInUpdateAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_KEY_NOT_FOUND)); |
| return; |
| } |
| |
| AuthFactorType auth_factor_type; |
| std::string auth_factor_label; |
| AuthFactorMetadata auth_factor_metadata; |
| if (!AuthFactorPropertiesFromProto(request.auth_factor(), *features_, |
| auth_factor_type, auth_factor_label, |
| auth_factor_metadata)) { |
| LOG(ERROR) |
| << "AuthSession: Failed to parse updated auth factor parameters."; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionUnknownFactorInUpdateAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| // Auth factor label has to be the same as before. |
| if (request.auth_factor_label() != auth_factor_label) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionDifferentLabelInUpdateAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| // Auth factor type has to be the same as before. |
| if (stored_auth_factor->auth_factor().type() != auth_factor_type) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionDifferentTypeInUpdateAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| // Determine the auth block type to use. |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor_type); |
| CryptoStatusOr<AuthBlockType> auth_block_type = |
| auth_block_utility_->SelectAuthBlockTypeForCreation( |
| factor_driver.block_types()); |
| if (!auth_block_type.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionInvalidBlockTypeInUpdateAuthFactor), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE) |
| .Wrap(std::move(auth_block_type).status())); |
| return; |
| } |
| |
| // Create and initialize fields for auth_input. |
| CryptohomeStatusOr<AuthInput> auth_input_status = CreateAuthInputForAdding( |
| request.auth_input(), auth_factor_type, auth_factor_metadata); |
| if (!auth_input_status.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNoInputInUpdateAuthFactor)) |
| .Wrap(std::move(auth_input_status).err_status())); |
| return; |
| } |
| |
| // Report timer for how long UpdateAuthFactor operation takes. |
| auto auth_session_performance_timer = |
| std::make_unique<AuthSessionPerformanceTimer>( |
| stored_auth_factor->storage_type() == |
| AuthFactorStorageType::kUserSecretStash |
| ? kAuthSessionUpdateAuthFactorUSSTimer |
| : kAuthSessionUpdateAuthFactorVKTimer, |
| auth_block_type.value()); |
| auth_session_performance_timer->auth_block_type = auth_block_type.value(); |
| |
| KeyData key_data; |
| // AuthFactorMetadata is needed for only smartcards. Since |
| // UpdateAuthFactor doesn't operate on smartcards pass an empty metadata, |
| // which is not going to be used. |
| user_data_auth::CryptohomeErrorCode error = converter_.AuthFactorToKeyData( |
| auth_factor_label, auth_factor_type, auth_factor_metadata, key_data); |
| if (error != user_data_auth::CRYPTOHOME_ERROR_NOT_SET && |
| auth_factor_type != AuthFactorType::kCryptohomeRecovery) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionConverterFailsInUpdateFactorViaVK), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), error)); |
| return; |
| } |
| |
| auto create_callback = GetUpdateAuthFactorCallback( |
| auth_factor_type, auth_factor_label, auth_factor_metadata, key_data, |
| auth_input_status.value(), stored_auth_factor->storage_type(), |
| std::move(auth_session_performance_timer), std::move(on_done)); |
| |
| auth_block_utility_->CreateKeyBlobsWithAuthBlock(auth_block_type.value(), |
| auth_input_status.value(), |
| std::move(create_callback)); |
| } |
| |
| AuthBlock::CreateCallback AuthSession::GetUpdateAuthFactorCallback( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthFactorMetadata& auth_factor_metadata, |
| const KeyData& key_data, |
| const AuthInput& auth_input, |
| const AuthFactorStorageType auth_factor_storage_type, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| StatusCallback on_done) { |
| switch (auth_factor_storage_type) { |
| case AuthFactorStorageType::kUserSecretStash: |
| return base::BindOnce(&AuthSession::UpdateAuthFactorViaUserSecretStash, |
| weak_factory_.GetWeakPtr(), auth_factor_type, |
| auth_factor_label, auth_factor_metadata, auth_input, |
| std::move(auth_session_performance_timer), |
| std::move(on_done)); |
| |
| case AuthFactorStorageType::kVaultKeyset: |
| return base::BindOnce(&AuthSession::MigrateToUssDuringUpdateVaultKeyset, |
| weak_factory_.GetWeakPtr(), auth_factor_type, |
| auth_factor_label, auth_factor_metadata, key_data, |
| auth_input, std::move(on_done)); |
| } |
| } |
| |
| void AuthSession::UpdateAuthFactorViaUserSecretStash( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthFactorMetadata& auth_factor_metadata, |
| const AuthInput& auth_input, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| StatusCallback on_done, |
| CryptohomeStatus callback_error, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::unique_ptr<AuthBlockState> auth_block_state) { |
| // Check the status of the callback error, to see if the key blob creation was |
| // actually successful. |
| if (!callback_error.ok() || !key_blobs || !auth_block_state) { |
| if (callback_error.ok()) { |
| callback_error = MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNullParamInUpdateViaUSS), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO, |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_NOT_IMPLEMENTED); |
| } |
| LOG(ERROR) << "KeyBlob creation failed before updating auth factor"; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionCreateFailedInUpdateViaUSS), |
| user_data_auth::CRYPTOHOME_UPDATE_CREDENTIALS_FAILED) |
| .Wrap(std::move(callback_error))); |
| return; |
| } |
| |
| // Create the auth factor by combining the metadata with the auth block |
| // state. |
| auto auth_factor = |
| std::make_unique<AuthFactor>(auth_factor_type, auth_factor_label, |
| auth_factor_metadata, *auth_block_state); |
| |
| CryptohomeStatus status = RemoveAuthFactorFromUssInMemory(auth_factor_label); |
| if (!status.ok()) { |
| LOG(ERROR) |
| << "AuthSession: Failed to remove old auth factor secret from USS."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionRemoveFromUSSFailedInUpdateViaUSS), |
| user_data_auth::CRYPTOHOME_UPDATE_CREDENTIALS_FAILED) |
| .Wrap(std::move(status))); |
| return; |
| } |
| |
| status = AddAuthFactorToUssInMemory(*auth_factor, *key_blobs, |
| OverwriteExistingKeyBlock::kDisabled); |
| if (!status.ok()) { |
| LOG(ERROR) |
| << "AuthSession: Failed to add updated auth factor secret to USS."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionAddToUSSFailedInUpdateViaUSS), |
| user_data_auth::CRYPTOHOME_UPDATE_CREDENTIALS_FAILED) |
| .Wrap(std::move(status))); |
| return; |
| } |
| |
| // Encrypt the updated USS. |
| CryptohomeStatusOr<brillo::Blob> encrypted_uss_container = |
| user_secret_stash_->GetEncryptedContainer( |
| user_secret_stash_main_key_.value()); |
| if (!encrypted_uss_container.ok()) { |
| LOG(ERROR) << "AuthSession: Failed to encrypt user secret stash for auth " |
| "factor update."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionEncryptFailedInUpdateViaUSS), |
| user_data_auth::CRYPTOHOME_UPDATE_CREDENTIALS_FAILED) |
| .Wrap(std::move(encrypted_uss_container).err_status())); |
| return; |
| } |
| |
| // Update/persist the factor. |
| auth_factor_manager_->UpdateAuthFactor( |
| obfuscated_username_, auth_factor_label, *auth_factor, |
| auth_block_utility_, |
| base::BindOnce(&AuthSession::ResaveUssWithFactorUpdated, |
| base::Unretained(this), auth_factor_type, |
| std::move(auth_factor), auth_input, |
| std::move(auth_session_performance_timer), |
| encrypted_uss_container.value(), std::move(on_done))); |
| } |
| |
| void AuthSession::ResaveUssWithFactorUpdated( |
| AuthFactorType auth_factor_type, |
| std::unique_ptr<AuthFactor> auth_factor, |
| const AuthInput& auth_input, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| const brillo::Blob& encrypted_uss_container, |
| StatusCallback on_done, |
| CryptohomeStatus status) { |
| if (!status.ok()) { |
| LOG(ERROR) << "AuthSession: Failed to update auth factor."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionPersistFactorFailedInUpdateViaUSS), |
| user_data_auth::CRYPTOHOME_UPDATE_CREDENTIALS_FAILED) |
| .Wrap(std::move(status))); |
| return; |
| } |
| |
| // Persist the USS. |
| // It's important to do this after persisting the factor, to minimize the |
| // chance of ending in an inconsistent state on the disk: a created/updated |
| // USS and a missing auth factor (note that we're using file system syncs to |
| // have best-effort ordering guarantee). |
| status = user_secret_stash_storage_->Persist(encrypted_uss_container, |
| obfuscated_username_); |
| if (!status.ok()) { |
| LOG(ERROR) |
| << "Failed to persist user secret stash after auth factor creation"; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionPersistUSSFailedInUpdateViaUSS), |
| user_data_auth::CRYPTOHOME_UPDATE_CREDENTIALS_FAILED) |
| .Wrap(std::move(status))); |
| return; |
| } |
| |
| // Create the credential verifier if applicable. |
| AddCredentialVerifier(auth_factor_type, auth_factor->label(), auth_input); |
| |
| LOG(INFO) << "AuthSession: updated auth factor " << auth_factor->label() |
| << " in USS."; |
| auth_factor_map_.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| ReportTimerDuration(auth_session_performance_timer.get()); |
| std::move(on_done).Run(OkStatus<CryptohomeError>()); |
| } |
| |
| void AuthSession::UpdateAuthFactorMetadata( |
| const user_data_auth::UpdateAuthFactorMetadataRequest request, |
| StatusCallback on_done) { |
| if (request.auth_factor_label().empty()) { |
| LOG(ERROR) << "AuthSession: UpdateAuthFactorMetadata request contains " |
| "empty auth factor label."; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNoLabelInUpdateAuthFactorMetadata), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| std::optional<AuthFactorMap::ValueView> stored_auth_factor = |
| auth_factor_map_.Find(request.auth_factor_label()); |
| if (!stored_auth_factor) { |
| LOG(ERROR) << "AuthSession: UpdateAuthFactorMetadata's to-be-updated auth " |
| "factor not found, label: " |
| << request.auth_factor_label(); |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionFactorNotFoundInUpdateAuthFactorMetadata), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| AuthFactorType auth_factor_type; |
| std::string auth_factor_label; |
| AuthFactorMetadata auth_factor_metadata; |
| if (!AuthFactorPropertiesFromProto(request.auth_factor(), *features_, |
| auth_factor_type, auth_factor_label, |
| auth_factor_metadata)) { |
| LOG(ERROR) |
| << "AuthSession: Failed to parse updated auth factor parameters."; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionUnknownFactorInUpdateAuthFactorMetadata), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| // Auth factor label has to be the same as before. |
| if (request.auth_factor_label() != auth_factor_label) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionDifferentLabelInUpdateAuthFactorMetadata), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| // Auth factor type has to be the same as before. |
| if (stored_auth_factor->auth_factor().type() != auth_factor_type) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionDifferentTypeInUpdateAuthFactorMetadata), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| if (auth_factor_metadata.common.user_specified_name.size() > |
| kUserSpecifiedNameSizeLimit) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionNameTooLongInUpdateAuthFactorMetadata), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| // Build the new auth factor with existing auth block state. |
| auto auth_factor = std::make_unique<AuthFactor>( |
| auth_factor_type, auth_factor_label, auth_factor_metadata, |
| stored_auth_factor.value().auth_factor().auth_block_state()); |
| // Update/persist the factor. |
| auto status = |
| auth_factor_manager_->SaveAuthFactor(obfuscated_username_, *auth_factor); |
| if (!status.ok()) { |
| LOG(ERROR) << "AuthSession: Failed to save updated auth factor: " << status; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionFailedSaveInUpdateAuthFactorMetadata)) |
| .Wrap(std::move(status))); |
| return; |
| } |
| std::move(on_done).Run(OkStatus<CryptohomeError>()); |
| } |
| |
| void AuthSession::PrepareAuthFactor( |
| const user_data_auth::PrepareAuthFactorRequest& request, |
| StatusCallback on_done) { |
| std::optional<AuthFactorType> auth_factor_type = |
| AuthFactorTypeFromProto(request.auth_factor_type()); |
| if (!auth_factor_type.has_value()) { |
| CryptohomeStatus status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionInvalidAuthFactorTypeInPrepareAuthFactor), |
| ErrorActionSet({PossibleAction::kRetry}), |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| std::move(on_done).Run(std::move(status)); |
| return; |
| } |
| |
| on_done = WrapCallbackWithMetricsReporting( |
| std::move(on_done), *auth_factor_type, |
| kCryptohomeErrorPrepareAuthFactorErrorBucket); |
| |
| AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(*auth_factor_type); |
| |
| std::optional<AuthFactorPreparePurpose> purpose = |
| AuthFactorPreparePurposeFromProto(request.purpose()); |
| if (!purpose.has_value()) { |
| CryptohomeStatus status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionInvalidPurposeInPrepareAuthFactor), |
| ErrorActionSet({PossibleAction::kRetry}), |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| std::move(on_done).Run(std::move(status)); |
| return; |
| } |
| |
| if (factor_driver.IsPrepareRequired()) { |
| switch (*purpose) { |
| case AuthFactorPreparePurpose::kPrepareAuthenticateAuthFactor: { |
| factor_driver.PrepareForAuthenticate( |
| obfuscated_username_, |
| base::BindOnce(&AuthSession::OnPrepareAuthFactorDone, |
| weak_factory_.GetWeakPtr(), std::move(on_done))); |
| break; |
| } |
| case AuthFactorPreparePurpose::kPrepareAddAuthFactor: { |
| factor_driver.PrepareForAdd( |
| obfuscated_username_, |
| base::BindOnce(&AuthSession::OnPrepareAuthFactorDone, |
| weak_factory_.GetWeakPtr(), std::move(on_done))); |
| break; |
| } |
| } |
| |
| // If this type of factor supports label-less verifiers, then create one. |
| if (auto verifier = factor_driver.CreateCredentialVerifier({}, {})) { |
| verifier_forwarder_.AddVerifier(std::move(verifier)); |
| } |
| } else { |
| // For auth factor types that do not require PrepareAuthFactor, |
| // return an invalid argument error. |
| CryptohomeStatus status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionPrepareBadAuthFactorType), |
| ErrorActionSet({PossibleAction::kRetry}), |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| std::move(on_done).Run(std::move(status)); |
| } |
| } |
| |
| void AuthSession::OnPrepareAuthFactorDone( |
| StatusCallback on_done, |
| CryptohomeStatusOr<std::unique_ptr<PreparedAuthFactorToken>> token) { |
| if (token.ok()) { |
| AuthFactorType type = (*token)->auth_factor_type(); |
| active_auth_factor_tokens_[type] = std::move(*token); |
| std::move(on_done).Run(OkStatus<CryptohomeError>()); |
| } else { |
| std::move(on_done).Run(std::move(token).status()); |
| } |
| } |
| |
| void AuthSession::TerminateAuthFactor( |
| const user_data_auth::TerminateAuthFactorRequest& request, |
| StatusCallback on_done) { |
| std::optional<AuthFactorType> auth_factor_type = |
| AuthFactorTypeFromProto(request.auth_factor_type()); |
| if (!auth_factor_type.has_value()) { |
| CryptohomeStatus status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionInvalidAuthFactorTypeInTerminateAuthFactor), |
| ErrorActionSet({PossibleAction::kRetry}), |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| std::move(on_done).Run(std::move(status)); |
| return; |
| } |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(*auth_factor_type); |
| |
| // For auth factor types that do not need Prepare, neither do they need |
| // Terminate, return an invalid argument error. |
| if (!factor_driver.IsPrepareRequired()) { |
| CryptohomeStatus status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionTerminateBadAuthFactorType), |
| ErrorActionSet({PossibleAction::kRetry}), |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| std::move(on_done).Run(std::move(status)); |
| return; |
| } |
| |
| // Throw error if the auth factor is not in the active list. |
| auto iter = active_auth_factor_tokens_.find(*auth_factor_type); |
| if (iter == active_auth_factor_tokens_.end()) { |
| CryptohomeStatus status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionTerminateInactiveAuthFactor), |
| ErrorActionSet({PossibleAction::kRetry}), |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| std::move(on_done).Run(std::move(status)); |
| return; |
| } |
| |
| // Terminate the auth factor and remove it from the active list. We do this |
| // removal even if termination fails. |
| CryptohomeStatus status = iter->second->Terminate(); |
| active_auth_factor_tokens_.erase(iter); |
| verifier_forwarder_.RemoveVerifier(*auth_factor_type); |
| std::move(on_done).Run(std::move(status)); |
| } |
| |
| void AuthSession::GetRecoveryRequest( |
| user_data_auth::GetRecoveryRequestRequest request, |
| base::OnceCallback<void(const user_data_auth::GetRecoveryRequestReply&)> |
| on_done) { |
| user_data_auth::GetRecoveryRequestReply reply; |
| |
| // Check the factor exists. |
| std::optional<AuthFactorMap::ValueView> stored_auth_factor = |
| auth_factor_map_.Find(request.auth_factor_label()); |
| if (!stored_auth_factor) { |
| LOG(ERROR) << "Authentication key not found: " |
| << request.auth_factor_label(); |
| ReplyWithError( |
| std::move(on_done), reply, |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionFactorNotFoundInGetRecoveryRequest), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_KEY_NOT_FOUND)); |
| return; |
| } |
| |
| // Read CryptohomeRecoveryAuthBlockState. |
| if (stored_auth_factor->auth_factor().type() != |
| AuthFactorType::kCryptohomeRecovery) { |
| LOG(ERROR) << "GetRecoveryRequest can be called only for " |
| "kCryptohomeRecovery auth factor"; |
| ReplyWithError( |
| std::move(on_done), reply, |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocWrongAuthFactorInGetRecoveryRequest), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_KEY_NOT_FOUND)); |
| return; |
| } |
| |
| auto* state = std::get_if<::cryptohome::CryptohomeRecoveryAuthBlockState>( |
| &(stored_auth_factor->auth_factor().auth_block_state().state)); |
| if (!state) { |
| ReplyWithError( |
| std::move(on_done), reply, |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocNoRecoveryAuthBlockStateInGetRecoveryRequest), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_KEY_NOT_FOUND)); |
| return; |
| } |
| |
| brillo::SecureBlob ephemeral_pub_key, recovery_request; |
| // GenerateRecoveryRequest will set: |
| // - `recovery_request` on the `reply` object |
| // - `ephemeral_pub_key` which is saved in AuthSession and retrieved during |
| // the `AuthenticateAuthFactor` call. |
| CryptoStatus status = auth_block_utility_->GenerateRecoveryRequest( |
| obfuscated_username_, RequestMetadataFromProto(request), |
| brillo::BlobFromString(request.epoch_response()), *state, |
| crypto_->GetRecoveryCrypto(), &recovery_request, &ephemeral_pub_key); |
| if (!status.ok()) { |
| if (status->local_legacy_error().has_value()) { |
| // Note: the error format should match `cryptohome_recovery_failure` in |
| // crash-reporter/anomaly_detector.cc |
| LOG(ERROR) << "Cryptohome Recovery GetRecoveryRequest failure, error = " |
| << status->local_legacy_error().value(); |
| } |
| ReplyWithError( |
| std::move(on_done), reply, |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocCryptoFailedInGenerateRecoveryRequest)) |
| .Wrap(std::move(status))); |
| return; |
| } |
| |
| cryptohome_recovery_ephemeral_pub_key_ = ephemeral_pub_key; |
| reply.set_recovery_request(recovery_request.to_string()); |
| std::move(on_done).Run(reply); |
| } |
| |
| AuthBlockType AuthSession::ResaveVaultKeysetIfNeeded( |
| const std::optional<brillo::SecureBlob> user_input, |
| AuthBlockType auth_block_type) { |
| // Check whether an update is needed for the VaultKeyset. If the user setup |
| // their account and the TPM was not owned, re-save it with the TPM. |
| // Also check whether the VaultKeyset has a wrapped reset seed and add reset |
| // seed if missing. |
| bool needs_update = false; |
| VaultKeyset updated_vault_keyset = *vault_keyset_.get(); |
| if (keyset_management_->ShouldReSaveKeyset(&updated_vault_keyset)) { |
| needs_update = true; |
| } |
| |
| // Adds a reset seed only to the password VaultKeysets. |
| if (keyset_management_->AddResetSeedIfMissing(updated_vault_keyset)) { |
| needs_update = true; |
| } |
| |
| if (needs_update == false) { |
| // No change is needed for |vault_keyset_| |
| return auth_block_type; |
| } |
| |
| // KeyBlobs needs to be re-created since there maybe a change in the |
| // AuthBlock type with the change in TPM state. Don't abort on failure. |
| // Only password and pin type credentials are evaluated for resave. |
| if (vault_keyset_->IsLECredential()) { |
| LOG(ERROR) << "Pinweaver AuthBlock is not supported for resave operation, " |
| "can't resave keyset."; |
| return auth_block_type; |
| } |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(AuthFactorType::kPassword); |
| CryptoStatusOr<AuthBlockType> out_auth_block_type = |
| auth_block_utility_->SelectAuthBlockTypeForCreation( |
| factor_driver.block_types()); |
| if (!out_auth_block_type.ok()) { |
| LOG(ERROR) |
| << "Error in creating obtaining AuthBlockType, can't resave keyset: " |
| << out_auth_block_type.status(); |
| return auth_block_type; |
| } |
| |
| // Create and initialize fields for AuthInput. |
| AuthInput auth_input = {.user_input = user_input, |
| .locked_to_single_user = std::nullopt, |
| .username = username_, |
| .obfuscated_username = obfuscated_username_, |
| .reset_secret = std::nullopt, |
| .reset_seed = std::nullopt, |
| .rate_limiter_label = std::nullopt, |
| .cryptohome_recovery_auth_input = std::nullopt, |
| .challenge_credential_auth_input = std::nullopt, |
| .fingerprint_auth_input = std::nullopt}; |
| |
| AuthBlock::CreateCallback create_callback = |
| base::BindOnce(&AuthSession::ResaveKeysetOnKeyBlobsGenerated, |
| base::Unretained(this), std::move(updated_vault_keyset)); |
| auth_block_utility_->CreateKeyBlobsWithAuthBlock( |
| out_auth_block_type.value(), auth_input, |
| /*CreateCallback*/ std::move(create_callback)); |
| |
| return out_auth_block_type.value(); |
| } |
| |
| void AuthSession::ResaveKeysetOnKeyBlobsGenerated( |
| VaultKeyset updated_vault_keyset, |
| CryptohomeStatus error, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::unique_ptr<AuthBlockState> auth_block_state) { |
| if (!error.ok() || key_blobs == nullptr || auth_block_state == nullptr) { |
| LOG(ERROR) << "Error in creating KeyBlobs, can't resave keyset."; |
| return; |
| } |
| |
| CryptohomeStatus status = keyset_management_->ReSaveKeyset( |
| updated_vault_keyset, std::move(*key_blobs), std::move(auth_block_state)); |
| // Updated ketyset is saved on the disk, it is safe to update |
| // |vault_keyset_|. |
| vault_keyset_ = std::make_unique<VaultKeyset>(updated_vault_keyset); |
| } |
| |
| CryptohomeStatusOr<AuthInput> AuthSession::CreateAuthInputForAuthentication( |
| const user_data_auth::AuthInput& auth_input_proto, |
| const AuthFactorMetadata& auth_factor_metadata) { |
| std::optional<AuthInput> auth_input = CreateAuthInput( |
| platform_, auth_input_proto, username_, obfuscated_username_, |
| auth_block_utility_->GetLockedToSingleUser(), |
| cryptohome_recovery_ephemeral_pub_key_, auth_factor_metadata); |
| if (!auth_input.has_value()) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocCreateFailedInAuthInputForAuth), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| return std::move(auth_input.value()); |
| } |
| |
| CryptohomeStatusOr<AuthInput> AuthSession::CreateAuthInputForMigration( |
| const AuthInput& auth_input, AuthFactorType auth_factor_type) { |
| AuthInput migration_auth_input = auth_input; |
| |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor_type); |
| if (!factor_driver.NeedsResetSecret()) { |
| // The factor is not resettable, so no extra data needed to be filled. |
| return std::move(migration_auth_input); |
| } |
| |
| if (!vault_keyset_) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocNoVkInAuthInputForMigration), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| |
| // After successful authentication `reset_secret` is available in the |
| // decrypted LE VaultKeyset, if the authenticated VaultKeyset is LE. |
| brillo::SecureBlob reset_secret = vault_keyset_->GetResetSecret(); |
| if (!reset_secret.empty()) { |
| LOG(INFO) << "Reset secret is obtained from PIN VaultKeyset with label: " |
| << vault_keyset_->GetLabel(); |
| migration_auth_input.reset_secret = std::move(reset_secret); |
| return std::move(migration_auth_input); |
| } |
| |
| // Update of an LE VaultKeyset can happen only after authenticating with a |
| // password VaultKeyset, which stores the password VaultKeyset in |
| // |vault_keyset_|. |
| return UpdateAuthInputWithResetParamsFromPasswordVk(auth_input, |
| *vault_keyset_); |
| } |
| |
| CryptohomeStatusOr<AuthInput> AuthSession::CreateAuthInputForAdding( |
| const user_data_auth::AuthInput& auth_input_proto, |
| AuthFactorType auth_factor_type, |
| const AuthFactorMetadata& auth_factor_metadata) { |
| // Convert the proto to a basic AuthInput. |
| std::optional<AuthInput> auth_input = CreateAuthInput( |
| platform_, auth_input_proto, username_, obfuscated_username_, |
| auth_block_utility_->GetLockedToSingleUser(), |
| cryptohome_recovery_ephemeral_pub_key_, auth_factor_metadata); |
| if (!auth_input.has_value()) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocCreateFailedInAuthInputForAdd), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| // Delegate the rest of the construction to the other overload. |
| return CreateAuthInputForAdding(*std::move(auth_input), auth_factor_type, |
| auth_factor_metadata); |
| } |
| |
| CryptohomeStatusOr<AuthInput> AuthSession::CreateAuthInputForAdding( |
| AuthInput auth_input, |
| AuthFactorType auth_factor_type, |
| const AuthFactorMetadata& auth_factor_metadata) { |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor_type); |
| |
| // Types which need rate-limiters are exclusive with those which need |
| // per-label reset secrets. |
| if (factor_driver.NeedsRateLimiter() && user_secret_stash_) { |
| // Currently fingerprint is the only auth factor type using rate limiter, so |
| // the interface isn't designed to be generic. We'll make it generic to any |
| // auth factor types in the future. |
| std::optional<uint64_t> rate_limiter_label = |
| user_secret_stash_->GetFingerprintRateLimiterId(); |
| // No existing rate-limiter, AuthBlock::Create will have to create one. |
| if (!rate_limiter_label.has_value()) { |
| return std::move(auth_input); |
| } |
| std::optional<brillo::SecureBlob> reset_secret = |
| user_secret_stash_->GetRateLimiterResetSecret(auth_factor_type); |
| if (!reset_secret.has_value()) { |
| LOG(ERROR) << "Found rate-limiter with no reset secret."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocRateLimiterNoResetSecretInAuthInputForAdd), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| auth_input.rate_limiter_label = rate_limiter_label; |
| auth_input.reset_secret = reset_secret; |
| return std::move(auth_input); |
| } |
| |
| if (factor_driver.NeedsResetSecret()) { |
| if (user_secret_stash_) { |
| // When using USS, every resettable factor gets a unique reset secret. |
| // When USS is not backed up by VaultKeysets this secret needs to be |
| // generated independently. |
| LOG(INFO) << "Adding random reset secret for UserSecretStash."; |
| auth_input.reset_secret = |
| CreateSecureRandomBlob(CRYPTOHOME_RESET_SECRET_LENGTH); |
| return std::move(auth_input); |
| } |
| |
| // When using VaultKeyset, reset is implemented via a seed that's shared |
| // among all of the user's VKs. Hence copy it from the previously loaded VK. |
| if (!vault_keyset_) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocNoVkInAuthInputForAdd), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| |
| return UpdateAuthInputWithResetParamsFromPasswordVk(auth_input, |
| *vault_keyset_); |
| } |
| |
| return std::move(auth_input); |
| } |
| |
| CryptohomeStatusOr<AuthInput> AuthSession::CreateAuthInputForSelectFactor( |
| AuthFactorType auth_factor_type) { |
| AuthInput auth_input{}; |
| |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor_type); |
| if (factor_driver.NeedsRateLimiter()) { |
| // Load the user metadata directly. |
| CryptohomeStatusOr<UserMetadata> user_metadata = |
| user_metadata_reader_->Load(obfuscated_username_); |
| if (!user_metadata.ok()) { |
| LOG(ERROR) << "Failed to load the user metadata."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionGetMetadataFailedInAuthInputForSelect), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE) |
| .Wrap(std::move(user_metadata).err_status()); |
| } |
| |
| // Currently fingerprint is the only auth factor type using rate |
| // limiter, so the field name isn't generic. We'll make it generic to any |
| // auth factor types in the future. |
| if (!user_metadata->fingerprint_rate_limiter_id.has_value()) { |
| LOG(ERROR) << "No rate limiter ID in user metadata."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNoRateLimiterInAuthInputForSelect), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState, |
| PossibleAction::kAuth}), |
| user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND); |
| } |
| |
| auth_input.rate_limiter_label = |
| user_metadata->fingerprint_rate_limiter_id.value(); |
| } |
| |
| return auth_input; |
| } |
| |
| CredentialVerifier* AuthSession::AddCredentialVerifier( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthInput& auth_input) { |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor_type); |
| if (auto new_verifier = factor_driver.CreateCredentialVerifier( |
| auth_factor_label, auth_input)) { |
| auto* return_ptr = new_verifier.get(); |
| verifier_forwarder_.AddVerifier(std::move(new_verifier)); |
| return return_ptr; |
| } |
| verifier_forwarder_.RemoveVerifier(auth_factor_label); |
| return nullptr; |
| } |
| |
| // static |
| std::optional<std::string> AuthSession::GetSerializedStringFromToken( |
| const base::UnguessableToken& token) { |
| if (token == base::UnguessableToken::Null()) { |
| LOG(ERROR) << "Invalid UnguessableToken given"; |
| return std::nullopt; |
| } |
| std::string serialized_token; |
| serialized_token.resize(kSizeOfSerializedValueInToken * |
| kNumberOfSerializedValuesInToken); |
| uint64_t high = token.GetHighForSerialization(); |
| uint64_t low = token.GetLowForSerialization(); |
| memcpy(&serialized_token[kHighTokenOffset], &high, sizeof(high)); |
| memcpy(&serialized_token[kLowTokenOffset], &low, sizeof(low)); |
| return serialized_token; |
| } |
| |
| // static |
| std::optional<base::UnguessableToken> AuthSession::GetTokenFromSerializedString( |
| const std::string& serialized_token) { |
| if (serialized_token.size() != |
| kSizeOfSerializedValueInToken * kNumberOfSerializedValuesInToken) { |
| LOG(ERROR) << "AuthSession: incorrect serialized string size: " |
| << serialized_token.size() << "."; |
| return std::nullopt; |
| } |
| uint64_t high, low; |
| memcpy(&high, &serialized_token[kHighTokenOffset], sizeof(high)); |
| memcpy(&low, &serialized_token[kLowTokenOffset], sizeof(low)); |
| if (high == 0 && low == 0) { |
| LOG(ERROR) << "AuthSession: all-zeroes serialized token is invalid"; |
| return std::nullopt; |
| } |
| return base::UnguessableToken::Deserialize(high, low); |
| } |
| |
| std::optional<ChallengeCredentialAuthInput> |
| AuthSession::CreateChallengeCredentialAuthInput( |
| const cryptohome::AuthorizationRequest& authorization) { |
| // There should only ever have 1 challenge response key in the request |
| // and having 0 or more than 1 element is considered invalid. |
| if (authorization.key().data().challenge_response_key_size() != 1) { |
| return std::nullopt; |
| } |
| if (!authorization.has_key_delegate() || |
| !authorization.key_delegate().has_dbus_service_name()) { |
| LOG(ERROR) << "Cannot do challenge-response operation without key " |
| "delegate information"; |
| return std::nullopt; |
| } |
| |
| const ChallengePublicKeyInfo& public_key_info = |
| authorization.key().data().challenge_response_key(0); |
| auto struct_public_key_info = cryptohome::proto::FromProto(public_key_info); |
| return ChallengeCredentialAuthInput{ |
| .public_key_spki_der = struct_public_key_info.public_key_spki_der, |
| .challenge_signature_algorithms = |
| struct_public_key_info.signature_algorithm, |
| .dbus_service_name = authorization.key_delegate().dbus_service_name(), |
| }; |
| } |
| |
| void AuthSession::PersistAuthFactorToUserSecretStash( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthFactorMetadata& auth_factor_metadata, |
| const AuthInput& auth_input, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| StatusCallback on_done, |
| CryptohomeStatus callback_error, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::unique_ptr<AuthBlockState> auth_block_state) { |
| CryptohomeStatus status = PersistAuthFactorToUserSecretStashImpl( |
| auth_factor_type, auth_factor_label, auth_factor_metadata, auth_input, |
| std::move(auth_session_performance_timer), |
| OverwriteExistingKeyBlock::kDisabled, std::move(callback_error), |
| std::move(key_blobs), std::move(auth_block_state)); |
| |
| std::move(on_done).Run(std::move(status)); |
| } |
| |
| void AuthSession::PersistAuthFactorToUserSecretStashOnMigration( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthFactorMetadata& auth_factor_metadata, |
| const AuthInput& auth_input, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| StatusCallback on_done, |
| CryptohomeStatus pre_migration_status, |
| CryptohomeStatus callback_error, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::unique_ptr<AuthBlockState> auth_block_state) { |
| // During the migration existing VaultKeyset should be recreated with the |
| // backup VaultKeyset logic. |
| CryptohomeStatus status = PersistAuthFactorToUserSecretStashImpl( |
| auth_factor_type, auth_factor_label, auth_factor_metadata, auth_input, |
| std::move(auth_session_performance_timer), |
| OverwriteExistingKeyBlock::kEnabled, std::move(callback_error), |
| std::move(key_blobs), std::move(auth_block_state)); |
| if (!status.ok()) { |
| LOG(ERROR) << "USS migration of VaultKeyset with label " |
| << auth_factor_label << " is failed: " << status; |
| ReapAndReportError(std::move(status), |
| kCryptohomeErrorUssMigrationErrorBucket); |
| ReportVkToUssMigrationStatus(VkToUssMigrationStatus::kFailedPersist); |
| std::move(on_done).Run(std::move(pre_migration_status)); |
| return; |
| } |
| |
| std::unique_ptr<VaultKeyset> remove_vk = keyset_management_->GetVaultKeyset( |
| obfuscated_username_, auth_factor_label); |
| if (!remove_vk || !keyset_management_->RemoveKeysetFile(*remove_vk).ok()) { |
| LOG(ERROR) |
| << "USS migration of VaultKeyset with label " << auth_factor_label |
| << " is completed, but failed removing the migrated VaultKeyset."; |
| ReportVkToUssMigrationStatus( |
| VkToUssMigrationStatus::kFailedRecordingMigrated); |
| std::move(on_done).Run(std::move(pre_migration_status)); |
| return; |
| } |
| |
| LOG(INFO) << "USS migration completed for VaultKeyset with label: " |
| << auth_factor_label; |
| ReportVkToUssMigrationStatus(VkToUssMigrationStatus::kSuccess); |
| std::move(on_done).Run(std::move(pre_migration_status)); |
| } |
| |
| CryptohomeStatus AuthSession::PersistAuthFactorToUserSecretStashImpl( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthFactorMetadata& auth_factor_metadata, |
| const AuthInput& auth_input, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| OverwriteExistingKeyBlock clobber_uss_key_block, |
| CryptohomeStatus callback_error, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::unique_ptr<AuthBlockState> auth_block_state) { |
| // Check the status of the callback error, to see if the key blob creation was |
| // actually successful. |
| if (!callback_error.ok() || !key_blobs || !auth_block_state) { |
| if (callback_error.ok()) { |
| callback_error = MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNullParamInPersistToUSS), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO, |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_NOT_IMPLEMENTED); |
| } |
| LOG(ERROR) << "KeyBlob creation failed before persisting USS and " |
| "auth factor with label: " |
| << auth_factor_label; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionCreateFailedInPersistToUSS), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED) |
| .Wrap(std::move(callback_error)); |
| } |
| |
| // Create the auth factor by combining the metadata with the auth block state. |
| auto auth_factor = |
| std::make_unique<AuthFactor>(auth_factor_type, auth_factor_label, |
| auth_factor_metadata, *auth_block_state); |
| |
| CryptohomeStatus status = AddAuthFactorToUssInMemory(*auth_factor, *key_blobs, |
| clobber_uss_key_block); |
| if (!status.ok()) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionAddToUssFailedInPersistToUSS), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED) |
| .Wrap(std::move(status)); |
| } |
| |
| // Encrypt the updated USS. |
| CryptohomeStatusOr<brillo::Blob> encrypted_uss_container = |
| user_secret_stash_->GetEncryptedContainer( |
| user_secret_stash_main_key_.value()); |
| if (!encrypted_uss_container.ok()) { |
| LOG(ERROR) << "Failed to encrypt user secret stash after auth factor " |
| "creation with label: " |
| << auth_factor_label; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionEncryptFailedInPersistToUSS), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED) |
| .Wrap(std::move(encrypted_uss_container).err_status()); |
| } |
| |
| // Persist the factor. |
| // It's important to do this after all the non-persistent steps so that we |
| // only start writing files after all validity checks (like the label |
| // duplication check). |
| status = |
| auth_factor_manager_->SaveAuthFactor(obfuscated_username_, *auth_factor); |
| if (!status.ok()) { |
| LOG(ERROR) << "Failed to persist created auth factor: " |
| << auth_factor_label; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionPersistFactorFailedInPersistToUSS), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED) |
| .Wrap(std::move(status)); |
| } |
| |
| // Persist the USS. |
| // It's important to do this after persisting the factor, to minimize the |
| // chance of ending in an inconsistent state on the disk: a created/updated |
| // USS and a missing auth factor (note that we're using file system syncs to |
| // have best-effort ordering guarantee). |
| status = user_secret_stash_storage_->Persist(encrypted_uss_container.value(), |
| obfuscated_username_); |
| if (!status.ok()) { |
| LOG(ERROR) << "Failed to persist user secret stash after the creation of " |
| "auth factor with label: " |
| << auth_factor_label; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionPersistUSSFailedInPersistToUSS), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED) |
| .Wrap(std::move(status)); |
| } |
| |
| // If a USS only factor is added backup keysets should be removed. |
| if (!IsFactorTypeSupportedByVk(auth_factor_type)) { |
| CryptohomeStatus cleanup_status = CleanUpAllBackupKeysets( |
| *keyset_management_, obfuscated_username_, auth_factor_map_); |
| if (!cleanup_status.ok()) { |
| LOG(ERROR) << "Cleaning up backup keysets failed."; |
| return (MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionCleanupBackupFailedInAddauthFactor), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED) |
| .Wrap(std::move(cleanup_status).err_status())); |
| } |
| } |
| |
| AddCredentialVerifier(auth_factor_type, auth_factor->label(), auth_input); |
| |
| LOG(INFO) << "AuthSession: added auth factor " << auth_factor->label() |
| << " into USS."; |
| auth_factor_map_.Add(std::move(auth_factor), |
| AuthFactorStorageType::kUserSecretStash); |
| |
| // Report timer for how long AuthSession operation takes. |
| ReportTimerDuration(auth_session_performance_timer.get()); |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| void AuthSession::CompleteVerifyOnlyAuthentication( |
| AuthenticateAuthFactorCallback on_done, |
| AuthenticateAuthFactorRequest original_request, |
| AuthFactorType auth_factor_type, |
| CryptohomeStatus error) { |
| // If there was no error then the verify was a success. |
| if (error.ok()) { |
| const AuthIntent lightweight_intents[] = {AuthIntent::kVerifyOnly}; |
| // Verify-only authentication might satisfy the kWebAuthn AuthIntent for the |
| // legacy FP AuthFactorType. In fact, that is the only possible scenario |
| // where we reach here with the kWebAuthn AuthIntent. |
| if (auth_intent_ == AuthIntent::kWebAuthn) { |
| authorized_intents_.insert(AuthIntent::kWebAuthn); |
| } |
| SetAuthorizedForIntents(lightweight_intents); |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor_type); |
| // There is at least 1 AuthFactor that needs full auth to reset, and the |
| // current auth factor used for authentication supports repeating full auth. |
| if (factor_driver.IsFullAuthRepeatable() && |
| NeedsFullAuthForReset(factor_driver.GetResetCapability())) { |
| original_request.flags.force_full_auth = ForceFullAuthFlag::kForce; |
| PostAuthAction action{ |
| .action_type = PostAuthActionType::kRepeat, |
| .repeat_request = std::move(original_request), |
| }; |
| std::move(on_done).Run(action, std::move(error)); |
| return; |
| } |
| } |
| // Forward whatever the result was to on_done. |
| std::move(on_done).Run(kNoPostAction, std::move(error)); |
| } |
| |
| CryptohomeStatus AuthSession::AddAuthFactorToUssInMemory( |
| AuthFactor& auth_factor, |
| const KeyBlobs& key_blobs, |
| OverwriteExistingKeyBlock clobber) { |
| // Derive the credential secret for the USS from the key blobs. |
| std::optional<brillo::SecureBlob> uss_credential_secret = |
| key_blobs.DeriveUssCredentialSecret(); |
| if (!uss_credential_secret.has_value()) { |
| LOG(ERROR) << "AuthSession: Failed to derive credential secret for " |
| "updated auth factor."; |
| // TODO(b/229834676): Migrate USS and wrap the error. |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionDeriveUSSSecretFailedInAddSecretToUSS), |
| ErrorActionSet({PossibleAction::kReboot, PossibleAction::kRetry, |
| PossibleAction::kDeleteVault}), |
| user_data_auth::CRYPTOHOME_UPDATE_CREDENTIALS_FAILED); |
| } |
| |
| // This wraps the USS Main Key with the credential secret. The wrapping_id |
| // field is defined equal to the factor's label. If this is called during the |
| // migration of a VaultKeyset to UserSecretStash clobbering should be enabled. |
| // This is because there may be some edge cases where migration fails after |
| // persisting to UserSecretStash; next time migration fail again since label |
| // already exists. |
| CryptohomeStatus status = user_secret_stash_->AddWrappedMainKey( |
| user_secret_stash_main_key_.value(), |
| /*wrapping_id=*/auth_factor.label(), uss_credential_secret.value(), |
| clobber); |
| if (!status.ok()) { |
| LOG(ERROR) << "AuthSession: Failed to add created auth factor into user " |
| "secret stash."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionAddMainKeyFailedInAddSecretToUSS), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED) |
| .Wrap(std::move(status)); |
| } |
| |
| // Types which need rate-limiters are exclusive with those which need |
| // per-label reset secrets. |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor.type()); |
| |
| if (factor_driver.NeedsRateLimiter() && |
| key_blobs.rate_limiter_label.has_value()) { |
| // A reset secret must come with the rate-limiter. |
| if (!key_blobs.reset_secret.has_value()) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocNewRateLimiterWithNoSecretInAddSecretToUSS), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED); |
| } |
| // Note that both setters don't allow overwrite, so if we run into a |
| // situation where one write succeeded where another failed, the state will |
| // be unrecoverable. |
| // |
| // Currently fingerprint is the only auth factor type using rate limiter, so |
| // the interface isn't designed to be generic. We'll make it generic to any |
| // auth factor types in the future. |
| if (!user_secret_stash_->InitializeFingerprintRateLimiterId( |
| key_blobs.rate_limiter_label.value())) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAddRateLimiterLabelFailedInAddSecretToUSS), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED); |
| } |
| if (!user_secret_stash_->SetRateLimiterResetSecret( |
| auth_factor.type(), key_blobs.reset_secret.value())) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAddRateLimiterSecretFailedInAddSecretToUSS), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED); |
| } |
| } |
| |
| if (factor_driver.NeedsResetSecret() && key_blobs.reset_secret.has_value()) { |
| // USS schema allows adding reset secrets before adding the actual key |
| // blocks. And it is possible that there is already an existing reset secret |
| // due to the USS migration flows such as password migration added the reset |
| // secret for the PIN beforehand. |
| bool reset_secret_exists = |
| user_secret_stash_->GetResetSecretForLabel(auth_factor.label()) |
| .has_value(); |
| if (reset_secret_exists && |
| clobber == OverwriteExistingKeyBlock::kDisabled) { |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| if ((reset_secret_exists) && |
| clobber == OverwriteExistingKeyBlock::kEnabled) { |
| if (!user_secret_stash_->RemoveResetSecretForLabel(auth_factor.label())) { |
| LOG(ERROR) << "AuthSession: Failed to add reset secret for auth factor " |
| "since clobbering failed removing existing secret."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionClobberResetSecretFailedInAddSecretToUSS), |
| ErrorActionSet({PossibleAction::kReboot, PossibleAction::kRetry}), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED); |
| } |
| } |
| |
| if (!user_secret_stash_->SetResetSecretForLabel( |
| auth_factor.label(), key_blobs.reset_secret.value())) { |
| LOG(ERROR) |
| << "AuthSession: Failed to insert reset secret for auth factor."; |
| // TODO(b/229834676): Migrate USS and wrap the error. |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionAddResetSecretFailedInAddSecretToUSS), |
| ErrorActionSet({PossibleAction::kReboot, PossibleAction::kRetry}), |
| user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED); |
| } |
| } |
| |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| void AuthSession::AddAuthFactor( |
| const user_data_auth::AddAuthFactorRequest& request, |
| StatusCallback on_done) { |
| // Preconditions: |
| CHECK_EQ(request.auth_session_id(), serialized_token_); |
| if (!authorized_intents_.contains(AuthIntent::kDecrypt)) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionUnauthedInAddAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION)); |
| return; |
| } |
| |
| AuthFactorType auth_factor_type; |
| std::string auth_factor_label; |
| AuthFactorMetadata auth_factor_metadata; |
| if (!AuthFactorPropertiesFromProto(request.auth_factor(), *features_, |
| auth_factor_type, auth_factor_label, |
| auth_factor_metadata)) { |
| LOG(ERROR) << "Failed to parse new auth factor parameters"; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionUnknownFactorInAddAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| on_done = WrapCallbackWithMetricsReporting( |
| std::move(on_done), auth_factor_type, |
| kCryptohomeErrorAddAuthFactorErrorBucket); |
| |
| CryptohomeStatusOr<AuthInput> auth_input_status = CreateAuthInputForAdding( |
| request.auth_input(), auth_factor_type, auth_factor_metadata); |
| if (!auth_input_status.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNoInputInAddAuthFactor)) |
| .Wrap(std::move(auth_input_status).err_status())); |
| return; |
| } |
| |
| if (is_ephemeral_user_) { |
| // If AuthSession is configured as an ephemeral user, then we do not save |
| // the key to the disk. |
| AddAuthFactorForEphemeral(auth_factor_type, auth_factor_label, |
| auth_input_status.value(), std::move(on_done)); |
| return; |
| } |
| |
| // Report timer for how long AddAuthFactor operation takes. |
| auto auth_session_performance_timer = |
| user_secret_stash_ ? std::make_unique<AuthSessionPerformanceTimer>( |
| kAuthSessionAddAuthFactorUSSTimer) |
| : std::make_unique<AuthSessionPerformanceTimer>( |
| kAuthSessionAddAuthFactorVKTimer); |
| |
| // Determine the auth block type to use. |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor_type); |
| CryptoStatusOr<AuthBlockType> auth_block_type = |
| auth_block_utility_->SelectAuthBlockTypeForCreation( |
| factor_driver.block_types()); |
| if (!auth_block_type.ok()) { |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionInvalidBlockTypeInAddAuthFactor), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE) |
| .Wrap(std::move(auth_block_type).status())); |
| return; |
| } |
| |
| // Parameterize timer by AuthBlockType. |
| auth_session_performance_timer->auth_block_type = auth_block_type.value(); |
| |
| KeyData key_data; |
| user_data_auth::CryptohomeErrorCode error = converter_.AuthFactorToKeyData( |
| auth_factor_label, auth_factor_type, auth_factor_metadata, key_data); |
| if (error != user_data_auth::CRYPTOHOME_ERROR_NOT_SET && |
| auth_factor_type != AuthFactorType::kCryptohomeRecovery && |
| auth_factor_type != AuthFactorType::kFingerprint) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionVKConverterFailsInAddAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), error)); |
| return; |
| } |
| |
| AuthFactorStorageType auth_factor_storage_type = |
| user_secret_stash_ ? AuthFactorStorageType::kUserSecretStash |
| : AuthFactorStorageType::kVaultKeyset; |
| |
| auto create_callback = GetAddAuthFactorCallback( |
| auth_factor_type, auth_factor_label, auth_factor_metadata, key_data, |
| auth_input_status.value(), auth_factor_storage_type, |
| std::move(auth_session_performance_timer), std::move(on_done)); |
| |
| auth_block_utility_->CreateKeyBlobsWithAuthBlock(auth_block_type.value(), |
| auth_input_status.value(), |
| std::move(create_callback)); |
| } |
| |
| AuthBlock::CreateCallback AuthSession::GetAddAuthFactorCallback( |
| const AuthFactorType& auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthFactorMetadata& auth_factor_metadata, |
| const KeyData& key_data, |
| const AuthInput& auth_input, |
| const AuthFactorStorageType auth_factor_storage_type, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| StatusCallback on_done) { |
| switch (auth_factor_storage_type) { |
| case AuthFactorStorageType::kUserSecretStash: |
| return base::BindOnce(&AuthSession::PersistAuthFactorToUserSecretStash, |
| weak_factory_.GetWeakPtr(), auth_factor_type, |
| auth_factor_label, auth_factor_metadata, auth_input, |
| std::move(auth_session_performance_timer), |
| std::move(on_done)); |
| |
| case AuthFactorStorageType::kVaultKeyset: |
| return base::BindOnce(&AuthSession::CreateAndPersistVaultKeyset, |
| weak_factory_.GetWeakPtr(), key_data, auth_input, |
| std::move(auth_session_performance_timer), |
| std::move(on_done)); |
| } |
| } |
| |
| void AuthSession::AddAuthFactorForEphemeral( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthInput& auth_input, |
| StatusCallback on_done) { |
| CHECK(is_ephemeral_user_); |
| |
| if (!auth_input.user_input.has_value()) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocNoUserInputInAddFactorForEphemeral), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| if (verifier_forwarder_.HasVerifier(auth_factor_label)) { |
| // Overriding the verifier for a given label is not supported. |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocVerifierAlreadySetInAddFactorForEphemeral), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE)); |
| return; |
| } |
| |
| CredentialVerifier* verifier = |
| AddCredentialVerifier(auth_factor_type, auth_factor_label, auth_input); |
| // Check whether the verifier creation failed. |
| if (!verifier) { |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocVerifierSettingErrorInAddFactorForEphemeral), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE)); |
| return; |
| } |
| |
| std::move(on_done).Run(OkStatus<CryptohomeError>()); |
| } |
| |
| void AuthSession::AuthenticateViaUserSecretStash( |
| const std::string& auth_factor_label, |
| const AuthInput auth_input, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| const AuthFactor& auth_factor, |
| StatusCallback on_done) { |
| // Determine the auth block type to use. |
| // TODO(b/223207622): This step is the same for both USS and VaultKeyset other |
| // than how the AuthBlock state is obtained, they can be merged. |
| std::optional<AuthBlockType> auth_block_type = |
| auth_block_utility_->GetAuthBlockTypeFromState( |
| auth_factor.auth_block_state()); |
| if (!auth_block_type) { |
| LOG(ERROR) << "Failed to determine auth block type for the loaded factor " |
| "with label " |
| << auth_factor.label(); |
| std::move(on_done).Run(MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionInvalidBlockTypeInAuthViaUSS), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO)); |
| return; |
| } |
| |
| // Parameterize timer by AuthBlockType. |
| auth_session_performance_timer->auth_block_type = *auth_block_type; |
| |
| // Derive the keyset and then use USS to complete the authentication. |
| auto derive_callback = base::BindOnce( |
| &AuthSession::LoadUSSMainKeyAndFsKeyset, weak_factory_.GetWeakPtr(), |
| auth_factor.type(), auth_factor_label, auth_input, |
| std::move(auth_session_performance_timer), std::move(on_done)); |
| auth_block_utility_->DeriveKeyBlobsWithAuthBlock( |
| *auth_block_type, auth_input, auth_factor.auth_block_state(), |
| std::move(derive_callback)); |
| } |
| |
| void AuthSession::AuthenticateViaSingleFactor( |
| const AuthFactorType& request_auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthInput& auth_input, |
| const AuthFactorMetadata& metadata, |
| const AuthFactorMap::ValueView& stored_auth_factor, |
| StatusCallback on_done) { |
| // If this auth factor comes from USS, run the USS flow. |
| if (stored_auth_factor.storage_type() == |
| AuthFactorStorageType::kUserSecretStash) { |
| // Record current time for timing for how long AuthenticateAuthFactor will |
| // take. |
| auto auth_session_performance_timer = |
| std::make_unique<AuthSessionPerformanceTimer>( |
| kAuthSessionAuthenticateAuthFactorUSSTimer); |
| |
| AuthenticateViaUserSecretStash(auth_factor_label, auth_input, |
| std::move(auth_session_performance_timer), |
| stored_auth_factor.auth_factor(), |
| std::move(on_done)); |
| return; |
| } |
| |
| // If user does not have USS AuthFactors, then we switch to authentication |
| // with Vaultkeyset. Status is flipped on the successful authentication. |
| user_data_auth::CryptohomeErrorCode error = converter_.PopulateKeyDataForVK( |
| obfuscated_username_, auth_factor_label, key_data_); |
| if (error != user_data_auth::CRYPTOHOME_ERROR_NOT_SET) { |
| LOG(ERROR) << "Failed to authenticate auth session via vk-factor " |
| << auth_factor_label; |
| // TODO(b/229834676): Migrate The USS VKK converter then wrap the error. |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionVKConverterFailedInAuthAuthFactor), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), error)); |
| return; |
| } |
| // Record current time for timing for how long AuthenticateAuthFactor will |
| // take. |
| auto auth_session_performance_timer = |
| std::make_unique<AuthSessionPerformanceTimer>( |
| kAuthSessionAuthenticateAuthFactorVKTimer); |
| |
| // Note that we pass in the auth factor type derived from the client request, |
| // instead of ones from the AuthFactor, because legacy VKs could not contain |
| // the auth factor type. |
| AuthenticateViaVaultKeysetAndMigrateToUss( |
| request_auth_factor_type, auth_factor_label, auth_input, metadata, |
| std::move(auth_session_performance_timer), std::move(on_done)); |
| } |
| |
| void AuthSession::AuthenticateViaSelectedAuthFactor( |
| StatusCallback on_done, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| CryptohomeStatus callback_error, |
| std::optional<AuthInput> auth_input, |
| std::optional<AuthFactor> auth_factor) { |
| if (!callback_error.ok() || !auth_input.has_value() || |
| !auth_factor.has_value()) { |
| if (callback_error.ok()) { |
| callback_error = MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNullParamInAuthViaSelected), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO, |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_NOT_IMPLEMENTED); |
| } |
| LOG(ERROR) << "AuthFactor selection failed before deriving KeyBlobs."; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionSelectionFailed)) |
| .Wrap(std::move(callback_error))); |
| return; |
| } |
| |
| AuthenticateViaUserSecretStash(auth_factor->label(), auth_input.value(), |
| std::move(auth_session_performance_timer), |
| auth_factor.value(), std::move(on_done)); |
| } |
| |
| void AuthSession::LoadUSSMainKeyAndFsKeyset( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| const AuthInput& auth_input, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| StatusCallback on_done, |
| CryptohomeStatus callback_error, |
| std::unique_ptr<KeyBlobs> key_blobs, |
| std::optional<AuthBlock::SuggestedAction> suggested_action) { |
| // Check the status of the callback error, to see if the key blob derivation |
| // was actually successful. |
| if (!callback_error.ok() || !key_blobs) { |
| if (callback_error.ok()) { |
| callback_error = MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionNullParamInLoadUSS), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO, |
| user_data_auth::CryptohomeErrorCode:: |
| CRYPTOHOME_ERROR_NOT_IMPLEMENTED); |
| } |
| // The user is locked out. So prepare an AuthFactorStatusUpdateSignal to be |
| // sent periodically until the user is not locked out anymore or until the |
| // auth session is timed out. |
| if (callback_error->local_legacy_error() == |
| user_data_auth::CRYPTOHOME_ERROR_CREDENTIAL_LOCKED) { |
| SendAuthFactorStatusUpdateSignal(); |
| } |
| LOG(ERROR) << "KeyBlob derivation failed before loading USS"; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionDeriveFailedInLoadUSS)) |
| .Wrap(std::move(callback_error))); |
| return; |
| } |
| |
| // Derive the credential secret for the USS from the key blobs. |
| std::optional<brillo::SecureBlob> uss_credential_secret = |
| key_blobs->DeriveUssCredentialSecret(); |
| if (!uss_credential_secret.has_value()) { |
| LOG(ERROR) |
| << "Failed to derive credential secret for authenticating auth factor"; |
| std::move(on_done).Run(MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionDeriveUSSSecretFailedInLoadUSS), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED)); |
| return; |
| } |
| |
| // Load the USS container with the encrypted payload. |
| CryptohomeStatusOr<brillo::Blob> encrypted_uss = |
| user_secret_stash_storage_->LoadPersisted(obfuscated_username_); |
| if (!encrypted_uss.ok()) { |
| LOG(ERROR) << "Failed to load the user secret stash"; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionLoadUSSFailedInLoadUSS), |
| user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED) |
| .Wrap(std::move(encrypted_uss).err_status())); |
| return; |
| } |
| |
| // Decrypt the USS payload. |
| // This unwraps the USS Main Key with the credential secret, and decrypts the |
| // USS payload using the USS Main Key. The wrapping_id field is defined equal |
| // to the factor's label. |
| brillo::SecureBlob decrypted_main_key; |
| CryptohomeStatusOr<std::unique_ptr<UserSecretStash>> |
| user_secret_stash_status = |
| UserSecretStash::FromEncryptedContainerWithWrappingKey( |
| encrypted_uss.value(), /*wrapping_id=*/auth_factor_label, |
| /*wrapping_key=*/uss_credential_secret.value(), |
| &decrypted_main_key); |
| if (!user_secret_stash_status.ok()) { |
| LOG(ERROR) << "Failed to decrypt the user secret stash"; |
| std::move(on_done).Run( |
| MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionDecryptUSSFailedInLoadUSS), |
| user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED) |
| .Wrap(std::move(user_secret_stash_status).err_status())); |
| return; |
| } |
| |
| // By this point we know that the GSC works correctly and we were able to |
| // successfully decrypt the USS. So, for GSC with updatable firmware, we |
| // assume that it is stable (and the GSC can invalidate the old version). |
| if (hwsec::Status status = crypto_->GetHwsec()->DeclareTpmFirmwareStable(); |
| !status.ok()) { |
| LOG(WARNING) << "Failed to declare TPM firmware stable: " << status; |
| } |
| |
| user_secret_stash_ = std::move(user_secret_stash_status).value(); |
| user_secret_stash_main_key_ = decrypted_main_key; |
| |
| // Populate data fields from the USS. |
| file_system_keyset_ = user_secret_stash_->GetFileSystemKeyset(); |
| |
| CryptohomeStatus prepare_status = OkStatus<error::CryptohomeError>(); |
| if (auth_intent_ == AuthIntent::kWebAuthn) { |
| // Even if we failed to prepare WebAuthn secret, file system keyset |
| // is already populated and we should proceed to set AuthSession as |
| // authenticated. Just return the error status at last. |
| prepare_status = PrepareWebAuthnSecret(); |
| if (!prepare_status.ok()) { |
| LOG(ERROR) << "Failed to prepare WebAuthn secret: " << prepare_status; |
| } |
| } |
| |
| // Flip the status on the successful authentication. |
| SetAuthorizedForFullAuthIntents(auth_factor_type); |
| |
| // Set the credential verifier for this credential. |
| AddCredentialVerifier(auth_factor_type, auth_factor_label, auth_input); |
| |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor_type); |
| // Reset all of the rate limiters and and credential lockouts. |
| ResetLECredentials(); |
| ResetRateLimiterCredentials(factor_driver.GetResetCapability()); |
| |
| // Backup VaultKeyset of the authenticated factor can still be in disk if |
| // the migration is not completed. Break the dependency of the migrated and |
| // not-migrated keysets and remove the backup keyset |
| if (auth_factor_map_.HasFactorWithStorage( |
| AuthFactorStorageType::kVaultKeyset) && |
| keyset_management_->GetVaultKeyset(obfuscated_username_, |
| auth_factor_label) != nullptr) { |
| // This code path runs to cleanup a backup VaultKeyset for a migrated-to-USS |
| // factor if it is not cleaned up due to the existence of not-migrated |
| // VaultKeyset factors. Report the cleanup result to UMA whether it is (i) |
| // success (ii) failure in adding reset_secret, or (iii) failure in removing |
| // the keyset file, recording whether is a password or PIN. |
| bool should_cleanup_backup_keyset = false; |
| if (auth_factor_type != AuthFactorType::kPassword) { |
| should_cleanup_backup_keyset = true; |
| } else { |
| // If there is an unmigrated PIN VaultKeyset we need to calculate the |
| // reset_secret from password backup VaultKeyset and not-migrated PIN |
| // keyset. In this case reset secret needs to be added to UserSecretStash |
| // before removing the backup keysets. |
| MountStatusOr<std::unique_ptr<VaultKeyset>> vk_status = |
| keyset_management_->GetValidKeyset(obfuscated_username_, |
| std::move(*key_blobs.get()), |
| auth_factor_label); |
| if (vk_status.ok()) { |
| vault_keyset_ = std::move(vk_status).value(); |
| if (PersistResetSecretToUss()) { |
| should_cleanup_backup_keyset = true; |
| } else { |
| ReportBackupKeysetCleanupResult( |
| BackupKeysetCleanupResult::kAddResetSecretFailed); |
| } |
| } else { |
| ReportBackupKeysetCleanupResult( |
| BackupKeysetCleanupResult::kGetValidKeysetFailed); |
| } |
| } |
| |
| // Cleanup backup VaultKeyset of the authenticated factor. |
| if (should_cleanup_backup_keyset) { |
| if (CleanUpBackupKeyset(*keyset_management_, obfuscated_username_, |
| auth_factor_label) |
| .ok()) { |
| ReportBackupKeysetCleanupSucessWithType(auth_factor_type); |
| |
| } else { |
| ReportBackupKeysetCleanupFileFailureWithType(auth_factor_type); |
| } |
| } |
| } |
| |
| // If the derive suggests recreating the factor, attempt to do that. If this |
| // fails we ignore the failure and report whatever status we were going to |
| // report anyway. |
| if (suggested_action == AuthBlock::SuggestedAction::kRecreate) { |
| RecreateUssAuthFactor(auth_factor_type, auth_factor_label, auth_input, |
| std::move(auth_session_performance_timer), |
| std::move(prepare_status), std::move(on_done)); |
| } else { |
| ReportTimerDuration(auth_session_performance_timer.get()); |
| std::move(on_done).Run(std::move(prepare_status)); |
| } |
| } |
| |
| void AuthSession::RecreateUssAuthFactor( |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label, |
| AuthInput auth_input, |
| std::unique_ptr<AuthSessionPerformanceTimer> auth_session_performance_timer, |
| CryptohomeStatus original_status, |
| StatusCallback on_done) { |
| const AuthFactorDriver& factor_driver = |
| auth_factor_driver_manager_->GetDriver(auth_factor_type); |
| CryptoStatusOr<AuthBlockType> auth_block_type = |
| auth_block_utility_->SelectAuthBlockTypeForCreation( |
| factor_driver.block_types()); |
| if (!auth_block_type.ok()) { |
| LOG(WARNING) << "Unable to update obsolete auth factor, cannot determine " |
| "new block type: " |
| << auth_block_type.err_status(); |
| ReportRecreateAuthFactorError(std::move(auth_block_type).status(), |
| auth_factor_type); |
| std::move(on_done).Run(std::move(original_status)); |
| return; |
| } |
| |
| std::optional<AuthFactorMap::ValueView> stored_auth_factor = |
| auth_factor_map_.Find(auth_factor_label); |
| if (!stored_auth_factor) { |
| auto status = MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionGetStoredFactorFailedInRecreate), |
| ErrorActionSet({PossibleAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND); |
| LOG(WARNING) << "Unable to update obsolete auth factor, it does not " |
| "seem to exist: " |
| << status; |
| ReportRecreateAuthFactorError(std::move(status), auth_factor_type); |
| std::move(on_done).Run(std::move(original_status)); |
| return; |
| } |
| const AuthFactor& auth_factor = stored_auth_factor->auth_factor(); |
| |
| CryptohomeStatusOr<AuthInput> auth_input_for_add = CreateAuthInputForAdding( |
| std::move(auth_input), auth_factor.type(), auth_factor.metadata()); |
| if (!auth_input_for_add.ok()) { |
| LOG(WARNING) << "Unable to construct an auth input to recreate the factor: " |
| << auth_input_for_add.err_status(); |
| ReportRecreateAuthFactorError(std::move(auth_input_for_add).status(), |
| auth_factor_type); |
| std::move(on_done).Run(std::move(original_status)); |
| return; |
| } |
| |
| // Make an on_done callback for passing in to GetUpdateAuthFactorCallback |
| // that ignores the result of the update and instead just sends in the |
| // existing prepare_status result that we would've sent if we hadn't tried |
| // the Update at all. |
| StatusCallback status_callback = base::BindOnce( |
| [](CryptohomeStatus original_status, StatusCallback on_done, |
| AuthFactorType auth_factor_type, CryptohomeStatus update_status) { |
| if (!update_status.ok()) { |
| LOG(WARNING) << "Recreating factor with update failed: " |
| << update_status; |
| ReportRecreateAuthFactorError(std::move(update_status), |
| auth_factor_type); |
| } else { |
| // If we reach here, the recreate operation is successful. If more |
| // error locations are added after this point, this needs to be moved. |
| ReportRecreateAuthFactorOk(auth_factor_type); |
| } |
| std::move(on_done).Run(std::move(original_status)); |
| }, |
| std::move(original_status), std::move(on_done), auth_factor_type); |
| |
| // Attempt to re-create the factor via a Create+Update. |
| auto create_callback = base::BindOnce( |
| &AuthSession::UpdateAuthFactorViaUserSecretStash, |
| weak_factory_.GetWeakPtr(), auth_factor.type(), auth_factor.label(), |
| auth_factor.metadata(), *auth_input_for_add, |
| std::move(auth_session_performance_timer), std::move(status_callback)); |
| auth_block_utility_->CreateKeyBlobsWithAuthBlock( |
| *auth_block_type, *auth_input_for_add, std::move(create_callback)); |
| } |
| |
| void AuthSession::ResetLECredentials() { |
| brillo::SecureBlob local_reset_seed; |
| if (vault_keyset_ && vault_keyset_->HasWrappedResetSeed()) { |
| local_reset_seed = vault_keyset_->GetResetSeed(); |
| } |
| |
| if (!user_secret_stash_ && local_reset_seed.empty()) { |
| LOG(ERROR) |
| << "No user secret stash or VK available to reset LE credentials."; |
| return; |
| } |
| |
| for (AuthFactorMap::ValueView stored_auth_factor : auth_factor_map_) { |
| const AuthFactor& auth_factor = stored_auth_factor.auth_factor(); |
| |
| // Look for only pinweaver backed AuthFactors. |
| auto* state = std::get_if<::cryptohome::PinWeaverAuthBlockState>( |
| &(auth_factor.auth_block_state().state)); |
| if (!state) { |
| continue; |
| } |
| // Ensure that the AuthFactor has le_label. |
| if (!state->le_label.has_value()) { |
| LOG(WARNING) << "PinWeaver AuthBlock State does not have le_label"; |
| continue; |
| } |
| // If the LECredential is already at 0 attempts, there is no need to reset |
| // it. |
| if (crypto_->GetWrongAuthAttempts(state->le_label.value()) == 0) { |
| continue; |
| } |
| |
| brillo::SecureBlob reset_secret; |
| std::optional<brillo::SecureBlob> reset_secret_uss; |
| // Get the reset secret from the USS for this auth factor label. |
| if (user_secret_stash_) { |
| reset_secret_uss = |
| user_secret_stash_->GetResetSecretForLabel(auth_factor.label()); |
| } |
| |
| if (reset_secret_uss.has_value()) { |
| reset_secret = reset_secret_uss.value(); |
| } else if (!local_reset_seed.empty()) { |
| // If USS does not have the reset secret for the auth factor, the reset |
| // secret might still be available through VK. |
| LOG(INFO) << "Reset secret could not be retrieved through USS for the LE " |
| "Credential with label " |
| << auth_factor.label() |
| << ". Will try to obtain it with the Vault Keyset reset seed."; |
| std::optional<brillo::SecureBlob> reset_secret_vk = |
| GetResetSecretFromVaultKeyset(local_reset_seed, obfuscated_username_, |
| auth_factor.label(), |
| *keyset_management_); |
| if (!reset_secret_vk.has_value()) { |
| LOG(WARNING) |
| << "Reset secret could not be retrieved through VaultKeyset for " |
| "the LE Credential with label " |
| << auth_factor.label(); |
| continue; |
| } |
| reset_secret = reset_secret_vk.value(); |
| } else { |
| LOG(WARNING) |
| << "Reset secret could not be retrieved through USS or " |
| "VaultKeyset since UserSecretStash doesn't include a reset " |
| "secret and VaultKeyset doesn't include a reset_salt for " |
| "the AuthFactor with label " |
| << auth_factor.label(); |
| continue; |
| } |
| |
| CryptoError error; |
| if (!crypto_->ResetLeCredential(state->le_label.value(), reset_secret, |
| /*strong_reset=*/false, error)) { |
| LOG(WARNING) << "Failed to reset an LE credential for " |
| << state->le_label.value() << " with error: " << error; |
| } |
| } |
| } |
| |
| void AuthSession::ResetRateLimiterCredentials( |
| AuthFactorDriver::ResetCapability capability) { |
| if (!user_secret_stash_) { |
| return; |
| } |
| std::optional<uint64_t> rate_limiter_label = |
| user_secret_stash_->GetFingerprintRateLimiterId(); |
| if (!rate_limiter_label.has_value()) { |
| return; |
| } |
| |
| // Currently only fingerprint auth factor has a rate-limiter. |
| std::optional<brillo::SecureBlob> reset_secret = |
| user_secret_stash_->GetRateLimiterResetSecret( |
| AuthFactorType::kFingerprint); |
| if (!reset_secret.has_value()) { |
| LOG(WARNING) << "Fingerprint rate-limiter has no reset secret in USS."; |
| return; |
| } |
| CryptoError error; |
| bool strong_reset = |
| capability == |
| AuthFactorDriver::ResetCapability::kResetWrongAttemptsAndExpiration; |
| // The only situation we don't need to reset the rate-limiter leaf is that |
| // wrong attempts is zero and expiration shouldn't be reset. |
| if (strong_reset || |
| crypto_->GetWrongAuthAttempts(rate_limiter_label.value()) != 0) { |
| if (!crypto_->ResetLeCredential(rate_limiter_label.value(), |
| reset_secret.value(), strong_reset, |
| error)) { |
| LOG(WARNING) << "Failed to reset fingerprint rate-limiter with error: " |
| << error; |
| } |
| } |
| |
| for (AuthFactorMap::ValueView stored_auth_factor : auth_factor_map_) { |
| const AuthFactor& auth_factor = stored_auth_factor.auth_factor(); |
| |
| // Look for only pinweaver backed AuthFactors. |
| auto* state = std::get_if<FingerprintAuthBlockState>( |
| &(auth_factor.auth_block_state().state)); |
| if (!state) { |
| continue; |
| } |
| // Ensure that the AuthFactor has le_label. |
| if (!state->gsc_secret_label.has_value()) { |
| LOG(WARNING) |
| << "Fingerprint AuthBlock State does not have gsc_secret_label."; |
| continue; |
| } |
| // If the credential is already at 0 attempts, there is no need to reset |
| // it. |
| if (crypto_->GetWrongAuthAttempts(state->gsc_secret_label.value()) == 0) { |
| continue; |
| } |
| if (!crypto_->ResetLeCredential(state->gsc_secret_label.value(), |
| reset_secret.value(), |
| /*strong_reset=*/false, error)) { |
| LOG(WARNING) << "Failed to reset fingerprint credential for " |
| << state->gsc_secret_label.value() |
| << " with error: " << error; |
| } |
| } |
| } |
| |
| bool AuthSession::NeedsFullAuthForReset( |
| AuthFactorDriver::ResetCapability capability) { |
| // Check if LE credentials need reset. |
| for (AuthFactorMap::ValueView stored_auth_factor : auth_factor_map_) { |
| const AuthFactor& auth_factor = stored_auth_factor.auth_factor(); |
| |
| // Look for only pinweaver backed AuthFactors. |
| auto* state = std::get_if<::cryptohome::PinWeaverAuthBlockState>( |
| &(auth_factor.auth_block_state().state)); |
| if (!state) { |
| continue; |
| } |
| // Ensure that the AuthFactor has le_label. |
| if (!state->le_label.has_value()) { |
| LOG(WARNING) << "PinWeaver AuthBlock State does not have le_label"; |
| continue; |
| } |
| // If the LECredential isn't at 0 attempts, it needs to be reset. |
| if (crypto_->GetWrongAuthAttempts(state->le_label.value()) != 0) { |
| return true; |
| } |
| } |
| |
| // Check if the rate-limiter needs reset. |
| CryptohomeStatusOr<UserMetadata> user_metadata = |
| user_metadata_reader_->Load(obfuscated_username_); |
| if (!user_metadata.ok()) { |
| return false; |
| } |
| if (!user_metadata->fingerprint_rate_limiter_id.has_value()) { |
| return false; |
| } |
| |
| // If reset expiration is supported, we should always reset. |
| if (capability == |
| AuthFactorDriver::ResetCapability::kResetWrongAttemptsAndExpiration) { |
| return true; |
| } |
| return crypto_->GetWrongAuthAttempts( |
| user_metadata->fingerprint_rate_limiter_id.value()) != 0; |
| } |
| |
| std::unique_ptr<brillo::SecureBlob> AuthSession::GetHibernateSecret() { |
| const FileSystemKeyset& fs_keyset = file_system_keyset(); |
| const std::string message(kHibernateSecretHmacMessage); |
| |
| return std::make_unique<brillo::SecureBlob>(HmacSha256( |
| brillo::SecureBlob::Combine(fs_keyset.Key().fnek, fs_keyset.Key().fek), |
| brillo::Blob(message.cbegin(), message.cend()))); |
| } |
| |
| void AuthSession::AddOnAuthCallback(base::OnceClosure on_auth) { |
| // If the session is not authorized, add it to the list of callbacks. |
| // Otherwise, just call the callback immediately. |
| if (authorized_intents_.empty()) { |
| on_auth_.push_back(std::move(on_auth)); |
| } else { |
| std::move(on_auth).Run(); |
| } |
| } |
| |
| void AuthSession::SetAuthFactorStatusUpdateCallback( |
| const AuthFactorStatusUpdateCallback& callback) { |
| auth_factor_status_update_callback_ = callback; |
| } |
| |
| CryptohomeStatus AuthSession::PrepareWebAuthnSecret() { |
| if (!file_system_keyset_.has_value()) { |
| LOG(ERROR) << "No file system keyset when preparing WebAuthn secret."; |
| return MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthSessionPrepareWebAuthnSecretNoFileSystemKeyset), |
| ErrorActionSet({error::PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO, |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_KEY_NOT_FOUND); |
| } |
| UserSession* const session = user_session_map_->Find(username_); |
| if (!session) { |
| LOG(ERROR) << "No user session found when preparing WebAuthn secret."; |
| return MakeStatus<CryptohomeCryptoError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthSessionPrepareWebAuthnSecretNoUserSession), |
| ErrorActionSet({error::PossibleAction::kDevCheckUnexpectedState}), |
| CryptoError::CE_OTHER_CRYPTO, |
| user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_KEY_NOT_FOUND); |
| } |
| session->PrepareWebAuthnSecret(file_system_keyset_->Key().fek, |
| file_system_keyset_->Key().fnek); |
| authorized_intents_.insert(AuthIntent::kWebAuthn); |
| return OkStatus<CryptohomeCryptoError>(); |
| } |
| |
| } // namespace cryptohome |