chromium/chrome/browser/component_updater/media_foundation_widevine_cdm_component_installer.cc

// Copyright 2021 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/component_updater/media_foundation_widevine_cdm_component_installer.h"

#include <stdint.h>

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/native_library.h"
#include "base/values.h"
#include "base/version.h"
#include "base/win/security_util.h"
#include "base/win/sid.h"
#include "build/build_config.h"
#include "chrome/browser/media/media_foundation_service_monitor.h"
#include "components/component_updater/component_updater_paths.h"
#include "content/public/browser/cdm_registry.h"
#include "content/public/common/cdm_info.h"
#include "media/base/media_switches.h"
#include "media/base/win/mf_feature_checks.h"
#include "media/cdm/win/media_foundation_cdm.h"
#include "sandbox/policy/win/lpac_capability.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"

namespace {

// The SHA256 of the SubjectPublicKeyInfo used to sign the extension.
// The extension id is: neifaoindggfcjicffkgpmnlppeffabd
const uint8_t kMediaFoundationWidevineCdmPublicKeySHA256[32] = {
    0xd4, 0x85, 0x0e, 0x8d, 0x36, 0x65, 0x29, 0x82, 0x55, 0xa6, 0xfc,
    0xdb, 0xff, 0x45, 0x50, 0x13, 0x5f, 0xed, 0x02, 0x65, 0xb5, 0x19,
    0x8e, 0xa0, 0x5b, 0x38, 0x38, 0xc9, 0x32, 0x3a, 0x9f, 0xf1};

base::FilePath GetCdmPath(const base::FilePath& install_dir) {
  return install_dir.AppendASCII(
      base::GetNativeLibraryName(kMediaFoundationWidevineCdmLibraryName));
}

// Name of the Widevine CDM architecture to avoid registering the wrong CDM.
const char kWidevineCdmArch[] =
#if defined(ARCH_CPU_X86)
    "x86";
#elif defined(ARCH_CPU_X86_64)
    "x64";
#elif defined(ARCH_CPU_ARMEL)
    "arm";
#elif defined(ARCH_CPU_ARM64)
    "arm64";
#else
#error This file should only be included for supported architecture.
#endif

}  // namespace

namespace component_updater {

// Allows this component to be disabled via `ComponentUpdatesEnabled` policy.
// See https://chromeenterprise.google/policies/?policy=ComponentUpdatesEnabled
bool MediaFoundationWidevineCdmComponentInstallerPolicy::
    SupportsGroupPolicyEnabledComponentUpdates() const {
  return true;
}

bool MediaFoundationWidevineCdmComponentInstallerPolicy::
    RequiresNetworkEncryption() const {
  return false;
}

// Set permission on `install_dir` so the CDM can be loaded in the LPAC process.
update_client::CrxInstaller::Result
MediaFoundationWidevineCdmComponentInstallerPolicy::OnCustomInstall(
    const base::Value::Dict& manifest,
    const base::FilePath& install_dir) {
  DVLOG(1) << __func__ << ": Set permission on " << install_dir;

  auto sids = base::win::Sid::FromNamedCapabilityVector(
      {sandbox::policy::kMediaFoundationCdmFiles});

  bool success = base::win::GrantAccessToPath(
      install_dir, sids, FILE_GENERIC_READ | FILE_GENERIC_EXECUTE,
      CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);

  return update_client::CrxInstaller::Result(
      success ? update_client::InstallError::NONE
              : update_client::InstallError::SET_PERMISSIONS_FAILED);
}

void MediaFoundationWidevineCdmComponentInstallerPolicy::OnCustomUninstall() {}

void MediaFoundationWidevineCdmComponentInstallerPolicy::ComponentReady(
    const base::Version& version,
    const base::FilePath& install_dir,
    base::Value::Dict manifest) {
  VLOG(1) << "Component ready, version " << version.GetString() << " in "
          << install_dir.value();

  VLOG(1) << "Register Media Foundation Widevine CDM";
  content::CdmInfo cdm_info(
      kWidevineKeySystem, content::CdmInfo::Robustness::kHardwareSecure,
      /*capability=*/std::nullopt, /*supports_sub_key_systems=*/false,
      kMediaFoundationWidevineCdmDisplayName, kMediaFoundationWidevineCdmType,
      version, GetCdmPath(install_dir));

  // Ensures MediaFoundationService process is monitored.
  MediaFoundationServiceMonitor::GetInstance();

  // Check whether hardware secure decryption CDM should be disabled.
  if (base::FeatureList::IsEnabled(media::kHardwareSecureDecryptionFallback) &&
      !media::kHardwareSecureDecryptionFallbackPerSite.Get() &&
      MediaFoundationServiceMonitor::
          IsHardwareSecureDecryptionDisabledByPref()) {
    VLOG(1) << "Media Foundation Widevine CDM disabled due to previous errors";
    cdm_info.status = content::CdmInfo::Status::kDisabledByPref;
    base::UmaHistogramBoolean("Media.EME.Widevine.HardwareSecure.Pref", false);
  } else {
    base::UmaHistogramBoolean("Media.EME.Widevine.HardwareSecure.Pref", true);
  }

  content::CdmRegistry::GetInstance()->RegisterCdm(cdm_info);
}

// Called during startup and installation before ComponentReady().
bool MediaFoundationWidevineCdmComponentInstallerPolicy::VerifyInstallation(
    const base::Value::Dict& manifest,
    const base::FilePath& install_dir) const {
  return base::PathExists(GetCdmPath(install_dir));
}

// The relative install directory looks like:
// <user-data-dir>\MediaFoundationWidevineCdm\<arch>.
base::FilePath
MediaFoundationWidevineCdmComponentInstallerPolicy::GetRelativeInstallDir()
    const {
  return base::FilePath::FromUTF8Unsafe(
             kMediaFoundationWidevineCdmBaseDirection)
      .AppendASCII(kWidevineCdmArch);
}

void MediaFoundationWidevineCdmComponentInstallerPolicy::GetHash(
    std::vector<uint8_t>* hash) const {
  hash->assign(std::begin(kMediaFoundationWidevineCdmPublicKeySHA256),
               std::end(kMediaFoundationWidevineCdmPublicKeySHA256));
}

std::string MediaFoundationWidevineCdmComponentInstallerPolicy::GetName()
    const {
  return kMediaFoundationWidevineCdmDisplayName;
}

update_client::InstallerAttributes
MediaFoundationWidevineCdmComponentInstallerPolicy::GetInstallerAttributes()
    const {
  return update_client::InstallerAttributes();
}

void RegisterMediaFoundationWidevineCdmComponent(
    component_updater::ComponentUpdateService* cus) {
  if (media::SupportMediaFoundationEncryptedPlayback()) {
    VLOG(1) << "Registering Media Foundation Widevine CDM component.";
    auto installer = base::MakeRefCounted<ComponentInstaller>(
        std::make_unique<MediaFoundationWidevineCdmComponentInstallerPolicy>());
    installer->Register(cus, base::NullCallback());
  }
}

}  // namespace component_updater