Skip to content

Commit 5951c58

Browse files
author
Ben Hillis
committed
Add admin protection error message for shadow admin scenarios
When Windows Admin Protection is enabled, the elevated process runs as a shadow admin with a different SID, so distributions registered under the real user are not visible. Surface an informational message in two cases: 1. Launching a distribution by name that is not found (WSL_E_DISTRO_NOT_FOUND) 2. Listing distributions when none are registered (WSL_E_DEFAULT_DISTRO_NOT_FOUND)
1 parent b61bb85 commit 5951c58

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

localization/strings/en-US/Resources.resw

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,11 @@ For information please visit https://aka.ms/wslinstall</value>
744744
<data name="MessageAdministratorAccessRequiredForDebugShell" xml:space="preserve">
745745
<value>Running the debug shell requires running wsl.exe as Administrator.</value>
746746
</data>
747+
<data name="MessageAdminProtectionEnabled" xml:space="preserve">
748+
<value>Windows Admin Protection is enabled and your distributions may be registered under a different account.
749+
For more information on Admin Protection, please visit https://aka.ms/apdevguide</value>
750+
<comment>{Locked="Windows Admin Protection"}{Locked="Admin Protection"}{Locked="https://aka.ms/apdevguide"}Command line arguments, file names and string inserts should not be translated</comment>
751+
</data>
747752
<data name="MessageInstallProcessFailed" xml:space="preserve">
748753
<value>The installation process for distribution '{}' failed with exit code: {}.</value>
749754
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>

src/windows/common/wslutil.cpp

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -522,10 +522,42 @@ wsl::windows::common::wslutil::GetDefaultVersion(void)
522522
return version;
523523
}
524524

525+
namespace
526+
{
527+
528+
// Returns true if the current process is running as a shadow admin under
529+
// Windows Admin Protection. Caches the DLL lookup on first call.
530+
bool IsAdminProtectionEnabled()
531+
{
532+
const auto token = wil::open_current_access_token();
533+
if (!wsl::windows::common::security::IsTokenElevated(token.get()))
534+
{
535+
return false;
536+
}
537+
538+
using ShadowAdminEnabledFn = BOOL(WINAPI)();
539+
static std::optional<LxssDynamicFunction<ShadowAdminEnabledFn>> s_fn;
540+
static std::once_flag s_initFlag;
541+
542+
std::call_once(s_initFlag, []()
543+
{
544+
LxssDynamicFunction<ShadowAdminEnabledFn> fn{DynamicFunctionErrorLogs::None};
545+
if (SUCCEEDED(fn.load(L"SecurityHealthUdk.dll", "Shield_LUAIsShadowAdminEnabled")))
546+
{
547+
s_fn.emplace(std::move(fn));
548+
}
549+
});
550+
551+
return s_fn.has_value() && (*s_fn)();
552+
}
553+
554+
} // anonymous namespace
555+
525556
std::wstring wsl::windows::common::wslutil::GetErrorString(HRESULT result)
526557
{
527558
ULONG buildNumber = 0;
528559
std::wstring kbUrl;
560+
std::wstring errorString;
529561

530562
switch (result)
531563
{
@@ -545,14 +577,16 @@ std::wstring wsl::windows::common::wslutil::GetErrorString(HRESULT result)
545577
return Localization::MessageHigherIntegrity();
546578

547579
case WSL_E_DEFAULT_DISTRO_NOT_FOUND:
548-
return Localization::MessageNoDefaultDistro();
580+
errorString = Localization::MessageNoDefaultDistro();
581+
break;
549582

550583
case HRESULT_FROM_WIN32(WSAECONNABORTED):
551584
case HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS):
552585
return Localization::MessageInstanceTerminated();
553586

554587
case WSL_E_DISTRO_NOT_FOUND:
555-
return Localization::MessageDistroNotFound();
588+
errorString = Localization::MessageDistroNotFound();
589+
break;
556590

557591
case HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS):
558592
return Localization::MessageDistroNameAlreadyExists();
@@ -695,7 +729,26 @@ std::wstring wsl::windows::common::wslutil::GetErrorString(HRESULT result)
695729
}
696730
}
697731

698-
return GetSystemErrorString(result);
732+
if (errorString.empty())
733+
{
734+
return GetSystemErrorString(result);
735+
}
736+
737+
// If Admin Protection is enabled, prepend an informational message for
738+
// errors that may be caused by the shadow admin's separate registry hive.
739+
try
740+
{
741+
if (IsAdminProtectionEnabled())
742+
{
743+
auto message = Localization::MessageAdminProtectionEnabled();
744+
message += L"\n\n";
745+
message += errorString;
746+
return message;
747+
}
748+
}
749+
CATCH_LOG()
750+
751+
return errorString;
699752
}
700753

701754
std::optional<std::pair<std::wstring, GitHubReleaseAsset>> wsl::windows::common::wslutil::GetGitHubAssetFromRelease(const GitHubRelease& Release)

0 commit comments

Comments
 (0)