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; }