From c89890c3a42a3316c4ea5c71f37e14dc9587e297 Mon Sep 17 00:00:00 2001 From: Yan Tesis Date: Thu, 17 Feb 2022 17:31:50 -0800 Subject: [PATCH] Adding PKCS11 support to SSH agent (#537) --- contrib/win32/openssh/Win32-OpenSSH.sln | 25 + contrib/win32/openssh/keygen.vcxproj | 1 + contrib/win32/openssh/keygen.vcxproj.filters | 5 +- contrib/win32/openssh/libssh.vcxproj.filters | 1 - contrib/win32/openssh/ssh-agent.vcxproj | 3 +- .../win32/openssh/ssh-pkcs11-helper.vcxproj | 407 ++++++++++++++++ contrib/win32/openssh/ssh.vcxproj.filters | 3 + .../win32compat/ssh-agent/agent-request.h | 2 + contrib/win32/win32compat/ssh-agent/agent.c | 2 + contrib/win32/win32compat/ssh-agent/agent.h | 2 + .../win32/win32compat/ssh-agent/connection.c | 10 + .../win32compat/ssh-agent/keyagent-request.c | 441 ++++++++++++++++-- contrib/win32/win32compat/w32fd.c | 16 +- pathnames.h | 4 + ssh-pkcs11-client.c | 205 ++++++++ ssh-pkcs11.h | 10 +- 16 files changed, 1103 insertions(+), 34 deletions(-) create mode 100644 contrib/win32/openssh/ssh-pkcs11-helper.vcxproj 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,