Bug 1858546 - part1 : ensure LPAC permission on Widevine L1 path. r=bobowen,aosmond

Differential Revision: https://phabricator.services.mozilla.com/D192888
This commit is contained in:
alwu
2023-11-20 21:07:11 +00:00
parent e4474fdf4a
commit fbd8ac28b3
11 changed files with 147 additions and 18 deletions

View File

@@ -29,6 +29,8 @@ inline constexpr char kWidevineExperimentKeySystemName[] =
// hardware decryption with codecs that support clear lead. // hardware decryption with codecs that support clear lead.
inline constexpr char kWidevineExperiment2KeySystemName[] = inline constexpr char kWidevineExperiment2KeySystemName[] =
"com.widevine.alpha.experiment2"; "com.widevine.alpha.experiment2";
// API name used for searching GMP Wideivine L1 plugin.
inline constexpr char kWidevineExperimentAPIName[] = "windows-mf-cdm";
// https://learn.microsoft.com/en-us/playready/overview/key-system-strings // https://learn.microsoft.com/en-us/playready/overview/key-system-strings
inline constexpr char kPlayReadyKeySystemName[] = inline constexpr char kPlayReadyKeySystemName[] =

View File

@@ -1106,8 +1106,8 @@ RefPtr<GenericPromise> GMPParent::ParseChromiumManifest(
mAdapter = u"chromium"_ns; mAdapter = u"chromium"_ns;
#ifdef MOZ_WMF_CDM #ifdef MOZ_WMF_CDM
} else if (mPluginType == GMPPluginType::WidevineL1) { } else if (mPluginType == GMPPluginType::WidevineL1) {
video.mAPIName = "windows-mf-cdm"_ns; video.mAPIName = nsCString(kWidevineExperimentAPIName);
mAdapter = u"windows-mf-cdm"_ns; mAdapter = NS_ConvertUTF8toUTF16(kWidevineExperimentAPIName);
#endif #endif
} else { } else {
GMP_PARENT_LOG_DEBUG("%s: CDM API not supported, failing.", __FUNCTION__); GMP_PARENT_LOG_DEBUG("%s: CDM API not supported, failing.", __FUNCTION__);
@@ -1232,7 +1232,7 @@ void GMPParent::UpdatePluginType() {
if (mDisplayName.EqualsLiteral("WidevineCdm")) { if (mDisplayName.EqualsLiteral("WidevineCdm")) {
mPluginType = GMPPluginType::Widevine; mPluginType = GMPPluginType::Widevine;
#ifdef MOZ_WMF_CDM #ifdef MOZ_WMF_CDM
} else if (mDisplayName.EqualsLiteral("windows-mf-cdm")) { } else if (mDisplayName.EqualsLiteral(kWidevineExperimentAPIName)) {
mPluginType = GMPPluginType::WidevineL1; mPluginType = GMPPluginType::WidevineL1;
#endif #endif
} else if (mDisplayName.EqualsLiteral("gmpopenh264")) { } else if (mDisplayName.EqualsLiteral("gmpopenh264")) {

View File

@@ -350,6 +350,13 @@ GeckoMediaPluginServiceChild::HasPluginForAPI(const nsACString& aAPI,
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
GeckoMediaPluginServiceChild::FindPluginDirectoryForAPI(
const nsACString& aAPI, const nsTArray<nsCString>& aTags,
nsIFile** aDirectory) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP NS_IMETHODIMP
GeckoMediaPluginServiceChild::GetNodeId( GeckoMediaPluginServiceChild::GetNodeId(
const nsAString& aOrigin, const nsAString& aTopLevelOrigin, const nsAString& aOrigin, const nsAString& aTopLevelOrigin,

View File

@@ -35,6 +35,9 @@ class GeckoMediaPluginServiceChild : public GeckoMediaPluginService,
NS_IMETHOD HasPluginForAPI(const nsACString& aAPI, NS_IMETHOD HasPluginForAPI(const nsACString& aAPI,
const nsTArray<nsCString>& aTags, const nsTArray<nsCString>& aTags,
bool* aRetVal) override; bool* aRetVal) override;
NS_IMETHOD FindPluginDirectoryForAPI(const nsACString& aAPI,
const nsTArray<nsCString>& aTags,
nsIFile** aDirectory) override;
NS_IMETHOD GetNodeId(const nsAString& aOrigin, NS_IMETHOD GetNodeId(const nsAString& aOrigin,
const nsAString& aTopLevelOrigin, const nsAString& aTopLevelOrigin,
const nsAString& aGMPName, const nsAString& aGMPName,

View File

@@ -904,6 +904,33 @@ GeckoMediaPluginServiceParent::HasPluginForAPI(const nsACString& aAPI,
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
GeckoMediaPluginServiceParent::FindPluginDirectoryForAPI(
const nsACString& aAPI, const nsTArray<nsCString>& aTags,
nsIFile** aDirectory) {
NS_ENSURE_ARG(!aTags.IsEmpty());
NS_ENSURE_ARG(aDirectory);
nsresult rv = EnsurePluginsOnDiskScanned();
if (NS_FAILED(rv)) {
NS_WARNING("Failed to load GMPs from disk.");
return rv;
}
{
MutexAutoLock lock(mMutex);
nsCString api(aAPI);
size_t index = 0;
RefPtr<GMPParent> gmp = FindPluginForAPIFrom(index, api, aTags, &index);
if (gmp) {
nsCOMPtr<nsIFile> dir = gmp->GetDirectory();
dir.forget(aDirectory);
}
}
return NS_OK;
}
nsresult GeckoMediaPluginServiceParent::EnsurePluginsOnDiskScanned() { nsresult GeckoMediaPluginServiceParent::EnsurePluginsOnDiskScanned() {
const char* env = nullptr; const char* env = nullptr;
if (!mScannedPluginOnDisk && (env = PR_GetEnv("MOZ_GMP_PATH")) && *env) { if (!mScannedPluginOnDisk && (env = PR_GetEnv("MOZ_GMP_PATH")) && *env) {

View File

@@ -51,6 +51,9 @@ class GeckoMediaPluginServiceParent final
NS_IMETHOD HasPluginForAPI(const nsACString& aAPI, NS_IMETHOD HasPluginForAPI(const nsACString& aAPI,
const nsTArray<nsCString>& aTags, const nsTArray<nsCString>& aTags,
bool* aRetVal) override; bool* aRetVal) override;
NS_IMETHOD FindPluginDirectoryForAPI(const nsACString& aAPI,
const nsTArray<nsCString>& aTags,
nsIFile** aDirectory) override;
NS_IMETHOD GetNodeId(const nsAString& aOrigin, NS_IMETHOD GetNodeId(const nsAString& aOrigin,
const nsAString& aTopLevelOrigin, const nsAString& aTopLevelOrigin,
const nsAString& aGMPName, const nsAString& aGMPName,

View File

@@ -6,6 +6,8 @@
#include "nsISupports.idl" #include "nsISupports.idl"
#include "nsIThread.idl" #include "nsIThread.idl"
interface nsIFile;
%{C++ %{C++
#include "mozilla/UniquePtr.h" #include "mozilla/UniquePtr.h"
#include "nsTArray.h" #include "nsTArray.h"
@@ -77,6 +79,13 @@ interface mozIGeckoMediaPluginService : nsISupports
[noscript] [noscript]
boolean hasPluginForAPI(in ACString api, in ConstTagArrayRef tags); boolean hasPluginForAPI(in ACString api, in ConstTagArrayRef tags);
/**
* Get the plugin directory for a plugin that supports the specified tags.
* Callable on any thread
*/
[noscript]
nsIFile findPluginDirectoryForAPI(in ACString api, in ConstTagArrayRef tags);
/** /**
* Get a video decoder that supports the specified tags. * Get a video decoder that supports the specified tags.
* The array of tags should at least contain a codec tag, and optionally * The array of tags should at least contain a codec tag, and optionally

View File

@@ -28,6 +28,15 @@
# include "mozilla/WinDllServices.h" # include "mozilla/WinDllServices.h"
#endif // defined(XP_WIN) #endif // defined(XP_WIN)
#if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX)
# include "GMPServiceParent.h"
# include "mozilla/dom/KeySystemNames.h"
# include "mozilla/MFMediaEngineUtils.h"
# include "mozilla/StaticPrefs_media.h"
# include "nsIFile.h"
# include "sandboxBroker.h"
#endif
#include "ProfilerParent.h" #include "ProfilerParent.h"
#include "mozilla/PProfilerChild.h" #include "mozilla/PProfilerChild.h"
@@ -36,6 +45,12 @@ namespace mozilla::ipc {
LazyLogModule gUtilityProcessLog("utilityproc"); LazyLogModule gUtilityProcessLog("utilityproc");
#define LOGD(...) MOZ_LOG(gUtilityProcessLog, LogLevel::Debug, (__VA_ARGS__)) #define LOGD(...) MOZ_LOG(gUtilityProcessLog, LogLevel::Debug, (__VA_ARGS__))
#if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX)
# define WMF_LOG(msg, ...) \
MOZ_LOG(gMFMediaEngineLog, LogLevel::Debug, \
("UtilityProcessHost=%p, " msg, this, ##__VA_ARGS__))
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX) #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
bool UtilityProcessHost::sLaunchWithMacSandbox = false; bool UtilityProcessHost::sLaunchWithMacSandbox = false;
#endif #endif
@@ -91,6 +106,10 @@ bool UtilityProcessHost::Launch(StringVector aExtraOpts) {
mSandboxLevel = Preferences::GetInt("security.sandbox.utility.level"); mSandboxLevel = Preferences::GetInt("security.sandbox.utility.level");
#endif #endif
#if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX)
EnsureWidevineL1PathForSandbox();
#endif
mLaunchPhase = LaunchPhase::Waiting; mLaunchPhase = LaunchPhase::Waiting;
if (!GeckoChildProcessHost::AsyncLaunch(aExtraOpts)) { if (!GeckoChildProcessHost::AsyncLaunch(aExtraOpts)) {
@@ -336,4 +355,54 @@ MacSandboxType UtilityProcessHost::GetMacSandboxType() {
} }
#endif #endif
#if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX)
void UtilityProcessHost::EnsureWidevineL1PathForSandbox() {
if (mSandbox != SandboxingKind::MF_MEDIA_ENGINE_CDM) {
return;
}
RefPtr<mozilla::gmp::GeckoMediaPluginServiceParent> gmps =
mozilla::gmp::GeckoMediaPluginServiceParent::GetSingleton();
if (NS_WARN_IF(!gmps)) {
WMF_LOG("Failed to get GeckoMediaPluginServiceParent!");
return;
}
if (!StaticPrefs::media_eme_widevine_experiment_enabled()) {
return;
}
static nsString sWidevineL1Path;
if (sWidevineL1Path.IsEmpty()) {
// TODO : install L1 if it's not downloaded yet in bug 1863800.
nsCOMPtr<nsIFile> pluginFile;
if (NS_WARN_IF(NS_FAILED(gmps->FindPluginDirectoryForAPI(
nsCString(kWidevineExperimentAPIName),
{nsCString(kWidevineExperimentKeySystemName)},
getter_AddRefs(pluginFile))))) {
WMF_LOG("Widevine L1 is not installed yet");
return;
}
if (!pluginFile) {
WMF_LOG("No plugin file found!");
return;
}
if (NS_WARN_IF(NS_FAILED(pluginFile->GetTarget(sWidevineL1Path)))) {
WMF_LOG("Failed to get L1 path!");
return;
}
MOZ_ASSERT(!sWidevineL1Path.IsEmpty());
WMF_LOG("Store Widevine L1 path=%s",
NS_ConvertUTF16toUTF8(sWidevineL1Path).get());
}
SandboxBroker::EnsureLpacPermsissionsOnDir(sWidevineL1Path);
}
# undef WMF_LOG
#endif
} // namespace mozilla::ipc } // namespace mozilla::ipc

View File

@@ -133,6 +133,10 @@ class UtilityProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
void RejectPromise(); void RejectPromise();
void ResolvePromise(); void ResolvePromise();
#if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX)
void EnsureWidevineL1PathForSandbox();
#endif
// Set to true on construction and to false just prior deletion. // Set to true on construction and to false just prior deletion.
// The UtilityProcessHost isn't refcounted; so we can capture this by value in // The UtilityProcessHost isn't refcounted; so we can capture this by value in
// lambdas along with a strong reference to mLiveToken and check if that value // lambdas along with a strong reference to mLiveToken and check if that value

View File

@@ -616,7 +616,8 @@ static void HexEncode(const Span<const uint8_t>& aBytes, nsACString& aEncoded) {
// This is left as a void because we might fail to set the permission for some // This is left as a void because we might fail to set the permission for some
// reason and yet the LPAC permission is already granted. So returning success // reason and yet the LPAC permission is already granted. So returning success
// or failure isn't really that useful. // or failure isn't really that useful.
static void EnsureLpacPermsissionsOnBinDir() { /* static */
void SandboxBroker::EnsureLpacPermsissionsOnDir(const nsString& aDir) {
// For MSIX packages we get access through the packageContents capability and // For MSIX packages we get access through the packageContents capability and
// we probably won't have access to add the permission either way. // we probably won't have access to add the permission either way.
if (widget::WinUtils::HasPackageIdentity()) { if (widget::WinUtils::HasPackageIdentity()) {
@@ -632,28 +633,28 @@ static void EnsureLpacPermsissionsOnBinDir() {
return; return;
} }
HANDLE hBinDir = HANDLE hDir = ::CreateFileW(aDir.get(), WRITE_DAC | READ_CONTROL, 0, NULL,
::CreateFileW(sBinDir->get(), WRITE_DAC | READ_CONTROL, 0, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hBinDir == INVALID_HANDLE_VALUE) { if (hDir == INVALID_HANDLE_VALUE) {
LOG_W("Unable to get binary directory handle."); LOG_W("Unable to get directory handle for %s",
NS_ConvertUTF16toUTF8(aDir).get());
return; return;
} }
UniquePtr<HANDLE, CloseHandleDeleter> autoHandleCloser(hBinDir); UniquePtr<HANDLE, CloseHandleDeleter> autoHandleCloser(hDir);
PACL pBinDirAcl = nullptr; PACL pBinDirAcl = nullptr;
PSECURITY_DESCRIPTOR pSD = nullptr; PSECURITY_DESCRIPTOR pSD = nullptr;
DWORD result = DWORD result =
::GetSecurityInfo(hBinDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, ::GetSecurityInfo(hDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
nullptr, nullptr, &pBinDirAcl, nullptr, &pSD); nullptr, nullptr, &pBinDirAcl, nullptr, &pSD);
if (result != ERROR_SUCCESS) { if (result != ERROR_SUCCESS) {
LOG_E("Failed to get DACL for binary directory."); LOG_E("Failed to get DACL for %s", NS_ConvertUTF16toUTF8(aDir).get());
return; return;
} }
UniquePtr<VOID, LocalFreeDeleter> autoFreeSecDesc(pSD); UniquePtr<VOID, LocalFreeDeleter> autoFreeSecDesc(pSD);
if (!pBinDirAcl) { if (!pBinDirAcl) {
LOG_E("DACL for binary directory was null."); LOG_E("DACL was null for %s", NS_ConvertUTF16toUTF8(aDir).get());
return; return;
} }
@@ -672,7 +673,8 @@ static void EnsureLpacPermsissionsOnBinDir() {
PSID aceSID = reinterpret_cast<PSID>(&(pAllowedAce->SidStart)); PSID aceSID = reinterpret_cast<PSID>(&(pAllowedAce->SidStart));
if (::EqualSid(aceSID, lpacFirefoxInstallFilesSid)) { if (::EqualSid(aceSID, lpacFirefoxInstallFilesSid)) {
LOG_D("Firefox install files permission found on binary directory."); LOG_D("Firefox install files permission found on %s",
NS_ConvertUTF16toUTF8(aDir).get());
return; return;
} }
} }
@@ -690,13 +692,14 @@ static void EnsureLpacPermsissionsOnBinDir() {
} }
UniquePtr<ACL, LocalFreeDeleter> autoFreeAcl(newDacl); UniquePtr<ACL, LocalFreeDeleter> autoFreeAcl(newDacl);
if (ERROR_SUCCESS != ::SetSecurityInfo(hBinDir, SE_FILE_OBJECT, if (ERROR_SUCCESS != ::SetSecurityInfo(hDir, SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION, nullptr, DACL_SECURITY_INFORMATION, nullptr,
nullptr, newDacl, nullptr)) { nullptr, newDacl, nullptr)) {
LOG_E("Failed to set new DACL on binary directory."); LOG_E("Failed to set new DACL on %s", NS_ConvertUTF16toUTF8(aDir).get());
} }
LOG_D("Firefox install files permission granted on binary directory."); LOG_D("Firefox install files permission granted on %s",
NS_ConvertUTF16toUTF8(aDir).get());
} }
static bool IsLowPrivilegedAppContainerSupported() { static bool IsLowPrivilegedAppContainerSupported() {
@@ -731,7 +734,7 @@ static sandbox::ResultCode AddAndConfigureAppContainerProfile(
::LoadLibraryW(L"userenv.dll"); ::LoadLibraryW(L"userenv.dll");
// Done during the package string initialization so we only do it once. // Done during the package string initialization so we only do it once.
EnsureLpacPermsissionsOnBinDir(); SandboxBroker::EnsureLpacPermsissionsOnDir(*sBinDir.get());
// This mirrors Edge's use of the exe path for the SHA1 hash to give a // This mirrors Edge's use of the exe path for the SHA1 hash to give a
// machine unique name per install. // machine unique name per install.

View File

@@ -77,6 +77,8 @@ class SandboxBroker : public AbstractSandboxBroker {
static void Initialize(sandbox::BrokerServices* aBrokerServices); static void Initialize(sandbox::BrokerServices* aBrokerServices);
static void EnsureLpacPermsissionsOnDir(const nsString& aDir);
void Shutdown() override {} void Shutdown() override {}
/** /**