diff options
author | George Hazan <george.hazan@gmail.com> | 2023-06-09 21:40:16 +0300 |
---|---|---|
committer | George Hazan <george.hazan@gmail.com> | 2023-06-09 21:40:16 +0300 |
commit | 927f00cc19b7239a1fe12abe30b472d61b753d8d (patch) | |
tree | 68a190dd83dc2dcceb82464a1953f2701af2a109 /libs/libssh2/src/agent.c | |
parent | 1b241cad53b8c3c5300409fe681de18e636dcf3d (diff) |
fixes #3551 (Update libssh2 to 1.11.0)
Diffstat (limited to 'libs/libssh2/src/agent.c')
-rw-r--r-- | libs/libssh2/src/agent.c | 266 |
1 files changed, 217 insertions, 49 deletions
diff --git a/libs/libssh2/src/agent.c b/libs/libssh2/src/agent.c index 03e7415a84..d99a319550 100644 --- a/libs/libssh2/src/agent.c +++ b/libs/libssh2/src/agent.c @@ -1,6 +1,6 @@ /* - * Copyright (c) 2009 by Daiki Ueno - * Copyright (C) 2010-2014 by Daniel Stenberg + * Copyright (C) Daiki Ueno + * Copyright (C) Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, @@ -35,11 +35,15 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-3-Clause */ #include "libssh2_priv.h" -#include "misc.h" + #include <errno.h> +#include <stdlib.h> /* for getenv() */ + #ifdef HAVE_SYS_UN_H #include <sys/un.h> #else @@ -48,6 +52,11 @@ support them. */ #undef PF_UNIX #endif + +#if defined(WIN32) && !defined(LIBSSH2_WINDOWS_UWP) +#define HAVE_WIN32_AGENTS +#endif + #include "userauth.h" #include "session.h" @@ -90,6 +99,10 @@ #define SSH_AGENT_CONSTRAIN_LIFETIME 1 #define SSH_AGENT_CONSTRAIN_CONFIRM 2 +/* Signature request methods */ +#define SSH_AGENT_RSA_SHA2_256 2 +#define SSH_AGENT_RSA_SHA2_512 4 + /* non-blocking mode on agent connection is not yet implemented, but for future use. */ typedef enum { @@ -107,6 +120,7 @@ typedef struct agent_transaction_ctx { unsigned char *response; size_t response_len; agent_nonblocking_states state; + size_t send_recv_total; } *agent_transaction_ctx_t; typedef int (*agent_connect_func)(LIBSSH2_AGENT *agent); @@ -122,9 +136,9 @@ struct agent_publickey { }; struct agent_ops { - agent_connect_func connect; - agent_transact_func transact; - agent_disconnect_func disconnect; + const agent_connect_func connect; + const agent_transact_func transact; + const agent_disconnect_func disconnect; }; struct _LIBSSH2_AGENT @@ -140,8 +154,16 @@ struct _LIBSSH2_AGENT struct list_head head; /* list of public keys */ char *identity_agent_path; /* Path to a custom identity agent socket */ + +#ifdef HAVE_WIN32_AGENTS + OVERLAPPED overlapped; + HANDLE pipe; + BOOL pending_io; +#endif }; +#include "agent_win.c" + #ifdef PF_UNIX static int agent_connect_unix(LIBSSH2_AGENT *agent) @@ -163,10 +185,10 @@ agent_connect_unix(LIBSSH2_AGENT *agent) "failed creating socket"); s_un.sun_family = AF_UNIX; - strncpy(s_un.sun_path, path, sizeof s_un.sun_path); + strncpy(s_un.sun_path, path, sizeof(s_un.sun_path)); s_un.sun_path[sizeof(s_un.sun_path)-1] = 0; /* make sure there's a trailing zero */ - if(connect(agent->fd, (struct sockaddr*)(&s_un), sizeof s_un) != 0) { + if(connect(agent->fd, (struct sockaddr*)(&s_un), sizeof(s_un)) != 0) { close(agent->fd); return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, "failed connecting with agent"); @@ -175,6 +197,40 @@ agent_connect_unix(LIBSSH2_AGENT *agent) return LIBSSH2_ERROR_NONE; } +#define RECV_SEND_ALL(func, socket, buffer, length, flags, abstract) \ + do { \ + size_t finished = 0; \ + \ + while(finished < length) { \ + ssize_t rc; \ + rc = func(socket, \ + (char *)buffer + finished, length - finished, \ + flags, abstract); \ + if(rc < 0) \ + return rc; \ + \ + finished += rc; \ + } \ + \ + return finished; \ + } while(0) + +static ssize_t _send_all(LIBSSH2_SEND_FUNC(func), libssh2_socket_t socket, + const void *buffer, size_t length, + int flags, void **abstract) +{ + RECV_SEND_ALL(func, socket, buffer, length, flags, abstract); +} + +static ssize_t _recv_all(LIBSSH2_RECV_FUNC(func), libssh2_socket_t socket, + void *buffer, size_t length, + int flags, void **abstract) +{ + RECV_SEND_ALL(func, socket, buffer, length, flags, abstract); +} + +#undef RECV_SEND_ALL + static int agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) { @@ -183,8 +239,10 @@ agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) /* Send the length of the request */ if(transctx->state == agent_NB_state_request_created) { - _libssh2_htonu32(buf, transctx->request_len); - rc = LIBSSH2_SEND_FD(agent->session, agent->fd, buf, sizeof buf, 0); + _libssh2_htonu32(buf, (uint32_t)transctx->request_len); + rc = (int)_send_all(agent->session->send, agent->fd, + buf, sizeof(buf), 0, + &agent->session->abstract); if(rc == -EAGAIN) return LIBSSH2_ERROR_EAGAIN; else if(rc < 0) @@ -195,8 +253,9 @@ agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) /* Send the request body */ if(transctx->state == agent_NB_state_request_length_sent) { - rc = LIBSSH2_SEND_FD(agent->session, agent->fd, transctx->request, - transctx->request_len, 0); + rc = (int)_send_all(agent->session->send, agent->fd, + transctx->request, transctx->request_len, 0, + &agent->session->abstract); if(rc == -EAGAIN) return LIBSSH2_ERROR_EAGAIN; else if(rc < 0) @@ -207,7 +266,9 @@ agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) /* Receive the length of a response */ if(transctx->state == agent_NB_state_request_sent) { - rc = LIBSSH2_RECV_FD(agent->session, agent->fd, buf, sizeof buf, 0); + rc = (int)_recv_all(agent->session->recv, agent->fd, + buf, sizeof(buf), 0, + &agent->session->abstract); if(rc < 0) { if(rc == -EAGAIN) return LIBSSH2_ERROR_EAGAIN; @@ -225,8 +286,9 @@ agent_transact_unix(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) /* Receive the response body */ if(transctx->state == agent_NB_state_response_length_received) { - rc = LIBSSH2_RECV_FD(agent->session, agent->fd, transctx->response, - transctx->response_len, 0); + rc = (int)_recv_all(agent->session->recv, agent->fd, + transctx->response, transctx->response_len, 0, + &agent->session->abstract); if(rc < 0) { if(rc == -EAGAIN) return LIBSSH2_ERROR_EAGAIN; @@ -252,14 +314,14 @@ agent_disconnect_unix(LIBSSH2_AGENT *agent) return LIBSSH2_ERROR_NONE; } -struct agent_ops agent_ops_unix = { +static struct agent_ops agent_ops_unix = { agent_connect_unix, agent_transact_unix, agent_disconnect_unix }; #endif /* PF_UNIX */ -#ifdef WIN32 +#ifdef HAVE_WIN32_AGENTS /* Code to talk to Pageant was taken from PuTTY. * * Portions copyright Robert de Bath, Joris van Rantwijk, Delian @@ -290,7 +352,7 @@ agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) HANDLE filemap; unsigned char *p; unsigned char *p2; - int id; + LRESULT id; COPYDATASTRUCT cds; if(!transctx || 4 + transctx->request_len > PAGEANT_MAX_MSGLEN) @@ -303,16 +365,16 @@ agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) "found no pageant"); snprintf(mapname, sizeof(mapname), - "PageantRequest%08x%c", (unsigned)GetCurrentThreadId(), '\0'); + "PageantRequest%08x", (unsigned)GetCurrentThreadId()); filemap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, - 0, PAGEANT_MAX_MSGLEN, mapname); + 0, PAGEANT_MAX_MSGLEN, mapname); - if(filemap == NULL || filemap == INVALID_HANDLE_VALUE) + if(!filemap || filemap == INVALID_HANDLE_VALUE) return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, "failed setting up pageant filemap"); p2 = p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); - if(p == NULL || p2 == NULL) { + if(!p || !p2) { CloseHandle(filemap); return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL, "failed to open pageant filemap for writing"); @@ -322,7 +384,7 @@ agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx) transctx->request_len); cds.dwData = PAGEANT_COPYDATA_ID; - cds.cbData = 1 + strlen(mapname); + cds.cbData = (DWORD)(1 + strlen(mapname)); cds.lpData = mapname; id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds); @@ -357,20 +419,21 @@ agent_disconnect_pageant(LIBSSH2_AGENT *agent) return 0; } -struct agent_ops agent_ops_pageant = { +static struct agent_ops agent_ops_pageant = { agent_connect_pageant, agent_transact_pageant, agent_disconnect_pageant }; -#endif /* WIN32 */ +#endif /* HAVE_WIN32_AGENTS */ static struct { const char *name; struct agent_ops *ops; } supported_backends[] = { -#ifdef WIN32 +#ifdef HAVE_WIN32_AGENTS {"Pageant", &agent_ops_pageant}, -#endif /* WIN32 */ + {"OpenSSH", &agent_ops_openssh}, +#endif /* HAVE_WIN32_AGENTS */ #ifdef PF_UNIX {"Unix", &agent_ops_unix}, #endif /* PF_UNIX */ @@ -388,6 +451,9 @@ agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, ssize_t method_len; unsigned char *s; int rc; + unsigned char *method_name = NULL; + uint32_t sign_flags = 0; + ssize_t plain_len; /* Create a request to sign the data */ if(transctx->state == agent_NB_state_init) { @@ -404,9 +470,21 @@ agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, _libssh2_store_str(&s, (const char *)data, data_len); /* flags */ - _libssh2_store_u32(&s, 0); + if(session->userauth_pblc_method_len > 0 && + session->userauth_pblc_method) { + if(session->userauth_pblc_method_len == 12 && + !memcmp(session->userauth_pblc_method, "rsa-sha2-512", 12)) { + sign_flags = SSH_AGENT_RSA_SHA2_512; + } + else if(session->userauth_pblc_method_len == 12 && + !memcmp(session->userauth_pblc_method, "rsa-sha2-256", 12)) { + sign_flags = SSH_AGENT_RSA_SHA2_256; + } + } + _libssh2_store_u32(&s, sign_flags); transctx->request_len = s - transctx->request; + transctx->send_recv_total = 0; transctx->state = agent_NB_state_request_created; } @@ -461,8 +539,32 @@ agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, rc = LIBSSH2_ERROR_AGENT_PROTOCOL; goto error; } + + /* method name */ + method_name = LIBSSH2_ALLOC(session, method_len); + if(!method_name) { + rc = LIBSSH2_ERROR_ALLOC; + goto error; + } + memcpy(method_name, s, method_len); s += method_len; + plain_len = plain_method((char *)session->userauth_pblc_method, + session->userauth_pblc_method_len); + + /* check to see if we match requested */ + if(((size_t)method_len != session->userauth_pblc_method_len && + method_len != plain_len) || + memcmp(method_name, session->userauth_pblc_method, method_len)) { + _libssh2_debug((session, + LIBSSH2_TRACE_KEX, + "Agent sign method %.*s", + method_len, method_name)); + + rc = LIBSSH2_ERROR_ALGO_UNSUPPORTED; + goto error; + } + /* Read the signature */ len -= 4; if(len < 0) { @@ -484,13 +586,19 @@ agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, } memcpy(*sig, s, *sig_len); - error: +error: + + if(method_name) + LIBSSH2_FREE(session, method_name); + LIBSSH2_FREE(session, transctx->request); transctx->request = NULL; LIBSSH2_FREE(session, transctx->response); transctx->response = NULL; + transctx->state = agent_NB_state_init; + return _libssh2_error(session, rc, "agent sign failure"); } @@ -507,6 +615,7 @@ agent_list_identities(LIBSSH2_AGENT *agent) if(transctx->state == agent_NB_state_init) { transctx->request = &c; transctx->request_len = 1; + transctx->send_recv_total = 0; transctx->state = agent_NB_state_request_created; } @@ -522,7 +631,9 @@ agent_list_identities(LIBSSH2_AGENT *agent) rc = agent->ops->transact(agent, transctx); if(rc) { - goto error; + LIBSSH2_FREE(agent->session, transctx->response); + transctx->response = NULL; + return rc; } transctx->request = NULL; @@ -550,7 +661,7 @@ agent_list_identities(LIBSSH2_AGENT *agent) while(num_identities--) { struct agent_publickey *identity; - ssize_t comment_len; + size_t comment_len; /* Read the length of the blob */ len -= 4; @@ -558,7 +669,7 @@ agent_list_identities(LIBSSH2_AGENT *agent) rc = LIBSSH2_ERROR_AGENT_PROTOCOL; goto error; } - identity = LIBSSH2_ALLOC(agent->session, sizeof *identity); + identity = LIBSSH2_ALLOC(agent->session, sizeof(*identity)); if(!identity) { rc = LIBSSH2_ERROR_ALLOC; goto error; @@ -595,14 +706,14 @@ agent_list_identities(LIBSSH2_AGENT *agent) comment_len = _libssh2_ntohu32(s); s += 4; - /* Read the comment */ - len -= comment_len; - if(len < 0) { + if(comment_len > (size_t)len) { rc = LIBSSH2_ERROR_AGENT_PROTOCOL; LIBSSH2_FREE(agent->session, identity->external.blob); LIBSSH2_FREE(agent->session, identity); goto error; } + /* Read the comment */ + len -= comment_len; identity->external.comment = LIBSSH2_ALLOC(agent->session, comment_len + 1); @@ -618,7 +729,7 @@ agent_list_identities(LIBSSH2_AGENT *agent) _libssh2_list_add(&agent->head, &identity->node); } - error: +error: LIBSSH2_FREE(agent->session, transctx->response); transctx->response = NULL; @@ -643,7 +754,7 @@ agent_free_identities(LIBSSH2_AGENT *agent) #define AGENT_PUBLICKEY_MAGIC 0x3bdefed2 /* - * agent_publickey_to_external() + * agent_publickey_to_external * * Copies data from the internal to the external representation struct. * @@ -670,7 +781,7 @@ libssh2_agent_init(LIBSSH2_SESSION *session) { LIBSSH2_AGENT *agent; - agent = LIBSSH2_CALLOC(session, sizeof *agent); + agent = LIBSSH2_CALLOC(session, sizeof(*agent)); if(!agent) { _libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for agent connection"); @@ -681,11 +792,17 @@ libssh2_agent_init(LIBSSH2_SESSION *session) agent->identity_agent_path = NULL; _libssh2_list_init(&agent->head); +#ifdef HAVE_WIN32_AGENTS + agent->pipe = INVALID_HANDLE_VALUE; + memset(&agent->overlapped, 0, sizeof(OVERLAPPED)); + agent->pending_io = FALSE; +#endif + return agent; } /* - * libssh2_agent_connect() + * libssh2_agent_connect * * Connect to an ssh-agent. * @@ -705,7 +822,7 @@ libssh2_agent_connect(LIBSSH2_AGENT *agent) } /* - * libssh2_agent_list_identities() + * libssh2_agent_list_identities * * Request ssh-agent to list identities. * @@ -714,14 +831,14 @@ libssh2_agent_connect(LIBSSH2_AGENT *agent) LIBSSH2_API int libssh2_agent_list_identities(LIBSSH2_AGENT *agent) { - memset(&agent->transctx, 0, sizeof agent->transctx); + memset(&agent->transctx, 0, sizeof(agent->transctx)); /* Abandon the last fetched identities */ agent_free_identities(agent); return agent_list_identities(agent); } /* - * libssh2_agent_get_identity() + * libssh2_agent_get_identity * * Traverse the internal list of public keys. Pass NULL to 'prev' to get * the first one. Or pass a pointer to the previously returned one to get the @@ -758,7 +875,7 @@ libssh2_agent_get_identity(LIBSSH2_AGENT *agent, } /* - * libssh2_agent_userauth() + * libssh2_agent_userauth * * Do publickey user authentication with the help of ssh-agent. * @@ -773,7 +890,7 @@ libssh2_agent_userauth(LIBSSH2_AGENT *agent, int rc; if(agent->session->userauth_pblc_state == libssh2_NB_state_idle) { - memset(&agent->transctx, 0, sizeof agent->transctx); + memset(&agent->transctx, 0, sizeof(agent->transctx)); agent->identity = identity->node; } @@ -788,7 +905,58 @@ libssh2_agent_userauth(LIBSSH2_AGENT *agent, } /* - * libssh2_agent_disconnect() + * libssh2_agent_sign + * + * Sign a payload using a system-installed ssh-agent. + * + * Returns 0 if succeeded, or a negative value for error. + */ +LIBSSH2_API int +libssh2_agent_sign(LIBSSH2_AGENT *agent, + struct libssh2_agent_publickey *identity, + unsigned char **sig, + size_t *s_len, + const unsigned char *data, + size_t d_len, + const char *method, + unsigned int method_len) +{ + void *abstract = agent; + int rc; + uint32_t methodLen; + + if(agent->session->userauth_pblc_state == libssh2_NB_state_idle) { + memset(&agent->transctx, 0, sizeof(agent->transctx)); + agent->identity = identity->node; + } + + if(identity->blob_len < sizeof(uint32_t)) { + return LIBSSH2_ERROR_BUFFER_TOO_SMALL; + } + + methodLen = _libssh2_ntohu32(identity->blob); + + if(identity->blob_len < sizeof(uint32_t) + methodLen) { + return LIBSSH2_ERROR_BUFFER_TOO_SMALL; + } + + agent->session->userauth_pblc_method_len = method_len; + agent->session->userauth_pblc_method = LIBSSH2_ALLOC(agent->session, + method_len); + + memcpy(agent->session->userauth_pblc_method, method, methodLen); + + rc = agent_sign(agent->session, sig, s_len, data, d_len, &abstract); + + LIBSSH2_FREE(agent->session, agent->session->userauth_pblc_method); + agent->session->userauth_pblc_method = NULL; + agent->session->userauth_pblc_method_len = 0; + + return rc; +} + +/* + * libssh2_agent_disconnect * * Close a connection to an ssh-agent. * @@ -803,7 +971,7 @@ libssh2_agent_disconnect(LIBSSH2_AGENT *agent) } /* - * libssh2_agent_free() + * libssh2_agent_free * * Free an ssh-agent handle. This function also frees the internal * collection of public keys. @@ -816,7 +984,7 @@ libssh2_agent_free(LIBSSH2_AGENT *agent) libssh2_agent_disconnect(agent); } - if(agent->identity_agent_path != NULL) + if(agent->identity_agent_path) LIBSSH2_FREE(agent->session, agent->identity_agent_path); agent_free_identities(agent); @@ -824,7 +992,7 @@ libssh2_agent_free(LIBSSH2_AGENT *agent) } /* - * libssh2_agent_set_identity_path() + * libssh2_agent_set_identity_path * * Allows a custom agent socket path beyond SSH_AUTH_SOCK env * @@ -849,7 +1017,7 @@ libssh2_agent_set_identity_path(LIBSSH2_AGENT *agent, const char *path) } /* - * libssh2_agent_get_identity_path() + * libssh2_agent_get_identity_path * * Returns the custom agent socket path if set * |