chromium/chrome/browser/ash/policy/fuzzer/policy_fuzzer.cc

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

#include <string>

#include "base/at_exit.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/i18n/icu_util.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/path_service.h"
#include "base/syslog_logging.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "chrome/browser/ash/dbus/ash_dbus_helper.h"
#include "chrome/browser/ash/policy/core/device_policy_decoder.h"
#include "chrome/browser/ash/policy/fuzzer/policy_fuzzer.pb.h"
#include "chrome/browser/ash/settings/device_settings_provider.h"
#include "chrome/browser/ash/settings/device_settings_service.h"
#include "chrome/browser/policy/configuration_policy_handler_list_factory.h"
#include "chrome/common/chrome_paths.h"
#include "chromeos/ash/components/attestation/attestation_features.h"
#include "chromeos/ash/components/install_attributes/install_attributes.h"
#include "components/policy/core/browser/configuration_policy_handler_list.h"
#include "components/policy/core/browser/policy_conversions_client.h"
#include "components/policy/core/browser/policy_error_map.h"
#include "components/policy/core/common/chrome_schema.h"
#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
#include "components/policy/core/common/external_data_manager.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_proto_decoders.h"
#include "components/policy/core/common/policy_types.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_value_map.h"
#include "testing/libfuzzer/proto/lpm_interface.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_paths.h"

namespace policy {

namespace {

constexpr logging::LogSeverity kLogSeverity = logging::LOGGING_FATAL;

// A log handler that discards messages whose severity is lower than the
// threshold. It's needed in order to suppress unneeded syslog logging (which by
// default is exempt from the level set by `logging::SetMinLogLevel()`).
bool VoidifyingLogHandler(int severity,
                          const char* /*file*/,
                          int /*line*/,
                          size_t /*message_start*/,
                          const std::string& /*str*/) {
  return severity < kLogSeverity;
}

struct Environment {
  Environment() {
    // Discard all log messages, including the syslog ones, below the threshold.
    logging::SetMinLogLevel(kLogSeverity);
    logging::SetSyslogLoggingForTesting(/*logging_enabled=*/false);
    logging::SetLogMessageHandler(&VoidifyingLogHandler);

    base::CommandLine::Init(0, nullptr);
    TestTimeouts::Initialize();
    CHECK(scoped_temp_dir.CreateUniqueTempDir());
    CHECK(base::PathService::Override(chrome::DIR_USER_DATA,
                                      scoped_temp_dir.GetPath()));
    CHECK(base::i18n::InitializeICU());

    ui::RegisterPathProvider();

    base::FilePath ui_test_pak_path =
        base::PathService::CheckedGet(ui::UI_TEST_PAK);
    ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);

    base::FilePath pak_path = base::PathService::CheckedGet(base::DIR_ASSETS);
    ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
        pak_path.AppendASCII("components_tests_resources.pak"),
        ui::kScaleFactorNone);
  }

  ~Environment() { ui::ResourceBundle::CleanupSharedInstance(); }

  base::ScopedTempDir scoped_temp_dir;
  base::AtExitManager exit_manager;
};

struct PerInputEnvironment {
  PerInputEnvironment() {
    policy_handler_list = BuildHandlerList(GetChromeSchema());
    ash::InitializeDBus();
    ash::InitializeFeatureListDependentDBus();
  }

  ~PerInputEnvironment() {
    ash::ShutdownDBus();
    ash::InstallAttributes::Shutdown();
    ash::DeviceSettingsService::Shutdown();
    ash::attestation::AttestationFeatures::Shutdown();
  }

  base::test::TaskEnvironment task_environment;
  std::unique_ptr<ConfigurationPolicyHandlerList> policy_handler_list;
};

void CheckPolicyMap(const PolicyMap& policy_map,
                    PolicyScope expected_policy_scope,
                    bool expected_is_device_policy) {
  for (const auto& it : policy_map) {
    const std::string& policy_name = it.first;
    const PolicyMap::Entry& entry = it.second;
    CHECK(entry.value_unsafe())
        << "Policy " << policy_name << " has an empty value";
    CHECK_EQ(entry.scope, expected_policy_scope)
        << "Policy " << policy_name << " has wrong scope";

    const PolicyDetails* policy_details = GetChromePolicyDetails(policy_name);
    CHECK(policy_details) << "Policy " << policy_name
                          << " has no policy details";
    if (expected_is_device_policy) {
      CHECK_EQ(policy_details->scope, kDevice)
        << "Policy " << policy_name << " should be device policy";
    } else {
      CHECK_NE(policy_details->scope, kDevice)
        << "Policy " << policy_name << " should not be device policy";
    }
  }
}

void CheckPolicyToPrefTranslation(const PolicyMap& policy_map,
                                  const PerInputEnvironment& per_input_env) {
  PrefValueMap prefs;
  PolicyErrorMap errors;
  PoliciesSet deprecated_policies;
  PoliciesSet future_policies;
  per_input_env.policy_handler_list->ApplyPolicySettings(
      policy_map, &prefs, &errors, &deprecated_policies, &future_policies);
}

void CheckPolicyToCrosSettingsTranslation(
    const enterprise_management::ChromeDeviceSettingsProto&
        chrome_device_settings) {
  PrefValueMap cros_settings_prefs;
  ash::DeviceSettingsProvider::DecodePolicies(chrome_device_settings,
                                              &cros_settings_prefs);

  for (const auto& it : cros_settings_prefs) {
    const std::string& pref_name = it.first;
    CHECK(ash::DeviceSettingsProvider::IsDeviceSetting(pref_name));
  }
}

}  // namespace

DEFINE_PROTO_FUZZER(const PolicyFuzzerProto& proto) {
  static Environment env;
  PerInputEnvironment per_input_env;

  if (proto.has_chrome_device_settings()) {
    const enterprise_management::ChromeDeviceSettingsProto&
        chrome_device_settings = proto.chrome_device_settings();
    base::WeakPtr<ExternalDataManager> data_manager;
    PolicyMap policy_map;
    DecodeDevicePolicy(chrome_device_settings, data_manager, &policy_map);

    CheckPolicyMap(policy_map, POLICY_SCOPE_MACHINE,
                   /*expected_is_device_policy=*/true);
    CheckPolicyToPrefTranslation(policy_map, per_input_env);
    CheckPolicyToCrosSettingsTranslation(chrome_device_settings);
  }

  if (proto.has_cloud_policy_settings()) {
    const enterprise_management::CloudPolicySettings& cloud_policy_settings =
        proto.cloud_policy_settings();
    base::WeakPtr<CloudExternalDataManager> cloud_data_manager;
    PolicyMap policy_map;
    DecodeProtoFields(cloud_policy_settings, cloud_data_manager,
                      PolicySource::POLICY_SOURCE_CLOUD,
                      PolicyScope::POLICY_SCOPE_USER, &policy_map,
                      PolicyPerProfileFilter::kAny);

    CheckPolicyMap(policy_map, POLICY_SCOPE_USER,
                   /*expected_is_device_policy=*/false);
    CheckPolicyToPrefTranslation(policy_map, per_input_env);
  }
}

}  // namespace policy