Skip to content
3 changes: 1 addition & 2 deletions contrib/win32/openssh/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ Before building OpenSSH for Windows, install the following:
same install / v143 toolset automatically.

2. Git for Windows.
The build script expects git.exe to be on PATH (it will add
"%ProgramFiles%\Git\cmd" to the machine PATH if missing).
The build script expects git.exe to be on PATH.

Comment thread
eransha-salvador marked this conversation as resolved.
3. vcpkg (one-time bootstrap).
Dependencies (LibreSSL, libfido2, zlib, libcbor) are managed via a
Expand Down
19 changes: 19 additions & 0 deletions contrib/win32/openssh/sshd-auth.vcxproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="paths.targets" />
<!-- sshd-auth is a privsep helper spawned by sshd as a different user and has
no desktop/window-station access. user32's DllMain binds to the process
window station and fails with STATUS_DLL_INIT_FAILED in restricted
environments (e.g. WinPE). Two mitigations: (1) add delayimp.lib and
delay-load user32 so its DLL is only loaded when a user32 API is
actually called, and (2) keep user32.lib (needed for the delay-load
thunks). sshd-auth only transitively references ShowWindow /
GetWindowPlacement from console.c's ConRestoreViewRect_NoPtyHack
helper, which sshd-auth does not execute, so in practice user32 is
never loaded. The ItemDefinitionGroup with /DELAYLOAD is placed after
Microsoft.Cpp.props (where the Link item type becomes defined). -->
<PropertyGroup>
<AdditionalDependentLibs>$(AdditionalDependentLibs);delayimp.lib</AdditionalDependentLibs>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
Expand Down Expand Up @@ -112,6 +126,11 @@
<VcpkgEnableManifest>true</VcpkgEnableManifest>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ItemDefinitionGroup>
<Link>
<AdditionalOptions>/DELAYLOAD:user32.dll %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
Expand Down
32 changes: 28 additions & 4 deletions contrib/win32/win32compat/win32_usertoken_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,24 @@ EnablePrivilege(const char *privName, int enabled)
return;
}

static BOOL
is_domain_joined_machine(void)
{
BOOL result = FALSE;
LPWSTR name = NULL;
NETSETUP_JOIN_STATUS s = NetSetupUnknownStatus;
DWORD api_res = NetGetJoinInformation(NULL, &name, &s);
if (api_res == NERR_Success) {
result = s == NetSetupDomainName;
NetApiBufferFree(name);
name = NULL;
} else {
debug("%s: NetGetJoinInformation() failed. Error %d.", __FUNCTION__, api_res);
}
debug("%s: NetGetJoinInformation(). Join result %d", __FUNCTION__, result);
return result;
}

HANDLE
generate_s4u_user_token(wchar_t* user_cpn, int impersonation) {
HANDLE lsa_handle = NULL, token = NULL;
Expand All @@ -112,7 +130,8 @@ generate_s4u_user_token(wchar_t* user_cpn, int impersonation) {
* so only the only two formats are a NetBiosDomain\SamAccountName which is
* a domain account or just SamAccountName in which is a local account */
BOOL domain_user = wcschr(user_cpn, L'\\') != NULL;

BOOL use_kerberos = domain_user && is_domain_joined_machine();

/* initialize connection to local security provider */
if (impersonation) {

Expand All @@ -132,11 +151,11 @@ generate_s4u_user_token(wchar_t* user_cpn, int impersonation) {
goto done;
}

InitLsaString(&auth_package_name, (domain_user) ? MICROSOFT_KERBEROS_NAME_A : MSV1_0_PACKAGE_NAME);
InitLsaString(&auth_package_name, use_kerberos ? MICROSOFT_KERBEROS_NAME_A : MSV1_0_PACKAGE_NAME);
if (ret = LsaLookupAuthenticationPackage(lsa_handle, &auth_package_name, &auth_package_id) != STATUS_SUCCESS)
goto done;

if (domain_user) {
if (use_kerberos) {

/* lookup the user principal name for the account */
WCHAR domain_upn[MAX_UPN_LEN + 1];
Expand Down Expand Up @@ -168,6 +187,11 @@ generate_s4u_user_token(wchar_t* user_cpn, int impersonation) {
}
else {

/* if a domain prefix slipped through (non-domain-joined host. e.g. winpe is backed with such domain),
point past it without mutating the caller's buffer */
wchar_t *backslash = wcschr(user_cpn, L'\\');
if (backslash != NULL)
user_cpn = backslash + 1;
MSV1_0_S4U_LOGON *s4u_logon;
logon_info_size = sizeof(MSV1_0_S4U_LOGON);

Expand Down Expand Up @@ -432,7 +456,7 @@ load_user_profile(HANDLE user_token, char* user)
EnablePrivilege("SeBackupPrivilege", 1);
EnablePrivilege("SeRestorePrivilege", 1);
if (LoadUserProfileW(user_token, &profileInfo) == FALSE) {
debug3("%s: LoadUserProfileW() failed for user %S with error %d.", __FUNCTION__, GetLastError());
debug3("%s: LoadUserProfileW() failed for user %S with error %d.", __FUNCTION__, user_name, GetLastError());
}
EnablePrivilege("SeBackupPrivilege", 0);
EnablePrivilege("SeRestorePrivilege", 0);
Expand Down