chromium/chrome/browser/ash/policy/core/user_cloud_policy_store_ash.cc

// Copyright 2012 The Chromium Authors
// 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_cloud_policy_store_ash.h"

#include <utility>

#include "base/debug/dump_without_crashing.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "chrome/browser/ash/crosapi/browser_manager.h"
#include "chrome/browser/ash/policy/core/cached_policy_key_loader.h"
#include "chrome/browser/ash/policy/value_validation/onc_user_policy_value_validator.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
#include "chromeos/ash/components/cryptohome/cryptohome_util.h"
#include "chromeos/ash/components/dbus/session_manager/policy_descriptor.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "google_apis/gaia/gaia_auth_util.h"

using RetrievePolicyResponseType =
    ash::SessionManagerClient::RetrievePolicyResponseType;

namespace em = enterprise_management;

namespace policy {

namespace {

// Extracts the domain name from the passed username.
std::string ExtractDomain(const std::string& username) {
  return gaia::ExtractDomainName(gaia::CanonicalizeEmail(username));
}

}  // namespace

UserCloudPolicyStoreAsh::UserCloudPolicyStoreAsh(
    ash::CryptohomeMiscClient* cryptohome_misc_client,
    ash::SessionManagerClient* session_manager_client,
    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
    const AccountId& account_id,
    const base::FilePath& user_policy_key_dir)
    : UserCloudPolicyStoreBase(background_task_runner,
                               PolicyScope::POLICY_SCOPE_USER),
      session_manager_client_(session_manager_client),
      account_id_(account_id),
      cached_policy_key_loader_(
          std::make_unique<CachedPolicyKeyLoader>(cryptohome_misc_client,
                                                  background_task_runner,
                                                  account_id,
                                                  user_policy_key_dir)) {}

UserCloudPolicyStoreAsh::~UserCloudPolicyStoreAsh() {}

void UserCloudPolicyStoreAsh::Store(const em::PolicyFetchResponse& policy) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // Cancel all pending requests.
  weak_factory_.InvalidateWeakPtrs();

  std::unique_ptr<em::PolicyFetchResponse> response(
      new em::PolicyFetchResponse(policy));
  cached_policy_key_loader_->EnsurePolicyKeyLoaded(
      base::BindOnce(&UserCloudPolicyStoreAsh::ValidatePolicyForStore,
                     weak_factory_.GetWeakPtr(), std::move(response)));
}

void UserCloudPolicyStoreAsh::Load() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // Cancel all pending requests.
  weak_factory_.InvalidateWeakPtrs();

  login_manager::PolicyDescriptor descriptor =
      ash::MakeChromePolicyDescriptor(login_manager::ACCOUNT_TYPE_USER,
                                      cryptohome::GetCryptohomeId(account_id_));
  session_manager_client_->RetrievePolicy(
      descriptor, base::BindOnce(&UserCloudPolicyStoreAsh::OnPolicyRetrieved,
                                 weak_factory_.GetWeakPtr()));
}

std::unique_ptr<UserCloudPolicyValidator>
UserCloudPolicyStoreAsh::CreateValidator(
    std::unique_ptr<em::PolicyFetchResponse> policy,
    CloudPolicyValidatorBase::ValidateTimestampOption option) {
  auto validator =
      UserCloudPolicyStoreBase::CreateValidator(std::move(policy), option);
  validator->ValidateValues(std::make_unique<ONCUserPolicyValueValidator>());
  return validator;
}

void UserCloudPolicyStoreAsh::LoadImmediately() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // This blocking D-Bus call is in the startup path and will block the UI
  // thread. This only happens when the Profile is created synchronously, which
  // on Chrome OS happens whenever the browser is restarted into the same
  // session. That happens when the browser crashes, or right after signin if
  // the user has flags configured in about:flags.
  // However, on those paths we must load policy synchronously so that the
  // Profile initialization never sees unmanaged prefs, which would lead to
  // data loss. http://crbug.com/263061
  std::string policy_blob;
  login_manager::PolicyDescriptor descriptor =
      ash::MakeChromePolicyDescriptor(login_manager::ACCOUNT_TYPE_USER,
                                      cryptohome::GetCryptohomeId(account_id_));
  RetrievePolicyResponseType response_type =
      session_manager_client_->BlockingRetrievePolicy(descriptor, &policy_blob);

  if (response_type == RetrievePolicyResponseType::GET_SERVICE_FAIL) {
    LOG(ERROR)
        << "Session manager claims that session doesn't exist; signing out";
    base::debug::DumpWithoutCrashing();
    chrome::AttemptUserExit();
    return;
  }

  if (policy_blob.empty()) {
    // The session manager doesn't have policy, or the call failed.
    NotifyStoreLoaded();
    return;
  }

  std::unique_ptr<em::PolicyFetchResponse> policy(
      new em::PolicyFetchResponse());
  if (!policy->ParseFromString(policy_blob)) {
    status_ = STATUS_PARSE_ERROR;
    NotifyStoreError();
    return;
  }

  if (!cached_policy_key_loader_->LoadPolicyKeyImmediately()) {
    status_ = STATUS_LOAD_ERROR;
    NotifyStoreError();
    return;
  }

  std::unique_ptr<UserCloudPolicyValidator> validator =
      CreateValidatorForLoad(std::move(policy));
  validator->RunValidation();
  OnRetrievedPolicyValidated(validator.get());
}

void UserCloudPolicyStoreAsh::ValidatePolicyForStore(
    std::unique_ptr<em::PolicyFetchResponse> policy) {
  // Create and configure a validator.
  std::unique_ptr<UserCloudPolicyValidator> validator = CreateValidator(
      std::move(policy), CloudPolicyValidatorBase::TIMESTAMP_VALIDATED);
  validator->ValidateUser(account_id_);
  const std::string& cached_policy_key =
      cached_policy_key_loader_->cached_policy_key();
  if (cached_policy_key.empty()) {
    validator->ValidateInitialKey(ExtractDomain(account_id_.GetUserEmail()));
  } else {
    validator->ValidateSignatureAllowingRotation(
        cached_policy_key, ExtractDomain(account_id_.GetUserEmail()));
  }

  // Start validation.
  UserCloudPolicyValidator::StartValidation(
      std::move(validator),
      base::BindOnce(&UserCloudPolicyStoreAsh::OnPolicyToStoreValidated,
                     weak_factory_.GetWeakPtr()));
}

void UserCloudPolicyStoreAsh::OnPolicyToStoreValidated(
    UserCloudPolicyValidator* validator) {
  validation_result_ = validator->GetValidationResult();
  if (!validator->success()) {
    status_ = STATUS_VALIDATION_ERROR;
    NotifyStoreError();
    return;
  }

  std::string policy_blob;
  if (!validator->policy()->SerializeToString(&policy_blob)) {
    status_ = STATUS_SERIALIZE_ERROR;
    NotifyStoreError();
    return;
  }

  session_manager_client_->StorePolicyForUser(
      cryptohome::CreateAccountIdentifierFromAccountId(account_id_),
      policy_blob,
      base::BindOnce(&UserCloudPolicyStoreAsh::OnPolicyStored,
                     weak_factory_.GetWeakPtr()));
}

void UserCloudPolicyStoreAsh::OnPolicyStored(bool success) {
  if (!success) {
    status_ = STATUS_STORE_ERROR;
    NotifyStoreError();
  } else {
    // Load the policy right after storing it, to make sure it was accepted by
    // the session manager. An additional validation is performed after the
    // load; reload the key for that validation too, in case it was rotated.
    cached_policy_key_loader_->ReloadPolicyKey(base::BindOnce(
        &UserCloudPolicyStoreAsh::Load, weak_factory_.GetWeakPtr()));
  }
}

void UserCloudPolicyStoreAsh::OnPolicyRetrieved(
    RetrievePolicyResponseType response_type,
    const std::string& policy_blob) {
  // Disallow the sign in when the Chrome OS user session has not started, which
  // should always happen before the profile construction. An attempt to read
  // the policy outside the session will always fail and return an empty policy
  // blob.
  if (response_type == RetrievePolicyResponseType::GET_SERVICE_FAIL) {
    LOG(ERROR)
        << "Session manager claims that session doesn't exist; signing out";
    base::debug::DumpWithoutCrashing();
    chrome::AttemptUserExit();
    return;
  }

  if (policy_blob.empty()) {
    // session_manager doesn't have policy. Adjust internal state and notify
    // the world about the policy update.
    ResetPolicy();
    policy_map_.Clear();
    policy_signature_public_key_.clear();
    NotifyStoreLoaded();
    return;
  }

  std::unique_ptr<em::PolicyFetchResponse> policy(
      new em::PolicyFetchResponse());
  if (!policy->ParseFromString(policy_blob)) {
    status_ = STATUS_PARSE_ERROR;
    NotifyStoreError();
    return;
  }

  // Load |cached_policy_key_| to verify the loaded policy.
  cached_policy_key_loader_->EnsurePolicyKeyLoaded(
      base::BindOnce(&UserCloudPolicyStoreAsh::ValidateRetrievedPolicy,
                     weak_factory_.GetWeakPtr(), std::move(policy)));
}

void UserCloudPolicyStoreAsh::ValidateRetrievedPolicy(
    std::unique_ptr<em::PolicyFetchResponse> policy) {
  UserCloudPolicyValidator::StartValidation(
      CreateValidatorForLoad(std::move(policy)),
      base::BindOnce(&UserCloudPolicyStoreAsh::OnRetrievedPolicyValidated,
                     weak_factory_.GetWeakPtr()));
}

void UserCloudPolicyStoreAsh::OnRetrievedPolicyValidated(
    UserCloudPolicyValidator* validator) {
  validation_result_ = validator->GetValidationResult();
  if (!validator->success()) {
    status_ = STATUS_VALIDATION_ERROR;
    NotifyStoreError();
    return;
  }

  InstallPolicy(std::move(validator->policy()),
                std::move(validator->policy_data()),
                std::move(validator->payload()),
                cached_policy_key_loader_->cached_policy_key());
  status_ = STATUS_OK;

  NotifyStoreLoaded();
}

std::unique_ptr<UserCloudPolicyValidator>
UserCloudPolicyStoreAsh::CreateValidatorForLoad(
    std::unique_ptr<em::PolicyFetchResponse> policy) {
  std::unique_ptr<UserCloudPolicyValidator> validator = CreateValidator(
      std::move(policy), CloudPolicyValidatorBase::TIMESTAMP_VALIDATED);
  validator->ValidateUser(account_id_);
  // The policy loaded from session manager need not be validated using the
  // verification key since it is secure, and since there may be legacy policy
  // data that was stored without a verification key.
  validator->ValidateSignature(cached_policy_key_loader_->cached_policy_key());
  return validator;
}

}  // namespace policy