chromium/chrome/browser/ash/policy/enrollment/auto_enrollment_state_message_processor.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/policy/enrollment/auto_enrollment_state_message_processor.h"

#include <memory>
#include <optional>
#include <string>

#include "base/logging.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/ash/policy/server_backed_state/server_backed_device_state.h"
#include "components/policy/proto/device_management_backend.pb.h"

// TODO(crbug.com/40805389): Logging as "WARNING" throughout the file to make
// sure it's preserved in the logs.

namespace policy {

namespace {

namespace em = ::enterprise_management;

// Converts a restore mode enum value from the DM protocol for FRE into the
// corresponding prefs string constant.
std::string ConvertRestoreMode(
    em::DeviceStateRetrievalResponse::RestoreMode restore_mode) {
  switch (restore_mode) {
    case em::DeviceStateRetrievalResponse::RESTORE_MODE_NONE:
      return std::string();
    case em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_REQUESTED:
      return kDeviceStateRestoreModeReEnrollmentRequested;
    case em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ENFORCED:
      return kDeviceStateRestoreModeReEnrollmentEnforced;
    case em::DeviceStateRetrievalResponse::RESTORE_MODE_DISABLED:
      return kDeviceStateModeDisabled;
    case em::DeviceStateRetrievalResponse::RESTORE_MODE_REENROLLMENT_ZERO_TOUCH:
      return kDeviceStateRestoreModeReEnrollmentZeroTouch;
  }
}

// Converts a enterprise_management::LicenseType_LicenseTypeEnum
// for AutoEnrollment to it corresponding string.
std::string ConvertAutoEnrollmentLicenseType(
    ::enterprise_management::LicenseType_LicenseTypeEnum license_type) {
  switch (license_type) {
    case em::LicenseType::UNDEFINED:
      return std::string();
    case em::LicenseType::CDM_PERPETUAL:
      return kDeviceStateLicenseTypeEnterprise;
    case em::LicenseType::CDM_ANNUAL:
      return kDeviceStateLicenseTypeEnterprise;
    case em::LicenseType::KIOSK:
      return kDeviceStateLicenseTypeTerminal;
    case em::LicenseType::CDM_PACKAGED:
      return kDeviceStateLicenseTypeEnterprise;
  }
}

// Converts an initial enrollment mode enum value from the DM protocol for
// initial enrollment into the corresponding prefs string constant.
std::string ConvertInitialEnrollmentMode(
    em::DeviceInitialEnrollmentStateResponse::InitialEnrollmentMode
        initial_enrollment_mode) {
  switch (initial_enrollment_mode) {
    case em::DeviceInitialEnrollmentStateResponse::INITIAL_ENROLLMENT_MODE_NONE:
      return std::string();
    case em::DeviceInitialEnrollmentStateResponse::
        INITIAL_ENROLLMENT_MODE_ENROLLMENT_ENFORCED:
      return kDeviceStateInitialModeEnrollmentEnforced;
    case em::DeviceInitialEnrollmentStateResponse::
        INITIAL_ENROLLMENT_MODE_ZERO_TOUCH_ENFORCED:
      return kDeviceStateInitialModeEnrollmentZeroTouch;
    case em::DeviceInitialEnrollmentStateResponse::
        INITIAL_ENROLLMENT_MODE_DISABLED:
      return kDeviceStateModeDisabled;
    case em::DeviceInitialEnrollmentStateResponse::
        INITIAL_ENROLLMENT_MODE_TOKEN_ENROLLMENT_ENFORCED:
      return kDeviceStateInitialModeTokenEnrollment;
  }
}

// Converts an assigned upgrade type enum value from the DM protocol for
// initial enrollment into the corresponding prefs string constant.
std::string ConvertAssignedUpgradeType(
    em::DeviceInitialEnrollmentStateResponse::AssignedUpgradeType
        assigned_upgrade_type) {
  switch (assigned_upgrade_type) {
    case em::DeviceInitialEnrollmentStateResponse::
        ASSIGNED_UPGRADE_TYPE_UNSPECIFIED:
      return std::string();
    case em::DeviceInitialEnrollmentStateResponse::
        ASSIGNED_UPGRADE_TYPE_CHROME_ENTERPRISE:
      return kDeviceStateAssignedUpgradeTypeChromeEnterprise;
    case em::DeviceInitialEnrollmentStateResponse::
        ASSIGNED_UPGRADE_TYPE_KIOSK_AND_SIGNAGE:
      return kDeviceStateAssignedUpgradeTypeKiosk;
  }
}

// Converts a license packaging sku enum value from the DM protocol for initial
// enrollment into the corresponding prefs string constant.
std::string ConvertLicenseType(
    em::DeviceInitialEnrollmentStateResponse::LicensePackagingSKU license_sku) {
  switch (license_sku) {
    case em::DeviceInitialEnrollmentStateResponse::NOT_EXIST:
      return std::string();
    case em::DeviceInitialEnrollmentStateResponse::CHROME_ENTERPRISE:
      return kDeviceStateLicenseTypeEnterprise;
    case em::DeviceInitialEnrollmentStateResponse::CHROME_EDUCATION:
      return kDeviceStateLicenseTypeEducation;
    case em::DeviceInitialEnrollmentStateResponse::CHROME_TERMINAL:
      return kDeviceStateLicenseTypeTerminal;
  }
}

}  // namespace

// Generates a request to download the device state during Initial Enrollment.
class InitialEnrollmentStateMessageProcessor
    : public AutoEnrollmentStateMessageProcessor {
 public:
  InitialEnrollmentStateMessageProcessor(
      const std::string& device_serial_number,
      const std::string& device_brand_code,
      const std::optional<std::string> flex_enrollment_token)
      : device_serial_number_(device_serial_number),
        device_brand_code_(device_brand_code),
        flex_enrollment_token_(std::move(flex_enrollment_token)) {}

  DeviceManagementService::JobConfiguration::JobType GetJobType()
      const override {
    return DeviceManagementService::JobConfiguration::
        TYPE_INITIAL_ENROLLMENT_STATE_RETRIEVAL;
  }

  void FillRequest(em::DeviceManagementRequest* request) override {
    auto* inner_request =
        request->mutable_device_initial_enrollment_state_request();
    inner_request->set_brand_code(device_brand_code_);
    inner_request->set_serial_number(device_serial_number_);
    if (flex_enrollment_token_.has_value()) {
      inner_request->set_enrollment_token(flex_enrollment_token_.value());
    }
  }

  std::optional<ParsedResponse> ParseResponse(
      const em::DeviceManagementResponse& response) override {
    if (!response.has_device_initial_enrollment_state_response()) {
      LOG(ERROR) << "Server failed to provide initial enrollment response.";
      return std::nullopt;
    }

    return ParseInitialEnrollmentStateResponse(
        response.device_initial_enrollment_state_response());
  }

  static std::optional<ParsedResponse> ParseInitialEnrollmentStateResponse(
      const em::DeviceInitialEnrollmentStateResponse& state_response) {
    ParsedResponse parsed_response;

    if (state_response.has_initial_enrollment_mode()) {
      parsed_response.restore_mode = ConvertInitialEnrollmentMode(
          state_response.initial_enrollment_mode());
    } else {
      // Unknown initial enrollment mode - treat as no enrollment.
      parsed_response.restore_mode.clear();
    }

    if (state_response.has_management_domain())
      parsed_response.management_domain = state_response.management_domain();

    if (state_response.has_is_license_packaged_with_device()) {
      parsed_response.is_license_packaged_with_device =
          state_response.is_license_packaged_with_device();
    }

    if (state_response.has_license_packaging_sku()) {
      parsed_response.license_type =
          ConvertLicenseType(state_response.license_packaging_sku());
    }

    if (state_response.has_assigned_upgrade_type()) {
      parsed_response.assigned_upgrade_type =
          ConvertAssignedUpgradeType(state_response.assigned_upgrade_type());
    }

    if (state_response.has_disabled_state()) {
      parsed_response.disabled_message =
          state_response.disabled_state().message();
    }

    LOG(WARNING) << "Received initial_enrollment_mode="
                 << state_response.initial_enrollment_mode() << " ("
                 << parsed_response.restore_mode << "). ";

    LOG(WARNING) << (state_response.is_license_packaged_with_device()
                         ? "Device has a packaged license for management."
                         : "No packaged license. ");

    LOG(WARNING) << (state_response.has_assigned_upgrade_type()
                         ? base::StrCat(
                               {"Assigned upgrade type=",
                                base::NumberToString(
                                    state_response.assigned_upgrade_type()),
                                " (",
                                parsed_response.assigned_upgrade_type.value_or(
                                    std::string()),
                                ")."})
                         : "No assigned upgrade type.");

    return parsed_response;
  }

 private:
  // Serial number of the device.
  std::string device_serial_number_;
  // 4-character brand code of the device.
  std::string device_brand_code_;

  const std::optional<std::string> flex_enrollment_token_;
};

// Generates a request to download the device state during Forced Re-Enrollment
// (FRE).
class FREStateMessageProcessor : public AutoEnrollmentStateMessageProcessor {
 public:
  explicit FREStateMessageProcessor(const std::string& server_backed_state_key)
      : server_backed_state_key_(server_backed_state_key) {}

  DeviceManagementService::JobConfiguration::JobType GetJobType()
      const override {
    return DeviceManagementService::JobConfiguration::
        TYPE_DEVICE_STATE_RETRIEVAL;
  }

  void FillRequest(em::DeviceManagementRequest* request) override {
    request->mutable_device_state_retrieval_request()
        ->set_server_backed_state_key(server_backed_state_key_);
  }

  std::optional<ParsedResponse> ParseResponse(
      const em::DeviceManagementResponse& response) override {
    if (!response.has_device_state_retrieval_response()) {
      LOG(ERROR) << "Server failed to provide auto-enrollment response.";
      return std::nullopt;
    }

    const em::DeviceStateRetrievalResponse& state_response =
        response.device_state_retrieval_response();
    const auto restore_mode = state_response.restore_mode();

    if (restore_mode == em::DeviceStateRetrievalResponse::RESTORE_MODE_NONE &&
        state_response.has_initial_state_response()) {
      LOG(WARNING) << "Received restore_mode=" << restore_mode << " ("
                   << ConvertRestoreMode(restore_mode) << ")"
                   << " . Parsing included initial state response.";

      return InitialEnrollmentStateMessageProcessor::
          ParseInitialEnrollmentStateResponse(
              state_response.initial_state_response());
    } else {
      ParsedResponse parsed_response;

      parsed_response.restore_mode = ConvertRestoreMode(restore_mode);

      if (state_response.has_management_domain())
        parsed_response.management_domain = state_response.management_domain();

      if (state_response.has_disabled_state()) {
        parsed_response.disabled_message =
            state_response.disabled_state().message();
      }

      // Package license is not available during the re-enrollment
      parsed_response.is_license_packaged_with_device.reset();

      if (state_response.has_license_type()) {
        parsed_response.license_type = ConvertAutoEnrollmentLicenseType(
            state_response.license_type().license_type());
      }

      LOG(WARNING) << "Received restore_mode=" << restore_mode << " ("
                   << parsed_response.restore_mode << ").";

      return parsed_response;
    }
  }

 private:
  std::string server_backed_state_key_;
};

AutoEnrollmentStateMessageProcessor::ParsedResponse::ParsedResponse() = default;

AutoEnrollmentStateMessageProcessor::ParsedResponse::~ParsedResponse() =
    default;

AutoEnrollmentStateMessageProcessor::ParsedResponse::ParsedResponse(
    const ParsedResponse&) = default;

AutoEnrollmentStateMessageProcessor::~AutoEnrollmentStateMessageProcessor() =
    default;

// static
std::unique_ptr<AutoEnrollmentStateMessageProcessor>
AutoEnrollmentStateMessageProcessor::CreateForFRE(
    const std::string& server_backed_state_key) {
  return std::make_unique<FREStateMessageProcessor>(server_backed_state_key);
}

// static
std::unique_ptr<AutoEnrollmentStateMessageProcessor>
AutoEnrollmentStateMessageProcessor::CreateForInitialEnrollment(
    const std::string& device_serial_number,
    const std::string& device_brand_code,
    std::optional<std::string> flex_enrollment_token) {
  return std::make_unique<InitialEnrollmentStateMessageProcessor>(
      device_serial_number, device_brand_code,
      std::move(flex_enrollment_token));
}

}  // namespace policy