blob: 815b3ce13ccebad39bbed39081f80e75ae14388b [file] [log] [blame]
// Copyright 2024 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/enterprise_companion/dm_client.h"
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/ranges/algorithm.h"
#include "base/sequence_checker.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
#include "chrome/enterprise_companion/device_management_storage/dm_storage.h"
#include "chrome/enterprise_companion/enterprise_companion_branding.h"
#include "chrome/enterprise_companion/enterprise_companion_status.h"
#include "chrome/enterprise_companion/enterprise_companion_version.h"
#include "chrome/enterprise_companion/event_logger.h"
#include "components/policy/core/common/cloud/client_data_delegate.h"
#include "components/policy/core/common/cloud/cloud_policy_client.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/cloud_policy_util.h"
#include "components/policy/core/common/cloud/cloud_policy_validator.h"
#include "components/policy/core/common/cloud/device_management_service.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace enterprise_companion {
namespace {
constexpr char kGoogleUpdateMachineLevelAppsPolicyType[] =
"google/machine-level-apps";
// Given the void-returning callbacks A and B with the same signature, return a
// callback that invokes A and B in sequence with the same arguments.
template <typename... Args>
base::OnceCallback<void(Args...)> TeeOnceCallback(
base::OnceCallback<void(Args...)> a,
base::OnceCallback<void(Args...)> b) {
return base::BindOnce(
[](base::OnceCallback<void(Args...)> a,
base::OnceCallback<void(Args...)> b, Args... args) {
std::move(a).Run(args...);
std::move(b).Run(args...);
},
std::move(a), std::move(b));
}
// Convert a CloudPolicyClient::ResponseMap to a DMPolicyMap by dropping the
// "settings entity ID" from the key and serializing the fetch response.
device_management_storage::DMPolicyMap ToDMPolicyMap(
const policy::CloudPolicyClient::ResponseMap& in) {
device_management_storage::DMPolicyMap out;
base::ranges::transform(
in, std::inserter(out, out.end()),
[](const std::pair<std::pair<std::string, std::string>,
enterprise_management::PolicyFetchResponse> pair) {
return std::make_pair(pair.first.first,
pair.second.SerializeAsString());
});
return out;
}
class DMConfiguration : public policy::DeviceManagementService::Configuration {
public:
DMConfiguration() = default;
~DMConfiguration() override = default;
std::string GetDMServerUrl() const override {
return DEVICE_MANAGEMENT_SERVER_URL;
}
std::string GetAgentParameter() const override {
return base::StrCat({PRODUCT_FULLNAME_STRING, kEnterpriseCompanionVersion});
}
std::string GetPlatformParameter() const override {
int32_t major = 0;
int32_t minor = 0;
int32_t bugfix = 0;
base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
return base::StringPrintf(
"%s|%s|%d.%d.%d", base::SysInfo::OperatingSystemName().c_str(),
base::SysInfo::OperatingSystemArchitecture().c_str(), major, minor,
bugfix);
}
std::string GetRealtimeReportingServerUrl() const override {
return DEVICE_MANAGEMENT_REALTIME_REPORTING_URL;
}
std::string GetEncryptedReportingServerUrl() const override {
return DEVICE_MANAGEMENT_ENCRYPTED_REPORTING_URL;
}
std::string GetReportingConnectorServerUrl(
content::BrowserContext* context) const override {
return std::string();
}
};
class ClientDataDelegate : public policy::ClientDataDelegate {
public:
ClientDataDelegate() = default;
~ClientDataDelegate() override = default;
void FillRegisterBrowserRequest(
enterprise_management::RegisterBrowserRequest* request,
base::OnceClosure callback) const override {
request->set_machine_name(policy::GetMachineName());
request->set_os_platform(policy::GetOSPlatform());
request->set_os_version(policy::GetOSVersion());
request->set_allocated_browser_device_identifier(
policy::GetBrowserDeviceIdentifier().release());
std::move(callback).Run();
}
};
class FetchedPolicyValidator final : public policy::CloudPolicyValidatorBase {
public:
explicit FetchedPolicyValidator(
std::unique_ptr<enterprise_management::PolicyFetchResponse>
policy_response)
: policy::CloudPolicyValidatorBase(std::move(policy_response),
/*background_task_runner=*/nullptr) {}
FetchedPolicyValidator(const FetchedPolicyValidator&) = delete;
FetchedPolicyValidator& operator=(const FetchedPolicyValidator&) = delete;
private:
// Overrides for CloudPolicyValidatorBase.
Status CheckPayload() override {
// The payload is valid so long as at least one policy is present.
return (policy_data() && policy_data()->has_policy_value())
? VALIDATION_OK
: VALIDATION_POLICY_PARSE_ERROR;
}
Status CheckValues() override {
// The enterprise companion is agnostic to the type of payload. Hence, the
// values are not verified.
return VALIDATION_OK;
}
};
// Interface to a CloudPolicyClient which interacts with the device management
// server. May perform blocking IO.
class DMClientImpl : public DMClient, policy::CloudPolicyClient::Observer {
public:
explicit DMClientImpl(
std::unique_ptr<policy::DeviceManagementService::Configuration> config,
CloudPolicyClientProvider cloud_policy_client_provider,
scoped_refptr<device_management_storage::DMStorage> dm_storage,
PolicyFetchResponseValidator policy_fetch_response_validator)
: dm_service_(std::move(config)),
cloud_policy_client_(
std::move(cloud_policy_client_provider).Run(&dm_service_)),
dm_storage_(dm_storage),
policy_fetch_response_validator_(policy_fetch_response_validator) {
dm_service_.ScheduleInitialization(0);
cloud_policy_client_->AddObserver(this);
cloud_policy_client_->AddPolicyTypeToFetch(
kGoogleUpdateMachineLevelAppsPolicyType,
/*settings_entity_id=*/"");
if (!dm_storage->GetDmToken().empty()) {
cloud_policy_client_->SetupRegistration(dm_storage_->GetDmToken(),
dm_storage_->GetDeviceID(),
/*user_affiliation_ids=*/{});
}
UpdateCachedPolicyInfo();
}
~DMClientImpl() override { cloud_policy_client_->RemoveObserver(this); }
// Overrides for DMClient.
void RegisterBrowser(scoped_refptr<EventLogger> event_logger,
StatusCallback callback) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!pending_callback_);
if (ShouldSkipRegistration()) {
std::move(callback).Run(EnterpriseCompanionStatus::Success());
return;
}
dm_storage_->RemoveAllPolicies();
pending_callback_ =
TeeOnceCallback(std::move(callback), event_logger->OnEnrollmentStart());
cloud_policy_client_->RegisterBrowserWithEnrollmentToken(
dm_storage_->GetEnrollmentToken(), dm_storage_->GetDeviceID(),
client_data_delegate_, dm_storage_->IsEnrollmentMandatory());
}
void FetchPolicies(scoped_refptr<EventLogger> event_logger,
StatusCallback callback) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!pending_callback_);
// Wrap the callback with event logging early to ensure that precondition
// errors are logged.
callback = TeeOnceCallback(std::move(callback),
event_logger->OnPolicyFetchStart());
if (!cloud_policy_client_->is_registered()) {
VLOG(1) << "Failed to fetch policies: client is not registered";
std::move(callback).Run(EnterpriseCompanionStatus(
ApplicationError::kRegistrationPreconditionFailed));
return;
}
if (!dm_storage_->CanPersistPolicies()) {
VLOG(1) << "Failed to fetch policies: policies cannot be persisted.";
std::move(callback).Run(EnterpriseCompanionStatus(
ApplicationError::kPolicyPersistenceImpossible));
}
pending_callback_ = std::move(callback);
UpdateCachedPolicyInfo();
cloud_policy_client_->FetchPolicy(policy::PolicyFetchReason::kUnspecified);
}
// Overrides for policy::CloudPolicyClient::Observer.
void OnPolicyFetched(policy::CloudPolicyClient*) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
StatusCallback callback =
pending_callback_ ? std::move(pending_callback_) : base::DoNothing();
if (cloud_policy_client_->last_dm_status() !=
policy::DeviceManagementStatus::DM_STATUS_SUCCESS) {
std::move(callback).Run(
EnterpriseCompanionStatus::FromDeviceManagementStatus(
cloud_policy_client_->last_dm_status()));
return;
}
// Make a copy to reduce the surface of TOCTOU errors between validation and
// serialization.
policy::CloudPolicyClient::ResponseMap responses =
cloud_policy_client_->last_policy_fetch_responses();
FetchedPolicyValidator::Status validation_result =
ValidatePolicyFetchResponses(responses);
if (validation_result != FetchedPolicyValidator::VALIDATION_OK) {
std::move(callback).Run(
EnterpriseCompanionStatus::FromCloudPolicyValidationResult(
validation_result));
return;
}
if (!dm_storage_->PersistPolicies(ToDMPolicyMap(std::move(responses)))) {
std::move(callback).Run(EnterpriseCompanionStatus(
ApplicationError::kPolicyPersistenceFailed));
return;
}
std::move(callback).Run(EnterpriseCompanionStatus::Success());
}
void OnRegistrationStateChanged(policy::CloudPolicyClient*) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
if (cloud_policy_client_->is_registered()) {
dm_storage_->StoreDmToken(cloud_policy_client_->dm_token());
}
if (pending_callback_) {
std::move(pending_callback_)
.Run(EnterpriseCompanionStatus::FromDeviceManagementStatus(
cloud_policy_client_->last_dm_status()));
}
}
void OnClientError(policy::CloudPolicyClient*) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << __func__;
if (cloud_policy_client_->last_dm_status() ==
policy::DM_STATUS_SERVICE_DEVICE_NEEDS_RESET) {
VLOG(1) << "DMServer requests deregister via DMToken deletion.";
LOG_IF(ERROR, !dm_storage_->DeleteDMToken())
<< "Could not deregister: Failed to delete the DMToken.";
} else if (cloud_policy_client_->last_dm_status() ==
policy::DM_STATUS_SERVICE_DEVICE_NOT_FOUND) {
VLOG(1) << "DMServer requests deregister via DMToken invalidation.";
LOG_IF(ERROR, !dm_storage_->InvalidateDMToken())
<< "Could not deregister: Failed to invalidate the DMToken.";
}
if (pending_callback_) {
std::move(pending_callback_)
.Run(EnterpriseCompanionStatus::FromDeviceManagementStatus(
cloud_policy_client_->last_dm_status()));
}
}
private:
SEQUENCE_CHECKER(sequence_checker_);
policy::DeviceManagementService dm_service_;
std::unique_ptr<policy::CloudPolicyClient> cloud_policy_client_;
scoped_refptr<device_management_storage::DMStorage> dm_storage_;
PolicyFetchResponseValidator policy_fetch_response_validator_;
ClientDataDelegate client_data_delegate_;
StatusCallback pending_callback_;
std::unique_ptr<device_management_storage::CachedPolicyInfo>
cached_policy_info_;
bool ShouldSkipRegistration() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (dm_storage_->GetEnrollmentToken().empty()) {
VLOG(1) << "Registration skipped: device not managed.";
return true;
} else if (cloud_policy_client_->is_registered()) {
VLOG(1) << "Registration skipped: device already registered.";
return true;
}
return false;
}
// Validates all of the fetched policies. Returns the last encountered error
// or `VALIDATION_OK`.
FetchedPolicyValidator::Status ValidatePolicyFetchResponses(
const policy::CloudPolicyClient::ResponseMap& responses) {
FetchedPolicyValidator::Status last_result =
FetchedPolicyValidator::VALIDATION_OK;
for (auto const& [key, response] : responses) {
const std::string& policy_type = key.first;
std::unique_ptr<FetchedPolicyValidator::ValidationResult>
validation_result = policy_fetch_response_validator_.Run(
dm_storage_->GetDmToken(), dm_storage_->GetDeviceID(),
cached_policy_info_->public_key(),
cached_policy_info_->timestamp(), response);
CHECK(validation_result) << "Policy validation result cannot be null";
if (validation_result->status != FetchedPolicyValidator::VALIDATION_OK) {
LOG(ERROR) << "Policy validation failed for " << policy_type
<< " response: "
<< FetchedPolicyValidator::StatusToString(
validation_result->status);
last_result = validation_result->status;
}
}
return last_result;
}
// Update the cached policy information, configuring the CloudPolicyClient
// with the results.
void UpdateCachedPolicyInfo() {
cached_policy_info_ = dm_storage_->GetCachedPolicyInfo();
if (cached_policy_info_->has_key_version()) {
cloud_policy_client_->set_public_key_version(
cached_policy_info_->key_version());
}
}
};
} // namespace
CloudPolicyClientProvider GetDefaultCloudPolicyClientProvider(
scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory) {
return base::BindOnce(
[](scoped_refptr<network::SharedURLLoaderFactory>
shared_url_loader_factory,
policy::DeviceManagementService* dm_service) {
return std::make_unique<policy::CloudPolicyClient>(
dm_service, shared_url_loader_factory);
},
std::move(shared_url_loader_factory));
}
PolicyFetchResponseValidator GetDefaultPolicyFetchResponseValidator() {
return base::BindRepeating([](const std::string& dm_token,
const std::string& device_id,
const std::string& cached_policy_public_key,
int64_t cached_policy_timestamp,
const enterprise_management::
PolicyFetchResponse& response) {
FetchedPolicyValidator validator(
std::make_unique<enterprise_management::PolicyFetchResponse>(response));
validator.ValidateDMToken(
dm_token,
FetchedPolicyValidator::ValidateDMTokenOption::DM_TOKEN_REQUIRED);
validator.ValidateDeviceId(
device_id,
FetchedPolicyValidator::ValidateDeviceIdOption::DEVICE_ID_REQUIRED);
validator.ValidateTimestamp(
base::Time::FromMillisecondsSinceUnixEpoch(cached_policy_timestamp),
FetchedPolicyValidator::ValidateTimestampOption::TIMESTAMP_VALIDATED);
if (cached_policy_public_key.empty()) {
validator.ValidateInitialKey("");
} else {
validator.ValidateSignatureAllowingRotation(cached_policy_public_key, "");
}
validator.ValidatePayload();
validator.RunValidation();
return validator.GetValidationResult();
});
}
std::unique_ptr<policy::DeviceManagementService::Configuration>
CreateDeviceManagementServiceConfig() {
return std::make_unique<DMConfiguration>();
}
std::unique_ptr<DMClient> CreateDMClient(
CloudPolicyClientProvider cloud_policy_client_provider,
scoped_refptr<device_management_storage::DMStorage> dm_storage,
PolicyFetchResponseValidator policy_fetch_response_validator,
std::unique_ptr<policy::DeviceManagementService::Configuration> config) {
return std::make_unique<DMClientImpl>(
std::move(config), std::move(cloud_policy_client_provider), dm_storage,
policy_fetch_response_validator);
}
} // namespace enterprise_companion