Revision: 10695 https://osdn.net/projects/ttssh2/scm/svn/commits/10695 Author: zmatsuo Date: 2023-05-02 22:27:52 +0900 (Tue, 02 May 2023) Log Message: ----------- 新実装のsshagent通信追加 - 通信できる agent - pageant (共有メモリ) - pageant (NamedPipe) - Microsoft SSH agent (NamedPipe) - sha256 は opensslIF版とbcrypt版の2つを準備 ticket #45653 Ticket Links: ------------ https://osdn.net/projects/ttssh2/tracker/detail/45653 Modified Paths: -------------- trunk/ttssh2/libsshagentc/CMakeLists.txt trunk/ttssh2/libsshagentc/README.md Added Paths: ----------- trunk/ttssh2/libsshagentc/sshagentc/ trunk/ttssh2/libsshagentc/sshagentc/CMakeLists.txt trunk/ttssh2/libsshagentc/sshagentc/README.md trunk/ttssh2/libsshagentc/sshagentc/agentc.cpp trunk/ttssh2/libsshagentc/sshagentc/sha256.h trunk/ttssh2/libsshagentc/sshagentc/sha256_bcrypt.cpp trunk/ttssh2/libsshagentc/sshagentc/sha256_openssl.cpp -------------- next part -------------- Modified: trunk/ttssh2/libsshagentc/CMakeLists.txt =================================================================== --- trunk/ttssh2/libsshagentc/CMakeLists.txt 2023-05-02 13:27:42 UTC (rev 10694) +++ trunk/ttssh2/libsshagentc/CMakeLists.txt 2023-05-02 13:27:52 UTC (rev 10695) @@ -1,9 +1,16 @@ cmake_minimum_required(VERSION 3.11) project(libsshagentc) +# どの libsshagentc を使用するか +# すべて OFF の時、従来と同じ putty を使用する option(SSHAGENTC_SKELTON "use skelton" OFF) +option(SSHAGENTC_NEXT "use next sshagent" OFF) -if(SSHAGENTC_SKELTON) +option(SSHAGENTC_TOOL "sshagentc test/debug tool" OFF) + +if(SSHAGENTC_NEXT) + add_subdirectory(sshagentc) +elseif(SSHAGENTC_SKELTON) add_subdirectory(skelton) else() add_subdirectory(putty) @@ -10,4 +17,6 @@ endif() # for debug/test -#add_subdirectory(sshagentc_tool) +if(SSHAGENTC_TOOL) + add_subdirectory(sshagentc_tool) +endif() Modified: trunk/ttssh2/libsshagentc/README.md =================================================================== --- trunk/ttssh2/libsshagentc/README.md 2023-05-02 13:27:42 UTC (rev 10694) +++ trunk/ttssh2/libsshagentc/README.md 2023-05-02 13:27:52 UTC (rev 10695) @@ -1,6 +1,7 @@ # libsshagentc -- ssh-agent と通信するためのライブラリ +ssh-agent と通信するためのライブラリ。 +Tera Term 以外でも使えるよう考慮。 ファイル/フォルダ @@ -11,6 +12,8 @@ - skelton/ - クライアントスケルトン - 何も行わない +- sshagentc/ + - 作成中 # 資料 Added: trunk/ttssh2/libsshagentc/sshagentc/CMakeLists.txt =================================================================== --- trunk/ttssh2/libsshagentc/sshagentc/CMakeLists.txt (rev 0) +++ trunk/ttssh2/libsshagentc/sshagentc/CMakeLists.txt 2023-05-02 13:27:52 UTC (rev 10695) @@ -0,0 +1,54 @@ +set(PACKAGE_NAME "libsshagentc") + +project(${PACKAGE_NAME}) + +add_library( + ${PACKAGE_NAME} + ../libputty.h + agentc.cpp + sha256.h +) + +if(1) + target_sources( + ${PACKAGE_NAME} + PRIVATE + sha256.h + sha256_bcrypt.cpp + ) +endif() + +if(0) + target_sources( + ${PACKAGE_NAME} + PRIVATE + sha256.h + sha256_openssl.cpp + ) +endif() + +if(MSVC) + target_compile_options( + ${PACKAGE_NAME} + PRIVATE + -W4 + ) +else() + target_compile_options( + ${PACKAGE_NAME} + PRIVATE + -Wall -Wextra + ) +endif() + +target_include_directories( + ${PACKAGE_NAME} + PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/.. +) + +target_link_libraries( + ${PACKAGE_NAME} + PRIVATE + crypt32 +) Added: trunk/ttssh2/libsshagentc/sshagentc/README.md =================================================================== --- trunk/ttssh2/libsshagentc/sshagentc/README.md (rev 0) +++ trunk/ttssh2/libsshagentc/sshagentc/README.md 2023-05-02 13:27:52 UTC (rev 10695) @@ -0,0 +1,16 @@ +# ssh agent client + +ssh ageant と通信するライブラリ + +## 通信できる agent + +- pageant (共有メモリ) +- pageant (NamedPipe) +- Microsoft SSH ageant (NamedPipe) + +## 参考にしたプロジェクト + +- PuTTY + - https://www.chiark.greenend.org.uk/~sgtatham/putty/ +- RLogin + - http://nanno.bf1.jp/softlib/man/rlogin/ Added: trunk/ttssh2/libsshagentc/sshagentc/agentc.cpp =================================================================== --- trunk/ttssh2/libsshagentc/sshagentc/agentc.cpp (rev 0) +++ trunk/ttssh2/libsshagentc/sshagentc/agentc.cpp 2023-05-02 13:27:52 UTC (rev 10695) @@ -0,0 +1,680 @@ +/* + * Copyright (C) 2023- TeraTerm Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// PuTTY is copyright 1997-2023 Simon Tatham. +// RLogin is copyright 1998-2023 Culti + +#include <stdio.h> +#include <stdlib.h> +#define _CRTDBG_MAP_ALLOC +#include <crtdbg.h> +#include <stdint.h> +#include <assert.h> +#include <vector> +#include <windows.h> +#include <Lmcons.h> // for UNLEN +#include "libputty.h" +#include "sha256.h" + +#define PUTTY_SHM 1 // pageant shared memory +#define PUTTY_NAMEDPIPE 1 // pageant named pipe +#define MS_NAMEDPIPE 1 // Microsoft agent + +// SSH Agent +// Message numbers +// https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-04#section-5.1 + +// requests from the client to the agent +#define SSH_AGENTC_REQUEST_IDENTITIES 11 +#define SSH_AGENTC_SIGN_REQUEST 13 +#define SSH_AGENTC_ADD_IDENTITY 17 +#define SSH_AGENTC_REMOVE_IDENTITY 18 +#define SSH_AGENTC_REMOVE_ALL_IDENTITIES 19 +#define SSH_AGENTC_EXTENSION 27 + +// replies from the agent to the client +#define SSH_AGENT_FAILURE 5 +#define SSH_AGENT_SUCCESS 6 +#define SSH_AGENT_EXTENSION_FAILURE 28 +#define SSH_AGENT_IDENTITIES_ANSWER 12 +#define SSH_AGENT_SIGN_RESPONSE 14 + +#if PUTTY_SHM +static PSID usersid; +#endif + +static uint32_t get_uint32(const uint8_t *p) +{ + return (((uint32_t)p[3] ) | ((uint32_t)p[2] << 8) | + ((uint32_t)p[1] << 16) | ((uint32_t)p[0] << 24)); +} + +/** + * pageant \x82\xCC named pipe\x96\xBC\x82̈ꕔ + * from putty windows/utils/cryptapi.c + * + * @param realname \x8C\xB3\x82ɂȂ镶\x8E\x9A\x97\xF1 + * @return named pipe\x96\xBC\x82̈ꕔ + * \x83T\x83C\x83\x93\x83C\x83\x93\x92\x86\x82͓\xAF\x88ꕶ\x8E\x9A\x97Ԃ\xE9 + * \x95s\x97v\x82ɂȂ\xC1\x82\xBD\x82\xE7 free() + * + * TODO + * CryptProtectMemory() API \x82͔\xE4\x8Ar\x93I\x90V\x82\xB5\x82\xA2 Windows \x82݂̂Ǝv\x82\xED\x82\xEA\x82\xE9 + */ +static char *capi_obfuscate_string(const char *realname) +{ + char *cryptdata; + int cryptlen; + unsigned char digest[32]; + char retbuf[65]; + int i; + + cryptlen = (int)(strlen(realname) + 1); + cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1; + cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE; + cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE; + + cryptdata = (char *)malloc(cryptlen); + memset(cryptdata, 0, cryptlen); + memcpy(cryptdata, realname, strlen(realname)); + + /* + * CRYPTPROTECTMEMORY_CROSS_PROCESS causes CryptProtectMemory to + * use the same key in all processes with this user id, meaning + * that the next PuTTY process calling this function with the same + * input will get the same data. + * + * (Contrast with CryptProtectData, which invents a new session + * key every time since its API permits returning more data than + * was input, so calling _that_ and hashing the output would not + * be stable.) + * + * We don't worry too much if this doesn't work for some reason. + * Omitting this step still has _some_ privacy value (in that + * another user can test-hash things to confirm guesses as to + * where you might be connecting to, but cannot invert SHA-256 in + * the absence of any plausible guess). So we don't abort if we + * can't call CryptProtectMemory at all, or if it fails. + */ + CryptProtectMemory(cryptdata, cryptlen, + CRYPTPROTECTMEMORY_CROSS_PROCESS); + + /* + * We don't want to give away the length of the hostname either, + * so having got it back out of CryptProtectMemory we now hash it. + */ + assert(cryptlen == 16); + uint8_t buf[4+16] = {0}; + buf[3] = 0x10; // = 0x00000010 + memcpy(&buf[4], cryptdata, cryptlen); + sha256(&buf[0], sizeof(buf), digest); + free(cryptdata); + + /* + * Finally, make printable. + */ + retbuf[0] = 0; + for (i = 0; i < 32; i++) { + char s[4]; + sprintf_s(s, "%02x", digest[i]); + strcat_s(retbuf, s); + } + + return _strdup(retbuf); +} + +/** + * pagent named pipe\x96\xBC + * from putty windows/utils/agent_named_pipe_name.c + */ +static char *agent_named_pipe_name(void) +{ + char user_name[UNLEN+1]; + DWORD len = _countof(user_name); + BOOL r = GetUserNameA(user_name, &len); + if (r == 0) { + return NULL; + } + char *suffix = capi_obfuscate_string("Pageant"); + // asprintf(&pipename, "\\\\.\\pipe\\pageant.%s.%s", user_name, suffix); + const char *base = "\\\\.\\pipe\\pageant."; + size_t pipe_len = strlen(base) + 2 + strlen(user_name) + strlen(suffix); + char *pipename = (char *)malloc(pipe_len); + strcpy_s(pipename, pipe_len, base); + strcat_s(pipename, pipe_len, user_name); + strcat_s(pipename, pipe_len, "."); + strcat_s(pipename, pipe_len, suffix); + free(suffix); + return pipename; +} + +/** + * \x83o\x83b\x83t\x83@\x91\x80\x8D\xEC + */ +class Buffer { +public: + virtual ~Buffer() { + clear(); + } + size_t size() const + { + return buf_.size(); + } + void clear() + { + const size_t size = buf_.size(); + if (size > 0) { + SecureZeroMemory(&buf_[0], size); + buf_.clear(); + } + } + void *get_ptr() const + { + if (buf_.size() == 0) { + return NULL; + } + return (void *)&buf_[0]; + } + void append_array(const void *ptr, size_t len) { + const uint8_t *u8ptr = (uint8_t *)ptr; + buf_.insert(buf_.end(), &u8ptr[0], &u8ptr[len]); + } + void append_byte(uint8_t u8) + { + buf_.push_back(u8); + } + void append_uint32(uint32_t u32) + { + buf_.push_back((u32 >> (8*3)) & 0xff); + buf_.push_back((u32 >> (8*2)) & 0xff); + buf_.push_back((u32 >> (8*1)) & 0xff); + buf_.push_back((u32 >> (8*0)) & 0xff); + } + /** + * malloc\x82\xB5\x82\xBD\x97̈\xE6\x82ɓ\xE0\x97e\x82\xF0\x83R\x83s\x81[\x82\xB5\x82ĕԂ\xB7 + */ + void *get_mallocdbuf(size_t *size) + { + size_t len = buf_.size(); + *size = len; + void *p = NULL; + if (len > 0) { + p = malloc(len); + memcpy(p, &buf_[0], len); + } + return p; + } + /** + * \x83o\x83b\x83t\x83@\x82̐擪\x82ɒlj\xC1\x82\xB7\x82\xE9 + */ + void prepend_uint32(uint32_t u32) + { + Buffer new_buf; + new_buf.append_uint32(u32); + new_buf.buf_.insert(new_buf.buf_.end(), buf_.begin(), buf_.end()); + buf_.swap(new_buf.buf_); + } +private: + std::vector<uint8_t> buf_; +}; + +/** + * Microsoft named pipe \x96\xBC\x82\xF0\x8E擾 + * + * \x8A\xAB\x95ϐ\x94 SSH_AUTH_SOCK \x82\xAA\x90ݒ肳\x82\xEA\x82Ă\xA2\x82\xEA\x82A\x82\xBB\x82̒l\x82\xF0\x95Ԃ\xB7 + * \x90ݒ肳\x82\xEA\x82Ă\xA2\x82Ȃ\xAF\x82\xEA\x82f\x83t\x83H\x83\x8B\x83g\x82\xF0\x95Ԃ\xB7 + * + * @return pipe name (\x95s\x97v\x82ɂȂ\xC1\x82\xBD\x82\xE7 free() \x82\xB7\x82邱\x82\xC6) + */ +#if MS_NAMEDPIPE +static char *get_ms_namedpipe(void) +{ + static const char *var_name = "SSH_AUTH_SOCK"; + static const char *pipename_default = "\\\\.\\pipe\\openssh-ssh-agent"; + char *var_ptr = NULL; + size_t var_size = 0; + getenv_s(&var_size, NULL, 0, var_name); + if (var_size != 0) { + var_ptr = (char*)malloc(var_size); + getenv_s(&var_size, var_ptr, var_size, var_name); + } + if (var_ptr == NULL) { + var_ptr = strdup(pipename_default); + } + return var_ptr; +} +#endif + +/** + * agent\x82ƒʐM,named pipe\x8Co\x97R + * RLogin CMainFrame::WageantQuery() MainFrm.cpp \x82\xF0\x8EQ\x8Dl\x82ɂ\xB5\x82\xBD + */ +#if PUTTY_NAMEDPIPE || MS_NAMEDPIPE +static BOOL query_namedpipe(const char *pipename, const Buffer &request, Buffer &reply) +{ + BOOL r = FALSE; + std::vector<BYTE> read_buffer(4096); + DWORD read_len = 0; + + HANDLE hPipe = CreateFileA(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hPipe == INVALID_HANDLE_VALUE) { + return FALSE; + } + + // \x83\x8A\x83N\x83G\x83X\x83g\x91\x97\x90M + const BYTE *req_ptr = (BYTE *)request.get_ptr(); + DWORD req_len = (DWORD)request.size(); + while (req_len > 0) { + DWORD written; + if (!WriteFile(hPipe, req_ptr, req_len, &written, NULL)) { + // \x8F\x91\x82\xAB\x8D\x9E\x82݃G\x83\x89\x81[\x82\xAA\x8BN\x82\xAB\x82\xBD\x81A\x92ʐM\x92\x86\x82\xC9 agent \x82\xAA\x97\x8E\x82\xBF\x82\xBD? + goto finish; + } + req_ptr += written; + req_len -= written; + } + + // \x83\x8A\x83v\x83\x89\x83C\x8E\xF3\x90M + reply.clear(); + if (ReadFile(hPipe, &read_buffer[0], 4, &read_len, NULL) && read_len == 4) { + const uint32_t len = get_uint32(&read_buffer[0]); + if (len < AGENT_MAX_MSGLEN) { + uint32_t total_len; + reply.append_array(&read_buffer[0], 4); + for (total_len = 0; total_len < len;) { + if (!ReadFile(hPipe, &read_buffer[0], (DWORD)read_buffer.size(), &read_len, NULL)) { + reply.clear(); + break; + } + reply.append_array(&read_buffer[0], read_len); + total_len += read_len; + } + } + } + + if (reply.size() > 0) + r = TRUE; + +finish: + CloseHandle(hPipe); + SecureZeroMemory(&read_buffer[0], read_buffer.size()); + return r; +} +#endif + +/** + * sid\x82̎擾 + * PuTTY security.c get_user_sid() + */ +#if PUTTY_SHM +static PSID get_user_sid(void) +{ + HANDLE proc = NULL, tok = NULL; + TOKEN_USER *user = NULL; + DWORD toklen, sidlen; + PSID sid = NULL, ret = NULL; + + if (usersid) + return usersid; + + if ((proc = OpenProcess(MAXIMUM_ALLOWED, false, + GetCurrentProcessId())) == NULL) + goto cleanup; + + if (!OpenProcessToken(proc, TOKEN_QUERY, &tok)) + goto cleanup; + + if (!GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) && + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + goto cleanup; + + if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL) + goto cleanup; + + if (!GetTokenInformation(tok, TokenUser, user, toklen, &toklen)) + goto cleanup; + + sidlen = GetLengthSid(user->User.Sid); + + sid = (PSID)malloc(sidlen); + + if (!CopySid(sidlen, sid, user->User.Sid)) + goto cleanup; + + /* Success. Move sid into the return value slot, and null it out + * to stop the cleanup code freeing it. */ + ret = usersid = sid; + sid = NULL; + + cleanup: + if (proc != NULL) + CloseHandle(proc); + if (tok != NULL) + CloseHandle(tok); + if (user != NULL) + LocalFree(user); + if (sid != NULL) + free(sid); + + return ret; +} +#endif + +/** + * SECURITY_ATTRIBUTES \x82̎擾 + * PuTTY windows/agent-client.c wm_copydata_agent_query() \x82\xF0\x8EQ\x8Dl\x82ɂ\xB5\x82\xBD + * + * @param psa SECURITY_ATTRIBUTES \x82ւ̃|\x83C\x83\x93\x83^ + * \x82\xB1\x82\xB1\x82Ɏ擾\x82\xB7\x82\xE9 + * @return SECURITY_ATTRIBUTES\x82ւ̃|\x83C\x83\x93\x83^ + * \x95s\x97v\x82ɂȂ\xC1\x82\xBD\x82\xE7 psa->lpSecurityDescriptor \x82\xF0 LocalFree() \x82\xB7\x82邱\x82\xC6 + * \x8E擾\x82ł\xAB\x82Ȃ\xA9\x82\xC1\x82\xBD\x82Ƃ\xAB\x82\xCD NULL + */ +#if PUTTY_SHM +static SECURITY_ATTRIBUTES *get_sa(SECURITY_ATTRIBUTES *psa) +{ + memset(psa, 0, sizeof(*psa)); + usersid = get_user_sid(); + if (!usersid) { + return NULL; + } + PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) + LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if (!psd) { + return NULL; + } + + if (InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION) && + SetSecurityDescriptorOwner(psd, usersid, false)) { + psa->nLength = sizeof(*psa); + psa->bInheritHandle = true; + psa->lpSecurityDescriptor = psd; // LocalFree() \x82\xB7\x82邱\x82\xC6 + } else { + LocalFree(psd); + psa = NULL; + } + return psa; +} +#endif + +/** + * agent(pageant)\x82ƒʐM,\x8B\xA4\x97L\x83\x81\x83\x82\x83\x8A\x8Co\x97R + * + * @retval FALSE \x83G\x83\x89\x81[ + */ +#if PUTTY_SHM +static BOOL query_SHM(const Buffer &request, Buffer &reply) +{ + HWND hwnd; + char mapname[25]; + HANDLE fmap = NULL; + unsigned char *p = NULL; + unsigned long len; + BOOL ret = FALSE; + const uint8_t *in = (uint8_t *)request.get_ptr(); + SECURITY_ATTRIBUTES *psa = NULL; + + reply.clear(); + + if ((len = get_uint32(in)) > AGENT_MAX_MSGLEN) { + goto agent_error; + } + + hwnd = FindWindowA("Pageant", "Pageant"); + if (!hwnd) { + goto agent_error; + } + + SECURITY_ATTRIBUTES sa; + psa = get_sa(&sa); + sprintf_s(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId()); + fmap = CreateFileMappingA(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE, + 0, AGENT_MAX_MSGLEN, mapname); + if (!fmap) { + goto agent_error; + } + + if ((p = (unsigned char *)MapViewOfFile(fmap, FILE_MAP_WRITE, 0, 0, 0)) == NULL) { + goto agent_error; + } + + COPYDATASTRUCT cds; +#define AGENT_COPYDATA_ID 0x804e50ba // ? + cds.dwData = AGENT_COPYDATA_ID; + cds.cbData = (DWORD)(strlen(mapname) + 1); + cds.lpData = mapname; + + memcpy(p, in, len + 4); + if (SendMessageA(hwnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds) > 0) { + // \x89\x9E\x93\x9A\x82\xAA\x82\xA0\x82\xC1\x82\xBD + len = get_uint32(p); + reply.append_array(p, len+4); + ret = TRUE; + } + +agent_error: + if (p) { + UnmapViewOfFile(p); + } + if (fmap) { + CloseHandle(fmap); + } + if (ret == 0) { + reply.append_uint32(5); + reply.append_byte(SSH_AGENT_FAILURE); + ret = FALSE; + } + if (psa != NULL) { + LocalFree(psa->lpSecurityDescriptor); + } + return ret; +} +#endif + +static BOOL query(const Buffer &request, Buffer &reply) +{ + BOOL r; + + reply.clear(); +#if PUTTY_NAMEDPIPE + char *pname = agent_named_pipe_name(); + if (pname != NULL) { + r = query_namedpipe(pname, request, reply); + free(pname); + if (r) { + goto finish; + } + } +#endif +#if PUTTY_SHM + r = query_SHM(request, reply); + if (r) { + goto finish; + } +#endif +#if MS_NAMEDPIPE + { + char *ms_namedpipe = get_ms_namedpipe(); + r = query_namedpipe(ms_namedpipe, request, reply); + free(ms_namedpipe); + if (r) { + goto finish; + } + } +#endif +finish: + return r; +} + +// https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-04#section-4.4 +int putty_get_ssh2_keylist(unsigned char **keylist) +{ + Buffer req; + req.append_uint32(1); + req.append_byte(SSH_AGENTC_REQUEST_IDENTITIES); + + Buffer rep; + query(req, rep); + + // check + const uint8_t *reply_ptr = (uint8_t *)rep.get_ptr(); + uint32_t reply_len = get_uint32(reply_ptr); + if (rep.size() != reply_len + 4 || reply_ptr[4] != SSH_AGENT_IDENTITIES_ANSWER) { + *keylist = NULL; + return 0; + } + + uint32_t key_blob_len = reply_len - (4+1); + uint8_t *key_blob_ptr = (uint8_t *)malloc(key_blob_len); + memcpy(key_blob_ptr, reply_ptr + (4+1), key_blob_len); + + *keylist = key_blob_ptr; + return key_blob_len; +} + +void *putty_sign_ssh2_key(unsigned char *pubkey, + unsigned char *data, + int datalen, + int *outlen, + int signflags) +{ + int pubkeylen = get_uint32(pubkey); + + Buffer req; + req.append_byte(SSH_AGENTC_SIGN_REQUEST); + req.append_array(pubkey, 4 + pubkeylen); + req.append_uint32(datalen); + req.append_array(data, datalen); + req.append_uint32(signflags); + req.prepend_uint32((uint32_t)req.size()); + + Buffer rep; + query(req, rep); + const uint8_t *reply_ptr = (uint8_t *)rep.get_ptr(); + + if (rep.size() < 5 || reply_ptr[4] != SSH_AGENT_SIGN_RESPONSE) { + return NULL; + } + + size_t signed_blob_len = rep.size() - (4+1); + void *signed_blob_ptr = malloc(signed_blob_len); + memcpy(signed_blob_ptr, reply_ptr + (4+1), signed_blob_len); + if (outlen) + *outlen = (int)signed_blob_len; + return signed_blob_ptr; +} + +int putty_get_ssh1_keylist(unsigned char **keylist) +{ + (void)keylist; + return 0; +} + +void *putty_hash_ssh1_challenge(unsigned char *pubkey, + int pubkeylen, + unsigned char *data, + int datalen, + unsigned char *session_id, + int *outlen) +{ + (void)pubkey; + (void)pubkeylen; + (void)data; + (void)datalen; + (void)session_id; + (void)outlen; + return NULL; +} + +int putty_get_ssh1_keylen(unsigned char *key, int maxlen) +{ + (void)key; + (void)maxlen; + return 0; +} + +const char *putty_get_version() +{ + return "libsshagent 0.1"; +} + +void putty_agent_query_synchronous(const void *req_ptr, int req_len, void **rep_ptr, int *rep_len) +{ + Buffer request; + request.append_array(req_ptr, req_len); + Buffer reply; + query(request, reply); + size_t len; + *rep_ptr = reply.get_mallocdbuf(&len); + *rep_len = (int)len; +} + +#if PUTTY_NAMEDPIPE +static BOOL check_puttyagent_namedpipe() +{ + char *pname = agent_named_pipe_name(); + DWORD r = GetFileAttributesA(pname); + free(pname); + return r != INVALID_FILE_ATTRIBUTES ? TRUE : FALSE; +} +#endif + +#if MS_NAMEDPIPE +static BOOL check_MSagent_namedpipe() +{ + char *ms_namedpipe = get_ms_namedpipe(); + DWORD r = GetFileAttributesA(ms_namedpipe); + free(ms_namedpipe); + return r != INVALID_FILE_ATTRIBUTES ? TRUE : FALSE; +} +#endif + +BOOL putty_agent_exists() +{ +#if PUTTY_NAMEDPIPE + if (check_puttyagent_namedpipe()) { + return TRUE; + } +#endif +#if PUTTY_SHM + HWND hwnd = FindWindowA("Pageant", "Pageant"); + if (hwnd) { + return TRUE; + } +#endif +#if MS_NAMEDPIPE + if (check_MSagent_namedpipe()) { + return TRUE; + } +#endif + return FALSE; +} + +void safefree(void *p) +{ + free(p); +} Added: trunk/ttssh2/libsshagentc/sshagentc/sha256.h =================================================================== --- trunk/ttssh2/libsshagentc/sshagentc/sha256.h (rev 0) +++ trunk/ttssh2/libsshagentc/sshagentc/sha256.h 2023-05-02 13:27:52 UTC (rev 10695) @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023- TeraTerm Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <windows.h> // for BOOL +#include <stdlib.h> // for size_t +#include <stdint.h> // for uint8_t + +BOOL sha256(const void *data_ptr, size_t data_size, uint8_t buf[32]); Added: trunk/ttssh2/libsshagentc/sshagentc/sha256_bcrypt.cpp =================================================================== --- trunk/ttssh2/libsshagentc/sshagentc/sha256_bcrypt.cpp (rev 0) +++ trunk/ttssh2/libsshagentc/sshagentc/sha256_bcrypt.cpp 2023-05-02 13:27:52 UTC (rev 10695) @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023- TeraTerm Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> +#include <windows.h> + +#include "sha256.h" + +static BOOL CalcHash(const void *data_ptr, size_t data_size, ALG_ID alg, void **hash_value, size_t *hash_len) +{ + HCRYPTPROV hProv; + HCRYPTHASH hHash; + BOOL r; + + *hash_value = 0; + *hash_len = 0; + + //DWORD dwProvType = PROV_RSA_FULL; + DWORD dwProvType = PROV_RSA_AES; + r = CryptAcquireContextA(&hProv, NULL, NULL, dwProvType, CRYPT_VERIFYCONTEXT); + if (r == FALSE) { + return FALSE; + } + + r = CryptCreateHash(hProv, alg, 0, 0, &hHash); + if (r == FALSE) { + error_release: + CryptReleaseContext(hProv, 0); + return FALSE; + } + + r = CryptHashData(hHash, (BYTE *)data_ptr, (DWORD)data_size, 0); + if (r == FALSE) { + goto error_release; + } + + DWORD len; + CryptGetHashParam(hHash, HP_HASHVAL, NULL, &len, 0); + BYTE *ptr = (BYTE *)malloc(len); + CryptGetHashParam(hHash, HP_HASHVAL, ptr, &len, 0); + + *hash_value = ptr; + *hash_len = len; + + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + + return TRUE; +} + +BOOL sha256(const void *data_ptr, size_t data_size, uint8_t buf[32]) +{ + const size_t sha256_len = 32; + void *hash_value; + size_t hash_len; + ALG_ID alg = CALG_SHA_256; + BOOL r = CalcHash(data_ptr, data_size, alg, &hash_value, &hash_len); + if (r == FALSE || hash_len != sha256_len) { + memset(buf, 0, sha256_len); + return FALSE; + } + memcpy(buf, hash_value, sha256_len); + free(hash_value); + return TRUE; +} Added: trunk/ttssh2/libsshagentc/sshagentc/sha256_openssl.cpp =================================================================== --- trunk/ttssh2/libsshagentc/sshagentc/sha256_openssl.cpp (rev 0) +++ trunk/ttssh2/libsshagentc/sshagentc/sha256_openssl.cpp 2023-05-02 13:27:52 UTC (rev 10695) @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023- TeraTerm Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +//#include <openssl/sha.h> +extern "C" unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md); + +#include "sha256.h" + +BOOL sha256(const void *data_ptr, size_t data_size, uint8_t buf[32]) +{ + SHA256((unsigned char *)data_ptr, data_size, buf); + return TRUE; +}