Skip to content

Commit

Permalink
Add enterprise policy for Bruschetta beta
Browse files Browse the repository at this point in the history
This covers the MVP requirement to validate downloaded VM images while
leaving a path to add more policy controls later.

Since this is an unreleased policy, it will only be applied to stable
and beta devices if enabled via the EnableExperimentalPolicies policy.

Bug: b:231900967
Change-Id: I8b3270d4581caa806ca4b4fa3fb1916d08ac3aa1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4022696
Reviewed-by: Dominic Battré <battre@chromium.org>
Commit-Queue: Fergus Dall <sidereal@google.com>
Reviewed-by: Alexander Hendrich <hendrich@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1073399}
  • Loading branch information
fergus-dall authored and Chromium LUCI CQ committed Nov 18, 2022
1 parent 142546e commit 54a6a43
Show file tree
Hide file tree
Showing 13 changed files with 1,143 additions and 0 deletions.
4 changes: 4 additions & 0 deletions chrome/browser/ash/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,10 @@ source_set("ash") {
"bruschetta/bruschetta_launcher.h",
"bruschetta/bruschetta_mount_provider.cc",
"bruschetta/bruschetta_mount_provider.h",
"bruschetta/bruschetta_policy_handler.cc",
"bruschetta/bruschetta_policy_handler.h",
"bruschetta/bruschetta_pref_names.cc",
"bruschetta/bruschetta_pref_names.h",
"bruschetta/bruschetta_service.cc",
"bruschetta/bruschetta_service.h",
"bruschetta/bruschetta_service_factory.cc",
Expand Down
263 changes: 263 additions & 0 deletions chrome/browser/ash/bruschetta/bruschetta_policy_handler.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
// 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/bruschetta/bruschetta_policy_handler.h"

#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/ash/bruschetta/bruschetta_pref_names.h"
#include "components/policy/core/browser/policy_error_map.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/schema.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_value_map.h"
#include "components/strings/grit/components_strings.h"
#include "crypto/sha2.h"
#include "url/gurl.h"

namespace bruschetta {

namespace {

#if defined(ARCH_CPU_X86_64)
const char kPolicyImageKeyArchSpecific[] = "installer_image_x86_64";
const char kPolicyPflashKeyArchSpecific[] = "uefi_pflash_x86_64";
const char kPolicyUefiKeyArchSpecific[] = "uefi_image_x86_64";

constexpr policy::PolicyMap::MessageType kUninstallableErrorLevel =
policy::PolicyMap::MessageType::kError;
#else
const char kPolicyImageKeyArchSpecific[] = "";
const char kPolicyPflashKeyArchSpecific[] = "";
const char kPolicyUefiKeyArchSpecific[] = "";

constexpr policy::PolicyMap::MessageType kUninstallableErrorLevel =
policy::PolicyMap::MessageType::kInfo;
#endif

prefs::PolicyEnabledState EnabledStrToEnum(const std::string& str) {
if (str == "RUN_ALLOWED") {
return prefs::PolicyEnabledState::RUN_ALLOWED;
}
if (str == "INSTALL_ALLOWED") {
return prefs::PolicyEnabledState::INSTALL_ALLOWED;
}

return prefs::PolicyEnabledState::BLOCKED;
}

prefs::PolicyUpdateAction UpdateActionStrToEnum(const std::string& str) {
if (str == "NONE") {
return prefs::PolicyUpdateAction::NONE;
}
if (str == "FORCE_SHUTDOWN_ALWAYS") {
return prefs::PolicyUpdateAction::FORCE_SHUTDOWN_ALWAYS;
}

return prefs::PolicyUpdateAction::FORCE_SHUTDOWN_IF_MORE_RESTRICTED;
}

} // namespace

BruschettaPolicyHandler::BruschettaPolicyHandler(policy::Schema schema)
: policy::SimpleSchemaValidatingPolicyHandler(
policy::key::kBruschettaVMConfiguration,
prefs::kBruschettaVMConfiguration,
schema,
policy::SchemaOnErrorStrategy::SCHEMA_ALLOW_UNKNOWN,
policy::SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
policy::SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED) {}

BruschettaPolicyHandler::~BruschettaPolicyHandler() = default;

bool BruschettaPolicyHandler::CheckDownloadableObject(
policy::PolicyErrorMap* errors,
const std::string& id,
const std::string& key,
const base::Value::Dict& dict) {
bool retval = true;

const auto* url_str = dict.FindString(prefs::kPolicyURLKey);
if (!GURL(*url_str).is_valid()) {
errors->AddError(policy_name(), IDS_POLICY_INVALID_URL_ERROR,
policy::PolicyErrorPath{id, key, prefs::kPolicyURLKey});
retval = false;
}

std::vector<uint8_t> hash_bytes;
const auto* hash_str = dict.FindString(prefs::kPolicyHashKey);
if (!base::HexStringToBytes(*hash_str, &hash_bytes) ||
hash_bytes.size() != crypto::kSHA256Length) {
errors->AddError(policy_name(), IDS_POLICY_INVALID_HASH_ERROR,
policy::PolicyErrorPath{id, key, prefs::kPolicyHashKey});
retval = false;
}

return retval;
}

bool BruschettaPolicyHandler::CheckPolicySettings(
const policy::PolicyMap& policies,
policy::PolicyErrorMap* errors) {
// Delegate to our super-class, which checks the JSON schema.
if (!policy::SimpleSchemaValidatingPolicyHandler::CheckPolicySettings(
policies, errors)) {
return false;
}

// Aside from outright schema violations we never reject a policy, we only
// downgrade configs from installable to runnable. This minimizes the damage
// caused by misconfigurations.

downgraded_by_error_.clear();
const base::Value* value =
policies.GetValue(policy_name(), base::Value::Type::DICT);

if (!value) {
return true;
}

for (const auto outer_config : value->GetDict()) {
const std::string& id = outer_config.first;
const base::Value::Dict& config = outer_config.second.GetDict();

bool valid_config = true;

const auto* installer_image = config.FindDict(kPolicyImageKeyArchSpecific);
if (installer_image) {
if (!CheckDownloadableObject(errors, id, kPolicyImageKeyArchSpecific,
*installer_image)) {
valid_config = false;
}
}

const auto* uefi_image = config.FindDict(kPolicyUefiKeyArchSpecific);
if (uefi_image) {
if (!CheckDownloadableObject(errors, id, kPolicyUefiKeyArchSpecific,
*uefi_image)) {
valid_config = false;
}
}

const auto* pflash = config.FindDict(kPolicyPflashKeyArchSpecific);
if (pflash) {
if (!CheckDownloadableObject(errors, id, kPolicyPflashKeyArchSpecific,
*pflash)) {
valid_config = false;
}
}

if (EnabledStrToEnum(*config.FindString(prefs::kPolicyEnabledKey)) ==
prefs::PolicyEnabledState::INSTALL_ALLOWED) {
if (!installer_image || !uefi_image) {
// This is an error on x86_64, since that's currently our *only*
// supported architecture so this definitely indicates a
// misconfiguration, but we also leave an informational level message
// for arm devices to be helpful.
errors->AddError(policy_name(),
IDS_POLICY_BRUSCHETTA_UNINSTALLABLE_ERROR,
policy::PolicyErrorPath{id}, kUninstallableErrorLevel);
}

if (!installer_image || !uefi_image || !valid_config) {
downgraded_by_error_.insert(id);
}
}
}

return true;
}

void BruschettaPolicyHandler::ApplyPolicySettings(
const policy::PolicyMap& policies,
PrefValueMap* prefs) {
const base::Value* value =
policies.GetValue(policy_name(), base::Value::Type::DICT);
if (!value)
return;

// We can mostly skip error checking here because by this point the policy has
// already been checked against the schema.

base::Value::Dict pref;

for (const auto outer_config : value->GetDict()) {
const std::string& id = outer_config.first;
const base::Value::Dict& config = outer_config.second.GetDict();

base::Value::Dict pref_config;
bool installable;

{
pref_config.Set(prefs::kPolicyNameKey,
*config.FindString(prefs::kPolicyNameKey));
}

{
auto policy_enabled =
EnabledStrToEnum(*config.FindString(prefs::kPolicyEnabledKey));
if (downgraded_by_error_.contains(id)) {
policy_enabled =
std::min(policy_enabled, prefs::PolicyEnabledState::RUN_ALLOWED);
}
pref_config.Set(prefs::kPolicyEnabledKey,
static_cast<int>(policy_enabled));
installable =
(policy_enabled == prefs::PolicyEnabledState::INSTALL_ALLOWED);
}

{
const auto* installer_image =
config.FindDict(kPolicyImageKeyArchSpecific);
if (installer_image && installable) {
pref_config.Set(prefs::kPolicyImageKey, installer_image->Clone());
}
}

{
const auto* uefi_image = config.FindDict(kPolicyUefiKeyArchSpecific);
if (uefi_image && installable) {
pref_config.Set(prefs::kPolicyUefiKey, uefi_image->Clone());
}
}

{
const auto* pflash = config.FindDict(kPolicyPflashKeyArchSpecific);
if (pflash && installable) {
pref_config.Set(prefs::kPolicyPflashKey, pflash->Clone());
}
}

{
const auto* vtpm = config.FindDict(prefs::kPolicyVTPMKey);
bool vtpm_enabled = false;
prefs::PolicyUpdateAction vtpm_update_action =
prefs::PolicyUpdateAction::FORCE_SHUTDOWN_IF_MORE_RESTRICTED;
if (vtpm) {
vtpm_enabled = *vtpm->FindBool(prefs::kPolicyVTPMEnabledKey);

const auto* vtpm_update_action_str =
vtpm->FindString(prefs::kPolicyVTPMUpdateActionKey);
if (vtpm_update_action_str) {
vtpm_update_action = UpdateActionStrToEnum(*vtpm_update_action_str);
}
}

base::Value::Dict pref_vtpm;
pref_vtpm.Set(prefs::kPolicyVTPMEnabledKey, vtpm_enabled);
pref_vtpm.Set(prefs::kPolicyVTPMUpdateActionKey,
static_cast<int>(vtpm_update_action));

pref_config.Set(prefs::kPolicyVTPMKey, std::move(pref_vtpm));
}

pref.Set(id, std::move(pref_config));
}

prefs->SetValue(prefs::kBruschettaVMConfiguration,
base::Value(std::move(pref)));
}

} // namespace bruschetta
50 changes: 50 additions & 0 deletions chrome/browser/ash/bruschetta/bruschetta_policy_handler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// 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.

#ifndef CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_POLICY_HANDLER_H_
#define CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_POLICY_HANDLER_H_

#include "components/policy/core/browser/configuration_policy_handler.h"

namespace bruschetta {

// BruschettaPolicyHandler is responsible for mapping the
// BruschettaVMConfiguration enterprise policy into chrome preferences. Because
// we do non-trivial mapping we have our own subclass. Right now we:
// - Fill in optional policies values with their default (e.g. vtpm)
// - Map the appropriate architecture specific downloads to the corresponding
// generic keys ("installer_image_x86_64" to "installer_image" and so on)
// - Replace string-enumerations with numeric values defined in
// bruschetta_pref_names.h
// - Reduce the enable level if necessary to match what we can actually offer
// i.e. we don't mark a config as installable if we can't actually install it.
//
// For concrete examples, see //chrome/test/data/policy/policy_test_cases.json
class BruschettaPolicyHandler
: public policy::SimpleSchemaValidatingPolicyHandler {
public:
explicit BruschettaPolicyHandler(policy::Schema schema);
~BruschettaPolicyHandler() override;

// ConfigurationPolicyHandler:
bool CheckPolicySettings(const policy::PolicyMap& policies,
policy::PolicyErrorMap* errors) override;
void ApplyPolicySettings(const policy::PolicyMap& policies,
PrefValueMap* prefs) override;

private:
bool CheckDownloadableObject(policy::PolicyErrorMap* errors,
const std::string& id,
const std::string& key,
const base::Value::Dict& dict);

// The set of configurations that have been downgraded from installable to
// runnable due to an error in their config. Filled by CheckPolicySettings and
// used by ApplyPolicySettings.
base::flat_set<std::string> downgraded_by_error_;
};

} // namespace bruschetta

#endif // CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_POLICY_HANDLER_H_
28 changes: 28 additions & 0 deletions chrome/browser/ash/bruschetta/bruschetta_pref_names.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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/bruschetta/bruschetta_pref_names.h"

#include "components/prefs/pref_registry_simple.h"

namespace bruschetta::prefs {

const char kBruschettaVMConfiguration[] = "bruschetta.vm_configuration";

const char kPolicyNameKey[] = "name";
const char kPolicyEnabledKey[] = "enabled_state";
const char kPolicyImageKey[] = "installer_image";
const char kPolicyPflashKey[] = "uefi_pflash";
const char kPolicyUefiKey[] = "uefi_image";
const char kPolicyURLKey[] = "url";
const char kPolicyHashKey[] = "hash";
const char kPolicyVTPMKey[] = "vtpm";
const char kPolicyVTPMEnabledKey[] = "enabled";
const char kPolicyVTPMUpdateActionKey[] = "policy_update_action";

void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(kBruschettaVMConfiguration);
}

} // namespace bruschetta::prefs
41 changes: 41 additions & 0 deletions chrome/browser/ash/bruschetta/bruschetta_pref_names.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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.

#ifndef CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_PREF_NAMES_H_
#define CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_PREF_NAMES_H_

class PrefRegistrySimple;

namespace bruschetta::prefs {

// Mapped pref for the enterprise policy BruschettaVMConfiguration.
extern const char kBruschettaVMConfiguration[];
extern const char kPolicyNameKey[];
extern const char kPolicyEnabledKey[];
extern const char kPolicyImageKey[];
extern const char kPolicyPflashKey[];
extern const char kPolicyUefiKey[];
extern const char kPolicyURLKey[];
extern const char kPolicyHashKey[];
extern const char kPolicyVTPMKey[];
extern const char kPolicyVTPMEnabledKey[];
extern const char kPolicyVTPMUpdateActionKey[];

enum class PolicyEnabledState {
BLOCKED = 0,
RUN_ALLOWED = 1,
INSTALL_ALLOWED = 2,
};

enum class PolicyUpdateAction {
NONE = 0,
FORCE_SHUTDOWN_IF_MORE_RESTRICTED = 1,
FORCE_SHUTDOWN_ALWAYS = 2,
};

void RegisterProfilePrefs(PrefRegistrySimple* registry);

} // namespace bruschetta::prefs

#endif // CHROME_BROWSER_ASH_BRUSCHETTA_BRUSCHETTA_PREF_NAMES_H_

0 comments on commit 54a6a43

Please sign in to comment.