blob: 78ff88a6fb40a261a95ef86fce58fcb5d30efbd6 [file] [log] [blame]
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cryptohome/error/converter.h"
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <base/logging.h>
#include <libhwsec-foundation/error/error.h>
#include "cryptohome/error/action.h"
#include "cryptohome/error/reporting.h"
namespace cryptohome {
namespace error {
namespace {
// Functions for mapping the ErrorAction in cryptohome into PossibleAction and
// PrimaryAction that the chromium side can understand.
std::optional<user_data_auth::PrimaryAction> ErrorActionToPrimaryAction(
ErrorAction err) {
switch (err) {
case ErrorAction::kCreateRequired:
return user_data_auth::PrimaryAction::PRIMARY_CREATE_REQUIRED;
case ErrorAction::kNotifyOldEncryption:
return user_data_auth::PrimaryAction::
PRIMARY_NOTIFY_OLD_ENCRYPTION_POLICY;
case ErrorAction::kResumePreviousMigration:
return user_data_auth::PrimaryAction::PRIMARY_RESUME_PREVIOUS_MIGRATION;
case ErrorAction::kTpmUpdateRequired:
return user_data_auth::PrimaryAction::PRIMARY_TPM_UDPATE_REQUIRED;
case ErrorAction::kTpmNeedsReboot:
return user_data_auth::PrimaryAction::PRIMARY_TPM_NEEDS_REBOOT;
case ErrorAction::kTpmLockout:
return user_data_auth::PrimaryAction::PRIMARY_TPM_LOCKOUT;
case ErrorAction::kIncorrectAuth:
return user_data_auth::PrimaryAction::PRIMARY_INCORRECT_AUTH;
default:
return std::nullopt;
}
}
std::optional<user_data_auth::PossibleAction> ErrorActionToPossibleAction(
ErrorAction err) {
switch (err) {
case ErrorAction::kRetry:
return user_data_auth::PossibleAction::POSSIBLY_RETRY;
case ErrorAction::kReboot:
return user_data_auth::PossibleAction::POSSIBLY_REBOOT;
case ErrorAction::kAuth:
return user_data_auth::PossibleAction::POSSIBLY_AUTH;
case ErrorAction::kDeleteVault:
return user_data_auth::PossibleAction::POSSIBLY_DELETE_VAULT;
case ErrorAction::kPowerwash:
return user_data_auth::PossibleAction::POSSIBLY_POWERWASH;
case ErrorAction::kDevCheckUnexpectedState:
return user_data_auth::PossibleAction::
POSSIBLY_DEV_CHECK_UNEXPECTED_STATE;
case ErrorAction::kFatal:
return user_data_auth::PossibleAction::POSSIBLY_FATAL;
default:
return std::nullopt;
}
}
// Retrieve the ErrorID (aka, the location) from the stack of errors.
// It looks something like this: 5-42-17
std::string ErrorIDFromStack(
const hwsec_foundation::status::StatusChain<CryptohomeError>& stack) {
std::string result;
for (const auto& err : stack.const_range()) {
if (!result.empty()) {
result += "-";
}
result += std::to_string(err->local_location());
}
return result;
}
// Retrieves the recommendation from cryptohome to the caller (Chromium).
// PrimaryAction means that cryptohome is certain that an action will resolve
// the issue, or there's a specific reason why it failed. PossibleAction means
// that cryptohome is uncertain if some actions would resolve the issue but it's
// worth a try anyway.
void ActionsFromStack(
const hwsec_foundation::status::StatusChain<CryptohomeError>& stack,
user_data_auth::PrimaryAction* primary,
std::set<user_data_auth::PossibleAction>* possible) {
// Check to see if we've any PrimaryAction in the stack.
*primary = user_data_auth::PrimaryAction::PRIMARY_NONE;
for (const auto& err : stack.const_range()) {
// NOTE(b/229708597) The underlying StatusChain will prohibit the iteration
// of the stack soon, and therefore other users of StatusChain should avoid
// iterating through the StatusChain without consulting the owner of the
// bug.
for (const auto& a : err->local_actions()) {
auto primary_result = ErrorActionToPrimaryAction(a);
if (primary_result.has_value()) {
// The recommended action is a PrimaryAction.
if (*primary != user_data_auth::PrimaryAction::PRIMARY_NONE) {
LOG(WARNING) << "Multiple PrimaryAction in an error, got: "
<< static_cast<int>(*primary) << " and "
<< static_cast<int>(*primary_result);
}
*primary = *primary_result;
}
// Obtain the possible actions while we're at it.
auto possible_result = ErrorActionToPossibleAction(a);
if (possible_result) {
possible->insert(*possible_result);
}
}
}
if (*primary != user_data_auth::PrimaryAction::PRIMARY_NONE) {
// If we are sure, we'll not propose actions that we're not certain about.
possible->clear();
return;
}
// If we get here, we're not sure about the failures.
// Since the PossibleAction(s) are populated as well, we can return.
return;
}
} // namespace
user_data_auth::CryptohomeErrorCode LegacyErrorCodeFromStack(
const hwsec_foundation::status::StatusChain<CryptohomeError>& stack) {
// Traverse down the stack for the first error
for (const auto& err : stack.const_range()) {
// NOTE(b/229708597) The underlying StatusChain will prohibit the iteration
// of the stack soon, and therefore other users of StatusChain should avoid
// iterating through the StatusChain without consulting the owner of the
// bug.
auto current_legacy_err = err->local_legacy_error();
if (current_legacy_err) {
return current_legacy_err.value();
}
}
// There's some form of an error because the original CryptohomeError is not
// nullptr, therefore, we should leave an unknown error here.
return user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_UNKNOWN_LEGACY;
}
user_data_auth::CryptohomeErrorInfo CryptohomeErrorToUserDataAuthError(
const hwsec_foundation::status::StatusChain<CryptohomeError>& err,
user_data_auth::CryptohomeErrorCode* legacy_ec) {
user_data_auth::CryptohomeErrorInfo result;
if (err.ok()) {
// No error.
result.set_primary_action(user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
if (legacy_ec) {
*legacy_ec =
user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET;
}
return result;
}
// Get the location and recommended actions.
result.set_error_id(ErrorIDFromStack(err));
user_data_auth::PrimaryAction primary;
std::set<user_data_auth::PossibleAction> possible;
ActionsFromStack(err, &primary, &possible);
result.set_primary_action(primary);
for (const auto& a : possible) {
result.add_possible_actions(a);
}
// Get the legacy CryptohomeErrorCode as well.
if (legacy_ec) {
*legacy_ec = LegacyErrorCodeFromStack(err);
if (*legacy_ec ==
user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_UNKNOWN_LEGACY) {
LOG(WARNING) << "No legacy error code in error stack for "
"CryptohomeErrorToUserDataAuthError: "
<< result.error_id();
}
}
return result;
}
template <typename ReplyType>
void ReplyWithError(
base::OnceCallback<void(const ReplyType&)> on_done,
const ReplyType& reply,
const hwsec_foundation::status::StatusChain<CryptohomeError>& err) {
bool success = err.ok();
ReplyType actual_reply;
// Unfortunately, we've to copy the reply protobuf, this is because the coding
// style demands that an input argument be const ref.
actual_reply.CopyFrom(reply);
if (!success) {
user_data_auth::CryptohomeErrorCode legacy_ec;
auto info = CryptohomeErrorToUserDataAuthError(err, &legacy_ec);
ReportCryptohomeError(err, info);
actual_reply.mutable_error_info()->CopyFrom(info);
actual_reply.set_error(legacy_ec);
} else {
actual_reply.clear_error_info();
actual_reply.set_error(
user_data_auth::CryptohomeErrorCode::CRYPTOHOME_ERROR_NOT_SET);
}
std::move(on_done).Run(actual_reply);
}
// Instantiate ReplyWithError and export them for types actually used.
template void ReplyWithError(
base::OnceCallback<void(const user_data_auth::MountReply&)> on_done,
const user_data_auth::MountReply& reply,
const hwsec_foundation::status::StatusChain<CryptohomeError>& err);
template void ReplyWithError(
base::OnceCallback<void(const user_data_auth::StartAuthSessionReply&)>
on_done,
const user_data_auth::StartAuthSessionReply& reply,
const hwsec_foundation::status::StatusChain<CryptohomeError>& err);
template void ReplyWithError(
base::OnceCallback<
void(const user_data_auth::AuthenticateAuthSessionReply&)> on_done,
const user_data_auth::AuthenticateAuthSessionReply& reply,
const hwsec_foundation::status::StatusChain<CryptohomeError>& err);
template void ReplyWithError(
base::OnceCallback<void(const user_data_auth::InvalidateAuthSessionReply&)>
on_done,
const user_data_auth::InvalidateAuthSessionReply& reply,
const hwsec_foundation::status::StatusChain<CryptohomeError>& err);
template void ReplyWithError(
base::OnceCallback<void(const user_data_auth::ExtendAuthSessionReply&)>
on_done,
const user_data_auth::ExtendAuthSessionReply& reply,
const hwsec_foundation::status::StatusChain<CryptohomeError>& err);
template void ReplyWithError(
base::OnceCallback<void(const user_data_auth::CreatePersistentUserReply&)>
on_done,
const user_data_auth::CreatePersistentUserReply& reply,
const hwsec_foundation::status::StatusChain<CryptohomeError>& err);
template void ReplyWithError(
base::OnceCallback<void(const user_data_auth::AuthenticateAuthFactorReply&)>
on_done,
const user_data_auth::AuthenticateAuthFactorReply& reply,
const hwsec_foundation::status::StatusChain<CryptohomeError>& err);
template void ReplyWithError(
base::OnceCallback<void(const user_data_auth::AddCredentialsReply&)>
on_done,
const user_data_auth::AddCredentialsReply& reply,
const hwsec_foundation::status::StatusChain<CryptohomeError>& err);
template void ReplyWithError(
base::OnceCallback<void(const user_data_auth::UpdateCredentialReply&)>
on_done,
const user_data_auth::UpdateCredentialReply& reply,
const hwsec_foundation::status::StatusChain<CryptohomeError>& err);
} // namespace error
} // namespace cryptohome