chromium/chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.cc

// 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.

#include "chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.h"

#include <stddef.h>

#include <memory>
#include <utility>

#include "ash/constants/ash_paths.h"
#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/path_service.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "chrome/browser/ash/attestation/enrollment_certificate_uploader_impl.h"
#include "chrome/browser/ash/attestation/enrollment_id_upload_manager.h"
#include "chrome/browser/ash/attestation/machine_certificate_uploader_impl.h"
#include "chrome/browser/ash/login/reporting/lock_unlock_reporter.h"
#include "chrome/browser/ash/login/reporting/login_logout_reporter.h"
#include "chrome/browser/ash/policy/core/device_cloud_policy_store_ash.h"
#include "chrome/browser/ash/policy/core/policy_pref_names.h"
#include "chrome/browser/ash/policy/core/reporting_user_tracker.h"
#include "chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.h"
#include "chrome/browser/ash/policy/networking/euicc_status_uploader.h"
#include "chrome/browser/ash/policy/remote_commands/crd/crd_admin_session_controller.h"
#include "chrome/browser/ash/policy/remote_commands/device_commands_factory_ash.h"
#include "chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h"
#include "chrome/browser/ash/policy/reporting/os_updates/os_updates_reporter.h"
#include "chrome/browser/ash/policy/reporting/user_added_removed/user_added_removed_reporter.h"
#include "chrome/browser/ash/policy/rsu/lookup_key_uploader.h"
#include "chrome/browser/ash/policy/server_backed_state/server_backed_state_keys_broker.h"
#include "chrome/browser/ash/policy/status_collector/device_status_collector.h"
#include "chrome/browser/ash/policy/status_collector/managed_session_service.h"
#include "chrome/browser/ash/policy/uploading/heartbeat_scheduler.h"
#include "chrome/browser/ash/policy/uploading/status_uploader.h"
#include "chrome/browser/ash/policy/uploading/system_log_uploader.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/components/install_attributes/install_attributes.h"
#include "chromeos/ash/components/system/statistics_provider.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/policy/core/common/cloud/cloud_policy_core.h"
#include "components/policy/core/common/cloud/cloud_policy_service.h"
#include "components/policy/core/common/cloud/cloud_policy_store.h"
#include "components/policy/core/common/cloud/policy_invalidation_scope.h"
#include "components/policy/core/common/remote_commands/remote_commands_factory.h"
#include "components/policy/core/common/schema_registry.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/network_service_instance.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"

namespace policy {

namespace {

// Default frequency for uploading enterprise status reports. Can be overriden
// by Device Policy.
// Keep the default value in sync with device_status_frequency in
// DeviceReportingProto in components/policy/proto/chrome_device_policy.proto.
constexpr base::TimeDelta kDeviceStatusUploadFrequency = base::Hours(3);

}  // namespace

DeviceCloudPolicyManagerAsh::DeviceCloudPolicyManagerAsh(
    std::unique_ptr<DeviceCloudPolicyStoreAsh> device_store,
    std::unique_ptr<CloudExternalDataManager> external_data_manager,
    const scoped_refptr<base::SequencedTaskRunner>& task_runner,
    ServerBackedStateKeysBroker* state_keys_broker,
    StartCrdSessionJobDelegate& crd_delegate)
    : CloudPolicyManager(
          dm_protocol::kChromeDevicePolicyType,
          std::string(),
          std::move(device_store),
          task_runner,
          base::BindRepeating(&content::GetNetworkConnectionTracker)),
      device_store_(static_cast<DeviceCloudPolicyStoreAsh*>(store())),
      external_data_manager_(std::move(external_data_manager)),
      state_keys_broker_(state_keys_broker),
      crd_delegate_(&crd_delegate),
      task_runner_(task_runner),
      local_state_(nullptr),
      helper_(std::make_unique<reporting::UserEventReporterHelper>(
          reporting::Destination::UNDEFINED_DESTINATION)) {}

DeviceCloudPolicyManagerAsh::~DeviceCloudPolicyManagerAsh() = default;

void DeviceCloudPolicyManagerAsh::Init(SchemaRegistry* registry) {
  ConfigurationPolicyProvider::Init(registry);

  store()->AddObserver(this);

  // If the underlying store is already initialized, pretend it was loaded now.
  // Note: It is not enough to just copy OnStoreLoaded's contents here because
  // subclasses can override it.
  if (store()->is_initialized()) {
    OnStoreLoaded(store());
  }
}

void DeviceCloudPolicyManagerAsh::Initialize(PrefService* local_state) {
  CHECK(local_state);

  local_state_ = local_state;

  // If FRE is enabled, we'll want to know about re-enrollment state keys.
  if (AutoEnrollmentTypeChecker::IsFREEnabled()) {
    state_keys_update_subscription_ =
        state_keys_broker_->RegisterUpdateCallback(base::BindRepeating(
            &DeviceCloudPolicyManagerAsh::OnStateKeysUpdated,
            base::Unretained(this)));
  }
}

void DeviceCloudPolicyManagerAsh::AddDeviceCloudPolicyManagerObserver(
    Observer* observer) {
  observers_.AddObserver(observer);
}

void DeviceCloudPolicyManagerAsh::RemoveDeviceCloudPolicyManagerObserver(
    Observer* observer) {
  observers_.RemoveObserver(observer);
}

// Keep clean up order as the reversed creation order.
void DeviceCloudPolicyManagerAsh::Shutdown() {
  os_updates_reporter_.reset();
  metric_reporting_manager_.reset();
  lock_unlock_reporter_.reset();
  login_logout_reporter_.reset();
  user_added_removed_reporter_.reset();
  heartbeat_scheduler_.reset();
  syslog_uploader_.reset();
  status_uploader_.reset();
  crd_delegate_ = nullptr;
  state_keys_broker_ = nullptr;
  managed_session_service_.reset();
  euicc_status_uploader_.reset();
  core()->Disconnect();
  machine_certificate_uploader_.reset();
  external_data_manager_->Disconnect();
  state_keys_update_subscription_ = {};
  CloudPolicyManager::Shutdown();
  signin_profile_forwarding_schema_registry_.reset();
}

// static
void DeviceCloudPolicyManagerAsh::RegisterPrefs(PrefRegistrySimple* registry) {
  ReportingUserTracker::RegisterPrefs(registry);

  registry->RegisterDictionaryPref(::prefs::kServerBackedDeviceState);
  registry->RegisterBooleanPref(::prefs::kRemoveUsersRemoteCommand, false);
  registry->RegisterStringPref(::prefs::kLastRsuDeviceIdUploaded,
                               std::string());
  registry->RegisterListPref(prefs::kStoreLogStatesAcrossReboots);
}

void DeviceCloudPolicyManagerAsh::StartConnection(
    std::unique_ptr<CloudPolicyClient> client_to_connect,
    ash::InstallAttributes* install_attributes) {
  CHECK(!service());

  // Set state keys here so the first policy fetch submits them to the server.
  if (AutoEnrollmentTypeChecker::IsFREEnabled()) {
    client_to_connect->SetStateKeysToUpload(state_keys_broker_->state_keys());
  }

  // Create the component cloud policy service for fetching, caching and
  // exposing policy for extensions.
  if (!component_policy_disabled_for_testing_) {
    const base::FilePath component_policy_cache_dir =
        base::PathService::CheckedGet(ash::DIR_SIGNIN_PROFILE_COMPONENT_POLICY);
    CHECK(signin_profile_forwarding_schema_registry_);
    CreateComponentCloudPolicyService(
        dm_protocol::kChromeSigninExtensionPolicyType,
        component_policy_cache_dir, client_to_connect.get(),
        signin_profile_forwarding_schema_registry_.get());
  }

  core()->Connect(std::move(client_to_connect));
  core()->StartRefreshScheduler();
  core()->RefreshSoon(PolicyFetchReason::kBrowserStart);
  core()->TrackRefreshDelayPref(local_state_,
                                ::prefs::kDevicePolicyRefreshRate);

  external_data_manager_->Connect(
      g_browser_process->shared_url_loader_factory());

  enrollment_certificate_uploader_ =
      std::make_unique<ash::attestation::EnrollmentCertificateUploaderImpl>(
          client());
  enrollment_id_upload_manager_ =
      std::make_unique<ash::attestation::EnrollmentIdUploadManager>(
          client(), enrollment_certificate_uploader_.get());
  lookup_key_uploader_ = std::make_unique<LookupKeyUploader>(
      device_store(), g_browser_process->local_state(),
      enrollment_certificate_uploader_.get());
  euicc_status_uploader_ = std::make_unique<EuiccStatusUploader>(
      client(), g_browser_process->local_state());

  // Don't create a MachineCertificateUploader if machine cert requests are
  // disabled.
  if (!(base::CommandLine::ForCurrentProcess()->HasSwitch(
          ash::switches::kDisableMachineCertRequest))) {
    machine_certificate_uploader_ =
        std::make_unique<ash::attestation::MachineCertificateUploaderImpl>(
            client());
    machine_certificate_uploader_->UploadCertificateIfNeeded(base::DoNothing());
  }

  // Start remote commands services now that we have setup everything they need.
  core()->StartRemoteCommandsService(
      std::make_unique<DeviceCommandsFactoryAsh>(
          machine_certificate_uploader_.get(), *crd_delegate_),
      PolicyInvalidationScope::kDevice);

  // Enable device reporting and status monitoring for cloud managed devices. We
  // want to create these objects even if monitoring is currently inactive, in
  // case monitoring is turned back on in a future policy fetch - the classes
  // themselves track the current state of the monitoring settings and only
  // perform monitoring if it is active.
  if (install_attributes->IsCloudManaged()) {
    CreateManagedSessionServiceAndReporters();
    CreateStatusUploader(managed_session_service_.get());
    syslog_uploader_ =
        std::make_unique<SystemLogUploader>(nullptr, task_runner_);
    if (base::FeatureList::IsEnabled(
            chromeos::features::kKioskHeartbeatsViaERP)) {
      // Do nothing as heartbeats go over ERP.
    } else {
      // Initialize legacy GCM heartbeat (default behaviour)
      heartbeat_scheduler_ = std::make_unique<HeartbeatScheduler>(
          g_browser_process->gcm_driver(), client(), device_store_.get(),
          install_attributes->GetDeviceId(), task_runner_);
    }
    metric_reporting_manager_ = reporting::MetricReportingManager::Create(
        managed_session_service_.get());
    os_updates_reporter_ = reporting::OsUpdatesReporter::Create();
  }

  NotifyConnected();
}

void DeviceCloudPolicyManagerAsh::OnPolicyStoreReady(
    ash::InstallAttributes* install_attributes) {
  if (!install_attributes->IsCloudManaged()) {
    return;
  }
  CreateManagedSessionServiceAndReporters();
}

void DeviceCloudPolicyManagerAsh::SetSigninProfileSchemaRegistry(
    SchemaRegistry* schema_registry) {
  DCHECK(!signin_profile_forwarding_schema_registry_);
  signin_profile_forwarding_schema_registry_ =
      std::make_unique<ForwardingSchemaRegistry>(schema_registry);
  NotifyGotRegistry();
}

void DeviceCloudPolicyManagerAsh::OnUserManagerCreated(
    user_manager::UserManager* user_manager) {
  user_manager_observation_.Observe(user_manager);
  reporting_user_tracker_ =
      std::make_unique<ReportingUserTracker>(user_manager);
}

void DeviceCloudPolicyManagerAsh::OnUserManagerWillBeDestroyed() {
  // DeviceStatusCollector internally holds the reference to the
  // ReportingUserTracker instance, so should be released via Shutdown()
  // before this is reached.
  DCHECK(!status_uploader_);
  reporting_user_tracker_.reset();
  user_manager_observation_.Reset();
}

void DeviceCloudPolicyManagerAsh::OnUserToBeRemoved(
    const AccountId& account_id) {
  // This logic needs to be consistent with UserAddedRemovedReporter.
  if (!helper_->ReportingEnabled(ash::kReportDeviceLoginLogout)) {
    return;
  }
  const user_manager::User* user =
      user_manager::UserManager::Get()->FindUser(account_id);
  if (!user || user->IsKioskType() ||
      user->GetType() == user_manager::UserType::kPublicAccount ||
      user->GetType() == user_manager::UserType::kGuest) {
    return;
  }

  const std::string email = account_id.GetUserEmail();
  users_to_be_removed_.insert_or_assign(
      account_id, reporting_user_tracker_->ShouldReportUser(email));
}

void DeviceCloudPolicyManagerAsh::OnUserRemoved(
    const AccountId& account_id,
    user_manager::UserRemovalReason reason) {
  // This logic needs to be consistent with UserAddedRemovedReporter.
  auto it = users_to_be_removed_.find(account_id);
  if (it == users_to_be_removed_.end()) {
    return;
  }

  bool is_affiliated_user = it->second;
  users_to_be_removed_.erase(it);

  // Check the pref, after we update the tracking map.
  if (!helper_->ReportingEnabled(ash::kReportDeviceLoginLogout)) {
    return;
  }

  // Unlike UserAddedRemovedReporter, instead of reporting, we cache
  // the removed user here.
  // They're going to be reported, once the reporter instance is created.
  removed_users_.emplace_back(
      RemovedUser{is_affiliated_user ? account_id.GetUserEmail() : "", reason});
}

void DeviceCloudPolicyManagerAsh::OnStateKeysUpdated() {
  // TODO(b/181140445): If we had a separate state keys upload request to DM
  // Server we should call it here.
  if (client()) {
    client()->SetStateKeysToUpload(state_keys_broker_->state_keys());
  }
}

void DeviceCloudPolicyManagerAsh::NotifyConnected() {
  for (auto& observer : observers_) {
    observer.OnDeviceCloudPolicyManagerConnected();
  }
}

void DeviceCloudPolicyManagerAsh::NotifyGotRegistry() {
  for (auto& observer : observers_) {
    observer.OnDeviceCloudPolicyManagerGotRegistry();
  }
}

void DeviceCloudPolicyManagerAsh::CreateStatusUploader(
    ManagedSessionService* managed_session_service) {
  CHECK(reporting_user_tracker_.get());
  auto collector = std::make_unique<DeviceStatusCollector>(
      local_state_, reporting_user_tracker_.get(),
      ash::system::StatisticsProvider::GetInstance(), managed_session_service);

  status_uploader_ = std::make_unique<StatusUploader>(
      client(), std::move(collector), task_runner_,
      kDeviceStatusUploadFrequency);
}

void DeviceCloudPolicyManagerAsh::CreateManagedSessionServiceAndReporters() {
  if (managed_session_service_) {
    return;
  }

  if (auto* user_manager = user_manager::UserManager::Get()) {
    user_manager->RemoveObserver(this);
  }

  managed_session_service_ = std::make_unique<ManagedSessionService>();
  login_logout_reporter_ = ash::reporting::LoginLogoutReporter::Create(
      managed_session_service_.get());

  user_added_removed_reporter_ = ::reporting::UserAddedRemovedReporter::Create(
      std::move(users_to_be_removed_), managed_session_service_.get());
  for (const auto& [user_email, reason] : removed_users_) {
    user_added_removed_reporter_->ProcessRemovedUser(user_email, reason);
  }
  removed_users_.clear();

  lock_unlock_reporter_ = ash::reporting::LockUnlockReporter::Create(
      managed_session_service_.get());
}

HeartbeatScheduler*
DeviceCloudPolicyManagerAsh::GetHeartbeatSchedulerForTesting() const {
  return heartbeat_scheduler_.get();
}

reporting::OsUpdatesReporter*
DeviceCloudPolicyManagerAsh::GetOsUpdatesReporter() const {
  return os_updates_reporter_.get();
}

reporting::MetricReportingManager*
DeviceCloudPolicyManagerAsh::GetMetricReportingManager() {
  return metric_reporting_manager_.get();
}
}  // namespace policy