diff --git a/contrib/win32/openssh/Win32-OpenSSH.sln b/contrib/win32/openssh/Win32-OpenSSH.sln
index c1667f5ca..0af233535 100644
--- a/contrib/win32/openssh/Win32-OpenSSH.sln
+++ b/contrib/win32/openssh/Win32-OpenSSH.sln
@@ -164,6 +164,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ssh-sk-helper", "ssh-sk-hel
{0D02F0F0-013B-4EE3-906D-86517F3822C0} = {0D02F0F0-013B-4EE3-906D-86517F3822C0}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ssh-pkcs11-helper", "ssh-pkcs11-helper.vcxproj", "{21D772C3-0EB0-47B7-A93C-FF624675A58D}"
+ 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
@@ -528,6 +536,22 @@ Global
{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
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Debug|ARM.ActiveCfg = Debug|ARM
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Debug|ARM.Build.0 = Debug|ARM
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Debug|ARM64.Build.0 = Debug|ARM64
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Debug|x64.ActiveCfg = Debug|x64
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Debug|x64.Build.0 = Debug|x64
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Debug|x86.ActiveCfg = Debug|Win32
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Debug|x86.Build.0 = Debug|Win32
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Release|ARM.ActiveCfg = Release|ARM
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Release|ARM.Build.0 = Release|ARM
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Release|ARM64.ActiveCfg = Release|ARM64
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Release|ARM64.Build.0 = Release|ARM64
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Release|x64.ActiveCfg = Release|x64
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Release|x64.Build.0 = Release|x64
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Release|x86.ActiveCfg = Release|Win32
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -555,6 +579,7 @@ Global
{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}
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D} = {17322AAF-808F-4646-AD37-5B0EDDCB8F3E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0AC224E8-C215-4270-954A-A2ACEE06DE58}
diff --git a/contrib/win32/openssh/keygen.vcxproj b/contrib/win32/openssh/keygen.vcxproj
index 5ba9b28d0..e986936ec 100644
--- a/contrib/win32/openssh/keygen.vcxproj
+++ b/contrib/win32/openssh/keygen.vcxproj
@@ -403,6 +403,7 @@
+
diff --git a/contrib/win32/openssh/keygen.vcxproj.filters b/contrib/win32/openssh/keygen.vcxproj.filters
index 012a334e4..78ab638bd 100644
--- a/contrib/win32/openssh/keygen.vcxproj.filters
+++ b/contrib/win32/openssh/keygen.vcxproj.filters
@@ -30,10 +30,13 @@
Source Files
+
+ Source Files
+
Resource Files
-
+
\ No newline at end of file
diff --git a/contrib/win32/openssh/libssh.vcxproj.filters b/contrib/win32/openssh/libssh.vcxproj.filters
index 6f00d47f3..cc3edebd6 100644
--- a/contrib/win32/openssh/libssh.vcxproj.filters
+++ b/contrib/win32/openssh/libssh.vcxproj.filters
@@ -48,7 +48,6 @@
-
diff --git a/contrib/win32/openssh/ssh-agent.vcxproj b/contrib/win32/openssh/ssh-agent.vcxproj
index 53316a4cf..47af7ae0e 100644
--- a/contrib/win32/openssh/ssh-agent.vcxproj
+++ b/contrib/win32/openssh/ssh-agent.vcxproj
@@ -406,7 +406,8 @@
-
+
+
diff --git a/contrib/win32/openssh/ssh-pkcs11-helper.vcxproj b/contrib/win32/openssh/ssh-pkcs11-helper.vcxproj
new file mode 100644
index 000000000..88379db24
--- /dev/null
+++ b/contrib/win32/openssh/ssh-pkcs11-helper.vcxproj
@@ -0,0 +1,407 @@
+
+
+
+
+
+ Debug
+ ARM
+
+
+ Debug
+ ARM64
+
+
+ Debug
+ Win32
+
+
+ Release
+ ARM
+
+
+ Release
+ ARM64
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {21D772C3-0EB0-47B7-A93C-FF624675A58D}
+ Win32Proj
+ sshpkcs11helper
+ $(WindowsSDKVersion)
+ ssh-pkcs11-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)\
+ $(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ true
+ $(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\$(TargetName)\
+ $(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;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-x86-Path);$(ZLib-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;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-x64-Path);$(ZLib-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;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-arm64-Path);$(ZLib-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;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-arm-Path);$(ZLib-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;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-x86-Path);$(ZLib-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;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-x64-Path);$(ZLib-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;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-arm64-Path);$(ZLib-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;setupapi.lib;hid.lib;$(SSLLib)$(AdditionalDependentLibs);%(AdditionalDependencies)
+ $(OpenSSH-Lib-Path)$(Platform)\$(Configuration);$(LibreSSL-arm-Path);$(ZLib-arm-Path);%(AdditionalLibraryDirectories)
+ wmainCRTStartup
+ true
+ /debug /debugtype:cv,fixup /opt:ref /opt:icf /incremental:no /ignore:4099 %(AdditionalOptions)
+
+
+ targetos.manifest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/contrib/win32/openssh/ssh.vcxproj.filters b/contrib/win32/openssh/ssh.vcxproj.filters
index a6aed6932..f05cc1fed 100644
--- a/contrib/win32/openssh/ssh.vcxproj.filters
+++ b/contrib/win32/openssh/ssh.vcxproj.filters
@@ -314,6 +314,9 @@
Source Files
+
+ Source Files
+
diff --git a/contrib/win32/win32compat/ssh-agent/agent-request.h b/contrib/win32/win32compat/ssh-agent/agent-request.h
index f0dcbb978..858360a22 100644
--- a/contrib/win32/win32compat/ssh-agent/agent-request.h
+++ b/contrib/win32/win32compat/ssh-agent/agent-request.h
@@ -18,5 +18,7 @@ int process_request_identities(struct sshbuf*, struct sshbuf*, struct agent_conn
int process_sign_request(struct sshbuf*, struct sshbuf*, struct agent_connection*);
int process_remove_key(struct sshbuf*, struct sshbuf*, struct agent_connection*);
int process_remove_all(struct sshbuf*, struct sshbuf*, struct agent_connection*);
+int process_add_smartcard_key(struct sshbuf*, struct sshbuf*, struct agent_connection*);
+int process_remove_smartcard_key(struct sshbuf*, struct sshbuf*, struct agent_connection*);
/* auth */
diff --git a/contrib/win32/win32compat/ssh-agent/agent.c b/contrib/win32/win32compat/ssh-agent/agent.c
index c1f8bdd8c..243db2110 100644
--- a/contrib/win32/win32compat/ssh-agent/agent.c
+++ b/contrib/win32/win32compat/ssh-agent/agent.c
@@ -37,6 +37,7 @@
#define BUFSIZE 5 * 1024
char* sshagent_con_username;
+int sshagent_client_pid;
static HANDLE ioc_port = NULL;
static BOOL debug_mode = FALSE;
@@ -277,6 +278,7 @@ get_con_client_info(struct agent_connection* con)
error("cannot retrieve client impersonation token");
goto done;
}
+ sshagent_client_pid = client_pid;
if (GetTokenInformation(client_primary_token, TokenUser, NULL, 0, &info_len) == TRUE ||
(info = (TOKEN_USER*)malloc(info_len)) == NULL ||
diff --git a/contrib/win32/win32compat/ssh-agent/agent.h b/contrib/win32/win32compat/ssh-agent/agent.h
index 2fb5e8236..6e8d8a8a1 100644
--- a/contrib/win32/win32compat/ssh-agent/agent.h
+++ b/contrib/win32/win32compat/ssh-agent/agent.h
@@ -8,6 +8,8 @@
#define SSH_AGENT_ROOT SSH_REGISTRY_ROOT L"\\Agent"
#define SSH_KEYS_KEY L"Keys"
#define SSH_KEYS_ROOT SSH_AGENT_ROOT L"\\" SSH_KEYS_KEY
+#define SSH_PKCS11_PROVIDERS_KEY L"PKCS11_Providers"
+#define SSH_PKCS11_PROVIDERS_ROOT SSH_AGENT_ROOT L"\\" SSH_PKCS11_PROVIDERS_KEY
#define HEADER_SIZE 4
diff --git a/contrib/win32/win32compat/ssh-agent/connection.c b/contrib/win32/win32compat/ssh-agent/connection.c
index ad6aa668c..d9d3a7170 100644
--- a/contrib/win32/win32compat/ssh-agent/connection.c
+++ b/contrib/win32/win32compat/ssh-agent/connection.c
@@ -30,6 +30,7 @@
*/
#include "agent.h"
#include "agent-request.h"
+#include "config.h"
#pragma warning(push, 3)
@@ -156,6 +157,15 @@ process_request(struct agent_connection* con)
case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
r = process_remove_all(request, response, con);
break;
+#ifdef ENABLE_PKCS11
+ case SSH_AGENTC_ADD_SMARTCARD_KEY:
+ case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED:
+ r = process_add_smartcard_key(request, response, con);
+ break;
+ case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
+ r = process_remove_smartcard_key(request, response, con);
+ break;
+#endif /* ENABLE_PKCS11 */
default:
debug("unknown agent request %d", type);
r = -1;
diff --git a/contrib/win32/win32compat/ssh-agent/keyagent-request.c b/contrib/win32/win32compat/ssh-agent/keyagent-request.c
index 76b81b049..37909ed99 100644
--- a/contrib/win32/win32compat/ssh-agent/keyagent-request.c
+++ b/contrib/win32/win32compat/ssh-agent/keyagent-request.c
@@ -31,18 +31,33 @@
#include "agent.h"
#include "agent-request.h"
+#include "config.h"
#include
+#ifdef ENABLE_PKCS11
+#include "ssh-pkcs11.h"
+#endif
#pragma warning(push, 3)
#define MAX_KEY_LENGTH 255
-#define MAX_VALUE_NAME 16383
+#define MAX_VALUE_NAME_LENGTH 16383
+#define MAX_VALUE_DATA_LENGTH 2048
/*
* get registry root where keys are stored
* user keys are stored in user's hive
* while system keys (host keys) in HKLM
*/
+
+extern struct sshkey *
+lookup_key(const struct sshkey *k);
+
+extern void
+add_key(struct sshkey *k, char *name);
+
+extern void
+del_all_keys();
+
static int
get_user_root(struct agent_connection* con, HKEY *root)
{
@@ -113,6 +128,89 @@ done:
return success? 0: -1;
}
+/*
+ * in user_root sub tree under key_name key
+ * remove all sub keys with value name value_name_to_remove
+ * and value data value_data_to_remove
+ */
+static int
+remove_matching_subkeys_from_registry(HKEY user_root, wchar_t const* key_name, wchar_t const* value_name_to_remove, char const* value_data_to_remove) {
+ int index = 0, success = 0;
+ DWORD data_len;
+ HKEY root = 0, sub = 0;
+ char *data = NULL;
+ wchar_t sub_name[MAX_KEY_LENGTH];
+ DWORD sub_name_len = MAX_KEY_LENGTH;
+ LSTATUS retCode;
+
+ if (RegOpenKeyExW(user_root, key_name, 0, DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY, &root) != 0) {
+ goto done;
+ }
+
+ while (1) {
+ sub_name_len = MAX_KEY_LENGTH;
+ if (sub) {
+ RegCloseKey(sub);
+ sub = NULL;
+ }
+ if ((retCode = RegEnumKeyExW(root, index++, sub_name, &sub_name_len, NULL, NULL, NULL, NULL)) == 0) {
+ if (RegOpenKeyExW(root, sub_name, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &sub) == 0 &&
+ RegQueryValueExW(sub, value_name_to_remove, 0, NULL, NULL, &data_len) == 0 &&
+ data_len <= MAX_VALUE_DATA_LENGTH) {
+
+ if (data)
+ free(data);
+ data = NULL;
+
+ if ((data = malloc(data_len + 1)) == NULL ||
+ RegQueryValueExW(sub, value_name_to_remove, 0, NULL, data, &data_len) != 0)
+ goto done;
+ data[data_len] = '\0';
+ if (strncmp(data, value_data_to_remove, data_len) == 0) {
+ if (RegDeleteTreeW(root, sub_name) != 0)
+ goto done;
+ --index;
+ }
+ }
+ }
+ else {
+ if (retCode == ERROR_NO_MORE_ITEMS)
+ success = 1;
+ break;
+ }
+ }
+done:
+ if (data)
+ free(data);
+ if (root)
+ RegCloseKey(root);
+ if (sub)
+ RegCloseKey(sub);
+ return success ? 0 : -1;
+}
+
+/*
+ * in user_root sub tree under key_name key
+ * check whether sub_key_name sub key exists
+ */
+static int
+is_reg_sub_key_exists(HKEY user_root, wchar_t const* key_name, char const* sub_key_name) {
+ int rv = 0;
+ HKEY root = 0, sub = 0;
+
+ if (RegOpenKeyExW(user_root, key_name, 0, STANDARD_RIGHTS_READ | KEY_WOW64_64KEY, &root) != 0 ||
+ RegOpenKeyExA(root, sub_key_name, 0, STANDARD_RIGHTS_READ | KEY_WOW64_64KEY, &sub) != 0 || !sub) {
+ rv = 0;
+ goto done;
+ }
+
+ rv = 1;
+done:
+ if (root)
+ RegCloseKey(root);
+ return rv;
+}
+
#define REG_KEY_SDDL L"D:P(A;; GA;;; SY)(A;; GA;;; BA)"
int
@@ -162,7 +260,7 @@ process_add_identity(struct sshbuf* request, struct sshbuf* response, struct age
RegSetValueExW(sub, L"pub", 0, REG_BINARY, pubkey_blob, (DWORD)pubkey_blob_len) != 0 ||
RegSetValueExW(sub, L"type", 0, REG_DWORD, (BYTE*)&key->type, 4) != 0 ||
RegSetValueExW(sub, L"comment", 0, REG_BINARY, comment, (DWORD)comment_len) != 0 ) {
- debug("failed to add key to store");
+ error("failed to add key to store");
goto done;
}
@@ -209,37 +307,45 @@ static int sign_blob(const struct sshkey *pubkey, u_char ** sig, size_t *siglen,
struct sshbuf* tmpbuf = NULL;
char *keyblob = NULL;
const char *sk_provider = NULL;
+#ifdef ENABLE_PKCS11
+ int is_pkcs11_key = 0;
+#endif /* ENABLE_PKCS11 */
*sig = NULL;
*siglen = 0;
- if ((thumbprint = sshkey_fingerprint(pubkey, SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL ||
- get_user_root(con, &user_root) != 0 ||
- RegOpenKeyExW(user_root, SSH_KEYS_ROOT,
- 0, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_WOW64_64KEY | KEY_ENUMERATE_SUB_KEYS, ®) != 0 ||
- RegOpenKeyExA(reg, thumbprint, 0,
- STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY, &sub) != 0 ||
- RegQueryValueExW(sub, NULL, 0, NULL, NULL, ®datalen) != ERROR_SUCCESS ||
- (regdata = malloc(regdatalen)) == NULL ||
- RegQueryValueExW(sub, NULL, 0, NULL, regdata, ®datalen) != ERROR_SUCCESS ||
- convert_blob(con, regdata, regdatalen, &keyblob, &keyblob_len, FALSE) != 0 ||
- (tmpbuf = sshbuf_from(keyblob, keyblob_len)) == NULL)
- goto done;
-
+#ifdef ENABLE_PKCS11
+ if ((prikey = lookup_key(pubkey)) == NULL) {
+#endif /* ENABLE_PKCS11 */
+ if ((thumbprint = sshkey_fingerprint(pubkey, SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL ||
+ get_user_root(con, &user_root) != 0 ||
+ RegOpenKeyExW(user_root, SSH_KEYS_ROOT,
+ 0, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_WOW64_64KEY | KEY_ENUMERATE_SUB_KEYS, ®) != 0 ||
+ RegOpenKeyExA(reg, thumbprint, 0,
+ STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY, &sub) != 0 ||
+ RegQueryValueExW(sub, NULL, 0, NULL, NULL, ®datalen) != ERROR_SUCCESS ||
+ (regdata = malloc(regdatalen)) == NULL ||
+ RegQueryValueExW(sub, NULL, 0, NULL, regdata, ®datalen) != ERROR_SUCCESS ||
+ convert_blob(con, regdata, regdatalen, &keyblob, &keyblob_len, FALSE) != 0 ||
+ (tmpbuf = sshbuf_from(keyblob, keyblob_len)) == NULL ||
+ sshkey_private_deserialize(tmpbuf, &prikey) != 0) {
+ error("cannot retrieve and deserialize key from registry");
+ goto done;
+ }
+#ifdef ENABLE_PKCS11
+ }
+ else
+ is_pkcs11_key = 1;
+#endif /* ENABLE_PKCS11 */
if (flags & SSH_AGENT_RSA_SHA2_256)
algo = "rsa-sha2-256";
else if (flags & SSH_AGENT_RSA_SHA2_512)
algo = "rsa-sha2-512";
- 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");
+ if (sshkey_sign(prikey, sig, siglen, blob, blen, algo, sk_provider, NULL, 0) != 0) {
+ error("cannot sign using retrieved key");
goto done;
}
@@ -252,8 +358,11 @@ done:
free(regdata);
if (tmpbuf)
sshbuf_free(tmpbuf);
- if (prikey)
- sshkey_free(prikey);
+#ifdef ENABLE_PKCS11
+ if (!is_pkcs11_key)
+#endif /* ENABLE_PKCS11 */
+ if (prikey)
+ sshkey_free(prikey);
if (thumbprint)
free(thumbprint);
if (user_root)
@@ -275,6 +384,74 @@ process_sign_request(struct sshbuf* request, struct sshbuf* response, struct age
int r, request_invalid = 0, success = 0;
struct sshkey *key = NULL;
+#ifdef ENABLE_PKCS11
+ int i, count = 0, index = 0;;
+ wchar_t sub_name[MAX_KEY_LENGTH];
+ DWORD sub_name_len = MAX_KEY_LENGTH;
+ DWORD pin_len, epin_len, provider_len;
+ char *pin = NULL, *npin = NULL, *epin = NULL, *provider = NULL;
+ HKEY root = 0, sub = 0, user_root = 0;
+ struct sshkey **keys = NULL;
+ SECURITY_ATTRIBUTES sa = { 0, NULL, 0 };
+
+ pkcs11_init(0);
+
+ memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
+ sa.nLength = sizeof(sa);
+ if ((!ConvertStringSecurityDescriptorToSecurityDescriptorW(REG_KEY_SDDL, SDDL_REVISION_1, &sa.lpSecurityDescriptor, &sa.nLength)) ||
+ get_user_root(con, &user_root) != 0 ||
+ RegCreateKeyExW(user_root, SSH_PKCS11_PROVIDERS_ROOT, 0, 0, 0, KEY_WRITE | STANDARD_RIGHTS_READ | KEY_ENUMERATE_SUB_KEYS | KEY_WOW64_64KEY, &sa, &root, NULL) != 0) {
+ goto done;
+ }
+
+ while (1) {
+ sub_name_len = MAX_KEY_LENGTH;
+ if (sub) {
+ RegCloseKey(sub);
+ sub = NULL;
+ }
+ if (RegEnumKeyExW(root, index++, sub_name, &sub_name_len, NULL, NULL, NULL, NULL) == 0) {
+ if (RegOpenKeyExW(root, sub_name, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &sub) == 0 &&
+ RegQueryValueExW(sub, L"provider", 0, NULL, NULL, &provider_len) == 0 &&
+ RegQueryValueExW(sub, L"pin", 0, NULL, NULL, &epin_len) == 0) {
+ if ((epin = malloc(epin_len + 1)) == NULL ||
+ (provider = malloc(provider_len + 1)) == NULL ||
+ RegQueryValueExW(sub, L"provider", 0, NULL, provider, &provider_len) != 0 ||
+ RegQueryValueExW(sub, L"pin", 0, NULL, epin, &epin_len) != 0)
+ goto done;
+ provider[provider_len] = '\0';
+ epin[epin_len] = '\0';
+ if (convert_blob(con, epin, epin_len, &pin, &pin_len, 0) != 0 ||
+ (npin = realloc(pin, pin_len + 1)) == NULL) {
+ goto done;
+ }
+ pin = npin;
+ pin[pin_len] = '\0';
+ count = pkcs11_add_provider(provider, pin, &keys, NULL);
+ for (i = 0; i < count; i++) {
+ add_key(keys[i], provider);
+ }
+ free(keys);
+ if (provider)
+ free(provider);
+ if (pin) {
+ SecureZeroMemory(pin, (DWORD)pin_len);
+ free(pin);
+ }
+ if (epin) {
+ SecureZeroMemory(epin, (DWORD)epin_len);
+ free(epin);
+ }
+ provider = NULL;
+ pin = NULL;
+ epin = NULL;
+ }
+ }
+ else
+ break;
+ }
+#endif /* ENABLE_PKCS11 */
+
if (sshbuf_get_string_direct(request, &blob, &blen) != 0 ||
sshbuf_get_string_direct(request, &data, &dlen) != 0 ||
sshbuf_get_u32(request, &flags) != 0 ||
@@ -298,8 +475,7 @@ done:
sshbuf_put_string(response, signature, slen) != 0) {
r = -1;
}
- } else
- if (sshbuf_put_u8(response, SSH_AGENT_FAILURE) != 0)
+ } else if (sshbuf_put_u8(response, SSH_AGENT_FAILURE) != 0)
r = -1;
}
@@ -307,6 +483,26 @@ done:
sshkey_free(key);
if (signature)
free(signature);
+#ifdef ENABLE_PKCS11
+ del_all_keys();
+ pkcs11_terminate();
+ if (provider)
+ free(provider);
+ if (pin) {
+ SecureZeroMemory(pin, (DWORD)pin_len);
+ free(pin);
+ }
+ if (epin) {
+ SecureZeroMemory(epin, (DWORD)epin_len);
+ free(epin);
+ }
+ if (user_root)
+ RegCloseKey(user_root);
+ if (root)
+ RegCloseKey(root);
+ if (sub)
+ RegCloseKey(sub);
+#endif /* ENABLE_PKCS11 */
return r;
}
@@ -362,6 +558,7 @@ process_remove_all(struct sshbuf* request, struct sshbuf* response, struct agent
}
RegDeleteTreeW(root, SSH_KEYS_KEY);
+ RegDeleteTreeW(root, SSH_PKCS11_PROVIDERS_KEY);
done:
r = 0;
if (sshbuf_put_u8(response, SSH_AGENT_SUCCESS) != 0)
@@ -374,6 +571,189 @@ done:
return r;
}
+#ifdef ENABLE_PKCS11
+int process_add_smartcard_key(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
+{
+ char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
+ int i, count = 0, r = 0, request_invalid = 0, success = 0;
+ struct sshkey **keys = NULL;
+ struct sshkey* key = NULL;
+ size_t pubkey_blob_len, provider_len, pin_len, epin_len;
+ u_char *pubkey_blob = NULL;
+ char *thumbprint = NULL;
+ char *epin = NULL;
+ HKEY reg = 0, sub = 0, user_root = 0;
+ SECURITY_ATTRIBUTES sa = { 0, NULL, 0 };
+
+ pkcs11_init(0);
+
+ if ((r = sshbuf_get_cstring(request, &provider, &provider_len)) != 0 ||
+ (r = sshbuf_get_cstring(request, &pin, &pin_len)) != 0 ||
+ pin_len > 256) {
+ error("add smartcard request is invalid");
+ request_invalid = 1;
+ goto done;
+ }
+
+ if (realpath(provider, canonical_provider) == NULL) {
+ error("failed PKCS#11 add of \"%.100s\": realpath: %s",
+ provider, strerror(errno));
+ request_invalid = 1;
+ goto done;
+ }
+
+ // Remove 'drive root' if exists
+ if (canonical_provider[0] == '/')
+ memmove(canonical_provider, canonical_provider + 1, strlen(canonical_provider));
+ if (get_user_root(con, &user_root) != 0 ||
+ is_reg_sub_key_exists(user_root, SSH_PKCS11_PROVIDERS_ROOT, canonical_provider))
+ goto done;
+
+ count = pkcs11_add_provider(canonical_provider, pin, &keys, NULL);
+ if (count <= 0) {
+ debug("failed to add key to store");
+ goto done;
+ }
+ for (i = 0; i < count; i++) {
+ key = keys[i];
+ if (sa.lpSecurityDescriptor)
+ LocalFree(sa.lpSecurityDescriptor);
+ if (reg) {
+ RegCloseKey(reg);
+ reg = NULL;
+ }
+ if (sub) {
+ RegCloseKey(sub);
+ sub = NULL;
+ }
+ memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
+ sa.nLength = sizeof(sa);
+ if ((!ConvertStringSecurityDescriptorToSecurityDescriptorW(REG_KEY_SDDL, SDDL_REVISION_1, &sa.lpSecurityDescriptor, &sa.nLength)) ||
+ sshkey_to_blob(key, &pubkey_blob, &pubkey_blob_len) != 0 ||
+ ((thumbprint = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL) ||
+ RegCreateKeyExW(user_root, SSH_KEYS_ROOT, 0, 0, 0, KEY_WRITE | KEY_WOW64_64KEY, &sa, ®, NULL) != 0 ||
+ RegCreateKeyExA(reg, thumbprint, 0, 0, 0, KEY_WRITE | KEY_WOW64_64KEY, &sa, &sub, NULL) != 0 ||
+ RegSetValueExW(sub, NULL, 0, REG_BINARY, pubkey_blob, (DWORD)pubkey_blob_len) != 0 ||
+ RegSetValueExW(sub, L"pub", 0, REG_BINARY, pubkey_blob, (DWORD)pubkey_blob_len) != 0 ||
+ RegSetValueExW(sub, L"type", 0, REG_DWORD, (BYTE*)&key->type, 4) != 0 ||
+ RegSetValueExW(sub, L"comment", 0, REG_BINARY, canonical_provider, (DWORD)strlen(canonical_provider)) != 0) {
+ error("failed to add key to store");
+ goto done;
+ }
+ }
+
+ debug("added smartcard keys to store");
+
+ memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
+ sa.nLength = sizeof(sa);
+ if ((!ConvertStringSecurityDescriptorToSecurityDescriptorW(REG_KEY_SDDL, SDDL_REVISION_1, &sa.lpSecurityDescriptor, &sa.nLength)) ||
+ convert_blob(con, pin, (DWORD)pin_len, &epin, (DWORD*)&epin_len, 1) != 0 ||
+ RegCreateKeyExW(user_root, SSH_PKCS11_PROVIDERS_ROOT, 0, 0, 0, KEY_WRITE | KEY_WOW64_64KEY, &sa, ®, NULL) != 0 ||
+ RegCreateKeyExA(reg, canonical_provider, 0, 0, 0, KEY_WRITE | KEY_WOW64_64KEY, &sa, &sub, NULL) != 0 ||
+ RegSetValueExW(sub, L"provider", 0, REG_BINARY, canonical_provider, (DWORD)strlen(canonical_provider)) != 0 ||
+ RegSetValueExW(sub, L"pin", 0, REG_BINARY, epin, (DWORD)epin_len) != 0) {
+ error("failed to add pkcs11 provider to store");
+ goto done;
+ }
+
+ debug("added pkcs11 provider to store");
+ success = 1;
+done:
+ r = 0;
+ if (request_invalid)
+ r = -1;
+ else if (sshbuf_put_u8(response, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE) != 0)
+ r = -1;
+
+ /* delete created reg keys if not succeeded*/
+ if ((success == 0) && reg) {
+ if (thumbprint)
+ RegDeleteKeyExA(reg, thumbprint, KEY_WOW64_64KEY, 0);
+ if (canonical_provider)
+ RegDeleteKeyExA(reg, canonical_provider, KEY_WOW64_64KEY, 0);
+ }
+
+ pkcs11_terminate();
+
+ if (sa.lpSecurityDescriptor)
+ LocalFree(sa.lpSecurityDescriptor);
+ for (i = 0; i < count; i++)
+ sshkey_free(keys[i]);
+ if (keys)
+ free(keys);
+ if (thumbprint)
+ free(thumbprint);
+ if (pubkey_blob)
+ free(pubkey_blob);
+ if (provider)
+ free(provider);
+ if (pin) {
+ SecureZeroMemory(pin, (DWORD)pin_len);
+ free(pin);
+ }
+ if (epin) {
+ SecureZeroMemory(epin, (DWORD)epin_len);
+ free(epin);
+ }
+ if (user_root)
+ RegCloseKey(user_root);
+ if (reg)
+ RegCloseKey(reg);
+ if (sub)
+ RegCloseKey(sub);
+ return r;
+}
+
+int process_remove_smartcard_key(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
+{
+ char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX];
+ int r = 0, request_invalid = 0, success = 0, index = 0;
+ HKEY user_root = 0;
+
+ if ((r = sshbuf_get_cstring(request, &provider, NULL)) != 0 ||
+ (r = sshbuf_get_cstring(request, &pin, NULL)) != 0) {
+ error("remove smartcard request is invalid");
+ request_invalid = 1;
+ goto done;
+ }
+
+ if (realpath(provider, canonical_provider) == NULL) {
+ error("failed PKCS#11 add of \"%.100s\": realpath: %s",
+ provider, strerror(errno));
+ request_invalid = 1;
+ goto done;
+ }
+
+ // Remove 'drive root' if exists
+ if (canonical_provider[0] == '/')
+ memmove(canonical_provider, canonical_provider + 1, strlen(canonical_provider));
+
+ if (get_user_root(con, &user_root) != 0 ||
+ !is_reg_sub_key_exists(user_root, SSH_PKCS11_PROVIDERS_ROOT, canonical_provider))
+ goto done;
+
+ if (remove_matching_subkeys_from_registry(user_root, SSH_KEYS_ROOT, L"comment", canonical_provider) != 0 ||
+ remove_matching_subkeys_from_registry(user_root, SSH_PKCS11_PROVIDERS_ROOT, L"provider", canonical_provider) != 0) {
+ goto done;
+ }
+
+ success = 1;
+done:
+ r = 0;
+ if (request_invalid)
+ r = -1;
+ else if (sshbuf_put_u8(response, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE) != 0)
+ r = -1;
+ if (provider)
+ free(provider);
+ if (pin)
+ free(pin);
+ if (user_root)
+ RegCloseKey(user_root);
+ return r;
+}
+#endif /* ENABLE_PKCS11 */
+
int
process_request_identities(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
{
@@ -473,6 +853,15 @@ int process_keyagent_request(struct sshbuf* request, struct sshbuf* response, st
return process_remove_key(request, response, con);
case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
return process_remove_all(request, response, con);
+#ifdef ENABLE_PKCS11
+ case SSH_AGENTC_ADD_SMARTCARD_KEY:
+ return process_add_smartcard_key(request, response, con);
+ case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED:
+ return process_add_smartcard_key(request, response, con);
+ case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
+ return process_remove_smartcard_key(request, response, con);
+ break;
+#endif /* ENABLE_PKCS11 */
default:
debug("unknown key agent request %d", type);
return -1;
diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c
index 01f590169..6a36d9474 100644
--- a/contrib/win32/win32compat/w32fd.c
+++ b/contrib/win32/win32compat/w32fd.c
@@ -1071,7 +1071,9 @@ spawn_child_internal(const char* cmd, char *const argv[], HANDLE in, HANDLE out,
if (strstr(cmd, "sshd.exe")) {
flags |= DETACHED_PROCESS;
}
-
+ if (strstr(cmd, "ssh-sk-helper.exe") || strstr(cmd, "ssh-pkcs11-helper.exe")) {
+ flags |= CREATE_NO_WINDOW;
+ }
if (is_bash_test_env()) {
flags |= CREATE_NO_WINDOW;
}
@@ -1309,3 +1311,15 @@ posix_spawnp(pid_t *pidp, const char *file, const posix_spawn_file_actions_t *fi
{
return posix_spawn_internal(pidp, file, file_actions, attrp, argv, envp, NULL, FALSE);
}
+
+int
+posix_spawn_as_user(pid_t *pidp, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], HANDLE user_token)
+{
+ return posix_spawn_internal(pidp, file, file_actions, attrp, argv, envp, user_token, TRUE);
+}
+
+int
+posix_spawnp_as_user(pid_t *pidp, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], HANDLE user_token)
+{
+ return posix_spawn_internal(pidp, file, file_actions, attrp, argv, envp, user_token, FALSE);
+}
\ No newline at end of file
diff --git a/pathnames.h b/pathnames.h
index 608cd176b..6c07f7989 100644
--- a/pathnames.h
+++ b/pathnames.h
@@ -131,8 +131,12 @@
/* Location of ssh-pkcs11-helper to support keys in tokens */
#ifndef _PATH_SSH_PKCS11_HELPER
+#ifdef WINDOWS
+#define _PATH_SSH_PKCS11_HELPER "C:\\Windows\\System32\\OpenSSH\\ssh-pkcs11-helper.exe"
+#else
#define _PATH_SSH_PKCS11_HELPER "/usr/libexec/ssh-pkcs11-helper"
#endif
+#endif
/* Location of ssh-sk-helper to support keys in security keys */
#ifndef _PATH_SSH_SK_HELPER
diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c
index e72473694..fe4f18fce 100644
--- a/ssh-pkcs11-client.c
+++ b/ssh-pkcs11-client.c
@@ -47,11 +47,140 @@
#include "ssh-pkcs11.h"
#include "ssherr.h"
+#ifdef WINDOWS
+#include "openbsd-compat/sys-queue.h"
+#define CRYPTOKI_COMPAT
+#include "pkcs11.h"
+
+static char module_path[PATH_MAX + 1];
+extern int sshagent_client_pid;
+
+struct pkcs11_provider {
+ char *name;
+ TAILQ_ENTRY(pkcs11_provider) next;
+};
+
+TAILQ_HEAD(, pkcs11_provider) pkcs11_providers;
+
+struct pkcs11_keyinfo {
+ struct sshkey *key;
+ char *providername, *label;
+ TAILQ_ENTRY(pkcs11_keyinfo) next;
+};
+
+TAILQ_HEAD(, pkcs11_keyinfo) pkcs11_keylist;
+
+#define MAX_MSG_LENGTH 10240 /*XXX*/
+
+/* input and output queue */
+struct sshbuf *iqueue;
+struct sshbuf *oqueue;
+
+void
+add_key(struct sshkey *k, char *name)
+{
+ struct pkcs11_keyinfo *ki;
+
+ ki = xcalloc(1, sizeof(*ki));
+ ki->providername = xstrdup(name);
+ ki->key = k;
+ TAILQ_INSERT_TAIL(&pkcs11_keylist, ki, next);
+}
+
+void
+del_all_keys()
+{
+ struct pkcs11_keyinfo *ki, *nxt;
+
+ for (ki = TAILQ_FIRST(&pkcs11_keylist); ki; ki = nxt) {
+ nxt = TAILQ_NEXT(ki, next);
+ TAILQ_REMOVE(&pkcs11_keylist, ki, next);
+ free(ki->providername);
+ sshkey_free(ki->key);
+ free(ki);
+ }
+}
+
+/* lookup matching 'private' key */
+struct sshkey *
+lookup_key(const struct sshkey *k)
+{
+ struct pkcs11_keyinfo *ki;
+
+ TAILQ_FOREACH(ki, &pkcs11_keylist, next) {
+ debug("check %p %s %s", ki, ki->providername, ki->label);
+ if (sshkey_equal(k, ki->key))
+ return (ki->key);
+ }
+ return (NULL);
+}
+
+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-pkcs11-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_PKCS11_HELPER")) == NULL || strlen(helper) == 0) {
+ if ((helper = find_helper_in_module_path()) == NULL)
+ helper = _PATH_SSH_PKCS11_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 */
+
/* borrows code from sftp-server and ssh-agent */
static int fd = -1;
static pid_t pid = -1;
+#ifdef WINDOWS
+static void
+pkcs11_terminate_helper() {
+ HANDLE helper = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
+ TerminateProcess(helper, 1);
+ CloseHandle(helper);
+}
+#endif /* WINDOWS */
+
static void
send_msg(struct sshbuf *m)
{
@@ -104,12 +233,26 @@ recv_msg(struct sshbuf *m)
int
pkcs11_init(int interactive)
{
+#ifdef WINDOWS
+ TAILQ_INIT(&pkcs11_providers);
+ TAILQ_INIT(&pkcs11_keylist);
+#endif /* WINDOWS */
return (0);
}
void
pkcs11_terminate(void)
{
+#ifdef WINDOWS
+ struct pkcs11_provider *p;
+
+ while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) {
+ // Send message to helper to gracefully unload providers
+ pkcs11_del_provider(p->name);
+ TAILQ_REMOVE(&pkcs11_providers, p, next);
+ }
+ pkcs11_terminate_helper();
+#endif /* WINDOWS */
if (fd >= 0)
close(fd);
}
@@ -273,6 +416,23 @@ pkcs11_start_helper(void)
{
int pair[2];
char *helper, *verbosity = NULL;
+#ifdef WINDOWS
+ int r, actions_inited = 0;
+ char *av[3];
+ posix_spawn_file_actions_t actions;
+ HANDLE client_token = NULL, client_process_handle = NULL;
+
+ r = SSH_ERR_SYSTEM_ERROR;
+ pair[0] = pair[1] = -1;
+
+ if ((helper = find_helper()) == NULL)
+ goto out;
+#endif /* WINDOWS */
+
+
+#ifdef DEBUG_PKCS11
+ verbosity = "-vvv";
+#endif
if (log_level_get() >= SYSLOG_LEVEL_DEBUG1)
verbosity = "-vvv";
@@ -286,6 +446,37 @@ pkcs11_start_helper(void)
error("socketpair: %s", strerror(errno));
return (-1);
}
+#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;
+ }
+
+ av[0] = helper;
+ av[1] = verbosity;
+ av[2] = NULL;
+
+ if ((client_process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, sshagent_client_pid)) == NULL ||
+ OpenProcessToken(client_process_handle, TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, &client_token) == FALSE) {
+ error_f("failed retrieve user token of the client process");
+ goto out;
+
+ }
+ if (posix_spawnp_as_user((pid_t *)&pid, av[0], &actions, NULL, av, NULL, client_token) != 0) {
+ error_f("failed to spwan process %s", av[0]);
+ goto out;
+ }
+ fd = pair[0];
+ r = 0;
+#else
if ((pid = fork()) == -1) {
error("fork: %s", strerror(errno));
return (-1);
@@ -309,6 +500,13 @@ pkcs11_start_helper(void)
close(pair[1]);
fd = pair[0];
return (0);
+#endif
+ /* success */
+ debug3_f("started pid=%ld", (long)pid);
+out:
+ if (client_token)
+ CloseHandle(client_token);
+ return r;
}
int
@@ -322,6 +520,7 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp,
size_t blen;
u_int nkeys, i;
struct sshbuf *msg;
+ struct pkcs11_provider *p;
if (fd < 0 && pkcs11_start_helper() < 0)
return (-1);
@@ -363,6 +562,12 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp,
} else {
nkeys = -1;
}
+
+#ifdef WINDOWS
+ p = xcalloc(1, sizeof(*p));
+ p->name = xstrdup(name);
+ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
+#endif /* WINDOWS */
sshbuf_free(msg);
return (nkeys);
}
diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h
index 81f1d7c5d..5caee9770 100644
--- a/ssh-pkcs11.h
+++ b/ssh-pkcs11.h
@@ -22,10 +22,12 @@
#define SSH_PKCS11_ERR_PIN_REQUIRED 4
#define SSH_PKCS11_ERR_PIN_LOCKED 5
-int pkcs11_init(int);
-void pkcs11_terminate(void);
-int pkcs11_add_provider(char *, char *, struct sshkey ***, char ***);
-int pkcs11_del_provider(char *);
+
+int pkcs11_init(int);
+void pkcs11_terminate(void);
+int pkcs11_add_provider(char *, char *, struct sshkey ***, char ***);
+int pkcs11_del_provider(char *);
+
#ifdef WITH_PKCS11_KEYGEN
struct sshkey *
pkcs11_gakp(char *, char *, unsigned int, char *, unsigned int,