diff --git a/.gitignore b/.gitignore
index 0e0efe9982d..c1a6c28121a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,6 +46,7 @@ tags
/config.h
/contrib/win32/openssh/LibreSSL
/contrib/win32/openssh/ZLib
+/contrib/win32/openssh/libfido2
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
diff --git a/contrib/win32/openssh/GetFIDO2.ps1 b/contrib/win32/openssh/GetFIDO2.ps1
new file mode 100644
index 00000000000..8a13020f0cd
--- /dev/null
+++ b/contrib/win32/openssh/GetFIDO2.ps1
@@ -0,0 +1,63 @@
+param (
+ [string] $paths_target_file_path,
+ [string] $destDir,
+ [switch] $override
+)
+
+# Workaround that $PSScriptRoot is not support on ps version 2
+If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path}
+
+if([string]::IsNullOrEmpty($paths_target_file_path))
+{
+ $paths_target_file_path = Join-Path $PSScriptRoot "paths.targets"
+}
+
+if([string]::IsNullOrEmpty($destDir))
+{
+ $destDir = $PSScriptRoot
+}
+
+if($override)
+{
+ Remove-Item (join-path $destDir "libfido2") -Recurse -Force -ErrorAction SilentlyContinue
+}
+elseif (Test-Path (Join-Path $destDir "libfido2") -PathType Container)
+{
+ return
+}
+
+[xml] $buildConfig = Get-Content $paths_target_file_path
+$version = $buildConfig.Project.PropertyGroup.fido2Version
+
+Write-Host "Downloading libfido2 version:$version"
+Write-Host "paths_target_file_path:$paths_target_file_path"
+Write-Host "destDir:$destDir"
+Write-Host "override:$override"
+
+$zip_path = Join-Path $PSScriptRoot "libfido2.zip"
+
+$release_url = "https://ambientworks.net/tmp/libfido2-1.10-b32020cc-win.zip"
+Write-Host "release_url:$release_url"
+
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor `
+ [Net.SecurityProtocolType]::Tls11 -bor `
+ [Net.SecurityProtocolType]::Tls
+
+Remove-Item $zip_path -Force -ErrorAction SilentlyContinue
+Invoke-WebRequest -Uri $release_url -OutFile $zip_path -UseBasicParsing
+if(-not (Test-Path $zip_path))
+{
+ throw "failed to download libfido2 version:$version"
+}
+
+# XXX check pgp sig?
+Expand-Archive -Path $zip_path -DestinationPath $destDir -Force -ErrorAction SilentlyContinue -ErrorVariable e
+if($e -ne $null)
+{
+ throw "Error when expand zip file. libfido2 version:$version"
+}
+
+Rename-Item -Path $destDir\libfido2-1.10-b32020cc-win -NewName libfido2
+Remove-Item $zip_path -Force -ErrorAction SilentlyContinue
+
+Write-Host "Succesfully downloaded libfido2 version:$version"
diff --git a/contrib/win32/openssh/OpenSSHBuildHelper.psm1 b/contrib/win32/openssh/OpenSSHBuildHelper.psm1
index 52440544962..35ddeafb6ec 100644
--- a/contrib/win32/openssh/OpenSSHBuildHelper.psm1
+++ b/contrib/win32/openssh/OpenSSHBuildHelper.psm1
@@ -344,7 +344,7 @@ function Start-OpenSSHPackage
$buildDir = Join-Path $repositoryRoot ("bin\" + $folderName + "\" + $Configuration)
$payload = "sshd.exe", "ssh.exe", "ssh-agent.exe", "ssh-add.exe", "sftp.exe"
- $payload += "sftp-server.exe", "scp.exe", "ssh-shellhost.exe", "ssh-keygen.exe", "ssh-keyscan.exe"
+ $payload += "sftp-server.exe", "scp.exe", "ssh-shellhost.exe", "ssh-keygen.exe", "ssh-keyscan.exe", "ssh-sk-helper.exe"
$payload += "sshd_config_default", "install-sshd.ps1", "uninstall-sshd.ps1"
$payload += "FixHostFilePermissions.ps1", "FixUserFilePermissions.ps1", "OpenSSHUtils.psm1", "OpenSSHUtils.psd1"
$payload += "openssh-events.man", "moduli", "LICENSE.txt"
diff --git a/contrib/win32/openssh/README.txt b/contrib/win32/openssh/README.txt
index 14b40d4d603..14b250eb6d6 100644
--- a/contrib/win32/openssh/README.txt
+++ b/contrib/win32/openssh/README.txt
@@ -10,3 +10,53 @@ 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.
+Notes on FIDO2 support
+----------------------
+
+* How to build:
+
+ - Open Windows PowerShell.
+
+ - Build OpenSSH for Windows:
+
+ $ cd \path\to\openssh-portable\..
+ $ .\openssh-portable\contrib\win32\openssh\OpenSSH-build.ps1
+
+* What has been tested:
+
+ * Using a Yubico Security Key.
+
+ - Create a new SSH key:
+
+ $ ssh-keygen.exe -t ecdsa-sk
+
+ * Save the key material in SSH's default paths without an associated passphrase.
+
+ - Add the SSH key to your GitHub account.
+
+ - Tell git to use our SSH build:
+
+ $ $Env:GIT_SSH = '\path\to\ssh.exe'
+
+ - Clone a repository using the SSH key for authentication:
+
+ $ git clone ssh://git@github.com/org/some-private-repo
+
+* WSL2:
+
+ - Export GIT_SSH:
+
+ $ export GIT_SSH=/mnt/c/.../path/to/ssh.exe
+
+ - Optionally, alias ssh:
+
+ $ alias ssh=/mnt/c/.../path/to/ssh.exe
+
+* Note: FIDO2 keys are supported by ssh-agent.
+
+* What definitely doesn't work:
+
+ * ssh-keygen -O no-touch-required:
+ - there does not appear to be a way to toggle user presence in WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS.
+ * ssh-keygen -K, ssh-add -K:
+ - these use Credential Management to reconstruct the SSH private key.
diff --git a/contrib/win32/openssh/Win32-OpenSSH.sln b/contrib/win32/openssh/Win32-OpenSSH.sln
index 0761f52adcd..c1667f5caf4 100644
--- a/contrib/win32/openssh/Win32-OpenSSH.sln
+++ b/contrib/win32/openssh/Win32-OpenSSH.sln
@@ -156,6 +156,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ssh-keyscan", "ssh-keyscan.
{0D02F0F0-013B-4EE3-906D-86517F3822C0} = {0D02F0F0-013B-4EE3-906D-86517F3822C0}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ssh-sk-helper", "ssh-sk-helper.vcxproj", "{7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}"
+ ProjectSection(ProjectDependencies) = postProject
+ {05E1115F-8529-46D0-AAAF-52A404CE79A7} = {05E1115F-8529-46D0-AAAF-52A404CE79A7}
+ {8F9D3B74-8D33-448E-9762-26E8DCC6B2F4} = {8F9D3B74-8D33-448E-9762-26E8DCC6B2F4}
+ {DD483F7D-C553-4740-BC1A-903805AD0174} = {DD483F7D-C553-4740-BC1A-903805AD0174}
+ {0D02F0F0-013B-4EE3-906D-86517F3822C0} = {0D02F0F0-013B-4EE3-906D-86517F3822C0}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
@@ -504,6 +512,22 @@ Global
{7D0A75FC-F366-4B60-B72F-B37C3EA07CCA}.Release|x64.Build.0 = Release|x64
{7D0A75FC-F366-4B60-B72F-B37C3EA07CCA}.Release|x86.ActiveCfg = Release|Win32
{7D0A75FC-F366-4B60-B72F-B37C3EA07CCA}.Release|x86.Build.0 = Release|Win32
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Debug|ARM.ActiveCfg = Debug|ARM
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Debug|ARM.Build.0 = Debug|ARM
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Debug|ARM64.Build.0 = Debug|ARM64
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Debug|x64.ActiveCfg = Debug|x64
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Debug|x64.Build.0 = Debug|x64
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Debug|x86.ActiveCfg = Debug|Win32
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Debug|x86.Build.0 = Debug|Win32
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Release|ARM.ActiveCfg = Release|ARM
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Release|ARM.Build.0 = Release|ARM
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Release|ARM64.ActiveCfg = Release|ARM64
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Release|ARM64.Build.0 = Release|ARM64
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Release|x64.ActiveCfg = Release|x64
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Release|x64.Build.0 = Release|x64
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Release|x86.ActiveCfg = Release|Win32
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -530,6 +554,7 @@ Global
{FC568FF0-60F2-4B2E-AF62-FD392EDBA1B9} = {A8096E32-E084-4FA0-AE01-A8D909EB2BB4}
{484A8CDE-B949-4BDA-B447-74685C8E032F} = {A8096E32-E084-4FA0-AE01-A8D909EB2BB4}
{7D0A75FC-F366-4B60-B72F-B37C3EA07CCA} = {17322AAF-808F-4646-AD37-5B0EDDCB8F3E}
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB} = {17322AAF-808F-4646-AD37-5B0EDDCB8F3E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0AC224E8-C215-4270-954A-A2ACEE06DE58}
diff --git a/contrib/win32/openssh/config.h.vs b/contrib/win32/openssh/config.h.vs
index 42c1301b001..71caf87e290 100644
--- a/contrib/win32/openssh/config.h.vs
+++ b/contrib/win32/openssh/config.h.vs
@@ -137,6 +137,19 @@
/* Enable for PKCS#11 support */
#define ENABLE_PKCS11 1
+/* Enable for U2F/FIDO support */
+#define ENABLE_SK
+
+/* Enable for built-in U2F/FIDO support */
+#define ENABLE_SK_INTERNAL
+
+/* Define for discoverable credential support */
+#define HAVE_FIDO_CRED_PROT
+#define HAVE_FIDO_CRED_SET_PROT
+#define HAVE_FIDO_DEV_SUPPORTS_CRED_PROT
+#define HAVE_FIDO_DEV_GET_TOUCH_BEGIN
+#define HAVE_FIDO_DEV_GET_TOUCH_STATUS
+
/* File names may not contain backslash characters */
/* #undef FILESYSTEM_NO_BACKSLASH */
@@ -1715,4 +1728,4 @@
#define HAVE_LOCALTIME_R
#define HAVE_DECL_MEMMEM 0
#define WITH_ZLIB
-#define _PATH_TTY "conin$"
\ No newline at end of file
+#define _PATH_TTY "conin$"
diff --git a/contrib/win32/openssh/config.vcxproj b/contrib/win32/openssh/config.vcxproj
index 8b4f291a5ee..fda66212a35 100644
--- a/contrib/win32/openssh/config.vcxproj
+++ b/contrib/win32/openssh/config.vcxproj
@@ -187,6 +187,7 @@
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)config.ps1" -Config_h_vs "$(SolutionDir)config.h.vs" -Config_h "$(OpenSSH-Src-Path)config.h" -VCIncludePath "$(VC_IncludePath)" -OutCRTHeader "$(OpenSSH-Src-Path)contrib\win32\win32compat\inc\crtheaders.h"
+powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetFIDO2.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetLibreSSL.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetZlib.ps1"
@@ -224,6 +225,7 @@ copy /Y "$(SolutionDir)openssh-events.man" "$(OutDir)"
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)config.ps1" -Config_h_vs "$(SolutionDir)config.h.vs" -Config_h "$(OpenSSH-Src-Path)config.h" -VCIncludePath "$(VC_IncludePath)" -OutCRTHeader "$(OpenSSH-Src-Path)contrib\win32\win32compat\inc\crtheaders.h"
+powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetFIDO2.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetLibreSSL.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetZlib.ps1"
@@ -261,6 +263,7 @@ copy /Y "$(SolutionDir)openssh-events.man" "$(OutDir)"
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)config.ps1" -Config_h_vs "$(SolutionDir)config.h.vs" -Config_h "$(OpenSSH-Src-Path)config.h" -VCIncludePath "$(VC_IncludePath)" -OutCRTHeader "$(OpenSSH-Src-Path)contrib\win32\win32compat\inc\crtheaders.h"
+powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetFIDO2.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetLibreSSL.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetZlib.ps1"
@@ -298,6 +301,7 @@ copy /Y "$(SolutionDir)openssh-events.man" "$(OutDir)"
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)config.ps1" -Config_h_vs "$(SolutionDir)config.h.vs" -Config_h "$(OpenSSH-Src-Path)config.h" -VCIncludePath "$(VC_IncludePath)" -OutCRTHeader "$(OpenSSH-Src-Path)contrib\win32\win32compat\inc\crtheaders.h"
+powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetFIDO2.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetLibreSSL.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetZlib.ps1"
@@ -339,6 +343,7 @@ copy /Y "$(SolutionDir)openssh-events.man" "$(OutDir)"
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)config.ps1" -Config_h_vs "$(SolutionDir)config.h.vs" -Config_h "$(OpenSSH-Src-Path)config.h" -VCIncludePath "$(VC_IncludePath)" -OutCRTHeader "$(OpenSSH-Src-Path)contrib\win32\win32compat\inc\crtheaders.h"
+powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetFIDO2.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetLibreSSL.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetZlib.ps1"
@@ -380,6 +385,7 @@ copy /Y "$(SolutionDir)openssh-events.man" "$(OutDir)"
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)config.ps1" -Config_h_vs "$(SolutionDir)config.h.vs" -Config_h "$(OpenSSH-Src-Path)config.h" -VCIncludePath "$(VC_IncludePath)" -OutCRTHeader "$(OpenSSH-Src-Path)contrib\win32\win32compat\inc\crtheaders.h"
+powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetFIDO2.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetLibreSSL.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetZlib.ps1"
@@ -421,6 +427,7 @@ copy /Y "$(SolutionDir)openssh-events.man" "$(OutDir)"
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)config.ps1" -Config_h_vs "$(SolutionDir)config.h.vs" -Config_h "$(OpenSSH-Src-Path)config.h" -VCIncludePath "$(VC_IncludePath)" -OutCRTHeader "$(OpenSSH-Src-Path)contrib\win32\win32compat\inc\crtheaders.h"
+powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetFIDO2.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetLibreSSL.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetZlib.ps1"
@@ -462,6 +469,7 @@ copy /Y "$(SolutionDir)openssh-events.man" "$(OutDir)"
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)config.ps1" -Config_h_vs "$(SolutionDir)config.h.vs" -Config_h "$(OpenSSH-Src-Path)config.h" -VCIncludePath "$(VC_IncludePath)" -OutCRTHeader "$(OpenSSH-Src-Path)contrib\win32\win32compat\inc\crtheaders.h"
+powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetFIDO2.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetLibreSSL.ps1
powershell.exe -Executionpolicy Bypass -File "$(SolutionDir)GetZlib.ps1"
@@ -486,4 +494,4 @@ copy /Y "$(SolutionDir)openssh-events.man" "$(OutDir)"
-
\ No newline at end of file
+
diff --git a/contrib/win32/openssh/paths.targets b/contrib/win32/openssh/paths.targets
index 74212fb5259..0e14f3f7994 100644
--- a/contrib/win32/openssh/paths.targets
+++ b/contrib/win32/openssh/paths.targets
@@ -6,11 +6,17 @@
$(SolutionDir)lib\
3.3.3.0
1.2.11
+ 1.9.0
$(SolutionDir)\LibreSSL\sdk\
$(SolutionDir)\LibreSSL\bin\desktop\x86\
$(SolutionDir)\LibreSSL\bin\desktop\x64\
$(SolutionDir)\LibreSSL\bin\desktop\arm64\
$(SolutionDir)\LibreSSL\bin\desktop\arm\
+ $(SolutionDir)\libfido2\
+ $(SolutionDir)\libfido2\Win32\Release\v143\static\
+ $(SolutionDir)\libfido2\Win64\Release\v143\static\
+ $(SolutionDir)\libfido2\ARM64\Release\v143\static\
+ $(SolutionDir)\libfido2\ARM\Release\v143\static\
$(SolutionDir)\ZLib\sdk\
$(SolutionDir)\ZLib\bin\x86\
$(SolutionDir)\ZLib\bin\x64\
@@ -22,4 +28,4 @@
bcrypt.lib;Userenv.lib;Crypt32.lib;Ws2_32.lib;Secur32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Netapi32.lib;Rpcrt4.lib;ntdll.lib
false
-
\ No newline at end of file
+
diff --git a/contrib/win32/openssh/ssh-sk-helper.vcxproj b/contrib/win32/openssh/ssh-sk-helper.vcxproj
new file mode 100644
index 00000000000..895df9bc500
--- /dev/null
+++ b/contrib/win32/openssh/ssh-sk-helper.vcxproj
@@ -0,0 +1,409 @@
+
+
+
+
+
+ Debug
+ ARM
+
+
+ Debug
+ ARM64
+
+
+ Debug
+ Win32
+
+
+ Release
+ ARM
+
+
+ Release
+ ARM64
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {7D0A75FC-F366-4B60-B72F-B37C3EA07CCB}
+ Win32Proj
+ sshskhelper
+ $(WindowsSDKVersion)
+ ssh-sk-helper
+
+
+
+ Application
+ true
+ v140
+ MultiByte
+
+
+ Application
+ false
+ v140
+ true
+ MultiByte
+
+
+ Application
+ true
+ v140
+ MultiByte
+
+
+ Application
+ true
+ v141
+ MultiByte
+
+
+ Application
+ true
+ v141
+ MultiByte
+
+
+ Application
+ false
+ v140
+ true
+ MultiByte
+
+
+ Application
+ false
+ v141
+ true
+ MultiByte
+
+
+ Application
+ false
+ v141
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(fido2-Path)include;$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(fido2-Path)include;$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(fido2-Path)include;$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(fido2-Path)include;$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(fido2-Path)include;$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(fido2-Path)include;$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(fido2-Path)include;$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(fido2-Path)include;$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+
+ NotUsing
+ Level1
+ Disabled
+ _WIN32_WINNT=0x600;WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)
+ false
+ $(SolutionDir);$(LibreSSL-Path)include;$(ZLib-Path);$(OpenSSH-Src-Path)includes;$(OpenSSH-Src-Path);$(OpenSSH-Src-Path)contrib\win32\win32compat;$(OpenSSH-Src-Path)libkrb;$(OpenSSH-Src-Path)libkrb\libKrb5;%(AdditionalIncludeDirectories)
+ MultiThreadedDebug
+ ProgramDatabase
+ Guard
+ /Gy %(AdditionalOptions)
+
+
+ Console
+ true
+ posix_compat.lib;libssh.lib;openbsd_compat.lib;zlib.lib;fido2.lib;cbor.lib;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-x86-Path);$(ZLib-x86-Path);$(fido2-x86-Path);%(AdditionalLibraryDirectories)
+ wmainCRTStartup
+ /debug /debugtype:cv,fixup /opt:ref /opt:icf /incremental:no /ignore:4099 /ignore:4098 %(AdditionalOptions)
+
+
+ targetos.manifest
+
+
+
+
+ NotUsing
+ Level1
+ Disabled
+ _WIN32_WINNT=0x600;WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)
+ false
+ $(SolutionDir);$(LibreSSL-Path)include;$(ZLib-Path);$(OpenSSH-Src-Path)includes;$(OpenSSH-Src-Path);$(OpenSSH-Src-Path)contrib\win32\win32compat;$(OpenSSH-Src-Path)libkrb;$(OpenSSH-Src-Path)libkrb\libKrb5;%(AdditionalIncludeDirectories)
+ MultiThreadedDebug
+ ProgramDatabase
+ Guard
+ /Gy %(AdditionalOptions)
+
+
+ Console
+ true
+ posix_compat.lib;libssh.lib;openbsd_compat.lib;zlib.lib;fido2.lib;cbor.lib;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-x64-Path);$(ZLib-x64-Path);$(fido2-x64-Path);%(AdditionalLibraryDirectories)
+ wmainCRTStartup
+ /debug /debugtype:cv,fixup /opt:ref /opt:icf /incremental:no /ignore:4099 /ignore:4098 %(AdditionalOptions)
+
+
+ targetos.manifest
+
+
+
+
+ NotUsing
+ Level1
+ Disabled
+ _WIN32_WINNT=0x600;WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)
+ false
+ $(SolutionDir);$(LibreSSL-Path)include;$(ZLib-Path);$(OpenSSH-Src-Path)includes;$(OpenSSH-Src-Path);$(OpenSSH-Src-Path)contrib\win32\win32compat;$(OpenSSH-Src-Path)libkrb;$(OpenSSH-Src-Path)libkrb\libKrb5;%(AdditionalIncludeDirectories)
+ MultiThreadedDebug
+ ProgramDatabase
+ Guard
+ /Gy %(AdditionalOptions)
+
+
+ Console
+ true
+ posix_compat.lib;libssh.lib;openbsd_compat.lib;zlib.lib;fido2.lib;cbor.lib;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-arm64-Path);$(ZLib-arm64-Path);$(fido2-arm64-Path);%(AdditionalLibraryDirectories)
+ wmainCRTStartup
+ /debug /debugtype:cv,fixup /opt:ref /opt:icf /incremental:no /ignore:4099 /ignore:4098 %(AdditionalOptions)
+
+
+ targetos.manifest
+
+
+
+
+ NotUsing
+ Level1
+ Disabled
+ _WIN32_WINNT=0x600;WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)
+ false
+ $(SolutionDir);$(LibreSSL-Path)include;$(ZLib-Path);$(OpenSSH-Src-Path)includes;$(OpenSSH-Src-Path);$(OpenSSH-Src-Path)contrib\win32\win32compat;$(OpenSSH-Src-Path)libkrb;$(OpenSSH-Src-Path)libkrb\libKrb5;%(AdditionalIncludeDirectories)
+ MultiThreadedDebug
+ ProgramDatabase
+ Guard
+ /Gy %(AdditionalOptions)
+
+
+ Console
+ true
+ posix_compat.lib;libssh.lib;openbsd_compat.lib;zlib.lib;fido2.lib;cbor.lib;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-arm-Path);$(ZLib-arm-Path);$(fido2-arm-Path);%(AdditionalLibraryDirectories)
+ wmainCRTStartup
+ /debug /debugtype:cv,fixup /opt:ref /opt:icf /incremental:no /ignore:4099 /ignore:4098 %(AdditionalOptions)
+
+
+ targetos.manifest
+
+
+
+
+ Level1
+ NotUsing
+ MaxSpeed
+ true
+ true
+ _WIN32_WINNT=0x600;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ false
+ $(SolutionDir);$(LibreSSL-Path)include;$(ZLib-Path);$(OpenSSH-Src-Path)includes;$(OpenSSH-Src-Path);$(OpenSSH-Src-Path)contrib\win32\win32compat;$(OpenSSH-Src-Path)libkrb;$(OpenSSH-Src-Path)libkrb\libKrb5;%(AdditionalIncludeDirectories)
+ MultiThreaded
+ Guard
+ /Gy %(AdditionalOptions)
+
+
+ Console
+ true
+ true
+ true
+ posix_compat.lib;libssh.lib;openbsd_compat.lib;zlib.lib;fido2.lib;cbor.lib;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-x86-Path);$(ZLib-x86-Path);$(fido2-x86-Path);%(AdditionalLibraryDirectories)
+ wmainCRTStartup
+ true
+ /debug /debugtype:cv,fixup /opt:ref /opt:icf /incremental:no /ignore:4099 %(AdditionalOptions)
+
+
+ targetos.manifest
+
+
+
+
+ Level1
+ NotUsing
+ MaxSpeed
+ true
+ true
+ _WIN32_WINNT=0x600;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ false
+ $(SolutionDir);$(LibreSSL-Path)include;$(ZLib-Path);$(OpenSSH-Src-Path)includes;$(OpenSSH-Src-Path);$(OpenSSH-Src-Path)contrib\win32\win32compat;$(OpenSSH-Src-Path)libkrb;$(OpenSSH-Src-Path)libkrb\libKrb5;%(AdditionalIncludeDirectories)
+ MultiThreaded
+ true
+ Guard
+ /Gy %(AdditionalOptions)
+
+
+ Console
+ true
+ true
+ true
+ posix_compat.lib;libssh.lib;openbsd_compat.lib;zlib.lib;fido2.lib;cbor.lib;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-x64-Path);$(ZLib-x64-Path);$(fido2-x64-Path);%(AdditionalLibraryDirectories)
+ wmainCRTStartup
+ true
+ /debug /debugtype:cv,fixup /opt:ref /opt:icf /incremental:no /ignore:4099 %(AdditionalOptions)
+
+
+ targetos.manifest
+
+
+
+
+ Level1
+ NotUsing
+ MaxSpeed
+ true
+ true
+ _WIN32_WINNT=0x600;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ false
+ $(SolutionDir);$(LibreSSL-Path)include;$(ZLib-Path);$(OpenSSH-Src-Path)includes;$(OpenSSH-Src-Path);$(OpenSSH-Src-Path)contrib\win32\win32compat;$(OpenSSH-Src-Path)libkrb;$(OpenSSH-Src-Path)libkrb\libKrb5;%(AdditionalIncludeDirectories)
+ MultiThreaded
+ true
+ Guard
+ /Gy %(AdditionalOptions)
+
+
+ Console
+ true
+ true
+ true
+ posix_compat.lib;libssh.lib;openbsd_compat.lib;zlib.lib;fido2.lib;cbor.lib;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-arm64-Path);$(ZLib-arm64-Path);$(fido2-arm64-Path);%(AdditionalLibraryDirectories)
+ wmainCRTStartup
+ true
+ /debug /debugtype:cv,fixup /opt:ref /opt:icf /incremental:no /ignore:4099 %(AdditionalOptions)
+
+
+ targetos.manifest
+
+
+
+
+ Level1
+ NotUsing
+ MaxSpeed
+ true
+ true
+ _WIN32_WINNT=0x600;_LIB;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ false
+ $(SolutionDir);$(LibreSSL-Path)include;$(ZLib-Path);$(OpenSSH-Src-Path)includes;$(OpenSSH-Src-Path);$(OpenSSH-Src-Path)contrib\win32\win32compat;$(OpenSSH-Src-Path)libkrb;$(OpenSSH-Src-Path)libkrb\libKrb5;%(AdditionalIncludeDirectories)
+ MultiThreaded
+ true
+ Guard
+ /Gy %(AdditionalOptions)
+
+
+ Console
+ true
+ true
+ true
+ posix_compat.lib;libssh.lib;openbsd_compat.lib;zlib.lib;fido2.lib;cbor.lib;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-arm-Path);$(ZLib-arm-Path);$(fido2-arm-Path);%(AdditionalLibraryDirectories)
+ wmainCRTStartup
+ true
+ /debug /debugtype:cv,fixup /opt:ref /opt:icf /incremental:no /ignore:4099 %(AdditionalOptions)
+
+
+ targetos.manifest
+
+
+
+
+
+
+
+ $(OpenSSH-Src-Path)contrib\libfido2\include;%(AdditionalIncludeDirectories)
+
+
+
+
+
+
+
+
+
diff --git a/contrib/win32/win32compat/ssh-agent/keyagent-request.c b/contrib/win32/win32compat/ssh-agent/keyagent-request.c
index 9d7dfce77aa..76b81b04984 100644
--- a/contrib/win32/win32compat/ssh-agent/keyagent-request.c
+++ b/contrib/win32/win32compat/ssh-agent/keyagent-request.c
@@ -208,6 +208,7 @@ static int sign_blob(const struct sshkey *pubkey, u_char ** sig, size_t *siglen,
DWORD regdatalen = 0, keyblob_len = 0;
struct sshbuf* tmpbuf = NULL;
char *keyblob = NULL;
+ const char *sk_provider = NULL;
*sig = NULL;
*siglen = 0;
@@ -230,8 +231,14 @@ static int sign_blob(const struct sshkey *pubkey, u_char ** sig, size_t *siglen,
else if (flags & SSH_AGENT_RSA_SHA2_512)
algo = "rsa-sha2-512";
- if (sshkey_private_deserialize(tmpbuf, &prikey) != 0 ||
- sshkey_sign(prikey, sig, siglen, blob, blen, algo, NULL, NULL, 0) != 0) {
+ if (sshkey_private_deserialize(tmpbuf, &prikey) != 0) {
+ debug("cannot deserialize key");
+ goto done;
+ }
+ if (sshkey_is_sk(prikey))
+ sk_provider = "internal";
+ if (sshkey_sign(prikey, sig, siglen, blob, blen, algo, sk_provider,
+ NULL, 0) != 0) {
debug("cannot sign using retrieved key");
goto done;
}
@@ -472,4 +479,4 @@ int process_keyagent_request(struct sshbuf* request, struct sshbuf* response, st
}
}
-#pragma warning(pop)
\ No newline at end of file
+#pragma warning(pop)
diff --git a/pathnames.h b/pathnames.h
index f7ca5a75a0d..608cd176bd4 100644
--- a/pathnames.h
+++ b/pathnames.h
@@ -136,8 +136,12 @@
/* Location of ssh-sk-helper to support keys in security keys */
#ifndef _PATH_SSH_SK_HELPER
+#ifdef WINDOWS
+#define _PATH_SSH_SK_HELPER "C:\\Windows\\System32\\OpenSSH\\ssh-sk-helper.exe"
+#else
#define _PATH_SSH_SK_HELPER "/usr/libexec/ssh-sk-helper"
#endif
+#endif
/* xauth for X11 forwarding */
#ifndef _PATH_XAUTH
diff --git a/sk-usbhid.c b/sk-usbhid.c
index c85b9857d2c..92b25552c2e 100644
--- a/sk-usbhid.c
+++ b/sk-usbhid.c
@@ -686,7 +686,12 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
goto out;
}
*enroll_response = NULL;
+#ifdef WINDOWS
+ /* Don't overwrite existing credentials on FIDO authenticators. */
+ arc4random_buf(user_id, sizeof(user_id));
+#else
memset(user_id, 0, sizeof(user_id));
+#endif
if (check_enroll_options(options, &device, user_id,
sizeof(user_id)) != 0)
goto out; /* error already logged */
@@ -706,8 +711,15 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
}
if (device != NULL)
sk = sk_open(device);
- else
+ else {
+#ifdef WINDOWS
+ if ((sk = sk_open("windows://hello")) == NULL)
+ sk = sk_probe(NULL, NULL, 0);
+#else
sk = sk_probe(NULL, NULL, 0);
+#endif
+ }
+
if (sk == NULL) {
skdebug(__func__, "failed to find sk");
goto out;
@@ -721,12 +733,31 @@ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
goto out;
}
- if ((r = fido_cred_set_clientdata_hash(cred, challenge,
- challenge_len)) != FIDO_OK) {
+#ifndef WINDOWS
+ if (sha256_mem(challenge, challenge_len,
+ chall_hash, sizeof(chall_hash)) != 0) {
+ skdebug(__func__, "hash challenge failed");
+ goto out;
+ }
+ if ((r = fido_cred_set_clientdata_hash(cred, chall_hash,
+ sizeof(chall_hash))) != FIDO_OK) {
skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
fido_strerr(r));
goto out;
}
+#else
+ /*
+ * webauthn.dll (windows://hello in libfido2) requires the unhashed
+ * clientdata body, so we use fido_cred_set_clientdata() instead of
+ * fido_cred_set_clientdata_hash().
+ */
+ if ((r = fido_cred_set_clientdata(cred, challenge,
+ challenge_len)) != FIDO_OK) {
+ skdebug(__func__, "fido_cred_set_clientdata: %s",
+ fido_strerr(r));
+ goto out;
+ }
+#endif
if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
@@ -972,7 +1003,9 @@ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
char *device = NULL;
struct sk_usbhid *sk = NULL;
struct sk_sign_response *response = NULL;
+#ifndef WINDOWS
uint8_t message[32];
+#endif
int ret = SSH_SK_ERR_GENERAL;
int r;
@@ -985,17 +1018,33 @@ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
*sign_response = NULL;
if (check_sign_load_resident_options(options, &device) != 0)
goto out; /* error already logged */
+#ifndef WINDOWS
/* hash data to be signed before it goes to the security key */
+ /* This happens elsewhere on Windows; see note below. */
if ((r = sha256_mem(data, datalen, message, sizeof(message))) != 0) {
skdebug(__func__, "hash message failed");
goto out;
}
+#endif
if (device != NULL)
sk = sk_open(device);
+#ifdef WINDOWS
+ else {
+ if ((sk = sk_open("windows://hello")) == NULL) {
+ if (pin != NULL ||
+ (flags & SSH_SK_USER_VERIFICATION_REQD))
+ sk = sk_probe(NULL, NULL, 0);
+ else
+ sk = sk_probe(application, key_handle,
+ key_handle_len);
+ }
+ }
+#else
else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
sk = sk_probe(NULL, NULL, 0);
else
sk = sk_probe(application, key_handle, key_handle_len);
+#endif
if (sk == NULL) {
skdebug(__func__, "failed to find sk");
goto out;
@@ -1004,12 +1053,26 @@ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
skdebug(__func__, "fido_assert_new failed");
goto out;
}
+#ifndef WINDOWS
if ((r = fido_assert_set_clientdata_hash(assert, message,
sizeof(message))) != FIDO_OK) {
skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
fido_strerr(r));
goto out;
}
+#else
+ /*
+ * webauthn.dll (windows://hello in libfido2) requires the unhashed
+ * clientdata body, so we use fido_assert_set_clientdata() instead of
+ * fido_assert_set_clientdata_hash().
+ */
+ if ((r = fido_assert_set_clientdata(assert, data,
+ datalen)) != FIDO_OK) {
+ skdebug(__func__, "fido_assert_set_clientdata: %s",
+ fido_strerr(r));
+ goto out;
+ }
+#endif
if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
goto out;
@@ -1050,7 +1113,9 @@ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
response = NULL;
ret = 0;
out:
+#ifndef WINDOWS
explicit_bzero(message, sizeof(message));
+#endif
free(device);
if (response != NULL) {
free(response->sig_r);
diff --git a/ssh-add.c b/ssh-add.c
index 92192fcfa23..19c985205fd 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -347,7 +347,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
}
ssh_free_identitylist(idlist);
}
-
+#ifndef WINDOWS
if (sshkey_is_sk(private)) {
if (skprovider == NULL) {
fprintf(stderr, "Cannot load FIDO key %s "
@@ -363,7 +363,10 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
/* Don't send provider constraint for other keys */
skprovider = NULL;
}
-
+#else
+ if (!sshkey_is_sk(private))
+ skprovider = NULL;
+#endif
if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
lifetime, confirm, maxsign, skprovider)) == 0) {
ret = 0;
@@ -796,7 +799,7 @@ main(int argc, char **argv)
goto done;
}
-#ifdef ENABLE_SK_INTERNAL
+#if !defined(WINDOWS) && defined(ENABLE_SK_INTERNAL)
if (skprovider == NULL)
skprovider = "internal";
#endif
diff --git a/ssh-keygen.c b/ssh-keygen.c
index fc6c21a53a3..1da25f47886 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1746,6 +1746,9 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
struct ssh_identitylist *agent_ids;
size_t j;
struct notifier_ctx *notifier = NULL;
+#ifdef WINDOWS
+ int retried = 0;
+#endif
#ifdef ENABLE_PKCS11
pkcs11_init(1);
@@ -1781,12 +1784,14 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
} else {
/* CA key is assumed to be a private key on the filesystem */
ca = load_identity(tmp, NULL);
+#ifndef WINDOWS
if (sshkey_is_sk(ca) &&
(ca->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
if ((pin = read_passphrase("Enter PIN for CA key: ",
RP_ALLOW_STDIN)) == NULL)
fatal_f("couldn't read PIN");
}
+#endif
}
free(tmp);
@@ -1848,6 +1853,9 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
&agent_fd)) != 0)
fatal_r(r, "Couldn't certify %s via agent", tmp);
} else {
+#ifdef WINDOWS
+ retry:
+#endif
if (sshkey_is_sk(ca) &&
(ca->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
notifier = notify_start(0,
@@ -1857,6 +1865,17 @@ do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
r = sshkey_certify(public, ca, key_type_name,
sk_provider, pin);
notify_complete(notifier, "User presence confirmed");
+#ifdef WINDOWS
+ if (r == SSH_ERR_KEY_WRONG_PASSPHRASE &&
+ pin == NULL && !retried && sshkey_is_sk(ca) &&
+ (ca->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
+ if ((pin = read_passphrase("Enter PIN for CA "
+ "key: ", RP_ALLOW_STDIN)) == NULL)
+ fatal_f("couldn't read PIN");
+ retried = 1;
+ goto retry;
+ }
+#endif
if (r != 0)
fatal_r(r, "Couldn't certify key %s", tmp);
}
@@ -2530,6 +2549,7 @@ sign_one(struct sshkey *signkey, const char *filename, int fd,
fprintf(stderr, "Signing file %s\n", filename);
}
if (signer == NULL && sshkey_is_sk(signkey)) {
+#ifndef WINDOWS
if ((signkey->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
xasprintf(&prompt, "Enter PIN for %s key: ",
sshkey_type(signkey));
@@ -2537,6 +2557,7 @@ sign_one(struct sshkey *signkey, const char *filename, int fd,
RP_ALLOW_STDIN)) == NULL)
fatal_f("couldn't read PIN");
}
+#endif
if ((signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
if ((fp = sshkey_fingerprint(signkey, fingerprint_hash,
SSH_FP_DEFAULT)) == NULL)
@@ -3615,6 +3636,7 @@ main(int argc, char **argv)
}
if ((attest = sshbuf_new()) == NULL)
fatal("sshbuf_new failed");
+#ifndef WINDOWS
if ((sk_flags &
(SSH_SK_USER_VERIFICATION_REQD|SSH_SK_RESIDENT_KEY))) {
passphrase = read_passphrase("Enter PIN for "
@@ -3622,6 +3644,9 @@ main(int argc, char **argv)
} else {
passphrase = NULL;
}
+#else
+ passphrase = NULL;
+#endif
for (i = 0 ; ; i++) {
fflush(stdout);
r = sshsk_enroll(type, sk_provider, sk_device,
diff --git a/ssh-sk-client.c b/ssh-sk-client.c
index 2c66bcbfe61..431b672ef35 100644
--- a/ssh-sk-client.c
+++ b/ssh-sk-client.c
@@ -43,24 +43,85 @@
/* #define DEBUG_SK 1 */
+#ifdef WINDOWS
+static char module_path[PATH_MAX + 1];
+
+static char *
+find_helper_in_module_path(void)
+{
+ wchar_t path[PATH_MAX + 1];
+ DWORD n;
+ char *ep;
+
+ memset(module_path, 0, sizeof(module_path));
+ memset(path, 0, sizeof(path));
+ if ((n = GetModuleFileNameW(NULL, path, PATH_MAX)) == 0 ||
+ n >= PATH_MAX) {
+ error_f("GetModuleFileNameW failed");
+ return NULL;
+ }
+ if (wcstombs_s(NULL, module_path, sizeof(module_path), path,
+ sizeof(module_path) - 1) != 0) {
+ error_f("wcstombs_s failed");
+ return NULL;
+ }
+ if ((ep = strrchr(module_path, '\\')) == NULL) {
+ error_f("couldn't locate trailing \\");
+ return NULL;
+ }
+ *(++ep) = '\0'; /* trim */
+ strlcat(module_path, "ssh-sk-helper.exe", sizeof(module_path) - 1);
+
+ return module_path;
+}
+
+static char *
+find_helper(void)
+{
+ char *helper;
+ char module_path[PATH_MAX + 1];
+ char *ep;
+ DWORD n;
+
+ if ((helper = getenv("SSH_SK_HELPER")) == NULL || strlen(helper) == 0) {
+ if ((helper = find_helper_in_module_path()) == NULL)
+ helper = _PATH_SSH_SK_HELPER;
+ }
+ if (!path_absolute(helper)) {
+ error_f("helper \"%s\" unusable: path not absolute", helper);
+ return NULL;
+ }
+ debug_f("using \"%s\" as helper", helper);
+
+ return helper;
+}
+#endif /* WINDOWS */
+
static int
start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int))
{
-#ifndef ENABLE_SK
- /* TODO - This is added temporarily to resolve build errors.
- * The below logic has to be converted using posix_internal() APIs as windows doesn't support fork.
- */
- return SSH_ERR_SYSTEM_ERROR;
-#else
void (*osigchld)(int);
int oerrno, pair[2];
pid_t pid;
char *helper, *verbosity = NULL;
+#ifdef WINDOWS
+ int r, actions_inited = 0;
+ char *av[3];
+ posix_spawn_file_actions_t actions;
+#endif
*fdp = -1;
*pidp = 0;
*osigchldp = SIG_DFL;
+#ifdef WINDOWS
+ r = SSH_ERR_SYSTEM_ERROR;
+ pair[0] = pair[1] = -1;
+#endif
+#ifdef WINDOWS
+ if ((helper = find_helper()) == NULL)
+ goto out;
+#else
helper = getenv("SSH_SK_HELPER");
if (helper == NULL || strlen(helper) == 0)
helper = _PATH_SSH_SK_HELPER;
@@ -70,6 +131,8 @@ start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int))
errno = oerrno;
return SSH_ERR_SYSTEM_ERROR;
}
+#endif
+
#ifdef DEBUG_SK
verbosity = "-vvv";
#endif
@@ -77,9 +140,39 @@ start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int))
/* Start helper */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
error("socketpair: %s", strerror(errno));
+#ifdef WINDOWS
+ goto out;
+#else
return SSH_ERR_SYSTEM_ERROR;
+#endif
}
osigchld = ssh_signal(SIGCHLD, SIG_DFL);
+
+#ifdef WINDOWS
+ if (posix_spawn_file_actions_init(&actions) != 0) {
+ error_f("posix_spawn_file_actions_init failed");
+ goto out;
+ }
+ actions_inited = 1;
+ if (posix_spawn_file_actions_adddup2(&actions, pair[1],
+ STDIN_FILENO) != 0 ||
+ posix_spawn_file_actions_adddup2(&actions, pair[1],
+ STDOUT_FILENO) != 0) {
+ error_f("posix_spawn_file_actions_adddup2 failed");
+ goto out;
+ }
+#endif
+
+#ifdef WINDOWS
+ av[0] = helper;
+ av[1] = verbosity;
+ av[2] = NULL;
+ if (posix_spawnp((pid_t *)&pid, av[0], &actions, NULL, av, NULL) != 0) {
+ error_f("posix_spawnp failed");
+ goto out;
+ }
+ r = 0;
+#else
if ((pid = fork()) == -1) {
oerrno = errno;
error("fork: %s", strerror(errno));
@@ -105,12 +198,24 @@ start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int))
_exit(1);
}
close(pair[1]);
-
+#endif
/* success */
debug3_f("started pid=%ld", (long)pid);
*fdp = pair[0];
*pidp = pid;
*osigchldp = osigchld;
+#ifdef WINDOWS
+ pair[0] = -1;
+out:
+ if (pair[0] != -1)
+ close(pair[0]);
+ if (pair[1] != -1)
+ close(pair[1]);
+ if (actions_inited)
+ posix_spawn_file_actions_destroy(&actions);
+
+ return r;
+#else
return 0;
#endif
}
diff --git a/sshconnect2.c b/sshconnect2.c
index a33daa5fa72..c302d832866 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1281,8 +1281,21 @@ identity_sign(struct identity *id, u_char **sigp, size_t *lenp,
}
sign_key = prv;
if (sshkey_is_sk(sign_key)) {
+#ifdef WINDOWS
+ /*
+ * Don't prompt for FIDO2 PINs by default on Windows.
+ * The odds are we are communicating with webauthn.dll,
+ * which handles this internally. In the event we are
+ * talking directly to a FIDO2 device and a PIN is
+ * required, sshkey_sign() will return WRONG_PASSPHRASE
+ * and we will prompt for a PIN when we retry.
+ */
+ if ((sign_key->sk_flags &
+ SSH_SK_USER_VERIFICATION_REQD) && 0) {
+#else
if ((sign_key->sk_flags &
SSH_SK_USER_VERIFICATION_REQD)) {
+#endif
retry_pin:
xasprintf(&prompt, "Enter PIN for %s key %s: ",
sshkey_type(sign_key), id->filename);
diff --git a/sshsig.c b/sshsig.c
index 4ce4674cd42..1fbabf66612 100644
--- a/sshsig.c
+++ b/sshsig.c
@@ -32,6 +32,9 @@
#include "sshsig.h"
#include "ssherr.h"
#include "sshkey.h"
+#ifdef WINDOWS
+#include "sk-api.h" /* XXX for SSH_SK_USER_VERIFICATION_REQD */
+#endif
#include "match.h"
#include "digest.h"
@@ -563,6 +566,10 @@ sshsig_sign_fd(struct sshkey *key, const char *hashalg,
{
struct sshbuf *b = NULL;
int r = SSH_ERR_INTERNAL_ERROR;
+#ifdef WINDOWS
+ int retried = 0;
+ char *pin = NULL, *prompt = NULL;
+#endif
if (hashalg == NULL)
hashalg = HASHALG_DEFAULT;
@@ -572,12 +579,38 @@ sshsig_sign_fd(struct sshkey *key, const char *hashalg,
error_fr(r, "hash_file");
return r;
}
+#ifdef WINDOWS
+ retry:
+#endif
if ((r = sshsig_wrap_sign(key, hashalg, sk_provider, sk_pin, b,
- sig_namespace, out, signer, signer_ctx)) != 0)
+ sig_namespace, out, signer, signer_ctx)) != 0) {
+#ifdef WINDOWS
+ if (r == SSH_ERR_KEY_WRONG_PASSPHRASE && signer == NULL &&
+ sshkey_is_sk(key) && sk_pin == NULL && !retried &&
+ (key->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
+ xasprintf(&prompt, "Enter PIN for %s key: ",
+ sshkey_type(key));
+ if ((pin = read_passphrase(prompt,
+ RP_ALLOW_STDIN)) == NULL) {
+ debug_f("couldn't read PIN");
+ goto out;
+ }
+ sk_pin = pin;
+ retried = 1;
+ goto retry;
+ }
+ error_fr(r, "sshsig_wrap_sign");
+#endif
goto out;
+ }
/* success */
r = 0;
out:
+#ifdef WINDOWS
+ free(prompt);
+ if (pin != NULL)
+ freezero(pin, strlen(pin));
+#endif
sshbuf_free(b);
return r;
}