blob: 08d326466d94eacdeffa2148b9ebd885b249e82d [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/settings/device_settings_provider.h"
#include <memory.h>
#include <stddef.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/syslog_logging.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/chromeos/policy/off_hours/off_hours_proto_parser.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/device_settings_cache.h"
#include "chrome/browser/chromeos/tpm_firmware_update.h"
#include "chrome/browser/metrics/metrics_reporting_state.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/settings/cros_settings_names.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/schema.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/prefs/pref_service.h"
using google::protobuf::RepeatedField;
using google::protobuf::RepeatedPtrField;
namespace em = enterprise_management;
namespace chromeos {
namespace {
// List of settings handled by the DeviceSettingsProvider.
const char* const kKnownSettings[] = {
kAccountsPrefAllowGuest,
kAccountsPrefAllowNewUser,
kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled,
kAccountsPrefDeviceLocalAccountAutoLoginDelay,
kAccountsPrefDeviceLocalAccountAutoLoginId,
kAccountsPrefDeviceLocalAccountPromptForNetworkWhenOffline,
kAccountsPrefDeviceLocalAccounts,
kAccountsPrefEphemeralUsersEnabled,
kAccountsPrefLoginScreenDomainAutoComplete,
kAccountsPrefShowUserNamesOnSignIn,
kAccountsPrefSupervisedUsersEnabled,
kAccountsPrefTransferSAMLCookies,
kAccountsPrefUsers,
kAllowBluetooth,
kAllowedConnectionTypesForUpdate,
kAllowRedeemChromeOsRegistrationOffers,
kAttestationForContentProtectionEnabled,
kCastReceiverName,
kDeviceAttestationEnabled,
kDeviceDisabled,
kDeviceDisabledMessage,
kDeviceHostnameTemplate,
kDeviceLoginScreenAppInstallList,
kDeviceLoginScreenInputMethods,
kDeviceLoginScreenLocales,
kDeviceOffHours,
kDeviceOwner,
kDevicePrintersAccessMode,
kDevicePrintersBlacklist,
kDevicePrintersConfigurations,
kDevicePrintersWhitelist,
kDeviceQuirksDownloadEnabled,
kDeviceWallpaperImage,
kDisplayRotationDefault,
kExtensionCacheSize,
kHeartbeatEnabled,
kHeartbeatFrequency,
kLoginAuthenticationBehavior,
kLoginVideoCaptureAllowedUrls,
kMinimumRequiredChromeVersion,
kPolicyMissingMitigationMode,
kRebootOnShutdown,
kReleaseChannel,
kReleaseChannelDelegated,
kReportDeviceActivityTimes,
kReportDeviceBootMode,
kReportDeviceHardwareStatus,
kReportDeviceLocation,
kReportDeviceNetworkInterfaces,
kReportDeviceSessionStatus,
kReportDeviceUsers,
kReportDeviceVersionInfo,
kReportOsUpdateStatus,
kReportRunningKioskApp,
kReportUploadFrequency,
kServiceAccountIdentity,
kSignedDataRoamingEnabled,
kStartUpFlags,
kStatsReportingPref,
kSystemLogUploadEnabled,
kSystemTimezonePolicy,
kSystemUse24HourClock,
kTargetVersionPrefix,
kTPMFirmwareUpdateSettings,
kUnaffiliatedArcAllowed,
kUpdateDisabled,
kVariationsRestrictParameter,
kVirtualMachinesAllowed,
kSamlLoginAuthenticationType,
kDeviceAutoUpdateTimeRestrictions,
};
// Decodes a JSON string to a base::Value, and drops unknown properties
// according to a policy schema. |policy_name| is the name of a policy schema
// defined in policy_templates.json. Returns null in case the input is not a
// valid JSON string.
std::unique_ptr<base::Value> DecodeJsonStringAndDropUnknownBySchema(
const std::string& json_string,
const std::string& policy_name) {
std::string error;
std::unique_ptr<base::Value> root = base::JSONReader::ReadAndReturnError(
json_string, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, &error);
if (!root) {
LOG(WARNING) << "Invalid JSON string: " << error << ", ignoring.";
return nullptr;
}
const policy::Schema& schema = g_browser_process->browser_policy_connector()
->GetChromeSchema()
.GetKnownProperty(policy_name);
if (!schema.valid()) {
LOG(WARNING) << "Unknown or invalid policy schema for " << policy_name
<< ".";
return nullptr;
}
std::string error_path;
bool changed = false;
if (!schema.Normalize(root.get(), policy::SCHEMA_ALLOW_UNKNOWN, &error_path,
&error, &changed)) {
LOG(WARNING) << "Invalid policy value for " << policy_name << ": " << error
<< " at " << error_path << ".";
return nullptr;
}
if (changed) {
LOG(WARNING) << "Some properties in " << policy_name
<< " were dropped: " << error << " at " << error_path << ".";
}
return root;
}
void DecodeLoginPolicies(
const em::ChromeDeviceSettingsProto& policy,
PrefValueMap* new_values_cache) {
// For all our boolean settings the following is applicable:
// true is default permissive value and false is safe prohibitive value.
// Exceptions:
// kAccountsPrefEphemeralUsersEnabled has a default value of false.
// kAccountsPrefSupervisedUsersEnabled has a default value of false
// for enterprise devices and true for consumer devices.
// kAccountsPrefTransferSAMLCookies has a default value of false.
if (policy.has_allow_new_users() &&
policy.allow_new_users().has_allow_new_users()) {
if (policy.allow_new_users().allow_new_users()) {
// New users allowed, user whitelist ignored.
new_values_cache->SetBoolean(kAccountsPrefAllowNewUser, true);
} else {
// New users not allowed, enforce user whitelist if present.
new_values_cache->SetBoolean(kAccountsPrefAllowNewUser,
!policy.has_user_whitelist());
}
} else {
// No configured allow-new-users value, enforce whitelist if non-empty.
new_values_cache->SetBoolean(
kAccountsPrefAllowNewUser,
policy.user_whitelist().user_whitelist_size() == 0);
}
new_values_cache->SetBoolean(
kRebootOnShutdown,
policy.has_reboot_on_shutdown() &&
policy.reboot_on_shutdown().has_reboot_on_shutdown() &&
policy.reboot_on_shutdown().reboot_on_shutdown());
new_values_cache->SetBoolean(
kAccountsPrefAllowGuest,
!policy.has_guest_mode_enabled() ||
!policy.guest_mode_enabled().has_guest_mode_enabled() ||
policy.guest_mode_enabled().guest_mode_enabled());
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
bool supervised_users_enabled = false;
if (connector->IsEnterpriseManaged()) {
supervised_users_enabled =
policy.has_supervised_users_settings() &&
policy.supervised_users_settings().has_supervised_users_enabled() &&
policy.supervised_users_settings().supervised_users_enabled();
} else {
supervised_users_enabled =
!policy.has_supervised_users_settings() ||
!policy.supervised_users_settings().has_supervised_users_enabled() ||
policy.supervised_users_settings().supervised_users_enabled();
}
new_values_cache->SetBoolean(
kAccountsPrefSupervisedUsersEnabled, supervised_users_enabled);
new_values_cache->SetBoolean(
kAccountsPrefShowUserNamesOnSignIn,
!policy.has_show_user_names() ||
!policy.show_user_names().has_show_user_names() ||
policy.show_user_names().show_user_names());
new_values_cache->SetBoolean(
kAccountsPrefEphemeralUsersEnabled,
policy.has_ephemeral_users_enabled() &&
policy.ephemeral_users_enabled().has_ephemeral_users_enabled() &&
policy.ephemeral_users_enabled().ephemeral_users_enabled());
std::unique_ptr<base::ListValue> list(new base::ListValue());
const em::UserWhitelistProto& whitelist_proto = policy.user_whitelist();
const RepeatedPtrField<std::string>& whitelist =
whitelist_proto.user_whitelist();
for (RepeatedPtrField<std::string>::const_iterator it = whitelist.begin();
it != whitelist.end(); ++it) {
list->AppendString(*it);
}
new_values_cache->SetValue(kAccountsPrefUsers, std::move(list));
std::unique_ptr<base::ListValue> account_list(new base::ListValue());
const em::DeviceLocalAccountsProto device_local_accounts_proto =
policy.device_local_accounts();
const RepeatedPtrField<em::DeviceLocalAccountInfoProto>& accounts =
device_local_accounts_proto.account();
RepeatedPtrField<em::DeviceLocalAccountInfoProto>::const_iterator entry;
for (entry = accounts.begin(); entry != accounts.end(); ++entry) {
std::unique_ptr<base::DictionaryValue> entry_dict(
new base::DictionaryValue());
if (entry->has_type()) {
if (entry->has_account_id()) {
entry_dict->SetKey(kAccountsPrefDeviceLocalAccountsKeyId,
base::Value(entry->account_id()));
}
entry_dict->SetKey(kAccountsPrefDeviceLocalAccountsKeyType,
base::Value(entry->type()));
if (entry->kiosk_app().has_app_id()) {
entry_dict->SetKey(kAccountsPrefDeviceLocalAccountsKeyKioskAppId,
base::Value(entry->kiosk_app().app_id()));
}
if (entry->kiosk_app().has_update_url()) {
entry_dict->SetKey(kAccountsPrefDeviceLocalAccountsKeyKioskAppUpdateURL,
base::Value(entry->kiosk_app().update_url()));
}
if (entry->android_kiosk_app().has_package_name()) {
entry_dict->SetKey(
chromeos::kAccountsPrefDeviceLocalAccountsKeyArcKioskPackage,
base::Value(entry->android_kiosk_app().package_name()));
}
if (entry->android_kiosk_app().has_class_name()) {
entry_dict->SetKey(
chromeos::kAccountsPrefDeviceLocalAccountsKeyArcKioskClass,
base::Value(entry->android_kiosk_app().class_name()));
}
if (entry->android_kiosk_app().has_action()) {
entry_dict->SetKey(
chromeos::kAccountsPrefDeviceLocalAccountsKeyArcKioskAction,
base::Value(entry->android_kiosk_app().action()));
}
if (entry->android_kiosk_app().has_display_name()) {
entry_dict->SetKey(
chromeos::kAccountsPrefDeviceLocalAccountsKeyArcKioskDisplayName,
base::Value(entry->android_kiosk_app().display_name()));
}
} else if (entry->has_deprecated_public_session_id()) {
// Deprecated public session specification.
entry_dict->SetKey(kAccountsPrefDeviceLocalAccountsKeyId,
base::Value(entry->deprecated_public_session_id()));
entry_dict->SetKey(
kAccountsPrefDeviceLocalAccountsKeyType,
base::Value(policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION));
}
account_list->Append(std::move(entry_dict));
}
new_values_cache->SetValue(kAccountsPrefDeviceLocalAccounts,
std::move(account_list));
if (policy.has_device_local_accounts()) {
if (policy.device_local_accounts().has_auto_login_id()) {
new_values_cache->SetString(
kAccountsPrefDeviceLocalAccountAutoLoginId,
policy.device_local_accounts().auto_login_id());
}
if (policy.device_local_accounts().has_auto_login_delay()) {
new_values_cache->SetInteger(
kAccountsPrefDeviceLocalAccountAutoLoginDelay,
policy.device_local_accounts().auto_login_delay());
}
}
new_values_cache->SetBoolean(
kAccountsPrefDeviceLocalAccountAutoLoginBailoutEnabled,
policy.device_local_accounts().enable_auto_login_bailout());
new_values_cache->SetBoolean(
kAccountsPrefDeviceLocalAccountPromptForNetworkWhenOffline,
policy.device_local_accounts().prompt_for_network_when_offline());
if (policy.has_start_up_flags()) {
std::unique_ptr<base::ListValue> list(new base::ListValue());
const em::StartUpFlagsProto& flags_proto = policy.start_up_flags();
const RepeatedPtrField<std::string>& flags = flags_proto.flags();
for (RepeatedPtrField<std::string>::const_iterator it = flags.begin();
it != flags.end(); ++it) {
list->AppendString(*it);
}
new_values_cache->SetValue(kStartUpFlags, std::move(list));
}
if (policy.has_saml_settings()) {
new_values_cache->SetBoolean(
kAccountsPrefTransferSAMLCookies,
policy.saml_settings().transfer_saml_cookies());
}
// The behavior when policy is not set and when it is set to an empty string
// is the same. Thus lets add policy only if it is set and its value is not
// an empty string.
if (policy.has_login_screen_domain_auto_complete() &&
policy.login_screen_domain_auto_complete()
.has_login_screen_domain_auto_complete() &&
!policy.login_screen_domain_auto_complete()
.login_screen_domain_auto_complete()
.empty()) {
new_values_cache->SetString(kAccountsPrefLoginScreenDomainAutoComplete,
policy.login_screen_domain_auto_complete()
.login_screen_domain_auto_complete());
}
if (policy.has_login_authentication_behavior() &&
policy.login_authentication_behavior()
.has_login_authentication_behavior()) {
new_values_cache->SetInteger(
kLoginAuthenticationBehavior,
policy.login_authentication_behavior().login_authentication_behavior());
}
if (policy.has_login_video_capture_allowed_urls()) {
std::unique_ptr<base::ListValue> list(new base::ListValue());
const em::LoginVideoCaptureAllowedUrlsProto&
login_video_capture_allowed_urls_proto =
policy.login_video_capture_allowed_urls();
for (const auto& value : login_video_capture_allowed_urls_proto.urls()) {
list->AppendString(value);
}
new_values_cache->SetValue(kLoginVideoCaptureAllowedUrls, std::move(list));
}
if (policy.has_device_login_screen_app_install_list()) {
std::unique_ptr<base::ListValue> apps(new base::ListValue);
const em::DeviceLoginScreenAppInstallListProto& proto(
policy.device_login_screen_app_install_list());
for (const auto& app : proto.device_login_screen_app_install_list())
apps->AppendString(app);
new_values_cache->SetValue(kDeviceLoginScreenAppInstallList,
std::move(apps));
}
if (policy.has_login_screen_locales()) {
std::unique_ptr<base::ListValue> locales(new base::ListValue);
const em::LoginScreenLocalesProto& login_screen_locales(
policy.login_screen_locales());
for (const auto& locale : login_screen_locales.login_screen_locales())
locales->AppendString(locale);
new_values_cache->SetValue(kDeviceLoginScreenLocales, std::move(locales));
}
if (policy.has_login_screen_input_methods()) {
std::unique_ptr<base::ListValue> input_methods(new base::ListValue);
const em::LoginScreenInputMethodsProto& login_screen_input_methods(
policy.login_screen_input_methods());
for (const auto& input_method :
login_screen_input_methods.login_screen_input_methods())
input_methods->AppendString(input_method);
new_values_cache->SetValue(kDeviceLoginScreenInputMethods,
std::move(input_methods));
}
if (policy.has_saml_login_authentication_type() &&
policy.saml_login_authentication_type()
.has_saml_login_authentication_type()) {
new_values_cache->SetInteger(kSamlLoginAuthenticationType,
policy.saml_login_authentication_type()
.saml_login_authentication_type());
}
}
void DecodeNetworkPolicies(
const em::ChromeDeviceSettingsProto& policy,
PrefValueMap* new_values_cache) {
// kSignedDataRoamingEnabled has a default value of false.
new_values_cache->SetBoolean(
kSignedDataRoamingEnabled,
policy.has_data_roaming_enabled() &&
policy.data_roaming_enabled().has_data_roaming_enabled() &&
policy.data_roaming_enabled().data_roaming_enabled());
}
void DecodeAutoUpdatePolicies(
const em::ChromeDeviceSettingsProto& policy,
PrefValueMap* new_values_cache) {
if (policy.has_auto_update_settings()) {
const em::AutoUpdateSettingsProto& au_settings_proto =
policy.auto_update_settings();
if (au_settings_proto.has_update_disabled()) {
new_values_cache->SetBoolean(kUpdateDisabled,
au_settings_proto.update_disabled());
}
if (au_settings_proto.has_target_version_prefix()) {
new_values_cache->SetString(kTargetVersionPrefix,
au_settings_proto.target_version_prefix());
}
const RepeatedField<int>& allowed_connection_types =
au_settings_proto.allowed_connection_types();
std::unique_ptr<base::ListValue> list(new base::ListValue());
for (RepeatedField<int>::const_iterator i(allowed_connection_types.begin());
i != allowed_connection_types.end(); ++i) {
list->AppendInteger(*i);
}
if (!list->empty()) {
new_values_cache->SetValue(kAllowedConnectionTypesForUpdate,
std::move(list));
}
if (au_settings_proto.has_disallowed_time_intervals()) {
std::unique_ptr<base::Value> decoded_intervals =
DecodeJsonStringAndDropUnknownBySchema(
au_settings_proto.disallowed_time_intervals(),
"DeviceAutoUpdateTimeRestrictions");
if (decoded_intervals) {
new_values_cache->SetValue(kDeviceAutoUpdateTimeRestrictions,
std::move(decoded_intervals));
}
}
}
}
void DecodeReportingPolicies(
const em::ChromeDeviceSettingsProto& policy,
PrefValueMap* new_values_cache) {
if (policy.has_device_reporting()) {
const em::DeviceReportingProto& reporting_policy =
policy.device_reporting();
if (reporting_policy.has_report_version_info()) {
new_values_cache->SetBoolean(
kReportDeviceVersionInfo,
reporting_policy.report_version_info());
}
if (reporting_policy.has_report_activity_times()) {
new_values_cache->SetBoolean(
kReportDeviceActivityTimes,
reporting_policy.report_activity_times());
}
if (reporting_policy.has_report_boot_mode()) {
new_values_cache->SetBoolean(
kReportDeviceBootMode,
reporting_policy.report_boot_mode());
}
if (reporting_policy.has_report_network_interfaces()) {
new_values_cache->SetBoolean(
kReportDeviceNetworkInterfaces,
reporting_policy.report_network_interfaces());
}
if (reporting_policy.has_report_users()) {
new_values_cache->SetBoolean(
kReportDeviceUsers,
reporting_policy.report_users());
}
if (reporting_policy.has_report_hardware_status()) {
new_values_cache->SetBoolean(
kReportDeviceHardwareStatus,
reporting_policy.report_hardware_status());
}
if (reporting_policy.has_report_session_status()) {
new_values_cache->SetBoolean(
kReportDeviceSessionStatus,
reporting_policy.report_session_status());
}
if (reporting_policy.has_report_os_update_status()) {
new_values_cache->SetBoolean(kReportOsUpdateStatus,
reporting_policy.report_os_update_status());
}
if (reporting_policy.has_report_running_kiosk_app()) {
new_values_cache->SetBoolean(kReportRunningKioskApp,
reporting_policy.report_running_kiosk_app());
}
if (reporting_policy.has_device_status_frequency()) {
new_values_cache->SetInteger(
kReportUploadFrequency,
reporting_policy.device_status_frequency());
}
}
}
void DecodeHeartbeatPolicies(
const em::ChromeDeviceSettingsProto& policy,
PrefValueMap* new_values_cache) {
if (!policy.has_device_heartbeat_settings())
return;
const em::DeviceHeartbeatSettingsProto& heartbeat_policy =
policy.device_heartbeat_settings();
if (heartbeat_policy.has_heartbeat_enabled()) {
new_values_cache->SetBoolean(
kHeartbeatEnabled,
heartbeat_policy.heartbeat_enabled());
}
if (heartbeat_policy.has_heartbeat_frequency()) {
new_values_cache->SetInteger(
kHeartbeatFrequency,
heartbeat_policy.heartbeat_frequency());
}
}
void DecodeGenericPolicies(
const em::ChromeDeviceSettingsProto& policy,
PrefValueMap* new_values_cache) {
if (policy.has_metrics_enabled() &&
policy.metrics_enabled().has_metrics_enabled()) {
new_values_cache->SetBoolean(kStatsReportingPref,
policy.metrics_enabled().metrics_enabled());
} else {
// If the policy is missing, default to reporting enabled on enterprise-
// enrolled devices, c.f. crbug/456186.
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
bool is_enterprise_managed = connector->IsEnterpriseManaged();
new_values_cache->SetBoolean(kStatsReportingPref, is_enterprise_managed);
}
if (!policy.has_release_channel() ||
!policy.release_channel().has_release_channel()) {
// Default to an invalid channel (will be ignored).
new_values_cache->SetString(kReleaseChannel, "");
} else {
new_values_cache->SetString(kReleaseChannel,
policy.release_channel().release_channel());
}
new_values_cache->SetBoolean(
kReleaseChannelDelegated,
policy.has_release_channel() &&
policy.release_channel().has_release_channel_delegated() &&
policy.release_channel().release_channel_delegated());
if (policy.has_system_timezone()) {
if (policy.system_timezone().has_timezone()) {
new_values_cache->SetString(
kSystemTimezonePolicy,
policy.system_timezone().timezone());
}
}
if (policy.has_use_24hour_clock()) {
if (policy.use_24hour_clock().has_use_24hour_clock()) {
new_values_cache->SetBoolean(
kSystemUse24HourClock, policy.use_24hour_clock().use_24hour_clock());
}
}
if (policy.has_allow_redeem_offers() &&
policy.allow_redeem_offers().has_allow_redeem_offers()) {
new_values_cache->SetBoolean(
kAllowRedeemChromeOsRegistrationOffers,
policy.allow_redeem_offers().allow_redeem_offers());
} else {
new_values_cache->SetBoolean(
kAllowRedeemChromeOsRegistrationOffers,
true);
}
if (policy.has_variations_parameter()) {
new_values_cache->SetString(
kVariationsRestrictParameter,
policy.variations_parameter().parameter());
}
new_values_cache->SetBoolean(
kDeviceAttestationEnabled,
policy.attestation_settings().attestation_enabled());
if (policy.has_attestation_settings() &&
policy.attestation_settings().has_content_protection_enabled()) {
new_values_cache->SetBoolean(
kAttestationForContentProtectionEnabled,
policy.attestation_settings().content_protection_enabled());
} else {
new_values_cache->SetBoolean(kAttestationForContentProtectionEnabled, true);
}
if (policy.has_extension_cache_size() &&
policy.extension_cache_size().has_extension_cache_size()) {
new_values_cache->SetInteger(
kExtensionCacheSize,
policy.extension_cache_size().extension_cache_size());
}
if (policy.has_display_rotation_default() &&
policy.display_rotation_default().has_display_rotation_default()) {
new_values_cache->SetInteger(
kDisplayRotationDefault,
policy.display_rotation_default().display_rotation_default());
}
if (policy.has_allow_bluetooth() &&
policy.allow_bluetooth().has_allow_bluetooth()) {
new_values_cache->SetBoolean(kAllowBluetooth,
policy.allow_bluetooth().allow_bluetooth());
} else {
new_values_cache->SetBoolean(kAllowBluetooth, true);
}
if (policy.has_quirks_download_enabled() &&
policy.quirks_download_enabled().has_quirks_download_enabled()) {
new_values_cache->SetBoolean(
kDeviceQuirksDownloadEnabled,
policy.quirks_download_enabled().quirks_download_enabled());
}
if (policy.has_device_wallpaper_image() &&
policy.device_wallpaper_image().has_device_wallpaper_image()) {
const std::string& wallpaper_policy(
policy.device_wallpaper_image().device_wallpaper_image());
std::unique_ptr<base::DictionaryValue> dict_val =
base::DictionaryValue::From(base::JSONReader::Read(wallpaper_policy));
if (dict_val) {
new_values_cache->SetValue(kDeviceWallpaperImage, std::move(dict_val));
} else {
SYSLOG(ERROR) << "Value of wallpaper policy has invalid format: "
<< wallpaper_policy;
}
}
if (policy.has_device_off_hours()) {
auto off_hours_policy = policy::off_hours::ConvertOffHoursProtoToValue(
policy.device_off_hours());
if (off_hours_policy)
new_values_cache->SetValue(kDeviceOffHours, std::move(off_hours_policy));
}
if (policy.has_tpm_firmware_update_settings()) {
new_values_cache->SetValue(kTPMFirmwareUpdateSettings,
tpm_firmware_update::DecodeSettingsProto(
policy.tpm_firmware_update_settings()));
}
if (policy.has_minimum_required_version()) {
const em::MinimumRequiredVersionProto& container(
policy.minimum_required_version());
if (container.has_chrome_version())
new_values_cache->SetString(kMinimumRequiredChromeVersion,
container.chrome_version());
}
if (policy.has_cast_receiver_name()) {
const em::CastReceiverNameProto& container(policy.cast_receiver_name());
if (container.has_name()) {
new_values_cache->SetValue(
kCastReceiverName, std::make_unique<base::Value>(container.name()));
}
}
if (policy.has_unaffiliated_arc_allowed()) {
const em::UnaffiliatedArcAllowedProto& container(
policy.unaffiliated_arc_allowed());
if (container.has_unaffiliated_arc_allowed()) {
new_values_cache->SetValue(
kUnaffiliatedArcAllowed,
std::make_unique<base::Value>(container.unaffiliated_arc_allowed()));
}
}
if (policy.has_network_hostname()) {
const em::NetworkHostnameProto& container(policy.network_hostname());
if (container.has_device_hostname_template() &&
!container.device_hostname_template().empty()) {
new_values_cache->SetString(kDeviceHostnameTemplate,
container.device_hostname_template());
}
}
if (policy.virtual_machines_allowed().has_virtual_machines_allowed()) {
new_values_cache->SetBoolean(
kVirtualMachinesAllowed,
policy.virtual_machines_allowed().virtual_machines_allowed());
} else {
// If the policy is missing, default to false on enterprise-enrolled
// devices.
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
if (connector->IsEnterpriseManaged()) {
new_values_cache->SetBoolean(kVirtualMachinesAllowed, false);
}
}
}
void DecodeLogUploadPolicies(const em::ChromeDeviceSettingsProto& policy,
PrefValueMap* new_values_cache) {
if (!policy.has_device_log_upload_settings())
return;
const em::DeviceLogUploadSettingsProto& log_upload_policy =
policy.device_log_upload_settings();
if (log_upload_policy.has_system_log_upload_enabled()) {
new_values_cache->SetBoolean(kSystemLogUploadEnabled,
log_upload_policy.system_log_upload_enabled());
}
}
void DecodeDeviceState(const em::PolicyData& policy_data,
PrefValueMap* new_values_cache) {
if (!policy_data.has_device_state())
return;
const em::DeviceState& device_state = policy_data.device_state();
if (device_state.device_mode() == em::DeviceState::DEVICE_MODE_DISABLED)
new_values_cache->SetBoolean(kDeviceDisabled, true);
if (device_state.has_disabled_state() &&
device_state.disabled_state().has_message()) {
new_values_cache->SetString(kDeviceDisabledMessage,
device_state.disabled_state().message());
}
}
} // namespace
DeviceSettingsProvider::DeviceSettingsProvider(
const NotifyObserversCallback& notify_cb,
DeviceSettingsService* device_settings_service)
: CrosSettingsProvider(notify_cb),
device_settings_service_(device_settings_service),
trusted_status_(TEMPORARILY_UNTRUSTED),
ownership_status_(device_settings_service_->GetOwnershipStatus()),
store_callback_factory_(this) {
device_settings_service_->AddObserver(this);
if (!UpdateFromService()) {
// Make sure we have at least the cache data immediately.
RetrieveCachedData();
}
}
DeviceSettingsProvider::~DeviceSettingsProvider() {
if (device_settings_service_->GetOwnerSettingsService())
device_settings_service_->GetOwnerSettingsService()->RemoveObserver(this);
device_settings_service_->RemoveObserver(this);
}
// static
bool DeviceSettingsProvider::IsDeviceSetting(const std::string& name) {
const char* const* end = kKnownSettings + arraysize(kKnownSettings);
return std::find(kKnownSettings, end, name) != end;
}
// static
void DeviceSettingsProvider::DecodePolicies(
const em::ChromeDeviceSettingsProto& policy,
PrefValueMap* new_values_cache) {
DecodeLoginPolicies(policy, new_values_cache);
DecodeNetworkPolicies(policy, new_values_cache);
DecodeAutoUpdatePolicies(policy, new_values_cache);
DecodeReportingPolicies(policy, new_values_cache);
DecodeHeartbeatPolicies(policy, new_values_cache);
DecodeGenericPolicies(policy, new_values_cache);
DecodeLogUploadPolicies(policy, new_values_cache);
}
void DeviceSettingsProvider::DoSet(const std::string& path,
const base::Value& in_value) {
// Make sure that either the current user is the device owner or the
// device doesn't have an owner yet.
if (!(device_settings_service_->HasPrivateOwnerKey() ||
ownership_status_ == DeviceSettingsService::OWNERSHIP_NONE)) {
LOG(WARNING) << "Changing settings from non-owner, setting=" << path;
// Revert UI change.
NotifyObservers(path);
return;
}
if (!IsDeviceSetting(path)) {
NOTREACHED() << "Try to set unhandled cros setting " << path;
return;
}
if (device_settings_service_->HasPrivateOwnerKey()) {
// Directly set setting through OwnerSettingsService.
ownership::OwnerSettingsService* service =
device_settings_service_->GetOwnerSettingsService();
if (!service->Set(path, in_value)) {
NotifyObservers(path);
return;
}
} else {
// Temporary store new setting in
// |device_settings_|. |device_settings_| will be stored on a disk
// as soon as an ownership of device the will be taken.
OwnerSettingsServiceChromeOS::UpdateDeviceSettings(
path, in_value, device_settings_);
em::PolicyData data;
data.set_username(device_settings_service_->GetUsername());
CHECK(device_settings_.SerializeToString(data.mutable_policy_value()));
// Set the cache to the updated value.
UpdateValuesCache(data, device_settings_, TEMPORARILY_UNTRUSTED);
if (!device_settings_cache::Store(data, g_browser_process->local_state())) {
LOG(ERROR) << "Couldn't store to the temp storage.";
NotifyObservers(path);
return;
}
}
}
void DeviceSettingsProvider::OwnershipStatusChanged() {
DeviceSettingsService::OwnershipStatus new_ownership_status =
device_settings_service_->GetOwnershipStatus();
if (device_settings_service_->GetOwnerSettingsService())
device_settings_service_->GetOwnerSettingsService()->AddObserver(this);
// If the device just became owned, write the settings accumulated in the
// cache to device settings proper. It is important that writing only happens
// in this case, as during normal operation, the contents of the cache should
// never overwrite actual device settings.
if (new_ownership_status == DeviceSettingsService::OWNERSHIP_TAKEN &&
ownership_status_ == DeviceSettingsService::OWNERSHIP_NONE &&
device_settings_service_->HasPrivateOwnerKey()) {
// There shouldn't be any pending writes, since the cache writes are all
// immediate.
DCHECK(!store_callback_factory_.HasWeakPtrs());
trusted_status_ = TEMPORARILY_UNTRUSTED;
// Apply the locally-accumulated device settings on top of the initial
// settings from the service and write back the result.
if (device_settings_service_->device_settings()) {
em::ChromeDeviceSettingsProto new_settings(
*device_settings_service_->device_settings());
new_settings.MergeFrom(device_settings_);
device_settings_.Swap(&new_settings);
}
std::unique_ptr<em::PolicyData> policy(new em::PolicyData());
policy->set_username(device_settings_service_->GetUsername());
CHECK(device_settings_.SerializeToString(policy->mutable_policy_value()));
if (!device_settings_service_->GetOwnerSettingsService()
->CommitTentativeDeviceSettings(std::move(policy))) {
LOG(ERROR) << "Can't store policy";
}
}
ownership_status_ = new_ownership_status;
}
void DeviceSettingsProvider::DeviceSettingsUpdated() {
if (!store_callback_factory_.HasWeakPtrs())
UpdateAndProceedStoring();
}
void DeviceSettingsProvider::OnDeviceSettingsServiceShutdown() {
device_settings_service_ = nullptr;
}
void DeviceSettingsProvider::OnTentativeChangesInPolicy(
const em::PolicyData& policy_data) {
em::ChromeDeviceSettingsProto device_settings;
CHECK(device_settings.ParseFromString(policy_data.policy_value()));
UpdateValuesCache(policy_data, device_settings, TEMPORARILY_UNTRUSTED);
}
void DeviceSettingsProvider::RetrieveCachedData() {
em::PolicyData policy_data;
if (!device_settings_cache::Retrieve(&policy_data,
g_browser_process->local_state()) ||
!device_settings_.ParseFromString(policy_data.policy_value())) {
VLOG(1) << "Can't retrieve temp store, possibly not created yet.";
}
UpdateValuesCache(policy_data, device_settings_, trusted_status_);
}
void DeviceSettingsProvider::UpdateValuesCache(
const em::PolicyData& policy_data,
const em::ChromeDeviceSettingsProto& settings,
TrustedStatus trusted_status) {
PrefValueMap new_values_cache;
// Determine whether device is managed. See PolicyData::management_mode docs
// for details.
bool managed = false;
if (policy_data.has_management_mode()) {
managed =
(policy_data.management_mode() == em::PolicyData::ENTERPRISE_MANAGED);
} else {
managed = policy_data.has_request_token();
}
// If the device is not managed, we set the device owner value.
if (policy_data.has_username() && !managed)
new_values_cache.SetString(kDeviceOwner, policy_data.username());
if (policy_data.has_service_account_identity()) {
new_values_cache.SetString(kServiceAccountIdentity,
policy_data.service_account_identity());
}
DecodePolicies(settings, &new_values_cache);
DecodeDeviceState(policy_data, &new_values_cache);
// Collect all notifications but send them only after we have swapped the
// cache so that if somebody actually reads the cache will be already valid.
std::vector<std::string> notifications;
// Go through the new values and verify in the old ones.
auto iter = new_values_cache.begin();
for (; iter != new_values_cache.end(); ++iter) {
const base::Value* old_value;
if (!values_cache_.GetValue(iter->first, &old_value) ||
!old_value->Equals(iter->second.get())) {
notifications.push_back(iter->first);
}
}
// Now check for values that have been removed from the policy blob.
for (iter = values_cache_.begin(); iter != values_cache_.end(); ++iter) {
const base::Value* value;
if (!new_values_cache.GetValue(iter->first, &value))
notifications.push_back(iter->first);
}
// Swap and notify.
values_cache_.Swap(&new_values_cache);
trusted_status_ = trusted_status;
for (size_t i = 0; i < notifications.size(); ++i)
NotifyObservers(notifications[i]);
}
bool DeviceSettingsProvider::MitigateMissingPolicy() {
// First check if the device has been owned already and if not exit
// immediately.
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
if (connector->GetDeviceMode() != policy::DEVICE_MODE_CONSUMER)
return false;
// If we are here the policy file were corrupted or missing. This can happen
// because we are migrating Pre R11 device to the new secure policies or there
// was an attempt to circumvent policy system. In this case we should populate
// the policy cache with "safe-mode" defaults which should allow the owner to
// log in but lock the device for anyone else until the policy blob has been
// recreated by the session manager.
LOG(ERROR) << "Corruption of the policy data has been detected."
<< "Switching to \"safe-mode\" policies until the owner logs in "
<< "to regenerate the policy data.";
device_settings_.Clear();
device_settings_.mutable_allow_new_users()->set_allow_new_users(true);
device_settings_.mutable_guest_mode_enabled()->set_guest_mode_enabled(true);
em::PolicyData empty_policy_data;
UpdateValuesCache(empty_policy_data, device_settings_, TRUSTED);
values_cache_.SetBoolean(kPolicyMissingMitigationMode, true);
return true;
}
const base::Value* DeviceSettingsProvider::Get(const std::string& path) const {
if (IsDeviceSetting(path)) {
const base::Value* value;
if (values_cache_.GetValue(path, &value))
return value;
} else {
NOTREACHED() << "Trying to get non cros setting.";
}
return NULL;
}
DeviceSettingsProvider::TrustedStatus
DeviceSettingsProvider::PrepareTrustedValues(const base::Closure& cb) {
TrustedStatus status = RequestTrustedEntity();
if (status == TEMPORARILY_UNTRUSTED && !cb.is_null())
callbacks_.push_back(cb);
return status;
}
bool DeviceSettingsProvider::HandlesSetting(const std::string& path) const {
return IsDeviceSetting(path);
}
DeviceSettingsProvider::TrustedStatus
DeviceSettingsProvider::RequestTrustedEntity() {
if (ownership_status_ == DeviceSettingsService::OWNERSHIP_NONE)
return TRUSTED;
return trusted_status_;
}
void DeviceSettingsProvider::UpdateAndProceedStoring() {
// Re-sync the cache from the service.
UpdateFromService();
}
bool DeviceSettingsProvider::UpdateFromService() {
bool settings_loaded = false;
switch (device_settings_service_->status()) {
case DeviceSettingsService::STORE_SUCCESS: {
const em::PolicyData* policy_data =
device_settings_service_->policy_data();
const em::ChromeDeviceSettingsProto* device_settings =
device_settings_service_->device_settings();
if (policy_data && device_settings) {
if (!device_settings_cache::Store(*policy_data,
g_browser_process->local_state())) {
LOG(ERROR) << "Couldn't update the local state cache.";
}
UpdateValuesCache(*policy_data, *device_settings, TRUSTED);
device_settings_ = *device_settings;
settings_loaded = true;
} else {
// Initial policy load is still pending.
trusted_status_ = TEMPORARILY_UNTRUSTED;
}
break;
}
case DeviceSettingsService::STORE_NO_POLICY:
if (MitigateMissingPolicy())
break;
FALLTHROUGH;
case DeviceSettingsService::STORE_KEY_UNAVAILABLE:
VLOG(1) << "No policies present yet, will use the temp storage.";
trusted_status_ = PERMANENTLY_UNTRUSTED;
break;
case DeviceSettingsService::STORE_VALIDATION_ERROR:
case DeviceSettingsService::STORE_INVALID_POLICY:
case DeviceSettingsService::STORE_OPERATION_FAILED:
LOG(ERROR) << "Failed to retrieve cros policies. Reason: "
<< device_settings_service_->status();
trusted_status_ = PERMANENTLY_UNTRUSTED;
break;
}
// Notify the observers we are done.
std::vector<base::Closure> callbacks;
callbacks.swap(callbacks_);
for (size_t i = 0; i < callbacks.size(); ++i)
callbacks[i].Run();
return settings_loaded;
}
} // namespace chromeos