diff --git a/contrib/win32/openssh/OpenSSH-build.ps1 b/contrib/win32/openssh/OpenSSH-build.ps1
index 22c6794a84de..190387a45408 100644
--- a/contrib/win32/openssh/OpenSSH-build.ps1
+++ b/contrib/win32/openssh/OpenSSH-build.ps1
@@ -2,7 +2,7 @@
# PowerShell Script to clone, build and package PowerShell from specified fork and branch
param (
[string] $repolocation = "$PSScriptRoot\..\..\..",
- [string] $destination = "$env:WORKSPACE",
+ [string] $destination = $(if ($env:WORKSPACE) { $env:WORKSPACE } else { "$PSScriptRoot\..\..\.." }),
[ValidateSet('x86', 'x64', 'arm64', 'arm')]
[String]$NativeHostArch = 'x64',
[ValidateSet('Debug', 'Release')]
diff --git a/contrib/win32/openssh/OpenSSHBuildHelper.psm1 b/contrib/win32/openssh/OpenSSHBuildHelper.psm1
index 61a7c4502e13..88ca67f720b9 100644
--- a/contrib/win32/openssh/OpenSSHBuildHelper.psm1
+++ b/contrib/win32/openssh/OpenSSHBuildHelper.psm1
@@ -559,6 +559,11 @@ function Start-OpenSSHBuild
$VisualStudioPath = Get-VisualStudioPath -NativeHostArch $NativeHostArch
if ($null -ne $VisualStudioPath) {
$msbuildCmd = Get-MSBuildPath -VSInstallPath $VisualStudioPath
+ # Pin vcpkg's CMake to the same VS install / toolset (v143) as the
+ # OpenSSH vcxproj files, so manifest-mode auto-install doesn't pick a
+ # newer VS (e.g. VS 2026) whose v14x toolset is unsupported here.
+ $env:VCPKG_VISUAL_STUDIO_PATH = $VisualStudioPath
+ $env:VCPKG_PLATFORM_TOOLSET = "v143"
}
else {
$msbuildCmd = Get-VS2015BuildToolPath
@@ -614,7 +619,12 @@ function Get-VisualStudioPath {
$VSPaths = (& $vsWherePath -products * -requires $requiredVCtools -property installationPath)
# for some reason, VSWhere does not seem to find MSBuild so check manually
if ($null -ne $VSPaths) {
- foreach ($VSPath in $VSPaths) {
+ # Prefer VS 2022 — the .vcxproj files pin v143,
+ # which ships with VS 2022. Older VS (2017=v141, 2019=v142) would need v143 build tools
+ # sideloaded; newer VS (e.g. 2026) defaults to v145 which isn't supported here.
+ $preferred = @($VSPaths | Where-Object { $_ -match '\\2022\\' })
+ $ordered = $preferred + @($VSPaths | Where-Object { $_ -notmatch '\\2022\\' })
+ foreach ($VSPath in $ordered) {
if (Get-MSBuildPath -VSInstallPath $VSPath) {
return $VSPath
}
diff --git a/contrib/win32/openssh/README.txt b/contrib/win32/openssh/README.txt
index 14b250eb6d64..18eb6bed0649 100644
--- a/contrib/win32/openssh/README.txt
+++ b/contrib/win32/openssh/README.txt
@@ -1,4 +1,4 @@
-Custom paths for the visual studio projects are defined in paths.targets.
+Custom paths for the visual studio projects are defined in paths.targets.
All projects import this targets file, and it should be in the same directory as the project.
@@ -10,12 +10,54 @@ OpenSSH-Lib-Path = The directory path of the location to which libra
LibreSSL-x86-Path = The directory path of LibreSSL statically compiled for x86 platform.
LibreSSL-x64-Path = The directory path of LibreSSL statically compiled for x64 platform.
+Prerequisites
+-------------
+
+Before building OpenSSH for Windows, install the following:
+
+1. Visual Studio 2022 (Community, Professional, or Build Tools).
+ Required components (Visual Studio Installer -> Modify):
+ - Workload: "Desktop development with C++"
+ This installs MSBuild, the v143 toolset, and the Windows 10/11 SDK.
+ - Individual component: "MSVC v143 - VS 2022 C++ x64/x86 Spectre-mitigated libs (Latest)"
+ Required because the vcpkg x64-custom triplet compiles
+ dependencies (LibreSSL, libfido2, zlib) with /Qspectre, which
+ demands matching Spectre-mitigated runtime libraries.
+ - For ARM64 builds, also install "MSVC v143 - VS 2022 C++ ARM64 Spectre-mitigated libs".
+
+ Note: If a newer Visual Studio (e.g. VS 2026) is also installed,
+ OpenSSH-build.ps1 prefers VS 2022 and pins vcpkg's CMake to the
+ 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).
+
+3. vcpkg (one-time bootstrap).
+ Dependencies (LibreSSL, libfido2, zlib, libcbor) are managed via a
+ vcpkg manifest (vcpkg.json). MSBuild auto-installs them at build
+ time, but vcpkg must be cloned, bootstrapped, and integrated first:
+
+ git clone https://github.com/microsoft/vcpkg
+ cd vcpkg
+ .\bootstrap-vcpkg.bat
+ .\vcpkg.exe integrate install
+
+ "vcpkg integrate install" registers vcpkg's MSBuild props user-wide;
+ after that, every OpenSSH-build.ps1 run picks up the manifest
+ automatically. No need to run "vcpkg install" manually.
+
+4. Administrator PowerShell.
+ The build script updates the machine PATH (to add Git / Chocolatey)
+ and may install the Windows SDK via Chocolatey if missing. Run the
+ build from an elevated PowerShell session.
+
Notes on FIDO2 support
----------------------
* How to build:
- - Open Windows PowerShell.
+ - Open Windows PowerShell as Administrator.
- Build OpenSSH for Windows:
diff --git a/contrib/win32/openssh/sshd-auth.vcxproj b/contrib/win32/openssh/sshd-auth.vcxproj
index fd3a28e06214..695afc0fb9dc 100644
--- a/contrib/win32/openssh/sshd-auth.vcxproj
+++ b/contrib/win32/openssh/sshd-auth.vcxproj
@@ -1,6 +1,20 @@
+
+
+ bcrypt.lib;Userenv.lib;Crypt32.lib;Ws2_32.lib;Secur32.lib;Shlwapi.lib;kernel32.lib;user32.lib;delayimp.lib;advapi32.lib;Netapi32.lib;Rpcrt4.lib;ntdll.lib
+ Debug
@@ -112,6 +126,11 @@
true
+
+
+ /DELAYLOAD:user32.dll %(AdditionalOptions)
+
+
diff --git a/contrib/win32/win32compat/win32_usertoken_utils.c b/contrib/win32/win32compat/win32_usertoken_utils.c
index 2b8ede0d43c8..7d932c97603e 100644
--- a/contrib/win32/win32compat/win32_usertoken_utils.c
+++ b/contrib/win32/win32compat/win32_usertoken_utils.c
@@ -432,7 +432,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);