// 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_store_ash.h"
#include <utility>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/login/startup_utils.h"
#include "chrome/browser/ash/policy/core/device_policy_decoder.h"
#include "chrome/browser/ash/policy/dev_mode/dev_mode_policy_util.h"
#include "chrome/browser/ash/policy/value_validation/onc_device_policy_value_validator.h"
#include "chromeos/ash/components/install_attributes/install_attributes.h"
#include "chromeos/dbus/constants/dbus_paths.h"
#include "components/ownership/owner_key_util.h"
#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/enterprise_metrics.h"
#include "components/policy/proto/chrome_device_policy.pb.h"
#include "components/policy/proto/device_management_backend.pb.h"
namespace em = enterprise_management;
namespace policy {
namespace {
const char kDMTokenCheckHistogram[] = "Enterprise.EnrolledPolicyHasDMToken";
const char kPolicyCheckHistogram[] = "Enterprise.EnrolledDevicePolicyPresent";
} // namespace
DeviceCloudPolicyStoreAsh::DeviceCloudPolicyStoreAsh(
ash::DeviceSettingsService* device_settings_service,
ash::InstallAttributes* install_attributes,
scoped_refptr<base::SequencedTaskRunner> background_task_runner)
: device_settings_service_(device_settings_service),
install_attributes_(install_attributes),
background_task_runner_(background_task_runner) {
device_settings_service_->AddObserver(this);
device_settings_service_->SetDeviceMode(install_attributes_->GetMode());
}
DeviceCloudPolicyStoreAsh::~DeviceCloudPolicyStoreAsh() {
if (device_settings_service_)
device_settings_service_->RemoveObserver(this);
}
void DeviceCloudPolicyStoreAsh::Store(const em::PolicyFetchResponse& policy) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The policy and the public key must have already been loaded by the device
// settings service.
DCHECK(is_initialized());
// Cancel all pending requests.
weak_factory_.InvalidateWeakPtrs();
scoped_refptr<ownership::PublicKey> public_key(
device_settings_service_->GetPublicKey());
if (!install_attributes_->IsCloudManaged() ||
!device_settings_service_->policy_data() || !public_key.get() ||
public_key->is_empty()) {
LOG(ERROR) << "Policy store failed, is_cloud_managed: "
<< install_attributes_->IsCloudManaged() << ", policy_data: "
<< (device_settings_service_->policy_data() != nullptr)
<< ", public_key: " << (public_key.get() != nullptr)
<< ", public_key_is_loaded: "
<< (public_key.get() ? !public_key->is_empty() : false);
status_ = STATUS_BAD_STATE;
NotifyStoreError();
return;
}
std::unique_ptr<DeviceCloudPolicyValidator> validator(
CreateValidator(policy));
validator->ValidateSignatureAllowingRotation(
public_key->as_string(), install_attributes_->GetDomain());
validator->ValidateAgainstCurrentPolicy(
device_settings_service_->policy_data(),
CloudPolicyValidatorBase::TIMESTAMP_VALIDATED,
CloudPolicyValidatorBase::DM_TOKEN_REQUIRED,
CloudPolicyValidatorBase::DEVICE_ID_REQUIRED);
validator->RunValidation();
OnPolicyToStoreValidated(validator.get());
}
void DeviceCloudPolicyStoreAsh::Load() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Cancel all pending requests.
weak_factory_.InvalidateWeakPtrs();
device_settings_service_->Load();
}
void DeviceCloudPolicyStoreAsh::InstallInitialPolicy(
const em::PolicyFetchResponse& policy) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Cancel all pending requests.
weak_factory_.InvalidateWeakPtrs();
if (!install_attributes_->IsCloudManaged()) {
status_ = STATUS_BAD_STATE;
NotifyStoreError();
return;
}
std::unique_ptr<DeviceCloudPolicyValidator> validator(
CreateValidator(policy));
validator->ValidateInitialKey(install_attributes_->GetDomain());
validator->ValidateDeviceId(install_attributes_->GetDeviceId(),
CloudPolicyValidatorBase::DEVICE_ID_REQUIRED);
validator->RunValidation();
OnPolicyToStoreValidated(validator.get());
}
void DeviceCloudPolicyStoreAsh::DeviceSettingsUpdated() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!weak_factory_.HasWeakPtrs())
UpdateFromService();
}
void DeviceCloudPolicyStoreAsh::OnDeviceSettingsServiceShutdown() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
device_settings_service_->RemoveObserver(this);
device_settings_service_ = nullptr;
}
std::unique_ptr<DeviceCloudPolicyValidator>
DeviceCloudPolicyStoreAsh::CreateValidator(
const em::PolicyFetchResponse& policy) {
auto validator = std::make_unique<DeviceCloudPolicyValidator>(
std::make_unique<em::PolicyFetchResponse>(policy),
background_task_runner_);
validator->ValidateDomain(install_attributes_->GetDomain());
validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType);
validator->ValidatePayload();
validator->ValidateValues(std::make_unique<ONCDevicePolicyValueValidator>());
return validator;
}
void DeviceCloudPolicyStoreAsh::OnPolicyToStoreValidated(
DeviceCloudPolicyValidator* validator) {
validation_result_ = validator->GetValidationResult();
if (!validator->success()) {
status_ = STATUS_VALIDATION_ERROR;
NotifyStoreError();
return;
}
if (GetDeviceBlockDevModePolicyValue(*(validator->payload())) &&
!IsDeviceBlockDevModePolicyAllowed()) {
LOG(ERROR) << "Rejected device policy: DeviceBlockDevmode not allowed";
status_ = STATUS_BAD_STATE;
NotifyStoreError();
return;
}
device_settings_service_->Store(
std::move(validator->policy()),
base::BindOnce(&DeviceCloudPolicyStoreAsh::OnPolicyStored,
weak_factory_.GetWeakPtr()));
}
void DeviceCloudPolicyStoreAsh::OnPolicyStored() {
UpdateFromService();
}
void DeviceCloudPolicyStoreAsh::UpdateFromService() {
if (!install_attributes_->IsEnterpriseManaged()) {
status_ = STATUS_BAD_STATE;
NotifyStoreError();
return;
}
CheckDMToken();
UpdateStatusFromService();
const ash::DeviceSettingsService::Status service_status =
device_settings_service_->status();
if (service_status == ash::DeviceSettingsService::STORE_SUCCESS) {
auto new_policy_fetch_response =
std::make_unique<em::PolicyFetchResponse>();
auto new_policy = std::make_unique<em::PolicyData>();
const em::PolicyFetchResponse* policy_fetch_response =
device_settings_service_->policy_fetch_response();
const em::PolicyData* policy_data = device_settings_service_->policy_data();
if (policy_data) {
DCHECK(policy_fetch_response);
new_policy_fetch_response->MergeFrom(*policy_fetch_response);
new_policy->MergeFrom(*policy_data);
}
SetPolicy(std::move(new_policy_fetch_response), std::move(new_policy));
PolicyMap new_policy_map;
if (is_managed()) {
DecodeDevicePolicy(*device_settings_service_->device_settings(),
external_data_manager(), &new_policy_map);
}
policy_map_.Swap(&new_policy_map);
scoped_refptr<ownership::PublicKey> key =
device_settings_service_->GetPublicKey();
policy_signature_public_key_ = key ? key->as_string() : std::string();
NotifyStoreLoaded();
return;
}
NotifyStoreError();
}
void DeviceCloudPolicyStoreAsh::UpdateStatusFromService() {
switch (device_settings_service_->status()) {
case ash::DeviceSettingsService::STORE_SUCCESS:
status_ = STATUS_OK;
return;
case ash::DeviceSettingsService::STORE_KEY_UNAVAILABLE:
status_ = STATUS_BAD_STATE;
return;
case ash::DeviceSettingsService::STORE_OPERATION_FAILED:
status_ = STATUS_STORE_ERROR;
return;
case ash::DeviceSettingsService::STORE_NO_POLICY:
case ash::DeviceSettingsService::STORE_INVALID_POLICY:
case ash::DeviceSettingsService::STORE_VALIDATION_ERROR:
status_ = STATUS_LOAD_ERROR;
return;
}
NOTREACHED_IN_MIGRATION();
}
void DeviceCloudPolicyStoreAsh::CheckDMToken() {
const ash::DeviceSettingsService::Status service_status =
device_settings_service_->status();
switch (service_status) {
case ash::DeviceSettingsService::STORE_SUCCESS:
case ash::DeviceSettingsService::STORE_KEY_UNAVAILABLE:
case ash::DeviceSettingsService::STORE_NO_POLICY:
case ash::DeviceSettingsService::STORE_INVALID_POLICY:
case ash::DeviceSettingsService::STORE_VALIDATION_ERROR:
// Continue with the check below.
break;
case ash::DeviceSettingsService::STORE_OPERATION_FAILED:
// Don't check for write errors or transient read errors.
return;
}
if (dm_token_checked_) {
return;
}
dm_token_checked_ = true;
const em::PolicyData* policy_data = device_settings_service_->policy_data();
if (policy_data && policy_data->has_request_token()) {
base::UmaHistogramBoolean(kDMTokenCheckHistogram, true);
base::UmaHistogramBoolean(kPolicyCheckHistogram, true);
return;
}
base::UmaHistogramBoolean(kDMTokenCheckHistogram, false);
base::UmaHistogramBoolean(kPolicyCheckHistogram, policy_data);
std::stringstream debug_info;
debug_info << "has_policy: " << (policy_data != nullptr);
// Log the value of the data from policy_fetch_response.
const em::PolicyFetchResponse* policy_fetch_response =
device_settings_service_->policy_fetch_response();
debug_info << ", has_fetch_response: " << (policy_fetch_response != nullptr);
if (policy_fetch_response) {
debug_info << ", has_signature: "
<< policy_fetch_response->has_policy_data_signature();
debug_info << ", size = " << policy_fetch_response->ByteSize();
std::unique_ptr<em::PolicyData> poldata =
std::make_unique<em::PolicyData>();
if (!policy_fetch_response->has_policy_data() ||
!poldata->ParseFromString(policy_fetch_response->policy_data()) ||
!poldata->IsInitialized()) {
debug_info << ", parse policy failed";
} else {
debug_info << ", has_dm_token: " << poldata->has_request_token();
if (poldata->has_request_token()) {
debug_info << ", dm_token size: " << poldata->request_token().size();
}
debug_info << ", has_device_id: " << poldata->has_device_id()
<< ", has_device_state: " << poldata->has_device_state();
}
}
debug_info << ", attrs mode: " << install_attributes_->GetMode()
<< ", is_locked: " << install_attributes_->IsDeviceLocked();
LOG(ERROR) << "Device policy read on enrolled device yields "
<< "no DM token! Status: " << service_status
<< ", debug_info: " << debug_info.str() << ".";
// At the time LoginDisplayHostWebUI decides whether enrollment flow is to
// be started, policy hasn't been read yet. To work around this, once the
// need for recovery is detected upon policy load, a flag is stored in prefs
// which is accessed by LoginDisplayHostWebUI early during (next) boot.
ash::StartupUtils::MarkEnrollmentRecoveryRequired();
}
void DeviceCloudPolicyStoreAsh::UpdateFirstPoliciesLoaded() {
CloudPolicyStore::UpdateFirstPoliciesLoaded();
// Mark policies as loaded if we don't expect any policies to be loaded.
first_policies_loaded_ |= !install_attributes_->IsEnterpriseManaged();
}
} // namespace policy