chromium/chromeos/ash/components/install_attributes/install_attributes.h

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

#ifndef CHROMEOS_ASH_COMPONENTS_INSTALL_ATTRIBUTES_INSTALL_ATTRIBUTES_H_
#define CHROMEOS_ASH_COMPONENTS_INSTALL_ATTRIBUTES_INSTALL_ATTRIBUTES_H_

#include <map>
#include <optional>
#include <string>

#include "base/compiler_specific.h"
#include "base/component_export.h"
#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/ash/components/dbus/device_management/install_attributes_client.h"
#include "chromeos/dbus/tpm_manager/tpm_manager.pb.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"

namespace ash {

// Brokers access to the installation-time attributes on Chrome OS.  When
// initialized with kInstallAttributesFileName, the attributes are fully trusted
// (signature has been verified by lockbox-cache).
class COMPONENT_EXPORT(ASH_INSTALL_ATTRIBUTES) InstallAttributes {
 public:
  // InstallAttributes status codes.  Do not change the numeric ids or the
  // meaning of the existing codes to preserve the interpretability of old
  // logfiles.
  enum LockResult {
    LOCK_SUCCESS = 0,          // Success.
    LOCK_NOT_READY = 1,        // Backend/TPM still initializing.
    LOCK_TIMEOUT = 2,          // Backend/TPM timed out.
    LOCK_BACKEND_INVALID = 3,  // Backend failed to initialize.
    LOCK_ALREADY_LOCKED = 4,   // TPM has already been locked.
    LOCK_SET_ERROR = 5,        // Failed to set attributes.
    LOCK_FINALIZE_ERROR = 6,   // Backend failed to lock.
    LOCK_READBACK_ERROR = 7,   // Inconsistency reading back registration data.
    LOCK_WRONG_DOMAIN = 8,     // Device already registered to another domain or
                               // other mismatch of other attributes.
    LOCK_WRONG_MODE = 9,       // Device already locked to a different mode.
  };

  // A callback to handle responses of methods returning a LockResult value.
  using LockResultCallback = base::OnceCallback<void(LockResult lock_result)>;

  // Manage singleton instance.
  static void Initialize();
  static bool IsInitialized();
  static void Shutdown();
  static InstallAttributes* Get();

  // Sets the singleton to |test_instance|. Does not take ownership of the
  // instance. Should be matched with a call to |ShutdownForTesting| once the
  // test is finished and before the instance is deleted.
  static void SetForTesting(InstallAttributes* test_instance);
  static void ShutdownForTesting();

  explicit InstallAttributes(
      InstallAttributesClient* install_attributes_client);

  InstallAttributes(const InstallAttributes&) = delete;
  InstallAttributes& operator=(const InstallAttributes&) = delete;

  ~InstallAttributes();

  // Tries to read install attributes from |cache_file| to work around slow
  // cryptohome startup which takes a while to register its D-Bus interface.
  // (See http://crosbug.com/37367 for background on this.)  When called with
  // kInstallAttributesFileName (created early during the boot process by
  // lockbox-cache) the install attributes are fully trusted.
  void Init(const base::FilePath& cache_file);

  // Makes sure the local caches for enterprise-related install attributes are
  // up to date with what cryptohome has. This method checks the readiness of
  // attributes and read them if ready. Actual read will be performed in
  // ReadAttributesIfReady().
  void ReadImmutableAttributes(base::OnceClosure callback);

  // Updates the firmware management parameters from TPM, storing the devmode
  // flag according to |block_devmode|. Invokes |callback| when done. Must be
  // called before LockDevice is done. Used to update TPM on enrollment.
  void SetBlockDevmodeInTpm(
      bool block_devmode,
      chromeos::DBusMethodCallback<
          device_management::SetFirmwareManagementParametersReply> callback);

  // Locks the device into |device_mode|.  Depending on |device_mode|, a
  // specific subset of |domain|, |realm| and |device_id| must be set.  Can also
  // be called after the lock has already been taken, in which case it checks
  // that the passed parameters fully agree with the locked attributes.
  // |callback| must not be null and is called with the result.  Must not be
  // called while a previous LockDevice() invocation is still pending.
  void LockDevice(policy::DeviceMode device_mode,
                  const std::string& domain,
                  const std::string& realm,
                  const std::string& device_id,
                  LockResultCallback callback);

  // Checks whether this devices is under any kind of enterprise management.
  bool IsEnterpriseManaged() const;

  // Checks whether this is a cloud (DM server) managed enterprise device.
  bool IsCloudManaged() const;

  // Whether the device is set up to run demo sessions.
  bool IsDeviceInDemoMode() const;

  // Return the mode the device was enrolled to. The return value for devices
  // that are not locked yet is DEVICE_MODE_UNKNOWN.
  policy::DeviceMode GetMode() const { return registration_mode_; }

  // Return the domain this device belongs to or an empty string if the device
  // is not a cloud-managed enterprise device.
  std::string GetDomain() const { return registration_domain_; }

  // Return the realm this device belongs to or an empty string if the device is
  // not an AD enterprise device.
  std::string GetRealm() const { return registration_realm_; }

  // Return the device id that was generated when the device was registered.
  // Returns an empty string if the device is not an enterprise device or the
  // device id was not stored in the lockbox (prior to R19).
  std::string GetDeviceId() const { return registration_device_id_; }

  // Return whether TPM is locked.
  bool IsDeviceLocked() const { return device_locked_; }

 protected:
  // True if install attributes have been read successfully.  False if read
  // failed or no read attempt was made.
  bool device_locked_ = false;

  // Whether the TPM / install attributes consistency check is running.
  bool consistency_check_running_ = false;

  // To be run after the consistency check has finished.
  base::OnceClosure post_check_action_;

  // Wether the LockDevice() initiated TPM calls are running.
  bool device_lock_running_ = false;

  // The actual install attributes.  Populated by DecodeInstallAttributes()
  // exclusively.
  policy::DeviceMode registration_mode_ = policy::DEVICE_MODE_PENDING;
  std::string registration_domain_;
  std::string registration_realm_;
  std::string registration_device_id_;

 private:
  FRIEND_TEST_ALL_PREFIXES(InstallAttributesTest, DeviceLockedFromOlderVersion);
  FRIEND_TEST_ALL_PREFIXES(InstallAttributesTest, Init);
  FRIEND_TEST_ALL_PREFIXES(InstallAttributesTest, InitForConsumerKiosk);
  FRIEND_TEST_ALL_PREFIXES(InstallAttributesTest, InitForEnterpriseDemo);
  FRIEND_TEST_ALL_PREFIXES(InstallAttributesTest, LockCanonicalize);
  FRIEND_TEST_ALL_PREFIXES(InstallAttributesTest,
                           VerifyFakeInstallAttributesCache);

  // Constants for the possible device modes that can be stored in the lockbox.
  static const char kConsumerDeviceMode[];
  static const char kEnterpriseDeviceMode[];
  static const char kLegacyRetailDeviceMode[];
  static const char kLegacyConsumerKioskDeviceMode[];
  static const char kDemoDeviceMode[];

  // Field names in the lockbox.
  static const char kAttrEnterpriseDeviceId[];
  static const char kAttrEnterpriseDomain[];
  static const char kAttrEnterpriseRealm[];
  static const char kAttrEnterpriseMode[];
  static const char kAttrEnterpriseOwned[];
  static const char kAttrEnterpriseUser[];
  static const char kAttrConsumerKioskEnabled[];

  // Called by |cryptohome_client_| when the cryptohome service becomes
  // initially available over D-Bus.
  void OnCryptohomeServiceInitiallyAvailable(bool service_is_ready);

  // Translates DeviceMode constants to strings used in the lockbox.
  std::string GetDeviceModeString(policy::DeviceMode mode);

  // Translates strings used in the lockbox to DeviceMode values.
  policy::DeviceMode GetDeviceModeFromString(const std::string& mode);

  // Decode the install attributes provided in |attr_map| (including some
  // normalization and processing for backward compatibility) and guarantee that
  // |registration_*| members are set self-consistently.
  void DecodeInstallAttributes(
      const std::map<std::string, std::string>& attr_map);

  // Helper for ReadImmutableAttributes.
  void ReadAttributesIfReady(
      base::OnceClosure callback,
      std::optional<device_management::InstallAttributesGetStatusReply> reply);

  // Helper for LockDevice(). Handles the result of InstallAttributesIsReady()
  // and continue processing LockDevice if the result is true.
  void LockDeviceIfAttributesIsReady(
      policy::DeviceMode device_mode,
      const std::string& domain,
      const std::string& realm,
      const std::string& device_id,
      LockResultCallback callback,
      std::optional<device_management::InstallAttributesGetStatusReply> reply);

  // Confirms the registered user and invoke the callback.
  void OnReadImmutableAttributes(policy::DeviceMode mode,
                                 const std::string& domain,
                                 const std::string& realm,
                                 const std::string& device_id,
                                 LockResultCallback callback);

  // Check state of install attributes against TPM lock state and generate UMA
  // for the result.  Asynchronously retry |dbus_retries| times in case of DBUS
  // errors (cryptohomed startup is slow).
  void TriggerConsistencyCheck(int dbus_retries);

  // Callback for `GetTpmNonsensitiveStatus()` D-Bus call. Generates UMA or
  // schedules retry in case of DBUS error.
  void OnTpmStatusComplete(
      int dbus_retries_remaining,
      const ::tpm_manager::GetTpmNonsensitiveStatusReply& reply);

  // Callback for `ClearStoredOwnerPassword()` D-Bus call. Logs status code if
  // the call fails.
  void OnClearStoredOwnerPassword(
      const ::tpm_manager::ClearStoredOwnerPasswordReply& reply);

  raw_ptr<InstallAttributesClient, DanglingUntriaged>
      install_attributes_client_;

  base::WeakPtrFactory<InstallAttributes> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // CHROMEOS_ASH_COMPONENTS_INSTALL_ATTRIBUTES_INSTALL_ATTRIBUTES_H_