| // Copyright 2021 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_factor/auth_factor_manager.h" |
| |
| #include <sys/stat.h> |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <variant> |
| |
| #include <base/files/file_enumerator.h> |
| #include <base/files/file_path.h> |
| #include <base/check.h> |
| #include <base/logging.h> |
| #include <brillo/secure_blob.h> |
| #include <flatbuffers/flatbuffers.h> |
| #include <libhwsec-foundation/flatbuffers/basic_objects.h> |
| #include <libhwsec-foundation/flatbuffers/flatbuffer_secure_allocator_bridge.h> |
| |
| #include "cryptohome/auth_blocks/auth_block_utility.h" |
| #include "cryptohome/auth_factor/auth_factor.h" |
| #include "cryptohome/auth_factor/auth_factor_label.h" |
| #include "cryptohome/auth_factor/auth_factor_metadata.h" |
| #include "cryptohome/auth_factor/auth_factor_type.h" |
| #include "cryptohome/auth_factor_generated.h" |
| #include "cryptohome/error/location_utils.h" |
| #include "cryptohome/filesystem_layout.h" |
| #include "cryptohome/flatbuffer_schemas/auth_block_state_flatbuffer.h" |
| #include "cryptohome/platform.h" |
| |
| using brillo::Blob; |
| using cryptohome::error::CryptohomeError; |
| using cryptohome::error::ErrorAction; |
| using cryptohome::error::ErrorActionSet; |
| using hwsec_foundation::status::MakeStatus; |
| using hwsec_foundation::status::OkStatus; |
| using hwsec_foundation::status::StatusChain; |
| |
| namespace cryptohome { |
| |
| namespace { |
| |
| // Use rw------- for the auth factor files. |
| constexpr mode_t kAuthFactorFilePermissions = 0600; |
| |
| constexpr int kFlatbufferAllocatorInitialSize = 4096; |
| |
| // Checks if the provided `auth_factor_label` is valid and on success returns |
| // `AuthFactorPath()`. |
| CryptohomeStatusOr<base::FilePath> GetAuthFactorPathFromStringType( |
| const ObfuscatedUsername& obfuscated_username, |
| const std::string& auth_factor_type_string, |
| const std::string& auth_factor_label) { |
| if (!IsValidAuthFactorLabel(auth_factor_label)) { |
| LOG(ERROR) << "Invalid auth factor label " << auth_factor_label |
| << " of type " << auth_factor_type_string; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocGetAuthFactorPathInvalidLabel), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| |
| return AuthFactorPath(obfuscated_username, auth_factor_type_string, |
| auth_factor_label); |
| } |
| |
| // Converts `auth_factor_type` to string and on success calls |
| // `GetAuthFactorPathFromStringType()` method above. |
| CryptohomeStatusOr<base::FilePath> GetAuthFactorPath( |
| const ObfuscatedUsername& obfuscated_username, |
| const AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label) { |
| const std::string type_string = AuthFactorTypeToString(auth_factor_type); |
| if (type_string.empty()) { |
| LOG(ERROR) << "Failed to convert auth factor type " |
| << static_cast<int>(auth_factor_type) << " for factor called " |
| << auth_factor_label; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocGetAuthFactorPathWrongTypeString), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| |
| return GetAuthFactorPathFromStringType(obfuscated_username, type_string, |
| auth_factor_label); |
| } |
| |
| // Serializes the factor-specific metadata into the given flatbuffer builder. |
| // Returns the flatbuffer offset, to be used for building the outer table. |
| flatbuffers::Offset<SerializedCommonMetadata> SerializeCommonMetadataToOffset( |
| const CommonAuthFactorMetadata& common_metadata, |
| flatbuffers::FlatBufferBuilder& builder) { |
| auto chromeos_offset = hwsec_foundation::ToFlatBuffer<std::string>()( |
| &builder, common_metadata.chromeos_version_last_updated); |
| auto chrome_offset = hwsec_foundation::ToFlatBuffer<std::string>()( |
| &builder, common_metadata.chrome_version_last_updated); |
| |
| SerializedCommonMetadataBuilder cm_builder(builder); |
| cm_builder.add_chromeos_version_last_updated(chromeos_offset); |
| cm_builder.add_chrome_version_last_updated(chrome_offset); |
| return cm_builder.Finish(); |
| } |
| |
| flatbuffers::Offset<SerializedPasswordMetadata> SerializeMetadataToOffset( |
| const PasswordAuthFactorMetadata& password_metadata, |
| flatbuffers::FlatBufferBuilder* builder) { |
| SerializedPasswordMetadataBuilder metadata_builder(*builder); |
| return metadata_builder.Finish(); |
| } |
| |
| flatbuffers::Offset<SerializedPinMetadata> SerializeMetadataToOffset( |
| const PinAuthFactorMetadata& pin_metadata, |
| flatbuffers::FlatBufferBuilder* builder) { |
| SerializedPinMetadataBuilder metadata_builder(*builder); |
| return metadata_builder.Finish(); |
| } |
| |
| flatbuffers::Offset<SerializedCryptohomeRecoveryMetadata> |
| SerializeMetadataToOffset( |
| const CryptohomeRecoveryAuthFactorMetadata& recovery_metadata, |
| flatbuffers::FlatBufferBuilder* builder) { |
| SerializedCryptohomeRecoveryMetadataBuilder metadata_builder(*builder); |
| return metadata_builder.Finish(); |
| } |
| |
| flatbuffers::Offset<SerializedKioskMetadata> SerializeMetadataToOffset( |
| const KioskAuthFactorMetadata& kiosk_metadata, |
| flatbuffers::FlatBufferBuilder* builder) { |
| SerializedKioskMetadataBuilder metadata_builder(*builder); |
| return metadata_builder.Finish(); |
| } |
| |
| flatbuffers::Offset<SerializedSmartCardMetadata> SerializeMetadataToOffset( |
| const SmartCardAuthFactorMetadata& smart_card_metadata, |
| flatbuffers::FlatBufferBuilder* builder) { |
| auto public_key_offset = hwsec_foundation::ToFlatBuffer<brillo::Blob>()( |
| builder, *smart_card_metadata.public_key_spki_der); |
| SerializedSmartCardMetadataBuilder metadata_builder(*builder); |
| metadata_builder.add_public_key_spki_der(public_key_offset); |
| return metadata_builder.Finish(); |
| } |
| |
| flatbuffers::Offset<SerializedFingerprintMetadata> SerializeMetadataToOffset( |
| const FingerprintAuthFactorMetadata& fingerprint_metadata, |
| flatbuffers::FlatBufferBuilder* builder) { |
| SerializedFingerprintMetadataBuilder metadata_builder(*builder); |
| return metadata_builder.Finish(); |
| } |
| |
| // Serializes the factor-specific metadata into the given flatbuffer builder. |
| // Returns the flatbuffer offset, to be used for building the outer table. |
| // |
| // Implemented by selecting the appropriate specific overload based on the |
| // factor type and delegating to it. |
| flatbuffers::Offset<void> SerializeMetadataToOffset( |
| const AuthFactorMetadata& metadata, |
| flatbuffers::FlatBufferBuilder* builder, |
| SerializedAuthFactorMetadata* metadata_type) { |
| if (const auto* password_metadata = |
| std::get_if<PasswordAuthFactorMetadata>(&metadata.metadata)) { |
| *metadata_type = SerializedAuthFactorMetadata::SerializedPasswordMetadata; |
| return SerializeMetadataToOffset(*password_metadata, builder).Union(); |
| } else if (const auto* pin_metadata = |
| std::get_if<PinAuthFactorMetadata>(&metadata.metadata)) { |
| *metadata_type = SerializedAuthFactorMetadata::SerializedPinMetadata; |
| return SerializeMetadataToOffset(*pin_metadata, builder).Union(); |
| } else if (const auto* smart_card_metadata = |
| std::get_if<SmartCardAuthFactorMetadata>(&metadata.metadata)) { |
| *metadata_type = SerializedAuthFactorMetadata::SerializedSmartCardMetadata; |
| return SerializeMetadataToOffset(*smart_card_metadata, builder).Union(); |
| } else if (const auto* recovery_metadata = |
| std::get_if<CryptohomeRecoveryAuthFactorMetadata>( |
| &metadata.metadata)) { |
| *metadata_type = |
| SerializedAuthFactorMetadata::SerializedCryptohomeRecoveryMetadata; |
| return SerializeMetadataToOffset(*recovery_metadata, builder).Union(); |
| } else if (const auto* kiosk_metadata = |
| std::get_if<KioskAuthFactorMetadata>(&metadata.metadata)) { |
| *metadata_type = SerializedAuthFactorMetadata::SerializedKioskMetadata; |
| return SerializeMetadataToOffset(*kiosk_metadata, builder).Union(); |
| } else if (const auto* fingerprint_metadata = |
| std::get_if<FingerprintAuthFactorMetadata>( |
| &metadata.metadata)) { |
| *metadata_type = |
| SerializedAuthFactorMetadata::SerializedFingerprintMetadata; |
| return SerializeMetadataToOffset(*fingerprint_metadata, builder).Union(); |
| } |
| LOG(ERROR) << "Missing or unexpected auth factor metadata: " |
| << metadata.metadata.index(); |
| return flatbuffers::Offset<void>(); |
| } |
| |
| // Serializes the auth factor into a flatbuffer blob. Returns null on failure. |
| std::optional<Blob> SerializeAuthFactor(const AuthFactor& auth_factor) { |
| hwsec_foundation::FlatbufferSecureAllocatorBridge allocator; |
| flatbuffers::FlatBufferBuilder builder(kFlatbufferAllocatorInitialSize, |
| &allocator); |
| |
| auto auth_block_state_offset = |
| hwsec_foundation::ToFlatBuffer<AuthBlockState>()( |
| &builder, auth_factor.auth_block_state()); |
| if (auth_block_state_offset.IsNull()) { |
| LOG(ERROR) << "Failed to serialize auth block state"; |
| return std::nullopt; |
| } |
| |
| SerializedAuthFactorMetadata metadata_type = |
| SerializedAuthFactorMetadata::NONE; |
| flatbuffers::Offset<void> metadata_offset = SerializeMetadataToOffset( |
| auth_factor.metadata(), &builder, &metadata_type); |
| if (metadata_offset.IsNull()) { |
| LOG(ERROR) << "Failed to serialize metadata"; |
| return std::nullopt; |
| } |
| |
| auto common_metadata_offset = |
| SerializeCommonMetadataToOffset(auth_factor.metadata().common, builder); |
| |
| SerializedAuthFactorBuilder auth_factor_builder(builder); |
| auth_factor_builder.add_auth_block_state(auth_block_state_offset); |
| auth_factor_builder.add_metadata(metadata_offset); |
| auth_factor_builder.add_metadata_type(metadata_type); |
| auth_factor_builder.add_common_metadata(common_metadata_offset); |
| auto auth_factor_offset = auth_factor_builder.Finish(); |
| |
| builder.Finish(auth_factor_offset); |
| return Blob(builder.GetBufferPointer(), |
| builder.GetBufferPointer() + builder.GetSize()); |
| } |
| |
| void ConvertCommonMetadataFromFlatbuffer( |
| const SerializedCommonMetadata& flatbuffer_table, |
| AuthFactorMetadata* metadata) { |
| metadata->common = CommonAuthFactorMetadata{ |
| .chromeos_version_last_updated = |
| hwsec_foundation::FromFlatBuffer<std::string>()( |
| flatbuffer_table.chromeos_version_last_updated()), |
| .chrome_version_last_updated = |
| hwsec_foundation::FromFlatBuffer<std::string>()( |
| flatbuffer_table.chrome_version_last_updated()), |
| }; |
| } |
| |
| bool ConvertPasswordMetadataFromFlatbuffer( |
| const SerializedPasswordMetadata& flatbuffer_table, |
| AuthFactorMetadata* metadata) { |
| // There's no password-specific metadata currently. |
| metadata->metadata = PasswordAuthFactorMetadata(); |
| return true; |
| } |
| |
| bool ConvertPinMetadataFromFlatbuffer( |
| const SerializedPinMetadata& flatbuffer_table, |
| AuthFactorMetadata* metadata) { |
| // There's no pin-specific metadata currently. |
| metadata->metadata = PinAuthFactorMetadata(); |
| return true; |
| } |
| |
| bool ConvertCryptohomeRecoveryMetadataFromFlatbuffer( |
| const SerializedCryptohomeRecoveryMetadata& flatbuffer_table, |
| AuthFactorMetadata* metadata) { |
| // There's no metadata currently. |
| metadata->metadata = CryptohomeRecoveryAuthFactorMetadata(); |
| return true; |
| } |
| |
| bool ConvertSmartCardMetadataFromFlatbuffer( |
| const SerializedSmartCardMetadata& flatbuffer_table, |
| AuthFactorMetadata* metadata) { |
| auto data = hwsec_foundation::FromFlatBuffer<brillo::Blob>()( |
| flatbuffer_table.public_key_spki_der()); |
| metadata->metadata = SmartCardAuthFactorMetadata{.public_key_spki_der = data}; |
| return true; |
| } |
| |
| bool ConvertKioskMetadataFromFlatbuffer( |
| const SerializedKioskMetadata& flatbuffer_table, |
| AuthFactorMetadata* metadata) { |
| // There's no metadata currently. |
| metadata->metadata = KioskAuthFactorMetadata(); |
| return true; |
| } |
| |
| bool ConvertFingerprintMetadataFromFlatbuffer( |
| const SerializedFingerprintMetadata& flatbuffer_table, |
| AuthFactorMetadata* metadata) { |
| // There's no metadata currently. |
| metadata->metadata = FingerprintAuthFactorMetadata(); |
| return true; |
| } |
| |
| bool ParseAuthFactorFlatbuffer(const Blob& flatbuffer, |
| AuthBlockState* auth_block_state, |
| AuthFactorMetadata* metadata) { |
| flatbuffers::Verifier flatbuffer_verifier(flatbuffer.data(), |
| flatbuffer.size()); |
| if (!VerifySerializedAuthFactorBuffer(flatbuffer_verifier)) { |
| LOG(ERROR) << "The SerializedAuthFactor flatbuffer is invalid"; |
| return false; |
| } |
| |
| auto auth_factor_table = GetSerializedAuthFactor(flatbuffer.data()); |
| |
| // Extract the auth block state from the serialized data. |
| if (!auth_factor_table->auth_block_state()) { |
| LOG(ERROR) << "SerializedAuthFactor has no auth block state"; |
| return false; |
| } |
| *auth_block_state = hwsec_foundation::FromFlatBuffer<AuthBlockState>()( |
| auth_factor_table->auth_block_state()); |
| |
| // Extract the common metadata from the serialized data. |
| if (!auth_factor_table->common_metadata()) { |
| // This is not an error, old auth factors have no common metadata. |
| LOG(WARNING) << "SerializedAuthFactor has no common metadata"; |
| } else { |
| ConvertCommonMetadataFromFlatbuffer(*auth_factor_table->common_metadata(), |
| metadata); |
| } |
| |
| // Extract the factor-specific metadata from the serialized data. |
| if (!auth_factor_table->metadata()) { |
| LOG(ERROR) << "SerializedAuthFactor has no metadata"; |
| return false; |
| } |
| if (const SerializedPasswordMetadata* password_metadata = |
| auth_factor_table->metadata_as_SerializedPasswordMetadata()) { |
| if (!ConvertPasswordMetadataFromFlatbuffer(*password_metadata, metadata)) { |
| LOG(ERROR) << "Failed to convert SerializedAuthFactor password metadata"; |
| return false; |
| } |
| } else if (const SerializedPinMetadata* pin_metadata = |
| auth_factor_table->metadata_as_SerializedPinMetadata()) { |
| if (!ConvertPinMetadataFromFlatbuffer(*pin_metadata, metadata)) { |
| LOG(ERROR) << "Failed to convert SerializedAuthFactor pin metadata"; |
| return false; |
| } |
| } else if (const SerializedCryptohomeRecoveryMetadata* recovery_metadata = |
| auth_factor_table |
| ->metadata_as_SerializedCryptohomeRecoveryMetadata()) { |
| if (!ConvertCryptohomeRecoveryMetadataFromFlatbuffer(*recovery_metadata, |
| metadata)) { |
| LOG(ERROR) << "Failed to convert SerializedAuthFactor recovery metadata"; |
| return false; |
| } |
| } else if (const SerializedSmartCardMetadata* smart_card_metadata = |
| auth_factor_table->metadata_as_SerializedSmartCardMetadata()) { |
| if (!ConvertSmartCardMetadataFromFlatbuffer(*smart_card_metadata, |
| metadata)) { |
| LOG(ERROR) |
| << "Failed to convert SerializedAuthFactor smart card metadata"; |
| return false; |
| } |
| } else if (const SerializedKioskMetadata* kiosk_metadata = |
| auth_factor_table->metadata_as_SerializedKioskMetadata()) { |
| if (!ConvertKioskMetadataFromFlatbuffer(*kiosk_metadata, metadata)) { |
| LOG(ERROR) << "Failed to convert SerializedAuthFactor kiosk metadata"; |
| return false; |
| } |
| } else if (const SerializedFingerprintMetadata* fingerprint_metadata = |
| auth_factor_table |
| ->metadata_as_SerializedFingerprintMetadata()) { |
| if (!ConvertFingerprintMetadataFromFlatbuffer(*fingerprint_metadata, |
| metadata)) { |
| LOG(ERROR) |
| << "Failed to convert SerializedAuthFactor fingerprint metadata"; |
| return false; |
| } |
| } else { |
| LOG(ERROR) << "SerializedAuthFactor has unknown metadata"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| AuthFactorManager::AuthFactorManager(Platform* platform) : platform_(platform) { |
| DCHECK(platform_); |
| } |
| |
| AuthFactorManager::~AuthFactorManager() = default; |
| |
| CryptohomeStatus AuthFactorManager::SaveAuthFactor( |
| const ObfuscatedUsername& obfuscated_username, |
| const AuthFactor& auth_factor) { |
| CryptohomeStatusOr<base::FilePath> file_path = GetAuthFactorPath( |
| obfuscated_username, auth_factor.type(), auth_factor.label()); |
| if (!file_path.ok()) { |
| LOG(ERROR) << "Failed to get auth factor path in Save."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerGetPathFailedInSave), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| |
| // Create a flatbuffer to be persisted. |
| std::optional<Blob> flatbuffer = SerializeAuthFactor(auth_factor); |
| if (!flatbuffer.has_value()) { |
| LOG(ERROR) << "Failed to serialize auth factor " << auth_factor.label() |
| << " of type " << AuthFactorTypeToString(auth_factor.type()); |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerSerializeFailedInSave), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| |
| // Write the file. |
| if (!platform_->WriteFileAtomicDurable(file_path.value(), flatbuffer.value(), |
| kAuthFactorFilePermissions)) { |
| LOG(ERROR) << "Failed to persist auth factor " << auth_factor.label() |
| << " of type " << AuthFactorTypeToString(auth_factor.type()) |
| << " for " << obfuscated_username; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerWriteFailedInSave), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| CryptohomeStatusOr<std::unique_ptr<AuthFactor>> |
| AuthFactorManager::LoadAuthFactor(const ObfuscatedUsername& obfuscated_username, |
| AuthFactorType auth_factor_type, |
| const std::string& auth_factor_label) { |
| CryptohomeStatusOr<base::FilePath> file_path = GetAuthFactorPath( |
| obfuscated_username, auth_factor_type, auth_factor_label); |
| if (!file_path.ok()) { |
| LOG(ERROR) << "Failed to get auth factor path in Load."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerGetPathFailedInLoad), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| |
| Blob file_contents; |
| if (!platform_->ReadFile(file_path.value(), &file_contents)) { |
| LOG(ERROR) << "Failed to load persisted auth factor " << auth_factor_label |
| << " of type " << AuthFactorTypeToString(auth_factor_type) |
| << " for " << obfuscated_username; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerReadFailedInLoad), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| // This check is redundant to the flatbuffer parsing below, but we check it |
| // here in order to distinguish "empty file" from "corrupted file" in metrics |
| // and logs. |
| if (file_contents.empty()) { |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerEmptyReadInLoad), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| |
| AuthBlockState auth_block_state; |
| AuthFactorMetadata auth_factor_metadata; |
| if (!ParseAuthFactorFlatbuffer(file_contents, &auth_block_state, |
| &auth_factor_metadata)) { |
| LOG(ERROR) << "Failed to parse persisted auth factor " << auth_factor_label |
| << " of type " << AuthFactorTypeToString(auth_factor_type) |
| << " for " << obfuscated_username; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerParseFailedInLoad), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| |
| return std::make_unique<AuthFactor>(auth_factor_type, auth_factor_label, |
| auth_factor_metadata, auth_block_state); |
| } |
| |
| std::map<std::string, std::unique_ptr<AuthFactor>> |
| AuthFactorManager::LoadAllAuthFactors( |
| const ObfuscatedUsername& obfuscated_username) { |
| std::map<std::string, std::unique_ptr<AuthFactor>> label_to_auth_factor; |
| for (const auto& [label, auth_factor_type] : |
| ListAuthFactors(obfuscated_username)) { |
| CryptohomeStatusOr<std::unique_ptr<AuthFactor>> auth_factor = |
| LoadAuthFactor(obfuscated_username, auth_factor_type, label); |
| if (!auth_factor.ok()) { |
| LOG(WARNING) << "Skipping malformed auth factor " << label; |
| continue; |
| } |
| label_to_auth_factor.emplace(label, std::move(auth_factor).value()); |
| } |
| return label_to_auth_factor; |
| } |
| |
| AuthFactorManager::LabelToTypeMap AuthFactorManager::ListAuthFactors( |
| const ObfuscatedUsername& obfuscated_username) { |
| LabelToTypeMap label_to_type_map; |
| |
| std::unique_ptr<FileEnumerator> file_enumerator(platform_->GetFileEnumerator( |
| AuthFactorsDirPath(obfuscated_username), /*recursive=*/false, |
| base::FileEnumerator::FILES)); |
| base::FilePath next_path; |
| while (!(next_path = file_enumerator->Next()).empty()) { |
| const base::FilePath base_name = next_path.BaseName(); |
| |
| if (!base_name.RemoveFinalExtension().FinalExtension().empty()) { |
| // Silently ignore files that have multiple extensions; to note, a |
| // legitimate case of such files is the checksum file |
| // ("<type>.<label>.sum"). |
| continue; |
| } |
| |
| // Parse and sanitize the type. |
| const std::string auth_factor_type_string = |
| base_name.RemoveExtension().value(); |
| const std::optional<AuthFactorType> auth_factor_type = |
| AuthFactorTypeFromString(auth_factor_type_string); |
| if (!auth_factor_type.has_value()) { |
| LOG(WARNING) << "Unknown auth factor type: file name = " |
| << base_name.value(); |
| continue; |
| } |
| |
| // Parse and sanitize the label. Note that `FilePath::Extension()` returns a |
| // string with a leading dot. |
| const std::string extension = base_name.Extension(); |
| if (extension.length() <= 1 || |
| extension[0] != base::FilePath::kExtensionSeparator) { |
| LOG(WARNING) << "Missing auth factor label: file name = " |
| << base_name.value(); |
| continue; |
| } |
| const std::string auth_factor_label = extension.substr(1); |
| if (!IsValidAuthFactorLabel(auth_factor_label)) { |
| LOG(WARNING) << "Invalid auth factor label: file name = " |
| << base_name.value(); |
| continue; |
| } |
| |
| // Check for label clashes. |
| if (label_to_type_map.count(auth_factor_label)) { |
| const AuthFactorType previous_type = label_to_type_map[auth_factor_label]; |
| LOG(WARNING) << "Ignoring duplicate auth factor: label = " |
| << auth_factor_label << " type = " << auth_factor_type_string |
| << " previous type = " |
| << AuthFactorTypeToString(previous_type); |
| continue; |
| } |
| |
| // All checks passed - add the factor. |
| label_to_type_map.insert({auth_factor_label, auth_factor_type.value()}); |
| } |
| |
| return label_to_type_map; |
| } |
| |
| CryptohomeStatus AuthFactorManager::RemoveAuthFactor( |
| const ObfuscatedUsername& obfuscated_username, |
| const AuthFactor& auth_factor, |
| AuthBlockUtility* auth_block_utility) { |
| CryptohomeStatusOr<base::FilePath> file_path = GetAuthFactorPath( |
| obfuscated_username, auth_factor.type(), auth_factor.label()); |
| if (!file_path.ok()) { |
| LOG(ERROR) << "Failed to get auth factor path in Remove."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerGetPathFailedInRemove), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState}), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT); |
| } |
| |
| CryptohomeStatus status = auth_block_utility->PrepareAuthBlockForRemoval( |
| auth_factor.auth_block_state()); |
| if (!status.ok()) { |
| LOG(WARNING) << "Failed to prepare for removal for auth factor " |
| << auth_factor.label() << " of type " |
| << AuthFactorTypeToString(auth_factor.type()) << " for " |
| << obfuscated_username; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthFactorManagerPrepareForRemovalFailedInRemove), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState})) |
| .Wrap(std::move(status)); |
| } |
| |
| // Remove the file. |
| if (!platform_->DeleteFileSecurely(file_path.value())) { |
| LOG(WARNING) << "Failed to securely delete from disk auth factor " |
| << auth_factor.label() << " of type " |
| << AuthFactorTypeToString(auth_factor.type()) << " for " |
| << obfuscated_username |
| << ". Attempting to delete without zeroization."; |
| if (!platform_->DeleteFile(file_path.value())) { |
| LOG(ERROR) << "Failed to delete from disk auth factor " |
| << auth_factor.label() << " of type " |
| << AuthFactorTypeToString(auth_factor.type()) << " for " |
| << obfuscated_username; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerDeleteFailedInRemove), |
| ErrorActionSet({ErrorAction::kDevCheckUnexpectedState, |
| ErrorAction::kRetry, ErrorAction::kReboot}), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE); |
| } |
| } |
| LOG(INFO) << "Deleted from disk auth factor label: " << auth_factor.label(); |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| CryptohomeStatus AuthFactorManager::UpdateAuthFactor( |
| const ObfuscatedUsername& obfuscated_username, |
| const std::string& auth_factor_label, |
| AuthFactor& auth_factor, |
| AuthBlockUtility* auth_block_utility) { |
| // 1. Load the old auth factor state from disk. |
| CryptohomeStatusOr<std::unique_ptr<AuthFactor>> existing_auth_factor = |
| LoadAuthFactor(obfuscated_username, auth_factor.type(), |
| auth_factor_label); |
| if (!existing_auth_factor.ok()) { |
| LOG(ERROR) << "Failed to load persisted auth factor " << auth_factor_label |
| << " of type " << AuthFactorTypeToString(auth_factor.type()) |
| << " for " << obfuscated_username << " in Update."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerLoadFailedInUpdate), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE) |
| .Wrap(std::move(existing_auth_factor).err_status()); |
| } |
| |
| // 2. Save auth factor to disk - the old auth factor state will be overridden |
| // and accessible only from `existing_auth_factor` object. |
| CryptohomeStatus save_result = |
| SaveAuthFactor(obfuscated_username, auth_factor); |
| if (!save_result.ok()) { |
| LOG(ERROR) << "Failed to save auth factor " << auth_factor.label() |
| << " of type " << AuthFactorTypeToString(auth_factor.type()) |
| << " for " << obfuscated_username << " in Update."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC(kLocAuthFactorManagerSaveFailedInUpdate), |
| user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE) |
| .Wrap(std::move(save_result)); |
| } |
| |
| // 3. The old auth factor state was removed from disk. Call |
| // `PrepareForRemoval()` to complete the removal. |
| CryptohomeStatus status = auth_block_utility->PrepareAuthBlockForRemoval( |
| existing_auth_factor.value()->auth_block_state()); |
| if (!status.ok()) { |
| LOG(WARNING) << "PrepareForRemoval failed for auth factor " |
| << auth_factor.label() << " of type " |
| << AuthFactorTypeToString(auth_factor.type()) << " for " |
| << obfuscated_username << " in Update."; |
| return MakeStatus<CryptohomeError>( |
| CRYPTOHOME_ERR_LOC( |
| kLocAuthFactorManagerPrepareForRemovalFailedInUpdate), |
| user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT) |
| .Wrap(std::move(status)); |
| } |
| |
| return OkStatus<CryptohomeError>(); |
| } |
| |
| } // namespace cryptohome |