blob: 664452b85e1deadc8d101d620a125563da1f795f [file] [log] [blame]
// Copyright (c) 2017 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/pre_signin_policy_fetcher.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "base/sequenced_task_runner.h"
#include "base/task_scheduler/post_task.h"
#include "base/task_scheduler/task_traits.h"
#include "base/time/time.h"
#include "chromeos/chromeos_paths.h"
#include "chromeos/cryptohome/homedir_methods.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "google_apis/gaia/gaia_auth_util.h"
namespace em = enterprise_management;
namespace policy {
namespace {
// We will abort fresh policy fetch after this time and use cached policy.
const int kPolicyFetchTimeoutSecs = 10;
// Traits for the tasks posted in pre-signin policy fetch. As this blocks
// signin, the tasks have user-visible priority.
constexpr base::TaskTraits kTaskTraits = {base::MayBlock(),
base::TaskPriority::USER_VISIBLE};
} // namespace
PreSigninPolicyFetcher::PreSigninPolicyFetcher(
chromeos::CryptohomeClient* cryptohome_client,
chromeos::SessionManagerClient* session_manager_client,
std::unique_ptr<CloudPolicyClient> cloud_policy_client,
bool is_active_directory_managed,
const AccountId& account_id,
const cryptohome::KeyDefinition& auth_key)
: cryptohome_client_(cryptohome_client),
session_manager_client_(session_manager_client),
cloud_policy_client_(std::move(cloud_policy_client)),
is_active_directory_managed_(is_active_directory_managed),
account_id_(account_id),
auth_key_(auth_key),
task_runner_(base::CreateSequencedTaskRunnerWithTraits(kTaskTraits)),
weak_ptr_factory_(this) {
DCHECK(account_id_.GetAccountType() != AccountType::ACTIVE_DIRECTORY ||
is_active_directory_managed_);
}
PreSigninPolicyFetcher ::~PreSigninPolicyFetcher() {}
void PreSigninPolicyFetcher::FetchPolicy(PolicyFetchResultCallback callback) {
DCHECK(callback_.is_null());
callback_ = std::move(callback);
cryptohome::AuthorizationRequest auth;
cryptohome::Key* key = auth.mutable_key();
if (!auth_key_.label.empty()) {
key->mutable_data()->set_label(auth_key_.label);
}
key->set_secret(auth_key_.secret);
cryptohome::MountRequest mount;
mount.set_hidden_mount(true);
cryptohome::HomedirMethods::GetInstance()->MountEx(
cryptohome::Identification(account_id_), auth, mount,
base::Bind(&PreSigninPolicyFetcher::OnMountTemporaryUserHome,
weak_ptr_factory_.GetWeakPtr()));
}
bool PreSigninPolicyFetcher::ForceTimeoutForTesting() {
if (!policy_fetch_timeout_.IsRunning())
return false;
policy_fetch_timeout_.Stop();
OnPolicyFetchTimeout();
return true;
}
void PreSigninPolicyFetcher::OnMountTemporaryUserHome(
bool success,
cryptohome::MountError return_code,
const std::string& mount_hash) {
if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) {
LOG(ERROR) << "Temporary user home mount failed.";
NotifyCallback(PolicyFetchResult::ERROR, nullptr);
return;
}
session_manager_client_->RetrievePolicyForUserWithoutSession(
cryptohome::Identification(account_id_),
base::Bind(&PreSigninPolicyFetcher::OnCachedPolicyRetrieved,
weak_ptr_factory_.GetWeakPtr()));
}
void PreSigninPolicyFetcher::OnCachedPolicyRetrieved(
RetrievePolicyResponseType retrieve_policy_response,
const std::string& policy_blob) {
// We only need the cached policy key if there was policy and if the device is
// not joined to Active Directory (policy blobs from Active Directory servers
// are not signed).
if (!policy_blob.empty() && !is_active_directory_managed_) {
base::FilePath policy_key_dir;
CHECK(PathService::Get(chromeos::DIR_USER_POLICY_KEYS, &policy_key_dir));
cached_policy_key_loader_ = base::MakeUnique<CachedPolicyKeyLoaderChromeOS>(
cryptohome_client_, task_runner_, account_id_, policy_key_dir);
cached_policy_key_loader_->EnsurePolicyKeyLoaded(base::Bind(
&PreSigninPolicyFetcher::OnPolicyKeyLoaded,
weak_ptr_factory_.GetWeakPtr(), retrieve_policy_response, policy_blob));
} else {
// Skip and pretend we've loaded policy key. We won't need it anyway,
// because there is no policy to validate or because it's not signed (Active
// Directory).
OnPolicyKeyLoaded(retrieve_policy_response, policy_blob);
}
}
void PreSigninPolicyFetcher::OnPolicyKeyLoaded(
RetrievePolicyResponseType retrieve_policy_response,
const std::string& policy_blob) {
cryptohome_client_->Unmount(base::BindOnce(
&PreSigninPolicyFetcher::OnUnmountTemporaryUserHome,
weak_ptr_factory_.GetWeakPtr(), retrieve_policy_response, policy_blob));
}
void PreSigninPolicyFetcher::OnUnmountTemporaryUserHome(
RetrievePolicyResponseType retrieve_policy_response,
const std::string& policy_blob,
base::Optional<bool> unmount_success) {
if (!unmount_success.has_value() || !unmount_success.value()) {
// The temporary userhome mount could not be unmounted. Log an error and
// continue, and hope that the unmount will be successful on the next mount
// (temporary user homes are automatically unmounted by cryptohomed on every
// mount request).
LOG(ERROR) << "Couldn't unmount temporary mount point";
}
if (retrieve_policy_response != RetrievePolicyResponseType::SUCCESS) {
NotifyCallback(PolicyFetchResult::ERROR, nullptr);
return;
}
if (policy_blob.empty()) {
VLOG(1) << "No cached policy.";
NotifyCallback(PolicyFetchResult::NO_POLICY, nullptr);
return;
}
// Parse policy.
auto policy = base::MakeUnique<em::PolicyFetchResponse>();
if (!policy->ParseFromString(policy_blob)) {
NotifyCallback(PolicyFetchResult::ERROR, nullptr);
return;
}
// Before validating, check that we have a cached policy key. This does not
// apply to Active Directory joined devices (cached policy is unsigned there).
if (!is_active_directory_managed_ &&
cached_policy_key_loader_->cached_policy_key().empty()) {
LOG(ERROR) << "No cached policy key loaded.";
NotifyCallback(PolicyFetchResult::ERROR, nullptr);
return;
}
// Validate policy from session_manager.
UserCloudPolicyValidator::StartValidation(
CreateValidatorForCachedPolicy(std::move(policy)),
base::Bind(&PreSigninPolicyFetcher::OnCachedPolicyValidated,
weak_ptr_factory_.GetWeakPtr()));
}
void PreSigninPolicyFetcher::OnCachedPolicyValidated(
UserCloudPolicyValidator* validator) {
if (!validator->success()) {
NotifyCallback(PolicyFetchResult::ERROR, nullptr);
return;
}
policy_data_ = std::move(validator->policy_data());
policy_payload_ = std::move(validator->payload());
if (is_active_directory_managed_) {
// For AD, we don't support fresh policy fetch at the moment. Simply exit
// with cached policy.
NotifyCallback(PolicyFetchResult::SUCCESS, std::move(policy_payload_));
return;
}
// Try to retrieve fresh policy.
cloud_policy_client_->SetupRegistration(policy_data_->request_token(),
policy_data_->device_id());
cloud_policy_client_->AddPolicyTypeToFetch(
dm_protocol::kChromeUserPolicyType,
std::string() /* settings_entity_id */);
if (policy_data_->has_public_key_version()) {
cloud_policy_client_->set_public_key_version(
policy_data_->public_key_version());
}
cloud_policy_client_->AddObserver(this);
// Start a timer that will limit how long we wait for fresh policy.
policy_fetch_timeout_.Start(
FROM_HERE, base::TimeDelta::FromSeconds(kPolicyFetchTimeoutSecs),
base::Bind(&PreSigninPolicyFetcher::OnPolicyFetchTimeout,
weak_ptr_factory_.GetWeakPtr()));
cloud_policy_client_->FetchPolicy();
}
void PreSigninPolicyFetcher::OnPolicyFetched(CloudPolicyClient* client) {
policy_fetch_timeout_.Stop();
const em::PolicyFetchResponse* fetched_policy =
cloud_policy_client_->GetPolicyFor(
dm_protocol::kChromeUserPolicyType,
std::string() /* settings_entity_id */);
if (!fetched_policy || !fetched_policy->has_policy_data()) {
// policy_payload_ still holds cached policy loaded from session_manager.
NotifyCallback(PolicyFetchResult::SUCCESS, std::move(policy_payload_));
return;
}
// Make a copy because there's currently no way to transfer ownership out of
// CloudPolicyClient.
auto fetched_policy_copy =
base::MakeUnique<em::PolicyFetchResponse>(*fetched_policy);
// Validate fresh policy.
UserCloudPolicyValidator::StartValidation(
CreateValidatorForFetchedPolicy(std::move(fetched_policy_copy)),
base::Bind(&PreSigninPolicyFetcher::OnFetchedPolicyValidated,
weak_ptr_factory_.GetWeakPtr()));
}
void PreSigninPolicyFetcher::OnRegistrationStateChanged(
CloudPolicyClient* client) {
// Ignored.
}
void PreSigninPolicyFetcher::OnClientError(CloudPolicyClient* client) {
policy_fetch_timeout_.Stop();
VLOG(1) << "Fresh policy fetch failed.";
// policy_payload_ still holds cached policy loaded from session_manager.
NotifyCallback(PolicyFetchResult::SUCCESS, std::move(policy_payload_));
}
void PreSigninPolicyFetcher::OnPolicyFetchTimeout() {
VLOG(1) << "Fresh policy fetch timed out.";
// Invalidate all weak pointrs so OnPolicyFetched is not called back anymore.
weak_ptr_factory_.InvalidateWeakPtrs();
NotifyCallback(PolicyFetchResult::SUCCESS, std::move(policy_payload_));
}
void PreSigninPolicyFetcher::OnFetchedPolicyValidated(
UserCloudPolicyValidator* validator) {
if (!validator->success()) {
VLOG(1) << "Validation of fetched policy failed.";
// Return the cached policy.
NotifyCallback(PolicyFetchResult::SUCCESS, std::move(policy_payload_));
return;
}
policy_data_ = std::move(validator->policy_data());
policy_payload_ = std::move(validator->payload());
NotifyCallback(PolicyFetchResult::SUCCESS, std::move(policy_payload_));
}
void PreSigninPolicyFetcher::NotifyCallback(
PolicyFetchResult result,
std::unique_ptr<em::CloudPolicySettings> policy_payload) {
// Clean up instances created during the policy fetch procedure.
cached_policy_key_loader_.reset();
policy_data_.reset();
if (cloud_policy_client_) {
cloud_policy_client_->RemoveObserver(this);
}
DCHECK(callback_);
std::move(callback_).Run(result, std::move(policy_payload));
}
std::unique_ptr<UserCloudPolicyValidator>
PreSigninPolicyFetcher::CreateValidatorForCachedPolicy(
std::unique_ptr<em::PolicyFetchResponse> policy) {
std::unique_ptr<UserCloudPolicyValidator> validator =
UserCloudPolicyValidator::Create(std::move(policy), task_runner_);
validator->ValidatePolicyType(dm_protocol::kChromeUserPolicyType);
validator->ValidatePayload();
if (!is_active_directory_managed_) {
// Also validate the user e-mail and the signature (except for authpolicy).
validator->ValidateUsername(account_id_.GetUserEmail(), true);
validator->ValidateSignature(
cached_policy_key_loader_->cached_policy_key());
}
return validator;
}
std::unique_ptr<UserCloudPolicyValidator>
PreSigninPolicyFetcher::CreateValidatorForFetchedPolicy(
std::unique_ptr<em::PolicyFetchResponse> policy) {
// Configure the validator to validate based on cached policy.
std::unique_ptr<UserCloudPolicyValidator> validator =
UserCloudPolicyValidator::Create(std::move(policy), task_runner_);
validator->ValidatePolicyType(dm_protocol::kChromeUserPolicyType);
validator->ValidateAgainstCurrentPolicy(
policy_data_.get(), CloudPolicyValidatorBase::TIMESTAMP_VALIDATED,
CloudPolicyValidatorBase::DM_TOKEN_REQUIRED,
CloudPolicyValidatorBase::DEVICE_ID_REQUIRED);
validator->ValidatePayload();
if (!is_active_directory_managed_) {
// Also validate the signature.
const std::string domain = gaia::ExtractDomainName(
gaia::CanonicalizeEmail(account_id_.GetUserEmail()));
validator->ValidateSignatureAllowingRotation(
cached_policy_key_loader_->cached_policy_key(), domain);
}
return validator;
}
} // namespace policy