| // Copyright 2019 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/ash/policy/core/user_policy_manager_builder_ash.h" |
| |
| #include <utility> |
| |
| #include "ash/constants/ash_switches.h" |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/path_service.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/task/post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/ash/policy/active_directory/active_directory_policy_manager.h" |
| #include "chrome/browser/ash/policy/core/browser_policy_connector_chromeos.h" |
| #include "chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h" |
| #include "chrome/browser/ash/policy/core/user_cloud_policy_store_ash.h" |
| #include "chrome/browser/ash/policy/external_data/user_cloud_external_data_manager.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/ash/settings/cros_settings.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/browser_process_platform_part.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/policy/schema_registry_service.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chromeos/dbus/constants/dbus_paths.h" |
| #include "chromeos/dbus/session_manager/session_manager_client.h" |
| #include "chromeos/dbus/userdataauth/cryptohome_misc_client.h" |
| #include "chromeos/tpm/install_attributes.h" |
| #include "components/arc/arc_features.h" |
| #include "components/policy/core/browser/browser_policy_connector.h" |
| #include "components/policy/core/common/cloud/cloud_external_data_manager.h" |
| #include "components/policy/core/common/cloud/device_management_service.h" |
| #include "components/policy/core/common/configuration_policy_provider.h" |
| #include "components/policy/policy_constants.h" |
| #include "components/user_manager/known_user.h" |
| #include "components/user_manager/user.h" |
| #include "components/user_manager/user_manager.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| |
| using user_manager::ProfileRequiresPolicy; |
| namespace policy { |
| |
| using PolicyEnforcement = UserCloudPolicyManagerAsh::PolicyEnforcement; |
| namespace { |
| |
| // Directory under the profile directory where policy-related resources are |
| // stored, see the following constants for details. |
| const base::FilePath::CharType kPolicy[] = FILE_PATH_LITERAL("Policy"); |
| |
| // Directory under kPolicy, in the user's profile dir, where policy for |
| // components is cached. |
| const base::FilePath::CharType kComponentsDir[] = |
| FILE_PATH_LITERAL("Components"); |
| |
| // Directory in which to store external policy data. This is specified relative |
| // to kPolicy. |
| const base::FilePath::CharType kPolicyExternalDataDir[] = |
| FILE_PATH_LITERAL("External Data"); |
| |
| // How long we'll block session initialization to try to refresh policy. |
| constexpr base::TimeDelta kPolicyRefreshTimeout = |
| base::TimeDelta::FromSeconds(10); |
| |
| // Called when the user policy loading fails with a fatal error, and the user |
| // session has to be terminated. |
| void OnUserPolicyFatalError(const AccountId& account_id) { |
| user_manager::UserManager::Get()->SaveForceOnlineSignin( |
| account_id, true /* force_online_signin */); |
| chrome::AttemptUserExit(); |
| } |
| |
| } // namespace |
| |
| void CreateConfigurationPolicyProvider( |
| Profile* profile, |
| bool force_immediate_load, |
| scoped_refptr<base::SequencedTaskRunner> background_task_runner, |
| std::unique_ptr<UserCloudPolicyManagerAsh>* |
| user_cloud_policy_manager_ash_out, |
| std::unique_ptr<ActiveDirectoryPolicyManager>* |
| active_directory_policy_manager_out) { |
| // Clear the two out parameters. Default return will be nullptr for both. |
| *user_cloud_policy_manager_ash_out = nullptr; |
| *active_directory_policy_manager_out = nullptr; |
| |
| // Don't initialize cloud policy for the signin and the lock screen profile. |
| if (!chromeos::ProfileHelper::IsRegularProfile(profile)) { |
| return; |
| } |
| |
| // |user| should never be nullptr except for the signin and lock screen app |
| // profile. This object is created as part of the Profile creation, which |
| // happens right after sign-in. The just-signed-in User is the active user |
| // during that time. |
| const user_manager::User* user = |
| chromeos::ProfileHelper::Get()->GetUserByProfile(profile); |
| CHECK(user); |
| |
| // User policy exists for enterprise accounts: |
| // - For regular cloud-managed users (those who have a GAIA account), a |
| // |UserCloudPolicyManagerAsh| is created here. |
| // - For Active Directory managed users, an |ActiveDirectoryPolicyManager| |
| // is created. |
| // - For device-local accounts, policy is provided by |
| // |DeviceLocalAccountPolicyService|. |
| // For non-enterprise accounts only for users with type USER_TYPE_CHILD |
| // |UserCloudPolicyManagerAsh| is created here. |
| // All other user types do not have user policy. |
| const AccountId& account_id = user->GetAccountId(); |
| if (user->GetType() != user_manager::USER_TYPE_CHILD && |
| BrowserPolicyConnector::IsNonEnterpriseUser(account_id.GetUserEmail())) { |
| DLOG(WARNING) << "No policy loaded for known non-enterprise user"; |
| // Mark this profile as not requiring policy. |
| user_manager::known_user::SetProfileRequiresPolicy( |
| account_id, ProfileRequiresPolicy::kNoPolicyRequired); |
| return; |
| } |
| |
| policy::BrowserPolicyConnectorChromeOS* connector = |
| g_browser_process->platform_part()->browser_policy_connector_chromeos(); |
| bool is_active_directory = false; |
| switch (account_id.GetAccountType()) { |
| case AccountType::UNKNOWN: |
| case AccountType::GOOGLE: |
| // TODO(tnagel): Return nullptr for unknown accounts once AccountId |
| // migration is finished. (KioskAppManager still needs to be migrated.) |
| if (!user->HasGaiaAccount()) { |
| DLOG(WARNING) << "No policy for users without Gaia accounts"; |
| return; |
| } |
| is_active_directory = false; |
| break; |
| case AccountType::ACTIVE_DIRECTORY: |
| // Active Directory users only exist on devices whose install attributes |
| // are locked into Active Directory mode. |
| CHECK(connector->GetInstallAttributes()->IsActiveDirectoryManaged()); |
| is_active_directory = true; |
| break; |
| } |
| |
| const ProfileRequiresPolicy requires_policy_user_property = |
| user_manager::known_user::GetProfileRequiresPolicy(account_id); |
| const base::CommandLine* command_line = |
| base::CommandLine::ForCurrentProcess(); |
| const bool is_stub_user = |
| user_manager::UserManager::Get()->IsStubAccountId(account_id); |
| |
| // If true, we don't know if we've ever checked for policy for this user, so |
| // we need to do a policy check during initialization. This differs from |
| // |policy_required| in that it's OK if the server says we don't have policy. |
| // If this is true, then |policy_required| must be false. |
| const bool policy_check_required = |
| (requires_policy_user_property == ProfileRequiresPolicy::kUnknown) && |
| !is_stub_user && !is_active_directory && |
| !command_line->HasSwitch(chromeos::switches::kProfileRequiresPolicy) && |
| !command_line->HasSwitch( |
| chromeos::switches::kAllowFailedPolicyFetchForTest); |
| |
| // |force_immediate_load| is true during Chrome restart, or during |
| // initialization of stub user profiles when running tests. If we ever get |
| // a Chrome restart before a real user session has been initialized, we should |
| // exit the user session entirely - it means that there was a crash during |
| // profile initialization, and we can't rely on the cached policy being valid |
| // (so can't force immediate load of policy). |
| if (policy_check_required && force_immediate_load) { |
| LOG(ERROR) << "Exiting non-stub session because browser restarted before" |
| << " profile was initialized."; |
| chrome::AttemptUserExit(); |
| return; |
| } |
| |
| // If true, we must load policy for this user - we will abort profile |
| // initialization if we are unable to load policy (say, due to disk errors). |
| // We either read this flag from the known_user database, or from a |
| // command-line flag (required for ephemeral users who are not persisted |
| // in the known_user database). |
| const bool policy_required = |
| !command_line->HasSwitch( |
| chromeos::switches::kAllowFailedPolicyFetchForTest) && |
| (is_active_directory || |
| (requires_policy_user_property == |
| ProfileRequiresPolicy::kPolicyRequired) || |
| (command_line->GetSwitchValueASCII( |
| chromeos::switches::kProfileRequiresPolicy) == "true")); |
| |
| // We should never have |policy_required| and |policy_check_required| both |
| // set, since the |policy_required| implies that we already know that |
| // the user requires policy. |
| CHECK(!(policy_required && policy_check_required)); |
| |
| // Determine whether we need to enforce policy load or not. |
| PolicyEnforcement enforcement_type = PolicyEnforcement::kPolicyOptional; |
| if (policy_required) { |
| enforcement_type = PolicyEnforcement::kPolicyRequired; |
| } else if (policy_check_required) { |
| enforcement_type = PolicyEnforcement::kServerCheckRequired; |
| } |
| |
| // If there's a chance the user might be managed (enforcement_type != |
| // kPolicyOptional) then we can't let the profile complete initialization |
| // until we complete a policy check. |
| // |
| // The only exception is if |force_immediate_load| is true, then we can't |
| // block at all (loading from network is not allowed - only from cache). In |
| // this case, logic in UserCloudPolicyManagerAsh will exit the session |
| // if we fail to load policy from our cache. |
| const bool block_profile_init_on_policy_refresh = |
| (enforcement_type != PolicyEnforcement::kPolicyOptional) && |
| !force_immediate_load && !is_stub_user; |
| |
| // If OAuth token is required for policy refresh for child user we should not |
| // block signin. Policy refresh will fail without the token that is available |
| // only after profile initialization. |
| const bool policy_refresh_requires_oauth_token = |
| user->GetType() == user_manager::USER_TYPE_CHILD && |
| base::FeatureList::IsEnabled(features::kDMServerOAuthForChildUser); |
| |
| base::TimeDelta policy_refresh_timeout; |
| if (block_profile_init_on_policy_refresh && |
| enforcement_type == PolicyEnforcement::kPolicyRequired && |
| !policy_refresh_requires_oauth_token) { |
| // We already have policy, so block signin for a short period to check |
| // for a policy update, so we can pick up any important policy changes |
| // that can't easily change on the fly (like changes to the startup tabs). |
| // We can fallback to the cached policy if we can't access the policy |
| // server. |
| policy_refresh_timeout = kPolicyRefreshTimeout; |
| } |
| |
| DeviceManagementService* device_management_service = |
| connector->device_management_service(); |
| if (block_profile_init_on_policy_refresh) |
| device_management_service->ScheduleInitialization(0); |
| |
| base::FilePath profile_dir = profile->GetPath(); |
| const base::FilePath component_policy_cache_dir = |
| profile_dir.Append(kPolicy).Append(kComponentsDir); |
| const base::FilePath external_data_dir = |
| profile_dir.Append(kPolicy).Append(kPolicyExternalDataDir); |
| const base::FilePath policy_key_dir = |
| base::PathService::CheckedGet(chromeos::dbus_paths::DIR_USER_POLICY_KEYS); |
| |
| std::unique_ptr<UserCloudPolicyStoreAsh> store = |
| std::make_unique<UserCloudPolicyStoreAsh>( |
| chromeos::CryptohomeMiscClient::Get(), |
| chromeos::SessionManagerClient::Get(), background_task_runner, |
| account_id, policy_key_dir, is_active_directory); |
| |
| scoped_refptr<base::SequencedTaskRunner> backend_task_runner = |
| base::ThreadPool::CreateSequencedTaskRunner( |
| {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); |
| std::unique_ptr<CloudExternalDataManager> external_data_manager( |
| new UserCloudExternalDataManager( |
| base::BindRepeating(&GetChromePolicyDetails), backend_task_runner, |
| external_data_dir, store.get())); |
| if (force_immediate_load) |
| store->LoadImmediately(); |
| |
| if (is_active_directory) { |
| auto manager = std::make_unique<UserActiveDirectoryPolicyManager>( |
| account_id, policy_required, policy_refresh_timeout, |
| base::BindOnce(&OnUserPolicyFatalError, account_id), std::move(store), |
| std::move(external_data_manager)); |
| manager->Init(profile->GetPolicySchemaRegistryService()->registry()); |
| *active_directory_policy_manager_out = std::move(manager); |
| } else { |
| std::unique_ptr<UserCloudPolicyManagerAsh> manager = |
| std::make_unique<UserCloudPolicyManagerAsh>( |
| profile, std::move(store), std::move(external_data_manager), |
| component_policy_cache_dir, enforcement_type, |
| policy_refresh_timeout, |
| base::BindOnce(&OnUserPolicyFatalError, account_id), account_id, |
| base::ThreadTaskRunnerHandle::Get()); |
| |
| bool wildcard_match = false; |
| if (connector->IsDeviceEnterpriseManaged() && |
| ash::CrosSettings::Get()->IsUserAllowlisted( |
| account_id.GetUserEmail(), &wildcard_match, user->GetType()) && |
| wildcard_match && |
| !connector->IsNonEnterpriseUser(account_id.GetUserEmail())) { |
| manager->EnableWildcardLoginCheck(account_id.GetUserEmail()); |
| } |
| |
| manager->Init(profile->GetPolicySchemaRegistryService()->registry()); |
| manager->Connect(g_browser_process->local_state(), |
| device_management_service, |
| g_browser_process->shared_url_loader_factory()); |
| *user_cloud_policy_manager_ash_out = std::move(manager); |
| } |
| } |
| |
| } // namespace policy |