summaryrefslogtreecommitdiff
path: root/libs/libssh2/src/agent.c
diff options
context:
space:
mode:
authorGeorge Hazan <george.hazan@gmail.com>2023-06-09 21:40:16 +0300
committerGeorge Hazan <george.hazan@gmail.com>2023-06-09 21:40:16 +0300
commit927f00cc19b7239a1fe12abe30b472d61b753d8d (patch)
tree68a190dd83dc2dcceb82464a1953f2701af2a109 /libs/libssh2/src/agent.c
parent1b241cad53b8c3c5300409fe681de18e636dcf3d (diff)
fixes #3551 (Update libssh2 to 1.11.0)
Diffstat (limited to 'libs/libssh2/src/agent.c')
-rw-r--r--libs/libssh2/src/agent.c266
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
*