summaryrefslogtreecommitdiffstats
path: root/contrib/libs/libssh2/src
diff options
context:
space:
mode:
authorgazimovbulat <[email protected]>2025-05-13 00:09:51 +0300
committergazimovbulat <[email protected]>2025-05-13 00:22:36 +0300
commit2c2bd72c4484a5704db08733e5e22b58eee89944 (patch)
tree83070d4d8e3ee75c05b3a61b990c72a127411f8d /contrib/libs/libssh2/src
parent31c8ff060ca43cd2220f90cbcc619ff5f8c6f37e (diff)
feat contrib curl: include libssh2 for sftp
commit_hash:6721cdd5ed873cd6f6243dcb846cacad6acb2155
Diffstat (limited to 'contrib/libs/libssh2/src')
-rw-r--r--contrib/libs/libssh2/src/agent.c880
-rw-r--r--contrib/libs/libssh2/src/agent.h112
-rw-r--r--contrib/libs/libssh2/src/agent_win.c361
-rw-r--r--contrib/libs/libssh2/src/bcrypt_pbkdf.c180
-rw-r--r--contrib/libs/libssh2/src/blf.h89
-rw-r--r--contrib/libs/libssh2/src/blowfish.c697
-rw-r--r--contrib/libs/libssh2/src/channel.c2895
-rw-r--r--contrib/libs/libssh2/src/channel.h141
-rw-r--r--contrib/libs/libssh2/src/comp.c377
-rw-r--r--contrib/libs/libssh2/src/comp.h44
-rw-r--r--contrib/libs/libssh2/src/crypt.c349
-rw-r--r--contrib/libs/libssh2/src/crypto.h248
-rw-r--r--contrib/libs/libssh2/src/global.c78
-rw-r--r--contrib/libs/libssh2/src/hostkey.c1168
-rw-r--r--contrib/libs/libssh2/src/keepalive.c100
-rw-r--r--contrib/libs/libssh2/src/kex.c4141
-rw-r--r--contrib/libs/libssh2/src/knownhost.c1271
-rw-r--r--contrib/libs/libssh2/src/libssh2_config-linux.h259
-rw-r--r--contrib/libs/libssh2/src/libssh2_config-osx.h262
-rw-r--r--contrib/libs/libssh2/src/libssh2_config-win.h262
-rw-r--r--contrib/libs/libssh2/src/libssh2_config.h9
-rw-r--r--contrib/libs/libssh2/src/libssh2_priv.h1154
-rw-r--r--contrib/libs/libssh2/src/mac.c414
-rw-r--r--contrib/libs/libssh2/src/mac.h66
-rw-r--r--contrib/libs/libssh2/src/misc.c872
-rw-r--r--contrib/libs/libssh2/src/misc.h125
-rw-r--r--contrib/libs/libssh2/src/openssl.c3286
-rw-r--r--contrib/libs/libssh2/src/openssl.h396
-rw-r--r--contrib/libs/libssh2/src/packet.c1338
-rw-r--r--contrib/libs/libssh2/src/packet.h76
-rw-r--r--contrib/libs/libssh2/src/pem.c911
-rw-r--r--contrib/libs/libssh2/src/publickey.c1278
-rw-r--r--contrib/libs/libssh2/src/scp.c1145
-rw-r--r--contrib/libs/libssh2/src/session.c1832
-rw-r--r--contrib/libs/libssh2/src/session.h93
-rw-r--r--contrib/libs/libssh2/src/sftp.c3755
-rw-r--r--contrib/libs/libssh2/src/sftp.h238
-rw-r--r--contrib/libs/libssh2/src/transport.c924
-rw-r--r--contrib/libs/libssh2/src/transport.h86
-rw-r--r--contrib/libs/libssh2/src/userauth.c2112
-rw-r--r--contrib/libs/libssh2/src/userauth.h51
-rw-r--r--contrib/libs/libssh2/src/version.c54
42 files changed, 34129 insertions, 0 deletions
diff --git a/contrib/libs/libssh2/src/agent.c b/contrib/libs/libssh2/src/agent.c
new file mode 100644
index 00000000000..8bfe28b9e9c
--- /dev/null
+++ b/contrib/libs/libssh2/src/agent.c
@@ -0,0 +1,880 @@
+/*
+ * Copyright (c) 2009 by Daiki Ueno
+ * Copyright (C) 2010-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include "agent.h"
+#include "misc.h"
+#include <errno.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#else
+/* Use the existence of sys/un.h as a test if Unix domain socket is
+ supported. winsock*.h define PF_UNIX/AF_UNIX but do not actually
+ support them. */
+#undef PF_UNIX
+#endif
+#include "userauth.h"
+#include "session.h"
+#ifdef WIN32
+#include <stdlib.h>
+#endif
+
+/* Requests from client to agent for protocol 1 key operations */
+#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1
+#define SSH_AGENTC_RSA_CHALLENGE 3
+#define SSH_AGENTC_ADD_RSA_IDENTITY 7
+#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8
+#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9
+#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24
+
+/* Requests from client to agent for protocol 2 key operations */
+#define SSH2_AGENTC_REQUEST_IDENTITIES 11
+#define SSH2_AGENTC_SIGN_REQUEST 13
+#define SSH2_AGENTC_ADD_IDENTITY 17
+#define SSH2_AGENTC_REMOVE_IDENTITY 18
+#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19
+#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25
+
+/* Key-type independent requests from client to agent */
+#define SSH_AGENTC_ADD_SMARTCARD_KEY 20
+#define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21
+#define SSH_AGENTC_LOCK 22
+#define SSH_AGENTC_UNLOCK 23
+#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26
+
+/* Generic replies from agent to client */
+#define SSH_AGENT_FAILURE 5
+#define SSH_AGENT_SUCCESS 6
+
+/* Replies from agent to client for protocol 1 key operations */
+#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2
+#define SSH_AGENT_RSA_RESPONSE 4
+
+/* Replies from agent to client for protocol 2 key operations */
+#define SSH2_AGENT_IDENTITIES_ANSWER 12
+#define SSH2_AGENT_SIGN_RESPONSE 14
+
+/* Key constraint identifiers */
+#define SSH_AGENT_CONSTRAIN_LIFETIME 1
+#define SSH_AGENT_CONSTRAIN_CONFIRM 2
+
+#ifdef PF_UNIX
+static int
+agent_connect_unix(LIBSSH2_AGENT *agent)
+{
+ const char *path;
+ struct sockaddr_un s_un;
+
+ path = agent->identity_agent_path;
+ if(!path) {
+ path = getenv("SSH_AUTH_SOCK");
+ if(!path)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
+ "no auth sock variable");
+ }
+
+ agent->fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if(agent->fd < 0)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_SOCKET,
+ "failed creating socket");
+
+ s_un.sun_family = AF_UNIX;
+ 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) {
+ close(agent->fd);
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "failed connecting with agent");
+ }
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+#define RECV_SEND_ALL(func, socket, buffer, length, flags, abstract) \
+ int rc; \
+ size_t finished = 0; \
+ \
+ while(finished < length) { \
+ rc = func(socket, \
+ (char *)buffer + finished, length - finished, \
+ flags, abstract); \
+ if(rc < 0) \
+ return rc; \
+ \
+ finished += rc; \
+ } \
+ \
+ return finished;
+
+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)
+{
+ unsigned char buf[4];
+ int rc;
+
+ /* Send the length of the request */
+ if(transctx->state == agent_NB_state_request_created) {
+ _libssh2_htonu32(buf, transctx->request_len);
+ rc = _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)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
+ "agent send failed");
+ transctx->state = agent_NB_state_request_length_sent;
+ }
+
+ /* Send the request body */
+ if(transctx->state == agent_NB_state_request_length_sent) {
+ rc = _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)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
+ "agent send failed");
+ transctx->state = agent_NB_state_request_sent;
+ }
+
+ /* Receive the length of a response */
+ if(transctx->state == agent_NB_state_request_sent) {
+ rc = _recv_all(agent->session->recv, agent->fd,
+ buf, sizeof buf, 0, &agent->session->abstract);
+ if(rc < 0) {
+ if(rc == -EAGAIN)
+ return LIBSSH2_ERROR_EAGAIN;
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV,
+ "agent recv failed");
+ }
+ transctx->response_len = _libssh2_ntohu32(buf);
+ transctx->response = LIBSSH2_ALLOC(agent->session,
+ transctx->response_len);
+ if(!transctx->response)
+ return LIBSSH2_ERROR_ALLOC;
+
+ transctx->state = agent_NB_state_response_length_received;
+ }
+
+ /* Receive the response body */
+ if(transctx->state == agent_NB_state_response_length_received) {
+ rc = _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;
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
+ "agent recv failed");
+ }
+ transctx->state = agent_NB_state_response_received;
+ }
+
+ return 0;
+}
+
+static int
+agent_disconnect_unix(LIBSSH2_AGENT *agent)
+{
+ int ret;
+ ret = close(agent->fd);
+ if(ret != -1)
+ agent->fd = LIBSSH2_INVALID_SOCKET;
+ else
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
+ "failed closing the agent socket");
+ return LIBSSH2_ERROR_NONE;
+}
+
+struct agent_ops agent_ops_unix = {
+ agent_connect_unix,
+ agent_transact_unix,
+ agent_disconnect_unix
+};
+#endif /* PF_UNIX */
+
+#ifdef WIN32
+/* Code to talk to Pageant was taken from PuTTY.
+ *
+ * Portions copyright Robert de Bath, Joris van Rantwijk, Delian
+ * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas
+ * Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,
+ * Markus Kuhn, Colin Watson, and CORE SDI S.A.
+ */
+#define PAGEANT_COPYDATA_ID 0x804e50ba /* random goop */
+#define PAGEANT_MAX_MSGLEN 8192
+
+static int
+agent_connect_pageant(LIBSSH2_AGENT *agent)
+{
+ HWND hwnd;
+ hwnd = FindWindowA("Pageant", "Pageant");
+ if(!hwnd)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "failed connecting agent");
+ agent->fd = 0; /* Mark as the connection has been established */
+ return LIBSSH2_ERROR_NONE;
+}
+
+static int
+agent_transact_pageant(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
+{
+ HWND hwnd;
+ char mapname[23];
+ HANDLE filemap;
+ unsigned char *p;
+ unsigned char *p2;
+ int id;
+ COPYDATASTRUCT cds;
+
+ if(!transctx || 4 + transctx->request_len > PAGEANT_MAX_MSGLEN)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_INVAL,
+ "illegal input");
+
+ hwnd = FindWindowA("Pageant", "Pageant");
+ if(!hwnd)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "found no pageant");
+
+ snprintf(mapname, sizeof(mapname),
+ "PageantRequest%08x%c", (unsigned)GetCurrentThreadId(), '\0');
+ filemap = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
+ 0, PAGEANT_MAX_MSGLEN, mapname);
+
+ if(filemap == NULL || 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) {
+ CloseHandle(filemap);
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "failed to open pageant filemap for writing");
+ }
+
+ _libssh2_store_str(&p2, (const char *)transctx->request,
+ transctx->request_len);
+
+ cds.dwData = PAGEANT_COPYDATA_ID;
+ cds.cbData = 1 + strlen(mapname);
+ cds.lpData = mapname;
+
+ id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds);
+ if(id > 0) {
+ transctx->response_len = _libssh2_ntohu32(p);
+ if(transctx->response_len > PAGEANT_MAX_MSGLEN) {
+ UnmapViewOfFile(p);
+ CloseHandle(filemap);
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "agent setup fail");
+ }
+ transctx->response = LIBSSH2_ALLOC(agent->session,
+ transctx->response_len);
+ if(!transctx->response) {
+ UnmapViewOfFile(p);
+ CloseHandle(filemap);
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_ALLOC,
+ "agent malloc");
+ }
+ memcpy(transctx->response, p + 4, transctx->response_len);
+ }
+
+ UnmapViewOfFile(p);
+ CloseHandle(filemap);
+ return 0;
+}
+
+static int
+agent_disconnect_pageant(LIBSSH2_AGENT *agent)
+{
+ agent->fd = LIBSSH2_INVALID_SOCKET;
+ return 0;
+}
+
+struct agent_ops agent_ops_pageant = {
+ agent_connect_pageant,
+ agent_transact_pageant,
+ agent_disconnect_pageant
+};
+#endif /* WIN32 */
+
+static struct {
+ const char *name;
+ struct agent_ops *ops;
+} supported_backends[] = {
+#ifdef WIN32
+ {"Pageant", &agent_ops_pageant},
+ {"OpenSSH", &agent_ops_openssh},
+#endif /* WIN32 */
+#ifdef PF_UNIX
+ {"Unix", &agent_ops_unix},
+#endif /* PF_UNIX */
+ {NULL, NULL}
+};
+
+static int
+agent_sign(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
+ const unsigned char *data, size_t data_len, void **abstract)
+{
+ LIBSSH2_AGENT *agent = (LIBSSH2_AGENT *) (*abstract);
+ agent_transaction_ctx_t transctx = &agent->transctx;
+ struct agent_publickey *identity = agent->identity;
+ ssize_t len = 1 + 4 + identity->external.blob_len + 4 + data_len + 4;
+ ssize_t method_len;
+ unsigned char *s;
+ int rc;
+
+ /* Create a request to sign the data */
+ if(transctx->state == agent_NB_state_init) {
+ s = transctx->request = LIBSSH2_ALLOC(session, len);
+ if(!transctx->request)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "out of memory");
+
+ *s++ = SSH2_AGENTC_SIGN_REQUEST;
+ /* key blob */
+ _libssh2_store_str(&s, (const char *)identity->external.blob,
+ identity->external.blob_len);
+ /* data */
+ _libssh2_store_str(&s, (const char *)data, data_len);
+
+ /* flags */
+ _libssh2_store_u32(&s, 0);
+
+ transctx->request_len = s - transctx->request;
+ transctx->send_recv_total = 0;
+ transctx->state = agent_NB_state_request_created;
+ }
+
+ /* Make sure to be re-called as a result of EAGAIN. */
+ if(*transctx->request != SSH2_AGENTC_SIGN_REQUEST)
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+ "illegal request");
+
+ if(!agent->ops)
+ /* if no agent has been connected, bail out */
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+ "agent not connected");
+
+ rc = agent->ops->transact(agent, transctx);
+ if(rc) {
+ goto error;
+ }
+ LIBSSH2_FREE(session, transctx->request);
+ transctx->request = NULL;
+
+ len = transctx->response_len;
+ s = transctx->response;
+ len--;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ if(*s != SSH2_AGENT_SIGN_RESPONSE) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ s++;
+
+ /* Skip the entire length of the signature */
+ len -= 4;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ s += 4;
+
+ /* Skip signing method */
+ len -= 4;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ method_len = _libssh2_ntohu32(s);
+ s += 4;
+ len -= method_len;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ s += method_len;
+
+ /* Read the signature */
+ len -= 4;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ *sig_len = _libssh2_ntohu32(s);
+ s += 4;
+ len -= *sig_len;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+
+ *sig = LIBSSH2_ALLOC(session, *sig_len);
+ if(!*sig) {
+ rc = LIBSSH2_ERROR_ALLOC;
+ goto error;
+ }
+ memcpy(*sig, s, *sig_len);
+
+ error:
+ LIBSSH2_FREE(session, transctx->request);
+ transctx->request = NULL;
+
+ LIBSSH2_FREE(session, transctx->response);
+ transctx->response = NULL;
+
+ return _libssh2_error(session, rc, "agent sign failure");
+}
+
+static int
+agent_list_identities(LIBSSH2_AGENT *agent)
+{
+ agent_transaction_ctx_t transctx = &agent->transctx;
+ ssize_t len, num_identities;
+ unsigned char *s;
+ int rc;
+ unsigned char c = SSH2_AGENTC_REQUEST_IDENTITIES;
+
+ /* Create a request to list identities */
+ 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;
+ }
+
+ /* Make sure to be re-called as a result of EAGAIN. */
+ if(*transctx->request != SSH2_AGENTC_REQUEST_IDENTITIES)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
+ "illegal agent request");
+
+ if(!agent->ops)
+ /* if no agent has been connected, bail out */
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_BAD_USE,
+ "agent not connected");
+
+ rc = agent->ops->transact(agent, transctx);
+ if(rc) {
+ LIBSSH2_FREE(agent->session, transctx->response);
+ transctx->response = NULL;
+ return rc;
+ }
+ transctx->request = NULL;
+
+ len = transctx->response_len;
+ s = transctx->response;
+ len--;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ if(*s != SSH2_AGENT_IDENTITIES_ANSWER) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ s++;
+
+ /* Read the length of identities */
+ len -= 4;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ num_identities = _libssh2_ntohu32(s);
+ s += 4;
+
+ while(num_identities--) {
+ struct agent_publickey *identity;
+ ssize_t comment_len;
+
+ /* Read the length of the blob */
+ len -= 4;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ goto error;
+ }
+ identity = LIBSSH2_ALLOC(agent->session, sizeof *identity);
+ if(!identity) {
+ rc = LIBSSH2_ERROR_ALLOC;
+ goto error;
+ }
+ identity->external.blob_len = _libssh2_ntohu32(s);
+ s += 4;
+
+ /* Read the blob */
+ len -= identity->external.blob_len;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ LIBSSH2_FREE(agent->session, identity);
+ goto error;
+ }
+
+ identity->external.blob = LIBSSH2_ALLOC(agent->session,
+ identity->external.blob_len);
+ if(!identity->external.blob) {
+ rc = LIBSSH2_ERROR_ALLOC;
+ LIBSSH2_FREE(agent->session, identity);
+ goto error;
+ }
+ memcpy(identity->external.blob, s, identity->external.blob_len);
+ s += identity->external.blob_len;
+
+ /* Read the length of the comment */
+ len -= 4;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ LIBSSH2_FREE(agent->session, identity->external.blob);
+ LIBSSH2_FREE(agent->session, identity);
+ goto error;
+ }
+ comment_len = _libssh2_ntohu32(s);
+ s += 4;
+
+ /* Read the comment */
+ len -= comment_len;
+ if(len < 0) {
+ rc = LIBSSH2_ERROR_AGENT_PROTOCOL;
+ LIBSSH2_FREE(agent->session, identity->external.blob);
+ LIBSSH2_FREE(agent->session, identity);
+ goto error;
+ }
+
+ identity->external.comment = LIBSSH2_ALLOC(agent->session,
+ comment_len + 1);
+ if(!identity->external.comment) {
+ rc = LIBSSH2_ERROR_ALLOC;
+ LIBSSH2_FREE(agent->session, identity->external.blob);
+ LIBSSH2_FREE(agent->session, identity);
+ goto error;
+ }
+ identity->external.comment[comment_len] = '\0';
+ memcpy(identity->external.comment, s, comment_len);
+ s += comment_len;
+
+ _libssh2_list_add(&agent->head, &identity->node);
+ }
+ error:
+ LIBSSH2_FREE(agent->session, transctx->response);
+ transctx->response = NULL;
+
+ return _libssh2_error(agent->session, rc,
+ "agent list id failed");
+}
+
+static void
+agent_free_identities(LIBSSH2_AGENT *agent)
+{
+ struct agent_publickey *node;
+ struct agent_publickey *next;
+
+ for(node = _libssh2_list_first(&agent->head); node; node = next) {
+ next = _libssh2_list_next(&node->node);
+ LIBSSH2_FREE(agent->session, node->external.blob);
+ LIBSSH2_FREE(agent->session, node->external.comment);
+ LIBSSH2_FREE(agent->session, node);
+ }
+ _libssh2_list_init(&agent->head);
+}
+
+#define AGENT_PUBLICKEY_MAGIC 0x3bdefed2
+/*
+ * agent_publickey_to_external()
+ *
+ * Copies data from the internal to the external representation struct.
+ *
+ */
+static struct libssh2_agent_publickey *
+agent_publickey_to_external(struct agent_publickey *node)
+{
+ struct libssh2_agent_publickey *ext = &node->external;
+
+ ext->magic = AGENT_PUBLICKEY_MAGIC;
+ ext->node = node;
+
+ return ext;
+}
+
+/*
+ * libssh2_agent_init
+ *
+ * Init an ssh-agent handle. Returns the pointer to the handle.
+ *
+ */
+LIBSSH2_API LIBSSH2_AGENT *
+libssh2_agent_init(LIBSSH2_SESSION *session)
+{
+ LIBSSH2_AGENT *agent;
+
+ agent = LIBSSH2_CALLOC(session, sizeof *agent);
+ if(!agent) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate space for agent connection");
+ return NULL;
+ }
+ agent->fd = LIBSSH2_INVALID_SOCKET;
+ agent->session = session;
+ agent->identity_agent_path = NULL;
+ _libssh2_list_init(&agent->head);
+
+#ifdef WIN32
+ agent->pipe = INVALID_HANDLE_VALUE;
+ memset(&agent->overlapped, 0, sizeof(OVERLAPPED));
+ agent->pending_io = FALSE;
+#endif
+
+ return agent;
+}
+
+/*
+ * libssh2_agent_connect()
+ *
+ * Connect to an ssh-agent.
+ *
+ * Returns 0 if succeeded, or a negative value for error.
+ */
+LIBSSH2_API int
+libssh2_agent_connect(LIBSSH2_AGENT *agent)
+{
+ int i, rc = -1;
+ for(i = 0; supported_backends[i].name; i++) {
+ agent->ops = supported_backends[i].ops;
+ rc = (agent->ops->connect)(agent);
+ if(!rc)
+ return 0;
+ }
+ return rc;
+}
+
+/*
+ * libssh2_agent_list_identities()
+ *
+ * Request ssh-agent to list identities.
+ *
+ * Returns 0 if succeeded, or a negative value for error.
+ */
+LIBSSH2_API int
+libssh2_agent_list_identities(LIBSSH2_AGENT *agent)
+{
+ 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()
+ *
+ * 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
+ * next.
+ *
+ * Returns:
+ * 0 if a fine public key was stored in 'store'
+ * 1 if end of public keys
+ * [negative] on errors
+ */
+LIBSSH2_API int
+libssh2_agent_get_identity(LIBSSH2_AGENT *agent,
+ struct libssh2_agent_publickey **ext,
+ struct libssh2_agent_publickey *oprev)
+{
+ struct agent_publickey *node;
+ if(oprev && oprev->node) {
+ /* we have a starting point */
+ struct agent_publickey *prev = oprev->node;
+
+ /* get the next node in the list */
+ node = _libssh2_list_next(&prev->node);
+ }
+ else
+ node = _libssh2_list_first(&agent->head);
+
+ if(!node)
+ /* no (more) node */
+ return 1;
+
+ *ext = agent_publickey_to_external(node);
+
+ return 0;
+}
+
+/*
+ * libssh2_agent_userauth()
+ *
+ * Do publickey user authentication with the help of ssh-agent.
+ *
+ * Returns 0 if succeeded, or a negative value for error.
+ */
+LIBSSH2_API int
+libssh2_agent_userauth(LIBSSH2_AGENT *agent,
+ const char *username,
+ struct libssh2_agent_publickey *identity)
+{
+ void *abstract = agent;
+ int rc;
+
+ if(agent->session->userauth_pblc_state == libssh2_NB_state_idle) {
+ memset(&agent->transctx, 0, sizeof agent->transctx);
+ agent->identity = identity->node;
+ }
+
+ BLOCK_ADJUST(rc, agent->session,
+ _libssh2_userauth_publickey(agent->session, username,
+ strlen(username),
+ identity->blob,
+ identity->blob_len,
+ agent_sign,
+ &abstract));
+ return rc;
+}
+
+/*
+ * libssh2_agent_disconnect()
+ *
+ * Close a connection to an ssh-agent.
+ *
+ * Returns 0 if succeeded, or a negative value for error.
+ */
+LIBSSH2_API int
+libssh2_agent_disconnect(LIBSSH2_AGENT *agent)
+{
+ if(agent->ops && agent->fd != LIBSSH2_INVALID_SOCKET)
+ return agent->ops->disconnect(agent);
+ return 0;
+}
+
+/*
+ * libssh2_agent_free()
+ *
+ * Free an ssh-agent handle. This function also frees the internal
+ * collection of public keys.
+ */
+LIBSSH2_API void
+libssh2_agent_free(LIBSSH2_AGENT *agent)
+{
+ /* Allow connection freeing when the socket has lost its connection */
+ if(agent->fd != LIBSSH2_INVALID_SOCKET) {
+ libssh2_agent_disconnect(agent);
+ }
+
+ if(agent->identity_agent_path != NULL)
+ LIBSSH2_FREE(agent->session, agent->identity_agent_path);
+
+ agent_free_identities(agent);
+ LIBSSH2_FREE(agent->session, agent);
+}
+
+/*
+ * libssh2_agent_set_identity_path()
+ *
+ * Allows a custom agent socket path beyond SSH_AUTH_SOCK env
+ *
+ */
+LIBSSH2_API void
+libssh2_agent_set_identity_path(LIBSSH2_AGENT *agent, const char *path)
+{
+ if(agent->identity_agent_path) {
+ LIBSSH2_FREE(agent->session, agent->identity_agent_path);
+ agent->identity_agent_path = NULL;
+ }
+
+ if(path) {
+ size_t path_len = strlen(path);
+ if(path_len < SIZE_MAX - 1) {
+ char *path_buf = LIBSSH2_ALLOC(agent->session, path_len + 1);
+ memcpy(path_buf, path, path_len);
+ path_buf[path_len] = '\0';
+ agent->identity_agent_path = path_buf;
+ }
+ }
+}
+
+/*
+ * libssh2_agent_get_identity_path()
+ *
+ * Returns the custom agent socket path if set
+ *
+ */
+LIBSSH2_API const char *libssh2_agent_get_identity_path(LIBSSH2_AGENT *agent)
+{
+ return agent->identity_agent_path;
+}
+
+
+/*
+ * _ya_libssh2_agent_sign()
+ *
+ * Yandex specific patch for exporting internal function.
+ *
+ * Returns 0 if succeeded, or a negative value for error.
+ */
+LIBSSH2_API int
+_ya_libssh2_agent_sign(LIBSSH2_SESSION *session,
+ unsigned char **sig,
+ size_t *sig_len,
+ const unsigned char *data,
+ size_t data_len,
+ LIBSSH2_AGENT *agent) {
+ return agent_sign(session,
+ sig,
+ sig_len,
+ data,
+ data_len,
+ &agent);
+}
diff --git a/contrib/libs/libssh2/src/agent.h b/contrib/libs/libssh2/src/agent.h
new file mode 100644
index 00000000000..dfac0715c83
--- /dev/null
+++ b/contrib/libs/libssh2/src/agent.h
@@ -0,0 +1,112 @@
+#ifndef __LIBSSH2_AGENT_H
+#define __LIBSSH2_AGENT_H
+/*
+ * Copyright (c) 2009 by Daiki Ueno
+ * Copyright (C) 2010-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include "misc.h"
+#include "session.h"
+#ifdef WIN32
+#include <stdlib.h>
+#endif
+
+/* non-blocking mode on agent connection is not yet implemented, but
+ for future use. */
+typedef enum {
+ agent_NB_state_init = 0,
+ agent_NB_state_request_created,
+ agent_NB_state_request_length_sent,
+ agent_NB_state_request_sent,
+ agent_NB_state_response_length_received,
+ agent_NB_state_response_received
+} agent_nonblocking_states;
+
+typedef struct agent_transaction_ctx {
+ unsigned char *request;
+ size_t request_len;
+ 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);
+typedef int (*agent_transact_func)(LIBSSH2_AGENT *agent,
+ agent_transaction_ctx_t transctx);
+typedef int (*agent_disconnect_func)(LIBSSH2_AGENT *agent);
+
+struct agent_publickey {
+ struct list_node node;
+
+ /* this is the struct we expose externally */
+ struct libssh2_agent_publickey external;
+};
+
+struct agent_ops {
+ agent_connect_func connect;
+ agent_transact_func transact;
+ agent_disconnect_func disconnect;
+};
+
+struct _LIBSSH2_AGENT
+{
+ LIBSSH2_SESSION *session; /* the session this "belongs to" */
+
+ libssh2_socket_t fd;
+
+ struct agent_ops *ops;
+
+ struct agent_transaction_ctx transctx;
+ struct agent_publickey *identity;
+ struct list_head head; /* list of public keys */
+
+ char *identity_agent_path; /* Path to a custom identity agent socket */
+
+#ifdef WIN32
+ OVERLAPPED overlapped;
+ HANDLE pipe;
+ BOOL pending_io;
+#endif
+};
+
+#ifdef WIN32
+extern struct agent_ops agent_ops_openssh;
+#endif
+
+#endif /* __LIBSSH2_AGENT_H */
diff --git a/contrib/libs/libssh2/src/agent_win.c b/contrib/libs/libssh2/src/agent_win.c
new file mode 100644
index 00000000000..a1605a95fcc
--- /dev/null
+++ b/contrib/libs/libssh2/src/agent_win.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2009 by Daiki Ueno
+ * Copyright (C) 2010-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include "agent.h"
+#include "misc.h"
+#include <errno.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#else
+/* Use the existence of sys/un.h as a test if Unix domain socket is
+ supported. winsock*.h define PF_UNIX/AF_UNIX but do not actually
+ support them. */
+#undef PF_UNIX
+#endif
+#include "userauth.h"
+#include "session.h"
+#ifdef WIN32
+#include <stdlib.h>
+#endif
+
+#ifdef WIN32
+/* Code to talk to OpenSSH was taken and modified from the Win32 port of
+ * Portable OpenSSH by the PowerShell team. Commit
+ * 8ab565c53f3619d6a1f5ac229e212cad8a52852c of
+ * https://github.com/PowerShell/openssh-portable.git was used as the base,
+ * specificaly the following files:
+ *
+ * - contrib\win32\win32compat\fileio.c
+ * - Structure of agent_connect_openssh from ssh_get_authentication_socket
+ * - Structure of agent_transact_openssh from ssh_request_reply
+ * - contrib\win32\win32compat\wmain_common.c
+ * - Windows equivalent functions for common Unix functions, inlined into
+ * this implementation
+ * - fileio_connect replacing connect
+ * - fileio_read replacing read
+ * - fileio_write replacing write
+ * - fileio_close replacing close
+ *
+ * Author: Tatu Ylonen <[email protected]>
+ * Copyright (c) 1995 Tatu Ylonen <[email protected]>, Espoo, Finland
+ * All rights reserved
+ * Functions for connecting the local authentication agent.
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ *
+ * SSH2 implementation,
+ * Copyright (c) 2000 Markus Friedl. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * Copyright (c) 2015 Microsoft Corp.
+ * All rights reserved
+ *
+ * Microsoft openssh win32 port
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#define WIN32_OPENSSH_AGENT_SOCK "\\\\.\\pipe\\openssh-ssh-agent"
+
+static int
+agent_connect_openssh(LIBSSH2_AGENT *agent)
+{
+ int ret = LIBSSH2_ERROR_NONE;
+ const char *path;
+ HANDLE pipe = INVALID_HANDLE_VALUE;
+ HANDLE event = NULL;
+
+ path = agent->identity_agent_path;
+ if(!path) {
+ path = getenv("SSH_AUTH_SOCK");
+ if(!path)
+ path = WIN32_OPENSSH_AGENT_SOCK;
+ }
+
+ for(;;) {
+ pipe = CreateFileA(
+ path,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ /* Non-blocking mode for agent connections is not implemented at
+ * the point this was implemented. The code for Win32 OpenSSH
+ * should support non-blocking IO, but the code calling it doesn't
+ * support it as of yet.
+ * When non-blocking IO is implemented for the surrounding code,
+ * uncomment the following line to enable support within the Win32
+ * OpenSSH code.
+ */
+ /* FILE_FLAG_OVERLAPPED | */
+ SECURITY_SQOS_PRESENT |
+ SECURITY_IDENTIFICATION,
+ NULL
+ );
+
+ if(pipe != INVALID_HANDLE_VALUE)
+ break;
+ if(GetLastError() != ERROR_PIPE_BUSY)
+ break;
+
+ /* Wait up to 1 second for a pipe instance to become available */
+ if(!WaitNamedPipeA(path, 1000))
+ break;
+ }
+
+ if(pipe == INVALID_HANDLE_VALUE) {
+ ret = _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "unable to connect to agent pipe");
+ goto cleanup;
+ }
+
+ if(SetHandleInformation(pipe, HANDLE_FLAG_INHERIT, 0) == FALSE) {
+ ret = _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "unable to set handle information of agent pipe");
+ goto cleanup;
+ }
+
+ event = CreateEventA(NULL, TRUE, FALSE, NULL);
+ if(event == NULL) {
+ ret = _libssh2_error(agent->session, LIBSSH2_ERROR_AGENT_PROTOCOL,
+ "unable to create async I/O event");
+ goto cleanup;
+ }
+
+ agent->pipe = pipe;
+ pipe = INVALID_HANDLE_VALUE;
+ agent->overlapped.hEvent = event;
+ event = NULL;
+ agent->fd = 0; /* Mark as the connection has been established */
+
+cleanup:
+ if(event != NULL)
+ CloseHandle(event);
+ if(pipe != INVALID_HANDLE_VALUE)
+ CloseHandle(pipe);
+ return ret;
+}
+
+#define RECV_SEND_ALL(func, agent, buffer, length, total) \
+ DWORD bytes_transfered; \
+ BOOL ret; \
+ DWORD err; \
+ int rc; \
+ \
+ while(*total < length) { \
+ if(!agent->pending_io) \
+ ret = func(agent->pipe, (char *)buffer + *total, \
+ (DWORD)(length - *total), &bytes_transfered, \
+ &agent->overlapped); \
+ else \
+ ret = GetOverlappedResult(agent->pipe, &agent->overlapped, \
+ &bytes_transfered, FALSE); \
+ \
+ *total += bytes_transfered; \
+ if(!ret) { \
+ err = GetLastError(); \
+ if((!agent->pending_io && ERROR_IO_PENDING == err) \
+ || (agent->pending_io && ERROR_IO_INCOMPLETE == err)) { \
+ agent->pending_io = TRUE; \
+ return LIBSSH2_ERROR_EAGAIN; \
+ } \
+ \
+ return LIBSSH2_ERROR_SOCKET_NONE; \
+ } \
+ agent->pending_io = FALSE; \
+ } \
+ \
+ rc = (int)*total; \
+ *total = 0; \
+ return rc;
+
+static int
+win32_openssh_send_all(LIBSSH2_AGENT *agent, void *buffer, size_t length,
+ size_t *send_recv_total)
+{
+ RECV_SEND_ALL(WriteFile, agent, buffer, length, send_recv_total)
+}
+
+static int
+win32_openssh_recv_all(LIBSSH2_AGENT *agent, void *buffer, size_t length,
+ size_t *send_recv_total)
+{
+ RECV_SEND_ALL(ReadFile, agent, buffer, length, send_recv_total)
+}
+
+#undef RECV_SEND_ALL
+
+static int
+agent_transact_openssh(LIBSSH2_AGENT *agent, agent_transaction_ctx_t transctx)
+{
+ unsigned char buf[4];
+ int rc;
+
+ /* Send the length of the request */
+ if(transctx->state == agent_NB_state_request_created) {
+ _libssh2_htonu32(buf, (uint32_t)transctx->request_len);
+ rc = win32_openssh_send_all(agent, buf, sizeof buf,
+ &transctx->send_recv_total);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return LIBSSH2_ERROR_EAGAIN;
+ else if(rc < 0)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
+ "agent send failed");
+ transctx->state = agent_NB_state_request_length_sent;
+ }
+
+ /* Send the request body */
+ if(transctx->state == agent_NB_state_request_length_sent) {
+ rc = win32_openssh_send_all(agent, transctx->request,
+ transctx->request_len,
+ &transctx->send_recv_total);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return LIBSSH2_ERROR_EAGAIN;
+ else if(rc < 0)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_SEND,
+ "agent send failed");
+ transctx->state = agent_NB_state_request_sent;
+ }
+
+ /* Receive the length of the body */
+ if(transctx->state == agent_NB_state_request_sent) {
+ rc = win32_openssh_recv_all(agent, buf, sizeof buf,
+ &transctx->send_recv_total);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return LIBSSH2_ERROR_EAGAIN;
+ else if(rc < 0)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV,
+ "agent recv failed");
+
+ transctx->response_len = _libssh2_ntohu32(buf);
+ transctx->response = LIBSSH2_ALLOC(agent->session,
+ transctx->response_len);
+ if(!transctx->response)
+ return LIBSSH2_ERROR_ALLOC;
+
+ transctx->state = agent_NB_state_response_length_received;
+ }
+
+ /* Receive the response body */
+ if(transctx->state == agent_NB_state_response_length_received) {
+ rc = win32_openssh_recv_all(agent, transctx->response,
+ transctx->response_len,
+ &transctx->send_recv_total);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return LIBSSH2_ERROR_EAGAIN;
+ else if(rc < 0)
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_RECV,
+ "agent recv failed");
+ transctx->state = agent_NB_state_response_received;
+ }
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+static int
+agent_disconnect_openssh(LIBSSH2_AGENT *agent)
+{
+ if(!CancelIo(agent->pipe))
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
+ "failed to cancel pending IO of agent pipe");
+ if(!CloseHandle(agent->overlapped.hEvent))
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
+ "failed to close handle to async I/O event");
+ agent->overlapped.hEvent = NULL;
+ /* let queued APCs (if any) drain */
+ SleepEx(0, TRUE);
+ if(!CloseHandle(agent->pipe))
+ return _libssh2_error(agent->session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
+ "failed to close handle to agent pipe");
+
+ agent->pipe = INVALID_HANDLE_VALUE;
+ agent->fd = LIBSSH2_INVALID_SOCKET;
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+struct agent_ops agent_ops_openssh = {
+ agent_connect_openssh,
+ agent_transact_openssh,
+ agent_disconnect_openssh
+};
+#endif /* WIN32 */
diff --git a/contrib/libs/libssh2/src/bcrypt_pbkdf.c b/contrib/libs/libssh2/src/bcrypt_pbkdf.c
new file mode 100644
index 00000000000..f782bcac5f7
--- /dev/null
+++ b/contrib/libs/libssh2/src/bcrypt_pbkdf.c
@@ -0,0 +1,180 @@
+/* $OpenBSD: bcrypt_pbkdf.c,v 1.4 2013/07/29 00:55:53 tedu Exp $ */
+/*
+ * Copyright (c) 2013 Ted Unangst <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#ifndef HAVE_BCRYPT_PBKDF
+
+#include "libssh2_priv.h"
+#include <stdlib.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include "blf.h"
+
+#define MINIMUM(a,b) (((a) < (b)) ? (a) : (b))
+
+/*
+ * pkcs #5 pbkdf2 implementation using the "bcrypt" hash
+ *
+ * The bcrypt hash function is derived from the bcrypt password hashing
+ * function with the following modifications:
+ * 1. The input password and salt are preprocessed with SHA512.
+ * 2. The output length is expanded to 256 bits.
+ * 3. Subsequently the magic string to be encrypted is lengthened and modified
+ * to "OxychromaticBlowfishSwatDynamite"
+ * 4. The hash function is defined to perform 64 rounds of initial state
+ * expansion. (More rounds are performed by iterating the hash.)
+ *
+ * Note that this implementation pulls the SHA512 operations into the caller
+ * as a performance optimization.
+ *
+ * One modification from official pbkdf2. Instead of outputting key material
+ * linearly, we mix it. pbkdf2 has a known weakness where if one uses it to
+ * generate (i.e.) 512 bits of key material for use as two 256 bit keys, an
+ * attacker can merely run once through the outer loop below, but the user
+ * always runs it twice. Shuffling output bytes requires computing the
+ * entirety of the key material to assemble any subkey. This is something a
+ * wise caller could do; we just do it for you.
+ */
+
+#define BCRYPT_BLOCKS 8
+#define BCRYPT_HASHSIZE (BCRYPT_BLOCKS * 4)
+
+static void
+bcrypt_hash(uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *out)
+{
+ blf_ctx state;
+ uint8_t ciphertext[BCRYPT_HASHSIZE] =
+ "OxychromaticBlowfishSwatDynamite";
+ uint32_t cdata[BCRYPT_BLOCKS];
+ int i;
+ uint16_t j;
+ size_t shalen = SHA512_DIGEST_LENGTH;
+
+ /* key expansion */
+ Blowfish_initstate(&state);
+ Blowfish_expandstate(&state, sha2salt, shalen, sha2pass, shalen);
+ for(i = 0; i < 64; i++) {
+ Blowfish_expand0state(&state, sha2salt, shalen);
+ Blowfish_expand0state(&state, sha2pass, shalen);
+ }
+
+ /* encryption */
+ j = 0;
+ for(i = 0; i < BCRYPT_BLOCKS; i++)
+ cdata[i] = Blowfish_stream2word(ciphertext, sizeof(ciphertext),
+ &j);
+ for(i = 0; i < 64; i++)
+ blf_enc(&state, cdata, BCRYPT_BLOCKS / 2);
+
+ /* copy out */
+ for(i = 0; i < BCRYPT_BLOCKS; i++) {
+ out[4 * i + 3] = (cdata[i] >> 24) & 0xff;
+ out[4 * i + 2] = (cdata[i] >> 16) & 0xff;
+ out[4 * i + 1] = (cdata[i] >> 8) & 0xff;
+ out[4 * i + 0] = cdata[i] & 0xff;
+ }
+
+ /* zap */
+ _libssh2_explicit_zero(ciphertext, sizeof(ciphertext));
+ _libssh2_explicit_zero(cdata, sizeof(cdata));
+ _libssh2_explicit_zero(&state, sizeof(state));
+}
+
+int
+bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt,
+ size_t saltlen,
+ uint8_t *key, size_t keylen, unsigned int rounds)
+{
+ uint8_t sha2pass[SHA512_DIGEST_LENGTH];
+ uint8_t sha2salt[SHA512_DIGEST_LENGTH];
+ uint8_t out[BCRYPT_HASHSIZE];
+ uint8_t tmpout[BCRYPT_HASHSIZE];
+ uint8_t *countsalt;
+ size_t i, j, amt, stride;
+ uint32_t count;
+ size_t origkeylen = keylen;
+ libssh2_sha512_ctx ctx;
+
+ /* nothing crazy */
+ if(rounds < 1)
+ return -1;
+ if(passlen == 0 || saltlen == 0 || keylen == 0 ||
+ keylen > sizeof(out) * sizeof(out) || saltlen > 1<<20)
+ return -1;
+ countsalt = calloc(1, saltlen + 4);
+ if(countsalt == NULL)
+ return -1;
+ stride = (keylen + sizeof(out) - 1) / sizeof(out);
+ amt = (keylen + stride - 1) / stride;
+
+ memcpy(countsalt, salt, saltlen);
+
+ /* collapse password */
+ libssh2_sha512_init(&ctx);
+ libssh2_sha512_update(ctx, pass, passlen);
+ libssh2_sha512_final(ctx, sha2pass);
+
+ /* generate key, sizeof(out) at a time */
+ for(count = 1; keylen > 0; count++) {
+ countsalt[saltlen + 0] = (count >> 24) & 0xff;
+ countsalt[saltlen + 1] = (count >> 16) & 0xff;
+ countsalt[saltlen + 2] = (count >> 8) & 0xff;
+ countsalt[saltlen + 3] = count & 0xff;
+
+ /* first round, salt is salt */
+ libssh2_sha512_init(&ctx);
+ libssh2_sha512_update(ctx, countsalt, saltlen + 4);
+ libssh2_sha512_final(ctx, sha2salt);
+
+ bcrypt_hash(sha2pass, sha2salt, tmpout);
+ memcpy(out, tmpout, sizeof(out));
+
+ for(i = 1; i < rounds; i++) {
+ /* subsequent rounds, salt is previous output */
+ libssh2_sha512_init(&ctx);
+ libssh2_sha512_update(ctx, tmpout, sizeof(tmpout));
+ libssh2_sha512_final(ctx, sha2salt);
+
+ bcrypt_hash(sha2pass, sha2salt, tmpout);
+ for(j = 0; j < sizeof(out); j++)
+ out[j] ^= tmpout[j];
+ }
+
+ /*
+ * pbkdf2 deviation: output the key material non-linearly.
+ */
+ amt = MINIMUM(amt, keylen);
+ for(i = 0; i < amt; i++) {
+ size_t dest = i * stride + (count - 1);
+ if(dest >= origkeylen) {
+ break;
+ }
+ key[dest] = out[i];
+ }
+ keylen -= i;
+ }
+
+ /* zap */
+ _libssh2_explicit_zero(out, sizeof(out));
+ free(countsalt);
+
+ return 0;
+}
+#endif /* HAVE_BCRYPT_PBKDF */
diff --git a/contrib/libs/libssh2/src/blf.h b/contrib/libs/libssh2/src/blf.h
new file mode 100644
index 00000000000..5b7c8aae06a
--- /dev/null
+++ b/contrib/libs/libssh2/src/blf.h
@@ -0,0 +1,89 @@
+#ifndef __LIBSSH2_BLF_H
+#define __LIBSSH2_BLF_H
+/* $OpenBSD: blf.h,v 1.7 2007/03/14 17:59:41 grunk Exp $ */
+/*
+ * Blowfish - a fast block cipher designed by Bruce Schneier
+ *
+ * Copyright 1997 Niels Provos <[email protected]>
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Niels Provos.
+ * 4. 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+#if !defined(HAVE_BCRYPT_PBKDF) && !defined(HAVE_BLH_H)
+
+/* Schneier specifies a maximum key length of 56 bytes.
+ * This ensures that every key bit affects every cipher
+ * bit. However, the subkeys can hold up to 72 bytes.
+ * Warning: For normal blowfish encryption only 56 bytes
+ * of the key affect all cipherbits.
+ */
+
+#define BLF_N 16 /* Number of Subkeys */
+#define BLF_MAXKEYLEN ((BLF_N-2)*4) /* 448 bits */
+#define BLF_MAXUTILIZED ((BLF_N + 2)*4) /* 576 bits */
+
+/* Blowfish context */
+typedef struct BlowfishContext {
+ uint32_t S[4][256]; /* S-Boxes */
+ uint32_t P[BLF_N + 2]; /* Subkeys */
+} blf_ctx;
+
+/* Raw access to customized Blowfish
+ * blf_key is just:
+ * Blowfish_initstate( state )
+ * Blowfish_expand0state( state, key, keylen )
+ */
+
+void Blowfish_encipher(blf_ctx *, uint32_t *, uint32_t *);
+void Blowfish_decipher(blf_ctx *, uint32_t *, uint32_t *);
+void Blowfish_initstate(blf_ctx *);
+void Blowfish_expand0state(blf_ctx *, const uint8_t *, uint16_t);
+void Blowfish_expandstate
+(blf_ctx *, const uint8_t *, uint16_t, const uint8_t *, uint16_t);
+
+/* Standard Blowfish */
+
+void blf_key(blf_ctx *, const uint8_t *, uint16_t);
+void blf_enc(blf_ctx *, uint32_t *, uint16_t);
+void blf_dec(blf_ctx *, uint32_t *, uint16_t);
+
+void blf_ecb_encrypt(blf_ctx *, uint8_t *, uint32_t);
+void blf_ecb_decrypt(blf_ctx *, uint8_t *, uint32_t);
+
+void blf_cbc_encrypt(blf_ctx *, uint8_t *, uint8_t *, uint32_t);
+void blf_cbc_decrypt(blf_ctx *, uint8_t *, uint8_t *, uint32_t);
+
+/* Converts uint8_t to uint32_t */
+uint32_t Blowfish_stream2word(const uint8_t *, uint16_t, uint16_t *);
+
+/* bcrypt with pbkd */
+int bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt,
+ size_t saltlen,
+ uint8_t *key, size_t keylen, unsigned int rounds);
+
+#endif /* !defined(HAVE_BCRYPT_PBKDF) && !defined(HAVE_BLH_H) */
+#endif /* __LIBSSH2_BLF_H */
diff --git a/contrib/libs/libssh2/src/blowfish.c b/contrib/libs/libssh2/src/blowfish.c
new file mode 100644
index 00000000000..4aefc66ac70
--- /dev/null
+++ b/contrib/libs/libssh2/src/blowfish.c
@@ -0,0 +1,697 @@
+/* $OpenBSD: blowfish.c,v 1.18 2004/11/02 17:23:26 hshoexer Exp $ */
+/*
+ * Blowfish block cipher for OpenBSD
+ * Copyright 1997 Niels Provos <[email protected]>
+ * All rights reserved.
+ *
+ * Implementation advice by David Mazieres <[email protected]>.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Niels Provos.
+ * 4. 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * This code is derived from section 14.3 and the given source
+ * in section V of Applied Cryptography, second edition.
+ * Blowfish is an unpatented fast block cipher designed by
+ * Bruce Schneier.
+ */
+
+
+#if !defined(HAVE_BCRYPT_PBKDF) && (!defined(HAVE_BLOWFISH_INITSTATE) || \
+ !defined(HAVE_BLOWFISH_EXPAND0STATE) || \
+ !defined(HAVE_BLF_ENC))
+
+#if 0
+#include <stdio.h> /* used for debugging */
+#include <string.h>
+#endif
+
+#include <sys/types.h>
+
+#include "libssh2.h"
+#include "blf.h"
+
+#undef inline
+#ifdef __GNUC__
+#define inline __inline
+#else /* !__GNUC__ */
+#define inline
+#endif /* !__GNUC__ */
+
+/* Function for Feistel Networks */
+
+#define F(s, x) ((((s)[ (((x)>>24)&0xFF)] \
+ + (s)[0x100 + (((x)>>16)&0xFF)]) \
+ ^ (s)[0x200 + (((x)>> 8)&0xFF)]) \
+ + (s)[0x300 + ( (x) &0xFF)])
+
+#define BLFRND(s,p,i,j,n) (i ^= F(s,j) ^ (p)[n])
+
+void
+Blowfish_encipher(blf_ctx *c, uint32_t *xl, uint32_t *xr)
+{
+ uint32_t Xl;
+ uint32_t Xr;
+ uint32_t *s = c->S[0];
+ uint32_t *p = c->P;
+
+ Xl = *xl;
+ Xr = *xr;
+
+ Xl ^= p[0];
+ BLFRND(s, p, Xr, Xl, 1); BLFRND(s, p, Xl, Xr, 2);
+ BLFRND(s, p, Xr, Xl, 3); BLFRND(s, p, Xl, Xr, 4);
+ BLFRND(s, p, Xr, Xl, 5); BLFRND(s, p, Xl, Xr, 6);
+ BLFRND(s, p, Xr, Xl, 7); BLFRND(s, p, Xl, Xr, 8);
+ BLFRND(s, p, Xr, Xl, 9); BLFRND(s, p, Xl, Xr, 10);
+ BLFRND(s, p, Xr, Xl, 11); BLFRND(s, p, Xl, Xr, 12);
+ BLFRND(s, p, Xr, Xl, 13); BLFRND(s, p, Xl, Xr, 14);
+ BLFRND(s, p, Xr, Xl, 15); BLFRND(s, p, Xl, Xr, 16);
+
+ *xl = Xr ^ p[17];
+ *xr = Xl;
+}
+
+void
+Blowfish_decipher(blf_ctx *c, uint32_t *xl, uint32_t *xr)
+{
+ uint32_t Xl;
+ uint32_t Xr;
+ uint32_t *s = c->S[0];
+ uint32_t *p = c->P;
+
+ Xl = *xl;
+ Xr = *xr;
+
+ Xl ^= p[17];
+ BLFRND(s, p, Xr, Xl, 16); BLFRND(s, p, Xl, Xr, 15);
+ BLFRND(s, p, Xr, Xl, 14); BLFRND(s, p, Xl, Xr, 13);
+ BLFRND(s, p, Xr, Xl, 12); BLFRND(s, p, Xl, Xr, 11);
+ BLFRND(s, p, Xr, Xl, 10); BLFRND(s, p, Xl, Xr, 9);
+ BLFRND(s, p, Xr, Xl, 8); BLFRND(s, p, Xl, Xr, 7);
+ BLFRND(s, p, Xr, Xl, 6); BLFRND(s, p, Xl, Xr, 5);
+ BLFRND(s, p, Xr, Xl, 4); BLFRND(s, p, Xl, Xr, 3);
+ BLFRND(s, p, Xr, Xl, 2); BLFRND(s, p, Xl, Xr, 1);
+
+ *xl = Xr ^ p[0];
+ *xr = Xl;
+}
+
+void
+Blowfish_initstate(blf_ctx *c)
+{
+ /* P-box and S-box tables initialized with digits of Pi */
+
+ static const blf_ctx initstate =
+ { {
+ {
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a},
+ {
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7},
+ {
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0},
+ {
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6}
+ },
+ {
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b
+ } };
+
+ *c = initstate;
+}
+
+uint32_t
+Blowfish_stream2word(const uint8_t *data, uint16_t databytes,
+ uint16_t *current)
+{
+ uint8_t i;
+ uint16_t j;
+ uint32_t temp;
+
+ temp = 0x00000000;
+ j = *current;
+
+ for(i = 0; i < 4; i++, j++) {
+ if(j >= databytes)
+ j = 0;
+ temp = (temp << 8) | data[j];
+ }
+
+ *current = j;
+ return temp;
+}
+
+void
+Blowfish_expand0state(blf_ctx *c, const uint8_t *key, uint16_t keybytes)
+{
+ uint16_t i;
+ uint16_t j;
+ uint16_t k;
+ uint32_t temp;
+ uint32_t datal;
+ uint32_t datar;
+
+ j = 0;
+ for(i = 0; i < BLF_N + 2; i++) {
+ /* Extract 4 int8 to 1 int32 from keystream */
+ temp = Blowfish_stream2word(key, keybytes, &j);
+ c->P[i] = c->P[i] ^ temp;
+ }
+
+ j = 0;
+ datal = 0x00000000;
+ datar = 0x00000000;
+ for(i = 0; i < BLF_N + 2; i += 2) {
+ Blowfish_encipher(c, &datal, &datar);
+
+ c->P[i] = datal;
+ c->P[i + 1] = datar;
+ }
+
+ for(i = 0; i < 4; i++) {
+ for(k = 0; k < 256; k += 2) {
+ Blowfish_encipher(c, &datal, &datar);
+
+ c->S[i][k] = datal;
+ c->S[i][k + 1] = datar;
+ }
+ }
+}
+
+
+void
+Blowfish_expandstate(blf_ctx *c, const uint8_t *data, uint16_t databytes,
+ const uint8_t *key, uint16_t keybytes)
+{
+ uint16_t i;
+ uint16_t j;
+ uint16_t k;
+ uint32_t temp;
+ uint32_t datal;
+ uint32_t datar;
+
+ j = 0;
+ for(i = 0; i < BLF_N + 2; i++) {
+ /* Extract 4 int8 to 1 int32 from keystream */
+ temp = Blowfish_stream2word(key, keybytes, &j);
+ c->P[i] = c->P[i] ^ temp;
+ }
+
+ j = 0;
+ datal = 0x00000000;
+ datar = 0x00000000;
+ for(i = 0; i < BLF_N + 2; i += 2) {
+ datal ^= Blowfish_stream2word(data, databytes, &j);
+ datar ^= Blowfish_stream2word(data, databytes, &j);
+ Blowfish_encipher(c, &datal, &datar);
+
+ c->P[i] = datal;
+ c->P[i + 1] = datar;
+ }
+
+ for(i = 0; i < 4; i++) {
+ for(k = 0; k < 256; k += 2) {
+ datal ^= Blowfish_stream2word(data, databytes, &j);
+ datar ^= Blowfish_stream2word(data, databytes, &j);
+ Blowfish_encipher(c, &datal, &datar);
+
+ c->S[i][k] = datal;
+ c->S[i][k + 1] = datar;
+ }
+ }
+
+}
+
+void
+blf_key(blf_ctx *c, const uint8_t *k, uint16_t len)
+{
+ /* Initialize S-boxes and subkeys with Pi */
+ Blowfish_initstate(c);
+
+ /* Transform S-boxes and subkeys with key */
+ Blowfish_expand0state(c, k, len);
+}
+
+void
+blf_enc(blf_ctx *c, uint32_t *data, uint16_t blocks)
+{
+ uint32_t *d;
+ uint16_t i;
+
+ d = data;
+ for(i = 0; i < blocks; i++) {
+ Blowfish_encipher(c, d, d + 1);
+ d += 2;
+ }
+}
+
+void
+blf_dec(blf_ctx *c, uint32_t *data, uint16_t blocks)
+{
+ uint32_t *d;
+ uint16_t i;
+
+ d = data;
+ for(i = 0; i < blocks; i++) {
+ Blowfish_decipher(c, d, d + 1);
+ d += 2;
+ }
+}
+
+void
+blf_ecb_encrypt(blf_ctx *c, uint8_t *data, uint32_t len)
+{
+ uint32_t l, r;
+ uint32_t i;
+
+ for(i = 0; i < len; i += 8) {
+ l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
+ r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];
+ Blowfish_encipher(c, &l, &r);
+ data[0] = l >> 24 & 0xff;
+ data[1] = l >> 16 & 0xff;
+ data[2] = l >> 8 & 0xff;
+ data[3] = l & 0xff;
+ data[4] = r >> 24 & 0xff;
+ data[5] = r >> 16 & 0xff;
+ data[6] = r >> 8 & 0xff;
+ data[7] = r & 0xff;
+ data += 8;
+ }
+}
+
+void
+blf_ecb_decrypt(blf_ctx *c, uint8_t *data, uint32_t len)
+{
+ uint32_t l, r;
+ uint32_t i;
+
+ for(i = 0; i < len; i += 8) {
+ l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
+ r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];
+ Blowfish_decipher(c, &l, &r);
+ data[0] = l >> 24 & 0xff;
+ data[1] = l >> 16 & 0xff;
+ data[2] = l >> 8 & 0xff;
+ data[3] = l & 0xff;
+ data[4] = r >> 24 & 0xff;
+ data[5] = r >> 16 & 0xff;
+ data[6] = r >> 8 & 0xff;
+ data[7] = r & 0xff;
+ data += 8;
+ }
+}
+
+void
+blf_cbc_encrypt(blf_ctx *c, uint8_t *iv, uint8_t *data, uint32_t len)
+{
+ uint32_t l, r;
+ uint32_t i, j;
+
+ for(i = 0; i < len; i += 8) {
+ for(j = 0; j < 8; j++)
+ data[j] ^= iv[j];
+ l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
+ r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];
+ Blowfish_encipher(c, &l, &r);
+ data[0] = l >> 24 & 0xff;
+ data[1] = l >> 16 & 0xff;
+ data[2] = l >> 8 & 0xff;
+ data[3] = l & 0xff;
+ data[4] = r >> 24 & 0xff;
+ data[5] = r >> 16 & 0xff;
+ data[6] = r >> 8 & 0xff;
+ data[7] = r & 0xff;
+ iv = data;
+ data += 8;
+ }
+}
+
+void
+blf_cbc_decrypt(blf_ctx *c, uint8_t *iva, uint8_t *data, uint32_t len)
+{
+ uint32_t l, r;
+ uint8_t *iv;
+ uint32_t i, j;
+
+ iv = data + len - 16;
+ data = data + len - 8;
+ for(i = len - 8; i >= 8; i -= 8) {
+ l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
+ r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];
+ Blowfish_decipher(c, &l, &r);
+ data[0] = l >> 24 & 0xff;
+ data[1] = l >> 16 & 0xff;
+ data[2] = l >> 8 & 0xff;
+ data[3] = l & 0xff;
+ data[4] = r >> 24 & 0xff;
+ data[5] = r >> 16 & 0xff;
+ data[6] = r >> 8 & 0xff;
+ data[7] = r & 0xff;
+ for(j = 0; j < 8; j++)
+ data[j] ^= iv[j];
+ iv -= 8;
+ data -= 8;
+ }
+ l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
+ r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7];
+ Blowfish_decipher(c, &l, &r);
+ data[0] = l >> 24 & 0xff;
+ data[1] = l >> 16 & 0xff;
+ data[2] = l >> 8 & 0xff;
+ data[3] = l & 0xff;
+ data[4] = r >> 24 & 0xff;
+ data[5] = r >> 16 & 0xff;
+ data[6] = r >> 8 & 0xff;
+ data[7] = r & 0xff;
+ for(j = 0; j < 8; j++)
+ data[j] ^= iva[j];
+}
+
+#if 0
+void
+report(uint32_t data[], uint16_t len)
+{
+ uint16_t i;
+ for(i = 0; i < len; i += 2)
+ printf("Block %0hd: %08lx %08lx.\n",
+ i / 2, data[i], data[i + 1]);
+}
+void
+main(void)
+{
+
+ blf_ctx c;
+ char key[] = "AAAAA";
+ char key2[] = "abcdefghijklmnopqrstuvwxyz";
+
+ uint32_t data[10];
+ uint32_t data2[] =
+ {0x424c4f57l, 0x46495348l};
+
+ uint16_t i;
+
+ /* First test */
+ for(i = 0; i < 10; i++)
+ data[i] = i;
+
+ blf_key(&c, (uint8_t *) key, 5);
+ blf_enc(&c, data, 5);
+ blf_dec(&c, data, 1);
+ blf_dec(&c, data + 2, 4);
+ printf("Should read as 0 - 9.\n");
+ report(data, 10);
+
+ /* Second test */
+ blf_key(&c, (uint8_t *) key2, strlen(key2));
+ blf_enc(&c, data2, 1);
+ printf("\nShould read as: 0x324ed0fe 0xf413a203.\n");
+ report(data2, 2);
+ blf_dec(&c, data2, 1);
+ report(data2, 2);
+}
+#endif
+
+#endif /* !defined(HAVE_BCRYPT_PBKDF) && \
+ (!defined(HAVE_BLOWFISH_INITSTATE) || \
+ !defined(HAVE_BLOWFISH_EXPAND0STATE) || \
+ '!defined(HAVE_BLF_ENC)) */
diff --git a/contrib/libs/libssh2/src/channel.c b/contrib/libs/libssh2/src/channel.c
new file mode 100644
index 00000000000..78ed40e8776
--- /dev/null
+++ b/contrib/libs/libssh2/src/channel.c
@@ -0,0 +1,2895 @@
+/* Copyright (c) 2004-2007 Sara Golemon <[email protected]>
+ * Copyright (c) 2005 Mikhail Gusarov <[email protected]>
+ * Copyright (c) 2008-2019 by Daniel Stenberg
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#include <assert.h>
+
+#include "channel.h"
+#include "transport.h"
+#include "packet.h"
+#include "session.h"
+
+/*
+ * _libssh2_channel_nextid
+ *
+ * Determine the next channel ID we can use at our end
+ */
+uint32_t
+_libssh2_channel_nextid(LIBSSH2_SESSION * session)
+{
+ uint32_t id = session->next_channel;
+ LIBSSH2_CHANNEL *channel;
+
+ channel = _libssh2_list_first(&session->channels);
+
+ while(channel) {
+ if(channel->local.id > id) {
+ id = channel->local.id;
+ }
+ channel = _libssh2_list_next(&channel->node);
+ }
+
+ /* This is a shortcut to avoid waiting for close packets on channels we've
+ * forgotten about, This *could* be a problem if we request and close 4
+ * billion or so channels in too rapid succession for the remote end to
+ * respond, but the worst case scenario is that some data meant for
+ * another channel Gets picked up by the new one.... Pretty unlikely all
+ * told...
+ */
+ session->next_channel = id + 1;
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Allocated new channel ID#%lu",
+ id);
+ return id;
+}
+
+/*
+ * _libssh2_channel_locate
+ *
+ * Locate a channel pointer by number
+ */
+LIBSSH2_CHANNEL *
+_libssh2_channel_locate(LIBSSH2_SESSION *session, uint32_t channel_id)
+{
+ LIBSSH2_CHANNEL *channel;
+ LIBSSH2_LISTENER *l;
+
+ for(channel = _libssh2_list_first(&session->channels);
+ channel;
+ channel = _libssh2_list_next(&channel->node)) {
+ if(channel->local.id == channel_id)
+ return channel;
+ }
+
+ /* We didn't find the channel in the session, let's then check its
+ listeners since each listener may have its own set of pending channels
+ */
+ for(l = _libssh2_list_first(&session->listeners); l;
+ l = _libssh2_list_next(&l->node)) {
+ for(channel = _libssh2_list_first(&l->queue);
+ channel;
+ channel = _libssh2_list_next(&channel->node)) {
+ if(channel->local.id == channel_id)
+ return channel;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * _libssh2_channel_open
+ *
+ * Establish a generic session channel
+ */
+LIBSSH2_CHANNEL *
+_libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type,
+ uint32_t channel_type_len,
+ uint32_t window_size,
+ uint32_t packet_size,
+ const unsigned char *message,
+ size_t message_len)
+{
+ static const unsigned char reply_codes[3] = {
+ SSH_MSG_CHANNEL_OPEN_CONFIRMATION,
+ SSH_MSG_CHANNEL_OPEN_FAILURE,
+ 0
+ };
+ unsigned char *s;
+ int rc;
+
+ if(session->open_state == libssh2_NB_state_idle) {
+ session->open_channel = NULL;
+ session->open_packet = NULL;
+ session->open_data = NULL;
+ /* 17 = packet_type(1) + channel_type_len(4) + sender_channel(4) +
+ * window_size(4) + packet_size(4) */
+ session->open_packet_len = channel_type_len + 17;
+ session->open_local_channel = _libssh2_channel_nextid(session);
+
+ /* Zero the whole thing out */
+ memset(&session->open_packet_requirev_state, 0,
+ sizeof(session->open_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Opening Channel - win %d pack %d", window_size,
+ packet_size);
+ session->open_channel =
+ LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL));
+ if(!session->open_channel) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate space for channel data");
+ return NULL;
+ }
+ session->open_channel->channel_type_len = channel_type_len;
+ session->open_channel->channel_type =
+ LIBSSH2_ALLOC(session, channel_type_len);
+ if(!session->open_channel->channel_type) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed allocating memory for channel type name");
+ LIBSSH2_FREE(session, session->open_channel);
+ session->open_channel = NULL;
+ return NULL;
+ }
+ memcpy(session->open_channel->channel_type, channel_type,
+ channel_type_len);
+
+ /* REMEMBER: local as in locally sourced */
+ session->open_channel->local.id = session->open_local_channel;
+ session->open_channel->remote.window_size = window_size;
+ session->open_channel->remote.window_size_initial = window_size;
+ session->open_channel->remote.packet_size = packet_size;
+ session->open_channel->session = session;
+
+ _libssh2_list_add(&session->channels,
+ &session->open_channel->node);
+
+ s = session->open_packet =
+ LIBSSH2_ALLOC(session, session->open_packet_len);
+ if(!session->open_packet) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate temporary space for packet");
+ goto channel_error;
+ }
+ *(s++) = SSH_MSG_CHANNEL_OPEN;
+ _libssh2_store_str(&s, channel_type, channel_type_len);
+ _libssh2_store_u32(&s, session->open_local_channel);
+ _libssh2_store_u32(&s, window_size);
+ _libssh2_store_u32(&s, packet_size);
+
+ /* Do not copy the message */
+
+ session->open_state = libssh2_NB_state_created;
+ }
+
+ if(session->open_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session,
+ session->open_packet,
+ session->open_packet_len,
+ message, message_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending channel-open request");
+ return NULL;
+ }
+ else if(rc) {
+ _libssh2_error(session, rc,
+ "Unable to send channel-open request");
+ goto channel_error;
+ }
+
+ session->open_state = libssh2_NB_state_sent;
+ }
+
+ if(session->open_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->open_data,
+ &session->open_data_len, 1,
+ session->open_packet + 5 +
+ channel_type_len, 4,
+ &session->open_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ return NULL;
+ }
+ else if(rc) {
+ _libssh2_error(session, rc, "Unexpected error");
+ goto channel_error;
+ }
+
+ if(session->open_data_len < 1) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected packet size");
+ goto channel_error;
+ }
+
+ if(session->open_data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
+
+ if(session->open_data_len < 17) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected packet size");
+ goto channel_error;
+ }
+
+ session->open_channel->remote.id =
+ _libssh2_ntohu32(session->open_data + 5);
+ session->open_channel->local.window_size =
+ _libssh2_ntohu32(session->open_data + 9);
+ session->open_channel->local.window_size_initial =
+ _libssh2_ntohu32(session->open_data + 9);
+ session->open_channel->local.packet_size =
+ _libssh2_ntohu32(session->open_data + 13);
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Connection Established - ID: %lu/%lu win: %lu/%lu"
+ " pack: %lu/%lu",
+ session->open_channel->local.id,
+ session->open_channel->remote.id,
+ session->open_channel->local.window_size,
+ session->open_channel->remote.window_size,
+ session->open_channel->local.packet_size,
+ session->open_channel->remote.packet_size);
+ LIBSSH2_FREE(session, session->open_packet);
+ session->open_packet = NULL;
+ LIBSSH2_FREE(session, session->open_data);
+ session->open_data = NULL;
+
+ session->open_state = libssh2_NB_state_idle;
+ return session->open_channel;
+ }
+
+ if(session->open_data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) {
+ unsigned int reason_code =
+ _libssh2_ntohu32(session->open_data + 5);
+ switch(reason_code) {
+ case SSH_OPEN_ADMINISTRATIVELY_PROHIBITED:
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Channel open failure "
+ "(administratively prohibited)");
+ break;
+ case SSH_OPEN_CONNECT_FAILED:
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Channel open failure (connect failed)");
+ break;
+ case SSH_OPEN_UNKNOWN_CHANNELTYPE:
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Channel open failure (unknown channel type)");
+ break;
+ case SSH_OPEN_RESOURCE_SHORTAGE:
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Channel open failure (resource shortage)");
+ break;
+ default:
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Channel open failure");
+ }
+ }
+ }
+
+ channel_error:
+
+ if(session->open_data) {
+ LIBSSH2_FREE(session, session->open_data);
+ session->open_data = NULL;
+ }
+ if(session->open_packet) {
+ LIBSSH2_FREE(session, session->open_packet);
+ session->open_packet = NULL;
+ }
+ if(session->open_channel) {
+ unsigned char channel_id[4];
+ LIBSSH2_FREE(session, session->open_channel->channel_type);
+
+ _libssh2_list_remove(&session->open_channel->node);
+
+ /* Clear out packets meant for this channel */
+ _libssh2_htonu32(channel_id, session->open_channel->local.id);
+ while((_libssh2_packet_ask(session, SSH_MSG_CHANNEL_DATA,
+ &session->open_data,
+ &session->open_data_len, 1,
+ channel_id, 4) >= 0)
+ ||
+ (_libssh2_packet_ask(session, SSH_MSG_CHANNEL_EXTENDED_DATA,
+ &session->open_data,
+ &session->open_data_len, 1,
+ channel_id, 4) >= 0)) {
+ LIBSSH2_FREE(session, session->open_data);
+ session->open_data = NULL;
+ }
+
+ LIBSSH2_FREE(session, session->open_channel);
+ session->open_channel = NULL;
+ }
+
+ session->open_state = libssh2_NB_state_idle;
+ return NULL;
+}
+
+/*
+ * libssh2_channel_open_ex
+ *
+ * Establish a generic session channel
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *type,
+ unsigned int type_len,
+ unsigned int window_size, unsigned int packet_size,
+ const char *msg, unsigned int msg_len)
+{
+ LIBSSH2_CHANNEL *ptr;
+
+ if(!session)
+ return NULL;
+
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ _libssh2_channel_open(session, type, type_len,
+ window_size, packet_size,
+ (unsigned char *)msg,
+ msg_len));
+ return ptr;
+}
+
+/*
+ * libssh2_channel_direct_tcpip_ex
+ *
+ * Tunnel TCP/IP connect through the SSH session to direct host/port
+ */
+static LIBSSH2_CHANNEL *
+channel_direct_tcpip(LIBSSH2_SESSION * session, const char *host,
+ int port, const char *shost, int sport)
+{
+ LIBSSH2_CHANNEL *channel;
+ unsigned char *s;
+
+ if(session->direct_state == libssh2_NB_state_idle) {
+ session->direct_host_len = strlen(host);
+ session->direct_shost_len = strlen(shost);
+ /* host_len(4) + port(4) + shost_len(4) + sport(4) */
+ session->direct_message_len =
+ session->direct_host_len + session->direct_shost_len + 16;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Requesting direct-tcpip session from %s:%d to %s:%d",
+ shost, sport, host, port);
+
+ s = session->direct_message =
+ LIBSSH2_ALLOC(session, session->direct_message_len);
+ if(!session->direct_message) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "direct-tcpip connection");
+ return NULL;
+ }
+ _libssh2_store_str(&s, host, session->direct_host_len);
+ _libssh2_store_u32(&s, port);
+ _libssh2_store_str(&s, shost, session->direct_shost_len);
+ _libssh2_store_u32(&s, sport);
+ }
+
+ channel =
+ _libssh2_channel_open(session, "direct-tcpip",
+ sizeof("direct-tcpip") - 1,
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT,
+ LIBSSH2_CHANNEL_PACKET_DEFAULT,
+ session->direct_message,
+ session->direct_message_len);
+
+ if(!channel &&
+ libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) {
+ /* The error code is still set to LIBSSH2_ERROR_EAGAIN, set our state
+ to created to avoid re-creating the package on next invoke */
+ session->direct_state = libssh2_NB_state_created;
+ return NULL;
+ }
+ /* by default we set (keep?) idle state... */
+ session->direct_state = libssh2_NB_state_idle;
+
+ LIBSSH2_FREE(session, session->direct_message);
+ session->direct_message = NULL;
+
+ return channel;
+}
+
+/*
+ * libssh2_channel_direct_tcpip_ex
+ *
+ * Tunnel TCP/IP connect through the SSH session to direct host/port
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, const char *host,
+ int port, const char *shost, int sport)
+{
+ LIBSSH2_CHANNEL *ptr;
+
+ if(!session)
+ return NULL;
+
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ channel_direct_tcpip(session, host, port,
+ shost, sport));
+ return ptr;
+}
+
+/*
+ * channel_forward_listen
+ *
+ * Bind a port on the remote host and listen for connections
+ */
+static LIBSSH2_LISTENER *
+channel_forward_listen(LIBSSH2_SESSION * session, const char *host,
+ int port, int *bound_port, int queue_maxsize)
+{
+ unsigned char *s;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_REQUEST_SUCCESS, SSH_MSG_REQUEST_FAILURE, 0 };
+ int rc;
+
+ if(!host)
+ host = "0.0.0.0";
+
+ if(session->fwdLstn_state == libssh2_NB_state_idle) {
+ session->fwdLstn_host_len = strlen(host);
+ /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4)
+ + port(4) */
+ session->fwdLstn_packet_len =
+ session->fwdLstn_host_len + (sizeof("tcpip-forward") - 1) + 14;
+
+ /* Zero the whole thing out */
+ memset(&session->fwdLstn_packet_requirev_state, 0,
+ sizeof(session->fwdLstn_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Requesting tcpip-forward session for %s:%d", host,
+ port);
+
+ s = session->fwdLstn_packet =
+ LIBSSH2_ALLOC(session, session->fwdLstn_packet_len);
+ if(!session->fwdLstn_packet) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for setenv packet");
+ return NULL;
+ }
+
+ *(s++) = SSH_MSG_GLOBAL_REQUEST;
+ _libssh2_store_str(&s, "tcpip-forward", sizeof("tcpip-forward") - 1);
+ *(s++) = 0x01; /* want_reply */
+
+ _libssh2_store_str(&s, host, session->fwdLstn_host_len);
+ _libssh2_store_u32(&s, port);
+
+ session->fwdLstn_state = libssh2_NB_state_created;
+ }
+
+ if(session->fwdLstn_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session,
+ session->fwdLstn_packet,
+ session->fwdLstn_packet_len,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending global-request packet for "
+ "forward listen request");
+ return NULL;
+ }
+ else if(rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send global-request packet for forward "
+ "listen request");
+ LIBSSH2_FREE(session, session->fwdLstn_packet);
+ session->fwdLstn_packet = NULL;
+ session->fwdLstn_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+ LIBSSH2_FREE(session, session->fwdLstn_packet);
+ session->fwdLstn_packet = NULL;
+
+ session->fwdLstn_state = libssh2_NB_state_sent;
+ }
+
+ if(session->fwdLstn_state == libssh2_NB_state_sent) {
+ unsigned char *data;
+ size_t data_len;
+ rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len,
+ 0, NULL, 0,
+ &session->fwdLstn_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block");
+ return NULL;
+ }
+ else if(rc || (data_len < 1)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO, "Unknown");
+ session->fwdLstn_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ if(data[0] == SSH_MSG_REQUEST_SUCCESS) {
+ LIBSSH2_LISTENER *listener;
+
+ listener = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_LISTENER));
+ if(!listener)
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for listener queue");
+ else {
+ listener->host =
+ LIBSSH2_ALLOC(session, session->fwdLstn_host_len + 1);
+ if(!listener->host) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory "
+ "for listener queue");
+ LIBSSH2_FREE(session, listener);
+ listener = NULL;
+ }
+ else {
+ listener->session = session;
+ memcpy(listener->host, host, session->fwdLstn_host_len);
+ listener->host[session->fwdLstn_host_len] = 0;
+ if(data_len >= 5 && !port) {
+ listener->port = _libssh2_ntohu32(data + 1);
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Dynamic tcpip-forward port "
+ "allocated: %d",
+ listener->port);
+ }
+ else
+ listener->port = port;
+
+ listener->queue_size = 0;
+ listener->queue_maxsize = queue_maxsize;
+
+ /* append this to the parent's list of listeners */
+ _libssh2_list_add(&session->listeners, &listener->node);
+
+ if(bound_port) {
+ *bound_port = listener->port;
+ }
+ }
+ }
+
+ LIBSSH2_FREE(session, data);
+ session->fwdLstn_state = libssh2_NB_state_idle;
+ return listener;
+ }
+ else if(data[0] == SSH_MSG_REQUEST_FAILURE) {
+ LIBSSH2_FREE(session, data);
+ _libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED,
+ "Unable to complete request for forward-listen");
+ session->fwdLstn_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+ }
+
+ session->fwdLstn_state = libssh2_NB_state_idle;
+
+ return NULL;
+}
+
+/*
+ * libssh2_channel_forward_listen_ex
+ *
+ * Bind a port on the remote host and listen for connections
+ */
+LIBSSH2_API LIBSSH2_LISTENER *
+libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, const char *host,
+ int port, int *bound_port, int queue_maxsize)
+{
+ LIBSSH2_LISTENER *ptr;
+
+ if(!session)
+ return NULL;
+
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ channel_forward_listen(session, host, port, bound_port,
+ queue_maxsize));
+ return ptr;
+}
+
+/*
+ * _libssh2_channel_forward_cancel
+ *
+ * Stop listening on a remote port and free the listener
+ * Toss out any pending (un-accept()ed) connections
+ *
+ * Return 0 on success, LIBSSH2_ERROR_EAGAIN if would block, -1 on error
+ */
+int _libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
+{
+ LIBSSH2_SESSION *session = listener->session;
+ LIBSSH2_CHANNEL *queued;
+ unsigned char *packet, *s;
+ size_t host_len = strlen(listener->host);
+ /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4) +
+ port(4) */
+ size_t packet_len =
+ host_len + 14 + sizeof("cancel-tcpip-forward") - 1;
+ int rc;
+ int retcode = 0;
+
+ if(listener->chanFwdCncl_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Cancelling tcpip-forward session for %s:%d",
+ listener->host, listener->port);
+
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!packet) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for setenv packet");
+ return LIBSSH2_ERROR_ALLOC;
+ }
+
+ *(s++) = SSH_MSG_GLOBAL_REQUEST;
+ _libssh2_store_str(&s, "cancel-tcpip-forward",
+ sizeof("cancel-tcpip-forward") - 1);
+ *(s++) = 0x00; /* want_reply */
+
+ _libssh2_store_str(&s, listener->host, host_len);
+ _libssh2_store_u32(&s, listener->port);
+
+ listener->chanFwdCncl_state = libssh2_NB_state_created;
+ }
+ else {
+ packet = listener->chanFwdCncl_data;
+ }
+
+ if(listener->chanFwdCncl_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, packet, packet_len, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending forward request");
+ listener->chanFwdCncl_data = packet;
+ return rc;
+ }
+ else if(rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send global-request packet for forward "
+ "listen request");
+ /* set the state to something we don't check for, for the
+ unfortunate situation where we get an EAGAIN further down
+ when trying to bail out due to errors! */
+ listener->chanFwdCncl_state = libssh2_NB_state_sent;
+ retcode = LIBSSH2_ERROR_SOCKET_SEND;
+ }
+ LIBSSH2_FREE(session, packet);
+
+ listener->chanFwdCncl_state = libssh2_NB_state_sent;
+ }
+
+ queued = _libssh2_list_first(&listener->queue);
+ while(queued) {
+ LIBSSH2_CHANNEL *next = _libssh2_list_next(&queued->node);
+
+ rc = _libssh2_channel_free(queued);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ queued = next;
+ }
+ LIBSSH2_FREE(session, listener->host);
+
+ /* remove this entry from the parent's list of listeners */
+ _libssh2_list_remove(&listener->node);
+
+ LIBSSH2_FREE(session, listener);
+
+ return retcode;
+}
+
+/*
+ * libssh2_channel_forward_cancel
+ *
+ * Stop listening on a remote port and free the listener
+ * Toss out any pending (un-accept()ed) connections
+ *
+ * Return 0 on success, LIBSSH2_ERROR_EAGAIN if would block, -1 on error
+ */
+LIBSSH2_API int
+libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
+{
+ int rc;
+
+ if(!listener)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, listener->session,
+ _libssh2_channel_forward_cancel(listener));
+ return rc;
+}
+
+/*
+ * channel_forward_accept
+ *
+ * Accept a connection
+ */
+static LIBSSH2_CHANNEL *
+channel_forward_accept(LIBSSH2_LISTENER *listener)
+{
+ int rc;
+
+ do {
+ rc = _libssh2_transport_read(listener->session);
+ } while(rc > 0);
+
+ if(_libssh2_list_first(&listener->queue)) {
+ LIBSSH2_CHANNEL *channel = _libssh2_list_first(&listener->queue);
+
+ /* detach channel from listener's queue */
+ _libssh2_list_remove(&channel->node);
+
+ listener->queue_size--;
+
+ /* add channel to session's channel list */
+ _libssh2_list_add(&channel->session->channels, &channel->node);
+
+ return channel;
+ }
+
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(listener->session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for packet");
+ }
+ else
+ _libssh2_error(listener->session, LIBSSH2_ERROR_CHANNEL_UNKNOWN,
+ "Channel not found");
+ return NULL;
+}
+
+/*
+ * libssh2_channel_forward_accept
+ *
+ * Accept a connection
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener)
+{
+ LIBSSH2_CHANNEL *ptr;
+
+ if(!listener)
+ return NULL;
+
+ BLOCK_ADJUST_ERRNO(ptr, listener->session,
+ channel_forward_accept(listener));
+ return ptr;
+
+}
+
+/*
+ * channel_setenv
+ *
+ * Set an environment variable prior to requesting a shell/program/subsystem
+ */
+static int channel_setenv(LIBSSH2_CHANNEL *channel,
+ const char *varname, unsigned int varname_len,
+ const char *value, unsigned int value_len)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s, *data;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 };
+ size_t data_len;
+ int rc;
+
+ if(channel->setenv_state == libssh2_NB_state_idle) {
+ /* 21 = packet_type(1) + channel_id(4) + request_len(4) +
+ * request(3)"env" + want_reply(1) + varname_len(4) + value_len(4) */
+ channel->setenv_packet_len = varname_len + value_len + 21;
+
+ /* Zero the whole thing out */
+ memset(&channel->setenv_packet_requirev_state, 0,
+ sizeof(channel->setenv_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Setting remote environment variable: %s=%s on "
+ "channel %lu/%lu",
+ varname, value, channel->local.id, channel->remote.id);
+
+ s = channel->setenv_packet =
+ LIBSSH2_ALLOC(session, channel->setenv_packet_len);
+ if(!channel->setenv_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory "
+ "for setenv packet");
+ }
+
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, "env", sizeof("env") - 1);
+ *(s++) = 0x01;
+ _libssh2_store_str(&s, varname, varname_len);
+ _libssh2_store_str(&s, value, value_len);
+
+ channel->setenv_state = libssh2_NB_state_created;
+ }
+
+ if(channel->setenv_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session,
+ channel->setenv_packet,
+ channel->setenv_packet_len,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending setenv request");
+ return rc;
+ }
+ else if(rc) {
+ LIBSSH2_FREE(session, channel->setenv_packet);
+ channel->setenv_packet = NULL;
+ channel->setenv_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send channel-request packet for "
+ "setenv request");
+ }
+ LIBSSH2_FREE(session, channel->setenv_packet);
+ channel->setenv_packet = NULL;
+
+ _libssh2_htonu32(channel->setenv_local_channel, channel->local.id);
+
+ channel->setenv_state = libssh2_NB_state_sent;
+ }
+
+ if(channel->setenv_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len,
+ 1, channel->setenv_local_channel, 4,
+ &channel->
+ setenv_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ if(rc) {
+ channel->setenv_state = libssh2_NB_state_idle;
+ return rc;
+ }
+ else if(data_len < 1) {
+ channel->setenv_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected packet size");
+ }
+
+ if(data[0] == SSH_MSG_CHANNEL_SUCCESS) {
+ LIBSSH2_FREE(session, data);
+ channel->setenv_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ LIBSSH2_FREE(session, data);
+ }
+
+ channel->setenv_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED,
+ "Unable to complete request for channel-setenv");
+}
+
+/*
+ * libssh2_channel_setenv_ex
+ *
+ * Set an environment variable prior to requesting a shell/program/subsystem
+ */
+LIBSSH2_API int
+libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel,
+ const char *varname, unsigned int varname_len,
+ const char *value, unsigned int value_len)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ channel_setenv(channel, varname, varname_len,
+ value, value_len));
+ return rc;
+}
+
+/*
+ * channel_request_pty
+ * Duh... Request a PTY
+ */
+static int channel_request_pty(LIBSSH2_CHANNEL *channel,
+ const char *term, unsigned int term_len,
+ const char *modes, unsigned int modes_len,
+ int width, int height,
+ int width_px, int height_px)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 };
+ int rc;
+
+ if(channel->reqPTY_state == libssh2_NB_state_idle) {
+ /* 41 = packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) +
+ * want_reply(1) + term_len(4) + width(4) + height(4) + width_px(4) +
+ * height_px(4) + modes_len(4) */
+ if(term_len + modes_len > 256) {
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "term + mode lengths too large");
+ }
+
+ channel->reqPTY_packet_len = term_len + modes_len + 41;
+
+ /* Zero the whole thing out */
+ memset(&channel->reqPTY_packet_requirev_state, 0,
+ sizeof(channel->reqPTY_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Allocating tty on channel %lu/%lu", channel->local.id,
+ channel->remote.id);
+
+ s = channel->reqPTY_packet;
+
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, (char *)"pty-req", sizeof("pty-req") - 1);
+
+ *(s++) = 0x01;
+
+ _libssh2_store_str(&s, term, term_len);
+ _libssh2_store_u32(&s, width);
+ _libssh2_store_u32(&s, height);
+ _libssh2_store_u32(&s, width_px);
+ _libssh2_store_u32(&s, height_px);
+ _libssh2_store_str(&s, modes, modes_len);
+
+ channel->reqPTY_state = libssh2_NB_state_created;
+ }
+
+ if(channel->reqPTY_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, channel->reqPTY_packet,
+ channel->reqPTY_packet_len,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending pty request");
+ return rc;
+ }
+ else if(rc) {
+ channel->reqPTY_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send pty-request packet");
+ }
+ _libssh2_htonu32(channel->reqPTY_local_channel, channel->local.id);
+
+ channel->reqPTY_state = libssh2_NB_state_sent;
+ }
+
+ if(channel->reqPTY_state == libssh2_NB_state_sent) {
+ unsigned char *data;
+ size_t data_len;
+ unsigned char code;
+ rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len,
+ 1, channel->reqPTY_local_channel, 4,
+ &channel->reqPTY_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc || data_len < 1) {
+ channel->reqPTY_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Failed to require the PTY package");
+ }
+
+ code = data[0];
+
+ LIBSSH2_FREE(session, data);
+ channel->reqPTY_state = libssh2_NB_state_idle;
+
+ if(code == SSH_MSG_CHANNEL_SUCCESS)
+ return 0;
+ }
+
+ return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED,
+ "Unable to complete request for "
+ "channel request-pty");
+}
+
+/**
+ * channel_request_auth_agent
+ * The actual re-entrant method which requests an auth agent.
+ * */
+static int channel_request_auth_agent(LIBSSH2_CHANNEL *channel,
+ const char *request_str,
+ int request_str_len)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 };
+ int rc;
+
+ if(channel->req_auth_agent_state == libssh2_NB_state_idle) {
+ /* Only valid options are "auth-agent-req" and
+ * "auth-agent-req_at_openssh.com" so we make sure it is not
+ * actually longer than the longest possible. */
+ if(request_str_len > 26) {
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "request_str length too large");
+ }
+
+ /*
+ * Length: 24 or 36 = packet_type(1) + channel(4) + req_len(4) +
+ * request_str (variable) + want_reply (1) */
+ channel->req_auth_agent_packet_len = 10 + request_str_len;
+
+ /* Zero out the requireev state to reset */
+ memset(&channel->req_auth_agent_requirev_state, 0,
+ sizeof(channel->req_auth_agent_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Requesting auth agent on channel %lu/%lu",
+ channel->local.id, channel->remote.id);
+
+ /*
+ * byte SSH_MSG_CHANNEL_REQUEST
+ * uint32 recipient channel
+ * string "auth-agent-req"
+ * boolean want reply
+ * */
+ s = channel->req_auth_agent_packet;
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, (char *)request_str, request_str_len);
+ *(s++) = 0x01;
+
+ channel->req_auth_agent_state = libssh2_NB_state_created;
+ }
+
+ if(channel->req_auth_agent_state == libssh2_NB_state_created) {
+ /* Send the packet, we can use sizeof() on the packet because it
+ * is always completely filled; there are no variable length fields. */
+ rc = _libssh2_transport_send(session, channel->req_auth_agent_packet,
+ channel->req_auth_agent_packet_len,
+ NULL, 0);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending auth-agent request");
+ }
+ else if(rc) {
+ channel->req_auth_agent_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send auth-agent request");
+ }
+ _libssh2_htonu32(channel->req_auth_agent_local_channel,
+ channel->local.id);
+ channel->req_auth_agent_state = libssh2_NB_state_sent;
+ }
+
+ if(channel->req_auth_agent_state == libssh2_NB_state_sent) {
+ unsigned char *data;
+ size_t data_len;
+ unsigned char code;
+
+ rc = _libssh2_packet_requirev(
+ session, reply_codes, &data, &data_len, 1,
+ channel->req_auth_agent_local_channel,
+ 4, &channel->req_auth_agent_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ channel->req_auth_agent_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Failed to request auth-agent");
+ }
+
+ code = data[0];
+
+ LIBSSH2_FREE(session, data);
+ channel->req_auth_agent_state = libssh2_NB_state_idle;
+
+ if(code == SSH_MSG_CHANNEL_SUCCESS)
+ return 0;
+ }
+
+ return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED,
+ "Unable to complete request for auth-agent");
+}
+
+/**
+ * libssh2_channel_request_auth_agent
+ * Requests that agent forwarding be enabled for the session. The
+ * request must be sent over a specific channel, which starts the agent
+ * listener on the remote side. Once the channel is closed, the agent
+ * listener continues to exist.
+ * */
+LIBSSH2_API int
+libssh2_channel_request_auth_agent(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ /* The current RFC draft for agent forwarding says you're supposed to
+ * send "auth-agent-req," but most SSH servers out there right now
+ * actually expect "[email protected]", so we try that
+ * first. */
+ if(channel->req_auth_agent_try_state == libssh2_NB_state_idle) {
+ BLOCK_ADJUST(rc, channel->session,
+ channel_request_auth_agent(channel,
+ 26));
+
+ /* If we failed (but not with EAGAIN), then we move onto
+ * the next step to try another request type. */
+ if(rc != 0 && rc != LIBSSH2_ERROR_EAGAIN)
+ channel->req_auth_agent_try_state = libssh2_NB_state_sent;
+ }
+
+ if(channel->req_auth_agent_try_state == libssh2_NB_state_sent) {
+ BLOCK_ADJUST(rc, channel->session,
+ channel_request_auth_agent(channel,
+ "auth-agent-req", 14));
+
+ /* If we failed without an EAGAIN, then move on with this
+ * state machine. */
+ if(rc != 0 && rc != LIBSSH2_ERROR_EAGAIN)
+ channel->req_auth_agent_try_state = libssh2_NB_state_sent1;
+ }
+
+ /* If things are good, reset the try state. */
+ if(rc == 0)
+ channel->req_auth_agent_try_state = libssh2_NB_state_idle;
+
+ return rc;
+}
+
+/*
+ * libssh2_channel_request_pty_ex
+ * Duh... Request a PTY
+ */
+LIBSSH2_API int
+libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, const char *term,
+ unsigned int term_len, const char *modes,
+ unsigned int modes_len, int width, int height,
+ int width_px, int height_px)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ channel_request_pty(channel, term, term_len, modes,
+ modes_len, width, height,
+ width_px, height_px));
+ return rc;
+}
+
+static int
+channel_request_pty_size(LIBSSH2_CHANNEL * channel, int width,
+ int height, int width_px, int height_px)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s;
+ int rc;
+ int retcode = LIBSSH2_ERROR_PROTO;
+
+ if(channel->reqPTY_state == libssh2_NB_state_idle) {
+ channel->reqPTY_packet_len = 39;
+
+ /* Zero the whole thing out */
+ memset(&channel->reqPTY_packet_requirev_state, 0,
+ sizeof(channel->reqPTY_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "changing tty size on channel %lu/%lu",
+ channel->local.id,
+ channel->remote.id);
+
+ s = channel->reqPTY_packet;
+
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, (char *)"window-change",
+ sizeof("window-change") - 1);
+ *(s++) = 0x00; /* Don't reply */
+ _libssh2_store_u32(&s, width);
+ _libssh2_store_u32(&s, height);
+ _libssh2_store_u32(&s, width_px);
+ _libssh2_store_u32(&s, height_px);
+
+ channel->reqPTY_state = libssh2_NB_state_created;
+ }
+
+ if(channel->reqPTY_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, channel->reqPTY_packet,
+ channel->reqPTY_packet_len,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending window-change request");
+ return rc;
+ }
+ else if(rc) {
+ channel->reqPTY_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send window-change packet");
+ }
+ _libssh2_htonu32(channel->reqPTY_local_channel, channel->local.id);
+ retcode = LIBSSH2_ERROR_NONE;
+ }
+
+ channel->reqPTY_state = libssh2_NB_state_idle;
+ return retcode;
+}
+
+LIBSSH2_API int
+libssh2_channel_request_pty_size_ex(LIBSSH2_CHANNEL *channel, int width,
+ int height, int width_px, int height_px)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ channel_request_pty_size(channel, width, height, width_px,
+ height_px));
+ return rc;
+}
+
+/* Keep this an even number */
+#define LIBSSH2_X11_RANDOM_COOKIE_LEN 32
+
+/*
+ * channel_x11_req
+ * Request X11 forwarding
+ */
+static int
+channel_x11_req(LIBSSH2_CHANNEL *channel, int single_connection,
+ const char *auth_proto, const char *auth_cookie,
+ int screen_number)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 };
+ size_t proto_len =
+ auth_proto ? strlen(auth_proto) : (sizeof("MIT-MAGIC-COOKIE-1") - 1);
+ size_t cookie_len =
+ auth_cookie ? strlen(auth_cookie) : LIBSSH2_X11_RANDOM_COOKIE_LEN;
+ int rc;
+
+ if(channel->reqX11_state == libssh2_NB_state_idle) {
+ /* 30 = packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) +
+ * want_reply(1) + single_cnx(1) + proto_len(4) + cookie_len(4) +
+ * screen_num(4) */
+ channel->reqX11_packet_len = proto_len + cookie_len + 30;
+
+ /* Zero the whole thing out */
+ memset(&channel->reqX11_packet_requirev_state, 0,
+ sizeof(channel->reqX11_packet_requirev_state));
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Requesting x11-req for channel %lu/%lu: single=%d "
+ "proto=%s cookie=%s screen=%d",
+ channel->local.id, channel->remote.id,
+ single_connection,
+ auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1",
+ auth_cookie ? auth_cookie : "<random>", screen_number);
+
+ s = channel->reqX11_packet =
+ LIBSSH2_ALLOC(session, channel->reqX11_packet_len);
+ if(!channel->reqX11_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for pty-request");
+ }
+
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, "x11-req", sizeof("x11-req") - 1);
+
+ *(s++) = 0x01; /* want_reply */
+ *(s++) = single_connection ? 0x01 : 0x00;
+
+ _libssh2_store_str(&s, auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1",
+ proto_len);
+
+ _libssh2_store_u32(&s, cookie_len);
+ if(auth_cookie) {
+ memcpy(s, auth_cookie, cookie_len);
+ }
+ else {
+ int i;
+ /* note: the extra +1 below is necessary since the sprintf()
+ loop will always write 3 bytes so the last one will write
+ the trailing zero at the LIBSSH2_X11_RANDOM_COOKIE_LEN/2
+ border */
+ unsigned char buffer[(LIBSSH2_X11_RANDOM_COOKIE_LEN / 2) + 1];
+
+ if(_libssh2_random(buffer, LIBSSH2_X11_RANDOM_COOKIE_LEN / 2)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_RANDGEN,
+ "Unable to get random bytes "
+ "for x11-req cookie");
+ }
+ for(i = 0; i < (LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); i++) {
+ snprintf((char *)&s[i*2], 3, "%02X", buffer[i]);
+ }
+ }
+ s += cookie_len;
+
+ _libssh2_store_u32(&s, screen_number);
+ channel->reqX11_state = libssh2_NB_state_created;
+ }
+
+ if(channel->reqX11_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, channel->reqX11_packet,
+ channel->reqX11_packet_len,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending X11-req packet");
+ return rc;
+ }
+ if(rc) {
+ LIBSSH2_FREE(session, channel->reqX11_packet);
+ channel->reqX11_packet = NULL;
+ channel->reqX11_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send x11-req packet");
+ }
+ LIBSSH2_FREE(session, channel->reqX11_packet);
+ channel->reqX11_packet = NULL;
+
+ _libssh2_htonu32(channel->reqX11_local_channel, channel->local.id);
+
+ channel->reqX11_state = libssh2_NB_state_sent;
+ }
+
+ if(channel->reqX11_state == libssh2_NB_state_sent) {
+ size_t data_len;
+ unsigned char *data;
+ unsigned char code;
+
+ rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len,
+ 1, channel->reqX11_local_channel, 4,
+ &channel->reqX11_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc || data_len < 1) {
+ channel->reqX11_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "waiting for x11-req response packet");
+ }
+
+ code = data[0];
+ LIBSSH2_FREE(session, data);
+ channel->reqX11_state = libssh2_NB_state_idle;
+
+ if(code == SSH_MSG_CHANNEL_SUCCESS)
+ return 0;
+ }
+
+ return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED,
+ "Unable to complete request for channel x11-req");
+}
+
+/*
+ * libssh2_channel_x11_req_ex
+ * Request X11 forwarding
+ */
+LIBSSH2_API int
+libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection,
+ const char *auth_proto, const char *auth_cookie,
+ int screen_number)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ channel_x11_req(channel, single_connection, auth_proto,
+ auth_cookie, screen_number));
+ return rc;
+}
+
+
+/*
+ * _libssh2_channel_process_startup
+ *
+ * Primitive for libssh2_channel_(shell|exec|subsystem)
+ */
+int
+_libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel,
+ const char *request, size_t request_len,
+ const char *message, size_t message_len)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *s;
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 };
+ int rc;
+
+ if(channel->process_state == libssh2_NB_state_end) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+ "Channel can not be reused");
+ }
+
+ if(channel->process_state == libssh2_NB_state_idle) {
+ /* 10 = packet_type(1) + channel(4) + request_len(4) + want_reply(1) */
+ channel->process_packet_len = request_len + 10;
+
+ /* Zero the whole thing out */
+ memset(&channel->process_packet_requirev_state, 0,
+ sizeof(channel->process_packet_requirev_state));
+
+ if(message)
+ channel->process_packet_len += + 4;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "starting request(%s) on channel %lu/%lu, message=%s",
+ request, channel->local.id, channel->remote.id,
+ message ? message : "<null>");
+ s = channel->process_packet =
+ LIBSSH2_ALLOC(session, channel->process_packet_len);
+ if(!channel->process_packet)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory "
+ "for channel-process request");
+
+ *(s++) = SSH_MSG_CHANNEL_REQUEST;
+ _libssh2_store_u32(&s, channel->remote.id);
+ _libssh2_store_str(&s, request, request_len);
+ *(s++) = 0x01;
+
+ if(message)
+ _libssh2_store_u32(&s, message_len);
+
+ channel->process_state = libssh2_NB_state_created;
+ }
+
+ if(channel->process_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session,
+ channel->process_packet,
+ channel->process_packet_len,
+ (unsigned char *)message, message_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending channel request");
+ return rc;
+ }
+ else if(rc) {
+ LIBSSH2_FREE(session, channel->process_packet);
+ channel->process_packet = NULL;
+ channel->process_state = libssh2_NB_state_end;
+ return _libssh2_error(session, rc,
+ "Unable to send channel request");
+ }
+ LIBSSH2_FREE(session, channel->process_packet);
+ channel->process_packet = NULL;
+
+ _libssh2_htonu32(channel->process_local_channel, channel->local.id);
+
+ channel->process_state = libssh2_NB_state_sent;
+ }
+
+ if(channel->process_state == libssh2_NB_state_sent) {
+ unsigned char *data;
+ size_t data_len;
+ unsigned char code;
+ rc = _libssh2_packet_requirev(session, reply_codes, &data, &data_len,
+ 1, channel->process_local_channel, 4,
+ &channel->process_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc || data_len < 1) {
+ channel->process_state = libssh2_NB_state_end;
+ return _libssh2_error(session, rc,
+ "Failed waiting for channel success");
+ }
+
+ code = data[0];
+ LIBSSH2_FREE(session, data);
+ channel->process_state = libssh2_NB_state_end;
+
+ if(code == SSH_MSG_CHANNEL_SUCCESS)
+ return 0;
+ }
+
+ return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED,
+ "Unable to complete request for "
+ "channel-process-startup");
+}
+
+/*
+ * libssh2_channel_process_startup
+ *
+ * Primitive for libssh2_channel_(shell|exec|subsystem)
+ */
+LIBSSH2_API int
+libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel,
+ const char *req, unsigned int req_len,
+ const char *msg, unsigned int msg_len)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_process_startup(channel, req, req_len,
+ msg, msg_len));
+ return rc;
+}
+
+
+/*
+ * libssh2_channel_set_blocking
+ *
+ * Set a channel's BEHAVIOR blocking on or off. The socket will remain non-
+ * blocking.
+ */
+LIBSSH2_API void
+libssh2_channel_set_blocking(LIBSSH2_CHANNEL * channel, int blocking)
+{
+ if(channel)
+ (void) _libssh2_session_set_blocking(channel->session, blocking);
+}
+
+/*
+ * _libssh2_channel_flush
+ *
+ * Flush data from one (or all) stream
+ * Returns number of bytes flushed, or negative on failure
+ */
+int
+_libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid)
+{
+ if(channel->flush_state == libssh2_NB_state_idle) {
+ LIBSSH2_PACKET *packet =
+ _libssh2_list_first(&channel->session->packets);
+ channel->flush_refund_bytes = 0;
+ channel->flush_flush_bytes = 0;
+
+ while(packet) {
+ unsigned char packet_type;
+ LIBSSH2_PACKET *next = _libssh2_list_next(&packet->node);
+
+ if(packet->data_len < 1) {
+ packet = next;
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_ERROR,
+ "Unexpected packet length");
+ continue;
+ }
+
+ packet_type = packet->data[0];
+
+ if(((packet_type == SSH_MSG_CHANNEL_DATA)
+ || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA))
+ && ((packet->data_len >= 5)
+ && (_libssh2_ntohu32(packet->data + 1)
+ == channel->local.id))) {
+ /* It's our channel at least */
+ int packet_stream_id;
+
+ if(packet_type == SSH_MSG_CHANNEL_DATA) {
+ packet_stream_id = 0;
+ }
+ else if(packet->data_len >= 9) {
+ packet_stream_id = _libssh2_ntohu32(packet->data + 5);
+ }
+ else {
+ channel->flush_state = libssh2_NB_state_idle;
+ return _libssh2_error(channel->session,
+ LIBSSH2_ERROR_PROTO,
+ "Unexpected packet length");
+ }
+
+ if((streamid == LIBSSH2_CHANNEL_FLUSH_ALL)
+ || ((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ && ((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA)
+ || (streamid == packet_stream_id)))
+ || ((packet_type == SSH_MSG_CHANNEL_DATA)
+ && (streamid == 0))) {
+ size_t bytes_to_flush = packet->data_len -
+ packet->data_head;
+
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN,
+ "Flushing %d bytes of data from stream "
+ "%lu on channel %lu/%lu",
+ bytes_to_flush, packet_stream_id,
+ channel->local.id, channel->remote.id);
+
+ /* It's one of the streams we wanted to flush */
+ channel->flush_refund_bytes += packet->data_len - 13;
+ channel->flush_flush_bytes += bytes_to_flush;
+
+ LIBSSH2_FREE(channel->session, packet->data);
+
+ /* remove this packet from the parent's list */
+ _libssh2_list_remove(&packet->node);
+ LIBSSH2_FREE(channel->session, packet);
+ }
+ }
+ packet = next;
+ }
+
+ channel->flush_state = libssh2_NB_state_created;
+ }
+
+ channel->read_avail -= channel->flush_flush_bytes;
+ channel->remote.window_size -= channel->flush_flush_bytes;
+
+ if(channel->flush_refund_bytes) {
+ int rc =
+ _libssh2_channel_receive_window_adjust(channel,
+ channel->flush_refund_bytes,
+ 1, NULL);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ channel->flush_state = libssh2_NB_state_idle;
+
+ return channel->flush_flush_bytes;
+}
+
+/*
+ * libssh2_channel_flush_ex
+ *
+ * Flush data from one (or all) stream
+ * Returns number of bytes flushed, or negative on failure
+ */
+LIBSSH2_API int
+libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int stream)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_flush(channel, stream));
+ return rc;
+}
+
+/*
+ * libssh2_channel_get_exit_status
+ *
+ * Return the channel's program exit status. Note that the actual protocol
+ * provides the full 32bit this function returns. We cannot abuse it to
+ * return error values in case of errors so we return a zero if channel is
+ * NULL.
+ */
+LIBSSH2_API int
+libssh2_channel_get_exit_status(LIBSSH2_CHANNEL *channel)
+{
+ if(!channel)
+ return 0;
+
+ return channel->exit_status;
+}
+
+/*
+ * libssh2_channel_get_exit_signal
+ *
+ * Get exit signal (without leading "SIG"), error message, and language
+ * tag into newly allocated buffers of indicated length. Caller can
+ * use NULL pointers to indicate that the value should not be set. The
+ * *_len variables are set if they are non-NULL even if the
+ * corresponding string parameter is NULL. Returns LIBSSH2_ERROR_NONE
+ * on success, or an API error code.
+ */
+LIBSSH2_API int
+libssh2_channel_get_exit_signal(LIBSSH2_CHANNEL *channel,
+ char **exitsignal,
+ size_t *exitsignal_len,
+ char **errmsg,
+ size_t *errmsg_len,
+ char **langtag,
+ size_t *langtag_len)
+{
+ size_t namelen = 0;
+
+ if(channel) {
+ LIBSSH2_SESSION *session = channel->session;
+
+ if(channel->exit_signal) {
+ namelen = strlen(channel->exit_signal);
+ if(exitsignal) {
+ *exitsignal = LIBSSH2_ALLOC(session, namelen + 1);
+ if(!*exitsignal) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for signal name");
+ }
+ memcpy(*exitsignal, channel->exit_signal, namelen);
+ (*exitsignal)[namelen] = '\0';
+ }
+ if(exitsignal_len)
+ *exitsignal_len = namelen;
+ }
+ else {
+ if(exitsignal)
+ *exitsignal = NULL;
+ if(exitsignal_len)
+ *exitsignal_len = 0;
+ }
+
+ /* TODO: set error message and language tag */
+
+ if(errmsg)
+ *errmsg = NULL;
+
+ if(errmsg_len)
+ *errmsg_len = 0;
+
+ if(langtag)
+ *langtag = NULL;
+
+ if(langtag_len)
+ *langtag_len = 0;
+ }
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * _libssh2_channel_receive_window_adjust
+ *
+ * Adjust the receive window for a channel by adjustment bytes. If the amount
+ * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the
+ * adjustment amount will be queued for a later packet.
+ *
+ * Calls _libssh2_error() !
+ */
+int
+_libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel,
+ uint32_t adjustment,
+ unsigned char force,
+ unsigned int *store)
+{
+ int rc;
+
+ if(store)
+ *store = channel->remote.window_size;
+
+ if(channel->adjust_state == libssh2_NB_state_idle) {
+ if(!force
+ && (adjustment + channel->adjust_queue <
+ LIBSSH2_CHANNEL_MINADJUST)) {
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN,
+ "Queueing %lu bytes for receive window adjustment "
+ "for channel %lu/%lu",
+ adjustment, channel->local.id, channel->remote.id);
+ channel->adjust_queue += adjustment;
+ return 0;
+ }
+
+ if(!adjustment && !channel->adjust_queue) {
+ return 0;
+ }
+
+ adjustment += channel->adjust_queue;
+ channel->adjust_queue = 0;
+
+ /* Adjust the window based on the block we just freed */
+ channel->adjust_adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST;
+ _libssh2_htonu32(&channel->adjust_adjust[1], channel->remote.id);
+ _libssh2_htonu32(&channel->adjust_adjust[5], adjustment);
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN,
+ "Adjusting window %lu bytes for data on "
+ "channel %lu/%lu",
+ adjustment, channel->local.id, channel->remote.id);
+
+ channel->adjust_state = libssh2_NB_state_created;
+ }
+
+ rc = _libssh2_transport_send(channel->session, channel->adjust_adjust, 9,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(channel->session, rc,
+ "Would block sending window adjust");
+ return rc;
+ }
+ else if(rc) {
+ channel->adjust_queue = adjustment;
+ return _libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send transfer-window adjustment "
+ "packet, deferring");
+ }
+ else {
+ channel->remote.window_size += adjustment;
+ }
+
+ channel->adjust_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/*
+ * libssh2_channel_receive_window_adjust
+ *
+ * DEPRECATED
+ *
+ * Adjust the receive window for a channel by adjustment bytes. If the amount
+ * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the
+ * adjustment amount will be queued for a later packet.
+ *
+ * Returns the new size of the receive window (as understood by remote end).
+ * Note that it might return EAGAIN too which is highly stupid.
+ *
+ */
+LIBSSH2_API unsigned long
+libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel,
+ unsigned long adj,
+ unsigned char force)
+{
+ unsigned int window;
+ int rc;
+
+ if(!channel)
+ return (unsigned long)LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_receive_window_adjust(channel, adj,
+ force, &window));
+
+ /* stupid - but this is how it was made to work before and this is just
+ kept for backwards compatibility */
+ return rc ? (unsigned long)rc : window;
+}
+
+/*
+ * libssh2_channel_receive_window_adjust2
+ *
+ * Adjust the receive window for a channel by adjustment bytes. If the amount
+ * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the
+ * adjustment amount will be queued for a later packet.
+ *
+ * Stores the new size of the receive window in the data 'window' points to.
+ *
+ * Returns the "normal" error code: 0 for success, negative for failure.
+ */
+LIBSSH2_API int
+libssh2_channel_receive_window_adjust2(LIBSSH2_CHANNEL *channel,
+ unsigned long adj,
+ unsigned char force,
+ unsigned int *window)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_receive_window_adjust(channel, adj, force,
+ window));
+ return rc;
+}
+
+int
+_libssh2_channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode)
+{
+ if(channel->extData2_state == libssh2_NB_state_idle) {
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN,
+ "Setting channel %lu/%lu handle_extended_data"
+ " mode to %d",
+ channel->local.id, channel->remote.id, ignore_mode);
+ channel->remote.extended_data_ignore_mode = (char)ignore_mode;
+
+ channel->extData2_state = libssh2_NB_state_created;
+ }
+
+ if(channel->extData2_state == libssh2_NB_state_idle) {
+ if(ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) {
+ int rc =
+ _libssh2_channel_flush(channel,
+ LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA);
+ if(LIBSSH2_ERROR_EAGAIN == rc)
+ return rc;
+ }
+ }
+
+ channel->extData2_state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * libssh2_channel_handle_extended_data2()
+ *
+ */
+LIBSSH2_API int
+libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL *channel,
+ int mode)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, _libssh2_channel_extended_data(channel,
+ mode));
+ return rc;
+}
+
+/*
+ * libssh2_channel_handle_extended_data
+ *
+ * DEPRECATED DO NOTE USE!
+ *
+ * How should extended data look to the calling app? Keep it in separate
+ * channels[_read() _read_stdder()]? (NORMAL) Merge the extended data to the
+ * standard data? [everything via _read()]? (MERGE) Ignore it entirely [toss
+ * out packets as they come in]? (IGNORE)
+ */
+LIBSSH2_API void
+libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel,
+ int ignore_mode)
+{
+ (void)libssh2_channel_handle_extended_data2(channel, ignore_mode);
+}
+
+
+
+/*
+ * _libssh2_channel_read
+ *
+ * Read data from a channel
+ *
+ * It is important to not return 0 until the currently read channel is
+ * complete. If we read stuff from the wire but it was no payload data to fill
+ * in the buffer with, we MUST make sure to return LIBSSH2_ERROR_EAGAIN.
+ *
+ * The receive window must be maintained (enlarged) by the user of this
+ * function.
+ */
+ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
+ char *buf, size_t buflen)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ int rc;
+ size_t bytes_read = 0;
+ size_t bytes_want;
+ int unlink_packet;
+ LIBSSH2_PACKET *read_packet;
+ LIBSSH2_PACKET *read_next;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "channel_read() wants %d bytes from channel %lu/%lu "
+ "stream #%d",
+ (int) buflen, channel->local.id, channel->remote.id,
+ stream_id);
+
+ /* expand the receiving window first if it has become too narrow */
+ if((channel->read_state == libssh2_NB_state_jump1) ||
+ (channel->remote.window_size <
+ channel->remote.window_size_initial / 4 * 3 + buflen) ) {
+
+ uint32_t adjustment = channel->remote.window_size_initial + buflen -
+ channel->remote.window_size;
+ if(adjustment < LIBSSH2_CHANNEL_MINADJUST)
+ adjustment = LIBSSH2_CHANNEL_MINADJUST;
+
+ /* the actual window adjusting may not finish so we need to deal with
+ this special state here */
+ channel->read_state = libssh2_NB_state_jump1;
+ rc = _libssh2_channel_receive_window_adjust(channel, adjustment,
+ 0, NULL);
+ if(rc)
+ return rc;
+
+ channel->read_state = libssh2_NB_state_idle;
+ }
+
+ /* Process all pending incoming packets. Tests prove that this way
+ produces faster transfers. */
+ do {
+ rc = _libssh2_transport_read(session);
+ } while(rc > 0);
+
+ if((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN))
+ return _libssh2_error(session, rc, "transport read");
+
+ read_packet = _libssh2_list_first(&session->packets);
+ while(read_packet && (bytes_read < buflen)) {
+ /* previously this loop condition also checked for
+ !channel->remote.close but we cannot let it do this:
+
+ We may have a series of packets to read that are still pending even
+ if a close has been received. Acknowledging the close too early
+ makes us flush buffers prematurely and loose data.
+ */
+
+ LIBSSH2_PACKET *readpkt = read_packet;
+
+ /* In case packet gets destroyed during this iteration */
+ read_next = _libssh2_list_next(&readpkt->node);
+
+ if(readpkt->data_len < 5) {
+ read_packet = read_next;
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_ERROR,
+ "Unexpected packet length");
+ continue;
+ }
+
+ channel->read_local_id =
+ _libssh2_ntohu32(readpkt->data + 1);
+
+ /*
+ * Either we asked for a specific extended data stream
+ * (and data was available),
+ * or the standard stream (and data was available),
+ * or the standard stream with extended_data_merge
+ * enabled and data was available
+ */
+ if((stream_id
+ && (readpkt->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ && (channel->local.id == channel->read_local_id)
+ && (readpkt->data_len >= 9)
+ && (stream_id == (int) _libssh2_ntohu32(readpkt->data + 5)))
+ || (!stream_id && (readpkt->data[0] == SSH_MSG_CHANNEL_DATA)
+ && (channel->local.id == channel->read_local_id))
+ || (!stream_id
+ && (readpkt->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ && (channel->local.id == channel->read_local_id)
+ && (channel->remote.extended_data_ignore_mode ==
+ LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) {
+
+ /* figure out much more data we want to read */
+ bytes_want = buflen - bytes_read;
+ unlink_packet = FALSE;
+
+ if(bytes_want >= (readpkt->data_len - readpkt->data_head)) {
+ /* we want more than this node keeps, so adjust the number and
+ delete this node after the copy */
+ bytes_want = readpkt->data_len - readpkt->data_head;
+ unlink_packet = TRUE;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "channel_read() got %d of data from %lu/%lu/%d%s",
+ bytes_want, channel->local.id,
+ channel->remote.id, stream_id,
+ unlink_packet?" [ul]":"");
+
+ /* copy data from this struct to the target buffer */
+ memcpy(&buf[bytes_read],
+ &readpkt->data[readpkt->data_head], bytes_want);
+
+ /* advance pointer and counter */
+ readpkt->data_head += bytes_want;
+ bytes_read += bytes_want;
+
+ /* if drained, remove from list */
+ if(unlink_packet) {
+ /* detach readpkt from session->packets list */
+ _libssh2_list_remove(&readpkt->node);
+
+ LIBSSH2_FREE(session, readpkt->data);
+ LIBSSH2_FREE(session, readpkt);
+ }
+ }
+
+ /* check the next struct in the chain */
+ read_packet = read_next;
+ }
+
+ if(!bytes_read) {
+ /* If the channel is already at EOF or even closed, we need to signal
+ that back. We may have gotten that info while draining the incoming
+ transport layer until EAGAIN so we must not be fooled by that
+ return code. */
+ if(channel->remote.eof || channel->remote.close)
+ return 0;
+ else if(rc != LIBSSH2_ERROR_EAGAIN)
+ return 0;
+
+ /* if the transport layer said EAGAIN then we say so as well */
+ return _libssh2_error(session, rc, "would block");
+ }
+
+ channel->read_avail -= bytes_read;
+ channel->remote.window_size -= bytes_read;
+
+ return bytes_read;
+}
+
+/*
+ * libssh2_channel_read_ex
+ *
+ * Read data from a channel (blocking or non-blocking depending on set state)
+ *
+ * When this is done non-blocking, it is important to not return 0 until the
+ * currently read channel is complete. If we read stuff from the wire but it
+ * was no payload data to fill in the buffer with, we MUST make sure to return
+ * LIBSSH2_ERROR_EAGAIN.
+ *
+ * This function will first make sure there's a receive window enough to
+ * receive a full buffer's wort of contents. An application may choose to
+ * adjust the receive window more to increase transfer performance.
+ */
+LIBSSH2_API ssize_t
+libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf,
+ size_t buflen)
+{
+ int rc;
+ unsigned long recv_window;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ recv_window = libssh2_channel_window_read_ex(channel, NULL, NULL);
+
+ if(buflen > recv_window) {
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_receive_window_adjust(channel, buflen,
+ 1, NULL));
+ }
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_read(channel, stream_id, buf, buflen));
+ return rc;
+}
+
+/*
+ * _libssh2_channel_packet_data_len
+ *
+ * Return the size of the data block of the current packet, or 0 if there
+ * isn't a packet.
+ */
+size_t
+_libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel, int stream_id)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ LIBSSH2_PACKET *read_packet;
+ LIBSSH2_PACKET *next_packet;
+ uint32_t read_local_id;
+
+ read_packet = _libssh2_list_first(&session->packets);
+ if(read_packet == NULL)
+ return 0;
+
+ while(read_packet) {
+
+ next_packet = _libssh2_list_next(&read_packet->node);
+
+ if(read_packet->data_len < 5) {
+ read_packet = next_packet;
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_ERROR,
+ "Unexpected packet length");
+ continue;
+ }
+
+ read_local_id = _libssh2_ntohu32(read_packet->data + 1);
+
+ /*
+ * Either we asked for a specific extended data stream
+ * (and data was available),
+ * or the standard stream (and data was available),
+ * or the standard stream with extended_data_merge
+ * enabled and data was available
+ */
+ if((stream_id
+ && (read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ && (channel->local.id == read_local_id)
+ && (read_packet->data_len >= 9)
+ && (stream_id == (int) _libssh2_ntohu32(read_packet->data + 5)))
+ ||
+ (!stream_id
+ && (read_packet->data[0] == SSH_MSG_CHANNEL_DATA)
+ && (channel->local.id == read_local_id))
+ ||
+ (!stream_id
+ && (read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ && (channel->local.id == read_local_id)
+ && (channel->remote.extended_data_ignore_mode
+ == LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) {
+ return (read_packet->data_len - read_packet->data_head);
+ }
+
+ read_packet = next_packet;
+ }
+
+ return 0;
+}
+
+/*
+ * _libssh2_channel_write
+ *
+ * Send data to a channel. Note that if this returns EAGAIN, the caller must
+ * call this function again with the SAME input arguments.
+ *
+ * Returns: number of bytes sent, or if it returns a negative number, that is
+ * the error code!
+ */
+ssize_t
+_libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id,
+ const unsigned char *buf, size_t buflen)
+{
+ int rc = 0;
+ LIBSSH2_SESSION *session = channel->session;
+ ssize_t wrote = 0; /* counter for this specific this call */
+
+ /* In theory we could split larger buffers into several smaller packets
+ * but it turns out to be really hard and nasty to do while still offering
+ * the API/prototype.
+ *
+ * Instead we only deal with the first 32K in this call and for the parent
+ * function to call it again with the remainder! 32K is a conservative
+ * limit based on the text in RFC4253 section 6.1.
+ */
+ if(buflen > 32700)
+ buflen = 32700;
+
+ if(channel->write_state == libssh2_NB_state_idle) {
+ unsigned char *s = channel->write_packet;
+
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_CONN,
+ "Writing %d bytes on channel %lu/%lu, stream #%d",
+ (int) buflen, channel->local.id, channel->remote.id,
+ stream_id);
+
+ if(channel->local.close)
+ return _libssh2_error(channel->session,
+ LIBSSH2_ERROR_CHANNEL_CLOSED,
+ "We've already closed this channel");
+ else if(channel->local.eof)
+ return _libssh2_error(channel->session,
+ LIBSSH2_ERROR_CHANNEL_EOF_SENT,
+ "EOF has already been received, "
+ "data might be ignored");
+
+ /* drain the incoming flow first, mostly to make sure we get all
+ * pending window adjust packets */
+ do
+ rc = _libssh2_transport_read(session);
+ while(rc > 0);
+
+ if((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN)) {
+ return _libssh2_error(channel->session, rc,
+ "Failure while draining incoming flow");
+ }
+
+ if(channel->local.window_size <= 0) {
+ /* there's no room for data so we stop */
+
+ /* Waiting on the socket to be writable would be wrong because we
+ * would be back here immediately, but a readable socket might
+ * herald an incoming window adjustment.
+ */
+ session->socket_block_directions = LIBSSH2_SESSION_BLOCK_INBOUND;
+
+ return (rc == LIBSSH2_ERROR_EAGAIN?rc:0);
+ }
+
+ channel->write_bufwrite = buflen;
+
+ *(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA :
+ SSH_MSG_CHANNEL_DATA;
+ _libssh2_store_u32(&s, channel->remote.id);
+ if(stream_id)
+ _libssh2_store_u32(&s, stream_id);
+
+ /* Don't exceed the remote end's limits */
+ /* REMEMBER local means local as the SOURCE of the data */
+ if(channel->write_bufwrite > channel->local.window_size) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Splitting write block due to %lu byte "
+ "window_size on %lu/%lu/%d",
+ channel->local.window_size, channel->local.id,
+ channel->remote.id, stream_id);
+ channel->write_bufwrite = channel->local.window_size;
+ }
+ if(channel->write_bufwrite > channel->local.packet_size) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Splitting write block due to %lu byte "
+ "packet_size on %lu/%lu/%d",
+ channel->local.packet_size, channel->local.id,
+ channel->remote.id, stream_id);
+ channel->write_bufwrite = channel->local.packet_size;
+ }
+ /* store the size here only, the buffer is passed in as-is to
+ _libssh2_transport_send() */
+ _libssh2_store_u32(&s, channel->write_bufwrite);
+ channel->write_packet_len = s - channel->write_packet;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Sending %d bytes on channel %lu/%lu, stream_id=%d",
+ (int) channel->write_bufwrite, channel->local.id,
+ channel->remote.id, stream_id);
+
+ channel->write_state = libssh2_NB_state_created;
+ }
+
+ if(channel->write_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, channel->write_packet,
+ channel->write_packet_len,
+ buf, channel->write_bufwrite);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, rc,
+ "Unable to send channel data");
+ }
+ else if(rc) {
+ channel->write_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send channel data");
+ }
+ /* Shrink local window size */
+ channel->local.window_size -= channel->write_bufwrite;
+
+ wrote += channel->write_bufwrite;
+
+ /* Since _libssh2_transport_write() succeeded, we must return
+ now to allow the caller to provide the next chunk of data.
+
+ We cannot move on to send the next piece of data that may
+ already have been provided in this same function call, as we
+ risk getting EAGAIN for that and we can't return information
+ both about sent data as well as EAGAIN. So, by returning short
+ now, the caller will call this function again with new data to
+ send */
+
+ channel->write_state = libssh2_NB_state_idle;
+
+ return wrote;
+ }
+
+ return LIBSSH2_ERROR_INVAL; /* reaching this point is really bad */
+}
+
+/*
+ * libssh2_channel_write_ex
+ *
+ * Send data to a channel
+ */
+LIBSSH2_API ssize_t
+libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id,
+ const char *buf, size_t buflen)
+{
+ ssize_t rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session,
+ _libssh2_channel_write(channel, stream_id,
+ (unsigned char *)buf, buflen));
+ return rc;
+}
+
+/*
+ * channel_send_eof
+ *
+ * Send EOF on channel
+ */
+static int channel_send_eof(LIBSSH2_CHANNEL *channel)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char packet[5]; /* packet_type(1) + channelno(4) */
+ int rc;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Sending EOF on channel %lu/%lu",
+ channel->local.id, channel->remote.id);
+ packet[0] = SSH_MSG_CHANNEL_EOF;
+ _libssh2_htonu32(packet + 1, channel->remote.id);
+ rc = _libssh2_transport_send(session, packet, 5, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending EOF");
+ return rc;
+ }
+ else if(rc) {
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send EOF on channel");
+ }
+ channel->local.eof = 1;
+
+ return 0;
+}
+
+/*
+ * libssh2_channel_send_eof
+ *
+ * Send EOF on channel
+ */
+LIBSSH2_API int
+libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, channel_send_eof(channel));
+ return rc;
+}
+
+/*
+ * libssh2_channel_eof
+ *
+ * Read channel's eof status
+ */
+LIBSSH2_API int
+libssh2_channel_eof(LIBSSH2_CHANNEL * channel)
+{
+ LIBSSH2_SESSION *session;
+ LIBSSH2_PACKET *packet;
+ LIBSSH2_PACKET *next_packet;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ session = channel->session;
+ packet = _libssh2_list_first(&session->packets);
+
+ while(packet) {
+
+ next_packet = _libssh2_list_next(&packet->node);
+
+ if(packet->data_len < 1) {
+ packet = next_packet;
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_ERROR,
+ "Unexpected packet length");
+ continue;
+ }
+
+ if(((packet->data[0] == SSH_MSG_CHANNEL_DATA)
+ || (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA))
+ && ((packet->data_len >= 5)
+ && (channel->local.id == _libssh2_ntohu32(packet->data + 1)))) {
+ /* There's data waiting to be read yet, mask the EOF status */
+ return 0;
+ }
+ packet = next_packet;
+ }
+
+ return channel->remote.eof;
+}
+
+/*
+ * channel_wait_eof
+ *
+ * Awaiting channel EOF
+ */
+static int channel_wait_eof(LIBSSH2_CHANNEL *channel)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ int rc;
+
+ if(channel->wait_eof_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Awaiting EOF for channel %lu/%lu", channel->local.id,
+ channel->remote.id);
+
+ channel->wait_eof_state = libssh2_NB_state_created;
+ }
+
+ /*
+ * While channel is not eof, read more packets from the network.
+ * Either the EOF will be set or network timeout will occur.
+ */
+ do {
+ if(channel->remote.eof) {
+ break;
+ }
+
+ if((channel->remote.window_size == channel->read_avail) &&
+ session->api_block_mode)
+ return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_FULL,
+ "Receiving channel window "
+ "has been exhausted");
+
+ rc = _libssh2_transport_read(session);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc < 0) {
+ channel->wait_eof_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "_libssh2_transport_read() bailed out!");
+ }
+ } while(1);
+
+ channel->wait_eof_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/*
+ * libssh2_channel_wait_eof
+ *
+ * Awaiting channel EOF
+ */
+LIBSSH2_API int
+libssh2_channel_wait_eof(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, channel_wait_eof(channel));
+ return rc;
+}
+
+int _libssh2_channel_close(LIBSSH2_CHANNEL * channel)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ int rc = 0;
+
+ if(channel->local.close) {
+ /* Already closed, act like we sent another close,
+ * even though we didn't... shhhhhh */
+ channel->close_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ if(!channel->local.eof) {
+ rc = channel_send_eof(channel);
+ if(rc) {
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ _libssh2_error(session, rc,
+ "Unable to send EOF, but closing channel anyway");
+ }
+ }
+
+ /* ignore if we have received a remote eof or not, as it is now too
+ late for us to wait for it. Continue closing! */
+
+ if(channel->close_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN, "Closing channel %lu/%lu",
+ channel->local.id, channel->remote.id);
+
+ channel->close_packet[0] = SSH_MSG_CHANNEL_CLOSE;
+ _libssh2_htonu32(channel->close_packet + 1, channel->remote.id);
+
+ channel->close_state = libssh2_NB_state_created;
+ }
+
+ if(channel->close_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, channel->close_packet, 5,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, rc,
+ "Would block sending close-channel");
+ return rc;
+
+ }
+ else if(rc) {
+ _libssh2_error(session, rc,
+ "Unable to send close-channel request, "
+ "but closing anyway");
+ /* skip waiting for the response and fall through to
+ LIBSSH2_CHANNEL_CLOSE below */
+
+ }
+ else
+ channel->close_state = libssh2_NB_state_sent;
+ }
+
+ if(channel->close_state == libssh2_NB_state_sent) {
+ /* We must wait for the remote SSH_MSG_CHANNEL_CLOSE message */
+
+ while(!channel->remote.close && !rc &&
+ (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED))
+ rc = _libssh2_transport_read(session);
+ }
+
+ if(rc != LIBSSH2_ERROR_EAGAIN) {
+ /* set the local close state first when we're perfectly confirmed to
+ not do any more EAGAINs */
+ channel->local.close = 1;
+
+ /* We call the callback last in this function to make it keep the local
+ data as long as EAGAIN is returned. */
+ if(channel->close_cb) {
+ LIBSSH2_CHANNEL_CLOSE(session, channel);
+ }
+
+ channel->close_state = libssh2_NB_state_idle;
+ }
+
+ /* return 0 or an error */
+ return rc >= 0 ? 0 : rc;
+}
+
+/*
+ * libssh2_channel_close
+ *
+ * Close a channel
+ */
+LIBSSH2_API int
+libssh2_channel_close(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, _libssh2_channel_close(channel) );
+ return rc;
+}
+
+/*
+ * channel_wait_closed
+ *
+ * Awaiting channel close after EOF
+ */
+static int channel_wait_closed(LIBSSH2_CHANNEL *channel)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ int rc;
+
+ if(!channel->remote.eof) {
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "libssh2_channel_wait_closed() invoked when "
+ "channel is not in EOF state");
+ }
+
+ if(channel->wait_closed_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Awaiting close of channel %lu/%lu", channel->local.id,
+ channel->remote.id);
+
+ channel->wait_closed_state = libssh2_NB_state_created;
+ }
+
+ /*
+ * While channel is not closed, read more packets from the network.
+ * Either the channel will be closed or network timeout will occur.
+ */
+ if(!channel->remote.close) {
+ do {
+ rc = _libssh2_transport_read(session);
+ if(channel->remote.close)
+ /* it is now closed, move on! */
+ break;
+ } while(rc > 0);
+ if(rc < 0)
+ return rc;
+ }
+
+ channel->wait_closed_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/*
+ * libssh2_channel_wait_closed
+ *
+ * Awaiting channel close after EOF
+ */
+LIBSSH2_API int
+libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, channel_wait_closed(channel));
+ return rc;
+}
+
+/*
+ * _libssh2_channel_free
+ *
+ * Make sure a channel is closed, then remove the channel from the session
+ * and free its resource(s)
+ *
+ * Returns 0 on success, negative on failure
+ */
+int _libssh2_channel_free(LIBSSH2_CHANNEL *channel)
+{
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char channel_id[4];
+ unsigned char *data;
+ size_t data_len;
+ int rc;
+
+ assert(session);
+
+ if(channel->free_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Freeing channel %lu/%lu resources", channel->local.id,
+ channel->remote.id);
+
+ channel->free_state = libssh2_NB_state_created;
+ }
+
+ /* Allow channel freeing even when the socket has lost its connection */
+ if(!channel->local.close
+ && (session->socket_state == LIBSSH2_SOCKET_CONNECTED)) {
+ rc = _libssh2_channel_close(channel);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ /* ignore all other errors as they otherwise risk blocking the channel
+ free from happening */
+ }
+
+ channel->free_state = libssh2_NB_state_idle;
+
+ if(channel->exit_signal) {
+ LIBSSH2_FREE(session, channel->exit_signal);
+ }
+
+ /*
+ * channel->remote.close *might* not be set yet, Well...
+ * We've sent the close packet, what more do you want?
+ * Just let packet_add ignore it when it finally arrives
+ */
+
+ /* Clear out packets meant for this channel */
+ _libssh2_htonu32(channel_id, channel->local.id);
+ while((_libssh2_packet_ask(session, SSH_MSG_CHANNEL_DATA, &data,
+ &data_len, 1, channel_id, 4) >= 0)
+ ||
+ (_libssh2_packet_ask(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data,
+ &data_len, 1, channel_id, 4) >= 0)) {
+ LIBSSH2_FREE(session, data);
+ }
+
+ /* free "channel_type" */
+ if(channel->channel_type) {
+ LIBSSH2_FREE(session, channel->channel_type);
+ }
+
+ /* Unlink from channel list */
+ _libssh2_list_remove(&channel->node);
+
+ /*
+ * Make sure all memory used in the state variables are free
+ */
+ if(channel->setenv_packet) {
+ LIBSSH2_FREE(session, channel->setenv_packet);
+ }
+ if(channel->reqX11_packet) {
+ LIBSSH2_FREE(session, channel->reqX11_packet);
+ }
+ if(channel->process_packet) {
+ LIBSSH2_FREE(session, channel->process_packet);
+ }
+
+ LIBSSH2_FREE(session, channel);
+
+ return 0;
+}
+
+/*
+ * libssh2_channel_free
+ *
+ * Make sure a channel is closed, then remove the channel from the session
+ * and free its resource(s)
+ *
+ * Returns 0 on success, negative on failure
+ */
+LIBSSH2_API int
+libssh2_channel_free(LIBSSH2_CHANNEL *channel)
+{
+ int rc;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, channel->session, _libssh2_channel_free(channel));
+ return rc;
+}
+/*
+ * libssh2_channel_window_read_ex
+ *
+ * Check the status of the read window. Returns the number of bytes which the
+ * remote end may send without overflowing the window limit read_avail (if
+ * passed) will be populated with the number of bytes actually available to be
+ * read window_size_initial (if passed) will be populated with the
+ * window_size_initial as defined by the channel_open request
+ */
+LIBSSH2_API unsigned long
+libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel,
+ unsigned long *read_avail,
+ unsigned long *window_size_initial)
+{
+ if(!channel)
+ return 0; /* no channel, no window! */
+
+ if(window_size_initial) {
+ *window_size_initial = channel->remote.window_size_initial;
+ }
+
+ if(read_avail) {
+ size_t bytes_queued = 0;
+ LIBSSH2_PACKET *next_packet;
+ LIBSSH2_PACKET *packet =
+ _libssh2_list_first(&channel->session->packets);
+
+ while(packet) {
+ unsigned char packet_type;
+ next_packet = _libssh2_list_next(&packet->node);
+
+ if(packet->data_len < 1) {
+ packet = next_packet;
+ _libssh2_debug(channel->session, LIBSSH2_TRACE_ERROR,
+ "Unexpected packet length");
+ continue;
+ }
+
+ packet_type = packet->data[0];
+
+ if(((packet_type == SSH_MSG_CHANNEL_DATA)
+ || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA))
+ && ((packet->data_len >= 5)
+ && (_libssh2_ntohu32(packet->data + 1) ==
+ channel->local.id))) {
+ bytes_queued += packet->data_len - packet->data_head;
+ }
+
+ packet = next_packet;
+ }
+
+ *read_avail = bytes_queued;
+ }
+
+ return channel->remote.window_size;
+}
+
+/*
+ * libssh2_channel_window_write_ex
+ *
+ * Check the status of the write window Returns the number of bytes which may
+ * be safely written on the channel without blocking window_size_initial (if
+ * passed) will be populated with the size of the initial window as defined by
+ * the channel_open request
+ */
+LIBSSH2_API unsigned long
+libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel,
+ unsigned long *window_size_initial)
+{
+ if(!channel)
+ return 0; /* no channel, no window! */
+
+ if(window_size_initial) {
+ /* For locally initiated channels this is very often 0, so it's not
+ * *that* useful as information goes */
+ *window_size_initial = channel->local.window_size_initial;
+ }
+
+ return channel->local.window_size;
+}
diff --git a/contrib/libs/libssh2/src/channel.h b/contrib/libs/libssh2/src/channel.h
new file mode 100644
index 00000000000..dc0ee3764f3
--- /dev/null
+++ b/contrib/libs/libssh2/src/channel.h
@@ -0,0 +1,141 @@
+#ifndef __LIBSSH2_CHANNEL_H
+#define __LIBSSH2_CHANNEL_H
+/* Copyright (c) 2008-2010 by Daniel Stenberg
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.
+ */
+
+/*
+ * _libssh2_channel_receive_window_adjust
+ *
+ * Adjust the receive window for a channel by adjustment bytes. If the amount
+ * to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0 the
+ * adjustment amount will be queued for a later packet.
+ *
+ * Always non-blocking.
+ */
+int _libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel,
+ uint32_t adjustment,
+ unsigned char force,
+ unsigned int *store);
+
+/*
+ * _libssh2_channel_flush
+ *
+ * Flush data from one (or all) stream
+ * Returns number of bytes flushed, or negative on failure
+ */
+int _libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid);
+
+/*
+ * _libssh2_channel_free
+ *
+ * Make sure a channel is closed, then remove the channel from the session
+ * and free its resource(s)
+ *
+ * Returns 0 on success, negative on failure
+ */
+int _libssh2_channel_free(LIBSSH2_CHANNEL *channel);
+
+int
+_libssh2_channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode);
+
+/*
+ * _libssh2_channel_write
+ *
+ * Send data to a channel
+ */
+ssize_t
+_libssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id,
+ const unsigned char *buf, size_t buflen);
+
+/*
+ * _libssh2_channel_open
+ *
+ * Establish a generic session channel
+ */
+LIBSSH2_CHANNEL *
+_libssh2_channel_open(LIBSSH2_SESSION * session, const char *channel_type,
+ uint32_t channel_type_len,
+ uint32_t window_size,
+ uint32_t packet_size,
+ const unsigned char *message, size_t message_len);
+
+
+/*
+ * _libssh2_channel_process_startup
+ *
+ * Primitive for libssh2_channel_(shell|exec|subsystem)
+ */
+int
+_libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel,
+ const char *request, size_t request_len,
+ const char *message, size_t message_len);
+
+/*
+ * _libssh2_channel_read
+ *
+ * Read data from a channel
+ *
+ * It is important to not return 0 until the currently read channel is
+ * complete. If we read stuff from the wire but it was no payload data to fill
+ * in the buffer with, we MUST make sure to return PACKET_EAGAIN.
+ */
+ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
+ char *buf, size_t buflen);
+
+uint32_t _libssh2_channel_nextid(LIBSSH2_SESSION * session);
+
+LIBSSH2_CHANNEL *_libssh2_channel_locate(LIBSSH2_SESSION * session,
+ uint32_t channel_id);
+
+size_t _libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel,
+ int stream_id);
+
+int _libssh2_channel_close(LIBSSH2_CHANNEL * channel);
+
+/*
+ * _libssh2_channel_forward_cancel
+ *
+ * Stop listening on a remote port and free the listener
+ * Toss out any pending (un-accept()ed) connections
+ *
+ * Return 0 on success, LIBSSH2_ERROR_EAGAIN if would block, -1 on error
+ */
+int _libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener);
+
+#endif /* __LIBSSH2_CHANNEL_H */
+
diff --git a/contrib/libs/libssh2/src/comp.c b/contrib/libs/libssh2/src/comp.c
new file mode 100644
index 00000000000..90ab30c89d8
--- /dev/null
+++ b/contrib/libs/libssh2/src/comp.c
@@ -0,0 +1,377 @@
+/* Copyright (c) 2004-2007, 2019, Sara Golemon <[email protected]>
+ * Copyright (c) 2010-2014, Daniel Stenberg <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#ifdef LIBSSH2_HAVE_ZLIB
+#include <zlib.h>
+#undef compress /* dodge name clash with ZLIB macro */
+#endif
+
+#include "comp.h"
+
+/* ********
+ * none *
+ ******** */
+
+/*
+ * comp_method_none_comp
+ *
+ * Minimalist compression: Absolutely none
+ */
+static int
+comp_method_none_comp(LIBSSH2_SESSION *session,
+ unsigned char *dest,
+ size_t *dest_len,
+ const unsigned char *src,
+ size_t src_len,
+ void **abstract)
+{
+ (void) session;
+ (void) abstract;
+ (void) dest;
+ (void) dest_len;
+ (void) src;
+ (void) src_len;
+
+ return 0;
+}
+
+/*
+ * comp_method_none_decomp
+ *
+ * Minimalist decompression: Absolutely none
+ */
+static int
+comp_method_none_decomp(LIBSSH2_SESSION * session,
+ unsigned char **dest,
+ size_t *dest_len,
+ size_t payload_limit,
+ const unsigned char *src,
+ size_t src_len, void **abstract)
+{
+ (void) session;
+ (void) payload_limit;
+ (void) abstract;
+ *dest = (unsigned char *) src;
+ *dest_len = src_len;
+ return 0;
+}
+
+
+
+static const LIBSSH2_COMP_METHOD comp_method_none = {
+ "none",
+ 0, /* not really compressing */
+ 0, /* isn't used in userauth, go figure */
+ NULL,
+ comp_method_none_comp,
+ comp_method_none_decomp,
+ NULL
+};
+
+#ifdef LIBSSH2_HAVE_ZLIB
+/* ********
+ * zlib *
+ ******** */
+
+/* Memory management wrappers
+ * Yes, I realize we're doing a callback to a callback,
+ * Deal...
+ */
+
+static voidpf
+comp_method_zlib_alloc(voidpf opaque, uInt items, uInt size)
+{
+ LIBSSH2_SESSION *session = (LIBSSH2_SESSION *) opaque;
+
+ return (voidpf) LIBSSH2_ALLOC(session, items * size);
+}
+
+static void
+comp_method_zlib_free(voidpf opaque, voidpf address)
+{
+ LIBSSH2_SESSION *session = (LIBSSH2_SESSION *) opaque;
+
+ LIBSSH2_FREE(session, address);
+}
+
+
+
+/* libssh2_comp_method_zlib_init
+ * All your bandwidth are belong to us (so save some)
+ */
+static int
+comp_method_zlib_init(LIBSSH2_SESSION * session, int compr,
+ void **abstract)
+{
+ z_stream *strm;
+ int status;
+
+ strm = LIBSSH2_CALLOC(session, sizeof(z_stream));
+ if(!strm) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "zlib compression/decompression");
+ }
+
+ strm->opaque = (voidpf) session;
+ strm->zalloc = (alloc_func) comp_method_zlib_alloc;
+ strm->zfree = (free_func) comp_method_zlib_free;
+ if(compr) {
+ /* deflate */
+ status = deflateInit(strm, Z_DEFAULT_COMPRESSION);
+ }
+ else {
+ /* inflate */
+ status = inflateInit(strm);
+ }
+
+ if(status != Z_OK) {
+ LIBSSH2_FREE(session, strm);
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "unhandled zlib error %d", status);
+ return LIBSSH2_ERROR_COMPRESS;
+ }
+ *abstract = strm;
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * libssh2_comp_method_zlib_comp
+ *
+ * Compresses source to destination. Without allocation.
+ */
+static int
+comp_method_zlib_comp(LIBSSH2_SESSION *session,
+ unsigned char *dest,
+
+ /* dest_len is a pointer to allow this function to
+ update it with the final actual size used */
+ size_t *dest_len,
+ const unsigned char *src,
+ size_t src_len,
+ void **abstract)
+{
+ z_stream *strm = *abstract;
+ int out_maxlen = *dest_len;
+ int status;
+
+ strm->next_in = (unsigned char *) src;
+ strm->avail_in = src_len;
+ strm->next_out = dest;
+ strm->avail_out = out_maxlen;
+
+ status = deflate(strm, Z_PARTIAL_FLUSH);
+
+ if((status == Z_OK) && (strm->avail_out > 0)) {
+ *dest_len = out_maxlen - strm->avail_out;
+ return 0;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "unhandled zlib compression error %d, avail_out",
+ status, strm->avail_out);
+ return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, "compression failure");
+}
+
+/*
+ * libssh2_comp_method_zlib_decomp
+ *
+ * Decompresses source to destination. Allocates the output memory.
+ */
+static int
+comp_method_zlib_decomp(LIBSSH2_SESSION * session,
+ unsigned char **dest,
+ size_t *dest_len,
+ size_t payload_limit,
+ const unsigned char *src,
+ size_t src_len, void **abstract)
+{
+ z_stream *strm = *abstract;
+ /* A short-term alloc of a full data chunk is better than a series of
+ reallocs */
+ char *out;
+ size_t out_maxlen = src_len;
+
+ if(src_len <= SIZE_MAX / 4)
+ out_maxlen = src_len * 4;
+ else
+ out_maxlen = payload_limit;
+
+ /* If strm is null, then we have not yet been initialized. */
+ if(strm == NULL)
+ return _libssh2_error(session, LIBSSH2_ERROR_COMPRESS,
+ "decompression uninitialized");;
+
+ /* In practice they never come smaller than this */
+ if(out_maxlen < 25)
+ out_maxlen = 25;
+
+ if(out_maxlen > payload_limit)
+ out_maxlen = payload_limit;
+
+ strm->next_in = (unsigned char *) src;
+ strm->avail_in = src_len;
+ strm->next_out = (unsigned char *) LIBSSH2_ALLOC(session, out_maxlen);
+ out = (char *) strm->next_out;
+ strm->avail_out = out_maxlen;
+ if(!strm->next_out)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate decompression buffer");
+
+ /* Loop until it's all inflated or hit error */
+ for(;;) {
+ int status;
+ size_t out_ofs;
+ char *newout;
+
+ status = inflate(strm, Z_PARTIAL_FLUSH);
+
+ if(status == Z_OK) {
+ if(strm->avail_out > 0)
+ /* status is OK and the output buffer has not been exhausted
+ so we're done */
+ break;
+ }
+ else if(status == Z_BUF_ERROR) {
+ /* the input data has been exhausted so we are done */
+ break;
+ }
+ else {
+ /* error state */
+ LIBSSH2_FREE(session, out);
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "unhandled zlib error %d", status);
+ return _libssh2_error(session, LIBSSH2_ERROR_ZLIB,
+ "decompression failure");
+ }
+
+ if(out_maxlen > payload_limit || out_maxlen > SIZE_MAX / 2) {
+ LIBSSH2_FREE(session, out);
+ return _libssh2_error(session, LIBSSH2_ERROR_ZLIB,
+ "Excessive growth in decompression phase");
+ }
+
+ /* If we get here we need to grow the output buffer and try again */
+ out_ofs = out_maxlen - strm->avail_out;
+ out_maxlen *= 2;
+ newout = LIBSSH2_REALLOC(session, out, out_maxlen);
+ if(!newout) {
+ LIBSSH2_FREE(session, out);
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to expand decompression buffer");
+ }
+ out = newout;
+ strm->next_out = (unsigned char *) out + out_ofs;
+ strm->avail_out = out_maxlen - out_ofs;
+ }
+
+ *dest = (unsigned char *) out;
+ *dest_len = out_maxlen - strm->avail_out;
+
+ return 0;
+}
+
+
+/* libssh2_comp_method_zlib_dtor
+ * All done, no more compression for you
+ */
+static int
+comp_method_zlib_dtor(LIBSSH2_SESSION *session, int compr, void **abstract)
+{
+ z_stream *strm = *abstract;
+
+ if(strm) {
+ if(compr)
+ deflateEnd(strm);
+ else
+ inflateEnd(strm);
+ LIBSSH2_FREE(session, strm);
+ }
+
+ *abstract = NULL;
+ return 0;
+}
+
+static const LIBSSH2_COMP_METHOD comp_method_zlib = {
+ "zlib",
+ 1, /* yes, this compresses */
+ 1, /* do compression during userauth */
+ comp_method_zlib_init,
+ comp_method_zlib_comp,
+ comp_method_zlib_decomp,
+ comp_method_zlib_dtor,
+};
+
+static const LIBSSH2_COMP_METHOD comp_method_zlib_openssh = {
+ 1, /* yes, this compresses */
+ 0, /* don't use compression during userauth */
+ comp_method_zlib_init,
+ comp_method_zlib_comp,
+ comp_method_zlib_decomp,
+ comp_method_zlib_dtor,
+};
+#endif /* LIBSSH2_HAVE_ZLIB */
+
+/* If compression is enabled by the API, then this array is used which then
+ may allow compression if zlib is available at build time */
+static const LIBSSH2_COMP_METHOD *comp_methods[] = {
+#ifdef LIBSSH2_HAVE_ZLIB
+ &comp_method_zlib,
+ &comp_method_zlib_openssh,
+#endif /* LIBSSH2_HAVE_ZLIB */
+ &comp_method_none,
+ NULL
+};
+
+/* If compression is disabled by the API, then this array is used */
+static const LIBSSH2_COMP_METHOD *no_comp_methods[] = {
+ &comp_method_none,
+ NULL
+};
+
+const LIBSSH2_COMP_METHOD **
+_libssh2_comp_methods(LIBSSH2_SESSION *session)
+{
+ if(session->flag.compress)
+ return comp_methods;
+ else
+ return no_comp_methods;
+}
diff --git a/contrib/libs/libssh2/src/comp.h b/contrib/libs/libssh2/src/comp.h
new file mode 100644
index 00000000000..82ac2dc9587
--- /dev/null
+++ b/contrib/libs/libssh2/src/comp.h
@@ -0,0 +1,44 @@
+#ifndef __LIBSSH2_COMP_H
+#define __LIBSSH2_COMP_H
+/* Copyright (C) 2009-2010 by Daniel Stenberg
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+
+const LIBSSH2_COMP_METHOD **_libssh2_comp_methods(LIBSSH2_SESSION *session);
+
+#endif /* __LIBSSH2_COMP_H */
diff --git a/contrib/libs/libssh2/src/crypt.c b/contrib/libs/libssh2/src/crypt.c
new file mode 100644
index 00000000000..8d493b4847c
--- /dev/null
+++ b/contrib/libs/libssh2/src/crypt.c
@@ -0,0 +1,349 @@
+/* Copyright (c) 2009, 2010 Simon Josefsson <[email protected]>
+ * Copyright (c) 2004-2007, Sara Golemon <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+
+#ifdef LIBSSH2_CRYPT_NONE
+
+/* crypt_none_crypt
+ * Minimalist cipher: VERY secure *wink*
+ */
+static int
+crypt_none_crypt(LIBSSH2_SESSION * session, unsigned char *buf,
+ void **abstract)
+{
+ /* Do nothing to the data! */
+ return 0;
+}
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_none = {
+ "none",
+ "DEK-Info: NONE",
+ 8, /* blocksize (SSH2 defines minimum blocksize as 8) */
+ 0, /* iv_len */
+ 0, /* secret_len */
+ 0, /* flags */
+ NULL,
+ crypt_none_crypt,
+ NULL
+};
+#endif /* LIBSSH2_CRYPT_NONE */
+
+struct crypt_ctx
+{
+ int encrypt;
+ _libssh2_cipher_type(algo);
+ _libssh2_cipher_ctx h;
+};
+
+static int
+crypt_init(LIBSSH2_SESSION * session,
+ const LIBSSH2_CRYPT_METHOD * method,
+ unsigned char *iv, int *free_iv,
+ unsigned char *secret, int *free_secret,
+ int encrypt, void **abstract)
+{
+ struct crypt_ctx *ctx = LIBSSH2_ALLOC(session,
+ sizeof(struct crypt_ctx));
+ if(!ctx)
+ return LIBSSH2_ERROR_ALLOC;
+
+ ctx->encrypt = encrypt;
+ ctx->algo = method->algo;
+ if(_libssh2_cipher_init(&ctx->h, ctx->algo, iv, secret, encrypt)) {
+ LIBSSH2_FREE(session, ctx);
+ return -1;
+ }
+ *abstract = ctx;
+ *free_iv = 1;
+ *free_secret = 1;
+ return 0;
+}
+
+static int
+crypt_encrypt(LIBSSH2_SESSION * session, unsigned char *block,
+ size_t blocksize, void **abstract)
+{
+ struct crypt_ctx *cctx = *(struct crypt_ctx **) abstract;
+ (void) session;
+ return _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block,
+ blocksize);
+}
+
+static int
+crypt_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ struct crypt_ctx **cctx = (struct crypt_ctx **) abstract;
+ if(cctx && *cctx) {
+ _libssh2_cipher_dtor(&(*cctx)->h);
+ LIBSSH2_FREE(session, *cctx);
+ *abstract = NULL;
+ }
+ return 0;
+}
+
+#if LIBSSH2_AES_CTR
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_ctr = {
+ "aes128-ctr",
+ "",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 16, /* secret length -- 16*8 == 128bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes128ctr
+};
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_ctr = {
+ "aes192-ctr",
+ "",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 24, /* secret length -- 24*8 == 192bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes192ctr
+};
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_ctr = {
+ "aes256-ctr",
+ "",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 32, /* secret length -- 32*8 == 256bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes256ctr
+};
+#endif
+
+#if LIBSSH2_AES
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = {
+ "aes128-cbc",
+ "DEK-Info: AES-128-CBC",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 16, /* secret length -- 16*8 == 128bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes128
+};
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_cbc = {
+ "aes192-cbc",
+ "DEK-Info: AES-192-CBC",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 24, /* secret length -- 24*8 == 192bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes192
+};
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_cbc = {
+ "aes256-cbc",
+ "DEK-Info: AES-256-CBC",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 32, /* secret length -- 32*8 == 256bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes256
+};
+
+/* [email protected] == aes256-cbc */
+static const LIBSSH2_CRYPT_METHOD
+ libssh2_crypt_method_rijndael_cbc_lysator_liu_se = {
+ "DEK-Info: AES-256-CBC",
+ 16, /* blocksize */
+ 16, /* initial value length */
+ 32, /* secret length -- 32*8 == 256bit */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_aes256
+};
+#endif /* LIBSSH2_AES */
+
+#if LIBSSH2_BLOWFISH
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_blowfish_cbc = {
+ "blowfish-cbc",
+ "",
+ 8, /* blocksize */
+ 8, /* initial value length */
+ 16, /* secret length */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_blowfish
+};
+#endif /* LIBSSH2_BLOWFISH */
+
+#if LIBSSH2_RC4
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour = {
+ "arcfour",
+ "DEK-Info: RC4",
+ 8, /* blocksize */
+ 8, /* initial value length */
+ 16, /* secret length */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_arcfour
+};
+
+static int
+crypt_init_arcfour128(LIBSSH2_SESSION * session,
+ const LIBSSH2_CRYPT_METHOD * method,
+ unsigned char *iv, int *free_iv,
+ unsigned char *secret, int *free_secret,
+ int encrypt, void **abstract)
+{
+ int rc;
+
+ rc = crypt_init(session, method, iv, free_iv, secret, free_secret,
+ encrypt, abstract);
+ if(rc == 0) {
+ struct crypt_ctx *cctx = *(struct crypt_ctx **) abstract;
+ unsigned char block[8];
+ size_t discard = 1536;
+ for(; discard; discard -= 8)
+ _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block,
+ method->blocksize);
+ }
+
+ return rc;
+}
+
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour128 = {
+ "arcfour128",
+ "",
+ 8, /* blocksize */
+ 8, /* initial value length */
+ 16, /* secret length */
+ 0, /* flags */
+ &crypt_init_arcfour128,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_arcfour
+};
+#endif /* LIBSSH2_RC4 */
+
+#if LIBSSH2_CAST
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_cast128_cbc = {
+ "cast128-cbc",
+ "",
+ 8, /* blocksize */
+ 8, /* initial value length */
+ 16, /* secret length */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_cast5
+};
+#endif /* LIBSSH2_CAST */
+
+#if LIBSSH2_3DES
+static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_3des_cbc = {
+ "3des-cbc",
+ "DEK-Info: DES-EDE3-CBC",
+ 8, /* blocksize */
+ 8, /* initial value length */
+ 24, /* secret length */
+ 0, /* flags */
+ &crypt_init,
+ &crypt_encrypt,
+ &crypt_dtor,
+ _libssh2_cipher_3des
+};
+#endif
+
+static const LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = {
+#if LIBSSH2_AES_CTR
+ &libssh2_crypt_method_aes128_ctr,
+ &libssh2_crypt_method_aes192_ctr,
+ &libssh2_crypt_method_aes256_ctr,
+#endif /* LIBSSH2_AES */
+#if LIBSSH2_AES
+ &libssh2_crypt_method_aes256_cbc,
+ &libssh2_crypt_method_rijndael_cbc_lysator_liu_se, /* == aes256-cbc */
+ &libssh2_crypt_method_aes192_cbc,
+ &libssh2_crypt_method_aes128_cbc,
+#endif /* LIBSSH2_AES */
+#if LIBSSH2_BLOWFISH
+ &libssh2_crypt_method_blowfish_cbc,
+#endif /* LIBSSH2_BLOWFISH */
+#if LIBSSH2_RC4
+ &libssh2_crypt_method_arcfour128,
+ &libssh2_crypt_method_arcfour,
+#endif /* LIBSSH2_RC4 */
+#if LIBSSH2_CAST
+ &libssh2_crypt_method_cast128_cbc,
+#endif /* LIBSSH2_CAST */
+#if LIBSSH2_3DES
+ &libssh2_crypt_method_3des_cbc,
+#endif /* LIBSSH2_DES */
+#ifdef LIBSSH2_CRYPT_NONE
+ &libssh2_crypt_method_none,
+#endif
+ NULL
+};
+
+/* Expose to kex.c */
+const LIBSSH2_CRYPT_METHOD **
+libssh2_crypt_methods(void)
+{
+ return _libssh2_crypt_methods;
+}
diff --git a/contrib/libs/libssh2/src/crypto.h b/contrib/libs/libssh2/src/crypto.h
new file mode 100644
index 00000000000..28f2380c3f2
--- /dev/null
+++ b/contrib/libs/libssh2/src/crypto.h
@@ -0,0 +1,248 @@
+#ifndef __LIBSSH2_CRYPTO_H
+#define __LIBSSH2_CRYPTO_H
+/* Copyright (C) 2009, 2010 Simon Josefsson
+ * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved.
+ * Copyright (C) 2010-2019 Daniel Stenberg
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.
+ */
+
+#ifdef LIBSSH2_OPENSSL
+#include "openssl.h"
+#endif
+
+#ifdef LIBSSH2_LIBGCRYPT
+#error #include "libgcrypt.h"
+#endif
+
+#ifdef LIBSSH2_WINCNG
+#error #include "wincng.h"
+#endif
+
+#ifdef LIBSSH2_OS400QC3
+#error #include "os400qc3.h"
+#endif
+
+#ifdef LIBSSH2_MBEDTLS
+#error #include "mbedtls.h"
+#endif
+
+#define LIBSSH2_ED25519_KEY_LEN 32
+#define LIBSSH2_ED25519_PRIVATE_KEY_LEN 64
+#define LIBSSH2_ED25519_SIG_LEN 64
+
+#if LIBSSH2_RSA
+int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata, unsigned long coefflen);
+int _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filename,
+ unsigned const char *passphrase);
+int _libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsa,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m, unsigned long m_len);
+int _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
+ libssh2_rsa_ctx * rsactx,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature,
+ size_t *signature_len);
+int _libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase);
+#endif
+
+#if LIBSSH2_DSA
+int _libssh2_dsa_new(libssh2_dsa_ctx ** dsa,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *gdata,
+ unsigned long glen,
+ const unsigned char *ydata,
+ unsigned long ylen,
+ const unsigned char *x, unsigned long x_len);
+int _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filename,
+ unsigned const char *passphrase);
+int _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx,
+ const unsigned char *sig,
+ const unsigned char *m, unsigned long m_len);
+int _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx,
+ const unsigned char *hash,
+ unsigned long hash_len, unsigned char *sig);
+int _libssh2_dsa_new_private_frommemory(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase);
+#endif
+
+#if LIBSSH2_ECDSA
+int
+_libssh2_ecdsa_curve_name_with_octal_new(libssh2_ecdsa_ctx ** ecdsactx,
+ const unsigned char *k,
+ size_t k_len,
+ libssh2_curve_type type);
+int
+_libssh2_ecdsa_new_private(libssh2_ecdsa_ctx ** ec_ctx,
+ LIBSSH2_SESSION * session,
+ const char *filename,
+ unsigned const char *passphrase);
+
+int
+_libssh2_ecdsa_verify(libssh2_ecdsa_ctx * ctx,
+ const unsigned char *r, size_t r_len,
+ const unsigned char *s, size_t s_len,
+ const unsigned char *m, size_t m_len);
+
+int
+_libssh2_ecdsa_create_key(LIBSSH2_SESSION *session,
+ _libssh2_ec_key **out_private_key,
+ unsigned char **out_public_key_octal,
+ size_t *out_public_key_octal_len,
+ libssh2_curve_type curve_type);
+
+int
+_libssh2_ecdh_gen_k(_libssh2_bn **k, _libssh2_ec_key *private_key,
+ const unsigned char *server_public_key,
+ size_t server_public_key_len);
+
+int
+_libssh2_ecdsa_sign(LIBSSH2_SESSION *session, libssh2_ecdsa_ctx *ec_ctx,
+ const unsigned char *hash, unsigned long hash_len,
+ unsigned char **signature, size_t *signature_len);
+
+int _libssh2_ecdsa_new_private_frommemory(libssh2_ecdsa_ctx ** ec_ctx,
+ LIBSSH2_SESSION * session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase);
+
+libssh2_curve_type
+_libssh2_ecdsa_get_curve_type(libssh2_ecdsa_ctx *ec_ctx);
+
+int
+_libssh2_ecdsa_curve_type_from_name(const char *name,
+ libssh2_curve_type *out_type);
+
+#endif /* LIBSSH2_ECDSA */
+
+#if LIBSSH2_ED25519
+
+int
+_libssh2_curve25519_new(LIBSSH2_SESSION *session, uint8_t **out_public_key,
+ uint8_t **out_private_key);
+
+int
+_libssh2_curve25519_gen_k(_libssh2_bn **k,
+ uint8_t private_key[LIBSSH2_ED25519_KEY_LEN],
+ uint8_t server_public_key[LIBSSH2_ED25519_KEY_LEN]);
+
+int
+_libssh2_ed25519_verify(libssh2_ed25519_ctx *ctx, const uint8_t *s,
+ size_t s_len, const uint8_t *m, size_t m_len);
+
+int
+_libssh2_ed25519_new_private(libssh2_ed25519_ctx **ed_ctx,
+ LIBSSH2_SESSION *session,
+ const char *filename, const uint8_t *passphrase);
+
+int
+_libssh2_ed25519_new_public(libssh2_ed25519_ctx **ed_ctx,
+ LIBSSH2_SESSION *session,
+ const unsigned char *raw_pub_key,
+ const uint8_t key_len);
+
+int
+_libssh2_ed25519_sign(libssh2_ed25519_ctx *ctx, LIBSSH2_SESSION *session,
+ uint8_t **out_sig, size_t *out_sig_len,
+ const uint8_t *message, size_t message_len);
+
+int
+_libssh2_ed25519_new_private_frommemory(libssh2_ed25519_ctx **ed_ctx,
+ LIBSSH2_SESSION *session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase);
+
+#endif /* LIBSSH2_ED25519 */
+
+
+int _libssh2_cipher_init(_libssh2_cipher_ctx * h,
+ _libssh2_cipher_type(algo),
+ unsigned char *iv,
+ unsigned char *secret, int encrypt);
+
+int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
+ _libssh2_cipher_type(algo),
+ int encrypt, unsigned char *block, size_t blocksize);
+
+int _libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase);
+
+int _libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase);
+
+#endif /* __LIBSSH2_CRYPTO_H */
diff --git a/contrib/libs/libssh2/src/global.c b/contrib/libs/libssh2/src/global.c
new file mode 100644
index 00000000000..68289845f0c
--- /dev/null
+++ b/contrib/libs/libssh2/src/global.c
@@ -0,0 +1,78 @@
+/* Copyright (c) 2010 Lars Nordin <[email protected]>
+ * Copyright (C) 2010 Simon Josefsson <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+
+static int _libssh2_initialized = 0;
+static int _libssh2_init_flags = 0;
+
+LIBSSH2_API int
+libssh2_init(int flags)
+{
+ if(_libssh2_initialized == 0 && !(flags & LIBSSH2_INIT_NO_CRYPTO)) {
+ libssh2_crypto_init();
+ }
+
+ _libssh2_initialized++;
+ _libssh2_init_flags |= flags;
+
+ return 0;
+}
+
+LIBSSH2_API void
+libssh2_exit(void)
+{
+ if(_libssh2_initialized == 0)
+ return;
+
+ _libssh2_initialized--;
+
+ if(_libssh2_initialized == 0 &&
+ !(_libssh2_init_flags & LIBSSH2_INIT_NO_CRYPTO)) {
+ libssh2_crypto_exit();
+ }
+
+ return;
+}
+
+void
+_libssh2_init_if_needed(void)
+{
+ if(_libssh2_initialized == 0)
+ (void)libssh2_init (0);
+}
diff --git a/contrib/libs/libssh2/src/hostkey.c b/contrib/libs/libssh2/src/hostkey.c
new file mode 100644
index 00000000000..d87a4c744f4
--- /dev/null
+++ b/contrib/libs/libssh2/src/hostkey.c
@@ -0,0 +1,1168 @@
+/* Copyright (c) 2004-2006, Sara Golemon <[email protected]>
+ * Copyright (c) 2009-2019 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include "misc.h"
+
+/* Needed for struct iovec on some platforms */
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#if LIBSSH2_RSA
+/* ***********
+ * ssh-rsa *
+ *********** */
+
+static int hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION * session,
+ void **abstract);
+
+/*
+ * hostkey_method_ssh_rsa_init
+ *
+ * Initialize the server hostkey working area with e/n pair
+ */
+static int
+hostkey_method_ssh_rsa_init(LIBSSH2_SESSION * session,
+ const unsigned char *hostkey_data,
+ size_t hostkey_data_len,
+ void **abstract)
+{
+ libssh2_rsa_ctx *rsactx;
+ unsigned char *e, *n;
+ size_t e_len, n_len;
+ struct string_buf buf;
+
+ if(*abstract) {
+ hostkey_method_ssh_rsa_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ if(hostkey_data_len < 19) {
+ _libssh2_debug(session, LIBSSH2_TRACE_ERROR,
+ "host key length too short");
+ return -1;
+ }
+
+ buf.data = (unsigned char *)hostkey_data;
+ buf.dataptr = buf.data;
+ buf.len = hostkey_data_len;
+
+ if(_libssh2_match_string(&buf, "ssh-rsa"))
+ return -1;
+
+ if(_libssh2_get_string(&buf, &e, &e_len))
+ return -1;
+
+ if(_libssh2_get_string(&buf, &n, &n_len))
+ return -1;
+
+ if(_libssh2_rsa_new(&rsactx, e, e_len, n, n_len, NULL, 0,
+ NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0)) {
+ return -1;
+ }
+
+ *abstract = rsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_rsa_initPEM
+ *
+ * Load a Private Key from a PEM file
+ */
+static int
+hostkey_method_ssh_rsa_initPEM(LIBSSH2_SESSION * session,
+ const char *privkeyfile,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_rsa_ctx *rsactx;
+ int ret;
+
+ if(*abstract) {
+ hostkey_method_ssh_rsa_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_rsa_new_private(&rsactx, session, privkeyfile, passphrase);
+ if(ret) {
+ return -1;
+ }
+
+ *abstract = rsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_rsa_initPEMFromMemory
+ *
+ * Load a Private Key from a memory
+ */
+static int
+hostkey_method_ssh_rsa_initPEMFromMemory(LIBSSH2_SESSION * session,
+ const char *privkeyfiledata,
+ size_t privkeyfiledata_len,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_rsa_ctx *rsactx;
+ int ret;
+
+ if(*abstract) {
+ hostkey_method_ssh_rsa_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_rsa_new_private_frommemory(&rsactx, session,
+ privkeyfiledata,
+ privkeyfiledata_len, passphrase);
+ if(ret) {
+ return -1;
+ }
+
+ *abstract = rsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_rsa_sign
+ *
+ * Verify signature created by remote
+ */
+static int
+hostkey_method_ssh_rsa_sig_verify(LIBSSH2_SESSION * session,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len, void **abstract)
+{
+ libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
+ (void) session;
+
+ /* Skip past keyname_len(4) + keyname(7){"ssh-rsa"} + signature_len(4) */
+ if(sig_len < 15)
+ return -1;
+
+ sig += 15;
+ sig_len -= 15;
+ return _libssh2_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len);
+}
+
+/*
+ * hostkey_method_ssh_rsa_signv
+ *
+ * Construct a signature from an array of vectors
+ */
+static int
+hostkey_method_ssh_rsa_signv(LIBSSH2_SESSION * session,
+ unsigned char **signature,
+ size_t *signature_len,
+ int veccount,
+ const struct iovec datavec[],
+ void **abstract)
+{
+ libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
+
+#ifdef _libssh2_rsa_sha1_signv
+ return _libssh2_rsa_sha1_signv(session, signature, signature_len,
+ veccount, datavec, rsactx);
+#else
+ int ret;
+ int i;
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ libssh2_sha1_ctx ctx;
+
+ libssh2_sha1_init(&ctx);
+ for(i = 0; i < veccount; i++) {
+ libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len);
+ }
+ libssh2_sha1_final(ctx, hash);
+
+ ret = _libssh2_rsa_sha1_sign(session, rsactx, hash, SHA_DIGEST_LENGTH,
+ signature, signature_len);
+ if(ret) {
+ return -1;
+ }
+
+ return 0;
+#endif
+}
+
+/*
+ * hostkey_method_ssh_rsa_dtor
+ *
+ * Shutdown the hostkey
+ */
+static int
+hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
+ (void) session;
+
+ _libssh2_rsa_free(rsactx);
+
+ *abstract = NULL;
+
+ return 0;
+}
+
+#ifdef OPENSSL_NO_MD5
+#define MD5_DIGEST_LENGTH 16
+#endif
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa = {
+ "ssh-rsa",
+ MD5_DIGEST_LENGTH,
+ hostkey_method_ssh_rsa_init,
+ hostkey_method_ssh_rsa_initPEM,
+ hostkey_method_ssh_rsa_initPEMFromMemory,
+ hostkey_method_ssh_rsa_sig_verify,
+ hostkey_method_ssh_rsa_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_rsa_dtor,
+};
+#endif /* LIBSSH2_RSA */
+
+#if LIBSSH2_DSA
+/* ***********
+ * ssh-dss *
+ *********** */
+
+static int hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION * session,
+ void **abstract);
+
+/*
+ * hostkey_method_ssh_dss_init
+ *
+ * Initialize the server hostkey working area with p/q/g/y set
+ */
+static int
+hostkey_method_ssh_dss_init(LIBSSH2_SESSION * session,
+ const unsigned char *hostkey_data,
+ size_t hostkey_data_len,
+ void **abstract)
+{
+ libssh2_dsa_ctx *dsactx;
+ unsigned char *p, *q, *g, *y;
+ size_t p_len, q_len, g_len, y_len;
+ struct string_buf buf;
+
+ if(*abstract) {
+ hostkey_method_ssh_dss_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ if(hostkey_data_len < 27) {
+ _libssh2_debug(session, LIBSSH2_TRACE_ERROR,
+ "host key length too short");
+ return -1;
+ }
+
+ buf.data = (unsigned char *)hostkey_data;
+ buf.dataptr = buf.data;
+ buf.len = hostkey_data_len;
+
+ if(_libssh2_match_string(&buf, "ssh-dss"))
+ return -1;
+
+ if(_libssh2_get_string(&buf, &p, &p_len))
+ return -1;
+
+ if(_libssh2_get_string(&buf, &q, &q_len))
+ return -1;
+
+ if(_libssh2_get_string(&buf, &g, &g_len))
+ return -1;
+
+ if(_libssh2_get_string(&buf, &y, &y_len))
+ return -1;
+
+ if(_libssh2_dsa_new(&dsactx, p, p_len, q, q_len,
+ g, g_len, y, y_len, NULL, 0)) {
+ return -1;
+ }
+
+ *abstract = dsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_dss_initPEM
+ *
+ * Load a Private Key from a PEM file
+ */
+static int
+hostkey_method_ssh_dss_initPEM(LIBSSH2_SESSION * session,
+ const char *privkeyfile,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_dsa_ctx *dsactx;
+ int ret;
+
+ if(*abstract) {
+ hostkey_method_ssh_dss_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_dsa_new_private(&dsactx, session, privkeyfile, passphrase);
+ if(ret) {
+ return -1;
+ }
+
+ *abstract = dsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_dss_initPEMFromMemory
+ *
+ * Load a Private Key from memory
+ */
+static int
+hostkey_method_ssh_dss_initPEMFromMemory(LIBSSH2_SESSION * session,
+ const char *privkeyfiledata,
+ size_t privkeyfiledata_len,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_dsa_ctx *dsactx;
+ int ret;
+
+ if(*abstract) {
+ hostkey_method_ssh_dss_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_dsa_new_private_frommemory(&dsactx, session,
+ privkeyfiledata,
+ privkeyfiledata_len, passphrase);
+ if(ret) {
+ return -1;
+ }
+
+ *abstract = dsactx;
+
+ return 0;
+}
+
+/*
+ * libssh2_hostkey_method_ssh_dss_sign
+ *
+ * Verify signature created by remote
+ */
+static int
+hostkey_method_ssh_dss_sig_verify(LIBSSH2_SESSION * session,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len, void **abstract)
+{
+ libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract);
+
+ /* Skip past keyname_len(4) + keyname(7){"ssh-dss"} + signature_len(4) */
+ if(sig_len != 55) {
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Invalid DSS signature length");
+ }
+
+ sig += 15;
+ sig_len -= 15;
+
+ return _libssh2_dsa_sha1_verify(dsactx, sig, m, m_len);
+}
+
+/*
+ * hostkey_method_ssh_dss_signv
+ *
+ * Construct a signature from an array of vectors
+ */
+static int
+hostkey_method_ssh_dss_signv(LIBSSH2_SESSION * session,
+ unsigned char **signature,
+ size_t *signature_len,
+ int veccount,
+ const struct iovec datavec[],
+ void **abstract)
+{
+ libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract);
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ libssh2_sha1_ctx ctx;
+ int i;
+
+ *signature = LIBSSH2_CALLOC(session, 2 * SHA_DIGEST_LENGTH);
+ if(!*signature) {
+ return -1;
+ }
+
+ *signature_len = 2 * SHA_DIGEST_LENGTH;
+
+ libssh2_sha1_init(&ctx);
+ for(i = 0; i < veccount; i++) {
+ libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len);
+ }
+ libssh2_sha1_final(ctx, hash);
+
+ if(_libssh2_dsa_sha1_sign(dsactx, hash, SHA_DIGEST_LENGTH, *signature)) {
+ LIBSSH2_FREE(session, *signature);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * libssh2_hostkey_method_ssh_dss_dtor
+ *
+ * Shutdown the hostkey method
+ */
+static int
+hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract);
+ (void) session;
+
+ _libssh2_dsa_free(dsactx);
+
+ *abstract = NULL;
+
+ return 0;
+}
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_dss = {
+ "ssh-dss",
+ MD5_DIGEST_LENGTH,
+ hostkey_method_ssh_dss_init,
+ hostkey_method_ssh_dss_initPEM,
+ hostkey_method_ssh_dss_initPEMFromMemory,
+ hostkey_method_ssh_dss_sig_verify,
+ hostkey_method_ssh_dss_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_dss_dtor,
+};
+#endif /* LIBSSH2_DSA */
+
+#if LIBSSH2_ECDSA
+
+/* ***********
+ * ecdsa-sha2-nistp256/384/521 *
+ *********** */
+
+static int
+hostkey_method_ssh_ecdsa_dtor(LIBSSH2_SESSION * session,
+ void **abstract);
+
+/*
+ * hostkey_method_ssh_ecdsa_init
+ *
+ * Initialize the server hostkey working area with e/n pair
+ */
+static int
+hostkey_method_ssh_ecdsa_init(LIBSSH2_SESSION * session,
+ const unsigned char *hostkey_data,
+ size_t hostkey_data_len,
+ void **abstract)
+{
+ libssh2_ecdsa_ctx *ecdsactx = NULL;
+ unsigned char *type_str, *domain, *public_key;
+ size_t key_len, len;
+ libssh2_curve_type type;
+ struct string_buf buf;
+
+ if(abstract != NULL && *abstract) {
+ hostkey_method_ssh_ecdsa_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ if(hostkey_data_len < 39) {
+ _libssh2_debug(session, LIBSSH2_TRACE_ERROR,
+ "host key length too short");
+ return -1;
+ }
+
+ buf.data = (unsigned char *)hostkey_data;
+ buf.dataptr = buf.data;
+ buf.len = hostkey_data_len;
+
+ if(_libssh2_get_string(&buf, &type_str, &len) || len != 19)
+ return -1;
+
+ if(strncmp((char *) type_str, "ecdsa-sha2-nistp256", 19) == 0) {
+ type = LIBSSH2_EC_CURVE_NISTP256;
+ }
+ else if(strncmp((char *) type_str, "ecdsa-sha2-nistp384", 19) == 0) {
+ type = LIBSSH2_EC_CURVE_NISTP384;
+ }
+ else if(strncmp((char *) type_str, "ecdsa-sha2-nistp521", 19) == 0) {
+ type = LIBSSH2_EC_CURVE_NISTP521;
+ }
+ else {
+ return -1;
+ }
+
+ if(_libssh2_get_string(&buf, &domain, &len) || len != 8)
+ return -1;
+
+ if(type == LIBSSH2_EC_CURVE_NISTP256 &&
+ strncmp((char *)domain, "nistp256", 8) != 0) {
+ return -1;
+ }
+ else if(type == LIBSSH2_EC_CURVE_NISTP384 &&
+ strncmp((char *)domain, "nistp384", 8) != 0) {
+ return -1;
+ }
+ else if(type == LIBSSH2_EC_CURVE_NISTP521 &&
+ strncmp((char *)domain, "nistp521", 8) != 0) {
+ return -1;
+ }
+
+ /* public key */
+ if(_libssh2_get_string(&buf, &public_key, &key_len))
+ return -1;
+
+ if(_libssh2_ecdsa_curve_name_with_octal_new(&ecdsactx, public_key,
+ key_len, type))
+ return -1;
+
+ if(abstract != NULL)
+ *abstract = ecdsactx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_ecdsa_initPEM
+ *
+ * Load a Private Key from a PEM file
+ */
+static int
+hostkey_method_ssh_ecdsa_initPEM(LIBSSH2_SESSION * session,
+ const char *privkeyfile,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_ecdsa_ctx *ec_ctx = NULL;
+ int ret;
+
+ if(abstract != NULL && *abstract) {
+ hostkey_method_ssh_ecdsa_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_ecdsa_new_private(&ec_ctx, session,
+ privkeyfile, passphrase);
+
+ if(abstract != NULL)
+ *abstract = ec_ctx;
+
+ return ret;
+}
+
+/*
+ * hostkey_method_ssh_ecdsa_initPEMFromMemory
+ *
+ * Load a Private Key from memory
+ */
+static int
+hostkey_method_ssh_ecdsa_initPEMFromMemory(LIBSSH2_SESSION * session,
+ const char *privkeyfiledata,
+ size_t privkeyfiledata_len,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_ecdsa_ctx *ec_ctx = NULL;
+ int ret;
+
+ if(abstract != NULL && *abstract) {
+ hostkey_method_ssh_ecdsa_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_ecdsa_new_private_frommemory(&ec_ctx, session,
+ privkeyfiledata,
+ privkeyfiledata_len,
+ passphrase);
+ if(ret) {
+ return -1;
+ }
+
+ if(abstract != NULL)
+ *abstract = ec_ctx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ecdsa_sig_verify
+ *
+ * Verify signature created by remote
+ */
+static int
+hostkey_method_ssh_ecdsa_sig_verify(LIBSSH2_SESSION * session,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len, void **abstract)
+{
+ unsigned char *r, *s, *name;
+ size_t r_len, s_len, name_len;
+ uint32_t len;
+ struct string_buf buf;
+ libssh2_ecdsa_ctx *ctx = (libssh2_ecdsa_ctx *) (*abstract);
+
+ (void) session;
+
+ if(sig_len < 35)
+ return -1;
+
+ /* keyname_len(4) + keyname(19){"ecdsa-sha2-nistp256"} +
+ signature_len(4) */
+ buf.data = (unsigned char *)sig;
+ buf.dataptr = buf.data;
+ buf.len = sig_len;
+
+ if(_libssh2_get_string(&buf, &name, &name_len) || name_len != 19)
+ return -1;
+
+ if(_libssh2_get_u32(&buf, &len) != 0 || len < 8)
+ return -1;
+
+ if(_libssh2_get_string(&buf, &r, &r_len))
+ return -1;
+
+ if(_libssh2_get_string(&buf, &s, &s_len))
+ return -1;
+
+ return _libssh2_ecdsa_verify(ctx, r, r_len, s, s_len, m, m_len);
+}
+
+
+#define LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(digest_type) \
+ { \
+ unsigned char hash[SHA##digest_type##_DIGEST_LENGTH]; \
+ libssh2_sha##digest_type##_ctx ctx; \
+ int i; \
+ libssh2_sha##digest_type##_init(&ctx); \
+ for(i = 0; i < veccount; i++) { \
+ libssh2_sha##digest_type##_update(ctx, datavec[i].iov_base, \
+ datavec[i].iov_len); \
+ } \
+ libssh2_sha##digest_type##_final(ctx, hash); \
+ ret = _libssh2_ecdsa_sign(session, ec_ctx, hash, \
+ SHA##digest_type##_DIGEST_LENGTH, \
+ signature, signature_len); \
+ }
+
+
+/*
+ * hostkey_method_ecdsa_signv
+ *
+ * Construct a signature from an array of vectors
+ */
+static int
+hostkey_method_ssh_ecdsa_signv(LIBSSH2_SESSION * session,
+ unsigned char **signature,
+ size_t *signature_len,
+ int veccount,
+ const struct iovec datavec[],
+ void **abstract)
+{
+ libssh2_ecdsa_ctx *ec_ctx = (libssh2_ecdsa_ctx *) (*abstract);
+ libssh2_curve_type type = _libssh2_ecdsa_get_curve_type(ec_ctx);
+ int ret = 0;
+
+ if(type == LIBSSH2_EC_CURVE_NISTP256) {
+ LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(256);
+ }
+ else if(type == LIBSSH2_EC_CURVE_NISTP384) {
+ LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(384);
+ }
+ else if(type == LIBSSH2_EC_CURVE_NISTP521) {
+ LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(512);
+ }
+ else {
+ return -1;
+ }
+
+ return ret;
+}
+
+/*
+ * hostkey_method_ssh_ecdsa_dtor
+ *
+ * Shutdown the hostkey by freeing EC_KEY context
+ */
+static int
+hostkey_method_ssh_ecdsa_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ libssh2_ecdsa_ctx *keyctx = (libssh2_ecdsa_ctx *) (*abstract);
+ (void) session;
+
+ if(keyctx != NULL)
+ _libssh2_ecdsa_free(keyctx);
+
+ *abstract = NULL;
+
+ return 0;
+}
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp256 = {
+ "ecdsa-sha2-nistp256",
+ SHA256_DIGEST_LENGTH,
+ hostkey_method_ssh_ecdsa_init,
+ hostkey_method_ssh_ecdsa_initPEM,
+ hostkey_method_ssh_ecdsa_initPEMFromMemory,
+ hostkey_method_ssh_ecdsa_sig_verify,
+ hostkey_method_ssh_ecdsa_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_ecdsa_dtor,
+};
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp384 = {
+ "ecdsa-sha2-nistp384",
+ SHA384_DIGEST_LENGTH,
+ hostkey_method_ssh_ecdsa_init,
+ hostkey_method_ssh_ecdsa_initPEM,
+ hostkey_method_ssh_ecdsa_initPEMFromMemory,
+ hostkey_method_ssh_ecdsa_sig_verify,
+ hostkey_method_ssh_ecdsa_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_ecdsa_dtor,
+};
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp521 = {
+ "ecdsa-sha2-nistp521",
+ SHA512_DIGEST_LENGTH,
+ hostkey_method_ssh_ecdsa_init,
+ hostkey_method_ssh_ecdsa_initPEM,
+ hostkey_method_ssh_ecdsa_initPEMFromMemory,
+ hostkey_method_ssh_ecdsa_sig_verify,
+ hostkey_method_ssh_ecdsa_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_ecdsa_dtor,
+};
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp256_cert = {
+ SHA256_DIGEST_LENGTH,
+ NULL,
+ hostkey_method_ssh_ecdsa_initPEM,
+ hostkey_method_ssh_ecdsa_initPEMFromMemory,
+ NULL,
+ hostkey_method_ssh_ecdsa_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_ecdsa_dtor,
+};
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp384_cert = {
+ SHA384_DIGEST_LENGTH,
+ NULL,
+ hostkey_method_ssh_ecdsa_initPEM,
+ hostkey_method_ssh_ecdsa_initPEMFromMemory,
+ NULL,
+ hostkey_method_ssh_ecdsa_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_ecdsa_dtor,
+};
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp521_cert = {
+ SHA512_DIGEST_LENGTH,
+ NULL,
+ hostkey_method_ssh_ecdsa_initPEM,
+ hostkey_method_ssh_ecdsa_initPEMFromMemory,
+ NULL,
+ hostkey_method_ssh_ecdsa_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_ecdsa_dtor,
+};
+
+#endif /* LIBSSH2_ECDSA */
+
+#if LIBSSH2_ED25519
+
+/* ***********
+ * ed25519 *
+ *********** */
+
+static int hostkey_method_ssh_ed25519_dtor(LIBSSH2_SESSION * session,
+ void **abstract);
+
+/*
+ * hostkey_method_ssh_ed25519_init
+ *
+ * Initialize the server hostkey working area with e/n pair
+ */
+static int
+hostkey_method_ssh_ed25519_init(LIBSSH2_SESSION * session,
+ const unsigned char *hostkey_data,
+ size_t hostkey_data_len,
+ void **abstract)
+{
+ const unsigned char *s;
+ unsigned long len, key_len;
+ libssh2_ed25519_ctx *ctx = NULL;
+
+ if(*abstract) {
+ hostkey_method_ssh_ed25519_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ if(hostkey_data_len < 19) {
+ _libssh2_debug(session, LIBSSH2_TRACE_ERROR,
+ "host key length too short");
+ return -1;
+ }
+
+ s = hostkey_data;
+ len = _libssh2_ntohu32(s);
+ s += 4;
+
+ if(len != 11 || strncmp((char *) s, "ssh-ed25519", 11) != 0) {
+ return -1;
+ }
+
+ s += 11;
+
+ /* public key */
+ key_len = _libssh2_ntohu32(s);
+ s += 4;
+
+ if(_libssh2_ed25519_new_public(&ctx, session, s, key_len) != 0) {
+ return -1;
+ }
+
+ *abstract = ctx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_ed25519_initPEM
+ *
+ * Load a Private Key from a PEM file
+ */
+static int
+hostkey_method_ssh_ed25519_initPEM(LIBSSH2_SESSION * session,
+ const char *privkeyfile,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_ed25519_ctx *ec_ctx = NULL;
+ int ret;
+
+ if(*abstract) {
+ hostkey_method_ssh_ed25519_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_ed25519_new_private(&ec_ctx, session,
+ privkeyfile, passphrase);
+ if(ret) {
+ return -1;
+ }
+
+ *abstract = ec_ctx;
+
+ return ret;
+}
+
+/*
+ * hostkey_method_ssh_ed25519_initPEMFromMemory
+ *
+ * Load a Private Key from memory
+ */
+static int
+hostkey_method_ssh_ed25519_initPEMFromMemory(LIBSSH2_SESSION * session,
+ const char *privkeyfiledata,
+ size_t privkeyfiledata_len,
+ unsigned const char *passphrase,
+ void **abstract)
+{
+ libssh2_ed25519_ctx *ed_ctx = NULL;
+ int ret;
+
+ if(abstract != NULL && *abstract) {
+ hostkey_method_ssh_ed25519_dtor(session, abstract);
+ *abstract = NULL;
+ }
+
+ ret = _libssh2_ed25519_new_private_frommemory(&ed_ctx, session,
+ privkeyfiledata,
+ privkeyfiledata_len,
+ passphrase);
+ if(ret) {
+ return -1;
+ }
+
+ if(abstract != NULL)
+ *abstract = ed_ctx;
+
+ return 0;
+}
+
+/*
+ * hostkey_method_ssh_ed25519_sig_verify
+ *
+ * Verify signature created by remote
+ */
+static int
+hostkey_method_ssh_ed25519_sig_verify(LIBSSH2_SESSION * session,
+ const unsigned char *sig,
+ size_t sig_len,
+ const unsigned char *m,
+ size_t m_len, void **abstract)
+{
+ libssh2_ed25519_ctx *ctx = (libssh2_ed25519_ctx *) (*abstract);
+ (void) session;
+
+ if(sig_len < 19)
+ return -1;
+
+ /* Skip past keyname_len(4) + keyname(11){"ssh-ed25519"} +
+ signature_len(4) */
+ sig += 19;
+ sig_len -= 19;
+
+ if(sig_len != LIBSSH2_ED25519_SIG_LEN)
+ return -1;
+
+ return _libssh2_ed25519_verify(ctx, sig, sig_len, m, m_len);
+}
+
+/*
+ * hostkey_method_ssh_ed25519_signv
+ *
+ * Construct a signature from an array of vectors
+ */
+static int
+hostkey_method_ssh_ed25519_signv(LIBSSH2_SESSION * session,
+ unsigned char **signature,
+ size_t *signature_len,
+ int veccount,
+ const struct iovec datavec[],
+ void **abstract)
+{
+ libssh2_ed25519_ctx *ctx = (libssh2_ed25519_ctx *) (*abstract);
+
+ if(veccount != 1) {
+ return -1;
+ }
+
+ return _libssh2_ed25519_sign(ctx, session, signature, signature_len,
+ datavec[0].iov_base, datavec[0].iov_len);
+}
+
+
+/*
+ * hostkey_method_ssh_ed25519_dtor
+ *
+ * Shutdown the hostkey by freeing key context
+ */
+static int
+hostkey_method_ssh_ed25519_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ libssh2_ed25519_ctx *keyctx = (libssh2_ed25519_ctx*) (*abstract);
+ (void) session;
+
+ if(keyctx)
+ _libssh2_ed25519_free(keyctx);
+
+ *abstract = NULL;
+
+ return 0;
+}
+
+static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_ed25519 = {
+ "ssh-ed25519",
+ SHA256_DIGEST_LENGTH,
+ hostkey_method_ssh_ed25519_init,
+ hostkey_method_ssh_ed25519_initPEM,
+ hostkey_method_ssh_ed25519_initPEMFromMemory,
+ hostkey_method_ssh_ed25519_sig_verify,
+ hostkey_method_ssh_ed25519_signv,
+ NULL, /* encrypt */
+ hostkey_method_ssh_ed25519_dtor,
+};
+
+#endif /*LIBSSH2_ED25519*/
+
+
+static const LIBSSH2_HOSTKEY_METHOD *hostkey_methods[] = {
+#if LIBSSH2_ECDSA
+ &hostkey_method_ecdsa_ssh_nistp256,
+ &hostkey_method_ecdsa_ssh_nistp384,
+ &hostkey_method_ecdsa_ssh_nistp521,
+ &hostkey_method_ecdsa_ssh_nistp256_cert,
+ &hostkey_method_ecdsa_ssh_nistp384_cert,
+ &hostkey_method_ecdsa_ssh_nistp521_cert,
+#endif
+#if LIBSSH2_ED25519
+ &hostkey_method_ssh_ed25519,
+#endif
+#if LIBSSH2_RSA
+ &hostkey_method_ssh_rsa,
+#endif /* LIBSSH2_RSA */
+#if LIBSSH2_DSA
+ &hostkey_method_ssh_dss,
+#endif /* LIBSSH2_DSA */
+ NULL
+};
+
+const LIBSSH2_HOSTKEY_METHOD **
+libssh2_hostkey_methods(void)
+{
+ return hostkey_methods;
+}
+
+/*
+ * libssh2_hostkey_hash
+ *
+ * Returns hash signature
+ * Returned buffer should NOT be freed
+ * Length of buffer is determined by hash type
+ * i.e. MD5 == 16, SHA1 == 20, SHA256 == 32
+ */
+LIBSSH2_API const char *
+libssh2_hostkey_hash(LIBSSH2_SESSION * session, int hash_type)
+{
+ switch(hash_type) {
+#if LIBSSH2_MD5
+ case LIBSSH2_HOSTKEY_HASH_MD5:
+ return (session->server_hostkey_md5_valid)
+ ? (char *) session->server_hostkey_md5
+ : NULL;
+ break;
+#endif /* LIBSSH2_MD5 */
+ case LIBSSH2_HOSTKEY_HASH_SHA1:
+ return (session->server_hostkey_sha1_valid)
+ ? (char *) session->server_hostkey_sha1
+ : NULL;
+ break;
+ case LIBSSH2_HOSTKEY_HASH_SHA256:
+ return (session->server_hostkey_sha256_valid)
+ ? (char *) session->server_hostkey_sha256
+ : NULL;
+ break;
+ default:
+ return NULL;
+ }
+}
+
+static int hostkey_type(const unsigned char *hostkey, size_t len)
+{
+ static const unsigned char rsa[] = {
+ 0, 0, 0, 0x07, 's', 's', 'h', '-', 'r', 's', 'a'
+ };
+ static const unsigned char dss[] = {
+ 0, 0, 0, 0x07, 's', 's', 'h', '-', 'd', 's', 's'
+ };
+ static const unsigned char ecdsa_256[] = {
+ 0, 0, 0, 0x13, 'e', 'c', 'd', 's', 'a', '-', 's', 'h', 'a', '2', '-',
+ 'n', 'i', 's', 't', 'p', '2', '5', '6'
+ };
+ static const unsigned char ecdsa_384[] = {
+ 0, 0, 0, 0x13, 'e', 'c', 'd', 's', 'a', '-', 's', 'h', 'a', '2', '-',
+ 'n', 'i', 's', 't', 'p', '3', '8', '4'
+ };
+ static const unsigned char ecdsa_521[] = {
+ 0, 0, 0, 0x13, 'e', 'c', 'd', 's', 'a', '-', 's', 'h', 'a', '2', '-',
+ 'n', 'i', 's', 't', 'p', '5', '2', '1'
+ };
+ static const unsigned char ed25519[] = {
+ 0, 0, 0, 0x0b, 's', 's', 'h', '-', 'e', 'd', '2', '5', '5', '1', '9'
+ };
+
+ if(len < 11)
+ return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
+
+ if(!memcmp(rsa, hostkey, 11))
+ return LIBSSH2_HOSTKEY_TYPE_RSA;
+
+ if(!memcmp(dss, hostkey, 11))
+ return LIBSSH2_HOSTKEY_TYPE_DSS;
+
+ if(len < 15)
+ return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
+
+ if(!memcmp(ed25519, hostkey, 15))
+ return LIBSSH2_HOSTKEY_TYPE_ED25519;
+
+ if(len < 23)
+ return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
+
+ if(!memcmp(ecdsa_256, hostkey, 23))
+ return LIBSSH2_HOSTKEY_TYPE_ECDSA_256;
+
+ if(!memcmp(ecdsa_384, hostkey, 23))
+ return LIBSSH2_HOSTKEY_TYPE_ECDSA_384;
+
+ if(!memcmp(ecdsa_521, hostkey, 23))
+ return LIBSSH2_HOSTKEY_TYPE_ECDSA_521;
+
+ return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
+}
+
+/*
+ * libssh2_session_hostkey()
+ *
+ * Returns the server key and length.
+ *
+ */
+LIBSSH2_API const char *
+libssh2_session_hostkey(LIBSSH2_SESSION *session, size_t *len, int *type)
+{
+ if(session->server_hostkey_len) {
+ if(len)
+ *len = session->server_hostkey_len;
+ if(type)
+ *type = hostkey_type(session->server_hostkey,
+ session->server_hostkey_len);
+ return (char *) session->server_hostkey;
+ }
+ if(len)
+ *len = 0;
+ return NULL;
+}
diff --git a/contrib/libs/libssh2/src/keepalive.c b/contrib/libs/libssh2/src/keepalive.c
new file mode 100644
index 00000000000..2151b171009
--- /dev/null
+++ b/contrib/libs/libssh2/src/keepalive.c
@@ -0,0 +1,100 @@
+/* Copyright (C) 2010 Simon Josefsson
+ * Author: Simon Josefsson
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include "transport.h" /* _libssh2_transport_write */
+
+/* Keep-alive stuff. */
+
+LIBSSH2_API void
+libssh2_keepalive_config (LIBSSH2_SESSION *session,
+ int want_reply,
+ unsigned interval)
+{
+ if(interval == 1)
+ session->keepalive_interval = 2;
+ else
+ session->keepalive_interval = interval;
+ session->keepalive_want_reply = want_reply ? 1 : 0;
+}
+
+LIBSSH2_API int
+libssh2_keepalive_send (LIBSSH2_SESSION *session,
+ int *seconds_to_next)
+{
+ time_t now;
+
+ if(!session->keepalive_interval) {
+ if(seconds_to_next)
+ *seconds_to_next = 0;
+ return 0;
+ }
+
+ now = time(NULL);
+
+ if(session->keepalive_last_sent + session->keepalive_interval <= now) {
+ /* Format is
+ "SSH_MSG_GLOBAL_REQUEST || 4-byte len || str || want-reply". */
+ unsigned char keepalive_data[]
+ = "\x50\x00\x00\x00\[email protected]";
+ size_t len = sizeof(keepalive_data) - 1;
+ int rc;
+
+ keepalive_data[len - 1] =
+ (unsigned char)session->keepalive_want_reply;
+
+ rc = _libssh2_transport_send(session, keepalive_data, len, NULL, 0);
+ /* Silently ignore PACKET_EAGAIN here: if the write buffer is
+ already full, sending another keepalive is not useful. */
+ if(rc && rc != LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send keepalive message");
+ return rc;
+ }
+
+ session->keepalive_last_sent = now;
+ if(seconds_to_next)
+ *seconds_to_next = session->keepalive_interval;
+ }
+ else if(seconds_to_next) {
+ *seconds_to_next = (int) (session->keepalive_last_sent - now)
+ + session->keepalive_interval;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/libssh2/src/kex.c b/contrib/libs/libssh2/src/kex.c
new file mode 100644
index 00000000000..9f3ef79925f
--- /dev/null
+++ b/contrib/libs/libssh2/src/kex.c
@@ -0,0 +1,4141 @@
+/* Copyright (c) 2004-2007, Sara Golemon <[email protected]>
+ * Copyright (c) 2010-2019, Daniel Stenberg <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+
+#include "transport.h"
+#include "comp.h"
+#include "mac.h"
+
+#include <assert.h>
+
+/* define SHA1_DIGEST_LENGTH for the macro below */
+#ifndef SHA1_DIGEST_LENGTH
+#define SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH
+#endif
+
+/* TODO: Switch this to an inline and handle alloc() failures */
+/* Helper macro called from
+ kex_method_diffie_hellman_group1_sha1_key_exchange */
+
+#define LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(value, reqlen, version) \
+ { \
+ if(type == LIBSSH2_EC_CURVE_NISTP256) { \
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, value, reqlen, version); \
+ } \
+ else if(type == LIBSSH2_EC_CURVE_NISTP384) { \
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(384, value, reqlen, version); \
+ } \
+ else if(type == LIBSSH2_EC_CURVE_NISTP521) { \
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(512, value, reqlen, version); \
+ } \
+ } \
+
+
+#define LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(digest_type, value, \
+ reqlen, version) \
+{ \
+ libssh2_sha##digest_type##_ctx hash; \
+ unsigned long len = 0; \
+ if(!(value)) { \
+ value = LIBSSH2_ALLOC(session, \
+ reqlen + SHA##digest_type##_DIGEST_LENGTH); \
+ } \
+ if(value) \
+ while(len < (unsigned long)reqlen) { \
+ libssh2_sha##digest_type##_init(&hash); \
+ libssh2_sha##digest_type##_update(hash, \
+ exchange_state->k_value, \
+ exchange_state->k_value_len); \
+ libssh2_sha##digest_type##_update(hash, \
+ exchange_state->h_sig_comp, \
+ SHA##digest_type##_DIGEST_LENGTH); \
+ if(len > 0) { \
+ libssh2_sha##digest_type##_update(hash, value, len); \
+ } \
+ else { \
+ libssh2_sha##digest_type##_update(hash, (version), 1); \
+ libssh2_sha##digest_type##_update(hash, session->session_id,\
+ session->session_id_len); \
+ } \
+ libssh2_sha##digest_type##_final(hash, (value) + len); \
+ len += SHA##digest_type##_DIGEST_LENGTH; \
+ } \
+}
+
+/*!
+ * @note The following are wrapper functions used by diffie_hellman_sha_algo().
+ * TODO: Switch backend SHA macros to functions to allow function pointers
+ * @discussion Ideally these would be function pointers but the backend macros
+ * don't allow it so we have to wrap them up in helper functions
+ */
+
+static void _libssh2_sha_algo_ctx_init(int sha_algo, void *ctx)
+{
+ if(sha_algo == 512) {
+ libssh2_sha512_init((libssh2_sha512_ctx*)ctx);
+ }
+ else if(sha_algo == 384) {
+ libssh2_sha384_init((libssh2_sha384_ctx*)ctx);
+ }
+ else if(sha_algo == 256) {
+ libssh2_sha256_init((libssh2_sha256_ctx*)ctx);
+ }
+ else if(sha_algo == 1) {
+ libssh2_sha1_init((libssh2_sha1_ctx*)ctx);
+ }
+ else {
+ assert(0);
+ }
+}
+
+static void _libssh2_sha_algo_ctx_update(int sha_algo, void *ctx,
+ void *data, size_t len)
+{
+ if(sha_algo == 512) {
+ libssh2_sha512_ctx *_ctx = (libssh2_sha512_ctx*)ctx;
+ libssh2_sha512_update(*_ctx, data, len);
+ }
+ else if(sha_algo == 384) {
+ libssh2_sha384_ctx *_ctx = (libssh2_sha384_ctx*)ctx;
+ libssh2_sha384_update(*_ctx, data, len);
+ }
+ else if(sha_algo == 256) {
+ libssh2_sha256_ctx *_ctx = (libssh2_sha256_ctx*)ctx;
+ libssh2_sha256_update(*_ctx, data, len);
+ }
+ else if(sha_algo == 1) {
+ libssh2_sha1_ctx *_ctx = (libssh2_sha1_ctx*)ctx;
+ libssh2_sha1_update(*_ctx, data, len);
+ }
+ else {
+#if LIBSSH2DEBUG
+ assert(0);
+#endif
+ }
+}
+
+static void _libssh2_sha_algo_ctx_final(int sha_algo, void *ctx,
+ void *hash)
+{
+ if(sha_algo == 512) {
+ libssh2_sha512_ctx *_ctx = (libssh2_sha512_ctx*)ctx;
+ libssh2_sha512_final(*_ctx, hash);
+ }
+ else if(sha_algo == 384) {
+ libssh2_sha384_ctx *_ctx = (libssh2_sha384_ctx*)ctx;
+ libssh2_sha384_final(*_ctx, hash);
+ }
+ else if(sha_algo == 256) {
+ libssh2_sha256_ctx *_ctx = (libssh2_sha256_ctx*)ctx;
+ libssh2_sha256_final(*_ctx, hash);
+ }
+ else if(sha_algo == 1) {
+ libssh2_sha1_ctx *_ctx = (libssh2_sha1_ctx*)ctx;
+ libssh2_sha1_final(*_ctx, hash);
+ }
+ else {
+#if LIBSSH2DEBUG
+ assert(0);
+#endif
+ }
+}
+
+static void _libssh2_sha_algo_value_hash(int sha_algo,
+ LIBSSH2_SESSION *session,
+ kmdhgGPshakex_state_t *exchange_state,
+ unsigned char **data, size_t data_len,
+ const unsigned char *version)
+{
+ if(sha_algo == 512) {
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(512, *data, data_len, version);
+ }
+ else if(sha_algo == 384) {
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(384, *data, data_len, version);
+ }
+ else if(sha_algo == 256) {
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, *data, data_len, version);
+ }
+ else if(sha_algo == 1) {
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(1, *data, data_len, version);
+ }
+ else {
+#if LIBSSH2DEBUG
+ assert(0);
+#endif
+ }
+}
+
+
+/*!
+ * @function diffie_hellman_sha_algo
+ * @abstract Diffie Hellman Key Exchange, Group Agnostic,
+ * SHA Algorithm Agnostic
+ * @result 0 on success, error code on failure
+ */
+static int diffie_hellman_sha_algo(LIBSSH2_SESSION *session,
+ _libssh2_bn *g,
+ _libssh2_bn *p,
+ int group_order,
+ int sha_algo_value,
+ void *exchange_hash_ctx,
+ unsigned char packet_type_init,
+ unsigned char packet_type_reply,
+ unsigned char *midhash,
+ unsigned long midhash_len,
+ kmdhgGPshakex_state_t *exchange_state)
+{
+ int ret = 0;
+ int rc;
+
+ int digest_len = 0;
+
+ if(sha_algo_value == 512)
+ digest_len = SHA512_DIGEST_LENGTH;
+ else if(sha_algo_value == 384)
+ digest_len = SHA384_DIGEST_LENGTH;
+ else if(sha_algo_value == 256)
+ digest_len = SHA256_DIGEST_LENGTH;
+ else if(sha_algo_value == 1)
+ digest_len = SHA1_DIGEST_LENGTH;
+ else {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "sha algo value is unimplemented");
+ goto clean_exit;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_idle) {
+ /* Setup initial values */
+ exchange_state->e_packet = NULL;
+ exchange_state->s_packet = NULL;
+ exchange_state->k_value = NULL;
+ exchange_state->ctx = _libssh2_bn_ctx_new();
+ libssh2_dh_init(&exchange_state->x);
+ exchange_state->e = _libssh2_bn_init(); /* g^x mod p */
+ exchange_state->f = _libssh2_bn_init_from_bin(); /* g^(Random from
+ server) mod p */
+ exchange_state->k = _libssh2_bn_init(); /* The shared secret: f^x mod
+ p */
+
+ /* Zero the whole thing out */
+ memset(&exchange_state->req_state, 0, sizeof(packet_require_state_t));
+
+ /* Generate x and e */
+ if(_libssh2_bn_bits(p) > LIBSSH2_DH_MAX_MODULUS_BITS) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "dh modulus value is too large");
+ goto clean_exit;
+ }
+
+ rc = libssh2_dh_key_pair(&exchange_state->x, exchange_state->e, g, p,
+ group_order, exchange_state->ctx);
+ if(rc)
+ goto clean_exit;
+
+ /* Send KEX init */
+ /* packet_type(1) + String Length(4) + leading 0(1) */
+ exchange_state->e_packet_len =
+ _libssh2_bn_bytes(exchange_state->e) + 6;
+ if(_libssh2_bn_bits(exchange_state->e) % 8) {
+ /* Leading 00 not needed */
+ exchange_state->e_packet_len--;
+ }
+
+ exchange_state->e_packet =
+ LIBSSH2_ALLOC(session, exchange_state->e_packet_len);
+ if(!exchange_state->e_packet) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Out of memory error");
+ goto clean_exit;
+ }
+ exchange_state->e_packet[0] = packet_type_init;
+ _libssh2_htonu32(exchange_state->e_packet + 1,
+ exchange_state->e_packet_len - 5);
+ if(_libssh2_bn_bits(exchange_state->e) % 8) {
+ _libssh2_bn_to_bin(exchange_state->e,
+ exchange_state->e_packet + 5);
+ }
+ else {
+ exchange_state->e_packet[5] = 0;
+ _libssh2_bn_to_bin(exchange_state->e,
+ exchange_state->e_packet + 6);
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending KEX packet %d",
+ (int) packet_type_init);
+ exchange_state->state = libssh2_NB_state_created;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, exchange_state->e_packet,
+ exchange_state->e_packet_len,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send KEX init message");
+ goto clean_exit;
+ }
+ exchange_state->state = libssh2_NB_state_sent;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_sent) {
+ if(session->burn_optimistic_kexinit) {
+ /* The first KEX packet to come along will be the guess initially
+ * sent by the server. That guess turned out to be wrong so we
+ * need to silently ignore it */
+ int burn_type;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Waiting for badly guessed KEX packet "
+ "(to be ignored)");
+ burn_type =
+ _libssh2_packet_burn(session, &exchange_state->burn_state);
+ if(burn_type == LIBSSH2_ERROR_EAGAIN) {
+ return burn_type;
+ }
+ else if(burn_type <= 0) {
+ /* Failed to receive a packet */
+ ret = burn_type;
+ goto clean_exit;
+ }
+ session->burn_optimistic_kexinit = 0;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Burnt packet of type: %02x",
+ (unsigned int) burn_type);
+ }
+
+ exchange_state->state = libssh2_NB_state_sent1;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_sent1) {
+ /* Wait for KEX reply */
+ struct string_buf buf;
+ size_t host_key_len;
+
+ rc = _libssh2_packet_require(session, packet_type_reply,
+ &exchange_state->s_packet,
+ &exchange_state->s_packet_len, 0, NULL,
+ 0, &exchange_state->req_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ if(rc) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "Timed out waiting for KEX reply");
+ goto clean_exit;
+ }
+
+ /* Parse KEXDH_REPLY */
+ if(exchange_state->s_packet_len < 5) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected packet length");
+ goto clean_exit;
+ }
+
+ buf.data = exchange_state->s_packet;
+ buf.len = exchange_state->s_packet_len;
+ buf.dataptr = buf.data;
+ buf.dataptr++; /* advance past type */
+
+ if(session->server_hostkey)
+ LIBSSH2_FREE(session, session->server_hostkey);
+
+ if(_libssh2_copy_string(session, &buf, &(session->server_hostkey),
+ &host_key_len)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Could not copy host key");
+ goto clean_exit;
+ }
+
+ session->server_hostkey_len = (uint32_t)host_key_len;
+
+#if LIBSSH2_MD5
+ {
+ libssh2_md5_ctx fingerprint_ctx;
+
+ if(libssh2_md5_init(&fingerprint_ctx)) {
+ libssh2_md5_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_md5_final(fingerprint_ctx,
+ session->server_hostkey_md5);
+ session->server_hostkey_md5_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_md5_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char fingerprint[50], *fprint = fingerprint;
+ int i;
+ for(i = 0; i < 16; i++, fprint += 3) {
+ snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]);
+ }
+ *(--fprint) = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's MD5 Fingerprint: %s", fingerprint);
+ }
+#endif /* LIBSSH2DEBUG */
+#endif /* ! LIBSSH2_MD5 */
+
+ {
+ libssh2_sha1_ctx fingerprint_ctx;
+
+ if(libssh2_sha1_init(&fingerprint_ctx)) {
+ libssh2_sha1_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_sha1_final(fingerprint_ctx,
+ session->server_hostkey_sha1);
+ session->server_hostkey_sha1_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_sha1_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char fingerprint[64], *fprint = fingerprint;
+ int i;
+
+ for(i = 0; i < 20; i++, fprint += 3) {
+ snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]);
+ }
+ *(--fprint) = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's SHA1 Fingerprint: %s", fingerprint);
+ }
+#endif /* LIBSSH2DEBUG */
+
+ {
+ libssh2_sha256_ctx fingerprint_ctx;
+
+ if(libssh2_sha256_init(&fingerprint_ctx)) {
+ libssh2_sha256_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_sha256_final(fingerprint_ctx,
+ session->server_hostkey_sha256);
+ session->server_hostkey_sha256_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_sha256_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char *base64Fingerprint = NULL;
+ _libssh2_base64_encode(session,
+ (const char *)
+ session->server_hostkey_sha256,
+ SHA256_DIGEST_LENGTH, &base64Fingerprint);
+ if(base64Fingerprint != NULL) {
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's SHA256 Fingerprint: %s",
+ base64Fingerprint);
+ LIBSSH2_FREE(session, base64Fingerprint);
+ }
+ }
+#endif /* LIBSSH2DEBUG */
+
+
+ if(session->hostkey->init(session, session->server_hostkey,
+ session->server_hostkey_len,
+ &session->server_hostkey_abstract)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Unable to initialize hostkey importer");
+ goto clean_exit;
+ }
+
+ if(_libssh2_get_string(&buf, &(exchange_state->f_value),
+ &(exchange_state->f_value_len))) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Unable to get f value");
+ goto clean_exit;
+ }
+
+ _libssh2_bn_from_bin(exchange_state->f, exchange_state->f_value_len,
+ exchange_state->f_value);
+
+ if(_libssh2_get_string(&buf, &(exchange_state->h_sig),
+ &(exchange_state->h_sig_len))) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Unable to get h sig");
+ goto clean_exit;
+ }
+
+ /* Compute the shared secret */
+ libssh2_dh_secret(&exchange_state->x, exchange_state->k,
+ exchange_state->f, p, exchange_state->ctx);
+ exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5;
+ if(_libssh2_bn_bits(exchange_state->k) % 8) {
+ /* don't need leading 00 */
+ exchange_state->k_value_len--;
+ }
+ exchange_state->k_value =
+ LIBSSH2_ALLOC(session, exchange_state->k_value_len);
+ if(!exchange_state->k_value) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate buffer for K");
+ goto clean_exit;
+ }
+ _libssh2_htonu32(exchange_state->k_value,
+ exchange_state->k_value_len - 4);
+ if(_libssh2_bn_bits(exchange_state->k) % 8) {
+ _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
+ }
+ else {
+ exchange_state->k_value[4] = 0;
+ _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
+ }
+
+ exchange_state->exchange_hash = (void *)&exchange_hash_ctx;
+ _libssh2_sha_algo_ctx_init(sha_algo_value, exchange_hash_ctx);
+
+ if(session->local.banner) {
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ strlen((char *) session->local.banner) - 2);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ session->local.banner,
+ strlen((char *) session->local.banner) - 2);
+ }
+ else {
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ (unsigned char *)
+ LIBSSH2_SSH_DEFAULT_BANNER,
+ sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
+ }
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ strlen((char *) session->remote.banner));
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ session->remote.banner,
+ strlen((char *) session->remote.banner));
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ session->local.kexinit_len);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ session->local.kexinit,
+ session->local.kexinit_len);
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ session->remote.kexinit_len);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ session->remote.kexinit,
+ session->remote.kexinit_len);
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ session->server_hostkey_len);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ session->server_hostkey,
+ session->server_hostkey_len);
+
+ if(packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) {
+ /* diffie-hellman-group-exchange hashes additional fields */
+#ifdef LIBSSH2_DH_GEX_NEW
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ LIBSSH2_DH_GEX_MINGROUP);
+ _libssh2_htonu32(exchange_state->h_sig_comp + 4,
+ LIBSSH2_DH_GEX_OPTGROUP);
+ _libssh2_htonu32(exchange_state->h_sig_comp + 8,
+ LIBSSH2_DH_GEX_MAXGROUP);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->h_sig_comp, 12);
+#else
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ LIBSSH2_DH_GEX_OPTGROUP);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+#endif
+ }
+
+ if(midhash) {
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ midhash, midhash_len);
+ }
+
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->e_packet + 1,
+ exchange_state->e_packet_len - 1);
+
+ _libssh2_htonu32(exchange_state->h_sig_comp,
+ exchange_state->f_value_len);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->h_sig_comp, 4);
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->f_value,
+ exchange_state->f_value_len);
+
+ _libssh2_sha_algo_ctx_update(sha_algo_value, exchange_hash_ctx,
+ exchange_state->k_value,
+ exchange_state->k_value_len);
+
+ _libssh2_sha_algo_ctx_final(sha_algo_value, exchange_hash_ctx,
+ exchange_state->h_sig_comp);
+
+ if(session->hostkey->
+ sig_verify(session, exchange_state->h_sig,
+ exchange_state->h_sig_len, exchange_state->h_sig_comp,
+ digest_len, &session->server_hostkey_abstract)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN,
+ "Unable to verify hostkey signature");
+ goto clean_exit;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending NEWKEYS message");
+ exchange_state->c = SSH_MSG_NEWKEYS;
+
+ exchange_state->state = libssh2_NB_state_sent2;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_sent2) {
+ rc = _libssh2_transport_send(session, &exchange_state->c, 1, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send NEWKEYS message");
+ goto clean_exit;
+ }
+
+ exchange_state->state = libssh2_NB_state_sent3;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_sent3) {
+ rc = _libssh2_packet_require(session, SSH_MSG_NEWKEYS,
+ &exchange_state->tmp,
+ &exchange_state->tmp_len, 0, NULL, 0,
+ &exchange_state->req_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc, "Timed out waiting for NEWKEYS");
+ goto clean_exit;
+ }
+ /* The first key exchange has been performed,
+ switch to active crypt/comp/mac mode */
+ session->state |= LIBSSH2_STATE_NEWKEYS;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Received NEWKEYS message");
+
+ /* This will actually end up being just packet_type(1)
+ for this packet type anyway */
+ LIBSSH2_FREE(session, exchange_state->tmp);
+
+ if(!session->session_id) {
+ session->session_id = LIBSSH2_ALLOC(session, digest_len);
+ if(!session->session_id) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate buffer for "
+ "SHA digest");
+ goto clean_exit;
+ }
+ memcpy(session->session_id, exchange_state->h_sig_comp,
+ digest_len);
+ session->session_id_len = digest_len;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "session_id calculated");
+ }
+
+ /* Cleanup any existing cipher */
+ if(session->local.crypt->dtor) {
+ session->local.crypt->dtor(session,
+ &session->local.crypt_abstract);
+ }
+
+ /* Calculate IV/Secret/Key for each direction */
+ if(session->local.crypt->init) {
+ unsigned char *iv = NULL, *secret = NULL;
+ int free_iv = 0, free_secret = 0;
+
+ _libssh2_sha_algo_value_hash(sha_algo_value, session,
+ exchange_state, &iv,
+ session->local.crypt->iv_len,
+ (const unsigned char *)"A");
+
+ if(!iv) {
+ ret = -1;
+ goto clean_exit;
+ }
+ _libssh2_sha_algo_value_hash(sha_algo_value, session,
+ exchange_state, &secret,
+ session->local.crypt->secret_len,
+ (const unsigned char *)"C");
+
+ if(!secret) {
+ LIBSSH2_FREE(session, iv);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ if(session->local.crypt->
+ init(session, session->local.crypt, iv, &free_iv, secret,
+ &free_secret, 1, &session->local.crypt_abstract)) {
+ LIBSSH2_FREE(session, iv);
+ LIBSSH2_FREE(session, secret);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+
+ if(free_iv) {
+ _libssh2_explicit_zero(iv, session->local.crypt->iv_len);
+ LIBSSH2_FREE(session, iv);
+ }
+
+ if(free_secret) {
+ _libssh2_explicit_zero(secret,
+ session->local.crypt->secret_len);
+ LIBSSH2_FREE(session, secret);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server IV and Key calculated");
+
+ if(session->remote.crypt->dtor) {
+ /* Cleanup any existing cipher */
+ session->remote.crypt->dtor(session,
+ &session->remote.crypt_abstract);
+ }
+
+ if(session->remote.crypt->init) {
+ unsigned char *iv = NULL, *secret = NULL;
+ int free_iv = 0, free_secret = 0;
+
+ _libssh2_sha_algo_value_hash(sha_algo_value, session,
+ exchange_state, &iv,
+ session->remote.crypt->iv_len,
+ (const unsigned char *)"B");
+ if(!iv) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ _libssh2_sha_algo_value_hash(sha_algo_value, session,
+ exchange_state, &secret,
+ session->remote.crypt->secret_len,
+ (const unsigned char *)"D");
+ if(!secret) {
+ LIBSSH2_FREE(session, iv);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ if(session->remote.crypt->
+ init(session, session->remote.crypt, iv, &free_iv, secret,
+ &free_secret, 0, &session->remote.crypt_abstract)) {
+ LIBSSH2_FREE(session, iv);
+ LIBSSH2_FREE(session, secret);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+
+ if(free_iv) {
+ _libssh2_explicit_zero(iv, session->remote.crypt->iv_len);
+ LIBSSH2_FREE(session, iv);
+ }
+
+ if(free_secret) {
+ _libssh2_explicit_zero(secret,
+ session->remote.crypt->secret_len);
+ LIBSSH2_FREE(session, secret);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client IV and Key calculated");
+
+ if(session->local.mac->dtor) {
+ session->local.mac->dtor(session, &session->local.mac_abstract);
+ }
+
+ if(session->local.mac->init) {
+ unsigned char *key = NULL;
+ int free_key = 0;
+
+ _libssh2_sha_algo_value_hash(sha_algo_value, session,
+ exchange_state, &key,
+ session->local.mac->key_len,
+ (const unsigned char *)"E");
+ if(!key) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ session->local.mac->init(session, key, &free_key,
+ &session->local.mac_abstract);
+
+ if(free_key) {
+ _libssh2_explicit_zero(key, session->local.mac->key_len);
+ LIBSSH2_FREE(session, key);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server HMAC Key calculated");
+
+ if(session->remote.mac->dtor) {
+ session->remote.mac->dtor(session, &session->remote.mac_abstract);
+ }
+
+ if(session->remote.mac->init) {
+ unsigned char *key = NULL;
+ int free_key = 0;
+
+ _libssh2_sha_algo_value_hash(sha_algo_value, session,
+ exchange_state, &key,
+ session->remote.mac->key_len,
+ (const unsigned char *)"F");
+ if(!key) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ session->remote.mac->init(session, key, &free_key,
+ &session->remote.mac_abstract);
+
+ if(free_key) {
+ _libssh2_explicit_zero(key, session->remote.mac->key_len);
+ LIBSSH2_FREE(session, key);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client HMAC Key calculated");
+
+ /* Initialize compression for each direction */
+
+ /* Cleanup any existing compression */
+ if(session->local.comp && session->local.comp->dtor) {
+ session->local.comp->dtor(session, 1,
+ &session->local.comp_abstract);
+ }
+
+ if(session->local.comp && session->local.comp->init) {
+ if(session->local.comp->init(session, 1,
+ &session->local.comp_abstract)) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server compression initialized");
+
+ if(session->remote.comp && session->remote.comp->dtor) {
+ session->remote.comp->dtor(session, 0,
+ &session->remote.comp_abstract);
+ }
+
+ if(session->remote.comp && session->remote.comp->init) {
+ if(session->remote.comp->init(session, 0,
+ &session->remote.comp_abstract)) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client compression initialized");
+
+ }
+
+ clean_exit:
+ libssh2_dh_dtor(&exchange_state->x);
+ _libssh2_bn_free(exchange_state->e);
+ exchange_state->e = NULL;
+ _libssh2_bn_free(exchange_state->f);
+ exchange_state->f = NULL;
+ _libssh2_bn_free(exchange_state->k);
+ exchange_state->k = NULL;
+ _libssh2_bn_ctx_free(exchange_state->ctx);
+ exchange_state->ctx = NULL;
+
+ if(exchange_state->e_packet) {
+ LIBSSH2_FREE(session, exchange_state->e_packet);
+ exchange_state->e_packet = NULL;
+ }
+
+ if(exchange_state->s_packet) {
+ LIBSSH2_FREE(session, exchange_state->s_packet);
+ exchange_state->s_packet = NULL;
+ }
+
+ if(exchange_state->k_value) {
+ LIBSSH2_FREE(session, exchange_state->k_value);
+ exchange_state->k_value = NULL;
+ }
+
+ exchange_state->state = libssh2_NB_state_idle;
+
+ return ret;
+}
+
+
+
+/* kex_method_diffie_hellman_group1_sha1_key_exchange
+ * Diffie-Hellman Group1 (Actually Group2) Key Exchange using SHA1
+ */
+static int
+kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SESSION *session,
+ key_exchange_state_low_t
+ * key_state)
+{
+ static const unsigned char p_value[128] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+
+ int ret;
+ libssh2_sha1_ctx exchange_hash_ctx;
+
+ if(key_state->state == libssh2_NB_state_idle) {
+ /* g == 2 */
+ key_state->p = _libssh2_bn_init_from_bin(); /* SSH2 defined value
+ (p_value) */
+ key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */
+
+ /* Initialize P and G */
+ _libssh2_bn_set_word(key_state->g, 2);
+ _libssh2_bn_from_bin(key_state->p, 128, p_value);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group1 Key Exchange");
+
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ ret = diffie_hellman_sha_algo(session, key_state->g, key_state->p, 128, 1,
+ (void *)&exchange_hash_ctx,
+ SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY,
+ NULL, 0, &key_state->exchange_state);
+ if(ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+ key_state->state = libssh2_NB_state_idle;
+
+ return ret;
+}
+
+
+/* kex_method_diffie_hellman_group14_key_exchange
+ * Diffie-Hellman Group14 Key Exchange with hash function callback
+ */
+typedef int (*diffie_hellman_hash_func_t)(LIBSSH2_SESSION *,
+ _libssh2_bn *,
+ _libssh2_bn *,
+ int,
+ int,
+ void *,
+ unsigned char,
+ unsigned char,
+ unsigned char *,
+ unsigned long,
+ kmdhgGPshakex_state_t *);
+static int
+kex_method_diffie_hellman_group14_key_exchange(LIBSSH2_SESSION *session,
+ key_exchange_state_low_t
+ * key_state,
+ int sha_algo_value,
+ void *exchange_hash_ctx,
+ diffie_hellman_hash_func_t
+ hashfunc)
+{
+ static const unsigned char p_value[256] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+ 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+ 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+ 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+ 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+ 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+ 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+ 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+ 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+ 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+ 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+ 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+ 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+ 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+ 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+ 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+ 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+ 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+ 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+ 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+ int ret;
+
+ if(key_state->state == libssh2_NB_state_idle) {
+ key_state->p = _libssh2_bn_init_from_bin(); /* SSH2 defined value
+ (p_value) */
+ key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */
+
+ /* g == 2 */
+ /* Initialize P and G */
+ _libssh2_bn_set_word(key_state->g, 2);
+ _libssh2_bn_from_bin(key_state->p, 256, p_value);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group14 Key Exchange");
+
+ key_state->state = libssh2_NB_state_created;
+ }
+ ret = hashfunc(session, key_state->g, key_state->p,
+ 256, sha_algo_value, exchange_hash_ctx, SSH_MSG_KEXDH_INIT,
+ SSH_MSG_KEXDH_REPLY, NULL, 0, &key_state->exchange_state);
+ if(ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ key_state->state = libssh2_NB_state_idle;
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+
+ return ret;
+}
+
+
+
+/* kex_method_diffie_hellman_group14_sha1_key_exchange
+ * Diffie-Hellman Group14 Key Exchange using SHA1
+ */
+static int
+kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_SESSION *session,
+ key_exchange_state_low_t
+ * key_state)
+{
+ libssh2_sha1_ctx ctx;
+ return kex_method_diffie_hellman_group14_key_exchange(session,
+ key_state, 1,
+ &ctx,
+ diffie_hellman_sha_algo);
+}
+
+
+
+/* kex_method_diffie_hellman_group14_sha256_key_exchange
+ * Diffie-Hellman Group14 Key Exchange using SHA256
+ */
+static int
+kex_method_diffie_hellman_group14_sha256_key_exchange(LIBSSH2_SESSION *session,
+ key_exchange_state_low_t
+ * key_state)
+{
+ libssh2_sha256_ctx ctx;
+ return kex_method_diffie_hellman_group14_key_exchange(session,
+ key_state, 256,
+ &ctx,
+ diffie_hellman_sha_algo);
+}
+
+/* kex_method_diffie_hellman_group16_sha512_key_exchange
+* Diffie-Hellman Group16 Key Exchange using SHA512
+*/
+static int
+kex_method_diffie_hellman_group16_sha512_key_exchange(LIBSSH2_SESSION *session,
+ key_exchange_state_low_t
+ * key_state)
+
+{
+ static const unsigned char p_value[512] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
+ 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
+ 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
+ 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2,
+ 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C,
+ 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D,
+ 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+ 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57,
+ 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+ 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0,
+ 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+ 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73,
+ 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+ 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0,
+ 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+ 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20,
+ 0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+ 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18,
+ 0x6A, 0xF4, 0xE2, 0x3C, 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+ 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, 0xDB, 0xBB, 0xC2, 0xDB,
+ 0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+ 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F,
+ 0xA0, 0x90, 0xC3, 0xA2, 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+ 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, 0xB8, 0x1B, 0xDD, 0x76,
+ 0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+ 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC,
+ 0x90, 0xA6, 0xC0, 0x8F, 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+ int ret;
+ libssh2_sha512_ctx exchange_hash_ctx;
+
+ if(key_state->state == libssh2_NB_state_idle) {
+ key_state->p = _libssh2_bn_init_from_bin(); /* SSH2 defined value
+ (p_value) */
+ key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */
+
+ /* g == 2 */
+ /* Initialize P and G */
+ _libssh2_bn_set_word(key_state->g, 2);
+ _libssh2_bn_from_bin(key_state->p, 512, p_value);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group16 Key Exchange");
+
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ ret = diffie_hellman_sha_algo(session, key_state->g, key_state->p, 512,
+ 512, (void *)&exchange_hash_ctx,
+ SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY,
+ NULL, 0, &key_state->exchange_state);
+ if(ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ key_state->state = libssh2_NB_state_idle;
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+
+ return ret;
+}
+
+/* kex_method_diffie_hellman_group16_sha512_key_exchange
+* Diffie-Hellman Group18 Key Exchange using SHA512
+*/
+static int
+kex_method_diffie_hellman_group18_sha512_key_exchange(LIBSSH2_SESSION *session,
+ key_exchange_state_low_t
+ * key_state)
+
+{
+ static const unsigned char p_value[1024] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+ 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+ 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+ 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+ 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+ 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+ 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+ 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+ 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+ 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+ 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
+ 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+ 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
+ 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+ 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
+ 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+ 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2,
+ 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+ 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C,
+ 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+ 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D,
+ 0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+ 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57,
+ 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+ 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0,
+ 0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+ 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73,
+ 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+ 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0,
+ 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+ 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20,
+ 0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+ 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18,
+ 0x6A, 0xF4, 0xE2, 0x3C, 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+ 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, 0xDB, 0xBB, 0xC2, 0xDB,
+ 0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+ 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F,
+ 0xA0, 0x90, 0xC3, 0xA2, 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+ 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, 0xB8, 0x1B, 0xDD, 0x76,
+ 0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+ 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC,
+ 0x90, 0xA6, 0xC0, 0x8F, 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+ 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26, 0xC1, 0xD4, 0xDC, 0xB2,
+ 0x60, 0x26, 0x46, 0xDE, 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+ 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E, 0xE5, 0xDB, 0x38, 0x2F,
+ 0x41, 0x30, 0x01, 0xAE, 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+ 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18, 0xDA, 0x3E, 0xDB, 0xEB,
+ 0xCF, 0x9B, 0x14, 0xED, 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+ 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B, 0x33, 0x20, 0x51, 0x51,
+ 0x2B, 0xD7, 0xAF, 0x42, 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+ 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC, 0xF0, 0x32, 0xEA, 0x15,
+ 0xD1, 0x72, 0x1D, 0x03, 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+ 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82, 0xB5, 0xA8, 0x40, 0x31,
+ 0x90, 0x0B, 0x1C, 0x9E, 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+ 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE, 0x0F, 0x1D, 0x45, 0xB7,
+ 0xFF, 0x58, 0x5A, 0xC5, 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+ 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8, 0x14, 0xCC, 0x5E, 0xD2,
+ 0x0F, 0x80, 0x37, 0xE0, 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+ 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76, 0xF5, 0x50, 0xAA, 0x3D,
+ 0x8A, 0x1F, 0xBF, 0xF0, 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+ 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32, 0x38, 0x7F, 0xE8, 0xD7,
+ 0x6E, 0x3C, 0x04, 0x68, 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+ 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6, 0xE6, 0x94, 0xF9, 0x1E,
+ 0x6D, 0xBE, 0x11, 0x59, 0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4,
+ 0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C, 0xD8, 0xBE, 0xC4, 0xD0,
+ 0x73, 0xB9, 0x31, 0xBA, 0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00,
+ 0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED, 0x25, 0x76, 0xF6, 0x93,
+ 0x6B, 0xA4, 0x24, 0x66, 0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68,
+ 0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78, 0x23, 0x8F, 0x16, 0xCB,
+ 0xE3, 0x9D, 0x65, 0x2D, 0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9,
+ 0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07, 0x13, 0xEB, 0x57, 0xA8,
+ 0x1A, 0x23, 0xF0, 0xC7, 0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B,
+ 0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD, 0xFA, 0x9D, 0x4B, 0x7F,
+ 0xA2, 0xC0, 0x87, 0xE8, 0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A,
+ 0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6, 0x6D, 0x2A, 0x13, 0xF8,
+ 0x3F, 0x44, 0xF8, 0x2D, 0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36,
+ 0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1, 0x64, 0xF3, 0x1C, 0xC5,
+ 0x08, 0x46, 0x85, 0x1D, 0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1,
+ 0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73, 0xFA, 0xF3, 0x6B, 0xC3,
+ 0x1E, 0xCF, 0xA2, 0x68, 0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92,
+ 0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7, 0x88, 0x9A, 0x00, 0x2E,
+ 0xD5, 0xEE, 0x38, 0x2B, 0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47,
+ 0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA, 0x9E, 0x30, 0x50, 0xE2,
+ 0x76, 0x56, 0x94, 0xDF, 0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71,
+ 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF
+ };
+ int ret;
+ libssh2_sha512_ctx exchange_hash_ctx;
+
+ if(key_state->state == libssh2_NB_state_idle) {
+ key_state->p = _libssh2_bn_init_from_bin(); /* SSH2 defined value
+ (p_value) */
+ key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */
+
+ /* g == 2 */
+ /* Initialize P and G */
+ _libssh2_bn_set_word(key_state->g, 2);
+ _libssh2_bn_from_bin(key_state->p, 1024, p_value);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group18 Key Exchange");
+
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ ret = diffie_hellman_sha_algo(session, key_state->g, key_state->p, 1024,
+ 512, (void *)&exchange_hash_ctx,
+ SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY,
+ NULL, 0, &key_state->exchange_state);
+ if(ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ key_state->state = libssh2_NB_state_idle;
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+
+ return ret;
+}
+
+/* kex_method_diffie_hellman_group_exchange_sha1_key_exchange
+ * Diffie-Hellman Group Exchange Key Exchange using SHA1
+ * Negotiates random(ish) group for secret derivation
+ */
+static int
+kex_method_diffie_hellman_group_exchange_sha1_key_exchange
+(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
+{
+ int ret = 0;
+ int rc;
+
+ if(key_state->state == libssh2_NB_state_idle) {
+ key_state->p = _libssh2_bn_init_from_bin();
+ key_state->g = _libssh2_bn_init_from_bin();
+ /* Ask for a P and G pair */
+#ifdef LIBSSH2_DH_GEX_NEW
+ key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST;
+ _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_MINGROUP);
+ _libssh2_htonu32(key_state->request + 5, LIBSSH2_DH_GEX_OPTGROUP);
+ _libssh2_htonu32(key_state->request + 9, LIBSSH2_DH_GEX_MAXGROUP);
+ key_state->request_len = 13;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group-Exchange "
+ "(New Method)");
+#else
+ key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD;
+ _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_OPTGROUP);
+ key_state->request_len = 5;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group-Exchange "
+ "(Old Method)");
+#endif
+
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ if(key_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, key_state->request,
+ key_state->request_len, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send Group Exchange Request");
+ goto dh_gex_clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_require(session, SSH_MSG_KEX_DH_GEX_GROUP,
+ &key_state->data, &key_state->data_len,
+ 0, NULL, 0, &key_state->req_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Timeout waiting for GEX_GROUP reply");
+ goto dh_gex_clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent1;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent1) {
+ size_t p_len, g_len;
+ unsigned char *p, *g;
+ struct string_buf buf;
+ libssh2_sha1_ctx exchange_hash_ctx;
+
+ if(key_state->data_len < 9) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected key length");
+ goto dh_gex_clean_exit;
+ }
+
+ buf.data = key_state->data;
+ buf.dataptr = buf.data;
+ buf.len = key_state->data_len;
+
+ buf.dataptr++; /* increment to big num */
+
+ if(_libssh2_get_bignum_bytes(&buf, &p, &p_len)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected value");
+ goto dh_gex_clean_exit;
+ }
+
+ if(_libssh2_get_bignum_bytes(&buf, &g, &g_len)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected value");
+ goto dh_gex_clean_exit;
+ }
+
+ _libssh2_bn_from_bin(key_state->p, p_len, p);
+ _libssh2_bn_from_bin(key_state->g, g_len, g);
+
+ ret = diffie_hellman_sha_algo(session, key_state->g, key_state->p,
+ p_len, 1,
+ (void *)&exchange_hash_ctx,
+ SSH_MSG_KEX_DH_GEX_INIT,
+ SSH_MSG_KEX_DH_GEX_REPLY,
+ key_state->data + 1,
+ key_state->data_len - 1,
+ &key_state->exchange_state);
+ if(ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ LIBSSH2_FREE(session, key_state->data);
+ }
+
+ dh_gex_clean_exit:
+ key_state->state = libssh2_NB_state_idle;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+
+ return ret;
+}
+
+
+
+/* kex_method_diffie_hellman_group_exchange_sha256_key_exchange
+ * Diffie-Hellman Group Exchange Key Exchange using SHA256
+ * Negotiates random(ish) group for secret derivation
+ */
+static int
+kex_method_diffie_hellman_group_exchange_sha256_key_exchange
+(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
+{
+ int ret = 0;
+ int rc;
+
+ if(key_state->state == libssh2_NB_state_idle) {
+ key_state->p = _libssh2_bn_init();
+ key_state->g = _libssh2_bn_init();
+ /* Ask for a P and G pair */
+#ifdef LIBSSH2_DH_GEX_NEW
+ key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST;
+ _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_MINGROUP);
+ _libssh2_htonu32(key_state->request + 5, LIBSSH2_DH_GEX_OPTGROUP);
+ _libssh2_htonu32(key_state->request + 9, LIBSSH2_DH_GEX_MAXGROUP);
+ key_state->request_len = 13;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group-Exchange "
+ "(New Method SHA256)");
+#else
+ key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD;
+ _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_OPTGROUP);
+ key_state->request_len = 5;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating Diffie-Hellman Group-Exchange "
+ "(Old Method SHA256)");
+#endif
+
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ if(key_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, key_state->request,
+ key_state->request_len, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send "
+ "Group Exchange Request SHA256");
+ goto dh_gex_clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_require(session, SSH_MSG_KEX_DH_GEX_GROUP,
+ &key_state->data, &key_state->data_len,
+ 0, NULL, 0, &key_state->req_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Timeout waiting for GEX_GROUP reply SHA256");
+ goto dh_gex_clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent1;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent1) {
+ unsigned char *p, *g;
+ size_t p_len, g_len;
+ struct string_buf buf;
+ libssh2_sha256_ctx exchange_hash_ctx;
+
+ if(key_state->data_len < 9) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected key length");
+ goto dh_gex_clean_exit;
+ }
+
+ buf.data = key_state->data;
+ buf.dataptr = buf.data;
+ buf.len = key_state->data_len;
+
+ buf.dataptr++; /* increment to big num */
+
+ if(_libssh2_get_bignum_bytes(&buf, &p, &p_len)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected value");
+ goto dh_gex_clean_exit;
+ }
+
+ if(_libssh2_get_bignum_bytes(&buf, &g, &g_len)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected value");
+ goto dh_gex_clean_exit;
+ }
+
+ _libssh2_bn_from_bin(key_state->p, p_len, p);
+ _libssh2_bn_from_bin(key_state->g, g_len, g);
+
+ ret = diffie_hellman_sha_algo(session, key_state->g, key_state->p,
+ p_len, 256,
+ (void *)&exchange_hash_ctx,
+ SSH_MSG_KEX_DH_GEX_INIT,
+ SSH_MSG_KEX_DH_GEX_REPLY,
+ key_state->data + 1,
+ key_state->data_len - 1,
+ &key_state->exchange_state);
+ if(ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ LIBSSH2_FREE(session, key_state->data);
+ }
+
+ dh_gex_clean_exit:
+ key_state->state = libssh2_NB_state_idle;
+ _libssh2_bn_free(key_state->g);
+ key_state->g = NULL;
+ _libssh2_bn_free(key_state->p);
+ key_state->p = NULL;
+
+ return ret;
+}
+
+
+/* LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY
+ *
+ * Macro that create and verifies EC SHA hash with a given digest bytes
+ *
+ * Payload format:
+ *
+ * string V_C, client's identification string (CR and LF excluded)
+ * string V_S, server's identification string (CR and LF excluded)
+ * string I_C, payload of the client's SSH_MSG_KEXINIT
+ * string I_S, payload of the server's SSH_MSG_KEXINIT
+ * string K_S, server's public host key
+ * string Q_C, client's ephemeral public key octet string
+ * string Q_S, server's ephemeral public key octet string
+ * mpint K, shared secret
+ *
+ */
+
+#define LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY(digest_type) \
+{ \
+ libssh2_sha##digest_type##_ctx ctx; \
+ exchange_state->exchange_hash = (void *)&ctx; \
+ libssh2_sha##digest_type##_init(&ctx); \
+ if(session->local.banner) { \
+ _libssh2_htonu32(exchange_state->h_sig_comp, \
+ strlen((char *) session->local.banner) - 2); \
+ libssh2_sha##digest_type##_update(ctx, \
+ exchange_state->h_sig_comp, 4); \
+ libssh2_sha##digest_type##_update(ctx, \
+ (char *) session->local.banner, \
+ strlen((char *) \
+ session->local.banner) \
+ - 2); \
+ } \
+ else { \
+ _libssh2_htonu32(exchange_state->h_sig_comp, \
+ sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); \
+ libssh2_sha##digest_type##_update(ctx, \
+ exchange_state->h_sig_comp, 4); \
+ libssh2_sha##digest_type##_update(ctx, \
+ LIBSSH2_SSH_DEFAULT_BANNER, \
+ sizeof(LIBSSH2_SSH_DEFAULT_BANNER) \
+ - 1); \
+ } \
+ \
+ _libssh2_htonu32(exchange_state->h_sig_comp, \
+ strlen((char *) session->remote.banner)); \
+ libssh2_sha##digest_type##_update(ctx, \
+ exchange_state->h_sig_comp, 4); \
+ libssh2_sha##digest_type##_update(ctx, \
+ session->remote.banner, \
+ strlen((char *) \
+ session->remote.banner)); \
+ \
+ _libssh2_htonu32(exchange_state->h_sig_comp, \
+ session->local.kexinit_len); \
+ libssh2_sha##digest_type##_update(ctx, \
+ exchange_state->h_sig_comp, 4); \
+ libssh2_sha##digest_type##_update(ctx, \
+ session->local.kexinit, \
+ session->local.kexinit_len); \
+ \
+ _libssh2_htonu32(exchange_state->h_sig_comp, \
+ session->remote.kexinit_len); \
+ libssh2_sha##digest_type##_update(ctx, \
+ exchange_state->h_sig_comp, 4); \
+ libssh2_sha##digest_type##_update(ctx, \
+ session->remote.kexinit, \
+ session->remote.kexinit_len); \
+ \
+ _libssh2_htonu32(exchange_state->h_sig_comp, \
+ session->server_hostkey_len); \
+ libssh2_sha##digest_type##_update(ctx, \
+ exchange_state->h_sig_comp, 4); \
+ libssh2_sha##digest_type##_update(ctx, \
+ session->server_hostkey, \
+ session->server_hostkey_len); \
+ \
+ _libssh2_htonu32(exchange_state->h_sig_comp, \
+ public_key_len); \
+ libssh2_sha##digest_type##_update(ctx, \
+ exchange_state->h_sig_comp, 4); \
+ libssh2_sha##digest_type##_update(ctx, \
+ public_key, \
+ public_key_len); \
+ \
+ _libssh2_htonu32(exchange_state->h_sig_comp, \
+ server_public_key_len); \
+ libssh2_sha##digest_type##_update(ctx, \
+ exchange_state->h_sig_comp, 4); \
+ libssh2_sha##digest_type##_update(ctx, \
+ server_public_key, \
+ server_public_key_len); \
+ \
+ libssh2_sha##digest_type##_update(ctx, \
+ exchange_state->k_value, \
+ exchange_state->k_value_len); \
+ \
+ libssh2_sha##digest_type##_final(ctx, exchange_state->h_sig_comp); \
+ \
+ if(session->hostkey-> \
+ sig_verify(session, exchange_state->h_sig, \
+ exchange_state->h_sig_len, exchange_state->h_sig_comp, \
+ SHA##digest_type##_DIGEST_LENGTH, \
+ &session->server_hostkey_abstract)) { \
+ rc = -1; \
+ } \
+} \
+
+
+#if LIBSSH2_ECDSA
+
+/* kex_session_ecdh_curve_type
+ * returns the EC curve type by name used in key exchange
+ */
+
+static int
+kex_session_ecdh_curve_type(const char *name, libssh2_curve_type *out_type)
+{
+ int ret = 0;
+ libssh2_curve_type type;
+
+ if(name == NULL)
+ return -1;
+
+ if(strcmp(name, "ecdh-sha2-nistp256") == 0)
+ type = LIBSSH2_EC_CURVE_NISTP256;
+ else if(strcmp(name, "ecdh-sha2-nistp384") == 0)
+ type = LIBSSH2_EC_CURVE_NISTP384;
+ else if(strcmp(name, "ecdh-sha2-nistp521") == 0)
+ type = LIBSSH2_EC_CURVE_NISTP521;
+ else {
+ ret = -1;
+ }
+
+ if(ret == 0 && out_type) {
+ *out_type = type;
+ }
+
+ return ret;
+}
+
+
+/* ecdh_sha2_nistp
+ * Elliptic Curve Diffie Hellman Key Exchange
+ */
+
+static int ecdh_sha2_nistp(LIBSSH2_SESSION *session, libssh2_curve_type type,
+ unsigned char *data, size_t data_len,
+ unsigned char *public_key,
+ size_t public_key_len, _libssh2_ec_key *private_key,
+ kmdhgGPshakex_state_t *exchange_state)
+{
+ int ret = 0;
+ int rc;
+
+ if(data_len < 5) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Host key data is too short");
+ return ret;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_idle) {
+
+ /* Setup initial values */
+ exchange_state->k = _libssh2_bn_init();
+
+ exchange_state->state = libssh2_NB_state_created;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_created) {
+ /* parse INIT reply data */
+
+ /* host key K_S */
+ unsigned char *server_public_key;
+ size_t server_public_key_len;
+ struct string_buf buf;
+
+ buf.data = data;
+ buf.len = data_len;
+ buf.dataptr = buf.data;
+ buf.dataptr++; /* Advance past packet type */
+
+ if(_libssh2_copy_string(session, &buf, &(session->server_hostkey),
+ &server_public_key_len)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for a copy "
+ "of the host key");
+ goto clean_exit;
+ }
+
+ session->server_hostkey_len = (uint32_t)server_public_key_len;
+
+#if LIBSSH2_MD5
+ {
+ libssh2_md5_ctx fingerprint_ctx;
+
+ if(libssh2_md5_init(&fingerprint_ctx)) {
+ libssh2_md5_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_md5_final(fingerprint_ctx,
+ session->server_hostkey_md5);
+ session->server_hostkey_md5_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_md5_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char fingerprint[50], *fprint = fingerprint;
+ int i;
+ for(i = 0; i < 16; i++, fprint += 3) {
+ snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]);
+ }
+ *(--fprint) = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's MD5 Fingerprint: %s", fingerprint);
+ }
+#endif /* LIBSSH2DEBUG */
+#endif /* ! LIBSSH2_MD5 */
+
+ {
+ libssh2_sha1_ctx fingerprint_ctx;
+
+ if(libssh2_sha1_init(&fingerprint_ctx)) {
+ libssh2_sha1_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_sha1_final(fingerprint_ctx,
+ session->server_hostkey_sha1);
+ session->server_hostkey_sha1_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_sha1_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char fingerprint[64], *fprint = fingerprint;
+ int i;
+
+ for(i = 0; i < 20; i++, fprint += 3) {
+ snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]);
+ }
+ *(--fprint) = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's SHA1 Fingerprint: %s", fingerprint);
+ }
+#endif /* LIBSSH2DEBUG */
+
+ /* SHA256 */
+ {
+ libssh2_sha256_ctx fingerprint_ctx;
+
+ if(libssh2_sha256_init(&fingerprint_ctx)) {
+ libssh2_sha256_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_sha256_final(fingerprint_ctx,
+ session->server_hostkey_sha256);
+ session->server_hostkey_sha256_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_sha256_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char *base64Fingerprint = NULL;
+ _libssh2_base64_encode(session,
+ (const char *)
+ session->server_hostkey_sha256,
+ SHA256_DIGEST_LENGTH, &base64Fingerprint);
+ if(base64Fingerprint != NULL) {
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's SHA256 Fingerprint: %s",
+ base64Fingerprint);
+ LIBSSH2_FREE(session, base64Fingerprint);
+ }
+ }
+#endif /* LIBSSH2DEBUG */
+
+ if(session->hostkey->init(session, session->server_hostkey,
+ session->server_hostkey_len,
+ &session->server_hostkey_abstract)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Unable to initialize hostkey importer");
+ goto clean_exit;
+ }
+
+ /* server public key Q_S */
+ if(_libssh2_get_string(&buf, &server_public_key,
+ &server_public_key_len)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected key length");
+ goto clean_exit;
+ }
+
+ /* server signature */
+ if(_libssh2_get_string(&buf, &exchange_state->h_sig,
+ &(exchange_state->h_sig_len))) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Unexpected ecdh server sig length");
+ goto clean_exit;
+ }
+
+ /* Compute the shared secret K */
+ rc = _libssh2_ecdh_gen_k(&exchange_state->k, private_key,
+ server_public_key, server_public_key_len);
+ if(rc != 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE,
+ "Unable to create ECDH shared secret");
+ goto clean_exit;
+ }
+
+ exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5;
+ if(_libssh2_bn_bits(exchange_state->k) % 8) {
+ /* don't need leading 00 */
+ exchange_state->k_value_len--;
+ }
+ exchange_state->k_value =
+ LIBSSH2_ALLOC(session, exchange_state->k_value_len);
+ if(!exchange_state->k_value) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate buffer for K");
+ goto clean_exit;
+ }
+ _libssh2_htonu32(exchange_state->k_value,
+ exchange_state->k_value_len - 4);
+ if(_libssh2_bn_bits(exchange_state->k) % 8) {
+ _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
+ }
+ else {
+ exchange_state->k_value[4] = 0;
+ _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
+ }
+
+ /* verify hash */
+
+ switch(type) {
+ case LIBSSH2_EC_CURVE_NISTP256:
+ LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY(256);
+ break;
+
+ case LIBSSH2_EC_CURVE_NISTP384:
+ LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY(384);
+ break;
+ case LIBSSH2_EC_CURVE_NISTP521:
+ LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY(512);
+ break;
+ }
+
+ if(rc != 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN,
+ "Unable to verify hostkey signature");
+ goto clean_exit;
+ }
+
+ exchange_state->c = SSH_MSG_NEWKEYS;
+ exchange_state->state = libssh2_NB_state_sent;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_sent) {
+ rc = _libssh2_transport_send(session, &exchange_state->c, 1, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send NEWKEYS message");
+ goto clean_exit;
+ }
+
+ exchange_state->state = libssh2_NB_state_sent2;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_sent2) {
+ rc = _libssh2_packet_require(session, SSH_MSG_NEWKEYS,
+ &exchange_state->tmp,
+ &exchange_state->tmp_len, 0, NULL, 0,
+ &exchange_state->req_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc, "Timed out waiting for NEWKEYS");
+ goto clean_exit;
+ }
+
+ /* The first key exchange has been performed,
+ switch to active crypt/comp/mac mode */
+ session->state |= LIBSSH2_STATE_NEWKEYS;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Received NEWKEYS message");
+
+ /* This will actually end up being just packet_type(1)
+ for this packet type anyway */
+ LIBSSH2_FREE(session, exchange_state->tmp);
+
+ if(!session->session_id) {
+
+ size_t digest_length = 0;
+
+ if(type == LIBSSH2_EC_CURVE_NISTP256)
+ digest_length = SHA256_DIGEST_LENGTH;
+ else if(type == LIBSSH2_EC_CURVE_NISTP384)
+ digest_length = SHA384_DIGEST_LENGTH;
+ else if(type == LIBSSH2_EC_CURVE_NISTP521)
+ digest_length = SHA512_DIGEST_LENGTH;
+ else{
+ ret = _libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE,
+ "Unknown SHA digest for EC curve");
+ goto clean_exit;
+
+ }
+ session->session_id = LIBSSH2_ALLOC(session, digest_length);
+ if(!session->session_id) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate buffer for "
+ "SHA digest");
+ goto clean_exit;
+ }
+ memcpy(session->session_id, exchange_state->h_sig_comp,
+ digest_length);
+ session->session_id_len = digest_length;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "session_id calculated");
+ }
+
+ /* Cleanup any existing cipher */
+ if(session->local.crypt->dtor) {
+ session->local.crypt->dtor(session,
+ &session->local.crypt_abstract);
+ }
+
+ /* Calculate IV/Secret/Key for each direction */
+ if(session->local.crypt->init) {
+ unsigned char *iv = NULL, *secret = NULL;
+ int free_iv = 0, free_secret = 0;
+
+ LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(iv,
+ session->local.crypt->
+ iv_len, "A");
+ if(!iv) {
+ ret = -1;
+ goto clean_exit;
+ }
+
+ LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(secret,
+ session->local.crypt->
+ secret_len, "C");
+
+ if(!secret) {
+ LIBSSH2_FREE(session, iv);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ if(session->local.crypt->
+ init(session, session->local.crypt, iv, &free_iv, secret,
+ &free_secret, 1, &session->local.crypt_abstract)) {
+ LIBSSH2_FREE(session, iv);
+ LIBSSH2_FREE(session, secret);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+
+ if(free_iv) {
+ _libssh2_explicit_zero(iv, session->local.crypt->iv_len);
+ LIBSSH2_FREE(session, iv);
+ }
+
+ if(free_secret) {
+ _libssh2_explicit_zero(secret,
+ session->local.crypt->secret_len);
+ LIBSSH2_FREE(session, secret);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server IV and Key calculated");
+
+ if(session->remote.crypt->dtor) {
+ /* Cleanup any existing cipher */
+ session->remote.crypt->dtor(session,
+ &session->remote.crypt_abstract);
+ }
+
+ if(session->remote.crypt->init) {
+ unsigned char *iv = NULL, *secret = NULL;
+ int free_iv = 0, free_secret = 0;
+
+ LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(iv,
+ session->remote.crypt->
+ iv_len, "B");
+
+ if(!iv) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(secret,
+ session->remote.crypt->
+ secret_len, "D");
+
+ if(!secret) {
+ LIBSSH2_FREE(session, iv);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ if(session->remote.crypt->
+ init(session, session->remote.crypt, iv, &free_iv, secret,
+ &free_secret, 0, &session->remote.crypt_abstract)) {
+ LIBSSH2_FREE(session, iv);
+ LIBSSH2_FREE(session, secret);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+
+ if(free_iv) {
+ _libssh2_explicit_zero(iv, session->remote.crypt->iv_len);
+ LIBSSH2_FREE(session, iv);
+ }
+
+ if(free_secret) {
+ _libssh2_explicit_zero(secret,
+ session->remote.crypt->secret_len);
+ LIBSSH2_FREE(session, secret);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client IV and Key calculated");
+
+ if(session->local.mac->dtor) {
+ session->local.mac->dtor(session, &session->local.mac_abstract);
+ }
+
+ if(session->local.mac->init) {
+ unsigned char *key = NULL;
+ int free_key = 0;
+
+ LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(key,
+ session->local.mac->
+ key_len, "E");
+
+ if(!key) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ session->local.mac->init(session, key, &free_key,
+ &session->local.mac_abstract);
+
+ if(free_key) {
+ _libssh2_explicit_zero(key, session->local.mac->key_len);
+ LIBSSH2_FREE(session, key);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server HMAC Key calculated");
+
+ if(session->remote.mac->dtor) {
+ session->remote.mac->dtor(session, &session->remote.mac_abstract);
+ }
+
+ if(session->remote.mac->init) {
+ unsigned char *key = NULL;
+ int free_key = 0;
+
+ LIBSSH2_KEX_METHOD_EC_SHA_VALUE_HASH(key,
+ session->remote.mac->
+ key_len, "F");
+
+ if(!key) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ session->remote.mac->init(session, key, &free_key,
+ &session->remote.mac_abstract);
+
+ if(free_key) {
+ _libssh2_explicit_zero(key, session->remote.mac->key_len);
+ LIBSSH2_FREE(session, key);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client HMAC Key calculated");
+
+ /* Initialize compression for each direction */
+
+ /* Cleanup any existing compression */
+ if(session->local.comp && session->local.comp->dtor) {
+ session->local.comp->dtor(session, 1,
+ &session->local.comp_abstract);
+ }
+
+ if(session->local.comp && session->local.comp->init) {
+ if(session->local.comp->init(session, 1,
+ &session->local.comp_abstract)) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server compression initialized");
+
+ if(session->remote.comp && session->remote.comp->dtor) {
+ session->remote.comp->dtor(session, 0,
+ &session->remote.comp_abstract);
+ }
+
+ if(session->remote.comp && session->remote.comp->init) {
+ if(session->remote.comp->init(session, 0,
+ &session->remote.comp_abstract)) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client compression initialized");
+
+ }
+
+clean_exit:
+ _libssh2_bn_free(exchange_state->k);
+ exchange_state->k = NULL;
+
+ if(exchange_state->k_value) {
+ LIBSSH2_FREE(session, exchange_state->k_value);
+ exchange_state->k_value = NULL;
+ }
+
+ exchange_state->state = libssh2_NB_state_idle;
+
+ return ret;
+}
+
+/* kex_method_ecdh_key_exchange
+ *
+ * Elliptic Curve Diffie Hellman Key Exchange
+ * supports SHA256/384/512 hashes based on negotated ecdh method
+ *
+ */
+
+static int
+kex_method_ecdh_key_exchange
+(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
+{
+ int ret = 0;
+ int rc = 0;
+ unsigned char *s;
+ libssh2_curve_type type;
+
+ if(key_state->state == libssh2_NB_state_idle) {
+
+ key_state->public_key_oct = NULL;
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ if(key_state->state == libssh2_NB_state_created) {
+ rc = kex_session_ecdh_curve_type(session->kex->name, &type);
+
+ if(rc != 0) {
+ ret = _libssh2_error(session, -1,
+ "Unknown KEX nistp curve type");
+ goto ecdh_clean_exit;
+ }
+
+ rc = _libssh2_ecdsa_create_key(session, &key_state->private_key,
+ &key_state->public_key_oct,
+ &key_state->public_key_oct_len, type);
+
+ if(rc != 0) {
+ ret = _libssh2_error(session, rc,
+ "Unable to create private key");
+ goto ecdh_clean_exit;
+ }
+
+ key_state->request[0] = SSH2_MSG_KEX_ECDH_INIT;
+ s = key_state->request + 1;
+ _libssh2_store_str(&s, (const char *)key_state->public_key_oct,
+ key_state->public_key_oct_len);
+ key_state->request_len = key_state->public_key_oct_len + 5;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating ECDH SHA2 NISTP256");
+
+ key_state->state = libssh2_NB_state_sent;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent) {
+ rc = _libssh2_transport_send(session, key_state->request,
+ key_state->request_len, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send ECDH_INIT");
+ goto ecdh_clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent1;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent1) {
+ rc = _libssh2_packet_require(session, SSH2_MSG_KEX_ECDH_REPLY,
+ &key_state->data, &key_state->data_len,
+ 0, NULL, 0, &key_state->req_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Timeout waiting for ECDH_REPLY reply");
+ goto ecdh_clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent2;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent2) {
+
+ (void)kex_session_ecdh_curve_type(session->kex->name, &type);
+
+ ret = ecdh_sha2_nistp(session, type, key_state->data,
+ key_state->data_len,
+ (unsigned char *)key_state->public_key_oct,
+ key_state->public_key_oct_len,
+ key_state->private_key,
+ &key_state->exchange_state);
+
+ if(ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ LIBSSH2_FREE(session, key_state->data);
+ }
+
+ecdh_clean_exit:
+
+ if(key_state->public_key_oct) {
+ LIBSSH2_FREE(session, key_state->public_key_oct);
+ key_state->public_key_oct = NULL;
+ }
+
+ if(key_state->private_key) {
+ _libssh2_ecdsa_free(key_state->private_key);
+ key_state->private_key = NULL;
+ }
+
+ key_state->state = libssh2_NB_state_idle;
+
+ return ret;
+}
+
+#endif /*LIBSSH2_ECDSA*/
+
+
+#if LIBSSH2_ED25519
+
+/* curve25519_sha256
+ * Elliptic Curve Key Exchange
+ */
+
+static int
+curve25519_sha256(LIBSSH2_SESSION *session, unsigned char *data,
+ size_t data_len,
+ unsigned char public_key[LIBSSH2_ED25519_KEY_LEN],
+ unsigned char private_key[LIBSSH2_ED25519_KEY_LEN],
+ kmdhgGPshakex_state_t *exchange_state)
+{
+ int ret = 0;
+ int rc;
+ int public_key_len = LIBSSH2_ED25519_KEY_LEN;
+
+ if(data_len < 5) {
+ return _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Data is too short");
+ }
+
+ if(exchange_state->state == libssh2_NB_state_idle) {
+
+ /* Setup initial values */
+ exchange_state->k = _libssh2_bn_init();
+
+ exchange_state->state = libssh2_NB_state_created;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_created) {
+ /* parse INIT reply data */
+ unsigned char *server_public_key, *server_host_key;
+ size_t server_public_key_len, hostkey_len;
+ struct string_buf buf;
+
+ if(data_len < 5) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected key length");
+ goto clean_exit;
+ }
+
+ buf.data = data;
+ buf.len = data_len;
+ buf.dataptr = buf.data;
+ buf.dataptr++; /* advance past packet type */
+
+ if(_libssh2_get_string(&buf, &server_host_key, &hostkey_len)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected key length");
+ goto clean_exit;
+ }
+
+ session->server_hostkey_len = (uint32_t)hostkey_len;
+ session->server_hostkey = LIBSSH2_ALLOC(session,
+ session->server_hostkey_len);
+ if(!session->server_hostkey) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for a copy "
+ "of the host key");
+ goto clean_exit;
+ }
+
+ memcpy(session->server_hostkey, server_host_key,
+ session->server_hostkey_len);
+
+#if LIBSSH2_MD5
+ {
+ libssh2_md5_ctx fingerprint_ctx;
+
+ if(libssh2_md5_init(&fingerprint_ctx)) {
+ libssh2_md5_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_md5_final(fingerprint_ctx,
+ session->server_hostkey_md5);
+ session->server_hostkey_md5_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_md5_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char fingerprint[50], *fprint = fingerprint;
+ int i;
+ for(i = 0; i < 16; i++, fprint += 3) {
+ snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]);
+ }
+ *(--fprint) = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's MD5 Fingerprint: %s", fingerprint);
+ }
+#endif /* LIBSSH2DEBUG */
+#endif /* ! LIBSSH2_MD5 */
+
+ {
+ libssh2_sha1_ctx fingerprint_ctx;
+
+ if(libssh2_sha1_init(&fingerprint_ctx)) {
+ libssh2_sha1_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_sha1_final(fingerprint_ctx,
+ session->server_hostkey_sha1);
+ session->server_hostkey_sha1_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_sha1_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char fingerprint[64], *fprint = fingerprint;
+ int i;
+
+ for(i = 0; i < 20; i++, fprint += 3) {
+ snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]);
+ }
+ *(--fprint) = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's SHA1 Fingerprint: %s", fingerprint);
+ }
+#endif /* LIBSSH2DEBUG */
+
+ /* SHA256 */
+ {
+ libssh2_sha256_ctx fingerprint_ctx;
+
+ if(libssh2_sha256_init(&fingerprint_ctx)) {
+ libssh2_sha256_update(fingerprint_ctx, session->server_hostkey,
+ session->server_hostkey_len);
+ libssh2_sha256_final(fingerprint_ctx,
+ session->server_hostkey_sha256);
+ session->server_hostkey_sha256_valid = TRUE;
+ }
+ else {
+ session->server_hostkey_sha256_valid = FALSE;
+ }
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ char *base64Fingerprint = NULL;
+ _libssh2_base64_encode(session,
+ (const char *)
+ session->server_hostkey_sha256,
+ SHA256_DIGEST_LENGTH, &base64Fingerprint);
+ if(base64Fingerprint != NULL) {
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server's SHA256 Fingerprint: %s",
+ base64Fingerprint);
+ LIBSSH2_FREE(session, base64Fingerprint);
+ }
+ }
+#endif /* LIBSSH2DEBUG */
+
+ if(session->hostkey->init(session, session->server_hostkey,
+ session->server_hostkey_len,
+ &session->server_hostkey_abstract)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Unable to initialize hostkey importer");
+ goto clean_exit;
+ }
+
+ /* server public key Q_S */
+ if(_libssh2_get_string(&buf, &server_public_key,
+ &server_public_key_len)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected key length");
+ goto clean_exit;
+ }
+
+ if(server_public_key_len != LIBSSH2_ED25519_KEY_LEN) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Unexpected curve25519 server "
+ "public key length");
+ goto clean_exit;
+ }
+
+ /* server signature */
+ if(_libssh2_get_string(&buf, &exchange_state->h_sig,
+ &(exchange_state->h_sig_len))) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+ "Unexpected curve25519 server sig length");
+ goto clean_exit;
+ }
+
+ /* Compute the shared secret K */
+ rc = _libssh2_curve25519_gen_k(&exchange_state->k, private_key,
+ server_public_key);
+ if(rc != 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE,
+ "Unable to create ECDH shared secret");
+ goto clean_exit;
+ }
+
+ exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5;
+ if(_libssh2_bn_bits(exchange_state->k) % 8) {
+ /* don't need leading 00 */
+ exchange_state->k_value_len--;
+ }
+ exchange_state->k_value =
+ LIBSSH2_ALLOC(session, exchange_state->k_value_len);
+ if(!exchange_state->k_value) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate buffer for K");
+ goto clean_exit;
+ }
+ _libssh2_htonu32(exchange_state->k_value,
+ exchange_state->k_value_len - 4);
+ if(_libssh2_bn_bits(exchange_state->k) % 8) {
+ _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
+ }
+ else {
+ exchange_state->k_value[4] = 0;
+ _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
+ }
+
+ /*/ verify hash */
+ LIBSSH2_KEX_METHOD_EC_SHA_HASH_CREATE_VERIFY(256);
+
+ if(rc != 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN,
+ "Unable to verify hostkey signature");
+ goto clean_exit;
+ }
+
+ exchange_state->c = SSH_MSG_NEWKEYS;
+ exchange_state->state = libssh2_NB_state_sent;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_sent) {
+ rc = _libssh2_transport_send(session, &exchange_state->c, 1, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send NEWKEYS message");
+ goto clean_exit;
+ }
+
+ exchange_state->state = libssh2_NB_state_sent2;
+ }
+
+ if(exchange_state->state == libssh2_NB_state_sent2) {
+ rc = _libssh2_packet_require(session, SSH_MSG_NEWKEYS,
+ &exchange_state->tmp,
+ &exchange_state->tmp_len, 0, NULL, 0,
+ &exchange_state->req_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc, "Timed out waiting for NEWKEYS");
+ goto clean_exit;
+ }
+
+ /* The first key exchange has been performed, switch to active
+ crypt/comp/mac mode */
+
+ session->state |= LIBSSH2_STATE_NEWKEYS;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Received NEWKEYS message");
+
+ /* This will actually end up being just packet_type(1) for this packet
+ type anyway */
+ LIBSSH2_FREE(session, exchange_state->tmp);
+
+ if(!session->session_id) {
+
+ size_t digest_length = SHA256_DIGEST_LENGTH;
+ session->session_id = LIBSSH2_ALLOC(session, digest_length);
+ if(!session->session_id) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allxcocate buffer for "
+ "SHA digest");
+ goto clean_exit;
+ }
+ memcpy(session->session_id, exchange_state->h_sig_comp,
+ digest_length);
+ session->session_id_len = digest_length;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "session_id calculated");
+ }
+
+ /* Cleanup any existing cipher */
+ if(session->local.crypt->dtor) {
+ session->local.crypt->dtor(session,
+ &session->local.crypt_abstract);
+ }
+
+ /* Calculate IV/Secret/Key for each direction */
+ if(session->local.crypt->init) {
+ unsigned char *iv = NULL, *secret = NULL;
+ int free_iv = 0, free_secret = 0;
+
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, iv,
+ session->local.crypt->
+ iv_len, "A");
+ if(!iv) {
+ ret = -1;
+ goto clean_exit;
+ }
+
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, secret,
+ session->local.crypt->
+ secret_len, "C");
+
+ if(!secret) {
+ LIBSSH2_FREE(session, iv);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ if(session->local.crypt->
+ init(session, session->local.crypt, iv, &free_iv, secret,
+ &free_secret, 1, &session->local.crypt_abstract)) {
+ LIBSSH2_FREE(session, iv);
+ LIBSSH2_FREE(session, secret);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+
+ if(free_iv) {
+ _libssh2_explicit_zero(iv, session->local.crypt->iv_len);
+ LIBSSH2_FREE(session, iv);
+ }
+
+ if(free_secret) {
+ _libssh2_explicit_zero(secret,
+ session->local.crypt->secret_len);
+ LIBSSH2_FREE(session, secret);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server IV and Key calculated");
+
+ if(session->remote.crypt->dtor) {
+ /* Cleanup any existing cipher */
+ session->remote.crypt->dtor(session,
+ &session->remote.crypt_abstract);
+ }
+
+ if(session->remote.crypt->init) {
+ unsigned char *iv = NULL, *secret = NULL;
+ int free_iv = 0, free_secret = 0;
+
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, iv,
+ session->remote.crypt->
+ iv_len, "B");
+
+ if(!iv) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, secret,
+ session->remote.crypt->
+ secret_len, "D");
+
+ if(!secret) {
+ LIBSSH2_FREE(session, iv);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ if(session->remote.crypt->
+ init(session, session->remote.crypt, iv, &free_iv, secret,
+ &free_secret, 0, &session->remote.crypt_abstract)) {
+ LIBSSH2_FREE(session, iv);
+ LIBSSH2_FREE(session, secret);
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+
+ if(free_iv) {
+ _libssh2_explicit_zero(iv, session->remote.crypt->iv_len);
+ LIBSSH2_FREE(session, iv);
+ }
+
+ if(free_secret) {
+ _libssh2_explicit_zero(secret,
+ session->remote.crypt->secret_len);
+ LIBSSH2_FREE(session, secret);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client IV and Key calculated");
+
+ if(session->local.mac->dtor) {
+ session->local.mac->dtor(session, &session->local.mac_abstract);
+ }
+
+ if(session->local.mac->init) {
+ unsigned char *key = NULL;
+ int free_key = 0;
+
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, key,
+ session->local.mac->
+ key_len, "E");
+
+ if(!key) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ session->local.mac->init(session, key, &free_key,
+ &session->local.mac_abstract);
+
+ if(free_key) {
+ _libssh2_explicit_zero(key, session->local.mac->key_len);
+ LIBSSH2_FREE(session, key);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server HMAC Key calculated");
+
+ if(session->remote.mac->dtor) {
+ session->remote.mac->dtor(session, &session->remote.mac_abstract);
+ }
+
+ if(session->remote.mac->init) {
+ unsigned char *key = NULL;
+ int free_key = 0;
+
+ LIBSSH2_KEX_METHOD_SHA_VALUE_HASH(256, key,
+ session->remote.mac->
+ key_len, "F");
+
+ if(!key) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ session->remote.mac->init(session, key, &free_key,
+ &session->remote.mac_abstract);
+
+ if(free_key) {
+ _libssh2_explicit_zero(key, session->remote.mac->key_len);
+ LIBSSH2_FREE(session, key);
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client HMAC Key calculated");
+
+ /* Initialize compression for each direction */
+
+ /* Cleanup any existing compression */
+ if(session->local.comp && session->local.comp->dtor) {
+ session->local.comp->dtor(session, 1,
+ &session->local.comp_abstract);
+ }
+
+ if(session->local.comp && session->local.comp->init) {
+ if(session->local.comp->init(session, 1,
+ &session->local.comp_abstract)) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Client to Server compression initialized");
+
+ if(session->remote.comp && session->remote.comp->dtor) {
+ session->remote.comp->dtor(session, 0,
+ &session->remote.comp_abstract);
+ }
+
+ if(session->remote.comp && session->remote.comp->init) {
+ if(session->remote.comp->init(session, 0,
+ &session->remote.comp_abstract)) {
+ ret = LIBSSH2_ERROR_KEX_FAILURE;
+ goto clean_exit;
+ }
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Server to Client compression initialized");
+ }
+
+clean_exit:
+ _libssh2_bn_free(exchange_state->k);
+ exchange_state->k = NULL;
+
+ if(exchange_state->k_value) {
+ LIBSSH2_FREE(session, exchange_state->k_value);
+ exchange_state->k_value = NULL;
+ }
+
+ exchange_state->state = libssh2_NB_state_idle;
+
+ return ret;
+}
+
+/* kex_method_curve25519_key_exchange
+ *
+ * Elliptic Curve X25519 Key Exchange with SHA256 hash
+ *
+ */
+
+static int
+kex_method_curve25519_key_exchange
+(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
+{
+ int ret = 0;
+ int rc = 0;
+
+ if(key_state->state == libssh2_NB_state_idle) {
+
+ key_state->public_key_oct = NULL;
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ if(key_state->state == libssh2_NB_state_created) {
+ unsigned char *s = NULL;
+
+ rc = strcmp(session->kex->name, "[email protected]");
+ if(rc != 0)
+ rc = strcmp(session->kex->name, "curve25519-sha256");
+
+ if(rc != 0) {
+ ret = _libssh2_error(session, -1,
+ "Unknown KEX curve25519 curve type");
+ goto clean_exit;
+ }
+
+ rc = _libssh2_curve25519_new(session,
+ &key_state->curve25519_public_key,
+ &key_state->curve25519_private_key);
+
+ if(rc != 0) {
+ ret = _libssh2_error(session, rc,
+ "Unable to create private key");
+ goto clean_exit;
+ }
+
+ key_state->request[0] = SSH2_MSG_KEX_ECDH_INIT;
+ s = key_state->request + 1;
+ _libssh2_store_str(&s, (const char *)key_state->curve25519_public_key,
+ LIBSSH2_ED25519_KEY_LEN);
+ key_state->request_len = LIBSSH2_ED25519_KEY_LEN + 5;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+ "Initiating curve25519 SHA2");
+
+ key_state->state = libssh2_NB_state_sent;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent) {
+ rc = _libssh2_transport_send(session, key_state->request,
+ key_state->request_len, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Unable to send ECDH_INIT");
+ goto clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent1;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent1) {
+ rc = _libssh2_packet_require(session, SSH2_MSG_KEX_ECDH_REPLY,
+ &key_state->data, &key_state->data_len,
+ 0, NULL, 0, &key_state->req_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ ret = _libssh2_error(session, rc,
+ "Timeout waiting for ECDH_REPLY reply");
+ goto clean_exit;
+ }
+
+ key_state->state = libssh2_NB_state_sent2;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent2) {
+
+ ret = curve25519_sha256(session, key_state->data, key_state->data_len,
+ key_state->curve25519_public_key,
+ key_state->curve25519_private_key,
+ &key_state->exchange_state);
+
+ if(ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+
+ LIBSSH2_FREE(session, key_state->data);
+ }
+
+clean_exit:
+
+ if(key_state->curve25519_public_key) {
+ _libssh2_explicit_zero(key_state->curve25519_public_key,
+ LIBSSH2_ED25519_KEY_LEN);
+ LIBSSH2_FREE(session, key_state->curve25519_public_key);
+ key_state->curve25519_public_key = NULL;
+ }
+
+ if(key_state->curve25519_private_key) {
+ _libssh2_explicit_zero(key_state->curve25519_private_key,
+ LIBSSH2_ED25519_KEY_LEN);
+ LIBSSH2_FREE(session, key_state->curve25519_private_key);
+ key_state->curve25519_private_key = NULL;
+ }
+
+ key_state->state = libssh2_NB_state_idle;
+
+ return ret;
+}
+
+
+#endif /*LIBSSH2_ED25519*/
+
+
+#define LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY 0x0001
+#define LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY 0x0002
+
+static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group1_sha1 = {
+ "diffie-hellman-group1-sha1",
+ kex_method_diffie_hellman_group1_sha1_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group14_sha1 = {
+ "diffie-hellman-group14-sha1",
+ kex_method_diffie_hellman_group14_sha1_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group14_sha256 = {
+ "diffie-hellman-group14-sha256",
+ kex_method_diffie_hellman_group14_sha256_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group16_sha512 = {
+ "diffie-hellman-group16-sha512",
+ kex_method_diffie_hellman_group16_sha512_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD kex_method_diffie_helman_group18_sha512 = {
+ "diffie-hellman-group18-sha512",
+ kex_method_diffie_hellman_group18_sha512_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD
+kex_method_diffie_helman_group_exchange_sha1 = {
+ "diffie-hellman-group-exchange-sha1",
+ kex_method_diffie_hellman_group_exchange_sha1_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD
+kex_method_diffie_helman_group_exchange_sha256 = {
+ "diffie-hellman-group-exchange-sha256",
+ kex_method_diffie_hellman_group_exchange_sha256_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+#if LIBSSH2_ECDSA
+static const LIBSSH2_KEX_METHOD
+kex_method_ecdh_sha2_nistp256 = {
+ "ecdh-sha2-nistp256",
+ kex_method_ecdh_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD
+kex_method_ecdh_sha2_nistp384 = {
+ "ecdh-sha2-nistp384",
+ kex_method_ecdh_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
+static const LIBSSH2_KEX_METHOD
+kex_method_ecdh_sha2_nistp521 = {
+ "ecdh-sha2-nistp521",
+ kex_method_ecdh_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+#endif
+
+#if LIBSSH2_ED25519
+static const LIBSSH2_KEX_METHOD
+kex_method_ssh_curve25519_sha256_libssh = {
+ kex_method_curve25519_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+static const LIBSSH2_KEX_METHOD
+kex_method_ssh_curve25519_sha256 = {
+ "curve25519-sha256",
+ kex_method_curve25519_key_exchange,
+ LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+#endif
+
+static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = {
+#if LIBSSH2_ED25519
+ &kex_method_ssh_curve25519_sha256,
+ &kex_method_ssh_curve25519_sha256_libssh,
+#endif
+#if LIBSSH2_ECDSA
+ &kex_method_ecdh_sha2_nistp256,
+ &kex_method_ecdh_sha2_nistp384,
+ &kex_method_ecdh_sha2_nistp521,
+#endif
+ &kex_method_diffie_helman_group_exchange_sha256,
+ &kex_method_diffie_helman_group16_sha512,
+ &kex_method_diffie_helman_group18_sha512,
+ &kex_method_diffie_helman_group14_sha256,
+ &kex_method_diffie_helman_group14_sha1,
+ &kex_method_diffie_helman_group1_sha1,
+ &kex_method_diffie_helman_group_exchange_sha1,
+ NULL
+};
+
+typedef struct _LIBSSH2_COMMON_METHOD
+{
+ const char *name;
+} LIBSSH2_COMMON_METHOD;
+
+/* kex_method_strlen
+ * Calculate the length of a particular method list's resulting string
+ * Includes SUM(strlen() of each individual method plus 1 (for coma)) - 1
+ * (because the last coma isn't used)
+ * Another sign of bad coding practices gone mad. Pretend you don't see this.
+ */
+static size_t
+kex_method_strlen(LIBSSH2_COMMON_METHOD ** method)
+{
+ size_t len = 0;
+
+ if(!method || !*method) {
+ return 0;
+ }
+
+ while(*method && (*method)->name) {
+ len += strlen((*method)->name) + 1;
+ method++;
+ }
+
+ return len - 1;
+}
+
+
+
+/* kex_method_list
+ * Generate formatted preference list in buf
+ */
+static size_t
+kex_method_list(unsigned char *buf, size_t list_strlen,
+ LIBSSH2_COMMON_METHOD ** method)
+{
+ _libssh2_htonu32(buf, list_strlen);
+ buf += 4;
+
+ if(!method || !*method) {
+ return 4;
+ }
+
+ while(*method && (*method)->name) {
+ int mlen = strlen((*method)->name);
+ memcpy(buf, (*method)->name, mlen);
+ buf += mlen;
+ *(buf++) = ',';
+ method++;
+ }
+
+ return list_strlen + 4;
+}
+
+
+
+#define LIBSSH2_METHOD_PREFS_LEN(prefvar, defaultvar) \
+ ((prefvar) ? strlen(prefvar) : \
+ kex_method_strlen((LIBSSH2_COMMON_METHOD**)(defaultvar)))
+
+#define LIBSSH2_METHOD_PREFS_STR(buf, prefvarlen, prefvar, defaultvar) \
+ if(prefvar) { \
+ _libssh2_htonu32((buf), (prefvarlen)); \
+ buf += 4; \
+ memcpy((buf), (prefvar), (prefvarlen)); \
+ buf += (prefvarlen); \
+ } \
+ else { \
+ buf += kex_method_list((buf), (prefvarlen), \
+ (LIBSSH2_COMMON_METHOD**)(defaultvar)); \
+ }
+
+/* kexinit
+ * Send SSH_MSG_KEXINIT packet
+ */
+static int kexinit(LIBSSH2_SESSION * session)
+{
+ /* 62 = packet_type(1) + cookie(16) + first_packet_follows(1) +
+ reserved(4) + length longs(40) */
+ size_t data_len = 62;
+ size_t kex_len, hostkey_len = 0;
+ size_t crypt_cs_len, crypt_sc_len;
+ size_t comp_cs_len, comp_sc_len;
+ size_t mac_cs_len, mac_sc_len;
+ size_t lang_cs_len, lang_sc_len;
+ unsigned char *data, *s;
+ int rc;
+
+ if(session->kexinit_state == libssh2_NB_state_idle) {
+ kex_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->kex_prefs, libssh2_kex_methods);
+ hostkey_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->hostkey_prefs,
+ libssh2_hostkey_methods());
+ crypt_cs_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->local.crypt_prefs,
+ libssh2_crypt_methods());
+ crypt_sc_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->remote.crypt_prefs,
+ libssh2_crypt_methods());
+ mac_cs_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->local.mac_prefs,
+ _libssh2_mac_methods());
+ mac_sc_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->remote.mac_prefs,
+ _libssh2_mac_methods());
+ comp_cs_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->local.comp_prefs,
+ _libssh2_comp_methods(session));
+ comp_sc_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->remote.comp_prefs,
+ _libssh2_comp_methods(session));
+ lang_cs_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->local.lang_prefs, NULL);
+ lang_sc_len =
+ LIBSSH2_METHOD_PREFS_LEN(session->remote.lang_prefs, NULL);
+
+ data_len += kex_len + hostkey_len + crypt_cs_len + crypt_sc_len +
+ comp_cs_len + comp_sc_len + mac_cs_len + mac_sc_len +
+ lang_cs_len + lang_sc_len;
+
+ s = data = LIBSSH2_ALLOC(session, data_len);
+ if(!data) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory");
+ }
+
+ *(s++) = SSH_MSG_KEXINIT;
+
+ if(_libssh2_random(s, 16)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_RANDGEN,
+ "Unable to get random bytes "
+ "for KEXINIT cookie");
+ }
+ s += 16;
+
+ /* Ennumerating through these lists twice is probably (certainly?)
+ inefficient from a CPU standpoint, but it saves multiple
+ malloc/realloc calls */
+ LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs,
+ libssh2_kex_methods);
+ LIBSSH2_METHOD_PREFS_STR(s, hostkey_len, session->hostkey_prefs,
+ libssh2_hostkey_methods());
+ LIBSSH2_METHOD_PREFS_STR(s, crypt_cs_len, session->local.crypt_prefs,
+ libssh2_crypt_methods());
+ LIBSSH2_METHOD_PREFS_STR(s, crypt_sc_len, session->remote.crypt_prefs,
+ libssh2_crypt_methods());
+ LIBSSH2_METHOD_PREFS_STR(s, mac_cs_len, session->local.mac_prefs,
+ _libssh2_mac_methods());
+ LIBSSH2_METHOD_PREFS_STR(s, mac_sc_len, session->remote.mac_prefs,
+ _libssh2_mac_methods());
+ LIBSSH2_METHOD_PREFS_STR(s, comp_cs_len, session->local.comp_prefs,
+ _libssh2_comp_methods(session));
+ LIBSSH2_METHOD_PREFS_STR(s, comp_sc_len, session->remote.comp_prefs,
+ _libssh2_comp_methods(session));
+ LIBSSH2_METHOD_PREFS_STR(s, lang_cs_len, session->local.lang_prefs,
+ NULL);
+ LIBSSH2_METHOD_PREFS_STR(s, lang_sc_len, session->remote.lang_prefs,
+ NULL);
+
+ /* No optimistic KEX packet follows */
+ /* Deal with optimistic packets
+ * session->flags |= KEXINIT_OPTIMISTIC
+ * session->flags |= KEXINIT_METHODSMATCH
+ */
+ *(s++) = 0;
+
+ /* Reserved == 0 */
+ _libssh2_htonu32(s, 0);
+
+#ifdef LIBSSH2DEBUG
+ {
+ /* Funnily enough, they'll all "appear" to be '\0' terminated */
+ unsigned char *p = data + 21; /* type(1) + cookie(16) + len(4) */
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent KEX: %s", p);
+ p += kex_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent HOSTKEY: %s", p);
+ p += hostkey_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent CRYPT_CS: %s", p);
+ p += crypt_cs_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent CRYPT_SC: %s", p);
+ p += crypt_sc_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent MAC_CS: %s", p);
+ p += mac_cs_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent MAC_SC: %s", p);
+ p += mac_sc_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent COMP_CS: %s", p);
+ p += comp_cs_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent COMP_SC: %s", p);
+ p += comp_sc_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent LANG_CS: %s", p);
+ p += lang_cs_len + 4;
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sent LANG_SC: %s", p);
+ p += lang_sc_len + 4;
+ }
+#endif /* LIBSSH2DEBUG */
+
+ session->kexinit_state = libssh2_NB_state_created;
+ }
+ else {
+ data = session->kexinit_data;
+ data_len = session->kexinit_data_len;
+ /* zap the variables to ensure there is NOT a double free later */
+ session->kexinit_data = NULL;
+ session->kexinit_data_len = 0;
+ }
+
+ rc = _libssh2_transport_send(session, data, data_len, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ session->kexinit_data = data;
+ session->kexinit_data_len = data_len;
+ return rc;
+ }
+ else if(rc) {
+ LIBSSH2_FREE(session, data);
+ session->kexinit_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send KEXINIT packet to remote host");
+
+ }
+
+ if(session->local.kexinit) {
+ LIBSSH2_FREE(session, session->local.kexinit);
+ }
+
+ session->local.kexinit = data;
+ session->local.kexinit_len = data_len;
+
+ session->kexinit_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/* kex_agree_instr
+ * Kex specific variant of strstr()
+ * Needle must be precede by BOL or ',', and followed by ',' or EOL
+ */
+static unsigned char *
+kex_agree_instr(unsigned char *haystack, unsigned long haystack_len,
+ const unsigned char *needle, unsigned long needle_len)
+{
+ unsigned char *s;
+ unsigned char *end_haystack;
+ unsigned long left;
+
+ if(haystack == NULL || needle == NULL) {
+ return NULL;
+ }
+
+ /* Haystack too short to bother trying */
+ if(haystack_len < needle_len || needle_len == 0) {
+ return NULL;
+ }
+
+ s = haystack;
+ end_haystack = &haystack[haystack_len];
+ left = end_haystack - s;
+
+ /* Needle at start of haystack */
+ if((strncmp((char *) haystack, (char *) needle, needle_len) == 0) &&
+ (needle_len == haystack_len || haystack[needle_len] == ',')) {
+ return haystack;
+ }
+
+ /* Search until we run out of comas or we run out of haystack,
+ whichever comes first */
+ while((s = (unsigned char *) memchr((char *) s, ',', left))) {
+ /* Advance buffer past coma if we can */
+ left = end_haystack - s;
+ if((left >= 1) && (left <= haystack_len) && (left > needle_len)) {
+ s++;
+ }
+ else {
+ return NULL;
+ }
+
+ /* Needle at X position */
+ if((strncmp((char *) s, (char *) needle, needle_len) == 0) &&
+ (((s - haystack) + needle_len) == haystack_len
+ || s[needle_len] == ',')) {
+ return s;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/* kex_get_method_by_name
+ */
+static const LIBSSH2_COMMON_METHOD *
+kex_get_method_by_name(const char *name, size_t name_len,
+ const LIBSSH2_COMMON_METHOD ** methodlist)
+{
+ while(*methodlist) {
+ if((strlen((*methodlist)->name) == name_len) &&
+ (strncmp((*methodlist)->name, name, name_len) == 0)) {
+ return *methodlist;
+ }
+ methodlist++;
+ }
+ return NULL;
+}
+
+
+
+/* kex_agree_hostkey
+ * Agree on a Hostkey which works with this kex
+ */
+static int kex_agree_hostkey(LIBSSH2_SESSION * session,
+ unsigned long kex_flags,
+ unsigned char *hostkey, unsigned long hostkey_len)
+{
+ const LIBSSH2_HOSTKEY_METHOD **hostkeyp = libssh2_hostkey_methods();
+ unsigned char *s;
+
+ if(session->hostkey_prefs) {
+ s = (unsigned char *) session->hostkey_prefs;
+
+ while(s && *s) {
+ unsigned char *p = (unsigned char *) strchr((char *) s, ',');
+ size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
+ if(kex_agree_instr(hostkey, hostkey_len, s, method_len)) {
+ const LIBSSH2_HOSTKEY_METHOD *method =
+ (const LIBSSH2_HOSTKEY_METHOD *)
+ kex_get_method_by_name((char *) s, method_len,
+ (const LIBSSH2_COMMON_METHOD **)
+ hostkeyp);
+
+ if(!method) {
+ /* Invalid method -- Should never be reached */
+ return -1;
+ }
+
+ /* So far so good, but does it suit our purposes? (Encrypting
+ vs Signing) */
+ if(((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) ==
+ 0) || (method->encrypt)) {
+ /* Either this hostkey can do encryption or this kex just
+ doesn't require it */
+ if(((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY)
+ == 0) || (method->sig_verify)) {
+ /* Either this hostkey can do signing or this kex just
+ doesn't require it */
+ session->hostkey = method;
+ return 0;
+ }
+ }
+ }
+
+ s = p ? p + 1 : NULL;
+ }
+ return -1;
+ }
+
+ while(hostkeyp && (*hostkeyp) && (*hostkeyp)->name) {
+ s = kex_agree_instr(hostkey, hostkey_len,
+ (unsigned char *) (*hostkeyp)->name,
+ strlen((*hostkeyp)->name));
+ if(s) {
+ /* So far so good, but does it suit our purposes? (Encrypting vs
+ Signing) */
+ if(((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == 0) ||
+ ((*hostkeyp)->encrypt)) {
+ /* Either this hostkey can do encryption or this kex just
+ doesn't require it */
+ if(((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY) ==
+ 0) || ((*hostkeyp)->sig_verify)) {
+ /* Either this hostkey can do signing or this kex just
+ doesn't require it */
+ session->hostkey = *hostkeyp;
+ return 0;
+ }
+ }
+ }
+ hostkeyp++;
+ }
+
+ return -1;
+}
+
+
+
+/* kex_agree_kex_hostkey
+ * Agree on a Key Exchange method and a hostkey encoding type
+ */
+static int kex_agree_kex_hostkey(LIBSSH2_SESSION * session, unsigned char *kex,
+ unsigned long kex_len, unsigned char *hostkey,
+ unsigned long hostkey_len)
+{
+ const LIBSSH2_KEX_METHOD **kexp = libssh2_kex_methods;
+ unsigned char *s;
+
+ if(session->kex_prefs) {
+ s = (unsigned char *) session->kex_prefs;
+
+ while(s && *s) {
+ unsigned char *q, *p = (unsigned char *) strchr((char *) s, ',');
+ size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
+ q = kex_agree_instr(kex, kex_len, s, method_len);
+ if(q) {
+ const LIBSSH2_KEX_METHOD *method = (const LIBSSH2_KEX_METHOD *)
+ kex_get_method_by_name((char *) s, method_len,
+ (const LIBSSH2_COMMON_METHOD **)
+ kexp);
+
+ if(!method) {
+ /* Invalid method -- Should never be reached */
+ return -1;
+ }
+
+ /* We've agreed on a key exchange method,
+ * Can we agree on a hostkey that works with this kex?
+ */
+ if(kex_agree_hostkey(session, method->flags, hostkey,
+ hostkey_len) == 0) {
+ session->kex = method;
+ if(session->burn_optimistic_kexinit && (kex == q)) {
+ /* Server sent an optimistic packet, and client agrees
+ * with preference cancel burning the first KEX_INIT
+ * packet that comes in */
+ session->burn_optimistic_kexinit = 0;
+ }
+ return 0;
+ }
+ }
+
+ s = p ? p + 1 : NULL;
+ }
+ return -1;
+ }
+
+ while(*kexp && (*kexp)->name) {
+ s = kex_agree_instr(kex, kex_len,
+ (unsigned char *) (*kexp)->name,
+ strlen((*kexp)->name));
+ if(s) {
+ /* We've agreed on a key exchange method,
+ * Can we agree on a hostkey that works with this kex?
+ */
+ if(kex_agree_hostkey(session, (*kexp)->flags, hostkey,
+ hostkey_len) == 0) {
+ session->kex = *kexp;
+ if(session->burn_optimistic_kexinit && (kex == s)) {
+ /* Server sent an optimistic packet, and client agrees
+ * with preference cancel burning the first KEX_INIT
+ * packet that comes in */
+ session->burn_optimistic_kexinit = 0;
+ }
+ return 0;
+ }
+ }
+ kexp++;
+ }
+ return -1;
+}
+
+
+
+/* kex_agree_crypt
+ * Agree on a cipher algo
+ */
+static int kex_agree_crypt(LIBSSH2_SESSION * session,
+ libssh2_endpoint_data *endpoint,
+ unsigned char *crypt,
+ unsigned long crypt_len)
+{
+ const LIBSSH2_CRYPT_METHOD **cryptp = libssh2_crypt_methods();
+ unsigned char *s;
+
+ (void) session;
+
+ if(endpoint->crypt_prefs) {
+ s = (unsigned char *) endpoint->crypt_prefs;
+
+ while(s && *s) {
+ unsigned char *p = (unsigned char *) strchr((char *) s, ',');
+ size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
+
+ if(kex_agree_instr(crypt, crypt_len, s, method_len)) {
+ const LIBSSH2_CRYPT_METHOD *method =
+ (const LIBSSH2_CRYPT_METHOD *)
+ kex_get_method_by_name((char *) s, method_len,
+ (const LIBSSH2_COMMON_METHOD **)
+ cryptp);
+
+ if(!method) {
+ /* Invalid method -- Should never be reached */
+ return -1;
+ }
+
+ endpoint->crypt = method;
+ return 0;
+ }
+
+ s = p ? p + 1 : NULL;
+ }
+ return -1;
+ }
+
+ while(*cryptp && (*cryptp)->name) {
+ s = kex_agree_instr(crypt, crypt_len,
+ (unsigned char *) (*cryptp)->name,
+ strlen((*cryptp)->name));
+ if(s) {
+ endpoint->crypt = *cryptp;
+ return 0;
+ }
+ cryptp++;
+ }
+
+ return -1;
+}
+
+
+
+/* kex_agree_mac
+ * Agree on a message authentication hash
+ */
+static int kex_agree_mac(LIBSSH2_SESSION * session,
+ libssh2_endpoint_data * endpoint, unsigned char *mac,
+ unsigned long mac_len)
+{
+ const LIBSSH2_MAC_METHOD **macp = _libssh2_mac_methods();
+ unsigned char *s;
+ (void) session;
+
+ if(endpoint->mac_prefs) {
+ s = (unsigned char *) endpoint->mac_prefs;
+
+ while(s && *s) {
+ unsigned char *p = (unsigned char *) strchr((char *) s, ',');
+ size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
+
+ if(kex_agree_instr(mac, mac_len, s, method_len)) {
+ const LIBSSH2_MAC_METHOD *method = (const LIBSSH2_MAC_METHOD *)
+ kex_get_method_by_name((char *) s, method_len,
+ (const LIBSSH2_COMMON_METHOD **)
+ macp);
+
+ if(!method) {
+ /* Invalid method -- Should never be reached */
+ return -1;
+ }
+
+ endpoint->mac = method;
+ return 0;
+ }
+
+ s = p ? p + 1 : NULL;
+ }
+ return -1;
+ }
+
+ while(*macp && (*macp)->name) {
+ s = kex_agree_instr(mac, mac_len, (unsigned char *) (*macp)->name,
+ strlen((*macp)->name));
+ if(s) {
+ endpoint->mac = *macp;
+ return 0;
+ }
+ macp++;
+ }
+
+ return -1;
+}
+
+
+
+/* kex_agree_comp
+ * Agree on a compression scheme
+ */
+static int kex_agree_comp(LIBSSH2_SESSION *session,
+ libssh2_endpoint_data *endpoint, unsigned char *comp,
+ unsigned long comp_len)
+{
+ const LIBSSH2_COMP_METHOD **compp = _libssh2_comp_methods(session);
+ unsigned char *s;
+ (void) session;
+
+ if(endpoint->comp_prefs) {
+ s = (unsigned char *) endpoint->comp_prefs;
+
+ while(s && *s) {
+ unsigned char *p = (unsigned char *) strchr((char *) s, ',');
+ size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
+
+ if(kex_agree_instr(comp, comp_len, s, method_len)) {
+ const LIBSSH2_COMP_METHOD *method =
+ (const LIBSSH2_COMP_METHOD *)
+ kex_get_method_by_name((char *) s, method_len,
+ (const LIBSSH2_COMMON_METHOD **)
+ compp);
+
+ if(!method) {
+ /* Invalid method -- Should never be reached */
+ return -1;
+ }
+
+ endpoint->comp = method;
+ return 0;
+ }
+
+ s = p ? p + 1 : NULL;
+ }
+ return -1;
+ }
+
+ while(*compp && (*compp)->name) {
+ s = kex_agree_instr(comp, comp_len, (unsigned char *) (*compp)->name,
+ strlen((*compp)->name));
+ if(s) {
+ endpoint->comp = *compp;
+ return 0;
+ }
+ compp++;
+ }
+
+ return -1;
+}
+
+
+/* TODO: When in server mode we need to turn this logic on its head
+ * The Client gets to make the final call on "agreed methods"
+ */
+
+/* kex_agree_methods
+ * Decide which specific method to use of the methods offered by each party
+ */
+static int kex_agree_methods(LIBSSH2_SESSION * session, unsigned char *data,
+ unsigned data_len)
+{
+ unsigned char *kex, *hostkey, *crypt_cs, *crypt_sc, *comp_cs, *comp_sc,
+ *mac_cs, *mac_sc;
+ size_t kex_len, hostkey_len, crypt_cs_len, crypt_sc_len, comp_cs_len;
+ size_t comp_sc_len, mac_cs_len, mac_sc_len;
+ struct string_buf buf;
+
+ if(data_len < 17)
+ return -1;
+
+ buf.data = (unsigned char *)data;
+ buf.len = data_len;
+ buf.dataptr = buf.data;
+ buf.dataptr++; /* advance past packet type */
+
+ /* Skip cookie, don't worry, it's preserved in the kexinit field */
+ buf.dataptr += 16;
+
+ /* Locate each string */
+ if(_libssh2_get_string(&buf, &kex, &kex_len))
+ return -1;
+ if(_libssh2_get_string(&buf, &hostkey, &hostkey_len))
+ return -1;
+ if(_libssh2_get_string(&buf, &crypt_cs, &crypt_cs_len))
+ return -1;
+ if(_libssh2_get_string(&buf, &crypt_sc, &crypt_sc_len))
+ return -1;
+ if(_libssh2_get_string(&buf, &mac_cs, &mac_cs_len))
+ return -1;
+ if(_libssh2_get_string(&buf, &mac_sc, &mac_sc_len))
+ return -1;
+ if(_libssh2_get_string(&buf, &comp_cs, &comp_cs_len))
+ return -1;
+ if(_libssh2_get_string(&buf, &comp_sc, &comp_sc_len))
+ return -1;
+
+ /* If the server sent an optimistic packet, assume that it guessed wrong.
+ * If the guess is determined to be right (by kex_agree_kex_hostkey)
+ * This flag will be reset to zero so that it's not ignored */
+ if(_libssh2_check_length(&buf, 1)) {
+ session->burn_optimistic_kexinit = *(buf.dataptr++);
+ }
+ else {
+ return -1;
+ }
+
+ /* Next uint32 in packet is all zeros (reserved) */
+
+ if(kex_agree_kex_hostkey(session, kex, kex_len, hostkey, hostkey_len)) {
+ return -1;
+ }
+
+ if(kex_agree_crypt(session, &session->local, crypt_cs, crypt_cs_len)
+ || kex_agree_crypt(session, &session->remote, crypt_sc,
+ crypt_sc_len)) {
+ return -1;
+ }
+
+ if(kex_agree_mac(session, &session->local, mac_cs, mac_cs_len) ||
+ kex_agree_mac(session, &session->remote, mac_sc, mac_sc_len)) {
+ return -1;
+ }
+
+ if(kex_agree_comp(session, &session->local, comp_cs, comp_cs_len) ||
+ kex_agree_comp(session, &session->remote, comp_sc, comp_sc_len)) {
+ return -1;
+ }
+
+#if 0
+ if(libssh2_kex_agree_lang(session, &session->local, lang_cs, lang_cs_len)
+ || libssh2_kex_agree_lang(session, &session->remote, lang_sc,
+ lang_sc_len)) {
+ return -1;
+ }
+#endif
+
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on KEX method: %s",
+ session->kex->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on HOSTKEY method: %s",
+ session->hostkey->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on CRYPT_CS method: %s",
+ session->local.crypt->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on CRYPT_SC method: %s",
+ session->remote.crypt->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on MAC_CS method: %s",
+ session->local.mac->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on MAC_SC method: %s",
+ session->remote.mac->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on COMP_CS method: %s",
+ session->local.comp->name);
+ _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Agreed on COMP_SC method: %s",
+ session->remote.comp->name);
+
+ return 0;
+}
+
+
+
+/* _libssh2_kex_exchange
+ * Exchange keys
+ * Returns 0 on success, non-zero on failure
+ *
+ * Returns some errors without _libssh2_error()
+ */
+int
+_libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
+ key_exchange_state_t * key_state)
+{
+ int rc = 0;
+ int retcode;
+
+ session->state |= LIBSSH2_STATE_KEX_ACTIVE;
+
+ if(key_state->state == libssh2_NB_state_idle) {
+ /* Prevent loop in packet_add() */
+ session->state |= LIBSSH2_STATE_EXCHANGING_KEYS;
+
+ if(reexchange) {
+ session->kex = NULL;
+
+ if(session->hostkey && session->hostkey->dtor) {
+ session->hostkey->dtor(session,
+ &session->server_hostkey_abstract);
+ }
+ session->hostkey = NULL;
+ }
+
+ key_state->state = libssh2_NB_state_created;
+ }
+
+ if(!session->kex || !session->hostkey) {
+ if(key_state->state == libssh2_NB_state_created) {
+ /* Preserve in case of failure */
+ key_state->oldlocal = session->local.kexinit;
+ key_state->oldlocal_len = session->local.kexinit_len;
+
+ session->local.kexinit = NULL;
+
+ key_state->state = libssh2_NB_state_sent;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent) {
+ retcode = kexinit(session);
+ if(retcode == LIBSSH2_ERROR_EAGAIN) {
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ return retcode;
+ }
+ else if(retcode) {
+ session->local.kexinit = key_state->oldlocal;
+ session->local.kexinit_len = key_state->oldlocal_len;
+ key_state->state = libssh2_NB_state_idle;
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
+ return -1;
+ }
+
+ key_state->state = libssh2_NB_state_sent1;
+ }
+
+ if(key_state->state == libssh2_NB_state_sent1) {
+ retcode =
+ _libssh2_packet_require(session, SSH_MSG_KEXINIT,
+ &key_state->data,
+ &key_state->data_len, 0, NULL, 0,
+ &key_state->req_state);
+ if(retcode == LIBSSH2_ERROR_EAGAIN) {
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ return retcode;
+ }
+ else if(retcode) {
+ if(session->local.kexinit) {
+ LIBSSH2_FREE(session, session->local.kexinit);
+ }
+ session->local.kexinit = key_state->oldlocal;
+ session->local.kexinit_len = key_state->oldlocal_len;
+ key_state->state = libssh2_NB_state_idle;
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
+ return -1;
+ }
+
+ if(session->remote.kexinit) {
+ LIBSSH2_FREE(session, session->remote.kexinit);
+ }
+ session->remote.kexinit = key_state->data;
+ session->remote.kexinit_len = key_state->data_len;
+
+ if(kex_agree_methods(session, key_state->data,
+ key_state->data_len))
+ rc = LIBSSH2_ERROR_KEX_FAILURE;
+
+ key_state->state = libssh2_NB_state_sent2;
+ }
+ }
+ else {
+ key_state->state = libssh2_NB_state_sent2;
+ }
+
+ if(rc == 0 && session->kex) {
+ if(key_state->state == libssh2_NB_state_sent2) {
+ retcode = session->kex->exchange_keys(session,
+ &key_state->key_state_low);
+ if(retcode == LIBSSH2_ERROR_EAGAIN) {
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ return retcode;
+ }
+ else if(retcode) {
+ rc = _libssh2_error(session,
+ LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE,
+ "Unrecoverable error exchanging keys");
+ }
+ }
+ }
+
+ /* Done with kexinit buffers */
+ if(session->local.kexinit) {
+ LIBSSH2_FREE(session, session->local.kexinit);
+ session->local.kexinit = NULL;
+ }
+ if(session->remote.kexinit) {
+ LIBSSH2_FREE(session, session->remote.kexinit);
+ session->remote.kexinit = NULL;
+ }
+
+ session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
+ session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
+
+ key_state->state = libssh2_NB_state_idle;
+
+ return rc;
+}
+
+
+
+/* libssh2_session_method_pref
+ * Set preferred method
+ */
+LIBSSH2_API int
+libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type,
+ const char *prefs)
+{
+ char **prefvar, *s, *newprefs;
+ int prefs_len = strlen(prefs);
+ const LIBSSH2_COMMON_METHOD **mlist;
+
+ switch(method_type) {
+ case LIBSSH2_METHOD_KEX:
+ prefvar = &session->kex_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_kex_methods;
+ break;
+
+ case LIBSSH2_METHOD_HOSTKEY:
+ prefvar = &session->hostkey_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_hostkey_methods();
+ break;
+
+ case LIBSSH2_METHOD_CRYPT_CS:
+ prefvar = &session->local.crypt_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods();
+ break;
+
+ case LIBSSH2_METHOD_CRYPT_SC:
+ prefvar = &session->remote.crypt_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods();
+ break;
+
+ case LIBSSH2_METHOD_MAC_CS:
+ prefvar = &session->local.mac_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods();
+ break;
+
+ case LIBSSH2_METHOD_MAC_SC:
+ prefvar = &session->remote.mac_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods();
+ break;
+
+ case LIBSSH2_METHOD_COMP_CS:
+ prefvar = &session->local.comp_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **)
+ _libssh2_comp_methods(session);
+ break;
+
+ case LIBSSH2_METHOD_COMP_SC:
+ prefvar = &session->remote.comp_prefs;
+ mlist = (const LIBSSH2_COMMON_METHOD **)
+ _libssh2_comp_methods(session);
+ break;
+
+ case LIBSSH2_METHOD_LANG_CS:
+ prefvar = &session->local.lang_prefs;
+ mlist = NULL;
+ break;
+
+ case LIBSSH2_METHOD_LANG_SC:
+ prefvar = &session->remote.lang_prefs;
+ mlist = NULL;
+ break;
+
+ default:
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "Invalid parameter specified for method_type");
+ }
+
+ s = newprefs = LIBSSH2_ALLOC(session, prefs_len + 1);
+ if(!newprefs) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Error allocated space for method preferences");
+ }
+ memcpy(s, prefs, prefs_len + 1);
+
+ while(s && *s && mlist) {
+ char *p = strchr(s, ',');
+ int method_len = p ? (p - s) : (int) strlen(s);
+
+ if(!kex_get_method_by_name(s, method_len, mlist)) {
+ /* Strip out unsupported method */
+ if(p) {
+ memcpy(s, p + 1, strlen(s) - method_len);
+ }
+ else {
+ if(s > newprefs) {
+ *(--s) = '\0';
+ }
+ else {
+ *s = '\0';
+ }
+ }
+ }
+ else {
+ s = p ? (p + 1) : NULL;
+ }
+ }
+
+ if(!*newprefs) {
+ LIBSSH2_FREE(session, newprefs);
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "The requested method(s) are not currently "
+ "supported");
+ }
+
+ if(*prefvar) {
+ LIBSSH2_FREE(session, *prefvar);
+ }
+ *prefvar = newprefs;
+
+ return 0;
+}
+
+/*
+ * libssh2_session_supported_algs()
+ * returns a number of returned algorithms (a positive number) on success,
+ * a negative number on failure
+ */
+
+LIBSSH2_API int libssh2_session_supported_algs(LIBSSH2_SESSION* session,
+ int method_type,
+ const char ***algs)
+{
+ unsigned int i;
+ unsigned int j;
+ unsigned int ialg;
+ const LIBSSH2_COMMON_METHOD **mlist;
+
+ /* to prevent coredumps due to dereferencing of NULL */
+ if(NULL == algs)
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+ "algs must not be NULL");
+
+ switch(method_type) {
+ case LIBSSH2_METHOD_KEX:
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_kex_methods;
+ break;
+
+ case LIBSSH2_METHOD_HOSTKEY:
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_hostkey_methods();
+ break;
+
+ case LIBSSH2_METHOD_CRYPT_CS:
+ case LIBSSH2_METHOD_CRYPT_SC:
+ mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods();
+ break;
+
+ case LIBSSH2_METHOD_MAC_CS:
+ case LIBSSH2_METHOD_MAC_SC:
+ mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_mac_methods();
+ break;
+
+ case LIBSSH2_METHOD_COMP_CS:
+ case LIBSSH2_METHOD_COMP_SC:
+ mlist = (const LIBSSH2_COMMON_METHOD **)
+ _libssh2_comp_methods(session);
+ break;
+
+ default:
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unknown method type");
+ } /* switch */
+
+ /* weird situation */
+ if(NULL == mlist)
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "No algorithm found");
+
+ /*
+ mlist is looped through twice. The first time to find the number od
+ supported algorithms (needed to allocate the proper size of array) and
+ the second time to actually copy the pointers. Typically this function
+ will not be called often (typically at the beginning of a session) and
+ the number of algorithms (i.e. number of iterations in one loop) will
+ not be high (typically it will not exceed 20) for quite a long time.
+
+ So double looping really shouldn't be an issue and it is definitely a
+ better solution than reallocation several times.
+ */
+
+ /* count the number of supported algorithms */
+ for(i = 0, ialg = 0; NULL != mlist[i]; i++) {
+ /* do not count fields with NULL name */
+ if(mlist[i]->name)
+ ialg++;
+ }
+
+ /* weird situation, no algorithm found */
+ if(0 == ialg)
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "No algorithm found");
+
+ /* allocate buffer */
+ *algs = (const char **) LIBSSH2_ALLOC(session, ialg*sizeof(const char *));
+ if(NULL == *algs) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Memory allocation failed");
+ }
+ /* Past this point *algs must be deallocated in case of an error!! */
+
+ /* copy non-NULL pointers only */
+ for(i = 0, j = 0; NULL != mlist[i] && j < ialg; i++) {
+ if(NULL == mlist[i]->name) {
+ /* maybe a weird situation but if it occurs, do not include NULL
+ pointers */
+ continue;
+ }
+
+ /* note that [] has higher priority than * (dereferencing) */
+ (*algs)[j++] = mlist[i]->name;
+ }
+
+ /* correct number of pointers copied? (test the code above) */
+ if(j != ialg) {
+ /* deallocate buffer */
+ LIBSSH2_FREE(session, (void *)*algs);
+ *algs = NULL;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_USE,
+ "Internal error");
+ }
+
+ return ialg;
+}
diff --git a/contrib/libs/libssh2/src/knownhost.c b/contrib/libs/libssh2/src/knownhost.c
new file mode 100644
index 00000000000..77798fbfded
--- /dev/null
+++ b/contrib/libs/libssh2/src/knownhost.c
@@ -0,0 +1,1271 @@
+/*
+ * Copyright (c) 2009-2019 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include "misc.h"
+
+struct known_host {
+ struct list_node node;
+ char *name; /* points to the name or the hash (allocated) */
+ size_t name_len; /* needed for hashed data */
+ int port; /* if non-zero, a specific port this key is for on this
+ host */
+ int typemask; /* plain, sha1, custom, ... */
+ char *salt; /* points to binary salt (allocated) */
+ size_t salt_len; /* size of salt */
+ char *key; /* the (allocated) associated key. This is kept base64
+ encoded in memory. */
+ char *key_type_name; /* the (allocated) key type name */
+ size_t key_type_len; /* size of key_type_name */
+ char *comment; /* the (allocated) optional comment text, may be
+ NULL */
+ size_t comment_len; /* the size of comment */
+
+ /* this is the struct we expose externally */
+ struct libssh2_knownhost external;
+};
+
+struct _LIBSSH2_KNOWNHOSTS
+{
+ LIBSSH2_SESSION *session; /* the session this "belongs to" */
+ struct list_head head;
+};
+
+static void free_host(LIBSSH2_SESSION *session, struct known_host *entry)
+{
+ if(entry) {
+ if(entry->comment)
+ LIBSSH2_FREE(session, entry->comment);
+ if(entry->key_type_name)
+ LIBSSH2_FREE(session, entry->key_type_name);
+ if(entry->key)
+ LIBSSH2_FREE(session, entry->key);
+ if(entry->salt)
+ LIBSSH2_FREE(session, entry->salt);
+ if(entry->name)
+ LIBSSH2_FREE(session, entry->name);
+ LIBSSH2_FREE(session, entry);
+ }
+}
+
+/*
+ * libssh2_knownhost_init
+ *
+ * Init a collection of known hosts. Returns the pointer to a collection.
+ *
+ */
+LIBSSH2_API LIBSSH2_KNOWNHOSTS *
+libssh2_knownhost_init(LIBSSH2_SESSION *session)
+{
+ LIBSSH2_KNOWNHOSTS *knh =
+ LIBSSH2_ALLOC(session, sizeof(struct _LIBSSH2_KNOWNHOSTS));
+
+ if(!knh) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for known-hosts "
+ "collection");
+ return NULL;
+ }
+
+ knh->session = session;
+
+ _libssh2_list_init(&knh->head);
+
+ return knh;
+}
+
+#define KNOWNHOST_MAGIC 0xdeadcafe
+/*
+ * knownhost_to_external()
+ *
+ * Copies data from the internal to the external representation struct.
+ *
+ */
+static struct libssh2_knownhost *knownhost_to_external(struct known_host *node)
+{
+ struct libssh2_knownhost *ext = &node->external;
+
+ ext->magic = KNOWNHOST_MAGIC;
+ ext->node = node;
+ ext->name = ((node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) ==
+ LIBSSH2_KNOWNHOST_TYPE_PLAIN)? node->name:NULL;
+ ext->key = node->key;
+ ext->typemask = node->typemask;
+
+ return ext;
+}
+
+static int
+knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, const char *salt,
+ const char *key_type_name, size_t key_type_len,
+ const char *key, size_t keylen,
+ const char *comment, size_t commentlen,
+ int typemask, struct libssh2_knownhost **store)
+{
+ struct known_host *entry;
+ size_t hostlen = strlen(host);
+ int rc;
+ char *ptr;
+ unsigned int ptrlen;
+
+ /* make sure we have a key type set */
+ if(!(typemask & LIBSSH2_KNOWNHOST_KEY_MASK))
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL,
+ "No key type set");
+
+ entry = LIBSSH2_CALLOC(hosts->session, sizeof(struct known_host));
+ if(!entry)
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for known host "
+ "entry");
+
+ entry->typemask = typemask;
+
+ switch(entry->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) {
+ case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
+ case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
+ entry->name = LIBSSH2_ALLOC(hosts->session, hostlen + 1);
+ if(!entry->name) {
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for host name");
+ goto error;
+ }
+ memcpy(entry->name, host, hostlen + 1);
+ entry->name_len = hostlen;
+ break;
+ case LIBSSH2_KNOWNHOST_TYPE_SHA1:
+ rc = libssh2_base64_decode(hosts->session, &ptr, &ptrlen,
+ host, hostlen);
+ if(rc)
+ goto error;
+ entry->name = ptr;
+ entry->name_len = ptrlen;
+
+ rc = libssh2_base64_decode(hosts->session, &ptr, &ptrlen,
+ salt, strlen(salt));
+ if(rc)
+ goto error;
+ entry->salt = ptr;
+ entry->salt_len = ptrlen;
+ break;
+ default:
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unknown host name type");
+ goto error;
+ }
+
+ if(typemask & LIBSSH2_KNOWNHOST_KEYENC_BASE64) {
+ /* the provided key is base64 encoded already */
+ if(!keylen)
+ keylen = strlen(key);
+ entry->key = LIBSSH2_ALLOC(hosts->session, keylen + 1);
+ if(!entry->key) {
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for key");
+ goto error;
+ }
+ memcpy(entry->key, key, keylen + 1);
+ entry->key[keylen] = 0; /* force a terminating zero trailer */
+ }
+ else {
+ /* key is raw, we base64 encode it and store it as such */
+ size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen,
+ &ptr);
+ if(!nlen) {
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "base64-encoded key");
+ goto error;
+ }
+
+ entry->key = ptr;
+ }
+
+ if(key_type_name && ((typemask & LIBSSH2_KNOWNHOST_KEY_MASK) ==
+ LIBSSH2_KNOWNHOST_KEY_UNKNOWN)) {
+ entry->key_type_name = LIBSSH2_ALLOC(hosts->session, key_type_len + 1);
+ if(!entry->key_type_name) {
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for key type");
+ goto error;
+ }
+ memcpy(entry->key_type_name, key_type_name, key_type_len);
+ entry->key_type_name[key_type_len] = 0;
+ entry->key_type_len = key_type_len;
+ }
+
+ if(comment) {
+ entry->comment = LIBSSH2_ALLOC(hosts->session, commentlen + 1);
+ if(!entry->comment) {
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for comment");
+ goto error;
+ }
+ memcpy(entry->comment, comment, commentlen + 1);
+ entry->comment[commentlen] = 0; /* force a terminating zero trailer */
+ entry->comment_len = commentlen;
+ }
+ else {
+ entry->comment = NULL;
+ }
+
+ /* add this new host to the big list of known hosts */
+ _libssh2_list_add(&hosts->head, &entry->node);
+
+ if(store)
+ *store = knownhost_to_external(entry);
+
+ return LIBSSH2_ERROR_NONE;
+ error:
+ free_host(hosts->session, entry);
+ return rc;
+}
+
+/*
+ * libssh2_knownhost_add
+ *
+ * Add a host and its associated key to the collection of known hosts.
+ *
+ * The 'type' argument specifies on what format the given host and keys are:
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - SHA1(<salt> <host>) base64-encoded!
+ * custom - another hash
+ *
+ * If 'sha1' is selected as type, the salt must be provided to the salt
+ * argument. This too base64 encoded.
+ *
+ * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If
+ * a custom type is used, salt is ignored and you must provide the host
+ * pre-hashed when checking for it in the libssh2_knownhost_check() function.
+ *
+ * The keylen parameter may be omitted (zero) if the key is provided as a
+ * NULL-terminated base64-encoded string.
+ */
+
+LIBSSH2_API int
+libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, const char *salt,
+ const char *key, size_t keylen,
+ int typemask, struct libssh2_knownhost **store)
+{
+ return knownhost_add(hosts, host, salt, NULL, 0, key, keylen, NULL,
+ 0, typemask, store);
+}
+
+
+/*
+ * libssh2_knownhost_addc
+ *
+ * Add a host and its associated key to the collection of known hosts.
+ *
+ * Takes a comment argument that may be NULL. A NULL comment indicates
+ * there is no comment and the entry will end directly after the key
+ * when written out to a file. An empty string "" comment will indicate an
+ * empty comment which will cause a single space to be written after the key.
+ *
+ * The 'type' argument specifies on what format the given host and keys are:
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - SHA1(<salt> <host>) base64-encoded!
+ * custom - another hash
+ *
+ * If 'sha1' is selected as type, the salt must be provided to the salt
+ * argument. This too base64 encoded.
+ *
+ * The SHA-1 hash is what OpenSSH can be told to use in known_hosts files. If
+ * a custom type is used, salt is ignored and you must provide the host
+ * pre-hashed when checking for it in the libssh2_knownhost_check() function.
+ *
+ * The keylen parameter may be omitted (zero) if the key is provided as a
+ * NULL-terminated base64-encoded string.
+ */
+
+LIBSSH2_API int
+libssh2_knownhost_addc(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, const char *salt,
+ const char *key, size_t keylen,
+ const char *comment, size_t commentlen,
+ int typemask, struct libssh2_knownhost **store)
+{
+ return knownhost_add(hosts, host, salt, NULL, 0, key, keylen,
+ comment, commentlen, typemask, store);
+}
+
+/*
+ * knownhost_check
+ *
+ * Check a host and its associated key against the collection of known hosts.
+ *
+ * The typemask is the type/format of the given host name and key
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - NOT SUPPORTED AS INPUT
+ * custom - prehashed base64 encoded. Note that this cannot use any salts.
+ *
+ * Returns:
+ *
+ * LIBSSH2_KNOWNHOST_CHECK_FAILURE
+ * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
+ * LIBSSH2_KNOWNHOST_CHECK_MATCH
+ * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
+ */
+static int
+knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *hostp, int port,
+ const char *key, size_t keylen,
+ int typemask,
+ struct libssh2_knownhost **ext)
+{
+ struct known_host *node;
+ struct known_host *badkey = NULL;
+ int type = typemask & LIBSSH2_KNOWNHOST_TYPE_MASK;
+ char *keyalloc = NULL;
+ int rc = LIBSSH2_KNOWNHOST_CHECK_NOTFOUND;
+ char hostbuff[270]; /* most host names can't be longer than like 256 */
+ const char *host;
+ int numcheck; /* number of host combos to check */
+ int match = 0;
+
+ if(type == LIBSSH2_KNOWNHOST_TYPE_SHA1)
+ /* we can't work with a sha1 as given input */
+ return LIBSSH2_KNOWNHOST_CHECK_MISMATCH;
+
+ /* if a port number is given, check for a '[host]:port' first before the
+ plain 'host' */
+ if(port >= 0) {
+ int len = snprintf(hostbuff, sizeof(hostbuff), "[%s]:%d", hostp, port);
+ if(len < 0 || len >= (int)sizeof(hostbuff)) {
+ _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Known-host write buffer too small");
+ return LIBSSH2_KNOWNHOST_CHECK_FAILURE;
+ }
+ host = hostbuff;
+ numcheck = 2; /* check both combos, start with this */
+ }
+ else {
+ host = hostp;
+ numcheck = 1; /* only check this host version */
+ }
+
+ if(!(typemask & LIBSSH2_KNOWNHOST_KEYENC_BASE64)) {
+ /* we got a raw key input, convert it to base64 for the checks below */
+ size_t nlen = _libssh2_base64_encode(hosts->session, key, keylen,
+ &keyalloc);
+ if(!nlen) {
+ _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for base64-encoded "
+ "key");
+ return LIBSSH2_KNOWNHOST_CHECK_FAILURE;
+ }
+
+ /* make the key point to this */
+ key = keyalloc;
+ }
+
+ do {
+ node = _libssh2_list_first(&hosts->head);
+ while(node) {
+ switch(node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) {
+ case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
+ if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN)
+ match = !strcmp(host, node->name);
+ break;
+ case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
+ if(type == LIBSSH2_KNOWNHOST_TYPE_CUSTOM)
+ match = !strcmp(host, node->name);
+ break;
+ case LIBSSH2_KNOWNHOST_TYPE_SHA1:
+ if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) {
+ /* when we have the sha1 version stored, we can use a
+ plain input to produce a hash to compare with the
+ stored hash.
+ */
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ libssh2_hmac_ctx ctx;
+ libssh2_hmac_ctx_init(ctx);
+
+ if(SHA_DIGEST_LENGTH != node->name_len) {
+ /* the name hash length must be the sha1 size or
+ we can't match it */
+ break;
+ }
+ libssh2_hmac_sha1_init(&ctx, (unsigned char *)node->salt,
+ node->salt_len);
+ libssh2_hmac_update(ctx, (unsigned char *)host,
+ strlen(host));
+ libssh2_hmac_final(ctx, hash);
+ libssh2_hmac_cleanup(&ctx);
+
+ if(!memcmp(hash, node->name, SHA_DIGEST_LENGTH))
+ /* this is a node we're interested in */
+ match = 1;
+ }
+ break;
+ default: /* unsupported type */
+ break;
+ }
+ if(match) {
+ int host_key_type = typemask & LIBSSH2_KNOWNHOST_KEY_MASK;
+ int known_key_type =
+ node->typemask & LIBSSH2_KNOWNHOST_KEY_MASK;
+ /* match on key type as follows:
+ - never match on an unknown key type
+ - if key_type is set to zero, ignore it an match always
+ - otherwise match when both key types are equal
+ */
+ if(host_key_type != LIBSSH2_KNOWNHOST_KEY_UNKNOWN &&
+ (host_key_type == 0 ||
+ host_key_type == known_key_type)) {
+ /* host name and key type match, now compare the keys */
+ if(!strcmp(key, node->key)) {
+ /* they match! */
+ if(ext)
+ *ext = knownhost_to_external(node);
+ badkey = NULL;
+ rc = LIBSSH2_KNOWNHOST_CHECK_MATCH;
+ break;
+ }
+ else {
+ /* remember the first node that had a host match but a
+ failed key match since we continue our search from
+ here */
+ if(!badkey)
+ badkey = node;
+ }
+ }
+ match = 0; /* don't count this as a match anymore */
+ }
+ node = _libssh2_list_next(&node->node);
+ }
+ host = hostp;
+ } while(!match && --numcheck);
+
+ if(badkey) {
+ /* key mismatch */
+ if(ext)
+ *ext = knownhost_to_external(badkey);
+ rc = LIBSSH2_KNOWNHOST_CHECK_MISMATCH;
+ }
+
+ if(keyalloc)
+ LIBSSH2_FREE(hosts->session, keyalloc);
+
+ return rc;
+}
+
+/*
+ * libssh2_knownhost_check
+ *
+ * Check a host and its associated key against the collection of known hosts.
+ *
+ * The typemask is the type/format of the given host name and key
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - NOT SUPPORTED AS INPUT
+ * custom - prehashed base64 encoded. Note that this cannot use any salts.
+ *
+ * Returns:
+ *
+ * LIBSSH2_KNOWNHOST_CHECK_FAILURE
+ * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
+ * LIBSSH2_KNOWNHOST_CHECK_MATCH
+ * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
+ */
+LIBSSH2_API int
+libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *hostp, const char *key, size_t keylen,
+ int typemask,
+ struct libssh2_knownhost **ext)
+{
+ return knownhost_check(hosts, hostp, -1, key, keylen,
+ typemask, ext);
+}
+
+/*
+ * libssh2_knownhost_checkp
+ *
+ * Check a host+port and its associated key against the collection of known
+ * hosts.
+ *
+ * Note that if 'port' is specified as greater than zero, the check function
+ * will be able to check for a dedicated key for this particular host+port
+ * combo, and if 'port' is negative it only checks for the generic host key.
+ *
+ * The typemask is the type/format of the given host name and key
+ *
+ * plain - ascii "hostname.domain.tld"
+ * sha1 - NOT SUPPORTED AS INPUT
+ * custom - prehashed base64 encoded. Note that this cannot use any salts.
+ *
+ * Returns:
+ *
+ * LIBSSH2_KNOWNHOST_CHECK_FAILURE
+ * LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
+ * LIBSSH2_KNOWNHOST_CHECK_MATCH
+ * LIBSSH2_KNOWNHOST_CHECK_MISMATCH
+ */
+LIBSSH2_API int
+libssh2_knownhost_checkp(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *hostp, int port,
+ const char *key, size_t keylen,
+ int typemask,
+ struct libssh2_knownhost **ext)
+{
+ return knownhost_check(hosts, hostp, port, key, keylen,
+ typemask, ext);
+}
+
+
+/*
+ * libssh2_knownhost_del
+ *
+ * Remove a host from the collection of known hosts.
+ *
+ */
+LIBSSH2_API int
+libssh2_knownhost_del(LIBSSH2_KNOWNHOSTS *hosts,
+ struct libssh2_knownhost *entry)
+{
+ struct known_host *node;
+
+ /* check that this was retrieved the right way or get out */
+ if(!entry || (entry->magic != KNOWNHOST_MAGIC))
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL,
+ "Invalid host information");
+
+ /* get the internal node pointer */
+ node = entry->node;
+
+ /* unlink from the list of all hosts */
+ _libssh2_list_remove(&node->node);
+
+ /* clear the struct now since the memory in which it is allocated is
+ about to be freed! */
+ memset(entry, 0, sizeof(struct libssh2_knownhost));
+
+ /* free all resources */
+ free_host(hosts->session, node);
+
+ return 0;
+}
+
+/*
+ * libssh2_knownhost_free
+ *
+ * Free an entire collection of known hosts.
+ *
+ */
+LIBSSH2_API void
+libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts)
+{
+ struct known_host *node;
+ struct known_host *next;
+
+ for(node = _libssh2_list_first(&hosts->head); node; node = next) {
+ next = _libssh2_list_next(&node->node);
+ free_host(hosts->session, node);
+ }
+ LIBSSH2_FREE(hosts->session, hosts);
+}
+
+
+/* old style plain text: [name]([,][name])*
+
+ for the sake of simplicity, we add them as separate hosts with the same
+ key
+*/
+static int oldstyle_hostline(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, size_t hostlen,
+ const char *key_type_name, size_t key_type_len,
+ const char *key, size_t keylen, int key_type,
+ const char *comment, size_t commentlen)
+{
+ int rc = 0;
+ size_t namelen = 0;
+ const char *name = host + hostlen;
+
+ if(hostlen < 1)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line "
+ "(no host names)");
+
+ while(name > host) {
+ --name;
+ ++namelen;
+
+ /* when we get the the start or see a comma coming up, add the host
+ name to the collection */
+ if((name == host) || (*(name-1) == ',')) {
+
+ char hostbuf[256];
+
+ /* make sure we don't overflow the buffer */
+ if(namelen >= sizeof(hostbuf)-1)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line "
+ "(unexpected length)");
+
+ /* copy host name to the temp buffer and zero terminate */
+ memcpy(hostbuf, name, namelen);
+ hostbuf[namelen] = 0;
+
+ rc = knownhost_add(hosts, hostbuf, NULL,
+ key_type_name, key_type_len,
+ key, keylen,
+ comment, commentlen,
+ key_type | LIBSSH2_KNOWNHOST_TYPE_PLAIN |
+ LIBSSH2_KNOWNHOST_KEYENC_BASE64, NULL);
+ if(rc)
+ return rc;
+
+ if(name > host) {
+ namelen = 0;
+ --name; /* skip comma */
+ }
+ }
+ }
+
+ return rc;
+}
+
+/* |1|[salt]|[hash] */
+static int hashed_hostline(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, size_t hostlen,
+ const char *key_type_name, size_t key_type_len,
+ const char *key, size_t keylen, int key_type,
+ const char *comment, size_t commentlen)
+{
+ const char *p;
+ char saltbuf[32];
+ char hostbuf[256];
+
+ const char *salt = &host[3]; /* skip the magic marker */
+ hostlen -= 3; /* deduct the marker */
+
+ /* this is where the salt starts, find the end of it */
+ for(p = salt; *p && (*p != '|'); p++)
+ ;
+
+ if(*p == '|') {
+ const char *hash = NULL;
+ size_t saltlen = p - salt;
+ if(saltlen >= (sizeof(saltbuf)-1)) /* weird length */
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line "
+ "(unexpectedly long salt)");
+
+ memcpy(saltbuf, salt, saltlen);
+ saltbuf[saltlen] = 0; /* zero terminate */
+ salt = saltbuf; /* point to the stack based buffer */
+
+ hash = p + 1; /* the host hash is after the separator */
+
+ /* now make the host point to the hash */
+ host = hash;
+ hostlen -= saltlen + 1; /* deduct the salt and separator */
+
+ /* check that the lengths seem sensible */
+ if(hostlen >= sizeof(hostbuf)-1)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line "
+ "(unexpected length)");
+
+ memcpy(hostbuf, host, hostlen);
+ hostbuf[hostlen] = 0;
+
+ return knownhost_add(hosts, hostbuf, salt,
+ key_type_name, key_type_len,
+ key, keylen,
+ comment, commentlen,
+ key_type | LIBSSH2_KNOWNHOST_TYPE_SHA1 |
+ LIBSSH2_KNOWNHOST_KEYENC_BASE64, NULL);
+ }
+ else
+ return 0; /* XXX: This should be an error, shouldn't it? */
+}
+
+/*
+ * hostline()
+ *
+ * Parse a single known_host line pre-split into host and key.
+ *
+ * The key part may include an optional comment which will be parsed here
+ * for ssh-rsa and ssh-dsa keys. Comments in other key types aren't handled.
+ *
+ * The function assumes new-lines have already been removed from the arguments.
+ */
+static int hostline(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *host, size_t hostlen,
+ const char *key, size_t keylen)
+{
+ const char *comment = NULL;
+ const char *key_type_name = NULL;
+ size_t commentlen = 0;
+ size_t key_type_len = 0;
+ int key_type;
+
+ /* make some checks that the lengths seem sensible */
+ if(keylen < 20)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line "
+ "(key too short)");
+
+ switch(key[0]) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ key_type = LIBSSH2_KNOWNHOST_KEY_RSA1;
+
+ /* Note that the old-style keys (RSA1) aren't truly base64, but we
+ * claim it is for now since we can get away with strcmp()ing the
+ * entire anything anyway! We need to check and fix these to make them
+ * work properly.
+ */
+ break;
+
+ default:
+ key_type_name = key;
+ while(keylen && *key &&
+ (*key != ' ') && (*key != '\t')) {
+ key++;
+ keylen--;
+ }
+ key_type_len = key - key_type_name;
+
+ if(!strncmp(key_type_name, "ssh-dss", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
+ else if(!strncmp(key_type_name, "ssh-rsa", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
+ else if(!strncmp(key_type_name, "ecdsa-sha2-nistp256", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
+ else if(!strncmp(key_type_name, "ecdsa-sha2-nistp384", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
+ else if(!strncmp(key_type_name, "ecdsa-sha2-nistp521", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
+ else if(!strncmp(key_type_name, "ssh-ed25519", key_type_len))
+ key_type = LIBSSH2_KNOWNHOST_KEY_ED25519;
+ else
+ key_type = LIBSSH2_KNOWNHOST_KEY_UNKNOWN;
+
+ /* skip whitespaces */
+ while((*key ==' ') || (*key == '\t')) {
+ key++;
+ keylen--;
+ }
+
+ comment = key;
+ commentlen = keylen;
+
+ /* move over key */
+ while(commentlen && *comment &&
+ (*comment != ' ') && (*comment != '\t')) {
+ comment++;
+ commentlen--;
+ }
+
+ /* reduce key by comment length */
+ keylen -= commentlen;
+
+ /* Distinguish empty comment (a space) from no comment (no space) */
+ if(commentlen == 0)
+ comment = NULL;
+
+ /* skip whitespaces */
+ while(commentlen && *comment &&
+ ((*comment ==' ') || (*comment == '\t'))) {
+ comment++;
+ commentlen--;
+ }
+ break;
+ }
+
+ /* Figure out host format */
+ if((hostlen >2) && memcmp(host, "|1|", 3)) {
+ /* old style plain text: [name]([,][name])*
+
+ for the sake of simplicity, we add them as separate hosts with the
+ same key
+ */
+ return oldstyle_hostline(hosts, host, hostlen, key_type_name,
+ key_type_len, key, keylen, key_type,
+ comment, commentlen);
+ }
+ else {
+ /* |1|[salt]|[hash] */
+ return hashed_hostline(hosts, host, hostlen, key_type_name,
+ key_type_len, key, keylen, key_type,
+ comment, commentlen);
+ }
+}
+
+/*
+ * libssh2_knownhost_readline()
+ *
+ * Pass in a line of a file of 'type'.
+ *
+ * LIBSSH2_KNOWNHOST_FILE_OPENSSH is the only supported type.
+ *
+ * OpenSSH line format:
+ *
+ * <host> <key>
+ *
+ * Where the two parts can be created like:
+ *
+ * <host> can be either
+ * <name> or <hash>
+ *
+ * <name> consists of
+ * [name] optionally followed by [,name] one or more times
+ *
+ * <hash> consists of
+ * |1|<salt>|hash
+ *
+ * <key> can be one of:
+ * [RSA bits] [e] [n as a decimal number]
+ * 'ssh-dss' [base64-encoded-key]
+ * 'ssh-rsa' [base64-encoded-key]
+ *
+ */
+LIBSSH2_API int
+libssh2_knownhost_readline(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *line, size_t len, int type)
+{
+ const char *cp;
+ const char *hostp;
+ const char *keyp;
+ size_t hostlen;
+ size_t keylen;
+ int rc;
+
+ if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unsupported type of known-host information "
+ "store");
+
+ cp = line;
+
+ /* skip leading whitespaces */
+ while(len && ((*cp == ' ') || (*cp == '\t'))) {
+ cp++;
+ len--;
+ }
+
+ if(!len || !*cp || (*cp == '#') || (*cp == '\n'))
+ /* comment or empty line */
+ return LIBSSH2_ERROR_NONE;
+
+ /* the host part starts here */
+ hostp = cp;
+
+ /* move over the host to the separator */
+ while(len && *cp && (*cp != ' ') && (*cp != '\t')) {
+ cp++;
+ len--;
+ }
+
+ hostlen = cp - hostp;
+
+ /* the key starts after the whitespaces */
+ while(len && *cp && ((*cp == ' ') || (*cp == '\t'))) {
+ cp++;
+ len--;
+ }
+
+ if(!*cp || !len) /* illegal line */
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Failed to parse known_hosts line");
+
+ keyp = cp; /* the key starts here */
+ keylen = len;
+
+ /* check if the line (key) ends with a newline and if so kill it */
+ while(len && *cp && (*cp != '\n')) {
+ cp++;
+ len--;
+ }
+
+ /* zero terminate where the newline is */
+ if(*cp == '\n')
+ keylen--; /* don't include this in the count */
+
+ /* deal with this one host+key line */
+ rc = hostline(hosts, hostp, hostlen, keyp, keylen);
+ if(rc)
+ return rc; /* failed */
+
+ return LIBSSH2_ERROR_NONE; /* success */
+}
+
+/*
+ * libssh2_knownhost_readfile
+ *
+ * Read hosts+key pairs from a given file.
+ *
+ * Returns a negative value for error or number of successfully added hosts.
+ *
+ */
+
+LIBSSH2_API int
+libssh2_knownhost_readfile(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *filename, int type)
+{
+ FILE *file;
+ int num = 0;
+ char buf[4092];
+
+ if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unsupported type of known-host information "
+ "store");
+
+ file = fopen(filename, FOPEN_READTEXT);
+ if(file) {
+ while(fgets(buf, sizeof(buf), file)) {
+ if(libssh2_knownhost_readline(hosts, buf, strlen(buf), type)) {
+ num = _libssh2_error(hosts->session, LIBSSH2_ERROR_KNOWN_HOSTS,
+ "Failed to parse known hosts file");
+ break;
+ }
+ num++;
+ }
+ fclose(file);
+ }
+ else
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE,
+ "Failed to open file");
+
+ return num;
+}
+
+/*
+ * knownhost_writeline()
+ *
+ * Ask libssh2 to convert a known host to an output line for storage.
+ *
+ * Note that this function returns LIBSSH2_ERROR_BUFFER_TOO_SMALL if the given
+ * output buffer is too small to hold the desired output. The 'outlen' field
+ * will then contain the size libssh2 wanted to store, which then is the
+ * smallest sufficient buffer it would require.
+ *
+ */
+static int
+knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
+ struct known_host *node,
+ char *buf, size_t buflen,
+ size_t *outlen, int type)
+{
+ size_t required_size;
+
+ const char *key_type_name;
+ size_t key_type_len;
+
+ /* we only support this single file type for now, bail out on all other
+ attempts */
+ if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unsupported type of known-host information "
+ "store");
+
+ switch(node->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) {
+ case LIBSSH2_KNOWNHOST_KEY_RSA1:
+ key_type_name = NULL;
+ key_type_len = 0;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
+ key_type_name = "ssh-rsa";
+ key_type_len = 7;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
+ key_type_name = "ssh-dss";
+ key_type_len = 7;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_ECDSA_256:
+ key_type_name = "ecdsa-sha2-nistp256";
+ key_type_len = 19;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_ECDSA_384:
+ key_type_name = "ecdsa-sha2-nistp384";
+ key_type_len = 19;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
+ key_type_name = "ecdsa-sha2-nistp521";
+ key_type_len = 19;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_ED25519:
+ key_type_name = "ssh-ed25519";
+ key_type_len = 11;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_UNKNOWN:
+ key_type_name = node->key_type_name;
+ if(key_type_name) {
+ key_type_len = node->key_type_len;
+ break;
+ }
+ /* otherwise fallback to default and error */
+ /* FALL-THROUGH */
+ default:
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unsupported type of known-host entry");
+ }
+
+ /* When putting together the host line there are three aspects to consider:
+ - Hashed (SHA1) or unhashed hostname
+ - key name or no key name (RSA1)
+ - comment or no comment
+
+ This means there are 2^3 different formats:
+ ("|1|%s|%s %s %s %s\n", salt, hashed_host, key_name, key, comment)
+ ("|1|%s|%s %s %s\n", salt, hashed_host, key_name, key)
+ ("|1|%s|%s %s %s\n", salt, hashed_host, key, comment)
+ ("|1|%s|%s %s\n", salt, hashed_host, key)
+ ("%s %s %s %s\n", host, key_name, key, comment)
+ ("%s %s %s\n", host, key_name, key)
+ ("%s %s %s\n", host, key, comment)
+ ("%s %s\n", host, key)
+
+ Even if the buffer is too small, we have to set outlen to the number of
+ characters the complete line would have taken. We also don't write
+ anything to the buffer unless we are sure we can write everything to the
+ buffer. */
+
+ required_size = strlen(node->key);
+
+ if(key_type_len)
+ required_size += key_type_len + 1; /* ' ' = 1 */
+ if(node->comment)
+ required_size += node->comment_len + 1; /* ' ' = 1 */
+
+ if((node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) ==
+ LIBSSH2_KNOWNHOST_TYPE_SHA1) {
+ char *namealloc;
+ size_t name_base64_len;
+ char *saltalloc;
+ size_t salt_base64_len;
+
+ name_base64_len = _libssh2_base64_encode(hosts->session, node->name,
+ node->name_len, &namealloc);
+ if(!name_base64_len)
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "base64-encoded host name");
+
+ salt_base64_len = _libssh2_base64_encode(hosts->session,
+ node->salt, node->salt_len,
+ &saltalloc);
+ if(!salt_base64_len) {
+ LIBSSH2_FREE(hosts->session, namealloc);
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "base64-encoded salt");
+ }
+
+ required_size += salt_base64_len + name_base64_len + 7;
+ /* |1| + | + ' ' + \n + \0 = 7 */
+
+ if(required_size <= buflen) {
+ if(node->comment && key_type_len)
+ snprintf(buf, buflen, "|1|%s|%s %s %s %s\n", saltalloc,
+ namealloc, key_type_name, node->key, node->comment);
+ else if(node->comment)
+ snprintf(buf, buflen, "|1|%s|%s %s %s\n", saltalloc, namealloc,
+ node->key, node->comment);
+ else if(key_type_len)
+ snprintf(buf, buflen, "|1|%s|%s %s %s\n", saltalloc, namealloc,
+ key_type_name, node->key);
+ else
+ snprintf(buf, buflen, "|1|%s|%s %s\n", saltalloc, namealloc,
+ node->key);
+ }
+
+ LIBSSH2_FREE(hosts->session, namealloc);
+ LIBSSH2_FREE(hosts->session, saltalloc);
+ }
+ else {
+ required_size += node->name_len + 3;
+ /* ' ' + '\n' + \0 = 3 */
+
+ if(required_size <= buflen) {
+ if(node->comment && key_type_len)
+ snprintf(buf, buflen, "%s %s %s %s\n", node->name,
+ key_type_name, node->key, node->comment);
+ else if(node->comment)
+ snprintf(buf, buflen, "%s %s %s\n", node->name, node->key,
+ node->comment);
+ else if(key_type_len)
+ snprintf(buf, buflen, "%s %s %s\n", node->name, key_type_name,
+ node->key);
+ else
+ snprintf(buf, buflen, "%s %s\n", node->name, node->key);
+ }
+ }
+
+ /* we report the full length of the data with the trailing zero excluded */
+ *outlen = required_size-1;
+
+ if(required_size <= buflen)
+ return LIBSSH2_ERROR_NONE;
+ else
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Known-host write buffer too small");
+}
+
+/*
+ * libssh2_knownhost_writeline()
+ *
+ * Ask libssh2 to convert a known host to an output line for storage.
+ *
+ * Note that this function returns LIBSSH2_ERROR_BUFFER_TOO_SMALL if the given
+ * output buffer is too small to hold the desired output.
+ */
+LIBSSH2_API int
+libssh2_knownhost_writeline(LIBSSH2_KNOWNHOSTS *hosts,
+ struct libssh2_knownhost *known,
+ char *buffer, size_t buflen,
+ size_t *outlen, /* the amount of written data */
+ int type)
+{
+ struct known_host *node;
+
+ if(known->magic != KNOWNHOST_MAGIC)
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_INVAL,
+ "Invalid host information");
+
+ node = known->node;
+
+ return knownhost_writeline(hosts, node, buffer, buflen, outlen, type);
+}
+
+/*
+ * libssh2_knownhost_writefile()
+ *
+ * Write hosts+key pairs to the given file.
+ */
+LIBSSH2_API int
+libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS *hosts,
+ const char *filename, int type)
+{
+ struct known_host *node;
+ FILE *file;
+ int rc = LIBSSH2_ERROR_NONE;
+ char buffer[4092];
+
+ /* we only support this single file type for now, bail out on all other
+ attempts */
+ if(type != LIBSSH2_KNOWNHOST_FILE_OPENSSH)
+ return _libssh2_error(hosts->session,
+ LIBSSH2_ERROR_METHOD_NOT_SUPPORTED,
+ "Unsupported type of known-host information "
+ "store");
+
+ file = fopen(filename, FOPEN_WRITETEXT);
+ if(!file)
+ return _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE,
+ "Failed to open file");
+
+ for(node = _libssh2_list_first(&hosts->head);
+ node;
+ node = _libssh2_list_next(&node->node)) {
+ size_t wrote = 0;
+ size_t nwrote;
+ rc = knownhost_writeline(hosts, node, buffer, sizeof(buffer), &wrote,
+ type);
+ if(rc)
+ break;
+
+ nwrote = fwrite(buffer, 1, wrote, file);
+ if(nwrote != wrote) {
+ /* failed to write the whole thing, bail out */
+ rc = _libssh2_error(hosts->session, LIBSSH2_ERROR_FILE,
+ "Write failed");
+ break;
+ }
+ }
+ fclose(file);
+
+ return rc;
+}
+
+
+/*
+ * libssh2_knownhost_get()
+ *
+ * Traverse the internal list of known hosts. Pass NULL to 'prev' to get
+ * the first one.
+ *
+ * Returns:
+ * 0 if a fine host was stored in 'store'
+ * 1 if end of hosts
+ * [negative] on errors
+ */
+LIBSSH2_API int
+libssh2_knownhost_get(LIBSSH2_KNOWNHOSTS *hosts,
+ struct libssh2_knownhost **ext,
+ struct libssh2_knownhost *oprev)
+{
+ struct known_host *node;
+ if(oprev && oprev->node) {
+ /* we have a starting point */
+ struct known_host *prev = oprev->node;
+
+ /* get the next node in the list */
+ node = _libssh2_list_next(&prev->node);
+
+ }
+ else
+ node = _libssh2_list_first(&hosts->head);
+
+ if(!node)
+ /* no (more) node */
+ return 1;
+
+ *ext = knownhost_to_external(node);
+
+ return 0;
+}
diff --git a/contrib/libs/libssh2/src/libssh2_config-linux.h b/contrib/libs/libssh2/src/libssh2_config-linux.h
new file mode 100644
index 00000000000..0a4fdde9dbf
--- /dev/null
+++ b/contrib/libs/libssh2/src/libssh2_config-linux.h
@@ -0,0 +1,259 @@
+/* src/libssh2_config.h. Generated from libssh2_config.h.in by configure. */
+/* src/libssh2_config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Define to 1 if using 'alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* Define to 1 if you have 'alloca', as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define to 1 if <alloca.h> works. */
+#define HAVE_ALLOCA_H 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Define to 1 if you have the declaration of 'SecureZeroMemory', and to 0 if
+ you don't. */
+/* #undef HAVE_DECL_SECUREZEROMEMORY */
+
+/* disabled non-blocking sockets */
+/* #undef HAVE_DISABLED_NONBLOCKING */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the 'EVP_aes_128_ctr' function. */
+#define HAVE_EVP_AES_128_CTR 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* use FIONBIO for non-blocking sockets */
+/* #undef HAVE_FIONBIO */
+
+/* Define to 1 if you have the 'gettimeofday' function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* use ioctlsocket() for non-blocking sockets */
+/* #undef HAVE_IOCTLSOCKET */
+
+/* use Ioctlsocket() for non-blocking sockets */
+/* #undef HAVE_IOCTLSOCKET_CASE */
+
+/* Define if you have the bcrypt library. */
+/* #undef HAVE_LIBBCRYPT */
+
+/* Define if you have the crypt32 library. */
+/* #undef HAVE_LIBCRYPT32 */
+
+/* Define if you have the gcrypt library. */
+/* #undef HAVE_LIBGCRYPT */
+
+/* Define if you have the mbedcrypto library. */
+/* #undef HAVE_LIBMBEDCRYPTO */
+
+/* Define if you have the ssl library. */
+#define HAVE_LIBSSL 1
+
+/* Define if you have the z library. */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if the compiler supports the 'long long' data type. */
+#define HAVE_LONGLONG 1
+
+/* Define to 1 if you have the 'memset_s' function. */
+/* #undef HAVE_MEMSET_S */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <ntdef.h> header file. */
+/* #undef HAVE_NTDEF_H */
+
+/* Define to 1 if you have the <ntstatus.h> header file. */
+/* #undef HAVE_NTSTATUS_H */
+
+/* use O_NONBLOCK for non-blocking sockets */
+#define HAVE_O_NONBLOCK 1
+
+/* Define to 1 if you have the 'poll' function. */
+#define HAVE_POLL 1
+
+/* Define to 1 if you have the select function. */
+#define HAVE_SELECT 1
+
+/* use SO_NONBLOCK for non-blocking sockets */
+/* #undef HAVE_SO_NONBLOCK */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the 'strtoll' function. */
+#define HAVE_STRTOLL 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#define HAVE_SYS_UN_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+/* #undef HAVE_WINSOCK2_H */
+
+/* Define to 1 if you have the <ws2tcpip.h> header file. */
+/* #undef HAVE_WS2TCPIP_H */
+
+/* to make a symbol visible */
+/* #undef LIBSSH2_API */
+
+/* Enable clearing of memory before being freed */
+/* #undef LIBSSH2_CLEAR_MEMORY */
+
+/* Enable "none" cipher -- NOT RECOMMENDED */
+/* #undef LIBSSH2_CRYPT_NONE */
+
+/* Enable newer diffie-hellman-group-exchange-sha1 syntax */
+#define LIBSSH2_DH_GEX_NEW 1
+
+/* Compile in zlib support */
+#define LIBSSH2_HAVE_ZLIB 1
+
+/* Use libgcrypt */
+/* #undef LIBSSH2_LIBGCRYPT */
+
+/* Enable "none" MAC -- NOT RECOMMENDED */
+/* #undef LIBSSH2_MAC_NONE */
+
+/* Use mbedtls */
+/* #undef LIBSSH2_MBEDTLS */
+
+/* Use openssl */
+#define LIBSSH2_OPENSSL 1
+
+/* Use wincng */
+/* #undef LIBSSH2_WINCNG */
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */
+/* #undef NEED_REENTRANT */
+
+/* Name of package */
+#define PACKAGE "libssh2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "[email protected]"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libssh2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libssh2 -"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libssh2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "-"
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at runtime.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* Define to 1 if all of the C89 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "-"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 on platforms where this makes off_t a 64-bit type. */
+/* #undef _LARGE_FILES */
+
+/* Number of bits in time_t, on hosts where this is settable. */
+/* #undef _TIME_BITS */
+
+/* Define to 1 on platforms where this makes time_t a 64-bit type. */
+/* #undef __MINGW_USE_VC2005_COMPAT */
+
+/* Define to empty if 'const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to '__inline__' or '__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define as 'unsigned int' if <stddef.h> doesn't define. */
+/* #undef size_t */
diff --git a/contrib/libs/libssh2/src/libssh2_config-osx.h b/contrib/libs/libssh2/src/libssh2_config-osx.h
new file mode 100644
index 00000000000..0afd72c7163
--- /dev/null
+++ b/contrib/libs/libssh2/src/libssh2_config-osx.h
@@ -0,0 +1,262 @@
+/* src/libssh2_config.h. Generated from libssh2_config.h.in by configure. */
+/* src/libssh2_config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define to 1 if using `alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+ */
+#define HAVE_ALLOCA_H 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Define to 1 if you have the declaration of `SecureZeroMemory', and to 0 if
+ you don't. */
+/* #undef HAVE_DECL_SECUREZEROMEMORY */
+
+/* disabled non-blocking sockets */
+/* #undef HAVE_DISABLED_NONBLOCKING */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the `EVP_aes_128_ctr' function. */
+#define HAVE_EVP_AES_128_CTR 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* use FIONBIO for non-blocking sockets */
+/* #undef HAVE_FIONBIO */
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* use ioctlsocket() for non-blocking sockets */
+/* #undef HAVE_IOCTLSOCKET */
+
+/* use Ioctlsocket() for non-blocking sockets */
+/* #undef HAVE_IOCTLSOCKET_CASE */
+
+/* Define if you have the bcrypt library. */
+/* #undef HAVE_LIBBCRYPT */
+
+/* Define if you have the crypt32 library. */
+/* #undef HAVE_LIBCRYPT32 */
+
+/* Define if you have the gcrypt library. */
+/* #undef HAVE_LIBGCRYPT */
+
+/* Define if you have the mbedtls library. */
+/* #undef HAVE_LIBMBEDTLS */
+
+/* Define if you have the ssl library. */
+#define HAVE_LIBSSL 1
+
+/* Define if you have the z library. */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if the compiler supports the 'long long' data type. */
+#define HAVE_LONGLONG 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <ntdef.h> header file. */
+/* #undef HAVE_NTDEF_H */
+
+/* Define to 1 if you have the <ntstatus.h> header file. */
+/* #undef HAVE_NTSTATUS_H */
+
+/* use O_NONBLOCK for non-blocking sockets */
+#define HAVE_O_NONBLOCK 1
+
+/* Define to 1 if you have the `poll' function. */
+#define HAVE_POLL 1
+
+/* Define to 1 if you have the select function. */
+#define HAVE_SELECT 1
+
+/* use SO_NONBLOCK for non-blocking sockets */
+/* #undef HAVE_SO_NONBLOCK */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strtoll' function. */
+#define HAVE_STRTOLL 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#define HAVE_SYS_UN_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+/* #undef HAVE_WINSOCK2_H */
+
+/* Define to 1 if you have the <ws2tcpip.h> header file. */
+/* #undef HAVE_WS2TCPIP_H */
+
+/* to make a symbol visible */
+/* #undef LIBSSH2_API */
+
+/* Enable clearing of memory before being freed */
+/* #undef LIBSSH2_CLEAR_MEMORY */
+
+/* Enable "none" cipher -- NOT RECOMMENDED */
+/* #undef LIBSSH2_CRYPT_NONE */
+
+/* Enable newer diffie-hellman-group-exchange-sha1 syntax */
+#define LIBSSH2_DH_GEX_NEW 1
+
+/* Compile in zlib support */
+#define LIBSSH2_HAVE_ZLIB 1
+
+/* Use libgcrypt */
+/* #undef LIBSSH2_LIBGCRYPT */
+
+/* Enable "none" MAC -- NOT RECOMMENDED */
+/* #undef LIBSSH2_MAC_NONE */
+
+/* Use mbedtls */
+/* #undef LIBSSH2_MBEDTLS */
+
+/* Use OpenSSL */
+#define LIBSSH2_OPENSSL 1
+
+/* Use Windows CNG */
+/* #undef LIBSSH2_WINCNG */
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */
+/* #undef NEED_REENTRANT */
+
+/* Name of package */
+#define PACKAGE "libssh2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "[email protected]"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libssh2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libssh2 -"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libssh2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "-"
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at runtime.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "-"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
diff --git a/contrib/libs/libssh2/src/libssh2_config-win.h b/contrib/libs/libssh2/src/libssh2_config-win.h
new file mode 100644
index 00000000000..60cabc0b951
--- /dev/null
+++ b/contrib/libs/libssh2/src/libssh2_config-win.h
@@ -0,0 +1,262 @@
+/* src/libssh2_config.h. Generated from libssh2_config.h.in by configure. */
+/* src/libssh2_config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define to 1 if using `alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+ */
+/* #undef HAVE_ALLOCA_H */
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+/* #undef HAVE_ARPA_INET_H */
+
+/* Define to 1 if you have the declaration of `SecureZeroMemory', and to 0 if
+ you don't. */
+/* #undef HAVE_DECL_SECUREZEROMEMORY */
+
+/* disabled non-blocking sockets */
+/* #undef HAVE_DISABLED_NONBLOCKING */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+/* #undef HAVE_DLFCN_H */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the `EVP_aes_128_ctr' function. */
+#define HAVE_EVP_AES_128_CTR 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* use FIONBIO for non-blocking sockets */
+/* #undef HAVE_FIONBIO */
+
+/* Define to 1 if you have the `gettimeofday' function. */
+/* #undef HAVE_GETTIMEOFDAY */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* use ioctlsocket() for non-blocking sockets */
+#define HAVE_IOCTLSOCKET 1
+
+/* use Ioctlsocket() for non-blocking sockets */
+/* #undef HAVE_IOCTLSOCKET_CASE */
+
+/* Define if you have the bcrypt library. */
+/* #undef HAVE_LIBBCRYPT */
+
+/* Define if you have the crypt32 library. */
+/* #undef HAVE_LIBCRYPT32 */
+
+/* Define if you have the gcrypt library. */
+/* #undef HAVE_LIBGCRYPT */
+
+/* Define if you have the mbedtls library. */
+/* #undef HAVE_LIBMBEDTLS */
+
+/* Define if you have the ssl library. */
+#define HAVE_LIBSSL 1
+
+/* Define if you have the z library. */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if the compiler supports the 'long long' data type. */
+#define HAVE_LONGLONG 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+/* #undef HAVE_NETINET_IN_H */
+
+/* Define to 1 if you have the <ntdef.h> header file. */
+/* #undef HAVE_NTDEF_H */
+
+/* Define to 1 if you have the <ntstatus.h> header file. */
+/* #undef HAVE_NTSTATUS_H */
+
+/* use O_NONBLOCK for non-blocking sockets */
+/* #undef HAVE_O_NONBLOCK */
+
+/* Define to 1 if you have the `poll' function. */
+/* #undef HAVE_POLL */
+
+/* Define to 1 if you have the select function. */
+#define HAVE_SELECT 1
+
+/* use SO_NONBLOCK for non-blocking sockets */
+/* #undef HAVE_SO_NONBLOCK */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strtoll' function. */
+#define HAVE_STRTOLL 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+/* #undef HAVE_SYS_IOCTL_H */
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+/* #undef HAVE_SYS_SELECT_H */
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+/* #undef HAVE_SYS_SOCKET_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+/* #undef HAVE_SYS_STAT_H */
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+/* #undef HAVE_SYS_TIME_H */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+/* #undef HAVE_SYS_TYPES_H */
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+/* #undef HAVE_SYS_UIO_H */
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+/* #undef HAVE_SYS_UN_H */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+/* #undef HAVE_UNISTD_H */
+
+/* Define to 1 if you have the <windows.h> header file. */
+#define HAVE_WINDOWS_H 1
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+#define HAVE_WINSOCK2_H 1
+
+/* Define to 1 if you have the <ws2tcpip.h> header file. */
+#define HAVE_WS2TCPIP_H 1
+
+/* to make a symbol visible */
+/* #undef LIBSSH2_API */
+
+/* Enable clearing of memory before being freed */
+/* #undef LIBSSH2_CLEAR_MEMORY */
+
+/* Enable "none" cipher -- NOT RECOMMENDED */
+/* #undef LIBSSH2_CRYPT_NONE */
+
+/* Enable newer diffie-hellman-group-exchange-sha1 syntax */
+#define LIBSSH2_DH_GEX_NEW 1
+
+/* Compile in zlib support */
+#define LIBSSH2_HAVE_ZLIB 1
+
+/* Use libgcrypt */
+/* #undef LIBSSH2_LIBGCRYPT */
+
+/* Enable "none" MAC -- NOT RECOMMENDED */
+/* #undef LIBSSH2_MAC_NONE */
+
+/* Use mbedtls */
+/* #undef LIBSSH2_MBEDTLS */
+
+/* Use OpenSSL */
+#define LIBSSH2_OPENSSL 1
+
+/* Use Windows CNG */
+/* #undef LIBSSH2_WINCNG */
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */
+#define NEED_REENTRANT 1
+
+/* Name of package */
+#define PACKAGE "libssh2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "[email protected]"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libssh2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libssh2 -"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libssh2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "-"
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at runtime.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "-"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#define _FILE_OFFSET_BITS 64
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
diff --git a/contrib/libs/libssh2/src/libssh2_config.h b/contrib/libs/libssh2/src/libssh2_config.h
new file mode 100644
index 00000000000..7422122cc2c
--- /dev/null
+++ b/contrib/libs/libssh2/src/libssh2_config.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#if defined(__APPLE__)
+# include "libssh2_config-osx.h"
+#elif defined(_MSC_VER)
+# include "libssh2_config-win.h"
+#else
+# include "libssh2_config-linux.h"
+#endif
diff --git a/contrib/libs/libssh2/src/libssh2_priv.h b/contrib/libs/libssh2/src/libssh2_priv.h
new file mode 100644
index 00000000000..da488b744c5
--- /dev/null
+++ b/contrib/libs/libssh2/src/libssh2_priv.h
@@ -0,0 +1,1154 @@
+#ifndef __LIBSSH2_PRIV_H
+#define __LIBSSH2_PRIV_H
+/* Copyright (c) 2004-2008, 2010, Sara Golemon <[email protected]>
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.
+ */
+
+#define LIBSSH2_LIBRARY
+#include "libssh2_config.h"
+
+#ifdef HAVE_WINDOWS_H
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#include <stdio.h>
+#include <time.h>
+
+/* The following CPP block should really only be in session.c and packet.c.
+ However, AIX have #define's for 'events' and 'revents' and we are using
+ those names in libssh2.h, so we need to include the AIX headers first, to
+ make sure all code is compiled with consistent names of these fields.
+ While arguable the best would to change libssh2.h to use other names, that
+ would break backwards compatibility.
+*/
+#ifdef HAVE_POLL
+# include <poll.h>
+#else
+# if defined(HAVE_SELECT) && !defined(WIN32)
+# ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# else
+# include <sys/time.h>
+# include <sys/types.h>
+# endif
+# endif
+#endif
+
+/* Needed for struct iovec on some platforms */
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include "libssh2.h"
+#include "libssh2_publickey.h"
+#include "libssh2_sftp.h"
+#include "misc.h" /* for the linked list stuff */
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifdef _MSC_VER
+/* "inline" keyword is valid only with C++ engine! */
+#define inline __inline
+#endif
+
+/* 3DS doesn't seem to have iovec */
+#if defined(WIN32) || defined(_3DS)
+
+struct iovec {
+ size_t iov_len;
+ void *iov_base;
+};
+
+#endif
+
+/* Provide iovec / writev on WIN32 platform. */
+#ifdef WIN32
+
+static inline int writev(int sock, struct iovec *iov, int nvecs)
+{
+ DWORD ret;
+ if(WSASend(sock, (LPWSABUF)iov, nvecs, &ret, 0, NULL, NULL) == 0) {
+ return ret;
+ }
+ return -1;
+}
+
+#endif /* WIN32 */
+
+#ifdef __OS400__
+/* Force parameter type. */
+#define send(s, b, l, f) send((s), (unsigned char *) (b), (l), (f))
+#endif
+
+#include "crypto.h"
+
+#ifdef HAVE_WINSOCK2_H
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#endif
+
+#ifndef SIZE_MAX
+#if _WIN64
+#define SIZE_MAX 0xFFFFFFFFFFFFFFFF
+#else
+#define SIZE_MAX 0xFFFFFFFF
+#endif
+#endif
+
+#ifndef UINT_MAX
+#define UINT_MAX 0xFFFFFFFF
+#endif
+
+/* RFC4253 section 6.1 Maximum Packet Length says:
+ *
+ * "All implementations MUST be able to process packets with
+ * uncompressed payload length of 32768 bytes or less and
+ * total packet size of 35000 bytes or less (including length,
+ * padding length, payload, padding, and MAC.)."
+ */
+#define MAX_SSH_PACKET_LEN 35000
+#define MAX_SHA_DIGEST_LEN SHA512_DIGEST_LENGTH
+
+#define LIBSSH2_ALLOC(session, count) \
+ session->alloc((count), &(session)->abstract)
+#define LIBSSH2_CALLOC(session, count) _libssh2_calloc(session, count)
+#define LIBSSH2_REALLOC(session, ptr, count) \
+ ((ptr) ? session->realloc((ptr), (count), &(session)->abstract) : \
+ session->alloc((count), &(session)->abstract))
+#define LIBSSH2_FREE(session, ptr) \
+ session->free((ptr), &(session)->abstract)
+#define LIBSSH2_IGNORE(session, data, datalen) \
+ session->ssh_msg_ignore((session), (data), (datalen), &(session)->abstract)
+#define LIBSSH2_DEBUG(session, always_display, message, message_len, \
+ language, language_len) \
+ session->ssh_msg_debug((session), (always_display), (message), \
+ (message_len), (language), (language_len), \
+ &(session)->abstract)
+#define LIBSSH2_DISCONNECT(session, reason, message, message_len, \
+ language, language_len) \
+ session->ssh_msg_disconnect((session), (reason), (message), \
+ (message_len), (language), (language_len), \
+ &(session)->abstract)
+
+#define LIBSSH2_MACERROR(session, data, datalen) \
+ session->macerror((session), (data), (datalen), &(session)->abstract)
+#define LIBSSH2_X11_OPEN(channel, shost, sport) \
+ channel->session->x11(((channel)->session), (channel), \
+ (shost), (sport), (&(channel)->session->abstract))
+
+#define LIBSSH2_CHANNEL_CLOSE(session, channel) \
+ channel->close_cb((session), &(session)->abstract, \
+ (channel), &(channel)->abstract)
+
+#define LIBSSH2_SEND_FD(session, fd, buffer, length, flags) \
+ (session->send)(fd, buffer, length, flags, &session->abstract)
+#define LIBSSH2_RECV_FD(session, fd, buffer, length, flags) \
+ (session->recv)(fd, buffer, length, flags, &session->abstract)
+
+#define LIBSSH2_SEND(session, buffer, length, flags) \
+ LIBSSH2_SEND_FD(session, session->socket_fd, buffer, length, flags)
+#define LIBSSH2_RECV(session, buffer, length, flags) \
+ LIBSSH2_RECV_FD(session, session->socket_fd, buffer, length, flags)
+
+typedef struct _LIBSSH2_KEX_METHOD LIBSSH2_KEX_METHOD;
+typedef struct _LIBSSH2_HOSTKEY_METHOD LIBSSH2_HOSTKEY_METHOD;
+typedef struct _LIBSSH2_CRYPT_METHOD LIBSSH2_CRYPT_METHOD;
+typedef struct _LIBSSH2_COMP_METHOD LIBSSH2_COMP_METHOD;
+
+typedef struct _LIBSSH2_PACKET LIBSSH2_PACKET;
+
+typedef enum
+{
+ libssh2_NB_state_idle = 0,
+ libssh2_NB_state_allocated,
+ libssh2_NB_state_created,
+ libssh2_NB_state_sent,
+ libssh2_NB_state_sent1,
+ libssh2_NB_state_sent2,
+ libssh2_NB_state_sent3,
+ libssh2_NB_state_sent4,
+ libssh2_NB_state_sent5,
+ libssh2_NB_state_sent6,
+ libssh2_NB_state_sent7,
+ libssh2_NB_state_jump1,
+ libssh2_NB_state_jump2,
+ libssh2_NB_state_jump3,
+ libssh2_NB_state_jump4,
+ libssh2_NB_state_jump5,
+ libssh2_NB_state_end
+} libssh2_nonblocking_states;
+
+typedef struct packet_require_state_t
+{
+ libssh2_nonblocking_states state;
+ time_t start;
+} packet_require_state_t;
+
+typedef struct packet_requirev_state_t
+{
+ time_t start;
+} packet_requirev_state_t;
+
+typedef struct kmdhgGPshakex_state_t
+{
+ libssh2_nonblocking_states state;
+ unsigned char *e_packet;
+ unsigned char *s_packet;
+ unsigned char *tmp;
+ unsigned char h_sig_comp[MAX_SHA_DIGEST_LEN];
+ unsigned char c;
+ size_t e_packet_len;
+ size_t s_packet_len;
+ size_t tmp_len;
+ _libssh2_bn_ctx *ctx;
+ _libssh2_dh_ctx x;
+ _libssh2_bn *e;
+ _libssh2_bn *f;
+ _libssh2_bn *k;
+ unsigned char *f_value;
+ unsigned char *k_value;
+ unsigned char *h_sig;
+ size_t f_value_len;
+ size_t k_value_len;
+ size_t h_sig_len;
+ void *exchange_hash;
+ packet_require_state_t req_state;
+ libssh2_nonblocking_states burn_state;
+} kmdhgGPshakex_state_t;
+
+typedef struct key_exchange_state_low_t
+{
+ libssh2_nonblocking_states state;
+ packet_require_state_t req_state;
+ kmdhgGPshakex_state_t exchange_state;
+ _libssh2_bn *p; /* SSH2 defined value (p_value) */
+ _libssh2_bn *g; /* SSH2 defined value (2) */
+ unsigned char request[256]; /* Must fit EC_MAX_POINT_LEN + data */
+ unsigned char *data;
+ size_t request_len;
+ size_t data_len;
+ _libssh2_ec_key *private_key; /* SSH2 ecdh private key */
+ unsigned char *public_key_oct; /* SSH2 ecdh public key octal value */
+ size_t public_key_oct_len; /* SSH2 ecdh public key octal value
+ length */
+ unsigned char *curve25519_public_key; /* curve25519 public key, 32
+ bytes */
+ unsigned char *curve25519_private_key; /* curve25519 private key, 32
+ bytes */
+} key_exchange_state_low_t;
+
+typedef struct key_exchange_state_t
+{
+ libssh2_nonblocking_states state;
+ packet_require_state_t req_state;
+ key_exchange_state_low_t key_state_low;
+ unsigned char *data;
+ size_t data_len;
+ unsigned char *oldlocal;
+ size_t oldlocal_len;
+} key_exchange_state_t;
+
+#define FwdNotReq "Forward not requested"
+
+typedef struct packet_queue_listener_state_t
+{
+ libssh2_nonblocking_states state;
+ unsigned char packet[17 + (sizeof(FwdNotReq) - 1)];
+ unsigned char *host;
+ unsigned char *shost;
+ uint32_t sender_channel;
+ uint32_t initial_window_size;
+ uint32_t packet_size;
+ uint32_t port;
+ uint32_t sport;
+ uint32_t host_len;
+ uint32_t shost_len;
+ LIBSSH2_CHANNEL *channel;
+} packet_queue_listener_state_t;
+
+#define X11FwdUnAvil "X11 Forward Unavailable"
+
+typedef struct packet_x11_open_state_t
+{
+ libssh2_nonblocking_states state;
+ unsigned char packet[17 + (sizeof(X11FwdUnAvil) - 1)];
+ unsigned char *shost;
+ uint32_t sender_channel;
+ uint32_t initial_window_size;
+ uint32_t packet_size;
+ uint32_t sport;
+ uint32_t shost_len;
+ LIBSSH2_CHANNEL *channel;
+} packet_x11_open_state_t;
+
+struct _LIBSSH2_PACKET
+{
+ struct list_node node; /* linked list header */
+
+ /* the raw unencrypted payload */
+ unsigned char *data;
+ size_t data_len;
+
+ /* Where to start reading data from,
+ * used for channel data that's been partially consumed */
+ size_t data_head;
+};
+
+typedef struct _libssh2_channel_data
+{
+ /* Identifier */
+ uint32_t id;
+
+ /* Limits and restrictions */
+ uint32_t window_size_initial, window_size, packet_size;
+
+ /* Set to 1 when CHANNEL_CLOSE / CHANNEL_EOF sent/received */
+ char close, eof, extended_data_ignore_mode;
+} libssh2_channel_data;
+
+struct _LIBSSH2_CHANNEL
+{
+ struct list_node node;
+
+ unsigned char *channel_type;
+ unsigned channel_type_len;
+
+ /* channel's program exit status */
+ int exit_status;
+
+ /* channel's program exit signal (without the SIG prefix) */
+ char *exit_signal;
+
+ libssh2_channel_data local, remote;
+ /* Amount of bytes to be refunded to receive window (but not yet sent) */
+ uint32_t adjust_queue;
+ /* Data immediately available for reading */
+ uint32_t read_avail;
+
+ LIBSSH2_SESSION *session;
+
+ void *abstract;
+ LIBSSH2_CHANNEL_CLOSE_FUNC((*close_cb));
+
+ /* State variables used in libssh2_channel_setenv_ex() */
+ libssh2_nonblocking_states setenv_state;
+ unsigned char *setenv_packet;
+ size_t setenv_packet_len;
+ unsigned char setenv_local_channel[4];
+ packet_requirev_state_t setenv_packet_requirev_state;
+
+ /* State variables used in libssh2_channel_request_pty_ex()
+ libssh2_channel_request_pty_size_ex() */
+ libssh2_nonblocking_states reqPTY_state;
+ unsigned char reqPTY_packet[41 + 256];
+ size_t reqPTY_packet_len;
+ unsigned char reqPTY_local_channel[4];
+ packet_requirev_state_t reqPTY_packet_requirev_state;
+
+ /* State variables used in libssh2_channel_x11_req_ex() */
+ libssh2_nonblocking_states reqX11_state;
+ unsigned char *reqX11_packet;
+ size_t reqX11_packet_len;
+ unsigned char reqX11_local_channel[4];
+ packet_requirev_state_t reqX11_packet_requirev_state;
+
+ /* State variables used in libssh2_channel_process_startup() */
+ libssh2_nonblocking_states process_state;
+ unsigned char *process_packet;
+ size_t process_packet_len;
+ unsigned char process_local_channel[4];
+ packet_requirev_state_t process_packet_requirev_state;
+
+ /* State variables used in libssh2_channel_flush_ex() */
+ libssh2_nonblocking_states flush_state;
+ size_t flush_refund_bytes;
+ size_t flush_flush_bytes;
+
+ /* State variables used in libssh2_channel_receive_window_adjust() */
+ libssh2_nonblocking_states adjust_state;
+ unsigned char adjust_adjust[9]; /* packet_type(1) + channel(4) +
+ adjustment(4) */
+
+ /* State variables used in libssh2_channel_read_ex() */
+ libssh2_nonblocking_states read_state;
+
+ uint32_t read_local_id;
+
+ /* State variables used in libssh2_channel_write_ex() */
+ libssh2_nonblocking_states write_state;
+ unsigned char write_packet[13];
+ size_t write_packet_len;
+ size_t write_bufwrite;
+
+ /* State variables used in libssh2_channel_close() */
+ libssh2_nonblocking_states close_state;
+ unsigned char close_packet[5];
+
+ /* State variables used in libssh2_channel_wait_closedeof() */
+ libssh2_nonblocking_states wait_eof_state;
+
+ /* State variables used in libssh2_channel_wait_closed() */
+ libssh2_nonblocking_states wait_closed_state;
+
+ /* State variables used in libssh2_channel_free() */
+ libssh2_nonblocking_states free_state;
+
+ /* State variables used in libssh2_channel_handle_extended_data2() */
+ libssh2_nonblocking_states extData2_state;
+
+ /* State variables used in libssh2_channel_request_auth_agent() */
+ libssh2_nonblocking_states req_auth_agent_try_state;
+ libssh2_nonblocking_states req_auth_agent_state;
+ unsigned char req_auth_agent_packet[36];
+ size_t req_auth_agent_packet_len;
+ unsigned char req_auth_agent_local_channel[4];
+ packet_requirev_state_t req_auth_agent_requirev_state;
+};
+
+struct _LIBSSH2_LISTENER
+{
+ struct list_node node; /* linked list header */
+
+ LIBSSH2_SESSION *session;
+
+ char *host;
+ int port;
+
+ /* a list of CHANNELs for this listener */
+ struct list_head queue;
+
+ int queue_size;
+ int queue_maxsize;
+
+ /* State variables used in libssh2_channel_forward_cancel() */
+ libssh2_nonblocking_states chanFwdCncl_state;
+ unsigned char *chanFwdCncl_data;
+ size_t chanFwdCncl_data_len;
+};
+
+typedef struct _libssh2_endpoint_data
+{
+ unsigned char *banner;
+
+ unsigned char *kexinit;
+ size_t kexinit_len;
+
+ const LIBSSH2_CRYPT_METHOD *crypt;
+ void *crypt_abstract;
+
+ const struct _LIBSSH2_MAC_METHOD *mac;
+ uint32_t seqno;
+ void *mac_abstract;
+
+ const LIBSSH2_COMP_METHOD *comp;
+ void *comp_abstract;
+
+ /* Method Preferences -- NULL yields "load order" */
+ char *crypt_prefs;
+ char *mac_prefs;
+ char *comp_prefs;
+ char *lang_prefs;
+} libssh2_endpoint_data;
+
+#define PACKETBUFSIZE (1024*16)
+
+struct transportpacket
+{
+ /* ------------- for incoming data --------------- */
+ unsigned char buf[PACKETBUFSIZE];
+ unsigned char init[5]; /* first 5 bytes of the incoming data stream,
+ still encrypted */
+ size_t writeidx; /* at what array index we do the next write into
+ the buffer */
+ size_t readidx; /* at what array index we do the next read from
+ the buffer */
+ uint32_t packet_length; /* the most recent packet_length as read from the
+ network data */
+ uint8_t padding_length; /* the most recent padding_length as read from the
+ network data */
+ size_t data_num; /* How much of the total package that has been read
+ so far. */
+ size_t total_num; /* How much a total package is supposed to be, in
+ number of bytes. A full package is
+ packet_length + padding_length + 4 +
+ mac_length. */
+ unsigned char *payload; /* this is a pointer to a LIBSSH2_ALLOC()
+ area to which we write decrypted data */
+ unsigned char *wptr; /* write pointer into the payload to where we
+ are currently writing decrypted data */
+
+ /* ------------- for outgoing data --------------- */
+ unsigned char outbuf[MAX_SSH_PACKET_LEN]; /* area for the outgoing data */
+
+ int ototal_num; /* size of outbuf in number of bytes */
+ const unsigned char *odata; /* original pointer to the data */
+ size_t olen; /* original size of the data we stored in
+ outbuf */
+ size_t osent; /* number of bytes already sent */
+};
+
+struct _LIBSSH2_PUBLICKEY
+{
+ LIBSSH2_CHANNEL *channel;
+ uint32_t version;
+
+ /* State variables used in libssh2_publickey_packet_receive() */
+ libssh2_nonblocking_states receive_state;
+ unsigned char *receive_packet;
+ size_t receive_packet_len;
+
+ /* State variables used in libssh2_publickey_add_ex() */
+ libssh2_nonblocking_states add_state;
+ unsigned char *add_packet;
+ unsigned char *add_s;
+
+ /* State variables used in libssh2_publickey_remove_ex() */
+ libssh2_nonblocking_states remove_state;
+ unsigned char *remove_packet;
+ unsigned char *remove_s;
+
+ /* State variables used in libssh2_publickey_list_fetch() */
+ libssh2_nonblocking_states listFetch_state;
+ unsigned char *listFetch_s;
+ unsigned char listFetch_buffer[12];
+ unsigned char *listFetch_data;
+ size_t listFetch_data_len;
+};
+
+#define LIBSSH2_SCP_RESPONSE_BUFLEN 256
+
+struct flags {
+ int sigpipe; /* LIBSSH2_FLAG_SIGPIPE */
+ int compress; /* LIBSSH2_FLAG_COMPRESS */
+};
+
+struct _LIBSSH2_SESSION
+{
+ /* Memory management callbacks */
+ void *abstract;
+ LIBSSH2_ALLOC_FUNC((*alloc));
+ LIBSSH2_REALLOC_FUNC((*realloc));
+ LIBSSH2_FREE_FUNC((*free));
+
+ /* Other callbacks */
+ LIBSSH2_IGNORE_FUNC((*ssh_msg_ignore));
+ LIBSSH2_DEBUG_FUNC((*ssh_msg_debug));
+ LIBSSH2_DISCONNECT_FUNC((*ssh_msg_disconnect));
+ LIBSSH2_MACERROR_FUNC((*macerror));
+ LIBSSH2_X11_OPEN_FUNC((*x11));
+ LIBSSH2_SEND_FUNC((*send));
+ LIBSSH2_RECV_FUNC((*recv));
+
+ /* Method preferences -- NULL yields "load order" */
+ char *kex_prefs;
+ char *hostkey_prefs;
+
+ int state;
+
+ /* Flag options */
+ struct flags flag;
+
+ /* Agreed Key Exchange Method */
+ const LIBSSH2_KEX_METHOD *kex;
+ unsigned int burn_optimistic_kexinit:1;
+
+ unsigned char *session_id;
+ uint32_t session_id_len;
+
+ /* this is set to TRUE if a blocking API behavior is requested */
+ int api_block_mode;
+
+ /* Timeout used when blocking API behavior is active */
+ long api_timeout;
+
+ /* Server's public key */
+ const LIBSSH2_HOSTKEY_METHOD *hostkey;
+ void *server_hostkey_abstract;
+
+ /* Either set with libssh2_session_hostkey() (for server mode)
+ * Or read from server in (eg) KEXDH_INIT (for client mode)
+ */
+ unsigned char *server_hostkey;
+ uint32_t server_hostkey_len;
+#if LIBSSH2_MD5
+ unsigned char server_hostkey_md5[MD5_DIGEST_LENGTH];
+ int server_hostkey_md5_valid;
+#endif /* ! LIBSSH2_MD5 */
+ unsigned char server_hostkey_sha1[SHA_DIGEST_LENGTH];
+ int server_hostkey_sha1_valid;
+
+ unsigned char server_hostkey_sha256[SHA256_DIGEST_LENGTH];
+ int server_hostkey_sha256_valid;
+
+ /* (remote as source of data -- packet_read ) */
+ libssh2_endpoint_data remote;
+
+ /* (local as source of data -- packet_write ) */
+ libssh2_endpoint_data local;
+
+ /* Inbound Data linked list -- Sometimes the packet that comes in isn't the
+ packet we're ready for */
+ struct list_head packets;
+
+ /* Active connection channels */
+ struct list_head channels;
+
+ uint32_t next_channel;
+
+ struct list_head listeners; /* list of LIBSSH2_LISTENER structs */
+
+ /* Actual I/O socket */
+ libssh2_socket_t socket_fd;
+ int socket_state;
+ int socket_block_directions;
+ int socket_prev_blockstate; /* stores the state of the socket blockiness
+ when libssh2_session_startup() is called */
+
+ /* Error tracking */
+ const char *err_msg;
+ int err_code;
+ int err_flags;
+
+ /* struct members for packet-level reading */
+ struct transportpacket packet;
+#ifdef LIBSSH2DEBUG
+ int showmask; /* what debug/trace messages to display */
+ libssh2_trace_handler_func tracehandler; /* callback to display trace
+ messages */
+ void *tracehandler_context; /* context for the trace handler */
+#endif
+
+ /* State variables used in libssh2_banner_send() */
+ libssh2_nonblocking_states banner_TxRx_state;
+ char banner_TxRx_banner[256];
+ ssize_t banner_TxRx_total_send;
+
+ /* State variables used in libssh2_kexinit() */
+ libssh2_nonblocking_states kexinit_state;
+ unsigned char *kexinit_data;
+ size_t kexinit_data_len;
+
+ /* State variables used in libssh2_session_startup() */
+ libssh2_nonblocking_states startup_state;
+ unsigned char *startup_data;
+ size_t startup_data_len;
+ unsigned char startup_service[sizeof("ssh-userauth") + 5 - 1];
+ size_t startup_service_length;
+ packet_require_state_t startup_req_state;
+ key_exchange_state_t startup_key_state;
+
+ /* State variables used in libssh2_session_free() */
+ libssh2_nonblocking_states free_state;
+
+ /* State variables used in libssh2_session_disconnect_ex() */
+ libssh2_nonblocking_states disconnect_state;
+ unsigned char disconnect_data[256 + 13];
+ size_t disconnect_data_len;
+
+ /* State variables used in libssh2_packet_read() */
+ libssh2_nonblocking_states readPack_state;
+ int readPack_encrypted;
+
+ /* State variables used in libssh2_userauth_list() */
+ libssh2_nonblocking_states userauth_list_state;
+ unsigned char *userauth_list_data;
+ size_t userauth_list_data_len;
+ packet_requirev_state_t userauth_list_packet_requirev_state;
+
+ /* State variables used in libssh2_userauth_password_ex() */
+ libssh2_nonblocking_states userauth_pswd_state;
+ unsigned char *userauth_pswd_data;
+ unsigned char userauth_pswd_data0;
+ size_t userauth_pswd_data_len;
+ char *userauth_pswd_newpw;
+ int userauth_pswd_newpw_len;
+ packet_requirev_state_t userauth_pswd_packet_requirev_state;
+
+ /* State variables used in libssh2_userauth_hostbased_fromfile_ex() */
+ libssh2_nonblocking_states userauth_host_state;
+ unsigned char *userauth_host_data;
+ size_t userauth_host_data_len;
+ unsigned char *userauth_host_packet;
+ size_t userauth_host_packet_len;
+ unsigned char *userauth_host_method;
+ size_t userauth_host_method_len;
+ unsigned char *userauth_host_s;
+ packet_requirev_state_t userauth_host_packet_requirev_state;
+
+ /* State variables used in libssh2_userauth_publickey_fromfile_ex() */
+ libssh2_nonblocking_states userauth_pblc_state;
+ unsigned char *userauth_pblc_data;
+ size_t userauth_pblc_data_len;
+ unsigned char *userauth_pblc_packet;
+ size_t userauth_pblc_packet_len;
+ unsigned char *userauth_pblc_method;
+ size_t userauth_pblc_method_len;
+ unsigned char *userauth_pblc_s;
+ unsigned char *userauth_pblc_b;
+ packet_requirev_state_t userauth_pblc_packet_requirev_state;
+
+ /* State variables used in libssh2_userauth_keyboard_interactive_ex() */
+ libssh2_nonblocking_states userauth_kybd_state;
+ unsigned char *userauth_kybd_data;
+ size_t userauth_kybd_data_len;
+ unsigned char *userauth_kybd_packet;
+ size_t userauth_kybd_packet_len;
+ unsigned int userauth_kybd_auth_name_len;
+ char *userauth_kybd_auth_name;
+ unsigned userauth_kybd_auth_instruction_len;
+ char *userauth_kybd_auth_instruction;
+ unsigned int userauth_kybd_num_prompts;
+ int userauth_kybd_auth_failure;
+ LIBSSH2_USERAUTH_KBDINT_PROMPT *userauth_kybd_prompts;
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE *userauth_kybd_responses;
+ packet_requirev_state_t userauth_kybd_packet_requirev_state;
+
+ /* State variables used in libssh2_channel_open_ex() */
+ libssh2_nonblocking_states open_state;
+ packet_requirev_state_t open_packet_requirev_state;
+ LIBSSH2_CHANNEL *open_channel;
+ unsigned char *open_packet;
+ size_t open_packet_len;
+ unsigned char *open_data;
+ size_t open_data_len;
+ uint32_t open_local_channel;
+
+ /* State variables used in libssh2_channel_direct_tcpip_ex() */
+ libssh2_nonblocking_states direct_state;
+ unsigned char *direct_message;
+ size_t direct_host_len;
+ size_t direct_shost_len;
+ size_t direct_message_len;
+
+ /* State variables used in libssh2_channel_forward_listen_ex() */
+ libssh2_nonblocking_states fwdLstn_state;
+ unsigned char *fwdLstn_packet;
+ uint32_t fwdLstn_host_len;
+ uint32_t fwdLstn_packet_len;
+ packet_requirev_state_t fwdLstn_packet_requirev_state;
+
+ /* State variables used in libssh2_publickey_init() */
+ libssh2_nonblocking_states pkeyInit_state;
+ LIBSSH2_PUBLICKEY *pkeyInit_pkey;
+ LIBSSH2_CHANNEL *pkeyInit_channel;
+ unsigned char *pkeyInit_data;
+ size_t pkeyInit_data_len;
+ /* 19 = packet_len(4) + version_len(4) + "version"(7) + version_num(4) */
+ unsigned char pkeyInit_buffer[19];
+ size_t pkeyInit_buffer_sent; /* how much of buffer that has been sent */
+
+ /* State variables used in libssh2_packet_add() */
+ libssh2_nonblocking_states packAdd_state;
+ LIBSSH2_CHANNEL *packAdd_channelp; /* keeper of the channel during EAGAIN
+ states */
+ packet_queue_listener_state_t packAdd_Qlstn_state;
+ packet_x11_open_state_t packAdd_x11open_state;
+
+ /* State variables used in fullpacket() */
+ libssh2_nonblocking_states fullpacket_state;
+ int fullpacket_macstate;
+ size_t fullpacket_payload_len;
+ int fullpacket_packet_type;
+
+ /* State variables used in libssh2_sftp_init() */
+ libssh2_nonblocking_states sftpInit_state;
+ LIBSSH2_SFTP *sftpInit_sftp;
+ LIBSSH2_CHANNEL *sftpInit_channel;
+ unsigned char sftpInit_buffer[9]; /* sftp_header(5){excludes request_id}
+ + version_id(4) */
+ int sftpInit_sent; /* number of bytes from the buffer that have been
+ sent */
+
+ /* State variables used in libssh2_scp_recv() / libssh_scp_recv2() */
+ libssh2_nonblocking_states scpRecv_state;
+ unsigned char *scpRecv_command;
+ size_t scpRecv_command_len;
+ unsigned char scpRecv_response[LIBSSH2_SCP_RESPONSE_BUFLEN];
+ size_t scpRecv_response_len;
+ long scpRecv_mode;
+#if defined(HAVE_LONGLONG) && defined(HAVE_STRTOLL)
+ /* we have the type and we can parse such numbers */
+ long long scpRecv_size;
+#define scpsize_strtol strtoll
+#elif defined(HAVE_STRTOI64)
+ __int64 scpRecv_size;
+#define scpsize_strtol _strtoi64
+#else
+ long scpRecv_size;
+#define scpsize_strtol strtol
+#endif
+ long scpRecv_mtime;
+ long scpRecv_atime;
+ LIBSSH2_CHANNEL *scpRecv_channel;
+
+ /* State variables used in libssh2_scp_send_ex() */
+ libssh2_nonblocking_states scpSend_state;
+ unsigned char *scpSend_command;
+ size_t scpSend_command_len;
+ unsigned char scpSend_response[LIBSSH2_SCP_RESPONSE_BUFLEN];
+ size_t scpSend_response_len;
+ LIBSSH2_CHANNEL *scpSend_channel;
+
+ /* Keepalive variables used by keepalive.c. */
+ int keepalive_interval;
+ int keepalive_want_reply;
+ time_t keepalive_last_sent;
+};
+
+/* session.state bits */
+#define LIBSSH2_STATE_EXCHANGING_KEYS 0x00000001
+#define LIBSSH2_STATE_NEWKEYS 0x00000002
+#define LIBSSH2_STATE_AUTHENTICATED 0x00000004
+#define LIBSSH2_STATE_KEX_ACTIVE 0x00000008
+
+/* session.flag helpers */
+#ifdef MSG_NOSIGNAL
+#define LIBSSH2_SOCKET_SEND_FLAGS(session) \
+ (((session)->flag.sigpipe) ? 0 : MSG_NOSIGNAL)
+#define LIBSSH2_SOCKET_RECV_FLAGS(session) \
+ (((session)->flag.sigpipe) ? 0 : MSG_NOSIGNAL)
+#else
+/* If MSG_NOSIGNAL isn't defined we're SOL on blocking SIGPIPE */
+#define LIBSSH2_SOCKET_SEND_FLAGS(session) 0
+#define LIBSSH2_SOCKET_RECV_FLAGS(session) 0
+#endif
+
+/* --------- */
+
+/* libssh2 extensible ssh api, ultimately I'd like to allow loading additional
+ methods via .so/.dll */
+
+struct _LIBSSH2_KEX_METHOD
+{
+ const char *name;
+
+ /* Key exchange, populates session->* and returns 0 on success, non-0 on
+ error */
+ int (*exchange_keys) (LIBSSH2_SESSION * session,
+ key_exchange_state_low_t * key_state);
+
+ long flags;
+};
+
+struct _LIBSSH2_HOSTKEY_METHOD
+{
+ const char *name;
+ unsigned long hash_len;
+
+ int (*init) (LIBSSH2_SESSION * session, const unsigned char *hostkey_data,
+ size_t hostkey_data_len, void **abstract);
+ int (*initPEM) (LIBSSH2_SESSION * session, const char *privkeyfile,
+ unsigned const char *passphrase, void **abstract);
+ int (*initPEMFromMemory) (LIBSSH2_SESSION * session,
+ const char *privkeyfiledata,
+ size_t privkeyfiledata_len,
+ unsigned const char *passphrase,
+ void **abstract);
+ int (*sig_verify) (LIBSSH2_SESSION * session, const unsigned char *sig,
+ size_t sig_len, const unsigned char *m,
+ size_t m_len, void **abstract);
+ int (*signv) (LIBSSH2_SESSION * session, unsigned char **signature,
+ size_t *signature_len, int veccount,
+ const struct iovec datavec[], void **abstract);
+ int (*encrypt) (LIBSSH2_SESSION * session, unsigned char **dst,
+ size_t *dst_len, const unsigned char *src,
+ size_t src_len, void **abstract);
+ int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
+};
+
+struct _LIBSSH2_CRYPT_METHOD
+{
+ const char *name;
+ const char *pem_annotation;
+
+ int blocksize;
+
+ /* iv and key sizes (-1 for variable length) */
+ int iv_len;
+ int secret_len;
+
+ long flags;
+
+ int (*init) (LIBSSH2_SESSION * session,
+ const LIBSSH2_CRYPT_METHOD * method, unsigned char *iv,
+ int *free_iv, unsigned char *secret, int *free_secret,
+ int encrypt, void **abstract);
+ int (*crypt) (LIBSSH2_SESSION * session, unsigned char *block,
+ size_t blocksize, void **abstract);
+ int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
+
+ _libssh2_cipher_type(algo);
+};
+
+struct _LIBSSH2_COMP_METHOD
+{
+ const char *name;
+ int compress; /* 1 if it does compress, 0 if it doesn't */
+ int use_in_auth; /* 1 if compression should be used in userauth */
+ int (*init) (LIBSSH2_SESSION *session, int compress, void **abstract);
+ int (*comp) (LIBSSH2_SESSION *session,
+ unsigned char *dest,
+ size_t *dest_len,
+ const unsigned char *src,
+ size_t src_len,
+ void **abstract);
+ int (*decomp) (LIBSSH2_SESSION *session,
+ unsigned char **dest,
+ size_t *dest_len,
+ size_t payload_limit,
+ const unsigned char *src,
+ size_t src_len,
+ void **abstract);
+ int (*dtor) (LIBSSH2_SESSION * session, int compress, void **abstract);
+};
+
+#ifdef LIBSSH2DEBUG
+void _libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format,
+ ...);
+#else
+#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+ defined(__GNUC__)
+/* C99 supported and also by older GCC */
+#define _libssh2_debug(x,y,z,...) do {} while (0)
+#else
+/* no gcc and not C99, do static and hopefully inline */
+static inline void
+_libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...)
+{
+ (void)session;
+ (void)context;
+ (void)format;
+}
+#endif
+#endif
+
+#define LIBSSH2_SOCKET_UNKNOWN 1
+#define LIBSSH2_SOCKET_CONNECTED 0
+#define LIBSSH2_SOCKET_DISCONNECTED -1
+
+/* Initial packet state, prior to MAC check */
+#define LIBSSH2_MAC_UNCONFIRMED 1
+/* When MAC type is "none" (proto initiation phase) all packets are deemed
+ "confirmed" */
+#define LIBSSH2_MAC_CONFIRMED 0
+/* Something very bad is going on */
+#define LIBSSH2_MAC_INVALID -1
+
+/* Flags for _libssh2_error_flags */
+/* Error message is allocated on the heap */
+#define LIBSSH2_ERR_FLAG_DUP 1
+
+/* SSH Packet Types -- Defined by internet draft */
+/* Transport Layer */
+#define SSH_MSG_DISCONNECT 1
+#define SSH_MSG_IGNORE 2
+#define SSH_MSG_UNIMPLEMENTED 3
+#define SSH_MSG_DEBUG 4
+#define SSH_MSG_SERVICE_REQUEST 5
+#define SSH_MSG_SERVICE_ACCEPT 6
+
+#define SSH_MSG_KEXINIT 20
+#define SSH_MSG_NEWKEYS 21
+
+/* diffie-hellman-group1-sha1 */
+#define SSH_MSG_KEXDH_INIT 30
+#define SSH_MSG_KEXDH_REPLY 31
+
+/* diffie-hellman-group-exchange-sha1 and
+ diffie-hellman-group-exchange-sha256 */
+#define SSH_MSG_KEX_DH_GEX_REQUEST_OLD 30
+#define SSH_MSG_KEX_DH_GEX_REQUEST 34
+#define SSH_MSG_KEX_DH_GEX_GROUP 31
+#define SSH_MSG_KEX_DH_GEX_INIT 32
+#define SSH_MSG_KEX_DH_GEX_REPLY 33
+
+/* ecdh */
+#define SSH2_MSG_KEX_ECDH_INIT 30
+#define SSH2_MSG_KEX_ECDH_REPLY 31
+
+/* User Authentication */
+#define SSH_MSG_USERAUTH_REQUEST 50
+#define SSH_MSG_USERAUTH_FAILURE 51
+#define SSH_MSG_USERAUTH_SUCCESS 52
+#define SSH_MSG_USERAUTH_BANNER 53
+
+/* "public key" method */
+#define SSH_MSG_USERAUTH_PK_OK 60
+/* "password" method */
+#define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 60
+/* "keyboard-interactive" method */
+#define SSH_MSG_USERAUTH_INFO_REQUEST 60
+#define SSH_MSG_USERAUTH_INFO_RESPONSE 61
+
+/* Channels */
+#define SSH_MSG_GLOBAL_REQUEST 80
+#define SSH_MSG_REQUEST_SUCCESS 81
+#define SSH_MSG_REQUEST_FAILURE 82
+
+#define SSH_MSG_CHANNEL_OPEN 90
+#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 91
+#define SSH_MSG_CHANNEL_OPEN_FAILURE 92
+#define SSH_MSG_CHANNEL_WINDOW_ADJUST 93
+#define SSH_MSG_CHANNEL_DATA 94
+#define SSH_MSG_CHANNEL_EXTENDED_DATA 95
+#define SSH_MSG_CHANNEL_EOF 96
+#define SSH_MSG_CHANNEL_CLOSE 97
+#define SSH_MSG_CHANNEL_REQUEST 98
+#define SSH_MSG_CHANNEL_SUCCESS 99
+#define SSH_MSG_CHANNEL_FAILURE 100
+
+/* Error codes returned in SSH_MSG_CHANNEL_OPEN_FAILURE message
+ (see RFC4254) */
+#define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED 1
+#define SSH_OPEN_CONNECT_FAILED 2
+#define SSH_OPEN_UNKNOWN_CHANNELTYPE 3
+#define SSH_OPEN_RESOURCE_SHORTAGE 4
+
+ssize_t _libssh2_recv(libssh2_socket_t socket, void *buffer,
+ size_t length, int flags, void **abstract);
+ssize_t _libssh2_send(libssh2_socket_t socket, const void *buffer,
+ size_t length, int flags, void **abstract);
+
+#define LIBSSH2_READ_TIMEOUT 60 /* generic timeout in seconds used when
+ waiting for more data to arrive */
+
+
+int _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
+ key_exchange_state_t * state);
+
+/* Let crypt.c/hostkey.c expose their method structs */
+const LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void);
+const LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void);
+
+/* misc.c */
+int _libssh2_bcrypt_pbkdf(const char *pass,
+ size_t passlen,
+ const uint8_t *salt,
+ size_t saltlen,
+ uint8_t *key,
+ size_t keylen,
+ unsigned int rounds);
+
+/* pem.c */
+int _libssh2_pem_parse(LIBSSH2_SESSION * session,
+ const char *headerbegin,
+ const char *headerend,
+ const unsigned char *passphrase,
+ FILE * fp, unsigned char **data, unsigned int *datalen);
+int _libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
+ const char *headerbegin,
+ const char *headerend,
+ const char *filedata, size_t filedata_len,
+ unsigned char **data, unsigned int *datalen);
+ /* OpenSSL keys */
+int
+_libssh2_openssh_pem_parse(LIBSSH2_SESSION * session,
+ const unsigned char *passphrase,
+ FILE * fp, struct string_buf **decrypted_buf);
+int
+_libssh2_openssh_pem_parse_memory(LIBSSH2_SESSION * session,
+ const unsigned char *passphrase,
+ const char *filedata, size_t filedata_len,
+ struct string_buf **decrypted_buf);
+
+int _libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen);
+int _libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen,
+ unsigned char **i, unsigned int *ilen);
+
+/* global.c */
+void _libssh2_init_if_needed(void);
+
+
+#define ARRAY_SIZE(a) (sizeof ((a)) / sizeof ((a)[0]))
+
+/* define to output the libssh2_int64_t type in a *printf() */
+#if defined(__BORLANDC__) || defined(_MSC_VER) || defined(__MINGW32__)
+#define LIBSSH2_INT64_T_FORMAT "I64d"
+#else
+#define LIBSSH2_INT64_T_FORMAT "lld"
+#endif
+
+/* In Windows the default file mode is text but an application can override it.
+Therefore we specify it explicitly. https://github.com/curl/curl/pull/258
+*/
+#if defined(WIN32) || defined(MSDOS)
+#define FOPEN_READTEXT "rt"
+#define FOPEN_WRITETEXT "wt"
+#define FOPEN_APPENDTEXT "at"
+#elif defined(__CYGWIN__)
+/* Cygwin has specific behavior we need to address when WIN32 is not defined.
+https://cygwin.com/cygwin-ug-net/using-textbinary.html
+For write we want our output to have line endings of LF and be compatible with
+other Cygwin utilities. For read we want to handle input that may have line
+endings either CRLF or LF so 't' is appropriate.
+*/
+#define FOPEN_READTEXT "rt"
+#define FOPEN_WRITETEXT "w"
+#define FOPEN_APPENDTEXT "a"
+#else
+#define FOPEN_READTEXT "r"
+#define FOPEN_WRITETEXT "w"
+#define FOPEN_APPENDTEXT "a"
+#endif
+
+#endif /* __LIBSSH2_PRIV_H */
diff --git a/contrib/libs/libssh2/src/mac.c b/contrib/libs/libssh2/src/mac.c
new file mode 100644
index 00000000000..5ac71df4cee
--- /dev/null
+++ b/contrib/libs/libssh2/src/mac.c
@@ -0,0 +1,414 @@
+/* Copyright (c) 2004-2007, Sara Golemon <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include "mac.h"
+
+#ifdef LIBSSH2_MAC_NONE
+/* mac_none_MAC
+ * Minimalist MAC: No MAC
+ */
+static int
+mac_none_MAC(LIBSSH2_SESSION * session, unsigned char *buf,
+ uint32_t seqno, const unsigned char *packet,
+ uint32_t packet_len, const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ return 0;
+}
+
+
+
+
+static LIBSSH2_MAC_METHOD mac_method_none = {
+ "none",
+ 0,
+ 0,
+ NULL,
+ mac_none_MAC,
+ NULL
+};
+#endif /* LIBSSH2_MAC_NONE */
+
+/* mac_method_common_init
+ * Initialize simple mac methods
+ */
+static int
+mac_method_common_init(LIBSSH2_SESSION * session, unsigned char *key,
+ int *free_key, void **abstract)
+{
+ *abstract = key;
+ *free_key = 0;
+ (void) session;
+
+ return 0;
+}
+
+
+
+/* mac_method_common_dtor
+ * Cleanup simple mac methods
+ */
+static int
+mac_method_common_dtor(LIBSSH2_SESSION * session, void **abstract)
+{
+ if(*abstract) {
+ LIBSSH2_FREE(session, *abstract);
+ }
+ *abstract = NULL;
+
+ return 0;
+}
+
+
+
+#if LIBSSH2_HMAC_SHA512
+/* mac_method_hmac_sha512_hash
+ * Calculate hash using full sha512 value
+ */
+static int
+mac_method_hmac_sha2_512_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ libssh2_hmac_ctx ctx;
+ unsigned char seqno_buf[4];
+ (void) session;
+
+ _libssh2_htonu32(seqno_buf, seqno);
+
+ libssh2_hmac_ctx_init(ctx);
+ libssh2_hmac_sha512_init(&ctx, *abstract, 64);
+ libssh2_hmac_update(ctx, seqno_buf, 4);
+ libssh2_hmac_update(ctx, packet, packet_len);
+ if(addtl && addtl_len) {
+ libssh2_hmac_update(ctx, addtl, addtl_len);
+ }
+ libssh2_hmac_final(ctx, buf);
+ libssh2_hmac_cleanup(&ctx);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_sha2_512 = {
+ "hmac-sha2-512",
+ 64,
+ 64,
+ mac_method_common_init,
+ mac_method_hmac_sha2_512_hash,
+ mac_method_common_dtor,
+};
+#endif
+
+
+
+#if LIBSSH2_HMAC_SHA256
+/* mac_method_hmac_sha256_hash
+ * Calculate hash using full sha256 value
+ */
+static int
+mac_method_hmac_sha2_256_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ libssh2_hmac_ctx ctx;
+ unsigned char seqno_buf[4];
+ (void) session;
+
+ _libssh2_htonu32(seqno_buf, seqno);
+
+ libssh2_hmac_ctx_init(ctx);
+ libssh2_hmac_sha256_init(&ctx, *abstract, 32);
+ libssh2_hmac_update(ctx, seqno_buf, 4);
+ libssh2_hmac_update(ctx, packet, packet_len);
+ if(addtl && addtl_len) {
+ libssh2_hmac_update(ctx, addtl, addtl_len);
+ }
+ libssh2_hmac_final(ctx, buf);
+ libssh2_hmac_cleanup(&ctx);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_sha2_256 = {
+ "hmac-sha2-256",
+ 32,
+ 32,
+ mac_method_common_init,
+ mac_method_hmac_sha2_256_hash,
+ mac_method_common_dtor,
+};
+#endif
+
+
+
+
+/* mac_method_hmac_sha1_hash
+ * Calculate hash using full sha1 value
+ */
+static int
+mac_method_hmac_sha1_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ libssh2_hmac_ctx ctx;
+ unsigned char seqno_buf[4];
+ (void) session;
+
+ _libssh2_htonu32(seqno_buf, seqno);
+
+ libssh2_hmac_ctx_init(ctx);
+ libssh2_hmac_sha1_init(&ctx, *abstract, 20);
+ libssh2_hmac_update(ctx, seqno_buf, 4);
+ libssh2_hmac_update(ctx, packet, packet_len);
+ if(addtl && addtl_len) {
+ libssh2_hmac_update(ctx, addtl, addtl_len);
+ }
+ libssh2_hmac_final(ctx, buf);
+ libssh2_hmac_cleanup(&ctx);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_sha1 = {
+ "hmac-sha1",
+ 20,
+ 20,
+ mac_method_common_init,
+ mac_method_hmac_sha1_hash,
+ mac_method_common_dtor,
+};
+
+/* mac_method_hmac_sha1_96_hash
+ * Calculate hash using first 96 bits of sha1 value
+ */
+static int
+mac_method_hmac_sha1_96_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ unsigned char temp[SHA_DIGEST_LENGTH];
+
+ mac_method_hmac_sha1_hash(session, temp, seqno, packet, packet_len,
+ addtl, addtl_len, abstract);
+ memcpy(buf, (char *) temp, 96 / 8);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_sha1_96 = {
+ "hmac-sha1-96",
+ 12,
+ 20,
+ mac_method_common_init,
+ mac_method_hmac_sha1_96_hash,
+ mac_method_common_dtor,
+};
+
+#if LIBSSH2_MD5
+/* mac_method_hmac_md5_hash
+ * Calculate hash using full md5 value
+ */
+static int
+mac_method_hmac_md5_hash(LIBSSH2_SESSION * session, unsigned char *buf,
+ uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ libssh2_hmac_ctx ctx;
+ unsigned char seqno_buf[4];
+ (void) session;
+
+ _libssh2_htonu32(seqno_buf, seqno);
+
+ libssh2_hmac_ctx_init(ctx);
+ libssh2_hmac_md5_init(&ctx, *abstract, 16);
+ libssh2_hmac_update(ctx, seqno_buf, 4);
+ libssh2_hmac_update(ctx, packet, packet_len);
+ if(addtl && addtl_len) {
+ libssh2_hmac_update(ctx, addtl, addtl_len);
+ }
+ libssh2_hmac_final(ctx, buf);
+ libssh2_hmac_cleanup(&ctx);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_md5 = {
+ "hmac-md5",
+ 16,
+ 16,
+ mac_method_common_init,
+ mac_method_hmac_md5_hash,
+ mac_method_common_dtor,
+};
+
+/* mac_method_hmac_md5_96_hash
+ * Calculate hash using first 96 bits of md5 value
+ */
+static int
+mac_method_hmac_md5_96_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract)
+{
+ unsigned char temp[MD5_DIGEST_LENGTH];
+ mac_method_hmac_md5_hash(session, temp, seqno, packet, packet_len,
+ addtl, addtl_len, abstract);
+ memcpy(buf, (char *) temp, 96 / 8);
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_md5_96 = {
+ "hmac-md5-96",
+ 12,
+ 16,
+ mac_method_common_init,
+ mac_method_hmac_md5_96_hash,
+ mac_method_common_dtor,
+};
+#endif /* LIBSSH2_MD5 */
+
+#if LIBSSH2_HMAC_RIPEMD
+/* mac_method_hmac_ripemd160_hash
+ * Calculate hash using ripemd160 value
+ */
+static int
+mac_method_hmac_ripemd160_hash(LIBSSH2_SESSION * session,
+ unsigned char *buf, uint32_t seqno,
+ const unsigned char *packet,
+ uint32_t packet_len,
+ const unsigned char *addtl,
+ uint32_t addtl_len,
+ void **abstract)
+{
+ libssh2_hmac_ctx ctx;
+ unsigned char seqno_buf[4];
+ (void) session;
+
+ _libssh2_htonu32(seqno_buf, seqno);
+
+ libssh2_hmac_ctx_init(ctx);
+ libssh2_hmac_ripemd160_init(&ctx, *abstract, 20);
+ libssh2_hmac_update(ctx, seqno_buf, 4);
+ libssh2_hmac_update(ctx, packet, packet_len);
+ if(addtl && addtl_len) {
+ libssh2_hmac_update(ctx, addtl, addtl_len);
+ }
+ libssh2_hmac_final(ctx, buf);
+ libssh2_hmac_cleanup(&ctx);
+
+ return 0;
+}
+
+
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_ripemd160 = {
+ "hmac-ripemd160",
+ 20,
+ 20,
+ mac_method_common_init,
+ mac_method_hmac_ripemd160_hash,
+ mac_method_common_dtor,
+};
+
+static const LIBSSH2_MAC_METHOD mac_method_hmac_ripemd160_openssh_com = {
+ 20,
+ 20,
+ mac_method_common_init,
+ mac_method_hmac_ripemd160_hash,
+ mac_method_common_dtor,
+};
+#endif /* LIBSSH2_HMAC_RIPEMD */
+
+static const LIBSSH2_MAC_METHOD *mac_methods[] = {
+#if LIBSSH2_HMAC_SHA256
+ &mac_method_hmac_sha2_256,
+#endif
+#if LIBSSH2_HMAC_SHA512
+ &mac_method_hmac_sha2_512,
+#endif
+ &mac_method_hmac_sha1,
+ &mac_method_hmac_sha1_96,
+#if LIBSSH2_MD5
+ &mac_method_hmac_md5,
+ &mac_method_hmac_md5_96,
+#endif
+#if LIBSSH2_HMAC_RIPEMD
+ &mac_method_hmac_ripemd160,
+ &mac_method_hmac_ripemd160_openssh_com,
+#endif /* LIBSSH2_HMAC_RIPEMD */
+#ifdef LIBSSH2_MAC_NONE
+ &mac_method_none,
+#endif /* LIBSSH2_MAC_NONE */
+ NULL
+};
+
+const LIBSSH2_MAC_METHOD **
+_libssh2_mac_methods(void)
+{
+ return mac_methods;
+}
diff --git a/contrib/libs/libssh2/src/mac.h b/contrib/libs/libssh2/src/mac.h
new file mode 100644
index 00000000000..46fce54248c
--- /dev/null
+++ b/contrib/libs/libssh2/src/mac.h
@@ -0,0 +1,66 @@
+#ifndef __LIBSSH2_MAC_H
+#define __LIBSSH2_MAC_H
+/* Copyright (C) 2009-2010 by Daniel Stenberg
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+
+struct _LIBSSH2_MAC_METHOD
+{
+ const char *name;
+
+ /* The length of a given MAC packet */
+ int mac_len;
+
+ /* integrity key length */
+ int key_len;
+
+ /* Message Authentication Code Hashing algo */
+ int (*init) (LIBSSH2_SESSION * session, unsigned char *key, int *free_key,
+ void **abstract);
+ int (*hash) (LIBSSH2_SESSION * session, unsigned char *buf,
+ uint32_t seqno, const unsigned char *packet,
+ uint32_t packet_len, const unsigned char *addtl,
+ uint32_t addtl_len, void **abstract);
+ int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
+};
+
+typedef struct _LIBSSH2_MAC_METHOD LIBSSH2_MAC_METHOD;
+
+const LIBSSH2_MAC_METHOD **_libssh2_mac_methods(void);
+
+#endif /* __LIBSSH2_MAC_H */
diff --git a/contrib/libs/libssh2/src/misc.c b/contrib/libs/libssh2/src/misc.c
new file mode 100644
index 00000000000..594b2d1f770
--- /dev/null
+++ b/contrib/libs/libssh2/src/misc.c
@@ -0,0 +1,872 @@
+/* Copyright (c) 2004-2007 Sara Golemon <[email protected]>
+ * Copyright (c) 2009-2019 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include "misc.h"
+#include "blf.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#if defined(HAVE_DECL_SECUREZEROMEMORY) && HAVE_DECL_SECUREZEROMEMORY
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+int _libssh2_error_flags(LIBSSH2_SESSION* session, int errcode,
+ const char *errmsg, int errflags)
+{
+ if(session->err_flags & LIBSSH2_ERR_FLAG_DUP)
+ LIBSSH2_FREE(session, (char *)session->err_msg);
+
+ session->err_code = errcode;
+ session->err_flags = 0;
+
+ if((errmsg != NULL) && ((errflags & LIBSSH2_ERR_FLAG_DUP) != 0)) {
+ size_t len = strlen(errmsg);
+ char *copy = LIBSSH2_ALLOC(session, len + 1);
+ if(copy) {
+ memcpy(copy, errmsg, len + 1);
+ session->err_flags = LIBSSH2_ERR_FLAG_DUP;
+ session->err_msg = copy;
+ }
+ else
+ /* Out of memory: this code path is very unlikely */
+ session->err_msg = "former error forgotten (OOM)";
+ }
+ else
+ session->err_msg = errmsg;
+
+#ifdef LIBSSH2DEBUG
+ if((errcode == LIBSSH2_ERROR_EAGAIN) && !session->api_block_mode)
+ /* if this is EAGAIN and we're in non-blocking mode, don't generate
+ a debug output for this */
+ return errcode;
+ _libssh2_debug(session, LIBSSH2_TRACE_ERROR, "%d - %s", session->err_code,
+ session->err_msg);
+#endif
+
+ return errcode;
+}
+
+int _libssh2_error(LIBSSH2_SESSION* session, int errcode, const char *errmsg)
+{
+ return _libssh2_error_flags(session, errcode, errmsg, 0);
+}
+
+#ifdef WIN32
+static int wsa2errno(void)
+{
+ switch(WSAGetLastError()) {
+ case WSAEWOULDBLOCK:
+ return EAGAIN;
+
+ case WSAENOTSOCK:
+ return EBADF;
+
+ case WSAEINTR:
+ return EINTR;
+
+ default:
+ /* It is most important to ensure errno does not stay at EAGAIN
+ * when a different error occurs so just set errno to a generic
+ * error */
+ return EIO;
+ }
+}
+#endif
+
+/* _libssh2_recv
+ *
+ * Replacement for the standard recv, return -errno on failure.
+ */
+ssize_t
+_libssh2_recv(libssh2_socket_t sock, void *buffer, size_t length,
+ int flags, void **abstract)
+{
+ ssize_t rc;
+
+ (void) abstract;
+
+ rc = recv(sock, buffer, length, flags);
+#ifdef WIN32
+ if(rc < 0)
+ return -wsa2errno();
+#else
+ if(rc < 0) {
+ /* Sometimes the first recv() function call sets errno to ENOENT on
+ Solaris and HP-UX */
+ if(errno == ENOENT)
+ return -EAGAIN;
+#ifdef EWOULDBLOCK /* For VMS and other special unixes */
+ else if(errno == EWOULDBLOCK)
+ return -EAGAIN;
+#endif
+ else
+ return -errno;
+ }
+#endif
+ return rc;
+}
+
+/* _libssh2_send
+ *
+ * Replacement for the standard send, return -errno on failure.
+ */
+ssize_t
+_libssh2_send(libssh2_socket_t sock, const void *buffer, size_t length,
+ int flags, void **abstract)
+{
+ ssize_t rc;
+
+ (void) abstract;
+
+ rc = send(sock, buffer, length, flags);
+#ifdef WIN32
+ if(rc < 0)
+ return -wsa2errno();
+#else
+ if(rc < 0) {
+#ifdef EWOULDBLOCK /* For VMS and other special unixes */
+ if(errno == EWOULDBLOCK)
+ return -EAGAIN;
+#endif
+ return -errno;
+ }
+#endif
+ return rc;
+}
+
+/* libssh2_ntohu32
+ */
+unsigned int
+_libssh2_ntohu32(const unsigned char *buf)
+{
+ return (((unsigned int)buf[0] << 24)
+ | ((unsigned int)buf[1] << 16)
+ | ((unsigned int)buf[2] << 8)
+ | ((unsigned int)buf[3]));
+}
+
+
+/* _libssh2_ntohu64
+ */
+libssh2_uint64_t
+_libssh2_ntohu64(const unsigned char *buf)
+{
+ unsigned long msl, lsl;
+
+ msl = ((libssh2_uint64_t)buf[0] << 24) | ((libssh2_uint64_t)buf[1] << 16)
+ | ((libssh2_uint64_t)buf[2] << 8) | (libssh2_uint64_t)buf[3];
+ lsl = ((libssh2_uint64_t)buf[4] << 24) | ((libssh2_uint64_t)buf[5] << 16)
+ | ((libssh2_uint64_t)buf[6] << 8) | (libssh2_uint64_t)buf[7];
+
+ return ((libssh2_uint64_t)msl <<32) | lsl;
+}
+
+/* _libssh2_htonu32
+ */
+void
+_libssh2_htonu32(unsigned char *buf, uint32_t value)
+{
+ buf[0] = (value >> 24) & 0xFF;
+ buf[1] = (value >> 16) & 0xFF;
+ buf[2] = (value >> 8) & 0xFF;
+ buf[3] = value & 0xFF;
+}
+
+/* _libssh2_store_u32
+ */
+void _libssh2_store_u32(unsigned char **buf, uint32_t value)
+{
+ _libssh2_htonu32(*buf, value);
+ *buf += sizeof(uint32_t);
+}
+
+/* _libssh2_store_str
+ */
+void _libssh2_store_str(unsigned char **buf, const char *str, size_t len)
+{
+ _libssh2_store_u32(buf, (uint32_t)len);
+ if(len) {
+ memcpy(*buf, str, len);
+ *buf += len;
+ }
+}
+
+/* Base64 Conversion */
+
+static const short base64_reverse_table[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+/* libssh2_base64_decode
+ *
+ * Decode a base64 chunk and store it into a newly alloc'd buffer
+ */
+LIBSSH2_API int
+libssh2_base64_decode(LIBSSH2_SESSION *session, char **data,
+ unsigned int *datalen, const char *src,
+ unsigned int src_len)
+{
+ unsigned char *s, *d;
+ short v;
+ int i = 0, len = 0;
+
+ *data = LIBSSH2_ALLOC(session, (3 * src_len / 4) + 1);
+ d = (unsigned char *) *data;
+ if(!d) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for base64 decoding");
+ }
+
+ for(s = (unsigned char *) src; ((char *) s) < (src + src_len); s++) {
+ v = base64_reverse_table[*s];
+ if(v < 0)
+ continue;
+ switch(i % 4) {
+ case 0:
+ d[len] = (unsigned char)(v << 2);
+ break;
+ case 1:
+ d[len++] |= v >> 4;
+ d[len] = (unsigned char)(v << 4);
+ break;
+ case 2:
+ d[len++] |= v >> 2;
+ d[len] = (unsigned char)(v << 6);
+ break;
+ case 3:
+ d[len++] |= v;
+ break;
+ }
+ i++;
+ }
+ if((i % 4) == 1) {
+ /* Invalid -- We have a byte which belongs exclusively to a partial
+ octet */
+ LIBSSH2_FREE(session, *data);
+ *data = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL, "Invalid base64");
+ }
+
+ *datalen = len;
+ return 0;
+}
+
+/* ---- Base64 Encoding/Decoding Table --- */
+static const char table64[]=
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * _libssh2_base64_encode()
+ *
+ * Returns the length of the newly created base64 string. The third argument
+ * is a pointer to an allocated area holding the base64 data. If something
+ * went wrong, 0 is returned.
+ *
+ */
+size_t _libssh2_base64_encode(LIBSSH2_SESSION *session,
+ const char *inp, size_t insize, char **outptr)
+{
+ unsigned char ibuf[3];
+ unsigned char obuf[4];
+ int i;
+ int inputparts;
+ char *output;
+ char *base64data;
+ const char *indata = inp;
+
+ *outptr = NULL; /* set to NULL in case of failure before we reach the
+ end */
+
+ if(0 == insize)
+ insize = strlen(indata);
+
+ base64data = output = LIBSSH2_ALLOC(session, insize * 4 / 3 + 4);
+ if(NULL == output)
+ return 0;
+
+ while(insize > 0) {
+ for(i = inputparts = 0; i < 3; i++) {
+ if(insize > 0) {
+ inputparts++;
+ ibuf[i] = *indata;
+ indata++;
+ insize--;
+ }
+ else
+ ibuf[i] = 0;
+ }
+
+ obuf[0] = (unsigned char) ((ibuf[0] & 0xFC) >> 2);
+ obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
+ ((ibuf[1] & 0xF0) >> 4));
+ obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
+ ((ibuf[2] & 0xC0) >> 6));
+ obuf[3] = (unsigned char) (ibuf[2] & 0x3F);
+
+ switch(inputparts) {
+ case 1: /* only one byte read */
+ snprintf(output, 5, "%c%c==",
+ table64[obuf[0]],
+ table64[obuf[1]]);
+ break;
+ case 2: /* two bytes read */
+ snprintf(output, 5, "%c%c%c=",
+ table64[obuf[0]],
+ table64[obuf[1]],
+ table64[obuf[2]]);
+ break;
+ default:
+ snprintf(output, 5, "%c%c%c%c",
+ table64[obuf[0]],
+ table64[obuf[1]],
+ table64[obuf[2]],
+ table64[obuf[3]]);
+ break;
+ }
+ output += 4;
+ }
+ *output = 0;
+ *outptr = base64data; /* make it return the actual data memory */
+
+ return strlen(base64data); /* return the length of the new data */
+}
+/* ---- End of Base64 Encoding ---- */
+
+LIBSSH2_API void
+libssh2_free(LIBSSH2_SESSION *session, void *ptr)
+{
+ LIBSSH2_FREE(session, ptr);
+}
+
+#ifdef LIBSSH2DEBUG
+#include <stdarg.h>
+
+LIBSSH2_API int
+libssh2_trace(LIBSSH2_SESSION * session, int bitmask)
+{
+ session->showmask = bitmask;
+ return 0;
+}
+
+LIBSSH2_API int
+libssh2_trace_sethandler(LIBSSH2_SESSION *session, void *handler_context,
+ libssh2_trace_handler_func callback)
+{
+ session->tracehandler = callback;
+ session->tracehandler_context = handler_context;
+ return 0;
+}
+
+void
+_libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...)
+{
+ char buffer[1536];
+ int len, msglen, buflen = sizeof(buffer);
+ va_list vargs;
+ struct timeval now;
+ static int firstsec;
+ static const char *const contexts[] = {
+ "Unknown",
+ "Transport",
+ "Key Ex",
+ "Userauth",
+ "Conn",
+ "SCP",
+ "SFTP",
+ "Failure Event",
+ "Publickey",
+ "Socket",
+ };
+ const char *contexttext = contexts[0];
+ unsigned int contextindex;
+
+ if(!(session->showmask & context)) {
+ /* no such output asked for */
+ return;
+ }
+
+ /* Find the first matching context string for this message */
+ for(contextindex = 0; contextindex < ARRAY_SIZE(contexts);
+ contextindex++) {
+ if((context & (1 << contextindex)) != 0) {
+ contexttext = contexts[contextindex];
+ break;
+ }
+ }
+
+ _libssh2_gettimeofday(&now, NULL);
+ if(!firstsec) {
+ firstsec = now.tv_sec;
+ }
+ now.tv_sec -= firstsec;
+
+ len = snprintf(buffer, buflen, "[libssh2] %d.%06d %s: ",
+ (int)now.tv_sec, (int)now.tv_usec, contexttext);
+
+ if(len >= buflen)
+ msglen = buflen - 1;
+ else {
+ buflen -= len;
+ msglen = len;
+ va_start(vargs, format);
+ len = vsnprintf(buffer + msglen, buflen, format, vargs);
+ va_end(vargs);
+ msglen += len < buflen ? len : buflen - 1;
+ }
+
+ if(session->tracehandler)
+ (session->tracehandler)(session, session->tracehandler_context, buffer,
+ msglen);
+ else
+ fprintf(stderr, "%s\n", buffer);
+}
+
+#else
+LIBSSH2_API int
+libssh2_trace(LIBSSH2_SESSION * session, int bitmask)
+{
+ (void) session;
+ (void) bitmask;
+ return 0;
+}
+
+LIBSSH2_API int
+libssh2_trace_sethandler(LIBSSH2_SESSION *session, void *handler_context,
+ libssh2_trace_handler_func callback)
+{
+ (void) session;
+ (void) handler_context;
+ (void) callback;
+ return 0;
+}
+#endif
+
+/* init the list head */
+void _libssh2_list_init(struct list_head *head)
+{
+ head->first = head->last = NULL;
+}
+
+/* add a node to the list */
+void _libssh2_list_add(struct list_head *head,
+ struct list_node *entry)
+{
+ /* store a pointer to the head */
+ entry->head = head;
+
+ /* we add this entry at the "top" so it has no next */
+ entry->next = NULL;
+
+ /* make our prev point to what the head thinks is last */
+ entry->prev = head->last;
+
+ /* and make head's last be us now */
+ head->last = entry;
+
+ /* make sure our 'prev' node points to us next */
+ if(entry->prev)
+ entry->prev->next = entry;
+ else
+ head->first = entry;
+}
+
+/* return the "first" node in the list this head points to */
+void *_libssh2_list_first(struct list_head *head)
+{
+ return head->first;
+}
+
+/* return the next node in the list */
+void *_libssh2_list_next(struct list_node *node)
+{
+ return node->next;
+}
+
+/* return the prev node in the list */
+void *_libssh2_list_prev(struct list_node *node)
+{
+ return node->prev;
+}
+
+/* remove this node from the list */
+void _libssh2_list_remove(struct list_node *entry)
+{
+ if(entry->prev)
+ entry->prev->next = entry->next;
+ else
+ entry->head->first = entry->next;
+
+ if(entry->next)
+ entry->next->prev = entry->prev;
+ else
+ entry->head->last = entry->prev;
+}
+
+#if 0
+/* insert a node before the given 'after' entry */
+void _libssh2_list_insert(struct list_node *after, /* insert before this */
+ struct list_node *entry)
+{
+ /* 'after' is next to 'entry' */
+ bentry->next = after;
+
+ /* entry's prev is then made to be the prev after current has */
+ entry->prev = after->prev;
+
+ /* the node that is now before 'entry' was previously before 'after'
+ and must be made to point to 'entry' correctly */
+ if(entry->prev)
+ entry->prev->next = entry;
+ else
+ /* there was no node before this, so we make sure we point the head
+ pointer to this node */
+ after->head->first = entry;
+
+ /* after's prev entry points back to entry */
+ after->prev = entry;
+
+ /* after's next entry is still the same as before */
+
+ /* entry's head is the same as after's */
+ entry->head = after->head;
+}
+
+#endif
+
+/* this define is defined in misc.h for the correct platforms */
+#ifdef LIBSSH2_GETTIMEOFDAY_WIN32
+/*
+ * gettimeofday
+ * Implementation according to:
+ * The Open Group Base Specifications Issue 6
+ * IEEE Std 1003.1, 2004 Edition
+ */
+
+/*
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ *
+ * This code is distributed in the hope that it will be useful but
+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
+ * DISCLAIMED. This includes but is not limited to warranties of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Contributed by:
+ * Danny Smith <[email protected]>
+ */
+
+/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */
+#define _W32_FT_OFFSET (116444736000000000)
+
+int __cdecl _libssh2_gettimeofday(struct timeval *tp, void *tzp)
+{
+ union {
+ unsigned __int64 ns100; /*time since 1 Jan 1601 in 100ns units */
+ FILETIME ft;
+ } _now;
+ (void)tzp;
+ if(tp) {
+ GetSystemTimeAsFileTime(&_now.ft);
+ tp->tv_usec = (long)((_now.ns100 / 10) % 1000000);
+ tp->tv_sec = (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000);
+ }
+ /* Always return 0 as per Open Group Base Specifications Issue 6.
+ Do not set errno on error. */
+ return 0;
+}
+
+
+#endif
+
+void *_libssh2_calloc(LIBSSH2_SESSION* session, size_t size)
+{
+ void *p = LIBSSH2_ALLOC(session, size);
+ if(p) {
+ memset(p, 0, size);
+ }
+ return p;
+}
+
+/* XOR operation on buffers input1 and input2, result in output.
+ It is safe to use an input buffer as the output buffer. */
+void _libssh2_xor_data(unsigned char *output,
+ const unsigned char *input1,
+ const unsigned char *input2,
+ size_t length)
+{
+ size_t i;
+
+ for(i = 0; i < length; i++)
+ *output++ = *input1++ ^ *input2++;
+}
+
+/* Increments an AES CTR buffer to prepare it for use with the
+ next AES block. */
+void _libssh2_aes_ctr_increment(unsigned char *ctr,
+ size_t length)
+{
+ unsigned char *pc;
+ unsigned int val, carry;
+
+ pc = ctr + length - 1;
+ carry = 1;
+
+ while(pc >= ctr) {
+ val = (unsigned int)*pc + carry;
+ *pc-- = val & 0xFF;
+ carry = val >> 8;
+ }
+}
+
+#ifdef WIN32
+static void * (__cdecl * const volatile memset_libssh)(void *, int, size_t) =
+ memset;
+#else
+static void * (* const volatile memset_libssh)(void *, int, size_t) = memset;
+#endif
+
+void _libssh2_explicit_zero(void *buf, size_t size)
+{
+#if defined(HAVE_DECL_SECUREZEROMEMORY) && HAVE_DECL_SECUREZEROMEMORY
+ SecureZeroMemory(buf, size);
+ (void)memset_libssh; /* Silence unused variable warning */
+#elif defined(HAVE_MEMSET_S)
+ (void)memset_s(buf, size, 0, size);
+ (void)memset_libssh; /* Silence unused variable warning */
+#else
+ memset_libssh(buf, 0, size);
+#endif
+}
+
+/* String buffer */
+
+struct string_buf* _libssh2_string_buf_new(LIBSSH2_SESSION *session)
+{
+ struct string_buf *ret;
+
+ ret = _libssh2_calloc(session, sizeof(*ret));
+ if(ret == NULL)
+ return NULL;
+
+ return ret;
+}
+
+void _libssh2_string_buf_free(LIBSSH2_SESSION *session, struct string_buf *buf)
+{
+ if(buf == NULL)
+ return;
+
+ if(buf->data != NULL)
+ LIBSSH2_FREE(session, buf->data);
+
+ LIBSSH2_FREE(session, buf);
+ buf = NULL;
+}
+
+int _libssh2_get_u32(struct string_buf *buf, uint32_t *out)
+{
+ if(!_libssh2_check_length(buf, 4)) {
+ return -1;
+ }
+
+ *out = _libssh2_ntohu32(buf->dataptr);
+ buf->dataptr += 4;
+ return 0;
+}
+
+int _libssh2_get_u64(struct string_buf *buf, libssh2_uint64_t *out)
+{
+ if(!_libssh2_check_length(buf, 8)) {
+ return -1;
+ }
+
+ *out = _libssh2_ntohu64(buf->dataptr);
+ buf->dataptr += 8;
+ return 0;
+}
+
+int _libssh2_match_string(struct string_buf *buf, const char *match)
+{
+ unsigned char *out;
+ size_t len = 0;
+ if(_libssh2_get_string(buf, &out, &len) || len != strlen(match) ||
+ strncmp((char *)out, match, strlen(match)) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+int _libssh2_get_string(struct string_buf *buf, unsigned char **outbuf,
+ size_t *outlen)
+{
+ uint32_t data_len;
+ if(_libssh2_get_u32(buf, &data_len) != 0) {
+ return -1;
+ }
+ if(!_libssh2_check_length(buf, data_len)) {
+ return -1;
+ }
+ *outbuf = buf->dataptr;
+ buf->dataptr += data_len;
+
+ if(outlen)
+ *outlen = (size_t)data_len;
+
+ return 0;
+}
+
+int _libssh2_copy_string(LIBSSH2_SESSION *session, struct string_buf *buf,
+ unsigned char **outbuf, size_t *outlen)
+{
+ size_t str_len;
+ unsigned char *str;
+
+ if(_libssh2_get_string(buf, &str, &str_len)) {
+ return -1;
+ }
+
+ *outbuf = LIBSSH2_ALLOC(session, str_len);
+ if(*outbuf) {
+ memcpy(*outbuf, str, str_len);
+ }
+ else {
+ return -1;
+ }
+
+ if(outlen)
+ *outlen = str_len;
+
+ return 0;
+}
+
+int _libssh2_get_bignum_bytes(struct string_buf *buf, unsigned char **outbuf,
+ size_t *outlen)
+{
+ uint32_t data_len;
+ uint32_t bn_len;
+ unsigned char *bnptr;
+
+ if(_libssh2_get_u32(buf, &data_len)) {
+ return -1;
+ }
+ if(!_libssh2_check_length(buf, data_len)) {
+ return -1;
+ }
+
+ bn_len = data_len;
+ bnptr = buf->dataptr;
+
+ /* trim leading zeros */
+ while(bn_len > 0 && *bnptr == 0x00) {
+ bn_len--;
+ bnptr++;
+ }
+
+ *outbuf = bnptr;
+ buf->dataptr += data_len;
+
+ if(outlen)
+ *outlen = (size_t)bn_len;
+
+ return 0;
+}
+
+/* Given the current location in buf, _libssh2_check_length ensures
+ callers can read the next len number of bytes out of the buffer
+ before reading the buffer content */
+
+int _libssh2_check_length(struct string_buf *buf, size_t len)
+{
+ unsigned char *endp = &buf->data[buf->len];
+ size_t left = endp - buf->dataptr;
+ return ((len <= left) && (left <= buf->len));
+}
+
+/* Wrappers */
+
+int _libssh2_bcrypt_pbkdf(const char *pass,
+ size_t passlen,
+ const uint8_t *salt,
+ size_t saltlen,
+ uint8_t *key,
+ size_t keylen,
+ unsigned int rounds)
+{
+ /* defined in bcrypt_pbkdf.c */
+ return bcrypt_pbkdf(pass,
+ passlen,
+ salt,
+ saltlen,
+ key,
+ keylen,
+ rounds);
+}
diff --git a/contrib/libs/libssh2/src/misc.h b/contrib/libs/libssh2/src/misc.h
new file mode 100644
index 00000000000..5481e666ca1
--- /dev/null
+++ b/contrib/libs/libssh2/src/misc.h
@@ -0,0 +1,125 @@
+#ifndef __LIBSSH2_MISC_H
+#define __LIBSSH2_MISC_H
+/* Copyright (c) 2009-2019 by Daniel Stenberg
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.
+ */
+
+struct list_head {
+ struct list_node *last;
+ struct list_node *first;
+};
+
+struct list_node {
+ struct list_node *next;
+ struct list_node *prev;
+ struct list_head *head;
+};
+
+struct string_buf {
+ unsigned char *data;
+ unsigned char *dataptr;
+ size_t len;
+};
+
+int _libssh2_error_flags(LIBSSH2_SESSION* session, int errcode,
+ const char *errmsg, int errflags);
+int _libssh2_error(LIBSSH2_SESSION* session, int errcode, const char *errmsg);
+
+void _libssh2_list_init(struct list_head *head);
+
+/* add a node last in the list */
+void _libssh2_list_add(struct list_head *head,
+ struct list_node *entry);
+
+/* return the "first" node in the list this head points to */
+void *_libssh2_list_first(struct list_head *head);
+
+/* return the next node in the list */
+void *_libssh2_list_next(struct list_node *node);
+
+/* return the prev node in the list */
+void *_libssh2_list_prev(struct list_node *node);
+
+/* remove this node from the list */
+void _libssh2_list_remove(struct list_node *entry);
+
+size_t _libssh2_base64_encode(LIBSSH2_SESSION *session,
+ const char *inp, size_t insize, char **outptr);
+
+unsigned int _libssh2_ntohu32(const unsigned char *buf);
+libssh2_uint64_t _libssh2_ntohu64(const unsigned char *buf);
+void _libssh2_htonu32(unsigned char *buf, uint32_t val);
+void _libssh2_store_u32(unsigned char **buf, uint32_t value);
+void _libssh2_store_str(unsigned char **buf, const char *str, size_t len);
+void *_libssh2_calloc(LIBSSH2_SESSION *session, size_t size);
+void _libssh2_explicit_zero(void *buf, size_t size);
+
+struct string_buf* _libssh2_string_buf_new(LIBSSH2_SESSION *session);
+void _libssh2_string_buf_free(LIBSSH2_SESSION *session,
+ struct string_buf *buf);
+int _libssh2_get_u32(struct string_buf *buf, uint32_t *out);
+int _libssh2_get_u64(struct string_buf *buf, libssh2_uint64_t *out);
+int _libssh2_match_string(struct string_buf *buf, const char *match);
+int _libssh2_get_string(struct string_buf *buf, unsigned char **outbuf,
+ size_t *outlen);
+int _libssh2_copy_string(LIBSSH2_SESSION* session, struct string_buf *buf,
+ unsigned char **outbuf, size_t *outlen);
+int _libssh2_get_bignum_bytes(struct string_buf *buf, unsigned char **outbuf,
+ size_t *outlen);
+int _libssh2_check_length(struct string_buf *buf, size_t requested_len);
+
+#if defined(LIBSSH2_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
+/* provide a private one */
+#undef HAVE_GETTIMEOFDAY
+int __cdecl _libssh2_gettimeofday(struct timeval *tp, void *tzp);
+#define HAVE_LIBSSH2_GETTIMEOFDAY
+#define LIBSSH2_GETTIMEOFDAY_WIN32 /* enable the win32 implementation */
+#else
+#ifdef HAVE_GETTIMEOFDAY
+#define _libssh2_gettimeofday(x,y) gettimeofday(x,y)
+#define HAVE_LIBSSH2_GETTIMEOFDAY
+#endif
+#endif
+
+void _libssh2_xor_data(unsigned char *output,
+ const unsigned char *input1,
+ const unsigned char *input2,
+ size_t length);
+
+void _libssh2_aes_ctr_increment(unsigned char *ctr, size_t length);
+
+#endif /* _LIBSSH2_MISC_H */
diff --git a/contrib/libs/libssh2/src/openssl.c b/contrib/libs/libssh2/src/openssl.c
new file mode 100644
index 00000000000..7a6810f13c0
--- /dev/null
+++ b/contrib/libs/libssh2/src/openssl.c
@@ -0,0 +1,3286 @@
+/* Copyright (C) 2009, 2010 Simon Josefsson
+ * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved.
+ * Copyright (c) 2004-2006, Sara Golemon <[email protected]>
+ *
+ * Author: Simon Josefsson
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+
+#ifdef LIBSSH2_OPENSSL /* compile only if we build with openssl */
+
+#include <string.h>
+#include "misc.h"
+
+#ifndef EVP_MAX_BLOCK_LENGTH
+#define EVP_MAX_BLOCK_LENGTH 32
+#endif
+
+int
+read_openssh_private_key_from_memory(void **key_ctx, LIBSSH2_SESSION *session,
+ const char *key_type,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase);
+
+static unsigned char *
+write_bn(unsigned char *buf, const BIGNUM *bn, int bn_bytes)
+{
+ unsigned char *p = buf;
+
+ /* Left space for bn size which will be written below. */
+ p += 4;
+
+ *p = 0;
+ BN_bn2bin(bn, p + 1);
+ if(!(*(p + 1) & 0x80)) {
+ memmove(p, p + 1, --bn_bytes);
+ }
+ _libssh2_htonu32(p - 4, bn_bytes); /* Post write bn size. */
+
+ return p + bn_bytes;
+}
+
+int
+_libssh2_rsa_new(libssh2_rsa_ctx ** rsa,
+ const unsigned char *edata,
+ unsigned long elen,
+ const unsigned char *ndata,
+ unsigned long nlen,
+ const unsigned char *ddata,
+ unsigned long dlen,
+ const unsigned char *pdata,
+ unsigned long plen,
+ const unsigned char *qdata,
+ unsigned long qlen,
+ const unsigned char *e1data,
+ unsigned long e1len,
+ const unsigned char *e2data,
+ unsigned long e2len,
+ const unsigned char *coeffdata, unsigned long coefflen)
+{
+ BIGNUM * e;
+ BIGNUM * n;
+ BIGNUM * d = 0;
+ BIGNUM * p = 0;
+ BIGNUM * q = 0;
+ BIGNUM * dmp1 = 0;
+ BIGNUM * dmq1 = 0;
+ BIGNUM * iqmp = 0;
+
+ e = BN_new();
+ BN_bin2bn(edata, elen, e);
+
+ n = BN_new();
+ BN_bin2bn(ndata, nlen, n);
+
+ if(ddata) {
+ d = BN_new();
+ BN_bin2bn(ddata, dlen, d);
+
+ p = BN_new();
+ BN_bin2bn(pdata, plen, p);
+
+ q = BN_new();
+ BN_bin2bn(qdata, qlen, q);
+
+ dmp1 = BN_new();
+ BN_bin2bn(e1data, e1len, dmp1);
+
+ dmq1 = BN_new();
+ BN_bin2bn(e2data, e2len, dmq1);
+
+ iqmp = BN_new();
+ BN_bin2bn(coeffdata, coefflen, iqmp);
+ }
+
+ *rsa = RSA_new();
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_set0_key(*rsa, n, e, d);
+#else
+ (*rsa)->e = e;
+ (*rsa)->n = n;
+ (*rsa)->d = d;
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_set0_factors(*rsa, p, q);
+#else
+ (*rsa)->p = p;
+ (*rsa)->q = q;
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_set0_crt_params(*rsa, dmp1, dmq1, iqmp);
+#else
+ (*rsa)->dmp1 = dmp1;
+ (*rsa)->dmq1 = dmq1;
+ (*rsa)->iqmp = iqmp;
+#endif
+ return 0;
+}
+
+int
+_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsactx,
+ const unsigned char *sig,
+ unsigned long sig_len,
+ const unsigned char *m, unsigned long m_len)
+{
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ int ret;
+
+ if(_libssh2_sha1(m, m_len, hash))
+ return -1; /* failure */
+ ret = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH,
+ (unsigned char *) sig, sig_len, rsactx);
+ return (ret == 1) ? 0 : -1;
+}
+
+#if LIBSSH2_DSA
+int
+_libssh2_dsa_new(libssh2_dsa_ctx ** dsactx,
+ const unsigned char *p,
+ unsigned long p_len,
+ const unsigned char *q,
+ unsigned long q_len,
+ const unsigned char *g,
+ unsigned long g_len,
+ const unsigned char *y,
+ unsigned long y_len,
+ const unsigned char *x, unsigned long x_len)
+{
+ BIGNUM * p_bn;
+ BIGNUM * q_bn;
+ BIGNUM * g_bn;
+ BIGNUM * pub_key;
+ BIGNUM * priv_key = NULL;
+
+ p_bn = BN_new();
+ BN_bin2bn(p, p_len, p_bn);
+
+ q_bn = BN_new();
+ BN_bin2bn(q, q_len, q_bn);
+
+ g_bn = BN_new();
+ BN_bin2bn(g, g_len, g_bn);
+
+ pub_key = BN_new();
+ BN_bin2bn(y, y_len, pub_key);
+
+ if(x_len) {
+ priv_key = BN_new();
+ BN_bin2bn(x, x_len, priv_key);
+ }
+
+ *dsactx = DSA_new();
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_set0_pqg(*dsactx, p_bn, q_bn, g_bn);
+#else
+ (*dsactx)->p = p_bn;
+ (*dsactx)->g = g_bn;
+ (*dsactx)->q = q_bn;
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_set0_key(*dsactx, pub_key, priv_key);
+#else
+ (*dsactx)->pub_key = pub_key;
+ (*dsactx)->priv_key = priv_key;
+#endif
+ return 0;
+}
+
+int
+_libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx,
+ const unsigned char *sig,
+ const unsigned char *m, unsigned long m_len)
+{
+ unsigned char hash[SHA_DIGEST_LENGTH];
+ DSA_SIG * dsasig;
+ BIGNUM * r;
+ BIGNUM * s;
+ int ret = -1;
+
+ r = BN_new();
+ BN_bin2bn(sig, 20, r);
+ s = BN_new();
+ BN_bin2bn(sig + 20, 20, s);
+
+ dsasig = DSA_SIG_new();
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_SIG_set0(dsasig, r, s);
+#else
+ dsasig->r = r;
+ dsasig->s = s;
+#endif
+ if(!_libssh2_sha1(m, m_len, hash))
+ /* _libssh2_sha1() succeeded */
+ ret = DSA_do_verify(hash, SHA_DIGEST_LENGTH, dsasig, dsactx);
+
+ DSA_SIG_free(dsasig);
+
+ return (ret == 1) ? 0 : -1;
+}
+#endif /* LIBSSH_DSA */
+
+#if LIBSSH2_ECDSA
+
+/* _libssh2_ecdsa_get_curve_type
+ *
+ * returns key curve type that maps to libssh2_curve_type
+ *
+ */
+
+libssh2_curve_type
+_libssh2_ecdsa_get_curve_type(libssh2_ecdsa_ctx *ec_ctx)
+{
+ const EC_GROUP *group = EC_KEY_get0_group(ec_ctx);
+ return EC_GROUP_get_curve_name(group);
+}
+
+/* _libssh2_ecdsa_curve_type_from_name
+ *
+ * returns 0 for success, key curve type that maps to libssh2_curve_type
+ *
+ */
+
+int
+_libssh2_ecdsa_curve_type_from_name(const char *name,
+ libssh2_curve_type *out_type)
+{
+ int ret = 0;
+ libssh2_curve_type type;
+
+ if(name == NULL || strlen(name) != 19)
+ return -1;
+
+ if(strcmp(name, "ecdsa-sha2-nistp256") == 0)
+ type = LIBSSH2_EC_CURVE_NISTP256;
+ else if(strcmp(name, "ecdsa-sha2-nistp384") == 0)
+ type = LIBSSH2_EC_CURVE_NISTP384;
+ else if(strcmp(name, "ecdsa-sha2-nistp521") == 0)
+ type = LIBSSH2_EC_CURVE_NISTP521;
+ else {
+ ret = -1;
+ }
+
+ if(ret == 0 && out_type) {
+ *out_type = type;
+ }
+
+ return ret;
+}
+
+/* _libssh2_ecdsa_curve_name_with_octal_new
+ *
+ * Creates a new public key given an octal string, length and type
+ *
+ */
+
+int
+_libssh2_ecdsa_curve_name_with_octal_new(libssh2_ecdsa_ctx ** ec_ctx,
+ const unsigned char *k,
+ size_t k_len, libssh2_curve_type curve)
+{
+
+ int ret = 0;
+ const EC_GROUP *ec_group = NULL;
+ EC_KEY *ec_key = EC_KEY_new_by_curve_name(curve);
+ EC_POINT *point = NULL;
+
+ if(ec_key) {
+ ec_group = EC_KEY_get0_group(ec_key);
+ point = EC_POINT_new(ec_group);
+ ret = EC_POINT_oct2point(ec_group, point, k, k_len, NULL);
+ ret = EC_KEY_set_public_key(ec_key, point);
+
+ if(point != NULL)
+ EC_POINT_free(point);
+
+ if(ec_ctx != NULL)
+ *ec_ctx = ec_key;
+ }
+
+ return (ret == 1) ? 0 : -1;
+}
+
+#define LIBSSH2_ECDSA_VERIFY(digest_type) \
+{ \
+ unsigned char hash[SHA##digest_type##_DIGEST_LENGTH]; \
+ libssh2_sha##digest_type(m, m_len, hash); \
+ ret = ECDSA_do_verify(hash, SHA##digest_type##_DIGEST_LENGTH, \
+ ecdsa_sig, ec_key); \
+ \
+}
+
+int
+_libssh2_ecdsa_verify(libssh2_ecdsa_ctx * ctx,
+ const unsigned char *r, size_t r_len,
+ const unsigned char *s, size_t s_len,
+ const unsigned char *m, size_t m_len)
+{
+ int ret = 0;
+ EC_KEY *ec_key = (EC_KEY*)ctx;
+ libssh2_curve_type type = _libssh2_ecdsa_get_curve_type(ec_key);
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ ECDSA_SIG *ecdsa_sig = ECDSA_SIG_new();
+ BIGNUM *pr = BN_new();
+ BIGNUM *ps = BN_new();
+
+ BN_bin2bn(r, r_len, pr);
+ BN_bin2bn(s, s_len, ps);
+ ECDSA_SIG_set0(ecdsa_sig, pr, ps);
+
+#else
+ ECDSA_SIG ecdsa_sig_;
+ ECDSA_SIG *ecdsa_sig = &ecdsa_sig_;
+ ecdsa_sig_.r = BN_new();
+ BN_bin2bn(r, r_len, ecdsa_sig_.r);
+ ecdsa_sig_.s = BN_new();
+ BN_bin2bn(s, s_len, ecdsa_sig_.s);
+#endif
+
+ if(type == LIBSSH2_EC_CURVE_NISTP256) {
+ LIBSSH2_ECDSA_VERIFY(256);
+ }
+ else if(type == LIBSSH2_EC_CURVE_NISTP384) {
+ LIBSSH2_ECDSA_VERIFY(384);
+ }
+ else if(type == LIBSSH2_EC_CURVE_NISTP521) {
+ LIBSSH2_ECDSA_VERIFY(512);
+ }
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ if(ecdsa_sig)
+ ECDSA_SIG_free(ecdsa_sig);
+#else
+ BN_clear_free(ecdsa_sig_.s);
+ BN_clear_free(ecdsa_sig_.r);
+#endif
+
+ return (ret == 1) ? 0 : -1;
+}
+
+#endif /* LIBSSH2_ECDSA */
+
+int
+_libssh2_cipher_init(_libssh2_cipher_ctx * h,
+ _libssh2_cipher_type(algo),
+ unsigned char *iv, unsigned char *secret, int encrypt)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ *h = EVP_CIPHER_CTX_new();
+ return !EVP_CipherInit(*h, algo(), secret, iv, encrypt);
+#else
+ EVP_CIPHER_CTX_init(h);
+ return !EVP_CipherInit(h, algo(), secret, iv, encrypt);
+#endif
+}
+
+int
+_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx,
+ _libssh2_cipher_type(algo),
+ int encrypt, unsigned char *block, size_t blocksize)
+{
+ unsigned char buf[EVP_MAX_BLOCK_LENGTH];
+ int ret;
+ (void) algo;
+ (void) encrypt;
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ ret = EVP_Cipher(*ctx, buf, block, blocksize);
+#else
+ ret = EVP_Cipher(ctx, buf, block, blocksize);
+#endif
+#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
+ if(ret != -1) {
+#else
+ if(ret == 1) {
+#endif
+ memcpy(block, buf, blocksize);
+ }
+
+#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3
+ return ret != -1 ? 0 : 1;
+#else
+ return ret == 1 ? 0 : 1;
+#endif
+}
+
+#if LIBSSH2_AES_CTR && !defined(HAVE_EVP_AES_128_CTR)
+
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+
+typedef struct
+{
+ AES_KEY key;
+ EVP_CIPHER_CTX *aes_ctx;
+ unsigned char ctr[AES_BLOCK_SIZE];
+} aes_ctr_ctx;
+
+static EVP_CIPHER * aes_128_ctr_cipher = NULL;
+static EVP_CIPHER * aes_192_ctr_cipher = NULL;
+static EVP_CIPHER * aes_256_ctr_cipher = NULL;
+
+static int
+aes_ctr_init(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+ const unsigned char *iv, int enc) /* init key */
+{
+ /*
+ * variable "c" is leaked from this scope, but is later freed
+ * in aes_ctr_cleanup
+ */
+ aes_ctr_ctx *c;
+ const EVP_CIPHER *aes_cipher;
+ (void) enc;
+
+ switch(EVP_CIPHER_CTX_key_length(ctx)) {
+ case 16:
+ aes_cipher = EVP_aes_128_ecb();
+ break;
+ case 24:
+ aes_cipher = EVP_aes_192_ecb();
+ break;
+ case 32:
+ aes_cipher = EVP_aes_256_ecb();
+ break;
+ default:
+ return 0;
+ }
+
+ c = malloc(sizeof(*c));
+ if(c == NULL)
+ return 0;
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ c->aes_ctx = EVP_CIPHER_CTX_new();
+#else
+ c->aes_ctx = malloc(sizeof(EVP_CIPHER_CTX));
+#endif
+ if(c->aes_ctx == NULL) {
+ free(c);
+ return 0;
+ }
+
+ if(EVP_EncryptInit(c->aes_ctx, aes_cipher, key, NULL) != 1) {
+#ifdef HAVE_OPAQUE_STRUCTS
+ EVP_CIPHER_CTX_free(c->aes_ctx);
+#else
+ free(c->aes_ctx);
+#endif
+ free(c);
+ return 0;
+ }
+
+ EVP_CIPHER_CTX_set_padding(c->aes_ctx, 0);
+
+ memcpy(c->ctr, iv, AES_BLOCK_SIZE);
+
+ EVP_CIPHER_CTX_set_app_data(ctx, c);
+
+ return 1;
+}
+
+static int
+aes_ctr_do_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in,
+ size_t inl) /* encrypt/decrypt data */
+{
+ aes_ctr_ctx *c = EVP_CIPHER_CTX_get_app_data(ctx);
+ unsigned char b1[AES_BLOCK_SIZE];
+ int outlen = 0;
+
+ if(inl != 16) /* libssh2 only ever encrypt one block */
+ return 0;
+
+ if(c == NULL) {
+ return 0;
+ }
+
+/*
+ To encrypt a packet P=P1||P2||...||Pn (where P1, P2, ..., Pn are each
+ blocks of length L), the encryptor first encrypts <X> with <cipher>
+ to obtain a block B1. The block B1 is then XORed with P1 to generate
+ the ciphertext block C1. The counter X is then incremented
+*/
+
+ if(EVP_EncryptUpdate(c->aes_ctx, b1, &outlen,
+ c->ctr, AES_BLOCK_SIZE) != 1) {
+ return 0;
+ }
+
+ _libssh2_xor_data(out, in, b1, AES_BLOCK_SIZE);
+ _libssh2_aes_ctr_increment(c->ctr, AES_BLOCK_SIZE);
+
+ return 1;
+}
+
+static int
+aes_ctr_cleanup(EVP_CIPHER_CTX *ctx) /* cleanup ctx */
+{
+ aes_ctr_ctx *c = EVP_CIPHER_CTX_get_app_data(ctx);
+
+ if(c == NULL) {
+ return 1;
+ }
+
+ if(c->aes_ctx != NULL) {
+#ifdef HAVE_OPAQUE_STRUCTS
+ EVP_CIPHER_CTX_free(c->aes_ctx);
+#else
+ _libssh2_cipher_dtor(c->aes_ctx);
+ free(c->aes_ctx);
+#endif
+ }
+
+ free(c);
+
+ return 1;
+}
+
+static const EVP_CIPHER *
+make_ctr_evp (size_t keylen, EVP_CIPHER **aes_ctr_cipher, int type)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ *aes_ctr_cipher = EVP_CIPHER_meth_new(type, 16, keylen);
+ if(*aes_ctr_cipher) {
+ EVP_CIPHER_meth_set_iv_length(*aes_ctr_cipher, 16);
+ EVP_CIPHER_meth_set_init(*aes_ctr_cipher, aes_ctr_init);
+ EVP_CIPHER_meth_set_do_cipher(*aes_ctr_cipher, aes_ctr_do_cipher);
+ EVP_CIPHER_meth_set_cleanup(*aes_ctr_cipher, aes_ctr_cleanup);
+ }
+#else
+ (*aes_ctr_cipher)->nid = type;
+ (*aes_ctr_cipher)->block_size = 16;
+ (*aes_ctr_cipher)->key_len = keylen;
+ (*aes_ctr_cipher)->iv_len = 16;
+ (*aes_ctr_cipher)->init = aes_ctr_init;
+ (*aes_ctr_cipher)->do_cipher = aes_ctr_do_cipher;
+ (*aes_ctr_cipher)->cleanup = aes_ctr_cleanup;
+#endif
+
+ return *aes_ctr_cipher;
+}
+
+const EVP_CIPHER *
+_libssh2_EVP_aes_128_ctr(void)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ return !aes_128_ctr_cipher ?
+ make_ctr_evp(16, &aes_128_ctr_cipher, NID_aes_128_ctr) :
+ aes_128_ctr_cipher;
+#else
+ static EVP_CIPHER aes_ctr_cipher;
+ if(!aes_128_ctr_cipher) {
+ aes_128_ctr_cipher = &aes_ctr_cipher;
+ make_ctr_evp(16, &aes_128_ctr_cipher, 0);
+ }
+ return aes_128_ctr_cipher;
+#endif
+}
+
+const EVP_CIPHER *
+_libssh2_EVP_aes_192_ctr(void)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ return !aes_192_ctr_cipher ?
+ make_ctr_evp(24, &aes_192_ctr_cipher, NID_aes_192_ctr) :
+ aes_192_ctr_cipher;
+#else
+ static EVP_CIPHER aes_ctr_cipher;
+ if(!aes_192_ctr_cipher) {
+ aes_192_ctr_cipher = &aes_ctr_cipher;
+ make_ctr_evp(24, &aes_192_ctr_cipher, 0);
+ }
+ return aes_192_ctr_cipher;
+#endif
+}
+
+const EVP_CIPHER *
+_libssh2_EVP_aes_256_ctr(void)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ return !aes_256_ctr_cipher ?
+ make_ctr_evp(32, &aes_256_ctr_cipher, NID_aes_256_ctr) :
+ aes_256_ctr_cipher;
+#else
+ static EVP_CIPHER aes_ctr_cipher;
+ if(!aes_256_ctr_cipher) {
+ aes_256_ctr_cipher = &aes_ctr_cipher;
+ make_ctr_evp(32, &aes_256_ctr_cipher, 0);
+ }
+ return aes_256_ctr_cipher;
+#endif
+}
+
+#endif /* LIBSSH2_AES_CTR && !defined(HAVE_EVP_AES_128_CTR) */
+
+void _libssh2_openssl_crypto_init(void)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE_load_builtin_engines();
+ ENGINE_register_all_complete();
+#endif
+#else
+ OpenSSL_add_all_algorithms();
+ OpenSSL_add_all_ciphers();
+ OpenSSL_add_all_digests();
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE_load_builtin_engines();
+ ENGINE_register_all_complete();
+#endif
+#endif
+#if LIBSSH2_AES_CTR && !defined(HAVE_EVP_AES_128_CTR)
+ aes_128_ctr_cipher = (EVP_CIPHER *) _libssh2_EVP_aes_128_ctr();
+ aes_192_ctr_cipher = (EVP_CIPHER *) _libssh2_EVP_aes_192_ctr();
+ aes_256_ctr_cipher = (EVP_CIPHER *) _libssh2_EVP_aes_256_ctr();
+#endif
+}
+
+void _libssh2_openssl_crypto_exit(void)
+{
+#if LIBSSH2_AES_CTR && !defined(HAVE_EVP_AES_128_CTR)
+#ifdef HAVE_OPAQUE_STRUCTS
+ if(aes_128_ctr_cipher) {
+ EVP_CIPHER_meth_free(aes_128_ctr_cipher);
+ }
+
+ if(aes_192_ctr_cipher) {
+ EVP_CIPHER_meth_free(aes_192_ctr_cipher);
+ }
+
+ if(aes_256_ctr_cipher) {
+ EVP_CIPHER_meth_free(aes_256_ctr_cipher);
+ }
+#endif
+
+ aes_128_ctr_cipher = NULL;
+ aes_192_ctr_cipher = NULL;
+ aes_256_ctr_cipher = NULL;
+#endif
+}
+
+/* TODO: Optionally call a passphrase callback specified by the
+ * calling program
+ */
+static int
+passphrase_cb(char *buf, int size, int rwflag, char *passphrase)
+{
+ int passphrase_len = strlen(passphrase);
+ (void) rwflag;
+
+ if(passphrase_len > (size - 1)) {
+ passphrase_len = size - 1;
+ }
+ memcpy(buf, passphrase, passphrase_len);
+ buf[passphrase_len] = '\0';
+
+ return passphrase_len;
+}
+
+typedef void * (*pem_read_bio_func)(BIO *, void **, pem_password_cb *,
+ void *u);
+
+static int
+read_private_key_from_memory(void **key_ctx,
+ pem_read_bio_func read_private_key,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ BIO * bp;
+
+ *key_ctx = NULL;
+
+ bp = BIO_new_mem_buf((char *)filedata, filedata_len);
+ if(!bp) {
+ return -1;
+ }
+ *key_ctx = read_private_key(bp, NULL, (pem_password_cb *) passphrase_cb,
+ (void *) passphrase);
+
+ BIO_free(bp);
+ return (*key_ctx) ? 0 : -1;
+}
+
+
+
+static int
+read_private_key_from_file(void **key_ctx,
+ pem_read_bio_func read_private_key,
+ const char *filename,
+ unsigned const char *passphrase)
+{
+ BIO * bp;
+
+ *key_ctx = NULL;
+
+ bp = BIO_new_file(filename, "r");
+ if(!bp) {
+ return -1;
+ }
+
+ *key_ctx = read_private_key(bp, NULL, (pem_password_cb *) passphrase_cb,
+ (void *) passphrase);
+
+ BIO_free(bp);
+ return (*key_ctx) ? 0 : -1;
+}
+
+int
+_libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filedata, size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ int rc;
+
+ pem_read_bio_func read_rsa =
+ (pem_read_bio_func) &PEM_read_bio_RSAPrivateKey;
+
+ _libssh2_init_if_needed();
+
+ rc = read_private_key_from_memory((void **) rsa, read_rsa,
+ filedata, filedata_len, passphrase);
+
+ if(rc) {
+ rc = read_openssh_private_key_from_memory((void **)rsa, session,
+ "ssh-rsa", filedata, filedata_len, passphrase);
+ }
+
+return rc;
+}
+
+static unsigned char *
+gen_publickey_from_rsa(LIBSSH2_SESSION *session, RSA *rsa,
+ size_t *key_len)
+{
+ int e_bytes, n_bytes;
+ unsigned long len;
+ unsigned char *key;
+ unsigned char *p;
+ const BIGNUM * e;
+ const BIGNUM * n;
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_get0_key(rsa, &n, &e, NULL);
+#else
+ e = rsa->e;
+ n = rsa->n;
+#endif
+ e_bytes = BN_num_bytes(e) + 1;
+ n_bytes = BN_num_bytes(n) + 1;
+
+ /* Key form is "ssh-rsa" + e + n. */
+ len = 4 + 7 + 4 + e_bytes + 4 + n_bytes;
+
+ key = LIBSSH2_ALLOC(session, len);
+ if(key == NULL) {
+ return NULL;
+ }
+
+ /* Process key encoding. */
+ p = key;
+
+ _libssh2_htonu32(p, 7); /* Key type. */
+ p += 4;
+ memcpy(p, "ssh-rsa", 7);
+ p += 7;
+
+ p = write_bn(p, e, e_bytes);
+ p = write_bn(p, n, n_bytes);
+
+ *key_len = (size_t)(p - key);
+ return key;
+}
+
+static int
+gen_publickey_from_rsa_evp(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ EVP_PKEY *pk)
+{
+ RSA* rsa = NULL;
+ unsigned char *key;
+ unsigned char *method_buf = NULL;
+ size_t key_len;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing public key from RSA private key envelope");
+
+ rsa = EVP_PKEY_get1_RSA(pk);
+ if(rsa == NULL) {
+ /* Assume memory allocation error... what else could it be ? */
+ goto __alloc_error;
+ }
+
+ method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-rsa. */
+ if(method_buf == NULL) {
+ goto __alloc_error;
+ }
+
+ key = gen_publickey_from_rsa(session, rsa, &key_len);
+ if(key == NULL) {
+ goto __alloc_error;
+ }
+ RSA_free(rsa);
+
+ memcpy(method_buf, "ssh-rsa", 7);
+ *method = method_buf;
+ *method_len = 7;
+ *pubkeydata = key;
+ *pubkeydata_len = key_len;
+ return 0;
+
+ __alloc_error:
+ if(rsa != NULL) {
+ RSA_free(rsa);
+ }
+ if(method_buf != NULL) {
+ LIBSSH2_FREE(session, method_buf);
+ }
+
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for private key data");
+}
+
+static int _libssh2_rsa_new_additional_parameters(RSA *rsa)
+{
+ BN_CTX *ctx = NULL;
+ BIGNUM *aux = NULL;
+ BIGNUM *dmp1 = NULL;
+ BIGNUM *dmq1 = NULL;
+ const BIGNUM *p = NULL;
+ const BIGNUM *q = NULL;
+ const BIGNUM *d = NULL;
+ int rc = 0;
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_get0_key(rsa, NULL, NULL, &d);
+ RSA_get0_factors(rsa, &p, &q);
+#else
+ d = (*rsa).d;
+ p = (*rsa).p;
+ q = (*rsa).q;
+#endif
+
+ ctx = BN_CTX_new();
+ if(ctx == NULL)
+ return -1;
+
+ aux = BN_new();
+ if(aux == NULL) {
+ rc = -1;
+ goto out;
+ }
+
+ dmp1 = BN_new();
+ if(dmp1 == NULL) {
+ rc = -1;
+ goto out;
+ }
+
+ dmq1 = BN_new();
+ if(dmq1 == NULL) {
+ rc = -1;
+ goto out;
+ }
+
+ if((BN_sub(aux, q, BN_value_one()) == 0) ||
+ (BN_mod(dmq1, d, aux, ctx) == 0) ||
+ (BN_sub(aux, p, BN_value_one()) == 0) ||
+ (BN_mod(dmp1, d, aux, ctx) == 0)) {
+ rc = -1;
+ goto out;
+ }
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_set0_crt_params(rsa, dmp1, dmq1, NULL);
+#else
+ (*rsa).dmp1 = dmp1;
+ (*rsa).dmq1 = dmq1;
+#endif
+
+out:
+ if(aux)
+ BN_clear_free(aux);
+ BN_CTX_free(ctx);
+
+ if(rc != 0) {
+ if(dmp1)
+ BN_clear_free(dmp1);
+ if(dmq1)
+ BN_clear_free(dmq1);
+ }
+
+ return rc;
+}
+
+static int
+gen_publickey_from_rsa_openssh_priv_data(LIBSSH2_SESSION *session,
+ struct string_buf *decrypted,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ libssh2_rsa_ctx **rsa_ctx)
+{
+ int rc = 0;
+ size_t nlen, elen, dlen, plen, qlen, coefflen, commentlen;
+ unsigned char *n, *e, *d, *p, *q, *coeff, *comment;
+ RSA *rsa = NULL;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing RSA keys from private key data");
+
+ /* public key data */
+ if(_libssh2_get_bignum_bytes(decrypted, &n, &nlen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "RSA no n");
+ return -1;
+ }
+
+ if(_libssh2_get_bignum_bytes(decrypted, &e, &elen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "RSA no e");
+ return -1;
+ }
+
+ /* private key data */
+ if(_libssh2_get_bignum_bytes(decrypted, &d, &dlen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "RSA no d");
+ return -1;
+ }
+
+ if(_libssh2_get_bignum_bytes(decrypted, &coeff, &coefflen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "RSA no coeff");
+ return -1;
+ }
+
+ if(_libssh2_get_bignum_bytes(decrypted, &p, &plen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "RSA no p");
+ return -1;
+ }
+
+ if(_libssh2_get_bignum_bytes(decrypted, &q, &qlen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "RSA no q");
+ return -1;
+ }
+
+ if(_libssh2_get_string(decrypted, &comment, &commentlen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "RSA no comment");
+ return -1;
+ }
+
+ if((rc = _libssh2_rsa_new(&rsa, e, elen, n, nlen, d, dlen, p, plen,
+ q, qlen, NULL, 0, NULL, 0,
+ coeff, coefflen)) != 0) {
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Could not create RSA private key");
+ goto fail;
+ }
+
+ if(rsa != NULL)
+ rc = _libssh2_rsa_new_additional_parameters(rsa);
+
+ if(rsa != NULL && pubkeydata != NULL && method != NULL) {
+ EVP_PKEY *pk = EVP_PKEY_new();
+ EVP_PKEY_set1_RSA(pk, rsa);
+
+ rc = gen_publickey_from_rsa_evp(session, method, method_len,
+ pubkeydata, pubkeydata_len,
+ pk);
+
+ if(pk)
+ EVP_PKEY_free(pk);
+ }
+
+ if(rsa_ctx != NULL)
+ *rsa_ctx = rsa;
+ else
+ RSA_free(rsa);
+
+ return rc;
+
+fail:
+
+ if(rsa != NULL)
+ RSA_free(rsa);
+
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for private key data");
+}
+
+static int
+_libssh2_rsa_new_openssh_private(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filename,
+ unsigned const char *passphrase)
+{
+ FILE *fp;
+ int rc;
+ unsigned char *buf = NULL;
+ struct string_buf *decrypted = NULL;
+
+ if(session == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Session is required");
+ return -1;
+ }
+
+ _libssh2_init_if_needed();
+
+ fp = fopen(filename, "r");
+ if(!fp) {
+ _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to open OpenSSH RSA private key file");
+ return -1;
+ }
+
+ rc = _libssh2_openssh_pem_parse(session, passphrase, fp, &decrypted);
+ fclose(fp);
+ if(rc) {
+ return rc;
+ }
+
+ /* We have a new key file, now try and parse it using supported types */
+ rc = _libssh2_get_string(decrypted, &buf, NULL);
+
+ if(rc != 0 || buf == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Public key type in decrypted key data not found");
+ return -1;
+ }
+
+ if(strcmp("ssh-rsa", (const char *)buf) == 0) {
+ rc = gen_publickey_from_rsa_openssh_priv_data(session, decrypted,
+ NULL, 0,
+ NULL, 0, rsa);
+ }
+ else {
+ rc = -1;
+ }
+
+ if(decrypted)
+ _libssh2_string_buf_free(session, decrypted);
+
+ return rc;
+}
+
+int
+_libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa,
+ LIBSSH2_SESSION * session,
+ const char *filename, unsigned const char *passphrase)
+{
+ int rc;
+
+ pem_read_bio_func read_rsa =
+ (pem_read_bio_func) &PEM_read_bio_RSAPrivateKey;
+
+ _libssh2_init_if_needed();
+
+ rc = read_private_key_from_file((void **) rsa, read_rsa,
+ filename, passphrase);
+
+ if(rc) {
+ rc = _libssh2_rsa_new_openssh_private(rsa, session,
+ filename, passphrase);
+ }
+
+ return rc;
+}
+
+#if LIBSSH2_DSA
+int
+_libssh2_dsa_new_private_frommemory(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filedata, size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ int rc;
+
+ pem_read_bio_func read_dsa =
+ (pem_read_bio_func) &PEM_read_bio_DSAPrivateKey;
+
+ _libssh2_init_if_needed();
+
+ rc = read_private_key_from_memory((void **)dsa, read_dsa,
+ filedata, filedata_len, passphrase);
+
+ if(rc) {
+ rc = read_openssh_private_key_from_memory((void **)dsa, session,
+ "ssh-dsa", filedata, filedata_len, passphrase);
+ }
+
+ return rc;
+}
+
+static unsigned char *
+gen_publickey_from_dsa(LIBSSH2_SESSION* session, DSA *dsa,
+ size_t *key_len)
+{
+ int p_bytes, q_bytes, g_bytes, k_bytes;
+ unsigned long len;
+ unsigned char *key;
+ unsigned char *p;
+
+ const BIGNUM * p_bn;
+ const BIGNUM * q;
+ const BIGNUM * g;
+ const BIGNUM * pub_key;
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_get0_pqg(dsa, &p_bn, &q, &g);
+#else
+ p_bn = dsa->p;
+ q = dsa->q;
+ g = dsa->g;
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_get0_key(dsa, &pub_key, NULL);
+#else
+ pub_key = dsa->pub_key;
+#endif
+ p_bytes = BN_num_bytes(p_bn) + 1;
+ q_bytes = BN_num_bytes(q) + 1;
+ g_bytes = BN_num_bytes(g) + 1;
+ k_bytes = BN_num_bytes(pub_key) + 1;
+
+ /* Key form is "ssh-dss" + p + q + g + pub_key. */
+ len = 4 + 7 + 4 + p_bytes + 4 + q_bytes + 4 + g_bytes + 4 + k_bytes;
+
+ key = LIBSSH2_ALLOC(session, len);
+ if(key == NULL) {
+ return NULL;
+ }
+
+ /* Process key encoding. */
+ p = key;
+
+ _libssh2_htonu32(p, 7); /* Key type. */
+ p += 4;
+ memcpy(p, "ssh-dss", 7);
+ p += 7;
+
+ p = write_bn(p, p_bn, p_bytes);
+ p = write_bn(p, q, q_bytes);
+ p = write_bn(p, g, g_bytes);
+ p = write_bn(p, pub_key, k_bytes);
+
+ *key_len = (size_t)(p - key);
+ return key;
+}
+
+static int
+gen_publickey_from_dsa_evp(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ EVP_PKEY *pk)
+{
+ DSA* dsa = NULL;
+ unsigned char *key;
+ unsigned char *method_buf = NULL;
+ size_t key_len;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing public key from DSA private key envelope");
+
+ dsa = EVP_PKEY_get1_DSA(pk);
+ if(dsa == NULL) {
+ /* Assume memory allocation error... what else could it be ? */
+ goto __alloc_error;
+ }
+
+ method_buf = LIBSSH2_ALLOC(session, 7); /* ssh-dss. */
+ if(method_buf == NULL) {
+ goto __alloc_error;
+ }
+
+ key = gen_publickey_from_dsa(session, dsa, &key_len);
+ if(key == NULL) {
+ goto __alloc_error;
+ }
+ DSA_free(dsa);
+
+ memcpy(method_buf, "ssh-dss", 7);
+ *method = method_buf;
+ *method_len = 7;
+ *pubkeydata = key;
+ *pubkeydata_len = key_len;
+ return 0;
+
+ __alloc_error:
+ if(dsa != NULL) {
+ DSA_free(dsa);
+ }
+ if(method_buf != NULL) {
+ LIBSSH2_FREE(session, method_buf);
+ }
+
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for private key data");
+}
+
+static int
+gen_publickey_from_dsa_openssh_priv_data(LIBSSH2_SESSION *session,
+ struct string_buf *decrypted,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ libssh2_dsa_ctx **dsa_ctx)
+{
+ int rc = 0;
+ size_t plen, qlen, glen, pub_len, priv_len;
+ unsigned char *p, *q, *g, *pub_key, *priv_key;
+ DSA *dsa = NULL;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing DSA keys from private key data");
+
+ if(_libssh2_get_bignum_bytes(decrypted, &p, &plen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "DSA no p");
+ return -1;
+ }
+
+ if(_libssh2_get_bignum_bytes(decrypted, &q, &qlen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "DSA no q");
+ return -1;
+ }
+
+ if(_libssh2_get_bignum_bytes(decrypted, &g, &glen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "DSA no g");
+ return -1;
+ }
+
+ if(_libssh2_get_bignum_bytes(decrypted, &pub_key, &pub_len)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "DSA no public key");
+ return -1;
+ }
+
+ if(_libssh2_get_bignum_bytes(decrypted, &priv_key, &priv_len)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "DSA no private key");
+ return -1;
+ }
+
+ rc = _libssh2_dsa_new(&dsa, p, plen, q, qlen, g, glen, pub_key, pub_len,
+ priv_key, priv_len);
+ if(rc != 0) {
+ _libssh2_debug(session,
+ LIBSSH2_ERROR_PROTO,
+ "Could not create DSA private key");
+ goto fail;
+ }
+
+ if(dsa != NULL && pubkeydata != NULL && method != NULL) {
+ EVP_PKEY *pk = EVP_PKEY_new();
+ EVP_PKEY_set1_DSA(pk, dsa);
+
+ rc = gen_publickey_from_dsa_evp(session, method, method_len,
+ pubkeydata, pubkeydata_len,
+ pk);
+
+ if(pk)
+ EVP_PKEY_free(pk);
+ }
+
+ if(dsa_ctx != NULL)
+ *dsa_ctx = dsa;
+ else
+ DSA_free(dsa);
+
+ return rc;
+
+fail:
+
+ if(dsa != NULL)
+ DSA_free(dsa);
+
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for private key data");
+}
+
+static int
+_libssh2_dsa_new_openssh_private(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filename,
+ unsigned const char *passphrase)
+{
+ FILE *fp;
+ int rc;
+ unsigned char *buf = NULL;
+ struct string_buf *decrypted = NULL;
+
+ if(session == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Session is required");
+ return -1;
+ }
+
+ _libssh2_init_if_needed();
+
+ fp = fopen(filename, "r");
+ if(!fp) {
+ _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to open OpenSSH DSA private key file");
+ return -1;
+ }
+
+ rc = _libssh2_openssh_pem_parse(session, passphrase, fp, &decrypted);
+ fclose(fp);
+ if(rc) {
+ return rc;
+ }
+
+ /* We have a new key file, now try and parse it using supported types */
+ rc = _libssh2_get_string(decrypted, &buf, NULL);
+
+ if(rc != 0 || buf == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Public key type in decrypted key data not found");
+ return -1;
+ }
+
+ if(strcmp("ssh-dss", (const char *)buf) == 0) {
+ rc = gen_publickey_from_dsa_openssh_priv_data(session, decrypted,
+ NULL, 0,
+ NULL, 0, dsa);
+ }
+ else {
+ rc = -1;
+ }
+
+ if(decrypted)
+ _libssh2_string_buf_free(session, decrypted);
+
+ return rc;
+}
+
+int
+_libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa,
+ LIBSSH2_SESSION * session,
+ const char *filename, unsigned const char *passphrase)
+{
+ int rc;
+
+ pem_read_bio_func read_dsa =
+ (pem_read_bio_func) &PEM_read_bio_DSAPrivateKey;
+
+ _libssh2_init_if_needed();
+
+ rc = read_private_key_from_file((void **) dsa, read_dsa,
+ filename, passphrase);
+
+ if(rc) {
+ rc = _libssh2_dsa_new_openssh_private(dsa, session,
+ filename, passphrase);
+ }
+
+ return rc;
+}
+
+#endif /* LIBSSH_DSA */
+
+#if LIBSSH2_ECDSA
+
+int
+_libssh2_ecdsa_new_private_frommemory(libssh2_ecdsa_ctx ** ec_ctx,
+ LIBSSH2_SESSION * session,
+ const char *filedata, size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ int rc;
+
+ pem_read_bio_func read_ec =
+ (pem_read_bio_func) &PEM_read_bio_ECPrivateKey;
+
+ _libssh2_init_if_needed();
+
+ rc = read_private_key_from_memory((void **) ec_ctx, read_ec,
+ filedata, filedata_len, passphrase);
+
+ if(rc) {
+ rc = read_openssh_private_key_from_memory((void **)ec_ctx, session,
+ "ssh-ecdsa", filedata,
+ filedata_len, passphrase);
+ }
+
+ return rc;
+}
+
+#endif /* LIBSSH2_ECDSA */
+
+
+#if LIBSSH2_ED25519
+
+int
+_libssh2_curve25519_new(LIBSSH2_SESSION *session,
+ unsigned char **out_public_key,
+ unsigned char **out_private_key)
+{
+ EVP_PKEY *key = NULL;
+ EVP_PKEY_CTX *pctx = NULL;
+ unsigned char *priv = NULL, *pub = NULL;
+ size_t privLen, pubLen;
+ int rc = -1;
+
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
+ if(pctx == NULL)
+ return -1;
+
+ if(EVP_PKEY_keygen_init(pctx) != 1 ||
+ EVP_PKEY_keygen(pctx, &key) != 1) {
+ goto cleanExit;
+ }
+
+ if(out_private_key != NULL) {
+ privLen = LIBSSH2_ED25519_KEY_LEN;
+ priv = LIBSSH2_ALLOC(session, privLen);
+ if(priv == NULL)
+ goto cleanExit;
+
+ if(EVP_PKEY_get_raw_private_key(key, priv, &privLen) != 1 ||
+ privLen != LIBSSH2_ED25519_KEY_LEN) {
+ goto cleanExit;
+ }
+
+ *out_private_key = priv;
+ priv = NULL;
+ }
+
+ if(out_public_key != NULL) {
+ pubLen = LIBSSH2_ED25519_KEY_LEN;
+ pub = LIBSSH2_ALLOC(session, pubLen);
+ if(pub == NULL)
+ goto cleanExit;
+
+ if(EVP_PKEY_get_raw_public_key(key, pub, &pubLen) != 1 ||
+ pubLen != LIBSSH2_ED25519_KEY_LEN) {
+ goto cleanExit;
+ }
+
+ *out_public_key = pub;
+ pub = NULL;
+ }
+
+ /* success */
+ rc = 0;
+
+cleanExit:
+
+ if(pctx)
+ EVP_PKEY_CTX_free(pctx);
+ if(key)
+ EVP_PKEY_free(key);
+ if(priv)
+ LIBSSH2_FREE(session, priv);
+ if(pub)
+ LIBSSH2_FREE(session, pub);
+
+ return rc;
+}
+
+
+static int
+gen_publickey_from_ed_evp(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ EVP_PKEY *pk)
+{
+ const char methodName[] = "ssh-ed25519";
+ unsigned char *methodBuf = NULL;
+ size_t rawKeyLen = 0;
+ unsigned char *keyBuf = NULL;
+ size_t bufLen = 0;
+ unsigned char *bufPos = NULL;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Computing public key from ED private key envelope");
+
+ methodBuf = LIBSSH2_ALLOC(session, sizeof(methodName) - 1);
+ if(!methodBuf) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for private key data");
+ goto fail;
+ }
+ memcpy(methodBuf, methodName, sizeof(methodName) - 1);
+
+ if(EVP_PKEY_get_raw_public_key(pk, NULL, &rawKeyLen) != 1) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "EVP_PKEY_get_raw_public_key failed");
+ goto fail;
+ }
+
+ /* Key form is: type_len(4) + type(11) + pub_key_len(4) + pub_key(32). */
+ bufLen = 4 + sizeof(methodName) - 1 + 4 + rawKeyLen;
+ bufPos = keyBuf = LIBSSH2_ALLOC(session, bufLen);
+ if(!keyBuf) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for private key data");
+ goto fail;
+ }
+
+ _libssh2_store_str(&bufPos, methodName, sizeof(methodName) - 1);
+ _libssh2_store_u32(&bufPos, rawKeyLen);
+
+ if(EVP_PKEY_get_raw_public_key(pk, bufPos, &rawKeyLen) != 1) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "EVP_PKEY_get_raw_public_key failed");
+ goto fail;
+ }
+
+ *method = methodBuf;
+ *method_len = sizeof(methodName) - 1;
+ *pubkeydata = keyBuf;
+ *pubkeydata_len = bufLen;
+ return 0;
+
+fail:
+ if(methodBuf)
+ LIBSSH2_FREE(session, methodBuf);
+ if(keyBuf)
+ LIBSSH2_FREE(session, keyBuf);
+ return -1;
+}
+
+
+static int
+gen_publickey_from_ed25519_openssh_priv_data(LIBSSH2_SESSION *session,
+ struct string_buf *decrypted,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ libssh2_ed25519_ctx **out_ctx)
+{
+ libssh2_ed25519_ctx *ctx = NULL;
+ unsigned char *method_buf = NULL;
+ unsigned char *key = NULL;
+ int i, ret = 0;
+ unsigned char *pub_key, *priv_key, *buf;
+ size_t key_len = 0, tmp_len = 0;
+ unsigned char *p;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing ED25519 keys from private key data");
+
+ if(_libssh2_get_string(decrypted, &pub_key, &tmp_len) ||
+ tmp_len != LIBSSH2_ED25519_KEY_LEN) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Wrong public key length");
+ return -1;
+ }
+
+ if(_libssh2_get_string(decrypted, &priv_key, &tmp_len) ||
+ tmp_len != LIBSSH2_ED25519_PRIVATE_KEY_LEN) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Wrong private key length");
+ ret = -1;
+ goto clean_exit;
+ }
+
+ /* first 32 bytes of priv_key is the private key, the last 32 bytes are
+ the public key */
+ ctx = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL,
+ (const unsigned char *)priv_key,
+ LIBSSH2_ED25519_KEY_LEN);
+
+ /* comment */
+ if(_libssh2_get_string(decrypted, &buf, &tmp_len)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unable to read comment");
+ ret = -1;
+ goto clean_exit;
+ }
+
+ if(tmp_len > 0) {
+ unsigned char *comment = LIBSSH2_CALLOC(session, tmp_len + 1);
+ if(comment != NULL) {
+ memcpy(comment, buf, tmp_len);
+ memcpy(comment + tmp_len, "\0", 1);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Key comment: %s",
+ comment);
+
+ LIBSSH2_FREE(session, comment);
+ }
+ }
+
+ /* Padding */
+ i = 1;
+ while(decrypted->dataptr < decrypted->data + decrypted->len) {
+ if(*decrypted->dataptr != i) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Wrong padding");
+ ret = -1;
+ goto clean_exit;
+ }
+ i++;
+ decrypted->dataptr++;
+ }
+
+ if(ret == 0) {
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing public key from ED25519 "
+ "private key envelope");
+
+ method_buf = LIBSSH2_ALLOC(session, 11); /* ssh-ed25519. */
+ if(method_buf == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for ED25519 key");
+ goto clean_exit;
+ }
+
+ /* Key form is: type_len(4) + type(11) + pub_key_len(4) +
+ pub_key(32). */
+ key_len = LIBSSH2_ED25519_KEY_LEN + 19;
+ key = LIBSSH2_CALLOC(session, key_len);
+ if(key == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for ED25519 key");
+ goto clean_exit;
+ }
+
+ p = key;
+
+ _libssh2_store_str(&p, "ssh-ed25519", 11);
+ _libssh2_store_str(&p, (const char *)pub_key, LIBSSH2_ED25519_KEY_LEN);
+
+ memcpy(method_buf, "ssh-ed25519", 11);
+
+ if(method != NULL)
+ *method = method_buf;
+ else
+ LIBSSH2_FREE(session, method_buf);
+
+ if(method_len != NULL)
+ *method_len = 11;
+
+ if(pubkeydata != NULL)
+ *pubkeydata = key;
+ else
+ LIBSSH2_FREE(session, key);
+
+ if(pubkeydata_len != NULL)
+ *pubkeydata_len = key_len;
+
+ if(out_ctx != NULL)
+ *out_ctx = ctx;
+ else if(ctx != NULL)
+ _libssh2_ed25519_free(ctx);
+
+ return 0;
+ }
+
+clean_exit:
+
+ if(ctx)
+ _libssh2_ed25519_free(ctx);
+
+ if(method_buf)
+ LIBSSH2_FREE(session, method_buf);
+
+ if(key)
+ LIBSSH2_FREE(session, key);
+
+ return -1;
+}
+
+int
+_libssh2_ed25519_new_private(libssh2_ed25519_ctx ** ed_ctx,
+ LIBSSH2_SESSION * session,
+ const char *filename, const uint8_t *passphrase)
+{
+ int rc;
+ FILE *fp;
+ unsigned char *buf;
+ struct string_buf *decrypted = NULL;
+ libssh2_ed25519_ctx *ctx = NULL;
+
+ if(session == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Session is required");
+ return -1;
+ }
+
+ _libssh2_init_if_needed();
+
+ fp = fopen(filename, "r");
+ if(!fp) {
+ _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to open ED25519 private key file");
+ return -1;
+ }
+
+ rc = _libssh2_openssh_pem_parse(session, passphrase, fp, &decrypted);
+ fclose(fp);
+ if(rc) {
+ return rc;
+ }
+
+ /* We have a new key file, now try and parse it using supported types */
+ rc = _libssh2_get_string(decrypted, &buf, NULL);
+
+ if(rc != 0 || buf == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Public key type in decrypted key data not found");
+ return -1;
+ }
+
+ if(strcmp("ssh-ed25519", (const char *)buf) == 0) {
+ rc = gen_publickey_from_ed25519_openssh_priv_data(session,
+ decrypted,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &ctx);
+ }
+ else {
+ rc = -1;
+ }
+
+ if(decrypted)
+ _libssh2_string_buf_free(session, decrypted);
+
+ if(rc == 0) {
+ if(ed_ctx != NULL)
+ *ed_ctx = ctx;
+ else if(ctx != NULL)
+ _libssh2_ed25519_free(ctx);
+ }
+
+ return rc;
+}
+
+int
+_libssh2_ed25519_new_private_frommemory(libssh2_ed25519_ctx ** ed_ctx,
+ LIBSSH2_SESSION * session,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ libssh2_ed25519_ctx *ctx = NULL;
+
+ _libssh2_init_if_needed();
+
+ if(read_private_key_from_memory((void **)&ctx,
+ (pem_read_bio_func)
+ &PEM_read_bio_PrivateKey,
+ filedata, filedata_len, passphrase) == 0) {
+ if(EVP_PKEY_id(ctx) != EVP_PKEY_ED25519) {
+ _libssh2_ed25519_free(ctx);
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Private key is not an ED25519 key");
+ }
+
+ *ed_ctx = ctx;
+ return 0;
+ }
+
+ return read_openssh_private_key_from_memory((void **)ed_ctx, session,
+ "ssh-ed25519",
+ filedata, filedata_len,
+ passphrase);
+}
+
+int
+_libssh2_ed25519_new_public(libssh2_ed25519_ctx ** ed_ctx,
+ LIBSSH2_SESSION * session,
+ const unsigned char *raw_pub_key,
+ const uint8_t key_len)
+{
+ libssh2_ed25519_ctx *ctx = NULL;
+
+ if(ed_ctx == NULL)
+ return -1;
+
+ ctx = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL,
+ raw_pub_key, key_len);
+ if(!ctx)
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "could not create ED25519 public key");
+
+ if(ed_ctx != NULL)
+ *ed_ctx = ctx;
+ else if(ctx)
+ _libssh2_ed25519_free(ctx);
+
+ return 0;
+}
+#endif /* LIBSSH2_ED25519 */
+
+
+int
+_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session,
+ libssh2_rsa_ctx * rsactx,
+ const unsigned char *hash,
+ size_t hash_len,
+ unsigned char **signature, size_t *signature_len)
+{
+ int ret;
+ unsigned char *sig;
+ unsigned int sig_len;
+
+ sig_len = RSA_size(rsactx);
+ sig = LIBSSH2_ALLOC(session, sig_len);
+
+ if(!sig) {
+ return -1;
+ }
+
+ ret = RSA_sign(NID_sha1, hash, hash_len, sig, &sig_len, rsactx);
+
+ if(!ret) {
+ LIBSSH2_FREE(session, sig);
+ return -1;
+ }
+
+ *signature = sig;
+ *signature_len = sig_len;
+
+ return 0;
+}
+
+#if LIBSSH2_DSA
+int
+_libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx,
+ const unsigned char *hash,
+ unsigned long hash_len, unsigned char *signature)
+{
+ DSA_SIG *sig;
+ const BIGNUM * r;
+ const BIGNUM * s;
+ int r_len, s_len;
+ (void) hash_len;
+
+ sig = DSA_do_sign(hash, SHA_DIGEST_LENGTH, dsactx);
+ if(!sig) {
+ return -1;
+ }
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ DSA_SIG_get0(sig, &r, &s);
+#else
+ r = sig->r;
+ s = sig->s;
+#endif
+ r_len = BN_num_bytes(r);
+ if(r_len < 1 || r_len > 20) {
+ DSA_SIG_free(sig);
+ return -1;
+ }
+ s_len = BN_num_bytes(s);
+ if(s_len < 1 || s_len > 20) {
+ DSA_SIG_free(sig);
+ return -1;
+ }
+
+ memset(signature, 0, 40);
+
+ BN_bn2bin(r, signature + (20 - r_len));
+ BN_bn2bin(s, signature + 20 + (20 - s_len));
+
+ DSA_SIG_free(sig);
+
+ return 0;
+}
+#endif /* LIBSSH_DSA */
+
+#if LIBSSH2_ECDSA
+
+int
+_libssh2_ecdsa_sign(LIBSSH2_SESSION * session, libssh2_ecdsa_ctx * ec_ctx,
+ const unsigned char *hash, unsigned long hash_len,
+ unsigned char **signature, size_t *signature_len)
+{
+ int r_len, s_len;
+ int rc = 0;
+ size_t out_buffer_len = 0;
+ unsigned char *sp;
+ const BIGNUM *pr = NULL, *ps = NULL;
+ unsigned char *temp_buffer = NULL;
+ unsigned char *out_buffer = NULL;
+
+ ECDSA_SIG *sig = ECDSA_do_sign(hash, hash_len, ec_ctx);
+ if(sig == NULL)
+ return -1;
+#ifdef HAVE_OPAQUE_STRUCTS
+ ECDSA_SIG_get0(sig, &pr, &ps);
+#else
+ pr = sig->r;
+ ps = sig->s;
+#endif
+
+ r_len = BN_num_bytes(pr) + 1;
+ s_len = BN_num_bytes(ps) + 1;
+
+ temp_buffer = malloc(r_len + s_len + 8);
+ if(temp_buffer == NULL) {
+ rc = -1;
+ goto clean_exit;
+ }
+
+ sp = temp_buffer;
+ sp = write_bn(sp, pr, r_len);
+ sp = write_bn(sp, ps, s_len);
+
+ out_buffer_len = (size_t)(sp - temp_buffer);
+
+ out_buffer = LIBSSH2_CALLOC(session, out_buffer_len);
+ if(out_buffer == NULL) {
+ rc = -1;
+ goto clean_exit;
+ }
+
+ memcpy(out_buffer, temp_buffer, out_buffer_len);
+
+ *signature = out_buffer;
+ *signature_len = out_buffer_len;
+
+clean_exit:
+
+ if(temp_buffer != NULL)
+ free(temp_buffer);
+
+ if(sig)
+ ECDSA_SIG_free(sig);
+
+ return rc;
+}
+#endif /* LIBSSH2_ECDSA */
+
+int
+_libssh2_sha1_init(libssh2_sha1_ctx *ctx)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ *ctx = EVP_MD_CTX_new();
+
+ if(*ctx == NULL)
+ return 0;
+
+ if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha1")))
+ return 1;
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+#else
+ EVP_MD_CTX_init(ctx);
+ return EVP_DigestInit(ctx, EVP_get_digestbyname("sha1"));
+#endif
+}
+
+int
+_libssh2_sha1(const unsigned char *message, unsigned long len,
+ unsigned char *out)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ EVP_MD_CTX * ctx = EVP_MD_CTX_new();
+
+ if(ctx == NULL)
+ return 1; /* error */
+
+ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha1"))) {
+ EVP_DigestUpdate(ctx, message, len);
+ EVP_DigestFinal(ctx, out, NULL);
+ EVP_MD_CTX_free(ctx);
+ return 0; /* success */
+ }
+ EVP_MD_CTX_free(ctx);
+#else
+ EVP_MD_CTX ctx;
+
+ EVP_MD_CTX_init(&ctx);
+ if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha1"))) {
+ EVP_DigestUpdate(&ctx, message, len);
+ EVP_DigestFinal(&ctx, out, NULL);
+ return 0; /* success */
+ }
+#endif
+ return 1; /* error */
+}
+
+int
+_libssh2_sha256_init(libssh2_sha256_ctx *ctx)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ *ctx = EVP_MD_CTX_new();
+
+ if(*ctx == NULL)
+ return 0;
+
+ if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha256")))
+ return 1;
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+#else
+ EVP_MD_CTX_init(ctx);
+ return EVP_DigestInit(ctx, EVP_get_digestbyname("sha256"));
+#endif
+}
+
+int
+_libssh2_sha256(const unsigned char *message, unsigned long len,
+ unsigned char *out)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ EVP_MD_CTX * ctx = EVP_MD_CTX_new();
+
+ if(ctx == NULL)
+ return 1; /* error */
+
+ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha256"))) {
+ EVP_DigestUpdate(ctx, message, len);
+ EVP_DigestFinal(ctx, out, NULL);
+ EVP_MD_CTX_free(ctx);
+ return 0; /* success */
+ }
+ EVP_MD_CTX_free(ctx);
+#else
+ EVP_MD_CTX ctx;
+
+ EVP_MD_CTX_init(&ctx);
+ if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha256"))) {
+ EVP_DigestUpdate(&ctx, message, len);
+ EVP_DigestFinal(&ctx, out, NULL);
+ return 0; /* success */
+ }
+#endif
+ return 1; /* error */
+}
+
+int
+_libssh2_sha384_init(libssh2_sha384_ctx *ctx)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ *ctx = EVP_MD_CTX_new();
+
+ if(*ctx == NULL)
+ return 0;
+
+ if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha384")))
+ return 1;
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+#else
+ EVP_MD_CTX_init(ctx);
+ return EVP_DigestInit(ctx, EVP_get_digestbyname("sha384"));
+#endif
+}
+
+int
+_libssh2_sha384(const unsigned char *message, unsigned long len,
+ unsigned char *out)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ EVP_MD_CTX * ctx = EVP_MD_CTX_new();
+
+ if(ctx == NULL)
+ return 1; /* error */
+
+ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha384"))) {
+ EVP_DigestUpdate(ctx, message, len);
+ EVP_DigestFinal(ctx, out, NULL);
+ EVP_MD_CTX_free(ctx);
+ return 0; /* success */
+ }
+ EVP_MD_CTX_free(ctx);
+#else
+ EVP_MD_CTX ctx;
+
+ EVP_MD_CTX_init(&ctx);
+ if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha384"))) {
+ EVP_DigestUpdate(&ctx, message, len);
+ EVP_DigestFinal(&ctx, out, NULL);
+ return 0; /* success */
+ }
+#endif
+ return 1; /* error */
+}
+
+int
+_libssh2_sha512_init(libssh2_sha512_ctx *ctx)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ *ctx = EVP_MD_CTX_new();
+
+ if(*ctx == NULL)
+ return 0;
+
+ if(EVP_DigestInit(*ctx, EVP_get_digestbyname("sha512")))
+ return 1;
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+#else
+ EVP_MD_CTX_init(ctx);
+ return EVP_DigestInit(ctx, EVP_get_digestbyname("sha512"));
+#endif
+}
+
+int
+_libssh2_sha512(const unsigned char *message, unsigned long len,
+ unsigned char *out)
+{
+#ifdef HAVE_OPAQUE_STRUCTS
+ EVP_MD_CTX * ctx = EVP_MD_CTX_new();
+
+ if(ctx == NULL)
+ return 1; /* error */
+
+ if(EVP_DigestInit(ctx, EVP_get_digestbyname("sha512"))) {
+ EVP_DigestUpdate(ctx, message, len);
+ EVP_DigestFinal(ctx, out, NULL);
+ EVP_MD_CTX_free(ctx);
+ return 0; /* success */
+ }
+ EVP_MD_CTX_free(ctx);
+#else
+ EVP_MD_CTX ctx;
+
+ EVP_MD_CTX_init(&ctx);
+ if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha512"))) {
+ EVP_DigestUpdate(&ctx, message, len);
+ EVP_DigestFinal(&ctx, out, NULL);
+ return 0; /* success */
+ }
+#endif
+ return 1; /* error */
+}
+
+int
+_libssh2_md5_init(libssh2_md5_ctx *ctx)
+{
+ /* MD5 digest is not supported in OpenSSL FIPS mode
+ * Trying to init it will result in a latent OpenSSL error:
+ * "digital envelope routines:FIPS_DIGESTINIT:disabled for fips"
+ * So, just return 0 in FIPS mode
+ */
+#if OPENSSL_VERSION_NUMBER >= 0x000907000L && \
+ defined(OPENSSL_VERSION_MAJOR) && \
+ OPENSSL_VERSION_MAJOR < 3 && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+ if(FIPS_mode() != 0)
+ return 0;
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ *ctx = EVP_MD_CTX_new();
+
+ if(*ctx == NULL)
+ return 0;
+
+ if(EVP_DigestInit(*ctx, EVP_get_digestbyname("md5")))
+ return 1;
+
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+
+ return 0;
+#else
+ EVP_MD_CTX_init(ctx);
+ return EVP_DigestInit(ctx, EVP_get_digestbyname("md5"));
+#endif
+}
+
+#if LIBSSH2_ECDSA
+
+static int
+gen_publickey_from_ec_evp(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ EVP_PKEY *pk)
+{
+ int rc = 0;
+ EC_KEY *ec = NULL;
+ unsigned char *p;
+ unsigned char *method_buf = NULL;
+ unsigned char *key;
+ size_t key_len = 0;
+ unsigned char *octal_value = NULL;
+ size_t octal_len;
+ const EC_POINT *public_key;
+ const EC_GROUP *group;
+ BN_CTX *bn_ctx;
+ libssh2_curve_type type;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing public key from EC private key envelope");
+
+ bn_ctx = BN_CTX_new();
+ if(bn_ctx == NULL)
+ return -1;
+
+ ec = EVP_PKEY_get1_EC_KEY(pk);
+ if(ec == NULL) {
+ rc = -1;
+ goto clean_exit;
+ }
+
+ public_key = EC_KEY_get0_public_key(ec);
+ group = EC_KEY_get0_group(ec);
+ type = _libssh2_ecdsa_get_curve_type(ec);
+
+ method_buf = LIBSSH2_ALLOC(session, 19);
+ if(method_buf == NULL) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "out of memory");
+ }
+
+ if(type == LIBSSH2_EC_CURVE_NISTP256)
+ memcpy(method_buf, "ecdsa-sha2-nistp256", 19);
+ else if(type == LIBSSH2_EC_CURVE_NISTP384)
+ memcpy(method_buf, "ecdsa-sha2-nistp384", 19);
+ else if(type == LIBSSH2_EC_CURVE_NISTP521)
+ memcpy(method_buf, "ecdsa-sha2-nistp521", 19);
+ else {
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_ERROR,
+ "Unsupported EC private key type");
+ rc = -1;
+ goto clean_exit;
+ }
+
+ /* get length */
+ octal_len = EC_POINT_point2oct(group, public_key,
+ POINT_CONVERSION_UNCOMPRESSED,
+ NULL, 0, bn_ctx);
+ if(octal_len > EC_MAX_POINT_LEN) {
+ rc = -1;
+ goto clean_exit;
+ }
+
+ octal_value = malloc(octal_len);
+ if(octal_value == NULL) {
+ rc = -1;
+ goto clean_exit;
+ }
+
+ /* convert to octal */
+ if(EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED,
+ octal_value, octal_len, bn_ctx) != octal_len) {
+ rc = -1;
+ goto clean_exit;
+ }
+
+ /* Key form is: type_len(4) + type(19) + domain_len(4) + domain(8) +
+ pub_key_len(4) + pub_key(~65). */
+ key_len = 4 + 19 + 4 + 8 + 4 + octal_len;
+ key = LIBSSH2_ALLOC(session, key_len);
+ if(key == NULL) {
+ rc = -1;
+ goto clean_exit;
+ }
+
+ /* Process key encoding. */
+ p = key;
+
+ /* Key type */
+ _libssh2_store_str(&p, (const char *)method_buf, 19);
+
+ /* Name domain */
+ _libssh2_store_str(&p, (const char *)method_buf + 11, 8);
+
+ /* Public key */
+ _libssh2_store_str(&p, (const char *)octal_value, octal_len);
+
+ *method = method_buf;
+ *method_len = 19;
+ *pubkeydata = key;
+ *pubkeydata_len = key_len;
+
+clean_exit:
+
+ if(ec != NULL)
+ EC_KEY_free(ec);
+
+ if(bn_ctx != NULL) {
+ BN_CTX_free(bn_ctx);
+ }
+
+ if(octal_value != NULL)
+ free(octal_value);
+
+ if(rc == 0)
+ return 0;
+
+ if(method_buf != NULL)
+ LIBSSH2_FREE(session, method_buf);
+
+ return -1;
+}
+
+static int
+gen_publickey_from_ecdsa_openssh_priv_data(LIBSSH2_SESSION *session,
+ libssh2_curve_type curve_type,
+ struct string_buf *decrypted,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ libssh2_ecdsa_ctx **ec_ctx)
+{
+ int rc = 0;
+ size_t curvelen, exponentlen, pointlen;
+ unsigned char *curve, *exponent, *point_buf;
+ EC_KEY *ec_key = NULL;
+ BIGNUM *bn_exponent;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing ECDSA keys from private key data");
+
+ if(_libssh2_get_string(decrypted, &curve, &curvelen) ||
+ curvelen == 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "ECDSA no curve");
+ return -1;
+ }
+
+ if(_libssh2_get_string(decrypted, &point_buf, &pointlen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "ECDSA no point");
+ return -1;
+ }
+
+ if(_libssh2_get_bignum_bytes(decrypted, &exponent, &exponentlen)) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "ECDSA no exponent");
+ return -1;
+ }
+
+ if((rc = _libssh2_ecdsa_curve_name_with_octal_new(&ec_key, point_buf,
+ pointlen, curve_type)) != 0) {
+ rc = -1;
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "ECDSA could not create key");
+ goto fail;
+ }
+
+ bn_exponent = BN_new();
+ if(bn_exponent == NULL) {
+ rc = -1;
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for private key data");
+ goto fail;
+ }
+
+ BN_bin2bn(exponent, exponentlen, bn_exponent);
+ rc = (EC_KEY_set_private_key(ec_key, bn_exponent) != 1);
+
+ if(rc == 0 && ec_key != NULL && pubkeydata != NULL && method != NULL) {
+ EVP_PKEY *pk = EVP_PKEY_new();
+ EVP_PKEY_set1_EC_KEY(pk, ec_key);
+
+ rc = gen_publickey_from_ec_evp(session, method, method_len,
+ pubkeydata, pubkeydata_len,
+ pk);
+
+ if(pk)
+ EVP_PKEY_free(pk);
+ }
+
+ if(ec_ctx != NULL)
+ *ec_ctx = ec_key;
+ else
+ EC_KEY_free(ec_key);
+
+ return rc;
+
+fail:
+ if(ec_key != NULL)
+ EC_KEY_free(ec_key);
+
+ return rc;
+}
+
+static int
+_libssh2_ecdsa_new_openssh_private(libssh2_ecdsa_ctx ** ec_ctx,
+ LIBSSH2_SESSION * session,
+ const char *filename,
+ unsigned const char *passphrase)
+{
+ FILE *fp;
+ int rc;
+ unsigned char *buf = NULL;
+ libssh2_curve_type type;
+ struct string_buf *decrypted = NULL;
+
+ if(session == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Session is required");
+ return -1;
+ }
+
+ _libssh2_init_if_needed();
+
+ fp = fopen(filename, "r");
+ if(!fp) {
+ _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to open OpenSSH ECDSA private key file");
+ return -1;
+ }
+
+ rc = _libssh2_openssh_pem_parse(session, passphrase, fp, &decrypted);
+ fclose(fp);
+ if(rc) {
+ return rc;
+ }
+
+ /* We have a new key file, now try and parse it using supported types */
+ rc = _libssh2_get_string(decrypted, &buf, NULL);
+
+ if(rc != 0 || buf == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Public key type in decrypted key data not found");
+ return -1;
+ }
+
+ rc = _libssh2_ecdsa_curve_type_from_name((const char *)buf, &type);
+
+ if(rc == 0) {
+ rc = gen_publickey_from_ecdsa_openssh_priv_data(session, type,
+ decrypted, NULL, 0,
+ NULL, 0, ec_ctx);
+ }
+ else {
+ rc = -1;
+ }
+
+ if(decrypted)
+ _libssh2_string_buf_free(session, decrypted);
+
+ return rc;
+}
+
+int
+_libssh2_ecdsa_new_private(libssh2_ecdsa_ctx ** ec_ctx,
+ LIBSSH2_SESSION * session,
+ const char *filename, unsigned const char *passphrase)
+{
+ int rc;
+
+ pem_read_bio_func read_ec = (pem_read_bio_func) &PEM_read_bio_ECPrivateKey;
+
+ _libssh2_init_if_needed();
+
+ rc = read_private_key_from_file((void **) ec_ctx, read_ec,
+ filename, passphrase);
+
+ if(rc) {
+ return _libssh2_ecdsa_new_openssh_private(ec_ctx, session,
+ filename, passphrase);
+ }
+
+ return rc;
+}
+
+/*
+ * _libssh2_ecdsa_create_key
+ *
+ * Creates a local private key based on input curve
+ * and returns octal value and octal length
+ *
+ */
+
+int
+_libssh2_ecdsa_create_key(LIBSSH2_SESSION *session,
+ _libssh2_ec_key **out_private_key,
+ unsigned char **out_public_key_octal,
+ size_t *out_public_key_octal_len,
+ libssh2_curve_type curve_type)
+{
+ int ret = 1;
+ size_t octal_len = 0;
+ unsigned char octal_value[EC_MAX_POINT_LEN];
+ const EC_POINT *public_key = NULL;
+ EC_KEY *private_key = NULL;
+ const EC_GROUP *group = NULL;
+
+ /* create key */
+ BN_CTX *bn_ctx = BN_CTX_new();
+ if(!bn_ctx)
+ return -1;
+
+ private_key = EC_KEY_new_by_curve_name(curve_type);
+ group = EC_KEY_get0_group(private_key);
+
+ EC_KEY_generate_key(private_key);
+ public_key = EC_KEY_get0_public_key(private_key);
+
+ /* get length */
+ octal_len = EC_POINT_point2oct(group, public_key,
+ POINT_CONVERSION_UNCOMPRESSED,
+ NULL, 0, bn_ctx);
+ if(octal_len > EC_MAX_POINT_LEN) {
+ ret = -1;
+ goto clean_exit;
+ }
+
+ /* convert to octal */
+ if(EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED,
+ octal_value, octal_len, bn_ctx) != octal_len) {
+ ret = -1;
+ goto clean_exit;
+ }
+
+ if(out_private_key != NULL)
+ *out_private_key = private_key;
+
+ if(out_public_key_octal) {
+ *out_public_key_octal = LIBSSH2_ALLOC(session, octal_len);
+ if(*out_public_key_octal == NULL) {
+ ret = -1;
+ goto clean_exit;
+ }
+
+ memcpy(*out_public_key_octal, octal_value, octal_len);
+ }
+
+ if(out_public_key_octal_len != NULL)
+ *out_public_key_octal_len = octal_len;
+
+clean_exit:
+
+ if(bn_ctx)
+ BN_CTX_free(bn_ctx);
+
+ return (ret == 1) ? 0 : -1;
+}
+
+/* _libssh2_ecdh_gen_k
+ *
+ * Computes the shared secret K given a local private key,
+ * remote public key and length
+ */
+
+int
+_libssh2_ecdh_gen_k(_libssh2_bn **k, _libssh2_ec_key *private_key,
+ const unsigned char *server_public_key, size_t server_public_key_len)
+{
+ int ret = 0;
+ int rc;
+ size_t secret_len;
+ unsigned char *secret = NULL;
+ const EC_GROUP *private_key_group;
+ EC_POINT *server_public_key_point;
+
+ BN_CTX *bn_ctx = BN_CTX_new();
+
+ if(!bn_ctx)
+ return -1;
+
+ if(k == NULL)
+ return -1;
+
+ private_key_group = EC_KEY_get0_group(private_key);
+
+ server_public_key_point = EC_POINT_new(private_key_group);
+ if(server_public_key_point == NULL)
+ return -1;
+
+ rc = EC_POINT_oct2point(private_key_group, server_public_key_point,
+ server_public_key, server_public_key_len, bn_ctx);
+ if(rc != 1) {
+ ret = -1;
+ goto clean_exit;
+ }
+
+ secret_len = (EC_GROUP_get_degree(private_key_group) + 7) / 8;
+ secret = malloc(secret_len);
+ if(!secret) {
+ ret = -1;
+ goto clean_exit;
+ }
+
+ secret_len = ECDH_compute_key(secret, secret_len, server_public_key_point,
+ private_key, NULL);
+
+ if(secret_len <= 0 || secret_len > EC_MAX_POINT_LEN) {
+ ret = -1;
+ goto clean_exit;
+ }
+
+ BN_bin2bn(secret, secret_len, *k);
+
+clean_exit:
+
+ if(server_public_key_point != NULL)
+ EC_POINT_free(server_public_key_point);
+
+ if(bn_ctx != NULL)
+ BN_CTX_free(bn_ctx);
+
+ if(secret != NULL)
+ free(secret);
+
+ return ret;
+}
+
+
+#endif /* LIBSSH2_ECDSA */
+
+#if LIBSSH2_ED25519
+
+int
+_libssh2_ed25519_sign(libssh2_ed25519_ctx *ctx, LIBSSH2_SESSION *session,
+ uint8_t **out_sig, size_t *out_sig_len,
+ const uint8_t *message, size_t message_len)
+{
+ int rc = -1;
+ EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
+ size_t sig_len = 0;
+ unsigned char *sig = NULL;
+
+ if(md_ctx != NULL) {
+ if(EVP_DigestSignInit(md_ctx, NULL, NULL, NULL, ctx) != 1)
+ goto clean_exit;
+ if(EVP_DigestSign(md_ctx, NULL, &sig_len, message, message_len) != 1)
+ goto clean_exit;
+
+ if(sig_len != LIBSSH2_ED25519_SIG_LEN)
+ goto clean_exit;
+
+ sig = LIBSSH2_CALLOC(session, sig_len);
+ if(sig == NULL)
+ goto clean_exit;
+
+ rc = EVP_DigestSign(md_ctx, sig, &sig_len, message, message_len);
+ }
+
+ if(rc == 1) {
+ *out_sig = sig;
+ *out_sig_len = sig_len;
+ }
+ else {
+ *out_sig_len = 0;
+ *out_sig = NULL;
+ LIBSSH2_FREE(session, sig);
+ }
+
+clean_exit:
+
+ if(md_ctx)
+ EVP_MD_CTX_free(md_ctx);
+
+ return (rc == 1 ? 0 : -1);
+}
+
+int
+_libssh2_curve25519_gen_k(_libssh2_bn **k,
+ uint8_t private_key[LIBSSH2_ED25519_KEY_LEN],
+ uint8_t server_public_key[LIBSSH2_ED25519_KEY_LEN])
+{
+ int rc = -1;
+ unsigned char out_shared_key[LIBSSH2_ED25519_KEY_LEN];
+ EVP_PKEY *peer_key = NULL, *server_key = NULL;
+ EVP_PKEY_CTX *server_key_ctx = NULL;
+ BN_CTX *bn_ctx = NULL;
+ size_t out_len = 0;
+
+ if(k == NULL || *k == NULL)
+ return -1;
+
+ bn_ctx = BN_CTX_new();
+ if(bn_ctx == NULL)
+ return -1;
+
+ peer_key = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL,
+ server_public_key,
+ LIBSSH2_ED25519_KEY_LEN);
+
+ server_key = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL,
+ private_key,
+ LIBSSH2_ED25519_KEY_LEN);
+
+ if(peer_key == NULL || server_key == NULL) {
+ goto cleanExit;
+ }
+
+ server_key_ctx = EVP_PKEY_CTX_new(server_key, NULL);
+ if(server_key_ctx == NULL) {
+ goto cleanExit;
+ }
+
+ rc = EVP_PKEY_derive_init(server_key_ctx);
+ if(rc <= 0) goto cleanExit;
+
+ rc = EVP_PKEY_derive_set_peer(server_key_ctx, peer_key);
+ if(rc <= 0) goto cleanExit;
+
+ rc = EVP_PKEY_derive(server_key_ctx, NULL, &out_len);
+ if(rc <= 0) goto cleanExit;
+
+ if(out_len != LIBSSH2_ED25519_KEY_LEN) {
+ rc = -1;
+ goto cleanExit;
+ }
+
+ rc = EVP_PKEY_derive(server_key_ctx, out_shared_key, &out_len);
+
+ if(rc == 1 && out_len == LIBSSH2_ED25519_KEY_LEN) {
+ BN_bin2bn(out_shared_key, LIBSSH2_ED25519_KEY_LEN, *k);
+ }
+ else {
+ rc = -1;
+ }
+
+cleanExit:
+
+ if(server_key_ctx)
+ EVP_PKEY_CTX_free(server_key_ctx);
+ if(peer_key)
+ EVP_PKEY_free(peer_key);
+ if(server_key)
+ EVP_PKEY_free(server_key);
+ if(bn_ctx != NULL)
+ BN_CTX_free(bn_ctx);
+
+ return (rc == 1) ? 0 : -1;
+}
+
+
+int
+_libssh2_ed25519_verify(libssh2_ed25519_ctx *ctx, const uint8_t *s,
+ size_t s_len, const uint8_t *m, size_t m_len)
+{
+ int ret = -1;
+
+ EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
+ if(NULL == md_ctx)
+ return -1;
+
+ ret = EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, ctx);
+ if(ret != 1)
+ goto clean_exit;
+
+ ret = EVP_DigestVerify(md_ctx, s, s_len, m, m_len);
+
+ clean_exit:
+
+ EVP_MD_CTX_free(md_ctx);
+
+ return (ret == 1) ? 0 : -1;
+}
+
+#endif /* LIBSSH2_ED25519 */
+
+static int
+_libssh2_pub_priv_openssh_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase)
+{
+ FILE *fp;
+ unsigned char *buf = NULL;
+ struct string_buf *decrypted = NULL;
+ int rc = 0;
+
+ if(session == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Session is required");
+ return -1;
+ }
+
+ _libssh2_init_if_needed();
+
+ fp = fopen(privatekey, "r");
+ if(!fp) {
+ _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to open private key file");
+ return -1;
+ }
+
+ rc = _libssh2_openssh_pem_parse(session, (const unsigned char *)passphrase,
+ fp, &decrypted);
+ fclose(fp);
+ if(rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Not an OpenSSH key file");
+ return rc;
+ }
+
+ /* We have a new key file, now try and parse it using supported types */
+ rc = _libssh2_get_string(decrypted, &buf, NULL);
+
+ if(rc != 0 || buf == NULL) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Public key type in decrypted key data not found");
+ return -1;
+ }
+
+ rc = -1;
+
+#if LIBSSH2_ED25519
+ if(strcmp("ssh-ed25519", (const char *)buf) == 0) {
+ rc = gen_publickey_from_ed25519_openssh_priv_data(session, decrypted,
+ method, method_len,
+ pubkeydata,
+ pubkeydata_len,
+ NULL);
+ }
+#endif
+#if LIBSSH2_RSA
+ if(strcmp("ssh-rsa", (const char *)buf) == 0) {
+ rc = gen_publickey_from_rsa_openssh_priv_data(session, decrypted,
+ method, method_len,
+ pubkeydata,
+ pubkeydata_len,
+ NULL);
+ }
+#endif
+#if LIBSSH2_DSA
+ if(strcmp("ssh-dss", (const char *)buf) == 0) {
+ rc = gen_publickey_from_dsa_openssh_priv_data(session, decrypted,
+ method, method_len,
+ pubkeydata,
+ pubkeydata_len,
+ NULL);
+ }
+#endif
+#if LIBSSH2_ECDSA
+ {
+ libssh2_curve_type type;
+
+ if(_libssh2_ecdsa_curve_type_from_name((const char *)buf,
+ &type) == 0) {
+ rc = gen_publickey_from_ecdsa_openssh_priv_data(session, type,
+ decrypted,
+ method, method_len,
+ pubkeydata,
+ pubkeydata_len,
+ NULL);
+ }
+ }
+#endif
+
+ if(decrypted)
+ _libssh2_string_buf_free(session, decrypted);
+
+ if(rc != 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unsupported OpenSSH key type");
+ }
+
+ return rc;
+}
+
+int
+_libssh2_pub_priv_keyfile(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekey,
+ const char *passphrase)
+{
+ int st;
+ BIO* bp;
+ EVP_PKEY* pk;
+ int pktype;
+ int rc;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing public key from private key file: %s",
+ privatekey);
+
+ bp = BIO_new_file(privatekey, "r");
+ if(bp == NULL) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_FILE,
+ "Unable to extract public key from private key "
+ "file: Unable to open private key file");
+ }
+
+ BIO_reset(bp);
+ pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void *)passphrase);
+ BIO_free(bp);
+
+ if(pk == NULL) {
+
+ /* Try OpenSSH format */
+ rc = _libssh2_pub_priv_openssh_keyfile(session,
+ method,
+ method_len,
+ pubkeydata, pubkeydata_len,
+ privatekey, passphrase);
+ if(rc != 0) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_FILE,
+ "Unable to extract public key "
+ "from private key file: "
+ "Wrong passphrase or invalid/unrecognized "
+ "private key file format");
+ }
+
+ return 0;
+ }
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ pktype = EVP_PKEY_id(pk);
+#else
+ pktype = pk->type;
+#endif
+
+ switch(pktype) {
+#if LIBSSH2_ED25519
+ case EVP_PKEY_ED25519 :
+ st = gen_publickey_from_ed_evp(
+ session, method, method_len, pubkeydata, pubkeydata_len, pk);
+ break;
+#endif /* LIBSSH2_ED25519 */
+ case EVP_PKEY_RSA :
+ st = gen_publickey_from_rsa_evp(
+ session, method, method_len, pubkeydata, pubkeydata_len, pk);
+ break;
+
+#if LIBSSH2_DSA
+ case EVP_PKEY_DSA :
+ st = gen_publickey_from_dsa_evp(
+ session, method, method_len, pubkeydata, pubkeydata_len, pk);
+ break;
+#endif /* LIBSSH_DSA */
+
+#if LIBSSH2_ECDSA
+ case EVP_PKEY_EC :
+ st = gen_publickey_from_ec_evp(
+ session, method, method_len, pubkeydata, pubkeydata_len, pk);
+ break;
+#endif
+
+ default :
+ st = _libssh2_error(session,
+ LIBSSH2_ERROR_FILE,
+ "Unable to extract public key "
+ "from private key file: "
+ "Unsupported private key file format");
+ break;
+ }
+
+ EVP_PKEY_free(pk);
+ return st;
+}
+
+static int
+_libssh2_pub_priv_openssh_keyfilememory(LIBSSH2_SESSION *session,
+ void **key_ctx,
+ const char *key_type,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ unsigned const char *passphrase)
+{
+ int rc;
+ unsigned char *buf = NULL;
+ struct string_buf *decrypted = NULL;
+
+ if(key_ctx != NULL)
+ *key_ctx = NULL;
+
+ if(session == NULL)
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Session is required");
+
+ if(key_type != NULL && (strlen(key_type) > 11 || strlen(key_type) < 7))
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "type is invalid");
+
+ _libssh2_init_if_needed();
+
+ rc = _libssh2_openssh_pem_parse_memory(session, passphrase,
+ privatekeydata,
+ privatekeydata_len, &decrypted);
+
+ if(rc)
+ return rc;
+
+ /* We have a new key file, now try and parse it using supported types */
+ rc = _libssh2_get_string(decrypted, &buf, NULL);
+
+ if(rc != 0 || buf == NULL)
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Public key type in decrypted "
+ "key data not found");
+
+ rc = LIBSSH2_ERROR_FILE;
+
+#if LIBSSH2_ED25519
+ if(strcmp("ssh-ed25519", (const char *)buf) == 0) {
+ if(key_type == NULL || strcmp("ssh-ed25519", key_type) == 0) {
+ rc = gen_publickey_from_ed25519_openssh_priv_data(session,
+ decrypted,
+ method,
+ method_len,
+ pubkeydata,
+ pubkeydata_len,
+ (libssh2_ed25519_ctx**)key_ctx);
+ }
+ }
+#endif
+#if LIBSSH2_RSA
+ if(strcmp("ssh-rsa", (const char *)buf) == 0) {
+ if(key_type == NULL || strcmp("ssh-rsa", key_type) == 0) {
+ rc = gen_publickey_from_rsa_openssh_priv_data(session, decrypted,
+ method, method_len,
+ pubkeydata,
+ pubkeydata_len,
+ (libssh2_rsa_ctx**)key_ctx);
+ }
+ }
+#endif
+#if LIBSSH2_DSA
+ if(strcmp("ssh-dss", (const char *)buf) == 0) {
+ if(key_type == NULL || strcmp("ssh-dss", key_type) == 0) {
+ rc = gen_publickey_from_dsa_openssh_priv_data(session, decrypted,
+ method, method_len,
+ pubkeydata,
+ pubkeydata_len,
+ (libssh2_dsa_ctx**)key_ctx);
+ }
+ }
+#endif
+#if LIBSSH2_ECDSA
+{
+ libssh2_curve_type type;
+
+ if(_libssh2_ecdsa_curve_type_from_name((const char *)buf, &type) == 0) {
+ if(key_type == NULL || strcmp("ssh-ecdsa", key_type) == 0) {
+ rc = gen_publickey_from_ecdsa_openssh_priv_data(session, type,
+ decrypted,
+ method, method_len,
+ pubkeydata,
+ pubkeydata_len,
+ (libssh2_ecdsa_ctx**)key_ctx);
+ }
+ }
+}
+#endif
+
+ if(rc == LIBSSH2_ERROR_FILE)
+ rc = _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to extract public key from private key file: "
+ "invalid/unrecognized private key file format");
+
+ if(decrypted)
+ _libssh2_string_buf_free(session, decrypted);
+
+ return rc;
+}
+
+int
+read_openssh_private_key_from_memory(void **key_ctx, LIBSSH2_SESSION *session,
+ const char *key_type,
+ const char *filedata,
+ size_t filedata_len,
+ unsigned const char *passphrase)
+{
+ return _libssh2_pub_priv_openssh_keyfilememory(session, key_ctx, key_type,
+ NULL, NULL, NULL, NULL,
+ filedata, filedata_len,
+ passphrase);
+}
+
+int
+_libssh2_pub_priv_keyfilememory(LIBSSH2_SESSION *session,
+ unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+ int st;
+ BIO* bp;
+ EVP_PKEY* pk;
+ int pktype;
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_AUTH,
+ "Computing public key from private key.");
+
+ bp = BIO_new_mem_buf((char *)privatekeydata, privatekeydata_len);
+ if(!bp)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory when"
+ "computing public key");
+ BIO_reset(bp);
+ pk = PEM_read_bio_PrivateKey(bp, NULL, NULL, (void *)passphrase);
+ BIO_free(bp);
+
+ if(pk == NULL) {
+ /* Try OpenSSH format */
+ st = _libssh2_pub_priv_openssh_keyfilememory(session, NULL, NULL,
+ method,
+ method_len,
+ pubkeydata,
+ pubkeydata_len,
+ privatekeydata,
+ privatekeydata_len,
+ (unsigned const char *)passphrase);
+ if(st != 0)
+ return st;
+ return 0;
+ }
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ pktype = EVP_PKEY_id(pk);
+#else
+ pktype = pk->type;
+#endif
+
+ switch(pktype) {
+#if LIBSSH2_ED25519
+ case EVP_PKEY_ED25519 :
+ st = gen_publickey_from_ed_evp(
+ session, method, method_len, pubkeydata, pubkeydata_len, pk);
+ break;
+#endif /* LIBSSH2_ED25519 */
+ case EVP_PKEY_RSA :
+ st = gen_publickey_from_rsa_evp(session, method, method_len,
+ pubkeydata, pubkeydata_len, pk);
+ break;
+#if LIBSSH2_DSA
+ case EVP_PKEY_DSA :
+ st = gen_publickey_from_dsa_evp(session, method, method_len,
+ pubkeydata, pubkeydata_len, pk);
+ break;
+#endif /* LIBSSH_DSA */
+#if LIBSSH2_ECDSA
+ case EVP_PKEY_EC :
+ st = gen_publickey_from_ec_evp(session, method, method_len,
+ pubkeydata, pubkeydata_len, pk);
+ break;
+#endif /* LIBSSH2_ECDSA */
+ default :
+ st = _libssh2_error(session,
+ LIBSSH2_ERROR_FILE,
+ "Unable to extract public key "
+ "from private key file: "
+ "Unsupported private key file format");
+ break;
+ }
+
+ EVP_PKEY_free(pk);
+ return st;
+}
+
+void
+_libssh2_dh_init(_libssh2_dh_ctx *dhctx)
+{
+ *dhctx = BN_new(); /* Random from client */
+}
+
+int
+_libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public,
+ _libssh2_bn *g, _libssh2_bn *p, int group_order,
+ _libssh2_bn_ctx *bnctx)
+{
+ /* Generate x and e */
+ BN_rand(*dhctx, group_order * 8 - 1, 0, -1);
+ BN_mod_exp(public, g, *dhctx, p, bnctx);
+ return 0;
+}
+
+int
+_libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret,
+ _libssh2_bn *f, _libssh2_bn *p,
+ _libssh2_bn_ctx *bnctx)
+{
+ /* Compute the shared secret */
+ BN_mod_exp(secret, f, *dhctx, p, bnctx);
+ return 0;
+}
+
+void
+_libssh2_dh_dtor(_libssh2_dh_ctx *dhctx)
+{
+ BN_clear_free(*dhctx);
+ *dhctx = NULL;
+}
+
+#endif /* LIBSSH2_OPENSSL */
diff --git a/contrib/libs/libssh2/src/openssl.h b/contrib/libs/libssh2/src/openssl.h
new file mode 100644
index 00000000000..658b040d65b
--- /dev/null
+++ b/contrib/libs/libssh2/src/openssl.h
@@ -0,0 +1,396 @@
+#ifndef __LIBSSH2_OPENSSL_H
+#define __LIBSSH2_OPENSSL_H
+/* Copyright (C) 2009, 2010 Simon Josefsson
+ * Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved.
+ *
+ * Author: Simon Josefsson
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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/opensslconf.h>
+#include <openssl/sha.h>
+#include <openssl/rsa.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif
+#ifndef OPENSSL_NO_DSA
+#include <openssl/dsa.h>
+#endif
+#ifndef OPENSSL_NO_MD5
+#include <openssl/md5.h>
+#endif
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+# define HAVE_OPAQUE_STRUCTS 1
+#endif
+
+#ifdef OPENSSL_NO_RSA
+# define LIBSSH2_RSA 0
+#else
+# define LIBSSH2_RSA 1
+#endif
+
+#ifdef OPENSSL_NO_DSA
+# define LIBSSH2_DSA 0
+#else
+# define LIBSSH2_DSA 1
+#endif
+
+#ifdef OPENSSL_NO_ECDSA
+# define LIBSSH2_ECDSA 0
+#else
+# define LIBSSH2_ECDSA 1
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+!defined(LIBRESSL_VERSION_NUMBER)
+# define LIBSSH2_ED25519 1
+#else
+# define LIBSSH2_ED25519 0
+#endif
+
+
+#ifdef OPENSSL_NO_MD5
+# define LIBSSH2_MD5 0
+#else
+# define LIBSSH2_MD5 1
+#endif
+
+#ifdef OPENSSL_NO_RIPEMD
+# define LIBSSH2_HMAC_RIPEMD 0
+#else
+# define LIBSSH2_HMAC_RIPEMD 1
+#endif
+
+#define LIBSSH2_HMAC_SHA256 1
+#define LIBSSH2_HMAC_SHA512 1
+
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES)
+# define LIBSSH2_AES_CTR 1
+# define LIBSSH2_AES 1
+#else
+# define LIBSSH2_AES_CTR 0
+# define LIBSSH2_AES 0
+#endif
+
+#ifdef OPENSSL_NO_BF
+# define LIBSSH2_BLOWFISH 0
+#else
+# define LIBSSH2_BLOWFISH 1
+#endif
+
+#ifdef OPENSSL_NO_RC4
+# define LIBSSH2_RC4 0
+#else
+# define LIBSSH2_RC4 1
+#endif
+
+#ifdef OPENSSL_NO_CAST
+# define LIBSSH2_CAST 0
+#else
+# define LIBSSH2_CAST 1
+#endif
+
+#ifdef OPENSSL_NO_DES
+# define LIBSSH2_3DES 0
+#else
+# define LIBSSH2_3DES 1
+#endif
+
+#define EC_MAX_POINT_LEN ((528 * 2 / 8) + 1)
+
+#define _libssh2_random(buf, len) (RAND_bytes((buf), (len)) == 1 ? 0 : -1)
+
+#define libssh2_prepare_iovec(vec, len) /* Empty. */
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha1_ctx EVP_MD_CTX *
+#else
+#define libssh2_sha1_ctx EVP_MD_CTX
+#endif
+
+/* returns 0 in case of failure */
+int _libssh2_sha1_init(libssh2_sha1_ctx *ctx);
+#define libssh2_sha1_init(x) _libssh2_sha1_init(x)
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha1_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len)
+#define libssh2_sha1_final(ctx, out) do { \
+ EVP_DigestFinal(ctx, out, NULL); \
+ EVP_MD_CTX_free(ctx); \
+ } while(0)
+#else
+#define libssh2_sha1_update(ctx, data, len) EVP_DigestUpdate(&(ctx), data, len)
+#define libssh2_sha1_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
+#endif
+int _libssh2_sha1(const unsigned char *message, unsigned long len,
+ unsigned char *out);
+#define libssh2_sha1(x,y,z) _libssh2_sha1(x,y,z)
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha256_ctx EVP_MD_CTX *
+#else
+#define libssh2_sha256_ctx EVP_MD_CTX
+#endif
+
+/* returns 0 in case of failure */
+int _libssh2_sha256_init(libssh2_sha256_ctx *ctx);
+#define libssh2_sha256_init(x) _libssh2_sha256_init(x)
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha256_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len)
+#define libssh2_sha256_final(ctx, out) do { \
+ EVP_DigestFinal(ctx, out, NULL); \
+ EVP_MD_CTX_free(ctx); \
+ } while(0)
+#else
+#define libssh2_sha256_update(ctx, data, len) \
+ EVP_DigestUpdate(&(ctx), data, len)
+#define libssh2_sha256_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
+#endif
+int _libssh2_sha256(const unsigned char *message, unsigned long len,
+ unsigned char *out);
+#define libssh2_sha256(x,y,z) _libssh2_sha256(x,y,z)
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha384_ctx EVP_MD_CTX *
+#else
+#define libssh2_sha384_ctx EVP_MD_CTX
+#endif
+
+/* returns 0 in case of failure */
+int _libssh2_sha384_init(libssh2_sha384_ctx *ctx);
+#define libssh2_sha384_init(x) _libssh2_sha384_init(x)
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha384_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len)
+#define libssh2_sha384_final(ctx, out) do { \
+ EVP_DigestFinal(ctx, out, NULL); \
+ EVP_MD_CTX_free(ctx); \
+ } while(0)
+#else
+#define libssh2_sha384_update(ctx, data, len) \
+ EVP_DigestUpdate(&(ctx), data, len)
+#define libssh2_sha384_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
+#endif
+int _libssh2_sha384(const unsigned char *message, unsigned long len,
+ unsigned char *out);
+#define libssh2_sha384(x,y,z) _libssh2_sha384(x,y,z)
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha512_ctx EVP_MD_CTX *
+#else
+#define libssh2_sha512_ctx EVP_MD_CTX
+#endif
+
+/* returns 0 in case of failure */
+int _libssh2_sha512_init(libssh2_sha512_ctx *ctx);
+#define libssh2_sha512_init(x) _libssh2_sha512_init(x)
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_sha512_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len)
+#define libssh2_sha512_final(ctx, out) do { \
+ EVP_DigestFinal(ctx, out, NULL); \
+ EVP_MD_CTX_free(ctx); \
+ } while(0)
+#else
+#define libssh2_sha512_update(ctx, data, len) \
+ EVP_DigestUpdate(&(ctx), data, len)
+#define libssh2_sha512_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
+#endif
+int _libssh2_sha512(const unsigned char *message, unsigned long len,
+ unsigned char *out);
+#define libssh2_sha512(x,y,z) _libssh2_sha512(x,y,z)
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_md5_ctx EVP_MD_CTX *
+#else
+#define libssh2_md5_ctx EVP_MD_CTX
+#endif
+
+/* returns 0 in case of failure */
+int _libssh2_md5_init(libssh2_md5_ctx *ctx);
+#define libssh2_md5_init(x) _libssh2_md5_init(x)
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_md5_update(ctx, data, len) EVP_DigestUpdate(ctx, data, len)
+#define libssh2_md5_final(ctx, out) do { \
+ EVP_DigestFinal(ctx, out, NULL); \
+ EVP_MD_CTX_free(ctx); \
+ } while(0)
+#else
+#define libssh2_md5_update(ctx, data, len) EVP_DigestUpdate(&(ctx), data, len)
+#define libssh2_md5_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
+#endif
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define libssh2_hmac_ctx HMAC_CTX *
+#define libssh2_hmac_ctx_init(ctx) ctx = HMAC_CTX_new()
+#define libssh2_hmac_sha1_init(ctx, key, keylen) \
+ HMAC_Init_ex(*(ctx), key, keylen, EVP_sha1(), NULL)
+#define libssh2_hmac_md5_init(ctx, key, keylen) \
+ HMAC_Init_ex(*(ctx), key, keylen, EVP_md5(), NULL)
+#define libssh2_hmac_ripemd160_init(ctx, key, keylen) \
+ HMAC_Init_ex(*(ctx), key, keylen, EVP_ripemd160(), NULL)
+#define libssh2_hmac_sha256_init(ctx, key, keylen) \
+ HMAC_Init_ex(*(ctx), key, keylen, EVP_sha256(), NULL)
+#define libssh2_hmac_sha512_init(ctx, key, keylen) \
+ HMAC_Init_ex(*(ctx), key, keylen, EVP_sha512(), NULL)
+
+#define libssh2_hmac_update(ctx, data, datalen) \
+ HMAC_Update(ctx, data, datalen)
+#define libssh2_hmac_final(ctx, data) HMAC_Final(ctx, data, NULL)
+#define libssh2_hmac_cleanup(ctx) HMAC_CTX_free(*(ctx))
+#else
+#define libssh2_hmac_ctx HMAC_CTX
+#define libssh2_hmac_ctx_init(ctx) \
+ HMAC_CTX_init(&ctx)
+#define libssh2_hmac_sha1_init(ctx, key, keylen) \
+ HMAC_Init_ex(ctx, key, keylen, EVP_sha1(), NULL)
+#define libssh2_hmac_md5_init(ctx, key, keylen) \
+ HMAC_Init_ex(ctx, key, keylen, EVP_md5(), NULL)
+#define libssh2_hmac_ripemd160_init(ctx, key, keylen) \
+ HMAC_Init_ex(ctx, key, keylen, EVP_ripemd160(), NULL)
+#define libssh2_hmac_sha256_init(ctx, key, keylen) \
+ HMAC_Init_ex(ctx, key, keylen, EVP_sha256(), NULL)
+#define libssh2_hmac_sha512_init(ctx, key, keylen) \
+ HMAC_Init_ex(ctx, key, keylen, EVP_sha512(), NULL)
+
+#define libssh2_hmac_update(ctx, data, datalen) \
+ HMAC_Update(&(ctx), data, datalen)
+#define libssh2_hmac_final(ctx, data) HMAC_Final(&(ctx), data, NULL)
+#define libssh2_hmac_cleanup(ctx) HMAC_cleanup(ctx)
+#endif
+
+extern void _libssh2_openssl_crypto_init(void);
+extern void _libssh2_openssl_crypto_exit(void);
+#define libssh2_crypto_init() _libssh2_openssl_crypto_init()
+#define libssh2_crypto_exit() _libssh2_openssl_crypto_exit()
+
+#define libssh2_rsa_ctx RSA
+
+#define _libssh2_rsa_free(rsactx) RSA_free(rsactx)
+
+#define libssh2_dsa_ctx DSA
+
+#define _libssh2_dsa_free(dsactx) DSA_free(dsactx)
+
+#if LIBSSH2_ECDSA
+#define libssh2_ecdsa_ctx EC_KEY
+#define _libssh2_ecdsa_free(ecdsactx) EC_KEY_free(ecdsactx)
+#define _libssh2_ec_key EC_KEY
+
+typedef enum {
+ LIBSSH2_EC_CURVE_NISTP256 = NID_X9_62_prime256v1,
+ LIBSSH2_EC_CURVE_NISTP384 = NID_secp384r1,
+ LIBSSH2_EC_CURVE_NISTP521 = NID_secp521r1
+}
+libssh2_curve_type;
+#else
+#define _libssh2_ec_key void
+#endif /* LIBSSH2_ECDSA */
+
+#if LIBSSH2_ED25519
+#define libssh2_ed25519_ctx EVP_PKEY
+
+#define _libssh2_ed25519_free(ctx) EVP_PKEY_free(ctx)
+#endif /* ED25519 */
+
+#define _libssh2_cipher_type(name) const EVP_CIPHER *(*name)(void)
+#ifdef HAVE_OPAQUE_STRUCTS
+#define _libssh2_cipher_ctx EVP_CIPHER_CTX *
+#else
+#define _libssh2_cipher_ctx EVP_CIPHER_CTX
+#endif
+
+#define _libssh2_cipher_aes256 EVP_aes_256_cbc
+#define _libssh2_cipher_aes192 EVP_aes_192_cbc
+#define _libssh2_cipher_aes128 EVP_aes_128_cbc
+#ifdef HAVE_EVP_AES_128_CTR
+#define _libssh2_cipher_aes128ctr EVP_aes_128_ctr
+#define _libssh2_cipher_aes192ctr EVP_aes_192_ctr
+#define _libssh2_cipher_aes256ctr EVP_aes_256_ctr
+#else
+#define _libssh2_cipher_aes128ctr _libssh2_EVP_aes_128_ctr
+#define _libssh2_cipher_aes192ctr _libssh2_EVP_aes_192_ctr
+#define _libssh2_cipher_aes256ctr _libssh2_EVP_aes_256_ctr
+#endif
+#define _libssh2_cipher_blowfish EVP_bf_cbc
+#define _libssh2_cipher_arcfour EVP_rc4
+#define _libssh2_cipher_cast5 EVP_cast5_cbc
+#define _libssh2_cipher_3des EVP_des_ede3_cbc
+
+#ifdef HAVE_OPAQUE_STRUCTS
+#define _libssh2_cipher_dtor(ctx) EVP_CIPHER_CTX_free(*(ctx))
+#else
+#define _libssh2_cipher_dtor(ctx) EVP_CIPHER_CTX_cleanup(ctx)
+#endif
+
+#define _libssh2_bn BIGNUM
+#define _libssh2_bn_ctx BN_CTX
+#define _libssh2_bn_ctx_new() BN_CTX_new()
+#define _libssh2_bn_ctx_free(bnctx) BN_CTX_free(bnctx)
+#define _libssh2_bn_init() BN_new()
+#define _libssh2_bn_init_from_bin() _libssh2_bn_init()
+#define _libssh2_bn_set_word(bn, val) BN_set_word(bn, val)
+#define _libssh2_bn_from_bin(bn, len, val) BN_bin2bn(val, len, bn)
+#define _libssh2_bn_to_bin(bn, val) BN_bn2bin(bn, val)
+#define _libssh2_bn_bytes(bn) BN_num_bytes(bn)
+#define _libssh2_bn_bits(bn) BN_num_bits(bn)
+#define _libssh2_bn_free(bn) BN_clear_free(bn)
+
+#define _libssh2_dh_ctx BIGNUM *
+#define libssh2_dh_init(dhctx) _libssh2_dh_init(dhctx)
+#define libssh2_dh_key_pair(dhctx, public, g, p, group_order, bnctx) \
+ _libssh2_dh_key_pair(dhctx, public, g, p, group_order, bnctx)
+#define libssh2_dh_secret(dhctx, secret, f, p, bnctx) \
+ _libssh2_dh_secret(dhctx, secret, f, p, bnctx)
+#define libssh2_dh_dtor(dhctx) _libssh2_dh_dtor(dhctx)
+extern void _libssh2_dh_init(_libssh2_dh_ctx *dhctx);
+extern int _libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public,
+ _libssh2_bn *g, _libssh2_bn *p,
+ int group_order,
+ _libssh2_bn_ctx *bnctx);
+extern int _libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret,
+ _libssh2_bn *f, _libssh2_bn *p,
+ _libssh2_bn_ctx *bnctx);
+extern void _libssh2_dh_dtor(_libssh2_dh_ctx *dhctx);
+
+const EVP_CIPHER *_libssh2_EVP_aes_128_ctr(void);
+const EVP_CIPHER *_libssh2_EVP_aes_192_ctr(void);
+const EVP_CIPHER *_libssh2_EVP_aes_256_ctr(void);
+
+#endif /* __LIBSSH2_OPENSSL_H */
diff --git a/contrib/libs/libssh2/src/packet.c b/contrib/libs/libssh2/src/packet.c
new file mode 100644
index 00000000000..04937d62a72
--- /dev/null
+++ b/contrib/libs/libssh2/src/packet.c
@@ -0,0 +1,1338 @@
+/* Copyright (c) 2004-2007, Sara Golemon <[email protected]>
+ * Copyright (c) 2005,2006 Mikhail Gusarov
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+/* Needed for struct iovec on some platforms */
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include <sys/types.h>
+
+#include "transport.h"
+#include "channel.h"
+#include "packet.h"
+
+/*
+ * libssh2_packet_queue_listener
+ *
+ * Queue a connection request for a listener
+ */
+static inline int
+packet_queue_listener(LIBSSH2_SESSION * session, unsigned char *data,
+ unsigned long datalen,
+ packet_queue_listener_state_t *listen_state)
+{
+ /*
+ * Look for a matching listener
+ */
+ /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
+ unsigned long packet_len = 17 + (sizeof(FwdNotReq) - 1);
+ unsigned char *p;
+ LIBSSH2_LISTENER *listn = _libssh2_list_first(&session->listeners);
+ char failure_code = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
+ int rc;
+
+ if(listen_state->state == libssh2_NB_state_idle) {
+ unsigned long offset = (sizeof("forwarded-tcpip") - 1) + 5;
+ size_t temp_len = 0;
+ struct string_buf buf;
+ buf.data = data;
+ buf.dataptr = buf.data;
+ buf.len = datalen;
+
+ if(datalen < offset) {
+ return _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Unexpected packet size");
+ }
+
+ buf.dataptr += offset;
+
+ if(_libssh2_get_u32(&buf, &(listen_state->sender_channel))) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Data too short extracting channel");
+ }
+ if(_libssh2_get_u32(&buf, &(listen_state->initial_window_size))) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Data too short extracting window size");
+ }
+ if(_libssh2_get_u32(&buf, &(listen_state->packet_size))) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Data too short extracting packet");
+ }
+ if(_libssh2_get_string(&buf, &(listen_state->host), &temp_len)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Data too short extracting host");
+ }
+ listen_state->host_len = (uint32_t)temp_len;
+
+ if(_libssh2_get_u32(&buf, &(listen_state->port))) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Data too short extracting port");
+ }
+ if(_libssh2_get_string(&buf, &(listen_state->shost), &temp_len)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Data too short extracting shost");
+ }
+ listen_state->shost_len = (uint32_t)temp_len;
+
+ if(_libssh2_get_u32(&buf, &(listen_state->sport))) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Data too short extracting sport");
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Remote received connection from %s:%ld to %s:%ld",
+ listen_state->shost, listen_state->sport,
+ listen_state->host, listen_state->port);
+
+ listen_state->state = libssh2_NB_state_allocated;
+ }
+
+ if(listen_state->state != libssh2_NB_state_sent) {
+ while(listn) {
+ if((listn->port == (int) listen_state->port) &&
+ (strlen(listn->host) == listen_state->host_len) &&
+ (memcmp (listn->host, listen_state->host,
+ listen_state->host_len) == 0)) {
+ /* This is our listener */
+ LIBSSH2_CHANNEL *channel = NULL;
+ listen_state->channel = NULL;
+
+ if(listen_state->state == libssh2_NB_state_allocated) {
+ if(listn->queue_maxsize &&
+ (listn->queue_maxsize <= listn->queue_size)) {
+ /* Queue is full */
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Listener queue full, ignoring");
+ listen_state->state = libssh2_NB_state_sent;
+ break;
+ }
+
+ channel = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL));
+ if(!channel) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a channel for "
+ "new connection");
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ listen_state->state = libssh2_NB_state_sent;
+ break;
+ }
+ listen_state->channel = channel;
+
+ channel->session = session;
+ channel->channel_type_len = sizeof("forwarded-tcpip") - 1;
+ channel->channel_type = LIBSSH2_ALLOC(session,
+ channel->
+ channel_type_len +
+ 1);
+ if(!channel->channel_type) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a channel for new"
+ " connection");
+ LIBSSH2_FREE(session, channel);
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ listen_state->state = libssh2_NB_state_sent;
+ break;
+ }
+ memcpy(channel->channel_type, "forwarded-tcpip",
+ channel->channel_type_len + 1);
+
+ channel->remote.id = listen_state->sender_channel;
+ channel->remote.window_size_initial =
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.window_size =
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.packet_size =
+ LIBSSH2_CHANNEL_PACKET_DEFAULT;
+
+ channel->local.id = _libssh2_channel_nextid(session);
+ channel->local.window_size_initial =
+ listen_state->initial_window_size;
+ channel->local.window_size =
+ listen_state->initial_window_size;
+ channel->local.packet_size = listen_state->packet_size;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Connection queued: channel %lu/%lu "
+ "win %lu/%lu packet %lu/%lu",
+ channel->local.id, channel->remote.id,
+ channel->local.window_size,
+ channel->remote.window_size,
+ channel->local.packet_size,
+ channel->remote.packet_size);
+
+ p = listen_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
+ _libssh2_store_u32(&p, channel->remote.id);
+ _libssh2_store_u32(&p, channel->local.id);
+ _libssh2_store_u32(&p,
+ channel->remote.window_size_initial);
+ _libssh2_store_u32(&p, channel->remote.packet_size);
+
+ listen_state->state = libssh2_NB_state_created;
+ }
+
+ if(listen_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, listen_state->packet,
+ 17, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if(rc) {
+ listen_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Unable to send channel "
+ "open confirmation");
+ }
+
+ /* Link the channel into the end of the queue list */
+ if(listen_state->channel) {
+ _libssh2_list_add(&listn->queue,
+ &listen_state->channel->node);
+ listn->queue_size++;
+ }
+
+ listen_state->state = libssh2_NB_state_idle;
+ return 0;
+ }
+ }
+
+ listn = _libssh2_list_next(&listn->node);
+ }
+
+ listen_state->state = libssh2_NB_state_sent;
+ }
+
+ /* We're not listening to you */
+ p = listen_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE;
+ _libssh2_store_u32(&p, listen_state->sender_channel);
+ _libssh2_store_u32(&p, failure_code);
+ _libssh2_store_str(&p, FwdNotReq, sizeof(FwdNotReq) - 1);
+ _libssh2_htonu32(p, 0);
+
+ rc = _libssh2_transport_send(session, listen_state->packet,
+ packet_len, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ listen_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc, "Unable to send open failure");
+
+ }
+ listen_state->state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * packet_x11_open
+ *
+ * Accept a forwarded X11 connection
+ */
+static inline int
+packet_x11_open(LIBSSH2_SESSION * session, unsigned char *data,
+ unsigned long datalen,
+ packet_x11_open_state_t *x11open_state)
+{
+ int failure_code = SSH_OPEN_CONNECT_FAILED;
+ /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */
+ unsigned long packet_len = 17 + (sizeof(X11FwdUnAvil) - 1);
+ unsigned char *p;
+ LIBSSH2_CHANNEL *channel = x11open_state->channel;
+ int rc;
+
+ if(x11open_state->state == libssh2_NB_state_idle) {
+
+ unsigned long offset = (sizeof("x11") - 1) + 5;
+ size_t temp_len = 0;
+ struct string_buf buf;
+ buf.data = data;
+ buf.dataptr = buf.data;
+ buf.len = datalen;
+
+ if(datalen < offset) {
+ _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "unexpected data length");
+ failure_code = SSH_OPEN_CONNECT_FAILED;
+ goto x11_exit;
+ }
+
+ buf.dataptr += offset;
+
+ if(_libssh2_get_u32(&buf, &(x11open_state->sender_channel))) {
+ _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "unexpected sender channel size");
+ failure_code = SSH_OPEN_CONNECT_FAILED;
+ goto x11_exit;
+ }
+ if(_libssh2_get_u32(&buf, &(x11open_state->initial_window_size))) {
+ _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "unexpected window size");
+ failure_code = SSH_OPEN_CONNECT_FAILED;
+ goto x11_exit;
+ }
+ if(_libssh2_get_u32(&buf, &(x11open_state->packet_size))) {
+ _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "unexpected window size");
+ failure_code = SSH_OPEN_CONNECT_FAILED;
+ goto x11_exit;
+ }
+ if(_libssh2_get_string(&buf, &(x11open_state->shost), &temp_len)) {
+ _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "unexpected host size");
+ failure_code = SSH_OPEN_CONNECT_FAILED;
+ goto x11_exit;
+ }
+ x11open_state->shost_len = (uint32_t)temp_len;
+
+ if(_libssh2_get_u32(&buf, &(x11open_state->sport))) {
+ _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "unexpected port size");
+ failure_code = SSH_OPEN_CONNECT_FAILED;
+ goto x11_exit;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "X11 Connection Received from %s:%ld on channel %lu",
+ x11open_state->shost, x11open_state->sport,
+ x11open_state->sender_channel);
+
+ x11open_state->state = libssh2_NB_state_allocated;
+ }
+
+ if(session->x11) {
+ if(x11open_state->state == libssh2_NB_state_allocated) {
+ channel = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_CHANNEL));
+ if(!channel) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "allocate a channel for new connection");
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ goto x11_exit;
+ }
+
+ channel->session = session;
+ channel->channel_type_len = sizeof("x11") - 1;
+ channel->channel_type = LIBSSH2_ALLOC(session,
+ channel->channel_type_len +
+ 1);
+ if(!channel->channel_type) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "allocate a channel for new connection");
+ LIBSSH2_FREE(session, channel);
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ goto x11_exit;
+ }
+ memcpy(channel->channel_type, "x11",
+ channel->channel_type_len + 1);
+
+ channel->remote.id = x11open_state->sender_channel;
+ channel->remote.window_size_initial =
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT;
+ channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT;
+
+ channel->local.id = _libssh2_channel_nextid(session);
+ channel->local.window_size_initial =
+ x11open_state->initial_window_size;
+ channel->local.window_size = x11open_state->initial_window_size;
+ channel->local.packet_size = x11open_state->packet_size;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "X11 Connection established: channel %lu/%lu "
+ "win %lu/%lu packet %lu/%lu",
+ channel->local.id, channel->remote.id,
+ channel->local.window_size,
+ channel->remote.window_size,
+ channel->local.packet_size,
+ channel->remote.packet_size);
+ p = x11open_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
+ _libssh2_store_u32(&p, channel->remote.id);
+ _libssh2_store_u32(&p, channel->local.id);
+ _libssh2_store_u32(&p, channel->remote.window_size_initial);
+ _libssh2_store_u32(&p, channel->remote.packet_size);
+
+ x11open_state->state = libssh2_NB_state_created;
+ }
+
+ if(x11open_state->state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, x11open_state->packet, 17,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ x11open_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send channel open "
+ "confirmation");
+ }
+
+ /* Link the channel into the session */
+ _libssh2_list_add(&session->channels, &channel->node);
+
+ /*
+ * Pass control to the callback, they may turn right around and
+ * free the channel, or actually use it
+ */
+ LIBSSH2_X11_OPEN(channel, (char *)x11open_state->shost,
+ x11open_state->sport);
+
+ x11open_state->state = libssh2_NB_state_idle;
+ return 0;
+ }
+ }
+ else
+ failure_code = SSH_OPEN_RESOURCE_SHORTAGE;
+ /* fall-trough */
+ x11_exit:
+ p = x11open_state->packet;
+ *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE;
+ _libssh2_store_u32(&p, x11open_state->sender_channel);
+ _libssh2_store_u32(&p, failure_code);
+ _libssh2_store_str(&p, X11FwdUnAvil, sizeof(X11FwdUnAvil) - 1);
+ _libssh2_htonu32(p, 0);
+
+ rc = _libssh2_transport_send(session, x11open_state->packet, packet_len,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ x11open_state->state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc, "Unable to send open failure");
+ }
+ x11open_state->state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * _libssh2_packet_add
+ *
+ * Create a new packet and attach it to the brigade. Called from the transport
+ * layer when it has received a packet.
+ *
+ * The input pointer 'data' is pointing to allocated data that this function
+ * is asked to deal with so on failure OR success, it must be freed fine.
+ * The only exception is when the return code is LIBSSH2_ERROR_EAGAIN.
+ *
+ * This function will always be called with 'datalen' greater than zero.
+ */
+int
+_libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
+ size_t datalen, int macstate)
+{
+ int rc = 0;
+ unsigned char *message = NULL;
+ unsigned char *language = NULL;
+ size_t message_len = 0;
+ size_t language_len = 0;
+ LIBSSH2_CHANNEL *channelp = NULL;
+ size_t data_head = 0;
+ unsigned char msg = data[0];
+
+ switch(session->packAdd_state) {
+ case libssh2_NB_state_idle:
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Packet type %d received, length=%d",
+ (int) msg, (int) datalen);
+
+ if((macstate == LIBSSH2_MAC_INVALID) &&
+ (!session->macerror ||
+ LIBSSH2_MACERROR(session, (char *) data, datalen))) {
+ /* Bad MAC input, but no callback set or non-zero return from the
+ callback */
+
+ LIBSSH2_FREE(session, data);
+ return _libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC,
+ "Invalid MAC received");
+ }
+ session->packAdd_state = libssh2_NB_state_allocated;
+ break;
+ case libssh2_NB_state_jump1:
+ goto libssh2_packet_add_jump_point1;
+ case libssh2_NB_state_jump2:
+ goto libssh2_packet_add_jump_point2;
+ case libssh2_NB_state_jump3:
+ goto libssh2_packet_add_jump_point3;
+ case libssh2_NB_state_jump4:
+ goto libssh2_packet_add_jump_point4;
+ case libssh2_NB_state_jump5:
+ goto libssh2_packet_add_jump_point5;
+ default: /* nothing to do */
+ break;
+ }
+
+ if(session->packAdd_state == libssh2_NB_state_allocated) {
+ /* A couple exceptions to the packet adding rule: */
+ switch(msg) {
+
+ /*
+ byte SSH_MSG_DISCONNECT
+ uint32 reason code
+ string description in ISO-10646 UTF-8 encoding [RFC3629]
+ string language tag [RFC3066]
+ */
+
+ case SSH_MSG_DISCONNECT:
+ if(datalen >= 5) {
+ uint32_t reason = 0;
+ struct string_buf buf;
+ buf.data = (unsigned char *)data;
+ buf.dataptr = buf.data;
+ buf.len = datalen;
+ buf.dataptr++; /* advance past type */
+
+ _libssh2_get_u32(&buf, &reason);
+ _libssh2_get_string(&buf, &message, &message_len);
+ _libssh2_get_string(&buf, &language, &language_len);
+
+ if(session->ssh_msg_disconnect) {
+ LIBSSH2_DISCONNECT(session, reason, (const char *)message,
+ message_len, (const char *)language,
+ language_len);
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Disconnect(%d): %s(%s)", reason,
+ message, language);
+ }
+
+ LIBSSH2_FREE(session, data);
+ session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
+ session->packAdd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
+ "socket disconnect");
+ /*
+ byte SSH_MSG_IGNORE
+ string data
+ */
+
+ case SSH_MSG_IGNORE:
+ if(datalen >= 2) {
+ if(session->ssh_msg_ignore) {
+ LIBSSH2_IGNORE(session, (char *) data + 1, datalen - 1);
+ }
+ }
+ else if(session->ssh_msg_ignore) {
+ LIBSSH2_IGNORE(session, "", 0);
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_DEBUG
+ boolean always_display
+ string message in ISO-10646 UTF-8 encoding [RFC3629]
+ string language tag [RFC3066]
+ */
+
+ case SSH_MSG_DEBUG:
+ if(datalen >= 2) {
+ int always_display = data[1];
+
+ if(datalen >= 6) {
+ struct string_buf buf;
+ buf.data = (unsigned char *)data;
+ buf.dataptr = buf.data;
+ buf.len = datalen;
+ buf.dataptr += 2; /* advance past type & always display */
+
+ _libssh2_get_string(&buf, &message, &message_len);
+ _libssh2_get_string(&buf, &language, &language_len);
+ }
+
+ if(session->ssh_msg_debug) {
+ LIBSSH2_DEBUG(session, always_display,
+ (const char *)message,
+ message_len, (const char *)language,
+ language_len);
+ }
+ }
+
+ /*
+ * _libssh2_debug will actually truncate this for us so
+ * that it's not an inordinate about of data
+ */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Debug Packet: %s", message);
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_GLOBAL_REQUEST
+ string request name in US-ASCII only
+ boolean want reply
+ .... request-specific data follows
+ */
+
+ case SSH_MSG_GLOBAL_REQUEST:
+ if(datalen >= 5) {
+ uint32_t len = 0;
+ unsigned char want_reply = 0;
+ len = _libssh2_ntohu32(data + 1);
+ if((len <= (UINT_MAX - 6)) && (datalen >= (6 + len))) {
+ want_reply = data[5 + len];
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_CONN,
+ "Received global request type %.*s (wr %X)",
+ len, data + 5, want_reply);
+ }
+
+
+ if(want_reply) {
+ static const unsigned char packet =
+ SSH_MSG_REQUEST_FAILURE;
+ libssh2_packet_add_jump_point5:
+ session->packAdd_state = libssh2_NB_state_jump5;
+ rc = _libssh2_transport_send(session, &packet, 1, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_CHANNEL_EXTENDED_DATA
+ uint32 recipient channel
+ uint32 data_type_code
+ string data
+ */
+
+ case SSH_MSG_CHANNEL_EXTENDED_DATA:
+ /* streamid(4) */
+ data_head += 4;
+
+ /* fall-through */
+
+ /*
+ byte SSH_MSG_CHANNEL_DATA
+ uint32 recipient channel
+ string data
+ */
+
+ case SSH_MSG_CHANNEL_DATA:
+ /* packet_type(1) + channelno(4) + datalen(4) */
+ data_head += 9;
+
+ if(datalen >= data_head)
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+
+ if(!channelp) {
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN,
+ "Packet received for unknown channel");
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+#ifdef LIBSSH2DEBUG
+ {
+ uint32_t stream_id = 0;
+ if(msg == SSH_MSG_CHANNEL_EXTENDED_DATA)
+ stream_id = _libssh2_ntohu32(data + 5);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "%d bytes packet_add() for %lu/%lu/%lu",
+ (int) (datalen - data_head),
+ channelp->local.id,
+ channelp->remote.id,
+ stream_id);
+ }
+#endif
+ if((channelp->remote.extended_data_ignore_mode ==
+ LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) &&
+ (msg == SSH_MSG_CHANNEL_EXTENDED_DATA)) {
+ /* Pretend we didn't receive this */
+ LIBSSH2_FREE(session, data);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Ignoring extended data and refunding %d bytes",
+ (int) (datalen - 13));
+ if(channelp->read_avail + datalen - data_head >=
+ channelp->remote.window_size)
+ datalen = channelp->remote.window_size -
+ channelp->read_avail + data_head;
+
+ channelp->remote.window_size -= datalen - data_head;
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "shrinking window size by %lu bytes to %lu, "
+ "read_avail %lu",
+ datalen - data_head,
+ channelp->remote.window_size,
+ channelp->read_avail);
+
+ session->packAdd_channelp = channelp;
+
+ /* Adjust the window based on the block we just freed */
+ libssh2_packet_add_jump_point1:
+ session->packAdd_state = libssh2_NB_state_jump1;
+ rc = _libssh2_channel_receive_window_adjust(session->
+ packAdd_channelp,
+ datalen - 13,
+ 1, NULL);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ /*
+ * REMEMBER! remote means remote as source of data,
+ * NOT remote window!
+ */
+ if(channelp->remote.packet_size < (datalen - data_head)) {
+ /*
+ * Spec says we MAY ignore bytes sent beyond
+ * packet_size
+ */
+ _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED,
+ "Packet contains more data than we offered"
+ " to receive, truncating");
+ datalen = channelp->remote.packet_size + data_head;
+ }
+ if(channelp->remote.window_size <= channelp->read_avail) {
+ /*
+ * Spec says we MAY ignore bytes sent beyond
+ * window_size
+ */
+ _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED,
+ "The current receive window is full,"
+ " data ignored");
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+ /* Reset EOF status */
+ channelp->remote.eof = 0;
+
+ if(channelp->read_avail + datalen - data_head >
+ channelp->remote.window_size) {
+ _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED,
+ "Remote sent more data than current "
+ "window allows, truncating");
+ datalen = channelp->remote.window_size -
+ channelp->read_avail + data_head;
+ }
+
+ /* Update the read_avail counter. The window size will be
+ * updated once the data is actually read from the queue
+ * from an upper layer */
+ channelp->read_avail += datalen - data_head;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "increasing read_avail by %lu bytes to %lu/%lu",
+ (long)(datalen - data_head),
+ (long)channelp->read_avail,
+ (long)channelp->remote.window_size);
+
+ break;
+
+ /*
+ byte SSH_MSG_CHANNEL_EOF
+ uint32 recipient channel
+ */
+
+ case SSH_MSG_CHANNEL_EOF:
+ if(datalen >= 5)
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+ if(!channelp)
+ /* We may have freed already, just quietly ignore this... */
+ ;
+ else {
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_CONN,
+ "EOF received for channel %lu/%lu",
+ channelp->local.id,
+ channelp->remote.id);
+ channelp->remote.eof = 1;
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_CHANNEL_REQUEST
+ uint32 recipient channel
+ string request type in US-ASCII characters only
+ boolean want reply
+ .... type-specific data follows
+ */
+
+ case SSH_MSG_CHANNEL_REQUEST:
+ if(datalen >= 9) {
+ uint32_t channel = _libssh2_ntohu32(data + 1);
+ uint32_t len = _libssh2_ntohu32(data + 5);
+ unsigned char want_reply = 1;
+
+ if((len + 9) < datalen)
+ want_reply = data[len + 9];
+
+ _libssh2_debug(session,
+ LIBSSH2_TRACE_CONN,
+ "Channel %d received request type %.*s (wr %X)",
+ channel, len, data + 9, want_reply);
+
+ if(len == sizeof("exit-status") - 1
+ && (sizeof("exit-status") - 1 + 9) <= datalen
+ && !memcmp("exit-status", data + 9,
+ sizeof("exit-status") - 1)) {
+
+ /* we've got "exit-status" packet. Set the session value */
+ if(datalen >= 20)
+ channelp =
+ _libssh2_channel_locate(session, channel);
+
+ if(channelp && (sizeof("exit-status") + 13) <= datalen) {
+ channelp->exit_status =
+ _libssh2_ntohu32(data + 9 + sizeof("exit-status"));
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Exit status %lu received for "
+ "channel %lu/%lu",
+ channelp->exit_status,
+ channelp->local.id,
+ channelp->remote.id);
+ }
+
+ }
+ else if(len == sizeof("exit-signal") - 1
+ && (sizeof("exit-signal") - 1 + 9) <= datalen
+ && !memcmp("exit-signal", data + 9,
+ sizeof("exit-signal") - 1)) {
+ /* command terminated due to signal */
+ if(datalen >= 20)
+ channelp = _libssh2_channel_locate(session, channel);
+
+ if(channelp && (sizeof("exit-signal") + 13) <= datalen) {
+ /* set signal name (without SIG prefix) */
+ uint32_t namelen =
+ _libssh2_ntohu32(data + 9 + sizeof("exit-signal"));
+
+ if(namelen <= UINT_MAX - 1) {
+ channelp->exit_signal =
+ LIBSSH2_ALLOC(session, namelen + 1);
+ }
+ else {
+ channelp->exit_signal = NULL;
+ }
+
+ if(!channelp->exit_signal)
+ rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "memory for signal name");
+ else if((sizeof("exit-signal") + 13 + namelen <=
+ datalen)) {
+ memcpy(channelp->exit_signal,
+ data + 13 + sizeof("exit-signal"), namelen);
+ channelp->exit_signal[namelen] = '\0';
+ /* TODO: save error message and language tag */
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Exit signal %s received for "
+ "channel %lu/%lu",
+ channelp->exit_signal,
+ channelp->local.id,
+ channelp->remote.id);
+ }
+ }
+ }
+
+
+ if(want_reply) {
+ unsigned char packet[5];
+ libssh2_packet_add_jump_point4:
+ session->packAdd_state = libssh2_NB_state_jump4;
+ packet[0] = SSH_MSG_CHANNEL_FAILURE;
+ memcpy(&packet[1], data + 1, 4);
+ rc = _libssh2_transport_send(session, packet, 5, NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return rc;
+
+ /*
+ byte SSH_MSG_CHANNEL_CLOSE
+ uint32 recipient channel
+ */
+
+ case SSH_MSG_CHANNEL_CLOSE:
+ if(datalen >= 5)
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+ if(!channelp) {
+ /* We may have freed already, just quietly ignore this... */
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Close received for channel %lu/%lu",
+ channelp->local.id,
+ channelp->remote.id);
+
+ channelp->remote.close = 1;
+ channelp->remote.eof = 1;
+
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+
+ /*
+ byte SSH_MSG_CHANNEL_OPEN
+ string "session"
+ uint32 sender channel
+ uint32 initial window size
+ uint32 maximum packet size
+ */
+
+ case SSH_MSG_CHANNEL_OPEN:
+ if(datalen < 17)
+ ;
+ else if((datalen >= (sizeof("forwarded-tcpip") + 4)) &&
+ ((sizeof("forwarded-tcpip") - 1) ==
+ _libssh2_ntohu32(data + 1))
+ &&
+ (memcmp(data + 5, "forwarded-tcpip",
+ sizeof("forwarded-tcpip") - 1) == 0)) {
+
+ /* init the state struct */
+ memset(&session->packAdd_Qlstn_state, 0,
+ sizeof(session->packAdd_Qlstn_state));
+
+ libssh2_packet_add_jump_point2:
+ session->packAdd_state = libssh2_NB_state_jump2;
+ rc = packet_queue_listener(session, data, datalen,
+ &session->packAdd_Qlstn_state);
+ }
+ else if((datalen >= (sizeof("x11") + 4)) &&
+ ((sizeof("x11") - 1) == _libssh2_ntohu32(data + 1)) &&
+ (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) {
+
+ /* init the state struct */
+ memset(&session->packAdd_x11open_state, 0,
+ sizeof(session->packAdd_x11open_state));
+
+ libssh2_packet_add_jump_point3:
+ session->packAdd_state = libssh2_NB_state_jump3;
+ rc = packet_x11_open(session, data, datalen,
+ &session->packAdd_x11open_state);
+ }
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return rc;
+
+ /*
+ byte SSH_MSG_CHANNEL_WINDOW_ADJUST
+ uint32 recipient channel
+ uint32 bytes to add
+ */
+ case SSH_MSG_CHANNEL_WINDOW_ADJUST:
+ if(datalen < 9)
+ ;
+ else {
+ uint32_t bytestoadd = _libssh2_ntohu32(data + 5);
+ channelp =
+ _libssh2_channel_locate(session,
+ _libssh2_ntohu32(data + 1));
+ if(channelp) {
+ channelp->local.window_size += bytestoadd;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Window adjust for channel %lu/%lu, "
+ "adding %lu bytes, new window_size=%lu",
+ channelp->local.id,
+ channelp->remote.id,
+ bytestoadd,
+ channelp->local.window_size);
+ }
+ }
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+ default:
+ break;
+ }
+
+ session->packAdd_state = libssh2_NB_state_sent;
+ }
+
+ if(session->packAdd_state == libssh2_NB_state_sent) {
+ LIBSSH2_PACKET *packetp =
+ LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET));
+ if(!packetp) {
+ _libssh2_debug(session, LIBSSH2_ERROR_ALLOC,
+ "memory for packet");
+ LIBSSH2_FREE(session, data);
+ session->packAdd_state = libssh2_NB_state_idle;
+ return LIBSSH2_ERROR_ALLOC;
+ }
+ packetp->data = data;
+ packetp->data_len = datalen;
+ packetp->data_head = data_head;
+
+ _libssh2_list_add(&session->packets, &packetp->node);
+
+ session->packAdd_state = libssh2_NB_state_sent1;
+ }
+
+ if((msg == SSH_MSG_KEXINIT &&
+ !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) ||
+ (session->packAdd_state == libssh2_NB_state_sent2)) {
+ if(session->packAdd_state == libssh2_NB_state_sent1) {
+ /*
+ * Remote wants new keys
+ * Well, it's already in the brigade,
+ * let's just call back into ourselves
+ */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Renegotiating Keys");
+
+ session->packAdd_state = libssh2_NB_state_sent2;
+ }
+
+ /*
+ * The KEXINIT message has been added to the queue. The packAdd and
+ * readPack states need to be reset because _libssh2_kex_exchange
+ * (eventually) calls upon _libssh2_transport_read to read the rest of
+ * the key exchange conversation.
+ */
+ session->readPack_state = libssh2_NB_state_idle;
+ session->packet.total_num = 0;
+ session->packAdd_state = libssh2_NB_state_idle;
+ session->fullpacket_state = libssh2_NB_state_idle;
+
+ memset(&session->startup_key_state, 0, sizeof(key_exchange_state_t));
+
+ /*
+ * If there was a key reexchange failure, let's just hope we didn't
+ * send NEWKEYS yet, otherwise remote will drop us like a rock
+ */
+ rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ session->packAdd_state = libssh2_NB_state_idle;
+ return 0;
+}
+
+/*
+ * _libssh2_packet_ask
+ *
+ * Scan the brigade for a matching packet type, optionally poll the socket for
+ * a packet first
+ */
+int
+_libssh2_packet_ask(LIBSSH2_SESSION * session, unsigned char packet_type,
+ unsigned char **data, size_t *data_len,
+ int match_ofs, const unsigned char *match_buf,
+ size_t match_len)
+{
+ LIBSSH2_PACKET *packet = _libssh2_list_first(&session->packets);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Looking for packet of type: %d", (int) packet_type);
+
+ while(packet) {
+ if(packet->data[0] == packet_type
+ && (packet->data_len >= (match_ofs + match_len))
+ && (!match_buf ||
+ (memcmp(packet->data + match_ofs, match_buf,
+ match_len) == 0))) {
+ *data = packet->data;
+ *data_len = packet->data_len;
+
+ /* unlink struct from session->packets */
+ _libssh2_list_remove(&packet->node);
+
+ LIBSSH2_FREE(session, packet);
+
+ return 0;
+ }
+ packet = _libssh2_list_next(&packet->node);
+ }
+ return -1;
+}
+
+/*
+ * libssh2_packet_askv
+ *
+ * Scan for any of a list of packet types in the brigade, optionally poll the
+ * socket for a packet first
+ */
+int
+_libssh2_packet_askv(LIBSSH2_SESSION * session,
+ const unsigned char *packet_types,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len)
+{
+ int i, packet_types_len = strlen((char *) packet_types);
+
+ for(i = 0; i < packet_types_len; i++) {
+ if(0 == _libssh2_packet_ask(session, packet_types[i], data,
+ data_len, match_ofs,
+ match_buf, match_len)) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * _libssh2_packet_require
+ *
+ * Loops _libssh2_transport_read() until the packet requested is available
+ * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
+ *
+ * Returns negative on error
+ * Returns 0 when it has taken care of the requested packet.
+ */
+int
+_libssh2_packet_require(LIBSSH2_SESSION * session, unsigned char packet_type,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len,
+ packet_require_state_t *state)
+{
+ if(state->start == 0) {
+ if(_libssh2_packet_ask(session, packet_type, data, data_len,
+ match_ofs, match_buf,
+ match_len) == 0) {
+ /* A packet was available in the packet brigade */
+ return 0;
+ }
+
+ state->start = time(NULL);
+ }
+
+ while(session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
+ int ret = _libssh2_transport_read(session);
+ if(ret == LIBSSH2_ERROR_EAGAIN)
+ return ret;
+ else if(ret < 0) {
+ state->start = 0;
+ /* an error which is not just because of blocking */
+ return ret;
+ }
+ else if(ret == packet_type) {
+ /* Be lazy, let packet_ask pull it out of the brigade */
+ ret = _libssh2_packet_ask(session, packet_type, data, data_len,
+ match_ofs, match_buf, match_len);
+ state->start = 0;
+ return ret;
+ }
+ else if(ret == 0) {
+ /* nothing available, wait until data arrives or we time out */
+ long left = LIBSSH2_READ_TIMEOUT - (long)(time(NULL) -
+ state->start);
+
+ if(left <= 0) {
+ state->start = 0;
+ return LIBSSH2_ERROR_TIMEOUT;
+ }
+ return -1; /* no packet available yet */
+ }
+ }
+
+ /* Only reached if the socket died */
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
+/*
+ * _libssh2_packet_burn
+ *
+ * Loops _libssh2_transport_read() until any packet is available and promptly
+ * discards it.
+ * Used during KEX exchange to discard badly guessed KEX_INIT packets
+ */
+int
+_libssh2_packet_burn(LIBSSH2_SESSION * session,
+ libssh2_nonblocking_states * state)
+{
+ unsigned char *data;
+ size_t data_len;
+ unsigned char i, all_packets[255];
+ int ret;
+
+ if(*state == libssh2_NB_state_idle) {
+ for(i = 1; i < 255; i++) {
+ all_packets[i - 1] = i;
+ }
+ all_packets[254] = 0;
+
+ if(_libssh2_packet_askv(session, all_packets, &data, &data_len, 0,
+ NULL, 0) == 0) {
+ i = data[0];
+ /* A packet was available in the packet brigade, burn it */
+ LIBSSH2_FREE(session, data);
+ return i;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Blocking until packet becomes available to burn");
+ *state = libssh2_NB_state_created;
+ }
+
+ while(session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
+ ret = _libssh2_transport_read(session);
+ if(ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+ else if(ret < 0) {
+ *state = libssh2_NB_state_idle;
+ return ret;
+ }
+ else if(ret == 0) {
+ /* FIXME: this might busyloop */
+ continue;
+ }
+
+ /* Be lazy, let packet_ask pull it out of the brigade */
+ if(0 ==
+ _libssh2_packet_ask(session, (unsigned char)ret,
+ &data, &data_len, 0, NULL, 0)) {
+ /* Smoke 'em if you got 'em */
+ LIBSSH2_FREE(session, data);
+ *state = libssh2_NB_state_idle;
+ return ret;
+ }
+ }
+
+ /* Only reached if the socket died */
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
+/*
+ * _libssh2_packet_requirev
+ *
+ * Loops _libssh2_transport_read() until one of a list of packet types
+ * requested is available. SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause
+ * a bailout. packet_types is a null terminated list of packet_type numbers
+ */
+
+int
+_libssh2_packet_requirev(LIBSSH2_SESSION *session,
+ const unsigned char *packet_types,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf, size_t match_len,
+ packet_requirev_state_t * state)
+{
+ if(_libssh2_packet_askv(session, packet_types, data, data_len, match_ofs,
+ match_buf, match_len) == 0) {
+ /* One of the packets listed was available in the packet brigade */
+ state->start = 0;
+ return 0;
+ }
+
+ if(state->start == 0) {
+ state->start = time(NULL);
+ }
+
+ while(session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) {
+ int ret = _libssh2_transport_read(session);
+ if((ret < 0) && (ret != LIBSSH2_ERROR_EAGAIN)) {
+ state->start = 0;
+ return ret;
+ }
+ if(ret <= 0) {
+ long left = LIBSSH2_READ_TIMEOUT -
+ (long)(time(NULL) - state->start);
+
+ if(left <= 0) {
+ state->start = 0;
+ return LIBSSH2_ERROR_TIMEOUT;
+ }
+ else if(ret == LIBSSH2_ERROR_EAGAIN) {
+ return ret;
+ }
+ }
+
+ if(strchr((char *) packet_types, ret)) {
+ /* Be lazy, let packet_ask pull it out of the brigade */
+ int ret = _libssh2_packet_askv(session, packet_types, data,
+ data_len, match_ofs, match_buf,
+ match_len);
+ state->start = 0;
+ return ret;
+ }
+ }
+
+ /* Only reached if the socket died */
+ state->start = 0;
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
diff --git a/contrib/libs/libssh2/src/packet.h b/contrib/libs/libssh2/src/packet.h
new file mode 100644
index 00000000000..79018bcf1d5
--- /dev/null
+++ b/contrib/libs/libssh2/src/packet.h
@@ -0,0 +1,76 @@
+#ifndef __LIBSSH2_PACKET_H
+#define __LIBSSH2_PACKET_H
+/*
+ * Copyright (C) 2010 by Daniel Stenberg
+ * Author: Daniel Stenberg <[email protected]>
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.
+ *
+ */
+
+int _libssh2_packet_read(LIBSSH2_SESSION * session);
+
+int _libssh2_packet_ask(LIBSSH2_SESSION * session, unsigned char packet_type,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len);
+
+int _libssh2_packet_askv(LIBSSH2_SESSION * session,
+ const unsigned char *packet_types,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len);
+int _libssh2_packet_require(LIBSSH2_SESSION * session,
+ unsigned char packet_type, unsigned char **data,
+ size_t *data_len, int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len,
+ packet_require_state_t * state);
+int _libssh2_packet_requirev(LIBSSH2_SESSION *session,
+ const unsigned char *packet_types,
+ unsigned char **data, size_t *data_len,
+ int match_ofs,
+ const unsigned char *match_buf,
+ size_t match_len,
+ packet_requirev_state_t * state);
+int _libssh2_packet_burn(LIBSSH2_SESSION * session,
+ libssh2_nonblocking_states * state);
+int _libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data,
+ unsigned long data_len);
+int _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
+ size_t datalen, int macstate);
+
+#endif /* __LIBSSH2_PACKET_H */
diff --git a/contrib/libs/libssh2/src/pem.c b/contrib/libs/libssh2/src/pem.c
new file mode 100644
index 00000000000..3416bd528a4
--- /dev/null
+++ b/contrib/libs/libssh2/src/pem.c
@@ -0,0 +1,911 @@
+/* Copyright (C) 2007 The Written Word, Inc.
+ * Copyright (C) 2008, Simon Josefsson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+
+static int
+readline(char *line, int line_size, FILE * fp)
+{
+ size_t len;
+
+ if(!line) {
+ return -1;
+ }
+ if(!fgets(line, line_size, fp)) {
+ return -1;
+ }
+
+ if(*line) {
+ len = strlen(line);
+ if(len > 0 && line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+ }
+
+ if(*line) {
+ len = strlen(line);
+ if(len > 0 && line[len - 1] == '\r') {
+ line[len - 1] = '\0';
+ }
+ }
+
+ return 0;
+}
+
+static int
+readline_memory(char *line, size_t line_size,
+ const char *filedata, size_t filedata_len,
+ size_t *filedata_offset)
+{
+ size_t off, len;
+
+ off = *filedata_offset;
+
+ for(len = 0; off + len < filedata_len && len < line_size - 1; len++) {
+ if(filedata[off + len] == '\n' ||
+ filedata[off + len] == '\r') {
+ break;
+ }
+ }
+
+ if(len) {
+ memcpy(line, filedata + off, len);
+ *filedata_offset += len;
+ }
+
+ line[len] = '\0';
+ *filedata_offset += 1;
+
+ return 0;
+}
+
+#define LINE_SIZE 128
+
+static const char *crypt_annotation = "Proc-Type: 4,ENCRYPTED";
+
+static unsigned char hex_decode(char digit)
+{
+ return (digit >= 'A') ? 0xA + (digit - 'A') : (digit - '0');
+}
+
+int
+_libssh2_pem_parse(LIBSSH2_SESSION * session,
+ const char *headerbegin,
+ const char *headerend,
+ const unsigned char *passphrase,
+ FILE * fp, unsigned char **data, unsigned int *datalen)
+{
+ char line[LINE_SIZE];
+ unsigned char iv[LINE_SIZE];
+ char *b64data = NULL;
+ unsigned int b64datalen = 0;
+ int ret;
+ const LIBSSH2_CRYPT_METHOD *method = NULL;
+
+ do {
+ *line = '\0';
+
+ if(readline(line, LINE_SIZE, fp)) {
+ return -1;
+ }
+ }
+ while(strcmp(line, headerbegin) != 0);
+
+ if(readline(line, LINE_SIZE, fp)) {
+ return -1;
+ }
+
+ if(passphrase &&
+ memcmp(line, crypt_annotation, strlen(crypt_annotation)) == 0) {
+ const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
+ int i;
+
+ if(readline(line, LINE_SIZE, fp)) {
+ ret = -1;
+ goto out;
+ }
+
+ all_methods = libssh2_crypt_methods();
+ while((cur_method = *all_methods++)) {
+ if(*cur_method->pem_annotation &&
+ memcmp(line, cur_method->pem_annotation,
+ strlen(cur_method->pem_annotation)) == 0) {
+ method = cur_method;
+ memcpy(iv, line + strlen(method->pem_annotation) + 1,
+ 2*method->iv_len);
+ }
+ }
+
+ /* None of the available crypt methods were able to decrypt the key */
+ if(method == NULL)
+ return -1;
+
+ /* Decode IV from hex */
+ for(i = 0; i < method->iv_len; ++i) {
+ iv[i] = hex_decode(iv[2*i]) << 4;
+ iv[i] |= hex_decode(iv[2*i + 1]);
+ }
+
+ /* skip to the next line */
+ if(readline(line, LINE_SIZE, fp)) {
+ ret = -1;
+ goto out;
+ }
+ }
+
+ do {
+ if(*line) {
+ char *tmp;
+ size_t linelen;
+
+ linelen = strlen(line);
+ tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
+ if(!tmp) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for PEM parsing");
+ ret = -1;
+ goto out;
+ }
+ memcpy(tmp + b64datalen, line, linelen);
+ b64data = tmp;
+ b64datalen += linelen;
+ }
+
+ *line = '\0';
+
+ if(readline(line, LINE_SIZE, fp)) {
+ ret = -1;
+ goto out;
+ }
+ } while(strcmp(line, headerend) != 0);
+
+ if(!b64data) {
+ return -1;
+ }
+
+ if(libssh2_base64_decode(session, (char **) data, datalen,
+ b64data, b64datalen)) {
+ ret = -1;
+ goto out;
+ }
+
+ if(method) {
+ /* Set up decryption */
+ int free_iv = 0, free_secret = 0, len_decrypted = 0, padding = 0;
+ int blocksize = method->blocksize;
+ void *abstract;
+ unsigned char secret[2*MD5_DIGEST_LENGTH];
+ libssh2_md5_ctx fingerprint_ctx;
+
+ /* Perform key derivation (PBKDF1/MD5) */
+ if(!libssh2_md5_init(&fingerprint_ctx)) {
+ ret = -1;
+ goto out;
+ }
+ libssh2_md5_update(fingerprint_ctx, passphrase,
+ strlen((char *)passphrase));
+ libssh2_md5_update(fingerprint_ctx, iv, 8);
+ libssh2_md5_final(fingerprint_ctx, secret);
+ if(method->secret_len > MD5_DIGEST_LENGTH) {
+ if(!libssh2_md5_init(&fingerprint_ctx)) {
+ ret = -1;
+ goto out;
+ }
+ libssh2_md5_update(fingerprint_ctx, secret, MD5_DIGEST_LENGTH);
+ libssh2_md5_update(fingerprint_ctx, passphrase,
+ strlen((char *)passphrase));
+ libssh2_md5_update(fingerprint_ctx, iv, 8);
+ libssh2_md5_final(fingerprint_ctx, secret + MD5_DIGEST_LENGTH);
+ }
+
+ /* Initialize the decryption */
+ if(method->init(session, method, iv, &free_iv, secret,
+ &free_secret, 0, &abstract)) {
+ _libssh2_explicit_zero((char *)secret, sizeof(secret));
+ LIBSSH2_FREE(session, data);
+ ret = -1;
+ goto out;
+ }
+
+ if(free_secret) {
+ _libssh2_explicit_zero((char *)secret, sizeof(secret));
+ }
+
+ /* Do the actual decryption */
+ if((*datalen % blocksize) != 0) {
+ _libssh2_explicit_zero((char *)secret, sizeof(secret));
+ method->dtor(session, &abstract);
+ _libssh2_explicit_zero(*data, *datalen);
+ LIBSSH2_FREE(session, *data);
+ ret = -1;
+ goto out;
+ }
+
+ while(len_decrypted <= (int)*datalen - blocksize) {
+ if(method->crypt(session, *data + len_decrypted, blocksize,
+ &abstract)) {
+ ret = LIBSSH2_ERROR_DECRYPT;
+ _libssh2_explicit_zero((char *)secret, sizeof(secret));
+ method->dtor(session, &abstract);
+ _libssh2_explicit_zero(*data, *datalen);
+ LIBSSH2_FREE(session, *data);
+ goto out;
+ }
+
+ len_decrypted += blocksize;
+ }
+
+ /* Account for padding */
+ padding = (*data)[*datalen - 1];
+ memset(&(*data)[*datalen-padding], 0, padding);
+ *datalen -= padding;
+
+ /* Clean up */
+ _libssh2_explicit_zero((char *)secret, sizeof(secret));
+ method->dtor(session, &abstract);
+ }
+
+ ret = 0;
+ out:
+ if(b64data) {
+ _libssh2_explicit_zero(b64data, b64datalen);
+ LIBSSH2_FREE(session, b64data);
+ }
+ return ret;
+}
+
+int
+_libssh2_pem_parse_memory(LIBSSH2_SESSION * session,
+ const char *headerbegin,
+ const char *headerend,
+ const char *filedata, size_t filedata_len,
+ unsigned char **data, unsigned int *datalen)
+{
+ char line[LINE_SIZE];
+ char *b64data = NULL;
+ unsigned int b64datalen = 0;
+ size_t off = 0;
+ int ret;
+
+ do {
+ *line = '\0';
+
+ if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
+ return -1;
+ }
+ }
+ while(strcmp(line, headerbegin) != 0);
+
+ *line = '\0';
+
+ do {
+ if(*line) {
+ char *tmp;
+ size_t linelen;
+
+ linelen = strlen(line);
+ tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
+ if(!tmp) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for PEM parsing");
+ ret = -1;
+ goto out;
+ }
+ memcpy(tmp + b64datalen, line, linelen);
+ b64data = tmp;
+ b64datalen += linelen;
+ }
+
+ *line = '\0';
+
+ if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
+ ret = -1;
+ goto out;
+ }
+ } while(strcmp(line, headerend) != 0);
+
+ if(!b64data) {
+ return -1;
+ }
+
+ if(libssh2_base64_decode(session, (char **) data, datalen,
+ b64data, b64datalen)) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = 0;
+ out:
+ if(b64data) {
+ _libssh2_explicit_zero(b64data, b64datalen);
+ LIBSSH2_FREE(session, b64data);
+ }
+ return ret;
+}
+
+/* OpenSSH formatted keys */
+#define AUTH_MAGIC "openssh-key-v1"
+#define OPENSSH_HEADER_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----"
+#define OPENSSH_HEADER_END "-----END OPENSSH PRIVATE KEY-----"
+
+static int
+_libssh2_openssh_pem_parse_data(LIBSSH2_SESSION * session,
+ const unsigned char *passphrase,
+ const char *b64data, size_t b64datalen,
+ struct string_buf **decrypted_buf)
+{
+ const LIBSSH2_CRYPT_METHOD *method = NULL;
+ struct string_buf decoded, decrypted, kdf_buf;
+ unsigned char *ciphername = NULL;
+ unsigned char *kdfname = NULL;
+ unsigned char *kdf = NULL;
+ unsigned char *buf = NULL;
+ unsigned char *salt = NULL;
+ uint32_t nkeys, check1, check2;
+ uint32_t rounds = 0;
+ unsigned char *key = NULL;
+ unsigned char *key_part = NULL;
+ unsigned char *iv_part = NULL;
+ unsigned char *f = NULL;
+ unsigned int f_len = 0;
+ int ret = 0, keylen = 0, ivlen = 0, total_len = 0;
+ size_t kdf_len = 0, tmp_len = 0, salt_len = 0;
+
+ if(decrypted_buf)
+ *decrypted_buf = NULL;
+
+ /* decode file */
+ if(libssh2_base64_decode(session, (char **)&f, &f_len,
+ b64data, b64datalen)) {
+ ret = -1;
+ goto out;
+ }
+
+ /* Parse the file */
+ decoded.data = (unsigned char *)f;
+ decoded.dataptr = (unsigned char *)f;
+ decoded.len = f_len;
+
+ if(decoded.len < strlen(AUTH_MAGIC)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO, "key too short");
+ goto out;
+ }
+
+ if(strncmp((char *) decoded.dataptr, AUTH_MAGIC,
+ strlen(AUTH_MAGIC)) != 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "key auth magic mismatch");
+ goto out;
+ }
+
+ decoded.dataptr += strlen(AUTH_MAGIC) + 1;
+
+ if(_libssh2_get_string(&decoded, &ciphername, &tmp_len) ||
+ tmp_len == 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "ciphername is missing");
+ goto out;
+ }
+
+ if(_libssh2_get_string(&decoded, &kdfname, &tmp_len) ||
+ tmp_len == 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "kdfname is missing");
+ goto out;
+ }
+
+ if(_libssh2_get_string(&decoded, &kdf, &kdf_len)) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "kdf is missing");
+ goto out;
+ }
+ else {
+ kdf_buf.data = kdf;
+ kdf_buf.dataptr = kdf;
+ kdf_buf.len = kdf_len;
+ }
+
+ if((passphrase == NULL || strlen((const char *)passphrase) == 0) &&
+ strcmp((const char *)ciphername, "none") != 0) {
+ /* passphrase required */
+ ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
+ goto out;
+ }
+
+ if(strcmp((const char *)kdfname, "none") != 0 &&
+ strcmp((const char *)kdfname, "bcrypt") != 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "unknown cipher");
+ goto out;
+ }
+
+ if(!strcmp((const char *)kdfname, "none") &&
+ strcmp((const char *)ciphername, "none") != 0) {
+ ret =_libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "invalid format");
+ goto out;
+ }
+
+ if(_libssh2_get_u32(&decoded, &nkeys) != 0 || nkeys != 1) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Multiple keys are unsupported");
+ goto out;
+ }
+
+ /* unencrypted public key */
+
+ if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Invalid private key; "
+ "expect embedded public key");
+ goto out;
+ }
+
+ if(_libssh2_get_string(&decoded, &buf, &tmp_len) || tmp_len == 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Private key data not found");
+ goto out;
+ }
+
+ /* decode encrypted private key */
+ decrypted.data = decrypted.dataptr = buf;
+ decrypted.len = tmp_len;
+
+ if(ciphername && strcmp((const char *)ciphername, "none") != 0) {
+ const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method;
+
+ all_methods = libssh2_crypt_methods();
+ while((cur_method = *all_methods++)) {
+ if(*cur_method->name &&
+ memcmp(ciphername, cur_method->name,
+ strlen(cur_method->name)) == 0) {
+ method = cur_method;
+ }
+ }
+
+ /* None of the available crypt methods were able to decrypt the key */
+
+ if(method == NULL) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "No supported cipher found");
+ goto out;
+ }
+ }
+
+ if(method) {
+ int free_iv = 0, free_secret = 0, len_decrypted = 0;
+ int blocksize;
+ void *abstract = NULL;
+
+ keylen = method->secret_len;
+ ivlen = method->iv_len;
+ total_len = keylen + ivlen;
+
+ key = LIBSSH2_CALLOC(session, total_len);
+ if(key == NULL) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Could not alloc key");
+ goto out;
+ }
+
+ if(strcmp((const char *)kdfname, "bcrypt") == 0 &&
+ passphrase != NULL) {
+ if((_libssh2_get_string(&kdf_buf, &salt, &salt_len)) ||
+ (_libssh2_get_u32(&kdf_buf, &rounds) != 0) ) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "kdf contains unexpected values");
+ LIBSSH2_FREE(session, key);
+ goto out;
+ }
+
+ if(_libssh2_bcrypt_pbkdf((const char *)passphrase,
+ strlen((const char *)passphrase),
+ salt, salt_len, key,
+ keylen + ivlen, rounds) < 0) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_DECRYPT,
+ "invalid format");
+ LIBSSH2_FREE(session, key);
+ goto out;
+ }
+ }
+ else {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_KEYFILE_AUTH_FAILED,
+ "bcrypted without passphrase");
+ LIBSSH2_FREE(session, key);
+ goto out;
+ }
+
+ /* Set up decryption */
+ blocksize = method->blocksize;
+
+ key_part = LIBSSH2_CALLOC(session, keylen);
+ if(key_part == NULL) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Could not alloc key part");
+ goto out;
+ }
+
+ iv_part = LIBSSH2_CALLOC(session, ivlen);
+ if(iv_part == NULL) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Could not alloc iv part");
+ goto out;
+ }
+
+ memcpy(key_part, key, keylen);
+ memcpy(iv_part, key + keylen, ivlen);
+
+ /* Initialize the decryption */
+ if(method->init(session, method, iv_part, &free_iv, key_part,
+ &free_secret, 0, &abstract)) {
+ ret = LIBSSH2_ERROR_DECRYPT;
+ goto out;
+ }
+
+ /* Do the actual decryption */
+ if((decrypted.len % blocksize) != 0) {
+ method->dtor(session, &abstract);
+ ret = LIBSSH2_ERROR_DECRYPT;
+ goto out;
+ }
+
+ while((size_t)len_decrypted <= decrypted.len - blocksize) {
+ if(method->crypt(session, decrypted.data + len_decrypted,
+ blocksize,
+ &abstract)) {
+ ret = LIBSSH2_ERROR_DECRYPT;
+ method->dtor(session, &abstract);
+ goto out;
+ }
+
+ len_decrypted += blocksize;
+ }
+
+ /* No padding */
+
+ method->dtor(session, &abstract);
+ }
+
+ /* Check random bytes match */
+
+ if(_libssh2_get_u32(&decrypted, &check1) != 0 ||
+ _libssh2_get_u32(&decrypted, &check2) != 0 ||
+ check1 != check2) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Private key unpack failed (correct password?)");
+ ret = LIBSSH2_ERROR_KEYFILE_AUTH_FAILED;
+ goto out;
+ }
+
+ if(decrypted_buf != NULL) {
+ /* copy data to out-going buffer */
+ struct string_buf *out_buf = _libssh2_string_buf_new(session);
+ if(!out_buf) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "decrypted struct");
+ goto out;
+ }
+
+ out_buf->data = LIBSSH2_CALLOC(session, decrypted.len);
+ if(out_buf->data == NULL) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "decrypted struct");
+ _libssh2_string_buf_free(session, out_buf);
+ goto out;
+ }
+ memcpy(out_buf->data, decrypted.data, decrypted.len);
+ out_buf->dataptr = out_buf->data +
+ (decrypted.dataptr - decrypted.data);
+ out_buf->len = decrypted.len;
+
+ *decrypted_buf = out_buf;
+ }
+
+out:
+
+ /* Clean up */
+ if(key) {
+ _libssh2_explicit_zero(key, total_len);
+ LIBSSH2_FREE(session, key);
+ }
+ if(key_part) {
+ _libssh2_explicit_zero(key_part, keylen);
+ LIBSSH2_FREE(session, key_part);
+ }
+ if(iv_part) {
+ _libssh2_explicit_zero(iv_part, ivlen);
+ LIBSSH2_FREE(session, iv_part);
+ }
+ if(f) {
+ _libssh2_explicit_zero(f, f_len);
+ LIBSSH2_FREE(session, f);
+ }
+
+ return ret;
+}
+
+int
+_libssh2_openssh_pem_parse(LIBSSH2_SESSION * session,
+ const unsigned char *passphrase,
+ FILE * fp, struct string_buf **decrypted_buf)
+{
+ char line[LINE_SIZE];
+ char *b64data = NULL;
+ unsigned int b64datalen = 0;
+ int ret = 0;
+
+ /* read file */
+
+ do {
+ *line = '\0';
+
+ if(readline(line, LINE_SIZE, fp)) {
+ return -1;
+ }
+ }
+ while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
+
+ if(readline(line, LINE_SIZE, fp)) {
+ return -1;
+ }
+
+ do {
+ if(*line) {
+ char *tmp;
+ size_t linelen;
+
+ linelen = strlen(line);
+ tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
+ if(!tmp) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for PEM parsing");
+ ret = -1;
+ goto out;
+ }
+ memcpy(tmp + b64datalen, line, linelen);
+ b64data = tmp;
+ b64datalen += linelen;
+ }
+
+ *line = '\0';
+
+ if(readline(line, LINE_SIZE, fp)) {
+ ret = -1;
+ goto out;
+ }
+ } while(strcmp(line, OPENSSH_HEADER_END) != 0);
+
+ if(!b64data) {
+ return -1;
+ }
+
+ ret = _libssh2_openssh_pem_parse_data(session,
+ passphrase,
+ (const char *)b64data,
+ (size_t)b64datalen,
+ decrypted_buf);
+
+ if(b64data) {
+ _libssh2_explicit_zero(b64data, b64datalen);
+ LIBSSH2_FREE(session, b64data);
+ }
+
+out:
+
+ return ret;
+}
+
+int
+_libssh2_openssh_pem_parse_memory(LIBSSH2_SESSION * session,
+ const unsigned char *passphrase,
+ const char *filedata, size_t filedata_len,
+ struct string_buf **decrypted_buf)
+{
+ char line[LINE_SIZE];
+ char *b64data = NULL;
+ unsigned int b64datalen = 0;
+ size_t off = 0;
+ int ret;
+
+ if(filedata == NULL || filedata_len <= 0)
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Error parsing PEM: filedata missing");
+
+ do {
+
+ *line = '\0';
+
+ if(off >= filedata_len)
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Error parsing PEM: offset out of bounds");
+
+ if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
+ return -1;
+ }
+ }
+ while(strcmp(line, OPENSSH_HEADER_BEGIN) != 0);
+
+ *line = '\0';
+
+ do {
+ if (*line) {
+ char *tmp;
+ size_t linelen;
+
+ linelen = strlen(line);
+ tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen);
+ if(!tmp) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "PEM parsing");
+ goto out;
+ }
+ memcpy(tmp + b64datalen, line, linelen);
+ b64data = tmp;
+ b64datalen += linelen;
+ }
+
+ *line = '\0';
+
+ if(off >= filedata_len) {
+ ret = _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Error parsing PEM: offset out of bounds");
+ goto out;
+ }
+
+ if(readline_memory(line, LINE_SIZE, filedata, filedata_len, &off)) {
+ ret = -1;
+ goto out;
+ }
+ } while(strcmp(line, OPENSSH_HEADER_END) != 0);
+
+ if(!b64data)
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Error parsing PEM: base 64 data missing");
+
+ ret = _libssh2_openssh_pem_parse_data(session, passphrase, b64data,
+ b64datalen, decrypted_buf);
+
+out:
+ if(b64data) {
+ _libssh2_explicit_zero(b64data, b64datalen);
+ LIBSSH2_FREE(session, b64data);
+ }
+ return ret;
+
+}
+
+static int
+read_asn1_length(const unsigned char *data,
+ unsigned int datalen, unsigned int *len)
+{
+ unsigned int lenlen;
+ int nextpos;
+
+ if(datalen < 1) {
+ return -1;
+ }
+ *len = data[0];
+
+ if(*len >= 0x80) {
+ lenlen = *len & 0x7F;
+ *len = data[1];
+ if(1 + lenlen > datalen) {
+ return -1;
+ }
+ if(lenlen > 1) {
+ *len <<= 8;
+ *len |= data[2];
+ }
+ }
+ else {
+ lenlen = 0;
+ }
+
+ nextpos = 1 + lenlen;
+ if(lenlen > 2 || 1 + lenlen + *len > datalen) {
+ return -1;
+ }
+
+ return nextpos;
+}
+
+int
+_libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen)
+{
+ unsigned int len;
+ int lenlen;
+
+ if(*datalen < 1) {
+ return -1;
+ }
+
+ if((*data)[0] != '\x30') {
+ return -1;
+ }
+
+ (*data)++;
+ (*datalen)--;
+
+ lenlen = read_asn1_length(*data, *datalen, &len);
+ if(lenlen < 0 || lenlen + len != *datalen) {
+ return -1;
+ }
+
+ *data += lenlen;
+ *datalen -= lenlen;
+
+ return 0;
+}
+
+int
+_libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen,
+ unsigned char **i, unsigned int *ilen)
+{
+ unsigned int len;
+ int lenlen;
+
+ if(*datalen < 1) {
+ return -1;
+ }
+
+ if((*data)[0] != '\x02') {
+ return -1;
+ }
+
+ (*data)++;
+ (*datalen)--;
+
+ lenlen = read_asn1_length(*data, *datalen, &len);
+ if(lenlen < 0 || lenlen + len > *datalen) {
+ return -1;
+ }
+
+ *data += lenlen;
+ *datalen -= lenlen;
+
+ *i = *data;
+ *ilen = len;
+
+ *data += len;
+ *datalen -= len;
+
+ return 0;
+}
diff --git a/contrib/libs/libssh2/src/publickey.c b/contrib/libs/libssh2/src/publickey.c
new file mode 100644
index 00000000000..f26c6327dcf
--- /dev/null
+++ b/contrib/libs/libssh2/src/publickey.c
@@ -0,0 +1,1278 @@
+/* Copyright (c) 2004-2007, Sara Golemon <[email protected]>
+ * Copyright (c) 2010-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include "libssh2_publickey.h"
+#include "channel.h"
+#include "session.h"
+
+#define LIBSSH2_PUBLICKEY_VERSION 2
+
+/* Numericised response codes -- Not IETF, just local representation */
+#define LIBSSH2_PUBLICKEY_RESPONSE_STATUS 0
+#define LIBSSH2_PUBLICKEY_RESPONSE_VERSION 1
+#define LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY 2
+
+typedef struct _LIBSSH2_PUBLICKEY_CODE_LIST
+{
+ int code;
+ const char *name;
+ int name_len;
+} LIBSSH2_PUBLICKEY_CODE_LIST;
+
+static const LIBSSH2_PUBLICKEY_CODE_LIST publickey_response_codes[] =
+{
+ {LIBSSH2_PUBLICKEY_RESPONSE_STATUS, "status", sizeof("status") - 1},
+ {LIBSSH2_PUBLICKEY_RESPONSE_VERSION, "version", sizeof("version") - 1},
+ {LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY, "publickey",
+ sizeof("publickey") - 1},
+ {0, NULL, 0}
+};
+
+/* PUBLICKEY status codes -- IETF defined */
+#define LIBSSH2_PUBLICKEY_SUCCESS 0
+#define LIBSSH2_PUBLICKEY_ACCESS_DENIED 1
+#define LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED 2
+#define LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED 3
+#define LIBSSH2_PUBLICKEY_KEY_NOT_FOUND 4
+#define LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED 5
+#define LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT 6
+#define LIBSSH2_PUBLICKEY_GENERAL_FAILURE 7
+#define LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED 8
+
+#define LIBSSH2_PUBLICKEY_STATUS_CODE_MAX 8
+
+static const LIBSSH2_PUBLICKEY_CODE_LIST publickey_status_codes[] = {
+ {LIBSSH2_PUBLICKEY_SUCCESS, "success", sizeof("success") - 1},
+ {LIBSSH2_PUBLICKEY_ACCESS_DENIED, "access denied",
+ sizeof("access denied") - 1},
+ {LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED, "storage exceeded",
+ sizeof("storage exceeded") - 1},
+ {LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED, "version not supported",
+ sizeof("version not supported") - 1},
+ {LIBSSH2_PUBLICKEY_KEY_NOT_FOUND, "key not found",
+ sizeof("key not found") - 1},
+ {LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED, "key not supported",
+ sizeof("key not supported") - 1},
+ {LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT, "key already present",
+ sizeof("key already present") - 1},
+ {LIBSSH2_PUBLICKEY_GENERAL_FAILURE, "general failure",
+ sizeof("general failure") - 1},
+ {LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED, "request not supported",
+ sizeof("request not supported") - 1},
+ {0, NULL, 0}
+};
+
+/*
+ * publickey_status_error
+ *
+ * Format an error message from a status code
+ */
+static void
+publickey_status_error(const LIBSSH2_PUBLICKEY *pkey,
+ LIBSSH2_SESSION *session, int status)
+{
+ const char *msg;
+
+ /* GENERAL_FAILURE got remapped between version 1 and 2 */
+ if(status == 6 && pkey && pkey->version == 1) {
+ status = 7;
+ }
+
+ if(status < 0 || status > LIBSSH2_PUBLICKEY_STATUS_CODE_MAX) {
+ msg = "unknown";
+ }
+ else {
+ msg = publickey_status_codes[status].name;
+ }
+
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, msg);
+}
+
+/*
+ * publickey_packet_receive
+ *
+ * Read a packet from the subsystem
+ */
+static int
+publickey_packet_receive(LIBSSH2_PUBLICKEY * pkey,
+ unsigned char **data, size_t *data_len)
+{
+ LIBSSH2_CHANNEL *channel = pkey->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char buffer[4];
+ int rc;
+ *data = NULL; /* default to nothing returned */
+ *data_len = 0;
+
+ if(pkey->receive_state == libssh2_NB_state_idle) {
+ rc = _libssh2_channel_read(channel, 0, (char *) buffer, 4);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc != 4) {
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Invalid response from publickey subsystem");
+ }
+
+ pkey->receive_packet_len = _libssh2_ntohu32(buffer);
+ pkey->receive_packet =
+ LIBSSH2_ALLOC(session, pkey->receive_packet_len);
+ if(!pkey->receive_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate publickey response "
+ "buffer");
+ }
+
+ pkey->receive_state = libssh2_NB_state_sent;
+ }
+
+ if(pkey->receive_state == libssh2_NB_state_sent) {
+ rc = _libssh2_channel_read(channel, 0, (char *) pkey->receive_packet,
+ pkey->receive_packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc != (int)pkey->receive_packet_len) {
+ LIBSSH2_FREE(session, pkey->receive_packet);
+ pkey->receive_packet = NULL;
+ pkey->receive_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
+ "Timeout waiting for publickey subsystem "
+ "response packet");
+ }
+
+ *data = pkey->receive_packet;
+ *data_len = pkey->receive_packet_len;
+ }
+
+ pkey->receive_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/* publickey_response_id
+ *
+ * Translate a string response name to a numeric code
+ * Data will be incremented by 4 + response_len on success only
+ */
+static int
+publickey_response_id(unsigned char **pdata, size_t data_len)
+{
+ size_t response_len;
+ unsigned char *data = *pdata;
+ const LIBSSH2_PUBLICKEY_CODE_LIST *codes = publickey_response_codes;
+
+ if(data_len < 4) {
+ /* Malformed response */
+ return -1;
+ }
+ response_len = _libssh2_ntohu32(data);
+ data += 4;
+ data_len -= 4;
+ if(data_len < response_len) {
+ /* Malformed response */
+ return -1;
+ }
+
+ while(codes->name) {
+ if((unsigned long)codes->name_len == response_len &&
+ strncmp(codes->name, (char *) data, response_len) == 0) {
+ *pdata = data + response_len;
+ return codes->code;
+ }
+ codes++;
+ }
+
+ return -1;
+}
+
+/* publickey_response_success
+ *
+ * Generic helper routine to wait for success response and nothing else
+ */
+static int
+publickey_response_success(LIBSSH2_PUBLICKEY * pkey)
+{
+ LIBSSH2_SESSION *session = pkey->channel->session;
+ unsigned char *data, *s;
+ size_t data_len;
+ int response;
+
+ while(1) {
+ int rc = publickey_packet_receive(pkey, &data, &data_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
+ "Timeout waiting for response from "
+ "publickey subsystem");
+ }
+
+ if(data_len < 4) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Publickey response too small");
+ }
+
+ s = data;
+ response = publickey_response_id(&s, data_len);
+
+ switch(response) {
+ case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
+ /* Error, or processing complete */
+ {
+ unsigned long status = 0;
+
+ if(data_len < 8) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Publickey response too small");
+ }
+
+ status = _libssh2_ntohu32(s);
+
+ LIBSSH2_FREE(session, data);
+
+ if(status == LIBSSH2_PUBLICKEY_SUCCESS)
+ return 0;
+
+ publickey_status_error(pkey, session, status);
+ return -1;
+ }
+ default:
+ LIBSSH2_FREE(session, data);
+ if(response < 0) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Invalid publickey subsystem response");
+ }
+ /* Unknown/Unexpected */
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Unexpected publickey subsystem response");
+ data = NULL;
+ }
+ }
+ /* never reached, but include `return` to silence compiler warnings */
+ return -1;
+}
+
+/* *****************
+ * Publickey API *
+ ***************** */
+
+/*
+ * publickey_init
+ *
+ * Startup the publickey subsystem
+ */
+static LIBSSH2_PUBLICKEY *publickey_init(LIBSSH2_SESSION *session)
+{
+ int response;
+ int rc;
+
+ if(session->pkeyInit_state == libssh2_NB_state_idle) {
+ session->pkeyInit_data = NULL;
+ session->pkeyInit_pkey = NULL;
+ session->pkeyInit_channel = NULL;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Initializing publickey subsystem");
+
+ session->pkeyInit_state = libssh2_NB_state_allocated;
+ }
+
+ if(session->pkeyInit_state == libssh2_NB_state_allocated) {
+
+ session->pkeyInit_channel =
+ _libssh2_channel_open(session, "session",
+ sizeof("session") - 1,
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT,
+ LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL,
+ 0);
+ if(!session->pkeyInit_channel) {
+ if(libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN)
+ /* The error state is already set, so leave it */
+ return NULL;
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Unable to startup channel");
+ goto err_exit;
+ }
+
+ session->pkeyInit_state = libssh2_NB_state_sent;
+ }
+
+ if(session->pkeyInit_state == libssh2_NB_state_sent) {
+ rc = _libssh2_channel_process_startup(session->pkeyInit_channel,
+ "subsystem",
+ sizeof("subsystem") - 1,
+ "publickey",
+ sizeof("publickey") - 1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block starting publickey subsystem");
+ return NULL;
+ }
+ else if(rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Unable to request publickey subsystem");
+ goto err_exit;
+ }
+
+ session->pkeyInit_state = libssh2_NB_state_sent1;
+ }
+
+ if(session->pkeyInit_state == libssh2_NB_state_sent1) {
+ unsigned char *s;
+ rc = _libssh2_channel_extended_data(session->pkeyInit_channel,
+ LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block starting publickey subsystem");
+ return NULL;
+ }
+
+ session->pkeyInit_pkey =
+ LIBSSH2_CALLOC(session, sizeof(LIBSSH2_PUBLICKEY));
+ if(!session->pkeyInit_pkey) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a new publickey structure");
+ goto err_exit;
+ }
+ session->pkeyInit_pkey->channel = session->pkeyInit_channel;
+ session->pkeyInit_pkey->version = 0;
+
+ s = session->pkeyInit_buffer;
+ _libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4);
+ s += 4;
+ _libssh2_htonu32(s, sizeof("version") - 1);
+ s += 4;
+ memcpy(s, "version", sizeof("version") - 1);
+ s += sizeof("version") - 1;
+ _libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION);
+
+ session->pkeyInit_buffer_sent = 0;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Sending publickey advertising version %d support",
+ (int) LIBSSH2_PUBLICKEY_VERSION);
+
+ session->pkeyInit_state = libssh2_NB_state_sent2;
+ }
+
+ if(session->pkeyInit_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_channel_write(session->pkeyInit_channel, 0,
+ session->pkeyInit_buffer,
+ 19 - session->pkeyInit_buffer_sent);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending publickey version packet");
+ return NULL;
+ }
+ else if(rc < 0) {
+ _libssh2_error(session, rc,
+ "Unable to send publickey version packet");
+ goto err_exit;
+ }
+ session->pkeyInit_buffer_sent += rc;
+ if(session->pkeyInit_buffer_sent < 19) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Need to be called again to complete this");
+ return NULL;
+ }
+
+ session->pkeyInit_state = libssh2_NB_state_sent3;
+ }
+
+ if(session->pkeyInit_state == libssh2_NB_state_sent3) {
+ while(1) {
+ unsigned char *s;
+ rc = publickey_packet_receive(session->pkeyInit_pkey,
+ &session->pkeyInit_data,
+ &session->pkeyInit_data_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for response from "
+ "publickey subsystem");
+ return NULL;
+ }
+ else if(rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
+ "Timeout waiting for response from "
+ "publickey subsystem");
+ goto err_exit;
+ }
+
+ s = session->pkeyInit_data;
+ if((response =
+ publickey_response_id(&s, session->pkeyInit_data_len)) < 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Invalid publickey subsystem response code");
+ goto err_exit;
+ }
+
+ if(session->pkeyInit_data_len < 4) {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Public key init data too small");
+ goto err_exit;
+ }
+
+ switch(response) {
+ case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
+ /* Error */
+ {
+ unsigned long status, descr_len, lang_len;
+
+ if(session->pkeyInit_data_len >= 8) {
+ status = _libssh2_ntohu32(s);
+ s += 4;
+ descr_len = _libssh2_ntohu32(s);
+ s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Public key init data too small");
+ goto err_exit;
+ }
+
+ if(s + descr_len + 4 <=
+ session->pkeyInit_data + session->pkeyInit_data_len) {
+ /* description starts here */
+ s += descr_len;
+ lang_len = _libssh2_ntohu32(s);
+ s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Public key init data too small");
+ goto err_exit;
+ }
+
+ if(s + lang_len <=
+ session->pkeyInit_data + session->pkeyInit_data_len) {
+ /* lang starts here */
+ s += lang_len;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Public key init data too small");
+ goto err_exit;
+ }
+
+ if(s >
+ session->pkeyInit_data + session->pkeyInit_data_len) {
+ _libssh2_error(session,
+ LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Malformed publickey subsystem packet");
+ goto err_exit;
+ }
+
+ publickey_status_error(NULL, session, status);
+
+ goto err_exit;
+ }
+
+ case LIBSSH2_PUBLICKEY_RESPONSE_VERSION:
+ /* What we want */
+ session->pkeyInit_pkey->version = _libssh2_ntohu32(s);
+ if(session->pkeyInit_pkey->version >
+ LIBSSH2_PUBLICKEY_VERSION) {
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Truncate remote publickey version "
+ "from %lu",
+ session->pkeyInit_pkey->version);
+ session->pkeyInit_pkey->version =
+ LIBSSH2_PUBLICKEY_VERSION;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Enabling publickey subsystem version %lu",
+ session->pkeyInit_pkey->version);
+ LIBSSH2_FREE(session, session->pkeyInit_data);
+ session->pkeyInit_data = NULL;
+ session->pkeyInit_state = libssh2_NB_state_idle;
+ return session->pkeyInit_pkey;
+
+ default:
+ /* Unknown/Unexpected */
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Unexpected publickey subsystem response, "
+ "ignoring");
+ LIBSSH2_FREE(session, session->pkeyInit_data);
+ session->pkeyInit_data = NULL;
+ }
+ }
+ }
+
+ /* Never reached except by direct goto */
+ err_exit:
+ session->pkeyInit_state = libssh2_NB_state_sent4;
+ if(session->pkeyInit_channel) {
+ rc = _libssh2_channel_close(session->pkeyInit_channel);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block closing channel");
+ return NULL;
+ }
+ }
+ if(session->pkeyInit_pkey) {
+ LIBSSH2_FREE(session, session->pkeyInit_pkey);
+ session->pkeyInit_pkey = NULL;
+ }
+ if(session->pkeyInit_data) {
+ LIBSSH2_FREE(session, session->pkeyInit_data);
+ session->pkeyInit_data = NULL;
+ }
+ session->pkeyInit_state = libssh2_NB_state_idle;
+ return NULL;
+}
+
+/*
+ * libssh2_publickey_init
+ *
+ * Startup the publickey subsystem
+ */
+LIBSSH2_API LIBSSH2_PUBLICKEY *
+libssh2_publickey_init(LIBSSH2_SESSION *session)
+{
+ LIBSSH2_PUBLICKEY *ptr;
+
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ publickey_init(session));
+ return ptr;
+}
+
+
+
+/*
+ * libssh2_publickey_add_ex
+ *
+ * Add a new public key entry
+ */
+LIBSSH2_API int
+libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name,
+ unsigned long name_len, const unsigned char *blob,
+ unsigned long blob_len, char overwrite,
+ unsigned long num_attrs,
+ const libssh2_publickey_attribute attrs[])
+{
+ LIBSSH2_CHANNEL *channel;
+ LIBSSH2_SESSION *session;
+ /* 19 = packet_len(4) + add_len(4) + "add"(3) + name_len(4) + {name}
+ blob_len(4) + {blob} */
+ unsigned long i, packet_len = 19 + name_len + blob_len;
+ unsigned char *comment = NULL;
+ unsigned long comment_len = 0;
+ int rc;
+
+ if(!pkey)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ channel = pkey->channel;
+ session = channel->session;
+
+ if(pkey->add_state == libssh2_NB_state_idle) {
+ pkey->add_packet = NULL;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, "Adding %s publickey",
+ name);
+
+ if(pkey->version == 1) {
+ for(i = 0; i < num_attrs; i++) {
+ /* Search for a comment attribute */
+ if(attrs[i].name_len == (sizeof("comment") - 1) &&
+ strncmp(attrs[i].name, "comment",
+ sizeof("comment") - 1) == 0) {
+ comment = (unsigned char *) attrs[i].value;
+ comment_len = attrs[i].value_len;
+ break;
+ }
+ }
+ packet_len += 4 + comment_len;
+ }
+ else {
+ packet_len += 5; /* overwrite(1) + attribute_count(4) */
+ for(i = 0; i < num_attrs; i++) {
+ packet_len += 9 + attrs[i].name_len + attrs[i].value_len;
+ /* name_len(4) + value_len(4) + mandatory(1) */
+ }
+ }
+
+ pkey->add_packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!pkey->add_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "publickey \"add\" packet");
+ }
+
+ pkey->add_s = pkey->add_packet;
+ _libssh2_htonu32(pkey->add_s, packet_len - 4);
+ pkey->add_s += 4;
+ _libssh2_htonu32(pkey->add_s, sizeof("add") - 1);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, "add", sizeof("add") - 1);
+ pkey->add_s += sizeof("add") - 1;
+ if(pkey->version == 1) {
+ _libssh2_htonu32(pkey->add_s, comment_len);
+ pkey->add_s += 4;
+ if(comment) {
+ memcpy(pkey->add_s, comment, comment_len);
+ pkey->add_s += comment_len;
+ }
+
+ _libssh2_htonu32(pkey->add_s, name_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, name, name_len);
+ pkey->add_s += name_len;
+ _libssh2_htonu32(pkey->add_s, blob_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, blob, blob_len);
+ pkey->add_s += blob_len;
+ }
+ else {
+ /* Version == 2 */
+
+ _libssh2_htonu32(pkey->add_s, name_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, name, name_len);
+ pkey->add_s += name_len;
+ _libssh2_htonu32(pkey->add_s, blob_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, blob, blob_len);
+ pkey->add_s += blob_len;
+ *(pkey->add_s++) = overwrite ? 0x01 : 0;
+ _libssh2_htonu32(pkey->add_s, num_attrs);
+ pkey->add_s += 4;
+ for(i = 0; i < num_attrs; i++) {
+ _libssh2_htonu32(pkey->add_s, attrs[i].name_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, attrs[i].name, attrs[i].name_len);
+ pkey->add_s += attrs[i].name_len;
+ _libssh2_htonu32(pkey->add_s, attrs[i].value_len);
+ pkey->add_s += 4;
+ memcpy(pkey->add_s, attrs[i].value, attrs[i].value_len);
+ pkey->add_s += attrs[i].value_len;
+ *(pkey->add_s++) = attrs[i].mandatory ? 0x01 : 0;
+ }
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Sending publickey \"add\" packet: "
+ "type=%s blob_len=%ld num_attrs=%ld",
+ name, blob_len, num_attrs);
+
+ pkey->add_state = libssh2_NB_state_created;
+ }
+
+ if(pkey->add_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, pkey->add_packet,
+ (pkey->add_s - pkey->add_packet));
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if((pkey->add_s - pkey->add_packet) != rc) {
+ LIBSSH2_FREE(session, pkey->add_packet);
+ pkey->add_packet = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send publickey add packet");
+ }
+ LIBSSH2_FREE(session, pkey->add_packet);
+ pkey->add_packet = NULL;
+
+ pkey->add_state = libssh2_NB_state_sent;
+ }
+
+ rc = publickey_response_success(pkey);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+
+ pkey->add_state = libssh2_NB_state_idle;
+
+ return rc;
+}
+
+/* libssh2_publickey_remove_ex
+ * Remove an existing publickey so that authentication can no longer be
+ * performed using it
+ */
+LIBSSH2_API int
+libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY * pkey,
+ const unsigned char *name, unsigned long name_len,
+ const unsigned char *blob, unsigned long blob_len)
+{
+ LIBSSH2_CHANNEL *channel;
+ LIBSSH2_SESSION *session;
+ /* 22 = packet_len(4) + remove_len(4) + "remove"(6) + name_len(4) + {name}
+ + blob_len(4) + {blob} */
+ unsigned long packet_len = 22 + name_len + blob_len;
+ int rc;
+
+ if(!pkey)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ channel = pkey->channel;
+ session = channel->session;
+
+ if(pkey->remove_state == libssh2_NB_state_idle) {
+ pkey->remove_packet = NULL;
+
+ pkey->remove_packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!pkey->remove_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "publickey \"remove\" packet");
+ }
+
+ pkey->remove_s = pkey->remove_packet;
+ _libssh2_htonu32(pkey->remove_s, packet_len - 4);
+ pkey->remove_s += 4;
+ _libssh2_htonu32(pkey->remove_s, sizeof("remove") - 1);
+ pkey->remove_s += 4;
+ memcpy(pkey->remove_s, "remove", sizeof("remove") - 1);
+ pkey->remove_s += sizeof("remove") - 1;
+ _libssh2_htonu32(pkey->remove_s, name_len);
+ pkey->remove_s += 4;
+ memcpy(pkey->remove_s, name, name_len);
+ pkey->remove_s += name_len;
+ _libssh2_htonu32(pkey->remove_s, blob_len);
+ pkey->remove_s += 4;
+ memcpy(pkey->remove_s, blob, blob_len);
+ pkey->remove_s += blob_len;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Sending publickey \"remove\" packet: "
+ "type=%s blob_len=%ld",
+ name, blob_len);
+
+ pkey->remove_state = libssh2_NB_state_created;
+ }
+
+ if(pkey->remove_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, pkey->remove_packet,
+ (pkey->remove_s - pkey->remove_packet));
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if((pkey->remove_s - pkey->remove_packet) != rc) {
+ LIBSSH2_FREE(session, pkey->remove_packet);
+ pkey->remove_packet = NULL;
+ pkey->remove_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send publickey remove packet");
+ }
+ LIBSSH2_FREE(session, pkey->remove_packet);
+ pkey->remove_packet = NULL;
+
+ pkey->remove_state = libssh2_NB_state_sent;
+ }
+
+ rc = publickey_response_success(pkey);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+
+ pkey->remove_state = libssh2_NB_state_idle;
+
+ return rc;
+}
+
+/* libssh2_publickey_list_fetch
+ * Fetch a list of supported public key from a server
+ */
+LIBSSH2_API int
+libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY * pkey, unsigned long *num_keys,
+ libssh2_publickey_list ** pkey_list)
+{
+ LIBSSH2_CHANNEL *channel;
+ LIBSSH2_SESSION *session;
+ libssh2_publickey_list *list = NULL;
+ unsigned long buffer_len = 12, keys = 0, max_keys = 0, i;
+ /* 12 = packet_len(4) + list_len(4) + "list"(4) */
+ int response;
+ int rc;
+
+ if(!pkey)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ channel = pkey->channel;
+ session = channel->session;
+
+ if(pkey->listFetch_state == libssh2_NB_state_idle) {
+ pkey->listFetch_data = NULL;
+
+ pkey->listFetch_s = pkey->listFetch_buffer;
+ _libssh2_htonu32(pkey->listFetch_s, buffer_len - 4);
+ pkey->listFetch_s += 4;
+ _libssh2_htonu32(pkey->listFetch_s, sizeof("list") - 1);
+ pkey->listFetch_s += 4;
+ memcpy(pkey->listFetch_s, "list", sizeof("list") - 1);
+ pkey->listFetch_s += sizeof("list") - 1;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
+ "Sending publickey \"list\" packet");
+
+ pkey->listFetch_state = libssh2_NB_state_created;
+ }
+
+ if(pkey->listFetch_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0,
+ pkey->listFetch_buffer,
+ (pkey->listFetch_s -
+ pkey->listFetch_buffer));
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if((pkey->listFetch_s - pkey->listFetch_buffer) != rc) {
+ pkey->listFetch_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send publickey list packet");
+ }
+
+ pkey->listFetch_state = libssh2_NB_state_sent;
+ }
+
+ while(1) {
+ rc = publickey_packet_receive(pkey, &pkey->listFetch_data,
+ &pkey->listFetch_data_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
+ "Timeout waiting for response from "
+ "publickey subsystem");
+ goto err_exit;
+ }
+
+ pkey->listFetch_s = pkey->listFetch_data;
+ if((response =
+ publickey_response_id(&pkey->listFetch_s,
+ pkey->listFetch_data_len)) < 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Invalid publickey subsystem response code");
+ goto err_exit;
+ }
+
+ switch(response) {
+ case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
+ /* Error, or processing complete */
+ {
+ unsigned long status, descr_len, lang_len;
+
+ if(pkey->listFetch_s + 8 <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ status = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ descr_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + descr_len + 4 <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ /* description starts at pkey->listFetch_s */
+ pkey->listFetch_s += descr_len;
+ lang_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + lang_len <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ /* lang starts at pkey->listFetch_s */
+ pkey->listFetch_s += lang_len;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s >
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Malformed publickey subsystem packet");
+ goto err_exit;
+ }
+
+ if(status == LIBSSH2_PUBLICKEY_SUCCESS) {
+ LIBSSH2_FREE(session, pkey->listFetch_data);
+ pkey->listFetch_data = NULL;
+ *pkey_list = list;
+ *num_keys = keys;
+ pkey->listFetch_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ publickey_status_error(pkey, session, status);
+ goto err_exit;
+ }
+ case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY:
+ /* What we want */
+ if(keys >= max_keys) {
+ libssh2_publickey_list *newlist;
+ /* Grow the key list if necessary */
+ max_keys += 8;
+ newlist =
+ LIBSSH2_REALLOC(session, list,
+ (max_keys +
+ 1) * sizeof(libssh2_publickey_list));
+ if(!newlist) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "publickey list");
+ goto err_exit;
+ }
+ list = newlist;
+ }
+ if(pkey->version == 1) {
+ unsigned long comment_len;
+
+ if(pkey->listFetch_s + 4 <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ comment_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(comment_len) {
+ list[keys].num_attrs = 1;
+ list[keys].attrs =
+ LIBSSH2_ALLOC(session,
+ sizeof(libssh2_publickey_attribute));
+ if(!list[keys].attrs) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "publickey attributes");
+ goto err_exit;
+ }
+ list[keys].attrs[0].name = "comment";
+ list[keys].attrs[0].name_len = sizeof("comment") - 1;
+ list[keys].attrs[0].value = (char *) pkey->listFetch_s;
+ list[keys].attrs[0].value_len = comment_len;
+ list[keys].attrs[0].mandatory = 0;
+
+ pkey->listFetch_s += comment_len;
+ }
+ else {
+ list[keys].num_attrs = 0;
+ list[keys].attrs = NULL;
+ }
+
+ if(pkey->listFetch_s + 4 <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].name_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + list[keys].name_len <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].name = pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].name_len;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + 4 <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].blob_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + list[keys].blob_len <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].blob = pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].blob_len;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+ }
+ else {
+ /* Version == 2 */
+
+ if(pkey->listFetch_s + 4 <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].name_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + list[keys].name_len <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].name = pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].name_len;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + 4 <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].blob_len = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + list[keys].blob_len <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].blob = pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].blob_len;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + 4 <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].num_attrs = _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(list[keys].num_attrs) {
+ list[keys].attrs =
+ LIBSSH2_ALLOC(session,
+ list[keys].num_attrs *
+ sizeof(libssh2_publickey_attribute));
+ if(!list[keys].attrs) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "publickey attributes");
+ goto err_exit;
+ }
+ for(i = 0; i < list[keys].num_attrs; i++) {
+ if(pkey->listFetch_s + 4 <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].attrs[i].name_len =
+ _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ }
+ else {
+ _libssh2_error(session,
+ LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + list[keys].attrs[i].name_len <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].attrs[i].name =
+ (char *) pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].attrs[i].name_len;
+ }
+ else {
+ _libssh2_error(session,
+ LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s + 4 <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].attrs[i].value_len =
+ _libssh2_ntohu32(pkey->listFetch_s);
+ pkey->listFetch_s += 4;
+ }
+ else {
+ _libssh2_error(session,
+ LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ if(pkey->listFetch_s +
+ list[keys].attrs[i].value_len <=
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ list[keys].attrs[i].value =
+ (char *) pkey->listFetch_s;
+ pkey->listFetch_s += list[keys].attrs[i].value_len;
+ }
+ else {
+ _libssh2_error(session,
+ LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
+ /* actually an ignored value */
+ list[keys].attrs[i].mandatory = 0;
+ }
+ }
+ else {
+ list[keys].attrs = NULL;
+ }
+ }
+ /* To be FREEd in libssh2_publickey_list_free() */
+ list[keys].packet = pkey->listFetch_data;
+ keys++;
+
+ list[keys].packet = NULL; /* Terminate the list */
+ pkey->listFetch_data = NULL;
+ break;
+ default:
+ /* Unknown/Unexpected */
+ _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
+ "Unexpected publickey subsystem response");
+ LIBSSH2_FREE(session, pkey->listFetch_data);
+ pkey->listFetch_data = NULL;
+ }
+ }
+
+ /* Only reached via explicit goto */
+ err_exit:
+ if(pkey->listFetch_data) {
+ LIBSSH2_FREE(session, pkey->listFetch_data);
+ pkey->listFetch_data = NULL;
+ }
+ if(list) {
+ libssh2_publickey_list_free(pkey, list);
+ }
+ pkey->listFetch_state = libssh2_NB_state_idle;
+ return -1;
+}
+
+/* libssh2_publickey_list_free
+ * Free a previously fetched list of public keys
+ */
+LIBSSH2_API void
+libssh2_publickey_list_free(LIBSSH2_PUBLICKEY * pkey,
+ libssh2_publickey_list * pkey_list)
+{
+ LIBSSH2_SESSION *session;
+ libssh2_publickey_list *p = pkey_list;
+
+ if(!pkey || !p)
+ return;
+
+ session = pkey->channel->session;
+
+ while(p->packet) {
+ if(p->attrs) {
+ LIBSSH2_FREE(session, p->attrs);
+ }
+ LIBSSH2_FREE(session, p->packet);
+ p++;
+ }
+
+ LIBSSH2_FREE(session, pkey_list);
+}
+
+/* libssh2_publickey_shutdown
+ * Shutdown the publickey subsystem
+ */
+LIBSSH2_API int
+libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey)
+{
+ LIBSSH2_SESSION *session;
+ int rc;
+
+ if(!pkey)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ session = pkey->channel->session;
+
+ /*
+ * Make sure all memory used in the state variables are free
+ */
+ if(pkey->receive_packet) {
+ LIBSSH2_FREE(session, pkey->receive_packet);
+ pkey->receive_packet = NULL;
+ }
+ if(pkey->add_packet) {
+ LIBSSH2_FREE(session, pkey->add_packet);
+ pkey->add_packet = NULL;
+ }
+ if(pkey->remove_packet) {
+ LIBSSH2_FREE(session, pkey->remove_packet);
+ pkey->remove_packet = NULL;
+ }
+ if(pkey->listFetch_data) {
+ LIBSSH2_FREE(session, pkey->listFetch_data);
+ pkey->listFetch_data = NULL;
+ }
+
+ rc = _libssh2_channel_free(pkey->channel);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ LIBSSH2_FREE(session, pkey);
+ return 0;
+}
diff --git a/contrib/libs/libssh2/src/scp.c b/contrib/libs/libssh2/src/scp.c
new file mode 100644
index 00000000000..8cb3d65c3b6
--- /dev/null
+++ b/contrib/libs/libssh2/src/scp.c
@@ -0,0 +1,1145 @@
+/* Copyright (c) 2009-2019 by Daniel Stenberg
+ * Copyright (c) 2004-2008, Sara Golemon <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include <errno.h>
+#include <stdlib.h>
+
+#include "channel.h"
+#include "session.h"
+
+
+/* Max. length of a quoted string after libssh2_shell_quotearg() processing */
+#define _libssh2_shell_quotedsize(s) (3 * strlen(s) + 2)
+
+/*
+ This function quotes a string in a way suitable to be used with a
+ shell, e.g. the file name
+ one two
+ becomes
+ 'one two'
+
+ The resulting output string is crafted in a way that makes it usable
+ with the two most common shell types: Bourne Shell derived shells
+ (sh, ksh, ksh93, bash, zsh) and C-Shell derivates (csh, tcsh).
+
+ The following special cases are handled:
+ o If the string contains an apostrophy itself, the apostrophy
+ character is written in quotation marks, e.g. "'".
+ The shell cannot handle the syntax 'doesn\'t', so we close the
+ current argument word, add the apostrophe in quotation marks "",
+ and open a new argument word instead (_ indicate the input
+ string characters):
+ _____ _ _
+ 'doesn' "'" 't'
+
+ Sequences of apostrophes are combined in one pair of quotation marks:
+ a'''b
+ becomes
+ _ ___ _
+ 'a'"'''"'b'
+
+ o If the string contains an exclamation mark (!), the C-Shell
+ interprets it as an event number. Using \! (not within quotation
+ marks or single quotation marks) is a mechanism understood by
+ both Bourne Shell and C-Shell.
+
+ If a quotation was already started, the argument word is closed
+ first:
+ a!b
+
+ become
+ _ _ _
+ 'a'\!'b'
+
+ The result buffer must be large enough for the expanded result. A
+ bad case regarding expansion is alternating characters and
+ apostrophes:
+
+ a'b'c'd' (length 8) gets converted to
+ 'a'"'"'b'"'"'c'"'"'d'"'" (length 24)
+
+ This is the worst case.
+
+ Maximum length of the result:
+ 1 + 6 * (length(input) + 1) / 2) + 1
+
+ => 3 * length(input) + 2
+
+ Explanation:
+ o leading apostrophe
+ o one character / apostrophe pair (two characters) can get
+ represented as 6 characters: a' -> a'"'"'
+ o String terminator (+1)
+
+ A result buffer three times the size of the input buffer + 2
+ characters should be safe.
+
+ References:
+ o csh-compatible quotation (special handling for '!' etc.), see
+ http://www.grymoire.com/Unix/Csh.html#toc-uh-10
+
+ Return value:
+ Length of the resulting string (not counting the terminating '\0'),
+ or 0 in case of errors, e.g. result buffer too small
+
+ Note: this function could possible be used elsewhere within libssh2, but
+ until then it is kept static and in this source file.
+*/
+
+static unsigned
+shell_quotearg(const char *path, unsigned char *buf,
+ unsigned bufsize)
+{
+ const char *src;
+ unsigned char *dst, *endp;
+
+ /*
+ * Processing States:
+ * UQSTRING: unquoted string: ... -- used for quoting exclamation
+ * marks. This is the initial state
+ * SQSTRING: single-quoted-string: '... -- any character may follow
+ * QSTRING: quoted string: "... -- only apostrophes may follow
+ */
+ enum { UQSTRING, SQSTRING, QSTRING } state = UQSTRING;
+
+ endp = &buf[bufsize];
+ src = path;
+ dst = buf;
+ while(*src && dst < endp - 1) {
+
+ switch(*src) {
+ /*
+ * Special handling for apostrophe.
+ * An apostrophe is always written in quotation marks, e.g.
+ * ' -> "'".
+ */
+
+ case '\'':
+ switch(state) {
+ case UQSTRING: /* Unquoted string */
+ if(dst + 1 >= endp)
+ return 0;
+ *dst++ = '"';
+ break;
+ case QSTRING: /* Continue quoted string */
+ break;
+ case SQSTRING: /* Close single quoted string */
+ if(dst + 2 >= endp)
+ return 0;
+ *dst++ = '\'';
+ *dst++ = '"';
+ break;
+ default:
+ break;
+ }
+ state = QSTRING;
+ break;
+
+ /*
+ * Special handling for exclamation marks. CSH interprets
+ * exclamation marks even when quoted with apostrophes. We convert
+ * it to the plain string \!, because both Bourne Shell and CSH
+ * interpret that as a verbatim exclamation mark.
+ */
+
+ case '!':
+ switch(state) {
+ case UQSTRING:
+ if(dst + 1 >= endp)
+ return 0;
+ *dst++ = '\\';
+ break;
+ case QSTRING:
+ if(dst + 2 >= endp)
+ return 0;
+ *dst++ = '"'; /* Closing quotation mark */
+ *dst++ = '\\';
+ break;
+ case SQSTRING: /* Close single quoted string */
+ if(dst + 2 >= endp)
+ return 0;
+ *dst++ = '\'';
+ *dst++ = '\\';
+ break;
+ default:
+ break;
+ }
+ state = UQSTRING;
+ break;
+
+ /*
+ * Ordinary character: prefer single-quoted string
+ */
+
+ default:
+ switch(state) {
+ case UQSTRING:
+ if(dst + 1 >= endp)
+ return 0;
+ *dst++ = '\'';
+ break;
+ case QSTRING:
+ if(dst + 2 >= endp)
+ return 0;
+ *dst++ = '"'; /* Closing quotation mark */
+ *dst++ = '\'';
+ break;
+ case SQSTRING: /* Continue single quoted string */
+ break;
+ default:
+ break;
+ }
+ state = SQSTRING; /* Start single-quoted string */
+ break;
+ }
+
+ if(dst + 1 >= endp)
+ return 0;
+ *dst++ = *src++;
+ }
+
+ switch(state) {
+ case UQSTRING:
+ break;
+ case QSTRING: /* Close quoted string */
+ if(dst + 1 >= endp)
+ return 0;
+ *dst++ = '"';
+ break;
+ case SQSTRING: /* Close single quoted string */
+ if(dst + 1 >= endp)
+ return 0;
+ *dst++ = '\'';
+ break;
+ default:
+ break;
+ }
+
+ if(dst + 1 >= endp)
+ return 0;
+ *dst = '\0';
+
+ /* The result cannot be larger than 3 * strlen(path) + 2 */
+ /* assert((dst - buf) <= (3 * (src - path) + 2)); */
+
+ return dst - buf;
+}
+
+/*
+ * scp_recv
+ *
+ * Open a channel and request a remote file via SCP
+ *
+ */
+static LIBSSH2_CHANNEL *
+scp_recv(LIBSSH2_SESSION * session, const char *path, libssh2_struct_stat * sb)
+{
+ int cmd_len;
+ int rc;
+ int tmp_err_code;
+ const char *tmp_err_msg;
+
+ if(session->scpRecv_state == libssh2_NB_state_idle) {
+ session->scpRecv_mode = 0;
+ session->scpRecv_size = 0;
+ session->scpRecv_mtime = 0;
+ session->scpRecv_atime = 0;
+
+ session->scpRecv_command_len =
+ _libssh2_shell_quotedsize(path) + sizeof("scp -f ") + (sb?1:0);
+
+ session->scpRecv_command =
+ LIBSSH2_ALLOC(session, session->scpRecv_command_len);
+
+ if(!session->scpRecv_command) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a command buffer for "
+ "SCP session");
+ return NULL;
+ }
+
+ snprintf((char *)session->scpRecv_command,
+ session->scpRecv_command_len,
+ "scp -%sf ", sb?"p":"");
+
+ cmd_len = strlen((char *)session->scpRecv_command);
+ cmd_len += shell_quotearg(path,
+ &session->scpRecv_command[cmd_len],
+ session->scpRecv_command_len - cmd_len);
+
+ /* the command to exec should _not_ be NUL-terminated */
+ session->scpRecv_command_len = cmd_len;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "Opening channel for SCP receive");
+
+ session->scpRecv_state = libssh2_NB_state_created;
+ }
+
+ if(session->scpRecv_state == libssh2_NB_state_created) {
+ /* Allocate a channel */
+ session->scpRecv_channel =
+ _libssh2_channel_open(session, "session",
+ sizeof("session") - 1,
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT,
+ LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL,
+ 0);
+ if(!session->scpRecv_channel) {
+ if(libssh2_session_last_errno(session) !=
+ LIBSSH2_ERROR_EAGAIN) {
+ LIBSSH2_FREE(session, session->scpRecv_command);
+ session->scpRecv_command = NULL;
+ session->scpRecv_state = libssh2_NB_state_idle;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block starting up channel");
+ }
+ return NULL;
+ }
+
+ session->scpRecv_state = libssh2_NB_state_sent;
+ }
+
+ if(session->scpRecv_state == libssh2_NB_state_sent) {
+ /* Request SCP for the desired file */
+ rc = _libssh2_channel_process_startup(session->scpRecv_channel, "exec",
+ sizeof("exec") - 1,
+ (char *)session->scpRecv_command,
+ session->scpRecv_command_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting SCP startup");
+ return NULL;
+ }
+ else if(rc) {
+ LIBSSH2_FREE(session, session->scpRecv_command);
+ session->scpRecv_command = NULL;
+ goto scp_recv_error;
+ }
+ LIBSSH2_FREE(session, session->scpRecv_command);
+ session->scpRecv_command = NULL;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sending initial wakeup");
+ /* SCP ACK */
+ session->scpRecv_response[0] = '\0';
+
+ session->scpRecv_state = libssh2_NB_state_sent1;
+ }
+
+ if(session->scpRecv_state == libssh2_NB_state_sent1) {
+ rc = _libssh2_channel_write(session->scpRecv_channel, 0,
+ session->scpRecv_response, 1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending initial wakeup");
+ return NULL;
+ }
+ else if(rc != 1) {
+ goto scp_recv_error;
+ }
+
+ /* Parse SCP response */
+ session->scpRecv_response_len = 0;
+
+ session->scpRecv_state = libssh2_NB_state_sent2;
+ }
+
+ if((session->scpRecv_state == libssh2_NB_state_sent2)
+ || (session->scpRecv_state == libssh2_NB_state_sent3)) {
+ while(sb && (session->scpRecv_response_len <
+ LIBSSH2_SCP_RESPONSE_BUFLEN)) {
+ unsigned char *s, *p;
+
+ if(session->scpRecv_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_channel_read(session->scpRecv_channel, 0,
+ (char *) session->
+ scpRecv_response +
+ session->scpRecv_response_len, 1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for SCP response");
+ return NULL;
+ }
+ else if(rc < 0) {
+ /* error, give up */
+ _libssh2_error(session, rc, "Failed reading SCP response");
+ goto scp_recv_error;
+ }
+ else if(rc == 0)
+ goto scp_recv_empty_channel;
+
+ session->scpRecv_response_len++;
+
+ if(session->scpRecv_response[0] != 'T') {
+ size_t err_len;
+ char *err_msg;
+
+ /* there can be
+ 01 for warnings
+ 02 for errors
+
+ The following string MUST be newline terminated
+ */
+ err_len =
+ _libssh2_channel_packet_data_len(session->
+ scpRecv_channel, 0);
+ err_msg = LIBSSH2_ALLOC(session, err_len + 1);
+ if(!err_msg) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed to get memory ");
+ goto scp_recv_error;
+ }
+
+ /* Read the remote error message */
+ (void)_libssh2_channel_read(session->scpRecv_channel, 0,
+ err_msg, err_len);
+ /* If it failed for any reason, we ignore it anyway. */
+
+ /* zero terminate the error */
+ err_msg[err_len] = 0;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "got %02x %s", session->scpRecv_response[0],
+ err_msg);
+
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Failed to recv file");
+
+ LIBSSH2_FREE(session, err_msg);
+ goto scp_recv_error;
+ }
+
+ if((session->scpRecv_response_len > 1) &&
+ ((session->
+ scpRecv_response[session->scpRecv_response_len - 1] <
+ '0')
+ || (session->
+ scpRecv_response[session->scpRecv_response_len - 1] >
+ '9'))
+ && (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ ' ')
+ && (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\r')
+ && (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\n')) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid data in SCP response");
+ goto scp_recv_error;
+ }
+
+ if((session->scpRecv_response_len < 9)
+ || (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\n')) {
+ if(session->scpRecv_response_len ==
+ LIBSSH2_SCP_RESPONSE_BUFLEN) {
+ /* You had your chance */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Unterminated response from "
+ "SCP server");
+ goto scp_recv_error;
+ }
+ /* Way too short to be an SCP response, or not done yet,
+ short circuit */
+ continue;
+ }
+
+ /* We're guaranteed not to go under response_len == 0 by the
+ logic above */
+ while((session->
+ scpRecv_response[session->scpRecv_response_len - 1] ==
+ '\r')
+ || (session->
+ scpRecv_response[session->scpRecv_response_len -
+ 1] == '\n'))
+ session->scpRecv_response_len--;
+ session->scpRecv_response[session->scpRecv_response_len] =
+ '\0';
+
+ if(session->scpRecv_response_len < 8) {
+ /* EOL came too soon */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "too short");
+ goto scp_recv_error;
+ }
+
+ s = session->scpRecv_response + 1;
+
+ p = (unsigned char *) strchr((char *) s, ' ');
+ if(!p || ((p - s) <= 0)) {
+ /* No spaces or space in the wrong spot */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "malformed mtime");
+ goto scp_recv_error;
+ }
+
+ *(p++) = '\0';
+ /* Make sure we don't get fooled by leftover values */
+ session->scpRecv_mtime = strtol((char *) s, NULL, 10);
+
+ s = (unsigned char *) strchr((char *) p, ' ');
+ if(!s || ((s - p) <= 0)) {
+ /* No spaces or space in the wrong spot */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "malformed mtime.usec");
+ goto scp_recv_error;
+ }
+
+ /* Ignore mtime.usec */
+ s++;
+ p = (unsigned char *) strchr((char *) s, ' ');
+ if(!p || ((p - s) <= 0)) {
+ /* No spaces or space in the wrong spot */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "too short or malformed");
+ goto scp_recv_error;
+ }
+
+ *p = '\0';
+ /* Make sure we don't get fooled by leftover values */
+ session->scpRecv_atime = strtol((char *) s, NULL, 10);
+
+ /* SCP ACK */
+ session->scpRecv_response[0] = '\0';
+
+ session->scpRecv_state = libssh2_NB_state_sent3;
+ }
+
+ if(session->scpRecv_state == libssh2_NB_state_sent3) {
+ rc = _libssh2_channel_write(session->scpRecv_channel, 0,
+ session->scpRecv_response, 1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting to send SCP ACK");
+ return NULL;
+ }
+ else if(rc != 1) {
+ goto scp_recv_error;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "mtime = %ld, atime = %ld",
+ session->scpRecv_mtime, session->scpRecv_atime);
+
+ /* We *should* check that atime.usec is valid, but why let
+ that stop use? */
+ break;
+ }
+ }
+
+ session->scpRecv_state = libssh2_NB_state_sent4;
+ }
+
+ if(session->scpRecv_state == libssh2_NB_state_sent4) {
+ session->scpRecv_response_len = 0;
+
+ session->scpRecv_state = libssh2_NB_state_sent5;
+ }
+
+ if((session->scpRecv_state == libssh2_NB_state_sent5)
+ || (session->scpRecv_state == libssh2_NB_state_sent6)) {
+ while(session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) {
+ char *s, *p, *e = NULL;
+
+ if(session->scpRecv_state == libssh2_NB_state_sent5) {
+ rc = _libssh2_channel_read(session->scpRecv_channel, 0,
+ (char *) session->
+ scpRecv_response +
+ session->scpRecv_response_len, 1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for SCP response");
+ return NULL;
+ }
+ else if(rc < 0) {
+ /* error, bail out*/
+ _libssh2_error(session, rc, "Failed reading SCP response");
+ goto scp_recv_error;
+ }
+ else if(rc == 0)
+ goto scp_recv_empty_channel;
+
+ session->scpRecv_response_len++;
+
+ if(session->scpRecv_response[0] != 'C') {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server");
+ goto scp_recv_error;
+ }
+
+ if((session->scpRecv_response_len > 1) &&
+ (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\r')
+ && (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\n')
+ &&
+ (session->
+ scpRecv_response[session->scpRecv_response_len - 1]
+ < 32)) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid data in SCP response");
+ goto scp_recv_error;
+ }
+
+ if((session->scpRecv_response_len < 7)
+ || (session->
+ scpRecv_response[session->scpRecv_response_len - 1] !=
+ '\n')) {
+ if(session->scpRecv_response_len ==
+ LIBSSH2_SCP_RESPONSE_BUFLEN) {
+ /* You had your chance */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Unterminated response "
+ "from SCP server");
+ goto scp_recv_error;
+ }
+ /* Way too short to be an SCP response, or not done yet,
+ short circuit */
+ continue;
+ }
+
+ /* We're guaranteed not to go under response_len == 0 by the
+ logic above */
+ while((session->
+ scpRecv_response[session->scpRecv_response_len - 1] ==
+ '\r')
+ || (session->
+ scpRecv_response[session->scpRecv_response_len -
+ 1] == '\n')) {
+ session->scpRecv_response_len--;
+ }
+ session->scpRecv_response[session->scpRecv_response_len] =
+ '\0';
+
+ if(session->scpRecv_response_len < 6) {
+ /* EOL came too soon */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "too short");
+ goto scp_recv_error;
+ }
+
+ s = (char *) session->scpRecv_response + 1;
+
+ p = strchr(s, ' ');
+ if(!p || ((p - s) <= 0)) {
+ /* No spaces or space in the wrong spot */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "malformed mode");
+ goto scp_recv_error;
+ }
+
+ *(p++) = '\0';
+ /* Make sure we don't get fooled by leftover values */
+
+ session->scpRecv_mode = strtol(s, &e, 8);
+ if(e && *e) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "invalid mode");
+ goto scp_recv_error;
+ }
+
+ s = strchr(p, ' ');
+ if(!s || ((s - p) <= 0)) {
+ /* No spaces or space in the wrong spot */
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "too short or malformed");
+ goto scp_recv_error;
+ }
+
+ *s = '\0';
+ /* Make sure we don't get fooled by leftover values */
+ session->scpRecv_size = scpsize_strtol(p, &e, 10);
+ if(e && *e) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid response from SCP server, "
+ "invalid size");
+ goto scp_recv_error;
+ }
+
+ /* SCP ACK */
+ session->scpRecv_response[0] = '\0';
+
+ session->scpRecv_state = libssh2_NB_state_sent6;
+ }
+
+ if(session->scpRecv_state == libssh2_NB_state_sent6) {
+ rc = _libssh2_channel_write(session->scpRecv_channel, 0,
+ session->scpRecv_response, 1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending SCP ACK");
+ return NULL;
+ }
+ else if(rc != 1) {
+ goto scp_recv_error;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "mode = 0%lo size = %ld", session->scpRecv_mode,
+ session->scpRecv_size);
+
+ /* We *should* check that basename is valid, but why let that
+ stop us? */
+ break;
+ }
+ }
+
+ session->scpRecv_state = libssh2_NB_state_sent7;
+ }
+
+ if(sb) {
+ memset(sb, 0, sizeof(libssh2_struct_stat));
+
+ sb->st_mtime = session->scpRecv_mtime;
+ sb->st_atime = session->scpRecv_atime;
+ sb->st_size = session->scpRecv_size;
+ sb->st_mode = (unsigned short)session->scpRecv_mode;
+ }
+
+ session->scpRecv_state = libssh2_NB_state_idle;
+ return session->scpRecv_channel;
+
+ scp_recv_empty_channel:
+ /* the code only jumps here as a result of a zero read from channel_read()
+ so we check EOF status to avoid getting stuck in a loop */
+ if(libssh2_channel_eof(session->scpRecv_channel))
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Unexpected channel close");
+ else
+ return session->scpRecv_channel;
+ /* fall-through */
+ scp_recv_error:
+ tmp_err_code = session->err_code;
+ tmp_err_msg = session->err_msg;
+ while(libssh2_channel_free(session->scpRecv_channel) ==
+ LIBSSH2_ERROR_EAGAIN);
+ session->err_code = tmp_err_code;
+ session->err_msg = tmp_err_msg;
+ session->scpRecv_channel = NULL;
+ session->scpRecv_state = libssh2_NB_state_idle;
+ return NULL;
+}
+
+/*
+ * libssh2_scp_recv
+ *
+ * DEPRECATED
+ *
+ * Open a channel and request a remote file via SCP. This receives files
+ * larger than 2 GB, but is unable to report the proper size on platforms
+ * where the st_size member of struct stat is limited to 2 GB (e.g. windows).
+ *
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat * sb)
+{
+ LIBSSH2_CHANNEL *ptr;
+
+ /* scp_recv uses libssh2_struct_stat, so pass one if the caller gave us a
+ struct to populate... */
+ libssh2_struct_stat sb_intl;
+ libssh2_struct_stat *sb_ptr;
+ memset(&sb_intl, 0, sizeof(sb_intl));
+ sb_ptr = sb ? &sb_intl : NULL;
+
+ BLOCK_ADJUST_ERRNO(ptr, session, scp_recv(session, path, sb_ptr));
+
+ /* ...and populate the caller's with as much info as fits. */
+ if(sb) {
+ memset(sb, 0, sizeof(struct stat));
+
+ sb->st_mtime = sb_intl.st_mtime;
+ sb->st_atime = sb_intl.st_atime;
+ sb->st_size = (off_t)sb_intl.st_size;
+ sb->st_mode = sb_intl.st_mode;
+ }
+
+ return ptr;
+}
+
+/*
+ * libssh2_scp_recv2
+ *
+ * Open a channel and request a remote file via SCP. This supports files > 2GB
+ * on platforms that support it.
+ *
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_scp_recv2(LIBSSH2_SESSION *session, const char *path,
+ libssh2_struct_stat *sb)
+{
+ LIBSSH2_CHANNEL *ptr;
+ BLOCK_ADJUST_ERRNO(ptr, session, scp_recv(session, path, sb));
+ return ptr;
+}
+
+/*
+ * scp_send()
+ *
+ * Send a file using SCP
+ *
+ */
+static LIBSSH2_CHANNEL *
+scp_send(LIBSSH2_SESSION * session, const char *path, int mode,
+ libssh2_int64_t size, time_t mtime, time_t atime)
+{
+ int cmd_len;
+ int rc;
+ int tmp_err_code;
+ const char *tmp_err_msg;
+
+ if(session->scpSend_state == libssh2_NB_state_idle) {
+ session->scpSend_command_len =
+ _libssh2_shell_quotedsize(path) + sizeof("scp -t ") +
+ ((mtime || atime)?1:0);
+
+ session->scpSend_command =
+ LIBSSH2_ALLOC(session, session->scpSend_command_len);
+
+ if(!session->scpSend_command) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a command buffer for "
+ "SCP session");
+ return NULL;
+ }
+
+ snprintf((char *)session->scpSend_command,
+ session->scpSend_command_len,
+ "scp -%st ", (mtime || atime)?"p":"");
+
+ cmd_len = strlen((char *)session->scpSend_command);
+ cmd_len += shell_quotearg(path,
+ &session->scpSend_command[cmd_len],
+ session->scpSend_command_len - cmd_len);
+
+ /* the command to exec should _not_ be NUL-terminated */
+ session->scpSend_command_len = cmd_len;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "Opening channel for SCP send");
+ /* Allocate a channel */
+
+ session->scpSend_state = libssh2_NB_state_created;
+ }
+
+ if(session->scpSend_state == libssh2_NB_state_created) {
+ session->scpSend_channel =
+ _libssh2_channel_open(session, "session", sizeof("session") - 1,
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT,
+ LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0);
+ if(!session->scpSend_channel) {
+ if(libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) {
+ /* previous call set libssh2_session_last_error(), pass it
+ through */
+ LIBSSH2_FREE(session, session->scpSend_command);
+ session->scpSend_command = NULL;
+ session->scpSend_state = libssh2_NB_state_idle;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block starting up channel");
+ }
+ return NULL;
+ }
+
+ session->scpSend_state = libssh2_NB_state_sent;
+ }
+
+ if(session->scpSend_state == libssh2_NB_state_sent) {
+ /* Request SCP for the desired file */
+ rc = _libssh2_channel_process_startup(session->scpSend_channel, "exec",
+ sizeof("exec") - 1,
+ (char *)session->scpSend_command,
+ session->scpSend_command_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting SCP startup");
+ return NULL;
+ }
+ else if(rc) {
+ /* previous call set libssh2_session_last_error(), pass it
+ through */
+ LIBSSH2_FREE(session, session->scpSend_command);
+ session->scpSend_command = NULL;
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Unknown error while getting error string");
+ goto scp_send_error;
+ }
+ LIBSSH2_FREE(session, session->scpSend_command);
+ session->scpSend_command = NULL;
+
+ session->scpSend_state = libssh2_NB_state_sent1;
+ }
+
+ if(session->scpSend_state == libssh2_NB_state_sent1) {
+ /* Wait for ACK */
+ rc = _libssh2_channel_read(session->scpSend_channel, 0,
+ (char *) session->scpSend_response, 1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for response from remote");
+ return NULL;
+ }
+ else if(rc < 0) {
+ _libssh2_error(session, rc, "SCP failure");
+ goto scp_send_error;
+ }
+ else if(!rc)
+ /* remain in the same state */
+ goto scp_send_empty_channel;
+ else if(session->scpSend_response[0] != 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid ACK response from remote");
+ goto scp_send_error;
+ }
+ if(mtime || atime) {
+ /* Send mtime and atime to be used for file */
+ session->scpSend_response_len =
+ snprintf((char *) session->scpSend_response,
+ LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n",
+ (long)mtime, (long)atime);
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sent %s",
+ session->scpSend_response);
+ }
+
+ session->scpSend_state = libssh2_NB_state_sent2;
+ }
+
+ /* Send mtime and atime to be used for file */
+ if(mtime || atime) {
+ if(session->scpSend_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_channel_write(session->scpSend_channel, 0,
+ session->scpSend_response,
+ session->scpSend_response_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending time data for SCP file");
+ return NULL;
+ }
+ else if(rc != (int)session->scpSend_response_len) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send time data for SCP file");
+ goto scp_send_error;
+ }
+
+ session->scpSend_state = libssh2_NB_state_sent3;
+ }
+
+ if(session->scpSend_state == libssh2_NB_state_sent3) {
+ /* Wait for ACK */
+ rc = _libssh2_channel_read(session->scpSend_channel, 0,
+ (char *) session->scpSend_response, 1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for response");
+ return NULL;
+ }
+ else if(rc < 0) {
+ _libssh2_error(session, rc, "SCP failure");
+ goto scp_send_error;
+ }
+ else if(!rc)
+ /* remain in the same state */
+ goto scp_send_empty_channel;
+ else if(session->scpSend_response[0] != 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid SCP ACK response");
+ goto scp_send_error;
+ }
+
+ session->scpSend_state = libssh2_NB_state_sent4;
+ }
+ }
+ else {
+ if(session->scpSend_state == libssh2_NB_state_sent2) {
+ session->scpSend_state = libssh2_NB_state_sent4;
+ }
+ }
+
+ if(session->scpSend_state == libssh2_NB_state_sent4) {
+ /* Send mode, size, and basename */
+ const char *base = strrchr(path, '/');
+ if(base)
+ base++;
+ else
+ base = path;
+
+ session->scpSend_response_len =
+ snprintf((char *) session->scpSend_response,
+ LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %"
+ LIBSSH2_INT64_T_FORMAT " %s\n", mode,
+ size, base);
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP, "Sent %s",
+ session->scpSend_response);
+
+ session->scpSend_state = libssh2_NB_state_sent5;
+ }
+
+ if(session->scpSend_state == libssh2_NB_state_sent5) {
+ rc = _libssh2_channel_write(session->scpSend_channel, 0,
+ session->scpSend_response,
+ session->scpSend_response_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block send core file data for SCP file");
+ return NULL;
+ }
+ else if(rc != (int)session->scpSend_response_len) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send core file data for SCP file");
+ goto scp_send_error;
+ }
+
+ session->scpSend_state = libssh2_NB_state_sent6;
+ }
+
+ if(session->scpSend_state == libssh2_NB_state_sent6) {
+ /* Wait for ACK */
+ rc = _libssh2_channel_read(session->scpSend_channel, 0,
+ (char *) session->scpSend_response, 1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for response");
+ return NULL;
+ }
+ else if(rc < 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Invalid ACK response from remote");
+ goto scp_send_error;
+ }
+ else if(rc == 0)
+ goto scp_send_empty_channel;
+
+ else if(session->scpSend_response[0] != 0) {
+ size_t err_len;
+ char *err_msg;
+
+ err_len =
+ _libssh2_channel_packet_data_len(session->scpSend_channel, 0);
+ err_msg = LIBSSH2_ALLOC(session, err_len + 1);
+ if(!err_msg) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "failed to get memory");
+ goto scp_send_error;
+ }
+
+ /* Read the remote error message */
+ rc = _libssh2_channel_read(session->scpSend_channel, 0,
+ err_msg, err_len);
+ if(rc > 0) {
+ err_msg[err_len] = 0;
+ _libssh2_debug(session, LIBSSH2_TRACE_SCP,
+ "got %02x %s", session->scpSend_response[0],
+ err_msg);
+ }
+ LIBSSH2_FREE(session, err_msg);
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "failed to send file");
+ goto scp_send_error;
+ }
+ }
+
+ session->scpSend_state = libssh2_NB_state_idle;
+ return session->scpSend_channel;
+
+ scp_send_empty_channel:
+ /* the code only jumps here as a result of a zero read from channel_read()
+ so we check EOF status to avoid getting stuck in a loop */
+ if(libssh2_channel_eof(session->scpSend_channel)) {
+ _libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
+ "Unexpected channel close");
+ }
+ else
+ return session->scpSend_channel;
+ /* fall-through */
+ scp_send_error:
+ tmp_err_code = session->err_code;
+ tmp_err_msg = session->err_msg;
+ while(libssh2_channel_free(session->scpSend_channel) ==
+ LIBSSH2_ERROR_EAGAIN);
+ session->err_code = tmp_err_code;
+ session->err_msg = tmp_err_msg;
+ session->scpSend_channel = NULL;
+ session->scpSend_state = libssh2_NB_state_idle;
+ return NULL;
+}
+
+/*
+ * libssh2_scp_send_ex
+ *
+ * Send a file using SCP. Old API.
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode,
+ size_t size, long mtime, long atime)
+{
+ LIBSSH2_CHANNEL *ptr;
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ scp_send(session, path, mode, size,
+ (time_t)mtime, (time_t)atime));
+ return ptr;
+}
+
+/*
+ * libssh2_scp_send64
+ *
+ * Send a file using SCP
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_scp_send64(LIBSSH2_SESSION *session, const char *path, int mode,
+ libssh2_int64_t size, time_t mtime, time_t atime)
+{
+ LIBSSH2_CHANNEL *ptr;
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ scp_send(session, path, mode, size, mtime, atime));
+ return ptr;
+}
diff --git a/contrib/libs/libssh2/src/session.c b/contrib/libs/libssh2/src/session.c
new file mode 100644
index 00000000000..212560b8857
--- /dev/null
+++ b/contrib/libs/libssh2/src/session.c
@@ -0,0 +1,1832 @@
+/* Copyright (c) 2004-2007 Sara Golemon <[email protected]>
+ * Copyright (c) 2009-2015 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <fcntl.h>
+
+#ifdef HAVE_GETTIMEOFDAY
+#include <sys/time.h>
+#endif
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
+#include "transport.h"
+#include "session.h"
+#include "channel.h"
+#include "mac.h"
+#include "misc.h"
+
+/* libssh2_default_alloc
+ */
+static
+LIBSSH2_ALLOC_FUNC(libssh2_default_alloc)
+{
+ (void) abstract;
+ return malloc(count);
+}
+
+/* libssh2_default_free
+ */
+static
+LIBSSH2_FREE_FUNC(libssh2_default_free)
+{
+ (void) abstract;
+ free(ptr);
+}
+
+/* libssh2_default_realloc
+ */
+static
+LIBSSH2_REALLOC_FUNC(libssh2_default_realloc)
+{
+ (void) abstract;
+ return realloc(ptr, count);
+}
+
+/*
+ * banner_receive
+ *
+ * Wait for a hello from the remote host
+ * Allocate a buffer and store the banner in session->remote.banner
+ * Returns: 0 on success, LIBSSH2_ERROR_EAGAIN if read would block, negative
+ * on failure
+ */
+static int
+banner_receive(LIBSSH2_SESSION * session)
+{
+ int ret;
+ int banner_len;
+
+ if(session->banner_TxRx_state == libssh2_NB_state_idle) {
+ banner_len = 0;
+
+ session->banner_TxRx_state = libssh2_NB_state_created;
+ }
+ else {
+ banner_len = session->banner_TxRx_total_send;
+ }
+
+ while((banner_len < (int) sizeof(session->banner_TxRx_banner)) &&
+ ((banner_len == 0)
+ || (session->banner_TxRx_banner[banner_len - 1] != '\n'))) {
+ char c = '\0';
+
+ /* no incoming block yet! */
+ session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND;
+
+ ret = LIBSSH2_RECV(session, &c, 1,
+ LIBSSH2_SOCKET_RECV_FLAGS(session));
+ if(ret < 0) {
+ if(session->api_block_mode || (ret != -EAGAIN))
+ /* ignore EAGAIN when non-blocking */
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error recving %d bytes: %d", 1, -ret);
+ }
+ else
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Recved %d bytes banner", ret);
+
+ if(ret < 0) {
+ if(ret == -EAGAIN) {
+ session->socket_block_directions =
+ LIBSSH2_SESSION_BLOCK_INBOUND;
+ session->banner_TxRx_total_send = banner_len;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ /* Some kinda error */
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+ return LIBSSH2_ERROR_SOCKET_RECV;
+ }
+
+ if(ret == 0) {
+ session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+ }
+
+ if(c == '\0') {
+ /* NULLs are not allowed in SSH banners */
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+ return LIBSSH2_ERROR_BANNER_RECV;
+ }
+
+ session->banner_TxRx_banner[banner_len++] = c;
+ }
+
+ while(banner_len &&
+ ((session->banner_TxRx_banner[banner_len - 1] == '\n') ||
+ (session->banner_TxRx_banner[banner_len - 1] == '\r'))) {
+ banner_len--;
+ }
+
+ /* From this point on, we are done here */
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+
+ if(!banner_len)
+ return LIBSSH2_ERROR_BANNER_RECV;
+
+ if(session->remote.banner)
+ LIBSSH2_FREE(session, session->remote.banner);
+
+ session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1);
+ if(!session->remote.banner) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Error allocating space for remote banner");
+ }
+ memcpy(session->remote.banner, session->banner_TxRx_banner, banner_len);
+ session->remote.banner[banner_len] = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Received Banner: %s",
+ session->remote.banner);
+ return LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * banner_send
+ *
+ * Send the default banner, or the one set via libssh2_setopt_string
+ *
+ * Returns LIBSSH2_ERROR_EAGAIN if it would block - and if it does so, you
+ * should call this function again as soon as it is likely that more data can
+ * be sent, and this function should then be called with the same argument set
+ * (same data pointer and same data_len) until zero or failure is returned.
+ */
+static int
+banner_send(LIBSSH2_SESSION * session)
+{
+ char *banner = (char *) LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF;
+ int banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1;
+ ssize_t ret;
+#ifdef LIBSSH2DEBUG
+ char banner_dup[256];
+#endif
+
+ if(session->banner_TxRx_state == libssh2_NB_state_idle) {
+ if(session->local.banner) {
+ /* setopt_string will have given us our \r\n characters */
+ banner_len = strlen((char *) session->local.banner);
+ banner = (char *) session->local.banner;
+ }
+#ifdef LIBSSH2DEBUG
+ /* Hack and slash to avoid sending CRLF in debug output */
+ if(banner_len < 256) {
+ memcpy(banner_dup, banner, banner_len - 2);
+ banner_dup[banner_len - 2] = '\0';
+ }
+ else {
+ memcpy(banner_dup, banner, 255);
+ banner_dup[255] = '\0';
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Sending Banner: %s",
+ banner_dup);
+#endif
+
+ session->banner_TxRx_state = libssh2_NB_state_created;
+ }
+
+ /* no outgoing block yet! */
+ session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_OUTBOUND;
+
+ ret = LIBSSH2_SEND(session,
+ banner + session->banner_TxRx_total_send,
+ banner_len - session->banner_TxRx_total_send,
+ LIBSSH2_SOCKET_SEND_FLAGS(session));
+ if(ret < 0)
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error sending %d bytes: %d",
+ banner_len - session->banner_TxRx_total_send, -ret);
+ else
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Sent %d/%d bytes at %p+%d", ret,
+ banner_len - session->banner_TxRx_total_send,
+ banner, session->banner_TxRx_total_send);
+
+ if(ret != (banner_len - session->banner_TxRx_total_send)) {
+ if(ret >= 0 || ret == -EAGAIN) {
+ /* the whole packet could not be sent, save the what was */
+ session->socket_block_directions =
+ LIBSSH2_SESSION_BLOCK_OUTBOUND;
+ if(ret > 0)
+ session->banner_TxRx_total_send += ret;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+ return LIBSSH2_ERROR_SOCKET_RECV;
+ }
+
+ /* Set the state back to idle */
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ session->banner_TxRx_total_send = 0;
+
+ return 0;
+}
+
+/*
+ * session_nonblock() sets the given socket to either blocking or
+ * non-blocking mode based on the 'nonblock' boolean argument. This function
+ * is copied from the libcurl sources with permission.
+ */
+static int
+session_nonblock(libssh2_socket_t sockfd, /* operate on this */
+ int nonblock /* TRUE or FALSE */ )
+{
+#undef SETBLOCK
+#define SETBLOCK 0
+#ifdef HAVE_O_NONBLOCK
+ /* most recent unix versions */
+ int flags;
+
+ flags = fcntl(sockfd, F_GETFL, 0);
+ if(nonblock)
+ return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+ else
+ return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
+#undef SETBLOCK
+#define SETBLOCK 1
+#endif
+
+#if defined(HAVE_FIONBIO) && (SETBLOCK == 0)
+ /* older unix versions and VMS*/
+ int flags;
+
+ flags = nonblock;
+ return ioctl(sockfd, FIONBIO, &flags);
+#undef SETBLOCK
+#define SETBLOCK 2
+#endif
+
+#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0)
+ /* Windows? */
+ unsigned long flags;
+ flags = nonblock;
+
+ return ioctlsocket(sockfd, FIONBIO, &flags);
+#undef SETBLOCK
+#define SETBLOCK 3
+#endif
+
+#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0)
+ /* presumably for Amiga */
+ return IoctlSocket(sockfd, FIONBIO, (long) nonblock);
+#undef SETBLOCK
+#define SETBLOCK 4
+#endif
+
+#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0)
+ /* BeOS */
+ long b = nonblock ? 1 : 0;
+ return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
+#undef SETBLOCK
+#define SETBLOCK 5
+#endif
+
+#ifdef HAVE_DISABLED_NONBLOCKING
+ return 0; /* returns success */
+#undef SETBLOCK
+#define SETBLOCK 6
+#endif
+
+#if(SETBLOCK == 0)
+#error "no non-blocking method was found/used/set"
+#endif
+}
+
+/*
+ * get_socket_nonblocking()
+ *
+ * gets the given blocking or non-blocking state of the socket.
+ */
+static int
+get_socket_nonblocking(int sockfd)
+{ /* operate on this */
+#undef GETBLOCK
+#define GETBLOCK 0
+#ifdef HAVE_O_NONBLOCK
+ /* most recent unix versions */
+ int flags = fcntl(sockfd, F_GETFL, 0);
+
+ if(flags == -1) {
+ /* Assume blocking on error */
+ return 1;
+ }
+ return (flags & O_NONBLOCK);
+#undef GETBLOCK
+#define GETBLOCK 1
+#endif
+
+#if defined(WSAEWOULDBLOCK) && (GETBLOCK == 0)
+ /* Windows? */
+ unsigned int option_value;
+ socklen_t option_len = sizeof(option_value);
+
+ if(getsockopt
+ (sockfd, SOL_SOCKET, SO_ERROR, (void *) &option_value, &option_len)) {
+ /* Assume blocking on error */
+ return 1;
+ }
+ return (int) option_value;
+#undef GETBLOCK
+#define GETBLOCK 2
+#endif
+
+#if defined(HAVE_SO_NONBLOCK) && (GETBLOCK == 0)
+ /* BeOS */
+ long b;
+ if(getsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b))) {
+ /* Assume blocking on error */
+ return 1;
+ }
+ return (int) b;
+#undef GETBLOCK
+#define GETBLOCK 5
+#endif
+
+#if defined(SO_STATE) && defined(__VMS) && (GETBLOCK == 0)
+
+ /* VMS TCP/IP Services */
+
+ size_t sockstat = 0;
+ int callstat = 0;
+ size_t size = sizeof(int);
+
+ callstat = getsockopt(sockfd, SOL_SOCKET, SO_STATE,
+ (char *)&sockstat, &size);
+ if(callstat == -1) return 0;
+ if((sockstat&SS_NBIO) != 0) return 1;
+ return 0;
+
+#undef GETBLOCK
+#define GETBLOCK 6
+#endif
+
+#ifdef HAVE_DISABLED_NONBLOCKING
+ return 1; /* returns blocking */
+#undef GETBLOCK
+#define GETBLOCK 7
+#endif
+
+#if(GETBLOCK == 0)
+#error "no non-blocking method was found/used/get"
+#endif
+}
+
+/* libssh2_session_banner_set
+ * Set the local banner to use in the server handshake.
+ */
+LIBSSH2_API int
+libssh2_session_banner_set(LIBSSH2_SESSION * session, const char *banner)
+{
+ size_t banner_len = banner ? strlen(banner) : 0;
+
+ if(session->local.banner) {
+ LIBSSH2_FREE(session, session->local.banner);
+ session->local.banner = NULL;
+ }
+
+ if(!banner_len)
+ return 0;
+
+ session->local.banner = LIBSSH2_ALLOC(session, banner_len + 3);
+ if(!session->local.banner) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for local banner");
+ }
+
+ memcpy(session->local.banner, banner, banner_len);
+
+ /* first zero terminate like this so that the debug output is nice */
+ session->local.banner[banner_len] = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Setting local Banner: %s",
+ session->local.banner);
+ session->local.banner[banner_len++] = '\r';
+ session->local.banner[banner_len++] = '\n';
+ session->local.banner[banner_len] = '\0';
+
+ return 0;
+}
+
+/* libssh2_banner_set
+ * Set the local banner. DEPRECATED VERSION
+ */
+LIBSSH2_API int
+libssh2_banner_set(LIBSSH2_SESSION * session, const char *banner)
+{
+ return libssh2_session_banner_set(session, banner);
+}
+
+/*
+ * libssh2_session_init_ex
+ *
+ * Allocate and initialize a libssh2 session structure. Allows for malloc
+ * callbacks in case the calling program has its own memory manager It's
+ * allowable (but unadvisable) to define some but not all of the malloc
+ * callbacks An additional pointer value may be optionally passed to be sent
+ * to the callbacks (so they know who's asking)
+ */
+LIBSSH2_API LIBSSH2_SESSION *
+libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)),
+ LIBSSH2_FREE_FUNC((*my_free)),
+ LIBSSH2_REALLOC_FUNC((*my_realloc)), void *abstract)
+{
+ LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc;
+ LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free;
+ LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc;
+ LIBSSH2_SESSION *session;
+
+ if(my_alloc) {
+ local_alloc = my_alloc;
+ }
+ if(my_free) {
+ local_free = my_free;
+ }
+ if(my_realloc) {
+ local_realloc = my_realloc;
+ }
+
+ session = local_alloc(sizeof(LIBSSH2_SESSION), &abstract);
+ if(session) {
+ memset(session, 0, sizeof(LIBSSH2_SESSION));
+ session->alloc = local_alloc;
+ session->free = local_free;
+ session->realloc = local_realloc;
+ session->send = _libssh2_send;
+ session->recv = _libssh2_recv;
+ session->abstract = abstract;
+ session->api_timeout = 0; /* timeout-free API by default */
+ session->api_block_mode = 1; /* blocking API by default */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "New session resource allocated");
+ _libssh2_init_if_needed();
+ }
+ return session;
+}
+
+/*
+ * libssh2_session_callback_set
+ *
+ * Set (or reset) a callback function
+ * Returns the prior address
+ *
+ * ALERT: this function relies on that we can typecast function pointers
+ * to void pointers, which isn't allowed in ISO C!
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+LIBSSH2_API void *
+libssh2_session_callback_set(LIBSSH2_SESSION * session,
+ int cbtype, void *callback)
+{
+ void *oldcb;
+
+ switch(cbtype) {
+ case LIBSSH2_CALLBACK_IGNORE:
+ oldcb = session->ssh_msg_ignore;
+ session->ssh_msg_ignore = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_DEBUG:
+ oldcb = session->ssh_msg_debug;
+ session->ssh_msg_debug = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_DISCONNECT:
+ oldcb = session->ssh_msg_disconnect;
+ session->ssh_msg_disconnect = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_MACERROR:
+ oldcb = session->macerror;
+ session->macerror = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_X11:
+ oldcb = session->x11;
+ session->x11 = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_SEND:
+ oldcb = session->send;
+ session->send = callback;
+ return oldcb;
+
+ case LIBSSH2_CALLBACK_RECV:
+ oldcb = session->recv;
+ session->recv = callback;
+ return oldcb;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Setting Callback %d",
+ cbtype);
+
+ return NULL;
+}
+#pragma GCC diagnostic pop
+
+/*
+ * _libssh2_wait_socket()
+ *
+ * Utility function that waits for action on the socket. Returns 0 when ready
+ * to run again or error on timeout.
+ */
+int _libssh2_wait_socket(LIBSSH2_SESSION *session, time_t start_time)
+{
+ int rc;
+ int seconds_to_next;
+ int dir;
+ int has_timeout;
+ long ms_to_next = 0;
+ long elapsed_ms;
+
+ /* since libssh2 often sets EAGAIN internally before this function is
+ called, we can decrease some amount of confusion in user programs by
+ resetting the error code in this function to reduce the risk of EAGAIN
+ being stored as error when a blocking function has returned */
+ session->err_code = LIBSSH2_ERROR_NONE;
+
+ rc = libssh2_keepalive_send(session, &seconds_to_next);
+ if(rc)
+ return rc;
+
+ ms_to_next = seconds_to_next * 1000;
+
+ /* figure out what to wait for */
+ dir = libssh2_session_block_directions(session);
+
+ if(!dir) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Nothing to wait for in wait_socket");
+ /* To avoid that we hang below just because there's nothing set to
+ wait for, we timeout on 1 second to also avoid busy-looping
+ during this condition */
+ ms_to_next = 1000;
+ }
+
+ if(session->api_timeout > 0 &&
+ (seconds_to_next == 0 ||
+ ms_to_next > session->api_timeout)) {
+ time_t now = time(NULL);
+ elapsed_ms = (long)(1000*difftime(now, start_time));
+ if(elapsed_ms > session->api_timeout) {
+ return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "API timeout expired");
+ }
+ ms_to_next = (session->api_timeout - elapsed_ms);
+ has_timeout = 1;
+ }
+ else if(ms_to_next > 0) {
+ has_timeout = 1;
+ }
+ else
+ has_timeout = 0;
+
+#ifdef HAVE_POLL
+ {
+ struct pollfd sockets[1];
+
+ sockets[0].fd = session->socket_fd;
+ sockets[0].events = 0;
+ sockets[0].revents = 0;
+
+ if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
+ sockets[0].events |= POLLIN;
+
+ if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
+ sockets[0].events |= POLLOUT;
+
+ rc = poll(sockets, 1, has_timeout?ms_to_next: -1);
+ }
+#else
+ {
+ fd_set rfd;
+ fd_set wfd;
+ fd_set *writefd = NULL;
+ fd_set *readfd = NULL;
+ struct timeval tv;
+
+ tv.tv_sec = ms_to_next / 1000;
+ tv.tv_usec = (ms_to_next - tv.tv_sec*1000) * 1000;
+
+ if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) {
+ FD_ZERO(&rfd);
+ FD_SET(session->socket_fd, &rfd);
+ readfd = &rfd;
+ }
+
+ if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
+ FD_ZERO(&wfd);
+ FD_SET(session->socket_fd, &wfd);
+ writefd = &wfd;
+ }
+
+ rc = select(session->socket_fd + 1, readfd, writefd, NULL,
+ has_timeout ? &tv : NULL);
+ }
+#endif
+ if(rc == 0) {
+ return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "Timed out waiting on socket");
+ }
+ if(rc < 0) {
+ return _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+ "Error waiting on socket");
+ }
+
+ return 0; /* ready to try again */
+}
+
+static int
+session_startup(LIBSSH2_SESSION *session, libssh2_socket_t sock)
+{
+ int rc;
+
+ if(session->startup_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "session_startup for socket %d", sock);
+ if(LIBSSH2_INVALID_SOCKET == sock) {
+ /* Did we forget something? */
+ return _libssh2_error(session, LIBSSH2_ERROR_BAD_SOCKET,
+ "Bad socket provided");
+ }
+ session->socket_fd = sock;
+
+ session->socket_prev_blockstate =
+ !get_socket_nonblocking(session->socket_fd);
+
+ if(session->socket_prev_blockstate) {
+ /* If in blocking state change to non-blocking */
+ rc = session_nonblock(session->socket_fd, 1);
+ if(rc) {
+ return _libssh2_error(session, rc,
+ "Failed changing socket's "
+ "blocking state to non-blocking");
+ }
+ }
+
+ session->startup_state = libssh2_NB_state_created;
+ }
+
+ if(session->startup_state == libssh2_NB_state_created) {
+ rc = banner_send(session);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if(rc) {
+ return _libssh2_error(session, rc,
+ "Failed sending banner");
+ }
+ session->startup_state = libssh2_NB_state_sent;
+ session->banner_TxRx_state = libssh2_NB_state_idle;
+ }
+
+ if(session->startup_state == libssh2_NB_state_sent) {
+ do {
+ rc = banner_receive(session);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if(rc)
+ return _libssh2_error(session, rc,
+ "Failed getting banner");
+ } while(strncmp("SSH-", (char *)session->remote.banner, 4));
+
+ session->startup_state = libssh2_NB_state_sent1;
+ }
+
+ if(session->startup_state == libssh2_NB_state_sent1) {
+ rc = _libssh2_kex_exchange(session, 0, &session->startup_key_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if(rc)
+ return _libssh2_error(session, rc,
+ "Unable to exchange encryption keys");
+
+ session->startup_state = libssh2_NB_state_sent2;
+ }
+
+ if(session->startup_state == libssh2_NB_state_sent2) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Requesting userauth service");
+
+ /* Request the userauth service */
+ session->startup_service[0] = SSH_MSG_SERVICE_REQUEST;
+ _libssh2_htonu32(session->startup_service + 1,
+ sizeof("ssh-userauth") - 1);
+ memcpy(session->startup_service + 5, "ssh-userauth",
+ sizeof("ssh-userauth") - 1);
+
+ session->startup_state = libssh2_NB_state_sent3;
+ }
+
+ if(session->startup_state == libssh2_NB_state_sent3) {
+ rc = _libssh2_transport_send(session, session->startup_service,
+ sizeof("ssh-userauth") + 5 - 1,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if(rc) {
+ return _libssh2_error(session, rc,
+ "Unable to ask for ssh-userauth service");
+ }
+
+ session->startup_state = libssh2_NB_state_sent4;
+ }
+
+ if(session->startup_state == libssh2_NB_state_sent4) {
+ rc = _libssh2_packet_require(session, SSH_MSG_SERVICE_ACCEPT,
+ &session->startup_data,
+ &session->startup_data_len, 0, NULL, 0,
+ &session->startup_req_state);
+ if(rc)
+ return rc;
+
+ if(session->startup_data_len < 5) {
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected packet length");
+ }
+
+ session->startup_service_length =
+ _libssh2_ntohu32(session->startup_data + 1);
+
+
+ if((session->startup_service_length != (sizeof("ssh-userauth") - 1))
+ || strncmp("ssh-userauth", (char *) session->startup_data + 5,
+ session->startup_service_length)) {
+ LIBSSH2_FREE(session, session->startup_data);
+ session->startup_data = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Invalid response received from server");
+ }
+ LIBSSH2_FREE(session, session->startup_data);
+ session->startup_data = NULL;
+
+ session->startup_state = libssh2_NB_state_idle;
+
+ return 0;
+ }
+
+ /* just for safety return some error */
+ return LIBSSH2_ERROR_INVAL;
+}
+
+/*
+ * libssh2_session_handshake()
+ *
+ * session: LIBSSH2_SESSION struct allocated and owned by the calling program
+ * sock: *must* be populated with an opened and connected socket.
+ *
+ * Returns: 0 on success, or non-zero on failure
+ */
+LIBSSH2_API int
+libssh2_session_handshake(LIBSSH2_SESSION *session, libssh2_socket_t sock)
+{
+ int rc;
+
+ BLOCK_ADJUST(rc, session, session_startup(session, sock) );
+
+ return rc;
+}
+
+/*
+ * libssh2_session_startup()
+ *
+ * DEPRECATED. Use libssh2_session_handshake() instead! This function is not
+ * portable enough.
+ *
+ * session: LIBSSH2_SESSION struct allocated and owned by the calling program
+ * sock: *must* be populated with an opened and connected socket.
+ *
+ * Returns: 0 on success, or non-zero on failure
+ */
+LIBSSH2_API int
+libssh2_session_startup(LIBSSH2_SESSION *session, int sock)
+{
+ return libssh2_session_handshake(session, (libssh2_socket_t) sock);
+}
+
+/*
+ * libssh2_session_free
+ *
+ * Frees the memory allocated to the session
+ * Also closes and frees any channels attached to this session
+ */
+static int
+session_free(LIBSSH2_SESSION *session)
+{
+ int rc;
+ LIBSSH2_PACKET *pkg;
+ LIBSSH2_CHANNEL *ch;
+ LIBSSH2_LISTENER *l;
+ int packets_left = 0;
+
+ if(session->free_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Freeing session resource",
+ session->remote.banner);
+
+ session->free_state = libssh2_NB_state_created;
+ }
+
+ if(session->free_state == libssh2_NB_state_created) {
+ while((ch = _libssh2_list_first(&session->channels))) {
+
+ rc = _libssh2_channel_free(ch);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ session->free_state = libssh2_NB_state_sent;
+ }
+
+ if(session->free_state == libssh2_NB_state_sent) {
+ while((l = _libssh2_list_first(&session->listeners))) {
+ rc = _libssh2_channel_forward_cancel(l);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+
+ session->free_state = libssh2_NB_state_sent1;
+ }
+
+ if(session->state & LIBSSH2_STATE_NEWKEYS) {
+ /* hostkey */
+ if(session->hostkey && session->hostkey->dtor) {
+ session->hostkey->dtor(session, &session->server_hostkey_abstract);
+ }
+
+ /* Client to Server */
+ /* crypt */
+ if(session->local.crypt && session->local.crypt->dtor) {
+ session->local.crypt->dtor(session,
+ &session->local.crypt_abstract);
+ }
+ /* comp */
+ if(session->local.comp && session->local.comp->dtor) {
+ session->local.comp->dtor(session, 1,
+ &session->local.comp_abstract);
+ }
+ /* mac */
+ if(session->local.mac && session->local.mac->dtor) {
+ session->local.mac->dtor(session, &session->local.mac_abstract);
+ }
+
+ /* Server to Client */
+ /* crypt */
+ if(session->remote.crypt && session->remote.crypt->dtor) {
+ session->remote.crypt->dtor(session,
+ &session->remote.crypt_abstract);
+ }
+ /* comp */
+ if(session->remote.comp && session->remote.comp->dtor) {
+ session->remote.comp->dtor(session, 0,
+ &session->remote.comp_abstract);
+ }
+ /* mac */
+ if(session->remote.mac && session->remote.mac->dtor) {
+ session->remote.mac->dtor(session, &session->remote.mac_abstract);
+ }
+
+ /* session_id */
+ if(session->session_id) {
+ LIBSSH2_FREE(session, session->session_id);
+ }
+ }
+
+ /* Free banner(s) */
+ if(session->remote.banner) {
+ LIBSSH2_FREE(session, session->remote.banner);
+ }
+ if(session->local.banner) {
+ LIBSSH2_FREE(session, session->local.banner);
+ }
+
+ /* Free preference(s) */
+ if(session->kex_prefs) {
+ LIBSSH2_FREE(session, session->kex_prefs);
+ }
+ if(session->hostkey_prefs) {
+ LIBSSH2_FREE(session, session->hostkey_prefs);
+ }
+
+ if(session->local.kexinit) {
+ LIBSSH2_FREE(session, session->local.kexinit);
+ }
+ if(session->local.crypt_prefs) {
+ LIBSSH2_FREE(session, session->local.crypt_prefs);
+ }
+ if(session->local.mac_prefs) {
+ LIBSSH2_FREE(session, session->local.mac_prefs);
+ }
+ if(session->local.comp_prefs) {
+ LIBSSH2_FREE(session, session->local.comp_prefs);
+ }
+ if(session->local.lang_prefs) {
+ LIBSSH2_FREE(session, session->local.lang_prefs);
+ }
+
+ if(session->remote.kexinit) {
+ LIBSSH2_FREE(session, session->remote.kexinit);
+ }
+ if(session->remote.crypt_prefs) {
+ LIBSSH2_FREE(session, session->remote.crypt_prefs);
+ }
+ if(session->remote.mac_prefs) {
+ LIBSSH2_FREE(session, session->remote.mac_prefs);
+ }
+ if(session->remote.comp_prefs) {
+ LIBSSH2_FREE(session, session->remote.comp_prefs);
+ }
+ if(session->remote.lang_prefs) {
+ LIBSSH2_FREE(session, session->remote.lang_prefs);
+ }
+
+ /*
+ * Make sure all memory used in the state variables are free
+ */
+ if(session->kexinit_data) {
+ LIBSSH2_FREE(session, session->kexinit_data);
+ }
+ if(session->startup_data) {
+ LIBSSH2_FREE(session, session->startup_data);
+ }
+ if(session->userauth_list_data) {
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ }
+ if(session->userauth_pswd_data) {
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ }
+ if(session->userauth_pswd_newpw) {
+ LIBSSH2_FREE(session, session->userauth_pswd_newpw);
+ }
+ if(session->userauth_host_packet) {
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ }
+ if(session->userauth_host_method) {
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ }
+ if(session->userauth_host_data) {
+ LIBSSH2_FREE(session, session->userauth_host_data);
+ }
+ if(session->userauth_pblc_data) {
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ }
+ if(session->userauth_pblc_packet) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ }
+ if(session->userauth_pblc_method) {
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ }
+ if(session->userauth_kybd_data) {
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ }
+ if(session->userauth_kybd_packet) {
+ LIBSSH2_FREE(session, session->userauth_kybd_packet);
+ }
+ if(session->userauth_kybd_auth_instruction) {
+ LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction);
+ }
+ if(session->open_packet) {
+ LIBSSH2_FREE(session, session->open_packet);
+ }
+ if(session->open_data) {
+ LIBSSH2_FREE(session, session->open_data);
+ }
+ if(session->direct_message) {
+ LIBSSH2_FREE(session, session->direct_message);
+ }
+ if(session->fwdLstn_packet) {
+ LIBSSH2_FREE(session, session->fwdLstn_packet);
+ }
+ if(session->pkeyInit_data) {
+ LIBSSH2_FREE(session, session->pkeyInit_data);
+ }
+ if(session->scpRecv_command) {
+ LIBSSH2_FREE(session, session->scpRecv_command);
+ }
+ if(session->scpSend_command) {
+ LIBSSH2_FREE(session, session->scpSend_command);
+ }
+ if(session->sftpInit_sftp) {
+ LIBSSH2_FREE(session, session->sftpInit_sftp);
+ }
+
+ /* Free payload buffer */
+ if(session->packet.total_num) {
+ LIBSSH2_FREE(session, session->packet.payload);
+ }
+
+ /* Cleanup all remaining packets */
+ while((pkg = _libssh2_list_first(&session->packets))) {
+ packets_left++;
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "packet left with id %d", pkg->data[0]);
+ /* unlink the node */
+ _libssh2_list_remove(&pkg->node);
+
+ /* free */
+ LIBSSH2_FREE(session, pkg->data);
+ LIBSSH2_FREE(session, pkg);
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Extra packets left %d", packets_left);
+
+ if(session->socket_prev_blockstate) {
+ /* if the socket was previously blocking, put it back so */
+ rc = session_nonblock(session->socket_fd, 0);
+ if(rc) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "unable to reset socket's blocking state");
+ }
+ }
+
+ if(session->server_hostkey) {
+ LIBSSH2_FREE(session, session->server_hostkey);
+ }
+
+ /* error string */
+ if(session->err_msg &&
+ ((session->err_flags & LIBSSH2_ERR_FLAG_DUP) != 0)) {
+ LIBSSH2_FREE(session, (char *)session->err_msg);
+ }
+
+ LIBSSH2_FREE(session, session);
+
+ return 0;
+}
+
+/*
+ * libssh2_session_free
+ *
+ * Frees the memory allocated to the session
+ * Also closes and frees any channels attached to this session
+ */
+LIBSSH2_API int
+libssh2_session_free(LIBSSH2_SESSION * session)
+{
+ int rc;
+
+ BLOCK_ADJUST(rc, session, session_free(session) );
+
+ return rc;
+}
+
+/*
+ * libssh2_session_disconnect_ex
+ */
+static int
+session_disconnect(LIBSSH2_SESSION *session, int reason,
+ const char *description,
+ const char *lang)
+{
+ unsigned char *s;
+ unsigned long descr_len = 0, lang_len = 0;
+ int rc;
+
+ if(session->disconnect_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
+ "Disconnecting: reason=%d, desc=%s, lang=%s", reason,
+ description, lang);
+ if(description)
+ descr_len = strlen(description);
+
+ if(lang)
+ lang_len = strlen(lang);
+
+ if(descr_len > 256)
+ return _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "too long description");
+
+ /* 13 = packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */
+ session->disconnect_data_len = descr_len + lang_len + 13;
+
+ s = session->disconnect_data;
+
+ *(s++) = SSH_MSG_DISCONNECT;
+ _libssh2_store_u32(&s, reason);
+ _libssh2_store_str(&s, description, descr_len);
+ /* store length only, lang is sent separately */
+ _libssh2_store_u32(&s, lang_len);
+
+ session->disconnect_state = libssh2_NB_state_created;
+ }
+
+ rc = _libssh2_transport_send(session, session->disconnect_data,
+ session->disconnect_data_len,
+ (unsigned char *)lang, lang_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+
+ session->disconnect_state = libssh2_NB_state_idle;
+
+ return 0;
+}
+
+/*
+ * libssh2_session_disconnect_ex
+ */
+LIBSSH2_API int
+libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason,
+ const char *desc, const char *lang)
+{
+ int rc;
+ session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
+ BLOCK_ADJUST(rc, session,
+ session_disconnect(session, reason, desc, lang));
+
+ return rc;
+}
+
+/* libssh2_session_methods
+ *
+ * Return the currently active methods for method_type
+ *
+ * NOTE: Currently lang_cs and lang_sc are ALWAYS set to empty string
+ * regardless of actual negotiation Strings should NOT be freed
+ */
+LIBSSH2_API const char *
+libssh2_session_methods(LIBSSH2_SESSION * session, int method_type)
+{
+ /* All methods have char *name as their first element */
+ const LIBSSH2_KEX_METHOD *method = NULL;
+
+ switch(method_type) {
+ case LIBSSH2_METHOD_KEX:
+ method = session->kex;
+ break;
+
+ case LIBSSH2_METHOD_HOSTKEY:
+ method = (LIBSSH2_KEX_METHOD *) session->hostkey;
+ break;
+
+ case LIBSSH2_METHOD_CRYPT_CS:
+ method = (LIBSSH2_KEX_METHOD *) session->local.crypt;
+ break;
+
+ case LIBSSH2_METHOD_CRYPT_SC:
+ method = (LIBSSH2_KEX_METHOD *) session->remote.crypt;
+ break;
+
+ case LIBSSH2_METHOD_MAC_CS:
+ method = (LIBSSH2_KEX_METHOD *) session->local.mac;
+ break;
+
+ case LIBSSH2_METHOD_MAC_SC:
+ method = (LIBSSH2_KEX_METHOD *) session->remote.mac;
+ break;
+
+ case LIBSSH2_METHOD_COMP_CS:
+ method = (LIBSSH2_KEX_METHOD *) session->local.comp;
+ break;
+
+ case LIBSSH2_METHOD_COMP_SC:
+ method = (LIBSSH2_KEX_METHOD *) session->remote.comp;
+ break;
+
+ case LIBSSH2_METHOD_LANG_CS:
+ return "";
+
+ case LIBSSH2_METHOD_LANG_SC:
+ return "";
+
+ default:
+ _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "Invalid parameter specified for method_type");
+ return NULL;
+ }
+
+ if(!method) {
+ _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
+ "No method negotiated");
+ return NULL;
+ }
+
+ return method->name;
+}
+
+/* libssh2_session_abstract
+ * Retrieve a pointer to the abstract property
+ */
+LIBSSH2_API void **
+libssh2_session_abstract(LIBSSH2_SESSION * session)
+{
+ return &session->abstract;
+}
+
+/* libssh2_session_last_error
+ *
+ * Returns error code and populates an error string into errmsg If want_buf is
+ * non-zero then the string placed into errmsg must be freed by the calling
+ * program. Otherwise it is assumed to be owned by libssh2
+ */
+LIBSSH2_API int
+libssh2_session_last_error(LIBSSH2_SESSION * session, char **errmsg,
+ int *errmsg_len, int want_buf)
+{
+ size_t msglen = 0;
+
+ /* No error to report */
+ if(!session->err_code) {
+ if(errmsg) {
+ if(want_buf) {
+ *errmsg = LIBSSH2_ALLOC(session, 1);
+ if(*errmsg) {
+ **errmsg = 0;
+ }
+ }
+ else {
+ *errmsg = (char *) "";
+ }
+ }
+ if(errmsg_len) {
+ *errmsg_len = 0;
+ }
+ return 0;
+ }
+
+ if(errmsg) {
+ const char *error = session->err_msg ? session->err_msg : "";
+
+ msglen = strlen(error);
+
+ if(want_buf) {
+ /* Make a copy so the calling program can own it */
+ *errmsg = LIBSSH2_ALLOC(session, msglen + 1);
+ if(*errmsg) {
+ memcpy(*errmsg, error, msglen);
+ (*errmsg)[msglen] = 0;
+ }
+ }
+ else
+ *errmsg = (char *)error;
+ }
+
+ if(errmsg_len) {
+ *errmsg_len = msglen;
+ }
+
+ return session->err_code;
+}
+
+/* libssh2_session_last_errno
+ *
+ * Returns error code
+ */
+LIBSSH2_API int
+libssh2_session_last_errno(LIBSSH2_SESSION * session)
+{
+ return session->err_code;
+}
+
+/* libssh2_session_set_last_error
+ *
+ * Sets the internal error code for the session.
+ *
+ * This function is available specifically to be used by high level
+ * language wrappers (i.e. Python or Perl) that may extend the library
+ * features while still relying on its error reporting mechanism.
+ */
+LIBSSH2_API int
+libssh2_session_set_last_error(LIBSSH2_SESSION* session,
+ int errcode,
+ const char *errmsg)
+{
+ return _libssh2_error_flags(session, errcode, errmsg,
+ LIBSSH2_ERR_FLAG_DUP);
+}
+
+/* Libssh2_session_flag
+ *
+ * Set/Get session flags
+ *
+ * Return error code.
+ */
+LIBSSH2_API int
+libssh2_session_flag(LIBSSH2_SESSION * session, int flag, int value)
+{
+ switch(flag) {
+ case LIBSSH2_FLAG_SIGPIPE:
+ session->flag.sigpipe = value;
+ break;
+ case LIBSSH2_FLAG_COMPRESS:
+ session->flag.compress = value;
+ break;
+ default:
+ /* unknown flag */
+ return LIBSSH2_ERROR_INVAL;
+ }
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+/* _libssh2_session_set_blocking
+ *
+ * Set a session's blocking mode on or off, return the previous status when
+ * this function is called. Note this function does not alter the state of the
+ * actual socket involved.
+ */
+int
+_libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking)
+{
+ int bl = session->api_block_mode;
+ _libssh2_debug(session, LIBSSH2_TRACE_CONN,
+ "Setting blocking mode %s", blocking?"ON":"OFF");
+ session->api_block_mode = blocking;
+
+ return bl;
+}
+
+/* libssh2_session_set_blocking
+ *
+ * Set a channel's blocking mode on or off, similar to a socket's
+ * fcntl(fd, F_SETFL, O_NONBLOCK); type command
+ */
+LIBSSH2_API void
+libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking)
+{
+ (void) _libssh2_session_set_blocking(session, blocking);
+}
+
+/* libssh2_session_get_blocking
+ *
+ * Returns a session's blocking mode on or off
+ */
+LIBSSH2_API int
+libssh2_session_get_blocking(LIBSSH2_SESSION * session)
+{
+ return session->api_block_mode;
+}
+
+
+/* libssh2_session_set_timeout
+ *
+ * Set a session's timeout (in msec) for blocking mode,
+ * or 0 to disable timeouts.
+ */
+LIBSSH2_API void
+libssh2_session_set_timeout(LIBSSH2_SESSION * session, long timeout)
+{
+ session->api_timeout = timeout;
+}
+
+/* libssh2_session_get_timeout
+ *
+ * Returns a session's timeout, or 0 if disabled
+ */
+LIBSSH2_API long
+libssh2_session_get_timeout(LIBSSH2_SESSION * session)
+{
+ return session->api_timeout;
+}
+
+/*
+ * libssh2_poll_channel_read
+ *
+ * Returns 0 if no data is waiting on channel,
+ * non-0 if data is available
+ */
+LIBSSH2_API int
+libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended)
+{
+ LIBSSH2_SESSION *session;
+ LIBSSH2_PACKET *packet;
+
+ if(!channel)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ session = channel->session;
+ packet = _libssh2_list_first(&session->packets);
+
+ while(packet) {
+ if(packet->data_len < 5) {
+ return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Packet too small");
+ }
+
+ if(channel->local.id == _libssh2_ntohu32(packet->data + 1)) {
+ if(extended == 1 &&
+ (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA
+ || packet->data[0] == SSH_MSG_CHANNEL_DATA)) {
+ return 1;
+ }
+ else if(extended == 0 &&
+ packet->data[0] == SSH_MSG_CHANNEL_DATA) {
+ return 1;
+ }
+ /* else - no data of any type is ready to be read */
+ }
+ packet = _libssh2_list_next(&packet->node);
+ }
+
+ return 0;
+}
+
+/*
+ * poll_channel_write
+ *
+ * Returns 0 if writing to channel would block,
+ * non-0 if data can be written without blocking
+ */
+static inline int
+poll_channel_write(LIBSSH2_CHANNEL * channel)
+{
+ return channel->local.window_size ? 1 : 0;
+}
+
+/* poll_listener_queued
+ *
+ * Returns 0 if no connections are waiting to be accepted
+ * non-0 if one or more connections are available
+ */
+static inline int
+poll_listener_queued(LIBSSH2_LISTENER * listener)
+{
+ return _libssh2_list_first(&listener->queue) ? 1 : 0;
+}
+
+/*
+ * libssh2_poll
+ *
+ * Poll sockets, channels, and listeners for activity
+ */
+LIBSSH2_API int
+libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout)
+{
+ long timeout_remaining;
+ unsigned int i, active_fds;
+#ifdef HAVE_POLL
+ LIBSSH2_SESSION *session = NULL;
+#ifdef HAVE_ALLOCA
+ struct pollfd *sockets = alloca(sizeof(struct pollfd) * nfds);
+#else
+ struct pollfd sockets[256];
+
+ if(nfds > 256)
+ /* systems without alloca use a fixed-size array, this can be fixed if
+ we really want to, at least if the compiler is a C99 capable one */
+ return -1;
+#endif
+ /* Setup sockets for polling */
+ for(i = 0; i < nfds; i++) {
+ fds[i].revents = 0;
+ switch(fds[i].type) {
+ case LIBSSH2_POLLFD_SOCKET:
+ sockets[i].fd = fds[i].fd.socket;
+ sockets[i].events = fds[i].events;
+ sockets[i].revents = 0;
+ break;
+
+ case LIBSSH2_POLLFD_CHANNEL:
+ sockets[i].fd = fds[i].fd.channel->session->socket_fd;
+ sockets[i].events = POLLIN;
+ sockets[i].revents = 0;
+ if(!session)
+ session = fds[i].fd.channel->session;
+ break;
+
+ case LIBSSH2_POLLFD_LISTENER:
+ sockets[i].fd = fds[i].fd.listener->session->socket_fd;
+ sockets[i].events = POLLIN;
+ sockets[i].revents = 0;
+ if(!session)
+ session = fds[i].fd.listener->session;
+ break;
+
+ default:
+ if(session)
+ _libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE,
+ "Invalid descriptor passed to libssh2_poll()");
+ return -1;
+ }
+ }
+#elif defined(HAVE_SELECT)
+ LIBSSH2_SESSION *session = NULL;
+ libssh2_socket_t maxfd = 0;
+ fd_set rfds, wfds;
+ struct timeval tv;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ for(i = 0; i < nfds; i++) {
+ fds[i].revents = 0;
+ switch(fds[i].type) {
+ case LIBSSH2_POLLFD_SOCKET:
+ if(fds[i].events & LIBSSH2_POLLFD_POLLIN) {
+ FD_SET(fds[i].fd.socket, &rfds);
+ if(fds[i].fd.socket > maxfd)
+ maxfd = fds[i].fd.socket;
+ }
+ if(fds[i].events & LIBSSH2_POLLFD_POLLOUT) {
+ FD_SET(fds[i].fd.socket, &wfds);
+ if(fds[i].fd.socket > maxfd)
+ maxfd = fds[i].fd.socket;
+ }
+ break;
+
+ case LIBSSH2_POLLFD_CHANNEL:
+ FD_SET(fds[i].fd.channel->session->socket_fd, &rfds);
+ if(fds[i].fd.channel->session->socket_fd > maxfd)
+ maxfd = fds[i].fd.channel->session->socket_fd;
+ if(!session)
+ session = fds[i].fd.channel->session;
+ break;
+
+ case LIBSSH2_POLLFD_LISTENER:
+ FD_SET(fds[i].fd.listener->session->socket_fd, &rfds);
+ if(fds[i].fd.listener->session->socket_fd > maxfd)
+ maxfd = fds[i].fd.listener->session->socket_fd;
+ if(!session)
+ session = fds[i].fd.listener->session;
+ break;
+
+ default:
+ if(session)
+ _libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE,
+ "Invalid descriptor passed to libssh2_poll()");
+ return -1;
+ }
+ }
+#else
+ /* No select() or poll()
+ * no sockets structure to setup
+ */
+
+ timeout = 0;
+#endif /* HAVE_POLL or HAVE_SELECT */
+
+ timeout_remaining = timeout;
+ do {
+#if defined(HAVE_POLL) || defined(HAVE_SELECT)
+ int sysret;
+#endif
+
+ active_fds = 0;
+
+ for(i = 0; i < nfds; i++) {
+ if(fds[i].events != fds[i].revents) {
+ switch(fds[i].type) {
+ case LIBSSH2_POLLFD_CHANNEL:
+ if((fds[i].events & LIBSSH2_POLLFD_POLLIN) &&
+ /* Want to be ready for read */
+ ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) {
+ /* Not yet known to be ready for read */
+ fds[i].revents |=
+ libssh2_poll_channel_read(fds[i].fd.channel,
+ 0) ?
+ LIBSSH2_POLLFD_POLLIN : 0;
+ }
+ if((fds[i].events & LIBSSH2_POLLFD_POLLEXT) &&
+ /* Want to be ready for extended read */
+ ((fds[i].revents & LIBSSH2_POLLFD_POLLEXT) == 0)) {
+ /* Not yet known to be ready for extended read */
+ fds[i].revents |=
+ libssh2_poll_channel_read(fds[i].fd.channel,
+ 1) ?
+ LIBSSH2_POLLFD_POLLEXT : 0;
+ }
+ if((fds[i].events & LIBSSH2_POLLFD_POLLOUT) &&
+ /* Want to be ready for write */
+ ((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) {
+ /* Not yet known to be ready for write */
+ fds[i].revents |=
+ poll_channel_write(fds[i].fd. channel) ?
+ LIBSSH2_POLLFD_POLLOUT : 0;
+ }
+ if(fds[i].fd.channel->remote.close
+ || fds[i].fd.channel->local.close) {
+ fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED;
+ }
+ if(fds[i].fd.channel->session->socket_state ==
+ LIBSSH2_SOCKET_DISCONNECTED) {
+ fds[i].revents |=
+ LIBSSH2_POLLFD_CHANNEL_CLOSED |
+ LIBSSH2_POLLFD_SESSION_CLOSED;
+ }
+ break;
+
+ case LIBSSH2_POLLFD_LISTENER:
+ if((fds[i].events & LIBSSH2_POLLFD_POLLIN) &&
+ /* Want a connection */
+ ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) {
+ /* No connections known of yet */
+ fds[i].revents |=
+ poll_listener_queued(fds[i].fd. listener) ?
+ LIBSSH2_POLLFD_POLLIN : 0;
+ }
+ if(fds[i].fd.listener->session->socket_state ==
+ LIBSSH2_SOCKET_DISCONNECTED) {
+ fds[i].revents |=
+ LIBSSH2_POLLFD_LISTENER_CLOSED |
+ LIBSSH2_POLLFD_SESSION_CLOSED;
+ }
+ break;
+ }
+ }
+ if(fds[i].revents) {
+ active_fds++;
+ }
+ }
+
+ if(active_fds) {
+ /* Don't block on the sockets if we have channels/listeners which
+ are ready */
+ timeout_remaining = 0;
+ }
+#ifdef HAVE_POLL
+
+#ifdef HAVE_LIBSSH2_GETTIMEOFDAY
+ {
+ struct timeval tv_begin, tv_end;
+
+ _libssh2_gettimeofday((struct timeval *) &tv_begin, NULL);
+ sysret = poll(sockets, nfds, timeout_remaining);
+ _libssh2_gettimeofday((struct timeval *) &tv_end, NULL);
+ timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000;
+ timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000;
+ }
+#else
+ /* If the platform doesn't support gettimeofday,
+ * then just make the call non-blocking and walk away
+ */
+ sysret = poll(sockets, nfds, timeout_remaining);
+ timeout_remaining = 0;
+#endif /* HAVE_GETTIMEOFDAY */
+
+ if(sysret > 0) {
+ for(i = 0; i < nfds; i++) {
+ switch(fds[i].type) {
+ case LIBSSH2_POLLFD_SOCKET:
+ fds[i].revents = sockets[i].revents;
+ sockets[i].revents = 0; /* In case we loop again, be
+ nice */
+ if(fds[i].revents) {
+ active_fds++;
+ }
+ break;
+ case LIBSSH2_POLLFD_CHANNEL:
+ if(sockets[i].events & POLLIN) {
+ /* Spin session until no data available */
+ while(_libssh2_transport_read(fds[i].fd.
+ channel->session)
+ > 0);
+ }
+ if(sockets[i].revents & POLLHUP) {
+ fds[i].revents |=
+ LIBSSH2_POLLFD_CHANNEL_CLOSED |
+ LIBSSH2_POLLFD_SESSION_CLOSED;
+ }
+ sockets[i].revents = 0;
+ break;
+ case LIBSSH2_POLLFD_LISTENER:
+ if(sockets[i].events & POLLIN) {
+ /* Spin session until no data available */
+ while(_libssh2_transport_read(fds[i].fd.
+ listener->session)
+ > 0);
+ }
+ if(sockets[i].revents & POLLHUP) {
+ fds[i].revents |=
+ LIBSSH2_POLLFD_LISTENER_CLOSED |
+ LIBSSH2_POLLFD_SESSION_CLOSED;
+ }
+ sockets[i].revents = 0;
+ break;
+ }
+ }
+ }
+#elif defined(HAVE_SELECT)
+ tv.tv_sec = timeout_remaining / 1000;
+ tv.tv_usec = (timeout_remaining % 1000) * 1000;
+#ifdef HAVE_LIBSSH2_GETTIMEOFDAY
+ {
+ struct timeval tv_begin, tv_end;
+
+ _libssh2_gettimeofday((struct timeval *) &tv_begin, NULL);
+ sysret = select(maxfd + 1, &rfds, &wfds, NULL, &tv);
+ _libssh2_gettimeofday((struct timeval *) &tv_end, NULL);
+
+ timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000;
+ timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000;
+ }
+#else
+ /* If the platform doesn't support gettimeofday,
+ * then just make the call non-blocking and walk away
+ */
+ sysret = select(maxfd + 1, &rfds, &wfds, NULL, &tv);
+ timeout_remaining = 0;
+#endif
+
+ if(sysret > 0) {
+ for(i = 0; i < nfds; i++) {
+ switch(fds[i].type) {
+ case LIBSSH2_POLLFD_SOCKET:
+ if(FD_ISSET(fds[i].fd.socket, &rfds)) {
+ fds[i].revents |= LIBSSH2_POLLFD_POLLIN;
+ }
+ if(FD_ISSET(fds[i].fd.socket, &wfds)) {
+ fds[i].revents |= LIBSSH2_POLLFD_POLLOUT;
+ }
+ if(fds[i].revents) {
+ active_fds++;
+ }
+ break;
+
+ case LIBSSH2_POLLFD_CHANNEL:
+ if(FD_ISSET(fds[i].fd.channel->session->socket_fd,
+ &rfds)) {
+ /* Spin session until no data available */
+ while(_libssh2_transport_read(fds[i].fd.
+ channel->session)
+ > 0);
+ }
+ break;
+
+ case LIBSSH2_POLLFD_LISTENER:
+ if(FD_ISSET
+ (fds[i].fd.listener->session->socket_fd, &rfds)) {
+ /* Spin session until no data available */
+ while(_libssh2_transport_read(fds[i].fd.
+ listener->session)
+ > 0);
+ }
+ break;
+ }
+ }
+ }
+#endif /* else no select() or poll() -- timeout (and by extension
+ * timeout_remaining) will be equal to 0 */
+ } while((timeout_remaining > 0) && !active_fds);
+
+ return active_fds;
+}
+
+/*
+ * libssh2_session_block_directions
+ *
+ * Get blocked direction when a function returns LIBSSH2_ERROR_EAGAIN
+ * Returns LIBSSH2_SOCKET_BLOCK_INBOUND if recv() blocked
+ * or LIBSSH2_SOCKET_BLOCK_OUTBOUND if send() blocked
+ */
+LIBSSH2_API int
+libssh2_session_block_directions(LIBSSH2_SESSION *session)
+{
+ return session->socket_block_directions;
+}
+
+/* libssh2_session_banner_get
+ * Get the remote banner (server ID string)
+ */
+
+LIBSSH2_API const char *
+libssh2_session_banner_get(LIBSSH2_SESSION *session)
+{
+ /* to avoid a coredump when session is NULL */
+ if(NULL == session)
+ return NULL;
+
+ if(NULL == session->remote.banner)
+ return NULL;
+
+ return (const char *) session->remote.banner;
+}
diff --git a/contrib/libs/libssh2/src/session.h b/contrib/libs/libssh2/src/session.h
new file mode 100644
index 00000000000..9f8f2c70609
--- /dev/null
+++ b/contrib/libs/libssh2/src/session.h
@@ -0,0 +1,93 @@
+#ifndef __LIBSSH2_SESSION_H
+#define __LIBSSH2_SESSION_H
+/* Copyright (c) 2004-2007 Sara Golemon <[email protected]>
+ * Copyright (c) 2009-2010 by Daniel Stenberg
+ * Copyright (c) 2010 Simon Josefsson <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.
+ */
+
+/* Conveniance-macros to allow code like this;
+
+ int rc = BLOCK_ADJUST(rc, session, session_startup(session, sock) );
+
+ int rc = BLOCK_ADJUST_ERRNO(ptr, session, session_startup(session, sock) );
+
+ The point of course being to make sure that while in non-blocking mode
+ these always return no matter what the return code is, but in blocking mode
+ it blocks if EAGAIN is the reason for the return from the underlying
+ function.
+
+*/
+#define BLOCK_ADJUST(rc, sess, x) \
+ do { \
+ time_t entry_time = time(NULL); \
+ do { \
+ rc = x; \
+ /* the order of the check below is important to properly deal with \
+ the case when the 'sess' is freed */ \
+ if((rc != LIBSSH2_ERROR_EAGAIN) || !sess->api_block_mode) \
+ break; \
+ rc = _libssh2_wait_socket(sess, entry_time); \
+ } while(!rc); \
+ } while(0)
+
+/*
+ * For functions that returns a pointer, we need to check if the API is
+ * non-blocking and return immediately. If the pointer is non-NULL we return
+ * immediately. If the API is blocking and we get a NULL we check the errno
+ * and *only* if that is EAGAIN we loop and wait for socket action.
+ */
+#define BLOCK_ADJUST_ERRNO(ptr, sess, x) \
+ do { \
+ time_t entry_time = time(NULL); \
+ int rc; \
+ do { \
+ ptr = x; \
+ if(!sess->api_block_mode || \
+ (ptr != NULL) || \
+ (libssh2_session_last_errno(sess) != LIBSSH2_ERROR_EAGAIN) ) \
+ break; \
+ rc = _libssh2_wait_socket(sess, entry_time); \
+ } while(!rc); \
+ } while(0)
+
+
+int _libssh2_wait_socket(LIBSSH2_SESSION *session, time_t entry_time);
+
+/* this is the lib-internal set blocking function */
+int _libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking);
+
+#endif /* __LIBSSH2_SESSION_H */
diff --git a/contrib/libs/libssh2/src/sftp.c b/contrib/libs/libssh2/src/sftp.c
new file mode 100644
index 00000000000..ac7ee016212
--- /dev/null
+++ b/contrib/libs/libssh2/src/sftp.c
@@ -0,0 +1,3755 @@
+/* Copyright (c) 2004-2008, Sara Golemon <[email protected]>
+ * Copyright (c) 2007 Eli Fant <[email protected]>
+ * Copyright (c) 2009-2019 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 <assert.h>
+
+#include "libssh2_priv.h"
+#include "libssh2_sftp.h"
+#include "channel.h"
+#include "session.h"
+#include "sftp.h"
+
+/* Note: Version 6 was documented at the time of writing
+ * However it was marked as "DO NOT IMPLEMENT" due to pending changes
+ *
+ * This release of libssh2 implements Version 5 with automatic downgrade
+ * based on server's declaration
+ */
+
+/* SFTP packet types */
+#define SSH_FXP_INIT 1
+#define SSH_FXP_VERSION 2
+#define SSH_FXP_OPEN 3
+#define SSH_FXP_CLOSE 4
+#define SSH_FXP_READ 5
+#define SSH_FXP_WRITE 6
+#define SSH_FXP_LSTAT 7
+#define SSH_FXP_FSTAT 8
+#define SSH_FXP_SETSTAT 9
+#define SSH_FXP_FSETSTAT 10
+#define SSH_FXP_OPENDIR 11
+#define SSH_FXP_READDIR 12
+#define SSH_FXP_REMOVE 13
+#define SSH_FXP_MKDIR 14
+#define SSH_FXP_RMDIR 15
+#define SSH_FXP_REALPATH 16
+#define SSH_FXP_STAT 17
+#define SSH_FXP_RENAME 18
+#define SSH_FXP_READLINK 19
+#define SSH_FXP_SYMLINK 20
+#define SSH_FXP_STATUS 101
+#define SSH_FXP_HANDLE 102
+#define SSH_FXP_DATA 103
+#define SSH_FXP_NAME 104
+#define SSH_FXP_ATTRS 105
+#define SSH_FXP_EXTENDED 200
+#define SSH_FXP_EXTENDED_REPLY 201
+
+/* S_IFREG */
+#define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000
+/* S_IFDIR */
+#define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000
+
+#define SSH_FXE_STATVFS_ST_RDONLY 0x00000001
+#define SSH_FXE_STATVFS_ST_NOSUID 0x00000002
+
+/* This is the maximum packet length to accept, as larger than this indicate
+ some kind of server problem. */
+#define LIBSSH2_SFTP_PACKET_MAXLEN (256 * 1024)
+
+static int sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type,
+ uint32_t request_id, unsigned char **data,
+ size_t *data_len);
+static void sftp_packet_flush(LIBSSH2_SFTP *sftp);
+
+/* sftp_attrsize
+ * Size that attr with this flagset will occupy when turned into a bin struct
+ */
+static int sftp_attrsize(unsigned long flags)
+{
+ return (4 + /* flags(4) */
+ ((flags & LIBSSH2_SFTP_ATTR_SIZE) ? 8 : 0) +
+ ((flags & LIBSSH2_SFTP_ATTR_UIDGID) ? 8 : 0) +
+ ((flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) ? 4 : 0) +
+ ((flags & LIBSSH2_SFTP_ATTR_ACMODTIME) ? 8 : 0));
+ /* atime + mtime as u32 */
+}
+
+/* _libssh2_store_u64
+ */
+static void _libssh2_store_u64(unsigned char **ptr, libssh2_uint64_t value)
+{
+ uint32_t msl = (uint32_t)(value >> 32);
+ unsigned char *buf = *ptr;
+
+ buf[0] = (unsigned char)((msl >> 24) & 0xFF);
+ buf[1] = (unsigned char)((msl >> 16) & 0xFF);
+ buf[2] = (unsigned char)((msl >> 8) & 0xFF);
+ buf[3] = (unsigned char)( msl & 0xFF);
+
+ buf[4] = (unsigned char)((value >> 24) & 0xFF);
+ buf[5] = (unsigned char)((value >> 16) & 0xFF);
+ buf[6] = (unsigned char)((value >> 8) & 0xFF);
+ buf[7] = (unsigned char)( value & 0xFF);
+
+ *ptr += 8;
+}
+
+/*
+ * Search list of zombied FXP_READ request IDs.
+ *
+ * Returns NULL if ID not in list.
+ */
+static struct sftp_zombie_requests *
+find_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id)
+{
+ struct sftp_zombie_requests *zombie =
+ _libssh2_list_first(&sftp->zombie_requests);
+
+ while(zombie) {
+ if(zombie->request_id == request_id)
+ break;
+ else
+ zombie = _libssh2_list_next(&zombie->node);
+ }
+
+ return zombie;
+}
+
+static void
+remove_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id)
+{
+ LIBSSH2_SESSION *session = sftp->channel->session;
+
+ struct sftp_zombie_requests *zombie = find_zombie_request(sftp,
+ request_id);
+ if(zombie) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Removing request ID %ld from the list of "
+ "zombie requests",
+ request_id);
+
+ _libssh2_list_remove(&zombie->node);
+ LIBSSH2_FREE(session, zombie);
+ }
+}
+
+static int
+add_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id)
+{
+ LIBSSH2_SESSION *session = sftp->channel->session;
+
+ struct sftp_zombie_requests *zombie;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Marking request ID %ld as a zombie request", request_id);
+
+ zombie = LIBSSH2_ALLOC(sftp->channel->session,
+ sizeof(struct sftp_zombie_requests));
+ if(!zombie)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "malloc fail for zombie request ID");
+ else {
+ zombie->request_id = request_id;
+ _libssh2_list_add(&sftp->zombie_requests, &zombie->node);
+ return LIBSSH2_ERROR_NONE;
+ }
+}
+
+/*
+ * sftp_packet_add
+ *
+ * Add a packet to the SFTP packet brigade
+ */
+static int
+sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data,
+ size_t data_len)
+{
+ LIBSSH2_SESSION *session = sftp->channel->session;
+ LIBSSH2_SFTP_PACKET *packet;
+ uint32_t request_id;
+
+ if(data_len < 5) {
+ return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Received packet type %d (len %d)",
+ (int) data[0], data_len);
+
+ /*
+ * Experience shows that if we mess up EAGAIN handling somewhere or
+ * otherwise get out of sync with the channel, this is where we first get
+ * a wrong byte and if so we need to bail out at once to aid tracking the
+ * problem better.
+ */
+
+ switch(data[0]) {
+ case SSH_FXP_INIT:
+ case SSH_FXP_VERSION:
+ case SSH_FXP_OPEN:
+ case SSH_FXP_CLOSE:
+ case SSH_FXP_READ:
+ case SSH_FXP_WRITE:
+ case SSH_FXP_LSTAT:
+ case SSH_FXP_FSTAT:
+ case SSH_FXP_SETSTAT:
+ case SSH_FXP_FSETSTAT:
+ case SSH_FXP_OPENDIR:
+ case SSH_FXP_READDIR:
+ case SSH_FXP_REMOVE:
+ case SSH_FXP_MKDIR:
+ case SSH_FXP_RMDIR:
+ case SSH_FXP_REALPATH:
+ case SSH_FXP_STAT:
+ case SSH_FXP_RENAME:
+ case SSH_FXP_READLINK:
+ case SSH_FXP_SYMLINK:
+ case SSH_FXP_STATUS:
+ case SSH_FXP_HANDLE:
+ case SSH_FXP_DATA:
+ case SSH_FXP_NAME:
+ case SSH_FXP_ATTRS:
+ case SSH_FXP_EXTENDED:
+ case SSH_FXP_EXTENDED_REPLY:
+ break;
+ default:
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Out of sync with the world");
+ }
+
+ request_id = _libssh2_ntohu32(&data[1]);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Received packet id %d",
+ request_id);
+
+ /* Don't add the packet if it answers a request we've given up on. */
+ if((data[0] == SSH_FXP_STATUS || data[0] == SSH_FXP_DATA)
+ && find_zombie_request(sftp, request_id)) {
+
+ /* If we get here, the file ended before the response arrived. We
+ are no longer interested in the request so we discard it */
+
+ LIBSSH2_FREE(session, data);
+
+ remove_zombie_request(sftp, request_id);
+ return LIBSSH2_ERROR_NONE;
+ }
+
+ packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_PACKET));
+ if(!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate datablock for SFTP packet");
+ }
+
+ packet->data = data;
+ packet->data_len = data_len;
+ packet->request_id = request_id;
+
+ _libssh2_list_add(&sftp->packets, &packet->node);
+
+ return LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * sftp_packet_read
+ *
+ * Frame an SFTP packet off the channel
+ */
+static int
+sftp_packet_read(LIBSSH2_SFTP *sftp)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ unsigned char *packet = NULL;
+ ssize_t rc;
+ unsigned long recv_window;
+ int packet_type;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "recv packet");
+
+ switch(sftp->packet_state) {
+ case libssh2_NB_state_sent: /* EAGAIN from window adjusting */
+ sftp->packet_state = libssh2_NB_state_idle;
+
+ packet = sftp->partial_packet;
+ goto window_adjust;
+
+ case libssh2_NB_state_sent1: /* EAGAIN from channel read */
+ sftp->packet_state = libssh2_NB_state_idle;
+
+ packet = sftp->partial_packet;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "partial read cont, len: %lu", sftp->partial_len);
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "partial read cont, already recvd: %lu",
+ sftp->partial_received);
+ /* fall-through */
+ default:
+ if(!packet) {
+ /* only do this if there's not already a packet buffer allocated
+ to use */
+
+ /* each packet starts with a 32 bit length field */
+ rc = _libssh2_channel_read(channel, 0,
+ (char *)&sftp->partial_size[
+ sftp->partial_size_len],
+ 4 - sftp->partial_size_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if(rc < 0)
+ return _libssh2_error(session, rc, "channel read");
+
+ sftp->partial_size_len += rc;
+
+ if(4 != sftp->partial_size_len)
+ /* we got a short read for the length part */
+ return LIBSSH2_ERROR_EAGAIN;
+
+ sftp->partial_len = _libssh2_ntohu32(sftp->partial_size);
+ /* make sure we don't proceed if the packet size is unreasonably
+ large */
+ if(sftp->partial_len > LIBSSH2_SFTP_PACKET_MAXLEN) {
+ libssh2_channel_flush(channel);
+ sftp->partial_size_len = 0;
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED,
+ "SFTP packet too large");
+ }
+
+ if(sftp->partial_len == 0)
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate empty SFTP packet");
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Data begin - Packet Length: %lu",
+ sftp->partial_len);
+ packet = LIBSSH2_ALLOC(session, sftp->partial_len);
+ if(!packet)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate SFTP packet");
+ sftp->partial_size_len = 0;
+ sftp->partial_received = 0; /* how much of the packet already
+ received */
+ sftp->partial_packet = packet;
+
+ window_adjust:
+ recv_window = libssh2_channel_window_read_ex(channel, NULL, NULL);
+
+ if(sftp->partial_len > recv_window) {
+ /* ask for twice the data amount we need at once */
+ rc = _libssh2_channel_receive_window_adjust(channel,
+ sftp->partial_len
+ * 2,
+ 1, NULL);
+ /* store the state so that we continue with the correct
+ operation at next invoke */
+ sftp->packet_state = (rc == LIBSSH2_ERROR_EAGAIN)?
+ libssh2_NB_state_sent:
+ libssh2_NB_state_idle;
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ }
+ }
+
+ /* Read as much of the packet as we can */
+ while(sftp->partial_len > sftp->partial_received) {
+ rc = _libssh2_channel_read(channel, 0,
+ (char *)&packet[sftp->partial_received],
+ sftp->partial_len -
+ sftp->partial_received);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ /*
+ * We received EAGAIN, save what we have and return EAGAIN to
+ * the caller. Set 'partial_packet' so that this function
+ * knows how to continue on the next invoke.
+ */
+ sftp->packet_state = libssh2_NB_state_sent1;
+ return rc;
+ }
+ else if(rc < 0) {
+ LIBSSH2_FREE(session, packet);
+ sftp->partial_packet = NULL;
+ return _libssh2_error(session, rc,
+ "Error waiting for SFTP packet");
+ }
+ sftp->partial_received += rc;
+ }
+
+ sftp->partial_packet = NULL;
+
+ /* sftp_packet_add takes ownership of the packet and might free it
+ so we take a copy of the packet type before we call it. */
+ packet_type = packet[0];
+ rc = sftp_packet_add(sftp, packet, sftp->partial_len);
+ if(rc) {
+ LIBSSH2_FREE(session, packet);
+ return rc;
+ }
+ else {
+ return packet_type;
+ }
+ }
+ /* WON'T REACH */
+}
+/*
+ * sftp_packetlist_flush
+ *
+ * Remove all pending packets in the packet_list and the corresponding one(s)
+ * in the SFTP packet brigade.
+ */
+static void sftp_packetlist_flush(LIBSSH2_SFTP_HANDLE *handle)
+{
+ struct sftp_pipeline_chunk *chunk;
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_SESSION *session = sftp->channel->session;
+
+ /* remove pending packets, if any */
+ chunk = _libssh2_list_first(&handle->packet_list);
+ while(chunk) {
+ unsigned char *data;
+ size_t data_len;
+ int rc;
+ struct sftp_pipeline_chunk *next = _libssh2_list_next(&chunk->node);
+
+ rc = sftp_packet_ask(sftp, SSH_FXP_STATUS,
+ chunk->request_id, &data, &data_len);
+ if(rc)
+ rc = sftp_packet_ask(sftp, SSH_FXP_DATA,
+ chunk->request_id, &data, &data_len);
+
+ if(!rc)
+ /* we found a packet, free it */
+ LIBSSH2_FREE(session, data);
+ else if(chunk->sent)
+ /* there was no incoming packet for this request, mark this
+ request as a zombie if it ever sent the request */
+ add_zombie_request(sftp, chunk->request_id);
+
+ _libssh2_list_remove(&chunk->node);
+ LIBSSH2_FREE(session, chunk);
+ chunk = next;
+ }
+}
+
+
+/*
+ * sftp_packet_ask()
+ *
+ * Checks if there's a matching SFTP packet available.
+ */
+static int
+sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type,
+ uint32_t request_id, unsigned char **data,
+ size_t *data_len)
+{
+ LIBSSH2_SESSION *session = sftp->channel->session;
+ LIBSSH2_SFTP_PACKET *packet = _libssh2_list_first(&sftp->packets);
+
+ if(!packet)
+ return -1;
+
+ /* Special consideration when getting VERSION packet */
+
+ while(packet) {
+ if((packet->data[0] == packet_type) &&
+ ((packet_type == SSH_FXP_VERSION) ||
+ (packet->request_id == request_id))) {
+
+ /* Match! Fetch the data */
+ *data = packet->data;
+ *data_len = packet->data_len;
+
+ /* unlink and free this struct */
+ _libssh2_list_remove(&packet->node);
+ LIBSSH2_FREE(session, packet);
+
+ return 0;
+ }
+ /* check next struct in the list */
+ packet = _libssh2_list_next(&packet->node);
+ }
+ return -1;
+}
+
+/* sftp_packet_require
+ * A la libssh2_packet_require
+ */
+static int
+sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_type,
+ uint32_t request_id, unsigned char **data,
+ size_t *data_len, size_t required_size)
+{
+ LIBSSH2_SESSION *session = sftp->channel->session;
+ int rc;
+
+ if(data == NULL || data_len == NULL || required_size == 0) {
+ return LIBSSH2_ERROR_BAD_USE;
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Requiring packet %d id %ld",
+ (int) packet_type, request_id);
+
+ if(sftp_packet_ask(sftp, packet_type, request_id, data, data_len) == 0) {
+ /* The right packet was available in the packet brigade */
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d",
+ (int) packet_type);
+
+ if (*data_len < required_size) {
+ return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ return LIBSSH2_ERROR_NONE;
+ }
+
+ while(session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
+ rc = sftp_packet_read(sftp);
+ if(rc < 0)
+ return rc;
+
+ /* data was read, check the queue again */
+ if(!sftp_packet_ask(sftp, packet_type, request_id, data, data_len)) {
+ /* The right packet was available in the packet brigade */
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d",
+ (int) packet_type);
+
+ if (*data_len < required_size) {
+ return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ return LIBSSH2_ERROR_NONE;
+ }
+ }
+
+ /* Only reached if the socket died */
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
+/* sftp_packet_requirev
+ * Require one of N possible responses
+ */
+static int
+sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses,
+ const unsigned char *valid_responses,
+ uint32_t request_id, unsigned char **data,
+ size_t *data_len, size_t required_size)
+{
+ int i;
+ int rc;
+
+ if(data == NULL || data_len == NULL || required_size == 0) {
+ return LIBSSH2_ERROR_BAD_USE;
+ }
+
+ /* If no timeout is active, start a new one */
+ if(sftp->requirev_start == 0)
+ sftp->requirev_start = time(NULL);
+
+ while(sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
+ for(i = 0; i < num_valid_responses; i++) {
+ if(sftp_packet_ask(sftp, valid_responses[i], request_id,
+ data, data_len) == 0) {
+ /*
+ * Set to zero before all returns to say
+ * the timeout is not active
+ */
+ sftp->requirev_start = 0;
+
+ if (*data_len < required_size) {
+ return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ return LIBSSH2_ERROR_NONE;
+ }
+ }
+
+ rc = sftp_packet_read(sftp);
+ if((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN)) {
+ sftp->requirev_start = 0;
+ return rc;
+ }
+ else if(rc <= 0) {
+ /* prevent busy-looping */
+ long left =
+ LIBSSH2_READ_TIMEOUT -
+ (long)(time(NULL) - sftp->requirev_start);
+
+ if(left <= 0) {
+ sftp->requirev_start = 0;
+ return LIBSSH2_ERROR_TIMEOUT;
+ }
+ else if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ }
+ }
+
+ sftp->requirev_start = 0;
+
+ /* Only reached if the socket died */
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+}
+
+/* sftp_attr2bin
+ * Populate attributes into an SFTP block
+ */
+static ssize_t
+sftp_attr2bin(unsigned char *p, const LIBSSH2_SFTP_ATTRIBUTES * attrs)
+{
+ unsigned char *s = p;
+ uint32_t flag_mask =
+ LIBSSH2_SFTP_ATTR_SIZE | LIBSSH2_SFTP_ATTR_UIDGID |
+ LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME;
+
+ /* TODO: When we add SFTP4+ functionality flag_mask can get additional
+ bits */
+
+ if(!attrs) {
+ _libssh2_htonu32(s, 0);
+ return 4;
+ }
+
+ _libssh2_store_u32(&s, attrs->flags & flag_mask);
+
+ if(attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) {
+ _libssh2_store_u64(&s, attrs->filesize);
+ }
+
+ if(attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) {
+ _libssh2_store_u32(&s, attrs->uid);
+ _libssh2_store_u32(&s, attrs->gid);
+ }
+
+ if(attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
+ _libssh2_store_u32(&s, attrs->permissions);
+ }
+
+ if(attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
+ _libssh2_store_u32(&s, attrs->atime);
+ _libssh2_store_u32(&s, attrs->mtime);
+ }
+
+ return (s - p);
+}
+
+/* sftp_bin2attr
+ */
+static int
+sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES *attrs, const unsigned char *p,
+ size_t data_len)
+{
+ struct string_buf buf;
+ uint32_t flags = 0;
+ buf.data = (unsigned char *)p;
+ buf.dataptr = buf.data;
+ buf.len = data_len;
+
+ if(_libssh2_get_u32(&buf, &flags) != 0) {
+ return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ }
+ attrs->flags = flags;
+
+ if(attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) {
+ if(_libssh2_get_u64(&buf, &(attrs->filesize)) != 0) {
+ return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ }
+ }
+
+ if(attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) {
+ uint32_t uid = 0;
+ uint32_t gid = 0;
+ if(_libssh2_get_u32(&buf, &uid) != 0 ||
+ _libssh2_get_u32(&buf, &gid) != 0) {
+ return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ }
+ attrs->uid = uid;
+ attrs->gid = gid;
+ }
+
+ if(attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
+ uint32_t permissions;
+ if(_libssh2_get_u32(&buf, &permissions) != 0) {
+ return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ }
+ attrs->permissions = permissions;
+ }
+
+ if(attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
+ uint32_t atime;
+ uint32_t mtime;
+ if(_libssh2_get_u32(&buf, &atime) != 0 ||
+ _libssh2_get_u32(&buf, &mtime) != 0) {
+ return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ }
+ attrs->atime = atime;
+ attrs->mtime = mtime;
+ }
+
+ return (buf.dataptr - buf.data);
+}
+
+/* ************
+ * SFTP API *
+ ************ */
+
+LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor);
+
+/* libssh2_sftp_dtor
+ * Shutdown an SFTP stream when the channel closes
+ */
+LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor)
+{
+ LIBSSH2_SFTP *sftp = (LIBSSH2_SFTP *) (*channel_abstract);
+
+ (void) session_abstract;
+ (void) channel;
+
+ /* Free the partial packet storage for sftp_packet_read */
+ if(sftp->partial_packet) {
+ LIBSSH2_FREE(session, sftp->partial_packet);
+ }
+
+ /* Free the packet storage for _libssh2_sftp_packet_readdir */
+ if(sftp->readdir_packet) {
+ LIBSSH2_FREE(session, sftp->readdir_packet);
+ }
+
+ LIBSSH2_FREE(session, sftp);
+}
+
+/*
+ * sftp_init
+ *
+ * Startup an SFTP session
+ */
+static LIBSSH2_SFTP *sftp_init(LIBSSH2_SESSION *session)
+{
+ unsigned char *data;
+ size_t data_len;
+ ssize_t rc;
+ LIBSSH2_SFTP *sftp_handle;
+ struct string_buf buf;
+ unsigned char *endp;
+
+ if(session->sftpInit_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Initializing SFTP subsystem");
+
+ /*
+ * The 'sftpInit_sftp' and 'sftpInit_channel' struct fields within the
+ * session struct are only to be used during the setup phase. As soon
+ * as the SFTP session is created they are cleared and can thus be
+ * re-used again to allow any amount of SFTP handles per sessions.
+ *
+ * Note that you MUST NOT try to call libssh2_sftp_init() again to get
+ * another handle until the previous call has finished and either
+ * successfully made a handle or failed and returned error (not
+ * including *EAGAIN).
+ */
+
+ assert(session->sftpInit_sftp == NULL);
+ session->sftpInit_sftp = NULL;
+ session->sftpInit_state = libssh2_NB_state_created;
+ }
+
+ sftp_handle = session->sftpInit_sftp;
+
+ if(session->sftpInit_state == libssh2_NB_state_created) {
+ session->sftpInit_channel =
+ _libssh2_channel_open(session, "session", sizeof("session") - 1,
+ LIBSSH2_CHANNEL_WINDOW_DEFAULT,
+ LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0);
+ if(!session->sftpInit_channel) {
+ if(libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block starting up channel");
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Unable to startup channel");
+ session->sftpInit_state = libssh2_NB_state_idle;
+ }
+ return NULL;
+ }
+
+ session->sftpInit_state = libssh2_NB_state_sent;
+ }
+
+ if(session->sftpInit_state == libssh2_NB_state_sent) {
+ int ret = _libssh2_channel_process_startup(session->sftpInit_channel,
+ "subsystem",
+ sizeof("subsystem") - 1,
+ "sftp",
+ strlen("sftp"));
+ if(ret == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block to request SFTP subsystem");
+ return NULL;
+ }
+ else if(ret) {
+ _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
+ "Unable to request SFTP subsystem");
+ goto sftp_init_error;
+ }
+
+ session->sftpInit_state = libssh2_NB_state_sent1;
+ }
+
+ if(session->sftpInit_state == libssh2_NB_state_sent1) {
+ rc = _libssh2_channel_extended_data(session->sftpInit_channel,
+ LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting handle extended data");
+ return NULL;
+ }
+
+ sftp_handle =
+ session->sftpInit_sftp =
+ LIBSSH2_CALLOC(session, sizeof(LIBSSH2_SFTP));
+ if(!sftp_handle) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate a new SFTP structure");
+ goto sftp_init_error;
+ }
+ sftp_handle->channel = session->sftpInit_channel;
+ sftp_handle->request_id = 0;
+
+ _libssh2_htonu32(session->sftpInit_buffer, 5);
+ session->sftpInit_buffer[4] = SSH_FXP_INIT;
+ _libssh2_htonu32(session->sftpInit_buffer + 5, LIBSSH2_SFTP_VERSION);
+ session->sftpInit_sent = 0; /* nothing's sent yet */
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Sending FXP_INIT packet advertising "
+ "version %d support",
+ (int) LIBSSH2_SFTP_VERSION);
+
+ session->sftpInit_state = libssh2_NB_state_sent2;
+ }
+
+ if(session->sftpInit_state == libssh2_NB_state_sent2) {
+ /* sent off what's left of the init buffer to send */
+ rc = _libssh2_channel_write(session->sftpInit_channel, 0,
+ session->sftpInit_buffer +
+ session->sftpInit_sent,
+ 9 - session->sftpInit_sent);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending SSH_FXP_INIT");
+ return NULL;
+ }
+ else if(rc < 0) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send SSH_FXP_INIT");
+ goto sftp_init_error;
+ }
+ else {
+ /* add up the number of bytes sent */
+ session->sftpInit_sent += rc;
+
+ if(session->sftpInit_sent == 9)
+ /* move on */
+ session->sftpInit_state = libssh2_NB_state_sent3;
+
+ /* if less than 9, we remain in this state to send more later on */
+ }
+ }
+
+ rc = sftp_packet_require(sftp_handle, SSH_FXP_VERSION,
+ 0, &data, &data_len, 5);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block receiving SSH_FXP_VERSION");
+ return NULL;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Invalid SSH_FXP_VERSION response");
+ goto sftp_init_error;
+ }
+ else if(rc) {
+ _libssh2_error(session, rc,
+ "Timeout waiting for response from SFTP subsystem");
+ goto sftp_init_error;
+ }
+
+ buf.data = data;
+ buf.dataptr = buf.data + 1;
+ buf.len = data_len;
+ endp = &buf.data[data_len];
+
+ if(_libssh2_get_u32(&buf, &(sftp_handle->version)) != 0) {
+ LIBSSH2_FREE(session, data);
+ rc = LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ goto sftp_init_error;
+ }
+
+ if(sftp_handle->version > LIBSSH2_SFTP_VERSION) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Truncating remote SFTP version from %lu",
+ sftp_handle->version);
+ sftp_handle->version = LIBSSH2_SFTP_VERSION;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Enabling SFTP version %lu compatibility",
+ sftp_handle->version);
+ while(buf.dataptr < endp) {
+ unsigned char *extname, *extdata;
+
+ if(_libssh2_get_string(&buf, &extname, NULL)) {
+ LIBSSH2_FREE(session, data);
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Data too short when extracting extname");
+ goto sftp_init_error;
+ }
+
+ if(_libssh2_get_string(&buf, &extdata, NULL)) {
+ LIBSSH2_FREE(session, data);
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "Data too short when extracting extdata");
+ goto sftp_init_error;
+ }
+ }
+ LIBSSH2_FREE(session, data);
+
+ /* Make sure that when the channel gets closed, the SFTP service is shut
+ down too */
+ sftp_handle->channel->abstract = sftp_handle;
+ sftp_handle->channel->close_cb = libssh2_sftp_dtor;
+
+ session->sftpInit_state = libssh2_NB_state_idle;
+
+ /* clear the sftp and channel pointers in this session struct now */
+ session->sftpInit_sftp = NULL;
+ session->sftpInit_channel = NULL;
+
+ _libssh2_list_init(&sftp_handle->sftp_handles);
+
+ return sftp_handle;
+
+ sftp_init_error:
+ while(_libssh2_channel_free(session->sftpInit_channel) ==
+ LIBSSH2_ERROR_EAGAIN);
+ session->sftpInit_channel = NULL;
+ if(session->sftpInit_sftp) {
+ LIBSSH2_FREE(session, session->sftpInit_sftp);
+ session->sftpInit_sftp = NULL;
+ }
+ session->sftpInit_state = libssh2_NB_state_idle;
+ return NULL;
+}
+
+/*
+ * libssh2_sftp_init
+ *
+ * Startup an SFTP session
+ */
+LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
+{
+ LIBSSH2_SFTP *ptr;
+
+ if(!session)
+ return NULL;
+
+ if(!(session->state & LIBSSH2_STATE_AUTHENTICATED)) {
+ _libssh2_error(session, LIBSSH2_ERROR_INVAL,
+ "session not authenticated yet");
+ return NULL;
+ }
+
+ BLOCK_ADJUST_ERRNO(ptr, session, sftp_init(session));
+ return ptr;
+}
+
+/*
+ * sftp_shutdown
+ *
+ * Shuts down the SFTP subsystem
+ */
+static int
+sftp_shutdown(LIBSSH2_SFTP *sftp)
+{
+ int rc;
+ LIBSSH2_SESSION *session = sftp->channel->session;
+ /*
+ * Make sure all memory used in the state variables are free
+ */
+ if(sftp->partial_packet) {
+ LIBSSH2_FREE(session, sftp->partial_packet);
+ sftp->partial_packet = NULL;
+ }
+ if(sftp->open_packet) {
+ LIBSSH2_FREE(session, sftp->open_packet);
+ sftp->open_packet = NULL;
+ }
+ if(sftp->readdir_packet) {
+ LIBSSH2_FREE(session, sftp->readdir_packet);
+ sftp->readdir_packet = NULL;
+ }
+ if(sftp->fstat_packet) {
+ LIBSSH2_FREE(session, sftp->fstat_packet);
+ sftp->fstat_packet = NULL;
+ }
+ if(sftp->unlink_packet) {
+ LIBSSH2_FREE(session, sftp->unlink_packet);
+ sftp->unlink_packet = NULL;
+ }
+ if(sftp->rename_packet) {
+ LIBSSH2_FREE(session, sftp->rename_packet);
+ sftp->rename_packet = NULL;
+ }
+ if(sftp->fstatvfs_packet) {
+ LIBSSH2_FREE(session, sftp->fstatvfs_packet);
+ sftp->fstatvfs_packet = NULL;
+ }
+ if(sftp->statvfs_packet) {
+ LIBSSH2_FREE(session, sftp->statvfs_packet);
+ sftp->statvfs_packet = NULL;
+ }
+ if(sftp->mkdir_packet) {
+ LIBSSH2_FREE(session, sftp->mkdir_packet);
+ sftp->mkdir_packet = NULL;
+ }
+ if(sftp->rmdir_packet) {
+ LIBSSH2_FREE(session, sftp->rmdir_packet);
+ sftp->rmdir_packet = NULL;
+ }
+ if(sftp->stat_packet) {
+ LIBSSH2_FREE(session, sftp->stat_packet);
+ sftp->stat_packet = NULL;
+ }
+ if(sftp->symlink_packet) {
+ LIBSSH2_FREE(session, sftp->symlink_packet);
+ sftp->symlink_packet = NULL;
+ }
+ if(sftp->fsync_packet) {
+ LIBSSH2_FREE(session, sftp->fsync_packet);
+ sftp->fsync_packet = NULL;
+ }
+
+ sftp_packet_flush(sftp);
+
+ /* TODO: We should consider walking over the sftp_handles list and kill
+ * any remaining sftp handles ... */
+
+ rc = _libssh2_channel_free(sftp->channel);
+
+ return rc;
+}
+
+/* libssh2_sftp_shutdown
+ * Shutsdown the SFTP subsystem
+ */
+LIBSSH2_API int
+libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session, sftp_shutdown(sftp));
+ return rc;
+}
+
+/* *******************************
+ * SFTP File and Directory Ops *
+ ******************************* */
+
+/* sftp_open
+ */
+static LIBSSH2_SFTP_HANDLE *
+sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
+ size_t filename_len, uint32_t flags, long mode,
+ int open_type)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ LIBSSH2_SFTP_HANDLE *fp;
+ LIBSSH2_SFTP_ATTRIBUTES attrs = {
+ LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0
+ };
+ unsigned char *s;
+ ssize_t rc;
+ int open_file = (open_type == LIBSSH2_SFTP_OPENFILE)?1:0;
+
+ if(sftp->open_state == libssh2_NB_state_idle) {
+ /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) +
+ flags(4) */
+ sftp->open_packet_len = filename_len + 13 +
+ (open_file? (4 +
+ sftp_attrsize(LIBSSH2_SFTP_ATTR_PERMISSIONS)) : 0);
+
+ /* surprise! this starts out with nothing sent */
+ sftp->open_packet_sent = 0;
+ s = sftp->open_packet = LIBSSH2_ALLOC(session, sftp->open_packet_len);
+ if(!sftp->open_packet) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_OPEN or "
+ "FXP_OPENDIR packet");
+ return NULL;
+ }
+ /* Filetype in SFTP 3 and earlier */
+ attrs.permissions = mode |
+ (open_file ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE :
+ LIBSSH2_SFTP_ATTR_PFILETYPE_DIR);
+
+ _libssh2_store_u32(&s, sftp->open_packet_len - 4);
+ *(s++) = open_file? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
+ sftp->open_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->open_request_id);
+ _libssh2_store_str(&s, filename, filename_len);
+
+ if(open_file) {
+ _libssh2_store_u32(&s, flags);
+ s += sftp_attr2bin(s, &attrs);
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Sending %s open request",
+ open_file? "file" : "directory");
+
+ sftp->open_state = libssh2_NB_state_created;
+ }
+
+ if(sftp->open_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->open_packet+
+ sftp->open_packet_sent,
+ sftp->open_packet_len -
+ sftp->open_packet_sent);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block sending FXP_OPEN or "
+ "FXP_OPENDIR command");
+ return NULL;
+ }
+ else if(rc < 0) {
+ _libssh2_error(session, rc, "Unable to send FXP_OPEN*");
+ LIBSSH2_FREE(session, sftp->open_packet);
+ sftp->open_packet = NULL;
+ sftp->open_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ /* bump the sent counter and remain in this state until the whole
+ data is off */
+ sftp->open_packet_sent += rc;
+
+ if(sftp->open_packet_len == sftp->open_packet_sent) {
+ LIBSSH2_FREE(session, sftp->open_packet);
+ sftp->open_packet = NULL;
+
+ sftp->open_state = libssh2_NB_state_sent;
+ }
+ }
+
+ if(sftp->open_state == libssh2_NB_state_sent) {
+ size_t data_len;
+ unsigned char *data;
+ static const unsigned char fopen_responses[2] =
+ { SSH_FXP_HANDLE, SSH_FXP_STATUS };
+ rc = sftp_packet_requirev(sftp, 2, fopen_responses,
+ sftp->open_request_id, &data,
+ &data_len, 1);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting for status message");
+ return NULL;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Response too small");
+ return NULL;
+ }
+ sftp->open_state = libssh2_NB_state_idle;
+ if(rc) {
+ _libssh2_error(session, rc, "Timeout waiting for status message");
+ return NULL;
+ }
+
+ /* OPEN can basically get STATUS or HANDLE back, where HANDLE implies
+ a fine response while STATUS means error. It seems though that at
+ times we get an SSH_FX_OK back in a STATUS, followed the "real"
+ HANDLE so we need to properly deal with that. */
+ if(data[0] == SSH_FXP_STATUS) {
+ int badness = 1;
+
+ if(data_len < 9) {
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Too small FXP_STATUS");
+ LIBSSH2_FREE(session, data);
+ return NULL;
+ }
+
+ sftp->last_errno = _libssh2_ntohu32(data + 5);
+
+ if(LIBSSH2_FX_OK == sftp->last_errno) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "got HANDLE FXOK!");
+
+ LIBSSH2_FREE(session, data);
+
+ /* silly situation, but check for a HANDLE */
+ rc = sftp_packet_require(sftp, SSH_FXP_HANDLE,
+ sftp->open_request_id, &data,
+ &data_len, 10);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ /* go back to sent state and wait for something else */
+ sftp->open_state = libssh2_NB_state_sent;
+ return NULL;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Too small FXP_HANDLE");
+ return NULL;
+ }
+ else if(!rc)
+ /* we got the handle so this is not a bad situation */
+ badness = 0;
+ }
+
+ if(badness) {
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Failed opening remote file");
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "got FXP_STATUS %d",
+ sftp->last_errno);
+ LIBSSH2_FREE(session, data);
+ return NULL;
+ }
+ }
+
+ if(data_len < 10) {
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Too small FXP_HANDLE");
+ LIBSSH2_FREE(session, data);
+ return NULL;
+ }
+
+ fp = LIBSSH2_CALLOC(session, sizeof(LIBSSH2_SFTP_HANDLE));
+ if(!fp) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate new SFTP handle structure");
+ LIBSSH2_FREE(session, data);
+ return NULL;
+ }
+ fp->handle_type = open_file ? LIBSSH2_SFTP_HANDLE_FILE :
+ LIBSSH2_SFTP_HANDLE_DIR;
+
+ fp->handle_len = _libssh2_ntohu32(data + 5);
+ if(fp->handle_len > SFTP_HANDLE_MAXLEN)
+ /* SFTP doesn't allow handles longer than 256 characters */
+ fp->handle_len = SFTP_HANDLE_MAXLEN;
+
+ if(fp->handle_len > (data_len - 9))
+ /* do not reach beyond the end of the data we got */
+ fp->handle_len = data_len - 9;
+
+ memcpy(fp->handle, data + 9, fp->handle_len);
+
+ LIBSSH2_FREE(session, data);
+
+ /* add this file handle to the list kept in the sftp session */
+ _libssh2_list_add(&sftp->sftp_handles, &fp->node);
+
+ fp->sftp = sftp; /* point to the parent struct */
+
+ fp->u.file.offset = 0;
+ fp->u.file.offset_sent = 0;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Open command successful");
+ return fp;
+ }
+ return NULL;
+}
+
+/* libssh2_sftp_open_ex
+ */
+LIBSSH2_API LIBSSH2_SFTP_HANDLE *
+libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, const char *filename,
+ unsigned int filename_len, unsigned long flags, long mode,
+ int open_type)
+{
+ LIBSSH2_SFTP_HANDLE *hnd;
+
+ if(!sftp)
+ return NULL;
+
+ BLOCK_ADJUST_ERRNO(hnd, sftp->channel->session,
+ sftp_open(sftp, filename, filename_len, flags, mode,
+ open_type));
+ return hnd;
+}
+
+/*
+ * sftp_read
+ *
+ * Read from an SFTP file handle
+ *
+ */
+static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
+ size_t buffer_size)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t count = 0;
+ struct sftp_pipeline_chunk *chunk;
+ struct sftp_pipeline_chunk *next;
+ ssize_t rc;
+ struct _libssh2_sftp_handle_file_data *filep =
+ &handle->u.file;
+ size_t bytes_in_buffer = 0;
+ char *sliding_bufferp = buffer;
+
+ /* This function can be interrupted in three different places where it
+ might need to wait for data from the network. It returns EAGAIN to
+ allow non-blocking clients to do other work but these client are
+ expected to call this function again (possibly many times) to finish
+ the operation.
+
+ The tricky part is that if we previously aborted a sftp_read due to
+ EAGAIN, we must continue at the same spot to continue the previously
+ interrupted operation. This is done using a state machine to record
+ what phase of execution we were at. The state is stored in
+ sftp->read_state.
+
+ libssh2_NB_state_idle: The first phase is where we prepare multiple
+ FXP_READ packets to do optimistic read-ahead. We send off as many as
+ possible in the second phase without waiting for a response to each
+ one; this is the key to fast reads. But we may have to adjust the
+ channel window size to do this which may interrupt this function while
+ waiting. The state machine saves the phase as libssh2_NB_state_idle so
+ it returns here on the next call.
+
+ libssh2_NB_state_sent: The second phase is where we send the FXP_READ
+ packets. Writing them to the channel can be interrupted with EAGAIN
+ but the state machine ensures we skip the first phase on the next call
+ and resume sending.
+
+ libssh2_NB_state_sent2: In the third phase (indicated by ) we read the
+ data from the responses that have arrived so far. Reading can be
+ interrupted with EAGAIN but the state machine ensures we skip the first
+ and second phases on the next call and resume sending.
+ */
+
+ switch(sftp->read_state) {
+ case libssh2_NB_state_idle:
+
+ /* Some data may already have been read from the server in the
+ previous call but didn't fit in the buffer at the time. If so, we
+ return that now as we can't risk being interrupted later with data
+ partially written to the buffer. */
+ if(filep->data_left) {
+ size_t copy = MIN(buffer_size, filep->data_left);
+
+ memcpy(buffer, &filep->data[ filep->data_len - filep->data_left],
+ copy);
+
+ filep->data_left -= copy;
+ filep->offset += copy;
+
+ if(!filep->data_left) {
+ LIBSSH2_FREE(session, filep->data);
+ filep->data = NULL;
+ }
+
+ return copy;
+ }
+
+ if(filep->eof) {
+ return 0;
+ }
+ else {
+ /* We allow a number of bytes being requested at any given time
+ without having been acked - until we reach EOF. */
+
+ /* Number of bytes asked for that haven't been acked yet */
+ size_t already = (size_t)(filep->offset_sent - filep->offset);
+
+ size_t max_read_ahead = buffer_size*4;
+ unsigned long recv_window;
+
+ if(max_read_ahead > LIBSSH2_CHANNEL_WINDOW_DEFAULT*4)
+ max_read_ahead = LIBSSH2_CHANNEL_WINDOW_DEFAULT*4;
+
+ /* if the buffer_size passed in now is smaller than what has
+ already been sent, we risk getting count become a very large
+ number */
+ if(max_read_ahead > already)
+ count = max_read_ahead - already;
+
+ /* 'count' is how much more data to ask for, and 'already' is how
+ much data that already has been asked for but not yet returned.
+ Specifically, 'count' means how much data that have or will be
+ asked for by the nodes that are already added to the linked
+ list. Some of those read requests may not actually have been
+ sent off successfully yet.
+
+ If 'already' is very large it should be perfectly fine to have
+ count set to 0 as then we don't have to ask for more data
+ (right now).
+
+ buffer_size*4 is just picked more or less out of the air. The
+ idea is that when reading SFTP from a remote server, we send
+ away multiple read requests guessing that the client will read
+ more than only this 'buffer_size' amount of memory. So we ask
+ for maximum buffer_size*4 amount of data so that we can return
+ them very fast in subsequent calls.
+ */
+
+ recv_window = libssh2_channel_window_read_ex(sftp->channel,
+ NULL, NULL);
+ if(max_read_ahead > recv_window) {
+ /* more data will be asked for than what the window currently
+ allows, expand it! */
+
+ rc = _libssh2_channel_receive_window_adjust(sftp->channel,
+ max_read_ahead*8,
+ 1, NULL);
+ /* if this returns EAGAIN, we will get back to this function
+ at next call */
+ assert(rc != LIBSSH2_ERROR_EAGAIN || !filep->data_left);
+ assert(rc != LIBSSH2_ERROR_EAGAIN || !filep->eof);
+ if(rc)
+ return rc;
+ }
+ }
+
+ while(count > 0) {
+ unsigned char *s;
+
+ /* 25 = packet_len(4) + packet_type(1) + request_id(4) +
+ handle_len(4) + offset(8) + count(4) */
+ uint32_t packet_len = (uint32_t)handle->handle_len + 25;
+ uint32_t request_id;
+
+ uint32_t size = count;
+ if(size < buffer_size)
+ size = buffer_size;
+ if(size > MAX_SFTP_READ_SIZE)
+ size = MAX_SFTP_READ_SIZE;
+
+ chunk = LIBSSH2_ALLOC(session, packet_len +
+ sizeof(struct sftp_pipeline_chunk));
+ if(!chunk)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "malloc fail for FXP_WRITE");
+
+ chunk->offset = filep->offset_sent;
+ chunk->len = size;
+ chunk->lefttosend = packet_len;
+ chunk->sent = 0;
+
+ s = chunk->packet;
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *s++ = SSH_FXP_READ;
+ request_id = sftp->request_id++;
+ chunk->request_id = request_id;
+ _libssh2_store_u32(&s, request_id);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+ _libssh2_store_u64(&s, filep->offset_sent);
+ filep->offset_sent += size; /* advance offset at once */
+ _libssh2_store_u32(&s, size);
+
+ /* add this new entry LAST in the list */
+ _libssh2_list_add(&handle->packet_list, &chunk->node);
+ count -= MIN(size, count); /* deduct the size we used, as we might
+ * have to create more packets */
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "read request id %d sent (offset: %d, size: %d)",
+ request_id, (int)chunk->offset, (int)chunk->len);
+ }
+ /* FALL-THROUGH */
+ case libssh2_NB_state_sent:
+
+ sftp->read_state = libssh2_NB_state_idle;
+
+ /* move through the READ packets that haven't been sent and send as
+ many as possible - remember that we don't block */
+ chunk = _libssh2_list_first(&handle->packet_list);
+
+ while(chunk) {
+ if(chunk->lefttosend) {
+
+ rc = _libssh2_channel_write(channel, 0,
+ &chunk->packet[chunk->sent],
+ chunk->lefttosend);
+ if(rc < 0) {
+ sftp->read_state = libssh2_NB_state_sent;
+ return rc;
+ }
+
+ /* remember where to continue sending the next time */
+ chunk->lefttosend -= rc;
+ chunk->sent += rc;
+
+ if(chunk->lefttosend) {
+ /* We still have data left to send for this chunk.
+ * If there is at least one completely sent chunk,
+ * we can get out of this loop and start reading. */
+ if(chunk != _libssh2_list_first(&handle->packet_list)) {
+ break;
+ }
+ else {
+ continue;
+ }
+ }
+ }
+
+ /* move on to the next chunk with data to send */
+ chunk = _libssh2_list_next(&chunk->node);
+ }
+ /* FALL-THROUGH */
+
+ case libssh2_NB_state_sent2:
+
+ sftp->read_state = libssh2_NB_state_idle;
+
+ /*
+ * Count all ACKed packets and act on the contents of them.
+ */
+ chunk = _libssh2_list_first(&handle->packet_list);
+
+ while(chunk) {
+ unsigned char *data;
+ size_t data_len;
+ uint32_t rc32;
+ static const unsigned char read_responses[2] = {
+ SSH_FXP_DATA, SSH_FXP_STATUS
+ };
+
+ if(chunk->lefttosend) {
+ /* if the chunk still has data left to send, we shouldn't wait
+ for an ACK for it just yet */
+ if(bytes_in_buffer > 0) {
+ return bytes_in_buffer;
+ }
+ else {
+ /* we should never reach this point */
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "sftp_read() internal error");
+ }
+ }
+
+ rc = sftp_packet_requirev(sftp, 2, read_responses,
+ chunk->request_id, &data, &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN && bytes_in_buffer != 0) {
+ /* do not return EAGAIN if we have already
+ * written data into the buffer */
+ return bytes_in_buffer;
+ }
+
+ if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Response too small");
+ }
+ else if(rc < 0) {
+ sftp->read_state = libssh2_NB_state_sent2;
+ return rc;
+ }
+
+ /*
+ * We get DATA or STATUS back. STATUS can be error, or it is
+ * FX_EOF when we reach the end of the file.
+ */
+
+ switch(data[0]) {
+ case SSH_FXP_STATUS:
+ /* remove the chunk we just processed */
+
+ _libssh2_list_remove(&chunk->node);
+ LIBSSH2_FREE(session, chunk);
+
+ /* we must remove all outstanding READ requests, as either we
+ got an error or we're at end of file */
+ sftp_packetlist_flush(handle);
+
+ rc32 = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if(rc32 == LIBSSH2_FX_EOF) {
+ filep->eof = TRUE;
+ return bytes_in_buffer;
+ }
+ else {
+ sftp->last_errno = rc32;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP READ error");
+ }
+ break;
+
+ case SSH_FXP_DATA:
+ if(chunk->offset != filep->offset) {
+ /* This could happen if the server returns less bytes than
+ requested, which shouldn't happen for normal files. See:
+ https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
+ #section-6.4
+ */
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Read Packet At Unexpected Offset");
+ }
+
+ rc32 = _libssh2_ntohu32(data + 5);
+ if(rc32 > (data_len - 9))
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol badness");
+
+ if(rc32 > chunk->len) {
+ /* A chunk larger than we requested was returned to us.
+ This is a protocol violation and we don't know how to
+ deal with it. Bail out! */
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "FXP_READ response too big");
+ }
+
+ if(rc32 != chunk->len) {
+ /* a short read does not imply end of file, but we must
+ adjust the offset_sent since it was advanced with a
+ full chunk->len before */
+ filep->offset_sent -= (chunk->len - rc32);
+ }
+
+ if((bytes_in_buffer + rc32) > buffer_size) {
+ /* figure out the overlap amount */
+ filep->data_left = (bytes_in_buffer + rc32) - buffer_size;
+
+ /* getting the full packet would overflow the buffer, so
+ only get the correct amount and keep the remainder */
+ rc32 = (uint32_t)buffer_size - bytes_in_buffer;
+
+ /* store data to keep for next call */
+ filep->data = data;
+ filep->data_len = data_len;
+ }
+ else
+ filep->data_len = 0;
+
+ /* copy the received data from the received FXP_DATA packet to
+ the buffer at the correct index */
+ memcpy(sliding_bufferp, data + 9, rc32);
+ filep->offset += rc32;
+ bytes_in_buffer += rc32;
+ sliding_bufferp += rc32;
+
+ if(filep->data_len == 0)
+ /* free the allocated data if not stored to keep */
+ LIBSSH2_FREE(session, data);
+
+ /* remove the chunk we just processed keeping track of the
+ * next one in case we need it */
+ next = _libssh2_list_next(&chunk->node);
+ _libssh2_list_remove(&chunk->node);
+ LIBSSH2_FREE(session, chunk);
+
+ /* check if we have space left in the buffer
+ * and either continue to the next chunk or stop
+ */
+ if(bytes_in_buffer < buffer_size) {
+ chunk = next;
+ }
+ else {
+ chunk = NULL;
+ }
+
+ break;
+ default:
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol badness: unrecognised "
+ "read request response");
+ }
+ }
+
+ if(bytes_in_buffer > 0)
+ return bytes_in_buffer;
+
+ break;
+
+ default:
+ assert(!"State machine error; unrecognised read state");
+ }
+
+ /* we should never reach this point */
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "sftp_read() internal error");
+}
+
+/* libssh2_sftp_read
+ * Read from an SFTP file handle
+ */
+LIBSSH2_API ssize_t
+libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *hnd, char *buffer,
+ size_t buffer_maxlen)
+{
+ ssize_t rc;
+ if(!hnd)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session,
+ sftp_read(hnd, buffer, buffer_maxlen));
+ return rc;
+}
+
+/* sftp_readdir
+ * Read from an SFTP directory handle
+ */
+static ssize_t sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
+ size_t buffer_maxlen, char *longentry,
+ size_t longentry_maxlen,
+ LIBSSH2_SFTP_ATTRIBUTES *attrs)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ uint32_t num_names;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
+ uint32_t packet_len = handle->handle_len + 13;
+ unsigned char *s, *data;
+ static const unsigned char read_responses[2] = {
+ SSH_FXP_NAME, SSH_FXP_STATUS };
+ ssize_t retcode;
+
+ if(sftp->readdir_state == libssh2_NB_state_idle) {
+ if(handle->u.dir.names_left) {
+ /*
+ * A prior request returned more than one directory entry,
+ * feed it back from the buffer
+ */
+ LIBSSH2_SFTP_ATTRIBUTES attrs_dummy;
+ size_t real_longentry_len;
+ size_t real_filename_len;
+ size_t filename_len;
+ size_t longentry_len;
+ size_t names_packet_len = handle->u.dir.names_packet_len;
+ int attr_len = 0;
+
+ if(names_packet_len >= 4) {
+ s = (unsigned char *) handle->u.dir.next_name;
+ real_filename_len = _libssh2_ntohu32(s);
+ s += 4;
+ names_packet_len -= 4;
+ }
+ else {
+ filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ goto end;
+ }
+
+ filename_len = real_filename_len;
+ if(filename_len >= buffer_maxlen) {
+ filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ goto end;
+ }
+
+ if(buffer_maxlen >= filename_len && names_packet_len >=
+ filename_len) {
+ memcpy(buffer, s, filename_len);
+ buffer[filename_len] = '\0'; /* zero terminate */
+ s += real_filename_len;
+ names_packet_len -= real_filename_len;
+ }
+ else {
+ filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ goto end;
+ }
+
+ if(names_packet_len >= 4) {
+ real_longentry_len = _libssh2_ntohu32(s);
+ s += 4;
+ names_packet_len -= 4;
+ }
+ else {
+ filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ goto end;
+ }
+
+ if(longentry && (longentry_maxlen>1)) {
+ longentry_len = real_longentry_len;
+
+ if(longentry_len >= longentry_maxlen ||
+ longentry_len > names_packet_len) {
+ filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ goto end;
+ }
+
+ memcpy(longentry, s, longentry_len);
+ longentry[longentry_len] = '\0'; /* zero terminate */
+ }
+
+ if(real_longentry_len <= names_packet_len) {
+ s += real_longentry_len;
+ names_packet_len -= real_longentry_len;
+ }
+ else {
+ filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ goto end;
+ }
+
+ if(attrs)
+ memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+
+ attr_len = sftp_bin2attr(attrs ? attrs : &attrs_dummy, s,
+ names_packet_len);
+
+ if(attr_len >= 0) {
+ s += attr_len;
+ names_packet_len -= attr_len;
+ }
+ else {
+ filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ goto end;
+ }
+
+ handle->u.dir.next_name = (char *) s;
+ handle->u.dir.names_packet_len = names_packet_len;
+ end:
+
+ if((--handle->u.dir.names_left) == 0)
+ LIBSSH2_FREE(session, handle->u.dir.names_packet);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "libssh2_sftp_readdir_ex() return %d",
+ filename_len);
+ return (ssize_t)filename_len;
+ }
+
+ /* Request another entry(entries?) */
+
+ s = sftp->readdir_packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!sftp->readdir_packet)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "FXP_READDIR packet");
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_READDIR;
+ sftp->readdir_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->readdir_request_id);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+
+ sftp->readdir_state = libssh2_NB_state_created;
+ }
+
+ if(sftp->readdir_state == libssh2_NB_state_created) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Reading entries from directory handle");
+ retcode = _libssh2_channel_write(channel, 0, sftp->readdir_packet,
+ packet_len);
+ if(retcode == LIBSSH2_ERROR_EAGAIN) {
+ return retcode;
+ }
+ else if((ssize_t)packet_len != retcode) {
+ LIBSSH2_FREE(session, sftp->readdir_packet);
+ sftp->readdir_packet = NULL;
+ sftp->readdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+
+ LIBSSH2_FREE(session, sftp->readdir_packet);
+ sftp->readdir_packet = NULL;
+
+ sftp->readdir_state = libssh2_NB_state_sent;
+ }
+
+ retcode = sftp_packet_requirev(sftp, 2, read_responses,
+ sftp->readdir_request_id, &data,
+ &data_len, 9);
+ if(retcode == LIBSSH2_ERROR_EAGAIN)
+ return retcode;
+ else if(retcode == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Status message too short");
+ }
+ else if(retcode) {
+ sftp->readdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, retcode,
+ "Timeout waiting for status message");
+ }
+
+ if(data[0] == SSH_FXP_STATUS) {
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+ if(retcode == LIBSSH2_FX_EOF) {
+ sftp->readdir_state = libssh2_NB_state_idle;
+ return 0;
+ }
+ else {
+ sftp->last_errno = retcode;
+ sftp->readdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+ }
+
+ sftp->readdir_state = libssh2_NB_state_idle;
+
+ num_names = _libssh2_ntohu32(data + 5);
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%lu entries returned",
+ num_names);
+ if(!num_names) {
+ LIBSSH2_FREE(session, data);
+ return 0;
+ }
+
+ handle->u.dir.names_left = num_names;
+ handle->u.dir.names_packet = data;
+ handle->u.dir.next_name = (char *) data + 9;
+ handle->u.dir.names_packet_len = data_len - 9;
+
+ /* use the name popping mechanism from the start of the function */
+ return sftp_readdir(handle, buffer, buffer_maxlen, longentry,
+ longentry_maxlen, attrs);
+}
+
+/* libssh2_sftp_readdir_ex
+ * Read from an SFTP directory handle
+ */
+LIBSSH2_API int
+libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *hnd, char *buffer,
+ size_t buffer_maxlen, char *longentry,
+ size_t longentry_maxlen,
+ LIBSSH2_SFTP_ATTRIBUTES *attrs)
+{
+ int rc;
+ if(!hnd)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session,
+ sftp_readdir(hnd, buffer, buffer_maxlen, longentry,
+ longentry_maxlen, attrs));
+ return rc;
+}
+
+/*
+ * sftp_write
+ *
+ * Write data to an SFTP handle. Returns the number of bytes written, or
+ * a negative error code.
+ *
+ * We recommend sending very large data buffers to this function!
+ *
+ * Concept:
+ *
+ * - Detect how much of the given buffer that was already sent in a previous
+ * call by inspecting the linked list of outgoing chunks. Make sure to skip
+ * passed the data that has already been taken care of.
+ *
+ * - Split all (new) outgoing data in chunks no larger than N.
+ *
+ * - Each N bytes chunk gets created as a separate SFTP packet.
+ *
+ * - Add all created outgoing packets to the linked list.
+ *
+ * - Walk through the list and send the chunks that haven't been sent,
+ * as many as possible until EAGAIN. Some of the chunks may have been put
+ * in the list in a previous invoke.
+ *
+ * - For all the chunks in the list that have been completely sent off, check
+ * for ACKs. If a chunk has been ACKed, it is removed from the linked
+ * list and the "acked" counter gets increased with that data amount.
+ *
+ * - Return TOTAL bytes acked so far.
+ *
+ * Caveats:
+ * - be careful: we must not return a higher number than what was given!
+ *
+ * TODO:
+ * Introduce an option that disables this sort of "speculative" ahead writing
+ * as there's a risk that it will do harm to some app.
+ */
+
+static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
+ size_t count)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ uint32_t retcode;
+ uint32_t packet_len;
+ unsigned char *s, *data;
+ ssize_t rc;
+ struct sftp_pipeline_chunk *chunk;
+ struct sftp_pipeline_chunk *next;
+ size_t acked = 0;
+ size_t org_count = count;
+ size_t already;
+
+ switch(sftp->write_state) {
+ default:
+ case libssh2_NB_state_idle:
+
+ /* Number of bytes sent off that haven't been acked and therefore we
+ will get passed in here again.
+
+ Also, add up the number of bytes that actually already have been
+ acked but we haven't been able to return as such yet, so we will
+ get that data as well passed in here again.
+ */
+ already = (size_t) (handle->u.file.offset_sent -
+ handle->u.file.offset)+
+ handle->u.file.acked;
+
+ if(count >= already) {
+ /* skip the part already made into packets */
+ buffer += already;
+ count -= already;
+ }
+ else
+ /* there is more data already fine than what we got in this call */
+ count = 0;
+
+ sftp->write_state = libssh2_NB_state_idle;
+ while(count) {
+ /* TODO: Possibly this should have some logic to prevent a very
+ very small fraction to be left but lets ignore that for now */
+ uint32_t size = MIN(MAX_SFTP_OUTGOING_SIZE, count);
+ uint32_t request_id;
+
+ /* 25 = packet_len(4) + packet_type(1) + request_id(4) +
+ handle_len(4) + offset(8) + count(4) */
+ packet_len = handle->handle_len + size + 25;
+
+ chunk = LIBSSH2_ALLOC(session, packet_len +
+ sizeof(struct sftp_pipeline_chunk));
+ if(!chunk)
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "malloc fail for FXP_WRITE");
+
+ chunk->len = size;
+ chunk->sent = 0;
+ chunk->lefttosend = packet_len;
+
+ s = chunk->packet;
+ _libssh2_store_u32(&s, packet_len - 4);
+
+ *(s++) = SSH_FXP_WRITE;
+ request_id = sftp->request_id++;
+ chunk->request_id = request_id;
+ _libssh2_store_u32(&s, request_id);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+ _libssh2_store_u64(&s, handle->u.file.offset_sent);
+ handle->u.file.offset_sent += size; /* advance offset at once */
+ _libssh2_store_str(&s, buffer, size);
+
+ /* add this new entry LAST in the list */
+ _libssh2_list_add(&handle->packet_list, &chunk->node);
+
+ buffer += size;
+ count -= size; /* deduct the size we used, as we might have
+ to create more packets */
+ }
+
+ /* move through the WRITE packets that haven't been sent and send as
+ many as possible - remember that we don't block */
+ chunk = _libssh2_list_first(&handle->packet_list);
+
+ while(chunk) {
+ if(chunk->lefttosend) {
+ rc = _libssh2_channel_write(channel, 0,
+ &chunk->packet[chunk->sent],
+ chunk->lefttosend);
+ if(rc < 0)
+ /* remain in idle state */
+ return rc;
+
+ /* remember where to continue sending the next time */
+ chunk->lefttosend -= rc;
+ chunk->sent += rc;
+
+ if(chunk->lefttosend)
+ /* data left to send, get out of loop */
+ break;
+ }
+
+ /* move on to the next chunk with data to send */
+ chunk = _libssh2_list_next(&chunk->node);
+ }
+
+ /* fall-through */
+ case libssh2_NB_state_sent:
+
+ sftp->write_state = libssh2_NB_state_idle;
+ /*
+ * Count all ACKed packets
+ */
+ chunk = _libssh2_list_first(&handle->packet_list);
+
+ while(chunk) {
+ if(chunk->lefttosend)
+ /* if the chunk still has data left to send, we shouldn't wait
+ for an ACK for it just yet */
+ break;
+
+ else if(acked)
+ /* if we have sent data that is acked, we must return that
+ info before we call a function that might return EAGAIN */
+ break;
+
+ /* we check the packets in order */
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ chunk->request_id, &data, &data_len, 9);
+ if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "FXP write packet too short");
+ }
+ else if(rc < 0) {
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ sftp->write_state = libssh2_NB_state_sent;
+ return rc;
+ }
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ sftp->last_errno = retcode;
+ if(retcode == LIBSSH2_FX_OK) {
+ acked += chunk->len; /* number of payload data that was acked
+ here */
+
+ /* we increase the offset value for all acks */
+ handle->u.file.offset += chunk->len;
+
+ next = _libssh2_list_next(&chunk->node);
+
+ _libssh2_list_remove(&chunk->node); /* remove from list */
+ LIBSSH2_FREE(session, chunk); /* free memory */
+
+ chunk = next;
+ }
+ else {
+ /* flush all pending packets from the outgoing list */
+ sftp_packetlist_flush(handle);
+
+ /* since we return error now, the application will not get any
+ outstanding data acked, so we need to rewind the offset to
+ where the application knows it has reached with acked
+ data */
+ handle->u.file.offset -= handle->u.file.acked;
+
+ /* then reset the offset_sent to be the same as the offset */
+ handle->u.file.offset_sent = handle->u.file.offset;
+
+ /* clear the acked counter since we can have no pending data to
+ ack after an error */
+ handle->u.file.acked = 0;
+
+ /* the server returned an error for that written chunk,
+ propagate this back to our parent function */
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "FXP write failed");
+ }
+ }
+ break;
+ }
+
+ /* if there were acked data in a previous call that wasn't returned then,
+ add that up and try to return it all now. This can happen if the app
+ first sends a huge buffer of data, and then in a second call it sends a
+ smaller one. */
+ acked += handle->u.file.acked;
+
+ if(acked) {
+ ssize_t ret = MIN(acked, org_count);
+ /* we got data acked so return that amount, but no more than what
+ was asked to get sent! */
+
+ /* store the remainder. 'ret' is always equal to or less than 'acked'
+ here */
+ handle->u.file.acked = acked - ret;
+
+ return ret;
+ }
+
+ else
+ return 0; /* nothing was acked, and no EAGAIN was received! */
+}
+
+/* libssh2_sftp_write
+ * Write data to a file handle
+ */
+LIBSSH2_API ssize_t
+libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *hnd, const char *buffer,
+ size_t count)
+{
+ ssize_t rc;
+ if(!hnd)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session,
+ sftp_write(hnd, buffer, count));
+ return rc;
+
+}
+
+static int sftp_fsync(LIBSSH2_SFTP_HANDLE *handle)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ /* 34 = packet_len(4) + packet_type(1) + request_id(4) +
+ string_len(4) + strlen("[email protected]")(17) + handle_len(4) */
+ uint32_t packet_len = handle->handle_len + 34;
+ size_t data_len;
+ unsigned char *packet, *s, *data;
+ ssize_t rc;
+ uint32_t retcode;
+
+ if(sftp->fsync_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Issuing fsync command");
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_EXTENDED "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_EXTENDED;
+ sftp->fsync_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->fsync_request_id);
+ _libssh2_store_str(&s, "[email protected]", 17);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+
+ sftp->fsync_state = libssh2_NB_state_created;
+ }
+ else {
+ packet = sftp->fsync_packet;
+ }
+
+ if(sftp->fsync_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, packet, packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN ||
+ (0 <= rc && rc < (ssize_t)packet_len)) {
+ sftp->fsync_packet = packet;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ LIBSSH2_FREE(session, packet);
+ sftp->fsync_packet = NULL;
+
+ if(rc < 0) {
+ sftp->fsync_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+ sftp->fsync_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ sftp->fsync_request_id, &data, &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP fsync packet too short");
+ }
+ else if(rc) {
+ sftp->fsync_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP EXTENDED REPLY");
+ }
+
+ sftp->fsync_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if(retcode != LIBSSH2_FX_OK) {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "fsync failed");
+ }
+
+ return 0;
+}
+
+/* libssh2_sftp_fsync
+ * Commit data on the handle to disk.
+ */
+LIBSSH2_API int
+libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *hnd)
+{
+ int rc;
+ if(!hnd)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session,
+ sftp_fsync(hnd));
+ return rc;
+}
+
+
+/*
+ * sftp_fstat
+ *
+ * Get or Set stat on a file
+ */
+static int sftp_fstat(LIBSSH2_SFTP_HANDLE *handle,
+ LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
+ uint32_t packet_len =
+ handle->handle_len + 13 + (setstat ? sftp_attrsize(attrs->flags) : 0);
+ unsigned char *s, *data;
+ static const unsigned char fstat_responses[2] =
+ { SSH_FXP_ATTRS, SSH_FXP_STATUS };
+ ssize_t rc;
+
+ if(sftp->fstat_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Issuing %s command",
+ setstat ? "set-stat" : "stat");
+ s = sftp->fstat_packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!sftp->fstat_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "FSTAT/FSETSTAT packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = setstat ? SSH_FXP_FSETSTAT : SSH_FXP_FSTAT;
+ sftp->fstat_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->fstat_request_id);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+
+ if(setstat) {
+ s += sftp_attr2bin(s, attrs);
+ }
+
+ sftp->fstat_state = libssh2_NB_state_created;
+ }
+
+ if(sftp->fstat_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->fstat_packet,
+ packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if((ssize_t)packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->fstat_packet);
+ sftp->fstat_packet = NULL;
+ sftp->fstat_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ (setstat ? "Unable to send FXP_FSETSTAT"
+ : "Unable to send FXP_FSTAT command"));
+ }
+ LIBSSH2_FREE(session, sftp->fstat_packet);
+ sftp->fstat_packet = NULL;
+
+ sftp->fstat_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_requirev(sftp, 2, fstat_responses,
+ sftp->fstat_request_id, &data,
+ &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP fstat packet too short");
+ }
+ else if(rc) {
+ sftp->fstat_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Timeout waiting for status message");
+ }
+
+ sftp->fstat_state = libssh2_NB_state_idle;
+
+ if(data[0] == SSH_FXP_STATUS) {
+ uint32_t retcode;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+ if(retcode == LIBSSH2_FX_OK) {
+ return 0;
+ }
+ else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+ }
+
+ if(sftp_bin2attr(attrs, data + 5, data_len - 5) < 0) {
+ LIBSSH2_FREE(session, data);
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Attributes too short in SFTP fstat");
+ }
+
+ LIBSSH2_FREE(session, data);
+
+ return 0;
+}
+
+/* libssh2_sftp_fstat_ex
+ * Get or Set stat on a file
+ */
+LIBSSH2_API int
+libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *hnd,
+ LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat)
+{
+ int rc;
+ if(!hnd || !attrs)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session,
+ sftp_fstat(hnd, attrs, setstat));
+ return rc;
+}
+
+
+/* libssh2_sftp_seek64
+ * Set the read/write pointer to an arbitrary position within the file
+ */
+LIBSSH2_API void
+libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE *handle, libssh2_uint64_t offset)
+{
+ if(!handle)
+ return;
+ if(handle->u.file.offset == offset && handle->u.file.offset_sent == offset)
+ return;
+
+ handle->u.file.offset = handle->u.file.offset_sent = offset;
+ /* discard all pending requests and currently read data */
+ sftp_packetlist_flush(handle);
+
+ /* free the left received buffered data */
+ if(handle->u.file.data_left) {
+ LIBSSH2_FREE(handle->sftp->channel->session, handle->u.file.data);
+ handle->u.file.data_left = handle->u.file.data_len = 0;
+ handle->u.file.data = NULL;
+ }
+
+ /* reset EOF to False */
+ handle->u.file.eof = FALSE;
+}
+
+/* libssh2_sftp_seek
+ * Set the read/write pointer to an arbitrary position within the file
+ */
+LIBSSH2_API void
+libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset)
+{
+ libssh2_sftp_seek64(handle, (libssh2_uint64_t)offset);
+}
+
+/* libssh2_sftp_tell
+ * Return the current read/write pointer's offset
+ */
+LIBSSH2_API size_t
+libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle)
+{
+ if(!handle)
+ return 0; /* no handle, no size */
+
+ /* NOTE: this may very well truncate the size if it is larger than what
+ size_t can hold, so libssh2_sftp_tell64() is really the function you
+ should use */
+ return (size_t)(handle->u.file.offset);
+}
+
+/* libssh2_sftp_tell64
+ * Return the current read/write pointer's offset
+ */
+LIBSSH2_API libssh2_uint64_t
+libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE *handle)
+{
+ if(!handle)
+ return 0; /* no handle, no size */
+
+ return handle->u.file.offset;
+}
+
+/*
+ * Flush all remaining incoming SFTP packets and zombies.
+ */
+static void sftp_packet_flush(LIBSSH2_SFTP *sftp)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ LIBSSH2_SFTP_PACKET *packet = _libssh2_list_first(&sftp->packets);
+ struct sftp_zombie_requests *zombie =
+ _libssh2_list_first(&sftp->zombie_requests);
+
+ while(packet) {
+ LIBSSH2_SFTP_PACKET *next;
+
+ /* check next struct in the list */
+ next = _libssh2_list_next(&packet->node);
+ _libssh2_list_remove(&packet->node);
+ LIBSSH2_FREE(session, packet->data);
+ LIBSSH2_FREE(session, packet);
+
+ packet = next;
+ }
+
+ while(zombie) {
+ /* figure out the next node */
+ struct sftp_zombie_requests *next = _libssh2_list_next(&zombie->node);
+ /* unlink the current one */
+ _libssh2_list_remove(&zombie->node);
+ /* free the memory */
+ LIBSSH2_FREE(session, zombie);
+ zombie = next;
+ }
+
+}
+
+/* sftp_close_handle
+ *
+ * Close a file or directory handle.
+ * Also frees handle resource and unlinks it from the SFTP structure.
+ * The handle is no longer usable after return of this function, unless
+ * the return value is LIBSSH2_ERROR_EAGAIN in which case this function
+ * should be called again.
+ */
+static int
+sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
+ uint32_t packet_len = handle->handle_len + 13;
+ unsigned char *s, *data = NULL;
+ int rc = 0;
+
+ if(handle->close_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Closing handle");
+ s = handle->close_packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!handle->close_packet) {
+ handle->close_state = libssh2_NB_state_idle;
+ rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_CLOSE "
+ "packet");
+ }
+ else {
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_CLOSE;
+ handle->close_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, handle->close_request_id);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+ handle->close_state = libssh2_NB_state_created;
+ }
+ }
+
+ if(handle->close_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, handle->close_packet,
+ packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if((ssize_t)packet_len != rc) {
+ handle->close_state = libssh2_NB_state_idle;
+ rc = _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send FXP_CLOSE command");
+ }
+ else
+ handle->close_state = libssh2_NB_state_sent;
+
+ LIBSSH2_FREE(session, handle->close_packet);
+ handle->close_packet = NULL;
+ }
+
+ if(handle->close_state == libssh2_NB_state_sent) {
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ handle->close_request_id, &data,
+ &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ data = NULL;
+ _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Packet too short in FXP_CLOSE command");
+ }
+ else if(rc) {
+ _libssh2_error(session, rc,
+ "Error waiting for status message");
+ }
+
+ handle->close_state = libssh2_NB_state_sent1;
+ }
+
+ if(!data) {
+ /* if it reaches this point with data unset, something unwanted
+ happened for which we should have set an error code */
+ assert(rc);
+
+ }
+ else {
+ int retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if(retcode != LIBSSH2_FX_OK) {
+ sftp->last_errno = retcode;
+ handle->close_state = libssh2_NB_state_idle;
+ rc = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+ }
+
+ /* remove this handle from the parent's list */
+ _libssh2_list_remove(&handle->node);
+
+ if(handle->handle_type == LIBSSH2_SFTP_HANDLE_DIR) {
+ if(handle->u.dir.names_left)
+ LIBSSH2_FREE(session, handle->u.dir.names_packet);
+ }
+ else if(handle->handle_type == LIBSSH2_SFTP_HANDLE_FILE) {
+ if(handle->u.file.data)
+ LIBSSH2_FREE(session, handle->u.file.data);
+ }
+
+ sftp_packetlist_flush(handle);
+ sftp->read_state = libssh2_NB_state_idle;
+
+ handle->close_state = libssh2_NB_state_idle;
+
+ LIBSSH2_FREE(session, handle);
+
+ return rc;
+}
+
+/* libssh2_sftp_close_handle
+ *
+ * Close a file or directory handle
+ * Also frees handle resource and unlinks it from the SFTP structure
+ */
+LIBSSH2_API int
+libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *hnd)
+{
+ int rc;
+ if(!hnd)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, hnd->sftp->channel->session, sftp_close_handle(hnd));
+ return rc;
+}
+
+/* sftp_unlink
+ * Delete a file from the remote server
+ */
+static int sftp_unlink(LIBSSH2_SFTP *sftp, const char *filename,
+ size_t filename_len)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ int retcode;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */
+ uint32_t packet_len = filename_len + 13;
+ unsigned char *s, *data;
+ int rc;
+
+ if(sftp->unlink_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Unlinking %s", filename);
+ s = sftp->unlink_packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!sftp->unlink_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_REMOVE "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_REMOVE;
+ sftp->unlink_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->unlink_request_id);
+ _libssh2_store_str(&s, filename, filename_len);
+ sftp->unlink_state = libssh2_NB_state_created;
+ }
+
+ if(sftp->unlink_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->unlink_packet,
+ packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if((ssize_t)packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->unlink_packet);
+ sftp->unlink_packet = NULL;
+ sftp->unlink_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send FXP_REMOVE command");
+ }
+ LIBSSH2_FREE(session, sftp->unlink_packet);
+ sftp->unlink_packet = NULL;
+
+ sftp->unlink_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ sftp->unlink_request_id, &data,
+ &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP unlink packet too short");
+ }
+ else if(rc) {
+ sftp->unlink_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP STATUS");
+ }
+
+ sftp->unlink_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if(retcode == LIBSSH2_FX_OK) {
+ return 0;
+ }
+ else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+}
+
+/* libssh2_sftp_unlink_ex
+ * Delete a file from the remote server
+ */
+LIBSSH2_API int
+libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, const char *filename,
+ unsigned int filename_len)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_unlink(sftp, filename, filename_len));
+ return rc;
+}
+
+/*
+ * sftp_rename
+ *
+ * Rename a file on the remote server
+ */
+static int sftp_rename(LIBSSH2_SFTP *sftp, const char *source_filename,
+ unsigned int source_filename_len,
+ const char *dest_filename,
+ unsigned int dest_filename_len, long flags)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ int retcode;
+ uint32_t packet_len =
+ source_filename_len + dest_filename_len + 17 + (sftp->version >=
+ 5 ? 4 : 0);
+ /* packet_len(4) + packet_type(1) + request_id(4) +
+ source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */
+ unsigned char *data;
+ ssize_t rc;
+
+ if(sftp->version < 2) {
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Server does not support RENAME");
+ }
+
+ if(sftp->rename_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Renaming %s to %s",
+ source_filename, dest_filename);
+ sftp->rename_s = sftp->rename_packet =
+ LIBSSH2_ALLOC(session, packet_len);
+ if(!sftp->rename_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_RENAME "
+ "packet");
+ }
+
+ _libssh2_store_u32(&sftp->rename_s, packet_len - 4);
+ *(sftp->rename_s++) = SSH_FXP_RENAME;
+ sftp->rename_request_id = sftp->request_id++;
+ _libssh2_store_u32(&sftp->rename_s, sftp->rename_request_id);
+ _libssh2_store_str(&sftp->rename_s, source_filename,
+ source_filename_len);
+ _libssh2_store_str(&sftp->rename_s, dest_filename, dest_filename_len);
+
+ if(sftp->version >= 5)
+ _libssh2_store_u32(&sftp->rename_s, flags);
+
+ sftp->rename_state = libssh2_NB_state_created;
+ }
+
+ if(sftp->rename_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->rename_packet,
+ sftp->rename_s - sftp->rename_packet);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if((ssize_t)packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->rename_packet);
+ sftp->rename_packet = NULL;
+ sftp->rename_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send FXP_RENAME command");
+ }
+ LIBSSH2_FREE(session, sftp->rename_packet);
+ sftp->rename_packet = NULL;
+
+ sftp->rename_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ sftp->rename_request_id, &data,
+ &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP rename packet too short");
+ }
+ else if(rc) {
+ sftp->rename_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP STATUS");
+ }
+
+ sftp->rename_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ sftp->last_errno = retcode;
+
+ /* now convert the SFTP error code to libssh2 return code or error
+ message */
+ switch(retcode) {
+ case LIBSSH2_FX_OK:
+ retcode = LIBSSH2_ERROR_NONE;
+ break;
+
+ case LIBSSH2_FX_FILE_ALREADY_EXISTS:
+ retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "File already exists and "
+ "SSH_FXP_RENAME_OVERWRITE not specified");
+ break;
+
+ case LIBSSH2_FX_OP_UNSUPPORTED:
+ retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Operation Not Supported");
+ break;
+
+ default:
+ retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ break;
+ }
+
+ return retcode;
+}
+
+/* libssh2_sftp_rename_ex
+ * Rename a file on the remote server
+ */
+LIBSSH2_API int
+libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, const char *source_filename,
+ unsigned int source_filename_len,
+ const char *dest_filename,
+ unsigned int dest_filename_len, long flags)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_rename(sftp, source_filename, source_filename_len,
+ dest_filename, dest_filename_len, flags));
+ return rc;
+}
+
+/*
+ * sftp_fstatvfs
+ *
+ * Get file system statistics
+ */
+static int sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_STATVFS *st)
+{
+ LIBSSH2_SFTP *sftp = handle->sftp;
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ /* 17 = packet_len(4) + packet_type(1) + request_id(4) + ext_len(4)
+ + handle_len (4) */
+ /* 20 = strlen ("[email protected]") */
+ uint32_t packet_len = handle->handle_len + 20 + 17;
+ unsigned char *packet, *s, *data;
+ ssize_t rc;
+ unsigned int flag;
+ static const unsigned char responses[2] =
+ { SSH_FXP_EXTENDED_REPLY, SSH_FXP_STATUS };
+
+ if(sftp->fstatvfs_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Getting file system statistics");
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_EXTENDED "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_EXTENDED;
+ sftp->fstatvfs_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->fstatvfs_request_id);
+ _libssh2_store_str(&s, "[email protected]", 20);
+ _libssh2_store_str(&s, handle->handle, handle->handle_len);
+
+ sftp->fstatvfs_state = libssh2_NB_state_created;
+ }
+ else {
+ packet = sftp->fstatvfs_packet;
+ }
+
+ if(sftp->fstatvfs_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, packet, packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN ||
+ (0 <= rc && rc < (ssize_t)packet_len)) {
+ sftp->fstatvfs_packet = packet;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ LIBSSH2_FREE(session, packet);
+ sftp->fstatvfs_packet = NULL;
+
+ if(rc < 0) {
+ sftp->fstatvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+ sftp->fstatvfs_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_requirev(sftp, 2, responses, sftp->fstatvfs_request_id,
+ &data, &data_len, 9);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP rename packet too short");
+ }
+ else if(rc) {
+ sftp->fstatvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP EXTENDED REPLY");
+ }
+
+ if(data[0] == SSH_FXP_STATUS) {
+ int retcode = _libssh2_ntohu32(data + 5);
+ sftp->fstatvfs_state = libssh2_NB_state_idle;
+ LIBSSH2_FREE(session, data);
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+
+ if(data_len < 93) {
+ LIBSSH2_FREE(session, data);
+ sftp->fstatvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error: short response");
+ }
+
+ sftp->fstatvfs_state = libssh2_NB_state_idle;
+
+ st->f_bsize = _libssh2_ntohu64(data + 5);
+ st->f_frsize = _libssh2_ntohu64(data + 13);
+ st->f_blocks = _libssh2_ntohu64(data + 21);
+ st->f_bfree = _libssh2_ntohu64(data + 29);
+ st->f_bavail = _libssh2_ntohu64(data + 37);
+ st->f_files = _libssh2_ntohu64(data + 45);
+ st->f_ffree = _libssh2_ntohu64(data + 53);
+ st->f_favail = _libssh2_ntohu64(data + 61);
+ st->f_fsid = _libssh2_ntohu64(data + 69);
+ flag = (unsigned int)_libssh2_ntohu64(data + 77);
+ st->f_namemax = _libssh2_ntohu64(data + 85);
+
+ st->f_flag = (flag & SSH_FXE_STATVFS_ST_RDONLY)
+ ? LIBSSH2_SFTP_ST_RDONLY : 0;
+ st->f_flag |= (flag & SSH_FXE_STATVFS_ST_NOSUID)
+ ? LIBSSH2_SFTP_ST_NOSUID : 0;
+
+ LIBSSH2_FREE(session, data);
+ return 0;
+}
+
+/* libssh2_sftp_fstatvfs
+ * Get filesystem space and inode utilization (requires [email protected]
+ * support on the server)
+ */
+LIBSSH2_API int
+libssh2_sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_STATVFS *st)
+{
+ int rc;
+ if(!handle || !st)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, handle->sftp->channel->session,
+ sftp_fstatvfs(handle, st));
+ return rc;
+}
+
+/*
+ * sftp_statvfs
+ *
+ * Get file system statistics
+ */
+static int sftp_statvfs(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, LIBSSH2_SFTP_STATVFS *st)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ /* 17 = packet_len(4) + packet_type(1) + request_id(4) + ext_len(4)
+ + path_len (4) */
+ /* 19 = strlen ("[email protected]") */
+ uint32_t packet_len = path_len + 19 + 17;
+ unsigned char *packet, *s, *data;
+ ssize_t rc;
+ unsigned int flag;
+ static const unsigned char responses[2] =
+ { SSH_FXP_EXTENDED_REPLY, SSH_FXP_STATUS };
+
+ if(sftp->statvfs_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Getting file system statistics of %s", path);
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_EXTENDED "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_EXTENDED;
+ sftp->statvfs_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->statvfs_request_id);
+ _libssh2_store_str(&s, "[email protected]", 19);
+ _libssh2_store_str(&s, path, path_len);
+
+ sftp->statvfs_state = libssh2_NB_state_created;
+ }
+ else {
+ packet = sftp->statvfs_packet;
+ }
+
+ if(sftp->statvfs_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, packet, packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN ||
+ (0 <= rc && rc < (ssize_t)packet_len)) {
+ sftp->statvfs_packet = packet;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ LIBSSH2_FREE(session, packet);
+ sftp->statvfs_packet = NULL;
+
+ if(rc < 0) {
+ sftp->statvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+ sftp->statvfs_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_requirev(sftp, 2, responses, sftp->statvfs_request_id,
+ &data, &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP fstat packet too short");
+ }
+ else if(rc) {
+ sftp->statvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP EXTENDED REPLY");
+ }
+
+ if(data[0] == SSH_FXP_STATUS) {
+ int retcode = _libssh2_ntohu32(data + 5);
+ sftp->statvfs_state = libssh2_NB_state_idle;
+ LIBSSH2_FREE(session, data);
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+
+ if(data_len < 93) {
+ LIBSSH2_FREE(session, data);
+ sftp->statvfs_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error: short response");
+ }
+
+ sftp->statvfs_state = libssh2_NB_state_idle;
+
+ st->f_bsize = _libssh2_ntohu64(data + 5);
+ st->f_frsize = _libssh2_ntohu64(data + 13);
+ st->f_blocks = _libssh2_ntohu64(data + 21);
+ st->f_bfree = _libssh2_ntohu64(data + 29);
+ st->f_bavail = _libssh2_ntohu64(data + 37);
+ st->f_files = _libssh2_ntohu64(data + 45);
+ st->f_ffree = _libssh2_ntohu64(data + 53);
+ st->f_favail = _libssh2_ntohu64(data + 61);
+ st->f_fsid = _libssh2_ntohu64(data + 69);
+ flag = (unsigned int)_libssh2_ntohu64(data + 77);
+ st->f_namemax = _libssh2_ntohu64(data + 85);
+
+ st->f_flag = (flag & SSH_FXE_STATVFS_ST_RDONLY)
+ ? LIBSSH2_SFTP_ST_RDONLY : 0;
+ st->f_flag |= (flag & SSH_FXE_STATVFS_ST_NOSUID)
+ ? LIBSSH2_SFTP_ST_NOSUID : 0;
+
+ LIBSSH2_FREE(session, data);
+ return 0;
+}
+
+/* libssh2_sftp_statvfs_ex
+ * Get filesystem space and inode utilization (requires [email protected]
+ * support on the server)
+ */
+LIBSSH2_API int
+libssh2_sftp_statvfs(LIBSSH2_SFTP *sftp, const char *path,
+ size_t path_len, LIBSSH2_SFTP_STATVFS *st)
+{
+ int rc;
+ if(!sftp || !st)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session, sftp_statvfs(sftp, path, path_len,
+ st));
+ return rc;
+}
+
+
+/*
+ * sftp_mkdir
+ *
+ * Create an SFTP directory
+ */
+static int sftp_mkdir(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, long mode)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ LIBSSH2_SFTP_ATTRIBUTES attrs = {
+ 0, 0, 0, 0, 0, 0, 0
+ };
+ size_t data_len;
+ int retcode;
+ ssize_t packet_len;
+ unsigned char *packet, *s, *data;
+ int rc;
+
+ if(mode != LIBSSH2_SFTP_DEFAULT_MODE) {
+ /* Filetype in SFTP 3 and earlier */
+ attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
+ attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR;
+ }
+
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
+ packet_len = path_len + 13 + sftp_attrsize(attrs.flags);
+
+ if(sftp->mkdir_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
+ "Creating directory %s with mode 0%lo", path, mode);
+ s = packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_MKDIR "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_MKDIR;
+ sftp->mkdir_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->mkdir_request_id);
+ _libssh2_store_str(&s, path, path_len);
+
+ s += sftp_attr2bin(s, &attrs);
+
+ sftp->mkdir_state = libssh2_NB_state_created;
+ }
+ else {
+ packet = sftp->mkdir_packet;
+ }
+
+ if(sftp->mkdir_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, packet, packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ sftp->mkdir_packet = packet;
+ return rc;
+ }
+ if(packet_len != rc) {
+ LIBSSH2_FREE(session, packet);
+ sftp->mkdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+ LIBSSH2_FREE(session, packet);
+ sftp->mkdir_state = libssh2_NB_state_sent;
+ sftp->mkdir_packet = NULL;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS, sftp->mkdir_request_id,
+ &data, &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP mkdir packet too short");
+ }
+ else if(rc) {
+ sftp->mkdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP STATUS");
+ }
+
+ sftp->mkdir_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if(retcode == LIBSSH2_FX_OK) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "OK!");
+ return 0;
+ }
+ else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+}
+
+/*
+ * libssh2_sftp_mkdir_ex
+ *
+ * Create an SFTP directory
+ */
+LIBSSH2_API int
+libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, long mode)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_mkdir(sftp, path, path_len, mode));
+ return rc;
+}
+
+/* sftp_rmdir
+ * Remove a directory
+ */
+static int sftp_rmdir(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ int retcode;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
+ ssize_t packet_len = path_len + 13;
+ unsigned char *s, *data;
+ int rc;
+
+ if(sftp->rmdir_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Removing directory: %s",
+ path);
+ s = sftp->rmdir_packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!sftp->rmdir_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_RMDIR "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+ *(s++) = SSH_FXP_RMDIR;
+ sftp->rmdir_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->rmdir_request_id);
+ _libssh2_store_str(&s, path, path_len);
+
+ sftp->rmdir_state = libssh2_NB_state_created;
+ }
+
+ if(sftp->rmdir_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->rmdir_packet,
+ packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->rmdir_packet);
+ sftp->rmdir_packet = NULL;
+ sftp->rmdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send FXP_RMDIR command");
+ }
+ LIBSSH2_FREE(session, sftp->rmdir_packet);
+ sftp->rmdir_packet = NULL;
+
+ sftp->rmdir_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ sftp->rmdir_request_id, &data, &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP rmdir packet too short");
+ }
+ else if(rc) {
+ sftp->rmdir_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Error waiting for FXP STATUS");
+ }
+
+ sftp->rmdir_state = libssh2_NB_state_idle;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+
+ if(retcode == LIBSSH2_FX_OK) {
+ return 0;
+ }
+ else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+}
+
+/* libssh2_sftp_rmdir_ex
+ * Remove a directory
+ */
+LIBSSH2_API int
+libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_rmdir(sftp, path, path_len));
+ return rc;
+}
+
+/* sftp_stat
+ * Stat a file or symbolic link
+ */
+static int sftp_stat(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, int stat_type,
+ LIBSSH2_SFTP_ATTRIBUTES * attrs)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
+ ssize_t packet_len =
+ path_len + 13 +
+ ((stat_type ==
+ LIBSSH2_SFTP_SETSTAT) ? sftp_attrsize(attrs->flags) : 0);
+ unsigned char *s, *data;
+ static const unsigned char stat_responses[2] =
+ { SSH_FXP_ATTRS, SSH_FXP_STATUS };
+ int rc;
+
+ if(sftp->stat_state == libssh2_NB_state_idle) {
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%s %s",
+ (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" :
+ (stat_type ==
+ LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path);
+ s = sftp->stat_packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!sftp->stat_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for FXP_*STAT "
+ "packet");
+ }
+
+ _libssh2_store_u32(&s, packet_len - 4);
+
+ switch(stat_type) {
+ case LIBSSH2_SFTP_SETSTAT:
+ *(s++) = SSH_FXP_SETSTAT;
+ break;
+
+ case LIBSSH2_SFTP_LSTAT:
+ *(s++) = SSH_FXP_LSTAT;
+ break;
+
+ case LIBSSH2_SFTP_STAT:
+ default:
+ *(s++) = SSH_FXP_STAT;
+ }
+ sftp->stat_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->stat_request_id);
+ _libssh2_store_str(&s, path, path_len);
+
+ if(stat_type == LIBSSH2_SFTP_SETSTAT)
+ s += sftp_attr2bin(s, attrs);
+
+ sftp->stat_state = libssh2_NB_state_created;
+ }
+
+ if(sftp->stat_state == libssh2_NB_state_created) {
+ rc = _libssh2_channel_write(channel, 0, sftp->stat_packet, packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return rc;
+ }
+ else if(packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->stat_packet);
+ sftp->stat_packet = NULL;
+ sftp->stat_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send STAT/LSTAT/SETSTAT command");
+ }
+ LIBSSH2_FREE(session, sftp->stat_packet);
+ sftp->stat_packet = NULL;
+
+ sftp->stat_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_requirev(sftp, 2, stat_responses,
+ sftp->stat_request_id, &data, &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP stat packet too short");
+ }
+ else if(rc) {
+ sftp->stat_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, rc,
+ "Timeout waiting for status message");
+ }
+
+ sftp->stat_state = libssh2_NB_state_idle;
+
+ if(data[0] == SSH_FXP_STATUS) {
+ int retcode;
+
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+ if(retcode == LIBSSH2_FX_OK) {
+ memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+ return 0;
+ }
+ else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+ }
+
+ memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+ if(sftp_bin2attr(attrs, data + 5, data_len - 5) < 0) {
+ LIBSSH2_FREE(session, data);
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Attributes too short in SFTP fstat");
+ }
+
+ LIBSSH2_FREE(session, data);
+
+ return 0;
+}
+
+/* libssh2_sftp_stat_ex
+ * Stat a file or symbolic link
+ */
+LIBSSH2_API int
+libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, int stat_type,
+ LIBSSH2_SFTP_ATTRIBUTES *attrs)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_stat(sftp, path, path_len, stat_type, attrs));
+ return rc;
+}
+
+/* sftp_symlink
+ * Read or set a symlink
+ */
+static int sftp_symlink(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, char *target,
+ unsigned int target_len, int link_type)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ size_t data_len, link_len;
+ /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
+ ssize_t packet_len =
+ path_len + 13 +
+ ((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0);
+ unsigned char *s, *data;
+ static const unsigned char link_responses[2] =
+ { SSH_FXP_NAME, SSH_FXP_STATUS };
+ int retcode;
+
+ if((sftp->version < 3) && (link_type != LIBSSH2_SFTP_REALPATH)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Server does not support SYMLINK or READLINK");
+ }
+
+ if(sftp->symlink_state == libssh2_NB_state_idle) {
+ s = sftp->symlink_packet = LIBSSH2_ALLOC(session, packet_len);
+ if(!sftp->symlink_packet) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "SYMLINK/READLINK/REALPATH packet");
+ }
+
+ _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%s %s on %s",
+ (link_type ==
+ LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading",
+ (link_type ==
+ LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path);
+
+ _libssh2_store_u32(&s, packet_len - 4);
+
+ switch(link_type) {
+ case LIBSSH2_SFTP_REALPATH:
+ *(s++) = SSH_FXP_REALPATH;
+ break;
+
+ case LIBSSH2_SFTP_SYMLINK:
+ *(s++) = SSH_FXP_SYMLINK;
+ break;
+
+ case LIBSSH2_SFTP_READLINK:
+ default:
+ *(s++) = SSH_FXP_READLINK;
+ }
+ sftp->symlink_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->symlink_request_id);
+ _libssh2_store_str(&s, path, path_len);
+
+ if(link_type == LIBSSH2_SFTP_SYMLINK)
+ _libssh2_store_str(&s, target, target_len);
+
+ sftp->symlink_state = libssh2_NB_state_created;
+ }
+
+ if(sftp->symlink_state == libssh2_NB_state_created) {
+ ssize_t rc = _libssh2_channel_write(channel, 0, sftp->symlink_packet,
+ packet_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ else if(packet_len != rc) {
+ LIBSSH2_FREE(session, sftp->symlink_packet);
+ sftp->symlink_packet = NULL;
+ sftp->symlink_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send SYMLINK/READLINK command");
+ }
+ LIBSSH2_FREE(session, sftp->symlink_packet);
+ sftp->symlink_packet = NULL;
+
+ sftp->symlink_state = libssh2_NB_state_sent;
+ }
+
+ retcode = sftp_packet_requirev(sftp, 2, link_responses,
+ sftp->symlink_request_id, &data,
+ &data_len, 9);
+ if(retcode == LIBSSH2_ERROR_EAGAIN)
+ return retcode;
+ else if(retcode == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP symlink packet too short");
+ }
+ else if(retcode) {
+ sftp->symlink_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, retcode,
+ "Error waiting for status message");
+ }
+
+ sftp->symlink_state = libssh2_NB_state_idle;
+
+ if(data[0] == SSH_FXP_STATUS) {
+ retcode = _libssh2_ntohu32(data + 5);
+ LIBSSH2_FREE(session, data);
+ if(retcode == LIBSSH2_FX_OK)
+ return LIBSSH2_ERROR_NONE;
+ else {
+ sftp->last_errno = retcode;
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP Protocol Error");
+ }
+ }
+
+ if(_libssh2_ntohu32(data + 5) < 1) {
+ LIBSSH2_FREE(session, data);
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "Invalid READLINK/REALPATH response, "
+ "no name entries");
+ }
+
+ if(data_len < 13) {
+ if(data_len > 0) {
+ LIBSSH2_FREE(session, data);
+ }
+ return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
+ "SFTP stat packet too short");
+ }
+
+ /* this reads a u32 and stores it into a signed 32bit value */
+ link_len = _libssh2_ntohu32(data + 9);
+ if(link_len < target_len) {
+ memcpy(target, data + 13, link_len);
+ target[link_len] = 0;
+ retcode = (int)link_len;
+ }
+ else
+ retcode = LIBSSH2_ERROR_BUFFER_TOO_SMALL;
+ LIBSSH2_FREE(session, data);
+
+ return retcode;
+}
+
+/* libssh2_sftp_symlink_ex
+ * Read or set a symlink
+ */
+LIBSSH2_API int
+libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path,
+ unsigned int path_len, char *target,
+ unsigned int target_len, int link_type)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_symlink(sftp, path, path_len, target, target_len,
+ link_type));
+ return rc;
+}
+
+/* libssh2_sftp_last_error
+ * Returns the last error code reported by SFTP
+ */
+LIBSSH2_API unsigned long
+libssh2_sftp_last_error(LIBSSH2_SFTP *sftp)
+{
+ if(!sftp)
+ return 0;
+
+ return sftp->last_errno;
+}
+
+/* libssh2_sftp_get_channel
+ * Return the channel of sftp, then caller can control the channel's behavior.
+ */
+LIBSSH2_API LIBSSH2_CHANNEL *
+libssh2_sftp_get_channel(LIBSSH2_SFTP *sftp)
+{
+ if(!sftp)
+ return NULL;
+
+ return sftp->channel;
+}
diff --git a/contrib/libs/libssh2/src/sftp.h b/contrib/libs/libssh2/src/sftp.h
new file mode 100644
index 00000000000..129b8f085fd
--- /dev/null
+++ b/contrib/libs/libssh2/src/sftp.h
@@ -0,0 +1,238 @@
+#ifndef __LIBSSH2_SFTP_H
+#define __LIBSSH2_SFTP_H
+/*
+ * Copyright (C) 2010 - 2012 by Daniel Stenberg
+ * Author: Daniel Stenberg <[email protected]>
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.
+ *
+ */
+
+/*
+ * MAX_SFTP_OUTGOING_SIZE MUST not be larger than 32500 or so. This is the
+ * amount of data sent in each FXP_WRITE packet
+ */
+#define MAX_SFTP_OUTGOING_SIZE 30000
+
+/* MAX_SFTP_READ_SIZE is how much data is asked for at max in each FXP_READ
+ * packets.
+ */
+#define MAX_SFTP_READ_SIZE 30000
+
+struct sftp_pipeline_chunk {
+ struct list_node node;
+ libssh2_uint64_t offset; /* READ: offset at which to start reading
+ WRITE: not used */
+ size_t len; /* WRITE: size of the data to write
+ READ: how many bytes that was asked for */
+ size_t sent;
+ ssize_t lefttosend; /* if 0, the entire packet has been sent off */
+ uint32_t request_id;
+ unsigned char packet[1]; /* data */
+};
+
+struct sftp_zombie_requests {
+ struct list_node node;
+ uint32_t request_id;
+};
+
+#ifndef MIN
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+struct _LIBSSH2_SFTP_PACKET
+{
+ struct list_node node; /* linked list header */
+ uint32_t request_id;
+ unsigned char *data;
+ size_t data_len; /* payload size */
+};
+
+typedef struct _LIBSSH2_SFTP_PACKET LIBSSH2_SFTP_PACKET;
+
+#define SFTP_HANDLE_MAXLEN 256 /* according to spec! */
+
+struct _LIBSSH2_SFTP_HANDLE
+{
+ struct list_node node;
+
+ LIBSSH2_SFTP *sftp;
+
+ char handle[SFTP_HANDLE_MAXLEN];
+ size_t handle_len;
+
+ enum {
+ LIBSSH2_SFTP_HANDLE_FILE,
+ LIBSSH2_SFTP_HANDLE_DIR
+ } handle_type;
+
+ union _libssh2_sftp_handle_data
+ {
+ struct _libssh2_sftp_handle_file_data
+ {
+ libssh2_uint64_t offset;
+ libssh2_uint64_t offset_sent;
+ size_t acked; /* container for acked data that hasn't been
+ returned to caller yet, used for sftp_write */
+
+ /* 'data' is used by sftp_read() and is allocated data that has
+ been received already from the server but wasn't returned to
+ the caller yet. It is of size 'data_len' and 'data_left is the
+ number of bytes not yet returned, counted from the end of the
+ buffer. */
+ unsigned char *data;
+ size_t data_len;
+ size_t data_left;
+
+ char eof; /* we have read to the end */
+ } file;
+ struct _libssh2_sftp_handle_dir_data
+ {
+ uint32_t names_left;
+ void *names_packet;
+ char *next_name;
+ size_t names_packet_len;
+ } dir;
+ } u;
+
+ /* State variables used in libssh2_sftp_close_handle() */
+ libssh2_nonblocking_states close_state;
+ uint32_t close_request_id;
+ unsigned char *close_packet;
+
+ /* list of outstanding packets sent to server */
+ struct list_head packet_list;
+
+};
+
+struct _LIBSSH2_SFTP
+{
+ LIBSSH2_CHANNEL *channel;
+
+ uint32_t request_id, version;
+
+ struct list_head packets;
+
+ /* List of FXP_READ responses to ignore because EOF already received. */
+ struct list_head zombie_requests;
+
+ /* a list of _LIBSSH2_SFTP_HANDLE structs */
+ struct list_head sftp_handles;
+
+ uint32_t last_errno;
+
+ /* Holder for partial packet, use in libssh2_sftp_packet_read() */
+ unsigned char partial_size[4]; /* buffer for size field */
+ size_t partial_size_len; /* size field length */
+ unsigned char *partial_packet; /* The data */
+ uint32_t partial_len; /* Desired number of bytes */
+ size_t partial_received; /* Bytes received so far */
+
+ /* Time that libssh2_sftp_packet_requirev() started reading */
+ time_t requirev_start;
+
+ /* State variables used in libssh2_sftp_open_ex() */
+ libssh2_nonblocking_states open_state;
+ unsigned char *open_packet;
+ uint32_t open_packet_len; /* 32 bit on the wire */
+ size_t open_packet_sent;
+ uint32_t open_request_id;
+
+ /* State variable used in sftp_read() */
+ libssh2_nonblocking_states read_state;
+
+ /* State variable used in sftp_packet_read() */
+ libssh2_nonblocking_states packet_state;
+
+ /* State variable used in sftp_write() */
+ libssh2_nonblocking_states write_state;
+
+ /* State variables used in sftp_fsync() */
+ libssh2_nonblocking_states fsync_state;
+ unsigned char *fsync_packet;
+ uint32_t fsync_request_id;
+
+ /* State variables used in libssh2_sftp_readdir() */
+ libssh2_nonblocking_states readdir_state;
+ unsigned char *readdir_packet;
+ uint32_t readdir_request_id;
+
+ /* State variables used in libssh2_sftp_fstat_ex() */
+ libssh2_nonblocking_states fstat_state;
+ unsigned char *fstat_packet;
+ uint32_t fstat_request_id;
+
+ /* State variables used in libssh2_sftp_unlink_ex() */
+ libssh2_nonblocking_states unlink_state;
+ unsigned char *unlink_packet;
+ uint32_t unlink_request_id;
+
+ /* State variables used in libssh2_sftp_rename_ex() */
+ libssh2_nonblocking_states rename_state;
+ unsigned char *rename_packet;
+ unsigned char *rename_s;
+ uint32_t rename_request_id;
+
+ /* State variables used in libssh2_sftp_fstatvfs() */
+ libssh2_nonblocking_states fstatvfs_state;
+ unsigned char *fstatvfs_packet;
+ uint32_t fstatvfs_request_id;
+
+ /* State variables used in libssh2_sftp_statvfs() */
+ libssh2_nonblocking_states statvfs_state;
+ unsigned char *statvfs_packet;
+ uint32_t statvfs_request_id;
+
+ /* State variables used in libssh2_sftp_mkdir() */
+ libssh2_nonblocking_states mkdir_state;
+ unsigned char *mkdir_packet;
+ uint32_t mkdir_request_id;
+
+ /* State variables used in libssh2_sftp_rmdir() */
+ libssh2_nonblocking_states rmdir_state;
+ unsigned char *rmdir_packet;
+ uint32_t rmdir_request_id;
+
+ /* State variables used in libssh2_sftp_stat() */
+ libssh2_nonblocking_states stat_state;
+ unsigned char *stat_packet;
+ uint32_t stat_request_id;
+
+ /* State variables used in libssh2_sftp_symlink() */
+ libssh2_nonblocking_states symlink_state;
+ unsigned char *symlink_packet;
+ uint32_t symlink_request_id;
+};
+
+#endif /* __LIBSSH2_SFTP_H */
diff --git a/contrib/libs/libssh2/src/transport.c b/contrib/libs/libssh2/src/transport.c
new file mode 100644
index 00000000000..17af3e4da11
--- /dev/null
+++ b/contrib/libs/libssh2/src/transport.c
@@ -0,0 +1,924 @@
+/* Copyright (C) 2007 The Written Word, Inc. All rights reserved.
+ * Copyright (C) 2009-2010 by Daniel Stenberg
+ * Author: Daniel Stenberg <[email protected]>
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.
+ *
+ * This file handles reading and writing to the SECSH transport layer. RFC4253.
+ */
+
+#include "libssh2_priv.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#ifdef LIBSSH2DEBUG
+#include <stdio.h>
+#endif
+
+#include <assert.h>
+
+#include "transport.h"
+#include "mac.h"
+
+#define MAX_BLOCKSIZE 32 /* MUST fit biggest crypto block size we use/get */
+#define MAX_MACSIZE 64 /* MUST fit biggest MAC length we support */
+
+#ifdef LIBSSH2DEBUG
+#define UNPRINTABLE_CHAR '.'
+static void
+debugdump(LIBSSH2_SESSION * session,
+ const char *desc, const unsigned char *ptr, size_t size)
+{
+ size_t i;
+ size_t c;
+ unsigned int width = 0x10;
+ char buffer[256]; /* Must be enough for width*4 + about 30 or so */
+ size_t used;
+ static const char *hex_chars = "0123456789ABCDEF";
+
+ if(!(session->showmask & LIBSSH2_TRACE_TRANS)) {
+ /* not asked for, bail out */
+ return;
+ }
+
+ used = snprintf(buffer, sizeof(buffer), "=> %s (%d bytes)\n",
+ desc, (int) size);
+ if(session->tracehandler)
+ (session->tracehandler)(session, session->tracehandler_context,
+ buffer, used);
+ else
+ fprintf(stderr, "%s", buffer);
+
+ for(i = 0; i < size; i += width) {
+
+ used = snprintf(buffer, sizeof(buffer), "%04lx: ", (long)i);
+
+ /* hex not disabled, show it */
+ for(c = 0; c < width; c++) {
+ if(i + c < size) {
+ buffer[used++] = hex_chars[(ptr[i + c] >> 4) & 0xF];
+ buffer[used++] = hex_chars[ptr[i + c] & 0xF];
+ }
+ else {
+ buffer[used++] = ' ';
+ buffer[used++] = ' ';
+ }
+
+ buffer[used++] = ' ';
+ if((width/2) - 1 == c)
+ buffer[used++] = ' ';
+ }
+
+ buffer[used++] = ':';
+ buffer[used++] = ' ';
+
+ for(c = 0; (c < width) && (i + c < size); c++) {
+ buffer[used++] = isprint(ptr[i + c]) ?
+ ptr[i + c] : UNPRINTABLE_CHAR;
+ }
+ buffer[used++] = '\n';
+ buffer[used] = 0;
+
+ if(session->tracehandler)
+ (session->tracehandler)(session, session->tracehandler_context,
+ buffer, used);
+ else
+ fprintf(stderr, "%s", buffer);
+ }
+}
+#else
+#define debugdump(a,x,y,z)
+#endif
+
+
+/* decrypt() decrypts 'len' bytes from 'source' to 'dest'.
+ *
+ * returns 0 on success and negative on failure
+ */
+
+static int
+decrypt(LIBSSH2_SESSION * session, unsigned char *source,
+ unsigned char *dest, int len)
+{
+ struct transportpacket *p = &session->packet;
+ int blocksize = session->remote.crypt->blocksize;
+
+ /* if we get called with a len that isn't an even number of blocksizes
+ we risk losing those extra bytes */
+ assert((len % blocksize) == 0);
+
+ while(len >= blocksize) {
+ if(session->remote.crypt->crypt(session, source, blocksize,
+ &session->remote.crypt_abstract)) {
+ LIBSSH2_FREE(session, p->payload);
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+
+ /* if the crypt() function would write to a given address it
+ wouldn't have to memcpy() and we could avoid this memcpy()
+ too */
+ memcpy(dest, source, blocksize);
+
+ len -= blocksize; /* less bytes left */
+ dest += blocksize; /* advance write pointer */
+ source += blocksize; /* advance read pointer */
+ }
+ return LIBSSH2_ERROR_NONE; /* all is fine */
+}
+
+/*
+ * fullpacket() gets called when a full packet has been received and properly
+ * collected.
+ */
+static int
+fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
+{
+ unsigned char macbuf[MAX_MACSIZE];
+ struct transportpacket *p = &session->packet;
+ int rc;
+ int compressed;
+
+ if(session->fullpacket_state == libssh2_NB_state_idle) {
+ session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED;
+ session->fullpacket_payload_len = p->packet_length - 1;
+
+ if(encrypted) {
+
+ /* Calculate MAC hash */
+ session->remote.mac->hash(session, macbuf, /* store hash here */
+ session->remote.seqno,
+ p->init, 5,
+ p->payload,
+ session->fullpacket_payload_len,
+ &session->remote.mac_abstract);
+
+ /* Compare the calculated hash with the MAC we just read from
+ * the network. The read one is at the very end of the payload
+ * buffer. Note that 'payload_len' here is the packet_length
+ * field which includes the padding but not the MAC.
+ */
+ if(memcmp(macbuf, p->payload + session->fullpacket_payload_len,
+ session->remote.mac->mac_len)) {
+ session->fullpacket_macstate = LIBSSH2_MAC_INVALID;
+ }
+ }
+
+ session->remote.seqno++;
+
+ /* ignore the padding */
+ session->fullpacket_payload_len -= p->padding_length;
+
+ /* Check for and deal with decompression */
+ compressed =
+ session->local.comp != NULL &&
+ session->local.comp->compress &&
+ ((session->state & LIBSSH2_STATE_AUTHENTICATED) ||
+ session->local.comp->use_in_auth);
+
+ if(compressed && session->remote.comp_abstract) {
+ /*
+ * The buffer for the decompression (remote.comp_abstract) is
+ * initialised in time when it is needed so as long it is NULL we
+ * cannot decompress.
+ */
+
+ unsigned char *data;
+ size_t data_len;
+ rc = session->remote.comp->decomp(session,
+ &data, &data_len,
+ LIBSSH2_PACKET_MAXDECOMP,
+ p->payload,
+ session->fullpacket_payload_len,
+ &session->remote.comp_abstract);
+ LIBSSH2_FREE(session, p->payload);
+ if(rc)
+ return rc;
+
+ p->payload = data;
+ session->fullpacket_payload_len = data_len;
+ }
+
+ session->fullpacket_packet_type = p->payload[0];
+
+ debugdump(session, "libssh2_transport_read() plain",
+ p->payload, session->fullpacket_payload_len);
+
+ session->fullpacket_state = libssh2_NB_state_created;
+ }
+
+ if(session->fullpacket_state == libssh2_NB_state_created) {
+ rc = _libssh2_packet_add(session, p->payload,
+ session->fullpacket_payload_len,
+ session->fullpacket_macstate);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return rc;
+ if(rc) {
+ session->fullpacket_state = libssh2_NB_state_idle;
+ return rc;
+ }
+ }
+
+ session->fullpacket_state = libssh2_NB_state_idle;
+
+ return session->fullpacket_packet_type;
+}
+
+
+/*
+ * _libssh2_transport_read
+ *
+ * Collect a packet into the input queue.
+ *
+ * Returns packet type added to input queue (0 if nothing added), or a
+ * negative error number.
+ */
+
+/*
+ * This function reads the binary stream as specified in chapter 6 of RFC4253
+ * "The Secure Shell (SSH) Transport Layer Protocol"
+ *
+ * DOES NOT call _libssh2_error() for ANY error case.
+ */
+int _libssh2_transport_read(LIBSSH2_SESSION * session)
+{
+ int rc;
+ struct transportpacket *p = &session->packet;
+ int remainbuf;
+ int remainpack;
+ int numbytes;
+ int numdecrypt;
+ unsigned char block[MAX_BLOCKSIZE];
+ int blocksize;
+ int encrypted = 1;
+
+ /* default clear the bit */
+ session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_INBOUND;
+
+ /*
+ * All channels, systems, subsystems, etc eventually make it down here
+ * when looking for more incoming data. If a key exchange is going on
+ * (LIBSSH2_STATE_EXCHANGING_KEYS bit is set) then the remote end will
+ * ONLY send key exchange related traffic. In non-blocking mode, there is
+ * a chance to break out of the kex_exchange function with an EAGAIN
+ * status, and never come back to it. If LIBSSH2_STATE_EXCHANGING_KEYS is
+ * active, then we must redirect to the key exchange. However, if
+ * kex_exchange is active (as in it is the one that calls this execution
+ * of packet_read, then don't redirect, as that would be an infinite loop!
+ */
+
+ if(session->state & LIBSSH2_STATE_EXCHANGING_KEYS &&
+ !(session->state & LIBSSH2_STATE_KEX_ACTIVE)) {
+
+ /* Whoever wants a packet won't get anything until the key re-exchange
+ * is done!
+ */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the"
+ " key re-exchange from _libssh2_transport_read");
+ rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state);
+ if(rc)
+ return rc;
+ }
+
+ /*
+ * =============================== NOTE ===============================
+ * I know this is very ugly and not a really good use of "goto", but
+ * this case statement would be even uglier to do it any other way
+ */
+ if(session->readPack_state == libssh2_NB_state_jump1) {
+ session->readPack_state = libssh2_NB_state_idle;
+ encrypted = session->readPack_encrypted;
+ goto libssh2_transport_read_point1;
+ }
+
+ do {
+ if(session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
+ return LIBSSH2_ERROR_SOCKET_DISCONNECT;
+ }
+
+ if(session->state & LIBSSH2_STATE_NEWKEYS) {
+ blocksize = session->remote.crypt->blocksize;
+ }
+ else {
+ encrypted = 0; /* not encrypted */
+ blocksize = 5; /* not strictly true, but we can use 5 here to
+ make the checks below work fine still */
+ }
+
+ /* read/use a whole big chunk into a temporary area stored in
+ the LIBSSH2_SESSION struct. We will decrypt data from that
+ buffer into the packet buffer so this temp one doesn't have
+ to be able to keep a whole SSH packet, just be large enough
+ so that we can read big chunks from the network layer. */
+
+ /* how much data there is remaining in the buffer to deal with
+ before we should read more from the network */
+ remainbuf = p->writeidx - p->readidx;
+
+ /* if remainbuf turns negative we have a bad internal error */
+ assert(remainbuf >= 0);
+
+ if(remainbuf < blocksize) {
+ /* If we have less than a blocksize left, it is too
+ little data to deal with, read more */
+ ssize_t nread;
+
+ /* move any remainder to the start of the buffer so
+ that we can do a full refill */
+ if(remainbuf) {
+ memmove(p->buf, &p->buf[p->readidx], remainbuf);
+ p->readidx = 0;
+ p->writeidx = remainbuf;
+ }
+ else {
+ /* nothing to move, just zero the indexes */
+ p->readidx = p->writeidx = 0;
+ }
+
+ /* now read a big chunk from the network into the temp buffer */
+ nread =
+ LIBSSH2_RECV(session, &p->buf[remainbuf],
+ PACKETBUFSIZE - remainbuf,
+ LIBSSH2_SOCKET_RECV_FLAGS(session));
+ if(nread <= 0) {
+ /* check if this is due to EAGAIN and return the special
+ return code if so, error out normally otherwise */
+ if((nread < 0) && (nread == -EAGAIN)) {
+ session->socket_block_directions |=
+ LIBSSH2_SESSION_BLOCK_INBOUND;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error recving %d bytes (got %d)",
+ PACKETBUFSIZE - remainbuf, -nread);
+ return LIBSSH2_ERROR_SOCKET_RECV;
+ }
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Recved %d/%d bytes to %p+%d", nread,
+ PACKETBUFSIZE - remainbuf, p->buf, remainbuf);
+
+ debugdump(session, "libssh2_transport_read() raw",
+ &p->buf[remainbuf], nread);
+ /* advance write pointer */
+ p->writeidx += nread;
+
+ /* update remainbuf counter */
+ remainbuf = p->writeidx - p->readidx;
+ }
+
+ /* how much data to deal with from the buffer */
+ numbytes = remainbuf;
+
+ if(!p->total_num) {
+ size_t total_num;
+
+ /* No payload package area allocated yet. To know the
+ size of this payload, we need to decrypt the first
+ blocksize data. */
+
+ if(numbytes < blocksize) {
+ /* we can't act on anything less than blocksize, but this
+ check is only done for the initial block since once we have
+ got the start of a block we can in fact deal with fractions
+ */
+ session->socket_block_directions |=
+ LIBSSH2_SESSION_BLOCK_INBOUND;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ if(encrypted) {
+ rc = decrypt(session, &p->buf[p->readidx], block, blocksize);
+ if(rc != LIBSSH2_ERROR_NONE) {
+ return rc;
+ }
+ /* save the first 5 bytes of the decrypted package, to be
+ used in the hash calculation later down. */
+ memcpy(p->init, block, 5);
+ }
+ else {
+ /* the data is plain, just copy it verbatim to
+ the working block buffer */
+ memcpy(block, &p->buf[p->readidx], blocksize);
+ }
+
+ /* advance the read pointer */
+ p->readidx += blocksize;
+
+ /* we now have the initial blocksize bytes decrypted,
+ * and we can extract packet and padding length from it
+ */
+ p->packet_length = _libssh2_ntohu32(block);
+ if(p->packet_length < 1) {
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+ else if(p->packet_length > LIBSSH2_PACKET_MAXPAYLOAD) {
+ return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+ }
+
+ p->padding_length = block[4];
+ if(p->padding_length > p->packet_length - 1) {
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+
+
+ /* total_num is the number of bytes following the initial
+ (5 bytes) packet length and padding length fields */
+ total_num =
+ p->packet_length - 1 +
+ (encrypted ? session->remote.mac->mac_len : 0);
+
+ /* RFC4253 section 6.1 Maximum Packet Length says:
+ *
+ * "All implementations MUST be able to process
+ * packets with uncompressed payload length of 32768
+ * bytes or less and total packet size of 35000 bytes
+ * or less (including length, padding length, payload,
+ * padding, and MAC.)."
+ */
+ if(total_num > LIBSSH2_PACKET_MAXPAYLOAD || total_num == 0) {
+ return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+ }
+
+ /* Get a packet handle put data into. We get one to
+ hold all data, including padding and MAC. */
+ p->payload = LIBSSH2_ALLOC(session, total_num);
+ if(!p->payload) {
+ return LIBSSH2_ERROR_ALLOC;
+ }
+ p->total_num = total_num;
+ /* init write pointer to start of payload buffer */
+ p->wptr = p->payload;
+
+ if(blocksize > 5) {
+ /* copy the data from index 5 to the end of
+ the blocksize from the temporary buffer to
+ the start of the decrypted buffer */
+ if(blocksize - 5 <= (int) total_num) {
+ memcpy(p->wptr, &block[5], blocksize - 5);
+ p->wptr += blocksize - 5; /* advance write pointer */
+ }
+ else {
+ if(p->payload)
+ LIBSSH2_FREE(session, p->payload);
+ return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+ }
+ }
+
+ /* init the data_num field to the number of bytes of
+ the package read so far */
+ p->data_num = p->wptr - p->payload;
+
+ /* we already dealt with a blocksize worth of data */
+ numbytes -= blocksize;
+ }
+
+ /* how much there is left to add to the current payload
+ package */
+ remainpack = p->total_num - p->data_num;
+
+ if(numbytes > remainpack) {
+ /* if we have more data in the buffer than what is going into this
+ particular packet, we limit this round to this packet only */
+ numbytes = remainpack;
+ }
+
+ if(encrypted) {
+ /* At the end of the incoming stream, there is a MAC,
+ and we don't want to decrypt that since we need it
+ "raw". We MUST however decrypt the padding data
+ since it is used for the hash later on. */
+ int skip = session->remote.mac->mac_len;
+
+ /* if what we have plus numbytes is bigger than the
+ total minus the skip margin, we should lower the
+ amount to decrypt even more */
+ if((p->data_num + numbytes) > (p->total_num - skip)) {
+ numdecrypt = (p->total_num - skip) - p->data_num;
+ }
+ else {
+ int frac;
+ numdecrypt = numbytes;
+ frac = numdecrypt % blocksize;
+ if(frac) {
+ /* not an aligned amount of blocks,
+ align it */
+ numdecrypt -= frac;
+ /* and make it no unencrypted data
+ after it */
+ numbytes = 0;
+ }
+ }
+ }
+ else {
+ /* unencrypted data should not be decrypted at all */
+ numdecrypt = 0;
+ }
+
+ /* if there are bytes to decrypt, do that */
+ if(numdecrypt > 0) {
+ /* now decrypt the lot */
+ rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt);
+ if(rc != LIBSSH2_ERROR_NONE) {
+ p->total_num = 0; /* no packet buffer available */
+ return rc;
+ }
+
+ /* advance the read pointer */
+ p->readidx += numdecrypt;
+ /* advance write pointer */
+ p->wptr += numdecrypt;
+ /* increase data_num */
+ p->data_num += numdecrypt;
+
+ /* bytes left to take care of without decryption */
+ numbytes -= numdecrypt;
+ }
+
+ /* if there are bytes to copy that aren't decrypted, simply
+ copy them as-is to the target buffer */
+ if(numbytes > 0) {
+
+ if(numbytes <= (int)(p->total_num - (p->wptr - p->payload))) {
+ memcpy(p->wptr, &p->buf[p->readidx], numbytes);
+ }
+ else {
+ if(p->payload)
+ LIBSSH2_FREE(session, p->payload);
+ return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
+ }
+
+ /* advance the read pointer */
+ p->readidx += numbytes;
+ /* advance write pointer */
+ p->wptr += numbytes;
+ /* increase data_num */
+ p->data_num += numbytes;
+ }
+
+ /* now check how much data there's left to read to finish the
+ current packet */
+ remainpack = p->total_num - p->data_num;
+
+ if(!remainpack) {
+ /* we have a full packet */
+ libssh2_transport_read_point1:
+ rc = fullpacket(session, encrypted);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+
+ if(session->packAdd_state != libssh2_NB_state_idle) {
+ /* fullpacket only returns LIBSSH2_ERROR_EAGAIN if
+ * libssh2_packet_add returns LIBSSH2_ERROR_EAGAIN. If
+ * that returns LIBSSH2_ERROR_EAGAIN but the packAdd_state
+ * is idle, then the packet has been added to the brigade,
+ * but some immediate action that was taken based on the
+ * packet type (such as key re-exchange) is not yet
+ * complete. Clear the way for a new packet to be read
+ * in.
+ */
+ session->readPack_encrypted = encrypted;
+ session->readPack_state = libssh2_NB_state_jump1;
+ }
+
+ return rc;
+ }
+
+ p->total_num = 0; /* no packet buffer available */
+
+ return rc;
+ }
+ } while(1); /* loop */
+
+ return LIBSSH2_ERROR_SOCKET_RECV; /* we never reach this point */
+}
+
+static int
+send_existing(LIBSSH2_SESSION *session, const unsigned char *data,
+ size_t data_len, ssize_t *ret)
+{
+ ssize_t rc;
+ ssize_t length;
+ struct transportpacket *p = &session->packet;
+
+ if(!p->olen) {
+ *ret = 0;
+ return LIBSSH2_ERROR_NONE;
+ }
+
+ /* send as much as possible of the existing packet */
+ if((data != p->odata) || (data_len != p->olen)) {
+ /* When we are about to complete the sending of a packet, it is vital
+ that the caller doesn't try to send a new/different packet since
+ we don't add this one up until the previous one has been sent. To
+ make the caller really notice his/hers flaw, we return error for
+ this case */
+ return LIBSSH2_ERROR_BAD_USE;
+ }
+
+ *ret = 1; /* set to make our parent return */
+
+ /* number of bytes left to send */
+ length = p->ototal_num - p->osent;
+
+ rc = LIBSSH2_SEND(session, &p->outbuf[p->osent], length,
+ LIBSSH2_SOCKET_SEND_FLAGS(session));
+ if(rc < 0)
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error sending %d bytes: %d", length, -rc);
+ else {
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Sent %d/%d bytes at %p+%d", rc, length, p->outbuf,
+ p->osent);
+ debugdump(session, "libssh2_transport_write send()",
+ &p->outbuf[p->osent], rc);
+ }
+
+ if(rc == length) {
+ /* the remainder of the package was sent */
+ p->ototal_num = 0;
+ p->olen = 0;
+ /* we leave *ret set so that the parent returns as we MUST return back
+ a send success now, so that we don't risk sending EAGAIN later
+ which then would confuse the parent function */
+ return LIBSSH2_ERROR_NONE;
+
+ }
+ else if(rc < 0) {
+ /* nothing was sent */
+ if(rc != -EAGAIN)
+ /* send failure! */
+ return LIBSSH2_ERROR_SOCKET_SEND;
+
+ session->socket_block_directions |= LIBSSH2_SESSION_BLOCK_OUTBOUND;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ p->osent += rc; /* we sent away this much data */
+
+ return rc < length ? LIBSSH2_ERROR_EAGAIN : LIBSSH2_ERROR_NONE;
+}
+
+/*
+ * libssh2_transport_send
+ *
+ * Send a packet, encrypting it and adding a MAC code if necessary
+ * Returns 0 on success, non-zero on failure.
+ *
+ * The data is provided as _two_ data areas that are combined by this
+ * function. The 'data' part is sent immediately before 'data2'. 'data2' may
+ * be set to NULL to only use a single part.
+ *
+ * Returns LIBSSH2_ERROR_EAGAIN if it would block or if the whole packet was
+ * not sent yet. If it does so, the caller should call this function again as
+ * soon as it is likely that more data can be sent, and this function MUST
+ * then be called with the same argument set (same data pointer and same
+ * data_len) until ERROR_NONE or failure is returned.
+ *
+ * This function DOES NOT call _libssh2_error() on any errors.
+ */
+int _libssh2_transport_send(LIBSSH2_SESSION *session,
+ const unsigned char *data, size_t data_len,
+ const unsigned char *data2, size_t data2_len)
+{
+ int blocksize =
+ (session->state & LIBSSH2_STATE_NEWKEYS) ?
+ session->local.crypt->blocksize : 8;
+ int padding_length;
+ size_t packet_length;
+ int total_length;
+#ifdef RANDOM_PADDING
+ int rand_max;
+ int seed = data[0]; /* FIXME: make this random */
+#endif
+ struct transportpacket *p = &session->packet;
+ int encrypted;
+ int compressed;
+ ssize_t ret;
+ int rc;
+ const unsigned char *orgdata = data;
+ size_t orgdata_len = data_len;
+
+ /*
+ * If the last read operation was interrupted in the middle of a key
+ * exchange, we must complete that key exchange before continuing to write
+ * further data.
+ *
+ * See the similar block in _libssh2_transport_read for more details.
+ */
+ if(session->state & LIBSSH2_STATE_EXCHANGING_KEYS &&
+ !(session->state & LIBSSH2_STATE_KEX_ACTIVE)) {
+ /* Don't write any new packets if we're still in the middle of a key
+ * exchange. */
+ _libssh2_debug(session, LIBSSH2_TRACE_TRANS, "Redirecting into the"
+ " key re-exchange from _libssh2_transport_send");
+ rc = _libssh2_kex_exchange(session, 1, &session->startup_key_state);
+ if(rc)
+ return rc;
+ }
+
+ debugdump(session, "libssh2_transport_write plain", data, data_len);
+ if(data2)
+ debugdump(session, "libssh2_transport_write plain2", data2, data2_len);
+
+ /* FIRST, check if we have a pending write to complete. send_existing
+ only sanity-check data and data_len and not data2 and data2_len!! */
+ rc = send_existing(session, data, data_len, &ret);
+ if(rc)
+ return rc;
+
+ session->socket_block_directions &= ~LIBSSH2_SESSION_BLOCK_OUTBOUND;
+
+ if(ret)
+ /* set by send_existing if data was sent */
+ return rc;
+
+ encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0;
+
+ compressed =
+ session->local.comp != NULL &&
+ session->local.comp->compress &&
+ ((session->state & LIBSSH2_STATE_AUTHENTICATED) ||
+ session->local.comp->use_in_auth);
+
+ if(encrypted && compressed && session->local.comp_abstract) {
+ /* the idea here is that these function must fail if the output gets
+ larger than what fits in the assigned buffer so thus they don't
+ check the input size as we don't know how much it compresses */
+ size_t dest_len = MAX_SSH_PACKET_LEN-5-256;
+ size_t dest2_len = dest_len;
+
+ /* compress directly to the target buffer */
+ rc = session->local.comp->comp(session,
+ &p->outbuf[5], &dest_len,
+ data, data_len,
+ &session->local.comp_abstract);
+ if(rc)
+ return rc; /* compression failure */
+
+ if(data2 && data2_len) {
+ /* compress directly to the target buffer right after where the
+ previous call put data */
+ dest2_len -= dest_len;
+
+ rc = session->local.comp->comp(session,
+ &p->outbuf[5 + dest_len],
+ &dest2_len,
+ data2, data2_len,
+ &session->local.comp_abstract);
+ }
+ else
+ dest2_len = 0;
+ if(rc)
+ return rc; /* compression failure */
+
+ data_len = dest_len + dest2_len; /* use the combined length */
+ }
+ else {
+ if((data_len + data2_len) >= (MAX_SSH_PACKET_LEN-0x100))
+ /* too large packet, return error for this until we make this
+ function split it up and send multiple SSH packets */
+ return LIBSSH2_ERROR_INVAL;
+
+ /* copy the payload data */
+ memcpy(&p->outbuf[5], data, data_len);
+ if(data2 && data2_len)
+ memcpy(&p->outbuf[5 + data_len], data2, data2_len);
+ data_len += data2_len; /* use the combined length */
+ }
+
+
+ /* RFC4253 says: Note that the length of the concatenation of
+ 'packet_length', 'padding_length', 'payload', and 'random padding'
+ MUST be a multiple of the cipher block size or 8, whichever is
+ larger. */
+
+ /* Plain math: (4 + 1 + packet_length + padding_length) % blocksize == 0 */
+
+ packet_length = data_len + 1 + 4; /* 1 is for padding_length field
+ 4 for the packet_length field */
+
+ /* at this point we have it all except the padding */
+
+ /* first figure out our minimum padding amount to make it an even
+ block size */
+ padding_length = blocksize - (packet_length % blocksize);
+
+ /* if the padding becomes too small we add another blocksize worth
+ of it (taken from the original libssh2 where it didn't have any
+ real explanation) */
+ if(padding_length < 4) {
+ padding_length += blocksize;
+ }
+#ifdef RANDOM_PADDING
+ /* FIXME: we can add padding here, but that also makes the packets
+ bigger etc */
+
+ /* now we can add 'blocksize' to the padding_length N number of times
+ (to "help thwart traffic analysis") but it must be less than 255 in
+ total */
+ rand_max = (255 - padding_length) / blocksize + 1;
+ padding_length += blocksize * (seed % rand_max);
+#endif
+
+ packet_length += padding_length;
+
+ /* append the MAC length to the total_length size */
+ total_length =
+ packet_length + (encrypted ? session->local.mac->mac_len : 0);
+
+ /* store packet_length, which is the size of the whole packet except
+ the MAC and the packet_length field itself */
+ _libssh2_htonu32(p->outbuf, packet_length - 4);
+ /* store padding_length */
+ p->outbuf[4] = (unsigned char)padding_length;
+
+ /* fill the padding area with random junk */
+ if(_libssh2_random(p->outbuf + 5 + data_len, padding_length)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_RANDGEN,
+ "Unable to get random bytes for packet padding");
+ }
+
+ if(encrypted) {
+ size_t i;
+
+ /* Calculate MAC hash. Put the output at index packet_length,
+ since that size includes the whole packet. The MAC is
+ calculated on the entire unencrypted packet, including all
+ fields except the MAC field itself. */
+ session->local.mac->hash(session, p->outbuf + packet_length,
+ session->local.seqno, p->outbuf,
+ packet_length, NULL, 0,
+ &session->local.mac_abstract);
+
+ /* Encrypt the whole packet data, one block size at a time.
+ The MAC field is not encrypted. */
+ for(i = 0; i < packet_length; i += session->local.crypt->blocksize) {
+ unsigned char *ptr = &p->outbuf[i];
+ if(session->local.crypt->crypt(session, ptr,
+ session->local.crypt->blocksize,
+ &session->local.crypt_abstract))
+ return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
+ }
+ }
+
+ session->local.seqno++;
+
+ ret = LIBSSH2_SEND(session, p->outbuf, total_length,
+ LIBSSH2_SOCKET_SEND_FLAGS(session));
+ if(ret < 0)
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET,
+ "Error sending %d bytes: %d", total_length, -ret);
+ else {
+ _libssh2_debug(session, LIBSSH2_TRACE_SOCKET, "Sent %d/%d bytes at %p",
+ ret, total_length, p->outbuf);
+ debugdump(session, "libssh2_transport_write send()", p->outbuf, ret);
+ }
+
+ if(ret != total_length) {
+ if(ret >= 0 || ret == -EAGAIN) {
+ /* the whole packet could not be sent, save the rest */
+ session->socket_block_directions |= LIBSSH2_SESSION_BLOCK_OUTBOUND;
+ p->odata = orgdata;
+ p->olen = orgdata_len;
+ p->osent = ret <= 0 ? 0 : ret;
+ p->ototal_num = total_length;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+ return LIBSSH2_ERROR_SOCKET_SEND;
+ }
+
+ /* the whole thing got sent away */
+ p->odata = NULL;
+ p->olen = 0;
+
+ return LIBSSH2_ERROR_NONE; /* all is good */
+}
diff --git a/contrib/libs/libssh2/src/transport.h b/contrib/libs/libssh2/src/transport.h
new file mode 100644
index 00000000000..7d395d0e78b
--- /dev/null
+++ b/contrib/libs/libssh2/src/transport.h
@@ -0,0 +1,86 @@
+#ifndef __LIBSSH2_TRANSPORT_H
+#define __LIBSSH2_TRANSPORT_H
+/* Copyright (C) 2007 The Written Word, Inc. All rights reserved.
+ * Copyright (C) 2009-2010 by Daniel Stenberg
+ * Author: Daniel Stenberg <[email protected]>
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.
+ *
+ * This file handles reading and writing to the SECSH transport layer. RFC4253.
+ */
+
+#include "libssh2_priv.h"
+#include "packet.h"
+
+
+/*
+ * libssh2_transport_send
+ *
+ * Send a packet, encrypting it and adding a MAC code if necessary
+ * Returns 0 on success, non-zero on failure.
+ *
+ * The data is provided as _two_ data areas that are combined by this
+ * function. The 'data' part is sent immediately before 'data2'. 'data2' can
+ * be set to NULL (or data2_len to 0) to only use a single part.
+ *
+ * Returns LIBSSH2_ERROR_EAGAIN if it would block or if the whole packet was
+ * not sent yet. If it does so, the caller should call this function again as
+ * soon as it is likely that more data can be sent, and this function MUST
+ * then be called with the same argument set (same data pointer and same
+ * data_len) until ERROR_NONE or failure is returned.
+ *
+ * This function DOES NOT call _libssh2_error() on any errors.
+ */
+int _libssh2_transport_send(LIBSSH2_SESSION *session,
+ const unsigned char *data, size_t data_len,
+ const unsigned char *data2, size_t data2_len);
+
+/*
+ * _libssh2_transport_read
+ *
+ * Collect a packet into the input brigade block only controls whether or not
+ * to wait for a packet to start.
+ *
+ * Returns packet type added to input brigade (PACKET_NONE if nothing added),
+ * or PACKET_FAIL on failure and PACKET_EAGAIN if it couldn't process a full
+ * packet.
+ */
+
+/*
+ * This function reads the binary stream as specified in chapter 6 of RFC4253
+ * "The Secure Shell (SSH) Transport Layer Protocol"
+ */
+int _libssh2_transport_read(LIBSSH2_SESSION * session);
+
+#endif /* __LIBSSH2_TRANSPORT_H */
diff --git a/contrib/libs/libssh2/src/userauth.c b/contrib/libs/libssh2/src/userauth.c
new file mode 100644
index 00000000000..40ef9153af2
--- /dev/null
+++ b/contrib/libs/libssh2/src/userauth.c
@@ -0,0 +1,2112 @@
+/* Copyright (c) 2004-2007, Sara Golemon <[email protected]>
+ * Copyright (c) 2005 Mikhail Gusarov <[email protected]>
+ * Copyright (c) 2009-2014 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include <assert.h>
+
+/* Needed for struct iovec on some platforms */
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include "transport.h"
+#include "session.h"
+#include "userauth.h"
+
+/* libssh2_userauth_list
+ *
+ * List authentication methods
+ * Will yield successful login if "none" happens to be allowable for this user
+ * Not a common configuration for any SSH server though
+ * username should be NULL, or a null terminated string
+ */
+static char *userauth_list(LIBSSH2_SESSION *session, const char *username,
+ unsigned int username_len)
+{
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
+ /* packet_type(1) + username_len(4) + service_len(4) +
+ service(14)"ssh-connection" + method_len(4) = 27 */
+ unsigned long methods_len;
+ unsigned char *s;
+ int rc;
+
+ if(session->userauth_list_state == libssh2_NB_state_idle) {
+ /* Zero the whole thing out */
+ memset(&session->userauth_list_packet_requirev_state, 0,
+ sizeof(session->userauth_list_packet_requirev_state));
+
+ session->userauth_list_data_len = username_len + 27;
+
+ s = session->userauth_list_data =
+ LIBSSH2_ALLOC(session, session->userauth_list_data_len);
+ if(!session->userauth_list_data) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for userauth_list");
+ return NULL;
+ }
+
+ *(s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection", 14);
+ _libssh2_store_u32(&s, 4); /* send "none" separately */
+
+ session->userauth_list_state = libssh2_NB_state_created;
+ }
+
+ if(session->userauth_list_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_list_data,
+ session->userauth_list_data_len,
+ (unsigned char *)"none", 4);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting userauth list");
+ return NULL;
+ }
+ /* now free the packet that was sent */
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+
+ if(rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-none request");
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ session->userauth_list_state = libssh2_NB_state_sent;
+ }
+
+ if(session->userauth_list_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_list_data,
+ &session->userauth_list_data_len, 0,
+ NULL, 0,
+ &session->userauth_list_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting userauth list");
+ return NULL;
+ }
+ else if(rc || (session->userauth_list_data_len < 1)) {
+ _libssh2_error(session, rc, "Failed getting response");
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ if(session->userauth_list_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ /* Wow, who'dve thought... */
+ _libssh2_error(session, LIBSSH2_ERROR_NONE, "No error");
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return NULL;
+ }
+
+ if(session->userauth_list_data_len < 5) {
+ LIBSSH2_FREE(session, session->userauth_list_data);
+ session->userauth_list_data = NULL;
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected packet size");
+ return NULL;
+ }
+
+ methods_len = _libssh2_ntohu32(session->userauth_list_data + 1);
+ if(methods_len >= session->userauth_list_data_len - 5) {
+ _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Unexpected userauth list size");
+ return NULL;
+ }
+
+ /* Do note that the memory areas overlap! */
+ memmove(session->userauth_list_data, session->userauth_list_data + 5,
+ methods_len);
+ session->userauth_list_data[methods_len] = '\0';
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Permitted auth methods: %s",
+ session->userauth_list_data);
+ }
+
+ session->userauth_list_state = libssh2_NB_state_idle;
+ return (char *) session->userauth_list_data;
+}
+
+/* libssh2_userauth_list
+ *
+ * List authentication methods
+ * Will yield successful login if "none" happens to be allowable for this user
+ * Not a common configuration for any SSH server though
+ * username should be NULL, or a null terminated string
+ */
+LIBSSH2_API char *
+libssh2_userauth_list(LIBSSH2_SESSION * session, const char *user,
+ unsigned int user_len)
+{
+ char *ptr;
+ BLOCK_ADJUST_ERRNO(ptr, session,
+ userauth_list(session, user, user_len));
+ return ptr;
+}
+
+/*
+ * libssh2_userauth_authenticated
+ *
+ * Returns: 0 if not yet authenticated
+ * 1 if already authenticated
+ */
+LIBSSH2_API int
+libssh2_userauth_authenticated(LIBSSH2_SESSION * session)
+{
+ return (session->state & LIBSSH2_STATE_AUTHENTICATED)?1:0;
+}
+
+
+
+/* userauth_password
+ * Plain ol' login
+ */
+static int
+userauth_password(LIBSSH2_SESSION *session,
+ const char *username, unsigned int username_len,
+ const unsigned char *password, unsigned int password_len,
+ LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)))
+{
+ unsigned char *s;
+ static const unsigned char reply_codes[4] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE,
+ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 0
+ };
+ int rc;
+
+ if(session->userauth_pswd_state == libssh2_NB_state_idle) {
+ /* Zero the whole thing out */
+ memset(&session->userauth_pswd_packet_requirev_state, 0,
+ sizeof(session->userauth_pswd_packet_requirev_state));
+
+ /*
+ * 40 = packet_type(1) + username_len(4) + service_len(4) +
+ * service(14)"ssh-connection" + method_len(4) + method(8)"password" +
+ * chgpwdbool(1) + password_len(4) */
+ session->userauth_pswd_data_len = username_len + 40;
+
+ session->userauth_pswd_data0 =
+ (unsigned char) ~SSH_MSG_USERAUTH_PASSWD_CHANGEREQ;
+
+ /* TODO: remove this alloc with a fixed buffer in the session
+ struct */
+ s = session->userauth_pswd_data =
+ LIBSSH2_ALLOC(session, session->userauth_pswd_data_len);
+ if(!session->userauth_pswd_data) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "userauth-password request");
+ }
+
+ *(s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection", sizeof("ssh-connection") - 1);
+ _libssh2_store_str(&s, "password", sizeof("password") - 1);
+ *s++ = '\0';
+ _libssh2_store_u32(&s, password_len);
+ /* 'password' is sent separately */
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting to login using password authentication");
+
+ session->userauth_pswd_state = libssh2_NB_state_created;
+ }
+
+ if(session->userauth_pswd_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_pswd_data,
+ session->userauth_pswd_data_len,
+ password, password_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block writing password request");
+ }
+
+ /* now free the sent packet */
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+
+ if(rc) {
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-password request");
+ }
+
+ session->userauth_pswd_state = libssh2_NB_state_sent;
+ }
+
+ password_response:
+
+ if((session->userauth_pswd_state == libssh2_NB_state_sent)
+ || (session->userauth_pswd_state == libssh2_NB_state_sent1)
+ || (session->userauth_pswd_state == libssh2_NB_state_sent2)) {
+ if(session->userauth_pswd_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_pswd_data,
+ &session->userauth_pswd_data_len,
+ 0, NULL, 0,
+ &session->
+ userauth_pswd_packet_requirev_state);
+
+ if(rc) {
+ if(rc != LIBSSH2_ERROR_EAGAIN)
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+
+ return _libssh2_error(session, rc,
+ "Waiting for password response");
+ }
+ else if(session->userauth_pswd_data_len < 1) {
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected packet size");
+ }
+
+ if(session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Password authentication successful");
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+ else if(session->userauth_pswd_data[0] ==
+ SSH_MSG_USERAUTH_FAILURE) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Password authentication failed");
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Authentication failed "
+ "(username/password)");
+ }
+
+ session->userauth_pswd_newpw = NULL;
+ session->userauth_pswd_newpw_len = 0;
+
+ session->userauth_pswd_state = libssh2_NB_state_sent1;
+ }
+
+ if(session->userauth_pswd_data_len < 1) {
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "Unexpected packet size");
+ }
+
+ if((session->userauth_pswd_data[0] ==
+ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ)
+ || (session->userauth_pswd_data0 ==
+ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ)) {
+ session->userauth_pswd_data0 = SSH_MSG_USERAUTH_PASSWD_CHANGEREQ;
+
+ if((session->userauth_pswd_state == libssh2_NB_state_sent1) ||
+ (session->userauth_pswd_state == libssh2_NB_state_sent2)) {
+ if(session->userauth_pswd_state == libssh2_NB_state_sent1) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Password change required");
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ }
+ if(passwd_change_cb) {
+ if(session->userauth_pswd_state ==
+ libssh2_NB_state_sent1) {
+ passwd_change_cb(session,
+ &session->userauth_pswd_newpw,
+ &session->userauth_pswd_newpw_len,
+ &session->abstract);
+ if(!session->userauth_pswd_newpw) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_PASSWORD_EXPIRED,
+ "Password expired, and "
+ "callback failed");
+ }
+
+ /* basic data_len + newpw_len(4) */
+ if(username_len + password_len + 44 <= UINT_MAX) {
+ session->userauth_pswd_data_len =
+ username_len + password_len + 44;
+ s = session->userauth_pswd_data =
+ LIBSSH2_ALLOC(session,
+ session->userauth_pswd_data_len);
+ }
+ else {
+ s = session->userauth_pswd_data = NULL;
+ session->userauth_pswd_data_len = 0;
+ }
+
+ if(!session->userauth_pswd_data) {
+ LIBSSH2_FREE(session,
+ session->userauth_pswd_newpw);
+ session->userauth_pswd_newpw = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory "
+ "for userauth password "
+ "change request");
+ }
+
+ *(s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection",
+ sizeof("ssh-connection") - 1);
+ _libssh2_store_str(&s, "password",
+ sizeof("password") - 1);
+ *s++ = 0x01;
+ _libssh2_store_str(&s, (char *)password, password_len);
+ _libssh2_store_u32(&s,
+ session->userauth_pswd_newpw_len);
+ /* send session->userauth_pswd_newpw separately */
+
+ session->userauth_pswd_state = libssh2_NB_state_sent2;
+ }
+
+ if(session->userauth_pswd_state ==
+ libssh2_NB_state_sent2) {
+ rc = _libssh2_transport_send(session,
+ session->userauth_pswd_data,
+ session->userauth_pswd_data_len,
+ (unsigned char *)
+ session->userauth_pswd_newpw,
+ session->userauth_pswd_newpw_len);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_EAGAIN,
+ "Would block waiting");
+ }
+
+ /* free the allocated packets again */
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ LIBSSH2_FREE(session, session->userauth_pswd_newpw);
+ session->userauth_pswd_newpw = NULL;
+
+ if(rc) {
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth "
+ "password-change request");
+ }
+
+ /*
+ * Ugliest use of goto ever. Blame it on the
+ * askN => requirev migration.
+ */
+ session->userauth_pswd_state = libssh2_NB_state_sent;
+ goto password_response;
+ }
+ }
+ }
+ else {
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED,
+ "Password Expired, and no callback "
+ "specified");
+ }
+ }
+ }
+
+ /* FAILURE */
+ LIBSSH2_FREE(session, session->userauth_pswd_data);
+ session->userauth_pswd_data = NULL;
+ session->userauth_pswd_state = libssh2_NB_state_idle;
+
+ return _libssh2_error(session, LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Authentication failed");
+}
+
+/*
+ * libssh2_userauth_password_ex
+ *
+ * Plain ol' login
+ */
+
+LIBSSH2_API int
+libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username,
+ unsigned int username_len, const char *password,
+ unsigned int password_len,
+ LIBSSH2_PASSWD_CHANGEREQ_FUNC
+ ((*passwd_change_cb)))
+{
+ int rc;
+ BLOCK_ADJUST(rc, session,
+ userauth_password(session, username, username_len,
+ (unsigned char *)password, password_len,
+ passwd_change_cb));
+ return rc;
+}
+
+static int
+memory_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *pubkeyfiledata,
+ size_t pubkeyfiledata_len)
+{
+ unsigned char *pubkey = NULL, *sp1, *sp2, *tmp;
+ size_t pubkey_len = pubkeyfiledata_len;
+ unsigned int tmp_len;
+
+ if(pubkeyfiledata_len <= 1) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid data in public key file");
+ }
+
+ pubkey = LIBSSH2_ALLOC(session, pubkeyfiledata_len);
+ if(!pubkey) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for public key data");
+ }
+
+ memcpy(pubkey, pubkeyfiledata, pubkeyfiledata_len);
+
+ /*
+ * Remove trailing whitespace
+ */
+ while(pubkey_len && isspace(pubkey[pubkey_len - 1]))
+ pubkey_len--;
+
+ if(!pubkey_len) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Missing public key data");
+ }
+
+ sp1 = memchr(pubkey, ' ', pubkey_len);
+ if(sp1 == NULL) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid public key data");
+ }
+
+ sp1++;
+
+ sp2 = memchr(sp1, ' ', pubkey_len - (sp1 - pubkey));
+ if(sp2 == NULL) {
+ /* Assume that the id string is missing, but that it's okay */
+ sp2 = pubkey + pubkey_len;
+ }
+
+ if(libssh2_base64_decode(session, (char **) &tmp, &tmp_len,
+ (char *) sp1, sp2 - sp1)) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid key data, not base64 encoded");
+ }
+
+ /* Wasting some bytes here (okay, more than some), but since it's likely
+ * to be freed soon anyway, we'll just avoid the extra free/alloc and call
+ * it a wash
+ */
+ *method = pubkey;
+ *method_len = sp1 - pubkey - 1;
+
+ *pubkeydata = tmp;
+ *pubkeydata_len = tmp_len;
+
+ return 0;
+}
+
+/*
+ * file_read_publickey
+ *
+ * Read a public key from an id_???.pub style file
+ *
+ * Returns an allocated string containing the decoded key in *pubkeydata
+ * on success.
+ * Returns an allocated string containing the key method (e.g. "ssh-dss")
+ * in method on success.
+ */
+static int
+file_read_publickey(LIBSSH2_SESSION * session, unsigned char **method,
+ size_t *method_len,
+ unsigned char **pubkeydata,
+ size_t *pubkeydata_len,
+ const char *pubkeyfile)
+{
+ FILE *fd;
+ char c;
+ unsigned char *pubkey = NULL, *sp1, *sp2, *tmp;
+ size_t pubkey_len = 0, sp_len;
+ unsigned int tmp_len;
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Loading public key file: %s",
+ pubkeyfile);
+ /* Read Public Key */
+ fd = fopen(pubkeyfile, FOPEN_READTEXT);
+ if(!fd) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to open public key file");
+ }
+ while(!feof(fd) && 1 == fread(&c, 1, 1, fd) && c != '\r' && c != '\n') {
+ pubkey_len++;
+ }
+ rewind(fd);
+
+ if(pubkey_len <= 1) {
+ fclose(fd);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid data in public key file");
+ }
+
+ pubkey = LIBSSH2_ALLOC(session, pubkey_len);
+ if(!pubkey) {
+ fclose(fd);
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for public key data");
+ }
+ if(fread(pubkey, 1, pubkey_len, fd) != pubkey_len) {
+ LIBSSH2_FREE(session, pubkey);
+ fclose(fd);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to read public key from file");
+ }
+ fclose(fd);
+ /*
+ * Remove trailing whitespace
+ */
+ while(pubkey_len && isspace(pubkey[pubkey_len - 1])) {
+ pubkey_len--;
+ }
+
+ if(!pubkey_len) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Missing public key data");
+ }
+
+ sp1 = memchr(pubkey, ' ', pubkey_len);
+ if(sp1 == NULL) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid public key data");
+ }
+
+ sp1++;
+
+ sp_len = sp1 > pubkey ? (sp1 - pubkey) : 0;
+ sp2 = memchr(sp1, ' ', pubkey_len - sp_len);
+ if(sp2 == NULL) {
+ /* Assume that the id string is missing, but that it's okay */
+ sp2 = pubkey + pubkey_len;
+ }
+
+ if(libssh2_base64_decode(session, (char **) &tmp, &tmp_len,
+ (char *) sp1, sp2 - sp1)) {
+ LIBSSH2_FREE(session, pubkey);
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid key data, not base64 encoded");
+ }
+
+ /* Wasting some bytes here (okay, more than some), but since it's likely
+ * to be freed soon anyway, we'll just avoid the extra free/alloc and call
+ * it a wash */
+ *method = pubkey;
+ *method_len = sp1 - pubkey - 1;
+
+ *pubkeydata = tmp;
+ *pubkeydata_len = tmp_len;
+
+ return 0;
+}
+
+static int
+memory_read_privatekey(LIBSSH2_SESSION * session,
+ const LIBSSH2_HOSTKEY_METHOD ** hostkey_method,
+ void **hostkey_abstract,
+ const unsigned char *method, int method_len,
+ const char *privkeyfiledata, size_t privkeyfiledata_len,
+ const char *passphrase)
+{
+ const LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail =
+ libssh2_hostkey_methods();
+
+ *hostkey_method = NULL;
+ *hostkey_abstract = NULL;
+ while(*hostkey_methods_avail && (*hostkey_methods_avail)->name) {
+ if((*hostkey_methods_avail)->initPEMFromMemory
+ && strncmp((*hostkey_methods_avail)->name, (const char *) method,
+ method_len) == 0) {
+ *hostkey_method = *hostkey_methods_avail;
+ break;
+ }
+ hostkey_methods_avail++;
+ }
+ if(!*hostkey_method) {
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
+ "No handler for specified private key");
+ }
+
+ if((*hostkey_method)->
+ initPEMFromMemory(session, privkeyfiledata, privkeyfiledata_len,
+ (unsigned char *) passphrase,
+ hostkey_abstract)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to initialize private key from file");
+ }
+
+ return 0;
+}
+
+/* libssh2_file_read_privatekey
+ * Read a PEM encoded private key from an id_??? style file
+ */
+static int
+file_read_privatekey(LIBSSH2_SESSION * session,
+ const LIBSSH2_HOSTKEY_METHOD ** hostkey_method,
+ void **hostkey_abstract,
+ const unsigned char *method, int method_len,
+ const char *privkeyfile, const char *passphrase)
+{
+ const LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail =
+ libssh2_hostkey_methods();
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH, "Loading private key file: %s",
+ privkeyfile);
+ *hostkey_method = NULL;
+ *hostkey_abstract = NULL;
+ while(*hostkey_methods_avail && (*hostkey_methods_avail)->name) {
+ if((*hostkey_methods_avail)->initPEM
+ && strncmp((*hostkey_methods_avail)->name, (const char *) method,
+ method_len) == 0) {
+ *hostkey_method = *hostkey_methods_avail;
+ break;
+ }
+ hostkey_methods_avail++;
+ }
+ if(!*hostkey_method) {
+ return _libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE,
+ "No handler for specified private key");
+ }
+
+ if((*hostkey_method)->
+ initPEM(session, privkeyfile, (unsigned char *) passphrase,
+ hostkey_abstract)) {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Unable to initialize private key from file");
+ }
+
+ return 0;
+}
+
+struct privkey_file {
+ const char *filename;
+ const char *passphrase;
+};
+
+static int
+sign_frommemory(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
+ const unsigned char *data, size_t data_len, void **abstract)
+{
+ struct privkey_file *pk_file = (struct privkey_file *) (*abstract);
+ const LIBSSH2_HOSTKEY_METHOD *privkeyobj;
+ void *hostkey_abstract;
+ struct iovec datavec;
+ int rc;
+
+ rc = memory_read_privatekey(session, &privkeyobj, &hostkey_abstract,
+ session->userauth_pblc_method,
+ session->userauth_pblc_method_len,
+ pk_file->filename,
+ strlen(pk_file->filename),
+ pk_file->passphrase);
+ if(rc)
+ return rc;
+
+ libssh2_prepare_iovec(&datavec, 1);
+ datavec.iov_base = (void *)data;
+ datavec.iov_len = data_len;
+
+ if(privkeyobj->signv(session, sig, sig_len, 1, &datavec,
+ &hostkey_abstract)) {
+ if(privkeyobj->dtor) {
+ privkeyobj->dtor(session, &hostkey_abstract);
+ }
+ return -1;
+ }
+
+ if(privkeyobj->dtor) {
+ privkeyobj->dtor(session, &hostkey_abstract);
+ }
+ return 0;
+}
+
+static int
+sign_fromfile(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len,
+ const unsigned char *data, size_t data_len, void **abstract)
+{
+ struct privkey_file *privkey_file = (struct privkey_file *) (*abstract);
+ const LIBSSH2_HOSTKEY_METHOD *privkeyobj;
+ void *hostkey_abstract;
+ struct iovec datavec;
+ int rc;
+
+ rc = file_read_privatekey(session, &privkeyobj, &hostkey_abstract,
+ session->userauth_pblc_method,
+ session->userauth_pblc_method_len,
+ privkey_file->filename,
+ privkey_file->passphrase);
+ if(rc)
+ return rc;
+
+ libssh2_prepare_iovec(&datavec, 1);
+ datavec.iov_base = (void *)data;
+ datavec.iov_len = data_len;
+
+ if(privkeyobj->signv(session, sig, sig_len, 1, &datavec,
+ &hostkey_abstract)) {
+ if(privkeyobj->dtor) {
+ privkeyobj->dtor(session, &hostkey_abstract);
+ }
+ return -1;
+ }
+
+ if(privkeyobj->dtor) {
+ privkeyobj->dtor(session, &hostkey_abstract);
+ }
+ return 0;
+}
+
+
+
+/* userauth_hostbased_fromfile
+ * Authenticate using a keypair found in the named files
+ */
+static int
+userauth_hostbased_fromfile(LIBSSH2_SESSION *session,
+ const char *username, size_t username_len,
+ const char *publickey, const char *privatekey,
+ const char *passphrase, const char *hostname,
+ size_t hostname_len,
+ const char *local_username,
+ size_t local_username_len)
+{
+ int rc;
+
+ if(session->userauth_host_state == libssh2_NB_state_idle) {
+ const LIBSSH2_HOSTKEY_METHOD *privkeyobj;
+ unsigned char *pubkeydata = NULL;
+ unsigned char *sig = NULL;
+ size_t pubkeydata_len = 0;
+ size_t sig_len = 0;
+ void *abstract;
+ unsigned char buf[5];
+ struct iovec datavec[4];
+
+ /* Zero the whole thing out */
+ memset(&session->userauth_host_packet_requirev_state, 0,
+ sizeof(session->userauth_host_packet_requirev_state));
+
+ if(publickey) {
+ rc = file_read_publickey(session, &session->userauth_host_method,
+ &session->userauth_host_method_len,
+ &pubkeydata, &pubkeydata_len, publickey);
+ if(rc)
+ /* Note: file_read_publickey() calls _libssh2_error() */
+ return rc;
+ }
+ else {
+ /* Compute public key from private key. */
+ rc = _libssh2_pub_priv_keyfile(session,
+ &session->userauth_host_method,
+ &session->userauth_host_method_len,
+ &pubkeydata, &pubkeydata_len,
+ privatekey, passphrase);
+ if(rc)
+ /* libssh2_pub_priv_keyfile calls _libssh2_error() */
+ return rc;
+ }
+
+ /*
+ * 52 = packet_type(1) + username_len(4) + servicename_len(4) +
+ * service_name(14)"ssh-connection" + authmethod_len(4) +
+ * authmethod(9)"hostbased" + method_len(4) + pubkeydata_len(4) +
+ * hostname_len(4) + local_username_len(4)
+ */
+ session->userauth_host_packet_len =
+ username_len + session->userauth_host_method_len + hostname_len +
+ local_username_len + pubkeydata_len + 52;
+
+ /*
+ * Preallocate space for an overall length, method name again,
+ * and the signature, which won't be any larger than the size of
+ * the publickeydata itself
+ */
+ session->userauth_host_s = session->userauth_host_packet =
+ LIBSSH2_ALLOC(session,
+ session->userauth_host_packet_len + 4 +
+ (4 + session->userauth_host_method_len) +
+ (4 + pubkeydata_len));
+ if(!session->userauth_host_packet) {
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ LIBSSH2_FREE(session, pubkeydata);
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Out of memory");
+ }
+
+ *(session->userauth_host_s++) = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&session->userauth_host_s, username, username_len);
+ _libssh2_store_str(&session->userauth_host_s, "ssh-connection", 14);
+ _libssh2_store_str(&session->userauth_host_s, "hostbased", 9);
+ _libssh2_store_str(&session->userauth_host_s,
+ (const char *)session->userauth_host_method,
+ session->userauth_host_method_len);
+ _libssh2_store_str(&session->userauth_host_s, (const char *)pubkeydata,
+ pubkeydata_len);
+ LIBSSH2_FREE(session, pubkeydata);
+ _libssh2_store_str(&session->userauth_host_s, hostname, hostname_len);
+ _libssh2_store_str(&session->userauth_host_s, local_username,
+ local_username_len);
+
+ rc = file_read_privatekey(session, &privkeyobj, &abstract,
+ session->userauth_host_method,
+ session->userauth_host_method_len,
+ privatekey, passphrase);
+ if(rc) {
+ /* Note: file_read_privatekey() calls _libssh2_error() */
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ return rc;
+ }
+
+ _libssh2_htonu32(buf, session->session_id_len);
+ libssh2_prepare_iovec(datavec, 4);
+ datavec[0].iov_base = (void *)buf;
+ datavec[0].iov_len = 4;
+ datavec[1].iov_base = (void *)session->session_id;
+ datavec[1].iov_len = session->session_id_len;
+ datavec[2].iov_base = (void *)session->userauth_host_packet;
+ datavec[2].iov_len = session->userauth_host_packet_len;
+
+ if(privkeyobj && privkeyobj->signv &&
+ privkeyobj->signv(session, &sig, &sig_len, 3,
+ datavec, &abstract)) {
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ if(privkeyobj->dtor) {
+ privkeyobj->dtor(session, &abstract);
+ }
+ return -1;
+ }
+
+ if(privkeyobj && privkeyobj->dtor) {
+ privkeyobj->dtor(session, &abstract);
+ }
+
+ if(sig_len > pubkeydata_len) {
+ unsigned char *newpacket;
+ /* Should *NEVER* happen, but...well.. better safe than sorry */
+ newpacket = LIBSSH2_REALLOC(session, session->userauth_host_packet,
+ session->userauth_host_packet_len + 4 +
+ (4 + session->userauth_host_method_len)
+ + (4 + sig_len)); /* PK sigblob */
+ if(!newpacket) {
+ LIBSSH2_FREE(session, sig);
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed allocating additional space for "
+ "userauth-hostbased packet");
+ }
+ session->userauth_host_packet = newpacket;
+ }
+
+ session->userauth_host_s =
+ session->userauth_host_packet + session->userauth_host_packet_len;
+
+ _libssh2_store_u32(&session->userauth_host_s,
+ 4 + session->userauth_host_method_len +
+ 4 + sig_len);
+ _libssh2_store_str(&session->userauth_host_s,
+ (const char *)session->userauth_host_method,
+ session->userauth_host_method_len);
+ LIBSSH2_FREE(session, session->userauth_host_method);
+ session->userauth_host_method = NULL;
+
+ _libssh2_store_str(&session->userauth_host_s, (const char *)sig,
+ sig_len);
+ LIBSSH2_FREE(session, sig);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting hostbased authentication");
+
+ session->userauth_host_state = libssh2_NB_state_created;
+ }
+
+ if(session->userauth_host_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_host_packet,
+ session->userauth_host_s -
+ session->userauth_host_packet,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ }
+ else if(rc) {
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+ session->userauth_host_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-hostbased request");
+ }
+ LIBSSH2_FREE(session, session->userauth_host_packet);
+ session->userauth_host_packet = NULL;
+
+ session->userauth_host_state = libssh2_NB_state_sent;
+ }
+
+ if(session->userauth_host_state == libssh2_NB_state_sent) {
+ static const unsigned char reply_codes[3] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
+ size_t data_len;
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_host_data,
+ &data_len, 0, NULL, 0,
+ &session->
+ userauth_host_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ }
+
+ session->userauth_host_state = libssh2_NB_state_idle;
+ if(rc || data_len < 1) {
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Auth failed");
+ }
+
+ if(session->userauth_host_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Hostbased authentication successful");
+ /* We are us and we've proved it. */
+ LIBSSH2_FREE(session, session->userauth_host_data);
+ session->userauth_host_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ return 0;
+ }
+ }
+
+ /* This public key is not allowed for this user on this server */
+ LIBSSH2_FREE(session, session->userauth_host_data);
+ session->userauth_host_data = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid signature for supplied public key, or bad "
+ "username/public key combination");
+}
+
+/* libssh2_userauth_hostbased_fromfile_ex
+ * Authenticate using a keypair found in the named files
+ */
+LIBSSH2_API int
+libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session,
+ const char *user,
+ unsigned int user_len,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase,
+ const char *host,
+ unsigned int host_len,
+ const char *localuser,
+ unsigned int localuser_len)
+{
+ int rc;
+ BLOCK_ADJUST(rc, session,
+ userauth_hostbased_fromfile(session, user, user_len,
+ publickey, privatekey,
+ passphrase, host, host_len,
+ localuser, localuser_len));
+ return rc;
+}
+
+static int plain_method_len(const char *method, size_t method_len)
+{
+ if(!strncmp("[email protected]",
+ method,
+ method_len) ||
+ !strncmp("[email protected]",
+ method,
+ method_len) ||
+ !strncmp("[email protected]",
+ method,
+ method_len)) {
+ return 19;
+ }
+ return method_len;
+}
+
+int
+_libssh2_userauth_publickey(LIBSSH2_SESSION *session,
+ const char *username,
+ unsigned int username_len,
+ const unsigned char *pubkeydata,
+ unsigned long pubkeydata_len,
+ LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC
+ ((*sign_callback)),
+ void *abstract)
+{
+ unsigned char reply_codes[4] =
+ { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE,
+ SSH_MSG_USERAUTH_PK_OK, 0
+ };
+ int rc;
+ unsigned char *s;
+
+ if(session->userauth_pblc_state == libssh2_NB_state_idle) {
+
+ /*
+ * The call to _libssh2_ntohu32 later relies on pubkeydata having at
+ * least 4 valid bytes containing the length of the method name.
+ */
+ if(pubkeydata_len < 4)
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid public key, too short");
+
+ /* Zero the whole thing out */
+ memset(&session->userauth_pblc_packet_requirev_state, 0,
+ sizeof(session->userauth_pblc_packet_requirev_state));
+
+ /*
+ * As an optimisation, userauth_publickey_fromfile reuses a
+ * previously allocated copy of the method name to avoid an extra
+ * allocation/free.
+ * For other uses, we allocate and populate it here.
+ */
+ if(!session->userauth_pblc_method) {
+ session->userauth_pblc_method_len = _libssh2_ntohu32(pubkeydata);
+
+ if(session->userauth_pblc_method_len > pubkeydata_len - 4)
+ /* the method length simply cannot be longer than the entire
+ passed in data, so we use this to detect crazy input
+ data */
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid public key");
+
+ session->userauth_pblc_method =
+ LIBSSH2_ALLOC(session, session->userauth_pblc_method_len);
+ if(!session->userauth_pblc_method) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory "
+ "for public key data");
+ }
+ memcpy(session->userauth_pblc_method, pubkeydata + 4,
+ session->userauth_pblc_method_len);
+ }
+ /*
+ * The length of the method name read from plaintext prefix in the
+ * file must match length embedded in the key.
+ * TODO: The data should match too but we don't check that. Should we?
+ */
+ else if(session->userauth_pblc_method_len !=
+ _libssh2_ntohu32(pubkeydata))
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid public key");
+
+ /*
+ * 45 = packet_type(1) + username_len(4) + servicename_len(4) +
+ * service_name(14)"ssh-connection" + authmethod_len(4) +
+ * authmethod(9)"publickey" + sig_included(1)'\0' + algmethod_len(4) +
+ * publickey_len(4)
+ */
+ session->userauth_pblc_packet_len =
+ username_len + session->userauth_pblc_method_len + pubkeydata_len +
+ 45;
+
+ /*
+ * Preallocate space for an overall length, method name again, and the
+ * signature, which won't be any larger than the size of the
+ * publickeydata itself.
+ *
+ * Note that the 'pubkeydata_len' extra bytes allocated here will not
+ * be used in this first send, but will be used in the later one where
+ * this same allocation is re-used.
+ */
+ s = session->userauth_pblc_packet =
+ LIBSSH2_ALLOC(session,
+ session->userauth_pblc_packet_len + 4 +
+ (4 + session->userauth_pblc_method_len)
+ + (4 + pubkeydata_len));
+ if(!session->userauth_pblc_packet) {
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Out of memory");
+ }
+
+ *s++ = SSH_MSG_USERAUTH_REQUEST;
+ _libssh2_store_str(&s, username, username_len);
+ _libssh2_store_str(&s, "ssh-connection", 14);
+ _libssh2_store_str(&s, "publickey", 9);
+
+ session->userauth_pblc_b = s;
+ /* Not sending signature with *this* packet */
+ *s++ = 0;
+
+ _libssh2_store_str(&s, (const char *)session->userauth_pblc_method,
+ session->userauth_pblc_method_len);
+ _libssh2_store_str(&s, (const char *)pubkeydata, pubkeydata_len);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting publickey authentication");
+
+ session->userauth_pblc_state = libssh2_NB_state_created;
+ }
+
+ if(session->userauth_pblc_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_pblc_packet,
+ session->userauth_pblc_packet_len,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ else if(rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-publickey request");
+ }
+
+ session->userauth_pblc_state = libssh2_NB_state_sent;
+ }
+
+ if(session->userauth_pblc_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_pblc_data,
+ &session->userauth_pblc_data_len, 0,
+ NULL, 0,
+ &session->
+ userauth_pblc_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ }
+ else if(rc || (session->userauth_pblc_data_len < 1)) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Waiting for USERAUTH response");
+ }
+
+ if(session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Pubkey authentication prematurely successful");
+ /*
+ * God help any SSH server that allows an UNVERIFIED
+ * public key to validate the user
+ */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ if(session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_FAILURE) {
+ /* This public key is not allowed for this user on this server */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Username/PublicKey combination invalid");
+ }
+
+ /* Semi-Success! */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+
+ *session->userauth_pblc_b = 0x01;
+ session->userauth_pblc_state = libssh2_NB_state_sent1;
+ }
+
+ if(session->userauth_pblc_state == libssh2_NB_state_sent1) {
+ unsigned char *buf;
+ unsigned char *sig;
+ size_t sig_len;
+
+ s = buf = LIBSSH2_ALLOC(session, 4 + session->session_id_len
+ + session->userauth_pblc_packet_len);
+ if(!buf) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "userauth-publickey signed data");
+ }
+
+ _libssh2_store_str(&s, (const char *)session->session_id,
+ session->session_id_len);
+
+ memcpy(s, session->userauth_pblc_packet,
+ session->userauth_pblc_packet_len);
+ s += session->userauth_pblc_packet_len;
+
+ rc = sign_callback(session, &sig, &sig_len, buf, s - buf, abstract);
+ LIBSSH2_FREE(session, buf);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ }
+ else if(rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Callback returned error");
+ }
+
+ /*
+ * If this function was restarted, pubkeydata_len might still be 0
+ * which will cause an unnecessary but harmless realloc here.
+ */
+ if(sig_len > pubkeydata_len) {
+ unsigned char *newpacket;
+ /* Should *NEVER* happen, but...well.. better safe than sorry */
+ newpacket = LIBSSH2_REALLOC(session,
+ session->userauth_pblc_packet,
+ session->userauth_pblc_packet_len + 4 +
+ (4 + session->userauth_pblc_method_len)
+ + (4 + sig_len)); /* PK sigblob */
+ if(!newpacket) {
+ LIBSSH2_FREE(session, sig);
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Failed allocating additional space for "
+ "userauth-publickey packet");
+ }
+ session->userauth_pblc_packet = newpacket;
+ }
+
+ s = session->userauth_pblc_packet + session->userauth_pblc_packet_len;
+ session->userauth_pblc_b = NULL;
+
+ session->userauth_pblc_method_len =
+ plain_method_len((const char *)session->userauth_pblc_method,
+ session->userauth_pblc_method_len);
+
+ _libssh2_store_u32(&s,
+ 4 + session->userauth_pblc_method_len + 4 +
+ sig_len);
+ _libssh2_store_str(&s, (const char *)session->userauth_pblc_method,
+ session->userauth_pblc_method_len);
+
+ LIBSSH2_FREE(session, session->userauth_pblc_method);
+ session->userauth_pblc_method = NULL;
+
+ _libssh2_store_str(&s, (const char *)sig, sig_len);
+ LIBSSH2_FREE(session, sig);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting publickey authentication -- phase 2");
+
+ session->userauth_pblc_s = s;
+ session->userauth_pblc_state = libssh2_NB_state_sent2;
+ }
+
+ if(session->userauth_pblc_state == libssh2_NB_state_sent2) {
+ rc = _libssh2_transport_send(session, session->userauth_pblc_packet,
+ session->userauth_pblc_s -
+ session->userauth_pblc_packet,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ }
+ else if(rc) {
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-publickey request");
+ }
+ LIBSSH2_FREE(session, session->userauth_pblc_packet);
+ session->userauth_pblc_packet = NULL;
+
+ session->userauth_pblc_state = libssh2_NB_state_sent3;
+ }
+
+ /* PK_OK is no longer valid */
+ reply_codes[2] = 0;
+
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_pblc_data,
+ &session->userauth_pblc_data_len, 0, NULL, 0,
+ &session->userauth_pblc_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block requesting userauth list");
+ }
+ else if(rc || session->userauth_pblc_data_len < 1) {
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Waiting for publickey USERAUTH response");
+ }
+
+ if(session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Publickey authentication successful");
+ /* We are us and we've proved it. */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ /* This public key is not allowed for this user on this server */
+ LIBSSH2_FREE(session, session->userauth_pblc_data);
+ session->userauth_pblc_data = NULL;
+ session->userauth_pblc_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED,
+ "Invalid signature for supplied public key, or bad "
+ "username/public key combination");
+}
+
+ /*
+ * userauth_publickey_frommemory
+ * Authenticate using a keypair from memory
+ */
+static int
+userauth_publickey_frommemory(LIBSSH2_SESSION *session,
+ const char *username,
+ size_t username_len,
+ const char *publickeydata,
+ size_t publickeydata_len,
+ const char *privatekeydata,
+ size_t privatekeydata_len,
+ const char *passphrase)
+{
+ unsigned char *pubkeydata = NULL;
+ size_t pubkeydata_len = 0;
+ struct privkey_file privkey_file;
+ void *abstract = &privkey_file;
+ int rc;
+
+ privkey_file.filename = privatekeydata;
+ privkey_file.passphrase = passphrase;
+
+ if(session->userauth_pblc_state == libssh2_NB_state_idle) {
+ if(publickeydata_len && publickeydata) {
+ rc = memory_read_publickey(session, &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,
+ publickeydata, publickeydata_len);
+ if(rc)
+ return rc;
+ }
+ else if(privatekeydata_len && privatekeydata) {
+ /* Compute public key from private key. */
+ rc = _libssh2_pub_priv_keyfilememory(session,
+ &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,
+ privatekeydata, privatekeydata_len,
+ passphrase);
+ if(rc)
+ return rc;
+ }
+ else {
+ return _libssh2_error(session, LIBSSH2_ERROR_FILE,
+ "Invalid data in public and private key.");
+ }
+ }
+
+ rc = _libssh2_userauth_publickey(session, username, username_len,
+ pubkeydata, pubkeydata_len,
+ sign_frommemory, &abstract);
+ if(pubkeydata)
+ LIBSSH2_FREE(session, pubkeydata);
+
+ return rc;
+}
+
+/*
+ * userauth_publickey_fromfile
+ * Authenticate using a keypair found in the named files
+ */
+static int
+userauth_publickey_fromfile(LIBSSH2_SESSION *session,
+ const char *username,
+ size_t username_len,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+ unsigned char *pubkeydata = NULL;
+ size_t pubkeydata_len = 0;
+ struct privkey_file privkey_file;
+ void *abstract = &privkey_file;
+ int rc;
+
+ privkey_file.filename = privatekey;
+ privkey_file.passphrase = passphrase;
+
+ if(session->userauth_pblc_state == libssh2_NB_state_idle) {
+ if(publickey) {
+ rc = file_read_publickey(session, &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len, publickey);
+ if(rc)
+ return rc;
+ }
+ else {
+ /* Compute public key from private key. */
+ rc = _libssh2_pub_priv_keyfile(session,
+ &session->userauth_pblc_method,
+ &session->userauth_pblc_method_len,
+ &pubkeydata, &pubkeydata_len,
+ privatekey, passphrase);
+
+ /* _libssh2_pub_priv_keyfile calls _libssh2_error() */
+ if(rc)
+ return rc;
+ }
+ }
+
+ rc = _libssh2_userauth_publickey(session, username, username_len,
+ pubkeydata, pubkeydata_len,
+ sign_fromfile, &abstract);
+ if(pubkeydata)
+ LIBSSH2_FREE(session, pubkeydata);
+
+ return rc;
+}
+
+/* libssh2_userauth_publickey_frommemory
+ * Authenticate using a keypair from memory
+ */
+LIBSSH2_API int
+libssh2_userauth_publickey_frommemory(LIBSSH2_SESSION *session,
+ const char *user,
+ size_t user_len,
+ const char *publickeyfiledata,
+ size_t publickeyfiledata_len,
+ const char *privatekeyfiledata,
+ size_t privatekeyfiledata_len,
+ const char *passphrase)
+{
+ int rc;
+
+ if(NULL == passphrase)
+ /* if given a NULL pointer, make it point to a zero-length
+ string to save us from having to check this all over */
+ passphrase = "";
+
+ BLOCK_ADJUST(rc, session,
+ userauth_publickey_frommemory(session, user, user_len,
+ publickeyfiledata,
+ publickeyfiledata_len,
+ privatekeyfiledata,
+ privatekeyfiledata_len,
+ passphrase));
+ return rc;
+}
+
+/* libssh2_userauth_publickey_fromfile_ex
+ * Authenticate using a keypair found in the named files
+ */
+LIBSSH2_API int
+libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
+ const char *user,
+ unsigned int user_len,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+ int rc;
+
+ if(NULL == passphrase)
+ /* if given a NULL pointer, make it point to a zero-length
+ string to save us from having to check this all over */
+ passphrase = "";
+
+ BLOCK_ADJUST(rc, session,
+ userauth_publickey_fromfile(session, user, user_len,
+ publickey, privatekey,
+ passphrase));
+ return rc;
+}
+
+/* libssh2_userauth_publickey_ex
+ * Authenticate using an external callback function
+ */
+LIBSSH2_API int
+libssh2_userauth_publickey(LIBSSH2_SESSION *session,
+ const char *user,
+ const unsigned char *pubkeydata,
+ size_t pubkeydata_len,
+ LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC
+ ((*sign_callback)),
+ void **abstract)
+{
+ int rc;
+
+ if(!session)
+ return LIBSSH2_ERROR_BAD_USE;
+
+ BLOCK_ADJUST(rc, session,
+ _libssh2_userauth_publickey(session, user, strlen(user),
+ pubkeydata, pubkeydata_len,
+ sign_callback, abstract));
+ return rc;
+}
+
+
+
+/*
+ * userauth_keyboard_interactive
+ *
+ * Authenticate using a challenge-response authentication
+ */
+static int
+userauth_keyboard_interactive(LIBSSH2_SESSION * session,
+ const char *username,
+ unsigned int username_len,
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC
+ ((*response_callback)))
+{
+ unsigned char *s;
+ int rc;
+
+ static const unsigned char reply_codes[4] = {
+ SSH_MSG_USERAUTH_SUCCESS,
+ SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0
+ };
+ unsigned int language_tag_len;
+ unsigned int i;
+
+ if(session->userauth_kybd_state == libssh2_NB_state_idle) {
+ session->userauth_kybd_auth_name = NULL;
+ session->userauth_kybd_auth_instruction = NULL;
+ session->userauth_kybd_num_prompts = 0;
+ session->userauth_kybd_auth_failure = 1;
+ session->userauth_kybd_prompts = NULL;
+ session->userauth_kybd_responses = NULL;
+
+ /* Zero the whole thing out */
+ memset(&session->userauth_kybd_packet_requirev_state, 0,
+ sizeof(session->userauth_kybd_packet_requirev_state));
+
+ session->userauth_kybd_packet_len =
+ 1 /* byte SSH_MSG_USERAUTH_REQUEST */
+ + 4 + username_len /* string user name (ISO-10646 UTF-8, as
+ defined in [RFC-3629]) */
+ + 4 + 14 /* string service name (US-ASCII) */
+ + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */
+ + 4 + 0 /* string language tag (as defined in
+ [RFC-3066]) */
+ + 4 + 0 /* string submethods (ISO-10646 UTF-8) */
+ ;
+
+ session->userauth_kybd_data = s =
+ LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len);
+ if(!s) {
+ return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive authentication");
+ }
+
+ *s++ = SSH_MSG_USERAUTH_REQUEST;
+
+ /* user name */
+ _libssh2_store_str(&s, username, username_len);
+
+ /* service name */
+ _libssh2_store_str(&s, "ssh-connection", sizeof("ssh-connection") - 1);
+
+ /* "keyboard-interactive" */
+ _libssh2_store_str(&s, "keyboard-interactive",
+ sizeof("keyboard-interactive") - 1);
+ /* language tag */
+ _libssh2_store_u32(&s, 0);
+
+ /* submethods */
+ _libssh2_store_u32(&s, 0);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Attempting keyboard-interactive authentication");
+
+ session->userauth_kybd_state = libssh2_NB_state_created;
+ }
+
+ if(session->userauth_kybd_state == libssh2_NB_state_created) {
+ rc = _libssh2_transport_send(session, session->userauth_kybd_data,
+ session->userauth_kybd_packet_len,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ }
+ else if(rc) {
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send keyboard-interactive"
+ " request");
+ }
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+
+ session->userauth_kybd_state = libssh2_NB_state_sent;
+ }
+
+ for(;;) {
+ if(session->userauth_kybd_state == libssh2_NB_state_sent) {
+ rc = _libssh2_packet_requirev(session, reply_codes,
+ &session->userauth_kybd_data,
+ &session->userauth_kybd_data_len,
+ 0, NULL, 0,
+ &session->
+ userauth_kybd_packet_requirev_state);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ }
+ else if(rc || session->userauth_kybd_data_len < 1) {
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Waiting for keyboard "
+ "USERAUTH response");
+ }
+
+ if(session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_SUCCESS) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Keyboard-interactive "
+ "authentication successful");
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+ session->state |= LIBSSH2_STATE_AUTHENTICATED;
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return 0;
+ }
+
+ if(session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_FAILURE) {
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Keyboard-interactive authentication failed");
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return _libssh2_error(session,
+ LIBSSH2_ERROR_AUTHENTICATION_FAILED,
+ "Authentication failed "
+ "(keyboard-interactive)");
+ }
+
+ /* server requested PAM-like conversation */
+ s = session->userauth_kybd_data + 1;
+
+ if(session->userauth_kybd_data_len >= 5) {
+ /* string name (ISO-10646 UTF-8) */
+ session->userauth_kybd_auth_name_len = _libssh2_ntohu32(s);
+ s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "userauth keyboard data buffer too small"
+ "to get length");
+ goto cleanup;
+ }
+
+ if(session->userauth_kybd_auth_name_len) {
+ session->userauth_kybd_auth_name =
+ LIBSSH2_ALLOC(session,
+ session->userauth_kybd_auth_name_len);
+ if(!session->userauth_kybd_auth_name) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive 'name' "
+ "request field");
+ goto cleanup;
+ }
+ if(s + session->userauth_list_data_len <=
+ session->userauth_kybd_data +
+ session->userauth_kybd_data_len) {
+ memcpy(session->userauth_kybd_auth_name, s,
+ session->userauth_kybd_auth_name_len);
+ s += session->userauth_kybd_auth_name_len;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "userauth keyboard data buffer too small"
+ "for auth name");
+ goto cleanup;
+ }
+ }
+
+ if(s + 4 <= session->userauth_kybd_data +
+ session->userauth_kybd_data_len) {
+ /* string instruction (ISO-10646 UTF-8) */
+ session->userauth_kybd_auth_instruction_len =
+ _libssh2_ntohu32(s);
+ s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "userauth keyboard data buffer too small"
+ "for auth instruction length");
+ goto cleanup;
+ }
+
+ if(session->userauth_kybd_auth_instruction_len) {
+ session->userauth_kybd_auth_instruction =
+ LIBSSH2_ALLOC(session,
+ session->userauth_kybd_auth_instruction_len);
+ if(!session->userauth_kybd_auth_instruction) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive 'instruction' "
+ "request field");
+ goto cleanup;
+ }
+ if(s + session->userauth_kybd_auth_instruction_len <=
+ session->userauth_kybd_data +
+ session->userauth_kybd_data_len) {
+ memcpy(session->userauth_kybd_auth_instruction, s,
+ session->userauth_kybd_auth_instruction_len);
+ s += session->userauth_kybd_auth_instruction_len;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "userauth keyboard data buffer too small"
+ "for auth instruction");
+ goto cleanup;
+ }
+ }
+
+ if(s + 4 <= session->userauth_kybd_data +
+ session->userauth_kybd_data_len) {
+ /* string language tag (as defined in [RFC-3066]) */
+ language_tag_len = _libssh2_ntohu32(s);
+ s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "userauth keyboard data buffer too small"
+ "for auth language tag length");
+ goto cleanup;
+ }
+
+ if(s + language_tag_len <= session->userauth_kybd_data +
+ session->userauth_kybd_data_len) {
+ /* ignoring this field as deprecated */
+ s += language_tag_len;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "userauth keyboard data buffer too small"
+ "for auth language tag");
+ goto cleanup;
+ }
+
+ if(s + 4 <= session->userauth_kybd_data +
+ session->userauth_kybd_data_len) {
+ /* int num-prompts */
+ session->userauth_kybd_num_prompts = _libssh2_ntohu32(s);
+ s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "userauth keyboard data buffer too small"
+ "for auth num keyboard prompts");
+ goto cleanup;
+ }
+
+ if(session->userauth_kybd_num_prompts > 100) {
+ _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Too many replies for "
+ "keyboard-interactive prompts");
+ goto cleanup;
+ }
+
+ if(session->userauth_kybd_num_prompts) {
+ session->userauth_kybd_prompts =
+ LIBSSH2_CALLOC(session,
+ sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) *
+ session->userauth_kybd_num_prompts);
+ if(!session->userauth_kybd_prompts) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive prompts array");
+ goto cleanup;
+ }
+
+ session->userauth_kybd_responses =
+ LIBSSH2_CALLOC(session,
+ sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) *
+ session->userauth_kybd_num_prompts);
+ if(!session->userauth_kybd_responses) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive responses array");
+ goto cleanup;
+ }
+
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ if(s + 4 <= session->userauth_kybd_data +
+ session->userauth_kybd_data_len) {
+ /* string prompt[1] (ISO-10646 UTF-8) */
+ session->userauth_kybd_prompts[i].length =
+ _libssh2_ntohu32(s);
+ s += 4;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "userauth keyboard data buffer too "
+ "small for auth keyboard "
+ "prompt length");
+ goto cleanup;
+ }
+
+ session->userauth_kybd_prompts[i].text =
+ LIBSSH2_CALLOC(session,
+ session->userauth_kybd_prompts[i].
+ length);
+ if(!session->userauth_kybd_prompts[i].text) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for "
+ "keyboard-interactive prompt message");
+ goto cleanup;
+ }
+
+ if(s + session->userauth_kybd_prompts[i].length <=
+ session->userauth_kybd_data +
+ session->userauth_kybd_data_len) {
+ memcpy(session->userauth_kybd_prompts[i].text, s,
+ session->userauth_kybd_prompts[i].length);
+ s += session->userauth_kybd_prompts[i].length;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "userauth keyboard data buffer too "
+ "small for auth keyboard prompt");
+ goto cleanup;
+ }
+ if(s < session->userauth_kybd_data +
+ session->userauth_kybd_data_len) {
+ /* boolean echo[1] */
+ session->userauth_kybd_prompts[i].echo = *s++;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "userauth keyboard data buffer too "
+ "small for auth keyboard prompt echo");
+ goto cleanup;
+ }
+ }
+ }
+
+ response_callback(session->userauth_kybd_auth_name,
+ session->userauth_kybd_auth_name_len,
+ session->userauth_kybd_auth_instruction,
+ session->userauth_kybd_auth_instruction_len,
+ session->userauth_kybd_num_prompts,
+ session->userauth_kybd_prompts,
+ session->userauth_kybd_responses,
+ &session->abstract);
+
+ _libssh2_debug(session, LIBSSH2_TRACE_AUTH,
+ "Keyboard-interactive response callback function"
+ " invoked");
+
+ session->userauth_kybd_packet_len =
+ 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */
+ + 4 /* int num-responses */
+ ;
+
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ /* string response[1] (ISO-10646 UTF-8) */
+ if(session->userauth_kybd_responses[i].length <=
+ (SIZE_MAX - 4 - session->userauth_kybd_packet_len) ) {
+ session->userauth_kybd_packet_len +=
+ 4 + session->userauth_kybd_responses[i].length;
+ }
+ else {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for keyboard-"
+ "interactive response packet");
+ goto cleanup;
+ }
+ }
+
+ /* A new userauth_kybd_data area is to be allocated, free the
+ former one. */
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+
+ session->userauth_kybd_data = s =
+ LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len);
+ if(!s) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for keyboard-"
+ "interactive response packet");
+ goto cleanup;
+ }
+
+ *s = SSH_MSG_USERAUTH_INFO_RESPONSE;
+ s++;
+ _libssh2_store_u32(&s, session->userauth_kybd_num_prompts);
+
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ _libssh2_store_str(&s,
+ session->userauth_kybd_responses[i].text,
+ session->userauth_kybd_responses[i].length);
+ }
+
+ session->userauth_kybd_state = libssh2_NB_state_sent1;
+ }
+
+ if(session->userauth_kybd_state == libssh2_NB_state_sent1) {
+ rc = _libssh2_transport_send(session, session->userauth_kybd_data,
+ session->userauth_kybd_packet_len,
+ NULL, 0);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
+ "Would block");
+ if(rc) {
+ _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "Unable to send userauth-keyboard-interactive"
+ " request");
+ goto cleanup;
+ }
+
+ session->userauth_kybd_auth_failure = 0;
+ }
+
+ cleanup:
+ /*
+ * It's safe to clean all the data here, because unallocated pointers
+ * are filled by zeroes
+ */
+
+ LIBSSH2_FREE(session, session->userauth_kybd_data);
+ session->userauth_kybd_data = NULL;
+
+ if(session->userauth_kybd_prompts) {
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ LIBSSH2_FREE(session, session->userauth_kybd_prompts[i].text);
+ session->userauth_kybd_prompts[i].text = NULL;
+ }
+ }
+
+ if(session->userauth_kybd_responses) {
+ for(i = 0; i < session->userauth_kybd_num_prompts; i++) {
+ LIBSSH2_FREE(session,
+ session->userauth_kybd_responses[i].text);
+ session->userauth_kybd_responses[i].text = NULL;
+ }
+ }
+
+ if(session->userauth_kybd_prompts) {
+ LIBSSH2_FREE(session, session->userauth_kybd_prompts);
+ session->userauth_kybd_prompts = NULL;
+ }
+ if(session->userauth_kybd_responses) {
+ LIBSSH2_FREE(session, session->userauth_kybd_responses);
+ session->userauth_kybd_responses = NULL;
+ }
+ if(session->userauth_kybd_auth_name) {
+ LIBSSH2_FREE(session, session->userauth_kybd_auth_name);
+ session->userauth_kybd_auth_name = NULL;
+ }
+ if(session->userauth_kybd_auth_instruction) {
+ LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction);
+ session->userauth_kybd_auth_instruction = NULL;
+ }
+
+ if(session->userauth_kybd_auth_failure) {
+ session->userauth_kybd_state = libssh2_NB_state_idle;
+ return -1;
+ }
+
+ session->userauth_kybd_state = libssh2_NB_state_sent;
+ }
+}
+
+/*
+ * libssh2_userauth_keyboard_interactive_ex
+ *
+ * Authenticate using a challenge-response authentication
+ */
+LIBSSH2_API int
+libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session,
+ const char *user,
+ unsigned int user_len,
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC
+ ((*response_callback)))
+{
+ int rc;
+ BLOCK_ADJUST(rc, session,
+ userauth_keyboard_interactive(session, user, user_len,
+ response_callback));
+ return rc;
+}
diff --git a/contrib/libs/libssh2/src/userauth.h b/contrib/libs/libssh2/src/userauth.h
new file mode 100644
index 00000000000..6b402ddbf9f
--- /dev/null
+++ b/contrib/libs/libssh2/src/userauth.h
@@ -0,0 +1,51 @@
+#ifndef __LIBSSH2_USERAUTH_H
+#define __LIBSSH2_USERAUTH_H
+/* Copyright (c) 2004-2007, Sara Golemon <[email protected]>
+ * Copyright (c) 2009-2010 by Daniel Stenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.
+ */
+
+int
+_libssh2_userauth_publickey(LIBSSH2_SESSION *session,
+ const char *username,
+ unsigned int username_len,
+ const unsigned char *pubkeydata,
+ unsigned long pubkeydata_len,
+ LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC
+ ((*sign_callback)),
+ void *abstract);
+
+#endif /* __LIBSSH2_USERAUTH_H */
diff --git a/contrib/libs/libssh2/src/version.c b/contrib/libs/libssh2/src/version.c
new file mode 100644
index 00000000000..408f83a398d
--- /dev/null
+++ b/contrib/libs/libssh2/src/version.c
@@ -0,0 +1,54 @@
+/* Copyright (C) 2009 Daniel Stenberg. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 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.
+ *
+ * Neither the name of the copyright holder nor the names
+ * of any other contributors may be used to endorse or
+ * promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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 "libssh2_priv.h"
+
+/*
+ libssh2_version() can be used like this:
+
+ if (!libssh2_version(LIBSSH2_VERSION_NUM)) {
+ fprintf (stderr, "Runtime libssh2 version too old!\n");
+ exit(1);
+ }
+*/
+LIBSSH2_API
+const char *libssh2_version(int req_version_num)
+{
+ if(req_version_num <= LIBSSH2_VERSION_NUM)
+ return LIBSSH2_VERSION;
+ return NULL; /* this is not a suitable library! */
+}