commit 740808ddd8a46d2da91d45eae2136bef4528377d
parent 41e741a9d9473fddf5d528aaafd90e326adf4c48
Author: JohnMcPMS <johnmcp@microsoft.com>
Date: Thu, 5 May 2022 11:04:58 -0700
Act on elevation requirements in majority cases (#2126)
When `ElevationRequirement` is set to `elevationProhibited` is set and winget is run elevated, block the installation with an error.
When `elevatesSelf` or `elevationRequired` is set and winget is not run elevated, inform the user that an elevation prompt is expected. Also in the case of `elevationRequired`, use the `runas` verb on ShellExecute to run the installer elevated. This can be used on installers that will fail if they are not run elevated.
Diffstat:
8 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt
@@ -443,6 +443,7 @@ Rpc
rpc
rubengustorage
ruleset
+runas
runsettings
runtimes
safecast
diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h
@@ -116,6 +116,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandShortDescription);
WINGET_DEFINE_RESOURCE_STRINGID(InstalledPackageNotAvailable);
WINGET_DEFINE_RESOURCE_STRINGID(InstalledPackageVersionNotAvailable);
+ WINGET_DEFINE_RESOURCE_STRINGID(InstallerElevationExpected);
WINGET_DEFINE_RESOURCE_STRINGID(InstallerBlockedByPolicy);
WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedSecurityCheck);
WINGET_DEFINE_RESOURCE_STRINGID(InstallerFailedVirusScan);
@@ -126,6 +127,7 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashMismatchOverrideRequired);
WINGET_DEFINE_RESOURCE_STRINGID(InstallerHashVerified);
WINGET_DEFINE_RESOURCE_STRINGID(InstallerLogAvailable);
+ WINGET_DEFINE_RESOURCE_STRINGID(InstallerProhibitsElevation);
WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowInstallSuccess);
WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowRegistrationDeferred);
WINGET_DEFINE_RESOURCE_STRINGID(InstallFlowReturnCodeAlreadyInstalled);
diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp
@@ -119,6 +119,14 @@ namespace AppInstaller::CLI::Workflow
}
context << EnsureSupportForInstall;
+
+ // This installer cannot be run elevated, but we are running elevated.
+ // Implementation of de-elevation is complex; simply block for now.
+ if (installer->ElevationRequirement == ElevationRequirementEnum::ElevationProhibited && Runtime::IsRunningAsAdmin())
+ {
+ context.Reporter.Error() << Resource::String::InstallerProhibitsElevation << std::endl;
+ AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_INSTALLER_PROHIBITS_ELEVATION);
+ }
}
void ShowInstallationDisclaimer(Execution::Context& context)
diff --git a/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp b/src/AppInstallerCLICore/Workflows/MsiInstallFlow.cpp
@@ -32,10 +32,17 @@ namespace AppInstaller::CLI::Workflow
{
context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl;
+ const auto& installer = context.Get<Execution::Data::Installer>();
const std::filesystem::path& installerPath = context.Get<Execution::Data::InstallerPath>();
Msi::MsiParsedArguments parsedArgs = Msi::ParseMSIArguments(context.Get<Execution::Data::InstallerArgs>());
+ // Inform of elevation requirements
+ if (!Runtime::IsRunningAsAdmin() && installer->ElevationRequirement == Manifest::ElevationRequirementEnum::ElevatesSelf)
+ {
+ context.Reporter.Warn() << Resource::String::InstallerElevationExpected << std::endl;
+ }
+
auto installResult = context.Reporter.ExecuteWithProgress(
std::bind(InvokeMsiInstallProduct,
installerPath,
diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp
@@ -14,7 +14,7 @@ namespace AppInstaller::CLI::Workflow
namespace
{
// ShellExecutes the given path.
- std::optional<DWORD> InvokeShellExecute(const std::filesystem::path& filePath, const std::string& args, IProgressCallback& progress)
+ std::optional<DWORD> InvokeShellExecuteEx(const std::filesystem::path& filePath, const std::string& args, bool useRunAs, IProgressCallback& progress)
{
AICLI_LOG(CLI, Info, << "Starting: '" << filePath.u8string() << "' with arguments '" << args << '\'');
@@ -28,6 +28,13 @@ namespace AppInstaller::CLI::Workflow
// Verified setting to SW_SHOW does not hurt silent mode since no UI will be shown.
execInfo.nShow = SW_SHOW;
+ // This installer must be run elevated, but we are not currently.
+ // Have ShellExecute elevate the installer since it won't do so itself.
+ if (useRunAs)
+ {
+ execInfo.lpVerb = L"runas";
+ }
+
THROW_LAST_ERROR_IF(!ShellExecuteExW(&execInfo) || !execInfo.hProcess);
wil::unique_process_handle process{ execInfo.hProcess };
@@ -58,6 +65,11 @@ namespace AppInstaller::CLI::Workflow
}
}
+ std::optional<DWORD> InvokeShellExecute(const std::filesystem::path& filePath, const std::string& args, IProgressCallback& progress)
+ {
+ return InvokeShellExecuteEx(filePath, args, false, progress);
+ }
+
// Gets the escaped installer args.
std::string GetInstallerArgsTemplate(Execution::Context& context)
{
@@ -187,12 +199,25 @@ namespace AppInstaller::CLI::Workflow
{
context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl;
+ const auto& installer = context.Get<Execution::Data::Installer>();
const std::string& installerArgs = context.Get<Execution::Data::InstallerArgs>();
+ // Inform of elevation requirements
+ bool isElevated = Runtime::IsRunningAsAdmin();
+
+ // The installer will run elevated, either by direct request or through the installer itself doing so.
+ if ((installer->ElevationRequirement == ElevationRequirementEnum::ElevationRequired ||
+ installer->ElevationRequirement == ElevationRequirementEnum::ElevatesSelf)
+ && !isElevated)
+ {
+ context.Reporter.Warn() << Resource::String::InstallerElevationExpected << std::endl;
+ }
+
auto installResult = context.Reporter.ExecuteWithProgress(
- std::bind(InvokeShellExecute,
+ std::bind(InvokeShellExecuteEx,
context.Get<Execution::Data::InstallerPath>(),
installerArgs,
+ installer->ElevationRequirement == ElevationRequirementEnum::ElevationRequired && !isElevated,
std::placeholders::_1));
if (!installResult)
diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
@@ -1287,6 +1287,12 @@ Please specify one of them using the `--source` option to proceed.</value>
<data name="WaitArgumentDescription" xml:space="preserve">
<value>Prompts the user to press any key before exiting</value>
</data>
+ <data name="InstallerElevationExpected" xml:space="preserve">
+ <value>The installer will request to run as administrator, expect a prompt.</value>
+ </data>
+ <data name="InstallerProhibitsElevation" xml:space="preserve">
+ <value>The installer cannot be run from an administrator context.</value>
+ </data>
<data name="ModifiedPathRequiresShellRestart" xml:space="preserve">
<value>Path environment variable modified; restart your shell to use the new value.</value>
</data>
diff --git a/src/AppInstallerCommonCore/Errors.cpp b/src/AppInstallerCommonCore/Errors.cpp
@@ -212,6 +212,8 @@ namespace AppInstaller
return "A higher version of this application is already installed.";
case APPINSTALLER_CLI_ERROR_INSTALL_BLOCKED_BY_POLICY:
return "Organization policies are preventing installation. Contact your admin.";
+ case APPINSTALLER_CLI_ERROR_INSTALLER_PROHIBITS_ELEVATION:
+ return "The installer cannot be run from an administrator context.";
default:
return "Unknown Error Code";
}
diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h
@@ -98,6 +98,7 @@
#define APPINSTALLER_CLI_ERROR_PORTABLE_REPARSE_POINT_NOT_SUPPORTED ((HRESULT)0x8A150053)
#define APPINSTALLER_CLI_ERROR_PORTABLE_PACKAGE_ALREADY_EXISTS ((HRESULT)0x8A150054)
#define APPINSTALLER_CLI_ERROR_PORTABLE_SYMLINK_PATH_IS_DIRECTORY ((HRESULT)0x8A150055)
+#define APPINSTALLER_CLI_ERROR_INSTALLER_PROHIBITS_ELEVATION ((HRESULT)0x8A150056)
// Install errors.
#define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101)