Bug 1346620 - Resolve symlinks and junction points in path to GMP dir when loading GMP process. r=bobowen
The sandbox blocks loading of GMPs when the GMP resides in a directory stored
in a path which contains a symlink or junction point. So resolve GMP paths
fully before instantiating the GMP process.
MozReview-Commit-ID: EvPCpNIDNwg
--- a/dom/media/gmp/GMPProcessParent.cpp
+++ b/dom/media/gmp/GMPProcessParent.cpp
@@ -6,16 +6,17 @@
#include "GMPProcessParent.h"
#include "GMPUtils.h"
#include "nsIFile.h"
#include "nsIRunnable.h"
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
#include "WinUtils.h"
#endif
+#include "GMPLog.h"
#include "base/string_util.h"
#include "base/process_util.h"
#include <string>
using std::vector;
using std::string;
@@ -46,20 +47,23 @@ GMPProcessParent::Launch(int32_t aTimeou
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
std::wstring wGMPPath = UTF8ToWide(mGMPPath.c_str());
// The sandbox doesn't allow file system rules where the paths contain
// symbolic links or junction points. Sometimes the Users folder has been
// moved to another drive using a junction point, so allow for this specific
// case. See bug 1236680 for details.
- if (!widget::WinUtils::ResolveMovedUsersFolder(wGMPPath)) {
- NS_WARNING("ResolveMovedUsersFolder failed for GMP path.");
+ if (!widget::WinUtils::ResolveJunctionPointsAndSymLinks(wGMPPath)) {
+ GMP_LOG("ResolveJunctionPointsAndSymLinks failed for GMP path=%S",
+ wGMPPath.c_str());
+ NS_WARNING("ResolveJunctionPointsAndSymLinks failed for GMP path.");
return false;
}
+ GMP_LOG("GMPProcessParent::Launch() resolved path to %S", wGMPPath.c_str());
// If the GMP path is a network path that is not mapped to a drive letter,
// then we need to fix the path format for the sandbox rule.
wchar_t volPath[MAX_PATH];
if (::GetVolumePathNameW(wGMPPath.c_str(), volPath, MAX_PATH) &&
::GetDriveTypeW(volPath) == DRIVE_REMOTE &&
wGMPPath.compare(0, 2, L"\\\\") == 0) {
std::wstring sandboxGMPPath(wGMPPath);
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -1804,121 +1804,51 @@ uint32_t
WinUtils::GetMaxTouchPoints()
{
if (IsTouchDeviceSupportPresent()) {
return GetSystemMetrics(SM_MAXIMUMTOUCHES);
}
return 0;
}
-#pragma pack(push, 1)
-typedef struct REPARSE_DATA_BUFFER {
- ULONG ReparseTag;
- USHORT ReparseDataLength;
- USHORT Reserved;
- union {
- struct {
- USHORT SubstituteNameOffset;
- USHORT SubstituteNameLength;
- USHORT PrintNameOffset;
- USHORT PrintNameLength;
- ULONG Flags;
- WCHAR PathBuffer[1];
- } SymbolicLinkReparseBuffer;
- struct {
- USHORT SubstituteNameOffset;
- USHORT SubstituteNameLength;
- USHORT PrintNameOffset;
- USHORT PrintNameLength;
- WCHAR PathBuffer[1];
- } MountPointReparseBuffer;
- struct {
- UCHAR DataBuffer[1];
- } GenericReparseBuffer;
- };
-} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
-#pragma pack(pop)
-
/* static */
bool
-WinUtils::ResolveMovedUsersFolder(std::wstring& aPath)
+WinUtils::ResolveJunctionPointsAndSymLinks(std::wstring& aPath)
{
- wchar_t* usersPath;
- if (FAILED(SHGetKnownFolderPath(FOLDERID_UserProfiles, 0, nullptr,
- &usersPath))) {
- return false;
- }
+ wchar_t path[MAX_PATH] = { 0 };
- // Ensure usersPath gets freed properly.
- UniquePtr<wchar_t, CoTaskMemFreePolicy> autoFreePath(usersPath);
+ nsAutoHandle handle(
+ ::CreateFileW(aPath.c_str(),
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ nullptr));
- // Is aPath in Users folder?
- size_t usersLen = wcslen(usersPath);
- if (_wcsnicmp(aPath.c_str(), usersPath, usersLen) != 0 ||
- aPath[usersLen] != L'\\') {
- return true;
- }
-
- DWORD attributes = ::GetFileAttributesW(usersPath);
- if (attributes == INVALID_FILE_ATTRIBUTES) {
+ if (handle == INVALID_HANDLE_VALUE) {
return false;
}
- // Junction points are implemented as reparse points, is the Users folder one?
- if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
- return true;
+ DWORD pathLen = GetFinalPathNameByHandleW(
+ handle, path, MAX_PATH, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
+ if (pathLen == 0 || pathLen >= MAX_PATH) {
+ return false;
}
+ aPath = path;
- // Get the reparse point data.
- nsAutoHandle usersHandle(
- ::CreateFileW(usersPath, 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr, OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
- nullptr));
-
- char maxReparseBuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] = {0};
- REPARSE_DATA_BUFFER* reparseBuf = (REPARSE_DATA_BUFFER*)maxReparseBuf;
- DWORD bytesReturned = 0;
- if (!::DeviceIoControl(usersHandle, FSCTL_GET_REPARSE_POINT, nullptr, 0,
- reparseBuf, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
- &bytesReturned, nullptr)) {
- return false;
+ // GetFinalPathNameByHandle sticks a '\\?\' in front of the path,
+ // but that confuses some APIs so strip it off. It will also put
+ // '\\?\UNC\' in front of network paths, we convert that to '\\'.
+ if (aPath.compare(0, 7, L"\\\\?\\UNC") == 0) {
+ aPath.erase(2, 6);
+ } else if (aPath.compare(0, 4, L"\\\\?\\") == 0) {
+ aPath.erase(0, 4);
}
- // Check to see if the reparse point is a junction point.
- if (reparseBuf->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
- return true;
- }
-
- // The offset and length are in bytes. Length doesn't include null.
- wchar_t* substituteName = reparseBuf->MountPointReparseBuffer.PathBuffer +
- reparseBuf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t);
- std::wstring::size_type substituteLen =
- reparseBuf->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
-
- // If the substitute path starts with the NT namespace then remove it.
- if (wcsncmp(substituteName, kNTPrefix, kNTPrefixLen) == 0) {
- substituteName += kNTPrefixLen;
- substituteLen -= kNTPrefixLen;
- }
-
- // Check that what remains looks like a drive letter path.
- if (substituteName[1] != L':' || substituteName[2] != L'\\') {
- return false;
- }
-
- // The documentation for SHGetKnownFolderPath says that it doesn't return a
- // trailing backslash. The REPARSE_DATA_BUFFER path doesn't seem to have one
- // either, but the documentation doesn't mention it, so let's make sure.
- if (substituteName[substituteLen - 1] == L'\\') {
- --substituteLen;
- }
-
- aPath.replace(0, usersLen, substituteName, substituteLen);
return true;
}
/* static */
bool
WinUtils::SanitizePath(const wchar_t* aInputPath, nsAString& aOutput)
{
aOutput.Truncate();
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -458,26 +458,26 @@ public:
* The maximum number of simultaneous touch contacts supported by the device.
* In the case of devices with multiple digitizers (e.g. multiple touch screens),
* the value will be the maximum of the set of maximum supported contacts by
* each individual digitizer.
*/
static uint32_t GetMaxTouchPoints();
/**
- * Detect if path is within the Users folder and Users is actually a junction
- * point to another folder.
- * If this is detected it will change the path to the actual path.
+ * Fully resolves a path to its final path name. So if path contains
+ * junction points or symlinks to other folders, we'll resolve the path
+ * fully to the actual path that the links target.
*
* @param aPath path to be resolved.
* @return true if successful, including if nothing needs to be changed.
* false if something failed or aPath does not exist, aPath will
* remain unchanged.
*/
- static bool ResolveMovedUsersFolder(std::wstring& aPath);
+ static bool ResolveJunctionPointsAndSymLinks(std::wstring& aPath);
static void Initialize();
static bool ShouldHideScrollbars();
/**
* This function normalizes the input path, converts short filenames to long
* filenames, and substitutes environment variables for system paths.