| // Copyright 2021 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 "rmad/state_handler/device_destination_state_handler.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/logging.h> |
| |
| #include "rmad/constants.h" |
| #include "rmad/metrics/metrics_constants.h" |
| #include "rmad/proto_bindings/rmad.pb.h" |
| #include "rmad/system/cryptohome_client_impl.h" |
| #include "rmad/system/fake_cryptohome_client.h" |
| #include "rmad/utils/cr50_utils_impl.h" |
| #include "rmad/utils/crossystem_utils_impl.h" |
| #include "rmad/utils/dbus_utils.h" |
| #include "rmad/utils/fake_cr50_utils.h" |
| #include "rmad/utils/fake_crossystem_utils.h" |
| |
| namespace rmad { |
| |
| using ComponentRepairStatus = ComponentsRepairState::ComponentRepairStatus; |
| |
| namespace fake { |
| |
| FakeDeviceDestinationStateHandler::FakeDeviceDestinationStateHandler( |
| scoped_refptr<JsonStore> json_store, const base::FilePath& working_dir_path) |
| : DeviceDestinationStateHandler( |
| json_store, |
| std::make_unique<FakeCryptohomeClient>(working_dir_path), |
| std::make_unique<FakeCr50Utils>(working_dir_path), |
| std::make_unique<FakeCrosSystemUtils>(working_dir_path)) {} |
| |
| } // namespace fake |
| |
| DeviceDestinationStateHandler::DeviceDestinationStateHandler( |
| scoped_refptr<JsonStore> json_store) |
| : BaseStateHandler(json_store) { |
| cryptohome_client_ = std::make_unique<CryptohomeClientImpl>(GetSystemBus()); |
| cr50_utils_ = std::make_unique<Cr50UtilsImpl>(); |
| crossystem_utils_ = std::make_unique<CrosSystemUtilsImpl>(); |
| } |
| |
| DeviceDestinationStateHandler::DeviceDestinationStateHandler( |
| scoped_refptr<JsonStore> json_store, |
| std::unique_ptr<CryptohomeClient> cryptohome_client, |
| std::unique_ptr<Cr50Utils> cr50_utils, |
| std::unique_ptr<CrosSystemUtils> crossystem_utils) |
| : BaseStateHandler(json_store), |
| cryptohome_client_(std::move(cryptohome_client)), |
| cr50_utils_(std::move(cr50_utils)), |
| crossystem_utils_(std::move(crossystem_utils)) {} |
| |
| RmadErrorCode DeviceDestinationStateHandler::InitializeState() { |
| if (!state_.has_device_destination()) { |
| state_.set_allocated_device_destination(new DeviceDestinationState); |
| } |
| return RMAD_ERROR_OK; |
| } |
| |
| BaseStateHandler::GetNextStateCaseReply |
| DeviceDestinationStateHandler::GetNextStateCase(const RmadState& state) { |
| if (!state.has_device_destination()) { |
| LOG(ERROR) << "RmadState missing |device destination| state."; |
| return NextStateCaseWrapper(RMAD_ERROR_REQUEST_INVALID); |
| } |
| const DeviceDestinationState& device_destination = state.device_destination(); |
| if (device_destination.destination() == |
| DeviceDestinationState::RMAD_DESTINATION_UNKNOWN) { |
| LOG(ERROR) << "RmadState missing |destination| argument."; |
| return NextStateCaseWrapper(RMAD_ERROR_REQUEST_ARGS_MISSING); |
| } |
| |
| state_ = state; |
| |
| // There are 5 paths: |
| // 1. Same owner + replaced components require WP disabling + CCD blocked: |
| // Go to WipeSelection state. |
| // 2. Same owner + replaced components require WP disabling + CCD not blocked: |
| // Go to WipeSelection state. |
| // 3. Same owner + replaced components don't require WP disabling: |
| // Go to WipeSelection state. |
| // 4. Different owner + CCD blocked: |
| // Mandatory RSU. Go straight to RSU state. |
| // 5. Different owner + CCD not blocked: |
| // Must wipe the device. Skip WipeSelection state and go to WpDisableMethod |
| // state. |
| // We should set the following values to json_store: |
| // - kSameOwner |
| // - kWpDisableRequired |
| // - kCcdBlocked (only required when kWpDisableRequired is true) |
| if (device_destination.destination() == |
| DeviceDestinationState::RMAD_DESTINATION_SAME) { |
| json_store_->SetValue(kSameOwner, true); |
| if (ReplacedComponentNeedHwwpDisabled()) { |
| json_store_->SetValue(kWpDisableRequired, true); |
| if (cryptohome_client_->IsCcdBlocked()) { |
| // Case 1. |
| json_store_->SetValue(kCcdBlocked, true); |
| return NextStateCaseWrapper(RmadState::StateCase::kWipeSelection); |
| } else { |
| // Case 2. |
| json_store_->SetValue(kCcdBlocked, false); |
| return NextStateCaseWrapper(RmadState::StateCase::kWipeSelection); |
| } |
| } else { |
| // Case 3. |
| json_store_->SetValue(kWpDisableRequired, false); |
| return NextStateCaseWrapper(RmadState::StateCase::kWipeSelection); |
| } |
| } else { |
| json_store_->SetValue(kSameOwner, false); |
| json_store_->SetValue(kWpDisableRequired, true); |
| json_store_->SetValue(kWipeDevice, true); |
| if (cryptohome_client_->IsCcdBlocked()) { |
| // Case 4. |
| json_store_->SetValue(kCcdBlocked, true); |
| return NextStateCaseWrapper(RmadState::StateCase::kWpDisableRsu); |
| } else { |
| // Case 5. |
| // If factory mode is already enabled, go directly to WpDisableComplete |
| // state. |
| json_store_->SetValue(kCcdBlocked, false); |
| if (cr50_utils_->IsFactoryModeEnabled()) { |
| json_store_->SetValue(kWpDisableSkipped, true); |
| json_store_->SetValue( |
| kWriteProtectDisableMethod, |
| static_cast<int>(WriteProtectDisableMethod::SKIPPED)); |
| return NextStateCaseWrapper(RmadState::StateCase::kWpDisableComplete); |
| } |
| // If HWWP is already disabled, assume the user will select the physical |
| // method and go directly to WpDisablePhysical state. |
| if (int hwwp_status; |
| crossystem_utils_->GetHwwpStatus(&hwwp_status) && hwwp_status == 0) { |
| return NextStateCaseWrapper(RmadState::StateCase::kWpDisablePhysical); |
| } |
| // Otherwise, let the user choose between physical method or RSU. |
| return NextStateCaseWrapper(RmadState::StateCase::kWpDisableMethod); |
| } |
| } |
| } |
| |
| bool DeviceDestinationStateHandler::ReplacedComponentNeedHwwpDisabled() const { |
| std::vector<std::string> replaced_component_names; |
| if (!json_store_->GetValue(kReplacedComponentNames, |
| &replaced_component_names)) { |
| return false; |
| } |
| // Using set union/intersection is faster, but since the number of components |
| // is small, we keep it simple by using linear searches for each replaced |
| // component. It shouldn't affect performance a lot. |
| for (const std::string& component_name : replaced_component_names) { |
| RmadComponent component; |
| CHECK(RmadComponent_Parse(component_name, &component)); |
| if (std::find(kComponentsNeedManualCalibration.begin(), |
| kComponentsNeedManualCalibration.end(), |
| component) != kComponentsNeedManualCalibration.end()) { |
| return true; |
| } |
| if (std::find(kComponentsNeedUpdateCbi.begin(), |
| kComponentsNeedUpdateCbi.end(), |
| component) != kComponentsNeedUpdateCbi.end()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace rmad |