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:
@@ -29,6 +29,8 @@ inline constexpr char kWidevineExperimentKeySystemName[] =
|
||||
// hardware decryption with codecs that support clear lead.
|
||||
inline constexpr char kWidevineExperiment2KeySystemName[] =
|
||||
"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
|
||||
inline constexpr char kPlayReadyKeySystemName[] =
|
||||
|
||||
@@ -1106,8 +1106,8 @@ RefPtr<GenericPromise> GMPParent::ParseChromiumManifest(
|
||||
mAdapter = u"chromium"_ns;
|
||||
#ifdef MOZ_WMF_CDM
|
||||
} else if (mPluginType == GMPPluginType::WidevineL1) {
|
||||
video.mAPIName = "windows-mf-cdm"_ns;
|
||||
mAdapter = u"windows-mf-cdm"_ns;
|
||||
video.mAPIName = nsCString(kWidevineExperimentAPIName);
|
||||
mAdapter = NS_ConvertUTF8toUTF16(kWidevineExperimentAPIName);
|
||||
#endif
|
||||
} else {
|
||||
GMP_PARENT_LOG_DEBUG("%s: CDM API not supported, failing.", __FUNCTION__);
|
||||
@@ -1232,7 +1232,7 @@ void GMPParent::UpdatePluginType() {
|
||||
if (mDisplayName.EqualsLiteral("WidevineCdm")) {
|
||||
mPluginType = GMPPluginType::Widevine;
|
||||
#ifdef MOZ_WMF_CDM
|
||||
} else if (mDisplayName.EqualsLiteral("windows-mf-cdm")) {
|
||||
} else if (mDisplayName.EqualsLiteral(kWidevineExperimentAPIName)) {
|
||||
mPluginType = GMPPluginType::WidevineL1;
|
||||
#endif
|
||||
} else if (mDisplayName.EqualsLiteral("gmpopenh264")) {
|
||||
|
||||
@@ -350,6 +350,13 @@ GeckoMediaPluginServiceChild::HasPluginForAPI(const nsACString& aAPI,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GeckoMediaPluginServiceChild::FindPluginDirectoryForAPI(
|
||||
const nsACString& aAPI, const nsTArray<nsCString>& aTags,
|
||||
nsIFile** aDirectory) {
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GeckoMediaPluginServiceChild::GetNodeId(
|
||||
const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
|
||||
|
||||
@@ -35,6 +35,9 @@ class GeckoMediaPluginServiceChild : public GeckoMediaPluginService,
|
||||
NS_IMETHOD HasPluginForAPI(const nsACString& aAPI,
|
||||
const nsTArray<nsCString>& aTags,
|
||||
bool* aRetVal) override;
|
||||
NS_IMETHOD FindPluginDirectoryForAPI(const nsACString& aAPI,
|
||||
const nsTArray<nsCString>& aTags,
|
||||
nsIFile** aDirectory) override;
|
||||
NS_IMETHOD GetNodeId(const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
const nsAString& aGMPName,
|
||||
|
||||
@@ -904,6 +904,33 @@ GeckoMediaPluginServiceParent::HasPluginForAPI(const nsACString& aAPI,
|
||||
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() {
|
||||
const char* env = nullptr;
|
||||
if (!mScannedPluginOnDisk && (env = PR_GetEnv("MOZ_GMP_PATH")) && *env) {
|
||||
|
||||
@@ -51,6 +51,9 @@ class GeckoMediaPluginServiceParent final
|
||||
NS_IMETHOD HasPluginForAPI(const nsACString& aAPI,
|
||||
const nsTArray<nsCString>& aTags,
|
||||
bool* aRetVal) override;
|
||||
NS_IMETHOD FindPluginDirectoryForAPI(const nsACString& aAPI,
|
||||
const nsTArray<nsCString>& aTags,
|
||||
nsIFile** aDirectory) override;
|
||||
NS_IMETHOD GetNodeId(const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
const nsAString& aGMPName,
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIThread.idl"
|
||||
|
||||
interface nsIFile;
|
||||
|
||||
%{C++
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsTArray.h"
|
||||
@@ -77,6 +79,13 @@ interface mozIGeckoMediaPluginService : nsISupports
|
||||
[noscript]
|
||||
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.
|
||||
* The array of tags should at least contain a codec tag, and optionally
|
||||
|
||||
@@ -28,6 +28,15 @@
|
||||
# include "mozilla/WinDllServices.h"
|
||||
#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 "mozilla/PProfilerChild.h"
|
||||
|
||||
@@ -36,6 +45,12 @@ namespace mozilla::ipc {
|
||||
LazyLogModule gUtilityProcessLog("utilityproc");
|
||||
#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)
|
||||
bool UtilityProcessHost::sLaunchWithMacSandbox = false;
|
||||
#endif
|
||||
@@ -91,6 +106,10 @@ bool UtilityProcessHost::Launch(StringVector aExtraOpts) {
|
||||
mSandboxLevel = Preferences::GetInt("security.sandbox.utility.level");
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX)
|
||||
EnsureWidevineL1PathForSandbox();
|
||||
#endif
|
||||
|
||||
mLaunchPhase = LaunchPhase::Waiting;
|
||||
|
||||
if (!GeckoChildProcessHost::AsyncLaunch(aExtraOpts)) {
|
||||
@@ -336,4 +355,54 @@ MacSandboxType UtilityProcessHost::GetMacSandboxType() {
|
||||
}
|
||||
#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
|
||||
|
||||
@@ -133,6 +133,10 @@ class UtilityProcessHost final : public mozilla::ipc::GeckoChildProcessHost {
|
||||
void RejectPromise();
|
||||
void ResolvePromise();
|
||||
|
||||
#if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX)
|
||||
void EnsureWidevineL1PathForSandbox();
|
||||
#endif
|
||||
|
||||
// Set to true on construction and to false just prior deletion.
|
||||
// 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
|
||||
|
||||
@@ -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
|
||||
// reason and yet the LPAC permission is already granted. So returning success
|
||||
// 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
|
||||
// we probably won't have access to add the permission either way.
|
||||
if (widget::WinUtils::HasPackageIdentity()) {
|
||||
@@ -632,28 +633,28 @@ static void EnsureLpacPermsissionsOnBinDir() {
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE hBinDir =
|
||||
::CreateFileW(sBinDir->get(), WRITE_DAC | READ_CONTROL, 0, NULL,
|
||||
HANDLE hDir = ::CreateFileW(aDir.get(), WRITE_DAC | READ_CONTROL, 0, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (hBinDir == INVALID_HANDLE_VALUE) {
|
||||
LOG_W("Unable to get binary directory handle.");
|
||||
if (hDir == INVALID_HANDLE_VALUE) {
|
||||
LOG_W("Unable to get directory handle for %s",
|
||||
NS_ConvertUTF16toUTF8(aDir).get());
|
||||
return;
|
||||
}
|
||||
|
||||
UniquePtr<HANDLE, CloseHandleDeleter> autoHandleCloser(hBinDir);
|
||||
UniquePtr<HANDLE, CloseHandleDeleter> autoHandleCloser(hDir);
|
||||
PACL pBinDirAcl = nullptr;
|
||||
PSECURITY_DESCRIPTOR pSD = nullptr;
|
||||
DWORD result =
|
||||
::GetSecurityInfo(hBinDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
|
||||
::GetSecurityInfo(hDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
|
||||
nullptr, nullptr, &pBinDirAcl, nullptr, &pSD);
|
||||
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;
|
||||
}
|
||||
|
||||
UniquePtr<VOID, LocalFreeDeleter> autoFreeSecDesc(pSD);
|
||||
if (!pBinDirAcl) {
|
||||
LOG_E("DACL for binary directory was null.");
|
||||
LOG_E("DACL was null for %s", NS_ConvertUTF16toUTF8(aDir).get());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -672,7 +673,8 @@ static void EnsureLpacPermsissionsOnBinDir() {
|
||||
|
||||
PSID aceSID = reinterpret_cast<PSID>(&(pAllowedAce->SidStart));
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -690,13 +692,14 @@ static void EnsureLpacPermsissionsOnBinDir() {
|
||||
}
|
||||
|
||||
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,
|
||||
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() {
|
||||
@@ -731,7 +734,7 @@ static sandbox::ResultCode AddAndConfigureAppContainerProfile(
|
||||
::LoadLibraryW(L"userenv.dll");
|
||||
|
||||
// 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
|
||||
// machine unique name per install.
|
||||
|
||||
@@ -77,6 +77,8 @@ class SandboxBroker : public AbstractSandboxBroker {
|
||||
|
||||
static void Initialize(sandbox::BrokerServices* aBrokerServices);
|
||||
|
||||
static void EnsureLpacPermsissionsOnDir(const nsString& aDir);
|
||||
|
||||
void Shutdown() override {}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user