Skip to content

Commit

Permalink
Launch COM server for elevated use via packaged path (#4567)
Browse files Browse the repository at this point in the history
## Change
Leverage the feature that allows launching a packaged process via its
packaged executable location as long as there is a matching alias. This
better ensures that we are launching the correct process.
  • Loading branch information
JohnMcPMS authored Jun 21, 2024
1 parent 38aac87 commit 75803c3
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// <copyright file="ErrorCode.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
Expand All @@ -25,5 +25,10 @@ internal static class ErrorCode
/// Error code for RPC_S_CALL_FAILED.
/// </summary>
public const int RpcCallFailed = unchecked((int)0x800706BE);

/// <summary>
/// Error code for ERROR_PACKAGE_NOT_REGISTERED_FOR_USER.
/// </summary>
public const int PackageNotRegisteredForUser = unchecked((int)0x80073D35);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// <copyright file="ManagementDeploymentFactory.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
Expand Down Expand Up @@ -179,7 +179,7 @@ private static T Create<T>(Type? type, in Guid iid)

if (hr < 0)
{
if (hr == ErrorCode.FileNotFound)
if (hr == ErrorCode.FileNotFound || hr == ErrorCode.PackageNotRegisteredForUser)
{
throw new WinGetIntegrityException(IntegrityCategory.AppInstallerNotInstalled);
}
Expand Down
134 changes: 111 additions & 23 deletions src/WinGetServer/WinGetServerManualActivation_Client.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "WinGetServer.h"
#include "appmodel.h"
#include "Utils.h"

#include <wil/com.h>
Expand All @@ -15,9 +16,12 @@
#include <shlobj_core.h>

#ifdef USE_PROD_WINGET_SERVER
const std::wstring_view s_ServerExePath = L"Microsoft\\WindowsApps\\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\\WindowsPackageManagerServer.exe";
const std::wstring_view s_ServerPackageFamilyName = L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe";
const std::wstring_view s_ServerFileName = L"WindowsPackageManagerServer.exe";
#else
const std::wstring_view s_ServerExePath = L"Microsoft\\WindowsApps\\WinGetDevCLI_8wekyb3d8bbwe\\WindowsPackageManagerServerDev.exe";
const std::wstring_view s_LocalAppDataRelativeServerExePath = L"Microsoft\\WindowsApps\\WinGetDevCLI_8wekyb3d8bbwe\\WindowsPackageManagerServerDev.exe";
const std::wstring_view s_ServerPackageFamilyName = L"WinGetDevCLI_8wekyb3d8bbwe";
const std::wstring_view s_ServerFileName = L"WinGetServer\\WindowsPackageManagerServer.exe";
#endif

_Must_inspect_result_
Expand All @@ -35,13 +39,6 @@ void __RPC_USER MIDL_user_free(_Pre_maybenull_ _Post_invalid_ void* ptr)
}
}

std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id)
{
wil::unique_cotaskmem_string knownFolder = nullptr;
THROW_IF_FAILED(SHGetKnownFolderPath(id, KF_FLAG_NO_ALIAS | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_PACKAGE_REDIRECTION, NULL, &knownFolder));
return knownFolder.get();
}

struct FreeWithRpcStringFree { void operator()(RPC_CSTR* in) { RpcStringFreeA(in); } };
using UniqueRpcString = std::unique_ptr<RPC_CSTR, FreeWithRpcStringFree>;

Expand All @@ -64,23 +61,112 @@ void InitializeRpcBinding()
THROW_HR_IF(HRESULT_FROM_WIN32(status), status != RPC_S_OK);
}

HRESULT LaunchWinGetServerWithManualActivation()
struct ServerProcessLauncher
{
const std::filesystem::path& localAppDataPath = GetKnownFolderPath(FOLDERID_LocalAppData);
const std::filesystem::path& serverExePath = localAppDataPath / s_ServerExePath;
std::wstring commandLineInput = std::wstring{ serverExePath } + L" --manualActivation";
ServerProcessLauncher()
{
try
{
m_serverExePath = GetPackageLocation(s_ServerPackageFamilyName, s_ServerFileName) / s_ServerFileName;

#ifndef USE_PROD_WINGET_SERVER
// The feature that allows directly launching a packaged process as long as it has a matching alias
// requires a failure to trigger, and the dev package is not ACL'd to force this to happen. Attempting
// to use the other code path results in an unpackaged server, causing other issues.
// We run the product code above to ensure that it is functioning properly, but then replace it with
// the path of the alias.
m_serverExePath = GetKnownFolderPath(FOLDERID_LocalAppData) / s_LocalAppDataRelativeServerExePath;
#endif
}
catch (wil::ResultException& re)
{
m_hr = re.GetErrorCode();
}
}

HRESULT LaunchWinGetServerWithManualActivation()
{
RETURN_IF_FAILED(m_hr);

STARTUPINFO info = { sizeof(info) };
wil::unique_process_information process;
std::wstring commandLineInput = std::wstring{ m_serverExePath } + L" --manualActivation";

RETURN_LAST_ERROR_IF(!CreateProcessW(NULL, &commandLineInput[0], NULL, NULL, FALSE, 0, NULL, NULL, &info, &process));
STARTUPINFO info = { sizeof(info) };
wil::unique_process_information process;

// Wait for manual reset event from server before proceeding with COM activation.
wil::unique_event manualResetEvent = CreateOrOpenServerStartEvent();
manualResetEvent.wait(10000);
RETURN_LAST_ERROR_IF(!CreateProcessW(NULL, &commandLineInput[0], NULL, NULL, FALSE, 0, NULL, NULL, &info, &process));

return S_OK;
}
// Wait for manual reset event from server before proceeding with COM activation.
wil::unique_event manualResetEvent = CreateOrOpenServerStartEvent();
manualResetEvent.wait(10000);

return S_OK;
}

private:
std::filesystem::path GetPackageLocation(std::wstring_view packageFamilyName, std::wstring_view fileName)
{
std::wstring pfn{ packageFamilyName };
UINT32 count = 0;
std::unique_ptr<PWSTR[]> fullNames;
UINT32 bufferLength = 0;
std::unique_ptr<WCHAR[]> buffer;
std::unique_ptr<UINT32[]> properties;

LONG result = FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &count, nullptr, &bufferLength, nullptr, nullptr);
THROW_WIN32_IF(result, result != ERROR_INSUFFICIENT_BUFFER);

for (size_t i = 0; i < 10 && result == ERROR_INSUFFICIENT_BUFFER; ++i)
{
fullNames = std::make_unique<PWSTR[]>(count);
buffer = std::make_unique<WCHAR[]>(bufferLength);
properties = std::make_unique<UINT32[]>(count);

result = FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &count, fullNames.get(), &bufferLength, buffer.get(), properties.get());
}

THROW_IF_WIN32_ERROR(result);

for (UINT32 i = 0; i < count; ++i)
{
// Includes null terminator
UINT32 pathLength = 0;
result = GetPackagePathByFullName(fullNames[i], &pathLength, nullptr);
if (result != ERROR_INSUFFICIENT_BUFFER)
{
continue;
}

std::wstring packagePath;
packagePath.resize(static_cast<size_t>(pathLength));

if (FAILED_WIN32(GetPackagePathByFullName(fullNames[i], &pathLength, &packagePath[0])))
{
continue;
}
packagePath.resize(static_cast<size_t>(pathLength - 1), L'\0');

std::filesystem::path resultPath = std::move(packagePath);
std::filesystem::path exePath = resultPath / fileName;

if (GetFileAttributesW(exePath.c_str()) != INVALID_FILE_ATTRIBUTES)
{
return resultPath;
}
}

THROW_WIN32(ERROR_PACKAGE_NOT_REGISTERED_FOR_USER);
}

std::filesystem::path GetKnownFolderPath(const KNOWNFOLDERID& id)
{
wil::unique_cotaskmem_string knownFolder = nullptr;
THROW_IF_FAILED(SHGetKnownFolderPath(id, KF_FLAG_NO_ALIAS | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_PACKAGE_REDIRECTION, NULL, &knownFolder));
return knownFolder.get();
}

std::filesystem::path m_serverExePath;
HRESULT m_hr = S_OK;
};

HRESULT CallCreateInstance(REFCLSID rclsid, REFIID riid, UINT32 flags, UINT32* bufferByteCount, BYTE** buffer)
{
Expand Down Expand Up @@ -132,10 +218,12 @@ extern "C" HRESULT WinGetServerManualActivation_CreateInstance(REFCLSID rclsid,
HRESULT result = CreateComInstance(rclsid, riid, flags, out);
if (FAILED(result))
{
ServerProcessLauncher launcher;

for (int i = 0; i < 3; i++)
{
result = LaunchWinGetServerWithManualActivation();
if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
result = launcher.LaunchWinGetServerWithManualActivation();
if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_PACKAGE_NOT_REGISTERED_FOR_USER))
{
break;
}
Expand Down

0 comments on commit 75803c3

Please sign in to comment.