// Copyright (c) 2011 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/policy/device_policy_cache.h" #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/task.h" #include "base/values.h" #include "chrome/browser/chromeos/cros_settings_names.h" #include "chrome/browser/chromeos/login/ownership_service.h" #include "chrome/browser/chromeos/login/signed_settings_helper.h" #include "chrome/browser/chromeos/user_cros_settings_provider.h" #include "chrome/browser/policy/configuration_policy_pref_store.h" #include "chrome/browser/policy/device_policy_identity_strategy.h" #include "chrome/browser/policy/enterprise_install_attributes.h" #include "chrome/browser/policy/policy_map.h" #include "chrome/browser/policy/proto/device_management_backend.pb.h" #include "chrome/browser/policy/proto/device_management_constants.h" #include "chrome/browser/policy/proto/device_management_local.pb.h" #include "content/browser/browser_thread.h" #include "policy/configuration_policy_type.h" namespace { // Stores policy, updates the owner key if required and reports the status // through a callback. class StorePolicyOperation : public chromeos::SignedSettingsHelper::Callback, public chromeos::OwnerManager::KeyUpdateDelegate { public: typedef Callback1<chromeos::SignedSettings::ReturnCode>::Type Callback; StorePolicyOperation(chromeos::SignedSettingsHelper* signed_settings_helper, const em::PolicyFetchResponse& policy, Callback* callback) : signed_settings_helper_(signed_settings_helper), policy_(policy), callback_(callback) { signed_settings_helper_->StartStorePolicyOp(policy, this); } virtual ~StorePolicyOperation() { signed_settings_helper_->CancelCallback(this); } // SignedSettingsHelper implementation: virtual void OnStorePolicyCompleted( chromeos::SignedSettings::ReturnCode code) OVERRIDE { if (code != chromeos::SignedSettings::SUCCESS) { callback_->Run(code); delete this; return; } if (policy_.has_new_public_key()) { // The session manager has successfully done a key rotation. Replace the // owner key also in chrome. const std::string& new_key = policy_.new_public_key(); const std::vector<uint8> new_key_data(new_key.c_str(), new_key.c_str() + new_key.size()); chromeos::OwnershipService::GetSharedInstance()->StartUpdateOwnerKey( new_key_data, this); return; } else { UpdateUserCrosSettings(); callback_->Run(chromeos::SignedSettings::SUCCESS); delete this; return; } } // OwnerManager::KeyUpdateDelegate implementation: virtual void OnKeyUpdated() OVERRIDE { UpdateUserCrosSettings(); callback_->Run(chromeos::SignedSettings::SUCCESS); delete this; } private: void UpdateUserCrosSettings() { // TODO(mnissler): Find a better way. This is a hack that updates the // UserCrosSettingsProvider's cache, since it is unable to notice we've // updated policy information. chromeos::UserCrosSettingsProvider().Reload(); } chromeos::SignedSettingsHelper* signed_settings_helper_; em::PolicyFetchResponse policy_; scoped_ptr<Callback> callback_; DISALLOW_COPY_AND_ASSIGN(StorePolicyOperation); }; // Decodes a protobuf integer to an IntegerValue. The caller assumes ownership // of the return Value*. Returns NULL in case the input value is out of bounds. Value* DecodeIntegerValue(google::protobuf::int64 value) { if (value < std::numeric_limits<int>::min() || value > std::numeric_limits<int>::max()) { LOG(WARNING) << "Integer value " << value << " out of numeric limits, ignoring."; return NULL; } return Value::CreateIntegerValue(static_cast<int>(value)); } } // namespace namespace policy { DevicePolicyCache::DevicePolicyCache( DevicePolicyIdentityStrategy* identity_strategy, EnterpriseInstallAttributes* install_attributes) : identity_strategy_(identity_strategy), install_attributes_(install_attributes), signed_settings_helper_(chromeos::SignedSettingsHelper::Get()), starting_up_(true), ALLOW_THIS_IN_INITIALIZER_LIST(callback_factory_(this)) { } DevicePolicyCache::DevicePolicyCache( DevicePolicyIdentityStrategy* identity_strategy, EnterpriseInstallAttributes* install_attributes, chromeos::SignedSettingsHelper* signed_settings_helper) : identity_strategy_(identity_strategy), install_attributes_(install_attributes), signed_settings_helper_(signed_settings_helper), starting_up_(true), ALLOW_THIS_IN_INITIALIZER_LIST(callback_factory_(this)) { } DevicePolicyCache::~DevicePolicyCache() { signed_settings_helper_->CancelCallback(this); } void DevicePolicyCache::Load() { signed_settings_helper_->StartRetrievePolicyOp(this); } void DevicePolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) { DCHECK(!starting_up_); // Make sure we have an enterprise device. std::string registration_user(install_attributes_->GetRegistrationUser()); if (registration_user.empty()) { LOG(WARNING) << "Refusing to accept policy on non-enterprise device."; InformNotifier(CloudPolicySubsystem::LOCAL_ERROR, CloudPolicySubsystem::POLICY_LOCAL_ERROR); return; } // Check the user this policy is for against the device-locked name. em::PolicyData policy_data; if (!policy_data.ParseFromString(policy.policy_data())) { LOG(WARNING) << "Invalid policy protobuf"; InformNotifier(CloudPolicySubsystem::LOCAL_ERROR, CloudPolicySubsystem::POLICY_LOCAL_ERROR); return; } if (registration_user != policy_data.username()) { LOG(WARNING) << "Refusing policy blob for " << policy_data.username() << " which doesn't match " << registration_user; InformNotifier(CloudPolicySubsystem::LOCAL_ERROR, CloudPolicySubsystem::POLICY_LOCAL_ERROR); return; } set_last_policy_refresh_time(base::Time::NowFromSystemTime()); // Start a store operation. new StorePolicyOperation(signed_settings_helper_, policy, callback_factory_.NewCallback( &DevicePolicyCache::PolicyStoreOpCompleted)); } void DevicePolicyCache::SetUnmanaged() { LOG(WARNING) << "Tried to set DevicePolicyCache to 'unmanaged'!"; // This is not supported for DevicePolicyCache. } void DevicePolicyCache::OnRetrievePolicyCompleted( chromeos::SignedSettings::ReturnCode code, const em::PolicyFetchResponse& policy) { DCHECK(CalledOnValidThread()); if (starting_up_) { starting_up_ = false; if (code == chromeos::SignedSettings::NOT_FOUND || code == chromeos::SignedSettings::KEY_UNAVAILABLE || !policy.has_policy_data()) { InformNotifier(CloudPolicySubsystem::UNENROLLED, CloudPolicySubsystem::NO_DETAILS); return; } em::PolicyData policy_data; if (!policy_data.ParseFromString(policy.policy_data())) { LOG(WARNING) << "Failed to parse PolicyData protobuf."; InformNotifier(CloudPolicySubsystem::LOCAL_ERROR, CloudPolicySubsystem::POLICY_LOCAL_ERROR); return; } if (!policy_data.has_request_token() || policy_data.request_token().empty()) { SetUnmanagedInternal(base::Time::NowFromSystemTime()); InformNotifier(CloudPolicySubsystem::UNMANAGED, CloudPolicySubsystem::NO_DETAILS); // TODO(jkummerow): Reminder: When we want to feed device-wide settings // made by a local owner into this cache, we need to call // SetPolicyInternal() here. return; } if (!policy_data.has_username() || !policy_data.has_device_id()) { InformNotifier(CloudPolicySubsystem::LOCAL_ERROR, CloudPolicySubsystem::POLICY_LOCAL_ERROR); return; } identity_strategy_->SetDeviceManagementCredentials( policy_data.username(), policy_data.device_id(), policy_data.request_token()); SetPolicyInternal(policy, NULL, false); } else { // In other words, starting_up_ == false. if (code != chromeos::SignedSettings::SUCCESS) { if (code == chromeos::SignedSettings::BAD_SIGNATURE) { InformNotifier(CloudPolicySubsystem::LOCAL_ERROR, CloudPolicySubsystem::SIGNATURE_MISMATCH); } else { InformNotifier(CloudPolicySubsystem::LOCAL_ERROR, CloudPolicySubsystem::POLICY_LOCAL_ERROR); } return; } SetPolicyInternal(policy, NULL, false); } } bool DevicePolicyCache::DecodePolicyData(const em::PolicyData& policy_data, PolicyMap* mandatory, PolicyMap* recommended) { em::ChromeDeviceSettingsProto policy; if (!policy.ParseFromString(policy_data.policy_value())) { LOG(WARNING) << "Failed to parse ChromeDeviceSettingsProto."; return false; } DecodeDevicePolicy(policy, mandatory, recommended); return true; } void DevicePolicyCache::PolicyStoreOpCompleted( chromeos::SignedSettings::ReturnCode code) { DCHECK(CalledOnValidThread()); if (code != chromeos::SignedSettings::SUCCESS) { if (code == chromeos::SignedSettings::BAD_SIGNATURE) { InformNotifier(CloudPolicySubsystem::LOCAL_ERROR, CloudPolicySubsystem::SIGNATURE_MISMATCH); } else { InformNotifier(CloudPolicySubsystem::LOCAL_ERROR, CloudPolicySubsystem::POLICY_LOCAL_ERROR); } return; } signed_settings_helper_->StartRetrievePolicyOp(this); } // static void DevicePolicyCache::DecodeDevicePolicy( const em::ChromeDeviceSettingsProto& policy, PolicyMap* mandatory, PolicyMap* recommended) { if (policy.has_policy_refresh_rate()) { const em::DevicePolicyRefreshRateProto container = policy.policy_refresh_rate(); if (container.has_policy_refresh_rate()) { mandatory->Set(kPolicyPolicyRefreshRate, DecodeIntegerValue(container.policy_refresh_rate())); } } if (policy.has_device_proxy_settings()) { const em::DeviceProxySettingsProto container = policy.device_proxy_settings(); if (container.has_proxy_mode()) { recommended->Set(kPolicyProxyMode, Value::CreateStringValue(container.proxy_mode())); } if (container.has_proxy_server()) { recommended->Set(kPolicyProxyServer, Value::CreateStringValue(container.proxy_server())); } if (container.has_proxy_pac_url()) { recommended->Set(kPolicyProxyPacUrl, Value::CreateStringValue(container.proxy_pac_url())); } if (container.has_proxy_bypass_list()) { recommended->Set(kPolicyProxyBypassList, Value::CreateStringValue(container.proxy_bypass_list())); } } } } // namespace policy