blob: 793538d0b00925b0c0395f4fdc69b0b358b561fb [file] [log] [blame]
// Copyright 2016 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/policy/active_directory_policy_manager.h"
#include <string>
#include <utility>
#include "base/logging.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/login/users/affiliation.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/auth_policy_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/login_manager/policy_descriptor.pb.h"
#include "chromeos/tools/variable_expander.h"
#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
#include "components/policy/core/common/cloud/component_cloud_policy_store.h"
#include "components/policy/core/common/policy_bundle.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/core/common/policy_switches.h"
#include "components/policy/policy_constants.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace em = enterprise_management;
namespace policy {
namespace {
// List of policies where variables like ${MACHINE_NAME} should be expanded.
constexpr const char* kPoliciesToExpand[] = {key::kNativePrinters};
// Fetch policy every 90 minutes which matches the Windows default:
// https://technet.microsoft.com/en-us/library/cc940895.aspx
constexpr base::TimeDelta kFetchInterval = base::TimeDelta::FromMinutes(90);
void RunRefreshCallback(base::OnceCallback<void(bool success)> callback,
authpolicy::ErrorType error) {
std::move(callback).Run(error == authpolicy::ERROR_NONE);
}
// Gets the AuthPolicy D-Bus interface.
chromeos::AuthPolicyClient* GetAuthPolicyClient() {
chromeos::DBusThreadManager* thread_manager =
chromeos::DBusThreadManager::Get();
DCHECK(thread_manager);
chromeos::AuthPolicyClient* auth_policy_client =
thread_manager->GetAuthPolicyClient();
DCHECK(auth_policy_client);
return auth_policy_client;
}
bool IsComponentPolicyDisabled() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableComponentCloudPolicy);
}
} // namespace
ActiveDirectoryPolicyManager::~ActiveDirectoryPolicyManager() = default;
void ActiveDirectoryPolicyManager::Init(SchemaRegistry* registry) {
ConfigurationPolicyProvider::Init(registry);
store_->AddObserver(this);
if (!store_->is_initialized()) {
store_->Load();
}
// Does nothing if |store_| hasn't yet initialized.
PublishPolicy();
scheduler_ = std::make_unique<PolicyScheduler>(
base::BindRepeating(&ActiveDirectoryPolicyManager::DoPolicyFetch,
weak_ptr_factory_.GetWeakPtr()),
base::BindRepeating(&ActiveDirectoryPolicyManager::OnPolicyFetched,
weak_ptr_factory_.GetWeakPtr()),
kFetchInterval);
if (external_data_manager_) {
// Use the system network context here instead of a context derived from the
// Profile because Connect() is called before the profile is fully
// initialized (required so we can perform the initial policy load).
// Note: The network context can be null for tests and for device policy.
external_data_manager_->Connect(
g_browser_process->system_network_context_manager()
? g_browser_process->system_network_context_manager()
->GetSharedURLLoaderFactory()
: nullptr);
}
}
void ActiveDirectoryPolicyManager::Shutdown() {
if (external_data_manager_)
external_data_manager_->Disconnect();
extension_policy_service_.reset();
store_->RemoveObserver(this);
ConfigurationPolicyProvider::Shutdown();
}
bool ActiveDirectoryPolicyManager::IsInitializationComplete(
PolicyDomain domain) const {
if (domain == POLICY_DOMAIN_CHROME)
return store_->is_initialized();
if (domain == extension_policy_domain_ && !IsComponentPolicyDisabled()) {
return extension_policy_service_ &&
extension_policy_service_->policy() != nullptr;
}
return true;
}
void ActiveDirectoryPolicyManager::RefreshPolicies() {
scheduler_->ScheduleTaskNow();
}
void ActiveDirectoryPolicyManager::OnStoreLoaded(
CloudPolicyStore* cloud_policy_store) {
DCHECK_EQ(store_.get(), cloud_policy_store);
PublishPolicy();
if (fetch_ever_completed_) {
// Policy is guaranteed to be up to date with the previous fetch result
// because OnPolicyFetched() cancels any potentially running Load()
// operations.
CancelWaitForInitialPolicy();
}
}
void ActiveDirectoryPolicyManager::OnStoreError(
CloudPolicyStore* cloud_policy_store) {
DCHECK_EQ(store_.get(), cloud_policy_store);
// Publish policy (even though it hasn't changed) in order to signal load
// complete on the ConfigurationPolicyProvider interface. Technically, this is
// only required on the first load, but doesn't hurt in any case.
PublishPolicy();
if (fetch_ever_completed_) {
CancelWaitForInitialPolicy();
}
}
ActiveDirectoryPolicyManager::ActiveDirectoryPolicyManager(
std::unique_ptr<CloudPolicyStore> store,
std::unique_ptr<CloudExternalDataManager> external_data_manager,
PolicyDomain extension_policy_domain)
: store_(std::move(store)),
external_data_manager_(std::move(external_data_manager)),
extension_policy_domain_(extension_policy_domain) {
DCHECK(extension_policy_domain_ == POLICY_DOMAIN_EXTENSIONS ||
extension_policy_domain_ == POLICY_DOMAIN_SIGNIN_EXTENSIONS);
}
void ActiveDirectoryPolicyManager::OnComponentActiveDirectoryPolicyUpdated() {
PublishPolicy();
}
void ActiveDirectoryPolicyManager::PublishPolicy() {
if (!store_->is_initialized())
return;
OnPublishPolicy();
std::unique_ptr<PolicyBundle> bundle = std::make_unique<PolicyBundle>();
PolicyMap& policy_map =
bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
policy_map.CopyFrom(store_->policy_map());
if (extension_policy_service_ && extension_policy_service_->policy())
bundle->MergeFrom(*extension_policy_service_->policy());
// Overwrite the source which is POLICY_SOURCE_CLOUD by default.
// TODO(tnagel): Rename CloudPolicyStore to PolicyStore and make the source
// configurable, then drop PolicyMap::SetSourceForAll().
policy_map.SetSourceForAll(POLICY_SOURCE_ACTIVE_DIRECTORY);
SetEnterpriseUsersDefaults(&policy_map);
// Expand e.g. ${MACHINE_NAME} for a selected set of policies.
ExpandVariables(&policy_map);
// Policy is ready, send it off.
UpdatePolicy(std::move(bundle));
}
void ActiveDirectoryPolicyManager::CreateExtensionPolicyService(
PolicyScope scope,
login_manager::PolicyAccountType account_type,
const AccountId& account_id,
SchemaRegistry* schema_registry) {
if (IsComponentPolicyDisabled())
return;
std::string cryptohome_id;
if (!account_id.empty())
cryptohome_id = cryptohome::Identification(account_id).id();
// Create the service for sign-in extensions (device scope) or user profile
// extensions (user scope).
DCHECK(!extension_policy_service_);
extension_policy_service_ =
std::make_unique<ComponentActiveDirectoryPolicyService>(
scope, extension_policy_domain_, account_type, cryptohome_id, this,
schema_registry);
}
void ActiveDirectoryPolicyManager::OnPolicyFetched(bool success) {
fetch_ever_completed_ = true;
// In case of failure try to proceed with cached policy.
if (!success && store()->is_initialized())
CancelWaitForInitialPolicy();
// Load/retrieve independently of success or failure to keep in sync with the
// state in session manager. This cancels any potentially running Load()
// operations thus it is guaranteed that at the next OnStoreLoaded()
// invocation the policy is up-to-date with what was fetched.
store_->Load();
if (extension_policy_service_)
extension_policy_service_->RetrievePolicies();
}
void ActiveDirectoryPolicyManager::ExpandVariables(PolicyMap* policy_map) {
const em::PolicyData* policy = store_->policy();
if (!policy || policy_map->empty())
return;
if (policy->machine_name().empty()) {
LOG(ERROR) << "Cannot expand machine_name (empty string in policy)";
return;
}
// TODO(rsorokin): remove "machine_name" in M72 (see
// https://crbug.com/875876).
chromeos::VariableExpander expander(
{{"MACHINE_NAME", policy->machine_name()},
{"machine_name", policy->machine_name()}});
for (const char* policy_name : kPoliciesToExpand) {
base::Value* value = policy_map->GetMutableValue(policy_name);
if (value) {
if (!expander.ExpandValue(value)) {
LOG(ERROR) << "Failed to expand at least one variable in policy "
<< policy_name;
}
}
}
}
UserActiveDirectoryPolicyManager::UserActiveDirectoryPolicyManager(
const AccountId& account_id,
bool policy_required,
base::TimeDelta initial_policy_fetch_timeout,
base::OnceClosure exit_session,
std::unique_ptr<CloudPolicyStore> store,
std::unique_ptr<CloudExternalDataManager> external_data_manager)
: ActiveDirectoryPolicyManager(
std::move(store),
std::move(external_data_manager),
POLICY_DOMAIN_EXTENSIONS /* extension_policy_domain */),
account_id_(account_id),
policy_required_(policy_required),
waiting_for_initial_policy_fetch_(
!initial_policy_fetch_timeout.is_zero()),
exit_session_(std::move(exit_session)) {
DCHECK(!initial_policy_fetch_timeout.is_max());
// Delaying initialization complete is intended for user policy only.
if (waiting_for_initial_policy_fetch_) {
initial_policy_timeout_.Start(
FROM_HERE, initial_policy_fetch_timeout,
base::Bind(&UserActiveDirectoryPolicyManager::OnBlockingFetchTimeout,
weak_ptr_factory_.GetWeakPtr()));
}
}
UserActiveDirectoryPolicyManager::~UserActiveDirectoryPolicyManager() = default;
void UserActiveDirectoryPolicyManager::Init(SchemaRegistry* registry) {
DCHECK(store()->is_initialized() || waiting_for_initial_policy_fetch_ ||
!policy_required_ /* policy may not be required in tests */);
if (store()->is_initialized() && !store()->has_policy() && policy_required_) {
// Exit the session in case of immediate load if policy is required.
LOG(ERROR) << "Policy from forced immediate load could not be obtained. "
<< "Aborting profile initialization";
if (exit_session_)
std::move(exit_session_).Run();
}
ActiveDirectoryPolicyManager::Init(registry);
// Create the extension policy handler here. This is different from the device
// policy manager, which can't do this in Init() because it needs to wait for
// the sign-in profile's schema registry.
CreateExtensionPolicyService(POLICY_SCOPE_USER,
login_manager::ACCOUNT_TYPE_USER, account_id_,
registry);
}
bool UserActiveDirectoryPolicyManager::IsInitializationComplete(
PolicyDomain domain) const {
if (waiting_for_initial_policy_fetch_)
return false;
return ActiveDirectoryPolicyManager::IsInitializationComplete(domain);
}
void UserActiveDirectoryPolicyManager::ForceTimeoutForTesting() {
DCHECK(initial_policy_timeout_.IsRunning());
// Stop the timer to mimic what happens when a real timer fires, then invoke
// the timer callback directly.
initial_policy_timeout_.Stop();
OnBlockingFetchTimeout();
}
void UserActiveDirectoryPolicyManager::DoPolicyFetch(
PolicyScheduler::TaskCallback callback) {
GetAuthPolicyClient()->RefreshUserPolicy(
account_id_, base::BindOnce(&RunRefreshCallback, std::move(callback)));
}
void UserActiveDirectoryPolicyManager::CancelWaitForInitialPolicy() {
if (!waiting_for_initial_policy_fetch_)
return;
initial_policy_timeout_.Stop();
// If the conditions to continue profile initialization are not met, the user
// session is exited and initialization is not set as completed.
if (!store()->has_policy() && policy_required_) {
// If there's no policy at all (not even cached), but policy is required,
// the user session must not continue.
LOG(ERROR) << "Policy could not be obtained. "
<< "Aborting profile initialization";
if (exit_session_)
std::move(exit_session_).Run();
return;
}
// Set initialization complete.
waiting_for_initial_policy_fetch_ = false;
// Publish policy (even though it hasn't changed) in order to signal load
// complete on the ConfigurationPolicyProvider interface.
PublishPolicy();
}
void UserActiveDirectoryPolicyManager::OnPublishPolicy() {
const em::PolicyData* policy_data = store()->policy();
if (!policy_data)
return;
// Update user affiliation IDs.
chromeos::AffiliationIDSet set_of_user_affiliation_ids(
policy_data->user_affiliation_ids().begin(),
policy_data->user_affiliation_ids().end());
chromeos::ChromeUserManager::Get()->SetUserAffiliation(
account_id_, set_of_user_affiliation_ids);
}
void UserActiveDirectoryPolicyManager::OnBlockingFetchTimeout() {
DCHECK(waiting_for_initial_policy_fetch_);
LOG(WARNING) << "Timed out while waiting for the policy fetch. "
<< "The session will start with the cached policy.";
if ((fetch_ever_completed_ && !store()->is_initialized()) ||
(!fetch_ever_completed_ && !store()->has_policy())) {
// Waiting for store to load if policy was fetched. Or for policy fetch to
// complete if there is no cached policy.
return;
}
CancelWaitForInitialPolicy();
}
DeviceActiveDirectoryPolicyManager::DeviceActiveDirectoryPolicyManager(
std::unique_ptr<CloudPolicyStore> store)
: ActiveDirectoryPolicyManager(
std::move(store),
nullptr /* external_data_manager */,
POLICY_DOMAIN_SIGNIN_EXTENSIONS /* extension_policy_domain */) {}
void DeviceActiveDirectoryPolicyManager::Shutdown() {
ActiveDirectoryPolicyManager::Shutdown();
signin_profile_forwarding_schema_registry_.reset();
}
void DeviceActiveDirectoryPolicyManager::SetSigninProfileSchemaRegistry(
SchemaRegistry* schema_registry) {
DCHECK(!signin_profile_forwarding_schema_registry_);
signin_profile_forwarding_schema_registry_ =
std::make_unique<ForwardingSchemaRegistry>(schema_registry);
CreateExtensionPolicyService(
POLICY_SCOPE_MACHINE, login_manager::ACCOUNT_TYPE_DEVICE,
EmptyAccountId(), signin_profile_forwarding_schema_registry_.get());
}
DeviceActiveDirectoryPolicyManager::~DeviceActiveDirectoryPolicyManager() =
default;
void DeviceActiveDirectoryPolicyManager::DoPolicyFetch(
base::OnceCallback<void(bool success)> callback) {
GetAuthPolicyClient()->RefreshDevicePolicy(
base::BindOnce(&RunRefreshCallback, std::move(callback)));
}
} // namespace policy