Skip to content

Commit 424f4dc

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 424f4dc

File tree

4 files changed

+53
-3
lines changed

4 files changed

+53
-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="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/WslSecurity.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,24 @@ bool wsl::windows::common::security::IsTokenElevated(_In_ HANDLE token)
135135
return (GetUserBasicIntegrityLevel(token) == SECURITY_MANDATORY_HIGH_RID);
136136
}
137137

138+
bool wsl::windows::common::security::IsAdminProtectionEnabled()
139+
{
140+
const auto token = wil::open_current_access_token();
141+
if (!IsTokenElevated(token.get()))
142+
{
143+
return false;
144+
}
145+
146+
using ShadowAdminEnabledFn = BOOL(WINAPI)();
147+
LxssDynamicFunction<ShadowAdminEnabledFn> isShadowAdminEnabled{DynamicFunctionErrorLogs::None};
148+
if (FAILED(isShadowAdminEnabled.load(L"SecurityHealthUdk.dll", "Shield_LUAIsShadowAdminEnabled")))
149+
{
150+
return false;
151+
}
152+
153+
return isShadowAdminEnabled();
154+
}
155+
138156
wil::unique_handle wsl::windows::common::security::GetUserToken(_In_ TOKEN_TYPE tokenType, _In_ RPC_BINDING_HANDLE handle)
139157
{
140158
wil::unique_handle contextToken;

src/windows/common/WslSecurity.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ wil::unique_handle GetUserToken(_In_ TOKEN_TYPE tokenType, _In_ RPC_BINDING_HAND
107107
/// </summary>
108108
bool IsTokenElevated(_In_ HANDLE token);
109109

110+
/// <summary>
111+
/// Returns true if the current token is elevated and Windows Admin Protection (shadow admin) is enabled.
112+
/// </summary>
113+
bool IsAdminProtectionEnabled();
114+
110115
/// <summary>
111116
/// Returns true if the provided token is a member of the localsystem group
112117
/// </summary>

src/windows/common/wslutil.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ std::wstring wsl::windows::common::wslutil::GetErrorString(HRESULT result)
526526
{
527527
ULONG buildNumber = 0;
528528
std::wstring kbUrl;
529+
std::wstring errorString;
529530

530531
switch (result)
531532
{
@@ -545,14 +546,16 @@ std::wstring wsl::windows::common::wslutil::GetErrorString(HRESULT result)
545546
return Localization::MessageHigherIntegrity();
546547

547548
case WSL_E_DEFAULT_DISTRO_NOT_FOUND:
548-
return Localization::MessageNoDefaultDistro();
549+
errorString = Localization::MessageNoDefaultDistro();
550+
break;
549551

550552
case HRESULT_FROM_WIN32(WSAECONNABORTED):
551553
case HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS):
552554
return Localization::MessageInstanceTerminated();
553555

554556
case WSL_E_DISTRO_NOT_FOUND:
555-
return Localization::MessageDistroNotFound();
557+
errorString = Localization::MessageDistroNotFound();
558+
break;
556559

557560
case HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS):
558561
return Localization::MessageDistroNameAlreadyExists();
@@ -695,7 +698,26 @@ std::wstring wsl::windows::common::wslutil::GetErrorString(HRESULT result)
695698
}
696699
}
697700

698-
return GetSystemErrorString(result);
701+
if (errorString.empty())
702+
{
703+
return GetSystemErrorString(result);
704+
}
705+
706+
// If Admin Protection is enabled, prepend an informational message for
707+
// errors that may be caused by the shadow admin's separate registry hive.
708+
try
709+
{
710+
if (wsl::windows::common::security::IsAdminProtectionEnabled())
711+
{
712+
auto message = Localization::MessageAdminProtectionEnabled();
713+
message += L"\n\n";
714+
message += errorString;
715+
return message;
716+
}
717+
}
718+
CATCH_LOG()
719+
720+
return errorString;
699721
}
700722

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

0 commit comments

Comments
 (0)